A unified Python framework for causal effect bounding algorithms
CausalBoundingEngine is a modular Python package that provides a unified interface for comparing and applying state-of-the-art causal bounding algorithms. It enables researchers and practitioners to compute bounds on causal effects when unmeasured confounding is present.
🔧 Unified Interface - Consistent API across all algorithms and scenarios
📊 Multiple Algorithms - Manski, Tian-Pearl, Autobound, Causaloptim, Zaffalonbounds, and more
🎯 Flexible Scenarios - Support for confounded and instrumental variable settings
🔗 External Engines - Integration with R (rpy2) and Java (jpype1) backends
🚀 Easy Extension - Simple framework for adding new algorithms and scenarios
📚 Comprehensive Docs - Detailed documentation with examples and API reference
| Algorithm | ATE | PNS | Scenarios | Dependencies | Reference |
|---|---|---|---|---|---|
| Manski | ✓ | ✗ | BinaryConf | Core | Manski (1990) |
| Tian-Pearl | ✗ | ✓ | BinaryConf | Core | Tian & Pearl (2000) |
| Autobound | ✓ | ✓ | BinaryConf, BinaryIV | Core | Duarte et al. (2023) |
| Entropybounds | ✓ | ✓ | BinaryConf | Core | Jiang et al. (2023) |
| Causaloptim | ✓ | ✓ | BinaryConf, BinaryIV | R | Sachs et al. (2022) |
| Zaffalonbounds | ✓ | ✓ | BinaryConf, BinaryIV | Java | Zaffalon et al. (2022) |
| ZhangBareinboim | ✓ | ✗ | ContIV | Core | Zhang & Bareinboim (2021) |
🐍 For smooth sailing, Python 3.12 or greater is recommended.
While CausalBoundingEngine supports Python 3.8+, you may encounter issues in some specific situations with older versions.
pip install causalboundingengineFor extended functionality, install with optional dependencies:
# R integration (Causaloptim algorithm)
pip install causalboundingengine[r]
# Java integration (Zaffalonbounds algorithm)
pip install causalboundingengine[java]
# All optional features
pip install causalboundingengine[full]
# Documentation building
pip install causalboundingengine[docs]For algorithms requiring external engines:
R Support (for Causaloptim):
# Ubuntu/Debian
sudo apt install r-base
# macOS
brew install r
# Windows: Download from https://cran.r-project.org/Java Support (for Zaffalonbounds):
# Ubuntu/Debian
sudo apt install default-jre
# macOS
brew install openjdk
# Windows: Download from https://adoptium.net/Note: numeric results in the examples are for illustrative porpuses and not necessarily correct.
import numpy as np
from causalboundingengine.scenarios import BinaryConf
# Your observational data
X = np.array([0, 1, 1, 0, 1, 0, 1, 0]) # Treatment
Y = np.array([1, 0, 1, 0, 1, 1, 0, 1]) # Outcome
# Create scenario and compute bounds
scenario = BinaryConf(X, Y)
# Compute ATE bounds with different algorithms
manski_bounds = scenario.ATE.manski() # (-1.0, 1.0) - Most conservative
autobound_bounds = scenario.ATE.autobound() # (-0.5, 0.5) - LP optimization
print(f"Manski bounds: {manski_bounds}")
print(f"Autobound bounds: {autobound_bounds}")from causalboundingengine.scenarios import BinaryIV
# IV data (e.g., randomized trial with non-compliance)
Z = np.array([0, 1, 1, 0, 1, 0, 1, 0]) # Instrument (randomization)
X = np.array([0, 1, 0, 0, 1, 0, 1, 0]) # Treatment (actual uptake)
Y = np.array([1, 0, 1, 0, 1, 1, 0, 1]) # Outcome
# Create IV scenario
scenario = BinaryIV(X, Y, Z)
# Compute bounds leveraging IV assumptions
iv_bounds = scenario.ATE.autobound()
print(f"IV-based bounds: {iv_bounds}") # Often tighter than confounded casefrom causalboundingengine.scenarios import ContIV
# Binary instrument/treatment with continuous outcome
Z = np.array([0, 1, 1, 0, 1]) # Binary instrument
X = np.array([0, 1, 1, 0, 1]) # Binary treatment
Y = np.array([0.2, 0.8, 0.6, 0.1, 0.9]) # Continuous outcome [0,1]
scenario = ContIV(X, Y, Z)
bounds = scenario.ATE.zhangbareinboim()
print(f"Continuous outcome bounds: {bounds}")import numpy as np
from causalboundingengine.scenarios import BinaryConf
# Generate example data
np.random.seed(42)
n = 1000
X = np.random.binomial(1, 0.3, n)
Y = np.random.binomial(1, 0.6, n)
scenario = BinaryConf(X, Y)
# Compare multiple algorithms
algorithms = ['manski', 'autobound', 'entropybounds']
results = {}
for alg in algorithms:
if alg == 'entropybounds':
bounds = getattr(scenario.ATE, alg)(theta=0.5)
else:
bounds = getattr(scenario.ATE, alg)()
results[alg] = bounds
print(f"{alg:15} ATE bounds: {bounds}")
# Output:
# manski ATE bounds: (-0.7, 0.7)
# autobound ATE bounds: (-0.3, 0.3)
# entropybounds ATE bounds: (-0.2, 0.2)# Sensitivity analysis with Entropybounds
thetas = [0.1, 0.3, 0.6, 0.8]
for theta in thetas:
bounds = scenario.ATE.entropybounds(theta=theta)
width = bounds[1] - bounds[0]
print(f"θ={theta}: bounds={bounds}, width={width:.3f}")
# Output shows how bounds widen as assumptions weaken:
# θ=0.1: bounds=(-0.15, 0.15), width=0.735
# θ=0.3: bounds=(-0.25, 0.25), width=0.995
# θ=0.6: bounds=(-0.35, 0.35), width=1.000
# θ=0.8: bounds=(-0.45, 0.45), width=1.000def robust_analysis(X, Y, Z=None):
"""Run multiple algorithms for robustness."""
if Z is None:
scenario = BinaryConf(X, Y)
algorithms = ['manski', 'autobound']
else:
scenario = BinaryIV(X, Y, Z)
algorithms = ['autobound'] # Add 'causaloptim', 'zaffalonbounds' if available
results = {}
for alg in algorithms:
try:
results[alg] = getattr(scenario.ATE, alg)()
print(f"✓ {alg}: {results[alg]}")
except Exception as e:
print(f"✗ {alg}: {e}")
return results
# Run robust analysis
bounds_dict = robust_analysis(X, Y)CausalBoundingEngine organizes algorithms by causal scenario:
- Use case: Observational studies with binary treatment/outcome
- Assumptions: Potential unmeasured confounding
- Algorithms: Manski, Autobound, Entropybounds, Causaloptim, Zaffalonbounds
- Use case: Instrumental variable analysis with binary variables
- Assumptions: Valid instrument (relevance, exclusion, exogeneity)
- Algorithms: Autobound, Causaloptim, Zaffalonbounds
- Use case: Binary instrument/treatment with continuous outcome [0,1]
- Assumptions: Valid instrument, bounded outcome
- Algorithms: ZhangBareinboim
# Entropybounds with custom confounding strength
bounds = scenario.ATE.entropybounds(theta=0.2)
# Causaloptim with custom R path
bounds = scenario.ATE.causaloptim(r_path="/usr/local/bin/R")# Check available algorithms
print("ATE algorithms:", scenario.get_algorithms('ATE'))
print("PNS algorithms:", scenario.get_algorithms('PNS'))
# Dynamic algorithm access
algorithm_name = 'manski'
if algorithm_name in scenario.get_algorithms('ATE'):
bounds = getattr(scenario.ATE, algorithm_name)()import logging
logging.basicConfig(level=logging.WARNING)
# Algorithms return trivial bounds on failure
bounds = scenario.ATE.some_algorithm()
if bounds == (-1.0, 1.0): # ATE trivial bounds
print("Algorithm failed, returned trivial bounds")📖 Full Documentation: https://causalboundingengine.readthedocs.io/
The documentation includes:
- Installation Guide - Detailed setup instructions
- Quick Start - Get up and running quickly
- User Guide - Concepts and best practices
- Algorithm Reference - Detailed algorithm documentation
- Scenario Guide - When to use which scenario
- API Reference - Complete API documentation
- Examples - Real-world usage examples
- References - Citations and credits
CausalBoundingEngine includes a comprehensive test suite to ensure reliability.
# Run basic tests (minimal dependencies)
python -m pytest tests/test_dummy.py tests/test_scenarios.py::TestDataClass -v
# Run core algorithm tests
python -m pytest tests/test_core_algorithms.py -v
# Run all tests
python -m pytest tests/ -v✅ Core functionality - Data structures, basic algorithms
✅ Algorithm interfaces - Manski, Tian-Pearl bounds
✅ Scenario framework - Data handling, dispatching
✅ Integration tests - End-to-end workflows
See TESTING.md for detailed testing documentation.
We welcome contributions! The process is simple:
- Fork the repository on GitHub
- Clone your fork and install:
pip install -e . - Make your changes and add tests
- Submit a Pull Request
See our Contributing Guide for details.
- 🔧 New algorithm implementations
- 📊 Additional causal scenarios
- 🐛 Bug fixes and improvements
- 📚 Documentation and examples
- 🚀 Performance optimizations
If you use CausalBoundingEngine in your research, please cite the relevant algorithm papers. See the References section for complete citations.
@software{causalboundingengine,
title={CausalBoundingEngine: A Unified Framework for Causal Effect Bounding},
author={[Tobias Maringgele]},
year={2025},
url={https://github.com/tmaringgele/CausalBoundingEngine},
note={Python package for causal effect bounding algorithms}
}This project is licensed under the MIT License - see the LICENSE file for details.
CausalBoundingEngine integrates algorithms from multiple research papers. We gratefully acknowledge:
- Manski (1990) - Nonparametric bounds foundation
- Tian & Pearl (2000) - Probability of causation bounds
- Duarte et al. (2023) - Autobound optimization approach
- Jiang et al. (2023) - Entropy-based weak confounding
- Sachs et al. (2022) - Causaloptim R library
- Zaffalon et al. (2022) - Causal expectation maximisation approach
- Zhang & Bareinboim (2021) - Continuous outcome bounding
See the References page for complete citations and attributions.
Built with 💙 for causal inference
Documentation • PyPI • GitHub
