示例#1
0
    def _comparePotentials(self, pot1, pot2):
        """
    Compare 2 potentials one in each Bayesian network

    Parameters
    ----------
    pot1 : pyAgrum.Potential
      one of b1's cpts
    pot2 : pyAgrum.Potential
      one of _bn2's cpts

    Returns
    -------
    str
      'OK' if CPTs are the same

    Raises
    ------
    gum.KeyError
      If cpts are not from the same variable
    """
        I1 = gum.Instantiation(pot1)
        I2 = gum.Instantiation(pot2)
        I1.setFirst()
        while not I1.end():
            I2.fromdict(I1.todict())  # copy value on the base of names
            if abs(pot1.get(I1) - pot2.get(I2)) > self.DELTA_ERROR:
                return "Different CPTs for " + pot1.variable(0).name()
            I1 += 1
        return "OK"
示例#2
0
文件: utils.py 项目: phwuil/gumApprox
def KL(p, q):
  """
  Compute KL(p,q), Kullback-Leibler divergence

  :param p: gum.Potential
  :param q: gum.Potential
  :return: float
  """
  Ip = gum.Instantiation(p)
  Iq = gum.Instantiation(q)
  s = 0
  while not Ip.end():
    if p.get(Ip) > 0:
      if q.get(Iq) > 0:
        s += p.get(Ip) * math.log2(p.get(Ip) / q.get(Iq))
      else:
        s += 1  # we penalize q==0 and p>0
    else:
      if q.get(Iq) > 0:
        s += 1  # we penalize q>0 and p==0

    Ip.inc()
    Iq.inc()

  # it may happen that if p==q, s<0 (approximation)
  return 0 if s <= 0 else s
示例#3
0
    def testChainingAdd(self):
        bn = gum.fastBN("A->B")
        i = bn.completeInstantiation()
        m1 = gum.Instantiation().add(bn.variable("A")).add(bn.variable("B"))
        m2 = gum.Instantiation().add(bn.variable("B")).add(bn.variable("A"))

        self.assertEqual(i, m1)
        self.assertEqual(i, m2)
        m1.inc()
        self.assertNotEqual(i, m1)

        i = gum.Instantiation().addVarsFromModel(bn, ["A", "B"])
        m1 = gum.Instantiation().add(bn.variable("A")).add(bn.variable("B"))
        m2 = gum.Instantiation().add(bn.variable("B")).add(bn.variable("A"))

        self.assertEqual(i, m1)
        self.assertEqual(i, m2)
        m1.inc()
        self.assertNotEqual(i, m1)

        i = gum.Instantiation().addVarsFromModel(bn, ["B", "A"])
        m1 = gum.Instantiation().add(bn.variable("A")).add(bn.variable("B"))
        m2 = gum.Instantiation().add(bn.variable("B")).add(bn.variable("A"))

        self.assertEqual(i, m1)
        self.assertEqual(i, m2)
        m1.inc()
        self.assertNotEqual(i, m1)
def seriesToInstantiation(mn, df):
    d = df.to_dict()
    I = gum.Instantiation()
    for i in d.keys():
        I.add(mn.variableFromName(i))
    I.fromdict(d)
    return I
示例#5
0
    def testInsertion(self):
        mn = gum.MarkovNet()
        self._fill(mn)

        with self.assertRaises(gum.InvalidArgument):
            mn.addFactor(gum.Potential())  # no empty factor

        with self.assertRaises(gum.InvalidArgument):
            mn.addFactor({"11", "31"})  # already exist

        mn1 = gum.MarkovNet()
        self._fill(mn1)
        pot = gum.Potential().add(mn1.variable("11")).add(mn1.variable("21"))
        pot.randomDistribution()
        mn1.addFactor(pot)
        self.assertEqual(pot.__str__(), mn1.factor({"11", "21"}).__str__())

        mn1 = gum.MarkovNet()
        self._fill(mn1)
        pot = gum.Potential().add(mn1.variable("21")).add(mn1.variable("11"))
        pot.randomDistribution()
        mn1.addFactor(pot)

        # should be equal : does not depend of the order of vars in the MarkonNet
        self.assertEqual(pot.__str__(), mn1.factor({"11", "21"}).__str__())

        # but the data should be the same
        I = gum.Instantiation(pot)
        factor = mn1.factor({"21", "11"})
        I.setFirst()
        while not I.end():
            self.assertAlmostEqual(pot.get(I), factor.get(I), places=7)
            I.inc()
