Repository: sympy/sympy
Pull request: Update RisingFactorial eval for k noninteger, x negative integer
#### References to other Issues or PRs
Closes #17166.

#### Brief description of what is fixed or changed
`RisingFactorial(x, k)` where `k` is noninteger, `x` is negative integer returns `0`.

#### Release Notes

<!-- BEGIN RELEASE NOTES -->
* functions
  * RisingFactorial(x, k) where k is a noninteger, x is a negative integer automatically evaluates to 0.
<!-- END RELEASE NOTES -->


Problem statement:
Automatic evaluation of RisingFactorial(n, k) with n negative integer, k non-integer
```
>>> RisingFactorial(-1,pi)
RisingFactorial(-1, pi)
>>> N(RisingFactorial(-1,pi))
0
```

This could be evaluated automatically. Note that this causes problems when used in larger expressions, for example:

```
>>> N(asech(RisingFactorial(-1,pi)))
Traceback (most recent call last):
  File "/usr/local/lib/python3.6/dist-packages/sympy/core/evalf.py", line 1308, in evalf
    rf = evalf_table[x.func]
KeyError: asech

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/usr/local/lib/python3.6/dist-packages/sympy/core/evalf.py", line 1537, in N
    return sympify(x).evalf(n, **options)
  File "/usr/local/lib/python3.6/dist-packages/sympy/core/evalf.py", line 1442, in evalf
    result = evalf(self, prec + 4, options)
  File "/usr/local/lib/python3.6/dist-packages/sympy/core/evalf.py", line 1314, in evalf
    xe = x._eval_evalf(prec)
  File "/usr/local/lib/python3.6/dist-packages/sympy/core/function.py", line 586, in _eval_evalf
    v = func(*args)
  File "/usr/local/lib/python3.6/dist-packages/mpmath/ctx_mp_python.py", line 1035, in f_wrapped
    retval = f(ctx, *args, **kwargs)
  File "/usr/local/lib/python3.6/dist-packages/mpmath/functions/functions.py", line 130, in asech
    def asech(ctx, z): return ctx.acosh(ctx.one / z)
  File "<string>", line 7, in __div__
  File "/usr/local/lib/python3.6/dist-packages/mpmath/libmp/libmpf.py", line 960, in mpf_div
    raise ZeroDivisionError
ZeroDivisionError
```

Hints from issue discussion:

Commit message: assert unchanged(rf, -3, x)

Patch to review:
diff --git a/sympy/functions/combinatorial/factorials.py b/sympy/functions/combinatorial/factorials.py
index bafc91a06925..297cec63f127 100644
--- a/sympy/functions/combinatorial/factorials.py
+++ b/sympy/functions/combinatorial/factorials.py
@@ -577,6 +577,10 @@ def eval(cls, x, k):
                             return 1/reduce(lambda r, i:
                                             r*(x - i),
                                             range(1, abs(int(k)) + 1), 1)
+        else:
+            if k.is_number:
+                if x.is_integer and x.is_negative:
+                    return S.Zero
 
     def _eval_rewrite_as_gamma(self, x, k, **kwargs):
         from sympy import gamma
diff --git a/sympy/functions/combinatorial/tests/test_comb_factorials.py b/sympy/functions/combinatorial/tests/test_comb_factorials.py
index 859474ee8691..2d02f975d52d 100644
--- a/sympy/functions/combinatorial/tests/test_comb_factorials.py
+++ b/sympy/functions/combinatorial/tests/test_comb_factorials.py
@@ -1,7 +1,7 @@
 from sympy import (S, Symbol, symbols, factorial, factorial2, Float, binomial,
                    rf, ff, gamma, polygamma, EulerGamma, O, pi, nan,
                    oo, zoo, simplify, expand_func, Product, Mul, Piecewise,
-                   Mod, Eq, sqrt, Poly, Dummy)
+                   Mod, Eq, sqrt, Poly, Dummy, I)
 from sympy.core.expr import unchanged
 from sympy.core.function import ArgumentIndexError
 from sympy.functions.combinatorial.factorials import subfactorial
@@ -30,6 +30,10 @@ def test_rf_eval_apply():
     assert rf(oo, -6) == oo
     assert rf(-oo, -7) == oo
 
+    assert rf(-1, pi) == 0
+    assert rf(-5, 1 + I) == 0
+    assert unchanged(rf, -3, x)
+
     assert rf(x, 0) == 1
     assert rf(x, 1) == x
     assert rf(x, 2) == x*(x + 1)


Code review comments:
diff_hunk: @@ -577,6 +577,10 @@ def eval(cls, x, k):
                             return 1/reduce(lambda r, i:
                                             r*(x - i),
                                             range(1, abs(int(k)) + 1), 1)
+        else:
+            if k.is_number:
+                if x.is_integer and x.is_negative:
+                    return S.Zero
path: sympy/functions/combinatorial/factorials.py
text: @user1:
Do we need the `is_number` check? Should this not also evaluate for `Symbol('x', integer=True, negative=True)`?

@user1:
Oh I see that the check is for `k.is_number` rather than for `x`. Still though what is the check for? Should this not also evaluate for symbolic k?