3
$\begingroup$

I want to automatically Compile an expression in between a routine of certain analytical manipulations. The number of arguments for the function may change for every run, so I would like to avoid hardcoding the arguments into my call of Compile. Consider this MWE:

vars = {x, y};
expr = Sin[x + y];
fun1 = With[{e = expr}, Compile[{{x, _Real}, {y, _Real}}, e]];

The disadvantage here is that I need to provide {{x, _Real}, {y, _Real}} as variables explicitly. I would rather like to be able to do something like

fun2 = With[{e = expr, v = Transpose[{vars, ConstantArray[_Real, Length@vars]}]},
            Compile[v, e]]

where knowing vars in advance allows me to avoid manually computing v, print it and then copy-paste into Compile. All variants of fun2 I could come up with boil down to the issue that Compile only recognizes a single variable as input. Is there any way to achieve the desired functionality?

$\endgroup$
4
$\begingroup$

This can be accomplished through Hold[] trickery:

vars = {x, y}; expr = Sin[x + y];

fun1 = Hold[Compile][Transpose[PadRight[{vars}, {2, Automatic}, {_Real}]],
                     expr] // ReleaseHold;

An alternative is to start with a Hold[] expression, inject any needed changes with With[], and then use Apply[]:

fun1 = With[{vlist = Transpose[PadRight[{vars}, {2, Automatic}, {_Real}]]},
            Compile @@ Hold[vlist, expr]];

Test:

fun1[π/6, π/3]
   1.
$\endgroup$
  • $\begingroup$ better perhaps to start with a HoldComplete than a List $\endgroup$ – b3m2a1 Mar 29 '18 at 5:02
  • $\begingroup$ Hmm, I don't think it works with Hold[] or HoldComplete[] for the second version, because the part that assembles the sequence of arguments won't fire inside a Hold[], unless you MapAt[] an Evaluate[]. $\endgroup$ – J. M. is away Mar 29 '18 at 5:04
  • 2
    $\begingroup$ I meant something like With[{vars=varList}, Compile@@Hold[vars, expr]]. Allows you to keep expr held. $\endgroup$ – b3m2a1 Mar 29 '18 at 5:28
  • $\begingroup$ Yes, injection should work; I'll edit in a bit. $\endgroup$ – J. M. is away Mar 29 '18 at 5:29
  • $\begingroup$ @J.M. Thanks a lot. I clearly need to get used to Hold a lot more. Nice and compact solution. Side question: Is there a specific reason why you are using the PadRight based way to generate the list of variables instead of my way? Is it just personal taste or is there more behind it? $\endgroup$ – Lukas Mar 29 '18 at 6:32
3
$\begingroup$

Often I do things like this with Replace as a destructuring function. Here's a super convoluted example that uses Replace, Hold, and Thread to define a Compile spec that holds all its arguments and auto-detects its variables (also not gonna lie it was kinda fun to write):

Options[compileWithVars] =
  Join[
   {
    "TypeMap" -> Automatic
    },
   Options[Compile]
   ];
compileWithVars[
  varList : {__Symbol} | Automatic : Automatic, 
  expr_, 
  ops___?OptionQ
  ] :=
 With[
  {
   varsHeld =
    Replace[Hold[varList], 
     {
      Hold[Automatic] :>
       DeleteDuplicates@
        Cases[Hold[expr], 
         s_Symbol?(Function[Null, Context[#] == $Context, 
             HoldFirst]) :> Hold[s],
         Infinity
         ],
      l_List :> Thread[l]
      }
     ],
   tm =
    Replace[
     Lookup[Flatten@{ops}, "TypeMap", Automatic ],
     Automatic :> {_ -> {_Real}}
     ]
   },
  Replace[
   Thread[
    Map[Replace[Join[#, Hold @@ (# /. tm)], Hold[l__] :> Hold[{l}]] &,
      varsHeld ], Hold],
   {
    Hold[vl : {__List}] :>
     Apply[
      Compile,
      HoldComplete[
       vl,
       expr,
       Evaluate@FilterRules[
         Flatten@{ops},
         Options[Compile]
         ]
       ]
      ],
    sheesh_ :>
     Failure["BadVarList",
      <|

       "MessageTemplate" :> "Var spec `` is invalid (originally ``)",
       "MessageParameters" :> {sheesh, 
         HoldForm @@ Thread[varsHeld, Hold]}
       |>
      ]
    }
   ]
  ]

Then we do it like so:

compileWithVars[Sin[x + y]]

blah

Or we can tell it that we expect x to be a {_Integer, 1} and everything else to default to {_Real, 2}:

compileWithVars[z*Cos[θ]*Sin[x + y], 
 "TypeMap" -> {Hold[x] -> {_Integer, 1}, _ -> {_Real, 2}}]

blib

$\endgroup$
  • 1
    $\begingroup$ Might be a useful addition: Reduce`FreeVariables[] will allow you to extract the variables from an arbitrary expression. $\endgroup$ – J. M. is away Mar 29 '18 at 6:00
  • $\begingroup$ @J.M. Ooh very useful. Can it extract them in a held manner? $\endgroup$ – b3m2a1 Mar 29 '18 at 6:01
  • $\begingroup$ Haven't tried yet, sorry. $\endgroup$ – J. M. is away Mar 29 '18 at 6:05
  • 1
    $\begingroup$ @J.M. seems it doesn't dig into things wrapped in Hold which is too bad. I'd prefer if it did but just returned that variables in held form. $\endgroup$ – b3m2a1 Mar 29 '18 at 6:53
  • 1
    $\begingroup$ @user49047 did you do compileWithVars[expr, {"RuntimeAttributes" -> {Listable}, "Parallelization" -> True}]? That seems to work for me. $\endgroup$ – b3m2a1 Apr 4 '18 at 15:43

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.