Spaces:
Sleeping
Sleeping
"""Module for querying SymPy objects about assumptions.""" | |
from sympy.assumptions.assume import (global_assumptions, Predicate, | |
AppliedPredicate) | |
from sympy.assumptions.cnf import CNF, EncodedCNF, Literal | |
from sympy.core import sympify | |
from sympy.core.kind import BooleanKind | |
from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le | |
from sympy.logic.inference import satisfiable | |
from sympy.utilities.decorator import memoize_property | |
from sympy.utilities.exceptions import (sympy_deprecation_warning, | |
SymPyDeprecationWarning, | |
ignore_warnings) | |
# Memoization is necessary for the properties of AssumptionKeys to | |
# ensure that only one object of Predicate objects are created. | |
# This is because assumption handlers are registered on those objects. | |
class AssumptionKeys: | |
""" | |
This class contains all the supported keys by ``ask``. | |
It should be accessed via the instance ``sympy.Q``. | |
""" | |
# DO NOT add methods or properties other than predicate keys. | |
# SAT solver checks the properties of Q and use them to compute the | |
# fact system. Non-predicate attributes will break this. | |
def hermitian(self): | |
from .handlers.sets import HermitianPredicate | |
return HermitianPredicate() | |
def antihermitian(self): | |
from .handlers.sets import AntihermitianPredicate | |
return AntihermitianPredicate() | |
def real(self): | |
from .handlers.sets import RealPredicate | |
return RealPredicate() | |
def extended_real(self): | |
from .handlers.sets import ExtendedRealPredicate | |
return ExtendedRealPredicate() | |
def imaginary(self): | |
from .handlers.sets import ImaginaryPredicate | |
return ImaginaryPredicate() | |
def complex(self): | |
from .handlers.sets import ComplexPredicate | |
return ComplexPredicate() | |
def algebraic(self): | |
from .handlers.sets import AlgebraicPredicate | |
return AlgebraicPredicate() | |
def transcendental(self): | |
from .predicates.sets import TranscendentalPredicate | |
return TranscendentalPredicate() | |
def integer(self): | |
from .handlers.sets import IntegerPredicate | |
return IntegerPredicate() | |
def noninteger(self): | |
from .predicates.sets import NonIntegerPredicate | |
return NonIntegerPredicate() | |
def rational(self): | |
from .handlers.sets import RationalPredicate | |
return RationalPredicate() | |
def irrational(self): | |
from .handlers.sets import IrrationalPredicate | |
return IrrationalPredicate() | |
def finite(self): | |
from .handlers.calculus import FinitePredicate | |
return FinitePredicate() | |
def infinite(self): | |
from .handlers.calculus import InfinitePredicate | |
return InfinitePredicate() | |
def positive_infinite(self): | |
from .handlers.calculus import PositiveInfinitePredicate | |
return PositiveInfinitePredicate() | |
def negative_infinite(self): | |
from .handlers.calculus import NegativeInfinitePredicate | |
return NegativeInfinitePredicate() | |
def positive(self): | |
from .handlers.order import PositivePredicate | |
return PositivePredicate() | |
def negative(self): | |
from .handlers.order import NegativePredicate | |
return NegativePredicate() | |
def zero(self): | |
from .handlers.order import ZeroPredicate | |
return ZeroPredicate() | |
def extended_positive(self): | |
from .handlers.order import ExtendedPositivePredicate | |
return ExtendedPositivePredicate() | |
def extended_negative(self): | |
from .handlers.order import ExtendedNegativePredicate | |
return ExtendedNegativePredicate() | |
def nonzero(self): | |
from .handlers.order import NonZeroPredicate | |
return NonZeroPredicate() | |
def nonpositive(self): | |
from .handlers.order import NonPositivePredicate | |
return NonPositivePredicate() | |
def nonnegative(self): | |
from .handlers.order import NonNegativePredicate | |
return NonNegativePredicate() | |
def extended_nonzero(self): | |
from .handlers.order import ExtendedNonZeroPredicate | |
return ExtendedNonZeroPredicate() | |
def extended_nonpositive(self): | |
from .handlers.order import ExtendedNonPositivePredicate | |
return ExtendedNonPositivePredicate() | |
def extended_nonnegative(self): | |
from .handlers.order import ExtendedNonNegativePredicate | |
return ExtendedNonNegativePredicate() | |
def even(self): | |
from .handlers.ntheory import EvenPredicate | |
return EvenPredicate() | |
def odd(self): | |
from .handlers.ntheory import OddPredicate | |
return OddPredicate() | |
def prime(self): | |
from .handlers.ntheory import PrimePredicate | |
return PrimePredicate() | |
def composite(self): | |
from .handlers.ntheory import CompositePredicate | |
return CompositePredicate() | |
def commutative(self): | |
from .handlers.common import CommutativePredicate | |
return CommutativePredicate() | |
def is_true(self): | |
from .handlers.common import IsTruePredicate | |
return IsTruePredicate() | |
def symmetric(self): | |
from .handlers.matrices import SymmetricPredicate | |
return SymmetricPredicate() | |
def invertible(self): | |
from .handlers.matrices import InvertiblePredicate | |
return InvertiblePredicate() | |
def orthogonal(self): | |
from .handlers.matrices import OrthogonalPredicate | |
return OrthogonalPredicate() | |
def unitary(self): | |
from .handlers.matrices import UnitaryPredicate | |
return UnitaryPredicate() | |
def positive_definite(self): | |
from .handlers.matrices import PositiveDefinitePredicate | |
return PositiveDefinitePredicate() | |
def upper_triangular(self): | |
from .handlers.matrices import UpperTriangularPredicate | |
return UpperTriangularPredicate() | |
def lower_triangular(self): | |
from .handlers.matrices import LowerTriangularPredicate | |
return LowerTriangularPredicate() | |
def diagonal(self): | |
from .handlers.matrices import DiagonalPredicate | |
return DiagonalPredicate() | |
def fullrank(self): | |
from .handlers.matrices import FullRankPredicate | |
return FullRankPredicate() | |
def square(self): | |
from .handlers.matrices import SquarePredicate | |
return SquarePredicate() | |
def integer_elements(self): | |
from .handlers.matrices import IntegerElementsPredicate | |
return IntegerElementsPredicate() | |
def real_elements(self): | |
from .handlers.matrices import RealElementsPredicate | |
return RealElementsPredicate() | |
def complex_elements(self): | |
from .handlers.matrices import ComplexElementsPredicate | |
return ComplexElementsPredicate() | |
def singular(self): | |
from .predicates.matrices import SingularPredicate | |
return SingularPredicate() | |
def normal(self): | |
from .predicates.matrices import NormalPredicate | |
return NormalPredicate() | |
def triangular(self): | |
from .predicates.matrices import TriangularPredicate | |
return TriangularPredicate() | |
def unit_triangular(self): | |
from .predicates.matrices import UnitTriangularPredicate | |
return UnitTriangularPredicate() | |
def eq(self): | |
from .relation.equality import EqualityPredicate | |
return EqualityPredicate() | |
def ne(self): | |
from .relation.equality import UnequalityPredicate | |
return UnequalityPredicate() | |
def gt(self): | |
from .relation.equality import StrictGreaterThanPredicate | |
return StrictGreaterThanPredicate() | |
def ge(self): | |
from .relation.equality import GreaterThanPredicate | |
return GreaterThanPredicate() | |
def lt(self): | |
from .relation.equality import StrictLessThanPredicate | |
return StrictLessThanPredicate() | |
def le(self): | |
from .relation.equality import LessThanPredicate | |
return LessThanPredicate() | |
Q = AssumptionKeys() | |
def _extract_all_facts(assump, exprs): | |
""" | |
Extract all relevant assumptions from *assump* with respect to given *exprs*. | |
Parameters | |
========== | |
assump : sympy.assumptions.cnf.CNF | |
exprs : tuple of expressions | |
Returns | |
======= | |
sympy.assumptions.cnf.CNF | |
Examples | |
======== | |
>>> from sympy import Q | |
>>> from sympy.assumptions.cnf import CNF | |
>>> from sympy.assumptions.ask import _extract_all_facts | |
>>> from sympy.abc import x, y | |
>>> assump = CNF.from_prop(Q.positive(x) & Q.integer(y)) | |
>>> exprs = (x,) | |
>>> cnf = _extract_all_facts(assump, exprs) | |
>>> cnf.clauses | |
{frozenset({Literal(Q.positive, False)})} | |
""" | |
facts = set() | |
for clause in assump.clauses: | |
args = [] | |
for literal in clause: | |
if isinstance(literal.lit, AppliedPredicate) and len(literal.lit.arguments) == 1: | |
if literal.lit.arg in exprs: | |
# Add literal if it has matching in it | |
args.append(Literal(literal.lit.function, literal.is_Not)) | |
else: | |
# If any of the literals doesn't have matching expr don't add the whole clause. | |
break | |
else: | |
# If any of the literals aren't unary predicate don't add the whole clause. | |
break | |
else: | |
if args: | |
facts.add(frozenset(args)) | |
return CNF(facts) | |
def ask(proposition, assumptions=True, context=global_assumptions): | |
""" | |
Function to evaluate the proposition with assumptions. | |
Explanation | |
=========== | |
This function evaluates the proposition to ``True`` or ``False`` if | |
the truth value can be determined. If not, it returns ``None``. | |
It should be discerned from :func:`~.refine()` which, when applied to a | |
proposition, simplifies the argument to symbolic ``Boolean`` instead of | |
Python built-in ``True``, ``False`` or ``None``. | |
**Syntax** | |
* ask(proposition) | |
Evaluate the *proposition* in global assumption context. | |
* ask(proposition, assumptions) | |
Evaluate the *proposition* with respect to *assumptions* in | |
global assumption context. | |
Parameters | |
========== | |
proposition : Boolean | |
Proposition which will be evaluated to boolean value. If this is | |
not ``AppliedPredicate``, it will be wrapped by ``Q.is_true``. | |
assumptions : Boolean, optional | |
Local assumptions to evaluate the *proposition*. | |
context : AssumptionsContext, optional | |
Default assumptions to evaluate the *proposition*. By default, | |
this is ``sympy.assumptions.global_assumptions`` variable. | |
Returns | |
======= | |
``True``, ``False``, or ``None`` | |
Raises | |
====== | |
TypeError : *proposition* or *assumptions* is not valid logical expression. | |
ValueError : assumptions are inconsistent. | |
Examples | |
======== | |
>>> from sympy import ask, Q, pi | |
>>> from sympy.abc import x, y | |
>>> ask(Q.rational(pi)) | |
False | |
>>> ask(Q.even(x*y), Q.even(x) & Q.integer(y)) | |
True | |
>>> ask(Q.prime(4*x), Q.integer(x)) | |
False | |
If the truth value cannot be determined, ``None`` will be returned. | |
>>> print(ask(Q.odd(3*x))) # cannot determine unless we know x | |
None | |
``ValueError`` is raised if assumptions are inconsistent. | |
>>> ask(Q.integer(x), Q.even(x) & Q.odd(x)) | |
Traceback (most recent call last): | |
... | |
ValueError: inconsistent assumptions Q.even(x) & Q.odd(x) | |
Notes | |
===== | |
Relations in assumptions are not implemented (yet), so the following | |
will not give a meaningful result. | |
>>> ask(Q.positive(x), x > 0) | |
It is however a work in progress. | |
See Also | |
======== | |
sympy.assumptions.refine.refine : Simplification using assumptions. | |
Proposition is not reduced to ``None`` if the truth value cannot | |
be determined. | |
""" | |
from sympy.assumptions.satask import satask | |
from sympy.assumptions.lra_satask import lra_satask | |
from sympy.logic.algorithms.lra_theory import UnhandledInput | |
proposition = sympify(proposition) | |
assumptions = sympify(assumptions) | |
if isinstance(proposition, Predicate) or proposition.kind is not BooleanKind: | |
raise TypeError("proposition must be a valid logical expression") | |
if isinstance(assumptions, Predicate) or assumptions.kind is not BooleanKind: | |
raise TypeError("assumptions must be a valid logical expression") | |
binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le} | |
if isinstance(proposition, AppliedPredicate): | |
key, args = proposition.function, proposition.arguments | |
elif proposition.func in binrelpreds: | |
key, args = binrelpreds[type(proposition)], proposition.args | |
else: | |
key, args = Q.is_true, (proposition,) | |
# convert local and global assumptions to CNF | |
assump_cnf = CNF.from_prop(assumptions) | |
assump_cnf.extend(context) | |
# extract the relevant facts from assumptions with respect to args | |
local_facts = _extract_all_facts(assump_cnf, args) | |
# convert default facts and assumed facts to encoded CNF | |
known_facts_cnf = get_all_known_facts() | |
enc_cnf = EncodedCNF() | |
enc_cnf.from_cnf(CNF(known_facts_cnf)) | |
enc_cnf.add_from_cnf(local_facts) | |
# check the satisfiability of given assumptions | |
if local_facts.clauses and satisfiable(enc_cnf) is False: | |
raise ValueError("inconsistent assumptions %s" % assumptions) | |
# quick computation for single fact | |
res = _ask_single_fact(key, local_facts) | |
if res is not None: | |
return res | |
# direct resolution method, no logic | |
res = key(*args)._eval_ask(assumptions) | |
if res is not None: | |
return bool(res) | |
# using satask (still costly) | |
res = satask(proposition, assumptions=assumptions, context=context) | |
if res is not None: | |
return res | |
try: | |
res = lra_satask(proposition, assumptions=assumptions, context=context) | |
except UnhandledInput: | |
return None | |
return res | |
def _ask_single_fact(key, local_facts): | |
""" | |
Compute the truth value of single predicate using assumptions. | |
Parameters | |
========== | |
key : sympy.assumptions.assume.Predicate | |
Proposition predicate. | |
local_facts : sympy.assumptions.cnf.CNF | |
Local assumption in CNF form. | |
Returns | |
======= | |
``True``, ``False`` or ``None`` | |
Examples | |
======== | |
>>> from sympy import Q | |
>>> from sympy.assumptions.cnf import CNF | |
>>> from sympy.assumptions.ask import _ask_single_fact | |
If prerequisite of proposition is rejected by the assumption, | |
return ``False``. | |
>>> key, assump = Q.zero, ~Q.zero | |
>>> local_facts = CNF.from_prop(assump) | |
>>> _ask_single_fact(key, local_facts) | |
False | |
>>> key, assump = Q.zero, ~Q.even | |
>>> local_facts = CNF.from_prop(assump) | |
>>> _ask_single_fact(key, local_facts) | |
False | |
If assumption implies the proposition, return ``True``. | |
>>> key, assump = Q.even, Q.zero | |
>>> local_facts = CNF.from_prop(assump) | |
>>> _ask_single_fact(key, local_facts) | |
True | |
If proposition rejects the assumption, return ``False``. | |
>>> key, assump = Q.even, Q.odd | |
>>> local_facts = CNF.from_prop(assump) | |
>>> _ask_single_fact(key, local_facts) | |
False | |
""" | |
if local_facts.clauses: | |
known_facts_dict = get_known_facts_dict() | |
if len(local_facts.clauses) == 1: | |
cl, = local_facts.clauses | |
if len(cl) == 1: | |
f, = cl | |
prop_facts = known_facts_dict.get(key, None) | |
prop_req = prop_facts[0] if prop_facts is not None else set() | |
if f.is_Not and f.arg in prop_req: | |
# the prerequisite of proposition is rejected | |
return False | |
for clause in local_facts.clauses: | |
if len(clause) == 1: | |
f, = clause | |
prop_facts = known_facts_dict.get(f.arg, None) if not f.is_Not else None | |
if prop_facts is None: | |
continue | |
prop_req, prop_rej = prop_facts | |
if key in prop_req: | |
# assumption implies the proposition | |
return True | |
elif key in prop_rej: | |
# proposition rejects the assumption | |
return False | |
return None | |
def register_handler(key, handler): | |
""" | |
Register a handler in the ask system. key must be a string and handler a | |
class inheriting from AskHandler. | |
.. deprecated:: 1.8. | |
Use multipledispatch handler instead. See :obj:`~.Predicate`. | |
""" | |
sympy_deprecation_warning( | |
""" | |
The AskHandler system is deprecated. The register_handler() function | |
should be replaced with the multipledispatch handler of Predicate. | |
""", | |
deprecated_since_version="1.8", | |
active_deprecations_target='deprecated-askhandler', | |
) | |
if isinstance(key, Predicate): | |
key = key.name.name | |
Qkey = getattr(Q, key, None) | |
if Qkey is not None: | |
Qkey.add_handler(handler) | |
else: | |
setattr(Q, key, Predicate(key, handlers=[handler])) | |
def remove_handler(key, handler): | |
""" | |
Removes a handler from the ask system. | |
.. deprecated:: 1.8. | |
Use multipledispatch handler instead. See :obj:`~.Predicate`. | |
""" | |
sympy_deprecation_warning( | |
""" | |
The AskHandler system is deprecated. The remove_handler() function | |
should be replaced with the multipledispatch handler of Predicate. | |
""", | |
deprecated_since_version="1.8", | |
active_deprecations_target='deprecated-askhandler', | |
) | |
if isinstance(key, Predicate): | |
key = key.name.name | |
# Don't show the same warning again recursively | |
with ignore_warnings(SymPyDeprecationWarning): | |
getattr(Q, key).remove_handler(handler) | |
from sympy.assumptions.ask_generated import (get_all_known_facts, | |
get_known_facts_dict) | |