Inference algorithm for Bayesian networks, loosely based on the Variable Elimination algorithm.

Namespace:  BayesServer.Inference.VariableElimination
Assembly:  BayesServer.Inference (in BayesServer.Inference.dll)
Version: 2.2.0.0 (2.2.0.0)

Syntax

C#
public sealed class VariableEliminationInference : IInference, 
	IDisposable
Visual Basic (Declaration)
Public NotInheritable Class VariableEliminationInference _
	Implements IInference, IDisposable
Visual C++
public ref class VariableEliminationInference sealed : IInference, 
	IDisposable

Examples

CopyC#
using System;
using BayesServer;
using System.IO;
using BayesServer.Inference;
using BayesServer.Inference.VariableElimination;

namespace BayesServer.HelpSamples
{
    public static class NetworkExample
    {
        public static void Main()
        {
            // create a simple Bayesian network

            Network network = new Network("Demo");

            string[] defaultStates = new string[]{"True", "False"}; 

            // add the nodes (variables)

            Node a = new Node("A", defaultStates);
            Node b = new Node("B", defaultStates);
            Node c = new Node("C", defaultStates);
            Node d = new Node("D", defaultStates);

            network.Nodes.Add(a);
            network.Nodes.Add(b);
            network.Nodes.Add(c);
            network.Nodes.Add(d);

            // add some directed links

            network.Links.Add(new Link(a, b));
            network.Links.Add(new Link(a, c));
            network.Links.Add(new Link(b, d));
            network.Links.Add(new Link(c, d));

            // at this point we have fully specified the structural (graphical) specification of the Bayesian Network.

            // next we must define the necessary probability distributions.

            // Each node in a Bayesian Network requires a probability distribution conditioned on it's parents.

            // NewDistribution() can be called on a Node to create the appropriate probability distribution for a node
            // or it can be created manually.

            // The interface IDistribution has been designed to represent both discrete and continuous variables,

            // As we are currently dealing with discrete distributions, we will use the
            // Table class.

            // To access the discrete part of a distribution, we use IDistribution.Table.

            // The Table class is used to define distributions over a number of discrete variables.

            Table tableA = a.NewDistribution().Table;     // access the table property of the Distribution

            // IMPORTANT
            // Note that calling Node.NewDistribution() does NOT assign the distribution to the node.
            // A distribution cannot be assigned to a node until it is correctly specified.
            // If a distribution becomes invalid  (e.g. a parent node is added), it is automatically set to null.

            // as node A has no parents there is no ambiguity about the order of variables in the distribution
            tableA[0] = 0.1;
            tableA[1] = 0.9;

            // now tableA is correctly specified we can assign it to Node A;
            a.Distribution = tableA;

            // node B has node A as a parent
            // here we use a TableIterator to access the values in the table ( sequentially ), with a particular ordering of the nodes,

            // the right most node always toggles fastest
            // e.g.

            // A, B
            // True, True
            // True, False
            // False, True
            // False, False

            Table tableB = b.NewDistribution().Table;   

            TableIterator iteratorB = new TableIterator(tableB, new Node[] { a, b });

            double[] values = new double[] { 0.2, 0.8, 0.15, 0.85 };  // see later use of TableIterator for an example of using CopyTo().

            for (int i = 0; i < tableB.Count; i++, iteratorB++)
            {
                iteratorB.Value = values[i];
            }

            b.Distribution = tableB;

            Table tableC = c.NewDistribution().Table;

            // we could also update the values using a TableAccessor ( which allows random access ).
            TableAccessor accessor = new TableAccessor(tableC, new Node[] { a, c });
            accessor[0] = 0.3;
            accessor[1] = 0.7;
            accessor[2] = 0.4;
            accessor[3] = 0.6;

            c.Distribution = tableC;

            Table tableD = d.NewDistribution().Table;

            TableIterator iteratorD = new TableIterator(tableD, new Node[] { b, c, d });
            iteratorD.CopyFrom(new double[] { 0.4, 0.6, 0.55, 0.45, 0.32, 0.68, 0.01, 0.99 });

            d.Distribution = tableD;

            // The network is now fully specified

            // If required the network can be saved...

            if (false)   // change this to true to save the network
            {
                network.Save("fileName.bayes");  // replace 'fileName.bayes' with your own path
            }

            // Now we will calculate P(A|D=True), i.e. the probability of A given the evidence that D is true

            // use the factory design pattern to create the necessary inference related objects
            IInferenceFactory factory = new VariableEliminationInferenceFactory();
            IInference inference = factory.CreateInferenceEngine(network);
            IQueryOptions queryOptions = factory.CreateQueryOptions();
            IQueryOutput queryOutput = factory.CreateQueryOutput();

            // we could have created these objects explicitly instead, but as the number of algorithms grows
            // this makes it easier to switch between them

            inference.Evidence.SetState(d, 0);  // set P(A|D=True)

            Table queryA = new Table(a);

            inference.QueryDistributions.Add(queryA);

            if (inference.Query(queryOptions, queryOutput))
            {
                Console.WriteLine("P(A|D=True) = {" + queryA[0] + "," + queryA[1] + "}.");

                // Expected output ...
                // P(A|D=True) = {0.0980748663101604,0.90192513368984}
            }
            else
            {
                Console.WriteLine("Inconsistent evidence was detected.");
            }

            // to perform another query we reuse all the objects

            // now lets calculate P(A|D=True, C=True)
            inference.Evidence.SetState(c, 0);

            // we will also return the log-likelihood of the case
            queryOptions.LogLikelihood = true; // only request the log-likelihood if you really need it, as extra computation is involved

            if (inference.Query(queryOptions, queryOutput))
            {
                Console.WriteLine(string.Format("P(A|D=True, C=True) = {{{0},{1}}}, log-likelihood = {2}.", queryA[0], queryA[1], queryOutput.LogLikelihood.Value));

                // Expected output ...
                // P(A|D=True, C=True) = {0.0777777777777778,0.922222222222222}, log-likelihood = -2.04330249506396.
            }
            else
            {
                Console.WriteLine("Inconsistent evidence was detected.");
            }

        }
    }
}

Inheritance Hierarchy

System..::.Object
  BayesServer.Inference.VariableElimination..::.VariableEliminationInference

See Also