1
$\begingroup$

I need to use Compile[] for a very large expression to be integrated. But for Compile[] the expression has to be evaluated, and this takes endless time.

The expression is composed of several functions having lists/tensors as arguments and return types. Something like this:

aFun[..., bFun[..., cFun[...], ...], ...]

Of course, that gets very unhandy quickly.

In a usual programming language I would write: cTensor=cFun[...]; bTensor=bFun[..., cTensor, ...]; aTensor=aFun[..., bTensor, ...];

If I could do that in Mathematica that would avoid replacing all the occurrences of the arguments in the functions and that actually should work since its going to be compiled. Unfortunately everything I tried doesn't work, like using a variable like above, using assignments like {{cT11,cT12,...},{cT21,...}}=cFun[...], also compiling the functions cFun[] etc..

How can I achieve this in Mathematica? Thank you very much in advance

Regards

edit: A very simplified example would be as follows, but the operations are much more complicated in my calculation, many parameters are filled in and there are more sums.

(f, F and g are vectors with three components)

f:=EulerMatrix[{\[Alpha]2,\[Beta]2,\[Gamma]2}].{0,0,1}Sum[Norm[EulerMatrix[{\[Alpha]2,\[Beta]2,\[Gamma]2}].{b,b,b}-EulerMatrix[{\[Alpha]1,\[Beta]1,\[Gamma]1}].{a,a,a}](EulerMatrix[{\[Alpha]2,\[Beta]2,\[Gamma]2}].{b,b,b}).(EulerMatrix[{\[Alpha]1,\[Beta]1,\[Gamma]1}].{a,a,a}),{a,20},{b,20}]

g[F_]:=EulerMatrix[{\[Alpha]1,\[Beta]1,\[Gamma]1}].{0,0,1}Sum[Norm[F-EulerMatrix[{\[Alpha]2,\[Beta]2,\[Gamma]2}].{c,c,c}]F.EulerMatrix[{\[Alpha]1,\[Beta]1,\[Gamma]1}].{c,c,c},{c,20},{d,20}]

fun=Compile[{\[Alpha]1,\[Beta]1,\[Gamma]1,\[Alpha]2,\[Beta]2,\[Gamma]2},Evaluate[g[f]]]

Unfortunately in Compile[] I have to evaluate g[f]. If I do this on my computer like this it takes about 42 seconds:

Timing[g[f];]
{41.875, Null}

When increasing the sums from 20 terms to something higher time consumption grows extremely, since f is used in g. For f and g[F] alone that's much faster:

Timing[f;]
{2.64063, Null}

Timing[g[{x, y, z}];]
{1.35938, Null}

The kernel of my problem should be that if Compile[] would first calculate f like {x,y,z}=f so that x, y and z are real values and then use these real values as parameters for g like g[{x,y,z}] it should be much faster. Of course, there would be no symbolic simplification be done what Evaluate[g[f]] would do, but that would be acceptable. I would like to write

fun=Compile[{...},
  {x,y,z}=f;
  g[{x,y,z}]
]

With x, y and z are always calculated as numeric values. But Evaluate[] has to be used in Compile[] and that leads in my tries always to evaluation of g[f].

$\endgroup$
4
  • 5
    $\begingroup$ Could you please post a minimal characteristic example of your problem, which can easily be copied and pasted into a Mathematica notebook. $\endgroup$ Commented Dec 4, 2021 at 16:02
  • 2
    $\begingroup$ (1) I don't see why you cannot write in Mma what you would write in a usual programming language. (2) Have you seen CompilationOptions -> {"ExpressionOptimization" -> False} or CompilationOptions -> {"InlineCompiledFunctions" -> False}? $\endgroup$
    – Michael E2
    Commented Dec 4, 2021 at 18:16
  • $\begingroup$ @MichaelWeyrauch: I made a little example and did put it in my message above. What I am looking for is a two step calculation instead of a symbolic evaluation of the whole calculation, but Compile[] doesn't seem to allow that. $\endgroup$
    – lxndr
    Commented Dec 5, 2021 at 11:07
  • $\begingroup$ @MichaelE2: I can write it like I would do in a usual programming language, but Mathematica automatically tries to evaluate/simplify and for that my functions are too big. In my example above f is not used much in g, but in my real calculation it's used in many places and I am working with big matrices and many terms. I saw these options to Compile[], but I don't see how these could help. $\endgroup$
    – lxndr
    Commented Dec 5, 2021 at 11:12

1 Answer 1

1
$\begingroup$

I compiled the OP's code twice, taking 78 sec. and 320 sec. It ran in $7\times10^{-5}$ sec/call), faster than the codes below. However, the compile times below are much, much shorter.

There are slightly faster run times if we use Real instead of Integer types (in Sum for example).

Manual optimization (shorter compile time, longer run time $3\times10^{-4}$ sec/call):

ClearAll[f, g];

f[e1_, e2_, n_] := e2 . N@{0, 0, 1}*
  Sum[Norm[
     e2 . {b, b, b} - 
      e1 . {a, a, a}] (e2 . {b, b, b}) . (e1 . {a, a, a}), {a, 1., 
    n}, {b, 1., n}]

g[F_, e1_, e2_, n_] := e1 . N@{0, 0, 1}*
  Sum[Norm[F - e2 . {c, c, c}] F . e1 . {c, c, c},
   {c, 1., n}, {d, 1., n}]

evaluateHeldFuncsRule[ff__] := (* evaluates functions inside `Hold` *)
  func : (Blank[#] & /@ Alternatives[ff]) :> 
   With[{res = func}, res /; True];
code = Hold[{α1, β1, γ1, α2, β2, γ2},
    Block[{e1, e2, F, n = 20.},
     e1 = EulerMatrix[{α1, β1, γ1}];
     e2 = EulerMatrix[{α2, β2, γ2}];
     F = f[e1, e2, n]; (* using a symbolic n means Sum won't *)
     g[F, e1, e2, n]   (* evaluate before compilation *)
     ]
    ] /. evaluateHeldFuncsRule[EulerMatrix, f, g];
fun = Compile @@ code; // AbsoluteTiming
(*  {0.003046, Null}  *)

Unrolled loops (longer compile time, shorter run time $1\times10^{-4}$ sec/call):

ClearAll[f, g];

f[e1_, e2_, n_] := e2 . N@{0, 0, 1}*
  Sum[Norm[
     e2 . {b, b, b} - 
      e1 . {a, a, a}] (e2 . {b, b, b}) . (e1 . {a, a, a}), {a, 1., 
    n}, {b, 1., n}]

g[F_, e1_, e2_, n_] := e1 . N@{0, 0, 1}*
  Sum[Norm[F - e2 . {c, c, c}] F . e1 . {c, c, c},
   {c, 1., n}, {d, 1., n}]

evaluateHeldFuncsRule[ff__] := 
  func : (Blank[#] & /@ Alternatives[ff]) :> 
   With[{res = func}, res /; True];
code = Hold[{α1, β1, γ1, α2, β2, γ2},
    Block[{e1, e2, F},
     e1 = EulerMatrix[{α1, β1, γ1}];
     e2 = EulerMatrix[{α2, β2, γ2}];
     F = f[e1, e2, 20]; (* Using an explicit n=20 means Sum will be *)
     g[F, e1, e2, 20]   (* evaluated before compilation *)
     ]
    ] /. evaluateHeldFuncsRule[EulerMatrix, f, g];
fun20 = Compile @@ code; // AbsoluteTiming
(*  {0.019748, Null}  *)

The "usual" way? (much longer run times, though, $3 \times 10^{-2}$ sec/call):

(fC = Compile @@ 
    With[{e1 = EulerMatrix[{α1, β1, γ1}],
      e2 = EulerMatrix[{α2, β2, γ2}]}, 
     Hold[{α1, β1, γ1, α2, β2, γ2}, 
      e2 . N@{0, 0, 1} Sum[
        Norm[
          e2 . {b, b, b} - 
           e1 . {a, a, a}] (e2 . {b, b, b}) . (e1 . {a, a, a}), {a, 
         1., 20.}, {b, 1., 20.}]
      ]
     ];
  
  gC = Compile @@ 
    With[{e1 = EulerMatrix[{α1, β1, γ1}],
      e2 = EulerMatrix[{α2, β2, γ2}]}, 
     Hold[{{α1, _Real}, {β1, _Real}, {γ1, _Real},
           {α2, _Real}, {β2, _Real}, {γ2, _Real},
           {F, _Real, 1}}, 
      e1 . {0, 0, 1} Sum[
        Norm[F - e2 . {c, c, c}] F . e1 . {c, c, c}, {c, 1., 20.}, {d,
          1., 20.}]
      ]
     ];
  
  fun1 = 
   Compile[{α1, β1, γ1, α2, β2, γ2}, 
    gC[α1, β1, γ1, α2, β2, γ2,
      fC[α1, β1, γ1, α2, β2, γ2]],
    CompilationOptions -> {
      "InlineExternalDefinitions" -> True
      (*,"InlineCompiledFunctions"->True*)}
    ];) // AbsoluteTiming
(*  {0.01999, Null}  *)
$\endgroup$
1
  • $\begingroup$ That helped and now it works. Thank you very much! $\endgroup$
    – lxndr
    Commented Dec 5, 2021 at 20:02

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

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