# 2D plot performance

I have this function

    sage: f=imag(I*(sqrt(-cos(l) + 1)*cosh(sin(1/2*l)) -
sqrt(2)*sinh(sqrt(sin(1/2*l)^2)))*sin(1/2*l)^3/((-cos(l)
+ 1)^(3/2)*e^(1/2*I*l)))


i.m.h.o. this not something terribly complicated. I wanted to plot it. So I do

sage: time plot(f,l,0,10)
..nice plot..
Time: CPU 9.28 s, Wall: 9.41 s


I.e. I was waiting almost 10s for this (on an intel core duo CPU P9500 @ 2.53GHz) laptop!? I thought that maybe fast_callable would help:

sage: ff=fast_callable(f,vars=[l],domain=CC)
sage: time plot(ff,0,10)
..same nice plot..
Time: CPU 13.24 s, Wall: 13.50 s


So that's even worse. Now I compare to Mathematica

In:= Timing[Plot[Im[(Sin[l/2]^3*(I*Sqrt[1 - Cos[l]]*Cosh[Sin[l/2]] -
I*Sqrt*Sinh[Sqrt[Sin[l/2]^2]]))/(E^((I/2)*l)*(1 -
Cos[l])^(3/2))], {l, 0, 10}]]

Out= {0.019997, ..same plot again..}


~500 times faster ... what am I doing wrong?

edit retag close merge delete

Sort by » oldest newest most voted

Apparently the imag() function is trying to do the imaginary part of everything right off the bat:

sage: f=(I*(sqrt(-cos(l) + 1)*cosh(sin(1/2*l)) - sqrt(2)*sinh(sqrt(sin(1/2*l)^2)))*sin(1/2*l)^3/((-cos(l) + 1)^(3/2)*e^(1/2*I*l)))
sage: fimag=imag(f)


Now, compare f and fimag. fimag is way bigger (we'll just show the lengths of the string representations here:

sage: len(str(fimag))
25541
sage: len(str(f))
121


Let's look at the fast callable versions

sage: ff=fast_callable(f,vars=[l],domain=CC)
sage: ff.python_calls()
[cos, cos, <function sqrt at 0x10abaa2a8>, sin, cosh, <function sqrt at 0x10abaa2a8>, sin, <function sqrt at 0x10abaa2a8>, sinh, exp, sin]
sage: ffimag=fast_callable(fimag,vars=[l],domain=CC)
sage: len(ffimag.python_calls())
2336
sage: timeit('ff(1.0)')
625 loops, best of 3: 173 µs per loop
sage: timeit('ffimag(1.0)')
25 loops, best of 3: 27.1 ms per loop


I think this is a problem with the imag function. Here's what I would do to work around it for now:

sage: ff=fast_callable(f,vars=[l],domain=CDF)
sage: time plot(lambda x: imag(ff(x)), (0,10))

Time: CPU 0.42 s, Wall: 0.42 s


(Note that I use domain=CDF; that will be faster, as it uses machine floating point numbers)

Or here's another way to do it that tells imag to not try to symbolically compute the imaginary part:

sage: var('l')
l
sage: f=(I*(sqrt(-cos(l) + 1)*cosh(sin(1/2*l)) - sqrt(2)*sinh(sqrt(sin(1/2*l)^2)))*sin(1/2*l)^3/((-cos(l) + 1)^(3/2)*e^(1/2*I*l)))
sage: fimag=imag(f,hold=True)
sage: fimag
imag_part((I*sqrt(-cos(l) + 1)*cosh(sin(1/2*l)) - I*sqrt(2)*sinh(sqrt(sin(1/2*l)^2)))*e^(-1/2*I*l)*sin(1/2*l)^3/(-cos(l) + 1)^(3/2))
sage: ffimag=fast_callable(fimag,vars=[l],domain=CDF)
sage: ffimag.python_calls()
[(^2), (^3), imag_part]
sage: time plot(ffimag, (0,10))

Time: CPU 0.39 s, Wall: 0.39 s

more

@Jason Grout so that would imply that if one was to plot something like F(imag(G(...,imag(..))), or other nested constructs containing imag() parts, one would have put all of the imag()'s on a hold prior to plotting? That could get quite cumbersome.

Agreed. I feel like there are some tickets about imag and real out there for related reasons. Jason, any word on the rewrite of fast_callable? ;-)

That imag thing is sick! And tons of real_parts strewn in there as well. There HAS to be a better way - is Ginac really doing that?

re: polishing (not rewrite) of fast_callable -- that is on the back burner until there is more time. I agree that imag() is going a bit crazy here.