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