Python Basics 1612354525
Python Basics 1612354525
Patrick Farrell
May 8, 2017
Morning Afternoon
Monday Datatypes, functions Modules, plotting, iteration
Tuesday Object orientation, NumPy SciPy, finite differences
Wednesday Poisson equation Heat equation
Thursday p-Laplace, hyperelasticity Stokes
Friday Navier–Stokes Optimal control
Morning Afternoon
Monday Datatypes, functions Modules, plotting, iteration
Tuesday Object orientation, NumPy SciPy, finite differences
Wednesday Poisson equation Heat equation
Thursday p-Laplace, hyperelasticity Stokes
Friday Navier–Stokes Optimal control
I No TAs: please help each other! (Except the exercises that are
submitted via email)
I Feedback (good or bad) welcome:
[email protected]
MMSC: for this to count for your MSc, you have to write a final report.
Your task:
I Implement a finite element discretisation of a PDE.
I Take a discretisation and configuration from the literature, or invent
your own.
I Bonus points for: nonlinearity, coupling, mathematical interest, . . .
I Submit working code and a report (∼ 20 pages) discussing the PDE
and its implementation.
I Inspiration: your summer projects; the PDE coffee table book.
Patrick Farrell
May 8, 2017
def factorial(n):
out = 1
while n >= 1:
out = out * n
n = n - 1
return out
print type(factorial(20))
print type(factorial(21))
produces
<type ’int’>
<type ’long’>
Very useful!
P. E. Farrell (Oxford) Python I May 8, 2017 7 / 21
Booleans and conditionals
V = x2 : x ∈ S
Lists are mutable: you can change them in-place. A tuple is an immutable
list. You can create and unpack tuples like so:
>>> t = (0, 1)
>>> t[0] = 2
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: ’tuple’ object does not support item assignment
>>> (a, b) = t
>>> a
0
Patrick Farrell
May 8, 2017
This prints
args: (’hello’, ’world’)
kwargs: {’argument’: 4, ’another’: ’black’}
def circle_area(radius):
area = pi * radius**2 # oops! forgot to return
def complex_to_polar(z):
r = sqrt(z.real**2 + z.imag**2)
phi = atan2(z.imag, z.real)
return (r, phi) # the tuple is one object
a = 54
b = [’hello’]
c = [’world’]
try_to_modify(a, b, c)
a == 54 # True
b == [’hello’] # False
c == [’world’] # True
Be careful!
def circle_stats(radius):
"""This function computes the perimeter and area of a circle.
Usage:
Python functions are objects like any other, and we can interrogate them
for their documentation:
print circle_stats.__doc__
help(circle_stats)
P. E. Farrell (Oxford) Python II May 8, 2017 7 / 15
Functions are objects
def myfun(x):
return x + 1
myfun = deprecated(myfun)
myfun(2)
myfun(2)
if n == 0:
return 1.0
elif n == 1:
return x
else:
return 2.0 * x * chebyshev(n - 1, x) - chebyshev(n - 2, x)
return cache[key]
return memoised_fun
@memoise
def chebyshev(n, x):
...
@profile
def test():
sleep()
sleep()
sleep()
# Equivalently
square = lambda x: x*x
Patrick Farrell
May 8, 2017
We can also import objects from another namespace into our current
namespace. For example:
from math import sin, pi
print(sin(pi))
or even
from math import *
print(sin(pi))
We can import modules with different names to their installed name. For
example:
import math as m
print(m.sin(m.pi))
For non-built-in modules, you can also find out where its code lives:
import scipy
print(scipy.__file__)
print(scipy.__version__)
You can make your own modules by putting your code in a file. Use your
favourite text editor to create a file fib.py:
def fibonacci(u0, u1, N):
l = [u0, u1]
for i in range(N-1):
l.append(l[-1] + l[-2])
return l
if __name__ == "__main__":
# won’t get executed on ’import fib’
f10 = fibonacci(1, 1, 10)
assert f10[-1] == 89
In the rest of this lecture we’ll look at one important module, matplotlib.
from scipy import *
from matplotlib.pyplot import *
samples = x[::4]
plot(samples, sin(samples), ’bo’, markersize=10)
title("sin(x)")
grid()
savefig("sin.pdf")
1.0 sin(x)
0.5
0.0
0.5
1.0 8 6 4 2 0 2 4 6 8
P. E. Farrell (Oxford) Python III May 8, 2017 9 / 15
Plotting
x = range(5)
y = [1, 2, 1, 3, 5]
p2 = polyfit(x, y, 2)
p4 = polyfit(x, y, 4)
xx = linspace(-1, 5, 200)
plot(xx, polyval(p2, xx), label="degree 2")
plot(xx, polyval(p4, xx), label="degree 4")
plot(x, y, ’b*’, markersize=10)
axis([-1, 5, 0,6])
legend(loc=’upper left’)
title(’Polynomial fitting’)
6 Polynomial fitting
degree 2
degree 4
5
01 0 1 2 3 4 5
P. E. Farrell (Oxford) Python III May 8, 2017 11 / 15
Plotting
from scipy import *
from matplotlib.pyplot import *
m.drawcoastlines()
m.drawcountries()
m.fillcontinents()
Patrick Farrell
May 8, 2017
return c
Sometimes we need to exit loops early. For example, interacting with users
could look like:
while True:
s = raw_input("Enter a string (quit to quit): ")
if s == "quit":
break
print("String length: %s" % len(s))
Sometimes we want to skip one particular iteration of a loop, but not exit
from the entire iteration:
for i in range(100):
if isprime(i):
continue
for loops aren’t just for iterating over lists; we can iterate over any
iterable object:
for val in tuple:
...
In particular, we can iterate over an object that creates each value of the
iteration on-the-fly. Such an object is called a generator:
for i in range(1000000000):
# stores the entire list 0 to
# 1000000000 in memory all at ance
for i in xrange(1000000000):
# yields each item one at a time,
# O(1) memory usage
You can define your own generators with the yield keyword.
def fibonacci(u0, u1):
yield u0
yield u1
while True:
(u0, u1) = (u1, u0 + u1)
yield u1
In this special case (where the generator is the only argument), we can
drop the brackets:
sum(x*x for x in xrange(10))
reversed takes in a finite iterable and goes through this list backwards.
A = [’a’, ’b’, ’c’]
for x in reversed(A):
print(x)
# result: c b a
sorted takes in a finite iterable and returns an iterator that yields the
elements in order.
A = [’c’, ’b’, ’a’]
for x in sorted(A):
print(x)
# result: a b c
next takes in an iterator and returns the next value of the sequence.
from itertools import count
counter = count()
i = next(counter)
print(i) # 0
i = next(counter)
print(i) # 1
Write a generator that takes in a generator for a sequence and yields the
Aitken acceleration of that sequence. As example input, use the sequence
N
X (−1)n
sN = ,
2n + 1
n=0
Compute the answer in one line of Python that uses O(1) memory.
(Without googling.)
For those taking the FEM part: please install (in order):
I Docker (Community Edition)
(http://www.docker.com/community-edition)
I FEniCS, using the fenicsproject script
(http://www.fenicsproject.org/download)
I ParaView
(http://www.paraview.org/download)
Patrick Farrell
May 8, 2017
Objects that share the same methods are grouped into classes.
Consider a polynomial p(x). This is just like the sin function — every
function method also applies to p. But we can define other, special
methods for p that we cannot for sin: for example,
I return p’s coefficients, a list of real numbers
I factorise p, returning a list of polynomial factors
In mathematics, we often use the same symbol for different things. For
example, 5 + 4 and sin + cos have different meanings. But by using the
same symbol we express similarities of the mathematical operations.
ui+1 = ui + hφ(f, ui , ti , h)
We will model this in Python with an abstract base class that collects the
methods common to all one-step methods, and make subclasses to fill in
the parameters for the concrete method.
P. E. Farrell (Oxford) Python V May 8, 2017 12 / 16
from numpy import linspace
class OneStepMethod(object):
def __init__(self, f, x0, interval, N):
self.f = f
self.x0 = x0
self.interval = [t0, tT] = interval
self.grid = linspace(t0, tT, N)
self.h = (tT - t0) / N
def generate(self):
(ti, ui) = (self.grid[0], self.x0)
yield (ti, ui)
for t in self.grid[1:]:
ui = ui + self.h * self.step(self.f, ui, ti, self.h)
ti = t
yield (ti, ui)
def solve(self):
return list(self.generate())
class MidPointRule(OneStepMethod):
def step(self, f, u, t, h):
return f(u + h/2.0 * f(u, t), t + h/2.0)
Patrick Farrell
May 8, 2017
One error all programmers see is where code has incorrect syntax:
>>> for i in range(10)
File "<stdin>", line 1
for i in range(10)
^
SyntaxError: invalid syntax
# later on ...
if iters > max_iters:
raise ConvergenceError("Algorithm did not converge")
The open function returns a context manager that ensures that the file
handle is closed when the block terminates.
with lock:
# do something with the protected resource
with errstate(invalid=’ignore’):
print(sqrt(-1)) # prints ’nan’
with errstate(invalid=’warn’):
print(sqrt(-1)) # prints ’nan’ and ’RuntimeWarning’
with errstate(invalid=’raise’):
print(sqrt(-1)) # raises FloatingPointError
def g():
h()
def h():
1/0
f()
def complex_to_polar(z):
pdb.set_trace()
r = sqrt(z.real**2 + z.imag**2)
phi = atan2(z.imag, z.real)
return (r, phi)
complex_to_polar(3 + 5j)
Patrick Farrell
May 8, 2017
Much of the scientific stack for Python is built on top of numpy. numpy
provides highly optimised implementations of the fundamental datatypes
for linear algebra: vectors, matrices, and their higher-dimensional
analogues.
>>> import numpy as np
>>> a = np.array([0, 1, 2, 3])
>>> a
array([0, 1, 2, 3])
Numpy arrays have a datatype. With the array constructor, numpy creates
arrays with the smallest datatype that can contain the given data.
>>> a = np.array([1, 2, 3])
>>> a.dtype
dtype(’int64’)
>>> a[0] = 0.5 # danger! will be cast to int64
>>> a
array([0, 2, 3])
>>> b = np.array([1., 2., 3.])
>>> b.dtype
dtype(’float64’)
return np.nonzero(is_prime)[0]
if __name__ == "__main__":
import sys
N = int(sys.argv[1]) # read in from command-line
print(primes(N))
P. E. Farrell (Oxford) Python VII May 8, 2017 6 / 15
Fancy indexing
Numpy arrays can also be indexed with Boolean or integer arrays (masks).
These create copies, not views.
>>> a = np.random.random_integers(0, 20, 15)
>>> a
array([10, 3, 8, 0, 19, 10, 11, 9, 10, 6, 0, 20, 12, 7, 14])
>>> (a % 3 == 0)
array([False, True, False, True, False, False, False, True, False,
True, True, False, True, False, False], dtype=bool)
>>> mask = (a % 3 == 0)
>>> extract_from_a = a[mask] # or, a[a%3==0]
>>> extract_from_a # extract a sub-array with the mask
array([ 3, 0, 9, 6, 0, 12])
0 1 2 + 5 5 5 = 5 6 7
1 1 1 0 1 2 1 2 3
1 1 1 + 0 1 2 = 1 2 3
1 1 1 0 1 2 1 2 3
0 0 0 0 1 2 0 1 2
1 1 1 + 0 1 2 = 1 2 3
2 2 2 0 1 2 2 3 4
>>> a.shape
(2, 3)
>>> b = a.ravel()
>>> b = b.reshape((2, 3))
>>> b
array([[1, 2, 3],
[4, 5, 6]])
>>> b[0, 0] = 99
>>> a
array([[99, 2, 3],
[ 4, 5, 6]])
Patrick Farrell
May 8, 2017
NumPy provides the core dense multidimensional array class for Python.
SciPy provides basically everything else:
Determinants:
>>> from scipy import linalg
>>> arr = np.array([[1, 2],
... [3, 4]])
>>> linalg.det(arr)
-2.0
>>> arr = np.array([[3, 2],
... [6, 4]])
>>> linalg.det(arr)
0.0
>>> linalg.det(np.ones((3, 4)))
Traceback (most recent call last):
...
ValueError: expected square matrix
Matrix inverses:
>>> arr = np.array([[1, 2],
... [3, 4]])
>>> iarr = linalg.inv(arr)
>>> iarr
array([[-2. , 1. ],
[ 1.5, -0.5]])
>>> np.allclose(np.dot(arr, iarr), np.eye(2))
True
import scipy.sparse as sp
import scipy.sparse.linalg as la
import numpy as np
K = K.tocsr()
u = la.spsolve(K, rhs)