示例#6
0
def draw(p):
  """
  Draw a sample using p
  :param p: the distribution
  :return: (v,q) where v is the value and q is the deterministic distribution for v
  """
  q = gum.Potential(p)
  r = random.random()
  i = gum.Instantiation(p)
  val = 0
  while not i.end():
    if r < 0:
      q.set(i, 0)
    else:
      if r <= p.get(i):
        val = i.val(0)
        q.set(i, 1)
      else:
        q.set(i, 0)
    r -= p.get(i)
    i.inc()

  if q.sum() != 1:
    print("ACHTUNG : {} {}".format(p.sum(), p))
    print("AND THEN : {}".format(q))

  return val,q
示例#7
0
    def testLogit(self):
        bn = gum.BayesNet()
        age = bn.add(gum.RangeVariable("age", "", 35, 67))
        taux = bn.add(gum.RangeVariable("taux", "", 115, 171))
        angine = bn.add(gum.LabelizedVariable("angine", ""))
        vc = gum.LabelizedVariable("coeur", "", 0)
        vc.addLabel("NON").addLabel("OUI")
        coeur = bn.addLogit(vc, 14.4937)

        bn.addWeightedArc(age, coeur, -0.1256)
        bn.addWeightedArc(taux, coeur, -0.0636)
        bn.addWeightedArc(angine, coeur, 1.779)

        witness_age = ("50", "49", "46", "49", "62", "35", "67", "65", "47")
        witness_taux = ("126", "126", "144", "139", "154", "156", "160", "140",
                        "143")
        witness_angine = ("1", "0", "0", "0", "1", "1", "0", "0", "0")
        witness_coeur = ("OUI", "OUI", "OUI", "OUI", "OUI", "OUI", "NON",
                         "NON", "NON")

        witness_proba = (0.8786, 0.5807, 0.3912, 0.3773, 0.2127, 0.8760,
                         1 - 0.0163, 1 - 0.0710, 1 - 0.3765)

        inst = gum.Instantiation(bn.cpt(coeur))
        for i in range(len(witness_age)):
            inst.chgVal(bn.variable(age), bn.variable(age)[witness_age[i]])
            inst.chgVal(bn.variable(taux), bn.variable(taux)[witness_taux[i]])
            inst.chgVal(bn.variable(angine),
                        bn.variable(angine)[witness_angine[i]])
            inst.chgVal(bn.variable(coeur),
                        bn.variable(coeur)[witness_coeur[i]])
            self.assertAlmostEqual(bn.cpt(coeur).get(inst),
                                   witness_proba[i],
                                   places=3)
示例#8
0
  def testEqualities(self):
    u = gum.LabelizedVariable("u", "u", 4)
    v = gum.LabelizedVariable("v", "v", 2)
    w = gum.LabelizedVariable("w", "w", 3)
    p = gum.Potential().add(u).add(v).add(w)
    p.random()

    q = gum.Potential(p)
    self.assertEqual(p, q)

    i = gum.Instantiation(q)
    i.setLast()
    q[i] = 0
    self.assertNotEqual(p, q)

    q.fillWith(p)
    self.assertEqual(p, q)

    q.fillWith(1)
    self.assertNotEqual(p, q)

    q.fillWith(p)
    self.assertEqual(p, q)

    x = gum.LabelizedVariable("x", "Unknown", 5)
    q.add(x)
    self.assertNotEqual(p, q)

    q = gum.Potential().add(v).add(w)
    self.assertNotEqual(p, q)
示例#9
0
def compactPot(p):
  res = ""
  i = gum.Instantiation(p)
  i.setFirst()
  while not i.end():
    res += "|{:7.3f}".format(100 * p.get(i))
    i.inc()
  return "[" + res[1:] + "]"
