SymPy Quick Start
SymPy represents mathematical expressions symbolically, so operations can preserve exact structure instead of immediately producing floating-point approximations.
from sympy import diff, integrate, simplify, solve, symbols
x = symbols("x", real=True)
expression = (x + 1) ** 2
expanded_derivative = diff(expression, x)
antiderivative = integrate(expression, x)
roots = solve(expression - 4, x)
identity = simplify((x**2 - 1) / (x - 1))
Run the opening example in the SymPy Live shell. Compare Rational(1, 3) with a Python float, then declare a new positive symbol and inspect sqrt(p**2). Change assumptions deliberately rather than relying on the shell’s predefined symbols.
Working Rules
- Create symbols explicitly and add assumptions such as
real=Trueonly when justified. - Use
Eq(left, right)when the distinction between an equation and an expression matters. - Treat
solve()output as structured data; its shape depends on the problem and requested symbols. - Prefer a specific transformation such as
factor(),expand(), orcancel()when the desired output form is known. Generalsimplify()is heuristic and does not guarantee one canonical form.
Start with the official introductory tutorial, then consult the relevant module documentation for advanced assumptions or solvers.
Exact inputs and immutable expressions
This quick start covers scalar symbolic algebra, elementary calculus, and univariate equations with SymPy 1.x. It assumes Python expressions and the mathematical meaning of the operation being requested. Start with the tutorial’s gotchas: Python evaluates 1/3 before SymPy sees it, so use Rational(1, 3) for an exact fraction. Rational("0.1") represents one tenth; passing a binary float does not retroactively recover decimal intent.
from sympy import Eq, Rational, S, expand, lambdify, solveset, sqrt
third = Rational(1, 3)
assert 3 * third == 1
assert expanded_derivative == 2*x + 2
assert antiderivative == x**3/3 + x**2 + x
assert roots == [-3, 1]
assert identity == x + 1
value = expression.subs(x, third)
assert value == Rational(16, 9)
print(value.evalf(6)) # 1.77778
assert expression == (x + 1)**2
Expressions are immutable: subs, differentiation, and transformations return new expressions, not edits to expression. integrate returns one antiderivative without an arbitrary integration constant; add that constant when writing the general indefinite integral. Here integrate(expression, (x, 0, 1)) is the exact definite integral 7/3. Use evalf only when an approximate number is wanted; extra digits do not repair an input already rounded as a Python float.
Equality, assumptions, and domain
== checks structural equality, not general mathematical equivalence. expand((x + 1)**2) == (x + 1)**2 is false even though their difference simplifies to zero. Use Eq to construct an equation (it can evaluate immediately when equality is decidable); an expression passed to a solver means that expression equals zero.
Because x was declared real, sqrt(x**2) is Abs(x), not x. With a positive symbol p = symbols("p", positive=True), sqrt(p**2) simplifies to p. Assumptions enable only transformations valid under those assumptions; an unconstrained symbol is generally complex.
Cancellation also changes the apparent domain: (x**2 - 1)/(x - 1) is undefined at 1, while the simplified x + 1 is defined there. Keep the original exclusion x != 1 when interpreting the result as the same function. Check candidate roots in the original equation, not only in a transformed numerator.
real_roots = solveset(Eq(x**2, 2), x, domain=S.Reals)
assert real_roots == {-sqrt(2), sqrt(2)}
assert solveset(x**2 + 1, x, domain=S.Reals) == S.EmptySet
assert solveset(x**2 + 1, x, domain=S.Complexes) == {-S.ImaginaryUnit, S.ImaginaryUnit}
solveset returns a set and takes an explicit domain; do not assume a symbol’s real assumption changes its default complex solving domain. An unevaluated ConditionSet means the solver has not resolved the solution set, not that there are no solutions. solve(..., dict=True) is useful when a list of symbol-to-value mappings is the desired interface.
Numerical evaluation boundary
f = lambdify(x, expression, modules="math")
assert f(2) == 9
lambdify translates an expression into a numerical callable. The math backend above is scalar; use modules="numpy" with NumPy arrays for vectorized evaluation and accept that numerical domain and floating-point rules now apply. It uses generated executable code, so do not pass expressions derived from untrusted input. Symbolic work can remain unevaluated or grow expensive; a symbolic object is not a guarantee that every integral or equation has a closed-form result.