Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
Download

Basic data structures in SageMath

100 views
unlisted
ubuntu2004-eol
Kernel: SageMath 10.0

Basic data structures in SageMath (and Python)

Below we will describe some basic data structures in SageMath.

These data structures play a crucial role in organizing and manipulating data efficiently.

SageMath builds on Python's foundations, so the data structures in SageMath are largely similar to those in Python, with some additional specialized types.

To describe the data structures that we have in mind, it will be useful to recall first a few details for Python variables (you may refer to main part (Chapter 1) for this as well)

1. Python variables

Recall that these variables can be used to store values and objects for later use within the program.

Thus, Python variables can hold numbers, strings, lists, functions, and more, helping organize your computations (and your data) efficiently.

Examples:
x = 5 y = 12.5 result = x + y print(result) # Output: 17.5
17.5000000000000

Notice, however, this approach in Sage outputs many zeros, and we may prefer a more refined display. Let's explore how to achieve this.

1st way:
x = 5 y = 12.5 result = x + y print("{:.1f}".format(result)) # Output: 17.5
17.5
Remark:

The  print(":.1f".format(result)){\tt{print("{:.1f}".format(result))}} statement is a way of formatting the output in Python (and SageMath).

Here's a breakdown of how it works:

\bullet "{:.1f}""\{:.1f\}" This is a format string that specifies how to format the number.

\bullet The colon  : :  indicates that what follows is the formatting specification

\bullet .1.1  means to display one digit after the decimal point

\bullet .f.f  stands for floating-point number format, which means we want a decimal number..format.

x = 5 y = 12.5 result = x + y print("{:.4f}".format(result)) # Output: 17.5000
17.5000
Remark:

A floating-point number format is a way to represent real numbers in computing that can accommodate a wide range of values.

It allows for the representation of both very large and very small numbers, making it useful for scientific calculations and applications requiring precision.

Floating-point representation is essential for performing arithmetic operations in programming but requires careful consideration due to potential precision issues.

2nd way : An alternative techique using f-strings

As an alternative we can use f-strings for more concise formatting (available in Python 3.6 and later):

x = 5 y = 12.5 ab= x *y print(f"{ab:.1f}") # Output: 17.5
62.5

Above, the last line prints the value of result using an f-string for formatting.

F-string: The ``f'' before the string indicates that it’s a formatted string literal. It allows you to embed expressions inside string literals.

Further simple examples of python variables:
a = 7.456 # Assigns the floating-point number 7.456 to the variable a b = 2.34 # Assigns the floating-point number 2.34 to the variable b total = a + b # Adds a and b, resulting in 9.796, and assigns it to the variable total print(f"{total:.5f}") # Prints the total formatted to 5 decimal places
9.79600
length = 15.8 # Assigns the floating-point number 15.8 to the variable length width = 7.25 # Assigns the floating-point number 7.25 to the variable width area = length * width # Calculates the area by multiplying length and width print(f"Area: {area:.3f} square units") # Prints the area formatted to three decimal places
Area: 114.550 square units
score1 = 88.5 # Assigns the floating-point number 88.5 to the variable score1 score2 = 91.2 # Assigns the floating-point number 91.2 to the variable score2 average = (score1 + score2) / 2 # Calculates the average of the two scores print(f"Average score: {average:.2f}") # Prints the average formatted to two decimal places
Average score: 89.85
celsius = 25.0 # Assigns the floating-point number 25.0 to the variable celsius fahrenheit = (celsius * 9/5) + 32 # Converts Celsius to Fahrenheit print(f"{celsius:.1f}°C is equivalent to {fahrenheit:.2f}°F") # Prints the conversion with formatting
25.0°C is equivalent to 77.00°F
a = 3 b = 4 sum_ab = a + b show(sum_ab) sentence = "Mohamed" + " " + "has fun!" show(sentence)

7\displaystyle 7

Mohamed has fun!\displaystyle \verb|Mohamed|\verb| |\verb|has|\verb| |\verb|fun!|

