One of the great things about computers is their ability to repeat tasks quickly, accurately and without getting bored. We will take advantage of this capability many times during this course. This lab will introduce loops, which perform such repetition, and use them to create animations. First, however, we’ll take a look at some of the different kinds of objects SageMath works with.

In the previous lab, you worked with numbers and functions. You also made plots, and in the process of making them, you encountered words enclosed in quotation marks. For example, when you want to make a plot red, you have to put the word "red" in quotation marks. Such words are called strings, which is short for "character strings". Strings allow computers to handle words, phrases, and typographic symbols. Strings can include numbers, not just letters. But when numbers are treated as strings, they act quite differently from regular numbers.

```
>>a=5
>>show(a)
>>b="5"
>>show(b)
```

**Exercise 2.** What happens when you add 1 to `a`

? To `b`

?

1

In [1]:

a=5 show(a)

2

$5$

In [6]:

b="5" show(b)

3

$5$

In [3]:

a+1

4

6

In [77]:

b+1

5

```
---------------------------------------------------------------------------
```

```
TypeError Traceback (most recent call last)
```

```
<ipython-input-77-d18ca6847e56> in <module>()
----> 1 b+Integer(1)
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/rings/integer.pyx in sage.rings.integer.Integer.__add__ (build/cythonized/sage/rings/integer.c:12304)()
1801 return y
1802
-> 1803 return coercion_model.bin_op(left, right, operator.add)
1804
1805 cpdef _add_(self, right):
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/structure/coerce.pyx in sage.structure.coerce.CoercionModel.bin_op (build/cythonized/sage/structure/coerce.c:11178)()
1253 # We should really include the underlying error.
1254 # This causes so much headache.
-> 1255 raise bin_op_exception(op, x, y)
1256
1257 cpdef canonical_coercion(self, x, y):
```

```
TypeError: unsupported operand parent(s) for +: '<class 'str'>' and 'Integer Ring'
```

In [9]:

#When 1 is added the value of a it changes to 6 due to adding the integer. When added to 1 is added to be there is an error due to "b" being a string.

6

Remark. There is a little bit of vocabulary you should be aware of, as you may see it online and in the SageMath documentation. While the fundamental idea to understand is that different types of objects behave differently even though they may look alike, programmers often use the word "type" for simple objects like strings and numbers and "class" for more complex ones like graphs. Just think "type" when you see “class” and you’ll be fine. You can find out what the type of an object is using the type command.

```
>>type(a)
<type ’sage.rings.integer.Integer’>
```

This output means that `a`

is a SageMath integer.

```
>>type(b)
<type 'str'>
```

This output means that `b`

is a character string.

7

In [10]:

type(plot(x^2, x,-10,10))

8

<class 'sage.plot.graphics.Graphics'>

In [12]:

type(0.2)

9

<class 'sage.rings.real_mpfr.RealLiteral'>

In the above exercises, the variables a and b look the same when displayed
using show but act very differently when used in an arithmetical calculation.
This happens because a is the integer 5 while b is the character string "5". We
say these variables have different **types**, which just means they’re different kinds
of things. The type of a is "integer", while the type of b is "string".
This explains what you saw in Exercise 2. Adding 1 to an integer is not a
problem, but adding 1 to a character string makes no sense and results in an
error.

Actually, addition is defined for strings in SageMath. Here’s how it works.

```
>>"5"+"1"
'51'
```

You can see that the + symbol (or "operator") still embodies the idea of
"putting things together". However, "putting things together" means something
different for integers than for character strings or for the plots in Lab 1 #33, so the exact meaning of + changes depending on the **types** of **objects** it’s acting on. (Programmers say this makes + an overloaded operator.)

10

In [1]:

"1"+"7"

11

'17'

Scientific data and the outputs of model simulations often come in the form of lists of numbers. SageMath gives us many tools for working with such lists and tables.

You make a list by enclosing its elements, separated by commas, in square brackets:

```
["Bacteria", "Protists", "Plants", "Fungi", "Animals"]
[2,3,5,7,11,13]
```

Each element of a list can be accessed by its position in the list, typically called its index. In Python, indexing starts with 0, so the first element of a list with $k$ elements has index 0 and the last element has index $k − 1$.

12

**Example 1.** Enter the list of biological kingdoms into SageMath and call it
kingdoms.

```
>>kingdoms = ["Bacteria", "Protists", "Plants", "Fungi", "Animals"]
```

