I am done

This commit is contained in:
2024-10-30 22:14:35 +01:00
parent 720dc28c09
commit 40e2a747cf
36901 changed files with 5011519 additions and 0 deletions

View File

@ -0,0 +1,13 @@
"""
A module to implement finitary relations [1] as predicate.
References
==========
.. [1] https://en.wikipedia.org/wiki/Finitary_relation
"""
__all__ = ['BinaryRelation', 'AppliedBinaryRelation']
from .binrel import BinaryRelation, AppliedBinaryRelation

View File

@ -0,0 +1,212 @@
"""
General binary relations.
"""
from typing import Optional
from sympy.core.singleton import S
from sympy.assumptions import AppliedPredicate, ask, Predicate, Q # type: ignore
from sympy.core.kind import BooleanKind
from sympy.core.relational import Eq, Ne, Gt, Lt, Ge, Le
from sympy.logic.boolalg import conjuncts, Not
__all__ = ["BinaryRelation", "AppliedBinaryRelation"]
class BinaryRelation(Predicate):
"""
Base class for all binary relational predicates.
Explanation
===========
Binary relation takes two arguments and returns ``AppliedBinaryRelation``
instance. To evaluate it to boolean value, use :obj:`~.ask()` or
:obj:`~.refine()` function.
You can add support for new types by registering the handler to dispatcher.
See :obj:`~.Predicate()` for more information about predicate dispatching.
Examples
========
Applying and evaluating to boolean value:
>>> from sympy import Q, ask, sin, cos
>>> from sympy.abc import x
>>> Q.eq(sin(x)**2+cos(x)**2, 1)
Q.eq(sin(x)**2 + cos(x)**2, 1)
>>> ask(_)
True
You can define a new binary relation by subclassing and dispatching.
Here, we define a relation $R$ such that $x R y$ returns true if
$x = y + 1$.
>>> from sympy import ask, Number, Q
>>> from sympy.assumptions import BinaryRelation
>>> class MyRel(BinaryRelation):
... name = "R"
... is_reflexive = False
>>> Q.R = MyRel()
>>> @Q.R.register(Number, Number)
... def _(n1, n2, assumptions):
... return ask(Q.zero(n1 - n2 - 1), assumptions)
>>> Q.R(2, 1)
Q.R(2, 1)
Now, we can use ``ask()`` to evaluate it to boolean value.
>>> ask(Q.R(2, 1))
True
>>> ask(Q.R(1, 2))
False
``Q.R`` returns ``False`` with minimum cost if two arguments have same
structure because it is antireflexive relation [1] by
``is_reflexive = False``.
>>> ask(Q.R(x, x))
False
References
==========
.. [1] https://en.wikipedia.org/wiki/Reflexive_relation
"""
is_reflexive: Optional[bool] = None
is_symmetric: Optional[bool] = None
def __call__(self, *args):
if not len(args) == 2:
raise ValueError("Binary relation takes two arguments, but got %s." % len(args))
return AppliedBinaryRelation(self, *args)
@property
def reversed(self):
if self.is_symmetric:
return self
return None
@property
def negated(self):
return None
def _compare_reflexive(self, lhs, rhs):
# quick exit for structurally same arguments
# do not check != here because it cannot catch the
# equivalent arguments with different structures.
# reflexivity does not hold to NaN
if lhs is S.NaN or rhs is S.NaN:
return None
reflexive = self.is_reflexive
if reflexive is None:
pass
elif reflexive and (lhs == rhs):
return True
elif not reflexive and (lhs == rhs):
return False
return None
def eval(self, args, assumptions=True):
# quick exit for structurally same arguments
ret = self._compare_reflexive(*args)
if ret is not None:
return ret
# don't perform simplify on args here. (done by AppliedBinaryRelation._eval_ask)
# evaluate by multipledispatch
lhs, rhs = args
ret = self.handler(lhs, rhs, assumptions=assumptions)
if ret is not None:
return ret
# check reversed order if the relation is reflexive
if self.is_reflexive:
types = (type(lhs), type(rhs))
if self.handler.dispatch(*types) is not self.handler.dispatch(*reversed(types)):
ret = self.handler(rhs, lhs, assumptions=assumptions)
return ret
class AppliedBinaryRelation(AppliedPredicate):
"""
The class of expressions resulting from applying ``BinaryRelation``
to the arguments.
"""
@property
def lhs(self):
"""The left-hand side of the relation."""
return self.arguments[0]
@property
def rhs(self):
"""The right-hand side of the relation."""
return self.arguments[1]
@property
def reversed(self):
"""
Try to return the relationship with sides reversed.
"""
revfunc = self.function.reversed
if revfunc is None:
return self
return revfunc(self.rhs, self.lhs)
@property
def reversedsign(self):
"""
Try to return the relationship with signs reversed.
"""
revfunc = self.function.reversed
if revfunc is None:
return self
if not any(side.kind is BooleanKind for side in self.arguments):
return revfunc(-self.lhs, -self.rhs)
return self
@property
def negated(self):
neg_rel = self.function.negated
if neg_rel is None:
return Not(self, evaluate=False)
return neg_rel(*self.arguments)
def _eval_ask(self, assumptions):
conj_assumps = set()
binrelpreds = {Eq: Q.eq, Ne: Q.ne, Gt: Q.gt, Lt: Q.lt, Ge: Q.ge, Le: Q.le}
for a in conjuncts(assumptions):
if a.func in binrelpreds:
conj_assumps.add(binrelpreds[type(a)](*a.args))
else:
conj_assumps.add(a)
# After CNF in assumptions module is modified to take polyadic
# predicate, this will be removed
if any(rel in conj_assumps for rel in (self, self.reversed)):
return True
neg_rels = (self.negated, self.reversed.negated, Not(self, evaluate=False),
Not(self.reversed, evaluate=False))
if any(rel in conj_assumps for rel in neg_rels):
return False
# evaluation using multipledispatching
ret = self.function.eval(self.arguments, assumptions)
if ret is not None:
return ret
# simplify the args and try again
args = tuple(a.simplify() for a in self.arguments)
return self.function.eval(args, assumptions)
def __bool__(self):
ret = ask(self)
if ret is None:
raise TypeError("Cannot determine truth value of %s" % self)
return ret

View File

@ -0,0 +1,302 @@
"""
Module for mathematical equality [1] and inequalities [2].
The purpose of this module is to provide the instances which represent the
binary predicates in order to combine the relationals into logical inference
system. Objects such as ``Q.eq``, ``Q.lt`` should remain internal to
assumptions module, and user must use the classes such as :obj:`~.Eq()`,
:obj:`~.Lt()` instead to construct the relational expressions.
References
==========
.. [1] https://en.wikipedia.org/wiki/Equality_(mathematics)
.. [2] https://en.wikipedia.org/wiki/Inequality_(mathematics)
"""
from sympy.assumptions import Q
from sympy.core.relational import is_eq, is_neq, is_gt, is_ge, is_lt, is_le
from .binrel import BinaryRelation
__all__ = ['EqualityPredicate', 'UnequalityPredicate', 'StrictGreaterThanPredicate',
'GreaterThanPredicate', 'StrictLessThanPredicate', 'LessThanPredicate']
class EqualityPredicate(BinaryRelation):
"""
Binary predicate for $=$.
The purpose of this class is to provide the instance which represent
the equality predicate in order to allow the logical inference.
This class must remain internal to assumptions module and user must
use :obj:`~.Eq()` instead to construct the equality expression.
Evaluating this predicate to ``True`` or ``False`` is done by
:func:`~.core.relational.is_eq()`
Examples
========
>>> from sympy import ask, Q
>>> Q.eq(0, 0)
Q.eq(0, 0)
>>> ask(_)
True
See Also
========
sympy.core.relational.Eq
"""
is_reflexive = True
is_symmetric = True
name = 'eq'
handler = None # Do not allow dispatching by this predicate
@property
def negated(self):
return Q.ne
def eval(self, args, assumptions=True):
if assumptions == True:
# default assumptions for is_eq is None
assumptions = None
return is_eq(*args, assumptions)
class UnequalityPredicate(BinaryRelation):
r"""
Binary predicate for $\neq$.
The purpose of this class is to provide the instance which represent
the inequation predicate in order to allow the logical inference.
This class must remain internal to assumptions module and user must
use :obj:`~.Ne()` instead to construct the inequation expression.
Evaluating this predicate to ``True`` or ``False`` is done by
:func:`~.core.relational.is_neq()`
Examples
========
>>> from sympy import ask, Q
>>> Q.ne(0, 0)
Q.ne(0, 0)
>>> ask(_)
False
See Also
========
sympy.core.relational.Ne
"""
is_reflexive = False
is_symmetric = True
name = 'ne'
handler = None
@property
def negated(self):
return Q.eq
def eval(self, args, assumptions=True):
if assumptions == True:
# default assumptions for is_neq is None
assumptions = None
return is_neq(*args, assumptions)
class StrictGreaterThanPredicate(BinaryRelation):
"""
Binary predicate for $>$.
The purpose of this class is to provide the instance which represent
the ">" predicate in order to allow the logical inference.
This class must remain internal to assumptions module and user must
use :obj:`~.Gt()` instead to construct the equality expression.
Evaluating this predicate to ``True`` or ``False`` is done by
:func:`~.core.relational.is_gt()`
Examples
========
>>> from sympy import ask, Q
>>> Q.gt(0, 0)
Q.gt(0, 0)
>>> ask(_)
False
See Also
========
sympy.core.relational.Gt
"""
is_reflexive = False
is_symmetric = False
name = 'gt'
handler = None
@property
def reversed(self):
return Q.lt
@property
def negated(self):
return Q.le
def eval(self, args, assumptions=True):
if assumptions == True:
# default assumptions for is_gt is None
assumptions = None
return is_gt(*args, assumptions)
class GreaterThanPredicate(BinaryRelation):
"""
Binary predicate for $>=$.
The purpose of this class is to provide the instance which represent
the ">=" predicate in order to allow the logical inference.
This class must remain internal to assumptions module and user must
use :obj:`~.Ge()` instead to construct the equality expression.
Evaluating this predicate to ``True`` or ``False`` is done by
:func:`~.core.relational.is_ge()`
Examples
========
>>> from sympy import ask, Q
>>> Q.ge(0, 0)
Q.ge(0, 0)
>>> ask(_)
True
See Also
========
sympy.core.relational.Ge
"""
is_reflexive = True
is_symmetric = False
name = 'ge'
handler = None
@property
def reversed(self):
return Q.le
@property
def negated(self):
return Q.lt
def eval(self, args, assumptions=True):
if assumptions == True:
# default assumptions for is_ge is None
assumptions = None
return is_ge(*args, assumptions)
class StrictLessThanPredicate(BinaryRelation):
"""
Binary predicate for $<$.
The purpose of this class is to provide the instance which represent
the "<" predicate in order to allow the logical inference.
This class must remain internal to assumptions module and user must
use :obj:`~.Lt()` instead to construct the equality expression.
Evaluating this predicate to ``True`` or ``False`` is done by
:func:`~.core.relational.is_lt()`
Examples
========
>>> from sympy import ask, Q
>>> Q.lt(0, 0)
Q.lt(0, 0)
>>> ask(_)
False
See Also
========
sympy.core.relational.Lt
"""
is_reflexive = False
is_symmetric = False
name = 'lt'
handler = None
@property
def reversed(self):
return Q.gt
@property
def negated(self):
return Q.ge
def eval(self, args, assumptions=True):
if assumptions == True:
# default assumptions for is_lt is None
assumptions = None
return is_lt(*args, assumptions)
class LessThanPredicate(BinaryRelation):
"""
Binary predicate for $<=$.
The purpose of this class is to provide the instance which represent
the "<=" predicate in order to allow the logical inference.
This class must remain internal to assumptions module and user must
use :obj:`~.Le()` instead to construct the equality expression.
Evaluating this predicate to ``True`` or ``False`` is done by
:func:`~.core.relational.is_le()`
Examples
========
>>> from sympy import ask, Q
>>> Q.le(0, 0)
Q.le(0, 0)
>>> ask(_)
True
See Also
========
sympy.core.relational.Le
"""
is_reflexive = True
is_symmetric = False
name = 'le'
handler = None
@property
def reversed(self):
return Q.ge
@property
def negated(self):
return Q.gt
def eval(self, args, assumptions=True):
if assumptions == True:
# default assumptions for is_le is None
assumptions = None
return is_le(*args, assumptions)