name = "Alice" name2 = "Katherina" print(name+ " "+name2) name="Art" print(name+ " "+ "and" " " + name2)
Alice Katherina Art and Katherina
# Define variables length = 5 width = 3 # Calculate the area of a rectangle area = length * width print(area) # Output: 15 # Using variables in a function via the ``def method'' (we will see more on this method later) def rectangle_perimeter(l, w): return 2 * (l + w) perimeter = rectangle_perimeter(length, width) print(perimeter) # Output: 16
15 16

Summary : Role of Python variables

a) Temporary Storage: Python variables in SageMath store intermediate results during calculations.

b) Reusable Code: Storing values in variables makes your code reusable and easy to modify.

2. Data structures in SageMath

To begin with, let us recall that in SageMath there are three types of brackets or braces (parentheses (), square brackets [], and curly braces {}).

They all serve different purposes, and each comes with its own specific functionality.

Below is an overview of how they are used.

First we recall how SageMath (and Python) can infrom us for the ``type'' of an object.

The type of an object

In SageMath, just like in Python, every object has a type.

The type of an object refers to its class or data structure, which determines what kind of object it is and what operations can be performed on it.

The type function in SageMath (and Python) is used to determine the type (or class) of a given object.

For instance:

a = 5; type(a)
<class 'sage.rings.integer.Integer'>
a, b, c, d = 117.331, 327, "Sage", 3+4*I

Observe that a, b, c, d, are all Python variables. Let us now examine their ``type''

type(a), type(b), type(c), type(d)
(<class 'sage.rings.real_mpfr.RealLiteral'>, <class 'sage.rings.integer.Integer'>, <class 'str'>, <class 'sage.rings.number_field.number_field_element_quadratic.NumberFieldElement_gaussian'>)

Observe the type of the variable a' changed, because we changed the value that we had assigned to a'.

Another example:

(This may not be the most insightful example:)

x, y, z=[], (), {}

We can check the type of these variables as well:

type(x), type(y), type(z)
(<class 'list'>, <class 'tuple'>, <class 'dict'>)

So we get the types: list{\tt{list}}, tuple{\tt{tuple}} and dict{\tt{dict}}, respectively. These 3 types are central in Python and SageMath (and also in other languages)

  1. A list is created by the square brackets [ ][ \ ]

  2. A tuple is created by the parenthesis ( )( \ )

  3. A set or a dictionary is created by the curly brackets { }\{ \ \}

2.a Strings

Strings are immutable sequences of characters.

SageMath uses Python’s string type for handling text, so the behavior is identical.

For instance, let us introduce the following:

course="An Introduction to SageMath."
type(course)
<class 'str'>

Hence, this object is a string. Hence there exists another important type of data objects in Sagemath, the strings!

We can compute the length of a string by the command len{\tt{len}}.

len(course)
28

Thus there are 28 characters including spaces and full stop in the end. Each character has an index (label), and the enumeration starts from 0.

course[0]
'A'
course[1]
'n'
course[20]
'a'

If we want to attain all the first 10 characters we type:

course[0:10]
'An Introdu'

To obtain the last character we can also type

course[-1]
'.'

For the second last character, we can type the following (and so on!)

course[-2]
'h'
TIP{\color{red} \textsf{TIP}}

Strings are objects with many built-in methods, which can be accessed using the ``dot notation''.

The Tab completion feature helps explore these methods interactively.

In particulat, you can explore the available methods (functions) that can be applied to our string by typing the following:

course. followed by pressing the Tab key in your SageMath environment or editor.

This applies to most of the objects that we will meet below (as lists, sets, tuples, dictionaries, etc).

For instance:

course.upper(){\tt{course.upper()}}: Converts the string to uppercase.

course.lower(){\tt{course.lower()}}: Converts the string to lowercase.

course.strip(){\tt{course.strip()}}: Removes any leading or trailing spaces.

course.replace("old","new"){\tt{course.replace("old", "new")}}: Replaces a substring with another.

Here are some examples (try to explore further examples yourselves).

course.capitalize()
'An introduction to sagemath.'
course.count("a")
2
course.count("o")
3
w in course
--------------------------------------------------------------------------- NameError Traceback (most recent call last) Cell In [39], line 1 ----> 1 w in course NameError: name 'w' is not defined
"w" in course
False
"d" in course
True
course.lower()
'an introduction to sagemath.'
course.upper()
'AN INTRODUCTION TO SAGEMATH.'
course.split()
['An', 'Introduction', 'to', 'SageMath.']

