0% found this document useful (0 votes)
22 views

Python Basics 1612354525

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
22 views

Python Basics 1612354525

Copyright
© © All Rights Reserved
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 129

Introduction to Python: preliminaries

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python 0 May 8, 2017 1/6


Rough schedule

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

P. E. Farrell (Oxford) Python 0 May 8, 2017 2/6


Rough schedule

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

P. E. Farrell (Oxford) Python 0 May 8, 2017 2/6


Timetable

Basic plan: 10:00 — 12:30; 14:00 — 17:00.

P. E. Farrell (Oxford) Python 0 May 8, 2017 3/6


Installing Python

If you haven’t installed Python: download Anaconda from


http://www.continuum.io/downloads.

Use the package for Python 2.7. Edit code in Spyder.

P. E. Farrell (Oxford) Python 0 May 8, 2017 4/6


Comments

I No TAs: please help each other! (Except the exercises that are
submitted via email)
I Feedback (good or bad) welcome:
[email protected]

P. E. Farrell (Oxford) Python 0 May 8, 2017 5/6


Preview: final challenge!

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.

P. E. Farrell (Oxford) Python 0 May 8, 2017 6/6


Introduction to Python: native datatypes

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python I May 8, 2017 1 / 21


What is Python?

I Very high-level language


I Powerful complex data structures
I Supports imperative, functional, object-oriented programming
I Dynamic, extremely rapid development
I Free (very useful for parallel)
I Main scripting language used in large-scale scientific computing
I Beautiful, and fun . . .

P. E. Farrell (Oxford) Python I May 8, 2017 2 / 21


P. E. Farrell (Oxford) Python I May 8, 2017 3 / 21
Running Python

Option 1: run interactively Option 2: put code in a file and run

$ ipython $ cat hello.py


In [1]: print("Hello, world") print("Hello, world")
Hello, world $ python hello.py
Hello, world

(mainly for development) (mainly for long runs)

P. E. Farrell (Oxford) Python I May 8, 2017 4 / 21


Interactive calculator

>>> 1 + 1 >>> c = 2 + 1.5j


2 >>> type(c)
>>> a = 1 <type ’complex’>
>>> a >>> c.imag
1 1.5
>>> type(a) >>> c.conjugate()
<type ’int’> (2-1.5j)
>>> 5 % 3 >>> s = "Hello, world!"
2 >>> type(s)
>>> b = 17.03 + 19.85 <type ’str’>
>>> type(b) >>> len(s)
<type ’float’> 13
>>> int(b) >>> s.split()
36 [’Hello,’, ’world!’]

P. E. Farrell (Oxford) Python I May 8, 2017 5 / 21


Integer division
Be aware of integer division (à la C/C++):
>>> 1/2
0
>>> # ?!?!
>>> 1.0/2
0.5
>>> 1.0/2.0
0.5

Use // to unambiguously specify integer division:


>>> 1.0//2.0
0.0

Python has a command line to make this issue a warning:


$ python -Q warn -c "print 1/2"
-c:1: DeprecationWarning: classic int division
P. E. Farrell (Oxford) Python I May 8, 2017 6 / 21
Integer promotion

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

Booleans behave as you expect:


>>> not False
True
>>> True and False
False
>>> True or False
True

Booleans can be used for conditionals:


>>> x = 5
>>> if 4 < x < 6:
... print "x \in [4, 6]"
...
x \in [4, 6]

P. E. Farrell (Oxford) Python I May 8, 2017 8 / 21


Strings and slicing
You can make literal strings in several ways:
>>> a = "This wouldn’t work with ’"
>>> b = ’"Arrest that man!", he cried.’
>>> c = """
... A large block
... of text
... """

Strings can be indexed and subscripted:


>>> b[0]
’"’
>>> b[1:7]
’Arrest’
>>> b[-6:]
’cried.’
P. E. Farrell (Oxford) Python I May 8, 2017 9 / 21
More strings

The Python str class is very powerful.


>>> dir(b)
... # too many methods to list
>>> b.swapcase()
’"aRREST THAT MAN!", HE CRIED.’
>>> b.replace("man", "donkey")
’"Arrest that donkey!", he cried.’
>>> print b + ’’’ "Why? He’s done nothing wrong."’’’
"Arrest that man!", he cried. "Why? He’s done nothing wrong."
>>> b.split(’!’)
[’"Arrest that man’, ’", he cried.’]

P. E. Farrell (Oxford) Python I May 8, 2017 10 / 21


String interpolation

Strings can be interpolated:


>>> profit = 90210
>>> print("Our annual profit was: EUR %s." % profit)
Our annual profit was: EUR 90210.
>>> from math import pi
>>> print("Pi: %1.5f" % pi)
Pi: 3.14159

P. E. Farrell (Oxford) Python I May 8, 2017 11 / 21


Compound types: lists
Python has very powerful, fast native data structures. Here we’ll introduce
four: lists, tuples, dictionaries and sets.
A list contains, well, a list of objects of any kind:
>>> l = [1, 1, 2, 3, 5, 8]
>>> l += [’can’, ’be’, ’different’]
>>> l
[1, 1, 2, 3, 5, 8, ’can’, ’be’, ’different’]
>>> l[0]
1
>>> l[-1]
’different’
>>> range(4)
[0, 1, 2, 3]
>>> len(range(4))
4
P. E. Farrell (Oxford) Python I May 8, 2017 12 / 21
Compound types: lists
A list has several methods that modify it in-place:
>>> l.sort()
>>> l.reverse()
>>> l
[’different’, ’can’, ’be’, 8, 5, 3, 2, 1, 1]
>>> l[-1] = 10

