Skip to main content

functional style - About auto-compiling and performance between Do and Fold


I was investigating how Fold could improve performance vs Do. I tested the code


AbsoluteTiming[
sum = 1.0;

inc = 1.0;
Do[inc = inc*Sin[10.5]/i; sum = sum + Tan[inc], {i, 10^5}];
sum]

The output is


Out[] = {2.303896, 0.105747}

I have hoped that using Fold could improve the performance:


AbsoluteTiming@ 
Fold[{#[[1]]*Sin[10.5]/(#2 + 1), #[[2]] + Tan@#[[1]]} &,

{Sin[10.5], 1.0}, N@Range[10^5]]

However, this code is not faster:


Out[] = {2.471189, {-5.537337857006675*10^-462146, 0.105747}}

I have some questions concerning the above example:


(1) [Partially solved. See EDIT] Why the Fold here is not faster? I thought it should have been auto-compiled and thus faster. But it didn't.


(2) [Solved. See EDIT] Here Mathematica is using precision much higher than double precision (by having number as small as 10^{-462146}). Would it be possible to set precision to boost performance? I tried SetPricision and SetAccuracy. The precision changes but it doesn't improve performance.


(3) [Solved. See Mr. Wizard's example in his answer] Another problem of Fold is that one has first to generate a long list (length 10^5 in this example). For much larger list, the Fold method may use too much memory. Is it possible to use functional way (not necessarily Fold) in a more memory-efficient way?


Thank you very much!



PS: I met this question when trying to reproduce Mr.Wizard's answer of the below thread, with some modifications. I can reproduce Wizard's result, where Fold boots the performance greatly compared with Do. But I don't understand why my above example is not as good.


Alternatives to procedural loops and iterating over lists in Mathematica


EDIT:


(a) Rojo's comment is the answer of (2): SetSystemOptions["CatchMachineUnderflow" -> False]


(b) About the performance of Fold: on Mathematica v7 after disabling CatchMachineUnderflow, the Fold code is 10x faster then Do code. However, on Mathematica v9 the Fold code is a bit slower than Do. This seems like a regression in Mathematica. For comparison, this code has 10x improvement than the Do version: Fold[# + Sin[#2] &, 1.0, Range[10^6]] both on v7 and v9.



Answer



In version 7, after setting the option that Rojo described, your Fold code is nearly an order of magnitude faster than Do:


SetSystemOptions["CatchMachineUnderflow" -> False];

AbsoluteTiming[

sum = 1.0;
inc = 1.0;
Do[inc = inc*Sin[10.5]/i; sum = sum + Tan[inc], {i, 10^6}];
sum
]


{2.0100028, 0.105747}

AbsoluteTiming@

Fold[{(#[[1]] Sin[10.5])/(#2 + 1), #[[2]] + Tan[#[[1]]]} &, {Sin[10.5], 1.0},
N@Range[10^6]]


{0.2500003, {0., 0.105747}}

The Fold code can be made faster still with a few optimizations:


sin = Sin[10.5];

AbsoluteTiming[

Fold[
{Divide[#[[1]] sin, #2], #[[2]] + Tan[#[[1]]]} &,
{sin, 1.0},
Range[2, 10^6 + 1]
]
]


{0.1600025, {0., 0.105747}}


In this case splitting the operations and vectorizing Tan yields a greater improvement:


AbsoluteTiming[
1 + Tr @ Tan @ FoldList[Divide[# sin, #2] &, sin, Range[2, 10^6 + 1]]
]


{0.0600001, 0.105747}

This may use additional memory, but trading memory consumption for greater speed is a common programming compromise. If you need to work with longer lists you can split the list into sections and use the output of one Fold as the input for the next:


Fold[

Fold[{Divide[#[[1]] sin, #2], #[[2]] + Tan[#[[1]]]} &, #, Range[#2, #2 + 10000]] &,
{sin, 1.0},
Range[2, 10^7, 10000]
] // AbsoluteTiming


{1.8000025, {0., 0.105747}}

Comments

Popular posts from this blog

plotting - Filling between two spheres in SphericalPlot3D

Manipulate[ SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, Mesh -> None, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], {n, 0, 1}] I cant' seem to be able to make a filling between two spheres. I've already tried the obvious Filling -> {1 -> {2}} but Mathematica doesn't seem to like that option. Is there any easy way around this or ... Answer There is no built-in filling in SphericalPlot3D . One option is to use ParametricPlot3D to draw the surfaces between the two shells: Manipulate[ Show[SphericalPlot3D[{1, 2 - n}, {θ, 0, Pi}, {ϕ, 0, 1.5 Pi}, PlotPoints -> 15, PlotRange -> {-2.2, 2.2}], ParametricPlot3D[{ r {Sin[t] Cos[1.5 Pi], Sin[t] Sin[1.5 Pi], Cos[t]}, r {Sin[t] Cos[0 Pi], Sin[t] Sin[0 Pi], Cos[t]}}, {r, 1, 2 - n}, {t, 0, Pi}, PlotStyle -> Yellow, Mesh -> {2, 15}]], {n, 0, 1}]

plotting - Plot 4D data with color as 4th dimension

I have a list of 4D data (x position, y position, amplitude, wavelength). I want to plot x, y, and amplitude on a 3D plot and have the color of the points correspond to the wavelength. I have seen many examples using functions to define color but my wavelength cannot be expressed by an analytic function. Is there a simple way to do this? Answer Here a another possible way to visualize 4D data: data = Flatten[Table[{x, y, x^2 + y^2, Sin[x - y]}, {x, -Pi, Pi,Pi/10}, {y,-Pi,Pi, Pi/10}], 1]; You can use the function Point along with VertexColors . Now the points are places using the first three elements and the color is determined by the fourth. In this case I used Hue, but you can use whatever you prefer. Graphics3D[ Point[data[[All, 1 ;; 3]], VertexColors -> Hue /@ data[[All, 4]]], Axes -> True, BoxRatios -> {1, 1, 1/GoldenRatio}]

plotting - Mathematica: 3D plot based on combined 2D graphs

I have several sigmoidal fits to 3 different datasets, with mean fit predictions plus the 95% confidence limits (not symmetrical around the mean) and the actual data. I would now like to show these different 2D plots projected in 3D as in but then using proper perspective. In the link here they give some solutions to combine the plots using isometric perspective, but I would like to use proper 3 point perspective. Any thoughts? Also any way to show the mean points per time point for each series plus or minus the standard error on the mean would be cool too, either using points+vertical bars, or using spheres plus tubes. Below are some test data and the fit function I am using. Note that I am working on a logit(proportion) scale and that the final vertical scale is Log10(percentage). (* some test data *) data = Table[Null, {i, 4}]; data[[1]] = {{1, -5.8}, {2, -5.4}, {3, -0.8}, {4, -0.2}, {5, 4.6}, {1, -6.4}, {2, -5.6}, {3, -0.7}, {4, 0.04}, {5, 1.0}, {1, -6.8}, {2, -4.7}, {3, -1.