b-load 0.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- b_load-0.1.2/LICENSE +21 -0
- b_load-0.1.2/PKG-INFO +266 -0
- b_load-0.1.2/README.md +233 -0
- b_load-0.1.2/b_load.egg-info/PKG-INFO +266 -0
- b_load-0.1.2/b_load.egg-info/SOURCES.txt +36 -0
- b_load-0.1.2/b_load.egg-info/dependency_links.txt +1 -0
- b_load-0.1.2/b_load.egg-info/requires.txt +9 -0
- b_load-0.1.2/b_load.egg-info/top_level.txt +2 -0
- b_load-0.1.2/bload/__init__.py +10 -0
- b_load-0.1.2/bload/analysis.py +1 -0
- b_load-0.1.2/bload/background_knowledge.py +1 -0
- b_load-0.1.2/bload/counting_ci_tests.py +1 -0
- b_load-0.1.2/bload/data_generation.py +1 -0
- b_load-0.1.2/bload/evaluation.py +1 -0
- b_load-0.1.2/bload/load.py +1 -0
- b_load-0.1.2/bload/mb_by_mb.py +1 -0
- b_load-0.1.2/bload/pc.py +1 -0
- b_load-0.1.2/bload/real_data_applications/__init__.py +1 -0
- b_load-0.1.2/bload/real_data_applications/dream.py +1 -0
- b_load-0.1.2/bload/real_data_applications/sachs.py +1 -0
- b_load-0.1.2/bload/running.py +1 -0
- b_load-0.1.2/bload/utils.py +1 -0
- b_load-0.1.2/pyproject.toml +55 -0
- b_load-0.1.2/setup.cfg +4 -0
- b_load-0.1.2/src/__init__.py +5 -0
- b_load-0.1.2/src/analysis.py +805 -0
- b_load-0.1.2/src/background_knowledge.py +283 -0
- b_load-0.1.2/src/counting_ci_tests.py +101 -0
- b_load-0.1.2/src/data_generation.py +185 -0
- b_load-0.1.2/src/evaluation.py +642 -0
- b_load-0.1.2/src/load.py +553 -0
- b_load-0.1.2/src/mb_by_mb.py +930 -0
- b_load-0.1.2/src/pc.py +341 -0
- b_load-0.1.2/src/real_data_applications/__init__.py +1 -0
- b_load-0.1.2/src/real_data_applications/dream.py +306 -0
- b_load-0.1.2/src/real_data_applications/sachs.py +202 -0
- b_load-0.1.2/src/running.py +1194 -0
- b_load-0.1.2/src/utils.py +106 -0
b_load-0.1.2/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 b-LOAD contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
b_load-0.1.2/PKG-INFO
ADDED
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: b-load
|
|
3
|
+
Version: 0.1.2
|
|
4
|
+
Summary: Background-Knowledge-Augmented Local Causal Discovery
|
|
5
|
+
Author: b-LOAD contributors
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/chrisahn99/b-load
|
|
8
|
+
Project-URL: Repository, https://github.com/chrisahn99/b-load
|
|
9
|
+
Project-URL: Issues, https://github.com/chrisahn99/b-load/issues
|
|
10
|
+
Keywords: causal discovery,background knowledge,graph learning,statistics,python
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Science/Research
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.9
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Information Analysis
|
|
20
|
+
Requires-Python: >=3.9
|
|
21
|
+
Description-Content-Type: text/markdown
|
|
22
|
+
License-File: LICENSE
|
|
23
|
+
Requires-Dist: causal-learn
|
|
24
|
+
Requires-Dist: rpy2
|
|
25
|
+
Requires-Dist: networkx
|
|
26
|
+
Requires-Dist: numpy
|
|
27
|
+
Requires-Dist: pandas
|
|
28
|
+
Requires-Dist: scikit-learn
|
|
29
|
+
Requires-Dist: scipy
|
|
30
|
+
Requires-Dist: matplotlib
|
|
31
|
+
Requires-Dist: tqdm
|
|
32
|
+
Dynamic: license-file
|
|
33
|
+
|
|
34
|
+
<h1 align="center">b-LOAD: Background-Knowledge-Augmented Local Causal Discovery
|
|
35
|
+
</h1>
|
|
36
|
+
|
|
37
|
+
<p>
|
|
38
|
+
<strong>b-Load</strong> is a knowledge-informed extension of the Local Ordering-based Ancestral Discovery (LOAD) algorithm for local causal discovery of optimal adjustment sets. It integrates prior edge constraints into local structure learning and dynamically expands the discovery frontier using Meek's rules, producing knowledge-constrained maximal partially directed acyclic graphs. By leveraging background knowledge, <strong>b-Load</strong> reduces structural uncertainty and enables the identification of adjustment sets that may not be identifiable from observational data alone. The method improves causal effect estimation, particularly in data-scarce and structurally complex settings, while maintaining scalability and robustness to moderate noise in prior knowledge.
|
|
39
|
+
</p>
|
|
40
|
+
|
|
41
|
+
<p align="center">
|
|
42
|
+
<a href="#theproblem">Problem</a> •
|
|
43
|
+
<a href="#install">Install</a> •
|
|
44
|
+
<a href="#quickstart">Quickstart</a> •
|
|
45
|
+
<a href="#results">Results</a> •
|
|
46
|
+
<a href="#reproducing-the-paper">Paper</a>
|
|
47
|
+
</p>
|
|
48
|
+
|
|
49
|
+
<p align="center">
|
|
50
|
+
<a href="https://doi.org/10.1145/3799682.3840893"><img alt="DOI" src="https://img.shields.io/badge/DOI-10.1145%2F3799682.3840893-1f6feb"></a>
|
|
51
|
+
<a href="https://pypi.org/project/b-load/"><img alt="PyPI" src="https://img.shields.io/pypi/v/b-load"></a>
|
|
52
|
+
<a href="https://pypi.org/project/b-load/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/b-load"></a>
|
|
53
|
+
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-green"></a>
|
|
54
|
+
</p>
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## The Problem
|
|
59
|
+
|
|
60
|
+
Local causal discovery methods such as LOAD can recover adjustment sets without learning the full global graph, but they operate under uncertainty and may miss identifiable causal directions that are already known from domain knowledge. This project addresses that limitation by augmenting the local search with background knowledge and Meek-rule propagation.
|
|
61
|
+
|
|
62
|
+
### LOAD (base)
|
|
63
|
+
The base LOAD algorithm performs local causal structure learning around a target node using a Markov-blanket-by-Markov-blanket (MB-by-MB) expansion. It identifies the optimal adjustment set (o-set) for a treatment–outcome pair without reconstructing the full global graph. The core MB-by-MB implementation in `src/mb_by_mb.py` and the top-level estimation logic in `src/load.py` are adapted from the original LOAD authors' public implementation. The public package exposes these components via `bload.mb_by_mb` and `bload.load`.
|
|
64
|
+
|
|
65
|
+
### b-LOAD (this work)
|
|
66
|
+
b-LOAD extends LOAD with three key modifications to the MB-by-MB loop:
|
|
67
|
+
|
|
68
|
+
1. **BK initialisation** — the growing graph is seeded with required background-knowledge edges so that known causal directions are present from the first iteration.
|
|
69
|
+
2. **BK-protected graph update** — after each local structure is merged, required edges are re-stamped to prevent CI-test errors from erasing known constraints.
|
|
70
|
+
3. **Four-rule MPDAG Meek** — a fourth Meek orientation rule (using the sep-set from skeleton search) propagates orientations induced by BK edges that the three structural rules alone cannot deduce.
|
|
71
|
+
|
|
72
|
+
### b-PC (baseline)
|
|
73
|
+
b-PC runs the standard PC algorithm to produce a CPDAG, injects required background-knowledge edges, and then applies four-rule Meek to propagate the induced orientations.
|
|
74
|
+
|
|
75
|
+
### Graph encoding
|
|
76
|
+
|
|
77
|
+
Throughout the implementation, adjacency matrices use the following internal convention:
|
|
78
|
+
|
|
79
|
+
| `g[i, j]` | `g[j, i]` | Meaning |
|
|
80
|
+
|-----------|-----------|---------|
|
|
81
|
+
| `-1` | `-1` | Undirected edge i — j |
|
|
82
|
+
| `-1` | `+1` | Directed edge i → j |
|
|
83
|
+
| `0` | `0` | No edge |
|
|
84
|
+
|
|
85
|
+
Background knowledge is encoded identically: `bk[i,j] = -1` and `bk[j,i] = 1` for a required edge i → j.
|
|
86
|
+
|
|
87
|
+
The final `"amat"` output (returned by `pc` and `pc_with_bk`) follows the `pcalg` convention: `amat[i][j] = 1` indicates a tail at i (edge from i toward j, directed or undirected).
|
|
88
|
+
|
|
89
|
+
---
|
|
90
|
+
|
|
91
|
+
## Install
|
|
92
|
+
|
|
93
|
+
### Requirements
|
|
94
|
+
|
|
95
|
+
Install the package from PyPI:
|
|
96
|
+
|
|
97
|
+
```bash
|
|
98
|
+
pip install b-load
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
Or install from source in editable mode:
|
|
102
|
+
|
|
103
|
+
```bash
|
|
104
|
+
git clone https://github.com/chrisahn99/b-load.git
|
|
105
|
+
cd b-load
|
|
106
|
+
pip install -e .
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
R dependencies (required for data generation and CPDAG/optAdjSet computation):
|
|
110
|
+
|
|
111
|
+
```r
|
|
112
|
+
install.packages(c("pcalg", "graph", "RBGL"))
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
The R scripts `generate_data.R` and `evaluate.R` are sourced automatically when the package is imported.
|
|
116
|
+
|
|
117
|
+
### Repository structure
|
|
118
|
+
|
|
119
|
+
```text
|
|
120
|
+
.
|
|
121
|
+
├── bload/ # Public import namespace for pip users
|
|
122
|
+
│ ├── __init__.py # Package version metadata
|
|
123
|
+
│ ├── load.py # Re-export of src.load
|
|
124
|
+
│ ├── pc.py # Re-export of src.pc
|
|
125
|
+
│ ├── running.py # Re-export of src.running
|
|
126
|
+
│ ├── analysis.py # Re-export of src.analysis
|
|
127
|
+
│ ├── background_knowledge.py # Re-export of src.background_knowledge
|
|
128
|
+
│ ├── data_generation.py # Re-export of src.data_generation
|
|
129
|
+
│ ├── evaluation.py # Re-export of src.evaluation
|
|
130
|
+
│ ├── mb_by_mb.py # Re-export of src.mb_by_mb
|
|
131
|
+
│ ├── counting_ci_tests.py # Re-export of src.counting_ci_tests
|
|
132
|
+
│ ├── utils.py # Re-export of src.utils
|
|
133
|
+
│ └── real_data_applications/
|
|
134
|
+
│ ├── __init__.py
|
|
135
|
+
│ ├── sachs.py # Sachs protein-signalling dataset helpers
|
|
136
|
+
│ └── dream.py # DREAM4 / DREAM5 benchmark helpers
|
|
137
|
+
│
|
|
138
|
+
├── src/ # Internal implementation package
|
|
139
|
+
│ ├── ...
|
|
140
|
+
│
|
|
141
|
+
├── experiment_scripts/ # Entry-point scripts for each experimental condition
|
|
142
|
+
│ ├── run_load_local_bk.py
|
|
143
|
+
│ ├── run_load_global_bk.py
|
|
144
|
+
│ ├── run_load_noised.py
|
|
145
|
+
│ ├── run_pc.py
|
|
146
|
+
│ ├── run_bpc_local_bk.py
|
|
147
|
+
│ └── run_bpc_global_bk.py
|
|
148
|
+
│
|
|
149
|
+
├── generate_data.R
|
|
150
|
+
├── evaluate.R
|
|
151
|
+
├── pyproject.toml
|
|
152
|
+
├── requirements.txt
|
|
153
|
+
├── LICENSE
|
|
154
|
+
├── README.md
|
|
155
|
+
└── .github/workflows/publish.yml
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
---
|
|
159
|
+
|
|
160
|
+
## Quickstart
|
|
161
|
+
|
|
162
|
+
### Running experiments
|
|
163
|
+
|
|
164
|
+
Each script in `experiment_scripts/` accepts a `--save_path` argument and optional graph/sample-size parameters. Example usage:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# b-LOAD vs LOAD with locally sampled background knowledge
|
|
168
|
+
python experiment_scripts/run_load_local_bk.py --save_path experiment_runs/load_local.csv
|
|
169
|
+
|
|
170
|
+
# b-PC with globally sampled background knowledge
|
|
171
|
+
python experiment_scripts/run_bpc_global_bk.py --save_path experiment_runs/bpc_global.csv
|
|
172
|
+
|
|
173
|
+
# PC baseline (no background knowledge)
|
|
174
|
+
python experiment_scripts/run_pc.py --save_path experiment_runs/pc.csv
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
> **Note:** Output files are written to `experiment_runs/` by default. Create this directory locally if needed — it is excluded from version control.
|
|
178
|
+
|
|
179
|
+
### Background knowledge sampling strategies
|
|
180
|
+
|
|
181
|
+
Two strategies are available (controlled by the `sampling_strategy` argument in experiment runners):
|
|
182
|
+
|
|
183
|
+
- **`"local"`** — only edges incident on the treatment or outcome nodes are sampled as required BK. This is the primary strategy evaluated in the paper.
|
|
184
|
+
- **`"global"`** — required edges are drawn uniformly from all true edges in the DAG.
|
|
185
|
+
|
|
186
|
+
The fraction of edges revealed is controlled by `fraction_required` (0–1).
|
|
187
|
+
|
|
188
|
+
### Real-data experiments
|
|
189
|
+
|
|
190
|
+
Real-data runners for the Sachs protein-signalling and DREAM4/DREAM5 benchmarks are provided in `bload.real_data_applications` and `src/real_data_applications`. Place the data files under `experiments/dream4/` and `experiments/dream5/` respectively, then call:
|
|
191
|
+
|
|
192
|
+
```python
|
|
193
|
+
from bload.real_data_applications.dream import run_experiment_dream4, run_experiment_dream5
|
|
194
|
+
from bload.real_data_applications.sachs import get_sachs_ground_truth_dag, fit_oracle_weights
|
|
195
|
+
|
|
196
|
+
# DREAM4 network 1, local BK, 30 % of true edges revealed
|
|
197
|
+
result_dict, details_dict = run_experiment_dream4(subset_number=1, sampling_strategy="local", frac_req=0.3)
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Analysing results
|
|
201
|
+
|
|
202
|
+
`bload.analysis` provides utilities for loading result CSVs, computing the complexity metric $\pi = n_{samples} / (n_{nodes} \times expected\_degree / 2)$, aggregating into regime tables, and producing F1 and intervention-distance plots:
|
|
203
|
+
|
|
204
|
+
```python
|
|
205
|
+
from bload.analysis import load_iter_folder, add_complexity_metric, regime_table_3, plot_f1
|
|
206
|
+
|
|
207
|
+
df_bload = load_iter_folder("experiment_runs/load_local_*", stat_col_pairs=[...])
|
|
208
|
+
df_bload = add_complexity_metric(df_bload)
|
|
209
|
+
fig = plot_f1(df_pc, df_bpc, df_bpc_global, df_load, df_bload_global)
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Results
|
|
215
|
+
|
|
216
|
+
The repository is organized around the experimental comparison between standard LOAD, background-knowledge-augmented b-LOAD, and PC-style baselines. The key outputs are CSV result files produced by the experiment scripts and summary plots generated via `src/analysis.py`.
|
|
217
|
+
|
|
218
|
+
These experiments typically report:
|
|
219
|
+
|
|
220
|
+
- structural accuracy (precision, recall, F1),
|
|
221
|
+
- intervention-distance performance,
|
|
222
|
+
- the effect of revealed background knowledge on adjustment-set identification, and
|
|
223
|
+
- robustness under partially noisy background knowledge.
|
|
224
|
+
|
|
225
|
+
The analysis utilities in `src/analysis.py` let you aggregate runs across graph sizes, sampling strategies, and difficulty regimes and visualize the outcome trends.
|
|
226
|
+
|
|
227
|
+
---
|
|
228
|
+
|
|
229
|
+
## Reproducing the Paper
|
|
230
|
+
|
|
231
|
+
To reproduce the paper's experiments:
|
|
232
|
+
|
|
233
|
+
1. Install the Python and R dependencies listed in the installation section.
|
|
234
|
+
2. Run the synthetic experiment scripts in `experiment_scripts/` with the desired background-knowledge setting.
|
|
235
|
+
3. Save the resulting outputs under `experiment_runs/`.
|
|
236
|
+
4. Use the analysis utilities in `src/analysis.py` to aggregate metrics and generate plots.
|
|
237
|
+
|
|
238
|
+
This workflow matches the study design used to compare:
|
|
239
|
+
|
|
240
|
+
- LOAD without background knowledge,
|
|
241
|
+
- b-LOAD with local and global background knowledge,
|
|
242
|
+
- b-PC with the same background-knowledge settings, and
|
|
243
|
+
- the PC baseline without knowledge augmentation.
|
|
244
|
+
|
|
245
|
+
---
|
|
246
|
+
|
|
247
|
+
## Citation
|
|
248
|
+
|
|
249
|
+
If you use this code, please cite:
|
|
250
|
+
|
|
251
|
+
```bibtex
|
|
252
|
+
@inproceedings{ahnbload206,
|
|
253
|
+
author = {Ahn, Seong Woo and Leite, Alessandro and De Melo Costa, Jos{\'e} Lucas and Popineau, Fabrice and Doan, Bich-Li{\^e}n and Rimmel, Arpad},
|
|
254
|
+
title = {Knowledge-Informed Local Causal Discovery of Optimal Adjustment Sets},
|
|
255
|
+
year = {2026},
|
|
256
|
+
doi = {10.1145/3799682.3840893},
|
|
257
|
+
booktitle = {The 35th ACM International Conference on Information and Knowledge Management},
|
|
258
|
+
pages = {72--82},
|
|
259
|
+
}
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## License
|
|
265
|
+
|
|
266
|
+
MIT, see [LICENSE](LICENSE).
|
b_load-0.1.2/README.md
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
<h1 align="center">b-LOAD: Background-Knowledge-Augmented Local Causal Discovery
|
|
2
|
+
</h1>
|
|
3
|
+
|
|
4
|
+
<p>
|
|
5
|
+
<strong>b-Load</strong> is a knowledge-informed extension of the Local Ordering-based Ancestral Discovery (LOAD) algorithm for local causal discovery of optimal adjustment sets. It integrates prior edge constraints into local structure learning and dynamically expands the discovery frontier using Meek's rules, producing knowledge-constrained maximal partially directed acyclic graphs. By leveraging background knowledge, <strong>b-Load</strong> reduces structural uncertainty and enables the identification of adjustment sets that may not be identifiable from observational data alone. The method improves causal effect estimation, particularly in data-scarce and structurally complex settings, while maintaining scalability and robustness to moderate noise in prior knowledge.
|
|
6
|
+
</p>
|
|
7
|
+
|
|
8
|
+
<p align="center">
|
|
9
|
+
<a href="#theproblem">Problem</a> •
|
|
10
|
+
<a href="#install">Install</a> •
|
|
11
|
+
<a href="#quickstart">Quickstart</a> •
|
|
12
|
+
<a href="#results">Results</a> •
|
|
13
|
+
<a href="#reproducing-the-paper">Paper</a>
|
|
14
|
+
</p>
|
|
15
|
+
|
|
16
|
+
<p align="center">
|
|
17
|
+
<a href="https://doi.org/10.1145/3799682.3840893"><img alt="DOI" src="https://img.shields.io/badge/DOI-10.1145%2F3799682.3840893-1f6feb"></a>
|
|
18
|
+
<a href="https://pypi.org/project/b-load/"><img alt="PyPI" src="https://img.shields.io/pypi/v/b-load"></a>
|
|
19
|
+
<a href="https://pypi.org/project/b-load/"><img alt="Python" src="https://img.shields.io/pypi/pyversions/b-load"></a>
|
|
20
|
+
<a href="LICENSE"><img alt="License" src="https://img.shields.io/badge/license-MIT-green"></a>
|
|
21
|
+
</p>
|
|
22
|
+
|
|
23
|
+
---
|
|
24
|
+
|
|
25
|
+
## The Problem
|
|
26
|
+
|
|
27
|
+
Local causal discovery methods such as LOAD can recover adjustment sets without learning the full global graph, but they operate under uncertainty and may miss identifiable causal directions that are already known from domain knowledge. This project addresses that limitation by augmenting the local search with background knowledge and Meek-rule propagation.
|
|
28
|
+
|
|
29
|
+
### LOAD (base)
|
|
30
|
+
The base LOAD algorithm performs local causal structure learning around a target node using a Markov-blanket-by-Markov-blanket (MB-by-MB) expansion. It identifies the optimal adjustment set (o-set) for a treatment–outcome pair without reconstructing the full global graph. The core MB-by-MB implementation in `src/mb_by_mb.py` and the top-level estimation logic in `src/load.py` are adapted from the original LOAD authors' public implementation. The public package exposes these components via `bload.mb_by_mb` and `bload.load`.
|
|
31
|
+
|
|
32
|
+
### b-LOAD (this work)
|
|
33
|
+
b-LOAD extends LOAD with three key modifications to the MB-by-MB loop:
|
|
34
|
+
|
|
35
|
+
1. **BK initialisation** — the growing graph is seeded with required background-knowledge edges so that known causal directions are present from the first iteration.
|
|
36
|
+
2. **BK-protected graph update** — after each local structure is merged, required edges are re-stamped to prevent CI-test errors from erasing known constraints.
|
|
37
|
+
3. **Four-rule MPDAG Meek** — a fourth Meek orientation rule (using the sep-set from skeleton search) propagates orientations induced by BK edges that the three structural rules alone cannot deduce.
|
|
38
|
+
|
|
39
|
+
### b-PC (baseline)
|
|
40
|
+
b-PC runs the standard PC algorithm to produce a CPDAG, injects required background-knowledge edges, and then applies four-rule Meek to propagate the induced orientations.
|
|
41
|
+
|
|
42
|
+
### Graph encoding
|
|
43
|
+
|
|
44
|
+
Throughout the implementation, adjacency matrices use the following internal convention:
|
|
45
|
+
|
|
46
|
+
| `g[i, j]` | `g[j, i]` | Meaning |
|
|
47
|
+
|-----------|-----------|---------|
|
|
48
|
+
| `-1` | `-1` | Undirected edge i — j |
|
|
49
|
+
| `-1` | `+1` | Directed edge i → j |
|
|
50
|
+
| `0` | `0` | No edge |
|
|
51
|
+
|
|
52
|
+
Background knowledge is encoded identically: `bk[i,j] = -1` and `bk[j,i] = 1` for a required edge i → j.
|
|
53
|
+
|
|
54
|
+
The final `"amat"` output (returned by `pc` and `pc_with_bk`) follows the `pcalg` convention: `amat[i][j] = 1` indicates a tail at i (edge from i toward j, directed or undirected).
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
## Install
|
|
59
|
+
|
|
60
|
+
### Requirements
|
|
61
|
+
|
|
62
|
+
Install the package from PyPI:
|
|
63
|
+
|
|
64
|
+
```bash
|
|
65
|
+
pip install b-load
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
Or install from source in editable mode:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
git clone https://github.com/chrisahn99/b-load.git
|
|
72
|
+
cd b-load
|
|
73
|
+
pip install -e .
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
R dependencies (required for data generation and CPDAG/optAdjSet computation):
|
|
77
|
+
|
|
78
|
+
```r
|
|
79
|
+
install.packages(c("pcalg", "graph", "RBGL"))
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The R scripts `generate_data.R` and `evaluate.R` are sourced automatically when the package is imported.
|
|
83
|
+
|
|
84
|
+
### Repository structure
|
|
85
|
+
|
|
86
|
+
```text
|
|
87
|
+
.
|
|
88
|
+
├── bload/ # Public import namespace for pip users
|
|
89
|
+
│ ├── __init__.py # Package version metadata
|
|
90
|
+
│ ├── load.py # Re-export of src.load
|
|
91
|
+
│ ├── pc.py # Re-export of src.pc
|
|
92
|
+
│ ├── running.py # Re-export of src.running
|
|
93
|
+
│ ├── analysis.py # Re-export of src.analysis
|
|
94
|
+
│ ├── background_knowledge.py # Re-export of src.background_knowledge
|
|
95
|
+
│ ├── data_generation.py # Re-export of src.data_generation
|
|
96
|
+
│ ├── evaluation.py # Re-export of src.evaluation
|
|
97
|
+
│ ├── mb_by_mb.py # Re-export of src.mb_by_mb
|
|
98
|
+
│ ├── counting_ci_tests.py # Re-export of src.counting_ci_tests
|
|
99
|
+
│ ├── utils.py # Re-export of src.utils
|
|
100
|
+
│ └── real_data_applications/
|
|
101
|
+
│ ├── __init__.py
|
|
102
|
+
│ ├── sachs.py # Sachs protein-signalling dataset helpers
|
|
103
|
+
│ └── dream.py # DREAM4 / DREAM5 benchmark helpers
|
|
104
|
+
│
|
|
105
|
+
├── src/ # Internal implementation package
|
|
106
|
+
│ ├── ...
|
|
107
|
+
│
|
|
108
|
+
├── experiment_scripts/ # Entry-point scripts for each experimental condition
|
|
109
|
+
│ ├── run_load_local_bk.py
|
|
110
|
+
│ ├── run_load_global_bk.py
|
|
111
|
+
│ ├── run_load_noised.py
|
|
112
|
+
│ ├── run_pc.py
|
|
113
|
+
│ ├── run_bpc_local_bk.py
|
|
114
|
+
│ └── run_bpc_global_bk.py
|
|
115
|
+
│
|
|
116
|
+
├── generate_data.R
|
|
117
|
+
├── evaluate.R
|
|
118
|
+
├── pyproject.toml
|
|
119
|
+
├── requirements.txt
|
|
120
|
+
├── LICENSE
|
|
121
|
+
├── README.md
|
|
122
|
+
└── .github/workflows/publish.yml
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## Quickstart
|
|
128
|
+
|
|
129
|
+
### Running experiments
|
|
130
|
+
|
|
131
|
+
Each script in `experiment_scripts/` accepts a `--save_path` argument and optional graph/sample-size parameters. Example usage:
|
|
132
|
+
|
|
133
|
+
```bash
|
|
134
|
+
# b-LOAD vs LOAD with locally sampled background knowledge
|
|
135
|
+
python experiment_scripts/run_load_local_bk.py --save_path experiment_runs/load_local.csv
|
|
136
|
+
|
|
137
|
+
# b-PC with globally sampled background knowledge
|
|
138
|
+
python experiment_scripts/run_bpc_global_bk.py --save_path experiment_runs/bpc_global.csv
|
|
139
|
+
|
|
140
|
+
# PC baseline (no background knowledge)
|
|
141
|
+
python experiment_scripts/run_pc.py --save_path experiment_runs/pc.csv
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
> **Note:** Output files are written to `experiment_runs/` by default. Create this directory locally if needed — it is excluded from version control.
|
|
145
|
+
|
|
146
|
+
### Background knowledge sampling strategies
|
|
147
|
+
|
|
148
|
+
Two strategies are available (controlled by the `sampling_strategy` argument in experiment runners):
|
|
149
|
+
|
|
150
|
+
- **`"local"`** — only edges incident on the treatment or outcome nodes are sampled as required BK. This is the primary strategy evaluated in the paper.
|
|
151
|
+
- **`"global"`** — required edges are drawn uniformly from all true edges in the DAG.
|
|
152
|
+
|
|
153
|
+
The fraction of edges revealed is controlled by `fraction_required` (0–1).
|
|
154
|
+
|
|
155
|
+
### Real-data experiments
|
|
156
|
+
|
|
157
|
+
Real-data runners for the Sachs protein-signalling and DREAM4/DREAM5 benchmarks are provided in `bload.real_data_applications` and `src/real_data_applications`. Place the data files under `experiments/dream4/` and `experiments/dream5/` respectively, then call:
|
|
158
|
+
|
|
159
|
+
```python
|
|
160
|
+
from bload.real_data_applications.dream import run_experiment_dream4, run_experiment_dream5
|
|
161
|
+
from bload.real_data_applications.sachs import get_sachs_ground_truth_dag, fit_oracle_weights
|
|
162
|
+
|
|
163
|
+
# DREAM4 network 1, local BK, 30 % of true edges revealed
|
|
164
|
+
result_dict, details_dict = run_experiment_dream4(subset_number=1, sampling_strategy="local", frac_req=0.3)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
### Analysing results
|
|
168
|
+
|
|
169
|
+
`bload.analysis` provides utilities for loading result CSVs, computing the complexity metric $\pi = n_{samples} / (n_{nodes} \times expected\_degree / 2)$, aggregating into regime tables, and producing F1 and intervention-distance plots:
|
|
170
|
+
|
|
171
|
+
```python
|
|
172
|
+
from bload.analysis import load_iter_folder, add_complexity_metric, regime_table_3, plot_f1
|
|
173
|
+
|
|
174
|
+
df_bload = load_iter_folder("experiment_runs/load_local_*", stat_col_pairs=[...])
|
|
175
|
+
df_bload = add_complexity_metric(df_bload)
|
|
176
|
+
fig = plot_f1(df_pc, df_bpc, df_bpc_global, df_load, df_bload_global)
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
---
|
|
180
|
+
|
|
181
|
+
## Results
|
|
182
|
+
|
|
183
|
+
The repository is organized around the experimental comparison between standard LOAD, background-knowledge-augmented b-LOAD, and PC-style baselines. The key outputs are CSV result files produced by the experiment scripts and summary plots generated via `src/analysis.py`.
|
|
184
|
+
|
|
185
|
+
These experiments typically report:
|
|
186
|
+
|
|
187
|
+
- structural accuracy (precision, recall, F1),
|
|
188
|
+
- intervention-distance performance,
|
|
189
|
+
- the effect of revealed background knowledge on adjustment-set identification, and
|
|
190
|
+
- robustness under partially noisy background knowledge.
|
|
191
|
+
|
|
192
|
+
The analysis utilities in `src/analysis.py` let you aggregate runs across graph sizes, sampling strategies, and difficulty regimes and visualize the outcome trends.
|
|
193
|
+
|
|
194
|
+
---
|
|
195
|
+
|
|
196
|
+
## Reproducing the Paper
|
|
197
|
+
|
|
198
|
+
To reproduce the paper's experiments:
|
|
199
|
+
|
|
200
|
+
1. Install the Python and R dependencies listed in the installation section.
|
|
201
|
+
2. Run the synthetic experiment scripts in `experiment_scripts/` with the desired background-knowledge setting.
|
|
202
|
+
3. Save the resulting outputs under `experiment_runs/`.
|
|
203
|
+
4. Use the analysis utilities in `src/analysis.py` to aggregate metrics and generate plots.
|
|
204
|
+
|
|
205
|
+
This workflow matches the study design used to compare:
|
|
206
|
+
|
|
207
|
+
- LOAD without background knowledge,
|
|
208
|
+
- b-LOAD with local and global background knowledge,
|
|
209
|
+
- b-PC with the same background-knowledge settings, and
|
|
210
|
+
- the PC baseline without knowledge augmentation.
|
|
211
|
+
|
|
212
|
+
---
|
|
213
|
+
|
|
214
|
+
## Citation
|
|
215
|
+
|
|
216
|
+
If you use this code, please cite:
|
|
217
|
+
|
|
218
|
+
```bibtex
|
|
219
|
+
@inproceedings{ahnbload206,
|
|
220
|
+
author = {Ahn, Seong Woo and Leite, Alessandro and De Melo Costa, Jos{\'e} Lucas and Popineau, Fabrice and Doan, Bich-Li{\^e}n and Rimmel, Arpad},
|
|
221
|
+
title = {Knowledge-Informed Local Causal Discovery of Optimal Adjustment Sets},
|
|
222
|
+
year = {2026},
|
|
223
|
+
doi = {10.1145/3799682.3840893},
|
|
224
|
+
booktitle = {The 35th ACM International Conference on Information and Knowledge Management},
|
|
225
|
+
pages = {72--82},
|
|
226
|
+
}
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
---
|
|
230
|
+
|
|
231
|
+
## License
|
|
232
|
+
|
|
233
|
+
MIT, see [LICENSE](LICENSE).
|