9
$\begingroup$

I have a Manipulate where a portion of the calculation that ends up displayed is dependent on a subset of the Manipulate control variables. It would make sense to cache that portion of the computation, only updating it if those controls are altered. Here's an example to illustrate:

ClearAll[preCalculateStuff]
preCalculateStuff = (#1  #2 ) & ;

Manipulate[ DynamicModule[{f},
  f = preCalculateStuff[a, b];
  Plot[(f t) Sin[  t + r ], {r, 0, 2 Pi}]],
 {{a, 0.5}, 0, 1},
 {{b, 0.5}, 0, 1},
 {{t, 0.5}, 0, 1} ]

My real preCalculateStuff builds a big table, each element requiring a 4x4 Eigensystem call. I'd like to restrict that calculation to only do it when the control variables it is dependent on change. I've been pointed to TrackedSymbols :> {var1, var2} as a mechanism to avoid re-evaluation of the display. However, I still want to re-evaluate the display here (when any variable changes, including any that I would track), so it's not obvious to me how that would be applicable (unless it was to implement a calculate button, which wouldn't be terribly elegant).

$\endgroup$
3
  • $\begingroup$ Perhaps no good reason. I'm pretty sure I'd seen that done in other examples, and was following them. $\endgroup$ Jan 9, 2014 at 2:06
  • 1
    $\begingroup$ In real life are a and b discrete or continuous? If discrete I'd use memoization. $\endgroup$ Jan 9, 2014 at 2:44
  • $\begingroup$ They are continuous. I'd considered memoization, but thought that would result in a potentially large set of cached, never to be used again, results. $\endgroup$ Jan 9, 2014 at 4:04

2 Answers 2

8
$\begingroup$

Perhaps something like this:

ClearAll[preCalculateStuff]
preCalculateStuff = (#1 #2) &;

Manipulate[
 DynamicModule[{f},
  f = preCalculateStuff[a, b];
  Dynamic@Plot[(f t) Sin[t + r], {r, 0, 2 Pi}]],
 {{a, 0.5}, 0, 1},
 {{b, 0.5}, 0, 1},
 {{t, 0.5}, 0, 1, ContinuousAction -> True},
 ContinuousAction -> None]

Mathematica graphics

ContinuousAction -> None is explained in the documentation on Manipulate and creates an update button "U" in the upper left corner. Click on it to update after changing a and b. The ContinuousAction -> True setting on the t slider and the Dynamic wrapping the plot make the plot be updated when the slider is moved. But f is not updated until the "U" button is clicked.

$\endgroup$
11
$\begingroup$

This is what I do. I have been using this method for long time. The idea is simple.

Use the second argument of Dynamics. In there, make any changes to the state of the program you want, that only relates to the change of the current control variable being changed. In your case, in the second argument of a and b, you can make your heavy computations. In t, you do not. The result of the computation is stored in f

Then if you want to update the main display, simply tickle the heart beat control variable. There is only ONE heart beat control variable to the whole program. No matter how large or complicated the program is.

i.e. one tracked control variable in the whole program. This is called the tick below.

So, this is like callback programming in openGL and any other event based GUI programing, so it is easy to learn and adopt and very flexible. The callback, is the second argument of Dynamics.

You can see from the movie below, that f is only updated when you move a and b but remains the same when t is moved.

This is very flexible as you can change the state machine logic any way you want and it scales to much more complicated logic.

Manipulate[
 tick;
 Grid[{
   {Row[{"f=", f, " a=", a, " b=", b, " t=", t}]},
   {Plot[(f t) Sin[t + r], {r, 0, 2 Pi}, ImagePadding -> 30, 
    ImageSize -> 400, Frame -> True]}
   }],

 Grid[{
   {"a", Manipulator[Dynamic[a, {a = #; f = preCalculateStuff[a, b]; 
         tick = Not[tick]} &], {0, 1, 0.01}], Dynamic[a]},
   {"b", Manipulator[Dynamic[b, {b = #; f = preCalculateStuff[a, b]; 
         tick = Not[tick]} &], {0, 1, 0.01}], Dynamic[b] },
   { "t", Manipulator[Dynamic[t, {t = #; tick = Not[tick]} &], {0, 1, 0.01}], 
      Dynamic[t]}
   }, Alignment -> Center, Spacings -> {.5, .5}
  ],
 {{tick, False}, None},
 {{a, .5}, None},
 {{b, .4}, None},
 {{t, .7}, None},
 {{f, .5*.4}, None},
 TrackedSymbols :> {tick},
 Initialization :>
  (
   preCalculateStuff[a_, b_] := Module[{},
     (*heavy computation goes here*)
     a*b 
     ]
   )
 ]

enter image description here

update: Just a small background on this: At first I used a different setup than the above, but which still avoids the problem being discussed here. I documented that method in this event_driven_manipulate note. The above is a simplified and easier to understand version of the earlier method.

I plan to make a second version note to document this pattern more but too many HW's and never got around to it. This simplified method is what I use now and not the first method in the above note for all my demos and any manipulate I write. I used it for small 200 lines demos to more complicated 3000 lines demos, and this method works and scales well I found.

$\endgroup$
3
  • $\begingroup$ +1 I can confirm that this is a method that works very well! The more you work with it, the more you start placing code inside initialization, and so, here follows an important note of attention: something like processThis:=(whatever) doesn't work as expected inside initialization! (there are posts on this topic) ; but processThis[]:=(whatever) works just fine. $\endgroup$
    – P. Fonseca
    Jan 9, 2014 at 19:56
  • $\begingroup$ I'm having trouble attempting to apply your method with TabView, and described the issue here: mathematica.stackexchange.com/questions/40559/… Do you understand what is happening there? $\endgroup$ Jan 16, 2014 at 14:07
  • $\begingroup$ Just as a comment, this method uses Manipulate as a DynamicModule, which offers more possibilities. "None" is a way to define DynamicModule variables in Manipulate. This method is really practical. $\endgroup$
    – faysou
    Dec 28, 2014 at 9:27

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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