| name | sympy-symbolic-math |
| description | Symbolic math in Python: exact algebra, calculus (derivatives, integrals, limits), equation solving, symbolic matrices, ODEs, code gen (lambdify, C/Fortran). Use for exact symbolic results. For numerical use numpy/scipy; for stats use statsmodels. |
| license | BSD-3-Clause |
SymPy — Symbolic Mathematics
Overview
SymPy is a Python library for symbolic mathematics that performs exact computation using mathematical symbols rather than numerical approximations. It covers algebra, calculus, equation solving, linear algebra, physics, and code generation — all within pure Python with no external dependencies.
When to Use
- Solving equations symbolically (algebraic, systems, differential equations)
- Performing calculus operations (derivatives, integrals, limits, series expansions)
- Simplifying and manipulating algebraic expressions
- Working with matrices symbolically (eigenvalues, determinants, decompositions)
- Converting symbolic expressions to fast numerical functions (lambdify → NumPy)
- Generating code from math expressions (C, Fortran, LaTeX)
- Needing exact results (e.g.,
sqrt(2) not 1.414...)
- For numerical computing (array operations, linear algebra on data), use numpy/scipy
- For statistical modeling (regression, hypothesis testing), use statsmodels
Prerequisites
pip install sympy
pip install numpy matplotlib
SymPy is pure Python — no compiled dependencies, installs everywhere.
Quick Start
from sympy import symbols, solve, diff, integrate, sqrt, pi
x = symbols('x')
print(solve(x**2 - 5*x + 6, x))
print(diff(x**3 + 2*x, x))
print(integrate(x**2, (x, 0, 1)))
print(sqrt(8))
print(pi.evalf(30))
Core API
1. Symbols and Expressions
Create symbolic variables and manipulate expressions.
from sympy import symbols, Symbol, Rational, S, oo, pi, E, I
from sympy import simplify, expand, factor, collect, cancel, trigsimp
x, y, z = symbols('x y z')
n = symbols('n', integer=True)
t = symbols('t', positive=True, real=True)
from sympy import sqrt
print(sqrt(t**2))
expr = Rational(1, 3) * x + S(1)/7
print(expr)
print(simplify(x**2 + 2*x + 1))
print(expand((x + 1)**3))
print(factor(x**3 - x))
print(collect(x*y + x - 3 + 2*x**2 - z*x**2, x))
2. Calculus
Derivatives, integrals, limits, and series.
from sympy import symbols, diff, integrate, limit, series, oo, sin, cos, exp, log
x = symbols('x')
print(diff(sin(x**2), x))
print(diff(x**4, x, 3))
x, y = symbols('x y')
f = x**2 * y**3
print(diff(f, x, y))
x = symbols('x')
print(integrate(x**2, x))
print(integrate(exp(-x**2), (x, -oo, oo)))
print(integrate(x * exp(-x), (x, 0, oo)))
print(limit(sin(x)/x, x, 0))
print(limit((1 + 1/x)**x, x, oo))
print(series(exp(x), x, 0, 5))
3. Equation Solving
Algebraic, transcendental, and differential equations.
from sympy import symbols, solve, solveset, Eq, S, linsolve, nonlinsolve, Function, dsolve
x, y = symbols('x y')
print(solve(x**2 - 4, x))
print(solveset(x**2 - 4, x, S.Reals))
print(linsolve([x + y - 5, 2*x - y - 1], x, y))
print(nonlinsolve([x**2 + y - 4, x + y**2 - 4], x, y))
f = Function('f')
ode = f(x).diff(x, 2) + f(x)
print(dsolve(ode, f(x)))
from sympy import Derivative
ics = {f(0): 1, f(x).diff(x).subs(x, 0): 0}
print(dsolve(ode, f(x), ics=ics))
4. Matrices and Linear Algebra
Symbolic matrix operations.
from sympy import Matrix, eye, zeros, ones, diag, symbols
M = Matrix([[1, 2], [3, 4]])
print(f"Det: {M.det()}")
print(f"Inverse:\n{M**-1}")
a, b = symbols('a b')
M = Matrix([[a, b], [b, a]])
print(f"Eigenvalues: {M.eigenvals()}")
eigendata = M.eigenvects()
P, D = M.diagonalize()
print(f"M = P*D*P^-1")
A = Matrix([[1, 2], [3, 4]])
b = Matrix([5, 6])
x = A.solve(b)
print(f"Solution: {x.T}")
t = symbols('t')
M_t = Matrix([[t, t**2], [1, t]])
print(f"dM/dt:\n{M_t.diff(t)}")
5. Code Generation
Convert symbolic expressions to fast numerical functions or compiled code.
import numpy as np
from sympy import symbols, lambdify, sin, exp, ccode, fcode, latex
x, y = symbols('x y')
expr = sin(x) * exp(-x**2 / 2)
f = lambdify(x, expr, 'numpy')
x_vals = np.linspace(-5, 5, 1000)
y_vals = f(x_vals)
print(f"Shape: {y_vals.shape}, Max: {y_vals.max():.4f}")
expr2 = x**2 + y**2
f2 = lambdify((x, y), expr2, 'numpy')
print(f"f(3, 4) = {f2(3, 4)}")
print(ccode(expr))
print(fcode(expr))
print(latex(expr))
6. Physics Module
Classical mechanics, vector analysis, and units.
from sympy import symbols, cos, sin, Function
from sympy.physics.mechanics import dynamicsymbols, LagrangesMethod, Particle, Point, ReferenceFrame
from sympy.physics.vector import dot, cross
N = ReferenceFrame('N')
v1 = 3*N.x + 4*N.y + 0*N.z
v2 = 1*N.x + 0*N.y + 2*N.z
print(f"Dot: {dot(v1, v2)}")
print(f"Cross: {cross(v1, v2)}")
q = dynamicsymbols('q')
m, g, l = symbols('m g l', positive=True)
T = Rational(1, 2) * m * (l * q.diff())**2
V = m * g * l * (1 - cos(q))
L = T - V
print(f"Lagrangian: {L}")
Key Concepts
Exact vs Numerical Arithmetic
from sympy import Rational, S, sqrt, pi
expr_bad = 0.5 * x
expr_good = Rational(1, 2) * x
expr_good = S(1)/2 * x
expr_good = x / 2
print(sqrt(2).evalf())
print(pi.evalf(50))
Solver Selection Guide
| Solver | Use When | Returns |
|---|
solve(eq, x) | General purpose, legacy | List of solutions |
solveset(eq, x, domain) | Algebraic equations (preferred) | Set (may be infinite) |
linsolve(system, vars) | Linear systems | FiniteSet of tuples |
nonlinsolve(system, vars) | Nonlinear systems | FiniteSet of tuples |
dsolve(ode, f(x)) | Ordinary differential equations | Equality (Eq) |
nsolve(eq, x0) | Numerical root finding | Float approximation |
Common Simplification Functions
| Function | Does | Example |
|---|
simplify() | General simplification (slow, tries everything) | sin(x)**2 + cos(x)**2 → 1 |
expand() | Distribute multiplication | (x+1)**2 → x**2+2*x+1 |
factor() | Factor into irreducibles | x**2-1 → (x-1)*(x+1) |
collect() | Group by variable | Collect terms in x |
cancel() | Cancel common factors in fractions | (x**2-1)/(x-1) → x+1 |
trigsimp() | Simplify trig expressions | Faster than simplify for trig |
powsimp() | Simplify powers/exponentials | Combine x**a * x**b |
Common Workflows
Workflow: Symbolic-to-Numeric Pipeline
from sympy import symbols, diff, integrate, lambdify, sin, cos
import numpy as np
import matplotlib.pyplot as plt
x = symbols('x')
f_expr = sin(x) * cos(x)**2
f_prime = diff(f_expr, x)
F_expr = integrate(f_expr, x)
print(f"f(x) = {f_expr}")
print(f"f'(x) = {f_prime}")
print(f"F(x) = {F_expr}")
f_num = lambdify(x, f_expr, 'numpy')
f_prime_num = lambdify(x, f_prime, 'numpy')
F_num = lambdify(x, F_expr, 'numpy')
x_vals = np.linspace(0, 2*np.pi, 500)
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
axes[0].plot(x_vals, f_num(x_vals)); axes[0].set_title('f(x)')
axes[1].plot(x_vals, f_prime_num(x_vals)); axes[1].set_title("f'(x)")
axes[2].plot(x_vals, F_num(x_vals)); axes[2].set_title('F(x)')
plt.tight_layout()
plt.savefig('symbolic_pipeline.png', dpi=150)
print("Saved symbolic_pipeline.png")
Workflow: Solve and Verify
from sympy import symbols, solve, simplify, Eq, sqrt
x = symbols('x')
equation = x**3 - 6*x**2 + 11*x - 6
solutions = solve(equation, x)
print(f"Solutions: {solutions}")
for sol in solutions:
result = simplify(equation.subs(x, sol))
assert result == 0, f"Solution {sol} failed!"
print(f" x={sol}: f(x) = {result} ✓")
from sympy import factor
print(f"Factored: {factor(equation)}")
Workflow: ODE System Analysis
- Define the ODE using
Function and dsolve()
- Solve symbolically; apply initial conditions with
ics={} parameter
- Convert solution to numerical function with
lambdify()
- Plot the solution trajectory with matplotlib
Key Parameters
| Parameter | Function | Default | Options | Effect |
|---|
domain | solveset() | S.Complexes | S.Reals, S.Integers | Restrict solution domain |
force | simplify() | False | True/False | Aggressive simplification |
n | diff(expr, x, n) | 1 | 1–∞ | Order of derivative |
| Precision | evalf(n) | 15 | 1–1000+ | Digits of numerical precision |
| Backend | lambdify() | "math" | "numpy", "scipy", "mpmath" | Numerical backend for evaluation |
rational | nsimplify() | True | True/False | Find exact rational approximation |
Best Practices
-
Always use Rational() or S() for fractions: 0.5 * x introduces floats that break exact computation. Use Rational(1, 2) * x or S(1)/2 * x.
-
Add assumptions to symbols: symbols('x', positive=True) enables simplifications like sqrt(x**2) → x. Without assumptions, SymPy must handle the general complex case.
-
Use lambdify for numerical evaluation, not subs().evalf(): subs/evalf in a loop is 100-1000x slower than a single lambdify call.
-
Anti-pattern — using simplify() as default: simplify() is slow because it tries many strategies. Use specific functions (factor, expand, trigsimp) when you know the desired form.
-
Prefer solveset over solve for algebraic equations: solveset returns proper mathematical sets and handles edge cases better. solve is legacy but still useful for general cases.
-
Anti-pattern — solving symbolically when numerical is sufficient: For equations with no closed-form solution, use nsolve(eq, x0) for numerical root finding instead of waiting for solve to fail.
-
Use init_printing() in Jupyter for readable output: from sympy import init_printing; init_printing() enables LaTeX rendering in notebooks.
Common Recipes
Recipe: Generate LaTeX Documentation
from sympy import symbols, Integral, Eq, latex, sqrt, pi
x = symbols('x')
integral = Integral(x**2 * sqrt(1 - x**2), (x, 0, 1))
result = integral.doit()
print(f"$$ {latex(integral)} = {latex(result)} $$")
Recipe: Parametric ODE Solution
from sympy import symbols, Function, dsolve, Eq, exp, lambdify
import numpy as np
x = symbols('x')
k, A = symbols('k A', positive=True)
f = Function('f')
ode = Eq(f(x).diff(x), -k * f(x))
solution = dsolve(ode, f(x), ics={f(0): A})
print(f"Solution: {solution}")
f_num = lambdify((x, k, A), solution.rhs, 'numpy')
x_vals = np.linspace(0, 5, 100)
y_vals = f_num(x_vals, k=0.5, A=10)
print(f"y(5) = {y_vals[-1]:.4f}")
Recipe: Symbolic Matrix Decomposition
from sympy import Matrix, symbols, pprint
a, b, c, d = symbols('a b c d')
M = Matrix([[a, b], [c, d]])
lam = symbols('lambda')
char_poly = M.charpoly(lam)
print(f"Characteristic polynomial: {char_poly.as_expr()}")
eigenvals = M.eigenvals()
print(f"Eigenvalues: {eigenvals}")
print(f"det(M) = {M.det()}")
print(f"tr(M) = {M.trace()}")
Troubleshooting
| Problem | Cause | Solution |
|---|
NameError: name 'x' is not defined | Symbol not created | Define with x = symbols('x') before use |
| Unexpected float results | Using 0.5 instead of Rational(1,2) | Use Rational() or S() for exact fractions |
simplify() very slow | Trying all strategies on complex expr | Use specific function: factor(), expand(), trigsimp() |
solve() returns empty list | No closed-form solution exists | Use nsolve(eq, x0) for numerical approximation |
sqrt(x**2) returns sqrt(x**2) not x | No assumption on x | Define x = symbols('x', positive=True) |
lambdify wrong results | Expression has SymPy-specific functions | Specify backend: lambdify(x, expr, 'numpy') or 'scipy' |
NotImplementedError in dsolve | ODE type not supported | Try numerical ODE solver (scipy odeint) instead |
Related Skills
- matplotlib-scientific-plotting — plot symbolic results after lambdify conversion
- statsmodels-statistical-modeling — statistical inference; use when you need p-values, not exact algebra
- matlab-scientific-computing — MATLAB alternative for numerical (not symbolic) computing
References