# Potentials : named tensors

In pyAgrum, 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.

In [1]:

import pyAgrum as gum
import pyAgrum.lib.notebook as gnb

a,b,c=[gum.LabelizedVariable(s,s,2) for s in "abc"]


## potential algebra

In [2]:

p1=gum.Potential().add(a).add(b).fillWith([1,2,3,4]).normalize()

In [3]:

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
In [4]:

p3=p1+p2
gnb.showPotential(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
In [5]:

p4=gum.Potential()+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

## Bayes’ theorem

In [6]:

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

Out[6]:


In such a small bayes net, we can directly manipulate $$P(a,b,c)$$. For instance :

$P(b|c)=\frac{\sum_{a} P(a,b,c)}{\sum_{a,b} P(a,b,c)}$
In [7]:

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

Out[7]:

b
c
0
1
2
0
0.66070.05070.2887
1
0.21840.07310.7086
2
0.34700.08550.5675

## Joint, marginal probability, likelihood

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

In [8]:

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 1.0

Out[8]:

a
c
0
1
2
0
0.21550.01120.0617
1
0.36270.02260.0302
2
0.17110.00980.1151

### Computing $$p(A)$$

In [9]:

pAC.sumOut(["c"])

Out[9]:

a
0
1
2
0.74940.04370.2070

### Computing $$p(A |C=1)$$

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

In [10]:

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

Out[10]:

a
0
1
2
0.36270.02260.0302

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

In [11]:

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

Out[11]:

0.4155395593294868


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

In [12]:

pAC.extract({"c":1}).normalize()

Out[12]:

a
0
1
2
0.87290.05450.0726

### Computing $$P(A|C)$$

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

In [13]:

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.74730.03890.2138
1
0.87290.05450.0726
2
0.57800.03320.3888

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

$P(A|C=1)$

### Likelihood $$P(A=2|C)$$

A likelihood can also be found in this matrix.

In [14]:

pAgivenC.extract({'a':2})

Out[14]:

c
0
1
2
0.21380.07260.3888

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

In [15]:

pAgivenC.sumIn(["a"])

Out[15]:

a
0
1
2
2.19820.12660.6752

## entropy of potential

In [16]:

%matplotlib inline
from pylab import *
import matplotlib.pyplot as plt
import numpy as np

In [17]:

p1=gum.Potential().add(a)
x = np.linspace(0, 1, 100)
plt.plot(x,[p1.fillWith([p,1-p]).entropy() for p in x])
plt.show()

In [18]:

t=gum.LabelizedVariable('t','t',3)

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.e-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):
import math

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.xlim(0, 1)
plt.ylim(0, 0.75**0.5)
plt.axis('off')

draw_entropy()
plt.show()