// --------------------------------------------------------------------------------------------------------------------// <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 and uncomment start of line            }            // 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)        }    }}