The usual workflow is derive symbolically, then evaluate numerically; lambdify is the hand-off.
lambdify(args, expr, modules) generates the source code of a Python function that computes expr, with symbolic functions mapped to a numeric library. When NumPy is installed the default backend uses it (plus SciPy if available), so the resulting function accepts arrays and evaluates element-wise at NumPy speed:
f = lambdify(x, sin(x) * x**2)
f(np.linspace(0, 1, 1_000_000))That is orders of magnitude faster than looping with subs and evalf.
Details worth knowing:
argsis a symbol, a list of symbols, or nested lists; the generated function takes them as positional arguments in that order.modulesselects the backend:"numpy","math","mpmath","scipy","jax"and others, or a dict mapping names to your own implementations.- An expression can return several outputs: pass a list or a
Matrix. inspect.getsource(f)shows exactly what was generated, which is invaluable when debugging.lambdify(..., cse=True)factors out common subexpressions, which speeds up large expressions.
Precision changes too. Inside the generated function, numbers are ordinary floats, so Rational(1, 3) becomes 1/3 as a float. If you need high precision, use the "mpmath" backend.
Example
import inspect
import numpy as np
from sympy import symbols, lambdify, sin, diff
x, y = symbols("x y")
expr = sin(x) * x**2
f = lambdify(x, expr, "numpy")
df = lambdify(x, diff(expr, x), "numpy")
xs = np.array([0.0, 1.0, 2.0])
print(f(xs)) # [0. 0.84147098 3.63718971]
print(df(1.0)) # 2.2232442754839328
g = lambdify([x, y], [x + y, x * y])
print(g(2, 3)) # [5, 6]
print(inspect.getsource(f))
# def _lambdifygenerated(x):
# return x**2*sin(x)Remember
- lambdify(args, expr) builds a fast NumPy-backed function
- Use it instead of subs/evalf loops for numeric evaluation
- inspect.getsource shows the generated code; cse=True speeds up large expressions
Watch out for
- Using the math backend and then passing NumPy arrays to the function.