123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587 |
- from __future__ import annotations
- from .basic import Atom, Basic
- from .sorting import ordered
- from .evalf import EvalfMixin
- from .function import AppliedUndef
- from .singleton import S
- from .sympify import _sympify, SympifyError
- from .parameters import global_parameters
- from .logic import fuzzy_bool, fuzzy_xor, fuzzy_and, fuzzy_not
- from sympy.logic.boolalg import Boolean, BooleanAtom
- from sympy.utilities.iterables import sift
- from sympy.utilities.misc import filldedent
- __all__ = (
- 'Rel', 'Eq', 'Ne', 'Lt', 'Le', 'Gt', 'Ge',
- 'Relational', 'Equality', 'Unequality', 'StrictLessThan', 'LessThan',
- 'StrictGreaterThan', 'GreaterThan',
- )
- from .expr import Expr
- from sympy.multipledispatch import dispatch
- from .containers import Tuple
- from .symbol import Symbol
- def _nontrivBool(side):
- return isinstance(side, Boolean) and \
- not isinstance(side, Atom)
- # Note, see issue 4986. Ideally, we wouldn't want to subclass both Boolean
- # and Expr.
- # from .. import Expr
- def _canonical(cond):
- # return a condition in which all relationals are canonical
- reps = {r: r.canonical for r in cond.atoms(Relational)}
- return cond.xreplace(reps)
- # XXX: AttributeError was being caught here but it wasn't triggered by any of
- # the tests so I've removed it...
- def _canonical_coeff(rel):
- # return -2*x + 1 < 0 as x > 1/2
- # XXX make this part of Relational.canonical?
- rel = rel.canonical
- if not rel.is_Relational or rel.rhs.is_Boolean:
- return rel # Eq(x, True)
- b, l = rel.lhs.as_coeff_Add(rational=True)
- m, lhs = l.as_coeff_Mul(rational=True)
- rhs = (rel.rhs - b)/m
- if m < 0:
- return rel.reversed.func(lhs, rhs)
- return rel.func(lhs, rhs)
- class Relational(Boolean, EvalfMixin):
- """Base class for all relation types.
- Explanation
- ===========
- Subclasses of Relational should generally be instantiated directly, but
- Relational can be instantiated with a valid ``rop`` value to dispatch to
- the appropriate subclass.
- Parameters
- ==========
- rop : str or None
- Indicates what subclass to instantiate. Valid values can be found
- in the keys of Relational.ValidRelationOperator.
- Examples
- ========
- >>> from sympy import Rel
- >>> from sympy.abc import x, y
- >>> Rel(y, x + x**2, '==')
- Eq(y, x**2 + x)
- A relation's type can be defined upon creation using ``rop``.
- The relation type of an existing expression can be obtained
- using its ``rel_op`` property.
- Here is a table of all the relation types, along with their
- ``rop`` and ``rel_op`` values:
- +---------------------+----------------------------+------------+
- |Relation |``rop`` |``rel_op`` |
- +=====================+============================+============+
- |``Equality`` |``==`` or ``eq`` or ``None``|``==`` |
- +---------------------+----------------------------+------------+
- |``Unequality`` |``!=`` or ``ne`` |``!=`` |
- +---------------------+----------------------------+------------+
- |``GreaterThan`` |``>=`` or ``ge`` |``>=`` |
- +---------------------+----------------------------+------------+
- |``LessThan`` |``<=`` or ``le`` |``<=`` |
- +---------------------+----------------------------+------------+
- |``StrictGreaterThan``|``>`` or ``gt`` |``>`` |
- +---------------------+----------------------------+------------+
- |``StrictLessThan`` |``<`` or ``lt`` |``<`` |
- +---------------------+----------------------------+------------+
- For example, setting ``rop`` to ``==`` produces an
- ``Equality`` relation, ``Eq()``.
- So does setting ``rop`` to ``eq``, or leaving ``rop`` unspecified.
- That is, the first three ``Rel()`` below all produce the same result.
- Using a ``rop`` from a different row in the table produces a
- different relation type.
- For example, the fourth ``Rel()`` below using ``lt`` for ``rop``
- produces a ``StrictLessThan`` inequality:
- >>> from sympy import Rel
- >>> from sympy.abc import x, y
- >>> Rel(y, x + x**2, '==')
- Eq(y, x**2 + x)
- >>> Rel(y, x + x**2, 'eq')
- Eq(y, x**2 + x)
- >>> Rel(y, x + x**2)
- Eq(y, x**2 + x)
- >>> Rel(y, x + x**2, 'lt')
- y < x**2 + x
- To obtain the relation type of an existing expression,
- get its ``rel_op`` property.
- For example, ``rel_op`` is ``==`` for the ``Equality`` relation above,
- and ``<`` for the strict less than inequality above:
- >>> from sympy import Rel
- >>> from sympy.abc import x, y
- >>> my_equality = Rel(y, x + x**2, '==')
- >>> my_equality.rel_op
- '=='
- >>> my_inequality = Rel(y, x + x**2, 'lt')
- >>> my_inequality.rel_op
- '<'
- """
- __slots__ = ()
- ValidRelationOperator: dict[str | None, type[Relational]] = {}
- is_Relational = True
- # ValidRelationOperator - Defined below, because the necessary classes
- # have not yet been defined
- def __new__(cls, lhs, rhs, rop=None, **assumptions):
- # If called by a subclass, do nothing special and pass on to Basic.
- if cls is not Relational:
- return Basic.__new__(cls, lhs, rhs, **assumptions)
- # XXX: Why do this? There should be a separate function to make a
- # particular subclass of Relational from a string.
- #
- # If called directly with an operator, look up the subclass
- # corresponding to that operator and delegate to it
- cls = cls.ValidRelationOperator.get(rop, None)
- if cls is None:
- raise ValueError("Invalid relational operator symbol: %r" % rop)
- if not issubclass(cls, (Eq, Ne)):
- # validate that Booleans are not being used in a relational
- # other than Eq/Ne;
- # Note: Symbol is a subclass of Boolean but is considered
- # acceptable here.
- if any(map(_nontrivBool, (lhs, rhs))):
- raise TypeError(filldedent('''
- A Boolean argument can only be used in
- Eq and Ne; all other relationals expect
- real expressions.
- '''))
- return cls(lhs, rhs, **assumptions)
- @property
- def lhs(self):
- """The left-hand side of the relation."""
- return self._args[0]
- @property
- def rhs(self):
- """The right-hand side of the relation."""
- return self._args[1]
- @property
- def reversed(self):
- """Return the relationship with sides reversed.
- Examples
- ========
- >>> from sympy import Eq
- >>> from sympy.abc import x
- >>> Eq(x, 1)
- Eq(x, 1)
- >>> _.reversed
- Eq(1, x)
- >>> x < 1
- x < 1
- >>> _.reversed
- 1 > x
- """
- ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne}
- a, b = self.args
- return Relational.__new__(ops.get(self.func, self.func), b, a)
- @property
- def reversedsign(self):
- """Return the relationship with signs reversed.
- Examples
- ========
- >>> from sympy import Eq
- >>> from sympy.abc import x
- >>> Eq(x, 1)
- Eq(x, 1)
- >>> _.reversedsign
- Eq(-x, -1)
- >>> x < 1
- x < 1
- >>> _.reversedsign
- -x > -1
- """
- a, b = self.args
- if not (isinstance(a, BooleanAtom) or isinstance(b, BooleanAtom)):
- ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne}
- return Relational.__new__(ops.get(self.func, self.func), -a, -b)
- else:
- return self
- @property
- def negated(self):
- """Return the negated relationship.
- Examples
- ========
- >>> from sympy import Eq
- >>> from sympy.abc import x
- >>> Eq(x, 1)
- Eq(x, 1)
- >>> _.negated
- Ne(x, 1)
- >>> x < 1
- x < 1
- >>> _.negated
- x >= 1
- Notes
- =====
- This works more or less identical to ``~``/``Not``. The difference is
- that ``negated`` returns the relationship even if ``evaluate=False``.
- Hence, this is useful in code when checking for e.g. negated relations
- to existing ones as it will not be affected by the `evaluate` flag.
- """
- ops = {Eq: Ne, Ge: Lt, Gt: Le, Le: Gt, Lt: Ge, Ne: Eq}
- # If there ever will be new Relational subclasses, the following line
- # will work until it is properly sorted out
- # return ops.get(self.func, lambda a, b, evaluate=False: ~(self.func(a,
- # b, evaluate=evaluate)))(*self.args, evaluate=False)
- return Relational.__new__(ops.get(self.func), *self.args)
- @property
- def weak(self):
- """return the non-strict version of the inequality or self
- EXAMPLES
- ========
- >>> from sympy.abc import x
- >>> (x < 1).weak
- x <= 1
- >>> _.weak
- x <= 1
- """
- return self
- @property
- def strict(self):
- """return the strict version of the inequality or self
- EXAMPLES
- ========
- >>> from sympy.abc import x
- >>> (x <= 1).strict
- x < 1
- >>> _.strict
- x < 1
- """
- return self
- def _eval_evalf(self, prec):
- return self.func(*[s._evalf(prec) for s in self.args])
- @property
- def canonical(self):
- """Return a canonical form of the relational by putting a
- number on the rhs, canonically removing a sign or else
- ordering the args canonically. No other simplification is
- attempted.
- Examples
- ========
- >>> from sympy.abc import x, y
- >>> x < 2
- x < 2
- >>> _.reversed.canonical
- x < 2
- >>> (-y < x).canonical
- x > -y
- >>> (-y > x).canonical
- x < -y
- >>> (-y < -x).canonical
- x < y
- The canonicalization is recursively applied:
- >>> from sympy import Eq
- >>> Eq(x < y, y > x).canonical
- True
- """
- args = tuple([i.canonical if isinstance(i, Relational) else i for i in self.args])
- if args != self.args:
- r = self.func(*args)
- if not isinstance(r, Relational):
- return r
- else:
- r = self
- if r.rhs.is_number:
- if r.rhs.is_Number and r.lhs.is_Number and r.lhs > r.rhs:
- r = r.reversed
- elif r.lhs.is_number:
- r = r.reversed
- elif tuple(ordered(args)) != args:
- r = r.reversed
- LHS_CEMS = getattr(r.lhs, 'could_extract_minus_sign', None)
- RHS_CEMS = getattr(r.rhs, 'could_extract_minus_sign', None)
- if isinstance(r.lhs, BooleanAtom) or isinstance(r.rhs, BooleanAtom):
- return r
- # Check if first value has negative sign
- if LHS_CEMS and LHS_CEMS():
- return r.reversedsign
- elif not r.rhs.is_number and RHS_CEMS and RHS_CEMS():
- # Right hand side has a minus, but not lhs.
- # How does the expression with reversed signs behave?
- # This is so that expressions of the type
- # Eq(x, -y) and Eq(-x, y)
- # have the same canonical representation
- expr1, _ = ordered([r.lhs, -r.rhs])
- if expr1 != r.lhs:
- return r.reversed.reversedsign
- return r
- def equals(self, other, failing_expression=False):
- """Return True if the sides of the relationship are mathematically
- identical and the type of relationship is the same.
- If failing_expression is True, return the expression whose truth value
- was unknown."""
- if isinstance(other, Relational):
- if other in (self, self.reversed):
- return True
- a, b = self, other
- if a.func in (Eq, Ne) or b.func in (Eq, Ne):
- if a.func != b.func:
- return False
- left, right = [i.equals(j,
- failing_expression=failing_expression)
- for i, j in zip(a.args, b.args)]
- if left is True:
- return right
- if right is True:
- return left
- lr, rl = [i.equals(j, failing_expression=failing_expression)
- for i, j in zip(a.args, b.reversed.args)]
- if lr is True:
- return rl
- if rl is True:
- return lr
- e = (left, right, lr, rl)
- if all(i is False for i in e):
- return False
- for i in e:
- if i not in (True, False):
- return i
- else:
- if b.func != a.func:
- b = b.reversed
- if a.func != b.func:
- return False
- left = a.lhs.equals(b.lhs,
- failing_expression=failing_expression)
- if left is False:
- return False
- right = a.rhs.equals(b.rhs,
- failing_expression=failing_expression)
- if right is False:
- return False
- if left is True:
- return right
- return left
- def _eval_simplify(self, **kwargs):
- from .add import Add
- from .expr import Expr
- r = self
- r = r.func(*[i.simplify(**kwargs) for i in r.args])
- if r.is_Relational:
- if not isinstance(r.lhs, Expr) or not isinstance(r.rhs, Expr):
- return r
- dif = r.lhs - r.rhs
- # replace dif with a valid Number that will
- # allow a definitive comparison with 0
- v = None
- if dif.is_comparable:
- v = dif.n(2)
- elif dif.equals(0): # XXX this is expensive
- v = S.Zero
- if v is not None:
- r = r.func._eval_relation(v, S.Zero)
- r = r.canonical
- # If there is only one symbol in the expression,
- # try to write it on a simplified form
- free = list(filter(lambda x: x.is_real is not False, r.free_symbols))
- if len(free) == 1:
- try:
- from sympy.solvers.solveset import linear_coeffs
- x = free.pop()
- dif = r.lhs - r.rhs
- m, b = linear_coeffs(dif, x)
- if m.is_zero is False:
- if m.is_negative:
- # Dividing with a negative number, so change order of arguments
- # canonical will put the symbol back on the lhs later
- r = r.func(-b / m, x)
- else:
- r = r.func(x, -b / m)
- else:
- r = r.func(b, S.Zero)
- except ValueError:
- # maybe not a linear function, try polynomial
- from sympy.polys.polyerrors import PolynomialError
- from sympy.polys.polytools import gcd, Poly, poly
- try:
- p = poly(dif, x)
- c = p.all_coeffs()
- constant = c[-1]
- c[-1] = 0
- scale = gcd(c)
- c = [ctmp / scale for ctmp in c]
- r = r.func(Poly.from_list(c, x).as_expr(), -constant / scale)
- except PolynomialError:
- pass
- elif len(free) >= 2:
- try:
- from sympy.solvers.solveset import linear_coeffs
- from sympy.polys.polytools import gcd
- free = list(ordered(free))
- dif = r.lhs - r.rhs
- m = linear_coeffs(dif, *free)
- constant = m[-1]
- del m[-1]
- scale = gcd(m)
- m = [mtmp / scale for mtmp in m]
- nzm = list(filter(lambda f: f[0] != 0, list(zip(m, free))))
- if scale.is_zero is False:
- if constant != 0:
- # lhs: expression, rhs: constant
- newexpr = Add(*[i * j for i, j in nzm])
- r = r.func(newexpr, -constant / scale)
- else:
- # keep first term on lhs
- lhsterm = nzm[0][0] * nzm[0][1]
- del nzm[0]
- newexpr = Add(*[i * j for i, j in nzm])
- r = r.func(lhsterm, -newexpr)
- else:
- r = r.func(constant, S.Zero)
- except ValueError:
- pass
- # Did we get a simplified result?
- r = r.canonical
- measure = kwargs['measure']
- if measure(r) < kwargs['ratio'] * measure(self):
- return r
- else:
- return self
- def _eval_trigsimp(self, **opts):
- from sympy.simplify.trigsimp import trigsimp
- return self.func(trigsimp(self.lhs, **opts), trigsimp(self.rhs, **opts))
- def expand(self, **kwargs):
- args = (arg.expand(**kwargs) for arg in self.args)
- return self.func(*args)
- def __bool__(self):
- raise TypeError("cannot determine truth value of Relational")
- def _eval_as_set(self):
- # self is univariate and periodicity(self, x) in (0, None)
- from sympy.solvers.inequalities import solve_univariate_inequality
- from sympy.sets.conditionset import ConditionSet
- syms = self.free_symbols
- assert len(syms) == 1
- x = syms.pop()
- try:
- xset = solve_univariate_inequality(self, x, relational=False)
- except NotImplementedError:
- # solve_univariate_inequality raises NotImplementedError for
- # unsolvable equations/inequalities.
- xset = ConditionSet(x, self, S.Reals)
- return xset
- @property
- def binary_symbols(self):
- # override where necessary
- return set()
- Rel = Relational
- class Equality(Relational):
- """
- An equal relation between two objects.
- Explanation
- ===========
- Represents that two objects are equal. If they can be easily shown
- to be definitively equal (or unequal), this will reduce to True (or
- False). Otherwise, the relation is maintained as an unevaluated
- Equality object. Use the ``simplify`` function on this object for
- more nontrivial evaluation of the equality relation.
- As usual, the keyword argument ``evaluate=False`` can be used to
- prevent any evaluation.
- Examples
- ========
- >>> from sympy import Eq, simplify, exp, cos
- >>> from sympy.abc import x, y
- >>> Eq(y, x + x**2)
- Eq(y, x**2 + x)
- >>> Eq(2, 5)
- False
- >>> Eq(2, 5, evaluate=False)
- Eq(2, 5)
- >>> _.doit()
- False
- >>> Eq(exp(x), exp(x).rewrite(cos))
- Eq(exp(x), sinh(x) + cosh(x))
- >>> simplify(_)
- True
- See Also
- ========
- sympy.logic.boolalg.Equivalent : for representing equality between two
- boolean expressions
- Notes
- =====
- Python treats 1 and True (and 0 and False) as being equal; SymPy
- does not. And integer will always compare as unequal to a Boolean:
- >>> Eq(True, 1), True == 1
- (False, True)
- This class is not the same as the == operator. The == operator tests
- for exact structural equality between two expressions; this class
- compares expressions mathematically.
- If either object defines an ``_eval_Eq`` method, it can be used in place of
- the default algorithm. If ``lhs._eval_Eq(rhs)`` or ``rhs._eval_Eq(lhs)``
- returns anything other than None, that return value will be substituted for
- the Equality. If None is returned by ``_eval_Eq``, an Equality object will
- be created as usual.
- Since this object is already an expression, it does not respond to
- the method ``as_expr`` if one tries to create `x - y` from ``Eq(x, y)``.
- This can be done with the ``rewrite(Add)`` method.
- .. deprecated:: 1.5
- ``Eq(expr)`` with a single argument is a shorthand for ``Eq(expr, 0)``,
- but this behavior is deprecated and will be removed in a future version
- of SymPy.
- """
- rel_op = '=='
- __slots__ = ()
- is_Equality = True
- def __new__(cls, lhs, rhs, **options):
- evaluate = options.pop('evaluate', global_parameters.evaluate)
- lhs = _sympify(lhs)
- rhs = _sympify(rhs)
- if evaluate:
- val = is_eq(lhs, rhs)
- if val is None:
- return cls(lhs, rhs, evaluate=False)
- else:
- return _sympify(val)
- return Relational.__new__(cls, lhs, rhs)
- @classmethod
- def _eval_relation(cls, lhs, rhs):
- return _sympify(lhs == rhs)
- def _eval_rewrite_as_Add(self, *args, **kwargs):
- """
- return Eq(L, R) as L - R. To control the evaluation of
- the result set pass `evaluate=True` to give L - R;
- if `evaluate=None` then terms in L and R will not cancel
- but they will be listed in canonical order; otherwise
- non-canonical args will be returned. If one side is 0, the
- non-zero side will be returned.
- Examples
- ========
- >>> from sympy import Eq, Add
- >>> from sympy.abc import b, x
- >>> eq = Eq(x + b, x - b)
- >>> eq.rewrite(Add)
- 2*b
- >>> eq.rewrite(Add, evaluate=None).args
- (b, b, x, -x)
- >>> eq.rewrite(Add, evaluate=False).args
- (b, x, b, -x)
- """
- from .add import _unevaluated_Add, Add
- L, R = args
- if L == 0:
- return R
- if R == 0:
- return L
- evaluate = kwargs.get('evaluate', True)
- if evaluate:
- # allow cancellation of args
- return L - R
- args = Add.make_args(L) + Add.make_args(-R)
- if evaluate is None:
- # no cancellation, but canonical
- return _unevaluated_Add(*args)
- # no cancellation, not canonical
- return Add._from_args(args)
- @property
- def binary_symbols(self):
- if S.true in self.args or S.false in self.args:
- if self.lhs.is_Symbol:
- return {self.lhs}
- elif self.rhs.is_Symbol:
- return {self.rhs}
- return set()
- def _eval_simplify(self, **kwargs):
- # standard simplify
- e = super()._eval_simplify(**kwargs)
- if not isinstance(e, Equality):
- return e
- from .expr import Expr
- if not isinstance(e.lhs, Expr) or not isinstance(e.rhs, Expr):
- return e
- free = self.free_symbols
- if len(free) == 1:
- try:
- from .add import Add
- from sympy.solvers.solveset import linear_coeffs
- x = free.pop()
- m, b = linear_coeffs(
- e.rewrite(Add, evaluate=False), x)
- if m.is_zero is False:
- enew = e.func(x, -b / m)
- else:
- enew = e.func(m * x, -b)
- measure = kwargs['measure']
- if measure(enew) <= kwargs['ratio'] * measure(e):
- e = enew
- except ValueError:
- pass
- return e.canonical
- def integrate(self, *args, **kwargs):
- """See the integrate function in sympy.integrals"""
- from sympy.integrals.integrals import integrate
- return integrate(self, *args, **kwargs)
- def as_poly(self, *gens, **kwargs):
- '''Returns lhs-rhs as a Poly
- Examples
- ========
- >>> from sympy import Eq
- >>> from sympy.abc import x
- >>> Eq(x**2, 1).as_poly(x)
- Poly(x**2 - 1, x, domain='ZZ')
- '''
- return (self.lhs - self.rhs).as_poly(*gens, **kwargs)
- Eq = Equality
- class Unequality(Relational):
- """An unequal relation between two objects.
- Explanation
- ===========
- Represents that two objects are not equal. If they can be shown to be
- definitively equal, this will reduce to False; if definitively unequal,
- this will reduce to True. Otherwise, the relation is maintained as an
- Unequality object.
- Examples
- ========
- >>> from sympy import Ne
- >>> from sympy.abc import x, y
- >>> Ne(y, x+x**2)
- Ne(y, x**2 + x)
- See Also
- ========
- Equality
- Notes
- =====
- This class is not the same as the != operator. The != operator tests
- for exact structural equality between two expressions; this class
- compares expressions mathematically.
- This class is effectively the inverse of Equality. As such, it uses the
- same algorithms, including any available `_eval_Eq` methods.
- """
- rel_op = '!='
- __slots__ = ()
- def __new__(cls, lhs, rhs, **options):
- lhs = _sympify(lhs)
- rhs = _sympify(rhs)
- evaluate = options.pop('evaluate', global_parameters.evaluate)
- if evaluate:
- val = is_neq(lhs, rhs)
- if val is None:
- return cls(lhs, rhs, evaluate=False)
- else:
- return _sympify(val)
- return Relational.__new__(cls, lhs, rhs, **options)
- @classmethod
- def _eval_relation(cls, lhs, rhs):
- return _sympify(lhs != rhs)
- @property
- def binary_symbols(self):
- if S.true in self.args or S.false in self.args:
- if self.lhs.is_Symbol:
- return {self.lhs}
- elif self.rhs.is_Symbol:
- return {self.rhs}
- return set()
- def _eval_simplify(self, **kwargs):
- # simplify as an equality
- eq = Equality(*self.args)._eval_simplify(**kwargs)
- if isinstance(eq, Equality):
- # send back Ne with the new args
- return self.func(*eq.args)
- return eq.negated # result of Ne is the negated Eq
- Ne = Unequality
- class _Inequality(Relational):
- """Internal base class for all *Than types.
- Each subclass must implement _eval_relation to provide the method for
- comparing two real numbers.
- """
- __slots__ = ()
- def __new__(cls, lhs, rhs, **options):
- try:
- lhs = _sympify(lhs)
- rhs = _sympify(rhs)
- except SympifyError:
- return NotImplemented
- evaluate = options.pop('evaluate', global_parameters.evaluate)
- if evaluate:
- for me in (lhs, rhs):
- if me.is_extended_real is False:
- raise TypeError("Invalid comparison of non-real %s" % me)
- if me is S.NaN:
- raise TypeError("Invalid NaN comparison")
- # First we invoke the appropriate inequality method of `lhs`
- # (e.g., `lhs.__lt__`). That method will try to reduce to
- # boolean or raise an exception. It may keep calling
- # superclasses until it reaches `Expr` (e.g., `Expr.__lt__`).
- # In some cases, `Expr` will just invoke us again (if neither it
- # nor a subclass was able to reduce to boolean or raise an
- # exception). In that case, it must call us with
- # `evaluate=False` to prevent infinite recursion.
- return cls._eval_relation(lhs, rhs, **options)
- # make a "non-evaluated" Expr for the inequality
- return Relational.__new__(cls, lhs, rhs, **options)
- @classmethod
- def _eval_relation(cls, lhs, rhs, **options):
- val = cls._eval_fuzzy_relation(lhs, rhs)
- if val is None:
- return cls(lhs, rhs, evaluate=False)
- else:
- return _sympify(val)
- class _Greater(_Inequality):
- """Not intended for general use
- _Greater is only used so that GreaterThan and StrictGreaterThan may
- subclass it for the .gts and .lts properties.
- """
- __slots__ = ()
- @property
- def gts(self):
- return self._args[0]
- @property
- def lts(self):
- return self._args[1]
- class _Less(_Inequality):
- """Not intended for general use.
- _Less is only used so that LessThan and StrictLessThan may subclass it for
- the .gts and .lts properties.
- """
- __slots__ = ()
- @property
- def gts(self):
- return self._args[1]
- @property
- def lts(self):
- return self._args[0]
- class GreaterThan(_Greater):
- r"""Class representations of inequalities.
- Explanation
- ===========
- The ``*Than`` classes represent inequal relationships, where the left-hand
- side is generally bigger or smaller than the right-hand side. For example,
- the GreaterThan class represents an inequal relationship where the
- left-hand side is at least as big as the right side, if not bigger. In
- mathematical notation:
- lhs $\ge$ rhs
- In total, there are four ``*Than`` classes, to represent the four
- inequalities:
- +-----------------+--------+
- |Class Name | Symbol |
- +=================+========+
- |GreaterThan | ``>=`` |
- +-----------------+--------+
- |LessThan | ``<=`` |
- +-----------------+--------+
- |StrictGreaterThan| ``>`` |
- +-----------------+--------+
- |StrictLessThan | ``<`` |
- +-----------------+--------+
- All classes take two arguments, lhs and rhs.
- +----------------------------+-----------------+
- |Signature Example | Math Equivalent |
- +============================+=================+
- |GreaterThan(lhs, rhs) | lhs $\ge$ rhs |
- +----------------------------+-----------------+
- |LessThan(lhs, rhs) | lhs $\le$ rhs |
- +----------------------------+-----------------+
- |StrictGreaterThan(lhs, rhs) | lhs $>$ rhs |
- +----------------------------+-----------------+
- |StrictLessThan(lhs, rhs) | lhs $<$ rhs |
- +----------------------------+-----------------+
- In addition to the normal .lhs and .rhs of Relations, ``*Than`` inequality
- objects also have the .lts and .gts properties, which represent the "less
- than side" and "greater than side" of the operator. Use of .lts and .gts
- in an algorithm rather than .lhs and .rhs as an assumption of inequality
- direction will make more explicit the intent of a certain section of code,
- and will make it similarly more robust to client code changes:
- >>> from sympy import GreaterThan, StrictGreaterThan
- >>> from sympy import LessThan, StrictLessThan
- >>> from sympy import And, Ge, Gt, Le, Lt, Rel, S
- >>> from sympy.abc import x, y, z
- >>> from sympy.core.relational import Relational
- >>> e = GreaterThan(x, 1)
- >>> e
- x >= 1
- >>> '%s >= %s is the same as %s <= %s' % (e.gts, e.lts, e.lts, e.gts)
- 'x >= 1 is the same as 1 <= x'
- Examples
- ========
- One generally does not instantiate these classes directly, but uses various
- convenience methods:
- >>> for f in [Ge, Gt, Le, Lt]: # convenience wrappers
- ... print(f(x, 2))
- x >= 2
- x > 2
- x <= 2
- x < 2
- Another option is to use the Python inequality operators (``>=``, ``>``,
- ``<=``, ``<``) directly. Their main advantage over the ``Ge``, ``Gt``,
- ``Le``, and ``Lt`` counterparts, is that one can write a more
- "mathematical looking" statement rather than littering the math with
- oddball function calls. However there are certain (minor) caveats of
- which to be aware (search for 'gotcha', below).
- >>> x >= 2
- x >= 2
- >>> _ == Ge(x, 2)
- True
- However, it is also perfectly valid to instantiate a ``*Than`` class less
- succinctly and less conveniently:
- >>> Rel(x, 1, ">")
- x > 1
- >>> Relational(x, 1, ">")
- x > 1
- >>> StrictGreaterThan(x, 1)
- x > 1
- >>> GreaterThan(x, 1)
- x >= 1
- >>> LessThan(x, 1)
- x <= 1
- >>> StrictLessThan(x, 1)
- x < 1
- Notes
- =====
- There are a couple of "gotchas" to be aware of when using Python's
- operators.
- The first is that what your write is not always what you get:
- >>> 1 < x
- x > 1
- Due to the order that Python parses a statement, it may
- not immediately find two objects comparable. When ``1 < x``
- is evaluated, Python recognizes that the number 1 is a native
- number and that x is *not*. Because a native Python number does
- not know how to compare itself with a SymPy object
- Python will try the reflective operation, ``x > 1`` and that is the
- form that gets evaluated, hence returned.
- If the order of the statement is important (for visual output to
- the console, perhaps), one can work around this annoyance in a
- couple ways:
- (1) "sympify" the literal before comparison
- >>> S(1) < x
- 1 < x
- (2) use one of the wrappers or less succinct methods described
- above
- >>> Lt(1, x)
- 1 < x
- >>> Relational(1, x, "<")
- 1 < x
- The second gotcha involves writing equality tests between relationals
- when one or both sides of the test involve a literal relational:
- >>> e = x < 1; e
- x < 1
- >>> e == e # neither side is a literal
- True
- >>> e == x < 1 # expecting True, too
- False
- >>> e != x < 1 # expecting False
- x < 1
- >>> x < 1 != x < 1 # expecting False or the same thing as before
- Traceback (most recent call last):
- ...
- TypeError: cannot determine truth value of Relational
- The solution for this case is to wrap literal relationals in
- parentheses:
- >>> e == (x < 1)
- True
- >>> e != (x < 1)
- False
- >>> (x < 1) != (x < 1)
- False
- The third gotcha involves chained inequalities not involving
- ``==`` or ``!=``. Occasionally, one may be tempted to write:
- >>> e = x < y < z
- Traceback (most recent call last):
- ...
- TypeError: symbolic boolean expression has no truth value.
- Due to an implementation detail or decision of Python [1]_,
- there is no way for SymPy to create a chained inequality with
- that syntax so one must use And:
- >>> e = And(x < y, y < z)
- >>> type( e )
- And
- >>> e
- (x < y) & (y < z)
- Although this can also be done with the '&' operator, it cannot
- be done with the 'and' operarator:
- >>> (x < y) & (y < z)
- (x < y) & (y < z)
- >>> (x < y) and (y < z)
- Traceback (most recent call last):
- ...
- TypeError: cannot determine truth value of Relational
- .. [1] This implementation detail is that Python provides no reliable
- method to determine that a chained inequality is being built.
- Chained comparison operators are evaluated pairwise, using "and"
- logic (see
- https://docs.python.org/3/reference/expressions.html#not-in). This
- is done in an efficient way, so that each object being compared
- is only evaluated once and the comparison can short-circuit. For
- example, ``1 > 2 > 3`` is evaluated by Python as ``(1 > 2) and (2
- > 3)``. The ``and`` operator coerces each side into a bool,
- returning the object itself when it short-circuits. The bool of
- the --Than operators will raise TypeError on purpose, because
- SymPy cannot determine the mathematical ordering of symbolic
- expressions. Thus, if we were to compute ``x > y > z``, with
- ``x``, ``y``, and ``z`` being Symbols, Python converts the
- statement (roughly) into these steps:
- (1) x > y > z
- (2) (x > y) and (y > z)
- (3) (GreaterThanObject) and (y > z)
- (4) (GreaterThanObject.__bool__()) and (y > z)
- (5) TypeError
- Because of the ``and`` added at step 2, the statement gets turned into a
- weak ternary statement, and the first object's ``__bool__`` method will
- raise TypeError. Thus, creating a chained inequality is not possible.
- In Python, there is no way to override the ``and`` operator, or to
- control how it short circuits, so it is impossible to make something
- like ``x > y > z`` work. There was a PEP to change this,
- :pep:`335`, but it was officially closed in March, 2012.
- """
- __slots__ = ()
- rel_op = '>='
- @classmethod
- def _eval_fuzzy_relation(cls, lhs, rhs):
- return is_ge(lhs, rhs)
- @property
- def strict(self):
- return Gt(*self.args)
- Ge = GreaterThan
- class LessThan(_Less):
- __doc__ = GreaterThan.__doc__
- __slots__ = ()
- rel_op = '<='
- @classmethod
- def _eval_fuzzy_relation(cls, lhs, rhs):
- return is_le(lhs, rhs)
- @property
- def strict(self):
- return Lt(*self.args)
- Le = LessThan
- class StrictGreaterThan(_Greater):
- __doc__ = GreaterThan.__doc__
- __slots__ = ()
- rel_op = '>'
- @classmethod
- def _eval_fuzzy_relation(cls, lhs, rhs):
- return is_gt(lhs, rhs)
- @property
- def weak(self):
- return Ge(*self.args)
- Gt = StrictGreaterThan
- class StrictLessThan(_Less):
- __doc__ = GreaterThan.__doc__
- __slots__ = ()
- rel_op = '<'
- @classmethod
- def _eval_fuzzy_relation(cls, lhs, rhs):
- return is_lt(lhs, rhs)
- @property
- def weak(self):
- return Le(*self.args)
- Lt = StrictLessThan
- # A class-specific (not object-specific) data item used for a minor speedup.
- # It is defined here, rather than directly in the class, because the classes
- # that it references have not been defined until now (e.g. StrictLessThan).
- Relational.ValidRelationOperator = {
- None: Equality,
- '==': Equality,
- 'eq': Equality,
- '!=': Unequality,
- '<>': Unequality,
- 'ne': Unequality,
- '>=': GreaterThan,
- 'ge': GreaterThan,
- '<=': LessThan,
- 'le': LessThan,
- '>': StrictGreaterThan,
- 'gt': StrictGreaterThan,
- '<': StrictLessThan,
- 'lt': StrictLessThan,
- }
- def _n2(a, b):
- """Return (a - b).evalf(2) if a and b are comparable, else None.
- This should only be used when a and b are already sympified.
- """
- # /!\ it is very important (see issue 8245) not to
- # use a re-evaluated number in the calculation of dif
- if a.is_comparable and b.is_comparable:
- dif = (a - b).evalf(2)
- if dif.is_comparable:
- return dif
- @dispatch(Expr, Expr)
- def _eval_is_ge(lhs, rhs):
- return None
- @dispatch(Basic, Basic)
- def _eval_is_eq(lhs, rhs):
- return None
- @dispatch(Tuple, Expr) # type: ignore
- def _eval_is_eq(lhs, rhs): # noqa:F811
- return False
- @dispatch(Tuple, AppliedUndef) # type: ignore
- def _eval_is_eq(lhs, rhs): # noqa:F811
- return None
- @dispatch(Tuple, Symbol) # type: ignore
- def _eval_is_eq(lhs, rhs): # noqa:F811
- return None
- @dispatch(Tuple, Tuple) # type: ignore
- def _eval_is_eq(lhs, rhs): # noqa:F811
- if len(lhs) != len(rhs):
- return False
- return fuzzy_and(fuzzy_bool(is_eq(s, o)) for s, o in zip(lhs, rhs))
- def is_lt(lhs, rhs, assumptions=None):
- """Fuzzy bool for lhs is strictly less than rhs.
- See the docstring for :func:`~.is_ge` for more.
- """
- return fuzzy_not(is_ge(lhs, rhs, assumptions))
- def is_gt(lhs, rhs, assumptions=None):
- """Fuzzy bool for lhs is strictly greater than rhs.
- See the docstring for :func:`~.is_ge` for more.
- """
- return fuzzy_not(is_le(lhs, rhs, assumptions))
- def is_le(lhs, rhs, assumptions=None):
- """Fuzzy bool for lhs is less than or equal to rhs.
- See the docstring for :func:`~.is_ge` for more.
- """
- return is_ge(rhs, lhs, assumptions)
- def is_ge(lhs, rhs, assumptions=None):
- """
- Fuzzy bool for *lhs* is greater than or equal to *rhs*.
- Parameters
- ==========
- lhs : Expr
- The left-hand side of the expression, must be sympified,
- and an instance of expression. Throws an exception if
- lhs is not an instance of expression.
- rhs : Expr
- The right-hand side of the expression, must be sympified
- and an instance of expression. Throws an exception if
- lhs is not an instance of expression.
- assumptions: Boolean, optional
- Assumptions taken to evaluate the inequality.
- Returns
- =======
- ``True`` if *lhs* is greater than or equal to *rhs*, ``False`` if *lhs*
- is less than *rhs*, and ``None`` if the comparison between *lhs* and
- *rhs* is indeterminate.
- Explanation
- ===========
- This function is intended to give a relatively fast determination and
- deliberately does not attempt slow calculations that might help in
- obtaining a determination of True or False in more difficult cases.
- The four comparison functions ``is_le``, ``is_lt``, ``is_ge``, and ``is_gt`` are
- each implemented in terms of ``is_ge`` in the following way:
- is_ge(x, y) := is_ge(x, y)
- is_le(x, y) := is_ge(y, x)
- is_lt(x, y) := fuzzy_not(is_ge(x, y))
- is_gt(x, y) := fuzzy_not(is_ge(y, x))
- Therefore, supporting new type with this function will ensure behavior for
- other three functions as well.
- To maintain these equivalences in fuzzy logic it is important that in cases where
- either x or y is non-real all comparisons will give None.
- Examples
- ========
- >>> from sympy import S, Q
- >>> from sympy.core.relational import is_ge, is_le, is_gt, is_lt
- >>> from sympy.abc import x
- >>> is_ge(S(2), S(0))
- True
- >>> is_ge(S(0), S(2))
- False
- >>> is_le(S(0), S(2))
- True
- >>> is_gt(S(0), S(2))
- False
- >>> is_lt(S(2), S(0))
- False
- Assumptions can be passed to evaluate the quality which is otherwise
- indeterminate.
- >>> print(is_ge(x, S(0)))
- None
- >>> is_ge(x, S(0), assumptions=Q.positive(x))
- True
- New types can be supported by dispatching to ``_eval_is_ge``.
- >>> from sympy import Expr, sympify
- >>> from sympy.multipledispatch import dispatch
- >>> class MyExpr(Expr):
- ... def __new__(cls, arg):
- ... return super().__new__(cls, sympify(arg))
- ... @property
- ... def value(self):
- ... return self.args[0]
- >>> @dispatch(MyExpr, MyExpr)
- ... def _eval_is_ge(a, b):
- ... return is_ge(a.value, b.value)
- >>> a = MyExpr(1)
- >>> b = MyExpr(2)
- >>> is_ge(b, a)
- True
- >>> is_le(a, b)
- True
- """
- from sympy.assumptions.wrapper import AssumptionsWrapper, is_extended_nonnegative
- if not (isinstance(lhs, Expr) and isinstance(rhs, Expr)):
- raise TypeError("Can only compare inequalities with Expr")
- retval = _eval_is_ge(lhs, rhs)
- if retval is not None:
- return retval
- else:
- n2 = _n2(lhs, rhs)
- if n2 is not None:
- # use float comparison for infinity.
- # otherwise get stuck in infinite recursion
- if n2 in (S.Infinity, S.NegativeInfinity):
- n2 = float(n2)
- return n2 >= 0
- _lhs = AssumptionsWrapper(lhs, assumptions)
- _rhs = AssumptionsWrapper(rhs, assumptions)
- if _lhs.is_extended_real and _rhs.is_extended_real:
- if (_lhs.is_infinite and _lhs.is_extended_positive) or (_rhs.is_infinite and _rhs.is_extended_negative):
- return True
- diff = lhs - rhs
- if diff is not S.NaN:
- rv = is_extended_nonnegative(diff, assumptions)
- if rv is not None:
- return rv
- def is_neq(lhs, rhs, assumptions=None):
- """Fuzzy bool for lhs does not equal rhs.
- See the docstring for :func:`~.is_eq` for more.
- """
- return fuzzy_not(is_eq(lhs, rhs, assumptions))
- def is_eq(lhs, rhs, assumptions=None):
- """
- Fuzzy bool representing mathematical equality between *lhs* and *rhs*.
- Parameters
- ==========
- lhs : Expr
- The left-hand side of the expression, must be sympified.
- rhs : Expr
- The right-hand side of the expression, must be sympified.
- assumptions: Boolean, optional
- Assumptions taken to evaluate the equality.
- Returns
- =======
- ``True`` if *lhs* is equal to *rhs*, ``False`` is *lhs* is not equal to *rhs*,
- and ``None`` if the comparison between *lhs* and *rhs* is indeterminate.
- Explanation
- ===========
- This function is intended to give a relatively fast determination and
- deliberately does not attempt slow calculations that might help in
- obtaining a determination of True or False in more difficult cases.
- :func:`~.is_neq` calls this function to return its value, so supporting
- new type with this function will ensure correct behavior for ``is_neq``
- as well.
- Examples
- ========
- >>> from sympy import Q, S
- >>> from sympy.core.relational import is_eq, is_neq
- >>> from sympy.abc import x
- >>> is_eq(S(0), S(0))
- True
- >>> is_neq(S(0), S(0))
- False
- >>> is_eq(S(0), S(2))
- False
- >>> is_neq(S(0), S(2))
- True
- Assumptions can be passed to evaluate the equality which is otherwise
- indeterminate.
- >>> print(is_eq(x, S(0)))
- None
- >>> is_eq(x, S(0), assumptions=Q.zero(x))
- True
- New types can be supported by dispatching to ``_eval_is_eq``.
- >>> from sympy import Basic, sympify
- >>> from sympy.multipledispatch import dispatch
- >>> class MyBasic(Basic):
- ... def __new__(cls, arg):
- ... return Basic.__new__(cls, sympify(arg))
- ... @property
- ... def value(self):
- ... return self.args[0]
- ...
- >>> @dispatch(MyBasic, MyBasic)
- ... def _eval_is_eq(a, b):
- ... return is_eq(a.value, b.value)
- ...
- >>> a = MyBasic(1)
- >>> b = MyBasic(1)
- >>> is_eq(a, b)
- True
- >>> is_neq(a, b)
- False
- """
- # here, _eval_Eq is only called for backwards compatibility
- # new code should use is_eq with multiple dispatch as
- # outlined in the docstring
- for side1, side2 in (lhs, rhs), (rhs, lhs):
- eval_func = getattr(side1, '_eval_Eq', None)
- if eval_func is not None:
- retval = eval_func(side2)
- if retval is not None:
- return retval
- retval = _eval_is_eq(lhs, rhs)
- if retval is not None:
- return retval
- if dispatch(type(lhs), type(rhs)) != dispatch(type(rhs), type(lhs)):
- retval = _eval_is_eq(rhs, lhs)
- if retval is not None:
- return retval
- # retval is still None, so go through the equality logic
- # If expressions have the same structure, they must be equal.
- if lhs == rhs:
- return True # e.g. True == True
- elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)):
- return False # True != False
- elif not (lhs.is_Symbol or rhs.is_Symbol) and (
- isinstance(lhs, Boolean) !=
- isinstance(rhs, Boolean)):
- return False # only Booleans can equal Booleans
- from sympy.assumptions.wrapper import (AssumptionsWrapper,
- is_infinite, is_extended_real)
- from .add import Add
- _lhs = AssumptionsWrapper(lhs, assumptions)
- _rhs = AssumptionsWrapper(rhs, assumptions)
- if _lhs.is_infinite or _rhs.is_infinite:
- if fuzzy_xor([_lhs.is_infinite, _rhs.is_infinite]):
- return False
- if fuzzy_xor([_lhs.is_extended_real, _rhs.is_extended_real]):
- return False
- if fuzzy_and([_lhs.is_extended_real, _rhs.is_extended_real]):
- return fuzzy_xor([_lhs.is_extended_positive, fuzzy_not(_rhs.is_extended_positive)])
- # Try to split real/imaginary parts and equate them
- I = S.ImaginaryUnit
- def split_real_imag(expr):
- real_imag = lambda t: (
- 'real' if is_extended_real(t, assumptions) else
- 'imag' if is_extended_real(I*t, assumptions) else None)
- return sift(Add.make_args(expr), real_imag)
- lhs_ri = split_real_imag(lhs)
- if not lhs_ri[None]:
- rhs_ri = split_real_imag(rhs)
- if not rhs_ri[None]:
- eq_real = is_eq(Add(*lhs_ri['real']), Add(*rhs_ri['real']), assumptions)
- eq_imag = is_eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag']), assumptions)
- return fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag]))
- from sympy.functions.elementary.complexes import arg
- # Compare e.g. zoo with 1+I*oo by comparing args
- arglhs = arg(lhs)
- argrhs = arg(rhs)
- # Guard against Eq(nan, nan) -> False
- if not (arglhs == S.NaN and argrhs == S.NaN):
- return fuzzy_bool(is_eq(arglhs, argrhs, assumptions))
- if all(isinstance(i, Expr) for i in (lhs, rhs)):
- # see if the difference evaluates
- dif = lhs - rhs
- _dif = AssumptionsWrapper(dif, assumptions)
- z = _dif.is_zero
- if z is not None:
- if z is False and _dif.is_commutative: # issue 10728
- return False
- if z:
- return True
- n2 = _n2(lhs, rhs)
- if n2 is not None:
- return _sympify(n2 == 0)
- # see if the ratio evaluates
- n, d = dif.as_numer_denom()
- rv = None
- _n = AssumptionsWrapper(n, assumptions)
- _d = AssumptionsWrapper(d, assumptions)
- if _n.is_zero:
- rv = _d.is_nonzero
- elif _n.is_finite:
- if _d.is_infinite:
- rv = True
- elif _n.is_zero is False:
- rv = _d.is_infinite
- if rv is None:
- # if the condition that makes the denominator
- # infinite does not make the original expression
- # True then False can be returned
- from sympy.simplify.simplify import clear_coefficients
- l, r = clear_coefficients(d, S.Infinity)
- args = [_.subs(l, r) for _ in (lhs, rhs)]
- if args != [lhs, rhs]:
- rv = fuzzy_bool(is_eq(*args, assumptions))
- if rv is True:
- rv = None
- elif any(is_infinite(a, assumptions) for a in Add.make_args(n)):
- # (inf or nan)/x != 0
- rv = False
- if rv is not None:
- return rv
|