To access the first element of this list, enter:

```
>>kingdoms[0]
'Bacteria'
```

[100, 200, 400, 800]

**Exercise 6.** Assign the list of bacteria population sizes to the variable
`bacteria`

. (You can just copy and paste the list.)

**Exercise 7.** Find the type of the variable `bacteria`

from the previous exercise.

**Exercise 8.** What is the value of `bacteria[1]`

? What about `bacteria[0]`

?
First, answer without entering the command into SageMath. Then, use SageMath to check your answers.

13

In [13]:

kingdoms = ["Bacteria", "Protists", "Plants", "Fungi", "Animals"]

14

In [14]:

kingdoms[0]

15

'Bacteria'

In [15]:

bacteria=[100,200,400,800]

16

In [16]:

type(bacteria)

17

<class 'list'>

In [2]:

#bateria[1] should equal 200 and bacteria[0] should equal 100

18

In [17]:

bacteria[1]

19

200

In [18]:

bacteria[0]

20

100

21

In [ ]:

22

You can add an element to the end of a list using listname.append(element). (The generic names listname or list are just placeholders for the real name of your list.) For instance if you wanted to add the string “Archaea” to the list named kingdoms, the code would look something like this:

```
>> kingdoms.append("Archaea")
```

Note that the above code does not output anything to the screen. This
is because the `append()`

function only tells the computer to save its input to
specified list. To see the result, we would have to type `kingdoms`

and evaluate
the cell.

[100, 200, 400, 800, 1600]

**Exercise 10.** What is the next value of the population? Append it to the list.

**Exercise 11.** What would happen in the example above if we did
kingdoms.append("Archaea") twice before viewing kingdoms? Try this out
and explain why you got the result that you did.

23

In [20]:

bacteria.append(1600)

24

In [21]:

bacteria

25

[100, 200, 400, 800, 1600]

In [22]:

#the next value would be 3200

26

In [23]:

bacteria.append(3200)

27

In [24]:

bacteria

28

[100, 200, 400, 800, 1600, 3200]

In [25]:

kingdoms.append("Archaea")

29

In [26]:

kingdoms

30

['Bacteria', 'Protists', 'Plants', 'Fungi', 'Animals', 'Archaea']

In [27]:

#archaea was appended/added on

31

To plot the entries in a list, use the `list_plot`

function. If you give this function
a single list of numbers as an input, it will plot each number against its position
in the list. For example, the command `list_plot(bacteria)`

plots the list of population
sizes you just created in Example 6, producing the graph below.

Notice that the $x$-coordinate of the first point is 0, not 1. This happens because SageMath starts counting at zero, so the index of the first element of a list is 0.

32

In [28]:

x=[3,5,7,9,11]

33

In [30]:

list_plot(x, color="blue", size=30)

34

In [ ]:

35

In [ ]:

36

Often, we will need to plot lists of points. For example, suppose we have the points (1,2), (2,1), (3,4), and (4,3), with the first number in the ordered pair an $x$-coordinate and the second a $y$-coordinate. How do we plot these points in SageMath?

First, we enter the list of points:

```
>>g = [(1,2), (2,1), (3,4), (4,3)]
```

Then, we use the list_plot function to produce the figure below

```
>>list_plot(g)
```

This plot is technically correct, but the points are a little hard to see. To
make them more noticeable, we might color them red and change their size.
The command `list_plot(g, color="red", size=30)`

produces the second figure below.

37

In [ ]:

38

39

In [31]:

x=[(2,3), (4,1), (3,5), (5,6)]

40

In [32]:

list_plot(x, color="red", size=30)

41

Whether you’re plotting a list of numbers using `list_plot()`

or a mathematical
function using `plot()`

, you can label the axes of the plot using the
`axes_labels`

plotting option.

```
list_plot(bacteria, axes_labels=["time", "population"])
```

Notice that axes_labels is a variable that we set equal to a list of labels. In SageMath, square brackets always mean that a list is involved. Another feature of list_plot is the ability to connect the points of the list together. This is accomplished by using the plotjoined option:

```
list_plot(bacteria, axes_labels=["time","population"],plotjoined = True)
```

Note that the value of `plotjoined`

is either `True`

or `False`

. This type of data
is called a boolean. You will learn more about this data type in the future.
Now that we know how to label axes and join points, we should do so whenever
it is reasonable to do so. We should join points whenever we want to see a
curve. Labeling axes is a good way to keep track of which values correspond to
which axes, especially when we plot lists against each other.

