Python cheatsheet¶
[designed specifically for understanding and modifying simple-dmrg]
For a programmer, the standard, online Python tutorial is quite nice. Below, we try
to mention a few things so that you can get acquainted with the
simple-dmrg
code as quickly as possible.
Python includes a few powerful internal data structures (lists,
tuples, and dictionaries), and we use numpy
(numeric python) and
scipy
(additional “scientific” python routines) for linear
algebra.
Basics¶
Unlike many languages where blocks are denoted by braces or special
end
statements, blocks in python are denoted by indentation level.
Thus indentation and whitespace are significant in a python program.
It is possible to execute python directly from the commandline:
$ python
This will bring you into python’s real-eval-print loop (REPL). From
here, you can experiment with various commands and expressions. The
examples below are taken from the REPL, and include the prompts
(“>>>
” and “...
”) one would see there.
Lists, tuples, and loops¶
The basic sequence data types in python are lists and tuples.
A list
can be constructed literally:
>>> x_list = [2, 3, 5, 7]
and a number of operations can be performed on it:
>>> len(x_list)
4
>>> x_list.append(11)
>>> x_list
[2, 3, 5, 7, 11]
>>> x_list[0]
2
>>> x_list[0] = 0
>>> x_list
[0, 3, 5, 7, 11]
Note, in particular, that python uses indices counting from zero, like C (but unlike Fortran and Matlab).
A tuple
in python acts very similarly to a list, but once it is constructed it cannot be modified. It is constructed using parentheses instead of brackets:
>>> x_tuple = (2, 3, 5, 7)
Lists and tuples can contain any data type, and the data type of the elements need not be consistent:
>>> x = ["hello", 4, 8, (23, 12)]
It is also possible to get a subset of a list (e.g. the first three elements) by using Python’s slice notation:
>>> x = [2, 3, 5, 7, 11]
>>> x[:3]
[2, 3, 5]
Looping over lists and tuples¶
Looping over a list
or tuple
is quite straightforward:
>>> x_list = [5, 7, 9, 11]
>>> for x in x_list:
... print(x)
...
5
7
9
11
If you wish to have the corresponding indices for each element of the
list, the enumerate()
function will provide this:
>>> x_list = [5, 7, 9, 11]
>>> for i, x in enumerate(x_list):
... print(i, x)
...
0 5
1 7
2 9
3 11
If you have two (or more) parallel arrays with the same number of
elements and you want to loop over each of them at once, use the
zip()
function:
>>> x_list = [2, 3, 5, 7]
>>> y_list = [12, 13, 14, 15]
>>> for x, y in zip(x_list, y_list):
... print(x, y)
...
2 12
3 13
5 14
7 15
There is a syntactic shortcut for transforming a list into a new one, known as a list comprehension:
>>> primes = [2, 3, 5, 7]
>>> doubled_primes = [2 * x for x in primes]
>>> doubled_primes
[4, 6, 10, 14]
Dictionaries¶
Dictionaries are python’s powerful mapping data type. A number, string, or even a tuple can be a key, and any data type can be the corresponding value.
Literal construction syntax:
>>> d = {2: "two", 3: "three"}
Lookup syntax:
>>> d[2]
'two'
>>> d[3]
'three'
Modifying (or creating) elements:
>>> d[4] = "four"
>>> d
{2: 'two', 3: 'three', 4: 'four'}
The method get()
is another way to lookup an element, but returns
the special value None
if the key does not exist (instead of
raising an error):
>>> d.get(2)
'two'
>>> d.get(4)
Looping over dictionaries¶
Looping over the keys of a dictionary:
>>> d = {2: "two", 3: "three"}
>>> for key in d:
... print(key)
...
2
3
Looping over the values of a dictionary:
>>> d = {2: "two", 3: "three"}
>>> for value in d.values():
... print(value)
...
two
three
Looping over the keys and values, together:
>>> d = {2: "two", 3: "three"}
>>> for key, value in d.items():
... print(key, value)
...
2 two
3 three
Functions¶
Function definition in python uses the def
keyword:
>>> def f(x):
... y = x + 2
... return 2 * y + x
...
Function calling uses parentheses, along with any arguments to be passed:
>>> f(2)
10
>>> f(3)
13
When calling a function, it is also possibly to specify the arguments by name (e.g. x=4
):
>>> f(x=4)
16
An alternative syntax for writing a one-line function is to use python’s lambda
keyword:
>>> g = lambda x: 3 * x
>>> g(5)
15
numpy arrays¶
numpy
provides a multi-dimensional array type. Unlike lists and
tuples, numpy
arrays have fixed size and hold values of a single
data type. This allows the program to perform operations on large
arrays very quickly.
Literal construction of a 2x2 matrix:
>>> np.array([[1, 2], [3, 4]], dtype='d')
array([[ 1., 2.],
[ 3., 4.]])
Note that dtype='d'
specifies that the type of the array should
be double-precision (real) floating point.
It is also possibly to construct an array of all zeros:
>>> np.zeros([3, 4], dtype='d')
array([[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.],
[ 0., 0., 0., 0.]])
And then elements can be added one-by-one:
>>> x = np.zeros([3, 4], dtype='d')
>>> x[1, 2] = 12
>>> x[1, 3] = 18
>>> x
array([[ 0., 0., 0., 0.],
[ 0., 0., 12., 18.],
[ 0., 0., 0., 0.]])
It is possible to access a given row or column by index:
>>> x[1, :]
array([ 0., 0., 12., 18.])
>>> x[:, 2]
array([ 0., 12., 0.])
or to access multiple columns (or rows) at once:
>>> col_indices = [2, 1, 3]
>>> x[:, col_indices]
array([[ 0., 0., 0.],
[ 12., 0., 18.],
[ 0., 0., 0.]])
For matrix-vector (or matrix-matrix) multiplication use the
np.dot()
function:
>>> np.dot(m, v)
Warning
One tricky thing about numpy
arrays is that they do not act as
matrices by default. In fact, if you multiply two numpy
arrays, python will attempt to multiply them element-wise!
To take an inner product, you will need to take the transpose-conjugate of the left vector yourself:
>>> np.dot(v1.conjugate().transpose(), v2)
Array storage order¶
Although a numpy
array acts as a multi-dimensional object, it is
actually stored in memory as a one-dimensional contiguous array.
Roughly speaking, the elements can either be stored column-by-column
(“column major”, or “Fortran-style”) or row-by-row (“row major”, or
“C-style”). As long as we understand the underlying storage order of
an array, we can reshape it to have different dimensions. In
particular, the logic for taking a partial trace in simple-dmrg
uses this reshaping to make the system and environment basis elements
correspond to the rows and columns of the matrix, respectively. Then,
only a simple matrix multiplication is required to find the reduced
density matrix.
Mathematical constants¶
numpy
also provides a variety of mathematical constants:
>>> np.pi
3.141592653589793
>>> np.e
2.718281828459045
Experimentation and getting help¶
As mentioned above, python’s REPL can be quite useful for
experimentation and getting familiar with the language. Another thing
we can do is to import the simple-dmrg
code directly into the REPL
so that we can experiment with it directly. The line:
>>> from simple_dmrg_01_infinite_system import *
will execute all lines except the ones within the block that says:
if __name__ == "__main__":
So if we want to use the finite system algorithm, we can (assuming our
source tree is in the PYTHONPATH
, which should typically include
the current directory):
$ python
>>> from simple_dmrg_04_eigenstate_prediction import *
>>> finite_system_algorithm(L=10, m_warmup=8, m_sweep_list=[8, 8, 8])
It is also possible to get help in the REPL by using python’s built-in
help()
function on various objects, functions, and types:
>>> help(sum) # help on python's sum function
>>> help([]) # python list methods
>>> help({}) # python dict methods
>>> help({}.setdefault) # help on a specific dict method
>>> import numpy as np
>>> help(np.log) # natural logarithm
>>> help(np.linalg.eigh) # eigensolver for hermitian matrices