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

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

  • Bayes Server must be installed (or the API downloaded), before starting this tutorial. An evaluation version can be downloaded from the Downloads page

  • Development environment

    In this tutorial we make use of a software development environment. Microsoft provides free software that can be used if required, such as Visual Studio Community Edition or Visual Studio Code. Many other languages also support the .NET framework and are therefore capable of using Bayes Server. For the purposes of this tutorial we will use Visual Studio.

Companion video (No Audio)

Create a new project

  • Open the development environment (Microsoft Visual Studio)
  • Click File->New Project...
  • Select Console Application
  • Call your new project BayesServerDemo
  • Click OK

Add Bayes Server .NET references

  • Open Solution explorer and expand your new project.
  • Right click on the references folder and click 'Add Reference...';
  • Click the Browse Tab and navigate to the folder where the Bayes Server .NET API is located.

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

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

  • Select BayesServer.dll and BayesServer.Inference.dll, and click OK.

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)

        }
    }
}

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

  • You are now ready to run the project.

    From the Debug menu either click Start Debugging (F5) or Start Without Debugging (Ctrl+F5).

  • Your output should look similar to this.

    Bayes Server API sample output