We can also convert the string ``course'' to a list:

Lst=list(course); print(Lst)
['A', 'n', ' ', 'I', 'n', 't', 'r', 'o', 'd', 'u', 'c', 't', 'i', 'o', 'n', ' ', 't', 'o', ' ', 'S', 'a', 'g', 'e', 'M', 'a', 't', 'h', '.']
len(Lst)
28
len(Lst)==len(course)
True

2.b. Lists

Lists are mutable, ordered sequences of elements. They can hold items of any type (e.g., numbers, strings, other lists).

They are created by square bracket [ ][ \ ].

In SageMath, lists behave the same as Python lists.

However, they can also hold Sage-specific objects, such as mathematical elements, algebraic expressions, matrices, etc.

Example
L=[1,2, 3, 10, 20, 30, 100, 200, 300, "lion", "dog", "cat", "human"]
show(L)

[1,2,3,10,20,30,100,200,300,lion,dog,cat,human]\displaystyle \left[1, 2, 3, 10, 20, 30, 100, 200, 300, \verb|lion|, \verb|dog|, \verb|cat|, \verb|human|\right]

Some characteristics: The type:

type(L)
<class 'list'>

The legth of the list:

len(L)
13

Some operations:

L.remove(1)
show(L)

[2,3,10,20,30,100,200,300,lion,dog,cat,human,1]\displaystyle \left[2, 3, 10, 20, 30, 100, 200, 300, \verb|lion|, \verb|dog|, \verb|cat|, \verb|human|, 1\right]

L.clear()
show(L)

[]\displaystyle \left[\right]

L.insert(0, "maths")
show(L)

[maths]\displaystyle \left[\verb|maths|\right]

L.insert(1,"algebra"); show(L)

[maths,algebra]\displaystyle \left[\verb|maths|, \verb|algebra|\right]

L.remove("algebra")
show(L)

[maths]\displaystyle \left[\verb|maths|\right]

L.insert(0, "yes"); show(L)

[yes,maths]\displaystyle \left[\verb|yes|, \verb|maths|\right]

L.reverse()
show(L)

[maths,yes]\displaystyle \left[\verb|maths|, \verb|yes|\right]

L.clear()
L=[1, 5, 10, 100, 5000, 1000, "ALICE", "BOB", "DOG", "CAT", "black", "white"]
L[0]
1
L[3]
100
len(L)
12
L[11]
'white'
L[12]
--------------------------------------------------------------------------- IndexError Traceback (most recent call last) Cell In [95], line 1 ----> 1 L[Integer(12)] IndexError: list index out of range
L[-1]
'white'
L[-2]
'black'
L[-11]
5
L.append("god"); L
[1, 5, 10, 100, 5000, 1000, 'ALICE', 'BOB', 'DOG', 'CAT', 'black', 'white', 'god']

We can change some entry in a list with another number or string. For example, suppose that we want to change the first entry (which is 1), by 0. Then we type:

L[0]=0; L
[0, 5, 10, 100, 5000, 1000, 'ALICE', 'BOB', 'DOG', 'CAT', 'black', 'white', 'god']

If we want to add in a place in the list another entry, we can use the insert{\tt{insert}} command. For example, suppose that we want to at the number "π\pi" at the 5th entry of the previous list. This can be done as follows:

L.insert(4, pi); L
[0, 5, 10, 100, pi, 5000, 1000, 'ALICE', 'BOB', 'DOG', 'CAT', 'black', 'white', 'god']
len(L)
14

Similarly we can remove some entry in the list:

L.remove("BOB"); L
[0, 5, 10, 100, pi, 5000, 1000, 'ALICE', 'DOG', 'CAT', 'black', 'white', 'god']
M=["blue", "pink", "brown", "water", "fire"]; M
['blue', 'pink', 'brown', 'water', 'fire']
L+M
[0, 5, 10, 100, pi, 5000, 1000, 'ALICE', 'DOG', 'CAT', 'black', 'white', 'god', 'blue', 'pink', 'brown', 'water', 'fire']
show(L+M)