Lists can be sliced and joined, too:


>>> l = [’c’, ’l’, ’o’, ’u’, ’d’, ’s’]
>>> l[1:5]
[’l’, ’o’, ’u’, ’d’]
>>> ’-’.join(l)
’c-l-o-u-d-s’
>>> ’’.join(l)
’clouds’
P. E. Farrell (Oxford) Python I May 8, 2017 13 / 21
Compound types: lists

The Python keyword for ∈ is in:


>>> l = range(5, 10)
>>> l
[5, 6, 7, 8, 9]
>>> 7 in l
True
>>> 10 in l
False

P. E. Farrell (Oxford) Python I May 8, 2017 14 / 21


List iteration

Lists can be iterated over:


>>> res = 0
>>> for i in l:
... res = res + i
...
>>> res
35
>>> sum(l)
35

P. E. Farrell (Oxford) Python I May 8, 2017 15 / 21


List comprehension

In mathematics, we write statements like:

V = x2 : x ∈ S


In Python, we can write analogous code: list comprehensions.


>>> l = range(4)
>>> [x**2 for x in l]
[0, 1, 4, 9]
>>> [x**2 for x in l if x % 2 == 0]
[0, 4]

P. E. Farrell (Oxford) Python I May 8, 2017 16 / 21


Dictionaries
A dictionary is an unordered table that maps keys to values. The keys
have to be hashable.
>>> numbers = {"Carol": "2714", "Hannah": "3852", "Marie": "7268"}
>>> numbers["Carol"]
’2714’
>>> "Carol" in numbers
True
>>> numbers.keys()
[’Hannah’, ’Carol’, ’Marie’]
>>> numbers.values()
[’3852’, ’2714’, ’7268’]
>>> for name in numbers:
... number = numbers[name]
... print("%s -> %s" % (name, number))
...
Hannah -> 3852
Carol -> 2714
Marie -> 7268

P. E. Farrell (Oxford) Python I May 8, 2017 17 / 21


Dictionaries

The dictionary can natively map different types to different types.


>>> mydict = {’a’: 1, True: "hello"}
>>>

P. E. Farrell (Oxford) Python I May 8, 2017 18 / 21


Tuples

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

Because they can’t be changed after creation, tuples are used:


I to pass arguments into functions
I to return values from functions
I as keys in dictionaries.

P. E. Farrell (Oxford) Python I May 8, 2017 19 / 21


Sets

Our final in-built container type is the set.


>>> {1, 2, 3, 3, 2} == {1, 2, 3}
True
>>> {1, 2, 3} == {1, 3, 2}
True
>>> {1, 3, 5}.union({2, 4})
set([1, 2, 3, 4, 5])

P. E. Farrell (Oxford) Python I May 8, 2017 20 / 21


Python 01 Challenge!
Consider the recursion formula
 
23 4 5
un+3 = un+2 + ha un+2 − un+1 + un ,
12 3 12
with n = 0, . . . , 1000, h = 1/1000, a = −1/2, u0 = exp (0),
u1 = exp (ha), u2 = exp (2ha).
(a) Create a list approx with the values of u, starting with the three
given values and completing it with the recursion formula.
(b) Create another list exact with values exp (anh).
(c) Create another list error with the difference between the two lists.
Hints:
from math import exp
for (e, a) in zip(exact, approx):
...

P. E. Farrell (Oxford) Python I May 8, 2017 21 / 21


Introduction to Python: functions

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python II May 8, 2017 1 / 15


Functions

Functions are a central idea in mathematics.


def subtract(x1, x2):
return x1 - x2

Note that functions, like all other blocks, must be indented.

By default, arguments are assigned to parameters by order:


subtract(5.0, 4.3) # returns 0.7

We can also specify arguments by keyword:


subtract(x2=4.3, x1=5.0) # returns 0.7

P. E. Farrell (Oxford) Python II May 8, 2017 2 / 15


Input to functions
Default values for parameters can be specified:
def slicer(seq, start=None, stop=None, step=None):
return seq[start:stop:step]

seq = [str(i) for i in range(5)]


print(slicer(seq)) # [’0’, ’1’, ’2’, ’3’, ’4’]
print(slicer(seq, start=2)) # [’2’, ’3’, ’4’]
print(slicer(seq, start=2, stop=4)) # [’2’, ’3’]

You can also mix positional and keyword arguments:


slicer(seq, 4, step=-1, stop=1) # [’4’, ’3’, ’2’]

We can also pass arguments in a dictionary:


d = {’start’: 4, ’stop’: 1, ’step’: -1}
slicer(seq, **d) # [’4’, ’3’, ’2’]
P. E. Farrell (Oxford) Python II May 8, 2017 3 / 15
Input to functions

Functions can take a variable number of arguments, too:


def printer(*args, **kwargs):
print("args: %s" % (args,))
print("kwargs: %s" % kwargs)

printer(’hello’, ’world’, argument=4, another=’black’)

This prints
args: (’hello’, ’world’)
kwargs: {’argument’: 4, ’another’: ’black’}

P. E. Farrell (Oxford) Python II May 8, 2017 4 / 15


Output from functions
A function always returns one single object.
By default, functions return None:
from math import pi

def circle_area(radius):
area = pi * radius**2 # oops! forgot to return

area = circle_area(1.0) # returns None

If you want to return more than one value, construct a tuple:


from math import sqrt, atan2

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

