{ "cells": [ { "cell_type": "markdown", "metadata": { "collapsed": false }, "source": [ "I was recently helping a student with some preliminary concepts in isogemetric analysis (IGA) and after taking a look at his pure Python implementation of the Cox - de Boor algorithm for computing B-Spline basis functions, I decided to look around for a Numpy implementation that could possibly be a little faster. While I found a few [B-spline implementations](https://github.com/scipy/scipy/blob/v0.15.1/scipy/signal/bsplines.py#L122) in Python, I thought I might be able to sqeeze out a little more performance through [vectorization](http://en.wikipedia.org/wiki/Array_programming) and [memoization](http://en.wikipedia.org/wiki/Memoization) while maintaining readablity of the code. I think I have something that accomplishes that, in the context of how basis functions are needed and used in IGA. The main portion of the code is here:" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "The full repository can be found/forked here: [johntfoster/bspline](https://github.com/johntfoster/bspline).\n", "\n", "An example use case is shown below. This reproduces Fig. 2.6 in [Isogeometric Analysis: Towards the integration of CAD and FEA](http://www.amazon.com/Isogeometric-Analysis-Toward-Integration-CAD/dp/0470748737/ref=sr_1_1?ie=UTF8&qid=1432735651&sr=8-1&keywords=isogeometric+analysis+toward+integration+of+cad+and+fea) by Contrell, Hughes, and Bazilevs. With [bspline.py](https://github.com/johntfoster/bspline/blob/master/bspline.py) in the working directory:" ] }, { "cell_type": "code", "execution_count": 1, "metadata": { "collapsed": false }, "outputs": [ { "data": { "image/png": "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\n", "text/plain": [ "" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "from bspline import Bspline\n", "\n", "knot_vector = [0,0,0,0,0,1,2,2,3,3,3,4,4,4,4,5,5,5,5,5]\n", "basis = Bspline(knot_vector,4)\n", "\n", "%matplotlib inline\n", "basis.plot()" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "As mentioned above the recursive function that computes the basis has been memoized such that it caches repeated evaluations of the function. This has a large impact on the evaluation speed. We can use [Jupyter](https://jupyter.org/)'s `%%timeit` magic function to investigate this. I've hidden the raw evaluation functions through Python's name mangaling, therefore the following function is not intended for general API usage, but for testing purposes, let's evalute the basis functions defined by the knot vector and basis order given above using the non-memoized code:" ] }, { "cell_type": "code", "execution_count": 2, "metadata": { "collapsed": true }, "outputs": [], "source": [ "import numpy as np\n", "x = np.linspace(0,5,num=1000)" ] }, { "cell_type": "code", "execution_count": 3, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "1 loops, best of 3: 192 ms per loop\n" ] } ], "source": [ "%%timeit \n", "[ basis._Bspline__basis(i, basis.p) for i in x ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "Now let's time the memoized code" ] }, { "cell_type": "code", "execution_count": 4, "metadata": { "collapsed": false }, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "100 loops, best of 3: 2.36 ms per loop\n" ] } ], "source": [ "%%timeit \n", "[ basis(i) for i in x ]" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "A speedup of roughly two orders of magnitude!" ] } ], "metadata": { "kernelspec": { "display_name": "Python 2", "language": "python", "name": "python2" }, "language_info": { "codemirror_mode": { "name": "ipython", "version": 2 }, "file_extension": ".py", "mimetype": "text/x-python", "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython2", "version": "2.7.9" } }, "nbformat": 4, "nbformat_minor": 0 }