Decision graph in JavaScript

The following example is provided in both TypeScript and JavaScript.

TypeScript

import { Network, Node, Variable, State, VariableValueType, VariableKind, Link, TableIterator, CLGaussian, RelevanceTreeInferenceFactory, DecisionAlgorithm, Table } from "bayes-server";


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

let network = new Network();

// Add standard probability nodes...

let oilDry = new State("Dry");
let oilWet = new State("Wet");
let oilSoaking = new State("Soaking");
let oil = new Variable("Oil", [oilDry, oilWet, oilSoaking]);
let nodeOil = new Node(oil);
network.nodes.push(nodeOil);

let testResultClosed = new State("Closed");
let testResultOpen = new State("Open");
let testResultDiffuse = new State("Diffuse");
let testResult = new Variable("Test Result", [testResultClosed, testResultOpen, testResultDiffuse]);
let nodeTestResult = new Node(testResult);
network.nodes.push(nodeTestResult);

// Add decision nodes...

let testYes = new State("Yes");
let testNo = new State("No");
let test = new Variable("Test?", VariableValueType.Discrete, VariableKind.Decision);
test.states.push(testYes);
test.states.push(testNo);
let nodeTest = new Node(test);
network.nodes.push(nodeTest);

let drillYes = new State("Yes");
let drillNo = new State("No");
let drill = new Variable("Drill?", VariableValueType.Discrete, VariableKind.Decision);
drill.states.push(drillYes);
drill.states.push(drillNo);
let nodeDrill = new Node(drill);
network.nodes.push(nodeDrill);

// Add utility nodes...

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

let drillUtility = new Variable("Drill utility", VariableValueType.Continuous, VariableKind.Utility);
let nodeDrillUtility = new Node(drillUtility);
network.nodes.push(nodeDrillUtility);

let testUtility = new Variable("Test utility", VariableValueType.Continuous, VariableKind.Utility);
let nodeTestUtility = new Node(testUtility);
network.nodes.push(nodeTestUtility);

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

let meu = new Variable("MEU", VariableValueType.Continuous, VariableKind.Utility);
let nodeMeu = new Node(meu);
network.nodes.push(nodeMeu);


// Add the links

let links = network.links;
links.push(new Link(nodeOil, nodeTestResult));
links.push(new Link(nodeOil, nodeDrillUtility));
links.push(new Link(nodeTestResult, nodeDrill));
links.push(new Link(nodeTest, nodeTestResult));
links.push(new Link(nodeTest, nodeDrill));
links.push(new Link(nodeTest, nodeTestUtility));
links.push(new Link(nodeDrill, nodeDrillUtility));
links.push(new Link(nodeDrillUtility, nodeMeu));
links.push(new Link(nodeTestUtility, nodeMeu));


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

let tableOil = nodeOil.newDistribution().table;
tableOil.set(0.5, [oilDry]);
tableOil.set(0.3, [oilWet]);
tableOil.set(0.2, [oilSoaking]);
nodeOil.distribution = tableOil;

let 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

let third = 1.0 / 3.0;