P. E. Farrell (Oxford) Python II May 8, 2017 5 / 15


Functions modifying inputs

Functions can modify mutable arguments (can be dangerous!).


def try_to_modify(a, b, c):
a = 23
b.append(’gotcha’)
c = [99, ’problems’]

a = 54
b = [’hello’]
c = [’world’]
try_to_modify(a, b, c)
a == 54 # True
b == [’hello’] # False
c == [’world’] # True

Be careful!

P. E. Farrell (Oxford) Python II May 8, 2017 6 / 15


Documenting functions
Functions carry their own documentation:
from math import pi

def circle_stats(radius):
"""This function computes the perimeter and area of a circle.

Usage:

(perimeter, area) = circle_stats(radius)


"""

return (2*pi*radius, pi*radius**2)

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

Functions are objects and can be passed in to other functions.


def deprecated(fun):
def wrapped_fun(*args, **kwargs):
print("Warning: function %s is deprecated." % fun)
return fun(*args, **kwargs)
return wrapped_fun

def myfun(x):
return x + 1

myfun = deprecated(myfun)
myfun(2)

P. E. Farrell (Oxford) Python II May 8, 2017 8 / 15


Functions are objects

This usage of functions modifying functions has a shorthand: decorators.


def deprecated(fun):
def wrapped_fun(*args, **kwargs):
print("Warning: function %s is deprecated." % fun)
fun(*args, **kwargs)
return wrapped_fun

@deprecated # equivalent to myfun = deprecated(myfun)


def myfun(x):
return x + 1

myfun(2)

P. E. Farrell (Oxford) Python II May 8, 2017 9 / 15


Recursive functions
Functions can call themselves:
def chebyshev(n, x):
"""This function computes the Chebyshev polynomial of
degree n at a point x with the recursion formula

T_n(x) = 2*x*T_{n-1}(x) - T_{n-2}(x)"""

print("chebyshev(%s, %s) called" % (n, x))

if n == 0:
return 1.0
elif n == 1:
return x
else:
return 2.0 * x * chebyshev(n - 1, x) - chebyshev(n - 2, x)

Recursive definitions are mathematically elegant but can be


computationally wasteful.
P. E. Farrell (Oxford) Python II May 8, 2017 10 / 15
Functions are objects
Let’s make it much faster with a decorator!
def memoise(fun):
cache = {}

def memoised_fun(*args, **kwargs):


kitems = tuple(kwargs.items())
key = (args, kitems)
if key not in cache:
cache[key] = fun(*args, **kwargs)

return cache[key]

return memoised_fun

@memoise
def chebyshev(n, x):
...

Introspection and dynamism are very very powerful.


P. E. Farrell (Oxford) Python II May 8, 2017 11 / 15
Useful decorators
Two extremely useful decorators: line_profiler and memory_profiler.
from line_profiler import profile
def sleep():
seconds = random.randint(0, 5); time.sleep(seconds)

@profile
def test():
sleep()
sleep()
sleep()

Line # Hits Time Per Hit % Time Line Contents


==============================================================
8 @profile
9 def test():
10 1 3999416 3999416.0 36.4 sleep()
11 1 4999982 4999982.0 45.5 sleep()
12 1 1999990 1999990.0 18.2 sleep()

P. E. Farrell (Oxford) Python II May 8, 2017 12 / 15


Useful decorators

from memory_profiler import profile


@profile
def my_func():
a = [1] * (10 ** 6)
b = [2] * (2 * 10 ** 7)
del b
return a

Line # Mem usage Increment Line Contents


==============================================
3 @profile
4 5.97 MB 0.00 MB def my_func():
5 13.61 MB 7.64 MB a = [1] * (10 ** 6)
6 166.20 MB 152.59 MB b = [2] * (2 * 10 ** 7)
7 13.61 MB -152.59 MB del b
8 13.61 MB 0.00 MB return a

P. E. Farrell (Oxford) Python II May 8, 2017 13 / 15


Anonymous functions

Functions can be defined anonymously (like matlab @):


def square(x):
return x*x

# Equivalently
square = lambda x: x*x

The lambda refers to λ-calculus.

P. E. Farrell (Oxford) Python II May 8, 2017 14 / 15


Python 02 Challenge!
(a) The previous challenge implemented the 3rd-order Adams-Bashforth
algorithm on the ODE
du
= au, u(0) = 1.0, u : [0, 1] 7→ R
dt
Write a function ab3(f, u0, t0, tT, N) that solves an ODE
du
= f (u), u(0) = u0 , u : [t0 , tT ] 7→ R
dt
using the 3rd-order Adams-Bashforth discretisation. (u0 is a tuple
with the first 3 values.)
(b) The greatest common divisor of two integers can be computed using
Euclid’s algorithm:
(
a, if b = 0
gcd(a, b) =
gcd(b, a mod b), otherwise
Implement a function gcd(a, b).
P. E. Farrell (Oxford) Python II May 8, 2017 15 / 15
Introduction to Python: modules, namespaces and
plotting

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python III May 8, 2017 1 / 15


Modules
Python code is organised into modules. Python comes with many powerful
modules by default.

We can import a module with the import statement:


import math
print(math.sin(math.pi)) # 1.22e-16

The math module defines its own namespace: a collection of Python


objects (e.g. variables, functions, classes). Namespaces allow clean
separation of code with the same name:
import math
import scipy

print(math.sin(math.pi)) # only works on single numbers


print(scipy.sin([math.pi, 2*math.pi])) # works on arrays
P. E. Farrell (Oxford) Python III May 8, 2017 2 / 15
Namespaces

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))

