4
$\begingroup$

I want to plot several curves of functions which are hard to calculate on the same plot. I would like to add to the same plot the sum of these functions without recalculating them twice at each point.

So instead of

 Plot[{Sin[x], Sin[2 x], Sin[x] + Sin[2 x]}, {x, 0, 6 Pi}]

I would like somthing like

 Plot[{aa = Sin[x], bb = Sin[2 x], aa + bb}, {x, 0, 6 Pi}]
$\endgroup$
6
  • 3
    $\begingroup$ precalculate points in advance and then use ListPlot? $\endgroup$
    – BlacKow
    May 10 '16 at 15:06
  • 3
    $\begingroup$ Try Plot[With[{aa = Sin[x], bb = Sin[2 x]}, {aa, bb, aa + bb}], {x, 0, 6 Pi}, Evaluated -> False] (untested). Might give three lines of a single colour. $\endgroup$
    – Szabolcs
    May 10 '16 at 15:11
  • $\begingroup$ @Szabolcs wouldn't it recalculate aa? how will it choose sample rate? $\endgroup$
    – BlacKow
    May 10 '16 at 15:14
  • 1
    $\begingroup$ @Szabolcs It works just like you said (all three lines in the same color, though). $\endgroup$
    – Jens
    May 10 '16 at 15:28
  • 1
    $\begingroup$ "without recalculating them twice at each point." - due to the adaptive sampling, it is quite possible that Plot[] will evaluate the sum at points where neither of the components were evaluated, and vice versa. $\endgroup$ May 10 '16 at 18:27
7
$\begingroup$

How about this:

f[x_] := Sin[x]
Plot[{#1, #2, #1 + #2} &[f[x], f[2 x]], {x, 0, 4}]

Strangely enough, this solution is slower that expected:

f[x_] := NIntegrate[Sin[1/y^2], {y, -x, x}] (*slow function*)

AbsoluteTiming[Plot[{f[x], f[2 x], f[x] + f[2 x]}, {x, 0, 1}]] (*naïve approach*)
AbsoluteTiming[Plot[{#1, #2, #1 + #2} &[f[x], f[2 x]], {x, 0, 1}]] (*my solution*)
AbsoluteTiming[Plot[With[{aa=f[x],bb=f[2x]},{aa,bb,aa+bb}],{x,0,1},Evaluated->False]] (*Szabolcs' comment*)

(*65.7*)
(*106.2*)
(*102.0*)

We do get a substantial improvement with memoization:

f[x_] := f[x] = NIntegrate[Sin[1/y^2], {y, -x, x}]
AbsoluteTiming[Plot[{f[x], f[2 x], f[x] + f[2 x]}, {x, 0, 1}]] (*naïve approach with memoization*)

(*40.5*)

Finally, the best approach so far is to follow BlacKow's suggestion and precalculate the function at discrete points:

f[x_] := NIntegrate[Sin[1/y^2], {y, -x, x}] (*two slow*)
g[x_] := NIntegrate[Cos[1/y^2], {y, -x, x}] (*functions*)

AbsoluteTiming[
  points = Range[0, 1, .01];
  F = f /@ points;
  G = g /@ points;
  ListPlot[{Transpose@{points, F}, Transpose@{points, G}, 
       Transpose@{points, F + G}}, Joined -> True]
]

(*3.4*)

though it can get tricky to choose the appropriate spacing.

$\endgroup$
2
  • $\begingroup$ newbie here any suggestion is very much appreciated :-) $\endgroup$ May 10 '16 at 16:10
  • $\begingroup$ ListLinePlot[Transpose@{points, #} & /@ {F, G, F + G}] ... maybe? $\endgroup$
    – BlacKow
    May 10 '16 at 22:52
1
$\begingroup$

A slight variation on AccidentalFourierTransform's version of BlacKow's suggestion.

f[x_] := NIntegrate[Sin[1/y^2], {y, -x, x}]

AbsoluteTiming[
  pts1 = Table[{x, f[x]}, {x, Subdivide[0., 1., 101]}]; 
  pts2 = Table[{x, f[2 x]}, {x, Subdivide[0., 1., 101]}]; 
  pts3 = MapThread[{#1[[1]], #1[[2]] + #2[[2]]} &, {pts1, pts2}]; 
  ListLinePlot[{pts1, pts2, pts3}]]

result

Since I am running on a six year old iMac, I don't think the slightly faster time is due to better processor speed.

$\endgroup$
1
  • $\begingroup$ I get about 10% increase if calculate pts3 = Transpose@{pts1[[All, 1]], pts1[[All, 2]] + pts2[[All, 2]]}; $\endgroup$
    – BlacKow
    May 10 '16 at 22:42

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.