Python For Quantum Mechanics#

Week 1: Data Structures#

from IPython.display import YouTubeVideo
YouTubeVideo('yCQQwTEaMvs',width=700, height=400)

Now that we have disscussed different data types we need a way to organise many pieces of data. This can be done using lists, tuples, sets, and dictionaries. Each of these have their own features and advantages which are summarised in the table below.

Type

Ordered

Changable

Allows Duplicates

Example

List

✔️

✔️

✔️

[a,b]

Tuple

✔️

✔️

(a,b)

Set

{a,b}

Dictionary

✔️

✔️

✔️

{“alpha” : a, “beta” : b}

These may seem confusing at first but we will work through each individually.

Lists#

Lists are a an ordered collection of items of any data type (mixing data types is allowed). To create a list of elements in Python, we use the following notation.

mylist = [1, -2.0, "Three", 4+0j, None]

print(type(mylist))
print(mylist)
<class 'list'>
[1, -2.0, 'Three', (4+0j), None]

One can access the elements of a lists by their placment starting with 0.

1

-2.0

"Three"

4+0j

None

0

1

2

3

4

print(mylist[0]) # first element has index 0
print(mylist[3]) # fourth element has index 3
1
(4+0j)

Tuples#

Tuples are immutable lists, they cannot be modified once created. The elements can be accessed in the same way.

mytuple = (4.1, 6.626e-34, -3, 6.6)

print(type(mytuple))
print(mytuple)
print(mytuple[2])
<class 'tuple'>
(4.1, 6.626e-34, -3, 6.6)
-3

We can use a tuple to assign values to multiple variables in a compact way.

print(mytuple[2])

x, y, z, q = mytuple

print("x =", x) # can print muplitple things with spaces between them in this way
print("y =", y)
print("z =", z)
print("q =", q)
-3
x = 4.1
y = 6.626e-34
z = -3
q = 6.6

Sets#

Sets are an unordered collection of unique items. Sets are useful to eliminate repeated elements.

myset = {2.0, 2 ,4 ,4.0 ,5+2j, 'copy', "copy", 4.1, 3.4e-6} 

print(type(myset))
print(myset)
<class 'set'>
{2.0, 4, 4.1, 'copy', (5+2j), 3.4e-06}

Notice that floats and integers are not distinguished. The data type of the element listed first is the one that is kept.

Dictionaries#

Dictionaries are similar to lists, but each element has a corresponding unique key that is used to map to that specific element.

mydictionary = {"x" : 7.0, "y" : 53.0,"z" : 2.0}

print(type(mydictionary))
print(mydictionary)
<class 'dict'>
{'x': 7.0, 'y': 53.0, 'z': 2.0}

Access a value using it’s key.

print("1st entry has value =", mydictionary["x"]) 
1st entry has value = 7.0

Operations with Data Structures#

Properties#

We can calculate the length (or number of elements in) these data structures using the len() function.

print(len(mylist))
print(len(mytuple))
print(len(myset))
print(len(mydictionary))
5
4
6
3

We can find the maximum or minimum value of a list, tuple, or set of only real numerical values.

print(max(mytuple))
print(min(mytuple))
6.6
-3

We can check if an element is present in the structure, obtaining a boolean data type. For this we use in in the following way.

print(6.6 in mytuple)
print(5 in mydictionary)
True
False

Changing, Adding, and Removing Elements#

Next we will talk about changing, adding and removing items from lists, sets, and dictionaries. We cannot do this to tuples as they cannot be changed once made.

Changing the values of lists and dictionaries can be done in such a way as you might expect.

print(mylist)
mylist[4] = 5.0
print(mylist)
[1, -2.0, 'Three', (4+0j), None]
[1, -2.0, 'Three', (4+0j), 5.0]
print(mydictionary)
mydictionary['z'] = 3.0
print(mydictionary)
mydictionary['z'] = 2.0
print(mydictionary)
{'x': 7.0, 'y': 53.0, 'z': 2.0}
{'x': 7.0, 'y': 53.0, 'z': 3.0}
{'x': 7.0, 'y': 53.0, 'z': 2.0}

To add to and remove from lists we use the .append(<element>) and .remove(<element>) attributes of the list class. We will discuss this structure in more detail in the object oriented programing portion of the course, however for now we can simply follow the syntax below.

print(mylist)
mylist.append(6.0)
print(mylist)
mylist.remove(6.0)
print(mylist)
[1, -2.0, 'Three', (4+0j), 5.0]
[1, -2.0, 'Three', (4+0j), 5.0, 6.0]
[1, -2.0, 'Three', (4+0j), 5.0]

