Real derivations blow up in size and time; experts know where SymPy spends effort and how to avoid it.
SymPy is pure Python, and symbolic algorithms are often exponential in expression size, so performance work is mostly about keeping expressions small and avoiding unnecessary work.
- Avoid blanket simplify. It tries many algorithms. Use the targeted function you need, or
simplify(e, ratio=...)to bound effort. - cse.
cse(exprs)finds repeated subexpressions and returns(replacements, reduced). Large Jacobians often shrink dramatically, and generated code computes each piece once.lambdify(..., cse=True)applies it automatically. - Control evaluation. Constructing an
AddorMulsorts and combines arguments. Building huge sums term by term with+is quadratic;Add(*terms)builds one node at once.evaluate=False(orwith evaluate(False):) skips automatic evaluation entirely, useful for display or for staged construction. - Use the polys module.
Poly(e, x, domain="QQ")represents polynomials in dense or sparse form with fast arithmetic in a fixed domain.factor,gcdandcancelare built on it, so converting once and staying inPolyavoids repeated conversion. - Expand late, substitute numbers early where exactness is not needed, and use
xreplaceinstead ofsubsin tight loops. - The cache speeds up repeated construction but costs memory;
SYMPY_USE_CACHE=nodisables it.
When pure Python is the bottleneck, the C++ library SymEngine (symengine package) implements core expression manipulation much faster with a similar API.
Example
from sympy import symbols, cse, sin, cos, Matrix, Poly, Add, sqrt
x, y = symbols("x y")
r = sqrt(x**2 + y**2)
J = Matrix([x * sin(r), y * cos(r)]).jacobian([x, y])
repl, reduced = cse(J)
for name, sub in repl:
print(name, "=", sub)
# x0 = x**2
# x1 = y**2
# x2 = sqrt(x0 + x1)
# x3 = sin(x2) ... (each shared piece computed once)
print(len(repl) > 0) # True
p = Poly(x**3 - 2*x + 1, x, domain="QQ")
print(p.degree(), p.all_coeffs()) # 3 [1, 0, -2, 1]
print(Add(*[x**k for k in range(4)])) # x**3 + x**2 + x + 1
print(Add(x, x, evaluate=False)) # x + xRemember
- cse() removes repeated subexpressions before codegen or numerics
- Use targeted functions instead of simplify; build big sums with Add(*terms)
- Poly with a fixed domain is the fast path for polynomial algebra
Watch out for
- Accumulating thousands of terms with total = total + term, which re-sorts every time.