{ "cells": [ { "cell_type": "markdown", "metadata": {}, "source": [ "> 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.\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "# 5.3. Wrapping a C library in Python with ctypes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "This example shows:\n", "\n", " * How to write and compile C code defining functions that are accessible from Python, and\n", " * How to call C functions from Python using the native ctypes module.\n", " \n", "This notebook has been written for Windows systems and Microsoft's C compiler (shipped with Visual Studio)." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "**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." ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's write the generation of the Mandelbrot fractal in C." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%%writefile mandelbrot.c\n", "\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.\n", "EXPORT 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", "}" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "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." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "!cl /LD mandelbrot.c" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "## Wrapping the C library with NumPy and ctypes" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Let's access the library with ctypes." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import ctypes" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "lb = ctypes.CDLL('mandelbrot.dll')" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "lib = ctypes.WinDLL(None, handle=lb._handle)" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Access the mandelbrot function.\n", "mandelbrot = lib.mandelbrot" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "NumPy and ctypes allow us to wrap the C function defined in the DLL." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from numpy.ctypeslib import ndpointer" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "# Define the types of the output and arguments of this function.\n", "mandelbrot.restype = None\n", "mandelbrot.argtypes = [ctypes.c_int, ctypes.c_int,\n", " ndpointer(ctypes.c_int)]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now, we can execute the mandelbrot function." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "# We initialize an empty array.\n", "size = 200\n", "iterations = 100\n", "col = np.empty((size, size), dtype=np.int32)\n", "# We execute the C function, which will update the array.\n", "mandelbrot(size, iterations, col)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The simulation has finished, let's display the fractal." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "import numpy as np\n", "import matplotlib.pyplot as plt\n", "%matplotlib inline" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "plt.imshow(np.log(col), cmap=plt.cm.hot,);\n", "plt.xticks([]);\n", "plt.yticks([]);" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "%timeit mandelbrot(size, iterations, col)" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "We free the library handle at the end." ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "lb._handle" ] }, { "cell_type": "code", "execution_count": null, "metadata": { "collapsed": false }, "outputs": [], "source": [ "from ctypes.wintypes import HMODULE\n", "ctypes.windll.kernel32.FreeLibrary.argtypes = [HMODULE]\n", "ctypes.windll.kernel32.FreeLibrary(lb._handle);" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "> You'll find all the explanations, figures, references, and much more in the book (to be released later this summer).\n", "\n", "> [IPython Cookbook](http://ipython-books.github.io/), by [Cyrille Rossant](http://cyrille.rossant.net), Packt Publishing, 2014 (500 pages)." ] } ], "metadata": { "kernelspec": { "display_name": "Python 3", "language": "python", "name": "python3" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 3 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.4.2" } }, "nbformat": 4, "nbformat_minor": 0 }