123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632 |
- """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.
- @memoize_property
- def hermitian(self):
- from .handlers.sets import HermitianPredicate
- return HermitianPredicate()
- @memoize_property
- def antihermitian(self):
- from .handlers.sets import AntihermitianPredicate
- return AntihermitianPredicate()
- @memoize_property
- def real(self):
- from .handlers.sets import RealPredicate
- return RealPredicate()
- @memoize_property
- def extended_real(self):
- from .handlers.sets import ExtendedRealPredicate
- return ExtendedRealPredicate()
- @memoize_property
- def imaginary(self):
- from .handlers.sets import ImaginaryPredicate
- return ImaginaryPredicate()
- @memoize_property
- def complex(self):
- from .handlers.sets import ComplexPredicate
- return ComplexPredicate()
- @memoize_property
- def algebraic(self):
- from .handlers.sets import AlgebraicPredicate
- return AlgebraicPredicate()
- @memoize_property
- def transcendental(self):
- from .predicates.sets import TranscendentalPredicate
- return TranscendentalPredicate()
- @memoize_property
- def integer(self):
- from .handlers.sets import IntegerPredicate
- return IntegerPredicate()
- @memoize_property
- def rational(self):
- from .handlers.sets import RationalPredicate
- return RationalPredicate()
- @memoize_property
- def irrational(self):
- from .handlers.sets import IrrationalPredicate
- return IrrationalPredicate()
- @memoize_property
- def finite(self):
- from .handlers.calculus import FinitePredicate
- return FinitePredicate()
- @memoize_property
- def infinite(self):
- from .handlers.calculus import InfinitePredicate
- return InfinitePredicate()
- @memoize_property
- def positive_infinite(self):
- from .handlers.calculus import PositiveInfinitePredicate
- return PositiveInfinitePredicate()
- @memoize_property
- def negative_infinite(self):
- from .handlers.calculus import NegativeInfinitePredicate
- return NegativeInfinitePredicate()
- @memoize_property
- def positive(self):
- from .handlers.order import PositivePredicate
- return PositivePredicate()
- @memoize_property
- def negative(self):
- from .handlers.order import NegativePredicate
- return NegativePredicate()
- @memoize_property
- def zero(self):
- from .handlers.order import ZeroPredicate
- return ZeroPredicate()
- @memoize_property
- def extended_positive(self):
- from .handlers.order import ExtendedPositivePredicate
- return ExtendedPositivePredicate()
- @memoize_property
- def extended_negative(self):
- from .handlers.order import ExtendedNegativePredicate
- return ExtendedNegativePredicate()
- @memoize_property
- def nonzero(self):
- from .handlers.order import NonZeroPredicate
- return NonZeroPredicate()
- @memoize_property
- def nonpositive(self):
- from .handlers.order import NonPositivePredicate
- return NonPositivePredicate()
- @memoize_property
- def nonnegative(self):
- from .handlers.order import NonNegativePredicate
- return NonNegativePredicate()
- @memoize_property
- def extended_nonzero(self):
- from .handlers.order import ExtendedNonZeroPredicate
- return ExtendedNonZeroPredicate()
- @memoize_property
- def extended_nonpositive(self):
- from .handlers.order import ExtendedNonPositivePredicate
- return ExtendedNonPositivePredicate()
- @memoize_property
- def extended_nonnegative(self):
- from .handlers.order import ExtendedNonNegativePredicate
- return ExtendedNonNegativePredicate()
- @memoize_property
- def even(self):
- from .handlers.ntheory import EvenPredicate
- return EvenPredicate()
- @memoize_property
- def odd(self):
- from .handlers.ntheory import OddPredicate
- return OddPredicate()
- @memoize_property
- def prime(self):
- from .handlers.ntheory import PrimePredicate
- return PrimePredicate()
- @memoize_property
- def composite(self):
- from .handlers.ntheory import CompositePredicate
- return CompositePredicate()
- @memoize_property
- def commutative(self):
- from .handlers.common import CommutativePredicate
- return CommutativePredicate()
- @memoize_property
- def is_true(self):
- from .handlers.common import IsTruePredicate
- return IsTruePredicate()
- @memoize_property
- def symmetric(self):
- from .handlers.matrices import SymmetricPredicate
- return SymmetricPredicate()
- @memoize_property
- def invertible(self):
- from .handlers.matrices import InvertiblePredicate
- return InvertiblePredicate()
- @memoize_property
- def orthogonal(self):
- from .handlers.matrices import OrthogonalPredicate
- return OrthogonalPredicate()
- @memoize_property
- def unitary(self):
- from .handlers.matrices import UnitaryPredicate
- return UnitaryPredicate()
- @memoize_property
- def positive_definite(self):
- from .handlers.matrices import PositiveDefinitePredicate
- return PositiveDefinitePredicate()
- @memoize_property
- def upper_triangular(self):
- from .handlers.matrices import UpperTriangularPredicate
- return UpperTriangularPredicate()
- @memoize_property
- def lower_triangular(self):
- from .handlers.matrices import LowerTriangularPredicate
- return LowerTriangularPredicate()
- @memoize_property
- def diagonal(self):
- from .handlers.matrices import DiagonalPredicate
- return DiagonalPredicate()
- @memoize_property
- def fullrank(self):
- from .handlers.matrices import FullRankPredicate
- return FullRankPredicate()
- @memoize_property
- def square(self):
- from .handlers.matrices import SquarePredicate
- return SquarePredicate()
- @memoize_property
- def integer_elements(self):
- from .handlers.matrices import IntegerElementsPredicate
- return IntegerElementsPredicate()
- @memoize_property
- def real_elements(self):
- from .handlers.matrices import RealElementsPredicate
- return RealElementsPredicate()
- @memoize_property
- def complex_elements(self):
- from .handlers.matrices import ComplexElementsPredicate
- return ComplexElementsPredicate()
- @memoize_property
- def singular(self):
- from .predicates.matrices import SingularPredicate
- return SingularPredicate()
- @memoize_property
- def normal(self):
- from .predicates.matrices import NormalPredicate
- return NormalPredicate()
- @memoize_property
- def triangular(self):
- from .predicates.matrices import TriangularPredicate
- return TriangularPredicate()
- @memoize_property
- def unit_triangular(self):
- from .predicates.matrices import UnitTriangularPredicate
- return UnitTriangularPredicate()
- @memoize_property
- def eq(self):
- from .relation.equality import EqualityPredicate
- return EqualityPredicate()
- @memoize_property
- def ne(self):
- from .relation.equality import UnequalityPredicate
- return UnequalityPredicate()
- @memoize_property
- def gt(self):
- from .relation.equality import StrictGreaterThanPredicate
- return StrictGreaterThanPredicate()
- @memoize_property
- def ge(self):
- from .relation.equality import GreaterThanPredicate
- return GreaterThanPredicate()
- @memoize_property
- def lt(self):
- from .relation.equality import StrictLessThanPredicate
- return StrictLessThanPredicate()
- @memoize_property
- 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 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 : Any boolean expression.
- Proposition which will be evaluated to boolean value. If this is
- not ``AppliedPredicate``, it will be wrapped by ``Q.is_true``.
- assumptions : Any boolean expression, 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
- 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)
- 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. Same syntax as register_handler
- .. 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)
|