CompSciWeek9
Contents
Class 1, an in-depth look at class structure
- magic methods, __init__, etc.
- class inheritance - extending something that does almost what you want
- ex. inheriting from dict
- isinstance() function
- methods and calling convention - the first argument to a class method is always the class (i.e. self)
- Binary operations __add__, __mul__, etc. - these have type class -> class -> class
- static (immutable) vs. dynamic (mutable)
- classes are static
- instances are dynamic
- unless you only initialize and don't ever change them
- if you know it's static and __hash__ is implemented, you can use it as a key
Example - wrapping a numpy array in a static class to build a cache: <source lang="python"> from numpy import *
- TODO: make static!
class s_ndarray:
def __init__(self, x): self.x = x def __hash__(self): return hash(self.x.__repr__())
a = s_ndarray(arange(12))
- array -> float
def en(x): print "Running (expensive) energy calculation." return x[0]
- { s_ndarray : float } -> s_ndarray -> float
def en_cached(cache, x): if x not in cache: cache[x] = en(x.x) # peel off s_ndarray and run the en function return cache[x]
cache = {} x = s_ndarray(arange(12)) y = s_ndarray(ones(12)) print en_cached(cache, x) print en_cached(cache, y)
print cache
- these don't need to call en
print en_cached(cache, y) print en_cached(cache, y) </source>
Class 2, python's ctypes
- Compiling a c code as a shared object
- the trouble with integers - always pick a fixed size!
- loading with ctypes
- Declaring function types.
- Performance test
Reference: Python Docs Numpy + Ctypes Cookbook
dwt.c - run one step of a discrete wavelet transformation.
<source lang="C">
- include <stdint.h>
/* Calculate the step-function discrete wavelet transformation
* on the data x, placing the result in y. * * This assumes x and y are already allocated and of size n. * * x = ababab ababab * y = (a+b) (b-a) */
int32_t dwt(double *x, double *y, int32_t n) { int i; // index into x, moves from 0, ..., n-1 int a = 0; // index into y sum, moves from 0, ..., ceil n/2 int b = (n+1)/2; // index into y difference, moves from (ceil n/2)+1, ..., n-1
for(i=0; i<n-1; i+=2) { y[a] = 0.5*(x[i+1]+x[i]); y[b] = x[i+1]-x[i]; a += 1; b += 1; } // odd case, ceil n/2 = (n+1)/2 and we have an extra sum if(i == n-1) { y[a] = x[i]; }
return 0; } </source>
<source lang="bash"> gcc -o dwt_c.so -shared dwt.c </source>
dwt.py - load and run the object from python
<source lang="python"> from ctypes import CDLL, c_int32, c_double import numpy as np import numpy.ctypeslib as ct import os
cwd = os.path.dirname(os.path.abspath(__file__)) dw = CDLL(os.path.join(cwd, "dwt_c.so"))
- Shorthand for setting function prototypes
def decl_fn(a, *args):
a.argtypes = args[:-1] a.restype = args[-1]
def void_fn(a, *args):
decl_fn(a, *(args+(None,)))
def int_fn(a, *args):
decl_fn(a, *(args+(c_int32,)))
def dbl_fn(a, *args):
decl_fn(a, *(args+(c_double,)))
- Building up common, useful data types
intarr = np.ctypeslib.ndpointer(dtype=np.int32, flags='C_CONTIGUOUS') dblarr = np.ctypeslib.ndpointer(dtype=np.float64, flags='C_CONTIGUOUS')
decl_fn(dw.dwt, dblarr, dblarr, c_int32)
def run_dwt(x): y = np.zeros(x.shape, dtype=np.float64) print dw.dwt(x.astype(np.float64), y, len(x)) return y
print run_dwt(np.arange(12)) print run_dwt(np.arange(11)) </source>
vis.py - visualize the DWT by running on image data
<source lang="python"> import PIL from dwt import run_dwt, np
- convert to 1-color (luminance)
img = PIL.Image.open("Mitosis.png").convert("L") x = np.array(img)
y = np.array(map(run_dwt, x))
img = PIL.Image.fromarray(y) img.show() </source>
Here's a test image to use: Image from Brooke Borel.