Show More
@@ -0,0 +1,145 b'' | |||||
|
1 | # -*- coding: utf-8 -*- | |||
|
2 | """ | |||
|
3 | Cython related magics. | |||
|
4 | ||||
|
5 | Author: | |||
|
6 | * Brian Granger | |||
|
7 | ||||
|
8 | Parts of this code were taken from Cython.inline. | |||
|
9 | """ | |||
|
10 | #----------------------------------------------------------------------------- | |||
|
11 | # Copyright (C) 2010-2011, IPython Development Team. | |||
|
12 | # | |||
|
13 | # Distributed under the terms of the Modified BSD License. | |||
|
14 | # | |||
|
15 | # The full license is in the file COPYING.txt, distributed with this software. | |||
|
16 | #----------------------------------------------------------------------------- | |||
|
17 | ||||
|
18 | import os, sys | |||
|
19 | from importlib import import_module | |||
|
20 | import imp | |||
|
21 | ||||
|
22 | try: | |||
|
23 | import hashlib | |||
|
24 | except ImportError: | |||
|
25 | import md5 as hashlib | |||
|
26 | ||||
|
27 | from distutils.core import Distribution, Extension | |||
|
28 | from distutils.command.build_ext import build_ext | |||
|
29 | ||||
|
30 | from IPython.core.magic import Magics, magics_class, cell_magic | |||
|
31 | from IPython.testing.skipdoctest import skip_doctest | |||
|
32 | from IPython.core.magic_arguments import ( | |||
|
33 | argument, magic_arguments, parse_argstring | |||
|
34 | ) | |||
|
35 | ||||
|
36 | import Cython | |||
|
37 | from Cython.Compiler.Errors import CompileError | |||
|
38 | from Cython.Compiler.Main import Context, default_options | |||
|
39 | from Cython.Build.Dependencies import cythonize | |||
|
40 | ||||
|
41 | ||||
|
42 | @magics_class | |||
|
43 | class CythonMagics(Magics): | |||
|
44 | ||||
|
45 | def __init__(self, shell): | |||
|
46 | super(CythonMagics,self).__init__(shell) | |||
|
47 | self._reloads = {} | |||
|
48 | self._code_cache = {} | |||
|
49 | ||||
|
50 | def _import_all(self, module): | |||
|
51 | for k,v in module.__dict__.items(): | |||
|
52 | if not k.startswith('__'): | |||
|
53 | self.shell.push({k:v}) | |||
|
54 | ||||
|
55 | @skip_doctest | |||
|
56 | @cell_magic | |||
|
57 | def cython_inline(self, line, cell): | |||
|
58 | """Compile and run a Cython code cell using Cython.inline.""" | |||
|
59 | locs = self.shell.user_global_ns | |||
|
60 | globs = self.shell.user_ns | |||
|
61 | return Cython.inline(cell, locals=locs, globals=globs) | |||
|
62 | ||||
|
63 | @skip_doctest | |||
|
64 | @cell_magic | |||
|
65 | def cython_pyximport(self, line, cell): | |||
|
66 | """Compile and import a Cython code cell using pyximport.""" | |||
|
67 | module_name = line.strip() | |||
|
68 | if not module_name: | |||
|
69 | raise ValueError('module name must be given') | |||
|
70 | fname = module_name + '.pyx' | |||
|
71 | with open(fname, 'w') as f: | |||
|
72 | f.write(cell) | |||
|
73 | if 'pyximport' not in sys.modules: | |||
|
74 | import pyximport | |||
|
75 | pyximport.install(reload_support=True) | |||
|
76 | if module_name in self._reloads: | |||
|
77 | module = self._reloads[module_name] | |||
|
78 | reload(module) | |||
|
79 | else: | |||
|
80 | module = import_module(module_name) | |||
|
81 | self._reloads[module_name] = module | |||
|
82 | self._import_all(module) | |||
|
83 | ||||
|
84 | @magic_arguments() | |||
|
85 | @argument( | |||
|
86 | '-f', '--force', action='store_true', default=False, | |||
|
87 | help="Force the compilation of the pyx module even if it hasn't changed" | |||
|
88 | ) | |||
|
89 | @skip_doctest | |||
|
90 | @cell_magic | |||
|
91 | def cython(self, line, cell): | |||
|
92 | """Compile and import everything from a Cython code cell.""" | |||
|
93 | args = parse_argstring(self.cython, line) | |||
|
94 | code = cell if cell.endswith('\n') else cell+'\n' | |||
|
95 | lib_dir=os.path.expanduser('~/.cython/magic') | |||
|
96 | cython_include_dirs=['.'] | |||
|
97 | force=args.force | |||
|
98 | quiet=True | |||
|
99 | ctx = Context(cython_include_dirs, default_options) | |||
|
100 | key = code, sys.version_info, sys.executable, Cython.__version__ | |||
|
101 | module_name = "_cython_magic_" + hashlib.md5(str(key).encode('utf-8')).hexdigest() | |||
|
102 | so_ext = [ ext for ext,_,mod_type in imp.get_suffixes() if mod_type == imp.C_EXTENSION ][0] | |||
|
103 | module_path = os.path.join(lib_dir, module_name+so_ext) | |||
|
104 | ||||
|
105 | if not os.path.exists(lib_dir): | |||
|
106 | os.makedirs(lib_dir) | |||
|
107 | ||||
|
108 | if force or not os.path.isfile(module_path): | |||
|
109 | cflags = [] | |||
|
110 | c_include_dirs = [] | |||
|
111 | if 'numpy' in code: | |||
|
112 | import numpy | |||
|
113 | c_include_dirs.append(numpy.get_include()) | |||
|
114 | pyx_file = os.path.join(lib_dir, module_name + '.pyx') | |||
|
115 | with open(pyx_file, 'w') as f: | |||
|
116 | f.write(code) | |||
|
117 | extension = Extension( | |||
|
118 | name = module_name, | |||
|
119 | sources = [pyx_file], | |||
|
120 | include_dirs = c_include_dirs, | |||
|
121 | extra_compile_args = cflags | |||
|
122 | ) | |||
|
123 | build_extension = build_ext(Distribution()) | |||
|
124 | build_extension.finalize_options() | |||
|
125 | try: | |||
|
126 | build_extension.extensions = cythonize([extension], ctx=ctx, quiet=quiet) | |||
|
127 | except CompileError: | |||
|
128 | return | |||
|
129 | build_extension.build_temp = os.path.dirname(pyx_file) | |||
|
130 | build_extension.build_lib = lib_dir | |||
|
131 | build_extension.run() | |||
|
132 | self._code_cache[key] = module_name | |||
|
133 | ||||
|
134 | module = imp.load_dynamic(module_name, module_path) | |||
|
135 | self._import_all(module) | |||
|
136 | ||||
|
137 | ||||
|
138 | _loaded = False | |||
|
139 | ||||
|
140 | def load_ipython_extension(ip): | |||
|
141 | """Load the extension in IPython.""" | |||
|
142 | global _loaded | |||
|
143 | if not _loaded: | |||
|
144 | ip.register_magics(CythonMagics) | |||
|
145 | _loaded = True |
@@ -0,0 +1,226 b'' | |||||
|
1 | { | |||
|
2 | "metadata": { | |||
|
3 | "name": "cython_extension" | |||
|
4 | }, | |||
|
5 | "nbformat": 3, | |||
|
6 | "worksheets": [ | |||
|
7 | { | |||
|
8 | "cells": [ | |||
|
9 | { | |||
|
10 | "cell_type": "heading", | |||
|
11 | "level": 1, | |||
|
12 | "source": [ | |||
|
13 | "Cython Magic Functions Extension" | |||
|
14 | ] | |||
|
15 | }, | |||
|
16 | { | |||
|
17 | "cell_type": "heading", | |||
|
18 | "level": 2, | |||
|
19 | "source": [ | |||
|
20 | "Loading the extension" | |||
|
21 | ] | |||
|
22 | }, | |||
|
23 | { | |||
|
24 | "cell_type": "markdown", | |||
|
25 | "source": [ | |||
|
26 | "IPtyhon 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:" | |||
|
27 | ] | |||
|
28 | }, | |||
|
29 | { | |||
|
30 | "cell_type": "code", | |||
|
31 | "collapsed": false, | |||
|
32 | "input": [ | |||
|
33 | "%load_ext cythonmagic" | |||
|
34 | ], | |||
|
35 | "language": "python", | |||
|
36 | "outputs": [], | |||
|
37 | "prompt_number": 1 | |||
|
38 | }, | |||
|
39 | { | |||
|
40 | "cell_type": "heading", | |||
|
41 | "level": 2, | |||
|
42 | "source": [ | |||
|
43 | "The %cython_inline magic" | |||
|
44 | ] | |||
|
45 | }, | |||
|
46 | { | |||
|
47 | "cell_type": "markdown", | |||
|
48 | "source": [ | |||
|
49 | "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. " | |||
|
50 | ] | |||
|
51 | }, | |||
|
52 | { | |||
|
53 | "cell_type": "code", | |||
|
54 | "collapsed": true, | |||
|
55 | "input": [ | |||
|
56 | "a = 10", | |||
|
57 | "b = 20" | |||
|
58 | ], | |||
|
59 | "language": "python", | |||
|
60 | "outputs": [], | |||
|
61 | "prompt_number": 8 | |||
|
62 | }, | |||
|
63 | { | |||
|
64 | "cell_type": "code", | |||
|
65 | "collapsed": false, | |||
|
66 | "input": [ | |||
|
67 | "%%cython_inline", | |||
|
68 | "return a+b" | |||
|
69 | ], | |||
|
70 | "language": "python", | |||
|
71 | "outputs": [ | |||
|
72 | { | |||
|
73 | "output_type": "pyout", | |||
|
74 | "prompt_number": 9, | |||
|
75 | "text": [ | |||
|
76 | "30" | |||
|
77 | ] | |||
|
78 | } | |||
|
79 | ], | |||
|
80 | "prompt_number": 9 | |||
|
81 | }, | |||
|
82 | { | |||
|
83 | "cell_type": "heading", | |||
|
84 | "level": 2, | |||
|
85 | "source": [ | |||
|
86 | "The %cython_pyximport magic" | |||
|
87 | ] | |||
|
88 | }, | |||
|
89 | { | |||
|
90 | "cell_type": "markdown", | |||
|
91 | "source": [ | |||
|
92 | "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." | |||
|
93 | ] | |||
|
94 | }, | |||
|
95 | { | |||
|
96 | "cell_type": "code", | |||
|
97 | "collapsed": false, | |||
|
98 | "input": [ | |||
|
99 | "%%cython_pyximport foo", | |||
|
100 | "def f(x):", | |||
|
101 | " return 4.0*x" | |||
|
102 | ], | |||
|
103 | "language": "python", | |||
|
104 | "outputs": [], | |||
|
105 | "prompt_number": 18 | |||
|
106 | }, | |||
|
107 | { | |||
|
108 | "cell_type": "code", | |||
|
109 | "collapsed": false, | |||
|
110 | "input": [ | |||
|
111 | "f(10)" | |||
|
112 | ], | |||
|
113 | "language": "python", | |||
|
114 | "outputs": [ | |||
|
115 | { | |||
|
116 | "output_type": "pyout", | |||
|
117 | "prompt_number": 19, | |||
|
118 | "text": [ | |||
|
119 | "40.0" | |||
|
120 | ] | |||
|
121 | } | |||
|
122 | ], | |||
|
123 | "prompt_number": 19 | |||
|
124 | }, | |||
|
125 | { | |||
|
126 | "cell_type": "heading", | |||
|
127 | "level": 2, | |||
|
128 | "source": [ | |||
|
129 | "The %cython magic" | |||
|
130 | ] | |||
|
131 | }, | |||
|
132 | { | |||
|
133 | "cell_type": "markdown", | |||
|
134 | "source": [ | |||
|
135 | "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.", | |||
|
136 | "", | |||
|
137 | "Here is a simple example of a Black-Scholes options pricing algorithm written in Cython:" | |||
|
138 | ] | |||
|
139 | }, | |||
|
140 | { | |||
|
141 | "cell_type": "code", | |||
|
142 | "collapsed": false, | |||
|
143 | "input": [ | |||
|
144 | "%%cython", | |||
|
145 | "cimport cython", | |||
|
146 | "from libc.math cimport exp, sqrt, pow, log, erf", | |||
|
147 | "", | |||
|
148 | "@cython.cdivision(True)", | |||
|
149 | "cdef double std_norm_cdf(double x) nogil:", | |||
|
150 | " return 0.5*(1+erf(x/sqrt(2.0)))", | |||
|
151 | "", | |||
|
152 | "@cython.cdivision(True)", | |||
|
153 | "def black_scholes(double s, double k, double t, double v,", | |||
|
154 | " double rf, double div, double cp):", | |||
|
155 | " \"\"\"Price an option using the Black-Scholes model.", | |||
|
156 | " ", | |||
|
157 | " s : initial stock price", | |||
|
158 | " k : strike price", | |||
|
159 | " t : expiration time", | |||
|
160 | " v : volatility", | |||
|
161 | " rf : risk-free rate", | |||
|
162 | " div : dividend", | |||
|
163 | " cp : +1/-1 for call/put", | |||
|
164 | " \"\"\"", | |||
|
165 | " cdef double d1, d2, optprice", | |||
|
166 | " with nogil:", | |||
|
167 | " d1 = (log(s/k)+(rf-div+0.5*pow(v,2))*t)/(v*sqrt(t))", | |||
|
168 | " d2 = d1 - v*sqrt(t)", | |||
|
169 | " optprice = cp*s*exp(-div*t)*std_norm_cdf(cp*d1) - \\", | |||
|
170 | " cp*k*exp(-rf*t)*std_norm_cdf(cp*d2)", | |||
|
171 | " return optprice" | |||
|
172 | ], | |||
|
173 | "language": "python", | |||
|
174 | "outputs": [], | |||
|
175 | "prompt_number": 6 | |||
|
176 | }, | |||
|
177 | { | |||
|
178 | "cell_type": "code", | |||
|
179 | "collapsed": false, | |||
|
180 | "input": [ | |||
|
181 | "black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" | |||
|
182 | ], | |||
|
183 | "language": "python", | |||
|
184 | "outputs": [ | |||
|
185 | { | |||
|
186 | "output_type": "pyout", | |||
|
187 | "prompt_number": 7, | |||
|
188 | "text": [ | |||
|
189 | "10.327861752731728" | |||
|
190 | ] | |||
|
191 | } | |||
|
192 | ], | |||
|
193 | "prompt_number": 7 | |||
|
194 | }, | |||
|
195 | { | |||
|
196 | "cell_type": "code", | |||
|
197 | "collapsed": false, | |||
|
198 | "input": [ | |||
|
199 | "%timeit black_scholes(100.0, 100.0, 1.0, 0.3, 0.03, 0.0, -1)" | |||
|
200 | ], | |||
|
201 | "language": "python", | |||
|
202 | "outputs": [ | |||
|
203 | { | |||
|
204 | "output_type": "stream", | |||
|
205 | "stream": "stdout", | |||
|
206 | "text": [ | |||
|
207 | "1000000 loops, best of 3: 621 ns per loop", | |||
|
208 | "" | |||
|
209 | ] | |||
|
210 | } | |||
|
211 | ], | |||
|
212 | "prompt_number": 14 | |||
|
213 | }, | |||
|
214 | { | |||
|
215 | "cell_type": "code", | |||
|
216 | "collapsed": true, | |||
|
217 | "input": [ | |||
|
218 | "" | |||
|
219 | ], | |||
|
220 | "language": "python", | |||
|
221 | "outputs": [] | |||
|
222 | } | |||
|
223 | ] | |||
|
224 | } | |||
|
225 | ] | |||
|
226 | } No newline at end of file |
General Comments 0
You need to be logged in to leave comments.
Login now