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)
def lambdify(g, l, omega, t, theta, v_0, z_0):
    # this section is cached and not compiled
    _cst_4 = sin(theta)
    _cst_2 = 1/l
    _cst_2 = _cst_2*g
    _cst_0 = _cst_4**2
    _cst_1 = omega**2
    _cst_0 = _cst_0*_cst_1
    _cst_2 = _cst_0 + _cst_2
    _cst_0 = sqrt(_cst_2)
    _cst_2 = _cst_2**0.5
    _cst_1 = 1/_cst_2
    _cst_3 = I*omega
    _cst_4 = _cst_3*_cst_4
    # this section is not cached and compiled in C
    _0 = _cst_0*t
    _1 = sin(_0)
    _2 = _1*_cst_1
    _0 = cos(_0)
    _3 = t*theta
    _4 = sin(_3)
    _4 = -_4*_cst_3
    _4 = exp(_4)
    _5 = _2*v_0
    _2 = _2*_cst_4
    _2 = _0 + _2
    _6 = _2*z_0
    _6 = _5 + _6
    _6 = _4*_6
    _5 = _0*v_0
    _0 = _0*_cst_4
    _1 = -_1*_cst_2
    _0 = _0 + _1
    _7 = _0*z_0
    _7 = _5 + _7
    _7 = _4*_7
    _0 = cos(_3)
    _5 = -_0*_6*_cst_3*theta
    _7 = _5 + _7
    return [_6, _7]
>>>
>>> # 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.