Introduction to the expression system#
As we saw in the previous section, heyoka.py needs to be able to represent the right-hand side of an ODE system in symbolic form in order to be able to compute its high-order derivatives via automatic differentiation. heyoka.py represents generic mathematical expressions via a simple abstract syntax tree (AST) in which the internal nodes are n-ary functions and the leaf nodes can be:
symbolic variables,
numerical constants,
Both constants and parameters can be used to represent mathematical constants, the difference being that the value of a constant is determined when the expression is created, whereas the value of a parameter is loaded from a user-supplied data array at a later stage. Additionally, it is possible to compute derivatives with respect to parameters.
The construction of the AST of an expression in heyoka.py can be accomplished via natural mathematical notation:
import heyoka as hy
# Define the symbolic variables x and y.
x, y = hy.make_vars("x", "y")
# Another way of creating a symbolic variable.
z = hy.expression("z")
# Create and print an expression.
print("The euclidean distance is: {}".format(hy.sqrt(x**2 + y**2)))
The euclidean distance is: (x**2.0000000000000000 + y**2.0000000000000000)**0.50000000000000000
Numerical constants#
Numerical constants can be created using any of the floating-point types supported by heyoka.py. For instance, on a typical Linux installation of heyoka.py on an x86 processor, one may write:
print("Double-precision 1.1: {}".format(hy.expression(1.1)))
import numpy as np
print("Single-precision 1.1: {}".format(hy.expression(np.float32("1.1"))))
print("Extended-precision 1.1: {}".format(hy.expression(np.longdouble("1.1"))))
print("Quadruple-precision 1.1: {}".format(hy.expression(hy.real128("1.1"))))
# NOTE: octuple precision has a
# 237-bit significand.
print("Octuple-precision 1.1: {}".format(hy.expression(hy.real("1.1", 237))))
Double-precision 1.1: 1.1000000000000001
Single-precision 1.1: 1.10000002
Extended-precision 1.1: 1.10000000000000000002
Quadruple-precision 1.1: 1.10000000000000000000000000000000008
Octuple-precision 1.1: 1.100000000000000000000000000000000000000000000000000000000000000000000004
Note that, while single and double precision are always supported in heyoka.py (via the numpy.single
and float
types respectively), support for higher precision varies depending on the platform and on the specifics of the build configuration. Specifically, higher-precision support is achieved through the following datatypes:
the extended-precision
numpy.longdouble
type,the quadruple-precision
heyoka.real128
type,the multiprecision
heyoka.real
type.
The exact meaning of the longdouble
type varies depending on the platform:
on most x86 platforms,
longdouble
corresponds to 80-bit extended precision. The notable exception is MSVC on Windows, wherelongdouble
is an alias for double precision;on other platforms (e.g., Linux ARM 64),
longdouble
implements the IEEE quadruple-precision floating-point format;on OSX ARM 64 (AKA Apple silicon),
longdouble
is an alias for double precision;on certain PowerPC platforms,
longdouble
implements a double-length floating-point representation with 106 significant bits which is NOT supported by heyoka.py at this time.
When in doubt, you can use the numpy.finfo
class to inspect the properties of longdouble
on your setup.
The heyoka.real128
type implements the IEEE quadruple-precision floating-point format. It is currently available on Linux x86 and PowerPC platforms. Note that on those platforms where longdouble
is a quadruple-precision datatype, heyoka.real128
is NOT avaiable (as it would be redundant with longdouble
).
heyoka.real
implements arbitrary-precision computations, and it is supported on all platforms.
Mathematical functions#
In addition to the standard mathematical operators, heyoka.py’s expression system also supports several elementary and special functions, such as:
the square root,
exponentiation,
the basic trigonometric and hyperbolic functions, and their inverse counterparts,
the natural logarithm and exponential,
the standard logistic function (sigmoid),
the error function,
Kepler’s elliptic anomaly and several other anomalies commonly used in astrodynamics.
# An expression involving a few elementary functions.
hy.cos(x + 2.0 * y) * hy.sqrt(z) - hy.exp(x)
((cos((x + (2.0000000000000000 * y))) * z**0.50000000000000000) - exp(x))
It must be emphasised that heyoka.py’s expression system is not a full-fledged computer algebra system. In particular, its simplification capabilities are essentially non-existent. Because heyoka.py’s performance is sensitive to the complexity of the ODEs, in order to achieve optimal performance it is important to ensure that the mathematical expressions supplied to heyoka.py are simplified as much as possible.
Starting form version 0.10, heyoka.py’s expressions can be converted to/from SymPy expressions. It is thus possible to use SymPy for the automatic simplifcation of heyoka.py’s expressions, and, more generally, to symbolically manipulate heyoka.py’s expressions using the wide array of SymPy’s capabilities. See the SymPy interoperability tutorial for a detailed example.