sympy.Float(0.0).is_integer is True, but sympy.Float(1.0).is_integer is None
ezyang opened this issue · comments
>>> import sympy
>>> sympy.Float(0.0).is_integer
True
>>> sympy.Float(1.0).is_integer
>>> sympy.__version__
'1.13.dev
Seems inconsistent to me. Personally, for my use case I'd prefer it if none of these were treated as integers, but I understand this is counter to what most users of Sympy want. Related #16918
I've always found this decision to be a bit odd. I think the reasoning had something to do with the fact that 0.0 has effectively "infinite precision" unlike other floats. But I do agree that for the purposes of the assumptions making 0.0 not special would be better.
In #25875 we left the Float(0).is_integer behavior intact. @smichr do you remember if there was a specific reason for this, or if it was just done because it was simplest?
The first step here is to try changing Float(0).is_integer to give None and seeing what tests break.
See OP of #25875 and your comments in the PR. Floats other than 0.0 do not behave as rationals in operations.
So I think actually the main reason for this is that there is a distinct is_zero
assumption, and we want Float(0.0).is_zero
to give True. But in the assumptions, zero -> integer -> rational, so you can't have one without the others.
Actually, given where is_zero is used, I'm not so sure this would be straightforward to fix. (a - b).is_zero
is used a way of testing if a == b. If we change this, a lot of code will probably start working differently. It could be a similar situation to the recent change in Float equality, where we decide it's worth doing anyways.
Just tried remove it and I discovered something else: even if you remove _eval_is_zero
from Float
, Float(0).is_zero
still evaluates to True
. That's because Float.is_extended_real
is set to True, and Float.is_extended_positive
and Float.is_extended_negative
both evaluate based on the sign of the Float. But the assumptions have the fact
'extended_real == extended_negative | zero | extended_positive',
So it is able to deduce is_zero = True
.
We definitely don't want to remove the ability for the assumptions to evaluate inequalities on Floats, and we don't want to remove any of these basic facts from the real number assumptions.
So to make it work, you have to Float.is_real = True
and Float.is_extended_real = True
, i.e.,
diff --git a/sympy/core/numbers.py b/sympy/core/numbers.py
index b497a1bedc..0ddb382188 100644
--- a/sympy/core/numbers.py
+++ b/sympy/core/numbers.py
@@ -758,8 +758,8 @@ class Float(Number):
is_irrational = None
is_number = True
- is_real = True
- is_extended_real = True
+ # is_real = True
+ # is_extended_real = True
is_Float = True
@@ -974,8 +974,8 @@ def _eval_is_infinite(self):
return False
def _eval_is_integer(self):
- if self._mpf_ == fzero:
- return True
+ # if self._mpf_ == fzero:
+ # return True
if not int_valued(self):
return False
@@ -1003,8 +1003,8 @@ def _eval_is_extended_positive(self):
return False
return self.num > 0
- def _eval_is_zero(self):
- return self._mpf_ == fzero
+ # def _eval_is_zero(self):
+ # return self._mpf_ == fzero
def __bool__(self):
return self._mpf_ != fzero
This makes Float(0).is_zero
give None (and also is_integer and is_rational).
I'm seeing what tests fail with this now and will report back.
Here are the test failures:
================================================================================ FAILURES =================================================================================
_______________________________________________________________________ test_all_classes_are_tested _______________________________________________________________________
sympy/core/tests/test_args.py:50: in test_all_classes_are_tested
with open(os.path.join(root, file), encoding='utf-8') as f:
E FileNotFoundError: [Errno 2] No such file or directory: '/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/algebras/.#quaternion.py'
_______________________________________________________________________________ test_round ________________________________________________________________________________
sympy/core/tests/test_expr.py:2080: in test_round
assert all(S(fi).round(p).is_Float for p in (-1, 0, 1))
E assert False
E + where False = all(<generator object test_round.<locals>.<genexpr> at 0x16ddb3bc0>)
_______________________________________________________________________________ test_Float ________________________________________________________________________________
sympy/core/tests/test_numbers.py:502: in test_Float
assert Float('0.0').is_zero is True
E AssertionError: assert None is True
E + where None = 0.0.is_zero
E + where 0.0 = Float('0.0')
_________________________________________________________________________ test_Mul_Infinity_Zero __________________________________________________________________________
sympy/core/tests/test_numbers.py:881: in test_Mul_Infinity_Zero
assert _inf*Float(0) is nan
E assert (inf * 0.0) is nan
E + where 0.0 = Float(0)
____________________________________________________________________________ test_cse_multiple ____________________________________________________________________________
sympy/printing/tests/test_llvmjit.py:184: in test_cse_multiple
assert isclose(res[0], jit_res[0])
E assert False
E + where False = isclose(0.0100000000000000, 2.25000000000000)
____________________________________________________________________________ test_lambdify_cse ____________________________________________________________________________
sympy/utilities/tests/test_lambdify.py:1691: in test_lambdify_cse
case.assertAllClose(result)
sympy/utilities/tests/test_lambdify.py:1636: in assertAllClose
assert abs_err/abs(r) < reltol
sympy/core/relational.py:516: in __bool__
raise TypeError("cannot determine truth value of Relational")
E TypeError: cannot determine truth value of Relational
DO *NOT* COMMIT!
========================================================================= short test summary info =========================================================================
FAILED sympy/core/tests/test_args.py::test_all_classes_are_tested - FileNotFoundError: [Errno 2] No such file or directory: '/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/algebras/.#quaternion.py'
FAILED sympy/core/tests/test_expr.py::test_round - assert False
FAILED sympy/core/tests/test_numbers.py::test_Float - AssertionError: assert None is True
FAILED sympy/core/tests/test_numbers.py::test_Mul_Infinity_Zero - assert (inf * 0.0) is nan
FAILED sympy/printing/tests/test_llvmjit.py::test_cse_multiple - assert False
FAILED sympy/utilities/tests/test_lambdify.py::test_lambdify_cse - TypeError: cannot determine truth value of Relational
Here's a PR with the change so you can see the failures on CI #26621
The zero-ness of 0.0 is, I suppose, the crux of other issues like this one:
>>> sympy.Float(0.0) + sympy.Float(0.0)
0
I'd prefer not to lose the float-ness in this case haha
I don't know if that's directly related. It still does that in my test branch.