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".
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
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
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)
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
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
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)
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)
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
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)
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.