4
$\begingroup$

Consider the simple case:

g[x] := x^3;
f[x] := x^2 + g[x];
cf = Compile[{{x, _Real}}, x^2 + g[x]]

Evaluating the two functions, Mathematica complains that the second depends on the function g[x], saying:

CompiledFunction::cfex: Could not complete external evaluation at instruction 2; proceeding with uncompiled evaluation.

Which is the correct way to compile the function f in this case?

$\endgroup$
  • 4
    $\begingroup$ Compile[{{x, _Real}}, Evaluate[x^2 + g[x]]] $\endgroup$ – QuantumDot Mar 13 at 17:30
  • 1
    $\begingroup$ I get no error in V12 from g[x_] := x^3; cf = Compile[{{x, _Real}}, x^2 + g[x]], although it and @ilian's suggestion both contain MainEvaluate[]. Note the argument to g is a pattern x_, not a symbol x. $\endgroup$ – Michael E2 Mar 13 at 21:45
  • $\begingroup$ @ilian I wouldn't do that, without at least wrapping a Block[{x}, ...] around Compile. But even then, I think it is generally an error - prone practice, unless you are in full control of exact evaluation path for the stuff inside Evaluate. $\endgroup$ – Leonid Shifrin Mar 13 at 23:09
  • $\begingroup$ Fair enough, I'm happy to retract my attempt at commenting $\endgroup$ – ilian Mar 13 at 23:23
  • 1
    $\begingroup$ A bit cumbersome but very robust is this way: Block[{x}, With[{code = x^2 + g[x]}, Compile[{{x, _Real}}, code]]]. $\endgroup$ – Henrik Schumacher Mar 14 at 7:44
4
$\begingroup$

Long time ago I wrote a macro, which expands global DownValues - based definitions, called withGlobalFunctions. It can be found at the end of this post. With it, all you need to do is wrap the Compile call like this:

g[x] := x^3;
f[x] := x^2 + g[x];
cf = withGlobalFunctions @ Compile[{{x, _Real}}, f[x]]

This has the advantage over Evaluate advice in that you can't leak a global value for x in, even if it exists. And it has an advantage over "InlineExternalDefinitions" -> True advice in that it expands arbitrary long chains of calls.

The limitation of this approach is that patterns in function definitions you may want to inline / expand in this way, better be very simple, involving blanks but not much else. This is because what this does is a kind of a macro-expansion, without actual evaluation involved. So that expansion will get stuck if patterns do any non-trivial checks.

withGlobalFunctions can trivially be extended to expand definitions based on other ...Values. As written, it only expands definitions from Global` context, but that restriction can be removed or lifted as well.

| improve this answer | |
$\endgroup$
  • $\begingroup$ So what good is "InlineExternalDefinitions->True" when withGlobalFunctions is superior in every way? Why can't "InlineExternalDefinitions->True" simply call withGlobalFunctions? $\endgroup$ – QuantumDot Apr 1 at 18:30
  • $\begingroup$ @QuantumDot I don't know. "InlineExternalDefinitions" is a built-in option. Perhaps the developers wanted to play it safe, and limit it in scope. As to your last question, it is largely rhetorical, since withGlobalFunctions is not a built-in. OTOH, anyone is free to define their own wrapper around Compile, which could do that. $\endgroup$ – Leonid Shifrin Apr 1 at 18:44
  • $\begingroup$ Ah, I was under the impression that you were a developer. $\endgroup$ – QuantumDot Apr 1 at 18:49
  • $\begingroup$ @QuantumDot Not of Compile, no. $\endgroup$ – Leonid Shifrin Apr 1 at 19:07

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.