Repository: sympy/sympy

Issue:
doit(deep=False): core vs matrices
In sympy core, `doit(deep=False)` does not evaluate unevaluated expression.
```python
>>> from sympy import Symbol, Add
>>> x = Symbol('x')
>>> expr1 = Add(x, -x, evaluate=False)
>>> expr1 # unevaluated
x - x
>>> expr1.doit(deep=False) # still unevaluated
x - x
```

However, in matrix module,  `doit(deep=False)` returns evaluated expression.
```python
>>> from sympy import MatrixSymbol, MatAdd
>>> A = MatrixSymbol('A', 2,2)
>>> expr2 = MatAdd(A, -A)
>>> expr2 # unevaluated
A -A
>>> expr2.doit(deep=False) #evaluated
ZeroMatrix(2, 2)
```

I think matrix module is doing it right. Shall we change core's `doit`?

Discussion:

Patch:
diff --git a/sympy/core/operations.py b/sympy/core/operations.py
index 8ee869dc26..df464e114a 100644
--- a/sympy/core/operations.py
+++ b/sympy/core/operations.py
@@ -375,6 +375,12 @@ def make_args(cls, expr):
         else:
             return (sympify(expr),)
 
+    def doit(self, **hints):
+        if hints.get('deep', True):
+            terms = [term.doit(**hints) for term in self.args]
+        else:
+            terms = self.args
+        return self.func(*terms, evaluate=True)
 
 class ShortCircuit(Exception):
     pass
diff --git a/sympy/logic/boolalg.py b/sympy/logic/boolalg.py
index dd734cedaf..d9bd39ae64 100644
--- a/sympy/logic/boolalg.py
+++ b/sympy/logic/boolalg.py
@@ -679,21 +679,20 @@ class And(LatticeOp, BooleanFunction):
 
     @classmethod
     def _new_args_filter(cls, args):
-        args = BooleanFunction.binary_check_and_simplify(*args)
-        args = LatticeOp._new_args_filter(args, And)
         newargs = []
-        rel = set()
-        for x in ordered(args):
+        rel = []
+        args = BooleanFunction.binary_check_and_simplify(*args)
+        for x in reversed(args):
             if x.is_Relational:
                 c = x.canonical
                 if c in rel:
                     continue
-                elif c.negated.canonical in rel:
+                nc = c.negated.canonical
+                if any(r == nc for r in rel):
                     return [S.false]
-                else:
-                    rel.add(c)
+                rel.append(c)
             newargs.append(x)
-        return newargs
+        return LatticeOp._new_args_filter(newargs, And)
 
     def _eval_subs(self, old, new):
         args = []


Test patch:
diff --git a/sympy/core/tests/test_arit.py b/sympy/core/tests/test_arit.py
index eecd1eb349..8ec639fe69 100644
--- a/sympy/core/tests/test_arit.py
+++ b/sympy/core/tests/test_arit.py
@@ -1535,6 +1535,19 @@ def test_suppressed_evaluation():
     assert c.func is Pow
     assert c.args == (3, 2)
 
+def test_AssocOp_doit():
+    a = Add(x,x, evaluate=False)
+    b = Mul(y,y, evaluate=False)
+    c = Add(b,b, evaluate=False)
+    d = Mul(a,a, evaluate=False)
+    assert c.doit(deep=False).func == Mul
+    assert c.doit(deep=False).args == (2,y,y)
+    assert c.doit().func == Mul
+    assert c.doit().args == (2, Pow(y,2))
+    assert d.doit(deep=False).func == Pow
+    assert d.doit(deep=False).args == (a, 2*S.One)
+    assert d.doit().func == Mul
+    assert d.doit().args == (4*S.One, Pow(x,2))
 
 def test_Add_as_coeff_mul():
     # issue 5524.  These should all be (1, self)
diff --git a/sympy/logic/tests/test_boolalg.py b/sympy/logic/tests/test_boolalg.py
index 493aa17e86..3fea482504 100644
--- a/sympy/logic/tests/test_boolalg.py
+++ b/sympy/logic/tests/test_boolalg.py
@@ -20,9 +20,9 @@
 from sympy.assumptions.cnf import CNF
 
 from sympy.testing.pytest import raises, XFAIL, slow
-from sympy.utilities.iterables import cartes
+from sympy.utilities import cartes
 
-from itertools import combinations, permutations
+from itertools import combinations
 
 A, B, C, D = symbols('A:D')
 a, b, c, d, e, w, x, y, z = symbols('a:e w:z')
@@ -60,9 +60,7 @@ def test_And():
     e = A > 1
     assert And(e, e.canonical) == e.canonical
     g, l, ge, le = A > B, B < A, A >= B, B <= A
-    assert And(g, l, ge, le) == And(ge, g)
-    assert set([And(*i) for i in permutations((l,g,le,ge))]) == {And(ge, g)}
-    assert And(And(Eq(a, 0), Eq(b, 0)), And(Ne(a, 0), Eq(c, 0))) is false
+    assert And(g, l, ge, le) == And(l, le)
 
 
 def test_Or():