Deprecate usage of non-integers in `scipy.special.factorial` when `exact=True` (and other factorial*-plans)
"h.vetinari--- via SciPy-Dev" <[email protected]> Tue, 19 Dec 2023 10:54:35 -0000
| Newsgroups | gmane.comp.python.scientific.devel |
|---|---|
| Message-ID | <[email protected]> |
In the big factorial overhaul [1] a while ago, one of the things where I tried
to be somewhat lenient (given that some minor breaking changes were necessary to
align the wildly different behaviour across factorial, factorial2 & factorialk),
was to keep allowing usage of `factorial(<float>, exact=True)`, even though it
had only worked more or less by accident when the floats where actually integers.
For scipy 1.10:
```
>>> factorial(1.1, exact=True)
ValueError: math.factorial() only accepts integral values # python <3.10
TypeError: 'float' object cannot be interpreted as an integer # python >=3.10
>>> factorial(1.0, exact=True)
1 # python <3.10
TypeError: 'float' object cannot be interpreted as an integer # python >=3.10
>>> factorial([1.1], exact=True)
ValueError: math.factorial() only accepts integral values # python <3.10
TypeError: 'float' object cannot be interpreted as an integer # python >=3.10
>>> factorial([1.0], exact=True)
array([1])
```
However, the array case had been so broken before for non-integer floats that I
didn't bother fixing it. Therefore, as of 1.11, we had:
```
>>> factorial(1.1, exact=True) # no warning
1.0464858468535607
>>> factorial(1.0, exact=True) # no warning
1.0
>>> factorial([1.1], exact=True) # error where n!=n.astype(int)
ValueError: factorial with exact=True does not support non-integral arrays
>>> factorial([1.0], exact=True) # DeprecationWarning where n==n.astype(int)
array([1])
```
I now want to close this gap between the scalar and array case, and deprecate
passing any floats to factorial when `exact=True`. Aside from also becoming more
consistent with the closely related factorial{2,k} functions (which were even
more broken than the factorial, and consequently never got this float-support
grandfathered in), there's no more (or less) "exactness" for the float case when
specifying `exact=True`, so at best it's useless and at worst it's misleading.
For 1.13 (after executing the deprecation w.r.t. the array-case from 1.11),
this would look like:
```
>>> factorial(1.0, exact=True) # DeprecationWarning
>>> factorial([1.0], exact=True) # ValueError
```
This proposal already has an open PR [2], with a clear indication to use
`exact=False` if non-integral arguments are required.
FWIW, I have some more factorial PRs in the pipeline, which should bring support
for `exact=False` also to factorialk, and later, universal float support via the
analytical continuation as an opt-in, for those who don't mind that some values
change slightly (e.g. the complex continuation of the double-factorial is off
by a factor of sqrt(2/pi) at even integers compared to the "natural" definiton).
Some plans for this are outlined here [3].
Comments welcome.
[1] https://github.com/scipy/scipy/pull/15841
[2] https://github.com/scipy/scipy/pull/19703
[3] https://github.com/scipy/scipy/issues/18409
_______________________________________________
SciPy-Dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3/lists/scipy-dev.python.org/
Member address: [email protected]