#12121: floor/ceil can be very slow at integral values
-------------------------------------+-------------------------------------
Reporter: dsm | Owner: AlexGhitza
Type: defect | Status: needs_work
Priority: major | Milestone: sage-7.2
Component: basic arithmetic | Resolution:
Keywords: | Merged in:
Authors: Vincent Delecroix | Reviewers: Marc Mezzarobba
Report Upstream: N/A | Work issues:
Branch: | Commit:
u/vdelecroix/12121 | 602e5158c5f37c6900a1dee8e27a228114af1319
Dependencies: | Stopgaps:
-------------------------------------+-------------------------------------
Changes (by mmezzarobba):
* status: needs_review => needs_work
Comment:
Okay, I'm back. Sorry that it took so long.
Replying to [comment:41 vdelecroix]:
> > * what don't you like about `unique_integer()`?
>
> an `assert` does not cost anything. And `unique_integer` silently fails
if the interval does not enclose a unique integer.
Uh? No, it doesn't.
> > * is it really better to have an absolute bound for the diameter that
makes us suspect we found an exact integer, rather than something that
depends on the precision?
>
> the precision of what? there is the field used for the evaluation which
is different from the diameter of the interval. If you have more than one
integer in your interval which one are you using to test equality?
The precision of the interval computation, i.e. `bits`. The idea being
that if we found an interval of width (say) 2⁻²⁰ containing an integer by
computing with 1000 bits of precision, we may want to see if the width of
the interval keeps decreasing when the precision increases and only run
the symbolic part of the algorithm if that's the case. But I agree that
this is a very minor issue at best.
> > * why do you insist on using `==` on symbolic expressions instead of
`is_trivial_zero()`?
>
> Because I want to check equality with an integer. Not if it is a trivial
equality.
I mean using `is_trivial_zero()` after calling `full_simplify()` & co, as
in my version: this is safer than relying on `==` and essentially as
powerful.
> > * do we really need two loops that do essentially the same thing
(including raising errors with the exact same message)?
>
> The equality test is potentially costly. And we want to avoid it as much
as possible. In particular, it makes no sense to test this equality within
each step of the loop as it is in your version.
Yes—as I said, my version was just a rough sketch of the changes I'd b
tempted to make, nothing finished. As the code I was starting with used to
loop forever in the typical situation where mine would do the symbolic
test repeatedly (that is, `x` ∈ ℤ integer but we don't manage to prove
it), I thought the additional cost would be acceptable. `;-)` But anyway,
it is not hard to do the test only once while avoiding the code
duplication.
> On a related note, I noticed that for `round` you need to test equality
with elements of `ZZ + 1/2`.
Couldn't you just compute ceil(x-1/2)?
------
Now for some comments on the current code:
* To summarize the above, I still think the main logic in
`incremental_rounding()` could be shortened to something like (not
tested):
{{{
unique_rounding = getattr(RealIntervalFieldElement, 'unique_' + mode)
r = RR.one() >> 20
bits = 64
candidate = None
while bits < maximum_bits:
interval = RealIntervalField(bits)(x) # may raise a TypeError
try:
return unique_rounding(interval)
except ValueError:
pass
if candidate is None and interval.absolute_diameter() > r:
candidate = interval.unique_integer()
try:
delta = x - candidate
if (delta.is_zero()
or
SR(delta).full_simplify().canonicalize_radical()
.is_trivial_zero()
or QQbar(delta).is_zero()):
return candidate
except (TypeError, ValueError):
pass
bits *= 2
}}}
* I'd also make `incremental_rounding()` private and dispense with the
argument checking (and perhaps move it to `real_mpfi` if your plan is to
use it from elsewhere)—but I'm okay with keeping it as it is. An advantage
of making it private is that you could take the “unique rounding” function
on intervals as input instead of accessing it via `getattr()`. Another
option would be to introduce a common base class for `Function_floor`,
`Function_ceil` and `Function_round`.
* I'm a little uneasy about the changes you made to
`BuiltinFunction.__call__()`. The fact that it used to convert non-Element
inputs to Elements looks intentional and pretty reasonable to me. Is it
really necessary to change that behavior? That being said, I'm a bit lost
in the maze of `Function.__call__`, `BuiltinFunction.__call__`, `_eval_`,
`_evalf_`, `_evalf_try_` and friends, so if you tell me you are confident
that the change is correct I'll trust you!
* If these changes stay, then I guess this
{{{
if module is not None:
func = getattr(module, self._name, None)
if func is None and self._alt_name is not None:
func = getattr(module, self._name, None)
^^^^^
}}}
should be `_alt_name`.
* Note that these changes also make
{{{
sage: sin(numpy.int32(0))
0.0
}}}
which is at odds with
{{{
sage: sin(ZZ(0)).parent()
Integer Ring
}}}
(perhaps not ideal, but predictable at least).
* In `Function_*`, what is the point of calling `_evalf_()` from
`_eval_()`?
* And why isn't the logic for choosing `maximum_bits` in `_evalf_()` the
same in `floor`, `ceil` and `round`?
* I wouldn't bother with checking that `x` is not a relation. First,
`_eval_()` methods of individual functions are probably not the right
place for that (either `BuiltinFunction.__call__()` or perhaps methods
`ceil()`, `floor()` etc. in a future subclass `RelationalExpression` of
`Expression` would be more reasonable). Besides, various other nonsensical
inputs (e.g. series, booleans) are accepted without error, so it is a bit
strange to have an ad hoc check dealing with this one.
--
Ticket URL: <http://trac.sagemath.org/ticket/12121#comment:49>
Sage <http://www.sagemath.org>
Sage: Creating a Viable Open Source Alternative to Magma, Maple, Mathematica,
and MATLAB
--
You received this message because you are subscribed to the Google Groups
"sage-trac" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sage-trac.
For more options, visit https://groups.google.com/d/optout.