ho22joshua commited on
Commit
de46a3c
·
1 Parent(s): d93a2db

Remove historical implementation from active tree

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. AGENTS.md +6 -7
  2. README_PROJECT.md +6 -4
  3. docs/agent-workflows/gnn4colliders-workflow.md +3 -2
  4. docs/architecture.md +68 -360
  5. docs/compatibility.md +3 -2
  6. docs/end_to_end_validation.md +1 -1
  7. docs/migration.md +35 -298
  8. legacy/LICENSE +0 -21
  9. legacy/README.md +0 -358
  10. legacy/physicsnemo/configs/config.yaml +0 -64
  11. legacy/physicsnemo/configs/config_stats_all.yaml +0 -65
  12. legacy/physicsnemo/configs/tHjb_CP_0_vs_45.yaml +0 -79
  13. legacy/physicsnemo/configs/tHjb_CP_0_vs_90.yaml +0 -87
  14. legacy/physicsnemo/configs/tHjb_CP_0_vs_90_edge_network.yaml +0 -82
  15. legacy/physicsnemo/configs/tHjb_CP_0_vs_90_globals.yaml +0 -84
  16. legacy/physicsnemo/dataset/Dataset.py +0 -243
  17. legacy/physicsnemo/dataset/GraphBuilder.py +0 -162
  18. legacy/physicsnemo/dataset/Graphs.py +0 -88
  19. legacy/physicsnemo/dataset/Normalization.py +0 -144
  20. legacy/physicsnemo/metrics.py +0 -110
  21. legacy/physicsnemo/models/Edge_Network.py +0 -72
  22. legacy/physicsnemo/models/MeshGraphNet.py +0 -51
  23. legacy/physicsnemo/models/utils.py +0 -135
  24. legacy/physicsnemo/setup/Dockerfile +0 -23
  25. legacy/physicsnemo/setup/build_image.sh +0 -4
  26. legacy/physicsnemo/train.py +0 -246
  27. legacy/physicsnemo/utils.py +0 -11
  28. legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-data-preparation/SKILL.md +0 -202
  29. legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-env-setup/SKILL.md +0 -63
  30. legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-inference/SKILL.md +0 -133
  31. legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-plotting/SKILL.md +0 -80
  32. legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-training/SKILL.md +0 -156
  33. legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-workflow/SKILL.md +0 -68
  34. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/config.yaml +0 -319
  35. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_0.pt +0 -3
  36. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_1.pt +0 -3
  37. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_10.pt +0 -3
  38. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_11.pt +0 -3
  39. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_12.pt +0 -3
  40. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_13.pt +0 -3
  41. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_14.pt +0 -3
  42. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_15.pt +0 -3
  43. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_16.pt +0 -3
  44. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_17.pt +0 -3
  45. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_18.pt +0 -3
  46. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_19.pt +0 -3
  47. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_2.pt +0 -3
  48. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_20.pt +0 -3
  49. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_21.pt +0 -3
  50. legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_22.pt +0 -3
AGENTS.md CHANGED
@@ -31,13 +31,12 @@ docs/migration.md
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
 
@@ -195,7 +194,7 @@ 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
 
31
 
32
  and the relevant existing source and tests.
33
 
34
+ The `root-gnn-parity-baseline` tag and committed reference fixtures preserve
35
+ the behavioral baseline for the rewrite. Historical implementation code is
36
+ not part of the active source tree.
37
 
38
+ Do not reintroduce historical implementation code or imports into the active
39
+ package. Preserve compatibility through explicit fixtures and adapters.
 
 
 
40
 
41
  When legacy behavior and documentation disagree, identify the discrepancy rather than silently choosing one.
42
 
 
194
 
195
  For substantial migrations:
196
 
197
+ 1. inspect the relevant reference fixture and compatibility contract
198
  2. identify externally observable behavior
199
  3. inspect existing characterization/parity tests
200
  4. state or infer the intended new interface
README_PROJECT.md CHANGED
@@ -14,8 +14,10 @@ ROOT files -> EventSample -> shared collider features
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
 
@@ -31,13 +33,13 @@ 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:
 
14
  ```
15
 
16
  The new implementation lives under [`src/gnn4colliders`](src/gnn4colliders/).
17
+ Historical behavior is preserved by the
18
+ [`root-gnn-parity-baseline`](https://huggingface.co/HWresearch/GNN4Colliders/tree/root-gnn-parity-baseline)
19
+ tag and committed reference fixtures, not by a supported historical runtime
20
+ backend.
21
 
22
  ## Installation
23
 
 
33
  ```
34
 
35
  The core package can be installed without DGL when only shared data or task
36
+ code is needed. ROOT-GNN models, graph construction, and ROOT-GNN reference tests
37
  require the `root-gnn` extra. On Linux x86_64, it uses the validated CUDA 12.1
38
  wheels configured in `pyproject.toml`; a compatible NVIDIA driver is still
39
  required. On Apple Silicon macOS, it installs the CPU DGL wheel, supporting
40
  local graph/cache development. The default ROOT-GNN backend performs training
41
  with native PyTorch graph tensors, so it runs on Apple MPS, NVIDIA CUDA, and
42
+ CPU; DGL remains a cache and graph compatibility adapter. Do not add
43
  site-specific CUDA, Slurm, or filesystem paths to model or task configuration.
44
 
45
  Use the MPS profile on an Apple Silicon Mac:
docs/agent-workflows/gnn4colliders-workflow.md CHANGED
@@ -15,8 +15,9 @@ docs/migration.md
15
 
16
  Production Python belongs under `src/gnn4colliders/`. Keep collider physics
17
  features in `features/`, graph topology in `graphs/`, architecture code in
18
- `models/`, and lifecycle code in `training/`. Do not modify `legacy/`; use it
19
- as a behavioral reference for parity work.
 
20
 
21
  ## Environment
22
 
 
15
 
16
  Production Python belongs under `src/gnn4colliders/`. Keep collider physics
17
  features in `features/`, graph topology in `graphs/`, architecture code in
18
+ `models/`, and lifecycle code in `training/`. Use the frozen parity fixtures
19
+ and compatibility adapters for historical behavior; do not add historical
20
+ implementation imports.
21
 
22
  ## Environment
23
 
docs/architecture.md CHANGED
@@ -1,378 +1,86 @@
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.
 
1
  # GNN4Colliders architecture
2
 
3
+ ## Current architecture
4
 
5
+ GNN4Colliders is organized around an architecture-neutral event boundary:
6
 
7
  ```text
8
  ROOT/Awkward
9
 
10
  EventSample + EventMetadata
11
 
12
+ shared features and representation adapters
13
+ ├── GraphSample -> GraphSampleCache -> GraphBatch
14
+ └── future SequenceSample -> transformer/token models
15
+
16
+ model family
17
+
18
+ Task -> Trainer/Predictor -> named outputs
 
 
 
 
 
19
  ```
20
 
21
  | Layer | Responsibility |
22
+ |---|---|
23
+ | `data` | ROOT/Awkward ingestion, metadata, datasets, folds, batching, and caches |
24
+ | `features` | Collider-object features and derived physics quantities |
25
+ | `graphs` | Topology, edge construction, and graph-specific adapters |
26
+ | `models` | Architecture-specific neural networks |
27
+ | `tasks` | Labels, weights, losses, scores, predictions, and metrics |
28
+ | `training` | Optimizers, lifecycle, reproducibility, checkpointing, and distributed utilities |
29
+ | `inference` | Ordered prediction, evaluation, and named output writing |
30
+ | `cli` / `config` | Thin semantic entry points and configuration composition |
31
+
32
+ Production code must depend on these package boundaries rather than on
33
+ historical implementation paths. The canonical ROOT-GNN implementation is
34
+ `gnn4colliders.models.root_gnn.EdgeNetwork` with
35
+ `FineTunedEdgeNetwork` as its transfer boundary. New architecture families
36
+ must reuse shared data, feature, task, training, and inference interfaces where
37
+ their representation permits it.
38
+
39
+ ## Data and representation boundaries
40
+
41
+ `EventSample` and named `EventMetadata(fold, weight, sample_id, extra)` are
42
+ representation-independent. `GraphSample` and `GraphBatch` are the current
43
+ graph representation boundary. `TensorGraph` provides a native tensor path
44
+ for ROOT-GNN execution on CPU, CUDA, and MPS; DGL remains an optional graph
45
+ adapter and cache dependency.
46
+
47
+ The active collider node schema has seven columns:
48
+ `[pt, eta, phi, energy, btag, charge, node_type]`. Graph edges are directed,
49
+ source-major, fully connected without self-loops except for the one-node
50
+ case, and carry `[deta, dphi, dR]` features. These are compatibility contracts
51
+ captured by deterministic tests and the frozen `root-gnn-parity-baseline`
52
+ reference fixture.
53
+
54
+ ## Configuration and lifecycle
55
+
56
+ Experiments use semantic configuration such as `model.type: root_gnn`; model
57
+ module paths are not part of the new public configuration contract. The CLI
58
+ delegates to tested application factories and does not contain model or data
59
+ processing logic.
60
+
61
+ Checkpoints carry model/task metadata, lifecycle state, schema versions, and
62
+ optional RNG state. The compatibility package accepts historical checkpoint
63
+ prefixes and classifier names as a one-way input adapter. No executable
64
+ historical model code is required at runtime.
65
+
66
+ ## Extending the model families
67
+
68
+ The next model family should introduce only its representation-specific
69
+ boundary and model implementation, for example:
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
70
 
71
  ```text
72
+ data.EventSample -> features -> SequenceSample -> models.root_transformer
 
 
 
 
 
 
73
  ```
74
 
75
+ It should include a deterministic fixture, unit tests for the representation,
76
+ an integration path through the shared task/trainer interfaces, and explicit
77
+ checkpoint/inference behavior. Shared infrastructure should be generalized
78
+ only when the second model demonstrates a real common use case.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
79
 
80
+ ## Frozen baseline
81
 
82
+ The complete ROOT-GNN parity campaign is recorded by the
83
+ `root-gnn-parity-baseline` tag. The historical implementation is no longer
84
+ part of the active source tree. Reference data and compatibility adapters are
85
+ kept so existing checkpoints and scientific observations remain usable while
86
+ development moves to new architectures.
 
docs/compatibility.md CHANGED
@@ -18,5 +18,6 @@ adapted into the new representation and are never rewritten implicitly.
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.
 
 
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 parity fixtures and `root-gnn-parity-baseline` tag preserve the
22
+ historical observations for parity tests and investigation. Production modules
23
+ do not import executable historical model code.
docs/end_to_end_validation.md CHANGED
@@ -1,4 +1,4 @@
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
 
1
+ # End-to-end reference 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
docs/migration.md CHANGED
@@ -1,312 +1,49 @@
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
  ## Frozen ROOT-GNN baseline
12
 