P. E. Farrell (Oxford) Python III May 8, 2017 3 / 15


More importing

We can import modules with different names to their installed name. For
example:
import math as m
print(m.sin(m.pi))

We can also import elements of a namespace with a different name:


from math import sin as s, pi as p
print(s(p))

P. E. Farrell (Oxford) Python III May 8, 2017 4 / 15


Namespaces

We can investigate all of the objects a namespace exports with dir:


import math
print(dir(math))

For non-built-in modules, you can also find out where its code lives:
import scipy
print(scipy.__file__)
print(scipy.__version__)

P. E. Farrell (Oxford) Python III May 8, 2017 5 / 15


Making modules

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

In another file, main.py:


import fib
print(fib.fibonacci(1, 1, 10))

P. E. Farrell (Oxford) Python III May 8, 2017 6 / 15


Making modules
Each namespace has a __name__ attribute:
>>> import math
>>> math.__name__
’math’

If this module is the main script being executed as python myscript.py,


its name is set to __main__. This can be used to protect test code:
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

P. E. Farrell (Oxford) Python III May 8, 2017 7 / 15


Plotting

In the rest of this lecture we’ll look at one important module, matplotlib.
from scipy import *
from matplotlib.pyplot import *

x = linspace(-2*pi, 2*pi, 200)


plot(x, sin(x))

samples = x[::4]
plot(samples, sin(samples), ’bo’, markersize=10)

title("sin(x)")
grid()
savefig("sin.pdf")

P. E. Farrell (Oxford) Python III May 8, 2017 8 / 15


Plotting

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

from scipy import *


from matplotlib.pyplot import *

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’)

P. E. Farrell (Oxford) Python III May 8, 2017 10 / 15


Plotting

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 *

def mandelbrot(h, w, maxit=20):


’’’Returns image of Mandelbrot fractal of size (h, w)’’’
x = linspace(-2, 0.8, w)
y = linspace(-1.4, 1.4, h)
X, Y= meshgrid(x, y)
c = X + Y*1j
z = c
divtime = maxit + zeros(z.shape, dtype=int)

for iteration in xrange(maxit):


z = z**2 + c
diverge = z*conj(z) > 2**2
div_now = diverge & (divtime == maxit)
divtime[div_now] = iteration
z[diverge] = 2
return divtime
P. E. Farrell (Oxford) Python III May 8, 2017 12 / 15
Plotting

P. E. Farrell (Oxford) Python III May 8, 2017 13 / 15


Plotting

from mpl_toolkits.basemap import Basemap


from matplotlib.pyplot import *

m = Basemap(projection=’merc’, llcrnrlat=-10, urcrnrlat=80,


llcrnrlon=-140, urcrnrlon=100, lat_ts=40,
resolution=’h’, area_thresh=10000)

m.drawcoastlines()
m.drawcountries()
m.fillcontinents()

(ny_lat, ny_lon) = (40.6397, -73.7789)


(lon_lat, lon_lon) = (51.4775, 0.4614)
m.drawgreatcircle(lon_lon, lon_lat, ny_lon, ny_lat, linewidth=3)
savefig("greatcircle.pdf")

P. E. Farrell (Oxford) Python III May 8, 2017 14 / 15


Plotting

P. E. Farrell (Oxford) Python III May 8, 2017 15 / 15


Introduction to Python: iteration

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python IV May 8, 2017 1 / 18


Iteration

Iteration is what makes computers useful. Your course is largely about


designing iterations that converge to some desired quantity, after all.

As we’ve seen, the simplest iteration in Python is looping over elements of


a list:
for s in [’a’, ’b’, ’c’]:
print(s), # prints ’a b c’

It’s also possible to iterate on a Boolean conditional:


while f(c) > tol:
# compute a better guess for the root c

return c

P. E. Farrell (Oxford) Python IV May 8, 2017 2 / 18


break, continue and pass

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))

P. E. Farrell (Oxford) Python IV May 8, 2017 3 / 18


break, continue and pass

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

# Do something with composite number i

P. E. Farrell (Oxford) Python IV May 8, 2017 4 / 18


break, continue and pass

Sometimes we want to do nothing, but Python expects some valid code.


This is what the pass statement is for:
while True:
pass # will loop forever doing nothing

P. E. Farrell (Oxford) Python IV May 8, 2017 5 / 18


Iterable objects

for loops aren’t just for iterating over lists; we can iterate over any
iterable object:
for val in tuple:
...

for key in dict:


...

P. E. Farrell (Oxford) Python IV May 8, 2017 6 / 18


Iterable objects

Another example of an iterable object: files.


mydata = open("mydata.txt", "r")
for line in mydata:
print(line.split())

We’ll see more about I/O later.

P. E. Farrell (Oxford) Python IV May 8, 2017 7 / 18


Generators

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

P. E. Farrell (Oxford) Python IV May 8, 2017 8 / 18


Generators

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

for fib in fibonacci(1, 1):


# do something with Fibonacci numbers,
# will loop forever unless you break

This pattern is extremely useful for numerical computing: it separates


generating computed values, using them, and terminating.

P. E. Farrell (Oxford) Python IV May 8, 2017 9 / 18


Generator comprehensions

Another way to define a generator: like a list comprehension, but with


(...) instead of [..].
sum((x*x for x in xrange(10)))

In this special case (where the generator is the only argument), we can
drop the brackets:
sum(x*x for x in xrange(10))

P. E. Farrell (Oxford) Python IV May 8, 2017 10 / 18


