making of-the-logistic-map-bifurcation-diagram

21
Making of: The logistic map bifurcation diagram Brad Martsberger

Upload: martsberger

Post on 03-Jun-2015

579 views

Category:

Software


1 download

DESCRIPTION

ChiPy presentation, June 12 2014. Speeding up logistic map bifurcation diagram using PyPy, C extension, and Cython.

TRANSCRIPT

Page 1: Making of-the-logistic-map-bifurcation-diagram

Making of:

The logistic mapbifurcation diagram

Brad Martsberger

Page 2: Making of-the-logistic-map-bifurcation-diagram

What is this talk about?Making a pretty picture

Squeezing performance out of python

PyPy, Cython, C extensions

With a toy physics model (chaos!)

Page 3: Making of-the-logistic-map-bifurcation-diagram

Fun with fractals

Page 4: Making of-the-logistic-map-bifurcation-diagram

Cool, but...Where did that come from?Can you show me some python already?

Page 5: Making of-the-logistic-map-bifurcation-diagram

The logistic mapMap a value, x, from [0, 1] to [0, 1] according to:

r * x * (1 - x)

Let's play...

x = 0.25 r = 2.5 for j in range(limit): print "x:", x x = r * x * (1 - x)

Page 6: Making of-the-logistic-map-bifurcation-diagram

What do we need to make thatsweet graph?

Python

Matplotlib

Patience ... lots of patience

Page 7: Making of-the-logistic-map-bifurcation-diagram

Simplified version bif_diag = [[0.0] * height] * width

for k in range(width): # histogram of how frequently each pixel is visited. h = logmap_histogram(r, x_len, x1, x2, height)

# Normalize the histogram to go from 0 to 1. h = divide_list(h, 1.0 * max(h))

# stick it in upside down to the image bif_diag[k - 1] = h[::-1]

plot_bif_diag(bif_diag, plt, cm, (x1, x2), (r_lower, r_upper))

Page 8: Making of-the-logistic-map-bifurcation-diagram

Inner loop def logmap_histogram(r, x_len, x1, x2, height): result = [0] * height dx = 1.0 * (x2 - x1) / (height - 1)

transient = 500 x = 0.25 for k in xrange(transient): x = x * r * (1 - x) for k in xrange(x_len): x = x * r * (1 - x) incr = int((x - x1) / dx) if incr >= 0 and incr < M: result[incr] += 1

return result

Page 9: Making of-the-logistic-map-bifurcation-diagram

Matplotlib bitdef plot_bif_diag(bif_diag, plt, cm, xlim, rlim): width, height, = len(bif_diag), len(bif_diag[1])

to_plot = [[1 - pix for pix in col] for col in bif_diag] to_plot = map(list, zip(*to_plot))

plt.imshow(to_plot, cm.gray, clim=(0.7, 1))

font = {'family': 'serif', 'size': 24} plt.xlabel('r', fontdict=font) plt.ylabel('x*', fontdict=font) plt.xticks(linspace(1, N, 9), ['%.2g' % r for r in linspace(rlim[0], rlim[1], 9)]) plt.yticks(linspace(1, M - 1, 5), ['%.2g' % x for x in linspace(xlim[1], xlim[0], 5)])

ax = plt.gca() ax.set_position([80. / (width + 100.), 80. / (height + 100.), width / (width + 100.), height / (height + plt.gcf().canvas.manager.window.geometry("{0}x{1}+30+30".format(width + 100, height +

plt.show()

Page 10: Making of-the-logistic-map-bifurcation-diagram

Let's run thisthing already...

Page 11: Making of-the-logistic-map-bifurcation-diagram

That was a little disappointingNot horrible, but too slow to do some cool stuff.

Page 12: Making of-the-logistic-map-bifurcation-diagram

Options for doing better0. Quit using python

1. PyPy

2. C extension

3. Cython

4. There are others (numpy with numexpr)

Page 13: Making of-the-logistic-map-bifurcation-diagram

PyPyStraight from pypy.org:

Fast, compliant alternative implementation of the Pythonlanguage (2.7.6 and 3.2.3) with several advantages:

Speed, due to Just-In-Time compiler

Takes less memory

Supports a number of third party libraries:

ctypes, django, sqlalchemy, twisted, etc.

Does not require any change to your code!

Unfortunately, matplotlib is not supported.

Numpy is partially supported.

Page 14: Making of-the-logistic-map-bifurcation-diagram

C extensionsWrite functions in C that are callable from python

Pass python objects as arguments that must be converted to Cvalues

Returns a python object, e.g., a list

Compiled and fast

Do this at least once

Page 15: Making of-the-logistic-map-bifurcation-diagram

Let's have a lookstatic PyObject *logmap_attractor_histogram(PyObject *self, PyObject *args) { double r1, r2, x, x1, x2; int xxlen, rrlen, M; int transient = 500; // Short transient for each r.

if (!PyArg_ParseTuple(args, "ddiiddi", &r1, &r2, &rrlen, &xxlen, &x1, &x2, &M)) { return NULL; }

// Store our histogram in an int array, copy it to a python list and return // the list as a PyObject* int array[M]; PyObject* list = PyList_New(M); // Initialize array to all zeros int j, k; for (j = 0; j < M; ++j) { array[j] = 0; }

double r, dr, dx; int incr;

// Set the amount we will step to get from r1 to r2 in rrlen-1 steps dr = 0; if (rrlen > 1) { dr = (r2 - r1)/(rrlen-1); }

// Histogram bin size dx = (x2 - x1)/(M - 1);

// loop over r (perhaps change to for (r=r1; r<=r2; r += dr) )

Page 16: Making of-the-logistic-map-bifurcation-diagram

Compiling to an importable moduleCreate a setup.py file, e.g:

from distutils.core import setup, Extension

setup(name='logmap', version='1.0', ext_modules=[Extension('logmap', ['logmapmodule.c'])])

Then install it

>>> python setup.py install

And it can be imported and called

import logmap

h = logmap.attractor_histogram(...)

Page 17: Making of-the-logistic-map-bifurcation-diagram

Let's see it already...

Page 18: Making of-the-logistic-map-bifurcation-diagram

CythonDirectly from cython.org:

Static compiler for python

Makes writing C extensions for python as easy as writingpython

Generates C code from your python code

Has the potential to deliver the speed gains that we get from a Cextension with less hassle

Page 19: Making of-the-logistic-map-bifurcation-diagram

How to make it workCreate setup.py file:

Build like this:

Then you can import fromlogistic_map_bifurcation_diagram_cython

from distutils.core import setupfrom Cython.Build import cythonize

setup( name = 'logmap_cython', ext_modules = cythonize("logistic_map_bifurcation_diagram_cython.pyx"),)

%>python setup.py build_ext --in_place

Page 20: Making of-the-logistic-map-bifurcation-diagram

Making cython fasterCython's code generation is helped by providing it hints in the

form of type declarations

def logmap_histogram(float r1, float r2, int rrlen, int xxlen, float x1, float x2, int M) result = [0] * M cdef float dx dx = 1.0 * (x2 - x1) / (M - 1)

transient = 500 cdef float x cdef float r cdef float k for r in linspace(r1, r2, rrlen): x = 0.25 for k in range(transient): x = x * r * (1 - x) for k in range(xxlen): x = x * r * (1 - x) incr = int((x - x1) / dx) if incr >= 0 and incr < M: result[incr] += 1

return result

Page 21: Making of-the-logistic-map-bifurcation-diagram

Questions...Comments, etc.