Credal Networks

Creative Commons License

aGrUM

interactive online version

In [1]:
import matplotlib.pyplot as plt

import pyagrum.credal_net as gum
import pyagrum.lib.notebook as gnb

gnb.configuration()
LibraryVersion
OSposix [darwin]
Python3.14.8 (main, Sep 30 2026, 17:55:09) [Clang 21.0.0 (clang-2100.3.34.2)]
IPython9.17.1
Matplotlib3.11.2
Numpy2.5.3
pyDot4.0.1
pyAgrum3.2.1
Fri Oct 09 08:52:22 2026 CEST

Credal Net from BN

In [2]:
bn = gum.fastBN("A->B[3]->C<-D<-A->E->F")
bn_min = gum.BayesNet(bn)
bn_max = gum.BayesNet(bn)
for n in bn.nodes():
  x = 0.4 * min(bn.cpt(n).min(), 1 - bn.cpt(n).max())
  bn_min.cpt(n).translate(-x)
  bn_max.cpt(n).translate(x)

cn = gum.CredalNet(bn_min, bn_max)
cn.intervalToCredal()
cn
Out[2]:
G E E F F E->F D D C C D->C A A A->E A->D B B A->B B->C

inference on Credal Net

In [3]:
gnb.flow.row(
  bn, bn.cpt("B"), cn, bn_min.cpt("B"), bn_max.cpt("B"), captions=["Bayes Net", "CPT", "Credal Net", "CPTmin", "CPTmax"]
)
Out[3]:
G E E F F E->F D D C C D->C A A A->E A->D B B A->B B->C
Bayes Net
B
A
0
1
2
0
0.50220.16210.3357
1
0.03730.34820.6144

CPT
G E E F F E->F D D C C D->C A A A->E A->D B B A->B B->C
Credal Net
B
A
0
1
2
0
0.48720.14720.3208
1
0.02240.33330.5995

CPTmin
B
A
0
1
2
0
0.51710.17710.3506
1
0.05230.36310.6294

CPTmax

Binarization

We can use LBP on CN (L2U) only for binary credal networks (here B is not binary). We then propose the classical binarization (but warn the user that this leads to approximation in the inference)

In [4]:
cn2 = gum.CredalNet(bn_min, bn_max)
cn2.intervalToCredal()
cn2.approximatedBinarization()
cn2.computeBinaryCPTMinMax()

gnb.flow.row(cn, cn2, captions=["Credal net", "Binarized credal net"])
Out[4]:
G E E F F E->F D D C C D->C A A A->E A->D B B A->B B->C
Credal net
G B-v1 B-v1 B-v0 B-v0 D D C C D->C E E F F E->F A A A->D A->E B-b1 B-b1 A->B-b1 B-b0 B-b0 A->B-b0 B-b1->B-v1 B-b1->B-v0 B-b1->C B-v2 B-v2 B-b1->B-v2 B-b0->B-v1 B-b0->B-v0 B-b0->C B-b0->B-b1 B-b0->B-v2
Binarized credal net

Here, \(B\) becomes

  • \(B\)-b\(i\) : the \(i\)-th bit of B

  • instrumental \(B\)-v\(k\) : the indicator variable for each modality \(k\) of \(B\)

