In this great answer a compiled version of the Nelder-Mead algorithm is presented.
Since it works on arbitrary dimensions (i.e. arbitrary number of arguments), it has to use apply on the objective function. The problem is that Apply is not directly supported inside Compile. To overcome this limitation the following code is used:
(* Produces compiled code for the Nelder-Mead algorithm with the objective function inlined *)
ClearAll[apply];
SetAttributes[apply, HoldAll];
apply[f : (_Function | _CompiledFunction), vars : {__Symbol}] :=
With[{applied := f @@ vars},
Function[arglist, Block[vars, vars = arglist; applied]]
];
This seems to work (inside the package), but I have no idea, how it works.
Could somebody explain the techniques behind this code snippet? Specifically:
- What does
SetDelayeddo insideWith? Why does
varsappear twice in the arguments toBlock?Why doesn't this minimal example work? (I believe it mimics what is done in the mentioned answer...)
Clear[a, b, c, x, y, z, objectiveFunction, cfunc]; objectiveFunction = Compile[{a, b, c, x, y, z} , (a - x)^2 + 50 (b - y)^2 + (c - z)^2]; cfunc = With[{f = apply[objectiveFunction, {a, b, c, x, y, z}]}, Compile[ {{pts, _Real, 1}}, f@pts] ] << CompiledFunctionTools` CompilePrint[cfunc]
EDIT:
Here is the working snippet. Note that "InlineCompiledFunctions" must be set to True to avoid the call to MainEvaluate.
ClearAll[a, b, c, x, y, z, apply, objectiveFunction, cfunc];
(* Produces inlinable code for use inside Compile (where Apply is not \
supported directly) *)
SetAttributes[apply, HoldRest];
apply[f : (_Function | _CompiledFunction), vars : {__Symbol}] :=
Function[arglist, Block[vars, vars = arglist; f @@ vars]];
objectiveFunction = Compile[{a, b, c, x, y, z} ,
(a - x)^2 + 50 (b - y)^2 + (c - z)^2];
apply[objectiveFunction, {a, b, c, x, y, z}]
cfunc = With[{f = apply[objectiveFunction, {a, b, c, x, y, z}]},
Compile[ {{pts, _Real, 1}}, f@pts,
CompilationOptions -> {"InlineCompiledFunctions" -> True}]
]
<< CompiledFunctionTools`
CompilePrint[cfunc]
Applyas discussed here evaluates at (pre)Compile-time and is a tool of code generation. The option you mention governs the auto-compilation ofApply, when it would be explicitly present in theCompilebody when auto-compiling. SinceApplyis only supported for 3 heads (List,PlusandTimes) byCompile, it does not generally make sense to set the mentioned option to a finite number. In case you have one of those heads, you can of course reset it. – Leonid Shifrin May 04 '12 at 09:18Infinity? – Szabolcs May 04 '12 at 09:27Infinityis equivalent to saying "don't attempt to auto-compile at all". The fact that only 3 heads are supported means thatCompiledoes not generally supportApply, which is probably the reason for this setting. For those heads, it can be reset manually, as I mentioned. – Leonid Shifrin May 04 '12 at 09:33