#!/usr/bin/env python # coding: utf-8 # # EuroSciPy 2015, Stefan Behnel # See http://consulting.behnel.de/ # In[ ]: get_ipython().run_line_magic('load_ext', 'cython') # In[ ]: import sys import Cython print("Python %d.%d.%d %s %s" % sys.version_info) print("Cython %s" % Cython.__version__) # # Building Cython modules # In[ ]: # simple setup without elaborate dependencies from distutils.core import setup from Cython.Build import cythonize setup( ext_modules = cythonize("*.pyx"), # <- glob pattern ) # In[ ]: # more complex case with per-module configuration from distutils.core import setup from Cython.Build import cythonize from distutils.extension import Extension ext_modules = [ Extension( ["mypackage/mymodule.pyx"], ... # configure C build + libraries here )] setup(ext_modules = cythonize(ext_modules)) # # Functions and Coercion # In[ ]: get_ipython().run_cell_magic('cython', '', '\ndef pyfunc(x):\n return x + 1\n\ndef cyfunc(int x):\n return x + 1\n\ncdef int cfunc(int x):\n return x + 1\n\ncpdef cypyfunc(int x):\n y = cfunc(x + 1)\n return y * 2\n') # In[ ]: pyfunc(2) # In[ ]: cyfunc(2) # In[ ]: cfunc(2) # In[ ]: cypyfunc(2) # # Static typing and type inference # In[ ]: import math def sin(x): return math.sin(x) # In[ ]: get_ipython().run_cell_magic('cython', '-a', 'cimport libc.math\n\ndef sin(double x):\n return libc.math.sin(x)\n') # In[ ]: get_ipython().run_cell_magic('cython', '', 'def local_variables(x):\n cdef int i = 5, ix = x\n print(i * ix)\n return (i + ix) // 2\n') # In[ ]: local_variables(2) # ## Exercise: optimise Python code into C code # Optimise this code so that it compiles to plain C code but uses as few type declarations as possible. # In[ ]: import math def py_circular_distance(radius, lon1, lat1, lon2, lat2): x = math.pi/180.0 a = (90.0-lat1) * x b = (90.0-lat2) * x theta = (lon2-lon1) * x c = math.acos((math.cos(a)*math.cos(b)) + (math.sin(a)*math.sin(b)*math.cos(theta))) return radius*c # In[ ]: get_ipython().run_cell_magic('cython', '-a', 'import math\n\ndef cy_circular_distance(radius, lon1, lat1, lon2, lat2):\n x = math.pi/180.0\n a = (90.0-lat1) * x\n b = (90.0-lat2) * x\n theta = (lon2-lon1) * x\n c = math.acos((math.cos(a)*math.cos(b)) + (math.sin(a)*math.sin(b)*math.cos(theta)))\n return radius*c\n') # In[ ]: print(py_circular_distance(10, 1.2, 2, 2, 4.3)) print(cy_circular_distance(10, 1.2, 2, 2, 4.3)) # In[ ]: get_ipython().run_line_magic('timeit', 'py_circular_distance(10, 1.2, 2, 2, 4.3)') # In[ ]: get_ipython().run_line_magic('timeit', 'cy_circular_distance(10, 1.2, 2, 2, 4.3)') # # Calling C functions # In[ ]: get_ipython().run_cell_magic('cython', '-a', '# libc math functions\nfrom libc cimport math\n\nprint( math.sin(math.M_PI / 2) )\n') # In[ ]: get_ipython().run_cell_magic('cython', '', '# dynamic C memory allocation\nfrom libc.stdlib cimport malloc, free\n\ncdef int* cmem = malloc(22 * sizeof(int))\nif not cmem:\n raise MemoryError()\nfree(cmem)\n') # In[ ]: get_ipython().run_cell_magic('cython', '', '# dynamic CPython heap memory allocation\nfrom cpython.mem cimport PyMem_Malloc, PyMem_Free\n\ncdef int* pymem = PyMem_Malloc(22 * sizeof(int))\nif not pymem:\n raise MemoryError()\n \ntry:\n pymem[:] = [1,2,3]\n print( pymem[0] + pymem[2] )\nfinally:\n PyMem_Free(pymem)\n') # # Auto-wrapping C functions to Python # In[ ]: get_ipython().run_cell_magic('cython', '-a', 'from libc cimport math, stdlib\npy_sin = math.sin\npy_atoi = stdlib.atoi\n') # In[ ]: py_sin(1/2) # In[ ]: py_atoi(b'123') # # External libraries # In[ ]: get_ipython().run_cell_magic('cython', '', '# distutils: include_dirs=/usr/include/luajit-2.0\n# distutils: libraries=luajit-5.1\n\n## distutils: include_dirs=/usr/include/lua5.1\n## distutils: libraries=lua5.1\n\ncdef extern from "lua.h":\n ctypedef struct lua_State\n lua_State *luaL_newstate ()\n void lua_close (lua_State *L)\n int luaL_loadbuffer (lua_State *L, char *buff, size_t sz, char *name)\n void lua_settop (lua_State *L, int idx)\n int lua_gettop (lua_State *L)\n int lua_pcall (lua_State *L, int nargs, int nresults, int errfunc)\n int lua_type (lua_State *L, int idx)\n float lua_tonumber (lua_State *L, int idx)\n enum:\n LUA_TNUMBER\n LUA_MULTRET\n\n\ndef run_lua(code):\n cdef int result_status\n cdef float result\n\n if isinstance(code, unicode):\n code = code.encode(\'utf8\')\n elif not isinstance(code, bytes):\n raise ValueError("code must be a string")\n\n # init Lua runtime\n L = luaL_newstate()\n if not L:\n raise MemoryError()\n\n try:\n # compile Lua code\n if luaL_loadbuffer(L, code, len(code), \'\'):\n raise SyntaxError()\n\n # execute code\n if lua_pcall(L, 0, LUA_MULTRET, 0):\n raise RuntimeError()\n\n # convert return value (Lua number == float)\n assert lua_type(L, 1) == LUA_TNUMBER, "not a numeric return value"\n return lua_tonumber(L, 1)\n finally:\n lua_settop(L, 0)\n lua_close(L)\n') # In[ ]: code = ''' function fib(i) if i > 2 then return fib(i-1) + fib(i-2) else return 1 end end ''' run_lua(code + "return fib(10)") # In[ ]: get_ipython().run_cell_magic('timeit', 'bench = code + "return fib(24)"', '\nrun_lua(bench)\n') # In[ ]: # distutils: include_dirs=/usr/include/luajit-2.0 # distutils: libraries=luajit-5.1 # # C arrays # In[ ]: get_ipython().run_cell_magic('cython', '-a', 'def carrays():\n cdef int[10] a, b\n a[:5] = [1,2,3,4,5]\n b = a\n b[5:] = [6,7,8,9,10]\n\n for i in b[:3]:\n print(i+1)\n\n return b\n') # In[ ]: carrays() # # C tuples # In[ ]: get_ipython().run_cell_magic('cython', '-a', '\ndef ctuples():\n cdef int a = 42\n cdef double b = a / 5.0\n\n cdef (int, double) t = (a, b)\n print(t[0])\n print(t[1])\n print(t)\n\n return t\n') # In[ ]: ctuples() # # C++ # In[ ]: get_ipython().run_cell_magic('cython', '-a', '# distutils: language=c++\n\n\nfrom libcpp.vector cimport vector\n\ndef func():\n cdef vector[int] v\n v.push_back(10)\n return v\n') # In[ ]: func() # In[ ]: get_ipython().run_cell_magic('cython', '-a', '# distutils: language=c++\n\nfrom libcpp.vector cimport vector\n\ncdef class Integers:\n cdef vector[int] _values\n\n def add(self, int value):\n self._values.push_back(value)\n\n def __repr__(self):\n return repr(self._values)\n') # In[ ]: x = Integers() x.add(2) x.add(3) x