Repository: sympy/sympy
Pull request: Float.is_integer updated for removing disparity in gamma
<!-- 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://github.com/blog/1506-closing-issues-via-pull-requests . Please also
write a comment on that issue linking back to this pull request once it is
open. -->
[1] Fixes https://github.com/sympy/sympy/issues/16847
[2] https://github.com/sympy/sympy/issues/16847#issuecomment-493653572

#### Brief description of what is fixed or changed
Float.is_integer now returns `True` for values like, `-1.0`,  which helps in removing disparity as mentioned in the issue referenced above. 

#### Other comments
ping @smichr @jksuom @ritesh99rakesh 

The following test is failing due to the changes,
```
__________ sympy/core/tests/test_power.py:test_issue_6100_12942_4473 ___________
Traceback (most recent call last):
  File "/home/gagandeep/sympy/sympy/core/tests/test_power.py", line 284, in test_issue_6100_12942_4473
    assert ((x*y)**1.0).func is Pow
AssertionError
```
`(x*y)**1.0` is converted to `x**1.0*y**1.0` returning `Mul` instead of `Pow` therefore failing the test.

#### Release Notes

<!-- Write the release notes for this release below. 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 -->
NO ENTRY
<!-- END RELEASE NOTES -->


Problem statement:
Disparity in gamma function result for non-positive float numbers
As nonpositive integers are poles for gamma function, we have the following:
```
>>> gamma(-1)
zoo
>>> gamma(0)
zoo
```
But for nonpositive integers represented as floats, we get the following error
```
>>> gamma(-1.0)
Traceback (most recent call last):
  File "/home/ritesh/Desktop/sympy/sympy/core/evalf.py", line 1308, in evalf
    rf = evalf_table[x.func]
KeyError: gamma

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/ritesh/Desktop/sympy/sympy/core/cache.py", line 94, in wrapper
    retval = cfunc(*args, **kwargs)
  File "/home/ritesh/Desktop/sympy/sympy/core/function.py", line 463, in __new__
    result = result.evalf(mlib.libmpf.prec_to_dps(pr))
  File "/home/ritesh/Desktop/sympy/sympy/core/evalf.py", line 1442, in evalf
    result = evalf(self, prec + 4, options)
  File "/home/ritesh/Desktop/sympy/sympy/core/evalf.py", line 1314, in evalf
    xe = x._eval_evalf(prec)
  File "/home/ritesh/Desktop/sympy/sympy/core/function.py", line 586, in _eval_evalf
    v = func(*args)
  File "/home/ritesh/venv/lib/python3.6/site-packages/mpmath/ctx_mp_python.py", line 1012, in f
    return ctx.make_mpf(mpf_f(x._mpf_, prec, rounding))
  File "/home/ritesh/venv/lib/python3.6/site-packages/mpmath/libmp/gammazeta.py", line 1933, in mpf_gamma
    raise ValueError("gamma function pole")
ValueError: gamma function pole
```
I think there should be a check like `floor(x) == x` and then we can return `gamma(floor(x))`, this will help us to avoid the `ValueError` as:
```
>>> gamma(floor(-1.0))
zoo
```

Hints from issue discussion:
As said in [this document](http://mhtlab.uwaterloo.ca/courses/me755/web_chap1.pdf) and [this stackexchange thread](https://math.stackexchange.com/questions/705103/what-is-the-domain-of-gamma-function), I think we should not allow computations of gamma function for negative integers and zero.
The following should happen,
```
>>> gamma(-1)
Traceback(most recent call last):
    ...
ValueError: gamma function is not defined for non-positive integers. 
```
SymPy instead of giving the error as `gamma function is not defined for non-positive integers`, returns the result as `zoo` which is `S.ComplexInfinity` similar to [mathworld](http://mathworld.wolfram.com/ComplexInfinity.html). I believe we should return `zoo` instead of `ValueError` since a similar result is expected in other gamma function like `polygamma`.
It would be easy to return `zoo` in `gamma.eval` for nonnegative integer arguments. However, Floats are currently not recognized as integers (apart from `Float(0)`). That will probably be changed when some current work in progress has been finished. Then also floating point integers could be covered by a condition like `arg.is_integer` (instead of `arg.is_Integer`). That would be preferable to `floor(arg) == arg`.
@jksuom Please refer the PR which aims to cover floating point integers by attributes like, `is_integer`. We can refer this issue there for fixing. Moreover, I think using attributes would be better for recognising floating point integers instead of `floor(arg) == arg` as it is faster to access constant attributes than computing `floor(arg)` to the best of my knowledge of sympy till now. Please correct me in a reply if I am wrong in my argument. 
There is no such PR yet but the matter is related to what is done in #16770. It would be easy to update `Float.is_integer` independently. (You could send a PR.) A Float is an integer when the `exp` part (of index 2) of its `_mpf_` is nonnegative. That would be a very quick test.

Commit message: Float.is_integer updated

Patch to review:
diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py
index c2bd28d796ea..6ea6f6fecd04 100644
--- a/sympy/core/numbers.py
+++ b/sympy/core/numbers.py
@@ -1254,7 +1254,7 @@ def _eval_is_infinite(self):
         return False
 
     def _eval_is_integer(self):
-        return self._mpf_ == fzero
+        return self._mpf_[2] == fzero[2]
 
     def _eval_is_negative(self):
         if self._mpf_ == _mpf_ninf:
diff --git a/sympy/functions/special/gamma_functions.py b/sympy/functions/special/gamma_functions.py
index 204c2c6ff944..40221ad35202 100644
--- a/sympy/functions/special/gamma_functions.py
+++ b/sympy/functions/special/gamma_functions.py
@@ -107,7 +107,7 @@ def eval(cls, arg):
                 return S.NaN
             elif arg is S.Infinity:
                 return S.Infinity
-            elif arg.is_Integer:
+            elif arg.is_integer:
                 if arg.is_positive:
                     return factorial(arg - 1)
                 else:


Code review comments:
diff_hunk: @@ -1254,7 +1254,7 @@ def _eval_is_infinite(self):
         return False
 
     def _eval_is_integer(self):
-        return self._mpf_ == fzero
+        return self._mpf_[2] == fzero[2]
path: sympy/core/numbers.py
text: @user1:
This will only work with odd integers. For all integers, we have `self._mpf_[2] >= 0`.