42

However, `list_plot`

requires a list of points, not two lists of numbers, as
input. To avoid typing long lists of points and all the required parentheses by
hand, we turn to the function `zip`

. This function takes two lists and turns them
into a list of ordered pairs. (It can also take more than two lists and turn them
into a list of $n$-tuples.) Actually, for reasons that are beyond the scope of this
class, we have to next apply the `list`

function to the output of `zip`

. For example:

```
>>list(zip([1,2,3], [4,5,6]))
[(1, 4), (2, 5), (3, 6)]
>>list(zip([1,2,3], [4,5,6], [7,8,9]))
[(1, 4, 7), (2, 5, 8), (3, 6, 9)]
```

This is the kind of input that `list_plot`

needs. It’s common to nest the `list(zip())`

command inside the list_plot command, as below.

```
>>list_plot(list(zip([1,2,3], [4,5,6])))
```

This means the same thing as:

```
>>pairs = list(zip([1,2,3], [4,5,6]))
>>list_plot(pairs)
```

43

44

In [78]:

hippos=[62,81,75,90,67]

45

In [79]:

crocodiles=[20,34,18,25,31]

46

In [80]:

hc=zip(hippos,crocodiles)

47

In [83]:

list_plot(hc, size=30, axes_labels=["hippos", "crocodiles"])

48

```
---------------------------------------------------------------------------
```

```
TypeError Traceback (most recent call last)
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/plot/plot.py in list_plot(data, plotjoined, **kwargs)
3007 from sage.rings.all import RDF
-> 3008 RDF(data[0])
3009 data = list(enumerate(data))
```

```
TypeError: 'zip' object is not subscriptable
```

```
During handling of the above exception, another exception occurred:
```

```
TypeError Traceback (most recent call last)
```

```
<ipython-input-83-5b2645f83134> in <module>()
----> 1 list_plot(hc, size=Integer(30), axes_labels=["hippos", "crocodiles"])
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/misc/decorators.py in wrapper(*args, **kwds)
491 options['__original_opts'] = kwds
492 options.update(kwds)
--> 493 return func(*args, **options)
494
495 #Add the options specified by @options to the signature of the wrapped
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/plot/plot.py in list_plot(data, plotjoined, **kwargs)
3016 # So, the only other check we need to do is whether data[0] is an
3017 # element of the Symbolic Ring.
-> 3018 if data[0] in sage.symbolic.ring.SR:
3019 data = list(enumerate(data))
3020
```

```
TypeError: 'zip' object is not subscriptable
```

The `zip`

function is very helpful in plotting time series graphs. All you
need to do is make a list of time values and `zip`

it with the values of your state
variable. It is usually used together with the `list`

function which converts the output of `zip`

into a list.

49

In [38]:

bacteria=[100,200,400,800,1600,3200]

50

In [39]:

time=[1/24, 2/24, 3/24, 4/24, 5/24, 6/24]

51

In [40]:

list_plot(bacteria, axes_labels=["time", "population"])

52

Having developed some basic tools, we will now use them to work with
real data. Your worksheet contains lists called `wt5_time`

, `wt5_heartrate`

and
`wt5_temp`

. These lists contain heart rate and body temperature data for a wild
type (control) rat, measured over 72 hours as part of a real study of circadian
rhythms.

**Exercise 17.** Compare the plots and describe any relationships you observe.

**Exercise 18.** Plot the data as a trajectory in temperature-heart rate space.
Make sure to label your axes.

53

In [43]:

list_plot(wt5_heartrate, axes_labels=["time", "rate"])

54

```
---------------------------------------------------------------------------
```

```
NameError Traceback (most recent call last)
```

```
<ipython-input-43-51775618f6d7> in <module>()
----> 1 list_plot(wt5_heartrate, axes_labels=["time", "rate"])
```

```
NameError: name 'wt5_heartrate' is not defined
```

In [44]:

list_plot(wt5_temp, axes_labels=["time", "rate"])

55

```
---------------------------------------------------------------------------
```

```
NameError Traceback (most recent call last)
```

```
<ipython-input-44-734ab826c155> in <module>()
----> 1 list_plot(wt5_temp, axes_labels=["time", "rate"])
```

```
NameError: name 'wt5_temp' is not defined
```

In [ ]:

56

In [ ]:

57

