Commit 31bb39f0 authored by scoder's avatar scoder

Merge pull request #286 from mgaitan/ipython

IPython magics
parents 34a3cff9 4a64dcb8
This diff is collapsed.
# -*- coding: utf-8 -*-
# tag: ipython
"""Tests for the Cython magics extension."""
import os
try:
from IPython.testing.globalipapp import get_ipython
from IPython.testing import decorators as dec
from IPython.utils import py3compat
except:
__test__ = False
from Cython.TestUtils import CythonTest
ip = get_ipython()
code = py3compat.str_to_unicode("""def f(x):
return 2*x
""")
class TestIPythonMagic(CythonTest):
def setUp(self):
CythonTest.setUp(self)
ip.extension_manager.load_extension('cython')
def test_cython_inline(self):
ip.ex('a=10; b=20')
result = ip.run_cell_magic('cython_inline', '', 'return a+b')
self.assertEqual(result, 30)
@dec.skip_win32
def test_cython_pyximport(self):
module_name = '_test_cython_pyximport'
ip.run_cell_magic('cython_pyximport', module_name, code)
ip.ex('g = f(10)')
self.assertEqual(ip.user_ns['g'], 20.0)
ip.run_cell_magic('cython_pyximport', module_name, code)
ip.ex('h = f(-10)')
self.assertEqual(ip.user_ns['h'], -20.0)
try:
os.remove(module_name + '.pyx')
except OSError:
pass
def test_cython(self):
ip.run_cell_magic('cython', '', code)
ip.ex('g = f(10)')
self.assertEqual(ip.user_ns['g'], 20.0)
def test_cython_name(self):
# The Cython module named 'mymodule' defines the function f.
ip.run_cell_magic('cython', '--name=mymodule', code)
# This module can now be imported in the interactive namespace.
ip.ex('import mymodule; g = mymodule.f(10)')
self.assertEqual(ip.user_ns['g'], 20.0)
@dec.skip_win32
def test_extlibs(self):
code = py3compat.str_to_unicode("""
from libc.math cimport sin
x = sin(0.0)
""")
ip.user_ns['x'] = 1
ip.run_cell_magic('cython', '-l m', code)
self.assertEqual(ip.user_ns['x'], 0)
=============================
The IPython licensing terms
=============================
IPython is licensed under the terms of the Modified BSD License (also known as
New or Revised or 3-Clause BSD), as follows:
- Copyright (c) 2008-2014, IPython Development Team
- Copyright (c) 2001-2007, Fernando Perez <fernando.perez@colorado.edu>
- Copyright (c) 2001, Janko Hauser <jhauser@zscout.de>
- Copyright (c) 2001, Nathaniel Gray <n8gray@caltech.edu>
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this
list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this
list of conditions and the following disclaimer in the documentation and/or
other materials provided with the distribution.
Neither the name of the IPython Development Team nor the names of its
contributors may be used to endorse or promote products derived from this
software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
About the IPython Development Team
----------------------------------
Fernando Perez began IPython in 2001 based on code from Janko Hauser
<jhauser@zscout.de> and Nathaniel Gray <n8gray@caltech.edu>. Fernando is still
the project lead.
The IPython Development Team is the set of all contributors to the IPython
project. This includes all of the IPython subprojects. A full list with
details is kept in the documentation directory, in the file
``about/credits.txt``.
The core team that coordinates development on GitHub can be found here:
https://github.com/ipython/.
Our Copyright Policy
--------------------
IPython uses a shared copyright model. Each contributor maintains copyright
over their contributions to IPython. But, it is important to note that these
contributions are typically only changes to the repositories. Thus, the IPython
source code, in its entirety is not the copyright of any single person or
institution. Instead, it is the collective copyright of the entire IPython
Development Team. If individual contributors want to maintain a record of what
changes/contributions they have specific copyright on, they should indicate
their copyright in the commit message of the change, when they commit the
change to one of the IPython repositories.
With this in mind, the following banner should be used in any source code file
to indicate the copyright and license terms:
::
# Copyright (c) IPython Development Team.
# Distributed under the terms of the Modified BSD License.
...@@ -2,3 +2,9 @@ from Cython.Shadow import __version__ ...@@ -2,3 +2,9 @@ from Cython.Shadow import __version__
# Void cython.* directives (for case insensitive operating systems). # Void cython.* directives (for case insensitive operating systems).
from Cython.Shadow import * from Cython.Shadow import *
def load_ipython_extension(ip):
"""Load the extension in IPython."""
from Cython.Build.IpythonMagic import CythonMagics
ip.register_magics(CythonMagics)
...@@ -21,3 +21,4 @@ else: ...@@ -21,3 +21,4 @@ else:
from Cython.Shadow import * from Cython.Shadow import *
## and bring in the __version__ ## and bring in the __version__
from Cython import __version__ from Cython import __version__
from Cython import load_ipython_extension
{
"metadata": {
"name": "Cython Magics",
"signature": "sha256:c357b93e9480d6347c6677862bf43750745cef4b30129c5bc53cb879a19d4074"
},
"nbformat": 3,
"nbformat_minor": 0,
"worksheets": [
{
"cells": [
{
"cell_type": "heading",
"level": 1,
"metadata": {},
"source": [
"Cython Magic Functions"
]
},
{
"cell_type": "heading",
"level": 2,
"metadata": {},
"source": [
"Loading the extension"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Cython has an IPython 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:"
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%load_ext cython"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 1
},
{
"cell_type": "heading",
"level": 2,
"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",
"collapsed": false,
"input": [
"a = 10\n",
"b = 20"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 2
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython_inline\n",
"return a+b"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 3,
"text": [
"30"
]
}
],
"prompt_number": 3
},
{
"cell_type": "heading",
"level": 2,
"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 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."
]
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"%%cython_pyximport foo\n",
"def f(x):\n",
" return 4.0*x"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 4
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"f(10)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 5,
"text": [
"40.0"
]
}
],
"prompt_number": 5
},
{
"cell_type": "heading",
"level": 2,
"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 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.\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",
"collapsed": false,
"input": [
"%%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"
],
"language": "python",
"metadata": {},
"outputs": [],
"prompt_number": 6
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"black_scholes_cy(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"metadata": {},
"output_type": "pyout",
"prompt_number": 7,
"text": [
"10.327861752731728"
]
}
],
"prompt_number": 7
},
{
"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
},
{
"cell_type": "code",
"collapsed": false,
"input": [
"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)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"1000000 loops, best of 3: 319 ns per loop\n"
]
}
],
"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
},
{
"cell_type": "heading",
"level": 2,
"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",
"collapsed": false,
"input": [
"%%cython -lm\n",
"from libc.math cimport sin\n",
"print 'sin(1)=', sin(1)"
],
"language": "python",
"metadata": {},
"outputs": [
{
"output_type": "stream",
"stream": "stdout",
"text": [
"sin(1)= 0.841470984808\n"
]
}
],
"prompt_number": 12
},
{
"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": {}
}
]
}
...@@ -95,6 +95,7 @@ EXT_DEP_MODULES = { ...@@ -95,6 +95,7 @@ EXT_DEP_MODULES = {
'tag:pstats': 'pstats', 'tag:pstats': 'pstats',
'tag:posix' : 'posix', 'tag:posix' : 'posix',
'tag:array' : 'array', 'tag:array' : 'array',
'tag:ipython': 'IPython'
} }
def patch_inspect_isfunction(): def patch_inspect_isfunction():
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment