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

functions - Get leading series expansion term?

Given a function f[x] , I would like to have a function leadingSeries that returns just the leading term in the series around x=0 . For example: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x)] x and leadingSeries[(1/x + 2 + (1 - 1/x^3)/4)/(4 + x)] -(1/(16 x^3)) Is there such a function in Mathematica? Or maybe one can implement it efficiently? EDIT I finally went with the following implementation, based on Carl Woll 's answer: lds[ex_,x_]:=( (ex/.x->(x+O[x]^2))/.SeriesData[U_,Z_,L_List,Mi_,Ma_,De_]:>SeriesData[U,Z,{L[[1]]},Mi,Mi+1,De]//Quiet//Normal) The advantage is, that this one also properly works with functions whose leading term is a constant: lds[Exp[x],x] 1 Answer Update 1 Updated to eliminate SeriesData and to not return additional terms Perhaps you could use: leadingSeries[expr_, x_] := Normal[expr /. x->(x+O[x]^2) /. a_List :> Take[a, 1]] Then for your examples: leadingSeries[(1/x + 2)/(4 + 1/x^2 + x), x] leadingSeries[Exp[x], x] leadingSeries[(1/x + 2 + (1 - 1/x...

How to thread a list

I have data in format data = {{a1, a2}, {b1, b2}, {c1, c2}, {d1, d2}} Tableform: I want to thread it to : tdata = {{{a1, b1}, {a2, b2}}, {{a1, c1}, {a2, c2}}, {{a1, d1}, {a2, d2}}} Tableform: And I would like to do better then pseudofunction[n_] := Transpose[{data2[[1]], data2[[n]]}]; SetAttributes[pseudofunction, Listable]; Range[2, 4] // pseudofunction Here is my benchmark data, where data3 is normal sample of real data. data3 = Drop[ExcelWorkBook[[Column1 ;; Column4]], None, 1]; data2 = {a #, b #, c #, d #} & /@ Range[1, 10^5]; data = RandomReal[{0, 1}, {10^6, 4}]; Here is my benchmark code kptnw[list_] := Transpose[{Table[First@#, {Length@# - 1}], Rest@#}, {3, 1, 2}] &@list kptnw2[list_] := Transpose[{ConstantArray[First@#, Length@# - 1], Rest@#}, {3, 1, 2}] &@list OleksandrR[list_] := Flatten[Outer[List, List@First[list], Rest[list], 1], {{2}, {1, 4}}] paradox2[list_] := Partition[Riffle[list[[1]], #], 2] & /@ Drop[list, 1] RM[list_] := FoldList[Transpose[{First@li...

front end - keyboard shortcut to invoke Insert new matrix

I frequently need to type in some matrices, and the menu command Insert > Table/Matrix > New... allows matrices with lines drawn between columns and rows, which is very helpful. I would like to make a keyboard shortcut for it, but cannot find the relevant frontend token command (4209405) for it. Since the FullForm[] and InputForm[] of matrices with lines drawn between rows and columns is the same as those without lines, it's hard to do this via 3rd party system-wide text expanders (e.g. autohotkey or atext on mac). How does one assign a keyboard shortcut for the menu item Insert > Table/Matrix > New... , preferably using only mathematica? Thanks! Answer In the MenuSetup.tr (for linux located in the $InstallationDirectory/SystemFiles/FrontEnd/TextResources/X/ directory), I changed the line MenuItem["&New...", "CreateGridBoxDialog"] to read MenuItem["&New...", "CreateGridBoxDialog", MenuKey["m", Modifiers-...