4
$\begingroup$

I am working on a project in which I need to dynamically generate CompiledFunctions from Inactive expressions. For example, I might be given something like

expr = Inactivate[Block[{x = ConstantArray[0.0, 100]},
    Do[x[[n]] = Sin[n/Pi]*Cos[y + n/100], {n, 100}];
    x]];

from which I need to generate the CompiledFunction given by

Compile[{y}, Block[{x = ConstantArray[0.0, 100]},
    Do[x[[n]] = Sin[n/Pi]*Cos[y + n/100], {n, 100}];
    x]];

The first thing I tried was simply Compile[{y}, Evaluate@Activate@expr], but this is no good; it actually evaluates the Block prior to compilation, and gives me a CompiledFunction which computes each array element separately.

For my project, it is crucial that the loop assigning to the array x be compiled, and I cannot figure out the right combination of Evaluate, Activate, Hold, etc. that will give me what I want. How can I take expr and generate the CompiledFunction given above?

P.S. I've noticed that ConstantArray[] doesn't seem to be compilable. Is there a faster way to declare an empty array in compiled functions?

$\endgroup$
7
$\begingroup$

Use good old Hold in place of Inactivate:

expr = 
   Hold[Block[{x = ConstantArray[0.0, 100]},
     Do[x[[n]] = Sin[n/Pi]*Cos[y + n/100], {n, 100}];
     x]];

then you can do simply use

expr /. Hold[code_] :> Compile @@ Hold[{y}, code]

which is, using injector pattern.

$\endgroup$
  • $\begingroup$ My apologies, but I appear to have spoken too soon. The code you've given above doesn't work for me; the problem is that Compile doesn't localize the y from expr, and using "InlineExternalDefinitions" -> True doesn't help. $\endgroup$ – David Zhang Nov 3 '14 at 18:24
  • $\begingroup$ @DavidZhang Indeed. That's what happens when comment is directly transferred into an answer. I assumed that you tested it and it worked for you, but I should've tested myself. See my edit. $\endgroup$ – Leonid Shifrin Nov 3 '14 at 18:52
  • $\begingroup$ Thanks much. Can you explain why this second version works but the first didn't? I am rather puzzled as to why y wasn't localized. $\endgroup$ – David Zhang Nov 3 '14 at 18:58
  • 1
    $\begingroup$ @DavidZhang Read this discussion, and references therein. Thanks for the accept. You may want to change your mind though, if some good answer involving (In)Activate arrives later. $\endgroup$ – Leonid Shifrin Nov 3 '14 at 19:04
  • $\begingroup$ Perhaps, but it is worth noting (if only for the benefit of future readers) that my original expr using Inactive can easily be transformed into your version using Hold; simply use Activate@Hold@Evaluate@expr. $\endgroup$ – David Zhang Nov 3 '14 at 19:08
6
$\begingroup$

This seems to work:

cf = Block[{n, x, y}, Inactive[Compile][{y}, expr] // Activate]

Edit - Some observations

Inactivate effectively blocks evaluation of heads in an expression by wrapping them with Inactive. It does not hold or otherwise protect other symbols from being evaluated. This was inconvenient above, and I used Block to protect n, x and y in case they had been assigned values.

There are ways to take advantage of this, but they may just as easily bite you. The inactive piece of code expr may contain parameters which can be substituted at compile time. For example, take the following modification of the OP's code:

expr = Inactivate[
  Block[{x = ConstantArray[0.0, max]}, 
   Do[x[[n]] = Sin[n/Pi]*Cos[y + n/max], {n, max}];
   x]]

Mathematica graphics

Note that max, n, x, and y are in black and in the inactive brown color. If they had had values when expr was defined, the values would have been subsituted -- a disaster if, say, x = 2. On the other hand, we can treat the code as a function of max and compile it at run time with whatever value of max we wish.

cf = Block[{max = 50, n, x, y}, Inactive[Compile][y, expr] // Activate];

We can examine the expression that was compiled:

cf[[-2]]
(*
  Function[{y}, 
   Block[{x = ConstantArray[0., 50]}, 
    Do[x[[n]] = Sin[n/π] Cos[y + n/50], {n, 50}]; x]]
*)

While it can be done, you have to block all the variables in expr. Using Hold as Leonid shows seems preferable. A judicious application of replacement before compiling, i.e., Compile @@ (Hold[{y}, code] /. max -> 50), reproduces my second example above. It has the advantage of not needing to know what variables are used in the code represented by expr.

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