Repository: sympy/sympy

Issue:
subs is not working in logic expression
subs is not working in logic expression, for a long expression it only replacement the first match subexpression

Discussion:

Patch:
diff --git a/sympy/logic/boolalg.py b/sympy/logic/boolalg.py
index ac19c02ad2..dec2300413 100644
--- a/sympy/logic/boolalg.py
+++ b/sympy/logic/boolalg.py
@@ -725,6 +725,14 @@ def _eval_subs(self, old, new):
         if bad is not None:
             # let it raise
             bad.subs(old, new)
+        # If old is And, replace the parts of the arguments with new if all
+        # are there
+        if isinstance(old, And):
+            old_set = set(old.args)
+            if old_set.issubset(args):
+                args = set(args) - old_set
+                args.add(new)
+
         return self.func(*args)
 
     def _eval_simplify(self, **kwargs):
@@ -872,6 +880,14 @@ def _eval_subs(self, old, new):
         if bad is not None:
             # let it raise
             bad.subs(old, new)
+        # If old is Or, replace the parts of the arguments with new if all
+        # are there
+        if isinstance(old, Or):
+            old_set = set(old.args)
+            if old_set.issubset(args):
+                args = set(args) - old_set
+                args.add(new)
+
         return self.func(*args)
 
     def _eval_as_set(self):
@@ -1136,6 +1152,16 @@ def _eval_simplify(self, **kwargs):
         return self._apply_patternbased_simplification(rv, patterns,
             kwargs['measure'], None)
 
+    def _eval_subs(self, old, new):
+        # If old is Xor, replace the parts of the arguments with new if all
+        # are there
+        if isinstance(old, Xor):
+            old_set = set(old.args)
+            if old_set.issubset(self.args):
+                args = set(self.args) - old_set
+                args.add(new)
+                return self.func(*args)
+
 
 class Nand(BooleanFunction):
     """


Test patch:
diff --git a/sympy/logic/tests/test_boolalg.py b/sympy/logic/tests/test_boolalg.py
index a5d4598e45..fef8211b21 100644
--- a/sympy/logic/tests/test_boolalg.py
+++ b/sympy/logic/tests/test_boolalg.py
@@ -931,6 +931,20 @@ def test_integer_to_term():
     assert integer_to_term(456, 16) == [0, 0, 0, 0, 0, 0, 0, 1,
                                         1, 1, 0, 0, 1, 0, 0, 0]
 
+def test_issue_21971():
+    a, b, c, d = symbols('a b c d')
+    f = a & b & c | a & c
+    assert f.subs(a & c, d) == b & d | d
+    assert f.subs(a & b & c, d) == a & c | d
+
+    f = (a | b | c) & (a | c)
+    assert f.subs(a | c, d) == (b | d) & d
+    assert f.subs(a | b | c, d) == (a | c) & d
+
+    f = (a ^ b ^ c) & (a ^ c)
+    assert f.subs(a ^ c, d) == (b ^ d) & d
+    assert f.subs(a ^ b ^ c, d) == (a ^ c) & d
+
 
 def test_truth_table():
     assert list(truth_table(And(x, y), [x, y], input=False)) == \