Hidden Assumptions in sympy

10 assumptions this code never checks · 3 critical · spanning Domain, Contract, Ordering, Resource, Temporal, Scale, Environment

Every codebase relies on things it never checks. Most of them are routine. CodeSea looked at sympy/sympy and picked out the few most likely to cause trouble. The full list is just below.

Most of what this code assumes is routine. These 3 are the ones most likely to cause trouble here. The rest are minor; they're under "Show everything".

Worth your attention first

For extremely large symbolic numbers or expressions that evaluate to infinity/NaN, int() will raise OverflowError or ValueError, causing assumption queries to crash instead of returning False

Worth your attention first

When expr.shape[0] or expr.shape[1] contain symbolic variables, the equality comparison expr.shape[0] == expr.shape[1] may return a symbolic boolean instead of True/False, breaking the assumption system's three-valued logic

Worth your attention first

If evalf() fails to produce a numeric approximation (e.g., for transcendental expressions involving undefined symbols), accessing r._prec will raise AttributeError, causing negative/positive queries to crash

Show everything (7 more)
Contract

The helper function assumes (expr - i).equals(0) will always return a definitive True/False, but equals() can return None for undecidable symbolic comparisons

If this fails: When equals() returns None due to symbolic indeterminacy, the 'not (expr - i).equals(0)' check evaluates to True, causing the function to incorrectly raise TypeError and return False for expressions that might actually be integers

sympy/assumptions/handlers/sets.py:_IntegerPredicate_number
Resource

The cache clearing mechanism assumes all cached functions have standard functools.lru_cache interfaces with cache_clear() methods, but custom cache decorators or third-party wrappers might not implement this protocol

If this fails: If a cached function uses a non-standard caching mechanism, clear_cache() silently skips it, leaving stale cached results that can cause incorrect symbolic computations in long-running processes

sympy/core/cache.py:CACHE.clear_cache
Domain

The finite predicate handler assumes that expr.is_finite attribute, when not None, contains a reliable boolean value, but symbols can be constructed with arbitrary assumption values including non-boolean types

If this fails: If a symbol is created with is_finite set to a non-boolean value (like a string or complex number), the handler returns this invalid value, breaking assumption queries that expect True/False/None

sympy/assumptions/handlers/calculus.py:@FinitePredicate.register(Symbol)
Contract

The commutative handler assumes expr.args contains only objects that can be passed to ask(Q.commutative(arg), assumptions), but args can contain non-Basic objects like strings, numbers, or None values

If this fails: When expr.args contains primitive Python objects that aren't SymPy expressions, ask() may fail with AttributeError or TypeError, causing commutativity queries to crash instead of returning a sensible default

sympy/assumptions/handlers/common.py:@CommutativePredicate.register(Basic)
Temporal

The print_cache method assumes cache_info() returns a stable snapshot of cache statistics, but in multi-threaded environments, cache statistics can change between the info() call and print() call

If this fails: Cache statistics displayed may be inconsistent or reflect a mixture of states from different time points, making cache debugging misleading in concurrent symbolic computation scenarios

sympy/core/cache.py:_cache.print_cache
Scale

The negative predicate evaluates expressions to only 2 decimal places of precision, assuming this is sufficient to determine sign, but expressions with values very close to zero may require higher precision

If this fails: For expressions like sin(π) that should be exactly zero but evaluate to tiny floating-point errors, 2-digit precision may incorrectly classify them as positive or negative instead of recognizing them as effectively zero

sympy/assumptions/handlers/order.py:r.evalf(2)
Environment

The module assumes that avoiding direct imports prevents circular dependencies, but Python's import system can still create circular dependencies through indirect imports or dynamic imports in handler functions

If this fails: When handlers dynamically import modules or when the import dependency graph changes, circular import errors can still occur at runtime, making the assumption system unreliable during complex symbolic operations

sympy/assumptions/handlers/__init__.py:circular import prevention

See the full structural analysis of sympy: the pipeline, data models, and system behavior that put these assumptions in context.

Full analysis of sympy/sympy →

Frequently Asked Questions

What does sympy assume that could break in production?

The one most likely to cause trouble: The _PrimePredicate_number function assumes int(expr.round()) will always produce a valid integer within Python's int range, but never validates the result fits in memory or that round() produces a finite value If this fails, For extremely large symbolic numbers or expressions that evaluate to infinity/NaN, int() will raise OverflowError or ValueError, causing assumption queries to crash instead of returning False

How many hidden assumptions does sympy have?

CodeSea found 10 assumptions sympy relies on but never validates, 3 of them critical, spanning Domain, Contract, Ordering, Resource, Temporal, Scale, Environment. Most are routine — the analysis flags the two or three most likely to actually bite.

What is a hidden assumption?

Something the code depends on but never checks: a data shape, an ordering, an environment condition, a scale limit, or a contract with another service. It holds until the world it runs in changes, then fails silently.