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