1
$\begingroup$

Sometimes when I try to create a function inside a function, Mathematica gives me a hard time. I usually just have to play around with the syntax until it gets something it "likes" but I really have idea why certain things work and others don't. Here's an example I just ran into recently:

Code version A:

ans[tf_] :=
  NDSolve[{x'[t] == v[t], v'[t] == -x[t] - v[t], x[0] == 1, 
     v[0] == 1}, {x[t], v[t]}, {t, 0, tf}] // Flatten;
ans[2]
Plot[{x[t], v[t]} /. %, {t, 0, 2}]

So this works beautifully. But for some reason if I try to nest my ans[2] inside my function by replacing it with the %, Mathematica can't evaluate it!

Code version B:

ans[tf_] :=
  NDSolve[{x'[t] == v[t], v'[t] == -x[t] - v[t], x[0] == 1, 
     v[0] == 1}, {x[t], v[t]}, {t, 0, tf}] // Flatten;

Plot[{x[t], v[t]} /. ans[2], {t, 0, 2}]

So my question boils down to: Why does Code Version A work and Code Version B doesn't?

$\endgroup$
5
  • 1
    $\begingroup$ Add the option Evaluated -> True to your second Plot $\endgroup$ Commented Oct 4, 2013 at 18:52
  • $\begingroup$ Welcome to Mathematica.SE Steven! Please do not use the bugs tag for new question. We use this tag to categorize confirmed bugs. It'll be re-added if this is confirmed to be a bug by the community. $\endgroup$
    – Szabolcs
    Commented Oct 4, 2013 at 19:07
  • $\begingroup$ See also mathematica.stackexchange.com/questions/11772/… and mathematica.stackexchange.com/a/18487/12 $\endgroup$
    – Szabolcs
    Commented Oct 4, 2013 at 19:13
  • 1
    $\begingroup$ @belisarius But Evaluated is not documented anywhere. Either, politically correct: Plot[Evaluate[{x[t], v[t]} /. %], {t, 0, 2}], or, faster: Plot @@ {{x[t], v[t]} /. %, {t, 0, 2}} $\endgroup$ Commented Oct 4, 2013 at 20:56
  • $\begingroup$ Maybe the reason why this is so, is that there is infinite evaluation, and sometimes this is not as efficient as needed, and therefore there is this funny attribute HoldAll, which is hard to explain. $\endgroup$ Commented Oct 4, 2013 at 20:57

1 Answer 1

1
$\begingroup$

NDSolve doesn't localize the function variable so it doesn't work if your t has a value while it is being evaluated. For example, the following won't work.

Block[{x = 1}, NDSolve[{y'[x] == y[x], y[0] == 1}, y, {x, 0, 1}]]

Plot often automatically decides not to evaluate the function until after giving the variable numerical values. If this is the case then for each point Plot wants to plot (say t=1.23), it does something equivalent to Block[{t=1.23}, {x[t], v[t]} /. ans[2]] to get the output value.

In your first case, ans[2] had been preevaluated into InterpolatingFunctions and stored in %, so it works. But in your second case, the ans[2] and hence the NDSolve are evaluated WHILE t has a numerical value so FAAIL

You could patch it by forcing Plot to preevaluate the argument as people suggested, but it seems more appropriate to fix the heart of the issue and localize your t on ans

ans[tf_] := 
 Block[{t}, 
  NDSolve[{x'[t] == v[t], v'[t] == -x[t] - v[t], x[0] == 1, 
     v[0] == 1}, {x[t], v[t]}, {t, 0, tf}] // Flatten]

Or even better, get pure functions out of NDSolve by using {x, v} as a third argument instead of {x, v}, and then you can use ANY variable name in your Plot. This opens the door to using a formal symbol (symbols that are protected by default) in ans if you want to do away with the Block.

$\endgroup$
3

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