Functional Bayesian Network

class pgmpy.models.FunctionalBayesianNetwork.FunctionalBayesianNetwork(ebunch: List[Tuple[Hashable, Hashable]] | None = None, latents: Set[Hashable] = {}, lavaan_str: str | None = None, dagitty_str: str | None = None)[source]

Class for representing Functional Bayesian Network.

Functional Bayesian Networks allow for representation of any probability distribution using CPDs in functional form (Functional CPD). Functional CPDs return a pyro.distribution object allowing for flexible representation of any distribution.

add_cpds(*cpds: FunctionalCPD) None[source]

Adds FunctionalCPDs to the Bayesian Network.

Parameters:

cpds (instances of FunctionalCPD) – List of FunctionalCPDs which will be associated with the model

Examples

>>> from pgmpy.factors.hybrid import FunctionalCPD
>>> from pgmpy.models import FunctionalBayesianNetwork
>>> import pyro.distributions as dist
>>> import numpy as np
>>> model = FunctionalBayesianNetwork([("x1", "x2"), ("x2", "x3")])
>>> cpd1 = FunctionalCPD("x1", lambda _: dist.Normal(0, 1))
>>> cpd2 = FunctionalCPD(
...     "x2", lambda parent: dist.Normal(parent["x1"] + 2.0, 1), parents=["x1"]
... )
>>> cpd3 = FunctionalCPD(
...     "x3", lambda parent: dist.Normal(parent["x2"] + 0.3, 2), parents=["x2"]
... )
>>> model.add_cpds(cpd1, cpd2, cpd3)
check_model() bool[source]

Checks the model for various errors. This method checks for the following error -

  • Checks if the CPDs associated with nodes are consistent with their parents.

Returns:

check – True if all the checks pass.

Return type:

boolean

fit(data: ~pandas.core.frame.DataFrame, method: str = 'SVI', optimizer: ~pyro.optim.optim.PyroOptim = <pyro.optim.optim.PyroOptim object>, prior_fn: ~typing.Callable | None = None, num_steps: int = 1000, seed: int | None = None, nuts_kwargs: ~typing.Dict | None = None, mcmc_kwargs: ~typing.Dict | None = None) Dict[str, Any][source]

Fit the Bayesian network to data using Pyro’s stochastic variational inference.

Parameters:
  • data (pandas.DataFrame) – DataFrame with observations of variables.

  • method (str (default: "SVI")) – Fitting method to use. Currently supports “SVI” and “MCMC”.

  • optimizer (Instance of pyro optimizer (default: pyro.optim.Adam({"lr": 1e-2}))) – Only used if method is “SVI”. The optimizer to use for optimization.

  • prior_fn (function) – Only used if method is “MCMC”. A function that returns a dictionary of pyro distributions for each parameter in the model.

  • num_steps (int (default: 100)) – Number of optimization steps. For SVI it is the num_steps argument for pyro.infer.SVI. For MCMC, it is the num_samples argument for pyro.infer.MCMC.

  • seed (int (default: None)) – Seed value for random number generator.

  • nuts_kwargs (dict (default: None)) – Only used if method is “MCMC”. Additional arguments to pass to pyro.infer.NUTS.

  • mcmc_kwargs (dict (default: None)) – Only used if method is “MCMC”. Additional arguments to pass to pyro.infer.MCMC.

Returns:

dict – If method is “MCMC”, returns a dictionary of posterior samples for each parameter.

Return type:

If method is “SVI”, returns a dictionary of parameter values.

Examples

>>> from pgmpy.factors.hybrid import FunctionalCPD
>>> from pgmpy.models import FunctionalBayesianNetwork
>>> import numpy as np
>>> import pyro.distributions as dist
>>> model = FunctionalBayesianNetwork([("x1", "x2")])
>>> x1 = np.random.normal(0.2, 0.8, size=10000)
>>> x2 = np.random.normal(0.6 + x1, 1)
>>> data = pd.DataFrame({"x1": x1, "x2": x2})
>>> def x1_fn(parents):
...     mu = pyro.param("x1_mu", torch.tensor(1.0))
...     sigma = pyro.param(
...         "x1_sigma", torch.tensor(1.0), constraint=constraints.positive
...     )
...     return dist.Normal(mu, sigma)
...
>>> def x2_fn(parents):
...     intercept = pyro.param("x2_inter", torch.tensor(1.0))
...     sigma = pyro.param(
...         "x2_sigma", torch.tensor(1.0), constraint=constraints.positive
...     )
...     return dist.Normal(intercept + parents["x1"], sigma)
...
>>> cpd1 = FunctionalCPD("x1", fn=x1_prior)
>>> cpd2 = FunctionalCPD("x2", fn=x2_prior, parents=["x1"])
>>> model.add_cpds(cpd1, cpd2)
>>> params = model.fit(data, method="SVI", num_steps=100)
>>> print(params)
>>> def prior_fn():
...     return {
...         "x1_mu": dist.Uniform(0, 1),
...         "x1_sigma": dist.HalfNormal(5),
...         "x2_inter": dist.Normal(1.0),
...         "x2_sigma": dist.HalfNormal(1),
...     }
...
>>> def x1_fn(priors, parents):
...     return dist.Normal(priors["x1_mu"], priors["x1_sigma"])
...
>>> def x2_fn(priors, parents):
...     return dist.Normal(
...         priors["x2_inter"] + parent["x1"], priors["x2_sigma"]
...     )
...
>>> cpd1 = FunctionalCPD("x1", fn=x1_fn)
>>> cpd2 = FunctionalCPD("x2", fn=x2_fn, parents=["x1"])
>>> model.add_cpds(cpd1, cpd2)
>>> params = model.fit(data, method="MCMC", prior_fn=prior_fn, num_steps=100)
>>> print(params["x1_mu"].mean(), params["x1_std"].mean())
get_cpds(node: Any | None = None) List[FunctionalCPD] | FunctionalCPD[source]

