#!/usr/bin/env python # coding: utf-8 # > This is one of the 100 recipes of the [IPython Cookbook](http://ipython-books.github.io/), the definitive guide to high-performance scientific computing and data science in Python. # # # 5.3. Wrapping a C library in Python with ctypes # This example shows: # # * How to write and compile C code defining functions that are accessible from Python, and # * How to call C functions from Python using the native ctypes module. # # This notebook has been written for Windows systems and Microsoft's C compiler (shipped with Visual Studio). # **Note**: on Windows, for the C compiler to run, you need to execute a sequence of magic incantations before launching the IPython notebook. See the `_launch_notebook.bat` file in this repository. # Let's write the generation of the Mandelbrot fractal in C. # In[ ]: get_ipython().run_cell_magic('writefile', 'mandelbrot.c', '\n// Needed when creating a DLL.\n#define EXPORT __declspec(dllexport)\n\n#include "stdio.h"\n#include "stdlib.h"\n\n// This function will be available in the DLL.\nEXPORT void __stdcall mandelbrot(int size,\n int iterations,\n int *col) \n{\n // Variable declarations.\n int i, j, n, index;\n double cx, cy;\n double z0, z1, z0_tmp, z0_2, z1_2;\n \n // Loop within the grid.\n for (i = 0; i < size; i++)\n {\n cy = -1.5 + (double)i / size * 3;\n for (j = 0; j < size; j++)\n {\n // We initialize the loop of the system.\n cx = -2.0 + (double)j / size * 3;\n index = i * size + j;\n // Let\'s run the system.\n z0 = 0.0;\n z1 = 0.0;\n for (n = 0; n < iterations; n++)\n {\n z0_2 = z0 * z0;\n z1_2 = z1 * z1;\n if (z0_2 + z1_2 <= 100)\n {\n // Update the system.\n z0_tmp = z0_2 - z1_2 + cx;\n z1 = 2 * z0 * z1 + cy;\n z0 = z0_tmp;\n col[index] = n;\n }\n else\n {\n break;\n }\n }\n }\n }\n}\n') # Now, let's build this C source file into a DLL with Microsoft Visual Studio's `cl.exe`. The `/LD` option specifies that a DLL has to be created. # In[ ]: get_ipython().system('cl /LD mandelbrot.c') # ## Wrapping the C library with NumPy and ctypes # Let's access the library with ctypes. # In[ ]: import ctypes # In[ ]: lb = ctypes.CDLL('mandelbrot.dll') # In[ ]: lib = ctypes.WinDLL(None, handle=lb._handle) # In[ ]: # Access the mandelbrot function. mandelbrot = lib.mandelbrot # NumPy and ctypes allow us to wrap the C function defined in the DLL. # In[ ]: from numpy.ctypeslib import ndpointer # In[ ]: # Define the types of the output and arguments of this function. mandelbrot.restype = None mandelbrot.argtypes = [ctypes.c_int, ctypes.c_int, ndpointer(ctypes.c_int)] # Now, we can execute the mandelbrot function. # In[ ]: import numpy as np # We initialize an empty array. size = 200 iterations = 100 col = np.empty((size, size), dtype=np.int32) # We execute the C function, which will update the array. mandelbrot(size, iterations, col) # The simulation has finished, let's display the fractal. # In[ ]: import numpy as np import matplotlib.pyplot as plt get_ipython().run_line_magic('matplotlib', 'inline') # In[ ]: plt.imshow(np.log(col), cmap=plt.cm.hot,); plt.xticks([]); plt.yticks([]); # In[ ]: get_ipython().run_line_magic('timeit', 'mandelbrot(size, iterations, col)') # We free the library handle at the end. # In[ ]: lb._handle # In[ ]: from ctypes.wintypes import HMODULE ctypes.windll.kernel32.FreeLibrary.argtypes = [HMODULE] ctypes.windll.kernel32.FreeLibrary(lb._handle); # > You'll find all the explanations, figures, references, and much more in the book (to be released later this summer). # # > [IPython Cookbook](http://ipython-books.github.io/), by [Cyrille Rossant](http://cyrille.rossant.net), Packt Publishing, 2014 (500 pages).