7
$\begingroup$

I'm about to build a package that will provide lots of simple functions. I want these functions to run as fast as possible when evaluating numerically but still provide symbolic results when needed. To cut down on coding errors I only want to define the core function once. I have a first cut at something that seems to work but when I look at the definition with FullDefinition[] I see things that I do not expect.

Here is a definition of one of the functions:

    Module[{code, a, v, i, W},
  code = {
    a (Cos[v] Cos[W] - Cos[i] Sin[v] Sin[W]),
    a (Cos[i] Cos[W] Sin[v] + Cos[v] Sin[W]),
    a Sin[i] Sin[v]};
  SATFast = With[
    {
     Ccode = code
     },
    Compile[{{a, _Real}, {i, _Real}, {W, _Real}, {v, _Real}},
     Ccode
     ,
     CompilationOptions -> {"InlineCompiledFunctions" -> True}, 
     CompilationTarget -> "C"
     ]
    ];

  With[{
    fun = SATFast
    },
   (*Define the special case first!*)

   SAT[av_?NumericQ, iv_?NumericQ, Wv_?NumericQ, vv_?NumericQ] := 
    fun[av, iv, Wv, vv];
   SAT[av_, iv_, Wv_, vv_] := 
    Evaluate[code /. {a -> av, i -> iv, W -> Wv, v -> vv}];
   ]
  ];

This will ultimately be in a package where I will have a usage statement for the function SAT but not for the function SATFast. I expect to be able to use SATFast inside of other functions within the package.

I'm concerned with the output of FullDefinition[SAT].

The first line of output, which was displayed as a CompiledFunction[] object in Mathematica but shows more detail here is as follows:

