Avoiding MainEvaluate in a CompiledFunction to fetch global variables

10

9

I'm trying to write a simulation using Mathematica 8.0. Since I will most likely be doing the same operation over and over again, I'm trying to Compile whatever I can. However, I've been having problems avoiding MainEvaluate calls when I define CompiledFunction objects which refer to global variables. A simplified sample of what I'm trying to use is:

g = 4.49*^3;
m = 1.;
s = 1.;
ϵ = 2.;
sAcceleration = Compile[{{sPosition, _Real, 1}},
   (-g (m + s))/(sPosition.sPosition + ϵ*ϵ)^(3/2) sPosition];

I have also tried wrapping the whole thing inside a Module, to no avail:

sAcceleration2 =  Compile[{{sPosition, _Real, 1}},
  Module[{gg = g, mm = m,  ss = s, ϵϵ = ϵ^2},
    (-gg (mm + ss))/(sPosition.sPosition + ϵϵ)^(3/2) sPosition]];

They seem to run just fine. However, when I take a look at what the CompiledFunction is trying to do internally using CompilePrint

<< CompiledFunctionTools`
CompilePrint[sAcceleration]
CompilePrint[sAcceleration2]

I get, respectively

1 R1 = MainEvaluate[ Function[{sPosition}, g][ T(R1)0]]

...

3 R1 = MainEvaluate[ Function[{sPosition}, m][ T(R1)0]]

4 R4 = MainEvaluate[ Function[{sPosition}, s][ T(R1)0]]

...

7 R6 = MainEvaluate[ Function[{sPosition}, ϵ][ T(R1)0]]

8 R7 = MainEvaluate[ Function[{sPosition}, ϵ][ T(R1)0]]

...

and

1 R1 = MainEvaluate[ Function[{sPosition}, g][ T(R1)0]]

2 R3 = MainEvaluate[ Function[{sPosition}, m][ T(R1)0]]

3 R4 = MainEvaluate[ Function[{sPosition}, s][ T(R1)0]]

4 R6 = MainEvaluate[ Function[{sPosition}, ϵ][ T(R1)0]]

...

Since I will most likely be toying with different values for these variables in different simulation runs, but they will be held constant within a given run, can I avoid both MainEvaluate and feeding these variables to my function as additional arguments? Thanks in advance.

Editortoise-Composerpent

Posted 2012-06-29T10:27:15.057

Reputation: 1 126

you might want to wait a bit longer before accepting the answer, in case someone else comes up with something better than I did. You can remove the checkmark for a while to encourage others to look at the question. – acl – 2012-06-29T10:58:14.680

Answers

12

Try this:

g = 4.49*^3;
m = 1.;
s = 1.;
ϵ = 2.;
With[{g = g, m = m, s = s, ϵ = ϵ},
 sAcceleration = Compile[{{sPosition, _Real, 1}},
     (-g (m + s))/(sPosition.sPosition + ϵ*ϵ)^(3/2) sPosition];
 sAcceleration2 = Compile[{{sPosition, _Real, 1}}, 
   Module[{gg = g, mm = m, ss = s, ϵϵ = ϵ^2},
     (-gg (mm + ss))/(sPosition.sPosition + ϵ)^(3/2) sPosition]];
 ]

that is, inject the values before evaluation with With.

By the way, if you do SetSystemOptions["CompileOptions" -> {"CompileReportExternal" -> True}] then you'll be warned of calls to the main evaluator at compile time (i.e. without using CompilePrint).

acl

Posted 2012-06-29T10:27:15.057

Reputation: 19 146

Works as intended! Thanks! – Editortoise-Composerpent – 2012-06-29T10:35:30.477

1By the way, using capitalized variable names can lead to conflicts. For instance, N and K are reserved. I use things like thisIsAVar to avoid this. Of course nothing will happen to you if you keep using G and so on, but you can sometimes end up with hard to diagnose bugs. – acl – 2012-06-29T11:28:56.947

I know, thanks. I usually use longer names for variables, but just this time I gave in to laziness. – Editortoise-Composerpent – 2012-06-29T12:42:35.213

7

You could go even further than @acl's answer with something like this:

mkCF[g_, m_, s_, e_] := Module[{body, SPosition},
  body = (-g (m + s))/(SPosition.SPosition + e*e)^(3/2) SPosition;
  With[{body = body}, Compile[{{SPosition, _Real, 1}}, body]]
  ]

This has the advantage that body has seen an evaluation which in this cases means a simplification and consequently less to compute.

user21

Posted 2012-06-29T10:27:15.057

Reputation: