CausalEngine Tutorial

Install the public PyPI distribution CausalScience before starting:

python -m pip install CausalScience
python -m causalscience.demo

The published release is 0.1.0a13; Python examples use import causalscience. See the installation guide. Then open one Python, IPython, or Jupyter session and run the cells in order: later cells reuse variables created earlier.

The computational walkthrough and recorded outputs retain their CausalEngine 0.1.0a13 baseline at b00fb28d, originally introduced at bd14e4dc. Installation and public imports have been updated without rewriting those historical output records.

Use Copy code to copy an input cell. The outputs below are from a recorded run of b00fb28d; this page does not run Python. Estimates from other environments or samples can differ.

Contents

Run six public estimation APIs

1. Setup

Import the installed package and set the data and estimator seeds. Keep this session open for every following cell.

In [1]

import numpy as np
import pandas as pd
import causalscience as ce
from causalscience.indexed_graph import Slot, ObsKernel, Product, Sum

assert ce.__version__ == "0.1.0a13"
rng = np.random.default_rng(27)
n = 2400
common = dict(seed=0, num_boost_round=30, params={"nthread": 1, "max_depth": 4})

print("CausalEngine:", ce.__version__)
print("NumPy:", np.__version__, "pandas:", pd.__version__)
print("Data seed: 27; rows per example:", n)
print("Estimator seed:", common["seed"], "boosting rounds:", common["num_boost_round"])

Recorded output

CausalEngine: 0.1.0a13
NumPy: 2.4.6 pandas: 3.1.0
Data seed: 27; rows per example: 2400
Estimator seed: 0 boosting rounds: 30

The version check confirms which package is loaded. The seeds and settings make this recorded example reproducible in the listed environment.

2. Back-door (BD) mean

Estimate the mean outcome under A=1. In this synthetic model, Z and C are declared to be a valid back-door adjustment set; that assumption is supplied by the example.

In [2]

# Back-door mean: baseline Z and C suffice for adjustment in this SCM.
z = rng.binomial(1, .5, n)
c = rng.binomial(1, .2 + .5*z)
a = rng.binomial(1, .1 + .3*c + .2*z)
y = rng.binomial(1, .05 + .15*c + .1*z + a*(.2 + .2*z))
bd_data = pd.DataFrame(dict(Z=z, C=c, A=a, Y=y))
bd_schema = {v: "binary" for v in bd_data}
bd_options = dict(treatment="A", outcome="Y", adjustment=["Z", "C"], schema=bd_schema)
bd = ce.estimate_bd(bd_data, action=1, **bd_options, **common)

print("Status:", bd["status"], "/", bd["estimation_status"])
print("Interventional mean (A=1):", bd["estimate"])

Recorded output

Status: ESTIMATED / ACCEPT
Interventional mean (A=1): [0.46923811]

The returned value is an interventional mean. The ground truth interventional mean for this synthetic model is 0.4675.

3. Sequential back-door (SBD) mean

Estimate the mean when both actions are set to 1. The variable L is measured after A0 and before A1, so its position in the sequence matters.

In [3]

# Sequential back-door: new blocks, in time order; L depends on A0.
c = rng.binomial(1, .5, n)
a0 = rng.binomial(1, .2 + .4*c)
l = rng.binomial(1, .1 + .2*c + .5*a0)
a1 = rng.binomial(1, .1 + .1*c + .6*l + .1*a0)
y = rng.binomial(1, .05 + .15*c + .3*l + .1*a0 + .25*a1)
sbd_data = pd.DataFrame(dict(C=c, A0=a0, L=l, A1=a1, Y=y))
sbd = ce.estimate_sbd(sbd_data, treatments=["A0", "A1"], outcome="Y",
    covariates=[["C"], ["L"]], actions={"A0": 1, "A1": 1},
    schema={v: "binary" for v in sbd_data}, **common)

print("Status:", sbd["status"], "/", sbd["estimation_status"])
print("Interventional mean (A0=1, A1=1):", sbd["estimate"])

Recorded output