Generators

Here are some useful tools.

enumerate takes in an iterator and produces a new iterator that yields


pairs (index, element):
A = [’a’, ’b’, ’c’]
for (index, x) in enumerate(A):
print(iteration, x)
# result: (0, ’a’) (1, ’b’) (2, ’c’)

P. E. Farrell (Oxford) Python IV May 8, 2017 11 / 18


Generators

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

P. E. Farrell (Oxford) Python IV May 8, 2017 12 / 18


Generators

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

P. E. Farrell (Oxford) Python IV May 8, 2017 13 / 18


Generators

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

P. E. Farrell (Oxford) Python IV May 8, 2017 14 / 18


Generators

itertools.islice truncates an iterator.


from itertools import count, islice
for x in islice(count(), 10):
# same effect as xrange(10)

P. E. Farrell (Oxford) Python IV May 8, 2017 15 / 18


Python 04 Challenge!

Aitken’s ∆2 –method accelerates the convergence of sequences by


transforming a slowly-converging sequence into a faster-converging one
with the same limit. The formula is
(si+1 − si )2
ŝi = si − .
si+2 − 2si+1 + si

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

which converges to π/4 (slowly!).


Hint: value = next(sequence).

P. E. Farrell (Oxford) Python IV May 8, 2017 16 / 18


Python 04 MMSC Challenge!

MMSC students have to work harder (as they get credit).

Project Euler is a series of mathematical and programming puzzles to


teach programming and number theory. Any programming language can
be used, but the code must run in under 10 seconds.

Problem 48 reads: the series 11 + 22 + · · · + 1010 = 10405071317. Find


the last 10 digits of the series 11 + 22 + · · · + 10001000 .

Compute the answer in one line of Python that uses O(1) memory.
(Without googling.)

Email your one-liner and computed answer to


[email protected].

P. E. Farrell (Oxford) Python IV May 8, 2017 17 / 18


Final task

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)

P. E. Farrell (Oxford) Python IV May 8, 2017 18 / 18


Introduction to Python: object orientation

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python V May 8, 2017 1 / 16


Object orientation

Object orientation is a fundamental way to think about programming that


is often very useful for mathematical software.

P. E. Farrell (Oxford) Python V May 8, 2017 2 / 16


Object orientation

In mathematics, when we write sin, we refer to an object that we can


manipulate in various ways. For example, we can
I evaluate sin at a point x, returning a real number
I compute its derivative, returning another object cos
I compute the coefficients of its Taylor polynomial

These methods are shared between all sufficiently smooth functions.

Objects that share the same methods are grouped into classes.

P. E. Farrell (Oxford) Python V May 8, 2017 3 / 16


Object orientation

Given a class, we can instantiate it. sin is an instance of the class of


smooth functions. One of its attributes is its name. Another might be its
domain.

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

These methods define a class of polynomials, which inherit the methods of


the class of smooth functions, and add new ones on top.

P. E. Farrell (Oxford) Python V May 8, 2017 4 / 16


Object orientation

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.

So far we have introduced the concepts:


I classes
I instantiation
I inheritance
I methods
I attributes
I operator overloading
Next we’ll see how these work in Python.

P. E. Farrell (Oxford) Python V May 8, 2017 5 / 16


Defining classes

In Python, the class statement defines a new type:


class RationalNumber(object):
pass

Although this class doesn’t do much yet, we can instantiate it and


interrogate the objects created:
>>> a = RationalNumber()
>>> type(a)
<class __main__.RationalNumber>
>>> isinstance(a, RationalNumber)
True

P. E. Farrell (Oxford) Python V May 8, 2017 6 / 16


Now we provide our example class with some attributes.
To start with, let’s define the __init__ method used for initialising the
class:
class RationalNumber(object):
def __init__(self, numerator, denominator):
self.numerator = numerator
self.denominator = denominator

Let’s see how this is used:


>>> a = RationalNumber(5, 15)
>>> a.numerator
5
>>> a.denominator
15

The constructor sets two attributes.

P. E. Farrell (Oxford) Python V May 8, 2017 7 / 16


We can define other methods to do useful work with the objects.
class RationalNumber(object):
...
def add(self, other):
(p1, q1) = (self.numerator, self.denominator)
if isinstance(other, int):
(p2, q2) = (other, 1)
else:
(p2, q2) = (other.numerator, other.denominator)

return RationalNumber(p1*q2 + p2*q1, q1*q2)


Using this looks like:
>>> p = RationalNumber(1, 2)
>>> q = RationalNumber(1, 3)
>>> r = q.add(p)
>>> print("%s/%s" % (r.numerator, r.denominator))
5/6
P. E. Farrell (Oxford) Python V May 8, 2017 8 / 16
It would be much nicer if we could just write q + p instead. To define the
plus sign for RationalNumber, we can define the __add__ method. (Just
rename the add method). This means that we can write:
>>> p = RationalNumber(1, 2)
>>> q = RationalNumber(1, 3)
>>> r = q + p # alias for q.__add__(p)

We have overloaded the addition operator. In fact, that’s how addition is


defined for other types:
>>> hasattr(float, ’__add__’)
True
>>> hasattr(list, ’__add__’)
True
>>> hasattr(dict, ’__add__’)
False

P. E. Farrell (Oxford) Python V May 8, 2017 9 / 16


The __init__ and __add__ methods are so-called special methods that
mean something to the Python object system. Here we’ll define another:
class RationalNumber(object):
...
def __float__(self):
return float(self.numerator) / float(self.denominator)

