Преобразование функции F# с несколькими параметрами в тип функции — MathNet.Numerics

В этом вопросе: Как в F# создать выражение с типом Func‹obj›? показано, что однозначное лямбда-выражение автоматически приводится/преобразуется в тип Func, а затем принимается в функции.

Я работаю с библиотекой MathNet.Numerics и могу подтвердить это, интегрировав x^2 между 0 и 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

Однако я не могу заставить это работать с несколькими функциями. Когда я пытаюсь это сделать, я получаю ошибку типа. Кто-нибудь знает обходной путь для этого?

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)   |])
                                                           )

и получаю следующую ошибку...

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>'   

person Shillington    schedule 08.08.2017    source источник
comment
Попробуйте fun (x, y) -> вместо fun x y -> Не за компьютером, позже преобразуется в ответ.   -  person CaringDev    schedule 08.08.2017
comment
@CaringDev Я был немного встревожен, обнаружив, что это не работает, по крайней мере, в моем тесте с использованием IEnumerable.Aggregate   -  person TheQuickBrownFox    schedule 08.08.2017
comment
@TheQuickBrownFox вполне возможно ... не особо много взаимодействует с F # -> C #, не могу вспомнить точные обстоятельства, с которыми я столкнулся в последний раз.   -  person CaringDev    schedule 08.08.2017


Ответы (1)


Вы можете использовать System.Func<_,_,_>() для преобразования функций следующим образом:

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) |])))

Если вам часто это нужно, вы можете сделать код немного менее уродливым с помощью помощника:

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

ОБНОВЛЕНИЕ

Глядя на документацию Math.NET для этого метода Теперь я вижу, что на самом деле он принимает функции только с одним входом. Возможно, вас смутила сигнатура типа Func<A, B>, но в данном случае A — это тип ввода, а B — тип вывода.

Функции F# с одним входом автоматически преобразуются в Func<_,_>. Я скачал Math.NET, и этот очень минимальный пример не дает ошибок компиляции:

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

Это говорит о том, что проблема не в преобразовании между типами функций, а в использовании функций с неправильной арностью. Я должен был увидеть это из вашего исходного сообщения об ошибке!

person TheQuickBrownFox    schedule 08.08.2017
comment
Спасибо за ваш ответ QBF. У меня все еще есть проблемы с типом, поэтому я привел все, что возможно: let f2 f = System.Func‹_,_› f let newAnswer = BfgsSolver.Solve(a, b, (f2 (fun (x : MathNet. Numerics.LinearAlgebra.Vector‹float›) -> [|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‹float›) ), но он все еще выдает мои ошибки. Вы проверили, работает ли ваше решение? Когда я вставляю в Visual Studio, я получаю ошибки - person Shillington; 08.08.2017
comment
Хорошо, но мой первоначальный вопрос заключался в том, как вы используете несколько параметров, в своем ответе вы по существу используете один параметр _, который, как мы уже проверили, работал в моем исходном примере с функцией интеграции. В моем ответе выше я попытался использовать переменную, приведенную к вектору (поскольку моя реальная проблема включает более одного параметра), но это давало другие ошибки... У вас есть какие-либо альтернативные предложения? - person Shillington; 09.08.2017
comment
@Shillington Вы не показали пример, в котором вам действительно нужно предоставить Func с двумя входами, поэтому я не могу сказать, в чем может быть ваша проблема. Я думаю, что мой оригинальный ответ должен справиться с этим. Обратите внимание, что f2 в вашем комментарии не совпадает с f2 в моем ответе. - person TheQuickBrownFox; 09.08.2017
comment
@Fox в исходном сообщении была эта функция: fun x y -> (x + y - 5.0) ** 2.0 + (y - x * x - 4.0) ** 2.0), разве Func, представляющий это, не нуждается в двух аргументах? - person Shillington; 09.08.2017
comment
@Shillington Да, но этот метод не принимает функцию с двумя аргументами, поэтому у вас возникает эта ошибка компиляции. - person TheQuickBrownFox; 09.08.2017
comment
@Фокс, ты уверен? numerics.mathdotnet.com/api/MathNet.Numerics.Optimization/ в документации говорится, что он находит минимум функции с помощью квази-ньютоновского метода BFGS. Он использует функцию и ее градиент (частные производные в каждом направлении) и аппроксимирует гессиан, безусловно, это подразумевает, что он принимает многовариантные функции? - person Shillington; 10.08.2017
comment
@Shillington Каждая функция принимает один объект Vector<double>, который может содержать несколько значений. Начните с моего кода компиляции выше и попробуйте добавить к нему. Предполагается, что входы функции являются векторами, из которых вы можете получить доступ к отдельным числам. - person TheQuickBrownFox; 10.08.2017
comment
@Fox спасибо за вашу помощь. Базовая функция работает не очень хорошо, если вы попытаетесь открыть 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)) (похож на ваш код, но немного переработан) Visual Studio дает мне ответ seq [3.414282807 ] когда очевидно, что ответ должен быть 5.0000000. Я посмотрю другие решения в библиотеке, посмотрю, есть ли что-то лучше. Спасибо еще раз - person Shillington; 11.08.2017