Repository: sympy/sympy
Pull request: Fixes 21079 issue, periodicity, solvify for piecewise
`solvify` is now able to solve `piecewise` expressions.
Fixes  #21079 as it was giving an error due to a bug in `periodicity`.
Hence, periodicity for `piecewise` is fixed as `None` as the period of `piecewise` should also be `piecewise`.(which is an inconsistent return type)

```
>>> p = Piecewise((0, x < -10), (x**2 + 5*x - 6, x >= -9))
>>> solvify(p, x, S.Reals) 
        [-6, 1]

>>> periodicity(p, x) is None
        True
```
Closes & Fixes #21079
<!-- BEGIN RELEASE NOTES -->
NO ENTRY
<!-- END RELEASE NOTES -->

Problem statement:
Solvify cannot solve Piecewise
```
>>> p = Piecewise((0, x < -1), (x**2, x <= 1), (log(x), True))
>>> solvify(p,x,S.Reals)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/sayandip/sympy/sympy/solvers/solveset.py", line 2446, in solvify
    period = periodicity(f, symbol)
  File "/home/sayandip/sympy/sympy/calculus/util.py", line 516, in periodicity
    g_s = decompogen(f, symbol)
  File "/home/sayandip/sympy/sympy/solvers/decompogen.py", line 47, in decompogen
    result += [f.subs(f.args[0], symbol)] + decompogen(f.args[0], symbol)
  File "/home/sayandip/sympy/sympy/solvers/decompogen.py", line 37, in decompogen
    raise TypeError('expecting Expr but got: `%s`' % func_name(f))
TypeError: expecting Expr but got: `ExprCondPair`
>>> solveset(p,x,S.Reals)
Union(FiniteSet(0), Interval.open(-oo, -1))

```

Hints from issue discussion:
Related: #20881.

Commit message: docs

Patch to review:
diff --git a/sympy/calculus/tests/test_util.py b/sympy/calculus/tests/test_util.py
index 510fb12741ba..d29f1edfbe55 100644
--- a/sympy/calculus/tests/test_util.py
+++ b/sympy/calculus/tests/test_util.py
@@ -1,6 +1,6 @@
 from sympy import (Symbol, S, exp, log, sqrt, oo, E, zoo, pi, tan, sin, cos,
                    cot, sec, csc, Abs, symbols, I, re, simplify,
-                   expint, Rational)
+                   expint, Rational, Piecewise)
 from sympy.calculus.util import (function_range, continuous_domain, not_empty_in,
                                  periodicity, lcim, AccumBounds, is_convex,
                                  stationary_points, minimum, maximum)
@@ -171,6 +171,9 @@ def test_periodicity():
     assert periodicity((E**x)%3, x) is None
 
     assert periodicity(sin(expint(1, x))/expint(1, x), x) is None
+    # returning `None` for any Piecewise
+    p = Piecewise((0, x < -1), (x**2, x <= 1), (log(x), True))
+    assert periodicity(p, x) is None
 
 
 def test_periodicity_check():
diff --git a/sympy/calculus/util.py b/sympy/calculus/util.py
index 81cdec0d18cc..fad8e13f05df 100644
--- a/sympy/calculus/util.py
+++ b/sympy/calculus/util.py
@@ -1,4 +1,4 @@
-from sympy import Order, S, log, limit, lcm_list, im, re, Dummy
+from sympy import Order, S, log, limit, lcm_list, im, re, Dummy, Piecewise
 from sympy.core import Add, Mul, Pow
 from sympy.core.basic import Basic
 from sympy.core.compatibility import iterable
@@ -511,6 +511,11 @@ def _check(orig_f, period):
             symbol not in n.free_symbols):
                 period = Abs(n / a.diff(symbol))
 
+    elif isinstance(f, Piecewise):
+        # Returning None, as the return type, period of the `piecewise`
+        # also should be a piecewise. (i.e. the return type is not favorable)
+        return None
+
     elif period is None:
         from sympy.solvers.decompogen import compogen
         g_s = decompogen(f, symbol)
diff --git a/sympy/solvers/solveset.py b/sympy/solvers/solveset.py
index c0d2616a9be4..c39aaea5f7f8 100644
--- a/sympy/solvers/solveset.py
+++ b/sympy/solvers/solveset.py
@@ -2318,6 +2318,8 @@ def solvify(f, symbol, domain):
     ImageSet,   | list (if `f` is periodic)
     Union       |
 
+    Union       | list (with FiniteSet)
+
     EmptySet    | empty list
 
     Others      | None
@@ -2377,7 +2379,16 @@ def solvify(f, symbol, domain):
 
         else:
             solution = solution_set.intersect(domain)
-            if isinstance(solution, FiniteSet):
+            if isinstance(solution, Union):
+                # concerned about only FiniteSet with Union but not about ImageSet
+                # if required could be extend
+                if any(isinstance(i, FiniteSet) for i in solution.args):
+                    result = [sol for soln in solution.args \
+                     for sol in soln.args if isinstance(soln,FiniteSet)]
+                else:
+                    return None
+
+            elif isinstance(solution, FiniteSet):
                 result += solution
 
     return result
diff --git a/sympy/solvers/tests/test_solveset.py b/sympy/solvers/tests/test_solveset.py
index 08d989363dce..5cc2d3746c27 100644
--- a/sympy/solvers/tests/test_solveset.py
+++ b/sympy/solvers/tests/test_solveset.py
@@ -1320,7 +1320,24 @@ def test_solvify():
     raises(NotImplementedError, lambda: solvify(sin(exp(x)), x, S.Complexes))
 
 
+def test_solvify_piecewise():
+    p1 = Piecewise((0, x < -1), (x**2, x <= 1), (log(x), True))
+    p2 = Piecewise((0, x < -10), (x**2 + 5*x - 6, x >= -9))
+    p3 = Piecewise((0, Eq(x, 0)), (x**2/Abs(x), True))
+    p4 = Piecewise((0, Eq(x, pi)), ((x - pi)/sin(x), True))
+
+    # issue 21079
+    assert solvify(p1, x, S.Reals) == [0]
+    assert solvify(p2, x, S.Reals) == [-6, 1]
+    assert solvify(p3, x, S.Reals) == [0]
+    assert solvify(p4, x, S.Reals) == [pi]
+
+
 def test_abs_invert_solvify():
+
+    x = Symbol('x',positive=True)
+    assert solvify(sin(Abs(x)), x, S.Reals) == [0, pi]
+    x = Symbol('x')
     assert solvify(sin(Abs(x)), x, S.Reals) is None
 
 


Code review comments:
diff_hunk: @@ -511,6 +511,11 @@ def _check(orig_f, period):
             symbol not in n.free_symbols):
                 period = Abs(n / a.diff(symbol))
 
+    elif isinstance(f, Piecewise):
+        # Returning None, as the return type, period of the `piecewise`
+        # also should be a piecewise. (i.e. the return type is not favorable)
+        return None
path: sympy/calculus/util.py
text: @user1:
```suggestion
        pass  # not handling Piecewise yet
```