Status: ESTIMATED / ACCEPT
Interventional mean (A0=1, A1=1): [0.69158194]

This value is the mean under the two-action regime. The ground truth interventional mean for this synthetic model is 0.685.

4. Front-door (FD) mean

Estimate the mean under A=1 using the conditional Front-door structure. C is the baseline covariate and M the mediator. U is used only to generate the example: it is not included in fd_data.

In [4]

# Conditional front-door: C enters every conditional and the outer average.
c = rng.binomial(1, .5, n)
u = rng.binomial(1, .5, n)
a = rng.binomial(1, .1 + .3*c + .4*u)
m = rng.binomial(1, .1 + .5*a + .25*c)
y = rng.binomial(1, .05 + .4*m + .25*c + .2*u)
fd_data = pd.DataFrame(dict(C=c, A=a, M=m, Y=y))
fd = ce.estimate_fd(fd_data, treatment="A", mediators=["M"], outcome="Y",
    covariates=["C"], action=1, schema={v: "binary" for v in fd_data}, **common)

print("Status:", fd["status"], "/", fd["estimation_status"])
print("Interventional mean (A=1):", fd["estimate"])

Recorded output

Status: ESTIMATED / ACCEPT
Interventional mean (A=1): [0.5759384]

The ground truth interventional mean for this synthetic model is 0.565. A successful numerical status does not verify the causal assumptions.

5. Unified Covariate Adjustment (UCA) mean

Supply the normalized expression explicitly. Here it represents the same functional as the BD cell, so the assertion checks that their estimates agree.

In [5]

# UCA: explicitly supply a normalized expression, with no graph object.
Z, C, A, Y = map(Slot, ("Z", "C", "A", "Y"))
formula = Sum((Z, C), Product((
    ObsKernel((("Z", Z), ("C", C))),
    ObsKernel((("Y", Y),), (("A", A), ("Z", Z), ("C", C))),
)))
uca = ce.estimate_uca(bd_data, formula=formula, actions={"A": 1}, outcome="Y",
    schema=bd_schema, **common)
np.testing.assert_array_equal(uca["estimate"], bd["estimate"])

print("Status:", uca["status"], "/", uca["estimation_status"])
print("Interventional mean (A=1):", uca["estimate"])

Recorded output

Status: ESTIMATED / ACCEPT
Interventional mean (A=1): [0.46923811]

The equality assertion passes for the explicitly supplied functional. This call does not discover or verify a graph.

6. Average Treatment Effect (ATE)

Request a contrast: the mean under A=1 minus the mean under A=0. This is an effect, rather than the single-intervention means returned by the preceding cells.

In [6]

# ATE: paired action 1 minus 0, retaining covariance of the two scores.
ate = ce.estimate_ate(bd_data, estimator="bd", actions={"A": 1},
    reference_actions={"A": 0}, **bd_options, **common)

print("Status:", ate["status"], "/", ate["estimation_status"])
print("Paired ATE (A=1 minus A=0):", ate["estimate"])

Recorded output

Status: ESTIMATED / ACCEPT
Paired ATE (A=1 minus A=0): [0.31323824]

The ground truth ATE for this synthetic model is 0.3. The finite-data estimate need not equal it exactly.

7. Conditional Average Treatment Effect (CATE)

Predict the effect for the two baseline groups Z=0 and Z=1. The two rows from model.predict are those conditional predictions; the separately printed inherited estimate is a marginal paired ATE summary.

In [7]

# CATE: regress paired pseudo-outcomes on the baseline modifier Z.
cate = ce.estimate_cate(bd_data, estimator="bd", modifiers=["Z"],
    actions={"A": 1}, reference_actions={"A": 0}, **bd_options, **common,
    cate_num_boost_round=30, cate_params={"nthread": 1, "max_depth": 4})

print("Status:", cate["status"], "/", cate["estimation_status"])
print("Marginal paired ATE summary (not a conditional CATE value):", cate["estimate"])

