Cython Magic Functions¶
Loading the extension¶
IPython has a cythonmagic
extension that contains a number of magic functions for working with Cython code. This extension can be loaded using the %load_ext
magic as follows:
%load_ext cythonmagic
The %cython_inline magic¶
The %%cython_inline
magic uses Cython.inline
to compile a Cython expression. This allows you to enter and run a function body with Cython code. Use a bare return
statement to return values.
a = 10
b = 20
%%cython_inline
return a+b
The %cython_pyximport magic¶
The %%cython_pyximport
magic allows you to enter arbitrary Cython code into a cell. That Cython code is written as a .pyx
file in the current working directory and then imported using pyximport
. You have the specify the name of the module that the Code will appear in. All symbols from the module are imported automatically by the magic function.
%%cython_pyximport foo
def f(x):
return 4.0*x
f(10)
The %cython magic¶
Probably the most important magic is the %cython
magic. This is similar to the %%cython_pyximport
magic, but doesn't require you to specify a module name. Instead, the %%cython
magic uses manages everything using temporary files in the ~/.cython/magic
directory. All of the symbols in the Cython module are imported automatically by the magic.
Here is a simple example of a Black-Scholes options pricing algorithm written in Cython. Please note that this example might not compile on non-POSIX systems (e.g., Windows) because of a missing erf
symbol.
%%cython
cimport cython
from libc.math cimport exp, sqrt, pow, log, erf
@cython.cdivision(True)
cdef double std_norm_cdf_cy(double x) nogil:
return 0.5*(1+erf(x/sqrt(2.0)))
@cython.cdivision(True)
def black_scholes_cy(double s, double k, double t, double v,
double rf, double div, double cp):
"""Price an option using the Black-Scholes model.
s : initial stock price
k : strike price
t : expiration time
v : volatility
rf : risk-free rate
div : dividend
cp : +1/-1 for call/put
"""
cdef double d1, d2, optprice
with nogil:
d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))
d2 = d1 - v*sqrt(t)
optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \
cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)
return optprice
black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)
For comparison, the same code is implemented here in pure python.
from math import exp, sqrt, pow, log, erf
def std_norm_cdf_py(x):
return 0.5*(1+erf(x/sqrt(2.0)))
def black_scholes_py(s, k, t, v, rf, div, cp):
"""Price an option using the Black-Scholes model.
s : initial stock price
k : strike price
t : expiration time
v : volatility
rf : risk-free rate
div : dividend
cp : +1/-1 for call/put
"""
d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))
d2 = d1 - v*sqrt(t)
optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \
cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)
return optprice
black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)
Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster.
%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)
%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)
External libraries¶
Cython allows you to specify additional libraries to be linked with your extension, you can do so with the -l
flag (also spelled --lib
). Note that this flag can be passed more than once to specify multiple libraries, such as -lm -llib2 --lib lib3
. Here's a simple example of how to access the system math library:
%%cython -lm
from libc.math cimport sin
print 'sin(1)=', sin(1)
You can similarly use the -I/--include
flag to add include directories to the search path, and -c/--compile-args
to add extra flags that are passed to Cython via the extra_compile_args
of the distutils Extension
class. Please see the Cython docs on C library usage for more details on the use of these flags.