{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "# Cython Magic Functions" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Loading the extension" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "IPython had a `cythonmagic` extension that contains a number of magic functions for working with Cython code. This extension can be found in the Cython package now and can be loaded using the `%load_ext` magic as follows:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%load_ext Cython" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The %cython_inline magic" ] }, { "cell_type": "markdown", "metadata": {}, "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", "execution_count": 2, "metadata": { "collapsed": false }, "outputs": [], "source": [ "a = 10\n", "b = 20" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "30" ] }, "execution_count": 3, "metadata": {}, "output_type": "execute_result" } ], "source": [ "%%cython_inline\n", "return a+b" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The %cython_pyximport magic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython_pyximport foo\n", "def f(x):\n", " return 4.0*x" ] }, { "cell_type": "code", "execution_count": 5, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "40.0" ] }, "execution_count": 5, "metadata": {}, "output_type": "execute_result" } ], "source": [ "f(10)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## The %cython magic" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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", "\n", "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." ] }, { "cell_type": "code", "execution_count": 6, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%cython\n", "cimport cython\n", "from libc.math cimport exp, sqrt, pow, log, erf\n", "\n", "@cython.cdivision(True)\n", "cdef double std_norm_cdf_cy(double x) nogil:\n", " return 0.5*(1+erf(x/sqrt(2.0)))\n", "\n", "@cython.cdivision(True)\n", "def black_scholes_cy(double s, double k, double t, double v,\n", " double rf, double div, double 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", " 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", " 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", " return optprice" ] }, { "cell_type": "code", "execution_count": 7, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "10.327861752731728" ] }, "execution_count": 7, "metadata": {}, "output_type": "execute_result" } ], "source": [ "black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "For comparison, the same code is implemented here in pure python." ] }, { "cell_type": "code", "execution_count": 8, "metadata": { "collapsed": false }, "outputs": [], "source": [ "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" ] }, { "cell_type": "code", "execution_count": 9, "metadata": { "collapsed": false }, "outputs": [ { "data": { "text/plain": [ "10.327861752731728" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" ] }, { "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", "execution_count": 10, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1000000 loops, best of 3: 319 ns per loop\n" ] } ], "source": [ "%timeit black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" ] }, { "cell_type": "code", "execution_count": 11, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100000 loops, best of 3: 2.28 µs per loop\n" ] } ], "source": [ "%timeit black_scholes_py(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## External libraries" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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:" ] }, { "cell_type": "code", "execution_count": 12, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "sin(1)= 0.841470984808\n" ] } ], "source": [ "%%cython -lm\n", "from libc.math cimport sin\n", "print 'sin(1)=', sin(1)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] } ], "metadata": {}, "nbformat": 4, "nbformat_minor": 0 }