Probabilistic Inference¶
The probabilistic inference algorithm used by py-bbn is an exact inference algorithm. Let’s go through an example on how to conduct exact inference.
Huang Graph¶
Below is the code to create the Huang Graph [HD99]. Note the typical procedure as follows.
create a Bayesian Belief Network (BBN)
create a junction tree from the graph
assert evidence
print out the marginal probabilities

Huang Bayesian Belief Network structure.¶
1from pybbn.graph.dag import Bbn
2from pybbn.graph.edge import Edge, EdgeType
3from pybbn.graph.jointree import EvidenceBuilder
4from pybbn.graph.node import BbnNode
5from pybbn.graph.variable import Variable
6from pybbn.pptc.inferencecontroller import InferenceController
7
8# create the nodes
9a = BbnNode(Variable(0, 'a', ['on', 'off']), [0.5, 0.5])
10b = BbnNode(Variable(1, 'b', ['on', 'off']), [0.5, 0.5, 0.4, 0.6])
11c = BbnNode(Variable(2, 'c', ['on', 'off']), [0.7, 0.3, 0.2, 0.8])
12d = BbnNode(Variable(3, 'd', ['on', 'off']), [0.9, 0.1, 0.5, 0.5])
13e = BbnNode(Variable(4, 'e', ['on', 'off']), [0.3, 0.7, 0.6, 0.4])
14f = BbnNode(Variable(5, 'f', ['on', 'off']), [0.01, 0.99, 0.01, 0.99, 0.01, 0.99, 0.99, 0.01])
15g = BbnNode(Variable(6, 'g', ['on', 'off']), [0.8, 0.2, 0.1, 0.9])
16h = BbnNode(Variable(7, 'h', ['on', 'off']), [0.05, 0.95, 0.95, 0.05, 0.95, 0.05, 0.95, 0.05])
17
18# create the network structure
19bbn = Bbn() \
20 .add_node(a) \
21 .add_node(b) \
22 .add_node(c) \
23 .add_node(d) \
24 .add_node(e) \
25 .add_node(f) \
26 .add_node(g) \
27 .add_node(h) \
28 .add_edge(Edge(a, b, EdgeType.DIRECTED)) \
29 .add_edge(Edge(a, c, EdgeType.DIRECTED)) \
30 .add_edge(Edge(b, d, EdgeType.DIRECTED)) \
31 .add_edge(Edge(c, e, EdgeType.DIRECTED)) \
32 .add_edge(Edge(d, f, EdgeType.DIRECTED)) \
33 .add_edge(Edge(e, f, EdgeType.DIRECTED)) \
34 .add_edge(Edge(c, g, EdgeType.DIRECTED)) \
35 .add_edge(Edge(e, h, EdgeType.DIRECTED)) \
36 .add_edge(Edge(g, h, EdgeType.DIRECTED))
37
38# convert the BBN to a join tree
39join_tree = InferenceController.apply(bbn)
40
41# insert an observation evidence
42ev = EvidenceBuilder() \
43 .with_node(join_tree.get_bbn_node_by_name('a')) \
44 .with_evidence('on', 1.0) \
45 .build()
46join_tree.set_observation(ev)
47
48# print the posterior probabilities
49for node, posteriors in join_tree.get_posteriors().items():
50 p = ', '.join([f'{val}={prob:.5f}' for val, prob in posteriors.items()])
51 print(f'{node} : {p}')
A Bayesian Belief Network (BBN) is defined as a pair, G, P
, where
G
is a directed acylic graph (DAG)P
is a joint probability distributionand
G
satisfies the Markov Condition (nodes are conditionally independent of non-descendants given its parents)
Ideally, the API should force the user to define G
and P
separately. However, there will be a bit of cognitive friction
with this API as we define nodes associated with their local probability models (conditional probability tables)
and then the structure afterwards. But this approach seems a bit more concise, no?
Updating Conditional Probability Tables¶
Sometimes, you may want to preserve the join tree structure and just update the condtional probability tables (CPTs). Here’s how to do so.
1from pybbn.graph.dag import Bbn
2from pybbn.graph.edge import EdgeType, Edge
3from pybbn.graph.node import BbnNode
4from pybbn.graph.variable import Variable
5from pybbn.pptc.inferencecontroller import InferenceController
6
7# you have built a BBN
8a = BbnNode(Variable(0, 'a', ['t', 'f']), [0.2, 0.8])
9b = BbnNode(Variable(1, 'b', ['t', 'f']), [0.1, 0.9, 0.9, 0.1])
10bbn = Bbn().add_node(a).add_node(b) \
11 .add_edge(Edge(a, b, EdgeType.DIRECTED))
12
13# you have built a junction tree from the BBN
14# let's call this "original" junction tree the left-hand side (lhs) junction tree
15lhs_jt = InferenceController.apply(bbn)
16
17# you may just update the CPTs with the original junction tree structure
18# the algorithm to find/build the junction tree is avoided
19# the CPTs are updated
20rhs_jt = InferenceController.reapply(lhs_jt, {0: [0.3, 0.7], 1: [0.2, 0.8, 0.8, 0.2]})
21
22# let's print out the marginal probabilities and see how things changed
23# print the marginal probabilities for the lhs junction tree
24print('lhs probabilities')
25# print the posterior probabilities
26for node, posteriors in lhs_jt.get_posteriors().items():
27 p = ', '.join([f'{val}={prob:.5f}' for val, prob in posteriors.items()])
28 print(f'{node} : {p}')
29
30# print the marginal probabilities for the rhs junction tree
31print('rhs probabilities')
32for node, posteriors in rhs_jt.get_posteriors().items():
33 p = ', '.join([f'{val}={prob:.5f}' for val, prob in posteriors.items()])
34 print(f'{node} : {p}')
Note that we use InferenceController.reapply(...)
to apply the new CPTs to a previous one and that we
get a new junction tree as an output.