NMR
NMR

Reputation: 166

Optimizing repeatedly called math function

I am writing some code as part of a nonlinear regression tool and I am trying to figure out an approach for returning the nth partial derivative of a given function in a way that is a good balance of readable and fast. The function (and the analytic representations of the partials) are known at runtime, these can be hardcoded. Here's what I have so far (which works):

let getPartials (paramVect: array<float>) idx =
    let a = paramVect.[0]
    let b = paramVect.[1]
    let c = paramVect.[2]
    match idx with
    | 1 -> (fun  x -> (1.0+b+c*x)**(-1.0/b)) // df(x)/da
    | 2 -> (fun  x -> ((a*(1.0+c*b*x)**(-(b+1.0)/b))*((b*c*x+1.0)*Math.Log(b*c*x+1.0)-b*c*x))/(b*b))       // df(x)/db
    | 3 -> (fun  x -> -a*x*(b*c*x+1.0)**(-(b+1.0)/b))   //  df/dc
    | _ -> (fun x  -> 0.0)   //everything else is zero

The way I am using this is to first construct a partial function with the parameter vector so that I am minimizing the number of times that needs to be passed in. Then I am repeatedly calling (getPartials(i) x_val) to construct a jacobian. This function gets called an extremely large number of times over the lifecycle the program.

I am getting pretty acceptable performance with this, however, I suspect it can be improved. Profiling shows that the evaluation of the 2nd function calcuation (the long one) is a cpu drain - can this be optimized? I am unsure if the anonymous functions create a performance problem, as readable as it is...

I am brand new to F# programming, so please let me know if you spot any egregarious problems with either the style/form or the performance!

Thank you


Update: after implementing the changes suggested by JohnPalmer and refactoring so that instead of returning an anonymous function which accepts the x-value as an argument, it instead does the whole calculation in-place, I am seeing approximately a 300% speed increase. It was more convenient to be able to return the partial functions, but not worth the cost.

let getPartials  (paramVect: array<float>) idx x =
let a = paramVect.[0]
let b = paramVect.[1]
let cbx =  paramVect.[1] * paramVect.[2] * x

match idx with
| 1 -> (1.0+cbx)**(-1.0/b) // df(x)/da
| 2 -> ((a*(1.0+cbx)**(-(b+1.0)/b))*((cbx+1.0)*Math.Log(cbx+1.0)-cbx))/(b**2.0) // df(x)/db
| 3 ->  -a*x*(cbx+1.0)**(-(b+1.0)/b)      //  df/dc
| _ ->  0.0                               //everything else is zero

Upvotes: 0

Views: 88

Answers (1)

phoog
phoog

Reputation: 43046

The most likely reason for the anonymous functions to cause a performance problem would be the fact that you create a new heap object each time you call the getPartials function. If you have only a small number of different paramVects then you might get some performance benefit by caching the anonymous functions.

As for the evaluation of the second expression, you might try this (taking John Palmer's suggestion to eliminate the common subexpressions):

fun  x -> let bcx = b * c * x
          let bcx1 = bcx + 1.0
          a * bcx1 ** (-(b+1.0)/b) * (bcx1 * Math.Log bcx1 - bcx)/(b*b)

Upvotes: 1

Related Questions