Cython Magics.ipynb
366 lines
| 9.5 KiB
| text/plain
|
TextLexer
Brian Granger
|
r7031 | { | ||
"metadata": { | ||||
Nikolay Koldunov
|
r16719 | "name": "Cython Magics", | ||
"signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074" | ||||
Brian Granger
|
r7031 | }, | ||
"nbformat": 3, | ||||
MinRK
|
r7739 | "nbformat_minor": 0, | ||
Brian Granger
|
r7031 | "worksheets": [ | ||
{ | ||||
"cells": [ | ||||
{ | ||||
"cell_type": "heading", | ||||
"level": 1, | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
Brian Granger
|
r9193 | "Cython Magic Functions" | ||
Brian Granger
|
r7031 | ] | ||
}, | ||||
{ | ||||
"cell_type": "heading", | ||||
"level": 2, | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
"Loading the extension" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
Erik Tollerud
|
r16220 | "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:" | ||
Brian Granger
|
r7031 | ] | ||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
"%load_ext cythonmagic" | ||||
], | ||||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [], | ||
"prompt_number": 1 | ||||
}, | ||||
{ | ||||
"cell_type": "heading", | ||||
"level": 2, | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
"The %cython_inline magic" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
"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. " | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Fernando Perez
|
r7421 | "a = 10\n", | ||
Brian Granger
|
r7031 | "b = 20" | ||
], | ||||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [], | ||
MinRK
|
r7739 | "prompt_number": 2 | ||
Brian Granger
|
r7031 | }, | ||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Fernando Perez
|
r7421 | "%%cython_inline\n", | ||
Brian Granger
|
r7031 | "return a+b" | ||
], | ||||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [ | ||
{ | ||||
Erik Tollerud
|
r16222 | "metadata": {}, | ||
Brian Granger
|
r7031 | "output_type": "pyout", | ||
MinRK
|
r7739 | "prompt_number": 3, | ||
Brian Granger
|
r7031 | "text": [ | ||
"30" | ||||
] | ||||
} | ||||
], | ||||
MinRK
|
r7739 | "prompt_number": 3 | ||
Brian Granger
|
r7031 | }, | ||
{ | ||||
"cell_type": "heading", | ||||
"level": 2, | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
"The %cython_pyximport magic" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
Nikolay Koldunov
|
r16718 | "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 to specify the name of the module that the Code will appear in. All symbols from the module are imported automatically by the magic function." | ||
Brian Granger
|
r7031 | ] | ||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Fernando Perez
|
r7421 | "%%cython_pyximport foo\n", | ||
"def f(x):\n", | ||||
Brian Granger
|
r7031 | " return 4.0*x" | ||
], | ||||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [], | ||
MinRK
|
r7739 | "prompt_number": 4 | ||
Brian Granger
|
r7031 | }, | ||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
"f(10)" | ||||
], | ||||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [ | ||
{ | ||||
Erik Tollerud
|
r16222 | "metadata": {}, | ||
Brian Granger
|
r7031 | "output_type": "pyout", | ||
MinRK
|
r7739 | "prompt_number": 5, | ||
Brian Granger
|
r7031 | "text": [ | ||
"40.0" | ||||
] | ||||
} | ||||
], | ||||
MinRK
|
r7739 | "prompt_number": 5 | ||
Brian Granger
|
r7031 | }, | ||
{ | ||||
"cell_type": "heading", | ||||
"level": 2, | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
"The %cython magic" | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "source": [ | ||
Nikolay Koldunov
|
r16718 | "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 manages everything using temporary files in the `~/.cython/magic` directory. All of the symbols in the Cython module are imported automatically by the magic.\n", | ||
Fernando Perez
|
r7421 | "\n", | ||
Bradley M. Froehle
|
r8964 | "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." | ||
Brian Granger
|
r7031 | ] | ||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Fernando Perez
|
r7421 | "%%cython\n", | ||
"cimport cython\n", | ||||
"from libc.math cimport exp, sqrt, pow, log, erf\n", | ||||
"\n", | ||||
"@cython.cdivision(True)\n", | ||||
Erik Tollerud
|
r16222 | "cdef double std_norm_cdf_cy(double x) nogil:\n", | ||
Fernando Perez
|
r7421 | " return 0.5*(1+erf(x/sqrt(2.0)))\n", | ||
"\n", | ||||
"@cython.cdivision(True)\n", | ||||
Erik Tollerud
|
r16222 | "def black_scholes_cy(double s, double k, double t, double v,\n", | ||
" double rf, double div, double cp):\n", | ||||
Fernando Perez
|
r7421 | " \"\"\"Price an option using the Black-Scholes model.\n", | ||
" \n", | ||||
" s : initial stock price\n", | ||||
" k : strike price\n", | ||||
" t : expiration time\n", | ||||
" v : volatility\n", | ||||
" rf : risk-free rate\n", | ||||
" div : dividend\n", | ||||
" cp : +1/-1 for call/put\n", | ||||
" \"\"\"\n", | ||||
" cdef double d1, d2, optprice\n", | ||||
" with nogil:\n", | ||||
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n", | ||||
" d2 = d1 - v*sqrt(t)\n", | ||||
Erik Tollerud
|
r16222 | " optprice = cp*s*exp(-div*t)*std_norm_cdf_cy(cp*d1) - \\\n", | ||
" cp*k*exp(-rf*t)*std_norm_cdf_cy(cp*d2)\n", | ||||
Brian Granger
|
r7031 | " return optprice" | ||
], | ||||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [], | ||
"prompt_number": 6 | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Erik Tollerud
|
r16222 | "black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" | ||
Brian Granger
|
r7031 | ], | ||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [ | ||
{ | ||||
Erik Tollerud
|
r16222 | "metadata": {}, | ||
Brian Granger
|
r7031 | "output_type": "pyout", | ||
"prompt_number": 7, | ||||
"text": [ | ||||
"10.327861752731728" | ||||
] | ||||
} | ||||
], | ||||
"prompt_number": 7 | ||||
}, | ||||
{ | ||||
Erik Tollerud
|
r16222 | "cell_type": "markdown", | ||
"metadata": {}, | ||||
"source": [ | ||||
"For comparison, the same code is implemented here in pure python." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
"collapsed": false, | ||||
"input": [ | ||||
"from math import exp, sqrt, pow, log, erf\n", | ||||
"\n", | ||||
"def std_norm_cdf_py(x):\n", | ||||
" return 0.5*(1+erf(x/sqrt(2.0)))\n", | ||||
"\n", | ||||
"def black_scholes_py(s, k, t, v, rf, div, cp):\n", | ||||
" \"\"\"Price an option using the Black-Scholes model.\n", | ||||
" \n", | ||||
" s : initial stock price\n", | ||||
" k : strike price\n", | ||||
" t : expiration time\n", | ||||
" v : volatility\n", | ||||
" rf : risk-free rate\n", | ||||
" div : dividend\n", | ||||
" cp : +1/-1 for call/put\n", | ||||
" \"\"\"\n", | ||||
" d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))\n", | ||||
" d2 = d1 - v*sqrt(t)\n", | ||||
" optprice = cp*s*exp(-div*t)*std_norm_cdf_py(cp*d1) - \\\n", | ||||
" cp*k*exp(-rf*t)*std_norm_cdf_py(cp*d2)\n", | ||||
" return optprice" | ||||
], | ||||
"language": "python", | ||||
"metadata": {}, | ||||
"outputs": [], | ||||
"prompt_number": 8 | ||||
}, | ||||
{ | ||||
Brian Granger
|
r7031 | "cell_type": "code", | ||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Erik Tollerud
|
r16222 | "black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" | ||
], | ||||
"language": "python", | ||||
"metadata": {}, | ||||
"outputs": [ | ||||
{ | ||||
"metadata": {}, | ||||
"output_type": "pyout", | ||||
"prompt_number": 9, | ||||
"text": [ | ||||
"10.327861752731728" | ||||
] | ||||
} | ||||
], | ||||
"prompt_number": 9 | ||||
}, | ||||
{ | ||||
"cell_type": "markdown", | ||||
"metadata": {}, | ||||
"source": [ | ||||
"Below we see the runtime of the two functions: the Cython version is nearly a factor of 10 faster." | ||||
] | ||||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
"collapsed": false, | ||||
"input": [ | ||||
"%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" | ||||
Brian Granger
|
r7031 | ], | ||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Brian Granger
|
r7031 | "outputs": [ | ||
{ | ||||
"output_type": "stream", | ||||
"stream": "stdout", | ||||
"text": [ | ||||
Erik Tollerud
|
r16222 | "1000000 loops, best of 3: 319 ns per loop\n" | ||
Brian Granger
|
r7031 | ] | ||
} | ||||
], | ||||
Erik Tollerud
|
r16222 | "prompt_number": 10 | ||
}, | ||||
{ | ||||
"cell_type": "code", | ||||
"collapsed": false, | ||||
"input": [ | ||||
"%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" | ||||
], | ||||
"language": "python", | ||||
"metadata": {}, | ||||
"outputs": [ | ||||
{ | ||||
"output_type": "stream", | ||||
"stream": "stdout", | ||||
"text": [ | ||||
"100000 loops, best of 3: 2.28 \u00b5s per loop\n" | ||||
] | ||||
} | ||||
], | ||||
"prompt_number": 11 | ||||
Brian Granger
|
r7031 | }, | ||
{ | ||||
Brian Granger
|
r9193 | "cell_type": "heading", | ||
"level": 2, | ||||
"metadata": {}, | ||||
"source": [ | ||||
"External libraries" | ||||
] | ||||
}, | ||||
{ | ||||
Fernando Perez
|
r7421 | "cell_type": "markdown", | ||
MinRK
|
r7739 | "metadata": {}, | ||
Fernando Perez
|
r7421 | "source": [ | ||
Fernando Perez
|
r7422 | "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:" | ||
Fernando Perez
|
r7421 | ] | ||
}, | ||||
{ | ||||
Brian Granger
|
r7031 | "cell_type": "code", | ||
MinRK
|
r7739 | "collapsed": false, | ||
Brian Granger
|
r7031 | "input": [ | ||
Fernando Perez
|
r7422 | "%%cython -lm\n", | ||
Fernando Perez
|
r7421 | "from libc.math cimport sin\n", | ||
"print 'sin(1)=', sin(1)" | ||||
Brian Granger
|
r7031 | ], | ||
"language": "python", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Fernando Perez
|
r7421 | "outputs": [ | ||
{ | ||||
"output_type": "stream", | ||||
"stream": "stdout", | ||||
"text": [ | ||||
"sin(1)= 0.841470984808\n" | ||||
] | ||||
} | ||||
], | ||||
Erik Tollerud
|
r16222 | "prompt_number": 12 | ||
Fernando Perez
|
r7421 | }, | ||
{ | ||||
"cell_type": "markdown", | ||||
MinRK
|
r7739 | "metadata": {}, | ||
Fernando Perez
|
r7421 | "source": [ | ||
Fernando Perez
|
r7422 | "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](http://docs.cython.org/src/tutorial/clibraries.html) for more details on the use of these flags." | ||
Fernando Perez
|
r7421 | ] | ||
Brian Granger
|
r7031 | } | ||
MinRK
|
r7739 | ], | ||
"metadata": {} | ||||
Brian Granger
|
r7031 | } | ||
] | ||||
Nikolay Koldunov
|
r16719 | } | ||