13
- The active ROOT-GNN rewrite and the legacy implementation are frozen at the
14
- `root-gnn-parity-baseline` tag. The legacy tree is now a read-only behavioral
15
- reference; new model development must not add production dependencies on it.
16
- The completed no-selection ttH CP-even versus CP-odd campaign covered full
17
- event preprocessing and graph parity, binary losses and metrics, one-step and
18
- multi-epoch fine-tuning, full-split training, checkpoint reload and resume,
19
- reproducibility, and chunked legacy `.bin` serialization.
20
-
21
- The next migration boundary is to replace live legacy imports in validation
22
- workflows with frozen reference fixtures. Until that boundary is complete,
23
- `legacy/` remains in the repository so the strict parity gate and historical
24
- checkpoint investigations remain reproducible. New architecture families may
25
- reuse shared data, features, tasks, training, and inference interfaces without
26
- depending on ROOT-GNN or the legacy tree.
27
-
28
- ## Phase 0 — freeze observations and fixtures
29
-
30
- Capture a small representative ROOT-equivalent fixture containing the seven
31
- active node features, three edge features, labels, fold values, weights, and
32
- globals. Record outputs of `node_features_from_tree`, `full_connected_graph`,
33
- `EdgeDataset.make_graph`, and `fold_selection`
34
- ([`dataset.py:15-59`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
35
- [`dataset.py:471-482`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
36
- [`utils.py:121-143`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)). Preserve
37
- one `.bin`, one `model_epoch_N.pt`, one evaluation `.npz`, and one inference
38
- `.npz` fixture if available.
39
-
40
- Task 3 characterization records the active-path observations. Node rows are
41
- concatenated by object type in the configured order (jets, electrons, muons,
42
- photons, MET), and the seven columns are `[pt, eta, phi, energy, btag, charge,
43
- node_type]`. `CALC_E` is `pt*cosh(eta)` before the configured column scale is
44
- applied. The graph is directed and uses all ordered pairs except self-loops
45
- for graphs with more than one node; edge order is source-major. A one-node
46
- graph is a special case: the no-self-loop branch retains its sole self-loop.
47
- Edge columns are `[deta, dphi, dR]`, with `dphi` wrapped into `[-pi, pi]`.
48
- Dataset items expose `(graph, label, tracking, global_features)`; tracking
49
- column 0 is the fold identifier and column 1 is the event weight. These are
50
- compatibility observations, not proposed fixes.
51
-
52
- ## Phase 1 — configuration boundary
53
-
54
- Implement a typed configuration layer that reads `Training`, `Model`,
55
- optional `Loss`, and `Datasets`. Initially retain a compatibility adapter for
56
- `module`/`class`/`args` and runtime injection of `sample_graph` and
57
- `sample_global`, matching `buildFromConfig`
58
- ([`utils.py:10-43`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)). Keep
59
- dynamic imports isolated at this boundary rather than spreading reflection
60
- through new code.
61
-
62
- ## Phase 2 — pure preprocessing parity
63
-
64
- Port and test, in isolation:
65
-
66
- - branch-to-node conversion, `CALC_E`, `NODE_TYPE`, constants, scaling, empty
67
- objects, and dtypes (`node_features_from_tree`,
68
- [`dataset.py:15-50`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py));
69
- - string/tuple selections and cutflow (`check_selection`, `selection_mask`,
70
- `compute_cutflow`, [`dataset.py:75-158`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py));
71
- - fold masks and cache suffixes (`fold_selection`, `fold_selection_name`,
72
- [`utils.py:121-143`](../legacy/root_gnn_dgl/root_gnn_base/utils.py));
73
- - deterministic chunk partitioning (`hash_partition`,
74
- [`batched_dataset.py:27-31`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
75
-
76
- This is the highest-value parity layer: model parity is invalid if graph inputs
77
- differ.
78
-
79
- Task 4 implements the shared branch-to-node feature builder under
80
- `gnn4colliders.features`. It preserves the active seven-column schema,
81
- object-type ordering, explicit scales, derived `CALC_E`, node-type codes,
82
- float32 output, and supported empty vector collections. Selection, fold, and
83
- chunk helpers remain deferred to later data-infrastructure work.
84
-
85
- Task 6 implements the shared ROOT/Awkward ingestion boundary under
86
- `gnn4colliders.data`. `RootEventDataset` returns immutable, architecture-neutral
87
- `EventSample` values with selected branch data, labels, tracking, and globals;
88
- events are ordered by input file order with a global zero-based index. Fold
89
- filtering, caching, batching, and model-specific conversion remain deferred.
90
-
91
- ## Phase 3 — graph construction and cache format
92
-
93
- Implement graph construction with tests for node/edge counts, directed edge
94
- ordering, self-loop policy, `[deta, dphi, dR]` order, metadata, and empty graphs.
95
- Preserve the dataset item contract `(graph, label, tracking, global_features)`
96
- from `RootDataset.__getitem__`
97
- ([`dataset.py:465-469`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py)).
98
-
99
- Then implement DGL `.bin` serialization, lazy chunk loading, pre-batching, and
100
- padding. Compare against `RootDataset.save/load`, `LazyDataset`, and
101
- `PreBatchedDataset` ([`dataset.py:396-469`](../legacy/root_gnn_dgl/root_gnn_base/dataset.py),
102
- [`batched_dataset.py:129-174`](../legacy/root_gnn_dgl/root_gnn_base/batched_dataset.py)).
103
- Treat `NONE`, `STEPS`, `FIXED`, and `NODE` as explicit features; do not hide
104
- the hardcoded fixed padding sizes.
105
-
106
- Task 7 establishes the metadata-aware orchestration boundary around this
107
- phase: `EventMetadata`, `GraphSample`, `GraphBatch`, fold-based split
108
- selection, deterministic batching, and a version-checked graph-sample cache.
109
- The cache is deliberately Level 2; normalized event caching remains a future
110
- extension so non-graph model families can reuse ROOT preprocessing.
111
-
112
- ## Phase 4 — active model parity
113
-
114
- Task 8 adds the active `EdgeNetwork` and `FineTunedEdgeNetwork` under
115
- `gnn4colliders.models.root_gnn`. The update order and MLP ordering follow the
116
- legacy active path. The rewrite uses an explicit backbone/classifier boundary,
117
- local DGL graph scope, and does not mutate global RNG state in constructors.
118
- Model parity now covers fixed-weight pretraining and transfer paths, including
119
- historical checkpoint prefixes. The legacy transfer implementation has an
120
- active bug when nonempty globals are supplied (`Pretrained_Output` ignores its
121
- argument); parity therefore characterizes its supported no-global path, while
122
- the rewritten model supports both global and fallback modes.
123
-
124
- Task 22 adds `TensorEdgeNetwork`, a native PyTorch realization of the same
125
- message-passing equations. It receives explicit node/edge index tensors from
126
- the batching boundary, retains parameter names for legacy checkpoint loading,
127
- and has fixed-weight CPU parity coverage against the DGL backend. It is the
128
- default training backend for CPU, CUDA, and Apple MPS; DGL remains available as
129
- a cache and compatibility adapter during the transition.
130
-
131
- Port `models.GCN.Edge_Network` first. Preserve constructor parameters,
132
- `forward(graph, global_feats)`, feature keys, processor order, MLP LayerNorm
133
- placement, and logits shape. Compare intermediate and final tensors on fixed
134
- graphs using the legacy architecture
135
- ([`GCN.py:18-35`](../legacy/root_gnn_dgl/models/GCN.py),
136
- [`GCN.py:182-251`](../legacy/root_gnn_dgl/models/GCN.py)).
137
-
138
- Next port `Transferred_Learning_Finetuning`, including pretrained
139
- `model_state_dict` loading, removal of the final classifier, and new classifier
140
- initialization ([`GCN.py:884-997`](../legacy/root_gnn_dgl/models/GCN.py)). Test
141
- both frozen and unfrozen modes. Defer other model classes until an active
142
- config or consumer proves they are needed.
143
-
144
- ## Phase 5 — objectives and metrics
145
-
146
- Implement the default objective exactly: elementwise configured loss,
147
- tracking-column weights, per-unique-label normalization, and averaging across
148
- labels ([`training_script.py:320-359`](../legacy/root_gnn_dgl/scripts/training_script.py)).
149
- Add parity cases for positive, zero, and negative weights and binary versus
150
- multiclass shapes.
151
-
152
- Port metric behavior from
153
- [`training_script.py:438-510`](../legacy/root_gnn_dgl/scripts/training_script.py):
154
- sigmoid threshold 0.5, argmax, weight masking, weighted ROC AUC, one-vs-rest
155
- multiclass AUC, and NaN behavior when AUC is undefined. Add `models/loss.py`
156
- classes only with dedicated tests; do not substitute their reductions.
157
-
158
- ## Phase 6 — checkpoint and lifecycle
159
-
160
- Task 10 implemented the in-memory single-process training lifecycle before the
161
- checkpoint portion of this phase: `Trainer`, explicit optimizer/scheduler
162
- builders, `EarlyStopping`, reproducibility seeding, `GraphBatch.to`, and
163
- epoch/history result types. Checkpoint persistence/resume and the Python
164
- inference/evaluation and named NPZ/ROOT output layers are now implemented.
165
- Distributed execution and CLI wiring were completed in the later phases.
166
-
167
- Task 10 also establishes corrected split semantics: validation is evaluated
168
- every epoch and is the only split used for model selection or early stopping;
169
- the test split remains held out and is evaluated separately after fitting. The
170
- legacy loader naming inversion (`test` used for selection and `val` held out)
171
- is not carried into the rewrite.
172
-
173
- Create a checkpoint adapter preserving `model_epoch_<epoch>.pt` and keys
174
- `epoch`, `model_state_dict`, `optimizer_state_dict`, and `early_stop`
175
- ([`training_script.py:565-604`](../legacy/root_gnn_dgl/scripts/training_script.py)).
176
- Support legacy DDP/compiled prefixes (`module.` and `_orig_mod.`) as exercised
177
- by checkpoint lookup and inference
178
- ([`utils.py:145-248`](../legacy/root_gnn_dgl/root_gnn_base/utils.py),
179
- [`inference.py:274-290`](../legacy/root_gnn_dgl/scripts/inference.py)). Port
180
- `EarlyStop` state and log parsing separately
181
- ([`utils.py:325-390`](../legacy/root_gnn_dgl/root_gnn_base/utils.py)). Verify
182
- resume, restart, early termination, and `.npz` fields before distributed work.
183
-
184
- ## Phase 7 — CLI, inference, and export
185
-
186
- Task 12 implemented ordered prediction/evaluation, task-owned score
187
- semantics, checkpoint weight-only loading, named metadata retention, NPZ
188
- output, and explicit ROOT entry alignment. The semantic CLI and the validated
189
- ROOT-GNN ONNX export adapter are implemented.
190
-
191
- Task 13 adds Hydra composition and a single-process CLI around those existing
192
- APIs. The current application data boundary is a versioned
193
- `GraphSampleCache`; ROOT preparation converts events through the shared
194
- feature and graph builders before writing that cache.
195
-
196
- Build thin new applications around tested library interfaces in this order:
197
-
198
- 1. preprocessing/cache generation (`scripts/prep_data.py`);
199
- 2. training/evaluation (`scripts/training_script.py`);
200
- 3. inference to `.npz` and ROOT (`scripts/inference.py`);
201
- 4. ONNX export after PyTorch parity (`gnn4colliders export`).
202
-
203
- Use subprocess integration tests with tiny fixtures. Preserve CLI options only
204
- where they serve an active workflow; document removed diagnostic/cluster-only
205
- options.
206
-
207
- ## Phase 8 — reproducibility and deployment
208
-
209
- Task 14 adds the initial deployment boundary: CPU/GPU DDP through standard
210
- `torchrun` variables, rank-local graph-sample sharding, global metric/output
211
- gathering, rank-0 checkpoint/config writing, and Perlmutter-oriented Slurm
212
- examples. Evaluation deliberately avoids sampler padding duplicates. The
213
- remaining follow-up is a streaming or sharded output path for very large
214
- distributed inference jobs.
215
-
216
- The seed policy remains explicit: the configured seed is offset by rank for
217
- process-local randomness, while distributed sample assignment is derived from
218
- the configured seed, world size, and epoch. GPU kernel nondeterminism and
219
- exact per-rank RNG checkpoint replay remain environment-dependent. Slurm/NCCL,
220
- Podman-HPC, ROOT, and Hugging Face integrations stay in launcher/adapters
221
- rather than package code.
222
-
223
- ## Checkpoint compatibility checklist
224
-
225
- - [x] Load a checked-in or generated multiclass pretrained checkpoint.
226
- - [x] Load a legacy fine-tuning checkpoint after prefix normalization.
227
- - [x] Resume optimizer and early-stop state.
228
- - [x] Produce equivalent logits on a deterministic graph fixture.
229
- - [x] Produce equivalent `.npz` score, label, and metadata fields.
230
- - [x] Preserve ROOT scalar/vector score branch conventions in the Python adapter.
231
-
232
- Known risks are documented in [`architecture.md`](architecture.md): edge order,
233
- self-loops, weight semantics, validation/test naming, padding, dynamic
234
- selection evaluation, reproducibility, and the experimental model/loss surface.
235
-
236
- ## Migration closure status
237
 
238
- ### Task 18 compatibility closure
 
 
 
239
 
240
- The compatibility boundary is now explicit in `gnn4colliders.compat`.
241
- Production ingestion stores named `EventMetadata`; legacy two-column tracking
242
- is converted only at the compatibility boundary. Checkpoint prefix cleanup and
243
- the historical ROOT-GNN `classify` to `classifier` mapping have one canonical
244
- implementation. The new checkpoint schema and named NPZ output remain
245
- canonical. See [`compatibility.md`](compatibility.md) for the supported and
246
- intentionally unsupported historical artifacts.
247
 
248
- The following matrix describes the supported new stack, rather than every
249
- class that exists in `legacy/`:
250
 
251
- | Legacy area | New-stack status | Notes |
252
- | --- | --- | --- |
253
- | ROOT/Awkward ingestion | migrated | `RootEventDataset` returns `EventSample` in file/event order |
254
- | node features | migrated + parity-tested | seven-column schema, `CALC_E`, ordering, scales, float32 |
255
- | edge construction | migrated + parity-tested | directed source-major topology and `[deta,dphi,dR]` |
256
- | graph cache | migrated | versioned `GraphSampleCache`; graph-level cache only |
257
- | folds and weights | migrated | named `EventMetadata.fold` and `.weight` |
258
- | batching | migrated | deterministic local loader and DDP sharding |
259
- | legacy padding modes | deferred | no active new-stack consumer |
260
- | `Edge_Network` | migrated + parity-tested | `EdgeNetwork`, raw logits |
261
- | transfer/fine-tuning | migrated + parity-tested | frozen or trainable backbone |
262
- | loss and metrics | migrated + parity-tested | task-owned weighted reductions and full-split AUC |
263
- | training lifecycle | migrated | `Trainer`, validation semantics, scheduler, early stopping |
264
- | checkpoints/resume | migrated | schema v1; historical weight/prefix adapter |
265
- | inference/NPZ | migrated | named output fields and ordered accumulation |
266
- | ROOT score output | compatibility adapter | Python API supported; CLI currently NPZ-only |
267
- | DDP | migrated | torchrun boundary, rank-0 artifacts, gathered metrics |
268
- | Slurm/Perlmutter | launcher examples | site policy remains outside package code |
269
- | ONNX export | migrated for ROOT-GNN | tensor-only adapter, ONNX Runtime validation, and `export` CLI; raw graph tensors are the input contract |
270
 
271
- ### Intentional redesigns
 
 
272
 
273
- These are deliberate new-stack contracts, not accidental parity failures:
274
 
275
- * `tracking[:, 0]` and `tracking[:, 1]` become named `metadata.fold` and
276
- `metadata.weight`; public consumers do not depend on positional columns.
277
- * Dynamic legacy YAML `module`/`class` construction becomes allow-listed
278
- semantic Hydra configuration.
279
- * The monolithic training script becomes `Task` + `Trainer` + checkpoint and
280
- inference adapters.
281
- * Graph state is scoped to the forward pass rather than relying on persistent
282
- mutation of shared graph state.
283
- * Model constructors do not mutate global RNG state; seeding is explicit in
284
- the training/application boundary.
285
- * Validation is the selection/early-stopping split and test is held out. This
286
- corrects the legacy loader-name inversion.
287
 
288
- Compatibility preserves externally observable scientific behavior where it is
289
- validated; it does not promise to preserve every legacy implementation bug.
290
- The characterized legacy transfer path had a nonempty-global handling bug;
291
- the rewrite supports named globals. Negative weights, rare empty graphs,
292
- historical checkpoint variants, and legacy padding edge cases remain areas to
293
- audit when a supported consumer requires them.
294
 
295
- ## ROOT-GNN v1 completion checklist
 
296
 
297
- - [x] active ROOT data path and graph cache
298
- - [x] validated feature, graph, model, task, and metric behavior
299
- - [x] train from scratch and fine-tune a pretrained backbone
300
- - [x] resume new-stack checkpoints and load supported historical weights
301
- - [x] evaluate and predict named outputs
302
- - [x] single-process and DDP application boundaries
303
- - [x] Perlmutter/Slurm launcher examples and profiling guidance
304
- - [x] ROOT-GNN ONNX export and CPU Runtime parity
305
- - [ ] streaming/sharded large-scale prediction output
306
- - [ ] removal of frozen legacy reference
307
- - [ ] ROOT-Transformer representation/model
308
 
309
- ROOT-GNN v1 is complete when the checked-in new stack can prepare active data,
310
- reproduce validated legacy behavior, train, transfer, resume, evaluate,
311
- predict, and run single-process or DDP workflows. The remaining unchecked
312
- items are intentionally deferred rather than undocumented promises.
 
 
1
+ # Migration and model-family roadmap
 
 
 
 
 
 
 
 
2
 
3
  ## Frozen ROOT-GNN baseline
4
 
5
+ The `root-gnn-parity-baseline` tag records the completed migration of the
6
+ active ROOT-GNN behavior into `src/gnn4colliders.models.root_gnn`. The
7
+ campaign covered full event preprocessing and graph parity, binary objectives
8
+ and metrics, deterministic fine-tuning, full-split training, checkpoint reload
9
+ and resume, reproducibility, and serialized graph-cache checks.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
10
 
11
+ The historical implementation is no longer in the active source tree. Its
12
+ observable behavior is represented by committed fixtures, tests, and the
13
+ one-way checkpoint/metadata compatibility adapters. New work must not add
14
+ imports from historical implementation paths.
15
 
16
+ ## Shared contracts
 
 
 
 
 
 
17
 
18
+ New model families should consume these boundaries:
 
19
 
20
+ - `EventSample` and named `EventMetadata` from `data`;
21
+ - shared collider feature builders from `features`;
22
+ - a representation-specific sample/batch type from the relevant adapter;
23
+ - task-owned loss, score, prediction, and metric semantics;
24
+ - the shared `Trainer`, checkpoint, reproducibility, and inference APIs.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
25
 
26
+ The graph path is the current ROOT-GNN representation. A sequence or token
27
+ model should add a separate representation boundary rather than placing
28
+ sequence behavior in graph modules or generic data code.
29
 
30
+ ## Next model-family milestone
31
 
32
+ The next vertical slice is a minimal `root_transformer` implementation:
 
 
 
 
 
 
 
 
 
 
 
33
 
34
+ 1. Define a small `SequenceSample` contract and deterministic fixture.
35
+ 2. Implement token construction using shared event/features infrastructure.
36
+ 3. Add the transformer model under `models/root_transformer/`.
37
+ 4. Connect it to the existing binary task and trainer on a tiny fixture.
38
+ 5. Add checkpoint, prediction, and reproducibility tests.
 
39
 
40
+ Do not generalize shared interfaces until this second representation exercises
41
+ the proposed common behavior.
42
 
43
+ ## Validation requirements
 
 
 
 
 
 
 
 
 
 
44
 
45
+ Every new model family must provide unit tests for its representation and
46
+ model, a small end-to-end integration test, checkpoint reload coverage, and a
47
+ deterministic repeatability check. Scientific behavior that is intentionally
48
+ shared with ROOT-GNN should be compared against the frozen reference fixture;
49
+ architecture-specific behavior should have its own reference outputs.
legacy/LICENSE DELETED
@@ -1,21 +0,0 @@
1
- MIT License
2
-
3
- Copyright (c) 2025 LBL ATLAS
4
-
5
- Permission is hereby granted, free of charge, to any person obtaining a copy
6
- of this software and associated documentation files (the "Software"), to deal
7
- in the Software without restriction, including without limitation the rights
8
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
- copies of the Software, and to permit persons to whom the Software is
10
- furnished to do so, subject to the following conditions:
11
-
12
- The above copyright notice and this permission notice shall be included in all
13
- copies or substantial portions of the Software.
14
-
15
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
- SOFTWARE.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/README.md DELETED
@@ -1,358 +0,0 @@
1
- ---
2
- license: mit
3
- tags:
4
- - arXiv:2412.10665
5
- ---
6
-
7
- This is a demo is of the approach described in the paper, ["Pretrained Event Classification Model for High Energy Physics Analysis"](https://arxiv.org/abs/2412.10665)
8
- ```
9
- @misc{ho2024pretrained,
10
- title={Pretrained Event Classification Model for High Energy Physics Analysis},
11
- author={Joshua Ho, Benjamin Ryan Roberts, Shuo Han, Haichen Wang},
12
- year={2024},
13
- eprint={2412.10665},
14
- archivePrefix={arXiv}
15
- }
16
- ```
17
-
18
- ## Abstract
19
-
20
- We introduce a foundation model for event classification in high-energy physics, built on a **Graph Neural Network** architecture and trained on **120 million simulated proton-proton collision events** spanning 12 distinct physics processes. The model is *pretrained* to learn a general and robust representation of collision data using challenging multiclass and multilabel classification tasks.
21
-
22
- Its performance is evaluated across five event classification tasks, which include both physics processes used during pretraining and new processes not encountered during pretraining. Fine-tuning the pretrained model significantly improves classification performance, particularly in scenarios with limited training data, demonstrating gains in both accuracy and computational efficiency.
23
-
24
- To investigate the underlying mechanisms behind these performance improvements, we employ a representational similarity evaluation framework based on *Centered Kernel Alignment*. This analysis reveals notable differences in the learned representations of fine-tuned pretrained models compared to baseline models trained from scratch.
25
-
26
- ## Introduction
27
-
28
- Machine learning has become a ubiquitous tool in particle physics, employed in a variety of tasks including triggering, simulation, reconstruction, and offline analysis. While its utility spans classification, regression, and generative tasks, the current paradigm of developing machine learning models from scratch for each specific application presents several challenges. This approach not only demands specialized expertise and substantial computing resources but can also result in suboptimal performance due to limited training data. The from-scratch development of models necessitates individual validation studies to ensure that neural networks utilize well-modeled information from training samples, whether derived from Monte Carlo simulations or control samples from experimental data.
29
-
30
- Foundation models offer a promising direction to address these limitations. These models, pre-trained on large, diverse datasets across various tasks, provide robust and general representations of underlying data structures. Notable examples in other fields include GPT-4 [OpenAI et al., 2024](#ref-openai-2024-gpt4) and BERT [Devlin et al., 2018](#ref-devlin-2018-bert) in natural language processing, Stable Diffusion [Rombach et al., 2021](#ref-rombach-2021-latentdiffusion) in image processing, and AlphaFold [Jumper et al., 2021](#ref-jumper-2021-alphafold) in structural biology. The foundation model approach offers several advantages for particle physics applications: reduced computing resources for fine-tuning [Yosinski et al., 2014](#ref-yosinski-2014-transfer) compared to training from scratch, superior performance on specific tasks (particularly with limited training data), and potentially simplified validation procedures as downstream tasks inherit verified representations from the pre-trained model.
31
-
32
- Current literature on pretrained models for particle physics can be categorized based on the data representation they handle. Models operating on particle- or event-level numerical data use features like particle four momenta or jets, leveraging self-supervised or generative methods to learn versatile representations. Detector-focused models operate on high-dimensional responses such as calorimeter deposits or pixel hits, employing geometry-aware techniques for accurate simulation and analysis. Finally, models using textual or code representations apply large language model architectures to integrate domain knowledge, enabling tasks like question answering and code generation.
33
-
34
- Recent studies have begun exploring foundation models tailored to particle physics data, which has a variety of distinct structures and properties across many experiments and data processing stages, including:
35
-
36
- - particle-level & event-level numeric data [Wildridge et al., 2024](#ref-wildridge-2024-bumblebee), [Katel et al., 2024](#ref-katel-2024-jet), [Golling et al., 2024](#ref-golling-2024-maskedset), [Mikuni & Nachman, 2024](#ref-mikuni-2024-omnilearn), [Harris et al., 2024](#ref-harris-2024-resimulation), [Birk et al., 2024](#ref-birk-2024-omnijet), [Vigl et al., 2024](#ref-vigl-2024-finetune),
37
- - detector-level & geometry-aware data [Araz et al., 2024](#ref-araz-2024-pointcloud), [Liu et al., 2023](#ref-liu-2023-gaam), [Hashemi et al., 2024](#ref-hashemi-2024-gen), [Huang et al., 2024](#ref-huang-2024-lmtracking),
38
- - textual or code data [Zhang et al., 2024](#ref-zhang-2024-xiwu).
39
-
40
- This paper presents a foundation model designed specifically for collider event-level data. In modern collider experiments, final-stage analysis processes information from reconstructed objects that either directly correspond to particles in collision final states (such as leptons and photons) or serve as proxies (such as jets and missing transverse energy). While traditional approaches often relied on "high-level" variables calculated from object features, recent trends favor direct input of event objects and their features into neural networks for analysis tasks. A notable example is [ATLAS Collaboration, 2023](#ref-atlas-2023-4top), which established the observation of simultaneous production of four top quarks with the ATLAS experiment by employing a graph neural network (GNN) architecture to process event-level object information.
41
-
42
- We present foundation models that adopt an architecture similar to that used for [ATLAS Collaboration, 2023](#ref-atlas-2023-4top). Our models are pre-trained using either multiclass classification or multi-label learning tasks across 12 distinct physics processes. We evaluate these models through fine-tuning and testing on five classification tasks, including both familiar and novel processes not seen during pre-training. Our analysis benchmarks the models' performance improvements, their scaling behavior with training sample size, and computational efficiency, representing the first prototype of a foundation model operating on collider final-state object data.
43
-
44
- ## Data Samples
45
-
46
- To provide a diverse set of physics processes for the pretraining, we use Madgraph@NLO 2.7.3 [Alwall et al., 2014](#ref-alwall-2014hca) to generate proton-proton collision events at next-to-leading order (NLO) in Quantum Chromodynamics (QCD). We generate 12 distinct Standard Model (SM) physics processes, including six major Higgs boson production mechanisms: gluon fusion production \\(ggF\\), vector boson fusion \\(VBF\\), associated production of the Higgs boson with a W boson \\(WH\\) or a Z boson \\(ZH\\), associated production of the Higgs boson with a top-quark pair \\(t\bar{t}H\\), and associated production of the Higgs boson with a single top quark and a forward quark \\(tHq\\). Additionally, we simulate six top quark production processes: single top production, top-quark pair production \\(t\bar{t}\\), top quark pair production in association with a pair of photons \\(t\bar{t}\gamma\gamma\\), associated production of a top-quark pair with a W boson \\(t\bar{t}W\\), simultaneous production of three top quarks \\(t\bar{t}t\\), and simultaneous production of four top quarks \\(t\bar{t}t\bar{t}\\). In these samples, the Higgs boson and top quarks decay inclusively. These 12 Higgs and top quark production processes constitute the pretraining dataset.
47
-
48
- To test the pretrained model, we further generated four processes including three beyond Standard Model (SM) processes: a SM \\(t\bar{t}H\\) production where the Higgs boson decays exclusively to a pair of photons, a \\(t\bar{t}H\\) production with the Higgs boson decaying to a pair of photons, where the top-Yukawa coupling is CP-odd, implemented using the Higgs Characterization model [Artoisenet et al., 2013](#ref-artoisinet-2013puc), the production of a pair of superpartners of the top quark (s-top) using the Minimal Supersymmetric Standard Model (MSSM) [Rosiek, 1990](#ref-rosiek-1990), [Allanach et al., 2009](#ref-allanach-2009), and flavor changing neutral current (FCNC) processes [Degrande et al., 2015](#ref-degrande-2015), [Durieux et al., 2015](#ref-durieux-2015). For the s-top process, we simulate the production of heavier s-top pairs \\(t_2\bar{t_2}\\), where each heavier s-top (mass 582 GeV) decays into a lighter s-top \\(t_1\\) or \\(\bar{t_1}\\), mass 400 GeV) and a Higgs boson. The FCNC process involves \\(t\bar{t}\\) production where one top quark decays to a Higgs boson and a light quark. We generate 10 million events for each process, except for \\(tHq\\) and \\(t\bar{t}t\bar{t}\\), where 5 million events were produced.
49
-
50
- In all simulation samples, the center of mass energy of the proton-proton collision is set to 13 TeV. The Higgs boson, top quarks, and vector bosons are set to decay inclusively (except the \\(t\bar{t}H \rightarrow \gamma\gamma\\) samples), with MadSpin [Artoisenet et al., 2012](#ref-artoisinet-2012st) handling the decays of top quarks and W bosons. The generated events are processed through Pythia 8.235 [Sjostrand et al., 2015](#ref-sjostrand-2015) for parton showering and heavy particle decays, followed by Delphes 3.4.2 [de Favereau et al., 2014](#ref-defavereau-2014) configured to emulate the ATLAS detector [ATLAS Collaboration, 2008](#ref-atlas-2008) for fast detector simulation.
51
-
52
- The detector-level object selection criteria are defined to align with typical experimental conditions. Photons are required to have transverse momentum \\(p_T \geq 20~\mathrm{GeV}\\) and pseudorapidity \\(|\eta| \leq 2.37\\), excluding the electromagnetic calorimeter crack region \\(1.37 < |\eta| < 1.52\\). Electrons must have \\(p_T \geq 10~\mathrm{GeV}\\) and \\(|\eta| \leq 2.47\\) (excluding the same crack region), while muons are selected with \\(p_T \geq 10~\mathrm{GeV}\\) and \\(|\eta| \leq 2.7\\). Jets are reconstructed using the anti-\\(k_t\\) algorithm [Cacciari et al., 2008](#ref-cacciari-2008gp) with radius parameter \\(\Delta R=0.4\\), where \\(\Delta R\\) is defined as \\(\sqrt{\Delta\eta ^2 + \Delta\phi^2}\\), with \\(\Delta\eta\\) being the difference in pseudorapidity and \\(\Delta\phi\\) the difference in azimuthal angle. Jets must satisfy \\(p_T \geq 25~\mathrm{GeV}\\) and \\(|\eta| \leq 2.5\\). To avoid double-counting, jets are removed if they are within \\(\Delta R < 0.4\\) of a photon or lepton. The identification of jets originating from b-quark decays (b-tagging) is performed by matching jets within \\(\Delta R = 0.4\\) of a b-quark, with efficiency corrections applied to match the performance of the ATLAS experiment's b-tagging algorithm [ATLAS Collaboration, 2019](#ref-atlas-2019bwq).
53
-
54
- ## Methods
55
-
56
- ### Overview
57
-
58
- We present a methodology for developing and evaluating a foundation model for particle collision event analysis. The approach centers on pretraining a Graph Neural Network (GNN) architecture using a comprehensive dataset that spans multiple physics tasks, enabling the model to learn robust and transferable features. For task-specific applications, we employ a fine-tuning strategy that combines output layer adaptation with carefully calibrated learning rates for updating the pretrained parameters.
59
-
60
- Given the prevalence of classification problems in particle physics data analysis, we evaluate the model's efficacy through a systematic assessment across five binary classification tasks:
61
-
62
- - \\(t\bar{t}H(\rightarrow \gamma\gamma)\\) with CP-even versus CP-odd t-H interaction
63
- - \\(t\bar{t}\\) with FCNC top quark decays versus $tHq$ processes
64
- - \\(t\bar{t}W\\) versus $ttt$ processes
65
- - Stop pair production with Higgs bosons in the decay chain versus \\(t\bar{t}H\\) processes
66
- - \\(WH\\) versus \\(ZH\\) production modes
67
-
68
- Our evaluation metrics encompass classification performance, computational efficiency, and model interpretability. The investigation extends to analyzing the model's scaling behavior with respect to training dataset size, benchmarked against models trained without pretraining. Although we explored transfer learning through parameter freezing of pretrained layers, this approach did not yield performance improvements, leading us to focus our detailed analysis on fine-tuning strategies.
69
-
70
- This methodological framework demonstrates the potential of foundation models to enhance the efficiency of particle physics analyses while improving task-specific performance, offering a promising direction for future high-energy physics research.
71
-
72
- ---
73
-
74
- ### GNN Architecture
75
-
76
- We implement a Graph Neural Network (GNN) architecture that naturally accommodates the point-cloud structure of particle physics data, employing the DGL framework with a PyTorch backend [Wang et al., 2019][ref-dgl-2019], [Paszke et al., 2019][ref-pytorch-2019]. A fully connected graph is constructed for each event, with nodes corresponding to reconstructed jets, electrons, muons, photons, and \\(\vec{E}_T^{\text{miss}}\\). The features of each node include the four-momentum \\((p_T, \eta, \phi, E)\\) of the object with a massless assumption (\\(E = p_T \cosh \eta\\)), the b-tagging label (for jets), the charge (for leptons), and an integer labeling the type of object represented by the node. We use a placeholder value of 0 for features which are not defined for every node type such as the b-jet tag, lepton charge, or the pseudorapidity of \\(\vec{E}_T^{\text{miss}}\\). We assign the angular distances (\\(\Delta \eta, \Delta \phi, \Delta R\\)) as edge features and the number of nodes $N$ in the graph as a global feature. We denote the node features \\(\{\vec x_i\}\\), edge features \\(\{\vec y_{ij}\}\\), and global features \\(\{\vec z\}\\).
77
-
78
- The GNN model is based on the graph network architecture described in [Battaglia et al., 2018][ref-graphnets-2018] using simple multilayer perceptron (MLP) feature functions and summation aggregation. The model is comprised of three primary components: an encoder, the graph network, and a decoder. In the encoder, three MLPs embed the nodes, edges, and global features into a latent space of dimension 64. The graph network block, which is designed to facilitate message passing between different domains of the graph, performs an edge update $f_e$, followed by a node update $f_n$, and finally a global update $f_g$, all defined below. The inputs to each update MLP are concatenated.
79
-
80
- $$
81
- \vec {y'}_{ij} = f_e\left(\{\vec x_k\},\vec y_{ij},\vec z\right) = \mathrm{MLP}\left(\vec x_i,\vec x_j,\vec y_{ij},\vec z\right)
82
- $$
83
-
84
- $$
85
- \vec{x'}_{i} = f_n\left(\vec x_i,\{\vec{y'}_{jk}\},\vec z\right) = \mathrm{MLP}\left(\vec x_i,\sum_j\vec{y'}_{ij},\vec z\right)
86
- $$
87
-
88
- $$
89
- \vec{z'} = f_g\left(\{\vec{x'}_i\},\{\vec{y'}_{ij}\},\vec z\right) = \mathrm{MLP}\left(\sum_i\vec{x'}_i,\sum_{i,j}\vec{y'}_{ij},\vec z\right)
90
- $$
91
-
92
- This graph block is iterated four times with the same update MLPs. Finally, the global features are passed through a decoder MLP and a final layer linear to produce the desired model outputs. Each MLP consists of 4 linear layers, each with an output width of 64, with the `ReLU` activation function. The output of the MLP is then passed through a `LayerNorm` layer [Ba et al., 2016][ref-layernorm-2016]. The total number of trainable parameters in this model is about 400,000.
93
-
94
- As a performance benchmark, a baseline GNN model is trained from scratch for each classification task. The initial learning rate is set to \\(10^{-4}\\) with an exponential decay following \\(LR(x) = LR_{\text{initial}}\cdot(0.99)^x\\), where \\(x\\) represents the epoch number.
95
-
96
- ---
97
-
98
- ### Pretraining Strategy
99
-
100
- We explore two complementary pretraining approaches to develop robust representations of collision events: (1) multi-class classification, which trains the model to distinguish between different physics processes, and (2) multi-label classification, which predicts the existence and kinematics of heavy particles with prompt decays. The pretraining dataset consists of approximately 120 million events, evenly distributed across 12 distinct physics processes, including all major Higgs boson production mechanisms and top quark processes as described in [Data Samples](#sec-data). This large-scale pretraining effort was conducted on the Perlmutter supercomputer at NERSC.
101
-
102
- #### Multi-class Classification
103
-
104
- For Monte Carlo simulated events, the underlying physics process that generated each event is known precisely, providing natural labels for supervised learning. However, the challenge lies in the complexity of collision events: different physics processes can produce similar kinematics and event topologies, particularly in certain regions of phase space. No single observable can unambiguously identify the underlying process. By training the model to distinguish between 12 different processes simultaneously, we challenge it to learn subtle differences in kinematics and topology that collectively characterize each process. The model is trained using categorical cross entropy as the loss function. The output layer of the multiclass classification model has 832 trainable parameters.
105
-
106
- #### Multi-label Classification
107
-
108
- This approach combines both classification and regression tasks to characterize collision events. For discrete properties like particle presence in specific kinematic regions, we employ classification labels with binary cross-entropy loss. For continuous quantities like particle multiplicities, we use regression labels with mean-squared error loss. This hybrid approach enables the model to learn both categorical and continuous aspects of the physics processes simultaneously.
109
-
110
- We develop a comprehensive set of 41 labels that capture both particle multiplicities and kinematic properties. This approach increases prediction granularity and enhances model interpretability. By training the model to predict event kinematics rather than event identification, we create a task-independent framework that can potentially generalize better to novel scenarios not seen during pretraining.
111
-
112
- The particle multiplicity labels count the number of Higgs bosons (\\(n_{\text{higgs}}\\)), top quarks (\\(n_{\text{tops}}\\)), vector bosons (\\(n_V\\)), \\(W\\) bosons (\\(n_W\\)), and \\(Z\\) bosons (\\(n_Z\\)). The kinematic labels characterize the transverse momentum (\\(p_T\\)), pseudorapidity (\\(\eta\\)), and azimuthal angle (\\(\phi\\)) of Higgs bosons and top quarks through binned classifications.
113
-
114
- For Higgs bosons, $p_T$ is categorized into three ranges: (0, 30) GeV, (30, 200) GeV, and (200, \\(\infty\\)) GeV, with the upper range particularly sensitive to potential BSM effects. Similarly, both leading and subleading top quarks have $p_T$ classifications spanning (0, 30) GeV, (30, 300) GeV, and (300, \\(\infty\\)) GeV. When no particle exists within a specific \\(p_T\\) range, the corresponding label is set to \\([0, 0, 0]\\). For all particles, \\(\eta\\) measurements are divided into 4 bins with boundaries at \\([-1.5, 0, 1.5]\\), while \\(\phi\\) measurements use 4 bins with boundaries at \\([-\frac{\pi}{2}, 0, \frac{\pi}{2}]\\). As with \\(p_T\\), both \\(\eta\\) and \\(\phi\\) labels default to \\([0, 0, 0, 0]\\) in the absence of a particle. This comprehensive labeling schema enables fine-grained learning of kinematic distributions and particle multiplicities, essential for characterizing complex collision events.
115
-
116
- The loss function combines individual losses from all 41 labels through weighted averaging. Binary cross-entropy is applied to classification labels, while mean-squared error is used for regression labels. The model generates predictions for all labels simultaneously, with individual losses calculated according to their respective types. The final loss is computed as an equally-weighted average across all labels, with weights set to 1 to ensure uniform contribution to the optimization process. The output layer of the multilabel model has 2,688 trainable parameters.
117
-
118
- #### Pretraining
119
-
120
- During pre-training, the initial learning rate is \\(10^{-4}\\), and the learning rate decays by 1% each epoch following the power law function \\(LR(x) = 10^{-4}\cdot(0.99)^x\\), where \\(x\\) is the number of epochs. Both pre-trained models reach a plateau in loss by epoch 50, at which point the training is stopped.
121
-
122
- ---
123
- ### Fine-tuning Methodology
124
-
125
- For downstream tasks, we adjust the model architecture for fine-tuning by replacing the original output layer (final linear layer) with a newly initialized linear layer while retaining the pre-trained weights for all other layers. This modification allows the model to specialize in the specific downstream task while leveraging the general features learned during pretraining.
126
-
127
- The fine-tuning process begins with distinct learning rate setups for different parts of the model. The newly initialized linear layer is trained with an initial learning rate of \\(10^{-4}\\), matching the rate used for models trained from scratch. Meanwhile, the pre-trained layers are fine-tuned more cautiously with a lower initial learning rate of \\(10^{-5}\\). This approach ensures that the pre-trained layers adapt gradually without losing their general features, while the new layer learns effectively from scratch. Both learning rates decay over time following the same power law function, \\(LR(x) = LR_{initial} \cdot (0.99)^x\\), to promote stable convergence as training progresses.
128
-
129
- We also evaluated a transfer learning setup in which either the decoder MLP or the final linear layer was replaced with a newly initialized component. During this process, all other model parameters remained frozen, leveraging the pre-trained features without further updating them. However, we did not observe performance improvements using the transfer learning setup. Consequently, we focus on reporting results obtained with the fine-tuning approach.
130
-
131
- ---
132
-
133
- ### Performance Evaluation
134
-
135
- We assess model performance using two figures of merit: the classification accuracy and the Area Under the Curve (AUC) of the Receiver Operating Characteristic (ROC) curve. The accuracy is defined as the fraction of correctly classified events when applying a threshold of 0.5 to the neural network output score. Both metrics demonstrate consistent trends in our analysis.
136
-
137
- To obtain reliable performance estimates and uncertainties, we employ an ensemble training approach where 5 independent models are trained for each configuration with random weight initialization and random subsets of the training dataset. This enables us to evaluate both the models' sensitivity to initial parameters and to quantify uncertainties in their performance.
138
-
139
- To investigate how model performance scales with training data, we conducted training runs using sample sizes ranging from \\(10^3\\) to \\(10^7\\) events per class (\\(10^3\\), \\(10^4\\), \\(10^5\\), \\(10^6\\), and \\(10^7\\)) for each model setup: the from-scratch baseline and models fine-tuned from multi-class or multi-label pretrained models. For the \\(10^7\\) case, only the initialization was randomized due to dataset size limitations. All models were evaluated on the same testing dataset, consisting of 2 million events per class, which remained separate from the training process.
140
-
141
- | **Name of Task** | **Pretraining Task** | \\(10^3\\) | \\(10^4\\) | \\(10^5\\) | \\(10^6\\) | \\(10^7\\) |
142
- |----------------------|----------------------|--------------------|--------------------|--------------------|--------------------|--------------------|
143
- | **ttH CP Even vs Odd** | Baseline Accuracy | 56.5 ± 1.1 | 62.2 ± 0.1 | 64.3 ± 0.0 | 65.7 ± 0.0 | 66.2 ± 0.0 |
144
- | | Multiclass (%) | +4.8 ± 1.1 | +3.4 ± 0.1 | +1.3 ± 0.0 | +0.2 ± 0.0 | −0.0 ± 0.0 |
145
- | | Multilabel (%) | +2.1 ± 1.2 | +1.9 ± 0.1 | +0.8 ± 0.1 | +0.0 ± 0.0 | −0.1 ± 0.0 |
146
- | **FCNC vs tHq** | Baseline Accuracy | 63.6 ± 0.7 | 67.8 ± 0.4 | 68.4 ± 0.3 | 69.3 ± 0.3 | 67.9 ± 0.0 |
147
- | | Multiclass (%) | +5.8 ± 0.8 | +1.2 ± 0.4 | +1.4 ± 0.3 | +0.5 ± 0.3 | −0.0 ± 0.0 |
148
- | | Multilabel (%) | −5.3 ± 0.8 | −1.3 ± 0.4 | +0.9 ± 0.4 | +0.3 ± 0.3 | +0.4 ± 0.1 |
149
- | **ttW vs ttt** | Baseline Accuracy | 75.8 ± 0.1 | 77.6 ± 0.1 | 78.9 ± 0.0 | 79.8 ± 0.0 | 80.3 ± 0.0 |
150
- | | Multiclass (%) | +3.7 ± 0.1 | +2.7 ± 0.1 | +1.3 ± 0.0 | +0.4 ± 0.0 | +0.0 ± 0.0 |
151
- | | Multilabel (%) | +2.2 ± 0.1 | +1.1 ± 0.1 | +0.5 ± 0.0 | +0.0 ± 0.0 | −0.1 ± 0.0 |
152
- | **stop vs ttH** | Baseline Accuracy | 83.0 ± 0.2 | 86.3 ± 0.1 | 87.6 ± 0.0 | 88.5 ± 0.0 | 88.8 ± 0.0 |
153
- | | Multiclass (%) | +0.4 ± 0.2 | +1.9 ± 0.1 | +1.0 ± 0.0 | +0.3 ± 0.0 | +0.0 ± 0.0 |
154
- | | Multilabel (%) | +2.8 ± 0.2 | +1.0 ± 0.1 | +0.5 ± 0.0 | +0.0 ± 0.0 | −0.0 ± 0.0 |
155
- | **WH vs ZH** | Baseline Accuracy | 51.4 ± 0.1 | 53.9 ± 0.1 | 55.8 ± 0.0 | 57.5 ± 0.0 | 58.0 ± 0.0 |
156
- | | Multiclass (%) | +5.2 ± 0.1 | +5.3 ± 0.1 | +3.1 ± 0.0 | +0.6 ± 0.0 | +0.1 ± 0.0 |
157
- | | Multilabel (%) | −1.1 ± 0.1 | −0.9 ± 0.2 | +0.5 ± 0.1 | +0.1 ± 0.0 | −0.1 ± 0.0 |
158
-
159
- > **Table 1**: Accuracy of the traditional model versus the accuracy increase due to fine-tuning from various pretraining tasks.
160
- > The accuracies are averaged over 5 independently trained models with randomly initialized weights and trained on a random subset of the data. One exception is the \\(10^7\\) training where all models use the same dataset due to limitations on our dataset size. The random subsets are allowed to overlap, but this overlap should be very minimal because all models take an independent random subset of \\(10^7\\) events. The testing accuracy is calculated from the same testing set of 2 million events per class across all models for a specific training task. The errors are the propagated errors (root sum of squares) of the standard deviation of accuracies for each model.
161
-
162
- ## Results
163
-
164
- ### Classification Performance
165
-
166
- Since the observations of AUC and accuracy show similar trends, we focus the presentation of the results using accuracy here for conciseness in Table 1.
167
-
168
- In general, the fine-tuned pretrained model achieves at least the same level of classification performance as the baseline model. Notably, there are significant improvements, particularly when the sample size is small, ranging from \\(10^3\\) to \\(10^4\\) events. In some cases, the accuracy improvements exceed five percentage points, demonstrating that pretrained models provide a strong initial representation that compensates for limited data. The numerical values of the improvements in accuracy may not fully capture the impact on the sensitivity of the measurements for which the neural network classifier is used, and the final sensitivity improvement is likely to be greater.
169
-
170
- As the training sample size grows to \\(10^5\\), \\(10^6\\), and eventually \\(10^7\\) events, the added benefit of pretraining diminishes. With abundant data, models trained from scratch approach or even match the accuracy of fine-tuned pretrained models. This suggests that large datasets enable effective learning from scratch, rendering the advantage of pretraining negligible in such scenarios.
171
-
172
- Although both pretraining approaches offer benefits, multiclass pretraining tends to provide more consistent improvements across tasks, especially in the low-data regime. In contrast, multilabel pretraining can sometimes lead to neutral or even slightly negative effects for certain tasks and data sizes. This highlights the importance of the pretraining task design, as the similarity between pretraining and fine-tuning tasks in the multiclass approach appears to yield better-aligned representations.
173
-
174
- Finally, the spread of accuracy across the five tasks for the baseline model is quite large, offering a robust test of fine-tuning across tasks of varying difficulty. The consistent observation of these trends across tasks confirms the reliability and robustness of the findings.
175
-
176
- ---
177
-
178
- ### Model Interpretability
179
-
180
- We aim to understand whether pretrained and baseline models learn the same underlying representations. If the two models exhibit high similarity, a plausible interpretation is that pretraining provides the pretrained model with an advantageous initialization, allowing it to converge to a similar state as the baseline model more efficiently. Conversely, significant differences between the models would indicate that pretraining facilitates the development of a more general and robust latent space, which serves as a foundation for fine-tuning to effectively adapt to the downstream task. To investigate this, we analyzed the representational similarity between a pretrained model fine-tuned for the downstream task and a baseline model trained directly on the downstream task without pretraining.
181
-
182
- We use Centered Kernel Alignment (CKA) [Kornblith et al., 2019][ref-kornblith-2019-cka] to analyze model similarity and interpretability. CKA is a robust metric that quantifies the similarity between the internal representations of neural networks by comparing their feature matrices in a manner that is invariant to scaling, rotation, and alignment. This invariance makes CKA particularly effective for studying relationships between network layers, even across networks of different sizes or those trained from varying initializations.
183
-
184
- The similarity is evaluated using a 64-dimensional latent representation after the decoder stage of the GNN model. This choice allows us to compare the internal states of the models at a fine-grained level and understand how training strategies impact the representations directly used for the output task.
185
-
186
- To provide an intuitive understanding of CKA values, we construct a table of the CKA scores for various transformations performed on a set of dummy data.
187
-
188
- - **A:** randomly initialized matrix with shape (1000, 64), following a normal distribution (\\(\sigma = 1, \mu = 0\\))
189
- - **B:** matrix with shape (1000, 64) constructed via various transformations performed on \\(A\\)
190
- - **Noise:** randomly initialized noise matrix with shape (1000, 64), following a normal distribution (\\(\sigma = 1, \mu = 0\\))
191
-
192
- | Dataset | CKA Score |
193
- |---------|-----------|
194
- | \\(A, B = A\\) | 1.00 |
195
- | \\(A, B =\\) permutation on columns of \\(A\\) | 1.00 |
196
- | \\(A, B = A + \mathrm{Noise}(0.1)\\) | 0.99 |
197
- | \\(A, B = A + \mathrm{Noise}(0.5)\\) | 0.80 |
198
- | \\(A, B = A + \mathrm{Noise}(0.75)\\) | 0.77 |
199
- | \\(A, B = A \cdot \mathrm{Noise}(1)\\) (Linear Transformation) | 0.76 |
200
- | \\(A, B = A + \mathrm{Noise}(1)\\) | 0.69 |
201
- | \\(A, B = A + \mathrm{Noise}(2)\\) | 0.51 |
202
- | \\(A, B = A + \mathrm{Noise}(5)\\) | 0.39 |
203
-
204
- **Table 2:** CKA scores for a dummy dataset \\(A\\) and \\(B\\), where \\(B\\) is created via various transformations performed on \\(A\\).
205
-
206
- As seen in Table 2 and in the definition of the CKA, the CKA score is permutation-invariant. We will use the CKA score to evaluate the similarity between various models and gain insight into the learned representation of detector events in each model (i.e., the information that each model learns).
207
-
208
- We train ensembles of models for each training task to observe how the CKA score changes due to the random initialization of our models. The CKA score between two models is then defined to be:
209
-
210
- \\[
211
- CKA(A, B) = \frac{1}{n^2} \sum_i^n \sum_j^n CKA(A_i, B_j)
212
- \\]
213
-
214
- where \\(A_i\\) is the representation learned by the \\(i^{\text{th}}\\) model in an ensemble with \\(n\\) total models. The error in CKA is the standard deviation of \\(CKA(A_i, B_j)\\).
215
-
216
- Here we present results for the CKA similarity between the final model in each setup with the final model in the baseline, shown in Table 3.
217
-
218
- | Training Task | Baseline | Multiclass | Multilabel |
219
- |-----------------------|------------------|-----------------|-----------------|
220
- | ttH CP Even vs Odd | 0.94 ± 0.05 | 0.82 ± 0.01 | 0.77 ± 0.06 |
221
- | FCNC vs tHq | 0.96 ± 0.03 | 0.76 ± 0.01 | 0.81 ± 0.01 |
222
- | ttW vs ttt | 0.91 ± 0.08 | 0.75 ± 0.10 | 0.72 ± 0.05 |
223
- | stop vs ttH | 0.87 ± 0.11 | 0.79 ± 0.12 | 0.71 ± 0.08 |
224
- | WH vs ZH | 0.90 ± 0.07 | 0.53 ± 0.03 | 0.44 ± 0.06 |
225
-
226
- **Table 3:** CKA Similarity of the latent representation before the decoder with the baseline model, averaged over 3 models per training setup, and all models trained with the full dataset (\\(10^7\\)). The baseline column is not guaranteed to be 1.0 because of the random initialization of the model. Each baseline model converges to a slightly different representation as seen in the CKA values in that column.
227
-
228
- The baseline models with different initializations exhibit high similarity values, ranging from approximately 0.87 to 0.96, which indicates that independently trained baseline models tend to converge on similar internal representations despite random initialization. Across the considered tasks, models trained as multi-class or multi-label classifiers exhibit noticeably lower CKA similarity scores when compared to the baseline model. For example, in the WH vs ZH task, the baseline model and another baseline trained model have a high similarity of 0.90, whereas the multi-class and multi-label models show significantly reduced similarities (0.53 and 0.44, respectively). This pattern suggests that the representational spaces developed by multi-class or multi-label models differ substantially from those learned by the baseline model that was trained directly on the downstream classification task.
229
-
230
- ### Computational Efficiency
231
-
232
- To estimate the computational resources required for each approach, we measured the wall time needed for a model to reach its final performance. For baseline models, this is defined as the wall time from the start of training until the loss of the model plateaus. For the foundation model approach, the estimate includes both the pretraining time and the fine-tuning time, each measured from the start of training until the loss plateaus. This approach ensures a consistent and comprehensive evaluation of the computational demands.
233
-
234
- ![The ratio of the fine-tuning time required to achieve 99% of the baseline model's final classification accuracy to the total time spent training the baseline model.](training_time.png)
235
- *Fig. 1: The ratio of the fine-tuning time required to achieve 99% of the baseline model's final classification accuracy to the total time spent training the baseline model.*
236
-
237
- Figure 1 shows the fine-tuning time for the model pretrained with multiclass classification, relative to the time required for the baseline model, as a function of training sample size. In general, the fine-tuning time is significantly shorter than the training time required by the baseline model approach. For smaller training sets, on the order of \\(10^5\\) events, tasks such as FCNC vs. tHq and ttW vs. ttt benefit substantially from the pretrained model’s “head start,” achieving their final performance in only about 1% of the baseline time. For large training datasets, the fine-tuning time relative to the baseline training time becomes larger; however, given that the large training sample typically requires longer training time, fine-tuning still yields much faster training convergence. The ttH CP-even vs. ttH CP-odd task, with a training sample size of \\(10^7\\) events, is an exception where the fine-tuning time exceeds the training time required for the baseline model. This is likely because the processes involved in this task include photon objects in the final states, which are absent from the events used during pretraining.
238
-
239
- To accurately evaluate the total time consumption, it is necessary to include the pretraining time required for the foundation model approach. The pretraining times are as follows:
240
-
241
- - **Multi-class pretraining:** 45.5 GPU hours
242
- - **Multi-label pretraining:** 60.0 GPU hours
243
-
244
- The GPU hours recorded for the multi-label model represent the total time required when training the model in parallel on 16 GPUs. This includes a model synchronization step, which results in higher GPU hours compared to the multi-class pretraining model.
245
-
246
- The foundation model approach becomes increasingly efficient when a large number of tasks are fine-tuned using the same pretrained model, compared to training each task independently from scratch. To illustrate this, we evaluate the computational time required for a scenario where the training sample contains \\(10^7\\) events. For the five tasks tested in this study, the baseline training time (training from scratch) ranges from 1.68 GPU hours (WH vs. ZH) to 5.30 GPU hours (ttW vs. ttt), with an average baseline training time of 2.94 GPU hours. In contrast, the average fine-tuning time for the foundation model approach, relative to the baseline, is 38% of the baseline training time for \\(10^7\\) events. Based on these averages, we estimate that the foundation model approach becomes more computationally efficient than the baseline approach when fine-tuning is performed for more than 41 tasks.
247
-
248
- As a practical example, the ATLAS measurement of Higgs boson couplings using the \\(H \rightarrow \gamma\gamma\\) decay channel [ATLAS Collaboration, 2023][ref-atlas-2023-higg] involved training 42 classifiers for event categorization. This coincides with our estimate, suggesting that the foundation model approach can reduce computational costs even for a single high-energy physics measurement.
249
-
250
- ## Conclusions
251
-
252
- We presented an in-depth study of a particle physics foundation model designed to operate on the four-momentum and identification properties of event final-state objects. This model is built on a Graph Neural Network (GNN) architecture and trained on a dataset comprising 120 million simulated proton-proton collision events across 12 distinct physics processes. The pretraining phase explored both multiclass and multilabel classification tasks, providing a robust foundation for downstream applications. Notably, the pretrained models demonstrated significant improvements in event classification performance when fine-tuned, particularly for tasks with limited training samples.
253
-
254
- The foundation model approach also offers substantial computational advantages. By leveraging fine-tuning, this methodology reduces the computational resources required for large-scale applications across multiple tasks. Our estimates indicate that significant resource savings can be achieved even for single particle physics measurements, making this approach both scalable and efficient.
255
-
256
- To better understand the learned representations of the pretrained model and guide future optimization efforts, we employed a representational similarity evaluation framework using Centered Kernel Alignment (CKA). This metric allowed us to investigate the source of the performance gains observed in the foundation model. Our analysis revealed notable differences in the learned representations between the fine-tuned pretrained model and a baseline model trained from scratch. In deep learning, it is well-established that multiple equally valid solutions can exist. Future studies are necessary to determine whether the low similarity in latent representations reflects complementary information uniquely captured by the foundation and baseline models, or if it can simply be attributed to connected local minima in the loss landscape.
257
-
258
- ## Acknowledgments
259
-
260
- This work is supported by the U.S. National Science Foundation under the Award No. 2046280, and by U.S. Department of Energy, Office of Science under contract DE-AC02-05CH11231.
261
-
262
- ## References
263
-
264
- - <span id="ref-openai-2024-gpt4"></span> **OpenAI et al.** GPT-4 Technical Report. arXiv:2303.08774 (2024). [https://arxiv.org/abs/2303.08774](https://arxiv.org/abs/2303.08774)
265
-
266
- - <span id="ref-yosinski-2014-transfer"></span> **Jason Yosinski, Jeff Clune, Yoshua Bengio, Hod Lipson.** How transferable are features in deep neural networks? CoRR abs/1411.1792 (2014). [http://arxiv.org/abs/1411.1792](http://arxiv.org/abs/1411.1792)
267
-
268
- - <span id="ref-rombach-2021-latentdiffusion"></span> **Robin Rombach, Andreas Blattmann, Dominik Lorenz, Patrick Esser, Björn Ommer.** High-Resolution Image Synthesis with Latent Diffusion Models. CoRR abs/2112.10752 (2021). [https://arxiv.org/abs/2112.10752](https://arxiv.org/abs/2112.10752)
269
-
270
- - <span id="ref-podell-2023-sdxl"></span> **Dustin Podell, Zion English, Kyle Lacey et al.** SDXL: Improving Latent Diffusion Models for High-Resolution Image Synthesis. arXiv:2307.01952 (2023). [https://arxiv.org/abs/2307.01952](https://arxiv.org/abs/2307.01952)
271
-
272
- - <span id="ref-jumper-2021-alphafold"></span> **John Jumper, Richard Evans, Alexander Pritzel et al.** Highly accurate protein structure prediction with AlphaFold. Nature 596, 583-589 (2021). [https://doi.org/10.1038/s41586-021-03819-2](https://doi.org/10.1038/s41586-021-03819-2)
273
-
274
- - <span id="ref-devlin-2018-bert"></span> **Jacob Devlin, Ming-Wei Chang, Kenton Lee, Kristina Toutanova.** BERT: Pre-training of Deep Bidirectional Transformers for Language Understanding. CoRR abs/1810.04805 (2018). [http://arxiv.org/abs/1810.04805](http://arxiv.org/abs/1810.04805)
275
-
276
- - <span id="ref-atlas-2023-higg"></span> **ATLAS Collaboration.** Measurement of the properties of Higgs boson production at \\(\sqrt{s} = 13\,\text{TeV}\\) in the \\(H \to \gamma\gamma\\) channel using \\(139\,\text{fb}^{-1}\\) of \\(pp\\) collision data with the ATLAS experiment. JHEP 07 (2023) 088. [arXiv:2207.00348](https://arxiv.org/abs/2207.00348), [https://doi.org/10.1007/JHEP07(2023)088](https://doi.org/10.1007/JHEP07(2023)088)
277
-
278
- - <span id="ref-atlas-2023-4top"></span> **ATLAS Collaboration.** Observation of four-top-quark production in the multilepton final state with the ATLAS detector. Eur. Phys. J. C 83 (2023) 496. [arXiv:2303.15061](https://arxiv.org/abs/2303.15061), [https://doi.org/10.1140/epjc/s10052-023-11573-0](https://doi.org/10.1140/epjc/s10052-023-11573-0)
279
-
280
- - <span id="ref-kornblith-2019-cka"></span> **Simon Kornblith, Mohammad Norouzi, Honglak Lee, Geoffrey Hinton.** Similarity of Neural Network Representations Revisited. CoRR abs/1905.00414 (2019). [http://arxiv.org/abs/1905.00414](http://arxiv.org/abs/1905.00414)
281
-
282
- ---
283
-
284
- <!-- Historical/General Physics foundational texts -->
285
-
286
- - <span id="ref-birell-1982-qfields"></span> **N. D. Birell, P. C. W. Davies.** Quantum Fields in Curved Space. Cambridge Univ. Press (1982).
287
-
288
- - <span id="ref-feynman-1954"></span> **R. P. Feynman.** Phys. Rev. 94, 262 (1954).
289
-
290
- - <span id="ref-einstein-1935-epr"></span> **A. Einstein, Yu. Podolsky, N. Rosen.** Phys. Rev. 47, 777 (1935).
291
-
292
- - <span id="ref-berman-1983-stability"></span> **G. P. Berman, Jr., F. M. Izrailev, Jr.** Stability of nonlinear modes. Physica D 88, 445 (1983).
293
-
294
- - <span id="ref-davies-1988-trapped"></span> **E. B. Davies, L. Parns.** Trapped modes in acoustic waveguides. Q. J. Mech. Appl. Math. 51, 477–492 (1988).
295
-
296
- - <span id="ref-witten-2001"></span> **Edward Witten.** hep-th/0106109 (2001). [https://arxiv.org/abs/hep-th/0106109](https://arxiv.org/abs/hep-th/0106109)
297
-
298
- ---
299
-
300
- <!-- Particle physics/data science foundational models -->
301
-
302
- - <span id="ref-beutler-1994-hem"></span> **E. Beutler.** Williams Hematology, 5th Edition, Chapter 7, pp. 654–662. McGraw-Hill, New York (1994).
303
-
304
- - <span id="ref-knuth-1973-fa"></span> **Donald E. Knuth.** The Art of Computer Programming vol. 1: Fundamental Algorithms, 2nd Ed., Addison-Wesley (1973).
305
-
306
- - <span id="ref-smith-2005-philos"></span> **J. S. Smith, G. W. Johnson.** Philos. Trans. R. Soc. London, Ser. B 777, 1395 (2005).
307
-
308
- - <span id="ref-smith-2010-jap-unpub"></span> **W. J. Smith, T. J. Johnson, B. G. Miller.** Surface chemistry and preferential crystal orientation on a silicon surface. J. Appl. Phys. (unpublished, 2010).
309
-
310
- - <span id="ref-smith-2010-jap-sub"></span> **V. K. Smith, K. Johnson, M. O. Klein.** Surface chemistry and preferential crystal orientation on a silicon surface. J. Appl. Phys. (submitted, 2010).
311
-
312
- - <span id="ref-underwood-1988-lowerbounds"></span> **Ulrich Underwood, Ned Net, Paul Pot.** Lower Bounds for Wishful Research Results. Talk at Fanstord University (1988).
313
-
314
- - <span id="ref-johnson-2007-comm"></span> **M. P. Johnson, K. L. Miller, K. Smith.** Personal communication (Jan-May 2007).
315
-
316
- ---
317
-
318
- <!-- Prototypical collider software and tools -->
319
-
320
- - <span id="ref-pytorch-2019"></span> **Adam Paszke et al.** PyTorch: An Imperative Style, High-Performance Deep Learning Library. arXiv:1912.01703 (2019). [http://arxiv.org/abs/1912.01703](http://arxiv.org/abs/1912.01703)
321
-
322
- - <span id="ref-dgl-2019"></span> **Minjie Wang et al.** Deep Graph Library: Towards Efficient and Scalable Deep Learning on Graphs. arXiv:1909.01315 (2019). [http://arxiv.org/abs/1909.01315](http://arxiv.org/abs/1909.01315)
323
-
324
- - <span id="ref-graphnets-2018"></span> **Peter W. Battaglia et al.** Relational inductive biases, deep learning, and graph networks. arXiv:1806.01261 (2018). [http://arxiv.org/abs/1806.01261](http://arxiv.org/abs/1806.01261)
325
-
326
- - <span id="ref-layernorm-2016"></span> **Jimmy Lei Ba, Jamie Ryan Kiros, Geoffrey E. Hinton.** Layer Normalization. arXiv:1607.06450 (2016). [https://arxiv.org/abs/1607.06450](https://arxiv.org/abs/1607.06450)
327
-
328
- ---
329
-
330
- <!-- Recent & foundation models in HEP ML -->
331
-
332
- - <span id="ref-wildridge-2024-bumblebee"></span> **Andrew J. Wildridge et al.** Bumblebee: Foundation Model for Particle Physics Discovery. arXiv:2412.07867 (2024). [https://arxiv.org/abs/2412.07867](https://arxiv.org/abs/2412.07867)
333
-
334
- - <span id="ref-katel-2024-jet"></span> **Subash Katel et al.** Learning Symmetry-Independent Jet Representations via Jet-Based Joint Embedding Predictive Architecture. arXiv:2412.05333 (2024). [https://arxiv.org/abs/2412.05333](https://arxiv.org/abs/2412.05333)
335
-
336
- - <span id="ref-araz-2024-pointcloud"></span> **Jack Y. Araz et al.** Point cloud-based diffusion models for the Electron-Ion Collider. arXiv:2410.22421 (2024). [https://arxiv.org/abs/2410.22421](https://arxiv.org/abs/2410.22421)
337
-
338
- - <span id="ref-leigh-2024-maskedparticle"></span> **Matthew Leigh et al.** Is Tokenization Needed for Masked Particle Modelling? arXiv:2409.12589 (2024). [https://arxiv.org/abs/2409.12589](https://arxiv.org/abs/2409.12589)
339
-
340
- - <span id="ref-mikuni-2024-omnilearn"></span> **Vinicius Mikuni, Benjamin Nachman.** OmniLearn: A Method to Simultaneously Facilitate All Jet Physics Tasks. arXiv:2404.16091 (2024). [https://arxiv.org/abs/2404.16091](https://arxiv.org/abs/2404.16091)
341
-
342
- - <span id="ref-zhang-2024-xiwu"></span> **Zhengde Zhang et al.** Xiwu: A Basis Flexible and Learnable LLM for High Energy Physics. arXiv:2404.08001 (2024). [https://arxiv.org/abs/2404.08001](https://arxiv.org/abs/2404.08001)
343
-
344
- - <span id="ref-harris-2024-resimulation"></span> **Philip Harris et al.** Re-Simulation-based Self-Supervised Learning for Pre-Training Foundation Models. arXiv:2403.07066 (2024). [https://arxiv.org/abs/2403.07066](https://arxiv.org/abs/2403.07066)
345
-
346
- - <span id="ref-birk-2024-omnijet"></span> **Joschka Birk, Anna Hallin, Gregor Kasieczka.** OmniJet-$\alpha$: the first cross-task foundation model for particle physics. Machine Learning: Science and Technology. 5(3), 035031 (Aug 2024). [https://doi.org/10.1088/2632-2153/ad66ad](https://doi.org/10.1088/2632-2153/ad66ad)
347
-
348
- - <span id="ref-huang-2024-lmtracking"></span> **Andris Huang et al.** A Language Model for Particle Tracking. arXiv:2402.10239 (2024). [https://arxiv.org/abs/2402.10239](https://arxiv.org/abs/2402.10239)
349
-
350
- - <span id="ref-golling-2024-maskedset"></span> **Tobias Golling et al.** Masked Particle Modeling on Sets: Towards Self-Supervised High Energy Physics Foundation Models. arXiv:2401.13537 (2024). [https://arxiv.org/abs/2401.13537](https://arxiv.org/abs/2401.13537)
351
-
352
- - <span id="ref-liu-2023-gaam"></span> **Junze Liu et al.** Generalizing to new geometries with Geometry-Aware Autoregressive Models (GAAMs) for fast calorimeter simulation. Journal of Instrumentation 18(11), P11003 (Nov 2023). [https://doi.org/10.1088/1748-0221/18/11/p11003](https://doi.org/10.1088/1748-0221/18/11/p11003)
353
-
354
- - <span id="ref-hashemi-2024-gen"></span> **Baran Hashemi et al.** Ultra-high-granularity detector simulation with intra-event aware generative adversarial network and self-supervised relational reasoning. Nature Communications 15(1) (June 2024). [https://doi.org/10.1038/s41467-024-49104-4](https://doi.org/10.1038/s41467-024-49104-4)
355
-
356
- - <span id="ref-vigl-2024-finetune"></span> **Matthias Vigl et al.** Finetuning Foundation Models for Joint Analysis Optimization. arXiv:2401.13536 (2024). [https://arxiv.org/abs/2401.13536](https://arxiv.org/abs/2401.13536)
357
-
358
- - <span id="ref-li-2024-refine"></span> **Chen Li, Hao Cai, Xianyang Jiang.** Refine neutrino events reconstruction with BEiT-3. Journal of Instrumentation 19(6), T06003 (Jun 2024). [https://doi.org/10.1088/1748-0221/19/06/t06003](https://doi.org/10.1088/1748-0221/19/06/t06003)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/configs/config.yaml DELETED
@@ -1,64 +0,0 @@
1
- # ignore_header_test
2
- # Copyright 2023 Stanford University
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- random_seed: 2
17
-
18
- scheduler:
19
- lr: 1.E-3
20
- lr_decay: 1.E-3
21
-
22
- training:
23
- epochs: 100
24
-
25
- checkpoints:
26
- ckpt_path: "checkpoints"
27
- ckpt_name: "config"
28
-
29
- performance:
30
- amp: False
31
- jit: False
32
-
33
- architecture:
34
- processor_size: 8
35
- hidden_dim_node_encoder: 128
36
- hidden_dim_edge_encoder: 128
37
- hidden_dim_processor: 128
38
- hidden_dim_node_decoder: 128
39
- out_dim: 1
40
-
41
- paths:
42
- data_dir: /global/cfs/projectdirs/atlas/joshua/hackathon_data/stats_100K
43
- save_dir: /pscratch/sd/j/joshuaho/physicsnemo/graphs/stats_100K
44
- training_dir: ./training_stats_100K/
45
-
46
- datasets:
47
- - name: ttH_cp_even
48
- load_path: ${paths.data_dir}/ttH_NLO.root
49
- label: 0
50
- - name: ttH_cp_odd
51
- load_path: ${paths.data_dir}/ttH_CPodd.root
52
- label: 1
53
-
54
- root_dataset:
55
- ttree: output
56
- type: torch.bfloat16
57
- particles: ["jet", "ele", "mu", "ph", "MET"]
58
- features: ["pt", "eta", "phi", "energy", "btag", "charge", "node_type"]
59
- globals: []
60
- weights: ""
61
- tracking: []
62
- step_size: 8192
63
- batch_size: 8192
64
- train_val_test_split: [0.75, 0.24, 0.01]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/configs/config_stats_all.yaml DELETED
@@ -1,65 +0,0 @@
1
- # ignore_header_test
2
- # Copyright 2023 Stanford University
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- random_seed: 2
17
-
18
- scheduler:
19
- lr: 1.E-4
20
- lr_decay: 1.E-3
21
-
22
- training:
23
- epochs: 100
24
-
25
- checkpoints:
26
- ckpt_path: "checkpoints"
27
- ckpt_name: "config_stats_all"
28
-
29
- performance:
30
- amp: False
31
- jit: False
32
-
33
- architecture:
34
- processor_size: 5
35
- hidden_dim_node_encoder: 64
36
- hidden_dim_edge_encoder: 64
37
- hidden_dim_processor: 64
38
- hidden_dim_node_decoder: 64
39
- out_dim: 1
40
-
41
- paths:
42
- data_dir: /global/cfs/projectdirs/atlas/joshua/hackathon_data/stats_all
43
- save_dir: /pscratch/sd/j/joshuaho/physicsnemo/graphs/stats_all
44
- training_dir: ./training_stats_all/
45
-
46
- datasets:
47
- - name: ttH_cp_even
48
- load_path: ${paths.data_dir}/ttH_NLO.root
49
- label: 0
50
- - name: ttH_cp_odd
51
- load_path: ${paths.data_dir}/ttH_CPodd.root
52
- label: 1
53
-
54
- root_dataset:
55
- ttree: output
56
- type: torch.bfloat16
57
- particles: ["jet", "ele", "mu", "ph", "MET"]
58
- features: ["pt", "eta", "phi", "energy", "btag", "charge", "node_type"]
59
- globals: []
60
- weights: ""
61
- tracking: []
62
- step_size: 81920
63
- batch_size: 8192
64
- train_val_test_split: [0.75, 0.24, 0.01]
65
- prebatch: True
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/configs/tHjb_CP_0_vs_45.yaml DELETED
@@ -1,79 +0,0 @@
1
- # ignore_header_test
2
- # Copyright 2023 Stanford University
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- random_seed: 2
17
-
18
- scheduler:
19
- lr: 1.E-3
20
- lr_decay: 1.E-3
21
-
22
- training:
23
- epochs: 100
24
-
25
- checkpoints:
26
- ckpt_path: "checkpoints"
27
- ckpt_name: "config"
28
-
29
- performance:
30
- amp: False
31
- jit: False
32
-
33
- architecture:
34
- processor_size: 8
35
- hidden_dim_node_encoder: 128
36
- hidden_dim_edge_encoder: 128
37
- hidden_dim_processor: 128
38
- hidden_dim_node_decoder: 128
39
- global_emb_dim: 128
40
- out_dim: 1
41
-
42
- paths:
43
- data_dir: /global/cfs/projectdirs/atlas/joshua/ttHCP/ntuples/v02/preselection/merged_fixed/train/
44
- save_dir: /pscratch/sd/j/joshuaho/physicsnemo/ttHCP/graphs/tHjb_CP_0_vs_45/
45
- training_dir: ./training_tHjb_CP_0_vs_45/
46
-
47
- datasets:
48
- - name: tHjb_cp_0_had
49
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_had_scaled.root
50
- label: 0
51
- - name: tHjb_cp_0_lep
52
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_lep_scaled.root
53
- label: 0
54
- - name: tHjb_cp_45_had
55
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_45_AF3_had_scaled.root
56
- label: 1
57
- - name: tHjb_cp_45_lep
58
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_45_AF3_lep_scaled.root
59
- label: 1
60
-
61
- root_dataset:
62
- ttree: output
63
- dtype: torch.bfloat16
64
- features:
65
- # pt, eta, phi, energy, btag, charge, node_type
66
- jet: [m_jet_pt, m_jet_eta, m_jet_phi, CALC_E, m_jet_PCbtag, 0, 0]
67
- electron: [m_el_pt, m_el_eta, m_el_phi, CALC_E, 0, m_el_charge, 1]
68
- muon: [m_mu_pt, m_mu_eta, m_mu_phi, CALC_E, 0, m_mu_charge, 2]
69
- photon: [ph_pt_myy, ph_eta, ph_phi, CALC_E, 0, 0, 3]
70
- met: [m_met, 0, m_met_phi, CALC_E, 0, 0, 4]
71
- globals: [NUM_NODES]
72
- weights: m_weightXlumi
73
- tracking: []
74
- step_size: 16384
75
- batch_size: 16384
76
- train_val_test_split: [0.5, 0.25, 0.25]
77
- prebatch:
78
- enabled: True
79
- chunk_size: 512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/configs/tHjb_CP_0_vs_90.yaml DELETED
@@ -1,87 +0,0 @@
1
- # ignore_header_test
2
- # Copyright 2023 Stanford University
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- random_seed: 2
17
-
18
- scheduler:
19
- lr: 1.E-3
20
- lr_decay: 1.E-3
21
-
22
- training:
23
- epochs: 100
24
-
25
- checkpoints:
26
- ckpt_path: "checkpoints"
27
- ckpt_name: "tHjb_CP_0_vs_90"
28
-
29
- performance:
30
- amp: False
31
- jit: False
32
-
33
- architecture:
34
- module: models.MeshGraphNet
35
- class: MeshGraphNet
36
- args:
37
- base_gnn:
38
- input_dim_nodes: 7
39
- input_dim_edges: 3
40
- output_dim: 128
41
- processor_size: 8
42
- hidden_dim_node_encoder: 128
43
- hidden_dim_edge_encoder: 128
44
- hidden_dim_processor: 128
45
- hidden_dim_node_decoder: 128
46
- global_emb_dim: 128
47
- global_feat_dim: 1
48
- out_dim: 1
49
-
50
- paths:
51
- data_dir: /global/cfs/projectdirs/atlas/joshua/ttHCP/ntuples/v02/preselection/merged_fixed/train/
52
- save_dir: /pscratch/sd/j/joshuaho/physicsnemo/ttHCP/graphs/tHjb_CP_0_vs_90/
53
- training_dir: ./tHjb_CP_0_vs_90/
54
-
55
- datasets:
56
- - name: tHjb_cp_0_had
57
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_had_scaled.root
58
- label: 0
59
- - name: tHjb_cp_0_lep
60
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_lep_scaled.root
61
- label: 0
62
- - name: tHjb_cp_90_had
63
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_90_AF3_had_scaled.root
64
- label: 1
65
- - name: tHjb_cp_90_lep
66
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_90_AF3_lep_scaled.root
67
- label: 1
68
-
69
- root_dataset:
70
- ttree: output
71
- dtype: torch.bfloat16
72
- features:
73
- # pt, eta, phi, energy, btag, charge, node_type
74
- jet: [m_jet_pt, m_jet_eta, m_jet_phi, CALC_E, m_jet_PCbtag, 0, 0]
75
- electron: [m_el_pt, m_el_eta, m_el_phi, CALC_E, 0, m_el_charge, 1]
76
- muon: [m_mu_pt, m_mu_eta, m_mu_phi, CALC_E, 0, m_mu_charge, 2]
77
- photon: [ph_pt_myy, ph_eta, ph_phi, CALC_E, 0, 0, 3]
78
- met: [m_met, 0, m_met_phi, CALC_E, 0, 0, 4]
79
- globals: [NUM_NODES]
80
- weights: 1
81
- tracking: []
82
- step_size: 16384
83
- batch_size: 16384
84
- train_val_test_split: [0.5, 0.25, 0.25]
85
- prebatch:
86
- enabled: True
87
- chunk_size: 512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/configs/tHjb_CP_0_vs_90_edge_network.yaml DELETED
@@ -1,82 +0,0 @@
1
- # ignore_header_test
2
- # Copyright 2023 Stanford University
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- random_seed: 2
17
-
18
- scheduler:
19
- lr: 1.E-3
20
- lr_decay: 1.E-3
21
-
22
- training:
23
- epochs: 100
24
-
25
- checkpoints:
26
- ckpt_path: "checkpoints"
27
- ckpt_name: "tHjb_CP_0_vs_90_edge_network"
28
-
29
- performance:
30
- amp: False
31
- jit: False
32
-
33
- architecture:
34
- module: models.Edge_Network
35
- class: Edge_Network
36
- args:
37
- input_dim_nodes: 7
38
- input_dim_edges: 3
39
- input_dim_globals: 1
40
- hid_size: 64
41
- n_layers: 4
42
- n_proc_steps: 4
43
- out_dim: 1
44
-
45
- paths:
46
- data_dir: /global/cfs/projectdirs/atlas/joshua/ttHCP/ntuples/v02/preselection/merged_fixed/train/
47
- save_dir: /pscratch/sd/j/joshuaho/physicsnemo/ttHCP/graphs/tHjb_CP_0_vs_90/
48
- training_dir: ./tHjb_CP_0_vs_90_edge_network/
49
-
50
- datasets:
51
- - name: tHjb_cp_0_had
52
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_had_scaled.root
53
- label: 0
54
- - name: tHjb_cp_0_lep
55
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_lep_scaled.root
56
- label: 0
57
- - name: tHjb_cp_90_had
58
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_90_AF3_had_scaled.root
59
- label: 1
60
- - name: tHjb_cp_90_lep
61
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_90_AF3_lep_scaled.root
62
- label: 1
63
-
64
- root_dataset:
65
- ttree: output
66
- dtype: torch.bfloat16
67
- features:
68
- # pt, eta, phi, energy, btag, charge, node_type
69
- jet: [m_jet_pt, m_jet_eta, m_jet_phi, CALC_E, m_jet_PCbtag, 0, 0]
70
- electron: [m_el_pt, m_el_eta, m_el_phi, CALC_E, 0, m_el_charge, 1]
71
- muon: [m_mu_pt, m_mu_eta, m_mu_phi, CALC_E, 0, m_mu_charge, 2]
72
- photon: [ph_pt_myy, ph_eta, ph_phi, CALC_E, 0, 0, 3]
73
- met: [m_met, 0, m_met_phi, CALC_E, 0, 0, 4]
74
- globals: [NUM_NODES]
75
- weights: 1
76
- tracking: []
77
- step_size: 16384
78
- batch_size: 16384
79
- train_val_test_split: [0.5, 0.25, 0.25]
80
- prebatch:
81
- enabled: True
82
- chunk_size: 512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/configs/tHjb_CP_0_vs_90_globals.yaml DELETED
@@ -1,84 +0,0 @@
1
- # ignore_header_test
2
- # Copyright 2023 Stanford University
3
- #
4
- # Licensed under the Apache License, Version 2.0 (the "License");
5
- # you may not use this file except in compliance with the License.
6
- # You may obtain a copy of the License at
7
- #
8
- # http://www.apache.org/licenses/LICENSE-2.0
9
- #
10
- # Unless required by applicable law or agreed to in writing, software
11
- # distributed under the License is distributed on an "AS IS" BASIS,
12
- # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13
- # See the License for the specific language governing permissions and
14
- # limitations under the License.
15
-
16
- random_seed: 2
17
-
18
- scheduler:
19
- lr: 1.E-3
20
- lr_decay: 1.E-3
21
-
22
- training:
23
- epochs: 100
24
-
25
- checkpoints:
26
- ckpt_path: "checkpoints"
27
- ckpt_name: "tHjb_CP_0_vs_90_globals"
28
-
29
- performance:
30
- amp: False
31
- jit: False
32
-
33
- architecture:
34
- base_gnn:
35
- input_dim_nodes: 7
36
- input_dim_edges: 3
37
- output_dim: 128
38
- processor_size: 8
39
- hidden_dim_node_encoder: 128
40
- hidden_dim_edge_encoder: 128
41
- hidden_dim_processor: 128
42
- hidden_dim_node_decoder: 128
43
- global_emb_dim: 128
44
- global_feat_dim: 5
45
- out_dim: 1
46
-
47
- paths:
48
- data_dir: /global/cfs/projectdirs/atlas/joshua/ttHCP/ntuples/v02/preselection/merged_fixed/train/
49
- save_dir: /pscratch/sd/j/joshuaho/physicsnemo/ttHCP/graphs/tHjb_CP_0_vs_90_globals/
50
- training_dir: ./tHjb_CP_0_vs_90_globals/
51
-
52
- datasets:
53
- - name: tHjb_cp_0_had
54
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_had_scaled.root
55
- label: 0
56
- - name: tHjb_cp_0_lep
57
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_0_AF3_lep_scaled.root
58
- label: 0
59
- - name: tHjb_cp_90_had
60
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_90_AF3_had_scaled.root
61
- label: 1
62
- - name: tHjb_cp_90_lep
63
- load_path: ${paths.data_dir}/merged_aMCPy8_tHjb125_CP_90_AF3_lep_scaled.root
64
- label: 1
65
-
66
- root_dataset:
67
- ttree: output
68
- dtype: torch.bfloat16
69
- features:
70
- # pt, eta, phi, energy, btag, charge, node_type
71
- jet: [m_jet_pt, m_jet_eta, m_jet_phi, CALC_E, m_jet_PCbtag, 0, 0]
72
- electron: [m_el_pt, m_el_eta, m_el_phi, CALC_E, 0, m_el_charge, 1]
73
- muon: [m_mu_pt, m_mu_eta, m_mu_phi, CALC_E, 0, m_mu_charge, 2]
74
- photon: [ph_pt_myy, ph_eta, ph_phi, CALC_E, 0, 0, 3]
75
- met: [m_met, 0, m_met_phi, CALC_E, 0, 0, 4]
76
- globals: [NUM_NODES, eta_H, pt_H, eta_recotop1, pT_recotop1]
77
- weights: 1
78
- tracking: []
79
- step_size: 16384
80
- batch_size: 16384
81
- train_val_test_split: [0.5, 0.25, 0.25]
82
- prebatch:
83
- enabled: True
84
- chunk_size: 512
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/dataset/Dataset.py DELETED
@@ -1,243 +0,0 @@
1
- import os
2
- import uproot
3
- import dgl
4
- import torch
5
- import numpy as np
6
- from omegaconf import DictConfig
7
- from typing import List
8
- from concurrent.futures import ProcessPoolExecutor, as_completed
9
- from tqdm import tqdm
10
-
11
- from dataset import GraphBuilder
12
- from dataset import Graphs
13
- from dataset import Normalization
14
-
15
- from dgl.dataloading import GraphDataLoader
16
-
17
- class Dataset:
18
- def __init__(
19
- self,
20
- name: str,
21
- label: int,
22
- load_path: str,
23
- save_path: str,
24
- dtype: torch.dtype,
25
- device: str,
26
- cfg: DictConfig
27
- ):
28
- self.name = name
29
- self.label = label
30
- self.load_path = load_path
31
- self.save_path = save_path
32
- self.dtype = dtype
33
- self.data = None
34
- self.device = device
35
-
36
- self.ttree = cfg.ttree
37
- self.features = cfg.features
38
- self.weights = cfg.weights
39
- self.globals = cfg.globals
40
- self.tracking = cfg.tracking
41
- self.step_size = cfg.step_size
42
- self.batch_size = cfg.batch_size
43
-
44
- self.prebatch = cfg.get('prebatch', {'enabled': False})
45
-
46
- self.train_val_test_split = cfg.train_val_test_split
47
- assert np.sum(self.train_val_test_split) == 1, "train_val_test_split must sum to 1"
48
-
49
- print(f"initializing dataset {name} with dtype {self.dtype}")
50
-
51
- def get_branches(self) -> List[str]:
52
- node_branches = [
53
- branches
54
- for particle in self.features.values()
55
- for branches in particle
56
- if isinstance(branches, str) and (branches != "CALC_E" or branches != "NUM_NODES")
57
- ]
58
- global_branches = [x for x in self.globals if isinstance(x, str)]
59
- weight_branch = [self.weights] if isinstance(self.weights, str) else []
60
- tracking_branches = [x for x in self.tracking if isinstance(x, str)]
61
- label_branch = [self.label] if isinstance(self.label, str) else []
62
-
63
- return node_branches + global_branches + weight_branch + tracking_branches + label_branch
64
-
65
- def process(self):
66
- branches = self.get_branches()
67
- with uproot.open(f"{self.load_path}:{self.ttree}") as tree:
68
- available_branches = set(tree.keys())
69
- num_entries = tree.num_entries
70
-
71
- print(f"getting branches: {branches}")
72
-
73
- num_cpus = os.cpu_count()
74
- total_chunks = np.ceil(num_entries / self.step_size)
75
-
76
- with ProcessPoolExecutor(max_workers=num_cpus) as executor:
77
- futures = []
78
-
79
- with tqdm(
80
- uproot.iterate(
81
- f"{self.load_path}:{self.ttree}",
82
- expressions=[b for b in branches if b in available_branches],
83
- step_size=self.step_size,
84
- library="ak"
85
- ),
86
- desc="loading root file",
87
- total=total_chunks,
88
- position=0,
89
- leave=True
90
- ) as pbar:
91
-
92
- for chunk_id, arrays in enumerate(pbar):
93
-
94
- cfg = GraphBuilder.ChunkConfig(
95
- name=self.name,
96
- label=self.label,
97
- chunk_id=chunk_id,
98
- batch_size=self.batch_size,
99
- arrays=arrays,
100
- features=self.features,
101
- globals=self.globals,
102
- tracking=self.tracking,
103
- weights=self.weights,
104
- branches=branches,
105
- dtype=self.dtype,
106
- save_path=self.save_path,
107
- prebatch = self.prebatch,
108
- )
109
-
110
- futures.append(executor.submit(GraphBuilder.process_chunk, cfg))
111
-
112
- for idx, future in enumerate(as_completed(futures)):
113
- try:
114
- future.result()
115
- except Exception as e:
116
- import traceback
117
- print(f"exception in chunk: {idx}")
118
- traceback.print_exception(type(e), e, e.__traceback__)
119
- return
120
-
121
- def load(self):
122
- with uproot.open(f"{self.load_path}:{self.ttree}") as tree:
123
- num_entries = tree.num_entries
124
- total_chunks = int(np.ceil(num_entries / self.step_size))
125
-
126
- chunk_files = [f"{self.save_path}/{self.name}_{chunk_id:04d}.bin" for chunk_id in range(total_chunks)]
127
- if not all(os.path.exists(f) for f in chunk_files):
128
- print("graphs not found. processing root file...")
129
- self.process()
130
-
131
- graph_tuple_list = []
132
-
133
- for chunk_id, f in enumerate(chunk_files):
134
- if chunk_id < total_chunks - 1:
135
- if (self.prebatch.enabled):
136
- n_graphs = self.step_size // self.prebatch.chunk_size
137
- else:
138
- n_graphs = self.step_size
139
- else:
140
- if (self.prebatch.enabled):
141
- n_graphs = (num_entries - self.step_size * (total_chunks - 1)) // self.prebatch.chunk_size + 1
142
- else:
143
- n_graphs = num_entries - self.step_size * (total_chunks - 1)
144
- graph_tuple_list.extend((f, idx) for idx in range(n_graphs))
145
-
146
- split = self.train_val_test_split
147
- n_total = len(graph_tuple_list)
148
- n_train = int(split[0] * n_total)
149
- n_val = int(split[1] * n_total)
150
-
151
- train_tuples = graph_tuple_list[:n_train]
152
- val_tuples = graph_tuple_list[n_train:n_train + n_val]
153
- test_tuples = graph_tuple_list[n_train + n_val:]
154
- return train_tuples, val_tuples, test_tuples
155
-
156
- class GraphTupleDataset:
157
- def __init__(self, tuple_list, stats):
158
- self.tuple_list = tuple_list
159
- self.stats = stats
160
- self.cache = {}
161
-
162
- def __len__(self):
163
- return len(self.tuple_list)
164
-
165
- def __getitem__(self, idx):
166
- f, graph_idx = self.tuple_list[idx]
167
- if f in self.cache:
168
- g = self.cache[f]
169
- else:
170
- g = Graphs.load_graphs(f)
171
- g.normalize(self.stats)
172
- self.cache[f] = g
173
- return g[graph_idx]
174
-
175
- @staticmethod
176
- def collate_fn(samples):
177
- all_graphs = []
178
- all_metadata = {}
179
-
180
- # Initialize keys in all_metadata from the first sample
181
- for k in samples[0][1]:
182
- all_metadata[k] = []
183
-
184
- for graph, metadata in samples:
185
- all_graphs.append(graph)
186
- for k, v in metadata.items():
187
- all_metadata[k].append(v)
188
-
189
- # Stack or concatenate metadata for each key
190
- for k in all_metadata:
191
- # If v is a tensor, stack or cat as appropriate
192
- # Use torch.cat if v is already [N, ...] (e.g. labels, features)
193
- # Use torch.stack if v is scalar or needs new dimension
194
- try:
195
- all_metadata[k] = torch.cat(all_metadata[k], dim=0)
196
- except Exception:
197
- all_metadata[k] = torch.stack(all_metadata[k], dim=0)
198
-
199
- batched_graph = dgl.batch(all_graphs)
200
- return batched_graph, all_metadata
201
-
202
- def get_dataset(cfg: DictConfig, device):
203
-
204
- all_train = []
205
- all_val = []
206
- all_test = []
207
-
208
- dtype_str = getattr(cfg.root_dataset, "dtype", "torch.float32")
209
- if isinstance(dtype_str, str) and dtype_str.startswith("torch."):
210
- dtype = getattr(torch, dtype_str.split(".")[-1], torch.float32)
211
- else:
212
- dtype = torch.float32
213
-
214
- for ds in cfg.datasets:
215
- name = ds['name']
216
- load_path = ds.get('load_path', f"{cfg.paths.data_dir}/{name}.root")
217
- save_path = ds.get('save_path', f"{cfg.paths.save_dir}/")
218
- datastet = Dataset(name, ds.get('label'), load_path, save_path, dtype, device, cfg.root_dataset)
219
- train, val, test = datastet.load()
220
- all_train.extend(train)
221
- all_val.extend(val)
222
- all_test.extend(test)
223
-
224
- stats = Normalization.global_stats(f"{cfg.paths.save_dir}/stats/", dtype=dtype)
225
-
226
- train_dataset = GraphTupleDataset(all_train, stats)
227
- val_dataset = GraphTupleDataset(all_val, stats)
228
- test_dataset = GraphTupleDataset(all_test, stats)
229
-
230
- if (cfg.root_dataset.get('prebatch', False)):
231
- batch_size = cfg.root_dataset.batch_size // cfg.root_dataset.prebatch.chunk_size
232
- collate_fn = GraphTupleDataset.collate_fn
233
- else:
234
- batch_size = cfg.root_dataset.batch_size
235
- collate_fn = None
236
-
237
- train_loader = GraphDataLoader(train_dataset, batch_size=batch_size, shuffle=True, pin_memory=True, num_workers=5, drop_last=False, collate_fn=collate_fn)
238
- val_loader = GraphDataLoader(val_dataset, batch_size=batch_size, shuffle=False, pin_memory=True, num_workers=5, drop_last=False, collate_fn=collate_fn)
239
- test_loader = GraphDataLoader(test_dataset, batch_size=batch_size, shuffle=False, pin_memory=True, num_workers=0, drop_last=False, collate_fn=collate_fn)
240
-
241
- print("all data loaded successfully")
242
- print(f"train: {len(train_dataset)}, val: {len(val_dataset)}, test: {len(test_dataset)}")
243
- return train_loader, val_loader, test_loader
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/dataset/GraphBuilder.py DELETED
@@ -1,162 +0,0 @@
1
- import dgl
2
- import torch
3
- import numpy as np
4
- import awkward as ak
5
- from dataclasses import dataclass
6
- from typing import List, Any, Union
7
-
8
- from dataset.Graphs import Graphs, save_graphs
9
- from dataset import Normalization
10
-
11
- @dataclass
12
- class ChunkConfig:
13
- name: str
14
- label: Union[str, int]
15
- chunk_id: int
16
- batch_size: int
17
- arrays: List[Any]
18
- features: List[Any]
19
- globals: List[Any]
20
- weights: Union[str, float]
21
- tracking: List[Any]
22
- branches: List[Any]
23
- dtype: torch.dtype
24
- save_path: str
25
- prebatch: dict
26
-
27
- def process_chunk(cfg: ChunkConfig):
28
- # Collect everything as lists first
29
- graph_list = []
30
- meta_dict = {
31
- 'globals': [],
32
- 'label': [],
33
- 'weight': [],
34
- 'tracking': [],
35
- 'batch_num_nodes': [],
36
- 'batch_num_edges': [],
37
- }
38
-
39
- for i in range(len(cfg.arrays)):
40
- g, meta = process_single_entry(cfg, i)
41
- graph_list.append(g)
42
- for k in meta_dict:
43
- meta_dict[k].append(meta[k])
44
-
45
- # Stack all metadata fields into tensors
46
- for k in meta_dict:
47
- meta_dict[k] = torch.stack(meta_dict[k])
48
-
49
- graphs = Graphs(graphs=graph_list, metadata=meta_dict)
50
- Normalization.save_stats(graphs, f"{cfg.save_path}/stats/{cfg.name}_{cfg.chunk_id:04d}.json")
51
-
52
- if getattr(cfg.prebatch, "enabled", False):
53
- graphs.shuffle()
54
- graphs.batch(cfg.prebatch["chunk_size"])
55
-
56
- save_graphs(graphs, f"{cfg.save_path}/{cfg.name}_{cfg.chunk_id:04d}.bin")
57
-
58
- def process_single_entry(cfg, i):
59
- # 1) node features
60
- node_features: List[torch.Tensor] = []
61
-
62
- for particle, branch_list in cfg.features.items():
63
- feature_tensors: List[torch.Tensor] = []
64
- for branch in branch_list:
65
- if branch == "CALC_E":
66
- pT = feature_tensors[0]
67
- eta = feature_tensors[1]
68
- val = pT * torch.cosh(eta)
69
- elif isinstance(branch, str):
70
- arr = cfg.arrays[branch][i]
71
- val = torch.from_numpy(ak.to_numpy(arr)).to(cfg.dtype)
72
- else:
73
- length = feature_tensors[0].shape[0]
74
- val = torch.full((length,), float(branch), dtype=cfg.dtype)
75
- feature_tensors.append(val)
76
-
77
- if feature_tensors and feature_tensors[0].numel() > 0:
78
- block = torch.stack(feature_tensors, dim=1)
79
- node_features.append(block)
80
-
81
- node_features = torch.cat(node_features, dim=0) if node_features else torch.empty((0, len(cfg.features)), dtype=cfg.dtype)
82
-
83
- # 2) global features
84
- global_feat_list: List[torch.Tensor] = []
85
- for b in cfg.globals:
86
- if b == "NUM_NODES":
87
- global_feat_list.append(torch.tensor([len(node_features)], dtype=cfg.dtype))
88
- else:
89
- arr = cfg.arrays[b][i]
90
- global_feat_list.append(torch.from_numpy(ak.to_numpy(arr)).to(cfg.dtype))
91
- global_feat = torch.cat(global_feat_list, dim=0) if global_feat_list else torch.zeros((1,), dtype=cfg.dtype)
92
-
93
- # 3) tracking
94
- tracking_list: List[torch.Tensor] = []
95
- for b in cfg.tracking:
96
- arr = cfg.arrays[b][i]
97
- tracking_list.append(torch.from_numpy(ak.to_numpy(arr)).to(cfg.dtype))
98
- tracking = torch.cat(tracking_list, dim=0) if tracking_list else torch.zeros((1,), dtype=cfg.dtype)
99
-
100
- # 4) weight
101
- weight = float(cfg.arrays[cfg.weights][i]) if isinstance(cfg.weights, str) else cfg.weights
102
- weight = torch.tensor(weight, dtype=cfg.dtype)
103
-
104
- # 5) label
105
- label = float(cfg.arrays[cfg.label][i]) if isinstance(cfg.label, str) else cfg.label
106
- label = torch.tensor(label, dtype=cfg.dtype)
107
-
108
- # 6) make the DGLGraph
109
- g = make_graph(node_features, dtype=cfg.dtype)
110
-
111
- # 7) batch_num_nodes and batch_num_edges
112
- batch_num_nodes = g.batch_num_nodes()
113
- batch_num_edges = g.batch_num_edges()
114
-
115
- meta = {
116
- 'globals': global_feat,
117
- 'label': label,
118
- 'weight': weight,
119
- 'tracking': tracking,
120
- 'batch_num_nodes': batch_num_nodes,
121
- 'batch_num_edges': batch_num_edges,
122
- }
123
- return g, meta
124
-
125
- src_dst_cache = {}
126
- def get_src_dst(num_nodes):
127
- if num_nodes not in src_dst_cache:
128
- src, dst = torch.meshgrid(torch.arange(num_nodes), torch.arange(num_nodes), indexing='ij')
129
- src_dst_cache[num_nodes] = (src.flatten(), dst.flatten())
130
- return src_dst_cache[num_nodes]
131
-
132
- @torch.jit.script
133
- def compute_edge_features(eta, phi, src, dst):
134
- deta = eta[src] - eta[dst]
135
- dphi = phi[src] - phi[dst]
136
- dphi = torch.remainder(dphi + np.pi, 2 * np.pi) - np.pi
137
- dR = torch.sqrt(deta ** 2 + dphi ** 2)
138
- edge_features = torch.stack([dR, deta, dphi], dim=1)
139
- return edge_features
140
-
141
- def make_graph(node_features: torch.tensor, dtype=torch.float32):
142
-
143
- num_nodes = node_features.shape[0]
144
- if num_nodes == 0:
145
- g = dgl.graph(([], []))
146
- g.ndata['features'] = node_features
147
- g.edata['features'] = torch.empty((0, 3), dtype=dtype)
148
- g.globals = torch.tensor([0], dtype=dtype)
149
- return g
150
-
151
- src, dst = get_src_dst(num_nodes)
152
- src = src.flatten()
153
- dst = dst.flatten()
154
- g = dgl.graph((src, dst))
155
- g.ndata['features'] = node_features
156
-
157
- eta = node_features[:, 1]
158
- phi = node_features[:, 2]
159
- edge_features = compute_edge_features(eta, phi, src, dst)
160
- g.edata['features'] = edge_features
161
-
162
- return g
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/dataset/Graphs.py DELETED
@@ -1,88 +0,0 @@
1
- import dgl
2
- import torch
3
- from dataclasses import dataclass, field
4
- from typing import List, Dict
5
-
6
- @dataclass
7
- class Graphs:
8
- graphs: List[dgl.DGLGraph]
9
- metadata: Dict[str, torch.Tensor]
10
-
11
- def __len__(self):
12
- return len(self.graphs)
13
-
14
- def __getitem__(self, idx):
15
- meta = {k: v[idx] for k, v in self.metadata.items()}
16
- return self.graphs[idx], meta
17
-
18
- def shuffle(self):
19
- idx = torch.randperm(len(self.graphs))
20
- self.graphs = [self.graphs[i] for i in idx]
21
- for k in self.metadata:
22
- self.metadata[k] = self.metadata[k][idx]
23
-
24
- def batch(self, batch_size, node_feature_dim=None, dtype=None):
25
- """
26
- In-place batching: after this, self.graphs is a list of batched DGLGraphs,
27
- and self.metadata[k] is a tensor of shape [num_batches, batch_size, ...].
28
- """
29
- batched_graphs = []
30
- batched_meta = {k: [] for k in self.metadata}
31
- N = len(self.graphs)
32
-
33
- # Infer node_feature_dim and dtype if not specified
34
- if node_feature_dim is None and N > 0:
35
- feats = self.graphs[0].ndata['features']
36
- node_feature_dim = feats.shape[1] if feats.ndim > 1 else 1
37
- if dtype is None and N > 0:
38
- dtype = self.graphs[0].ndata['features'].dtype
39
-
40
- for start in range(0, N, batch_size):
41
- end = start + batch_size
42
- batch_graphs = self.graphs[start:end]
43
- batch_meta = {k: v[start:end] for k, v in self.metadata.items()}
44
-
45
- # Padding if needed
46
- pad_count = batch_size - len(batch_graphs)
47
- if pad_count > 0:
48
- dummy_graph = dgl.graph(([], []))
49
- dummy_graph.ndata['features'] = torch.empty((0, node_feature_dim), dtype=dtype)
50
- dummy_graph.edata['features'] = torch.empty((0, 3), dtype=dtype) # assuming 3 edge features
51
- batch_graphs += [dummy_graph] * pad_count
52
-
53
- # Pad metadata with zeros
54
- for k, v in batch_meta.items():
55
- shape = list(v[0].shape) if len(v) > 0 else []
56
- pad_tensor = torch.zeros([pad_count] + shape, dtype=v.dtype, device=v.device)
57
- batch_meta[k] = torch.cat([v, pad_tensor], dim=0)
58
- else:
59
- for k, v in batch_meta.items():
60
- batch_meta[k] = torch.stack(v, dim=0) if isinstance(v, list) else v
61
-
62
- batched_graphs.append(dgl.batch(batch_graphs))
63
- for k in batched_meta:
64
- batched_meta[k].append(batch_meta[k])
65
-
66
- # Now stack along a new axis: [num_batches, batch_size, ...]
67
- for k in batched_meta:
68
- self.metadata[k] = torch.stack(batched_meta[k], dim=0)
69
-
70
- self.graphs = batched_graphs
71
-
72
- def normalize(self, stats):
73
- node_mean, node_std, _ = stats['node']
74
- edge_mean, edge_std, _ = stats['edge']
75
- for g in self.graphs:
76
- g.ndata['features'] = (g.ndata['features'] - node_mean) / node_std
77
- g.edata['features'] = (g.edata['features'] - edge_mean) / edge_std
78
-
79
- def save_graphs(graphs: Graphs, f: str):
80
- meta_to_save = {k: v for k, v in graphs.metadata.items()}
81
- dgl.save_graphs(f, graphs.graphs, meta_to_save)
82
-
83
- def load_graphs(f: str) -> Graphs:
84
- g, meta = dgl.load_graphs(f)
85
- for k in meta:
86
- if not isinstance(meta[k], torch.Tensor):
87
- meta[k] = torch.stack(meta[k])
88
- return Graphs(graphs=g, metadata=meta)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/dataset/Normalization.py DELETED
@@ -1,144 +0,0 @@
1
- import torch
2
- import json
3
- import os
4
- from dataset.Graphs import Graphs
5
- from typing import List, Dict, Tuple
6
-
7
- def combine_feature_stats(chunks: List[Dict]) -> Tuple[torch.Tensor, torch.Tensor, int]:
8
- """
9
- Combine mean/std/count from multiple chunks using Welford's algorithm.
10
- Returns combined mean, std, and total count.
11
- """
12
- n_total = 0
13
- mean_total = None
14
- M2_total = None
15
-
16
- for chunk in chunks:
17
- n_k = chunk['count']
18
- if n_k == 0:
19
- continue
20
-
21
- mean_k = torch.tensor(chunk['mean'])
22
- std_k = torch.tensor(chunk['std'])
23
- M2_k = (std_k ** 2) * n_k
24
-
25
- if n_total == 0:
26
- mean_total = mean_k
27
- M2_total = M2_k
28
- n_total = n_k
29
- else:
30
- delta = mean_k - mean_total
31
- N = n_total + n_k
32
- mean_total += delta * (n_k / N)
33
- M2_total += M2_k + (delta ** 2) * (n_total * n_k / N)
34
- n_total = N
35
-
36
- if n_total == 0:
37
- return torch.tensor([]), torch.tensor([]), 0
38
-
39
- std_total = torch.sqrt(M2_total / n_total)
40
- return mean_total, std_total, n_total
41
-
42
- def global_stats(dirpath: str, dtype: torch.dtype) -> Dict[str, Tuple[torch.Tensor, torch.Tensor, int]]:
43
- """
44
- Load all JSON stats files in a directory, combine node, edge, and global stats,
45
- and optionally save the combined stats as JSON to `save_path`.
46
- """
47
-
48
- combined_stats_path = os.path.join(dirpath, "global_stats.json")
49
-
50
- if not os.path.exists(combined_stats_path):
51
- stats_list = []
52
- for fname in os.listdir(dirpath):
53
- if fname.endswith('.json'):
54
- with open(os.path.join(dirpath, fname), 'r') as f:
55
- stats_list.append(json.load(f))
56
-
57
- node_stats = [s['node'] for s in stats_list]
58
- edge_stats = [s['edge'] for s in stats_list]
59
-
60
- combined = {
61
- 'node': combine_feature_stats(node_stats),
62
- 'edge': combine_feature_stats(edge_stats),
63
- }
64
-
65
- combined_json = {}
66
- for key, (mean, std, count) in combined.items():
67
- combined_json[key] = {
68
- 'mean': mean.tolist() if mean.numel() > 0 else [],
69
- 'std': std.tolist() if std.numel() > 0 else [],
70
- 'count': count,
71
- }
72
-
73
- with open(combined_stats_path, 'w') as f:
74
- json.dump(combined_json, f, indent=4)
75
-
76
- with open(combined_stats_path, 'r') as f:
77
- combined_json = json.load(f)
78
-
79
- def to_tensor(d):
80
- mean = torch.tensor(d['mean'], dtype=dtype) if d['mean'] else torch.tensor([], dtype=dtype)
81
- std = torch.tensor(d['std'], dtype=dtype) if d['std'] else torch.tensor([], dtype=dtype)
82
- count = d['count']
83
- return mean, std, count
84
-
85
- return {
86
- 'node': to_tensor(combined_json['node']),
87
- 'edge': to_tensor(combined_json['edge']),
88
- }
89
-
90
- def compute_stats(feats, eps=1e-6):
91
- mean = feats.mean(dim=0)
92
- if feats.size(0) > 1:
93
- var = ((feats - mean) ** 2).mean(dim=0)
94
- else:
95
- var = torch.zeros_like(mean)
96
- std = torch.sqrt(var)
97
- std = torch.where(std < eps, torch.full_like(std, eps), std)
98
-
99
- return mean, std
100
-
101
- def save_stats(graphs: 'Graphs', filepath: str, categorical_unique_threshold=50):
102
- """
103
- Compute and save normalization stats (mean, std, counts) for node and edge features.
104
- Categorical features (few unique values) have normalization disabled (mean=0, std=1).
105
- """
106
- if len(graphs) == 0:
107
- raise ValueError("No graphs to compute stats from.")
108
-
109
- # Node and edge features
110
- all_node_feats = torch.cat([g.ndata['features'] for g, _ in graphs], dim=0)
111
- all_edge_feats = torch.cat([g.edata['features'] for g, _ in graphs], dim=0)
112
-
113
- counts = {
114
- 'node': all_node_feats.size(0),
115
- 'edge': all_edge_feats.size(0),
116
- }
117
-
118
- node_mean, node_std = compute_stats(all_node_feats)
119
- edge_mean, edge_std = compute_stats(all_edge_feats)
120
-
121
- categorical_mask = torch.tensor([
122
- torch.unique(all_node_feats[:, i]).numel() < categorical_unique_threshold
123
- for i in range(node_mean.size(0))
124
- ], dtype=torch.bool)
125
- node_mean[categorical_mask] = 0.0
126
- node_std[categorical_mask] = 1.0
127
-
128
- stats = {
129
- 'node': {
130
- 'mean': node_mean.tolist(),
131
- 'std': node_std.tolist(),
132
- 'count': counts['node'],
133
- },
134
- 'edge': {
135
- 'mean': edge_mean.tolist(),
136
- 'std': edge_std.tolist(),
137
- 'count': counts['edge'],
138
- },
139
- }
140
-
141
- os.makedirs(os.path.dirname(filepath), exist_ok=True)
142
-
143
- with open(filepath, 'w') as f:
144
- json.dump(stats, f, indent=4)
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/metrics.py DELETED
@@ -1,110 +0,0 @@
1
- import torch
2
- import numpy as np
3
- import torch.nn.functional as F
4
-
5
- def bce(input, target, weights=None):
6
-
7
- if input.shape != target.shape:
8
- if input.shape[-1] == 1 and input.shape[:-1] == target.shape:
9
- input = input.squeeze(-1)
10
- elif target.shape[-1] == 1 and target.shape[:-1] == input.shape:
11
- target = target.squeeze(-1)
12
-
13
- loss = F.binary_cross_entropy_with_logits(input, target, reduction='none')
14
- return torch.mean(loss)
15
-
16
- def weighted_bce(input, target, weights=None):
17
- """
18
- Compute a weighted and label-normalized binary cross entropy (BCE) loss.
19
-
20
- For each unique label in the target tensor, the BCE loss is computed and weighted,
21
- then normalized by the sum of weights for that label. The final loss is the mean
22
- of these per-label normalized losses.
23
-
24
- Args:
25
- input (Tensor): Predicted logits of shape (N, ...).
26
- target (Tensor): Ground truth labels of shape (N, ...), with discrete label values.
27
- weights (Tensor or None): Optional tensor of per-sample weights, same shape as input/target.
28
-
29
- Returns:
30
- Tensor: Scalar tensor representing the normalized weighted BCE loss.
31
- """
32
-
33
- if input.shape != target.shape:
34
- if input.shape[-1] == 1 and input.shape[:-1] == target.shape:
35
- input = input.squeeze(-1)
36
- elif target.shape[-1] == 1 and target.shape[:-1] == input.shape:
37
- target = target.squeeze(-1)
38
-
39
- # Compute per-element BCE loss (no reduction)
40
- loss = F.binary_cross_entropy_with_logits(input, target, reduction='none')
41
-
42
- # If weights not provided, use ones
43
- if weights is None:
44
- weights = torch.ones_like(loss)
45
-
46
- unique_labels = torch.unique(target)
47
- normalized_losses = []
48
- for label in unique_labels:
49
- label_mask = (target == label) # This will be a bool tensor
50
- # Defensive: make sure mask is bool
51
- if label_mask.dtype != torch.bool:
52
- label_mask = label_mask.bool()
53
- label_weights = weights[label_mask]
54
- label_losses = loss[label_mask]
55
- weight_sum = label_weights.sum()
56
- if weight_sum > 0:
57
- label_loss = (label_weights * label_losses).sum() / weight_sum
58
- normalized_losses.append(label_loss)
59
-
60
- if normalized_losses:
61
- return torch.stack(normalized_losses).mean()
62
- else:
63
- return torch.tensor(0.0, device=input.device)
64
-
65
-
66
- def roc_auc_score(classes : np.ndarray,
67
- predictions : np.ndarray,
68
- weights : np.ndarray = None) -> float:
69
- """
70
- Calculating ROC AUC score as the probability of correct ordering
71
- """
72
-
73
- if weights is None:
74
- weights = np.ones_like(predictions)
75
-
76
- assert len(classes) == len(predictions) == len(weights)
77
- assert classes.ndim == predictions.ndim == weights.ndim == 1
78
- class0, class1 = sorted(np.unique(classes))
79
-
80
- data = np.empty(
81
- shape=len(classes),
82
- dtype=[('c', classes.dtype),
83
- ('p', predictions.dtype),
84
- ('w', weights.dtype)]
85
- )
86
- data['c'], data['p'], data['w'] = classes, predictions, weights
87
-
88
- data = data[np.argsort(data['c'])]
89
- data = data[np.argsort(data['p'], kind='mergesort')] # here we're relying on stability as we need class orders preserved
90
-
91
- correction = 0.
92
- # mask1 - bool mask to highlight collision areas
93
- # mask2 - bool mask with collision areas' start points
94
- mask1 = np.empty(len(data), dtype=bool)
95
- mask2 = np.empty(len(data), dtype=bool)
96
- mask1[0] = mask2[-1] = False
97
- mask1[1:] = data['p'][1:] == data['p'][:-1]
98
- if mask1.any():
99
- mask2[:-1] = ~mask1[:-1] & mask1[1:]
100
- mask1[:-1] |= mask1[1:]
101
- ids, = mask2.nonzero()
102
- correction = sum([((dsplit['c'] == class0) * dsplit['w'] * msplit).sum() *
103
- ((dsplit['c'] == class1) * dsplit['w'] * msplit).sum()
104
- for dsplit, msplit in zip(np.split(data, ids), np.split(mask1, ids))]) * 0.5
105
-
106
- weights_0 = data['w'] * (data['c'] == class0)
107
- weights_1 = data['w'] * (data['c'] == class1)
108
- cumsum_0 = weights_0.cumsum()
109
-
110
- return ((cumsum_0 * weights_1).sum() - correction) / (weights_1.sum() * cumsum_0[-1])
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/models/Edge_Network.py DELETED
@@ -1,72 +0,0 @@
1
- import torch
2
- import torch.nn as nn
3
- import dgl
4
-
5
- from models import utils
6
-
7
- class Edge_Network(nn.Module):
8
- def __init__(self, cfg):
9
- super().__init__()
10
- hid_size = cfg.hid_size
11
- n_layers = cfg.n_layers
12
- self.n_proc_steps = cfg.n_proc_steps
13
-
14
- #encoder
15
- self.node_encoder = utils.Make_MLP(cfg.input_dim_nodes, hid_size, hid_size, n_layers)
16
- self.edge_encoder = utils.Make_MLP(cfg.input_dim_edges, hid_size, hid_size, n_layers)
17
- self.global_encoder = utils.Make_MLP(cfg.input_dim_globals, hid_size, hid_size, n_layers)
18
-
19
- #GNN
20
- self.node_update = utils.Make_MLP(3*hid_size, hid_size, hid_size, n_layers)
21
- self.edge_update = utils.Make_MLP(4*hid_size, hid_size, hid_size, n_layers)
22
- self.global_update = utils.Make_MLP(3*hid_size, hid_size, hid_size, n_layers)
23
-
24
- #decoder
25
- self.global_decoder = utils.Make_MLP(hid_size, hid_size, hid_size, n_layers)
26
- self.classify = nn.Linear(hid_size, cfg.out_dim)
27
-
28
- def forward(self, node_feats, edge_feats, global_feats, batched_graph, metadata={}):
29
- # encoders
30
- batched_graph.ndata['h'] = self.node_encoder(node_feats)
31
- batched_graph.edata['e'] = self.edge_encoder(edge_feats)
32
-
33
- if global_feats.ndim == 3:
34
- global_feats = global_feats.view(-1, global_feats.shape[-1])
35
- h_global = self.global_encoder(global_feats)
36
-
37
- # message passing
38
- for _ in range(self.n_proc_steps):
39
- batched_graph.apply_edges(dgl.function.copy_u('h', 'm_u'))
40
- batched_graph.apply_edges(utils.copy_v)
41
-
42
- # edge update
43
- edge_inputs = torch.cat([
44
- batched_graph.edata['e'],
45
- batched_graph.edata['m_u'],
46
- batched_graph.edata['m_v'],
47
- utils.broadcast_global_to_edges(h_global, edge_split=metadata.get("batch_num_edges", None))
48
- ], dim=1)
49
- batched_graph.edata['e'] = self.edge_update(edge_inputs)
50
-
51
- # node update
52
- batched_graph.update_all(dgl.function.copy_e('e', 'm'), dgl.function.sum('m', 'h_e'))
53
- node_inputs = torch.cat([
54
- batched_graph.ndata['h'],
55
- batched_graph.ndata['h_e'],
56
- utils.broadcast_global_to_nodes(h_global, node_split=metadata.get("batch_num_nodes", None))
57
- ], dim=1)
58
- batched_graph.ndata['h'] = self.node_update(node_inputs)
59
-
60
- # global update
61
- graph_node_feat = utils.mean_nodes(
62
- batched_graph, 'h', node_split=metadata.get("batch_num_nodes", None)
63
- )
64
- graph_edge_feat = utils.mean_edges(
65
- batched_graph, 'e', edge_split=metadata.get("batch_num_edges", None)
66
- )
67
- h_global = self.global_update(torch.cat([h_global, graph_node_feat, graph_edge_feat], dim=1))
68
-
69
- h_global = self.global_decoder(h_global)
70
- out = self.classify(h_global)
71
- return out
72
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/models/MeshGraphNet.py DELETED
@@ -1,51 +0,0 @@
1
- import torch
2
- import torch.nn as nn
3
- import dgl
4
-
5
- from models import utils
6
-
7
- # Import the PhysicsNemo MeshGraphNet model
8
- from physicsnemo.models.meshgraphnet import MeshGraphNet as PhysicsNemoMeshGraphNet
9
-
10
- class MeshGraphNet(nn.Module):
11
- def __init__(self, cfg):
12
- super().__init__()
13
- base_gnn_cfg = cfg.base_gnn
14
- self.base_gnn = PhysicsNemoMeshGraphNet(**base_gnn_cfg)
15
-
16
- self.global_mlp = nn.Sequential(
17
- nn.Linear(cfg.global_feat_dim, cfg.global_emb_dim),
18
- nn.ReLU(),
19
- )
20
-
21
- self.mlp = nn.Linear(
22
- base_gnn_cfg['output_dim'] + base_gnn_cfg['input_dim_edges'] + cfg.global_emb_dim,
23
- cfg.out_dim
24
- )
25
-
26
- def forward(self, node_feats, edge_feats, global_feats, batched_graph, metadata={}):
27
- """
28
- node_feats: [total_num_nodes, node_feat_dim]
29
- edge_feats: [total_num_edges, edge_feat_dim]
30
- global_feats: [num_graphs, global_feat_dim]
31
- batched_graph: DGLGraph, representing the collection of graphs in a batch
32
- metadata: dict, may contain 'batch_num_nodes', 'batch_num_edges', etc.
33
- Returns:
34
- graph_pred: [num_graphs, out_dim]
35
- """
36
- node_pred = self.base_gnn(node_feats, edge_feats, batched_graph)
37
- batched_graph.ndata['h'] = node_pred
38
- batched_graph.edata['e'] = edge_feats
39
-
40
- graph_node_feat = utils.mean_nodes(batched_graph, 'h', node_split=metadata.get("batch_num_nodes", None))
41
- graph_edge_feat = utils.mean_edges(batched_graph, 'e', edge_split=metadata.get("batch_num_edges", None))
42
-
43
- # Flatten global_feats if needed
44
- if global_feats.ndim == 3:
45
- global_feats = global_feats.view(-1, global_feats.shape[-1])
46
- global_emb = self.global_mlp(global_feats) # [num_graphs, global_emb_dim]
47
-
48
- combined_feat = torch.cat([graph_node_feat, graph_edge_feat, global_emb], dim=-1)
49
- graph_pred = self.mlp(combined_feat)
50
- return graph_pred
51
-
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/models/utils.py DELETED
@@ -1,135 +0,0 @@
1
- import torch
2
- import torch.nn as nn
3
- import dgl
4
-
5
- def mean_nodes(batched_graph, feat_key='h', op='mean', node_split=None):
6
- """
7
- Aggregates node features per disjoint graph in a batched DGLGraph.
8
-
9
- Args:
10
- batched_graph: DGLGraph
11
- feat_key: str, node feature key
12
- op: 'mean', 'sum', or 'max'
13
- node_split: 1D tensor or list of ints (num nodes per graph)
14
-
15
- Returns:
16
- Tensor of shape [num_graphs, node_feat_dim]
17
- """
18
- h = batched_graph.ndata[feat_key]
19
- if node_split is None or len(node_split) == 0:
20
- if op == 'mean':
21
- return dgl.mean_nodes(batched_graph, feat_key)
22
- elif op == 'sum':
23
- return dgl.sum_nodes(batched_graph, feat_key)
24
- elif op == 'max':
25
- return dgl.max_nodes(batched_graph, feat_key)
26
- else:
27
- raise ValueError(f"Unknown op: {op}")
28
- else:
29
- # Ensure node_split is a flat list of ints
30
- if isinstance(node_split, torch.Tensor):
31
- splits = node_split.view(-1).tolist()
32
- else:
33
- splits = [int(x) for x in node_split]
34
- chunks = torch.split(h, splits, dim=0)
35
- if op == 'mean':
36
- out = torch.stack([chunk.mean(0) if chunk.shape[0] > 0 else torch.zeros_like(h[0]) for chunk in chunks])
37
- elif op == 'sum':
38
- out = torch.stack([chunk.sum(0) if chunk.shape[0] > 0 else torch.zeros_like(h[0]) for chunk in chunks])
39
- elif op == 'max':
40
- out = torch.stack([chunk.max(0).values if chunk.shape[0] > 0 else torch.zeros_like(h[0]) for chunk in chunks])
41
- else:
42
- raise ValueError(f"Unknown op: {op}")
43
- return out
44
-
45
- def mean_edges(batched_graph, feat_key='e', op='mean', edge_split=None):
46
- """
47
- Aggregates edge features per disjoint graph in a batched DGLGraph.
48
-
49
- Args:
50
- batched_graph: DGLGraph
51
- feat_key: str, edge feature key
52
- op: 'mean', 'sum', or 'max'
53
- edge_split: 1D tensor or list of ints (num edges per graph)
54
-
55
- Returns:
56
- Tensor of shape [num_graphs, edge_feat_dim]
57
- """
58
- e = batched_graph.edata[feat_key]
59
- if edge_split is None or len(edge_split) == 0:
60
- if op == 'mean':
61
- return dgl.mean_edges(batched_graph, feat_key)
62
- elif op == 'sum':
63
- return dgl.sum_edges(batched_graph, feat_key)
64
- elif op == 'max':
65
- return dgl.max_edges(batched_graph, feat_key)
66
- else:
67
- raise ValueError(f"Unknown op: {op}")
68
- else:
69
- # Ensure edge_split is a flat list of ints
70
- if isinstance(edge_split, torch.Tensor):
71
- splits = edge_split.view(-1).tolist()
72
- else:
73
- splits = [int(x) for x in edge_split]
74
- chunks = torch.split(e, splits, dim=0)
75
- if op == 'mean':
76
- out = torch.stack([chunk.mean(0) if chunk.shape[0] > 0 else torch.zeros_like(e[0]) for chunk in chunks])
77
- elif op == 'sum':
78
- out = torch.stack([chunk.sum(0) if chunk.shape[0] > 0 else torch.zeros_like(e[0]) for chunk in chunks])
79
- elif op == 'max':
80
- out = torch.stack([chunk.max(0).values if chunk.shape[0] > 0 else torch.zeros_like(e[0]) for chunk in chunks])
81
- else:
82
- raise ValueError(f"Unknown op: {op}")
83
- return out
84
-
85
- def Make_SLP(in_size, out_size, activation = nn.ReLU, dropout = 0):
86
- layers = []
87
- layers.append(nn.Linear(in_size, out_size))
88
- layers.append(activation())
89
- layers.append(nn.Dropout(dropout))
90
- return layers
91
-
92
- def Make_MLP(in_size, hid_size, out_size, n_layers, activation = nn.ReLU, dropout = 0):
93
- layers = []
94
- if n_layers > 1:
95
- layers += Make_SLP(in_size, hid_size, activation, dropout)
96
- for i in range(n_layers-2):
97
- layers += Make_SLP(hid_size, hid_size, activation, dropout)
98
- layers += Make_SLP(hid_size, out_size, activation, dropout)
99
- else:
100
- layers += Make_SLP(in_size, out_size, activation, dropout)
101
- layers.append(torch.nn.LayerNorm(out_size))
102
- return nn.Sequential(*layers)
103
-
104
- def broadcast_global_to_nodes(globals, node_split):
105
- """
106
- globals: [num_graphs, global_dim]
107
- node_split: list/1D tensor of length num_graphs, number of nodes per graph
108
- Returns: [total_num_nodes, global_dim]
109
- """
110
- if node_split is None:
111
- raise ValueError("node_split must be provided")
112
- if not torch.is_tensor(node_split):
113
- node_split = torch.tensor(node_split, dtype=torch.long, device=globals.device)
114
- else:
115
- node_split = node_split.to(device=globals.device, dtype=torch.long)
116
- node_split = node_split.flatten()
117
- return torch.repeat_interleave(globals, node_split, dim=0)
118
-
119
- def broadcast_global_to_edges(globals, edge_split):
120
- """
121
- globals: [num_graphs, global_dim] (on CUDA or CPU)
122
- edge_split: list/1D tensor of length num_graphs, number of edges per graph (CPU or CUDA)
123
- Returns: [total_num_edges, global_dim]
124
- """
125
- if edge_split is None:
126
- raise ValueError("edge_split must be provided")
127
- if not torch.is_tensor(edge_split):
128
- edge_split = torch.tensor(edge_split, dtype=torch.long, device=globals.device)
129
- else:
130
- edge_split = edge_split.to(device=globals.device, dtype=torch.long)
131
- edge_split = edge_split.flatten()
132
- return torch.repeat_interleave(globals, edge_split, dim=0)
133
-
134
- def copy_v(edges):
135
- return {'m_v': edges.dst['h']}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/setup/Dockerfile DELETED
@@ -1,23 +0,0 @@
1
- FROM nvcr.io/nvidia/physicsnemo/physicsnemo:25.06
2
-
3
- WORKDIR /global/cfs/projectdirs/atlas/joshua/GNN4Colliders
4
-
5
- LABEL maintainer.name="Joshua Ho"
6
- LABEL maintainer.email="ho22joshua@berkeley.edu"
7
-
8
- ENV LANG=C.UTF-8
9
-
10
- # Install system dependencies: vim, OpenMPI, and build tools
11
- RUN apt-get update -qq \
12
- && apt-get install -y --no-install-recommends \
13
- wget lsb-release gnupg software-properties-common \
14
- vim \
15
- g++-11 gcc-11 libstdc++-11-dev \
16
- openmpi-bin openmpi-common libopenmpi-dev \
17
- && rm -rf /var/lib/apt/lists/*
18
-
19
- # Install Python packages: mpi4py and jupyter
20
- RUN pip install --no-cache-dir mpi4py jupyter uproot
21
-
22
- # (Optional) Expose Jupyter port
23
- EXPOSE 8888
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/setup/build_image.sh DELETED
@@ -1,4 +0,0 @@
1
- tag=$1
2
- echo $tag
3
- podman-hpc build -t joshuaho/nemo:$tag --platform linux/amd64 .
4
- podman-hpc migrate joshuaho/nemo:$tag
 
 
 
 
 
legacy/physicsnemo/train.py DELETED
@@ -1,246 +0,0 @@
1
- import time, os
2
-
3
- start = time.time()
4
- import torch
5
- from torch.nn.parallel import DistributedDataParallel
6
- from dgl.dataloading import GraphDataLoader
7
- from torch.amp import GradScaler
8
- import numpy as np
9
- import hydra
10
- from omegaconf import DictConfig
11
- from physicsnemo.launch.logging import (
12
- PythonLogger,
13
- RankZeroLoggingWrapper,
14
- )
15
- from physicsnemo.launch.utils import load_checkpoint, save_checkpoint
16
- from physicsnemo.distributed.manager import DistributedManager
17
-
18
- import json
19
- from tqdm import tqdm
20
- import random
21
-
22
- import models.MeshGraphNet as MeshGraphNet
23
- from dataset.Dataset import get_dataset
24
- import metrics
25
-
26
- import utils
27
-
28
- class MGNTrainer:
29
- def __init__(self, logger, cfg, dist):
30
- # set device
31
- self.device = dist.device
32
- logger.info(f"Using {self.device} device")
33
-
34
- start = time.time()
35
- self.trainloader, self.valloader, self.testloader = get_dataset(cfg, self.device)
36
- print(f"total time loading dataset: {time.time() - start:.2f} seconds")
37
-
38
- dtype_str = getattr(cfg.root_dataset, "dtype", "torch.float32")
39
- if isinstance(dtype_str, str) and dtype_str.startswith("torch."):
40
- self.dtype = getattr(torch, dtype_str.split(".")[-1], torch.float32)
41
- else:
42
- self.dtype = torch.float32
43
-
44
- self.model = utils.build_from_module(cfg.architecture)
45
- self.model = self.model.to(dtype=self.dtype, device=self.device)
46
- # num_params = sum(p.numel() for p in self.model.parameters() if p.requires_grad)
47
- # print(f"Number of trainable parameters: {num_params}")
48
-
49
- if cfg.performance.jit:
50
- self.model = torch.jit.script(self.model).to(self.device)
51
- else:
52
- self.model = self.model.to(self.device)
53
-
54
- # instantiate loss, optimizer, and scheduler
55
- self.optimizer = torch.optim.Adam(self.model.parameters(), lr=cfg.scheduler.lr)
56
- self.scheduler = torch.optim.lr_scheduler.CosineAnnealingLR(
57
- self.optimizer,
58
- T_max=cfg.training.epochs,
59
- eta_min=cfg.scheduler.lr * cfg.scheduler.lr_decay,
60
- )
61
- self.scaler = GradScaler('cuda')
62
-
63
- # load checkpoint
64
- self.epoch_init = load_checkpoint(
65
- os.path.join(cfg.checkpoints.ckpt_path, cfg.checkpoints.ckpt_name),
66
- models=self.model,
67
- optimizer=self.optimizer,
68
- scheduler=self.scheduler,
69
- scaler=self.scaler,
70
- device=self.device,
71
- )
72
-
73
- self.cfg = cfg
74
-
75
- def backward(self, loss):
76
- """
77
- Perform backward pass.
78
-
79
- Arguments:
80
- loss: loss value.
81
-
82
- """
83
- # backward pass
84
- if self.cfg.performance.amp:
85
- self.scaler.scale(loss).backward()
86
- self.scaler.step(self.optimizer)
87
- self.scaler.update()
88
- else:
89
- loss.backward()
90
- self.optimizer.step()
91
-
92
- def train(self, graph, metadata):
93
- """
94
- Perform one training iteration over one graph. The training is performed
95
- over multiple timesteps, where the number of timesteps is specified in
96
- the 'stride' parameter.
97
-
98
- Arguments:
99
- graph: the desired graph.
100
-
101
- Returns:
102
- loss: loss value.
103
-
104
- """
105
- graph = graph.to(self.device, non_blocking=True)
106
- globals = metadata['globals'].to(self.device, non_blocking=True)
107
- label = metadata['label'].to(self.device, non_blocking=True)
108
- weight = metadata['weight'].to(self.device, non_blocking=True)
109
-
110
- self.optimizer.zero_grad()
111
- pred = self.model(graph.ndata["features"], graph.edata["features"], globals, graph, metadata)
112
- loss = metrics.weighted_bce(pred, label, weights=weight)
113
- self.backward(loss)
114
- return loss.detach()
115
-
116
- @torch.no_grad()
117
- def eval(self):
118
- """
119
- Evaluate the model on one batch.
120
-
121
- Args:
122
- graph (DGLGraph): The input graph.
123
- label (Tensor): The target labels.
124
-
125
- Returns:
126
- loss (Tensor): The computed loss value (scalar).
127
- """
128
- predictions = []
129
- labels = []
130
- weights = []
131
-
132
- for graph, metadata in self.valloader:
133
-
134
- graph = graph.to(self.device, non_blocking=True)
135
- globals = metadata['globals'].to(self.device, non_blocking=True)
136
- label = metadata['label'].to(self.device, non_blocking=True)
137
- weight = metadata['weight'].to(self.device, non_blocking=True)
138
-
139
- pred = self.model(graph.ndata["features"], graph.edata["features"], globals, graph, metadata)
140
- predictions.append(pred)
141
- labels.append(label)
142
- weights.append(weight)
143
-
144
- predictions = torch.cat(predictions, dim=0)
145
- labels = torch.cat(labels, dim=0)
146
- weights = torch.cat(weights, dim=0)
147
-
148
- loss = metrics.weighted_bce(predictions, labels, weights=weights)
149
-
150
- # Convert logits to probabilities
151
- prob = torch.sigmoid(predictions)
152
-
153
- # Flatten to 1D arrays
154
- prob_flat = prob.detach().to(torch.float32).cpu().numpy().flatten()
155
- labels_flat = labels.detach().to(torch.float32).cpu().numpy().flatten()
156
-
157
- # Calculate AUC
158
- try:
159
- auc = metrics.roc_auc_score(labels_flat, prob_flat)
160
- except ValueError:
161
- auc = float('nan') # Not enough classes present for AUC
162
-
163
- return loss, auc
164
-
165
- @hydra.main(version_base=None, config_path="./configs/", config_name="tHjb_CP_0_vs_45")
166
- def do_training(cfg: DictConfig):
167
- """
168
- Perform training over all graphs in the dataset.
169
-
170
- Arguments:
171
- cfg: Dictionary of parameters.
172
-
173
- """
174
- random.seed(cfg.random_seed)
175
- np.random.seed(cfg.random_seed)
176
- torch.manual_seed(cfg.random_seed)
177
-
178
- # initialize distributed manager
179
- DistributedManager.initialize()
180
- dist = DistributedManager()
181
-
182
- # initialize loggers
183
- os.makedirs(cfg.checkpoints.ckpt_path, exist_ok=True)
184
- logger = PythonLogger("main")
185
- logger.file_logging(os.path.join(cfg.checkpoints.ckpt_path, "train.log"))
186
-
187
- # initialize trainer
188
- trainer = MGNTrainer(logger, cfg, dist)
189
-
190
- if dist.distributed:
191
- ddps = torch.cuda.Stream()
192
- with torch.cuda.stream(ddps):
193
- trainer.model = DistributedDataParallel(
194
- trainer.model,
195
- device_ids=[dist.local_rank], # Set the device_id to be
196
- # the local rank of this process on
197
- # this node
198
- output_device=dist.device,
199
- broadcast_buffers=dist.broadcast_buffers,
200
- find_unused_parameters=dist.find_unused_parameters,
201
- )
202
- torch.cuda.current_stream().wait_stream(ddps)
203
-
204
- # training loop
205
- start = time.time()
206
- logger.info("Training started...")
207
- for epoch in range(trainer.epoch_init, cfg.training.epochs):
208
-
209
- # Training
210
- train_loss = []
211
- for graph, metadata in tqdm(trainer.trainloader, desc=f"epoch {epoch} trianing"):
212
- trainer.model.train()
213
- loss = trainer.train(graph, metadata)
214
- train_loss.append(loss.item())
215
-
216
- val_loss, val_auc = trainer.eval()
217
-
218
- train_loss = torch.tensor(train_loss).mean()
219
-
220
- logger.info(
221
- f"epoch: {epoch}, loss: {train_loss:10.3e}, val_loss: {val_loss:10.3e}, val_auc = {val_auc:10.3e}, time per epoch: {(time.time()-start):10.3e}"
222
- )
223
-
224
- # save checkpoint
225
- save_checkpoint(
226
- os.path.join(cfg.checkpoints.ckpt_path, cfg.checkpoints.ckpt_name),
227
- models=trainer.model,
228
- optimizer=trainer.optimizer,
229
- scheduler=trainer.scheduler,
230
- scaler=trainer.scaler,
231
- epoch=epoch,
232
- )
233
- start = time.time()
234
- trainer.scheduler.step()
235
- logger.info("Training completed!")
236
-
237
-
238
- """
239
- Perform training over all graphs in the dataset.
240
-
241
- Arguments:
242
- cfg: Dictionary of parameters.
243
-
244
- """
245
- if __name__ == "__main__":
246
- do_training()
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/physicsnemo/utils.py DELETED
@@ -1,11 +0,0 @@
1
- import importlib
2
- from types import SimpleNamespace
3
-
4
- def build_from_module(cfg):
5
- modname = cfg['module']
6
- classname = cfg['class']
7
- args = cfg['args']
8
- module = importlib.import_module(modname)
9
- model_cls = getattr(module, classname)
10
- cfg_obj = SimpleNamespace(**args)
11
- return model_cls(cfg_obj)
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-data-preparation/SKILL.md DELETED
@@ -1,202 +0,0 @@
1
- ---
2
- name: root-gnn-dgl-data-preparation
3
- description: Use when the user asks to build graphs or prebatched .bin files, rerun failed data prep, verify missing graph chunks, or use scripts/prep_data.py, scripts/check_dataset_files.py, or jobs/prep_data/prep_data.sh before training in root_gnn_dgl.
4
- ---
5
-
6
- # root-gnn-dgl-data-preparation
7
-
8
- Use this skill for graph creation and graph-readiness checks before training.
9
-
10
- ## Fast parallel graph creation
11
-
12
- For the Delphes baseline configs, use the bounded launcher rather than
13
- `jobs/prep_data/run_processing.py` or a hand-written unbounded process loop:
14
-
15
- ```bash
16
- python jobs/prep_data/parallel_prep.py \
17
- configs/delphes/*_baseline.yaml \
18
- --phase raw \
19
- --workers 8
20
- ```
21
-
22
- The launcher runs one subprocess per graph chunk, with a bounded worker pool.
23
- The graph-building path streams ROOT entries for the requested chunk, so
24
- parallel workers do not each materialize the complete input ROOT file.
25
- Start with 8 workers per CPU session; 16 is reasonable when memory and I/O
26
- are healthy. Do not equate the number of workers with all available CPUs:
27
- filesystem contention and per-process graph memory generally become the
28
- bottleneck first. Set numerical-library thread counts to one before launch:
29
-
30
- ```bash
31
- export OMP_NUM_THREADS=1
32
- export MKL_NUM_THREADS=1
33
- export OPENBLAS_NUM_THREADS=1
34
- export NUMEXPR_NUM_THREADS=1
35
- ```
36
-
37
- For two independent CPU sessions, split the task list without overlap:
38
-
39
- ```bash
40
- # Session 1
41
- python jobs/prep_data/parallel_prep.py configs/delphes/*_baseline.yaml \
42
- --phase raw --split 1/2 --workers 16
43
-
44
- # Session 2
45
- python jobs/prep_data/parallel_prep.py configs/delphes/*_baseline.yaml \
46
- --phase raw --split 2/2 --workers 16
47
- ```
48
-
49
- Run prebatching only after both raw phases finish:
50
-
51
- ```bash
52
- python jobs/prep_data/parallel_prep.py configs/delphes/*_baseline.yaml \
53
- --phase shuffle --split 1/2 --workers 8 --buffer-size 1
54
- python jobs/prep_data/parallel_prep.py configs/delphes/*_baseline.yaml \
55
- --phase shuffle --split 2/2 --workers 8 --buffer-size 1
56
- ```
57
-
58
- `--split PART/TOTAL` partitions dataset tasks, not events. Use the same
59
- config glob and split value in both sessions. Run only baseline configs for
60
- graph creation; finetuning configs share the same processed data directories.
61
-
62
- ## Primary entry point
63
-
64
- Run from the repo root:
65
-
66
- ```bash
67
- python scripts/prep_data.py --config <config.yaml> --dataset <dataset_name> --chunk <chunk_index>
68
- ```
69
-
70
- Use `--shuffle_mode` when you want preshuffled, prebatched graph files for training:
71
-
72
- ```bash
73
- python scripts/prep_data.py --config <config.yaml> --dataset <dataset_name> --shuffle_mode --chunk <chunk_index>
74
- ```
75
-
76
- ## Chunk and memory semantics
77
-
78
- - `args.chunks` is the number of ordinary raw graph `.bin` files. It controls
79
- the size of each graph-creation task and must match the configs used later
80
- by training.
81
- - `shuffle_chunks` is the number of shuffled/prebatched output partitions.
82
- - `buffer_size` is the number of raw graph `.bin` chunks cached in memory by a
83
- lazy dataset during shuffling. It does not need to be less than `chunks`,
84
- but use `1` (or `2`) for memory-constrained runs.
85
- - Reducing `chunks` makes each raw task larger; it usually increases, rather
86
- than reduces, per-worker memory. Change it consistently in baseline and
87
- finetuning configs, and do not mix old cache files from a different chunk
88
- layout.
89
- - For Delphes configs, the repository currently uses `chunks: 10` and
90
- `shuffle_chunks: 10`.
91
-
92
- The old `jobs/prep_data/prep_data.sh` wrapper is sequential and previously
93
- ran chunk 0 twice. Prefer `parallel_prep.py`; if the wrapper is needed, it
94
- now runs each requested chunk once.
95
-
96
- ## Recommended single-dataset run pattern
97
-
98
- - Read dataset names from `config["Datasets"]`.
99
- - Read the chunk count from each dataset's `args.chunks`.
100
- - For a single raw chunk, run the command without `--shuffle_mode`.
101
- - Add `--shuffle_mode` only after the ordinary graph chunks exist.
102
-
103
- Example pattern:
104
-
105
- ```bash
106
- python scripts/prep_data.py --config <config.yaml> --dataset <dataset> --chunk 0
107
- python scripts/prep_data.py --config <config.yaml> --dataset <dataset> \
108
- --shuffle_mode --chunk 0 --buffer_size 1
109
- ```
110
-
111
- Use the repo wrapper when you want the standard loop:
112
-
113
- ```bash
114
- bash jobs/prep_data/prep_data.sh <config> <dataset> <chunks> [extra_args]
115
- ```
116
-
117
- ## Important flags and caveats
118
-
119
- - `--shuffle_mode` creates the prebatched artifacts consumed by `scripts/training_script.py --preshuffle`.
120
- - `scripts/prep_data.py` accepts `--buffer_size` and `--shuffle_chunks` as
121
- runtime overrides; changing `--shuffle_chunks` changes output filenames,
122
- so update training configs before using that override for production.
123
- - `--drop_last` is inverted by the CLI definition: passing the flag sets `drop_last=False`.
124
- - Dataset configs can override training batch size during prebatching with a dataset-level `batch_size`.
125
- - The README says a `list index out of range` after graph saving is currently expected in some prep runs. Treat it as non-fatal if the output `.bin` files were written successfully.
126
-
127
- ## Dataset selections
128
-
129
- Datasets may define event selections at the dataset level:
130
-
131
- ```yaml
132
- Datasets:
133
- signal:
134
- args:
135
- ...
136
- selections:
137
- - [n_jets, 4, ">="]
138
- - "met_met_NOSYS > 30000"
139
- ```
140
-
141
- Selection behavior:
142
-
143
- - Selections are applied during data prep before graph chunking.
144
- - `scripts/prep_data.py` prints a cutflow for each dataset before processing.
145
- - The streaming optimization applies to the no-selection Delphes path. Configs
146
- with selections still use the legacy full-array selection path and should
147
- be tested with one worker before parallelizing.
148
- - Tuple/list selections use `[branch, cut, op]`, where `op` can be `>`, `>=`, `<`, `<=`, `==`, or `!=`.
149
- - String selections are evaluated against loaded ROOT branches, so referenced branch names must exist.
150
- - Selection branches are added automatically to the branch list through `selection_branches()`.
151
- - Empty or omitted `selections` means all events pass.
152
- - If a selection references vector branches, verify the result is one boolean per event; jagged per-object masks will not index event arrays correctly.
153
-
154
- ## Audit the outputs
155
-
156
- Run from the repo root:
157
-
158
- ```bash
159
- python scripts/check_dataset_files.py --configs stats_100K/pretraining_multiclass.yaml
160
- ```
161
-
162
- The `--configs` argument must be a comma-separated list of paths relative to `configs/`.
163
-
164
- This checker validates:
165
-
166
- - chunk files named `${dataset}_${chunk}.bin`
167
- - prebatched fold files named `${dataset}_prebatched_padded_${i}_n_${n_folds}_f_${foldlist}.bin`
168
-
169
- Use rerun mode to repair missing artifacts:
170
-
171
- ```bash
172
- python scripts/check_dataset_files.py --configs stats_100K/pretraining_multiclass.yaml --rerun
173
- ```
174
-
175
- For bulk prep over every dataset in one or more configs, use the bounded
176
- launcher above. Avoid the legacy bulk helper for large Delphes files because
177
- it can start too many full-file readers.
178
-
179
- The legacy helper is:
180
-
181
- ```bash
182
- python jobs/prep_data/run_processing.py configs/run_3_ttH/scratch.yaml configs/run_3_ttH/finetuning.yaml
183
- ```
184
-
185
- This calls `jobs/prep_data/prep_data.sh` for each dataset using the dataset-level `shuffle_chunks` value.
186
-
187
- Treat data prep as ready only if:
188
-
189
- - every required chunk file exists
190
- - every required prebatched fold file exists when training will use `--preshuffle`
191
- - save paths match the config
192
- - any post-save `IndexError` did not prevent the files from being written
193
-
194
- If stopping a Slurm run, prefer cancelling the whole allocation:
195
-
196
- ```bash
197
- scancel "$SLURM_JOB_ID"
198
- ```
199
-
200
- Completed `.bin` files remain on disk, but inspect files being written at the
201
- time of cancellation before restarting; a partially written file may exist
202
- and be mistaken for a valid cache.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-env-setup/SKILL.md DELETED
@@ -1,63 +0,0 @@
1
- ---
2
- name: root-gnn-dgl-env-setup
3
- description: Use when the user asks to set up or validate the root_gnn_dgl runtime, such as running conda setup from setup/environment.yml, setup/test_setup.py, import ROOT checks, podman-hpc image setup, or the interactive allocation scripts in jobs/ before data prep, training, or inference.
4
- ---
5
-
6
- # root-gnn-dgl-env-setup
7
-
8
- Use this skill from the repo root before any stage run.
9
-
10
- ## Choose the runtime
11
-
12
- - Use the conda environment in `setup/environment.yml` for `scripts/inference.py`. The repo README says inference needs PyROOT, and the podman image does not include ROOT.
13
- - Use the `podman-hpc` image `joshuaho/pytorch:1.0` for training on Perlmutter when you want the containerized path.
14
- - For parallel inference, make sure `mpi4py` is available. The README notes it is not listed in the conda environment requirements; `setup/Dockerfile` installs it in the container image.
15
-
16
- ## Conda path
17
-
18
- ```bash
19
- cd setup
20
- conda env create -f environment.yml
21
- conda activate pytorch
22
- cd ..
23
- python setup/test_setup.py
24
- python -c "import ROOT"
25
- ```
26
-
27
- Run `setup/test_setup.py` from the repo root. It appends the current working directory to `sys.path` and checks imports in `scripts`, `root_gnn_base`, and `models`.
28
-
29
- ## Podman path
30
-
31
- ```bash
32
- podman-hpc pull docker.io/joshuaho/pytorch:1.0
33
- ```
34
-
35
- Or build locally:
36
-
37
- ```bash
38
- cd setup
39
- source build_image.sh
40
- ```
41
-
42
- The helper `setup/launch_image.sh` mounts `/pscratch/sd/j/joshuaho/` and `/global/cfs/projectdirs/atlas/joshua/` into the container and then runs the given entrypoint.
43
-
44
- ## Interactive allocations
45
-
46
- - `source jobs/interactive.sh` for one shared interactive GPU node.
47
- - `source jobs/cpu.sh` for a CPU allocation that suits large prep loops.
48
- - `source jobs/salloc.sh` for a multi-node GPU allocation.
49
-
50
- ## Runtime audit
51
-
52
- - Use `nvidia-smi` before training on login or interactive nodes to confirm memory availability.
53
- - Validate the basic repo imports with `python setup/test_setup.py`.
54
- - Validate PyROOT explicitly with `python -c "import ROOT"`.
55
- - For parallel inference, also validate `python -c "from mpi4py import MPI"`.
56
- - Some repo scripts hard-code NERSC-style paths under `/global/cfs/projectdirs/atlas/joshua/...`. If running elsewhere, fix those paths before assuming the environment is valid.
57
-
58
- Treat environment setup as passing only if:
59
-
60
- - imports succeed
61
- - the chosen runtime matches the stage you plan to run
62
- - required site-specific paths exist
63
- - GPU or CPU resources are actually available for the intended stage
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-inference/SKILL.md DELETED
@@ -1,133 +0,0 @@
1
- ---
2
- name: root-gnn-dgl-inference
3
- description: Use when the user asks to score ROOT files, add GNN score branches, launch MPI inference, or verify inference outputs in root_gnn_dgl, including scripts/inference.py, jobs/inference/run_inference.py, ROOT branch checks, and NPZ or ROOT output audits.
4
- ---
5
-
6
- # root-gnn-dgl-inference
7
-
8
- Use this skill to score ntuples with trained models and verify the outputs.
9
-
10
- ## Environment
11
-
12
- - Run inference in an environment with PyROOT available.
13
- - The repo README says the conda environment is required for inference because the podman training image does not include ROOT.
14
-
15
- ## Entry point
16
-
17
- Run from the repo root:
18
-
19
- ```bash
20
- python scripts/inference.py \
21
- --target <input.root> \
22
- --destination <output.root> \
23
- --config <config.yaml> \
24
- --branch_name <branch_name> \
25
- --chunks 1 \
26
- --chunkno 0 \
27
- --write
28
- ```
29
-
30
- ## Multi-model inference
31
-
32
- The script accepts multiple configs and multiple branch names in one run:
33
-
34
- ```bash
35
- python scripts/inference.py \
36
- --target <input.root> \
37
- --destination <output.root> \
38
- --config config_a.yaml config_b.yaml \
39
- --branch_name score_a score_b \
40
- --chunks 1 \
41
- --chunkno 0 \
42
- --write
43
- ```
44
-
45
- The number of configs and branch names must match.
46
-
47
- ## Checkpoint selection
48
-
49
- - With the default `--ckpt -1`, the script selects the best epoch from `training.log` using `--var` and `--mode`.
50
- - Use `--ckpt <n>` to force a specific checkpoint.
51
- - If `--destination` is omitted, the script writes under `<Training_Directory>/inference/`.
52
-
53
- ## Output modes
54
-
55
- - `--write` creates a new ROOT file and adds score branches.
56
- - Without `--write`, the script saves an `.npz` bundle containing scores, labels, and tracking info.
57
- - Use `--clobber` when reusing an existing destination path.
58
-
59
- ## Parallel inference
60
-
61
- Use the generic MPI/local wrapper for many files or many models:
62
-
63
- ```bash
64
- mpirun -np <num_ranks> python jobs/inference/run_inference.py \
65
- --sample-config <config-with-target-datasets.yaml> \
66
- --config-dir <directory-of-model-configs> \
67
- --output-dir <output-directory> \
68
- --write
69
- ```
70
-
71
- Useful variants:
72
-
73
- ```bash
74
- python jobs/inference/run_inference.py --target '<glob-or-file.root>' --config model.yaml --output-dir scores --write
75
- python jobs/inference/run_inference.py --sample-config samples.yaml --config model_a.yaml model_b.yaml --branch-name score_a score_b --output-dir scores --write
76
- python jobs/inference/run_inference.py --sample-config samples.yaml --config-dir configs/run_3_ttH --output-dir scores --write --test
77
- ```
78
-
79
- Wrapper behavior:
80
-
81
- - `--sample-config` discovers target ROOT files from every dataset's `args.raw_dir` and `args.file_names`.
82
- - `--target` accepts explicit files or glob patterns.
83
- - `--config-dir` discovers model configs from `*.yaml`; `--config` accepts explicit config files.
84
- - Branch names default to each model config's `Training_Name` plus `_score`.
85
- - `--branch-name` may override branch names, but the count must match the config count.
86
- - `--test` prints the planned `scripts/inference.py` commands without running them.
87
- - With MPI, tasks are split by target file across ranks; without MPI it runs serially.
88
- - GPU assignment is local rank modulo 4 through `CUDA_VISIBLE_DEVICES`.
89
-
90
- ## Repo-specific behavior
91
-
92
- - The first config's first dataset is used as the template dataset. The script rewrites `raw_dir`, `file_names`, `save_dir`, `chunks`, `process_chunks`, and optionally `tree_name` at runtime.
93
- - Pass `--tree <name>` if the ROOT tree name differs from the config default.
94
- - Chunked inference writes per-chunk outputs; merging those outputs is a separate step.
95
- - Job wrappers should derive the repo root from their own path; avoid adding hard-coded checkout paths.
96
-
97
- ## Audit the outputs
98
-
99
- Start with basic runtime evidence if you have a log:
100
-
101
- - `Writing to file`
102
- - `Input entries:`
103
- - `Output entries:`
104
- - `Wrote scores to`
105
- - absence of `Traceback`
106
-
107
- For ROOT outputs, prefer `uproot`:
108
-
109
- ```bash
110
- python - <<'PY'
111
- import numpy as np
112
- import uproot
113
- path = "<output.root>"
114
- branches = ["<score_branch>"]
115
- tree = uproot.open(path)["output"]
116
- print("entries", tree.num_entries)
117
- for branch in branches:
118
- arr = tree[branch].array(library="np")
119
- print(branch, len(arr), np.isnan(arr).sum(), float(np.nanmin(arr)), float(np.nanmax(arr)), float(np.nanmean(arr)))
120
- PY
121
- ```
122
-
123
- Treat inference as valid only if:
124
-
125
- - the destination file exists
126
- - every requested score branch exists
127
- - output entry count matches the input tree
128
- - score arrays contain no NaNs
129
- - score arrays are not constant
130
-
131
- For multi-model inference, every branch must exist and branch statistics should usually differ unless the models are intentionally identical.
132
-
133
- Without `--write`, inspect the `.npz` keys `scores`, `labels`, and `tracking_info` and verify array lengths and NaN counts.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-plotting/SKILL.md DELETED
@@ -1,80 +0,0 @@
1
- ---
2
- name: root-gnn-dgl-plotting
3
- description: Use when the user asks to plot training curves, regenerate training.png, build the sweep PDF from plotting/training_performance.py, compare training runs, or extract Loss, Accuracy, Test_Loss, Test_AUC, and timing information from training.log files in root_gnn_dgl.
4
- ---
5
-
6
- # root-gnn-dgl-plotting
7
-
8
- Use this skill when the task is about plots or metrics derived from `training.log`.
9
-
10
- ## Single-run plot regeneration
11
-
12
- The training script can regenerate the per-run PNG directly:
13
-
14
- ```bash
15
- python scripts/training_script.py --config <config.yaml> --plot
16
- ```
17
-
18
- That uses `root_gnn_base.utils.read_log()` and `root_gnn_base.utils.plot_log()` to rebuild `training.png` from `Training_Directory/training.log`.
19
-
20
- `plot_log()` produces a 2x2 figure with:
21
-
22
- - cumulative time in seconds
23
- - train and test loss
24
- - accuracy
25
- - test AUC
26
-
27
- Be aware that `plot_log()` fixes the accuracy axis to `(0.44, 0.56)`, which may be too narrow for some runs.
28
-
29
- ## Sweep-level plotting
30
-
31
- Use the dedicated plotting script when the user wants a PDF comparing shipped sweeps:
32
-
33
- ```bash
34
- python plotting/training_performance.py
35
- python plotting/training_performance.py --output <output.pdf>
36
- ```
37
-
38
- The script currently plots two config groups:
39
-
40
- - `pretraining`
41
- - `higgs_production`
42
-
43
- It writes one PDF page per group and resolves each run's `Training_Directory` from its config.
44
-
45
- ## What the plotting script reads from training.log
46
-
47
- `plotting/training_performance.py` parses rows that start with `Epoch` and extracts:
48
-
49
- - `Epoch`
50
- - `Loss`
51
- - `Accuracy`
52
- - `Test_Loss`
53
- - `Test_AUC`
54
- - `Time`
55
-
56
- It also computes cumulative time in hours.
57
-
58
- Baseline runs are drawn as lines. Non-baseline sweep variants are drawn as point clouds and labeled by the parameter change relative to the baseline.
59
-
60
- ## Audit the log before plotting
61
-
62
- Treat plotting input as valid only if:
63
-
64
- - `training.log` exists
65
- - it contains at least one valid `Epoch ...` row
66
- - parsed metric arrays are finite
67
- - the referenced `Training_Directory` actually exists
68
-
69
- If the plotting script fails, inspect the log directly:
70
-
71
- ```bash
72
- sed -n '1,40p' <training_dir>/training.log
73
- tail -n 25 <training_dir>/training.log
74
- ```
75
-
76
- ## When to use which plot path
77
-
78
- - Use `--plot` on `scripts/training_script.py` when the user wants the repo's standard per-run `training.png`.
79
- - Use `plotting/training_performance.py` when the user wants a cross-run PDF for the built-in sweep groups.
80
- - If the user wants custom comparisons outside the built-in groups, start from the parsing logic in `plotting/training_performance.py` and the metric schema in `training.log`.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-training/SKILL.md DELETED
@@ -1,156 +0,0 @@
1
- ---
2
- name: root-gnn-dgl-training
3
- description: Use when the user asks to train, finetune, resume, submit, queue-check, log-check, or validate training runs in root_gnn_dgl, including scripts/training_script.py, sqs, jobs/slurm/<JOBID>.out, training.log, checkpoints, and scripts/generate_multiclass_finetuning_configs.py.
4
- ---
5
-
6
- # root-gnn-dgl-training
7
-
8
- Use this skill for any training stage in the repo, from launch through monitoring and artifact review.
9
-
10
- ## Run locally
11
-
12
- Run from the repo root:
13
-
14
- ```bash
15
- python scripts/training_script.py --config <config.yaml> --preshuffle --nocompile --lazy
16
- ```
17
-
18
- This matches the README, `run_demo.sh`, and the podman job wrappers.
19
-
20
- ## Why these defaults
21
-
22
- - `--preshuffle` uses the saved prebatched graph files created during data prep.
23
- - `--nocompile` is recommended by the README because compiled mode requires padded graphs at prep time.
24
- - `--lazy` matches the common dataset classes used by the shipped configs.
25
-
26
- ## Common runtime modes
27
-
28
- - `--restart` starts from scratch instead of resuming from the last checkpoint.
29
- - Without `--restart`, the script resumes from the last `model_epoch_<n>.pt` it finds in `Training_Directory`.
30
- - `--evaluate <epoch>` skips training and evaluates a specific checkpoint.
31
- - `--plot` regenerates `training.png` from `training.log`.
32
- - `--directory <suffix>` appends a suffix to `Training_Directory`.
33
- - `--cpu`, `--multigpu`, `--multinode`, `--statistics`, `--seed`, and `--abs` are available when needed.
34
-
35
- ## Run multiple local trainings
36
-
37
- Use the single-node launcher when queueing many config files on the current node:
38
-
39
- ```bash
40
- python jobs/training/run_parallel_trainings.py <config-or-directory> --split <K/N>
41
- ```
42
-
43
- Useful examples:
44
-
45
- ```bash
46
- python jobs/training/run_parallel_trainings.py configs/run_3_ttH --split 2/2
47
- python jobs/training/run_parallel_trainings.py configs/run_3_ttH --split 2/2 --test
48
- ```
49
-
50
- Launcher behavior:
51
-
52
- - Discovers `.yaml` files from each target directory, or accepts explicit config files.
53
- - Uses fixed local GPU slots `0,1,2,3` via `CUDA_VISIBLE_DEVICES`.
54
- - Runs `scripts/training_script.py --config <config> --preshuffle --nocompile --lazy`.
55
- - Forwards unknown args to `training_script.py`, such as `--restart` or `--seed 7`.
56
- - Rejects configs with duplicate `Training_Directory` values.
57
- - `--split K/N` selects configs by deterministic index modulo `N`; use `--split 1/2` and `--split 2/2` for two complementary halves.
58
- - `--test` prints the launch plan without starting training.
59
- - Logs go under `jobs/slurm/parallel_training_logs/<timestamp>/`.
60
-
61
- ## Submit on Perlmutter
62
-
63
- Prefer the podman path:
64
-
65
- ```bash
66
- sbatch jobs/training/podman/run_job.sh <config>
67
- ```
68
-
69
- The job wrappers derive the repo root from their own location. `jobs/training/podman/submit.sh` and `jobs/training/conda/submit.sh` accept config paths as arguments; without arguments they run their built-in default sweeps. Set `SLURM_ACCOUNT` when the cluster requires an account:
70
-
71
- ```bash
72
- SLURM_ACCOUNT=atlas sbatch jobs/training/podman/run_job.sh configs/run_3_ttH/scratch.yaml
73
- bash jobs/training/podman/submit.sh configs/run_3_ttH/scratch.yaml configs/run_3_ttH/finetuning.yaml
74
- ```
75
-
76
- For the conda wrapper, set `ROOT_GNN_CONDA_ENV` if the environment is not named `dgl`.
77
-
78
- For distributed training, pass `--multinode` and launch under an environment that sets `RANK`, `LOCAL_RANK`, and `WORLD_SIZE`.
79
-
80
- ## Preconditions
81
-
82
- - If you use `--preshuffle`, run data preparation first and confirm the graph artifacts exist.
83
- - For finetuning configs, verify that `Model.args.pretraining_path` points to an existing checkpoint before launching training.
84
- - For multinode runs, pass `--multinode` and launch under the relevant distributed job environment.
85
-
86
- ## Monitor queue and logs
87
-
88
- Check queue state:
89
-
90
- ```bash
91
- sqs -u "$USER"
92
- sqs -u "$USER" | rg "<pattern>"
93
- ```
94
-
95
- Useful interpretations:
96
-
97
- - `PD` means pending
98
- - `R` means running
99
- - `START_TIME N/A` with reason `Priority` means queued normally, not broken
100
-
101
- Once a job has a `JOBID`, inspect:
102
-
103
- ```bash
104
- sed -n '1,80p' jobs/slurm/<JOBID>.out
105
- tail -n 80 jobs/slurm/<JOBID>.out
106
- rg -n "Traceback|Error|Exception|Epoch|Epoch Done|Early Termination|Done" jobs/slurm/<JOBID>.out
107
- ```
108
-
109
- Healthy training logs usually show:
110
-
111
- - the `Executing: python -u ... scripts/training_script.py ...` line
112
- - dataset cache loads
113
- - repeated `Epoch ... | LR ... | Loss ... | Accuracy ... | Test_Loss ... | Test_AUC ... | Time ... s`
114
- - `Epoch Done.`
115
- - `Num batches trained = ...`
116
- - valid completion via `Done`, sometimes after `Early Termination at Epoch ...`
117
-
118
- Early stopping is a normal completion mode in this repo.
119
-
120
- ## Audit training artifacts
121
-
122
- Training writes into `Training_Directory`:
123
-
124
- - `config.yaml`
125
- - `model_epoch_<n>.pt`
126
- - `model_epoch_<n>.npz`
127
- - `training.log`
128
- - `training.png`
129
-
130
- Primary checks:
131
-
132
- ```bash
133
- sed -n '1,40p' <training_dir>/training.log
134
- tail -n 25 <training_dir>/training.log
135
- ```
136
-
137
- Treat the run as healthy only if:
138
-
139
- - epoch numbers increase monotonically
140
- - `Loss`, `Test_Loss`, and `Test_AUC` stay finite
141
- - the latest logged epoch has a matching `model_epoch_<n>.pt`
142
- - the run produced real epoch rows rather than stopping before training started
143
-
144
- If `training.log` grows but checkpoints stop appearing, suspect a save-path or filesystem issue.
145
-
146
- Use `python plotting/training_performance.py` or the `root-gnn-dgl-plotting` skill when you want consolidated sweep-level PDFs instead of a single-run `training.png`.
147
-
148
- ## Generate finetuning configs
149
-
150
- Use this when you want to derive `configs/higgs_production/multiclass_finetuning/*.yaml` from completed multiclass pretraining runs:
151
-
152
- ```bash
153
- python scripts/generate_multiclass_finetuning_configs.py
154
- ```
155
-
156
- Before using the generated configs, verify that the chosen best-epoch checkpoint paths still exist.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/.codex/skills/root-gnn-dgl-workflow/SKILL.md DELETED
@@ -1,68 +0,0 @@
1
- ---
2
- name: root-gnn-dgl-workflow
3
- description: Use when the user asks to run or review the full root_gnn_dgl workflow, such as run_demo.sh, a full pretraining-to-finetuning-to-inference campaign, or a stage-by-stage pass, warning, fail audit across environment setup, data preparation, training, inference, and outputs.
4
- ---
5
-
6
- # root-gnn-dgl-workflow
7
-
8
- Use this skill when the user wants an end-to-end workflow rather than a single isolated stage.
9
-
10
- ## Shipped demo
11
-
12
- Run from the repo root:
13
-
14
- ```bash
15
- source run_demo.sh
16
- ```
17
-
18
- The demo does:
19
-
20
- 1. graph prep for multiclass pretraining
21
- 2. multiclass pretraining
22
- 3. graph prep for binary classification
23
- 4. from-scratch binary training
24
- 5. finetuned binary training
25
- 6. inference with two output score branches
26
-
27
- ## Before running the workflow
28
-
29
- - Check GPU availability with `nvidia-smi` or request an interactive node with `jobs/interactive.sh`.
30
- - Confirm the target data and output directories in `run_demo.sh` exist and are writable.
31
- - Confirm `configs/stats_100K/finetuning_ttH_CP_even_vs_odd.yaml` points at the checkpoint you actually want to finetune from.
32
-
33
- ## Workflow audit order
34
-
35
- When reviewing a campaign, check stages in this order:
36
-
37
- 1. environment readiness
38
- 2. data-prep outputs
39
- 3. training submission and queue state
40
- 4. training logs and checkpoints
41
- 5. inference outputs
42
-
43
- Use the retained stage skills for each check:
44
-
45
- - `root-gnn-dgl-env-setup`
46
- - `root-gnn-dgl-data-preparation`
47
- - `root-gnn-dgl-training`
48
- - `root-gnn-dgl-inference`
49
-
50
- ## Output style
51
-
52
- Return a short status for each stage:
53
-
54
- - `pass`: evidence is consistent with a healthy stage
55
- - `warning`: stage likely worked but still needs a follow-up check
56
- - `fail`: concrete blocker or corrupted or missing artifact found
57
-
58
- Repo-specific workflow blockers:
59
-
60
- - pending jobs in `sqs` with `Priority` are waiting, not failed
61
- - missing prebatched `.bin` files block `--preshuffle` training
62
- - missing `pretraining_path` blocks finetuning
63
- - ROOT outputs without the requested score branches are inference failures even if the file exists
64
-
65
- ## When to adapt instead of sourcing the demo
66
-
67
- - If you only want one stage, call the underlying prep, training, or inference script directly.
68
- - If dataset paths, branch names, or chunk counts differ, copy the command pattern from `run_demo.sh` and adjust the values instead of editing the demo in place.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/config.yaml DELETED
@@ -1,319 +0,0 @@
1
- Datasets:
2
- SingleT_schan:
3
- args:
4
- buffer_size: 11
5
- chunks: 100
6
- file_names: SingleT_schan.root
7
- fold_var: Number
8
- label: 8
9
- name: SingleT_schan
10
- node_branch_names: &id001
11
- - - jet_pt
12
- - ele_pt
13
- - mu_pt
14
- - ph_pt
15
- - MET_met
16
- - - jet_eta
17
- - ele_eta
18
- - mu_eta
19
- - ph_eta
20
- - 0
21
- - - jet_phi
22
- - ele_phi
23
- - mu_phi
24
- - ph_phi
25
- - MET_phi
26
- - CALC_E
27
- - - jet_btag
28
- - 0
29
- - 0
30
- - 0
31
- - 0
32
- - - 0
33
- - ele_charge
34
- - mu_charge
35
- - 0
36
- - 0
37
- - NODE_TYPE
38
- node_branch_types: &id002
39
- - vector
40
- - vector
41
- - vector
42
- - vector
43
- - single
44
- node_feature_scales: &id003
45
- - 1e-1
46
- - 1
47
- - 1
48
- - 1e-1
49
- - 1
50
- - 1
51
- - 1
52
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
53
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
54
- tree_name: output
55
- weight_var: weight
56
- class: LazyDataset
57
- folding: &id004
58
- n_folds: 10
59
- test:
60
- - 0
61
- - 1
62
- train:
63
- - 2
64
- - 3
65
- - 4
66
- - 5
67
- - 6
68
- - 7
69
- - 8
70
- - 9
71
- module: root_gnn_base.dataset
72
- padding_mode: NONE
73
- shuffle_chunks: 10
74
- VBF:
75
- args:
76
- buffer_size: 11
77
- chunks: 100
78
- file_names: VBF_NLO_inc.root
79
- fold_var: Number
80
- label: 3
81
- name: VBF
82
- node_branch_names: *id001
83
- node_branch_types: *id002
84
- node_feature_scales: *id003
85
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
86
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
87
- tree_name: output
88
- weight_var: weight
89
- class: LazyDataset
90
- folding: *id004
91
- module: root_gnn_base.dataset
92
- padding_mode: NONE
93
- shuffle_chunks: 10
94
- WH:
95
- args:
96
- buffer_size: 11
97
- chunks: 100
98
- file_names: WH_NLO_inc.root
99
- fold_var: Number
100
- label: 4
101
- name: WH
102
- node_branch_names: *id001
103
- node_branch_types: *id002
104
- node_feature_scales: *id003
105
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
106
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
107
- tree_name: output
108
- weight_var: weight
109
- class: LazyDataset
110
- folding: *id004
111
- module: root_gnn_base.dataset
112
- padding_mode: NONE
113
- shuffle_chunks: 10
114
- ZH:
115
- args:
116
- buffer_size: 11
117
- chunks: 100
118
- file_names: ZH_NLO_inc.root
119
- fold_var: Number
120
- label: 5
121
- name: ZH
122
- node_branch_names: *id001
123
- node_branch_types: *id002
124
- node_feature_scales: *id003
125
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
126
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
127
- tree_name: output
128
- weight_var: weight
129
- class: LazyDataset
130
- folding: *id004
131
- module: root_gnn_base.dataset
132
- padding_mode: NONE
133
- shuffle_chunks: 10
134
- ggF:
135
- args:
136
- buffer_size: 11
137
- chunks: 100
138
- file_names: ggF_NLO_inc.root
139
- fold_var: Number
140
- label: 2
141
- name: ggF
142
- node_branch_names: *id001
143
- node_branch_types: *id002
144
- node_feature_scales: *id003
145
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
146
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
147
- tree_name: output
148
- weight_var: weight
149
- class: LazyDataset
150
- folding: *id004
151
- module: root_gnn_base.dataset
152
- padding_mode: NONE
153
- shuffle_chunks: 10
154
- tHjb:
155
- args:
156
- buffer_size: 11
157
- chunks: 100
158
- file_names: tHjb_NLO_inc.root
159
- fold_var: Number
160
- label: 1
161
- name: tHjb
162
- node_branch_names: *id001
163
- node_branch_types: *id002
164
- node_feature_scales: *id003
165
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
166
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
167
- tree_name: output
168
- weight_var: weight
169
- class: LazyDataset
170
- folding: *id004
171
- module: root_gnn_base.dataset
172
- padding_mode: NONE
173
- shuffle_chunks: 10
174
- ttH:
175
- args:
176
- buffer_size: 11
177
- chunks: 100
178
- file_names: ttH_NLO_inc.root
179
- fold_var: Number
180
- label: 0
181
- name: ttH
182
- node_branch_names: *id001
183
- node_branch_types: *id002
184
- node_feature_scales: *id003
185
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
186
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
187
- tree_name: output
188
- weight_var: weight
189
- class: LazyDataset
190
- folding: *id004
191
- module: root_gnn_base.dataset
192
- padding_mode: NONE
193
- shuffle_chunks: 10
194
- ttW:
195
- args:
196
- buffer_size: 11
197
- chunks: 100
198
- file_names: ttW.root
199
- fold_var: Number
200
- label: 10
201
- name: ttW
202
- node_branch_names: *id001
203
- node_branch_types: *id002
204
- node_feature_scales: *id003
205
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
206
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
207
- tree_name: output
208
- weight_var: weight
209
- class: LazyDataset
210
- folding: *id004
211
- module: root_gnn_base.dataset
212
- padding_mode: NONE
213
- shuffle_chunks: 10
214
- ttbar:
215
- args:
216
- buffer_size: 11
217
- chunks: 100
218
- file_names: ttbar.root
219
- fold_var: Number
220
- label: 9
221
- name: ttbar
222
- node_branch_names: *id001
223
- node_branch_types: *id002
224
- node_feature_scales: *id003
225
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
226
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
227
- tree_name: output
228
- weight_var: weight
229
- class: LazyDataset
230
- folding: *id004
231
- module: root_gnn_base.dataset
232
- padding_mode: NONE
233
- shuffle_chunks: 10
234
- ttt:
235
- args:
236
- buffer_size: 11
237
- chunks: 100
238
- file_names: ttt.root
239
- fold_var: Number
240
- label: 11
241
- name: ttt
242
- node_branch_names: *id001
243
- node_branch_types: *id002
244
- node_feature_scales: *id003
245
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
246
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
247
- tree_name: output
248
- weight_var: weight
249
- class: LazyDataset
250
- folding: *id004
251
- module: root_gnn_base.dataset
252
- padding_mode: NONE
253
- shuffle_chunks: 10
254
- tttt:
255
- args:
256
- buffer_size: 11
257
- chunks: 100
258
- file_names: tttt.root
259
- fold_var: Number
260
- label: 7
261
- name: tttt
262
- node_branch_names: *id001
263
- node_branch_types: *id002
264
- node_feature_scales: *id003
265
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
266
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
267
- tree_name: output
268
- weight_var: weight
269
- class: LazyDataset
270
- folding: *id004
271
- module: root_gnn_base.dataset
272
- padding_mode: NONE
273
- shuffle_chunks: 10
274
- ttyy:
275
- args:
276
- buffer_size: 11
277
- chunks: 100
278
- file_names: ttyy.root
279
- fold_var: Number
280
- label: 6
281
- name: ttyy_ch
282
- node_branch_names: *id001
283
- node_branch_types: *id002
284
- node_feature_scales: *id003
285
- raw_dir: /global/cfs/projectdirs/atlas/joshua/root_gnn/root_gnn_dgl/data/ntuples/Hyy_pretraining/
286
- save_dir: /pscratch/sd/j/joshuaho/root_gnn/root_gnn_dgl/data/processed_pretraining_multiclass_12_process/
287
- tree_name: output
288
- weight_var: weight
289
- class: LazyDataset
290
- folding: *id004
291
- module: root_gnn_base.dataset
292
- padding_mode: NONE
293
- shuffle_chunks: 10
294
- Loss:
295
- args: {}
296
- class: CrossEntropyLoss
297
- finish:
298
- args:
299
- dim: 1
300
- class: Softmax
301
- module: torch.nn
302
- module: torch.nn
303
- Model:
304
- args:
305
- dropout: 0
306
- hid_size: 64
307
- in_size: 7
308
- n_layers: 4
309
- n_proc_steps: 4
310
- out_size: 12
311
- class: Edge_Network
312
- module: models.GCN
313
- Training:
314
- batch_size: 1024
315
- epochs: 100
316
- gamma: 0.99
317
- learning_rate: 0.0001
318
- Training_Directory: trainings/pretraining_multiclass/multiclass_12_process/
319
- Training_Name: multiclass_12_process
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_0.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:f13357759bae5ee14a4dd682fe867c09381aed0842ed712b0c12770aa621dd44
3
- size 1707210
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_1.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:9617167a01ee1d11ba2ec03f71440bd4fa25f4fa5f32e72bd9d71f5d10cd11b2
3
- size 1707210
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_10.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:5224b15282d60c8f09724904e5a665f27426d81ff577b234d901260050cb97e7
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_11.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a2fd4704348456e2a6c9655ee3aa2a9143df3f8394ba0d745207d46af139b558
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_12.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:6960240dabb5128be0a501077ff952a985295885fbcece663282df40bb8d4584
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_13.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:c11d6492d985d8f46b3476ed13671a3f9295269c0108dc55f491d7ef8270b901
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_14.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:15bfd6edcb5cb2d9176683d13c546cdd2115eda320ddbc9a04b23dba8936d989
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_15.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:5697fbccabb743b9c900a9e7f1e432077879fa4f83db42385e8ad1f597844195
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_16.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:dc1818c04ee5d17f5cf05e985e69dbabf55e0c1c4c4b7c2cd29939b766350b39
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_17.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:945575404de1372332c67423b1ea681edd4445e5445a21774d517dec45341c34
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_18.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:7f8e9f611bba22ad5682c44d158de95f76c3dfc2223654a64540f91972c12ed7
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_19.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:b1957ba99bf8b1aeb6b5a19a6867433aa86e5658c3abfdb737bc16489ce3d83b
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_2.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:67b72a00c515215745d693f7f62d3a02274c8b702f53d7e68c1b162b0eee44ec
3
- size 1707210
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_20.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:38bd863cfeeb558c586b3c90686de8faf33f08b2675ca93edd4bf8ce63ba62ee
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_21.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:7e90498dffe402581e1e554870b10369c05b45aba7b98a3480422512fbd9832e
3
- size 1707502
 
 
 
 
legacy/root_gnn_dgl/Pretrained_GNN/multiclass_pretrained_model_12/model_epoch_22.pt DELETED
@@ -1,3 +0,0 @@
1
- version https://git-lfs.github.com/spec/v1
2
- oid sha256:a0ea74fb7cfbfa3ebf6908f75fd6d5e018ee307801f608ecdde228d29e666f81
3
- size 1707502