# Registering a Factor in F# (Migrated from community.research.microsoft.com)

• ### Question

• 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)
end

Attaching the assembly attribute was a bit hacky, but it definitely had an effect.

let mutable i = 5
[<assembly: HasMessageFunctions()>]
do i <- 3

I 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

Friday, June 3, 2011 4:48 PM

• 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

Friday, June 3, 2011 4:49 PM

### All replies

• 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 3, 2011 4:49 PM
• 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

Friday, June 3, 2011 4:49 PM