In [5]:
ie_mc = gum.CNMonteCarloSampling(cn)
ie2_lbp = gum.CNLoopyPropagation(cn2)
ie2_mc = gum.CNMonteCarloSampling(cn2)
In [6]:
gnb.sideBySide(
  gnb.getInference(cn, engine=ie_mc), gnb.getInference(cn2, engine=ie2_mc), gnb.getInference(cn2, engine=ie2_lbp)
)
structs Inference in  21.03ms A 2026-10-09T08:52:24.397636 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B 2026-10-09T08:52:24.436742 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->B D 2026-10-09T08:52:24.521812 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->D E 2026-10-09T08:52:24.554590 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->E C 2026-10-09T08:52:24.487813 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B->C D->C F 2026-10-09T08:52:24.585415 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ E->F
structs Inference in  20.38ms A 2026-10-09T08:52:25.136693 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0 2026-10-09T08:52:25.160143 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->B-b0 B-b1 2026-10-09T08:52:25.181740 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->B-b1 D 2026-10-09T08:52:25.227557 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->D E 2026-10-09T08:52:25.257003 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->E B-b0->B-b1 C 2026-10-09T08:52:25.202243 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->C B-v0 2026-10-09T08:52:25.317612 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->B-v0 B-v1 2026-10-09T08:52:25.352025 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->B-v1 B-v2 2026-10-09T08:52:25.389550 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->B-v2 B-b1->C B-b1->B-v0 B-b1->B-v1 B-b1->B-v2 D->C F 2026-10-09T08:52:25.284002 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ E->F
structs Inference in   0.42ms A 2026-10-09T08:52:25.751748 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0 2026-10-09T08:52:25.793566 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->B-b0 B-b1 2026-10-09T08:52:25.832394 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->B-b1 D 2026-10-09T08:52:25.910519 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->D E 2026-10-09T08:52:25.933552 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ A->E B-b0->B-b1 C 2026-10-09T08:52:25.876645 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->C B-v0 2026-10-09T08:52:25.983621 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->B-v0 B-v1 2026-10-09T08:52:26.049646 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->B-v1 B-v2 2026-10-09T08:52:26.080755 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ B-b0->B-v2 B-b1->C B-b1->B-v0 B-b1->B-v1 B-b1->B-v2 D->C F 2026-10-09T08:52:25.953561 image/svg+xml Matplotlib v3.11.2, https://matplotlib.org/ E->F
In [7]:
gnb.sideBySide(
  ie_mc.CN(),
  ie_mc.marginalMin("F"),
  ie_mc.marginalMax("F"),
  ie_mc.CN(),
  ie2_lbp.marginalMin("F"),
  ie2_lbp.marginalMax("F"),
  ncols=3,
)
print(cn)
G E E F F E->F D D C C D->C A A A->E A->D B B A->B B->C
F
0
1
0.35900.3529
F
0
1
0.64710.6410
G E E F F E->F D D C C D->C A A A->E A->D B B A->B B->C
F
0
1
0.34860.3529
F
0
1
0.64710.6514

A:Range([0,1])
<> : [[0.0822669 , 0.917733] , [0.191956 , 0.808044]]

B:Range([0,2])
<A:0> : [[0.487237 , 0.162132 , 0.350631] , [0.487237 , 0.177072 , 0.335691] , [0.502179 , 0.177072 , 0.320749] , [0.517117 , 0.162133 , 0.320749] , [0.502176 , 0.147193 , 0.350631] , [0.517117 , 0.147193 , 0.33569]]
<A:1> : [[0.022409 , 0.348209 , 0.629382] , [0.022409 , 0.363148 , 0.614443] , [0.037349 , 0.363148 , 0.599502] , [0.0522906 , 0.348207 , 0.599502] , [0.0373505 , 0.333267 , 0.629382] , [0.0522906 , 0.333267 , 0.614442]]

C:Range([0,1])
<B:0|D:0> : [[0.733804 , 0.266196] , [0.822059 , 0.177941]]
<B:1|D:0> : [[0.390707 , 0.609293] , [0.478962 , 0.521038]]
<B:2|D:0> : [[0.696602 , 0.303398] , [0.784856 , 0.215144]]
<B:0|D:1> : [[0.370031 , 0.629969] , [0.458283 , 0.541717]]
<B:1|D:1> : [[0.845557 , 0.154443] , [0.93381 , 0.0661896]]
<B:2|D:1> : [[0.179367 , 0.820633] , [0.267621 , 0.732379]]

D:Range([0,1])
<A:0> : [[0.0700837 , 0.929916] , [0.163528 , 0.836472]]
<A:1> : [[0.587363 , 0.412637] , [0.680806 , 0.319194]]

E:Range([0,1])
<A:0> : [[0.175949 , 0.824051] , [0.410546 , 0.589454]]
<A:1> : [[0.358344 , 0.641656] , [0.592941 , 0.407059]]

F:Range([0,1])
<E:0> : [[0.120823 , 0.879177] , [0.281922 , 0.718078]]
<E:1> : [[0.660513 , 0.339487] , [0.821612 , 0.178388]]


Credal Net from bif files

In [8]:
cn = gum.CredalNet("res/cn/2Umin.bif", "res/cn/2Umax.bif")
cn.intervalToCredal()
In [9]:
gnb.showCN(cn, "2")
../_images/notebooks_24-Models_credalNetworks_15_0.svg
In [10]:
ie = gum.CNMonteCarloSampling(cn)
ie.insertEvidenceFile("res/cn/L2U.evi")
In [11]:
ie.setRepetitiveInd(False)
ie.setMaxTime(1)
ie.setMaxIter(1000)

