mixle.inference.bayesian_network module

Heterogeneous Bayesian network learning – a directed graph over mixed-type fields with parametric edges.

This deepens mixle.inference.structure (a single-parent forest with quantile-binned continuous parents) into the real thing: a DAG where a field may have several parents, and continuous dependence is a parametric conditional, not a binning. A continuous child is a conditional-linear-Gaussian node – child ~ N(w . [continuous parents, one-hot(discrete parents)] + b, sigma^2) – so a real driven by two reals, or by a category and a real, is modeled exactly and cheaply (closed-form least squares). A discrete/count child with all-discrete parents conditions on their joint configuration (marginal backoff for unseen configs); with a continuous driver it becomes a GLM node (logistic / Poisson log-link / multinomial softmax), so category<->real dependence is representable in BOTH orientations and BIC picks the cheaper one.

learn_bayesian_network grows each node’s parent set greedily by description-length gain, up to max_parents, keeping the graph acyclic. The result scores, samples, and composes like any mixle distribution – the moat: automatic discovery and fitting of a heterogeneous graphical model across arbitrary families, which no mainstream tool does (Stan/PyMC: you write it; sklearn/pomegranate: independence; bnlearn/pgmpy: discrete-or-Gaussian).

class HeterogeneousBayesianNetwork(factors)[source]

Bases: object

A DAG joint over a heterogeneous record: log p(x) = sum_i log P(x_i | parents(i)) over fitted factors.

Parameters:

factors (Sequence[Any])

edges()[source]
Return type:

list[tuple[int, int]]

log_density(x)[source]
Parameters:

x (tuple)

Return type:

float

seq_log_density(encoded)[source]
Parameters:

encoded (Any)

Return type:

ndarray

dist_to_encoder()[source]
Return type:

Any

sampler(seed=None)[source]
Parameters:

seed (int | None)

Return type:

Any

class MixtureOfBayesianNetworks(components, weights)[source]

Bases: object

A latent mixture whose components each carry their own heterogeneous DAG (regression edges and all).

The fullest form of the moat: it discovers the clustering and each cluster’s cross-field graphical model, so the slope of one field on another (or the whole dependency graph) can differ between clusters – which a single network cannot represent. log p(x) = logsumexp_k(log w_k + log p_k(x)) over HeterogeneousBayesianNetwork components. Fit by learn_mixture_bayesian_network().

Parameters:
  • components (Sequence[HeterogeneousBayesianNetwork])

  • weights (Sequence[float])

log_density(x)[source]
Parameters:

x (tuple)

Return type:

float

seq_log_density(encoded)[source]
Parameters:

encoded (Any)

Return type:

ndarray

dist_to_encoder()[source]
Return type:

Any

responsibilities(data)[source]
Parameters:

data (Sequence[tuple])

Return type:

ndarray

sampler(seed=None)[source]
Parameters:

seed (int | None)

Return type:

Any

property n_components: int
learn_mixture_bayesian_network(data, n_components, *, restarts=3, max_iter=12, seed=0, max_parents=2, min_gain=0.0, max_its=30, em='hard')[source]

Fit a MixtureOfBayesianNetworks by EM – discover clusters AND each cluster’s DAG.

em="hard" (default): each iteration re-learns a network per cluster on its assigned points and reassigns every record to its most-probable cluster, until assignments stabilize. em="soft": proper EM – every record contributes to EVERY cluster with its responsibility, each component’s structure search and factor fits are responsibility-weighted (learn_bayesian_network(weights=)), and convergence is on the observed-data log-likelihood; boundary points shape both clusters instead of whipsawing between them. Both run from the same restarts (k-means-seeded + random); best final log-likelihood wins. Starved clusters are re-seeded (hard) / responsibility-floored (soft).

Parameters:
Return type:

MixtureOfBayesianNetworks

bayesian_network_bic(model, data)[source]

BIC (lower is better) for a fitted network or mixture: -2 LL + n_params log n.

Parameters:
Return type:

float

select_mixture_components(data, k_values=(1, 2, 3, 4), *, em='hard', seed=0, **kwargs)[source]

Model selection over the number of clusters by BIC.

Fits learn_bayesian_network() for k=1 and learn_mixture_bayesian_network() for each larger k, scores each by bayesian_network_bic(), and returns (best_model, report) where report = {"k": chosen, "bic": {k: score, ...}}. BIC’s n_params log n penalty is what stops a mixture of per-cluster DAGs from always preferring more clusters.

Parameters:
Return type:

tuple[Any, dict[str, Any]]

learn_bayesian_network(data, *, max_parents=2, min_gain=0.0, max_its=30, weights=None)[source]

Discover a heterogeneous DAG for data and return the fitted network.

Each field greedily gains up to max_parents parents by BIC-penalized conditional likelihood, keeping the graph acyclic. Continuous children become conditional-linear-Gaussian factors (regression on continuous + one-hot discrete parents); discrete/count children condition on the joint config of their discrete parents, or become GLM nodes when a driver is continuous. With weights (soft-EM responsibilities), every factor fit and the BIC search itself are responsibility-weighted, with effective sample size sum(weights).

Parameters:
Return type:

HeterogeneousBayesianNetwork