示例#10
0
  def predict_proba(self, X):
    """
    Predicts the probability of classes for each row of input data, with bn's Markov Blanket

    Parameters
    ----------
    X: str or {array-like, sparse matrix} of shape (n_samples, n_features) or str
            test data, can be either dataFrame, matrix or name of a csv file

    Returns
    -------
    array-like of shape (n_samples,)
      Predicted probability for each classes
    """
    # dictionary of the name of a variable and his column in the data base
    dictName = self.variableNameIndexDictionary

    if isinstance(X, pandas.DataFrame):  # type(X) == pandas.DataFrame:
      dictName = DFNames(X)
      vals = X.to_numpy()
    elif type(X) == str:
      vals, _ = self.XYfromCSV(X, target=self.target)
      dictName = DFNames(vals, vals)
      vals = vals.to_numpy()
    else:
      vals = X

    if self.fromModel:
      vals = sklearn.utils.check_array(vals, dtype='str', ensure_2d=False)
    else:
      sklearn.utils.check_array(vals, dtype=None, ensure_2d=False)

    returned_list = []

    # label of the target
    label1 = self.label
    # list of other labels of the target
    labels = [self.bn.variable(self.target).label(i)
              for i in range(self.bn.variable(self.target).domainSize())
              if self.bn.variable(self.target).label(i) != self.label]

    # Instantiation use to apply values of the data base
    I = self.MarkovBlanket.completeInstantiation()

    # read through data base's ligns
    if self.isBinaryClassifier:
      for x in vals:
        res = round(_calcul_proba_for_binary_class(x, label1, labels, I,
                                                   dictName, self.MarkovBlanket, self.target), self.significant_digit)
        returned_list.append([1 - res, res])
    else:
      local_inst = gum.Instantiation(I)
      local_inst.erase(self.target)
      for x in vals:
        returned_list.append(_calcul_proba_for_nary_class(
          x, local_inst, dictName, self.MarkovBlanket, self.target).tolist())

    return numpy.array(returned_list)
示例#11
0
  def testRandomPotential(self):
    u = gum.LabelizedVariable("u", "u", 4)
    v = gum.LabelizedVariable("v", "v", 2)
    w = gum.LabelizedVariable("w", "w", 3)

    p = gum.Potential().add(u).add(v).add(w)
    I = gum.Instantiation(p)

    p.random()
    I.setFirst()
    while not I.end():
      self.assertTrue(p.get(I) <= 1.0)
      I.inc()

    p.randomDistribution()
    I.setFirst()
    cum = 0
    while not I.end():
      self.assertTrue(p.get(I) <= 1.0)
      cum += p.get(I)
      I.inc()
    self.assertAlmostEqual(cum, 1.0, 6)

    p.randomCPT()
    v = p.variable(0)
    I.setFirst()
    while not I.end():
      cum = 0.0
      I.setFirstVar(v)
      while not I.end():
        self.assertTrue(p.get(I) <= 1.0)
        cum += p.get(I)
        I.incVar(v)
      self.assertAlmostEqual(cum, 1.0, 6)

      I.unsetEnd()
      I.incNotVar(v)

    p.fillWith(1).normalizeAsCPT()
    I.setFirst()
    while not I.end():
      self.assertAlmostEqual(p.get(I), 0.25, 6)
      I.inc()

    alpha = 0.0
    while alpha <= 1.0:
      p.fillWith(1).normalizeAsCPT()
      p.noising(alpha)
      min = (1 - alpha) * 0.25 + alpha * 0.0
      max = (1 - alpha) * 0.25 + alpha * 1.0
      I.setFirst()
      while not I.end():
        self.assertTrue(min <= p.get(I) <= max)
        I.inc()
      alpha += 0.1