for name, result in dict(BD=bd, SBD=sbd, FD=fd, UCA=uca, ATE=ate, CATE=cate).items():
    assert result["estimation_status"] == "ACCEPT", result.get("reason")
    assert result["graph_verification"] == "not_run"
    assert result["causal_interval"] is None
print("Conditional CATE predictions at Z=0,1:", cate["model"].predict(pd.DataFrame({"Z": [0, 1]})))

Recorded output

Status: ESTIMATED / ACCEPT
Marginal paired ATE summary (not a conditional CATE value): [0.31323824]
Conditional CATE predictions at Z=0,1: [[0.21757159]
 [0.41964203]]

The first prediction is for Z=0 and the second for Z=1. The ground truth CATE values for these groups are 0.2 and 0.4.

Recorded output environment

Rerun October 27, 2026. Python 3.11.14 · causalengine 0.1.0a13 · numpy 2.4.6 · pandas 3.1.0 · xgboost 3.2.0 · scipy 1.17.1 · scikit-learn 1.9.1 · osqp 1.1.3. Package source: b00fb28d. These website cells are adapted from the upstream walkthrough. Their computational AST, assertions and prediction call match the source; code and outputs were checked together against this installed main revision.

Interpret the result

BD, SBD, FD and UCA above return interventional means for the requested fixed actions. They are not automatically treatment-effect contrasts. The independent SCM means are .4675 for BD/UCA, .685 for SBD, and .565 for FD. The first SCM has ATE .3 and CATE .2 at Z=0, .4 at Z=1. Finite-data estimates need not equal these truths; the assertions check executable contracts, not accuracy.

ESTIMATED/ACCEPT here means the supplied functional was numerically executed. Graph-free calls record user-assumed identification; they run neither graph discovery nor FMC/MC identification. Use identify_query and fit_query_mean when a graph should supply the identifying formula. The default backward learner is XGBoost and weights use normalized minimum-L2 balance in the supported feature class. Positivity, valid causal assumptions and nuisance learning remain required.

CATE requires baseline modifiers and a supported baseline-anchor program with shared source-row evaluation. It uses a separate pseudo-outcome training and validation split. model.predict returns conditional effects. Inherited estimate, standard_error and conditional_score_interval remain marginal paired ATE summaries; validation_pseudo_mse measures prediction of noisy scores, not true CATE error. No automatic causal or pointwise CATE interval is returned.

Continuous modifiers are supported with explicit schema, while continuous fixed-point interventions remain unsupported by this default score route. Malformed roles/schema raise ValueError; unsupported numerical schedules or support return a reason. See the full API and boundaries.

Continue

Current source tutorials

The latest tutorial index separates maintained examples, checkout tools, benchmarks, and historical notebooks. From the current source checkout with dependencies installed, run:

PYTHONPATH=src python -B -m tutorials.demo_scm
PYTHONPATH=src python -B -m tutorials.demo_identification
PYTHONPATH=src python -B -m tutorials.demo_sbd

Use the Document’s SCM generation and truth section for observed-X conditional average potential outcomes (CAPOs) and CSCM_capo, including continuous, binary and count outcomes. CSCM_capo keeps the X/A mechanisms and changes the outcome parameterization; its direct truth query is fast, while generation can require numerical conditional centering. The Wiki implementation map locates the code.

The installed English graph-identification tool is python -m causalscience.graph_tool. The checkout’s Korean SCM-generation editor is separate. Historical notebooks keep their retired API and are reference material; use the maintained walkthrough above for current estimation APIs.

Installation and public examples updated for CausalScience 0.1.0a13. Base documentation: CausalEngine main · b00fb28d on October 27, 2026. Historical validation and benchmark sections retain their own original implementation revisions.

Upstream documentation source: tutorials/current-workflow.md. Source SHA-256: 0df7e5a824e114c69d90fd092d94d6e585052c6fdf32575bbb706db7ec0dadb6.

Additional tutorial index: tutorials/README.md. Source SHA-256: 90f1794a4d3c17db976bd21af2aebbdaca68fda72a2c662b53e6ecd2540d2098.