mrkeyoor.com_
Thu 06 Aug 02:47 UTC
PyPIDataupdated 05 Aug 2026

sympy

SymPy is a computer algebra system written in pure Python. Instead of computing with numbers, it computes with expressions: you declare symbols, build expressions out of them, and ask for the derivative, the integral, the limit, the factored form, or the exact solution. Answers come back as expressions, not floats, so 1/3 stays one third and sqrt(2) stays sqrt(2) until you ask for digits. It covers calculus, equation solving, linear algebra with exact entries, differential equations, series expansion, and number theory, and it can print any expression as LaTeX or turn it into a fast numeric function for NumPy with lambdify. The only dependency is mpmath, which handles arbitrary-precision floats.

Verdict

The default computer algebra system for Python, and the only one that installs with a single pip command and no compiler. Use it to derive and verify expressions, then lambdify them into NumPy for anything that runs in a loop, and keep a timeout around calls to solve and integrate on inputs you do not control.

API stability4/5Core names such as symbols, diff, integrate, Matrix, and lambdify have been stable for many years and deprecations run for several releases; the ongoing solve to solveset migration means two overlapping APIs coexist with different return types.
Docs5/5docs.sympy.org has a genuine tutorial, an explanation of the gotchas that trip beginners, and full API reference for every module, backed by doctested examples throughout the codebase.
Maintenance3/5master is pushed daily and the contributor base is large and NumFOCUS-backed, but the last release, 1.14.0, is from April 2025 and the tracker carries 4724 open issues and 1186 open PRs.
Ecosystem5/5Roughly 46M downloads a week, a dependency of PyTorch and much of the scientific Python stack, and the symbolic layer behind SymEngine, sympy-plot-backends, and codegen tools.

Use it if

  • You need a derivative, integral, limit, or series expansion done symbolically and correctly rather than approximated numerically
  • You want to derive a formula once and then evaluate it fast on arrays: lambdify compiles a SymPy expression into a plain Python function backed by NumPy
  • You need exact arithmetic, where Rational(1, 3) times 3 is exactly 1 and a matrix inverse comes back with fractions instead of floating point noise
  • You are generating LaTeX for a paper, or C, Fortran, or Python source, from expressions you built or solved programmatically
  • It is pure Python with one dependency, so it installs anywhere, including places where compiling a scientific stack is painful
Skip it if

Setup reality

pip install sympy pulls in mpmath and nothing else, and there is nothing to configure. The learning curve is entirely conceptual and it bites early. Python evaluates before SymPy sees anything, so 1/3 inside an expression is already 0.333 and x + 1/3 needs Rational(1, 3) or S(1)/3 to stay exact. Equality is structural, not mathematical: expr1 == expr2 compares trees, and the way to ask whether two expressions are equal is simplify(expr1 - expr2) == 0. Equations are built with Eq(lhs, rhs) because x == y is a Python bool. Assumptions change results, so sqrt(x**2) stays as written until you declare x positive, at which point it becomes x, and forgetting this makes simplify look broken. Finally, symbols are matched by name, so two separately created Symbol('x') objects are the same symbol, which is convenient until you rely on it accidentally.

Patterns

Create symbols and give them assumptionsdeclare-symbols

from sympy import symbols, sqrt

x, y = symbols('x y')
a = symbols('a', positive=True)
n = symbols('n', integer=True)

print(sqrt(x**2))  # sqrt(x**2), unchanged
print(sqrt(a**2))  # a

Assumptions are not decoration; they decide which simplifications are valid. Without positive=True, SymPy cannot know sqrt(x**2) is not abs(x), so it leaves the expression alone and simplify looks like it failed.

Rearrange an expressionsimplify-and-factor

from sympy import symbols, simplify, expand, factor, sin, cos

x = symbols('x')
print(simplify(sin(x)**2 + cos(x)**2))  # 1
print(expand((x + 1)**3))               # x**3 + 3*x**2 + 3*x + 1
print(factor(x**3 - 1))                 # (x - 1)*(x**2 + x + 1)

simplify is a heuristic search over many transformations and can be slow with no guarantee of the form you wanted. When you know the shape you need, call the specific function (expand, factor, cancel, trigsimp, radsimp) instead.

Take derivativesdifferentiate

from sympy import symbols, diff, sin, exp, Function

x, y = symbols('x y')
print(diff(sin(x) * exp(x), x))   # exp(x)*sin(x) + exp(x)*cos(x)
print(diff(x**4, x, 2))           # 12*x**2, second derivative
print(diff(x**2 * y**3, x, y))    # 6*x*y**2, mixed partial

f = Function('f')
print(diff(f(x)**2, x))           # 2*f(x)*Derivative(f(x), x)

An undefined Function stays symbolic and its derivative comes back as Derivative(...), which is exactly what you want when setting up a differential equation. expr.diff(x) is the same call as a method.

Compute indefinite and definite integralsintegrate

from sympy import symbols, integrate, exp, sin, oo, pi

x = symbols('x')
print(integrate(exp(x) * sin(x), x))
# exp(x)*sin(x)/2 - exp(x)*cos(x)/2

print(integrate(exp(-x**2), (x, -oo, oo)))  # sqrt(pi)

No constant of integration is added to indefinite results. If SymPy cannot find a closed form it returns an unevaluated Integral rather than raising, and hard integrands can run for a long time with no way to interrupt short of a signal.