示例#12
0
    def testDBNTonda(self):
        dbn = gum.BayesNet()
        l = [
            dbn.add(gum.LabelizedVariable(name, name, nbr))
            for (name, nbr) in [("bf_0", 4), ("bf_t", 4), ("c_0", 5), (
                "c_t", 5), ("h_0", 5), ("h_t", 5), ("tf_0",
                                                    5), ("tf_t",
                                                         5), ("wl_0",
                                                              4), ("wl_t", 4)]
        ]
        for node in ["c_t", "h_t", "wl_t"]:
            dbn.addArc(dbn.idFromName("tf_0"), dbn.idFromName(node))
            dbn.addArc(dbn.idFromName("bf_0"), dbn.idFromName(node))
        dbn.addArc(dbn.idFromName("c_0"), dbn.idFromName("c_t"))
        dbn.addArc(dbn.idFromName("h_0"), dbn.idFromName("h_t"))
        dbn.addArc(dbn.idFromName("wl_0"), dbn.idFromName("wl_t"))

        csvfile = self.agrumSrcDir('DBN_Tonda.csv')
        l1 = gum.BNLearner(csvfile)
        l1.setInitialDAG(dbn.dag())
        l1.useScoreLog2Likelihood()
        l1.useSmoothingPrior()
        bn1 = l1.learnParameters()

        l2 = gum.BNLearner(csvfile, dbn)
        l2.setInitialDAG(dbn.dag())
        l2.useScoreLog2Likelihood()
        l2.useSmoothingPrior()
        bn2 = l2.learnParameters()

        p1 = bn1.cpt(bn1.idFromName("c_0"))
        I1 = gum.Instantiation(p1)
        p2 = bn2.cpt(bn2.idFromName("c_0"))
        I2 = gum.Instantiation(p2)
        I1.setFirst()
        I2.setFirst()
        while not I1.end():
            self.assertEqual(p1.get(I1), p2.get(I2))
            I1.inc()
            I2.inc()
示例#13
0
 def testWithInstantiation(self):
   bn = gum.BayesNet()
   id_list = []
   self.fillBN(bn, id_list)
   list3 = bn.cpt(id_list[3])
   list3[:] = [[[1, 0], [0.1, 0.9]],
               [[0.1, 0.9], [0.01, 0.99]]]
   i = gum.Instantiation(list3)
   list3.set(i, 0)
   i.inc()
   list3.set(i, 1)
   self.assertListsAlmostEqual(list3[:],
                               [[[0, 1], [0.1, 0.9]],
                                [[0.1, 0.9], [0.01, 0.99]]])
   self.assertListsAlmostEqual(list3[:], bn.cpt(id_list[3])[:])
示例#14
0
    def testOperatorEqual(self):
        bn = gum.fastBN("a{chaud|tiede|froid}->b[5]<-c->d->e;c->e")
        i = bn.completeInstantiation()
        j = bn.completeInstantiation()
        self.assertEqual(i, j)
        while not i.end():
            self.assertEqual(i, j)
            i.inc()
            self.assertNotEqual(i, j)
            j.inc()

        k = gum.Instantiation()
        self.assertNotEqual(i, k)  # not the same size
        k.inc()
        self.assertEqual(i, k)  # both are in overflow => equals
示例#15
0
    def compareParams(self, bn1, bn2):

        # Valeur d'epsilon
        epsilon = 0

        # On calcule la somme des carrés de la difference de toutes les valeurs des BN deux à deux
        for i in range(len(bn1.names())):
            inst = gum.Instantiation(bn1.cpt(i))
            inst.setFirst()
            epsilon += pow(bn1.cpt(i).get(inst) - bn2.cpt(i).get(inst), 2)
            inst.inc()

        # On divise la somme par le nombre de paramètres
        epsilon /= len(bn1.names())

        return epsilon
示例#16
0
  def testExtraction(self):
    a, b, c = [gum.LabelizedVariable(s, s, 3) for s in "abc"]
    p = gum.Potential().add(a).add(b).fillWith([1, 2, 3, 4, 5, 6, 7, 8, 9])
    q = gum.Potential().add(c).fillWith([1, 2, 3])

    pot = q * p

    I = gum.Instantiation()
    I.add(c)
    I.chgVal(c, 0)
    self.assertEqual(pot.extract(I), p)

    I.chgVal(c, 2)
    r = gum.Potential().add(a).add(b).fillWith(
      [3, 6, 9, 12, 15, 18, 21, 24, 27])
    self.assertEqual(pot.reorganize(['b', 'c', 'a']).extract(I), r)
