Coverage for /usr/lib/python3/dist-packages/sympy/core/relational.py: 35%
550 statements
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
« prev ^ index » next coverage.py v7.9.1, created at 2025-06-14 15:55 +0200
1from __future__ import annotations
3from .basic import Atom, Basic
4from .sorting import ordered
5from .evalf import EvalfMixin
6from .function import AppliedUndef
7from .singleton import S
8from .sympify import _sympify, SympifyError
9from .parameters import global_parameters
10from .logic import fuzzy_bool, fuzzy_xor, fuzzy_and, fuzzy_not
11from sympy.logic.boolalg import Boolean, BooleanAtom
12from sympy.utilities.iterables import sift
13from sympy.utilities.misc import filldedent
15__all__ = (
16 'Rel', 'Eq', 'Ne', 'Lt', 'Le', 'Gt', 'Ge',
17 'Relational', 'Equality', 'Unequality', 'StrictLessThan', 'LessThan',
18 'StrictGreaterThan', 'GreaterThan',
19)
21from .expr import Expr
22from sympy.multipledispatch import dispatch
23from .containers import Tuple
24from .symbol import Symbol
27def _nontrivBool(side):
28 return isinstance(side, Boolean) and \
29 not isinstance(side, Atom)
32# Note, see issue 4986. Ideally, we wouldn't want to subclass both Boolean
33# and Expr.
34# from .. import Expr
37def _canonical(cond):
38 # return a condition in which all relationals are canonical
39 reps = {r: r.canonical for r in cond.atoms(Relational)}
40 return cond.xreplace(reps)
41 # XXX: AttributeError was being caught here but it wasn't triggered by any of
42 # the tests so I've removed it...
45def _canonical_coeff(rel):
46 # return -2*x + 1 < 0 as x > 1/2
47 # XXX make this part of Relational.canonical?
48 rel = rel.canonical
49 if not rel.is_Relational or rel.rhs.is_Boolean:
50 return rel # Eq(x, True)
51 b, l = rel.lhs.as_coeff_Add(rational=True)
52 m, lhs = l.as_coeff_Mul(rational=True)
53 rhs = (rel.rhs - b)/m
54 if m < 0:
55 return rel.reversed.func(lhs, rhs)
56 return rel.func(lhs, rhs)
59class Relational(Boolean, EvalfMixin):
60 """Base class for all relation types.
62 Explanation
63 ===========
65 Subclasses of Relational should generally be instantiated directly, but
66 Relational can be instantiated with a valid ``rop`` value to dispatch to
67 the appropriate subclass.
69 Parameters
70 ==========
72 rop : str or None
73 Indicates what subclass to instantiate. Valid values can be found
74 in the keys of Relational.ValidRelationOperator.
76 Examples
77 ========
79 >>> from sympy import Rel
80 >>> from sympy.abc import x, y
81 >>> Rel(y, x + x**2, '==')
82 Eq(y, x**2 + x)
84 A relation's type can be defined upon creation using ``rop``.
85 The relation type of an existing expression can be obtained
86 using its ``rel_op`` property.
87 Here is a table of all the relation types, along with their
88 ``rop`` and ``rel_op`` values:
90 +---------------------+----------------------------+------------+
91 |Relation |``rop`` |``rel_op`` |
92 +=====================+============================+============+
93 |``Equality`` |``==`` or ``eq`` or ``None``|``==`` |
94 +---------------------+----------------------------+------------+
95 |``Unequality`` |``!=`` or ``ne`` |``!=`` |
96 +---------------------+----------------------------+------------+
97 |``GreaterThan`` |``>=`` or ``ge`` |``>=`` |
98 +---------------------+----------------------------+------------+
99 |``LessThan`` |``<=`` or ``le`` |``<=`` |
100 +---------------------+----------------------------+------------+
101 |``StrictGreaterThan``|``>`` or ``gt`` |``>`` |
102 +---------------------+----------------------------+------------+
103 |``StrictLessThan`` |``<`` or ``lt`` |``<`` |
104 +---------------------+----------------------------+------------+
106 For example, setting ``rop`` to ``==`` produces an
107 ``Equality`` relation, ``Eq()``.
108 So does setting ``rop`` to ``eq``, or leaving ``rop`` unspecified.
109 That is, the first three ``Rel()`` below all produce the same result.
110 Using a ``rop`` from a different row in the table produces a
111 different relation type.
112 For example, the fourth ``Rel()`` below using ``lt`` for ``rop``
113 produces a ``StrictLessThan`` inequality:
115 >>> from sympy import Rel
116 >>> from sympy.abc import x, y
117 >>> Rel(y, x + x**2, '==')
118 Eq(y, x**2 + x)
119 >>> Rel(y, x + x**2, 'eq')
120 Eq(y, x**2 + x)
121 >>> Rel(y, x + x**2)
122 Eq(y, x**2 + x)
123 >>> Rel(y, x + x**2, 'lt')
124 y < x**2 + x
126 To obtain the relation type of an existing expression,
127 get its ``rel_op`` property.
128 For example, ``rel_op`` is ``==`` for the ``Equality`` relation above,
129 and ``<`` for the strict less than inequality above:
131 >>> from sympy import Rel
132 >>> from sympy.abc import x, y
133 >>> my_equality = Rel(y, x + x**2, '==')
134 >>> my_equality.rel_op
135 '=='
136 >>> my_inequality = Rel(y, x + x**2, 'lt')
137 >>> my_inequality.rel_op
138 '<'
140 """
141 __slots__ = ()
143 ValidRelationOperator: dict[str | None, type[Relational]] = {}
145 is_Relational = True
147 # ValidRelationOperator - Defined below, because the necessary classes
148 # have not yet been defined
150 def __new__(cls, lhs, rhs, rop=None, **assumptions):
151 # If called by a subclass, do nothing special and pass on to Basic.
152 if cls is not Relational:
153 return Basic.__new__(cls, lhs, rhs, **assumptions)
155 # XXX: Why do this? There should be a separate function to make a
156 # particular subclass of Relational from a string.
157 #
158 # If called directly with an operator, look up the subclass
159 # corresponding to that operator and delegate to it
160 cls = cls.ValidRelationOperator.get(rop, None)
161 if cls is None:
162 raise ValueError("Invalid relational operator symbol: %r" % rop)
164 if not issubclass(cls, (Eq, Ne)):
165 # validate that Booleans are not being used in a relational
166 # other than Eq/Ne;
167 # Note: Symbol is a subclass of Boolean but is considered
168 # acceptable here.
169 if any(map(_nontrivBool, (lhs, rhs))):
170 raise TypeError(filldedent('''
171 A Boolean argument can only be used in
172 Eq and Ne; all other relationals expect
173 real expressions.
174 '''))
176 return cls(lhs, rhs, **assumptions)
178 @property
179 def lhs(self):
180 """The left-hand side of the relation."""
181 return self._args[0]
183 @property
184 def rhs(self):
185 """The right-hand side of the relation."""
186 return self._args[1]
188 @property
189 def reversed(self):
190 """Return the relationship with sides reversed.
192 Examples
193 ========
195 >>> from sympy import Eq
196 >>> from sympy.abc import x
197 >>> Eq(x, 1)
198 Eq(x, 1)
199 >>> _.reversed
200 Eq(1, x)
201 >>> x < 1
202 x < 1
203 >>> _.reversed
204 1 > x
205 """
206 ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne}
207 a, b = self.args
208 return Relational.__new__(ops.get(self.func, self.func), b, a)
210 @property
211 def reversedsign(self):
212 """Return the relationship with signs reversed.
214 Examples
215 ========
217 >>> from sympy import Eq
218 >>> from sympy.abc import x
219 >>> Eq(x, 1)
220 Eq(x, 1)
221 >>> _.reversedsign
222 Eq(-x, -1)
223 >>> x < 1
224 x < 1
225 >>> _.reversedsign
226 -x > -1
227 """
228 a, b = self.args
229 if not (isinstance(a, BooleanAtom) or isinstance(b, BooleanAtom)):
230 ops = {Eq: Eq, Gt: Lt, Ge: Le, Lt: Gt, Le: Ge, Ne: Ne}
231 return Relational.__new__(ops.get(self.func, self.func), -a, -b)
232 else:
233 return self
235 @property
236 def negated(self):
237 """Return the negated relationship.
239 Examples
240 ========
242 >>> from sympy import Eq
243 >>> from sympy.abc import x
244 >>> Eq(x, 1)
245 Eq(x, 1)
246 >>> _.negated
247 Ne(x, 1)
248 >>> x < 1
249 x < 1
250 >>> _.negated
251 x >= 1
253 Notes
254 =====
256 This works more or less identical to ``~``/``Not``. The difference is
257 that ``negated`` returns the relationship even if ``evaluate=False``.
258 Hence, this is useful in code when checking for e.g. negated relations
259 to existing ones as it will not be affected by the `evaluate` flag.
261 """
262 ops = {Eq: Ne, Ge: Lt, Gt: Le, Le: Gt, Lt: Ge, Ne: Eq}
263 # If there ever will be new Relational subclasses, the following line
264 # will work until it is properly sorted out
265 # return ops.get(self.func, lambda a, b, evaluate=False: ~(self.func(a,
266 # b, evaluate=evaluate)))(*self.args, evaluate=False)
267 return Relational.__new__(ops.get(self.func), *self.args)
269 @property
270 def weak(self):
271 """return the non-strict version of the inequality or self
273 EXAMPLES
274 ========
276 >>> from sympy.abc import x
277 >>> (x < 1).weak
278 x <= 1
279 >>> _.weak
280 x <= 1
281 """
282 return self
284 @property
285 def strict(self):
286 """return the strict version of the inequality or self
288 EXAMPLES
289 ========
291 >>> from sympy.abc import x
292 >>> (x <= 1).strict
293 x < 1
294 >>> _.strict
295 x < 1
296 """
297 return self
299 def _eval_evalf(self, prec):
300 return self.func(*[s._evalf(prec) for s in self.args])
302 @property
303 def canonical(self):
304 """Return a canonical form of the relational by putting a
305 number on the rhs, canonically removing a sign or else
306 ordering the args canonically. No other simplification is
307 attempted.
309 Examples
310 ========
312 >>> from sympy.abc import x, y
313 >>> x < 2
314 x < 2
315 >>> _.reversed.canonical
316 x < 2
317 >>> (-y < x).canonical
318 x > -y
319 >>> (-y > x).canonical
320 x < -y
321 >>> (-y < -x).canonical
322 x < y
324 The canonicalization is recursively applied:
326 >>> from sympy import Eq
327 >>> Eq(x < y, y > x).canonical
328 True
329 """
330 args = tuple([i.canonical if isinstance(i, Relational) else i for i in self.args])
331 if args != self.args:
332 r = self.func(*args)
333 if not isinstance(r, Relational):
334 return r
335 else:
336 r = self
337 if r.rhs.is_number:
338 if r.rhs.is_Number and r.lhs.is_Number and r.lhs > r.rhs:
339 r = r.reversed
340 elif r.lhs.is_number:
341 r = r.reversed
342 elif tuple(ordered(args)) != args:
343 r = r.reversed
345 LHS_CEMS = getattr(r.lhs, 'could_extract_minus_sign', None)
346 RHS_CEMS = getattr(r.rhs, 'could_extract_minus_sign', None)
348 if isinstance(r.lhs, BooleanAtom) or isinstance(r.rhs, BooleanAtom):
349 return r
351 # Check if first value has negative sign
352 if LHS_CEMS and LHS_CEMS():
353 return r.reversedsign
354 elif not r.rhs.is_number and RHS_CEMS and RHS_CEMS():
355 # Right hand side has a minus, but not lhs.
356 # How does the expression with reversed signs behave?
357 # This is so that expressions of the type
358 # Eq(x, -y) and Eq(-x, y)
359 # have the same canonical representation
360 expr1, _ = ordered([r.lhs, -r.rhs])
361 if expr1 != r.lhs:
362 return r.reversed.reversedsign
364 return r
366 def equals(self, other, failing_expression=False):
367 """Return True if the sides of the relationship are mathematically
368 identical and the type of relationship is the same.
369 If failing_expression is True, return the expression whose truth value
370 was unknown."""
371 if isinstance(other, Relational):
372 if other in (self, self.reversed):
373 return True
374 a, b = self, other
375 if a.func in (Eq, Ne) or b.func in (Eq, Ne):
376 if a.func != b.func:
377 return False
378 left, right = [i.equals(j,
379 failing_expression=failing_expression)
380 for i, j in zip(a.args, b.args)]
381 if left is True:
382 return right
383 if right is True:
384 return left
385 lr, rl = [i.equals(j, failing_expression=failing_expression)
386 for i, j in zip(a.args, b.reversed.args)]
387 if lr is True:
388 return rl
389 if rl is True:
390 return lr
391 e = (left, right, lr, rl)
392 if all(i is False for i in e):
393 return False
394 for i in e:
395 if i not in (True, False):
396 return i
397 else:
398 if b.func != a.func:
399 b = b.reversed
400 if a.func != b.func:
401 return False
402 left = a.lhs.equals(b.lhs,
403 failing_expression=failing_expression)
404 if left is False:
405 return False
406 right = a.rhs.equals(b.rhs,
407 failing_expression=failing_expression)
408 if right is False:
409 return False
410 if left is True:
411 return right
412 return left
414 def _eval_simplify(self, **kwargs):
415 from .add import Add
416 from .expr import Expr
417 r = self
418 r = r.func(*[i.simplify(**kwargs) for i in r.args])
419 if r.is_Relational:
420 if not isinstance(r.lhs, Expr) or not isinstance(r.rhs, Expr):
421 return r
422 dif = r.lhs - r.rhs
423 # replace dif with a valid Number that will
424 # allow a definitive comparison with 0
425 v = None
426 if dif.is_comparable:
427 v = dif.n(2)
428 elif dif.equals(0): # XXX this is expensive
429 v = S.Zero
430 if v is not None:
431 r = r.func._eval_relation(v, S.Zero)
432 r = r.canonical
433 # If there is only one symbol in the expression,
434 # try to write it on a simplified form
435 free = list(filter(lambda x: x.is_real is not False, r.free_symbols))
436 if len(free) == 1:
437 try:
438 from sympy.solvers.solveset import linear_coeffs
439 x = free.pop()
440 dif = r.lhs - r.rhs
441 m, b = linear_coeffs(dif, x)
442 if m.is_zero is False:
443 if m.is_negative:
444 # Dividing with a negative number, so change order of arguments
445 # canonical will put the symbol back on the lhs later
446 r = r.func(-b / m, x)
447 else:
448 r = r.func(x, -b / m)
449 else:
450 r = r.func(b, S.Zero)
451 except ValueError:
452 # maybe not a linear function, try polynomial
453 from sympy.polys.polyerrors import PolynomialError
454 from sympy.polys.polytools import gcd, Poly, poly
455 try:
456 p = poly(dif, x)
457 c = p.all_coeffs()
458 constant = c[-1]
459 c[-1] = 0
460 scale = gcd(c)
461 c = [ctmp / scale for ctmp in c]
462 r = r.func(Poly.from_list(c, x).as_expr(), -constant / scale)
463 except PolynomialError:
464 pass
465 elif len(free) >= 2:
466 try:
467 from sympy.solvers.solveset import linear_coeffs
468 from sympy.polys.polytools import gcd
469 free = list(ordered(free))
470 dif = r.lhs - r.rhs
471 m = linear_coeffs(dif, *free)
472 constant = m[-1]
473 del m[-1]
474 scale = gcd(m)
475 m = [mtmp / scale for mtmp in m]
476 nzm = list(filter(lambda f: f[0] != 0, list(zip(m, free))))
477 if scale.is_zero is False:
478 if constant != 0:
479 # lhs: expression, rhs: constant
480 newexpr = Add(*[i * j for i, j in nzm])
481 r = r.func(newexpr, -constant / scale)
482 else:
483 # keep first term on lhs
484 lhsterm = nzm[0][0] * nzm[0][1]
485 del nzm[0]
486 newexpr = Add(*[i * j for i, j in nzm])
487 r = r.func(lhsterm, -newexpr)
489 else:
490 r = r.func(constant, S.Zero)
491 except ValueError:
492 pass
493 # Did we get a simplified result?
494 r = r.canonical
495 measure = kwargs['measure']
496 if measure(r) < kwargs['ratio'] * measure(self):
497 return r
498 else:
499 return self
501 def _eval_trigsimp(self, **opts):
502 from sympy.simplify.trigsimp import trigsimp
503 return self.func(trigsimp(self.lhs, **opts), trigsimp(self.rhs, **opts))
505 def expand(self, **kwargs):
506 args = (arg.expand(**kwargs) for arg in self.args)
507 return self.func(*args)
509 def __bool__(self):
510 raise TypeError("cannot determine truth value of Relational")
512 def _eval_as_set(self):
513 # self is univariate and periodicity(self, x) in (0, None)
514 from sympy.solvers.inequalities import solve_univariate_inequality
515 from sympy.sets.conditionset import ConditionSet
516 syms = self.free_symbols
517 assert len(syms) == 1
518 x = syms.pop()
519 try:
520 xset = solve_univariate_inequality(self, x, relational=False)
521 except NotImplementedError:
522 # solve_univariate_inequality raises NotImplementedError for
523 # unsolvable equations/inequalities.
524 xset = ConditionSet(x, self, S.Reals)
525 return xset
527 @property
528 def binary_symbols(self):
529 # override where necessary
530 return set()
533Rel = Relational
536class Equality(Relational):
537 """
538 An equal relation between two objects.
540 Explanation
541 ===========
543 Represents that two objects are equal. If they can be easily shown
544 to be definitively equal (or unequal), this will reduce to True (or
545 False). Otherwise, the relation is maintained as an unevaluated
546 Equality object. Use the ``simplify`` function on this object for
547 more nontrivial evaluation of the equality relation.
549 As usual, the keyword argument ``evaluate=False`` can be used to
550 prevent any evaluation.
552 Examples
553 ========
555 >>> from sympy import Eq, simplify, exp, cos
556 >>> from sympy.abc import x, y
557 >>> Eq(y, x + x**2)
558 Eq(y, x**2 + x)
559 >>> Eq(2, 5)
560 False
561 >>> Eq(2, 5, evaluate=False)
562 Eq(2, 5)
563 >>> _.doit()
564 False
565 >>> Eq(exp(x), exp(x).rewrite(cos))
566 Eq(exp(x), sinh(x) + cosh(x))
567 >>> simplify(_)
568 True
570 See Also
571 ========
573 sympy.logic.boolalg.Equivalent : for representing equality between two
574 boolean expressions
576 Notes
577 =====
579 Python treats 1 and True (and 0 and False) as being equal; SymPy
580 does not. And integer will always compare as unequal to a Boolean:
582 >>> Eq(True, 1), True == 1
583 (False, True)
585 This class is not the same as the == operator. The == operator tests
586 for exact structural equality between two expressions; this class
587 compares expressions mathematically.
589 If either object defines an ``_eval_Eq`` method, it can be used in place of
590 the default algorithm. If ``lhs._eval_Eq(rhs)`` or ``rhs._eval_Eq(lhs)``
591 returns anything other than None, that return value will be substituted for
592 the Equality. If None is returned by ``_eval_Eq``, an Equality object will
593 be created as usual.
595 Since this object is already an expression, it does not respond to
596 the method ``as_expr`` if one tries to create `x - y` from ``Eq(x, y)``.
597 This can be done with the ``rewrite(Add)`` method.
599 .. deprecated:: 1.5
601 ``Eq(expr)`` with a single argument is a shorthand for ``Eq(expr, 0)``,
602 but this behavior is deprecated and will be removed in a future version
603 of SymPy.
605 """
606 rel_op = '=='
608 __slots__ = ()
610 is_Equality = True
612 def __new__(cls, lhs, rhs, **options):
613 evaluate = options.pop('evaluate', global_parameters.evaluate)
614 lhs = _sympify(lhs)
615 rhs = _sympify(rhs)
616 if evaluate:
617 val = is_eq(lhs, rhs)
618 if val is None:
619 return cls(lhs, rhs, evaluate=False)
620 else:
621 return _sympify(val)
623 return Relational.__new__(cls, lhs, rhs)
625 @classmethod
626 def _eval_relation(cls, lhs, rhs):
627 return _sympify(lhs == rhs)
629 def _eval_rewrite_as_Add(self, *args, **kwargs):
630 """
631 return Eq(L, R) as L - R. To control the evaluation of
632 the result set pass `evaluate=True` to give L - R;
633 if `evaluate=None` then terms in L and R will not cancel
634 but they will be listed in canonical order; otherwise
635 non-canonical args will be returned. If one side is 0, the
636 non-zero side will be returned.
638 Examples
639 ========
641 >>> from sympy import Eq, Add
642 >>> from sympy.abc import b, x
643 >>> eq = Eq(x + b, x - b)
644 >>> eq.rewrite(Add)
645 2*b
646 >>> eq.rewrite(Add, evaluate=None).args
647 (b, b, x, -x)
648 >>> eq.rewrite(Add, evaluate=False).args
649 (b, x, b, -x)
650 """
651 from .add import _unevaluated_Add, Add
652 L, R = args
653 if L == 0:
654 return R
655 if R == 0:
656 return L
657 evaluate = kwargs.get('evaluate', True)
658 if evaluate:
659 # allow cancellation of args
660 return L - R
661 args = Add.make_args(L) + Add.make_args(-R)
662 if evaluate is None:
663 # no cancellation, but canonical
664 return _unevaluated_Add(*args)
665 # no cancellation, not canonical
666 return Add._from_args(args)
668 @property
669 def binary_symbols(self):
670 if S.true in self.args or S.false in self.args:
671 if self.lhs.is_Symbol:
672 return {self.lhs}
673 elif self.rhs.is_Symbol:
674 return {self.rhs}
675 return set()
677 def _eval_simplify(self, **kwargs):
678 # standard simplify
679 e = super()._eval_simplify(**kwargs)
680 if not isinstance(e, Equality):
681 return e
682 from .expr import Expr
683 if not isinstance(e.lhs, Expr) or not isinstance(e.rhs, Expr):
684 return e
685 free = self.free_symbols
686 if len(free) == 1:
687 try:
688 from .add import Add
689 from sympy.solvers.solveset import linear_coeffs
690 x = free.pop()
691 m, b = linear_coeffs(
692 e.rewrite(Add, evaluate=False), x)
693 if m.is_zero is False:
694 enew = e.func(x, -b / m)
695 else:
696 enew = e.func(m * x, -b)
697 measure = kwargs['measure']
698 if measure(enew) <= kwargs['ratio'] * measure(e):
699 e = enew
700 except ValueError:
701 pass
702 return e.canonical
704 def integrate(self, *args, **kwargs):
705 """See the integrate function in sympy.integrals"""
706 from sympy.integrals.integrals import integrate
707 return integrate(self, *args, **kwargs)
709 def as_poly(self, *gens, **kwargs):
710 '''Returns lhs-rhs as a Poly
712 Examples
713 ========
715 >>> from sympy import Eq
716 >>> from sympy.abc import x
717 >>> Eq(x**2, 1).as_poly(x)
718 Poly(x**2 - 1, x, domain='ZZ')
719 '''
720 return (self.lhs - self.rhs).as_poly(*gens, **kwargs)
723Eq = Equality
726class Unequality(Relational):
727 """An unequal relation between two objects.
729 Explanation
730 ===========
732 Represents that two objects are not equal. If they can be shown to be
733 definitively equal, this will reduce to False; if definitively unequal,
734 this will reduce to True. Otherwise, the relation is maintained as an
735 Unequality object.
737 Examples
738 ========
740 >>> from sympy import Ne
741 >>> from sympy.abc import x, y
742 >>> Ne(y, x+x**2)
743 Ne(y, x**2 + x)
745 See Also
746 ========
747 Equality
749 Notes
750 =====
751 This class is not the same as the != operator. The != operator tests
752 for exact structural equality between two expressions; this class
753 compares expressions mathematically.
755 This class is effectively the inverse of Equality. As such, it uses the
756 same algorithms, including any available `_eval_Eq` methods.
758 """
759 rel_op = '!='
761 __slots__ = ()
763 def __new__(cls, lhs, rhs, **options):
764 lhs = _sympify(lhs)
765 rhs = _sympify(rhs)
766 evaluate = options.pop('evaluate', global_parameters.evaluate)
767 if evaluate:
768 val = is_neq(lhs, rhs)
769 if val is None:
770 return cls(lhs, rhs, evaluate=False)
771 else:
772 return _sympify(val)
774 return Relational.__new__(cls, lhs, rhs, **options)
776 @classmethod
777 def _eval_relation(cls, lhs, rhs):
778 return _sympify(lhs != rhs)
780 @property
781 def binary_symbols(self):
782 if S.true in self.args or S.false in self.args:
783 if self.lhs.is_Symbol:
784 return {self.lhs}
785 elif self.rhs.is_Symbol:
786 return {self.rhs}
787 return set()
789 def _eval_simplify(self, **kwargs):
790 # simplify as an equality
791 eq = Equality(*self.args)._eval_simplify(**kwargs)
792 if isinstance(eq, Equality):
793 # send back Ne with the new args
794 return self.func(*eq.args)
795 return eq.negated # result of Ne is the negated Eq
798Ne = Unequality
801class _Inequality(Relational):
802 """Internal base class for all *Than types.
804 Each subclass must implement _eval_relation to provide the method for
805 comparing two real numbers.
807 """
808 __slots__ = ()
810 def __new__(cls, lhs, rhs, **options):
812 try:
813 lhs = _sympify(lhs)
814 rhs = _sympify(rhs)
815 except SympifyError:
816 return NotImplemented
818 evaluate = options.pop('evaluate', global_parameters.evaluate)
819 if evaluate:
820 for me in (lhs, rhs):
821 if me.is_extended_real is False:
822 raise TypeError("Invalid comparison of non-real %s" % me)
823 if me is S.NaN:
824 raise TypeError("Invalid NaN comparison")
825 # First we invoke the appropriate inequality method of `lhs`
826 # (e.g., `lhs.__lt__`). That method will try to reduce to
827 # boolean or raise an exception. It may keep calling
828 # superclasses until it reaches `Expr` (e.g., `Expr.__lt__`).
829 # In some cases, `Expr` will just invoke us again (if neither it
830 # nor a subclass was able to reduce to boolean or raise an
831 # exception). In that case, it must call us with
832 # `evaluate=False` to prevent infinite recursion.
833 return cls._eval_relation(lhs, rhs, **options)
835 # make a "non-evaluated" Expr for the inequality
836 return Relational.__new__(cls, lhs, rhs, **options)
838 @classmethod
839 def _eval_relation(cls, lhs, rhs, **options):
840 val = cls._eval_fuzzy_relation(lhs, rhs)
841 if val is None:
842 return cls(lhs, rhs, evaluate=False)
843 else:
844 return _sympify(val)
847class _Greater(_Inequality):
848 """Not intended for general use
850 _Greater is only used so that GreaterThan and StrictGreaterThan may
851 subclass it for the .gts and .lts properties.
853 """
854 __slots__ = ()
856 @property
857 def gts(self):
858 return self._args[0]
860 @property
861 def lts(self):
862 return self._args[1]
865class _Less(_Inequality):
866 """Not intended for general use.
868 _Less is only used so that LessThan and StrictLessThan may subclass it for
869 the .gts and .lts properties.
871 """
872 __slots__ = ()
874 @property
875 def gts(self):
876 return self._args[1]
878 @property
879 def lts(self):
880 return self._args[0]
883class GreaterThan(_Greater):
884 r"""Class representations of inequalities.
886 Explanation
887 ===========
889 The ``*Than`` classes represent inequal relationships, where the left-hand
890 side is generally bigger or smaller than the right-hand side. For example,
891 the GreaterThan class represents an inequal relationship where the
892 left-hand side is at least as big as the right side, if not bigger. In
893 mathematical notation:
895 lhs $\ge$ rhs
897 In total, there are four ``*Than`` classes, to represent the four
898 inequalities:
900 +-----------------+--------+
901 |Class Name | Symbol |
902 +=================+========+
903 |GreaterThan | ``>=`` |
904 +-----------------+--------+
905 |LessThan | ``<=`` |
906 +-----------------+--------+
907 |StrictGreaterThan| ``>`` |
908 +-----------------+--------+
909 |StrictLessThan | ``<`` |
910 +-----------------+--------+
912 All classes take two arguments, lhs and rhs.
914 +----------------------------+-----------------+
915 |Signature Example | Math Equivalent |
916 +============================+=================+
917 |GreaterThan(lhs, rhs) | lhs $\ge$ rhs |
918 +----------------------------+-----------------+
919 |LessThan(lhs, rhs) | lhs $\le$ rhs |
920 +----------------------------+-----------------+
921 |StrictGreaterThan(lhs, rhs) | lhs $>$ rhs |
922 +----------------------------+-----------------+
923 |StrictLessThan(lhs, rhs) | lhs $<$ rhs |
924 +----------------------------+-----------------+
926 In addition to the normal .lhs and .rhs of Relations, ``*Than`` inequality
927 objects also have the .lts and .gts properties, which represent the "less
928 than side" and "greater than side" of the operator. Use of .lts and .gts
929 in an algorithm rather than .lhs and .rhs as an assumption of inequality
930 direction will make more explicit the intent of a certain section of code,
931 and will make it similarly more robust to client code changes:
933 >>> from sympy import GreaterThan, StrictGreaterThan
934 >>> from sympy import LessThan, StrictLessThan
935 >>> from sympy import And, Ge, Gt, Le, Lt, Rel, S
936 >>> from sympy.abc import x, y, z
937 >>> from sympy.core.relational import Relational
939 >>> e = GreaterThan(x, 1)
940 >>> e
941 x >= 1
942 >>> '%s >= %s is the same as %s <= %s' % (e.gts, e.lts, e.lts, e.gts)
943 'x >= 1 is the same as 1 <= x'
945 Examples
946 ========
948 One generally does not instantiate these classes directly, but uses various
949 convenience methods:
951 >>> for f in [Ge, Gt, Le, Lt]: # convenience wrappers
952 ... print(f(x, 2))
953 x >= 2
954 x > 2
955 x <= 2
956 x < 2
958 Another option is to use the Python inequality operators (``>=``, ``>``,
959 ``<=``, ``<``) directly. Their main advantage over the ``Ge``, ``Gt``,
960 ``Le``, and ``Lt`` counterparts, is that one can write a more
961 "mathematical looking" statement rather than littering the math with
962 oddball function calls. However there are certain (minor) caveats of
963 which to be aware (search for 'gotcha', below).
965 >>> x >= 2
966 x >= 2
967 >>> _ == Ge(x, 2)
968 True
970 However, it is also perfectly valid to instantiate a ``*Than`` class less
971 succinctly and less conveniently:
973 >>> Rel(x, 1, ">")
974 x > 1
975 >>> Relational(x, 1, ">")
976 x > 1
978 >>> StrictGreaterThan(x, 1)
979 x > 1
980 >>> GreaterThan(x, 1)
981 x >= 1
982 >>> LessThan(x, 1)
983 x <= 1
984 >>> StrictLessThan(x, 1)
985 x < 1
987 Notes
988 =====
990 There are a couple of "gotchas" to be aware of when using Python's
991 operators.
993 The first is that what your write is not always what you get:
995 >>> 1 < x
996 x > 1
998 Due to the order that Python parses a statement, it may
999 not immediately find two objects comparable. When ``1 < x``
1000 is evaluated, Python recognizes that the number 1 is a native
1001 number and that x is *not*. Because a native Python number does
1002 not know how to compare itself with a SymPy object
1003 Python will try the reflective operation, ``x > 1`` and that is the
1004 form that gets evaluated, hence returned.
1006 If the order of the statement is important (for visual output to
1007 the console, perhaps), one can work around this annoyance in a
1008 couple ways:
1010 (1) "sympify" the literal before comparison
1012 >>> S(1) < x
1013 1 < x
1015 (2) use one of the wrappers or less succinct methods described
1016 above
1018 >>> Lt(1, x)
1019 1 < x
1020 >>> Relational(1, x, "<")
1021 1 < x
1023 The second gotcha involves writing equality tests between relationals
1024 when one or both sides of the test involve a literal relational:
1026 >>> e = x < 1; e
1027 x < 1
1028 >>> e == e # neither side is a literal
1029 True
1030 >>> e == x < 1 # expecting True, too
1031 False
1032 >>> e != x < 1 # expecting False
1033 x < 1
1034 >>> x < 1 != x < 1 # expecting False or the same thing as before
1035 Traceback (most recent call last):
1036 ...
1037 TypeError: cannot determine truth value of Relational
1039 The solution for this case is to wrap literal relationals in
1040 parentheses:
1042 >>> e == (x < 1)
1043 True
1044 >>> e != (x < 1)
1045 False
1046 >>> (x < 1) != (x < 1)
1047 False
1049 The third gotcha involves chained inequalities not involving
1050 ``==`` or ``!=``. Occasionally, one may be tempted to write:
1052 >>> e = x < y < z
1053 Traceback (most recent call last):
1054 ...
1055 TypeError: symbolic boolean expression has no truth value.
1057 Due to an implementation detail or decision of Python [1]_,
1058 there is no way for SymPy to create a chained inequality with
1059 that syntax so one must use And:
1061 >>> e = And(x < y, y < z)
1062 >>> type( e )
1063 And
1064 >>> e
1065 (x < y) & (y < z)
1067 Although this can also be done with the '&' operator, it cannot
1068 be done with the 'and' operarator:
1070 >>> (x < y) & (y < z)
1071 (x < y) & (y < z)
1072 >>> (x < y) and (y < z)
1073 Traceback (most recent call last):
1074 ...
1075 TypeError: cannot determine truth value of Relational
1077 .. [1] This implementation detail is that Python provides no reliable
1078 method to determine that a chained inequality is being built.
1079 Chained comparison operators are evaluated pairwise, using "and"
1080 logic (see
1081 https://docs.python.org/3/reference/expressions.html#not-in). This
1082 is done in an efficient way, so that each object being compared
1083 is only evaluated once and the comparison can short-circuit. For
1084 example, ``1 > 2 > 3`` is evaluated by Python as ``(1 > 2) and (2
1085 > 3)``. The ``and`` operator coerces each side into a bool,
1086 returning the object itself when it short-circuits. The bool of
1087 the --Than operators will raise TypeError on purpose, because
1088 SymPy cannot determine the mathematical ordering of symbolic
1089 expressions. Thus, if we were to compute ``x > y > z``, with
1090 ``x``, ``y``, and ``z`` being Symbols, Python converts the
1091 statement (roughly) into these steps:
1093 (1) x > y > z
1094 (2) (x > y) and (y > z)
1095 (3) (GreaterThanObject) and (y > z)
1096 (4) (GreaterThanObject.__bool__()) and (y > z)
1097 (5) TypeError
1099 Because of the ``and`` added at step 2, the statement gets turned into a
1100 weak ternary statement, and the first object's ``__bool__`` method will
1101 raise TypeError. Thus, creating a chained inequality is not possible.
1103 In Python, there is no way to override the ``and`` operator, or to
1104 control how it short circuits, so it is impossible to make something
1105 like ``x > y > z`` work. There was a PEP to change this,
1106 :pep:`335`, but it was officially closed in March, 2012.
1108 """
1109 __slots__ = ()
1111 rel_op = '>='
1113 @classmethod
1114 def _eval_fuzzy_relation(cls, lhs, rhs):
1115 return is_ge(lhs, rhs)
1117 @property
1118 def strict(self):
1119 return Gt(*self.args)
1121Ge = GreaterThan
1124class LessThan(_Less):
1125 __doc__ = GreaterThan.__doc__
1126 __slots__ = ()
1128 rel_op = '<='
1130 @classmethod
1131 def _eval_fuzzy_relation(cls, lhs, rhs):
1132 return is_le(lhs, rhs)
1134 @property
1135 def strict(self):
1136 return Lt(*self.args)
1138Le = LessThan
1141class StrictGreaterThan(_Greater):
1142 __doc__ = GreaterThan.__doc__
1143 __slots__ = ()
1145 rel_op = '>'
1147 @classmethod
1148 def _eval_fuzzy_relation(cls, lhs, rhs):
1149 return is_gt(lhs, rhs)
1151 @property
1152 def weak(self):
1153 return Ge(*self.args)
1156Gt = StrictGreaterThan
1159class StrictLessThan(_Less):
1160 __doc__ = GreaterThan.__doc__
1161 __slots__ = ()
1163 rel_op = '<'
1165 @classmethod
1166 def _eval_fuzzy_relation(cls, lhs, rhs):
1167 return is_lt(lhs, rhs)
1169 @property
1170 def weak(self):
1171 return Le(*self.args)
1173Lt = StrictLessThan
1175# A class-specific (not object-specific) data item used for a minor speedup.
1176# It is defined here, rather than directly in the class, because the classes
1177# that it references have not been defined until now (e.g. StrictLessThan).
1178Relational.ValidRelationOperator = {
1179 None: Equality,
1180 '==': Equality,
1181 'eq': Equality,
1182 '!=': Unequality,
1183 '<>': Unequality,
1184 'ne': Unequality,
1185 '>=': GreaterThan,
1186 'ge': GreaterThan,
1187 '<=': LessThan,
1188 'le': LessThan,
1189 '>': StrictGreaterThan,
1190 'gt': StrictGreaterThan,
1191 '<': StrictLessThan,
1192 'lt': StrictLessThan,
1193}
1196def _n2(a, b):
1197 """Return (a - b).evalf(2) if a and b are comparable, else None.
1198 This should only be used when a and b are already sympified.
1199 """
1200 # /!\ it is very important (see issue 8245) not to
1201 # use a re-evaluated number in the calculation of dif
1202 if a.is_comparable and b.is_comparable:
1203 dif = (a - b).evalf(2)
1204 if dif.is_comparable:
1205 return dif
1208@dispatch(Expr, Expr)
1209def _eval_is_ge(lhs, rhs):
1210 return None
1213@dispatch(Basic, Basic)
1214def _eval_is_eq(lhs, rhs):
1215 return None
1218@dispatch(Tuple, Expr) # type: ignore
1219def _eval_is_eq(lhs, rhs): # noqa:F811
1220 return False
1223@dispatch(Tuple, AppliedUndef) # type: ignore
1224def _eval_is_eq(lhs, rhs): # noqa:F811
1225 return None
1228@dispatch(Tuple, Symbol) # type: ignore
1229def _eval_is_eq(lhs, rhs): # noqa:F811
1230 return None
1233@dispatch(Tuple, Tuple) # type: ignore
1234def _eval_is_eq(lhs, rhs): # noqa:F811
1235 if len(lhs) != len(rhs):
1236 return False
1238 return fuzzy_and(fuzzy_bool(is_eq(s, o)) for s, o in zip(lhs, rhs))
1241def is_lt(lhs, rhs, assumptions=None):
1242 """Fuzzy bool for lhs is strictly less than rhs.
1244 See the docstring for :func:`~.is_ge` for more.
1245 """
1246 return fuzzy_not(is_ge(lhs, rhs, assumptions))
1249def is_gt(lhs, rhs, assumptions=None):
1250 """Fuzzy bool for lhs is strictly greater than rhs.
1252 See the docstring for :func:`~.is_ge` for more.
1253 """
1254 return fuzzy_not(is_le(lhs, rhs, assumptions))
1257def is_le(lhs, rhs, assumptions=None):
1258 """Fuzzy bool for lhs is less than or equal to rhs.
1260 See the docstring for :func:`~.is_ge` for more.
1261 """
1262 return is_ge(rhs, lhs, assumptions)
1265def is_ge(lhs, rhs, assumptions=None):
1266 """
1267 Fuzzy bool for *lhs* is greater than or equal to *rhs*.
1269 Parameters
1270 ==========
1272 lhs : Expr
1273 The left-hand side of the expression, must be sympified,
1274 and an instance of expression. Throws an exception if
1275 lhs is not an instance of expression.
1277 rhs : Expr
1278 The right-hand side of the expression, must be sympified
1279 and an instance of expression. Throws an exception if
1280 lhs is not an instance of expression.
1282 assumptions: Boolean, optional
1283 Assumptions taken to evaluate the inequality.
1285 Returns
1286 =======
1288 ``True`` if *lhs* is greater than or equal to *rhs*, ``False`` if *lhs*
1289 is less than *rhs*, and ``None`` if the comparison between *lhs* and
1290 *rhs* is indeterminate.
1292 Explanation
1293 ===========
1295 This function is intended to give a relatively fast determination and
1296 deliberately does not attempt slow calculations that might help in
1297 obtaining a determination of True or False in more difficult cases.
1299 The four comparison functions ``is_le``, ``is_lt``, ``is_ge``, and ``is_gt`` are
1300 each implemented in terms of ``is_ge`` in the following way:
1302 is_ge(x, y) := is_ge(x, y)
1303 is_le(x, y) := is_ge(y, x)
1304 is_lt(x, y) := fuzzy_not(is_ge(x, y))
1305 is_gt(x, y) := fuzzy_not(is_ge(y, x))
1307 Therefore, supporting new type with this function will ensure behavior for
1308 other three functions as well.
1310 To maintain these equivalences in fuzzy logic it is important that in cases where
1311 either x or y is non-real all comparisons will give None.
1313 Examples
1314 ========
1316 >>> from sympy import S, Q
1317 >>> from sympy.core.relational import is_ge, is_le, is_gt, is_lt
1318 >>> from sympy.abc import x
1319 >>> is_ge(S(2), S(0))
1320 True
1321 >>> is_ge(S(0), S(2))
1322 False
1323 >>> is_le(S(0), S(2))
1324 True
1325 >>> is_gt(S(0), S(2))
1326 False
1327 >>> is_lt(S(2), S(0))
1328 False
1330 Assumptions can be passed to evaluate the quality which is otherwise
1331 indeterminate.
1333 >>> print(is_ge(x, S(0)))
1334 None
1335 >>> is_ge(x, S(0), assumptions=Q.positive(x))
1336 True
1338 New types can be supported by dispatching to ``_eval_is_ge``.
1340 >>> from sympy import Expr, sympify
1341 >>> from sympy.multipledispatch import dispatch
1342 >>> class MyExpr(Expr):
1343 ... def __new__(cls, arg):
1344 ... return super().__new__(cls, sympify(arg))
1345 ... @property
1346 ... def value(self):
1347 ... return self.args[0]
1348 >>> @dispatch(MyExpr, MyExpr)
1349 ... def _eval_is_ge(a, b):
1350 ... return is_ge(a.value, b.value)
1351 >>> a = MyExpr(1)
1352 >>> b = MyExpr(2)
1353 >>> is_ge(b, a)
1354 True
1355 >>> is_le(a, b)
1356 True
1357 """
1358 from sympy.assumptions.wrapper import AssumptionsWrapper, is_extended_nonnegative
1360 if not (isinstance(lhs, Expr) and isinstance(rhs, Expr)):
1361 raise TypeError("Can only compare inequalities with Expr")
1363 retval = _eval_is_ge(lhs, rhs)
1365 if retval is not None:
1366 return retval
1367 else:
1368 n2 = _n2(lhs, rhs)
1369 if n2 is not None:
1370 # use float comparison for infinity.
1371 # otherwise get stuck in infinite recursion
1372 if n2 in (S.Infinity, S.NegativeInfinity):
1373 n2 = float(n2)
1374 return n2 >= 0
1376 _lhs = AssumptionsWrapper(lhs, assumptions)
1377 _rhs = AssumptionsWrapper(rhs, assumptions)
1378 if _lhs.is_extended_real and _rhs.is_extended_real:
1379 if (_lhs.is_infinite and _lhs.is_extended_positive) or (_rhs.is_infinite and _rhs.is_extended_negative):
1380 return True
1381 diff = lhs - rhs
1382 if diff is not S.NaN:
1383 rv = is_extended_nonnegative(diff, assumptions)
1384 if rv is not None:
1385 return rv
1388def is_neq(lhs, rhs, assumptions=None):
1389 """Fuzzy bool for lhs does not equal rhs.
1391 See the docstring for :func:`~.is_eq` for more.
1392 """
1393 return fuzzy_not(is_eq(lhs, rhs, assumptions))
1396def is_eq(lhs, rhs, assumptions=None):
1397 """
1398 Fuzzy bool representing mathematical equality between *lhs* and *rhs*.
1400 Parameters
1401 ==========
1403 lhs : Expr
1404 The left-hand side of the expression, must be sympified.
1406 rhs : Expr
1407 The right-hand side of the expression, must be sympified.
1409 assumptions: Boolean, optional
1410 Assumptions taken to evaluate the equality.
1412 Returns
1413 =======
1415 ``True`` if *lhs* is equal to *rhs*, ``False`` is *lhs* is not equal to *rhs*,
1416 and ``None`` if the comparison between *lhs* and *rhs* is indeterminate.
1418 Explanation
1419 ===========
1421 This function is intended to give a relatively fast determination and
1422 deliberately does not attempt slow calculations that might help in
1423 obtaining a determination of True or False in more difficult cases.
1425 :func:`~.is_neq` calls this function to return its value, so supporting
1426 new type with this function will ensure correct behavior for ``is_neq``
1427 as well.
1429 Examples
1430 ========
1432 >>> from sympy import Q, S
1433 >>> from sympy.core.relational import is_eq, is_neq
1434 >>> from sympy.abc import x
1435 >>> is_eq(S(0), S(0))
1436 True
1437 >>> is_neq(S(0), S(0))
1438 False
1439 >>> is_eq(S(0), S(2))
1440 False
1441 >>> is_neq(S(0), S(2))
1442 True
1444 Assumptions can be passed to evaluate the equality which is otherwise
1445 indeterminate.
1447 >>> print(is_eq(x, S(0)))
1448 None
1449 >>> is_eq(x, S(0), assumptions=Q.zero(x))
1450 True
1452 New types can be supported by dispatching to ``_eval_is_eq``.
1454 >>> from sympy import Basic, sympify
1455 >>> from sympy.multipledispatch import dispatch
1456 >>> class MyBasic(Basic):
1457 ... def __new__(cls, arg):
1458 ... return Basic.__new__(cls, sympify(arg))
1459 ... @property
1460 ... def value(self):
1461 ... return self.args[0]
1462 ...
1463 >>> @dispatch(MyBasic, MyBasic)
1464 ... def _eval_is_eq(a, b):
1465 ... return is_eq(a.value, b.value)
1466 ...
1467 >>> a = MyBasic(1)
1468 >>> b = MyBasic(1)
1469 >>> is_eq(a, b)
1470 True
1471 >>> is_neq(a, b)
1472 False
1474 """
1475 # here, _eval_Eq is only called for backwards compatibility
1476 # new code should use is_eq with multiple dispatch as
1477 # outlined in the docstring
1478 for side1, side2 in (lhs, rhs), (rhs, lhs):
1479 eval_func = getattr(side1, '_eval_Eq', None)
1480 if eval_func is not None:
1481 retval = eval_func(side2)
1482 if retval is not None:
1483 return retval
1485 retval = _eval_is_eq(lhs, rhs)
1486 if retval is not None:
1487 return retval
1489 if dispatch(type(lhs), type(rhs)) != dispatch(type(rhs), type(lhs)):
1490 retval = _eval_is_eq(rhs, lhs)
1491 if retval is not None:
1492 return retval
1494 # retval is still None, so go through the equality logic
1495 # If expressions have the same structure, they must be equal.
1496 if lhs == rhs:
1497 return True # e.g. True == True
1498 elif all(isinstance(i, BooleanAtom) for i in (rhs, lhs)):
1499 return False # True != False
1500 elif not (lhs.is_Symbol or rhs.is_Symbol) and (
1501 isinstance(lhs, Boolean) !=
1502 isinstance(rhs, Boolean)):
1503 return False # only Booleans can equal Booleans
1505 from sympy.assumptions.wrapper import (AssumptionsWrapper,
1506 is_infinite, is_extended_real)
1507 from .add import Add
1509 _lhs = AssumptionsWrapper(lhs, assumptions)
1510 _rhs = AssumptionsWrapper(rhs, assumptions)
1512 if _lhs.is_infinite or _rhs.is_infinite:
1513 if fuzzy_xor([_lhs.is_infinite, _rhs.is_infinite]):
1514 return False
1515 if fuzzy_xor([_lhs.is_extended_real, _rhs.is_extended_real]):
1516 return False
1517 if fuzzy_and([_lhs.is_extended_real, _rhs.is_extended_real]):
1518 return fuzzy_xor([_lhs.is_extended_positive, fuzzy_not(_rhs.is_extended_positive)])
1520 # Try to split real/imaginary parts and equate them
1521 I = S.ImaginaryUnit
1523 def split_real_imag(expr):
1524 real_imag = lambda t: (
1525 'real' if is_extended_real(t, assumptions) else
1526 'imag' if is_extended_real(I*t, assumptions) else None)
1527 return sift(Add.make_args(expr), real_imag)
1529 lhs_ri = split_real_imag(lhs)
1530 if not lhs_ri[None]:
1531 rhs_ri = split_real_imag(rhs)
1532 if not rhs_ri[None]:
1533 eq_real = is_eq(Add(*lhs_ri['real']), Add(*rhs_ri['real']), assumptions)
1534 eq_imag = is_eq(I * Add(*lhs_ri['imag']), I * Add(*rhs_ri['imag']), assumptions)
1535 return fuzzy_and(map(fuzzy_bool, [eq_real, eq_imag]))
1537 from sympy.functions.elementary.complexes import arg
1538 # Compare e.g. zoo with 1+I*oo by comparing args
1539 arglhs = arg(lhs)
1540 argrhs = arg(rhs)
1541 # Guard against Eq(nan, nan) -> False
1542 if not (arglhs == S.NaN and argrhs == S.NaN):
1543 return fuzzy_bool(is_eq(arglhs, argrhs, assumptions))
1545 if all(isinstance(i, Expr) for i in (lhs, rhs)):
1546 # see if the difference evaluates
1547 dif = lhs - rhs
1548 _dif = AssumptionsWrapper(dif, assumptions)
1549 z = _dif.is_zero
1550 if z is not None:
1551 if z is False and _dif.is_commutative: # issue 10728
1552 return False
1553 if z:
1554 return True
1556 n2 = _n2(lhs, rhs)
1557 if n2 is not None:
1558 return _sympify(n2 == 0)
1560 # see if the ratio evaluates
1561 n, d = dif.as_numer_denom()
1562 rv = None
1563 _n = AssumptionsWrapper(n, assumptions)
1564 _d = AssumptionsWrapper(d, assumptions)
1565 if _n.is_zero:
1566 rv = _d.is_nonzero
1567 elif _n.is_finite:
1568 if _d.is_infinite:
1569 rv = True
1570 elif _n.is_zero is False:
1571 rv = _d.is_infinite
1572 if rv is None:
1573 # if the condition that makes the denominator
1574 # infinite does not make the original expression
1575 # True then False can be returned
1576 from sympy.simplify.simplify import clear_coefficients
1577 l, r = clear_coefficients(d, S.Infinity)
1578 args = [_.subs(l, r) for _ in (lhs, rhs)]
1579 if args != [lhs, rhs]:
1580 rv = fuzzy_bool(is_eq(*args, assumptions))
1581 if rv is True:
1582 rv = None
1583 elif any(is_infinite(a, assumptions) for a in Add.make_args(n)):
1584 # (inf or nan)/x != 0
1585 rv = False
1586 if rv is not None:
1587 return rv