Tutorial 4 - API

This tutorial shows the steps required to compile and run one of the help samples with the Bayes Server .NET library using C#.

NOTE

Bayes Server has a .NET library and a Java library for Bayesian networks and Dynamic Bayesian networks, as well as integration with Python, R, Matlab, Apache Spark and Excel functions. Please visit our code center for code examples.

Pre-requisites

Companion video (No Audio)

Create a new project

Add Bayes Server .NET references

NOTE

The .NET API can either be found within the User Interface installation folder or can be downloaded separately.

NOTE

If you installed the Bayes Server User Interface to the default location this is 'C:\Program Files\Bayes Server\Bayes Server x.x\API\DotNet\Standard20' or 'C:\Program Files (x86)\Bayes Server\Bayes Server x.x\API\DotNet\Standard20'.

NOTE

You may also want to add references to other commonly used dlls such as BayesServer.Data.dll for accessing data from a database, or BayesServer.Learning.Parameters.dll for parameter learning, etc...

Sample code

Replace the code in Program.cs (or the equivalent main file in your project) with the following:

// ------------------------------------------------------------
// <copyright file="NetworkExample.cs" company="Bayes Server">
//   Copyright (C) Bayes Server.  All rights reserved.
// </copyright>
// ------------------------------------------------------------

namespace BayesServer.HelpSamples
{
    using System;

    using BayesServer.Inference.RelevanceTree;

    public static class NetworkExample
    {
        public static void Main()
        {
            // In this example we programatically create a simple Bayesian network.
            // Note that you can automatically define nodes from data using
            // classes in BayesServer.Data.Discovery,
            // and you can automatically learn the parameters using classes in
            // BayesServer.Learning.Parameters,
            // however here we build a Bayesian network from scratch.

            var network = new Network("Demo");

            // add the nodes (variables)

            var aTrue = new State("True");
            var aFalse = new State("False");
            var a = new Node("A", aTrue, aFalse);

            var bTrue = new State("True");
            var bFalse = new State("False");
            var b = new Node("B", bTrue, bFalse);

            var cTrue = new State("True");
            var cFalse = new State("False");
            var c = new Node("C", cTrue, cFalse);

            var dTrue = new State("True");
            var dFalse = new State("False");
            var d = new Node("D", dTrue, dFalse);

            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.

            // We must define the necessary probability distributions for each node.

            // 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.

            var 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.

            tableA[aTrue] = 0.1;
            tableA[aFalse] = 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, therefore its distribution will be P(B|A)

            var tableB = b.NewDistribution().Table;
            tableB[aTrue, bTrue] = 0.2;
            tableB[aTrue, bFalse] = 0.8;
            tableB[aFalse, bTrue] = 0.15;
            tableB[aFalse, bFalse] = 0.85;
            b.Distribution = tableB;


            // specify P(C|A)
            var tableC = c.NewDistribution().Table;
            tableC[aTrue, cTrue] = 0.3;
            tableC[aTrue, cFalse] = 0.7;
            tableC[aFalse, cTrue] = 0.4;
            tableC[aFalse, cFalse] = 0.6;
            c.Distribution = tableC;


            // specify P(D|B,C)
            var tableD = d.NewDistribution().Table;

            // we could specify the values individually as above, or we can use a TableIterator as follows
            var 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
            var factory = new RelevanceTreeInferenceFactory();
            var inference = factory.CreateInferenceEngine(network);
            var queryOptions = factory.CreateQueryOptions();
            var 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(dTrue);  // set D = True

            var queryA = new Table(a);
            inference.QueryDistributions.Add(queryA);
            inference.Query(queryOptions, queryOutput); // note that this can raise an exception (see help for details)

            Console.WriteLine("P(A|D=True) = {" + queryA[aTrue] + "," + queryA[aFalse] + "}.");

            // Expected output ...
            // P(A|D=True) = {0.0980748663101604,0.90192513368984}

            // to perform another query we reuse all the objects

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

            // 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

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

            // Expected output ...
            // P(A|D=True, C=True) = [0.0777777777777778,0.922222222222222], log-likelihood = -2.04330249506396.


            // Note that we can also calculate joint queries such as P(A,B|D=True,C=True)

        }
    }
}
NOTE

If you are using a licensed copy of Bayes Server you will also need to call BayesServer.License.Validate("<license-key-goes-here>") at the start, passing in your license key.

Bayes Server API tutorial

Run the project