I want to create a compiled function from inter-dependent code blocks. Here is a minimal example showing the salient features based on strings:

f[x_, g_] := Module[{template, code, block},
    block = StringReplace["z = y*#x#;", "#x#" -> ToString[x, InputForm]]; 
    template = "Compile[{}, Module[{y,z}, y = #g#[#x#]; #block# z], CompilationTarget->\"C\"]";
    code = StringReplace[template, {"#block#"->block, 
        "#g#"->ToString[g, InputForm], "#x#"->ToString[x, InputForm]}];
    ToExpression[code]
];

Calling f[3, (#^2 &)][] yields 27.

Note that

  • code block(s) use variables from the template (and other code blocks)
  • code block(s) and template use function arguments; actual arguments can be compiled functions themselves
  • actual code blocks and template are larger and more complicated

This approach has shortcomings:

  • StringReplace does not work recursively, so multiple calls are necessary. Dependencies need to be considered, which can get complicated.
  • syntax highlighting et al do not work on string literals
  • conversions like ToString[g,InputForm] seem unnecessary and potentially slow

What would be an approach that avoids these shortcomings?

This question follows up on
Compile from String using function arguments

share|improve this question
    
What's the actual question here? – kutaisi 17 hours ago
    
@kutaisi "What is a better approach?" – mrupp 16 hours ago
    
You may be interested in this answer mathematica.stackexchange.com/a/24596/66 – faysou 16 hours ago
up vote 7 down vote accepted

To compose code from blocks you could use simple quoting mechanism like following:

ClearAll[quote, unquote, eval]
SetAttributes[{quote, unquote}, HoldAllComplete]
quote@expr : Except@_Symbol :=
    Unevaluated@expr /. {x : _unquote | _quote :> x, s_Symbol -> quote@s}
unquote@args___ := args
eval = # /. HoldPattern@quote@s_ :> s &;

or its more elaborate version.

Those code fragments that are intended to appear in final compiled code should be quoted, and those that should evaluate, during "code assembly", should be unquoted:

ClearAll@f
f[x_, g_] := Module[{code, block},
    block = quote[z = y x];
    code = quote@Compile[{}, 
        quote[Module][{y, z}, y = g[x]; unquote@block; z], 
        CompilationTarget -> "C"
    ];
    code // eval
];

In above definition constituent code blocks are quoted. block variable inside Compile is unquoted because we want it to evaluate, so that quoted code, assigned to this variable, will be inserted in desired position. There's also quote around Module which at first might seem superfluous, but it's needed to prevent outer Module (one with code and block variables) from renaming variables of inner Module, so that y and z used in block and those used in code are interpreted as the same variables.

To make sure we're actually compiling what we want, we can inspect final compiled expression by evaluating f function in environment with Compiled dynamically replaced by Hold:

Block[{Compile = Hold}, f[3, (#^2 &)]]
(* Hold[{}, Module[{y, z}, y = (#1^2 &)[3]; z = y 3; z], CompilationTarget -> "C"] *)

We can inspect compiled code:

Needs@"CompiledFunctionTools`"
f[3, (#^2 &)] // CompilePrint
(*
        No argument
        3 Integer registers
        Underflow checking off
        Overflow checking off
        Integer overflow checking on
        RuntimeAttributes -> {}

        I0 = 3
        Result = I2

1   I1 = Square[ I0]
2   I2 = I1 * I0
3   Return
*)

Evaluation of compiled function gives expected result:

f[3, (#^2 &)][]
(* 27 *)
share|improve this answer
    
Clean solution that worked out-of-the-box and avoided the problems of the string-based version. – mrupp 14 hours ago

You already have a very good and general answer for your question. Just for completeness I wanted to show you an alternative which is much less general but shows that there is actually not too much magic in manipulating unevaluated code:

f[x_, g_] := Module[{template, code, block, module},
  template = HoldComplete[
    Compile[{}, module[{y, z}, y = g[x]; block; z], CompilationTarget -> "C"]
  ];
  code = template /. block :> (z = y*x) /. module -> Module;
  ReleaseHold[code]
];

The idea is straightforward: define a template expression for your code, then use standard pattern matching to insert code fragments into that code (and implicitly also use the standard litteral insertion of function arguments) and when done evaluate the result using ReleaseHold. The only part that is a bit tricky is that you need to prevent the outer Module to rename the inner Modules local variables (exactly the same reason why jkuczm needed an extra quote for Module). Here I solved it by using a dummy symbol module which is only replaced by Module after the outer Module has done its magic.

One technique that you might need when going that way is how you can construct a replacement rule from a held expression. One possibility is this:

block = Hold[z = y*x];
blockrule = RuleDelayed @@ Prepend[block, HoldPattern[block]]
template /. blockrule /. module :> Module
share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

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