Repository: sympy/sympy
Pull request: recursive canonical
This is for Eq and Ne which can have relational args.

<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->

closes #8243 

#### Brief description of what is fixed or changed


#### Other comments


#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->
* core
    * canonical will recurse into Relational args in Eq and Ne so `Eq(y<x,x>y).canonical -> True`
<!-- END RELEASE NOTES -->


Problem statement:
relationals created with symbols can be made canonical
``` python
>>> a=1<x
>>> b=S(1)<x
>>> c=x>S(1)
>>> a==b
False
>>> b==c
False
```

The `a==b` failure is expected since Python flips the args around. But it and the `b==c` case could all be the same if Relationals created from symbols (<, >, etc...) would sort args canonically. This, like sorting arguments of an evaluated Add (so that `x + 2` == `2 + x`), would make working with inequalities a little friendlier.

Hints from issue discussion:
Actually, there is the gts and lts (in addition to the lhs and rhs attributes):

``` python
>>> a = S(1)
>>> b = a<x
>>> c=x>a
>>> b.lts == c.lts
True
>>> b.gts == c.gts
True
```

Some work toward this end is in #8423

I don't think that auto-canonicalization is favored. It is possible to simplify this expression:
```python
>>> Eq((S.One<x),(x>1))
Eq(1 < x, x > 1)
>>> _.canonical
Eq(x > 1, 1 < x)  <--- canonical probably isn't recursive but could be
>>> simplify(_)
True
```

Commit message: Update test_relational.py

Patch to review:
diff --git a/sympy/core/relational.py b/sympy/core/relational.py
index 3d95a9102bad..2b30b5d0d9a4 100644
--- a/sympy/core/relational.py
+++ b/sympy/core/relational.py
@@ -111,7 +111,8 @@ def __new__(cls, lhs, rhs, rop=None, **assumptions):
             # other than Eq/Ne;
             # Note: Symbol is a subclass of Boolean but is considered
             # acceptable here.
-            if any(map(_nontrivBool, (lhs, rhs))):
+            if any(map(_nontrivBool, (lhs, rhs))) or isinstance(lhs, Relational
+                    ) or isinstance(rhs, Relational)):
                 raise TypeError(filldedent('''
                     A Boolean argument can only be used in
                     Eq and Ne; all other relationals expect
@@ -266,7 +267,7 @@ def canonical(self):
         >>> (-y < -x).canonical
         x < y
         """
-        args = self.args
+        args = [i.canonical if isinstance(i, Relational) else i for i in self.args]
         r = self
         if r.rhs.is_number:
             if r.rhs.is_Number and r.lhs.is_Number and r.lhs > r.rhs:
diff --git a/sympy/core/tests/test_relational.py b/sympy/core/tests/test_relational.py
index eeb5f9afec4d..e98f5f5df754 100644
--- a/sympy/core/tests/test_relational.py
+++ b/sympy/core/tests/test_relational.py
@@ -854,6 +854,7 @@ def test_canonical():
     assert [i.canonical for i in c] == c
     assert [i.reversed.canonical for i in c] == c
     assert not any(i.lhs.is_Number and not i.rhs.is_Number for i in c)
+    assert Eq(y < x, x > y).canonical is S.true
 
 
 @XFAIL


Code review comments:
diff_hunk: @@ -111,7 +111,8 @@ def __new__(cls, lhs, rhs, rop=None, **assumptions):
             # other than Eq/Ne;
             # Note: Symbol is a subclass of Boolean but is considered
             # acceptable here.
-            if any(map(_nontrivBool, (lhs, rhs))):
+            if any(map(_nontrivBool, (lhs, rhs))) or isinstance(lhs, Relational
+                    ) or isinstance(rhs, Relational)):
path: sympy/core/relational.py
text: @author:
```suggestion
            if any(map(_nontrivBool, (lhs, rhs))):
```