[0,5,10,100,π,5000,1000,ALICE,DOG,CAT,black,white,god,blue,pink,brown,water,fire]\displaystyle \left[0, 5, 10, 100, \pi, 5000, 1000, \verb|ALICE|, \verb|DOG|, \verb|CAT|, \verb|black|, \verb|white|, \verb|god|, \verb|blue|, \verb|pink|, \verb|brown|, \verb|water|, \verb|fire|\right]

We can create lists inside another list: (nested lists)

Q=[[1,2,3], [4, 5, 6], [7, 8, 9]]; show(Q)

[[1,2,3],[4,5,6],[7,8,9]]\displaystyle \left[\left[1, 2, 3\right], \left[4, 5, 6\right], \left[7, 8, 9\right]\right]

Q[0]
[1, 2, 3]
Q[1]
[4, 5, 6]
Q[2]
[7, 8, 9]

To access some entry inside some sublist: e.g. in the list [1, 2, 3], let us access the 2nd entry, which is the number 2. We should type:

Q[0][1]
2

another example:

Q[2][1]
8

Matrices are essentially lists with a bit more information; We will learn the tools that SageMath offers for matrix calculus later in this course. But let us present a simple example:

A=matrix(QQ, [[1, 2], [3, 4]]); show(A)

(1234)\displaystyle \left(\begin{array}{rr} 1 & 2 \\ 3 & 4 \end{array}\right)

If we do not care for the field (above we fixed the field of rationals Q\mathbb{Q}), we can directly create our matrix. For instance:

Aa=matrix([[1, 2], [3, 4]]); show(Aa)

(1234)\displaystyle \left(\begin{array}{rr} 1 & 2 \\ 3 & 4 \end{array}\right)

Lists of numbers in an arithmetic progression:

If we want to create automatically a list of the first 10 numbers, we can use the command range{\tt{range}}.

Ranges: A range is an immutable sequence of numbers, commonly used in loops.

my_range = range(1, 10, 2)

Here is an example of how we can combine the list and range commands:

v=list(range(10)); v
[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]

We can also fix the increment: For instance:

u=list(range(0, 41, 5)); u
[0, 5, 10, 15, 20, 25, 30, 35, 40]

2.c. Tuples

Tuples are immutable, ordered collections of elements.

They are useful when you want to create a sequence of values that should not be modified.

Tuples are created by round brackets (parentheses) ( )( \ )

Tuples in SageMath are similar to Python tuples, but they can also store Sage objects.

T=(43, 43123, 112122, 345678.9043, 1/2, "dog"); T
(43, 43123, 112122, 345678.904300000, 1/2, 'dog')
len(T)
6
T[1]
43123
T[-1]
'dog'
Remark:

Tuples does not support item assigment (as in the case of lists). In other words we cannot change the entries of a tuple.

For instance:

T[2]=0; T
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In [147], line 1 ----> 1 T[Integer(2)]=Integer(0); T TypeError: 'tuple' object does not support item assignment

.....and similarly with other functions...For instance:

T.append("cat")
--------------------------------------------------------------------------- AttributeError Traceback (most recent call last) Cell In [148], line 1 ----> 1 T.append("cat") AttributeError: 'tuple' object has no attribute 'append'
Main difference between lists and tuples : Mutability

Tuples are "un-mutatable data type'', whereas lists are "mutatable data type''.

Tuples are generally faster and use less memory than lists, because tuples are immutable.

Remark:{\small\color{red}{\mathsf {Remark:}}}

Lists are useful when you need a collection that might change over time.

Tuples are used when the collection should remain constant, or as a lightweight way to group different types of data.

2.d. Sets

In SageMath, sets are collections of unique, unordered elements.

They are similar to sets in mathematics and are useful when you need to work with distinct elements without caring about their order.

So, a set is an unordered collection of unique elements and we cannot have duplicate items in a set.

In SageMath sets can include mathematical sets such as finite fields, rings, and sets of elements related to algebraic structures.