示例#17
0
def KL(p, q):
  """
  Compute KL(p,k), Kullback-Leibler divergence

  :param p: gum.Potential
  :param q: gum.Potential
  :return: float
  """
  I = gum.Instantiation(p)
  s = 0
  while not I.end():
    if p.get(I) > 0:
      s += p.get(I) * math.log2(p.get(I) / q.get(I))
    I.inc()

  # it may happen that if p==q, s<0 (approximation)
  return 0 if s <= 0 else s
示例#18
0
文件: utils.py 项目: phwuil/gumApprox
def draw(p):
  """
  Draw a sample using p
  :param p: a probability distribution over a variable v
  :return: (v,q) where v is the value and q is the deterministic distribution for v
  """
  r = random.random()
  i = gum.Instantiation(p)
  val = 0
  while not i.end():
    r -= p.get(i)
    if r <= 0:
      val = i.val(0)
      break
    i.inc()

  return val, deterministicPotential(p.variable(0), val)
 def implement_constraint(self, bn):
     bn.cpt('constraint')
     p_con = bn.cpt('constraint')
     i_con = gum.Instantiation(p_con)
     i_con.setFirst()
     while (not i_con.end()):
         dict_of_row = i_con.todict(withLabels=True)
         values = list(dict_of_row.values())
         scn, truth_val = dict_of_row['aux'], dict_of_row['constraint']
         if scn == "NA":
             if truth_val == self.truth_values[1]:
                 p_con.set(i_con, 1)
             else:
                 p_con.set(i_con, 0)
         else:
             index_of_label = bn.variable(scn).index(
                 truth_val)  # i hate this, but this is the only way to access labels in vars
             p_con.set(i_con, 1 - bn.cpt(scn)[index_of_label])
         i_con.inc()
示例#20
0
  def testFillWithFunction(self):
    bn = gum.fastBN(
      "C[0,1,2,3,4,5,6,7,8]<-A[0,3]->B{0|1|2|3|4|5|6|7|8};A->D[9]")

    bn.cpt("D").fillWithFunction("3*A+2")

    I = gum.Instantiation(bn.cpt("D"))
    while not I.end():
      v = 3 * I.val(1) + 2
      if v >= I.variable(0).domainSize():
        v = I.variable(0).domainSize() - 1
      self.assertEqual(bn.cpt("D").get(I), 1 if I.val(0) == v else 0)
      I.inc()

    with self.assertRaises(gum.InvalidArgument):
      bn.cpt("B").fillWithFunction("3*A+2", noise=[2, 1])

    with self.assertRaises(gum.InvalidArgument):
      bn.cpt("B").fillWithFunction("3*A+2")
 def create_first_cases(self):
     new_case_list = []
     for scn in self.scenario_nodes:
         p = self.bn.cpt(scn)
         i = gum.Instantiation(p)
         i.setFirst()
         while (not i.end()):
             dict_of_row = i.todict(withLabels=True)
             if dict_of_row[scn] == self.truth_values[0]:      # only select the 'true' priors for the first case model line
                 case_name = scn
                 case_scenario = scn
                 case_area = p.get(i)
                 case_width = p.get(i)
                 case_conditional_prior_dict = {scn : case_area}
                 case_evidence_dict = {}
                 new_case = case.Case(case_name, case_scenario, case_area, case_width,
                                     case_conditional_prior_dict, case_evidence_dict)
                 new_case_list.append(new_case)
             i.inc()
     self.casemodel.cases = new_case_list
示例#22
0
  def testCopyConstructor(self):
    pot = gum.Potential()
    pot.add(self.var['c'])
    pot.add(self.var['s'])
    pot.add(self.var['r'])

    i = gum.Instantiation(pot)
    val = 1
    i.setFirst()
    while not i.end():
      pot.set(i, val)
      val += 1
      i.inc()
    self.assertEqual(pot.sum(), 36.0)

    pot2 = gum.Potential(pot)
    self.assertEqual(pot2.sum(), 36.0)

    i.setFirst()
    pot.set(i, 0)  # instead of 1
    self.assertEqual(pot.sum(), 35.0)
    self.assertEqual(pot2.sum(), 36.0)