This is used by Python’s float command:


>>> a = RationalNumber(5, 15)
>>> float(a)
0.3333333333333333

P. E. Farrell (Oxford) Python V May 8, 2017 10 / 16


Special methods

Here’s a flavour of some of the special methods (google ’special method


names’ for the full list):
I __str__: return a string representation of the object
I __cmp__: called for obj < other, obj > other, etc
I __call__: called when the object is called as a function
I __mul__, __div__: obj * other, obj / other
I __pow__: implements obj**x
I __abs__: implements abs(obj)
I __contains__: implements x in obj
I __iter__: iterate over an object
I __getitem__, __setitem__: getting and setting obj[x]

P. E. Farrell (Oxford) Python V May 8, 2017 11 / 16


Subclassing
Consider one-step methods for solving an ODE. An explicit one-step
method construct the solution values ui by the recursion steps

ui+1 = ui + hφ(f, ui , ti , h)

This is an abstract description that characterises many algorithms: to


actually use it, we have to fill in the details for φ. Different concrete
algorithms make different choices:
I Explicit Euler: φ = f (ui , ti )
I Midpoint rule: φ = f (ui + h/2f (ui ), ti + h/2)
I Runge-Kutta 4: φ = 1/6(s1 + 2s2 + 2s3 + s4 )

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())

def step(self, f, u, t, h):


raise NotImplementedError

P. E. Farrell (Oxford) Python V May 8, 2017 13 / 16


We can inherit from this type to specialise it:
class ExplicitEuler(OneStepMethod):
def step(self, f, u, t, h):
return f(u, t)

class MidPointRule(OneStepMethod):
def step(self, f, u, t, h):
return f(u + h/2.0 * f(u, t), t + h/2.0)

OneStepMethod is called the parent class. Any method or attribute that is


not overridden is inherited.

P. E. Farrell (Oxford) Python V May 8, 2017 14 / 16


When overriding, it is sometimes useful to access the attributes of the
parent class:
class NewmarkBeta(OneStepMethod):
def __init__(self, f, x0, interval, N, beta=0.25):
self.beta = beta
OneStepMethod.__init__(self, f, x0, interval, N)

Here, we override the constructor of the NewmarkBeta class, which in turn


must call the constructor of the subclass for the inherited methods to work.

P. E. Farrell (Oxford) Python V May 8, 2017 15 / 16


Python 05 Challenge!

Implement a Polynomial class with the following methods:


I Construction from coefficients, or from x–y values
I Addition
I Evaluation at a point
I Differentiation and integration (to give new polynomials)
I Roots
Test your code on examples with known answers (e.g. from Wolfram
Alpha).

Extra challenge: implement multiplication of two polynomials.

Does the code 5 + p work, where p is a Polynomial? Hint: look up the


difference between __add__ and __radd__.

P. E. Farrell (Oxford) Python V May 8, 2017 16 / 16


Day 1 Review Challenge!

Implement a recursive fibonacci(N) function that computes the N th


Fibonacci number. Include a print statement to trace the execution.

Once that is working, memoise the recursive function with a decorator.


Compare the number of executions before and after for fibonacci(25).

P. E. Farrell (Oxford) Python VI May 8, 2017 1 / 12


Introduction to Python: errors and debugging

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python VI May 8, 2017 2 / 12


Exceptions

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

This is an example of an exception being raised.

P. E. Farrell (Oxford) Python VI May 8, 2017 3 / 12


Exceptions
More examples of built-in exceptions:
>>> 1/0
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ZeroDivisionError: integer division or modulo by zero
>>>
>>> [’a’, ’b’, ’c’][4]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
IndexError: list index out of range
>>>
>>> float(1.0 + 1.0j)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: can’t convert complex to float
P. E. Farrell (Oxford) Python VI May 8, 2017 4 / 12
Exceptions
Creating an error is called raising an exception. You can raise an exception
with the raise statement:
raise Exception("Something went wrong")

For example, we might


def factorial(n):
if n < 0:
raise ValueError("A nonnegative integer is expected")
...

Why do this over printing an error message?


I Print statements are easy to miss, if the message is buried in many
other messages.
I With a print statement, calling code won’t know something went
wrong, and handle it.
P. E. Farrell (Oxford) Python VI May 8, 2017 5 / 12
Exceptions

Dealing with exceptions is referred to a catching an exception. We use


three statements for this: try, except, finally.
try:
f = open("data.txt", "r")
data = f.readline()
value = float(data)
except IOError:
print("Caught an IOError! Maybe couldn’t open the file?")
except ValueError:
print("Caught a ValueError! Maybe couldn’t convert to float?")
finally:
f.close()

P. E. Farrell (Oxford) Python VI May 8, 2017 6 / 12


Exceptions

You can also define your own exceptions:


class ConvergenceError(Exception):
pass

# later on ...
if iters > max_iters:
raise ConvergenceError("Algorithm did not converge")

P. E. Farrell (Oxford) Python VI May 8, 2017 7 / 12


Context managers

There is a very useful construct in Python for simplifying exception


handling when working with “contexts” like files or databases: the with
statement.
with open("data.txt", "r") as f:
# process f

The open function returns a context manager that ensures that the file
handle is closed when the block terminates.

P. E. Farrell (Oxford) Python VI May 8, 2017 8 / 12


Context managers
Context managers are defined by two special methods: __enter__ and
__exit__. Here are some more examples, from threaded parallelism:
import threading
lock = threading.Lock()

with lock:
# do something with the protected resource