We also saw some special built-in functions for treating for example sets of the real line.

S = {1, 2, 2, 3} print(S) # Output: {1, 2, 3}
{1, 2, 3}
type(S)
<class 'set'>
Sets are Unordered:

The elements in a set are unordered, meaning their order of appearance may not match their insertion order.

Ss = {3, 1, 2} print(Ss) # Output: {1, 2, 3}]
{1, 2, 3}
Sets are also Mutable as the lists:

Sets are mutable, meaning you can add or remove elements after the set is created.

S = {1, 2, 3} S.add(4) # Adds 4 to the set S.remove(2) # Removes 2 from the set
Main Difference Between Sets, Lists, and Tuples:
Uniqueness:

Sets: Only unique elements. Duplicate entries are automatically removed.

Lists: Allow duplicate elements.

Tuples: Also allow duplicates.

Ordering:

Sets: Unordered, meaning there’s no guarantee that the elements will stay in the order you insert them.

Lists and Tuples: Ordered, and elements maintain their position based on how they were inserted.

Example:
# Define a set with repeated entries St = {1, 2, 2, 3, 4, 4, 5} # Print the set to see how SageMath handles repeated entries print(St)
{1, 2, 3, 4, 5}

Hence SageMath automatically removes the duplicates in sets, and keeps only one instance of each repeated element.

Summary example
# Set: unique, unordered, mutable S = {1, 2, 3, 3, 4} # Duplicate 3 will be ignored S.add(5) # Adds 5 to the set S.remove(1) # Removes 1 from the set print(S) # Output: {2, 3, 4, 5} # List: ordered, allows duplicates, mutable L = [1, 2, 3, 3, 4] L.append(5) # Adds 5 to the list print(L) # Output: [1, 2, 3, 3, 4, 5] # Tuple: ordered, allows duplicates, immutable T = (1, 2, 3, 3, 4) # T[0] = 5 # This would cause an error because tuples are immutable print(T) # Output: (1, 2, 3, 3, 4)
{2, 3, 4, 5} [1, 2, 3, 3, 4, 5] (1, 2, 3, 3, 4)

2.e. Dictionaries

Dictionaries are constructed again via the curly brackets { }\{ \ \}.

In SageMath (and Python), dictionaries are collections of key-value pairs, where each key is unique, and it maps to a specific value.

Summary:{\color{red}\textsf{Summary:}} ``In a dictionary each key is unique, and it maps to a value.''

Dictionaries in SageMath are the same as in Python, but the keys and values can be Sage objects (such as matrices, expressions, etc.).

As we will see below, dictionaries are mutable, meaning you can change, add, or remove key-value pairs after creating them (See also the last section of Chapter 1).

Examples:

my_dict = {'a': 1, 'b': 2, 'c': 3}

Further examples:

# Create a dictionary with some key-value pairs student_grades = {"Alice": 85, "Bob": 92, "Charlie": 78, "John" : 57} # Access a value by key print(student_grades["Alice"]) # Output: 85
85
print(student_grades["Charlie"])
78
print(student_grades["John"]) # Output: 57
57
# Update Bob's grade and add a new student student_grades["Bob"] = 95 # Updating an existing value student_grades["David"] = 88 # Adding a new key-value pair print(student_grades) # Output: {'Alice': 85, 'Bob': 95, 'Charlie': 78, 'John': 57, 'David': 88}
{'Alice': 85, 'Bob': 95, 'Charlie': 78, 'John': 57, 'David': 88}
# Loop through the dictionary and print each student's name and grade for student, grade in student_grades.items(): print(f"{student}: {grade}")
Alice: 85 Bob: 95 Charlie: 78 John: 57 David: 88
# Remove a key-value pair using the 'del' keyword del student_grades["John"] print(student_grades)
{'Alice': 85, 'Bob': 95, 'Charlie': 78, 'David': 88}

Why Dictionaries Are Useful:

1st reason: Efficient Data Retrieval: They allow for quick lookup of values by using unique keys.

(Example: A phonebook can be modeled using a dictionary where names (keys) map to phone numbers (values).)

2nd reason: Flexible Data Storage: Dictionaries can store different types of values, making them flexible for handling complex data structures.

