The autowrap module works very well in tandem with the Indexed classes of the Tensor Module. Here is a simple example that shows how to setup a binary routine that calculates a matrix-vector product.
>>> from sympy.utilities.autowrap import autowrap
>>> from sympy import symbols, IndexedBase, Idx, Eq
>>> A, x, y = map(IndexedBase, ['A', 'x', 'y'])
>>> m, n = symbols('m n', integer=True)
>>> i = Idx('i', m)
>>> j = Idx('j', n)
>>> instruction = Eq(y[i], A[i, j]*x[j]); instruction
y[i] == A[i, j]*x[j]
Because the code printers treat Indexed objects with repeated indices as a summation, the above equality instance will be translated to low-level code for a matrix vector product. This is how you tell SymPy to generate the code, compile it and wrap it as a python function:
>>> matvec = autowrap(instruction)
That’s it. Now let’s test it with some numpy arrays. The default wrapper backend is f2py. The wrapper function it provides is set up to accept python lists, which it will silently convert to numpy arrays. So we can test the matrix vector product like this:
>>> M = [[0, 1],
... [1, 0]]
>>> matvec(M, [2, 3])
[ 3. 2.]
The autowrap module is implemented with a backend consisting of CodeWrapper objects. The base class CodeWrapper takes care of details about module name, filenames and options. It also contains the driver routine, which runs through all steps in the correct order, and also takes care of setting up and removing the temporary working directory.
The actual compilation and wrapping is done by external resources, such as the system installed f2py command. The Cython backend runs a distutils setup script in a subprocess. Subclasses of CodeWrapper takes care of these backend-dependent details.
Module for compiling codegen output, and wrap the binary for use in python.
Note
To use the autowrap module it must first be imported
>>> from sympy.utilities.autowrap import autowrap
This module provides a common interface for different external backends, such as f2py, fwrap, Cython, SWIG(?) etc. (Currently only f2py and Cython are implemented) The goal is to provide access to compiled binaries of acceptable performance with a one-button user interface, i.e.
>>> from sympy.abc import x,y
>>> expr = ((x - y)**(25)).expand()
>>> binary_callable = autowrap(expr)
>>> binary_callable(1, 2)
-1.0
The callable returned from autowrap() is a binary python function, not a SymPy object. If it is desired to use the compiled function in symbolic expressions, it is better to use binary_function() which returns a SymPy Function object. The binary callable is attached as the _imp_ attribute and invoked when a numerical evaluation is requested with evalf(), or with lambdify().
>>> from sympy.utilities.autowrap import binary_function
>>> f = binary_function('f', expr)
>>> 2*f(x, y) + y
y + 2*f(x, y)
>>> (2*f(x, y) + y).evalf(2, subs={x: 1, y:2})
0.0
The idea is that a SymPy user will primarily be interested in working with mathematical expressions, and should not have to learn details about wrapping tools in order to evaluate expressions numerically, even if they are computationally expensive.
When is this useful?
- For computations on large arrays, Python iterations may be too slow, and depending on the mathematical expression, it may be difficult to exploit the advanced index operations provided by NumPy.
- For really long expressions that will be called repeatedly, the compiled binary should be significantly faster than SymPy’s .evalf()
- If you are generating code with the codegen utility in order to use it in another project, the automatic python wrappers let you test the binaries immediately from within SymPy.
- To create customized ufuncs for use with numpy arrays. See ufuncify.
When is this module NOT the best approach?
- If you are really concerned about speed or memory optimizations, you will probably get better results by working directly with the wrapper tools and the low level code. However, the files generated by this utility may provide a useful starting point and reference code. Temporary files will be left intact if you supply the keyword tempdir=”path/to/files/”.
- If the array computation can be handled easily by numpy, and you don’t need the binaries for another project.
Base Class for code wrappers
Wrapper that uses Cython
Write a Cython file with python wrappers
This file contains all the definitions of the routines in c code and refers to the header file.
Arguments : |
---|
Class used for testing independent of backends
Wrapper that uses f2py
Generates python callable binaries based on the math expression.
Optional arguments: | |
---|---|
>>> from sympy.abc import x, y, z
>>> from sympy.utilities.autowrap import autowrap
>>> expr = ((x - y + z)**(13)).expand()
>>> binary_func = autowrap(expr)
>>> binary_func(1, 4, 2)
-1.0
Returns a sympy function with expr as binary implementation
This is a convenience function that automates the steps needed to autowrap the SymPy expression and attaching it to a Function object with implemented_function().
>>> from sympy.abc import x, y, z
>>> from sympy.utilities.autowrap import binary_function
>>> expr = ((x - y)**(25)).expand()
>>> f = binary_function('f', expr)
>>> type(f)
<class 'sympy.core.function.FunctionClass'>
>>> 2*f(x, y)
2*f(x, y)
>>> f(x, y).evalf(2, subs={x: 1, y: 2})
-1.0
Generates a binary ufunc-like lambda function for numpy arrays
The returned function can only act on one array at a time, as only the first argument accept arrays as input.
Note
a proper numpy ufunc is required to support broadcasting, type casting and more. The function returned here, may not qualify for numpy’s definition of a ufunc. That why we use the term ufunc-like.
References
[1] http://docs.scipy.org/doc/numpy/reference/ufuncs.html
Examples
>>> from sympy.utilities.autowrap import ufuncify
>>> from sympy.abc import x, y, z
>>> f = ufuncify([x, y], y + x**2)
>>> f([1, 2, 3], 2)
[2. 5. 10.]