8
$\begingroup$
f = #^2 &;
Compile[{{x, _Real, 1}}, f /@ x, 
  CompilationOptions -> {"InlineExternalDefinitions" -> 
     True}] // CompilePrint

works fine.

f = g[#]^2 &;
g = # - 1 &;
Compile[{{x, _Real, 1}}, f /@ x, 
  CompilationOptions -> {"InlineExternalDefinitions" -> 
     True}] // CompilePrint

will have MainEvaluate[ Hold[g][ R1]]. I guess the automatic replacement will only do such replacement once.

I have found one workaround so far, but it requires listing all the related functions (not necessary in the correct replacement order).

ReleaseHold[
  Hold[Compile[{{x, _Real, 1}}, f /@ x]] //. 
   Flatten[OwnValues /@ Unevaluated@{f, g}]] // CompilePrint

Is there a better one without listing {f, g}?

$\endgroup$
5
  • $\begingroup$ The mechanism used by "InlineExternalDefinitions" -> True seems opaque, that is I cannot find any information about how it is implemented using Trace. Would it be useful to you simply to programmatically extract f and g from your Compile expression for use in your own method? $\endgroup$ – Mr.Wizard Jul 21 '16 at 10:28
  • $\begingroup$ @Mr.Wizard Yeah, if I can programmatically list all the "Global" symbols like f inside Compile and the derived symbols like g, the problem is solved. But I don't know how to do that. $\endgroup$ – vapor Jul 21 '16 at 11:35
  • $\begingroup$ Would you like to have all non System` Symbols or do you prefer to restrict it to Global` Symbols? $\endgroup$ – Mr.Wizard Jul 21 '16 at 11:37
  • $\begingroup$ @Mr.Wizard I am not sure how to define that, since all temporary variable is unwanted (including param of Compile and local variable in Module inside compile which has Global context) $\endgroup$ – vapor Jul 21 '16 at 11:42
  • $\begingroup$ Okay. I'll look at this and try to come up with something useful. $\endgroup$ – Mr.Wizard Jul 21 '16 at 11:47
7
$\begingroup$

This is a proof of concept of the idea of parsing the Compile expression, extracting Symbols, finding dependent definitions, and inserting them back into Compile.

First a HoldFirst variation of heldCases:

makeHeld[(L_ -> R_) | (L_ :> R_)] := L :> HoldComplete[R];
makeHeld[pat_] := x : pat :> HoldComplete[x];

Attributes[heldCases] = {HoldFirst};

heldCases[expr_, rule_, args___] := 
  Join @@ Cases[Unevaluated @ expr, makeHeld @ rule, args]

Then the main definition:

Attributes[inject] = {HoldFirst};

inject[all : Compile[var_, body_, opts___]] :=
  Complement[
    heldCases[body, s_Symbol /; Context[s] =!= "System`", {-1}, Heads -> True], 
    heldCases[var, s_Symbol | {s_Symbol, __} :> s, {2}]
  ] //
    Apply[FullDefinition] //
    InputForm // ToString // ToHeldExpression //
    Cases[(Set | SetDelayed)[s_Symbol, RHS_] :> (HoldPattern[s] :> RHS)] //
    ( Unevaluated[all] //. # & )

Usage :

Compile[{{x, _Real, 1}}, f /@ x, 
  CompilationOptions -> {"InlineExternalDefinitions" -> True}] // inject

Replacing Unevaluated with Hold in the last line of inject shows the intermediate output:

Hold[Compile[{{x, _Real, 1}}, ((#1 - 1 &)[#1]^2 &) /@ x, 
  CompilationOptions -> {"InlineExternalDefinitions" -> True}]]

Notes

  • This is rather verbose for what is conceptually a fairly simple operation.

  • I am not happy with the ToString conversion and back. Possibly I should have extracted Symbols only and used OwnValues as you did, but since I resorted to pulling the definitions with FullDefinition using that information directly seemed a natural approach.

  • The replacements should really only be done on the body rather than the entire Compile, but the latter was expedient for a proof of concept.

$\endgroup$

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.