Skip to content

Named tensors

In pyAgrum>=2.0.0, Tensors (previously Potentials) represent multi-dimensionnal arrays with (discrete) random variables attached to each dimension. This mathematical object have tensorial operators w.r.t. to the variables attached.

Creative Commons LicenseaGrUMinteractive online version
import pyagrum as gum
import pyagrum.lib.notebook as gnb
va, vb, vc = [gum.LabelizedVariable(s, s, 2) for s in "abc"]
p1 = gum.Tensor(va, vb).fillWith([1, 2, 3, 4]).normalize()
p2 = gum.Tensor(vb, vc).fillWith([4, 5, 2, 3]).normalize()
gnb.flow.row(p1, p2, p1 + p2, captions=["p1", "p2", "p1+p2"])
a
b
0
1
0
0.10000.2000
1
0.30000.4000

p1
b
c
0
1
0
0.28570.3571
1
0.14290.2143

p2
b
a
c
0
1
0
0
0.38570.6571
1
0.24290.5143
1
0
0.48570.7571
1
0.34290.6143

p1+p2
p3 = p1 + p2
p3 / p3.sumOut(["b"])
c
b
a
0
1
0
0
0.36990.3208
1
0.39080.3582
1
0
0.63010.6792
1
0.60920.6418
p4 = gum.Tensor() + p3
gnb.flow.row(p3, p4, captions=["p3", "p4"])
b
a
c
0
1
0
0
0.38570.6571
1
0.24290.5143
1
0
0.48570.7571
1
0.34290.6143

p3
b
a
c
0
1
0
0
1.38571.6571
1
1.24291.5143
1
0
1.48571.7571
1
1.34291.6143

p4
bn = gum.fastBN("a->c;b->c", 3)
bn
G c c b b b->c a a a->c

In such a small bayes net, we can directly manipulate P(a,b,c)P(a,b,c). For instance : P(bc)=aP(a,b,c)a,bP(a,b,c)P(b|c)=\frac{\sum_{a} P(a,b,c)}{\sum_{a,b} P(a,b,c)}

pABC = bn.cpt("a") * bn.cpt("b") * bn.cpt("c")
pBgivenC = pABC.sumOut(["a"]) / pABC.sumOut(["a", "b"])
pBgivenC.putFirst("b") # in order to have b horizontally in the table
b
c
0
1
2
0
0.46670.50240.0308
1
0.48850.46440.0471
2
0.65210.27610.0719

Let’s compute the joint probability P(A,B)P(A,B) from P(A,B,C)P(A,B,C)

pAC = pABC.sumOut(["b"])
print("pAC really is a probability : it sums to {}".format(pAC.sum()))
pAC
pAC really is a probability : it sums to 0.9999999999999999
a
c
0
1
2
0
0.26080.17820.0036
1
0.22110.07150.0051
2
0.11470.14090.0041
pAC.sumOut(["c"])
a
0
1
2
0.59670.39060.0128

It is easy to compute p(A,C=1)p(A, C=1)

pAC.extract({"c": 1})
a
0
1
2
0.22110.07150.0051

Moreover, we know that P(C=1)=AP(A,C=1)P(C=1)=\sum_A P(A,C=1)

pAC.extract({"c": 1}).sum()
0.2977224595357324

Now we can compute p(AC=1)=P(A,C=1)p(C=1)p(A|C=1)=\frac{P(A,C=1)}{p(C=1)}

pAC.extract({"c": 1}).normalize()
a
0
1
2
0.74270.24030.0170

P(AC)P(A|C) is represented by a matrix that verifies p(AC)=P(A,C)P(Cp(A|C)=\frac{P(A,C)}{P(C}

pAgivenC = (pAC / pAC.sumIn("c")).putFirst("a")
## putFirst("a") : to correctly show a cpt, the first variable have to bethe conditionned one
gnb.flow.row(pAgivenC, pAgivenC.extract({"c": 1}), captions=["$P(A|C)$", "$P(A|C=1)$"])
a
c
0
1
2
0
0.58930.40250.0082
1
0.74270.24030.0170
2
0.44180.54260.0157

$P(A|C)$
a
0
1
2
0.74270.24030.0170

$P(A|C=1)$

A likelihood can also be found in this matrix.

pAgivenC.extract({"a": 2})
c
0
1
2
0.00820.01700.0157

A likelihood does not have to sum to 1. It is not relevant to normalize it.

pAgivenC.sumIn(["a"])
a
0
1
2
1.77381.18530.0409
%matplotlib inline
from pylab import *
import matplotlib.pyplot as plt
import numpy as np
p1 = gum.Tensor(va)
x = np.linspace(0, 1, 100)
plt.plot(x, [p1.fillWith([p, 1 - p]).entropy() for p in x])
plt.show()

svg

t = gum.LabelizedVariable("t", "t", 3)
p1 = gum.Tensor().add(t)
def entrop(bc):
"""
bc is a list [a,b,c] close to a distribution
(normalized just to be sure)
"""
return p1.fillWith(bc).normalize().entropy()
import matplotlib.tri as tri
corners = np.array([[0, 0], [1, 0], [0.5, 0.75**0.5]])
triangle = tri.Triangulation(corners[:, 0], corners[:, 1])
## Mid-points of triangle sides opposite of each corner
midpoints = [(corners[(i + 1) % 3] + corners[(i + 2) % 3]) / 2.0 for i in range(3)]
def xy2bc(xy, tol=1.0e-3):
"""
From 2D Cartesian coordinates to barycentric.
"""
s = [(corners[i] - midpoints[i]).dot(xy - midpoints[i]) / 0.75 for i in range(3)]
return np.clip(s, tol, 1.0 - tol)
def draw_entropy(nlevels=200, subdiv=6, **kwargs):
refiner = tri.UniformTriRefiner(triangle)
trimesh = refiner.refine_triangulation(subdiv=subdiv)
pvals = [entrop(xy2bc(xy)) for xy in zip(trimesh.x, trimesh.y)]
plt.tricontourf(trimesh, pvals, nlevels, **kwargs)
plt.axis("equal")
plt.ylim(0, 0.75**0.5)
plt.axis("off")
draw_entropy()
plt.show()

svg