A Causal simulator for manufacturing process systems. CausalMan generates synthetic observational and interventional datasets from complex production lines with known ground-truth causal graphs, enabling benchmarking of causal discovery, inference and RCA algorithms.
CausalMan implements an expert-defined SCM of a manufacturing production line. Parts flow through machines and parallel sections, and each path through the line has distinct properties and parameterizations. The whole SCM is specified by symbolic structural equations (SymPy). The simulator returns:
- Observational data — sensor readings from the partially observable production process
- Interventional data — data under hard do-calculus interventions on selected variables
- Path data — per-part routing through parallel sections
- Causal graph — the ground-truth DAG (and optionally its ADMG/MAG projections after marginalizing latent variables)
causal_simulator_to_release/
├── src/ # Installable Python package
│ ├── causalman.py # Main CausalMan simulator class
│ ├── fcm.py # FCM core: DAG construction, sampling, interventions.
│ ├── node.py # Node model definitions
│ ├── sample_batch.py # Per-batch sampling logic
│ ├── graph_plotter.py # Interactive graph visualization (Pyvis)
│ ├── graph_projections.py # ADMG/MAG latent variable projections
│ ├── marginalization.py # Marginalization via R dagitty (optional)
│ ├── col_masking.py # Filter datasets to observable columns
│ ├── FCM_Definitions/ # FCM equation templates for parallel sections
│ ├── line_structure/ # Production line hierarchy (line, section, machine)
│ ├── dataset_objects/ # Pre-computed simulation configurations (~7.6 GB)
│ ├── utils/
│ │ ├── sampling.py # Sequential and parallel batch runners
│ │ ├── graph.py # Graph manipulation and I/O utilities
│ │ ├── data.py # DataFrame processing utilities
│ │ └── equation.py # Sympy equation construction helpers
│ ├── output/ # Generated simulation results
│ ├── example_observational.ipynb
│ └── interventions_example.ipynb
│ ├── causal_inference_data_generation.ipynb # Generate CI benchmark datasets (notebook)
│ ├── rca_data_generation.ipynb # Generate RCA benchmark datasets (notebook)
│ └── generate_causal_inference_data.py # Generate CI benchmark datasets (CLI)
├── pyproject.toml
└── requirements.txt
Requirements: Python 3.9+
Clone the repository and install the package in one step:
git clone <repository-url>
cd causalman
pip install .This installs the causalman package along with all required dependencies. The precomputed dataset objects (~7.6 GB of pickle files) are bundled inside the package and installed automatically — no separate download needed.
Note: Installation copies the full
dataset_objects/directory into your Python environment. Make sure you have at least 8 GB of free disk space before installing.
Optional extras — install only what you need:
pip install ".[graph-layout]" # Graphviz-based graph layouts (pygraphviz)
pip install ".[png-export]" # Export graphs to PNG via headless browser (playwright)
pip install ".[r-dagitty]" # R dagitty marginalization bridge (rpy2)
pip install ".[all]" # Everything aboveAfter installing playwright, run:
playwright install chromiumAfter installing rpy2, ensure R is installed locally with the dagitty package.
Set the R_HOME environment variable to your R installation directory before importing causalman.marginalization:
# Linux / macOS
export R_HOME=/usr/lib/R # typical system R
export R_HOME=/usr/local/lib/R # Homebrew R on macOS
# Windows (PowerShell)
$env:R_HOME = "C:\Program Files\R\R-4.4.2"
# Windows (Command Prompt)
set R_HOME=C:\Program Files\R\R-4.4.2You can also set it in Python before the import:
import os
os.environ["R_HOME"] = "/path/to/R" # must be set before importing rpy2
from causalman.marginalization import marginalize_to_magTo find your R home directory, run R.home() inside an R session.
from causalman import CausalMan
simulator = CausalMan(
name="causalman_small", # dataset variant
seed=42, # random seed
batch_multiplier=1, # controls number of samples per batch
)
obs_df, int_table, path_df, causal_dag = simulator.sample()| Variable | Type | Description |
|---|---|---|
obs_df |
pd.DataFrame |
Observational sensor readings (partially observable) |
int_table |
pd.DataFrame |
Binary indicator table of which variables were intervened on |
path_df |
pd.DataFrame |
Path routing of each part through parallel sections |
causal_dag |
nx.DiGraph |
Ground-truth causal DAG (level 2) |
| Name | Description |
|---|---|
causalman_micro |
Small production line with a single product. 24 Observable variables. |
causalman_small |
Production line with ~50 observable variables. Multiple products. |
causalman_medium |
Medium-size production line with ~180 observable variables. |
causalman_large |
Large-size production line with >400 observable variables. |
CausalMan Large simulates the whole production line, and the other variants instead simulate progressively smaller parts of it, with the goal of providing a simpler benchmarking scenario.
CausalMan(
name="causalman_small", # one of: micro, small, medium, large
seed=42, # reproducibility seed
batch_multiplier=1, # scale factor for number of samples
parallelize=False, # enable multi-process batch sampling
max_workers=5, # worker count when parallelize=True
debug_mode=False, # write debug outputs to disk
save_path="output/run1", # directory for CSV and graph outputs
)Specify a dictionary of hard interventions before calling sample():
simulator = CausalMan(name="causalman_small", seed=42)
simulator.intervention_dict = {"PF_M1_T1_sgrad": 18500}
obs_df, int_table, path_df, dag = simulator.sample()The simulator mutates the causal graph and resamples downstream variables according to the intervention.
The returned causal_dag is a NetworkX DiGraph. Observable and latent nodes are tracked as node attributes.
import networkx as nx
# List observable nodes
observable = [n for n, d in dag.nodes(data=True) if d.get("observable")]
# Export to GraphML
nx.write_graphml(dag, "causal_graph.graphml")To compute the ADMG (Acyclic Mixed Graph) and MAG (Maximal Ancestral Graph) after marginalizing latent variables:
from causalman.graph_projections import get_latent_projection_single, admg2mag
admg = get_latent_projection_single(dag)
mag = admg2mag(admg)from causalman.graph_plotter import GraphPlotter
plotter = GraphPlotter(dag)
plotter.plot() # opens interactive HTML in browserpython -m causalman.col_masking \
--graph output/run1/batch_data/batch_0/batch_graph.pkl \
--csv output/run1/merged.csv \
--output_dir output/run1/observable/When save_path is provided:
output/run1/
├── batch_data/
│ └── batch_0/
│ ├── batch_graph.pkl # Ground-truth causal DAG (pickled)
│ ├── batch_graph.graphml # Graph in GraphML format
│ └── observed_nodes_list.txt # List of observable node names
└── DEBUG/ # Debug outputs (if debug_mode=True)
For large-scale sampling, enable multi-process execution:
simulator = CausalMan(
name="causalman_medium",
parallelize=True,
max_workers=8,
)
obs_df, int_table, path_df, dag = simulator.sample()Two tutorial notebooks are provided in causalman/:
example_observational.ipynb— basic FCM construction and observational samplinginterventions_example.ipynb— interventional sampling and comparison with observational distributions
Two notebooks in src/ generate ready-to-use benchmark CSV datasets:
-
src/causal_inference_data_generation.ipynb— generates causal inference benchmark datasets. SetSCALE,SEEDS, andN_SAMPLESat the top of the notebook and run all cells. Produces for each (scale, seed) combination:observational.csv— training data with no interventionstask1_force_ltl_control.csv/task1_force_ltl_treatment.csv— do(PF_M1_T1_Force_LTL = 15000/18000)task2_force_control.csv/task2_force_treatment.csv— do(PF_M1_T1_Force = 16000/30000)
-
src/rca_data_generation.ipynb— generates root-cause analysis benchmark datasets across 4 tasks and multiple scales. SetSCALES,SEED, andN_SAMPLESat the top and run all cells.
A CLI equivalent of the causal inference notebook is also available:
# Basic usage (variant required)
python src/generate_causal_inference_data.py --variant small
# Full benchmark: 5 seeds, 10 000 rows per dataset
python src/generate_causal_inference_data.py --variant medium --seeds 4 6 42 66 90
# Custom output directory and sample count
python src/generate_causal_inference_data.py --variant large --samples 5000 --output my_output/| Argument | Default | Description |
|---|---|---|
--variant |
(required) | Scale variant: micro, small, medium, large |
--seeds |
42 |
One or more random seeds |
--samples |
10000 |
Rows to write per dataset |
--output |
output/causalman_causal_inference |
Root output directory |
If you use CausalMan in your research, please cite the associated work.
@misc{tagliapietra2025causalman,
title={CausalMan: A physics-based simulator for large-scale causality},
author={Nicholas Tagliapietra and Juergen Luettin and Lavdim Halilaj and Moritz Willig and Tim Pychynski and Kristian Kersting},
year={2025},
eprint={2502.12707},
archivePrefix={arXiv},
primaryClass={cs.LG},
url={https://arxiv.org/abs/2502.12707},
doi={10.48550/arXiv.2502.12707}
}arXiv: https://arxiv.org/abs/2502.12707
CausalMan is open-sourced under the AGPL-3.0 license. See the LICENSE file for details.
For a list of other open source components included in CausalMan, see the file 3rd-party-licenses.txt.