diff --git a/.gitignore b/.gitignore index 066262adf027e18ea15a2e65159ba05b99a80546..f4ca5447e2f1632bd9e0dd5fc73f1f16b159ba87 100644 --- a/.gitignore +++ b/.gitignore @@ -49,3 +49,4 @@ distribute-*.tar.gz core *.sess _build +.ipynb_checkpoints diff --git a/MANIFEST.in b/MANIFEST.in index aef7d36079902368807d1d82c6c55321870160b6..d3f99bb682c3fee18a57a91d4bf234fe2bbf7238 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -6,6 +6,7 @@ include src/wrapper/*.cpp include test/*.py include test/*.h include examples/*.py +include examples/*.ipynb include doc/source/*.rst include doc/Makefile diff --git a/doc/misc.rst b/doc/misc.rst index 794fa982281e38718217bf4cb31a93dcfc4af50d..dcaeb8fc889f1320a8a2fad677b6f2460591a362 100644 --- a/doc/misc.rst +++ b/doc/misc.rst @@ -24,6 +24,21 @@ checking `this file Note that the triple-quoted strings containing the source must start with `"""//CL// ..."""`. +IPython integration +------------------- + +PyOpenCL comes with IPython integration, which lets you seamlessly integrate +PyOpenCL kernels into your IPython notebooks. Simply load the PyOpenCL +IPython extension using:: + + %%load_ext pyopencl.ipython + +and then use the ``%%cl_kernel`` 'cell-magic' command. See `this notebook +`_ +(which ships with PyOpenCL) for a demonstration. + +.. versionadded:: 2014.1 + Guidelines ========== @@ -91,13 +106,16 @@ other software to be turned into the corresponding :mod:`pyopencl` objects. User-visible Changes ==================== -Version 2013.3 +Version 2014.1 -------------- .. note:: This version is currently under development. You can get snapshots from PyOpenCL's `git repository `_ +* :ref:`ipython-integration` +* Bug fixes + Version 2013.2 -------------- diff --git a/examples/ipython-demo.ipynb b/examples/ipython-demo.ipynb new file mode 100644 index 0000000000000000000000000000000000000000..a9da829411e4ed38313dbbeaf9ba0dccfb4132ec --- /dev/null +++ b/examples/ipython-demo.ipynb @@ -0,0 +1,192 @@ +{ + "metadata": { + "name": "", + "signature": "sha256:feb0eba3ebfcee43d39ecc6ec00c204e004dfdde2b89c4afe16f3c19bf2ee045" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "code", + "collapsed": false, + "input": [ + "from __future__ import division\n", + "import pyopencl as cl\n", + "import pyopencl.array" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 2 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Load the PyOpenCL IPython extension:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%load_ext pyopencl.ipython" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 3 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Create an OpenCL context and a command queue:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "ctx = cl.create_some_context(interactive=True)\n", + "queue = cl.CommandQueue(ctx)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Choose platform:\n", + "[0] \n", + "[1] \n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "stream": "stdout", + "text": [ + "Choice [0]:0\n" + ] + }, + { + "output_type": "stream", + "stream": "stdout", + "text": [ + "Set the environment variable PYOPENCL_CTX='0' to avoid being asked again.\n" + ] + } + ], + "prompt_number": 4 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "-----\n", + "\n", + "Define an OpenCL kernel using the `%%cl_kernel` magic:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "%%cl_kernel\n", + "\n", + "__kernel void sum_vector(__global const float *a,\n", + "__global const float *b, __global float *c)\n", + "{\n", + " int gid = get_global_id(0);\n", + " c[gid] = a[gid] + b[gid];\n", + "}" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 8 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "This looks for `cl_ctx` or `ctx` in the user namespace to find a PyOpenCL context.\n", + "\n", + "Kernel names are automatically injected into the user namespace, so we can just use `sum_vector` from Python below.\n", + "\n", + "Now create some data to work on:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "n = 10000\n", + "\n", + "a = cl.array.empty(queue, n, dtype=np.float32)\n", + "a.fill(15)\n", + "\n", + "b_host = np.random.randn(n).astype(np.float32)\n", + "b = cl.array.to_device(queue, b_host)\n", + "\n", + "c = cl.array.empty_like(a)" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 10 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Run the kernel:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "sum_vector(queue, (n,), None, a.data, b.data, c.data)" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 11, + "text": [ + "" + ] + } + ], + "prompt_number": 11 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Check the result using `numpy` operations:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "assert (c.get() == b_host + 15).all()" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 12 + } + ], + "metadata": {} + } + ] +} \ No newline at end of file diff --git a/pyopencl/__init__.py b/pyopencl/__init__.py index 6e60f793baa87d702e75c08c2ba1d2abdaa931cb..88749a73d2bfba683979cbb1e9daa3091d883c72 100644 --- a/pyopencl/__init__.py +++ b/pyopencl/__init__.py @@ -767,7 +767,7 @@ _add_functionality() # {{{ convenience -def create_some_context(interactive=True, answers=None): +def create_some_context(interactive=None, answers=None): import os if answers is None and "PYOPENCL_CTX" in os.environ: ctx_spec = os.environ["PYOPENCL_CTX"] @@ -781,12 +781,14 @@ def create_some_context(interactive=True, answers=None): user_inputs = [] - try: - import sys - if not sys.stdin.isatty(): + if interactive is None: + interactive = True + try: + import sys + if not sys.stdin.isatty(): + interactive = False + except: interactive = False - except: - interactive = False def cc_print(s): if interactive: diff --git a/pyopencl/ipython.py b/pyopencl/ipython.py new file mode 100644 index 0000000000000000000000000000000000000000..309a6830f06d6904e9ed5123edcf14931b275e75 --- /dev/null +++ b/pyopencl/ipython.py @@ -0,0 +1,37 @@ +from __future__ import division + +from IPython.core.magic import (magics_class, Magics, cell_magic) + +import pyopencl as cl + + +@magics_class +class PyOpenCLMagics(Magics): + @cell_magic + def cl_kernel(self, line, cell): + try: + ctx = self.shell.user_ns["cl_ctx"] + except KeyError: + ctx = None + + if not isinstance(ctx, cl.Context): + ctx = None + + if ctx is None: + try: + ctx = self.shell.user_ns["ctx"] + except KeyError: + ctx = None + + if ctx is None: + raise RuntimeError("unable to locate cl context, which must be " + "present in namespace as 'cl_ctx' or 'ctx'") + + prg = cl.Program(ctx, cell.encode("utf8")).build() + + for knl in prg.all_kernels(): + self.shell.user_ns[knl.function_name] = knl + + +def load_ipython_extension(ip): + ip.register_magics(PyOpenCLMagics)