Repository: sympy/sympy

Issue:
How to cast nan to not-nan in an expression
Hi, I'm new to Sympy and I wonder how to cast null value to a default value in a Sympy expression
For example
```
import sympy as sp
x = sp.symbols("x")
# Return 0 if x is of nan value, and 1 otherwise
exp = sp.Piecewise((0.0, sp.Eq(x, sp.nan)), (1.0, True))
sp.lambdify((x), exp)(x=float('nan'))
Out[28]: 1.0
```
The above expression returns 1.0 instead of 0.0
I found that the problem comes from the expression
```
In [29]: sp.Eq(x, sp.nan)
Out[29]: False
```
It seems that `sp.Eq` does symbolic evaluation on variable `x` instead of waiting for the input from the actual calculation

Maybe I'm using sympy in a wrong way. But would appreciate someone to shed some light on how to do this

Thank you

Discussion:

Patch:
diff --git a/sympy/codegen/cfunctions.py b/sympy/codegen/cfunctions.py
index 2eb3176ea8..7b79291f12 100644
--- a/sympy/codegen/cfunctions.py
+++ b/sympy/codegen/cfunctions.py
@@ -12,7 +12,7 @@
 from sympy.core.singleton import S
 from sympy.functions.elementary.exponential import exp, log
 from sympy.functions.elementary.miscellaneous import sqrt
-
+from sympy.logic.boolalg import BooleanFunction, true, false
 
 def _expm1(x):
     return exp(x) - S.One
@@ -532,5 +532,27 @@ def _eval_rewrite_as_Pow(self, arg, **kwargs):
     _eval_rewrite_as_tractable = _eval_rewrite_as_Pow
 
 
-class isnan(Function):
+class isnan(BooleanFunction):
     nargs = 1
+
+    @classmethod
+    def eval(cls, arg):
+        if arg is S.NaN:
+            return true
+        elif arg.is_number:
+            return false
+        else:
+            return None
+
+
+class isinf(BooleanFunction):
+    nargs = 1
+
+    @classmethod
+    def eval(cls, arg):
+        if arg.is_infinite:
+            return true
+        elif arg.is_finite:
+            return false
+        else:
+            return None
diff --git a/sympy/printing/codeprinter.py b/sympy/printing/codeprinter.py
index d58c5f76b1..346a801d30 100644
--- a/sympy/printing/codeprinter.py
+++ b/sympy/printing/codeprinter.py
@@ -519,6 +519,12 @@ def _print_Not(self, expr):
     def _print_BooleanFunction(self, expr):
         return self._print(expr.to_nnf())
 
+    def _print_isnan(self, arg):
+        return 'isnan(%s)' % self._print(*arg.args)
+
+    def _print_isinf(self, arg):
+        return 'isinf(%s)' % self._print(*arg.args)
+
     def _print_Mul(self, expr):
 
         prec = precedence(expr)
diff --git a/sympy/printing/numpy.py b/sympy/printing/numpy.py
index 00713ebc67..37650b0163 100644
--- a/sympy/printing/numpy.py
+++ b/sympy/printing/numpy.py
@@ -19,7 +19,9 @@
     'sign': 'sign',
     'logaddexp': 'logaddexp',
     'logaddexp2': 'logaddexp2',
-    'isnan': 'isnan'
+    'isinf': 'isinf',
+    'isnan': 'isnan',
+
 })
 _known_constants_numpy = {
     'Exp1': 'e',
diff --git a/sympy/printing/pycode.py b/sympy/printing/pycode.py
index 54e88e7e10..7094e07660 100644
--- a/sympy/printing/pycode.py
+++ b/sympy/printing/pycode.py
@@ -41,6 +41,7 @@
     'floor': 'floor',
     'gamma': 'gamma',
     'hypot': 'hypot',
+    'isinf': 'isinf',
     'isnan': 'isnan',
     'loggamma': 'lgamma',
     'log': 'log',


Test patch:
diff --git a/sympy/codegen/tests/test_cfunctions.py b/sympy/codegen/tests/test_cfunctions.py
index ca85758d34..f2bc3992ec 100644
--- a/sympy/codegen/tests/test_cfunctions.py
+++ b/sympy/codegen/tests/test_cfunctions.py
@@ -3,7 +3,7 @@
 from sympy.core.symbol import (Symbol, symbols)
 from sympy.functions.elementary.exponential import (exp, log)
 from sympy.codegen.cfunctions import (
-    expm1, log1p, exp2, log2, fma, log10, Sqrt, Cbrt, hypot
+    expm1, log1p, exp2, log2, fma, log10, Sqrt, Cbrt, hypot, isnan, isinf
 )
 from sympy.core.function import expand_log
 
@@ -163,3 +163,24 @@ def test_hypot():
 
     assert hypot(17*x, 42*y).diff(x).expand(func=True) - 2*17*17*x*((17*x)**2 + (42*y)**2)**Rational(-1, 2)/2 == 0
     assert hypot(17*x, 42*y).diff(y).expand(func=True) - 2*42*42*y*((17*x)**2 + (42*y)**2)**Rational(-1, 2)/2 == 0
+
+
+def test_isnan_isinf():
+    x = Symbol('x')
+
+    # isinf
+    assert isinf(+S.Infinity) == True
+    assert isinf(-S.Infinity) == True
+    assert isinf(S.Pi) == False
+    isinfx = isinf(x)
+    assert isinfx not in (False, True)
+    assert isinfx.func is isinf
+    assert isinfx.args == (x,)
+
+    # isnan
+    assert isnan(S.NaN) == True
+    assert isnan(S.Pi) == False
+    isnanx = isnan(x)
+    assert isnanx not in (False, True)
+    assert isnanx.func is isnan
+    assert isnanx.args == (x,)
diff --git a/sympy/core/tests/test_args.py b/sympy/core/tests/test_args.py
index 6c69e8f555..8ec028503d 100644
--- a/sympy/core/tests/test_args.py
+++ b/sympy/core/tests/test_args.py
@@ -5156,6 +5156,11 @@ def test_sympy__codegen__cfunctions__isnan():
     assert _test_args(isnan(x))
 
 
+def test_sympy__codegen__cfunctions__isinf():
+    from sympy.codegen.cfunctions import isinf
+    assert _test_args(isinf(x))
+
+
 def test_sympy__codegen__fnodes__FFunction():
     from sympy.codegen.fnodes import FFunction
     assert _test_args(FFunction('f'))
diff --git a/sympy/printing/tests/test_c.py b/sympy/printing/tests/test_c.py
index 11836539f0..bd76af28cc 100644
--- a/sympy/printing/tests/test_c.py
+++ b/sympy/printing/tests/test_c.py
@@ -19,7 +19,7 @@
     While, Scope, Print, FunctionPrototype, FunctionDefinition, FunctionCall, Return,
     real, float32, float64, float80, float128, intc, Comment, CodeBlock, stderr, QuotedString
 )