3rd reason: Handling Unordered Data: Since dictionaries don’t maintain any specific order (prior to Python 3.7), they are useful for unordered collections of items.

Example: Using a Dictionary to Count Elements

In this example, the dictionary fruit_count efficiently counts how many times each fruit appears in the list.

# List of fruits fruits = ["apple", "banana", "apple", "orange", "banana", "apple"] # Create a dictionary to count the occurrences of each fruit fruit_count = {} for fruit in fruits: if fruit in fruit_count: fruit_count[fruit] += 1 else: fruit_count[fruit] = 1 print(fruit_count) # Output: {'apple': 3, 'banana': 2, 'orange': 1}
{'apple': 3, 'banana': 2, 'orange': 1}

3. Symbolic expressions and functions

A symbolic expression is a mathematical formula involving symbolic variables, but it's not treated as a formal function.

Symbolic expressions can be manipulated algebraically (e.g., simplified, differentiated), but they are not inherently designed to take inputs as functions do.

Example
# Declare symbolic variable x = var('x') # Define a symbolic expression expr = x^2 + 2*x + 1; #display the expression print(expr) # lets try to evaluate it as it was a function (we will get an error) expr(2)
x^2 + 2*x + 1
--------------------------------------------------------------------------- TypeError Traceback (most recent call last) Cell In [19], line 13 9 print(expr) 11 # lets try to evaluate it as it was a function (we will get an error) ---> 13 expr(Integer(2))
File /ext/sage/10.0/src/sage/symbolic/expression.pyx:6192, in sage.symbolic.expression.Expression.__call__() 6190 z^2 + x^y 6191 """ -> 6192 return self._parent._call_element_(self, *args, **kwds) 6193 6194 def variables(self):
File /ext/sage/10.0/src/sage/symbolic/ring.pyx:1047, in sage.symbolic.ring.SymbolicRing._call_element_() 1045 d = args[0] 1046 else: -> 1047 raise TypeError("Substitution using function-call syntax " 1048 "and unnamed arguments has been removed. You " 1049 "can use named arguments instead, like "
TypeError: Substitution using function-call syntax and unnamed arguments has been removed. You can use named arguments instead, like EXPR(x=..., y=...)

In this case, expr is a symbolic expression in terms of x.

It's not a function that you can directly evaluate by passing an argument like expr(2) unless you manually substitute a value for x.

The substitute(){\tt{substitute()}} command

Use .substitute() to evaluate symbolic expressions:

Example
var("x") exprnew=x^3+x exprnew.substitute(x=2) # This gives 10, as it computes 2^3 + 2
10
exprnew.substitute(x=pi).n()
34.1478693338896

Let us now see how we can "convert" a symbolic expression to a function. It is really easy!!!

# Declare symbolic variable x = var('x') # to see the previous symbolic expression as a function is enought to type the following expr(x) = x^2 + 2*x + 1; show(expr) show(expr(x)) print(expr(2)) # make some experiments a=4 num=var("num") print(expr(a)) print(expr(num))

x  x2+2x+1\displaystyle x \ {\mapsto}\ x^{2} + 2 \, x + 1

x2+2x+1\displaystyle x^{2} + 2 \, x + 1

9 25 num^2 + 2*num + 1

In SageMath, a function is a callable object that takes inputs and produces outputs, behaving much like familiar mathematical functions.

Functions are explicitly designed to be evaluated with arguments.

As we already know, they are defined using parentheses to specify the variables they depend on.

# Define a function f(x) = x^2 + 2*x + 1 # a function depending on a unique variable f(2) # This gives 9, as it computes 2^2 + 2*2 + 1
9
var("y") f(x, y)=x^2+y^2-1 # a function depending on two variables show(f) show(f(x, y))

(x,y)  x2+y21\displaystyle \left( x, y \right) \ {\mapsto} \ x^{2} + y^{2} - 1

x2+y21\displaystyle x^{2} + y^{2} - 1

Let us present the graph of ff: We can use the plot3d(){\tt{plot3d()}} command, as follows:

plot3d(f(x, y), (x, -1, 1), (y, -1, 1))

°