6

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?

Michael E2
  • 235,386
  • 17
  • 334
  • 747
mattiav27
  • 6,677
  • 3
  • 28
  • 64
  • 4
    Compile[{{x, _Real}}, Evaluate[x^2 + g[x]]] – QuantumDot Mar 13 '20 at 17:30
  • 1
    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. – Michael E2 Mar 13 '20 at 21:45
  • @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. – Leonid Shifrin Mar 13 '20 at 23:09
  • Fair enough, I'm happy to retract my attempt at commenting – ilian Mar 13 '20 at 23:23
  • @ilian I didn't at all mean to discourage your comments, I hope it didn't sound like that. – Leonid Shifrin Mar 13 '20 at 23:30
  • Not at all, I had just wanted to mention "InlineExternalDefinitions" which the answer already does. – ilian Mar 13 '20 at 23:42
  • 2
    A bit cumbersome but very robust is this way: Block[{x}, With[{code = x^2 + g[x]}, Compile[{{x, _Real}}, code]]]. – Henrik Schumacher Mar 14 '20 at 07:44

1 Answers1

8

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.

Leonid Shifrin
  • 114,335
  • 15
  • 329
  • 420
  • So what good is "InlineExternalDefinitions->True" when withGlobalFunctions is superior in every way? Why can't "InlineExternalDefinitions->True" simply call withGlobalFunctions? – QuantumDot Apr 01 '20 at 18:30
  • @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. – Leonid Shifrin Apr 01 '20 at 18:44
  • Ah, I was under the impression that you were a developer. – QuantumDot Apr 01 '20 at 18:49
  • @QuantumDot Not of Compile, no. – Leonid Shifrin Apr 01 '20 at 19:07