new TableIterator(
    tableTestResult,
    [nodeOil, nodeTest, nodeTestResult]
).copyFrom(
    [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;

let tableTest = nodeTest.newDistribution().table;
tableTest.normalize(true);  // set to uniform distribution
nodeTest.distribution = tableTest;

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

let 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;

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

let factory = new RelevanceTreeInferenceFactory();
let inference = factory.createInferenceEngine(network);
let queryOptions = factory.createQueryOptions();
let queryOutput = factory.createQueryOutput();

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

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

let queryDistributions = inference.queryDistributions;
queryDistributions.pushDistribution(queryOil);
queryDistributions.pushDistribution(queryDrill);
queryDistributions.pushDistribution(queryMeu);
queryDistributions.pushDistribution(queryJoint);

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

inference.query(queryOptions, queryOutput);

let oilDryValue = queryOil.get([oilDry]);
console.log("Oil = Dry\t{0}", oilDryValue);   // expected 0.5

let meuValue = queryMeu.getMean(meu);
console.log("MEU\t{0}", meuValue);   // expected value 22.5

let drillYesValue = queryDrill.get([drillYes]);
console.log("Drill? = Yes\t{0}", drillYesValue);   // expected 0.59

let meuOilDry = queryJoint.getMean(meu, [oilDry]);
console.log("MEU Oil=Dry\t{0}", meuOilDry);   // expected -38.0

JavaScript (es2015)

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const bayes_server_1 = require("bayes-server");
// 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.
let network = new bayes_server_1.Network();
// Add standard probability nodes...
let oilDry = new bayes_server_1.State("Dry");
let oilWet = new bayes_server_1.State("Wet");
let oilSoaking = new bayes_server_1.State("Soaking");
let oil = new bayes_server_1.Variable("Oil", [oilDry, oilWet, oilSoaking]);
let nodeOil = new bayes_server_1.Node(oil);
network.nodes.push(nodeOil);
let testResultClosed = new bayes_server_1.State("Closed");
let testResultOpen = new bayes_server_1.State("Open");
let testResultDiffuse = new bayes_server_1.State("Diffuse");
let testResult = new bayes_server_1.Variable("Test Result", [testResultClosed, testResultOpen, testResultDiffuse]);
let nodeTestResult = new bayes_server_1.Node(testResult);
network.nodes.push(nodeTestResult);
// Add decision nodes...
let testYes = new bayes_server_1.State("Yes");
let testNo = new bayes_server_1.State("No");
let test = new bayes_server_1.Variable("Test?", bayes_server_1.VariableValueType.Discrete, bayes_server_1.VariableKind.Decision);
test.states.push(testYes);
test.states.push(testNo);
let nodeTest = new bayes_server_1.Node(test);
network.nodes.push(nodeTest);
let drillYes = new bayes_server_1.State("Yes");
let drillNo = new bayes_server_1.State("No");
let drill = new bayes_server_1.Variable("Drill?", bayes_server_1.VariableValueType.Discrete, bayes_server_1.VariableKind.Decision);
drill.states.push(drillYes);
drill.states.push(drillNo);
let nodeDrill = new bayes_server_1.Node(drill);
network.nodes.push(nodeDrill);
// Add utility nodes...
// Note that utility variables in Bayes Server are continuous.  They can even have variances.
let drillUtility = new bayes_server_1.Variable("Drill utility", bayes_server_1.VariableValueType.Continuous, bayes_server_1.VariableKind.Utility);
let nodeDrillUtility = new bayes_server_1.Node(drillUtility);
network.nodes.push(nodeDrillUtility);
let testUtility = new bayes_server_1.Variable("Test utility", bayes_server_1.VariableValueType.Continuous, bayes_server_1.VariableKind.Utility);
let nodeTestUtility = new bayes_server_1.Node(testUtility);
network.nodes.push(nodeTestUtility);
// 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.
let meu = new bayes_server_1.Variable("MEU", bayes_server_1.VariableValueType.Continuous, bayes_server_1.VariableKind.Utility);
let nodeMeu = new bayes_server_1.Node(meu);
network.nodes.push(nodeMeu);
// Add the links
let links = network.links;
links.push(new bayes_server_1.Link(nodeOil, nodeTestResult));
links.push(new bayes_server_1.Link(nodeOil, nodeDrillUtility));
links.push(new bayes_server_1.Link(nodeTestResult, nodeDrill));
links.push(new bayes_server_1.Link(nodeTest, nodeTestResult));
links.push(new bayes_server_1.Link(nodeTest, nodeDrill));
links.push(new bayes_server_1.Link(nodeTest, nodeTestUtility));
links.push(new bayes_server_1.Link(nodeDrill, nodeDrillUtility));
links.push(new bayes_server_1.Link(nodeDrillUtility, nodeMeu));
links.push(new bayes_server_1.Link(nodeTestUtility, nodeMeu));
// Here we will manually specify the distributions
// but we could also learn them from data
let tableOil = nodeOil.newDistribution().table;
tableOil.set(0.5, [oilDry]);
tableOil.set(0.3, [oilWet]);
tableOil.set(0.2, [oilSoaking]);
nodeOil.distribution = tableOil;
let 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
let third = 1.0 / 3.0;
new bayes_server_1.TableIterator(tableTestResult, [nodeOil, nodeTest, nodeTestResult]).copyFrom([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;
let tableTest = nodeTest.newDistribution().table;
tableTest.normalize(true); // set to uniform distribution
nodeTest.distribution = tableTest;
let 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.
let gaussianDrillUtility = 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;
let gaussianTestUtility = 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
let gaussianMeu = 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.
let factory = new bayes_server_1.RelevanceTreeInferenceFactory();
let inference = factory.createInferenceEngine(network);
let queryOptions = factory.createQueryOptions();
let queryOutput = factory.createQueryOutput();
// We want to optimize the decisions under uncertainty so will
// use the Single Policy Updating algorithm (SPU)
queryOptions.decisionAlgorithm = bayes_server_1.DecisionAlgorithm.SinglePolicyUpdating;
let queryOil = new bayes_server_1.Table(oil); // query a probability variable
let queryDrill = new bayes_server_1.Table(drill); // query a decision variable
let queryMeu = new bayes_server_1.CLGaussian(meu); // get the Maximum Expected Utility (MEU)
let queryJoint = new bayes_server_1.CLGaussian([meu, oil]); // we can also query joint distributions.
let queryDistributions = inference.queryDistributions;
queryDistributions.pushDistribution(queryOil);
queryDistributions.pushDistribution(queryDrill);
queryDistributions.pushDistribution(queryMeu);
queryDistributions.pushDistribution(queryJoint);
// If we have any evidence to set use
// inference.Evidence.Set or inference.Evidence.SetState
// here
inference.query(queryOptions, queryOutput);
let oilDryValue = queryOil.get([oilDry]);
console.log("Oil = Dry\t{0}", oilDryValue); // expected 0.5
let meuValue = queryMeu.getMean(meu);
console.log("MEU\t{0}", meuValue); // expected value 22.5
let drillYesValue = queryDrill.get([drillYes]);
console.log("Drill? = Yes\t{0}", drillYesValue); // expected 0.59
let meuOilDry = queryJoint.getMean(meu, [oilDry]);
console.log("MEU Oil=Dry\t{0}", meuOilDry); // expected -38.0