Skip to main content

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