3. Short Review on Python Programming

3.1. Programming and Data Structures

The notebook for the first lecture introduced some basic mathematical computations using SageMath. Among the many things that one can do with SageMath include the execution of a sequence of instructions. You many have observed this in the yesterday’s notebook. The SageMath computer algebra system is in fact an extension of the Python programming language, and allows, with a few exceptions, the use of Python programming constructs. In this notebook, we will briefly discuss how to use the Python programming language within SageMath.

Hopefully you should be aware that one should avoid using Python keywords as variable or function names since this will cause programming. The list of keywords is available by:

import keyword; keyword.kwlist
2*3;3*4;4*5 # one comment, 3 results

In addition to these keywords, we have the constants None(empty value, named NULL in other languages), True and False, and several functions predefined by Python or SageMath like len(), cos() and integrate(). It is better not to use these as variable names, otherwise some functionalities might no longer be available. The interpreter knows some additional commands, like quit to exit the Sage session. We will discover other commands like time or timeit later.

3.1.1. Function Calls

To evaluate a function, its arguments should be put inside parentheses — for example cos(pi) — or in the function call without argument reset(). However, the parentheses are superfluous for a command: the instructions print 6*7and print(6*7) are equivalent. In Python 3, print() is a function and thus requires parentheses.

3.1.2. More About Variables

As seen previously, SageMath denotes the assignment of a value to a variable by the equal sign =. The expression to the right of the equal sign is first evaluated, then its value is saved in the variable whose name is on the left. Thus we have:

y = 3; y = 3 * y + 1; y = 3 * y + 1; y

The three first assignments change the value of the variable y without any output, the last command prints the final value of y.

The del x command discards the value assigned to the variable x, and the function call reset() recovers the initial SageMath state.  Several variables can be assigned simultaneously, which differs from sequential assignments a = b; b = a

a, b = 10, 20 # (a, b) = (10, 20) and [10, 20] are also possible
a, b = b, a
a,b

The assignment a, b = b, a is equivalent to swapping the values of a and b using an auxiliary variable:

temp = a; a = b; b = temp # equivalent to: a, b = b, a

The following trick swaps the values of a and b without any auxiliary variable, using additions and subtractions:

x, y = var('x, y'); a = x ; b = y
a, b
a = a + b ; b = a - b ; a = a - b
a,b

The instruction a = b = c = 0 assigns the same value, here 0, to several variables; the instructions x += 5 and n *= 2 are respectively equivalent to x =x+5 and n = n*2.

