Assumptions on symbols

SymPy · Practical · 11 min

Many 'obvious' simplifications are false for complex numbers; assumptions are how you tell SymPy what you know.

By default a SymPy symbol is a complex number with no known properties. That is why sqrt(x**2) does not simplify to x (it is false for x = -1) and why log(a*b) is not expanded into log(a) + log(b).

You attach assumptions when creating the symbol: symbols("x", positive=True), real=True, integer=True, nonnegative=True, and so on. SymPy then uses them everywhere: in automatic evaluation, simplify, solveset, integrals (removing Piecewise conditions) and more.

You can query properties with attributes like expr.is_positive, expr.is_real or expr.is_integer. These use three-valued logic: they return True, False or None, where None means "cannot be decided". Never write if expr.is_positive: without thinking about None, which is falsy.

Assumptions are part of a symbol's identity: Symbol("x") and Symbol("x", positive=True) are different symbols, and substituting one for the other is a common source of "why didn't it simplify?" bugs. Keep one definition per name.

There is also a newer query system, ask(Q.positive(x), Q.positive(x) & ...), and refine(expr, Q.positive(x)), which lets you apply assumptions to an existing expression without re-creating symbols.

Example

from sympy import symbols, sqrt, simplify, log, expand_log, refine, Q, Abs, solveset

x, y = symbols("x y")
p = symbols("p", positive=True)
r = symbols("r", real=True)

print(sqrt(x**2), sqrt(p**2), sqrt(r**2))  # sqrt(x**2) p Abs(r)
q = symbols("q", positive=True)
print(expand_log(log(p * q)))              # log(p) + log(q)
print(expand_log(log(x * y)))              # log(x*y)

print((p + 1).is_positive)  # True
print((x + 1).is_positive)  # None  (unknown, not False!)
print(refine(Abs(x), Q.positive(x)))       # x

print(solveset(x**2 - 4, x))               # {-2, 2}
print(symbols("x") == symbols("x", positive=True))  # False

Remember

  • Plain symbols are complex; add positive=True, real=True etc. when true
  • is_* attributes return True, False or None (unknown)
  • A symbol with assumptions is a different symbol from one without

Watch out for

  • Treating is_positive == None as False in an if statement.
  • Mixing x = Symbol('x') and x = Symbol('x', real=True) in one computation.

Check yourself

  1. For a plain x = symbols('x'), what is sqrt(x**2)?
  2. x = symbols('x'); what does (x**2).is_nonnegative return?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: docs.sympy.org/latest/guides/assumptions.html