Decision graph in Python

# __author__ = 'Bayes Server'
# __version__= '0.1'

from jpype import *

classpath = "C:\\Program Files\\Bayes Server\\Bayes Server 7.25\\API\\Java\\bayesserver-7.25.jar"

startJVM(getDefaultJVMPath(), "-Djava.class.path=%s" % classpath)

bayes_server = JPackage("com.bayesserver")
bayes_server_inference = bayes_server.inference

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

# Uncomment the following 2 lines and change the license key, if you are using a licensed version
# License = JClass("com.bayesserver.License")
# License.validate("xxx")

network = bayes_server.Network()

# Add standard probability nodes...

oilDry = bayes_server.State("Dry")
oilWet = bayes_server.State("Wet")
oilSoaking = bayes_server.State("Soaking")
oil = bayes_server.Variable("Oil", [oilDry, oilWet, oilSoaking])
nodeOil = bayes_server.Node(oil)
network.getNodes().add(nodeOil)

testResultClosed = bayes_server.State("Closed")
testResultOpen = bayes_server.State("Open")
testResultDiffuse = bayes_server.State("Diffuse")
testResult = bayes_server.Variable("Test Result", [testResultClosed, testResultOpen, testResultDiffuse])
nodeTestResult = bayes_server.Node(testResult)
network.getNodes().add(nodeTestResult)

# Add decision nodes...

testYes = bayes_server.State("Yes")
testNo = bayes_server.State("No")
test = bayes_server.Variable("Test?", bayes_server.VariableValueType.DISCRETE, bayes_server.VariableKind.DECISION)
test.getStates().add(testYes)
test.getStates().add(testNo)
nodeTest = bayes_server.Node(test)
network.getNodes().add(nodeTest)

drillYes = bayes_server.State("Yes")
drillNo = bayes_server.State("No")
drill = bayes_server.Variable("Drill?", bayes_server.VariableValueType.DISCRETE, bayes_server.VariableKind.DECISION)
drill.getStates().add(drillYes)
drill.getStates().add(drillNo)
nodeDrill = bayes_server.Node(drill)
network.getNodes().add(nodeDrill)

# Add utility nodes...

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

drillUtility = bayes_server.Variable("Drill utility", bayes_server.VariableValueType.CONTINUOUS,
                                     bayes_server.VariableKind.UTILITY)
nodeDrillUtility = bayes_server.Node(drillUtility)
network.getNodes().add(nodeDrillUtility)

testUtility = bayes_server.Variable("Test utility", bayes_server.VariableValueType.CONTINUOUS,
                                    bayes_server.VariableKind.UTILITY)
nodeTestUtility = bayes_server.Node(testUtility)
network.getNodes().add(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.

meu = bayes_server.Variable("MEU", bayes_server.VariableValueType.CONTINUOUS, bayes_server.VariableKind.UTILITY)
nodeMeu = bayes_server.Node(meu)
network.getNodes().add(nodeMeu)


# Add the links

links = network.getLinks()
links.add(bayes_server.Link(nodeOil, nodeTestResult))
links.add(bayes_server.Link(nodeOil, nodeDrillUtility))
links.add(bayes_server.Link(nodeTestResult, nodeDrill))
links.add(bayes_server.Link(nodeTest, nodeTestResult))
links.add(bayes_server.Link(nodeTest, nodeDrill))
links.add(bayes_server.Link(nodeTest, nodeTestUtility))
links.add(bayes_server.Link(nodeDrill, nodeDrillUtility))
links.add(bayes_server.Link(nodeDrillUtility, nodeMeu))
links.add(bayes_server.Link(nodeTestUtility, nodeMeu))


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

tableOil = nodeOil.newDistribution().getTable()
tableOil.set(0.5, [oilDry])
tableOil.set(0.3, [oilWet])
tableOil.set(0.2, [oilSoaking])
nodeOil.setDistribution(tableOil)

tableTestResult = nodeTestResult.newDistribution().getTable()

# 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

third = 1.0 / 3.0

bayes_server.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.setDistribution(tableTestResult)

tableTest = nodeTest.newDistribution().getTable()
tableTest.normalize(True)  # set to uniform distribution
nodeTest.setDistribution(tableTest)

tableDrill = nodeDrill.newDistribution().getTable()
tableDrill.normalize(True)  # set to uniform distribution
nodeDrill.setDistribution(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.

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.setDistribution(gaussianDrillUtility)

gaussianTestUtility = nodeTestUtility.newDistribution()
gaussianTestUtility.setMean(testUtility, -10.0, [testYes])
gaussianTestUtility.setMean(testUtility, 0.0, [testNo])
nodeTestUtility.setDistribution(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
gaussianMeu = nodeMeu.newDistribution()
gaussianMeu.setWeight(meu, drillUtility, 1.0)
gaussianMeu.setWeight(meu, testUtility, 1.0)
nodeMeu.setDistribution(gaussianMeu)


# Now the network structure and distributions are fully specified

# Next, lets query the network.

factory = bayes_server_inference.RelevanceTreeInferenceFactory()
inference = factory.createInferenceEngine(network)
queryOptions = factory.createQueryOptions()
queryOutput = factory.createQueryOutput()

# We want to optimize the decisions under uncertainty so will
# use the Single Policy Updating algorithm (SPU)
queryOptions.setDecisionAlgorithm(bayes_server_inference.DecisionAlgorithm.SINGLE_POLICY_UPDATING)

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

queryDistributions = inference.getQueryDistributions()
queryDistributions.add(bayes_server_inference.QueryDistribution(queryOil))
queryDistributions.add(bayes_server_inference.QueryDistribution(queryDrill))
queryDistributions.add(bayes_server_inference.QueryDistribution(queryMeu))
queryDistributions.add(bayes_server_inference.QueryDistribution(queryJoint))

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

inference.query(queryOptions, queryOutput)

oilDryValue = queryOil.get([oilDry])
print("Oil = Dry\t{}".format(oilDryValue))  # expected 0.5

meuValue = queryMeu.getMean(meu)
print("MEU\t{}".format(meuValue))  # expected value 22.5

drillYesValue = queryDrill.get([drillYes])
print("Drill? = Yes\t{}".format(drillYesValue))  # expected 0.59

meuOilDry = queryJoint.getMean(meu, [oilDry])
print("MEU Oil=Dry\t{}".format(meuOilDry))  # expected -38.0

shutdownJVM()