Append will added to the end of the list but we can put our new item anywhere in our list using .insert(<index>,<elemment>). To delete an element del() can be used ; note that this is not an attribute of the list and can also be used on dictionaries.

mylist.insert(2,2.5)
print(mylist)
del(mylist[2])
print(mylist)
[1, -2.0, 2.5, 'Three', (4+0j), 5.0]
[1, -2.0, 'Three', (4+0j), 5.0]

For sets the structure is very similar with .add() and .remove().

print(myset)
myset.add(6)
print(myset)
myset.remove(6)
print(myset)
{2.0, 4, 4.1, 'copy', (5+2j), 3.4e-06}
{2.0, 4, 4.1, 'copy', 6, (5+2j), 3.4e-06}
{2.0, 4, 4.1, 'copy', (5+2j), 3.4e-06}

For dictionaries we can simply make a new key and use pop() to remove. One can also use del() to delete as noted above.

print(mydictionary)
mydictionary['w'] = 9.0
print(mydictionary)
mydictionary.pop('w')
print(mydictionary)
{'x': 7.0, 'y': 53.0, 'z': 2.0}
{'x': 7.0, 'y': 53.0, 'z': 2.0, 'w': 9.0}
{'x': 7.0, 'y': 53.0, 'z': 2.0}

Slicing and Reorganising#

We can slice (or obtain section of) a list or tuple using the following syntax.

sublist = mylist[0:3] # first three elements
print('mylist =', mylist)
print('sublist =', sublist, '\n') # \n makes a new line

subtuple = mytuple[2:4] # last two elements
print('mytuple =', mytuple)
print('subtuple =', subtuple)
mylist = [1, -2.0, 'Three', (4+0j), 5.0]
sublist = [1, -2.0, 'Three'] 

mytuple = (4.1, 6.626e-34, -3, 6.6)
subtuple = (-3, 6.6)

Or we slice incrementaly.

#This operation extracts the 0th element and takes two steps to extract the third and then fifth element.
print(mylist[0::2])
#Simlary, starting at index 1
print(mylist[1::2])
#If an index is omitted from the operation, it will be interpreted as 0, for example:
print(mylist[::2])
[1, 'Three', 5.0]
[-2.0, (4+0j)]
[1, 'Three', 5.0]

For reversing and sorting lists we will need a real numerical list.

numlist = [-20, 6.8e10,6.0,19]
print('list =', numlist)
numlist.reverse() # reverses order of elements in the list
print('reversed list =', numlist)
numlist.sort() # places list in asending order
print('sorted list =', numlist)
list = [-20, 68000000000.0, 6.0, 19]
reversed list = [19, 6.0, 68000000000.0, -20]
sorted list = [-20, 6.0, 19, 68000000000.0]

We can also wipe a list clean by using clear().

numlist.clear()
print(numlist)
[]

Combining Structures#

We can concatenate lists, as shown

L = [7.0,8.0,9.0]
M = [1.0,5.0,9.0,7.0]

concat = L + M
print(concat)
[7.0, 8.0, 9.0, 1.0, 5.0, 9.0, 7.0]

Lets define two sets, using these lists, and calulate their union and intersection.

L = set(L) #recasting list as a set with the same elements
M = set(M)

un = L.union(M)#Returns a union set of the two sets
inter = L.intersection(M)#Returns the intersection of the two sets

print(un)
print(inter)
{1.0, 5.0, 7.0, 8.0, 9.0}
{9.0, 7.0}

Can calculate difference between sets.

print(L.difference(M))#Returns elements in L that are not in the M

print(L.symmetric_difference(M))#Returns elements that are only in one of the sets
{8.0}
{1.0, 5.0, 8.0}

Can also do following actions to sets

print(L.issubset(M))#Checks if L is a subset of the M

print(L.isdisjoint(M))#Checks if L is a disjoint of the M

print(L.issuperset(M))#Checks if L is a superset of the M
False
False
False

Copying#

Copying these data structures can be tricky and it requires some care to avoid bugs.

a = [1,2]
b = a

a[1] = 3 

print(a)
print(b)
[1, 3]
[1, 3]

This is cleary not the desired result; it seems a and b are now inextricably linked together by the = assignment. This is because when we refer to a in our code we are refering to the place where the list a is stored in our memory, we have therefore just com. This can be fixed by using a.copy() which will return a copy of the list which is what is desired.

a = [1,2]
b = a.copy()

a[1] = 3 

print(a)
print(b)
[1, 3]
[1, 2]