def computePseudoLogLikelihoodData(mn, df):
    """
    Compute the pseudo loglikelihood, on a given markov network, of an instantiation

    Parameters
    ----------
    mn : pyAgrum.MarkovNet
            the markov network
    df : pandas.core.series.Series
            the instantiation
            
    Returns
    -------
    float
            the pseudo Loglikelihood
    """
    data = df.to_dict()
    I = gum.Instantiation()
    for i in data.keys():
        I.add(mn.variableFromName(i))
    I.fromdict(data)
    sumPseudoLogLikelihood = 0
    factors = factorsName(mn)

    for variable in data.keys():
        originalValue = I[variable]
        variable = mn.variableFromName(variable)
        variableFactors = [
            factor for factor in factors if variable.name() in factor
        ]
        phat = multiplyFactor(variableFactors, I, mn)
        sommephat = sum([
            multiplyFactor(variableFactors, I.chgVal(variable, int(value)), mn)
            for value in range(variable.domainSize())
        ])
        I.chgVal(variable, originalValue)
        sumPseudoLogLikelihood += np.log(phat / sommephat)
    return sumPseudoLogLikelihood
 def implement_aux(self, bn):
     p_aux = bn.cpt('aux')
     i_aux = gum.Instantiation(p_aux)
     i_aux.setFirst()
     while (not i_aux.end()):
         dict_of_row = i_aux.todict(withLabels=True)
         values = list(dict_of_row.values())
         num_true = values.count(self.truth_values[0])
         if num_true < 1 or num_true > 1:  # no scenario is true, aux is NA
             if dict_of_row['aux'] == "NA":
                 p_aux.set(i_aux, 1)
             else:
                 p_aux.set(i_aux, 0)
         else:
             true_scenario = False
             for key in dict_of_row.keys():
                 if dict_of_row[key] == self.truth_values[0]:
                     true_scenario = key
             if dict_of_row['aux'] == true_scenario:
                 p_aux.set(i_aux, 1)
             else:
                 p_aux.set(i_aux, 0)
         i_aux.inc()
示例#25
0
#notice appear in supporting documentation or portions
#thereof, including modifications, that you make.

#THE AUTHOR P.H. WUILLEMIN (@LIP6)  DISCLAIMS ALL WARRANTIES
#WITH REGARD TO THIS SOFTWARE, INCLUDING ALL IMPLIED
#WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT
#SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT
#OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER
#RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER
#IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS
#ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE
#OR PERFORMANCE OF THIS SOFTWARE!
"""
How to use Instantiation to iterate in a cpt
"""

import pyAgrum as gum

bn = gum.BayesNet()
bn.loadBIF("../resources/bn.bif")

p_a = bn.cpt(0)
i = gum.Instantiation(p_a)
i.setFirst()
s = 0.0
while (not i.end()):
    print i
    s += p_a.get(i)
    i.inc()