-from sympy.codegen.cfunctions import expm1, log1p, exp2, log2, fma, log10, Cbrt, hypot, Sqrt
+from sympy.codegen.cfunctions import expm1, log1p, exp2, log2, fma, log10, Cbrt, hypot, Sqrt, isnan, isinf
 from sympy.codegen.cnodes import restrict
 from sympy.utilities.lambdify import implemented_function
 from sympy.tensor import IndexedBase, Idx
@@ -881,3 +881,7 @@ def test_ccode_UnevaluatedExpr():
 def test_ccode_array_like_containers():
     assert ccode([2,3,4]) == "{2, 3, 4}"
     assert ccode((2,3,4)) == "{2, 3, 4}"
+
+def test_ccode__isinf_isnan():
+    assert ccode(isinf(x)) == 'isinf(x)'
+    assert ccode(isnan(x)) == 'isnan(x)'
diff --git a/sympy/utilities/tests/test_lambdify.py b/sympy/utilities/tests/test_lambdify.py
index fecb84ae7a..96203a3ccb 100644
--- a/sympy/utilities/tests/test_lambdify.py
+++ b/sympy/utilities/tests/test_lambdify.py
@@ -14,7 +14,7 @@
 from sympy.core.symbol import (Dummy, symbols)
 from sympy.functions.combinatorial.factorials import (RisingFactorial, factorial)
 from sympy.functions.combinatorial.numbers import bernoulli, harmonic
-from sympy.functions.elementary.complexes import Abs
+from sympy.functions.elementary.complexes import Abs, sign
 from sympy.functions.elementary.exponential import exp, log
 from sympy.functions.elementary.hyperbolic import acosh
 from sympy.functions.elementary.integers import floor
@@ -38,7 +38,7 @@
 from sympy.utilities.iterables import numbered_symbols
 from sympy.vector import CoordSys3D
 from sympy.core.expr import UnevaluatedExpr
-from sympy.codegen.cfunctions import expm1, log1p, exp2, log2, log10, hypot
+from sympy.codegen.cfunctions import expm1, log1p, exp2, log2, log10, hypot, isnan, isinf
 from sympy.codegen.numpy_nodes import logaddexp, logaddexp2
 from sympy.codegen.scipy_nodes import cosm1, powm1
 from sympy.functions.elementary.complexes import re, im, arg
@@ -1918,6 +1918,30 @@ def test_assoc_legendre_numerical_evaluation():
     assert all_close(sympy_result_complex, mpmath_result_complex, tol)
 
 
+def test_Piecewise():
+
+    modules = [math]
+    if numpy:
+        modules.append('numpy')
+
+    for mod in modules:
+        # test isinf
+        f = lambdify(x, Piecewise((7.0, isinf(x)), (3.0, True)), mod)
+        assert f(+float('inf')) == +7.0
+        assert f(-float('inf')) == +7.0
+        assert f(42.) == 3.0
+
+        f2 = lambdify(x, Piecewise((7.0*sign(x), isinf(x)), (3.0, True)), mod)
+        assert f2(+float('inf')) == +7.0
+        assert f2(-float('inf')) == -7.0
+        assert f2(42.) == 3.0
+
+        # test isnan (gh-26784)
+        g = lambdify(x, Piecewise((7.0, isnan(x)), (3.0, True)), mod)
+        assert g(float('nan')) == 7.0
+        assert g(42.) == 3.0
+
+
 def test_array_symbol():
     if not numpy:
         skip("numpy not installed.")