# Choosing among different function definitions without sacrificing speed

I'm working on some code that numerically integrates a function, let's call it G, which calls another function, F, that can be defined according to any of several models. In one model, there is an analytic expression for F. In the other models, F needs to be defined by loading data from a file and interpolating it. There is a different data file for each model (other than the analytic one).

One way I tried to do this was to have a Switch statement in F, and get it to choose whether to use the analytic expression or to take data from a file each time it's called based on the options provided.

(* load data from a file and Interpolate it *)
FFromFile[filename_] := FFromFile[filename] = Module[{...}, ...]
FAnalytic[a_,b_,c_] := a^2+b^2+c^2
F[a_,b_,c_,OptionsPattern[]] :=  Switch[OptionValue[Model],
"File",FFromFile[OptionValue[Source]][a,b,c],
"Expr",FAnalytic[a,b,c]
]
G[d_,e_,f_,phi_,opts:OptionsPattern[]] := Block[{g,h,i},
(* do a bunch of calculations to figure out g and h *)
i = F[g, h, 5, opts];
(* do more calculations involving i *)
]
GenerateData[phi_,opts:OptionsPattern[]] := NIntegrate[G[x,y,z,phi,opts],
{x,2,10}, {y,0,x}, {z,0,3}, Method->"QuasiMonteCarlo", PrecisionGoal->3]


Unsurprisingly, that turned out to be too slow. So, another method I tried was to predefine F, since the same model is used throughout an integration.

(* load data from a file and Interpolate it *)
FFromFile[a_,b_,c_] = Module[{v,...}, v=ReadList[filename, ...]; ...];
FAnalytic[a_,b_,c_] = a^2+b^2+c^2;
Switch[TheModel,
"File",F=FFromFile,
"Expr",F=FAnalytic
]
G[d_,e_,f_,phi_] := Block[{g,h,i},
(* do a bunch of calculations to figure out g and h *)
i = F[g, h, 5];
(* do more calculations involving i *)
]
GenerateData[phi_] := NIntegrate[G[x,y,z,phi],
{x,2,10}, {y,0,x}, {z,0,3}, Method->"QuasiMonteCarlo", PrecisionGoal->3]


filename and TheModel would be defined prior to running this code snippet. This way is faster, though it turns out to be kind of inconvenient because sometimes I want to run calculations that compare two different models (for example, from two different files), which requires me to run one, save the data to a file, quit the kernel to ensure that all the definitions are cleared, start it up again and run the other. Plus, this method of passing information to the function by defining variables in advance (filename and TheModel) makes me kind of queasy ;-)

Is there some way I can get the best of both worlds, namely be able to specify the model by passing options to GenerateData, while still retaining the speed benefits of predefining F?

-
It sounds like Patterns may be useful. You can special case based on any condition you want, but the condition may not be easy to specify. –  Mike Bantegui Jan 18 '12 at 1:25

What I'd do (this is just one way of doing it out of many) is to make functions F and G local and generate them at run-time:

FFromFile[filename_] := FFromFile[filename] = Module[{ ...}, ...]
FAnalytic[a_, b_, c_] := a^2 + b^2 + c^2;
GenerateData[phi_, opts : OptionsPattern[]] :=
Module[{F, G},
If[OptionValue[Model] === "File",
F[a_, b_, c_] := FFromFile[OptionValue[Source]][a, b, c],
(* else *)
F[a_, b_, c_] := FAnalytic[a, b, c]
];
G[d_, e_, f_, phiLocal_] :=
Block[{g, h, i},
(*do a bunch of calculations to figure out g and h*)
i = F[g, h, 5];
(*do more calculations involving i*)
];
NIntegrate[G[x, y, z, phi, opts], {x, 2, 10}, {y, 0, x}, {z, 0, 3},
Method -> "QuasiMonteCarlo",  PrecisionGoal -> 3]];

-
In order for this to work, is it necessary to put the definition of G inside the Module? I ask because G is a stand-in for a chain of about 6 functions constituting maybe 150 lines of my actual code. That seems like a lot to put inside one block, and besides, a couple of those functions need to be callable independently. (+1 though) –  David Z Jan 19 '12 at 5:14
@David Terribly sorry, I just noticed your question now, while it has been hanging here for a while. You could use Block[{F},...] in place of Module, and then keep G outside. Thanks for the accept! –  Leonid Shifrin Jan 31 '12 at 9:53