locked
Implement Noisy-Or model using infer.net RRS feed

  • Question

  • Hi, I tried to implement a Noisy-Or model with the following code but failed. Can anyone help?

    	    Variable<int> nDiseases = Variable.New<int>();
                Variable<int> nSymptoms = Variable.New<int>();
                Range disease = new Range(nDiseases).Named("disease");
                Range symptom = new Range(nSymptoms).Named("symptom");
    
                VariableArray<double> diseasePriors = Variable.Array<double>(disease).Named("disease priors");
                VariableArray2D<double> gatePriors = Variable.Array<double>(disease, symptom).Named("gate priors");
                VariableArray<double> leakPriors = Variable.Array<double>(symptom).Named("leak priors");
    
                VariableArray<bool> diseases = Variable.Array<bool>(disease).Named("diseases");
                VariableArray<bool> symptoms = Variable.Array<bool>(symptom).Named("symptoms");
                VariableArray2D<bool> gates = Variable.Array<bool>(disease, symptom).Named("gates");
    
                diseases[disease] = Variable.Bernoulli(diseasePriors[disease]);
                gates[disease, symptom] =  Variable.Bernoulli(gatePriors[disease, symptom]);
                symptoms[symptom] = diseases[disease] & gates[disease, symptom] | Variable.Bernoulli(leakPriors[symptom]);
    
                bool[] evidences = new bool[] {true, true };
                symptoms.ObservedValue = evidences;
                nDiseases.ObservedValue = 2;
                nSymptoms.ObservedValue = evidences.Length;
                diseasePriors.ObservedValue = new double[] { 0.006, 0.0006 };
                gatePriors.ObservedValue = new double[,] { { 0.251, 0.153 }, { 0.0251, 0.153 } };
                leakPriors.ObservedValue = new double[] { 0.00005, 0.00005 };
    
                InferenceEngine ie = new InferenceEngine();
                Bernoulli[] results = ie.Infer<Bernoulli[]>(diseases);

    The problem, I think, may due to the line where I filled a 1D variable array with a 2D one.

    symptoms[symptom] = diseases[disease] & gates[disease, symptom] | Variable.Bernoulli(leakPriors[symptom]);

    The error message I got is as follows,

    System.InvalidOperationException: Array elements cannot have different definition types

    stack trace:
    at MicrosoftResearch.Infer.Models.Variable`1.SetTo(MethodInvoke methodInvoke)
       at MicrosoftResearch.Infer.Models.Variable`1.SetTo(Variable`1 variable)
       at MicrosoftResearch.Infer.Models.VariableArrayBase`2.SetItem(VariableArrayBase`2 array, TItem itemPrototype, TItem value, IModelExpression[] index)
       at MicrosoftResearch.Infer.Models.VariableArrayBase`2.set_Item(Range[] index, TItem value)
       at MicrosoftResearch.Infer.Models.VariableArray`2.set_Item(Range range, TItem value)



    • Edited by laotao Friday, September 5, 2014 2:33 AM
    Thursday, September 4, 2014 6:37 AM

Answers

  • You need to be precise about the generative process otherwise you cannot hope to successfully build a model. Here is one interpretation of what I think you are saying. But there may be others. For example the scope of the leak variable - are you generating that once for each symptom, or for each pair, or for each instance (once you bring instances into your model)? A note to understand the below: You can do an Or across a random variable array by (a) negating the elements of the array, (b) applying the AllTrue factor to the array, and (c) negating the result.

     
    Variable<int> nDiseases = Variable.New<int>();
    Variable<int> nSymptoms = Variable.New<int>();
    Range disease = new Range(nDiseases).Named("disease");
    Range symptom = new Range(nSymptoms).Named("symptom");

    var gatePriors = Variable.Array(Variable.Array<double>(disease), symptom).Named("gate priors");
    var gateOpen = Variable.Array(Variable.Array<bool>(disease), symptom).Named("gate open");

    gateOpen[symptom][disease] = Variable.Bernoulli(gatePriors[symptom][disease]);
    VariableArray<double> leakPriors = Variable.Array<double>(symptom).Named("leak priors");

    VariableArray<double> diseasePriors = Variable.Array<double>(disease).Named("disease priors");
    VariableArray<bool> diseasePresent = Variable.Array<bool>(disease).Named("disease present");
    diseasePresent[disease] = Variable.Bernoulli(diseasePriors[disease]);
    VariableArray<bool> symptoms = Variable.Array<bool>(symptom).Named("symptoms");
    var notEnabledDiseases = Variable.Array(Variable.Array<bool>(disease), symptom).Named("not enabled diseases");

    VariableArray<bool> isLeak = Variable.Array<bool>(symptom).Named("isLeak");
    isLeak[symptom] = Variable.Bernoulli(leakPriors[symptom]);

    using (Variable.ForEach(disease))
    {
        using (Variable.If(diseasePresent[disease]))
        {
            using (Variable.ForEach(symptom))
            {
                notEnabledDiseases[symptom][disease] = !(gateOpen[symptom][disease] | isLeak[symptom]);
            }
        }
        using (Variable.IfNot(diseasePresent[disease]))
        {
            using (Variable.ForEach(symptom))
            {
                notEnabledDiseases[symptom][disease] = !isLeak[symptom];
            }
        }
    }

    symptoms[symptom] = !Variable.AllTrue(notEnabledDiseases[symptom]);


    bool[] evidences = new bool[] { true, true };
    symptoms.ObservedValue = evidences;
    nDiseases.ObservedValue = 2;
    nSymptoms.ObservedValue = evidences.Length;
    diseasePriors.ObservedValue = new double[] { 0.006, 0.0006 };
    gatePriors.ObservedValue = new double[][]
    {
        new double[] { 0.251, 0.153 },
        new double[]{ 0.0251, 0.153 }
    };

    leakPriors.ObservedValue = new double[] { 0.00005, 0.00005 };

    InferenceEngine ie = new InferenceEngine();
    Bernoulli[] results = ie.Infer<Bernoulli[]>(diseasePresent);

    • Marked as answer by laotao Friday, September 5, 2014 2:22 AM
    • Unmarked as answer by laotao Friday, September 5, 2014 2:23 AM
    • Marked as answer by laotao Friday, September 5, 2014 2:31 AM
    Thursday, September 4, 2014 10:31 AM
    Owner

All replies

  • Can you say mathematically how you are trying to go from 2D to 1D?
    Thursday, September 4, 2014 8:18 AM
    Owner
  • Hi John.

    Thanks for your response. I cannot say it mathematically (sorry for my poor math). But the basic idea is as follows (I changed my code above to improve readability).

    The Noisy-Or model I'm trying to implement is a BN2 Bayesian model. Basically there are two layers of variables. The upper layer are the disease variables - diseases, which are indexed by the 'disease' range. The lower layer are the symptoms variables - symptoms, which are indexed by the 'symptom' range. The direction of influences/edges are from diseases to symptoms.

    In a Noisy-Or model, a disease can cause a symptom only if the corresponding gate is open. And a symptom is true if any of its parenting disease-gate output is true. It also can be true even if none of its parent disease-gate output is true, which is why there are leak priors.

    Since there is a gate for each disease-symptom pair, I have to use a 2D variable array for gates. But the symptoms array is 1D. Maybe I should join all parent disease-gate output variables of a symptom with an OR option, but I don't know how. Can you help me?





    • Edited by laotao Thursday, September 4, 2014 9:22 AM
    Thursday, September 4, 2014 9:08 AM
  • You need to be precise about the generative process otherwise you cannot hope to successfully build a model. Here is one interpretation of what I think you are saying. But there may be others. For example the scope of the leak variable - are you generating that once for each symptom, or for each pair, or for each instance (once you bring instances into your model)? A note to understand the below: You can do an Or across a random variable array by (a) negating the elements of the array, (b) applying the AllTrue factor to the array, and (c) negating the result.

     
    Variable<int> nDiseases = Variable.New<int>();
    Variable<int> nSymptoms = Variable.New<int>();
    Range disease = new Range(nDiseases).Named("disease");
    Range symptom = new Range(nSymptoms).Named("symptom");

    var gatePriors = Variable.Array(Variable.Array<double>(disease), symptom).Named("gate priors");
    var gateOpen = Variable.Array(Variable.Array<bool>(disease), symptom).Named("gate open");

    gateOpen[symptom][disease] = Variable.Bernoulli(gatePriors[symptom][disease]);
    VariableArray<double> leakPriors = Variable.Array<double>(symptom).Named("leak priors");

    VariableArray<double> diseasePriors = Variable.Array<double>(disease).Named("disease priors");
    VariableArray<bool> diseasePresent = Variable.Array<bool>(disease).Named("disease present");
    diseasePresent[disease] = Variable.Bernoulli(diseasePriors[disease]);
    VariableArray<bool> symptoms = Variable.Array<bool>(symptom).Named("symptoms");
    var notEnabledDiseases = Variable.Array(Variable.Array<bool>(disease), symptom).Named("not enabled diseases");

    VariableArray<bool> isLeak = Variable.Array<bool>(symptom).Named("isLeak");
    isLeak[symptom] = Variable.Bernoulli(leakPriors[symptom]);

    using (Variable.ForEach(disease))
    {
        using (Variable.If(diseasePresent[disease]))
        {
            using (Variable.ForEach(symptom))
            {
                notEnabledDiseases[symptom][disease] = !(gateOpen[symptom][disease] | isLeak[symptom]);
            }
        }
        using (Variable.IfNot(diseasePresent[disease]))
        {
            using (Variable.ForEach(symptom))
            {
                notEnabledDiseases[symptom][disease] = !isLeak[symptom];
            }
        }
    }

    symptoms[symptom] = !Variable.AllTrue(notEnabledDiseases[symptom]);


    bool[] evidences = new bool[] { true, true };
    symptoms.ObservedValue = evidences;
    nDiseases.ObservedValue = 2;
    nSymptoms.ObservedValue = evidences.Length;
    diseasePriors.ObservedValue = new double[] { 0.006, 0.0006 };
    gatePriors.ObservedValue = new double[][]
    {
        new double[] { 0.251, 0.153 },
        new double[]{ 0.0251, 0.153 }
    };

    leakPriors.ObservedValue = new double[] { 0.00005, 0.00005 };

    InferenceEngine ie = new InferenceEngine();
    Bernoulli[] results = ie.Infer<Bernoulli[]>(diseasePresent);

    • Marked as answer by laotao Friday, September 5, 2014 2:22 AM
    • Unmarked as answer by laotao Friday, September 5, 2014 2:23 AM
    • Marked as answer by laotao Friday, September 5, 2014 2:31 AM
    Thursday, September 4, 2014 10:31 AM
    Owner
  • Thanks John. Your code is almost what I want. The scope of the leak variable is for each symptom. So I changed the modeling part of the code as follows and it works as expected.

     using (Variable.ForEach(disease))
                {
                    using (Variable.If(diseasePresent[disease]))
                    {
                        using (Variable.ForEach(symptom))
                        {
                            notEnabledDiseases[symptom][disease] = !(gateOpen[symptom][disease]);
                        }
                    }
                    using (Variable.IfNot(diseasePresent[disease]))
                    {
                        using (Variable.ForEach(symptom))
                        {
                            notEnabledDiseases[symptom][disease] = true;
                        }
                    }
                }
    
                symptoms[symptom] = !Variable.AllTrue(notEnabledDiseases[symptom]) | isLeak[symptom];

    Thanks for your help!

    Friday, September 5, 2014 2:31 AM