speeding up programs (was slowness in repeated Maxima evaluations) from Sage
Subject: speeding up programs (was slowness in repeated Maxima evaluations) from Sage
From: Richard Fateman
Date: Sun, 07 Dec 2008 11:25:28 -0800
So far as I can tell, the original computation in Maxima was
(fc_all(n):=fc_ab(-1,n,-%pi,(-%pi)/2)+fc_ab(2,n,-%pi/2,0)
+fc_ab(-1,n,0,%pi/2)+fc_ab(2,n,%pi/2,%pi),
fc_ab(e,n,a,b):=if n = 0 then [integrate(e,x,a,b)/(2*%pi),0]
else fc1_ab(e,n,a,b),
fc1_ab(e,n,a,b):=[integrate(e*cos(n*x),x,a,b)/%pi,
integrate(e*sin(n*x),x,a,b)/%pi],
coefs : map (fc_all, makelist (i, i, 0, 15)),
fsum : sum (coefs[1 + n][1] * cos (n*x) + coefs[1 + n][2] * sin (n*x),
n, 0, 15),
xx : makelist ((i - 1) * 2 * %pi / 100 - %pi,i,1,101),numer,
fsum_xx : makelist (''fsum, x, xx), numer);
which in my (GCL) maxima takes 5 seconds.
There are a number of peculiarities in the way this computation was set
up, perhaps some because the original author was not concerned with
efficiency, or aware of the consequences of programming in certain ways.
or perhaps Sage is itself producing bad stuff from good input. I don't
know which. Anyway, a few changes to make it into idiomatic Maxima
results in a program that is over 30X faster. Usually this kind of
speedup is an indication that something should have been done another
way. Especially when the faster program is also shorter and clearer.
What is being done wrong? Well, it seems that many computations with
large expressions are being done with symbols such as %pi in them, and
then later, %pi is replaced with 3.14159... at which point the large
expressions become a single numbers.
If one does the same calculations but using 3.1415... instead of the
symbol %pi, few of those large expressions are produced. Just numbers.
Another thing.
If you are computing a symbolic integral integrate(XXXX,x,a,b), then
when you change the limits, you don't have to recompute the symbolic
integral. Just change the limits.
Another thing. Using arrays instead of lists is usually a good thing, if
you are going to access data by array references.
Also, if you are going to define global variables and functions and
never use them again you should kill them. In debugging, I usually use
a "batch" file and kill all the data at the beginning, wiping out all
previous definitions.
Here's a version of the same computation. Maybe Sagenatics (Sagenicks?)
in addition to the original author will be able to see some useful
idioms here.
(kill(fc_ab,coefs,fsum,xx,fsum_xx,pi),
pi:ev(%pi,numer),
fc_ab[n,e](a,b):= [integrate(e*cos(n*x),x,a,b)/pi,
integrate(e*sin(n*x),x,a,b)/pi],
coefs[n]:= fc_ab[n,-1](-pi,-pi/2) + fc_ab[n,2](-pi/2,0)
+fc_ab[n,-1](0,pi/2) + fc_ab[n,2](pi/2,pi),
fsum(z) := sum (coefs[n][1] * cos (n*z) + coefs[n][2] * sin (n*z), n, 0,
15),
xx[i] := pi*(i-51)/50,
fsum_xx:makelist(fsum(xx[i]),i,1,101))