1
$\begingroup$

My problem is a classic one: I want to plot a function-generated list, and there's no way to pre-evaluate (because the actual function includes NMinimize and thus cannot be calculated on symbolic values). I use the following replacement for the actual function:

f[x_?NumericQ]:={x,x^2}

Now doing

Plot[f[x], {x, 0, 1}, Evaluated->True]

will still generate only one colour, as evaluation with a symbolic value will not generate a list. So I thought I could bypass this by just saving the result in a temporary variable and creating a wrapper function to read that out again (after all, unlike Mathematica, I do know the shape of the generated list):

Module[{fwrap1,fwrap2,tmp},
  fwrap1[x_?NumericQ] := (tmp = f[x])[[1]];
  fwrap2[x_?NumericQ] := tmp[[2]];
  Plot[{fwrap1[x], fwrap2[x]}, {x, 0, 1}]]

I expected this to work as follows:

  • First, fwrap1 is called, which obtains the complete list from f, stores it in tmp and returns the first item.
  • Then, fwrap2 is called, which just reads the second value which fwrap1 stored in tmp.

However while this indeed gives two colours as expected, the second function is replaced with a constant value (the one at maximal x).

By having fwrap2 increment a counter, I can verify that it is called 53 times, and by recording the minimal and maximal x, I can verify that it is indeed called on the complete interval (well, at least at both end points). Moreover, adding lines of the form

Print[{fwrap1[0.5],fwrap2[0.5]}];

inside the module (with different values each time) gives the expected output.

So why does this code not work?

$\endgroup$
  • 1
    $\begingroup$ You assumed that, for each value of x, fwrap1 is called, then fwrap2. However, from the behavior you observe I suspect that Plot calculates all values for fwrap1 first, repeatedly rewriting the value of tmp and discarding the intermediate values you wanted to save; only afterwards does it move on to fwrap2. This would seem to make sense with the fact that only the value of tmp corresponding to one of the boundaries of the $x$ range is retained. $\endgroup$ – MarcoB Oct 20 '15 at 17:33
  • $\begingroup$ Thanks, that makes sense. I now tested the call order, and indeed, after an initial single call to each, there follows a row of calls only to fwrap1, and then another row of calls only to fwrap2. If you turn your comment into an answer, I'll accept it. $\endgroup$ – celtschk Oct 20 '15 at 17:55
  • $\begingroup$ Glad it helped. I've converted to an answer below. $\endgroup$ – MarcoB Oct 20 '15 at 18:24
1
$\begingroup$

I'm turning my previous comment into an answer. In your case, you assumed that, for each value of $x$, fwrap1 is called, then fwrap2.

However, from the behavior you observe I suspect that Plot calculates all values for fwrap1 first, repeatedly rewriting the value of tmp and discarding the intermediate values you wanted to save; only afterwards does it move on to fwrap2.

This would seem to make sense with the fact that only the value of tmp corresponding to one of the boundaries of the $x$ range is retained.

$\endgroup$
0
$\begingroup$
f[x_?NumericQ] := {x, x^2}

These three expressions all produce the same output. Quiet is used in each case to suppress the message "Part::partw: "Part 2 of f[x] does not exist."

Module[{fwrap1, fwrap2, tmp},
  fwrap1[x_] := (tmp = f[x])[[1]];
  fwrap2[x_] := tmp[[2]];
  Plot[{fwrap1[x], fwrap2[x]}, {x, 0, 1},
   Evaluated -> True,
   PlotLegends -> {x, x^2}]] //
 Quiet

Module[{fwrap1, fwrap2, tmp},
  fwrap1[x_] = (tmp = f[x])[[1]];
  fwrap2[x_] = tmp[[2]];
  Plot[{fwrap1[x], fwrap2[x]}, {x, 0, 1},
   PlotLegends -> {x, x^2}]] //
 Quiet

Module[{tmp},
  Plot[
   {(tmp = f[x])[[1]], tmp[[2]]},
   {x, 0, 1},
   Evaluated -> True,
   PlotLegends -> {x, x^2}]] //
 Quiet

enter image description here

EDIT:

Looking at the number of times at f[x] is evaluated

f[x_?NumericQ] := (n++; {x, x^2})

n = 0;
Module[{fwrap1, fwrap2, tmp}, fwrap1[x_] := (tmp = f[x])[[1]];
  fwrap2[x_] := tmp[[2]];
  Plot[{fwrap1[x], fwrap2[x]}, {x, 0, 1}, Evaluated -> True, 
   PlotLegends -> {x, x^2}]] // Quiet; n

(*  159  *)

n = 0;
Module[{fwrap1, fwrap2, tmp}, fwrap1[x_] = (tmp = f[x])[[1]];
   fwrap2[x_] = tmp[[2]];
   Plot[{fwrap1[x], fwrap2[x]}, {x, 0, 1}, PlotLegends -> {x, x^2}]] // 
  Quiet;
n

(*  159  *)

n = 0;
Module[{tmp}, 
   Plot[{(tmp = f[x])[[1]], tmp[[2]]}, {x, 0, 1}, Evaluated -> True, 
    PlotLegends -> {x, x^2}]] // Quiet;
n

(*  159  *)

n = 0; Plot[{f[x][[1]], f[x][[2]]}, {x, 0, 1}];
n

(*  238  *)
$\endgroup$
  • $\begingroup$ But all those do the equivalent of Plot[f[x][[1]], f[x][[2]],{x,0,1}]. That is, the function is evaluated twice (this can easily be verified by incrementing a counter in the function; with my version, there are 78 calls, with any of yours, there are 158). The whole point of that construction is that this extra call is avoided (as the calls of the actual function are expensive). $\endgroup$ – celtschk Oct 20 '15 at 21:03
  • $\begingroup$ @celtschk - See edit with counters. The code that you posted as equivalent to mine (after your syntax is corrected) executes `f`` 238 times vice 159; so they are not equivalent. I do not know what other code to which you are comparing. $\endgroup$ – Bob Hanlon Oct 20 '15 at 21:36
  • $\begingroup$ I'm comparing with the code I posted in my question (the one that gave the wrong curves). That code makes only 78 calls to f (tested on Mathematica 8.0). However I defined the function for counting as f[x_?NumericQ] := {count++; x, x^2}. $\endgroup$ – celtschk Oct 20 '15 at 21:48
  • $\begingroup$ @celtschk - it makes no sense to me to compare performance of working code against non-working code. The fact that the curve is wrong impacts the adaptive algorithm that determines the number of points used for drawing the curve. Consequently, you can only reasonably compare code that produces the same curves. $\endgroup$ – Bob Hanlon Oct 20 '15 at 21:54

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.