The comparison between two objects is performed by the double equal sign `==:

2 + 2 == 2^2, 3 * 3 == 3^3

Click here if you want to go back to the table of content of this notebook. Otherwise, continue to the next Section.

3.2. Algorithmics

The paradigm of structured programming consists in designing a computer program as a finite sequence of instructions, which are executed in order. Those instructions can be atomic or composed: an example of atomic instruction is the assignment of a value to a variable a composed instruction, like a loop or a conditional, is made up from several instructions, themselves atomic or composed.

3.2.1. Loops/ Enumeration Loops

An enumeration loop performs the same computation for all integer values of an index k \in \{a, . . . , b\}: the following example outputs the beginning of the multiplication table by 7:

for k in [1..5]:
    print 7*k # block containing a single instruction
              # The colon symbol “:” at the end of the first line starts the instruction block

In this example, the repeated instruction block contains a single instruction (namely print), which is indented to the right with respect to the for keyword. A block with several instructions has its instructions written one below the other, with the same indentation.

The block positioning is important: the two programs below, which differ in the indentation of a single line, yield different results.

S = 0
for k in [1..3]:
    S = S+k
S = 2*S
S
S = 0
for k in [1..3]:
    S = S+k
    S = 2*S
S

On the left the instruction **S = 2*S** is executed only once at the end of the loop, while on the right it is executed at every iteration, which explains the different results:

S = (0 + 1 + 2 + 3) · 2 = 12  \qquad S = ((((0 + 1) · 2) + 2) · 2 + 3) · 2 = 22.

While Loops. The other kind of loops are the while loops. Like the enumeration loops, they execute a certain number of times the same sequence of instructions; however, here the number of repetitions is not known a priori, but depends on a condition. The while loop, as its name says, executes instructions while a given condition is fulfilled. The following example computes the sum of the squares of non-negative integers whose exponential is less or equal to 10^6 i.e., 1^2 + 2^2 + \cdots + 13^2.

S = 0; k =0              # The sum S starts to 0
while e^k<=10^6:         # e^13 <= 10^6 < e^14
    S = S + k^2
    k = k+1
S

The above instruction block contains two assignments: the first one accumulates the new term, and the second one moves to the next index. Those two instructions are indented in the same way inside the while loop structure.

3.2.2. Functions and Procedures

General Syntax.As in other computer languages, the SageMath user can define her/his own procedures or functions, using the def command whose syntax is detailed below. We call a function (resp. procedure) a sub-program with zero, one or several arguments, which returns (resp. does not return) a result. Let us define the function (x,y) \mapsto x^2 + y^2:.

def fct2 (x,y):
    return x^2 + y^2
a = var('a')
fct2(a,2*a)

By default, all variables appearing in a function are considered local variables. Local variables are created at each function call, destroyed at the end of the function, and do not interact with other variables of the same name. In particular, global variables are not modified by the evaluation of a function having local variables of the same name:

def foo(u):
    t = u^2
    return t*(t+1)
t= 1; u= 2
foo(3),t,u

It is possible to modify a global variable from within a function, with the global keyword:

a = b =1
def f(): global a; a = b= 2
f();a,b

Iterative and Recursive Methods.

As we have seen above, a user-defined function is a sequence of instructions. A function is called recursive when duringits evaluation, it calls itself with different parameters. The factorial sequence is a toy example of recursive sequence:

0! = 1,  \qquad (n+1)! = (n+1) \ n!   \quad \text{ for all } n \in \mathbb{N}.

The two following functions yield the same result for a nonnegative integer argument n; the first function uses the iterative method with a for loop,  while the second one is a word-by-word translation of the above recursive definition:

def fact1 (n):
    res = 1
    for k in [1..n]: res = res*k
    return res
def fact2 (n):
    if n == 0: return 1
    else: return n*fact2(n-1)

Global List Operations: The addition operator + concatenates two lists, and the multiplication operator *, together with an integer, performs an iterated concatenation:

L = [1, 2, 3] ; L + [10, 20, 30]
4 * [1, 2, 3]

The concatenation of the two sub-lists L[:k] and L[k:] reconstructs the original list. This explains why the left bound p of a sub-list L[p:q] is included

L = 5*[10, 20, 30]; L[:3]+L[3:] == L

The operator made from two points “..” makes it easy to construct integerlists without explicitly enumerating all elements, and can be mixed with isolatedelements:

[1..3, 7, 10..13]

We explain below how to build the image of a list under a function, and a sub-list of a list. The corresponding functions are map and filter, together with the [..for..x..in..]construction. Mathematics often involve lists made by applying a function f to its elements:

(a_0,a_1,\cdots,a_{n-1}) \mapsto  (fa_0,fa_1,\cdots,fa_{n-1})  .

The map command builds this “map”: the following example applies the trigonometric function cos to a list of usual angles:

map (cos, [0, pi/6, pi/4, pi/3, pi/2])

A user-defined function — with def — or a lambda-expression might also be used as first argument of map; the following command is equivalent to the above, using the function t \mapsto \cos t:

map (lambda t: cos(t), [0, pi/6, pi/4, pi/3, pi/2])

The lambda command is followed by the parameters separated by commas, and the colon must be followed by exactly one expression, which is the function result (without the returnkeyword).  A lambda expression may contain a test, whence the following functions are equivalent:

fctTest1 = lambda x: res1 if cond else res2

def fctTest2 (x):

  if cond: return res1

  else: return res2

As a consequence, the three following map commands are equivalent, the composition N \circ \cos being expressed in different ways:

map (lambda t: N(cos(t)), [0, pi/6, pi/4, pi/3, pi/2])
map (N, map (cos, [0, pi/6, pi/4, pi/3, pi/2]))
map (compose(N, cos), [0, pi/6, pi/4, pi/3, pi/2])

The filter command builds the sub-list of the elements satisfying a given condition. To get all integers in 1, …, 57 that are prime:

filter (is_prime, [1..57])

The test condition might be defined inside the filter command, as in thefollowing example which finds by exhaustive search all fourth roots of 7 modulothe prime 37; this equation has four solutions 3, 18, 19 and 34:

p = 37 ; filter (lambda n: n^4 % p == 7, [0..p-1])

Another way to build a list is using the comprehension form [..for..x..in..]; both commands below enumerate odd integers from 1 to 31:

map(lambda n:2*n+1, [0..15])
[2*n+1 for n in [0..15]]

The comprehension command is independent of the for loop. Associated with the if condition, it yields an equivalent construction to filter:

filter(is_prime, [1..55])
[p for p in [1..55] if is_prime()]

In the two following examples, we combine the if and filter tests with the comprehension for to determine a list of primes congruent to 1 modulo 4, then a list of squares of prime numbers:

filter (is_prime, [4*n+1 for n in [0..20]])
[n^2 for n in [1..20] if is_prime(n)]

In the first case the is_prime test is performed after the computation of 4n + 1, while in the second one the primality test is done before the computation of the square n^2.

Click here if you want to go back to the table of content of this notebook. Otherwise, continue to the next Section.

3.3. Graphics in Two Dimensions

Graphical Representation of a Function: To draw the graph of a symbolic or Python function on an interval [a, b], we use plot(f(x), a, b) or the alternative syntax plot(f(x), x, a, b).

plot(x * sin(1/x), x, -2, 2, plot_points=500)

Among the numerous options of the plot command, we mention the following:

  • plot_points (default value 200): minimal number of computed points;

  • xmin and xmax: interval bounds over which the function is displayed;

  • color: colour of the graph, either a RGB triple, a character string such as’blue’, or an HTML colour like ’#aaff0b’;

  • detect_poles (default value False): enables to draw a vertical asymptoteat poles of the function;

  • alpha: line transparency;

  • thickness: line thickness;

  • linestyle: style of the line, either dotted with ’:’, dash-dotted with ’-.’,or solid with the default value ’-’.

To visualise the graph, we assign the graphical object to a variable, say g, then we use the show command; in addition we can give bounds for the y-axis (g.show(ymin=-1, ymax=3)) or choose the aspect ratio (g.show(aspect_ratio=1) to have equal scales for x and y).

The graph obtained may be exported using the save command into several formats defined by the suffixes .pdf, .png, .ps, .eps, .svg and .sobj

g.save(name, aspect_ratio=1, xmin=-1, xmax=3, ymin=-1, ymax=3)

Let us draw on the same graphics the sine function and its first Taylor polynomials at 0.

def p(x, n):
    return(taylor(sin(x), x, 0, n))
xmax = 15 ; n = 15
g = plot(sin(x), x, -xmax, xmax)
for d in range(n):
    g += plot(p(x, 2 * d + 1), x, -xmax, xmax,color=(1.7*d/(2*n), 1.5*d/(2*n), 1-3*d/(4*n)))
g.show(ymin=-2, ymax=2)

We can also create an animation, to see how the Taylor polynomials approximate better and better the sine function when their degree increases. To keep the animation, it suffices to save it in the gif format.

a = animate([[sin(x), taylor(sin(x), x, 0, 2*k+1)]\
for k in range(0, 14)], xmin=-14, xmax=14,\
ymin=-3, ymax=3, figsize=[8, 4])
a.show(); a.save('/home/yae/Desktop/plots/animation.gif')
t = var('t')
x = cos(t) + cos(7*t)/2 + sin(17*t)/3
y = sin(t) + sin(7*t)/2 + cos(17*t)/3
g = parametric_plot((x, y), (t, 0, 2*pi))
g.show(aspect_ratio=1)

3.3.1. Data Plot

To construct a bar graph, two distinct functions are available. On the one hand, bar_chart takes as input an integer list and draws vertical bars whose height is given by the list elements (in the given order). The width option enables us tochoose the bar width.

bar_chart([randrange(15) for i in range(20)])
bar_chart([x^2 for x in range(1,20)], width=0.2)

On the other hand, to draw the histogram of a random variable from a list offloating-point numbers, we use the plot_histogram function. The list values arefirst sorted and grouped into intervals (the number of intervals is given by the option binswhose default value is 50). The height of each bar being proportional to the number of corresponding values.

liste = [10 + floor(10*sin(i)) for i in range(100)]
bar_chart(liste)

3.3.2. More on plotting

g(x) = sin(x)/x;

diff(g(x), x)
limit(g(x), x=0);  # evaluate the limit
plot(g(x), (x,-30,30),figsize=4)
gplot = plot(g, (-10,10), legend_label=r'$g(x) = %s$'%latex(g(x)),figsize=5)
gplot
hplot = plot(g,(-10,10), color='darkgreen', legend_label=r'$h(x)=\cos(x)$',figsize=5, gridlines=[range(10),srange(0,1,0.1)])
hplot

3.4. Exercises

  1. Given the lists:

    list_a = ["Evans", "Yae", "Jan"]
    
    list_b = ["Mary", "Rosemond", "Jjulian", "Frances"]
    

    Create the least possible list of pairs {(a, b) | a :raw-latex:`\in `list_a, b:raw-latex:`in `list_b}

  2. Given the list [(“Evans”, “Ocansey”), (“Yae”, “Gaba”), (“Jeff”, “Sanders”), (“Karthy”, “Green”)] where the first and second element of each tuple in the list is the first and last name. Write a program that creates two tuples, first_name and last_name which will collects all the first names and last names of each tuple in the list.


< 1. Introduction to Sagemath| Table of contents | 3. Sum of squares and plotting >