SAT[av$_?NumericQ, iv$_?NumericQ, Wv$_?NumericQ, vv$_?NumericQ] := 
 CompiledFunction[{10, 11.1, 5468}, {
Blank[Real], 
Blank[Real], 
Blank[Real], 
Blank[Real]}, {{3, 0, 0}, {3, 0, 1}, {3, 0, 2}, {3, 0, 3}, {3, 1, 
   0}}, {}, {0, 0, 13, 0, 
   1}, {{40, 2, 3, 0, 1, 3, 0, 4}, {40, 2, 3, 0, 2, 3, 0, 5}, {40, 1, 
    3, 0, 3, 3, 0, 6}, {40, 2, 3, 0, 3, 3, 0, 7}, {40, 1, 3, 0, 2, 3, 
    0, 8}, {16, 7, 5, 9}, {16, 4, 6, 8, 10}, {19, 10, 11}, {13, 9, 11,
     9}, {16, 0, 9, 11}, {16, 4, 5, 6, 9}, {16, 7, 8, 10}, {13, 9, 10,
     9}, {16, 0, 9, 10}, {40, 1, 3, 0, 1, 3, 0, 9}, {16, 0, 9, 6, 
    12}, {34, 1, 3, 11, 10, 12, 3, 0}, {1}}, 
Function[{a$5296, i$5296, W$5296, v$5296}, 
Block[{Compile`$9, Compile`$7, Compile`$10, Compile`$3, Compile`$11}, 
     Compile`$9 = Cos[i$5296]; Compile`$7 = Cos[
       W$5296]; Compile`$10 = Sin[v$5296]; Compile`$3 = Cos[
       v$5296]; Compile`$11 = Sin[W$5296]; {
      a$5296 (Compile`$3 Compile`$7 - Compile`$9 Compile`$10 Compile`$\
11), a$5296 (
        Compile`$9 Compile`$7 Compile`$10 + Compile`$3 Compile`$11), 
       a$5296 Sin[i$5296] Compile`$10}]], Evaluate, 
LibraryFunction[
   "/home/paulb/.Mathematica/ApplicationData/CCompilerDriver/\
BuildFolder/trail-19983/compiledFunction1.so", 
    "compiledFunction1", {{Real, 0, "Constant"}, {
     Real, 0, "Constant"}, {Real, 0, "Constant"}, {
     Real, 0, "Constant"}}, {Real, 1}]][av$, iv$, Wv$, vv$]

Line two:

SAT[av$_, iv$_, Wv$_, 
  vv$_] := {av$ (Cos[vv$] Cos[Wv$] - Cos[iv$] Sin[vv$] Sin[Wv$]), 
  av$ (Cos[iv$] Cos[Wv$] Sin[vv$] + Cos[vv$] Sin[Wv$]), 
  av$ Sin[iv$] Sin[vv$]}

So far these two lines are what I would expect but then I also get the following:

Attributes[av$] = {Temporary}

Attributes[iv$] = {Temporary}

Attributes[Wv$] = {Temporary}

Attributes[vv$] = {Temporary}

Attributes[a$5296] = {Temporary}

Attributes[i$5296] = {Temporary}

Attributes[W$5296] = {Temporary}

Attributes[v$5296] = {Temporary}

Attributes[Compile`$9] = {Temporary}

Attributes[Compile`$7] = {Temporary}

Attributes[Compile`$10] = {Temporary}

Attributes[Compile`$3] = {Temporary}

Attributes[Compile`$11] = {Temporary}

Am I not scoping my variables correctly? Is there a better way to scope the dummy variables in the symbolic definition?

$\endgroup$
3
$\begingroup$

I believe that what you are doing is fine and that the Temporary attributes may be ignored. Symbols created by Module bear this by default but it should not affect the definitions you create.

Using Block in place of Module will clean up the definitions somewhat, but understand what it does before applying it blindly.

Block[{code, a, v, i, W}, 
  code = {a (Cos[v] Cos[W] - Cos[i] Sin[v] Sin[W]), 
    a (Cos[i] Cos[W] Sin[v] + Cos[v] Sin[W]), a Sin[i] Sin[v]};

  SATFast = 
   Compile[{{a, _Real}, {i, _Real}, {W, _Real}, {v, _Real}}, #, 
      CompilationOptions -> {"InlineCompiledFunctions" -> True}] &[code];

  (SAT[a_?NumericQ, i_?NumericQ, W_?NumericQ, v_?NumericQ] := #[a, i, W, v]) &[
   SATFast];

  SAT[a_, i_, W_, v_] = code;
];


FullDefinition[SAT]
SAT[a_?NumericQ, i_?NumericQ, W_?NumericQ, v_?NumericQ] := 
 CompiledFunction[Argument count : 4 Argument types : {_Real, _Real, _Real, _Real}][
  a, i, W, v]

SAT[a_, i_, W_, v_] = {a (Cos[v] Cos[W] - Cos[i] Sin[v] Sin[W]), 
  a (Cos[i] Cos[W] Sin[v] + Cos[v] Sin[W]), a Sin[i] Sin[v]}     

Attributes[Compile`$4] = {Temporary}

Attributes[Compile`$2] = {Temporary}

Attributes[Compile`$5] = {Temporary}

Attributes[Compile`$1] = {Temporary}

Attributes[Compile`$6] = {Temporary}

Possibly of interest:

$\endgroup$
  • $\begingroup$ J. M. thanks for the correction! $\endgroup$ – Mr.Wizard Apr 6 '17 at 7:28
  • $\begingroup$ Anytime. ;) ${}$ $\endgroup$ – J. M. will be back soon Apr 6 '17 at 7:29
  • $\begingroup$ I like the application of the pure function. Am I to understand that that is taking the place of the way I used the With[] command? Why do you use the Set with the code Why not do (SAT[a_,...]:=#)&[code] $\endgroup$ – c186282 Apr 6 '17 at 21:56
  • $\begingroup$ @c186282 Yes, I used it in place of With; because Function is not a scoping construct in the general manner that With is it does not rename Symbols inside it (ref: (20776)), making the final definitions a little cleaner (IMHO). When the entire RHS can/should be evaluated it is cleaner to use Set as that is just what it does. The Function method (or With) is needed for piecewise evaluation of the RHS. $\endgroup$ – Mr.Wizard Apr 7 '17 at 3:00
4
$\begingroup$

Here's one possibility. Write out the body as a separate block wrapped in Hold[], and then use either of Compile[] or Function[] as needed. This approach has been used in some of the add-on packages that accompany Mathematica:

(* for more complicated cases, you'd have something like Hold[Module[(* stuff *)]] *)
SATBody := Hold[{a (Cos[v] Cos[W] - Cos[i] Sin[v] Sin[W]),
                 a (Cos[i] Cos[W] Sin[v] + Cos[v] Sin[W]), a Sin[i] Sin[v]}]

SATCompiled = Function[body, 
                       Compile[{{a, _Real}, {i, _Real}, {W, _Real}, {v, _Real}}, body, 
                               CompilationOptions -> {"InlineCompiledFunctions" -> True},
                               CompilationTarget -> "C"], {HoldAll}] @@ SATBody;

SATUncompiled = Function[body, Function @@ Hold[{a, i, W, v}, body], {HoldAll}] @@ SATBody;

SAT[a_, i_, W_, v_] := If[Apply[And, NumericQ /@ {a, i, W, v}], (* or some other test *)
                          SATCompiled[a, i, W, v], 
                          SATUncompiled[a, i, W, v]]
$\endgroup$
  • $\begingroup$ "injector pattern" would make this more comfortable, IMHO $\endgroup$ – Mr.Wizard Apr 6 '17 at 7:29
  • $\begingroup$ Yes, With[{body = SATBody}, SATCompiled = Compile[(* stuff *)]], would work too, I guess. $\endgroup$ – J. M. will be back soon Apr 6 '17 at 7:31

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.