5
$\begingroup$

Original

I'm looking to write an integrator for a function of two variables. Here is my implementation for the RK4 update rule using FoldList.

RK4Update[fx_, x_, prms_, numsteps_] :=
Block[{},

1.0/(6.0 numsteps)
Dot[{1.0, 2.0, 2.0, 1.0},
 Rest[
  FoldList[
   fx @@ {x + #1 #2, prms} &,
   x,
   Divide[{0.0, 0.5, 0.5, 1.0}, 1.0 numsteps]
   ]
  ]
 ]

];

This function evaluates correctly when given g (below) and numerical list arguments for x and prms. I'm attempting to compile to gain speed:

Compile[
{{sts, _Real, 1}, {pms, _Real, 1}, {tsteps, _Real}},
Evaluate[RK4Update[g, sts, pms, tsteps]],
CompilationOptions -> {"InlineExternalDefinitions" -> True}]

Here g is an already compiled function of two vector arguments. The compiler is unhappy, returning errors of the form:

Part::partd: Part specification pms[[2]] is longer than depth of object. 
Part specification pms[[1]] is longer than depth of object
Part::partw: "Part 3 of 0. +sts does not exist"

and further errors from the attempted evaluation of g. I'm not sure if there is any way to proceed here. Is there a simple fix? Or is an overhaul needed?

If it's of any help, a sample function g is polynomial in elements of its arguments

(#2[[2]] #1[[3]] (1- #1[[1]])+ #1[[2]]^2(1 - #1[[1]])
+ #1[[1]](2 - #1[[1]]))/#2[[1]]

Curiously, CompilePrint returns what appears to be a fully compiled code. However, this code (perhaps unsurprisingly) does not produce the correct result.

Update

The function RK4Update can be condensed to

RK4Update[fx_, x_, prms_,numsteps_] :=
  1.0/(6.0 numsteps)
  Dot[{1.0, 2.0, 2.0, 1.0},
   FoldList[
    fx[x + #1 #2, prms] &,
    fx[x, prms],
    Divide[{0.5, 0.5, 1.0}, 1.0 numsteps]
   ]
  ]

The following compilation command outputs several errors, but returns a successfully compiled function. A Quiet prefix suppresses display of these errors:

 cf= Quiet@Compile[
 {{st, _Real, 1}, {pm, _Real, 1}, {tstep, _Real}},
 Evaluate[RK4Update[h, st, pm, tstep]],
 CompilationOptions -> {"InlineCompiledFunctions" -> True}]

where, importantly, h[x,y] = {g1[x,y],g2[x,y],g3[x,y]} with x,y each a numeric list and g_i a set of compiled functions. In the original question, g in the FoldList call returns a single number, whereas x is a triplet. A function in FoldList must output a quantity with the same dimension as its argument.

For some reason, cf outputs a 3x3 matrix whose diagonal is the desired result. My current working (yet unsatisfactorily ad hoc) solution is given by calling Diagonal[cf[Range[3],Range[2]]

$\endgroup$
8
  • $\begingroup$ Does RK4Update work when you do not compile it? $\endgroup$
    – bbgodfrey
    Jun 27, 2015 at 6:31
  • $\begingroup$ Sorry, wrong g. The g posted in the question evaluates RK4Update[g, 0.001 Range[3], 0.1 Range[2], 10.] to 0.008720484 $\endgroup$
    – user30193
    Jun 27, 2015 at 6:37
  • 1
    $\begingroup$ You might be interested in looking at Roman Maeder's implementation of a Runge-Kutta integrator in his Programming in Mathematica. The implementation there uses replacement rules, however, so it can't be compiled. $\endgroup$ Jun 27, 2015 at 16:03
  • $\begingroup$ Thanks for the suggestion, GWII. I've discovered the problem with my code and will update the post. $\endgroup$
    – user30193
    Jun 27, 2015 at 18:00
  • 1
    $\begingroup$ @sampson - If you have solved the problem, you should write your own answer to this question (and mark it as accepted) rather than updating your question. $\endgroup$
    – Myridium
    Jun 29, 2015 at 2:42

1 Answer 1

2
$\begingroup$

The problem is the use of the Evaluate function inside the Compile function. The Evaluate function requires that RK4Update, and therefore g, be passed the correct form of the x and pms parameters. The pure function definition of g provided dictates that these should be a lists of length at least three and at least two, respectively.

However, within the scope of the Compile function RK4Update, and therefore g, are passed undefined variables sts and pms, which have length 0. When RK4Update is evaluated in this context, you see the Part specification errors in the original problem.

This simple modification to the code should work (no Evaluation):

h = Compile[{{sts, _Real, 1}, {pms, _Real, 1}, {tsteps, _Real}}, 
        RK4Update[g, sts, pms, tsteps], 
        CompilationOptions -> {"InlineExternalDefinitions" -> True}]
$\endgroup$

Your Answer

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