Decision graph in C#

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

namespace BayesServer.HelpSamples
    using Inference;
    using Inference.RelevanceTree;
    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;

    public static class DecisionGraphExample
        public static void Main()
            // In this example we will first construct the well known
            // Oil Wildcatter Decision Graph (Influence diagram) manually.
            // We could instead use network.Load(...) if we have an existing network.
            // We will then use the Single Policy Updating algorithm to
            // optimize decisions under uncertainty.

            var network = new Network();

            // Add standard probability nodes...

            var oilDry = new State("Dry");
            var oilWet = new State("Wet");
            var oilSoaking = new State("Soaking");
            var oil = new Variable("Oil", oilDry, oilWet, oilSoaking);
            var nodeOil = new Node(oil);

            var testResultClosed = new State("Closed");
            var testResultOpen = new State("Open");
            var testResultDiffuse = new State("Diffuse");
            var testResult = new Variable("Test Result", testResultClosed, testResultOpen, testResultDiffuse);
            var nodeTestResult = new Node(testResult);

            // Add decision nodes...

            var testYes = new State("Yes");
            var testNo = new State("No");
            var test = new Variable("Test?", VariableValueType.Discrete, VariableKind.Decision);
            var nodeTest = new Node(test);

            var drillYes = new State("Yes");
            var drillNo = new State("No");
            var drill = new Variable("Drill?", VariableValueType.Discrete, VariableKind.Decision);
            var nodeDrill = new Node(drill);

            // Add utility nodes...

            // Note that utility variables in Bayes Server are continuous.  They can even have variances.

            var drillUtility = new Variable("Drill utility", VariableValueType.Continuous, VariableKind.Utility);
            var nodeDrillUtility = new Node(drillUtility);

            var testUtility = new Variable("Test utility", VariableValueType.Continuous, VariableKind.Utility);
            var nodeTestUtility = new Node(testUtility);

            // When a network has more than one utility node
            // we need to add a further (leaf) utility node which
            // both determines how the other utilities are to be combined
            // and also provides a means of querying the maximum expected utility.
            // We can even perform joint queries.

            var meu = new Variable("MEU", VariableValueType.Continuous, VariableKind.Utility);
            var nodeMeu = new Node(meu);

            // Add the links

            var links = network.Links;
            links.Add(new Link(nodeOil, nodeTestResult));
            links.Add(new Link(nodeOil, nodeDrillUtility));
            links.Add(new Link(nodeTestResult, nodeDrill));
            links.Add(new Link(nodeTest, nodeTestResult));
            links.Add(new Link(nodeTest, nodeDrill));
            links.Add(new Link(nodeTest, nodeTestUtility));
            links.Add(new Link(nodeDrill, nodeDrillUtility));
            links.Add(new Link(nodeDrillUtility, nodeMeu));
            links.Add(new Link(nodeTestUtility, nodeMeu));

            // Here we will manually specify the distributions
            // but we could also learn them from data

            var tableOil = nodeOil.NewDistribution().Table;
            tableOil[oilDry] = 0.5;
            tableOil[oilWet] = 0.3;
            tableOil[oilSoaking] = 0.2;
            nodeOil.Distribution = tableOil;

            var tableTestResult = nodeTestResult.NewDistribution().Table;

            // We could set each value as we did for the previous distribution
            // however because there are quite a few values we will use
            // a table iterator

            var third = 1.0 / 3.0;

            new TableIterator(
                new Node[] { nodeOil, nodeTest, nodeTestResult }
                new double[]{
                    0.1, 0.3, 0.6, third, third, third, 0.3, 0.4, 0.3, third, third, third, 0.5, 0.4, 0.1, third, third, third});

            nodeTestResult.Distribution = tableTestResult;

            var tableTest = nodeTest.NewDistribution().Table;
            tableTest.Normalize(true);  // set to uniform distribution
            nodeTest.Distribution = tableTest;

            var tableDrill = nodeDrill.NewDistribution().Table;
            tableDrill.Normalize(true); // set to uniform distribution
            nodeDrill.Distribution = tableDrill;

            // In the oil wildcatter example, all utilities have zero variance (point Gaussians)
            // however Bayes Server supports utility distributions with variances.
            // In fact, if you learn the distributions from data they will typically have
            // non-zero variances.

            var gaussianDrillUtility = (CLGaussian)nodeDrillUtility.NewDistribution();
            gaussianDrillUtility.SetMean(drillUtility, -70.0, oilDry, drillYes);
            gaussianDrillUtility.SetMean(drillUtility, 0.0, oilDry, drillNo);
            gaussianDrillUtility.SetMean(drillUtility, 50.0, oilWet, drillYes);
            gaussianDrillUtility.SetMean(drillUtility, 0.0, oilWet, drillNo);
            gaussianDrillUtility.SetMean(drillUtility, 200.0, oilSoaking, drillYes);
            gaussianDrillUtility.SetMean(drillUtility, 0.0, oilSoaking, drillNo);
            nodeDrillUtility.Distribution = gaussianDrillUtility;

            var gaussianTestUtility = (CLGaussian)nodeTestUtility.NewDistribution();
            gaussianTestUtility.SetMean(testUtility, -10.0, testYes);
            gaussianTestUtility.SetMean(testUtility, 0.0, testNo);
            nodeTestUtility.Distribution = gaussianTestUtility;

            // The MEU utility defines how the utilities are combined.
            // In this example we just add them, by giving each parent a weight of 1
            var gaussianMeu = (CLGaussian)nodeMeu.NewDistribution();
            gaussianMeu.SetWeight(meu, drillUtility, 1.0);
            gaussianMeu.SetWeight(meu, testUtility, 1.0);
            nodeMeu.Distribution = gaussianMeu;

            // Now the network structure and distributions are fully specified

            // Next, lets query the network.

            var factory = new RelevanceTreeInferenceFactory();
            var inference = factory.CreateInferenceEngine(network);
            var queryOptions = factory.CreateQueryOptions();
            var queryOutput = factory.CreateQueryOutput();

            // We want to optimize the decisions under uncertainty so will
            // use the Single Policy Updating algorithm (SPU)
            queryOptions.DecisionAlgorithm = DecisionAlgorithm.SinglePolicyUpdatingLight;

            var queryOil = new Table(oil);    // query a probability variable
            var queryDrill = new Table(drill);  // query a decision variable
            var queryMeu = new CLGaussian(meu); // get the Maximum Expected Utility (MEU)
            var queryJoint = new CLGaussian(new Variable[] { meu, oil });   // we can also query joint distributions.

            var queryDistributions = inference.QueryDistributions;

            // If we have any evidence to set use
            // inference.Evidence.Set or inference.Evidence.SetState
            // here

            inference.Query(queryOptions, queryOutput);

            var oilDryValue = queryOil[oilDry];
            Console.WriteLine("Oil = Dry\t{0}", oilDryValue);   // expected 0.5

            var meuValue = queryMeu.GetMean(meu);
            Console.WriteLine("MEU\t{0}", meuValue);   // expected value 22.5

            var drillYesValue = queryDrill[drillYes];
            Console.WriteLine("Drill? = Yes\t{0}", drillYesValue);   // expected 0.59

            var meuOilDry = queryJoint.GetMean(meu, oilDry);    
            Console.WriteLine("MEU Oil=Dry\t{0}", meuOilDry);   // expected -38.0