简体   繁体   中英

Handling byref and out params in translating C# to F#

Can someone help to translate the following C# example code from the Alglib library to F#? I was unable to find any example on the internet as to how to use it from F#.

public static void function1_grad(double[] x, ref double func, double[] grad, object obj)
{
    // this callback calculates f(x0,x1) = 100*(x0+3)^4 + (x1-3)^4
    // and its derivatives df/d0 and df/dx1
    func = 100*System.Math.Pow(x[0]+3,4) + System.Math.Pow(x[1]-3,4);
    grad[0] = 400*System.Math.Pow(x[0]+3,3);
    grad[1] = 4*System.Math.Pow(x[1]-3,3);
}
public static int Main(string[] args)
{
    //
    // This example demonstrates minimization of f(x,y) = 100*(x+3)^4+(y-3)^4
    // using LBFGS method.
    //
    // Several advanced techniques are demonstrated:
    // * upper limit on step size
    // * restart from new point
    //
    double[] x = new double[]{0,0};
    double epsg = 0.0000000001;
    double epsf = 0;
    double epsx = 0;
    double stpmax = 0.1;
    int maxits = 0;
    alglib.minlbfgsstate state;
    alglib.minlbfgsreport rep;

    // first run
    alglib.minlbfgscreate(1, x, out state);
    alglib.minlbfgssetcond(state, epsg, epsf, epsx, maxits);
    alglib.minlbfgssetstpmax(state, stpmax);
    alglib.minlbfgsoptimize(state, function1_grad, null, null);
    alglib.minlbfgsresults(state, out x, out rep);

    System.Console.WriteLine("{0}", alglib.ap.format(x,2)); // EXPECTED: [-3,3]

    // second run - algorithm is restarted
    x = new double[]{10,10};
    alglib.minlbfgsrestartfrom(state, x);
    alglib.minlbfgsoptimize(state, function1_grad, null, null);
    alglib.minlbfgsresults(state, out x, out rep);

    System.Console.WriteLine("{0}", rep.terminationtype); // EXPECTED: 4
    System.Console.WriteLine("{0}", alglib.ap.format(x,2)); // EXPECTED: [-3,3]
    System.Console.ReadLine();
    return 0;
}

And here is my (poor) attempt at a F# translation:

let function1_grad(x : float [], func : float, grad : float [], obj_ : obj) =
    func <- 100.*System.Math.Pow(x.[0]+3.,4.) + System.Math.Pow(x.[1]-3., 4.)
    grad.[0] <- 400.*System.Math.Pow(x.[0]+3,3.);
    grad.[1] <- 4.*System.Math.Pow(x.[1]-3.,3.);

let runOptim() =

    let x = [|0.; 0.|]
    let epsg = 0.0000000001
    let epsf = 0.
    let epsx = 0.
    let stpmax = 0.1
    let maxits = 0
    let state = alglib.minlbfgsstate
    let mutable rep = alglib.minlbfgsreport()

    // first run
    alglib.minlbfgscreate(1, x, state)
    alglib.minlbfgssetcond(state, epsg, epsf, epsx, maxits)
    alglib.minlbfgssetstpmax(state, stpmax)
    alglib.minlbfgsoptimize(state, function1_grad, null, null)
    alglib.minlbfgsresults(state, x, rep)

    printfn "{0}", alglib.ap.format(x,2)

    let x = [|10.; 10.|]
    alglib.minlbfgsrestartfrom(state, x)
    alglib.minlbfgsoptimize(state, function1_grad, null, null)
    alglib.minlbfgsresults(state, x, rep)

    printfn "%A" rep.terminationtype
    printfn "%A" (alglib.ap.format(x, 2))
    System.Console.ReadLine() |> ignore

That library uses a lot of byref and out parameters, so it's not straightforward to translate it to F#, you need to handle them properly:

#r @"C:\packages\alglibnet2\lib\alglibnet2.dll"

open System

let function1_grad(x : float [], func : float byref, grad : float [], obj_ : obj) =
    func <- 100.*System.Math.Pow(x.[0]+3.,4.) + System.Math.Pow(x.[1]-3., 4.)        
    grad.[0] <- 400.*System.Math.Pow(x.[0]+3. ,3.)
    grad.[1] <- 4.*System.Math.Pow(x.[1]-3. ,3.)

let runOptim() =

    let x = ref [|0.; 0.|]
    let epsg = 0.0000000001
    let epsf = 0.
    let epsx = 0.
    let stpmax = 0.1
    let maxits = 0

    let state = alglib.minlbfgscreate(1, !x)
    alglib.minlbfgssetcond(state, epsg, epsf, epsx, maxits)
    alglib.minlbfgssetstpmax(state, stpmax)
    alglib.minlbfgsoptimize(state, (fun a b c d -> function1_grad(a, &b, c, d)), null, null)
    let rep = alglib.minlbfgsresults(state, x)

    printfn "%s" (alglib.ap.format(!x, 2))

    let x = ref [|10.; 10.|]
    alglib.minlbfgsrestartfrom(state, !x)
    alglib.minlbfgsoptimize(state, (fun a b c d -> function1_grad(a, &b, c, d)), null, null)
    alglib.minlbfgsresults(state, x, ref rep)

    printfn "%i" rep.terminationtype
    printfn "%s" (alglib.ap.format(!x, 2))
    System.Console.ReadLine() |> ignore

I recommend you to read a bit about how F# handles out and byref parameters, for instance don't confuse ref with byref in the declaration and keep in mind that out parameters can be seen as additional tupled results for F#.

But I think the trickiest part is the conversion to a delegate with a byref parameter, you can read about it in this answer .

Note that you can use the ** operator instead of Math.Pow :

func <- 100. * (x.[0]+3. ** 4.) + (x.[1]-3. ** 4.)
grad.[0] <- 400. * (x.[0]+3. ** 3.)
grad.[1] <- 4. * (x.[1]-3. ** 3.)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM