Interventions

Given a bn_testing.models.Model object, modifications, like interventions, of the terms and source distributions can be applied and data from the modified model can be sampled.

The goal of these modificiations may be:

  • Generating interventional data.

  • Simulation of changed behavior in the system under study.

Apply Modifications

Assume that model represents a additive structural equation model with DAG \(G\) and equations

\[X_i = f_i(X_j, j\in\text{pa}_G(i)) + \epsilon_i\]

Generally, modifications are changes of the transformations \(f_i\) and the additive noise \(\epsilon_i\).

Assume we have given a model having a node Assume a node \(X_3\) with parents \(X_1\) and \(X_2\) and \(f_3(x_1, x_2)=x_1^2\cdot x_2+x_1\) and \(\epsilon_3\sim\mathcal{N}(0, 1)\). That means,

\[X_3 = X_1^2\cdot X_2+X_1+\epsilon_3\]

Randomized modification

One way to change the construction of \(X_3\) from its parents is by randomly generating a new Term and additive noise object using the Conditional used when instantiating the model. This can be done as folllows:

model.modify_node(node='X3')

The same works for source nodes, where a new source distribution is generated using the make_source() method of the condidtional object.

Note

The model modification is done inplace, that means, an applied modification cannot be undone.

Alternatively, the new term can also be generated using another conditional:

from bn_testing.conditionals import LinearConditional

model.modify_node(node='X3', conditionals=LinearConditional())

Note

A modification not only changes the term, but also the additive noise!

Controlled modification

Inner nodes

Instead of randomly generating a new term for \(X_3\) is constructed, we can also directly set the new term:

from bn_testing.terms import Linear
import pymc as pm

model.modify_inner_node(
   node='X3',
   term=Linear(['X1', 'X2'], [0.5, -0.1],
   noise=pm.Beta.normal(alpha=0.2, beta=0.1),
 )

Note that if either the noise or the term are omitted, a respective object is generated using the Conditional given when instantiating the model.

Source nodes

If \(X1\) is a source node, its distribution can be replaced with another distribution as follows:

import pymc as pm

model.modify_source_node(
   node='X3',
   distribution=pm.Beta.dist(alpha=0.9, beta=0.1),
)

Computing causal effects

A special case of a modification is when a random variable is set to a constant value. Typically, this is done because the average causal effect of \(X_i\) having value \(k\) onto another variable, like \(X_j\) should be determined. This is

\[\mathbb{E}[X_j|X_i=k] - \mathbb{E}[X_j]\]

In our example above, setting \(X_1=2\) permantently can be archived with:

from bn_testing.conditionals import ConstantConditional
import pymc as pm

model.modify_node(
   node='X1',
   conditionals=ConstantConditional(2)
 )

The average causal effect for this setting can be computed using the shortcut compute_average_causal_effect():

model.compute_average_causal_effect(
   node_from='X1',
   node_onto='X3',
   value=2,
)

Note

The calculation of the expected values used in the formula of the average causal effect is done empirically by sampling from the model with and without modification. The number of samples used can be changed by setting the paramater n.