Suppose you wanted to print out a series of sentences listing your favorite foods. You might use the print command and write:

```
print("Pizza is one of my favorite foods.")
print("Chocolate is one of my favorite foods.")
print("Green curry is one of my favorite foods.")
```

This works, but it’s rather tedious, especially if you like many kinds of food. A shortcut would be useful.

Looking at the example, you can see that the `print`

command and the string
" is one of my favorite foods." are the same in every line. The only thing that
changes is the name of the food. It would be convenient if we could just make
a list of the foods and insert them into the code one at a time.

We can do this using something called a *for loop*. One way to handle the
foods example with a for loop is the following:

```
favorites = ["Pizza", "Chocolate", "Green curry"]
for food in favorites:
print(food + " is one of my favorite foods.")
```

In this example, `food`

is a variable that takes on the value "Pizza" the
first time the computer executes the statement print food + " is one of
my favorite foods.", "Chocolate" the second time and "Green curry" the
third time.

58

In [48]:

favorites=["Night", "Say Sorry", "Say Hellow", "Feathersome", "Hello"]

59

In [49]:

for songs in favorites: print(songs + "is one of my favorite songs")

60

Nightis one of my favorite songs
Say Sorryis one of my favorite songs
Say Hellowis one of my favorite songs
Feathersomeis one of my favorite songs
Hellois one of my favorite songs

More generally, a for loop in SageMath (or Python) has the form:

```
for var in list:
loop body
```

The loop body must be indented. After the loop body, go back to your previous level of indentation.

**Example 4.** A string is similar in most ways to a list. This loop will print
each character in the word “dynamics” on a separate line.

```
>>for char in "dynamics":
>> print(char)
d
y
n
a
m
i
c
s
```

As you can see, the variable given immediately after the word for takes on the value of each list element in turn. First char was "d", then it was "y", then "n", and so on. This is the key to how SageMath for loops work. The body of the loop is executed for each element in the list. The body stays the same, while the list element changes. When writing loops, think about what should stay the same and what should change.

**Example 5.** The following loop computes the base-10 logarithm of 10, 100,
and 1000 in SageMath:

```
>>for val in [10, 100, 1000]:
>> print(log(val, 10))
1
2
3
```

**Exercise 21.** Use a for loop to square the numbers 15, 27, 39 and 84.

```
225
729
1521
7056
```

61

In [ ]:

62

In [53]:

for char in "Andrew": print(char)

63

A
n
d
r
e
w

In [55]:

for char in [15,27,39,84]: print(char^2)

64

225
729
1521
7056

In the previous exercises, you used loops to output values. Often, it is useful to store these values in a list.

To start, you’ll need to create a list with no elements. (Think of this as
tearing out a piece of paper and titling it “Groceries” when making a shopping
list.) To make such an empty list, enter `listname = []`

. Then, use
`listname.append()`

to add computed values to your list.

**Example 6.** This code makes a list of the first ten multiples of 2.

```
>>mult2 = [] #Set up empty list
>>for n in [1,2,3,4,5,6,7,8,9,10]:
>> mult2.append(2*n) #Compute the n’th multiple of 2 and append to list
>>mult2 #Display the list
[2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
```

[3, 6, 9, 12, 15]

**Exercise 23.** Write a loop that makes a list of the squares of the numbers 0.1,
0.2, $\dots$, 0.7. Then, plot your list.

[0.0100000000000000, 0.0400000000000000, 0.0900000000000000, 0.160000000000000, 0.250000000000000, 0.360000000000000, 0.490000000000000]

**Exercise 24.** Create a function and apply it to the numbers 0 through 5,
inclusive. Plot the list of resulting values.

65

In [56]:

mult3=[] for n in [1,2,3,4,5]: mult3.append(3*n)

66

In [57]:

mult3

67

[3, 6, 9, 12, 15]

In [58]:

sqr=[] for n in [0.1,0.2,0.3,0.4,0.5,0.6,0.7]: sqr.append(n^2)

68

In [59]:

sqr

69

[0.0100000000000000,
0.0400000000000000,
0.0900000000000000,
0.160000000000000,
0.250000000000000,
0.360000000000000,
0.490000000000000]

In [60]:

list_plot(sqr)

70

We can use loops to process data.

`wt5_time`

is measured in hours. Create another list
in which it is given in minutes.
**Exercise 26.** Convert the temperatures in `wt5_temp`

