Julia interface to SymPy via PyCall
SymPyfunctionality into
Juliavia
PyCall
SymPy (
http://sympy.org/) is a Python library for symbolic mathematics.
With the excellent
PyCallpackage of
julia, one has access to the many features of the SymPy library from within a
Juliasession.
This
SymPypackage provides a light interface for the features of the SymPy library that makes working with SymPy objects a bit easier.
The documentation inludes an introduction document and a version of the SymPy tutorial translated from the Python syntax into Julia.
To use this package, both Python and its SymPy library must be installed on your system. If
PyCallis installed using
Conda(which is the default if no system
pythonis found), then the underlying SymPy library will be installed via
Condawhen the package is first loaded. Otherwise, installing both Python and the SymPy library (which also requires mpmath) can be done by other means. In this case, the
Anacondadistribution is suggested, as it provides a single installation of Python that includes SymPy and many other scientific libraries that can be profitably accessed within
Juliavia
PyCall. (Otherwise, install Python then download the SymPy library from https://github.com/sympy/sympy/releases and install.)
To upgrade the underlying
sympylibrary, which has new releases at a rate similar to
Julia, when installed with
Conda, the following commands are available:
using Pkg Pkg.add("Conda") # if needed using Conda Conda.update()
PyCallinterface to
SymPy
The only point to this package is that using
PyCallto access SymPy is somewhat cumbersome. The following is how one would define a symbolic value
x, take its sine, then evaluate the symboic expression for
xequal
pi, say:
using PyCall sympy = pyimport("sympy") # x = sympy.Symbol("x") # PyObject x y = sympy.sin(x) # PyObject sin(x) z = y.subs(x, sympy.pi) # PyObject 0 convert(Float64, z) # 0.0
The
sympyobject imported on the second line provides the access to much of SymPy's functionality, allowing access to functions (
sympy.sin), properties, modules (
sympy), and classes (
sympy.Symbol,
sympy.Pi). The
Symboland
sinoperations are found within the imported
sympymodule and, as seen, are referenced with
Python's dot call syntax, as implemented in
PyCallthrough a specialized
getpropertymethod.
SymPy's functionality is also found through methods bound to an object of a certain class. The
subsmethod of the
yobject is an example. Such methods are also accessed with Python's dot-call syntax. The call above substitutes a value of
sympy.pifor the symbolic variable
x. This leaves the object as a
PyObjectstoring a number which can be brought back into
juliathrough conversion, in this case through an explicit
convertcall.
Alternatively,
PyCallnow has a
*method, so the above could also be done with:
x = sympy.Symbol("x") y = sympy.pi * x z = sympy.sin(y) convert(Float64, z.subs(x, 1))
With the
SymPypackage this gets replaced by a more
julian syntax:
using SymPy x = symbols("x") # or @syms x y = sin(pi*x) y(1) # Does y.subs(x, 1). Use y(x=>1) to be specific as to which symbol to substitute
The object
xwe create is of type
Sym, a simple proxy for the underlying
PyObject. The package overloads the familiar math functions so that working with symbolic expressions can use natural
juliaidioms. The final result here is a symbolic value of
0, which prints as
0and not
PyObject 0. To convert it into a numeric value within
Julia, the
Nfunction may be used, which acts like the float conversion, only there is an attempt to preserve the variable type.
(There is a subtlety, the value of
pihere (an
Irrationalin
Julia) is converted to the symbolic
PI, but in general won't be if the math constant is coerced to a floating point value before it encounters a symbolic object. It is better to just use the symbolic value
PI, an alias for
sympy.piused above.)
SymPy has a mix of function calls (as in
sin(x)) and method calls (as in
y.subs(x,1)). The function calls are from objects in the base
sympymodule. When the
SymPypackage is loaded, in addition to specialized methods for many generic
Juliafunctions, such as
sin, a priviledged set of the function calls in
sympyare imported as generic functions narrowed on their first argument being a symbolic object, as constructed by
@syms,
Sym, or
symbols. (Calling
import_from(sympy)will import all the function calls.)
The basic usage follows these points:
generic methods from
Juliaand imported functions in the
sympynamespace are called through
fn(object)
SymPy methods are called through Python's dot-call syntax:
object.fn(...)
Contructors, like
sympy.Symbol, and other non-function calls from
sympyare qualified with
sympy.Constructor(...). Such qualified calls are also useful when the first argument is not symbolic.
So, these three calls are different,
sin(1), sin(Sym(1)), sympy.sin(1)
The first involves no symbolic values. The second and third are related and return a symbolic value for
sin(1). The second dispatches on the symbolic argument
Sym(1), the third has no dispatch, but refers to a SymPy function from the
sympyobject. Its argument,
1, is converted by
PyCallinto a Python object for the function to process.
In the initial example, slightly rewritten, we could have issued:
x = symbols("x") y = sin(pi*x) y.subs(x, 1)
The first line calls a provided alias for
sympy.symbolswhich is defined to allow a string (or a symbol) as an argument. The second, dispatches to
sympy.sin, as
pi*xis symbolic--
xis, and multiplication promotes to a symbolic value. The third line uses the dot-call syntax of
PyCallto call the
subsmethod of the symbolic
yobject.
Not illustrated above, but classes and other objects from SymPy are not brought in by default, and can be accessed using qualification, as in
sympy.Function(used, as is
@syms, to define symbolic functions).