4

In this question: In F# how can I produce an expression with a type of Func<obj>? it is shown that a single valued lambda expression is automatically cast/converted to a Func type and then accepted in the function.

I'm working with the MathNet.Numerics library and can confirm this by integrating x^2 between 0 and 10:

#r "../packages/MathNet.Numerics.3.20.0/lib/net40/MathNet.Numerics.dll"
#r "../packages/MathNet.Numerics.FSharp.3.20.0/lib/net40/MathNet.Numerics.FSharp.dll"

#load "Library1.fs"
open Library3

// Define your library scripting code here

open MathNet.Numerics.Integration

let integral = DoubleExponentialTransformation.Integrate((fun x -> x**2.0), 0.0, 10.0, 1.0)

val answer : float = 333.3333333

However, I can't get this to work with multiple valued functions. When I try this I get a type error. Does anyone know a work around for this?

open MathNet.Numerics.Optimization
open MathNet.Numerics.LinearAlgebra.Double

let newAnswer = BfgsSolver.Solve(DenseVector[|1.0, 1.0|], 
                                 (fun x y -> (x + y - 5.0) ** 2.0 + (y - x*x - 4.0) ** 2.0), 
                                 (fun x y -> DenseVector[| 2.0 * (x + y - 5.0) - 4.0 * x * (y - x*x - 4); 
                                                           2.0 * (x + y - 5.0) + 2.0 * (y - x*x - 4.0)   |])
                                                           )

and I get the following error...

Script.fsx(20,34): error FS0193: Type constraint mismatch. The type 
    ''a -> 'b -> 'c'    
is not compatible with type
    'System.Func<MathNet.Numerics.LinearAlgebra.Vector<float>,float>'   
Shillington
  • 127
  • 1
  • 9

1 Answers1

3

You can use System.Func<_,_,_>() to convert the functions like this:

let newAnswer = BfgsSolver.Solve(DenseVector[|1.0, 1.0|], 
                                 (System.Func<_,_,_>(fun x y -> (x + y - 5.0) ** 2.0 + (y - x*x - 4.0) ** 2.0)),
                                 (System.Func<_,_,_>(fun x y ->
                                    DenseVector[| 2.0 * (x + y - 5.0) - 4.0 * x * (y - x*x - 4)
                                                  2.0 * (x + y - 5.0) + 2.0 * (y - x*x - 4.0) |])))

If you find yourself needing this often you could make code a bit less ugly with a helper:

let f2 f = System.Func<_,_,_> f

UPDATE

Looking at the Math.NET documentation for this method I see now that it actually takes functions with only one input. Maybe you were confused by the Func<A, B> type signature, but in this case A is the input type and B is the output type.

F# functions with one input are converted to Func<_,_> automatically. I downloaded Math.NET and this very minimal example gives no compilation errors:

open MathNet.Numerics.Optimization
open MathNet.Numerics.LinearAlgebra.Double
BfgsSolver.Solve(DenseVector [||], (fun x -> x.[0]), (fun x -> x))

This suggests that the problem is not with converting between function types, but with using functions with the wrong arity. I should've seen this from your original error message!

TheQuickBrownFox
  • 10,544
  • 1
  • 22
  • 35
  • Thanks for your response QBF. I'm still having type issues, so I've cast everything where possible: let f2 f = System.Func<_,_> f let newAnswer = BfgsSolver.Solve(a, b, (f2 (fun (x : MathNet.Numerics.LinearAlgebra.Vector) -> [| 2.0 * (x.[0] + x.[1] - 5.0) - 4.0 * x.[0] * (x.[1] - x.[0]*x.[0] - 4); 2.0 * (x.[0] + x.[1] - 5.0) + 2.0 * (x.[1] - x.[0]*x.[0] - 4.0) |]) : MathNet.Numerics.LinearAlgebra.Vector) ) but it's still giving my errors. Did you check if your solution works? When I paste into Visual Studio I get errors – Shillington Aug 08 '17 at 15:56
  • Ok, but my original question was how do you use multiple parameters, in your answer you're essentially using a single parameter _, which we already verified worked in my original example with the Integrate function. In my response above I tried to use a variable cast to a vector (as my actual problem involves more than one parameter), but this was giving other errors... Do you have any alternative suggestions? – Shillington Aug 09 '17 at 14:00
  • @Shillington You haven't shown an example where you actually need to provide a Func with two inputs, so I can't tell where your problem might be. I think my original answer should handle it. Note that the `f2` in your comment is not the same as the `f2` in my answer. – TheQuickBrownFox Aug 09 '17 at 15:36
  • @Fox the original post had this function: fun x y -> (x + y - 5.0) ** 2.0 + (y - x*x - 4.0) ** 2.0), doesn't a Func representing this need two arguments? – Shillington Aug 09 '17 at 16:23
  • @Shillington Yes, but that method doesn't accept a function with two arguments, which is why you have that compile error. – TheQuickBrownFox Aug 09 '17 at 16:44
  • @Fox are you sure? https://numerics.mathdotnet.com/api/MathNet.Numerics.Optimization/BfgsSolver.htm the documentation says that it _Finds a minimum of a function by the BFGS quasi-Newton method This uses the function and it's gradient (partial derivatives in each direction) and approximates the Hessian_ surely this is implying that it accepts multi-variate functions? – Shillington Aug 10 '17 at 07:58
  • @Shillington The functions each take in a single `Vector` object, which may contain multiple values. Start with my compiling code above and try adding to it. The function inputs are inferred to be vectors, which you can access individual numbers from. – TheQuickBrownFox Aug 10 '17 at 09:02
  • @Fox thanks for your help. The underlying function doesn't seem to work very well, if you try _open MathNet.Numerics.Optimization open MathNet.Numerics.LinearAlgebra.Double BfgsSolver.Solve(DenseVector [|5.0|], (fun x -> (x.[0]-5.0) ** 2.0), (fun x -> 2.0 * x))_ (similar to your code but slightly re-worked) Visual Studio gives me an answer of _seq [3.414282807]_ when obviously the answer should be 5.0000000. I'll have a look at the other solutions in the library, see if something better exists. Thanks again – Shillington Aug 11 '17 at 09:07