Solve an equation for a variablesolve-equations

from sympy import symbols, Eq, solve, solveset, nsolve, S, cos

x = symbols('x')
print(solve(x**2 - 4, x))                          # [-2, 2]
print(solveset(Eq(x**2, 4), x, domain=S.Reals))    # {-2, 2}
print(solve([Eq(x + symbols('y'), 5),
             Eq(x - symbols('y'), 1)], [x, symbols('y')]))  # {x: 3, y: 2}
print(nsolve(cos(x) - x, x, 1))                    # 0.739085133215161

An expression with no Eq is assumed equal to zero. solve returns a list for one unknown and a dict for a system, so branch on the type; solveset always returns a set object and needs a domain to stay real. nsolve is the numeric fallback and needs a starting guess.

Work with exact matriceslinear-algebra

from sympy import Matrix, symbols, linsolve

M = Matrix([[1, 2], [3, 4]])
print(M.det())        # -2
print(M.inv())        # Matrix([[-2, 1], [3/2, -1/2]])
print(M.eigenvals())  # {5/2 - sqrt(33)/2: 1, 5/2 + sqrt(33)/2: 1}

x, y, z = symbols('x y z')
print(linsolve([x + y + z - 6, 2*x + y - 3, x - y + z - 2], (x, y, z)))
# {(1/2, 2, 7/2)}

Entries stay exact, so the inverse has fractions and eigenvalues have radicals instead of floats. This is also why a symbolic matrix inverse blows up quickly with size; past roughly 10x10 with symbols, use numpy or scipy.

Take limits and expand as a serieslimits-and-series

from sympy import symbols, limit, sin, cos, oo

x = symbols('x')
print(limit(sin(x) / x, x, 0))     # 1
print(limit(1 / x, x, 0, '+'))     # oo
print(limit(1 / x, x, 0, '-'))     # -oo
print(cos(x).series(x, 0, 6))
# 1 - x**2/2 + x**4/24 + O(x**6)

The default limit is from the right, so a two-sided limit that does not exist can still return a value; pass '+' or '-' explicitly when the sides differ. Call removeO() on a series to get a plain polynomial you can evaluate.

Keep numbers exact, then ask for digitsexact-numbers

from sympy import Rational, S, pi, N, sqrt

print(Rational(1, 3) + Rational(1, 6))  # 1/2
print(S(1) / 3)                          # 1/3
print(1 / 3)                             # 0.3333333333333333, plain Python

print(N(pi, 30))       # 3.14159265358979323846264338328
print(sqrt(2).evalf(50))

Python computes 1/3 before SymPy ever sees it, which is the single most common source of unexpected floats. Rational(1, 3), S(1)/3, or sympify('1/3') all keep it exact.

Substitute values into an expressionsubstitute-values

from sympy import symbols, sin, pi

x, y = symbols('x y')
expr = x**2 + sin(y)

print(expr.subs(x, 3))                    # sin(y) + 9
print(expr.subs({x: 3, y: pi / 2}))       # 10
print(expr.subs([(x, y), (y, 2)]))        # careful: sequential

subs with a list applies substitutions one after another, so the result of the first can be rewritten by the second. Pass a dict for simultaneous substitution, or subs(..., simultaneous=True).

Turn an expression into a fast numeric functionlambdify-for-numpy

import numpy as np
from sympy import symbols, sin, lambdify

x, y = symbols('x y')
expr = x**2 + sin(y)

f = lambdify((x, y), expr, 'numpy')
print(f(np.array([1.0, 2.0]), np.array([0.0, 0.0])))  # [1. 4.]

This is the bridge out of symbolic land and the fix for slow SymPy code: derive once, lambdify, then loop. lambdify builds the function with exec, so never pass it an expression parsed from untrusted input.

Solve an ODEsolve-differential-equation

from sympy import symbols, Function, Eq, dsolve, sin

x = symbols('x')
f = Function('f')

print(dsolve(Eq(f(x).diff(x, 2) + f(x), 0), f(x)))
# Eq(f(x), C1*sin(x) + C2*cos(x))

print(dsolve(Eq(f(x).diff(x) + f(x), sin(x)), f(x),
             ics={f(0): 1}))

Constants come back as C1, C2 and so on unless you pass ics with initial conditions. dsolve picks a solving method by classifying the equation; classify_ode(eq, f(x)) shows you which ones it considered when the answer looks odd.

Print an expression as LaTeXexport-latex

from sympy import symbols, Integral, cos, pi, latex, init_printing

x = symbols('x')
print(latex(Integral(cos(x)**2, (x, 0, pi))))
# \int\limits_{0}^{\pi} \cos^{2}{\left(x \right)}\, dx

init_printing()  # pretty output in a Jupyter notebook or terminal

Capital-letter constructors such as Integral, Derivative, and Sum build the unevaluated object, which is what you want for display; call .doit() to actually compute it. init_printing() only affects display, never the values.

Alternatives

PackageRegistryPick it when
python-flintPyPIYou need exact polynomial, matrix, or number-theory work that is fast, and can accept a compiled dependency and a narrower feature set.
mpmathPyPIYou only need arbitrary-precision floating point, special functions, and numeric root finding, without symbolic manipulation.
numpyPyPIThe problem is numeric all the way down and you never needed a closed-form answer.
sagemath-standardPyPIYou want a full mathematics system that wraps many specialized libraries and outperforms SymPy on serious algebra.