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
SetDelayed
do insideWith
? Why does
vars
appear 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]
Apply
as 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 theCompile
body when auto-compiling. SinceApply
is only supported for 3 heads (List
,Plus
andTimes
) 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. $\endgroup$Infinity
? $\endgroup$Infinity
is equivalent to saying "don't attempt to auto-compile at all". The fact that only 3 heads are supported means thatCompile
does not generally supportApply
, which is probably the reason for this setting. For those heads, it can be reset manually, as I mentioned. $\endgroup$