Question about scientific calculations in Python
Andrew Dalke
dalke at dalkescientific.com
Wed Mar 13 00:15:43 EST 2002
Martin Kaufmann:
> for s in s_vector[1:]:
> sum = 0
> for r in r_vector[1:]:
> x = 2 * pi * r * s
> sum = sum + (2 * (sin(x))/x)
> intensity = atoms * pow(f_s_vector[n], 2) * (1 + sum / atoms)
> i_vector.append(intensity)
> n = n + 1
Here's some performance improvements you can make
def ...
# Precompute the 2*pi*r, which is a constant inside of the s loop
precomputed_r1_vector = [2 * pi * r for r in r_vector[1:]]
local_sin = math.sin # local variables have fast lookup
# using the zip gets rid of the extra n+1 and [n], at the expense
# of making a new list. I suspect it's faster, but I'm not sure
for s, f_s_vector in zip(s_vector[1:], f_s_vector):
sum = 0.0
for scaled_r in precomputed_r1_vector:
x = scaled_r * s
sum = sum + 2 * (local_sin(x)/x)
# Did some algebra here to merge the first and last terms,
# which gets rid of the division. Also replaced the function call
# to 'pow' with the equivalent use of **2
i_vector.append((f_s_vector**2) * (atoms + sum))
Some of these changes are only faster for sufficiently long arrays,
so you should test them out yourself. The biggest trick is to
precompute as much as possible (Python doesn't optimize constant
expressions). This includes precomputing lookups of functions
references, like the "local_sin". Also, you could precompute the
lookup of i_vector.append, as in
i_vector_append = i_vector.append
...
for ...
i_vector_append((f_s_vector**2) * (atoms + sum))
>Now the histogram code where the radii are binned in the histogram
>hist and only the bins with a value are used:
That code is almost identical with the previous code, so you can do
the same optimization tweaks. You could also precompute which hist
elements have entry[1] == 0 so you don't do that check every time
in the loop. (As it is now, the "if" check is probably more expensive
than the calculation, even if you know it's zero, unless there are a
lot of zeros.)
If your lists are really long (O(100,000) or more?) then you might find
that the append starts taking too long -- append is asymptotically O(n**n)
in Python -- so that precomputing
i_vector = [None] * len(s_vector - 1)
then keeping an index 'n' to replace
i_vector.append( ...)
with
i_vector[n] = ...
is faster. But this is unlikely.
Probably the best optimization you can do, if PyInline is an acceptable
approach, is to write put that sinc sum into C code. It reduces to
taking two lists and returning a float.
Andrew
dalke at dalkescientific.com
More information about the Python-list
mailing list