Registering a Factor in F# (Migrated from community.research.microsoft.com)
-
Friday, June 03, 2011 4:48 PMOwner
laura posted on 03-20-2009 5:33 AM
I tried to translate John G's Discrete GreaterThan factor to F# ( see his post http://community.research.microsoft.com/forums/p/2964/4839.aspx#4839 )
Factor definition is straight forward
type MyFactors = class
[<ParameterNames([|"B"; "X"; "Y"|])>]
static member GreaterThan x y =
x > (y:int)
endAttaching the assembly attribute was a bit hacky, but it definitely had an effect.
let mutable i = 5
[<assembly: HasMessageFunctions()>]
do i <- 3I translated the factor operator code to F#
/// <summary>
/// Implements the constraint B = (X > Y).
/// </summary>
/// <remarks>X and Y need not have the same dimension.</remarks>
[<FactorMethod(typeof<MyFactors>, "GreaterThan" ,[|typeof<int>; typeof<int>|] )>]
type GreaterThanOp = class
/// <summary>
/// Compute the probability that X > Y.
/// </summary>
static member public BAverageConditional (x:Discrete) (y:Discrete) ( result_:Bernoulli):Bernoulli =
let mutable result = result_
if (x.IsPointMass && y.IsPointMass) then
result.Point <- (x.Point > y.Point);
result;
.....Note that attributed in F# are denoted by [< >] instead of []
in the main program I register the operator
Variable.RegisterOperatorFactor(Operator.GreaterThan, new FactorMethod<bool, int, int>(MyFactors.GreaterThan));
... and used the factor
let win =(Variable.op_GreaterThan(Variable.DiscretePointMass(1,4), Variable.DiscretePointMass(2,4)))
let ie = InferenceEngine (Algorithm = new ExpectationPropagation(), NumberOfIterations = 1)
let winMarginal = ie.Infer<Bernoulli>(win);
It seems as if factor registration failed as the registration line writes the following to the console
System.MissingMethodException: MyFactors.GreaterThan(int,int)
at MicrosoftResearch.Infer.Transforms.MethodReference.GetMethodInfo()
at MicrosoftResearch.Infer.Transforms.FactorManager.#Un()The inference call fails with:
"Value cannot be null.
Parameter name: key"I guess that the signature
[<FactorMethod(typeof<MyFactors>, "GreaterThan" ,[|typeof<int>; typeof<int>|] )>]
does not correspond to the F# signature of MyFactors.GreaterThan which is
MyFactors.GreaterThan : x:int -> (int -> bool)
I tried to omit the parameters as described in the user manual (http://research.microsoft.com/en-us/um/cambridge/projects/infernet/docs/How%20to%20add%20a%20new%20factor%20and%20message%20operators.aspx
) but the compiler complained that FactorMethod does not take 2 arguments.Any hints?
Laura
All Replies
-
Friday, June 03, 2011 4:49 PMOwner
minka replied on 03-30-2009 7:53 AM
Yes, the problem is in the signature of the factor function. If you define a function in F# like this:
static member GreaterThan x y =
x > (y:int)Then you get a function whose equivalent C# definition is:
public static FastFunc<int,bool> GreaterThan(int x);
In the other hand, if you define the function in F# like this:
static member GreaterThan(x, y) =
x > (y:int)Then you get a function whose equivalent C# defintion is:
public static bool GreaterThan(int x, int y);
which is what you want.
-
Friday, June 03, 2011 4:49 PMOwner
John Guiver replied on 03-30-2009 10:09 AM
Hi Laura
There are actually several issues here.
(1) The factor function signature as mentioned by Tom
(2) The F# delegate creates a FastFunc wrapper of the original factor method. We need to create a delegate which directly references the factor method. Here is a utility function that will do this. Its argument is a quotation of the factor method (more on calling this later):
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
let createDelegate x =
let info =
match x with
| Call (target,info,args) -> info
| _ -> failwith "Not a method"
let returnType = info.ReturnType
let argInfo = info.GetParameters()
let argTypes = argInfo |> Array.map (fun pi ->pi.ParameterType)
let a = Array.append [|returnType|] argTypes
let methodString = @"MicrosoftResearch.Infer.Factors.FactorMethod`" ^ (string a.Length)
let ass = (typeof<FactorMethod<_>>).Assembly
let methodTyp = ass.GetType(methodString).MakeGenericType(a)
let d = System.Delegate.CreateDelegate(methodTyp,info)
d
(3) You create the delegate, and register the operator as follows.
let gtDelegate = createDelegate <@ MyFactors.GreaterThan(0,0) @>
Variable.RegisterOperatorFactor(Operator.GreaterThan, gtDelegate);(4) The class containing message passing methods must be in a namespace and not in a module. If it is in a module, the generated code sees this as a nested class, and this is not yet supported in the Infer.NET language writer.
(5) The message passing methods must use the correct names for arguments - you must use 'result' rather than '_result'
(6) Finally, note that the assembly attribute can be applied in the following less hacky way:
[<assembly: HasMessageFunctions()>]
()John
- Marked As Answer by Microsoft ResearchOwner Friday, June 03, 2011 4:49 PM