Open dkrenn opened 9 years ago
Branch: u/dkrenn/rif/bisect
Hello Daniel,
This is cool!
I guess that the line
verbose('iteration %s with results in %s of %s cells' %
(iteration, len(result), len(open)), level=2)
is quite bad for performances. The string formatting is done whatever the verbose function is doing. Did you do some profiling?
Just for curiosity, do you know how good is fast_callable
with interval fields? It looks pretty bad on the following example
sage: f(x) = log(exp(x*sin(x)) + exp(x*cos(x)))
sage: F = fast_callable(f, domain=RIF)
sage: r = RIF(0.1,0.2)
sage: %timeit F(r)
10000 loops, best of 3: 54.6 µs per loop
sage: %timeit ((r*r.sin()).exp() + (r*r.cos()).exp()).log()
10000 loops, best of 3: 32.8 µs per loop
compared to reals
sage: F = fast_callable(f, domain=RR)
sage: r = 0.1
sage: %timeit F(r)
100000 loops, best of 3: 12.4 µs per loop
sage: %timeit ((r*r.sin()).exp() + (r*r.cos()).exp()).log()
10000 loops, best of 3: 16.5 µs per loop
Vincent
Hi Vincent,
Replying to @videlec:
I guess that the line
verbose('iteration %s with results in %s of %s cells' % (iteration, len(result), len(open)), level=2)
is quite bad for performances. The string formatting is done whatever the verbose function is doing. > Did you do some profiling?
No, I didn't do any with this line included (I did on my old code, which used if and print für debugging).
Just for curiosity, do you know how good is
fast_callable
with interval fields?
Don't know. I used it with some larger symbolic expressions, and what I remember, I had a significant speedup there.
It looks pretty bad on the following example
sage: f(x) = log(exp(x*sin(x)) + exp(x*cos(x))) sage: F = fast_callable(f, domain=RIF) sage: r = RIF(0.1,0.2) sage: %timeit F(r) 10000 loops, best of 3: 54.6 µs per loop sage: %timeit ((r*r.sin()).exp() + (r*r.cos()).exp()).log() 10000 loops, best of 3: 32.8 µs per loop
compared to reals
sage: F = fast_callable(f, domain=RR) sage: r = 0.1 sage: %timeit F(r) 100000 loops, best of 3: 12.4 µs per loop sage: %timeit ((r*r.sin()).exp() + (r*r.cos()).exp()).log() 10000 loops, best of 3: 16.5 µs per loop
Not so good...
Any suggestions on the bisect-code using fast_callable?
Daniel
Replying to @dkrenn:
Hi Vincent,
Replying to @videlec:
I guess that the line
verbose('iteration %s with results in %s of %s cells' % (iteration, len(result), len(open)), level=2)
is quite bad for performances. The string formatting is done whatever the verbose function is doing. > Did you do some profiling?
No, I didn't do any with this line included (I did on my old code, which used if and print für debugging).
If you really want these verbose you should use lazy formatting. I do not remember what is the state of the art, but you can have a look at sage.misc.lazy_string
and sage.misc.lazy_format
. But you are really creating a string within the critical loop. I do find it not very reasonable (the other is fine though).
Just for curiosity, do you know how good is
fast_callable
with interval fields?Don't know. I used it with some larger symbolic expressions, and what I remember, I had a significant speedup there.
I am pretty sure that fast_callable
is better than symbolic and you seem to confirm that. However, you can see in my small snippet above that fast_callable
looks worse than using Python. I should try on a larger expression but then you have to translate into an object call oriented expression and I am lazy...
Any suggestions on the bisect-code using fast_callable?
Check what fast_callable
is doing with domain=RIF
and domain=CIF
(quickly looking at ext/fast_callable.pyx
I would say nothing). It can be done independently of this ticket.
Other comments:
What is your variable result
for?
did you try to more typing. In other words open = [] -> cdef list open = []
and iteration = 0 -> cdef size_t iteration = 0
, etc. Cython does good job with Python list when it knows that these are lists.
Branch pushed to git repo; I updated commit sha1. New commits:
a97ddce | reintroduced flag bisect_on_success |
Replying to @videlec:
- What is your variable
result
for?
It collects the already finished cells. I've rewritten the code and introduced a new option (this was already there in a pre-version of this code).
Bad syntax for INPUT::
, it should be INPUT:
Replying to @videlec:
If you really want these verbose you should use lazy formatting. I do not remember what is the state of the art, but you can have a look at
sage.misc.lazy_string
andsage.misc.lazy_format
. But you are really creating a string within the critical loop. I do find it not very reasonable (the other is fine though).
LazyFormat did not bring something on the performance. Now checking verbosity level directly; this is faster now.
Replying to @videlec:
- did you try to more typing. In other words
open = [] -> cdef list open = []
anditeration = 0 -> cdef size_t iteration = 0
, etc. Cython does good job with Python list when it knows that these are lists.
Done. Is faster now.
Replying to @fchapoton:
Bad syntax for
INPUT::
, it should beINPUT:
Corrected. Also fixed some broken links in the files (in existing code).
Again, needs_review :)
Unless I'm missing something, the case f is None
is completely undocumented and untested.
Also, I guess the test is meant to satisfy the assumption that if an interval A
passes, any interval containing A
should also pass. This should be documented.
Another detail: you can replace
from sage.rings.real_mpfi import bisect
return bisect(f, self, test, **kwds)
by
return real_mpfi.bisect(f, self, test, **kwds)
Replying to @videlec:
- did you try to more typing. In other words
open = [] -> cdef list open = []
anditeration = 0 -> cdef size_t iteration = 0
, etc. Cython does good job with Python list when it knows that these are lists.
I agree, there should be a lot more typing. All variables which are boolean should be typed bint
and all integers a suitable integer type (Py_ssize_t
for lengths).
Branch pushed to git repo; I updated commit sha1. New commits:
565c482 | Merge tag '6.9' into t/19033/rif/bisect |
dfbbccb | Trac #19033, comment 15: simplify import |
1381c29 | Trac #19033, comment 16: types specified |
6ccfcf0 | Trac #19033, comment 13: remove case f=None |
dd7cb13 | Trac #19033. comment 14: add note on monotonicity of parameter test |
Replying to @jdemeyer:
Unless I'm missing something, the case
f is None
is completely undocumented and untested.
Is now removed (since not needed).
Replying to @jdemeyer:
Also, I guess the test is meant to satisfy the assumption that if an interval
A
passes, any interval containingA
should also pass. This should be documented.
I've added a note block explaining this.
Replying to @jdemeyer:
Another detail: you can replace
from sage.rings.real_mpfi import bisect return bisect(f, self, test, **kwds)
by
return real_mpfi.bisect(f, self, test, **kwds)
Done.
Replying to @jdemeyer:
Replying to @videlec:
- did you try to more typing. In other words
open = [] -> cdef list open = []
anditeration = 0 -> cdef size_t iteration = 0
, etc. Cython does good job with Python list when it knows that these are lists.I agree, there should be a lot more typing. All variables which are boolean should be typed
bint
and all integers a suitable integer type (Py_ssize_t
for lengths).
I've added cdef bint success
; this saves about 5 percent in time in my tested examples. I've tried to add Py_ssize_t
and other bint
...it does not seem that this brought something. Maybe I am using it wrong...
I've also merged in 6.9 (I am sorry for this, since it was not needed, but I didn't have the previous version available and did not want to want until it was compiled).
Set it back to needs_review.
one failing doctest
Branch pushed to git repo; I updated commit sha1. New commits:
99c3e9d | Revert "Trac #19033, comment 15: simplify import" since imports changed on 6.10.beta0 |
Branch pushed to git repo; I updated commit sha1. New commits:
faacb5f | Merge tag '6.10.beta0' into t/19033/rif/bisect |
Replying to @fchapoton:
one failing doctest
Was not there on 6.9.
Merged 6.10.beta0 and reverted the changes from comment 15 of this ticket (change of imports). Back to needs_review.
Branch pushed to git repo; I updated commit sha1. New commits:
0dfca39 | Merge tag '7.1.beta3' into t/19033/rif/bisect |
Merged 7.1.beta3.
does not apply
Branch pushed to git repo; I updated commit sha1. New commits:
d378b1f | Merge tag '7.1.beta4' into t/19033/rif/bisect |
does not apply
Note that arb already offers a bissection/newton scheme for root finding of analytic functions, see arb_calc.
Branch pushed to git repo; I updated commit sha1. New commits:
4f4b2c9 | Merge tag '8.6' into u/dkrenn/rif/bisect |
Merged in 8.6, so back to needs review again.
When I see this correctly, all comments given on this ticket so far have been incorporated or answered, but noone gave a positive review three years ago.....I guess, however, we are close to a positive review after all. Can someone have a look again, please.
[comment:15] is not addressed. real_mpfi
is already globally cimported in complex_interval.pyx
.
I don't think that "bisection on success"/"bisection on failure" is enough. In many instances you have better than a boolean test. Let me consider the situation where you want to isolate all roots of the function f
(e.g. a square-free polynomial). Interval arithmetic allows you to:
f(left)
and f(right)
have different signs and f'(cell)
does not vanish)f(cell)
does not contain zero)
In this situation, you want to perform bisection if the two above certificate failed. It is a True/False/Unknown
situation.I think that the bisection should be general enough to handle this search because it does provide a proof of what you are looking for. The return value would be a pairs of lists: the one you are interested in and the pieces for which bisection failed to determinate their status.
Also, isolating roots in an example such as
sage: def contains_zero(fct, cell):
....: return fct(cell).contains_zero()
sage: result = bisect(lambda x: x^3-4*x+2, RIF(-10, 10), contains_zero)
would be faster since you would not refine the already valid cells.
(for all this, you should have a look at arb_calc_isolate_roots
that was already mentioned)
The following is a big waste.
if join_neighboring_cells:
verbose('joining neighboring cells...', level=1)
cells = result
result = []
while len(cells) > 0:
joined = cells.pop(0)
k = 0
while k < len(cells):
try:
joined.intersection(cells[k])
except ValueError:
k += 1
else:
joined = joined.union(cells.pop(k))
result.append(joined)
The intervals should be stored in order (a binary tree seems the most appropriate).
Replying to @videlec:
[comment:15] is not addressed.
real_mpfi
is already globally cimported incomplex_interval.pyx
.
Doesn't work:
return real_mpfi.bisect(f, self, test, **kwds)
^
------------------------------------------------------------
sage/rings/complex_interval.pyx:2201:24: cimported module has no attribute 'bisect'
This ticket implements a bisection algorithm used with interval field elements.
CC: @cheuberg
Component: numerical
Author: Daniel Krenn
Branch/Commit: u/dkrenn/rif/bisect @
4f4b2c9
Issue created by migration from https://trac.sagemath.org/ticket/19033