Add files using upload-large-folder tool
Browse filesThis view is limited to 50 files because it contains too many changes. See raw diff
- lib/python3.12/site-packages/sympy/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/__pycache__/abc.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/__pycache__/conftest.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/__pycache__/galgebra.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/__pycache__/release.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/__pycache__/this.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/__init__.py +18 -0
- lib/python3.12/site-packages/sympy/assumptions/ask.py +651 -0
- lib/python3.12/site-packages/sympy/assumptions/ask_generated.py +352 -0
- lib/python3.12/site-packages/sympy/assumptions/assume.py +485 -0
- lib/python3.12/site-packages/sympy/assumptions/cnf.py +445 -0
- lib/python3.12/site-packages/sympy/assumptions/facts.py +270 -0
- lib/python3.12/site-packages/sympy/assumptions/handlers/calculus.py +258 -0
- lib/python3.12/site-packages/sympy/assumptions/handlers/matrices.py +716 -0
- lib/python3.12/site-packages/sympy/assumptions/handlers/ntheory.py +269 -0
- lib/python3.12/site-packages/sympy/assumptions/handlers/order.py +436 -0
- lib/python3.12/site-packages/sympy/assumptions/handlers/sets.py +772 -0
- lib/python3.12/site-packages/sympy/assumptions/lra_satask.py +286 -0
- lib/python3.12/site-packages/sympy/assumptions/refine.py +405 -0
- lib/python3.12/site-packages/sympy/assumptions/satask.py +369 -0
- lib/python3.12/site-packages/sympy/assumptions/sathandlers.py +322 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__init__.py +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_assumptions_2.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_context.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_matrices.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_refine.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_rel_queries.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_satask.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_sathandlers.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_wrapper.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_assumptions_2.py +35 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_context.py +39 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_matrices.py +283 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_query.py +0 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_refine.py +227 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_rel_queries.py +172 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_satask.py +378 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_sathandlers.py +50 -0
- lib/python3.12/site-packages/sympy/assumptions/tests/test_wrapper.py +39 -0
- lib/python3.12/site-packages/sympy/assumptions/wrapper.py +164 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/__init__.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/bbp_pi.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/continued_fraction.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/digits.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/ecm.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/egyptian_fraction.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/elliptic_curve.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/factor_.cpython-312.pyc +0 -0
- lib/python3.12/site-packages/sympy/ntheory/__pycache__/generate.cpython-312.pyc +0 -0
lib/python3.12/site-packages/sympy/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (31 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/__pycache__/abc.cpython-312.pyc
ADDED
|
Binary file (4.37 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/__pycache__/conftest.cpython-312.pyc
ADDED
|
Binary file (4.41 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/__pycache__/galgebra.cpython-312.pyc
ADDED
|
Binary file (318 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/__pycache__/release.cpython-312.pyc
ADDED
|
Binary file (210 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/__pycache__/this.cpython-312.pyc
ADDED
|
Binary file (769 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/__init__.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
A module to implement logical predicates and assumption system.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from .assume import (
|
| 6 |
+
AppliedPredicate, Predicate, AssumptionsContext, assuming,
|
| 7 |
+
global_assumptions
|
| 8 |
+
)
|
| 9 |
+
from .ask import Q, ask, register_handler, remove_handler
|
| 10 |
+
from .refine import refine
|
| 11 |
+
from .relation import BinaryRelation, AppliedBinaryRelation
|
| 12 |
+
|
| 13 |
+
__all__ = [
|
| 14 |
+
'AppliedPredicate', 'Predicate', 'AssumptionsContext', 'assuming',
|
| 15 |
+
'global_assumptions', 'Q', 'ask', 'register_handler', 'remove_handler',
|
| 16 |
+
'refine',
|
| 17 |
+
'BinaryRelation', 'AppliedBinaryRelation'
|
| 18 |
+
]
|
lib/python3.12/site-packages/sympy/assumptions/ask.py
ADDED
|
@@ -0,0 +1,651 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Module for querying SymPy objects about assumptions."""
|
| 2 |
+
|
| 3 |
+
from sympy.assumptions.assume import (global_assumptions, Predicate,
|
| 4 |
+
AppliedPredicate)
|
| 5 |
+
from sympy.assumptions.cnf import CNF, EncodedCNF, Literal
|
| 6 |
+
from sympy.core import sympify
|
| 7 |
+
from sympy.core.kind import BooleanKind
|
| 8 |
+
from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
|
| 9 |
+
from sympy.logic.inference import satisfiable
|
| 10 |
+
from sympy.utilities.decorator import memoize_property
|
| 11 |
+
from sympy.utilities.exceptions import (sympy_deprecation_warning,
|
| 12 |
+
SymPyDeprecationWarning,
|
| 13 |
+
ignore_warnings)
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
# Memoization is necessary for the properties of AssumptionKeys to
|
| 17 |
+
# ensure that only one object of Predicate objects are created.
|
| 18 |
+
# This is because assumption handlers are registered on those objects.
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
class AssumptionKeys:
|
| 22 |
+
"""
|
| 23 |
+
This class contains all the supported keys by ``ask``.
|
| 24 |
+
It should be accessed via the instance ``sympy.Q``.
|
| 25 |
+
|
| 26 |
+
"""
|
| 27 |
+
|
| 28 |
+
# DO NOT add methods or properties other than predicate keys.
|
| 29 |
+
# SAT solver checks the properties of Q and use them to compute the
|
| 30 |
+
# fact system. Non-predicate attributes will break this.
|
| 31 |
+
|
| 32 |
+
@memoize_property
|
| 33 |
+
def hermitian(self):
|
| 34 |
+
from .handlers.sets import HermitianPredicate
|
| 35 |
+
return HermitianPredicate()
|
| 36 |
+
|
| 37 |
+
@memoize_property
|
| 38 |
+
def antihermitian(self):
|
| 39 |
+
from .handlers.sets import AntihermitianPredicate
|
| 40 |
+
return AntihermitianPredicate()
|
| 41 |
+
|
| 42 |
+
@memoize_property
|
| 43 |
+
def real(self):
|
| 44 |
+
from .handlers.sets import RealPredicate
|
| 45 |
+
return RealPredicate()
|
| 46 |
+
|
| 47 |
+
@memoize_property
|
| 48 |
+
def extended_real(self):
|
| 49 |
+
from .handlers.sets import ExtendedRealPredicate
|
| 50 |
+
return ExtendedRealPredicate()
|
| 51 |
+
|
| 52 |
+
@memoize_property
|
| 53 |
+
def imaginary(self):
|
| 54 |
+
from .handlers.sets import ImaginaryPredicate
|
| 55 |
+
return ImaginaryPredicate()
|
| 56 |
+
|
| 57 |
+
@memoize_property
|
| 58 |
+
def complex(self):
|
| 59 |
+
from .handlers.sets import ComplexPredicate
|
| 60 |
+
return ComplexPredicate()
|
| 61 |
+
|
| 62 |
+
@memoize_property
|
| 63 |
+
def algebraic(self):
|
| 64 |
+
from .handlers.sets import AlgebraicPredicate
|
| 65 |
+
return AlgebraicPredicate()
|
| 66 |
+
|
| 67 |
+
@memoize_property
|
| 68 |
+
def transcendental(self):
|
| 69 |
+
from .predicates.sets import TranscendentalPredicate
|
| 70 |
+
return TranscendentalPredicate()
|
| 71 |
+
|
| 72 |
+
@memoize_property
|
| 73 |
+
def integer(self):
|
| 74 |
+
from .handlers.sets import IntegerPredicate
|
| 75 |
+
return IntegerPredicate()
|
| 76 |
+
|
| 77 |
+
@memoize_property
|
| 78 |
+
def noninteger(self):
|
| 79 |
+
from .predicates.sets import NonIntegerPredicate
|
| 80 |
+
return NonIntegerPredicate()
|
| 81 |
+
|
| 82 |
+
@memoize_property
|
| 83 |
+
def rational(self):
|
| 84 |
+
from .handlers.sets import RationalPredicate
|
| 85 |
+
return RationalPredicate()
|
| 86 |
+
|
| 87 |
+
@memoize_property
|
| 88 |
+
def irrational(self):
|
| 89 |
+
from .handlers.sets import IrrationalPredicate
|
| 90 |
+
return IrrationalPredicate()
|
| 91 |
+
|
| 92 |
+
@memoize_property
|
| 93 |
+
def finite(self):
|
| 94 |
+
from .handlers.calculus import FinitePredicate
|
| 95 |
+
return FinitePredicate()
|
| 96 |
+
|
| 97 |
+
@memoize_property
|
| 98 |
+
def infinite(self):
|
| 99 |
+
from .handlers.calculus import InfinitePredicate
|
| 100 |
+
return InfinitePredicate()
|
| 101 |
+
|
| 102 |
+
@memoize_property
|
| 103 |
+
def positive_infinite(self):
|
| 104 |
+
from .handlers.calculus import PositiveInfinitePredicate
|
| 105 |
+
return PositiveInfinitePredicate()
|
| 106 |
+
|
| 107 |
+
@memoize_property
|
| 108 |
+
def negative_infinite(self):
|
| 109 |
+
from .handlers.calculus import NegativeInfinitePredicate
|
| 110 |
+
return NegativeInfinitePredicate()
|
| 111 |
+
|
| 112 |
+
@memoize_property
|
| 113 |
+
def positive(self):
|
| 114 |
+
from .handlers.order import PositivePredicate
|
| 115 |
+
return PositivePredicate()
|
| 116 |
+
|
| 117 |
+
@memoize_property
|
| 118 |
+
def negative(self):
|
| 119 |
+
from .handlers.order import NegativePredicate
|
| 120 |
+
return NegativePredicate()
|
| 121 |
+
|
| 122 |
+
@memoize_property
|
| 123 |
+
def zero(self):
|
| 124 |
+
from .handlers.order import ZeroPredicate
|
| 125 |
+
return ZeroPredicate()
|
| 126 |
+
|
| 127 |
+
@memoize_property
|
| 128 |
+
def extended_positive(self):
|
| 129 |
+
from .handlers.order import ExtendedPositivePredicate
|
| 130 |
+
return ExtendedPositivePredicate()
|
| 131 |
+
|
| 132 |
+
@memoize_property
|
| 133 |
+
def extended_negative(self):
|
| 134 |
+
from .handlers.order import ExtendedNegativePredicate
|
| 135 |
+
return ExtendedNegativePredicate()
|
| 136 |
+
|
| 137 |
+
@memoize_property
|
| 138 |
+
def nonzero(self):
|
| 139 |
+
from .handlers.order import NonZeroPredicate
|
| 140 |
+
return NonZeroPredicate()
|
| 141 |
+
|
| 142 |
+
@memoize_property
|
| 143 |
+
def nonpositive(self):
|
| 144 |
+
from .handlers.order import NonPositivePredicate
|
| 145 |
+
return NonPositivePredicate()
|
| 146 |
+
|
| 147 |
+
@memoize_property
|
| 148 |
+
def nonnegative(self):
|
| 149 |
+
from .handlers.order import NonNegativePredicate
|
| 150 |
+
return NonNegativePredicate()
|
| 151 |
+
|
| 152 |
+
@memoize_property
|
| 153 |
+
def extended_nonzero(self):
|
| 154 |
+
from .handlers.order import ExtendedNonZeroPredicate
|
| 155 |
+
return ExtendedNonZeroPredicate()
|
| 156 |
+
|
| 157 |
+
@memoize_property
|
| 158 |
+
def extended_nonpositive(self):
|
| 159 |
+
from .handlers.order import ExtendedNonPositivePredicate
|
| 160 |
+
return ExtendedNonPositivePredicate()
|
| 161 |
+
|
| 162 |
+
@memoize_property
|
| 163 |
+
def extended_nonnegative(self):
|
| 164 |
+
from .handlers.order import ExtendedNonNegativePredicate
|
| 165 |
+
return ExtendedNonNegativePredicate()
|
| 166 |
+
|
| 167 |
+
@memoize_property
|
| 168 |
+
def even(self):
|
| 169 |
+
from .handlers.ntheory import EvenPredicate
|
| 170 |
+
return EvenPredicate()
|
| 171 |
+
|
| 172 |
+
@memoize_property
|
| 173 |
+
def odd(self):
|
| 174 |
+
from .handlers.ntheory import OddPredicate
|
| 175 |
+
return OddPredicate()
|
| 176 |
+
|
| 177 |
+
@memoize_property
|
| 178 |
+
def prime(self):
|
| 179 |
+
from .handlers.ntheory import PrimePredicate
|
| 180 |
+
return PrimePredicate()
|
| 181 |
+
|
| 182 |
+
@memoize_property
|
| 183 |
+
def composite(self):
|
| 184 |
+
from .handlers.ntheory import CompositePredicate
|
| 185 |
+
return CompositePredicate()
|
| 186 |
+
|
| 187 |
+
@memoize_property
|
| 188 |
+
def commutative(self):
|
| 189 |
+
from .handlers.common import CommutativePredicate
|
| 190 |
+
return CommutativePredicate()
|
| 191 |
+
|
| 192 |
+
@memoize_property
|
| 193 |
+
def is_true(self):
|
| 194 |
+
from .handlers.common import IsTruePredicate
|
| 195 |
+
return IsTruePredicate()
|
| 196 |
+
|
| 197 |
+
@memoize_property
|
| 198 |
+
def symmetric(self):
|
| 199 |
+
from .handlers.matrices import SymmetricPredicate
|
| 200 |
+
return SymmetricPredicate()
|
| 201 |
+
|
| 202 |
+
@memoize_property
|
| 203 |
+
def invertible(self):
|
| 204 |
+
from .handlers.matrices import InvertiblePredicate
|
| 205 |
+
return InvertiblePredicate()
|
| 206 |
+
|
| 207 |
+
@memoize_property
|
| 208 |
+
def orthogonal(self):
|
| 209 |
+
from .handlers.matrices import OrthogonalPredicate
|
| 210 |
+
return OrthogonalPredicate()
|
| 211 |
+
|
| 212 |
+
@memoize_property
|
| 213 |
+
def unitary(self):
|
| 214 |
+
from .handlers.matrices import UnitaryPredicate
|
| 215 |
+
return UnitaryPredicate()
|
| 216 |
+
|
| 217 |
+
@memoize_property
|
| 218 |
+
def positive_definite(self):
|
| 219 |
+
from .handlers.matrices import PositiveDefinitePredicate
|
| 220 |
+
return PositiveDefinitePredicate()
|
| 221 |
+
|
| 222 |
+
@memoize_property
|
| 223 |
+
def upper_triangular(self):
|
| 224 |
+
from .handlers.matrices import UpperTriangularPredicate
|
| 225 |
+
return UpperTriangularPredicate()
|
| 226 |
+
|
| 227 |
+
@memoize_property
|
| 228 |
+
def lower_triangular(self):
|
| 229 |
+
from .handlers.matrices import LowerTriangularPredicate
|
| 230 |
+
return LowerTriangularPredicate()
|
| 231 |
+
|
| 232 |
+
@memoize_property
|
| 233 |
+
def diagonal(self):
|
| 234 |
+
from .handlers.matrices import DiagonalPredicate
|
| 235 |
+
return DiagonalPredicate()
|
| 236 |
+
|
| 237 |
+
@memoize_property
|
| 238 |
+
def fullrank(self):
|
| 239 |
+
from .handlers.matrices import FullRankPredicate
|
| 240 |
+
return FullRankPredicate()
|
| 241 |
+
|
| 242 |
+
@memoize_property
|
| 243 |
+
def square(self):
|
| 244 |
+
from .handlers.matrices import SquarePredicate
|
| 245 |
+
return SquarePredicate()
|
| 246 |
+
|
| 247 |
+
@memoize_property
|
| 248 |
+
def integer_elements(self):
|
| 249 |
+
from .handlers.matrices import IntegerElementsPredicate
|
| 250 |
+
return IntegerElementsPredicate()
|
| 251 |
+
|
| 252 |
+
@memoize_property
|
| 253 |
+
def real_elements(self):
|
| 254 |
+
from .handlers.matrices import RealElementsPredicate
|
| 255 |
+
return RealElementsPredicate()
|
| 256 |
+
|
| 257 |
+
@memoize_property
|
| 258 |
+
def complex_elements(self):
|
| 259 |
+
from .handlers.matrices import ComplexElementsPredicate
|
| 260 |
+
return ComplexElementsPredicate()
|
| 261 |
+
|
| 262 |
+
@memoize_property
|
| 263 |
+
def singular(self):
|
| 264 |
+
from .predicates.matrices import SingularPredicate
|
| 265 |
+
return SingularPredicate()
|
| 266 |
+
|
| 267 |
+
@memoize_property
|
| 268 |
+
def normal(self):
|
| 269 |
+
from .predicates.matrices import NormalPredicate
|
| 270 |
+
return NormalPredicate()
|
| 271 |
+
|
| 272 |
+
@memoize_property
|
| 273 |
+
def triangular(self):
|
| 274 |
+
from .predicates.matrices import TriangularPredicate
|
| 275 |
+
return TriangularPredicate()
|
| 276 |
+
|
| 277 |
+
@memoize_property
|
| 278 |
+
def unit_triangular(self):
|
| 279 |
+
from .predicates.matrices import UnitTriangularPredicate
|
| 280 |
+
return UnitTriangularPredicate()
|
| 281 |
+
|
| 282 |
+
@memoize_property
|
| 283 |
+
def eq(self):
|
| 284 |
+
from .relation.equality import EqualityPredicate
|
| 285 |
+
return EqualityPredicate()
|
| 286 |
+
|
| 287 |
+
@memoize_property
|
| 288 |
+
def ne(self):
|
| 289 |
+
from .relation.equality import UnequalityPredicate
|
| 290 |
+
return UnequalityPredicate()
|
| 291 |
+
|
| 292 |
+
@memoize_property
|
| 293 |
+
def gt(self):
|
| 294 |
+
from .relation.equality import StrictGreaterThanPredicate
|
| 295 |
+
return StrictGreaterThanPredicate()
|
| 296 |
+
|
| 297 |
+
@memoize_property
|
| 298 |
+
def ge(self):
|
| 299 |
+
from .relation.equality import GreaterThanPredicate
|
| 300 |
+
return GreaterThanPredicate()
|
| 301 |
+
|
| 302 |
+
@memoize_property
|
| 303 |
+
def lt(self):
|
| 304 |
+
from .relation.equality import StrictLessThanPredicate
|
| 305 |
+
return StrictLessThanPredicate()
|
| 306 |
+
|
| 307 |
+
@memoize_property
|
| 308 |
+
def le(self):
|
| 309 |
+
from .relation.equality import LessThanPredicate
|
| 310 |
+
return LessThanPredicate()
|
| 311 |
+
|
| 312 |
+
|
| 313 |
+
Q = AssumptionKeys()
|
| 314 |
+
|
| 315 |
+
def _extract_all_facts(assump, exprs):
|
| 316 |
+
"""
|
| 317 |
+
Extract all relevant assumptions from *assump* with respect to given *exprs*.
|
| 318 |
+
|
| 319 |
+
Parameters
|
| 320 |
+
==========
|
| 321 |
+
|
| 322 |
+
assump : sympy.assumptions.cnf.CNF
|
| 323 |
+
|
| 324 |
+
exprs : tuple of expressions
|
| 325 |
+
|
| 326 |
+
Returns
|
| 327 |
+
=======
|
| 328 |
+
|
| 329 |
+
sympy.assumptions.cnf.CNF
|
| 330 |
+
|
| 331 |
+
Examples
|
| 332 |
+
========
|
| 333 |
+
|
| 334 |
+
>>> from sympy import Q
|
| 335 |
+
>>> from sympy.assumptions.cnf import CNF
|
| 336 |
+
>>> from sympy.assumptions.ask import _extract_all_facts
|
| 337 |
+
>>> from sympy.abc import x, y
|
| 338 |
+
>>> assump = CNF.from_prop(Q.positive(x) & Q.integer(y))
|
| 339 |
+
>>> exprs = (x,)
|
| 340 |
+
>>> cnf = _extract_all_facts(assump, exprs)
|
| 341 |
+
>>> cnf.clauses
|
| 342 |
+
{frozenset({Literal(Q.positive, False)})}
|
| 343 |
+
|
| 344 |
+
"""
|
| 345 |
+
facts = set()
|
| 346 |
+
|
| 347 |
+
for clause in assump.clauses:
|
| 348 |
+
args = []
|
| 349 |
+
for literal in clause:
|
| 350 |
+
if isinstance(literal.lit, AppliedPredicate) and len(literal.lit.arguments) == 1:
|
| 351 |
+
if literal.lit.arg in exprs:
|
| 352 |
+
# Add literal if it has matching in it
|
| 353 |
+
args.append(Literal(literal.lit.function, literal.is_Not))
|
| 354 |
+
else:
|
| 355 |
+
# If any of the literals doesn't have matching expr don't add the whole clause.
|
| 356 |
+
break
|
| 357 |
+
else:
|
| 358 |
+
# If any of the literals aren't unary predicate don't add the whole clause.
|
| 359 |
+
break
|
| 360 |
+
|
| 361 |
+
else:
|
| 362 |
+
if args:
|
| 363 |
+
facts.add(frozenset(args))
|
| 364 |
+
return CNF(facts)
|
| 365 |
+
|
| 366 |
+
|
| 367 |
+
def ask(proposition, assumptions=True, context=global_assumptions):
|
| 368 |
+
"""
|
| 369 |
+
Function to evaluate the proposition with assumptions.
|
| 370 |
+
|
| 371 |
+
Explanation
|
| 372 |
+
===========
|
| 373 |
+
|
| 374 |
+
This function evaluates the proposition to ``True`` or ``False`` if
|
| 375 |
+
the truth value can be determined. If not, it returns ``None``.
|
| 376 |
+
|
| 377 |
+
It should be discerned from :func:`~.refine()` which, when applied to a
|
| 378 |
+
proposition, simplifies the argument to symbolic ``Boolean`` instead of
|
| 379 |
+
Python built-in ``True``, ``False`` or ``None``.
|
| 380 |
+
|
| 381 |
+
**Syntax**
|
| 382 |
+
|
| 383 |
+
* ask(proposition)
|
| 384 |
+
Evaluate the *proposition* in global assumption context.
|
| 385 |
+
|
| 386 |
+
* ask(proposition, assumptions)
|
| 387 |
+
Evaluate the *proposition* with respect to *assumptions* in
|
| 388 |
+
global assumption context.
|
| 389 |
+
|
| 390 |
+
Parameters
|
| 391 |
+
==========
|
| 392 |
+
|
| 393 |
+
proposition : Boolean
|
| 394 |
+
Proposition which will be evaluated to boolean value. If this is
|
| 395 |
+
not ``AppliedPredicate``, it will be wrapped by ``Q.is_true``.
|
| 396 |
+
|
| 397 |
+
assumptions : Boolean, optional
|
| 398 |
+
Local assumptions to evaluate the *proposition*.
|
| 399 |
+
|
| 400 |
+
context : AssumptionsContext, optional
|
| 401 |
+
Default assumptions to evaluate the *proposition*. By default,
|
| 402 |
+
this is ``sympy.assumptions.global_assumptions`` variable.
|
| 403 |
+
|
| 404 |
+
Returns
|
| 405 |
+
=======
|
| 406 |
+
|
| 407 |
+
``True``, ``False``, or ``None``
|
| 408 |
+
|
| 409 |
+
Raises
|
| 410 |
+
======
|
| 411 |
+
|
| 412 |
+
TypeError : *proposition* or *assumptions* is not valid logical expression.
|
| 413 |
+
|
| 414 |
+
ValueError : assumptions are inconsistent.
|
| 415 |
+
|
| 416 |
+
Examples
|
| 417 |
+
========
|
| 418 |
+
|
| 419 |
+
>>> from sympy import ask, Q, pi
|
| 420 |
+
>>> from sympy.abc import x, y
|
| 421 |
+
>>> ask(Q.rational(pi))
|
| 422 |
+
False
|
| 423 |
+
>>> ask(Q.even(x*y), Q.even(x) & Q.integer(y))
|
| 424 |
+
True
|
| 425 |
+
>>> ask(Q.prime(4*x), Q.integer(x))
|
| 426 |
+
False
|
| 427 |
+
|
| 428 |
+
If the truth value cannot be determined, ``None`` will be returned.
|
| 429 |
+
|
| 430 |
+
>>> print(ask(Q.odd(3*x))) # cannot determine unless we know x
|
| 431 |
+
None
|
| 432 |
+
|
| 433 |
+
``ValueError`` is raised if assumptions are inconsistent.
|
| 434 |
+
|
| 435 |
+
>>> ask(Q.integer(x), Q.even(x) & Q.odd(x))
|
| 436 |
+
Traceback (most recent call last):
|
| 437 |
+
...
|
| 438 |
+
ValueError: inconsistent assumptions Q.even(x) & Q.odd(x)
|
| 439 |
+
|
| 440 |
+
Notes
|
| 441 |
+
=====
|
| 442 |
+
|
| 443 |
+
Relations in assumptions are not implemented (yet), so the following
|
| 444 |
+
will not give a meaningful result.
|
| 445 |
+
|
| 446 |
+
>>> ask(Q.positive(x), x > 0)
|
| 447 |
+
|
| 448 |
+
It is however a work in progress.
|
| 449 |
+
|
| 450 |
+
See Also
|
| 451 |
+
========
|
| 452 |
+
|
| 453 |
+
sympy.assumptions.refine.refine : Simplification using assumptions.
|
| 454 |
+
Proposition is not reduced to ``None`` if the truth value cannot
|
| 455 |
+
be determined.
|
| 456 |
+
"""
|
| 457 |
+
from sympy.assumptions.satask import satask
|
| 458 |
+
from sympy.assumptions.lra_satask import lra_satask
|
| 459 |
+
from sympy.logic.algorithms.lra_theory import UnhandledInput
|
| 460 |
+
|
| 461 |
+
proposition = sympify(proposition)
|
| 462 |
+
assumptions = sympify(assumptions)
|
| 463 |
+
|
| 464 |
+
if isinstance(proposition, Predicate) or proposition.kind is not BooleanKind:
|
| 465 |
+
raise TypeError("proposition must be a valid logical expression")
|
| 466 |
+
|
| 467 |
+
if isinstance(assumptions, Predicate) or assumptions.kind is not BooleanKind:
|
| 468 |
+
raise TypeError("assumptions must be a valid logical expression")
|
| 469 |
+
|
| 470 |
+
binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
|
| 471 |
+
if isinstance(proposition, AppliedPredicate):
|
| 472 |
+
key, args = proposition.function, proposition.arguments
|
| 473 |
+
elif proposition.func in binrelpreds:
|
| 474 |
+
key, args = binrelpreds[type(proposition)], proposition.args
|
| 475 |
+
else:
|
| 476 |
+
key, args = Q.is_true, (proposition,)
|
| 477 |
+
|
| 478 |
+
# convert local and global assumptions to CNF
|
| 479 |
+
assump_cnf = CNF.from_prop(assumptions)
|
| 480 |
+
assump_cnf.extend(context)
|
| 481 |
+
|
| 482 |
+
# extract the relevant facts from assumptions with respect to args
|
| 483 |
+
local_facts = _extract_all_facts(assump_cnf, args)
|
| 484 |
+
|
| 485 |
+
# convert default facts and assumed facts to encoded CNF
|
| 486 |
+
known_facts_cnf = get_all_known_facts()
|
| 487 |
+
enc_cnf = EncodedCNF()
|
| 488 |
+
enc_cnf.from_cnf(CNF(known_facts_cnf))
|
| 489 |
+
enc_cnf.add_from_cnf(local_facts)
|
| 490 |
+
|
| 491 |
+
# check the satisfiability of given assumptions
|
| 492 |
+
if local_facts.clauses and satisfiable(enc_cnf) is False:
|
| 493 |
+
raise ValueError("inconsistent assumptions %s" % assumptions)
|
| 494 |
+
|
| 495 |
+
# quick computation for single fact
|
| 496 |
+
res = _ask_single_fact(key, local_facts)
|
| 497 |
+
if res is not None:
|
| 498 |
+
return res
|
| 499 |
+
|
| 500 |
+
# direct resolution method, no logic
|
| 501 |
+
res = key(*args)._eval_ask(assumptions)
|
| 502 |
+
if res is not None:
|
| 503 |
+
return bool(res)
|
| 504 |
+
|
| 505 |
+
# using satask (still costly)
|
| 506 |
+
res = satask(proposition, assumptions=assumptions, context=context)
|
| 507 |
+
if res is not None:
|
| 508 |
+
return res
|
| 509 |
+
|
| 510 |
+
try:
|
| 511 |
+
res = lra_satask(proposition, assumptions=assumptions, context=context)
|
| 512 |
+
except UnhandledInput:
|
| 513 |
+
return None
|
| 514 |
+
|
| 515 |
+
return res
|
| 516 |
+
|
| 517 |
+
|
| 518 |
+
def _ask_single_fact(key, local_facts):
|
| 519 |
+
"""
|
| 520 |
+
Compute the truth value of single predicate using assumptions.
|
| 521 |
+
|
| 522 |
+
Parameters
|
| 523 |
+
==========
|
| 524 |
+
|
| 525 |
+
key : sympy.assumptions.assume.Predicate
|
| 526 |
+
Proposition predicate.
|
| 527 |
+
|
| 528 |
+
local_facts : sympy.assumptions.cnf.CNF
|
| 529 |
+
Local assumption in CNF form.
|
| 530 |
+
|
| 531 |
+
Returns
|
| 532 |
+
=======
|
| 533 |
+
|
| 534 |
+
``True``, ``False`` or ``None``
|
| 535 |
+
|
| 536 |
+
Examples
|
| 537 |
+
========
|
| 538 |
+
|
| 539 |
+
>>> from sympy import Q
|
| 540 |
+
>>> from sympy.assumptions.cnf import CNF
|
| 541 |
+
>>> from sympy.assumptions.ask import _ask_single_fact
|
| 542 |
+
|
| 543 |
+
If prerequisite of proposition is rejected by the assumption,
|
| 544 |
+
return ``False``.
|
| 545 |
+
|
| 546 |
+
>>> key, assump = Q.zero, ~Q.zero
|
| 547 |
+
>>> local_facts = CNF.from_prop(assump)
|
| 548 |
+
>>> _ask_single_fact(key, local_facts)
|
| 549 |
+
False
|
| 550 |
+
>>> key, assump = Q.zero, ~Q.even
|
| 551 |
+
>>> local_facts = CNF.from_prop(assump)
|
| 552 |
+
>>> _ask_single_fact(key, local_facts)
|
| 553 |
+
False
|
| 554 |
+
|
| 555 |
+
If assumption implies the proposition, return ``True``.
|
| 556 |
+
|
| 557 |
+
>>> key, assump = Q.even, Q.zero
|
| 558 |
+
>>> local_facts = CNF.from_prop(assump)
|
| 559 |
+
>>> _ask_single_fact(key, local_facts)
|
| 560 |
+
True
|
| 561 |
+
|
| 562 |
+
If proposition rejects the assumption, return ``False``.
|
| 563 |
+
|
| 564 |
+
>>> key, assump = Q.even, Q.odd
|
| 565 |
+
>>> local_facts = CNF.from_prop(assump)
|
| 566 |
+
>>> _ask_single_fact(key, local_facts)
|
| 567 |
+
False
|
| 568 |
+
"""
|
| 569 |
+
if local_facts.clauses:
|
| 570 |
+
|
| 571 |
+
known_facts_dict = get_known_facts_dict()
|
| 572 |
+
|
| 573 |
+
if len(local_facts.clauses) == 1:
|
| 574 |
+
cl, = local_facts.clauses
|
| 575 |
+
if len(cl) == 1:
|
| 576 |
+
f, = cl
|
| 577 |
+
prop_facts = known_facts_dict.get(key, None)
|
| 578 |
+
prop_req = prop_facts[0] if prop_facts is not None else set()
|
| 579 |
+
if f.is_Not and f.arg in prop_req:
|
| 580 |
+
# the prerequisite of proposition is rejected
|
| 581 |
+
return False
|
| 582 |
+
|
| 583 |
+
for clause in local_facts.clauses:
|
| 584 |
+
if len(clause) == 1:
|
| 585 |
+
f, = clause
|
| 586 |
+
prop_facts = known_facts_dict.get(f.arg, None) if not f.is_Not else None
|
| 587 |
+
if prop_facts is None:
|
| 588 |
+
continue
|
| 589 |
+
|
| 590 |
+
prop_req, prop_rej = prop_facts
|
| 591 |
+
if key in prop_req:
|
| 592 |
+
# assumption implies the proposition
|
| 593 |
+
return True
|
| 594 |
+
elif key in prop_rej:
|
| 595 |
+
# proposition rejects the assumption
|
| 596 |
+
return False
|
| 597 |
+
|
| 598 |
+
return None
|
| 599 |
+
|
| 600 |
+
|
| 601 |
+
def register_handler(key, handler):
|
| 602 |
+
"""
|
| 603 |
+
Register a handler in the ask system. key must be a string and handler a
|
| 604 |
+
class inheriting from AskHandler.
|
| 605 |
+
|
| 606 |
+
.. deprecated:: 1.8.
|
| 607 |
+
Use multipledispatch handler instead. See :obj:`~.Predicate`.
|
| 608 |
+
|
| 609 |
+
"""
|
| 610 |
+
sympy_deprecation_warning(
|
| 611 |
+
"""
|
| 612 |
+
The AskHandler system is deprecated. The register_handler() function
|
| 613 |
+
should be replaced with the multipledispatch handler of Predicate.
|
| 614 |
+
""",
|
| 615 |
+
deprecated_since_version="1.8",
|
| 616 |
+
active_deprecations_target='deprecated-askhandler',
|
| 617 |
+
)
|
| 618 |
+
if isinstance(key, Predicate):
|
| 619 |
+
key = key.name.name
|
| 620 |
+
Qkey = getattr(Q, key, None)
|
| 621 |
+
if Qkey is not None:
|
| 622 |
+
Qkey.add_handler(handler)
|
| 623 |
+
else:
|
| 624 |
+
setattr(Q, key, Predicate(key, handlers=[handler]))
|
| 625 |
+
|
| 626 |
+
|
| 627 |
+
def remove_handler(key, handler):
|
| 628 |
+
"""
|
| 629 |
+
Removes a handler from the ask system.
|
| 630 |
+
|
| 631 |
+
.. deprecated:: 1.8.
|
| 632 |
+
Use multipledispatch handler instead. See :obj:`~.Predicate`.
|
| 633 |
+
|
| 634 |
+
"""
|
| 635 |
+
sympy_deprecation_warning(
|
| 636 |
+
"""
|
| 637 |
+
The AskHandler system is deprecated. The remove_handler() function
|
| 638 |
+
should be replaced with the multipledispatch handler of Predicate.
|
| 639 |
+
""",
|
| 640 |
+
deprecated_since_version="1.8",
|
| 641 |
+
active_deprecations_target='deprecated-askhandler',
|
| 642 |
+
)
|
| 643 |
+
if isinstance(key, Predicate):
|
| 644 |
+
key = key.name.name
|
| 645 |
+
# Don't show the same warning again recursively
|
| 646 |
+
with ignore_warnings(SymPyDeprecationWarning):
|
| 647 |
+
getattr(Q, key).remove_handler(handler)
|
| 648 |
+
|
| 649 |
+
|
| 650 |
+
from sympy.assumptions.ask_generated import (get_all_known_facts,
|
| 651 |
+
get_known_facts_dict)
|
lib/python3.12/site-packages/sympy/assumptions/ask_generated.py
ADDED
|
@@ -0,0 +1,352 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Do NOT manually edit this file.
|
| 3 |
+
Instead, run ./bin/ask_update.py.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from sympy.assumptions.ask import Q
|
| 7 |
+
from sympy.assumptions.cnf import Literal
|
| 8 |
+
from sympy.core.cache import cacheit
|
| 9 |
+
|
| 10 |
+
@cacheit
|
| 11 |
+
def get_all_known_facts():
|
| 12 |
+
"""
|
| 13 |
+
Known facts between unary predicates as CNF clauses.
|
| 14 |
+
"""
|
| 15 |
+
return {
|
| 16 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.imaginary, True), Literal(Q.transcendental, False))),
|
| 17 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.negative, True), Literal(Q.transcendental, False))),
|
| 18 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.positive, True), Literal(Q.transcendental, False))),
|
| 19 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
|
| 20 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.transcendental, False), Literal(Q.zero, True))),
|
| 21 |
+
frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
|
| 22 |
+
frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
|
| 23 |
+
frozenset((Literal(Q.antihermitian, False), Literal(Q.hermitian, False), Literal(Q.zero, True))),
|
| 24 |
+
frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
|
| 25 |
+
frozenset((Literal(Q.commutative, False), Literal(Q.finite, True))),
|
| 26 |
+
frozenset((Literal(Q.commutative, False), Literal(Q.infinite, True))),
|
| 27 |
+
frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
|
| 28 |
+
frozenset((Literal(Q.composite, False), Literal(Q.even, True), Literal(Q.positive, True), Literal(Q.prime, False))),
|
| 29 |
+
frozenset((Literal(Q.composite, True), Literal(Q.even, False), Literal(Q.odd, False))),
|
| 30 |
+
frozenset((Literal(Q.composite, True), Literal(Q.positive, False))),
|
| 31 |
+
frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
|
| 32 |
+
frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
|
| 33 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
|
| 34 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
|
| 35 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
|
| 36 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
|
| 37 |
+
frozenset((Literal(Q.even, False), Literal(Q.odd, False), Literal(Q.prime, True))),
|
| 38 |
+
frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
|
| 39 |
+
frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
|
| 40 |
+
frozenset((Literal(Q.even, True), Literal(Q.rational, False))),
|
| 41 |
+
frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
|
| 42 |
+
frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
|
| 43 |
+
frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
|
| 44 |
+
frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
|
| 45 |
+
frozenset((Literal(Q.hermitian, False), Literal(Q.negative, True))),
|
| 46 |
+
frozenset((Literal(Q.hermitian, False), Literal(Q.positive, True))),
|
| 47 |
+
frozenset((Literal(Q.hermitian, False), Literal(Q.zero, True))),
|
| 48 |
+
frozenset((Literal(Q.imaginary, True), Literal(Q.negative, True))),
|
| 49 |
+
frozenset((Literal(Q.imaginary, True), Literal(Q.positive, True))),
|
| 50 |
+
frozenset((Literal(Q.imaginary, True), Literal(Q.zero, True))),
|
| 51 |
+
frozenset((Literal(Q.infinite, False), Literal(Q.negative_infinite, True))),
|
| 52 |
+
frozenset((Literal(Q.infinite, False), Literal(Q.positive_infinite, True))),
|
| 53 |
+
frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
|
| 54 |
+
frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
|
| 55 |
+
frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
|
| 56 |
+
frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
|
| 57 |
+
frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
|
| 58 |
+
frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
|
| 59 |
+
frozenset((Literal(Q.irrational, False), Literal(Q.negative, True), Literal(Q.rational, False))),
|
| 60 |
+
frozenset((Literal(Q.irrational, False), Literal(Q.positive, True), Literal(Q.rational, False))),
|
| 61 |
+
frozenset((Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.zero, True))),
|
| 62 |
+
frozenset((Literal(Q.irrational, True), Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.zero, False))),
|
| 63 |
+
frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
|
| 64 |
+
frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
|
| 65 |
+
frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
|
| 66 |
+
frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.rational, True), Literal(Q.zero, False))),
|
| 67 |
+
frozenset((Literal(Q.negative, True), Literal(Q.negative_infinite, True))),
|
| 68 |
+
frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
|
| 69 |
+
frozenset((Literal(Q.negative, True), Literal(Q.positive_infinite, True))),
|
| 70 |
+
frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
|
| 71 |
+
frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True))),
|
| 72 |
+
frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive_infinite, True))),
|
| 73 |
+
frozenset((Literal(Q.negative_infinite, True), Literal(Q.zero, True))),
|
| 74 |
+
frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
|
| 75 |
+
frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
|
| 76 |
+
frozenset((Literal(Q.odd, True), Literal(Q.rational, False))),
|
| 77 |
+
frozenset((Literal(Q.orthogonal, False), Literal(Q.real_elements, True), Literal(Q.unitary, True))),
|
| 78 |
+
frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
|
| 79 |
+
frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
|
| 80 |
+
frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
|
| 81 |
+
frozenset((Literal(Q.positive, True), Literal(Q.positive_infinite, True))),
|
| 82 |
+
frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
|
| 83 |
+
frozenset((Literal(Q.positive_infinite, True), Literal(Q.zero, True))),
|
| 84 |
+
frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
|
| 85 |
+
frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
|
| 86 |
+
frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
|
| 87 |
+
}
|
| 88 |
+
|
| 89 |
+
@cacheit
|
| 90 |
+
def get_all_known_matrix_facts():
|
| 91 |
+
"""
|
| 92 |
+
Known facts between unary predicates for matrices as CNF clauses.
|
| 93 |
+
"""
|
| 94 |
+
return {
|
| 95 |
+
frozenset((Literal(Q.complex_elements, False), Literal(Q.real_elements, True))),
|
| 96 |
+
frozenset((Literal(Q.diagonal, False), Literal(Q.lower_triangular, True), Literal(Q.upper_triangular, True))),
|
| 97 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.lower_triangular, False))),
|
| 98 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.normal, False))),
|
| 99 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.symmetric, False))),
|
| 100 |
+
frozenset((Literal(Q.diagonal, True), Literal(Q.upper_triangular, False))),
|
| 101 |
+
frozenset((Literal(Q.fullrank, False), Literal(Q.invertible, True))),
|
| 102 |
+
frozenset((Literal(Q.fullrank, True), Literal(Q.invertible, False), Literal(Q.square, True))),
|
| 103 |
+
frozenset((Literal(Q.integer_elements, True), Literal(Q.real_elements, False))),
|
| 104 |
+
frozenset((Literal(Q.invertible, False), Literal(Q.positive_definite, True))),
|
| 105 |
+
frozenset((Literal(Q.invertible, False), Literal(Q.singular, False))),
|
| 106 |
+
frozenset((Literal(Q.invertible, False), Literal(Q.unitary, True))),
|
| 107 |
+
frozenset((Literal(Q.invertible, True), Literal(Q.singular, True))),
|
| 108 |
+
frozenset((Literal(Q.invertible, True), Literal(Q.square, False))),
|
| 109 |
+
frozenset((Literal(Q.lower_triangular, False), Literal(Q.triangular, True), Literal(Q.upper_triangular, False))),
|
| 110 |
+
frozenset((Literal(Q.lower_triangular, True), Literal(Q.triangular, False))),
|
| 111 |
+
frozenset((Literal(Q.normal, False), Literal(Q.unitary, True))),
|
| 112 |
+
frozenset((Literal(Q.normal, True), Literal(Q.square, False))),
|
| 113 |
+
frozenset((Literal(Q.orthogonal, False), Literal(Q.real_elements, True), Literal(Q.unitary, True))),
|
| 114 |
+
frozenset((Literal(Q.orthogonal, True), Literal(Q.positive_definite, False))),
|
| 115 |
+
frozenset((Literal(Q.orthogonal, True), Literal(Q.unitary, False))),
|
| 116 |
+
frozenset((Literal(Q.square, False), Literal(Q.symmetric, True))),
|
| 117 |
+
frozenset((Literal(Q.triangular, False), Literal(Q.unit_triangular, True))),
|
| 118 |
+
frozenset((Literal(Q.triangular, False), Literal(Q.upper_triangular, True)))
|
| 119 |
+
}
|
| 120 |
+
|
| 121 |
+
@cacheit
|
| 122 |
+
def get_all_known_number_facts():
|
| 123 |
+
"""
|
| 124 |
+
Known facts between unary predicates for numbers as CNF clauses.
|
| 125 |
+
"""
|
| 126 |
+
return {
|
| 127 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.imaginary, True), Literal(Q.transcendental, False))),
|
| 128 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.negative, True), Literal(Q.transcendental, False))),
|
| 129 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.positive, True), Literal(Q.transcendental, False))),
|
| 130 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.rational, True))),
|
| 131 |
+
frozenset((Literal(Q.algebraic, False), Literal(Q.transcendental, False), Literal(Q.zero, True))),
|
| 132 |
+
frozenset((Literal(Q.algebraic, True), Literal(Q.finite, False))),
|
| 133 |
+
frozenset((Literal(Q.algebraic, True), Literal(Q.transcendental, True))),
|
| 134 |
+
frozenset((Literal(Q.antihermitian, False), Literal(Q.hermitian, False), Literal(Q.zero, True))),
|
| 135 |
+
frozenset((Literal(Q.antihermitian, False), Literal(Q.imaginary, True))),
|
| 136 |
+
frozenset((Literal(Q.commutative, False), Literal(Q.finite, True))),
|
| 137 |
+
frozenset((Literal(Q.commutative, False), Literal(Q.infinite, True))),
|
| 138 |
+
frozenset((Literal(Q.composite, False), Literal(Q.even, True), Literal(Q.positive, True), Literal(Q.prime, False))),
|
| 139 |
+
frozenset((Literal(Q.composite, True), Literal(Q.even, False), Literal(Q.odd, False))),
|
| 140 |
+
frozenset((Literal(Q.composite, True), Literal(Q.positive, False))),
|
| 141 |
+
frozenset((Literal(Q.composite, True), Literal(Q.prime, True))),
|
| 142 |
+
frozenset((Literal(Q.even, False), Literal(Q.odd, False), Literal(Q.prime, True))),
|
| 143 |
+
frozenset((Literal(Q.even, False), Literal(Q.zero, True))),
|
| 144 |
+
frozenset((Literal(Q.even, True), Literal(Q.odd, True))),
|
| 145 |
+
frozenset((Literal(Q.even, True), Literal(Q.rational, False))),
|
| 146 |
+
frozenset((Literal(Q.finite, False), Literal(Q.transcendental, True))),
|
| 147 |
+
frozenset((Literal(Q.finite, True), Literal(Q.infinite, True))),
|
| 148 |
+
frozenset((Literal(Q.hermitian, False), Literal(Q.negative, True))),
|
| 149 |
+
frozenset((Literal(Q.hermitian, False), Literal(Q.positive, True))),
|
| 150 |
+
frozenset((Literal(Q.hermitian, False), Literal(Q.zero, True))),
|
| 151 |
+
frozenset((Literal(Q.imaginary, True), Literal(Q.negative, True))),
|
| 152 |
+
frozenset((Literal(Q.imaginary, True), Literal(Q.positive, True))),
|
| 153 |
+
frozenset((Literal(Q.imaginary, True), Literal(Q.zero, True))),
|
| 154 |
+
frozenset((Literal(Q.infinite, False), Literal(Q.negative_infinite, True))),
|
| 155 |
+
frozenset((Literal(Q.infinite, False), Literal(Q.positive_infinite, True))),
|
| 156 |
+
frozenset((Literal(Q.irrational, False), Literal(Q.negative, True), Literal(Q.rational, False))),
|
| 157 |
+
frozenset((Literal(Q.irrational, False), Literal(Q.positive, True), Literal(Q.rational, False))),
|
| 158 |
+
frozenset((Literal(Q.irrational, False), Literal(Q.rational, False), Literal(Q.zero, True))),
|
| 159 |
+
frozenset((Literal(Q.irrational, True), Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.zero, False))),
|
| 160 |
+
frozenset((Literal(Q.irrational, True), Literal(Q.rational, True))),
|
| 161 |
+
frozenset((Literal(Q.negative, False), Literal(Q.positive, False), Literal(Q.rational, True), Literal(Q.zero, False))),
|
| 162 |
+
frozenset((Literal(Q.negative, True), Literal(Q.negative_infinite, True))),
|
| 163 |
+
frozenset((Literal(Q.negative, True), Literal(Q.positive, True))),
|
| 164 |
+
frozenset((Literal(Q.negative, True), Literal(Q.positive_infinite, True))),
|
| 165 |
+
frozenset((Literal(Q.negative, True), Literal(Q.zero, True))),
|
| 166 |
+
frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive, True))),
|
| 167 |
+
frozenset((Literal(Q.negative_infinite, True), Literal(Q.positive_infinite, True))),
|
| 168 |
+
frozenset((Literal(Q.negative_infinite, True), Literal(Q.zero, True))),
|
| 169 |
+
frozenset((Literal(Q.odd, True), Literal(Q.rational, False))),
|
| 170 |
+
frozenset((Literal(Q.positive, False), Literal(Q.prime, True))),
|
| 171 |
+
frozenset((Literal(Q.positive, True), Literal(Q.positive_infinite, True))),
|
| 172 |
+
frozenset((Literal(Q.positive, True), Literal(Q.zero, True))),
|
| 173 |
+
frozenset((Literal(Q.positive_infinite, True), Literal(Q.zero, True)))
|
| 174 |
+
}
|
| 175 |
+
|
| 176 |
+
@cacheit
|
| 177 |
+
def get_known_facts_dict():
|
| 178 |
+
"""
|
| 179 |
+
Logical relations between unary predicates as dictionary.
|
| 180 |
+
|
| 181 |
+
Each key is a predicate, and item is two groups of predicates.
|
| 182 |
+
First group contains the predicates which are implied by the key, and
|
| 183 |
+
second group contains the predicates which are rejected by the key.
|
| 184 |
+
|
| 185 |
+
"""
|
| 186 |
+
return {
|
| 187 |
+
Q.algebraic: (set([Q.algebraic, Q.commutative, Q.complex, Q.finite]),
|
| 188 |
+
set([Q.infinite, Q.negative_infinite, Q.positive_infinite,
|
| 189 |
+
Q.transcendental])),
|
| 190 |
+
Q.antihermitian: (set([Q.antihermitian]), set([])),
|
| 191 |
+
Q.commutative: (set([Q.commutative]), set([])),
|
| 192 |
+
Q.complex: (set([Q.commutative, Q.complex, Q.finite]),
|
| 193 |
+
set([Q.infinite, Q.negative_infinite, Q.positive_infinite])),
|
| 194 |
+
Q.complex_elements: (set([Q.complex_elements]), set([])),
|
| 195 |
+
Q.composite: (set([Q.algebraic, Q.commutative, Q.complex, Q.composite,
|
| 196 |
+
Q.extended_nonnegative, Q.extended_nonzero,
|
| 197 |
+
Q.extended_positive, Q.extended_real, Q.finite, Q.hermitian,
|
| 198 |
+
Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.rational,
|
| 199 |
+
Q.real]), set([Q.extended_negative, Q.extended_nonpositive,
|
| 200 |
+
Q.imaginary, Q.infinite, Q.irrational, Q.negative,
|
| 201 |
+
Q.negative_infinite, Q.nonpositive, Q.positive_infinite,
|
| 202 |
+
Q.prime, Q.transcendental, Q.zero])),
|
| 203 |
+
Q.diagonal: (set([Q.diagonal, Q.lower_triangular, Q.normal, Q.square,
|
| 204 |
+
Q.symmetric, Q.triangular, Q.upper_triangular]), set([])),
|
| 205 |
+
Q.even: (set([Q.algebraic, Q.commutative, Q.complex, Q.even,
|
| 206 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.integer, Q.rational,
|
| 207 |
+
Q.real]), set([Q.imaginary, Q.infinite, Q.irrational,
|
| 208 |
+
Q.negative_infinite, Q.odd, Q.positive_infinite,
|
| 209 |
+
Q.transcendental])),
|
| 210 |
+
Q.extended_negative: (set([Q.commutative, Q.extended_negative,
|
| 211 |
+
Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real]),
|
| 212 |
+
set([Q.composite, Q.extended_nonnegative, Q.extended_positive,
|
| 213 |
+
Q.imaginary, Q.nonnegative, Q.positive, Q.positive_infinite,
|
| 214 |
+
Q.prime, Q.zero])),
|
| 215 |
+
Q.extended_nonnegative: (set([Q.commutative, Q.extended_nonnegative,
|
| 216 |
+
Q.extended_real]), set([Q.extended_negative, Q.imaginary,
|
| 217 |
+
Q.negative, Q.negative_infinite])),
|
| 218 |
+
Q.extended_nonpositive: (set([Q.commutative, Q.extended_nonpositive,
|
| 219 |
+
Q.extended_real]), set([Q.composite, Q.extended_positive,
|
| 220 |
+
Q.imaginary, Q.positive, Q.positive_infinite, Q.prime])),
|
| 221 |
+
Q.extended_nonzero: (set([Q.commutative, Q.extended_nonzero,
|
| 222 |
+
Q.extended_real]), set([Q.imaginary, Q.zero])),
|
| 223 |
+
Q.extended_positive: (set([Q.commutative, Q.extended_nonnegative,
|
| 224 |
+
Q.extended_nonzero, Q.extended_positive, Q.extended_real]),
|
| 225 |
+
set([Q.extended_negative, Q.extended_nonpositive, Q.imaginary,
|
| 226 |
+
Q.negative, Q.negative_infinite, Q.nonpositive, Q.zero])),
|
| 227 |
+
Q.extended_real: (set([Q.commutative, Q.extended_real]),
|
| 228 |
+
set([Q.imaginary])),
|
| 229 |
+
Q.finite: (set([Q.commutative, Q.finite]), set([Q.infinite,
|
| 230 |
+
Q.negative_infinite, Q.positive_infinite])),
|
| 231 |
+
Q.fullrank: (set([Q.fullrank]), set([])),
|
| 232 |
+
Q.hermitian: (set([Q.hermitian]), set([])),
|
| 233 |
+
Q.imaginary: (set([Q.antihermitian, Q.commutative, Q.complex,
|
| 234 |
+
Q.finite, Q.imaginary]), set([Q.composite, Q.even,
|
| 235 |
+
Q.extended_negative, Q.extended_nonnegative,
|
| 236 |
+
Q.extended_nonpositive, Q.extended_nonzero,
|
| 237 |
+
Q.extended_positive, Q.extended_real, Q.infinite, Q.integer,
|
| 238 |
+
Q.irrational, Q.negative, Q.negative_infinite, Q.nonnegative,
|
| 239 |
+
Q.nonpositive, Q.nonzero, Q.odd, Q.positive,
|
| 240 |
+
Q.positive_infinite, Q.prime, Q.rational, Q.real, Q.zero])),
|
| 241 |
+
Q.infinite: (set([Q.commutative, Q.infinite]), set([Q.algebraic,
|
| 242 |
+
Q.complex, Q.composite, Q.even, Q.finite, Q.imaginary,
|
| 243 |
+
Q.integer, Q.irrational, Q.negative, Q.nonnegative,
|
| 244 |
+
Q.nonpositive, Q.nonzero, Q.odd, Q.positive, Q.prime,
|
| 245 |
+
Q.rational, Q.real, Q.transcendental, Q.zero])),
|
| 246 |
+
Q.integer: (set([Q.algebraic, Q.commutative, Q.complex,
|
| 247 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.integer, Q.rational,
|
| 248 |
+
Q.real]), set([Q.imaginary, Q.infinite, Q.irrational,
|
| 249 |
+
Q.negative_infinite, Q.positive_infinite, Q.transcendental])),
|
| 250 |
+
Q.integer_elements: (set([Q.complex_elements, Q.integer_elements,
|
| 251 |
+
Q.real_elements]), set([])),
|
| 252 |
+
Q.invertible: (set([Q.fullrank, Q.invertible, Q.square]),
|
| 253 |
+
set([Q.singular])),
|
| 254 |
+
Q.irrational: (set([Q.commutative, Q.complex, Q.extended_nonzero,
|
| 255 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.irrational,
|
| 256 |
+
Q.nonzero, Q.real]), set([Q.composite, Q.even, Q.imaginary,
|
| 257 |
+
Q.infinite, Q.integer, Q.negative_infinite, Q.odd,
|
| 258 |
+
Q.positive_infinite, Q.prime, Q.rational, Q.zero])),
|
| 259 |
+
Q.is_true: (set([Q.is_true]), set([])),
|
| 260 |
+
Q.lower_triangular: (set([Q.lower_triangular, Q.triangular]), set([])),
|
| 261 |
+
Q.negative: (set([Q.commutative, Q.complex, Q.extended_negative,
|
| 262 |
+
Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real,
|
| 263 |
+
Q.finite, Q.hermitian, Q.negative, Q.nonpositive, Q.nonzero,
|
| 264 |
+
Q.real]), set([Q.composite, Q.extended_nonnegative,
|
| 265 |
+
Q.extended_positive, Q.imaginary, Q.infinite,
|
| 266 |
+
Q.negative_infinite, Q.nonnegative, Q.positive,
|
| 267 |
+
Q.positive_infinite, Q.prime, Q.zero])),
|
| 268 |
+
Q.negative_infinite: (set([Q.commutative, Q.extended_negative,
|
| 269 |
+
Q.extended_nonpositive, Q.extended_nonzero, Q.extended_real,
|
| 270 |
+
Q.infinite, Q.negative_infinite]), set([Q.algebraic,
|
| 271 |
+
Q.complex, Q.composite, Q.even, Q.extended_nonnegative,
|
| 272 |
+
Q.extended_positive, Q.finite, Q.imaginary, Q.integer,
|
| 273 |
+
Q.irrational, Q.negative, Q.nonnegative, Q.nonpositive,
|
| 274 |
+
Q.nonzero, Q.odd, Q.positive, Q.positive_infinite, Q.prime,
|
| 275 |
+
Q.rational, Q.real, Q.transcendental, Q.zero])),
|
| 276 |
+
Q.noninteger: (set([Q.noninteger]), set([])),
|
| 277 |
+
Q.nonnegative: (set([Q.commutative, Q.complex, Q.extended_nonnegative,
|
| 278 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.nonnegative,
|
| 279 |
+
Q.real]), set([Q.extended_negative, Q.imaginary, Q.infinite,
|
| 280 |
+
Q.negative, Q.negative_infinite, Q.positive_infinite])),
|
| 281 |
+
Q.nonpositive: (set([Q.commutative, Q.complex, Q.extended_nonpositive,
|
| 282 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.nonpositive,
|
| 283 |
+
Q.real]), set([Q.composite, Q.extended_positive, Q.imaginary,
|
| 284 |
+
Q.infinite, Q.negative_infinite, Q.positive,
|
| 285 |
+
Q.positive_infinite, Q.prime])),
|
| 286 |
+
Q.nonzero: (set([Q.commutative, Q.complex, Q.extended_nonzero,
|
| 287 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.nonzero, Q.real]),
|
| 288 |
+
set([Q.imaginary, Q.infinite, Q.negative_infinite,
|
| 289 |
+
Q.positive_infinite, Q.zero])),
|
| 290 |
+
Q.normal: (set([Q.normal, Q.square]), set([])),
|
| 291 |
+
Q.odd: (set([Q.algebraic, Q.commutative, Q.complex,
|
| 292 |
+
Q.extended_nonzero, Q.extended_real, Q.finite, Q.hermitian,
|
| 293 |
+
Q.integer, Q.nonzero, Q.odd, Q.rational, Q.real]),
|
| 294 |
+
set([Q.even, Q.imaginary, Q.infinite, Q.irrational,
|
| 295 |
+
Q.negative_infinite, Q.positive_infinite, Q.transcendental,
|
| 296 |
+
Q.zero])),
|
| 297 |
+
Q.orthogonal: (set([Q.fullrank, Q.invertible, Q.normal, Q.orthogonal,
|
| 298 |
+
Q.positive_definite, Q.square, Q.unitary]), set([Q.singular])),
|
| 299 |
+
Q.positive: (set([Q.commutative, Q.complex, Q.extended_nonnegative,
|
| 300 |
+
Q.extended_nonzero, Q.extended_positive, Q.extended_real,
|
| 301 |
+
Q.finite, Q.hermitian, Q.nonnegative, Q.nonzero, Q.positive,
|
| 302 |
+
Q.real]), set([Q.extended_negative, Q.extended_nonpositive,
|
| 303 |
+
Q.imaginary, Q.infinite, Q.negative, Q.negative_infinite,
|
| 304 |
+
Q.nonpositive, Q.positive_infinite, Q.zero])),
|
| 305 |
+
Q.positive_definite: (set([Q.fullrank, Q.invertible,
|
| 306 |
+
Q.positive_definite, Q.square]), set([Q.singular])),
|
| 307 |
+
Q.positive_infinite: (set([Q.commutative, Q.extended_nonnegative,
|
| 308 |
+
Q.extended_nonzero, Q.extended_positive, Q.extended_real,
|
| 309 |
+
Q.infinite, Q.positive_infinite]), set([Q.algebraic,
|
| 310 |
+
Q.complex, Q.composite, Q.even, Q.extended_negative,
|
| 311 |
+
Q.extended_nonpositive, Q.finite, Q.imaginary, Q.integer,
|
| 312 |
+
Q.irrational, Q.negative, Q.negative_infinite, Q.nonnegative,
|
| 313 |
+
Q.nonpositive, Q.nonzero, Q.odd, Q.positive, Q.prime,
|
| 314 |
+
Q.rational, Q.real, Q.transcendental, Q.zero])),
|
| 315 |
+
Q.prime: (set([Q.algebraic, Q.commutative, Q.complex,
|
| 316 |
+
Q.extended_nonnegative, Q.extended_nonzero,
|
| 317 |
+
Q.extended_positive, Q.extended_real, Q.finite, Q.hermitian,
|
| 318 |
+
Q.integer, Q.nonnegative, Q.nonzero, Q.positive, Q.prime,
|
| 319 |
+
Q.rational, Q.real]), set([Q.composite, Q.extended_negative,
|
| 320 |
+
Q.extended_nonpositive, Q.imaginary, Q.infinite, Q.irrational,
|
| 321 |
+
Q.negative, Q.negative_infinite, Q.nonpositive,
|
| 322 |
+
Q.positive_infinite, Q.transcendental, Q.zero])),
|
| 323 |
+
Q.rational: (set([Q.algebraic, Q.commutative, Q.complex,
|
| 324 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.rational, Q.real]),
|
| 325 |
+
set([Q.imaginary, Q.infinite, Q.irrational,
|
| 326 |
+
Q.negative_infinite, Q.positive_infinite, Q.transcendental])),
|
| 327 |
+
Q.real: (set([Q.commutative, Q.complex, Q.extended_real, Q.finite,
|
| 328 |
+
Q.hermitian, Q.real]), set([Q.imaginary, Q.infinite,
|
| 329 |
+
Q.negative_infinite, Q.positive_infinite])),
|
| 330 |
+
Q.real_elements: (set([Q.complex_elements, Q.real_elements]), set([])),
|
| 331 |
+
Q.singular: (set([Q.singular]), set([Q.invertible, Q.orthogonal,
|
| 332 |
+
Q.positive_definite, Q.unitary])),
|
| 333 |
+
Q.square: (set([Q.square]), set([])),
|
| 334 |
+
Q.symmetric: (set([Q.square, Q.symmetric]), set([])),
|
| 335 |
+
Q.transcendental: (set([Q.commutative, Q.complex, Q.finite,
|
| 336 |
+
Q.transcendental]), set([Q.algebraic, Q.composite, Q.even,
|
| 337 |
+
Q.infinite, Q.integer, Q.negative_infinite, Q.odd,
|
| 338 |
+
Q.positive_infinite, Q.prime, Q.rational, Q.zero])),
|
| 339 |
+
Q.triangular: (set([Q.triangular]), set([])),
|
| 340 |
+
Q.unit_triangular: (set([Q.triangular, Q.unit_triangular]), set([])),
|
| 341 |
+
Q.unitary: (set([Q.fullrank, Q.invertible, Q.normal, Q.square,
|
| 342 |
+
Q.unitary]), set([Q.singular])),
|
| 343 |
+
Q.upper_triangular: (set([Q.triangular, Q.upper_triangular]), set([])),
|
| 344 |
+
Q.zero: (set([Q.algebraic, Q.commutative, Q.complex, Q.even,
|
| 345 |
+
Q.extended_nonnegative, Q.extended_nonpositive,
|
| 346 |
+
Q.extended_real, Q.finite, Q.hermitian, Q.integer,
|
| 347 |
+
Q.nonnegative, Q.nonpositive, Q.rational, Q.real, Q.zero]),
|
| 348 |
+
set([Q.composite, Q.extended_negative, Q.extended_nonzero,
|
| 349 |
+
Q.extended_positive, Q.imaginary, Q.infinite, Q.irrational,
|
| 350 |
+
Q.negative, Q.negative_infinite, Q.nonzero, Q.odd, Q.positive,
|
| 351 |
+
Q.positive_infinite, Q.prime, Q.transcendental])),
|
| 352 |
+
}
|
lib/python3.12/site-packages/sympy/assumptions/assume.py
ADDED
|
@@ -0,0 +1,485 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""A module which implements predicates and assumption context."""
|
| 2 |
+
|
| 3 |
+
from contextlib import contextmanager
|
| 4 |
+
import inspect
|
| 5 |
+
from sympy.core.symbol import Str
|
| 6 |
+
from sympy.core.sympify import _sympify
|
| 7 |
+
from sympy.logic.boolalg import Boolean, false, true
|
| 8 |
+
from sympy.multipledispatch.dispatcher import Dispatcher, str_signature
|
| 9 |
+
from sympy.utilities.exceptions import sympy_deprecation_warning
|
| 10 |
+
from sympy.utilities.iterables import is_sequence
|
| 11 |
+
from sympy.utilities.source import get_class
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
class AssumptionsContext(set):
|
| 15 |
+
"""
|
| 16 |
+
Set containing default assumptions which are applied to the ``ask()``
|
| 17 |
+
function.
|
| 18 |
+
|
| 19 |
+
Explanation
|
| 20 |
+
===========
|
| 21 |
+
|
| 22 |
+
This is used to represent global assumptions, but you can also use this
|
| 23 |
+
class to create your own local assumptions contexts. It is basically a thin
|
| 24 |
+
wrapper to Python's set, so see its documentation for advanced usage.
|
| 25 |
+
|
| 26 |
+
Examples
|
| 27 |
+
========
|
| 28 |
+
|
| 29 |
+
The default assumption context is ``global_assumptions``, which is initially empty:
|
| 30 |
+
|
| 31 |
+
>>> from sympy import ask, Q
|
| 32 |
+
>>> from sympy.assumptions import global_assumptions
|
| 33 |
+
>>> global_assumptions
|
| 34 |
+
AssumptionsContext()
|
| 35 |
+
|
| 36 |
+
You can add default assumptions:
|
| 37 |
+
|
| 38 |
+
>>> from sympy.abc import x
|
| 39 |
+
>>> global_assumptions.add(Q.real(x))
|
| 40 |
+
>>> global_assumptions
|
| 41 |
+
AssumptionsContext({Q.real(x)})
|
| 42 |
+
>>> ask(Q.real(x))
|
| 43 |
+
True
|
| 44 |
+
|
| 45 |
+
And remove them:
|
| 46 |
+
|
| 47 |
+
>>> global_assumptions.remove(Q.real(x))
|
| 48 |
+
>>> print(ask(Q.real(x)))
|
| 49 |
+
None
|
| 50 |
+
|
| 51 |
+
The ``clear()`` method removes every assumption:
|
| 52 |
+
|
| 53 |
+
>>> global_assumptions.add(Q.positive(x))
|
| 54 |
+
>>> global_assumptions
|
| 55 |
+
AssumptionsContext({Q.positive(x)})
|
| 56 |
+
>>> global_assumptions.clear()
|
| 57 |
+
>>> global_assumptions
|
| 58 |
+
AssumptionsContext()
|
| 59 |
+
|
| 60 |
+
See Also
|
| 61 |
+
========
|
| 62 |
+
|
| 63 |
+
assuming
|
| 64 |
+
|
| 65 |
+
"""
|
| 66 |
+
|
| 67 |
+
def add(self, *assumptions):
|
| 68 |
+
"""Add assumptions."""
|
| 69 |
+
for a in assumptions:
|
| 70 |
+
super().add(a)
|
| 71 |
+
|
| 72 |
+
def _sympystr(self, printer):
|
| 73 |
+
if not self:
|
| 74 |
+
return "%s()" % self.__class__.__name__
|
| 75 |
+
return "{}({})".format(self.__class__.__name__, printer._print_set(self))
|
| 76 |
+
|
| 77 |
+
global_assumptions = AssumptionsContext()
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
class AppliedPredicate(Boolean):
|
| 81 |
+
"""
|
| 82 |
+
The class of expressions resulting from applying ``Predicate`` to
|
| 83 |
+
the arguments. ``AppliedPredicate`` merely wraps its argument and
|
| 84 |
+
remain unevaluated. To evaluate it, use the ``ask()`` function.
|
| 85 |
+
|
| 86 |
+
Examples
|
| 87 |
+
========
|
| 88 |
+
|
| 89 |
+
>>> from sympy import Q, ask
|
| 90 |
+
>>> Q.integer(1)
|
| 91 |
+
Q.integer(1)
|
| 92 |
+
|
| 93 |
+
The ``function`` attribute returns the predicate, and the ``arguments``
|
| 94 |
+
attribute returns the tuple of arguments.
|
| 95 |
+
|
| 96 |
+
>>> type(Q.integer(1))
|
| 97 |
+
<class 'sympy.assumptions.assume.AppliedPredicate'>
|
| 98 |
+
>>> Q.integer(1).function
|
| 99 |
+
Q.integer
|
| 100 |
+
>>> Q.integer(1).arguments
|
| 101 |
+
(1,)
|
| 102 |
+
|
| 103 |
+
Applied predicates can be evaluated to a boolean value with ``ask``:
|
| 104 |
+
|
| 105 |
+
>>> ask(Q.integer(1))
|
| 106 |
+
True
|
| 107 |
+
|
| 108 |
+
"""
|
| 109 |
+
__slots__ = ()
|
| 110 |
+
|
| 111 |
+
def __new__(cls, predicate, *args):
|
| 112 |
+
if not isinstance(predicate, Predicate):
|
| 113 |
+
raise TypeError("%s is not a Predicate." % predicate)
|
| 114 |
+
args = map(_sympify, args)
|
| 115 |
+
return super().__new__(cls, predicate, *args)
|
| 116 |
+
|
| 117 |
+
@property
|
| 118 |
+
def arg(self):
|
| 119 |
+
"""
|
| 120 |
+
Return the expression used by this assumption.
|
| 121 |
+
|
| 122 |
+
Examples
|
| 123 |
+
========
|
| 124 |
+
|
| 125 |
+
>>> from sympy import Q, Symbol
|
| 126 |
+
>>> x = Symbol('x')
|
| 127 |
+
>>> a = Q.integer(x + 1)
|
| 128 |
+
>>> a.arg
|
| 129 |
+
x + 1
|
| 130 |
+
|
| 131 |
+
"""
|
| 132 |
+
# Will be deprecated
|
| 133 |
+
args = self._args
|
| 134 |
+
if len(args) == 2:
|
| 135 |
+
# backwards compatibility
|
| 136 |
+
return args[1]
|
| 137 |
+
raise TypeError("'arg' property is allowed only for unary predicates.")
|
| 138 |
+
|
| 139 |
+
@property
|
| 140 |
+
def function(self):
|
| 141 |
+
"""
|
| 142 |
+
Return the predicate.
|
| 143 |
+
"""
|
| 144 |
+
# Will be changed to self.args[0] after args overriding is removed
|
| 145 |
+
return self._args[0]
|
| 146 |
+
|
| 147 |
+
@property
|
| 148 |
+
def arguments(self):
|
| 149 |
+
"""
|
| 150 |
+
Return the arguments which are applied to the predicate.
|
| 151 |
+
"""
|
| 152 |
+
# Will be changed to self.args[1:] after args overriding is removed
|
| 153 |
+
return self._args[1:]
|
| 154 |
+
|
| 155 |
+
def _eval_ask(self, assumptions):
|
| 156 |
+
return self.function.eval(self.arguments, assumptions)
|
| 157 |
+
|
| 158 |
+
@property
|
| 159 |
+
def binary_symbols(self):
|
| 160 |
+
from .ask import Q
|
| 161 |
+
if self.function == Q.is_true:
|
| 162 |
+
i = self.arguments[0]
|
| 163 |
+
if i.is_Boolean or i.is_Symbol:
|
| 164 |
+
return i.binary_symbols
|
| 165 |
+
if self.function in (Q.eq, Q.ne):
|
| 166 |
+
if true in self.arguments or false in self.arguments:
|
| 167 |
+
if self.arguments[0].is_Symbol:
|
| 168 |
+
return {self.arguments[0]}
|
| 169 |
+
elif self.arguments[1].is_Symbol:
|
| 170 |
+
return {self.arguments[1]}
|
| 171 |
+
return set()
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
class PredicateMeta(type):
|
| 175 |
+
def __new__(cls, clsname, bases, dct):
|
| 176 |
+
# If handler is not defined, assign empty dispatcher.
|
| 177 |
+
if "handler" not in dct:
|
| 178 |
+
name = f"Ask{clsname.capitalize()}Handler"
|
| 179 |
+
handler = Dispatcher(name, doc="Handler for key %s" % name)
|
| 180 |
+
dct["handler"] = handler
|
| 181 |
+
|
| 182 |
+
dct["_orig_doc"] = dct.get("__doc__", "")
|
| 183 |
+
|
| 184 |
+
return super().__new__(cls, clsname, bases, dct)
|
| 185 |
+
|
| 186 |
+
@property
|
| 187 |
+
def __doc__(cls):
|
| 188 |
+
handler = cls.handler
|
| 189 |
+
doc = cls._orig_doc
|
| 190 |
+
if cls is not Predicate and handler is not None:
|
| 191 |
+
doc += "Handler\n"
|
| 192 |
+
doc += " =======\n\n"
|
| 193 |
+
|
| 194 |
+
# Append the handler's doc without breaking sphinx documentation.
|
| 195 |
+
docs = [" Multiply dispatched method: %s" % handler.name]
|
| 196 |
+
if handler.doc:
|
| 197 |
+
for line in handler.doc.splitlines():
|
| 198 |
+
if not line:
|
| 199 |
+
continue
|
| 200 |
+
docs.append(" %s" % line)
|
| 201 |
+
other = []
|
| 202 |
+
for sig in handler.ordering[::-1]:
|
| 203 |
+
func = handler.funcs[sig]
|
| 204 |
+
if func.__doc__:
|
| 205 |
+
s = ' Inputs: <%s>' % str_signature(sig)
|
| 206 |
+
lines = []
|
| 207 |
+
for line in func.__doc__.splitlines():
|
| 208 |
+
lines.append(" %s" % line)
|
| 209 |
+
s += "\n".join(lines)
|
| 210 |
+
docs.append(s)
|
| 211 |
+
else:
|
| 212 |
+
other.append(str_signature(sig))
|
| 213 |
+
if other:
|
| 214 |
+
othersig = " Other signatures:"
|
| 215 |
+
for line in other:
|
| 216 |
+
othersig += "\n * %s" % line
|
| 217 |
+
docs.append(othersig)
|
| 218 |
+
|
| 219 |
+
doc += '\n\n'.join(docs)
|
| 220 |
+
|
| 221 |
+
return doc
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
class Predicate(Boolean, metaclass=PredicateMeta):
|
| 225 |
+
"""
|
| 226 |
+
Base class for mathematical predicates. It also serves as a
|
| 227 |
+
constructor for undefined predicate objects.
|
| 228 |
+
|
| 229 |
+
Explanation
|
| 230 |
+
===========
|
| 231 |
+
|
| 232 |
+
Predicate is a function that returns a boolean value [1].
|
| 233 |
+
|
| 234 |
+
Predicate function is object, and it is instance of predicate class.
|
| 235 |
+
When a predicate is applied to arguments, ``AppliedPredicate``
|
| 236 |
+
instance is returned. This merely wraps the argument and remain
|
| 237 |
+
unevaluated. To obtain the truth value of applied predicate, use the
|
| 238 |
+
function ``ask``.
|
| 239 |
+
|
| 240 |
+
Evaluation of predicate is done by multiple dispatching. You can
|
| 241 |
+
register new handler to the predicate to support new types.
|
| 242 |
+
|
| 243 |
+
Every predicate in SymPy can be accessed via the property of ``Q``.
|
| 244 |
+
For example, ``Q.even`` returns the predicate which checks if the
|
| 245 |
+
argument is even number.
|
| 246 |
+
|
| 247 |
+
To define a predicate which can be evaluated, you must subclass this
|
| 248 |
+
class, make an instance of it, and register it to ``Q``. After then,
|
| 249 |
+
dispatch the handler by argument types.
|
| 250 |
+
|
| 251 |
+
If you directly construct predicate using this class, you will get
|
| 252 |
+
``UndefinedPredicate`` which cannot be dispatched. This is useful
|
| 253 |
+
when you are building boolean expressions which do not need to be
|
| 254 |
+
evaluated.
|
| 255 |
+
|
| 256 |
+
Examples
|
| 257 |
+
========
|
| 258 |
+
|
| 259 |
+
Applying and evaluating to boolean value:
|
| 260 |
+
|
| 261 |
+
>>> from sympy import Q, ask
|
| 262 |
+
>>> ask(Q.prime(7))
|
| 263 |
+
True
|
| 264 |
+
|
| 265 |
+
You can define a new predicate by subclassing and dispatching. Here,
|
| 266 |
+
we define a predicate for sexy primes [2] as an example.
|
| 267 |
+
|
| 268 |
+
>>> from sympy import Predicate, Integer
|
| 269 |
+
>>> class SexyPrimePredicate(Predicate):
|
| 270 |
+
... name = "sexyprime"
|
| 271 |
+
>>> Q.sexyprime = SexyPrimePredicate()
|
| 272 |
+
>>> @Q.sexyprime.register(Integer, Integer)
|
| 273 |
+
... def _(int1, int2, assumptions):
|
| 274 |
+
... args = sorted([int1, int2])
|
| 275 |
+
... if not all(ask(Q.prime(a), assumptions) for a in args):
|
| 276 |
+
... return False
|
| 277 |
+
... return args[1] - args[0] == 6
|
| 278 |
+
>>> ask(Q.sexyprime(5, 11))
|
| 279 |
+
True
|
| 280 |
+
|
| 281 |
+
Direct constructing returns ``UndefinedPredicate``, which can be
|
| 282 |
+
applied but cannot be dispatched.
|
| 283 |
+
|
| 284 |
+
>>> from sympy import Predicate, Integer
|
| 285 |
+
>>> Q.P = Predicate("P")
|
| 286 |
+
>>> type(Q.P)
|
| 287 |
+
<class 'sympy.assumptions.assume.UndefinedPredicate'>
|
| 288 |
+
>>> Q.P(1)
|
| 289 |
+
Q.P(1)
|
| 290 |
+
>>> Q.P.register(Integer)(lambda expr, assump: True)
|
| 291 |
+
Traceback (most recent call last):
|
| 292 |
+
...
|
| 293 |
+
TypeError: <class 'sympy.assumptions.assume.UndefinedPredicate'> cannot be dispatched.
|
| 294 |
+
|
| 295 |
+
References
|
| 296 |
+
==========
|
| 297 |
+
|
| 298 |
+
.. [1] https://en.wikipedia.org/wiki/Predicate_%28mathematical_logic%29
|
| 299 |
+
.. [2] https://en.wikipedia.org/wiki/Sexy_prime
|
| 300 |
+
|
| 301 |
+
"""
|
| 302 |
+
|
| 303 |
+
is_Atom = True
|
| 304 |
+
|
| 305 |
+
def __new__(cls, *args, **kwargs):
|
| 306 |
+
if cls is Predicate:
|
| 307 |
+
return UndefinedPredicate(*args, **kwargs)
|
| 308 |
+
obj = super().__new__(cls, *args)
|
| 309 |
+
return obj
|
| 310 |
+
|
| 311 |
+
@property
|
| 312 |
+
def name(self):
|
| 313 |
+
# May be overridden
|
| 314 |
+
return type(self).__name__
|
| 315 |
+
|
| 316 |
+
@classmethod
|
| 317 |
+
def register(cls, *types, **kwargs):
|
| 318 |
+
"""
|
| 319 |
+
Register the signature to the handler.
|
| 320 |
+
"""
|
| 321 |
+
if cls.handler is None:
|
| 322 |
+
raise TypeError("%s cannot be dispatched." % type(cls))
|
| 323 |
+
return cls.handler.register(*types, **kwargs)
|
| 324 |
+
|
| 325 |
+
@classmethod
|
| 326 |
+
def register_many(cls, *types, **kwargs):
|
| 327 |
+
"""
|
| 328 |
+
Register multiple signatures to same handler.
|
| 329 |
+
"""
|
| 330 |
+
def _(func):
|
| 331 |
+
for t in types:
|
| 332 |
+
if not is_sequence(t):
|
| 333 |
+
t = (t,) # for convenience, allow passing `type` to mean `(type,)`
|
| 334 |
+
cls.register(*t, **kwargs)(func)
|
| 335 |
+
return _
|
| 336 |
+
|
| 337 |
+
def __call__(self, *args):
|
| 338 |
+
return AppliedPredicate(self, *args)
|
| 339 |
+
|
| 340 |
+
def eval(self, args, assumptions=True):
|
| 341 |
+
"""
|
| 342 |
+
Evaluate ``self(*args)`` under the given assumptions.
|
| 343 |
+
|
| 344 |
+
This uses only direct resolution methods, not logical inference.
|
| 345 |
+
"""
|
| 346 |
+
result = None
|
| 347 |
+
try:
|
| 348 |
+
result = self.handler(*args, assumptions=assumptions)
|
| 349 |
+
except NotImplementedError:
|
| 350 |
+
pass
|
| 351 |
+
return result
|
| 352 |
+
|
| 353 |
+
def _eval_refine(self, assumptions):
|
| 354 |
+
# When Predicate is no longer Boolean, delete this method
|
| 355 |
+
return self
|
| 356 |
+
|
| 357 |
+
|
| 358 |
+
class UndefinedPredicate(Predicate):
|
| 359 |
+
"""
|
| 360 |
+
Predicate without handler.
|
| 361 |
+
|
| 362 |
+
Explanation
|
| 363 |
+
===========
|
| 364 |
+
|
| 365 |
+
This predicate is generated by using ``Predicate`` directly for
|
| 366 |
+
construction. It does not have a handler, and evaluating this with
|
| 367 |
+
arguments is done by SAT solver.
|
| 368 |
+
|
| 369 |
+
Examples
|
| 370 |
+
========
|
| 371 |
+
|
| 372 |
+
>>> from sympy import Predicate, Q
|
| 373 |
+
>>> Q.P = Predicate('P')
|
| 374 |
+
>>> Q.P.func
|
| 375 |
+
<class 'sympy.assumptions.assume.UndefinedPredicate'>
|
| 376 |
+
>>> Q.P.name
|
| 377 |
+
Str('P')
|
| 378 |
+
|
| 379 |
+
"""
|
| 380 |
+
|
| 381 |
+
handler = None
|
| 382 |
+
|
| 383 |
+
def __new__(cls, name, handlers=None):
|
| 384 |
+
# "handlers" parameter supports old design
|
| 385 |
+
if not isinstance(name, Str):
|
| 386 |
+
name = Str(name)
|
| 387 |
+
obj = super(Boolean, cls).__new__(cls, name)
|
| 388 |
+
obj.handlers = handlers or []
|
| 389 |
+
return obj
|
| 390 |
+
|
| 391 |
+
@property
|
| 392 |
+
def name(self):
|
| 393 |
+
return self.args[0]
|
| 394 |
+
|
| 395 |
+
def _hashable_content(self):
|
| 396 |
+
return (self.name,)
|
| 397 |
+
|
| 398 |
+
def __getnewargs__(self):
|
| 399 |
+
return (self.name,)
|
| 400 |
+
|
| 401 |
+
def __call__(self, expr):
|
| 402 |
+
return AppliedPredicate(self, expr)
|
| 403 |
+
|
| 404 |
+
def add_handler(self, handler):
|
| 405 |
+
sympy_deprecation_warning(
|
| 406 |
+
"""
|
| 407 |
+
The AskHandler system is deprecated. Predicate.add_handler()
|
| 408 |
+
should be replaced with the multipledispatch handler of Predicate.
|
| 409 |
+
""",
|
| 410 |
+
deprecated_since_version="1.8",
|
| 411 |
+
active_deprecations_target='deprecated-askhandler',
|
| 412 |
+
)
|
| 413 |
+
self.handlers.append(handler)
|
| 414 |
+
|
| 415 |
+
def remove_handler(self, handler):
|
| 416 |
+
sympy_deprecation_warning(
|
| 417 |
+
"""
|
| 418 |
+
The AskHandler system is deprecated. Predicate.remove_handler()
|
| 419 |
+
should be replaced with the multipledispatch handler of Predicate.
|
| 420 |
+
""",
|
| 421 |
+
deprecated_since_version="1.8",
|
| 422 |
+
active_deprecations_target='deprecated-askhandler',
|
| 423 |
+
)
|
| 424 |
+
self.handlers.remove(handler)
|
| 425 |
+
|
| 426 |
+
def eval(self, args, assumptions=True):
|
| 427 |
+
# Support for deprecated design
|
| 428 |
+
# When old design is removed, this will always return None
|
| 429 |
+
sympy_deprecation_warning(
|
| 430 |
+
"""
|
| 431 |
+
The AskHandler system is deprecated. Evaluating UndefinedPredicate
|
| 432 |
+
objects should be replaced with the multipledispatch handler of
|
| 433 |
+
Predicate.
|
| 434 |
+
""",
|
| 435 |
+
deprecated_since_version="1.8",
|
| 436 |
+
active_deprecations_target='deprecated-askhandler',
|
| 437 |
+
stacklevel=5,
|
| 438 |
+
)
|
| 439 |
+
expr, = args
|
| 440 |
+
res, _res = None, None
|
| 441 |
+
mro = inspect.getmro(type(expr))
|
| 442 |
+
for handler in self.handlers:
|
| 443 |
+
cls = get_class(handler)
|
| 444 |
+
for subclass in mro:
|
| 445 |
+
eval_ = getattr(cls, subclass.__name__, None)
|
| 446 |
+
if eval_ is None:
|
| 447 |
+
continue
|
| 448 |
+
res = eval_(expr, assumptions)
|
| 449 |
+
# Do not stop if value returned is None
|
| 450 |
+
# Try to check for higher classes
|
| 451 |
+
if res is None:
|
| 452 |
+
continue
|
| 453 |
+
if _res is None:
|
| 454 |
+
_res = res
|
| 455 |
+
else:
|
| 456 |
+
# only check consistency if both resolutors have concluded
|
| 457 |
+
if _res != res:
|
| 458 |
+
raise ValueError('incompatible resolutors')
|
| 459 |
+
break
|
| 460 |
+
return res
|
| 461 |
+
|
| 462 |
+
|
| 463 |
+
@contextmanager
|
| 464 |
+
def assuming(*assumptions):
|
| 465 |
+
"""
|
| 466 |
+
Context manager for assumptions.
|
| 467 |
+
|
| 468 |
+
Examples
|
| 469 |
+
========
|
| 470 |
+
|
| 471 |
+
>>> from sympy import assuming, Q, ask
|
| 472 |
+
>>> from sympy.abc import x, y
|
| 473 |
+
>>> print(ask(Q.integer(x + y)))
|
| 474 |
+
None
|
| 475 |
+
>>> with assuming(Q.integer(x), Q.integer(y)):
|
| 476 |
+
... print(ask(Q.integer(x + y)))
|
| 477 |
+
True
|
| 478 |
+
"""
|
| 479 |
+
old_global_assumptions = global_assumptions.copy()
|
| 480 |
+
global_assumptions.update(assumptions)
|
| 481 |
+
try:
|
| 482 |
+
yield
|
| 483 |
+
finally:
|
| 484 |
+
global_assumptions.clear()
|
| 485 |
+
global_assumptions.update(old_global_assumptions)
|
lib/python3.12/site-packages/sympy/assumptions/cnf.py
ADDED
|
@@ -0,0 +1,445 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
The classes used here are for the internal use of assumptions system
|
| 3 |
+
only and should not be used anywhere else as these do not possess the
|
| 4 |
+
signatures common to SymPy objects. For general use of logic constructs
|
| 5 |
+
please refer to sympy.logic classes And, Or, Not, etc.
|
| 6 |
+
"""
|
| 7 |
+
from itertools import combinations, product, zip_longest
|
| 8 |
+
from sympy.assumptions.assume import AppliedPredicate, Predicate
|
| 9 |
+
from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
|
| 10 |
+
from sympy.core.singleton import S
|
| 11 |
+
from sympy.logic.boolalg import Or, And, Not, Xnor
|
| 12 |
+
from sympy.logic.boolalg import (Equivalent, ITE, Implies, Nand, Nor, Xor)
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
class Literal:
|
| 16 |
+
"""
|
| 17 |
+
The smallest element of a CNF object.
|
| 18 |
+
|
| 19 |
+
Parameters
|
| 20 |
+
==========
|
| 21 |
+
|
| 22 |
+
lit : Boolean expression
|
| 23 |
+
|
| 24 |
+
is_Not : bool
|
| 25 |
+
|
| 26 |
+
Examples
|
| 27 |
+
========
|
| 28 |
+
|
| 29 |
+
>>> from sympy import Q
|
| 30 |
+
>>> from sympy.assumptions.cnf import Literal
|
| 31 |
+
>>> from sympy.abc import x
|
| 32 |
+
>>> Literal(Q.even(x))
|
| 33 |
+
Literal(Q.even(x), False)
|
| 34 |
+
>>> Literal(~Q.even(x))
|
| 35 |
+
Literal(Q.even(x), True)
|
| 36 |
+
"""
|
| 37 |
+
|
| 38 |
+
def __new__(cls, lit, is_Not=False):
|
| 39 |
+
if isinstance(lit, Not):
|
| 40 |
+
lit = lit.args[0]
|
| 41 |
+
is_Not = True
|
| 42 |
+
elif isinstance(lit, (AND, OR, Literal)):
|
| 43 |
+
return ~lit if is_Not else lit
|
| 44 |
+
obj = super().__new__(cls)
|
| 45 |
+
obj.lit = lit
|
| 46 |
+
obj.is_Not = is_Not
|
| 47 |
+
return obj
|
| 48 |
+
|
| 49 |
+
@property
|
| 50 |
+
def arg(self):
|
| 51 |
+
return self.lit
|
| 52 |
+
|
| 53 |
+
def rcall(self, expr):
|
| 54 |
+
if callable(self.lit):
|
| 55 |
+
lit = self.lit(expr)
|
| 56 |
+
else:
|
| 57 |
+
lit = self.lit.apply(expr)
|
| 58 |
+
return type(self)(lit, self.is_Not)
|
| 59 |
+
|
| 60 |
+
def __invert__(self):
|
| 61 |
+
is_Not = not self.is_Not
|
| 62 |
+
return Literal(self.lit, is_Not)
|
| 63 |
+
|
| 64 |
+
def __str__(self):
|
| 65 |
+
return '{}({}, {})'.format(type(self).__name__, self.lit, self.is_Not)
|
| 66 |
+
|
| 67 |
+
__repr__ = __str__
|
| 68 |
+
|
| 69 |
+
def __eq__(self, other):
|
| 70 |
+
return self.arg == other.arg and self.is_Not == other.is_Not
|
| 71 |
+
|
| 72 |
+
def __hash__(self):
|
| 73 |
+
h = hash((type(self).__name__, self.arg, self.is_Not))
|
| 74 |
+
return h
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
class OR:
|
| 78 |
+
"""
|
| 79 |
+
A low-level implementation for Or
|
| 80 |
+
"""
|
| 81 |
+
def __init__(self, *args):
|
| 82 |
+
self._args = args
|
| 83 |
+
|
| 84 |
+
@property
|
| 85 |
+
def args(self):
|
| 86 |
+
return sorted(self._args, key=str)
|
| 87 |
+
|
| 88 |
+
def rcall(self, expr):
|
| 89 |
+
return type(self)(*[arg.rcall(expr)
|
| 90 |
+
for arg in self._args
|
| 91 |
+
])
|
| 92 |
+
|
| 93 |
+
def __invert__(self):
|
| 94 |
+
return AND(*[~arg for arg in self._args])
|
| 95 |
+
|
| 96 |
+
def __hash__(self):
|
| 97 |
+
return hash((type(self).__name__,) + tuple(self.args))
|
| 98 |
+
|
| 99 |
+
def __eq__(self, other):
|
| 100 |
+
return self.args == other.args
|
| 101 |
+
|
| 102 |
+
def __str__(self):
|
| 103 |
+
s = '(' + ' | '.join([str(arg) for arg in self.args]) + ')'
|
| 104 |
+
return s
|
| 105 |
+
|
| 106 |
+
__repr__ = __str__
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
class AND:
|
| 110 |
+
"""
|
| 111 |
+
A low-level implementation for And
|
| 112 |
+
"""
|
| 113 |
+
def __init__(self, *args):
|
| 114 |
+
self._args = args
|
| 115 |
+
|
| 116 |
+
def __invert__(self):
|
| 117 |
+
return OR(*[~arg for arg in self._args])
|
| 118 |
+
|
| 119 |
+
@property
|
| 120 |
+
def args(self):
|
| 121 |
+
return sorted(self._args, key=str)
|
| 122 |
+
|
| 123 |
+
def rcall(self, expr):
|
| 124 |
+
return type(self)(*[arg.rcall(expr)
|
| 125 |
+
for arg in self._args
|
| 126 |
+
])
|
| 127 |
+
|
| 128 |
+
def __hash__(self):
|
| 129 |
+
return hash((type(self).__name__,) + tuple(self.args))
|
| 130 |
+
|
| 131 |
+
def __eq__(self, other):
|
| 132 |
+
return self.args == other.args
|
| 133 |
+
|
| 134 |
+
def __str__(self):
|
| 135 |
+
s = '('+' & '.join([str(arg) for arg in self.args])+')'
|
| 136 |
+
return s
|
| 137 |
+
|
| 138 |
+
__repr__ = __str__
|
| 139 |
+
|
| 140 |
+
|
| 141 |
+
def to_NNF(expr, composite_map=None):
|
| 142 |
+
"""
|
| 143 |
+
Generates the Negation Normal Form of any boolean expression in terms
|
| 144 |
+
of AND, OR, and Literal objects.
|
| 145 |
+
|
| 146 |
+
Examples
|
| 147 |
+
========
|
| 148 |
+
|
| 149 |
+
>>> from sympy import Q, Eq
|
| 150 |
+
>>> from sympy.assumptions.cnf import to_NNF
|
| 151 |
+
>>> from sympy.abc import x, y
|
| 152 |
+
>>> expr = Q.even(x) & ~Q.positive(x)
|
| 153 |
+
>>> to_NNF(expr)
|
| 154 |
+
(Literal(Q.even(x), False) & Literal(Q.positive(x), True))
|
| 155 |
+
|
| 156 |
+
Supported boolean objects are converted to corresponding predicates.
|
| 157 |
+
|
| 158 |
+
>>> to_NNF(Eq(x, y))
|
| 159 |
+
Literal(Q.eq(x, y), False)
|
| 160 |
+
|
| 161 |
+
If ``composite_map`` argument is given, ``to_NNF`` decomposes the
|
| 162 |
+
specified predicate into a combination of primitive predicates.
|
| 163 |
+
|
| 164 |
+
>>> cmap = {Q.nonpositive: Q.negative | Q.zero}
|
| 165 |
+
>>> to_NNF(Q.nonpositive, cmap)
|
| 166 |
+
(Literal(Q.negative, False) | Literal(Q.zero, False))
|
| 167 |
+
>>> to_NNF(Q.nonpositive(x), cmap)
|
| 168 |
+
(Literal(Q.negative(x), False) | Literal(Q.zero(x), False))
|
| 169 |
+
"""
|
| 170 |
+
from sympy.assumptions.ask import Q
|
| 171 |
+
|
| 172 |
+
if composite_map is None:
|
| 173 |
+
composite_map = {}
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
|
| 177 |
+
if type(expr) in binrelpreds:
|
| 178 |
+
pred = binrelpreds[type(expr)]
|
| 179 |
+
expr = pred(*expr.args)
|
| 180 |
+
|
| 181 |
+
if isinstance(expr, Not):
|
| 182 |
+
arg = expr.args[0]
|
| 183 |
+
tmp = to_NNF(arg, composite_map) # Strategy: negate the NNF of expr
|
| 184 |
+
return ~tmp
|
| 185 |
+
|
| 186 |
+
if isinstance(expr, Or):
|
| 187 |
+
return OR(*[to_NNF(x, composite_map) for x in Or.make_args(expr)])
|
| 188 |
+
|
| 189 |
+
if isinstance(expr, And):
|
| 190 |
+
return AND(*[to_NNF(x, composite_map) for x in And.make_args(expr)])
|
| 191 |
+
|
| 192 |
+
if isinstance(expr, Nand):
|
| 193 |
+
tmp = AND(*[to_NNF(x, composite_map) for x in expr.args])
|
| 194 |
+
return ~tmp
|
| 195 |
+
|
| 196 |
+
if isinstance(expr, Nor):
|
| 197 |
+
tmp = OR(*[to_NNF(x, composite_map) for x in expr.args])
|
| 198 |
+
return ~tmp
|
| 199 |
+
|
| 200 |
+
if isinstance(expr, Xor):
|
| 201 |
+
cnfs = []
|
| 202 |
+
for i in range(0, len(expr.args) + 1, 2):
|
| 203 |
+
for neg in combinations(expr.args, i):
|
| 204 |
+
clause = [~to_NNF(s, composite_map) if s in neg else to_NNF(s, composite_map)
|
| 205 |
+
for s in expr.args]
|
| 206 |
+
cnfs.append(OR(*clause))
|
| 207 |
+
return AND(*cnfs)
|
| 208 |
+
|
| 209 |
+
if isinstance(expr, Xnor):
|
| 210 |
+
cnfs = []
|
| 211 |
+
for i in range(0, len(expr.args) + 1, 2):
|
| 212 |
+
for neg in combinations(expr.args, i):
|
| 213 |
+
clause = [~to_NNF(s, composite_map) if s in neg else to_NNF(s, composite_map)
|
| 214 |
+
for s in expr.args]
|
| 215 |
+
cnfs.append(OR(*clause))
|
| 216 |
+
return ~AND(*cnfs)
|
| 217 |
+
|
| 218 |
+
if isinstance(expr, Implies):
|
| 219 |
+
L, R = to_NNF(expr.args[0], composite_map), to_NNF(expr.args[1], composite_map)
|
| 220 |
+
return OR(~L, R)
|
| 221 |
+
|
| 222 |
+
if isinstance(expr, Equivalent):
|
| 223 |
+
cnfs = []
|
| 224 |
+
for a, b in zip_longest(expr.args, expr.args[1:], fillvalue=expr.args[0]):
|
| 225 |
+
a = to_NNF(a, composite_map)
|
| 226 |
+
b = to_NNF(b, composite_map)
|
| 227 |
+
cnfs.append(OR(~a, b))
|
| 228 |
+
return AND(*cnfs)
|
| 229 |
+
|
| 230 |
+
if isinstance(expr, ITE):
|
| 231 |
+
L = to_NNF(expr.args[0], composite_map)
|
| 232 |
+
M = to_NNF(expr.args[1], composite_map)
|
| 233 |
+
R = to_NNF(expr.args[2], composite_map)
|
| 234 |
+
return AND(OR(~L, M), OR(L, R))
|
| 235 |
+
|
| 236 |
+
if isinstance(expr, AppliedPredicate):
|
| 237 |
+
pred, args = expr.function, expr.arguments
|
| 238 |
+
newpred = composite_map.get(pred, None)
|
| 239 |
+
if newpred is not None:
|
| 240 |
+
return to_NNF(newpred.rcall(*args), composite_map)
|
| 241 |
+
|
| 242 |
+
if isinstance(expr, Predicate):
|
| 243 |
+
newpred = composite_map.get(expr, None)
|
| 244 |
+
if newpred is not None:
|
| 245 |
+
return to_NNF(newpred, composite_map)
|
| 246 |
+
|
| 247 |
+
return Literal(expr)
|
| 248 |
+
|
| 249 |
+
|
| 250 |
+
def distribute_AND_over_OR(expr):
|
| 251 |
+
"""
|
| 252 |
+
Distributes AND over OR in the NNF expression.
|
| 253 |
+
Returns the result( Conjunctive Normal Form of expression)
|
| 254 |
+
as a CNF object.
|
| 255 |
+
"""
|
| 256 |
+
if not isinstance(expr, (AND, OR)):
|
| 257 |
+
tmp = set()
|
| 258 |
+
tmp.add(frozenset((expr,)))
|
| 259 |
+
return CNF(tmp)
|
| 260 |
+
|
| 261 |
+
if isinstance(expr, OR):
|
| 262 |
+
return CNF.all_or(*[distribute_AND_over_OR(arg)
|
| 263 |
+
for arg in expr._args])
|
| 264 |
+
|
| 265 |
+
if isinstance(expr, AND):
|
| 266 |
+
return CNF.all_and(*[distribute_AND_over_OR(arg)
|
| 267 |
+
for arg in expr._args])
|
| 268 |
+
|
| 269 |
+
|
| 270 |
+
class CNF:
|
| 271 |
+
"""
|
| 272 |
+
Class to represent CNF of a Boolean expression.
|
| 273 |
+
Consists of set of clauses, which themselves are stored as
|
| 274 |
+
frozenset of Literal objects.
|
| 275 |
+
|
| 276 |
+
Examples
|
| 277 |
+
========
|
| 278 |
+
|
| 279 |
+
>>> from sympy import Q
|
| 280 |
+
>>> from sympy.assumptions.cnf import CNF
|
| 281 |
+
>>> from sympy.abc import x
|
| 282 |
+
>>> cnf = CNF.from_prop(Q.real(x) & ~Q.zero(x))
|
| 283 |
+
>>> cnf.clauses
|
| 284 |
+
{frozenset({Literal(Q.zero(x), True)}),
|
| 285 |
+
frozenset({Literal(Q.negative(x), False),
|
| 286 |
+
Literal(Q.positive(x), False), Literal(Q.zero(x), False)})}
|
| 287 |
+
"""
|
| 288 |
+
def __init__(self, clauses=None):
|
| 289 |
+
if not clauses:
|
| 290 |
+
clauses = set()
|
| 291 |
+
self.clauses = clauses
|
| 292 |
+
|
| 293 |
+
def add(self, prop):
|
| 294 |
+
clauses = CNF.to_CNF(prop).clauses
|
| 295 |
+
self.add_clauses(clauses)
|
| 296 |
+
|
| 297 |
+
def __str__(self):
|
| 298 |
+
s = ' & '.join(
|
| 299 |
+
['(' + ' | '.join([str(lit) for lit in clause]) +')'
|
| 300 |
+
for clause in self.clauses]
|
| 301 |
+
)
|
| 302 |
+
return s
|
| 303 |
+
|
| 304 |
+
def extend(self, props):
|
| 305 |
+
for p in props:
|
| 306 |
+
self.add(p)
|
| 307 |
+
return self
|
| 308 |
+
|
| 309 |
+
def copy(self):
|
| 310 |
+
return CNF(set(self.clauses))
|
| 311 |
+
|
| 312 |
+
def add_clauses(self, clauses):
|
| 313 |
+
self.clauses |= clauses
|
| 314 |
+
|
| 315 |
+
@classmethod
|
| 316 |
+
def from_prop(cls, prop):
|
| 317 |
+
res = cls()
|
| 318 |
+
res.add(prop)
|
| 319 |
+
return res
|
| 320 |
+
|
| 321 |
+
def __iand__(self, other):
|
| 322 |
+
self.add_clauses(other.clauses)
|
| 323 |
+
return self
|
| 324 |
+
|
| 325 |
+
def all_predicates(self):
|
| 326 |
+
predicates = set()
|
| 327 |
+
for c in self.clauses:
|
| 328 |
+
predicates |= {arg.lit for arg in c}
|
| 329 |
+
return predicates
|
| 330 |
+
|
| 331 |
+
def _or(self, cnf):
|
| 332 |
+
clauses = set()
|
| 333 |
+
for a, b in product(self.clauses, cnf.clauses):
|
| 334 |
+
tmp = set(a)
|
| 335 |
+
tmp.update(b)
|
| 336 |
+
clauses.add(frozenset(tmp))
|
| 337 |
+
return CNF(clauses)
|
| 338 |
+
|
| 339 |
+
def _and(self, cnf):
|
| 340 |
+
clauses = self.clauses.union(cnf.clauses)
|
| 341 |
+
return CNF(clauses)
|
| 342 |
+
|
| 343 |
+
def _not(self):
|
| 344 |
+
clss = list(self.clauses)
|
| 345 |
+
ll = {frozenset((~x,)) for x in clss[-1]}
|
| 346 |
+
ll = CNF(ll)
|
| 347 |
+
|
| 348 |
+
for rest in clss[:-1]:
|
| 349 |
+
p = {frozenset((~x,)) for x in rest}
|
| 350 |
+
ll = ll._or(CNF(p))
|
| 351 |
+
return ll
|
| 352 |
+
|
| 353 |
+
def rcall(self, expr):
|
| 354 |
+
clause_list = []
|
| 355 |
+
for clause in self.clauses:
|
| 356 |
+
lits = [arg.rcall(expr) for arg in clause]
|
| 357 |
+
clause_list.append(OR(*lits))
|
| 358 |
+
expr = AND(*clause_list)
|
| 359 |
+
return distribute_AND_over_OR(expr)
|
| 360 |
+
|
| 361 |
+
@classmethod
|
| 362 |
+
def all_or(cls, *cnfs):
|
| 363 |
+
b = cnfs[0].copy()
|
| 364 |
+
for rest in cnfs[1:]:
|
| 365 |
+
b = b._or(rest)
|
| 366 |
+
return b
|
| 367 |
+
|
| 368 |
+
@classmethod
|
| 369 |
+
def all_and(cls, *cnfs):
|
| 370 |
+
b = cnfs[0].copy()
|
| 371 |
+
for rest in cnfs[1:]:
|
| 372 |
+
b = b._and(rest)
|
| 373 |
+
return b
|
| 374 |
+
|
| 375 |
+
@classmethod
|
| 376 |
+
def to_CNF(cls, expr):
|
| 377 |
+
from sympy.assumptions.facts import get_composite_predicates
|
| 378 |
+
expr = to_NNF(expr, get_composite_predicates())
|
| 379 |
+
expr = distribute_AND_over_OR(expr)
|
| 380 |
+
return expr
|
| 381 |
+
|
| 382 |
+
@classmethod
|
| 383 |
+
def CNF_to_cnf(cls, cnf):
|
| 384 |
+
"""
|
| 385 |
+
Converts CNF object to SymPy's boolean expression
|
| 386 |
+
retaining the form of expression.
|
| 387 |
+
"""
|
| 388 |
+
def remove_literal(arg):
|
| 389 |
+
return Not(arg.lit) if arg.is_Not else arg.lit
|
| 390 |
+
|
| 391 |
+
return And(*(Or(*(remove_literal(arg) for arg in clause)) for clause in cnf.clauses))
|
| 392 |
+
|
| 393 |
+
|
| 394 |
+
class EncodedCNF:
|
| 395 |
+
"""
|
| 396 |
+
Class for encoding the CNF expression.
|
| 397 |
+
"""
|
| 398 |
+
def __init__(self, data=None, encoding=None):
|
| 399 |
+
if not data and not encoding:
|
| 400 |
+
data = []
|
| 401 |
+
encoding = {}
|
| 402 |
+
self.data = data
|
| 403 |
+
self.encoding = encoding
|
| 404 |
+
self._symbols = list(encoding.keys())
|
| 405 |
+
|
| 406 |
+
def from_cnf(self, cnf):
|
| 407 |
+
self._symbols = list(cnf.all_predicates())
|
| 408 |
+
n = len(self._symbols)
|
| 409 |
+
self.encoding = dict(zip(self._symbols, range(1, n + 1)))
|
| 410 |
+
self.data = [self.encode(clause) for clause in cnf.clauses]
|
| 411 |
+
|
| 412 |
+
@property
|
| 413 |
+
def symbols(self):
|
| 414 |
+
return self._symbols
|
| 415 |
+
|
| 416 |
+
@property
|
| 417 |
+
def variables(self):
|
| 418 |
+
return range(1, len(self._symbols) + 1)
|
| 419 |
+
|
| 420 |
+
def copy(self):
|
| 421 |
+
new_data = [set(clause) for clause in self.data]
|
| 422 |
+
return EncodedCNF(new_data, dict(self.encoding))
|
| 423 |
+
|
| 424 |
+
def add_prop(self, prop):
|
| 425 |
+
cnf = CNF.from_prop(prop)
|
| 426 |
+
self.add_from_cnf(cnf)
|
| 427 |
+
|
| 428 |
+
def add_from_cnf(self, cnf):
|
| 429 |
+
clauses = [self.encode(clause) for clause in cnf.clauses]
|
| 430 |
+
self.data += clauses
|
| 431 |
+
|
| 432 |
+
def encode_arg(self, arg):
|
| 433 |
+
literal = arg.lit
|
| 434 |
+
value = self.encoding.get(literal, None)
|
| 435 |
+
if value is None:
|
| 436 |
+
n = len(self._symbols)
|
| 437 |
+
self._symbols.append(literal)
|
| 438 |
+
value = self.encoding[literal] = n + 1
|
| 439 |
+
if arg.is_Not:
|
| 440 |
+
return -value
|
| 441 |
+
else:
|
| 442 |
+
return value
|
| 443 |
+
|
| 444 |
+
def encode(self, clause):
|
| 445 |
+
return {self.encode_arg(arg) if not arg.lit == S.false else 0 for arg in clause}
|
lib/python3.12/site-packages/sympy/assumptions/facts.py
ADDED
|
@@ -0,0 +1,270 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Known facts in assumptions module.
|
| 3 |
+
|
| 4 |
+
This module defines the facts between unary predicates in ``get_known_facts()``,
|
| 5 |
+
and supports functions to generate the contents in
|
| 6 |
+
``sympy.assumptions.ask_generated`` file.
|
| 7 |
+
"""
|
| 8 |
+
|
| 9 |
+
from sympy.assumptions.ask import Q
|
| 10 |
+
from sympy.assumptions.assume import AppliedPredicate
|
| 11 |
+
from sympy.core.cache import cacheit
|
| 12 |
+
from sympy.core.symbol import Symbol
|
| 13 |
+
from sympy.logic.boolalg import (to_cnf, And, Not, Implies, Equivalent,
|
| 14 |
+
Exclusive,)
|
| 15 |
+
from sympy.logic.inference import satisfiable
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
@cacheit
|
| 19 |
+
def get_composite_predicates():
|
| 20 |
+
# To reduce the complexity of sat solver, these predicates are
|
| 21 |
+
# transformed into the combination of primitive predicates.
|
| 22 |
+
return {
|
| 23 |
+
Q.real : Q.negative | Q.zero | Q.positive,
|
| 24 |
+
Q.integer : Q.even | Q.odd,
|
| 25 |
+
Q.nonpositive : Q.negative | Q.zero,
|
| 26 |
+
Q.nonzero : Q.negative | Q.positive,
|
| 27 |
+
Q.nonnegative : Q.zero | Q.positive,
|
| 28 |
+
Q.extended_real : Q.negative_infinite | Q.negative | Q.zero | Q.positive | Q.positive_infinite,
|
| 29 |
+
Q.extended_positive: Q.positive | Q.positive_infinite,
|
| 30 |
+
Q.extended_negative: Q.negative | Q.negative_infinite,
|
| 31 |
+
Q.extended_nonzero: Q.negative_infinite | Q.negative | Q.positive | Q.positive_infinite,
|
| 32 |
+
Q.extended_nonpositive: Q.negative_infinite | Q.negative | Q.zero,
|
| 33 |
+
Q.extended_nonnegative: Q.zero | Q.positive | Q.positive_infinite,
|
| 34 |
+
Q.complex : Q.algebraic | Q.transcendental
|
| 35 |
+
}
|
| 36 |
+
|
| 37 |
+
|
| 38 |
+
@cacheit
|
| 39 |
+
def get_known_facts(x=None):
|
| 40 |
+
"""
|
| 41 |
+
Facts between unary predicates.
|
| 42 |
+
|
| 43 |
+
Parameters
|
| 44 |
+
==========
|
| 45 |
+
|
| 46 |
+
x : Symbol, optional
|
| 47 |
+
Placeholder symbol for unary facts. Default is ``Symbol('x')``.
|
| 48 |
+
|
| 49 |
+
Returns
|
| 50 |
+
=======
|
| 51 |
+
|
| 52 |
+
fact : Known facts in conjugated normal form.
|
| 53 |
+
|
| 54 |
+
"""
|
| 55 |
+
if x is None:
|
| 56 |
+
x = Symbol('x')
|
| 57 |
+
|
| 58 |
+
fact = And(
|
| 59 |
+
get_number_facts(x),
|
| 60 |
+
get_matrix_facts(x)
|
| 61 |
+
)
|
| 62 |
+
return fact
|
| 63 |
+
|
| 64 |
+
|
| 65 |
+
@cacheit
|
| 66 |
+
def get_number_facts(x = None):
|
| 67 |
+
"""
|
| 68 |
+
Facts between unary number predicates.
|
| 69 |
+
|
| 70 |
+
Parameters
|
| 71 |
+
==========
|
| 72 |
+
|
| 73 |
+
x : Symbol, optional
|
| 74 |
+
Placeholder symbol for unary facts. Default is ``Symbol('x')``.
|
| 75 |
+
|
| 76 |
+
Returns
|
| 77 |
+
=======
|
| 78 |
+
|
| 79 |
+
fact : Known facts in conjugated normal form.
|
| 80 |
+
|
| 81 |
+
"""
|
| 82 |
+
if x is None:
|
| 83 |
+
x = Symbol('x')
|
| 84 |
+
|
| 85 |
+
fact = And(
|
| 86 |
+
# primitive predicates for extended real exclude each other.
|
| 87 |
+
Exclusive(Q.negative_infinite(x), Q.negative(x), Q.zero(x),
|
| 88 |
+
Q.positive(x), Q.positive_infinite(x)),
|
| 89 |
+
|
| 90 |
+
# build complex plane
|
| 91 |
+
Exclusive(Q.real(x), Q.imaginary(x)),
|
| 92 |
+
Implies(Q.real(x) | Q.imaginary(x), Q.complex(x)),
|
| 93 |
+
|
| 94 |
+
# other subsets of complex
|
| 95 |
+
Exclusive(Q.transcendental(x), Q.algebraic(x)),
|
| 96 |
+
Equivalent(Q.real(x), Q.rational(x) | Q.irrational(x)),
|
| 97 |
+
Exclusive(Q.irrational(x), Q.rational(x)),
|
| 98 |
+
Implies(Q.rational(x), Q.algebraic(x)),
|
| 99 |
+
|
| 100 |
+
# integers
|
| 101 |
+
Exclusive(Q.even(x), Q.odd(x)),
|
| 102 |
+
Implies(Q.integer(x), Q.rational(x)),
|
| 103 |
+
Implies(Q.zero(x), Q.even(x)),
|
| 104 |
+
Exclusive(Q.composite(x), Q.prime(x)),
|
| 105 |
+
Implies(Q.composite(x) | Q.prime(x), Q.integer(x) & Q.positive(x)),
|
| 106 |
+
Implies(Q.even(x) & Q.positive(x) & ~Q.prime(x), Q.composite(x)),
|
| 107 |
+
|
| 108 |
+
# hermitian and antihermitian
|
| 109 |
+
Implies(Q.real(x), Q.hermitian(x)),
|
| 110 |
+
Implies(Q.imaginary(x), Q.antihermitian(x)),
|
| 111 |
+
Implies(Q.zero(x), Q.hermitian(x) | Q.antihermitian(x)),
|
| 112 |
+
|
| 113 |
+
# define finity and infinity, and build extended real line
|
| 114 |
+
Exclusive(Q.infinite(x), Q.finite(x)),
|
| 115 |
+
Implies(Q.complex(x), Q.finite(x)),
|
| 116 |
+
Implies(Q.negative_infinite(x) | Q.positive_infinite(x), Q.infinite(x)),
|
| 117 |
+
|
| 118 |
+
# commutativity
|
| 119 |
+
Implies(Q.finite(x) | Q.infinite(x), Q.commutative(x)),
|
| 120 |
+
)
|
| 121 |
+
return fact
|
| 122 |
+
|
| 123 |
+
|
| 124 |
+
@cacheit
|
| 125 |
+
def get_matrix_facts(x = None):
|
| 126 |
+
"""
|
| 127 |
+
Facts between unary matrix predicates.
|
| 128 |
+
|
| 129 |
+
Parameters
|
| 130 |
+
==========
|
| 131 |
+
|
| 132 |
+
x : Symbol, optional
|
| 133 |
+
Placeholder symbol for unary facts. Default is ``Symbol('x')``.
|
| 134 |
+
|
| 135 |
+
Returns
|
| 136 |
+
=======
|
| 137 |
+
|
| 138 |
+
fact : Known facts in conjugated normal form.
|
| 139 |
+
|
| 140 |
+
"""
|
| 141 |
+
if x is None:
|
| 142 |
+
x = Symbol('x')
|
| 143 |
+
|
| 144 |
+
fact = And(
|
| 145 |
+
# matrices
|
| 146 |
+
Implies(Q.orthogonal(x), Q.positive_definite(x)),
|
| 147 |
+
Implies(Q.orthogonal(x), Q.unitary(x)),
|
| 148 |
+
Implies(Q.unitary(x) & Q.real_elements(x), Q.orthogonal(x)),
|
| 149 |
+
Implies(Q.unitary(x), Q.normal(x)),
|
| 150 |
+
Implies(Q.unitary(x), Q.invertible(x)),
|
| 151 |
+
Implies(Q.normal(x), Q.square(x)),
|
| 152 |
+
Implies(Q.diagonal(x), Q.normal(x)),
|
| 153 |
+
Implies(Q.positive_definite(x), Q.invertible(x)),
|
| 154 |
+
Implies(Q.diagonal(x), Q.upper_triangular(x)),
|
| 155 |
+
Implies(Q.diagonal(x), Q.lower_triangular(x)),
|
| 156 |
+
Implies(Q.lower_triangular(x), Q.triangular(x)),
|
| 157 |
+
Implies(Q.upper_triangular(x), Q.triangular(x)),
|
| 158 |
+
Implies(Q.triangular(x), Q.upper_triangular(x) | Q.lower_triangular(x)),
|
| 159 |
+
Implies(Q.upper_triangular(x) & Q.lower_triangular(x), Q.diagonal(x)),
|
| 160 |
+
Implies(Q.diagonal(x), Q.symmetric(x)),
|
| 161 |
+
Implies(Q.unit_triangular(x), Q.triangular(x)),
|
| 162 |
+
Implies(Q.invertible(x), Q.fullrank(x)),
|
| 163 |
+
Implies(Q.invertible(x), Q.square(x)),
|
| 164 |
+
Implies(Q.symmetric(x), Q.square(x)),
|
| 165 |
+
Implies(Q.fullrank(x) & Q.square(x), Q.invertible(x)),
|
| 166 |
+
Equivalent(Q.invertible(x), ~Q.singular(x)),
|
| 167 |
+
Implies(Q.integer_elements(x), Q.real_elements(x)),
|
| 168 |
+
Implies(Q.real_elements(x), Q.complex_elements(x)),
|
| 169 |
+
)
|
| 170 |
+
return fact
|
| 171 |
+
|
| 172 |
+
|
| 173 |
+
|
| 174 |
+
def generate_known_facts_dict(keys, fact):
|
| 175 |
+
"""
|
| 176 |
+
Computes and returns a dictionary which contains the relations between
|
| 177 |
+
unary predicates.
|
| 178 |
+
|
| 179 |
+
Each key is a predicate, and item is two groups of predicates.
|
| 180 |
+
First group contains the predicates which are implied by the key, and
|
| 181 |
+
second group contains the predicates which are rejected by the key.
|
| 182 |
+
|
| 183 |
+
All predicates in *keys* and *fact* must be unary and have same placeholder
|
| 184 |
+
symbol.
|
| 185 |
+
|
| 186 |
+
Parameters
|
| 187 |
+
==========
|
| 188 |
+
|
| 189 |
+
keys : list of AppliedPredicate instances.
|
| 190 |
+
|
| 191 |
+
fact : Fact between predicates in conjugated normal form.
|
| 192 |
+
|
| 193 |
+
Examples
|
| 194 |
+
========
|
| 195 |
+
|
| 196 |
+
>>> from sympy import Q, And, Implies
|
| 197 |
+
>>> from sympy.assumptions.facts import generate_known_facts_dict
|
| 198 |
+
>>> from sympy.abc import x
|
| 199 |
+
>>> keys = [Q.even(x), Q.odd(x), Q.zero(x)]
|
| 200 |
+
>>> fact = And(Implies(Q.even(x), ~Q.odd(x)),
|
| 201 |
+
... Implies(Q.zero(x), Q.even(x)))
|
| 202 |
+
>>> generate_known_facts_dict(keys, fact)
|
| 203 |
+
{Q.even: ({Q.even}, {Q.odd}),
|
| 204 |
+
Q.odd: ({Q.odd}, {Q.even, Q.zero}),
|
| 205 |
+
Q.zero: ({Q.even, Q.zero}, {Q.odd})}
|
| 206 |
+
"""
|
| 207 |
+
fact_cnf = to_cnf(fact)
|
| 208 |
+
mapping = single_fact_lookup(keys, fact_cnf)
|
| 209 |
+
|
| 210 |
+
ret = {}
|
| 211 |
+
for key, value in mapping.items():
|
| 212 |
+
implied = set()
|
| 213 |
+
rejected = set()
|
| 214 |
+
for expr in value:
|
| 215 |
+
if isinstance(expr, AppliedPredicate):
|
| 216 |
+
implied.add(expr.function)
|
| 217 |
+
elif isinstance(expr, Not):
|
| 218 |
+
pred = expr.args[0]
|
| 219 |
+
rejected.add(pred.function)
|
| 220 |
+
ret[key.function] = (implied, rejected)
|
| 221 |
+
return ret
|
| 222 |
+
|
| 223 |
+
|
| 224 |
+
@cacheit
|
| 225 |
+
def get_known_facts_keys():
|
| 226 |
+
"""
|
| 227 |
+
Return every unary predicates registered to ``Q``.
|
| 228 |
+
|
| 229 |
+
This function is used to generate the keys for
|
| 230 |
+
``generate_known_facts_dict``.
|
| 231 |
+
|
| 232 |
+
"""
|
| 233 |
+
# exclude polyadic predicates
|
| 234 |
+
exclude = {Q.eq, Q.ne, Q.gt, Q.lt, Q.ge, Q.le}
|
| 235 |
+
|
| 236 |
+
result = []
|
| 237 |
+
for attr in Q.__class__.__dict__:
|
| 238 |
+
if attr.startswith('__'):
|
| 239 |
+
continue
|
| 240 |
+
pred = getattr(Q, attr)
|
| 241 |
+
if pred in exclude:
|
| 242 |
+
continue
|
| 243 |
+
result.append(pred)
|
| 244 |
+
return result
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def single_fact_lookup(known_facts_keys, known_facts_cnf):
|
| 248 |
+
# Return the dictionary for quick lookup of single fact
|
| 249 |
+
mapping = {}
|
| 250 |
+
for key in known_facts_keys:
|
| 251 |
+
mapping[key] = {key}
|
| 252 |
+
for other_key in known_facts_keys:
|
| 253 |
+
if other_key != key:
|
| 254 |
+
if ask_full_inference(other_key, key, known_facts_cnf):
|
| 255 |
+
mapping[key].add(other_key)
|
| 256 |
+
if ask_full_inference(~other_key, key, known_facts_cnf):
|
| 257 |
+
mapping[key].add(~other_key)
|
| 258 |
+
return mapping
|
| 259 |
+
|
| 260 |
+
|
| 261 |
+
def ask_full_inference(proposition, assumptions, known_facts_cnf):
|
| 262 |
+
"""
|
| 263 |
+
Method for inferring properties about objects.
|
| 264 |
+
|
| 265 |
+
"""
|
| 266 |
+
if not satisfiable(And(known_facts_cnf, assumptions, proposition)):
|
| 267 |
+
return False
|
| 268 |
+
if not satisfiable(And(known_facts_cnf, assumptions, Not(proposition))):
|
| 269 |
+
return True
|
| 270 |
+
return None
|
lib/python3.12/site-packages/sympy/assumptions/handlers/calculus.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module contains query handlers responsible for calculus queries:
|
| 3 |
+
infinitesimal, finite, etc.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from sympy.assumptions import Q, ask
|
| 7 |
+
from sympy.core import Add, Mul, Pow, Symbol
|
| 8 |
+
from sympy.core.numbers import (NegativeInfinity, GoldenRatio,
|
| 9 |
+
Infinity, Exp1, ComplexInfinity, ImaginaryUnit, NaN, Number, Pi, E,
|
| 10 |
+
TribonacciConstant)
|
| 11 |
+
from sympy.functions import cos, exp, log, sign, sin
|
| 12 |
+
from sympy.logic.boolalg import conjuncts
|
| 13 |
+
|
| 14 |
+
from ..predicates.calculus import (FinitePredicate, InfinitePredicate,
|
| 15 |
+
PositiveInfinitePredicate, NegativeInfinitePredicate)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# FinitePredicate
|
| 19 |
+
|
| 20 |
+
|
| 21 |
+
@FinitePredicate.register(Symbol)
|
| 22 |
+
def _(expr, assumptions):
|
| 23 |
+
"""
|
| 24 |
+
Handles Symbol.
|
| 25 |
+
"""
|
| 26 |
+
if expr.is_finite is not None:
|
| 27 |
+
return expr.is_finite
|
| 28 |
+
if Q.finite(expr) in conjuncts(assumptions):
|
| 29 |
+
return True
|
| 30 |
+
return None
|
| 31 |
+
|
| 32 |
+
@FinitePredicate.register(Add)
|
| 33 |
+
def _(expr, assumptions):
|
| 34 |
+
"""
|
| 35 |
+
Return True if expr is bounded, False if not and None if unknown.
|
| 36 |
+
|
| 37 |
+
Truth Table:
|
| 38 |
+
|
| 39 |
+
+-------+-----+-----------+-----------+
|
| 40 |
+
| | | | |
|
| 41 |
+
| | B | U | ? |
|
| 42 |
+
| | | | |
|
| 43 |
+
+-------+-----+---+---+---+---+---+---+
|
| 44 |
+
| | | | | | | | |
|
| 45 |
+
| | |'+'|'-'|'x'|'+'|'-'|'x'|
|
| 46 |
+
| | | | | | | | |
|
| 47 |
+
+-------+-----+---+---+---+---+---+---+
|
| 48 |
+
| | | | |
|
| 49 |
+
| B | B | U | ? |
|
| 50 |
+
| | | | |
|
| 51 |
+
+---+---+-----+---+---+---+---+---+---+
|
| 52 |
+
| | | | | | | | | |
|
| 53 |
+
| |'+'| | U | ? | ? | U | ? | ? |
|
| 54 |
+
| | | | | | | | | |
|
| 55 |
+
| +---+-----+---+---+---+---+---+---+
|
| 56 |
+
| | | | | | | | | |
|
| 57 |
+
| U |'-'| | ? | U | ? | ? | U | ? |
|
| 58 |
+
| | | | | | | | | |
|
| 59 |
+
| +---+-----+---+---+---+---+---+---+
|
| 60 |
+
| | | | | |
|
| 61 |
+
| |'x'| | ? | ? |
|
| 62 |
+
| | | | | |
|
| 63 |
+
+---+---+-----+---+---+---+---+---+---+
|
| 64 |
+
| | | | |
|
| 65 |
+
| ? | | | ? |
|
| 66 |
+
| | | | |
|
| 67 |
+
+-------+-----+-----------+---+---+---+
|
| 68 |
+
|
| 69 |
+
* 'B' = Bounded
|
| 70 |
+
|
| 71 |
+
* 'U' = Unbounded
|
| 72 |
+
|
| 73 |
+
* '?' = unknown boundedness
|
| 74 |
+
|
| 75 |
+
* '+' = positive sign
|
| 76 |
+
|
| 77 |
+
* '-' = negative sign
|
| 78 |
+
|
| 79 |
+
* 'x' = sign unknown
|
| 80 |
+
|
| 81 |
+
* All Bounded -> True
|
| 82 |
+
|
| 83 |
+
* 1 Unbounded and the rest Bounded -> False
|
| 84 |
+
|
| 85 |
+
* >1 Unbounded, all with same known sign -> False
|
| 86 |
+
|
| 87 |
+
* Any Unknown and unknown sign -> None
|
| 88 |
+
|
| 89 |
+
* Else -> None
|
| 90 |
+
|
| 91 |
+
When the signs are not the same you can have an undefined
|
| 92 |
+
result as in oo - oo, hence 'bounded' is also undefined.
|
| 93 |
+
"""
|
| 94 |
+
sign = -1 # sign of unknown or infinite
|
| 95 |
+
result = True
|
| 96 |
+
for arg in expr.args:
|
| 97 |
+
_bounded = ask(Q.finite(arg), assumptions)
|
| 98 |
+
if _bounded:
|
| 99 |
+
continue
|
| 100 |
+
s = ask(Q.extended_positive(arg), assumptions)
|
| 101 |
+
# if there has been more than one sign or if the sign of this arg
|
| 102 |
+
# is None and Bounded is None or there was already
|
| 103 |
+
# an unknown sign, return None
|
| 104 |
+
if sign != -1 and s != sign or \
|
| 105 |
+
s is None and None in (_bounded, sign):
|
| 106 |
+
return None
|
| 107 |
+
else:
|
| 108 |
+
sign = s
|
| 109 |
+
# once False, do not change
|
| 110 |
+
if result is not False:
|
| 111 |
+
result = _bounded
|
| 112 |
+
return result
|
| 113 |
+
|
| 114 |
+
@FinitePredicate.register(Mul)
|
| 115 |
+
def _(expr, assumptions):
|
| 116 |
+
"""
|
| 117 |
+
Return True if expr is bounded, False if not and None if unknown.
|
| 118 |
+
|
| 119 |
+
Truth Table:
|
| 120 |
+
|
| 121 |
+
+---+---+---+--------+
|
| 122 |
+
| | | | |
|
| 123 |
+
| | B | U | ? |
|
| 124 |
+
| | | | |
|
| 125 |
+
+---+---+---+---+----+
|
| 126 |
+
| | | | | |
|
| 127 |
+
| | | | s | /s |
|
| 128 |
+
| | | | | |
|
| 129 |
+
+---+---+---+---+----+
|
| 130 |
+
| | | | |
|
| 131 |
+
| B | B | U | ? |
|
| 132 |
+
| | | | |
|
| 133 |
+
+---+---+---+---+----+
|
| 134 |
+
| | | | | |
|
| 135 |
+
| U | | U | U | ? |
|
| 136 |
+
| | | | | |
|
| 137 |
+
+---+---+---+---+----+
|
| 138 |
+
| | | | |
|
| 139 |
+
| ? | | | ? |
|
| 140 |
+
| | | | |
|
| 141 |
+
+---+---+---+---+----+
|
| 142 |
+
|
| 143 |
+
* B = Bounded
|
| 144 |
+
|
| 145 |
+
* U = Unbounded
|
| 146 |
+
|
| 147 |
+
* ? = unknown boundedness
|
| 148 |
+
|
| 149 |
+
* s = signed (hence nonzero)
|
| 150 |
+
|
| 151 |
+
* /s = not signed
|
| 152 |
+
"""
|
| 153 |
+
result = True
|
| 154 |
+
for arg in expr.args:
|
| 155 |
+
_bounded = ask(Q.finite(arg), assumptions)
|
| 156 |
+
if _bounded:
|
| 157 |
+
continue
|
| 158 |
+
elif _bounded is None:
|
| 159 |
+
if result is None:
|
| 160 |
+
return None
|
| 161 |
+
if ask(Q.extended_nonzero(arg), assumptions) is None:
|
| 162 |
+
return None
|
| 163 |
+
if result is not False:
|
| 164 |
+
result = None
|
| 165 |
+
else:
|
| 166 |
+
result = False
|
| 167 |
+
return result
|
| 168 |
+
|
| 169 |
+
@FinitePredicate.register(Pow)
|
| 170 |
+
def _(expr, assumptions):
|
| 171 |
+
"""
|
| 172 |
+
* Unbounded ** NonZero -> Unbounded
|
| 173 |
+
|
| 174 |
+
* Bounded ** Bounded -> Bounded
|
| 175 |
+
|
| 176 |
+
* Abs()<=1 ** Positive -> Bounded
|
| 177 |
+
|
| 178 |
+
* Abs()>=1 ** Negative -> Bounded
|
| 179 |
+
|
| 180 |
+
* Otherwise unknown
|
| 181 |
+
"""
|
| 182 |
+
if expr.base == E:
|
| 183 |
+
return ask(Q.finite(expr.exp), assumptions)
|
| 184 |
+
|
| 185 |
+
base_bounded = ask(Q.finite(expr.base), assumptions)
|
| 186 |
+
exp_bounded = ask(Q.finite(expr.exp), assumptions)
|
| 187 |
+
if base_bounded is None and exp_bounded is None: # Common Case
|
| 188 |
+
return None
|
| 189 |
+
if base_bounded is False and ask(Q.extended_nonzero(expr.exp), assumptions):
|
| 190 |
+
return False
|
| 191 |
+
if base_bounded and exp_bounded:
|
| 192 |
+
return True
|
| 193 |
+
if (abs(expr.base) <= 1) == True and ask(Q.extended_positive(expr.exp), assumptions):
|
| 194 |
+
return True
|
| 195 |
+
if (abs(expr.base) >= 1) == True and ask(Q.extended_negative(expr.exp), assumptions):
|
| 196 |
+
return True
|
| 197 |
+
if (abs(expr.base) >= 1) == True and exp_bounded is False:
|
| 198 |
+
return False
|
| 199 |
+
return None
|
| 200 |
+
|
| 201 |
+
@FinitePredicate.register(exp)
|
| 202 |
+
def _(expr, assumptions):
|
| 203 |
+
return ask(Q.finite(expr.exp), assumptions)
|
| 204 |
+
|
| 205 |
+
@FinitePredicate.register(log)
|
| 206 |
+
def _(expr, assumptions):
|
| 207 |
+
# After complex -> finite fact is registered to new assumption system,
|
| 208 |
+
# querying Q.infinite may be removed.
|
| 209 |
+
if ask(Q.infinite(expr.args[0]), assumptions):
|
| 210 |
+
return False
|
| 211 |
+
return ask(~Q.zero(expr.args[0]), assumptions)
|
| 212 |
+
|
| 213 |
+
@FinitePredicate.register_many(cos, sin, Number, Pi, Exp1, GoldenRatio,
|
| 214 |
+
TribonacciConstant, ImaginaryUnit, sign)
|
| 215 |
+
def _(expr, assumptions):
|
| 216 |
+
return True
|
| 217 |
+
|
| 218 |
+
@FinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
|
| 219 |
+
def _(expr, assumptions):
|
| 220 |
+
return False
|
| 221 |
+
|
| 222 |
+
@FinitePredicate.register(NaN)
|
| 223 |
+
def _(expr, assumptions):
|
| 224 |
+
return None
|
| 225 |
+
|
| 226 |
+
|
| 227 |
+
# InfinitePredicate
|
| 228 |
+
|
| 229 |
+
|
| 230 |
+
@InfinitePredicate.register_many(ComplexInfinity, Infinity, NegativeInfinity)
|
| 231 |
+
def _(expr, assumptions):
|
| 232 |
+
return True
|
| 233 |
+
|
| 234 |
+
|
| 235 |
+
# PositiveInfinitePredicate
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
@PositiveInfinitePredicate.register(Infinity)
|
| 239 |
+
def _(expr, assumptions):
|
| 240 |
+
return True
|
| 241 |
+
|
| 242 |
+
|
| 243 |
+
@PositiveInfinitePredicate.register_many(NegativeInfinity, ComplexInfinity)
|
| 244 |
+
def _(expr, assumptions):
|
| 245 |
+
return False
|
| 246 |
+
|
| 247 |
+
|
| 248 |
+
# NegativeInfinitePredicate
|
| 249 |
+
|
| 250 |
+
|
| 251 |
+
@NegativeInfinitePredicate.register(NegativeInfinity)
|
| 252 |
+
def _(expr, assumptions):
|
| 253 |
+
return True
|
| 254 |
+
|
| 255 |
+
|
| 256 |
+
@NegativeInfinitePredicate.register_many(Infinity, ComplexInfinity)
|
| 257 |
+
def _(expr, assumptions):
|
| 258 |
+
return False
|
lib/python3.12/site-packages/sympy/assumptions/handlers/matrices.py
ADDED
|
@@ -0,0 +1,716 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
This module contains query handlers responsible for Matrices queries:
|
| 3 |
+
Square, Symmetric, Invertible etc.
|
| 4 |
+
"""
|
| 5 |
+
|
| 6 |
+
from sympy.logic.boolalg import conjuncts
|
| 7 |
+
from sympy.assumptions import Q, ask
|
| 8 |
+
from sympy.assumptions.handlers import test_closed_group
|
| 9 |
+
from sympy.matrices import MatrixBase
|
| 10 |
+
from sympy.matrices.expressions import (BlockMatrix, BlockDiagMatrix, Determinant,
|
| 11 |
+
DiagMatrix, DiagonalMatrix, HadamardProduct, Identity, Inverse, MatAdd, MatMul,
|
| 12 |
+
MatPow, MatrixExpr, MatrixSlice, MatrixSymbol, OneMatrix, Trace, Transpose,
|
| 13 |
+
ZeroMatrix)
|
| 14 |
+
from sympy.matrices.expressions.blockmatrix import reblock_2x2
|
| 15 |
+
from sympy.matrices.expressions.factorizations import Factorization
|
| 16 |
+
from sympy.matrices.expressions.fourier import DFT
|
| 17 |
+
from sympy.core.logic import fuzzy_and
|
| 18 |
+
from sympy.utilities.iterables import sift
|
| 19 |
+
from sympy.core import Basic
|
| 20 |
+
|
| 21 |
+
from ..predicates.matrices import (SquarePredicate, SymmetricPredicate,
|
| 22 |
+
InvertiblePredicate, OrthogonalPredicate, UnitaryPredicate,
|
| 23 |
+
FullRankPredicate, PositiveDefinitePredicate, UpperTriangularPredicate,
|
| 24 |
+
LowerTriangularPredicate, DiagonalPredicate, IntegerElementsPredicate,
|
| 25 |
+
RealElementsPredicate, ComplexElementsPredicate)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
def _Factorization(predicate, expr, assumptions):
|
| 29 |
+
if predicate in expr.predicates:
|
| 30 |
+
return True
|
| 31 |
+
|
| 32 |
+
|
| 33 |
+
# SquarePredicate
|
| 34 |
+
|
| 35 |
+
@SquarePredicate.register(MatrixExpr)
|
| 36 |
+
def _(expr, assumptions):
|
| 37 |
+
return expr.shape[0] == expr.shape[1]
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
# SymmetricPredicate
|
| 41 |
+
|
| 42 |
+
@SymmetricPredicate.register(MatMul)
|
| 43 |
+
def _(expr, assumptions):
|
| 44 |
+
factor, mmul = expr.as_coeff_mmul()
|
| 45 |
+
if all(ask(Q.symmetric(arg), assumptions) for arg in mmul.args):
|
| 46 |
+
return True
|
| 47 |
+
# TODO: implement sathandlers system for the matrices.
|
| 48 |
+
# Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
|
| 49 |
+
if ask(Q.diagonal(expr), assumptions):
|
| 50 |
+
return True
|
| 51 |
+
if len(mmul.args) >= 2 and mmul.args[0] == mmul.args[-1].T:
|
| 52 |
+
if len(mmul.args) == 2:
|
| 53 |
+
return True
|
| 54 |
+
return ask(Q.symmetric(MatMul(*mmul.args[1:-1])), assumptions)
|
| 55 |
+
|
| 56 |
+
@SymmetricPredicate.register(MatPow)
|
| 57 |
+
def _(expr, assumptions):
|
| 58 |
+
# only for integer powers
|
| 59 |
+
base, exp = expr.args
|
| 60 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 61 |
+
if not int_exp:
|
| 62 |
+
return None
|
| 63 |
+
non_negative = ask(~Q.negative(exp), assumptions)
|
| 64 |
+
if (non_negative or non_negative == False
|
| 65 |
+
and ask(Q.invertible(base), assumptions)):
|
| 66 |
+
return ask(Q.symmetric(base), assumptions)
|
| 67 |
+
return None
|
| 68 |
+
|
| 69 |
+
@SymmetricPredicate.register(MatAdd)
|
| 70 |
+
def _(expr, assumptions):
|
| 71 |
+
return all(ask(Q.symmetric(arg), assumptions) for arg in expr.args)
|
| 72 |
+
|
| 73 |
+
@SymmetricPredicate.register(MatrixSymbol)
|
| 74 |
+
def _(expr, assumptions):
|
| 75 |
+
if not expr.is_square:
|
| 76 |
+
return False
|
| 77 |
+
# TODO: implement sathandlers system for the matrices.
|
| 78 |
+
# Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
|
| 79 |
+
if ask(Q.diagonal(expr), assumptions):
|
| 80 |
+
return True
|
| 81 |
+
if Q.symmetric(expr) in conjuncts(assumptions):
|
| 82 |
+
return True
|
| 83 |
+
|
| 84 |
+
@SymmetricPredicate.register_many(OneMatrix, ZeroMatrix)
|
| 85 |
+
def _(expr, assumptions):
|
| 86 |
+
return ask(Q.square(expr), assumptions)
|
| 87 |
+
|
| 88 |
+
@SymmetricPredicate.register_many(Inverse, Transpose)
|
| 89 |
+
def _(expr, assumptions):
|
| 90 |
+
return ask(Q.symmetric(expr.arg), assumptions)
|
| 91 |
+
|
| 92 |
+
@SymmetricPredicate.register(MatrixSlice)
|
| 93 |
+
def _(expr, assumptions):
|
| 94 |
+
# TODO: implement sathandlers system for the matrices.
|
| 95 |
+
# Now it duplicates the general fact: Implies(Q.diagonal, Q.symmetric).
|
| 96 |
+
if ask(Q.diagonal(expr), assumptions):
|
| 97 |
+
return True
|
| 98 |
+
if not expr.on_diag:
|
| 99 |
+
return None
|
| 100 |
+
else:
|
| 101 |
+
return ask(Q.symmetric(expr.parent), assumptions)
|
| 102 |
+
|
| 103 |
+
@SymmetricPredicate.register(Identity)
|
| 104 |
+
def _(expr, assumptions):
|
| 105 |
+
return True
|
| 106 |
+
|
| 107 |
+
|
| 108 |
+
# InvertiblePredicate
|
| 109 |
+
|
| 110 |
+
@InvertiblePredicate.register(MatMul)
|
| 111 |
+
def _(expr, assumptions):
|
| 112 |
+
factor, mmul = expr.as_coeff_mmul()
|
| 113 |
+
if all(ask(Q.invertible(arg), assumptions) for arg in mmul.args):
|
| 114 |
+
return True
|
| 115 |
+
if any(ask(Q.invertible(arg), assumptions) is False
|
| 116 |
+
for arg in mmul.args):
|
| 117 |
+
return False
|
| 118 |
+
|
| 119 |
+
@InvertiblePredicate.register(MatPow)
|
| 120 |
+
def _(expr, assumptions):
|
| 121 |
+
# only for integer powers
|
| 122 |
+
base, exp = expr.args
|
| 123 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 124 |
+
if not int_exp:
|
| 125 |
+
return None
|
| 126 |
+
if exp.is_negative == False:
|
| 127 |
+
return ask(Q.invertible(base), assumptions)
|
| 128 |
+
return None
|
| 129 |
+
|
| 130 |
+
@InvertiblePredicate.register(MatAdd)
|
| 131 |
+
def _(expr, assumptions):
|
| 132 |
+
return None
|
| 133 |
+
|
| 134 |
+
@InvertiblePredicate.register(MatrixSymbol)
|
| 135 |
+
def _(expr, assumptions):
|
| 136 |
+
if not expr.is_square:
|
| 137 |
+
return False
|
| 138 |
+
if Q.invertible(expr) in conjuncts(assumptions):
|
| 139 |
+
return True
|
| 140 |
+
|
| 141 |
+
@InvertiblePredicate.register_many(Identity, Inverse)
|
| 142 |
+
def _(expr, assumptions):
|
| 143 |
+
return True
|
| 144 |
+
|
| 145 |
+
@InvertiblePredicate.register(ZeroMatrix)
|
| 146 |
+
def _(expr, assumptions):
|
| 147 |
+
return False
|
| 148 |
+
|
| 149 |
+
@InvertiblePredicate.register(OneMatrix)
|
| 150 |
+
def _(expr, assumptions):
|
| 151 |
+
return expr.shape[0] == 1 and expr.shape[1] == 1
|
| 152 |
+
|
| 153 |
+
@InvertiblePredicate.register(Transpose)
|
| 154 |
+
def _(expr, assumptions):
|
| 155 |
+
return ask(Q.invertible(expr.arg), assumptions)
|
| 156 |
+
|
| 157 |
+
@InvertiblePredicate.register(MatrixSlice)
|
| 158 |
+
def _(expr, assumptions):
|
| 159 |
+
if not expr.on_diag:
|
| 160 |
+
return None
|
| 161 |
+
else:
|
| 162 |
+
return ask(Q.invertible(expr.parent), assumptions)
|
| 163 |
+
|
| 164 |
+
@InvertiblePredicate.register(MatrixBase)
|
| 165 |
+
def _(expr, assumptions):
|
| 166 |
+
if not expr.is_square:
|
| 167 |
+
return False
|
| 168 |
+
return expr.rank() == expr.rows
|
| 169 |
+
|
| 170 |
+
@InvertiblePredicate.register(MatrixExpr)
|
| 171 |
+
def _(expr, assumptions):
|
| 172 |
+
if not expr.is_square:
|
| 173 |
+
return False
|
| 174 |
+
return None
|
| 175 |
+
|
| 176 |
+
@InvertiblePredicate.register(BlockMatrix)
|
| 177 |
+
def _(expr, assumptions):
|
| 178 |
+
if not expr.is_square:
|
| 179 |
+
return False
|
| 180 |
+
if expr.blockshape == (1, 1):
|
| 181 |
+
return ask(Q.invertible(expr.blocks[0, 0]), assumptions)
|
| 182 |
+
expr = reblock_2x2(expr)
|
| 183 |
+
if expr.blockshape == (2, 2):
|
| 184 |
+
[[A, B], [C, D]] = expr.blocks.tolist()
|
| 185 |
+
if ask(Q.invertible(A), assumptions) == True:
|
| 186 |
+
invertible = ask(Q.invertible(D - C * A.I * B), assumptions)
|
| 187 |
+
if invertible is not None:
|
| 188 |
+
return invertible
|
| 189 |
+
if ask(Q.invertible(B), assumptions) == True:
|
| 190 |
+
invertible = ask(Q.invertible(C - D * B.I * A), assumptions)
|
| 191 |
+
if invertible is not None:
|
| 192 |
+
return invertible
|
| 193 |
+
if ask(Q.invertible(C), assumptions) == True:
|
| 194 |
+
invertible = ask(Q.invertible(B - A * C.I * D), assumptions)
|
| 195 |
+
if invertible is not None:
|
| 196 |
+
return invertible
|
| 197 |
+
if ask(Q.invertible(D), assumptions) == True:
|
| 198 |
+
invertible = ask(Q.invertible(A - B * D.I * C), assumptions)
|
| 199 |
+
if invertible is not None:
|
| 200 |
+
return invertible
|
| 201 |
+
return None
|
| 202 |
+
|
| 203 |
+
@InvertiblePredicate.register(BlockDiagMatrix)
|
| 204 |
+
def _(expr, assumptions):
|
| 205 |
+
if expr.rowblocksizes != expr.colblocksizes:
|
| 206 |
+
return None
|
| 207 |
+
return fuzzy_and([ask(Q.invertible(a), assumptions) for a in expr.diag])
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
# OrthogonalPredicate
|
| 211 |
+
|
| 212 |
+
@OrthogonalPredicate.register(MatMul)
|
| 213 |
+
def _(expr, assumptions):
|
| 214 |
+
factor, mmul = expr.as_coeff_mmul()
|
| 215 |
+
if (all(ask(Q.orthogonal(arg), assumptions) for arg in mmul.args) and
|
| 216 |
+
factor == 1):
|
| 217 |
+
return True
|
| 218 |
+
if any(ask(Q.invertible(arg), assumptions) is False
|
| 219 |
+
for arg in mmul.args):
|
| 220 |
+
return False
|
| 221 |
+
|
| 222 |
+
@OrthogonalPredicate.register(MatPow)
|
| 223 |
+
def _(expr, assumptions):
|
| 224 |
+
# only for integer powers
|
| 225 |
+
base, exp = expr.args
|
| 226 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 227 |
+
if int_exp:
|
| 228 |
+
return ask(Q.orthogonal(base), assumptions)
|
| 229 |
+
return None
|
| 230 |
+
|
| 231 |
+
@OrthogonalPredicate.register(MatAdd)
|
| 232 |
+
def _(expr, assumptions):
|
| 233 |
+
if (len(expr.args) == 1 and
|
| 234 |
+
ask(Q.orthogonal(expr.args[0]), assumptions)):
|
| 235 |
+
return True
|
| 236 |
+
|
| 237 |
+
@OrthogonalPredicate.register(MatrixSymbol)
|
| 238 |
+
def _(expr, assumptions):
|
| 239 |
+
if (not expr.is_square or
|
| 240 |
+
ask(Q.invertible(expr), assumptions) is False):
|
| 241 |
+
return False
|
| 242 |
+
if Q.orthogonal(expr) in conjuncts(assumptions):
|
| 243 |
+
return True
|
| 244 |
+
|
| 245 |
+
@OrthogonalPredicate.register(Identity)
|
| 246 |
+
def _(expr, assumptions):
|
| 247 |
+
return True
|
| 248 |
+
|
| 249 |
+
@OrthogonalPredicate.register(ZeroMatrix)
|
| 250 |
+
def _(expr, assumptions):
|
| 251 |
+
return False
|
| 252 |
+
|
| 253 |
+
@OrthogonalPredicate.register_many(Inverse, Transpose)
|
| 254 |
+
def _(expr, assumptions):
|
| 255 |
+
return ask(Q.orthogonal(expr.arg), assumptions)
|
| 256 |
+
|
| 257 |
+
@OrthogonalPredicate.register(MatrixSlice)
|
| 258 |
+
def _(expr, assumptions):
|
| 259 |
+
if not expr.on_diag:
|
| 260 |
+
return None
|
| 261 |
+
else:
|
| 262 |
+
return ask(Q.orthogonal(expr.parent), assumptions)
|
| 263 |
+
|
| 264 |
+
@OrthogonalPredicate.register(Factorization)
|
| 265 |
+
def _(expr, assumptions):
|
| 266 |
+
return _Factorization(Q.orthogonal, expr, assumptions)
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
# UnitaryPredicate
|
| 270 |
+
|
| 271 |
+
@UnitaryPredicate.register(MatMul)
|
| 272 |
+
def _(expr, assumptions):
|
| 273 |
+
factor, mmul = expr.as_coeff_mmul()
|
| 274 |
+
if (all(ask(Q.unitary(arg), assumptions) for arg in mmul.args) and
|
| 275 |
+
abs(factor) == 1):
|
| 276 |
+
return True
|
| 277 |
+
if any(ask(Q.invertible(arg), assumptions) is False
|
| 278 |
+
for arg in mmul.args):
|
| 279 |
+
return False
|
| 280 |
+
|
| 281 |
+
@UnitaryPredicate.register(MatPow)
|
| 282 |
+
def _(expr, assumptions):
|
| 283 |
+
# only for integer powers
|
| 284 |
+
base, exp = expr.args
|
| 285 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 286 |
+
if int_exp:
|
| 287 |
+
return ask(Q.unitary(base), assumptions)
|
| 288 |
+
return None
|
| 289 |
+
|
| 290 |
+
@UnitaryPredicate.register(MatrixSymbol)
|
| 291 |
+
def _(expr, assumptions):
|
| 292 |
+
if (not expr.is_square or
|
| 293 |
+
ask(Q.invertible(expr), assumptions) is False):
|
| 294 |
+
return False
|
| 295 |
+
if Q.unitary(expr) in conjuncts(assumptions):
|
| 296 |
+
return True
|
| 297 |
+
|
| 298 |
+
@UnitaryPredicate.register_many(Inverse, Transpose)
|
| 299 |
+
def _(expr, assumptions):
|
| 300 |
+
return ask(Q.unitary(expr.arg), assumptions)
|
| 301 |
+
|
| 302 |
+
@UnitaryPredicate.register(MatrixSlice)
|
| 303 |
+
def _(expr, assumptions):
|
| 304 |
+
if not expr.on_diag:
|
| 305 |
+
return None
|
| 306 |
+
else:
|
| 307 |
+
return ask(Q.unitary(expr.parent), assumptions)
|
| 308 |
+
|
| 309 |
+
@UnitaryPredicate.register_many(DFT, Identity)
|
| 310 |
+
def _(expr, assumptions):
|
| 311 |
+
return True
|
| 312 |
+
|
| 313 |
+
@UnitaryPredicate.register(ZeroMatrix)
|
| 314 |
+
def _(expr, assumptions):
|
| 315 |
+
return False
|
| 316 |
+
|
| 317 |
+
@UnitaryPredicate.register(Factorization)
|
| 318 |
+
def _(expr, assumptions):
|
| 319 |
+
return _Factorization(Q.unitary, expr, assumptions)
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
# FullRankPredicate
|
| 323 |
+
|
| 324 |
+
@FullRankPredicate.register(MatMul)
|
| 325 |
+
def _(expr, assumptions):
|
| 326 |
+
if all(ask(Q.fullrank(arg), assumptions) for arg in expr.args):
|
| 327 |
+
return True
|
| 328 |
+
|
| 329 |
+
@FullRankPredicate.register(MatPow)
|
| 330 |
+
def _(expr, assumptions):
|
| 331 |
+
# only for integer powers
|
| 332 |
+
base, exp = expr.args
|
| 333 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 334 |
+
if int_exp and ask(~Q.negative(exp), assumptions):
|
| 335 |
+
return ask(Q.fullrank(base), assumptions)
|
| 336 |
+
return None
|
| 337 |
+
|
| 338 |
+
@FullRankPredicate.register(Identity)
|
| 339 |
+
def _(expr, assumptions):
|
| 340 |
+
return True
|
| 341 |
+
|
| 342 |
+
@FullRankPredicate.register(ZeroMatrix)
|
| 343 |
+
def _(expr, assumptions):
|
| 344 |
+
return False
|
| 345 |
+
|
| 346 |
+
@FullRankPredicate.register(OneMatrix)
|
| 347 |
+
def _(expr, assumptions):
|
| 348 |
+
return expr.shape[0] == 1 and expr.shape[1] == 1
|
| 349 |
+
|
| 350 |
+
@FullRankPredicate.register_many(Inverse, Transpose)
|
| 351 |
+
def _(expr, assumptions):
|
| 352 |
+
return ask(Q.fullrank(expr.arg), assumptions)
|
| 353 |
+
|
| 354 |
+
@FullRankPredicate.register(MatrixSlice)
|
| 355 |
+
def _(expr, assumptions):
|
| 356 |
+
if ask(Q.orthogonal(expr.parent), assumptions):
|
| 357 |
+
return True
|
| 358 |
+
|
| 359 |
+
|
| 360 |
+
# PositiveDefinitePredicate
|
| 361 |
+
|
| 362 |
+
@PositiveDefinitePredicate.register(MatMul)
|
| 363 |
+
def _(expr, assumptions):
|
| 364 |
+
factor, mmul = expr.as_coeff_mmul()
|
| 365 |
+
if (all(ask(Q.positive_definite(arg), assumptions)
|
| 366 |
+
for arg in mmul.args) and factor > 0):
|
| 367 |
+
return True
|
| 368 |
+
if (len(mmul.args) >= 2
|
| 369 |
+
and mmul.args[0] == mmul.args[-1].T
|
| 370 |
+
and ask(Q.fullrank(mmul.args[0]), assumptions)):
|
| 371 |
+
return ask(Q.positive_definite(
|
| 372 |
+
MatMul(*mmul.args[1:-1])), assumptions)
|
| 373 |
+
|
| 374 |
+
@PositiveDefinitePredicate.register(MatPow)
|
| 375 |
+
def _(expr, assumptions):
|
| 376 |
+
# a power of a positive definite matrix is positive definite
|
| 377 |
+
if ask(Q.positive_definite(expr.args[0]), assumptions):
|
| 378 |
+
return True
|
| 379 |
+
|
| 380 |
+
@PositiveDefinitePredicate.register(MatAdd)
|
| 381 |
+
def _(expr, assumptions):
|
| 382 |
+
if all(ask(Q.positive_definite(arg), assumptions)
|
| 383 |
+
for arg in expr.args):
|
| 384 |
+
return True
|
| 385 |
+
|
| 386 |
+
@PositiveDefinitePredicate.register(MatrixSymbol)
|
| 387 |
+
def _(expr, assumptions):
|
| 388 |
+
if not expr.is_square:
|
| 389 |
+
return False
|
| 390 |
+
if Q.positive_definite(expr) in conjuncts(assumptions):
|
| 391 |
+
return True
|
| 392 |
+
|
| 393 |
+
@PositiveDefinitePredicate.register(Identity)
|
| 394 |
+
def _(expr, assumptions):
|
| 395 |
+
return True
|
| 396 |
+
|
| 397 |
+
@PositiveDefinitePredicate.register(ZeroMatrix)
|
| 398 |
+
def _(expr, assumptions):
|
| 399 |
+
return False
|
| 400 |
+
|
| 401 |
+
@PositiveDefinitePredicate.register(OneMatrix)
|
| 402 |
+
def _(expr, assumptions):
|
| 403 |
+
return expr.shape[0] == 1 and expr.shape[1] == 1
|
| 404 |
+
|
| 405 |
+
@PositiveDefinitePredicate.register_many(Inverse, Transpose)
|
| 406 |
+
def _(expr, assumptions):
|
| 407 |
+
return ask(Q.positive_definite(expr.arg), assumptions)
|
| 408 |
+
|
| 409 |
+
@PositiveDefinitePredicate.register(MatrixSlice)
|
| 410 |
+
def _(expr, assumptions):
|
| 411 |
+
if not expr.on_diag:
|
| 412 |
+
return None
|
| 413 |
+
else:
|
| 414 |
+
return ask(Q.positive_definite(expr.parent), assumptions)
|
| 415 |
+
|
| 416 |
+
|
| 417 |
+
# UpperTriangularPredicate
|
| 418 |
+
|
| 419 |
+
@UpperTriangularPredicate.register(MatMul)
|
| 420 |
+
def _(expr, assumptions):
|
| 421 |
+
factor, matrices = expr.as_coeff_matrices()
|
| 422 |
+
if all(ask(Q.upper_triangular(m), assumptions) for m in matrices):
|
| 423 |
+
return True
|
| 424 |
+
|
| 425 |
+
@UpperTriangularPredicate.register(MatAdd)
|
| 426 |
+
def _(expr, assumptions):
|
| 427 |
+
if all(ask(Q.upper_triangular(arg), assumptions) for arg in expr.args):
|
| 428 |
+
return True
|
| 429 |
+
|
| 430 |
+
@UpperTriangularPredicate.register(MatPow)
|
| 431 |
+
def _(expr, assumptions):
|
| 432 |
+
# only for integer powers
|
| 433 |
+
base, exp = expr.args
|
| 434 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 435 |
+
if not int_exp:
|
| 436 |
+
return None
|
| 437 |
+
non_negative = ask(~Q.negative(exp), assumptions)
|
| 438 |
+
if (non_negative or non_negative == False
|
| 439 |
+
and ask(Q.invertible(base), assumptions)):
|
| 440 |
+
return ask(Q.upper_triangular(base), assumptions)
|
| 441 |
+
return None
|
| 442 |
+
|
| 443 |
+
@UpperTriangularPredicate.register(MatrixSymbol)
|
| 444 |
+
def _(expr, assumptions):
|
| 445 |
+
if Q.upper_triangular(expr) in conjuncts(assumptions):
|
| 446 |
+
return True
|
| 447 |
+
|
| 448 |
+
@UpperTriangularPredicate.register_many(Identity, ZeroMatrix)
|
| 449 |
+
def _(expr, assumptions):
|
| 450 |
+
return True
|
| 451 |
+
|
| 452 |
+
@UpperTriangularPredicate.register(OneMatrix)
|
| 453 |
+
def _(expr, assumptions):
|
| 454 |
+
return expr.shape[0] == 1 and expr.shape[1] == 1
|
| 455 |
+
|
| 456 |
+
@UpperTriangularPredicate.register(Transpose)
|
| 457 |
+
def _(expr, assumptions):
|
| 458 |
+
return ask(Q.lower_triangular(expr.arg), assumptions)
|
| 459 |
+
|
| 460 |
+
@UpperTriangularPredicate.register(Inverse)
|
| 461 |
+
def _(expr, assumptions):
|
| 462 |
+
return ask(Q.upper_triangular(expr.arg), assumptions)
|
| 463 |
+
|
| 464 |
+
@UpperTriangularPredicate.register(MatrixSlice)
|
| 465 |
+
def _(expr, assumptions):
|
| 466 |
+
if not expr.on_diag:
|
| 467 |
+
return None
|
| 468 |
+
else:
|
| 469 |
+
return ask(Q.upper_triangular(expr.parent), assumptions)
|
| 470 |
+
|
| 471 |
+
@UpperTriangularPredicate.register(Factorization)
|
| 472 |
+
def _(expr, assumptions):
|
| 473 |
+
return _Factorization(Q.upper_triangular, expr, assumptions)
|
| 474 |
+
|
| 475 |
+
# LowerTriangularPredicate
|
| 476 |
+
|
| 477 |
+
@LowerTriangularPredicate.register(MatMul)
|
| 478 |
+
def _(expr, assumptions):
|
| 479 |
+
factor, matrices = expr.as_coeff_matrices()
|
| 480 |
+
if all(ask(Q.lower_triangular(m), assumptions) for m in matrices):
|
| 481 |
+
return True
|
| 482 |
+
|
| 483 |
+
@LowerTriangularPredicate.register(MatAdd)
|
| 484 |
+
def _(expr, assumptions):
|
| 485 |
+
if all(ask(Q.lower_triangular(arg), assumptions) for arg in expr.args):
|
| 486 |
+
return True
|
| 487 |
+
|
| 488 |
+
@LowerTriangularPredicate.register(MatPow)
|
| 489 |
+
def _(expr, assumptions):
|
| 490 |
+
# only for integer powers
|
| 491 |
+
base, exp = expr.args
|
| 492 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 493 |
+
if not int_exp:
|
| 494 |
+
return None
|
| 495 |
+
non_negative = ask(~Q.negative(exp), assumptions)
|
| 496 |
+
if (non_negative or non_negative == False
|
| 497 |
+
and ask(Q.invertible(base), assumptions)):
|
| 498 |
+
return ask(Q.lower_triangular(base), assumptions)
|
| 499 |
+
return None
|
| 500 |
+
|
| 501 |
+
@LowerTriangularPredicate.register(MatrixSymbol)
|
| 502 |
+
def _(expr, assumptions):
|
| 503 |
+
if Q.lower_triangular(expr) in conjuncts(assumptions):
|
| 504 |
+
return True
|
| 505 |
+
|
| 506 |
+
@LowerTriangularPredicate.register_many(Identity, ZeroMatrix)
|
| 507 |
+
def _(expr, assumptions):
|
| 508 |
+
return True
|
| 509 |
+
|
| 510 |
+
@LowerTriangularPredicate.register(OneMatrix)
|
| 511 |
+
def _(expr, assumptions):
|
| 512 |
+
return expr.shape[0] == 1 and expr.shape[1] == 1
|
| 513 |
+
|
| 514 |
+
@LowerTriangularPredicate.register(Transpose)
|
| 515 |
+
def _(expr, assumptions):
|
| 516 |
+
return ask(Q.upper_triangular(expr.arg), assumptions)
|
| 517 |
+
|
| 518 |
+
@LowerTriangularPredicate.register(Inverse)
|
| 519 |
+
def _(expr, assumptions):
|
| 520 |
+
return ask(Q.lower_triangular(expr.arg), assumptions)
|
| 521 |
+
|
| 522 |
+
@LowerTriangularPredicate.register(MatrixSlice)
|
| 523 |
+
def _(expr, assumptions):
|
| 524 |
+
if not expr.on_diag:
|
| 525 |
+
return None
|
| 526 |
+
else:
|
| 527 |
+
return ask(Q.lower_triangular(expr.parent), assumptions)
|
| 528 |
+
|
| 529 |
+
@LowerTriangularPredicate.register(Factorization)
|
| 530 |
+
def _(expr, assumptions):
|
| 531 |
+
return _Factorization(Q.lower_triangular, expr, assumptions)
|
| 532 |
+
|
| 533 |
+
|
| 534 |
+
# DiagonalPredicate
|
| 535 |
+
|
| 536 |
+
def _is_empty_or_1x1(expr):
|
| 537 |
+
return expr.shape in ((0, 0), (1, 1))
|
| 538 |
+
|
| 539 |
+
@DiagonalPredicate.register(MatMul)
|
| 540 |
+
def _(expr, assumptions):
|
| 541 |
+
if _is_empty_or_1x1(expr):
|
| 542 |
+
return True
|
| 543 |
+
factor, matrices = expr.as_coeff_matrices()
|
| 544 |
+
if all(ask(Q.diagonal(m), assumptions) for m in matrices):
|
| 545 |
+
return True
|
| 546 |
+
|
| 547 |
+
@DiagonalPredicate.register(MatPow)
|
| 548 |
+
def _(expr, assumptions):
|
| 549 |
+
# only for integer powers
|
| 550 |
+
base, exp = expr.args
|
| 551 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 552 |
+
if not int_exp:
|
| 553 |
+
return None
|
| 554 |
+
non_negative = ask(~Q.negative(exp), assumptions)
|
| 555 |
+
if (non_negative or non_negative == False
|
| 556 |
+
and ask(Q.invertible(base), assumptions)):
|
| 557 |
+
return ask(Q.diagonal(base), assumptions)
|
| 558 |
+
return None
|
| 559 |
+
|
| 560 |
+
@DiagonalPredicate.register(MatAdd)
|
| 561 |
+
def _(expr, assumptions):
|
| 562 |
+
if all(ask(Q.diagonal(arg), assumptions) for arg in expr.args):
|
| 563 |
+
return True
|
| 564 |
+
|
| 565 |
+
@DiagonalPredicate.register(MatrixSymbol)
|
| 566 |
+
def _(expr, assumptions):
|
| 567 |
+
if _is_empty_or_1x1(expr):
|
| 568 |
+
return True
|
| 569 |
+
if Q.diagonal(expr) in conjuncts(assumptions):
|
| 570 |
+
return True
|
| 571 |
+
|
| 572 |
+
@DiagonalPredicate.register(OneMatrix)
|
| 573 |
+
def _(expr, assumptions):
|
| 574 |
+
return expr.shape[0] == 1 and expr.shape[1] == 1
|
| 575 |
+
|
| 576 |
+
@DiagonalPredicate.register_many(Inverse, Transpose)
|
| 577 |
+
def _(expr, assumptions):
|
| 578 |
+
return ask(Q.diagonal(expr.arg), assumptions)
|
| 579 |
+
|
| 580 |
+
@DiagonalPredicate.register(MatrixSlice)
|
| 581 |
+
def _(expr, assumptions):
|
| 582 |
+
if _is_empty_or_1x1(expr):
|
| 583 |
+
return True
|
| 584 |
+
if not expr.on_diag:
|
| 585 |
+
return None
|
| 586 |
+
else:
|
| 587 |
+
return ask(Q.diagonal(expr.parent), assumptions)
|
| 588 |
+
|
| 589 |
+
@DiagonalPredicate.register_many(DiagonalMatrix, DiagMatrix, Identity, ZeroMatrix)
|
| 590 |
+
def _(expr, assumptions):
|
| 591 |
+
return True
|
| 592 |
+
|
| 593 |
+
@DiagonalPredicate.register(Factorization)
|
| 594 |
+
def _(expr, assumptions):
|
| 595 |
+
return _Factorization(Q.diagonal, expr, assumptions)
|
| 596 |
+
|
| 597 |
+
|
| 598 |
+
# IntegerElementsPredicate
|
| 599 |
+
|
| 600 |
+
def BM_elements(predicate, expr, assumptions):
|
| 601 |
+
""" Block Matrix elements. """
|
| 602 |
+
return all(ask(predicate(b), assumptions) for b in expr.blocks)
|
| 603 |
+
|
| 604 |
+
def MS_elements(predicate, expr, assumptions):
|
| 605 |
+
""" Matrix Slice elements. """
|
| 606 |
+
return ask(predicate(expr.parent), assumptions)
|
| 607 |
+
|
| 608 |
+
def MatMul_elements(matrix_predicate, scalar_predicate, expr, assumptions):
|
| 609 |
+
d = sift(expr.args, lambda x: isinstance(x, MatrixExpr))
|
| 610 |
+
factors, matrices = d[False], d[True]
|
| 611 |
+
return fuzzy_and([
|
| 612 |
+
test_closed_group(Basic(*factors), assumptions, scalar_predicate),
|
| 613 |
+
test_closed_group(Basic(*matrices), assumptions, matrix_predicate)])
|
| 614 |
+
|
| 615 |
+
|
| 616 |
+
@IntegerElementsPredicate.register_many(Determinant, HadamardProduct, MatAdd,
|
| 617 |
+
Trace, Transpose)
|
| 618 |
+
def _(expr, assumptions):
|
| 619 |
+
return test_closed_group(expr, assumptions, Q.integer_elements)
|
| 620 |
+
|
| 621 |
+
@IntegerElementsPredicate.register(MatPow)
|
| 622 |
+
def _(expr, assumptions):
|
| 623 |
+
# only for integer powers
|
| 624 |
+
base, exp = expr.args
|
| 625 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 626 |
+
if not int_exp:
|
| 627 |
+
return None
|
| 628 |
+
if exp.is_negative == False:
|
| 629 |
+
return ask(Q.integer_elements(base), assumptions)
|
| 630 |
+
return None
|
| 631 |
+
|
| 632 |
+
@IntegerElementsPredicate.register_many(Identity, OneMatrix, ZeroMatrix)
|
| 633 |
+
def _(expr, assumptions):
|
| 634 |
+
return True
|
| 635 |
+
|
| 636 |
+
@IntegerElementsPredicate.register(MatMul)
|
| 637 |
+
def _(expr, assumptions):
|
| 638 |
+
return MatMul_elements(Q.integer_elements, Q.integer, expr, assumptions)
|
| 639 |
+
|
| 640 |
+
@IntegerElementsPredicate.register(MatrixSlice)
|
| 641 |
+
def _(expr, assumptions):
|
| 642 |
+
return MS_elements(Q.integer_elements, expr, assumptions)
|
| 643 |
+
|
| 644 |
+
@IntegerElementsPredicate.register(BlockMatrix)
|
| 645 |
+
def _(expr, assumptions):
|
| 646 |
+
return BM_elements(Q.integer_elements, expr, assumptions)
|
| 647 |
+
|
| 648 |
+
|
| 649 |
+
# RealElementsPredicate
|
| 650 |
+
|
| 651 |
+
@RealElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
|
| 652 |
+
MatAdd, Trace, Transpose)
|
| 653 |
+
def _(expr, assumptions):
|
| 654 |
+
return test_closed_group(expr, assumptions, Q.real_elements)
|
| 655 |
+
|
| 656 |
+
@RealElementsPredicate.register(MatPow)
|
| 657 |
+
def _(expr, assumptions):
|
| 658 |
+
# only for integer powers
|
| 659 |
+
base, exp = expr.args
|
| 660 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 661 |
+
if not int_exp:
|
| 662 |
+
return None
|
| 663 |
+
non_negative = ask(~Q.negative(exp), assumptions)
|
| 664 |
+
if (non_negative or non_negative == False
|
| 665 |
+
and ask(Q.invertible(base), assumptions)):
|
| 666 |
+
return ask(Q.real_elements(base), assumptions)
|
| 667 |
+
return None
|
| 668 |
+
|
| 669 |
+
@RealElementsPredicate.register(MatMul)
|
| 670 |
+
def _(expr, assumptions):
|
| 671 |
+
return MatMul_elements(Q.real_elements, Q.real, expr, assumptions)
|
| 672 |
+
|
| 673 |
+
@RealElementsPredicate.register(MatrixSlice)
|
| 674 |
+
def _(expr, assumptions):
|
| 675 |
+
return MS_elements(Q.real_elements, expr, assumptions)
|
| 676 |
+
|
| 677 |
+
@RealElementsPredicate.register(BlockMatrix)
|
| 678 |
+
def _(expr, assumptions):
|
| 679 |
+
return BM_elements(Q.real_elements, expr, assumptions)
|
| 680 |
+
|
| 681 |
+
|
| 682 |
+
# ComplexElementsPredicate
|
| 683 |
+
|
| 684 |
+
@ComplexElementsPredicate.register_many(Determinant, Factorization, HadamardProduct,
|
| 685 |
+
Inverse, MatAdd, Trace, Transpose)
|
| 686 |
+
def _(expr, assumptions):
|
| 687 |
+
return test_closed_group(expr, assumptions, Q.complex_elements)
|
| 688 |
+
|
| 689 |
+
@ComplexElementsPredicate.register(MatPow)
|
| 690 |
+
def _(expr, assumptions):
|
| 691 |
+
# only for integer powers
|
| 692 |
+
base, exp = expr.args
|
| 693 |
+
int_exp = ask(Q.integer(exp), assumptions)
|
| 694 |
+
if not int_exp:
|
| 695 |
+
return None
|
| 696 |
+
non_negative = ask(~Q.negative(exp), assumptions)
|
| 697 |
+
if (non_negative or non_negative == False
|
| 698 |
+
and ask(Q.invertible(base), assumptions)):
|
| 699 |
+
return ask(Q.complex_elements(base), assumptions)
|
| 700 |
+
return None
|
| 701 |
+
|
| 702 |
+
@ComplexElementsPredicate.register(MatMul)
|
| 703 |
+
def _(expr, assumptions):
|
| 704 |
+
return MatMul_elements(Q.complex_elements, Q.complex, expr, assumptions)
|
| 705 |
+
|
| 706 |
+
@ComplexElementsPredicate.register(MatrixSlice)
|
| 707 |
+
def _(expr, assumptions):
|
| 708 |
+
return MS_elements(Q.complex_elements, expr, assumptions)
|
| 709 |
+
|
| 710 |
+
@ComplexElementsPredicate.register(BlockMatrix)
|
| 711 |
+
def _(expr, assumptions):
|
| 712 |
+
return BM_elements(Q.complex_elements, expr, assumptions)
|
| 713 |
+
|
| 714 |
+
@ComplexElementsPredicate.register(DFT)
|
| 715 |
+
def _(expr, assumptions):
|
| 716 |
+
return True
|
lib/python3.12/site-packages/sympy/assumptions/handlers/ntheory.py
ADDED
|
@@ -0,0 +1,269 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Handlers for keys related to number theory: prime, even, odd, etc.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from sympy.assumptions import Q, ask
|
| 6 |
+
from sympy.core import Add, Basic, Expr, Float, Mul, Pow, S
|
| 7 |
+
from sympy.core.numbers import (ImaginaryUnit, Infinity, Integer, NaN,
|
| 8 |
+
NegativeInfinity, NumberSymbol, Rational, int_valued)
|
| 9 |
+
from sympy.functions import Abs, im, re
|
| 10 |
+
from sympy.ntheory import isprime
|
| 11 |
+
|
| 12 |
+
from sympy.multipledispatch import MDNotImplementedError
|
| 13 |
+
|
| 14 |
+
from ..predicates.ntheory import (PrimePredicate, CompositePredicate,
|
| 15 |
+
EvenPredicate, OddPredicate)
|
| 16 |
+
|
| 17 |
+
|
| 18 |
+
# PrimePredicate
|
| 19 |
+
|
| 20 |
+
def _PrimePredicate_number(expr, assumptions):
|
| 21 |
+
# helper method
|
| 22 |
+
exact = not expr.atoms(Float)
|
| 23 |
+
try:
|
| 24 |
+
i = int(expr.round())
|
| 25 |
+
if (expr - i).equals(0) is False:
|
| 26 |
+
raise TypeError
|
| 27 |
+
except TypeError:
|
| 28 |
+
return False
|
| 29 |
+
if exact:
|
| 30 |
+
return isprime(i)
|
| 31 |
+
# when not exact, we won't give a True or False
|
| 32 |
+
# since the number represents an approximate value
|
| 33 |
+
|
| 34 |
+
@PrimePredicate.register(Expr)
|
| 35 |
+
def _(expr, assumptions):
|
| 36 |
+
ret = expr.is_prime
|
| 37 |
+
if ret is None:
|
| 38 |
+
raise MDNotImplementedError
|
| 39 |
+
return ret
|
| 40 |
+
|
| 41 |
+
@PrimePredicate.register(Basic)
|
| 42 |
+
def _(expr, assumptions):
|
| 43 |
+
if expr.is_number:
|
| 44 |
+
return _PrimePredicate_number(expr, assumptions)
|
| 45 |
+
|
| 46 |
+
@PrimePredicate.register(Mul)
|
| 47 |
+
def _(expr, assumptions):
|
| 48 |
+
if expr.is_number:
|
| 49 |
+
return _PrimePredicate_number(expr, assumptions)
|
| 50 |
+
for arg in expr.args:
|
| 51 |
+
if not ask(Q.integer(arg), assumptions):
|
| 52 |
+
return None
|
| 53 |
+
for arg in expr.args:
|
| 54 |
+
if arg.is_number and arg.is_composite:
|
| 55 |
+
return False
|
| 56 |
+
|
| 57 |
+
@PrimePredicate.register(Pow)
|
| 58 |
+
def _(expr, assumptions):
|
| 59 |
+
"""
|
| 60 |
+
Integer**Integer -> !Prime
|
| 61 |
+
"""
|
| 62 |
+
if expr.is_number:
|
| 63 |
+
return _PrimePredicate_number(expr, assumptions)
|
| 64 |
+
if ask(Q.integer(expr.exp), assumptions) and \
|
| 65 |
+
ask(Q.integer(expr.base), assumptions):
|
| 66 |
+
return False
|
| 67 |
+
|
| 68 |
+
@PrimePredicate.register(Integer)
|
| 69 |
+
def _(expr, assumptions):
|
| 70 |
+
return isprime(expr)
|
| 71 |
+
|
| 72 |
+
@PrimePredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
|
| 73 |
+
def _(expr, assumptions):
|
| 74 |
+
return False
|
| 75 |
+
|
| 76 |
+
@PrimePredicate.register(Float)
|
| 77 |
+
def _(expr, assumptions):
|
| 78 |
+
return _PrimePredicate_number(expr, assumptions)
|
| 79 |
+
|
| 80 |
+
@PrimePredicate.register(NumberSymbol)
|
| 81 |
+
def _(expr, assumptions):
|
| 82 |
+
return _PrimePredicate_number(expr, assumptions)
|
| 83 |
+
|
| 84 |
+
@PrimePredicate.register(NaN)
|
| 85 |
+
def _(expr, assumptions):
|
| 86 |
+
return None
|
| 87 |
+
|
| 88 |
+
|
| 89 |
+
# CompositePredicate
|
| 90 |
+
|
| 91 |
+
@CompositePredicate.register(Expr)
|
| 92 |
+
def _(expr, assumptions):
|
| 93 |
+
ret = expr.is_composite
|
| 94 |
+
if ret is None:
|
| 95 |
+
raise MDNotImplementedError
|
| 96 |
+
return ret
|
| 97 |
+
|
| 98 |
+
@CompositePredicate.register(Basic)
|
| 99 |
+
def _(expr, assumptions):
|
| 100 |
+
_positive = ask(Q.positive(expr), assumptions)
|
| 101 |
+
if _positive:
|
| 102 |
+
_integer = ask(Q.integer(expr), assumptions)
|
| 103 |
+
if _integer:
|
| 104 |
+
_prime = ask(Q.prime(expr), assumptions)
|
| 105 |
+
if _prime is None:
|
| 106 |
+
return
|
| 107 |
+
# Positive integer which is not prime is not
|
| 108 |
+
# necessarily composite
|
| 109 |
+
if expr.equals(1):
|
| 110 |
+
return False
|
| 111 |
+
return not _prime
|
| 112 |
+
else:
|
| 113 |
+
return _integer
|
| 114 |
+
else:
|
| 115 |
+
return _positive
|
| 116 |
+
|
| 117 |
+
|
| 118 |
+
# EvenPredicate
|
| 119 |
+
|
| 120 |
+
def _EvenPredicate_number(expr, assumptions):
|
| 121 |
+
# helper method
|
| 122 |
+
if isinstance(expr, (float, Float)):
|
| 123 |
+
if int_valued(expr):
|
| 124 |
+
return None
|
| 125 |
+
return False
|
| 126 |
+
try:
|
| 127 |
+
i = int(expr.round())
|
| 128 |
+
except TypeError:
|
| 129 |
+
return False
|
| 130 |
+
if not (expr - i).equals(0):
|
| 131 |
+
return False
|
| 132 |
+
return i % 2 == 0
|
| 133 |
+
|
| 134 |
+
@EvenPredicate.register(Expr)
|
| 135 |
+
def _(expr, assumptions):
|
| 136 |
+
ret = expr.is_even
|
| 137 |
+
if ret is None:
|
| 138 |
+
raise MDNotImplementedError
|
| 139 |
+
return ret
|
| 140 |
+
|
| 141 |
+
@EvenPredicate.register(Basic)
|
| 142 |
+
def _(expr, assumptions):
|
| 143 |
+
if expr.is_number:
|
| 144 |
+
return _EvenPredicate_number(expr, assumptions)
|
| 145 |
+
|
| 146 |
+
@EvenPredicate.register(Mul)
|
| 147 |
+
def _(expr, assumptions):
|
| 148 |
+
"""
|
| 149 |
+
Even * Integer -> Even
|
| 150 |
+
Even * Odd -> Even
|
| 151 |
+
Integer * Odd -> ?
|
| 152 |
+
Odd * Odd -> Odd
|
| 153 |
+
Even * Even -> Even
|
| 154 |
+
Integer * Integer -> Even if Integer + Integer = Odd
|
| 155 |
+
otherwise -> ?
|
| 156 |
+
"""
|
| 157 |
+
if expr.is_number:
|
| 158 |
+
return _EvenPredicate_number(expr, assumptions)
|
| 159 |
+
even, odd, irrational, acc = False, 0, False, 1
|
| 160 |
+
for arg in expr.args:
|
| 161 |
+
# check for all integers and at least one even
|
| 162 |
+
if ask(Q.integer(arg), assumptions):
|
| 163 |
+
if ask(Q.even(arg), assumptions):
|
| 164 |
+
even = True
|
| 165 |
+
elif ask(Q.odd(arg), assumptions):
|
| 166 |
+
odd += 1
|
| 167 |
+
elif not even and acc != 1:
|
| 168 |
+
if ask(Q.odd(acc + arg), assumptions):
|
| 169 |
+
even = True
|
| 170 |
+
elif ask(Q.irrational(arg), assumptions):
|
| 171 |
+
# one irrational makes the result False
|
| 172 |
+
# two makes it undefined
|
| 173 |
+
if irrational:
|
| 174 |
+
break
|
| 175 |
+
irrational = True
|
| 176 |
+
else:
|
| 177 |
+
break
|
| 178 |
+
acc = arg
|
| 179 |
+
else:
|
| 180 |
+
if irrational:
|
| 181 |
+
return False
|
| 182 |
+
if even:
|
| 183 |
+
return True
|
| 184 |
+
if odd == len(expr.args):
|
| 185 |
+
return False
|
| 186 |
+
|
| 187 |
+
@EvenPredicate.register(Add)
|
| 188 |
+
def _(expr, assumptions):
|
| 189 |
+
"""
|
| 190 |
+
Even + Odd -> Odd
|
| 191 |
+
Even + Even -> Even
|
| 192 |
+
Odd + Odd -> Even
|
| 193 |
+
|
| 194 |
+
"""
|
| 195 |
+
if expr.is_number:
|
| 196 |
+
return _EvenPredicate_number(expr, assumptions)
|
| 197 |
+
_result = True
|
| 198 |
+
for arg in expr.args:
|
| 199 |
+
if ask(Q.even(arg), assumptions):
|
| 200 |
+
pass
|
| 201 |
+
elif ask(Q.odd(arg), assumptions):
|
| 202 |
+
_result = not _result
|
| 203 |
+
else:
|
| 204 |
+
break
|
| 205 |
+
else:
|
| 206 |
+
return _result
|
| 207 |
+
|
| 208 |
+
@EvenPredicate.register(Pow)
|
| 209 |
+
def _(expr, assumptions):
|
| 210 |
+
if expr.is_number:
|
| 211 |
+
return _EvenPredicate_number(expr, assumptions)
|
| 212 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 213 |
+
if ask(Q.positive(expr.exp), assumptions):
|
| 214 |
+
return ask(Q.even(expr.base), assumptions)
|
| 215 |
+
elif ask(~Q.negative(expr.exp) & Q.odd(expr.base), assumptions):
|
| 216 |
+
return False
|
| 217 |
+
elif expr.base is S.NegativeOne:
|
| 218 |
+
return False
|
| 219 |
+
|
| 220 |
+
@EvenPredicate.register(Integer)
|
| 221 |
+
def _(expr, assumptions):
|
| 222 |
+
return not bool(expr.p & 1)
|
| 223 |
+
|
| 224 |
+
@EvenPredicate.register_many(Rational, Infinity, NegativeInfinity, ImaginaryUnit)
|
| 225 |
+
def _(expr, assumptions):
|
| 226 |
+
return False
|
| 227 |
+
|
| 228 |
+
@EvenPredicate.register(NumberSymbol)
|
| 229 |
+
def _(expr, assumptions):
|
| 230 |
+
return _EvenPredicate_number(expr, assumptions)
|
| 231 |
+
|
| 232 |
+
@EvenPredicate.register(Abs)
|
| 233 |
+
def _(expr, assumptions):
|
| 234 |
+
if ask(Q.real(expr.args[0]), assumptions):
|
| 235 |
+
return ask(Q.even(expr.args[0]), assumptions)
|
| 236 |
+
|
| 237 |
+
@EvenPredicate.register(re)
|
| 238 |
+
def _(expr, assumptions):
|
| 239 |
+
if ask(Q.real(expr.args[0]), assumptions):
|
| 240 |
+
return ask(Q.even(expr.args[0]), assumptions)
|
| 241 |
+
|
| 242 |
+
@EvenPredicate.register(im)
|
| 243 |
+
def _(expr, assumptions):
|
| 244 |
+
if ask(Q.real(expr.args[0]), assumptions):
|
| 245 |
+
return True
|
| 246 |
+
|
| 247 |
+
@EvenPredicate.register(NaN)
|
| 248 |
+
def _(expr, assumptions):
|
| 249 |
+
return None
|
| 250 |
+
|
| 251 |
+
|
| 252 |
+
# OddPredicate
|
| 253 |
+
|
| 254 |
+
@OddPredicate.register(Expr)
|
| 255 |
+
def _(expr, assumptions):
|
| 256 |
+
ret = expr.is_odd
|
| 257 |
+
if ret is None:
|
| 258 |
+
raise MDNotImplementedError
|
| 259 |
+
return ret
|
| 260 |
+
|
| 261 |
+
@OddPredicate.register(Basic)
|
| 262 |
+
def _(expr, assumptions):
|
| 263 |
+
_integer = ask(Q.integer(expr), assumptions)
|
| 264 |
+
if _integer:
|
| 265 |
+
_even = ask(Q.even(expr), assumptions)
|
| 266 |
+
if _even is None:
|
| 267 |
+
return None
|
| 268 |
+
return not _even
|
| 269 |
+
return _integer
|
lib/python3.12/site-packages/sympy/assumptions/handlers/order.py
ADDED
|
@@ -0,0 +1,436 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Handlers related to order relations: positive, negative, etc.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from sympy.assumptions import Q, ask
|
| 6 |
+
from sympy.core import Add, Basic, Expr, Mul, Pow
|
| 7 |
+
from sympy.core.logic import fuzzy_not, fuzzy_and, fuzzy_or
|
| 8 |
+
from sympy.core.numbers import E, ImaginaryUnit, NaN, I, pi
|
| 9 |
+
from sympy.functions import Abs, acos, acot, asin, atan, exp, factorial, log
|
| 10 |
+
from sympy.matrices import Determinant, Trace
|
| 11 |
+
from sympy.matrices.expressions.matexpr import MatrixElement
|
| 12 |
+
|
| 13 |
+
from sympy.multipledispatch import MDNotImplementedError
|
| 14 |
+
|
| 15 |
+
from ..predicates.order import (NegativePredicate, NonNegativePredicate,
|
| 16 |
+
NonZeroPredicate, ZeroPredicate, NonPositivePredicate, PositivePredicate,
|
| 17 |
+
ExtendedNegativePredicate, ExtendedNonNegativePredicate,
|
| 18 |
+
ExtendedNonPositivePredicate, ExtendedNonZeroPredicate,
|
| 19 |
+
ExtendedPositivePredicate,)
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
# NegativePredicate
|
| 23 |
+
|
| 24 |
+
def _NegativePredicate_number(expr, assumptions):
|
| 25 |
+
r, i = expr.as_real_imag()
|
| 26 |
+
# If the imaginary part can symbolically be shown to be zero then
|
| 27 |
+
# we just evaluate the real part; otherwise we evaluate the imaginary
|
| 28 |
+
# part to see if it actually evaluates to zero and if it does then
|
| 29 |
+
# we make the comparison between the real part and zero.
|
| 30 |
+
if not i:
|
| 31 |
+
r = r.evalf(2)
|
| 32 |
+
if r._prec != 1:
|
| 33 |
+
return r < 0
|
| 34 |
+
else:
|
| 35 |
+
i = i.evalf(2)
|
| 36 |
+
if i._prec != 1:
|
| 37 |
+
if i != 0:
|
| 38 |
+
return False
|
| 39 |
+
r = r.evalf(2)
|
| 40 |
+
if r._prec != 1:
|
| 41 |
+
return r < 0
|
| 42 |
+
|
| 43 |
+
@NegativePredicate.register(Basic)
|
| 44 |
+
def _(expr, assumptions):
|
| 45 |
+
if expr.is_number:
|
| 46 |
+
return _NegativePredicate_number(expr, assumptions)
|
| 47 |
+
|
| 48 |
+
@NegativePredicate.register(Expr)
|
| 49 |
+
def _(expr, assumptions):
|
| 50 |
+
ret = expr.is_negative
|
| 51 |
+
if ret is None:
|
| 52 |
+
raise MDNotImplementedError
|
| 53 |
+
return ret
|
| 54 |
+
|
| 55 |
+
@NegativePredicate.register(Add)
|
| 56 |
+
def _(expr, assumptions):
|
| 57 |
+
"""
|
| 58 |
+
Positive + Positive -> Positive,
|
| 59 |
+
Negative + Negative -> Negative
|
| 60 |
+
"""
|
| 61 |
+
if expr.is_number:
|
| 62 |
+
return _NegativePredicate_number(expr, assumptions)
|
| 63 |
+
|
| 64 |
+
r = ask(Q.real(expr), assumptions)
|
| 65 |
+
if r is not True:
|
| 66 |
+
return r
|
| 67 |
+
|
| 68 |
+
nonpos = 0
|
| 69 |
+
for arg in expr.args:
|
| 70 |
+
if ask(Q.negative(arg), assumptions) is not True:
|
| 71 |
+
if ask(Q.positive(arg), assumptions) is False:
|
| 72 |
+
nonpos += 1
|
| 73 |
+
else:
|
| 74 |
+
break
|
| 75 |
+
else:
|
| 76 |
+
if nonpos < len(expr.args):
|
| 77 |
+
return True
|
| 78 |
+
|
| 79 |
+
@NegativePredicate.register(Mul)
|
| 80 |
+
def _(expr, assumptions):
|
| 81 |
+
if expr.is_number:
|
| 82 |
+
return _NegativePredicate_number(expr, assumptions)
|
| 83 |
+
result = None
|
| 84 |
+
for arg in expr.args:
|
| 85 |
+
if result is None:
|
| 86 |
+
result = False
|
| 87 |
+
if ask(Q.negative(arg), assumptions):
|
| 88 |
+
result = not result
|
| 89 |
+
elif ask(Q.positive(arg), assumptions):
|
| 90 |
+
pass
|
| 91 |
+
else:
|
| 92 |
+
return
|
| 93 |
+
return result
|
| 94 |
+
|
| 95 |
+
@NegativePredicate.register(Pow)
|
| 96 |
+
def _(expr, assumptions):
|
| 97 |
+
"""
|
| 98 |
+
Real ** Even -> NonNegative
|
| 99 |
+
Real ** Odd -> same_as_base
|
| 100 |
+
NonNegative ** Positive -> NonNegative
|
| 101 |
+
"""
|
| 102 |
+
if expr.base == E:
|
| 103 |
+
# Exponential is always positive:
|
| 104 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 105 |
+
return False
|
| 106 |
+
return
|
| 107 |
+
|
| 108 |
+
if expr.is_number:
|
| 109 |
+
return _NegativePredicate_number(expr, assumptions)
|
| 110 |
+
if ask(Q.real(expr.base), assumptions):
|
| 111 |
+
if ask(Q.positive(expr.base), assumptions):
|
| 112 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 113 |
+
return False
|
| 114 |
+
if ask(Q.even(expr.exp), assumptions):
|
| 115 |
+
return False
|
| 116 |
+
if ask(Q.odd(expr.exp), assumptions):
|
| 117 |
+
return ask(Q.negative(expr.base), assumptions)
|
| 118 |
+
|
| 119 |
+
@NegativePredicate.register_many(Abs, ImaginaryUnit)
|
| 120 |
+
def _(expr, assumptions):
|
| 121 |
+
return False
|
| 122 |
+
|
| 123 |
+
@NegativePredicate.register(exp)
|
| 124 |
+
def _(expr, assumptions):
|
| 125 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 126 |
+
return False
|
| 127 |
+
raise MDNotImplementedError
|
| 128 |
+
|
| 129 |
+
|
| 130 |
+
# NonNegativePredicate
|
| 131 |
+
|
| 132 |
+
@NonNegativePredicate.register(Basic)
|
| 133 |
+
def _(expr, assumptions):
|
| 134 |
+
if expr.is_number:
|
| 135 |
+
notnegative = fuzzy_not(_NegativePredicate_number(expr, assumptions))
|
| 136 |
+
if notnegative:
|
| 137 |
+
return ask(Q.real(expr), assumptions)
|
| 138 |
+
else:
|
| 139 |
+
return notnegative
|
| 140 |
+
|
| 141 |
+
@NonNegativePredicate.register(Expr)
|
| 142 |
+
def _(expr, assumptions):
|
| 143 |
+
ret = expr.is_nonnegative
|
| 144 |
+
if ret is None:
|
| 145 |
+
raise MDNotImplementedError
|
| 146 |
+
return ret
|
| 147 |
+
|
| 148 |
+
|
| 149 |
+
# NonZeroPredicate
|
| 150 |
+
|
| 151 |
+
@NonZeroPredicate.register(Expr)
|
| 152 |
+
def _(expr, assumptions):
|
| 153 |
+
ret = expr.is_nonzero
|
| 154 |
+
if ret is None:
|
| 155 |
+
raise MDNotImplementedError
|
| 156 |
+
return ret
|
| 157 |
+
|
| 158 |
+
@NonZeroPredicate.register(Basic)
|
| 159 |
+
def _(expr, assumptions):
|
| 160 |
+
if ask(Q.real(expr)) is False:
|
| 161 |
+
return False
|
| 162 |
+
if expr.is_number:
|
| 163 |
+
# if there are no symbols just evalf
|
| 164 |
+
i = expr.evalf(2)
|
| 165 |
+
def nonz(i):
|
| 166 |
+
if i._prec != 1:
|
| 167 |
+
return i != 0
|
| 168 |
+
return fuzzy_or(nonz(i) for i in i.as_real_imag())
|
| 169 |
+
|
| 170 |
+
@NonZeroPredicate.register(Add)
|
| 171 |
+
def _(expr, assumptions):
|
| 172 |
+
if all(ask(Q.positive(x), assumptions) for x in expr.args) \
|
| 173 |
+
or all(ask(Q.negative(x), assumptions) for x in expr.args):
|
| 174 |
+
return True
|
| 175 |
+
|
| 176 |
+
@NonZeroPredicate.register(Mul)
|
| 177 |
+
def _(expr, assumptions):
|
| 178 |
+
for arg in expr.args:
|
| 179 |
+
result = ask(Q.nonzero(arg), assumptions)
|
| 180 |
+
if result:
|
| 181 |
+
continue
|
| 182 |
+
return result
|
| 183 |
+
return True
|
| 184 |
+
|
| 185 |
+
@NonZeroPredicate.register(Pow)
|
| 186 |
+
def _(expr, assumptions):
|
| 187 |
+
return ask(Q.nonzero(expr.base), assumptions)
|
| 188 |
+
|
| 189 |
+
@NonZeroPredicate.register(Abs)
|
| 190 |
+
def _(expr, assumptions):
|
| 191 |
+
return ask(Q.nonzero(expr.args[0]), assumptions)
|
| 192 |
+
|
| 193 |
+
@NonZeroPredicate.register(NaN)
|
| 194 |
+
def _(expr, assumptions):
|
| 195 |
+
return None
|
| 196 |
+
|
| 197 |
+
|
| 198 |
+
# ZeroPredicate
|
| 199 |
+
|
| 200 |
+
@ZeroPredicate.register(Expr)
|
| 201 |
+
def _(expr, assumptions):
|
| 202 |
+
ret = expr.is_zero
|
| 203 |
+
if ret is None:
|
| 204 |
+
raise MDNotImplementedError
|
| 205 |
+
return ret
|
| 206 |
+
|
| 207 |
+
@ZeroPredicate.register(Basic)
|
| 208 |
+
def _(expr, assumptions):
|
| 209 |
+
return fuzzy_and([fuzzy_not(ask(Q.nonzero(expr), assumptions)),
|
| 210 |
+
ask(Q.real(expr), assumptions)])
|
| 211 |
+
|
| 212 |
+
@ZeroPredicate.register(Mul)
|
| 213 |
+
def _(expr, assumptions):
|
| 214 |
+
# TODO: This should be deducible from the nonzero handler
|
| 215 |
+
return fuzzy_or(ask(Q.zero(arg), assumptions) for arg in expr.args)
|
| 216 |
+
|
| 217 |
+
|
| 218 |
+
# NonPositivePredicate
|
| 219 |
+
|
| 220 |
+
@NonPositivePredicate.register(Expr)
|
| 221 |
+
def _(expr, assumptions):
|
| 222 |
+
ret = expr.is_nonpositive
|
| 223 |
+
if ret is None:
|
| 224 |
+
raise MDNotImplementedError
|
| 225 |
+
return ret
|
| 226 |
+
|
| 227 |
+
@NonPositivePredicate.register(Basic)
|
| 228 |
+
def _(expr, assumptions):
|
| 229 |
+
if expr.is_number:
|
| 230 |
+
notpositive = fuzzy_not(_PositivePredicate_number(expr, assumptions))
|
| 231 |
+
if notpositive:
|
| 232 |
+
return ask(Q.real(expr), assumptions)
|
| 233 |
+
else:
|
| 234 |
+
return notpositive
|
| 235 |
+
|
| 236 |
+
|
| 237 |
+
# PositivePredicate
|
| 238 |
+
|
| 239 |
+
def _PositivePredicate_number(expr, assumptions):
|
| 240 |
+
r, i = expr.as_real_imag()
|
| 241 |
+
# If the imaginary part can symbolically be shown to be zero then
|
| 242 |
+
# we just evaluate the real part; otherwise we evaluate the imaginary
|
| 243 |
+
# part to see if it actually evaluates to zero and if it does then
|
| 244 |
+
# we make the comparison between the real part and zero.
|
| 245 |
+
if not i:
|
| 246 |
+
r = r.evalf(2)
|
| 247 |
+
if r._prec != 1:
|
| 248 |
+
return r > 0
|
| 249 |
+
else:
|
| 250 |
+
i = i.evalf(2)
|
| 251 |
+
if i._prec != 1:
|
| 252 |
+
if i != 0:
|
| 253 |
+
return False
|
| 254 |
+
r = r.evalf(2)
|
| 255 |
+
if r._prec != 1:
|
| 256 |
+
return r > 0
|
| 257 |
+
|
| 258 |
+
@PositivePredicate.register(Expr)
|
| 259 |
+
def _(expr, assumptions):
|
| 260 |
+
ret = expr.is_positive
|
| 261 |
+
if ret is None:
|
| 262 |
+
raise MDNotImplementedError
|
| 263 |
+
return ret
|
| 264 |
+
|
| 265 |
+
@PositivePredicate.register(Basic)
|
| 266 |
+
def _(expr, assumptions):
|
| 267 |
+
if expr.is_number:
|
| 268 |
+
return _PositivePredicate_number(expr, assumptions)
|
| 269 |
+
|
| 270 |
+
@PositivePredicate.register(Mul)
|
| 271 |
+
def _(expr, assumptions):
|
| 272 |
+
if expr.is_number:
|
| 273 |
+
return _PositivePredicate_number(expr, assumptions)
|
| 274 |
+
result = True
|
| 275 |
+
for arg in expr.args:
|
| 276 |
+
if ask(Q.positive(arg), assumptions):
|
| 277 |
+
continue
|
| 278 |
+
elif ask(Q.negative(arg), assumptions):
|
| 279 |
+
result = result ^ True
|
| 280 |
+
else:
|
| 281 |
+
return
|
| 282 |
+
return result
|
| 283 |
+
|
| 284 |
+
@PositivePredicate.register(Add)
|
| 285 |
+
def _(expr, assumptions):
|
| 286 |
+
if expr.is_number:
|
| 287 |
+
return _PositivePredicate_number(expr, assumptions)
|
| 288 |
+
|
| 289 |
+
r = ask(Q.real(expr), assumptions)
|
| 290 |
+
if r is not True:
|
| 291 |
+
return r
|
| 292 |
+
|
| 293 |
+
nonneg = 0
|
| 294 |
+
for arg in expr.args:
|
| 295 |
+
if ask(Q.positive(arg), assumptions) is not True:
|
| 296 |
+
if ask(Q.negative(arg), assumptions) is False:
|
| 297 |
+
nonneg += 1
|
| 298 |
+
else:
|
| 299 |
+
break
|
| 300 |
+
else:
|
| 301 |
+
if nonneg < len(expr.args):
|
| 302 |
+
return True
|
| 303 |
+
|
| 304 |
+
@PositivePredicate.register(Pow)
|
| 305 |
+
def _(expr, assumptions):
|
| 306 |
+
if expr.base == E:
|
| 307 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 308 |
+
return True
|
| 309 |
+
if ask(Q.imaginary(expr.exp), assumptions):
|
| 310 |
+
return ask(Q.even(expr.exp/(I*pi)), assumptions)
|
| 311 |
+
return
|
| 312 |
+
|
| 313 |
+
if expr.is_number:
|
| 314 |
+
return _PositivePredicate_number(expr, assumptions)
|
| 315 |
+
if ask(Q.positive(expr.base), assumptions):
|
| 316 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 317 |
+
return True
|
| 318 |
+
if ask(Q.negative(expr.base), assumptions):
|
| 319 |
+
if ask(Q.even(expr.exp), assumptions):
|
| 320 |
+
return True
|
| 321 |
+
if ask(Q.odd(expr.exp), assumptions):
|
| 322 |
+
return False
|
| 323 |
+
|
| 324 |
+
@PositivePredicate.register(exp)
|
| 325 |
+
def _(expr, assumptions):
|
| 326 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 327 |
+
return True
|
| 328 |
+
if ask(Q.imaginary(expr.exp), assumptions):
|
| 329 |
+
return ask(Q.even(expr.exp/(I*pi)), assumptions)
|
| 330 |
+
|
| 331 |
+
@PositivePredicate.register(log)
|
| 332 |
+
def _(expr, assumptions):
|
| 333 |
+
r = ask(Q.real(expr.args[0]), assumptions)
|
| 334 |
+
if r is not True:
|
| 335 |
+
return r
|
| 336 |
+
if ask(Q.positive(expr.args[0] - 1), assumptions):
|
| 337 |
+
return True
|
| 338 |
+
if ask(Q.negative(expr.args[0] - 1), assumptions):
|
| 339 |
+
return False
|
| 340 |
+
|
| 341 |
+
@PositivePredicate.register(factorial)
|
| 342 |
+
def _(expr, assumptions):
|
| 343 |
+
x = expr.args[0]
|
| 344 |
+
if ask(Q.integer(x) & Q.positive(x), assumptions):
|
| 345 |
+
return True
|
| 346 |
+
|
| 347 |
+
@PositivePredicate.register(ImaginaryUnit)
|
| 348 |
+
def _(expr, assumptions):
|
| 349 |
+
return False
|
| 350 |
+
|
| 351 |
+
@PositivePredicate.register(Abs)
|
| 352 |
+
def _(expr, assumptions):
|
| 353 |
+
return ask(Q.nonzero(expr), assumptions)
|
| 354 |
+
|
| 355 |
+
@PositivePredicate.register(Trace)
|
| 356 |
+
def _(expr, assumptions):
|
| 357 |
+
if ask(Q.positive_definite(expr.arg), assumptions):
|
| 358 |
+
return True
|
| 359 |
+
|
| 360 |
+
@PositivePredicate.register(Determinant)
|
| 361 |
+
def _(expr, assumptions):
|
| 362 |
+
if ask(Q.positive_definite(expr.arg), assumptions):
|
| 363 |
+
return True
|
| 364 |
+
|
| 365 |
+
@PositivePredicate.register(MatrixElement)
|
| 366 |
+
def _(expr, assumptions):
|
| 367 |
+
if (expr.i == expr.j
|
| 368 |
+
and ask(Q.positive_definite(expr.parent), assumptions)):
|
| 369 |
+
return True
|
| 370 |
+
|
| 371 |
+
@PositivePredicate.register(atan)
|
| 372 |
+
def _(expr, assumptions):
|
| 373 |
+
return ask(Q.positive(expr.args[0]), assumptions)
|
| 374 |
+
|
| 375 |
+
@PositivePredicate.register(asin)
|
| 376 |
+
def _(expr, assumptions):
|
| 377 |
+
x = expr.args[0]
|
| 378 |
+
if ask(Q.positive(x) & Q.nonpositive(x - 1), assumptions):
|
| 379 |
+
return True
|
| 380 |
+
if ask(Q.negative(x) & Q.nonnegative(x + 1), assumptions):
|
| 381 |
+
return False
|
| 382 |
+
|
| 383 |
+
@PositivePredicate.register(acos)
|
| 384 |
+
def _(expr, assumptions):
|
| 385 |
+
x = expr.args[0]
|
| 386 |
+
if ask(Q.nonpositive(x - 1) & Q.nonnegative(x + 1), assumptions):
|
| 387 |
+
return True
|
| 388 |
+
|
| 389 |
+
@PositivePredicate.register(acot)
|
| 390 |
+
def _(expr, assumptions):
|
| 391 |
+
return ask(Q.real(expr.args[0]), assumptions)
|
| 392 |
+
|
| 393 |
+
@PositivePredicate.register(NaN)
|
| 394 |
+
def _(expr, assumptions):
|
| 395 |
+
return None
|
| 396 |
+
|
| 397 |
+
|
| 398 |
+
# ExtendedNegativePredicate
|
| 399 |
+
|
| 400 |
+
@ExtendedNegativePredicate.register(object)
|
| 401 |
+
def _(expr, assumptions):
|
| 402 |
+
return ask(Q.negative(expr) | Q.negative_infinite(expr), assumptions)
|
| 403 |
+
|
| 404 |
+
|
| 405 |
+
# ExtendedPositivePredicate
|
| 406 |
+
|
| 407 |
+
@ExtendedPositivePredicate.register(object)
|
| 408 |
+
def _(expr, assumptions):
|
| 409 |
+
return ask(Q.positive(expr) | Q.positive_infinite(expr), assumptions)
|
| 410 |
+
|
| 411 |
+
|
| 412 |
+
# ExtendedNonZeroPredicate
|
| 413 |
+
|
| 414 |
+
@ExtendedNonZeroPredicate.register(object)
|
| 415 |
+
def _(expr, assumptions):
|
| 416 |
+
return ask(
|
| 417 |
+
Q.negative_infinite(expr) | Q.negative(expr) | Q.positive(expr) | Q.positive_infinite(expr),
|
| 418 |
+
assumptions)
|
| 419 |
+
|
| 420 |
+
|
| 421 |
+
# ExtendedNonPositivePredicate
|
| 422 |
+
|
| 423 |
+
@ExtendedNonPositivePredicate.register(object)
|
| 424 |
+
def _(expr, assumptions):
|
| 425 |
+
return ask(
|
| 426 |
+
Q.negative_infinite(expr) | Q.negative(expr) | Q.zero(expr),
|
| 427 |
+
assumptions)
|
| 428 |
+
|
| 429 |
+
|
| 430 |
+
# ExtendedNonNegativePredicate
|
| 431 |
+
|
| 432 |
+
@ExtendedNonNegativePredicate.register(object)
|
| 433 |
+
def _(expr, assumptions):
|
| 434 |
+
return ask(
|
| 435 |
+
Q.zero(expr) | Q.positive(expr) | Q.positive_infinite(expr),
|
| 436 |
+
assumptions)
|
lib/python3.12/site-packages/sympy/assumptions/handlers/sets.py
ADDED
|
@@ -0,0 +1,772 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Handlers for predicates related to set membership: integer, rational, etc.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from sympy.assumptions import Q, ask
|
| 6 |
+
from sympy.core import Add, Basic, Expr, Mul, Pow, S
|
| 7 |
+
from sympy.core.numbers import (AlgebraicNumber, ComplexInfinity, Exp1, Float,
|
| 8 |
+
GoldenRatio, ImaginaryUnit, Infinity, Integer, NaN, NegativeInfinity,
|
| 9 |
+
Number, NumberSymbol, Pi, pi, Rational, TribonacciConstant, E)
|
| 10 |
+
from sympy.core.logic import fuzzy_bool
|
| 11 |
+
from sympy.functions import (Abs, acos, acot, asin, atan, cos, cot, exp, im,
|
| 12 |
+
log, re, sin, tan)
|
| 13 |
+
from sympy.core.numbers import I
|
| 14 |
+
from sympy.core.relational import Eq
|
| 15 |
+
from sympy.functions.elementary.complexes import conjugate
|
| 16 |
+
from sympy.matrices import Determinant, MatrixBase, Trace
|
| 17 |
+
from sympy.matrices.expressions.matexpr import MatrixElement
|
| 18 |
+
|
| 19 |
+
from sympy.multipledispatch import MDNotImplementedError
|
| 20 |
+
|
| 21 |
+
from .common import test_closed_group
|
| 22 |
+
from ..predicates.sets import (IntegerPredicate, RationalPredicate,
|
| 23 |
+
IrrationalPredicate, RealPredicate, ExtendedRealPredicate,
|
| 24 |
+
HermitianPredicate, ComplexPredicate, ImaginaryPredicate,
|
| 25 |
+
AntihermitianPredicate, AlgebraicPredicate)
|
| 26 |
+
|
| 27 |
+
|
| 28 |
+
# IntegerPredicate
|
| 29 |
+
|
| 30 |
+
def _IntegerPredicate_number(expr, assumptions):
|
| 31 |
+
# helper function
|
| 32 |
+
try:
|
| 33 |
+
i = int(expr.round())
|
| 34 |
+
if not (expr - i).equals(0):
|
| 35 |
+
raise TypeError
|
| 36 |
+
return True
|
| 37 |
+
except TypeError:
|
| 38 |
+
return False
|
| 39 |
+
|
| 40 |
+
@IntegerPredicate.register_many(int, Integer) # type:ignore
|
| 41 |
+
def _(expr, assumptions):
|
| 42 |
+
return True
|
| 43 |
+
|
| 44 |
+
@IntegerPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
|
| 45 |
+
NegativeInfinity, Pi, Rational, TribonacciConstant)
|
| 46 |
+
def _(expr, assumptions):
|
| 47 |
+
return False
|
| 48 |
+
|
| 49 |
+
@IntegerPredicate.register(Expr)
|
| 50 |
+
def _(expr, assumptions):
|
| 51 |
+
ret = expr.is_integer
|
| 52 |
+
if ret is None:
|
| 53 |
+
raise MDNotImplementedError
|
| 54 |
+
return ret
|
| 55 |
+
|
| 56 |
+
@IntegerPredicate.register_many(Add, Pow)
|
| 57 |
+
def _(expr, assumptions):
|
| 58 |
+
"""
|
| 59 |
+
* Integer + Integer -> Integer
|
| 60 |
+
* Integer + !Integer -> !Integer
|
| 61 |
+
* !Integer + !Integer -> ?
|
| 62 |
+
"""
|
| 63 |
+
if expr.is_number:
|
| 64 |
+
return _IntegerPredicate_number(expr, assumptions)
|
| 65 |
+
return test_closed_group(expr, assumptions, Q.integer)
|
| 66 |
+
|
| 67 |
+
@IntegerPredicate.register(Mul)
|
| 68 |
+
def _(expr, assumptions):
|
| 69 |
+
"""
|
| 70 |
+
* Integer*Integer -> Integer
|
| 71 |
+
* Integer*Irrational -> !Integer
|
| 72 |
+
* Odd/Even -> !Integer
|
| 73 |
+
* Integer*Rational -> ?
|
| 74 |
+
"""
|
| 75 |
+
if expr.is_number:
|
| 76 |
+
return _IntegerPredicate_number(expr, assumptions)
|
| 77 |
+
_output = True
|
| 78 |
+
for arg in expr.args:
|
| 79 |
+
if not ask(Q.integer(arg), assumptions):
|
| 80 |
+
if arg.is_Rational:
|
| 81 |
+
if arg.q == 2:
|
| 82 |
+
return ask(Q.even(2*expr), assumptions)
|
| 83 |
+
if ~(arg.q & 1):
|
| 84 |
+
return None
|
| 85 |
+
elif ask(Q.irrational(arg), assumptions):
|
| 86 |
+
if _output:
|
| 87 |
+
_output = False
|
| 88 |
+
else:
|
| 89 |
+
return
|
| 90 |
+
else:
|
| 91 |
+
return
|
| 92 |
+
|
| 93 |
+
return _output
|
| 94 |
+
|
| 95 |
+
@IntegerPredicate.register(Abs)
|
| 96 |
+
def _(expr, assumptions):
|
| 97 |
+
return ask(Q.integer(expr.args[0]), assumptions)
|
| 98 |
+
|
| 99 |
+
@IntegerPredicate.register_many(Determinant, MatrixElement, Trace)
|
| 100 |
+
def _(expr, assumptions):
|
| 101 |
+
return ask(Q.integer_elements(expr.args[0]), assumptions)
|
| 102 |
+
|
| 103 |
+
|
| 104 |
+
# RationalPredicate
|
| 105 |
+
|
| 106 |
+
@RationalPredicate.register(Rational)
|
| 107 |
+
def _(expr, assumptions):
|
| 108 |
+
return True
|
| 109 |
+
|
| 110 |
+
@RationalPredicate.register(Float)
|
| 111 |
+
def _(expr, assumptions):
|
| 112 |
+
return None
|
| 113 |
+
|
| 114 |
+
@RationalPredicate.register_many(Exp1, GoldenRatio, ImaginaryUnit, Infinity,
|
| 115 |
+
NegativeInfinity, Pi, TribonacciConstant)
|
| 116 |
+
def _(expr, assumptions):
|
| 117 |
+
return False
|
| 118 |
+
|
| 119 |
+
@RationalPredicate.register(Expr)
|
| 120 |
+
def _(expr, assumptions):
|
| 121 |
+
ret = expr.is_rational
|
| 122 |
+
if ret is None:
|
| 123 |
+
raise MDNotImplementedError
|
| 124 |
+
return ret
|
| 125 |
+
|
| 126 |
+
@RationalPredicate.register_many(Add, Mul)
|
| 127 |
+
def _(expr, assumptions):
|
| 128 |
+
"""
|
| 129 |
+
* Rational + Rational -> Rational
|
| 130 |
+
* Rational + !Rational -> !Rational
|
| 131 |
+
* !Rational + !Rational -> ?
|
| 132 |
+
"""
|
| 133 |
+
if expr.is_number:
|
| 134 |
+
if expr.as_real_imag()[1]:
|
| 135 |
+
return False
|
| 136 |
+
return test_closed_group(expr, assumptions, Q.rational)
|
| 137 |
+
|
| 138 |
+
@RationalPredicate.register(Pow)
|
| 139 |
+
def _(expr, assumptions):
|
| 140 |
+
"""
|
| 141 |
+
* Rational ** Integer -> Rational
|
| 142 |
+
* Irrational ** Rational -> Irrational
|
| 143 |
+
* Rational ** Irrational -> ?
|
| 144 |
+
"""
|
| 145 |
+
if expr.base == E:
|
| 146 |
+
x = expr.exp
|
| 147 |
+
if ask(Q.rational(x), assumptions):
|
| 148 |
+
return ask(~Q.nonzero(x), assumptions)
|
| 149 |
+
return
|
| 150 |
+
|
| 151 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 152 |
+
return ask(Q.rational(expr.base), assumptions)
|
| 153 |
+
elif ask(Q.rational(expr.exp), assumptions):
|
| 154 |
+
if ask(Q.prime(expr.base), assumptions):
|
| 155 |
+
return False
|
| 156 |
+
|
| 157 |
+
@RationalPredicate.register_many(asin, atan, cos, sin, tan)
|
| 158 |
+
def _(expr, assumptions):
|
| 159 |
+
x = expr.args[0]
|
| 160 |
+
if ask(Q.rational(x), assumptions):
|
| 161 |
+
return ask(~Q.nonzero(x), assumptions)
|
| 162 |
+
|
| 163 |
+
@RationalPredicate.register(exp)
|
| 164 |
+
def _(expr, assumptions):
|
| 165 |
+
x = expr.exp
|
| 166 |
+
if ask(Q.rational(x), assumptions):
|
| 167 |
+
return ask(~Q.nonzero(x), assumptions)
|
| 168 |
+
|
| 169 |
+
@RationalPredicate.register_many(acot, cot)
|
| 170 |
+
def _(expr, assumptions):
|
| 171 |
+
x = expr.args[0]
|
| 172 |
+
if ask(Q.rational(x), assumptions):
|
| 173 |
+
return False
|
| 174 |
+
|
| 175 |
+
@RationalPredicate.register_many(acos, log)
|
| 176 |
+
def _(expr, assumptions):
|
| 177 |
+
x = expr.args[0]
|
| 178 |
+
if ask(Q.rational(x), assumptions):
|
| 179 |
+
return ask(~Q.nonzero(x - 1), assumptions)
|
| 180 |
+
|
| 181 |
+
|
| 182 |
+
# IrrationalPredicate
|
| 183 |
+
|
| 184 |
+
@IrrationalPredicate.register(Expr)
|
| 185 |
+
def _(expr, assumptions):
|
| 186 |
+
ret = expr.is_irrational
|
| 187 |
+
if ret is None:
|
| 188 |
+
raise MDNotImplementedError
|
| 189 |
+
return ret
|
| 190 |
+
|
| 191 |
+
@IrrationalPredicate.register(Basic)
|
| 192 |
+
def _(expr, assumptions):
|
| 193 |
+
_real = ask(Q.real(expr), assumptions)
|
| 194 |
+
if _real:
|
| 195 |
+
_rational = ask(Q.rational(expr), assumptions)
|
| 196 |
+
if _rational is None:
|
| 197 |
+
return None
|
| 198 |
+
return not _rational
|
| 199 |
+
else:
|
| 200 |
+
return _real
|
| 201 |
+
|
| 202 |
+
|
| 203 |
+
# RealPredicate
|
| 204 |
+
|
| 205 |
+
def _RealPredicate_number(expr, assumptions):
|
| 206 |
+
# let as_real_imag() work first since the expression may
|
| 207 |
+
# be simpler to evaluate
|
| 208 |
+
i = expr.as_real_imag()[1].evalf(2)
|
| 209 |
+
if i._prec != 1:
|
| 210 |
+
return not i
|
| 211 |
+
# allow None to be returned if we couldn't show for sure
|
| 212 |
+
# that i was 0
|
| 213 |
+
|
| 214 |
+
@RealPredicate.register_many(Abs, Exp1, Float, GoldenRatio, im, Pi, Rational,
|
| 215 |
+
re, TribonacciConstant)
|
| 216 |
+
def _(expr, assumptions):
|
| 217 |
+
return True
|
| 218 |
+
|
| 219 |
+
@RealPredicate.register_many(ImaginaryUnit, Infinity, NegativeInfinity)
|
| 220 |
+
def _(expr, assumptions):
|
| 221 |
+
return False
|
| 222 |
+
|
| 223 |
+
@RealPredicate.register(Expr)
|
| 224 |
+
def _(expr, assumptions):
|
| 225 |
+
ret = expr.is_real
|
| 226 |
+
if ret is None:
|
| 227 |
+
raise MDNotImplementedError
|
| 228 |
+
return ret
|
| 229 |
+
|
| 230 |
+
@RealPredicate.register(Add)
|
| 231 |
+
def _(expr, assumptions):
|
| 232 |
+
"""
|
| 233 |
+
* Real + Real -> Real
|
| 234 |
+
* Real + (Complex & !Real) -> !Real
|
| 235 |
+
"""
|
| 236 |
+
if expr.is_number:
|
| 237 |
+
return _RealPredicate_number(expr, assumptions)
|
| 238 |
+
return test_closed_group(expr, assumptions, Q.real)
|
| 239 |
+
|
| 240 |
+
@RealPredicate.register(Mul)
|
| 241 |
+
def _(expr, assumptions):
|
| 242 |
+
"""
|
| 243 |
+
* Real*Real -> Real
|
| 244 |
+
* Real*Imaginary -> !Real
|
| 245 |
+
* Imaginary*Imaginary -> Real
|
| 246 |
+
"""
|
| 247 |
+
if expr.is_number:
|
| 248 |
+
return _RealPredicate_number(expr, assumptions)
|
| 249 |
+
result = True
|
| 250 |
+
for arg in expr.args:
|
| 251 |
+
if ask(Q.real(arg), assumptions):
|
| 252 |
+
pass
|
| 253 |
+
elif ask(Q.imaginary(arg), assumptions):
|
| 254 |
+
result = result ^ True
|
| 255 |
+
else:
|
| 256 |
+
break
|
| 257 |
+
else:
|
| 258 |
+
return result
|
| 259 |
+
|
| 260 |
+
@RealPredicate.register(Pow)
|
| 261 |
+
def _(expr, assumptions):
|
| 262 |
+
"""
|
| 263 |
+
* Real**Integer -> Real
|
| 264 |
+
* Positive**Real -> Real
|
| 265 |
+
* Real**(Integer/Even) -> Real if base is nonnegative
|
| 266 |
+
* Real**(Integer/Odd) -> Real
|
| 267 |
+
* Imaginary**(Integer/Even) -> Real
|
| 268 |
+
* Imaginary**(Integer/Odd) -> not Real
|
| 269 |
+
* Imaginary**Real -> ? since Real could be 0 (giving real)
|
| 270 |
+
or 1 (giving imaginary)
|
| 271 |
+
* b**Imaginary -> Real if log(b) is imaginary and b != 0
|
| 272 |
+
and exponent != integer multiple of
|
| 273 |
+
I*pi/log(b)
|
| 274 |
+
* Real**Real -> ? e.g. sqrt(-1) is imaginary and
|
| 275 |
+
sqrt(2) is not
|
| 276 |
+
"""
|
| 277 |
+
if expr.is_number:
|
| 278 |
+
return _RealPredicate_number(expr, assumptions)
|
| 279 |
+
|
| 280 |
+
if expr.base == E:
|
| 281 |
+
return ask(
|
| 282 |
+
Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
|
| 283 |
+
)
|
| 284 |
+
|
| 285 |
+
if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
|
| 286 |
+
if ask(Q.imaginary(expr.base.exp), assumptions):
|
| 287 |
+
if ask(Q.imaginary(expr.exp), assumptions):
|
| 288 |
+
return True
|
| 289 |
+
# If the i = (exp's arg)/(I*pi) is an integer or half-integer
|
| 290 |
+
# multiple of I*pi then 2*i will be an integer. In addition,
|
| 291 |
+
# exp(i*I*pi) = (-1)**i so the overall realness of the expr
|
| 292 |
+
# can be determined by replacing exp(i*I*pi) with (-1)**i.
|
| 293 |
+
i = expr.base.exp/I/pi
|
| 294 |
+
if ask(Q.integer(2*i), assumptions):
|
| 295 |
+
return ask(Q.real((S.NegativeOne**i)**expr.exp), assumptions)
|
| 296 |
+
return
|
| 297 |
+
|
| 298 |
+
if ask(Q.imaginary(expr.base), assumptions):
|
| 299 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 300 |
+
odd = ask(Q.odd(expr.exp), assumptions)
|
| 301 |
+
if odd is not None:
|
| 302 |
+
return not odd
|
| 303 |
+
return
|
| 304 |
+
|
| 305 |
+
if ask(Q.imaginary(expr.exp), assumptions):
|
| 306 |
+
imlog = ask(Q.imaginary(log(expr.base)), assumptions)
|
| 307 |
+
if imlog is not None:
|
| 308 |
+
# I**i -> real, log(I) is imag;
|
| 309 |
+
# (2*I)**i -> complex, log(2*I) is not imag
|
| 310 |
+
return imlog
|
| 311 |
+
|
| 312 |
+
if ask(Q.real(expr.base), assumptions):
|
| 313 |
+
if ask(Q.real(expr.exp), assumptions):
|
| 314 |
+
if expr.exp.is_Rational and \
|
| 315 |
+
ask(Q.even(expr.exp.q), assumptions):
|
| 316 |
+
return ask(Q.positive(expr.base), assumptions)
|
| 317 |
+
elif ask(Q.integer(expr.exp), assumptions):
|
| 318 |
+
return True
|
| 319 |
+
elif ask(Q.positive(expr.base), assumptions):
|
| 320 |
+
return True
|
| 321 |
+
elif ask(Q.negative(expr.base), assumptions):
|
| 322 |
+
return False
|
| 323 |
+
|
| 324 |
+
@RealPredicate.register_many(cos, sin)
|
| 325 |
+
def _(expr, assumptions):
|
| 326 |
+
if ask(Q.real(expr.args[0]), assumptions):
|
| 327 |
+
return True
|
| 328 |
+
|
| 329 |
+
@RealPredicate.register(exp)
|
| 330 |
+
def _(expr, assumptions):
|
| 331 |
+
return ask(
|
| 332 |
+
Q.integer(expr.exp/I/pi) | Q.real(expr.exp), assumptions
|
| 333 |
+
)
|
| 334 |
+
|
| 335 |
+
@RealPredicate.register(log)
|
| 336 |
+
def _(expr, assumptions):
|
| 337 |
+
return ask(Q.positive(expr.args[0]), assumptions)
|
| 338 |
+
|
| 339 |
+
@RealPredicate.register_many(Determinant, MatrixElement, Trace)
|
| 340 |
+
def _(expr, assumptions):
|
| 341 |
+
return ask(Q.real_elements(expr.args[0]), assumptions)
|
| 342 |
+
|
| 343 |
+
|
| 344 |
+
# ExtendedRealPredicate
|
| 345 |
+
|
| 346 |
+
@ExtendedRealPredicate.register(object)
|
| 347 |
+
def _(expr, assumptions):
|
| 348 |
+
return ask(Q.negative_infinite(expr)
|
| 349 |
+
| Q.negative(expr)
|
| 350 |
+
| Q.zero(expr)
|
| 351 |
+
| Q.positive(expr)
|
| 352 |
+
| Q.positive_infinite(expr),
|
| 353 |
+
assumptions)
|
| 354 |
+
|
| 355 |
+
@ExtendedRealPredicate.register_many(Infinity, NegativeInfinity)
|
| 356 |
+
def _(expr, assumptions):
|
| 357 |
+
return True
|
| 358 |
+
|
| 359 |
+
@ExtendedRealPredicate.register_many(Add, Mul, Pow) # type:ignore
|
| 360 |
+
def _(expr, assumptions):
|
| 361 |
+
return test_closed_group(expr, assumptions, Q.extended_real)
|
| 362 |
+
|
| 363 |
+
|
| 364 |
+
# HermitianPredicate
|
| 365 |
+
|
| 366 |
+
@HermitianPredicate.register(object) # type:ignore
|
| 367 |
+
def _(expr, assumptions):
|
| 368 |
+
if isinstance(expr, MatrixBase):
|
| 369 |
+
return None
|
| 370 |
+
return ask(Q.real(expr), assumptions)
|
| 371 |
+
|
| 372 |
+
@HermitianPredicate.register(Add) # type:ignore
|
| 373 |
+
def _(expr, assumptions):
|
| 374 |
+
"""
|
| 375 |
+
* Hermitian + Hermitian -> Hermitian
|
| 376 |
+
* Hermitian + !Hermitian -> !Hermitian
|
| 377 |
+
"""
|
| 378 |
+
if expr.is_number:
|
| 379 |
+
raise MDNotImplementedError
|
| 380 |
+
return test_closed_group(expr, assumptions, Q.hermitian)
|
| 381 |
+
|
| 382 |
+
@HermitianPredicate.register(Mul) # type:ignore
|
| 383 |
+
def _(expr, assumptions):
|
| 384 |
+
"""
|
| 385 |
+
As long as there is at most only one noncommutative term:
|
| 386 |
+
|
| 387 |
+
* Hermitian*Hermitian -> Hermitian
|
| 388 |
+
* Hermitian*Antihermitian -> !Hermitian
|
| 389 |
+
* Antihermitian*Antihermitian -> Hermitian
|
| 390 |
+
"""
|
| 391 |
+
if expr.is_number:
|
| 392 |
+
raise MDNotImplementedError
|
| 393 |
+
nccount = 0
|
| 394 |
+
result = True
|
| 395 |
+
for arg in expr.args:
|
| 396 |
+
if ask(Q.antihermitian(arg), assumptions):
|
| 397 |
+
result = result ^ True
|
| 398 |
+
elif not ask(Q.hermitian(arg), assumptions):
|
| 399 |
+
break
|
| 400 |
+
if ask(~Q.commutative(arg), assumptions):
|
| 401 |
+
nccount += 1
|
| 402 |
+
if nccount > 1:
|
| 403 |
+
break
|
| 404 |
+
else:
|
| 405 |
+
return result
|
| 406 |
+
|
| 407 |
+
@HermitianPredicate.register(Pow) # type:ignore
|
| 408 |
+
def _(expr, assumptions):
|
| 409 |
+
"""
|
| 410 |
+
* Hermitian**Integer -> Hermitian
|
| 411 |
+
"""
|
| 412 |
+
if expr.is_number:
|
| 413 |
+
raise MDNotImplementedError
|
| 414 |
+
if expr.base == E:
|
| 415 |
+
if ask(Q.hermitian(expr.exp), assumptions):
|
| 416 |
+
return True
|
| 417 |
+
raise MDNotImplementedError
|
| 418 |
+
if ask(Q.hermitian(expr.base), assumptions):
|
| 419 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 420 |
+
return True
|
| 421 |
+
raise MDNotImplementedError
|
| 422 |
+
|
| 423 |
+
@HermitianPredicate.register_many(cos, sin) # type:ignore
|
| 424 |
+
def _(expr, assumptions):
|
| 425 |
+
if ask(Q.hermitian(expr.args[0]), assumptions):
|
| 426 |
+
return True
|
| 427 |
+
raise MDNotImplementedError
|
| 428 |
+
|
| 429 |
+
@HermitianPredicate.register(exp) # type:ignore
|
| 430 |
+
def _(expr, assumptions):
|
| 431 |
+
if ask(Q.hermitian(expr.exp), assumptions):
|
| 432 |
+
return True
|
| 433 |
+
raise MDNotImplementedError
|
| 434 |
+
|
| 435 |
+
@HermitianPredicate.register(MatrixBase) # type:ignore
|
| 436 |
+
def _(mat, assumptions):
|
| 437 |
+
rows, cols = mat.shape
|
| 438 |
+
ret_val = True
|
| 439 |
+
for i in range(rows):
|
| 440 |
+
for j in range(i, cols):
|
| 441 |
+
cond = fuzzy_bool(Eq(mat[i, j], conjugate(mat[j, i])))
|
| 442 |
+
if cond is None:
|
| 443 |
+
ret_val = None
|
| 444 |
+
if cond == False:
|
| 445 |
+
return False
|
| 446 |
+
if ret_val is None:
|
| 447 |
+
raise MDNotImplementedError
|
| 448 |
+
return ret_val
|
| 449 |
+
|
| 450 |
+
|
| 451 |
+
# ComplexPredicate
|
| 452 |
+
|
| 453 |
+
@ComplexPredicate.register_many(Abs, cos, exp, im, ImaginaryUnit, log, Number, # type:ignore
|
| 454 |
+
NumberSymbol, re, sin)
|
| 455 |
+
def _(expr, assumptions):
|
| 456 |
+
return True
|
| 457 |
+
|
| 458 |
+
@ComplexPredicate.register_many(Infinity, NegativeInfinity) # type:ignore
|
| 459 |
+
def _(expr, assumptions):
|
| 460 |
+
return False
|
| 461 |
+
|
| 462 |
+
@ComplexPredicate.register(Expr) # type:ignore
|
| 463 |
+
def _(expr, assumptions):
|
| 464 |
+
ret = expr.is_complex
|
| 465 |
+
if ret is None:
|
| 466 |
+
raise MDNotImplementedError
|
| 467 |
+
return ret
|
| 468 |
+
|
| 469 |
+
@ComplexPredicate.register_many(Add, Mul) # type:ignore
|
| 470 |
+
def _(expr, assumptions):
|
| 471 |
+
return test_closed_group(expr, assumptions, Q.complex)
|
| 472 |
+
|
| 473 |
+
@ComplexPredicate.register(Pow) # type:ignore
|
| 474 |
+
def _(expr, assumptions):
|
| 475 |
+
if expr.base == E:
|
| 476 |
+
return True
|
| 477 |
+
return test_closed_group(expr, assumptions, Q.complex)
|
| 478 |
+
|
| 479 |
+
@ComplexPredicate.register_many(Determinant, MatrixElement, Trace) # type:ignore
|
| 480 |
+
def _(expr, assumptions):
|
| 481 |
+
return ask(Q.complex_elements(expr.args[0]), assumptions)
|
| 482 |
+
|
| 483 |
+
@ComplexPredicate.register(NaN) # type:ignore
|
| 484 |
+
def _(expr, assumptions):
|
| 485 |
+
return None
|
| 486 |
+
|
| 487 |
+
|
| 488 |
+
# ImaginaryPredicate
|
| 489 |
+
|
| 490 |
+
def _Imaginary_number(expr, assumptions):
|
| 491 |
+
# let as_real_imag() work first since the expression may
|
| 492 |
+
# be simpler to evaluate
|
| 493 |
+
r = expr.as_real_imag()[0].evalf(2)
|
| 494 |
+
if r._prec != 1:
|
| 495 |
+
return not r
|
| 496 |
+
# allow None to be returned if we couldn't show for sure
|
| 497 |
+
# that r was 0
|
| 498 |
+
|
| 499 |
+
@ImaginaryPredicate.register(ImaginaryUnit) # type:ignore
|
| 500 |
+
def _(expr, assumptions):
|
| 501 |
+
return True
|
| 502 |
+
|
| 503 |
+
@ImaginaryPredicate.register(Expr) # type:ignore
|
| 504 |
+
def _(expr, assumptions):
|
| 505 |
+
ret = expr.is_imaginary
|
| 506 |
+
if ret is None:
|
| 507 |
+
raise MDNotImplementedError
|
| 508 |
+
return ret
|
| 509 |
+
|
| 510 |
+
@ImaginaryPredicate.register(Add) # type:ignore
|
| 511 |
+
def _(expr, assumptions):
|
| 512 |
+
"""
|
| 513 |
+
* Imaginary + Imaginary -> Imaginary
|
| 514 |
+
* Imaginary + Complex -> ?
|
| 515 |
+
* Imaginary + Real -> !Imaginary
|
| 516 |
+
"""
|
| 517 |
+
if expr.is_number:
|
| 518 |
+
return _Imaginary_number(expr, assumptions)
|
| 519 |
+
|
| 520 |
+
reals = 0
|
| 521 |
+
for arg in expr.args:
|
| 522 |
+
if ask(Q.imaginary(arg), assumptions):
|
| 523 |
+
pass
|
| 524 |
+
elif ask(Q.real(arg), assumptions):
|
| 525 |
+
reals += 1
|
| 526 |
+
else:
|
| 527 |
+
break
|
| 528 |
+
else:
|
| 529 |
+
if reals == 0:
|
| 530 |
+
return True
|
| 531 |
+
if reals in (1, len(expr.args)):
|
| 532 |
+
# two reals could sum 0 thus giving an imaginary
|
| 533 |
+
return False
|
| 534 |
+
|
| 535 |
+
@ImaginaryPredicate.register(Mul) # type:ignore
|
| 536 |
+
def _(expr, assumptions):
|
| 537 |
+
"""
|
| 538 |
+
* Real*Imaginary -> Imaginary
|
| 539 |
+
* Imaginary*Imaginary -> Real
|
| 540 |
+
"""
|
| 541 |
+
if expr.is_number:
|
| 542 |
+
return _Imaginary_number(expr, assumptions)
|
| 543 |
+
result = False
|
| 544 |
+
reals = 0
|
| 545 |
+
for arg in expr.args:
|
| 546 |
+
if ask(Q.imaginary(arg), assumptions):
|
| 547 |
+
result = result ^ True
|
| 548 |
+
elif not ask(Q.real(arg), assumptions):
|
| 549 |
+
break
|
| 550 |
+
else:
|
| 551 |
+
if reals == len(expr.args):
|
| 552 |
+
return False
|
| 553 |
+
return result
|
| 554 |
+
|
| 555 |
+
@ImaginaryPredicate.register(Pow) # type:ignore
|
| 556 |
+
def _(expr, assumptions):
|
| 557 |
+
"""
|
| 558 |
+
* Imaginary**Odd -> Imaginary
|
| 559 |
+
* Imaginary**Even -> Real
|
| 560 |
+
* b**Imaginary -> !Imaginary if exponent is an integer
|
| 561 |
+
multiple of I*pi/log(b)
|
| 562 |
+
* Imaginary**Real -> ?
|
| 563 |
+
* Positive**Real -> Real
|
| 564 |
+
* Negative**Integer -> Real
|
| 565 |
+
* Negative**(Integer/2) -> Imaginary
|
| 566 |
+
* Negative**Real -> not Imaginary if exponent is not Rational
|
| 567 |
+
"""
|
| 568 |
+
if expr.is_number:
|
| 569 |
+
return _Imaginary_number(expr, assumptions)
|
| 570 |
+
|
| 571 |
+
if expr.base == E:
|
| 572 |
+
a = expr.exp/I/pi
|
| 573 |
+
return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
|
| 574 |
+
|
| 575 |
+
if expr.base.func == exp or (expr.base.is_Pow and expr.base.base == E):
|
| 576 |
+
if ask(Q.imaginary(expr.base.exp), assumptions):
|
| 577 |
+
if ask(Q.imaginary(expr.exp), assumptions):
|
| 578 |
+
return False
|
| 579 |
+
i = expr.base.exp/I/pi
|
| 580 |
+
if ask(Q.integer(2*i), assumptions):
|
| 581 |
+
return ask(Q.imaginary((S.NegativeOne**i)**expr.exp), assumptions)
|
| 582 |
+
|
| 583 |
+
if ask(Q.imaginary(expr.base), assumptions):
|
| 584 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 585 |
+
odd = ask(Q.odd(expr.exp), assumptions)
|
| 586 |
+
if odd is not None:
|
| 587 |
+
return odd
|
| 588 |
+
return
|
| 589 |
+
|
| 590 |
+
if ask(Q.imaginary(expr.exp), assumptions):
|
| 591 |
+
imlog = ask(Q.imaginary(log(expr.base)), assumptions)
|
| 592 |
+
if imlog is not None:
|
| 593 |
+
# I**i -> real; (2*I)**i -> complex ==> not imaginary
|
| 594 |
+
return False
|
| 595 |
+
|
| 596 |
+
if ask(Q.real(expr.base) & Q.real(expr.exp), assumptions):
|
| 597 |
+
if ask(Q.positive(expr.base), assumptions):
|
| 598 |
+
return False
|
| 599 |
+
else:
|
| 600 |
+
rat = ask(Q.rational(expr.exp), assumptions)
|
| 601 |
+
if not rat:
|
| 602 |
+
return rat
|
| 603 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 604 |
+
return False
|
| 605 |
+
else:
|
| 606 |
+
half = ask(Q.integer(2*expr.exp), assumptions)
|
| 607 |
+
if half:
|
| 608 |
+
return ask(Q.negative(expr.base), assumptions)
|
| 609 |
+
return half
|
| 610 |
+
|
| 611 |
+
@ImaginaryPredicate.register(log) # type:ignore
|
| 612 |
+
def _(expr, assumptions):
|
| 613 |
+
if ask(Q.real(expr.args[0]), assumptions):
|
| 614 |
+
if ask(Q.positive(expr.args[0]), assumptions):
|
| 615 |
+
return False
|
| 616 |
+
return
|
| 617 |
+
# XXX it should be enough to do
|
| 618 |
+
# return ask(Q.nonpositive(expr.args[0]), assumptions)
|
| 619 |
+
# but ask(Q.nonpositive(exp(x)), Q.imaginary(x)) -> None;
|
| 620 |
+
# it should return True since exp(x) will be either 0 or complex
|
| 621 |
+
if expr.args[0].func == exp or (expr.args[0].is_Pow and expr.args[0].base == E):
|
| 622 |
+
if expr.args[0].exp in [I, -I]:
|
| 623 |
+
return True
|
| 624 |
+
im = ask(Q.imaginary(expr.args[0]), assumptions)
|
| 625 |
+
if im is False:
|
| 626 |
+
return False
|
| 627 |
+
|
| 628 |
+
@ImaginaryPredicate.register(exp) # type:ignore
|
| 629 |
+
def _(expr, assumptions):
|
| 630 |
+
a = expr.exp/I/pi
|
| 631 |
+
return ask(Q.integer(2*a) & ~Q.integer(a), assumptions)
|
| 632 |
+
|
| 633 |
+
@ImaginaryPredicate.register_many(Number, NumberSymbol) # type:ignore
|
| 634 |
+
def _(expr, assumptions):
|
| 635 |
+
return not (expr.as_real_imag()[1] == 0)
|
| 636 |
+
|
| 637 |
+
@ImaginaryPredicate.register(NaN) # type:ignore
|
| 638 |
+
def _(expr, assumptions):
|
| 639 |
+
return None
|
| 640 |
+
|
| 641 |
+
|
| 642 |
+
# AntihermitianPredicate
|
| 643 |
+
|
| 644 |
+
@AntihermitianPredicate.register(object) # type:ignore
|
| 645 |
+
def _(expr, assumptions):
|
| 646 |
+
if isinstance(expr, MatrixBase):
|
| 647 |
+
return None
|
| 648 |
+
if ask(Q.zero(expr), assumptions):
|
| 649 |
+
return True
|
| 650 |
+
return ask(Q.imaginary(expr), assumptions)
|
| 651 |
+
|
| 652 |
+
@AntihermitianPredicate.register(Add) # type:ignore
|
| 653 |
+
def _(expr, assumptions):
|
| 654 |
+
"""
|
| 655 |
+
* Antihermitian + Antihermitian -> Antihermitian
|
| 656 |
+
* Antihermitian + !Antihermitian -> !Antihermitian
|
| 657 |
+
"""
|
| 658 |
+
if expr.is_number:
|
| 659 |
+
raise MDNotImplementedError
|
| 660 |
+
return test_closed_group(expr, assumptions, Q.antihermitian)
|
| 661 |
+
|
| 662 |
+
@AntihermitianPredicate.register(Mul) # type:ignore
|
| 663 |
+
def _(expr, assumptions):
|
| 664 |
+
"""
|
| 665 |
+
As long as there is at most only one noncommutative term:
|
| 666 |
+
|
| 667 |
+
* Hermitian*Hermitian -> !Antihermitian
|
| 668 |
+
* Hermitian*Antihermitian -> Antihermitian
|
| 669 |
+
* Antihermitian*Antihermitian -> !Antihermitian
|
| 670 |
+
"""
|
| 671 |
+
if expr.is_number:
|
| 672 |
+
raise MDNotImplementedError
|
| 673 |
+
nccount = 0
|
| 674 |
+
result = False
|
| 675 |
+
for arg in expr.args:
|
| 676 |
+
if ask(Q.antihermitian(arg), assumptions):
|
| 677 |
+
result = result ^ True
|
| 678 |
+
elif not ask(Q.hermitian(arg), assumptions):
|
| 679 |
+
break
|
| 680 |
+
if ask(~Q.commutative(arg), assumptions):
|
| 681 |
+
nccount += 1
|
| 682 |
+
if nccount > 1:
|
| 683 |
+
break
|
| 684 |
+
else:
|
| 685 |
+
return result
|
| 686 |
+
|
| 687 |
+
@AntihermitianPredicate.register(Pow) # type:ignore
|
| 688 |
+
def _(expr, assumptions):
|
| 689 |
+
"""
|
| 690 |
+
* Hermitian**Integer -> !Antihermitian
|
| 691 |
+
* Antihermitian**Even -> !Antihermitian
|
| 692 |
+
* Antihermitian**Odd -> Antihermitian
|
| 693 |
+
"""
|
| 694 |
+
if expr.is_number:
|
| 695 |
+
raise MDNotImplementedError
|
| 696 |
+
if ask(Q.hermitian(expr.base), assumptions):
|
| 697 |
+
if ask(Q.integer(expr.exp), assumptions):
|
| 698 |
+
return False
|
| 699 |
+
elif ask(Q.antihermitian(expr.base), assumptions):
|
| 700 |
+
if ask(Q.even(expr.exp), assumptions):
|
| 701 |
+
return False
|
| 702 |
+
elif ask(Q.odd(expr.exp), assumptions):
|
| 703 |
+
return True
|
| 704 |
+
raise MDNotImplementedError
|
| 705 |
+
|
| 706 |
+
@AntihermitianPredicate.register(MatrixBase) # type:ignore
|
| 707 |
+
def _(mat, assumptions):
|
| 708 |
+
rows, cols = mat.shape
|
| 709 |
+
ret_val = True
|
| 710 |
+
for i in range(rows):
|
| 711 |
+
for j in range(i, cols):
|
| 712 |
+
cond = fuzzy_bool(Eq(mat[i, j], -conjugate(mat[j, i])))
|
| 713 |
+
if cond is None:
|
| 714 |
+
ret_val = None
|
| 715 |
+
if cond == False:
|
| 716 |
+
return False
|
| 717 |
+
if ret_val is None:
|
| 718 |
+
raise MDNotImplementedError
|
| 719 |
+
return ret_val
|
| 720 |
+
|
| 721 |
+
|
| 722 |
+
# AlgebraicPredicate
|
| 723 |
+
|
| 724 |
+
@AlgebraicPredicate.register_many(AlgebraicNumber, Float, GoldenRatio, # type:ignore
|
| 725 |
+
ImaginaryUnit, TribonacciConstant)
|
| 726 |
+
def _(expr, assumptions):
|
| 727 |
+
return True
|
| 728 |
+
|
| 729 |
+
@AlgebraicPredicate.register_many(ComplexInfinity, Exp1, Infinity, # type:ignore
|
| 730 |
+
NegativeInfinity, Pi)
|
| 731 |
+
def _(expr, assumptions):
|
| 732 |
+
return False
|
| 733 |
+
|
| 734 |
+
@AlgebraicPredicate.register_many(Add, Mul) # type:ignore
|
| 735 |
+
def _(expr, assumptions):
|
| 736 |
+
return test_closed_group(expr, assumptions, Q.algebraic)
|
| 737 |
+
|
| 738 |
+
@AlgebraicPredicate.register(Pow) # type:ignore
|
| 739 |
+
def _(expr, assumptions):
|
| 740 |
+
if expr.base == E:
|
| 741 |
+
if ask(Q.algebraic(expr.exp), assumptions):
|
| 742 |
+
return ask(~Q.nonzero(expr.exp), assumptions)
|
| 743 |
+
return
|
| 744 |
+
return expr.exp.is_Rational and ask(Q.algebraic(expr.base), assumptions)
|
| 745 |
+
|
| 746 |
+
@AlgebraicPredicate.register(Rational) # type:ignore
|
| 747 |
+
def _(expr, assumptions):
|
| 748 |
+
return expr.q != 0
|
| 749 |
+
|
| 750 |
+
@AlgebraicPredicate.register_many(asin, atan, cos, sin, tan) # type:ignore
|
| 751 |
+
def _(expr, assumptions):
|
| 752 |
+
x = expr.args[0]
|
| 753 |
+
if ask(Q.algebraic(x), assumptions):
|
| 754 |
+
return ask(~Q.nonzero(x), assumptions)
|
| 755 |
+
|
| 756 |
+
@AlgebraicPredicate.register(exp) # type:ignore
|
| 757 |
+
def _(expr, assumptions):
|
| 758 |
+
x = expr.exp
|
| 759 |
+
if ask(Q.algebraic(x), assumptions):
|
| 760 |
+
return ask(~Q.nonzero(x), assumptions)
|
| 761 |
+
|
| 762 |
+
@AlgebraicPredicate.register_many(acot, cot) # type:ignore
|
| 763 |
+
def _(expr, assumptions):
|
| 764 |
+
x = expr.args[0]
|
| 765 |
+
if ask(Q.algebraic(x), assumptions):
|
| 766 |
+
return False
|
| 767 |
+
|
| 768 |
+
@AlgebraicPredicate.register_many(acos, log) # type:ignore
|
| 769 |
+
def _(expr, assumptions):
|
| 770 |
+
x = expr.args[0]
|
| 771 |
+
if ask(Q.algebraic(x), assumptions):
|
| 772 |
+
return ask(~Q.nonzero(x - 1), assumptions)
|
lib/python3.12/site-packages/sympy/assumptions/lra_satask.py
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.assume import global_assumptions
|
| 2 |
+
from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 3 |
+
from sympy.assumptions.ask import Q
|
| 4 |
+
from sympy.logic.inference import satisfiable
|
| 5 |
+
from sympy.logic.algorithms.lra_theory import UnhandledInput, ALLOWED_PRED
|
| 6 |
+
from sympy.matrices.kind import MatrixKind
|
| 7 |
+
from sympy.core.kind import NumberKind
|
| 8 |
+
from sympy.assumptions.assume import AppliedPredicate
|
| 9 |
+
from sympy.core.mul import Mul
|
| 10 |
+
from sympy.core.singleton import S
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def lra_satask(proposition, assumptions=True, context=global_assumptions):
|
| 14 |
+
"""
|
| 15 |
+
Function to evaluate the proposition with assumptions using SAT algorithm
|
| 16 |
+
in conjunction with an Linear Real Arithmetic theory solver.
|
| 17 |
+
|
| 18 |
+
Used to handle inequalities. Should eventually be depreciated and combined
|
| 19 |
+
into satask, but infinity handling and other things need to be implemented
|
| 20 |
+
before that can happen.
|
| 21 |
+
"""
|
| 22 |
+
props = CNF.from_prop(proposition)
|
| 23 |
+
_props = CNF.from_prop(~proposition)
|
| 24 |
+
|
| 25 |
+
cnf = CNF.from_prop(assumptions)
|
| 26 |
+
assumptions = EncodedCNF()
|
| 27 |
+
assumptions.from_cnf(cnf)
|
| 28 |
+
|
| 29 |
+
context_cnf = CNF()
|
| 30 |
+
if context:
|
| 31 |
+
context_cnf = context_cnf.extend(context)
|
| 32 |
+
|
| 33 |
+
assumptions.add_from_cnf(context_cnf)
|
| 34 |
+
|
| 35 |
+
return check_satisfiability(props, _props, assumptions)
|
| 36 |
+
|
| 37 |
+
# Some predicates such as Q.prime can't be handled by lra_satask.
|
| 38 |
+
# For example, (x > 0) & (x < 1) & Q.prime(x) is unsat but lra_satask would think it was sat.
|
| 39 |
+
# WHITE_LIST is a list of predicates that can always be handled.
|
| 40 |
+
WHITE_LIST = ALLOWED_PRED | {Q.positive, Q.negative, Q.zero, Q.nonzero, Q.nonpositive, Q.nonnegative,
|
| 41 |
+
Q.extended_positive, Q.extended_negative, Q.extended_nonpositive,
|
| 42 |
+
Q.extended_negative, Q.extended_nonzero, Q.negative_infinite,
|
| 43 |
+
Q.positive_infinite}
|
| 44 |
+
|
| 45 |
+
|
| 46 |
+
def check_satisfiability(prop, _prop, factbase):
|
| 47 |
+
sat_true = factbase.copy()
|
| 48 |
+
sat_false = factbase.copy()
|
| 49 |
+
sat_true.add_from_cnf(prop)
|
| 50 |
+
sat_false.add_from_cnf(_prop)
|
| 51 |
+
|
| 52 |
+
all_pred, all_exprs = get_all_pred_and_expr_from_enc_cnf(sat_true)
|
| 53 |
+
|
| 54 |
+
for pred in all_pred:
|
| 55 |
+
if pred.function not in WHITE_LIST and pred.function != Q.ne:
|
| 56 |
+
raise UnhandledInput(f"LRASolver: {pred} is an unhandled predicate")
|
| 57 |
+
for expr in all_exprs:
|
| 58 |
+
if expr.kind == MatrixKind(NumberKind):
|
| 59 |
+
raise UnhandledInput(f"LRASolver: {expr} is of MatrixKind")
|
| 60 |
+
if expr == S.NaN:
|
| 61 |
+
raise UnhandledInput("LRASolver: nan")
|
| 62 |
+
|
| 63 |
+
# convert old assumptions into predicates and add them to sat_true and sat_false
|
| 64 |
+
# also check for unhandled predicates
|
| 65 |
+
for assm in extract_pred_from_old_assum(all_exprs):
|
| 66 |
+
n = len(sat_true.encoding)
|
| 67 |
+
if assm not in sat_true.encoding:
|
| 68 |
+
sat_true.encoding[assm] = n+1
|
| 69 |
+
sat_true.data.append([sat_true.encoding[assm]])
|
| 70 |
+
|
| 71 |
+
n = len(sat_false.encoding)
|
| 72 |
+
if assm not in sat_false.encoding:
|
| 73 |
+
sat_false.encoding[assm] = n+1
|
| 74 |
+
sat_false.data.append([sat_false.encoding[assm]])
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
sat_true = _preprocess(sat_true)
|
| 78 |
+
sat_false = _preprocess(sat_false)
|
| 79 |
+
|
| 80 |
+
can_be_true = satisfiable(sat_true, use_lra_theory=True) is not False
|
| 81 |
+
can_be_false = satisfiable(sat_false, use_lra_theory=True) is not False
|
| 82 |
+
|
| 83 |
+
if can_be_true and can_be_false:
|
| 84 |
+
return None
|
| 85 |
+
|
| 86 |
+
if can_be_true and not can_be_false:
|
| 87 |
+
return True
|
| 88 |
+
|
| 89 |
+
if not can_be_true and can_be_false:
|
| 90 |
+
return False
|
| 91 |
+
|
| 92 |
+
if not can_be_true and not can_be_false:
|
| 93 |
+
raise ValueError("Inconsistent assumptions")
|
| 94 |
+
|
| 95 |
+
|
| 96 |
+
def _preprocess(enc_cnf):
|
| 97 |
+
"""
|
| 98 |
+
Returns an encoded cnf with only Q.eq, Q.gt, Q.lt,
|
| 99 |
+
Q.ge, and Q.le predicate.
|
| 100 |
+
|
| 101 |
+
Converts every unequality into a disjunction of strict
|
| 102 |
+
inequalities. For example, x != 3 would become
|
| 103 |
+
x < 3 OR x > 3.
|
| 104 |
+
|
| 105 |
+
Also converts all negated Q.ne predicates into
|
| 106 |
+
equalities.
|
| 107 |
+
"""
|
| 108 |
+
|
| 109 |
+
# loops through each literal in each clause
|
| 110 |
+
# to construct a new, preprocessed encodedCNF
|
| 111 |
+
|
| 112 |
+
enc_cnf = enc_cnf.copy()
|
| 113 |
+
cur_enc = 1
|
| 114 |
+
rev_encoding = {value: key for key, value in enc_cnf.encoding.items()}
|
| 115 |
+
|
| 116 |
+
new_encoding = {}
|
| 117 |
+
new_data = []
|
| 118 |
+
for clause in enc_cnf.data:
|
| 119 |
+
new_clause = []
|
| 120 |
+
for lit in clause:
|
| 121 |
+
if lit == 0:
|
| 122 |
+
new_clause.append(lit)
|
| 123 |
+
new_encoding[lit] = False
|
| 124 |
+
continue
|
| 125 |
+
prop = rev_encoding[abs(lit)]
|
| 126 |
+
negated = lit < 0
|
| 127 |
+
sign = (lit > 0) - (lit < 0)
|
| 128 |
+
|
| 129 |
+
prop = _pred_to_binrel(prop)
|
| 130 |
+
|
| 131 |
+
if not isinstance(prop, AppliedPredicate):
|
| 132 |
+
if prop not in new_encoding:
|
| 133 |
+
new_encoding[prop] = cur_enc
|
| 134 |
+
cur_enc += 1
|
| 135 |
+
lit = new_encoding[prop]
|
| 136 |
+
new_clause.append(sign*lit)
|
| 137 |
+
continue
|
| 138 |
+
|
| 139 |
+
|
| 140 |
+
if negated and prop.function == Q.eq:
|
| 141 |
+
negated = False
|
| 142 |
+
prop = Q.ne(*prop.arguments)
|
| 143 |
+
|
| 144 |
+
if prop.function == Q.ne:
|
| 145 |
+
arg1, arg2 = prop.arguments
|
| 146 |
+
if negated:
|
| 147 |
+
new_prop = Q.eq(arg1, arg2)
|
| 148 |
+
if new_prop not in new_encoding:
|
| 149 |
+
new_encoding[new_prop] = cur_enc
|
| 150 |
+
cur_enc += 1
|
| 151 |
+
|
| 152 |
+
new_enc = new_encoding[new_prop]
|
| 153 |
+
new_clause.append(new_enc)
|
| 154 |
+
continue
|
| 155 |
+
else:
|
| 156 |
+
new_props = (Q.gt(arg1, arg2), Q.lt(arg1, arg2))
|
| 157 |
+
for new_prop in new_props:
|
| 158 |
+
if new_prop not in new_encoding:
|
| 159 |
+
new_encoding[new_prop] = cur_enc
|
| 160 |
+
cur_enc += 1
|
| 161 |
+
|
| 162 |
+
new_enc = new_encoding[new_prop]
|
| 163 |
+
new_clause.append(new_enc)
|
| 164 |
+
continue
|
| 165 |
+
|
| 166 |
+
if prop.function == Q.eq and negated:
|
| 167 |
+
assert False
|
| 168 |
+
|
| 169 |
+
if prop not in new_encoding:
|
| 170 |
+
new_encoding[prop] = cur_enc
|
| 171 |
+
cur_enc += 1
|
| 172 |
+
new_clause.append(new_encoding[prop]*sign)
|
| 173 |
+
new_data.append(new_clause)
|
| 174 |
+
|
| 175 |
+
assert len(new_encoding) >= cur_enc - 1
|
| 176 |
+
|
| 177 |
+
enc_cnf = EncodedCNF(new_data, new_encoding)
|
| 178 |
+
return enc_cnf
|
| 179 |
+
|
| 180 |
+
|
| 181 |
+
def _pred_to_binrel(pred):
|
| 182 |
+
if not isinstance(pred, AppliedPredicate):
|
| 183 |
+
return pred
|
| 184 |
+
|
| 185 |
+
if pred.function in pred_to_pos_neg_zero:
|
| 186 |
+
f = pred_to_pos_neg_zero[pred.function]
|
| 187 |
+
if f is False:
|
| 188 |
+
return False
|
| 189 |
+
pred = f(pred.arguments[0])
|
| 190 |
+
|
| 191 |
+
if pred.function == Q.positive:
|
| 192 |
+
pred = Q.gt(pred.arguments[0], 0)
|
| 193 |
+
elif pred.function == Q.negative:
|
| 194 |
+
pred = Q.lt(pred.arguments[0], 0)
|
| 195 |
+
elif pred.function == Q.zero:
|
| 196 |
+
pred = Q.eq(pred.arguments[0], 0)
|
| 197 |
+
elif pred.function == Q.nonpositive:
|
| 198 |
+
pred = Q.le(pred.arguments[0], 0)
|
| 199 |
+
elif pred.function == Q.nonnegative:
|
| 200 |
+
pred = Q.ge(pred.arguments[0], 0)
|
| 201 |
+
elif pred.function == Q.nonzero:
|
| 202 |
+
pred = Q.ne(pred.arguments[0], 0)
|
| 203 |
+
|
| 204 |
+
return pred
|
| 205 |
+
|
| 206 |
+
pred_to_pos_neg_zero = {
|
| 207 |
+
Q.extended_positive: Q.positive,
|
| 208 |
+
Q.extended_negative: Q.negative,
|
| 209 |
+
Q.extended_nonpositive: Q.nonpositive,
|
| 210 |
+
Q.extended_negative: Q.negative,
|
| 211 |
+
Q.extended_nonzero: Q.nonzero,
|
| 212 |
+
Q.negative_infinite: False,
|
| 213 |
+
Q.positive_infinite: False
|
| 214 |
+
}
|
| 215 |
+
|
| 216 |
+
def get_all_pred_and_expr_from_enc_cnf(enc_cnf):
|
| 217 |
+
all_exprs = set()
|
| 218 |
+
all_pred = set()
|
| 219 |
+
for pred in enc_cnf.encoding.keys():
|
| 220 |
+
if isinstance(pred, AppliedPredicate):
|
| 221 |
+
all_pred.add(pred)
|
| 222 |
+
all_exprs.update(pred.arguments)
|
| 223 |
+
|
| 224 |
+
return all_pred, all_exprs
|
| 225 |
+
|
| 226 |
+
def extract_pred_from_old_assum(all_exprs):
|
| 227 |
+
"""
|
| 228 |
+
Returns a list of relevant new assumption predicate
|
| 229 |
+
based on any old assumptions.
|
| 230 |
+
|
| 231 |
+
Raises an UnhandledInput exception if any of the assumptions are
|
| 232 |
+
unhandled.
|
| 233 |
+
|
| 234 |
+
Ignored predicate:
|
| 235 |
+
- commutative
|
| 236 |
+
- complex
|
| 237 |
+
- algebraic
|
| 238 |
+
- transcendental
|
| 239 |
+
- extended_real
|
| 240 |
+
- real
|
| 241 |
+
- all matrix predicate
|
| 242 |
+
- rational
|
| 243 |
+
- irrational
|
| 244 |
+
|
| 245 |
+
Example
|
| 246 |
+
=======
|
| 247 |
+
>>> from sympy.assumptions.lra_satask import extract_pred_from_old_assum
|
| 248 |
+
>>> from sympy import symbols
|
| 249 |
+
>>> x, y = symbols("x y", positive=True)
|
| 250 |
+
>>> extract_pred_from_old_assum([x, y, 2])
|
| 251 |
+
[Q.positive(x), Q.positive(y)]
|
| 252 |
+
"""
|
| 253 |
+
ret = []
|
| 254 |
+
for expr in all_exprs:
|
| 255 |
+
if not hasattr(expr, "free_symbols"):
|
| 256 |
+
continue
|
| 257 |
+
if len(expr.free_symbols) == 0:
|
| 258 |
+
continue
|
| 259 |
+
|
| 260 |
+
if expr.is_real is not True:
|
| 261 |
+
raise UnhandledInput(f"LRASolver: {expr} must be real")
|
| 262 |
+
# test for I times imaginary variable; such expressions are considered real
|
| 263 |
+
if isinstance(expr, Mul) and any(arg.is_real is not True for arg in expr.args):
|
| 264 |
+
raise UnhandledInput(f"LRASolver: {expr} must be real")
|
| 265 |
+
|
| 266 |
+
if expr.is_integer == True and expr.is_zero != True:
|
| 267 |
+
raise UnhandledInput(f"LRASolver: {expr} is an integer")
|
| 268 |
+
if expr.is_integer == False:
|
| 269 |
+
raise UnhandledInput(f"LRASolver: {expr} can't be an integer")
|
| 270 |
+
if expr.is_rational == False:
|
| 271 |
+
raise UnhandledInput(f"LRASolver: {expr} is irational")
|
| 272 |
+
|
| 273 |
+
if expr.is_zero:
|
| 274 |
+
ret.append(Q.zero(expr))
|
| 275 |
+
elif expr.is_positive:
|
| 276 |
+
ret.append(Q.positive(expr))
|
| 277 |
+
elif expr.is_negative:
|
| 278 |
+
ret.append(Q.negative(expr))
|
| 279 |
+
elif expr.is_nonzero:
|
| 280 |
+
ret.append(Q.nonzero(expr))
|
| 281 |
+
elif expr.is_nonpositive:
|
| 282 |
+
ret.append(Q.nonpositive(expr))
|
| 283 |
+
elif expr.is_nonnegative:
|
| 284 |
+
ret.append(Q.nonnegative(expr))
|
| 285 |
+
|
| 286 |
+
return ret
|
lib/python3.12/site-packages/sympy/assumptions/refine.py
ADDED
|
@@ -0,0 +1,405 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from __future__ import annotations
|
| 2 |
+
from typing import Callable
|
| 3 |
+
|
| 4 |
+
from sympy.core import S, Add, Expr, Basic, Mul, Pow, Rational
|
| 5 |
+
from sympy.core.logic import fuzzy_not
|
| 6 |
+
from sympy.logic.boolalg import Boolean
|
| 7 |
+
|
| 8 |
+
from sympy.assumptions import ask, Q # type: ignore
|
| 9 |
+
|
| 10 |
+
|
| 11 |
+
def refine(expr, assumptions=True):
|
| 12 |
+
"""
|
| 13 |
+
Simplify an expression using assumptions.
|
| 14 |
+
|
| 15 |
+
Explanation
|
| 16 |
+
===========
|
| 17 |
+
|
| 18 |
+
Unlike :func:`~.simplify()` which performs structural simplification
|
| 19 |
+
without any assumption, this function transforms the expression into
|
| 20 |
+
the form which is only valid under certain assumptions. Note that
|
| 21 |
+
``simplify()`` is generally not done in refining process.
|
| 22 |
+
|
| 23 |
+
Refining boolean expression involves reducing it to ``S.true`` or
|
| 24 |
+
``S.false``. Unlike :func:`~.ask()`, the expression will not be reduced
|
| 25 |
+
if the truth value cannot be determined.
|
| 26 |
+
|
| 27 |
+
Examples
|
| 28 |
+
========
|
| 29 |
+
|
| 30 |
+
>>> from sympy import refine, sqrt, Q
|
| 31 |
+
>>> from sympy.abc import x
|
| 32 |
+
>>> refine(sqrt(x**2), Q.real(x))
|
| 33 |
+
Abs(x)
|
| 34 |
+
>>> refine(sqrt(x**2), Q.positive(x))
|
| 35 |
+
x
|
| 36 |
+
|
| 37 |
+
>>> refine(Q.real(x), Q.positive(x))
|
| 38 |
+
True
|
| 39 |
+
>>> refine(Q.positive(x), Q.real(x))
|
| 40 |
+
Q.positive(x)
|
| 41 |
+
|
| 42 |
+
See Also
|
| 43 |
+
========
|
| 44 |
+
|
| 45 |
+
sympy.simplify.simplify.simplify : Structural simplification without assumptions.
|
| 46 |
+
sympy.assumptions.ask.ask : Query for boolean expressions using assumptions.
|
| 47 |
+
"""
|
| 48 |
+
if not isinstance(expr, Basic):
|
| 49 |
+
return expr
|
| 50 |
+
|
| 51 |
+
if not expr.is_Atom:
|
| 52 |
+
args = [refine(arg, assumptions) for arg in expr.args]
|
| 53 |
+
# TODO: this will probably not work with Integral or Polynomial
|
| 54 |
+
expr = expr.func(*args)
|
| 55 |
+
if hasattr(expr, '_eval_refine'):
|
| 56 |
+
ref_expr = expr._eval_refine(assumptions)
|
| 57 |
+
if ref_expr is not None:
|
| 58 |
+
return ref_expr
|
| 59 |
+
name = expr.__class__.__name__
|
| 60 |
+
handler = handlers_dict.get(name, None)
|
| 61 |
+
if handler is None:
|
| 62 |
+
return expr
|
| 63 |
+
new_expr = handler(expr, assumptions)
|
| 64 |
+
if (new_expr is None) or (expr == new_expr):
|
| 65 |
+
return expr
|
| 66 |
+
if not isinstance(new_expr, Expr):
|
| 67 |
+
return new_expr
|
| 68 |
+
return refine(new_expr, assumptions)
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def refine_abs(expr, assumptions):
|
| 72 |
+
"""
|
| 73 |
+
Handler for the absolute value.
|
| 74 |
+
|
| 75 |
+
Examples
|
| 76 |
+
========
|
| 77 |
+
|
| 78 |
+
>>> from sympy import Q, Abs
|
| 79 |
+
>>> from sympy.assumptions.refine import refine_abs
|
| 80 |
+
>>> from sympy.abc import x
|
| 81 |
+
>>> refine_abs(Abs(x), Q.real(x))
|
| 82 |
+
>>> refine_abs(Abs(x), Q.positive(x))
|
| 83 |
+
x
|
| 84 |
+
>>> refine_abs(Abs(x), Q.negative(x))
|
| 85 |
+
-x
|
| 86 |
+
|
| 87 |
+
"""
|
| 88 |
+
from sympy.functions.elementary.complexes import Abs
|
| 89 |
+
arg = expr.args[0]
|
| 90 |
+
if ask(Q.real(arg), assumptions) and \
|
| 91 |
+
fuzzy_not(ask(Q.negative(arg), assumptions)):
|
| 92 |
+
# if it's nonnegative
|
| 93 |
+
return arg
|
| 94 |
+
if ask(Q.negative(arg), assumptions):
|
| 95 |
+
return -arg
|
| 96 |
+
# arg is Mul
|
| 97 |
+
if isinstance(arg, Mul):
|
| 98 |
+
r = [refine(abs(a), assumptions) for a in arg.args]
|
| 99 |
+
non_abs = []
|
| 100 |
+
in_abs = []
|
| 101 |
+
for i in r:
|
| 102 |
+
if isinstance(i, Abs):
|
| 103 |
+
in_abs.append(i.args[0])
|
| 104 |
+
else:
|
| 105 |
+
non_abs.append(i)
|
| 106 |
+
return Mul(*non_abs) * Abs(Mul(*in_abs))
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def refine_Pow(expr, assumptions):
|
| 110 |
+
"""
|
| 111 |
+
Handler for instances of Pow.
|
| 112 |
+
|
| 113 |
+
Examples
|
| 114 |
+
========
|
| 115 |
+
|
| 116 |
+
>>> from sympy import Q
|
| 117 |
+
>>> from sympy.assumptions.refine import refine_Pow
|
| 118 |
+
>>> from sympy.abc import x,y,z
|
| 119 |
+
>>> refine_Pow((-1)**x, Q.real(x))
|
| 120 |
+
>>> refine_Pow((-1)**x, Q.even(x))
|
| 121 |
+
1
|
| 122 |
+
>>> refine_Pow((-1)**x, Q.odd(x))
|
| 123 |
+
-1
|
| 124 |
+
|
| 125 |
+
For powers of -1, even parts of the exponent can be simplified:
|
| 126 |
+
|
| 127 |
+
>>> refine_Pow((-1)**(x+y), Q.even(x))
|
| 128 |
+
(-1)**y
|
| 129 |
+
>>> refine_Pow((-1)**(x+y+z), Q.odd(x) & Q.odd(z))
|
| 130 |
+
(-1)**y
|
| 131 |
+
>>> refine_Pow((-1)**(x+y+2), Q.odd(x))
|
| 132 |
+
(-1)**(y + 1)
|
| 133 |
+
>>> refine_Pow((-1)**(x+3), True)
|
| 134 |
+
(-1)**(x + 1)
|
| 135 |
+
|
| 136 |
+
"""
|
| 137 |
+
from sympy.functions.elementary.complexes import Abs
|
| 138 |
+
from sympy.functions import sign
|
| 139 |
+
if isinstance(expr.base, Abs):
|
| 140 |
+
if ask(Q.real(expr.base.args[0]), assumptions) and \
|
| 141 |
+
ask(Q.even(expr.exp), assumptions):
|
| 142 |
+
return expr.base.args[0] ** expr.exp
|
| 143 |
+
if ask(Q.real(expr.base), assumptions):
|
| 144 |
+
if expr.base.is_number:
|
| 145 |
+
if ask(Q.even(expr.exp), assumptions):
|
| 146 |
+
return abs(expr.base) ** expr.exp
|
| 147 |
+
if ask(Q.odd(expr.exp), assumptions):
|
| 148 |
+
return sign(expr.base) * abs(expr.base) ** expr.exp
|
| 149 |
+
if isinstance(expr.exp, Rational):
|
| 150 |
+
if isinstance(expr.base, Pow):
|
| 151 |
+
return abs(expr.base.base) ** (expr.base.exp * expr.exp)
|
| 152 |
+
|
| 153 |
+
if expr.base is S.NegativeOne:
|
| 154 |
+
if expr.exp.is_Add:
|
| 155 |
+
|
| 156 |
+
old = expr
|
| 157 |
+
|
| 158 |
+
# For powers of (-1) we can remove
|
| 159 |
+
# - even terms
|
| 160 |
+
# - pairs of odd terms
|
| 161 |
+
# - a single odd term + 1
|
| 162 |
+
# - A numerical constant N can be replaced with mod(N,2)
|
| 163 |
+
|
| 164 |
+
coeff, terms = expr.exp.as_coeff_add()
|
| 165 |
+
terms = set(terms)
|
| 166 |
+
even_terms = set()
|
| 167 |
+
odd_terms = set()
|
| 168 |
+
initial_number_of_terms = len(terms)
|
| 169 |
+
|
| 170 |
+
for t in terms:
|
| 171 |
+
if ask(Q.even(t), assumptions):
|
| 172 |
+
even_terms.add(t)
|
| 173 |
+
elif ask(Q.odd(t), assumptions):
|
| 174 |
+
odd_terms.add(t)
|
| 175 |
+
|
| 176 |
+
terms -= even_terms
|
| 177 |
+
if len(odd_terms) % 2:
|
| 178 |
+
terms -= odd_terms
|
| 179 |
+
new_coeff = (coeff + S.One) % 2
|
| 180 |
+
else:
|
| 181 |
+
terms -= odd_terms
|
| 182 |
+
new_coeff = coeff % 2
|
| 183 |
+
|
| 184 |
+
if new_coeff != coeff or len(terms) < initial_number_of_terms:
|
| 185 |
+
terms.add(new_coeff)
|
| 186 |
+
expr = expr.base**(Add(*terms))
|
| 187 |
+
|
| 188 |
+
# Handle (-1)**((-1)**n/2 + m/2)
|
| 189 |
+
e2 = 2*expr.exp
|
| 190 |
+
if ask(Q.even(e2), assumptions):
|
| 191 |
+
if e2.could_extract_minus_sign():
|
| 192 |
+
e2 *= expr.base
|
| 193 |
+
if e2.is_Add:
|
| 194 |
+
i, p = e2.as_two_terms()
|
| 195 |
+
if p.is_Pow and p.base is S.NegativeOne:
|
| 196 |
+
if ask(Q.integer(p.exp), assumptions):
|
| 197 |
+
i = (i + 1)/2
|
| 198 |
+
if ask(Q.even(i), assumptions):
|
| 199 |
+
return expr.base**p.exp
|
| 200 |
+
elif ask(Q.odd(i), assumptions):
|
| 201 |
+
return expr.base**(p.exp + 1)
|
| 202 |
+
else:
|
| 203 |
+
return expr.base**(p.exp + i)
|
| 204 |
+
|
| 205 |
+
if old != expr:
|
| 206 |
+
return expr
|
| 207 |
+
|
| 208 |
+
|
| 209 |
+
def refine_atan2(expr, assumptions):
|
| 210 |
+
"""
|
| 211 |
+
Handler for the atan2 function.
|
| 212 |
+
|
| 213 |
+
Examples
|
| 214 |
+
========
|
| 215 |
+
|
| 216 |
+
>>> from sympy import Q, atan2
|
| 217 |
+
>>> from sympy.assumptions.refine import refine_atan2
|
| 218 |
+
>>> from sympy.abc import x, y
|
| 219 |
+
>>> refine_atan2(atan2(y,x), Q.real(y) & Q.positive(x))
|
| 220 |
+
atan(y/x)
|
| 221 |
+
>>> refine_atan2(atan2(y,x), Q.negative(y) & Q.negative(x))
|
| 222 |
+
atan(y/x) - pi
|
| 223 |
+
>>> refine_atan2(atan2(y,x), Q.positive(y) & Q.negative(x))
|
| 224 |
+
atan(y/x) + pi
|
| 225 |
+
>>> refine_atan2(atan2(y,x), Q.zero(y) & Q.negative(x))
|
| 226 |
+
pi
|
| 227 |
+
>>> refine_atan2(atan2(y,x), Q.positive(y) & Q.zero(x))
|
| 228 |
+
pi/2
|
| 229 |
+
>>> refine_atan2(atan2(y,x), Q.negative(y) & Q.zero(x))
|
| 230 |
+
-pi/2
|
| 231 |
+
>>> refine_atan2(atan2(y,x), Q.zero(y) & Q.zero(x))
|
| 232 |
+
nan
|
| 233 |
+
"""
|
| 234 |
+
from sympy.functions.elementary.trigonometric import atan
|
| 235 |
+
y, x = expr.args
|
| 236 |
+
if ask(Q.real(y) & Q.positive(x), assumptions):
|
| 237 |
+
return atan(y / x)
|
| 238 |
+
elif ask(Q.negative(y) & Q.negative(x), assumptions):
|
| 239 |
+
return atan(y / x) - S.Pi
|
| 240 |
+
elif ask(Q.positive(y) & Q.negative(x), assumptions):
|
| 241 |
+
return atan(y / x) + S.Pi
|
| 242 |
+
elif ask(Q.zero(y) & Q.negative(x), assumptions):
|
| 243 |
+
return S.Pi
|
| 244 |
+
elif ask(Q.positive(y) & Q.zero(x), assumptions):
|
| 245 |
+
return S.Pi/2
|
| 246 |
+
elif ask(Q.negative(y) & Q.zero(x), assumptions):
|
| 247 |
+
return -S.Pi/2
|
| 248 |
+
elif ask(Q.zero(y) & Q.zero(x), assumptions):
|
| 249 |
+
return S.NaN
|
| 250 |
+
else:
|
| 251 |
+
return expr
|
| 252 |
+
|
| 253 |
+
|
| 254 |
+
def refine_re(expr, assumptions):
|
| 255 |
+
"""
|
| 256 |
+
Handler for real part.
|
| 257 |
+
|
| 258 |
+
Examples
|
| 259 |
+
========
|
| 260 |
+
|
| 261 |
+
>>> from sympy.assumptions.refine import refine_re
|
| 262 |
+
>>> from sympy import Q, re
|
| 263 |
+
>>> from sympy.abc import x
|
| 264 |
+
>>> refine_re(re(x), Q.real(x))
|
| 265 |
+
x
|
| 266 |
+
>>> refine_re(re(x), Q.imaginary(x))
|
| 267 |
+
0
|
| 268 |
+
"""
|
| 269 |
+
arg = expr.args[0]
|
| 270 |
+
if ask(Q.real(arg), assumptions):
|
| 271 |
+
return arg
|
| 272 |
+
if ask(Q.imaginary(arg), assumptions):
|
| 273 |
+
return S.Zero
|
| 274 |
+
return _refine_reim(expr, assumptions)
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
def refine_im(expr, assumptions):
|
| 278 |
+
"""
|
| 279 |
+
Handler for imaginary part.
|
| 280 |
+
|
| 281 |
+
Explanation
|
| 282 |
+
===========
|
| 283 |
+
|
| 284 |
+
>>> from sympy.assumptions.refine import refine_im
|
| 285 |
+
>>> from sympy import Q, im
|
| 286 |
+
>>> from sympy.abc import x
|
| 287 |
+
>>> refine_im(im(x), Q.real(x))
|
| 288 |
+
0
|
| 289 |
+
>>> refine_im(im(x), Q.imaginary(x))
|
| 290 |
+
-I*x
|
| 291 |
+
"""
|
| 292 |
+
arg = expr.args[0]
|
| 293 |
+
if ask(Q.real(arg), assumptions):
|
| 294 |
+
return S.Zero
|
| 295 |
+
if ask(Q.imaginary(arg), assumptions):
|
| 296 |
+
return - S.ImaginaryUnit * arg
|
| 297 |
+
return _refine_reim(expr, assumptions)
|
| 298 |
+
|
| 299 |
+
def refine_arg(expr, assumptions):
|
| 300 |
+
"""
|
| 301 |
+
Handler for complex argument
|
| 302 |
+
|
| 303 |
+
Explanation
|
| 304 |
+
===========
|
| 305 |
+
|
| 306 |
+
>>> from sympy.assumptions.refine import refine_arg
|
| 307 |
+
>>> from sympy import Q, arg
|
| 308 |
+
>>> from sympy.abc import x
|
| 309 |
+
>>> refine_arg(arg(x), Q.positive(x))
|
| 310 |
+
0
|
| 311 |
+
>>> refine_arg(arg(x), Q.negative(x))
|
| 312 |
+
pi
|
| 313 |
+
"""
|
| 314 |
+
rg = expr.args[0]
|
| 315 |
+
if ask(Q.positive(rg), assumptions):
|
| 316 |
+
return S.Zero
|
| 317 |
+
if ask(Q.negative(rg), assumptions):
|
| 318 |
+
return S.Pi
|
| 319 |
+
return None
|
| 320 |
+
|
| 321 |
+
|
| 322 |
+
def _refine_reim(expr, assumptions):
|
| 323 |
+
# Helper function for refine_re & refine_im
|
| 324 |
+
expanded = expr.expand(complex = True)
|
| 325 |
+
if expanded != expr:
|
| 326 |
+
refined = refine(expanded, assumptions)
|
| 327 |
+
if refined != expanded:
|
| 328 |
+
return refined
|
| 329 |
+
# Best to leave the expression as is
|
| 330 |
+
return None
|
| 331 |
+
|
| 332 |
+
|
| 333 |
+
def refine_sign(expr, assumptions):
|
| 334 |
+
"""
|
| 335 |
+
Handler for sign.
|
| 336 |
+
|
| 337 |
+
Examples
|
| 338 |
+
========
|
| 339 |
+
|
| 340 |
+
>>> from sympy.assumptions.refine import refine_sign
|
| 341 |
+
>>> from sympy import Symbol, Q, sign, im
|
| 342 |
+
>>> x = Symbol('x', real = True)
|
| 343 |
+
>>> expr = sign(x)
|
| 344 |
+
>>> refine_sign(expr, Q.positive(x) & Q.nonzero(x))
|
| 345 |
+
1
|
| 346 |
+
>>> refine_sign(expr, Q.negative(x) & Q.nonzero(x))
|
| 347 |
+
-1
|
| 348 |
+
>>> refine_sign(expr, Q.zero(x))
|
| 349 |
+
0
|
| 350 |
+
>>> y = Symbol('y', imaginary = True)
|
| 351 |
+
>>> expr = sign(y)
|
| 352 |
+
>>> refine_sign(expr, Q.positive(im(y)))
|
| 353 |
+
I
|
| 354 |
+
>>> refine_sign(expr, Q.negative(im(y)))
|
| 355 |
+
-I
|
| 356 |
+
"""
|
| 357 |
+
arg = expr.args[0]
|
| 358 |
+
if ask(Q.zero(arg), assumptions):
|
| 359 |
+
return S.Zero
|
| 360 |
+
if ask(Q.real(arg)):
|
| 361 |
+
if ask(Q.positive(arg), assumptions):
|
| 362 |
+
return S.One
|
| 363 |
+
if ask(Q.negative(arg), assumptions):
|
| 364 |
+
return S.NegativeOne
|
| 365 |
+
if ask(Q.imaginary(arg)):
|
| 366 |
+
arg_re, arg_im = arg.as_real_imag()
|
| 367 |
+
if ask(Q.positive(arg_im), assumptions):
|
| 368 |
+
return S.ImaginaryUnit
|
| 369 |
+
if ask(Q.negative(arg_im), assumptions):
|
| 370 |
+
return -S.ImaginaryUnit
|
| 371 |
+
return expr
|
| 372 |
+
|
| 373 |
+
|
| 374 |
+
def refine_matrixelement(expr, assumptions):
|
| 375 |
+
"""
|
| 376 |
+
Handler for symmetric part.
|
| 377 |
+
|
| 378 |
+
Examples
|
| 379 |
+
========
|
| 380 |
+
|
| 381 |
+
>>> from sympy.assumptions.refine import refine_matrixelement
|
| 382 |
+
>>> from sympy import MatrixSymbol, Q
|
| 383 |
+
>>> X = MatrixSymbol('X', 3, 3)
|
| 384 |
+
>>> refine_matrixelement(X[0, 1], Q.symmetric(X))
|
| 385 |
+
X[0, 1]
|
| 386 |
+
>>> refine_matrixelement(X[1, 0], Q.symmetric(X))
|
| 387 |
+
X[0, 1]
|
| 388 |
+
"""
|
| 389 |
+
from sympy.matrices.expressions.matexpr import MatrixElement
|
| 390 |
+
matrix, i, j = expr.args
|
| 391 |
+
if ask(Q.symmetric(matrix), assumptions):
|
| 392 |
+
if (i - j).could_extract_minus_sign():
|
| 393 |
+
return expr
|
| 394 |
+
return MatrixElement(matrix, j, i)
|
| 395 |
+
|
| 396 |
+
handlers_dict: dict[str, Callable[[Expr, Boolean], Expr]] = {
|
| 397 |
+
'Abs': refine_abs,
|
| 398 |
+
'Pow': refine_Pow,
|
| 399 |
+
'atan2': refine_atan2,
|
| 400 |
+
're': refine_re,
|
| 401 |
+
'im': refine_im,
|
| 402 |
+
'arg': refine_arg,
|
| 403 |
+
'sign': refine_sign,
|
| 404 |
+
'MatrixElement': refine_matrixelement
|
| 405 |
+
}
|
lib/python3.12/site-packages/sympy/assumptions/satask.py
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Module to evaluate the proposition with assumptions using SAT algorithm.
|
| 3 |
+
"""
|
| 4 |
+
|
| 5 |
+
from sympy.core.singleton import S
|
| 6 |
+
from sympy.core.symbol import Symbol
|
| 7 |
+
from sympy.core.kind import NumberKind, UndefinedKind
|
| 8 |
+
from sympy.assumptions.ask_generated import get_all_known_matrix_facts, get_all_known_number_facts
|
| 9 |
+
from sympy.assumptions.assume import global_assumptions, AppliedPredicate
|
| 10 |
+
from sympy.assumptions.sathandlers import class_fact_registry
|
| 11 |
+
from sympy.core import oo
|
| 12 |
+
from sympy.logic.inference import satisfiable
|
| 13 |
+
from sympy.assumptions.cnf import CNF, EncodedCNF
|
| 14 |
+
from sympy.matrices.kind import MatrixKind
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def satask(proposition, assumptions=True, context=global_assumptions,
|
| 18 |
+
use_known_facts=True, iterations=oo):
|
| 19 |
+
"""
|
| 20 |
+
Function to evaluate the proposition with assumptions using SAT algorithm.
|
| 21 |
+
|
| 22 |
+
This function extracts every fact relevant to the expressions composing
|
| 23 |
+
proposition and assumptions. For example, if a predicate containing
|
| 24 |
+
``Abs(x)`` is proposed, then ``Q.zero(Abs(x)) | Q.positive(Abs(x))``
|
| 25 |
+
will be found and passed to SAT solver because ``Q.nonnegative`` is
|
| 26 |
+
registered as a fact for ``Abs``.
|
| 27 |
+
|
| 28 |
+
Proposition is evaluated to ``True`` or ``False`` if the truth value can be
|
| 29 |
+
determined. If not, ``None`` is returned.
|
| 30 |
+
|
| 31 |
+
Parameters
|
| 32 |
+
==========
|
| 33 |
+
|
| 34 |
+
proposition : Any boolean expression.
|
| 35 |
+
Proposition which will be evaluated to boolean value.
|
| 36 |
+
|
| 37 |
+
assumptions : Any boolean expression, optional.
|
| 38 |
+
Local assumptions to evaluate the *proposition*.
|
| 39 |
+
|
| 40 |
+
context : AssumptionsContext, optional.
|
| 41 |
+
Default assumptions to evaluate the *proposition*. By default,
|
| 42 |
+
this is ``sympy.assumptions.global_assumptions`` variable.
|
| 43 |
+
|
| 44 |
+
use_known_facts : bool, optional.
|
| 45 |
+
If ``True``, facts from ``sympy.assumptions.ask_generated``
|
| 46 |
+
module are passed to SAT solver as well.
|
| 47 |
+
|
| 48 |
+
iterations : int, optional.
|
| 49 |
+
Number of times that relevant facts are recursively extracted.
|
| 50 |
+
Default is infinite times until no new fact is found.
|
| 51 |
+
|
| 52 |
+
Returns
|
| 53 |
+
=======
|
| 54 |
+
|
| 55 |
+
``True``, ``False``, or ``None``
|
| 56 |
+
|
| 57 |
+
Examples
|
| 58 |
+
========
|
| 59 |
+
|
| 60 |
+
>>> from sympy import Abs, Q
|
| 61 |
+
>>> from sympy.assumptions.satask import satask
|
| 62 |
+
>>> from sympy.abc import x
|
| 63 |
+
>>> satask(Q.zero(Abs(x)), Q.zero(x))
|
| 64 |
+
True
|
| 65 |
+
|
| 66 |
+
"""
|
| 67 |
+
props = CNF.from_prop(proposition)
|
| 68 |
+
_props = CNF.from_prop(~proposition)
|
| 69 |
+
|
| 70 |
+
assumptions = CNF.from_prop(assumptions)
|
| 71 |
+
|
| 72 |
+
context_cnf = CNF()
|
| 73 |
+
if context:
|
| 74 |
+
context_cnf = context_cnf.extend(context)
|
| 75 |
+
|
| 76 |
+
sat = get_all_relevant_facts(props, assumptions, context_cnf,
|
| 77 |
+
use_known_facts=use_known_facts, iterations=iterations)
|
| 78 |
+
sat.add_from_cnf(assumptions)
|
| 79 |
+
if context:
|
| 80 |
+
sat.add_from_cnf(context_cnf)
|
| 81 |
+
|
| 82 |
+
return check_satisfiability(props, _props, sat)
|
| 83 |
+
|
| 84 |
+
|
| 85 |
+
def check_satisfiability(prop, _prop, factbase):
|
| 86 |
+
sat_true = factbase.copy()
|
| 87 |
+
sat_false = factbase.copy()
|
| 88 |
+
sat_true.add_from_cnf(prop)
|
| 89 |
+
sat_false.add_from_cnf(_prop)
|
| 90 |
+
can_be_true = satisfiable(sat_true)
|
| 91 |
+
can_be_false = satisfiable(sat_false)
|
| 92 |
+
|
| 93 |
+
if can_be_true and can_be_false:
|
| 94 |
+
return None
|
| 95 |
+
|
| 96 |
+
if can_be_true and not can_be_false:
|
| 97 |
+
return True
|
| 98 |
+
|
| 99 |
+
if not can_be_true and can_be_false:
|
| 100 |
+
return False
|
| 101 |
+
|
| 102 |
+
if not can_be_true and not can_be_false:
|
| 103 |
+
# TODO: Run additional checks to see which combination of the
|
| 104 |
+
# assumptions, global_assumptions, and relevant_facts are
|
| 105 |
+
# inconsistent.
|
| 106 |
+
raise ValueError("Inconsistent assumptions")
|
| 107 |
+
|
| 108 |
+
|
| 109 |
+
def extract_predargs(proposition, assumptions=None, context=None):
|
| 110 |
+
"""
|
| 111 |
+
Extract every expression in the argument of predicates from *proposition*,
|
| 112 |
+
*assumptions* and *context*.
|
| 113 |
+
|
| 114 |
+
Parameters
|
| 115 |
+
==========
|
| 116 |
+
|
| 117 |
+
proposition : sympy.assumptions.cnf.CNF
|
| 118 |
+
|
| 119 |
+
assumptions : sympy.assumptions.cnf.CNF, optional.
|
| 120 |
+
|
| 121 |
+
context : sympy.assumptions.cnf.CNF, optional.
|
| 122 |
+
CNF generated from assumptions context.
|
| 123 |
+
|
| 124 |
+
Examples
|
| 125 |
+
========
|
| 126 |
+
|
| 127 |
+
>>> from sympy import Q, Abs
|
| 128 |
+
>>> from sympy.assumptions.cnf import CNF
|
| 129 |
+
>>> from sympy.assumptions.satask import extract_predargs
|
| 130 |
+
>>> from sympy.abc import x, y
|
| 131 |
+
>>> props = CNF.from_prop(Q.zero(Abs(x*y)))
|
| 132 |
+
>>> assump = CNF.from_prop(Q.zero(x) & Q.zero(y))
|
| 133 |
+
>>> extract_predargs(props, assump)
|
| 134 |
+
{x, y, Abs(x*y)}
|
| 135 |
+
|
| 136 |
+
"""
|
| 137 |
+
req_keys = find_symbols(proposition)
|
| 138 |
+
keys = proposition.all_predicates()
|
| 139 |
+
# XXX: We need this since True/False are not Basic
|
| 140 |
+
lkeys = set()
|
| 141 |
+
if assumptions:
|
| 142 |
+
lkeys |= assumptions.all_predicates()
|
| 143 |
+
if context:
|
| 144 |
+
lkeys |= context.all_predicates()
|
| 145 |
+
|
| 146 |
+
lkeys = lkeys - {S.true, S.false}
|
| 147 |
+
tmp_keys = None
|
| 148 |
+
while tmp_keys != set():
|
| 149 |
+
tmp = set()
|
| 150 |
+
for l in lkeys:
|
| 151 |
+
syms = find_symbols(l)
|
| 152 |
+
if (syms & req_keys) != set():
|
| 153 |
+
tmp |= syms
|
| 154 |
+
tmp_keys = tmp - req_keys
|
| 155 |
+
req_keys |= tmp_keys
|
| 156 |
+
keys |= {l for l in lkeys if find_symbols(l) & req_keys != set()}
|
| 157 |
+
|
| 158 |
+
exprs = set()
|
| 159 |
+
for key in keys:
|
| 160 |
+
if isinstance(key, AppliedPredicate):
|
| 161 |
+
exprs |= set(key.arguments)
|
| 162 |
+
else:
|
| 163 |
+
exprs.add(key)
|
| 164 |
+
return exprs
|
| 165 |
+
|
| 166 |
+
def find_symbols(pred):
|
| 167 |
+
"""
|
| 168 |
+
Find every :obj:`~.Symbol` in *pred*.
|
| 169 |
+
|
| 170 |
+
Parameters
|
| 171 |
+
==========
|
| 172 |
+
|
| 173 |
+
pred : sympy.assumptions.cnf.CNF, or any Expr.
|
| 174 |
+
|
| 175 |
+
"""
|
| 176 |
+
if isinstance(pred, CNF):
|
| 177 |
+
symbols = set()
|
| 178 |
+
for a in pred.all_predicates():
|
| 179 |
+
symbols |= find_symbols(a)
|
| 180 |
+
return symbols
|
| 181 |
+
return pred.atoms(Symbol)
|
| 182 |
+
|
| 183 |
+
|
| 184 |
+
def get_relevant_clsfacts(exprs, relevant_facts=None):
|
| 185 |
+
"""
|
| 186 |
+
Extract relevant facts from the items in *exprs*. Facts are defined in
|
| 187 |
+
``assumptions.sathandlers`` module.
|
| 188 |
+
|
| 189 |
+
This function is recursively called by ``get_all_relevant_facts()``.
|
| 190 |
+
|
| 191 |
+
Parameters
|
| 192 |
+
==========
|
| 193 |
+
|
| 194 |
+
exprs : set
|
| 195 |
+
Expressions whose relevant facts are searched.
|
| 196 |
+
|
| 197 |
+
relevant_facts : sympy.assumptions.cnf.CNF, optional.
|
| 198 |
+
Pre-discovered relevant facts.
|
| 199 |
+
|
| 200 |
+
Returns
|
| 201 |
+
=======
|
| 202 |
+
|
| 203 |
+
exprs : set
|
| 204 |
+
Candidates for next relevant fact searching.
|
| 205 |
+
|
| 206 |
+
relevant_facts : sympy.assumptions.cnf.CNF
|
| 207 |
+
Updated relevant facts.
|
| 208 |
+
|
| 209 |
+
Examples
|
| 210 |
+
========
|
| 211 |
+
|
| 212 |
+
Here, we will see how facts relevant to ``Abs(x*y)`` are recursively
|
| 213 |
+
extracted. On the first run, set containing the expression is passed
|
| 214 |
+
without pre-discovered relevant facts. The result is a set containing
|
| 215 |
+
candidates for next run, and ``CNF()`` instance containing facts
|
| 216 |
+
which are relevant to ``Abs`` and its argument.
|
| 217 |
+
|
| 218 |
+
>>> from sympy import Abs
|
| 219 |
+
>>> from sympy.assumptions.satask import get_relevant_clsfacts
|
| 220 |
+
>>> from sympy.abc import x, y
|
| 221 |
+
>>> exprs = {Abs(x*y)}
|
| 222 |
+
>>> exprs, facts = get_relevant_clsfacts(exprs)
|
| 223 |
+
>>> exprs
|
| 224 |
+
{x*y}
|
| 225 |
+
>>> facts.clauses #doctest: +SKIP
|
| 226 |
+
{frozenset({Literal(Q.odd(Abs(x*y)), False), Literal(Q.odd(x*y), True)}),
|
| 227 |
+
frozenset({Literal(Q.zero(Abs(x*y)), False), Literal(Q.zero(x*y), True)}),
|
| 228 |
+
frozenset({Literal(Q.even(Abs(x*y)), False), Literal(Q.even(x*y), True)}),
|
| 229 |
+
frozenset({Literal(Q.zero(Abs(x*y)), True), Literal(Q.zero(x*y), False)}),
|
| 230 |
+
frozenset({Literal(Q.even(Abs(x*y)), False),
|
| 231 |
+
Literal(Q.odd(Abs(x*y)), False),
|
| 232 |
+
Literal(Q.odd(x*y), True)}),
|
| 233 |
+
frozenset({Literal(Q.even(Abs(x*y)), False),
|
| 234 |
+
Literal(Q.even(x*y), True),
|
| 235 |
+
Literal(Q.odd(Abs(x*y)), False)}),
|
| 236 |
+
frozenset({Literal(Q.positive(Abs(x*y)), False),
|
| 237 |
+
Literal(Q.zero(Abs(x*y)), False)})}
|
| 238 |
+
|
| 239 |
+
We pass the first run's results to the second run, and get the expressions
|
| 240 |
+
for next run and updated facts.
|
| 241 |
+
|
| 242 |
+
>>> exprs, facts = get_relevant_clsfacts(exprs, relevant_facts=facts)
|
| 243 |
+
>>> exprs
|
| 244 |
+
{x, y}
|
| 245 |
+
|
| 246 |
+
On final run, no more candidate is returned thus we know that all
|
| 247 |
+
relevant facts are successfully retrieved.
|
| 248 |
+
|
| 249 |
+
>>> exprs, facts = get_relevant_clsfacts(exprs, relevant_facts=facts)
|
| 250 |
+
>>> exprs
|
| 251 |
+
set()
|
| 252 |
+
|
| 253 |
+
"""
|
| 254 |
+
if not relevant_facts:
|
| 255 |
+
relevant_facts = CNF()
|
| 256 |
+
|
| 257 |
+
newexprs = set()
|
| 258 |
+
for expr in exprs:
|
| 259 |
+
for fact in class_fact_registry(expr):
|
| 260 |
+
newfact = CNF.to_CNF(fact)
|
| 261 |
+
relevant_facts = relevant_facts._and(newfact)
|
| 262 |
+
for key in newfact.all_predicates():
|
| 263 |
+
if isinstance(key, AppliedPredicate):
|
| 264 |
+
newexprs |= set(key.arguments)
|
| 265 |
+
|
| 266 |
+
return newexprs - exprs, relevant_facts
|
| 267 |
+
|
| 268 |
+
|
| 269 |
+
def get_all_relevant_facts(proposition, assumptions, context,
|
| 270 |
+
use_known_facts=True, iterations=oo):
|
| 271 |
+
"""
|
| 272 |
+
Extract all relevant facts from *proposition* and *assumptions*.
|
| 273 |
+
|
| 274 |
+
This function extracts the facts by recursively calling
|
| 275 |
+
``get_relevant_clsfacts()``. Extracted facts are converted to
|
| 276 |
+
``EncodedCNF`` and returned.
|
| 277 |
+
|
| 278 |
+
Parameters
|
| 279 |
+
==========
|
| 280 |
+
|
| 281 |
+
proposition : sympy.assumptions.cnf.CNF
|
| 282 |
+
CNF generated from proposition expression.
|
| 283 |
+
|
| 284 |
+
assumptions : sympy.assumptions.cnf.CNF
|
| 285 |
+
CNF generated from assumption expression.
|
| 286 |
+
|
| 287 |
+
context : sympy.assumptions.cnf.CNF
|
| 288 |
+
CNF generated from assumptions context.
|
| 289 |
+
|
| 290 |
+
use_known_facts : bool, optional.
|
| 291 |
+
If ``True``, facts from ``sympy.assumptions.ask_generated``
|
| 292 |
+
module are encoded as well.
|
| 293 |
+
|
| 294 |
+
iterations : int, optional.
|
| 295 |
+
Number of times that relevant facts are recursively extracted.
|
| 296 |
+
Default is infinite times until no new fact is found.
|
| 297 |
+
|
| 298 |
+
Returns
|
| 299 |
+
=======
|
| 300 |
+
|
| 301 |
+
sympy.assumptions.cnf.EncodedCNF
|
| 302 |
+
|
| 303 |
+
Examples
|
| 304 |
+
========
|
| 305 |
+
|
| 306 |
+
>>> from sympy import Q
|
| 307 |
+
>>> from sympy.assumptions.cnf import CNF
|
| 308 |
+
>>> from sympy.assumptions.satask import get_all_relevant_facts
|
| 309 |
+
>>> from sympy.abc import x, y
|
| 310 |
+
>>> props = CNF.from_prop(Q.nonzero(x*y))
|
| 311 |
+
>>> assump = CNF.from_prop(Q.nonzero(x))
|
| 312 |
+
>>> context = CNF.from_prop(Q.nonzero(y))
|
| 313 |
+
>>> get_all_relevant_facts(props, assump, context) #doctest: +SKIP
|
| 314 |
+
<sympy.assumptions.cnf.EncodedCNF at 0x7f09faa6ccd0>
|
| 315 |
+
|
| 316 |
+
"""
|
| 317 |
+
# The relevant facts might introduce new keys, e.g., Q.zero(x*y) will
|
| 318 |
+
# introduce the keys Q.zero(x) and Q.zero(y), so we need to run it until
|
| 319 |
+
# we stop getting new things. Hopefully this strategy won't lead to an
|
| 320 |
+
# infinite loop in the future.
|
| 321 |
+
i = 0
|
| 322 |
+
relevant_facts = CNF()
|
| 323 |
+
all_exprs = set()
|
| 324 |
+
while True:
|
| 325 |
+
if i == 0:
|
| 326 |
+
exprs = extract_predargs(proposition, assumptions, context)
|
| 327 |
+
all_exprs |= exprs
|
| 328 |
+
exprs, relevant_facts = get_relevant_clsfacts(exprs, relevant_facts)
|
| 329 |
+
i += 1
|
| 330 |
+
if i >= iterations:
|
| 331 |
+
break
|
| 332 |
+
if not exprs:
|
| 333 |
+
break
|
| 334 |
+
|
| 335 |
+
if use_known_facts:
|
| 336 |
+
known_facts_CNF = CNF()
|
| 337 |
+
|
| 338 |
+
if any(expr.kind == MatrixKind(NumberKind) for expr in all_exprs):
|
| 339 |
+
known_facts_CNF.add_clauses(get_all_known_matrix_facts())
|
| 340 |
+
# check for undefinedKind since kind system isn't fully implemented
|
| 341 |
+
if any(((expr.kind == NumberKind) or (expr.kind == UndefinedKind)) for expr in all_exprs):
|
| 342 |
+
known_facts_CNF.add_clauses(get_all_known_number_facts())
|
| 343 |
+
|
| 344 |
+
kf_encoded = EncodedCNF()
|
| 345 |
+
kf_encoded.from_cnf(known_facts_CNF)
|
| 346 |
+
|
| 347 |
+
def translate_literal(lit, delta):
|
| 348 |
+
if lit > 0:
|
| 349 |
+
return lit + delta
|
| 350 |
+
else:
|
| 351 |
+
return lit - delta
|
| 352 |
+
|
| 353 |
+
def translate_data(data, delta):
|
| 354 |
+
return [{translate_literal(i, delta) for i in clause} for clause in data]
|
| 355 |
+
data = []
|
| 356 |
+
symbols = []
|
| 357 |
+
n_lit = len(kf_encoded.symbols)
|
| 358 |
+
for i, expr in enumerate(all_exprs):
|
| 359 |
+
symbols += [pred(expr) for pred in kf_encoded.symbols]
|
| 360 |
+
data += translate_data(kf_encoded.data, i * n_lit)
|
| 361 |
+
|
| 362 |
+
encoding = dict(list(zip(symbols, range(1, len(symbols)+1))))
|
| 363 |
+
ctx = EncodedCNF(data, encoding)
|
| 364 |
+
else:
|
| 365 |
+
ctx = EncodedCNF()
|
| 366 |
+
|
| 367 |
+
ctx.add_from_cnf(relevant_facts)
|
| 368 |
+
|
| 369 |
+
return ctx
|
lib/python3.12/site-packages/sympy/assumptions/sathandlers.py
ADDED
|
@@ -0,0 +1,322 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from collections import defaultdict
|
| 2 |
+
|
| 3 |
+
from sympy.assumptions.ask import Q
|
| 4 |
+
from sympy.core import (Add, Mul, Pow, Number, NumberSymbol, Symbol)
|
| 5 |
+
from sympy.core.numbers import ImaginaryUnit
|
| 6 |
+
from sympy.functions.elementary.complexes import Abs
|
| 7 |
+
from sympy.logic.boolalg import (Equivalent, And, Or, Implies)
|
| 8 |
+
from sympy.matrices.expressions import MatMul
|
| 9 |
+
|
| 10 |
+
# APIs here may be subject to change
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
### Helper functions ###
|
| 14 |
+
|
| 15 |
+
def allargs(symbol, fact, expr):
|
| 16 |
+
"""
|
| 17 |
+
Apply all arguments of the expression to the fact structure.
|
| 18 |
+
|
| 19 |
+
Parameters
|
| 20 |
+
==========
|
| 21 |
+
|
| 22 |
+
symbol : Symbol
|
| 23 |
+
A placeholder symbol.
|
| 24 |
+
|
| 25 |
+
fact : Boolean
|
| 26 |
+
Resulting ``Boolean`` expression.
|
| 27 |
+
|
| 28 |
+
expr : Expr
|
| 29 |
+
|
| 30 |
+
Examples
|
| 31 |
+
========
|
| 32 |
+
|
| 33 |
+
>>> from sympy import Q
|
| 34 |
+
>>> from sympy.assumptions.sathandlers import allargs
|
| 35 |
+
>>> from sympy.abc import x, y
|
| 36 |
+
>>> allargs(x, Q.negative(x) | Q.positive(x), x*y)
|
| 37 |
+
(Q.negative(x) | Q.positive(x)) & (Q.negative(y) | Q.positive(y))
|
| 38 |
+
|
| 39 |
+
"""
|
| 40 |
+
return And(*[fact.subs(symbol, arg) for arg in expr.args])
|
| 41 |
+
|
| 42 |
+
|
| 43 |
+
def anyarg(symbol, fact, expr):
|
| 44 |
+
"""
|
| 45 |
+
Apply any argument of the expression to the fact structure.
|
| 46 |
+
|
| 47 |
+
Parameters
|
| 48 |
+
==========
|
| 49 |
+
|
| 50 |
+
symbol : Symbol
|
| 51 |
+
A placeholder symbol.
|
| 52 |
+
|
| 53 |
+
fact : Boolean
|
| 54 |
+
Resulting ``Boolean`` expression.
|
| 55 |
+
|
| 56 |
+
expr : Expr
|
| 57 |
+
|
| 58 |
+
Examples
|
| 59 |
+
========
|
| 60 |
+
|
| 61 |
+
>>> from sympy import Q
|
| 62 |
+
>>> from sympy.assumptions.sathandlers import anyarg
|
| 63 |
+
>>> from sympy.abc import x, y
|
| 64 |
+
>>> anyarg(x, Q.negative(x) & Q.positive(x), x*y)
|
| 65 |
+
(Q.negative(x) & Q.positive(x)) | (Q.negative(y) & Q.positive(y))
|
| 66 |
+
|
| 67 |
+
"""
|
| 68 |
+
return Or(*[fact.subs(symbol, arg) for arg in expr.args])
|
| 69 |
+
|
| 70 |
+
|
| 71 |
+
def exactlyonearg(symbol, fact, expr):
|
| 72 |
+
"""
|
| 73 |
+
Apply exactly one argument of the expression to the fact structure.
|
| 74 |
+
|
| 75 |
+
Parameters
|
| 76 |
+
==========
|
| 77 |
+
|
| 78 |
+
symbol : Symbol
|
| 79 |
+
A placeholder symbol.
|
| 80 |
+
|
| 81 |
+
fact : Boolean
|
| 82 |
+
Resulting ``Boolean`` expression.
|
| 83 |
+
|
| 84 |
+
expr : Expr
|
| 85 |
+
|
| 86 |
+
Examples
|
| 87 |
+
========
|
| 88 |
+
|
| 89 |
+
>>> from sympy import Q
|
| 90 |
+
>>> from sympy.assumptions.sathandlers import exactlyonearg
|
| 91 |
+
>>> from sympy.abc import x, y
|
| 92 |
+
>>> exactlyonearg(x, Q.positive(x), x*y)
|
| 93 |
+
(Q.positive(x) & ~Q.positive(y)) | (Q.positive(y) & ~Q.positive(x))
|
| 94 |
+
|
| 95 |
+
"""
|
| 96 |
+
pred_args = [fact.subs(symbol, arg) for arg in expr.args]
|
| 97 |
+
res = Or(*[And(pred_args[i], *[~lit for lit in pred_args[:i] +
|
| 98 |
+
pred_args[i+1:]]) for i in range(len(pred_args))])
|
| 99 |
+
return res
|
| 100 |
+
|
| 101 |
+
|
| 102 |
+
### Fact registry ###
|
| 103 |
+
|
| 104 |
+
class ClassFactRegistry:
|
| 105 |
+
"""
|
| 106 |
+
Register handlers against classes.
|
| 107 |
+
|
| 108 |
+
Explanation
|
| 109 |
+
===========
|
| 110 |
+
|
| 111 |
+
``register`` method registers the handler function for a class. Here,
|
| 112 |
+
handler function should return a single fact. ``multiregister`` method
|
| 113 |
+
registers the handler function for multiple classes. Here, handler function
|
| 114 |
+
should return a container of multiple facts.
|
| 115 |
+
|
| 116 |
+
``registry(expr)`` returns a set of facts for *expr*.
|
| 117 |
+
|
| 118 |
+
Examples
|
| 119 |
+
========
|
| 120 |
+
|
| 121 |
+
Here, we register the facts for ``Abs``.
|
| 122 |
+
|
| 123 |
+
>>> from sympy import Abs, Equivalent, Q
|
| 124 |
+
>>> from sympy.assumptions.sathandlers import ClassFactRegistry
|
| 125 |
+
>>> reg = ClassFactRegistry()
|
| 126 |
+
>>> @reg.register(Abs)
|
| 127 |
+
... def f1(expr):
|
| 128 |
+
... return Q.nonnegative(expr)
|
| 129 |
+
>>> @reg.register(Abs)
|
| 130 |
+
... def f2(expr):
|
| 131 |
+
... arg = expr.args[0]
|
| 132 |
+
... return Equivalent(~Q.zero(arg), ~Q.zero(expr))
|
| 133 |
+
|
| 134 |
+
Calling the registry with expression returns the defined facts for the
|
| 135 |
+
expression.
|
| 136 |
+
|
| 137 |
+
>>> from sympy.abc import x
|
| 138 |
+
>>> reg(Abs(x))
|
| 139 |
+
{Q.nonnegative(Abs(x)), Equivalent(~Q.zero(x), ~Q.zero(Abs(x)))}
|
| 140 |
+
|
| 141 |
+
Multiple facts can be registered at once by ``multiregister`` method.
|
| 142 |
+
|
| 143 |
+
>>> reg2 = ClassFactRegistry()
|
| 144 |
+
>>> @reg2.multiregister(Abs)
|
| 145 |
+
... def _(expr):
|
| 146 |
+
... arg = expr.args[0]
|
| 147 |
+
... return [Q.even(arg) >> Q.even(expr), Q.odd(arg) >> Q.odd(expr)]
|
| 148 |
+
>>> reg2(Abs(x))
|
| 149 |
+
{Implies(Q.even(x), Q.even(Abs(x))), Implies(Q.odd(x), Q.odd(Abs(x)))}
|
| 150 |
+
|
| 151 |
+
"""
|
| 152 |
+
def __init__(self):
|
| 153 |
+
self.singlefacts = defaultdict(frozenset)
|
| 154 |
+
self.multifacts = defaultdict(frozenset)
|
| 155 |
+
|
| 156 |
+
def register(self, cls):
|
| 157 |
+
def _(func):
|
| 158 |
+
self.singlefacts[cls] |= {func}
|
| 159 |
+
return func
|
| 160 |
+
return _
|
| 161 |
+
|
| 162 |
+
def multiregister(self, *classes):
|
| 163 |
+
def _(func):
|
| 164 |
+
for cls in classes:
|
| 165 |
+
self.multifacts[cls] |= {func}
|
| 166 |
+
return func
|
| 167 |
+
return _
|
| 168 |
+
|
| 169 |
+
def __getitem__(self, key):
|
| 170 |
+
ret1 = self.singlefacts[key]
|
| 171 |
+
for k in self.singlefacts:
|
| 172 |
+
if issubclass(key, k):
|
| 173 |
+
ret1 |= self.singlefacts[k]
|
| 174 |
+
|
| 175 |
+
ret2 = self.multifacts[key]
|
| 176 |
+
for k in self.multifacts:
|
| 177 |
+
if issubclass(key, k):
|
| 178 |
+
ret2 |= self.multifacts[k]
|
| 179 |
+
|
| 180 |
+
return ret1, ret2
|
| 181 |
+
|
| 182 |
+
def __call__(self, expr):
|
| 183 |
+
ret = set()
|
| 184 |
+
|
| 185 |
+
handlers1, handlers2 = self[type(expr)]
|
| 186 |
+
|
| 187 |
+
ret.update(h(expr) for h in handlers1)
|
| 188 |
+
for h in handlers2:
|
| 189 |
+
ret.update(h(expr))
|
| 190 |
+
return ret
|
| 191 |
+
|
| 192 |
+
class_fact_registry = ClassFactRegistry()
|
| 193 |
+
|
| 194 |
+
|
| 195 |
+
|
| 196 |
+
### Class fact registration ###
|
| 197 |
+
|
| 198 |
+
x = Symbol('x')
|
| 199 |
+
|
| 200 |
+
## Abs ##
|
| 201 |
+
|
| 202 |
+
@class_fact_registry.multiregister(Abs)
|
| 203 |
+
def _(expr):
|
| 204 |
+
arg = expr.args[0]
|
| 205 |
+
return [Q.nonnegative(expr),
|
| 206 |
+
Equivalent(~Q.zero(arg), ~Q.zero(expr)),
|
| 207 |
+
Q.even(arg) >> Q.even(expr),
|
| 208 |
+
Q.odd(arg) >> Q.odd(expr),
|
| 209 |
+
Q.integer(arg) >> Q.integer(expr),
|
| 210 |
+
]
|
| 211 |
+
|
| 212 |
+
|
| 213 |
+
### Add ##
|
| 214 |
+
|
| 215 |
+
@class_fact_registry.multiregister(Add)
|
| 216 |
+
def _(expr):
|
| 217 |
+
return [allargs(x, Q.positive(x), expr) >> Q.positive(expr),
|
| 218 |
+
allargs(x, Q.negative(x), expr) >> Q.negative(expr),
|
| 219 |
+
allargs(x, Q.real(x), expr) >> Q.real(expr),
|
| 220 |
+
allargs(x, Q.rational(x), expr) >> Q.rational(expr),
|
| 221 |
+
allargs(x, Q.integer(x), expr) >> Q.integer(expr),
|
| 222 |
+
exactlyonearg(x, ~Q.integer(x), expr) >> ~Q.integer(expr),
|
| 223 |
+
]
|
| 224 |
+
|
| 225 |
+
@class_fact_registry.register(Add)
|
| 226 |
+
def _(expr):
|
| 227 |
+
allargs_real = allargs(x, Q.real(x), expr)
|
| 228 |
+
onearg_irrational = exactlyonearg(x, Q.irrational(x), expr)
|
| 229 |
+
return Implies(allargs_real, Implies(onearg_irrational, Q.irrational(expr)))
|
| 230 |
+
|
| 231 |
+
|
| 232 |
+
### Mul ###
|
| 233 |
+
|
| 234 |
+
@class_fact_registry.multiregister(Mul)
|
| 235 |
+
def _(expr):
|
| 236 |
+
return [Equivalent(Q.zero(expr), anyarg(x, Q.zero(x), expr)),
|
| 237 |
+
allargs(x, Q.positive(x), expr) >> Q.positive(expr),
|
| 238 |
+
allargs(x, Q.real(x), expr) >> Q.real(expr),
|
| 239 |
+
allargs(x, Q.rational(x), expr) >> Q.rational(expr),
|
| 240 |
+
allargs(x, Q.integer(x), expr) >> Q.integer(expr),
|
| 241 |
+
exactlyonearg(x, ~Q.rational(x), expr) >> ~Q.integer(expr),
|
| 242 |
+
allargs(x, Q.commutative(x), expr) >> Q.commutative(expr),
|
| 243 |
+
]
|
| 244 |
+
|
| 245 |
+
@class_fact_registry.register(Mul)
|
| 246 |
+
def _(expr):
|
| 247 |
+
# Implicitly assumes Mul has more than one arg
|
| 248 |
+
# Would be allargs(x, Q.prime(x) | Q.composite(x)) except 1 is composite
|
| 249 |
+
# More advanced prime assumptions will require inequalities, as 1 provides
|
| 250 |
+
# a corner case.
|
| 251 |
+
allargs_prime = allargs(x, Q.prime(x), expr)
|
| 252 |
+
return Implies(allargs_prime, ~Q.prime(expr))
|
| 253 |
+
|
| 254 |
+
@class_fact_registry.register(Mul)
|
| 255 |
+
def _(expr):
|
| 256 |
+
# General Case: Odd number of imaginary args implies mul is imaginary(To be implemented)
|
| 257 |
+
allargs_imag_or_real = allargs(x, Q.imaginary(x) | Q.real(x), expr)
|
| 258 |
+
onearg_imaginary = exactlyonearg(x, Q.imaginary(x), expr)
|
| 259 |
+
return Implies(allargs_imag_or_real, Implies(onearg_imaginary, Q.imaginary(expr)))
|
| 260 |
+
|
| 261 |
+
@class_fact_registry.register(Mul)
|
| 262 |
+
def _(expr):
|
| 263 |
+
allargs_real = allargs(x, Q.real(x), expr)
|
| 264 |
+
onearg_irrational = exactlyonearg(x, Q.irrational(x), expr)
|
| 265 |
+
return Implies(allargs_real, Implies(onearg_irrational, Q.irrational(expr)))
|
| 266 |
+
|
| 267 |
+
@class_fact_registry.register(Mul)
|
| 268 |
+
def _(expr):
|
| 269 |
+
# Including the integer qualification means we don't need to add any facts
|
| 270 |
+
# for odd, since the assumptions already know that every integer is
|
| 271 |
+
# exactly one of even or odd.
|
| 272 |
+
allargs_integer = allargs(x, Q.integer(x), expr)
|
| 273 |
+
anyarg_even = anyarg(x, Q.even(x), expr)
|
| 274 |
+
return Implies(allargs_integer, Equivalent(anyarg_even, Q.even(expr)))
|
| 275 |
+
|
| 276 |
+
|
| 277 |
+
### MatMul ###
|
| 278 |
+
|
| 279 |
+
@class_fact_registry.register(MatMul)
|
| 280 |
+
def _(expr):
|
| 281 |
+
allargs_square = allargs(x, Q.square(x), expr)
|
| 282 |
+
allargs_invertible = allargs(x, Q.invertible(x), expr)
|
| 283 |
+
return Implies(allargs_square, Equivalent(Q.invertible(expr), allargs_invertible))
|
| 284 |
+
|
| 285 |
+
|
| 286 |
+
### Pow ###
|
| 287 |
+
|
| 288 |
+
@class_fact_registry.multiregister(Pow)
|
| 289 |
+
def _(expr):
|
| 290 |
+
base, exp = expr.base, expr.exp
|
| 291 |
+
return [
|
| 292 |
+
(Q.real(base) & Q.even(exp) & Q.nonnegative(exp)) >> Q.nonnegative(expr),
|
| 293 |
+
(Q.nonnegative(base) & Q.odd(exp) & Q.nonnegative(exp)) >> Q.nonnegative(expr),
|
| 294 |
+
(Q.nonpositive(base) & Q.odd(exp) & Q.nonnegative(exp)) >> Q.nonpositive(expr),
|
| 295 |
+
Equivalent(Q.zero(expr), Q.zero(base) & Q.positive(exp))
|
| 296 |
+
]
|
| 297 |
+
|
| 298 |
+
|
| 299 |
+
### Numbers ###
|
| 300 |
+
|
| 301 |
+
_old_assump_getters = {
|
| 302 |
+
Q.positive: lambda o: o.is_positive,
|
| 303 |
+
Q.zero: lambda o: o.is_zero,
|
| 304 |
+
Q.negative: lambda o: o.is_negative,
|
| 305 |
+
Q.rational: lambda o: o.is_rational,
|
| 306 |
+
Q.irrational: lambda o: o.is_irrational,
|
| 307 |
+
Q.even: lambda o: o.is_even,
|
| 308 |
+
Q.odd: lambda o: o.is_odd,
|
| 309 |
+
Q.imaginary: lambda o: o.is_imaginary,
|
| 310 |
+
Q.prime: lambda o: o.is_prime,
|
| 311 |
+
Q.composite: lambda o: o.is_composite,
|
| 312 |
+
}
|
| 313 |
+
|
| 314 |
+
@class_fact_registry.multiregister(Number, NumberSymbol, ImaginaryUnit)
|
| 315 |
+
def _(expr):
|
| 316 |
+
ret = []
|
| 317 |
+
for p, getter in _old_assump_getters.items():
|
| 318 |
+
pred = p(expr)
|
| 319 |
+
prop = getter(expr)
|
| 320 |
+
if prop is not None:
|
| 321 |
+
ret.append(Equivalent(pred, prop))
|
| 322 |
+
return ret
|
lib/python3.12/site-packages/sympy/assumptions/tests/__init__.py
ADDED
|
File without changes
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (202 Bytes). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_assumptions_2.cpython-312.pyc
ADDED
|
Binary file (2.27 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_context.cpython-312.pyc
ADDED
|
Binary file (3.6 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_matrices.cpython-312.pyc
ADDED
|
Binary file (32.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_refine.cpython-312.pyc
ADDED
|
Binary file (25.3 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_rel_queries.cpython-312.pyc
ADDED
|
Binary file (14.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_satask.cpython-312.pyc
ADDED
|
Binary file (46.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_sathandlers.cpython-312.pyc
ADDED
|
Binary file (5.64 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/__pycache__/test_wrapper.cpython-312.pyc
ADDED
|
Binary file (2.22 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_assumptions_2.py
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
rename this to test_assumptions.py when the old assumptions system is deleted
|
| 3 |
+
"""
|
| 4 |
+
from sympy.abc import x, y
|
| 5 |
+
from sympy.assumptions.assume import global_assumptions
|
| 6 |
+
from sympy.assumptions.ask import Q
|
| 7 |
+
from sympy.printing import pretty
|
| 8 |
+
|
| 9 |
+
|
| 10 |
+
def test_equal():
|
| 11 |
+
"""Test for equality"""
|
| 12 |
+
assert Q.positive(x) == Q.positive(x)
|
| 13 |
+
assert Q.positive(x) != ~Q.positive(x)
|
| 14 |
+
assert ~Q.positive(x) == ~Q.positive(x)
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def test_pretty():
|
| 18 |
+
assert pretty(Q.positive(x)) == "Q.positive(x)"
|
| 19 |
+
assert pretty(
|
| 20 |
+
{Q.positive, Q.integer}) == "{Q.integer, Q.positive}"
|
| 21 |
+
|
| 22 |
+
|
| 23 |
+
def test_global():
|
| 24 |
+
"""Test for global assumptions"""
|
| 25 |
+
global_assumptions.add(x > 0)
|
| 26 |
+
assert (x > 0) in global_assumptions
|
| 27 |
+
global_assumptions.remove(x > 0)
|
| 28 |
+
assert not (x > 0) in global_assumptions
|
| 29 |
+
# same with multiple of assumptions
|
| 30 |
+
global_assumptions.add(x > 0, y > 0)
|
| 31 |
+
assert (x > 0) in global_assumptions
|
| 32 |
+
assert (y > 0) in global_assumptions
|
| 33 |
+
global_assumptions.clear()
|
| 34 |
+
assert not (x > 0) in global_assumptions
|
| 35 |
+
assert not (y > 0) in global_assumptions
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_context.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions import ask, Q
|
| 2 |
+
from sympy.assumptions.assume import assuming, global_assumptions
|
| 3 |
+
from sympy.abc import x, y
|
| 4 |
+
|
| 5 |
+
def test_assuming():
|
| 6 |
+
with assuming(Q.integer(x)):
|
| 7 |
+
assert ask(Q.integer(x))
|
| 8 |
+
assert not ask(Q.integer(x))
|
| 9 |
+
|
| 10 |
+
def test_assuming_nested():
|
| 11 |
+
assert not ask(Q.integer(x))
|
| 12 |
+
assert not ask(Q.integer(y))
|
| 13 |
+
with assuming(Q.integer(x)):
|
| 14 |
+
assert ask(Q.integer(x))
|
| 15 |
+
assert not ask(Q.integer(y))
|
| 16 |
+
with assuming(Q.integer(y)):
|
| 17 |
+
assert ask(Q.integer(x))
|
| 18 |
+
assert ask(Q.integer(y))
|
| 19 |
+
assert ask(Q.integer(x))
|
| 20 |
+
assert not ask(Q.integer(y))
|
| 21 |
+
assert not ask(Q.integer(x))
|
| 22 |
+
assert not ask(Q.integer(y))
|
| 23 |
+
|
| 24 |
+
def test_finally():
|
| 25 |
+
try:
|
| 26 |
+
with assuming(Q.integer(x)):
|
| 27 |
+
1/0
|
| 28 |
+
except ZeroDivisionError:
|
| 29 |
+
pass
|
| 30 |
+
assert not ask(Q.integer(x))
|
| 31 |
+
|
| 32 |
+
def test_remove_safe():
|
| 33 |
+
global_assumptions.add(Q.integer(x))
|
| 34 |
+
with assuming():
|
| 35 |
+
assert ask(Q.integer(x))
|
| 36 |
+
global_assumptions.remove(Q.integer(x))
|
| 37 |
+
assert not ask(Q.integer(x))
|
| 38 |
+
assert ask(Q.integer(x))
|
| 39 |
+
global_assumptions.clear() # for the benefit of other tests
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_matrices.py
ADDED
|
@@ -0,0 +1,283 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.ask import (Q, ask)
|
| 2 |
+
from sympy.core.symbol import Symbol
|
| 3 |
+
from sympy.matrices.expressions.diagonal import (DiagMatrix, DiagonalMatrix)
|
| 4 |
+
from sympy.matrices.dense import Matrix
|
| 5 |
+
from sympy.matrices.expressions import (MatrixSymbol, Identity, ZeroMatrix,
|
| 6 |
+
OneMatrix, Trace, MatrixSlice, Determinant, BlockMatrix, BlockDiagMatrix)
|
| 7 |
+
from sympy.matrices.expressions.factorizations import LofLU
|
| 8 |
+
from sympy.testing.pytest import XFAIL
|
| 9 |
+
|
| 10 |
+
X = MatrixSymbol('X', 2, 2)
|
| 11 |
+
Y = MatrixSymbol('Y', 2, 3)
|
| 12 |
+
Z = MatrixSymbol('Z', 2, 2)
|
| 13 |
+
A1x1 = MatrixSymbol('A1x1', 1, 1)
|
| 14 |
+
B1x1 = MatrixSymbol('B1x1', 1, 1)
|
| 15 |
+
C0x0 = MatrixSymbol('C0x0', 0, 0)
|
| 16 |
+
V1 = MatrixSymbol('V1', 2, 1)
|
| 17 |
+
V2 = MatrixSymbol('V2', 2, 1)
|
| 18 |
+
|
| 19 |
+
def test_square():
|
| 20 |
+
assert ask(Q.square(X))
|
| 21 |
+
assert not ask(Q.square(Y))
|
| 22 |
+
assert ask(Q.square(Y*Y.T))
|
| 23 |
+
|
| 24 |
+
def test_invertible():
|
| 25 |
+
assert ask(Q.invertible(X), Q.invertible(X))
|
| 26 |
+
assert ask(Q.invertible(Y)) is False
|
| 27 |
+
assert ask(Q.invertible(X*Y), Q.invertible(X)) is False
|
| 28 |
+
assert ask(Q.invertible(X*Z), Q.invertible(X)) is None
|
| 29 |
+
assert ask(Q.invertible(X*Z), Q.invertible(X) & Q.invertible(Z)) is True
|
| 30 |
+
assert ask(Q.invertible(X.T)) is None
|
| 31 |
+
assert ask(Q.invertible(X.T), Q.invertible(X)) is True
|
| 32 |
+
assert ask(Q.invertible(X.I)) is True
|
| 33 |
+
assert ask(Q.invertible(Identity(3))) is True
|
| 34 |
+
assert ask(Q.invertible(ZeroMatrix(3, 3))) is False
|
| 35 |
+
assert ask(Q.invertible(OneMatrix(1, 1))) is True
|
| 36 |
+
assert ask(Q.invertible(OneMatrix(3, 3))) is False
|
| 37 |
+
assert ask(Q.invertible(X), Q.fullrank(X) & Q.square(X))
|
| 38 |
+
|
| 39 |
+
def test_singular():
|
| 40 |
+
assert ask(Q.singular(X)) is None
|
| 41 |
+
assert ask(Q.singular(X), Q.invertible(X)) is False
|
| 42 |
+
assert ask(Q.singular(X), ~Q.invertible(X)) is True
|
| 43 |
+
|
| 44 |
+
@XFAIL
|
| 45 |
+
def test_invertible_fullrank():
|
| 46 |
+
assert ask(Q.invertible(X), Q.fullrank(X)) is True
|
| 47 |
+
|
| 48 |
+
|
| 49 |
+
def test_invertible_BlockMatrix():
|
| 50 |
+
assert ask(Q.invertible(BlockMatrix([Identity(3)]))) == True
|
| 51 |
+
assert ask(Q.invertible(BlockMatrix([ZeroMatrix(3, 3)]))) == False
|
| 52 |
+
|
| 53 |
+
X = Matrix([[1, 2, 3], [3, 5, 4]])
|
| 54 |
+
Y = Matrix([[4, 2, 7], [2, 3, 5]])
|
| 55 |
+
# non-invertible A block
|
| 56 |
+
assert ask(Q.invertible(BlockMatrix([
|
| 57 |
+
[Matrix.ones(3, 3), Y.T],
|
| 58 |
+
[X, Matrix.eye(2)],
|
| 59 |
+
]))) == True
|
| 60 |
+
# non-invertible B block
|
| 61 |
+
assert ask(Q.invertible(BlockMatrix([
|
| 62 |
+
[Y.T, Matrix.ones(3, 3)],
|
| 63 |
+
[Matrix.eye(2), X],
|
| 64 |
+
]))) == True
|
| 65 |
+
# non-invertible C block
|
| 66 |
+
assert ask(Q.invertible(BlockMatrix([
|
| 67 |
+
[X, Matrix.eye(2)],
|
| 68 |
+
[Matrix.ones(3, 3), Y.T],
|
| 69 |
+
]))) == True
|
| 70 |
+
# non-invertible D block
|
| 71 |
+
assert ask(Q.invertible(BlockMatrix([
|
| 72 |
+
[Matrix.eye(2), X],
|
| 73 |
+
[Y.T, Matrix.ones(3, 3)],
|
| 74 |
+
]))) == True
|
| 75 |
+
|
| 76 |
+
|
| 77 |
+
def test_invertible_BlockDiagMatrix():
|
| 78 |
+
assert ask(Q.invertible(BlockDiagMatrix(Identity(3), Identity(5)))) == True
|
| 79 |
+
assert ask(Q.invertible(BlockDiagMatrix(ZeroMatrix(3, 3), Identity(5)))) == False
|
| 80 |
+
assert ask(Q.invertible(BlockDiagMatrix(Identity(3), OneMatrix(5, 5)))) == False
|
| 81 |
+
|
| 82 |
+
|
| 83 |
+
def test_symmetric():
|
| 84 |
+
assert ask(Q.symmetric(X), Q.symmetric(X))
|
| 85 |
+
assert ask(Q.symmetric(X*Z), Q.symmetric(X)) is None
|
| 86 |
+
assert ask(Q.symmetric(X*Z), Q.symmetric(X) & Q.symmetric(Z)) is True
|
| 87 |
+
assert ask(Q.symmetric(X + Z), Q.symmetric(X) & Q.symmetric(Z)) is True
|
| 88 |
+
assert ask(Q.symmetric(Y)) is False
|
| 89 |
+
assert ask(Q.symmetric(Y*Y.T)) is True
|
| 90 |
+
assert ask(Q.symmetric(Y.T*X*Y)) is None
|
| 91 |
+
assert ask(Q.symmetric(Y.T*X*Y), Q.symmetric(X)) is True
|
| 92 |
+
assert ask(Q.symmetric(X**10), Q.symmetric(X)) is True
|
| 93 |
+
assert ask(Q.symmetric(A1x1)) is True
|
| 94 |
+
assert ask(Q.symmetric(A1x1 + B1x1)) is True
|
| 95 |
+
assert ask(Q.symmetric(A1x1 * B1x1)) is True
|
| 96 |
+
assert ask(Q.symmetric(V1.T*V1)) is True
|
| 97 |
+
assert ask(Q.symmetric(V1.T*(V1 + V2))) is True
|
| 98 |
+
assert ask(Q.symmetric(V1.T*(V1 + V2) + A1x1)) is True
|
| 99 |
+
assert ask(Q.symmetric(MatrixSlice(Y, (0, 1), (1, 2)))) is True
|
| 100 |
+
assert ask(Q.symmetric(Identity(3))) is True
|
| 101 |
+
assert ask(Q.symmetric(ZeroMatrix(3, 3))) is True
|
| 102 |
+
assert ask(Q.symmetric(OneMatrix(3, 3))) is True
|
| 103 |
+
|
| 104 |
+
def _test_orthogonal_unitary(predicate):
|
| 105 |
+
assert ask(predicate(X), predicate(X))
|
| 106 |
+
assert ask(predicate(X.T), predicate(X)) is True
|
| 107 |
+
assert ask(predicate(X.I), predicate(X)) is True
|
| 108 |
+
assert ask(predicate(X**2), predicate(X))
|
| 109 |
+
assert ask(predicate(Y)) is False
|
| 110 |
+
assert ask(predicate(X)) is None
|
| 111 |
+
assert ask(predicate(X), ~Q.invertible(X)) is False
|
| 112 |
+
assert ask(predicate(X*Z*X), predicate(X) & predicate(Z)) is True
|
| 113 |
+
assert ask(predicate(Identity(3))) is True
|
| 114 |
+
assert ask(predicate(ZeroMatrix(3, 3))) is False
|
| 115 |
+
assert ask(Q.invertible(X), predicate(X))
|
| 116 |
+
assert not ask(predicate(X + Z), predicate(X) & predicate(Z))
|
| 117 |
+
|
| 118 |
+
def test_orthogonal():
|
| 119 |
+
_test_orthogonal_unitary(Q.orthogonal)
|
| 120 |
+
|
| 121 |
+
def test_unitary():
|
| 122 |
+
_test_orthogonal_unitary(Q.unitary)
|
| 123 |
+
assert ask(Q.unitary(X), Q.orthogonal(X))
|
| 124 |
+
|
| 125 |
+
def test_fullrank():
|
| 126 |
+
assert ask(Q.fullrank(X), Q.fullrank(X))
|
| 127 |
+
assert ask(Q.fullrank(X**2), Q.fullrank(X))
|
| 128 |
+
assert ask(Q.fullrank(X.T), Q.fullrank(X)) is True
|
| 129 |
+
assert ask(Q.fullrank(X)) is None
|
| 130 |
+
assert ask(Q.fullrank(Y)) is None
|
| 131 |
+
assert ask(Q.fullrank(X*Z), Q.fullrank(X) & Q.fullrank(Z)) is True
|
| 132 |
+
assert ask(Q.fullrank(Identity(3))) is True
|
| 133 |
+
assert ask(Q.fullrank(ZeroMatrix(3, 3))) is False
|
| 134 |
+
assert ask(Q.fullrank(OneMatrix(1, 1))) is True
|
| 135 |
+
assert ask(Q.fullrank(OneMatrix(3, 3))) is False
|
| 136 |
+
assert ask(Q.invertible(X), ~Q.fullrank(X)) == False
|
| 137 |
+
|
| 138 |
+
|
| 139 |
+
def test_positive_definite():
|
| 140 |
+
assert ask(Q.positive_definite(X), Q.positive_definite(X))
|
| 141 |
+
assert ask(Q.positive_definite(X.T), Q.positive_definite(X)) is True
|
| 142 |
+
assert ask(Q.positive_definite(X.I), Q.positive_definite(X)) is True
|
| 143 |
+
assert ask(Q.positive_definite(Y)) is False
|
| 144 |
+
assert ask(Q.positive_definite(X)) is None
|
| 145 |
+
assert ask(Q.positive_definite(X**3), Q.positive_definite(X))
|
| 146 |
+
assert ask(Q.positive_definite(X*Z*X),
|
| 147 |
+
Q.positive_definite(X) & Q.positive_definite(Z)) is True
|
| 148 |
+
assert ask(Q.positive_definite(X), Q.orthogonal(X))
|
| 149 |
+
assert ask(Q.positive_definite(Y.T*X*Y),
|
| 150 |
+
Q.positive_definite(X) & Q.fullrank(Y)) is True
|
| 151 |
+
assert not ask(Q.positive_definite(Y.T*X*Y), Q.positive_definite(X))
|
| 152 |
+
assert ask(Q.positive_definite(Identity(3))) is True
|
| 153 |
+
assert ask(Q.positive_definite(ZeroMatrix(3, 3))) is False
|
| 154 |
+
assert ask(Q.positive_definite(OneMatrix(1, 1))) is True
|
| 155 |
+
assert ask(Q.positive_definite(OneMatrix(3, 3))) is False
|
| 156 |
+
assert ask(Q.positive_definite(X + Z), Q.positive_definite(X) &
|
| 157 |
+
Q.positive_definite(Z)) is True
|
| 158 |
+
assert not ask(Q.positive_definite(-X), Q.positive_definite(X))
|
| 159 |
+
assert ask(Q.positive(X[1, 1]), Q.positive_definite(X))
|
| 160 |
+
|
| 161 |
+
def test_triangular():
|
| 162 |
+
assert ask(Q.upper_triangular(X + Z.T + Identity(2)), Q.upper_triangular(X) &
|
| 163 |
+
Q.lower_triangular(Z)) is True
|
| 164 |
+
assert ask(Q.upper_triangular(X*Z.T), Q.upper_triangular(X) &
|
| 165 |
+
Q.lower_triangular(Z)) is True
|
| 166 |
+
assert ask(Q.lower_triangular(Identity(3))) is True
|
| 167 |
+
assert ask(Q.lower_triangular(ZeroMatrix(3, 3))) is True
|
| 168 |
+
assert ask(Q.upper_triangular(ZeroMatrix(3, 3))) is True
|
| 169 |
+
assert ask(Q.lower_triangular(OneMatrix(1, 1))) is True
|
| 170 |
+
assert ask(Q.upper_triangular(OneMatrix(1, 1))) is True
|
| 171 |
+
assert ask(Q.lower_triangular(OneMatrix(3, 3))) is False
|
| 172 |
+
assert ask(Q.upper_triangular(OneMatrix(3, 3))) is False
|
| 173 |
+
assert ask(Q.triangular(X), Q.unit_triangular(X))
|
| 174 |
+
assert ask(Q.upper_triangular(X**3), Q.upper_triangular(X))
|
| 175 |
+
assert ask(Q.lower_triangular(X**3), Q.lower_triangular(X))
|
| 176 |
+
|
| 177 |
+
|
| 178 |
+
def test_diagonal():
|
| 179 |
+
assert ask(Q.diagonal(X + Z.T + Identity(2)), Q.diagonal(X) &
|
| 180 |
+
Q.diagonal(Z)) is True
|
| 181 |
+
assert ask(Q.diagonal(ZeroMatrix(3, 3)))
|
| 182 |
+
assert ask(Q.diagonal(OneMatrix(1, 1))) is True
|
| 183 |
+
assert ask(Q.diagonal(OneMatrix(3, 3))) is False
|
| 184 |
+
assert ask(Q.lower_triangular(X) & Q.upper_triangular(X), Q.diagonal(X))
|
| 185 |
+
assert ask(Q.diagonal(X), Q.lower_triangular(X) & Q.upper_triangular(X))
|
| 186 |
+
assert ask(Q.symmetric(X), Q.diagonal(X))
|
| 187 |
+
assert ask(Q.triangular(X), Q.diagonal(X))
|
| 188 |
+
assert ask(Q.diagonal(C0x0))
|
| 189 |
+
assert ask(Q.diagonal(A1x1))
|
| 190 |
+
assert ask(Q.diagonal(A1x1 + B1x1))
|
| 191 |
+
assert ask(Q.diagonal(A1x1*B1x1))
|
| 192 |
+
assert ask(Q.diagonal(V1.T*V2))
|
| 193 |
+
assert ask(Q.diagonal(V1.T*(X + Z)*V1))
|
| 194 |
+
assert ask(Q.diagonal(MatrixSlice(Y, (0, 1), (1, 2)))) is True
|
| 195 |
+
assert ask(Q.diagonal(V1.T*(V1 + V2))) is True
|
| 196 |
+
assert ask(Q.diagonal(X**3), Q.diagonal(X))
|
| 197 |
+
assert ask(Q.diagonal(Identity(3)))
|
| 198 |
+
assert ask(Q.diagonal(DiagMatrix(V1)))
|
| 199 |
+
assert ask(Q.diagonal(DiagonalMatrix(X)))
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def test_non_atoms():
|
| 203 |
+
assert ask(Q.real(Trace(X)), Q.positive(Trace(X)))
|
| 204 |
+
|
| 205 |
+
@XFAIL
|
| 206 |
+
def test_non_trivial_implies():
|
| 207 |
+
X = MatrixSymbol('X', 3, 3)
|
| 208 |
+
Y = MatrixSymbol('Y', 3, 3)
|
| 209 |
+
assert ask(Q.lower_triangular(X+Y), Q.lower_triangular(X) &
|
| 210 |
+
Q.lower_triangular(Y)) is True
|
| 211 |
+
assert ask(Q.triangular(X), Q.lower_triangular(X)) is True
|
| 212 |
+
assert ask(Q.triangular(X+Y), Q.lower_triangular(X) &
|
| 213 |
+
Q.lower_triangular(Y)) is True
|
| 214 |
+
|
| 215 |
+
def test_MatrixSlice():
|
| 216 |
+
X = MatrixSymbol('X', 4, 4)
|
| 217 |
+
B = MatrixSlice(X, (1, 3), (1, 3))
|
| 218 |
+
C = MatrixSlice(X, (0, 3), (1, 3))
|
| 219 |
+
assert ask(Q.symmetric(B), Q.symmetric(X))
|
| 220 |
+
assert ask(Q.invertible(B), Q.invertible(X))
|
| 221 |
+
assert ask(Q.diagonal(B), Q.diagonal(X))
|
| 222 |
+
assert ask(Q.orthogonal(B), Q.orthogonal(X))
|
| 223 |
+
assert ask(Q.upper_triangular(B), Q.upper_triangular(X))
|
| 224 |
+
|
| 225 |
+
assert not ask(Q.symmetric(C), Q.symmetric(X))
|
| 226 |
+
assert not ask(Q.invertible(C), Q.invertible(X))
|
| 227 |
+
assert not ask(Q.diagonal(C), Q.diagonal(X))
|
| 228 |
+
assert not ask(Q.orthogonal(C), Q.orthogonal(X))
|
| 229 |
+
assert not ask(Q.upper_triangular(C), Q.upper_triangular(X))
|
| 230 |
+
|
| 231 |
+
def test_det_trace_positive():
|
| 232 |
+
X = MatrixSymbol('X', 4, 4)
|
| 233 |
+
assert ask(Q.positive(Trace(X)), Q.positive_definite(X))
|
| 234 |
+
assert ask(Q.positive(Determinant(X)), Q.positive_definite(X))
|
| 235 |
+
|
| 236 |
+
def test_field_assumptions():
|
| 237 |
+
X = MatrixSymbol('X', 4, 4)
|
| 238 |
+
Y = MatrixSymbol('Y', 4, 4)
|
| 239 |
+
assert ask(Q.real_elements(X), Q.real_elements(X))
|
| 240 |
+
assert not ask(Q.integer_elements(X), Q.real_elements(X))
|
| 241 |
+
assert ask(Q.complex_elements(X), Q.real_elements(X))
|
| 242 |
+
assert ask(Q.complex_elements(X**2), Q.real_elements(X))
|
| 243 |
+
assert ask(Q.real_elements(X**2), Q.integer_elements(X))
|
| 244 |
+
assert ask(Q.real_elements(X+Y), Q.real_elements(X)) is None
|
| 245 |
+
assert ask(Q.real_elements(X+Y), Q.real_elements(X) & Q.real_elements(Y))
|
| 246 |
+
from sympy.matrices.expressions.hadamard import HadamardProduct
|
| 247 |
+
assert ask(Q.real_elements(HadamardProduct(X, Y)),
|
| 248 |
+
Q.real_elements(X) & Q.real_elements(Y))
|
| 249 |
+
assert ask(Q.complex_elements(X+Y), Q.real_elements(X) & Q.complex_elements(Y))
|
| 250 |
+
|
| 251 |
+
assert ask(Q.real_elements(X.T), Q.real_elements(X))
|
| 252 |
+
assert ask(Q.real_elements(X.I), Q.real_elements(X) & Q.invertible(X))
|
| 253 |
+
assert ask(Q.real_elements(Trace(X)), Q.real_elements(X))
|
| 254 |
+
assert ask(Q.integer_elements(Determinant(X)), Q.integer_elements(X))
|
| 255 |
+
assert not ask(Q.integer_elements(X.I), Q.integer_elements(X))
|
| 256 |
+
alpha = Symbol('alpha')
|
| 257 |
+
assert ask(Q.real_elements(alpha*X), Q.real_elements(X) & Q.real(alpha))
|
| 258 |
+
assert ask(Q.real_elements(LofLU(X)), Q.real_elements(X))
|
| 259 |
+
e = Symbol('e', integer=True, negative=True)
|
| 260 |
+
assert ask(Q.real_elements(X**e), Q.real_elements(X) & Q.invertible(X))
|
| 261 |
+
assert ask(Q.real_elements(X**e), Q.real_elements(X)) is None
|
| 262 |
+
|
| 263 |
+
def test_matrix_element_sets():
|
| 264 |
+
X = MatrixSymbol('X', 4, 4)
|
| 265 |
+
assert ask(Q.real(X[1, 2]), Q.real_elements(X))
|
| 266 |
+
assert ask(Q.integer(X[1, 2]), Q.integer_elements(X))
|
| 267 |
+
assert ask(Q.complex(X[1, 2]), Q.complex_elements(X))
|
| 268 |
+
assert ask(Q.integer_elements(Identity(3)))
|
| 269 |
+
assert ask(Q.integer_elements(ZeroMatrix(3, 3)))
|
| 270 |
+
assert ask(Q.integer_elements(OneMatrix(3, 3)))
|
| 271 |
+
from sympy.matrices.expressions.fourier import DFT
|
| 272 |
+
assert ask(Q.complex_elements(DFT(3)))
|
| 273 |
+
|
| 274 |
+
|
| 275 |
+
def test_matrix_element_sets_slices_blocks():
|
| 276 |
+
X = MatrixSymbol('X', 4, 4)
|
| 277 |
+
assert ask(Q.integer_elements(X[:, 3]), Q.integer_elements(X))
|
| 278 |
+
assert ask(Q.integer_elements(BlockMatrix([[X], [X]])),
|
| 279 |
+
Q.integer_elements(X))
|
| 280 |
+
|
| 281 |
+
def test_matrix_element_sets_determinant_trace():
|
| 282 |
+
assert ask(Q.integer(Determinant(X)), Q.integer_elements(X))
|
| 283 |
+
assert ask(Q.integer(Trace(X)), Q.integer_elements(X))
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_query.py
ADDED
|
The diff for this file is too large to render.
See raw diff
|
|
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_refine.py
ADDED
|
@@ -0,0 +1,227 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.ask import Q
|
| 2 |
+
from sympy.assumptions.refine import refine
|
| 3 |
+
from sympy.core.expr import Expr
|
| 4 |
+
from sympy.core.numbers import (I, Rational, nan, pi)
|
| 5 |
+
from sympy.core.singleton import S
|
| 6 |
+
from sympy.core.symbol import Symbol
|
| 7 |
+
from sympy.functions.elementary.complexes import (Abs, arg, im, re, sign)
|
| 8 |
+
from sympy.functions.elementary.exponential import exp
|
| 9 |
+
from sympy.functions.elementary.miscellaneous import sqrt
|
| 10 |
+
from sympy.functions.elementary.trigonometric import (atan, atan2)
|
| 11 |
+
from sympy.abc import w, x, y, z
|
| 12 |
+
from sympy.core.relational import Eq, Ne
|
| 13 |
+
from sympy.functions.elementary.piecewise import Piecewise
|
| 14 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
def test_Abs():
|
| 18 |
+
assert refine(Abs(x), Q.positive(x)) == x
|
| 19 |
+
assert refine(1 + Abs(x), Q.positive(x)) == 1 + x
|
| 20 |
+
assert refine(Abs(x), Q.negative(x)) == -x
|
| 21 |
+
assert refine(1 + Abs(x), Q.negative(x)) == 1 - x
|
| 22 |
+
|
| 23 |
+
assert refine(Abs(x**2)) != x**2
|
| 24 |
+
assert refine(Abs(x**2), Q.real(x)) == x**2
|
| 25 |
+
|
| 26 |
+
|
| 27 |
+
def test_pow1():
|
| 28 |
+
assert refine((-1)**x, Q.even(x)) == 1
|
| 29 |
+
assert refine((-1)**x, Q.odd(x)) == -1
|
| 30 |
+
assert refine((-2)**x, Q.even(x)) == 2**x
|
| 31 |
+
|
| 32 |
+
# nested powers
|
| 33 |
+
assert refine(sqrt(x**2)) != Abs(x)
|
| 34 |
+
assert refine(sqrt(x**2), Q.complex(x)) != Abs(x)
|
| 35 |
+
assert refine(sqrt(x**2), Q.real(x)) == Abs(x)
|
| 36 |
+
assert refine(sqrt(x**2), Q.positive(x)) == x
|
| 37 |
+
assert refine((x**3)**Rational(1, 3)) != x
|
| 38 |
+
|
| 39 |
+
assert refine((x**3)**Rational(1, 3), Q.real(x)) != x
|
| 40 |
+
assert refine((x**3)**Rational(1, 3), Q.positive(x)) == x
|
| 41 |
+
|
| 42 |
+
assert refine(sqrt(1/x), Q.real(x)) != 1/sqrt(x)
|
| 43 |
+
assert refine(sqrt(1/x), Q.positive(x)) == 1/sqrt(x)
|
| 44 |
+
|
| 45 |
+
# powers of (-1)
|
| 46 |
+
assert refine((-1)**(x + y), Q.even(x)) == (-1)**y
|
| 47 |
+
assert refine((-1)**(x + y + z), Q.odd(x) & Q.odd(z)) == (-1)**y
|
| 48 |
+
assert refine((-1)**(x + y + 1), Q.odd(x)) == (-1)**y
|
| 49 |
+
assert refine((-1)**(x + y + 2), Q.odd(x)) == (-1)**(y + 1)
|
| 50 |
+
assert refine((-1)**(x + 3)) == (-1)**(x + 1)
|
| 51 |
+
|
| 52 |
+
# continuation
|
| 53 |
+
assert refine((-1)**((-1)**x/2 - S.Half), Q.integer(x)) == (-1)**x
|
| 54 |
+
assert refine((-1)**((-1)**x/2 + S.Half), Q.integer(x)) == (-1)**(x + 1)
|
| 55 |
+
assert refine((-1)**((-1)**x/2 + 5*S.Half), Q.integer(x)) == (-1)**(x + 1)
|
| 56 |
+
|
| 57 |
+
|
| 58 |
+
def test_pow2():
|
| 59 |
+
assert refine((-1)**((-1)**x/2 - 7*S.Half), Q.integer(x)) == (-1)**(x + 1)
|
| 60 |
+
assert refine((-1)**((-1)**x/2 - 9*S.Half), Q.integer(x)) == (-1)**x
|
| 61 |
+
|
| 62 |
+
# powers of Abs
|
| 63 |
+
assert refine(Abs(x)**2, Q.real(x)) == x**2
|
| 64 |
+
assert refine(Abs(x)**3, Q.real(x)) == Abs(x)**3
|
| 65 |
+
assert refine(Abs(x)**2) == Abs(x)**2
|
| 66 |
+
|
| 67 |
+
|
| 68 |
+
def test_exp():
|
| 69 |
+
x = Symbol('x', integer=True)
|
| 70 |
+
assert refine(exp(pi*I*2*x)) == 1
|
| 71 |
+
assert refine(exp(pi*I*2*(x + S.Half))) == -1
|
| 72 |
+
assert refine(exp(pi*I*2*(x + Rational(1, 4)))) == I
|
| 73 |
+
assert refine(exp(pi*I*2*(x + Rational(3, 4)))) == -I
|
| 74 |
+
|
| 75 |
+
|
| 76 |
+
def test_Piecewise():
|
| 77 |
+
assert refine(Piecewise((1, x < 0), (3, True)), (x < 0)) == 1
|
| 78 |
+
assert refine(Piecewise((1, x < 0), (3, True)), ~(x < 0)) == 3
|
| 79 |
+
assert refine(Piecewise((1, x < 0), (3, True)), (y < 0)) == \
|
| 80 |
+
Piecewise((1, x < 0), (3, True))
|
| 81 |
+
assert refine(Piecewise((1, x > 0), (3, True)), (x > 0)) == 1
|
| 82 |
+
assert refine(Piecewise((1, x > 0), (3, True)), ~(x > 0)) == 3
|
| 83 |
+
assert refine(Piecewise((1, x > 0), (3, True)), (y > 0)) == \
|
| 84 |
+
Piecewise((1, x > 0), (3, True))
|
| 85 |
+
assert refine(Piecewise((1, x <= 0), (3, True)), (x <= 0)) == 1
|
| 86 |
+
assert refine(Piecewise((1, x <= 0), (3, True)), ~(x <= 0)) == 3
|
| 87 |
+
assert refine(Piecewise((1, x <= 0), (3, True)), (y <= 0)) == \
|
| 88 |
+
Piecewise((1, x <= 0), (3, True))
|
| 89 |
+
assert refine(Piecewise((1, x >= 0), (3, True)), (x >= 0)) == 1
|
| 90 |
+
assert refine(Piecewise((1, x >= 0), (3, True)), ~(x >= 0)) == 3
|
| 91 |
+
assert refine(Piecewise((1, x >= 0), (3, True)), (y >= 0)) == \
|
| 92 |
+
Piecewise((1, x >= 0), (3, True))
|
| 93 |
+
assert refine(Piecewise((1, Eq(x, 0)), (3, True)), (Eq(x, 0)))\
|
| 94 |
+
== 1
|
| 95 |
+
assert refine(Piecewise((1, Eq(x, 0)), (3, True)), (Eq(0, x)))\
|
| 96 |
+
== 1
|
| 97 |
+
assert refine(Piecewise((1, Eq(x, 0)), (3, True)), ~(Eq(x, 0)))\
|
| 98 |
+
== 3
|
| 99 |
+
assert refine(Piecewise((1, Eq(x, 0)), (3, True)), ~(Eq(0, x)))\
|
| 100 |
+
== 3
|
| 101 |
+
assert refine(Piecewise((1, Eq(x, 0)), (3, True)), (Eq(y, 0)))\
|
| 102 |
+
== Piecewise((1, Eq(x, 0)), (3, True))
|
| 103 |
+
assert refine(Piecewise((1, Ne(x, 0)), (3, True)), (Ne(x, 0)))\
|
| 104 |
+
== 1
|
| 105 |
+
assert refine(Piecewise((1, Ne(x, 0)), (3, True)), ~(Ne(x, 0)))\
|
| 106 |
+
== 3
|
| 107 |
+
assert refine(Piecewise((1, Ne(x, 0)), (3, True)), (Ne(y, 0)))\
|
| 108 |
+
== Piecewise((1, Ne(x, 0)), (3, True))
|
| 109 |
+
|
| 110 |
+
|
| 111 |
+
def test_atan2():
|
| 112 |
+
assert refine(atan2(y, x), Q.real(y) & Q.positive(x)) == atan(y/x)
|
| 113 |
+
assert refine(atan2(y, x), Q.negative(y) & Q.positive(x)) == atan(y/x)
|
| 114 |
+
assert refine(atan2(y, x), Q.negative(y) & Q.negative(x)) == atan(y/x) - pi
|
| 115 |
+
assert refine(atan2(y, x), Q.positive(y) & Q.negative(x)) == atan(y/x) + pi
|
| 116 |
+
assert refine(atan2(y, x), Q.zero(y) & Q.negative(x)) == pi
|
| 117 |
+
assert refine(atan2(y, x), Q.positive(y) & Q.zero(x)) == pi/2
|
| 118 |
+
assert refine(atan2(y, x), Q.negative(y) & Q.zero(x)) == -pi/2
|
| 119 |
+
assert refine(atan2(y, x), Q.zero(y) & Q.zero(x)) is nan
|
| 120 |
+
|
| 121 |
+
|
| 122 |
+
def test_re():
|
| 123 |
+
assert refine(re(x), Q.real(x)) == x
|
| 124 |
+
assert refine(re(x), Q.imaginary(x)) is S.Zero
|
| 125 |
+
assert refine(re(x+y), Q.real(x) & Q.real(y)) == x + y
|
| 126 |
+
assert refine(re(x+y), Q.real(x) & Q.imaginary(y)) == x
|
| 127 |
+
assert refine(re(x*y), Q.real(x) & Q.real(y)) == x * y
|
| 128 |
+
assert refine(re(x*y), Q.real(x) & Q.imaginary(y)) == 0
|
| 129 |
+
assert refine(re(x*y*z), Q.real(x) & Q.real(y) & Q.real(z)) == x * y * z
|
| 130 |
+
|
| 131 |
+
|
| 132 |
+
def test_im():
|
| 133 |
+
assert refine(im(x), Q.imaginary(x)) == -I*x
|
| 134 |
+
assert refine(im(x), Q.real(x)) is S.Zero
|
| 135 |
+
assert refine(im(x+y), Q.imaginary(x) & Q.imaginary(y)) == -I*x - I*y
|
| 136 |
+
assert refine(im(x+y), Q.real(x) & Q.imaginary(y)) == -I*y
|
| 137 |
+
assert refine(im(x*y), Q.imaginary(x) & Q.real(y)) == -I*x*y
|
| 138 |
+
assert refine(im(x*y), Q.imaginary(x) & Q.imaginary(y)) == 0
|
| 139 |
+
assert refine(im(1/x), Q.imaginary(x)) == -I/x
|
| 140 |
+
assert refine(im(x*y*z), Q.imaginary(x) & Q.imaginary(y)
|
| 141 |
+
& Q.imaginary(z)) == -I*x*y*z
|
| 142 |
+
|
| 143 |
+
|
| 144 |
+
def test_complex():
|
| 145 |
+
assert refine(re(1/(x + I*y)), Q.real(x) & Q.real(y)) == \
|
| 146 |
+
x/(x**2 + y**2)
|
| 147 |
+
assert refine(im(1/(x + I*y)), Q.real(x) & Q.real(y)) == \
|
| 148 |
+
-y/(x**2 + y**2)
|
| 149 |
+
assert refine(re((w + I*x) * (y + I*z)), Q.real(w) & Q.real(x) & Q.real(y)
|
| 150 |
+
& Q.real(z)) == w*y - x*z
|
| 151 |
+
assert refine(im((w + I*x) * (y + I*z)), Q.real(w) & Q.real(x) & Q.real(y)
|
| 152 |
+
& Q.real(z)) == w*z + x*y
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def test_sign():
|
| 156 |
+
x = Symbol('x', real = True)
|
| 157 |
+
assert refine(sign(x), Q.positive(x)) == 1
|
| 158 |
+
assert refine(sign(x), Q.negative(x)) == -1
|
| 159 |
+
assert refine(sign(x), Q.zero(x)) == 0
|
| 160 |
+
assert refine(sign(x), True) == sign(x)
|
| 161 |
+
assert refine(sign(Abs(x)), Q.nonzero(x)) == 1
|
| 162 |
+
|
| 163 |
+
x = Symbol('x', imaginary=True)
|
| 164 |
+
assert refine(sign(x), Q.positive(im(x))) == S.ImaginaryUnit
|
| 165 |
+
assert refine(sign(x), Q.negative(im(x))) == -S.ImaginaryUnit
|
| 166 |
+
assert refine(sign(x), True) == sign(x)
|
| 167 |
+
|
| 168 |
+
x = Symbol('x', complex=True)
|
| 169 |
+
assert refine(sign(x), Q.zero(x)) == 0
|
| 170 |
+
|
| 171 |
+
def test_arg():
|
| 172 |
+
x = Symbol('x', complex = True)
|
| 173 |
+
assert refine(arg(x), Q.positive(x)) == 0
|
| 174 |
+
assert refine(arg(x), Q.negative(x)) == pi
|
| 175 |
+
|
| 176 |
+
def test_func_args():
|
| 177 |
+
class MyClass(Expr):
|
| 178 |
+
# A class with nontrivial .func
|
| 179 |
+
|
| 180 |
+
def __init__(self, *args):
|
| 181 |
+
self.my_member = ""
|
| 182 |
+
|
| 183 |
+
@property
|
| 184 |
+
def func(self):
|
| 185 |
+
def my_func(*args):
|
| 186 |
+
obj = MyClass(*args)
|
| 187 |
+
obj.my_member = self.my_member
|
| 188 |
+
return obj
|
| 189 |
+
return my_func
|
| 190 |
+
|
| 191 |
+
x = MyClass()
|
| 192 |
+
x.my_member = "A very important value"
|
| 193 |
+
assert x.my_member == refine(x).my_member
|
| 194 |
+
|
| 195 |
+
def test_issue_refine_9384():
|
| 196 |
+
assert refine(Piecewise((1, x < 0), (0, True)), Q.positive(x)) == 0
|
| 197 |
+
assert refine(Piecewise((1, x < 0), (0, True)), Q.negative(x)) == 1
|
| 198 |
+
assert refine(Piecewise((1, x > 0), (0, True)), Q.positive(x)) == 1
|
| 199 |
+
assert refine(Piecewise((1, x > 0), (0, True)), Q.negative(x)) == 0
|
| 200 |
+
|
| 201 |
+
|
| 202 |
+
def test_eval_refine():
|
| 203 |
+
class MockExpr(Expr):
|
| 204 |
+
def _eval_refine(self, assumptions):
|
| 205 |
+
return True
|
| 206 |
+
|
| 207 |
+
mock_obj = MockExpr()
|
| 208 |
+
assert refine(mock_obj)
|
| 209 |
+
|
| 210 |
+
def test_refine_issue_12724():
|
| 211 |
+
expr1 = refine(Abs(x * y), Q.positive(x))
|
| 212 |
+
expr2 = refine(Abs(x * y * z), Q.positive(x))
|
| 213 |
+
assert expr1 == x * Abs(y)
|
| 214 |
+
assert expr2 == x * Abs(y * z)
|
| 215 |
+
y1 = Symbol('y1', real = True)
|
| 216 |
+
expr3 = refine(Abs(x * y1**2 * z), Q.positive(x))
|
| 217 |
+
assert expr3 == x * y1**2 * Abs(z)
|
| 218 |
+
|
| 219 |
+
|
| 220 |
+
def test_matrixelement():
|
| 221 |
+
x = MatrixSymbol('x', 3, 3)
|
| 222 |
+
i = Symbol('i', positive = True)
|
| 223 |
+
j = Symbol('j', positive = True)
|
| 224 |
+
assert refine(x[0, 1], Q.symmetric(x)) == x[0, 1]
|
| 225 |
+
assert refine(x[1, 0], Q.symmetric(x)) == x[0, 1]
|
| 226 |
+
assert refine(x[i, j], Q.symmetric(x)) == x[j, i]
|
| 227 |
+
assert refine(x[j, i], Q.symmetric(x)) == x[j, i]
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_rel_queries.py
ADDED
|
@@ -0,0 +1,172 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.lra_satask import lra_satask
|
| 2 |
+
from sympy.logic.algorithms.lra_theory import UnhandledInput
|
| 3 |
+
from sympy.assumptions.ask import Q, ask
|
| 4 |
+
|
| 5 |
+
from sympy.core import symbols, Symbol
|
| 6 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 7 |
+
from sympy.core.numbers import I
|
| 8 |
+
|
| 9 |
+
from sympy.testing.pytest import raises, XFAIL
|
| 10 |
+
x, y, z = symbols("x y z", real=True)
|
| 11 |
+
|
| 12 |
+
def test_lra_satask():
|
| 13 |
+
im = Symbol('im', imaginary=True)
|
| 14 |
+
|
| 15 |
+
# test preprocessing of unequalities is working correctly
|
| 16 |
+
assert lra_satask(Q.eq(x, 1), ~Q.ne(x, 0)) is False
|
| 17 |
+
assert lra_satask(Q.eq(x, 0), ~Q.ne(x, 0)) is True
|
| 18 |
+
assert lra_satask(~Q.ne(x, 0), Q.eq(x, 0)) is True
|
| 19 |
+
assert lra_satask(~Q.eq(x, 0), Q.eq(x, 0)) is False
|
| 20 |
+
assert lra_satask(Q.ne(x, 0), Q.eq(x, 0)) is False
|
| 21 |
+
|
| 22 |
+
# basic tests
|
| 23 |
+
assert lra_satask(Q.ne(x, x)) is False
|
| 24 |
+
assert lra_satask(Q.eq(x, x)) is True
|
| 25 |
+
assert lra_satask(Q.gt(x, 0), Q.gt(x, 1)) is True
|
| 26 |
+
|
| 27 |
+
# check that True/False are handled
|
| 28 |
+
assert lra_satask(Q.gt(x, 0), True) is None
|
| 29 |
+
assert raises(ValueError, lambda: lra_satask(Q.gt(x, 0), False))
|
| 30 |
+
|
| 31 |
+
# check imaginary numbers are correctly handled
|
| 32 |
+
# (im * I).is_real returns True so this is an edge case
|
| 33 |
+
raises(UnhandledInput, lambda: lra_satask(Q.gt(im * I, 0), Q.gt(im * I, 0)))
|
| 34 |
+
|
| 35 |
+
# check matrix inputs
|
| 36 |
+
X = MatrixSymbol("X", 2, 2)
|
| 37 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(X, 2) & Q.gt(X, 3)))
|
| 38 |
+
|
| 39 |
+
|
| 40 |
+
def test_old_assumptions():
|
| 41 |
+
# test unhandled old assumptions
|
| 42 |
+
w = symbols("w")
|
| 43 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 44 |
+
w = symbols("w", rational=False, real=True)
|
| 45 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 46 |
+
w = symbols("w", odd=True, real=True)
|
| 47 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 48 |
+
w = symbols("w", even=True, real=True)
|
| 49 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 50 |
+
w = symbols("w", prime=True, real=True)
|
| 51 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 52 |
+
w = symbols("w", composite=True, real=True)
|
| 53 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 54 |
+
w = symbols("w", integer=True, real=True)
|
| 55 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 56 |
+
w = symbols("w", integer=False, real=True)
|
| 57 |
+
raises(UnhandledInput, lambda: lra_satask(Q.lt(w, 2) & Q.gt(w, 3)))
|
| 58 |
+
|
| 59 |
+
# test handled
|
| 60 |
+
w = symbols("w", positive=True, real=True)
|
| 61 |
+
assert lra_satask(Q.le(w, 0)) is False
|
| 62 |
+
assert lra_satask(Q.gt(w, 0)) is True
|
| 63 |
+
w = symbols("w", negative=True, real=True)
|
| 64 |
+
assert lra_satask(Q.lt(w, 0)) is True
|
| 65 |
+
assert lra_satask(Q.ge(w, 0)) is False
|
| 66 |
+
w = symbols("w", zero=True, real=True)
|
| 67 |
+
assert lra_satask(Q.eq(w, 0)) is True
|
| 68 |
+
assert lra_satask(Q.ne(w, 0)) is False
|
| 69 |
+
w = symbols("w", nonzero=True, real=True)
|
| 70 |
+
assert lra_satask(Q.ne(w, 0)) is True
|
| 71 |
+
assert lra_satask(Q.eq(w, 1)) is None
|
| 72 |
+
w = symbols("w", nonpositive=True, real=True)
|
| 73 |
+
assert lra_satask(Q.le(w, 0)) is True
|
| 74 |
+
assert lra_satask(Q.gt(w, 0)) is False
|
| 75 |
+
w = symbols("w", nonnegative=True, real=True)
|
| 76 |
+
assert lra_satask(Q.ge(w, 0)) is True
|
| 77 |
+
assert lra_satask(Q.lt(w, 0)) is False
|
| 78 |
+
|
| 79 |
+
|
| 80 |
+
def test_rel_queries():
|
| 81 |
+
assert ask(Q.lt(x, 2) & Q.gt(x, 3)) is False
|
| 82 |
+
assert ask(Q.positive(x - z), (x > y) & (y > z)) is True
|
| 83 |
+
assert ask(x + y > 2, (x < 0) & (y <0)) is False
|
| 84 |
+
assert ask(x > z, (x > y) & (y > z)) is True
|
| 85 |
+
|
| 86 |
+
|
| 87 |
+
def test_unhandled_queries():
|
| 88 |
+
X = MatrixSymbol("X", 2, 2)
|
| 89 |
+
assert ask(Q.lt(X, 2) & Q.gt(X, 3)) is None
|
| 90 |
+
|
| 91 |
+
|
| 92 |
+
def test_all_pred():
|
| 93 |
+
# test usable pred
|
| 94 |
+
assert lra_satask(Q.extended_positive(x), (x > 2)) is True
|
| 95 |
+
assert lra_satask(Q.positive_infinite(x)) is False
|
| 96 |
+
assert lra_satask(Q.negative_infinite(x)) is False
|
| 97 |
+
|
| 98 |
+
# test disallowed pred
|
| 99 |
+
raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.prime(x)))
|
| 100 |
+
raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.composite(x)))
|
| 101 |
+
raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.odd(x)))
|
| 102 |
+
raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.even(x)))
|
| 103 |
+
raises(UnhandledInput, lambda: lra_satask((x > 0), (x > 2) & Q.integer(x)))
|
| 104 |
+
|
| 105 |
+
|
| 106 |
+
def test_number_line_properties():
|
| 107 |
+
# From:
|
| 108 |
+
# https://en.wikipedia.org/wiki/Inequality_(mathematics)#Properties_on_the_number_line
|
| 109 |
+
|
| 110 |
+
a, b, c = symbols("a b c", real=True)
|
| 111 |
+
|
| 112 |
+
# Transitivity
|
| 113 |
+
# If a <= b and b <= c, then a <= c.
|
| 114 |
+
assert ask(a <= c, (a <= b) & (b <= c)) is True
|
| 115 |
+
# If a <= b and b < c, then a < c.
|
| 116 |
+
assert ask(a < c, (a <= b) & (b < c)) is True
|
| 117 |
+
# If a < b and b <= c, then a < c.
|
| 118 |
+
assert ask(a < c, (a < b) & (b <= c)) is True
|
| 119 |
+
|
| 120 |
+
# Addition and subtraction
|
| 121 |
+
# If a <= b, then a + c <= b + c and a - c <= b - c.
|
| 122 |
+
assert ask(a + c <= b + c, a <= b) is True
|
| 123 |
+
assert ask(a - c <= b - c, a <= b) is True
|
| 124 |
+
|
| 125 |
+
|
| 126 |
+
@XFAIL
|
| 127 |
+
def test_failing_number_line_properties():
|
| 128 |
+
# From:
|
| 129 |
+
# https://en.wikipedia.org/wiki/Inequality_(mathematics)#Properties_on_the_number_line
|
| 130 |
+
|
| 131 |
+
a, b, c = symbols("a b c", real=True)
|
| 132 |
+
|
| 133 |
+
# Multiplication and division
|
| 134 |
+
# If a <= b and c > 0, then ac <= bc and a/c <= b/c. (True for non-zero c)
|
| 135 |
+
assert ask(a*c <= b*c, (a <= b) & (c > 0) & ~ Q.zero(c)) is True
|
| 136 |
+
assert ask(a/c <= b/c, (a <= b) & (c > 0) & ~ Q.zero(c)) is True
|
| 137 |
+
# If a <= b and c < 0, then ac >= bc and a/c >= b/c. (True for non-zero c)
|
| 138 |
+
assert ask(a*c >= b*c, (a <= b) & (c < 0) & ~ Q.zero(c)) is True
|
| 139 |
+
assert ask(a/c >= b/c, (a <= b) & (c < 0) & ~ Q.zero(c)) is True
|
| 140 |
+
|
| 141 |
+
# Additive inverse
|
| 142 |
+
# If a <= b, then -a >= -b.
|
| 143 |
+
assert ask(-a >= -b, a <= b) is True
|
| 144 |
+
|
| 145 |
+
# Multiplicative inverse
|
| 146 |
+
# For a, b that are both negative or both positive:
|
| 147 |
+
# If a <= b, then 1/a >= 1/b .
|
| 148 |
+
assert ask(1/a >= 1/b, (a <= b) & Q.positive(x) & Q.positive(b)) is True
|
| 149 |
+
assert ask(1/a >= 1/b, (a <= b) & Q.negative(x) & Q.negative(b)) is True
|
| 150 |
+
|
| 151 |
+
|
| 152 |
+
def test_equality():
|
| 153 |
+
# test symetry and reflexivity
|
| 154 |
+
assert ask(Q.eq(x, x)) is True
|
| 155 |
+
assert ask(Q.eq(y, x), Q.eq(x, y)) is True
|
| 156 |
+
assert ask(Q.eq(y, x), ~Q.eq(z, z) | Q.eq(x, y)) is True
|
| 157 |
+
|
| 158 |
+
# test transitivity
|
| 159 |
+
assert ask(Q.eq(x,z), Q.eq(x,y) & Q.eq(y,z)) is True
|
| 160 |
+
|
| 161 |
+
|
| 162 |
+
@XFAIL
|
| 163 |
+
def test_equality_failing():
|
| 164 |
+
# Note that implementing the substitution property of equality
|
| 165 |
+
# most likely requires a redesign of the new assumptions.
|
| 166 |
+
# See issue #25485 for why this is the case and general ideas
|
| 167 |
+
# about how things could be redesigned.
|
| 168 |
+
|
| 169 |
+
# test substitution property
|
| 170 |
+
assert ask(Q.prime(x), Q.eq(x, y) & Q.prime(y)) is True
|
| 171 |
+
assert ask(Q.real(x), Q.eq(x, y) & Q.real(y)) is True
|
| 172 |
+
assert ask(Q.imaginary(x), Q.eq(x, y) & Q.imaginary(y)) is True
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_satask.py
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.ask import Q
|
| 2 |
+
from sympy.assumptions.assume import assuming
|
| 3 |
+
from sympy.core.numbers import (I, pi)
|
| 4 |
+
from sympy.core.relational import (Eq, Gt)
|
| 5 |
+
from sympy.core.singleton import S
|
| 6 |
+
from sympy.core.symbol import symbols
|
| 7 |
+
from sympy.functions.elementary.complexes import Abs
|
| 8 |
+
from sympy.logic.boolalg import Implies
|
| 9 |
+
from sympy.matrices.expressions.matexpr import MatrixSymbol
|
| 10 |
+
from sympy.assumptions.cnf import CNF, Literal
|
| 11 |
+
from sympy.assumptions.satask import (satask, extract_predargs,
|
| 12 |
+
get_relevant_clsfacts)
|
| 13 |
+
|
| 14 |
+
from sympy.testing.pytest import raises, XFAIL
|
| 15 |
+
|
| 16 |
+
|
| 17 |
+
x, y, z = symbols('x y z')
|
| 18 |
+
|
| 19 |
+
|
| 20 |
+
def test_satask():
|
| 21 |
+
# No relevant facts
|
| 22 |
+
assert satask(Q.real(x), Q.real(x)) is True
|
| 23 |
+
assert satask(Q.real(x), ~Q.real(x)) is False
|
| 24 |
+
assert satask(Q.real(x)) is None
|
| 25 |
+
|
| 26 |
+
assert satask(Q.real(x), Q.positive(x)) is True
|
| 27 |
+
assert satask(Q.positive(x), Q.real(x)) is None
|
| 28 |
+
assert satask(Q.real(x), ~Q.positive(x)) is None
|
| 29 |
+
assert satask(Q.positive(x), ~Q.real(x)) is False
|
| 30 |
+
|
| 31 |
+
raises(ValueError, lambda: satask(Q.real(x), Q.real(x) & ~Q.real(x)))
|
| 32 |
+
|
| 33 |
+
with assuming(Q.positive(x)):
|
| 34 |
+
assert satask(Q.real(x)) is True
|
| 35 |
+
assert satask(~Q.positive(x)) is False
|
| 36 |
+
raises(ValueError, lambda: satask(Q.real(x), ~Q.positive(x)))
|
| 37 |
+
|
| 38 |
+
assert satask(Q.zero(x), Q.nonzero(x)) is False
|
| 39 |
+
assert satask(Q.positive(x), Q.zero(x)) is False
|
| 40 |
+
assert satask(Q.real(x), Q.zero(x)) is True
|
| 41 |
+
assert satask(Q.zero(x), Q.zero(x*y)) is None
|
| 42 |
+
assert satask(Q.zero(x*y), Q.zero(x))
|
| 43 |
+
|
| 44 |
+
|
| 45 |
+
def test_zero():
|
| 46 |
+
"""
|
| 47 |
+
Everything in this test doesn't work with the ask handlers, and most
|
| 48 |
+
things would be very difficult or impossible to make work under that
|
| 49 |
+
model.
|
| 50 |
+
|
| 51 |
+
"""
|
| 52 |
+
assert satask(Q.zero(x) | Q.zero(y), Q.zero(x*y)) is True
|
| 53 |
+
assert satask(Q.zero(x*y), Q.zero(x) | Q.zero(y)) is True
|
| 54 |
+
|
| 55 |
+
assert satask(Implies(Q.zero(x), Q.zero(x*y))) is True
|
| 56 |
+
|
| 57 |
+
# This one in particular requires computing the fixed-point of the
|
| 58 |
+
# relevant facts, because going from Q.nonzero(x*y) -> ~Q.zero(x*y) and
|
| 59 |
+
# Q.zero(x*y) -> Equivalent(Q.zero(x*y), Q.zero(x) | Q.zero(y)) takes two
|
| 60 |
+
# steps.
|
| 61 |
+
assert satask(Q.zero(x) | Q.zero(y), Q.nonzero(x*y)) is False
|
| 62 |
+
|
| 63 |
+
assert satask(Q.zero(x), Q.zero(x**2)) is True
|
| 64 |
+
|
| 65 |
+
|
| 66 |
+
def test_zero_positive():
|
| 67 |
+
assert satask(Q.zero(x + y), Q.positive(x) & Q.positive(y)) is False
|
| 68 |
+
assert satask(Q.positive(x) & Q.positive(y), Q.zero(x + y)) is False
|
| 69 |
+
assert satask(Q.nonzero(x + y), Q.positive(x) & Q.positive(y)) is True
|
| 70 |
+
assert satask(Q.positive(x) & Q.positive(y), Q.nonzero(x + y)) is None
|
| 71 |
+
|
| 72 |
+
# This one requires several levels of forward chaining
|
| 73 |
+
assert satask(Q.zero(x*(x + y)), Q.positive(x) & Q.positive(y)) is False
|
| 74 |
+
|
| 75 |
+
assert satask(Q.positive(pi*x*y + 1), Q.positive(x) & Q.positive(y)) is True
|
| 76 |
+
assert satask(Q.positive(pi*x*y - 5), Q.positive(x) & Q.positive(y)) is None
|
| 77 |
+
|
| 78 |
+
|
| 79 |
+
def test_zero_pow():
|
| 80 |
+
assert satask(Q.zero(x**y), Q.zero(x) & Q.positive(y)) is True
|
| 81 |
+
assert satask(Q.zero(x**y), Q.nonzero(x) & Q.zero(y)) is False
|
| 82 |
+
|
| 83 |
+
assert satask(Q.zero(x), Q.zero(x**y)) is True
|
| 84 |
+
|
| 85 |
+
assert satask(Q.zero(x**y), Q.zero(x)) is None
|
| 86 |
+
|
| 87 |
+
|
| 88 |
+
@XFAIL
|
| 89 |
+
# Requires correct Q.square calculation first
|
| 90 |
+
def test_invertible():
|
| 91 |
+
A = MatrixSymbol('A', 5, 5)
|
| 92 |
+
B = MatrixSymbol('B', 5, 5)
|
| 93 |
+
assert satask(Q.invertible(A*B), Q.invertible(A) & Q.invertible(B)) is True
|
| 94 |
+
assert satask(Q.invertible(A), Q.invertible(A*B)) is True
|
| 95 |
+
assert satask(Q.invertible(A) & Q.invertible(B), Q.invertible(A*B)) is True
|
| 96 |
+
|
| 97 |
+
|
| 98 |
+
def test_prime():
|
| 99 |
+
assert satask(Q.prime(5)) is True
|
| 100 |
+
assert satask(Q.prime(6)) is False
|
| 101 |
+
assert satask(Q.prime(-5)) is False
|
| 102 |
+
|
| 103 |
+
assert satask(Q.prime(x*y), Q.integer(x) & Q.integer(y)) is None
|
| 104 |
+
assert satask(Q.prime(x*y), Q.prime(x) & Q.prime(y)) is False
|
| 105 |
+
|
| 106 |
+
|
| 107 |
+
def test_old_assump():
|
| 108 |
+
assert satask(Q.positive(1)) is True
|
| 109 |
+
assert satask(Q.positive(-1)) is False
|
| 110 |
+
assert satask(Q.positive(0)) is False
|
| 111 |
+
assert satask(Q.positive(I)) is False
|
| 112 |
+
assert satask(Q.positive(pi)) is True
|
| 113 |
+
|
| 114 |
+
assert satask(Q.negative(1)) is False
|
| 115 |
+
assert satask(Q.negative(-1)) is True
|
| 116 |
+
assert satask(Q.negative(0)) is False
|
| 117 |
+
assert satask(Q.negative(I)) is False
|
| 118 |
+
assert satask(Q.negative(pi)) is False
|
| 119 |
+
|
| 120 |
+
assert satask(Q.zero(1)) is False
|
| 121 |
+
assert satask(Q.zero(-1)) is False
|
| 122 |
+
assert satask(Q.zero(0)) is True
|
| 123 |
+
assert satask(Q.zero(I)) is False
|
| 124 |
+
assert satask(Q.zero(pi)) is False
|
| 125 |
+
|
| 126 |
+
assert satask(Q.nonzero(1)) is True
|
| 127 |
+
assert satask(Q.nonzero(-1)) is True
|
| 128 |
+
assert satask(Q.nonzero(0)) is False
|
| 129 |
+
assert satask(Q.nonzero(I)) is False
|
| 130 |
+
assert satask(Q.nonzero(pi)) is True
|
| 131 |
+
|
| 132 |
+
assert satask(Q.nonpositive(1)) is False
|
| 133 |
+
assert satask(Q.nonpositive(-1)) is True
|
| 134 |
+
assert satask(Q.nonpositive(0)) is True
|
| 135 |
+
assert satask(Q.nonpositive(I)) is False
|
| 136 |
+
assert satask(Q.nonpositive(pi)) is False
|
| 137 |
+
|
| 138 |
+
assert satask(Q.nonnegative(1)) is True
|
| 139 |
+
assert satask(Q.nonnegative(-1)) is False
|
| 140 |
+
assert satask(Q.nonnegative(0)) is True
|
| 141 |
+
assert satask(Q.nonnegative(I)) is False
|
| 142 |
+
assert satask(Q.nonnegative(pi)) is True
|
| 143 |
+
|
| 144 |
+
|
| 145 |
+
def test_rational_irrational():
|
| 146 |
+
assert satask(Q.irrational(2)) is False
|
| 147 |
+
assert satask(Q.rational(2)) is True
|
| 148 |
+
assert satask(Q.irrational(pi)) is True
|
| 149 |
+
assert satask(Q.rational(pi)) is False
|
| 150 |
+
assert satask(Q.irrational(I)) is False
|
| 151 |
+
assert satask(Q.rational(I)) is False
|
| 152 |
+
|
| 153 |
+
assert satask(Q.irrational(x*y*z), Q.irrational(x) & Q.irrational(y) &
|
| 154 |
+
Q.rational(z)) is None
|
| 155 |
+
assert satask(Q.irrational(x*y*z), Q.irrational(x) & Q.rational(y) &
|
| 156 |
+
Q.rational(z)) is True
|
| 157 |
+
assert satask(Q.irrational(pi*x*y), Q.rational(x) & Q.rational(y)) is True
|
| 158 |
+
|
| 159 |
+
assert satask(Q.irrational(x + y + z), Q.irrational(x) & Q.irrational(y) &
|
| 160 |
+
Q.rational(z)) is None
|
| 161 |
+
assert satask(Q.irrational(x + y + z), Q.irrational(x) & Q.rational(y) &
|
| 162 |
+
Q.rational(z)) is True
|
| 163 |
+
assert satask(Q.irrational(pi + x + y), Q.rational(x) & Q.rational(y)) is True
|
| 164 |
+
|
| 165 |
+
assert satask(Q.irrational(x*y*z), Q.rational(x) & Q.rational(y) &
|
| 166 |
+
Q.rational(z)) is False
|
| 167 |
+
assert satask(Q.rational(x*y*z), Q.rational(x) & Q.rational(y) &
|
| 168 |
+
Q.rational(z)) is True
|
| 169 |
+
|
| 170 |
+
assert satask(Q.irrational(x + y + z), Q.rational(x) & Q.rational(y) &
|
| 171 |
+
Q.rational(z)) is False
|
| 172 |
+
assert satask(Q.rational(x + y + z), Q.rational(x) & Q.rational(y) &
|
| 173 |
+
Q.rational(z)) is True
|
| 174 |
+
|
| 175 |
+
|
| 176 |
+
def test_even_satask():
|
| 177 |
+
assert satask(Q.even(2)) is True
|
| 178 |
+
assert satask(Q.even(3)) is False
|
| 179 |
+
|
| 180 |
+
assert satask(Q.even(x*y), Q.even(x) & Q.odd(y)) is True
|
| 181 |
+
assert satask(Q.even(x*y), Q.even(x) & Q.integer(y)) is True
|
| 182 |
+
assert satask(Q.even(x*y), Q.even(x) & Q.even(y)) is True
|
| 183 |
+
assert satask(Q.even(x*y), Q.odd(x) & Q.odd(y)) is False
|
| 184 |
+
assert satask(Q.even(x*y), Q.even(x)) is None
|
| 185 |
+
assert satask(Q.even(x*y), Q.odd(x) & Q.integer(y)) is None
|
| 186 |
+
assert satask(Q.even(x*y), Q.odd(x) & Q.odd(y)) is False
|
| 187 |
+
|
| 188 |
+
assert satask(Q.even(abs(x)), Q.even(x)) is True
|
| 189 |
+
assert satask(Q.even(abs(x)), Q.odd(x)) is False
|
| 190 |
+
assert satask(Q.even(x), Q.even(abs(x))) is None # x could be complex
|
| 191 |
+
|
| 192 |
+
|
| 193 |
+
def test_odd_satask():
|
| 194 |
+
assert satask(Q.odd(2)) is False
|
| 195 |
+
assert satask(Q.odd(3)) is True
|
| 196 |
+
|
| 197 |
+
assert satask(Q.odd(x*y), Q.even(x) & Q.odd(y)) is False
|
| 198 |
+
assert satask(Q.odd(x*y), Q.even(x) & Q.integer(y)) is False
|
| 199 |
+
assert satask(Q.odd(x*y), Q.even(x) & Q.even(y)) is False
|
| 200 |
+
assert satask(Q.odd(x*y), Q.odd(x) & Q.odd(y)) is True
|
| 201 |
+
assert satask(Q.odd(x*y), Q.even(x)) is None
|
| 202 |
+
assert satask(Q.odd(x*y), Q.odd(x) & Q.integer(y)) is None
|
| 203 |
+
assert satask(Q.odd(x*y), Q.odd(x) & Q.odd(y)) is True
|
| 204 |
+
|
| 205 |
+
assert satask(Q.odd(abs(x)), Q.even(x)) is False
|
| 206 |
+
assert satask(Q.odd(abs(x)), Q.odd(x)) is True
|
| 207 |
+
assert satask(Q.odd(x), Q.odd(abs(x))) is None # x could be complex
|
| 208 |
+
|
| 209 |
+
|
| 210 |
+
def test_integer():
|
| 211 |
+
assert satask(Q.integer(1)) is True
|
| 212 |
+
assert satask(Q.integer(S.Half)) is False
|
| 213 |
+
|
| 214 |
+
assert satask(Q.integer(x + y), Q.integer(x) & Q.integer(y)) is True
|
| 215 |
+
assert satask(Q.integer(x + y), Q.integer(x)) is None
|
| 216 |
+
|
| 217 |
+
assert satask(Q.integer(x + y), Q.integer(x) & ~Q.integer(y)) is False
|
| 218 |
+
assert satask(Q.integer(x + y + z), Q.integer(x) & Q.integer(y) &
|
| 219 |
+
~Q.integer(z)) is False
|
| 220 |
+
assert satask(Q.integer(x + y + z), Q.integer(x) & ~Q.integer(y) &
|
| 221 |
+
~Q.integer(z)) is None
|
| 222 |
+
assert satask(Q.integer(x + y + z), Q.integer(x) & ~Q.integer(y)) is None
|
| 223 |
+
assert satask(Q.integer(x + y), Q.integer(x) & Q.irrational(y)) is False
|
| 224 |
+
|
| 225 |
+
assert satask(Q.integer(x*y), Q.integer(x) & Q.integer(y)) is True
|
| 226 |
+
assert satask(Q.integer(x*y), Q.integer(x)) is None
|
| 227 |
+
|
| 228 |
+
assert satask(Q.integer(x*y), Q.integer(x) & ~Q.integer(y)) is None
|
| 229 |
+
assert satask(Q.integer(x*y), Q.integer(x) & ~Q.rational(y)) is False
|
| 230 |
+
assert satask(Q.integer(x*y*z), Q.integer(x) & Q.integer(y) &
|
| 231 |
+
~Q.rational(z)) is False
|
| 232 |
+
assert satask(Q.integer(x*y*z), Q.integer(x) & ~Q.rational(y) &
|
| 233 |
+
~Q.rational(z)) is None
|
| 234 |
+
assert satask(Q.integer(x*y*z), Q.integer(x) & ~Q.rational(y)) is None
|
| 235 |
+
assert satask(Q.integer(x*y), Q.integer(x) & Q.irrational(y)) is False
|
| 236 |
+
|
| 237 |
+
|
| 238 |
+
def test_abs():
|
| 239 |
+
assert satask(Q.nonnegative(abs(x))) is True
|
| 240 |
+
assert satask(Q.positive(abs(x)), ~Q.zero(x)) is True
|
| 241 |
+
assert satask(Q.zero(x), ~Q.zero(abs(x))) is False
|
| 242 |
+
assert satask(Q.zero(x), Q.zero(abs(x))) is True
|
| 243 |
+
assert satask(Q.nonzero(x), ~Q.zero(abs(x))) is None # x could be complex
|
| 244 |
+
assert satask(Q.zero(abs(x)), Q.zero(x)) is True
|
| 245 |
+
|
| 246 |
+
|
| 247 |
+
def test_imaginary():
|
| 248 |
+
assert satask(Q.imaginary(2*I)) is True
|
| 249 |
+
assert satask(Q.imaginary(x*y), Q.imaginary(x)) is None
|
| 250 |
+
assert satask(Q.imaginary(x*y), Q.imaginary(x) & Q.real(y)) is True
|
| 251 |
+
assert satask(Q.imaginary(x), Q.real(x)) is False
|
| 252 |
+
assert satask(Q.imaginary(1)) is False
|
| 253 |
+
assert satask(Q.imaginary(x*y), Q.real(x) & Q.real(y)) is False
|
| 254 |
+
assert satask(Q.imaginary(x + y), Q.real(x) & Q.real(y)) is False
|
| 255 |
+
|
| 256 |
+
|
| 257 |
+
def test_real():
|
| 258 |
+
assert satask(Q.real(x*y), Q.real(x) & Q.real(y)) is True
|
| 259 |
+
assert satask(Q.real(x + y), Q.real(x) & Q.real(y)) is True
|
| 260 |
+
assert satask(Q.real(x*y*z), Q.real(x) & Q.real(y) & Q.real(z)) is True
|
| 261 |
+
assert satask(Q.real(x*y*z), Q.real(x) & Q.real(y)) is None
|
| 262 |
+
assert satask(Q.real(x*y*z), Q.real(x) & Q.real(y) & Q.imaginary(z)) is False
|
| 263 |
+
assert satask(Q.real(x + y + z), Q.real(x) & Q.real(y) & Q.real(z)) is True
|
| 264 |
+
assert satask(Q.real(x + y + z), Q.real(x) & Q.real(y)) is None
|
| 265 |
+
|
| 266 |
+
|
| 267 |
+
def test_pos_neg():
|
| 268 |
+
assert satask(~Q.positive(x), Q.negative(x)) is True
|
| 269 |
+
assert satask(~Q.negative(x), Q.positive(x)) is True
|
| 270 |
+
assert satask(Q.positive(x + y), Q.positive(x) & Q.positive(y)) is True
|
| 271 |
+
assert satask(Q.negative(x + y), Q.negative(x) & Q.negative(y)) is True
|
| 272 |
+
assert satask(Q.positive(x + y), Q.negative(x) & Q.negative(y)) is False
|
| 273 |
+
assert satask(Q.negative(x + y), Q.positive(x) & Q.positive(y)) is False
|
| 274 |
+
|
| 275 |
+
|
| 276 |
+
def test_pow_pos_neg():
|
| 277 |
+
assert satask(Q.nonnegative(x**2), Q.positive(x)) is True
|
| 278 |
+
assert satask(Q.nonpositive(x**2), Q.positive(x)) is False
|
| 279 |
+
assert satask(Q.positive(x**2), Q.positive(x)) is True
|
| 280 |
+
assert satask(Q.negative(x**2), Q.positive(x)) is False
|
| 281 |
+
assert satask(Q.real(x**2), Q.positive(x)) is True
|
| 282 |
+
|
| 283 |
+
assert satask(Q.nonnegative(x**2), Q.negative(x)) is True
|
| 284 |
+
assert satask(Q.nonpositive(x**2), Q.negative(x)) is False
|
| 285 |
+
assert satask(Q.positive(x**2), Q.negative(x)) is True
|
| 286 |
+
assert satask(Q.negative(x**2), Q.negative(x)) is False
|
| 287 |
+
assert satask(Q.real(x**2), Q.negative(x)) is True
|
| 288 |
+
|
| 289 |
+
assert satask(Q.nonnegative(x**2), Q.nonnegative(x)) is True
|
| 290 |
+
assert satask(Q.nonpositive(x**2), Q.nonnegative(x)) is None
|
| 291 |
+
assert satask(Q.positive(x**2), Q.nonnegative(x)) is None
|
| 292 |
+
assert satask(Q.negative(x**2), Q.nonnegative(x)) is False
|
| 293 |
+
assert satask(Q.real(x**2), Q.nonnegative(x)) is True
|
| 294 |
+
|
| 295 |
+
assert satask(Q.nonnegative(x**2), Q.nonpositive(x)) is True
|
| 296 |
+
assert satask(Q.nonpositive(x**2), Q.nonpositive(x)) is None
|
| 297 |
+
assert satask(Q.positive(x**2), Q.nonpositive(x)) is None
|
| 298 |
+
assert satask(Q.negative(x**2), Q.nonpositive(x)) is False
|
| 299 |
+
assert satask(Q.real(x**2), Q.nonpositive(x)) is True
|
| 300 |
+
|
| 301 |
+
assert satask(Q.nonnegative(x**3), Q.positive(x)) is True
|
| 302 |
+
assert satask(Q.nonpositive(x**3), Q.positive(x)) is False
|
| 303 |
+
assert satask(Q.positive(x**3), Q.positive(x)) is True
|
| 304 |
+
assert satask(Q.negative(x**3), Q.positive(x)) is False
|
| 305 |
+
assert satask(Q.real(x**3), Q.positive(x)) is True
|
| 306 |
+
|
| 307 |
+
assert satask(Q.nonnegative(x**3), Q.negative(x)) is False
|
| 308 |
+
assert satask(Q.nonpositive(x**3), Q.negative(x)) is True
|
| 309 |
+
assert satask(Q.positive(x**3), Q.negative(x)) is False
|
| 310 |
+
assert satask(Q.negative(x**3), Q.negative(x)) is True
|
| 311 |
+
assert satask(Q.real(x**3), Q.negative(x)) is True
|
| 312 |
+
|
| 313 |
+
assert satask(Q.nonnegative(x**3), Q.nonnegative(x)) is True
|
| 314 |
+
assert satask(Q.nonpositive(x**3), Q.nonnegative(x)) is None
|
| 315 |
+
assert satask(Q.positive(x**3), Q.nonnegative(x)) is None
|
| 316 |
+
assert satask(Q.negative(x**3), Q.nonnegative(x)) is False
|
| 317 |
+
assert satask(Q.real(x**3), Q.nonnegative(x)) is True
|
| 318 |
+
|
| 319 |
+
assert satask(Q.nonnegative(x**3), Q.nonpositive(x)) is None
|
| 320 |
+
assert satask(Q.nonpositive(x**3), Q.nonpositive(x)) is True
|
| 321 |
+
assert satask(Q.positive(x**3), Q.nonpositive(x)) is False
|
| 322 |
+
assert satask(Q.negative(x**3), Q.nonpositive(x)) is None
|
| 323 |
+
assert satask(Q.real(x**3), Q.nonpositive(x)) is True
|
| 324 |
+
|
| 325 |
+
# If x is zero, x**negative is not real.
|
| 326 |
+
assert satask(Q.nonnegative(x**-2), Q.nonpositive(x)) is None
|
| 327 |
+
assert satask(Q.nonpositive(x**-2), Q.nonpositive(x)) is None
|
| 328 |
+
assert satask(Q.positive(x**-2), Q.nonpositive(x)) is None
|
| 329 |
+
assert satask(Q.negative(x**-2), Q.nonpositive(x)) is None
|
| 330 |
+
assert satask(Q.real(x**-2), Q.nonpositive(x)) is None
|
| 331 |
+
|
| 332 |
+
# We could deduce things for negative powers if x is nonzero, but it
|
| 333 |
+
# isn't implemented yet.
|
| 334 |
+
|
| 335 |
+
|
| 336 |
+
def test_prime_composite():
|
| 337 |
+
assert satask(Q.prime(x), Q.composite(x)) is False
|
| 338 |
+
assert satask(Q.composite(x), Q.prime(x)) is False
|
| 339 |
+
assert satask(Q.composite(x), ~Q.prime(x)) is None
|
| 340 |
+
assert satask(Q.prime(x), ~Q.composite(x)) is None
|
| 341 |
+
# since 1 is neither prime nor composite the following should hold
|
| 342 |
+
assert satask(Q.prime(x), Q.integer(x) & Q.positive(x) & ~Q.composite(x)) is None
|
| 343 |
+
assert satask(Q.prime(2)) is True
|
| 344 |
+
assert satask(Q.prime(4)) is False
|
| 345 |
+
assert satask(Q.prime(1)) is False
|
| 346 |
+
assert satask(Q.composite(1)) is False
|
| 347 |
+
|
| 348 |
+
|
| 349 |
+
def test_extract_predargs():
|
| 350 |
+
props = CNF.from_prop(Q.zero(Abs(x*y)) & Q.zero(x*y))
|
| 351 |
+
assump = CNF.from_prop(Q.zero(x))
|
| 352 |
+
context = CNF.from_prop(Q.zero(y))
|
| 353 |
+
assert extract_predargs(props) == {Abs(x*y), x*y}
|
| 354 |
+
assert extract_predargs(props, assump) == {Abs(x*y), x*y, x}
|
| 355 |
+
assert extract_predargs(props, assump, context) == {Abs(x*y), x*y, x, y}
|
| 356 |
+
|
| 357 |
+
props = CNF.from_prop(Eq(x, y))
|
| 358 |
+
assump = CNF.from_prop(Gt(y, z))
|
| 359 |
+
assert extract_predargs(props, assump) == {x, y, z}
|
| 360 |
+
|
| 361 |
+
|
| 362 |
+
def test_get_relevant_clsfacts():
|
| 363 |
+
exprs = {Abs(x*y)}
|
| 364 |
+
exprs, facts = get_relevant_clsfacts(exprs)
|
| 365 |
+
assert exprs == {x*y}
|
| 366 |
+
assert facts.clauses == \
|
| 367 |
+
{frozenset({Literal(Q.odd(Abs(x*y)), False), Literal(Q.odd(x*y), True)}),
|
| 368 |
+
frozenset({Literal(Q.zero(Abs(x*y)), False), Literal(Q.zero(x*y), True)}),
|
| 369 |
+
frozenset({Literal(Q.even(Abs(x*y)), False), Literal(Q.even(x*y), True)}),
|
| 370 |
+
frozenset({Literal(Q.zero(Abs(x*y)), True), Literal(Q.zero(x*y), False)}),
|
| 371 |
+
frozenset({Literal(Q.even(Abs(x*y)), False),
|
| 372 |
+
Literal(Q.odd(Abs(x*y)), False),
|
| 373 |
+
Literal(Q.odd(x*y), True)}),
|
| 374 |
+
frozenset({Literal(Q.even(Abs(x*y)), False),
|
| 375 |
+
Literal(Q.even(x*y), True),
|
| 376 |
+
Literal(Q.odd(Abs(x*y)), False)}),
|
| 377 |
+
frozenset({Literal(Q.positive(Abs(x*y)), False),
|
| 378 |
+
Literal(Q.zero(Abs(x*y)), False)})}
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_sathandlers.py
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.ask import Q
|
| 2 |
+
from sympy.core.basic import Basic
|
| 3 |
+
from sympy.core.expr import Expr
|
| 4 |
+
from sympy.core.mul import Mul
|
| 5 |
+
from sympy.core.symbol import symbols
|
| 6 |
+
from sympy.logic.boolalg import (And, Or)
|
| 7 |
+
|
| 8 |
+
from sympy.assumptions.sathandlers import (ClassFactRegistry, allargs,
|
| 9 |
+
anyarg, exactlyonearg,)
|
| 10 |
+
|
| 11 |
+
x, y, z = symbols('x y z')
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_class_handler_registry():
|
| 15 |
+
my_handler_registry = ClassFactRegistry()
|
| 16 |
+
|
| 17 |
+
# The predicate doesn't matter here, so just pass
|
| 18 |
+
@my_handler_registry.register(Mul)
|
| 19 |
+
def fact1(expr):
|
| 20 |
+
pass
|
| 21 |
+
@my_handler_registry.multiregister(Expr)
|
| 22 |
+
def fact2(expr):
|
| 23 |
+
pass
|
| 24 |
+
|
| 25 |
+
assert my_handler_registry[Basic] == (frozenset(), frozenset())
|
| 26 |
+
assert my_handler_registry[Expr] == (frozenset(), frozenset({fact2}))
|
| 27 |
+
assert my_handler_registry[Mul] == (frozenset({fact1}), frozenset({fact2}))
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def test_allargs():
|
| 31 |
+
assert allargs(x, Q.zero(x), x*y) == And(Q.zero(x), Q.zero(y))
|
| 32 |
+
assert allargs(x, Q.positive(x) | Q.negative(x), x*y) == And(Q.positive(x) | Q.negative(x), Q.positive(y) | Q.negative(y))
|
| 33 |
+
|
| 34 |
+
|
| 35 |
+
def test_anyarg():
|
| 36 |
+
assert anyarg(x, Q.zero(x), x*y) == Or(Q.zero(x), Q.zero(y))
|
| 37 |
+
assert anyarg(x, Q.positive(x) & Q.negative(x), x*y) == \
|
| 38 |
+
Or(Q.positive(x) & Q.negative(x), Q.positive(y) & Q.negative(y))
|
| 39 |
+
|
| 40 |
+
|
| 41 |
+
def test_exactlyonearg():
|
| 42 |
+
assert exactlyonearg(x, Q.zero(x), x*y) == \
|
| 43 |
+
Or(Q.zero(x) & ~Q.zero(y), Q.zero(y) & ~Q.zero(x))
|
| 44 |
+
assert exactlyonearg(x, Q.zero(x), x*y*z) == \
|
| 45 |
+
Or(Q.zero(x) & ~Q.zero(y) & ~Q.zero(z), Q.zero(y)
|
| 46 |
+
& ~Q.zero(x) & ~Q.zero(z), Q.zero(z) & ~Q.zero(x) & ~Q.zero(y))
|
| 47 |
+
assert exactlyonearg(x, Q.positive(x) | Q.negative(x), x*y) == \
|
| 48 |
+
Or((Q.positive(x) | Q.negative(x)) &
|
| 49 |
+
~(Q.positive(y) | Q.negative(y)), (Q.positive(y) | Q.negative(y)) &
|
| 50 |
+
~(Q.positive(x) | Q.negative(x)))
|
lib/python3.12/site-packages/sympy/assumptions/tests/test_wrapper.py
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
from sympy.assumptions.ask import Q
|
| 2 |
+
from sympy.assumptions.wrapper import (AssumptionsWrapper, is_infinite,
|
| 3 |
+
is_extended_real)
|
| 4 |
+
from sympy.core.symbol import Symbol
|
| 5 |
+
from sympy.core.assumptions import _assume_defined
|
| 6 |
+
|
| 7 |
+
|
| 8 |
+
def test_all_predicates():
|
| 9 |
+
for fact in _assume_defined:
|
| 10 |
+
method_name = f'_eval_is_{fact}'
|
| 11 |
+
assert hasattr(AssumptionsWrapper, method_name)
|
| 12 |
+
|
| 13 |
+
|
| 14 |
+
def test_AssumptionsWrapper():
|
| 15 |
+
x = Symbol('x', positive=True)
|
| 16 |
+
y = Symbol('y')
|
| 17 |
+
assert AssumptionsWrapper(x).is_positive
|
| 18 |
+
assert AssumptionsWrapper(y).is_positive is None
|
| 19 |
+
assert AssumptionsWrapper(y, Q.positive(y)).is_positive
|
| 20 |
+
|
| 21 |
+
|
| 22 |
+
def test_is_infinite():
|
| 23 |
+
x = Symbol('x', infinite=True)
|
| 24 |
+
y = Symbol('y', infinite=False)
|
| 25 |
+
z = Symbol('z')
|
| 26 |
+
assert is_infinite(x)
|
| 27 |
+
assert not is_infinite(y)
|
| 28 |
+
assert is_infinite(z) is None
|
| 29 |
+
assert is_infinite(z, Q.infinite(z))
|
| 30 |
+
|
| 31 |
+
|
| 32 |
+
def test_is_extended_real():
|
| 33 |
+
x = Symbol('x', extended_real=True)
|
| 34 |
+
y = Symbol('y', extended_real=False)
|
| 35 |
+
z = Symbol('z')
|
| 36 |
+
assert is_extended_real(x)
|
| 37 |
+
assert not is_extended_real(y)
|
| 38 |
+
assert is_extended_real(z) is None
|
| 39 |
+
assert is_extended_real(z, Q.extended_real(z))
|
lib/python3.12/site-packages/sympy/assumptions/wrapper.py
ADDED
|
@@ -0,0 +1,164 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""
|
| 2 |
+
Functions and wrapper object to call assumption property and predicate
|
| 3 |
+
query with same syntax.
|
| 4 |
+
|
| 5 |
+
In SymPy, there are two assumption systems. Old assumption system is
|
| 6 |
+
defined in sympy/core/assumptions, and it can be accessed by attribute
|
| 7 |
+
such as ``x.is_even``. New assumption system is defined in
|
| 8 |
+
sympy/assumptions, and it can be accessed by predicates such as
|
| 9 |
+
``Q.even(x)``.
|
| 10 |
+
|
| 11 |
+
Old assumption is fast, while new assumptions can freely take local facts.
|
| 12 |
+
In general, old assumption is used in evaluation method and new assumption
|
| 13 |
+
is used in refinement method.
|
| 14 |
+
|
| 15 |
+
In most cases, both evaluation and refinement follow the same process, and
|
| 16 |
+
the only difference is which assumption system is used. This module provides
|
| 17 |
+
``is_[...]()`` functions and ``AssumptionsWrapper()`` class which allows
|
| 18 |
+
using two systems with same syntax so that parallel code implementation can be
|
| 19 |
+
avoided.
|
| 20 |
+
|
| 21 |
+
Examples
|
| 22 |
+
========
|
| 23 |
+
|
| 24 |
+
For multiple use, use ``AssumptionsWrapper()``.
|
| 25 |
+
|
| 26 |
+
>>> from sympy import Q, Symbol
|
| 27 |
+
>>> from sympy.assumptions.wrapper import AssumptionsWrapper
|
| 28 |
+
>>> x = Symbol('x')
|
| 29 |
+
>>> _x = AssumptionsWrapper(x, Q.even(x))
|
| 30 |
+
>>> _x.is_integer
|
| 31 |
+
True
|
| 32 |
+
>>> _x.is_odd
|
| 33 |
+
False
|
| 34 |
+
|
| 35 |
+
For single use, use ``is_[...]()`` functions.
|
| 36 |
+
|
| 37 |
+
>>> from sympy.assumptions.wrapper import is_infinite
|
| 38 |
+
>>> a = Symbol('a')
|
| 39 |
+
>>> print(is_infinite(a))
|
| 40 |
+
None
|
| 41 |
+
>>> is_infinite(a, Q.finite(a))
|
| 42 |
+
False
|
| 43 |
+
|
| 44 |
+
"""
|
| 45 |
+
|
| 46 |
+
from sympy.assumptions import ask, Q
|
| 47 |
+
from sympy.core.basic import Basic
|
| 48 |
+
from sympy.core.sympify import _sympify
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def make_eval_method(fact):
|
| 52 |
+
def getit(self):
|
| 53 |
+
pred = getattr(Q, fact)
|
| 54 |
+
ret = ask(pred(self.expr), self.assumptions)
|
| 55 |
+
return ret
|
| 56 |
+
return getit
|
| 57 |
+
|
| 58 |
+
|
| 59 |
+
# we subclass Basic to use the fact deduction and caching
|
| 60 |
+
class AssumptionsWrapper(Basic):
|
| 61 |
+
"""
|
| 62 |
+
Wrapper over ``Basic`` instances to call predicate query by
|
| 63 |
+
``.is_[...]`` property
|
| 64 |
+
|
| 65 |
+
Parameters
|
| 66 |
+
==========
|
| 67 |
+
|
| 68 |
+
expr : Basic
|
| 69 |
+
|
| 70 |
+
assumptions : Boolean, optional
|
| 71 |
+
|
| 72 |
+
Examples
|
| 73 |
+
========
|
| 74 |
+
|
| 75 |
+
>>> from sympy import Q, Symbol
|
| 76 |
+
>>> from sympy.assumptions.wrapper import AssumptionsWrapper
|
| 77 |
+
>>> x = Symbol('x', even=True)
|
| 78 |
+
>>> AssumptionsWrapper(x).is_integer
|
| 79 |
+
True
|
| 80 |
+
>>> y = Symbol('y')
|
| 81 |
+
>>> AssumptionsWrapper(y, Q.even(y)).is_integer
|
| 82 |
+
True
|
| 83 |
+
|
| 84 |
+
With ``AssumptionsWrapper``, both evaluation and refinement can be supported
|
| 85 |
+
by single implementation.
|
| 86 |
+
|
| 87 |
+
>>> from sympy import Function
|
| 88 |
+
>>> class MyAbs(Function):
|
| 89 |
+
... @classmethod
|
| 90 |
+
... def eval(cls, x, assumptions=True):
|
| 91 |
+
... _x = AssumptionsWrapper(x, assumptions)
|
| 92 |
+
... if _x.is_nonnegative:
|
| 93 |
+
... return x
|
| 94 |
+
... if _x.is_negative:
|
| 95 |
+
... return -x
|
| 96 |
+
... def _eval_refine(self, assumptions):
|
| 97 |
+
... return MyAbs.eval(self.args[0], assumptions)
|
| 98 |
+
>>> MyAbs(x)
|
| 99 |
+
MyAbs(x)
|
| 100 |
+
>>> MyAbs(x).refine(Q.positive(x))
|
| 101 |
+
x
|
| 102 |
+
>>> MyAbs(Symbol('y', negative=True))
|
| 103 |
+
-y
|
| 104 |
+
|
| 105 |
+
"""
|
| 106 |
+
def __new__(cls, expr, assumptions=None):
|
| 107 |
+
if assumptions is None:
|
| 108 |
+
return expr
|
| 109 |
+
obj = super().__new__(cls, expr, _sympify(assumptions))
|
| 110 |
+
obj.expr = expr
|
| 111 |
+
obj.assumptions = assumptions
|
| 112 |
+
return obj
|
| 113 |
+
|
| 114 |
+
_eval_is_algebraic = make_eval_method("algebraic")
|
| 115 |
+
_eval_is_antihermitian = make_eval_method("antihermitian")
|
| 116 |
+
_eval_is_commutative = make_eval_method("commutative")
|
| 117 |
+
_eval_is_complex = make_eval_method("complex")
|
| 118 |
+
_eval_is_composite = make_eval_method("composite")
|
| 119 |
+
_eval_is_even = make_eval_method("even")
|
| 120 |
+
_eval_is_extended_negative = make_eval_method("extended_negative")
|
| 121 |
+
_eval_is_extended_nonnegative = make_eval_method("extended_nonnegative")
|
| 122 |
+
_eval_is_extended_nonpositive = make_eval_method("extended_nonpositive")
|
| 123 |
+
_eval_is_extended_nonzero = make_eval_method("extended_nonzero")
|
| 124 |
+
_eval_is_extended_positive = make_eval_method("extended_positive")
|
| 125 |
+
_eval_is_extended_real = make_eval_method("extended_real")
|
| 126 |
+
_eval_is_finite = make_eval_method("finite")
|
| 127 |
+
_eval_is_hermitian = make_eval_method("hermitian")
|
| 128 |
+
_eval_is_imaginary = make_eval_method("imaginary")
|
| 129 |
+
_eval_is_infinite = make_eval_method("infinite")
|
| 130 |
+
_eval_is_integer = make_eval_method("integer")
|
| 131 |
+
_eval_is_irrational = make_eval_method("irrational")
|
| 132 |
+
_eval_is_negative = make_eval_method("negative")
|
| 133 |
+
_eval_is_noninteger = make_eval_method("noninteger")
|
| 134 |
+
_eval_is_nonnegative = make_eval_method("nonnegative")
|
| 135 |
+
_eval_is_nonpositive = make_eval_method("nonpositive")
|
| 136 |
+
_eval_is_nonzero = make_eval_method("nonzero")
|
| 137 |
+
_eval_is_odd = make_eval_method("odd")
|
| 138 |
+
_eval_is_polar = make_eval_method("polar")
|
| 139 |
+
_eval_is_positive = make_eval_method("positive")
|
| 140 |
+
_eval_is_prime = make_eval_method("prime")
|
| 141 |
+
_eval_is_rational = make_eval_method("rational")
|
| 142 |
+
_eval_is_real = make_eval_method("real")
|
| 143 |
+
_eval_is_transcendental = make_eval_method("transcendental")
|
| 144 |
+
_eval_is_zero = make_eval_method("zero")
|
| 145 |
+
|
| 146 |
+
|
| 147 |
+
# one shot functions which are faster than AssumptionsWrapper
|
| 148 |
+
|
| 149 |
+
def is_infinite(obj, assumptions=None):
|
| 150 |
+
if assumptions is None:
|
| 151 |
+
return obj.is_infinite
|
| 152 |
+
return ask(Q.infinite(obj), assumptions)
|
| 153 |
+
|
| 154 |
+
|
| 155 |
+
def is_extended_real(obj, assumptions=None):
|
| 156 |
+
if assumptions is None:
|
| 157 |
+
return obj.is_extended_real
|
| 158 |
+
return ask(Q.extended_real(obj), assumptions)
|
| 159 |
+
|
| 160 |
+
|
| 161 |
+
def is_extended_nonnegative(obj, assumptions=None):
|
| 162 |
+
if assumptions is None:
|
| 163 |
+
return obj.is_extended_nonnegative
|
| 164 |
+
return ask(Q.extended_nonnegative(obj), assumptions)
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/__init__.cpython-312.pyc
ADDED
|
Binary file (2.64 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/bbp_pi.cpython-312.pyc
ADDED
|
Binary file (6.51 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/continued_fraction.cpython-312.pyc
ADDED
|
Binary file (13.1 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/digits.cpython-312.pyc
ADDED
|
Binary file (4.69 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/ecm.cpython-312.pyc
ADDED
|
Binary file (13.9 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/egyptian_fraction.cpython-312.pyc
ADDED
|
Binary file (8.48 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/elliptic_curve.cpython-312.pyc
ADDED
|
Binary file (19.3 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/factor_.cpython-312.pyc
ADDED
|
Binary file (84.2 kB). View file
|
|
|
lib/python3.12/site-packages/sympy/ntheory/__pycache__/generate.cpython-312.pyc
ADDED
|
Binary file (39.4 kB). View file
|
|
|