Extending pgmpy

It’s really easy to extend pgmpy to quickly prototype your ideas. pgmpy has a base abstract class for most of main functionalities like: BaseInference for inference, BaseFactor for model parameters, BaseEstimators for parameter and model learning. For adding a new feature to pgmpy we just need to implement a new class inheriting one of these base classes and then we can use the new class with other functionality of pgmpy.

In this example we will see how to write a new inference algorithm. We will take the example of a very simple algorithm in which we will multiply all the factors/CPD of the network and marginalize over variable to get the desired query.

[1]:
# A simple Exact inference algorithm

import itertools

from pgmpy.inference.base import Inference
from pgmpy.factors import factor_product


class SimpleInference(Inference):
    # By inheriting Inference we can use self.model, self.factors and self.cardinality in our class
    def query(self, var, evidence):
        # self.factors is a dict of the form of {node: [factors_involving_node]}
        factors_list = set(itertools.chain(*self.factors.values()))
        product = factor_product(*factors_list)
        reduced_prod = product.reduce(evidence, inplace=False)
        reduced_prod.normalize()
        var_to_marg = (
            set(self.model.nodes()) - set(var) - set([state[0] for state in evidence])
        )
        marg_prod = reduced_prod.marginalize(var_to_marg, inplace=False)
        return marg_prod
[2]:
# Defining a model

from pgmpy.models import BayesianModel
from pgmpy.factors.discrete import TabularCPD

model = BayesianModel([("A", "J"), ("R", "J"), ("J", "Q"), ("J", "L"), ("G", "L")])
cpd_a = TabularCPD("A", 2, values=[[0.2], [0.8]])
cpd_r = TabularCPD("R", 2, values=[[0.4], [0.6]])
cpd_j = TabularCPD(
    "J",
    2,
    values=[[0.9, 0.6, 0.7, 0.1], [0.1, 0.4, 0.3, 0.9]],
    evidence=["A", "R"],
    evidence_card=[2, 2],
)
cpd_q = TabularCPD(
    "Q", 2, values=[[0.9, 0.2], [0.1, 0.8]], evidence=["J"], evidence_card=[2]
)
cpd_l = TabularCPD(
    "L",
    2,
    values=[[0.9, 0.45, 0.8, 0.1], [0.1, 0.55, 0.2, 0.9]],
    evidence=["J", "G"],
    evidence_card=[2, 2],
)
cpd_g = TabularCPD("G", 2, values=[[0.6], [0.4]])

model.add_cpds(cpd_a, cpd_r, cpd_j, cpd_q, cpd_l, cpd_g)
[3]:
# Doing inference with our SimpleInference

infer = SimpleInference(model)
a = infer.query(var=["A"], evidence=[("J", 0), ("R", 1)])
[4]:
print(a)
+-----+----------+
| A   |   phi(A) |
|-----+----------|
| A_0 |   0.6000 |
| A_1 |   0.4000 |
+-----+----------+
[5]:
# Comparing the results with Variable Elimination Algorithm

from pgmpy.inference import VariableElimination

infer = VariableElimination(model)
result = infer.query(["A"], evidence={"J": 0, "R": 1})
print(result["A"])
+-----+----------+
| A   |   phi(A) |
|-----+----------|
| A_0 |   0.6000 |
| A_1 |   0.4000 |
+-----+----------+

Similarly we can also create new classes for Factor or CPDs and add them to networks and do inference over it or can write a new estimator class.