SymPy

Julia interface to SymPy via PyCall


License
MIT

Documentation

SymPy   SymPy

Linux: Build Status   Windows: Build Status

SymPy Package to bring Python's Sympy functionality into Julia via PyCall

The SymPy package (http://sympy.org/) is a Python library for symbolic mathematics.

With the excellent PyCall package of julia, one has access to the many features of SymPy from a julia session.

This SymPy package provides a light interface for some of the features of SymPy that makes working with SymPy objects a bit easier.

The tutorial provides an overview. It is viewable as an IJulia notebook here.

Installation

To use this package, both Python and its SymPy library must be installed on your system. If PyCall is installed using Conda (which is the default if no system python is found), then the underlying SymPy library will be installed via Conda when the package is first loaded. Otherwise, installing both Python and SymPy (which also requires mpmath) can be done by other means. The Anaconda distribution is suggested, as it provides a single installation of Python that includes SymPy and many other scientifice libraries that can be profitably accessed within Julia via PyCall. (Otherwise, install Python then download the sympy library from https://github.com/sympy/sympy/releases and install.)

The PyCall interface to SymPy

The only point to this package is that using PyCall to access SymPy is somewhat cumbersome. The following is how one would define a symbolic value x, take its sine, then evaluate at pi, say:

using PyCall            
@pyimport sympy
x = sympy.Symbol("x")
y = sympy.sin(x)
y[:subs](x, sympy.pi) |> float

The Symbol and sin function of SymPy are found within the imported sympy object. They may be referenced with Python's dot notation. However, the subs method of the y object is accessed differently, using indexing notation with a symbol. The call above substitutes a value of sympy.pi for x. This leaves the object as a PyObject storing a number which can be brought back into julia through conversion, in this case with the float function.

The above isn't so awkward, but even more cumbersome is the similarly simple task of finding sin(pi*x). As this multiplication is done at the python level and is not a method of sympy or the x object, we need to evaluate python code. Here is one solution:

x = sympy.Symbol("x")
y = pyeval("k*x", k=sympy.pi, x=x)     
z = sympy.sin(y)        
z[:subs](x, 1) |> float

This gets replaced by a more julian syntax:

using SymPy                    
x = symbols("x")               # or   @syms x, Sym("x"), or  Sym(:x)
y = sin(pi*x)
y(1)                           # Does subs(y, x, 1). Use y(x=>1) to be specific as to which symbol to substitute

The object x we create is of type Sym, a simple proxy for the underlying PyObject. We then overload the familiar math functions so that working with symbolic expressions can use natural julia idioms. The final result is here is a symbolic value of 0, which prints as 0 and not PyObject 0. To convert it into a numeric value within Julia, the N function may be used, which acts like the float call, only attempts to preserve the variable type.

However, for some tasks the PyCall interface is still needed, as only a portion of the SymPy interface is exposed. To call an underlying SymPy method, the getindex method is overloaded for symbol indices so that ex[:meth_name](...) dispatches to either to SymPy's ex.meth_name(...) or meth_name(ex, ...), as possible. Any Sym objects are projected down onto the underlying PyObject for use within PyCall. Otherwise, to dig the PyObject out of a Sym object, you access its property x, as in y.x. To find a SymPy method, a call like sympy.meth_name(...) is possible, e.g. sympy.harmonic(10). Any Sym-type arguments must be projected into PyObjects or an error will be thrown.

Notes

Some aspects of SymPy require more modern versions of sympy to be installed. For example, the matrix functions rely on features of sympy that are not exposed in the sympy installed with Ubuntu LTS 12.04.

In that particular instance, calls such as

x = symbols("x")
a = [x 1; 1 x]
det(a)

Can be replaced with

a[:det]()

Similarly for :trace, :eigenvects, ... . Note these are sympy methods, not Julia methods that have been ported. (Hence, :eigenvects and not eigvecs.)

TODO

  • Reintegrate the Plots package through RecipesBase