print s
示例#26
0
def _reprPotential(pot,
                   digits=None,
                   withColors=True,
                   varnames=None,
                   asString=False):
    """
  return a representation of a gum.Potential as a HTML table.
  The first dimension is special (horizontal) due to the representation of conditional probability table

  :param pot: the potential to get
  :param digits: number of digits to show
  :param withColors: bgcolor for proba cells or not
  :param varnames: the aliases for variables name in the table
  :param asString: display the table or a HTML string

  :return: the representation
  """
    from fractions import Fraction

    r0, g0, b0 = gumcols.hex2rgb(gum.config['notebook', 'potential_color_0'])
    r1, g1, b1 = gumcols.hex2rgb(gum.config['notebook', 'potential_color_1'])

    if digits is None:
        digits = gum.config['notebook', 'potential_visible_digits']

    if gum.config["notebook", "potential_with_colors"] == "False":
        withColors = False

    with_fraction = gum.config['notebook', 'potential_with_fraction'] == "True"
    if with_fraction:
        fraction_limit = int(gum.config['notebook',
                                        'potential_fraction_limit'])
        fraction_round_error = float(
            gum.config['notebook', 'potential_fraction_round_error'])
        fraction_with_latex = gum.config[
            'notebook', 'potential_fraction_with_latex'] == "True"

    def _rgb(r, g, b):
        return '#%02x%02x%02x' % (r, g, b)

    def _mkCell(val):
        s = "<td style='"
        if withColors and (0 <= val <= 1):
            r = int(r0 + val * (r1 - r0))
            g = int(g0 + val * (g1 - g0))
            b = int(b0 + val * (b1 - b0))

            tx = gumcols.rgb2brightness(r, g, b)

            s += "color:" + tx + ";background-color:" + _rgb(r, g, b) + ";"

        str_val = ""
        if with_fraction:
            frac_val = Fraction(val).limit_denominator(fraction_limit)
            val_app = frac_val.numerator / frac_val.denominator
            if abs(val_app - val) < fraction_round_error:
                str_val = "text-align:center;'>"
                if fraction_with_latex:
                    str_val += "$$"
                    if frac_val.denominator > 1:
                        str_val += f"\\frac{{{frac_val.numerator}}}{{{frac_val.denominator}}}"
                    else:
                        str_val += f"{frac_val.numerator}"
                    str_val += "$$"
                else:
                    str_val += f"{frac_val}"
                str_val += "</td>"
        if str_val == "":
            str_val = f"text-align:right;'>{val:.{digits}f}</td>"

        return s + str_val

    html = list()
    html.append('<table style="border:1px solid black;">')
    if pot.empty():
        html.append("<tr><th>&nbsp;</th></tr>")
        html.append("<tr>" + _mkCell(pot.get(gum.Instantiation())) + "</tr>")
    else:
        if varnames is not None and len(varnames) != pot.nbrDim():
            raise ValueError(
                f"varnames contains {len(varnames)} value(s) instead of the needed {pot.nbrDim()} value(s)."
            )

        nparents = pot.nbrDim() - 1
        var = pot.variable(0)
        varname = var.name() if varnames == None else varnames[0]

        # first line
        if nparents > 0:
            html.append(f"""<tr><th colspan='{nparents}'></th>
      <th colspan='{var.domainSize()}' style='border:1px solid black;color:black;background-color:#808080;'><center>{varname}</center>
      </th></tr>""")
        else:
            html.append(
                f"""<tr style='border:1px solid black;color:black;background-color:#808080'>
      <th colspan='{var.domainSize()}'><center>{varname}</center></th></tr>""")

        # second line
        s = "<tr>"
        if nparents > 0:
            # parents order
            if gum.config["notebook", "potential_parent_values"] == "revmerge":
                pmin, pmax, pinc = nparents - 1, 0 - 1, -1
            else:
                pmin, pmax, pinc = 0, nparents, 1

            if varnames is None:
                varnames = list(reversed(pot.names))
            for par in range(pmin, pmax, pinc):
                parent = varnames[par]
                s += f"<th style='border:1px solid black;color:black;background-color:#808080'><center>{parent}</center></th>"

        for label in var.labels():
            s += f"""<th style='border:1px solid black;border-bottom-style: double;color:black;background-color:#BBBBBB'>
      <center>{label}</center></th>"""
        s += '</tr>'

        html.append(s)

        inst = gum.Instantiation(pot)
        off = 1
        offset = dict()
        for i in range(1, nparents + 1):
            offset[i] = off
            off *= inst.variable(i).domainSize()

        inst.setFirst()
        while not inst.end():
            s = "<tr>"
            # parents order
            if gum.config["notebook", "potential_parent_values"] == "revmerge":
                pmin, pmax, pinc = 1, nparents + 1, 1
            else:
                pmin, pmax, pinc = nparents, 0, -1
            for par in range(pmin, pmax, pinc):
                label = inst.variable(par).label(inst.val(par))
                if par == 1 or gum.config[
                        "notebook", "potential_parent_values"] == "nomerge":
                    s += f"<th style='border:1px solid black;color:black;background-color:#BBBBBB'><center>{label}</center></th>"
                else:
                    if sum([inst.val(i) for i in range(1, par)]) == 0:
                        s += f"""<th style='border:1px solid black;color:black;background-color:#BBBBBB;' rowspan = '{offset[par]}'>
            <center>{label}</center></th>"""
            for j in range(pot.variable(0).domainSize()):
                s += _mkCell(pot.get(inst))
                inst.inc()
            s += "</tr>"
            html.append(s)

    html.append("</table>")

    if asString:
        return "\n".join(html)
    else:
        return IPython.display.HTML("".join(html))