Manipulate auto re-evaluation logic puzzler
- To: mathgroup at smc.vnet.net
- Subject: [mg123939] Manipulate auto re-evaluation logic puzzler
- From: Frank Iannarilli <frankeye at cox.net>
- Date: Sun, 1 Jan 2012 02:26:41 -0500 (EST)
- Delivered-to: l-mathgroup@mail-archive0.wolfram.com
- Reply-to: comp.soft-sys.math.mathematica at googlegroups.com
Can someone please explain this extremely puzzling behavior of Manipulate[]?
(can follow along by evaluating code later below):
In short, a first Button[] push invokes A & B, where B changes a TrackedSymbols variable ("plots") that induces display update. But a second button push fails to induce display update, when it seemingly should.
My Manipulate cell generates both Button[] and the usual Control[] activated interactions. The Button[] executes both processImage[] AND genPlots[] Modules defined within the Manipulate. processImage[] does expensive processing on an externally "passed-in" Global` variable named "image" to generate the intermediate result processedImage, which is localized to the Manipulate. User adjustment of the Control[] activates cheap analysis "post-processing" by genPlots[] on this intermediate result.
So please evaluate the later-below defined cells, as follows:
testMyPatience[]:=Manipulate[... (* Manipulate cell definition *)
testMyPatience[]
image= (the first of the two image= cells)
Now push the Process! button - displayed results appear.
Now evaluate the second of the two image= cells (change from random to Sine image):
image=
Now push the Process! button - the display is unchanged!! (Yuck!)
But now push the L2 control - the display updates to show the newer image results!
Yet the Button[] function processImage[] is careful to invoke plots=genPlots[] as its last instruction. And "plots" is included in TrackedSymbols. With my full-scale code, I can see that pressing the Process! button does actually result in computation (30 seconds of it) - how can Manipulate not see "plots" getting modified upon Button-push, and therefore execute the appropriate Refresh?
John Fultz, are you there? :-)
Thanks!
------------
image = Table[RandomReal[], {32}, {32}];
testMyPatience[];
image = Table[Sin[i*j/64], {i, 1, 32}, {j, 1, 32}];
testMyPatience[] := Manipulate[
(* image is in Global` namespace,
and is "passed" by an external party. *)
(* The actual processing of image is expensive which is why we have \
it button-activated.
It produces processedImage which can be analyzed repeatedly by \
genPlots[] for different controller settings w/
o processedImage needing to be recomputed. *)(* This Button-
activated Module modifies the external variables {firstTime,
snapShot,processedImage}. *)
processButton[] :=
Module[{},
firstTime = False;
snapShot = image;
(* Yields external variable pocessedImage *)
processedImage = Transpose[image];
(* Yields external plots *)
plots = genPlots[];
];
(* Take processedImage and post-
process as desired into diagnostic graphics.
Preserve processedImage by treating as "read-only" -
it is expensive to compute - the diagnostic results are not,
and can be iterated for different settings,
while preserving the expensive processedImage. *)
genPlots[] := Module[{touchedProcessedImage, g1, g2},
(* Now change appearance of processedImage IAW chosen metric *)
If[cmetric == "L2",
touchedProcessedImage = processedImage^2];
If[cmetric == "L1",
touchedProcessedImage = Reverse[processedImage]];
(* Now make graphics *)
g1 = ListPlot3D[snapShot, PlotLabel -> "Image", PlotRange -> All];
g2 = ListPlot3D[touchedProcessedImage, PlotLabel -> "touched",
PlotRange -> All];
(* Return *) GraphicsGrid[{{g1, g2}}, ImageSize -> 400]
];
plots = If[Not[firstTime],
genPlots[],
"Hi trusty Mathematica weenie!"
],
(* The last executable statement above must terminate with a comma,
thus marking the division between the "action" and control/
layout specification sections of the Manipulate[] *)
(* >>>>>>> CONTROL/LAYOUT SECTION <<<<<<<<< *)
Item[Style["Test My Patience", 16, Bold, Brown],
Alignment -> Center],
Delimiter,
{{cmetric, "L1", "Plot Appearance"}, {"L1", "L2"}},
Button["Process!", processButton[], Method -> "Queued"],
(* Dummy control variables,
which are actually variables we'd like to keep localized to within \
this Manipulate, or otherwise they'd leak out as session-
global variables. A good rule of thumb:
if a variable is included in TrackSymbols, it better be localized.
LocalizeVariables->
True localizes variables associated with Controls. *)
{firstTime, ControlType -> None},
{snapShot, ControlType -> None},
{plots, ControlType -> None},
{processedImage, ControlType -> None},
(* Manipulate options *)
LocalizeVariables -> True,
(* Very Strange!!! If we DON'T track processedImage,
graphics don't update, even when processButton is pressed,
which includes invocation of plots=genPlots[],
AND we are tracking the dummy control variable "plots" ! *)
TrackedSymbols :> {cmetric, firstTime, plots},
ContinuousAction -> False, Initialization :> (firstTime = True)
]