Repository: sympy/sympy

Issue:
Simplifying And boolean operation removes a condition
Hi,

Simplifying a Piecewise with boolean operations results in the wrong simplification:

Here is what I get from Sympy live shell entering

```python
parse_expr("Piecewise((log(x), (x <= 5) & (x > 3)), (x, True))")
```
 vs 
```python
parse_expr("Piecewise((log(x), (x <= 5) & (x > 3)), (x, True))").simplify()
```

![image](https://github.com/sympy/sympy/assets/47865674/ae40c955-d41b-415f-8296-21e2d1a56d0a)

As you can see, the simplified expression removes a condition on `x`: `x>3` has disappeared despite it's not implied by `x <= 5` (e.g., 0 is less than 5 but not greater than 3).


Discussion:
sympy-bot: 

Hi, I am the [SymPy bot](https://github.com/sympy/sympy-bot). I'm here to help you write a release notes entry. Please read the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).

:x: There was an issue with the release notes. **Please do not close this pull request;** instead edit the description after reading the [guide on how to write release notes](https://github.com/sympy/sympy/wiki/Writing-Release-Notes).

* `piecewise` is not a valid release notes header. Release notes headers should be SymPy submodule names, like

  ```
  * core
    * made Add faster

  * printing
    * improve LaTeX printing of fractions
  ```
  or `other`.

  If you have added a new submodule, please add it to the list of valid release notes headers at https://github.com/sympy/sympy-bot/blob/master/sympy_bot/submodules.txt.

<details><summary>Click here to see the pull request description that was parsed.</summary>

    <!-- 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. -->

    Fixes #25603 

    #### Brief description of what is fixed or changed
    Added the And function in the piecewise simplification function. Because of its absence, there was wrong domains when simplifying the parsing of a particular piecewise expression. Not all the expressions in "&" were being considered in output. Hence, the And operator has been added that will take care of all expressions given. Plus, a test has been added in the test_piecewise that is derived from the example given in the issue. 

    #### 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. Formerly, `log(-x)` incorrectly gave `-log(x)`.

    * physics.units
      * Corrected a semantical error in the conversion between volt and statvolt which
        reported the volt as being larger than the statvolt.

    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 -->
    * piecewise
        * fixed the bug in the case of simplication of parsing when more than one expression are present in boolean formation in piecewise


    <!-- END RELEASE NOTES -->


</details><p>

oscarbenjamin: Looks like this test fails:
https://github.com/sympy/sympy/blob/aaf4a74e9fbe4ba6f3263840286c95bb98438e13/sympy/functions/elementary/tests/test_piecewise.py#L537-L538
With master we have:
```python
In [1]: p = Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True))

In [2]: p
Out[2]: 
⎧1      for x < 2    
⎪                    
⎨2  for x ≤ 3 ∧ x > 1
⎪                    
⎩3      otherwise    

In [3]: simplify(p)
Out[3]: 
⎧1  for x < 2
⎪            
⎨2  for x ≤ 3
⎪            
⎩3  otherwise
```
With the PR we instead have:
```python
In [3]: simplify(p)
Out[3]: 
⎧1      for x < 2    
⎪                    
⎨2  for x ≤ 3 ∧ x > 1
⎪                    
⎩3      otherwise 
```
The `x > 1` condition is superfluous because the first case handles all `x < 2` so it is impossible for `x > 1` to be False in the second case.

It is unfortunate if this PR prevents that simplification but it is more important to fix the bug that gives erroneous output. The change to the code in this PR is correct even if it indirectly affects this test case.


oscarbenjamin: > It is unfortunate if this PR prevents that simplification but it is more important to fix the bug that gives erroneous output. The change to the code in this PR is correct even if it indirectly affects this test case.

I suggest marking the test as XFAIL like this:
```diff
diff --git a/sympy/functions/elementary/tests/test_piecewise.py b/sympy/functions/elementary/tests/test_piecewise.py
index 0b53cfd9da..b9259f2062 100644
--- a/sympy/functions/elementary/tests/test_piecewise.py
+++ b/sympy/functions/elementary/tests/test_piecewise.py
@@ -26,7 +26,7 @@
 from sympy.sets.contains import Contains
 from sympy.sets.sets import Interval
 from sympy.solvers.solvers import solve
-from sympy.testing.pytest import raises, slow
+from sympy.testing.pytest import raises, slow, XFAIL
 from sympy.utilities.lambdify import lambdify
 
 a, b, c, d, x, y = symbols('a:d, x, y')
@@ -534,8 +534,13 @@ def test_piecewise_simplify():
         ).simplify() == Piecewise((1, (x >= 2) & (x < oo)), (nan, True))
     assert Piecewise((1, x < 2), (2, (x > 1) & (x < 3)), (3, True)
         ). simplify() == Piecewise((1, x < 2), (2, x < 3), (3, True))
-    assert Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True)
-        ).simplify() == Piecewise((1, x < 2), (2, x <= 3), (3, True))
+
+    # See test_piecewise_simplify_xfail_redundant_inequality below
+    #
+    # assert Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True)
+    #     ).simplify() == Piecewise((1, x < 2), (2, x <= 3), (3, True))
+    #
+
     assert Piecewise((1, x < 2), (2, (x > 2) & (x < 3)), (3, True)
         ).simplify() == Piecewise((1, x < 2), (2, (x > 2) & (x < 3)),
         (3, True))
@@ -546,6 +551,14 @@ def test_piecewise_simplify():
         (3, True))
 
 
+@XFAIL
+def test_piecewise_simplify_xfail_redundant_inequality():
+    # Uncomment this case in test_piecewise_simplify() when it is fixed.
+    # See https://github.com/sympy/sympy/pull/25605
+    assert Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True)
+        ).simplify() == Piecewise((1, x < 2), (2, x <= 3), (3, True))
+
+
 def test_piecewise_solve():
     abs2 = Piecewise((-x, x <= 0), (x, x > 0))
     f = abs2.subs(x, x - 2)
```

Patch:
diff --git a/sympy/functions/elementary/piecewise.py b/sympy/functions/elementary/piecewise.py
index 629f59ec38..18b1cfcc9b 100644
--- a/sympy/functions/elementary/piecewise.py
+++ b/sympy/functions/elementary/piecewise.py
@@ -1190,7 +1190,7 @@ def include(c, x, a):
                     if b.is_infinite:
                         c = (x > a)
                     else:
-                        c = (x <= b)
+                        c = And(a < x, x <= b)
                 else:
                     if a in covered:
                         c = (x < b)


Test patch:
diff --git a/sympy/functions/elementary/tests/test_piecewise.py b/sympy/functions/elementary/tests/test_piecewise.py
index 2d4de12b28..7d7dca88ba 100644
--- a/sympy/functions/elementary/tests/test_piecewise.py
+++ b/sympy/functions/elementary/tests/test_piecewise.py
@@ -26,7 +26,7 @@
 from sympy.sets.contains import Contains
 from sympy.sets.sets import Interval
 from sympy.solvers.solvers import solve
-from sympy.testing.pytest import raises, slow
+from sympy.testing.pytest import raises, slow, XFAIL
 from sympy.utilities.lambdify import lambdify
 
 a, b, c, d, x, y = symbols('a:d, x, y')
@@ -534,8 +534,13 @@ def test_piecewise_simplify():
         ).simplify() == Piecewise((1, (x >= 2) & (x < oo)), (nan, True))
     assert Piecewise((1, x < 2), (2, (x > 1) & (x < 3)), (3, True)
         ). simplify() == Piecewise((1, x < 2), (2, x < 3), (3, True))
-    assert Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True)
-        ).simplify() == Piecewise((1, x < 2), (2, x <= 3), (3, True))
+
+    # See test_piecewise_simplify_xfail_redundant_inequality below
+    #
+    # assert Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True)
+    #     ).simplify() == Piecewise((1, x < 2), (2, x <= 3), (3, True))
+    #
+
     assert Piecewise((1, x < 2), (2, (x > 2) & (x < 3)), (3, True)
         ).simplify() == Piecewise((1, x < 2), (2, (x > 2) & (x < 3)),
         (3, True))
@@ -544,6 +549,17 @@ def test_piecewise_simplify():
     assert Piecewise((1, x < 1), (2, (x >= 2) & (x <= 3)), (3, True)
         ).simplify() == Piecewise((1, x < 1), (2, (x >= 2) & (x <= 3)),
         (3, True))
+    # https://github.com/sympy/sympy/issues/25603
+    assert Piecewise((log(x), (x <= 5) & (x > 3)), (x, True)
+        ).simplify() == Piecewise((log(x), (x <= 5) & (x > 3)), (x, True))
+
+
+@XFAIL
+def test_piecewise_simplify_xfail_redundant_inequality():
+    # Uncomment this case in test_piecewise_simplify() when it is fixed.
+    # See https://github.com/sympy/sympy/pull/25605
+    assert Piecewise((1, x < 2), (2, (x <= 3) & (x > 1)), (3, True)
+        ).simplify() == Piecewise((1, x < 2), (2, x <= 3), (3, True))
 
 
 def test_piecewise_solve():