Autowrap Module#

The autowrap module works very well in tandem with the Indexed classes of the Tensor. 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
Eq(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.]

Implementation details#

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.

API Reference#

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, e.g.,

>>> from sympy.abc import x,y
>>> expr = (x - y)**25
>>> flat = expr.expand()
>>> binary_callable = autowrap(flat)
>>> binary_callable(2, 3)
-1.0

Although a SymPy user might primarily be interested in working with mathematical expressions and not in the details of wrapping tools needed to evaluate such expressions efficiently in numerical form, the user cannot do so without some understanding of the limits in the target language. For example, the expanded expression contains large coefficients which result in loss of precision when computing the expression:

>>> binary_callable(3, 2)
0.0
>>> binary_callable(4, 5), binary_callable(5, 4)
(-22925376.0, 25165824.0)

Wrapping the unexpanded expression gives the expected behavior:

>>> e = autowrap(expr)
>>> e(4, 5), e(5, 4)
(-1.0, 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.e-110

When is this useful?

  1. 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.

  2. For really long expressions that will be called repeatedly, the compiled binary should be significantly faster than SymPy’s .evalf()

  3. 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.

  4. To create customized ufuncs for use with numpy arrays. See ufuncify.

When is this module NOT the best approach?

  1. 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/”.

  2. If the array computation can be handled easily by numpy, and you do not need the binaries for another project.

class sympy.utilities.autowrap.CodeWrapper(generator, filepath=None, flags=[], verbose=False)[source]#

Base Class for code wrappers

class sympy.utilities.autowrap.CythonCodeWrapper(*args, **kwargs)[source]#

Wrapper that uses Cython

dump_pyx(routines, f, prefix)[source]#

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

routines

List of Routine instances

f

File-like object to write the file to

prefix

The filename prefix, used to refer to the proper header file. Only the basename of the prefix is used.

class sympy.utilities.autowrap.DummyWrapper(generator, filepath=None, flags=[], verbose=False)[source]#

Class used for testing independent of backends

class sympy.utilities.autowrap.F2PyCodeWrapper(*args, **kwargs)[source]#

Wrapper that uses f2py

class sympy.utilities.autowrap.UfuncifyCodeWrapper(*args, **kwargs)[source]#

Wrapper for Ufuncify

dump_c(routines, f, prefix, funcname=None)[source]#

Write a C file with Python wrappers

This file contains all the definitions of the routines in c code.

Arguments

routines

List of Routine instances

f

File-like object to write the file to

prefix

The filename prefix, used to name the imported module.

funcname

Name of the main function to be returned.

sympy.utilities.autowrap.autowrap(expr, language=None, backend='f2py', tempdir=None, args=None, flags=None, verbose=False, helpers=None, code_gen=None, **kwargs)[source]#

Generates Python callable binaries based on the math expression.

Parameters:

expr

The SymPy expression that should be wrapped as a binary routine.

language : string, optional

If supplied, (options: ‘C’ or ‘F95’), specifies the language of the generated code. If None [default], the language is inferred based upon the specified backend.

backend : string, optional

Backend used to wrap the generated code. Either ‘f2py’ [default], or ‘cython’.

tempdir : string, optional

Path to directory for temporary files. If this argument is supplied, the generated code and the wrapper input files are left intact in the specified path.

args : iterable, optional

An ordered iterable of symbols. Specifies the argument sequence for the function.

flags : iterable, optional

Additional option flags that will be passed to the backend.

verbose : bool, optional

If True, autowrap will not mute the command line backends. This can be helpful for debugging.

helpers : 3-tuple or iterable of 3-tuples, optional

Used to define auxiliary expressions needed for the main expr. If the main expression needs to call a specialized function it should be passed in via helpers. Autowrap will then make sure that the compiled main expression can link to the helper routine. Items should be 3-tuples with (<function_name>, <sympy_expression>, <argument_tuple>). It is mandatory to supply an argument sequence to helper routines.

code_gen : CodeGen instance

An instance of a CodeGen subclass. Overrides language.

include_dirs : [string]

A list of directories to search for C/C++ header files (in Unix form for portability).

library_dirs : [string]

A list of directories to search for C/C++ libraries at link time.

libraries : [string]

A list of library names (not filenames or paths) to link against.

extra_compile_args : [string]

Any extra platform- and compiler-specific information to use when compiling the source files in ‘sources’. For platforms and compilers where “command line” makes sense, this is typically a list of command-line arguments, but for other platforms it could be anything.

extra_link_args : [string]

Any extra platform- and compiler-specific information to use when linking object files together to create the extension (or to create a new static Python interpreter). Similar interpretation as for ‘extra_compile_args’.

Examples

>>> 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
sympy.utilities.autowrap.binary_function(symfunc, expr, **kwargs)[source]#

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().

Parameters:

symfunc : SymPy Function

The function to bind the callable to.

expr : SymPy Expression

The expression used to generate the function.

kwargs : dict

Any kwargs accepted by autowrap.

Examples

>>> from sympy.abc import x, y
>>> from sympy.utilities.autowrap import binary_function
>>> expr = ((x - y)**(25)).expand()
>>> f = binary_function('f', expr)
>>> type(f)
<class 'sympy.core.function.UndefinedFunction'>
>>> 2*f(x, y)
2*f(x, y)
>>> f(x, y).evalf(2, subs={x: 1, y: 2})
-1.0
sympy.utilities.autowrap.ufuncify(args, expr, language=None, backend='numpy', tempdir=None, flags=None, verbose=False, helpers=None, **kwargs)[source]#

Generates a binary function that supports broadcasting on numpy arrays.

Parameters:

args : iterable

Either a Symbol or an iterable of symbols. Specifies the argument sequence for the function.

expr

A SymPy expression that defines the element wise operation.

language : string, optional

If supplied, (options: ‘C’ or ‘F95’), specifies the language of the generated code. If None [default], the language is inferred based upon the specified backend.

backend : string, optional

Backend used to wrap the generated code. Either ‘numpy’ [default], ‘cython’, or ‘f2py’.

tempdir : string, optional

Path to directory for temporary files. If this argument is supplied, the generated code and the wrapper input files are left intact in the specified path.

flags : iterable, optional

Additional option flags that will be passed to the backend.

verbose : bool, optional

If True, autowrap will not mute the command line backends. This can be helpful for debugging.

helpers : iterable, optional

Used to define auxiliary expressions needed for the main expr. If the main expression needs to call a specialized function it should be put in the helpers iterable. Autowrap will then make sure that the compiled main expression can link to the helper routine. Items should be tuples with (<funtion_name>, <sympy_expression>, <arguments>). It is mandatory to supply an argument sequence to helper routines.

kwargs : dict

These kwargs will be passed to autowrap if the \(f2py\) or \(cython\) backend is used and ignored if the \(numpy\) backend is used.

Notes

The default backend (‘numpy’) will create actual instances of numpy.ufunc. These support ndimensional broadcasting, and implicit type conversion. Use of the other backends will result in a “ufunc-like” function, which requires equal length 1-dimensional arrays for all arguments, and will not perform any type conversions.

Examples

>>> from sympy.utilities.autowrap import ufuncify
>>> from sympy.abc import x, y
>>> import numpy as np
>>> f = ufuncify((x, y), y + x**2)
>>> type(f)
<class 'numpy.ufunc'>
>>> f([1, 2, 3], 2)
array([  3.,   6.,  11.])
>>> f(np.arange(5), 3)
array([  3.,   4.,   7.,  12.,  19.])

For the ‘f2py’ and ‘cython’ backends, inputs are required to be equal length 1-dimensional arrays. The ‘f2py’ backend will perform type conversion, but the Cython backend will error if the inputs are not of the expected type.

>>> f_fortran = ufuncify((x, y), y + x**2, backend='f2py')
>>> f_fortran(1, 2)
array([ 3.])
>>> f_fortran(np.array([1, 2, 3]), np.array([1.0, 2.0, 3.0]))
array([  2.,   6.,  12.])
>>> f_cython = ufuncify((x, y), y + x**2, backend='Cython')
>>> f_cython(1, 2)  
Traceback (most recent call last):
  ...
TypeError: Argument '_x' has incorrect type (expected numpy.ndarray, got int)
>>> f_cython(np.array([1.0]), np.array([2.0]))
array([ 3.])

References