and from connecting to a database:


import sqlite

with sqlite.connect(db) as conn:


query = "INSERT INTO table VALUES (’hello’, ’world’)"
conn.execute(query)

P. E. Farrell (Oxford) Python VI May 8, 2017 9 / 12


Context managers

Numerically, one of the more useful is numpy’s errstate:


from numpy import errstate, sqrt

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

P. E. Farrell (Oxford) Python VI May 8, 2017 10 / 12


Debugging
When an Exception is raised, you see the call stack. The call stack
contains the trace of all the functions that called the code where the
exception was raised. Consider the fle callstack.py:
def f():
g()

def g():
h()

def h():
1/0

f()

This yields the stack trace


Traceback (most recent call last):
File "callstack.py", line 10, in <module>
f()
File "callstack.py", line 2, in f
g()
File "callstack.py", line 5, in g
h()
File "callstack.py", line 8, in h
1/0
ZeroDivisionError: integer division or modulo by zero

P. E. Farrell (Oxford) Python VI May 8, 2017 11 / 12


Debugging

Start the debugger by calling pdb.set_trace at the appropriate point in


your code:
import pdb
from math import sqrt, atan2

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)

This starts a debugger at the specified line:


[pef@aislinn:/tmp]$ python bugs.py
> /tmp/showpdb.py(6)complex_to_polar()
-> r = sqrt(z.real**2 + z.imag**2)
(Pdb)

P. E. Farrell (Oxford) Python VI May 8, 2017 12 / 12


Introduction to Python: NumPy

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python VII May 8, 2017 1 / 15


NumPy

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])

Key differences from lists:


I Fixed data type
I Fixed size =⇒ vectorisation
I Much, much faster

P. E. Farrell (Oxford) Python VII May 8, 2017 2 / 15


NumPy
>>> b = np.arange(1, 4, 0.5) # start, stop, step
>>> b
array([ 1. , 1.5, 2. , 2.5, 3. , 3.5])
>>> c = np.linspace(0, 1, 6) # start, step, num-points
>>> c
array([ 0. , 0.2, 0.4, 0.6, 0.8, 1. ])
>>> a = np.ones((3, 3)) # reminder: (3, 3) is a tuple
>>> a
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])
>>> b = np.zeros((2, 2))
>>> b
array([[ 0., 0.],
[ 0., 0.]])
>>> d = np.diag(np.array([1, 2, 3, 4]))
>>> d
array([[1, 0, 0, 0],
[0, 2, 0, 0],
[0, 0, 3, 0],
[0, 0, 0, 4]])

P. E. Farrell (Oxford) Python VII May 8, 2017 3 / 15


Datatypes

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’)

P. E. Farrell (Oxford) Python VII May 8, 2017 4 / 15


Slicing

Numpy arrays can be sliced in all sorts of powerful ways:


>>> M = np.random.randn(10, 10)
>>> M[2:4, 5:8]
array([[ 0.57563161, -1.81466408, 0.98266811],
[-2.40016935, -0.42627965, 0.80222344]])
>>> M[2:4, 5:8] = 0.0

These simple slices create views into the dataset.

P. E. Farrell (Oxford) Python VII May 8, 2017 5 / 15


Example: Sieve of Eratosthenes
import numpy as np
def primes(N):
is_prime = np.ones(N, dtype=bool)
is_prime[:2] = False # 0, 1 not prime
N_sqrt = int(np.sqrt(N))

for j in range(2, N_sqrt):


is_prime[2*j::j] = False

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])

We actually saw this earlier in plotting the Mandelbrot fractal.

P. E. Farrell (Oxford) Python VII May 8, 2017 7 / 15


Operations on arrays

All arithmetic operates elementwise.


>>> a = np.array([1, 2, 3, 4])
>>> a + 1
array([2, 3, 4, 5])
>>> 2**a
array([ 2, 4, 8, 16])
>>> b = np.ones(4) + 1
>>> a - b
array([-1., 0., 1., 2.])
>>> a * b
array([ 2., 4., 6., 8.])

These operations are vectorised: much faster than if implemented in


Python.

P. E. Farrell (Oxford) Python VII May 8, 2017 8 / 15


More examples

>>> a = np.array([1, 1, 0, 0], dtype=bool)


>>> b = np.array([1, 0, 1, 0], dtype=bool)
>>> a == b
array([ True, False, False, True], dtype=bool)
>>> a > b
array([False, True, False, False], dtype=bool)
>>> a & b
array([ True, False, False, False], dtype=bool)
>>> a | b
array([ True, True, True, False], dtype=bool)
>>> np.sin(a) # elementwise
array([ 0.84130859, 0.84130859, 0. , 0. ])

P. E. Farrell (Oxford) Python VII May 8, 2017 9 / 15


Reductions

>>> x = np.array([[1, 1], [2, 2]])


>>> x
array([[1, 1],
[2, 2]])
>>> x.sum(axis=0) # columns (first dimension)
array([3, 3])
>>> x[:, 0].sum(), x[:, 1].sum()
(3, 3)
>>> x.sum(axis=1) # rows (second dimension)
array([2, 4])
>>> x[0, :].sum(), x[1, :].sum()
(2, 4)

P. E. Farrell (Oxford) Python VII May 8, 2017 10 / 15


Broadcasting
np.arange(3) + 5

0 1 2 + 5 5 5 = 5 6 7

np.ones((3, 3)) + np.arange(3)

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

