PR 7 — Pretraining Objective Experiment Framework
#16
by ho22joshua - opened
This view is limited to 50 files because it contains too many changes. See the raw diff here.
- .github/workflows/ci.yml +87 -0
- .gitignore +22 -6
- .python-version +1 -1
- AGENTS.md +463 -38
- CHANGELOG.md +13 -0
- MANIFEST.in +3 -0
- README.md +1 -0
- README_PROJECT.md +282 -0
- benchmarks/README.md +48 -0
- benchmarks/__init__.py +1 -0
- benchmarks/_common.py +70 -0
- benchmarks/benchmark_dataloader.py +63 -0
- benchmarks/benchmark_inference.py +52 -0
- benchmarks/benchmark_preprocessing.py +61 -0
- benchmarks/benchmark_training.py +73 -0
- configs/.gitkeep +1 -0
- configs/data/.gitkeep +1 -0
- configs/delphes_contrastive.yaml +0 -81
- configs/delphes_jepa.yaml +0 -77
- configs/delphes_jepa_edge.yaml +0 -82
- configs/delphes_masked.yaml +0 -78
- configs/delphes_pfn.yaml +0 -86
- configs/delphes_pretraining.yaml +0 -61
- configs/delphes_tabicl.yaml +0 -89
- configs/delphes_tabpfn.yaml +0 -89
- configs/environment/.gitkeep +1 -0
- configs/gamgam.yaml +0 -97
- configs/gamgam_deep_sets.yaml +0 -88
- configs/gamgam_finetune.yaml +0 -97
- configs/gamgam_jepa_finetune.yaml +0 -95
- configs/gamgam_pfn.yaml +0 -90
- configs/gamgam_tabdpt.yaml +0 -91
- configs/gamgam_tabicl.yaml +0 -95
- configs/gamgam_tabpfn.yaml +0 -96
- configs/gamgam_transformer.yaml +0 -92
- configs/model/.gitkeep +1 -0
- configs/model/root_gnn/.gitkeep +1 -0
- configs/opendata_pfn.yaml +0 -0
- configs/task/.gitkeep +1 -0
- configs/trainer/.gitkeep +1 -0
- configs/tth_cp_even_odd.yaml +0 -49
- data/fixtures/.gitkeep +1 -0
- data/raw/.gitkeep +1 -0
- docs/architecture.md +378 -0
- docs/compatibility.md +22 -0
- docs/configuration.md +96 -0
- docs/design.md +0 -540
- docs/end_to_end_validation.md +30 -0
- docs/export.md +36 -0
- docs/migration.md +295 -0
.github/workflows/ci.yml
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
name: CI
|
| 2 |
+
|
| 3 |
+
on:
|
| 4 |
+
push:
|
| 5 |
+
pull_request:
|
| 6 |
+
|
| 7 |
+
concurrency:
|
| 8 |
+
group: ci-${{ github.workflow }}-${{ github.ref }}
|
| 9 |
+
cancel-in-progress: true
|
| 10 |
+
|
| 11 |
+
jobs:
|
| 12 |
+
lint:
|
| 13 |
+
name: lint
|
| 14 |
+
runs-on: ubuntu-latest
|
| 15 |
+
steps:
|
| 16 |
+
- uses: actions/checkout@v4
|
| 17 |
+
- uses: astral-sh/setup-uv@v6
|
| 18 |
+
with:
|
| 19 |
+
version: "0.8.x"
|
| 20 |
+
enable-cache: true
|
| 21 |
+
- run: uv sync --dev
|
| 22 |
+
- run: uv run ruff check .
|
| 23 |
+
- run: uv run ruff format --check .
|
| 24 |
+
|
| 25 |
+
test:
|
| 26 |
+
name: test
|
| 27 |
+
runs-on: ubuntu-latest
|
| 28 |
+
steps:
|
| 29 |
+
- uses: actions/checkout@v4
|
| 30 |
+
- uses: astral-sh/setup-uv@v6
|
| 31 |
+
with:
|
| 32 |
+
version: "0.8.x"
|
| 33 |
+
enable-cache: true
|
| 34 |
+
- run: uv sync --dev
|
| 35 |
+
- run: uv run pytest
|
| 36 |
+
|
| 37 |
+
test-macos:
|
| 38 |
+
name: test (macOS ROOT-GNN CPU)
|
| 39 |
+
runs-on: macos-14
|
| 40 |
+
steps:
|
| 41 |
+
- uses: actions/checkout@v4
|
| 42 |
+
- uses: astral-sh/setup-uv@v6
|
| 43 |
+
with:
|
| 44 |
+
version: "0.8.x"
|
| 45 |
+
enable-cache: true
|
| 46 |
+
- run: uv sync --dev --extra root-gnn
|
| 47 |
+
- run: uv run pytest tests/unit tests/integration
|
| 48 |
+
|
| 49 |
+
package:
|
| 50 |
+
name: package
|
| 51 |
+
runs-on: ubuntu-latest
|
| 52 |
+
steps:
|
| 53 |
+
- uses: actions/checkout@v4
|
| 54 |
+
- uses: astral-sh/setup-uv@v6
|
| 55 |
+
with:
|
| 56 |
+
version: "0.8.x"
|
| 57 |
+
enable-cache: true
|
| 58 |
+
- run: uv sync --dev
|
| 59 |
+
- run: uv build
|
| 60 |
+
- run: uv run python -m twine check dist/*
|
| 61 |
+
- name: Wheel install smoke test
|
| 62 |
+
shell: bash
|
| 63 |
+
run: |
|
| 64 |
+
set -euo pipefail
|
| 65 |
+
smoke_dir="$(mktemp -d)"
|
| 66 |
+
trap 'rm -rf "$smoke_dir"' EXIT
|
| 67 |
+
uv venv "$smoke_dir/venv"
|
| 68 |
+
uv pip install --python "$smoke_dir/venv/bin/python" dist/*.whl
|
| 69 |
+
(
|
| 70 |
+
cd "$smoke_dir"
|
| 71 |
+
"$smoke_dir/venv/bin/python" -c 'import gnn4colliders; print(gnn4colliders.__version__)'
|
| 72 |
+
"$smoke_dir/venv/bin/gnn4colliders" --help
|
| 73 |
+
"$smoke_dir/venv/bin/gnn4colliders" train --help
|
| 74 |
+
"$smoke_dir/venv/bin/gnn4colliders" export --help
|
| 75 |
+
)
|
| 76 |
+
|
| 77 |
+
onnx:
|
| 78 |
+
name: onnx
|
| 79 |
+
runs-on: ubuntu-latest
|
| 80 |
+
steps:
|
| 81 |
+
- uses: actions/checkout@v4
|
| 82 |
+
- uses: astral-sh/setup-uv@v6
|
| 83 |
+
with:
|
| 84 |
+
version: "0.8.x"
|
| 85 |
+
enable-cache: true
|
| 86 |
+
- run: uv sync --dev --extra root-gnn --extra onnx
|
| 87 |
+
- run: uv run pytest tests/unit/export -v
|
.gitignore
CHANGED
|
@@ -1,16 +1,32 @@
|
|
| 1 |
-
# Python tooling
|
| 2 |
__pycache__/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 3 |
*.py[cod]
|
| 4 |
-
*.egg-info/
|
| 5 |
-
.venv/
|
| 6 |
.pytest_cache/
|
| 7 |
.ruff_cache/
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 8 |
.coverage
|
|
|
|
|
|
|
| 9 |
dist/
|
| 10 |
build/
|
|
|
|
|
|
|
| 11 |
|
| 12 |
# Local data and generated outputs
|
| 13 |
-
data/
|
|
|
|
|
|
|
| 14 |
outputs/
|
| 15 |
-
|
| 16 |
-
|
|
|
|
|
|
|
|
|
| 1 |
__pycache__/
|
| 2 |
+
trainings/
|
| 3 |
+
scores/
|
| 4 |
+
slurm/
|
| 5 |
+
.onnx
|
| 6 |
+
.png
|
| 7 |
+
|
| 8 |
+
# Python tooling
|
| 9 |
*.py[cod]
|
|
|
|
|
|
|
| 10 |
.pytest_cache/
|
| 11 |
.ruff_cache/
|
| 12 |
+
.mypy_cache/
|
| 13 |
+
*.egg-info/
|
| 14 |
+
.venv/
|
| 15 |
+
venv/
|
| 16 |
+
htmlcov/
|
| 17 |
.coverage
|
| 18 |
+
profiles/
|
| 19 |
+
*.trace.json
|
| 20 |
dist/
|
| 21 |
build/
|
| 22 |
+
benchmark-results/
|
| 23 |
+
validation_output/
|
| 24 |
|
| 25 |
# Local data and generated outputs
|
| 26 |
+
data/raw/*
|
| 27 |
+
!data/raw/.gitkeep
|
| 28 |
+
data/processed/
|
| 29 |
outputs/
|
| 30 |
+
checkpoints/
|
| 31 |
+
|
| 32 |
+
# Keep the legacy implementation immutable by convention; do not add generated files there.
|
.python-version
CHANGED
|
@@ -1 +1 @@
|
|
| 1 |
-
3.
|
|
|
|
| 1 |
+
3.12
|
AGENTS.md
CHANGED
|
@@ -1,52 +1,477 @@
|
|
| 1 |
-
# GNN4Colliders
|
| 2 |
|
| 3 |
-
|
| 4 |
-
al., JINST 21 (2026) P08006, a transformer and Deep Sets) on reconstructed
|
| 5 |
-
collider objects. Keep it small and readable: a physicist should be able to follow a
|
| 6 |
-
run from `cli.py` to the model in a few files.
|
| 7 |
|
| 8 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 9 |
|
| 10 |
```text
|
| 11 |
-
|
| 12 |
-
|
| 13 |
-
|
| 14 |
-
|
| 15 |
-
|
| 16 |
```
|
| 17 |
|
| 18 |
-
|
| 19 |
-
|
| 20 |
-
|
| 21 |
-
classes); compare with Table 3 of the paper (`docs/design.md`).
|
| 22 |
|
| 23 |
-
|
| 24 |
|
| 25 |
-
|
| 26 |
-
clearly. No new frameworks (Lightning, Hydra, DGL, PyG) without agreement.
|
| 27 |
-
* Configuration is plain dataclasses in `config.py`; every key has a default
|
| 28 |
-
or is required, and unknown keys are errors. Site settings (accounts,
|
| 29 |
-
paths, `UV_CACHE_DIR`) belong in scripts, not in configs or code.
|
| 30 |
-
* Name domain quantities (`labels`, `weight`, `fold`, `event_index`) instead
|
| 31 |
-
of relying on tensor column positions, except for the documented seven node
|
| 32 |
-
columns.
|
| 33 |
-
* Randomness is seeded explicitly (`train.seed`, `data.seed`); never mutate
|
| 34 |
-
global random state elsewhere. Do not claim bitwise reproducibility on GPUs.
|
| 35 |
-
* The node schema, edge construction, MLP layout and parameter names must
|
| 36 |
-
stay compatible with `Pretrained_GNN/`; a test loads both pretrained models.
|
| 37 |
-
* Changes to features, splits, loss or metrics change physics results: say so
|
| 38 |
-
explicitly and re-check the GamGam benchmark.
|
| 39 |
|
| 40 |
-
|
| 41 |
|
| 42 |
```bash
|
| 43 |
-
|
| 44 |
-
|
| 45 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 46 |
```
|
| 47 |
|
| 48 |
-
|
| 49 |
-
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 50 |
|
| 51 |
-
|
| 52 |
-
results.
|
|
|
|
| 1 |
+
# GNN4Colliders Agent Instructions
|
| 2 |
|
| 3 |
+
## Project purpose
|
|
|
|
|
|
|
|
|
|
| 4 |
|
| 5 |
+
GNN4Colliders is a collider machine-learning package intended to support multiple model architectures over shared collider-physics data infrastructure.
|
| 6 |
+
|
| 7 |
+
The first model family being rewritten is:
|
| 8 |
+
|
| 9 |
+
```text
|
| 10 |
+
root_gnn
|
| 11 |
+
```
|
| 12 |
+
|
| 13 |
+
Future model families may include architectures such as:
|
| 14 |
+
|
| 15 |
+
```text
|
| 16 |
+
root_transformer
|
| 17 |
+
```
|
| 18 |
+
|
| 19 |
+
Do not design shared infrastructure around assumptions that only apply to GNNs.
|
| 20 |
+
|
| 21 |
+
---
|
| 22 |
+
|
| 23 |
+
## Source of truth
|
| 24 |
+
|
| 25 |
+
Before making non-trivial changes, inspect:
|
| 26 |
+
|
| 27 |
+
```text
|
| 28 |
+
docs/architecture.md
|
| 29 |
+
docs/migration.md
|
| 30 |
+
```
|
| 31 |
+
|
| 32 |
+
and the relevant existing source and tests.
|
| 33 |
+
|
| 34 |
+
The `legacy/` tree is a behavioral reference for the rewrite.
|
| 35 |
+
|
| 36 |
+
Unless explicitly instructed otherwise:
|
| 37 |
+
|
| 38 |
+
* do not modify legacy code
|
| 39 |
+
* do not reorganize legacy code
|
| 40 |
+
* do not mechanically copy legacy architecture into the new package
|
| 41 |
+
|
| 42 |
+
When legacy behavior and documentation disagree, identify the discrepancy rather than silently choosing one.
|
| 43 |
+
|
| 44 |
+
---
|
| 45 |
+
|
| 46 |
+
## Package architecture
|
| 47 |
+
|
| 48 |
+
Production Python code belongs under:
|
| 49 |
+
|
| 50 |
+
```text
|
| 51 |
+
src/gnn4colliders/
|
| 52 |
+
```
|
| 53 |
+
|
| 54 |
+
The intended boundaries are:
|
| 55 |
+
|
| 56 |
+
```text
|
| 57 |
+
data/
|
| 58 |
+
Data access, datasets, caching, batching, folds, and generic
|
| 59 |
+
representation-independent data infrastructure.
|
| 60 |
+
|
| 61 |
+
features/
|
| 62 |
+
Collider-domain feature transformations, selections, scaling,
|
| 63 |
+
and derived physics quantities.
|
| 64 |
+
|
| 65 |
+
graphs/
|
| 66 |
+
Graph representation, topology, edge construction, and other
|
| 67 |
+
graph-specific transformations.
|
| 68 |
+
|
| 69 |
+
models/
|
| 70 |
+
Architecture-specific neural network implementations.
|
| 71 |
+
|
| 72 |
+
models/root_gnn/
|
| 73 |
+
ROOT-GNN-specific model components.
|
| 74 |
+
|
| 75 |
+
training/
|
| 76 |
+
Training lifecycle, losses, metrics, checkpointing,
|
| 77 |
+
reproducibility, and distributed-training utilities where
|
| 78 |
+
architecture-independent.
|
| 79 |
+
|
| 80 |
+
inference/
|
| 81 |
+
Prediction, evaluation, output writing, and model export.
|
| 82 |
+
|
| 83 |
+
cli/
|
| 84 |
+
Thin command-line entry points only.
|
| 85 |
+
```
|
| 86 |
+
|
| 87 |
+
Do not place ROOT file-reading logic inside a specific model family unless it is genuinely architecture-specific.
|
| 88 |
+
|
| 89 |
+
Do not place reusable physics-feature logic inside `root_gnn`.
|
| 90 |
+
|
| 91 |
+
Do not place core implementation in notebooks or shell scripts.
|
| 92 |
+
|
| 93 |
+
---
|
| 94 |
+
|
| 95 |
+
## Shared infrastructure vs model-specific code
|
| 96 |
+
|
| 97 |
+
Use this decision rule:
|
| 98 |
+
|
| 99 |
+
**Shared collider or experiment behavior → shared package module.**
|
| 100 |
+
|
| 101 |
+
Examples:
|
| 102 |
+
|
| 103 |
+
```text
|
| 104 |
+
ROOT reading
|
| 105 |
+
event selections
|
| 106 |
+
physics features
|
| 107 |
+
dataset splits
|
| 108 |
+
metrics
|
| 109 |
+
checkpoint orchestration
|
| 110 |
+
```
|
| 111 |
+
|
| 112 |
+
**Representation-specific behavior → representation module.**
|
| 113 |
+
|
| 114 |
+
Examples:
|
| 115 |
+
|
| 116 |
+
```text
|
| 117 |
+
graph topology
|
| 118 |
+
edge construction
|
| 119 |
+
sequence/token construction
|
| 120 |
+
```
|
| 121 |
+
|
| 122 |
+
**Architecture-specific behavior → models/<model_family>/.**
|
| 123 |
+
|
| 124 |
+
Examples:
|
| 125 |
+
|
| 126 |
+
```text
|
| 127 |
+
message-passing network
|
| 128 |
+
transformer encoder
|
| 129 |
+
architecture-specific layers
|
| 130 |
+
```
|
| 131 |
+
|
| 132 |
+
The package should allow future architectures to reuse the same data and physics infrastructure where practical.
|
| 133 |
+
|
| 134 |
+
---
|
| 135 |
+
|
| 136 |
+
## Configuration
|
| 137 |
+
|
| 138 |
+
Experiment configuration should describe intent rather than expose implementation details.
|
| 139 |
+
|
| 140 |
+
Prefer:
|
| 141 |
+
|
| 142 |
+
```yaml
|
| 143 |
+
model:
|
| 144 |
+
type: root_gnn
|
| 145 |
+
```
|
| 146 |
+
|
| 147 |
+
over configuration that directly names Python module paths and class names.
|
| 148 |
+
|
| 149 |
+
Configuration should eventually support composition across concerns such as:
|
| 150 |
|
| 151 |
```text
|
| 152 |
+
data
|
| 153 |
+
model
|
| 154 |
+
task
|
| 155 |
+
trainer
|
| 156 |
+
environment
|
| 157 |
```
|
| 158 |
|
| 159 |
+
Do not hardcode site-specific filesystem paths, CUDA settings, Slurm settings, or machine configuration into model or task definitions.
|
| 160 |
+
|
| 161 |
+
Environment-specific configuration belongs in an environment configuration layer.
|
|
|
|
| 162 |
|
| 163 |
+
---
|
| 164 |
|
| 165 |
+
## CLI design
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 166 |
|
| 167 |
+
The intended user-facing interface is a single project CLI with subcommands conceptually similar to:
|
| 168 |
|
| 169 |
```bash
|
| 170 |
+
gnn4colliders prepare
|
| 171 |
+
gnn4colliders train
|
| 172 |
+
gnn4colliders evaluate
|
| 173 |
+
gnn4colliders predict
|
| 174 |
+
gnn4colliders export
|
| 175 |
+
```
|
| 176 |
+
|
| 177 |
+
CLI modules should be thin.
|
| 178 |
+
|
| 179 |
+
They may:
|
| 180 |
+
|
| 181 |
+
* parse/compose configuration
|
| 182 |
+
* construct application objects
|
| 183 |
+
* invoke library functions
|
| 184 |
+
* handle user-facing errors
|
| 185 |
+
|
| 186 |
+
They should not contain core training, data-processing, graph-building, or model logic.
|
| 187 |
+
|
| 188 |
+
---
|
| 189 |
+
|
| 190 |
+
## Rewrite strategy
|
| 191 |
+
|
| 192 |
+
This repository is being rewritten incrementally.
|
| 193 |
+
|
| 194 |
+
Do not attempt to rewrite the entire legacy repository in one task.
|
| 195 |
+
|
| 196 |
+
For substantial migrations:
|
| 197 |
+
|
| 198 |
+
1. inspect the relevant legacy implementation
|
| 199 |
+
2. identify externally observable behavior
|
| 200 |
+
3. inspect existing characterization/parity tests
|
| 201 |
+
4. state or infer the intended new interface
|
| 202 |
+
5. implement the smallest coherent unit
|
| 203 |
+
6. add or update tests
|
| 204 |
+
7. run relevant validation
|
| 205 |
+
8. report intentional differences and unresolved ambiguity
|
| 206 |
+
|
| 207 |
+
Prefer vertical, testable migration steps over large speculative refactors.
|
| 208 |
+
|
| 209 |
+
---
|
| 210 |
+
|
| 211 |
+
## Behavioral parity
|
| 212 |
+
|
| 213 |
+
The legacy implementation defines important behavior that may need to remain compatible during migration.
|
| 214 |
+
|
| 215 |
+
Important compatibility areas include, where applicable:
|
| 216 |
+
|
| 217 |
+
* input feature definitions
|
| 218 |
+
* feature ordering
|
| 219 |
+
* tensor shapes
|
| 220 |
+
* tensor dtypes
|
| 221 |
+
* graph topology
|
| 222 |
+
* edge feature definitions
|
| 223 |
+
* label semantics
|
| 224 |
+
* event weights
|
| 225 |
+
* fold semantics
|
| 226 |
+
* loss calculations
|
| 227 |
+
* metric definitions
|
| 228 |
+
* model outputs
|
| 229 |
+
* checkpoint compatibility
|
| 230 |
+
* inference outputs
|
| 231 |
+
|
| 232 |
+
Do not alter behavior merely because the legacy implementation appears unusual.
|
| 233 |
+
|
| 234 |
+
If behavior seems incorrect or ambiguous:
|
| 235 |
+
|
| 236 |
+
1. document it
|
| 237 |
+
2. characterize it with a test when possible
|
| 238 |
+
3. separate compatibility from proposed improvement
|
| 239 |
+
|
| 240 |
+
Improvements can be made deliberately after the behavior is understood.
|
| 241 |
+
|
| 242 |
+
---
|
| 243 |
+
|
| 244 |
+
## Testing
|
| 245 |
+
|
| 246 |
+
Tests belong under:
|
| 247 |
+
|
| 248 |
+
```text
|
| 249 |
+
tests/unit/
|
| 250 |
+
tests/integration/
|
| 251 |
+
tests/parity/
|
| 252 |
```
|
| 253 |
|
| 254 |
+
### Unit tests
|
| 255 |
+
|
| 256 |
+
Use unit tests for isolated transformations and components.
|
| 257 |
+
|
| 258 |
+
They should be:
|
| 259 |
+
|
| 260 |
+
* fast
|
| 261 |
+
* deterministic
|
| 262 |
+
* focused
|
| 263 |
+
* independent of large external datasets
|
| 264 |
+
|
| 265 |
+
### Integration tests
|
| 266 |
+
|
| 267 |
+
Use integration tests for small end-to-end workflows such as:
|
| 268 |
+
|
| 269 |
+
```text
|
| 270 |
+
input fixture
|
| 271 |
+
→ features
|
| 272 |
+
→ representation
|
| 273 |
+
→ model
|
| 274 |
+
→ output
|
| 275 |
+
```
|
| 276 |
+
|
| 277 |
+
### Parity tests
|
| 278 |
+
|
| 279 |
+
Use parity tests to compare the rewrite with the legacy implementation.
|
| 280 |
+
|
| 281 |
+
Prefer small deterministic fixtures and reference outputs.
|
| 282 |
+
|
| 283 |
+
Do not weaken or delete parity tests simply to make new code pass.
|
| 284 |
+
|
| 285 |
+
If a parity difference is intentional, document the reason.
|
| 286 |
+
|
| 287 |
+
---
|
| 288 |
+
|
| 289 |
+
## Test data
|
| 290 |
+
|
| 291 |
+
Large production datasets do not belong in the repository.
|
| 292 |
+
|
| 293 |
+
Small deterministic fixtures may live under:
|
| 294 |
+
|
| 295 |
+
```text
|
| 296 |
+
tests/fixtures/
|
| 297 |
+
```
|
| 298 |
+
|
| 299 |
+
Fixtures should be only large enough to exercise relevant behavior.
|
| 300 |
+
|
| 301 |
+
Where possible, create reference outputs for deterministic legacy behavior before replacing that behavior.
|
| 302 |
+
|
| 303 |
+
---
|
| 304 |
+
|
| 305 |
+
## Reproducibility
|
| 306 |
+
|
| 307 |
+
Reproducibility is a project requirement.
|
| 308 |
+
|
| 309 |
+
Randomness should be explicit and controllable.
|
| 310 |
+
|
| 311 |
+
Where relevant, account for:
|
| 312 |
+
|
| 313 |
+
* Python random state
|
| 314 |
+
* NumPy random state
|
| 315 |
+
* PyTorch random state
|
| 316 |
+
* CUDA random state
|
| 317 |
+
* DataLoader workers
|
| 318 |
+
* shuffling
|
| 319 |
+
* sampling
|
| 320 |
+
* data augmentation
|
| 321 |
+
* distributed execution
|
| 322 |
+
|
| 323 |
+
Do not introduce hidden global random-state mutation.
|
| 324 |
+
|
| 325 |
+
Seeds should be passed or configured explicitly.
|
| 326 |
+
|
| 327 |
+
Do not claim bitwise deterministic training unless the execution environment actually guarantees it.
|
| 328 |
+
|
| 329 |
+
---
|
| 330 |
+
|
| 331 |
+
## Code quality
|
| 332 |
+
|
| 333 |
+
Domain meaning must be represented by named fields or schemas, not only by
|
| 334 |
+
positional column indices. New APIs must not require callers to know that a
|
| 335 |
+
particular tensor column means fold, weight, or another domain field.
|
| 336 |
+
|
| 337 |
+
Prefer:
|
| 338 |
+
|
| 339 |
+
* small modules with clear responsibilities
|
| 340 |
+
* typed function signatures
|
| 341 |
+
* explicit inputs and outputs
|
| 342 |
+
* dataclasses or typed configuration where appropriate
|
| 343 |
+
* composition over hidden global state
|
| 344 |
+
* readable names over abbreviations
|
| 345 |
+
* dependency injection over implicit filesystem/environment assumptions
|
| 346 |
+
|
| 347 |
+
Avoid:
|
| 348 |
+
|
| 349 |
+
* repository-relative `sys.path` modifications
|
| 350 |
+
* mutable global configuration
|
| 351 |
+
* hidden singleton state
|
| 352 |
+
* wildcard imports
|
| 353 |
+
* giant utility modules
|
| 354 |
+
* giant training scripts
|
| 355 |
+
* model-specific behavior in generic data code
|
| 356 |
+
* duplicated preprocessing logic
|
| 357 |
+
* unnecessary abstraction introduced before a second use case exists
|
| 358 |
+
|
| 359 |
+
Keep public interfaces intentionally small.
|
| 360 |
+
|
| 361 |
+
---
|
| 362 |
+
|
| 363 |
+
## Dependencies
|
| 364 |
+
|
| 365 |
+
Do not introduce a dependency merely to simplify a small amount of code.
|
| 366 |
+
|
| 367 |
+
Before adding a major framework or runtime dependency, explain why it is necessary.
|
| 368 |
+
|
| 369 |
+
In particular, do not introduce architectural frameworks such as:
|
| 370 |
+
|
| 371 |
+
```text
|
| 372 |
+
PyTorch Lightning
|
| 373 |
+
Kedro
|
| 374 |
+
```
|
| 375 |
+
|
| 376 |
+
unless the task explicitly calls for evaluating or adopting them.
|
| 377 |
+
|
| 378 |
+
Scientific dependencies should be introduced intentionally and with environment compatibility in mind.
|
| 379 |
+
|
| 380 |
+
PyTorch is shared infrastructure for the expected model families and belongs
|
| 381 |
+
in the base package dependencies. Architecture-specific dependencies belong in
|
| 382 |
+
named extras, for example `root-gnn` for DGL. The canonical development
|
| 383 |
+
environment for the active ROOT-GNN rewrite is:
|
| 384 |
+
|
| 385 |
+
```bash
|
| 386 |
+
uv sync --extra root-gnn
|
| 387 |
+
```
|
| 388 |
+
|
| 389 |
+
---
|
| 390 |
+
|
| 391 |
+
## Documentation
|
| 392 |
+
|
| 393 |
+
Update documentation when a change alters:
|
| 394 |
+
|
| 395 |
+
* architecture
|
| 396 |
+
* configuration
|
| 397 |
+
* public interfaces
|
| 398 |
+
* expected workflow
|
| 399 |
+
* compatibility guarantees
|
| 400 |
+
|
| 401 |
+
Do not document speculative functionality as if it already exists.
|
| 402 |
+
|
| 403 |
+
Use:
|
| 404 |
+
|
| 405 |
+
```text
|
| 406 |
+
docs/architecture.md
|
| 407 |
+
```
|
| 408 |
+
|
| 409 |
+
for the target system structure and important architectural decisions.
|
| 410 |
+
|
| 411 |
+
Use:
|
| 412 |
+
|
| 413 |
+
```text
|
| 414 |
+
docs/migration.md
|
| 415 |
+
```
|
| 416 |
+
|
| 417 |
+
for rewrite progress and migration sequencing.
|
| 418 |
+
|
| 419 |
+
---
|
| 420 |
+
|
| 421 |
+
## Scope discipline
|
| 422 |
+
|
| 423 |
+
Do not make unrelated changes.
|
| 424 |
+
|
| 425 |
+
When implementing a task:
|
| 426 |
+
|
| 427 |
+
* modify only the modules necessary for that task
|
| 428 |
+
* do not opportunistically refactor unrelated code
|
| 429 |
+
* do not rename public concepts without a clear reason
|
| 430 |
+
* do not remove compatibility behavior without explicit instruction
|
| 431 |
+
* do not modify generated artifacts unless required
|
| 432 |
+
|
| 433 |
+
If a larger architectural issue is discovered, report it rather than expanding the task automatically.
|
| 434 |
+
|
| 435 |
+
---
|
| 436 |
+
|
| 437 |
+
## Validation before finishing
|
| 438 |
+
|
| 439 |
+
After code changes, run the relevant available checks.
|
| 440 |
+
|
| 441 |
+
As the project matures, the expected baseline should include:
|
| 442 |
+
|
| 443 |
+
```bash
|
| 444 |
+
pytest
|
| 445 |
+
ruff check .
|
| 446 |
+
```
|
| 447 |
+
|
| 448 |
+
ROOT-GNN parity validation requires DGL. A package-wide environment where DGL
|
| 449 |
+
is intentionally absent may skip DGL-dependent tests, but that is incomplete
|
| 450 |
+
ROOT-GNN validation. The required parity gate is:
|
| 451 |
+
|
| 452 |
+
```bash
|
| 453 |
+
GNN4COLLIDERS_REQUIRE_ROOT_GNN=1 pytest
|
| 454 |
+
```
|
| 455 |
+
|
| 456 |
+
The gate must fail when DGL cannot be imported.
|
| 457 |
+
|
| 458 |
+
Run more focused tests first when appropriate.
|
| 459 |
+
|
| 460 |
+
If the full suite is expensive, run the relevant subset and clearly report what was and was not run.
|
| 461 |
+
|
| 462 |
+
Do not claim validation succeeded unless the commands actually succeeded.
|
| 463 |
+
|
| 464 |
+
---
|
| 465 |
+
|
| 466 |
+
## Completion report
|
| 467 |
+
|
| 468 |
+
For non-trivial changes, finish with a concise report containing:
|
| 469 |
+
|
| 470 |
+
* files changed
|
| 471 |
+
* behavior implemented
|
| 472 |
+
* tests/checks run
|
| 473 |
+
* parity status where relevant
|
| 474 |
+
* intentional deviations from legacy behavior
|
| 475 |
+
* unresolved questions or ambiguities
|
| 476 |
|
| 477 |
+
Do not hide failed checks or incomplete behavior.
|
|
|
CHANGELOG.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Changelog
|
| 2 |
+
|
| 3 |
+
## Unreleased
|
| 4 |
+
|
| 5 |
+
### Added
|
| 6 |
+
|
| 7 |
+
- Release validation and CI coverage for the rewritten package.
|
| 8 |
+
- Installed-package Hydra configuration discovery and public import smoke tests.
|
| 9 |
+
|
| 10 |
+
### Compatibility
|
| 11 |
+
|
| 12 |
+
- The `0.1.0` package version remains a static, single-source version exposed
|
| 13 |
+
as `gnn4colliders.__version__`.
|
MANIFEST.in
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
include README.md
|
| 2 |
+
include CHANGELOG.md
|
| 3 |
+
recursive-include docs *.md
|
README.md
CHANGED
|
@@ -6,6 +6,7 @@ tags:
|
|
| 6 |
- particle-physics
|
| 7 |
- event-classification
|
| 8 |
- pytorch
|
|
|
|
| 9 |
datasets:
|
| 10 |
- HWresearch/Delphes
|
| 11 |
---
|
|
|
|
| 6 |
- particle-physics
|
| 7 |
- event-classification
|
| 8 |
- pytorch
|
| 9 |
+
- dgl
|
| 10 |
datasets:
|
| 11 |
- HWresearch/Delphes
|
| 12 |
---
|
README_PROJECT.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GNN4Colliders
|
| 2 |
+
|
| 3 |
+
GNN4Colliders is a collider-machine-learning toolkit. The repository name
|
| 4 |
+
reflects its first production model family, ROOT-GNN; the Python package is
|
| 5 |
+
`gnn4colliders`, and the configuration identifier is `root_gnn`. Shared ROOT
|
| 6 |
+
ingestion, collider features, metadata, tasks, training, inference, and
|
| 7 |
+
distributed utilities are designed so that a future sequence model can reuse
|
| 8 |
+
them without requiring every event to be a graph.
|
| 9 |
+
|
| 10 |
+
```text
|
| 11 |
+
ROOT files -> EventSample -> shared collider features
|
| 12 |
+
├── GraphSample -> ROOT-GNN
|
| 13 |
+
└── future SequenceSample -> ROOT-Transformer
|
| 14 |
+
```
|
| 15 |
+
|
| 16 |
+
The new implementation lives under [`src/gnn4colliders`](src/gnn4colliders/).
|
| 17 |
+
[`legacy/`](legacy/) is a frozen behavioral reference for parity work and
|
| 18 |
+
historical checkpoint investigation, not a supported runtime backend.
|
| 19 |
+
|
| 20 |
+
## Installation
|
| 21 |
+
|
| 22 |
+
The supported development environment is Python 3.12 (`>=3.12,<3.13`). Core
|
| 23 |
+
development is supported on macOS and Linux:
|
| 24 |
+
|
| 25 |
+
```bash
|
| 26 |
+
# macOS (Apple Silicon): CPU ROOT-GNN development and tests
|
| 27 |
+
uv sync --dev --extra root-gnn
|
| 28 |
+
|
| 29 |
+
# Linux x86_64 with an NVIDIA GPU: validated ROOT-GNN development
|
| 30 |
+
uv sync --dev --extra root-gnn
|
| 31 |
+
```
|
| 32 |
+
|
| 33 |
+
The core package can be installed without DGL when only shared data or task
|
| 34 |
+
code is needed. ROOT-GNN models, graph construction, and ROOT-GNN parity tests
|
| 35 |
+
require the `root-gnn` extra. On Linux x86_64, it uses the validated CUDA 12.1
|
| 36 |
+
wheels configured in `pyproject.toml`; a compatible NVIDIA driver is still
|
| 37 |
+
required. On Apple Silicon macOS, it installs the CPU DGL wheel, supporting
|
| 38 |
+
local graph/cache development. The default ROOT-GNN backend performs training
|
| 39 |
+
with native PyTorch graph tensors, so it runs on Apple MPS, NVIDIA CUDA, and
|
| 40 |
+
CPU; DGL remains a cache and legacy-compatibility adapter. Do not add
|
| 41 |
+
site-specific CUDA, Slurm, or filesystem paths to model or task configuration.
|
| 42 |
+
|
| 43 |
+
Use the MPS profile on an Apple Silicon Mac:
|
| 44 |
+
|
| 45 |
+
```bash
|
| 46 |
+
uv run gnn4colliders train environment=macos
|
| 47 |
+
```
|
| 48 |
+
|
| 49 |
+
## Data samples
|
| 50 |
+
|
| 51 |
+
ROOT inputs are available from the
|
| 52 |
+
[HWresearch/Delphes dataset](https://huggingface.co/datasets/HWresearch/Delphes).
|
| 53 |
+
Download the 64-event smoke-test sample with the Hugging Face CLI:
|
| 54 |
+
|
| 55 |
+
```bash
|
| 56 |
+
hf download HWresearch/Delphes testing/ttH_NLO_64.root \
|
| 57 |
+
--repo-type dataset --local-dir data/raw
|
| 58 |
+
```
|
| 59 |
+
|
| 60 |
+
The sample is `data/raw/testing/ttH_NLO_64.root`, has tree name `output`, and
|
| 61 |
+
is suitable for checking the prepare/train workflow. The dataset also provides
|
| 62 |
+
larger process-specific ROOT samples under `samples/`, derived datasets under
|
| 63 |
+
`derived/`, and analysis-specific ntuples under `analyses/`. These data are
|
| 64 |
+
intentionally ignored by Git; inspect a selected ROOT file's tree and branches
|
| 65 |
+
before writing its preparation configuration.
|
| 66 |
+
|
| 67 |
+
## Quick start
|
| 68 |
+
|
| 69 |
+
Prepare a graph cache from a ROOT tree. The feature specifications below are
|
| 70 |
+
illustrative placeholders; replace them with the branches in the input tree.
|
| 71 |
+
The full preparation interface is documented in
|
| 72 |
+
[`docs/configuration.md`](docs/configuration.md).
|
| 73 |
+
|
| 74 |
+
```bash
|
| 75 |
+
uv run gnn4colliders prepare \
|
| 76 |
+
data.files=[data/events.root] \
|
| 77 |
+
data.tree_name=Events \
|
| 78 |
+
data.cache.path=cache/events.pt \
|
| 79 |
+
'data.feature_branches=[["jet_pt"],["jet_eta"],["jet_phi"],CALC_E,[1.0],[0.0],NODE_TYPE]' \
|
| 80 |
+
data.object_types=[vector] \
|
| 81 |
+
data.scales=[1,1,1,1,1,1,1]
|
| 82 |
+
```
|
| 83 |
+
|
| 84 |
+
Train, evaluate, and predict from that cache:
|
| 85 |
+
|
| 86 |
+
```bash
|
| 87 |
+
uv run gnn4colliders train \
|
| 88 |
+
data.cache.path=cache/events.pt \
|
| 89 |
+
trainer.max_epochs=1 \
|
| 90 |
+
environment.output_root=outputs/pretraining_multiclass
|
| 91 |
+
|
| 92 |
+
uv run gnn4colliders evaluate \
|
| 93 |
+
data.cache.path=cache/events.pt \
|
| 94 |
+
inference.checkpoint=outputs/pretraining_multiclass/checkpoints/epoch_0000.pt
|
| 95 |
+
|
| 96 |
+
uv run gnn4colliders predict \
|
| 97 |
+
data.cache.path=cache/events.pt \
|
| 98 |
+
inference.checkpoint=outputs/pretraining_multiclass/checkpoints/epoch_0000.pt \
|
| 99 |
+
inference.output=outputs/pretraining_multiclass/predictions.npz
|
| 100 |
+
```
|
| 101 |
+
|
| 102 |
+
For a dependency-complete, temporary-data version of this flow, run
|
| 103 |
+
`uv run python scripts/dev/smoke_end_to_end.py`.
|
| 104 |
+
|
| 105 |
+
## Core concepts
|
| 106 |
+
|
| 107 |
+
`EventSample` is the architecture-neutral event boundary. It contains the
|
| 108 |
+
selected `objects`, `label`, `global_features`, and named `EventMetadata`.
|
| 109 |
+
Metadata includes `fold`, `weight`, and stable `sample_id`; callers should not
|
| 110 |
+
interpret public `tracking[:, N]` columns. Legacy tracking mappings exist only
|
| 111 |
+
at compatibility boundaries.
|
| 112 |
+
|
| 113 |
+
The ROOT-GNN adapter converts shared features to a directed, fully connected
|
| 114 |
+
graph with no self-loops: an event with `N` nodes has `N * (N - 1)` edges.
|
| 115 |
+
Node columns are, in order, `pt`, `eta`, `phi`, `energy`, `btag`, `charge`,
|
| 116 |
+
and `node_type`. Edge columns are `deta`, wrapped `dphi`, and `dR`.
|
| 117 |
+
Object collections are concatenated in configured object-type order. The
|
| 118 |
+
compatibility energy is `pt * cosh(eta)` before per-column scaling.
|
| 119 |
+
|
| 120 |
+
`GraphSampleCache` stores processed graph samples and schema metadata. It is a
|
| 121 |
+
Level-2 graph cache, not the universal event cache. Feature, graph, and cache
|
| 122 |
+
schema versions are checked when loading; incompatible versions fail before
|
| 123 |
+
training.
|
| 124 |
+
|
| 125 |
+
## ROOT-GNN training and transfer
|
| 126 |
+
|
| 127 |
+
`EdgeNetwork` encodes node, edge, and global features, performs iterative
|
| 128 |
+
edge/node/global message passing, decodes a graph representation, and applies
|
| 129 |
+
the classifier. Its output is raw logits; sigmoid or softmax is task-owned.
|
| 130 |
+
|
| 131 |
+
Multiclass pretraining uses the semantic `model=root_gnn/edge_network` and
|
| 132 |
+
`task=pretraining_multiclass` groups:
|
| 133 |
+
|
| 134 |
+
```bash
|
| 135 |
+
uv run gnn4colliders train \
|
| 136 |
+
data.cache.path=cache/events.pt \
|
| 137 |
+
model=root_gnn/edge_network task=pretraining_multiclass \
|
| 138 |
+
trainer.max_epochs=20 data.batch_size=64 \
|
| 139 |
+
environment.output_root=outputs/pretraining_multiclass
|
| 140 |
+
```
|
| 141 |
+
|
| 142 |
+
Fine-tuning is a separate workflow. It loads a pretrained backbone, replaces
|
| 143 |
+
the classifier, and creates a new task/head optimizer:
|
| 144 |
+
|
| 145 |
+
```bash
|
| 146 |
+
uv run gnn4colliders train \
|
| 147 |
+
data.cache.path=cache/target.pt \
|
| 148 |
+
model=root_gnn/fine_tuned_edge_network \
|
| 149 |
+
task=binary_classification \
|
| 150 |
+
checkpoint.pretrained=/path/to/pretrained.pt \
|
| 151 |
+
model.freeze_backbone=true \
|
| 152 |
+
trainer.max_epochs=10
|
| 153 |
+
```
|
| 154 |
+
|
| 155 |
+
Set `model.freeze_backbone=false` to train the reused backbone as well.
|
| 156 |
+
Transfer learning is not resume training:
|
| 157 |
+
|
| 158 |
+
| Workflow | Meaning | Restored state |
|
| 159 |
+
| --- | --- | --- |
|
| 160 |
+
| Resume | Continue the same task/run | model, optimizer, scheduler, trainer, early stopping, and RNG state when present |
|
| 161 |
+
| Transfer | Start a new task from a pretrained backbone | model weights only; new classifier and optimizer |
|
| 162 |
+
|
| 163 |
+
Resume example:
|
| 164 |
+
|
| 165 |
+
```bash
|
| 166 |
+
uv run gnn4colliders train \
|
| 167 |
+
data.cache.path=cache/events.pt \
|
| 168 |
+
checkpoint.resume=outputs/pretraining_multiclass/checkpoints/epoch_0000.pt \
|
| 169 |
+
trainer.max_epochs=20
|
| 170 |
+
```
|
| 171 |
+
|
| 172 |
+
Validation is evaluated each epoch and drives scheduling/early stopping;
|
| 173 |
+
`test` remains held out. Evaluation computes task metrics over the complete
|
| 174 |
+
selected split, including weighted ROC AUC where defined:
|
| 175 |
+
|
| 176 |
+
```bash
|
| 177 |
+
uv run gnn4colliders evaluate \
|
| 178 |
+
data.cache.path=cache/events.pt \
|
| 179 |
+
inference.split=test \
|
| 180 |
+
inference.checkpoint=/path/to/checkpoint.pt
|
| 181 |
+
```
|
| 182 |
+
|
| 183 |
+
Prediction writes a named compressed NPZ. Labeled data includes `labels`;
|
| 184 |
+
`fold` and `weight` are included when available. Every result includes
|
| 185 |
+
`sample_id`, `logits`, `scores`, and `predictions`:
|
| 186 |
+
|
| 187 |
+
```bash
|
| 188 |
+
uv run gnn4colliders predict \
|
| 189 |
+
data.cache.path=cache/events.pt \
|
| 190 |
+
inference.checkpoint=/path/to/checkpoint.pt \
|
| 191 |
+
inference.output=outputs/predictions.npz
|
| 192 |
+
```
|
| 193 |
+
|
| 194 |
+
Optional Python-level ROOT writing is provided by
|
| 195 |
+
`gnn4colliders.inference.write_root_scores`. It clones the selected tree,
|
| 196 |
+
adds `score` (or `score_class_N`), and writes `selection_pass`; IDs ending in
|
| 197 |
+
`:<entry>` preserve alignment and unselected entries receive NaN scores. The
|
| 198 |
+
CLI currently exposes NPZ output only.
|
| 199 |
+
|
| 200 |
+
The supported legacy checkpoint, metadata, and output boundary is documented
|
| 201 |
+
in [`docs/compatibility.md`](docs/compatibility.md). New code should use named
|
| 202 |
+
metadata fields; positional tracking is accepted only by the explicit
|
| 203 |
+
compatibility adapter.
|
| 204 |
+
|
| 205 |
+
### ONNX export
|
| 206 |
+
|
| 207 |
+
Install the optional export dependencies and export a prepared graph-cache
|
| 208 |
+
checkpoint with numerical ONNX validation:
|
| 209 |
+
|
| 210 |
+
```bash
|
| 211 |
+
uv sync --extra root-gnn --extra onnx
|
| 212 |
+
uv run gnn4colliders export \
|
| 213 |
+
export.checkpoint=/path/to/checkpoint.pt \
|
| 214 |
+
export.output=model.onnx \
|
| 215 |
+
data.cache.path=/path/to/graph-cache.pt
|
| 216 |
+
```
|
| 217 |
+
|
| 218 |
+
The model accepts processed graph tensors and returns raw logits. See
|
| 219 |
+
[`docs/export.md`](docs/export.md) for the tensor contract and limitations.
|
| 220 |
+
|
| 221 |
+
## Configuration and environments
|
| 222 |
+
|
| 223 |
+
Hydra groups are `data`, `model`, `task`, `trainer`, `checkpoint`,
|
| 224 |
+
`inference`, `environment`, and `distributed`. Use configuration for a new
|
| 225 |
+
experiment and Python for new behavior. Examples:
|
| 226 |
+
|
| 227 |
+
```bash
|
| 228 |
+
uv run gnn4colliders train trainer.max_epochs=50 data.batch_size=64
|
| 229 |
+
uv run gnn4colliders train environment=perlmutter environment.device=cuda
|
| 230 |
+
uv run gnn4colliders train distributed=ddp environment=perlmutter
|
| 231 |
+
```
|
| 232 |
+
|
| 233 |
+
Each run writes a resolved configuration to
|
| 234 |
+
`<environment.output_root>/resolved_config.yaml`. See
|
| 235 |
+
[`docs/configuration.md`](docs/configuration.md) for the group reference and
|
| 236 |
+
[`docs/perlmutter.md`](docs/perlmutter.md) for launch examples.
|
| 237 |
+
|
| 238 |
+
## Distributed execution and reproducibility
|
| 239 |
+
|
| 240 |
+
Launch DDP with `torchrun` or the provided Slurm wrappers. `data.batch_size`
|
| 241 |
+
and `data.num_workers` are per process, so the ordinary effective batch size
|
| 242 |
+
is `batch_size * world_size`. Training shards may be padded for equal steps;
|
| 243 |
+
validation and prediction are unpadded. Rank 0 writes shared checkpoints,
|
| 244 |
+
configs, and predictions, and metrics/results are gathered across ranks.
|
| 245 |
+
|
| 246 |
+
The configured seed controls initialization and deterministic local loader
|
| 247 |
+
ordering; distributed process seeds are rank-offset and samplers use
|
| 248 |
+
`set_epoch`. CPU runs are reproducible for fixed inputs and environment. GPU
|
| 249 |
+
kernels, DGL, and distributed scheduling can remain nondeterministic, so the
|
| 250 |
+
project does not promise bitwise GPU reproducibility.
|
| 251 |
+
|
| 252 |
+
## Development and validation
|
| 253 |
+
|
| 254 |
+
```bash
|
| 255 |
+
uv run pytest
|
| 256 |
+
uv run pytest tests/unit
|
| 257 |
+
GNN4COLLIDERS_REQUIRE_ROOT_GNN=1 uv run pytest tests/parity -v
|
| 258 |
+
uv run ruff check .
|
| 259 |
+
uv run ruff format --check .
|
| 260 |
+
uv run python benchmarks/benchmark_preprocessing.py
|
| 261 |
+
uv run python benchmarks/benchmark_training.py --device cpu
|
| 262 |
+
```
|
| 263 |
+
|
| 264 |
+
Unit tests cover isolated components, integration tests cover small workflows,
|
| 265 |
+
and parity tests compare deterministic behavior with the frozen legacy
|
| 266 |
+
reference. Performance guidance and measured caveats are in
|
| 267 |
+
[`docs/performance.md`](docs/performance.md) and
|
| 268 |
+
[`benchmarks/README.md`](benchmarks/README.md).
|
| 269 |
+
See [`docs/testing.md`](docs/testing.md) for test layers, optional dependency
|
| 270 |
+
markers, and package smoke validation.
|
| 271 |
+
|
| 272 |
+
## Architecture and migration status
|
| 273 |
+
|
| 274 |
+
See [`docs/architecture.md`](docs/architecture.md) for responsibility
|
| 275 |
+
boundaries and the future sequence-model extension point. See
|
| 276 |
+
[`docs/migration.md`](docs/migration.md) for the migration matrix,
|
| 277 |
+
intentional redesigns, compatibility limits, and deferred work.
|
| 278 |
+
|
| 279 |
+
ROOT-GNN v1 covers ROOT preparation, validated feature/graph/model/task
|
| 280 |
+
behavior, training, fine-tuning, checkpoint resume, evaluation, prediction,
|
| 281 |
+
single-process/DDP execution, and validated ONNX export. Streaming distributed
|
| 282 |
+
output, legacy cleanup, and ROOT-Transformer remain follow-up work.
|
benchmarks/README.md
ADDED
|
@@ -0,0 +1,48 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Performance benchmarks
|
| 2 |
+
|
| 3 |
+
These scripts use fixed synthetic inputs, explicit seeds, warmup iterations,
|
| 4 |
+
and JSON-lines output. GPU timings synchronize after every measured operation;
|
| 5 |
+
warmup and one-time setup costs are excluded from steady-state numbers.
|
| 6 |
+
|
| 7 |
+
```bash
|
| 8 |
+
uv run python benchmarks/benchmark_preprocessing.py
|
| 9 |
+
uv run python benchmarks/benchmark_dataloader.py
|
| 10 |
+
uv run python benchmarks/benchmark_training.py --device cpu
|
| 11 |
+
uv run python benchmarks/benchmark_inference.py --device cpu
|
| 12 |
+
uv run python benchmarks/benchmark_training.py --device cuda --profile
|
| 13 |
+
```
|
| 14 |
+
|
| 15 |
+
DGL-dependent scripts report a structured `skipped` result when the optional
|
| 16 |
+
`root-gnn` extra is absent. Profiler traces go under `profiles/` and are not
|
| 17 |
+
committed.
|
| 18 |
+
|
| 19 |
+
## Baseline measurements
|
| 20 |
+
|
| 21 |
+
The portable baseline in this checkout uses Python 3.12.13, PyTorch 2.2.2+cu121,
|
| 22 |
+
DGL 2.4.0+cu121, CPU, `nodes=32`, `iterations=10`, and `warmup=3`. Exact timings are machine
|
| 23 |
+
dependent; the JSON output from a local run is authoritative.
|
| 24 |
+
|
| 25 |
+
| Measurement | Mean | Median |
|
| 26 |
+
| --- | ---: | ---: |
|
| 27 |
+
| feature construction | 0.47 ms/event | 0.46 ms/event |
|
| 28 |
+
| edge features | 0.11 ms/graph | 0.11 ms/graph |
|
| 29 |
+
| graph-sample loader | 1.65 ms/iteration | 1.65 ms/iteration |
|
| 30 |
+
| ROOT-GNN training step | 21.19 ms/step | 5.32 ms/step |
|
| 31 |
+
| ROOT-GNN inference | 1.48 ms/graph | 1.49 ms/graph |
|
| 32 |
+
|
| 33 |
+
On the available NVIDIA A100-PCIE-40GB with CUDA 12.1 runtime, the same small
|
| 34 |
+
synthetic ROOT-GNN benchmark measured 6.41 ms/step (17.3 MiB peak allocated)
|
| 35 |
+
and 2.00 ms/graph. These are microbenchmarks, not production-workload claims.
|
| 36 |
+
|
| 37 |
+
DGL is available in this environment. The training mean is skewed by one CPU
|
| 38 |
+
warmup-adjacent outlier; median is the more useful steady-state indicator. DDP
|
| 39 |
+
measurements require a multi-process run and are not inferred from CPU numbers.
|
| 40 |
+
|
| 41 |
+
The topology cache is bounded to 32 node-count/device/policy entries and only
|
| 42 |
+
stores reusable index tensors, never event-specific edge features.
|
| 43 |
+
|
| 44 |
+
On the same CPU, a direct topology microbenchmark for 64 nodes measured
|
| 45 |
+
201.6 microseconds per cold construction versus 10.5 microseconds for a warm
|
| 46 |
+
cache lookup (about 19x for this isolated operation). This is a targeted
|
| 47 |
+
index-construction result, not an end-to-end training speedup; graph feature
|
| 48 |
+
construction and DGL message passing remain separate costs.
|
benchmarks/__init__.py
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
"""Standalone, reproducible performance measurements for GNN4Colliders."""
|
benchmarks/_common.py
ADDED
|
@@ -0,0 +1,70 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Small helpers shared by benchmark entry points."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import argparse
|
| 6 |
+
import json
|
| 7 |
+
import platform
|
| 8 |
+
import statistics
|
| 9 |
+
import time
|
| 10 |
+
from collections.abc import Callable
|
| 11 |
+
from typing import Any
|
| 12 |
+
|
| 13 |
+
import torch
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def common_parser(description: str) -> argparse.ArgumentParser:
|
| 17 |
+
parser = argparse.ArgumentParser(description=description)
|
| 18 |
+
parser.add_argument("--iterations", type=int, default=20)
|
| 19 |
+
parser.add_argument("--warmup", type=int, default=5)
|
| 20 |
+
parser.add_argument("--device", default="cpu")
|
| 21 |
+
parser.add_argument("--seed", type=int, default=1234)
|
| 22 |
+
return parser
|
| 23 |
+
|
| 24 |
+
|
| 25 |
+
def synchronize(device: torch.device) -> None:
|
| 26 |
+
if device.type == "cuda":
|
| 27 |
+
torch.cuda.synchronize(device)
|
| 28 |
+
|
| 29 |
+
|
| 30 |
+
def measure(
|
| 31 |
+
operation: Callable[[], Any], *, iterations: int, warmup: int, device: torch.device
|
| 32 |
+
) -> dict[str, float]:
|
| 33 |
+
for _ in range(warmup):
|
| 34 |
+
operation()
|
| 35 |
+
synchronize(device)
|
| 36 |
+
durations = []
|
| 37 |
+
for _ in range(iterations):
|
| 38 |
+
start = time.perf_counter()
|
| 39 |
+
operation()
|
| 40 |
+
synchronize(device)
|
| 41 |
+
durations.append(time.perf_counter() - start)
|
| 42 |
+
return {
|
| 43 |
+
"mean_ms": statistics.mean(durations) * 1000,
|
| 44 |
+
"median_ms": statistics.median(durations) * 1000,
|
| 45 |
+
"min_ms": min(durations) * 1000,
|
| 46 |
+
"max_ms": max(durations) * 1000,
|
| 47 |
+
"stdev_ms": statistics.stdev(durations) * 1000 if len(durations) > 1 else 0.0,
|
| 48 |
+
}
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
def metadata(device: torch.device) -> dict[str, Any]:
|
| 52 |
+
result: dict[str, Any] = {
|
| 53 |
+
"python": platform.python_version(),
|
| 54 |
+
"torch": torch.__version__,
|
| 55 |
+
"device": str(device),
|
| 56 |
+
"cuda_available": torch.cuda.is_available(),
|
| 57 |
+
}
|
| 58 |
+
if device.type == "cuda" and torch.cuda.is_available():
|
| 59 |
+
result["gpu"] = torch.cuda.get_device_name(device)
|
| 60 |
+
try:
|
| 61 |
+
import dgl
|
| 62 |
+
|
| 63 |
+
result["dgl"] = dgl.__version__
|
| 64 |
+
except ImportError:
|
| 65 |
+
result["dgl"] = None
|
| 66 |
+
return result
|
| 67 |
+
|
| 68 |
+
|
| 69 |
+
def report(name: str, values: dict[str, Any]) -> None:
|
| 70 |
+
print(json.dumps({"benchmark": name, **values}, sort_keys=True))
|
benchmarks/benchmark_dataloader.py
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Measure deterministic loader/batch construction when ROOT-GNN is installed."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
from ._common import common_parser, measure, metadata, report
|
| 9 |
+
except ImportError:
|
| 10 |
+
from _common import common_parser, measure, metadata, report
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def main() -> None:
|
| 14 |
+
parser = common_parser(__doc__)
|
| 15 |
+
parser.add_argument("--batch-size", type=int, default=8)
|
| 16 |
+
parser.add_argument("--samples", type=int, default=64)
|
| 17 |
+
args = parser.parse_args()
|
| 18 |
+
device = torch.device(args.device)
|
| 19 |
+
try:
|
| 20 |
+
import dgl
|
| 21 |
+
|
| 22 |
+
from gnn4colliders.data import (
|
| 23 |
+
EventMetadata,
|
| 24 |
+
GraphDataLoader,
|
| 25 |
+
GraphDataset,
|
| 26 |
+
GraphSample,
|
| 27 |
+
)
|
| 28 |
+
except ImportError:
|
| 29 |
+
report(
|
| 30 |
+
"dataloader",
|
| 31 |
+
{**metadata(device), "status": "skipped: install root-gnn extra"},
|
| 32 |
+
)
|
| 33 |
+
return
|
| 34 |
+
samples = []
|
| 35 |
+
for index in range(args.samples):
|
| 36 |
+
graph = dgl.graph((torch.tensor([0, 1]), torch.tensor([1, 0])), num_nodes=2)
|
| 37 |
+
graph.ndata["features"] = torch.ones(2, 7)
|
| 38 |
+
graph.edata["features"] = torch.ones(2, 3)
|
| 39 |
+
samples.append(
|
| 40 |
+
GraphSample(
|
| 41 |
+
graph, torch.tensor(index % 2), None, EventMetadata(0, 1.0, str(index))
|
| 42 |
+
)
|
| 43 |
+
)
|
| 44 |
+
loader = GraphDataLoader(GraphDataset(samples), args.batch_size)
|
| 45 |
+
result = measure(
|
| 46 |
+
lambda: list(loader),
|
| 47 |
+
iterations=args.iterations,
|
| 48 |
+
warmup=args.warmup,
|
| 49 |
+
device=device,
|
| 50 |
+
)
|
| 51 |
+
report(
|
| 52 |
+
"dataloader",
|
| 53 |
+
{
|
| 54 |
+
**metadata(device),
|
| 55 |
+
**result,
|
| 56 |
+
"batch_size": args.batch_size,
|
| 57 |
+
"batches_per_second": 1000 / result["mean_ms"],
|
| 58 |
+
},
|
| 59 |
+
)
|
| 60 |
+
|
| 61 |
+
|
| 62 |
+
if __name__ == "__main__":
|
| 63 |
+
main()
|
benchmarks/benchmark_inference.py
ADDED
|
@@ -0,0 +1,52 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Measure ROOT-GNN inference with fixed synthetic graph input."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
try:
|
| 8 |
+
from ._common import common_parser, measure, metadata, report
|
| 9 |
+
except ImportError:
|
| 10 |
+
from _common import common_parser, measure, metadata, report
|
| 11 |
+
|
| 12 |
+
|
| 13 |
+
def main() -> None:
|
| 14 |
+
parser = common_parser(__doc__)
|
| 15 |
+
args = parser.parse_args()
|
| 16 |
+
device = torch.device(args.device)
|
| 17 |
+
try:
|
| 18 |
+
import dgl
|
| 19 |
+
|
| 20 |
+
from gnn4colliders.models.root_gnn import EdgeNetwork
|
| 21 |
+
except ImportError:
|
| 22 |
+
report(
|
| 23 |
+
"inference",
|
| 24 |
+
{**metadata(device), "status": "skipped: install root-gnn extra"},
|
| 25 |
+
)
|
| 26 |
+
return
|
| 27 |
+
graph = dgl.graph((torch.tensor([0, 1]), torch.tensor([1, 0])), num_nodes=2).to(
|
| 28 |
+
device
|
| 29 |
+
)
|
| 30 |
+
graph.ndata["features"] = torch.ones(2, 7, device=device)
|
| 31 |
+
graph.edata["features"] = torch.ones(2, 3, device=device)
|
| 32 |
+
model = (
|
| 33 |
+
EdgeNetwork(graph, None, hid_size=16, out_size=2, n_layers=1, n_proc_steps=1)
|
| 34 |
+
.to(device)
|
| 35 |
+
.eval()
|
| 36 |
+
)
|
| 37 |
+
|
| 38 |
+
def infer() -> None:
|
| 39 |
+
with torch.inference_mode():
|
| 40 |
+
model(graph)
|
| 41 |
+
|
| 42 |
+
result = measure(
|
| 43 |
+
infer, iterations=args.iterations, warmup=args.warmup, device=device
|
| 44 |
+
)
|
| 45 |
+
report(
|
| 46 |
+
"inference",
|
| 47 |
+
{**metadata(device), **result, "graphs_per_second": 1000 / result["mean_ms"]},
|
| 48 |
+
)
|
| 49 |
+
|
| 50 |
+
|
| 51 |
+
if __name__ == "__main__":
|
| 52 |
+
main()
|
benchmarks/benchmark_preprocessing.py
ADDED
|
@@ -0,0 +1,61 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Measure shared feature and graph preprocessing on fixed synthetic events."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
import torch
|
| 6 |
+
|
| 7 |
+
from gnn4colliders.features import build_node_features
|
| 8 |
+
from gnn4colliders.graphs import build_edge_features, fully_connected_edges
|
| 9 |
+
|
| 10 |
+
try:
|
| 11 |
+
from ._common import common_parser, measure, metadata, report
|
| 12 |
+
except ImportError:
|
| 13 |
+
from _common import common_parser, measure, metadata, report
|
| 14 |
+
|
| 15 |
+
|
| 16 |
+
def main() -> None:
|
| 17 |
+
parser = common_parser(__doc__)
|
| 18 |
+
parser.add_argument("--nodes", type=int, default=32)
|
| 19 |
+
args = parser.parse_args()
|
| 20 |
+
torch.manual_seed(args.seed)
|
| 21 |
+
event = {
|
| 22 |
+
"pt": torch.arange(args.nodes, dtype=torch.float32) + 1,
|
| 23 |
+
"eta": torch.linspace(-2, 2, args.nodes),
|
| 24 |
+
"phi": torch.linspace(-3.0, 3.0, args.nodes),
|
| 25 |
+
}
|
| 26 |
+
branches = [["pt"], ["eta"], ["phi"], "CALC_E", [1.0], [0.0], "NODE_TYPE"]
|
| 27 |
+
object_types = ["vector"]
|
| 28 |
+
scales = [1.0] * 7
|
| 29 |
+
device = torch.device(args.device)
|
| 30 |
+
|
| 31 |
+
feature_result = measure(
|
| 32 |
+
lambda: build_node_features(event, branches, object_types, scales),
|
| 33 |
+
iterations=args.iterations,
|
| 34 |
+
warmup=args.warmup,
|
| 35 |
+
device=device,
|
| 36 |
+
)
|
| 37 |
+
nodes = build_node_features(event, branches, object_types, scales)[0]
|
| 38 |
+
src, dst = fully_connected_edges(args.nodes)
|
| 39 |
+
graph_result = measure(
|
| 40 |
+
lambda: build_edge_features(nodes, src, dst, eta_index=1, phi_index=2),
|
| 41 |
+
iterations=args.iterations,
|
| 42 |
+
warmup=args.warmup,
|
| 43 |
+
device=device,
|
| 44 |
+
)
|
| 45 |
+
base = {**metadata(device), "nodes": args.nodes, "iterations": args.iterations}
|
| 46 |
+
report(
|
| 47 |
+
"feature_construction",
|
| 48 |
+
{
|
| 49 |
+
**base,
|
| 50 |
+
**feature_result,
|
| 51 |
+
"events_per_second": 1000 / feature_result["mean_ms"],
|
| 52 |
+
},
|
| 53 |
+
)
|
| 54 |
+
report(
|
| 55 |
+
"edge_features",
|
| 56 |
+
{**base, **graph_result, "graphs_per_second": 1000 / graph_result["mean_ms"]},
|
| 57 |
+
)
|
| 58 |
+
|
| 59 |
+
|
| 60 |
+
if __name__ == "__main__":
|
| 61 |
+
main()
|
benchmarks/benchmark_training.py
ADDED
|
@@ -0,0 +1,73 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
"""Measure a short ROOT-GNN training section and optionally emit a profiler trace."""
|
| 2 |
+
|
| 3 |
+
from __future__ import annotations
|
| 4 |
+
|
| 5 |
+
from pathlib import Path
|
| 6 |
+
|
| 7 |
+
import torch
|
| 8 |
+
|
| 9 |
+
try:
|
| 10 |
+
from ._common import common_parser, measure, metadata, report
|
| 11 |
+
except ImportError:
|
| 12 |
+
from _common import common_parser, measure, metadata, report
|
| 13 |
+
|
| 14 |
+
|
| 15 |
+
def main() -> None:
|
| 16 |
+
parser = common_parser(__doc__)
|
| 17 |
+
parser.add_argument("--batch-size", type=int, default=4)
|
| 18 |
+
parser.add_argument("--profile", action="store_true")
|
| 19 |
+
parser.add_argument("--profile-dir", default="profiles")
|
| 20 |
+
args = parser.parse_args()
|
| 21 |
+
device = torch.device(args.device)
|
| 22 |
+
try:
|
| 23 |
+
import dgl
|
| 24 |
+
|
| 25 |
+
from gnn4colliders.models.root_gnn import EdgeNetwork
|
| 26 |
+
except ImportError:
|
| 27 |
+
report(
|
| 28 |
+
"training",
|
| 29 |
+
{**metadata(device), "status": "skipped: install root-gnn extra"},
|
| 30 |
+
)
|
| 31 |
+
return
|
| 32 |
+
torch.manual_seed(args.seed)
|
| 33 |
+
graph = dgl.graph((torch.tensor([0, 1]), torch.tensor([1, 0])), num_nodes=2).to(
|
| 34 |
+
device
|
| 35 |
+
)
|
| 36 |
+
graph.ndata["features"] = torch.randn(2, 7, device=device)
|
| 37 |
+
graph.edata["features"] = torch.randn(2, 3, device=device)
|
| 38 |
+
model = EdgeNetwork(
|
| 39 |
+
graph, None, hid_size=16, out_size=2, n_layers=1, n_proc_steps=1
|
| 40 |
+
).to(device)
|
| 41 |
+
optimizer = torch.optim.Adam(model.parameters(), lr=1e-3)
|
| 42 |
+
|
| 43 |
+
def step() -> None:
|
| 44 |
+
optimizer.zero_grad(set_to_none=True)
|
| 45 |
+
loss = model(graph).square().mean()
|
| 46 |
+
loss.backward()
|
| 47 |
+
optimizer.step()
|
| 48 |
+
|
| 49 |
+
if args.profile:
|
| 50 |
+
profile_dir = Path(args.profile_dir)
|
| 51 |
+
profile_dir.mkdir(parents=True, exist_ok=True)
|
| 52 |
+
with torch.profiler.profile(record_shapes=True, profile_memory=True) as prof:
|
| 53 |
+
for _ in range(args.warmup + args.iterations):
|
| 54 |
+
step()
|
| 55 |
+
prof.export_chrome_trace(str(profile_dir / "training.trace.json"))
|
| 56 |
+
result = measure(
|
| 57 |
+
step, iterations=args.iterations, warmup=args.warmup, device=device
|
| 58 |
+
)
|
| 59 |
+
report(
|
| 60 |
+
"training_step",
|
| 61 |
+
{
|
| 62 |
+
**metadata(device),
|
| 63 |
+
**result,
|
| 64 |
+
"steps_per_second": 1000 / result["mean_ms"],
|
| 65 |
+
"peak_memory_mb": torch.cuda.max_memory_allocated(device) / 2**20
|
| 66 |
+
if device.type == "cuda"
|
| 67 |
+
else 0.0,
|
| 68 |
+
},
|
| 69 |
+
)
|
| 70 |
+
|
| 71 |
+
|
| 72 |
+
if __name__ == "__main__":
|
| 73 |
+
main()
|
configs/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/data/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/delphes_contrastive.yaml
DELETED
|
@@ -1,81 +0,0 @@
|
|
| 1 |
-
# Contrastive (SimCLR) pretraining of the object transformer on the 12
|
| 2 |
-
# Delphes samples of configs/delphes_jepa.yaml, without their labels (same
|
| 3 |
-
# event cache): two random views of every event (rotation, eta flip, energy
|
| 4 |
-
# scale, object dropout) are pulled together (src/gnn4colliders/contrastive.py).
|
| 5 |
-
# Fine-tune with configs/gamgam_jepa_finetune.yaml
|
| 6 |
-
# model.pretrained=outputs/delphes_contrastive/last.pt, or compare encoders
|
| 7 |
-
# without fine-tuning: gnn4colliders probe configs/gamgam_jepa_finetune.yaml
|
| 8 |
-
# data.train_events=1000 split=validation model.pretrained=...
|
| 9 |
-
#
|
| 10 |
-
# gnn4colliders train configs/delphes_contrastive.yaml
|
| 11 |
-
name: delphes_contrastive
|
| 12 |
-
|
| 13 |
-
data:
|
| 14 |
-
hf_repo: HWresearch/Delphes
|
| 15 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 16 |
-
files:
|
| 17 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 18 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 19 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 20 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 21 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 22 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 23 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 24 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 25 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 26 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 27 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 28 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 29 |
-
tree_name: output
|
| 30 |
-
feature_branches:
|
| 31 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 32 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 33 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 34 |
-
- CALC_E
|
| 35 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 36 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 37 |
-
- NODE_TYPE
|
| 38 |
-
object_types: [vector, vector, vector, vector, single]
|
| 39 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 40 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 41 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 42 |
-
fold_var: Number
|
| 43 |
-
fold_modulus: 10
|
| 44 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 45 |
-
validation_folds: [0]
|
| 46 |
-
test_folds: [1]
|
| 47 |
-
# JEPA ignores labels and weights; the cache is that of
|
| 48 |
-
# configs/delphes_pretraining.yaml.
|
| 49 |
-
weight_var: weight
|
| 50 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 51 |
-
batch_size: 1024
|
| 52 |
-
prepare_workers: 8
|
| 53 |
-
loader_workers: 4
|
| 54 |
-
pin_memory: true
|
| 55 |
-
|
| 56 |
-
model:
|
| 57 |
-
name: contrastive
|
| 58 |
-
backbone: transformer
|
| 59 |
-
# The encoder; configs/gamgam_jepa_finetune.yaml must match it.
|
| 60 |
-
hid_size: 128
|
| 61 |
-
n_layers: 4
|
| 62 |
-
n_heads: 8
|
| 63 |
-
ffn_size: 512
|
| 64 |
-
dropout: 0.0
|
| 65 |
-
pair_bias: true
|
| 66 |
-
temperature: 0.1
|
| 67 |
-
scale_jitter: 0.1
|
| 68 |
-
object_dropout: 0.1
|
| 69 |
-
|
| 70 |
-
task:
|
| 71 |
-
name: contrastive
|
| 72 |
-
|
| 73 |
-
train:
|
| 74 |
-
max_epochs: 30
|
| 75 |
-
optimizer: adamw
|
| 76 |
-
learning_rate: 5.0e-4
|
| 77 |
-
weight_decay: 0.05
|
| 78 |
-
lr_decay: 0.95
|
| 79 |
-
early_stopping: null
|
| 80 |
-
# Fine-tune from last.pt.
|
| 81 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_jepa.yaml
DELETED
|
@@ -1,77 +0,0 @@
|
|
| 1 |
-
# JEPA pretraining of the object transformer on the 12 Delphes samples of
|
| 2 |
-
# the supervised pretraining (configs/delphes_pretraining.yaml), without
|
| 3 |
-
# their labels. Fine-tune with configs/gamgam_jepa_finetune.yaml.
|
| 4 |
-
#
|
| 5 |
-
# gnn4colliders prepare configs/delphes_jepa.yaml
|
| 6 |
-
# gnn4colliders train configs/delphes_jepa.yaml
|
| 7 |
-
name: delphes_jepa
|
| 8 |
-
|
| 9 |
-
data:
|
| 10 |
-
hf_repo: HWresearch/Delphes
|
| 11 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 12 |
-
files:
|
| 13 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 14 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 15 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 16 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 17 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 18 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 19 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 20 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 21 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 22 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 23 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 24 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 25 |
-
tree_name: output
|
| 26 |
-
feature_branches:
|
| 27 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 28 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 29 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 30 |
-
- CALC_E
|
| 31 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 32 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 33 |
-
- NODE_TYPE
|
| 34 |
-
object_types: [vector, vector, vector, vector, single]
|
| 35 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 36 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 37 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 38 |
-
fold_var: Number
|
| 39 |
-
fold_modulus: 10
|
| 40 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 41 |
-
validation_folds: [0]
|
| 42 |
-
test_folds: [1]
|
| 43 |
-
# JEPA ignores labels and weights; the cache is that of
|
| 44 |
-
# configs/delphes_pretraining.yaml.
|
| 45 |
-
weight_var: weight
|
| 46 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 47 |
-
batch_size: 1024
|
| 48 |
-
prepare_workers: 8
|
| 49 |
-
loader_workers: 4
|
| 50 |
-
pin_memory: true
|
| 51 |
-
|
| 52 |
-
model:
|
| 53 |
-
name: jepa
|
| 54 |
-
# The encoder; configs/gamgam_jepa_finetune.yaml must match it.
|
| 55 |
-
hid_size: 128
|
| 56 |
-
n_layers: 4
|
| 57 |
-
n_heads: 8
|
| 58 |
-
ffn_size: 512
|
| 59 |
-
dropout: 0.0
|
| 60 |
-
pair_bias: true
|
| 61 |
-
predictor_layers: 2
|
| 62 |
-
mask_fraction: 0.4
|
| 63 |
-
ema_momentum: 0.996
|
| 64 |
-
|
| 65 |
-
task:
|
| 66 |
-
name: jepa
|
| 67 |
-
|
| 68 |
-
train:
|
| 69 |
-
max_epochs: 30
|
| 70 |
-
optimizer: adamw
|
| 71 |
-
learning_rate: 5.0e-4
|
| 72 |
-
weight_decay: 0.05
|
| 73 |
-
lr_decay: 0.95
|
| 74 |
-
early_stopping: null
|
| 75 |
-
# The target drifts with the EMA, so the loss does not compare epochs;
|
| 76 |
-
# fine-tune from last.pt.
|
| 77 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_jepa_edge.yaml
DELETED
|
@@ -1,82 +0,0 @@
|
|
| 1 |
-
# JEPA pretraining of the edge network, of the size of the supervised
|
| 2 |
-
# pretrained models (Pretrained_GNN/), on the same 12 Delphes samples without
|
| 3 |
-
# their labels. Fine-tune with configs/gamgam_finetune.yaml and
|
| 4 |
-
# model.pretrained=outputs/delphes_jepa_edge/last.pt.
|
| 5 |
-
#
|
| 6 |
-
# gnn4colliders prepare configs/delphes_jepa_edge.yaml
|
| 7 |
-
# gnn4colliders train configs/delphes_jepa_edge.yaml
|
| 8 |
-
name: delphes_jepa_edge
|
| 9 |
-
|
| 10 |
-
data:
|
| 11 |
-
hf_repo: HWresearch/Delphes
|
| 12 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 13 |
-
files:
|
| 14 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 15 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 16 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 17 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 18 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 19 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 20 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 21 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 22 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 23 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 24 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 25 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 26 |
-
tree_name: output
|
| 27 |
-
feature_branches:
|
| 28 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 29 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 30 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 31 |
-
- CALC_E
|
| 32 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 33 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 34 |
-
- NODE_TYPE
|
| 35 |
-
object_types: [vector, vector, vector, vector, single]
|
| 36 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 37 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 38 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 39 |
-
fold_var: Number
|
| 40 |
-
fold_modulus: 10
|
| 41 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 42 |
-
validation_folds: [0]
|
| 43 |
-
test_folds: [1]
|
| 44 |
-
# JEPA ignores labels and weights; the cache is that of
|
| 45 |
-
# configs/delphes_pretraining.yaml.
|
| 46 |
-
weight_var: weight
|
| 47 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 48 |
-
batch_size: 1024
|
| 49 |
-
prepare_workers: 8
|
| 50 |
-
loader_workers: 4
|
| 51 |
-
pin_memory: true
|
| 52 |
-
|
| 53 |
-
model:
|
| 54 |
-
name: jepa
|
| 55 |
-
backbone: edge_network
|
| 56 |
-
# The encoder: as in Pretrained_GNN/*/config.yaml.
|
| 57 |
-
hid_size: 64
|
| 58 |
-
n_layers: 4
|
| 59 |
-
n_proc_steps: 4
|
| 60 |
-
dropout: 0.0
|
| 61 |
-
# The predictor: transformer blocks.
|
| 62 |
-
predictor_layers: 2
|
| 63 |
-
n_heads: 4
|
| 64 |
-
ffn_size: 256
|
| 65 |
-
mask_fraction: 0.4
|
| 66 |
-
ema_momentum: 0.996
|
| 67 |
-
# Without it the encodings collapse (docs/design.md, JEPA pretraining).
|
| 68 |
-
variance_weight: 0.1
|
| 69 |
-
|
| 70 |
-
task:
|
| 71 |
-
name: jepa
|
| 72 |
-
|
| 73 |
-
train:
|
| 74 |
-
max_epochs: 30
|
| 75 |
-
optimizer: adamw
|
| 76 |
-
learning_rate: 5.0e-4
|
| 77 |
-
weight_decay: 0.05
|
| 78 |
-
lr_decay: 0.95
|
| 79 |
-
early_stopping: null
|
| 80 |
-
# The target drifts with the EMA, so the loss does not compare epochs;
|
| 81 |
-
# fine-tune from last.pt.
|
| 82 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_masked.yaml
DELETED
|
@@ -1,78 +0,0 @@
|
|
| 1 |
-
# Masked object modelling of the object transformer on the 12 Delphes
|
| 2 |
-
# samples of configs/delphes_jepa.yaml, without their labels (same event
|
| 3 |
-
# cache): JEPA's masking and predictor, but the predictor predicts the hidden
|
| 4 |
-
# objects' kinematics instead of their target encodings (model.jepa_target:
|
| 5 |
-
# features; src/gnn4colliders/jepa.py). Fine-tune or probe with
|
| 6 |
-
# configs/gamgam_jepa_finetune.yaml model.pretrained=outputs/delphes_masked/last.pt.
|
| 7 |
-
#
|
| 8 |
-
# gnn4colliders train configs/delphes_masked.yaml
|
| 9 |
-
name: delphes_masked
|
| 10 |
-
|
| 11 |
-
data:
|
| 12 |
-
hf_repo: HWresearch/Delphes
|
| 13 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 14 |
-
files:
|
| 15 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 16 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 17 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 18 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 19 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 20 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 21 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 22 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 23 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 24 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 25 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 26 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 27 |
-
tree_name: output
|
| 28 |
-
feature_branches:
|
| 29 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 30 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 31 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 32 |
-
- CALC_E
|
| 33 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 34 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 35 |
-
- NODE_TYPE
|
| 36 |
-
object_types: [vector, vector, vector, vector, single]
|
| 37 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 38 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 39 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 40 |
-
fold_var: Number
|
| 41 |
-
fold_modulus: 10
|
| 42 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 43 |
-
validation_folds: [0]
|
| 44 |
-
test_folds: [1]
|
| 45 |
-
# JEPA ignores labels and weights; the cache is that of
|
| 46 |
-
# configs/delphes_pretraining.yaml.
|
| 47 |
-
weight_var: weight
|
| 48 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 49 |
-
batch_size: 1024
|
| 50 |
-
prepare_workers: 8
|
| 51 |
-
loader_workers: 4
|
| 52 |
-
pin_memory: true
|
| 53 |
-
|
| 54 |
-
model:
|
| 55 |
-
name: jepa
|
| 56 |
-
jepa_target: features
|
| 57 |
-
# The encoder; configs/gamgam_jepa_finetune.yaml must match it.
|
| 58 |
-
hid_size: 128
|
| 59 |
-
n_layers: 4
|
| 60 |
-
n_heads: 8
|
| 61 |
-
ffn_size: 512
|
| 62 |
-
dropout: 0.0
|
| 63 |
-
pair_bias: true
|
| 64 |
-
predictor_layers: 2
|
| 65 |
-
mask_fraction: 0.4
|
| 66 |
-
|
| 67 |
-
task:
|
| 68 |
-
name: jepa
|
| 69 |
-
|
| 70 |
-
train:
|
| 71 |
-
max_epochs: 30
|
| 72 |
-
optimizer: adamw
|
| 73 |
-
learning_rate: 5.0e-4
|
| 74 |
-
weight_decay: 0.05
|
| 75 |
-
lr_decay: 0.95
|
| 76 |
-
early_stopping: null
|
| 77 |
-
# Fine-tune from last.pt.
|
| 78 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_pfn.yaml
DELETED
|
@@ -1,86 +0,0 @@
|
|
| 1 |
-
# Meta-training of a prior-fitted network (src/gnn4colliders/pfn.py) on
|
| 2 |
-
# classification tasks drawn from the 12 Delphes samples: it learns to
|
| 3 |
-
# classify events given labelled context events, and is then evaluated in
|
| 4 |
-
# context on new data (configs/gamgam_pfn.yaml). The encoder has the sizes of
|
| 5 |
-
# configs/delphes_jepa.yaml, so model.pretrained=outputs/delphes_jepa/last.pt
|
| 6 |
-
# starts it from the JEPA encoder. task.prior_classes=[1,6,7,8,9,10,11]
|
| 7 |
-
# leaves out the five processes of the GamGam task (ttH, ggF, VBF, WH, ZH).
|
| 8 |
-
#
|
| 9 |
-
# gnn4colliders train configs/delphes_pfn.yaml
|
| 10 |
-
name: delphes_pfn
|
| 11 |
-
|
| 12 |
-
data:
|
| 13 |
-
hf_repo: HWresearch/Delphes
|
| 14 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 15 |
-
files:
|
| 16 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 17 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 18 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 19 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 20 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 21 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 22 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 23 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 24 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 25 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 26 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 27 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 28 |
-
tree_name: output
|
| 29 |
-
feature_branches:
|
| 30 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 31 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 32 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 33 |
-
- CALC_E
|
| 34 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 35 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 36 |
-
- NODE_TYPE
|
| 37 |
-
object_types: [vector, vector, vector, vector, single]
|
| 38 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 39 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 40 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 41 |
-
fold_var: Number
|
| 42 |
-
fold_modulus: 10
|
| 43 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 44 |
-
validation_folds: [0]
|
| 45 |
-
test_folds: [1]
|
| 46 |
-
# Tasks use the labels, not the weights; the cache is that of
|
| 47 |
-
# configs/delphes_pretraining.yaml.
|
| 48 |
-
weight_var: weight
|
| 49 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 50 |
-
# Events per step (tasks of context and query events).
|
| 51 |
-
batch_size: 4096
|
| 52 |
-
prepare_workers: 8
|
| 53 |
-
loader_workers: 4
|
| 54 |
-
pin_memory: true
|
| 55 |
-
|
| 56 |
-
model:
|
| 57 |
-
name: pfn
|
| 58 |
-
hid_size: 128
|
| 59 |
-
n_layers: 4
|
| 60 |
-
n_heads: 8
|
| 61 |
-
ffn_size: 512
|
| 62 |
-
dropout: 0.0
|
| 63 |
-
pair_bias: true
|
| 64 |
-
pretrained: null
|
| 65 |
-
max_classes: 10
|
| 66 |
-
pfn_layers: 6
|
| 67 |
-
|
| 68 |
-
task:
|
| 69 |
-
name: pfn
|
| 70 |
-
# The prior: classes per task, context events (log-uniform) and queries.
|
| 71 |
-
prior_classes: null
|
| 72 |
-
task_classes: [2, 10]
|
| 73 |
-
context_events: [16, 2048]
|
| 74 |
-
query_events: 256
|
| 75 |
-
energy_jitter: 0.2
|
| 76 |
-
steps_per_epoch: 1000
|
| 77 |
-
validation_steps: 50
|
| 78 |
-
|
| 79 |
-
train:
|
| 80 |
-
max_epochs: 50
|
| 81 |
-
optimizer: adamw
|
| 82 |
-
learning_rate: 1.0e-4
|
| 83 |
-
weight_decay: 0.01
|
| 84 |
-
lr_decay: 0.95
|
| 85 |
-
early_stopping: null
|
| 86 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_pretraining.yaml
DELETED
|
@@ -1,61 +0,0 @@
|
|
| 1 |
-
# 12-class pretraining on the Delphes samples of Ho et al. (2026), read from
|
| 2 |
-
# the HWresearch/Delphes Hugging Face dataset.
|
| 3 |
-
name: delphes_pretraining
|
| 4 |
-
|
| 5 |
-
data:
|
| 6 |
-
hf_repo: HWresearch/Delphes
|
| 7 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 8 |
-
files:
|
| 9 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 10 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 11 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 12 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 13 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 14 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 15 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 16 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 17 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 18 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 19 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 20 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 21 |
-
tree_name: output
|
| 22 |
-
feature_branches:
|
| 23 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 24 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 25 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 26 |
-
- CALC_E
|
| 27 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 28 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 29 |
-
- NODE_TYPE
|
| 30 |
-
object_types: [vector, vector, vector, vector, single]
|
| 31 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 32 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 33 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 34 |
-
fold_var: Number
|
| 35 |
-
fold_modulus: 10
|
| 36 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 37 |
-
validation_folds: [0]
|
| 38 |
-
test_folds: [1]
|
| 39 |
-
weight_var: weight
|
| 40 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 41 |
-
batch_size: 1024
|
| 42 |
-
prepare_workers: 8
|
| 43 |
-
loader_workers: 4
|
| 44 |
-
pin_memory: true
|
| 45 |
-
|
| 46 |
-
model:
|
| 47 |
-
name: edge_network
|
| 48 |
-
hid_size: 64
|
| 49 |
-
out_size: 12
|
| 50 |
-
n_layers: 4
|
| 51 |
-
n_proc_steps: 4
|
| 52 |
-
|
| 53 |
-
task:
|
| 54 |
-
name: multiclass
|
| 55 |
-
|
| 56 |
-
train:
|
| 57 |
-
max_epochs: 100
|
| 58 |
-
learning_rate: 1.0e-4
|
| 59 |
-
lr_decay: 0.99
|
| 60 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 61 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_tabicl.yaml
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
# Meta-training of the TabICL network (src/gnn4colliders/tabular.py; BSD-3,
|
| 2 |
-
# so meta-trained weights can be shared freely) on the classification tasks
|
| 3 |
-
# of configs/delphes_pfn.yaml, on the fixed-width event rows of
|
| 4 |
-
# configs/gamgam_tabicl.yaml. By default TabICL is frozen and its in-context
|
| 5 |
-
# blocks learn LoRA updates (model.lora_rank; 0 trains all 28M weights).
|
| 6 |
-
# task.prior_classes=[1,6,7,8,9,10,11] leaves out the five processes of the
|
| 7 |
-
# GamGam task. Evaluate in context with
|
| 8 |
-
# gnn4colliders evaluate configs/gamgam_tabicl.yaml split=validation \
|
| 9 |
-
# checkpoint=outputs/delphes_tabicl/best.pt
|
| 10 |
-
# Needs `uv sync --extra tabicl`.
|
| 11 |
-
#
|
| 12 |
-
# gnn4colliders train configs/delphes_tabicl.yaml
|
| 13 |
-
name: delphes_tabicl
|
| 14 |
-
|
| 15 |
-
data:
|
| 16 |
-
hf_repo: HWresearch/Delphes
|
| 17 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 18 |
-
files:
|
| 19 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 20 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 21 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 22 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 23 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 24 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 25 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 26 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 27 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 28 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 29 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 30 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 31 |
-
tree_name: output
|
| 32 |
-
feature_branches:
|
| 33 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 34 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 35 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 36 |
-
- CALC_E
|
| 37 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 38 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 39 |
-
- NODE_TYPE
|
| 40 |
-
object_types: [vector, vector, vector, vector, single]
|
| 41 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 42 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 43 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 44 |
-
fold_var: Number
|
| 45 |
-
fold_modulus: 10
|
| 46 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 47 |
-
validation_folds: [0]
|
| 48 |
-
test_folds: [1]
|
| 49 |
-
# Tasks use the labels, not the weights; the cache is that of
|
| 50 |
-
# configs/delphes_pretraining.yaml.
|
| 51 |
-
weight_var: weight
|
| 52 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 53 |
-
# Events per step (tasks of context and query events). TabICL (28M
|
| 54 |
-
# weights) recomputes activations in the backward pass.
|
| 55 |
-
batch_size: 4096
|
| 56 |
-
prepare_workers: 8
|
| 57 |
-
loader_workers: 4
|
| 58 |
-
pin_memory: true
|
| 59 |
-
|
| 60 |
-
model:
|
| 61 |
-
name: tabicl
|
| 62 |
-
tabicl_checkpoint: tabicl-classifier-v2-20260212.ckpt
|
| 63 |
-
# Leading jets, electrons, muons, photons and MET in a table row.
|
| 64 |
-
slots: [6, 2, 2, 3, 1]
|
| 65 |
-
# As in configs/gamgam_tabicl.yaml, which evaluates this model.
|
| 66 |
-
relative_phi: false
|
| 67 |
-
pair_objects: 0
|
| 68 |
-
max_classes: 10
|
| 69 |
-
lora_rank: 8
|
| 70 |
-
|
| 71 |
-
task:
|
| 72 |
-
name: pfn
|
| 73 |
-
# The prior: classes per task, context events (log-uniform) and queries.
|
| 74 |
-
prior_classes: null
|
| 75 |
-
task_classes: [2, 10]
|
| 76 |
-
context_events: [16, 2048]
|
| 77 |
-
query_events: 256
|
| 78 |
-
energy_jitter: 0.2
|
| 79 |
-
steps_per_epoch: 500
|
| 80 |
-
validation_steps: 50
|
| 81 |
-
|
| 82 |
-
train:
|
| 83 |
-
max_epochs: 20
|
| 84 |
-
optimizer: adamw
|
| 85 |
-
learning_rate: 1.0e-4
|
| 86 |
-
weight_decay: 0.01
|
| 87 |
-
lr_decay: 0.95
|
| 88 |
-
early_stopping: null
|
| 89 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/delphes_tabpfn.yaml
DELETED
|
@@ -1,89 +0,0 @@
|
|
| 1 |
-
# Meta-training of the TabPFN-3.5 network (src/gnn4colliders/tabular.py) on
|
| 2 |
-
# the classification tasks of configs/delphes_pfn.yaml, on the fixed-width
|
| 3 |
-
# event rows of configs/gamgam_tabpfn.yaml. By default TabPFN is frozen and
|
| 4 |
-
# its in-context blocks learn LoRA updates (model.lora_rank; 0 trains all
|
| 5 |
-
# weights). task.prior_classes=[1,6,7,8,9,10,11] leaves out the five
|
| 6 |
-
# processes of the GamGam task. Evaluate in context with
|
| 7 |
-
# gnn4colliders evaluate configs/gamgam_tabpfn.yaml split=validation \
|
| 8 |
-
# checkpoint=outputs/delphes_tabpfn/best.pt
|
| 9 |
-
# Needs `uv sync --extra tabpfn` and the TabPFN-3.5 weights (docs/usage.md).
|
| 10 |
-
#
|
| 11 |
-
# gnn4colliders train configs/delphes_tabpfn.yaml
|
| 12 |
-
name: delphes_tabpfn
|
| 13 |
-
|
| 14 |
-
data:
|
| 15 |
-
hf_repo: HWresearch/Delphes
|
| 16 |
-
hf_revision: 76a6c362bfba8e766ba7255a5c08a55257f78d0e
|
| 17 |
-
files:
|
| 18 |
-
- {path: samples/higgs/top-associated/tth/ttH_NLO_inc.root, label: 0}
|
| 19 |
-
- {path: samples/higgs/top-associated/thjb/tHjb_NLO_inc.root, label: 1}
|
| 20 |
-
- {path: samples/higgs/ggf/ggF_NLO_inc.root, label: 2}
|
| 21 |
-
- {path: samples/higgs/vbf/VBF_NLO_inc.root, label: 3}
|
| 22 |
-
- {path: samples/higgs/vh/wh/WH_NLO_inc.root, label: 4}
|
| 23 |
-
- {path: samples/higgs/vh/zh/ZH_NLO_inc.root, label: 5}
|
| 24 |
-
- {path: samples/photon/ttyy/ttyy.root, label: 6}
|
| 25 |
-
- {path: samples/top/multitop/tttt.root, label: 7}
|
| 26 |
-
- {path: samples/top/single-top/SingleT_schan.root, label: 8}
|
| 27 |
-
- {path: samples/top/ttbar/ttbar.root, label: 9}
|
| 28 |
-
- {path: samples/top/ttv/ttW.root, label: 10}
|
| 29 |
-
- {path: samples/top/multitop/ttt.root, label: 11}
|
| 30 |
-
tree_name: output
|
| 31 |
-
feature_branches:
|
| 32 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 33 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 34 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 35 |
-
- CALC_E
|
| 36 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 37 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 38 |
-
- NODE_TYPE
|
| 39 |
-
object_types: [vector, vector, vector, vector, single]
|
| 40 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 41 |
-
# Number is the event index; Number % 10 is the fold. Pretraining trained
|
| 42 |
-
# on folds 2-9 and tested on 0-1; here fold 0 validates and fold 1 tests.
|
| 43 |
-
fold_var: Number
|
| 44 |
-
fold_modulus: 10
|
| 45 |
-
train_folds: [2, 3, 4, 5, 6, 7, 8, 9]
|
| 46 |
-
validation_folds: [0]
|
| 47 |
-
test_folds: [1]
|
| 48 |
-
# Tasks use the labels, not the weights; the cache is that of
|
| 49 |
-
# configs/delphes_pretraining.yaml.
|
| 50 |
-
weight_var: weight
|
| 51 |
-
cache: outputs/delphes_pretraining/events.pt
|
| 52 |
-
# Events per step (tasks of context and query events): one task of the
|
| 53 |
-
# largest context. TabPFN recomputes activations in the backward pass.
|
| 54 |
-
batch_size: 2304
|
| 55 |
-
prepare_workers: 8
|
| 56 |
-
loader_workers: 4
|
| 57 |
-
pin_memory: true
|
| 58 |
-
|
| 59 |
-
model:
|
| 60 |
-
name: tabpfn
|
| 61 |
-
tabpfn_version: v3.5
|
| 62 |
-
tabpfn_checkpoint: null
|
| 63 |
-
# Leading jets, electrons, muons, photons and MET in a table row.
|
| 64 |
-
slots: [6, 2, 2, 3, 1]
|
| 65 |
-
# As in configs/gamgam_tabpfn.yaml, which evaluates this model.
|
| 66 |
-
relative_phi: false
|
| 67 |
-
pair_objects: 0
|
| 68 |
-
max_classes: 10
|
| 69 |
-
lora_rank: 8
|
| 70 |
-
|
| 71 |
-
task:
|
| 72 |
-
name: pfn
|
| 73 |
-
# The prior: classes per task, context events (log-uniform) and queries.
|
| 74 |
-
prior_classes: null
|
| 75 |
-
task_classes: [2, 10]
|
| 76 |
-
context_events: [16, 2048]
|
| 77 |
-
query_events: 256
|
| 78 |
-
energy_jitter: 0.2
|
| 79 |
-
steps_per_epoch: 500
|
| 80 |
-
validation_steps: 50
|
| 81 |
-
|
| 82 |
-
train:
|
| 83 |
-
max_epochs: 20
|
| 84 |
-
optimizer: adamw
|
| 85 |
-
learning_rate: 1.0e-4
|
| 86 |
-
weight_decay: 0.01
|
| 87 |
-
lr_decay: 0.95
|
| 88 |
-
early_stopping: null
|
| 89 |
-
best_metric: loss
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/environment/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/gamgam.yaml
DELETED
|
@@ -1,97 +0,0 @@
|
|
| 1 |
-
# Higgs production-mode classification on ATLAS Open Data (13 TeV, 2020
|
| 2 |
-
# release) GamGam MC: the 5-class from-scratch baseline of Ho et al.,
|
| 3 |
-
# JINST 21 (2026) P08006, Tables 1 and 3. 2,473,335 events.
|
| 4 |
-
#
|
| 5 |
-
# gnn4colliders prepare configs/gamgam.yaml
|
| 6 |
-
# gnn4colliders train configs/gamgam.yaml [train.fast=true] [train.seed=2]
|
| 7 |
-
# gnn4colliders evaluate configs/gamgam.yaml
|
| 8 |
-
name: gamgam
|
| 9 |
-
|
| 10 |
-
data:
|
| 11 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 12 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 13 |
-
files:
|
| 14 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 15 |
-
label: 0
|
| 16 |
-
size_bytes: 217819076
|
| 17 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 18 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 19 |
-
label: 1
|
| 20 |
-
size_bytes: 218418347
|
| 21 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 22 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 23 |
-
label: 2
|
| 24 |
-
size_bytes: 108616148
|
| 25 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 26 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 27 |
-
label: 3
|
| 28 |
-
size_bytes: 29757932
|
| 29 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 30 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 31 |
-
label: 4
|
| 32 |
-
size_bytes: 58701804
|
| 33 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 34 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 35 |
-
tree_name: mini
|
| 36 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 37 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 38 |
-
derived_branches:
|
| 39 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 40 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 41 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 42 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 43 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 44 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 45 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 46 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 47 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 48 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 49 |
-
feature_branches:
|
| 50 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 51 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 52 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 53 |
-
- CALC_E
|
| 54 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 55 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 56 |
-
- NODE_TYPE
|
| 57 |
-
object_types: [vector, vector, vector, vector, single]
|
| 58 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 59 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 60 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 61 |
-
fold_var: eventNumber
|
| 62 |
-
fold_modulus: 10
|
| 63 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 64 |
-
validation_folds: [8]
|
| 65 |
-
test_folds: [9]
|
| 66 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 67 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 68 |
-
weight_var: null
|
| 69 |
-
cache: outputs/gamgam/events.pt
|
| 70 |
-
batch_size: 1024
|
| 71 |
-
prepare_workers: 8
|
| 72 |
-
loader_workers: 4
|
| 73 |
-
pin_memory: true
|
| 74 |
-
|
| 75 |
-
# Baseline GNN of Ho et al. (2026), Table 1.
|
| 76 |
-
model:
|
| 77 |
-
name: edge_network
|
| 78 |
-
hid_size: 64
|
| 79 |
-
out_size: 5
|
| 80 |
-
n_layers: 4
|
| 81 |
-
n_proc_steps: 4
|
| 82 |
-
dropout: 0.0
|
| 83 |
-
|
| 84 |
-
task:
|
| 85 |
-
name: multiclass
|
| 86 |
-
|
| 87 |
-
# Sections 3.2 and 3.5: Adam at 1e-4 decayed by 0.99 per epoch, stopped
|
| 88 |
-
# after 5 epochs without training-loss improvement; the epoch with the best
|
| 89 |
-
# validation AUC is kept (best.pt). train.fast=true adds torch.compile, CUDA
|
| 90 |
-
# graphs, bfloat16 and TF32 (about 3x faster on an A100; results agree to
|
| 91 |
-
# rounding, not bitwise).
|
| 92 |
-
train:
|
| 93 |
-
max_epochs: 200
|
| 94 |
-
learning_rate: 1.0e-4
|
| 95 |
-
lr_decay: 0.99
|
| 96 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 97 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_deep_sets.yaml
DELETED
|
@@ -1,88 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task with Deep Sets (a per-object MLP, summed): the
|
| 2 |
-
# baseline without object interactions (no paper reference). Uses the cache
|
| 3 |
-
# of configs/gamgam.yaml.
|
| 4 |
-
#
|
| 5 |
-
# gnn4colliders train configs/gamgam_deep_sets.yaml
|
| 6 |
-
name: gamgam_deep_sets
|
| 7 |
-
|
| 8 |
-
data:
|
| 9 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 10 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 11 |
-
files:
|
| 12 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 13 |
-
label: 0
|
| 14 |
-
size_bytes: 217819076
|
| 15 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 16 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 17 |
-
label: 1
|
| 18 |
-
size_bytes: 218418347
|
| 19 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 20 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 21 |
-
label: 2
|
| 22 |
-
size_bytes: 108616148
|
| 23 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 24 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 25 |
-
label: 3
|
| 26 |
-
size_bytes: 29757932
|
| 27 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 28 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 29 |
-
label: 4
|
| 30 |
-
size_bytes: 58701804
|
| 31 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 32 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 33 |
-
tree_name: mini
|
| 34 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 35 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 36 |
-
derived_branches:
|
| 37 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 38 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 39 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 40 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 41 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 42 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 43 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 44 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 45 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 46 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 47 |
-
feature_branches:
|
| 48 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 49 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 50 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 51 |
-
- CALC_E
|
| 52 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 53 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 54 |
-
- NODE_TYPE
|
| 55 |
-
object_types: [vector, vector, vector, vector, single]
|
| 56 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 57 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 58 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 59 |
-
fold_var: eventNumber
|
| 60 |
-
fold_modulus: 10
|
| 61 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 62 |
-
validation_folds: [8]
|
| 63 |
-
test_folds: [9]
|
| 64 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 65 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 66 |
-
weight_var: null
|
| 67 |
-
cache: outputs/gamgam/events.pt
|
| 68 |
-
batch_size: 1024
|
| 69 |
-
prepare_workers: 8
|
| 70 |
-
loader_workers: 4
|
| 71 |
-
pin_memory: true
|
| 72 |
-
|
| 73 |
-
model:
|
| 74 |
-
name: deep_sets
|
| 75 |
-
hid_size: 64
|
| 76 |
-
out_size: 5
|
| 77 |
-
n_layers: 3
|
| 78 |
-
dropout: 0.0
|
| 79 |
-
|
| 80 |
-
task:
|
| 81 |
-
name: multiclass
|
| 82 |
-
|
| 83 |
-
train:
|
| 84 |
-
max_epochs: 100
|
| 85 |
-
learning_rate: 1.0e-4
|
| 86 |
-
lr_decay: 0.99
|
| 87 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 88 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_finetune.yaml
DELETED
|
@@ -1,97 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task fine-tuned from the multiclass pretrained model of
|
| 2 |
-
# Ho et al. (2026): its edge network with a new 5-class classifier, all
|
| 3 |
-
# weights trained. Uses the cache of configs/gamgam.yaml.
|
| 4 |
-
#
|
| 5 |
-
# gnn4colliders train configs/gamgam_finetune.yaml
|
| 6 |
-
name: gamgam_finetune
|
| 7 |
-
|
| 8 |
-
data:
|
| 9 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 10 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 11 |
-
files:
|
| 12 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 13 |
-
label: 0
|
| 14 |
-
size_bytes: 217819076
|
| 15 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 16 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 17 |
-
label: 1
|
| 18 |
-
size_bytes: 218418347
|
| 19 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 20 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 21 |
-
label: 2
|
| 22 |
-
size_bytes: 108616148
|
| 23 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 24 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 25 |
-
label: 3
|
| 26 |
-
size_bytes: 29757932
|
| 27 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 28 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 29 |
-
label: 4
|
| 30 |
-
size_bytes: 58701804
|
| 31 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 32 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 33 |
-
tree_name: mini
|
| 34 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 35 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 36 |
-
derived_branches:
|
| 37 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 38 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 39 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 40 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 41 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 42 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 43 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 44 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 45 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 46 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 47 |
-
feature_branches:
|
| 48 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 49 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 50 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 51 |
-
- CALC_E
|
| 52 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 53 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 54 |
-
- NODE_TYPE
|
| 55 |
-
object_types: [vector, vector, vector, vector, single]
|
| 56 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 57 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 58 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 59 |
-
fold_var: eventNumber
|
| 60 |
-
fold_modulus: 10
|
| 61 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 62 |
-
validation_folds: [8]
|
| 63 |
-
test_folds: [9]
|
| 64 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 65 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 66 |
-
weight_var: null
|
| 67 |
-
cache: outputs/gamgam/events.pt
|
| 68 |
-
batch_size: 1024
|
| 69 |
-
prepare_workers: 8
|
| 70 |
-
loader_workers: 4
|
| 71 |
-
pin_memory: true
|
| 72 |
-
|
| 73 |
-
# The pretrained network's size (Pretrained_GNN/*/config.yaml).
|
| 74 |
-
model:
|
| 75 |
-
name: edge_network
|
| 76 |
-
pretrained: Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_71.pt
|
| 77 |
-
freeze_backbone: false
|
| 78 |
-
hid_size: 64
|
| 79 |
-
out_size: 5
|
| 80 |
-
n_layers: 4
|
| 81 |
-
n_proc_steps: 4
|
| 82 |
-
dropout: 0.0
|
| 83 |
-
|
| 84 |
-
task:
|
| 85 |
-
name: multiclass
|
| 86 |
-
|
| 87 |
-
# Sections 3.2 and 3.5: Adam at 1e-4 decayed by 0.99 per epoch, stopped
|
| 88 |
-
# after 5 epochs without training-loss improvement; the epoch with the best
|
| 89 |
-
# validation AUC is kept (best.pt). train.fast=true adds torch.compile, CUDA
|
| 90 |
-
# graphs, bfloat16 and TF32 (about 3x faster on an A100; results agree to
|
| 91 |
-
# rounding, not bitwise).
|
| 92 |
-
train:
|
| 93 |
-
max_epochs: 200
|
| 94 |
-
learning_rate: 1.0e-4
|
| 95 |
-
lr_decay: 0.99
|
| 96 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 97 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_jepa_finetune.yaml
DELETED
|
@@ -1,95 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task with the transformer encoder pretrained by JEPA on
|
| 2 |
-
# the Delphes samples (configs/delphes_jepa.yaml). model.pretrained=null
|
| 3 |
-
# trains the same architecture from scratch. Uses the cache of
|
| 4 |
-
# configs/gamgam.yaml.
|
| 5 |
-
#
|
| 6 |
-
# gnn4colliders train configs/gamgam_jepa_finetune.yaml
|
| 7 |
-
name: gamgam_jepa_finetune
|
| 8 |
-
|
| 9 |
-
data:
|
| 10 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 11 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 12 |
-
files:
|
| 13 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 14 |
-
label: 0
|
| 15 |
-
size_bytes: 217819076
|
| 16 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 17 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 18 |
-
label: 1
|
| 19 |
-
size_bytes: 218418347
|
| 20 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 21 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 22 |
-
label: 2
|
| 23 |
-
size_bytes: 108616148
|
| 24 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 25 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 26 |
-
label: 3
|
| 27 |
-
size_bytes: 29757932
|
| 28 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 29 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 30 |
-
label: 4
|
| 31 |
-
size_bytes: 58701804
|
| 32 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 33 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 34 |
-
tree_name: mini
|
| 35 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 36 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 37 |
-
derived_branches:
|
| 38 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 39 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 40 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 41 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 42 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 43 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 44 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 45 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 46 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 47 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 48 |
-
feature_branches:
|
| 49 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 50 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 51 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 52 |
-
- CALC_E
|
| 53 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 54 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 55 |
-
- NODE_TYPE
|
| 56 |
-
object_types: [vector, vector, vector, vector, single]
|
| 57 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 58 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 59 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 60 |
-
fold_var: eventNumber
|
| 61 |
-
fold_modulus: 10
|
| 62 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 63 |
-
validation_folds: [8]
|
| 64 |
-
test_folds: [9]
|
| 65 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 66 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 67 |
-
weight_var: null
|
| 68 |
-
cache: outputs/gamgam/events.pt
|
| 69 |
-
batch_size: 1024
|
| 70 |
-
prepare_workers: 8
|
| 71 |
-
loader_workers: 4
|
| 72 |
-
pin_memory: true
|
| 73 |
-
|
| 74 |
-
model:
|
| 75 |
-
name: transformer
|
| 76 |
-
out_size: 5
|
| 77 |
-
hid_size: 128
|
| 78 |
-
n_layers: 4
|
| 79 |
-
n_heads: 8
|
| 80 |
-
ffn_size: 512
|
| 81 |
-
dropout: 0.0
|
| 82 |
-
pair_bias: true
|
| 83 |
-
pretrained: outputs/delphes_jepa/last.pt
|
| 84 |
-
# true trains only the classifier (linear probe).
|
| 85 |
-
freeze_backbone: false
|
| 86 |
-
|
| 87 |
-
task:
|
| 88 |
-
name: multiclass
|
| 89 |
-
|
| 90 |
-
train:
|
| 91 |
-
max_epochs: 100
|
| 92 |
-
learning_rate: 1.0e-4
|
| 93 |
-
lr_decay: 0.99
|
| 94 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 95 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_pfn.yaml
DELETED
|
@@ -1,90 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task classified in context by the PFN meta-trained on the
|
| 2 |
-
# Delphes samples (configs/delphes_pfn.yaml), with no training on GamGam:
|
| 3 |
-
# data.train_events labelled training events, an equal number per class, are
|
| 4 |
-
# the context. The model section must match the meta-training. Uses the
|
| 5 |
-
# cache of configs/gamgam.yaml.
|
| 6 |
-
#
|
| 7 |
-
# gnn4colliders evaluate configs/gamgam_pfn.yaml split=validation
|
| 8 |
-
name: gamgam_pfn
|
| 9 |
-
checkpoint: outputs/delphes_pfn/best.pt
|
| 10 |
-
|
| 11 |
-
data:
|
| 12 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 13 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 14 |
-
files:
|
| 15 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 16 |
-
label: 0
|
| 17 |
-
size_bytes: 217819076
|
| 18 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 19 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 20 |
-
label: 1
|
| 21 |
-
size_bytes: 218418347
|
| 22 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 23 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 24 |
-
label: 2
|
| 25 |
-
size_bytes: 108616148
|
| 26 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 27 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 28 |
-
label: 3
|
| 29 |
-
size_bytes: 29757932
|
| 30 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 31 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 32 |
-
label: 4
|
| 33 |
-
size_bytes: 58701804
|
| 34 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 35 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 36 |
-
tree_name: mini
|
| 37 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 38 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 39 |
-
derived_branches:
|
| 40 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 41 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 42 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 43 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 44 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 45 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 46 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 47 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 48 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 49 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 50 |
-
feature_branches:
|
| 51 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 52 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 53 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 54 |
-
- CALC_E
|
| 55 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 56 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 57 |
-
- NODE_TYPE
|
| 58 |
-
object_types: [vector, vector, vector, vector, single]
|
| 59 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 60 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 61 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 62 |
-
fold_var: eventNumber
|
| 63 |
-
fold_modulus: 10
|
| 64 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 65 |
-
validation_folds: [8]
|
| 66 |
-
test_folds: [9]
|
| 67 |
-
# Labelled events (equal per class) that form the context.
|
| 68 |
-
train_events: 1000
|
| 69 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 70 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 71 |
-
weight_var: null
|
| 72 |
-
cache: outputs/gamgam/events.pt
|
| 73 |
-
batch_size: 1024
|
| 74 |
-
prepare_workers: 8
|
| 75 |
-
loader_workers: 4
|
| 76 |
-
pin_memory: true
|
| 77 |
-
|
| 78 |
-
model:
|
| 79 |
-
name: pfn
|
| 80 |
-
out_size: 5
|
| 81 |
-
hid_size: 128
|
| 82 |
-
n_layers: 4
|
| 83 |
-
n_heads: 8
|
| 84 |
-
ffn_size: 512
|
| 85 |
-
pair_bias: true
|
| 86 |
-
max_classes: 10
|
| 87 |
-
pfn_layers: 6
|
| 88 |
-
|
| 89 |
-
task:
|
| 90 |
-
name: multiclass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_tabdpt.yaml
DELETED
|
@@ -1,91 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task classified in context by TabDPT (Layer 6;
|
| 2 |
-
# Apache-2.0 weights from Hugging Face), with no training: data.train_events
|
| 3 |
-
# labelled training events, an equal number per class, are the context of
|
| 4 |
-
# every prediction (src/gnn4colliders/tabular.py). TabDPT is trained on real
|
| 5 |
-
# tables; with model.tabdpt_context_size each event's context is its nearest
|
| 6 |
-
# context events (retrieval), which allows contexts beyond what fits at once.
|
| 7 |
-
# Needs `uv sync --extra tabdpt`. Uses the cache of configs/gamgam.yaml.
|
| 8 |
-
#
|
| 9 |
-
# gnn4colliders evaluate configs/gamgam_tabdpt.yaml split=validation
|
| 10 |
-
name: gamgam_tabdpt
|
| 11 |
-
|
| 12 |
-
data:
|
| 13 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 14 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 15 |
-
files:
|
| 16 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 17 |
-
label: 0
|
| 18 |
-
size_bytes: 217819076
|
| 19 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 20 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 21 |
-
label: 1
|
| 22 |
-
size_bytes: 218418347
|
| 23 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 24 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 25 |
-
label: 2
|
| 26 |
-
size_bytes: 108616148
|
| 27 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 28 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 29 |
-
label: 3
|
| 30 |
-
size_bytes: 29757932
|
| 31 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 32 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 33 |
-
label: 4
|
| 34 |
-
size_bytes: 58701804
|
| 35 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 36 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 37 |
-
tree_name: mini
|
| 38 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 39 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 40 |
-
derived_branches:
|
| 41 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 42 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 43 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 44 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 45 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 46 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 47 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 48 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 49 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 50 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 51 |
-
feature_branches:
|
| 52 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 53 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 54 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 55 |
-
- CALC_E
|
| 56 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 57 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 58 |
-
- NODE_TYPE
|
| 59 |
-
object_types: [vector, vector, vector, vector, single]
|
| 60 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 61 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 62 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 63 |
-
fold_var: eventNumber
|
| 64 |
-
fold_modulus: 10
|
| 65 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 66 |
-
validation_folds: [8]
|
| 67 |
-
test_folds: [9]
|
| 68 |
-
# Labelled events (equal per class) that form the context.
|
| 69 |
-
train_events: 1000
|
| 70 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 71 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 72 |
-
weight_var: null
|
| 73 |
-
cache: outputs/gamgam/events.pt
|
| 74 |
-
# Events classified per TabDPT call.
|
| 75 |
-
batch_size: 8192
|
| 76 |
-
prepare_workers: 8
|
| 77 |
-
loader_workers: 4
|
| 78 |
-
pin_memory: true
|
| 79 |
-
|
| 80 |
-
model:
|
| 81 |
-
name: tabdpt
|
| 82 |
-
out_size: 5
|
| 83 |
-
tabdpt_estimators: 8
|
| 84 |
-
tabdpt_context_size: null
|
| 85 |
-
# Leading jets, electrons, muons, photons and MET in a table row.
|
| 86 |
-
slots: [6, 2, 2, 3, 1]
|
| 87 |
-
relative_phi: false
|
| 88 |
-
pair_objects: 0
|
| 89 |
-
|
| 90 |
-
task:
|
| 91 |
-
name: multiclass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_tabicl.yaml
DELETED
|
@@ -1,95 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task classified in context by TabICLv2 (Inria; BSD-3
|
| 2 |
-
# weights from Hugging Face), with no training: data.train_events labelled
|
| 3 |
-
# training events, an equal number per class, are the context of every
|
| 4 |
-
# prediction (src/gnn4colliders/tabular.py). Needs `uv sync --extra tabicl`.
|
| 5 |
-
# Uses the cache of configs/gamgam.yaml. The rows are those of
|
| 6 |
-
# configs/gamgam_tabpfn.yaml; model.relative_phi=true and
|
| 7 |
-
# model.pair_objects=4 add the physics features.
|
| 8 |
-
#
|
| 9 |
-
# gnn4colliders evaluate configs/gamgam_tabicl.yaml split=validation
|
| 10 |
-
#
|
| 11 |
-
# With checkpoint=outputs/delphes_tabicl/best.pt, the TabICL network
|
| 12 |
-
# meta-trained by configs/delphes_tabicl.yaml (model.lora_rank and
|
| 13 |
-
# model.max_classes must match it) classifies with its own forward pass.
|
| 14 |
-
name: gamgam_tabicl
|
| 15 |
-
|
| 16 |
-
data:
|
| 17 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 18 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 19 |
-
files:
|
| 20 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 21 |
-
label: 0
|
| 22 |
-
size_bytes: 217819076
|
| 23 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 24 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 25 |
-
label: 1
|
| 26 |
-
size_bytes: 218418347
|
| 27 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 28 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 29 |
-
label: 2
|
| 30 |
-
size_bytes: 108616148
|
| 31 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 32 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 33 |
-
label: 3
|
| 34 |
-
size_bytes: 29757932
|
| 35 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 36 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 37 |
-
label: 4
|
| 38 |
-
size_bytes: 58701804
|
| 39 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 40 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 41 |
-
tree_name: mini
|
| 42 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 43 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 44 |
-
derived_branches:
|
| 45 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 46 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 47 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 48 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 49 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 50 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 51 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 52 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 53 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 54 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 55 |
-
feature_branches:
|
| 56 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 57 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 58 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 59 |
-
- CALC_E
|
| 60 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 61 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 62 |
-
- NODE_TYPE
|
| 63 |
-
object_types: [vector, vector, vector, vector, single]
|
| 64 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 65 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 66 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 67 |
-
fold_var: eventNumber
|
| 68 |
-
fold_modulus: 10
|
| 69 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 70 |
-
validation_folds: [8]
|
| 71 |
-
test_folds: [9]
|
| 72 |
-
# Labelled events (equal per class) that form the context.
|
| 73 |
-
train_events: 1000
|
| 74 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 75 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 76 |
-
weight_var: null
|
| 77 |
-
cache: outputs/gamgam/events.pt
|
| 78 |
-
# Events classified per TabICL call.
|
| 79 |
-
batch_size: 8192
|
| 80 |
-
prepare_workers: 8
|
| 81 |
-
loader_workers: 4
|
| 82 |
-
pin_memory: true
|
| 83 |
-
|
| 84 |
-
model:
|
| 85 |
-
name: tabicl
|
| 86 |
-
out_size: 5
|
| 87 |
-
tabicl_checkpoint: tabicl-classifier-v2-20260212.ckpt
|
| 88 |
-
tabicl_estimators: 8
|
| 89 |
-
# Leading jets, electrons, muons, photons and MET in a table row.
|
| 90 |
-
slots: [6, 2, 2, 3, 1]
|
| 91 |
-
relative_phi: false
|
| 92 |
-
pair_objects: 0
|
| 93 |
-
|
| 94 |
-
task:
|
| 95 |
-
name: multiclass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_tabpfn.yaml
DELETED
|
@@ -1,96 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task classified in context by TabPFN (Prior Labs), with
|
| 2 |
-
# no training: data.train_events labelled training events, an equal number
|
| 3 |
-
# per class, are the context of every prediction (src/gnn4colliders/tabular.py).
|
| 4 |
-
# Needs `uv sync --extra tabpfn` and, for TabPFN-2.5 and later, a one-time
|
| 5 |
-
# login (a token in ~/.cache/tabpfn or TABPFN_TOKEN). Uses the cache of
|
| 6 |
-
# configs/gamgam.yaml.
|
| 7 |
-
#
|
| 8 |
-
# gnn4colliders evaluate configs/gamgam_tabpfn.yaml split=validation
|
| 9 |
-
#
|
| 10 |
-
# With checkpoint=outputs/delphes_tabpfn/best.pt, the TabPFN network
|
| 11 |
-
# meta-trained by configs/delphes_tabpfn.yaml (model.lora_rank and
|
| 12 |
-
# model.max_classes must match it) classifies with its own forward pass.
|
| 13 |
-
name: gamgam_tabpfn
|
| 14 |
-
|
| 15 |
-
data:
|
| 16 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 17 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 18 |
-
files:
|
| 19 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 20 |
-
label: 0
|
| 21 |
-
size_bytes: 217819076
|
| 22 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 23 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 24 |
-
label: 1
|
| 25 |
-
size_bytes: 218418347
|
| 26 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 27 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 28 |
-
label: 2
|
| 29 |
-
size_bytes: 108616148
|
| 30 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 31 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 32 |
-
label: 3
|
| 33 |
-
size_bytes: 29757932
|
| 34 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 35 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 36 |
-
label: 4
|
| 37 |
-
size_bytes: 58701804
|
| 38 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 39 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 40 |
-
tree_name: mini
|
| 41 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 42 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 43 |
-
derived_branches:
|
| 44 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 45 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 46 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 47 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 48 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 49 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 50 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 51 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 52 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 53 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 54 |
-
feature_branches:
|
| 55 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 56 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 57 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 58 |
-
- CALC_E
|
| 59 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 60 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 61 |
-
- NODE_TYPE
|
| 62 |
-
object_types: [vector, vector, vector, vector, single]
|
| 63 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 64 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 65 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 66 |
-
fold_var: eventNumber
|
| 67 |
-
fold_modulus: 10
|
| 68 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 69 |
-
validation_folds: [8]
|
| 70 |
-
test_folds: [9]
|
| 71 |
-
# Labelled events (equal per class) that form the context.
|
| 72 |
-
train_events: 1000
|
| 73 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 74 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 75 |
-
weight_var: null
|
| 76 |
-
cache: outputs/gamgam/events.pt
|
| 77 |
-
# Events classified per TabPFN call.
|
| 78 |
-
batch_size: 8192
|
| 79 |
-
prepare_workers: 8
|
| 80 |
-
loader_workers: 4
|
| 81 |
-
pin_memory: true
|
| 82 |
-
|
| 83 |
-
model:
|
| 84 |
-
name: tabpfn
|
| 85 |
-
out_size: 5
|
| 86 |
-
tabpfn_version: v3.5
|
| 87 |
-
tabpfn_estimators: 4
|
| 88 |
-
# Leading jets, electrons, muons, photons and MET in a table row.
|
| 89 |
-
slots: [6, 2, 2, 3, 1]
|
| 90 |
-
# Physics features: azimuths from the leading object's, and the mass and
|
| 91 |
-
# Delta R of every pair of the leading pair_objects objects.
|
| 92 |
-
relative_phi: false
|
| 93 |
-
pair_objects: 0
|
| 94 |
-
|
| 95 |
-
task:
|
| 96 |
-
name: multiclass
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/gamgam_transformer.yaml
DELETED
|
@@ -1,92 +0,0 @@
|
|
| 1 |
-
# The GamGam 5-class task with a transformer over the same objects (no
|
| 2 |
-
# paper reference). Uses the cache of configs/gamgam.yaml.
|
| 3 |
-
#
|
| 4 |
-
# gnn4colliders train configs/gamgam_transformer.yaml
|
| 5 |
-
name: gamgam_transformer
|
| 6 |
-
|
| 7 |
-
data:
|
| 8 |
-
hf_repo: ho22joshua/atlas_opendata
|
| 9 |
-
hf_revision: d1bcc9ccaa304ea09e11f7d724342568f72a0d6e
|
| 10 |
-
files:
|
| 11 |
-
- path: GamGam/2020/MC/mc_341081.ttH125_gamgam.GamGam.root
|
| 12 |
-
label: 0
|
| 13 |
-
size_bytes: 217819076
|
| 14 |
-
sha256: c498974926f6c55143d17fc4d3d6fa69bd3718ff92cf7d5cad5c3d67a836c7b7
|
| 15 |
-
- path: GamGam/2020/MC/mc_343981.ggH125_gamgam.GamGam.root
|
| 16 |
-
label: 1
|
| 17 |
-
size_bytes: 218418347
|
| 18 |
-
sha256: 05b3e6709e8836a7b862a7fde09086b3a9d4d494d62a6984d5df39d8daad13eb
|
| 19 |
-
- path: GamGam/2020/MC/mc_345041.VBFH125_gamgam.GamGam.root
|
| 20 |
-
label: 2
|
| 21 |
-
size_bytes: 108616148
|
| 22 |
-
sha256: 92334fce3bffc9eb6e0745dd6ef354d5ce33da764858511b8f9ca49b862b44e2
|
| 23 |
-
- path: GamGam/2020/MC/mc_345318.WpH125J_Wincl_gamgam.GamGam.root
|
| 24 |
-
label: 3
|
| 25 |
-
size_bytes: 29757932
|
| 26 |
-
sha256: f255a9c3c6935f976f788b6f32d9a8f6bf576eb12ecd03a6f3fd0a5dea9d1248
|
| 27 |
-
- path: GamGam/2020/MC/mc_345319.ZH125J_Zincl_gamgam.GamGam.root
|
| 28 |
-
label: 4
|
| 29 |
-
size_bytes: 58701804
|
| 30 |
-
sha256: c85623b04ac0176c64acdb2ecf8e992a63824cfc430a421df742319518ff6748
|
| 31 |
-
class_names: [ttH, ggF, VBF, WH, ZH]
|
| 32 |
-
tree_name: mini
|
| 33 |
-
# Electrons and muons share the lep_* collection; b-tagging uses the
|
| 34 |
-
# MV2c10 70% efficiency working point of the Open Data documentation.
|
| 35 |
-
derived_branches:
|
| 36 |
-
ele_pt: {source: lep_pt, where: {lep_type: 11}}
|
| 37 |
-
ele_eta: {source: lep_eta, where: {lep_type: 11}}
|
| 38 |
-
ele_phi: {source: lep_phi, where: {lep_type: 11}}
|
| 39 |
-
ele_charge: {source: lep_charge, where: {lep_type: 11}}
|
| 40 |
-
mu_pt: {source: lep_pt, where: {lep_type: 13}}
|
| 41 |
-
mu_eta: {source: lep_eta, where: {lep_type: 13}}
|
| 42 |
-
mu_phi: {source: lep_phi, where: {lep_type: 13}}
|
| 43 |
-
mu_charge: {source: lep_charge, where: {lep_type: 13}}
|
| 44 |
-
jet_btag: {source: jet_MV2c10, above: 0.8244273}
|
| 45 |
-
# Object types (node types) as in pretraining: jet, electron, muon, photon, MET.
|
| 46 |
-
feature_branches:
|
| 47 |
-
- [jet_pt, ele_pt, mu_pt, photon_pt, met_et]
|
| 48 |
-
- [jet_eta, ele_eta, mu_eta, photon_eta, 0]
|
| 49 |
-
- [jet_phi, ele_phi, mu_phi, photon_phi, met_phi]
|
| 50 |
-
- CALC_E
|
| 51 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 52 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 53 |
-
- NODE_TYPE
|
| 54 |
-
object_types: [vector, vector, vector, vector, single]
|
| 55 |
-
# Momenta are in MeV; 1e-4 gives the pretraining unit of 10 GeV.
|
| 56 |
-
scales: [1.0e-4, 1, 1, 1.0e-4, 1, 1, 1]
|
| 57 |
-
# eventNumber % 10 gives the paper's 80:10:10 train:validation:test split.
|
| 58 |
-
fold_var: eventNumber
|
| 59 |
-
fold_modulus: 10
|
| 60 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 61 |
-
validation_folds: [8]
|
| 62 |
-
test_folds: [9]
|
| 63 |
-
# Unweighted: each event counts once and classes are balanced by the
|
| 64 |
-
# per-class mean loss (23% of ttH events have mcWeight = -1).
|
| 65 |
-
weight_var: null
|
| 66 |
-
cache: outputs/gamgam/events.pt
|
| 67 |
-
batch_size: 1024
|
| 68 |
-
prepare_workers: 8
|
| 69 |
-
loader_workers: 4
|
| 70 |
-
pin_memory: true
|
| 71 |
-
|
| 72 |
-
model:
|
| 73 |
-
name: transformer
|
| 74 |
-
hid_size: 64
|
| 75 |
-
out_size: 5
|
| 76 |
-
n_layers: 2
|
| 77 |
-
n_heads: 4
|
| 78 |
-
ffn_size: 256
|
| 79 |
-
dropout: 0.1
|
| 80 |
-
# Attention bias from object-pair (deta, dphi, dR, mass) features, as in the
|
| 81 |
-
# Particle Transformer; false gives a plain transformer.
|
| 82 |
-
pair_bias: true
|
| 83 |
-
|
| 84 |
-
task:
|
| 85 |
-
name: multiclass
|
| 86 |
-
|
| 87 |
-
train:
|
| 88 |
-
max_epochs: 100
|
| 89 |
-
learning_rate: 1.0e-4
|
| 90 |
-
lr_decay: 0.99
|
| 91 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 92 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
configs/model/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/model/root_gnn/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/opendata_pfn.yaml
DELETED
|
The diff for this file is too large to render.
See raw diff
|
|
|
configs/task/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/trainer/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
configs/tth_cp_even_odd.yaml
DELETED
|
@@ -1,49 +0,0 @@
|
|
| 1 |
-
# CP-even versus CP-odd top-Yukawa ttH (H -> gamma gamma), Delphes samples.
|
| 2 |
-
# Point data.files at local copies of the two samples.
|
| 3 |
-
name: tth_cp_even_odd
|
| 4 |
-
|
| 5 |
-
data:
|
| 6 |
-
files:
|
| 7 |
-
- {path: data/raw/ttH_NLO_download/samples/higgs/top-associated/tth/ttH_NLO.root, label: 0}
|
| 8 |
-
- {path: data/raw/ttH_cp_even_odd/samples/higgs/top-associated/tth/ttH_CPodd.root, label: 1}
|
| 9 |
-
tree_name: output
|
| 10 |
-
feature_branches:
|
| 11 |
-
- [jet_pt, ele_pt, mu_pt, ph_pt, MET_met]
|
| 12 |
-
- [jet_eta, ele_eta, mu_eta, ph_eta, 0]
|
| 13 |
-
- [jet_phi, ele_phi, mu_phi, ph_phi, MET_phi]
|
| 14 |
-
- CALC_E
|
| 15 |
-
- [jet_btag, 0, 0, 0, 0]
|
| 16 |
-
- [0, ele_charge, mu_charge, 0, 0]
|
| 17 |
-
- NODE_TYPE
|
| 18 |
-
object_types: [vector, vector, vector, vector, single]
|
| 19 |
-
# Delphes momenta are in GeV; 0.1 gives the pretraining unit of 10 GeV.
|
| 20 |
-
scales: [0.1, 1, 1, 0.1, 1, 1, 1]
|
| 21 |
-
# Number is the event index; Number % 10 is the fold.
|
| 22 |
-
fold_var: Number
|
| 23 |
-
fold_modulus: 10
|
| 24 |
-
train_folds: [0, 1, 2, 3, 4, 5, 6, 7]
|
| 25 |
-
validation_folds: [8]
|
| 26 |
-
test_folds: [9]
|
| 27 |
-
weight_var: weight
|
| 28 |
-
cache: outputs/tth_cp_even_odd/events.pt
|
| 29 |
-
batch_size: 1024
|
| 30 |
-
|
| 31 |
-
model:
|
| 32 |
-
name: edge_network
|
| 33 |
-
hid_size: 64
|
| 34 |
-
out_size: 1
|
| 35 |
-
n_layers: 4
|
| 36 |
-
n_proc_steps: 4
|
| 37 |
-
|
| 38 |
-
# NLO samples have signed weights; |weight| keeps the loss denominator
|
| 39 |
-
# stable (metrics use events with positive weight).
|
| 40 |
-
task:
|
| 41 |
-
name: binary
|
| 42 |
-
absolute_weights: true
|
| 43 |
-
|
| 44 |
-
train:
|
| 45 |
-
max_epochs: 100
|
| 46 |
-
learning_rate: 1.0e-4
|
| 47 |
-
lr_decay: 0.99
|
| 48 |
-
early_stopping: {monitor: train_loss, patience: 5, min_delta: 0.0}
|
| 49 |
-
best_metric: auc
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
data/fixtures/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
data/raw/.gitkeep
ADDED
|
@@ -0,0 +1 @@
|
|
|
|
|
|
|
| 1 |
+
|
docs/architecture.md
ADDED
|
@@ -0,0 +1,378 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# GNN4Colliders architecture
|
| 2 |
+
|
| 3 |
+
## Current v1 architecture
|
| 4 |
+
|
| 5 |
+
The supported rewrite is layered around an architecture-neutral event boundary:
|
| 6 |
+
|
| 7 |
+
```text
|
| 8 |
+
ROOT/Awkward
|
| 9 |
+
↓
|
| 10 |
+
EventSample + EventMetadata
|
| 11 |
+
↓
|
| 12 |
+
shared collider feature construction
|
| 13 |
+
├── GraphSample -> versioned graph cache -> GraphBatch
|
| 14 |
+
└── future SequenceSample -> ROOT-Transformer (not implemented)
|
| 15 |
+
↓
|
| 16 |
+
ROOT-GNN EdgeNetwork
|
| 17 |
+
↓
|
| 18 |
+
raw logits -> Task
|
| 19 |
+
├── loss
|
| 20 |
+
├── predictions
|
| 21 |
+
└── full-split metrics
|
| 22 |
+
↓
|
| 23 |
+
Trainer / Predictor / outputs
|
| 24 |
+
```
|
| 25 |
+
|
| 26 |
+
| Layer | Responsibility |
|
| 27 |
+
| --- | --- |
|
| 28 |
+
| `data` | ROOT/Awkward ingestion, event samples, metadata, graph caches, folds, and batching |
|
| 29 |
+
| `features` | Shared collider-object features and derived physics quantities |
|
| 30 |
+
| `graphs` | Topology, edge features, and the DGL representation adapter |
|
| 31 |
+
| `models/root_gnn` | ROOT-GNN encoders, portable tensor message passing, classifier, and transfer boundary |
|
| 32 |
+
| `tasks` | Loss, score/prediction, labels, weights, and metrics |
|
| 33 |
+
| `training` | Optimizer lifecycle, validation, early stopping, checkpointing, and reproducibility |
|
| 34 |
+
| `inference` | Ordered prediction/evaluation and NPZ/ROOT output adapters |
|
| 35 |
+
| `distributed` | Rank-local devices, sharding, DDP, and cross-rank collection |
|
| 36 |
+
| `config` / `cli` | Semantic Hydra composition and thin user-facing commands |
|
| 37 |
+
|
| 38 |
+
`EventSample` is shared infrastructure, not a ROOT-GNN object. `GraphSample`
|
| 39 |
+
is the current representation-specific adapter. At batching, the default
|
| 40 |
+
ROOT-GNN backend converts cached DGL graphs into `TensorGraph` values with
|
| 41 |
+
explicit node/edge indices and graph membership. Native message passing then
|
| 42 |
+
uses only PyTorch tensor operations, allowing the same model to train on CPU,
|
| 43 |
+
CUDA, and Apple MPS. This separation is the extension point for a future
|
| 44 |
+
sequence/token representation.
|
| 45 |
+
|
| 46 |
+
The new public metadata contract is named `EventMetadata(fold, weight,
|
| 47 |
+
sample_id, extra)`. The legacy positional tracking tensor is accepted only by
|
| 48 |
+
compatibility-facing ingestion code. A `GraphSampleCache` is deliberately a
|
| 49 |
+
Level-2 graph cache; replacing it with a universal cache would couple future
|
| 50 |
+
model families to DGL.
|
| 51 |
+
|
| 52 |
+
For deployment, a prepared `GraphBatch` can pass through the isolated
|
| 53 |
+
`RootGNNExportAdapter` into an ONNX model. ONNX is still an inference boundary:
|
| 54 |
+
it does not read ROOT or construct collider features.
|
| 55 |
+
|
| 56 |
+
## Current public workflow
|
| 57 |
+
|
| 58 |
+
`prepare` reads ROOT through `RootEventDataset`, builds shared features and
|
| 59 |
+
DGL graphs, and saves a schema-checked cache. `train` creates a model/task and
|
| 60 |
+
`Trainer`; validation is the model-selection split and test is held out.
|
| 61 |
+
`evaluate` computes metrics after collecting the complete split. `predict`
|
| 62 |
+
returns detached CPU tensors in loader order and writes named NPZ fields.
|
| 63 |
+
`write_root_scores` is an optional Python adapter with explicit entry alignment;
|
| 64 |
+
the CLI currently exposes NPZ output.
|
| 65 |
+
|
| 66 |
+
Compatibility responsibilities are isolated in `gnn4colliders.compat`.
|
| 67 |
+
Supported historical checkpoint prefixes, classifier names, and the two-column
|
| 68 |
+
tracking conversion are listed in [`compatibility.md`](compatibility.md).
|
| 69 |
+
The modern pipeline does not propagate positional tracking or historical NPZ
|
| 70 |
+
fields.
|
| 71 |
+
|
| 72 |
+
Checkpoints are independent of the model implementation: they carry model and
|
| 73 |
+
task metadata, lifecycle state, schema versions, and optional RNG state.
|
| 74 |
+
Prefix normalization supports DDP `module.` and compiled `_orig_mod.` weights,
|
| 75 |
+
plus the active ROOT-GNN historical classifier-name compatibility path.
|
| 76 |
+
|
| 77 |
+
---
|
| 78 |
+
|
| 79 |
+
## Historical behavioral reference
|
| 80 |
+
|
| 81 |
+
This document covers the target system in `legacy/root_gnn_dgl/`. The sibling
|
| 82 |
+
`legacy/physicsnemo/` tree is a prior rewrite attempt and is not a behavioral
|
| 83 |
+
target.
|
| 84 |
+
|
| 85 |
+
## 1. High-level system description
|
| 86 |
+
|
| 87 |
+
The active rewrite exposes `gnn4colliders.models.root_gnn.EdgeNetwork`. Its
|
| 88 |
+
encoders and message-passing blocks form a reusable backbone whose decoded
|
| 89 |
+
graph representation is passed to an explicit classifier. `FineTunedEdgeNetwork`
|
| 90 |
+
reuses that backbone and replaces only the task-specific classifier, with
|
| 91 |
+
explicit frozen or trainable-backbone control.
|
| 92 |
+
|
| 93 |
+
`root_gnn_dgl` is a ROOT-to-DGL graph classification system. YAML selects
|
| 94 |
+
dataset, model, loss, and finish-function classes by import path. The dataset
|
| 95 |
+
reads ROOT trees, converts collider objects to fully connected DGL graphs, and
|
| 96 |
+
saves graph chunks. Training loads those chunks, applies fold selection and
|
| 97 |
+
optional pre-batching/padding, trains a graph network, writes one PyTorch
|
| 98 |
+
checkpoint per epoch, and reports weighted loss, accuracy, and ROC AUC.
|
| 99 |
+
|
| 100 |
+
The primary model is `models.GCN.Edge_Network` ([`GCN.py:182-251`](../legacy/root_gnn_dgl/models/GCN.py)).
|
| 101 |
+
It encodes node, edge, and global features, repeats edge -> node -> global
|
| 102 |
+
message passing `n_proc_steps` times, decodes the global state, and applies
|
| 103 |
+
`classify`. Fine-tuning uses `models.GCN.Transferred_Learning_Finetuning`
|
| 104 |
+
([`GCN.py:884-997`](../legacy/root_gnn_dgl/models/GCN.py)), which loads a
|
| 105 |
+
pretrained `Edge_Network`, removes its final classifier, and applies a new one.
|
| 106 |
+
The active configs use output size 12 for multiclass pretraining and output
|
| 107 |
+
size 1 for binary tasks ([`configs/stats_100K/pretraining_multiclass.yaml:1-45`](../legacy/root_gnn_dgl/configs/stats_100K/pretraining_multiclass.yaml),
|
| 108 |
+
[`configs/stats_100K/finetuning_ttH_CP_even_vs_odd.yaml:1-45`](../legacy/root_gnn_dgl/configs/stats_100K/finetuning_ttH_CP_even_vs_odd.yaml)).
|
| 109 |
+
|
| 110 |
+
### Entry points and flows
|
| 111 |
+
|
| 112 |
+
- `scripts/training_script.py:main` and its CLI parser load YAML, create
|
| 113 |
+
loaders, construct the model, and call `train`; `--evaluate` calls
|
| 114 |
+
`evaluate` ([`training_script.py:638-843`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 115 |
+
- `scripts/prep_data.py:main` creates configured graph caches
|
| 116 |
+
([`prep_data.py:68-110`](../legacy/root_gnn_dgl/scripts/prep_data.py)).
|
| 117 |
+
- `scripts/inference.py:main` reconstructs an unlazy dataset, loads one or
|
| 118 |
+
more checkpoints, and writes `.npz` or ROOT scores
|
| 119 |
+
([`inference.py:163-387`](../legacy/root_gnn_dgl/scripts/inference.py)).
|
| 120 |
+
- `scripts/export_onnx.py:main` exports an ONNX-friendly model
|
| 121 |
+
([`export_onnx.py:979-1035`](../legacy/root_gnn_dgl/scripts/export_onnx.py)).
|
| 122 |
+
- `selections.py:main`, `check_dataset_files.py:main`, and
|
| 123 |
+
`plot_config_distributions.py:main` are diagnostic entry points. `run_demo.sh`
|
| 124 |
+
sequences pretraining, binary training, fine-tuning, and inference
|
| 125 |
+
([`run_demo.sh:3-59`](../legacy/root_gnn_dgl/run_demo.sh)).
|
| 126 |
+
|
| 127 |
+
The training flow is:
|
| 128 |
+
|
| 129 |
+
```text
|
| 130 |
+
YAML -> load_config/buildFromConfig -> RootDataset/LazyDataset
|
| 131 |
+
-> ROOT/Awkward -> DGL graph + labels/tracking/globals -> .bin cache
|
| 132 |
+
-> fold_selection -> prebatch/padding -> GraphDataLoader
|
| 133 |
+
-> Edge_Network or transfer model -> weighted loss/metrics
|
| 134 |
+
-> model_epoch_N.pt, logs, evaluation/inference output
|
| 135 |
+
```
|
| 136 |
+
|
| 137 |
+
`training_script.train` is the lifecycle implementation
|
| 138 |
+
([`training_script.py:143-614`](../legacy/root_gnn_dgl/scripts/training_script.py));
|
| 139 |
+
distributed paths use NCCL/DDP ([`training_script.py:616-839`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 140 |
+
Inference uses `CustomPreBatchedDataset`, applies a configured finish function,
|
| 141 |
+
and collects `scores`, `labels`, and `tracking_info`
|
| 142 |
+
([`inference.py:20-76`](../legacy/root_gnn_dgl/scripts/inference.py),
|
| 143 |
+
[`inference.py:223-325`](../legacy/root_gnn_dgl/scripts/inference.py)).
|
| 144 |
+
|
| 145 |
+
## 2. Dependency and data-flow map
|
| 146 |
+
|
| 147 |
+
```text
|
| 148 |
+
ROOT files (raw_dir/file_names, tree_name)
|
| 149 |
+
-> gnn4colliders.data.RootEventDataset (Uproot/Awkward)
|
| 150 |
+
-> node feature construction (dataset.py:15-50)
|
| 151 |
+
-> full_connected_graph (dataset.py:52-59)
|
| 152 |
+
-> EdgeDataset.make_graph: [deta, dphi, dR] (dataset.py:471-482)
|
| 153 |
+
-> DGL .bin cache / LazyDataset / PreBatchedDataset
|
| 154 |
+
-> GraphDataLoader -> models.GCN -> loss/metrics -> outputs
|
| 155 |
+
```
|
| 156 |
+
|
| 157 |
+
`RootDataset` provides `process`, `save`, `load`, `__getitem__`, and `__len__`
|
| 158 |
+
([`dataset.py:160-469`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py));
|
| 159 |
+
`LazyDataset` loads one chunk through a ring buffer
|
| 160 |
+
([`dataset.py:525-578`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py));
|
| 161 |
+
`PreBatchedDataset.process` selects, shuffles, batches, pads, and caches
|
| 162 |
+
([`batched_dataset.py:34-146`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
|
| 163 |
+
|
| 164 |
+
`load_config` uses PyYAML `FullLoader` and shallow `include` merging, while
|
| 165 |
+
`buildFromConfig` dynamically imports `module`, resolves `class`, merges extra
|
| 166 |
+
keys into `args`, converts list-valued weights to tensors, and injects runtime
|
| 167 |
+
arguments ([`utils.py:10-43`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)).
|
| 168 |
+
This reflection shape is a de facto interface for configured components.
|
| 169 |
+
|
| 170 |
+
### Data and preprocessing
|
| 171 |
+
|
| 172 |
+
The active node schema is seven columns: `pt`, `eta`, `phi`, `energy`, `btag`,
|
| 173 |
+
`charge`, and `node_type`. `CALC_E` is `pt*cosh(eta)`, constants are broadcast
|
| 174 |
+
per object type, `NODE_TYPE` is an integer type code, and feature scales are
|
| 175 |
+
applied columnwise ([`dataset.py:15-50`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)).
|
| 176 |
+
`full_connected_graph` makes directed all-pairs edges; `EdgeDataset` requests
|
| 177 |
+
no self-loops and stores `[deta, dphi, dR]`
|
| 178 |
+
([`dataset.py:52-59`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
|
| 179 |
+
[`dataset.py:471-482`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)).
|
| 180 |
+
|
| 181 |
+
Selections are strings evaluated with builtins disabled or
|
| 182 |
+
`(variable, cut, operator)` triples (`check_selection`, `selection_mask`;
|
| 183 |
+
[`dataset.py:75-145`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)). Fold
|
| 184 |
+
selection uses `tracking[:,0] % n_folds`; tracking column 0 is fold and column 1
|
| 185 |
+
is weight ([`utils.py:121-143`](../legacy/root_gnn_dgl/root_gnn_base/utils.py),
|
| 186 |
+
[`dataset.py:176-182`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)).
|
| 187 |
+
`hash_partition` and a seeded Torch generator control pre-batch order
|
| 188 |
+
([`batched_dataset.py:27-99`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
|
| 189 |
+
Padding modes are `NONE`, `STEPS`, `FIXED`, and `NODE`; `FIXED` is hardcoded to
|
| 190 |
+
16,000 nodes and 104,000 edges ([`batched_dataset.py:100-125`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
|
| 191 |
+
|
| 192 |
+
### Model, losses, and metrics
|
| 193 |
+
|
| 194 |
+
`Make_MLP` builds linear/ReLU/dropout blocks followed by LayerNorm
|
| 195 |
+
([`GCN.py:18-35`](../legacy/root_gnn_dgl/models/GCN.py)). Each `Edge_Network`
|
| 196 |
+
step encodes inputs, copies source/destination states to edges, updates edges,
|
| 197 |
+
sums edge messages into nodes, updates nodes, then mean-pools nodes/edges to
|
| 198 |
+
update globals ([`GCN.py:195-249`](../legacy/root_gnn_dgl/models/GCN.py)). It
|
| 199 |
+
returns logits `[graphs, out_size]` without sigmoid/softmax.
|
| 200 |
+
|
| 201 |
+
The default objective is elementwise `BCEWithLogitsLoss`, multiplied by
|
| 202 |
+
`tracking[:,1]`, averaged separately per unique label, then averaged across
|
| 203 |
+
labels ([`training_script.py:143-185`](../legacy/root_gnn_dgl/scripts/training_script.py),
|
| 204 |
+
[`training_script.py:320-359`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 205 |
+
`--abs` makes weights positive. Binary metrics use sigmoid threshold 0.5 and
|
| 206 |
+
weighted ROC AUC; multiclass metrics use argmax and one-vs-rest ROC AUC
|
| 207 |
+
([`training_script.py:438-510`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 208 |
+
Additional configurable losses/finishers live in `models/loss.py`
|
| 209 |
+
([`loss.py:6-310`](../legacy/root_gnn_dgl/models/loss.py)).
|
| 210 |
+
|
| 211 |
+
### Checkpoints and outputs
|
| 212 |
+
|
| 213 |
+
Training writes `Training_Directory/model_epoch_<epoch>.pt` containing `epoch`,
|
| 214 |
+
`model_state_dict`, `optimizer_state_dict`, and serialized `early_stop`
|
| 215 |
+
([`training_script.py:565-604`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 216 |
+
Keys strip `module.` and compiled models save the underlying `_orig_mod` state;
|
| 217 |
+
`get_last_epoch`, `get_specific_epoch`, and `get_best_epoch` load the files
|
| 218 |
+
([`utils.py:145-248`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)).
|
| 219 |
+
`evaluate` writes `evaluation_<epoch>.npz`; inference writes `.npz` fields
|
| 220 |
+
`scores`, `labels`, `tracking_info`, or adds score branches and `selection_pass`
|
| 221 |
+
to a cloned ROOT tree ([`training_script.py:57-140`](../legacy/root_gnn_dgl/scripts/training_script.py),
|
| 222 |
+
[`inference.py:328-385`](../legacy/root_gnn_dgl/scripts/inference.py)).
|
| 223 |
+
|
| 224 |
+
### Training lifecycle boundary
|
| 225 |
+
|
| 226 |
+
The active rewrite keeps lifecycle orchestration architecture-independent:
|
| 227 |
+
|
| 228 |
+
```text
|
| 229 |
+
GraphDataLoader -> GraphBatch -> Model -> Task -> Trainer
|
| 230 |
+
loss/metrics
|
| 231 |
+
```
|
| 232 |
+
|
| 233 |
+
`gnn4colliders.training.Trainer` owns device placement, train/evaluation mode,
|
| 234 |
+
gradient and optimizer steps, epoch aggregation, optional scheduler stepping,
|
| 235 |
+
early stopping, and in-memory history. Tasks own loss and metric semantics;
|
| 236 |
+
the trainer does not inspect positional tracking columns or collider-specific
|
| 237 |
+
features. Evaluation concatenates detached outputs across the complete split
|
| 238 |
+
before calling task metrics, so ROC AUC is not computed per mini-batch.
|
| 239 |
+
|
| 240 |
+
Checkpoint persistence and the Python inference/output layer are implemented
|
| 241 |
+
as separate adapters. The semantic CLI and distributed application boundary
|
| 242 |
+
are implemented in the current stack. `gnn4colliders.inference.Predictor` accumulates detached CPU
|
| 243 |
+
logits, task-defined scores/predictions, labels, and named event metadata in
|
| 244 |
+
loader order; `write_npz` is the primary named-field format and ROOT score
|
| 245 |
+
writing is an optional alignment-aware adapter.
|
| 246 |
+
|
| 247 |
+
The new lifecycle uses conventional split semantics. `train` updates model
|
| 248 |
+
parameters, `validation` is evaluated after every epoch and drives scheduler,
|
| 249 |
+
early-stopping, and later model selection, and `test` is held out. The trainer
|
| 250 |
+
does not accept a test loader in `fit`; callers evaluate the held-out test set
|
| 251 |
+
separately after training. This deliberately corrects the legacy convention
|
| 252 |
+
where a loader named `test` was used for model selection and `val` represented
|
| 253 |
+
held-out testing.
|
| 254 |
+
|
| 255 |
+
## Distributed execution
|
| 256 |
+
|
| 257 |
+
`gnn4colliders.distributed` contains the small DDP boundary used by the
|
| 258 |
+
application layer. `DistributedContext` reads the standard `torchrun`
|
| 259 |
+
environment (`RANK`, `LOCAL_RANK`, and `WORLD_SIZE`), selects the rank-local
|
| 260 |
+
device, and owns process-group cleanup. Graph samples are sharded before
|
| 261 |
+
batching; training may pad rank shards for equal step counts, while validation
|
| 262 |
+
and prediction use unpadded shards so events are not counted twice.
|
| 263 |
+
|
| 264 |
+
The configured graph `batch_size` is per process. DDP wraps an otherwise
|
| 265 |
+
ordinary model after device placement, and checkpoint state is normalized to
|
| 266 |
+
the underlying model keys. Loss gradients are synchronized by DDP; epoch
|
| 267 |
+
metrics and evaluation outputs are gathered across ranks. Rank 0 writes
|
| 268 |
+
resolved configuration, checkpoints, and NPZ predictions. Moderate-size
|
| 269 |
+
prediction gathering is in-memory; streaming/sharded output is a future
|
| 270 |
+
extension.
|
| 271 |
+
|
| 272 |
+
|
| 273 |
+
## Randomness, external services, and coupling
|
| 274 |
+
|
| 275 |
+
The CLI exposes `--seed`, but `main` passes it to model construction rather
|
| 276 |
+
than globally seeding Python, NumPy, or Torch
|
| 277 |
+
([`training_script.py:638-753`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 278 |
+
Pre-batching has explicit seeds, but `AugmentedDataset` mutates the process-wide
|
| 279 |
+
NumPy seed ([`dataset.py:716-827`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)),
|
| 280 |
+
clustering uses unseeded `torch.randperm`/`randint` (`loss.py:259-295`), and
|
| 281 |
+
model reset/fine-tuning hardcodes `torch.manual_seed(2)`
|
| 282 |
+
([`GCN.py:58-65`](../legacy/root_gnn_dgl/models/GCN.py),
|
| 283 |
+
[`GCN.py:900-915`](../legacy/root_gnn_dgl/models/GCN.py)). CUDA kernels, DDP,
|
| 284 |
+
and DataLoader behavior are not made deterministic.
|
| 285 |
+
|
| 286 |
+
Implicit coupling includes repository-relative `sys.path` insertion
|
| 287 |
+
([`training_script.py:14-20`](../legacy/root_gnn_dgl/scripts/training_script.py)),
|
| 288 |
+
dynamic imports, mutable default lists/dicts, global `FEATURE_DTYPE`
|
| 289 |
+
([`dataset.py:13`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)),
|
| 290 |
+
in-place `tracking_info` mutation ([`dataset.py:176-181`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)),
|
| 291 |
+
and in-place DGL graph mutation during forward.
|
| 292 |
+
|
| 293 |
+
The legacy environment assumes Python 3.8, PyTorch 2.0.1, CUDA 11.8, DGL
|
| 294 |
+
1.1.1, ROOT, Awkward, Uproot, PyYAML, and scikit-learn
|
| 295 |
+
([`setup/environment.yml:1-10`](../legacy/root_gnn_dgl/setup/environment.yml),
|
| 296 |
+
[`setup/environment.yml:240-295`](../legacy/root_gnn_dgl/setup/environment.yml)).
|
| 297 |
+
The active Linux development environment is intentionally separate: Python
|
| 298 |
+
3.12, PyTorch 2.2.2/CUDA 12.1, and DGL 2.4.0 from the official DGL wheel
|
| 299 |
+
repository. CUDA runtime wheels do not replace the compatible host NVIDIA
|
| 300 |
+
driver and do not encode Perlmutter module settings.
|
| 301 |
+
Standard configs assume `/global/cfs/` and `/pscratch/` paths, CUDA/NCCL,
|
| 302 |
+
Slurm, and optionally Podman-HPC. `setup/download_data.sh` downloads the
|
| 303 |
+
external Hugging Face dataset `HWresearch/Delphes`
|
| 304 |
+
([`download_data.sh:13-67`](../legacy/root_gnn_dgl/setup/download_data.sh)).
|
| 305 |
+
|
| 306 |
+
## Apparent unused or secondary code
|
| 307 |
+
|
| 308 |
+
Not selected by the standard stats/Delphes configs, or only reachable from
|
| 309 |
+
optional workflows, are `GCN_global`, `GCN_global_2way`, most transfer variants,
|
| 310 |
+
attention models, `MultiModel`, and `Clustering`
|
| 311 |
+
([`GCN.py:122-1933`](../legacy/root_gnn_dgl/models/GCN.py)); `UprootDataset`,
|
| 312 |
+
`tHbbEdgeDataset`, `AugmentedDataset`, and photon-ID paths
|
| 313 |
+
([`uproot_dataset.py:10-31`](../legacy/root_gnn_dgl/root_gnn_base/uproot_dataset.py),
|
| 314 |
+
[`dataset.py:484-827`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
|
| 315 |
+
[`photon_ID_dataset.py:1-33`](../legacy/root_gnn_dgl/root_gnn_base/photon_ID_dataset.py));
|
| 316 |
+
optional loss and similarity utilities; and the no-op
|
| 317 |
+
`root_gnn_base.utils.graph_augmentation` ([`utils.py:393-395`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)).
|
| 318 |
+
The main path evaluates `test_loaders`; validation loaders are only assembled
|
| 319 |
+
when a config has a validation fold ([`training_script.py:682-747`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 320 |
+
|
| 321 |
+
## 3. De facto interfaces to preserve
|
| 322 |
+
|
| 323 |
+
### Metadata-aware dataset boundary
|
| 324 |
+
|
| 325 |
+
The rewrite uses named `EventMetadata` (`fold`, `weight`, and stable
|
| 326 |
+
`sample_id`) instead of exposing the legacy positional tracking tensor.
|
| 327 |
+
`GraphSample`, `GraphBatch`, `SplitDefinition`, and `GraphDataLoader` form the
|
| 328 |
+
ROOT-GNN orchestration boundary. Graph caches carry feature, graph, and cache
|
| 329 |
+
schema versions and reject incompatible artifacts before loading.
|
| 330 |
+
|
| 331 |
+
The current cache implementation stores processed `GraphSample` values (the
|
| 332 |
+
Level-2 cache). The separation from `RootEventDataset` is intentional: a
|
| 333 |
+
future Level-1 cache can store normalized `EventSample`/feature data for
|
| 334 |
+
sequence or transformer representations without requiring DGL graph caches.
|
| 335 |
+
|
| 336 |
+
The rewrite's shared data boundary is `gnn4colliders.data`: it reads selected
|
| 337 |
+
ROOT/Awkward branches and returns architecture-neutral event samples. Feature
|
| 338 |
+
construction and graph building remain separate downstream boundaries, so the
|
| 339 |
+
same samples can be reused by non-graph model families.
|
| 340 |
+
|
| 341 |
+
### Configuration and CLI boundary
|
| 342 |
+
|
| 343 |
+
Hydra composes semantic YAML groups under `configs/` and passes the resolved
|
| 344 |
+
configuration to explicit application factories in `gnn4colliders.config`.
|
| 345 |
+
Those factories allow-list supported models, tasks, and trainer components;
|
| 346 |
+
YAML is never treated as an arbitrary Python import specification. The thin
|
| 347 |
+
`gnn4colliders` CLI selects `prepare`, `train`, `evaluate`, or `predict` and
|
| 348 |
+
delegates to the stable data, training, checkpoint, and inference APIs. A new
|
| 349 |
+
experiment should generally be a YAML change; new behavior belongs in Python.
|
| 350 |
+
|
| 351 |
+
1. YAML `module`, `class`, `args`, plus runtime `sample_graph` and
|
| 352 |
+
`sample_global` injection.
|
| 353 |
+
2. Dataset items `(DGLGraph, label, tracking, global_features)`.
|
| 354 |
+
3. `ndata['features']`, `edata['features']`, seven node columns, and three edge
|
| 355 |
+
columns in `[deta, dphi, dR]` order.
|
| 356 |
+
4. Tracking column 0 fold and column 1 weight semantics.
|
| 357 |
+
5. `model(graph, global_feats)`, logits shape `[batch, out_size]`, and
|
| 358 |
+
`representation` where used.
|
| 359 |
+
6. Weighted per-label loss, metric thresholds, checkpoint keys/prefix cleanup,
|
| 360 |
+
epoch filenames, and `.npz`/ROOT output fields.
|
| 361 |
+
|
| 362 |
+
## 4. Ambiguous behavior
|
| 363 |
+
|
| 364 |
+
- Historical edge order/self-loop expectations; empty and padding graph inputs.
|
| 365 |
+
- Whether negative weights are meaningful or should always be absolute.
|
| 366 |
+
- Whether “validation” is intended to differ from the active test-loader path.
|
| 367 |
+
- Shape semantics of multi-label finishers and experimental transfer classes.
|
| 368 |
+
- Whether chunk IDs must match historical `np.array_split` boundaries.
|
| 369 |
+
- Required behavior for missing branches and dynamic selection expressions.
|
| 370 |
+
|
| 371 |
+
## 5. Recommended rewrite boundaries
|
| 372 |
+
|
| 373 |
+
Separate typed configuration; ROOT/Awkward I/O; selections/folds/features/
|
| 374 |
+
edges; DGL cache/lazy loading/batching; active models and checkpoint adapters;
|
| 375 |
+
objectives/metrics; training lifecycle; and inference/ONNX applications.
|
| 376 |
+
Establish parity for the active `LazyDataset -> PreBatchedDataset ->
|
| 377 |
+
Edge_Network` binary/multiclass path first. Add experimental classes only when
|
| 378 |
+
a config or consumer proves they are required.
|
docs/compatibility.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Compatibility boundary
|
| 2 |
+
|
| 3 |
+
The package's canonical APIs use named `EventMetadata` fields and the version
|
| 4 |
+
1 checkpoint schema. Compatibility is explicit and one-way: old artifacts are
|
| 5 |
+
adapted into the new representation and are never rewritten implicitly.
|
| 6 |
+
|
| 7 |
+
| Artifact or behavior | Supported | Boundary | Notes |
|
| 8 |
+
| --- | :---: | --- | --- |
|
| 9 |
+
| New checkpoint (`schema_version: 1`) | yes | `CheckpointManager` | Full model/lifecycle resume when state is present |
|
| 10 |
+
| Legacy `model_epoch_N.pt` checkpoint | yes | `gnn4colliders.compat.load_legacy_checkpoint` | Model state and active early-stop fields are adapted |
|
| 11 |
+
| Legacy DDP/compiled prefixes | yes | `normalize_legacy_state_dict_keys` | Supports `module.` and `_orig_mod.` |
|
| 12 |
+
| Legacy ROOT-GNN classifier name | yes | `map_legacy_edge_network_state_dict` | Maps `classify` to `classifier` |
|
| 13 |
+
| Legacy optimizer state | partial | legacy checkpoint adapter | Loaded when present; scheduler state is not available in the historical format |
|
| 14 |
+
| Legacy scheduler state | no | — | Historical checkpoints do not carry a supported scheduler state |
|
| 15 |
+
| Positional tracking rows | yes, at ingestion boundary | `EventMetadata.from_legacy_tracking` | Exactly `tracking[0] = fold`, `tracking[1] = weight`; shorter rows fail |
|
| 16 |
+
| Generic/unknown tracking layouts | no | — | The package does not guess historical column meanings |
|
| 17 |
+
| Modern NPZ output | yes | `inference.write_npz` | Named fields: `sample_id`, `logits`, `scores`, `predictions`, and available metadata |
|
| 18 |
+
| Historical `tracking_info` NPZ output | no | — | No active consumer remains; positional output is intentionally unsupported |
|
| 19 |
+
| Legacy YAML `module`/`class`/`args` | compatibility only | configuration boundary | Accepted only where the semantic factory can safely interpret it; new configs use semantic model names |
|
| 20 |
+
|
| 21 |
+
The frozen `legacy/` tree remains available to parity tests and historical
|
| 22 |
+
investigation. Production modules do not import executable code from it.
|
docs/configuration.md
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Configuration guide
|
| 2 |
+
|
| 3 |
+
The CLI composes the installed `gnn4colliders.configs/config.yaml` with semantic
|
| 4 |
+
Hydra groups. A config
|
| 5 |
+
describes experiment intent; it does not contain arbitrary Python module or
|
| 6 |
+
class import paths.
|
| 7 |
+
|
| 8 |
+
## Groups
|
| 9 |
+
|
| 10 |
+
| Group | Purpose |
|
| 11 |
+
| --- | --- |
|
| 12 |
+
| `data` | ROOT source or graph-cache path, batch settings, and fold splits |
|
| 13 |
+
| `model` | Model family and supported ROOT-GNN constructor settings |
|
| 14 |
+
| `task` | Binary or multiclass loss, score, and metric semantics |
|
| 15 |
+
| `trainer` | Device, seed, epochs, optimizer, scheduler, and early stopping |
|
| 16 |
+
| `checkpoint` | Output directory, resume checkpoint, or pretrained checkpoint |
|
| 17 |
+
| `inference` | Checkpoint, split, output path, and output format |
|
| 18 |
+
| `environment` | Device and experiment output root |
|
| 19 |
+
| `distributed` | Single-process/DDP selection and process-group backend |
|
| 20 |
+
|
| 21 |
+
The active model groups are `root_gnn/edge_network` and
|
| 22 |
+
`root_gnn/fine_tuned_edge_network`. The active task groups are
|
| 23 |
+
`pretraining_multiclass`, `binary_classification`, and `tth_cp_finetune`.
|
| 24 |
+
|
| 25 |
+
## Preparation
|
| 26 |
+
|
| 27 |
+
`prepare` requires `data.files`, `data.cache.path`, `data.feature_branches`,
|
| 28 |
+
`data.object_types`, and `data.scales`. `feature_branches` follows the shared
|
| 29 |
+
seven-column feature contract: one branch/constant specification per output
|
| 30 |
+
column and one entry per configured object type. `CALC_E` and `NODE_TYPE` are
|
| 31 |
+
reserved derived specifications. `object_types` entries are `vector` or
|
| 32 |
+
`single`. Preparation reads the configured tree in file order and writes a
|
| 33 |
+
versioned `GraphSampleCache`.
|
| 34 |
+
|
| 35 |
+
Example overrides are easiest to maintain in a YAML file for real datasets:
|
| 36 |
+
|
| 37 |
+
```yaml
|
| 38 |
+
# project-local example: data/my_events.yaml
|
| 39 |
+
files: [data/events.root]
|
| 40 |
+
tree_name: Events
|
| 41 |
+
cache:
|
| 42 |
+
path: cache/events.pt
|
| 43 |
+
feature_branches:
|
| 44 |
+
- [jet_pt]
|
| 45 |
+
- [jet_eta]
|
| 46 |
+
- [jet_phi]
|
| 47 |
+
- CALC_E
|
| 48 |
+
- [1.0]
|
| 49 |
+
- [0.0]
|
| 50 |
+
- NODE_TYPE
|
| 51 |
+
object_types: [vector]
|
| 52 |
+
scales: [1, 1, 1, 1, 1, 1, 1]
|
| 53 |
+
fold_var: eventNumber
|
| 54 |
+
weight_var: weight
|
| 55 |
+
```
|
| 56 |
+
|
| 57 |
+
Then compose it with `data=my_events`. The cache stores processed graph
|
| 58 |
+
samples, labels, globals, named metadata, and feature/graph/cache schema
|
| 59 |
+
versions. Changing the feature or graph schema requires a new compatible cache;
|
| 60 |
+
loading a mismatched schema raises an error.
|
| 61 |
+
|
| 62 |
+
## Common overrides
|
| 63 |
+
|
| 64 |
+
```bash
|
| 65 |
+
uv run gnn4colliders train \
|
| 66 |
+
data.cache.path=cache/events.pt \
|
| 67 |
+
data.batch_size=64 \
|
| 68 |
+
trainer.max_epochs=50 \
|
| 69 |
+
trainer.seed=123 \
|
| 70 |
+
environment.output_root=outputs/my_run
|
| 71 |
+
```
|
| 72 |
+
|
| 73 |
+
`data.batch_size` is per process. `data.splits.train_folds`,
|
| 74 |
+
`validation_folds`, and `test_folds` define conventional train/validation/test
|
| 75 |
+
selection and must be disjoint. Model/task mismatches are rejected during
|
| 76 |
+
config validation; binary tasks require `model.out_size=1`, while multiclass
|
| 77 |
+
tasks require `model.out_size=task.num_classes`.
|
| 78 |
+
|
| 79 |
+
## Checkpoints and resolved configuration
|
| 80 |
+
|
| 81 |
+
Training writes `epoch_####.pt` and the fully resolved configuration at
|
| 82 |
+
`<environment.output_root>/resolved_config.yaml`. A checkpoint includes schema
|
| 83 |
+
version, model weights/config, task config, trainer/optimizer/scheduler state,
|
| 84 |
+
early stopping state, metadata, and optional RNG state. Set
|
| 85 |
+
`checkpoint.resume=/path/to/epoch_####.pt` to continue a run. Set
|
| 86 |
+
`checkpoint.pretrained=/path/to/epoch_####.pt` with the fine-tuned model group
|
| 87 |
+
to load weights into a new task head; these options are mutually exclusive.
|
| 88 |
+
|
| 89 |
+
## Environment profiles
|
| 90 |
+
|
| 91 |
+
`environment=local` selects CPU by default. `environment=macos` selects the
|
| 92 |
+
Apple MPS device for the portable PyTorch ROOT-GNN backend.
|
| 93 |
+
`environment=perlmutter` selects
|
| 94 |
+
the CUDA device and a conventional output-root pattern. Profiles should hold
|
| 95 |
+
device/output policy only; site-specific module loads and filesystem paths
|
| 96 |
+
belong in a launcher or shell environment.
|
docs/design.md
DELETED
|
@@ -1,540 +0,0 @@
|
|
| 1 |
-
# Design
|
| 2 |
-
|
| 3 |
-
## Modules
|
| 4 |
-
|
| 5 |
-
| Module | Contents |
|
| 6 |
-
| --- | --- |
|
| 7 |
-
| `config.py` | the experiment dataclasses, YAML loading and overrides |
|
| 8 |
-
| `data.py` | ROOT reading, Hugging Face download, derived branches, object features, `PackedEvents` and its cache |
|
| 9 |
-
| `loader.py` | `EventLoader`: deterministic shuffling, DDP sharding, collation workers |
|
| 10 |
-
| `graphs.py` | fully connected `TensorGraph` batches, edge features, CUDA-graph padding buffers |
|
| 11 |
-
| `sequences.py` | padded token batches for the transformer and Deep Sets, event augmentations |
|
| 12 |
-
| `models.py` | `EdgeNetwork`, `FineTunedEdgeNetwork`, `ObjectTransformer`, `TransformerClassifier`, `DeepSets`, checkpoint loading |
|
| 13 |
-
| `jepa.py` | JEPA pretraining of the transformer or edge network encoder (encoding or kinematic targets), event embeddings |
|
| 14 |
-
| `contrastive.py` | contrastive (SimCLR) pretraining of the same encoders |
|
| 15 |
-
| `probe.py` | k-NN and linear probes of frozen encoders, linear CKA between them |
|
| 16 |
-
| `pfn.py` | our prior-fitted network over events and the task prior of meta-training |
|
| 17 |
-
| `tabular.py` | event rows and encoder embeddings for TabPFN, TabICL and TabDPT; LoRA meta-training of TabPFN and TabICL |
|
| 18 |
-
| `tasks.py` | multiclass and binary losses and metrics, the pretraining and PFN losses |
|
| 19 |
-
| `train.py` | `Trainer`, early stopping, checkpoints |
|
| 20 |
-
| `cuda_graphs.py` | captured training steps |
|
| 21 |
-
| `distributed.py` | `torchrun` process group and collectives |
|
| 22 |
-
| `inference.py` | model outputs over a loader, prediction files |
|
| 23 |
-
| `export.py` | ONNX export |
|
| 24 |
-
| `cli.py` | the six commands |
|
| 25 |
-
|
| 26 |
-
## Data flow
|
| 27 |
-
|
| 28 |
-
`prepare` reads each ROOT file in chunks of 100,000 entries (in parallel with
|
| 29 |
-
`data.prepare_workers`) and stores every event's objects as rows of one
|
| 30 |
-
`[objects, 7]` tensor with offsets, plus per-event labels, folds, weights and
|
| 31 |
-
source file and entry. Nothing in the cache depends on the model: graphs and
|
| 32 |
-
token sequences are built per batch, in the loader workers.
|
| 33 |
-
|
| 34 |
-
A batch of the edge network is a set of fully connected graphs in flat
|
| 35 |
-
edge-index form (`edge_src`, `edge_dst`, `node_batch`, `edge_batch`). Edges
|
| 36 |
-
are directed, source-major and without self-loops except for one-node graphs,
|
| 37 |
-
with features `[deta, dphi, dR]`. These conventions, the seven node columns,
|
| 38 |
-
the MLP layout and the parameter names are those of the original ROOT-GNN, so
|
| 39 |
-
the pretrained checkpoints in `Pretrained_GNN/` load without conversion.
|
| 40 |
-
|
| 41 |
-
The transformer and Deep Sets take each event's objects as tokens
|
| 42 |
-
`[events, objects, 7]`, zero-padded to the largest event of the batch, with
|
| 43 |
-
a mask of real objects. A token is a linear map of the kinematic columns
|
| 44 |
-
(`phi` as its cosine and sine) plus a learned embedding of `node_type`. The
|
| 45 |
-
objects are stored by type (jets, electrons, muons, photons, MET), so a
|
| 46 |
-
position embedding would mostly relearn the type; without one, neither
|
| 47 |
-
model depends on object order, as with the edge network.
|
| 48 |
-
|
| 49 |
-
* The transformer is a stack of pre-norm attention blocks, a mean over real
|
| 50 |
-
objects and a linear classifier. With `model.pair_bias` (as in the
|
| 51 |
-
Particle Transformer, Qu et al. 2022), an MLP of the pair features
|
| 52 |
-
`[deta, dphi, dR, log(1 + m^2)]` (the edge features plus the invariant
|
| 53 |
-
mass of the pair, objects taken as massless) adds a bias per head to the
|
| 54 |
-
attention logits, so the transformer sees the same pairwise angles as the
|
| 55 |
-
edge network.
|
| 56 |
-
* Deep Sets (Zaheer et al. 2017; the particle flow network of Komiske et
|
| 57 |
-
al. 2019) applies an MLP to each object, sums over real objects and
|
| 58 |
-
applies an MLP to the sum: the baseline without object interactions.
|
| 59 |
-
|
| 60 |
-
### JEPA pretraining
|
| 61 |
-
|
| 62 |
-
`model.name: jepa` pretrains an object encoder without labels, as in
|
| 63 |
-
I-JEPA (Assran et al. 2023), on the 12 Delphes samples of the supervised
|
| 64 |
-
pretraining. `model.backbone` selects the encoder: the transformer's
|
| 65 |
-
(`configs/delphes_jepa.yaml`) or the edge network, of the size of the
|
| 66 |
-
supervised pretrained models (`configs/delphes_jepa_edge.yaml`), whose
|
| 67 |
-
object encodings are its node states after the last processing step and
|
| 68 |
-
whose context is the fully connected graph of the visible objects only.
|
| 69 |
-
|
| 70 |
-
* a random `model.mask_fraction` of each event's objects (rounded, at least
|
| 71 |
-
one, and at least one left visible) is hidden;
|
| 72 |
-
* the context encoder sees the visible objects; the target encoder, an
|
| 73 |
-
exponential moving average of it (momentum rising linearly from
|
| 74 |
-
`model.ema_momentum` to 1 over training) without gradients, sees all
|
| 75 |
-
objects;
|
| 76 |
-
* a predictor (`model.predictor_layers` attention blocks over the visible
|
| 77 |
-
objects' encodings and one query per hidden object) predicts the hidden
|
| 78 |
-
objects' layer-normalized target encodings, with a mean squared error.
|
| 79 |
-
A query is told only the hidden object's type and its index among the
|
| 80 |
-
objects of that type (their pt order), not its kinematics; pair features
|
| 81 |
-
are used only between visible objects. For the edge network, its decoded
|
| 82 |
-
global state (what its classifier reads) is one more predictor input, so
|
| 83 |
-
the whole network is pretrained.
|
| 84 |
-
|
| 85 |
-
The history reports `target_spread`, the standard deviation of the target
|
| 86 |
-
encodings over a batch's objects averaged over dimensions, which falls
|
| 87 |
-
towards zero if the representation collapses. The validation loss compares
|
| 88 |
-
predictions with a moving target, so it does not select epochs: fine-tuning
|
| 89 |
-
starts from `last.pt` (`configs/gamgam_jepa_finetune.yaml` for the
|
| 90 |
-
transformer, `configs/gamgam_finetune.yaml` with `model.pretrained` for the
|
| 91 |
-
edge network; encoder sizes must match). With `model.freeze_backbone=true` only the classifier is
|
| 92 |
-
trained (a linear probe); `model.pretrained=null` trains the same
|
| 93 |
-
architecture from scratch.
|
| 94 |
-
|
| 95 |
-
`model.variance_weight` adds the VICReg variance term (Bardes et al. 2022):
|
| 96 |
-
the mean shortfall below 1 of each dimension's standard deviation over the
|
| 97 |
-
batch's context encodings (and the edge network's global states).
|
| 98 |
-
|
| 99 |
-
First checks (one A100, `train.fast=true`, 2 pretraining epochs on Delphes
|
| 100 |
-
fold 2, then 2 GamGam epochs, validation AUC, seed 1; not converged):
|
| 101 |
-
|
| 102 |
-
| GamGam model | Validation AUC |
|
| 103 |
-
| --- | --- |
|
| 104 |
-
| transformer from scratch / from JEPA | 0.898 / 0.904 |
|
| 105 |
-
| edge network from scratch | 0.895 |
|
| 106 |
-
| edge network from the supervised pretrained model | 0.907 |
|
| 107 |
-
| edge network from JEPA, last node states only | 0.845 |
|
| 108 |
-
| edge network from JEPA, mean node states | 0.869 |
|
| 109 |
-
| the same with `variance_weight` 0.1 / 1 | 0.874 / 0.810 |
|
| 110 |
-
|
| 111 |
-
The edge network's JEPA encodings collapse without the variance term
|
| 112 |
-
(`target_spread` 0.09 against about 1 with it), and none of the edge
|
| 113 |
-
network variants yet beats training from scratch.
|
| 114 |
-
|
| 115 |
-
### Contrastive and masked-object pretraining, and probes
|
| 116 |
-
|
| 117 |
-
Two more label-free objectives pretrain the same encoders on the same
|
| 118 |
-
samples, and their encoders load the same way (`context_encoder`):
|
| 119 |
-
|
| 120 |
-
* `contrastive.py` (`configs/delphes_contrastive.yaml`): SimCLR, as JetCLR
|
| 121 |
-
and RS3L for jets. Two views of each event (random phi rotation, eta
|
| 122 |
-
flip, momentum scale within `model.scale_jitter`, each object but the
|
| 123 |
-
first dropped with `model.object_dropout`) are embedded and projected;
|
| 124 |
-
the NT-Xent loss at `model.temperature` contrasts them with the other
|
| 125 |
-
events of the batch. `embedding_spread` watches for collapse.
|
| 126 |
-
* `model.jepa_target: features` (`configs/delphes_masked.yaml`): masked
|
| 127 |
-
object modelling (Golling et al. 2024). JEPA's masking and predictor
|
| 128 |
-
predict the hidden objects' kinematics instead of their target
|
| 129 |
-
encodings, with no target encoder: the comparison with JEPA isolates
|
| 130 |
-
predicting in embedding space.
|
| 131 |
-
|
| 132 |
-
`gnn4colliders probe` compares encoders without fine-tuning (as jBOT):
|
| 133 |
-
k-NN (cosine, `model.probe_neighbours`) and linear (L-BFGS logistic
|
| 134 |
-
regression, `model.probe_l2`) classifiers of frozen event embeddings,
|
| 135 |
-
trained on `data.train_events` events. A randomly initialized encoder
|
| 136 |
-
(`model.pretrained=null`) is the reference; supervised checkpoints probe
|
| 137 |
-
too. Results on GamGam: "Label efficiency on GamGam" below.
|
| 138 |
-
|
| 139 |
-
`scripts/compare_encoders.py` compares encoders of one architecture by
|
| 140 |
-
linear CKA (Kornblith et al. 2019), as the paper does: of their event
|
| 141 |
-
embeddings (a matrix), and layer by layer against the first one (the mean
|
| 142 |
-
over objects after each transformer block, or the edge network's mean node
|
| 143 |
-
states after each step, then the event embedding). With a random encoder
|
| 144 |
-
first, it shows where each objective moves the representation.
|
| 145 |
-
|
| 146 |
-
### In-context models: TabPFN, TabICL, TabDPT and a collider PFN
|
| 147 |
-
|
| 148 |
-
Both classify a dataset's events given `data.train_events` labelled
|
| 149 |
-
training events (an equal number per class) as context, without training on
|
| 150 |
-
that dataset; `cli.in_context_forward` builds them for `evaluate` and
|
| 151 |
-
`predict`.
|
| 152 |
-
|
| 153 |
-
* `tabular.py`: TabPFN (Prior Labs; `model.tabpfn_version`, default 3.5)
|
| 154 |
-
on fixed-width rows: the leading `model.slots` objects of each type (6
|
| 155 |
-
jets, 2 electrons, 2 muons, 3 photons, MET) with their six kinematic
|
| 156 |
-
columns, NaN when absent, and the number of objects of each type.
|
| 157 |
-
* `pfn.py`: our own prior-fitted network. The object transformer (which
|
| 158 |
-
can start from the JEPA encoder) embeds each event; the context events get
|
| 159 |
-
a label embedding; `model.pfn_layers` attention blocks run over a task's
|
| 160 |
-
events, every event attending to the context events only, so queries are
|
| 161 |
-
independent and the context's keys and values are computed once. It is
|
| 162 |
-
meta-trained (`task.name: pfn`) on tasks drawn from the Delphes samples:
|
| 163 |
-
2-10 classes of `task.prior_classes` in random order, Dirichlet class
|
| 164 |
-
proportions, 16-2048 context events (log-uniform), 256 queries, and per
|
| 165 |
-
event a random phi rotation and eta flip, per task a momentum and energy
|
| 166 |
-
scale within `task.energy_jitter`. The GamGam processes are among the
|
| 167 |
-
Delphes samples; `task.prior_classes=[1,6,7,8,9,10,11]` leaves them out.
|
| 168 |
-
`configs/opendata_pfn.yaml` (written by `scripts/opendata_prior.py`)
|
| 169 |
-
meta-trains it on the ATLAS Open Data 2025 MC instead: 925 classes of 331
|
| 170 |
-
processes, H -> gamma gamma left out, a tenth of the processes held out
|
| 171 |
-
for validation, classes mixing 1 to 3 processes.
|
| 172 |
-
* Options that bring the PFN closer to TabPFN, all off by default (the
|
| 173 |
-
default model's outputs match the earlier code to 2e-7):
|
| 174 |
-
`model.pfn_summary_tokens` learned tokens carry each event through the
|
| 175 |
-
encoder, with a context event's label added at the input instead of after
|
| 176 |
-
the encoder; `model.pfn_alternating` adds attention across the task's
|
| 177 |
-
events (summary tokens, context only) after every encoder block, as
|
| 178 |
-
TabPFN alternates attention over features and over rows, so an event's
|
| 179 |
-
encoding depends on the task; `model.pfn_normalize` scales each task's
|
| 180 |
-
momenta so that the context's geometric-mean object pT is 50 GeV (as
|
| 181 |
-
TabPFN standardizes features by the context); `model.pfn_ensemble`
|
| 182 |
-
averages the class probabilities of passes with the events rotated in
|
| 183 |
-
phi, flipped in eta and the class labels permuted (as TabPFN ensembles
|
| 184 |
-
over class orders and preprocessings); and `task.synthetic_tasks` makes
|
| 185 |
-
a fraction of the training tasks synthetic, as TabPFN's prior draws
|
| 186 |
-
random functions: events of 1 to 3 processes ranked by a random network
|
| 187 |
-
(16 tanh units) of 1 to 4 of 21 event observables (per object type the
|
| 188 |
-
count, scalar sum pT, leading pT and mass; the b-tag count), cut into
|
| 189 |
-
classes of Dirichlet fractions. A large context is encoded in chunks, one
|
| 190 |
-
block at a time, but the alternating PFN keeps every context event's
|
| 191 |
-
tokens and the keys and values of every block on the GPU: 100k events
|
| 192 |
-
fit, all 1.98M GamGam training events do not (out of memory on a 40 GB
|
| 193 |
-
A100).
|
| 194 |
-
* Options from TabPFN-3.5 (219M parameters, 79 times ours; read from
|
| 195 |
-
its checkpoint and code), all off by default: `model.pfn_retrieval` makes
|
| 196 |
-
the logits the log of an attention-weighted vote of the context's labels
|
| 197 |
-
(TabPFN's many-class decoder) instead of a linear classifier;
|
| 198 |
-
`model.pfn_qk_norm` RMS-norms queries and keys per head and scales the
|
| 199 |
-
queries by a learned factor times the log of the number of keys
|
| 200 |
-
(TabPFN-3's scalable softmax), in all attention across events;
|
| 201 |
-
`model.pfn_query_kv` lets the queries read the context through one
|
| 202 |
-
key/value head of their own, so the memory of a large context is
|
| 203 |
-
`n_heads` times smaller; `model.pfn_concat` joins the summary tokens into
|
| 204 |
-
one token `pfn_summary_tokens * hid_size` wide for the blocks after the
|
| 205 |
-
encoder (TabPFN-3.5 joins 8 x 128 to 1024), with the label embedded
|
| 206 |
-
again; `model.pfn_fourier` learned frequencies of each object's log pT,
|
| 207 |
-
eta and phi and `model.pfn_ranks` (each object's pT and energy ranked
|
| 208 |
-
among the context's objects of its type, as TabPFN adds ECDF ranks) are
|
| 209 |
-
added to the object embeddings, through maps that start at zero.
|
| 210 |
-
`train.lr_schedule: cosine` warms up over `train.warmup_epochs`.
|
| 211 |
-
* Meta-training speed: training batches are encoded in 4 parts of events
|
| 212 |
-
with similar numbers of objects (only 37% of the padded object tokens
|
| 213 |
-
were real), tasks are sampled in the loader workers rather than all at
|
| 214 |
-
the start of an epoch, and under `torchrun` all ranks draw the same
|
| 215 |
-
context size for a step, so that they wait less for each other. Steps are
|
| 216 |
-
drawn from a generator per seed, epoch, rank and step, so the tasks
|
| 217 |
-
differ from those of runs before this change. In-context inference sorts
|
| 218 |
-
the context by number of objects and attends in chunks of
|
| 219 |
-
`data.batch_size` events.
|
| 220 |
-
|
| 221 |
-
* `tabular.py` also runs TabICLv2 (Qu et al., ICML 2026; BSD-3, open
|
| 222 |
-
pretraining code) on the same rows, as a second tabular in-context model.
|
| 223 |
-
Rows can measure azimuths from the leading object's (`model.relative_phi`)
|
| 224 |
-
and end with the invariant mass and Delta R of every pair of the
|
| 225 |
-
`model.pair_objects` leading objects by pt (energies are `pt cosh(eta)`,
|
| 226 |
-
so objects are massless; a pair with MET gives a transverse-like mass).
|
| 227 |
-
Both are off by default, so the default rows are unchanged.
|
| 228 |
-
* `tabular.py` also runs TabDPT (Ma et al. 2024; Apache-2.0), trained on
|
| 229 |
-
real tables rather than a synthetic prior; with
|
| 230 |
-
`model.tabdpt_context_size` each event's context is its nearest context
|
| 231 |
-
events (faiss retrieval), beyond what fits in one pass. It computes in
|
| 232 |
-
float64 in places (on a Mac it runs on the CPU), and on macOS faiss and
|
| 233 |
-
torch crash together unless `OMP_NUM_THREADS=1`.
|
| 234 |
-
* Tabular models can see, instead of or beside the rows, the event
|
| 235 |
-
embeddings of a pretrained object encoder (`model.tabular_inputs:
|
| 236 |
-
embeddings` or `both`, the encoder of `model.backbone` and
|
| 237 |
-
`model.pretrained`): physics-aware embeddings under a generic tabular
|
| 238 |
-
prior. In meta-training the encoder trains with the LoRA updates unless
|
| 239 |
-
`model.freeze_backbone`.
|
| 240 |
-
* `tabular.py` also meta-trains the TabPFN-3.5 network itself on the
|
| 241 |
-
PFN's tasks (`model.name: tabpfn`, `task.name: pfn`), on the rows above
|
| 242 |
-
(built after the augmentation), starting from Prior Labs' weights. Calling
|
| 243 |
-
the network directly skips TabPFN's preprocessing and ensembling, which
|
| 244 |
-
on the GamGam fixtures cost nothing (validation AUC 0.852 against 0.843
|
| 245 |
-
with `predict_proba`, 61 events). By default the network is frozen and
|
| 246 |
-
the four attention projections and two MLP layers of each of its 24
|
| 247 |
-
in-context blocks get rank-8 LoRA updates (2.8M parameters, zero at the
|
| 248 |
-
start, so training starts from TabPFN as released); `model.lora_rank=0`
|
| 249 |
-
trains all weights. In context, the context's keys and values are cached
|
| 250 |
-
(the same logits as one pass over context and queries, to 2e-5). It
|
| 251 |
-
separates what Prior Labs' prior and what collider tasks contribute:
|
| 252 |
-
TabPFN as released, TabPFN meta-trained here, and our PFN. TabICL
|
| 253 |
-
(`configs/delphes_tabicl.yaml`; BSD-3, so meta-trained weights can be
|
| 254 |
-
shared) meta-trains the same way. Its attention reads fused projection
|
| 255 |
-
weights directly, so LoRA is a parametrization of the weights (12 blocks,
|
| 256 |
-
0.59M of 28M parameters at rank 8), for TabPFN too. TabICL's network takes
|
| 257 |
-
no NaN: rows are standardized by the context's statistics, absent values
|
| 258 |
-
at the mean, clipped at 8 sigma. On the fixtures this gives AUC 0.827
|
| 259 |
-
against 0.820 with its classifier, and the cached in-context forward
|
| 260 |
-
equals the training forward only in float32 (with mixed precision on the
|
| 261 |
-
CPU, AUC 0.59).
|
| 262 |
-
|
| 263 |
-
### Label efficiency on GamGam (first study, 2026-10-22)
|
| 264 |
-
|
| 265 |
-
GamGam validation AUC (%) against the number of labelled training events N
|
| 266 |
-
(`data.train_events`, equal per class), one seed, on one 4-GPU A100 node in
|
| 267 |
-
an interactive allocation (3.5 h). Each entry is evaluated on 20,000 random
|
| 268 |
-
validation events (`data.eval_events=20000`, about +-0.3); on all 246,922,
|
| 269 |
-
evaluated for the supervised runs and the leading TabPFN runs, each is 0.05
|
| 270 |
-
to 0.41 lower, in the same order (TabPFN-3.5: 80.28, 86.92, 89.01, 0.08 to
|
| 271 |
-
0.13 lower, matching the M2 Max's 80.26 and 86.93).
|
| 272 |
-
|
| 273 |
-
| model | N = 100 | 1000 | 10000 | 50000 |
|
| 274 |
-
|---|---|---|---|---|
|
| 275 |
-
| edge network, from scratch | 78.00 | 82.74 | 87.66 | |
|
| 276 |
-
| edge network, from `Pretrained_GNN` | 84.95 | 87.48 | 89.98 | |
|
| 277 |
-
| transformer, from scratch | 72.33 | 83.47 | 87.03 | |
|
| 278 |
-
| transformer, from JEPA | 73.25 | 85.10 | 88.60 | |
|
| 279 |
-
| transformer, from masked objects | 75.92 | 86.12 | 88.73 | |
|
| 280 |
-
| TabPFN-3.5 (rows) | 80.41 | 87.05 | 89.09 | 89.56 |
|
| 281 |
-
| TabPFN-3.5 (rows, `relative_phi`, `pair_objects: 4`) | 80.61 | 87.78 | 89.56 | |
|
| 282 |
-
| TabPFN-3.5 (rows + JEPA embeddings) | 80.97 | 87.43 | 89.44 | |
|
| 283 |
-
| TabPFN-3.5 (rows + masked-object embeddings) | 82.99 | 88.22 | 89.94 | |
|
| 284 |
-
| TabPFN-3.5 (rows + `Pretrained_GNN` embeddings) | 87.32 | 88.96 | 90.07 | |
|
| 285 |
-
| TabPFN-3.5, released network, direct forward | 80.74 | 86.64 | 89.03 | |
|
| 286 |
-
| TabPFN-3.5, meta-trained on Delphes (LoRA) | 80.52 | 84.77 | 88.34 | |
|
| 287 |
-
| TabICLv2 | 80.53 | 85.68 | 88.41 | 89.12 |
|
| 288 |
-
| TabDPT | 77.48 | 84.71 | 88.42 | |
|
| 289 |
-
| our PFN, meta-trained on Delphes | 78.13 | 76.18 | 76.51 | |
|
| 290 |
-
|
| 291 |
-
Probes (k-NN / linear) of the frozen encoders, the same N:
|
| 292 |
-
|
| 293 |
-
| encoder | N = 100 | 1000 | 10000 |
|
| 294 |
-
|---|---|---|---|
|
| 295 |
-
| random | 73.4 / 72.6 | 76.6 / 83.1 | 79.5 / 85.4 |
|
| 296 |
-
| JEPA | 73.0 / 73.4 | 78.9 / 83.4 | 81.8 / 87.9 |
|
| 297 |
-
| masked objects | 75.0 / 77.5 | 81.7 / 85.7 | 84.4 / 88.5 |
|
| 298 |
-
| contrastive | 61.6 / 67.9 | 70.0 / 80.3 | 76.4 / 85.9 |
|
| 299 |
-
|
| 300 |
-
Setup (every run and its overrides: `scripts/label_efficiency_study.sh`):
|
| 301 |
-
the pretraining runs saw the same 4M class-balanced Delphes events for 8
|
| 302 |
-
epochs (`data.train_events=4000000`, 50 to 70 min each); the PFN
|
| 303 |
-
meta-trained for 25 epochs of 1000 steps (25,000 steps, 95 min) and TabPFN
|
| 304 |
-
for 6 epochs of 500 (3,000 steps, 48 min). Supervised runs train for a
|
| 305 |
-
fixed number of steps with early stopping off (`train.early_stopping=null`;
|
| 306 |
-
batch size, epochs: 100, 1000 for N = 100; 128, 400 for N = 1000; 256, 100
|
| 307 |
-
for N = 10000; 20 to 35 min at N = 100, 2 to 4 min at N = 10000) and keep
|
| 308 |
-
the epoch of best validation AUC, chosen on the same 20,000 events that are
|
| 309 |
-
reported. That choice uses validation labels the in-context models do not
|
| 310 |
-
get, and from N = 1000 most supervised runs overfit: their last epoch is far
|
| 311 |
-
below the kept one. On all validation events (92% of them not used for the
|
| 312 |
-
choice) the kept epochs lose 0.05 to 0.41, no more than the in-context runs
|
| 313 |
-
lose, so picking the maximum over noisy evaluations inflates them little:
|
| 314 |
-
|
| 315 |
-
| supervised run (N = 100 / 1000 / 10000) | kept epoch, 20k | kept epoch, all | last epoch, 20k |
|
| 316 |
-
|---|---|---|---|
|
| 317 |
-
| edge network, from scratch | 78.00 / 82.74 / 87.66 | 77.80 / 82.69 / 87.42 | 68.88 / 78.88 / 87.65 |
|
| 318 |
-
| edge network, from `Pretrained_GNN` | 84.95 / 87.48 / 89.98 | 84.54 / 87.09 / 89.74 | 80.75 / 81.58 / 86.85 |
|
| 319 |
-
| transformer, from scratch | 72.33 / 83.47 / 87.03 | 71.99 / 83.35 / 86.95 | 72.33 / 83.44 / 87.03 |
|
| 320 |
-
| transformer, from JEPA | 73.25 / 85.10 / 88.60 | 73.07 / 84.96 / 88.45 | 72.34 / 78.82 / 81.46 |
|
| 321 |
-
| transformer, from masked objects | 75.92 / 86.12 / 88.73 | 75.73 / 85.84 / 88.60 | 75.92 / 79.73 / 85.62 |
|
| 322 |
-
|
| 323 |
-
With only N labelled events there is no large labelled set to choose the
|
| 324 |
-
epoch on; without the choice (last epoch) every supervised model is below
|
| 325 |
-
TabPFN-3.5 as released at every N except `Pretrained_GNN` at N = 100.
|
| 326 |
-
|
| 327 |
-
With the configs' settings instead (batch size 1024, at most 100 epochs,
|
| 328 |
-
early stopping after 5 epochs without training-loss improvement) N = 1000
|
| 329 |
-
is one step per epoch, at most 100 steps: the transformer then reaches
|
| 330 |
-
70.04 at N = 1000 instead of 83.47. The in-context models take 10 to 30 s
|
| 331 |
-
for the 20,000 queries at N = 1000 and 10000 (TabPFN 109 s at N = 50000;
|
| 332 |
-
the first run of each, N = 100, also loads the weights).
|
| 333 |
-
|
| 334 |
-
Findings:
|
| 335 |
-
|
| 336 |
-
* TabPFN-3.5 as released, without training, comes within 0.4 (N = 1000)
|
| 337 |
-
and 0.9 (N = 10000) of fine-tuning the supervised `Pretrained_GNN` (0.2
|
| 338 |
-
and 0.7 on all validation events), with its epoch chosen on validation
|
| 339 |
-
labels, and above it from N = 1000 without that choice. It beats every
|
| 340 |
-
model trained from scratch at every N. TabICL is close behind; TabDPT is
|
| 341 |
-
behind at small N. The physics row features add up to 0.7.
|
| 342 |
-
* Encoder embeddings help TabPFN most at small N. With the label-free
|
| 343 |
-
masked-object encoder: +2.6 at N = 100. With `Pretrained_GNN` (trained
|
| 344 |
-
with labels, on Delphes samples that include the GamGam processes): the
|
| 345 |
-
best result at every N run (100 to 10000; not run at 50000), +6.9 at
|
| 346 |
-
N = 100.
|
| 347 |
-
* Meta-training on Delphes tasks does not help. TabPFN with LoRA loses 1.9
|
| 348 |
-
at N = 1000 against the same network with zero updates (its direct
|
| 349 |
-
forward costs at most 0.4 against the classifier's preprocessing and
|
| 350 |
-
ensembling). Our PFN is below TabPFN and gets worse with more context
|
| 351 |
-
(76 at N = 1000, inside the meta-training context sizes 16 to 2048): to
|
| 352 |
-
investigate before the held-out-processes run, which was not done.
|
| 353 |
-
* Of the label-free pretraining objectives, masked-object modelling is the
|
| 354 |
-
best, in probes and fine-tuned; JEPA's prediction in embedding space adds
|
| 355 |
-
nothing over it here. Both improve fine-tuning the transformer over
|
| 356 |
-
training it from scratch (+0.9 to +3.6) but stay below fine-tuning the
|
| 357 |
-
edge network from `Pretrained_GNN`. That gap mixes the architecture (from
|
| 358 |
-
scratch, the edge network is 5.7 above the transformer at N = 100) with
|
| 359 |
-
the objective (label-free or supervised); the like-for-like comparison,
|
| 360 |
-
JEPA of the edge network (`configs/delphes_jepa_edge.yaml`), was not run.
|
| 361 |
-
* Contrastive pretraining is below a random encoder in the k-NN probe at
|
| 362 |
-
every N and in the linear probe at N = 100 and 1000 (67.9 against 72.6,
|
| 363 |
-
80.3 against 83.1); at N = 10000 its linear probe is 0.5 above (85.9
|
| 364 |
-
against 85.4). Without the momentum-scale augmentation
|
| 365 |
-
(`model.scale_jitter=0`, 4 epochs) it is no better (72.4 / 84.3 at
|
| 366 |
-
N = 10000).
|
| 367 |
-
|
| 368 |
-
Not done:
|
| 369 |
-
|
| 370 |
-
* the held-out-processes meta-training (`task.prior_classes` without the
|
| 371 |
-
five GamGam processes) of the PFN, gated on it beating TabPFN, and of
|
| 372 |
-
TabPFN and TabICL;
|
| 373 |
-
* TabICL meta-training and `model.lora_rank=0`;
|
| 374 |
-
* fine-tuning from contrastive pretraining (only probed), and JEPA of the
|
| 375 |
-
edge network;
|
| 376 |
-
* more seeds, and the test split.
|
| 377 |
-
|
| 378 |
-
The loss is a weighted mean per class present in the batch, averaged over
|
| 379 |
-
those classes, which balances the classes without explicit class weights.
|
| 380 |
-
Metrics (accuracy, one-vs-rest AUC averaged over classes, macro precision,
|
| 381 |
-
recall and F1) use events with positive weight.
|
| 382 |
-
|
| 383 |
-
### The TabPFN-like PFN on GamGam (2026-10-27)
|
| 384 |
-
|
| 385 |
-
GamGam test AUC (%) in context, against the number of labelled context
|
| 386 |
-
events N (equal per class), on 50,000 test events, one seed. Every PFN is
|
| 387 |
-
meta-trained on the Open Data prior (`configs/opendata_pfn.yaml`). The
|
| 388 |
-
earlier PFN (pooled events, contexts of 16 to 4096, random encoder, fp32)
|
| 389 |
-
is the checkpoint of epoch 38. The new ones train 48 epochs of 1000 steps
|
| 390 |
-
(2.3 h on one A100 each, bf16) with 2 summary tokens, alternating
|
| 391 |
-
attention, the per-task momentum scale, the masked-object encoder
|
| 392 |
-
(`delphes_masked`), half synthetic tasks, contexts of 16 to 16384 and
|
| 393 |
-
batches of 16384 events; each ablation changes one of them.
|
| 394 |
-
|
| 395 |
-
| PFN | N = 100 | 1000 | 10000 | 100000 |
|
| 396 |
-
|---|---|---|---|---|
|
| 397 |
-
| earlier | 80.63 | 82.47 | 83.54 | 83.53 |
|
| 398 |
-
| earlier, ensemble of 8 | 81.92 | 83.87 | 84.67 | 84.77 |
|
| 399 |
-
| new | 79.12 | 80.39 | 83.79 | 84.18 |
|
| 400 |
-
| new, ensemble of 8 | 79.67 | 82.26 | 84.46 | 84.77 |
|
| 401 |
-
| new, no alternating attention | 77.14 | 81.21 | 84.43 | 84.68 |
|
| 402 |
-
| new, random encoder | 76.47 | 81.76 | 83.42 | 83.56 |
|
| 403 |
-
| new, no synthetic tasks | 81.28 | 83.14 | 84.72 | 85.45 |
|
| 404 |
-
| new, no synthetic tasks, ensemble of 8 | **83.75** | **86.11** | **86.54** | **86.52** |
|
| 405 |
-
|
| 406 |
-
Validation on held-out Open Data processes after 48 epochs (loss /
|
| 407 |
-
accuracy): new 0.772 / 0.722, no alternating 0.800 / 0.712, random encoder
|
| 408 |
-
0.809 / 0.711, no synthetic tasks 0.665 / 0.764; all still falling. One
|
| 409 |
-
pass takes 1 to 3 s for the 50,000 queries at N = 1000 and 10000 and 35 to
|
| 410 |
-
58 s at N = 100000; the ensemble of 8 takes 460 s at N = 100000.
|
| 411 |
-
|
| 412 |
-
Findings:
|
| 413 |
-
|
| 414 |
-
* Synthetic tasks hurt at every N (0.9 to 2.8 against the same PFN
|
| 415 |
-
without them; 1.8 to 4.1 ensembled) and in validation. Without them the
|
| 416 |
-
PFN beats the earlier one at every N, by 1.8 to 2.2 with both
|
| 417 |
-
ensembled.
|
| 418 |
-
* The ensemble of 8 adds 1.1 to 3.0 to the PFN without synthetic tasks.
|
| 419 |
-
* The masked-object encoder adds 2.7 at N = 100 and 0.6 at N = 100000.
|
| 420 |
-
* Alternating attention adds 2.0 at N = 100 and costs 0.5 to 0.8 from
|
| 421 |
-
N = 1000; it makes training 20% slower.
|
| 422 |
-
* Against the validation AUCs of the first study (on other events, so
|
| 423 |
-
only roughly comparable): the best PFN is above TabPFN-3.5 on rows
|
| 424 |
-
(80.41) and every supervised transformer at N = 100, below the edge
|
| 425 |
-
network from `Pretrained_GNN` (84.95) and TabPFN-3.5 with its embeddings
|
| 426 |
-
(87.32). At N = 1000 it equals the transformer from masked objects
|
| 427 |
-
(86.12) and is 0.9 and 1.4 below TabPFN-3.5 and `Pretrained_GNN`; at
|
| 428 |
-
10000 it is 2.2 to 3.4 below all three, and it gains nothing from 10000
|
| 429 |
-
to 100000.
|
| 430 |
-
|
| 431 |
-
Not done: all 1.98M training events as context (out of memory; see the
|
| 432 |
-
PFN options above), longer meta-training, and the held-out-processes
|
| 433 |
-
prior.
|
| 434 |
-
|
| 435 |
-
### TabPFN-3.5 options and a larger PFN on GamGam (2026-10-28)
|
| 436 |
-
|
| 437 |
-
Both PFNs start from the best one above (no synthetic tasks) and add the
|
| 438 |
-
TabPFN-3.5 options `pfn_retrieval`, `pfn_qk_norm`, `pfn_query_kv`,
|
| 439 |
-
`pfn_fourier=16` and `pfn_ranks`, the cosine schedule (2 warm-up epochs,
|
| 440 |
-
learning rate 1e-4) and the faster meta-training. "Small" keeps its size
|
| 441 |
-
(2.9M parameters) and trains 40 epochs on one A100 (168 s each). "Large"
|
| 442 |
-
has 4 summary tokens joined to one 512-wide token and 12 blocks after the
|
| 443 |
-
encoder (42M parameters) and trains 26 epochs on three A100s under
|
| 444 |
-
`torchrun` (280 s each, three times the tasks per step). Same test events
|
| 445 |
-
and protocol as above, one seed.
|
| 446 |
-
|
| 447 |
-
| PFN | N = 100 | 1000 | 10000 | 100000 |
|
| 448 |
-
|---|---|---|---|---|
|
| 449 |
-
| best above, ensemble of 8 | 83.75 | 86.11 | 86.54 | 86.52 |
|
| 450 |
-
| small | 81.73 | 84.65 | 86.76 | 87.66 |
|
| 451 |
-
| small, ensemble of 8 | 83.15 | 86.20 | 87.63 | 88.20 |
|
| 452 |
-
| large | 82.90 | 85.83 | 87.72 | 88.43 |
|
| 453 |
-
| large, ensemble of 8 | **84.06** | **86.80** | **88.24** | **88.62** |
|
| 454 |
-
|
| 455 |
-
Validation on held-out Open Data processes in the last epoch (loss /
|
| 456 |
-
accuracy): small 0.599 / 0.789, large 0.561 / 0.799 (best above 0.665 /
|
| 457 |
-
0.764). At N = 100000 one pass takes 21 s (small) and 87 s (large), the
|
| 458 |
-
ensemble of 8 169 s and 698 s; the large ensemble peaks at 13 GiB.
|
| 459 |
-
|
| 460 |
-
Findings:
|
| 461 |
-
|
| 462 |
-
* The options, schedule and faster training add 0.5 to 2.2 to one pass of
|
| 463 |
-
the best PFN above (81.28, 83.14, 84.72, 85.45), and the AUC now rises
|
| 464 |
-
from 10000 to 100000 (by 0.4 to 0.9) instead of staying flat.
|
| 465 |
-
* The larger PFN adds 0.8 to 1.2 more. One pass of it is 0.9 and 0.3
|
| 466 |
-
below the earlier ensemble at N = 100 and 1000 and 1.2 and 1.9 above it
|
| 467 |
-
from 10000; ensembled, it gains 0.3 to 2.1 on the earlier ensemble.
|
| 468 |
-
* The ensemble adds less than before: 0.2 to 1.6.
|
| 469 |
-
* The options were not ablated one by one, and the two runs differ in
|
| 470 |
-
epochs and GPUs as well as size.
|
| 471 |
-
* Against the validation AUCs of the first study (only roughly
|
| 472 |
-
comparable): at N = 100 the large ensemble is 3.7 above TabPFN-3.5 on
|
| 473 |
-
rows and 0.9 below `Pretrained_GNN`; at 1000 it is 0.3 and 0.7 below
|
| 474 |
-
them, at 10000 0.9 and 1.7 below.
|
| 475 |
-
|
| 476 |
-
Not done: all 1.98M training events as context, one ablation per option,
|
| 477 |
-
more seeds.
|
| 478 |
-
|
| 479 |
-
## Performance
|
| 480 |
-
|
| 481 |
-
Graphs differ in size, which suits neither `torch.compile` nor CUDA graphs
|
| 482 |
-
out of the box:
|
| 483 |
-
|
| 484 |
-
* `torch.compile(dynamic=True)` treats node and edge counts as symbolic
|
| 485 |
-
sizes, so one compiled graph serves all batches without padding.
|
| 486 |
-
* CUDA graph replay needs fixed shapes. Batches are copied into buffers
|
| 487 |
-
rounded up to multiples of 512 nodes and 4096 edges; the remainder forms 64
|
| 488 |
-
padding graphs connected only to themselves, so the real graphs' outputs do
|
| 489 |
-
not change. A step is captured once per bucket, a few per epoch. The
|
| 490 |
-
optimizer runs in Adam's `capturable` form with device-tensor learning
|
| 491 |
-
rates, which the scheduler updates in place.
|
| 492 |
-
* bfloat16 autocast covers the forward pass; logits, loss, graph sums (which
|
| 493 |
-
divide by edge counts) and the optimizer stay float32.
|
| 494 |
-
|
| 495 |
-
GamGam training step, 1024-event batches (about 5,600 nodes and 30,000
|
| 496 |
-
edges), one A100-SXM4-80GB, `OMP_NUM_THREADS=8`, four loader workers (step
|
| 497 |
-
times measured before the move to flat modules, epoch times after):
|
| 498 |
-
|
| 499 |
-
| Mode | ms per batch | Epoch with validation |
|
| 500 |
-
| --- | ---: | ---: |
|
| 501 |
-
| float32 eager | 19.2 | 37 s |
|
| 502 |
-
| CUDA graphs + TF32 | 8.5 | |
|
| 503 |
-
| `torch.compile` | 13.0 | |
|
| 504 |
-
| compile + CUDA graphs + TF32 | 6.1 | |
|
| 505 |
-
| compile + CUDA graphs + bfloat16 (`train.fast`) | 5.9 | 9 s (first: 59 s, compiling) |
|
| 506 |
-
|
| 507 |
-
Eager training is bound by kernel launches (about a thousand small kernels
|
| 508 |
-
per step), not by arithmetic; bfloat16 alone is slower because of the extra
|
| 509 |
-
cast kernels, which `torch.compile` fuses. Results of the fast modes agree
|
| 510 |
-
with float32 to rounding, not bitwise (validation AUC 0.895 against 0.897
|
| 511 |
-
after three epochs). Preparing the 2.5M GamGam events with 8 workers takes
|
| 512 |
-
about 9 s on a login node (40 s was seen on a compute node, likely a cold
|
| 513 |
-
file cache).
|
| 514 |
-
|
| 515 |
-
PyTorch sizes its CPU thread pool to every visible core; on a 128-core node
|
| 516 |
-
that makes batch construction several times slower and CPU reductions
|
| 517 |
-
non-reproducible, so set `OMP_NUM_THREADS`. Single-threaded CPU training is
|
| 518 |
-
bitwise reproducible.
|
| 519 |
-
|
| 520 |
-
## ATLAS Open Data GamGam versus Ho et al. (2026)
|
| 521 |
-
|
| 522 |
-
The main benchmark reproduces the from-scratch baseline of Table 3 (5
|
| 523 |
-
classes, 2.5M events, 80:10:10 split, five seeds). Choices the paper does not
|
| 524 |
-
specify:
|
| 525 |
-
|
| 526 |
-
* electrons and muons are split from `lep_*` by `lep_type` (11, 13);
|
| 527 |
-
* b-tagging is `jet_MV2c10 > 0.8244273`, the 70% working point;
|
| 528 |
-
* momenta and energies are scaled by 1e-4 (MeV to the pretraining unit of
|
| 529 |
-
10 GeV);
|
| 530 |
-
* the fold is `eventNumber % 10`: folds 0-7 train, 8 validates, 9 tests;
|
| 531 |
-
* training is unweighted (23% of ttH events have `mcWeight = -1`).
|
| 532 |
-
|
| 533 |
-
The paper's stopping rule (5 epochs without training-loss improvement) never
|
| 534 |
-
fires within 200 epochs on this task; validation AUC peaks earlier (epoch 67
|
| 535 |
-
for seed 1) and `best.pt` keeps that epoch.
|
| 536 |
-
|
| 537 |
-
| Test split | Accuracy | AUC | F1 | Precision | Recall |
|
| 538 |
-
| --- | ---: | ---: | ---: | ---: | ---: |
|
| 539 |
-
| Paper (5 seeds) | 71.8 ± 0.2 | 91.2 | 63.0 | 62.2 | 66.3 |
|
| 540 |
-
| This package, seed 1 (CUDA graphs + TF32) | 71.1 | 91.3 | 62.6 | 62.4 | 66.7 |
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
docs/end_to_end_validation.md
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# End-to-end legacy/rewrite validation
|
| 2 |
+
|
| 3 |
+
Task 21 compares staged event identity, labels, folds, weights, globals, node
|
| 4 |
+
features, topology, edge features, batching, fixed-weight forward, loss and
|
| 5 |
+
metrics, one optimizer step, short training, checkpoint reload, and inference.
|
| 6 |
+
The first failing stage is retained in JSON and Markdown reports.
|
| 7 |
+
|
| 8 |
+
Legacy and rewrite extraction may use separate environments. Each writes the
|
| 9 |
+
version-1 artifact described in `validation/README.md`; the comparator never
|
| 10 |
+
imports legacy modules or DGL graph objects. Record ROOT file/tree, selection,
|
| 11 |
+
event count, file size, SHA-256, branches, seeds, versions, device and source
|
| 12 |
+
identities in the artifact manifest. Do not use an absolute personal path as a
|
| 13 |
+
scientific event identity.
|
| 14 |
+
|
| 15 |
+
The captured runtime versions and seed policy are recorded in
|
| 16 |
+
`validation/manifests/environments.json`; the HF composite fixture provenance
|
| 17 |
+
is recorded in `validation/manifests/multiclass_fixture.json`.
|
| 18 |
+
|
| 19 |
+
Strict parity applies to preprocessing, topology, fixed forward, loss, and
|
| 20 |
+
one-step CPU updates. Multi-epoch, GPU and DDP comparisons are scientific:
|
| 21 |
+
compare curves, metrics, distributions and event-level correlations. Named
|
| 22 |
+
metadata, modern NPZ names and the version-1 checkpoint schema are expected
|
| 23 |
+
interface differences and must be reported explicitly.
|
| 24 |
+
|
| 25 |
+
Run the existing gates separately:
|
| 26 |
+
|
| 27 |
+
```bash
|
| 28 |
+
GNN4COLLIDERS_REQUIRE_ROOT_GNN=1 uv run pytest
|
| 29 |
+
uv run ruff check .
|
| 30 |
+
```
|
docs/export.md
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# ROOT-GNN ONNX export
|
| 2 |
+
|
| 3 |
+
ONNX export is an inference-only adapter for prepared ROOT-GNN graph batches.
|
| 4 |
+
It does not read ROOT files or move feature construction into the deployment
|
| 5 |
+
model. Install the optional dependencies with:
|
| 6 |
+
|
| 7 |
+
```bash
|
| 8 |
+
uv sync --extra root-gnn --extra onnx
|
| 9 |
+
```
|
| 10 |
+
|
| 11 |
+
Export a checkpoint with the project CLI:
|
| 12 |
+
|
| 13 |
+
```bash
|
| 14 |
+
uv run gnn4colliders export \
|
| 15 |
+
export.checkpoint=/path/to/checkpoint.pt \
|
| 16 |
+
export.output=model.onnx \
|
| 17 |
+
data.cache.path=/path/to/graph-cache.pt
|
| 18 |
+
```
|
| 19 |
+
|
| 20 |
+
The exported model accepts six tensor inputs: `node_features`,
|
| 21 |
+
`edge_features`, `edge_src`, `edge_dst`, `node_batch`, and `global_features`.
|
| 22 |
+
`node_batch` identifies the graph for each node; edge membership is derived
|
| 23 |
+
from `node_batch[edge_src]`. Graph, node, edge, and batch dimensions are
|
| 24 |
+
dynamic. The model returns raw `logits`; task postprocessing and metadata stay
|
| 25 |
+
outside ONNX.
|
| 26 |
+
|
| 27 |
+
The exporter uses ONNX opset 17, validates the structure with `onnx.checker`,
|
| 28 |
+
and writes compact provenance/schema metadata to `model.onnx.json`. It
|
| 29 |
+
supports `EdgeNetwork` multiclass models and `FineTunedEdgeNetwork` binary
|
| 30 |
+
models. Empty graphs are invalid; single-node graphs (zero edges) are
|
| 31 |
+
represented and pooled with a zero edge contribution.
|
| 32 |
+
|
| 33 |
+
Direct DGL export was not retained: the active model uses DGL graph mutation
|
| 34 |
+
and reductions (`apply_edges`, `update_all`, and graph pooling) that are not a
|
| 35 |
+
portable ONNX contract. The adapter expresses those operations with standard
|
| 36 |
+
tensor indexing, `index_add`, and per-graph means.
|
docs/migration.md
ADDED
|
@@ -0,0 +1,295 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
# Incremental migration plan: `root_gnn_dgl`
|
| 2 |
+
|
| 3 |
+
The target is `legacy/root_gnn_dgl/`. `legacy/physicsnemo/` is a prior rewrite
|
| 4 |
+
attempt and may inspire abstractions, but it is not a parity target. Neither
|
| 5 |
+
legacy tree should be modified during migration.
|
| 6 |
+
|
| 7 |
+
Each phase should add focused unit tests, a deterministic fixture in
|
| 8 |
+
`data/fixtures/`, and parity tests under `tests/parity/` before moving upward.
|
| 9 |
+
Record intentional differences and checkpoint consequences here.
|
| 10 |
+
|
| 11 |
+
## Phase 0 — freeze observations and fixtures
|
| 12 |
+
|
| 13 |
+
Capture a small representative ROOT-equivalent fixture containing the seven
|
| 14 |
+
active node features, three edge features, labels, fold values, weights, and
|
| 15 |
+
globals. Record outputs of `node_features_from_tree`, `full_connected_graph`,
|
| 16 |
+
`EdgeDataset.make_graph`, and `fold_selection`
|
| 17 |
+
([`dataset.py:15-59`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
|
| 18 |
+
[`dataset.py:471-482`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
|
| 19 |
+
[`utils.py:121-143`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)). Preserve
|
| 20 |
+
one `.bin`, one `model_epoch_N.pt`, one evaluation `.npz`, and one inference
|
| 21 |
+
`.npz` fixture if available.
|
| 22 |
+
|
| 23 |
+
Task 3 characterization records the active-path observations. Node rows are
|
| 24 |
+
concatenated by object type in the configured order (jets, electrons, muons,
|
| 25 |
+
photons, MET), and the seven columns are `[pt, eta, phi, energy, btag, charge,
|
| 26 |
+
node_type]`. `CALC_E` is `pt*cosh(eta)` before the configured column scale is
|
| 27 |
+
applied. The graph is directed and uses all ordered pairs except self-loops
|
| 28 |
+
for graphs with more than one node; edge order is source-major. A one-node
|
| 29 |
+
graph is a special case: the no-self-loop branch retains its sole self-loop.
|
| 30 |
+
Edge columns are `[deta, dphi, dR]`, with `dphi` wrapped into `[-pi, pi]`.
|
| 31 |
+
Dataset items expose `(graph, label, tracking, global_features)`; tracking
|
| 32 |
+
column 0 is the fold identifier and column 1 is the event weight. These are
|
| 33 |
+
compatibility observations, not proposed fixes.
|
| 34 |
+
|
| 35 |
+
## Phase 1 — configuration boundary
|
| 36 |
+
|
| 37 |
+
Implement a typed configuration layer that reads `Training`, `Model`,
|
| 38 |
+
optional `Loss`, and `Datasets`. Initially retain a compatibility adapter for
|
| 39 |
+
`module`/`class`/`args` and runtime injection of `sample_graph` and
|
| 40 |
+
`sample_global`, matching `buildFromConfig`
|
| 41 |
+
([`utils.py:10-43`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)). Keep
|
| 42 |
+
dynamic imports isolated at this boundary rather than spreading reflection
|
| 43 |
+
through new code.
|
| 44 |
+
|
| 45 |
+
## Phase 2 — pure preprocessing parity
|
| 46 |
+
|
| 47 |
+
Port and test, in isolation:
|
| 48 |
+
|
| 49 |
+
- branch-to-node conversion, `CALC_E`, `NODE_TYPE`, constants, scaling, empty
|
| 50 |
+
objects, and dtypes (`node_features_from_tree`,
|
| 51 |
+
[`dataset.py:15-50`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py));
|
| 52 |
+
- string/tuple selections and cutflow (`check_selection`, `selection_mask`,
|
| 53 |
+
`compute_cutflow`, [`dataset.py:75-158`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py));
|
| 54 |
+
- fold masks and cache suffixes (`fold_selection`, `fold_selection_name`,
|
| 55 |
+
[`utils.py:121-143`](../legacy/root_gnn_dgl/root_gnn_base/utils.py));
|
| 56 |
+
- deterministic chunk partitioning (`hash_partition`,
|
| 57 |
+
[`batched_dataset.py:27-31`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
|
| 58 |
+
|
| 59 |
+
This is the highest-value parity layer: model parity is invalid if graph inputs
|
| 60 |
+
differ.
|
| 61 |
+
|
| 62 |
+
Task 4 implements the shared branch-to-node feature builder under
|
| 63 |
+
`gnn4colliders.features`. It preserves the active seven-column schema,
|
| 64 |
+
object-type ordering, explicit scales, derived `CALC_E`, node-type codes,
|
| 65 |
+
float32 output, and supported empty vector collections. Selection, fold, and
|
| 66 |
+
chunk helpers remain deferred to later data-infrastructure work.
|
| 67 |
+
|
| 68 |
+
Task 6 implements the shared ROOT/Awkward ingestion boundary under
|
| 69 |
+
`gnn4colliders.data`. `RootEventDataset` returns immutable, architecture-neutral
|
| 70 |
+
`EventSample` values with selected branch data, labels, tracking, and globals;
|
| 71 |
+
events are ordered by input file order with a global zero-based index. Fold
|
| 72 |
+
filtering, caching, batching, and model-specific conversion remain deferred.
|
| 73 |
+
|
| 74 |
+
## Phase 3 — graph construction and cache format
|
| 75 |
+
|
| 76 |
+
Implement graph construction with tests for node/edge counts, directed edge
|
| 77 |
+
ordering, self-loop policy, `[deta, dphi, dR]` order, metadata, and empty graphs.
|
| 78 |
+
Preserve the dataset item contract `(graph, label, tracking, global_features)`
|
| 79 |
+
from `RootDataset.__getitem__`
|
| 80 |
+
([`dataset.py:465-469`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)).
|
| 81 |
+
|
| 82 |
+
Then implement DGL `.bin` serialization, lazy chunk loading, pre-batching, and
|
| 83 |
+
padding. Compare against `RootDataset.save/load`, `LazyDataset`, and
|
| 84 |
+
`PreBatchedDataset` ([`dataset.py:396-469`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
|
| 85 |
+
[`batched_dataset.py:129-174`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
|
| 86 |
+
Treat `NONE`, `STEPS`, `FIXED`, and `NODE` as explicit features; do not hide
|
| 87 |
+
the hardcoded fixed padding sizes.
|
| 88 |
+
|
| 89 |
+
Task 7 establishes the metadata-aware orchestration boundary around this
|
| 90 |
+
phase: `EventMetadata`, `GraphSample`, `GraphBatch`, fold-based split
|
| 91 |
+
selection, deterministic batching, and a version-checked graph-sample cache.
|
| 92 |
+
The cache is deliberately Level 2; normalized event caching remains a future
|
| 93 |
+
extension so non-graph model families can reuse ROOT preprocessing.
|
| 94 |
+
|
| 95 |
+
## Phase 4 — active model parity
|
| 96 |
+
|
| 97 |
+
Task 8 adds the active `EdgeNetwork` and `FineTunedEdgeNetwork` under
|
| 98 |
+
`gnn4colliders.models.root_gnn`. The update order and MLP ordering follow the
|
| 99 |
+
legacy active path. The rewrite uses an explicit backbone/classifier boundary,
|
| 100 |
+
local DGL graph scope, and does not mutate global RNG state in constructors.
|
| 101 |
+
Model parity now covers fixed-weight pretraining and transfer paths, including
|
| 102 |
+
historical checkpoint prefixes. The legacy transfer implementation has an
|
| 103 |
+
active bug when nonempty globals are supplied (`Pretrained_Output` ignores its
|
| 104 |
+
argument); parity therefore characterizes its supported no-global path, while
|
| 105 |
+
the rewritten model supports both global and fallback modes.
|
| 106 |
+
|
| 107 |
+
Task 22 adds `TensorEdgeNetwork`, a native PyTorch realization of the same
|
| 108 |
+
message-passing equations. It receives explicit node/edge index tensors from
|
| 109 |
+
the batching boundary, retains parameter names for legacy checkpoint loading,
|
| 110 |
+
and has fixed-weight CPU parity coverage against the DGL backend. It is the
|
| 111 |
+
default training backend for CPU, CUDA, and Apple MPS; DGL remains available as
|
| 112 |
+
a cache and compatibility adapter during the transition.
|
| 113 |
+
|
| 114 |
+
Port `models.GCN.Edge_Network` first. Preserve constructor parameters,
|
| 115 |
+
`forward(graph, global_feats)`, feature keys, processor order, MLP LayerNorm
|
| 116 |
+
placement, and logits shape. Compare intermediate and final tensors on fixed
|
| 117 |
+
graphs using the legacy architecture
|
| 118 |
+
([`GCN.py:18-35`](../legacy/root_gnn_dgl/models/GCN.py),
|
| 119 |
+
[`GCN.py:182-251`](../legacy/root_gnn_dgl/models/GCN.py)).
|
| 120 |
+
|
| 121 |
+
Next port `Transferred_Learning_Finetuning`, including pretrained
|
| 122 |
+
`model_state_dict` loading, removal of the final classifier, and new classifier
|
| 123 |
+
initialization ([`GCN.py:884-997`](../legacy/root_gnn_dgl/models/GCN.py)). Test
|
| 124 |
+
both frozen and unfrozen modes. Defer other model classes until an active
|
| 125 |
+
config or consumer proves they are needed.
|
| 126 |
+
|
| 127 |
+
## Phase 5 — objectives and metrics
|
| 128 |
+
|
| 129 |
+
Implement the default objective exactly: elementwise configured loss,
|
| 130 |
+
tracking-column weights, per-unique-label normalization, and averaging across
|
| 131 |
+
labels ([`training_script.py:320-359`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 132 |
+
Add parity cases for positive, zero, and negative weights and binary versus
|
| 133 |
+
multiclass shapes.
|
| 134 |
+
|
| 135 |
+
Port metric behavior from
|
| 136 |
+
[`training_script.py:438-510`](../legacy/root_gnn_dgl/scripts/training_script.py):
|
| 137 |
+
sigmoid threshold 0.5, argmax, weight masking, weighted ROC AUC, one-vs-rest
|
| 138 |
+
multiclass AUC, and NaN behavior when AUC is undefined. Add `models/loss.py`
|
| 139 |
+
classes only with dedicated tests; do not substitute their reductions.
|
| 140 |
+
|
| 141 |
+
## Phase 6 — checkpoint and lifecycle
|
| 142 |
+
|
| 143 |
+
Task 10 implemented the in-memory single-process training lifecycle before the
|
| 144 |
+
checkpoint portion of this phase: `Trainer`, explicit optimizer/scheduler
|
| 145 |
+
builders, `EarlyStopping`, reproducibility seeding, `GraphBatch.to`, and
|
| 146 |
+
epoch/history result types. Checkpoint persistence/resume and the Python
|
| 147 |
+
inference/evaluation and named NPZ/ROOT output layers are now implemented.
|
| 148 |
+
Distributed execution and CLI wiring were completed in the later phases.
|
| 149 |
+
|
| 150 |
+
Task 10 also establishes corrected split semantics: validation is evaluated
|
| 151 |
+
every epoch and is the only split used for model selection or early stopping;
|
| 152 |
+
the test split remains held out and is evaluated separately after fitting. The
|
| 153 |
+
legacy loader naming inversion (`test` used for selection and `val` held out)
|
| 154 |
+
is not carried into the rewrite.
|
| 155 |
+
|
| 156 |
+
Create a checkpoint adapter preserving `model_epoch_<epoch>.pt` and keys
|
| 157 |
+
`epoch`, `model_state_dict`, `optimizer_state_dict`, and `early_stop`
|
| 158 |
+
([`training_script.py:565-604`](../legacy/root_gnn_dgl/scripts/training_script.py)).
|
| 159 |
+
Support legacy DDP/compiled prefixes (`module.` and `_orig_mod.`) as exercised
|
| 160 |
+
by checkpoint lookup and inference
|
| 161 |
+
([`utils.py:145-248`](../legacy/root_gnn_dgl/root_gnn_base/utils.py),
|
| 162 |
+
[`inference.py:274-290`](../legacy/root_gnn_dgl/scripts/inference.py)). Port
|
| 163 |
+
`EarlyStop` state and log parsing separately
|
| 164 |
+
([`utils.py:325-390`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)). Verify
|
| 165 |
+
resume, restart, early termination, and `.npz` fields before distributed work.
|
| 166 |
+
|
| 167 |
+
## Phase 7 — CLI, inference, and export
|
| 168 |
+
|
| 169 |
+
Task 12 implemented ordered prediction/evaluation, task-owned score
|
| 170 |
+
semantics, checkpoint weight-only loading, named metadata retention, NPZ
|
| 171 |
+
output, and explicit ROOT entry alignment. The semantic CLI and the validated
|
| 172 |
+
ROOT-GNN ONNX export adapter are implemented.
|
| 173 |
+
|
| 174 |
+
Task 13 adds Hydra composition and a single-process CLI around those existing
|
| 175 |
+
APIs. The current application data boundary is a versioned
|
| 176 |
+
`GraphSampleCache`; ROOT preparation converts events through the shared
|
| 177 |
+
feature and graph builders before writing that cache.
|
| 178 |
+
|
| 179 |
+
Build thin new applications around tested library interfaces in this order:
|
| 180 |
+
|
| 181 |
+
1. preprocessing/cache generation (`scripts/prep_data.py`);
|
| 182 |
+
2. training/evaluation (`scripts/training_script.py`);
|
| 183 |
+
3. inference to `.npz` and ROOT (`scripts/inference.py`);
|
| 184 |
+
4. ONNX export after PyTorch parity (`gnn4colliders export`).
|
| 185 |
+
|
| 186 |
+
Use subprocess integration tests with tiny fixtures. Preserve CLI options only
|
| 187 |
+
where they serve an active workflow; document removed diagnostic/cluster-only
|
| 188 |
+
options.
|
| 189 |
+
|
| 190 |
+
## Phase 8 — reproducibility and deployment
|
| 191 |
+
|
| 192 |
+
Task 14 adds the initial deployment boundary: CPU/GPU DDP through standard
|
| 193 |
+
`torchrun` variables, rank-local graph-sample sharding, global metric/output
|
| 194 |
+
gathering, rank-0 checkpoint/config writing, and Perlmutter-oriented Slurm
|
| 195 |
+
examples. Evaluation deliberately avoids sampler padding duplicates. The
|
| 196 |
+
remaining follow-up is a streaming or sharded output path for very large
|
| 197 |
+
distributed inference jobs.
|
| 198 |
+
|
| 199 |
+
The seed policy remains explicit: the configured seed is offset by rank for
|
| 200 |
+
process-local randomness, while distributed sample assignment is derived from
|
| 201 |
+
the configured seed, world size, and epoch. GPU kernel nondeterminism and
|
| 202 |
+
exact per-rank RNG checkpoint replay remain environment-dependent. Slurm/NCCL,
|
| 203 |
+
Podman-HPC, ROOT, and Hugging Face integrations stay in launcher/adapters
|
| 204 |
+
rather than package code.
|
| 205 |
+
|
| 206 |
+
## Checkpoint compatibility checklist
|
| 207 |
+
|
| 208 |
+
- [x] Load a checked-in or generated multiclass pretrained checkpoint.
|
| 209 |
+
- [x] Load a legacy fine-tuning checkpoint after prefix normalization.
|
| 210 |
+
- [x] Resume optimizer and early-stop state.
|
| 211 |
+
- [x] Produce equivalent logits on a deterministic graph fixture.
|
| 212 |
+
- [x] Produce equivalent `.npz` score, label, and metadata fields.
|
| 213 |
+
- [x] Preserve ROOT scalar/vector score branch conventions in the Python adapter.
|
| 214 |
+
|
| 215 |
+
Known risks are documented in [`architecture.md`](architecture.md): edge order,
|
| 216 |
+
self-loops, weight semantics, validation/test naming, padding, dynamic
|
| 217 |
+
selection evaluation, reproducibility, and the experimental model/loss surface.
|
| 218 |
+
|
| 219 |
+
## Migration closure status
|
| 220 |
+
|
| 221 |
+
### Task 18 compatibility closure
|
| 222 |
+
|
| 223 |
+
The compatibility boundary is now explicit in `gnn4colliders.compat`.
|
| 224 |
+
Production ingestion stores named `EventMetadata`; legacy two-column tracking
|
| 225 |
+
is converted only at the compatibility boundary. Checkpoint prefix cleanup and
|
| 226 |
+
the historical ROOT-GNN `classify` to `classifier` mapping have one canonical
|
| 227 |
+
implementation. The new checkpoint schema and named NPZ output remain
|
| 228 |
+
canonical. See [`compatibility.md`](compatibility.md) for the supported and
|
| 229 |
+
intentionally unsupported historical artifacts.
|
| 230 |
+
|
| 231 |
+
The following matrix describes the supported new stack, rather than every
|
| 232 |
+
class that exists in `legacy/`:
|
| 233 |
+
|
| 234 |
+
| Legacy area | New-stack status | Notes |
|
| 235 |
+
| --- | --- | --- |
|
| 236 |
+
| ROOT/Awkward ingestion | migrated | `RootEventDataset` returns `EventSample` in file/event order |
|
| 237 |
+
| node features | migrated + parity-tested | seven-column schema, `CALC_E`, ordering, scales, float32 |
|
| 238 |
+
| edge construction | migrated + parity-tested | directed source-major topology and `[deta,dphi,dR]` |
|
| 239 |
+
| graph cache | migrated | versioned `GraphSampleCache`; graph-level cache only |
|
| 240 |
+
| folds and weights | migrated | named `EventMetadata.fold` and `.weight` |
|
| 241 |
+
| batching | migrated | deterministic local loader and DDP sharding |
|
| 242 |
+
| legacy padding modes | deferred | no active new-stack consumer |
|
| 243 |
+
| `Edge_Network` | migrated + parity-tested | `EdgeNetwork`, raw logits |
|
| 244 |
+
| transfer/fine-tuning | migrated + parity-tested | frozen or trainable backbone |
|
| 245 |
+
| loss and metrics | migrated + parity-tested | task-owned weighted reductions and full-split AUC |
|
| 246 |
+
| training lifecycle | migrated | `Trainer`, validation semantics, scheduler, early stopping |
|
| 247 |
+
| checkpoints/resume | migrated | schema v1; historical weight/prefix adapter |
|
| 248 |
+
| inference/NPZ | migrated | named output fields and ordered accumulation |
|
| 249 |
+
| ROOT score output | compatibility adapter | Python API supported; CLI currently NPZ-only |
|
| 250 |
+
| DDP | migrated | torchrun boundary, rank-0 artifacts, gathered metrics |
|
| 251 |
+
| Slurm/Perlmutter | launcher examples | site policy remains outside package code |
|
| 252 |
+
| ONNX export | migrated for ROOT-GNN | tensor-only adapter, ONNX Runtime validation, and `export` CLI; raw graph tensors are the input contract |
|
| 253 |
+
|
| 254 |
+
### Intentional redesigns
|
| 255 |
+
|
| 256 |
+
These are deliberate new-stack contracts, not accidental parity failures:
|
| 257 |
+
|
| 258 |
+
* `tracking[:, 0]` and `tracking[:, 1]` become named `metadata.fold` and
|
| 259 |
+
`metadata.weight`; public consumers do not depend on positional columns.
|
| 260 |
+
* Dynamic legacy YAML `module`/`class` construction becomes allow-listed
|
| 261 |
+
semantic Hydra configuration.
|
| 262 |
+
* The monolithic training script becomes `Task` + `Trainer` + checkpoint and
|
| 263 |
+
inference adapters.
|
| 264 |
+
* Graph state is scoped to the forward pass rather than relying on persistent
|
| 265 |
+
mutation of shared graph state.
|
| 266 |
+
* Model constructors do not mutate global RNG state; seeding is explicit in
|
| 267 |
+
the training/application boundary.
|
| 268 |
+
* Validation is the selection/early-stopping split and test is held out. This
|
| 269 |
+
corrects the legacy loader-name inversion.
|
| 270 |
+
|
| 271 |
+
Compatibility preserves externally observable scientific behavior where it is
|
| 272 |
+
validated; it does not promise to preserve every legacy implementation bug.
|
| 273 |
+
The characterized legacy transfer path had a nonempty-global handling bug;
|
| 274 |
+
the rewrite supports named globals. Negative weights, rare empty graphs,
|
| 275 |
+
historical checkpoint variants, and legacy padding edge cases remain areas to
|
| 276 |
+
audit when a supported consumer requires them.
|
| 277 |
+
|
| 278 |
+
## ROOT-GNN v1 completion checklist
|
| 279 |
+
|
| 280 |
+
- [x] active ROOT data path and graph cache
|
| 281 |
+
- [x] validated feature, graph, model, task, and metric behavior
|
| 282 |
+
- [x] train from scratch and fine-tune a pretrained backbone
|
| 283 |
+
- [x] resume new-stack checkpoints and load supported historical weights
|
| 284 |
+
- [x] evaluate and predict named outputs
|
| 285 |
+
- [x] single-process and DDP application boundaries
|
| 286 |
+
- [x] Perlmutter/Slurm launcher examples and profiling guidance
|
| 287 |
+
- [x] ROOT-GNN ONNX export and CPU Runtime parity
|
| 288 |
+
- [ ] streaming/sharded large-scale prediction output
|
| 289 |
+
- [ ] removal of frozen legacy reference
|
| 290 |
+
- [ ] ROOT-Transformer representation/model
|
| 291 |
+
|
| 292 |
+
ROOT-GNN v1 is complete when the checked-in new stack can prepare active data,
|
| 293 |
+
reproduce validated legacy behavior, train, transfer, resume, evaluate,
|
| 294 |
+
predict, and run single-process or DDP workflows. The remaining unchecked
|
| 295 |
+
items are intentionally deferred rather than undocumented promises.
|