6
$\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$
7
  • 4
    $\begingroup$ Compile[{{x, _Real}}, Evaluate[x^2 + g[x]]] $\endgroup$ Commented Mar 13, 2020 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$ Commented Mar 13, 2020 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$ Commented Mar 13, 2020 at 23:09
  • $\begingroup$ Fair enough, I'm happy to retract my attempt at commenting $\endgroup$ Commented Mar 13, 2020 at 23:23
  • 2
    $\begingroup$ A bit cumbersome but very robust is this way: Block[{x}, With[{code = x^2 + g[x]}, Compile[{{x, _Real}}, code]]]. $\endgroup$ Commented Mar 14, 2020 at 7:44

1 Answer 1

8
$\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.

$\endgroup$
4
  • $\begingroup$ So what good is "InlineExternalDefinitions->True" when withGlobalFunctions is superior in every way? Why can't "InlineExternalDefinitions->True" simply call withGlobalFunctions? $\endgroup$ Commented Apr 1, 2020 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$ Commented Apr 1, 2020 at 18:44
  • $\begingroup$ Ah, I was under the impression that you were a developer. $\endgroup$ Commented Apr 1, 2020 at 18:49
  • $\begingroup$ @QuantumDot Not of Compile, no. $\endgroup$ Commented Apr 1, 2020 at 19:07

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.