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

  • 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

Answers

  • 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