Causal Model Definition

Here we will consider the different ways for defining a structural causal model (SCM) in Credici. This can be done by explicitly specifiying all the nodes, arcs and factors in the model, or with the help of the class CausalBuilder.

Explicit Definition

The code snippet shown below shows how to explicitly define a SCM. For this, an object of class StructuralCausalModel is created. Then endogenous and exogenous variable are added to the model by indicating the cardinality. In case of the exogenous ones, the second input parameter should be set to true, which indicates the type of variable. Then the parents are set and finaly the factors are specified, which are basically objects of class BayesianFactor.

StructuralCausalModel model = new StructuralCausalModel();

// define the variables (endogenous and exogenous)
int x1 = model.addVariable(2);
int x2 = model.addVariable(2);
int x3 = model.addVariable(2);
int x4 = model.addVariable(2);

int u1 = model.addVariable(2, true);
int u2 = model.addVariable(4, true);
int u3 = model.addVariable(4, true);
int u4 = model.addVariable(3, true);

// Define the structure
model.addParents(x1, u1);
model.addParents(x2, u2, x1);
model.addParents(x3, u3, x1);
model.addParents(x4, u4, x2, x3);


// define the CPTs of the exogenous variables
BayesianFactor pu1 = new BayesianFactor(model.getDomain(u1), new double[] { .4, .6 });
BayesianFactor pu2 = new BayesianFactor(model.getDomain(u2), new double[] { .07, .9, .03, .0 });
BayesianFactor pu3 = new BayesianFactor(model.getDomain(u3), new double[] { .05, .0, .85, .10 });
BayesianFactor pu4 = new BayesianFactor(model.getDomain(u4), new double[] { .05, .9, .05 });

model.setFactor(u1,pu1);
model.setFactor(u2,pu2);
model.setFactor(u3,pu3);
model.setFactor(u4,pu4);

// Define the CPTs of endogenous variables as deterministic functions
BayesianFactor f1 = EquationBuilder.of(model).fromVector(x1, 0,1);
BayesianFactor f2 = EquationBuilder.of(model).fromVector(x2,0,0,1,1,  0,1,0,1);
BayesianFactor f3 = EquationBuilder.of(model).fromVector(x3,0,0,1,1,  0,1,0,1);
BayesianFactor f4 = EquationBuilder.of(model).fromVector(x4,0,1,1,  0,0,0,  0,0,0, 0,1,1);

model.setFactor(x1,f1);
model.setFactor(x2,f2);
model.setFactor(x3,f3);
model.setFactor(x4,f4);

model.printSummary();

Causal Builder

Additionaly, Credici provides class CausalBuilder at ch.idsia.credici.model.builder for simplifying the code under some settings. We will assume that we have BayesianNetwork specifying the empirical information: empirical DAG and eventually empirical probabilities. For example:

BayesianNetwork bnet = new BayesianNetwork();
int y = bnet.addVariable(2);
int x = bnet.addVariable(2);

The following code shows 4 equivalent ways of building a SCM from such BN under the markovian setting.

// Markovian equationless from BN
StructuralCausalModel m1 = StructuralCausalModel.of(bnet);

// Markovian equationless from DAG and sizes
StructuralCausalModel m2 = StructuralCausalModel.of(bnet.getNetwork(), bnet.getSizes(bnet.getVariables()));

// Markovian equationless from BN
StructuralCausalModel m3 = CausalBuilder.of(bnet).build();

// Markovian equationless from DAG and sizes
StructuralCausalModel m4 = CausalBuilder.of(bnet.getNetwork(), bnet.getSizes(bnet.getVariables())).build();

In the previous cases, factors associated to exogenous variables are empty. Instead, we could build it with some random factors:

// Markovian equationless with random P(U)
StructuralCausalModel m5 =
        CausalBuilder.of(bnet)
                .setFillRandomExogenousFactors(2)
                .build();

// Markovian with random P(U) and equations
StructuralCausalModel m6 =
        CausalBuilder.of(bnet)
                .setFillRandomExogenousFactors(2)
                .setFillRandomEquations(true)
                .build();

Instead of considering the default structural equation, these could be specifyed as follows.

// Markovian case specifying equations and with random exogenous factors

BayesianFactor eqy = EquationBuilder.fromVector(
        Strides.as(y,2), Strides.as(u1,2),
        0,1
);

BayesianFactor eqx = EquationBuilder.fromVector(
        Strides.as(x,2), Strides.as(u2,4),
        0,1,1,0
);

BayesianFactor[] eqs = {eqy, eqx};

StructuralCausalModel m9 = CausalBuilder.of(bnet)
        .setEquations(eqs)
        .setFillRandomExogenousFactors(3)
        .build();

The quasi-markovian case could be also considered by specifying the causal DAG, which should be consistent with the empirical one.

// Quasi Markovian specifying causal DAG with random factors
SparseDirectedAcyclicGraph dag2 = bnet.getNetwork().copy();
int u = dag2.addVariable();
dag2.addLink(u, y);
dag2.addLink(u,x);

StructuralCausalModel m8 =
        CausalBuilder.of(bnet)
                .setCausalDAG(dag2)
                .setExoVarSizes(new int[]{4})
                .setFillRandomEquations(true)
                .build();