from Celsius to Fahrenheit.
(The formula is $F = (9/5) C + 32$.)

**Exercise 27.** Plot a time series of your transformed data.

**Exercise 28.** Plot a trajectory of the transformed temperature data and the
original heart rate data.

71

In [63]:

min=[] for n in wt5_time: min.append(n*50

72

```
---------------------------------------------------------------------------
```

```
NameError Traceback (most recent call last)
```

```
<ipython-input-63-062800fa2616> in <module>()
1 min=[]
----> 2 for n in wt5_time:
3 min.append(n*Integer(50))
```

```
NameError: name 'wt5_time' is not defined
```

In [ ]:

73

In [ ]:

74

In [ ]:

75

When investigating functions and models, it can be useful to animate their response to changes in parameters. Sage’s animate function allows us to easily produce such animations.

Animations are created by showing a series of still images one after the other, fast enough to create the illusion of motion. The animate function takes a list of plots as input and animates it.

**Example 7.** The following code shows how a change in the slope of a line
affects the line’s appearance.

```
plots = [] #Set up empty list to hold plots
slopes = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] #Make a list of slopes
for m in slopes: #For each m in slope, create plot, add to list
p=plot(m*x, (x,-10,10))
plots.append(p)
a=animate(plots) #Create the animation
show(a) #Necessary to display the animation
```

Try this code now. The show command is necessary to view the animation; it can also be used with other graphics.

Oops! The code produces an animation all right, but the animation is useless because it’s the axes, not the line, that move. To stop this from happening, we can specify maximum and minimum values for $y$, fixing the $y$-axis in place.

**Example 8.** Fixing the y-axis in an animation

```
plots = [] #Set up empty list to hold plots
slopes = [-5, -4, -3, -2, -1, 0, 1, 2, 3, 4, 5] #Make a list of slopes
for m in slopes: #For each m in slope, create plot, add to list
p=plot(m*x, (x,-10,10), ymin=-50, ymax=50)
plots.append(p)
a=animate(plots) #Create the animation
show(a) #Necessary to display the animation
```

This code produces a useful animated plot.

**Exercise 30.** Change the previous animation to make the slope range from -3
to 3 in steps of 0.5.

**Exercise 31.** Rewrite the animation in Exercise 29 so that the slope of the line
plotted is always 1 but the $y$-intercept ranges between -5 and 5.

76

In [87]:

plots = [] #Set up empty list to hold plots slopes = [-3, -2.5, -2, -1.5, -1, -0.5, 0, 0.5, 1, 1.5, 2, 2.5, 3] #Make a list of slopes for m in slopes: #For each m in slope, create plot, add to list p=plot(m+x, (x,-5,5), ymin=-50, ymax=50) plots.append(p) a=animate(plots, color="green") #Create the animation show(a) #Necessary to display the animation

77

```
---------------------------------------------------------------------------
```

```
TypeError Traceback (most recent call last)
```

```
<ipython-input-87-77c47acae03e> in <module>()
2 slopes = [-Integer(3), -RealNumber('2.5'), -Integer(2), -RealNumber('1.5'), -Integer(1), -RealNumber('0.5'), Integer(0), RealNumber('0.5'), Integer(1), RealNumber('1.5'), Integer(2), RealNumber('2.5'), Integer(3)] #Make a list of slopes
3 for m in slopes: #For each m in slope, create plot, add to list
----> 4 p=plot(m+x, (x,-Integer(5),Integer(5)), ymin=-Integer(50), ymax=Integer(50))
5 plots.append(p)
6 a=animate(plots, color="green") #Create the animation
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/rings/integer.pyx in sage.rings.integer.Integer.__add__ (build/cythonized/sage/rings/integer.c:12304)()
1801 return y
1802
-> 1803 return coercion_model.bin_op(left, right, operator.add)
1804
1805 cpdef _add_(self, right):
```

```
/ext/sage/sage-9.1/local/lib/python3.7/site-packages/sage/structure/coerce.pyx in sage.structure.coerce.CoercionModel.bin_op (build/cythonized/sage/structure/coerce.c:11178)()
1253 # We should really include the underlying error.
1254 # This causes so much headache.
-> 1255 raise bin_op_exception(op, x, y)
1256
1257 cpdef canonical_coercion(self, x, y):
```

```
TypeError: unsupported operand parent(s) for +: 'Integer Ring' and '<class 'list'>'
```

In [ ]:

78

In [ ]:

79