Returns the cpd of the node. If node is not specified returns all the CPDs that have been added till now to the graph

Parameter

node: any hashable python object (optional)

The node whose CPD we want. If node not specified returns all the CPDs added to the model.

rtype:

A list of Functional CPDs.

Examples

>>> from pgmpy.factors.hybrid import FunctionalCPD
>>> from pgmpy.models import FunctionalBayesianNetwork
>>> import numpy as np
>>> import pyro.distributions as dist
>>> model = FunctionalBayesianNetwork([("x1", "x2"), ("x2", "x3")])
>>> cpd1 = FunctionalCPD("x1", lambda _: dist.Normal(0, 1))
>>> cpd2 = FunctionalCPD(
...     "x2", lambda parent: dist.Normal(parent["x1"] + 2.0, 1), parents=["x1"]
... )
>>> cpd3 = FunctionalCPD(
...     "x3", lambda parent: dist.Normal(parent["x2"] + 0.3, 2), parents=["x2"]
... )
>>> model.add_cpds(cpd1, cpd2, cpd3)
>>> model.get_cpds()
remove_cpds(*cpds: FunctionalCPD) None[source]

Removes the given cpds from the model.

Parameters:

*cpds (FunctionalCPD objects) – A list of FunctionalCPD objects that need to be removed from the model.

Examples

>>> from pgmpy.factors.hybrid import FunctionalCPD
>>> from pgmpy.models import FunctionalBayesianNetwork
>>> import numpy as np
>>> import pyro.distributions as dist
>>> model = FunctionalBayesianNetwork([("x1", "x2"), ("x2", "x3")])
>>> cpd1 = FunctionalCPD("x1", lambda _: dist.Normal(0, 1))
>>> cpd2 = FunctionalCPD(
...     "x2", lambda parent: dist.Normal(parent["x1"] + 2.0, 1), parents=["x1"]
... )
>>> cpd3 = FunctionalCPD(
...     "x3", lambda parent: dist.Normal(parent["x2"] + 0.3, 2), parents=["x2"]
... )
>>> model.add_cpds(cpd1, cpd2, cpd3)
>>> for cpd in model.get_cpds():
...     print(cpd)
...
>>> model.remove_cpds(cpd2, cpd3)
>>> for cpd in model.get_cpds():
...     print(cpd)
...
simulate(n_samples: int = 1000, seed: int | None = None) DataFrame[source]

Simulate samples from the model.

Parameters:
  • n_samples (int, optional (default: 1000)) – Number of samples to generate

  • seed (int, optional) – The seed value for the random number generator.

Returns:

Simulated samples with columns corresponding to network variables

Return type:

pandas.DataFrame

Examples

>>> from pgmpy.factors.hybrid import FunctionalCPD
>>> from pgmpy.models import FunctionalBayesianNetwork
>>> import numpy as np
>>> import pyro.distributions as dist
>>> model = FunctionalBayesianNetwork([("x1", "x2"), ("x2", "x3")])
>>> cpd1 = FunctionalCPD("x1", lambda _: dist.Normal(0, 1))
>>> cpd2 = FunctionalCPD(
...     "x2", lambda parent: dist.Normal(parent["x1"] + 2.0, 1), parents=["x1"]
... )
>>> cpd3 = FunctionalCPD(
...     "x3", lambda parent: dist.Normal(parent["x2"] + 0.3, 2), parents=["x2"]
... )
>>> model.add_cpds(cpd1, cpd2, cpd3)
>>> model.simulate(n_samples=1000)