np.arange(3).reshape((3, 1)) + np.arange(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

P. E. Farrell (Oxford) Python VII May 8, 2017 11 / 15


Shape manipulation

>>> a = np.array([[1, 2, 3], [4, 5, 6]])


>>> a.ravel()
array([1, 2, 3, 4, 5, 6])
>>> a.T
array([[1, 4],
[2, 5],
[3, 6]])
>>> a.T.ravel()
array([1, 4, 2, 5, 3, 6])

P. E. Farrell (Oxford) Python VII May 8, 2017 12 / 15


Shape manipulation

>>> 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]])

P. E. Farrell (Oxford) Python VII May 8, 2017 13 / 15


Python 07 Challenge!
Conway’s Game of Life is a cellular automaton that creates beautiful
patterns. Given an N × N grid of cells, each cell is either dead (0) or alive
(1). Every cell interacts with its eight neighbours. At each step in time,
the following transitions occur:
I Any live cell with fewer than two neighbours dies (underpopulation).
I Any live cell with two or three neighbours lives on (survival).
I Any live cell with more than three neighbours dies (overcrowding).
I Any dead cell with exactly three live neighbours becomes alive
(reproduction).
Represent the N × N grid with an (N + 2) × (N + 2) numpy matrix bordered by
zeros. Implement:
I a neighbours function that counts the live neighbours of each cell.
I an iterate function that applies the rules.
I call your code on the glider pattern [[0, 0, 1], [1, 0, 1], [0, 1, 1]].
P. E. Farrell (Oxford) Python VII May 8, 2017 14 / 15
P. E. Farrell (Oxford) Python VII May 8, 2017 15 / 15
Introduction to Python: SciPy

Patrick Farrell

MMSC: Python in Scientific Computing

May 8, 2017

P. E. Farrell (Oxford) Python VIII May 8, 2017 1 / 10


SciPy

NumPy provides the core dense multidimensional array class for Python.
SciPy provides basically everything else:

scipy.linalg Linear algebra


scipy.integrate Quadrature
scipy.fftpack Fourier transforms
scipy.io I/O
scipy.sparse Sparse matrices
scipy.interpolate Interpolation
scipy.optimize Optimisation
scipy.stats Statistics

Let’s take a closer look at some of them.

P. E. Farrell (Oxford) Python VIII May 8, 2017 2 / 10


Interoperability with MATLAB

>>> from scipy import io as spio


>>> a = np.ones((3, 3))
>>> spio.savemat(’file.mat’, {’a’: a})
>>> data = spio.loadmat(’file.mat’)
>>> data[’a’]
array([[ 1., 1., 1.],
[ 1., 1., 1.],
[ 1., 1., 1.]])

Note: for .mat 7.3 format, use HDF5 via h5py.

P. E. Farrell (Oxford) Python VIII May 8, 2017 3 / 10


Dense linear algebra

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

P. E. Farrell (Oxford) Python VIII May 8, 2017 4 / 10


Dense linear algebra

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

P. E. Farrell (Oxford) Python VIII May 8, 2017 5 / 10


Dense linear algebra

Singular value decompositions:


>>> H = linalg.hilbert(3)
>>> (U, L, VT) = linalg.svd(H)
>>> L
array([ 1.40831893, 0.12232707, 0.00268734])

P. E. Farrell (Oxford) Python VIII May 8, 2017 6 / 10


Dense linear algebra

Some more routines (see help(scipy.linalg) for the full list):

solve Solve with LU


norm Matrix and vector norms
pinv Moore–Penrose pseudoinverse
eig Eigenvalues
qr QR factorisation
expm Matrix exponentiation

P. E. Farrell (Oxford) Python VIII May 8, 2017 7 / 10


Sparse linear algebra

Almost all applications in scientific computing involve sparse matrices.


scipy.sparse implements several efficient sparse matrix data formats.
You should be aware of two:
I List of lists format ("lil"): efficient for construction and
modification
I Compressed sparse row format ("csr"): efficient for matvec and
sparse LU

P. E. Farrell (Oxford) Python VIII May 8, 2017 8 / 10


Example: 1D Laplacian with homogeneous Dirichlet BCs

import scipy.sparse as sp
import scipy.sparse.linalg as la
import numpy as np

N = 1000; h = 1.0 / (N+1)


K = sp.diags(diagonals=[2, -1, -1], offsets=[0, -1, 1],
shape=(N, N), format="lil")
rhs = 2 * np.ones(N) * h**2

K[+0,:] = 0.0; K[+0, +0] = 1.0


K[-1,:] = 0.0; K[-1, -1] = 1.0
rhs[+0] = 0.0; rhs[-1] = 0.0

K = K.tocsr()

u = la.spsolve(K, rhs)

P. E. Farrell (Oxford) Python VIII May 8, 2017 9 / 10


Python 08 Challenge!
Consider the one-dimensional Gray–Scott equations:
ut = u ∆u − uv 2 + F (1 − u)
vt = v ∆v + uv 2 − (c + F )v
with homogeneous Dirichlet boundary conditions on [−1, 1).
(a) Solve the zero-diffusion case with c = 0.065, F = 0.06 with forward
Euler, for various initial conditions. Plot the steady-state solutions.
(b) Take u = 6 × 10−5 , v = 2 × 10−5 . Use the initial condition
v(x) = χ(x) + 0.05η(x)
u(x) = 1 − v(x)
where η(x) ∼ N (0, 1) and
(
1 if |x − 0.1| ≤ 0.1
χ(x) =
0 otherwise
P. E. Farrell (Oxford) Python VIII May 8, 2017 10 / 10

You might also like