Open saraedum opened 6 months ago
@videlec this is biting me in the harmonic differentials code of sage-flatsurf.
Here's a simplified reproducer without number fields:
sage: x = loads(b'x\x9c\x9dTi\x97\x13E\x14\xcd\x0c\xa0\xd0\xca"2\xb8+\xa0@@\x89\xd9&\x0bn\x803 \x06\xc2X\x02\xb6\x0b\x96\x9dJ\xa5\xab\x93\xea\xe5V\xaa\xe78\x9e\xe39~\xe9\xe4\xe4_[\xddI\x98\xcc\x18\xe6\x8c|\xabWu\xdf}\xef\xdd\xbaU\xff\xac\xb2\xa1\xe3\xf2\x82\xf2\x02wXP\xdc\x91T:\x7f\xedX\xbe3\xe0\x94K\xee\xf3@[\xc8-\x07\x11\xb3z`\x16w=.\xbb\x16V\xae\x11\xec\xa1\x03:\x8e\xb2nK\x97w\x94\xe3\xb1\x14n\xe1\xc8\x12D{\xf3O\xcd\x83\xa1\x17\x06\x9b\xf3\x92G\x0f \xba\xc7\x03\xae\x1c\x1d*\x0b\xc7\xa6\xb0\xa1V1\xd3\xb1\xe2\x85\x9e\xc3\xcc\xc1\x8e\xe5\xa6\x18\x8f\xd1YL\xe3 \xf2\xd8@r\x0b\xaf\xe4_\x94#\xc3p\x10G\xd4\x95a\'\xed\xf4U\xbb\x99\xcb\xe5\x16\xda\x08b\xbf\xc3\x15\xed\xa5\xe3\xee\r\xdaY\x90\xe9@\xb7\xb9J\')\xe3x\x82\x13\x04V\xcbj\xad\x8e\xf0Z~\x8f\x86\x8e6\x10\xa3c\x96n\x91Y8\x13\xf2u#\xe4\xc9Ex\x14\xca\x9d \xf4=G.,\xe9.\x89\xf4\x8cd[/<\xc1\xa9\xfc!\xd8\xcc\x19ea0\x95\xc5h;W\x8c\xee\x12\x13\x83\xb1p:/N\xda+F\x9a\x1d\x9cI\xf0F{\xa2q\x96\xe0\xcdg8\xb7t\xc6\xa9\x97\x9eGX\xb3WM\xee\x8d2\xce\'x\x8b\xe0m\xb1\x96\x91\x15\xf1N\x82w\t\xde\x9b\xc5%\xbc\x9f\xe0\x03\x82\x0f\xf9\xc4\x14\xf8\x88\xe0B\xb6\xed\xe0b\x82Kmq\xb1=ik|\xfc7>\xb1\x8f\x99m&8\x1b\xe0\xf2d\xa8q\x85\xe0\xea2\x8f\x9104R\xe4\x0f\xf0\xd5\xee\xa0\x8f\x95\xc3\x06\xdc\xf8\xeb\x9a8\x95\x17f\xb4\xeby\xf1\xbc\xefO\x13|FpC\xac\t\xd3q\x81\xe0s\xb32\xbd\x16\tJY\xafe\x82J\x82*\xc1\xfa\x7f\x9e\x8d\x1f\xf5<\x8bRf\x02\xcd)M_\xc4\xfd@s\xb5=\xbb\xfb\x99\xfd\xe7\x1e*Y\xa8\xfdo\x8eyr\xd1B\xfdane2B\x83\xa0\xb9\x8cF\xed\xa3\x99\xbax!\xff\xe6a\xb2\xf6\x17\xfd"-j\x1f5Z\x91\xf6\xc6\x13|9\xc6W\x04_\xdb\xf5\xf4N\x0b\xddF5f\xd1v?\xe8W]\xc1\x02U\xee\xf6\xb9\\\xef\xe8z\xa8\xbb\xda\xed\xc8rS\xc9R\x8dc]3\xd7/\xf5\xeb\xb5\x86\x7f\xab\x88oZ\x17\xc6\xb8Ep[\xdc\x14{\xe87pg\x8co\t6^\x9a^\x1a\xfa\xcd\x94\xfe.\xc1\xbd1\xbe#\xb8\xdfZ\x19\xe3{\x82\xd6\x08\x0f\x08\x1e\xb2\xc3|\x00\xf3\xaf\x92"v\xba\xa9\xd5\x99\x11)\xf3\xfd\xc2\xcf\xb0\xb9\x1fc\xae\xb7\x9d\x17W_\xb6\xc2A\xd4t\x08e\xdc\xfeh\x91\xdb|\x05\xdc5\xa6\xce\xfa\x9a\x07\xd8\xb2\xd7\x8ct\x95(\xf0y\xcc\x8a\xdb\xfd\xa6\xf65\xefwU\xb4.\xf0C\x02B\xf0\xa3\xd8\xb2\xcfg\xd6\xf7\xcbR\x0e*\xfdZ\\n\xb2\xc1\xb0Q)\xf5+C<N\xf0\x84\xe0\xa9\x98\x12\xf5jq\xa4"\xb7\x12\x8bjO\x95C\xaf\xea\xd6\xd1\xc0O\tl\x82\x9f5~!\xf8u\x84\xdf\x08\x9e%\xf8\x9d\x80\x8e\xf0\x07\x81S\xf8\x17T\xa3M\xa8')
sage: x.parent()
Real Lazy Field
sage: x > 0
True
sage: float(x) < 0
True
Note that float(x)
is implemented as float(x._value)
and ._value
is an algebraic real. So maybe this is related to #37927?
Then again, ._value
is just a ANExtensionElement
here. So maybe that's unrelated.
Note that
float(x)
is implemented asfloat(x._value)
and._value
is an algebraic real. So maybe this is related to #37927?
I do not think so (I am following your second definition of x
). Even though the enclosure is super close to zero and the minimal polynomial gigantic it looks correct
sage: p = x._value.minpoly()
sage: a = min(p.roots(RealIntervalField(2048), False))
sage: a.intersection(x._value._value) # one would get an error if there was no intersection
6.62...e-30
Though the enclosure contains zero
sage: x._value._value.contains_zero()
True
which makes it impossible (without further refinement) to determine the sign.
In the description you wrote
sage: RealField(63) < 0
True
sage: RealField(64) > 0
True
Maybe you wanted instead
sage: RealField(63)(x) < 0
True
sage: RealField(64)(x) > 0
True
For the second x
you defined, I get a different behavior
sage: float(x) > 0
False
sage: x > 0
True
sage: float(x) > 0
True
And in my case, the real lazy field is not involved
sage: float(x._value) > 0
False
sage: x._value > 0
True
sage: float(x._value) > 0
True
If we want the sign of float(x)
to be correct one needs a smarter AlgebraicReal.real_number
. Though I am not sure we want to slow down such method.
The value changes quite a bit in between
sage: float(x)
-6.776263578034403e-21
sage: x > 0
True
sage: float(x)
6.629999776684485e-30
It seems to me that the description of the method AlgebraicReal.real_number
is wrong here
def real_number(self, field):
"""
Given a :class:`RealField`, compute a good approximation to ``self`` in
that field. The approximation will be off by at most two
ulp's, except for numbers which are very close to 0, which
will have an absolute error at most
``2**(-(field.prec()-1))``. Also, the rounding mode of the
field is respected.
...
"""
My conclusion (for the time being):
float(x)
or RealField(prec)(x)
when x
is exact. Though on some exact numbers this is specified in one way or another.float(x)
to be as fast as possible (not being too careful about precision). In particular, I do not find it shocking for the sign of float(x)
to be wrong sometimes, especially when x
is close to zero.@videlec I see. I expected it to be the best approximation as a Python float but I see that there seems to be simply no documentation about what __float__
actually does.
What's weird is that there is a coercion from my original x's parent to RDF.
sage: RDF.coerce(x) < 0
True
Now, coercion to inexact rings is a weird animal anyway. But this feels wrong then.
sage: RDF.coerce(x) == RDF.coerce(QuadraticField(2, embedding=1).coerce(x))
False
It is true that the existence of a coercion suggest that RDF(x)
should not depend on the approximation carried by x
. In particular, the behavior of https://github.com/sagemath/sage/issues/37983#issuecomment-2105982840 looks wrong in that direction.
I think that based on your example, one can build two exact numbers x
and y
with the same parent, that compare equal and such that RDF(x)
and RDF(y)
are different (this is pretty straightforward to do in QQbar
using the expression trees).
Just to clarify, I can work around this in the context of harmonic differentials. I just don't trust the sign of floats anymore.
I think that the behavior of float(x)
and RealField(prec)(x)
ought to be specified in sage. And as you mentioned, the coercion tend to suggest that it should be the closest element in the target.
To do so, one would need to plug in the right places the single function
approx: prec -> ball
or approx: prec -> interval
that converges to a real number as prec
tends to infinityfield
field
that is closest (depending on the rounding) to the real number defined by approx
Maybe such function already exists somewhere in sage?
But then come the question: what should be RBF(x)
and RIF(x)
? Since there are coercions AA -> RBF
and AA -> RIF
one could argue the same. Computing the closest interval certainly makes sense. But the closest ball seems like a weird concept to me. And beyond that, it is not completely clear to me whether this is the desirable behavior for RBF
and RIF
. Many times, you just want a ball or interval that is computed at the precision of RBF
or RIF
. This is what is done when you call functions as in RBF(1/3).zeta()
.
Why should there be a coercion in these cases? (I mean, apart from not breaking existing code.)
Why should there be a coercion in these cases? (I mean, apart from not breaking existing code.)
That is a good question. The main purpose of coercion is to have $x + y$ works when $x$ and $y$ have different parents. But I do not really see a use case here.
One could also question the purpose of coercions AA -> RR
and AA -> RDF
.
Steps To Reproduce
In a lengthy computation I manage to create a broken number field (element):
Note that somehow the number field is broken. Copying the element over into a "fresh copy" of the quadratic number field, fixes things:
Expected Behavior
The
float
of a positive number should be non-negative.Actual Behavior
The
float
of a positive number is negative.Additional Information
I couldn't find an issue like that reported. I am not sure how complex embeddings work which seem to be the underlying machinery here. Maybe it's another instance of interval arithmetic not doing the right thing?
Environment
I tried this with SageMath 10.3 from ArchLinux and SageMath 10.2 from conda-forge.
Checklist