ie.makeInference()
In [12]:
cn
Out[12]:
G H H L L H->L E E E->H D D G G D->G F F D->F C C C->F A A A->E B B B->E F->H
In [13]:
gnb.showInference(cn, targets={"A", "H", "L", "D"}, engine=ie, evs={"L": [0, 1], "G": [1, 0]})
../_images/notebooks_24-Models_credalNetworks_19_0.svg

Comparing inference in credal networks

In [14]:
import pyagrum.credal_net as gum


def showDiffInference(model, mc, lbp):
  for i in model.current_bn().nodes():
    a, b = mc.marginalMin(i)[:]
    c, d = mc.marginalMax(i)[:]

    e, f = lbp.marginalMin(i)[:]
    g, h = lbp.marginalMax(i)[:]

    plt.scatter([a, b, c, d], [e, f, g, h])


cn = gum.CredalNet("res/cn/2Umin.bif", "res/cn/2Umax.bif")
cn.intervalToCredal()

Inference with no evidence

The two inference give quite the same result

In [15]:
ie_mc = gum.CNMonteCarloSampling(cn)
ie_mc.makeInference()

cn.computeBinaryCPTMinMax()
ie_lbp = gum.CNLoopyPropagation(cn)
ie_lbp.makeInference()

showDiffInference(cn, ie_mc, ie_lbp)
../_images/notebooks_24-Models_credalNetworks_23_0.svg

The problem of evidence

When evidence are inserted, there are some divergence.

In [16]:
ie_mc = gum.CNMonteCarloSampling(cn)
ie_mc.insertEvidenceFile("res/cn/L2U.evi")
ie_mc.makeInference()

ie_lbp = gum.CNLoopyPropagation(cn)
ie_lbp.insertEvidenceFile("res/cn/L2U.evi")
ie_lbp.makeInference()

showDiffInference(cn, ie_mc, ie_lbp)
../_images/notebooks_24-Models_credalNetworks_25_0.svg

Dynamical Credal Net

In [17]:
cn = gum.CredalNet("res/cn/bn_c_8.bif", "res/cn/den_c_8.bif")
cn.bnToCredal(0.8, False)
In [18]:
ie = gum.CNMonteCarloSampling(cn)
ie.insertModalsFile("res/cn/modalities.modal")

ie.setRepetitiveInd(True)
ie.setMaxTime(5)
ie.setMaxIter(1000)

ie.makeInference()
In [19]:
print(ie.dynamicExpMax("temp"))
(14.203404648134416, 11.669951317919612, 12.190483075680442, 12.019944850932905, 12.00214897282622, 12.008870898650432, 12.007624551734526, 12.007682925808101, 12.007727248106775)
In [20]:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill_between(range(9), ie.dynamicExpMax("temp"), ie.dynamicExpMin("temp"))
plt.show()
../_images/notebooks_24-Models_credalNetworks_30_0.svg
In [21]:
ie = gum.CNMonteCarloSampling(cn)
ie.insertModalsFile("res/cn/modalities.modal")

ie.setRepetitiveInd(False)
ie.setMaxTime(5)
ie.setMaxIter(1000)

ie.makeInference()
print(ie.messageApproximationScheme())
stopped with epsilon=0
In [22]:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill_between(range(9), ie.dynamicExpMax("temp"), ie.dynamicExpMin("temp"))
plt.show()
../_images/notebooks_24-Models_credalNetworks_32_0.svg
In [23]:
ie = gum.CNMonteCarloSampling(cn)
ie.insertModalsFile("res/cn/modalities.modal")

ie.setRepetitiveInd(False)
ie.setMaxTime(5)
ie.setMaxIter(5000)

gnb.animApproximationScheme(ie)
ie.makeInference()
../_images/notebooks_24-Models_credalNetworks_33_0.svg
../_images/notebooks_24-Models_credalNetworks_33_1.svg
In [24]:
fig = plt.figure()
ax = fig.add_subplot(111)
ax.fill_between(range(9), ie.dynamicExpMax("temp"), ie.dynamicExpMin("temp"))
plt.show()
../_images/notebooks_24-Models_credalNetworks_34_0.svg
In [ ]: