cutcutcodec.core.compilation.sympy_to_torch.lambdify

Convert a sympy expression into a torch function.

It is the main entry point. The preprocessing is delegated to the cutcutcodec.core.compilation.sympy_to_torch.preprocess module. The compilation is delegated to the cutcutcodec.core.compilation.sympy_to_torch.printer module.

Classes

Lambdify(*args, **kwargs)

Convert a sympy expression into an evaluable torch function.

Details

class cutcutcodec.core.compilation.sympy_to_torch.lambdify.Lambdify(*args, **kwargs)[source]

Convert a sympy expression into an evaluable torch function.

Attributes

argslist[str]

The ordered names of the input arguments of this function (readonly).

Examples

>>> from sympy import I, cos, exp, im, re, sqrt, sin, symbols
>>> from torch import linspace, tensor
>>> from cutcutcodec.core.compilation.sympy_to_torch.lambdify import Lambdify
>>>
>>> # case of Faucault pendulum
>>> # angular earth speed, latitude, gravity, pendulum length, time
>>> omega, theta, g, l, t = symbols("omega theta g l t", real=True)
>>> z0, v0 = symbols("z_0 v_0", complex=True)  # initial position and speed
>>> w0 = sqrt(sqrt(g/l)**2 + omega**2*sin(theta)**2)
>>> z = exp(-I*omega*sin(theta*t)) * (
...     z0*(cos(w0*t) + I*(omega*sin(theta)/w0)*sin(w0*t)) + (v0/w0)*sin(w0*t)
... )
>>> func = Lambdify(
...     [z, z.diff(t)], cst_args={omega, theta, g, l}, shapes={(omega, theta, g, l), (z0, v0)}
... )
>>> print(func)
\begin{aligned}
\textbf{def}~f\left(g, l, omega, t, theta, v_0, z_0\right): \\
\quad x_{cst_3} \leftarrow \sin{\left(\theta \right)} \\
\quad x_{cst_0} \leftarrow \frac{1}{l} \\
\quad x_{cst_0} \leftarrow g x_{cst_0} \\
\quad x_{cst_1} \leftarrow x_{cst_3}^{2} \\
\quad x_{cst_2} \leftarrow \omega^{2} \\
\quad x_{cst_1} \leftarrow x_{cst_1} x_{cst_2} \\
\quad x_{cst_0} \leftarrow x_{cst_0} + x_{cst_1} \\
\quad x_{cst_0} \leftarrow x_{cst_0}^{0.5} \\
\quad x_{cst_1} \leftarrow \frac{1}{x_{cst_0}} \\
\quad x_{cst_2} \leftarrow i \omega \\
\quad x_{cst_3} \leftarrow x_{cst_2} x_{cst_3} \\
\quad x_0 \leftarrow t x_{cst_0} \\
\quad x_1 \leftarrow \sin{\left(x_{0} \right)} \\
\quad x_2 \leftarrow x_{1} x_{cst_1} \\
\quad x_0 \leftarrow \cos{\left(x_{0} \right)} \\
\quad x_3 \leftarrow t \theta \\
\quad x_4 \leftarrow \sin{\left(x_{3} \right)} \\
\quad x_4 \leftarrow - x_{4} x_{cst_2} \\
\quad x_4 \leftarrow e^{x_{4}} \\
\quad x_5 \leftarrow v_{0} x_{2} \\
\quad x_2 \leftarrow x_{2} x_{cst_3} \\
\quad x_2 \leftarrow x_{0} + x_{2} \\
\quad x_6 \leftarrow x_{2} z_{0} \\
\quad x_6 \leftarrow x_{5} + x_{6} \\
\quad x_6 \leftarrow x_{4} x_{6} \\
\quad x_5 \leftarrow v_{0} x_{0} \\
\quad x_0 \leftarrow x_{0} x_{cst_3} \\
\quad x_1 \leftarrow - x_{1} x_{cst_0} \\
\quad x_0 \leftarrow x_{0} + x_{1} \\
\quad x_7 \leftarrow x_{0} z_{0} \\
\quad x_7 \leftarrow x_{5} + x_{7} \\
\quad x_7 \leftarrow x_{4} x_{7} \\
\quad x_0 \leftarrow \cos{\left(x_{3} \right)} \\
\quad x_5 \leftarrow - \theta x_{0} x_{6} x_{cst_2} \\
\quad x_7 \leftarrow x_{5} + x_{7} \\
\quad \textbf{return}~\left[ x_{6}, \  x_{7}\right]
\end{aligned}
>>>
>>> # parameters of the pantheon pendulum in Paris
>>> position, speed = func(
...     linspace(0, (23*3600+56*60+4)/4, 1_000_000),  # a tour in 23h 56min 4s
...     g=tensor(9.81), l=tensor(67.0), omega=tensor(7.292115e-5), theta=tensor(0.8524362),
...     v_0=tensor(1+0j), z_0=tensor(0j),
... )
>>> print(position)
tensor([ 0.0000+0.0000e+00j,  0.0215-2.8842e-08j,  0.0431-1.1534e-07j,
         ..., -2.1698+4.9477e-05j, -2.1583+4.6581e-05j,
        -2.1453+4.3373e-05j])
>>> print(speed)
tensor([1.0000+0.0000e+00j, 1.0000-2.6776e-06j, 0.9999-5.3531e-06j,
         ..., 0.5574-1.4082e-04j, 0.5639-1.4032e-04j,
        0.5711-1.3967e-04j])
>>>

Initialise and create the class.

Parameters

exprsympy.core.basic.Basic

The sympy expression of the function.

cst_argstyping.Iterable[sympy.core.symbol.Symbol], optional

Arguments that change infrequently enough to be cached. The subexpressions computed from this parameters will be cached as well. If the parameters change frequently, don’t specify them in cst_args, This will slow down the function.

shapesset[frozenset[sympy.core.symbol.Symbol]], optional

If some parameters have the same shape, it is possible to give this information in order to find a more optimal solution for limited the allocations. It variable represents the set of all tensor subsets with the same shapes. For example, {frozenset({a, b, c}), frozenset({x, y})} means that a, b, and c are the same shape, and x and y as well.

compileboolean, default=True

The default behavior is to translate the expression into C, compile it with gcc, import the compiled version and then use this function. If any of these steps fail, the calculation is performed dynamically via pytorch only. If False, the function is evaluated dynamically only. No compilation is performed. It’s faster to instantiate the first time but it’s generally slower to evaluate.

safeboolean or set[sympy.core.symbol.Symbol], default=True

If True, the default behavior, then the tensors provided as input are not modified. This helps avoid unpleasant surprises but it is slower in certain cases. If False, no preventive copy is made. It is the fastest but the tensors provided as input can be modified in place. It is possible to be more precise by selecting only the variables to preserve, in this case, provide all the variables to be preserved.