mixle.inference.causal module

do – interventions on a learned heterogeneous Bayesian network (graph-surgery semantics).

The causality front door over mixle.inference.bayesian_network.learn_bayesian_network(). An intervention do(net, {field: value}) CLAMPS the intervened fields during ancestral sampling — their own factors (and hence their parents) are cut out of the generation, which is exactly Pearl’s graph surgery — and everything downstream flows through the fitted conditional factors:

net = learn_bayesian_network(records)
world = do(net, {0: 2.0})                    # the world where field 0 is SET to 2.0
world.sample(1000)                            # interventional draws
world.expectation(2)                          # E[field 2 | do(field 0 = 2.0)]
average_causal_effect(net, 0, 2.0, 0.0, outcome=2)   # E[Y|do(a)] - E[Y|do(b)]

The signature difference from conditioning: intervening on a DOWNSTREAM field leaves its ancestors at their marginal law (observing it would have shifted them). do gives interventional distributions; counterfactuals (abduction over exogenous noise) are a separate, harder rung and are deliberately not claimed here.

class InterventionalNetwork(net, interventions)[source]

Bases: object

A Bayesian network under do(...): sample and summarize the post-intervention world.

Parameters:
  • net (Any)

  • interventions (dict[int, Any])

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 E[field | do(...)] for a numeric field.

Parameters:
Return type:

float

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

Interventional marginal of a discrete field as {value: probability}.

Parameters:
Return type:

dict[Any, float]

do(net, interventions)[source]

Return the network under Pearl’s do operator (see module docstring).

Parameters:
Return type:

InterventionalNetwork

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

E[outcome | do(treatment=a)] - E[outcome | do(treatment=b)] (numeric outcome).

Parameters:
Return type:

float

counterfactual(net, observed, interventions)[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.

Honest 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:
Return type:

tuple