In Mathematica 8.0, suppose I have some constants:
a:=7
b:=9
c:=13
d:=.002
e:=2
f:=1
and I want to use them to evaluate some interlinked functions
g[0,k_]:=0
g[t_,0]:=e
g[t_,k_]:=g[t-1,k]*a+h[t-1,k-1]*b
h[0,k_]:=0
h[t_,0]:=f
h[t_,k_]:=h[t-1,k]*c+g[t-1,k-1]*d
But this is really slow and in need of dynamic programming, or else you get an exponential slowdown:
g[0, k_] := 0
g[t_, 0] := e
g[t_, k_] := g[t, k] = g[t - 1, k]*a + h[t - 1, k - 1]*b
h[0, k_] := 0
h[t_, 0] := f
h[t_, k_] := h[t, k] = h[t - 1, k]*c + g[t - 1, k - 1]*d
Now it's really fast, but if we ever want to change the constants(say, to use this in a Manipulate function) we have to Clear
g
and h
each time. If we had complex interdependencies, it might be really annoying to clear them all out every single time we wanted a value from g
and h
.
Is there an easy way to run g
and h
in a Module
or Block
or similar, so that I can get a fresh result back each time it's evaluated without the exponential slowdown? Or even a fast way to build up a table of results for both g
and h
in a nice way? As said, I want to be able to compute g
and h
in a Manipulate
function.
Here is one way, using
Block
as you requested:We will use this to give definitions to
f
andh
asWe call now:
There are no global memoized definitions left after each call -
Block
takes care to destroy them just before the execution exitsBlock
. In particular, here I will change the parameters and call them again:An alternative to this scheme would be to explicitly pass all parameters like
a,b,c,d,e,f
to functions, extending their formal parameter lists (signatures), but this has a disadvantage that the older memoized definitions corresponding to different past parameter values would not be automatically cleared. Another problem with that approach is that the resulting code will be more fragile, w.r.t changes in the number of parameters, etc.EDIT
However, if you want to build a table of results, this could be somewhat faster since you do it once and for all, and in this case you do want to keep all memoized definitions. So, here is the code:
You call it, passing the parameters explicitly:
(I was using the original parameters). You can check that the memoized definitions remain in the global rule base, in this method. Next time you call a function with exact same parameters, it will fetch the memoized definition rather than recompute. Apart from the problems with this approach that I outlined above, you should also watch for the memory usage, since nothing gets cleared.
Memoization Using Auxiliary Symbol
The memoization technique exhibited in the question can be modified so that the definitions of
g
andh
do not need to be re-established whenever the cache needs to be cleared. The idea is to store the memoized values on an auxiliary symbol instead of directly ong
andh
:The definitions are essentially the same as the original memoized versions of
g
andh
except that a new symbol,memo
, has been introduced. With these definitions in place, the cache can be cleared simply usingClear@memo
-- there is no need to redefineg
andh
anew. Better still, the cache can be localized by placingmemo
inBlock
, thus:The cache is discarded when the block is exited.
Memoization Using Advice
The original and revised memoization techniques required invasive changes within the function
g
andh
. Sometimes, it is convenient to introduce memoization after the fact. One way to do this would be to use the technique of advising -- a kind of functional programming analog to subclassing in OO programming. A particular implementation of function advice appears regularly in the pages of StackOverflow. However, that technique is also invasive. Let's consider an alternate technique of adding advice tog
andh
without altering their global definitions.The trick will be to temporarily redefine
g
andh
within aBlock
. The redefinitions will first check the cache for the result and, failing that, call the original definitions from outside the block. Let's go back to the original definitions ofg
andh
that are blissfully unaware of memoization:The basic schema for this technique looks like this:
The temporary symbols
gg
andhh
are introduced to hold the original definitions ofg
andh
. Theng
andh
are locally rebound to new caching definitions that delegate to the original definitions as necessary. Here is definition ofcopyDownValues
:In an effort to keep this post short(er), this "copy" function is concerned only with down-values. A general advice facility also needs to account for up-values, subvalues, symbol attributes and so on.
This general pattern is easy, if tedious, to automate. The following macro function
memoize
does this, presented with little comment:After much ado, we are now in a position to externally impose memoization upon the non-caching versions of
g
andh
:Putting it all together, we can now create a responsive
Manipulate
block:The
LocalizeVariables
andTrackedSymbols
options are artifacts of the dependencies thatg
andh
have on the global symbolsa
throughf
.