Repository: sympy/sympy

Issue:
# Heaviside function simplification under assumptions

It seems that, under certain conditions, SymPy (version 1.0) does not apply assumptions to the Heaviside function. If I understand correctly, an appropriate handler (e.g. `refine_Heaviside`) has not been implemented for the Heaviside function. 

```
import sympy as sp
sp.assumptions.assume.global_assumptions.clear()

""" The code below works as expected and applies the assumption to x"""
x = sp.Symbol('x', positive=True)
print(sp.Heaviside(x))
print(sp.simplify(sp.Heaviside(x)))

""" Every method in this section does not seem to provide the expected result."""
y = sp.Symbol('y')
print(
    sp.refine(
        sp.Heaviside(y),
        sp.assumptions.Q.real(y) & sp.assumptions.Q.positive(y)
    )
)
sp.assumptions.assume.global_assumptions.add(sp.assumptions.Q.real(y))
sp.assumptions.assume.global_assumptions.add(sp.assumptions.Q.positive(y))
print(sp.assumptions.assume.global_assumptions)
print(sp.Heaviside(y))
print(sp.simplify(sp.Heaviside(y), fu=True))
with sp.assumptions.assuming(sp.assumptions.Q.positive(y)):
    print(sp.assumptions.ask(sp.assumptions.Q.positive(sp.Heaviside(y))))

```
Result: 

```
1
1
Heaviside(y)
AssumptionsContext({Q.real(y), Q.positive(y)})
Heaviside(y)
Heaviside(y)
None

```


Implementation patch:
diff --git a/sympy/assumptions/refine.py b/sympy/assumptions/refine.py
index 9180131956cf..4f738a2eec34 100644
--- a/sympy/assumptions/refine.py
+++ b/sympy/assumptions/refine.py
@@ -480,6 +480,34 @@ def refine_sin_cos(expr, assumptions):
         return ((-1)**((k + 1) / 2)) * sin(rem)
 
 
+def refine_Heaviside(expr, assumptions):
+    """
+    Handler for the Heaviside step function.
+
+    Examples
+    ========
+
+    >>> from sympy.assumptions.refine import refine_Heaviside
+    >>> from sympy import Q, Heaviside
+    >>> from sympy.abc import x
+    >>> refine_Heaviside(Heaviside(x), Q.positive(x))
+    1
+    >>> refine_Heaviside(Heaviside(x), Q.negative(x))
+    0
+    >>> refine_Heaviside(Heaviside(x), Q.zero(x))
+    1/2
+
+    """
+    arg, H0 = expr.args
+    if ask(Q.positive(arg), assumptions):
+        return S.One
+    if ask(Q.negative(arg), assumptions):
+        return S.Zero
+    if ask(Q.zero(arg), assumptions):
+        return H0
+    return expr
+
+
 def refine_floor_ceiling(expr, assumptions):
     """
     Handler for the floor and ceiling functions
@@ -531,6 +559,7 @@ def refine_floor_ceiling(expr, assumptions):
     'MatrixElement': refine_matrixelement,
     'cos': refine_sin_cos,
     'sin': refine_sin_cos,
+    'Heaviside': refine_Heaviside,
     'floor': refine_floor_ceiling,
     'ceiling' : refine_floor_ceiling,
 }


Test patch:
diff --git a/sympy/assumptions/tests/test_refine.py b/sympy/assumptions/tests/test_refine.py
index 64f896e2a7b5..bfe09e5d707a 100644
--- a/sympy/assumptions/tests/test_refine.py
+++ b/sympy/assumptions/tests/test_refine.py
@@ -14,6 +14,7 @@
 from sympy.functions.elementary.piecewise import Piecewise
 from sympy.matrices.expressions.matexpr import MatrixSymbol
 from sympy.functions.elementary.integers import floor, ceiling
+from sympy.functions.special.delta_functions import Heaviside
 
 
 def test_Abs():
@@ -305,3 +306,17 @@ def test_floor_ceiling():
 
     assert refine(floor(floor(x)+ floor(y))) == floor(x) + floor(y)
     assert refine(ceiling(ceiling(x) - ceiling(y))) == ceiling(x) - ceiling(y)
+
+
+def test_Heaviside():
+    assert refine(Heaviside(x), Q.positive(x)) == 1
+    assert refine(Heaviside(x), Q.negative(x)) == 0
+    assert refine(Heaviside(x), Q.zero(x)) == S.Half
+    assert refine(Heaviside(x), Q.nonnegative(x)) == Heaviside(x)
+    assert refine(Heaviside(x), Q.nonpositive(x)) == Heaviside(x)
+    assert refine(Heaviside(x), True) == Heaviside(x)
+
+    # custom H0 value
+    assert refine(Heaviside(x, 1), Q.zero(x)) == 1
+    assert refine(Heaviside(x, 1), Q.positive(x)) == 1
+    assert refine(Heaviside(x, 1), Q.negative(x)) == 0