mixle.inference.causal module

Structural interventions on a heterogeneous Bayesian network.

do(net, ...) performs graph surgery on the supplied directed factorization. That operation is useful for simulation, but observational structure learning does not establish that an arrow is a causal direction. Consequently an unaccompanied do result is explicitly an unidentified structural scenario, not an estimate of an intervention in the data-generating world.

Callers that have a domain-asserted causal graph or an identified design can attach a CausalIdentification receipt. APIs whose names make a causal claim, such as average_causal_effect() and counterfactual(), require that receipt.

class CausalIdentification(graph_source, estimand, evidence, assumptions, exchangeability, positivity, consistency, no_interference, structural_counterfactuals=False)[source]

Bases: object

Auditable assumptions under which a structural contrast is interpreted causally.

This record does not prove its assertions. It makes the assertions reviewable and prevents a learned observational arrow from silently becoming a causal result. graph_source must say how the direction was established, and evidence should identify the design, protocol, or domain artifact supporting the claim.

Parameters:
property identified: bool

Whether all common treatment-effect identification assumptions were declared.

classmethod domain_asserted(evidence, *, estimand='average treatment effect', assumptions=('causal DAG supplied independently of fitted associations',), structural_counterfactuals=False)[source]

Construct a receipt for a domain-asserted causal DAG.

This convenience constructor records that the caller accepts the standard identification assumptions. Applications needing qualified assumptions should instantiate the dataclass directly rather than use this shorthand.

Parameters:
  • evidence (str)

  • estimand (str)

  • assumptions (tuple[str, ...])

  • structural_counterfactuals (bool)

Return type:

CausalIdentification

to_dict()[source]

Return a serializable copy of the receipt.

Return type:

dict[str, Any]

class InterventionalNetwork(net, interventions, identification=None)[source]

Bases: object

A graph-surgery scenario with an explicit causal-identification status.

Parameters:
  • net (Any)

  • interventions (dict[int, Any])

  • identification (CausalIdentification | None)

sample(size=1, *, seed=None)[source]

Ancestral sampling with the intervened fields clamped (their factors are never consulted).

Parameters:
  • size (int)

  • seed (int | None)

Return type:

list[tuple]

expectation(field, *, n=4000, seed=0)[source]

Monte-Carlo expectation in this structural scenario.

Parameters:
Return type:

float

distribution(field, *, n=4000, seed=0)[source]

Structural-scenario marginal of a discrete field as {value: probability}.

Parameters:
Return type:

dict[Any, float]

do(net, interventions, *, identification=None)[source]

Apply graph surgery, labeling the result unidentified unless a valid receipt is supplied.

Parameters:
  • net (Any)

  • interventions (dict[int, Any])

  • identification (CausalIdentification | None)

Return type:

InterventionalNetwork

average_causal_effect(net, treatment, a, b, outcome, *, identification=None, n=4000, seed=0)[source]

Return an identified average causal contrast under an explicit assumption receipt.

Parameters:
  • net (Any)

  • treatment (int)

  • a (Any)

  • b (Any)

  • outcome (int)

  • identification (CausalIdentification | None)

  • n (int)

  • seed (int)

Return type:

float

counterfactual(net, observed, interventions, *, identification=None)[source]

What this observed record would have been under the intervention (abduction-action-prediction).

Per Pearl’s three steps, walked in topological order:

  • abduction – a linear-Gaussian field’s exogenous noise is point-identified from the row: its residual eps = observed - coef @ parents_observed;

  • action – intervened fields take their do values;

  • prediction – the same residual replays through the counterfactual parents: cf = coef @ parents_cf + eps.

An identification receipt with structural_counterfactuals=True is required. Boundaries: (1) a field that is not linear-Gaussian keeps its observed value only while its parents are unchanged under the intervention (that much IS identified); if its parents change, its exogenous noise cannot be recovered from one observation and this raises — use average_causal_effect() for the population answer instead of a guessed individual one. (2) The counterfactual is relative to the network’s DAG as given: purely observational structure learning cannot orient Markov-equivalent edges (x -> y and y -> x fit equally well), so if the causal direction matters, assert it from domain knowledge rather than trusting the learned arrow.

Parameters:
  • net (Any)

  • observed (tuple)

  • interventions (dict[int, Any])

  • identification (CausalIdentification | None)

Return type:

tuple