supanthadey1 commited on
Commit
22acc33
·
verified ·
1 Parent(s): cc1df70

Publish revised BERTose and BERToseBind release

Browse files

Add the verified versioned scientific release and reader-facing documentation; preserve legacy files and history.

This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. .gitattributes +2 -0
  2. README.md +14 -88
  3. releases/2026-10-02/iar_completion/requirements.txt +5 -0
  4. releases/2026-10-02/iar_completion/runtime_dependencies.json +280 -0
  5. releases/2026-10-02/iar_completion/runtime_snapshot/java/review_A5WurcsArrayTreeAudit.java +467 -0
  6. releases/2026-10-02/iar_completion/runtime_snapshot/java/review_MolWURCS.jar +3 -0
  7. releases/2026-10-02/iar_completion/runtime_snapshot/java/review_wurcsframework-1.3.1.jar +3 -0
  8. releases/2026-10-02/iar_completion/runtime_snapshot/project/bert_training_v4/downstream_tasks/utils/wurcs_bpe_tokenizer.py +740 -0
  9. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/bpe_ambiguity_tokens.json +721 -0
  10. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/bpe_vocabulary_clean.json +0 -0
  11. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_ambiguity_tokens.json +764 -0
  12. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json +0 -0
  13. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_sequence_tree_runtime.py +287 -0
  14. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_training_a5_topology.py +180 -0
  15. releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_training_a5_wurcs_bpe.py +368 -0
  16. releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/model/__init__.py +3 -0
  17. releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/model/glycan_bert.py +303 -0
  18. releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/model/multimodal_glycan_bert_v3.py +1084 -0
  19. releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/training/ipa_bpe_distillation.py +377 -0
  20. releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/glycangt_heldout_ipa_validation/cluster/heldout_ipa_eval.py +962 -0
  21. releases/2026-10-02/iar_completion/runtime_snapshot/schema/classify.py +78 -0
  22. releases/2026-10-02/iar_completion/runtime_snapshot/schema/pipelib.py +119 -0
  23. releases/2026-10-02/iar_inference/README.md +58 -0
  24. releases/2026-10-02/iar_inference/SHA256SUMS +10 -0
  25. releases/2026-10-02/iar_inference/config.json +178 -0
  26. releases/2026-10-02/iar_inference/fixtures/case0_pre_sequence_head.npy +3 -0
  27. releases/2026-10-02/iar_inference/fixtures/case1_pre_sequence_head.npy +3 -0
  28. releases/2026-10-02/iar_inference/fixtures/rebuilt_two_case_inputs.json +3718 -0
  29. releases/2026-10-02/iar_inference/fixtures/reference_smoke_report.json +253 -0
  30. releases/2026-10-02/iar_inference/infer.py +290 -0
  31. releases/2026-10-02/iar_inference/original_inference_helpers.py +97 -0
  32. releases/2026-10-02/iar_inference/preparation_validation.json +17 -0
  33. releases/2026-10-02/iar_inference/requirements.txt +3 -0
  34. releases/2026-10-02/notebooks/BERTose_workbench.ipynb +583 -0
  35. releases/2026-10-02/provenance/WA3_checkpoint_index.json +1 -0
  36. releases/2026-10-02/provenance/WA3_final_receipt.json +1 -0
  37. releases/2026-10-02/provenance/affinose_arms_on_test_20260909.json +263 -0
  38. releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_A_glycan_s42/receipt.json +101 -0
  39. releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_A_glycan_s42/results.json +339 -0
  40. releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_C_doubly_s42/receipt.json +101 -0
  41. releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_C_doubly_s42/results.json +294 -0
  42. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s43/receipt.json +101 -0
  43. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s43/results.json +294 -0
  44. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s44/receipt.json +101 -0
  45. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s44/results.json +324 -0
  46. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s43/receipt.json +101 -0
  47. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s43/results.json +234 -0
  48. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s44/receipt.json +101 -0
  49. releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s44/results.json +264 -0
  50. releases/2026-10-02/provenance/affinose_cells/affinose_sa_20260908_mo_B_protein_s42/receipt.json +101 -0
.gitattributes CHANGED
@@ -6,3 +6,5 @@
6
  *.npz filter=lfs diff=lfs merge=lfs -text
7
  *.npy filter=lfs diff=lfs merge=lfs -text
8
  data/splits/train_val_split.json filter=lfs diff=lfs merge=lfs -text
 
 
 
6
  *.npz filter=lfs diff=lfs merge=lfs -text
7
  *.npy filter=lfs diff=lfs merge=lfs -text
8
  data/splits/train_val_split.json filter=lfs diff=lfs merge=lfs -text
9
+ releases/2026-10-02/iar_completion/runtime_snapshot/java/review_MolWURCS.jar filter=lfs diff=lfs merge=lfs -text
10
+ releases/2026-10-02/iar_completion/runtime_snapshot/java/review_wurcsframework-1.3.1.jar filter=lfs diff=lfs merge=lfs -text
README.md CHANGED
@@ -2,103 +2,29 @@
2
  license: apache-2.0
3
  tags:
4
  - glycans
5
- - wurcs
6
  - bertose
7
- - affinose
8
- - reproducibility
9
- - training-code
10
- - pytorch
11
  ---
12
 
13
- # BERTose and AFFINose Training Code
14
 
15
- This repository is the public training and reproducibility companion for the BERTose and AFFINose Hugging Face release. It contains the code, configs, tokenizer assets, split files, and provenance notes needed to understand how the released checkpoints were trained and evaluated.
16
 
17
- The released inference checkpoints live separately:
18
 
19
- - BERTose glycan encoder: `supanthadey1/bertose-glycan-encoder`
20
- - BERTose IAR resolver: `supanthadey1/bertose-iar-resolver`
21
- - AFFINose interaction model: `supanthadey1/affinose-interaction-model`
22
- - Cloud inference notebook: `supanthadey1/bertose-affinose-inference`
23
-
24
- ## Repository Map
25
-
26
- | Public workflow | Files |
27
- |---|---|
28
- | BERTose multimodal pretraining | `code/training/train_multimodal.py`, `code/training/multimodal_dataset.py`, `code/training/multimodal_masking.py`, `configs/` |
29
- | BERTose WURCS-BPE tokenizer training | `code/training/train_wurcs_bpe.py`, `code/model/wurcs_bpe_tokenizer.py`, `data/vocab/` |
30
- | BERTose IAR / contrastive refinement | `code/contrastive/`, `code/contrastive_training/`, `code/tokenizer/generate_bpe_ambiguity.py` |
31
- | AFFINose interaction training | `code/affinose/README.md` |
32
- | Benchmark reproduction | `code/benchmarks/`, `code/downstream_tasks/utils/` |
33
- | Embedding and biology probes | `code/probes/` |
34
- | Split and vocabulary assets | `data/splits/`, `data/vocab/` |
35
- | Compute and lineage provenance | `provenance/` |
36
-
37
- Some executable filenames and directories retain historical names so that provenance remains traceable to the original training runs. In public-facing text, use BERTose for the glycan encoder / IAR workflows and AFFINose for the protein-glycan interaction workflow.
38
-
39
- ## What Is Included
40
-
41
- - Core BERTose architecture and dataset utilities.
42
- - BERTose multimodal pretraining entrypoint and configs.
43
- - WURCS-BPE tokenizer training and ambiguity-token generation code.
44
- - Contrastive refinement / IAR training and negative generation scripts.
45
- - AFFINose interaction-model data construction, split generation, training, and inference code.
46
- - Benchmark and probe scripts used for manuscript analyses.
47
- - Vocabulary files, ambiguity-token maps, train/validation split metadata, and leakage-exclusion lists.
48
- - Compute-provenance notes and representative launch scripts.
49
-
50
- ## What Is Not Included
51
-
52
- Large training artifacts are intentionally not bundled here:
53
-
54
- - Full pretraining corpus pickles such as `sequences_bpe.pkl`.
55
- - Multi-GB intermediate mapping files.
56
- - Full training checkpoints; released checkpoints are hosted in the separate model repositories listed above.
57
- - ESM-C protein embeddings required for AFFINose training; users should generate or provide those separately according to the ESM-C access rules.
58
-
59
- ## Quick Import Check
60
-
61
- After downloading the repository, a lightweight import check should work without access to the full training data:
62
 
63
  ```bash
64
- cd code
65
- PYTHONDONTWRITEBYTECODE=1 python - <<'PY'
66
- import importlib
67
-
68
- for module in [
69
- "training.multimodal_dataset",
70
- "training.masking",
71
- "training.multimodal_masking",
72
- "training.train_multimodal",
73
- "training.train_wurcs_bpe",
74
- "model.dataset",
75
- "downstream_tasks.utils.tokenizer",
76
- "downstream_tasks.utils.wurcs_bpe_tokenizer",
77
- "downstream_tasks.utils.dataset",
78
- ]:
79
- importlib.import_module(module)
80
- print("ok", module)
81
- PY
82
  ```
83
 
84
- Full training requires the original large data artifacts and appropriate GPU resources.
85
-
86
- ## Environment
87
-
88
- Install the core dependencies with:
89
-
90
- ```bash
91
- pip install -r requirements.txt
92
- ```
93
-
94
- Some probe scripts also use optional scientific plotting and glycan-analysis packages. See `requirements.txt` for the split between core and optional dependencies.
95
-
96
- ## Reproducibility Notes
97
-
98
- - `MANIFEST.md` documents the file provenance and model-lineage mapping.
99
- - `RELEASE_AUDIT.md` documents the public-release state, fixed gaps, verification checks, and known scope limits.
100
- - Cluster launch scripts under `provenance/compute_provenance/` preserve the original compute context. They are provenance records, not portable one-command launchers for every environment.
101
 
102
- ## License And Citation
103
 
104
- This repository is released under the Apache License 2.0 as a research reproducibility companion for the BERTose and AFFINose manuscript. See `LICENSE`. Third-party dependencies and external models, including EvolutionaryScale ESM-C, retain their own licenses and access terms. Please cite the associated manuscript once available.
 
2
  license: apache-2.0
3
  tags:
4
  - glycans
 
5
  - bertose
 
 
 
 
6
  ---
7
 
8
+ # BERTose and BERToseBind shared code
9
 
10
+ This repository preserves the original submission and provides the revised code once under [`releases/2026-10-02/`](releases/2026-10-02/README.md).
11
 
12
+ Start with the [shared workbench notebook](https://huggingface.co/supanthadey1/bertose-affinose-inference/blob/main/BERTose_workbench.ipynb) for sequence embeddings, BERToseBind prediction and supported IAR completion. The notebook uses this same code package and downloads only the required separately supplied model/data assets. You can download the notebook from its Hub page and open it in Jupyter or upload it into Google Colab.
13
 
14
+ ## Install locally
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
15
 
16
  ```bash
17
+ python3.12 -m venv .venv
18
+ source .venv/bin/activate
19
+ python -m pip install huggingface_hub==1.1.5
20
+ hf download supanthadey1/bertose-affinose-training-code --repo-type model --revision main \
21
+ --include "releases/2026-10-02/*" --local-dir shared-code
22
+ cd shared-code/releases/2026-10-02
23
+ python -m pip install -r requirements-workbench.txt
 
 
 
 
 
 
 
 
 
 
 
24
  ```
25
 
26
+ See the [package README](releases/2026-10-02/README.md) for the task commands and [reproduction instructions](releases/2026-10-02/docs/reproduction.md) for training entrypoints, additional input contracts and compute requirements. The complete sources, configurations, figures and execution evidence are preserved here. Portable inference checks do not establish a complete portable training or benchmark rerun.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
27
 
28
+ Model weights remain versioned under `releases/2026-09-28/` in the [encoder](https://huggingface.co/supanthadey1/bertose-glycan-encoder), [BERToseBind](https://huggingface.co/supanthadey1/affinose-interaction-model) and [IAR](https://huggingface.co/supanthadey1/bertose-iar-resolver) repositories. Shared data remain in [GlyCorpus](https://huggingface.co/datasets/supanthadey1/glycorpus). Their scientific-file hashes are recorded in the asset downloader configuration.
29
 
30
+ Original code uses Apache-2.0 with preserved third-party notices and exceptions. Revised weights use Apache-2.0. Author-generated data use CC-BY-4.0; inherited inputs and ESM-C dependencies retain their own terms. See the versioned LICENSE, NOTICE and provenance manifest.
releases/2026-10-02/iar_completion/requirements.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # Local artifact checks: Python 3.12.4, these already installed versions.
2
+ # Historical final job: Python 3.10.16, torch 2.9.1+cu128; NumPy unrecorded.
3
+ torch==2.9.1
4
+ numpy==1.26.4
5
+ tqdm>=4.0
releases/2026-10-02/iar_completion/runtime_dependencies.json ADDED
@@ -0,0 +1,280 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "scope": "Static dependency audit of the unchanged final entrypoint; no full pipeline replay",
3
+ "final_source": "originals/wa3_final_iar_readout_all.py",
4
+ "top_level_external_packages": [
5
+ "numpy",
6
+ "torch"
7
+ ],
8
+ "transitive_direct_packages": [
9
+ "tqdm (submitted WA0 harness and distiller)"
10
+ ],
11
+ "external_executables": [
12
+ "java",
13
+ "javac"
14
+ ],
15
+ "imports": [
16
+ {
17
+ "module": "__future__",
18
+ "names": [
19
+ "annotations"
20
+ ],
21
+ "line": 111
22
+ },
23
+ {
24
+ "module": "base64",
25
+ "line": 113
26
+ },
27
+ {
28
+ "module": "builtins",
29
+ "line": 114
30
+ },
31
+ {
32
+ "module": "collections",
33
+ "line": 115
34
+ },
35
+ {
36
+ "module": "csv",
37
+ "line": 116
38
+ },
39
+ {
40
+ "module": "functools",
41
+ "line": 117
42
+ },
43
+ {
44
+ "module": "gzip",
45
+ "line": 118
46
+ },
47
+ {
48
+ "module": "hashlib",
49
+ "line": 119
50
+ },
51
+ {
52
+ "module": "importlib.util",
53
+ "line": 120
54
+ },
55
+ {
56
+ "module": "json",
57
+ "line": 121
58
+ },
59
+ {
60
+ "module": "os",
61
+ "line": 122
62
+ },
63
+ {
64
+ "module": "platform",
65
+ "line": 123
66
+ },
67
+ {
68
+ "module": "re",
69
+ "line": 124
70
+ },
71
+ {
72
+ "module": "shutil",
73
+ "line": 125
74
+ },
75
+ {
76
+ "module": "subprocess",
77
+ "line": 126
78
+ },
79
+ {
80
+ "module": "sys",
81
+ "line": 127
82
+ },
83
+ {
84
+ "module": "time",
85
+ "line": 128
86
+ },
87
+ {
88
+ "module": "pathlib",
89
+ "names": [
90
+ "Path"
91
+ ],
92
+ "line": 129
93
+ },
94
+ {
95
+ "module": "numpy",
96
+ "line": 131
97
+ },
98
+ {
99
+ "module": "torch",
100
+ "line": 132
101
+ },
102
+ {
103
+ "module": "frozen_training.a5_wurcs_bpe",
104
+ "line": 339
105
+ },
106
+ {
107
+ "module": "frozen_tree_runtime",
108
+ "line": 340
109
+ },
110
+ {
111
+ "module": "pipelib",
112
+ "names": [
113
+ "parse_record",
114
+ "SIDE_RE",
115
+ "node_index"
116
+ ],
117
+ "line": 521
118
+ },
119
+ {
120
+ "module": "classify",
121
+ "names": [
122
+ "PLAIN_LINK"
123
+ ],
124
+ "line": 522
125
+ }
126
+ ],
127
+ "path_bindings": [
128
+ {
129
+ "variable": "PROJ",
130
+ "source_expression": "Path(\"/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training\")",
131
+ "line": 137
132
+ },
133
+ {
134
+ "variable": "PKGD",
135
+ "source_expression": "PROJ / \"data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1\"",
136
+ "line": 138
137
+ },
138
+ {
139
+ "variable": "WA0_HARNESS",
140
+ "source_expression": "PROJ / \"writing/glycangt_heldout_ipa_validation/cluster/heldout_ipa_eval.py\"",
141
+ "line": 139
142
+ },
143
+ {
144
+ "variable": "WA0_VOCAB",
145
+ "source_expression": "PROJ / \"data/bpe_vocabulary_clean.json\"",
146
+ "line": 140
147
+ },
148
+ {
149
+ "variable": "WA0_AMB",
150
+ "source_expression": "PROJ / \"data/bpe_ambiguity_tokens.json\"",
151
+ "line": 141
152
+ },
153
+ {
154
+ "variable": "WA0_CKPT_PATH",
155
+ "source_expression": "PROJ / \"checkpoints_v5b_excluded/best_v5b_excluded_model.pt\"",
156
+ "line": 142
157
+ },
158
+ {
159
+ "variable": "BENCH_DIR",
160
+ "source_expression": "PROJ / \"writing/glycangt_heldout_ipa_validation\"",
161
+ "line": 143
162
+ },
163
+ {
164
+ "variable": "SCHEMA_DIR",
165
+ "source_expression": "Path(\"/work/ratul1/supantha/glycan-SD-VS/review/iar_pipe_schema_20260823_r1\")",
166
+ "line": 144
167
+ },
168
+ {
169
+ "variable": "JAVA_BIN",
170
+ "source_expression": "Path(\"/work/ratul1/supantha/miniconda3/envs/DeepGlycanSite/bin\")",
171
+ "line": 145
172
+ },
173
+ {
174
+ "variable": "ROOT",
175
+ "source_expression": "Path(\"/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc\")",
176
+ "line": 146
177
+ },
178
+ {
179
+ "variable": "TRAIN_MOD",
180
+ "source_expression": "ROOT / (\"writing/review/multimodal/runtime/md_temporal_20x6_end_to_end_20260802_r3_native_intersection/\"\n \"run_review_md_temporal_20x6_training_r3.py\")",
181
+ "line": 147
182
+ },
183
+ {
184
+ "variable": "CK",
185
+ "source_expression": "ROOT / (\"writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/\"\n \"review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/\"\n \"review_md20x6_checkpoint_best.pt\")",
186
+ "line": 149
187
+ },
188
+ {
189
+ "variable": "WORK",
190
+ "source_expression": "Path(os.environ[\"WORK\"])",
191
+ "line": 154
192
+ },
193
+ {
194
+ "variable": "OUT",
195
+ "source_expression": "WORK / \"outputs\"",
196
+ "line": 156
197
+ },
198
+ {
199
+ "variable": "JAVA_DIR",
200
+ "source_expression": "WORK / \"inputs/java\"",
201
+ "line": 157
202
+ },
203
+ {
204
+ "variable": "SETS",
205
+ "source_expression": "{\n \"bench1392\": {\"runs\": BENCH_RUNS, \"wa0_root\": BENCH_DIR / \"outputs\",\n \"baseline_runs\": [\"benchmark1392_residue_full\", \"benchmark1392_linkage_full\"]},\n \"seal162\": {\"runs\": SEAL_RUNS, \"wa0_root\": WORK / \"inputs/wa0_seal\",\n \"baseline_runs\": [\"seal162_residue_t080\", \"seal162_linkage_t080\"]},\n}",
206
+ "line": 182
207
+ }
208
+ ],
209
+ "dynamic_imports": [
210
+ {
211
+ "entry": "load_wa0_evaluator",
212
+ "path_variable": "WA0_HARNESS",
213
+ "included": "runtime_snapshot/project/writing/glycangt_heldout_ipa_validation/cluster/heldout_ipa_eval.py",
214
+ "transitive": [
215
+ "writing/cluster/nature_submission/code/training/ipa_bpe_distillation.py",
216
+ "writing/cluster/nature_submission/code/model/__init__.py",
217
+ "writing/cluster/nature_submission/code/model/multimodal_glycan_bert_v3.py",
218
+ "writing/cluster/nature_submission/code/model/glycan_bert.py",
219
+ "bert_training_v4/downstream_tasks/utils/wurcs_bpe_tokenizer.py"
220
+ ],
221
+ "scope": "Original harness imported and initialized with identity model in smoke check"
222
+ },
223
+ {
224
+ "entry": "build_wa3_tokenizer",
225
+ "included": "runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1",
226
+ "transitive": [
227
+ "three frozen Python sources",
228
+ "review_bpe_vocabulary.json",
229
+ "review_bpe_ambiguity_tokens.json",
230
+ "bert_training_v4/downstream_tasks/utils/wurcs_bpe_tokenizer.py"
231
+ ],
232
+ "scope": "Imports through generated temporary shim verified"
233
+ },
234
+ {
235
+ "entry": "load_achem",
236
+ "included": "runtime_snapshot/schema",
237
+ "transitive": [
238
+ "pipelib.py",
239
+ "classify.py"
240
+ ],
241
+ "scope": "Imports verified; these dependencies were not hash-pinned by final job"
242
+ },
243
+ {
244
+ "entry": "load_model",
245
+ "path_variable": "TRAIN_MOD",
246
+ "in_surrounding_code_package": "../bertose_source/writing/review/multimodal/runtime/md_temporal_20x6_end_to_end_20260802_r3_native_intersection/run_review_md_temporal_20x6_training_r3.py",
247
+ "transitive": "Native runtime imports and authority receipts in ../bertose_source; additional immutable external workspace inputs remain required",
248
+ "scope": "Not called in local checks because it loads the full encoder checkpoint"
249
+ }
250
+ ],
251
+ "external_data": [
252
+ {
253
+ "resource": "WA3 checkpoint",
254
+ "sha256": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
255
+ "included": false
256
+ },
257
+ {
258
+ "resource": "Parent index and six training arrays",
259
+ "details": "provenance/required_input_hashes.json",
260
+ "included": false
261
+ },
262
+ {
263
+ "resource": "Benchmark and grey-seal CSVs",
264
+ "details": "provenance/final_run/run_manifest.json inputs",
265
+ "included": false
266
+ },
267
+ {
268
+ "resource": "Four original WA0 case_details.jsonl and summary.md pairs",
269
+ "runs": [
270
+ "benchmark1392_residue_full",
271
+ "benchmark1392_linkage_full",
272
+ "seal162_residue_t080",
273
+ "seal162_linkage_t080"
274
+ ],
275
+ "included": false
276
+ }
277
+ ],
278
+ "lineage_sources_are_runtime_imports": false,
279
+ "java_helper_compilation_checked": false
280
+ }
releases/2026-10-02/iar_completion/runtime_snapshot/java/review_A5WurcsArrayTreeAudit.java ADDED
@@ -0,0 +1,467 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ package review.a5;
2
+
3
+ import java.io.BufferedReader;
4
+ import java.io.InputStreamReader;
5
+ import java.nio.charset.StandardCharsets;
6
+ import java.security.MessageDigest;
7
+ import java.util.ArrayDeque;
8
+ import java.util.ArrayList;
9
+ import java.util.Base64;
10
+ import java.util.Collections;
11
+ import java.util.HashMap;
12
+ import java.util.HashSet;
13
+ import java.util.LinkedHashMap;
14
+ import java.util.List;
15
+ import java.util.Map;
16
+ import java.util.Queue;
17
+ import java.util.Set;
18
+
19
+ import org.glycoinfo.WURCSFramework.util.array.WURCSImporter;
20
+ import org.glycoinfo.WURCSFramework.wurcs.array.GLIP;
21
+ import org.glycoinfo.WURCSFramework.wurcs.array.GLIPs;
22
+ import org.glycoinfo.WURCSFramework.wurcs.array.LIN;
23
+ import org.glycoinfo.WURCSFramework.wurcs.array.RES;
24
+ import org.glycoinfo.WURCSFramework.wurcs.array.UniqueRES;
25
+ import org.glycoinfo.WURCSFramework.wurcs.array.WURCSArray;
26
+
27
+ /**
28
+ * Original-axis official WURCS adjudicator.
29
+ *
30
+ * <p>This helper deliberately uses WURCSImporter/WURCSArray rather than a
31
+ * normalized WURCSGraph. RES list order is the sequence residue axis and
32
+ * every GLIP carries its explicit RESIndex, so no normalized-backbone
33
+ * permutation is applied to depth or topology targets.</p>
34
+ */
35
+ public final class A5WurcsArrayTreeAudit {
36
+ private A5WurcsArrayTreeAudit() {}
37
+
38
+ private static final Base64.Decoder B64_DECODER = Base64.getUrlDecoder();
39
+ private static final Base64.Encoder B64_ENCODER =
40
+ Base64.getUrlEncoder().withoutPadding();
41
+
42
+ private static String b64(String value) {
43
+ return B64_ENCODER.encodeToString(value.getBytes(StandardCharsets.UTF_8));
44
+ }
45
+
46
+ private static String sha256(String value) throws Exception {
47
+ byte[] digest = MessageDigest.getInstance("SHA-256")
48
+ .digest(value.getBytes(StandardCharsets.UTF_8));
49
+ StringBuilder result = new StringBuilder(64);
50
+ for (byte item : digest) {
51
+ result.append(String.format("%02x", item & 0xff));
52
+ }
53
+ return result.toString();
54
+ }
55
+
56
+ private static boolean probabilityIsExact(GLIP linkage) {
57
+ return linkage.getBackboneProbabilityLower() == 1.0
58
+ && linkage.getBackboneProbabilityUpper() == 1.0
59
+ && linkage.getModificationProbabilityLower() == 1.0
60
+ && linkage.getModificationProbabilityUpper() == 1.0;
61
+ }
62
+
63
+ private static String pairKey(int left, int right) {
64
+ return Math.min(left, right) + ":" + Math.max(left, right);
65
+ }
66
+
67
+ private static String process(String rowIndex, String wurcs) throws Exception {
68
+ WURCSImporter importer = new WURCSImporter();
69
+ WURCSArray array = importer.extractWURCSArray(wurcs);
70
+ List<RES> residues = array.getRESs();
71
+ List<UniqueRES> uniqueResidues = array.getUniqueRESs();
72
+ List<LIN> linkages = array.getLINs();
73
+ int residueCount = residues.size();
74
+ if (residueCount == 0
75
+ || residueCount != array.getRESCount()
76
+ || linkages.size() != array.getLINCount()
77
+ || uniqueResidues.size() != array.getUniqueRESCount()) {
78
+ throw new IllegalStateException("official WURCSArray cardinality mismatch");
79
+ }
80
+
81
+ Map<String, Integer> residueIndex = new LinkedHashMap<>();
82
+ int[] anomericPositions = new int[residueCount];
83
+ List<String> residueLabels = new ArrayList<>();
84
+ List<String> uniqueIds = new ArrayList<>();
85
+ for (int index = 0; index < residueCount; index++) {
86
+ RES residue = residues.get(index);
87
+ String label = residue.getRESIndex();
88
+ if (label == null || label.isEmpty()
89
+ || residueIndex.put(label, index) != null) {
90
+ throw new IllegalStateException(
91
+ "official RES indices are blank or duplicated"
92
+ );
93
+ }
94
+ int uniqueId = residue.getUniqueRESID();
95
+ if (uniqueId < 1 || uniqueId > uniqueResidues.size()) {
96
+ throw new IllegalStateException(
97
+ "official RES references an invalid UniqueRES"
98
+ );
99
+ }
100
+ anomericPositions[index] =
101
+ uniqueResidues.get(uniqueId - 1).getAnomericPosition();
102
+ residueLabels.add(label);
103
+ uniqueIds.add(Integer.toString(uniqueId));
104
+ }
105
+
106
+ List<Set<Integer>> adjacency = new ArrayList<>();
107
+ List<List<Integer>> directedChildren = new ArrayList<>();
108
+ for (int index = 0; index < residueCount; index++) {
109
+ adjacency.add(new HashSet<>());
110
+ directedChildren.add(new ArrayList<>());
111
+ }
112
+ Map<Integer, Integer> childParent = new HashMap<>();
113
+ Set<String> seenPairs = new HashSet<>();
114
+ List<String> pairRecords = new ArrayList<>();
115
+ int connectorCount = 0;
116
+ int monovalentCount = 0;
117
+ int repeatCount = 0;
118
+ int alternativeGroupCount = 0;
119
+ int residueIdentityAmbiguousGroupCount = 0;
120
+ int sameResidueAlternativeGroupCount = 0;
121
+ int groupedPositionGroupCount = 0;
122
+ int probabilityEndpointCount = 0;
123
+ int unknownBackbonePositionCount = 0;
124
+ int unknownModificationPositionCount = 0;
125
+ int unknownDirectionCount = 0;
126
+ int mapConnectorCount = 0;
127
+ int hyperedgeCount = 0;
128
+ int duplicatePairCount = 0;
129
+ int orientationFailureCount = 0;
130
+ int groupedOrientationNonInvariantCount = 0;
131
+ String topologyBlocker = "";
132
+ String rootedBlocker = "";
133
+ StringBuilder serialization = new StringBuilder();
134
+
135
+ if (array.isComposition()) {
136
+ topologyBlocker = "official_composition_record";
137
+ }
138
+
139
+ for (int linkageIndex = 0;
140
+ linkageIndex < linkages.size();
141
+ linkageIndex++) {
142
+ LIN linkage = linkages.get(linkageIndex);
143
+ if (linkage.isRepeatingUnit()) {
144
+ repeatCount++;
145
+ }
146
+ Map<Integer, List<GLIP>> byResidue = new LinkedHashMap<>();
147
+ boolean groupIdentityUncertain = false;
148
+ boolean connectorProbability = false;
149
+ for (GLIPs group : linkage.getListOfGLIPs()) {
150
+ boolean officialAlternative =
151
+ group.getAlternativeType() != null;
152
+ if (officialAlternative) {
153
+ alternativeGroupCount++;
154
+ }
155
+ if (group.getGLIPs().size() > 1) {
156
+ groupedPositionGroupCount++;
157
+ }
158
+ Set<Integer> groupResidues = new HashSet<>();
159
+ for (GLIP endpoint : group.getGLIPs()) {
160
+ Integer index = residueIndex.get(endpoint.getRESIndex());
161
+ if (index == null) {
162
+ throw new IllegalStateException(
163
+ "official GLIP references an unknown RESIndex"
164
+ );
165
+ }
166
+ groupResidues.add(index);
167
+ byResidue.computeIfAbsent(
168
+ index, ignored -> new ArrayList<>()
169
+ ).add(endpoint);
170
+ if (!probabilityIsExact(endpoint)) {
171
+ probabilityEndpointCount++;
172
+ connectorProbability = true;
173
+ }
174
+ if (endpoint.getBackbonePosition() < 1) {
175
+ unknownBackbonePositionCount++;
176
+ }
177
+ if (endpoint.getModificationPosition() < 0) {
178
+ unknownModificationPositionCount++;
179
+ }
180
+ char direction = endpoint.getBackboneDirection();
181
+ if (direction == '?' || direction == 'x'
182
+ || direction == 'X') {
183
+ unknownDirectionCount++;
184
+ }
185
+ }
186
+ if (groupResidues.size() != 1) {
187
+ groupIdentityUncertain = true;
188
+ residueIdentityAmbiguousGroupCount++;
189
+ } else if (officialAlternative) {
190
+ /*
191
+ * Official ``{``/``}`` marks an alternative lead-out or
192
+ * lead-in GLIPs group. It does not by itself make the
193
+ * residue identity uncertain. If every endpoint in the
194
+ * group names the same RES, undirected residue adjacency
195
+ * remains exact; only attachment chemistry and, unless
196
+ * every endpoint has the same donor status, rooting are
197
+ * uncertain.
198
+ */
199
+ sameResidueAlternativeGroupCount++;
200
+ }
201
+ }
202
+
203
+ int degree = byResidue.size();
204
+ if (degree <= 1) {
205
+ monovalentCount++;
206
+ if (linkage.isRepeatingUnit() && topologyBlocker.isEmpty()) {
207
+ topologyBlocker = "official_repeat_record";
208
+ }
209
+ continue;
210
+ }
211
+ if (linkage.isRepeatingUnit() && topologyBlocker.isEmpty()) {
212
+ topologyBlocker = "official_repeat_connector";
213
+ }
214
+ if (groupIdentityUncertain && topologyBlocker.isEmpty()) {
215
+ topologyBlocker = "residue_identity_ambiguous_connector";
216
+ }
217
+ if (degree != 2) {
218
+ hyperedgeCount++;
219
+ if (topologyBlocker.isEmpty()) {
220
+ topologyBlocker = "connector_residue_degree_not_two";
221
+ }
222
+ continue;
223
+ }
224
+ if (connectorProbability && topologyBlocker.isEmpty()) {
225
+ topologyBlocker = "probabilistic_connector_endpoint";
226
+ }
227
+
228
+ List<Integer> linked = new ArrayList<>(byResidue.keySet());
229
+ Collections.sort(linked);
230
+ int left = linked.get(0);
231
+ int right = linked.get(1);
232
+ String pair = pairKey(left, right);
233
+ pairRecords.add(left + "-" + right);
234
+ connectorCount++;
235
+ if (!seenPairs.add(pair)) {
236
+ duplicatePairCount++;
237
+ if (topologyBlocker.isEmpty()) {
238
+ topologyBlocker = "duplicate_residue_pair_connector";
239
+ }
240
+ }
241
+ adjacency.get(left).add(right);
242
+ adjacency.get(right).add(left);
243
+ String mapCode = linkage.getMAPCode();
244
+ if (mapCode != null && !mapCode.isEmpty()) {
245
+ mapConnectorCount++;
246
+ }
247
+
248
+ Map<Integer, Boolean> donorByResidue = new HashMap<>();
249
+ boolean orientationConsistent = true;
250
+ for (int residue : linked) {
251
+ Boolean donor = null;
252
+ boolean grouped = byResidue.get(residue).size() > 1;
253
+ for (GLIP endpoint : byResidue.get(residue)) {
254
+ int position = endpoint.getBackbonePosition();
255
+ int anomeric = anomericPositions[residue];
256
+ if (position < 1 || anomeric < 0) {
257
+ orientationConsistent = false;
258
+ continue;
259
+ }
260
+ boolean endpointDonor = anomeric > 0
261
+ && position == anomeric;
262
+ if (donor != null
263
+ && donor.booleanValue() != endpointDonor) {
264
+ orientationConsistent = false;
265
+ if (grouped) {
266
+ groupedOrientationNonInvariantCount++;
267
+ }
268
+ }
269
+ donor = endpointDonor;
270
+ }
271
+ if (donor == null) {
272
+ orientationConsistent = false;
273
+ } else {
274
+ donorByResidue.put(residue, donor);
275
+ }
276
+ }
277
+ boolean leftDonor = Boolean.TRUE.equals(donorByResidue.get(left));
278
+ boolean rightDonor = Boolean.TRUE.equals(donorByResidue.get(right));
279
+ if (!orientationConsistent || leftDonor == rightDonor) {
280
+ orientationFailureCount++;
281
+ if (rootedBlocker.isEmpty()) {
282
+ rootedBlocker = "official_donor_not_unique";
283
+ }
284
+ } else {
285
+ int child = leftDonor ? left : right;
286
+ int parent = leftDonor ? right : left;
287
+ Integer previous = childParent.putIfAbsent(child, parent);
288
+ if (previous != null && previous.intValue() != parent) {
289
+ orientationFailureCount++;
290
+ if (rootedBlocker.isEmpty()) {
291
+ rootedBlocker = "residue_has_multiple_parents";
292
+ }
293
+ }
294
+ directedChildren.get(parent).add(child);
295
+ }
296
+ serialization.append(linkageIndex).append(':')
297
+ .append(left).append('-').append(right).append(':')
298
+ .append(leftDonor).append('-').append(rightDonor).append(':')
299
+ .append(mapCode == null ? "NULL" : mapCode).append('\n');
300
+ }
301
+
302
+ if (repeatCount > 0 && topologyBlocker.isEmpty()) {
303
+ topologyBlocker = "official_repeat_record";
304
+ }
305
+ if (connectorCount != residueCount - 1 && topologyBlocker.isEmpty()) {
306
+ topologyBlocker = "edge_count_not_residue_count_minus_one";
307
+ }
308
+
309
+ Set<Integer> visited = new HashSet<>();
310
+ Queue<Integer> queue = new ArrayDeque<>();
311
+ visited.add(0);
312
+ queue.add(0);
313
+ while (!queue.isEmpty()) {
314
+ int current = queue.remove();
315
+ for (int neighbor : adjacency.get(current)) {
316
+ if (visited.add(neighbor)) {
317
+ queue.add(neighbor);
318
+ }
319
+ }
320
+ }
321
+ boolean connected = visited.size() == residueCount;
322
+ if (!connected && topologyBlocker.isEmpty()) {
323
+ topologyBlocker = "residue_graph_disconnected";
324
+ }
325
+ boolean exactUndirectedTree = topologyBlocker.isEmpty();
326
+
327
+ List<Integer> depths = new ArrayList<>();
328
+ int maxDepth = -1;
329
+ if (!exactUndirectedTree && rootedBlocker.isEmpty()) {
330
+ rootedBlocker = topologyBlocker;
331
+ }
332
+ if (rootedBlocker.isEmpty()) {
333
+ Set<Integer> roots = new HashSet<>();
334
+ for (int index = 0; index < residueCount; index++) {
335
+ if (!childParent.containsKey(index)) {
336
+ roots.add(index);
337
+ }
338
+ }
339
+ if (roots.size() != 1) {
340
+ rootedBlocker = "root_not_unique";
341
+ } else {
342
+ int[] values = new int[residueCount];
343
+ for (int index = 0; index < residueCount; index++) {
344
+ values[index] = -1;
345
+ }
346
+ int root = roots.iterator().next();
347
+ values[root] = 0;
348
+ queue.clear();
349
+ queue.add(root);
350
+ while (!queue.isEmpty() && rootedBlocker.isEmpty()) {
351
+ int parent = queue.remove();
352
+ for (int child : directedChildren.get(parent)) {
353
+ if (values[child] >= 0) {
354
+ rootedBlocker = "directed_cycle_or_duplicate_child";
355
+ break;
356
+ }
357
+ values[child] = values[parent] + 1;
358
+ queue.add(child);
359
+ }
360
+ }
361
+ for (int value : values) {
362
+ if (value < 0 && rootedBlocker.isEmpty()) {
363
+ rootedBlocker = "directed_tree_disconnected";
364
+ }
365
+ depths.add(value);
366
+ maxDepth = Math.max(maxDepth, value);
367
+ }
368
+ }
369
+ }
370
+ boolean exactRootedTree = exactUndirectedTree && rootedBlocker.isEmpty();
371
+ if (!exactRootedTree) {
372
+ depths.clear();
373
+ maxDepth = -1;
374
+ }
375
+ StringBuilder depthText = new StringBuilder();
376
+ for (int index = 0; index < depths.size(); index++) {
377
+ if (index > 0) {
378
+ depthText.append(',');
379
+ }
380
+ depthText.append(depths.get(index));
381
+ }
382
+ Collections.sort(pairRecords);
383
+ String pairText = exactUndirectedTree
384
+ ? String.join(",", pairRecords) : "";
385
+ StringBuilder parentText = new StringBuilder();
386
+ if (exactRootedTree) {
387
+ for (int index = 0; index < residueCount; index++) {
388
+ if (index > 0) {
389
+ parentText.append(',');
390
+ }
391
+ parentText.append(childParent.getOrDefault(index, -1));
392
+ }
393
+ }
394
+ String residueOrder = String.join(",", residueLabels);
395
+ String uniqueOrder = String.join(",", uniqueIds);
396
+ String axisProof = residueOrder + "\n" + uniqueOrder;
397
+
398
+ return String.join("\t",
399
+ rowIndex,
400
+ "OK",
401
+ Integer.toString(residueCount),
402
+ Integer.toString(linkages.size()),
403
+ Integer.toString(connectorCount),
404
+ Integer.toString(monovalentCount),
405
+ Integer.toString(repeatCount),
406
+ Integer.toString(alternativeGroupCount),
407
+ Integer.toString(groupedPositionGroupCount),
408
+ Integer.toString(probabilityEndpointCount),
409
+ Integer.toString(unknownBackbonePositionCount),
410
+ Integer.toString(unknownModificationPositionCount),
411
+ Integer.toString(unknownDirectionCount),
412
+ Integer.toString(mapConnectorCount),
413
+ Integer.toString(hyperedgeCount),
414
+ Integer.toString(duplicatePairCount),
415
+ Integer.toString(orientationFailureCount),
416
+ Integer.toString(groupedOrientationNonInvariantCount),
417
+ Boolean.toString(connected),
418
+ Boolean.toString(exactUndirectedTree),
419
+ Boolean.toString(exactRootedTree),
420
+ b64(topologyBlocker),
421
+ b64(rootedBlocker),
422
+ Integer.toString(maxDepth),
423
+ b64(depthText.toString()),
424
+ b64(pairText),
425
+ b64(parentText.toString()),
426
+ b64(residueOrder),
427
+ b64(uniqueOrder),
428
+ sha256(serialization.toString()),
429
+ sha256(wurcs),
430
+ sha256(axisProof),
431
+ Integer.toString(residueIdentityAmbiguousGroupCount),
432
+ Integer.toString(sameResidueAlternativeGroupCount)
433
+ );
434
+ }
435
+
436
+ public static void main(String[] args) throws Exception {
437
+ try (BufferedReader reader = new BufferedReader(
438
+ new InputStreamReader(System.in, StandardCharsets.UTF_8)
439
+ )) {
440
+ String line;
441
+ while ((line = reader.readLine()) != null) {
442
+ if (line.isEmpty()) {
443
+ continue;
444
+ }
445
+ String[] fields = line.split("\t", -1);
446
+ String rowIndex = fields.length > 0 ? fields[0] : "";
447
+ try {
448
+ if (fields.length != 2 || rowIndex.isEmpty()) {
449
+ throw new IllegalArgumentException(
450
+ "input must be row_index and base64 WURCS"
451
+ );
452
+ }
453
+ String wurcs = new String(
454
+ B64_DECODER.decode(fields[1]), StandardCharsets.UTF_8
455
+ );
456
+ System.out.println(process(rowIndex, wurcs));
457
+ } catch (Exception error) {
458
+ String message = error.getClass().getName() + ": "
459
+ + error.getMessage();
460
+ System.out.println(
461
+ rowIndex + "\tERROR\t" + b64(message)
462
+ );
463
+ }
464
+ }
465
+ }
466
+ }
467
+ }
releases/2026-10-02/iar_completion/runtime_snapshot/java/review_MolWURCS.jar ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:bc38e44e66daed7ec04f7c7907a13509aac125a8a09c97d9972725ab4278edaf
3
+ size 4019920
releases/2026-10-02/iar_completion/runtime_snapshot/java/review_wurcsframework-1.3.1.jar ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:5449f9276a7b952adf087468c874bb13c7295062cce7d1afb13fdea78ace7f37
3
+ size 340319
releases/2026-10-02/iar_completion/runtime_snapshot/project/bert_training_v4/downstream_tasks/utils/wurcs_bpe_tokenizer.py ADDED
@@ -0,0 +1,740 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ WURCS-BPE Tokenizer
4
+
5
+ A hybrid tokenizer that learns semantic subwords from WURCS while preserving
6
+ the ability to handle rare/novel glycan structures character-by-character.
7
+
8
+ Key features:
9
+ 1. Pre-tokenization: Split WURCS into semantic units (residues, linkages, mods)
10
+ 2. BPE: Learn subword merges from corpus
11
+ 3. Character fallback: Handle novel structures
12
+ 4. Tree embeddings: Preserve branch_depth and linkage_type per token
13
+
14
+ Usage:
15
+ # Train BPE on corpus
16
+ tokenizer = WURCSBPETokenizer.train_from_corpus(
17
+ wurcs_strings,
18
+ num_merges=500,
19
+ output_path="bpe_vocabulary.json"
20
+ )
21
+
22
+ # Tokenize
23
+ result = tokenizer.tokenize(wurcs_string)
24
+ """
25
+
26
+ import json
27
+ import re
28
+ from collections import Counter, defaultdict
29
+ from pathlib import Path
30
+ from typing import Dict, List, Optional, Tuple, Set
31
+ import pickle
32
+
33
+
34
+ class WURCSPreTokenizer:
35
+ """
36
+ Pre-tokenize WURCS into semantic units before BPE.
37
+
38
+ WURCS format: WURCS=2.0/count/[residues]/indices/linkages
39
+
40
+ We split into:
41
+ - Residues: [a2122h-1b_1-5_2*NCC/3=O] -> one unit per []
42
+ - Linkages: a4-b1 -> one unit per linkage
43
+ - Special markers: [BRANCH_OPEN], [BRANCH_CLOSE], etc.
44
+ """
45
+
46
+ # Residue patterns for common monosaccharides
47
+ RESIDUE_PATTERN = re.compile(r'\[([^\]]+)\]')
48
+ LINKAGE_PATTERN = re.compile(r'([a-z])(\d+|\?)-([a-z])(\d+|\?)')
49
+
50
+ def __init__(self):
51
+ self.special_tokens = {
52
+ '[PAD]': 0,
53
+ '[UNK]': 1,
54
+ '[START]': 2,
55
+ '[END]': 3,
56
+ '[MASK]': 4,
57
+ '[BRANCH_OPEN]': 5,
58
+ '[BRANCH_CLOSE]': 6,
59
+ '[LINK]': 7,
60
+ '[MOD]': 8,
61
+ '[RESIDUE_ERROR]': 9,
62
+ }
63
+
64
+ def pre_tokenize(self, wurcs: str) -> List[Dict]:
65
+ """
66
+ Pre-tokenize WURCS into semantic units.
67
+
68
+ Returns list of dicts with:
69
+ - text: The unit text
70
+ - type: 'special', 'residue', 'linkage', 'mod', 'index'
71
+ - residue_id: Which residue this belongs to (-1 for special, -2 for linkage)
72
+ - branch_depth: Tree depth (computed later)
73
+ """
74
+ units = []
75
+
76
+ # Add start token
77
+ units.append({
78
+ 'text': '[START]',
79
+ 'type': 'special',
80
+ 'residue_id': -1,
81
+ 'branch_depth': 0,
82
+ 'linkage_type': 0,
83
+ })
84
+
85
+ # Parse WURCS sections
86
+ if not wurcs.startswith('WURCS='):
87
+ units.append({'text': '[RESIDUE_ERROR]', 'type': 'special', 'residue_id': -1, 'branch_depth': 0, 'linkage_type': 0})
88
+ units.append({'text': '[END]', 'type': 'special', 'residue_id': -1, 'branch_depth': 0, 'linkage_type': 0})
89
+ return units
90
+
91
+ try:
92
+ parts = self._split_wurcs_sections(wurcs)
93
+ if len(parts) < 4:
94
+ return [{'text': '[ERROR]', 'type': 'special', 'residue_id': -1, 'branch_depth': 0, 'linkage_type': 0}]
95
+
96
+ # parts: WURCS=2.0/3,3,2/[a2122h-1b_1-5][a2122h-1a_1-5][a1122h-1b_1-5]/1-2-3-1/a4-b1_b3-c1_c4-d1
97
+ # section 2: residue definitions
98
+ # section 3: indices
99
+ # section 4: linkages (optional)
100
+
101
+ version = parts[0] # WURCS=2.0
102
+ counts = parts[1] # residue_count,node_count,link_count
103
+ residue_defs = parts[2] # [res1][res2]...
104
+ indices = parts[3] # 1-2-3-1
105
+ linkages = parts[4] if len(parts) > 4 else "" # a4-b1_b3-c1
106
+
107
+ # Parse residue definitions
108
+ residue_list = self.RESIDUE_PATTERN.findall(residue_defs)
109
+
110
+ # Parse linkages to compute branch structure
111
+ linkage_list = linkages.split('_') if linkages else []
112
+ branch_points, residue_depths, linkage_types_map, adj = self._analyze_tree_structure(linkage_list, num_residues=len(residue_list))
113
+
114
+ # Compute distance matrix and cache it based on the linkage string (structure)
115
+ # This is the most expensive part, so we cache it
116
+ if not hasattr(self, '_dist_cache'): self._dist_cache = {}
117
+ if linkages not in self._dist_cache:
118
+ self._dist_cache[linkages] = self._compute_distance_matrix(adj, len(residue_list))
119
+ dist_matrix_raw = self._dist_cache[linkages]
120
+
121
+ # Parse indices to map positions to residue definitions
122
+ index_list = indices.split('-') if indices else []
123
+
124
+ # Process each residue instance
125
+ residue_letter = ord('a')
126
+ for idx, res_idx in enumerate(index_list):
127
+ current_residue_id = idx
128
+ res_letter = chr(residue_letter + idx)
129
+
130
+ # Check if this is a branch point - add branch marker before
131
+ if res_letter in branch_points and branch_points[res_letter] > 0:
132
+ for _ in range(branch_points[res_letter]):
133
+ units.append({
134
+ 'text': '[BRANCH_OPEN]',
135
+ 'type': 'special',
136
+ 'residue_id': -1,
137
+ 'branch_depth': residue_depths.get(res_letter, 0),
138
+ 'linkage_type': 0,
139
+ })
140
+
141
+ # Get residue definition
142
+ try:
143
+ res_def_idx = int(res_idx) - 1 # 1-indexed to 0-indexed
144
+ res_def = residue_list[res_def_idx] if res_def_idx < len(residue_list) else ""
145
+ except (ValueError, IndexError):
146
+ res_def = ""
147
+
148
+ # Split residue into base and modifications
149
+ res_parts = res_def.split('_')
150
+ base = res_parts[0] if res_parts else res_def
151
+ mods = res_parts[1:] if len(res_parts) > 1 else []
152
+
153
+ # Add residue base as a single unit
154
+ depth = residue_depths.get(res_letter, 0)
155
+ units.append({
156
+ 'text': base,
157
+ 'type': 'residue',
158
+ 'residue_id': current_residue_id,
159
+ 'branch_depth': depth,
160
+ 'linkage_type': 0,
161
+ })
162
+
163
+ # Add modifications
164
+ for mod in mods:
165
+ units.append({
166
+ 'text': mod,
167
+ 'type': 'mod',
168
+ 'residue_id': current_residue_id,
169
+ 'branch_depth': depth,
170
+ 'linkage_type': 0,
171
+ })
172
+
173
+ # Store distance matrix in units for easy access in tokenizer
174
+ if units:
175
+ # Find first residue unit or just use START
176
+ units[0]['distance_matrix'] = dist_matrix_raw
177
+
178
+ # Add linkages
179
+ for link in linkage_list:
180
+ if not link:
181
+ continue
182
+ # Parse linkage type
183
+ lt = self._parse_linkage_type(link)
184
+ units.append({
185
+ 'text': link,
186
+ 'type': 'linkage',
187
+ 'residue_id': -2,
188
+ 'branch_depth': 0,
189
+ 'linkage_type': lt,
190
+ })
191
+ except Exception:
192
+ # Fallback for truly broken WURCS
193
+ pass
194
+
195
+ # Add end token
196
+ units.append({
197
+ 'text': '[END]',
198
+ 'type': 'special',
199
+ 'residue_id': -1,
200
+ 'branch_depth': 0,
201
+ 'linkage_type': 0,
202
+ })
203
+
204
+ return units
205
+
206
+ def _split_wurcs_sections(self, wurcs: str) -> List[str]:
207
+ """Split WURCS string into sections, handling nested brackets."""
208
+ # Remove WURCS= prefix
209
+ if wurcs.startswith('WURCS='):
210
+ wurcs = wurcs[6:]
211
+
212
+ sections = []
213
+ current = ""
214
+ bracket_depth = 0
215
+
216
+ for char in wurcs:
217
+ if char == '[':
218
+ bracket_depth += 1
219
+ current += char
220
+ elif char == ']':
221
+ bracket_depth -= 1
222
+ current += char
223
+ elif char == '/' and bracket_depth == 0:
224
+ sections.append(current)
225
+ current = ""
226
+ else:
227
+ current += char
228
+
229
+ if current:
230
+ sections.append(current)
231
+
232
+ return sections
233
+
234
+ def _analyze_tree_structure(self, linkages: List[str], num_residues: int) -> Tuple[Dict, Dict, Dict, Dict]:
235
+ """Analyze linkages to compute branch points and residue depths."""
236
+ branch_points = defaultdict(int) # residue -> number of children
237
+ children = defaultdict(list)
238
+ all_residues = set()
239
+ linkage_types = {}
240
+
241
+ for link in linkages:
242
+ match = self.LINKAGE_PATTERN.match(link)
243
+ if match:
244
+ from_res, from_pos, to_res, to_pos = match.groups()
245
+ children[from_res].append(to_res)
246
+ all_residues.add(from_res)
247
+ all_residues.add(to_res)
248
+
249
+ # Store linkage type
250
+ linkage_types[link] = self._parse_linkage_type(link)
251
+
252
+ # Build adjacency list for BFS
253
+ adj = defaultdict(list)
254
+ for link in linkages:
255
+ match = self.LINKAGE_PATTERN.match(link)
256
+ if match:
257
+ u = ord(match.group(1)) - ord('a')
258
+ v = ord(match.group(3)) - ord('a')
259
+ if 0 <= u < num_residues and 0 <= v < num_residues:
260
+ adj[u].append(v)
261
+ adj[v].append(u)
262
+
263
+ # Find branch points (residues with >1 child)
264
+ for res, kids in children.items():
265
+ if len(kids) > 1:
266
+ branch_points[res] = len(kids) - 1 # Number of extra branches
267
+
268
+ # Compute depths using BFS
269
+ # Find root (residue with no parent)
270
+ child_set = set()
271
+ for kids in children.values():
272
+ child_set.update(kids)
273
+ roots = all_residues - child_set
274
+ root = min(roots) if roots else 'a'
275
+
276
+ depths = {root: 0}
277
+ queue = [root]
278
+ while queue:
279
+ current = queue.pop(0)
280
+ for child in children.get(current, []):
281
+ if child not in depths:
282
+ depths[child] = depths[current] + 1
283
+ queue.append(child)
284
+
285
+ return branch_points, depths, linkage_types, adj
286
+
287
+ def _compute_distance_matrix(self, adj: Dict[int, List[int]], num_residues: int) -> List[List[int]]:
288
+ """
289
+ Compute shortest path distance (number of bonds) between all residue pairs using BFS.
290
+ """
291
+ if num_residues == 0:
292
+ return []
293
+
294
+ dist_matrix = [[-1] * num_residues for _ in range(num_residues)]
295
+
296
+ for i in range(num_residues):
297
+ dist_matrix[i][i] = 0
298
+ queue = [(i, 0)]
299
+ visited = {i}
300
+
301
+ while queue:
302
+ curr, d = queue.pop(0)
303
+ dist_matrix[i][curr] = d
304
+
305
+ for neighbor in adj[curr]:
306
+ if neighbor not in visited:
307
+ visited.add(neighbor)
308
+ queue.append((neighbor, d + 1))
309
+
310
+ return dist_matrix
311
+
312
+ def _compute_distance_matrix_OLD(self, linkages: List[str], num_residues: int) -> List[List[int]]:
313
+ """
314
+ Compute shortest path distance (number of bonds) between all residue pairs.
315
+ Returns a symmetric N x N matrix where N is num_residues.
316
+ Values are integers (number of steps). 0 on diagonal. -1 if unreachable (shouldn't happen in single tree).
317
+ """
318
+ if num_residues == 0:
319
+ return []
320
+
321
+ # Initialize adjacency list
322
+ adj = defaultdict(list)
323
+ for link in linkages:
324
+ match = self.LINKAGE_PATTERN.match(link)
325
+ if match:
326
+ # WURCS indices are 1-based letters (a=1, b=2...)
327
+ from_res_char, _, to_res_char, _ = match.groups()
328
+ # Convert char to 0-based index
329
+ u = ord(from_res_char) - ord('a')
330
+ v = ord(to_res_char) - ord('a')
331
+
332
+ # Undirected graph for structural distance
333
+ if 0 <= u < num_residues and 0 <= v < num_residues:
334
+ adj[u].append(v)
335
+ adj[v].append(u)
336
+
337
+ # Compute All-Pairs Shortest Path (BFS from each node is fine for small N)
338
+ # Glycans are small (N ~ 5-20 usually), so O(N^2) BFS is cheap.
339
+ dist_matrix = [[-1] * num_residues for _ in range(num_residues)]
340
+
341
+ for i in range(num_residues):
342
+ dist_matrix[i][i] = 0
343
+ queue = [(i, 0)]
344
+ visited = {i}
345
+
346
+ while queue:
347
+ curr, d = queue.pop(0)
348
+ dist_matrix[i][curr] = d
349
+
350
+ for neighbor in adj[curr]:
351
+ if neighbor not in visited:
352
+ visited.add(neighbor)
353
+ queue.append((neighbor, d + 1))
354
+
355
+ return dist_matrix
356
+
357
+ def _parse_linkage_type(self, link: str) -> int:
358
+ """Parse linkage string to get type ID."""
359
+ LINKAGE_TYPES = {
360
+ (1, 2): 0, (2, 1): 0,
361
+ (1, 3): 1, (3, 1): 1,
362
+ (1, 4): 2, (4, 1): 2,
363
+ (1, 6): 3, (6, 1): 3,
364
+ (2, 3): 4, (3, 2): 4,
365
+ (2, 6): 5, (6, 2): 5,
366
+ (3, 6): 6, (6, 3): 6,
367
+ }
368
+
369
+ match = self.LINKAGE_PATTERN.match(link)
370
+ if match:
371
+ _, from_pos, _, to_pos = match.groups()
372
+ try:
373
+ pos_tuple = (int(from_pos), int(to_pos))
374
+ return LINKAGE_TYPES.get(pos_tuple, 7)
375
+ except ValueError:
376
+ return 8 # Unknown
377
+ return 8
378
+
379
+
380
+ class WURCSBPETokenizer:
381
+ """
382
+ BPE tokenizer for WURCS with tree-aware embeddings.
383
+ """
384
+
385
+ def __init__(self, vocab_path: Optional[str] = None):
386
+ self.pre_tokenizer = WURCSPreTokenizer()
387
+
388
+ # Special tokens (fixed)
389
+ self.special_tokens = self.pre_tokenizer.special_tokens
390
+
391
+ # BPE vocabulary
392
+ self.token_to_id: Dict[str, int] = {}
393
+ self.id_to_token: Dict[int, str] = {}
394
+ self.merges: List[Tuple[str, str]] = []
395
+
396
+ if vocab_path:
397
+ self.load_vocab(vocab_path)
398
+ else:
399
+ # Initialize with special tokens only
400
+ self.token_to_id = dict(self.special_tokens)
401
+ self.id_to_token = {v: k for k, v in self.token_to_id.items()}
402
+
403
+ @classmethod
404
+ def train_from_corpus(
405
+ cls,
406
+ wurcs_strings: List[str],
407
+ num_merges: int = 500,
408
+ output_path: Optional[str] = None,
409
+ min_frequency: int = 2,
410
+ max_token_length: Optional[int] = None,
411
+ ) -> 'WURCSBPETokenizer':
412
+ """
413
+ Train BPE on a corpus of WURCS strings.
414
+
415
+ Args:
416
+ wurcs_strings: List of WURCS strings
417
+ num_merges: Number of BPE merge operations
418
+ output_path: Optional path to save vocabulary
419
+ min_frequency: Minimum frequency for a token to be kept
420
+ max_token_length: Maximum length of a merged token (None = no limit)
421
+
422
+ Returns:
423
+ Trained tokenizer
424
+ """
425
+ tokenizer = cls()
426
+ pre_tok = WURCSPreTokenizer()
427
+
428
+ print(f"Training BPE on {len(wurcs_strings)} WURCS strings...")
429
+
430
+ # Step 1: Pre-tokenize all strings to get semantic units
431
+ all_units = []
432
+ for wurcs in wurcs_strings:
433
+ units = pre_tok.pre_tokenize(wurcs)
434
+ for unit in units:
435
+ if unit['type'] != 'special':
436
+ all_units.append(unit['text'])
437
+
438
+ # Step 2: Count unit frequencies
439
+ unit_counts = Counter(all_units)
440
+ print(f"Found {len(unit_counts)} unique units")
441
+
442
+ # Step 3: Initialize vocabulary with characters from all units
443
+ char_vocab = set()
444
+ for unit in unit_counts:
445
+ for char in unit:
446
+ char_vocab.add(char)
447
+
448
+ # Build initial vocab: special tokens + characters
449
+ vocab_id = len(tokenizer.special_tokens)
450
+ for char in sorted(char_vocab):
451
+ tokenizer.token_to_id[char] = vocab_id
452
+ tokenizer.id_to_token[vocab_id] = char
453
+ vocab_id += 1
454
+
455
+ print(f"Initial vocab size: {vocab_id} (special + characters)")
456
+
457
+ # Step 4: Convert units to character sequences
458
+ word_freqs = {}
459
+ for unit, count in unit_counts.items():
460
+ if count >= min_frequency:
461
+ # Split into characters with space separator
462
+ chars = tuple(unit)
463
+ word_freqs[chars] = count
464
+
465
+ # Step 5: BPE merging
466
+ merges = []
467
+
468
+ for merge_idx in range(num_merges):
469
+ # Count pairs
470
+ pair_counts = Counter()
471
+ for word, freq in word_freqs.items():
472
+ for i in range(len(word) - 1):
473
+ pair = (word[i], word[i + 1])
474
+ pair_counts[pair] += freq
475
+
476
+ if not pair_counts:
477
+ break
478
+
479
+ # Find most frequent pair
480
+ best_pair = pair_counts.most_common(1)[0][0]
481
+ best_count = pair_counts[best_pair]
482
+
483
+ if best_count < min_frequency:
484
+ break
485
+
486
+ # Merge pair
487
+ new_token = best_pair[0] + best_pair[1]
488
+
489
+ # Check length constraint
490
+ if max_token_length and len(new_token) > max_token_length:
491
+ # remove this pair from consideration for this iteration and future?
492
+ # Actually, skipping it here is tricky because we need to ignore it in pair_counts next time
493
+ # Simpler: Just skip adding it to merges and modify word_freqs?
494
+ # No, if we don't merge, we just continue to the next best pair in THIS iteration.
495
+ # But pair_counts is already computed.
496
+ # We need to loop until we find a valid pair or run out
497
+
498
+ # In this simple implementation, let's just skip this merge efficiently
499
+ # We need to find the NEXT most common pair.
500
+
501
+ # Re-do finding best pair loop
502
+ found_valid_pair = False
503
+ for pair, count in pair_counts.most_common():
504
+ token_candidate = pair[0] + pair[1]
505
+ if max_token_length and len(token_candidate) > max_token_length:
506
+ continue # Skip too long
507
+
508
+ if count < min_frequency:
509
+ break # Stop if frequency too low
510
+
511
+ # Found valid pair
512
+ best_pair = pair
513
+ best_count = count
514
+ new_token = token_candidate
515
+ found_valid_pair = True
516
+ break
517
+
518
+ if not found_valid_pair:
519
+ print(f" Stopping early: No more pairs satisfy max_token_length={max_token_length}")
520
+ break
521
+
522
+ # Final check before merging (in case we didn't enter the if block but updated vars)
523
+ # Actually the logic above handles it. If we entered the block, we either found a new best_pair or broke.
524
+
525
+ merges.append(best_pair)
526
+
527
+ # Add to vocab
528
+ tokenizer.token_to_id[new_token] = vocab_id
529
+ tokenizer.id_to_token[vocab_id] = new_token
530
+ vocab_id += 1
531
+
532
+ # Update word_freqs
533
+ new_word_freqs = {}
534
+ for word, freq in word_freqs.items():
535
+ new_word = []
536
+ i = 0
537
+ while i < len(word):
538
+ if i < len(word) - 1 and word[i] == best_pair[0] and word[i + 1] == best_pair[1]:
539
+ new_word.append(new_token)
540
+ i += 2
541
+ else:
542
+ new_word.append(word[i])
543
+ i += 1
544
+ new_word_freqs[tuple(new_word)] = freq
545
+ word_freqs = new_word_freqs
546
+
547
+ if (merge_idx + 1) % 100 == 0:
548
+ print(f" Merge {merge_idx + 1}/{num_merges}: '{best_pair[0]}' + '{best_pair[1]}' -> '{new_token}' (count={best_count})")
549
+
550
+ tokenizer.merges = merges
551
+ print(f"Final vocab size: {len(tokenizer.token_to_id)}")
552
+
553
+ # Save if requested
554
+ if output_path:
555
+ tokenizer.save_vocab(output_path)
556
+
557
+ return tokenizer
558
+
559
+ def apply_bpe(self, text: str) -> List[str]:
560
+ """Apply BPE merges to a text string."""
561
+ if text in self.token_to_id:
562
+ return [text]
563
+
564
+ # Split into characters
565
+ tokens = list(text)
566
+
567
+ # Apply merges
568
+ for pair in self.merges:
569
+ new_tokens = []
570
+ i = 0
571
+ while i < len(tokens):
572
+ if i < len(tokens) - 1 and tokens[i] == pair[0] and tokens[i + 1] == pair[1]:
573
+ new_tokens.append(pair[0] + pair[1])
574
+ i += 2
575
+ else:
576
+ new_tokens.append(tokens[i])
577
+ i += 1
578
+ tokens = new_tokens
579
+
580
+ return tokens
581
+
582
+ def tokenize(self, wurcs: str, max_length: int = 256) -> Dict:
583
+ """
584
+ Tokenize a WURCS string.
585
+
586
+ Returns:
587
+ Dict with:
588
+ - tokens: List of token strings
589
+ - token_ids: List of token IDs
590
+ - residue_ids: List of residue IDs
591
+ - branch_depths: List of branch depths
592
+ - linkage_types: List of linkage types
593
+ - attention_mask: Attention mask
594
+ """
595
+ # Pre-tokenize
596
+ units = self.pre_tokenizer.pre_tokenize(wurcs)
597
+
598
+ tokens = []
599
+ token_ids = []
600
+ residue_ids = []
601
+ branch_depths = []
602
+ linkage_types = []
603
+
604
+ for unit in units:
605
+ if unit['type'] == 'special':
606
+ # Special tokens stay as-is
607
+ tok = unit['text']
608
+ tokens.append(tok)
609
+ token_ids.append(self.token_to_id.get(tok, self.token_to_id['[UNK]']))
610
+ residue_ids.append(unit['residue_id'])
611
+ branch_depths.append(unit['branch_depth'])
612
+ linkage_types.append(unit['linkage_type'])
613
+ else:
614
+ # Apply BPE to this unit
615
+ bpe_tokens = self.apply_bpe(unit['text'])
616
+ for tok in bpe_tokens:
617
+ tokens.append(tok)
618
+ token_ids.append(self.token_to_id.get(tok, self.token_to_id['[UNK]']))
619
+ residue_ids.append(unit['residue_id'])
620
+ branch_depths.append(unit['branch_depth'])
621
+ linkage_types.append(unit['linkage_type'])
622
+
623
+ # Truncate if needed
624
+ if len(tokens) > max_length:
625
+ tokens = tokens[:max_length - 1] + ['[END]']
626
+ token_ids = token_ids[:max_length - 1] + [self.token_to_id['[END]']]
627
+ residue_ids = residue_ids[:max_length - 1] + [-1]
628
+ branch_depths = branch_depths[:max_length - 1] + [0]
629
+ linkage_types = linkage_types[:max_length - 1] + [0]
630
+
631
+ # Create attention mask and pad
632
+ length = len(tokens)
633
+ attention_mask = [1] * length
634
+
635
+ while len(tokens) < max_length:
636
+ tokens.append('[PAD]')
637
+ token_ids.append(self.token_to_id['[PAD]'])
638
+ residue_ids.append(-1)
639
+ branch_depths.append(0)
640
+ linkage_types.append(0)
641
+ attention_mask.append(0)
642
+ # Pre-tokenize
643
+ units = self.pre_tokenizer.pre_tokenize(wurcs)
644
+
645
+ # Extract distance matrix from pre-tokenizer result
646
+ dist_matrix_raw = units[0].get('distance_matrix', [])
647
+ num_residues = len(dist_matrix_raw)
648
+
649
+ # Map token-to-token distances using residue_ids
650
+ # token_i is associated with residue_ids[i].
651
+ # residue_ids[i] is index into dist_matrix_raw.
652
+ # If residue_ids[i] == -1 (special), distance is undefined (use -1 or 999)
653
+
654
+ # Use UNPADDED length for distance matrix to save massive memory
655
+ # distance_matrix will be e.g. 20x20, while tokens are padded to 256
656
+ token_len = length
657
+ distance_matrix = [[-1] * token_len for _ in range(token_len)]
658
+
659
+ for i in range(token_len):
660
+ for j in range(token_len):
661
+ r_i = residue_ids[i]
662
+ r_j = residue_ids[j]
663
+
664
+ if r_i >= 0 and r_j >= 0 and r_i < num_residues and r_j < num_residues:
665
+ distance_matrix[i][j] = dist_matrix_raw[r_i][r_j]
666
+ else:
667
+ distance_matrix[i][j] = -1 # Special/Padding
668
+
669
+ # MEMORY OPTIMIZATION: Do NOT pad matrix here.
670
+ # Pad on-the-fly in Dataset class instead.
671
+ # This saves massive memory (0.2GB vs 66GB).
672
+
673
+ return {
674
+ 'tokens': tokens,
675
+ 'token_ids': token_ids,
676
+ 'residue_ids': residue_ids,
677
+ 'branch_depths': branch_depths,
678
+ 'linkage_types': linkage_types,
679
+ 'attention_mask': attention_mask,
680
+ 'distance_matrix': distance_matrix, # New Output
681
+ 'length': length,
682
+ }
683
+
684
+ def save_vocab(self, path: str):
685
+ """Save vocabulary to JSON file."""
686
+ data = {
687
+ 'special_tokens': self.special_tokens,
688
+ 'token_to_id': self.token_to_id,
689
+ 'merges': [list(m) for m in self.merges],
690
+ 'metadata': {
691
+ 'vocab_size': len(self.token_to_id),
692
+ 'num_merges': len(self.merges),
693
+ }
694
+ }
695
+ with open(path, 'w') as f:
696
+ json.dump(data, f, indent=2)
697
+ print(f"Saved vocabulary to {path}")
698
+
699
+ def load_vocab(self, path: str):
700
+ """Load vocabulary from JSON file."""
701
+ with open(path, 'r') as f:
702
+ data = json.load(f)
703
+
704
+ self.special_tokens = data['special_tokens']
705
+ self.token_to_id = data['token_to_id']
706
+ self.id_to_token = {int(v): k for k, v in self.token_to_id.items()}
707
+ self.merges = [tuple(m) for m in data['merges']]
708
+
709
+ print(f"Loaded vocabulary with {len(self.token_to_id)} tokens")
710
+
711
+ @property
712
+ def vocab_size(self) -> int:
713
+ return len(self.token_to_id)
714
+
715
+
716
+ # ============================================================================
717
+ # Testing
718
+ # ============================================================================
719
+
720
+ if __name__ == '__main__':
721
+ # Test pre-tokenizer
722
+ print("="*80)
723
+ print("Testing WURCSPreTokenizer")
724
+ print("="*80)
725
+
726
+ pre_tok = WURCSPreTokenizer()
727
+
728
+ test_wurcs = [
729
+ "WURCS=2.0/2,2,1/[a2122h-1b_1-5][a2211m-1a_1-5]/1-2/a4-b1",
730
+ "WURCS=2.0/3,3,2/[a2122h-1b_1-5_2*NCC/3=O][a2112h-1a_1-5][a2211m-1a_1-5]/1-2-3/a4-b1_b3-c1",
731
+ ]
732
+
733
+ for wurcs in test_wurcs:
734
+ print(f"\nWURCS: {wurcs[:60]}...")
735
+ units = pre_tok.pre_tokenize(wurcs)
736
+ print(f"Units ({len(units)}):")
737
+ for u in units[:10]:
738
+ print(f" {u['type']:10} | res={u['residue_id']:2} | depth={u['branch_depth']} | {u['text']}")
739
+ if len(units) > 10:
740
+ print(f" ... and {len(units) - 10} more")
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/bpe_ambiguity_tokens.json ADDED
@@ -0,0 +1,721 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ambiguous_tokens": {
3
+ "?": 32,
4
+ "?|": 90,
5
+ "a?|": 108,
6
+ "a?|b": 109,
7
+ "?|c": 110,
8
+ "a?|b?|c": 111,
9
+ "?|d": 112,
10
+ "a?|b?|c?|d": 113,
11
+ "?|e": 114,
12
+ "a?|b?|c?|d?|e": 115,
13
+ "?|f": 116,
14
+ "a?|b?|c?|d?|e?|f": 117,
15
+ "?-": 118,
16
+ "?|g": 119,
17
+ "a?|b?|c?|d?|e?|f?|g": 120,
18
+ "?|h": 122,
19
+ "?|i": 123,
20
+ "?|h?|i": 124,
21
+ "?|j": 125,
22
+ "?|h?|i?|j": 126,
23
+ "?|k": 128,
24
+ "?|h?|i?|j?|k": 129,
25
+ "?|l": 130,
26
+ "?|h?|i?|j?|k?|l": 131,
27
+ "?|m": 132,
28
+ "?|h?|i?|j?|k?|l?|m": 133,
29
+ "?|h?|i?|j?|k?|l?|m?|": 138,
30
+ "n?|": 141,
31
+ "n?|o": 142,
32
+ "?}": 143,
33
+ "n?|o?|": 146,
34
+ "n?|o?|p": 147,
35
+ "?}-": 149,
36
+ "?}-{": 150,
37
+ "n?|o?|p?|": 153,
38
+ "n?|o?|p?|q": 154,
39
+ "n?|o?|p?|q?|": 157,
40
+ "n?|o?|p?|q?|r": 158,
41
+ "n?|o?|p?|q?|r?|": 165,
42
+ "n?|o?|p?|q?|r?|s": 166,
43
+ "n?|o?|p?|q?|r?|s?|": 170,
44
+ "n?|o?|p?|q?|r?|s?|t": 171,
45
+ "?|u": 189,
46
+ "a?-": 197,
47
+ "c?-": 201,
48
+ "?|u?|": 209,
49
+ "?|u?|v": 210,
50
+ "b?-": 211,
51
+ "a?-b1": 213,
52
+ "d?-": 217,
53
+ "b?-c1": 221,
54
+ "c?-d1": 230,
55
+ "?|u?|v?|": 231,
56
+ "?|u?|v?|w": 232,
57
+ "1-?": 242,
58
+ "d?-e1": 244,
59
+ "e?-": 245,
60
+ "e?-f1": 262,
61
+ "?|u?|v?|w?|": 266,
62
+ "?|u?|v?|w?|x": 267,
63
+ "f?-": 273,
64
+ "?|u?|v?|w?|x?|": 288,
65
+ "?|u?|v?|w?|x?|y": 289,
66
+ "g?-": 298,
67
+ "n?|o?|p?|q?|r?|s?|t?": 304,
68
+ "i?-": 306,
69
+ "h?-": 308,
70
+ "?|u?}-{": 312,
71
+ "?|u?": 313,
72
+ "n?|o?|p?|q?|r?}-{": 314,
73
+ "n?|o?|p?|q?|r?": 315,
74
+ "f?-g1": 318,
75
+ "n?|o?}-{": 322,
76
+ "n?|o?": 323,
77
+ "?|u?|v?|w?|x?|y?|": 325,
78
+ "?|u?|v?|w?|x?|y?|z": 326,
79
+ "n?|o?|p?|q?|r?|s?}-{": 328,
80
+ "n?|o?|p?|q?|r?|s?": 329,
81
+ "n?|o?|p?}-{": 331,
82
+ "n?|o?|p?": 332,
83
+ "n?|o?|p?|q?}-{": 336,
84
+ "n?|o?|p?|q?": 337,
85
+ "g?-h1": 339,
86
+ "?|h?|i?|j?|k?|l?}-{": 342,
87
+ "?|h?|i?|j?|k?|l?": 343,
88
+ "n?}-{": 344,
89
+ "n?": 345,
90
+ "j?-": 346,
91
+ "h?-i1": 347,
92
+ "?|u?|v?}-{": 351,
93
+ "?|u?|v?": 352,
94
+ "k?-": 353,
95
+ "i?-j1": 355,
96
+ "?|h?|i?|j?|k?|l?|m?": 363,
97
+ "?|h?|i?|j?|k?}-{": 364,
98
+ "?|h?|i?|j?|k?": 365,
99
+ "?|u?|v?|w?|x?|y?|z?|": 369,
100
+ "?|h?|i?|j?}-{": 375,
101
+ "?|h?|i?|j?": 376,
102
+ "l?-": 377,
103
+ "A?|": 392,
104
+ "A?|B": 393,
105
+ "j?-k1": 401,
106
+ "m?-": 404,
107
+ "?|h?|i?}-{": 408,
108
+ "?|h?|i?": 409,
109
+ "k?-l1": 418,
110
+ "?|h?}-{": 420,
111
+ "?|h?": 421,
112
+ "A?|B?|": 424,
113
+ "A?|B?|C": 425,
114
+ "a?|b?|c?|d?|e?|f?|g?": 427,
115
+ "f?-g2": 431,
116
+ "a?|b?|c?|d?|e?|f?}-{": 437,
117
+ "a?|b?|c?|d?|e?|f?": 438,
118
+ "l?-m1": 442,
119
+ "?|u?|v?|w?}-{": 450,
120
+ "?|u?|v?|w?": 451,
121
+ "A?|B?|C?|": 464,
122
+ "A?|B?|C?|D": 465,
123
+ "a?|b?|c?|d?|e?}-{": 475,
124
+ "a?|b?|c?|d?|e?": 476,
125
+ "n?-": 499,
126
+ "a?|b?|c?|d?}-{": 502,
127
+ "a?|b?|c?|d?": 503,
128
+ "m?-n1": 518,
129
+ "A?|B?|C?|D?|": 521,
130
+ "A?|B?|C?|D?|E": 522,
131
+ "o?-": 534,
132
+ "d?-h1": 536,
133
+ "A?|B?|C?|D?|E?|": 542,
134
+ "A?|B?|C?|D?|E?|F": 543,
135
+ "c?-i1": 544,
136
+ "c?-h1": 549,
137
+ "A?|B?|C?|D?|E?|F?|": 550,
138
+ "A?|B?|C?|D?|E?|F?|G": 551,
139
+ "?|u?|v?|w?|x?}-{": 563,
140
+ "?|u?|v?|w?|x?": 564,
141
+ "a?|b?|c?}-{": 571,
142
+ "a?|b?|c?}-{a?|b?|c": 572,
143
+ "a?|b?|c?}-{a?|b?|c?": 573,
144
+ "?|H": 581,
145
+ "2-?": 592,
146
+ "?|H?|": 598,
147
+ "?|H?|I": 599,
148
+ "?}*OC": 600,
149
+ "c?-k1": 607,
150
+ "c?-g1": 609,
151
+ "?|H?|I?|": 615,
152
+ "?|H?|I?|J": 616,
153
+ "n?-o1": 617,
154
+ "d?-g1": 629,
155
+ "o?-p1": 634,
156
+ "p?-": 646,
157
+ "?|u?|v?|w?|x?|y?}-{": 653,
158
+ "?|u?|v?|w?|x?|y?": 654,
159
+ "b?-c2": 656,
160
+ "d?-i1": 658,
161
+ "c?-j1": 691,
162
+ "?}*OSO": 696,
163
+ "e?-h1": 701,
164
+ "q?-": 713,
165
+ "c?-f1": 720,
166
+ "i?-j2": 728,
167
+ "?|h?|i?}": 742,
168
+ "h?-i2": 747,
169
+ "g?-h2": 753,
170
+ "c?-l1": 756,
171
+ "j?-k2": 758,
172
+ "?|h?}": 759,
173
+ "c?-e1": 760,
174
+ "?|H?|I?|J?|": 761,
175
+ "?|H?|I?|J?|K": 762,
176
+ "a?|b?|c?|d?|e?|f?}": 772,
177
+ "b?-e1": 774,
178
+ "b?-f1": 791,
179
+ "d?-f1": 794,
180
+ "p?-q1": 796,
181
+ "a?|b?|c?|d?|e?}": 798,
182
+ "a?-d1": 800,
183
+ "m?-n2": 803,
184
+ "e?-g1": 809,
185
+ "?|h?|i?|j?}": 812,
186
+ "r?-": 817,
187
+ "a?-c1": 818,
188
+ "?|u?|v?|w?|x?|y?|z?": 822,
189
+ "a?-e1": 826,
190
+ "d?-j1": 833,
191
+ "b?-g1": 834,
192
+ "q?-r1": 847,
193
+ "d?-e2": 854,
194
+ "c?-m1": 860,
195
+ "a?-f1": 875,
196
+ "b?-d1": 887,
197
+ "?|H?|I?|J?|K?|": 892,
198
+ "?|H?|I?|J?|K?|L": 893,
199
+ "?|H?|I?|J?|K?|L?|": 894,
200
+ "?|H?|I?|J?|K?|L?|M": 895,
201
+ "?|H?|I?|J?|K?|L?|M?|": 896,
202
+ "a?-l1": 920,
203
+ "?*OSO/3=O/3=O": 923,
204
+ "k?-l2": 940,
205
+ "k?-o1": 942,
206
+ "N?|": 965,
207
+ "N?|O": 966,
208
+ "N?|O?|": 967,
209
+ "N?|O?|P": 968,
210
+ "N?|O?|P?|": 969,
211
+ "N?|O?|P?|Q": 970,
212
+ "N?|O?|P?|Q?|": 971,
213
+ "N?|O?|P?|Q?|R": 972,
214
+ "N?|O?|P?|Q?|R?|": 973,
215
+ "N?|O?|P?|Q?|R?|S": 974,
216
+ "N?|O?|P?|Q?|R?|S?|": 975,
217
+ "N?|O?|P?|Q?|R?|S?|T": 976,
218
+ "?|U": 977,
219
+ "?|U?|": 978,
220
+ "?|U?|V": 979,
221
+ "c?-d2": 983,
222
+ "r?-s1": 988,
223
+ "a?|b?}-{": 995,
224
+ "a?|b?}-{a?|b": 996,
225
+ "a?|b?}-{a?|b?": 997,
226
+ "e?-f2": 1001,
227
+ "g?-i1": 1006,
228
+ "i?-l1": 1010,
229
+ "s?-": 1011,
230
+ "?|h?|i?|j?|k?}": 1017,
231
+ "b?-h1": 1034,
232
+ "a?-j1": 1038,
233
+ "n?-o2": 1046,
234
+ "a?-b2": 1069,
235
+ "e?-i1": 1095,
236
+ "h?-j1": 1102,
237
+ "a?-k1": 1108,
238
+ "i?-k1": 1115,
239
+ "a?-g1": 1116,
240
+ "?}*OPO": 1122,
241
+ "d?-k1": 1129,
242
+ "a?-m1": 1151,
243
+ "a?-i1": 1159,
244
+ "A?}-{": 1174,
245
+ "A?": 1175,
246
+ "?}*OCC": 1177,
247
+ "l?-m2": 1179,
248
+ "A?|B?}-{": 1180,
249
+ "A?|B?": 1181,
250
+ "f?-h1": 1183,
251
+ "a?-n1": 1189,
252
+ "p?-q2": 1192,
253
+ "c?-n1": 1197,
254
+ "?|U?|V?|": 1202,
255
+ "?|U?|V?|W": 1203,
256
+ "?|U?|V?|W?|": 1204,
257
+ "?|U?|V?|W?|X": 1205,
258
+ "?|U?|V?|W?|X?|": 1206,
259
+ "?|U?|V?|W?|X?|Y": 1207,
260
+ "?|a": 1208,
261
+ "s?-t1": 1223,
262
+ "?|h?|i?|j?|k?|l?|m?}": 1228,
263
+ "g?-j1": 1234,
264
+ "A?|B?|C?|D?}-{": 1242,
265
+ "A?|B?|C?|D?": 1243,
266
+ "a?-h1": 1253,
267
+ "?|H?|I?|J?}-{": 1257,
268
+ "?|H?|I?|J?": 1258,
269
+ "o?-p2": 1261,
270
+ "b?-i1": 1273,
271
+ "?|h?|i?|j?|k?|l?}": 1309,
272
+ "j?-m1": 1317,
273
+ "c?-o1": 1318,
274
+ "a?-o1": 1330,
275
+ "a?|b?|c?}*OC": 1331,
276
+ "b?-j1": 1357,
277
+ "a?-r1": 1361,
278
+ "n?}": 1363,
279
+ "A?|B?|C?}-{": 1371,
280
+ "A?|B?|C?": 1372,
281
+ "m?-p1": 1375,
282
+ "l?-p1": 1383,
283
+ "a?-p1": 1444,
284
+ "k?-n1": 1446,
285
+ "j?-l1": 1470,
286
+ "?|U?|V?|W?|X?|Y?|": 1471,
287
+ "?|U?|V?|W?|X?|Y?|Z": 1472,
288
+ "?|aa?|": 1473,
289
+ "?|aa?|a": 1474,
290
+ "?|aa?|ab": 1475,
291
+ "?*OPO/3O/3=O": 1476,
292
+ "l?-q1": 1489,
293
+ "l?-n1": 1499,
294
+ "a?-s1": 1517,
295
+ "k?-m1": 1524,
296
+ "a?-q1": 1546,
297
+ "c?-q1": 1547,
298
+ "t?-": 1551,
299
+ "a?|b?|c?|d?}*OC": 1565,
300
+ "f?-i1": 1590,
301
+ "c?-p1": 1591,
302
+ "n?-q1": 1593,
303
+ "?|i?}": 1611,
304
+ "a?|b?|c?|d?|e?}*OC": 1612,
305
+ "m?-q1": 1617,
306
+ "q?-r2": 1623,
307
+ "l?-o1": 1624,
308
+ "m?-r1": 1628,
309
+ "a?-t1": 1630,
310
+ "a?|b?|c?|d?}*OSO": 1649,
311
+ "c?-r1": 1675,
312
+ "1-d?|i?}": 1683,
313
+ "j?-n1": 1691,
314
+ "u?-": 1694,
315
+ "a?|b?|c?|d?|e?}*OSO": 1718,
316
+ "?*OCC/3=O": 1723,
317
+ "?%": 1752,
318
+ "?*OP^XOCCN/3O/3=O": 1770,
319
+ "t?-u1": 1772,
320
+ "?*": 1774,
321
+ "c?-s1": 1775,
322
+ "a?-u1": 1793,
323
+ "f?-h2": 1808,
324
+ "e?-j1": 1811,
325
+ "c?-t1": 1818,
326
+ "f1-a?|b?|c?|d?|e?}": 1822,
327
+ "u?-v1": 1835,
328
+ "h?-k1": 1841,
329
+ "?|H?|I?|J?|K?}-{": 1846,
330
+ "?|H?|I?|J?|K?": 1847,
331
+ "n?|o?}": 1851,
332
+ "1-d?|h?}": 1852,
333
+ "q?-s1": 1872,
334
+ "%?%": 1880,
335
+ "b?-g2": 1881,
336
+ "r?-s2": 1882,
337
+ "d?-l1": 1898,
338
+ "v?-": 1917,
339
+ "b?-k1": 1927,
340
+ "?|aa?|ab?|a": 1942,
341
+ "?|aa?|ab?|ac": 1943,
342
+ "?|aa?|ab?|ac?|": 1944,
343
+ "?|aa?|ab?|ac?|ad": 1945,
344
+ "a?|b?}*OC": 1949,
345
+ "?*OC": 1952,
346
+ "e?-k1": 1955,
347
+ "a?-d2": 1999,
348
+ "s?-t2": 2013,
349
+ "a?-f2": 2027,
350
+ "o?-q1": 2030,
351
+ "?}*OP^XOCCN": 2040,
352
+ "a?|b?|c?}*OCC": 2047,
353
+ "m?-o1": 2048,
354
+ "c?-f2": 2058,
355
+ "A?|B?|C?|D?|E?|F?|G?": 2060,
356
+ "a?|b?|c?}*OSO": 2071,
357
+ "?|U?|V?}-{": 2079,
358
+ "?|U?|V?": 2080,
359
+ "c?-u1": 2087
360
+ },
361
+ "ambiguous_ids": [
362
+ 32,
363
+ 90,
364
+ 108,
365
+ 109,
366
+ 110,
367
+ 111,
368
+ 112,
369
+ 113,
370
+ 114,
371
+ 115,
372
+ 116,
373
+ 117,
374
+ 118,
375
+ 119,
376
+ 120,
377
+ 122,
378
+ 123,
379
+ 124,
380
+ 125,
381
+ 126,
382
+ 128,
383
+ 129,
384
+ 130,
385
+ 131,
386
+ 132,
387
+ 133,
388
+ 138,
389
+ 141,
390
+ 142,
391
+ 143,
392
+ 146,
393
+ 147,
394
+ 149,
395
+ 150,
396
+ 153,
397
+ 154,
398
+ 157,
399
+ 158,
400
+ 165,
401
+ 166,
402
+ 170,
403
+ 171,
404
+ 189,
405
+ 197,
406
+ 201,
407
+ 209,
408
+ 210,
409
+ 211,
410
+ 213,
411
+ 217,
412
+ 221,
413
+ 230,
414
+ 231,
415
+ 232,
416
+ 242,
417
+ 244,
418
+ 245,
419
+ 262,
420
+ 266,
421
+ 267,
422
+ 273,
423
+ 288,
424
+ 289,
425
+ 298,
426
+ 304,
427
+ 306,
428
+ 308,
429
+ 312,
430
+ 313,
431
+ 314,
432
+ 315,
433
+ 318,
434
+ 322,
435
+ 323,
436
+ 325,
437
+ 326,
438
+ 328,
439
+ 329,
440
+ 331,
441
+ 332,
442
+ 336,
443
+ 337,
444
+ 339,
445
+ 342,
446
+ 343,
447
+ 344,
448
+ 345,
449
+ 346,
450
+ 347,
451
+ 351,
452
+ 352,
453
+ 353,
454
+ 355,
455
+ 363,
456
+ 364,
457
+ 365,
458
+ 369,
459
+ 375,
460
+ 376,
461
+ 377,
462
+ 392,
463
+ 393,
464
+ 401,
465
+ 404,
466
+ 408,
467
+ 409,
468
+ 418,
469
+ 420,
470
+ 421,
471
+ 424,
472
+ 425,
473
+ 427,
474
+ 431,
475
+ 437,
476
+ 438,
477
+ 442,
478
+ 450,
479
+ 451,
480
+ 464,
481
+ 465,
482
+ 475,
483
+ 476,
484
+ 499,
485
+ 502,
486
+ 503,
487
+ 518,
488
+ 521,
489
+ 522,
490
+ 534,
491
+ 536,
492
+ 542,
493
+ 543,
494
+ 544,
495
+ 549,
496
+ 550,
497
+ 551,
498
+ 563,
499
+ 564,
500
+ 571,
501
+ 572,
502
+ 573,
503
+ 581,
504
+ 592,
505
+ 598,
506
+ 599,
507
+ 600,
508
+ 607,
509
+ 609,
510
+ 615,
511
+ 616,
512
+ 617,
513
+ 629,
514
+ 634,
515
+ 646,
516
+ 653,
517
+ 654,
518
+ 656,
519
+ 658,
520
+ 691,
521
+ 696,
522
+ 701,
523
+ 713,
524
+ 720,
525
+ 728,
526
+ 742,
527
+ 747,
528
+ 753,
529
+ 756,
530
+ 758,
531
+ 759,
532
+ 760,
533
+ 761,
534
+ 762,
535
+ 772,
536
+ 774,
537
+ 791,
538
+ 794,
539
+ 796,
540
+ 798,
541
+ 800,
542
+ 803,
543
+ 809,
544
+ 812,
545
+ 817,
546
+ 818,
547
+ 822,
548
+ 826,
549
+ 833,
550
+ 834,
551
+ 847,
552
+ 854,
553
+ 860,
554
+ 875,
555
+ 887,
556
+ 892,
557
+ 893,
558
+ 894,
559
+ 895,
560
+ 896,
561
+ 920,
562
+ 923,
563
+ 940,
564
+ 942,
565
+ 965,
566
+ 966,
567
+ 967,
568
+ 968,
569
+ 969,
570
+ 970,
571
+ 971,
572
+ 972,
573
+ 973,
574
+ 974,
575
+ 975,
576
+ 976,
577
+ 977,
578
+ 978,
579
+ 979,
580
+ 983,
581
+ 988,
582
+ 995,
583
+ 996,
584
+ 997,
585
+ 1001,
586
+ 1006,
587
+ 1010,
588
+ 1011,
589
+ 1017,
590
+ 1034,
591
+ 1038,
592
+ 1046,
593
+ 1069,
594
+ 1095,
595
+ 1102,
596
+ 1108,
597
+ 1115,
598
+ 1116,
599
+ 1122,
600
+ 1129,
601
+ 1151,
602
+ 1159,
603
+ 1174,
604
+ 1175,
605
+ 1177,
606
+ 1179,
607
+ 1180,
608
+ 1181,
609
+ 1183,
610
+ 1189,
611
+ 1192,
612
+ 1197,
613
+ 1202,
614
+ 1203,
615
+ 1204,
616
+ 1205,
617
+ 1206,
618
+ 1207,
619
+ 1208,
620
+ 1223,
621
+ 1228,
622
+ 1234,
623
+ 1242,
624
+ 1243,
625
+ 1253,
626
+ 1257,
627
+ 1258,
628
+ 1261,
629
+ 1273,
630
+ 1309,
631
+ 1317,
632
+ 1318,
633
+ 1330,
634
+ 1331,
635
+ 1357,
636
+ 1361,
637
+ 1363,
638
+ 1371,
639
+ 1372,
640
+ 1375,
641
+ 1383,
642
+ 1444,
643
+ 1446,
644
+ 1470,
645
+ 1471,
646
+ 1472,
647
+ 1473,
648
+ 1474,
649
+ 1475,
650
+ 1476,
651
+ 1489,
652
+ 1499,
653
+ 1517,
654
+ 1524,
655
+ 1546,
656
+ 1547,
657
+ 1551,
658
+ 1565,
659
+ 1590,
660
+ 1591,
661
+ 1593,
662
+ 1611,
663
+ 1612,
664
+ 1617,
665
+ 1623,
666
+ 1624,
667
+ 1628,
668
+ 1630,
669
+ 1649,
670
+ 1675,
671
+ 1683,
672
+ 1691,
673
+ 1694,
674
+ 1718,
675
+ 1723,
676
+ 1752,
677
+ 1770,
678
+ 1772,
679
+ 1774,
680
+ 1775,
681
+ 1793,
682
+ 1808,
683
+ 1811,
684
+ 1818,
685
+ 1822,
686
+ 1835,
687
+ 1841,
688
+ 1846,
689
+ 1847,
690
+ 1851,
691
+ 1852,
692
+ 1872,
693
+ 1880,
694
+ 1881,
695
+ 1882,
696
+ 1898,
697
+ 1917,
698
+ 1927,
699
+ 1942,
700
+ 1943,
701
+ 1944,
702
+ 1945,
703
+ 1949,
704
+ 1952,
705
+ 1955,
706
+ 1999,
707
+ 2013,
708
+ 2027,
709
+ 2030,
710
+ 2040,
711
+ 2047,
712
+ 2048,
713
+ 2058,
714
+ 2060,
715
+ 2071,
716
+ 2079,
717
+ 2080,
718
+ 2087
719
+ ],
720
+ "source_vocab": "data/bpe_vocabulary_clean.json"
721
+ }
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/bpe_vocabulary_clean.json ADDED
The diff for this file is too large to render. See raw diff
 
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_ambiguity_tokens.json ADDED
@@ -0,0 +1,764 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "ambiguous_ids": [
3
+ 39,
4
+ 114,
5
+ 131,
6
+ 132,
7
+ 133,
8
+ 134,
9
+ 135,
10
+ 136,
11
+ 137,
12
+ 138,
13
+ 140,
14
+ 141,
15
+ 142,
16
+ 143,
17
+ 144,
18
+ 145,
19
+ 147,
20
+ 148,
21
+ 149,
22
+ 150,
23
+ 151,
24
+ 152,
25
+ 153,
26
+ 155,
27
+ 156,
28
+ 157,
29
+ 158,
30
+ 163,
31
+ 164,
32
+ 165,
33
+ 167,
34
+ 168,
35
+ 170,
36
+ 175,
37
+ 176,
38
+ 180,
39
+ 181,
40
+ 183,
41
+ 184,
42
+ 192,
43
+ 193,
44
+ 197,
45
+ 198,
46
+ 217,
47
+ 218,
48
+ 226,
49
+ 229,
50
+ 237,
51
+ 238,
52
+ 239,
53
+ 241,
54
+ 247,
55
+ 251,
56
+ 258,
57
+ 264,
58
+ 265,
59
+ 273,
60
+ 275,
61
+ 276,
62
+ 295,
63
+ 298,
64
+ 299,
65
+ 309,
66
+ 322,
67
+ 323,
68
+ 329,
69
+ 334,
70
+ 335,
71
+ 336,
72
+ 340,
73
+ 341,
74
+ 342,
75
+ 345,
76
+ 349,
77
+ 350,
78
+ 351,
79
+ 352,
80
+ 353,
81
+ 354,
82
+ 355,
83
+ 359,
84
+ 360,
85
+ 361,
86
+ 363,
87
+ 364,
88
+ 365,
89
+ 366,
90
+ 367,
91
+ 368,
92
+ 369,
93
+ 371,
94
+ 372,
95
+ 373,
96
+ 375,
97
+ 376,
98
+ 377,
99
+ 378,
100
+ 379,
101
+ 380,
102
+ 383,
103
+ 384,
104
+ 385,
105
+ 387,
106
+ 388,
107
+ 389,
108
+ 390,
109
+ 391,
110
+ 401,
111
+ 405,
112
+ 409,
113
+ 415,
114
+ 416,
115
+ 419,
116
+ 420,
117
+ 421,
118
+ 426,
119
+ 427,
120
+ 428,
121
+ 430,
122
+ 431,
123
+ 432,
124
+ 434,
125
+ 435,
126
+ 440,
127
+ 441,
128
+ 442,
129
+ 444,
130
+ 445,
131
+ 446,
132
+ 450,
133
+ 462,
134
+ 463,
135
+ 470,
136
+ 476,
137
+ 477,
138
+ 478,
139
+ 481,
140
+ 487,
141
+ 488,
142
+ 489,
143
+ 493,
144
+ 494,
145
+ 496,
146
+ 499,
147
+ 516,
148
+ 535,
149
+ 536,
150
+ 538,
151
+ 539,
152
+ 540,
153
+ 552,
154
+ 590,
155
+ 592,
156
+ 602,
157
+ 603,
158
+ 604,
159
+ 608,
160
+ 609,
161
+ 620,
162
+ 635,
163
+ 636,
164
+ 637,
165
+ 640,
166
+ 646,
167
+ 647,
168
+ 649,
169
+ 654,
170
+ 659,
171
+ 660,
172
+ 661,
173
+ 667,
174
+ 668,
175
+ 675,
176
+ 676,
177
+ 688,
178
+ 690,
179
+ 711,
180
+ 716,
181
+ 720,
182
+ 721,
183
+ 734,
184
+ 742,
185
+ 752,
186
+ 754,
187
+ 778,
188
+ 788,
189
+ 791,
190
+ 793,
191
+ 808,
192
+ 809,
193
+ 810,
194
+ 817,
195
+ 820,
196
+ 831,
197
+ 833,
198
+ 834,
199
+ 843,
200
+ 844,
201
+ 859,
202
+ 869,
203
+ 870,
204
+ 873,
205
+ 880,
206
+ 885,
207
+ 886,
208
+ 893,
209
+ 894,
210
+ 910,
211
+ 928,
212
+ 938,
213
+ 939,
214
+ 940,
215
+ 941,
216
+ 942,
217
+ 943,
218
+ 944,
219
+ 950,
220
+ 952,
221
+ 953,
222
+ 958,
223
+ 959,
224
+ 961,
225
+ 963,
226
+ 972,
227
+ 979,
228
+ 987,
229
+ 988,
230
+ 989,
231
+ 994,
232
+ 995,
233
+ 996,
234
+ 1000,
235
+ 1001,
236
+ 1003,
237
+ 1004,
238
+ 1005,
239
+ 1006,
240
+ 1007,
241
+ 1008,
242
+ 1009,
243
+ 1010,
244
+ 1011,
245
+ 1012,
246
+ 1013,
247
+ 1014,
248
+ 1015,
249
+ 1016,
250
+ 1017,
251
+ 1019,
252
+ 1021,
253
+ 1026,
254
+ 1038,
255
+ 1050,
256
+ 1055,
257
+ 1072,
258
+ 1078,
259
+ 1115,
260
+ 1116,
261
+ 1124,
262
+ 1138,
263
+ 1150,
264
+ 1161,
265
+ 1167,
266
+ 1168,
267
+ 1169,
268
+ 1170,
269
+ 1171,
270
+ 1172,
271
+ 1173,
272
+ 1178,
273
+ 1193,
274
+ 1197,
275
+ 1220,
276
+ 1233,
277
+ 1250,
278
+ 1253,
279
+ 1262,
280
+ 1267,
281
+ 1301,
282
+ 1305,
283
+ 1317,
284
+ 1318,
285
+ 1333,
286
+ 1342,
287
+ 1349,
288
+ 1360,
289
+ 1370,
290
+ 1371,
291
+ 1372,
292
+ 1373,
293
+ 1374,
294
+ 1383,
295
+ 1389,
296
+ 1390,
297
+ 1391,
298
+ 1396,
299
+ 1416,
300
+ 1420,
301
+ 1425,
302
+ 1428,
303
+ 1433,
304
+ 1445,
305
+ 1478,
306
+ 1483,
307
+ 1499,
308
+ 1500,
309
+ 1501,
310
+ 1503,
311
+ 1513,
312
+ 1524,
313
+ 1531,
314
+ 1561,
315
+ 1563,
316
+ 1566,
317
+ 1567,
318
+ 1568,
319
+ 1600,
320
+ 1601,
321
+ 1602,
322
+ 1605,
323
+ 1608,
324
+ 1626,
325
+ 1634,
326
+ 1635,
327
+ 1636,
328
+ 1661,
329
+ 1669,
330
+ 1671,
331
+ 1672,
332
+ 1686,
333
+ 1726,
334
+ 1737,
335
+ 1738,
336
+ 1739,
337
+ 1740,
338
+ 1741,
339
+ 1752,
340
+ 1763,
341
+ 1773,
342
+ 1780,
343
+ 1790,
344
+ 1796,
345
+ 1798,
346
+ 1804,
347
+ 1829,
348
+ 1842,
349
+ 1888,
350
+ 1893,
351
+ 1905,
352
+ 1907,
353
+ 1912,
354
+ 1918,
355
+ 1923,
356
+ 1940,
357
+ 1945,
358
+ 1948,
359
+ 1951,
360
+ 1961,
361
+ 1991,
362
+ 1992,
363
+ 1993,
364
+ 2016,
365
+ 2020,
366
+ 2021,
367
+ 2030,
368
+ 2033,
369
+ 2034,
370
+ 2035,
371
+ 2064,
372
+ 2066,
373
+ 2067,
374
+ 2077,
375
+ 2093,
376
+ 2100,
377
+ 2101,
378
+ 2105,
379
+ 2110
380
+ ],
381
+ "ambiguous_tokens": {
382
+ "%?%": 1763,
383
+ "-?": 1918,
384
+ "1-?": 273,
385
+ "1-a?|b?|c?|d?|e?|f?}": 928,
386
+ "1-a?|b?|c?|d?|e?}": 869,
387
+ "1-d?|i?}": 2100,
388
+ "2-?": 721,
389
+ "<Q>-?": 2016,
390
+ "?": 39,
391
+ "?%": 1669,
392
+ "?*OCC/3=O": 2033,
393
+ "?*OPO/3O/3=O": 1796,
394
+ "?*OSO/3=O/3=O": 1078,
395
+ "?-": 149,
396
+ "?-?": 2067,
397
+ "?|": 114,
398
+ "?|A": 434,
399
+ "?|B": 462,
400
+ "?|C": 493,
401
+ "?|D": 535,
402
+ "?|E": 608,
403
+ "?|F": 635,
404
+ "?|G": 646,
405
+ "?|H": 667,
406
+ "?|I": 675,
407
+ "?|J": 688,
408
+ "?|K": 831,
409
+ "?|L": 939,
410
+ "?|L?|M": 942,
411
+ "?|L?|M?|N": 943,
412
+ "?|M": 940,
413
+ "?|N": 941,
414
+ "?|O": 1003,
415
+ "?|O?|P": 1011,
416
+ "?|O?|P?|Q": 1012,
417
+ "?|O?|P?|Q?|R": 1013,
418
+ "?|O?|P?|Q?|R?|S": 1014,
419
+ "?|O?|P?|Q?|R?|S?|T": 1015,
420
+ "?|O?|P?|Q?|R?|S?|T?|U": 1016,
421
+ "?|O?|P?|Q?|R?|S?|T?|U?|V": 1017,
422
+ "?|P": 1004,
423
+ "?|Q": 1005,
424
+ "?|R": 1006,
425
+ "?|S": 1007,
426
+ "?|T": 1008,
427
+ "?|U": 1009,
428
+ "?|V": 1010,
429
+ "?|W": 1167,
430
+ "?|W?|X": 1170,
431
+ "?|W?|X?|Y": 1171,
432
+ "?|X": 1168,
433
+ "?|Y": 1169,
434
+ "?|Z": 1370,
435
+ "?|Z?|a": 1371,
436
+ "?|Z?|aa?|": 1372,
437
+ "?|Z?|aa?|ab": 1373,
438
+ "?|a": 552,
439
+ "?|ac": 1738,
440
+ "?|ac?|ad": 1739,
441
+ "?|ad": 1737,
442
+ "?|c": 133,
443
+ "?|d": 135,
444
+ "?|e": 137,
445
+ "?|f": 140,
446
+ "?|g": 142,
447
+ "?|h": 144,
448
+ "?|h?}": 2064,
449
+ "?|i": 147,
450
+ "?|i?}": 1842,
451
+ "?|j": 150,
452
+ "?|k": 152,
453
+ "?|l": 155,
454
+ "?|m": 157,
455
+ "?|n": 164,
456
+ "?|o": 167,
457
+ "?|p": 175,
458
+ "?|s": 192,
459
+ "?|t": 197,
460
+ "?|u": 217,
461
+ "?|v": 238,
462
+ "?|w": 264,
463
+ "?|x": 298,
464
+ "?|y": 322,
465
+ "?|z": 387,
466
+ "?}": 163,
467
+ "?}*OC": 716,
468
+ "?}*OCC/3=O": 870,
469
+ "?}*OPO/3O/3=O": 742,
470
+ "?}*OSO/3=O/3=O": 592,
471
+ "?}-{": 170,
472
+ "?}-{a?|b": 994,
473
+ "?}-{a?|b?": 995,
474
+ "?}-{a?|b?|c": 602,
475
+ "?}-{a?|b?|c?": 603,
476
+ "?}-{a?|b?|c?|d": 487,
477
+ "?}-{a?|b?|c?|d?": 488,
478
+ "?}-{a?|b?|c?|d?|e": 476,
479
+ "?}-{a?|b?|c?|d?|e?": 477,
480
+ "?}-{a?|b?|c?|d?|e?|f": 440,
481
+ "?}-{a?|b?|c?|d?|e?|f?": 441,
482
+ "?}-{a?|b?|c?|d?|e?|f?|g": 444,
483
+ "?}-{a?|b?|c?|d?|e?|f?|g?": 445,
484
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h": 430,
485
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?": 431,
486
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i": 419,
487
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?": 420,
488
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j": 383,
489
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?": 384,
490
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k": 371,
491
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?": 372,
492
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l": 340,
493
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?": 341,
494
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m": 389,
495
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?": 390,
496
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n": 378,
497
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?": 379,
498
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o": 359,
499
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?": 360,
500
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p": 349,
501
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?": 350,
502
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q": 375,
503
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?": 376,
504
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r": 353,
505
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?": 354,
506
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s": 367,
507
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?": 368,
508
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t": 334,
509
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?": 335,
510
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u": 364,
511
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?": 365,
512
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v": 426,
513
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?": 427,
514
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w": 538,
515
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?": 539,
516
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x": 659,
517
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?": 660,
518
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y": 808,
519
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?": 809,
520
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z": 987,
521
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?": 988,
522
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A": 1499,
523
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?": 1500,
524
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B": 1389,
525
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?": 1390,
526
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C": 1634,
527
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?": 1635,
528
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D": 1600,
529
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?": 1601,
530
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J": 1566,
531
+ "?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?": 1567,
532
+ "a?-": 226,
533
+ "a?-b1": 241,
534
+ "a?-b2": 1267,
535
+ "a?-c1": 972,
536
+ "a?-d1": 959,
537
+ "a?-e1": 979,
538
+ "a?-f1": 1055,
539
+ "a?-g1": 1342,
540
+ "a?-h1": 1561,
541
+ "a?-i1": 1420,
542
+ "a?-j1": 1233,
543
+ "a?-k1": 1318,
544
+ "a?-l1": 1115,
545
+ "a?-m1": 1416,
546
+ "a?-n1": 1425,
547
+ "a?-o1": 1605,
548
+ "a?-p1": 1740,
549
+ "a?-q1": 1912,
550
+ "a?-r1": 1661,
551
+ "a?-s1": 1907,
552
+ "a?-t1": 2020,
553
+ "a?|": 131,
554
+ "a?|b": 132,
555
+ "a?|b?|c": 134,
556
+ "a?|b?|c?|d": 136,
557
+ "a?|b?|c?|d?|e": 138,
558
+ "a?|b?|c?|d?|e?|f": 141,
559
+ "a?|b?|c?|d?|e?|f?|g": 143,
560
+ "a?|b?|c?|d?|e?|f?|g?|h": 145,
561
+ "a?|b?|c?|d?|e?|f?|g?|h?|i": 148,
562
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j": 151,
563
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k": 153,
564
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l": 156,
565
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m": 158,
566
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n": 165,
567
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o": 168,
568
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p": 176,
569
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|": 180,
570
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q": 181,
571
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|": 183,
572
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r": 184,
573
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s": 193,
574
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t": 198,
575
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u": 218,
576
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v": 239,
577
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w": 265,
578
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x": 299,
579
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y": 323,
580
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z": 388,
581
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A": 435,
582
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B": 463,
583
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C": 494,
584
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D": 536,
585
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E": 609,
586
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F": 636,
587
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G": 647,
588
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H": 668,
589
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I": 676,
590
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J": 690,
591
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?|K": 833,
592
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?|K?|L?|M?|N": 944,
593
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?|K?|L?|M?|N?|O?|P?|Q?|R?|S?|T?|U?|V": 1019,
594
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?|K?|L?|M?|N?|O?|P?|Q?|R?|S?|T?|U?|V?|W?|X?|Y": 1172,
595
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?|K?|L?|M?|N?|O?|P?|Q?|R?|S?|T?|U?|V?|W?|X?|Y?|Z?|aa?|ab": 1374,
596
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?|K?|L?|M?|N?|O?|P?|Q?|R?|S?|T?|U?|V?|W?|X?|Y?|Z?|aa?|ab?|ac?|ad": 1741,
597
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?|E?|F?|G?|H?|I?|J?": 1568,
598
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?|D?": 1602,
599
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?|C?": 1636,
600
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?|B?": 1391,
601
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?|A?": 1501,
602
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?|z?": 989,
603
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?|y?": 810,
604
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?|x?": 661,
605
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?|w?": 540,
606
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?|v?": 428,
607
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?|u?": 366,
608
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?|t?": 336,
609
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?|s?": 369,
610
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?|r?": 355,
611
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?|q?": 377,
612
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?|p?": 351,
613
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?}": 2105,
614
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?|o?": 361,
615
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?}": 1524,
616
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?|n?": 380,
617
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?}": 1478,
618
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?|m?": 391,
619
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?}": 1483,
620
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?|l?": 342,
621
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?}": 1262,
622
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?|k?": 373,
623
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?}": 958,
624
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?|j?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?|j?": 385,
625
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?}": 886,
626
+ "a?|b?|c?|d?|e?|f?|g?|h?|i?}-{a?|b?|c?|d?|e?|f?|g?|h?|i?": 421,
627
+ "a?|b?|c?|d?|e?|f?|g?|h?}": 1000,
628
+ "a?|b?|c?|d?|e?|f?|g?|h?}*OSO/3=O/3=O": 2035,
629
+ "a?|b?|c?|d?|e?|f?|g?|h?}-{a?|b?|c?|d?|e?|f?|g?|h?": 432,
630
+ "a?|b?|c?|d?|e?|f?|g?}": 1150,
631
+ "a?|b?|c?|d?|e?|f?|g?}-{a?|b?|c?|d?|e?|f?|g?": 446,
632
+ "a?|b?|c?|d?|e?|f?}": 834,
633
+ "a?|b?|c?|d?|e?|f?}*OC": 2110,
634
+ "a?|b?|c?|d?|e?|f?}*OSO/3=O/3=O": 1333,
635
+ "a?|b?|c?|d?|e?|f?}-{a?|b?|c?|d?|e?|f?": 442,
636
+ "a?|b?|c?|d?|e?}": 791,
637
+ "a?|b?|c?|d?|e?}*OC": 2034,
638
+ "a?|b?|c?|d?|e?}*OSO/3=O/3=O": 1253,
639
+ "a?|b?|c?|d?|e?}-{a?|b?|c?|d?|e?": 478,
640
+ "a?|b?|c?|d?}": 1923,
641
+ "a?|b?|c?|d?}*OC": 1951,
642
+ "a?|b?|c?|d?}*OSO/3=O/3=O": 1349,
643
+ "a?|b?|c?|d?}-{a?|b?|c?|d?": 489,
644
+ "a?|b?|c?}*OC": 1563,
645
+ "a?|b?|c?}*OCC/3=O": 1773,
646
+ "a?|b?|c?}*OSO/3=O/3=O": 2030,
647
+ "a?|b?|c?}-{a?|b?|c?": 604,
648
+ "a?|b?}*OSO/3=O/3=O": 2101,
649
+ "a?|b?}-{a?|b?": 996,
650
+ "b?-": 237,
651
+ "b?-c1": 251,
652
+ "b?-c2": 793,
653
+ "b?-d1": 1072,
654
+ "b?-e1": 910,
655
+ "b?-f1": 938,
656
+ "b?-g1": 1021,
657
+ "b?-h1": 1250,
658
+ "b?-i1": 1531,
659
+ "b?-j1": 1671,
660
+ "c?-": 229,
661
+ "c?-d1": 258,
662
+ "c?-d2": 1161,
663
+ "c?-e1": 880,
664
+ "c?-f1": 844,
665
+ "c?-g1": 711,
666
+ "c?-h1": 649,
667
+ "c?-i1": 654,
668
+ "c?-j1": 820,
669
+ "c?-k1": 720,
670
+ "c?-l1": 894,
671
+ "c?-m1": 1038,
672
+ "c?-n1": 1445,
673
+ "c?-o1": 1608,
674
+ "c?-p1": 1992,
675
+ "c?-q1": 1945,
676
+ "c?-r1": 2066,
677
+ "d?-": 247,
678
+ "d?-e1": 275,
679
+ "d?-e2": 1050,
680
+ "d?-f1": 961,
681
+ "d?-g1": 754,
682
+ "d?-h1": 640,
683
+ "d?-i1": 788,
684
+ "d?-j1": 1001,
685
+ "d?-k1": 1360,
686
+ "e?-": 276,
687
+ "e?-f1": 295,
688
+ "e?-f2": 1197,
689
+ "e?-g1": 953,
690
+ "e?-h1": 817,
691
+ "e?-i1": 1305,
692
+ "f1-a?|b?|c?|d?|e?}": 1798,
693
+ "f?-": 309,
694
+ "f?-g1": 363,
695
+ "f?-g2": 499,
696
+ "f?-h1": 1396,
697
+ "f?-i1": 1940,
698
+ "g1-a?|b?|c?|d?|e?|f?}": 1672,
699
+ "g?-": 329,
700
+ "g?-h1": 401,
701
+ "g?-h2": 885,
702
+ "g?-i1": 1178,
703
+ "g?-j1": 1513,
704
+ "h?-": 352,
705
+ "h?-i1": 409,
706
+ "h?-i2": 893,
707
+ "h?-j1": 1301,
708
+ "i1-a?|b?|c?|d?|e?|f?|g?|h?}": 1961,
709
+ "i1-a?|b?|c?|d?|e?}": 2077,
710
+ "i?-": 345,
711
+ "i?-j1": 415,
712
+ "i?-j2": 859,
713
+ "i?-k1": 1317,
714
+ "i?-l1": 1193,
715
+ "j1-a?|b?|c?|d?|e?|f?|g?|h?|i?}": 1905,
716
+ "j?-": 405,
717
+ "j?-k1": 470,
718
+ "j?-k2": 873,
719
+ "j?-l1": 1752,
720
+ "j?-m1": 1626,
721
+ "j?-n1": 2093,
722
+ "k?-": 416,
723
+ "k?-l1": 496,
724
+ "k?-l2": 1116,
725
+ "k?-m1": 1888,
726
+ "k?-n1": 1780,
727
+ "k?-o1": 1124,
728
+ "l?-": 450,
729
+ "l?-m1": 516,
730
+ "l?-m2": 1383,
731
+ "l?-n1": 1790,
732
+ "l?-o1": 1991,
733
+ "l?-p1": 1726,
734
+ "l?-q1": 1804,
735
+ "m?-": 481,
736
+ "m?-n1": 620,
737
+ "m?-n2": 950,
738
+ "m?-p1": 1686,
739
+ "m?-q1": 2021,
740
+ "m?-r1": 1948,
741
+ "n?-": 590,
742
+ "n?-o1": 734,
743
+ "n?-o2": 1220,
744
+ "n?-q1": 1893,
745
+ "o?-": 637,
746
+ "o?-p1": 752,
747
+ "o?-p2": 1503,
748
+ "p?-": 778,
749
+ "p?-q1": 952,
750
+ "p?-q2": 1433,
751
+ "q?-": 843,
752
+ "q?-r1": 1026,
753
+ "q?-r2": 1993,
754
+ "r?-": 963,
755
+ "r?-s1": 1138,
756
+ "s?-": 1173,
757
+ "s?-t1": 1428,
758
+ "t?-": 1829
759
+ },
760
+ "definition": "non-special vocabulary token containing literal question mark",
761
+ "schema": "bertose_a5_literal_question_mark_ambiguity_v1",
762
+ "source_vocabulary": "review_bpe_vocabulary.json",
763
+ "training_authorized": false
764
+ }
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json ADDED
The diff for this file is too large to render. See raw diff
 
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_sequence_tree_runtime.py ADDED
@@ -0,0 +1,287 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Fail-closed WURCS tree channels for the review-only A5 runtime.
2
+
3
+ The fitted BPE vocabulary is deliberately not changed here. This module only
4
+ repairs deterministic transform-time annotations. A tree depth is emitted
5
+ only when every linkage is a full, unambiguous residue-pair token, exactly one
6
+ endpoint of every bond is the anomeric donor, and the resulting directed graph
7
+ is one rooted tree. Otherwise residue tokens receive an explicit UNKNOWN
8
+ depth while the original WURCS units remain available to BPE.
9
+
10
+ This file is an isolated review runtime. It must not be copied over the shared
11
+ ``training/a5_wurcs_bpe.py`` module.
12
+ """
13
+
14
+ from __future__ import annotations
15
+
16
+ from collections import defaultdict, deque
17
+ from dataclasses import dataclass
18
+ from pathlib import Path
19
+ from typing import Optional
20
+
21
+ from training.a5_topology import node_index, node_label
22
+ from training.a5_wurcs_bpe import A5WURCSBPETokenizer, A5WURCSPreTokenizer
23
+
24
+
25
+ BRANCH_DEPTH_UNKNOWN = 202
26
+ BRANCH_DEPTH_VOCAB_SIZE = 203
27
+
28
+ LINKAGE_NONE = 0
29
+ LINKAGE_1_2 = 1
30
+ LINKAGE_1_3 = 2
31
+ LINKAGE_1_4 = 3
32
+ LINKAGE_1_6 = 4
33
+ LINKAGE_2_3 = 5
34
+ LINKAGE_2_6 = 6
35
+ LINKAGE_3_6 = 7
36
+ LINKAGE_OTHER_EXACT = 8
37
+ LINKAGE_UNKNOWN = 9
38
+ LINKAGE_VOCAB_SIZE = 10
39
+
40
+
41
+ @dataclass(frozen=True)
42
+ class TreeChannelAnalysis:
43
+ residue_count: int
44
+ exact_undirected_tree: bool
45
+ exact_rooted_tree: bool
46
+ failure_reason: str
47
+ depths: dict[int, int]
48
+ branch_open_counts: dict[int, int]
49
+ distance_matrix: list[list[int]]
50
+
51
+
52
+ def _distance_matrix(adjacency: dict[int, set[int]], count: int) -> list[list[int]]:
53
+ matrix: list[list[int]] = []
54
+ for source in range(count):
55
+ distances = {source: 0}
56
+ queue: deque[int] = deque([source])
57
+ while queue:
58
+ current = queue.popleft()
59
+ for neighbor in sorted(adjacency[current]):
60
+ if neighbor not in distances:
61
+ distances[neighbor] = distances[current] + 1
62
+ queue.append(neighbor)
63
+ if len(distances) != count:
64
+ return []
65
+ matrix.append([distances[target] for target in range(count)])
66
+ return matrix
67
+
68
+
69
+ class ReviewA5WURCSPreTokenizerV3(A5WURCSPreTokenizer):
70
+ """A5 semantic units with fail-closed branch and linkage channels."""
71
+
72
+ def _sections(
73
+ self, wurcs: str
74
+ ) -> tuple[list[str], list[str], list[str], list[int | None]]:
75
+ parts = self._split_wurcs_sections(wurcs)
76
+ if len(parts) < 4:
77
+ return [], [], [], []
78
+ residue_definitions = self.RESIDUE_PATTERN.findall(parts[2])
79
+ index_list = parts[3].split("-") if parts[3] else []
80
+ linkages = parts[4].split("_") if len(parts) > 4 and parts[4] else []
81
+ anomeric = self._instance_anomeric_positions(residue_definitions, index_list)
82
+ return residue_definitions, index_list, linkages, anomeric
83
+
84
+ def analyze_tree(self, wurcs: str) -> TreeChannelAnalysis:
85
+ _, index_list, linkages, anomeric = self._sections(wurcs)
86
+ count = len(index_list)
87
+ if count == 0:
88
+ return TreeChannelAnalysis(0, False, False, "missing_residue_instances", {}, {}, [])
89
+ if count == 1:
90
+ if linkages:
91
+ return TreeChannelAnalysis(
92
+ 1, False, False, "single_residue_has_linkage_tokens", {}, {}, []
93
+ )
94
+ return TreeChannelAnalysis(1, True, True, "", {0: 0}, {}, [[0]])
95
+
96
+ adjacency: dict[int, set[int]] = {index: set() for index in range(count)}
97
+ directed_children: dict[int, list[int]] = defaultdict(list)
98
+ child_parent: dict[int, int] = {}
99
+ seen_edges: set[tuple[int, int]] = set()
100
+ orientation_failure = ""
101
+
102
+ for linkage in linkages:
103
+ match = self.A5_LINKAGE_PATTERN.fullmatch(linkage)
104
+ if match is None:
105
+ return TreeChannelAnalysis(
106
+ count, False, False, "non_exact_linkage_token", {}, {}, []
107
+ )
108
+ left_label, left_position, right_label, right_position = match.groups()
109
+ try:
110
+ left = node_index(left_label) - 1
111
+ right = node_index(right_label) - 1
112
+ except ValueError:
113
+ return TreeChannelAnalysis(
114
+ count, False, False, "invalid_residue_label", {}, {}, []
115
+ )
116
+ if not (0 <= left < count and 0 <= right < count) or left == right:
117
+ return TreeChannelAnalysis(
118
+ count, False, False, "invalid_residue_edge", {}, {}, []
119
+ )
120
+ edge = tuple(sorted((left, right)))
121
+ if edge in seen_edges:
122
+ return TreeChannelAnalysis(
123
+ count, False, False, "duplicate_residue_edge", {}, {}, []
124
+ )
125
+ seen_edges.add(edge)
126
+ adjacency[left].add(right)
127
+ adjacency[right].add(left)
128
+
129
+ left_is_donor = (
130
+ left_position != "?"
131
+ and anomeric[left] is not None
132
+ and int(left_position) == anomeric[left]
133
+ )
134
+ right_is_donor = (
135
+ right_position != "?"
136
+ and anomeric[right] is not None
137
+ and int(right_position) == anomeric[right]
138
+ )
139
+ if left_is_donor == right_is_donor:
140
+ orientation_failure = "bond_donor_not_unique"
141
+ continue
142
+ child = left if left_is_donor else right
143
+ parent = right if left_is_donor else left
144
+ if child in child_parent and child_parent[child] != parent:
145
+ orientation_failure = "residue_has_multiple_parents"
146
+ continue
147
+ child_parent[child] = parent
148
+ directed_children[parent].append(child)
149
+
150
+ if len(seen_edges) != count - 1:
151
+ return TreeChannelAnalysis(
152
+ count, False, False, "edge_count_not_residue_count_minus_one", {}, {}, []
153
+ )
154
+ distances = _distance_matrix(adjacency, count)
155
+ if not distances:
156
+ return TreeChannelAnalysis(
157
+ count, False, False, "residue_graph_disconnected", {}, {}, []
158
+ )
159
+ if orientation_failure:
160
+ return TreeChannelAnalysis(
161
+ count, True, False, orientation_failure, {}, {}, distances
162
+ )
163
+
164
+ roots = sorted(set(range(count)) - set(child_parent))
165
+ if len(roots) != 1:
166
+ return TreeChannelAnalysis(
167
+ count, True, False, "root_not_unique", {}, {}, distances
168
+ )
169
+ depths = {roots[0]: 0}
170
+ queue: deque[int] = deque([roots[0]])
171
+ while queue:
172
+ parent = queue.popleft()
173
+ for child in sorted(directed_children.get(parent, [])):
174
+ if child in depths:
175
+ return TreeChannelAnalysis(
176
+ count, True, False, "directed_cycle", {}, {}, distances
177
+ )
178
+ depths[child] = depths[parent] + 1
179
+ queue.append(child)
180
+ if len(depths) != count:
181
+ return TreeChannelAnalysis(
182
+ count, True, False, "directed_tree_disconnected", {}, {}, distances
183
+ )
184
+ if max(depths.values()) >= BRANCH_DEPTH_UNKNOWN:
185
+ return TreeChannelAnalysis(
186
+ count, True, False, "exact_depth_collides_with_unknown_id", {}, {}, distances
187
+ )
188
+ branch_counts = {
189
+ parent: len(children) - 1
190
+ for parent, children in directed_children.items()
191
+ if len(children) > 1
192
+ }
193
+ return TreeChannelAnalysis(
194
+ count, True, True, "", depths, branch_counts, distances
195
+ )
196
+
197
+ @staticmethod
198
+ def exact_linkage_type(linkage: str) -> int:
199
+ match = ReviewA5WURCSPreTokenizerV3.A5_LINKAGE_PATTERN.fullmatch(linkage)
200
+ if match is None:
201
+ return LINKAGE_UNKNOWN
202
+ _, left_position, _, right_position = match.groups()
203
+ if left_position == "?" or right_position == "?":
204
+ return LINKAGE_UNKNOWN
205
+ positions = tuple(sorted((int(left_position), int(right_position))))
206
+ mapping = {
207
+ (1, 2): LINKAGE_1_2,
208
+ (1, 3): LINKAGE_1_3,
209
+ (1, 4): LINKAGE_1_4,
210
+ (1, 6): LINKAGE_1_6,
211
+ (2, 3): LINKAGE_2_3,
212
+ (2, 6): LINKAGE_2_6,
213
+ (3, 6): LINKAGE_3_6,
214
+ }
215
+ return mapping.get(positions, LINKAGE_OTHER_EXACT)
216
+
217
+ def pre_tokenize(self, wurcs: str) -> list[dict]:
218
+ # Reuse only the already-audited semantic WURCS unit extraction. All
219
+ # v2 branch markers, depths, linkage classes, and distance matrices are
220
+ # discarded and rebuilt below.
221
+ inherited = super().pre_tokenize(wurcs)
222
+ units = [dict(unit) for unit in inherited if unit.get("text") != "[BRANCH_OPEN]"]
223
+ analysis = self.analyze_tree(wurcs)
224
+ first_unit_for_residue: set[int] = set()
225
+ repaired: list[dict] = []
226
+ for unit in units:
227
+ unit.pop("distance_matrix", None)
228
+ residue_id = int(unit.get("residue_id", -1))
229
+ unit["linkage_type"] = LINKAGE_NONE
230
+ if residue_id >= 0:
231
+ depth = (
232
+ analysis.depths[residue_id]
233
+ if analysis.exact_rooted_tree
234
+ else BRANCH_DEPTH_UNKNOWN
235
+ )
236
+ unit["branch_depth"] = depth
237
+ if residue_id not in first_unit_for_residue:
238
+ for _ in range(analysis.branch_open_counts.get(residue_id, 0)):
239
+ repaired.append(
240
+ {
241
+ "text": "[BRANCH_OPEN]",
242
+ "type": "special",
243
+ "residue_id": -1,
244
+ "branch_depth": depth,
245
+ "linkage_type": LINKAGE_NONE,
246
+ }
247
+ )
248
+ first_unit_for_residue.add(residue_id)
249
+ else:
250
+ unit["branch_depth"] = 0
251
+ if unit.get("type") == "linkage":
252
+ unit["linkage_type"] = self.exact_linkage_type(str(unit["text"]))
253
+ repaired.append(unit)
254
+
255
+ if repaired:
256
+ repaired[0]["distance_matrix"] = analysis.distance_matrix
257
+ repaired[0]["tree_channels_valid"] = analysis.exact_rooted_tree
258
+ repaired[0]["tree_channel_failure_reason"] = analysis.failure_reason
259
+ return repaired
260
+
261
+
262
+ class ReviewA5WURCSBPETokenizerV3(A5WURCSBPETokenizer):
263
+ """Existing train-only BPE vocabulary with repaired transform channels."""
264
+
265
+ def __init__(self, vocab_path: Optional[str | Path] = None) -> None:
266
+ super().__init__(vocab_path)
267
+ self.pre_tokenizer = ReviewA5WURCSPreTokenizerV3()
268
+
269
+ def tokenize_sequence(self, wurcs: str, max_length: int = 256) -> dict:
270
+ encoded = super().tokenize_sequence(wurcs, max_length=max_length)
271
+ analysis = self.pre_tokenizer.analyze_tree(wurcs)
272
+ encoded["tree_channels_valid"] = analysis.exact_rooted_tree
273
+ encoded["tree_channel_failure_reason"] = analysis.failure_reason
274
+ return encoded
275
+
276
+
277
+ __all__ = [
278
+ "BRANCH_DEPTH_UNKNOWN",
279
+ "BRANCH_DEPTH_VOCAB_SIZE",
280
+ "LINKAGE_NONE",
281
+ "LINKAGE_OTHER_EXACT",
282
+ "LINKAGE_UNKNOWN",
283
+ "LINKAGE_VOCAB_SIZE",
284
+ "ReviewA5WURCSBPETokenizerV3",
285
+ "ReviewA5WURCSPreTokenizerV3",
286
+ "TreeChannelAnalysis",
287
+ ]
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_training_a5_topology.py ADDED
@@ -0,0 +1,180 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Residue-level shortest-path targets used by the A5 topology objective."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import hashlib
6
+ import json
7
+ import re
8
+ from collections import deque
9
+
10
+
11
+ EDGE_RE = re.compile(
12
+ r"(?P<n1>[A-Za-z]+)(?P<p1>[0-9?]+)-(?P<n2>[A-Za-z]+)(?P<p2>[0-9?]+)"
13
+ )
14
+ RESDEF_RE = re.compile(r"\[([^\]]*)\]")
15
+ WURCS_RES_INDEX_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
16
+
17
+
18
+ def node_label(index: int) -> str:
19
+ if index < 1:
20
+ raise ValueError("WURCS residue indices are one-based")
21
+ letters = WURCS_RES_INDEX_ALPHABET
22
+ base = len(letters)
23
+ index -= 1
24
+ label = ""
25
+ while True:
26
+ label = letters[index % base] + label
27
+ index = index // base - 1
28
+ if index < 0:
29
+ return label
30
+
31
+
32
+ def node_index(label: str) -> int:
33
+ if not label:
34
+ raise ValueError("WURCS residue label cannot be empty")
35
+ base = len(WURCS_RES_INDEX_ALPHABET)
36
+ value = 0
37
+ for character in label:
38
+ try:
39
+ digit = WURCS_RES_INDEX_ALPHABET.index(character) + 1
40
+ except ValueError as error:
41
+ raise ValueError(f"Invalid WURCS residue label: {label}") from error
42
+ value = value * base + digit
43
+ return value
44
+
45
+
46
+ def split_wurcs_sections(body: str) -> list[str]:
47
+ sections: list[str] = []
48
+ current: list[str] = []
49
+ bracket_depth = 0
50
+ for character in body:
51
+ if character == "[":
52
+ bracket_depth += 1
53
+ elif character == "]" and bracket_depth:
54
+ bracket_depth -= 1
55
+ if character == "/" and bracket_depth == 0:
56
+ sections.append("".join(current))
57
+ current = []
58
+ else:
59
+ current.append(character)
60
+ sections.append("".join(current))
61
+ return sections
62
+
63
+
64
+ def split_wurcs(wurcs: str) -> tuple[list[str], dict[str, str], str]:
65
+ if not wurcs.startswith("WURCS=2.0/"):
66
+ return [], {}, ""
67
+ parts = split_wurcs_sections(wurcs[len("WURCS=2.0/") :])
68
+ if len(parts) < 4:
69
+ return [], {}, ""
70
+ residue_definitions = RESDEF_RE.findall(parts[1])
71
+ sequence = parts[2].split("-") if parts[2] else []
72
+ node_to_residue: dict[str, str] = {}
73
+ for index, value in enumerate(sequence, start=1):
74
+ try:
75
+ residue = residue_definitions[int(value) - 1]
76
+ except (ValueError, IndexError):
77
+ residue = ""
78
+ node_to_residue[node_label(index)] = residue
79
+ return residue_definitions, node_to_residue, "/".join(parts[3:])
80
+
81
+
82
+ def _hash_fields(*values: object) -> str:
83
+ payload = "\x1f".join(str(value) for value in values).encode("utf-8")
84
+ return hashlib.sha256(payload).hexdigest()
85
+
86
+
87
+ def build_residue_shortest_path(wurcs: str) -> dict[str, object]:
88
+ """Build the same immutable residue target frozen by the P0/P1 audit."""
89
+ _, node_to_residue, linkage_section = split_wurcs(wurcs)
90
+ node_ids = list(node_to_residue)
91
+ if not node_ids or any(not node_to_residue[node] for node in node_ids):
92
+ return _failure("missing_or_unmapped_residue_sequence", len(node_ids), 0)
93
+ if len(node_ids) < 2:
94
+ return _failure(
95
+ "fewer_than_two_residues_no_off_diagonal_target", len(node_ids), 0
96
+ )
97
+
98
+ adjacency: dict[str, set[str]] = {node: set() for node in node_ids}
99
+ parsed_edges: list[tuple[str, str]] = []
100
+ unparsed_tokens: list[str] = []
101
+ for token in linkage_section.split("_") if linkage_section else []:
102
+ match = EDGE_RE.fullmatch(token)
103
+ if not match:
104
+ if token:
105
+ unparsed_tokens.append(token)
106
+ continue
107
+ left, right = match.group("n1"), match.group("n2")
108
+ if left not in adjacency or right not in adjacency or left == right:
109
+ unparsed_tokens.append(token)
110
+ continue
111
+ parsed_edges.append(tuple(sorted((left, right))))
112
+ adjacency[left].add(right)
113
+ adjacency[right].add(left)
114
+
115
+ reason = ""
116
+ if unparsed_tokens:
117
+ reason = "one_or_more_edge_tokens_not_unambiguous_residue_pairs"
118
+ elif len(set(parsed_edges)) != len(parsed_edges):
119
+ reason = "duplicate_residue_edge"
120
+ elif len(parsed_edges) != len(node_ids) - 1:
121
+ reason = "edge_count_not_residue_count_minus_one"
122
+
123
+ seen = {node_ids[0]}
124
+ queue = deque([node_ids[0]])
125
+ while queue:
126
+ node = queue.popleft()
127
+ for neighbor in sorted(adjacency[node]):
128
+ if neighbor not in seen:
129
+ seen.add(neighbor)
130
+ queue.append(neighbor)
131
+ if not reason and len(seen) != len(node_ids):
132
+ reason = "residue_graph_disconnected"
133
+ if reason:
134
+ return _failure(reason, len(node_ids), len(parsed_edges))
135
+
136
+ matrix: list[list[int]] = []
137
+ for source in node_ids:
138
+ distance = {source: 0}
139
+ queue = deque([source])
140
+ while queue:
141
+ node = queue.popleft()
142
+ for neighbor in sorted(adjacency[node]):
143
+ if neighbor not in distance:
144
+ distance[neighbor] = distance[node] + 1
145
+ queue.append(neighbor)
146
+ matrix.append([distance[target] for target in node_ids])
147
+
148
+ matrix_payload = json.dumps(matrix, separators=(",", ":"))
149
+ matrix_sha256 = hashlib.sha256(matrix_payload.encode("utf-8")).hexdigest()
150
+ return {
151
+ "target_valid": True,
152
+ "failure_reason": "",
153
+ "residue_count": len(node_ids),
154
+ "parsed_edge_count": len(parsed_edges),
155
+ "valid_unordered_pair_count": len(node_ids) * (len(node_ids) - 1) // 2,
156
+ "matrix": matrix,
157
+ "matrix_sha256": matrix_sha256,
158
+ "target_identity_sha256": _hash_fields(
159
+ "residue_shortest_path_v2",
160
+ wurcs,
161
+ matrix_sha256,
162
+ len(node_ids),
163
+ len(parsed_edges),
164
+ ),
165
+ "schema": "residue_shortest_path_v2",
166
+ }
167
+
168
+
169
+ def _failure(reason: str, residues: int, edges: int) -> dict[str, object]:
170
+ return {
171
+ "target_valid": False,
172
+ "failure_reason": reason,
173
+ "residue_count": residues,
174
+ "parsed_edge_count": edges,
175
+ "valid_unordered_pair_count": 0,
176
+ "matrix": [],
177
+ "matrix_sha256": "",
178
+ "target_identity_sha256": "",
179
+ "schema": "residue_shortest_path_v2",
180
+ }
releases/2026-10-02/iar_completion/runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_training_a5_wurcs_bpe.py ADDED
@@ -0,0 +1,368 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Deterministic train-only WURCS BPE utilities for A5."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import re
6
+ from collections import Counter, defaultdict
7
+ from pathlib import Path
8
+ from typing import Iterable, Optional
9
+
10
+ from .a5_topology import node_index, node_label
11
+
12
+ try:
13
+ from bert_training_v4.downstream_tasks.utils.wurcs_bpe_tokenizer import (
14
+ WURCSBPETokenizer,
15
+ WURCSPreTokenizer,
16
+ )
17
+ except ImportError:
18
+ from ..bert_training_v4.downstream_tasks.utils.wurcs_bpe_tokenizer import (
19
+ WURCSBPETokenizer,
20
+ WURCSPreTokenizer,
21
+ )
22
+
23
+
24
+ SUBMITTED_FIT_ALPHABET = tuple(
25
+ "#$%()*-./0123456789<=>?ABCDEFGHIJKLMNOPQRSTUVWXYZ^abcdefghijklmnopqrstuvwxyz{|}~"
26
+ )
27
+
28
+
29
+ class _TopLevelBracketPattern:
30
+ """Expose regex-like findall while respecting nested SMILES brackets."""
31
+
32
+ @staticmethod
33
+ def findall(text: str) -> list[str]:
34
+ residues: list[str] = []
35
+ depth = 0
36
+ start = -1
37
+ for index, character in enumerate(text):
38
+ if character == "[":
39
+ if depth == 0:
40
+ start = index + 1
41
+ depth += 1
42
+ elif character == "]" and depth:
43
+ depth -= 1
44
+ if depth == 0 and start >= 0:
45
+ residues.append(text[start:index])
46
+ start = -1
47
+ if depth:
48
+ return []
49
+ return residues
50
+
51
+
52
+ class A5WURCSPreTokenizer(WURCSPreTokenizer):
53
+ """Submitted pre-tokenizer with nested bracket-safe residue extraction."""
54
+
55
+ A5_LINKAGE_PATTERN = re.compile(
56
+ r"([A-Za-z]+)(\d+|\?)-([A-Za-z]+)(\d+|\?)"
57
+ )
58
+ A5_ANOMERIC_PATTERN = re.compile(r"-([0-9]+)([abuxo])(?:_|$)")
59
+
60
+ def __init__(self) -> None:
61
+ super().__init__()
62
+ self.RESIDUE_PATTERN = _TopLevelBracketPattern()
63
+
64
+ def _split_wurcs_sections(self, wurcs: str) -> list[str]:
65
+ sections = super()._split_wurcs_sections(wurcs)
66
+ if len(sections) <= 5:
67
+ return sections
68
+ # WURCS LIN modifications may contain unbracketed slashes. The format
69
+ # has exactly five top-level sections, so every trailing fragment
70
+ # belongs to the linkage section rather than a new section.
71
+ return sections[:4] + ["/".join(sections[4:])]
72
+
73
+ def _instance_anomeric_positions(
74
+ self, residue_definitions: list[str], index_list: list[str]
75
+ ) -> list[int | None]:
76
+ positions: list[int | None] = []
77
+ for definition_index in index_list:
78
+ try:
79
+ residue_definition = residue_definitions[int(definition_index) - 1]
80
+ except (IndexError, ValueError):
81
+ positions.append(None)
82
+ continue
83
+ match = self.A5_ANOMERIC_PATTERN.search(residue_definition)
84
+ positions.append(int(match.group(1)) if match is not None else None)
85
+ return positions
86
+
87
+ def _analyze_a5_tree(
88
+ self,
89
+ linkages: list[str],
90
+ residue_count: int,
91
+ anomeric_positions: list[int | None] | None = None,
92
+ ) -> tuple[dict[str, int], dict[str, int], dict[int, list[int]]]:
93
+ branch_points: dict[str, int] = defaultdict(int)
94
+ children: dict[str, list[str]] = defaultdict(list)
95
+ all_residues: set[str] = set()
96
+ adjacency: dict[int, list[int]] = defaultdict(list)
97
+ for linkage in linkages:
98
+ match = self.A5_LINKAGE_PATTERN.match(linkage)
99
+ if not match:
100
+ continue
101
+ left, left_position, right, right_position = match.groups()
102
+ try:
103
+ left_index = node_index(left) - 1
104
+ right_index = node_index(right) - 1
105
+ except ValueError:
106
+ continue
107
+ if not (
108
+ 0 <= left_index < residue_count
109
+ and 0 <= right_index < residue_count
110
+ and left_index != right_index
111
+ ):
112
+ continue
113
+ parent, child = left, right
114
+ if anomeric_positions is not None:
115
+ left_anomeric = anomeric_positions[left_index]
116
+ right_anomeric = anomeric_positions[right_index]
117
+ left_is_donor = (
118
+ left_position != "?"
119
+ and left_anomeric is not None
120
+ and int(left_position) == left_anomeric
121
+ )
122
+ right_is_donor = (
123
+ right_position != "?"
124
+ and right_anomeric is not None
125
+ and int(right_position) == right_anomeric
126
+ )
127
+ if left_is_donor and not right_is_donor:
128
+ parent, child = right, left
129
+ elif right_is_donor and not left_is_donor:
130
+ parent, child = left, right
131
+ children[parent].append(child)
132
+ all_residues.update((parent, child))
133
+ adjacency[left_index].append(right_index)
134
+ adjacency[right_index].append(left_index)
135
+
136
+ for residue, child_labels in children.items():
137
+ if len(child_labels) > 1:
138
+ branch_points[residue] = len(child_labels) - 1
139
+ child_set = {child for values in children.values() for child in values}
140
+ roots = all_residues - child_set
141
+ root = min(roots, key=node_index) if roots else node_label(1)
142
+ depths = {root: 0}
143
+ queue = [root]
144
+ while queue:
145
+ current = queue.pop(0)
146
+ for child in children.get(current, []):
147
+ if child not in depths:
148
+ depths[child] = depths[current] + 1
149
+ queue.append(child)
150
+ return dict(branch_points), depths, adjacency
151
+
152
+ def _a5_linkage_type(self, linkage: str) -> int:
153
+ match = self.A5_LINKAGE_PATTERN.match(linkage)
154
+ if not match:
155
+ return 8
156
+ _, from_position, _, to_position = match.groups()
157
+ try:
158
+ positions = (int(from_position), int(to_position))
159
+ except ValueError:
160
+ return 8
161
+ linkage_types = {
162
+ (1, 2): 0,
163
+ (2, 1): 0,
164
+ (1, 3): 1,
165
+ (3, 1): 1,
166
+ (1, 4): 2,
167
+ (4, 1): 2,
168
+ (1, 6): 3,
169
+ (6, 1): 3,
170
+ (2, 3): 4,
171
+ (3, 2): 4,
172
+ (2, 6): 5,
173
+ (6, 2): 5,
174
+ (3, 6): 6,
175
+ (6, 3): 6,
176
+ }
177
+ return linkage_types.get(positions, 7)
178
+
179
+ def pre_tokenize(self, wurcs: str) -> list[dict]:
180
+ # Reuse the submitted semantic split, then correct only its tree
181
+ # annotations. This keeps the BPE unit text and special-token behavior.
182
+ inherited_units = super().pre_tokenize(wurcs)
183
+ try:
184
+ parts = self._split_wurcs_sections(wurcs)
185
+ index_list = parts[3].split("-") if len(parts) > 3 and parts[3] else []
186
+ linkages = parts[4].split("_") if len(parts) > 4 and parts[4] else []
187
+ residue_definitions = self.RESIDUE_PATTERN.findall(parts[2])
188
+ anomeric_positions = self._instance_anomeric_positions(
189
+ residue_definitions, index_list
190
+ )
191
+ branch_points, depths, adjacency = self._analyze_a5_tree(
192
+ linkages, len(index_list), anomeric_positions
193
+ )
194
+ except (IndexError, ValueError):
195
+ return inherited_units
196
+
197
+ units: list[dict] = []
198
+ opened_residues: set[int] = set()
199
+ for inherited in inherited_units:
200
+ if inherited.get("text") == "[BRANCH_OPEN]":
201
+ continue
202
+ unit = dict(inherited)
203
+ residue_id = int(unit.get("residue_id", -1))
204
+ if residue_id >= 0:
205
+ label = node_label(residue_id + 1)
206
+ if residue_id not in opened_residues:
207
+ for _ in range(branch_points.get(label, 0)):
208
+ units.append(
209
+ {
210
+ "text": "[BRANCH_OPEN]",
211
+ "type": "special",
212
+ "residue_id": -1,
213
+ "branch_depth": depths.get(label, 0),
214
+ "linkage_type": 0,
215
+ }
216
+ )
217
+ opened_residues.add(residue_id)
218
+ unit["branch_depth"] = depths.get(label, 0)
219
+ elif unit.get("type") == "linkage":
220
+ unit["linkage_type"] = self._a5_linkage_type(str(unit["text"]))
221
+ units.append(unit)
222
+
223
+ if units:
224
+ units[0]["distance_matrix"] = self._compute_distance_matrix(
225
+ adjacency, len(index_list)
226
+ )
227
+ return units
228
+
229
+
230
+ class A5WURCSBPETokenizer(WURCSBPETokenizer):
231
+ """Train-only BPE with a fit-derived alphabet and deterministic merge ties."""
232
+
233
+ def __init__(self, vocab_path: Optional[str | Path] = None) -> None:
234
+ super().__init__(str(vocab_path) if vocab_path is not None else None)
235
+ self.pre_tokenizer = A5WURCSPreTokenizer()
236
+
237
+ @classmethod
238
+ def train_from_fit_corpus(
239
+ cls,
240
+ wurcs_strings: Iterable[str],
241
+ num_merges: int = 2000,
242
+ output_path: Optional[str | Path] = None,
243
+ min_frequency: int = 2,
244
+ max_token_length: Optional[int] = None,
245
+ ) -> "A5WURCSBPETokenizer":
246
+ tokenizer = cls()
247
+ pre_tokenizer = tokenizer.pre_tokenizer
248
+ unit_counts: Counter[str] = Counter()
249
+ for wurcs in wurcs_strings:
250
+ for unit in pre_tokenizer.pre_tokenize(wurcs):
251
+ if unit["type"] != "special":
252
+ unit_counts[unit["text"]] += 1
253
+
254
+ # The submitted tokenizer learned the base character inventory from its
255
+ # corpus. Preserve that behavior, but fit it from training parents only.
256
+ tokenizer.fit_alphabet = tuple(
257
+ sorted({char for unit in unit_counts for char in unit})
258
+ )
259
+
260
+ next_id = len(tokenizer.special_tokens)
261
+ for char in tokenizer.fit_alphabet:
262
+ tokenizer.token_to_id[char] = next_id
263
+ tokenizer.id_to_token[next_id] = char
264
+ next_id += 1
265
+
266
+ word_freqs: dict[tuple[str, ...], int] = {
267
+ tuple(unit): count
268
+ for unit, count in sorted(unit_counts.items())
269
+ if count >= min_frequency
270
+ }
271
+ merges: list[tuple[str, str]] = []
272
+ for _ in range(num_merges):
273
+ pair_counts: Counter[tuple[str, str]] = Counter()
274
+ for word, frequency in word_freqs.items():
275
+ for index in range(len(word) - 1):
276
+ pair_counts[(word[index], word[index + 1])] += frequency
277
+ candidates = [
278
+ (pair, count)
279
+ for pair, count in pair_counts.items()
280
+ if count >= min_frequency
281
+ and (
282
+ max_token_length is None
283
+ or len(pair[0]) + len(pair[1]) <= max_token_length
284
+ )
285
+ ]
286
+ if not candidates:
287
+ break
288
+ best_pair, _ = min(
289
+ candidates,
290
+ key=lambda item: (-item[1], item[0][0], item[0][1]),
291
+ )
292
+ new_token = best_pair[0] + best_pair[1]
293
+ merges.append(best_pair)
294
+ tokenizer.token_to_id[new_token] = next_id
295
+ tokenizer.id_to_token[next_id] = new_token
296
+ next_id += 1
297
+
298
+ merged_freqs: Counter[tuple[str, ...]] = Counter()
299
+ for word, frequency in word_freqs.items():
300
+ merged_word: list[str] = []
301
+ index = 0
302
+ while index < len(word):
303
+ if (
304
+ index < len(word) - 1
305
+ and word[index] == best_pair[0]
306
+ and word[index + 1] == best_pair[1]
307
+ ):
308
+ merged_word.append(new_token)
309
+ index += 2
310
+ else:
311
+ merged_word.append(word[index])
312
+ index += 1
313
+ merged_freqs[tuple(merged_word)] += frequency
314
+ word_freqs = dict(merged_freqs)
315
+
316
+ tokenizer.merges = merges
317
+ if output_path is not None:
318
+ tokenizer.save_vocab(str(output_path))
319
+ return tokenizer
320
+
321
+ def tokenize_sequence(self, wurcs: str, max_length: int = 256) -> dict:
322
+ units = self.pre_tokenizer.pre_tokenize(wurcs)
323
+ tokens: list[str] = []
324
+ token_ids: list[int] = []
325
+ residue_ids: list[int] = []
326
+ branch_depths: list[int] = []
327
+ linkage_types: list[int] = []
328
+
329
+ for unit in units:
330
+ unit_tokens = (
331
+ [unit["text"]]
332
+ if unit["type"] == "special"
333
+ else self.apply_bpe(unit["text"])
334
+ )
335
+ for token in unit_tokens:
336
+ tokens.append(token)
337
+ token_ids.append(
338
+ self.token_to_id.get(token, self.token_to_id["[UNK]"])
339
+ )
340
+ residue_ids.append(int(unit["residue_id"]))
341
+ branch_depths.append(int(unit["branch_depth"]))
342
+ linkage_types.append(int(unit["linkage_type"]))
343
+
344
+ if len(tokens) > max_length:
345
+ tokens = tokens[: max_length - 1] + ["[END]"]
346
+ token_ids = token_ids[: max_length - 1] + [self.token_to_id["[END]"]]
347
+ residue_ids = residue_ids[: max_length - 1] + [-1]
348
+ branch_depths = branch_depths[: max_length - 1] + [0]
349
+ linkage_types = linkage_types[: max_length - 1] + [0]
350
+
351
+ length = len(tokens)
352
+ attention_mask = [1] * length
353
+ padding = max_length - length
354
+ tokens.extend(["[PAD]"] * padding)
355
+ token_ids.extend([self.token_to_id["[PAD]"]] * padding)
356
+ residue_ids.extend([-1] * padding)
357
+ branch_depths.extend([0] * padding)
358
+ linkage_types.extend([0] * padding)
359
+ attention_mask.extend([0] * padding)
360
+ return {
361
+ "tokens": tokens,
362
+ "token_ids": token_ids,
363
+ "residue_ids": residue_ids,
364
+ "branch_depths": branch_depths,
365
+ "linkage_types": linkage_types,
366
+ "attention_mask": attention_mask,
367
+ "length": length,
368
+ }
releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/model/__init__.py ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Model module
2
+ from .multimodal_glycan_bert_v3 import MultimodalGlycanBERT, MultimodalGlycanBERTConfig
3
+
releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/model/glycan_bert.py ADDED
@@ -0,0 +1,303 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Glycan BERT Model
3
+
4
+ Transformer-based masked language model for glycan structures.
5
+ Based on BERT/ESM2 architecture adapted for atomic glycan tokenization.
6
+ """
7
+
8
+ import torch
9
+ import torch.nn as nn
10
+ import math
11
+
12
+
13
+ class GlycanBERTConfig:
14
+ """Configuration for GlycanBERT."""
15
+
16
+ def __init__(
17
+ self,
18
+ vocab_size: int = 102,
19
+ hidden_size: int = 384,
20
+ num_hidden_layers: int = 6,
21
+ num_attention_heads: int = 6,
22
+ intermediate_size: int = 1536,
23
+ hidden_dropout_prob: float = 0.1,
24
+ attention_probs_dropout_prob: float = 0.1,
25
+ max_position_embeddings: int = 512,
26
+ layer_norm_eps: float = 1e-12,
27
+ pad_token_id: int = 0,
28
+ mask_token_id: int = 4,
29
+ initializer_range: float = 0.02
30
+ ):
31
+ self.vocab_size = vocab_size
32
+ self.hidden_size = hidden_size
33
+ self.num_hidden_layers = num_hidden_layers
34
+ self.num_attention_heads = num_attention_heads
35
+ self.intermediate_size = intermediate_size
36
+ self.hidden_dropout_prob = hidden_dropout_prob
37
+ self.attention_probs_dropout_prob = attention_probs_dropout_prob
38
+ self.max_position_embeddings = max_position_embeddings
39
+ self.layer_norm_eps = layer_norm_eps
40
+ self.pad_token_id = pad_token_id
41
+ self.mask_token_id = mask_token_id
42
+ self.initializer_range = initializer_range
43
+
44
+
45
+ class GlycanBERTEmbeddings(nn.Module):
46
+ """
47
+ Embeddings for glycan tokens including token and positional embeddings.
48
+ """
49
+
50
+ def __init__(self, config: GlycanBERTConfig):
51
+ super().__init__()
52
+ self.token_embeddings = nn.Embedding(config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id)
53
+ self.position_embeddings = nn.Embedding(config.max_position_embeddings, config.hidden_size)
54
+
55
+ self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
56
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
57
+
58
+ # position_ids (1, max_seq_len) is contiguous in memory and exported when serialized
59
+ self.register_buffer("position_ids", torch.arange(config.max_position_embeddings).expand((1, -1)))
60
+
61
+ def forward(self, input_ids: torch.Tensor) -> torch.Tensor:
62
+ """
63
+ Args:
64
+ input_ids: Tensor of shape (batch_size, seq_len)
65
+
66
+ Returns:
67
+ Embeddings of shape (batch_size, seq_len, hidden_size)
68
+ """
69
+ batch_size, seq_len = input_ids.shape
70
+
71
+ # Token embeddings
72
+ token_embeds = self.token_embeddings(input_ids)
73
+
74
+ # Position embeddings
75
+ position_ids = self.position_ids[:, :seq_len]
76
+ position_embeds = self.position_embeddings(position_ids)
77
+
78
+ # Combine
79
+ embeddings = token_embeds + position_embeds
80
+ embeddings = self.LayerNorm(embeddings)
81
+ embeddings = self.dropout(embeddings)
82
+
83
+ return embeddings
84
+
85
+
86
+ class GlycanBERTAttention(nn.Module):
87
+ """Multi-head self-attention."""
88
+
89
+ def __init__(self, config: GlycanBERTConfig):
90
+ super().__init__()
91
+ assert config.hidden_size % config.num_attention_heads == 0
92
+
93
+ self.num_attention_heads = config.num_attention_heads
94
+ self.attention_head_size = config.hidden_size // config.num_attention_heads
95
+ self.all_head_size = self.num_attention_heads * self.attention_head_size
96
+
97
+ self.query = nn.Linear(config.hidden_size, self.all_head_size)
98
+ self.key = nn.Linear(config.hidden_size, self.all_head_size)
99
+ self.value = nn.Linear(config.hidden_size, self.all_head_size)
100
+
101
+ self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
102
+
103
+ def transpose_for_scores(self, x: torch.Tensor) -> torch.Tensor:
104
+ """Reshape for multi-head attention."""
105
+ new_shape = x.size()[:-1] + (self.num_attention_heads, self.attention_head_size)
106
+ x = x.view(*new_shape)
107
+ return x.permute(0, 2, 1, 3) # (batch, heads, seq_len, head_size)
108
+
109
+ def forward(
110
+ self,
111
+ hidden_states: torch.Tensor,
112
+ attention_mask: torch.Tensor = None
113
+ ) -> torch.Tensor:
114
+ """
115
+ Args:
116
+ hidden_states: (batch_size, seq_len, hidden_size)
117
+ attention_mask: (batch_size, seq_len) - 1 for valid, 0 for padding
118
+
119
+ Returns:
120
+ Attention output: (batch_size, seq_len, hidden_size)
121
+ """
122
+ batch_size, seq_len, _ = hidden_states.shape
123
+
124
+ # Linear projections
125
+ query_layer = self.transpose_for_scores(self.query(hidden_states))
126
+ key_layer = self.transpose_for_scores(self.key(hidden_states))
127
+ value_layer = self.transpose_for_scores(self.value(hidden_states))
128
+
129
+ # Attention scores
130
+ attention_scores = torch.matmul(query_layer, key_layer.transpose(-1, -2))
131
+ attention_scores = attention_scores / math.sqrt(self.attention_head_size)
132
+
133
+ # Apply attention mask
134
+ if attention_mask is not None:
135
+ # Convert mask to additive mask
136
+ attention_mask = attention_mask.unsqueeze(1).unsqueeze(2) # (batch, 1, 1, seq_len)
137
+ attention_mask = (1.0 - attention_mask) * -10000.0
138
+ attention_scores = attention_scores + attention_mask
139
+
140
+ # Attention probabilities
141
+ attention_probs = nn.functional.softmax(attention_scores, dim=-1)
142
+ attention_probs = self.dropout(attention_probs)
143
+
144
+ # Apply attention to values
145
+ context_layer = torch.matmul(attention_probs, value_layer)
146
+
147
+ # Reshape back
148
+ context_layer = context_layer.permute(0, 2, 1, 3).contiguous()
149
+ new_shape = context_layer.size()[:-2] + (self.all_head_size,)
150
+ context_layer = context_layer.view(*new_shape)
151
+
152
+ return context_layer
153
+
154
+
155
+ class GlycanBERTLayer(nn.Module):
156
+ """Single transformer layer."""
157
+
158
+ def __init__(self, config: GlycanBERTConfig):
159
+ super().__init__()
160
+ self.attention = GlycanBERTAttention(config)
161
+ self.attention_output = nn.Linear(config.hidden_size, config.hidden_size)
162
+ self.attention_layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
163
+
164
+ self.intermediate = nn.Linear(config.hidden_size, config.intermediate_size)
165
+ self.output = nn.Linear(config.intermediate_size, config.hidden_size)
166
+ self.output_layer_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
167
+
168
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
169
+
170
+ def forward(
171
+ self,
172
+ hidden_states: torch.Tensor,
173
+ attention_mask: torch.Tensor = None
174
+ ) -> torch.Tensor:
175
+ """
176
+ Args:
177
+ hidden_states: (batch_size, seq_len, hidden_size)
178
+ attention_mask: (batch_size, seq_len)
179
+
180
+ Returns:
181
+ Output: (batch_size, seq_len, hidden_size)
182
+ """
183
+ # Self-attention
184
+ attention_output = self.attention(hidden_states, attention_mask)
185
+ attention_output = self.attention_output(attention_output)
186
+ attention_output = self.dropout(attention_output)
187
+
188
+ # Add & Norm
189
+ hidden_states = self.attention_layer_norm(hidden_states + attention_output)
190
+
191
+ # Feed-forward
192
+ intermediate_output = self.intermediate(hidden_states)
193
+ intermediate_output = nn.functional.gelu(intermediate_output)
194
+
195
+ layer_output = self.output(intermediate_output)
196
+ layer_output = self.dropout(layer_output)
197
+
198
+ # Add & Norm
199
+ layer_output = self.output_layer_norm(hidden_states + layer_output)
200
+
201
+ return layer_output
202
+
203
+
204
+ class GlycanBERT(nn.Module):
205
+ """
206
+ Glycan BERT model for masked language modeling.
207
+ """
208
+
209
+ def __init__(self, config: GlycanBERTConfig):
210
+ super().__init__()
211
+ self.config = config
212
+
213
+ # Embeddings
214
+ self.embeddings = GlycanBERTEmbeddings(config)
215
+
216
+ # Transformer layers
217
+ self.layers = nn.ModuleList([GlycanBERTLayer(config) for _ in range(config.num_hidden_layers)])
218
+
219
+ # MLM head
220
+ self.mlm_head = nn.Linear(config.hidden_size, config.vocab_size)
221
+
222
+ # Initialize weights
223
+ self.apply(self._init_weights)
224
+
225
+ def _init_weights(self, module):
226
+ """Initialize weights."""
227
+ if isinstance(module, nn.Linear):
228
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
229
+ if module.bias is not None:
230
+ module.bias.data.zero_()
231
+ elif isinstance(module, nn.Embedding):
232
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
233
+ if module.padding_idx is not None:
234
+ module.weight.data[module.padding_idx].zero_()
235
+ elif isinstance(module, nn.LayerNorm):
236
+ module.bias.data.zero_()
237
+ module.weight.data.fill_(1.0)
238
+
239
+ def forward(
240
+ self,
241
+ input_ids: torch.Tensor,
242
+ attention_mask: torch.Tensor = None,
243
+ labels: torch.Tensor = None
244
+ ):
245
+ """
246
+ Args:
247
+ input_ids: (batch_size, seq_len)
248
+ attention_mask: (batch_size, seq_len) - 1 for valid, 0 for padding
249
+ labels: (batch_size, seq_len) - token IDs to predict, -100 for positions to ignore
250
+
251
+ Returns:
252
+ If labels provided: (loss, logits)
253
+ Else: logits
254
+ """
255
+ # Create attention mask if not provided
256
+ if attention_mask is None:
257
+ attention_mask = (input_ids != self.config.pad_token_id).float()
258
+
259
+ # Embeddings
260
+ hidden_states = self.embeddings(input_ids)
261
+
262
+ # Transformer layers
263
+ for layer in self.layers:
264
+ hidden_states = layer(hidden_states, attention_mask)
265
+
266
+ # MLM prediction
267
+ logits = self.mlm_head(hidden_states)
268
+
269
+ # Calculate loss if labels provided
270
+ loss = None
271
+ if labels is not None:
272
+ loss_fct = nn.CrossEntropyLoss() # -100 is ignored
273
+ loss = loss_fct(logits.view(-1, self.config.vocab_size), labels.view(-1))
274
+
275
+ if loss is not None:
276
+ return loss, logits
277
+ return logits
278
+
279
+ def get_embeddings(
280
+ self,
281
+ input_ids: torch.Tensor,
282
+ attention_mask: torch.Tensor = None
283
+ ) -> torch.Tensor:
284
+ """
285
+ Get contextualized embeddings (for downstream tasks).
286
+
287
+ Args:
288
+ input_ids: (batch_size, seq_len)
289
+ attention_mask: (batch_size, seq_len)
290
+
291
+ Returns:
292
+ Embeddings: (batch_size, seq_len, hidden_size)
293
+ """
294
+ if attention_mask is None:
295
+ attention_mask = (input_ids != self.config.pad_token_id).float()
296
+
297
+ hidden_states = self.embeddings(input_ids)
298
+
299
+ for layer in self.layers:
300
+ hidden_states = layer(hidden_states, attention_mask)
301
+
302
+ return hidden_states
303
+
releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/model/multimodal_glycan_bert_v3.py ADDED
@@ -0,0 +1,1084 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """
2
+ Multimodal Glycan BERT Model v3
3
+
4
+ Extends GlycanBERT to handle three modalities:
5
+ - Sequence (WURCS atomic tokenization)
6
+ - MS (mass spectrometry peaks, RT, intensity)
7
+ - 3D structure (VQ-VAE discrete tokens, 4 per residue)
8
+
9
+ Each modality has its own encoder, with cross-attention for sequence-structure alignment.
10
+ """
11
+
12
+ import torch
13
+ import torch.nn as nn
14
+ from typing import Dict, Optional, Tuple
15
+ import math
16
+
17
+ try:
18
+ from .glycan_bert import GlycanBERTConfig, GlycanBERTEmbeddings, GlycanBERTLayer
19
+ except ImportError:
20
+ from glycan_bert import GlycanBERTConfig, GlycanBERTEmbeddings, GlycanBERTLayer
21
+
22
+
23
+ class ConvGlycanBERTEmbeddings(nn.Module):
24
+ """
25
+ Improved Convolutional front-end that mixes local WURCS context before the Transformer.
26
+
27
+ Key improvements over original:
28
+ 1. Position embeddings added BEFORE convolution (provides spatial context to conv)
29
+ 2. Residual connection (conv enriches embeddings rather than replacing them)
30
+ 3. Multi-scale convolutions (kernel sizes 3, 5, 7) for better receptive field
31
+ 4. Proper layer normalization on the residual path
32
+ """
33
+
34
+ def __init__(self, config):
35
+ super().__init__()
36
+ self.token_embeddings = nn.Embedding(
37
+ config.vocab_size, config.hidden_size, padding_idx=config.pad_token_id
38
+ )
39
+ self.position_embeddings = nn.Embedding(
40
+ config.max_position_embeddings, config.hidden_size
41
+ )
42
+
43
+ # NEW: Branch depth embeddings - encodes depth in glycan tree (0=root, 1=child, etc.)
44
+ max_branch_depth = getattr(config, "max_branch_depth", 8)
45
+ self.branch_embeddings = nn.Embedding(max_branch_depth, config.hidden_size)
46
+
47
+ # NEW: Linkage type embeddings - encodes chemistry of glycosidic bond
48
+ # 0=none, 1=1-3, 2=1-4, 3=1-6, etc.
49
+ num_linkage_types = getattr(config, "num_linkage_types", 9)
50
+ self.linkage_embeddings = nn.Embedding(num_linkage_types, config.hidden_size)
51
+
52
+ # Multi-scale convolutions for different receptive fields
53
+ kernel_size = getattr(config, "cnn_kernel_size", 3)
54
+ # Split channels evenly: 256 + 256 + 256 = 768 for hidden_size=768
55
+ channels_per_scale = config.hidden_size // 3
56
+ self.conv_layers = nn.ModuleList([
57
+ nn.Conv1d(
58
+ in_channels=config.hidden_size,
59
+ out_channels=channels_per_scale,
60
+ kernel_size=kernel_size + 2 * i, # Kernels: 3, 5, 7
61
+ padding=(kernel_size + 2 * i) // 2, # Same padding
62
+ )
63
+ for i in range(3)
64
+ ])
65
+ self.conv_activation = nn.GELU()
66
+ self.conv_proj = nn.Linear(channels_per_scale * 3, config.hidden_size) # Project concatenated back
67
+
68
+ self.LayerNorm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
69
+ self.conv_norm = nn.LayerNorm(config.hidden_size, eps=config.layer_norm_eps)
70
+ self.dropout = nn.Dropout(config.hidden_dropout_prob)
71
+ self.register_buffer(
72
+ "position_ids",
73
+ torch.arange(config.max_position_embeddings).expand((1, -1)),
74
+ )
75
+
76
+ self.hidden_size = config.hidden_size
77
+
78
+ def forward(self, input_ids, branch_depths=None, linkage_types=None):
79
+ seq_len = input_ids.shape[1]
80
+
81
+ # Step 1: Token + Position embeddings FIRST (provides spatial context to conv)
82
+ x = self.token_embeddings(input_ids) # (batch, seq, hidden)
83
+ position_ids = self.position_ids[:, :seq_len]
84
+ x = x + self.position_embeddings(position_ids)
85
+
86
+ # NEW: Add branch depth embeddings (encodes tree structure)
87
+ if branch_depths is not None:
88
+ # Clamp to valid range
89
+ branch_depths = branch_depths.clamp(0, self.branch_embeddings.num_embeddings - 1)
90
+ x = x + self.branch_embeddings(branch_depths)
91
+
92
+ # NEW: Add linkage type embeddings (encodes bond chemistry)
93
+ if linkage_types is not None:
94
+ linkage_types = linkage_types.clamp(0, self.linkage_embeddings.num_embeddings - 1)
95
+ x = x + self.linkage_embeddings(linkage_types)
96
+
97
+ x = self.LayerNorm(x)
98
+
99
+ # Step 2: Multi-scale convolution with RESIDUAL connection
100
+ # Convolution expects (batch, hidden, seq)
101
+ conv_in = x.permute(0, 2, 1)
102
+
103
+ # Apply multi-scale convolutions and concatenate
104
+ conv_outputs = []
105
+ for conv in self.conv_layers:
106
+ conv_out = self.conv_activation(conv(conv_in))
107
+ conv_outputs.append(conv_out)
108
+
109
+ # Concatenate multi-scale features and project back
110
+ conv_out = torch.cat(conv_outputs, dim=1) # (batch, hidden, seq)
111
+ conv_out = conv_out.permute(0, 2, 1) # (batch, seq, hidden)
112
+ conv_out = self.conv_proj(conv_out) # Project to correct size
113
+
114
+ # Step 3: Residual connection - conv ENRICHES rather than replaces
115
+ x = self.conv_norm(x + self.dropout(conv_out))
116
+
117
+ return x
118
+
119
+
120
+ def create_residue_level_mask(
121
+ seq_residue_ids: torch.Tensor, # (batch, N_seq)
122
+ struct_residue_ids: torch.Tensor # (batch, N_struct)
123
+ ) -> torch.Tensor:
124
+ """
125
+ Create residue-level attention mask for cross-attention.
126
+
127
+ Maps WURCS tokens to VQ-VAE structural tokens based on residue IDs.
128
+ A WURCS token with residue_id=0 can only attend to VQ-VAE tokens with residue_id=0.
129
+
130
+ Args:
131
+ seq_residue_ids: Residue IDs for sequence tokens (batch, N_seq)
132
+ struct_residue_ids: Residue IDs for structural tokens (batch, N_struct)
133
+
134
+ Returns:
135
+ Boolean mask (batch, N_seq, N_struct) where True = can attend
136
+ """
137
+ # Expand dimensions for broadcasting
138
+ # seq: (batch, N_seq, 1)
139
+ # struct: (batch, 1, N_struct)
140
+ mask = seq_residue_ids.unsqueeze(2) == struct_residue_ids.unsqueeze(1)
141
+ # Shape: (batch, N_seq, N_struct)
142
+
143
+ # Mask out structural tokens (residue_id = -1) and MS tokens (residue_id = -2)
144
+ # Only tokens with residue_id >= 0 can attend
145
+ mask &= (seq_residue_ids.unsqueeze(2) >= 0)
146
+
147
+ return mask # True = can attend, False = cannot attend
148
+
149
+
150
+ class MultimodalGlycanBERTConfig:
151
+ """Configuration for Multimodal GlycanBERT v3."""
152
+
153
+ def __init__(
154
+ self,
155
+ # Sequence modality
156
+ seq_vocab_size: int = 166,
157
+ seq_hidden_size: int = 768,
158
+ seq_num_layers: int = 12,
159
+ seq_num_heads: int = 12,
160
+ seq_max_length: int = 512,
161
+
162
+ # MS modality
163
+ ms_vocab_size: int = 242,
164
+ ms_hidden_size: int = 384,
165
+ ms_num_layers: int = 6,
166
+ ms_num_heads: int = 6,
167
+ ms_max_length: int = 150,
168
+
169
+ # 3D structure modality
170
+ struct_vocab_size: int = 1024, # VQ-VAE codebook size
171
+ struct_hidden_size: int = 512,
172
+ struct_num_layers: int = 8,
173
+ struct_num_heads: int = 8,
174
+ struct_max_length: int = 200,
175
+ use_3d: bool = True,
176
+
177
+ # Cross-attention
178
+ use_cross_attention: bool = True,
179
+ cross_attn_num_heads: int = 8,
180
+
181
+ # Fusion
182
+ fusion_hidden_size: int = 768,
183
+ fusion_num_layers: int = 2,
184
+
185
+ # Training
186
+ hidden_dropout_prob: float = 0.1,
187
+ attention_probs_dropout_prob: float = 0.1,
188
+ layer_norm_eps: float = 1e-12,
189
+ initializer_range: float = 0.02,
190
+
191
+ # Conv front-end
192
+ use_cnn_frontend: bool = True,
193
+ cnn_kernel_size: int = 3,
194
+
195
+ # Loss weights
196
+ seq_loss_weight: float = 0.60,
197
+ ms_loss_weight: float = 0.15,
198
+ struct_loss_weight: float = 0.25,
199
+ dist_loss_weight: float = 0.25,
200
+
201
+ # Token IDs
202
+ pad_token_id: int = 0,
203
+ mask_token_id: int = 1,
204
+ ):
205
+ # Sequence config
206
+ self.seq_vocab_size = seq_vocab_size
207
+ self.seq_hidden_size = seq_hidden_size
208
+ self.seq_num_layers = seq_num_layers
209
+ self.seq_num_heads = seq_num_heads
210
+ self.seq_max_length = seq_max_length
211
+
212
+ # MS config
213
+ self.ms_vocab_size = ms_vocab_size
214
+ self.ms_vocab_offset = seq_vocab_size # MS tokens start at 166
215
+ self.ms_total_vocab_size = seq_vocab_size + ms_vocab_size # 408 total
216
+ self.ms_hidden_size = ms_hidden_size
217
+ self.ms_num_layers = ms_num_layers
218
+ self.ms_num_heads = ms_num_heads
219
+ self.ms_max_length = ms_max_length
220
+
221
+ # Structure config
222
+ self.struct_vocab_size = struct_vocab_size
223
+ self.struct_hidden_size = struct_hidden_size
224
+ self.struct_num_layers = struct_num_layers
225
+ self.struct_num_heads = struct_num_heads
226
+ self.struct_max_length = struct_max_length
227
+ self.use_3d = use_3d
228
+
229
+ # Cross-attention config
230
+ self.use_cross_attention = use_cross_attention
231
+ self.cross_attn_num_heads = cross_attn_num_heads
232
+
233
+ # Fusion config
234
+ self.fusion_hidden_size = fusion_hidden_size
235
+ self.fusion_num_layers = fusion_num_layers
236
+
237
+ # Training config
238
+ self.hidden_dropout_prob = hidden_dropout_prob
239
+ self.attention_probs_dropout_prob = attention_probs_dropout_prob
240
+ self.layer_norm_eps = layer_norm_eps
241
+ self.initializer_range = initializer_range
242
+
243
+ # Conv front-end
244
+ self.use_cnn_frontend = use_cnn_frontend
245
+ self.cnn_kernel_size = cnn_kernel_size
246
+
247
+ # Loss weights
248
+ self.seq_loss_weight = seq_loss_weight
249
+ self.ms_loss_weight = ms_loss_weight
250
+ self.struct_loss_weight = struct_loss_weight
251
+ self.dist_loss_weight = dist_loss_weight
252
+
253
+ # Token IDs
254
+ self.pad_token_id = pad_token_id
255
+ self.mask_token_id = mask_token_id
256
+
257
+ def to_seq_config(self) -> GlycanBERTConfig:
258
+ """Convert to sequence-only config."""
259
+ return GlycanBERTConfig(
260
+ vocab_size=self.seq_vocab_size,
261
+ hidden_size=self.seq_hidden_size,
262
+ num_hidden_layers=self.seq_num_layers,
263
+ num_attention_heads=self.seq_num_heads,
264
+ intermediate_size=self.seq_hidden_size * 4,
265
+ hidden_dropout_prob=self.hidden_dropout_prob,
266
+ attention_probs_dropout_prob=self.attention_probs_dropout_prob,
267
+ max_position_embeddings=self.seq_max_length,
268
+ layer_norm_eps=self.layer_norm_eps,
269
+ pad_token_id=self.pad_token_id,
270
+ mask_token_id=self.mask_token_id,
271
+ initializer_range=self.initializer_range,
272
+ )
273
+
274
+ def to_ms_config(self) -> GlycanBERTConfig:
275
+ """Convert to MS-only config."""
276
+ return GlycanBERTConfig(
277
+ vocab_size=self.ms_total_vocab_size,
278
+ hidden_size=self.ms_hidden_size,
279
+ num_hidden_layers=self.ms_num_layers,
280
+ num_attention_heads=self.ms_num_heads,
281
+ intermediate_size=self.ms_hidden_size * 4,
282
+ hidden_dropout_prob=self.hidden_dropout_prob,
283
+ attention_probs_dropout_prob=self.attention_probs_dropout_prob,
284
+ max_position_embeddings=self.ms_max_length,
285
+ layer_norm_eps=self.layer_norm_eps,
286
+ pad_token_id=self.pad_token_id,
287
+ mask_token_id=self.mask_token_id,
288
+ initializer_range=self.initializer_range,
289
+ )
290
+
291
+ def to_struct_config(self) -> GlycanBERTConfig:
292
+ """Convert to structure-only config."""
293
+ return GlycanBERTConfig(
294
+ vocab_size=self.struct_vocab_size,
295
+ hidden_size=self.struct_hidden_size,
296
+ num_hidden_layers=self.struct_num_layers,
297
+ num_attention_heads=self.struct_num_heads,
298
+ intermediate_size=self.struct_hidden_size * 4,
299
+ hidden_dropout_prob=self.hidden_dropout_prob,
300
+ attention_probs_dropout_prob=self.attention_probs_dropout_prob,
301
+ max_position_embeddings=self.struct_max_length,
302
+ layer_norm_eps=self.layer_norm_eps,
303
+ pad_token_id=self.pad_token_id,
304
+ mask_token_id=self.mask_token_id,
305
+ initializer_range=self.initializer_range,
306
+ )
307
+
308
+
309
+ # =============================================================================
310
+ # Improvement #1: Monosaccharide-Level Pooling
311
+ # =============================================================================
312
+
313
+ class MonosaccharidePooling(nn.Module):
314
+ """
315
+ Pool token representations to monosaccharide level, then aggregate.
316
+
317
+ This bridges the gap between token-level BERT and monosaccharide-level CNNs/GNNs.
318
+ Uses monosaccharide_indices from the data to know where each residue starts.
319
+ """
320
+
321
+ def __init__(self, hidden_size: int, num_attention_heads: int = 8, dropout: float = 0.1):
322
+ super().__init__()
323
+ self.hidden_size = hidden_size
324
+
325
+ # Attention pooling over monosaccharide representations
326
+ self.mono_attention = nn.MultiheadAttention(
327
+ embed_dim=hidden_size,
328
+ num_heads=num_attention_heads,
329
+ dropout=dropout,
330
+ batch_first=True
331
+ )
332
+ self.mono_norm = nn.LayerNorm(hidden_size)
333
+
334
+ # Final aggregation to single glycan representation
335
+ self.glycan_query = nn.Parameter(torch.randn(1, 1, hidden_size) * 0.02)
336
+ self.glycan_attention = nn.MultiheadAttention(
337
+ embed_dim=hidden_size,
338
+ num_heads=num_attention_heads,
339
+ dropout=dropout,
340
+ batch_first=True
341
+ )
342
+ self.glycan_norm = nn.LayerNorm(hidden_size)
343
+
344
+ def forward(
345
+ self,
346
+ hidden_states: torch.Tensor, # (batch, seq_len, hidden)
347
+ residue_ids: torch.Tensor, # (batch, seq_len) - which residue each token belongs to
348
+ attention_mask: torch.Tensor = None, # (batch, seq_len)
349
+ ) -> torch.Tensor:
350
+ """
351
+ Pool tokens to monosaccharide level, then to glycan level.
352
+
353
+ Returns:
354
+ Glycan representation: (batch, hidden_size)
355
+ """
356
+ batch_size = hidden_states.size(0)
357
+ device = hidden_states.device
358
+
359
+ # Get unique residue IDs per sample (excluding -1 padding)
360
+ max_residues = 50 # Reasonable max for glycans
361
+
362
+ # Pool tokens within each residue using mean pooling
363
+ mono_reps = torch.zeros(batch_size, max_residues, self.hidden_size, device=device)
364
+ mono_mask = torch.zeros(batch_size, max_residues, dtype=torch.bool, device=device)
365
+
366
+ for b in range(batch_size):
367
+ unique_residues = torch.unique(residue_ids[b][residue_ids[b] >= 0])
368
+ for i, rid in enumerate(unique_residues):
369
+ if i >= max_residues:
370
+ break
371
+ token_mask = residue_ids[b] == rid
372
+ if attention_mask is not None:
373
+ token_mask = token_mask & (attention_mask[b] > 0)
374
+ if token_mask.sum() > 0:
375
+ mono_reps[b, i] = hidden_states[b][token_mask].mean(dim=0)
376
+ mono_mask[b, i] = True
377
+
378
+ # Apply attention over monosaccharide representations
379
+ # Convert mask for attention: True = valid, need to invert for PyTorch
380
+ key_padding_mask = ~mono_mask # True = ignore
381
+
382
+ mono_out, _ = self.mono_attention(
383
+ mono_reps, mono_reps, mono_reps,
384
+ key_padding_mask=key_padding_mask
385
+ )
386
+ mono_out = self.mono_norm(mono_reps + mono_out)
387
+
388
+ # Aggregate to single glycan representation using learned query
389
+ glycan_query = self.glycan_query.expand(batch_size, -1, -1)
390
+ glycan_out, _ = self.glycan_attention(
391
+ glycan_query, mono_out, mono_out,
392
+ key_padding_mask=key_padding_mask
393
+ )
394
+ glycan_out = self.glycan_norm(glycan_query + glycan_out)
395
+
396
+ return glycan_out.squeeze(1) # (batch, hidden)
397
+
398
+
399
+ # =============================================================================
400
+ # Improvement #2: Residue Type Embeddings
401
+ # =============================================================================
402
+
403
+ # Common monosaccharide types vocabulary
404
+ MONOSACCHARIDE_VOCAB = {
405
+ '[PAD_MONO]': 0, '[UNK_MONO]': 1,
406
+ 'Glc': 2, 'GlcNAc': 3, 'GlcA': 4, 'GlcN': 5,
407
+ 'Gal': 6, 'GalNAc': 7, 'GalA': 8, 'GalN': 9,
408
+ 'Man': 10, 'ManNAc': 11, 'ManA': 12, 'ManN': 13,
409
+ 'Fuc': 14, 'Rha': 15, 'Xyl': 16, 'Ara': 17,
410
+ 'Neu5Ac': 18, 'Neu5Gc': 19, 'Kdn': 20, 'Sia': 21,
411
+ 'GalNAcA': 22, 'GlcNAcA': 23, 'IdoA': 24, 'GulA': 25,
412
+ 'Rib': 26, 'Lyx': 27, 'All': 28, 'Alt': 29,
413
+ 'Tal': 30, 'Ido': 31, 'Qui': 32, 'Oli': 33,
414
+ 'Tyv': 34, 'Abe': 35, 'Par': 36, 'Dig': 37,
415
+ 'Col': 38, 'Dha': 39, 'Kdo': 40, 'Hep': 41,
416
+ 'NeuroGc': 42, 'Muramic': 43, 'LDManHep': 44, 'DDManHep': 45,
417
+ 'Bac': 46, 'Pse': 47, 'Leg': 48, 'Aci': 49,
418
+ '6dTal': 50, 'Fru': 51, 'Tag': 52, 'Sor': 53,
419
+ 'Psi': 54, 'Sed': 55, 'MurNAc': 56, 'MurNGc': 57,
420
+ 'Api': 58, 'Erwiniose': 59, 'Yer': 60, 'Thre': 61,
421
+ # Add more as needed, up to ~70
422
+ }
423
+
424
+
425
+ class ResidueTypeEmbeddings(nn.Module):
426
+ """
427
+ Learnable embeddings for monosaccharide types.
428
+
429
+ Instead of the model having to learn that 'a1221m' = Fucose from character patterns,
430
+ we explicitly add a Fucose embedding to all tokens belonging to that residue.
431
+ """
432
+
433
+ def __init__(self, hidden_size: int, num_mono_types: int = 70):
434
+ super().__init__()
435
+ self.mono_embeddings = nn.Embedding(num_mono_types, hidden_size)
436
+ self.mono_vocab = MONOSACCHARIDE_VOCAB
437
+ self.hidden_size = hidden_size
438
+
439
+ def forward(
440
+ self,
441
+ token_embeddings: torch.Tensor, # (batch, seq_len, hidden)
442
+ residue_ids: torch.Tensor, # (batch, seq_len)
443
+ mono_type_ids: torch.Tensor = None, # (batch, max_residues) - monosaccharide type per residue
444
+ ) -> torch.Tensor:
445
+ """
446
+ Add residue type embeddings to token embeddings.
447
+
448
+ Args:
449
+ token_embeddings: Base token embeddings
450
+ residue_ids: Which residue each token belongs to (-1 for special tokens)
451
+ mono_type_ids: Monosaccharide type ID for each residue position
452
+
453
+ Returns:
454
+ Enhanced embeddings with residue type information
455
+ """
456
+ if mono_type_ids is None:
457
+ return token_embeddings
458
+
459
+ batch_size, seq_len, _ = token_embeddings.shape
460
+ enhanced = token_embeddings.clone()
461
+
462
+ # Add mono type embedding to each token based on its residue
463
+ for b in range(batch_size):
464
+ for pos in range(seq_len):
465
+ rid = residue_ids[b, pos].item()
466
+ if rid >= 0 and rid < mono_type_ids.size(1):
467
+ mono_id = mono_type_ids[b, rid]
468
+ enhanced[b, pos] = enhanced[b, pos] + self.mono_embeddings(mono_id)
469
+
470
+ return enhanced
471
+
472
+ @staticmethod
473
+ def get_mono_type_id(mono_name: str) -> int:
474
+ """Convert monosaccharide name to type ID."""
475
+ return MONOSACCHARIDE_VOCAB.get(mono_name, MONOSACCHARIDE_VOCAB['[UNK_MONO]'])
476
+
477
+
478
+ # =============================================================================
479
+ # Improvement #4: Relative Position Encoding for Glycan Trees
480
+ # =============================================================================
481
+
482
+ class RelativePositionBias(nn.Module):
483
+ """
484
+ Compute relative position bias for attention based on residue IDs.
485
+
486
+ Tokens in the same residue get distance 0.
487
+ Tokens in adjacent residues get distance ±1.
488
+ This helps the model understand glycan tree structure.
489
+ """
490
+
491
+ def __init__(self, num_heads: int, max_distance: int = 10):
492
+ super().__init__()
493
+ self.num_heads = num_heads
494
+ self.max_distance = max_distance
495
+
496
+ # Learnable bias for each relative distance (-max to +max)
497
+ num_distances = 2 * max_distance + 1
498
+ self.relative_bias = nn.Embedding(num_distances, num_heads)
499
+
500
+ def forward(self, residue_ids: torch.Tensor) -> torch.Tensor:
501
+ """
502
+ Compute relative position bias.
503
+
504
+ Args:
505
+ residue_ids: (batch, seq_len)
506
+
507
+ Returns:
508
+ Bias to add to attention scores: (batch, num_heads, seq_len, seq_len)
509
+ """
510
+ # Compute pairwise residue distances
511
+ # (batch, seq_len, 1) - (batch, 1, seq_len) = (batch, seq_len, seq_len)
512
+ distance = residue_ids.unsqueeze(2) - residue_ids.unsqueeze(1)
513
+
514
+ # Clamp to max distance range and shift to 0-indexed
515
+ distance_clamped = distance.clamp(-self.max_distance, self.max_distance)
516
+ distance_idx = distance_clamped + self.max_distance # Now 0 to 2*max_distance
517
+
518
+ # Look up bias: (batch, seq_len, seq_len, num_heads)
519
+ bias = self.relative_bias(distance_idx)
520
+
521
+ # Transpose to (batch, num_heads, seq_len, seq_len)
522
+ bias = bias.permute(0, 3, 1, 2)
523
+
524
+ return bias
525
+
526
+
527
+ class CrossAttentionLayer(nn.Module):
528
+ """
529
+ Cross-attention layer for sequence-structure alignment.
530
+
531
+ Allows sequence tokens to attend to structural atoms using attention masks.
532
+ """
533
+
534
+ def __init__(self, config: MultimodalGlycanBERTConfig):
535
+ super().__init__()
536
+ self.num_heads = config.cross_attn_num_heads
537
+ self.hidden_size = config.seq_hidden_size
538
+ self.head_dim = self.hidden_size // self.num_heads
539
+
540
+ assert self.hidden_size % self.num_heads == 0, "hidden_size must be divisible by num_heads"
541
+
542
+ # Query from sequence, Key/Value from structure (VQ-VAE tokens)
543
+ self.query = nn.Linear(config.seq_hidden_size, self.hidden_size)
544
+ self.key = nn.Linear(config.struct_hidden_size, self.hidden_size)
545
+ self.value = nn.Linear(config.struct_hidden_size, self.hidden_size)
546
+
547
+ self.output = nn.Linear(self.hidden_size, config.seq_hidden_size)
548
+ self.dropout = nn.Dropout(config.attention_probs_dropout_prob)
549
+ self.layer_norm = nn.LayerNorm(config.seq_hidden_size, eps=config.layer_norm_eps)
550
+
551
+ def forward(
552
+ self,
553
+ seq_hidden: torch.Tensor, # (batch, seq_len, seq_hidden)
554
+ struct_hidden: torch.Tensor, # (batch, struct_len, struct_hidden)
555
+ attention_mask: Optional[torch.Tensor] = None, # (batch, seq_len, struct_len)
556
+ ) -> torch.Tensor:
557
+ """
558
+ Apply cross-attention from sequence to structure.
559
+
560
+ Args:
561
+ seq_hidden: Sequence hidden states
562
+ struct_hidden: Structure hidden states
563
+ attention_mask: Boolean mask (True = can attend, False = cannot attend)
564
+
565
+ Returns:
566
+ Updated sequence hidden states
567
+ """
568
+ batch_size, seq_len, _ = seq_hidden.shape
569
+ struct_len = struct_hidden.shape[1]
570
+
571
+ # Project to Q, K, V
572
+ Q = self.query(seq_hidden) # (batch, seq_len, hidden)
573
+ K = self.key(struct_hidden) # (batch, struct_len, hidden)
574
+ V = self.value(struct_hidden) # (batch, struct_len, hidden)
575
+
576
+ # Reshape for multi-head attention
577
+ Q = Q.view(batch_size, seq_len, self.num_heads, self.head_dim).transpose(1, 2) # (batch, heads, seq_len, head_dim)
578
+ K = K.view(batch_size, struct_len, self.num_heads, self.head_dim).transpose(1, 2) # (batch, heads, struct_len, head_dim)
579
+ V = V.view(batch_size, struct_len, self.num_heads, self.head_dim).transpose(1, 2) # (batch, heads, struct_len, head_dim)
580
+
581
+ # Compute attention scores
582
+ scores = torch.matmul(Q, K.transpose(-2, -1)) / math.sqrt(self.head_dim) # (batch, heads, seq_len, struct_len)
583
+
584
+ # Apply attention mask
585
+ if attention_mask is not None:
586
+ # attention_mask: (batch, seq_len, struct_len) -> (batch, 1, seq_len, struct_len)
587
+ attention_mask = attention_mask.unsqueeze(1)
588
+ # Convert boolean mask to float: True -> 0.0, False -> -10000.0
589
+ attention_mask = (~attention_mask).float() * -10000.0
590
+ scores = scores + attention_mask
591
+
592
+ # Softmax and dropout
593
+ attn_weights = torch.softmax(scores, dim=-1) # (batch, heads, seq_len, struct_len)
594
+ attn_weights = self.dropout(attn_weights)
595
+
596
+ # Apply attention to values
597
+ context = torch.matmul(attn_weights, V) # (batch, heads, seq_len, head_dim)
598
+
599
+ # Reshape back
600
+ context = context.transpose(1, 2).contiguous().view(batch_size, seq_len, self.hidden_size)
601
+
602
+ # Output projection
603
+ output = self.output(context)
604
+ output = self.dropout(output)
605
+
606
+ # Residual connection + layer norm
607
+ output = self.layer_norm(seq_hidden + output)
608
+
609
+ return output
610
+
611
+
612
+ class MultimodalGlycanBERT(nn.Module):
613
+ """
614
+ Multimodal BERT for glycan representation learning (v3).
615
+
616
+ Architecture:
617
+ 1. Separate encoders for each modality (sequence, MS, 3D structure)
618
+ 2. Cross-attention for sequence-structure alignment
619
+ 3. Modality-specific MLM heads
620
+ 4. Fusion layer for combined representation
621
+ """
622
+
623
+ def __init__(self, config: MultimodalGlycanBERTConfig):
624
+ super().__init__()
625
+ self.config = config
626
+
627
+ # ===== Sequence Encoder =====
628
+ seq_config = config.to_seq_config()
629
+ seq_config.cnn_kernel_size = config.cnn_kernel_size
630
+
631
+ if config.use_cnn_frontend:
632
+ print(f"✅ Enabled Convolutional Front-End (Kernel={config.cnn_kernel_size})")
633
+ self.seq_embeddings = ConvGlycanBERTEmbeddings(seq_config)
634
+ else:
635
+ self.seq_embeddings = GlycanBERTEmbeddings(seq_config)
636
+ self.seq_layers = nn.ModuleList([GlycanBERTLayer(seq_config) for _ in range(seq_config.num_hidden_layers)])
637
+ self.seq_mlm_head = nn.Linear(seq_config.hidden_size, seq_config.vocab_size)
638
+
639
+ # ===== MS Encoder =====
640
+ ms_config = config.to_ms_config()
641
+ self.ms_embeddings = GlycanBERTEmbeddings(ms_config)
642
+ self.ms_layers = nn.ModuleList([GlycanBERTLayer(ms_config) for _ in range(ms_config.num_hidden_layers)])
643
+ self.ms_mlm_head = nn.Linear(ms_config.hidden_size, ms_config.vocab_size)
644
+
645
+ # ===== Structure Encoder (VQ-VAE tokens) =====
646
+ if config.use_3d:
647
+ struct_config = config.to_struct_config()
648
+ self.struct_embeddings = GlycanBERTEmbeddings(struct_config)
649
+ self.struct_layers = nn.ModuleList([GlycanBERTLayer(struct_config) for _ in range(struct_config.num_hidden_layers)])
650
+ self.struct_mlm_head = nn.Linear(struct_config.hidden_size, struct_config.vocab_size)
651
+
652
+ # Cross-attention layer (sequence → VQ-VAE structural tokens)
653
+ if config.use_cross_attention:
654
+ self.cross_attention = CrossAttentionLayer(config)
655
+
656
+ # ===== Projection layers (align hidden sizes) =====
657
+ if config.ms_hidden_size != config.seq_hidden_size:
658
+ self.ms_projection = nn.Linear(config.ms_hidden_size, config.seq_hidden_size)
659
+ else:
660
+ self.ms_projection = nn.Identity()
661
+
662
+ if config.use_3d and config.struct_hidden_size != config.seq_hidden_size:
663
+ self.struct_projection = nn.Linear(config.struct_hidden_size, config.seq_hidden_size)
664
+ else:
665
+ self.struct_projection = nn.Identity()
666
+
667
+ # ===== Fusion Layer =====
668
+ # Concatenate seq + ms + struct
669
+ fusion_input_size = config.seq_hidden_size * (3 if config.use_3d else 2)
670
+ self.fusion_layer = nn.Sequential(
671
+ nn.Linear(fusion_input_size, config.fusion_hidden_size),
672
+ nn.LayerNorm(config.fusion_hidden_size, eps=config.layer_norm_eps),
673
+ nn.GELU(),
674
+ nn.Dropout(config.hidden_dropout_prob),
675
+ nn.Linear(config.fusion_hidden_size, config.fusion_hidden_size),
676
+ )
677
+
678
+ # ===== Distance Prediction Head (Topology) =====
679
+ # OPTIMIZED: Project down to 128 dim first to save GPU memory
680
+ # (Batch, 256, 256, 768) -> (Batch, 256, 256, 128) reduces memory by 6x
681
+ self.dist_proj = nn.Linear(config.seq_hidden_size, 128)
682
+ self.distance_head = nn.Sequential(
683
+ nn.Linear(128, 64),
684
+ nn.ReLU(),
685
+ nn.Linear(64, 1)
686
+ )
687
+
688
+ # Initialize weights
689
+ self.apply(self._init_weights)
690
+
691
+ def _init_weights(self, module):
692
+ """Initialize weights."""
693
+ if isinstance(module, nn.Linear):
694
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
695
+ if module.bias is not None:
696
+ module.bias.data.zero_()
697
+ elif isinstance(module, nn.Embedding):
698
+ module.weight.data.normal_(mean=0.0, std=self.config.initializer_range)
699
+ if module.padding_idx is not None:
700
+ module.weight.data[module.padding_idx].zero_()
701
+ elif isinstance(module, nn.LayerNorm):
702
+ module.bias.data.zero_()
703
+ module.weight.data.fill_(1.0)
704
+
705
+ def forward(
706
+ self,
707
+ seq_token_ids: torch.Tensor,
708
+ seq_attention_mask: torch.Tensor,
709
+ seq_residue_ids: torch.Tensor,
710
+ seq_branch_depths: Optional[torch.Tensor] = None, # NEW: Branch depths
711
+ seq_linkage_types: Optional[torch.Tensor] = None, # NEW: Linkage types
712
+ ms_token_ids: torch.Tensor = None,
713
+ ms_attention_mask: torch.Tensor = None,
714
+ has_ms: torch.Tensor = None,
715
+ struct_token_ids: Optional[torch.Tensor] = None,
716
+ struct_attention_mask: Optional[torch.Tensor] = None,
717
+ struct_residue_ids: Optional[torch.Tensor] = None,
718
+ has_3d: Optional[torch.Tensor] = None,
719
+ seq_labels: Optional[torch.Tensor] = None,
720
+ ms_labels: Optional[torch.Tensor] = None,
721
+ struct_labels: Optional[torch.Tensor] = None,
722
+ dist_labels: Optional[torch.Tensor] = None, # NEW: Topology distance labels
723
+ return_dict: bool = True,
724
+ ) -> Dict[str, torch.Tensor]:
725
+ """
726
+ Forward pass for multimodal BERT v3.
727
+
728
+ Args:
729
+ seq_token_ids: (batch_size, seq_len) - Sequence token IDs
730
+ seq_attention_mask: (batch_size, seq_len) - Sequence attention mask
731
+ seq_residue_ids: (batch_size, seq_len) - Sequence token residue IDs
732
+ ms_token_ids: (batch_size, ms_len) - MS token IDs
733
+ ms_attention_mask: (batch_size, ms_len) - MS attention mask
734
+ has_ms: (batch_size,) - Boolean mask for samples with MS data
735
+ struct_token_ids: (batch_size, struct_len) - Structure VQ-VAE token IDs (optional)
736
+ struct_attention_mask: (batch_size, struct_len) - Structure attention mask (optional)
737
+ struct_residue_ids: (batch_size, struct_len) - Structure token residue IDs (optional)
738
+ has_3d: (batch_size,) - Boolean mask for samples with 3D data (optional)
739
+ seq_labels: (batch_size, seq_len) - Masked sequence labels (optional)
740
+ ms_labels: (batch_size, ms_len) - Masked MS labels (optional)
741
+ struct_labels: (batch_size, struct_len) - Masked structure labels (optional)
742
+ return_dict: Whether to return dict or tuple
743
+
744
+ Returns:
745
+ Dictionary containing logits, hidden states, losses, etc.
746
+ """
747
+ batch_size = seq_token_ids.shape[0]
748
+ device = seq_token_ids.device
749
+
750
+ # ===== Sequence Encoder =====
751
+ # Pass branch_depths and linkage_types to embeddings for tree-aware encoding
752
+ seq_hidden = self.seq_embeddings(seq_token_ids, seq_branch_depths, seq_linkage_types)
753
+ for layer in self.seq_layers:
754
+ seq_hidden = layer(seq_hidden, seq_attention_mask)
755
+
756
+ seq_pooled = seq_hidden[:, 0, :] # [CLS] token
757
+ seq_logits = self.seq_mlm_head(seq_hidden)
758
+
759
+ # ===== Distance Predictions (Topology) =====
760
+ # Compute pairwise distance predictions
761
+ # MEMORY OPTIMIZATION: Project to 128-dim first
762
+ seq_hidden_small = self.dist_proj(seq_hidden) # (batch, seq_len, 128)
763
+
764
+ # Expand for pairwise: (batch, seq_len, 1, 128) - (batch, 1, seq_len, 128)
765
+ h_i = seq_hidden_small.unsqueeze(2)
766
+ h_j = seq_hidden_small.unsqueeze(1)
767
+ h_diff = torch.abs(h_i - h_j) # (batch, seq_len, seq_len, 128) - Much smaller!
768
+ dist_predictions = self.distance_head(h_diff) # (batch, seq_len, seq_len, 1)
769
+
770
+ # ===== MS Encoder =====
771
+ ms_hidden = None
772
+ ms_pooled = None
773
+ ms_logits = None
774
+
775
+ if ms_token_ids is not None:
776
+ ms_hidden = self.ms_embeddings(ms_token_ids)
777
+ for layer in self.ms_layers:
778
+ ms_hidden = layer(ms_hidden, ms_attention_mask)
779
+
780
+ ms_pooled = ms_hidden[:, 0, :] # [CLS] token
781
+ ms_logits = self.ms_mlm_head(ms_hidden)
782
+
783
+ # Zero out MS representations for samples without MS data
784
+ if has_ms is not None:
785
+ has_ms_expanded = has_ms.unsqueeze(1).float() # (batch, 1)
786
+ ms_pooled = ms_pooled * has_ms_expanded
787
+
788
+ # ===== Structure Encoder =====
789
+ struct_pooled = None
790
+ struct_logits = None
791
+ struct_hidden = None
792
+
793
+ if self.config.use_3d and struct_token_ids is not None:
794
+ struct_hidden = self.struct_embeddings(struct_token_ids)
795
+ for layer in self.struct_layers:
796
+ struct_hidden = layer(struct_hidden, struct_attention_mask)
797
+
798
+ struct_pooled = struct_hidden[:, 0, :] # [CLS] token
799
+ struct_logits = self.struct_mlm_head(struct_hidden)
800
+
801
+ # Zero out structure representations for samples without 3D data
802
+ if has_3d is not None:
803
+ has_3d_expanded = has_3d.unsqueeze(1).float() # (batch, 1)
804
+ struct_pooled = struct_pooled * has_3d_expanded
805
+
806
+ # ===== Cross-Attention (Sequence → VQ-VAE Structural Tokens) =====
807
+ # Use residue-level alignment between WURCS tokens and VQ-VAE tokens
808
+ if self.config.use_cross_attention and struct_residue_ids is not None:
809
+ # Create residue-level mask
810
+ # WURCS token with residue_id=0 → VQ-VAE tokens with residue_id=0
811
+ residue_mask = create_residue_level_mask(
812
+ seq_residue_ids=seq_residue_ids,
813
+ struct_residue_ids=struct_residue_ids,
814
+ ) # (batch, N_seq, N_struct)
815
+
816
+ # Apply cross-attention: sequence tokens attend to VQ-VAE tokens
817
+ seq_hidden = self.cross_attention(
818
+ seq_hidden=seq_hidden,
819
+ struct_hidden=struct_hidden, # VQ-VAE token features
820
+ attention_mask=residue_mask, # Residue-based mask
821
+ )
822
+
823
+ # Update seq_pooled after cross-attention
824
+ seq_pooled = seq_hidden[:, 0, :]
825
+
826
+ # ===== Fusion =====
827
+ # Project to common hidden size
828
+ ms_pooled_projected = self.ms_projection(ms_pooled)
829
+
830
+ if self.config.use_3d and struct_pooled is not None:
831
+ struct_pooled_projected = self.struct_projection(struct_pooled)
832
+ combined = torch.cat([seq_pooled, ms_pooled_projected, struct_pooled_projected], dim=-1)
833
+ else:
834
+ combined = torch.cat([seq_pooled, ms_pooled_projected], dim=-1)
835
+
836
+ fused_repr = self.fusion_layer(combined)
837
+
838
+ # ===== Compute Losses =====
839
+ total_loss = None
840
+ seq_loss = None
841
+ ms_loss = None
842
+ struct_loss = None
843
+ dist_loss = None # NEW: Topology distance loss
844
+
845
+ if seq_labels is not None:
846
+ loss_fct = nn.CrossEntropyLoss(ignore_index=-100)
847
+ seq_loss = loss_fct(
848
+ seq_logits.view(-1, self.config.seq_vocab_size),
849
+ seq_labels.view(-1)
850
+ )
851
+
852
+ if ms_labels is not None:
853
+ ms_labels_masked = ms_labels.clone()
854
+ ms_labels_masked[~has_ms] = -100
855
+ # Only compute loss if there are valid labels (not all -100)
856
+ if (ms_labels_masked != -100).any():
857
+ loss_fct = nn.CrossEntropyLoss(ignore_index=-100)
858
+ ms_loss = loss_fct(
859
+ ms_logits.view(-1, self.config.ms_total_vocab_size),
860
+ ms_labels_masked.view(-1)
861
+ )
862
+ else:
863
+ ms_loss = torch.tensor(0.0, device=seq_token_ids.device)
864
+
865
+ if self.config.use_3d and struct_labels is not None and struct_logits is not None:
866
+ struct_labels_masked = struct_labels.clone()
867
+ if has_3d is not None:
868
+ struct_labels_masked[~has_3d] = -100
869
+ # Only compute loss if there are valid labels (not all -100)
870
+ if (struct_labels_masked != -100).any():
871
+ loss_fct = nn.CrossEntropyLoss(ignore_index=-100)
872
+ struct_loss = loss_fct(
873
+ struct_logits.view(-1, self.config.struct_vocab_size),
874
+ struct_labels_masked.view(-1)
875
+ )
876
+ else:
877
+ struct_loss = torch.tensor(0.0, device=seq_token_ids.device)
878
+
879
+ # ===== Distance Loss (Topology) =====
880
+ if dist_labels is not None:
881
+ # dist_predictions: (Batch, Seq, Seq, 1) -> (Batch, Seq, Seq)
882
+ preds = dist_predictions.squeeze(-1)
883
+
884
+ # Create mask for valid distance pairs (label != -1)
885
+ # Also respect attention mask to avoid padding
886
+ valid_mask = (dist_labels != -1) & (seq_attention_mask.unsqueeze(1) * seq_attention_mask.unsqueeze(2) == 1)
887
+
888
+ # DEBUG: Print once
889
+ if not hasattr(self, '_dist_debug_printed'):
890
+ print(f"[DIST DEBUG] dist_labels shape: {dist_labels.shape}, valid_mask.sum: {valid_mask.sum().item()}")
891
+ self._dist_debug_printed = True
892
+
893
+ if valid_mask.sum() > 0:
894
+ # MSE loss on valid positions only
895
+ loss_fct = nn.MSELoss()
896
+ dist_loss = loss_fct(preds[valid_mask], dist_labels[valid_mask].float())
897
+ else:
898
+ dist_loss = torch.tensor(0.0, device=seq_token_ids.device)
899
+ else:
900
+ # DEBUG: dist_labels is None
901
+ if not hasattr(self, '_dist_none_printed'):
902
+ print("[DIST DEBUG] dist_labels is None!")
903
+ self._dist_none_printed = True
904
+
905
+ # Weighted combination
906
+ losses = []
907
+ if seq_loss is not None:
908
+ losses.append(self.config.seq_loss_weight * seq_loss)
909
+ if ms_loss is not None:
910
+ losses.append(self.config.ms_loss_weight * ms_loss)
911
+ if struct_loss is not None:
912
+ losses.append(self.config.struct_loss_weight * struct_loss)
913
+ if dist_loss is not None:
914
+ losses.append(self.config.dist_loss_weight * dist_loss)
915
+
916
+ if losses:
917
+ total_loss = sum(losses)
918
+
919
+ if return_dict:
920
+ return {
921
+ 'loss': total_loss,
922
+ 'seq_loss': seq_loss,
923
+ 'ms_loss': ms_loss,
924
+ 'struct_loss': struct_loss,
925
+ 'dist_loss': dist_loss, # NEW: Topology loss
926
+ 'seq_logits': seq_logits,
927
+ 'ms_logits': ms_logits,
928
+ 'struct_logits': struct_logits,
929
+ 'dist_predictions': dist_predictions, # NEW: Distance predictions
930
+ 'seq_hidden': seq_hidden,
931
+ 'ms_hidden': ms_hidden,
932
+ 'struct_hidden': struct_hidden,
933
+ 'seq_pooled': seq_pooled,
934
+ 'ms_pooled': ms_pooled,
935
+ 'struct_pooled': struct_pooled,
936
+ 'fused_repr': fused_repr,
937
+ }
938
+ else:
939
+ return (total_loss, seq_logits, ms_logits, struct_logits, fused_repr)
940
+
941
+ def get_multimodal_representation(
942
+ self,
943
+ seq_token_ids: torch.Tensor,
944
+ seq_attention_mask: torch.Tensor,
945
+ seq_residue_ids: torch.Tensor,
946
+ ms_token_ids: torch.Tensor,
947
+ ms_attention_mask: torch.Tensor,
948
+ has_ms: torch.Tensor,
949
+ struct_token_ids: Optional[torch.Tensor] = None,
950
+ struct_attention_mask: Optional[torch.Tensor] = None,
951
+ struct_residue_ids: Optional[torch.Tensor] = None,
952
+ has_3d: Optional[torch.Tensor] = None,
953
+ ) -> torch.Tensor:
954
+ """Get fused multimodal representation (for inference)."""
955
+ outputs = self.forward(
956
+ seq_token_ids=seq_token_ids,
957
+ seq_attention_mask=seq_attention_mask,
958
+ seq_residue_ids=seq_residue_ids,
959
+ ms_token_ids=ms_token_ids,
960
+ ms_attention_mask=ms_attention_mask,
961
+ has_ms=has_ms,
962
+ struct_token_ids=struct_token_ids,
963
+ struct_attention_mask=struct_attention_mask,
964
+ struct_residue_ids=struct_residue_ids,
965
+ has_3d=has_3d,
966
+ return_dict=True,
967
+ )
968
+ return outputs['fused_repr']
969
+
970
+
971
+ if __name__ == "__main__":
972
+ # Test the model
973
+ print("="*80)
974
+ print("Testing Multimodal GlycanBERT v3")
975
+ print("="*80)
976
+
977
+ # Create config
978
+ config = MultimodalGlycanBERTConfig(
979
+ seq_vocab_size=166,
980
+ seq_hidden_size=768,
981
+ seq_num_layers=12,
982
+ seq_num_heads=12,
983
+ ms_vocab_size=242,
984
+ ms_hidden_size=384,
985
+ ms_num_layers=6,
986
+ ms_num_heads=6,
987
+ struct_vocab_size=1024,
988
+ struct_hidden_size=512,
989
+ struct_num_layers=8,
990
+ struct_num_heads=8,
991
+ use_3d=True,
992
+ use_cross_attention=True,
993
+ seq_loss_weight=0.60,
994
+ ms_loss_weight=0.15,
995
+ struct_loss_weight=0.25,
996
+ )
997
+
998
+ print(f"\nConfig:")
999
+ print(f" Sequence vocab: {config.seq_vocab_size}")
1000
+ print(f" MS vocab: {config.ms_vocab_size}")
1001
+ print(f" Structure vocab: {config.struct_vocab_size}")
1002
+ print(f" Loss weights: seq={config.seq_loss_weight}, ms={config.ms_loss_weight}, struct={config.struct_loss_weight}")
1003
+
1004
+ # Create model
1005
+ model = MultimodalGlycanBERT(config)
1006
+
1007
+ # Count parameters
1008
+ total_params = sum(p.numel() for p in model.parameters())
1009
+ trainable_params = sum(p.numel() for p in model.parameters() if p.requires_grad)
1010
+
1011
+ print(f"\nModel Parameters:")
1012
+ print(f" Total: {total_params:,}")
1013
+ print(f" Trainable: {trainable_params:,}")
1014
+
1015
+ # Test forward pass
1016
+ print(f"\n{'='*80}")
1017
+ print("Testing Forward Pass (with Conv front-end)")
1018
+ print("="*80)
1019
+
1020
+ batch_size = 4
1021
+ seq_len = 128
1022
+ ms_len = 50
1023
+ struct_len = 40
1024
+
1025
+ # Create dummy inputs
1026
+ seq_token_ids = torch.randint(0, config.seq_vocab_size, (batch_size, seq_len))
1027
+ seq_attention_mask = torch.ones(batch_size, seq_len)
1028
+ # Approximate: ~5 tokens per residue
1029
+ seq_residue_ids = torch.div(
1030
+ torch.arange(seq_len), 5, rounding_mode="floor"
1031
+ ).unsqueeze(0).expand(batch_size, -1)
1032
+
1033
+ ms_token_ids = torch.randint(config.ms_vocab_offset, config.ms_total_vocab_size, (batch_size, ms_len))
1034
+ ms_attention_mask = torch.ones(batch_size, ms_len)
1035
+ struct_token_ids = torch.randint(0, config.struct_vocab_size, (batch_size, struct_len))
1036
+ struct_attention_mask = torch.ones(batch_size, struct_len)
1037
+ # Approximate: 4 tokens per residue for VQ-VAE tokens
1038
+ struct_residue_ids = torch.div(
1039
+ torch.arange(struct_len), 4, rounding_mode="floor"
1040
+ ).unsqueeze(0).expand(batch_size, -1)
1041
+
1042
+ has_ms = torch.tensor([True, True, False, True])
1043
+ has_3d = torch.tensor([True, False, True, True])
1044
+
1045
+ # Create labels for MLM
1046
+ seq_labels = seq_token_ids.clone()
1047
+ seq_labels[seq_labels != config.mask_token_id] = -100
1048
+ ms_labels = ms_token_ids.clone()
1049
+ ms_labels[ms_labels != config.mask_token_id] = -100
1050
+ struct_labels = struct_token_ids.clone()
1051
+ struct_labels[struct_labels != config.mask_token_id] = -100
1052
+
1053
+ # Forward pass
1054
+ outputs = model(
1055
+ seq_token_ids=seq_token_ids,
1056
+ seq_attention_mask=seq_attention_mask,
1057
+ seq_residue_ids=seq_residue_ids,
1058
+ ms_token_ids=ms_token_ids,
1059
+ ms_attention_mask=ms_attention_mask,
1060
+ has_ms=has_ms,
1061
+ struct_token_ids=struct_token_ids,
1062
+ struct_attention_mask=struct_attention_mask,
1063
+ struct_residue_ids=struct_residue_ids,
1064
+ has_3d=has_3d,
1065
+ seq_labels=seq_labels,
1066
+ ms_labels=ms_labels,
1067
+ struct_labels=struct_labels,
1068
+ )
1069
+
1070
+ print(f"\nOutput shapes:")
1071
+ print(f" seq_logits: {outputs['seq_logits'].shape}")
1072
+ print(f" ms_logits: {outputs['ms_logits'].shape}")
1073
+ print(f" struct_logits: {outputs['struct_logits'].shape}")
1074
+ print(f" fused_repr: {outputs['fused_repr'].shape}")
1075
+
1076
+ print(f"\nLosses:")
1077
+ print(f" Total loss: {outputs['loss'].item():.4f}")
1078
+ print(f" Sequence loss: {outputs['seq_loss'].item():.4f}")
1079
+ print(f" MS loss: {outputs['ms_loss'].item():.4f}")
1080
+ print(f" Structure loss: {outputs['struct_loss'].item():.4f}")
1081
+
1082
+ print(f"\n{'='*80}")
1083
+ print("Model Test Complete!")
1084
+ print("="*80)
releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/cluster/nature_submission/code/training/ipa_bpe_distillation.py ADDED
@@ -0,0 +1,377 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ IPA Self-Distillation for BPE Tokenized Glycan Sequences
4
+ Iterative Pseudo-Annotation (IPA) to resolve ambiguous BPE tokens.
5
+ """
6
+
7
+ import sys
8
+ import torch
9
+ import torch.nn.functional as F
10
+ import numpy as np
11
+ from pathlib import Path
12
+ from typing import List, Dict, Set, Tuple
13
+ import logging
14
+ import pickle
15
+ import json
16
+ import argparse
17
+ from copy import deepcopy
18
+ from tqdm import tqdm
19
+
20
+ # Add parent path for model imports
21
+ sys.path.insert(0, str(Path(__file__).parent.parent))
22
+
23
+ from model.multimodal_glycan_bert_v3 import MultimodalGlycanBERT, MultimodalGlycanBERTConfig
24
+
25
+ logger = logging.getLogger(__name__)
26
+
27
+
28
+ class BPEIPADistiller:
29
+ """IPA Self-Distillation for BPE tokenized sequences."""
30
+
31
+ def __init__(
32
+ self,
33
+ checkpoint_path: str,
34
+ vocab_path: str,
35
+ ambiguity_path: str,
36
+ device: str = "cuda",
37
+ threshold: float = 0.8,
38
+ batch_size: int = 32,
39
+ ):
40
+ self.checkpoint_path = Path(checkpoint_path)
41
+ self.vocab_path = Path(vocab_path)
42
+ self.device = torch.device(device)
43
+ self.threshold = threshold
44
+ self.batch_size = batch_size
45
+
46
+ # Load vocabulary
47
+ logger.info(f"Loading BPE vocabulary from {vocab_path}")
48
+ with open(vocab_path, 'r') as f:
49
+ vocab = json.load(f)
50
+
51
+ self.vocab = vocab
52
+ self.token_to_id = vocab.get('token_to_id', vocab)
53
+ self.id_to_token = {v: k for k, v in self.token_to_id.items()}
54
+ self.vocab_size = len(self.token_to_id)
55
+
56
+ # Special tokens
57
+ self.pad_id = self.token_to_id.get('[PAD]', 0)
58
+ self.mask_id = self.token_to_id.get('[MASK]', 4)
59
+ self.start_id = self.token_to_id.get('[START]', 2)
60
+ self.end_id = self.token_to_id.get('[END]', 3)
61
+
62
+ logger.info(f"Vocabulary size: {self.vocab_size}")
63
+ logger.info(f"MASK token ID: {self.mask_id}")
64
+
65
+ # Load ambiguous token IDs
66
+ logger.info(f"Loading ambiguity data from {ambiguity_path}")
67
+ with open(ambiguity_path, 'r') as f:
68
+ ambig_data = json.load(f)
69
+
70
+ self.ambiguous_token_ids = set(ambig_data.get('ambiguous_ids', []))
71
+ self.ambiguous_tokens = ambig_data.get('ambiguous_tokens', {})
72
+
73
+ logger.info(f"Ambiguous BPE tokens: {len(self.ambiguous_token_ids)}")
74
+
75
+ # Load model
76
+ logger.info(f"Loading model from {checkpoint_path}")
77
+ self.model = self._load_model()
78
+ self.model.to(self.device)
79
+ self.model.eval()
80
+
81
+ logger.info(f"BPE IPA Distiller initialized (threshold={threshold})")
82
+
83
+ def _load_model(self) -> MultimodalGlycanBERT:
84
+ """Load model from checkpoint."""
85
+ checkpoint = torch.load(self.checkpoint_path, map_location=self.device)
86
+
87
+ if 'config' in checkpoint:
88
+ cfg = checkpoint['config']['model']
89
+ else:
90
+ cfg = {
91
+ 'sequence': {'vocab_size': 2200, 'hidden_size': 768, 'num_hidden_layers': 12,
92
+ 'num_attention_heads': 12, 'max_length': 256},
93
+ 'mass_spectrometry': {'vocab_size': 242, 'hidden_size': 384,
94
+ 'num_hidden_layers': 6, 'num_attention_heads': 6, 'max_length': 150},
95
+ 'structure_3d': {'vocab_size': 1024, 'hidden_size': 512,
96
+ 'num_hidden_layers': 8, 'num_attention_heads': 8, 'max_length': 200},
97
+ 'fusion': {'fusion_hidden_size': 768, 'fusion_num_layers': 2},
98
+ }
99
+
100
+ config = MultimodalGlycanBERTConfig(
101
+ seq_vocab_size=cfg['sequence']['vocab_size'],
102
+ seq_hidden_size=cfg['sequence']['hidden_size'],
103
+ seq_num_layers=cfg['sequence']['num_hidden_layers'],
104
+ seq_num_heads=cfg['sequence']['num_attention_heads'],
105
+ seq_max_length=cfg['sequence']['max_length'],
106
+ ms_vocab_size=cfg['mass_spectrometry']['vocab_size'],
107
+ ms_hidden_size=cfg['mass_spectrometry']['hidden_size'],
108
+ ms_num_layers=cfg['mass_spectrometry']['num_hidden_layers'],
109
+ ms_num_heads=cfg['mass_spectrometry']['num_attention_heads'],
110
+ ms_max_length=cfg['mass_spectrometry']['max_length'],
111
+ struct_vocab_size=cfg['structure_3d']['vocab_size'],
112
+ struct_hidden_size=cfg['structure_3d']['hidden_size'],
113
+ struct_num_layers=cfg['structure_3d']['num_hidden_layers'],
114
+ struct_num_heads=cfg['structure_3d']['num_attention_heads'],
115
+ struct_max_length=cfg['structure_3d']['max_length'],
116
+ fusion_hidden_size=cfg['fusion']['fusion_hidden_size'],
117
+ fusion_num_layers=cfg['fusion']['fusion_num_layers'],
118
+ )
119
+
120
+ model = MultimodalGlycanBERT(config)
121
+
122
+ if 'model_state_dict' in checkpoint:
123
+ model.load_state_dict(checkpoint['model_state_dict'])
124
+ else:
125
+ model.load_state_dict(checkpoint)
126
+
127
+ return model
128
+
129
+ def find_ambiguous_sequences(self, sequences: List[Dict]) -> Tuple[List[Dict], List[Dict], Dict]:
130
+ """Separate sequences into ambiguous and clean."""
131
+ ambiguous, clean = [], []
132
+ total_ambig_tokens = 0
133
+
134
+ for seq in sequences:
135
+ token_ids = seq.get('token_ids', [])
136
+ ambig_count = sum(1 for tid in token_ids if tid in self.ambiguous_token_ids)
137
+
138
+ if ambig_count > 0:
139
+ seq_copy = seq.copy()
140
+ seq_copy['_ambig_count'] = ambig_count
141
+ ambiguous.append(seq_copy)
142
+ total_ambig_tokens += ambig_count
143
+ else:
144
+ clean.append(seq)
145
+
146
+ stats = {
147
+ 'total_sequences': len(sequences),
148
+ 'ambiguous_sequences': len(ambiguous),
149
+ 'clean_sequences': len(clean),
150
+ 'total_ambiguous_tokens': total_ambig_tokens,
151
+ 'ambiguous_percentage': 100 * len(ambiguous) / len(sequences) if sequences else 0,
152
+ }
153
+
154
+ return ambiguous, clean, stats
155
+
156
+ @torch.no_grad()
157
+ def resolve_batch(self, sequences: List[Dict], max_length: int = 256) -> Tuple[List[Dict], int, int]:
158
+ """Resolve ambiguous tokens in a batch."""
159
+ if not sequences:
160
+ return [], 0, 0
161
+
162
+ batch_token_ids, batch_attention_masks, batch_ambig_positions = [], [], []
163
+
164
+ for seq in sequences:
165
+ token_ids = list(seq.get('token_ids', []))
166
+ ambig_positions = [i for i, tid in enumerate(token_ids) if tid in self.ambiguous_token_ids]
167
+ batch_ambig_positions.append(ambig_positions)
168
+
169
+ if len(token_ids) > max_length:
170
+ token_ids = token_ids[:max_length]
171
+
172
+ attention_mask = [1] * len(token_ids)
173
+ pad_length = max_length - len(token_ids)
174
+ token_ids = token_ids + [self.pad_id] * pad_length
175
+ attention_mask = attention_mask + [0] * pad_length
176
+
177
+ masked_ids = token_ids.copy()
178
+ for pos in ambig_positions:
179
+ if pos < max_length:
180
+ masked_ids[pos] = self.mask_id
181
+
182
+ batch_token_ids.append(masked_ids)
183
+ batch_attention_masks.append(attention_mask)
184
+
185
+ seq_tensor = torch.tensor(batch_token_ids, dtype=torch.long, device=self.device)
186
+ attention_tensor = torch.tensor(batch_attention_masks, dtype=torch.long, device=self.device)
187
+ residue_tensor = torch.zeros_like(seq_tensor)
188
+
189
+ batch_size = seq_tensor.shape[0]
190
+ ms_len, struct_len = 150, 200
191
+
192
+ outputs = self.model(
193
+ seq_token_ids=seq_tensor,
194
+ seq_attention_mask=attention_tensor,
195
+ seq_residue_ids=residue_tensor,
196
+ ms_token_ids=torch.zeros(batch_size, ms_len, dtype=torch.long, device=self.device),
197
+ ms_attention_mask=torch.zeros(batch_size, ms_len, dtype=torch.long, device=self.device),
198
+ has_ms=torch.zeros(batch_size, dtype=torch.bool, device=self.device),
199
+ struct_token_ids=torch.zeros(batch_size, struct_len, dtype=torch.long, device=self.device),
200
+ struct_attention_mask=torch.zeros(batch_size, struct_len, dtype=torch.long, device=self.device),
201
+ struct_residue_ids=torch.full((batch_size, struct_len), -1, dtype=torch.long, device=self.device),
202
+ has_3d=torch.zeros(batch_size, dtype=torch.bool, device=self.device),
203
+ return_dict=True,
204
+ )
205
+
206
+ logits = outputs['seq_logits']
207
+
208
+ resolved_sequences = []
209
+ total_resolved, total_remaining = 0, 0
210
+
211
+ for i, seq in enumerate(sequences):
212
+ resolved = deepcopy(seq)
213
+ token_ids = list(resolved['token_ids'])
214
+ ambig_positions = batch_ambig_positions[i]
215
+ n_resolved = 0
216
+
217
+ for pos in ambig_positions:
218
+ if pos >= max_length:
219
+ continue
220
+
221
+ pos_logits = logits[i, pos]
222
+ probs = F.softmax(pos_logits, dim=-1)
223
+ top_prob, top_idx = probs.max(dim=-1)
224
+ confidence = top_prob.item()
225
+ pred_id = top_idx.item()
226
+
227
+ if (confidence >= self.threshold and
228
+ pred_id not in self.ambiguous_token_ids and
229
+ pred_id != token_ids[pos]):
230
+
231
+ original_token = self.id_to_token.get(token_ids[pos], '?')
232
+ new_token = self.id_to_token.get(pred_id, '?')
233
+
234
+ token_ids[pos] = pred_id
235
+ n_resolved += 1
236
+
237
+ if 'resolved_positions' not in resolved:
238
+ resolved['resolved_positions'] = []
239
+ resolved['resolved_positions'].append({
240
+ 'pos': pos, 'original': original_token,
241
+ 'resolved': new_token, 'confidence': confidence,
242
+ })
243
+
244
+ resolved['token_ids'] = token_ids
245
+ if n_resolved > 0:
246
+ resolved['is_distilled'] = True
247
+
248
+ resolved_sequences.append(resolved)
249
+ total_resolved += n_resolved
250
+ n_remaining = sum(1 for tid in token_ids if tid in self.ambiguous_token_ids)
251
+ total_remaining += n_remaining
252
+
253
+ return resolved_sequences, total_resolved, total_remaining
254
+
255
+ def run_ipa(self, sequences: List[Dict], iterations: int = 3) -> Tuple[List[Dict], Dict]:
256
+ """Run IPA self-distillation."""
257
+ logger.info(f"Starting BPE IPA self-distillation (threshold={self.threshold}, iterations={iterations})")
258
+
259
+ ambiguous, clean, initial_stats = self.find_ambiguous_sequences(sequences)
260
+
261
+ logger.info(f"Initial: {initial_stats['ambiguous_sequences']} ambiguous sequences ({initial_stats['ambiguous_percentage']:.1f}%)")
262
+ logger.info(f"Total ambiguous tokens: {initial_stats['total_ambiguous_tokens']}")
263
+
264
+ stats = {'initial': initial_stats, 'iterations': [], 'threshold': self.threshold}
265
+ current_sequences = ambiguous
266
+
267
+ for iteration in range(iterations):
268
+ logger.info(f"\n=== Iteration {iteration + 1}/{iterations} ===")
269
+
270
+ resolved_this_iter, total_remaining = 0, 0
271
+ new_sequences = []
272
+
273
+ num_batches = (len(current_sequences) + self.batch_size - 1) // self.batch_size
274
+
275
+ for batch_idx in tqdm(range(num_batches), desc=f"Iteration {iteration + 1}"):
276
+ start_idx = batch_idx * self.batch_size
277
+ end_idx = min(start_idx + self.batch_size, len(current_sequences))
278
+ batch = current_sequences[start_idx:end_idx]
279
+
280
+ resolved_batch, n_resolved, n_remaining = self.resolve_batch(batch)
281
+ new_sequences.extend(resolved_batch)
282
+ resolved_this_iter += n_resolved
283
+ total_remaining += n_remaining
284
+
285
+ current_sequences = new_sequences
286
+ stats['iterations'].append({
287
+ 'iteration': iteration + 1,
288
+ 'tokens_resolved': resolved_this_iter,
289
+ 'tokens_remaining': total_remaining,
290
+ })
291
+
292
+ logger.info(f" Resolved {resolved_this_iter} tokens, {total_remaining} remaining")
293
+
294
+ if resolved_this_iter == 0:
295
+ logger.info(" No progress, stopping early")
296
+ break
297
+
298
+ all_sequences = clean + current_sequences
299
+ _, still_ambiguous, final_stats = self.find_ambiguous_sequences(current_sequences)
300
+
301
+ stats['final'] = {
302
+ 'total_sequences': len(all_sequences),
303
+ 'fully_resolved': initial_stats['ambiguous_sequences'] - final_stats['ambiguous_sequences'],
304
+ 'still_ambiguous': final_stats['ambiguous_sequences'],
305
+ 'remaining_ambiguous_tokens': final_stats['total_ambiguous_tokens'],
306
+ 'total_tokens_resolved': sum(it['tokens_resolved'] for it in stats['iterations']),
307
+ }
308
+
309
+ logger.info(f"\n=== IPA Complete ===")
310
+ logger.info(f"Total tokens resolved: {stats['final']['total_tokens_resolved']}")
311
+
312
+ return all_sequences, stats
313
+
314
+
315
+ def main():
316
+ parser = argparse.ArgumentParser(description="BPE IPA Self-Distillation")
317
+ parser.add_argument("--checkpoint", type=str, required=True)
318
+ parser.add_argument("--sequences", type=str, required=True)
319
+ parser.add_argument("--vocab", type=str, required=True)
320
+ parser.add_argument("--ambiguity", type=str, required=True)
321
+ parser.add_argument("--output", type=str, required=True)
322
+ parser.add_argument("--threshold", type=float, default=0.8)
323
+ parser.add_argument("--iterations", type=int, default=3)
324
+ parser.add_argument("--batch-size", type=int, default=32)
325
+ parser.add_argument("--device", type=str, default="cuda")
326
+
327
+ args = parser.parse_args()
328
+
329
+ logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
330
+
331
+ logger.info(f"Loading sequences from {args.sequences}")
332
+ with open(args.sequences, 'rb') as f:
333
+ sequences = pickle.load(f)
334
+
335
+ if isinstance(sequences, dict):
336
+ sequences = list(sequences.values())
337
+
338
+ logger.info(f"Loaded {len(sequences)} sequences")
339
+
340
+ distiller = BPEIPADistiller(
341
+ checkpoint_path=args.checkpoint,
342
+ vocab_path=args.vocab,
343
+ ambiguity_path=args.ambiguity,
344
+ device=args.device,
345
+ threshold=args.threshold,
346
+ batch_size=args.batch_size,
347
+ )
348
+
349
+ expanded_sequences, stats = distiller.run_ipa(sequences=sequences, iterations=args.iterations)
350
+
351
+ output_path = Path(args.output)
352
+ output_path.parent.mkdir(parents=True, exist_ok=True)
353
+
354
+ with open(output_path, 'wb') as f:
355
+ pickle.dump(expanded_sequences, f)
356
+
357
+ logger.info(f"Saved {len(expanded_sequences)} expanded sequences to {output_path}")
358
+
359
+ stats_path = output_path.with_suffix('.stats.json')
360
+ with open(stats_path, 'w') as f:
361
+ json.dump(stats, f, indent=2)
362
+
363
+ print("\n" + "="*60)
364
+ print("BPE IPA SELF-DISTILLATION SUMMARY")
365
+ print("="*60)
366
+ print(f"Input sequences: {stats['initial']['total_sequences']}")
367
+ print(f"Initially ambiguous: {stats['initial']['ambiguous_sequences']} ({stats['initial']['ambiguous_percentage']:.1f}%)")
368
+ print(f"Ambiguous tokens: {stats['initial']['total_ambiguous_tokens']}")
369
+ print(f"Threshold: {stats['threshold']}")
370
+ print(f"Iterations run: {len(stats['iterations'])}")
371
+ print(f"Tokens resolved: {stats['final']['total_tokens_resolved']}")
372
+ print(f"Still ambiguous: {stats['final']['still_ambiguous']}")
373
+ print("="*60)
374
+
375
+
376
+ if __name__ == "__main__":
377
+ main()
releases/2026-10-02/iar_completion/runtime_snapshot/project/writing/glycangt_heldout_ipa_validation/cluster/heldout_ipa_eval.py ADDED
@@ -0,0 +1,962 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """
3
+ Held-out IPA-style ambiguity completion evaluation for bertose_v5b_excluded.
4
+
5
+ This script turns fully specified held-out glycans into synthetic ambiguity-style
6
+ test cases by masking semantic WURCS-BPE spans, then evaluates whether the
7
+ leakage-proof Bertose checkpoint can recover the hidden truth.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import argparse
13
+ import csv
14
+ import importlib.util
15
+ import json
16
+ import logging
17
+ import random
18
+ import sys
19
+ from collections import Counter, defaultdict
20
+ from dataclasses import dataclass
21
+ from pathlib import Path
22
+ from typing import Any, Dict, Iterable, List, Optional, Sequence
23
+
24
+ import torch
25
+ import torch.nn.functional as F
26
+ from tqdm import tqdm
27
+
28
+
29
+ LOGGER = logging.getLogger("heldout_ipa_eval")
30
+
31
+ THIS_FILE = Path(__file__).resolve()
32
+ ANALYSIS_ROOT = THIS_FILE.parent.parent
33
+ WRITING_ROOT = ANALYSIS_ROOT.parent
34
+ PROJECT_ROOT = WRITING_ROOT.parent
35
+ TOKENIZER_UTILS_ROOT = PROJECT_ROOT / "bert_training_v4" / "downstream_tasks" / "utils"
36
+
37
+
38
+ def first_existing_path(*candidates: Path) -> Path:
39
+ for candidate in candidates:
40
+ if candidate.exists():
41
+ return candidate
42
+ return candidates[0]
43
+
44
+
45
+ NATURE_CODE_ROOT = first_existing_path(
46
+ WRITING_ROOT / "cluster" / "nature_submission" / "code",
47
+ PROJECT_ROOT / "nature_submission" / "code",
48
+ )
49
+ DEFAULT_CHECKPOINT = first_existing_path(
50
+ WRITING_ROOT / "cluster" / "nature_submission" / "checkpoints" / "bertose_v5b_excluded.pt",
51
+ PROJECT_ROOT / "checkpoints_v5b_excluded" / "best_v5b_excluded_model.pt",
52
+ )
53
+ DEFAULT_VOCAB = first_existing_path(
54
+ WRITING_ROOT / "cluster" / "nature_submission" / "data" / "vocab" / "bpe_vocabulary_clean.json",
55
+ PROJECT_ROOT / "data" / "bpe_vocabulary_clean.json",
56
+ )
57
+ DEFAULT_AMBIGUITY = first_existing_path(
58
+ WRITING_ROOT / "cluster" / "nature_submission" / "data" / "vocab" / "bpe_ambiguity_tokens.json",
59
+ PROJECT_ROOT / "data" / "bpe_ambiguity_tokens.json",
60
+ )
61
+ IPA_DISTILLATION_MODULE = first_existing_path(
62
+ NATURE_CODE_ROOT / "training" / "ipa_bpe_distillation.py",
63
+ PROJECT_ROOT / "bertose" / "code" / "training" / "ipa_bpe_distillation.py",
64
+ PROJECT_ROOT / "bert_v5_bpe_topo" / "ipa_bpe_distillation.py",
65
+ )
66
+ WURCS_TOKENIZER_MODULE = first_existing_path(
67
+ TOKENIZER_UTILS_ROOT / "wurcs_bpe_tokenizer.py",
68
+ NATURE_CODE_ROOT / "tokenizer" / "wurcs_bpe_tokenizer.py",
69
+ PROJECT_ROOT / "bertose" / "code" / "tokenizer" / "wurcs_bpe_tokenizer.py",
70
+ )
71
+
72
+
73
+ def load_attr_from_module(module_path: Path, attr_name: str) -> Any:
74
+ spec = importlib.util.spec_from_file_location(f"heldout_dynamic_{module_path.stem}", module_path)
75
+ if spec is None or spec.loader is None:
76
+ raise ImportError(f"Could not load module spec from {module_path}")
77
+ module = importlib.util.module_from_spec(spec)
78
+ spec.loader.exec_module(module)
79
+ return getattr(module, attr_name)
80
+
81
+
82
+ BPEIPADistiller = load_attr_from_module(IPA_DISTILLATION_MODULE, "BPEIPADistiller")
83
+ WURCSBPETokenizer = load_attr_from_module(WURCS_TOKENIZER_MODULE, "WURCSBPETokenizer")
84
+
85
+
86
+ SPECIAL_TOKEN_NAMES = {
87
+ "[PAD]",
88
+ "[UNK]",
89
+ "[START]",
90
+ "[END]",
91
+ "[MASK]",
92
+ "[BRANCH_OPEN]",
93
+ "[BRANCH_CLOSE]",
94
+ "[LINK]",
95
+ "[MOD]",
96
+ "[RESIDUE_ERROR]",
97
+ }
98
+
99
+
100
+ @dataclass
101
+ class TokenSpan:
102
+ start: int
103
+ end: int
104
+ unit_type: str
105
+ unit_text: str
106
+ residue_id: int
107
+ token_texts: List[str]
108
+
109
+ @property
110
+ def positions(self) -> List[int]:
111
+ return list(range(self.start, self.end + 1))
112
+
113
+
114
+ @dataclass
115
+ class EvalCase:
116
+ case_id: str
117
+ glycan_index: int
118
+ wurcs: str
119
+ wurcs_length: int
120
+ source_membership: str
121
+ source_count: str
122
+ unit_type: str
123
+ unit_text: str
124
+ residue_id: int
125
+ mask_start: int
126
+ mask_end: int
127
+ positions: List[int]
128
+ truth_tokens: List[str]
129
+ truth_token_ids: List[int]
130
+ masked_token_ids: List[int]
131
+ attention_mask: List[int]
132
+ residue_ids: List[int]
133
+ branch_depths: List[int]
134
+ linkage_types: List[int]
135
+ mask_plan: str
136
+
137
+
138
+ class HeldoutIPAEvaluator:
139
+ def __init__(
140
+ self,
141
+ checkpoint: Path,
142
+ vocab: Path,
143
+ ambiguity: Path,
144
+ device: str,
145
+ threshold: float,
146
+ batch_size: int,
147
+ top_k: int,
148
+ max_length: int,
149
+ ) -> None:
150
+ self.distiller = BPEIPADistiller(
151
+ checkpoint_path=str(checkpoint),
152
+ vocab_path=str(vocab),
153
+ ambiguity_path=str(ambiguity),
154
+ device=device,
155
+ threshold=threshold,
156
+ batch_size=batch_size,
157
+ )
158
+ self.tokenizer = WURCSBPETokenizer(str(vocab))
159
+ self.device = self.distiller.device
160
+ self.threshold = threshold
161
+ self.batch_size = batch_size
162
+ self.top_k = top_k
163
+ self.max_length = max_length
164
+ self.model = self.distiller.model
165
+ self.model.eval()
166
+
167
+ self.special_token_ids = {
168
+ self.distiller.token_to_id[name]
169
+ for name in SPECIAL_TOKEN_NAMES
170
+ if name in self.distiller.token_to_id
171
+ }
172
+ self.invalid_prediction_ids = set(self.special_token_ids) | set(self.distiller.ambiguous_token_ids)
173
+
174
+ LOGGER.info(
175
+ "Evaluator ready | device=%s threshold=%.2f batch_size=%d top_k=%d max_length=%d",
176
+ self.device,
177
+ self.threshold,
178
+ self.batch_size,
179
+ self.top_k,
180
+ self.max_length,
181
+ )
182
+
183
+ def build_cases(
184
+ self,
185
+ rows: Sequence[Dict[str, str]],
186
+ span_types: Sequence[str],
187
+ selection_mode: str,
188
+ max_cases_per_glycan: int,
189
+ seed: int,
190
+ mask_requirements: Optional[Dict[str, int]] = None,
191
+ ) -> tuple[List[EvalCase], Dict[str, Any]]:
192
+ rng = random.Random(seed)
193
+ span_types = set(span_types)
194
+ mask_requirements = mask_requirements or {}
195
+
196
+ all_cases: List[EvalCase] = []
197
+ build_stats: Dict[str, Any] = {
198
+ "glycans_seen": len(rows),
199
+ "glycans_with_eligible_spans": 0,
200
+ "glycans_without_eligible_spans": 0,
201
+ "spans_seen_by_type": Counter(),
202
+ "spans_eligible_by_type": Counter(),
203
+ "spans_selected_by_type": Counter(),
204
+ "spans_skipped_truth_ambiguous": Counter(),
205
+ "spans_skipped_truncated": Counter(),
206
+ "glycans_skipped_mask_plan": 0,
207
+ }
208
+
209
+ for glycan_index, row in enumerate(tqdm(rows, desc="Building held-out cases")):
210
+ wurcs = row["wurcs"]
211
+ tokenized, spans, span_meta = self._tokenize_with_spans(wurcs)
212
+ for unit_type, count in span_meta["seen_by_type"].items():
213
+ build_stats["spans_seen_by_type"][unit_type] += count
214
+ for unit_type, count in span_meta["skipped_truncated_by_type"].items():
215
+ build_stats["spans_skipped_truncated"][unit_type] += count
216
+
217
+ eligible_spans: List[TokenSpan] = []
218
+ for span in spans:
219
+ if span.unit_type not in span_types:
220
+ continue
221
+ if any(token_id in self.invalid_prediction_ids for token_id in self._slice(tokenized["token_ids"], span)):
222
+ build_stats["spans_skipped_truth_ambiguous"][span.unit_type] += 1
223
+ continue
224
+ build_stats["spans_eligible_by_type"][span.unit_type] += 1
225
+ eligible_spans.append(span)
226
+
227
+ if not eligible_spans:
228
+ build_stats["glycans_without_eligible_spans"] += 1
229
+ continue
230
+
231
+ build_stats["glycans_with_eligible_spans"] += 1
232
+ eligible_spans_by_type: Dict[str, List[TokenSpan]] = defaultdict(list)
233
+ for span in eligible_spans:
234
+ eligible_spans_by_type[span.unit_type].append(span)
235
+
236
+ selected_spans = self._select_spans(
237
+ spans=eligible_spans,
238
+ spans_by_type=eligible_spans_by_type,
239
+ selection_mode=selection_mode,
240
+ max_cases_per_glycan=max_cases_per_glycan,
241
+ rng=rng,
242
+ mask_requirements=mask_requirements,
243
+ )
244
+ if mask_requirements and not selected_spans:
245
+ build_stats["glycans_skipped_mask_plan"] += 1
246
+ continue
247
+
248
+ for local_idx, span in enumerate(selected_spans):
249
+ build_stats["spans_selected_by_type"][span.unit_type] += 1
250
+ if mask_requirements:
251
+ selected_ordered = sorted(selected_spans, key=lambda item: (item.start, item.end))
252
+ truth_ids: List[int] = []
253
+ truth_tokens: List[str] = []
254
+ positions: List[int] = []
255
+ mask_start = min(span.start for span in selected_ordered)
256
+ mask_end = max(span.end for span in selected_ordered)
257
+ masked_token_ids = list(tokenized["token_ids"])
258
+ unit_text = " | ".join(f"{span.unit_type}:{span.unit_text}" for span in selected_ordered)
259
+ mask_plan = ",".join(f"{unit_type}:{count}" for unit_type, count in sorted(mask_requirements.items()))
260
+ case_id = f"g{glycan_index:04d}_plan_{mask_plan.replace(',', '_').replace(':', 'x')}"
261
+ for span in selected_ordered:
262
+ truth_ids.extend(self._slice(tokenized["token_ids"], span))
263
+ truth_tokens.extend(self._slice(tokenized["tokens"], span))
264
+ positions.extend(span.positions)
265
+ for pos in span.positions:
266
+ masked_token_ids[pos] = self.distiller.mask_id
267
+ all_cases.append(
268
+ EvalCase(
269
+ case_id=case_id,
270
+ glycan_index=glycan_index,
271
+ wurcs=wurcs,
272
+ wurcs_length=len(wurcs),
273
+ source_membership=row.get("source_membership", ""),
274
+ source_count=row.get("source_count", ""),
275
+ unit_type=mask_plan,
276
+ unit_text=unit_text,
277
+ residue_id=selected_ordered[0].residue_id,
278
+ mask_start=mask_start,
279
+ mask_end=mask_end,
280
+ positions=positions,
281
+ truth_tokens=truth_tokens,
282
+ truth_token_ids=truth_ids,
283
+ masked_token_ids=masked_token_ids,
284
+ attention_mask=list(tokenized["attention_mask"]),
285
+ residue_ids=list(tokenized["residue_ids"]),
286
+ branch_depths=list(tokenized["branch_depths"]),
287
+ linkage_types=list(tokenized["linkage_types"]),
288
+ mask_plan=mask_plan,
289
+ )
290
+ )
291
+ else:
292
+ for local_idx, span in enumerate(selected_spans):
293
+ truth_ids = self._slice(tokenized["token_ids"], span)
294
+ truth_tokens = self._slice(tokenized["tokens"], span)
295
+ masked_token_ids = list(tokenized["token_ids"])
296
+ for pos in span.positions:
297
+ masked_token_ids[pos] = self.distiller.mask_id
298
+
299
+ all_cases.append(
300
+ EvalCase(
301
+ case_id=f"g{glycan_index:04d}_s{local_idx:02d}_{span.unit_type}_{span.start}_{span.end}",
302
+ glycan_index=glycan_index,
303
+ wurcs=wurcs,
304
+ wurcs_length=len(wurcs),
305
+ source_membership=row.get("source_membership", ""),
306
+ source_count=row.get("source_count", ""),
307
+ unit_type=span.unit_type,
308
+ unit_text=span.unit_text,
309
+ residue_id=span.residue_id,
310
+ mask_start=span.start,
311
+ mask_end=span.end,
312
+ positions=span.positions,
313
+ truth_tokens=truth_tokens,
314
+ truth_token_ids=truth_ids,
315
+ masked_token_ids=masked_token_ids,
316
+ attention_mask=list(tokenized["attention_mask"]),
317
+ residue_ids=list(tokenized["residue_ids"]),
318
+ branch_depths=list(tokenized["branch_depths"]),
319
+ linkage_types=list(tokenized["linkage_types"]),
320
+ mask_plan=f"{span.unit_type}:1",
321
+ )
322
+ )
323
+
324
+ build_stats["num_cases"] = len(all_cases)
325
+ build_stats["num_masked_positions"] = sum(len(case.positions) for case in all_cases)
326
+ build_stats["avg_masked_positions_per_case"] = (
327
+ build_stats["num_masked_positions"] / len(all_cases) if all_cases else 0.0
328
+ )
329
+ build_stats["span_types"] = sorted(span_types)
330
+ return all_cases, self._json_ready(build_stats)
331
+
332
+ def evaluate_cases(self, cases: Sequence[EvalCase], iterations: int) -> tuple[List[Dict[str, Any]], Dict[str, Any]]:
333
+ if not cases:
334
+ return [], {}
335
+
336
+ results: List[Dict[str, Any]] = []
337
+ num_batches = (len(cases) + self.batch_size - 1) // self.batch_size
338
+
339
+ for batch_idx in tqdm(range(num_batches), desc="Evaluating held-out cases"):
340
+ start = batch_idx * self.batch_size
341
+ end = min(start + self.batch_size, len(cases))
342
+ batch_cases = list(cases[start:end])
343
+ batch_results = self._evaluate_batch(batch_cases=batch_cases, iterations=iterations)
344
+ results.extend(batch_results)
345
+
346
+ summary = self._summarize_results(results=results, iterations=iterations)
347
+ return results, summary
348
+
349
+ def _evaluate_batch(self, batch_cases: Sequence[EvalCase], iterations: int) -> List[Dict[str, Any]]:
350
+ current_token_ids = [list(case.masked_token_ids) for case in batch_cases]
351
+ unresolved_positions = [set(case.positions) for case in batch_cases]
352
+ initial_position_details: List[List[Dict[str, Any]]] = [[] for _ in batch_cases]
353
+ accepted_updates_per_case: List[List[Dict[str, Any]]] = [[] for _ in batch_cases]
354
+
355
+ for iteration_idx in range(iterations):
356
+ logits = self._forward_logits(batch_cases=batch_cases, current_token_ids=current_token_ids)
357
+
358
+ progress_this_round = 0
359
+ for case_idx, case in enumerate(batch_cases):
360
+ if not unresolved_positions[case_idx]:
361
+ continue
362
+
363
+ iteration_updates: List[Dict[str, Any]] = []
364
+ for local_pos_idx, pos in enumerate(case.positions):
365
+ position_logits = logits[case_idx, pos]
366
+ probs = F.softmax(position_logits, dim=-1)
367
+ top_probs, top_ids = torch.topk(probs, k=self.top_k)
368
+
369
+ top_tokens = [self.distiller.id_to_token[int(token_id)] for token_id in top_ids.tolist()]
370
+ detail = {
371
+ "position": pos,
372
+ "truth_token": case.truth_tokens[local_pos_idx],
373
+ "truth_token_id": case.truth_token_ids[local_pos_idx],
374
+ "top_ids": [int(token_id) for token_id in top_ids.tolist()],
375
+ "top_tokens": top_tokens,
376
+ "top_probs": [float(prob) for prob in top_probs.tolist()],
377
+ "top1_id": int(top_ids[0].item()),
378
+ "top1_token": top_tokens[0],
379
+ "top1_prob": float(top_probs[0].item()),
380
+ "top1_correct": int(top_ids[0].item()) == case.truth_token_ids[local_pos_idx],
381
+ "top5_contains_truth": case.truth_token_ids[local_pos_idx] in {
382
+ int(token_id) for token_id in top_ids.tolist()
383
+ },
384
+ }
385
+
386
+ if iteration_idx == 0:
387
+ initial_position_details[case_idx].append(detail)
388
+
389
+ if pos not in unresolved_positions[case_idx]:
390
+ continue
391
+
392
+ pred_id = detail["top1_id"]
393
+ pred_prob = detail["top1_prob"]
394
+ if pred_prob < self.threshold:
395
+ continue
396
+ if pred_id in self.invalid_prediction_ids:
397
+ continue
398
+
399
+ current_token_ids[case_idx][pos] = pred_id
400
+ unresolved_positions[case_idx].remove(pos)
401
+ progress_this_round += 1
402
+ iteration_updates.append(
403
+ {
404
+ "iteration": iteration_idx + 1,
405
+ "position": pos,
406
+ "predicted_id": pred_id,
407
+ "predicted_token": detail["top1_token"],
408
+ "confidence": pred_prob,
409
+ "correct": pred_id == detail["truth_token_id"],
410
+ }
411
+ )
412
+
413
+ if iteration_updates:
414
+ accepted_updates_per_case[case_idx].extend(iteration_updates)
415
+
416
+ if progress_this_round == 0:
417
+ LOGGER.info("Stopping early after iteration %d because no new positions were accepted", iteration_idx + 1)
418
+ break
419
+
420
+ batch_results: List[Dict[str, Any]] = []
421
+ for case_idx, case in enumerate(batch_cases):
422
+ final_token_ids = [current_token_ids[case_idx][pos] for pos in case.positions]
423
+ final_tokens = [self.distiller.id_to_token[token_id] for token_id in final_token_ids]
424
+ final_correct_flags = [
425
+ final_token_ids[pos_idx] == case.truth_token_ids[pos_idx]
426
+ for pos_idx in range(len(case.positions))
427
+ ]
428
+ resolved_flags = [
429
+ case.positions[pos_idx] not in unresolved_positions[case_idx]
430
+ for pos_idx in range(len(case.positions))
431
+ ]
432
+ initial_details = initial_position_details[case_idx]
433
+
434
+ batch_results.append(
435
+ {
436
+ "case_id": case.case_id,
437
+ "glycan_index": case.glycan_index,
438
+ "wurcs": case.wurcs,
439
+ "wurcs_length": case.wurcs_length,
440
+ "source_membership": case.source_membership,
441
+ "source_count": case.source_count,
442
+ "unit_type": case.unit_type,
443
+ "mask_plan": case.mask_plan,
444
+ "unit_text": case.unit_text,
445
+ "residue_id": case.residue_id,
446
+ "positions": case.positions,
447
+ "mask_start": case.mask_start,
448
+ "mask_end": case.mask_end,
449
+ "num_masked_positions": len(case.positions),
450
+ "truth_tokens": case.truth_tokens,
451
+ "truth_token_ids": case.truth_token_ids,
452
+ "initial_position_details": initial_details,
453
+ "accepted_updates": accepted_updates_per_case[case_idx],
454
+ "final_tokens": final_tokens,
455
+ "final_token_ids": final_token_ids,
456
+ "resolved_flags": resolved_flags,
457
+ "final_correct_flags": final_correct_flags,
458
+ "initial_top1_correct_count": sum(1 for item in initial_details if item["top1_correct"]),
459
+ "initial_top5_correct_count": sum(1 for item in initial_details if item["top5_contains_truth"]),
460
+ "resolved_count": sum(1 for flag in resolved_flags if flag),
461
+ "final_correct_count": sum(1 for flag in final_correct_flags if flag),
462
+ "initial_span_exact_top1": all(item["top1_correct"] for item in initial_details) if initial_details else False,
463
+ "initial_span_exact_top5": all(item["top5_contains_truth"] for item in initial_details) if initial_details else False,
464
+ "iterative_span_exact": all(final_correct_flags) if final_correct_flags else False,
465
+ "iterations_used": max(
466
+ (update["iteration"] for update in accepted_updates_per_case[case_idx]),
467
+ default=0,
468
+ ),
469
+ }
470
+ )
471
+
472
+ return batch_results
473
+
474
+ def _forward_logits(
475
+ self,
476
+ batch_cases: Sequence[EvalCase],
477
+ current_token_ids: Sequence[List[int]],
478
+ ) -> torch.Tensor:
479
+ batch_size = len(batch_cases)
480
+
481
+ seq_token_ids = torch.tensor(current_token_ids, dtype=torch.long, device=self.device)
482
+ seq_attention_mask = torch.tensor(
483
+ [case.attention_mask for case in batch_cases], dtype=torch.long, device=self.device
484
+ )
485
+ seq_residue_ids = torch.tensor(
486
+ [case.residue_ids for case in batch_cases], dtype=torch.long, device=self.device
487
+ )
488
+ seq_branch_depths = torch.tensor(
489
+ [case.branch_depths for case in batch_cases], dtype=torch.long, device=self.device
490
+ )
491
+ seq_linkage_types = torch.tensor(
492
+ [case.linkage_types for case in batch_cases], dtype=torch.long, device=self.device
493
+ )
494
+
495
+ ms_len = 150
496
+ struct_len = 200
497
+ outputs = self.model(
498
+ seq_token_ids=seq_token_ids,
499
+ seq_attention_mask=seq_attention_mask,
500
+ seq_residue_ids=seq_residue_ids,
501
+ seq_branch_depths=seq_branch_depths,
502
+ seq_linkage_types=seq_linkage_types,
503
+ ms_token_ids=torch.zeros(batch_size, ms_len, dtype=torch.long, device=self.device),
504
+ ms_attention_mask=torch.zeros(batch_size, ms_len, dtype=torch.long, device=self.device),
505
+ has_ms=torch.zeros(batch_size, dtype=torch.bool, device=self.device),
506
+ struct_token_ids=torch.zeros(batch_size, struct_len, dtype=torch.long, device=self.device),
507
+ struct_attention_mask=torch.zeros(batch_size, struct_len, dtype=torch.long, device=self.device),
508
+ struct_residue_ids=torch.full((batch_size, struct_len), -1, dtype=torch.long, device=self.device),
509
+ has_3d=torch.zeros(batch_size, dtype=torch.bool, device=self.device),
510
+ return_dict=True,
511
+ )
512
+ return outputs["seq_logits"]
513
+
514
+ def _tokenize_with_spans(self, wurcs: str) -> tuple[Dict[str, Any], List[TokenSpan], Dict[str, Counter]]:
515
+ tokenized = self.tokenizer.tokenize(wurcs, max_length=self.max_length)
516
+ unpadded_tokens = tokenized["tokens"][: tokenized["length"]]
517
+ units = self.tokenizer.pre_tokenizer.pre_tokenize(wurcs)
518
+
519
+ spans: List[TokenSpan] = []
520
+ raw_cursor = 0
521
+ span_meta = {
522
+ "seen_by_type": Counter(),
523
+ "skipped_truncated_by_type": Counter(),
524
+ }
525
+
526
+ for unit in units:
527
+ unit_tokens = [unit["text"]] if unit["type"] == "special" else self.tokenizer.apply_bpe(unit["text"])
528
+ span_start = raw_cursor
529
+ span_end = raw_cursor + len(unit_tokens) - 1
530
+ raw_cursor += len(unit_tokens)
531
+
532
+ if unit["type"] != "special":
533
+ span_meta["seen_by_type"][unit["type"]] += 1
534
+
535
+ if unit["type"] == "special":
536
+ continue
537
+ if span_end >= len(unpadded_tokens):
538
+ span_meta["skipped_truncated_by_type"][unit["type"]] += 1
539
+ continue
540
+ if unpadded_tokens[span_start : span_end + 1] != unit_tokens:
541
+ span_meta["skipped_truncated_by_type"][unit["type"]] += 1
542
+ continue
543
+
544
+ spans.append(
545
+ TokenSpan(
546
+ start=span_start,
547
+ end=span_end,
548
+ unit_type=unit["type"],
549
+ unit_text=unit["text"],
550
+ residue_id=unit["residue_id"],
551
+ token_texts=unit_tokens,
552
+ )
553
+ )
554
+
555
+ return tokenized, spans, span_meta
556
+
557
+ @staticmethod
558
+ def _slice(values: Sequence[Any], span: TokenSpan) -> List[Any]:
559
+ return list(values[span.start : span.end + 1])
560
+
561
+ @staticmethod
562
+ def _select_spans(
563
+ spans: Sequence[TokenSpan],
564
+ spans_by_type: Dict[str, List[TokenSpan]],
565
+ selection_mode: str,
566
+ max_cases_per_glycan: int,
567
+ rng: random.Random,
568
+ mask_requirements: Optional[Dict[str, int]] = None,
569
+ ) -> List[TokenSpan]:
570
+ ordered = sorted(spans, key=lambda item: (item.start, item.end))
571
+ mask_requirements = mask_requirements or {}
572
+ if mask_requirements:
573
+ selected: List[TokenSpan] = []
574
+ for unit_type, count in sorted(mask_requirements.items()):
575
+ unit_spans = sorted(spans_by_type.get(unit_type, []), key=lambda item: (item.start, item.end))
576
+ if len(unit_spans) < count:
577
+ return []
578
+ if selection_mode == "all":
579
+ chosen = unit_spans[:count]
580
+ elif selection_mode == "first":
581
+ chosen = unit_spans[:count]
582
+ elif selection_mode == "sample":
583
+ chosen = sorted(rng.sample(unit_spans, count), key=lambda item: (item.start, item.end))
584
+ else:
585
+ raise ValueError(f"Unsupported selection mode: {selection_mode}")
586
+ selected.extend(chosen)
587
+ return sorted(selected, key=lambda item: (item.start, item.end))
588
+ if selection_mode == "all":
589
+ return ordered
590
+ if selection_mode == "first":
591
+ return ordered[:max_cases_per_glycan]
592
+ if selection_mode == "sample":
593
+ k = min(max_cases_per_glycan, len(ordered))
594
+ return sorted(rng.sample(ordered, k), key=lambda item: (item.start, item.end))
595
+ raise ValueError(f"Unsupported selection mode: {selection_mode}")
596
+
597
+ @staticmethod
598
+ def _json_ready(obj: Any) -> Any:
599
+ if isinstance(obj, Counter):
600
+ return dict(obj)
601
+ if isinstance(obj, defaultdict):
602
+ return {key: HeldoutIPAEvaluator._json_ready(value) for key, value in obj.items()}
603
+ if isinstance(obj, dict):
604
+ return {key: HeldoutIPAEvaluator._json_ready(value) for key, value in obj.items()}
605
+ if isinstance(obj, list):
606
+ return [HeldoutIPAEvaluator._json_ready(value) for value in obj]
607
+ return obj
608
+
609
+ def _summarize_results(self, results: Sequence[Dict[str, Any]], iterations: int) -> Dict[str, Any]:
610
+ total_cases = len(results)
611
+ total_positions = sum(result["num_masked_positions"] for result in results)
612
+ if total_cases == 0 or total_positions == 0:
613
+ return {}
614
+
615
+ overall = self._aggregate_slice(results)
616
+ by_unit_type = {
617
+ unit_type: self._aggregate_slice([result for result in results if result["unit_type"] == unit_type])
618
+ for unit_type in sorted({result["unit_type"] for result in results})
619
+ }
620
+
621
+ return {
622
+ "num_cases": total_cases,
623
+ "num_masked_positions": total_positions,
624
+ "iterations_requested": iterations,
625
+ "threshold": self.threshold,
626
+ "top_k": self.top_k,
627
+ "overall": overall,
628
+ "by_unit_type": by_unit_type,
629
+ }
630
+
631
+ @staticmethod
632
+ def _aggregate_slice(results: Sequence[Dict[str, Any]]) -> Dict[str, Any]:
633
+ total_cases = len(results)
634
+ total_positions = sum(result["num_masked_positions"] for result in results)
635
+ initial_top1_correct = sum(result["initial_top1_correct_count"] for result in results)
636
+ initial_top5_correct = sum(result["initial_top5_correct_count"] for result in results)
637
+ resolved_count = sum(result["resolved_count"] for result in results)
638
+ final_correct_count = sum(result["final_correct_count"] for result in results)
639
+
640
+ return {
641
+ "num_cases": total_cases,
642
+ "num_masked_positions": total_positions,
643
+ "avg_masked_positions_per_case": total_positions / total_cases if total_cases else 0.0,
644
+ "initial_top1_token_accuracy": initial_top1_correct / total_positions if total_positions else 0.0,
645
+ "initial_top5_token_accuracy": initial_top5_correct / total_positions if total_positions else 0.0,
646
+ "initial_exact_span_accuracy_top1": (
647
+ sum(1 for result in results if result["initial_span_exact_top1"]) / total_cases if total_cases else 0.0
648
+ ),
649
+ "initial_exact_span_accuracy_top5": (
650
+ sum(1 for result in results if result["initial_span_exact_top5"]) / total_cases if total_cases else 0.0
651
+ ),
652
+ "iterative_resolved_fraction": resolved_count / total_positions if total_positions else 0.0,
653
+ "iterative_accuracy_among_resolved": final_correct_count / resolved_count if resolved_count else 0.0,
654
+ "iterative_final_token_accuracy": final_correct_count / total_positions if total_positions else 0.0,
655
+ "iterative_exact_span_accuracy": (
656
+ sum(1 for result in results if result["iterative_span_exact"]) / total_cases if total_cases else 0.0
657
+ ),
658
+ "avg_iterations_used_per_case": (
659
+ sum(result["iterations_used"] for result in results) / total_cases if total_cases else 0.0
660
+ ),
661
+ }
662
+
663
+
664
+ def parse_args() -> argparse.Namespace:
665
+ parser = argparse.ArgumentParser(description="Held-out IPA-style evaluation for bertose_v5b_excluded")
666
+ parser.add_argument(
667
+ "--input-csv",
668
+ type=Path,
669
+ default=ANALYSIS_ROOT / "data" / "benchmark_exclusion_1392_unseen_to_bertose_v5b_excluded.csv",
670
+ )
671
+ parser.add_argument(
672
+ "--checkpoint",
673
+ type=Path,
674
+ default=DEFAULT_CHECKPOINT,
675
+ )
676
+ parser.add_argument(
677
+ "--vocab",
678
+ type=Path,
679
+ default=DEFAULT_VOCAB,
680
+ )
681
+ parser.add_argument(
682
+ "--ambiguity",
683
+ type=Path,
684
+ default=DEFAULT_AMBIGUITY,
685
+ )
686
+ parser.add_argument("--output-dir", type=Path, default=ANALYSIS_ROOT / "outputs")
687
+ parser.add_argument("--run-name", type=str, default="benchmark1392_pilot")
688
+ parser.add_argument("--device", type=str, default="cuda")
689
+ parser.add_argument("--batch-size", type=int, default=32)
690
+ parser.add_argument("--iterations", type=int, default=3)
691
+ parser.add_argument("--threshold", type=float, default=0.80)
692
+ parser.add_argument("--top-k", type=int, default=5)
693
+ parser.add_argument("--max-length", type=int, default=256)
694
+ parser.add_argument("--limit", type=int, default=None)
695
+ parser.add_argument("--seed", type=int, default=42)
696
+ parser.add_argument("--shuffle", action="store_true")
697
+ parser.add_argument("--selection-mode", choices=["sample", "first", "all"], default="sample")
698
+ parser.add_argument("--max-cases-per-glycan", type=int, default=1)
699
+ parser.add_argument("--span-types", type=str, default="residue,linkage")
700
+ parser.add_argument("--mask-plan", type=str, default="")
701
+ parser.add_argument("--log-level", type=str, default="INFO")
702
+ return parser.parse_args()
703
+
704
+
705
+ def parse_mask_plan(mask_plan: str) -> Dict[str, int]:
706
+ mask_plan = mask_plan.strip()
707
+ if not mask_plan:
708
+ return {}
709
+ requirements: Dict[str, int] = {}
710
+ for item in mask_plan.split(","):
711
+ part = item.strip()
712
+ if not part:
713
+ continue
714
+ if ":" not in part:
715
+ raise ValueError(f"Invalid mask-plan item: {part}")
716
+ unit_type, count_str = part.split(":", 1)
717
+ unit_type = unit_type.strip()
718
+ count = int(count_str.strip())
719
+ if count <= 0:
720
+ raise ValueError(f"Mask-plan counts must be positive: {part}")
721
+ requirements[unit_type] = count
722
+ return requirements
723
+
724
+
725
+ def setup_logging(level: str) -> None:
726
+ logging.basicConfig(
727
+ level=getattr(logging, level.upper(), logging.INFO),
728
+ format="%(asctime)s - %(levelname)s - %(message)s",
729
+ )
730
+
731
+
732
+ def load_rows(input_csv: Path, limit: Optional[int], shuffle: bool, seed: int) -> List[Dict[str, str]]:
733
+ with input_csv.open() as handle:
734
+ rows = list(csv.DictReader(handle))
735
+
736
+ if "in_reconstructed_leakage_proof_pretrain" in rows[0]:
737
+ not_unseen = [
738
+ row["wurcs"]
739
+ for row in rows
740
+ if row["in_reconstructed_leakage_proof_pretrain"].strip().lower() != "false"
741
+ ]
742
+ if not_unseen:
743
+ raise ValueError(
744
+ f"Input CSV contains {len(not_unseen)} glycans that are not marked unseen to the leakage-proof model"
745
+ )
746
+
747
+ if shuffle:
748
+ rng = random.Random(seed)
749
+ rng.shuffle(rows)
750
+
751
+ if limit is not None:
752
+ rows = rows[:limit]
753
+
754
+ return rows
755
+
756
+
757
+ def write_json(path: Path, payload: Dict[str, Any]) -> None:
758
+ path.parent.mkdir(parents=True, exist_ok=True)
759
+ path.write_text(json.dumps(payload, indent=2))
760
+
761
+
762
+ def write_jsonl(path: Path, rows: Iterable[Dict[str, Any]]) -> None:
763
+ path.parent.mkdir(parents=True, exist_ok=True)
764
+ with path.open("w") as handle:
765
+ for row in rows:
766
+ handle.write(json.dumps(row) + "\n")
767
+
768
+
769
+ def write_case_csv(path: Path, rows: Sequence[Dict[str, Any]]) -> None:
770
+ flat_rows = []
771
+ for row in rows:
772
+ flat_rows.append(
773
+ {
774
+ "case_id": row["case_id"],
775
+ "glycan_index": row["glycan_index"],
776
+ "unit_type": row["unit_type"],
777
+ "mask_plan": row.get("mask_plan", row["unit_type"]),
778
+ "unit_text": row["unit_text"],
779
+ "mask_start": row["mask_start"],
780
+ "mask_end": row["mask_end"],
781
+ "num_masked_positions": row["num_masked_positions"],
782
+ "initial_top1_correct_count": row["initial_top1_correct_count"],
783
+ "initial_top5_correct_count": row["initial_top5_correct_count"],
784
+ "resolved_count": row["resolved_count"],
785
+ "final_correct_count": row["final_correct_count"],
786
+ "initial_span_exact_top1": row["initial_span_exact_top1"],
787
+ "initial_span_exact_top5": row["initial_span_exact_top5"],
788
+ "iterative_span_exact": row["iterative_span_exact"],
789
+ "iterations_used": row["iterations_used"],
790
+ "truth_tokens": "|".join(row["truth_tokens"]),
791
+ "final_tokens": "|".join(row["final_tokens"]),
792
+ "wurcs": row["wurcs"],
793
+ }
794
+ )
795
+
796
+ fieldnames = list(flat_rows[0].keys()) if flat_rows else [
797
+ "case_id",
798
+ "glycan_index",
799
+ "unit_type",
800
+ "mask_plan",
801
+ "unit_text",
802
+ "mask_start",
803
+ "mask_end",
804
+ "num_masked_positions",
805
+ "initial_top1_correct_count",
806
+ "initial_top5_correct_count",
807
+ "resolved_count",
808
+ "final_correct_count",
809
+ "initial_span_exact_top1",
810
+ "initial_span_exact_top5",
811
+ "iterative_span_exact",
812
+ "iterations_used",
813
+ "truth_tokens",
814
+ "final_tokens",
815
+ "wurcs",
816
+ ]
817
+
818
+ with path.open("w", newline="") as handle:
819
+ writer = csv.DictWriter(handle, fieldnames=fieldnames)
820
+ writer.writeheader()
821
+ writer.writerows(flat_rows)
822
+
823
+
824
+ def write_summary_markdown(
825
+ path: Path,
826
+ *,
827
+ args: argparse.Namespace,
828
+ input_rows: Sequence[Dict[str, str]],
829
+ build_stats: Dict[str, Any],
830
+ summary: Dict[str, Any],
831
+ ) -> None:
832
+ overall = summary.get("overall", {})
833
+ by_unit_type = summary.get("by_unit_type", {})
834
+
835
+ lines = [
836
+ "# Held-out IPA Evaluation Summary",
837
+ "",
838
+ "## Run",
839
+ "",
840
+ f"- run_name: `{args.run_name}`",
841
+ f"- input_csv: `{args.input_csv}`",
842
+ f"- checkpoint: `{args.checkpoint}`",
843
+ f"- vocab: `{args.vocab}`",
844
+ f"- ambiguity: `{args.ambiguity}`",
845
+ f"- device: `{args.device}`",
846
+ f"- threshold: `{args.threshold}`",
847
+ f"- iterations: `{args.iterations}`",
848
+ f"- top_k: `{args.top_k}`",
849
+ f"- selection_mode: `{args.selection_mode}`",
850
+ f"- max_cases_per_glycan: `{args.max_cases_per_glycan}`",
851
+ f"- span_types: `{args.span_types}`",
852
+ f"- mask_plan: `{args.mask_plan or 'single-span default'}`",
853
+ "",
854
+ "## Dataset",
855
+ "",
856
+ f"- glycans_loaded: `{len(input_rows)}`",
857
+ f"- cases_built: `{build_stats.get('num_cases', 0)}`",
858
+ f"- masked_positions: `{build_stats.get('num_masked_positions', 0)}`",
859
+ f"- glycans_with_eligible_spans: `{build_stats.get('glycans_with_eligible_spans', 0)}`",
860
+ f"- glycans_without_eligible_spans: `{build_stats.get('glycans_without_eligible_spans', 0)}`",
861
+ f"- glycans_skipped_mask_plan: `{build_stats.get('glycans_skipped_mask_plan', 0)}`",
862
+ "",
863
+ "## Overall Metrics",
864
+ "",
865
+ ]
866
+
867
+ metric_order = [
868
+ "initial_top1_token_accuracy",
869
+ "initial_top5_token_accuracy",
870
+ "initial_exact_span_accuracy_top1",
871
+ "initial_exact_span_accuracy_top5",
872
+ "iterative_resolved_fraction",
873
+ "iterative_accuracy_among_resolved",
874
+ "iterative_final_token_accuracy",
875
+ "iterative_exact_span_accuracy",
876
+ "avg_iterations_used_per_case",
877
+ ]
878
+ for metric in metric_order:
879
+ value = overall.get(metric)
880
+ if value is not None:
881
+ lines.append(f"- {metric}: `{value:.4f}`")
882
+
883
+ if by_unit_type:
884
+ lines.extend(["", "## By Evaluation Group", ""])
885
+ for unit_type, unit_metrics in by_unit_type.items():
886
+ lines.append(f"### {unit_type}")
887
+ lines.append("")
888
+ for metric in metric_order:
889
+ value = unit_metrics.get(metric)
890
+ if value is not None:
891
+ lines.append(f"- {metric}: `{value:.4f}`")
892
+ lines.append("")
893
+
894
+ path.write_text("\n".join(lines).rstrip() + "\n")
895
+
896
+
897
+ def main() -> None:
898
+ args = parse_args()
899
+ setup_logging(args.log_level)
900
+
901
+ LOGGER.info("Loading held-out rows from %s", args.input_csv)
902
+ rows = load_rows(input_csv=args.input_csv, limit=args.limit, shuffle=args.shuffle, seed=args.seed)
903
+ LOGGER.info("Loaded %d glycans", len(rows))
904
+
905
+ span_types = [item.strip() for item in args.span_types.split(",") if item.strip()]
906
+ mask_requirements = parse_mask_plan(args.mask_plan)
907
+ if mask_requirements:
908
+ span_types = sorted(set(span_types) | set(mask_requirements))
909
+ evaluator = HeldoutIPAEvaluator(
910
+ checkpoint=args.checkpoint,
911
+ vocab=args.vocab,
912
+ ambiguity=args.ambiguity,
913
+ device=args.device,
914
+ threshold=args.threshold,
915
+ batch_size=args.batch_size,
916
+ top_k=args.top_k,
917
+ max_length=args.max_length,
918
+ )
919
+
920
+ cases, build_stats = evaluator.build_cases(
921
+ rows=rows,
922
+ span_types=span_types,
923
+ selection_mode=args.selection_mode,
924
+ max_cases_per_glycan=args.max_cases_per_glycan,
925
+ seed=args.seed,
926
+ mask_requirements=mask_requirements,
927
+ )
928
+ LOGGER.info(
929
+ "Built %d evaluation cases across %d masked positions",
930
+ build_stats.get("num_cases", 0),
931
+ build_stats.get("num_masked_positions", 0),
932
+ )
933
+
934
+ results, summary = evaluator.evaluate_cases(cases=cases, iterations=args.iterations)
935
+
936
+ run_dir = args.output_dir / args.run_name
937
+ run_dir.mkdir(parents=True, exist_ok=True)
938
+ write_json(run_dir / "build_stats.json", build_stats)
939
+ write_json(run_dir / "summary.json", summary)
940
+ write_jsonl(run_dir / "case_details.jsonl", results)
941
+ write_case_csv(run_dir / "case_results.csv", results)
942
+ write_summary_markdown(
943
+ run_dir / "summary.md",
944
+ args=args,
945
+ input_rows=rows,
946
+ build_stats=build_stats,
947
+ summary=summary,
948
+ )
949
+
950
+ LOGGER.info("Saved outputs under %s", run_dir)
951
+ if summary:
952
+ LOGGER.info(
953
+ "Overall top1=%.4f top5=%.4f iterative_final=%.4f span_exact=%.4f",
954
+ summary["overall"]["initial_top1_token_accuracy"],
955
+ summary["overall"]["initial_top5_token_accuracy"],
956
+ summary["overall"]["iterative_final_token_accuracy"],
957
+ summary["overall"]["iterative_exact_span_accuracy"],
958
+ )
959
+
960
+
961
+ if __name__ == "__main__":
962
+ main()
releases/2026-10-02/iar_completion/runtime_snapshot/schema/classify.py ADDED
@@ -0,0 +1,78 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Definitive classification of '|' in WURCS LIN + residue-definition sections."""
2
+ import re, collections, json, sys
3
+ sys.path.insert(0,'.')
4
+ from pipelib import (split_sections, _TopLevelBracket, parse_record, ALT_GROUP_RE,
5
+ SIDE_RE, node_index)
6
+
7
+ TOKEN_RE = re.compile(r"[A-Za-z]+[0-9?]+")
8
+
9
+ def groups_in(entry):
10
+ return [m.group(0) for m in ALT_GROUP_RE.finditer(entry)]
11
+
12
+ def group_kind(g):
13
+ members = g.split("|")
14
+ parsed = []
15
+ for m in members:
16
+ mm = SIDE_RE.match(m)
17
+ if not mm: return None
18
+ parsed.append((mm.group(1), mm.group(2)))
19
+ res = [p[0] for p in parsed]; pos = [p[1] for p in parsed]
20
+ ures, upos = set(res), set(pos)
21
+ if len(ures) == 1 and len(upos) > 1:
22
+ return ("P_ALT_UNK" if "?" in upos else "P_ALT"), parsed
23
+ if len(upos) == 1 and len(ures) > 1:
24
+ return ("R_ALT_UNK" if "?" in upos else "R_ALT"), parsed
25
+ if len(ures) == 1 and len(upos) == 1:
26
+ return "DEGENERATE", parsed
27
+ return "JOINT_ALT", parsed
28
+
29
+ # entry-level shape: is it a plain 2-sided linkage with no repeat furniture?
30
+ PLAIN_LINK = re.compile(r"^([A-Za-z0-9?|]+)-([A-Za-z0-9?|]+)$")
31
+
32
+ def classify_entry(entry, rec):
33
+ """Returns dict describing one pipe-bearing LIN entry."""
34
+ out = {}
35
+ out["repeat"] = ("{" in entry) or ("}" in entry)
36
+ out["tilde"] = "~" in entry
37
+ out["substituent"] = "*" in entry
38
+ gs = groups_in(entry)
39
+ kinds = []
40
+ for g in gs:
41
+ k = group_kind(g)
42
+ kinds.append(k[0] if k else "UNPARSED")
43
+ out["groups"] = gs
44
+ out["kinds"] = kinds
45
+ m = PLAIN_LINK.match(entry)
46
+ out["plain_link"] = bool(m)
47
+ out["r205"] = False
48
+ out["r205_detail"] = None
49
+ if m and not out["repeat"] and not out["tilde"] and not out["substituent"]:
50
+ L, R = m.group(1), m.group(2)
51
+ for altside, fixside in ((L, R), (R, L)):
52
+ if "|" not in altside or "|" in fixside:
53
+ continue
54
+ k = group_kind(altside)
55
+ if not k or k[0] != "P_ALT":
56
+ continue
57
+ fm = SIDE_RE.match(fixside)
58
+ if not fm or fm.group(2) == "?":
59
+ continue
60
+ dres, dpos = fm.group(1), int(fm.group(2))
61
+ ares = k[1][0][0]
62
+ apos = sorted(int(p) for _, p in k[1])
63
+ try:
64
+ di = node_index(dres) - 1; ai = node_index(ares) - 1
65
+ except ValueError:
66
+ continue
67
+ if not (0 <= di < rec["nres"] and 0 <= ai < rec["nres"]):
68
+ continue
69
+ danom = rec["anom"][di]; aanom = rec["anom"][ai]
70
+ donor_ok = (danom is not None and danom == dpos)
71
+ acceptor_ok = (aanom is None) or (aanom not in apos)
72
+ if donor_ok and acceptor_ok:
73
+ out["r205"] = True
74
+ out["r205_detail"] = dict(donor_skel=rec["skel"][di], donor_pos=dpos,
75
+ acceptor_skel=rec["skel"][ai], alts=apos,
76
+ acceptor_subs=sorted(rec["subs"][ai]))
77
+ break
78
+ return out
releases/2026-10-02/iar_completion/runtime_snapshot/schema/pipelib.py ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Shared WURCS '|' classification library. Frozen-parser-faithful where it matters."""
2
+ import re
3
+
4
+ WURCS_RES_INDEX_ALPHABET = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ"
5
+
6
+ def node_index(label):
7
+ base = len(WURCS_RES_INDEX_ALPHABET)
8
+ value = 0
9
+ for ch in label:
10
+ d = WURCS_RES_INDEX_ALPHABET.index(ch) + 1 # raises ValueError
11
+ value = value * base + d
12
+ return value
13
+
14
+ def split_sections(wurcs):
15
+ """Frozen A5WURCSPreTokenizer._split_wurcs_sections semantics."""
16
+ if wurcs.startswith("WURCS="):
17
+ body = wurcs[6:]
18
+ else:
19
+ return None
20
+ sections, cur, depth = [], [], 0
21
+ for ch in body:
22
+ if ch == "[":
23
+ depth += 1; cur.append(ch)
24
+ elif ch == "]":
25
+ if depth: depth -= 1
26
+ cur.append(ch)
27
+ elif ch == "/" and depth == 0:
28
+ sections.append("".join(cur)); cur = []
29
+ else:
30
+ cur.append(ch)
31
+ sections.append("".join(cur))
32
+ if len(sections) > 5:
33
+ sections = sections[:4] + ["/".join(sections[4:])]
34
+ return sections
35
+
36
+ class _TopLevelBracket:
37
+ @staticmethod
38
+ def findall(text):
39
+ out, depth, start = [], 0, -1
40
+ for i, c in enumerate(text):
41
+ if c == "[":
42
+ if depth == 0: start = i + 1
43
+ depth += 1
44
+ elif c == "]" and depth:
45
+ depth -= 1
46
+ if depth == 0 and start >= 0:
47
+ out.append(text[start:i]); start = -1
48
+ return [] if depth else out
49
+
50
+ ANOM_RE = re.compile(r"-([0-9]+)([abuxo])(?:_|$)")
51
+ RING_RE = re.compile(r"^(\d+)-(\d+|\?)$")
52
+
53
+ def parse_record(wurcs):
54
+ """Return dict with sections, residue defs, per-instance def index, anomeric pos, LIN entries."""
55
+ s = split_sections(wurcs)
56
+ if s is None or len(s) < 4:
57
+ return None
58
+ defs = _TopLevelBracket.findall(s[2])
59
+ idx_list = s[3].split("-") if s[3] else []
60
+ lin = s[4] if len(s) > 4 else ""
61
+ anom, skel, ring, subs = [], [], [], []
62
+ for di in idx_list:
63
+ try:
64
+ d = defs[int(di) - 1]
65
+ except (IndexError, ValueError):
66
+ anom.append(None); skel.append(None); ring.append(None); subs.append(set()); continue
67
+ m = ANOM_RE.search(d)
68
+ anom.append(int(m.group(1)) if m else None)
69
+ parts = d.split("_")
70
+ skel.append(parts[0].split("-")[0])
71
+ r = None; sb = set()
72
+ for p in parts[1:]:
73
+ mr = RING_RE.match(p)
74
+ if mr:
75
+ r = (int(mr.group(1)), mr.group(2)); continue
76
+ # substituent forms like '2*NCC/3=O', '3|4*OCC/3=O', '6%?%*OC'
77
+ for mm in re.finditer(r"(\d+)(?:%\?%)?\*", p):
78
+ sb.add(int(mm.group(1)))
79
+ ring.append(r); subs.append(sb)
80
+ return dict(wurcs=wurcs, sections=s, defs=defs, idx_list=idx_list, lin=lin,
81
+ anom=anom, skel=skel, ring=ring, subs=subs, nres=len(idx_list))
82
+
83
+ # ---- alternation groups -------------------------------------------------
84
+ ALT_GROUP_RE = re.compile(r"(?:[A-Za-z]+[0-9?]+)(?:\|[A-Za-z]+[0-9?]+)+")
85
+ SIDE_RE = re.compile(r"^([A-Za-z]+)([0-9?]+)$")
86
+
87
+ def alt_groups(entry):
88
+ return [(m.group(0), m.start(), m.end()) for m in ALT_GROUP_RE.finditer(entry)]
89
+
90
+ def classify_group(text):
91
+ members = text.split("|")
92
+ parsed = []
93
+ for m in members:
94
+ mm = SIDE_RE.match(m)
95
+ if not mm: return None
96
+ parsed.append((mm.group(1), mm.group(2)))
97
+ residues = {p[0] for p in parsed}
98
+ positions = {p[1] for p in parsed}
99
+ n = len(parsed)
100
+ if len(residues) == 1 and len(positions) == n:
101
+ kind = "POS_ALT"
102
+ elif len(positions) == 1 and len(residues) == n:
103
+ kind = "RES_ALT_Q" if "?" in positions else "RES_ALT"
104
+ elif len(residues) == 1:
105
+ kind = "POS_ALT_DUP"
106
+ elif len(positions) == 1:
107
+ kind = "RES_ALT_DUP"
108
+ else:
109
+ kind = "JOINT_ALT"
110
+ return dict(kind=kind, n=n, residues=sorted(residues), positions=sorted(positions),
111
+ parsed=parsed, all_q=(positions == {"?"}), any_q=("?" in positions))
112
+
113
+ # split a LIN entry into left/right around the top-level '-' that separates the two sides
114
+ LINK_SPLIT_RE = re.compile(r"^(?P<L>[A-Za-z0-9?|%]+?)(?P<lb>\}?)-(?P<rb>\{?)(?P<R>[A-Za-z0-9?|%]+)(?P<tail>.*)$")
115
+
116
+ def split_entry(entry):
117
+ m = LINK_SPLIT_RE.match(entry)
118
+ if not m: return None
119
+ return m.group("L"), m.group("R"), m.group("lb"), m.group("rb"), m.group("tail")
releases/2026-10-02/iar_inference/README.md ADDED
@@ -0,0 +1,58 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Full-checkpoint IAR inference on two recorded cases
2
+
3
+ This entry point loads the full frozen BERTose WA3 checkpoint and runs the saved 211-class linkage readout on two real, previously executed single-linkage cases. It preserves the original masked side channels, training-frequency-weighted neutral linkage embedding, input-of-`sequence_head` feature, NumPy float32 feature normalization, 0.80 acceptance threshold and maximum of three rounds. No fitting occurs.
4
+
5
+ The fixture was rebuilt with the frozen tokenizer and official WURCS helper in Slurm job 16662851. That job accepted `g8-h2` for the benchmark case and abstained on the grey-seal case. These two cases are an inference check, not an estimate of completion accuracy. The full portable forward passed in job 16683971; the earlier preparation check recorded here loaded no model.
6
+
7
+ ## Files and dependencies
8
+
9
+ Keep these three released directories together, or supply the two root arguments below:
10
+
11
+ ```text
12
+ code/
13
+ iar_inference/ # this runner, configuration and two-case fixtures
14
+ iar_completion/ # saved readout, metadata, original source and vocabulary
15
+ bertose_source/ # unchanged model sources and small component authorities
16
+ ```
17
+
18
+ `config.json` pins the 14 required files under `bertose_source`, five files under `iar_completion`, copied helpers and four fixture files. The full checkpoint is supplied separately through `--checkpoint`; it is not copied here. Its required SHA-256 is `8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82`.
19
+
20
+ Python 3.10 or later is required. `requirements.txt` records the PyTorch and NumPy versions used by the executed CPU reference. Use a CPU-capable PyTorch build. The runner always uses CPU float32, even on a machine with CUDA. Allow several GB of memory for the complete historical checkpoint and model. On Nova, run the full command only in a Slurm compute allocation.
21
+
22
+ ## Run
23
+
24
+ Check the small files without importing PyTorch or opening the checkpoint:
25
+
26
+ ```bash
27
+ python code/iar_inference/infer.py --preflight-only
28
+ ```
29
+
30
+ Run the full checkpoint and compare both cases with the saved execution:
31
+
32
+ ```bash
33
+ python code/iar_inference/infer.py \
34
+ --checkpoint /absolute/path/to/review_md20x6_checkpoint_best.pt \
35
+ --output-dir /absolute/path/to/new_iar_output \
36
+ --threads 4
37
+ ```
38
+
39
+ The output directory must not exist and its parent must exist. To relocate the companion artifacts, add `--source-root /absolute/path/to/bertose_source` and `--readout-root /absolute/path/to/iar_completion`. Their internal relative layouts and bytes must remain intact. No Nova path, Java installation, training dataset or scheduler is required by this runner.
40
+
41
+ A successful full run writes `inference_report.json` and two `caseN_pre_sequence_head.npy` arrays. It must report `PASS_PORTABLE_FULL_WA3_TWO_CASE_REFERENCE_CHECK`, 505 strictly loaded state entries, 138,487,995 model parameters, 12 verified component bindings, two finite model forwards, the recorded acceptance/abstention decisions and the same ordered top-10 token IDs. Hidden vectors and probabilities must agree with the CPU reference using `atol=2e-5, rtol=2e-5`; exact equality and maximum hidden differences are reported separately. This tolerance is an explicit smoke-test criterion, not a claim of historical CUDA bitwise replay. A mismatch fails the run.
42
+
43
+ ## What the loader preserves
44
+
45
+ The loader imports the byte-pinned R5 composite and MD factory from the released source tree. The factory receives the exact original composite class and module through the two attributes it uses from the training executor. It constructs the original `A111`/`A1111`, 2,114-token model with temporal size 128. It retains the unmodified production configuration validation, constructor authority checks, no-follow authority-file reads, tensor-surface validation and live model contract. It also explicitly rechecks the component authorities after strict checkpoint loading. `isolated_test_fixture` remains false.
46
+
47
+ The original R5 source pin matches the R5 executor's bound source. Its constructor independently validates the frozen R4 lineage, sequence vocabulary and tokenizer bindings, topology source and MS authority/receipt/certificate. The loader neither edits those checks nor changes their archival authorization flags. It does not invoke the training launcher, optimizer/planner authorities or the later static-release launch bootstrap. Those gates authorize a different operation: this entry point has no training path and supplies explicitly absent MS, static and MD carriers, as the executed IAR smoke did. Full checkpoint loading does not establish that active physical-modality inference is portable.
48
+
49
+ `original_inference_helpers.py` contains only eight verbatim function definitions from the final historical IAR source: the absent-carrier constructor, hook/capture utilities and forward helper, plus its failure function. Preflight compares their ASTs with that pinned source. The historical module itself is not imported because its top level changes `builtins.open` and requires a `WORK` environment variable. The saved `iar_completion/readout.py` is used unchanged. The full historical checkpoint uses `torch.load(..., weights_only=False)` only after its complete bytes match the mandatory SHA-256, through the same open file descriptor; do not replace this with untrusted checkpoint bytes.
50
+
51
+ ## Scope limits
52
+
53
+ - The runner accepts only the two byte-pinned rebuilt fixture cases. Their WURCS strings and helper records are provenance; raw WURCS is not converted at runtime. Moving the fixture files does not require regenerating them, so no input export helper is needed.
54
+ - General glycan completion, new masking cases, residue or multi-token fallback, corpus reconstruction and full historical metrics are outside this executable interface.
55
+ - The complete checkpoint is loaded, but the two forwards exercise sequence inference with MS, static and MD explicitly absent. Active carrier paths, CUDA and other checkpoints are untested here.
56
+ - A passing small-file preflight does not establish successful checkpoint loading or numerical inference. Only a completed full-run report does so for these two cases.
57
+
58
+ The public runner passed a fresh-extraction CPU execution in Nova job 16683971: full strict loading, both hidden vectors bitwise equal to the prior reference, and unchanged ordered predictions and abstention. The receipt is in `../provenance/portable_execution_20260930/iar_portable_report.json`.
releases/2026-10-02/iar_inference/SHA256SUMS ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ 8fc1d034d7b8a3d7c12cdfa958f1b5825be73cedabce8e96a83441e883bea0e7 README.md
2
+ 043a38744f9fd832181d822ae8481488d954604bf615b2eb05c4c8ad29794325 config.json
3
+ a212cfbd49ec2f951e942c92caaac63260687a626eca588cb692d86bb1e52198 fixtures/case0_pre_sequence_head.npy
4
+ 6ffca438526432a33168a85a0fee1158fd2a9c9f23ecb97f587235f204ae50b3 fixtures/case1_pre_sequence_head.npy
5
+ 60ac0c0fd1b6e0ada2d847d1fccff4f6fe7c085aab3146247dc5d7bfebbabc27 fixtures/rebuilt_two_case_inputs.json
6
+ 60dcdb6262457962da84c7b239c194fac343bd8d27db0e05f15906acb9bf5f4b fixtures/reference_smoke_report.json
7
+ 9237b349668ea3fb5266f4696169fd958e9f997e5064ede0ab06e41859b17361 infer.py
8
+ 87fa484e1f6105fa476dff4cf8c7e6aea71a2aac537796131a4858632f54b906 original_inference_helpers.py
9
+ 4e36f9237dc494eb47898ff3ab9a5b1a483f5004aa933a5294af794edd0757ce preparation_validation.json
10
+ 16e89b25a13eabc8586ba2a8ec32890ecf321f652b03f7548c63112d13cb2e62 requirements.txt
releases/2026-10-02/iar_inference/config.json ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "bertose_portable_two_case_iar_v1",
3
+ "source_root": "../bertose_source",
4
+ "readout_root": "../iar_completion",
5
+ "model_sources": {
6
+ "r5": "writing/review/multimodal/runtime/a5_pretraining_failclosed_integration_20260721_r5/review_a5_native_eight_composite_r5.py",
7
+ "md_core": "writing/review/multimodal/runtime/md_temporal_20x6_end_to_end_20260802_r3_native_intersection/review_md_temporal_20x6_core_r3.py"
8
+ },
9
+ "source_files": [
10
+ {
11
+ "path": "writing/review/multimodal/runtime/a5_pretraining_failclosed_integration_20260721_r4/review_a5_native_eight_composite_r4.py",
12
+ "bytes": 70358,
13
+ "sha256": "52a311636e7631b97711aab63aec28e2ada47a446d1aecc1a47921d584f69f9d"
14
+ },
15
+ {
16
+ "path": "writing/review/multimodal/runtime/a5_pretraining_failclosed_integration_20260721_r5/review_a5_native_eight_composite_r5.py",
17
+ "bytes": 58459,
18
+ "sha256": "24c35257fc61e1c9c77630d2b0075775a7f46b6500ecb5f175ef8f42bd214e0e"
19
+ },
20
+ {
21
+ "path": "writing/review/multimodal/runtime/md_temporal_20x6_end_to_end_20260802_r3_native_intersection/review_md_temporal_20x6_core_r3.py",
22
+ "bytes": 48643,
23
+ "sha256": "3e96d8092239c2c21f217ad5bae4ee7e07dbe0f7630b213bad468d6b08d3b1c1"
24
+ },
25
+ {
26
+ "path": "writing/review/multimodal/runtime/a5_pretraining_failclosed_integration_20260721_r5/review_a5_native_eight_r4_lineage_receipt_r1.json",
27
+ "bytes": 1716,
28
+ "sha256": "b5fbdff168de23676208484ccccc3cc32f613ea411d193e6d8930456e8edeeef"
29
+ },
30
+ {
31
+ "path": "writing/review/multimodal/outputs/review_a5_official_ragged_sequence_topology_runtime_20260721_r3/review_a5_topology_loss_v2.py",
32
+ "bytes": 8067,
33
+ "sha256": "64eaf9174f523ae909c84987049d071bf3869ca8e83919f14f0572ef77b0530e"
34
+ },
35
+ {
36
+ "path": "writing/review/multimodal/runtime/a5_ms_acquisition_top30_v3_20260721_r3/review_a5_ms_acquisition_top30_runtime_v3.py",
37
+ "bytes": 43711,
38
+ "sha256": "68c0d543c5411276ef56c7072bded4ebda886f46b9fa4ebf3c95c1e54c1a08c6"
39
+ },
40
+ {
41
+ "path": "data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_sequence_package_receipt.json",
42
+ "bytes": 5797,
43
+ "sha256": "399df3de2b6c4d5e88aa442c8326214ab9cff2e8f0411e6921762bbade1475ba"
44
+ },
45
+ {
46
+ "path": "data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_sequence_package_summary.json",
47
+ "bytes": 7777,
48
+ "sha256": "93ed219d807d4a7735be96bf7cf6a59a0bd407619925ee194aed6d473048cdf0"
49
+ },
50
+ {
51
+ "path": "data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
52
+ "bytes": 138150,
53
+ "sha256": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b"
54
+ },
55
+ {
56
+ "path": "data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_training_a5_wurcs_bpe.py",
57
+ "bytes": 14161,
58
+ "sha256": "6591b854962c278dc797981c942e114c18a26e97af43029c1d539bae3badb2f4"
59
+ },
60
+ {
61
+ "path": "data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_frozen_sequence_tree_runtime.py",
62
+ "bytes": 11405,
63
+ "sha256": "10ca4b121d2f22d7a64208e4f05172e7a756b653d0828c9dcc49dd8ea1a5ad04"
64
+ },
65
+ {
66
+ "path": "data/review_ms_acquisition_top30_runtime_20260721_r3/review_a5_ms_acquisition_top30_runtime_authority.json",
67
+ "bytes": 20541,
68
+ "sha256": "48914855c4e458ff02452ad4db3743a4fbef12221e0313584db4c2b1bd684cd4"
69
+ },
70
+ {
71
+ "path": "data/review_ms_acquisition_top30_runtime_20260721_r3/review_a5_ms_acquisition_top30_runtime_receipt.json",
72
+ "bytes": 6260,
73
+ "sha256": "f654177eb6fde7289b6755812c2be298084697cf0d85159b3ec9da310fe3d4b4"
74
+ },
75
+ {
76
+ "path": "data/review_ms_acquisition_top30_runtime_20260721_r3/review_a5_ms_acquisition_top30_runtime_independent_certificate.json",
77
+ "bytes": 19121,
78
+ "sha256": "53098620a5c22dafdc951ba743515b4b10b1673c02f0aa4d881db669584902fa"
79
+ }
80
+ ],
81
+ "readout_files": [
82
+ {
83
+ "path": "readout.py",
84
+ "bytes": 5515,
85
+ "sha256": "42464d76c743a302bae79ced6b09af9657444c6f977535b5de49dcf59a5a8a90"
86
+ },
87
+ {
88
+ "path": "model_metadata.json",
89
+ "bytes": 3132,
90
+ "sha256": "bbf016ce2f1a7dc82940be28397cbe8083da7ad0f5f525b1cf7345fd42a5758d"
91
+ },
92
+ {
93
+ "path": "assets/linkage_readout.pt",
94
+ "bytes": 663845,
95
+ "sha256": "a03990a02a1737de05a7fa1739164e84fa6a0a4170ebaa65317a9f3c2f16c0b0"
96
+ },
97
+ {
98
+ "path": "originals/wa3_final_iar_readout_all.py",
99
+ "bytes": 82622,
100
+ "sha256": "72f8583bf32799ba29c09829d138900c0cbb589f73bd278f389da6fa8a786730"
101
+ },
102
+ {
103
+ "path": "runtime_snapshot/project/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
104
+ "bytes": 138150,
105
+ "sha256": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b"
106
+ }
107
+ ],
108
+ "local_files": [
109
+ {
110
+ "path": "original_inference_helpers.py",
111
+ "bytes": 4608,
112
+ "sha256": "87fa484e1f6105fa476dff4cf8c7e6aea71a2aac537796131a4858632f54b906"
113
+ },
114
+ {
115
+ "path": "fixtures/case0_pre_sequence_head.npy",
116
+ "bytes": 3200,
117
+ "sha256": "a212cfbd49ec2f951e942c92caaac63260687a626eca588cb692d86bb1e52198"
118
+ },
119
+ {
120
+ "path": "fixtures/case1_pre_sequence_head.npy",
121
+ "bytes": 3200,
122
+ "sha256": "6ffca438526432a33168a85a0fee1158fd2a9c9f23ecb97f587235f204ae50b3"
123
+ },
124
+ {
125
+ "path": "fixtures/rebuilt_two_case_inputs.json",
126
+ "bytes": 42915,
127
+ "sha256": "60ac0c0fd1b6e0ada2d847d1fccff4f6fe7c085aab3146247dc5d7bfebbabc27"
128
+ },
129
+ {
130
+ "path": "fixtures/reference_smoke_report.json",
131
+ "bytes": 7532,
132
+ "sha256": "60dcdb6262457962da84c7b239c194fac343bd8d27db0e05f15906acb9bf5f4b"
133
+ }
134
+ ],
135
+ "original_helper_functions": [
136
+ "fail",
137
+ "all_absent",
138
+ "_linkage_hook",
139
+ "find_linkage_embedding",
140
+ "_cap_hook",
141
+ "find_lm_projection",
142
+ "_gather_h",
143
+ "_forward"
144
+ ],
145
+ "checkpoint": {
146
+ "sha256": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
147
+ "md_cell_id": "A1111",
148
+ "base_cell_id": "A111",
149
+ "best_epoch": 58,
150
+ "state_entries": 505,
151
+ "parameter_count": 138487995,
152
+ "vocabulary_size": 2114,
153
+ "md_temporal_size": 128
154
+ },
155
+ "inference": {
156
+ "device": "cpu",
157
+ "dtype": "float32",
158
+ "threshold": 0.8,
159
+ "maximum_rounds": 3,
160
+ "neutral_module": "sequence_embeddings.linkage_embeddings",
161
+ "hidden_module": "sequence_head",
162
+ "neutral_training_counts": [
163
+ 97966,
164
+ 150442,
165
+ 219104,
166
+ 95827,
167
+ 13486,
168
+ 11976,
169
+ 23,
170
+ 36422
171
+ ],
172
+ "hidden_allclose_atol": 2e-05,
173
+ "hidden_allclose_rtol": 2e-05,
174
+ "probability_allclose_atol": 2e-05,
175
+ "probability_allclose_rtol": 2e-05
176
+ },
177
+ "scope": "The two byte-pinned, official-helper-rebuilt single-linkage cases only; physical modalities explicitly absent. No refit, raw-WURCS conversion, fallback, corpus metrics or training authorization."
178
+ }
releases/2026-10-02/iar_inference/fixtures/case0_pre_sequence_head.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:a212cfbd49ec2f951e942c92caaac63260687a626eca588cb692d86bb1e52198
3
+ size 3200
releases/2026-10-02/iar_inference/fixtures/case1_pre_sequence_head.npy ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:6ffca438526432a33168a85a0fee1158fd2a9c9f23ecb97f587235f204ae50b3
3
+ size 3200
releases/2026-10-02/iar_inference/fixtures/rebuilt_two_case_inputs.json ADDED
@@ -0,0 +1,3718 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ [
2
+ {
3
+ "dataset": "bench1392",
4
+ "source_case_id": "g0000_s00_linkage_38_38",
5
+ "recorded_source_sha256": "124effa978c07bab00e7056c41d4c727a2a4c7d766d8d2a510c11a0fc52d3721",
6
+ "official_helper": {
7
+ "rooted": true,
8
+ "depths": [
9
+ 0,
10
+ 1,
11
+ 2,
12
+ 3,
13
+ 4,
14
+ 5,
15
+ 6,
16
+ 7,
17
+ 8,
18
+ 9
19
+ ],
20
+ "parents": [
21
+ -1,
22
+ 0,
23
+ 1,
24
+ 2,
25
+ 3,
26
+ 4,
27
+ 5,
28
+ 6,
29
+ 7,
30
+ 8
31
+ ],
32
+ "helper_error": false
33
+ },
34
+ "rebuilt_case": {
35
+ "case_id": "g0000_s00_linkage_38_38",
36
+ "glycan_index": 0,
37
+ "wurcs": "WURCS=2.0/2,10,9/[AUd21122h_5*NCC/3=O][Aad21122h-2a_2-6_5*NCC/3=O]/1-2-2-2-2-2-2-2-2-2/a8-b2_b8-c2_c8-d2_d8-e2_e8-f2_f8-g2_g8-h2_h8-i2_i8-j2",
38
+ "mask_plan": "linkage:1",
39
+ "units": [
40
+ {
41
+ "g": 35,
42
+ "unit_type": "linkage",
43
+ "unit_text": "g8-h2"
44
+ }
45
+ ],
46
+ "wa0_iterative_span_exact": true,
47
+ "wa0_initial_span_exact_top1": true,
48
+ "unit_text": "linkage:g8-h2",
49
+ "positions": [
50
+ 43
51
+ ],
52
+ "truth_ids": [
53
+ 1720
54
+ ],
55
+ "masked_ids": [
56
+ 2,
57
+ 316,
58
+ 225,
59
+ 254,
60
+ 208,
61
+ 225,
62
+ 254,
63
+ 208,
64
+ 225,
65
+ 254,
66
+ 208,
67
+ 225,
68
+ 254,
69
+ 208,
70
+ 225,
71
+ 254,
72
+ 208,
73
+ 225,
74
+ 254,
75
+ 208,
76
+ 225,
77
+ 254,
78
+ 208,
79
+ 225,
80
+ 254,
81
+ 208,
82
+ 225,
83
+ 254,
84
+ 208,
85
+ 225,
86
+ 1283,
87
+ 568,
88
+ 72,
89
+ 557,
90
+ 436,
91
+ 1484,
92
+ 2015,
93
+ 75,
94
+ 557,
95
+ 619,
96
+ 76,
97
+ 557,
98
+ 348,
99
+ 4,
100
+ 1380,
101
+ 79,
102
+ 557,
103
+ 585,
104
+ 3,
105
+ 0,
106
+ 0,
107
+ 0,
108
+ 0,
109
+ 0,
110
+ 0,
111
+ 0,
112
+ 0,
113
+ 0,
114
+ 0,
115
+ 0,
116
+ 0,
117
+ 0,
118
+ 0,
119
+ 0,
120
+ 0,
121
+ 0,
122
+ 0,
123
+ 0,
124
+ 0,
125
+ 0,
126
+ 0,
127
+ 0,
128
+ 0,
129
+ 0,
130
+ 0,
131
+ 0,
132
+ 0,
133
+ 0,
134
+ 0,
135
+ 0,
136
+ 0,
137
+ 0,
138
+ 0,
139
+ 0,
140
+ 0,
141
+ 0,
142
+ 0,
143
+ 0,
144
+ 0,
145
+ 0,
146
+ 0,
147
+ 0,
148
+ 0,
149
+ 0,
150
+ 0,
151
+ 0,
152
+ 0,
153
+ 0,
154
+ 0,
155
+ 0,
156
+ 0,
157
+ 0,
158
+ 0,
159
+ 0,
160
+ 0,
161
+ 0,
162
+ 0,
163
+ 0,
164
+ 0,
165
+ 0,
166
+ 0,
167
+ 0,
168
+ 0,
169
+ 0,
170
+ 0,
171
+ 0,
172
+ 0,
173
+ 0,
174
+ 0,
175
+ 0,
176
+ 0,
177
+ 0,
178
+ 0,
179
+ 0,
180
+ 0,
181
+ 0,
182
+ 0,
183
+ 0,
184
+ 0,
185
+ 0,
186
+ 0,
187
+ 0,
188
+ 0,
189
+ 0,
190
+ 0,
191
+ 0,
192
+ 0,
193
+ 0,
194
+ 0,
195
+ 0,
196
+ 0,
197
+ 0,
198
+ 0,
199
+ 0,
200
+ 0,
201
+ 0,
202
+ 0,
203
+ 0,
204
+ 0,
205
+ 0,
206
+ 0,
207
+ 0,
208
+ 0,
209
+ 0,
210
+ 0,
211
+ 0,
212
+ 0,
213
+ 0,
214
+ 0,
215
+ 0,
216
+ 0,
217
+ 0,
218
+ 0,
219
+ 0,
220
+ 0,
221
+ 0,
222
+ 0,
223
+ 0,
224
+ 0,
225
+ 0,
226
+ 0,
227
+ 0,
228
+ 0,
229
+ 0,
230
+ 0,
231
+ 0,
232
+ 0,
233
+ 0,
234
+ 0,
235
+ 0,
236
+ 0,
237
+ 0,
238
+ 0,
239
+ 0,
240
+ 0,
241
+ 0,
242
+ 0,
243
+ 0,
244
+ 0,
245
+ 0,
246
+ 0,
247
+ 0,
248
+ 0,
249
+ 0,
250
+ 0,
251
+ 0,
252
+ 0,
253
+ 0,
254
+ 0,
255
+ 0,
256
+ 0,
257
+ 0,
258
+ 0,
259
+ 0,
260
+ 0,
261
+ 0,
262
+ 0,
263
+ 0,
264
+ 0,
265
+ 0,
266
+ 0,
267
+ 0,
268
+ 0,
269
+ 0,
270
+ 0,
271
+ 0,
272
+ 0,
273
+ 0,
274
+ 0,
275
+ 0,
276
+ 0,
277
+ 0,
278
+ 0,
279
+ 0,
280
+ 0,
281
+ 0,
282
+ 0,
283
+ 0,
284
+ 0,
285
+ 0,
286
+ 0,
287
+ 0,
288
+ 0,
289
+ 0,
290
+ 0,
291
+ 0,
292
+ 0,
293
+ 0,
294
+ 0,
295
+ 0,
296
+ 0,
297
+ 0,
298
+ 0,
299
+ 0,
300
+ 0,
301
+ 0,
302
+ 0,
303
+ 0,
304
+ 0,
305
+ 0,
306
+ 0,
307
+ 0,
308
+ 0,
309
+ 0,
310
+ 0,
311
+ 0
312
+ ],
313
+ "attention_mask": [
314
+ 1,
315
+ 1,
316
+ 1,
317
+ 1,
318
+ 1,
319
+ 1,
320
+ 1,
321
+ 1,
322
+ 1,
323
+ 1,
324
+ 1,
325
+ 1,
326
+ 1,
327
+ 1,
328
+ 1,
329
+ 1,
330
+ 1,
331
+ 1,
332
+ 1,
333
+ 1,
334
+ 1,
335
+ 1,
336
+ 1,
337
+ 1,
338
+ 1,
339
+ 1,
340
+ 1,
341
+ 1,
342
+ 1,
343
+ 1,
344
+ 1,
345
+ 1,
346
+ 1,
347
+ 1,
348
+ 1,
349
+ 1,
350
+ 1,
351
+ 1,
352
+ 1,
353
+ 1,
354
+ 1,
355
+ 1,
356
+ 1,
357
+ 1,
358
+ 1,
359
+ 1,
360
+ 1,
361
+ 1,
362
+ 1,
363
+ 0,
364
+ 0,
365
+ 0,
366
+ 0,
367
+ 0,
368
+ 0,
369
+ 0,
370
+ 0,
371
+ 0,
372
+ 0,
373
+ 0,
374
+ 0,
375
+ 0,
376
+ 0,
377
+ 0,
378
+ 0,
379
+ 0,
380
+ 0,
381
+ 0,
382
+ 0,
383
+ 0,
384
+ 0,
385
+ 0,
386
+ 0,
387
+ 0,
388
+ 0,
389
+ 0,
390
+ 0,
391
+ 0,
392
+ 0,
393
+ 0,
394
+ 0,
395
+ 0,
396
+ 0,
397
+ 0,
398
+ 0,
399
+ 0,
400
+ 0,
401
+ 0,
402
+ 0,
403
+ 0,
404
+ 0,
405
+ 0,
406
+ 0,
407
+ 0,
408
+ 0,
409
+ 0,
410
+ 0,
411
+ 0,
412
+ 0,
413
+ 0,
414
+ 0,
415
+ 0,
416
+ 0,
417
+ 0,
418
+ 0,
419
+ 0,
420
+ 0,
421
+ 0,
422
+ 0,
423
+ 0,
424
+ 0,
425
+ 0,
426
+ 0,
427
+ 0,
428
+ 0,
429
+ 0,
430
+ 0,
431
+ 0,
432
+ 0,
433
+ 0,
434
+ 0,
435
+ 0,
436
+ 0,
437
+ 0,
438
+ 0,
439
+ 0,
440
+ 0,
441
+ 0,
442
+ 0,
443
+ 0,
444
+ 0,
445
+ 0,
446
+ 0,
447
+ 0,
448
+ 0,
449
+ 0,
450
+ 0,
451
+ 0,
452
+ 0,
453
+ 0,
454
+ 0,
455
+ 0,
456
+ 0,
457
+ 0,
458
+ 0,
459
+ 0,
460
+ 0,
461
+ 0,
462
+ 0,
463
+ 0,
464
+ 0,
465
+ 0,
466
+ 0,
467
+ 0,
468
+ 0,
469
+ 0,
470
+ 0,
471
+ 0,
472
+ 0,
473
+ 0,
474
+ 0,
475
+ 0,
476
+ 0,
477
+ 0,
478
+ 0,
479
+ 0,
480
+ 0,
481
+ 0,
482
+ 0,
483
+ 0,
484
+ 0,
485
+ 0,
486
+ 0,
487
+ 0,
488
+ 0,
489
+ 0,
490
+ 0,
491
+ 0,
492
+ 0,
493
+ 0,
494
+ 0,
495
+ 0,
496
+ 0,
497
+ 0,
498
+ 0,
499
+ 0,
500
+ 0,
501
+ 0,
502
+ 0,
503
+ 0,
504
+ 0,
505
+ 0,
506
+ 0,
507
+ 0,
508
+ 0,
509
+ 0,
510
+ 0,
511
+ 0,
512
+ 0,
513
+ 0,
514
+ 0,
515
+ 0,
516
+ 0,
517
+ 0,
518
+ 0,
519
+ 0,
520
+ 0,
521
+ 0,
522
+ 0,
523
+ 0,
524
+ 0,
525
+ 0,
526
+ 0,
527
+ 0,
528
+ 0,
529
+ 0,
530
+ 0,
531
+ 0,
532
+ 0,
533
+ 0,
534
+ 0,
535
+ 0,
536
+ 0,
537
+ 0,
538
+ 0,
539
+ 0,
540
+ 0,
541
+ 0,
542
+ 0,
543
+ 0,
544
+ 0,
545
+ 0,
546
+ 0,
547
+ 0,
548
+ 0,
549
+ 0,
550
+ 0,
551
+ 0,
552
+ 0,
553
+ 0,
554
+ 0,
555
+ 0,
556
+ 0,
557
+ 0,
558
+ 0,
559
+ 0,
560
+ 0,
561
+ 0,
562
+ 0,
563
+ 0,
564
+ 0,
565
+ 0,
566
+ 0,
567
+ 0,
568
+ 0,
569
+ 0
570
+ ],
571
+ "residue_ids": [
572
+ -1,
573
+ 0,
574
+ 0,
575
+ 1,
576
+ 1,
577
+ 1,
578
+ 2,
579
+ 2,
580
+ 2,
581
+ 3,
582
+ 3,
583
+ 3,
584
+ 4,
585
+ 4,
586
+ 4,
587
+ 5,
588
+ 5,
589
+ 5,
590
+ 6,
591
+ 6,
592
+ 6,
593
+ 7,
594
+ 7,
595
+ 7,
596
+ 8,
597
+ 8,
598
+ 8,
599
+ 9,
600
+ 9,
601
+ 9,
602
+ -2,
603
+ -2,
604
+ -2,
605
+ -2,
606
+ -2,
607
+ -2,
608
+ -2,
609
+ -2,
610
+ -2,
611
+ -2,
612
+ -2,
613
+ -2,
614
+ -2,
615
+ -2,
616
+ -2,
617
+ -2,
618
+ -2,
619
+ -2,
620
+ -1,
621
+ -1,
622
+ -1,
623
+ -1,
624
+ -1,
625
+ -1,
626
+ -1,
627
+ -1,
628
+ -1,
629
+ -1,
630
+ -1,
631
+ -1,
632
+ -1,
633
+ -1,
634
+ -1,
635
+ -1,
636
+ -1,
637
+ -1,
638
+ -1,
639
+ -1,
640
+ -1,
641
+ -1,
642
+ -1,
643
+ -1,
644
+ -1,
645
+ -1,
646
+ -1,
647
+ -1,
648
+ -1,
649
+ -1,
650
+ -1,
651
+ -1,
652
+ -1,
653
+ -1,
654
+ -1,
655
+ -1,
656
+ -1,
657
+ -1,
658
+ -1,
659
+ -1,
660
+ -1,
661
+ -1,
662
+ -1,
663
+ -1,
664
+ -1,
665
+ -1,
666
+ -1,
667
+ -1,
668
+ -1,
669
+ -1,
670
+ -1,
671
+ -1,
672
+ -1,
673
+ -1,
674
+ -1,
675
+ -1,
676
+ -1,
677
+ -1,
678
+ -1,
679
+ -1,
680
+ -1,
681
+ -1,
682
+ -1,
683
+ -1,
684
+ -1,
685
+ -1,
686
+ -1,
687
+ -1,
688
+ -1,
689
+ -1,
690
+ -1,
691
+ -1,
692
+ -1,
693
+ -1,
694
+ -1,
695
+ -1,
696
+ -1,
697
+ -1,
698
+ -1,
699
+ -1,
700
+ -1,
701
+ -1,
702
+ -1,
703
+ -1,
704
+ -1,
705
+ -1,
706
+ -1,
707
+ -1,
708
+ -1,
709
+ -1,
710
+ -1,
711
+ -1,
712
+ -1,
713
+ -1,
714
+ -1,
715
+ -1,
716
+ -1,
717
+ -1,
718
+ -1,
719
+ -1,
720
+ -1,
721
+ -1,
722
+ -1,
723
+ -1,
724
+ -1,
725
+ -1,
726
+ -1,
727
+ -1,
728
+ -1,
729
+ -1,
730
+ -1,
731
+ -1,
732
+ -1,
733
+ -1,
734
+ -1,
735
+ -1,
736
+ -1,
737
+ -1,
738
+ -1,
739
+ -1,
740
+ -1,
741
+ -1,
742
+ -1,
743
+ -1,
744
+ -1,
745
+ -1,
746
+ -1,
747
+ -1,
748
+ -1,
749
+ -1,
750
+ -1,
751
+ -1,
752
+ -1,
753
+ -1,
754
+ -1,
755
+ -1,
756
+ -1,
757
+ -1,
758
+ -1,
759
+ -1,
760
+ -1,
761
+ -1,
762
+ -1,
763
+ -1,
764
+ -1,
765
+ -1,
766
+ -1,
767
+ -1,
768
+ -1,
769
+ -1,
770
+ -1,
771
+ -1,
772
+ -1,
773
+ -1,
774
+ -1,
775
+ -1,
776
+ -1,
777
+ -1,
778
+ -1,
779
+ -1,
780
+ -1,
781
+ -1,
782
+ -1,
783
+ -1,
784
+ -1,
785
+ -1,
786
+ -1,
787
+ -1,
788
+ -1,
789
+ -1,
790
+ -1,
791
+ -1,
792
+ -1,
793
+ -1,
794
+ -1,
795
+ -1,
796
+ -1,
797
+ -1,
798
+ -1,
799
+ -1,
800
+ -1,
801
+ -1,
802
+ -1,
803
+ -1,
804
+ -1,
805
+ -1,
806
+ -1,
807
+ -1,
808
+ -1,
809
+ -1,
810
+ -1,
811
+ -1,
812
+ -1,
813
+ -1,
814
+ -1,
815
+ -1,
816
+ -1,
817
+ -1,
818
+ -1,
819
+ -1,
820
+ -1,
821
+ -1,
822
+ -1,
823
+ -1,
824
+ -1,
825
+ -1,
826
+ -1,
827
+ -1
828
+ ],
829
+ "branch_depths": [
830
+ 0,
831
+ 0,
832
+ 0,
833
+ 1,
834
+ 1,
835
+ 1,
836
+ 2,
837
+ 2,
838
+ 2,
839
+ 3,
840
+ 3,
841
+ 3,
842
+ 4,
843
+ 4,
844
+ 4,
845
+ 5,
846
+ 5,
847
+ 5,
848
+ 6,
849
+ 6,
850
+ 6,
851
+ 7,
852
+ 7,
853
+ 7,
854
+ 8,
855
+ 8,
856
+ 8,
857
+ 9,
858
+ 9,
859
+ 9,
860
+ 0,
861
+ 0,
862
+ 0,
863
+ 0,
864
+ 0,
865
+ 0,
866
+ 0,
867
+ 0,
868
+ 0,
869
+ 0,
870
+ 0,
871
+ 0,
872
+ 0,
873
+ 0,
874
+ 0,
875
+ 0,
876
+ 0,
877
+ 0,
878
+ 0,
879
+ 0,
880
+ 0,
881
+ 0,
882
+ 0,
883
+ 0,
884
+ 0,
885
+ 0,
886
+ 0,
887
+ 0,
888
+ 0,
889
+ 0,
890
+ 0,
891
+ 0,
892
+ 0,
893
+ 0,
894
+ 0,
895
+ 0,
896
+ 0,
897
+ 0,
898
+ 0,
899
+ 0,
900
+ 0,
901
+ 0,
902
+ 0,
903
+ 0,
904
+ 0,
905
+ 0,
906
+ 0,
907
+ 0,
908
+ 0,
909
+ 0,
910
+ 0,
911
+ 0,
912
+ 0,
913
+ 0,
914
+ 0,
915
+ 0,
916
+ 0,
917
+ 0,
918
+ 0,
919
+ 0,
920
+ 0,
921
+ 0,
922
+ 0,
923
+ 0,
924
+ 0,
925
+ 0,
926
+ 0,
927
+ 0,
928
+ 0,
929
+ 0,
930
+ 0,
931
+ 0,
932
+ 0,
933
+ 0,
934
+ 0,
935
+ 0,
936
+ 0,
937
+ 0,
938
+ 0,
939
+ 0,
940
+ 0,
941
+ 0,
942
+ 0,
943
+ 0,
944
+ 0,
945
+ 0,
946
+ 0,
947
+ 0,
948
+ 0,
949
+ 0,
950
+ 0,
951
+ 0,
952
+ 0,
953
+ 0,
954
+ 0,
955
+ 0,
956
+ 0,
957
+ 0,
958
+ 0,
959
+ 0,
960
+ 0,
961
+ 0,
962
+ 0,
963
+ 0,
964
+ 0,
965
+ 0,
966
+ 0,
967
+ 0,
968
+ 0,
969
+ 0,
970
+ 0,
971
+ 0,
972
+ 0,
973
+ 0,
974
+ 0,
975
+ 0,
976
+ 0,
977
+ 0,
978
+ 0,
979
+ 0,
980
+ 0,
981
+ 0,
982
+ 0,
983
+ 0,
984
+ 0,
985
+ 0,
986
+ 0,
987
+ 0,
988
+ 0,
989
+ 0,
990
+ 0,
991
+ 0,
992
+ 0,
993
+ 0,
994
+ 0,
995
+ 0,
996
+ 0,
997
+ 0,
998
+ 0,
999
+ 0,
1000
+ 0,
1001
+ 0,
1002
+ 0,
1003
+ 0,
1004
+ 0,
1005
+ 0,
1006
+ 0,
1007
+ 0,
1008
+ 0,
1009
+ 0,
1010
+ 0,
1011
+ 0,
1012
+ 0,
1013
+ 0,
1014
+ 0,
1015
+ 0,
1016
+ 0,
1017
+ 0,
1018
+ 0,
1019
+ 0,
1020
+ 0,
1021
+ 0,
1022
+ 0,
1023
+ 0,
1024
+ 0,
1025
+ 0,
1026
+ 0,
1027
+ 0,
1028
+ 0,
1029
+ 0,
1030
+ 0,
1031
+ 0,
1032
+ 0,
1033
+ 0,
1034
+ 0,
1035
+ 0,
1036
+ 0,
1037
+ 0,
1038
+ 0,
1039
+ 0,
1040
+ 0,
1041
+ 0,
1042
+ 0,
1043
+ 0,
1044
+ 0,
1045
+ 0,
1046
+ 0,
1047
+ 0,
1048
+ 0,
1049
+ 0,
1050
+ 0,
1051
+ 0,
1052
+ 0,
1053
+ 0,
1054
+ 0,
1055
+ 0,
1056
+ 0,
1057
+ 0,
1058
+ 0,
1059
+ 0,
1060
+ 0,
1061
+ 0,
1062
+ 0,
1063
+ 0,
1064
+ 0,
1065
+ 0,
1066
+ 0,
1067
+ 0,
1068
+ 0,
1069
+ 0,
1070
+ 0,
1071
+ 0,
1072
+ 0,
1073
+ 0,
1074
+ 0,
1075
+ 0,
1076
+ 0,
1077
+ 0,
1078
+ 0,
1079
+ 0,
1080
+ 0,
1081
+ 0,
1082
+ 0,
1083
+ 0,
1084
+ 0,
1085
+ 0
1086
+ ],
1087
+ "linkage_types": [
1088
+ 0,
1089
+ 0,
1090
+ 0,
1091
+ 0,
1092
+ 0,
1093
+ 0,
1094
+ 0,
1095
+ 0,
1096
+ 0,
1097
+ 0,
1098
+ 0,
1099
+ 0,
1100
+ 0,
1101
+ 0,
1102
+ 0,
1103
+ 0,
1104
+ 0,
1105
+ 0,
1106
+ 0,
1107
+ 0,
1108
+ 0,
1109
+ 0,
1110
+ 0,
1111
+ 0,
1112
+ 0,
1113
+ 0,
1114
+ 0,
1115
+ 0,
1116
+ 0,
1117
+ 0,
1118
+ 8,
1119
+ 8,
1120
+ 8,
1121
+ 8,
1122
+ 8,
1123
+ 8,
1124
+ 8,
1125
+ 8,
1126
+ 8,
1127
+ 8,
1128
+ 8,
1129
+ 8,
1130
+ 8,
1131
+ 8,
1132
+ 8,
1133
+ 8,
1134
+ 8,
1135
+ 8,
1136
+ 0,
1137
+ 0,
1138
+ 0,
1139
+ 0,
1140
+ 0,
1141
+ 0,
1142
+ 0,
1143
+ 0,
1144
+ 0,
1145
+ 0,
1146
+ 0,
1147
+ 0,
1148
+ 0,
1149
+ 0,
1150
+ 0,
1151
+ 0,
1152
+ 0,
1153
+ 0,
1154
+ 0,
1155
+ 0,
1156
+ 0,
1157
+ 0,
1158
+ 0,
1159
+ 0,
1160
+ 0,
1161
+ 0,
1162
+ 0,
1163
+ 0,
1164
+ 0,
1165
+ 0,
1166
+ 0,
1167
+ 0,
1168
+ 0,
1169
+ 0,
1170
+ 0,
1171
+ 0,
1172
+ 0,
1173
+ 0,
1174
+ 0,
1175
+ 0,
1176
+ 0,
1177
+ 0,
1178
+ 0,
1179
+ 0,
1180
+ 0,
1181
+ 0,
1182
+ 0,
1183
+ 0,
1184
+ 0,
1185
+ 0,
1186
+ 0,
1187
+ 0,
1188
+ 0,
1189
+ 0,
1190
+ 0,
1191
+ 0,
1192
+ 0,
1193
+ 0,
1194
+ 0,
1195
+ 0,
1196
+ 0,
1197
+ 0,
1198
+ 0,
1199
+ 0,
1200
+ 0,
1201
+ 0,
1202
+ 0,
1203
+ 0,
1204
+ 0,
1205
+ 0,
1206
+ 0,
1207
+ 0,
1208
+ 0,
1209
+ 0,
1210
+ 0,
1211
+ 0,
1212
+ 0,
1213
+ 0,
1214
+ 0,
1215
+ 0,
1216
+ 0,
1217
+ 0,
1218
+ 0,
1219
+ 0,
1220
+ 0,
1221
+ 0,
1222
+ 0,
1223
+ 0,
1224
+ 0,
1225
+ 0,
1226
+ 0,
1227
+ 0,
1228
+ 0,
1229
+ 0,
1230
+ 0,
1231
+ 0,
1232
+ 0,
1233
+ 0,
1234
+ 0,
1235
+ 0,
1236
+ 0,
1237
+ 0,
1238
+ 0,
1239
+ 0,
1240
+ 0,
1241
+ 0,
1242
+ 0,
1243
+ 0,
1244
+ 0,
1245
+ 0,
1246
+ 0,
1247
+ 0,
1248
+ 0,
1249
+ 0,
1250
+ 0,
1251
+ 0,
1252
+ 0,
1253
+ 0,
1254
+ 0,
1255
+ 0,
1256
+ 0,
1257
+ 0,
1258
+ 0,
1259
+ 0,
1260
+ 0,
1261
+ 0,
1262
+ 0,
1263
+ 0,
1264
+ 0,
1265
+ 0,
1266
+ 0,
1267
+ 0,
1268
+ 0,
1269
+ 0,
1270
+ 0,
1271
+ 0,
1272
+ 0,
1273
+ 0,
1274
+ 0,
1275
+ 0,
1276
+ 0,
1277
+ 0,
1278
+ 0,
1279
+ 0,
1280
+ 0,
1281
+ 0,
1282
+ 0,
1283
+ 0,
1284
+ 0,
1285
+ 0,
1286
+ 0,
1287
+ 0,
1288
+ 0,
1289
+ 0,
1290
+ 0,
1291
+ 0,
1292
+ 0,
1293
+ 0,
1294
+ 0,
1295
+ 0,
1296
+ 0,
1297
+ 0,
1298
+ 0,
1299
+ 0,
1300
+ 0,
1301
+ 0,
1302
+ 0,
1303
+ 0,
1304
+ 0,
1305
+ 0,
1306
+ 0,
1307
+ 0,
1308
+ 0,
1309
+ 0,
1310
+ 0,
1311
+ 0,
1312
+ 0,
1313
+ 0,
1314
+ 0,
1315
+ 0,
1316
+ 0,
1317
+ 0,
1318
+ 0,
1319
+ 0,
1320
+ 0,
1321
+ 0,
1322
+ 0,
1323
+ 0,
1324
+ 0,
1325
+ 0,
1326
+ 0,
1327
+ 0,
1328
+ 0,
1329
+ 0,
1330
+ 0,
1331
+ 0,
1332
+ 0,
1333
+ 0,
1334
+ 0,
1335
+ 0,
1336
+ 0,
1337
+ 0,
1338
+ 0,
1339
+ 0,
1340
+ 0,
1341
+ 0,
1342
+ 0,
1343
+ 0
1344
+ ],
1345
+ "branch_depths_blank": [
1346
+ 0,
1347
+ 0,
1348
+ 0,
1349
+ 1,
1350
+ 1,
1351
+ 1,
1352
+ 2,
1353
+ 2,
1354
+ 2,
1355
+ 3,
1356
+ 3,
1357
+ 3,
1358
+ 4,
1359
+ 4,
1360
+ 4,
1361
+ 5,
1362
+ 5,
1363
+ 5,
1364
+ 6,
1365
+ 6,
1366
+ 6,
1367
+ 7,
1368
+ 7,
1369
+ 7,
1370
+ 8,
1371
+ 8,
1372
+ 8,
1373
+ 9,
1374
+ 9,
1375
+ 9,
1376
+ 0,
1377
+ 0,
1378
+ 0,
1379
+ 0,
1380
+ 0,
1381
+ 0,
1382
+ 0,
1383
+ 0,
1384
+ 0,
1385
+ 0,
1386
+ 0,
1387
+ 0,
1388
+ 0,
1389
+ 0,
1390
+ 0,
1391
+ 0,
1392
+ 0,
1393
+ 0,
1394
+ 0,
1395
+ 0,
1396
+ 0,
1397
+ 0,
1398
+ 0,
1399
+ 0,
1400
+ 0,
1401
+ 0,
1402
+ 0,
1403
+ 0,
1404
+ 0,
1405
+ 0,
1406
+ 0,
1407
+ 0,
1408
+ 0,
1409
+ 0,
1410
+ 0,
1411
+ 0,
1412
+ 0,
1413
+ 0,
1414
+ 0,
1415
+ 0,
1416
+ 0,
1417
+ 0,
1418
+ 0,
1419
+ 0,
1420
+ 0,
1421
+ 0,
1422
+ 0,
1423
+ 0,
1424
+ 0,
1425
+ 0,
1426
+ 0,
1427
+ 0,
1428
+ 0,
1429
+ 0,
1430
+ 0,
1431
+ 0,
1432
+ 0,
1433
+ 0,
1434
+ 0,
1435
+ 0,
1436
+ 0,
1437
+ 0,
1438
+ 0,
1439
+ 0,
1440
+ 0,
1441
+ 0,
1442
+ 0,
1443
+ 0,
1444
+ 0,
1445
+ 0,
1446
+ 0,
1447
+ 0,
1448
+ 0,
1449
+ 0,
1450
+ 0,
1451
+ 0,
1452
+ 0,
1453
+ 0,
1454
+ 0,
1455
+ 0,
1456
+ 0,
1457
+ 0,
1458
+ 0,
1459
+ 0,
1460
+ 0,
1461
+ 0,
1462
+ 0,
1463
+ 0,
1464
+ 0,
1465
+ 0,
1466
+ 0,
1467
+ 0,
1468
+ 0,
1469
+ 0,
1470
+ 0,
1471
+ 0,
1472
+ 0,
1473
+ 0,
1474
+ 0,
1475
+ 0,
1476
+ 0,
1477
+ 0,
1478
+ 0,
1479
+ 0,
1480
+ 0,
1481
+ 0,
1482
+ 0,
1483
+ 0,
1484
+ 0,
1485
+ 0,
1486
+ 0,
1487
+ 0,
1488
+ 0,
1489
+ 0,
1490
+ 0,
1491
+ 0,
1492
+ 0,
1493
+ 0,
1494
+ 0,
1495
+ 0,
1496
+ 0,
1497
+ 0,
1498
+ 0,
1499
+ 0,
1500
+ 0,
1501
+ 0,
1502
+ 0,
1503
+ 0,
1504
+ 0,
1505
+ 0,
1506
+ 0,
1507
+ 0,
1508
+ 0,
1509
+ 0,
1510
+ 0,
1511
+ 0,
1512
+ 0,
1513
+ 0,
1514
+ 0,
1515
+ 0,
1516
+ 0,
1517
+ 0,
1518
+ 0,
1519
+ 0,
1520
+ 0,
1521
+ 0,
1522
+ 0,
1523
+ 0,
1524
+ 0,
1525
+ 0,
1526
+ 0,
1527
+ 0,
1528
+ 0,
1529
+ 0,
1530
+ 0,
1531
+ 0,
1532
+ 0,
1533
+ 0,
1534
+ 0,
1535
+ 0,
1536
+ 0,
1537
+ 0,
1538
+ 0,
1539
+ 0,
1540
+ 0,
1541
+ 0,
1542
+ 0,
1543
+ 0,
1544
+ 0,
1545
+ 0,
1546
+ 0,
1547
+ 0,
1548
+ 0,
1549
+ 0,
1550
+ 0,
1551
+ 0,
1552
+ 0,
1553
+ 0,
1554
+ 0,
1555
+ 0,
1556
+ 0,
1557
+ 0,
1558
+ 0,
1559
+ 0,
1560
+ 0,
1561
+ 0,
1562
+ 0,
1563
+ 0,
1564
+ 0,
1565
+ 0,
1566
+ 0,
1567
+ 0,
1568
+ 0,
1569
+ 0,
1570
+ 0,
1571
+ 0,
1572
+ 0,
1573
+ 0,
1574
+ 0,
1575
+ 0,
1576
+ 0,
1577
+ 0,
1578
+ 0,
1579
+ 0,
1580
+ 0,
1581
+ 0,
1582
+ 0,
1583
+ 0,
1584
+ 0,
1585
+ 0,
1586
+ 0,
1587
+ 0,
1588
+ 0,
1589
+ 0,
1590
+ 0,
1591
+ 0,
1592
+ 0,
1593
+ 0,
1594
+ 0,
1595
+ 0,
1596
+ 0,
1597
+ 0,
1598
+ 0,
1599
+ 0,
1600
+ 0,
1601
+ 0
1602
+ ],
1603
+ "linkage_types_blank": [
1604
+ 0,
1605
+ 0,
1606
+ 0,
1607
+ 0,
1608
+ 0,
1609
+ 0,
1610
+ 0,
1611
+ 0,
1612
+ 0,
1613
+ 0,
1614
+ 0,
1615
+ 0,
1616
+ 0,
1617
+ 0,
1618
+ 0,
1619
+ 0,
1620
+ 0,
1621
+ 0,
1622
+ 0,
1623
+ 0,
1624
+ 0,
1625
+ 0,
1626
+ 0,
1627
+ 0,
1628
+ 0,
1629
+ 0,
1630
+ 0,
1631
+ 0,
1632
+ 0,
1633
+ 0,
1634
+ 8,
1635
+ 8,
1636
+ 8,
1637
+ 8,
1638
+ 8,
1639
+ 8,
1640
+ 8,
1641
+ 8,
1642
+ 8,
1643
+ 8,
1644
+ 8,
1645
+ 8,
1646
+ 8,
1647
+ 9,
1648
+ 8,
1649
+ 8,
1650
+ 8,
1651
+ 8,
1652
+ 0,
1653
+ 0,
1654
+ 0,
1655
+ 0,
1656
+ 0,
1657
+ 0,
1658
+ 0,
1659
+ 0,
1660
+ 0,
1661
+ 0,
1662
+ 0,
1663
+ 0,
1664
+ 0,
1665
+ 0,
1666
+ 0,
1667
+ 0,
1668
+ 0,
1669
+ 0,
1670
+ 0,
1671
+ 0,
1672
+ 0,
1673
+ 0,
1674
+ 0,
1675
+ 0,
1676
+ 0,
1677
+ 0,
1678
+ 0,
1679
+ 0,
1680
+ 0,
1681
+ 0,
1682
+ 0,
1683
+ 0,
1684
+ 0,
1685
+ 0,
1686
+ 0,
1687
+ 0,
1688
+ 0,
1689
+ 0,
1690
+ 0,
1691
+ 0,
1692
+ 0,
1693
+ 0,
1694
+ 0,
1695
+ 0,
1696
+ 0,
1697
+ 0,
1698
+ 0,
1699
+ 0,
1700
+ 0,
1701
+ 0,
1702
+ 0,
1703
+ 0,
1704
+ 0,
1705
+ 0,
1706
+ 0,
1707
+ 0,
1708
+ 0,
1709
+ 0,
1710
+ 0,
1711
+ 0,
1712
+ 0,
1713
+ 0,
1714
+ 0,
1715
+ 0,
1716
+ 0,
1717
+ 0,
1718
+ 0,
1719
+ 0,
1720
+ 0,
1721
+ 0,
1722
+ 0,
1723
+ 0,
1724
+ 0,
1725
+ 0,
1726
+ 0,
1727
+ 0,
1728
+ 0,
1729
+ 0,
1730
+ 0,
1731
+ 0,
1732
+ 0,
1733
+ 0,
1734
+ 0,
1735
+ 0,
1736
+ 0,
1737
+ 0,
1738
+ 0,
1739
+ 0,
1740
+ 0,
1741
+ 0,
1742
+ 0,
1743
+ 0,
1744
+ 0,
1745
+ 0,
1746
+ 0,
1747
+ 0,
1748
+ 0,
1749
+ 0,
1750
+ 0,
1751
+ 0,
1752
+ 0,
1753
+ 0,
1754
+ 0,
1755
+ 0,
1756
+ 0,
1757
+ 0,
1758
+ 0,
1759
+ 0,
1760
+ 0,
1761
+ 0,
1762
+ 0,
1763
+ 0,
1764
+ 0,
1765
+ 0,
1766
+ 0,
1767
+ 0,
1768
+ 0,
1769
+ 0,
1770
+ 0,
1771
+ 0,
1772
+ 0,
1773
+ 0,
1774
+ 0,
1775
+ 0,
1776
+ 0,
1777
+ 0,
1778
+ 0,
1779
+ 0,
1780
+ 0,
1781
+ 0,
1782
+ 0,
1783
+ 0,
1784
+ 0,
1785
+ 0,
1786
+ 0,
1787
+ 0,
1788
+ 0,
1789
+ 0,
1790
+ 0,
1791
+ 0,
1792
+ 0,
1793
+ 0,
1794
+ 0,
1795
+ 0,
1796
+ 0,
1797
+ 0,
1798
+ 0,
1799
+ 0,
1800
+ 0,
1801
+ 0,
1802
+ 0,
1803
+ 0,
1804
+ 0,
1805
+ 0,
1806
+ 0,
1807
+ 0,
1808
+ 0,
1809
+ 0,
1810
+ 0,
1811
+ 0,
1812
+ 0,
1813
+ 0,
1814
+ 0,
1815
+ 0,
1816
+ 0,
1817
+ 0,
1818
+ 0,
1819
+ 0,
1820
+ 0,
1821
+ 0,
1822
+ 0,
1823
+ 0,
1824
+ 0,
1825
+ 0,
1826
+ 0,
1827
+ 0,
1828
+ 0,
1829
+ 0,
1830
+ 0,
1831
+ 0,
1832
+ 0,
1833
+ 0,
1834
+ 0,
1835
+ 0,
1836
+ 0,
1837
+ 0,
1838
+ 0,
1839
+ 0,
1840
+ 0,
1841
+ 0,
1842
+ 0,
1843
+ 0,
1844
+ 0,
1845
+ 0,
1846
+ 0,
1847
+ 0,
1848
+ 0,
1849
+ 0,
1850
+ 0,
1851
+ 0,
1852
+ 0,
1853
+ 0,
1854
+ 0,
1855
+ 0,
1856
+ 0,
1857
+ 0,
1858
+ 0,
1859
+ 0
1860
+ ],
1861
+ "enumerated_alternative": false,
1862
+ "unknown_question_mark": false,
1863
+ "wa3_truth_uses_invalid_token": false,
1864
+ "single_answer": true
1865
+ }
1866
+ },
1867
+ {
1868
+ "dataset": "seal162",
1869
+ "source_case_id": "g0000_s00_linkage_9_9",
1870
+ "recorded_source_sha256": "86b08c4b8c5081ee90eef3401ae5b0a09595c301f6c79f3d892521b3ea3e33d1",
1871
+ "official_helper": {
1872
+ "rooted": true,
1873
+ "depths": [
1874
+ 0,
1875
+ 1,
1876
+ 2
1877
+ ],
1878
+ "parents": [
1879
+ -1,
1880
+ 0,
1881
+ 1
1882
+ ],
1883
+ "helper_error": false
1884
+ },
1885
+ "rebuilt_case": {
1886
+ "case_id": "g0000_s00_linkage_9_9",
1887
+ "glycan_index": 0,
1888
+ "wurcs": "WURCS=2.0/3,3,2/[h2122h_2*NCC/3=O][a2112h-1b_1-5][a2122h-1b_1-5_2*NCC/3=O_6*OSO/3=O/3=O]/1-2-3/a4-b1_b3-c1",
1889
+ "mask_plan": "linkage:1",
1890
+ "units": [
1891
+ {
1892
+ "g": 8,
1893
+ "unit_type": "linkage",
1894
+ "unit_text": "a4-b1"
1895
+ }
1896
+ ],
1897
+ "wa0_iterative_span_exact": true,
1898
+ "wa0_initial_span_exact_top1": true,
1899
+ "unit_text": "linkage:a4-b1",
1900
+ "positions": [
1901
+ 9
1902
+ ],
1903
+ "truth_ids": [
1904
+ 228
1905
+ ],
1906
+ "masked_ids": [
1907
+ 2,
1908
+ 525,
1909
+ 139,
1910
+ 190,
1911
+ 119,
1912
+ 177,
1913
+ 119,
1914
+ 139,
1915
+ 333,
1916
+ 4,
1917
+ 291,
1918
+ 3,
1919
+ 0,
1920
+ 0,
1921
+ 0,
1922
+ 0,
1923
+ 0,
1924
+ 0,
1925
+ 0,
1926
+ 0,
1927
+ 0,
1928
+ 0,
1929
+ 0,
1930
+ 0,
1931
+ 0,
1932
+ 0,
1933
+ 0,
1934
+ 0,
1935
+ 0,
1936
+ 0,
1937
+ 0,
1938
+ 0,
1939
+ 0,
1940
+ 0,
1941
+ 0,
1942
+ 0,
1943
+ 0,
1944
+ 0,
1945
+ 0,
1946
+ 0,
1947
+ 0,
1948
+ 0,
1949
+ 0,
1950
+ 0,
1951
+ 0,
1952
+ 0,
1953
+ 0,
1954
+ 0,
1955
+ 0,
1956
+ 0,
1957
+ 0,
1958
+ 0,
1959
+ 0,
1960
+ 0,
1961
+ 0,
1962
+ 0,
1963
+ 0,
1964
+ 0,
1965
+ 0,
1966
+ 0,
1967
+ 0,
1968
+ 0,
1969
+ 0,
1970
+ 0,
1971
+ 0,
1972
+ 0,
1973
+ 0,
1974
+ 0,
1975
+ 0,
1976
+ 0,
1977
+ 0,
1978
+ 0,
1979
+ 0,
1980
+ 0,
1981
+ 0,
1982
+ 0,
1983
+ 0,
1984
+ 0,
1985
+ 0,
1986
+ 0,
1987
+ 0,
1988
+ 0,
1989
+ 0,
1990
+ 0,
1991
+ 0,
1992
+ 0,
1993
+ 0,
1994
+ 0,
1995
+ 0,
1996
+ 0,
1997
+ 0,
1998
+ 0,
1999
+ 0,
2000
+ 0,
2001
+ 0,
2002
+ 0,
2003
+ 0,
2004
+ 0,
2005
+ 0,
2006
+ 0,
2007
+ 0,
2008
+ 0,
2009
+ 0,
2010
+ 0,
2011
+ 0,
2012
+ 0,
2013
+ 0,
2014
+ 0,
2015
+ 0,
2016
+ 0,
2017
+ 0,
2018
+ 0,
2019
+ 0,
2020
+ 0,
2021
+ 0,
2022
+ 0,
2023
+ 0,
2024
+ 0,
2025
+ 0,
2026
+ 0,
2027
+ 0,
2028
+ 0,
2029
+ 0,
2030
+ 0,
2031
+ 0,
2032
+ 0,
2033
+ 0,
2034
+ 0,
2035
+ 0,
2036
+ 0,
2037
+ 0,
2038
+ 0,
2039
+ 0,
2040
+ 0,
2041
+ 0,
2042
+ 0,
2043
+ 0,
2044
+ 0,
2045
+ 0,
2046
+ 0,
2047
+ 0,
2048
+ 0,
2049
+ 0,
2050
+ 0,
2051
+ 0,
2052
+ 0,
2053
+ 0,
2054
+ 0,
2055
+ 0,
2056
+ 0,
2057
+ 0,
2058
+ 0,
2059
+ 0,
2060
+ 0,
2061
+ 0,
2062
+ 0,
2063
+ 0,
2064
+ 0,
2065
+ 0,
2066
+ 0,
2067
+ 0,
2068
+ 0,
2069
+ 0,
2070
+ 0,
2071
+ 0,
2072
+ 0,
2073
+ 0,
2074
+ 0,
2075
+ 0,
2076
+ 0,
2077
+ 0,
2078
+ 0,
2079
+ 0,
2080
+ 0,
2081
+ 0,
2082
+ 0,
2083
+ 0,
2084
+ 0,
2085
+ 0,
2086
+ 0,
2087
+ 0,
2088
+ 0,
2089
+ 0,
2090
+ 0,
2091
+ 0,
2092
+ 0,
2093
+ 0,
2094
+ 0,
2095
+ 0,
2096
+ 0,
2097
+ 0,
2098
+ 0,
2099
+ 0,
2100
+ 0,
2101
+ 0,
2102
+ 0,
2103
+ 0,
2104
+ 0,
2105
+ 0,
2106
+ 0,
2107
+ 0,
2108
+ 0,
2109
+ 0,
2110
+ 0,
2111
+ 0,
2112
+ 0,
2113
+ 0,
2114
+ 0,
2115
+ 0,
2116
+ 0,
2117
+ 0,
2118
+ 0,
2119
+ 0,
2120
+ 0,
2121
+ 0,
2122
+ 0,
2123
+ 0,
2124
+ 0,
2125
+ 0,
2126
+ 0,
2127
+ 0,
2128
+ 0,
2129
+ 0,
2130
+ 0,
2131
+ 0,
2132
+ 0,
2133
+ 0,
2134
+ 0,
2135
+ 0,
2136
+ 0,
2137
+ 0,
2138
+ 0,
2139
+ 0,
2140
+ 0,
2141
+ 0,
2142
+ 0,
2143
+ 0,
2144
+ 0,
2145
+ 0,
2146
+ 0,
2147
+ 0,
2148
+ 0,
2149
+ 0,
2150
+ 0,
2151
+ 0,
2152
+ 0,
2153
+ 0,
2154
+ 0,
2155
+ 0,
2156
+ 0,
2157
+ 0,
2158
+ 0,
2159
+ 0,
2160
+ 0,
2161
+ 0,
2162
+ 0
2163
+ ],
2164
+ "attention_mask": [
2165
+ 1,
2166
+ 1,
2167
+ 1,
2168
+ 1,
2169
+ 1,
2170
+ 1,
2171
+ 1,
2172
+ 1,
2173
+ 1,
2174
+ 1,
2175
+ 1,
2176
+ 1,
2177
+ 0,
2178
+ 0,
2179
+ 0,
2180
+ 0,
2181
+ 0,
2182
+ 0,
2183
+ 0,
2184
+ 0,
2185
+ 0,
2186
+ 0,
2187
+ 0,
2188
+ 0,
2189
+ 0,
2190
+ 0,
2191
+ 0,
2192
+ 0,
2193
+ 0,
2194
+ 0,
2195
+ 0,
2196
+ 0,
2197
+ 0,
2198
+ 0,
2199
+ 0,
2200
+ 0,
2201
+ 0,
2202
+ 0,
2203
+ 0,
2204
+ 0,
2205
+ 0,
2206
+ 0,
2207
+ 0,
2208
+ 0,
2209
+ 0,
2210
+ 0,
2211
+ 0,
2212
+ 0,
2213
+ 0,
2214
+ 0,
2215
+ 0,
2216
+ 0,
2217
+ 0,
2218
+ 0,
2219
+ 0,
2220
+ 0,
2221
+ 0,
2222
+ 0,
2223
+ 0,
2224
+ 0,
2225
+ 0,
2226
+ 0,
2227
+ 0,
2228
+ 0,
2229
+ 0,
2230
+ 0,
2231
+ 0,
2232
+ 0,
2233
+ 0,
2234
+ 0,
2235
+ 0,
2236
+ 0,
2237
+ 0,
2238
+ 0,
2239
+ 0,
2240
+ 0,
2241
+ 0,
2242
+ 0,
2243
+ 0,
2244
+ 0,
2245
+ 0,
2246
+ 0,
2247
+ 0,
2248
+ 0,
2249
+ 0,
2250
+ 0,
2251
+ 0,
2252
+ 0,
2253
+ 0,
2254
+ 0,
2255
+ 0,
2256
+ 0,
2257
+ 0,
2258
+ 0,
2259
+ 0,
2260
+ 0,
2261
+ 0,
2262
+ 0,
2263
+ 0,
2264
+ 0,
2265
+ 0,
2266
+ 0,
2267
+ 0,
2268
+ 0,
2269
+ 0,
2270
+ 0,
2271
+ 0,
2272
+ 0,
2273
+ 0,
2274
+ 0,
2275
+ 0,
2276
+ 0,
2277
+ 0,
2278
+ 0,
2279
+ 0,
2280
+ 0,
2281
+ 0,
2282
+ 0,
2283
+ 0,
2284
+ 0,
2285
+ 0,
2286
+ 0,
2287
+ 0,
2288
+ 0,
2289
+ 0,
2290
+ 0,
2291
+ 0,
2292
+ 0,
2293
+ 0,
2294
+ 0,
2295
+ 0,
2296
+ 0,
2297
+ 0,
2298
+ 0,
2299
+ 0,
2300
+ 0,
2301
+ 0,
2302
+ 0,
2303
+ 0,
2304
+ 0,
2305
+ 0,
2306
+ 0,
2307
+ 0,
2308
+ 0,
2309
+ 0,
2310
+ 0,
2311
+ 0,
2312
+ 0,
2313
+ 0,
2314
+ 0,
2315
+ 0,
2316
+ 0,
2317
+ 0,
2318
+ 0,
2319
+ 0,
2320
+ 0,
2321
+ 0,
2322
+ 0,
2323
+ 0,
2324
+ 0,
2325
+ 0,
2326
+ 0,
2327
+ 0,
2328
+ 0,
2329
+ 0,
2330
+ 0,
2331
+ 0,
2332
+ 0,
2333
+ 0,
2334
+ 0,
2335
+ 0,
2336
+ 0,
2337
+ 0,
2338
+ 0,
2339
+ 0,
2340
+ 0,
2341
+ 0,
2342
+ 0,
2343
+ 0,
2344
+ 0,
2345
+ 0,
2346
+ 0,
2347
+ 0,
2348
+ 0,
2349
+ 0,
2350
+ 0,
2351
+ 0,
2352
+ 0,
2353
+ 0,
2354
+ 0,
2355
+ 0,
2356
+ 0,
2357
+ 0,
2358
+ 0,
2359
+ 0,
2360
+ 0,
2361
+ 0,
2362
+ 0,
2363
+ 0,
2364
+ 0,
2365
+ 0,
2366
+ 0,
2367
+ 0,
2368
+ 0,
2369
+ 0,
2370
+ 0,
2371
+ 0,
2372
+ 0,
2373
+ 0,
2374
+ 0,
2375
+ 0,
2376
+ 0,
2377
+ 0,
2378
+ 0,
2379
+ 0,
2380
+ 0,
2381
+ 0,
2382
+ 0,
2383
+ 0,
2384
+ 0,
2385
+ 0,
2386
+ 0,
2387
+ 0,
2388
+ 0,
2389
+ 0,
2390
+ 0,
2391
+ 0,
2392
+ 0,
2393
+ 0,
2394
+ 0,
2395
+ 0,
2396
+ 0,
2397
+ 0,
2398
+ 0,
2399
+ 0,
2400
+ 0,
2401
+ 0,
2402
+ 0,
2403
+ 0,
2404
+ 0,
2405
+ 0,
2406
+ 0,
2407
+ 0,
2408
+ 0,
2409
+ 0,
2410
+ 0,
2411
+ 0,
2412
+ 0,
2413
+ 0,
2414
+ 0,
2415
+ 0,
2416
+ 0,
2417
+ 0,
2418
+ 0,
2419
+ 0,
2420
+ 0
2421
+ ],
2422
+ "residue_ids": [
2423
+ -1,
2424
+ 0,
2425
+ 0,
2426
+ 1,
2427
+ 1,
2428
+ 2,
2429
+ 2,
2430
+ 2,
2431
+ 2,
2432
+ -2,
2433
+ -2,
2434
+ -1,
2435
+ -1,
2436
+ -1,
2437
+ -1,
2438
+ -1,
2439
+ -1,
2440
+ -1,
2441
+ -1,
2442
+ -1,
2443
+ -1,
2444
+ -1,
2445
+ -1,
2446
+ -1,
2447
+ -1,
2448
+ -1,
2449
+ -1,
2450
+ -1,
2451
+ -1,
2452
+ -1,
2453
+ -1,
2454
+ -1,
2455
+ -1,
2456
+ -1,
2457
+ -1,
2458
+ -1,
2459
+ -1,
2460
+ -1,
2461
+ -1,
2462
+ -1,
2463
+ -1,
2464
+ -1,
2465
+ -1,
2466
+ -1,
2467
+ -1,
2468
+ -1,
2469
+ -1,
2470
+ -1,
2471
+ -1,
2472
+ -1,
2473
+ -1,
2474
+ -1,
2475
+ -1,
2476
+ -1,
2477
+ -1,
2478
+ -1,
2479
+ -1,
2480
+ -1,
2481
+ -1,
2482
+ -1,
2483
+ -1,
2484
+ -1,
2485
+ -1,
2486
+ -1,
2487
+ -1,
2488
+ -1,
2489
+ -1,
2490
+ -1,
2491
+ -1,
2492
+ -1,
2493
+ -1,
2494
+ -1,
2495
+ -1,
2496
+ -1,
2497
+ -1,
2498
+ -1,
2499
+ -1,
2500
+ -1,
2501
+ -1,
2502
+ -1,
2503
+ -1,
2504
+ -1,
2505
+ -1,
2506
+ -1,
2507
+ -1,
2508
+ -1,
2509
+ -1,
2510
+ -1,
2511
+ -1,
2512
+ -1,
2513
+ -1,
2514
+ -1,
2515
+ -1,
2516
+ -1,
2517
+ -1,
2518
+ -1,
2519
+ -1,
2520
+ -1,
2521
+ -1,
2522
+ -1,
2523
+ -1,
2524
+ -1,
2525
+ -1,
2526
+ -1,
2527
+ -1,
2528
+ -1,
2529
+ -1,
2530
+ -1,
2531
+ -1,
2532
+ -1,
2533
+ -1,
2534
+ -1,
2535
+ -1,
2536
+ -1,
2537
+ -1,
2538
+ -1,
2539
+ -1,
2540
+ -1,
2541
+ -1,
2542
+ -1,
2543
+ -1,
2544
+ -1,
2545
+ -1,
2546
+ -1,
2547
+ -1,
2548
+ -1,
2549
+ -1,
2550
+ -1,
2551
+ -1,
2552
+ -1,
2553
+ -1,
2554
+ -1,
2555
+ -1,
2556
+ -1,
2557
+ -1,
2558
+ -1,
2559
+ -1,
2560
+ -1,
2561
+ -1,
2562
+ -1,
2563
+ -1,
2564
+ -1,
2565
+ -1,
2566
+ -1,
2567
+ -1,
2568
+ -1,
2569
+ -1,
2570
+ -1,
2571
+ -1,
2572
+ -1,
2573
+ -1,
2574
+ -1,
2575
+ -1,
2576
+ -1,
2577
+ -1,
2578
+ -1,
2579
+ -1,
2580
+ -1,
2581
+ -1,
2582
+ -1,
2583
+ -1,
2584
+ -1,
2585
+ -1,
2586
+ -1,
2587
+ -1,
2588
+ -1,
2589
+ -1,
2590
+ -1,
2591
+ -1,
2592
+ -1,
2593
+ -1,
2594
+ -1,
2595
+ -1,
2596
+ -1,
2597
+ -1,
2598
+ -1,
2599
+ -1,
2600
+ -1,
2601
+ -1,
2602
+ -1,
2603
+ -1,
2604
+ -1,
2605
+ -1,
2606
+ -1,
2607
+ -1,
2608
+ -1,
2609
+ -1,
2610
+ -1,
2611
+ -1,
2612
+ -1,
2613
+ -1,
2614
+ -1,
2615
+ -1,
2616
+ -1,
2617
+ -1,
2618
+ -1,
2619
+ -1,
2620
+ -1,
2621
+ -1,
2622
+ -1,
2623
+ -1,
2624
+ -1,
2625
+ -1,
2626
+ -1,
2627
+ -1,
2628
+ -1,
2629
+ -1,
2630
+ -1,
2631
+ -1,
2632
+ -1,
2633
+ -1,
2634
+ -1,
2635
+ -1,
2636
+ -1,
2637
+ -1,
2638
+ -1,
2639
+ -1,
2640
+ -1,
2641
+ -1,
2642
+ -1,
2643
+ -1,
2644
+ -1,
2645
+ -1,
2646
+ -1,
2647
+ -1,
2648
+ -1,
2649
+ -1,
2650
+ -1,
2651
+ -1,
2652
+ -1,
2653
+ -1,
2654
+ -1,
2655
+ -1,
2656
+ -1,
2657
+ -1,
2658
+ -1,
2659
+ -1,
2660
+ -1,
2661
+ -1,
2662
+ -1,
2663
+ -1,
2664
+ -1,
2665
+ -1,
2666
+ -1,
2667
+ -1,
2668
+ -1,
2669
+ -1,
2670
+ -1,
2671
+ -1,
2672
+ -1,
2673
+ -1,
2674
+ -1,
2675
+ -1,
2676
+ -1,
2677
+ -1,
2678
+ -1
2679
+ ],
2680
+ "branch_depths": [
2681
+ 0,
2682
+ 0,
2683
+ 0,
2684
+ 1,
2685
+ 1,
2686
+ 2,
2687
+ 2,
2688
+ 2,
2689
+ 2,
2690
+ 0,
2691
+ 0,
2692
+ 0,
2693
+ 0,
2694
+ 0,
2695
+ 0,
2696
+ 0,
2697
+ 0,
2698
+ 0,
2699
+ 0,
2700
+ 0,
2701
+ 0,
2702
+ 0,
2703
+ 0,
2704
+ 0,
2705
+ 0,
2706
+ 0,
2707
+ 0,
2708
+ 0,
2709
+ 0,
2710
+ 0,
2711
+ 0,
2712
+ 0,
2713
+ 0,
2714
+ 0,
2715
+ 0,
2716
+ 0,
2717
+ 0,
2718
+ 0,
2719
+ 0,
2720
+ 0,
2721
+ 0,
2722
+ 0,
2723
+ 0,
2724
+ 0,
2725
+ 0,
2726
+ 0,
2727
+ 0,
2728
+ 0,
2729
+ 0,
2730
+ 0,
2731
+ 0,
2732
+ 0,
2733
+ 0,
2734
+ 0,
2735
+ 0,
2736
+ 0,
2737
+ 0,
2738
+ 0,
2739
+ 0,
2740
+ 0,
2741
+ 0,
2742
+ 0,
2743
+ 0,
2744
+ 0,
2745
+ 0,
2746
+ 0,
2747
+ 0,
2748
+ 0,
2749
+ 0,
2750
+ 0,
2751
+ 0,
2752
+ 0,
2753
+ 0,
2754
+ 0,
2755
+ 0,
2756
+ 0,
2757
+ 0,
2758
+ 0,
2759
+ 0,
2760
+ 0,
2761
+ 0,
2762
+ 0,
2763
+ 0,
2764
+ 0,
2765
+ 0,
2766
+ 0,
2767
+ 0,
2768
+ 0,
2769
+ 0,
2770
+ 0,
2771
+ 0,
2772
+ 0,
2773
+ 0,
2774
+ 0,
2775
+ 0,
2776
+ 0,
2777
+ 0,
2778
+ 0,
2779
+ 0,
2780
+ 0,
2781
+ 0,
2782
+ 0,
2783
+ 0,
2784
+ 0,
2785
+ 0,
2786
+ 0,
2787
+ 0,
2788
+ 0,
2789
+ 0,
2790
+ 0,
2791
+ 0,
2792
+ 0,
2793
+ 0,
2794
+ 0,
2795
+ 0,
2796
+ 0,
2797
+ 0,
2798
+ 0,
2799
+ 0,
2800
+ 0,
2801
+ 0,
2802
+ 0,
2803
+ 0,
2804
+ 0,
2805
+ 0,
2806
+ 0,
2807
+ 0,
2808
+ 0,
2809
+ 0,
2810
+ 0,
2811
+ 0,
2812
+ 0,
2813
+ 0,
2814
+ 0,
2815
+ 0,
2816
+ 0,
2817
+ 0,
2818
+ 0,
2819
+ 0,
2820
+ 0,
2821
+ 0,
2822
+ 0,
2823
+ 0,
2824
+ 0,
2825
+ 0,
2826
+ 0,
2827
+ 0,
2828
+ 0,
2829
+ 0,
2830
+ 0,
2831
+ 0,
2832
+ 0,
2833
+ 0,
2834
+ 0,
2835
+ 0,
2836
+ 0,
2837
+ 0,
2838
+ 0,
2839
+ 0,
2840
+ 0,
2841
+ 0,
2842
+ 0,
2843
+ 0,
2844
+ 0,
2845
+ 0,
2846
+ 0,
2847
+ 0,
2848
+ 0,
2849
+ 0,
2850
+ 0,
2851
+ 0,
2852
+ 0,
2853
+ 0,
2854
+ 0,
2855
+ 0,
2856
+ 0,
2857
+ 0,
2858
+ 0,
2859
+ 0,
2860
+ 0,
2861
+ 0,
2862
+ 0,
2863
+ 0,
2864
+ 0,
2865
+ 0,
2866
+ 0,
2867
+ 0,
2868
+ 0,
2869
+ 0,
2870
+ 0,
2871
+ 0,
2872
+ 0,
2873
+ 0,
2874
+ 0,
2875
+ 0,
2876
+ 0,
2877
+ 0,
2878
+ 0,
2879
+ 0,
2880
+ 0,
2881
+ 0,
2882
+ 0,
2883
+ 0,
2884
+ 0,
2885
+ 0,
2886
+ 0,
2887
+ 0,
2888
+ 0,
2889
+ 0,
2890
+ 0,
2891
+ 0,
2892
+ 0,
2893
+ 0,
2894
+ 0,
2895
+ 0,
2896
+ 0,
2897
+ 0,
2898
+ 0,
2899
+ 0,
2900
+ 0,
2901
+ 0,
2902
+ 0,
2903
+ 0,
2904
+ 0,
2905
+ 0,
2906
+ 0,
2907
+ 0,
2908
+ 0,
2909
+ 0,
2910
+ 0,
2911
+ 0,
2912
+ 0,
2913
+ 0,
2914
+ 0,
2915
+ 0,
2916
+ 0,
2917
+ 0,
2918
+ 0,
2919
+ 0,
2920
+ 0,
2921
+ 0,
2922
+ 0,
2923
+ 0,
2924
+ 0,
2925
+ 0,
2926
+ 0,
2927
+ 0,
2928
+ 0,
2929
+ 0,
2930
+ 0,
2931
+ 0,
2932
+ 0,
2933
+ 0,
2934
+ 0,
2935
+ 0,
2936
+ 0
2937
+ ],
2938
+ "linkage_types": [
2939
+ 0,
2940
+ 0,
2941
+ 0,
2942
+ 0,
2943
+ 0,
2944
+ 0,
2945
+ 0,
2946
+ 0,
2947
+ 0,
2948
+ 3,
2949
+ 2,
2950
+ 0,
2951
+ 0,
2952
+ 0,
2953
+ 0,
2954
+ 0,
2955
+ 0,
2956
+ 0,
2957
+ 0,
2958
+ 0,
2959
+ 0,
2960
+ 0,
2961
+ 0,
2962
+ 0,
2963
+ 0,
2964
+ 0,
2965
+ 0,
2966
+ 0,
2967
+ 0,
2968
+ 0,
2969
+ 0,
2970
+ 0,
2971
+ 0,
2972
+ 0,
2973
+ 0,
2974
+ 0,
2975
+ 0,
2976
+ 0,
2977
+ 0,
2978
+ 0,
2979
+ 0,
2980
+ 0,
2981
+ 0,
2982
+ 0,
2983
+ 0,
2984
+ 0,
2985
+ 0,
2986
+ 0,
2987
+ 0,
2988
+ 0,
2989
+ 0,
2990
+ 0,
2991
+ 0,
2992
+ 0,
2993
+ 0,
2994
+ 0,
2995
+ 0,
2996
+ 0,
2997
+ 0,
2998
+ 0,
2999
+ 0,
3000
+ 0,
3001
+ 0,
3002
+ 0,
3003
+ 0,
3004
+ 0,
3005
+ 0,
3006
+ 0,
3007
+ 0,
3008
+ 0,
3009
+ 0,
3010
+ 0,
3011
+ 0,
3012
+ 0,
3013
+ 0,
3014
+ 0,
3015
+ 0,
3016
+ 0,
3017
+ 0,
3018
+ 0,
3019
+ 0,
3020
+ 0,
3021
+ 0,
3022
+ 0,
3023
+ 0,
3024
+ 0,
3025
+ 0,
3026
+ 0,
3027
+ 0,
3028
+ 0,
3029
+ 0,
3030
+ 0,
3031
+ 0,
3032
+ 0,
3033
+ 0,
3034
+ 0,
3035
+ 0,
3036
+ 0,
3037
+ 0,
3038
+ 0,
3039
+ 0,
3040
+ 0,
3041
+ 0,
3042
+ 0,
3043
+ 0,
3044
+ 0,
3045
+ 0,
3046
+ 0,
3047
+ 0,
3048
+ 0,
3049
+ 0,
3050
+ 0,
3051
+ 0,
3052
+ 0,
3053
+ 0,
3054
+ 0,
3055
+ 0,
3056
+ 0,
3057
+ 0,
3058
+ 0,
3059
+ 0,
3060
+ 0,
3061
+ 0,
3062
+ 0,
3063
+ 0,
3064
+ 0,
3065
+ 0,
3066
+ 0,
3067
+ 0,
3068
+ 0,
3069
+ 0,
3070
+ 0,
3071
+ 0,
3072
+ 0,
3073
+ 0,
3074
+ 0,
3075
+ 0,
3076
+ 0,
3077
+ 0,
3078
+ 0,
3079
+ 0,
3080
+ 0,
3081
+ 0,
3082
+ 0,
3083
+ 0,
3084
+ 0,
3085
+ 0,
3086
+ 0,
3087
+ 0,
3088
+ 0,
3089
+ 0,
3090
+ 0,
3091
+ 0,
3092
+ 0,
3093
+ 0,
3094
+ 0,
3095
+ 0,
3096
+ 0,
3097
+ 0,
3098
+ 0,
3099
+ 0,
3100
+ 0,
3101
+ 0,
3102
+ 0,
3103
+ 0,
3104
+ 0,
3105
+ 0,
3106
+ 0,
3107
+ 0,
3108
+ 0,
3109
+ 0,
3110
+ 0,
3111
+ 0,
3112
+ 0,
3113
+ 0,
3114
+ 0,
3115
+ 0,
3116
+ 0,
3117
+ 0,
3118
+ 0,
3119
+ 0,
3120
+ 0,
3121
+ 0,
3122
+ 0,
3123
+ 0,
3124
+ 0,
3125
+ 0,
3126
+ 0,
3127
+ 0,
3128
+ 0,
3129
+ 0,
3130
+ 0,
3131
+ 0,
3132
+ 0,
3133
+ 0,
3134
+ 0,
3135
+ 0,
3136
+ 0,
3137
+ 0,
3138
+ 0,
3139
+ 0,
3140
+ 0,
3141
+ 0,
3142
+ 0,
3143
+ 0,
3144
+ 0,
3145
+ 0,
3146
+ 0,
3147
+ 0,
3148
+ 0,
3149
+ 0,
3150
+ 0,
3151
+ 0,
3152
+ 0,
3153
+ 0,
3154
+ 0,
3155
+ 0,
3156
+ 0,
3157
+ 0,
3158
+ 0,
3159
+ 0,
3160
+ 0,
3161
+ 0,
3162
+ 0,
3163
+ 0,
3164
+ 0,
3165
+ 0,
3166
+ 0,
3167
+ 0,
3168
+ 0,
3169
+ 0,
3170
+ 0,
3171
+ 0,
3172
+ 0,
3173
+ 0,
3174
+ 0,
3175
+ 0,
3176
+ 0,
3177
+ 0,
3178
+ 0,
3179
+ 0,
3180
+ 0,
3181
+ 0,
3182
+ 0,
3183
+ 0,
3184
+ 0,
3185
+ 0,
3186
+ 0,
3187
+ 0,
3188
+ 0,
3189
+ 0,
3190
+ 0,
3191
+ 0,
3192
+ 0,
3193
+ 0,
3194
+ 0
3195
+ ],
3196
+ "branch_depths_blank": [
3197
+ 0,
3198
+ 0,
3199
+ 0,
3200
+ 1,
3201
+ 1,
3202
+ 2,
3203
+ 2,
3204
+ 2,
3205
+ 2,
3206
+ 0,
3207
+ 0,
3208
+ 0,
3209
+ 0,
3210
+ 0,
3211
+ 0,
3212
+ 0,
3213
+ 0,
3214
+ 0,
3215
+ 0,
3216
+ 0,
3217
+ 0,
3218
+ 0,
3219
+ 0,
3220
+ 0,
3221
+ 0,
3222
+ 0,
3223
+ 0,
3224
+ 0,
3225
+ 0,
3226
+ 0,
3227
+ 0,
3228
+ 0,
3229
+ 0,
3230
+ 0,
3231
+ 0,
3232
+ 0,
3233
+ 0,
3234
+ 0,
3235
+ 0,
3236
+ 0,
3237
+ 0,
3238
+ 0,
3239
+ 0,
3240
+ 0,
3241
+ 0,
3242
+ 0,
3243
+ 0,
3244
+ 0,
3245
+ 0,
3246
+ 0,
3247
+ 0,
3248
+ 0,
3249
+ 0,
3250
+ 0,
3251
+ 0,
3252
+ 0,
3253
+ 0,
3254
+ 0,
3255
+ 0,
3256
+ 0,
3257
+ 0,
3258
+ 0,
3259
+ 0,
3260
+ 0,
3261
+ 0,
3262
+ 0,
3263
+ 0,
3264
+ 0,
3265
+ 0,
3266
+ 0,
3267
+ 0,
3268
+ 0,
3269
+ 0,
3270
+ 0,
3271
+ 0,
3272
+ 0,
3273
+ 0,
3274
+ 0,
3275
+ 0,
3276
+ 0,
3277
+ 0,
3278
+ 0,
3279
+ 0,
3280
+ 0,
3281
+ 0,
3282
+ 0,
3283
+ 0,
3284
+ 0,
3285
+ 0,
3286
+ 0,
3287
+ 0,
3288
+ 0,
3289
+ 0,
3290
+ 0,
3291
+ 0,
3292
+ 0,
3293
+ 0,
3294
+ 0,
3295
+ 0,
3296
+ 0,
3297
+ 0,
3298
+ 0,
3299
+ 0,
3300
+ 0,
3301
+ 0,
3302
+ 0,
3303
+ 0,
3304
+ 0,
3305
+ 0,
3306
+ 0,
3307
+ 0,
3308
+ 0,
3309
+ 0,
3310
+ 0,
3311
+ 0,
3312
+ 0,
3313
+ 0,
3314
+ 0,
3315
+ 0,
3316
+ 0,
3317
+ 0,
3318
+ 0,
3319
+ 0,
3320
+ 0,
3321
+ 0,
3322
+ 0,
3323
+ 0,
3324
+ 0,
3325
+ 0,
3326
+ 0,
3327
+ 0,
3328
+ 0,
3329
+ 0,
3330
+ 0,
3331
+ 0,
3332
+ 0,
3333
+ 0,
3334
+ 0,
3335
+ 0,
3336
+ 0,
3337
+ 0,
3338
+ 0,
3339
+ 0,
3340
+ 0,
3341
+ 0,
3342
+ 0,
3343
+ 0,
3344
+ 0,
3345
+ 0,
3346
+ 0,
3347
+ 0,
3348
+ 0,
3349
+ 0,
3350
+ 0,
3351
+ 0,
3352
+ 0,
3353
+ 0,
3354
+ 0,
3355
+ 0,
3356
+ 0,
3357
+ 0,
3358
+ 0,
3359
+ 0,
3360
+ 0,
3361
+ 0,
3362
+ 0,
3363
+ 0,
3364
+ 0,
3365
+ 0,
3366
+ 0,
3367
+ 0,
3368
+ 0,
3369
+ 0,
3370
+ 0,
3371
+ 0,
3372
+ 0,
3373
+ 0,
3374
+ 0,
3375
+ 0,
3376
+ 0,
3377
+ 0,
3378
+ 0,
3379
+ 0,
3380
+ 0,
3381
+ 0,
3382
+ 0,
3383
+ 0,
3384
+ 0,
3385
+ 0,
3386
+ 0,
3387
+ 0,
3388
+ 0,
3389
+ 0,
3390
+ 0,
3391
+ 0,
3392
+ 0,
3393
+ 0,
3394
+ 0,
3395
+ 0,
3396
+ 0,
3397
+ 0,
3398
+ 0,
3399
+ 0,
3400
+ 0,
3401
+ 0,
3402
+ 0,
3403
+ 0,
3404
+ 0,
3405
+ 0,
3406
+ 0,
3407
+ 0,
3408
+ 0,
3409
+ 0,
3410
+ 0,
3411
+ 0,
3412
+ 0,
3413
+ 0,
3414
+ 0,
3415
+ 0,
3416
+ 0,
3417
+ 0,
3418
+ 0,
3419
+ 0,
3420
+ 0,
3421
+ 0,
3422
+ 0,
3423
+ 0,
3424
+ 0,
3425
+ 0,
3426
+ 0,
3427
+ 0,
3428
+ 0,
3429
+ 0,
3430
+ 0,
3431
+ 0,
3432
+ 0,
3433
+ 0,
3434
+ 0,
3435
+ 0,
3436
+ 0,
3437
+ 0,
3438
+ 0,
3439
+ 0,
3440
+ 0,
3441
+ 0,
3442
+ 0,
3443
+ 0,
3444
+ 0,
3445
+ 0,
3446
+ 0,
3447
+ 0,
3448
+ 0,
3449
+ 0,
3450
+ 0,
3451
+ 0,
3452
+ 0
3453
+ ],
3454
+ "linkage_types_blank": [
3455
+ 0,
3456
+ 0,
3457
+ 0,
3458
+ 0,
3459
+ 0,
3460
+ 0,
3461
+ 0,
3462
+ 0,
3463
+ 0,
3464
+ 9,
3465
+ 2,
3466
+ 0,
3467
+ 0,
3468
+ 0,
3469
+ 0,
3470
+ 0,
3471
+ 0,
3472
+ 0,
3473
+ 0,
3474
+ 0,
3475
+ 0,
3476
+ 0,
3477
+ 0,
3478
+ 0,
3479
+ 0,
3480
+ 0,
3481
+ 0,
3482
+ 0,
3483
+ 0,
3484
+ 0,
3485
+ 0,
3486
+ 0,
3487
+ 0,
3488
+ 0,
3489
+ 0,
3490
+ 0,
3491
+ 0,
3492
+ 0,
3493
+ 0,
3494
+ 0,
3495
+ 0,
3496
+ 0,
3497
+ 0,
3498
+ 0,
3499
+ 0,
3500
+ 0,
3501
+ 0,
3502
+ 0,
3503
+ 0,
3504
+ 0,
3505
+ 0,
3506
+ 0,
3507
+ 0,
3508
+ 0,
3509
+ 0,
3510
+ 0,
3511
+ 0,
3512
+ 0,
3513
+ 0,
3514
+ 0,
3515
+ 0,
3516
+ 0,
3517
+ 0,
3518
+ 0,
3519
+ 0,
3520
+ 0,
3521
+ 0,
3522
+ 0,
3523
+ 0,
3524
+ 0,
3525
+ 0,
3526
+ 0,
3527
+ 0,
3528
+ 0,
3529
+ 0,
3530
+ 0,
3531
+ 0,
3532
+ 0,
3533
+ 0,
3534
+ 0,
3535
+ 0,
3536
+ 0,
3537
+ 0,
3538
+ 0,
3539
+ 0,
3540
+ 0,
3541
+ 0,
3542
+ 0,
3543
+ 0,
3544
+ 0,
3545
+ 0,
3546
+ 0,
3547
+ 0,
3548
+ 0,
3549
+ 0,
3550
+ 0,
3551
+ 0,
3552
+ 0,
3553
+ 0,
3554
+ 0,
3555
+ 0,
3556
+ 0,
3557
+ 0,
3558
+ 0,
3559
+ 0,
3560
+ 0,
3561
+ 0,
3562
+ 0,
3563
+ 0,
3564
+ 0,
3565
+ 0,
3566
+ 0,
3567
+ 0,
3568
+ 0,
3569
+ 0,
3570
+ 0,
3571
+ 0,
3572
+ 0,
3573
+ 0,
3574
+ 0,
3575
+ 0,
3576
+ 0,
3577
+ 0,
3578
+ 0,
3579
+ 0,
3580
+ 0,
3581
+ 0,
3582
+ 0,
3583
+ 0,
3584
+ 0,
3585
+ 0,
3586
+ 0,
3587
+ 0,
3588
+ 0,
3589
+ 0,
3590
+ 0,
3591
+ 0,
3592
+ 0,
3593
+ 0,
3594
+ 0,
3595
+ 0,
3596
+ 0,
3597
+ 0,
3598
+ 0,
3599
+ 0,
3600
+ 0,
3601
+ 0,
3602
+ 0,
3603
+ 0,
3604
+ 0,
3605
+ 0,
3606
+ 0,
3607
+ 0,
3608
+ 0,
3609
+ 0,
3610
+ 0,
3611
+ 0,
3612
+ 0,
3613
+ 0,
3614
+ 0,
3615
+ 0,
3616
+ 0,
3617
+ 0,
3618
+ 0,
3619
+ 0,
3620
+ 0,
3621
+ 0,
3622
+ 0,
3623
+ 0,
3624
+ 0,
3625
+ 0,
3626
+ 0,
3627
+ 0,
3628
+ 0,
3629
+ 0,
3630
+ 0,
3631
+ 0,
3632
+ 0,
3633
+ 0,
3634
+ 0,
3635
+ 0,
3636
+ 0,
3637
+ 0,
3638
+ 0,
3639
+ 0,
3640
+ 0,
3641
+ 0,
3642
+ 0,
3643
+ 0,
3644
+ 0,
3645
+ 0,
3646
+ 0,
3647
+ 0,
3648
+ 0,
3649
+ 0,
3650
+ 0,
3651
+ 0,
3652
+ 0,
3653
+ 0,
3654
+ 0,
3655
+ 0,
3656
+ 0,
3657
+ 0,
3658
+ 0,
3659
+ 0,
3660
+ 0,
3661
+ 0,
3662
+ 0,
3663
+ 0,
3664
+ 0,
3665
+ 0,
3666
+ 0,
3667
+ 0,
3668
+ 0,
3669
+ 0,
3670
+ 0,
3671
+ 0,
3672
+ 0,
3673
+ 0,
3674
+ 0,
3675
+ 0,
3676
+ 0,
3677
+ 0,
3678
+ 0,
3679
+ 0,
3680
+ 0,
3681
+ 0,
3682
+ 0,
3683
+ 0,
3684
+ 0,
3685
+ 0,
3686
+ 0,
3687
+ 0,
3688
+ 0,
3689
+ 0,
3690
+ 0,
3691
+ 0,
3692
+ 0,
3693
+ 0,
3694
+ 0,
3695
+ 0,
3696
+ 0,
3697
+ 0,
3698
+ 0,
3699
+ 0,
3700
+ 0,
3701
+ 0,
3702
+ 0,
3703
+ 0,
3704
+ 0,
3705
+ 0,
3706
+ 0,
3707
+ 0,
3708
+ 0,
3709
+ 0,
3710
+ 0
3711
+ ],
3712
+ "enumerated_alternative": false,
3713
+ "unknown_question_mark": false,
3714
+ "wa3_truth_uses_invalid_token": false,
3715
+ "single_answer": true
3716
+ }
3717
+ }
3718
+ ]
releases/2026-10-02/iar_inference/fixtures/reference_smoke_report.json ADDED
@@ -0,0 +1,253 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "iar_two_real_saved_readout_full_model_smoke_v1",
3
+ "status": "PASS_FULL_WA3_STRICT_LOAD_AND_TWO_REAL_SAVED_READOUT_FORWARDS",
4
+ "started_unix": 1790740892.3816009,
5
+ "script_sha256": "d757fda5194ba6a6ce9fc03096b5596e47f7889844034aa70efbac33fb8235cb",
6
+ "scope": "Strict full WA3 plus two recorded single-linkage cases and fixed saved readout; no refit or metrics.",
7
+ "limits": [
8
+ "CPU smoke, not historical CUDA numerical replay or model-quality evidence.",
9
+ "No residue or multi-token fallback; no full corpus parity or seeded case reconstruction.",
10
+ "Original Nova native authority bootstrap can read complete sequence/topology packages."
11
+ ],
12
+ "manifest_sha256": "440a748a14e9c09d017677ef3477d19ed02169a8e5ee048ff8b3f05bdaa1a12d",
13
+ "runtime": {
14
+ "python": "3.10.16",
15
+ "torch": "2.9.1+cu128",
16
+ "numpy": "2.2.6",
17
+ "device": "cpu",
18
+ "precision": "float32",
19
+ "threads": 4,
20
+ "host": "nova18-wide-4",
21
+ "slurm_job_id": "16662851"
22
+ },
23
+ "phase": "two_case_saved_readout_forward",
24
+ "inputs_sha256": "60ac0c0fd1b6e0ada2d847d1fccff4f6fe7c085aab3146247dc5d7bfebbabc27",
25
+ "full_model": {
26
+ "state_entries": 505,
27
+ "parameters": 138487995,
28
+ "strict_load": true,
29
+ "checkpoint_sha256": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
30
+ "neutral_vector": {
31
+ "shape": [
32
+ 768
33
+ ],
34
+ "dtype": "float32",
35
+ "sha256_c_order_bytes": "297bc451bcb34097045af18278e9cbeabbf38fdb22777d77aee87001784d4b29"
36
+ },
37
+ "hidden_capture_input_of": "sequence_head"
38
+ },
39
+ "readout": {
40
+ "sha256": "a03990a02a1737de05a7fa1739164e84fa6a0a4170ebaa65317a9f3c2f16c0b0",
41
+ "classes": 211,
42
+ "fit": {
43
+ "dev_accuracy_for_selection": 0.7359,
44
+ "best_epoch": 46,
45
+ "tokens": 211,
46
+ "train_samples": 111989,
47
+ "development_samples": 27690,
48
+ "feature_dim": 768
49
+ },
50
+ "refit": false
51
+ },
52
+ "forward_calls": 2,
53
+ "cases": [
54
+ {
55
+ "dataset": "bench1392",
56
+ "case_id": "g0000_s00_linkage_38_38",
57
+ "used_saved_readout": true,
58
+ "wa3_position": 43,
59
+ "truth_token_id": 1720,
60
+ "truth_token": "g8-h2",
61
+ "final_token_id": 1720,
62
+ "final_token": "g8-h2",
63
+ "abstained": false,
64
+ "rounds": [
65
+ {
66
+ "iteration": 1,
67
+ "hidden_sha256_c_order_bytes": "ff29f35c4eb22549a5185b89c2bf9dabe5ff7f4a3fdc44291cbfa724eb5349ee",
68
+ "top10": [
69
+ {
70
+ "token_id": 1720,
71
+ "token": "g8-h2",
72
+ "probability": 0.9999593496322632
73
+ },
74
+ {
75
+ "token_id": 1261,
76
+ "token": "g3-h2",
77
+ "probability": 4.006435483461246e-05
78
+ },
79
+ {
80
+ "token_id": 1380,
81
+ "token": "h8-i2",
82
+ "probability": 2.607788189834537e-07
83
+ },
84
+ {
85
+ "token_id": 1122,
86
+ "token": "d3-e2",
87
+ "probability": 1.445049662152087e-07
88
+ },
89
+ {
90
+ "token_id": 531,
91
+ "token": "g2-h1",
92
+ "probability": 1.0591251964342518e-07
93
+ },
94
+ {
95
+ "token_id": 544,
96
+ "token": "g3-h1",
97
+ "probability": 6.952672038096352e-08
98
+ },
99
+ {
100
+ "token_id": 1548,
101
+ "token": "m6-n2",
102
+ "probability": 1.4976388129639417e-08
103
+ },
104
+ {
105
+ "token_id": 1572,
106
+ "token": "h3-i2",
107
+ "probability": 8.80937189862152e-09
108
+ },
109
+ {
110
+ "token_id": 710,
111
+ "token": "f3-g2",
112
+ "probability": 6.006392716528808e-09
113
+ },
114
+ {
115
+ "token_id": 486,
116
+ "token": "g4-h1",
117
+ "probability": 4.912561468728427e-09
118
+ }
119
+ ],
120
+ "accepted": true
121
+ }
122
+ ]
123
+ },
124
+ {
125
+ "dataset": "seal162",
126
+ "case_id": "g0000_s00_linkage_9_9",
127
+ "used_saved_readout": true,
128
+ "wa3_position": 9,
129
+ "truth_token_id": 228,
130
+ "truth_token": "a4-b1",
131
+ "final_token_id": 4,
132
+ "final_token": "[MASK]",
133
+ "abstained": true,
134
+ "rounds": [
135
+ {
136
+ "iteration": 1,
137
+ "hidden_sha256_c_order_bytes": "1cd693d3ea8df175d65f0eca3673ce2a73dff666d1cf06aa84bf4943127c3be8",
138
+ "top10": [
139
+ {
140
+ "token_id": 228,
141
+ "token": "a4-b1",
142
+ "probability": 0.7713369131088257
143
+ },
144
+ {
145
+ "token_id": 262,
146
+ "token": "a3-b1",
147
+ "probability": 0.2158111184835434
148
+ },
149
+ {
150
+ "token_id": 404,
151
+ "token": "a6-b1",
152
+ "probability": 0.01060701534152031
153
+ },
154
+ {
155
+ "token_id": 399,
156
+ "token": "a2-b1",
157
+ "probability": 0.002030769130215049
158
+ },
159
+ {
160
+ "token_id": 518,
161
+ "token": "a5-b1",
162
+ "probability": 9.787840826902539e-05
163
+ },
164
+ {
165
+ "token_id": 1282,
166
+ "token": "a7-b1",
167
+ "probability": 7.618743984494358e-05
168
+ },
169
+ {
170
+ "token_id": 710,
171
+ "token": "f3-g2",
172
+ "probability": 1.4471691429207567e-05
173
+ },
174
+ {
175
+ "token_id": 1252,
176
+ "token": "d6-g1",
177
+ "probability": 8.236345820478164e-06
178
+ },
179
+ {
180
+ "token_id": 1331,
181
+ "token": "c3-d2",
182
+ "probability": 2.9395614546956494e-06
183
+ },
184
+ {
185
+ "token_id": 466,
186
+ "token": "h4-i1",
187
+ "probability": 1.904394366647466e-06
188
+ }
189
+ ],
190
+ "accepted": false
191
+ },
192
+ {
193
+ "iteration": 2,
194
+ "hidden_sha256_c_order_bytes": "1cd693d3ea8df175d65f0eca3673ce2a73dff666d1cf06aa84bf4943127c3be8",
195
+ "top10": [
196
+ {
197
+ "token_id": 228,
198
+ "token": "a4-b1",
199
+ "probability": 0.7713369131088257
200
+ },
201
+ {
202
+ "token_id": 262,
203
+ "token": "a3-b1",
204
+ "probability": 0.2158111184835434
205
+ },
206
+ {
207
+ "token_id": 404,
208
+ "token": "a6-b1",
209
+ "probability": 0.01060701534152031
210
+ },
211
+ {
212
+ "token_id": 399,
213
+ "token": "a2-b1",
214
+ "probability": 0.002030769130215049
215
+ },
216
+ {
217
+ "token_id": 518,
218
+ "token": "a5-b1",
219
+ "probability": 9.787840826902539e-05
220
+ },
221
+ {
222
+ "token_id": 1282,
223
+ "token": "a7-b1",
224
+ "probability": 7.618743984494358e-05
225
+ },
226
+ {
227
+ "token_id": 710,
228
+ "token": "f3-g2",
229
+ "probability": 1.4471691429207567e-05
230
+ },
231
+ {
232
+ "token_id": 1252,
233
+ "token": "d6-g1",
234
+ "probability": 8.236345820478164e-06
235
+ },
236
+ {
237
+ "token_id": 1331,
238
+ "token": "c3-d2",
239
+ "probability": 2.9395614546956494e-06
240
+ },
241
+ {
242
+ "token_id": 466,
243
+ "token": "h4-i1",
244
+ "probability": 1.904394366647466e-06
245
+ }
246
+ ],
247
+ "accepted": false
248
+ }
249
+ ]
250
+ }
251
+ ],
252
+ "finished_unix": 1790740986.703624
253
+ }
releases/2026-10-02/iar_inference/infer.py ADDED
@@ -0,0 +1,290 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #!/usr/bin/env python3
2
+ """Strict full-WA3 CPU inference on two recorded single-linkage IAR cases.
3
+
4
+ The full model uses unchanged, pinned released classes and authority checks.
5
+ This entry point neither fits a readout nor imports the historical main script.
6
+ """
7
+ from pathlib import Path
8
+ import argparse
9
+ import ast
10
+ import hashlib
11
+ import importlib.util
12
+ import json
13
+ import os
14
+ import platform
15
+ import sys
16
+ import time
17
+ from types import SimpleNamespace
18
+
19
+
20
+ ROOT = Path(__file__).resolve().parent
21
+ sys.dont_write_bytecode = True
22
+
23
+
24
+ def require(condition, message):
25
+ if not condition:
26
+ raise ValueError(message)
27
+
28
+
29
+ def sha256(path):
30
+ digest = hashlib.sha256()
31
+ with Path(path).open("rb") as handle:
32
+ for block in iter(lambda: handle.read(4 << 20), b""):
33
+ digest.update(block)
34
+ return digest.hexdigest()
35
+
36
+
37
+ def load_module(name, path):
38
+ spec = importlib.util.spec_from_file_location(name, path)
39
+ require(spec is not None and spec.loader is not None, "Cannot import " + str(path))
40
+ module = importlib.util.module_from_spec(spec)
41
+ sys.modules[name] = module
42
+ spec.loader.exec_module(module)
43
+ return module
44
+
45
+
46
+ def read_config(path=ROOT / "config.json", source_root=None, readout_root=None):
47
+ path = Path(path).resolve()
48
+ config = json.loads(path.read_text())
49
+ require(config["schema"] == "bertose_portable_two_case_iar_v1", "Unknown config schema")
50
+ source = Path(source_root).resolve() if source_root else (path.parent / config["source_root"]).resolve()
51
+ readout = Path(readout_root).resolve() if readout_root else (path.parent / config["readout_root"]).resolve()
52
+ return config, source, readout
53
+
54
+
55
+ def preflight(config, source_root, readout_root):
56
+ """Check small source/authority/fixture bytes without torch or a checkpoint."""
57
+ verified = []
58
+ for root, key in ((source_root, "source_files"), (readout_root, "readout_files"), (ROOT, "local_files")):
59
+ for item in config[key]:
60
+ path = root / item["path"]
61
+ require(path.is_file() and path.stat().st_size == item["bytes"], "Missing/changed file: " + str(path))
62
+ require(sha256(path) == item["sha256"], "SHA-256 differs: " + str(path))
63
+ verified.append({"path": str(path), "sha256": item["sha256"]})
64
+ # Verify that the copied helpers retain the original function ASTs. Do not
65
+ # import the monolithic historical source (it changes builtins.open).
66
+ def functions(path):
67
+ return {n.name: ast.dump(n, include_attributes=False) for n in ast.parse(path.read_text()).body
68
+ if isinstance(n, ast.FunctionDef)}
69
+ original = functions(readout_root / "originals/wa3_final_iar_readout_all.py")
70
+ portable = functions(ROOT / "original_inference_helpers.py")
71
+ names = config["original_helper_functions"]
72
+ require(set(portable) == set(names), "Unexpected portable inference helper")
73
+ require(all(portable[n] == original[n] for n in names), "Original helper semantics changed")
74
+ records = json.loads((ROOT / "fixtures/rebuilt_two_case_inputs.json").read_text())
75
+ require(len(records) == 2, "Expected exactly two recorded cases")
76
+ for record in records:
77
+ case = record["rebuilt_case"]
78
+ require(len(case["positions"]) == len(case["units"]) == len(case["truth_ids"]) == 1,
79
+ "Only one masked token and one semantic unit are supported")
80
+ require(case["single_answer"] and case["units"][0]["unit_type"] == "linkage", "Not an ordinary single linkage")
81
+ for key in ("masked_ids", "attention_mask", "residue_ids", "branch_depths_blank", "linkage_types_blank"):
82
+ require(len(case[key]) == 256 and all(type(v) is int for v in case[key]), "Invalid input channel: " + key)
83
+ position = case["positions"][0]
84
+ require(0 <= position < 256 and case["attention_mask"][position] == 1
85
+ and case["masked_ids"][position] == 4 and case["residue_ids"][position] == -2
86
+ and case["branch_depths_blank"][position] == 0 and case["linkage_types_blank"][position] == 9,
87
+ "Masked linkage channel contract changed")
88
+ return records, verified
89
+
90
+
91
+ def load_model(checkpoint, config, source_root):
92
+ """Load the pinned full checkpoint through the original production classes.
93
+
94
+ Caller must run preflight first. The R5 constructor independently reopens
95
+ all twelve component authorities with its original no-follow traversal.
96
+ """
97
+ import torch
98
+ checkpoint = Path(checkpoint).resolve()
99
+ expected = config["checkpoint"]
100
+ # Keep the same descriptor for hashing and deserialization. weights_only=False
101
+ # is permitted only for these exact, predeclared historical checkpoint bytes.
102
+ with checkpoint.open("rb") as handle:
103
+ before = os.fstat(handle.fileno())
104
+ digest = hashlib.sha256()
105
+ for block in iter(lambda: handle.read(4 << 20), b""):
106
+ digest.update(block)
107
+ require(digest.hexdigest() == expected["sha256"], "Full checkpoint SHA-256 differs; refusing deserialization")
108
+ handle.seek(0)
109
+ saved = torch.load(handle, map_location="cpu", weights_only=False)
110
+ after = os.fstat(handle.fileno())
111
+ require((before.st_dev, before.st_ino, before.st_size, before.st_mtime_ns)
112
+ == (after.st_dev, after.st_ino, after.st_size, after.st_mtime_ns), "Checkpoint changed during load")
113
+ require(saved.get("md_cell_id") == expected["md_cell_id"]
114
+ and int(saved["selection"]["best_epoch"]) == expected["best_epoch"], "Checkpoint cell/epoch identity differs")
115
+ r5 = load_module("_portable_iar_r5", source_root / config["model_sources"]["r5"])
116
+ md = load_module("_portable_iar_md", source_root / config["model_sources"]["md_core"])
117
+ # The original factory accesses exactly these two attributes of the executor.
118
+ # No training planner, scheduler or static-release launcher is needed here.
119
+ executor_binding = SimpleNamespace(_composite=r5, NativeEightCompositeR5=r5.NativeEightCompositeR5)
120
+ model_type = md.make_md_enabled_composite_class(executor_binding)
121
+ model = model_type(r5.NativeEightConfig(cell_id=expected["base_cell_id"], seq_vocab_size=expected["vocabulary_size"]),
122
+ md_cell_id=expected["md_cell_id"], md_temporal_size=expected["md_temporal_size"])
123
+ require(len(saved["model_state"]) == expected["state_entries"], "Checkpoint state count differs")
124
+ model.load_state_dict(saved["model_state"], strict=True)
125
+ del saved
126
+ require(len(model.state_dict()) == expected["state_entries"]
127
+ and sum(p.numel() for p in model.parameters()) == expected["parameter_count"], "Full model inventory differs")
128
+ require(all(p.dtype == torch.float32 for p in model.parameters()), "Expected float32 model parameters")
129
+ model.eval().requires_grad_(False)
130
+ model.verify_live_contract()
131
+ bindings = dict(model.reverify_component_authorities())
132
+ require(bindings == dict(r5.EXPECTED_COMPONENT_BINDINGS), "Component authority bindings differ")
133
+ return model, bindings
134
+
135
+
136
+ def run_cases(model, records, config, readout_root, output_dir):
137
+ import numpy as np
138
+ import torch
139
+ helper = load_module("_portable_iar_helpers", ROOT / "original_inference_helpers.py")
140
+ readout = load_module("_portable_iar_readout", readout_root / "readout.py")
141
+ payload, linear, metadata = readout.load_readout(readout_root)
142
+ contract = config["inference"]
143
+ require(metadata["linkage_input"]["training_counts"] == contract["neutral_training_counts"]
144
+ and metadata["evaluation"]["threshold"] == contract["threshold"]
145
+ and metadata["evaluation"]["rounds"] == contract["maximum_rounds"], "Readout inference contract differs")
146
+ require(metadata["encoder"]["checkpoint_sha256"] == config["checkpoint"]["sha256"], "Readout encoder binding differs")
147
+ vocab = json.loads((readout_root / metadata["vocabulary"]["path"]).read_text())["token_to_id"]
148
+ id_to_token = {v: k for k, v in vocab.items()}
149
+ cases = [r["rebuilt_case"] for r in records]
150
+ require(all(c["truth_ids"][0] in payload["token_ids"] for c in cases), "Fixture truth is outside saved readout")
151
+ lk_name, linkage = helper.find_linkage_embedding(model)
152
+ head_name, head = helper.find_lm_projection(model)
153
+ require(lk_name == contract["neutral_module"] and head_name == contract["hidden_module"], "Hook module differs")
154
+ counts = np.asarray(contract["neutral_training_counts"], dtype=np.float64)
155
+ weights = torch.as_tensor(counts / counts.sum(), dtype=torch.float32)
156
+ helper.HOOK["vec"] = (linkage.weight[1:9].detach().float() * weights[:, None]).sum(0)
157
+ neutral_sha = hashlib.sha256(helper.HOOK["vec"].numpy().tobytes()).hexdigest()
158
+ hooks = [linkage.register_forward_hook(helper._linkage_hook), head.register_forward_hook(helper._cap_hook)]
159
+ current = [list(c["masked_ids"]) for c in cases]
160
+ unresolved = [True] * len(cases)
161
+ histories = [[] for _ in cases]
162
+ comparisons = []
163
+ positions = [(i, c["positions"][0]) for i, c in enumerate(cases)]
164
+ tensor = lambda key: torch.tensor([c[key] for c in cases], dtype=torch.long)
165
+ reference = json.loads((ROOT / "fixtures/reference_smoke_report.json").read_text())
166
+ try:
167
+ for iteration in range(contract["maximum_rounds"]):
168
+ helper.CAP["h"] = None
169
+ logits = helper._forward(model, "cpu", torch.tensor(current, dtype=torch.long),
170
+ tensor("attention_mask"), tensor("residue_ids"), tensor("branch_depths_blank"),
171
+ tensor("linkage_types_blank"), positions)
172
+ require(tuple(logits.shape) == (2, 256, 2114) and torch.isfinite(logits).all().item(), "Invalid full MLM output")
173
+ progress = 0
174
+ for i, case in enumerate(cases):
175
+ if not unresolved[i]:
176
+ continue
177
+ position = case["positions"][0]
178
+ hidden = helper._gather_h(2, 256, i, position).float().cpu().numpy()[None, :]
179
+ require(hidden.shape == (1, 768) and np.isfinite(hidden).all(), "Invalid hidden feature")
180
+ if iteration == 0:
181
+ expected_hidden = np.load(ROOT / ("fixtures/case%d_pre_sequence_head.npy" % i), allow_pickle=False)
182
+ matches = bool(np.allclose(hidden, expected_hidden, atol=contract["hidden_allclose_atol"],
183
+ rtol=contract["hidden_allclose_rtol"]))
184
+ comparisons.append({"case_id": case["case_id"], "hidden_allclose": matches,
185
+ "hidden_max_abs_difference": float(np.max(np.abs(hidden - expected_hidden))),
186
+ "hidden_bitwise_equal": bool(np.array_equal(hidden, expected_hidden))})
187
+ require(matches, "Hidden feature differs from executed reference for " + case["case_id"])
188
+ with (output_dir / ("case%d_pre_sequence_head.npy" % i)).open("xb") as handle:
189
+ np.save(handle, hidden, allow_pickle=False)
190
+ probabilities = readout.probabilities(hidden, payload, linear)[0]
191
+ pp, indices = torch.topk(torch.from_numpy(probabilities), k=10)
192
+ ids = [int(payload["token_ids"][int(j)]) for j in indices]
193
+ # Every saved class is verified by load_readout as an ordinary
194
+ # linkage token; it therefore cannot be a special/ambiguous ID.
195
+ accepted = float(pp[0]) >= contract["threshold"]
196
+ histories[i].append({"iteration": iteration + 1,
197
+ "hidden_sha256_c_order_bytes": hashlib.sha256(hidden.tobytes()).hexdigest(),
198
+ "top10": [{"token_id": token, "token": id_to_token[token], "probability": float(p)}
199
+ for token, p in zip(ids, pp)], "accepted": accepted})
200
+ if accepted:
201
+ current[i][position] = ids[0]
202
+ unresolved[i] = False
203
+ progress += 1
204
+ if progress == 0:
205
+ break
206
+ finally:
207
+ for hook in hooks:
208
+ hook.remove()
209
+ helper.HOOK["mask"] = None
210
+ helper.CAP["h"] = None
211
+ results = [{"dataset": record["dataset"], "case_id": case["case_id"], "used_saved_readout": True,
212
+ "wa3_position": case["positions"][0], "truth_token_id": case["truth_ids"][0],
213
+ "truth_token": id_to_token[case["truth_ids"][0]], "final_token_id": current[i][case["positions"][0]],
214
+ "final_token": id_to_token[current[i][case["positions"][0]]], "abstained": unresolved[i], "rounds": histories[i]}
215
+ for i, (record, case) in enumerate(zip(records, cases))]
216
+ for result, expected in zip(results, reference["cases"]):
217
+ for key in ("dataset", "case_id", "wa3_position", "truth_token_id", "final_token_id", "abstained"):
218
+ require(result[key] == expected[key], "Reference result differs: " + key)
219
+ require(len(result["rounds"]) == len(expected["rounds"]), "Reference round count differs")
220
+ for actual_round, expected_round in zip(result["rounds"], expected["rounds"]):
221
+ require(actual_round["accepted"] == expected_round["accepted"], "Reference acceptance differs")
222
+ require([x["token_id"] for x in actual_round["top10"]] == [x["token_id"] for x in expected_round["top10"]],
223
+ "Reference top-10 token order differs")
224
+ require(np.allclose([x["probability"] for x in actual_round["top10"]],
225
+ [x["probability"] for x in expected_round["top10"]],
226
+ atol=contract["probability_allclose_atol"], rtol=contract["probability_allclose_rtol"]),
227
+ "Reference probabilities differ")
228
+ return {"cases": results, "forward_calls": iteration + 1, "reference_comparisons": comparisons,
229
+ "neutral_vector_sha256_c_order_bytes": neutral_sha,
230
+ "neutral_vector_bitwise_reference_match": neutral_sha == reference["full_model"]["neutral_vector"]["sha256_c_order_bytes"],
231
+ "readout": {"sha256": metadata["readout"]["sha256"], "classes": len(payload["token_ids"]), "fit": metadata["fit"], "refit": False}}
232
+
233
+
234
+ def main():
235
+ parser = argparse.ArgumentParser(description=__doc__)
236
+ parser.add_argument("--config", type=Path, default=ROOT / "config.json")
237
+ parser.add_argument("--source-root", type=Path, help="Released bertose_source root; preserves its relative layout")
238
+ parser.add_argument("--readout-root", type=Path, help="Released iar_completion root")
239
+ parser.add_argument("--checkpoint", type=Path, help="Full pinned WA3 review_md20x6_checkpoint_best.pt")
240
+ parser.add_argument("--output-dir", type=Path, help="New directory for report and two hidden feature arrays")
241
+ parser.add_argument("--threads", type=int, default=4, choices=range(1, 9))
242
+ parser.add_argument("--preflight-only", action="store_true", help="Small-file integrity and input checks only; no torch/checkpoint load")
243
+ args = parser.parse_args()
244
+ config, source, readout = read_config(args.config, args.source_root, args.readout_root)
245
+ records, verified = preflight(config, source, readout)
246
+ if args.preflight_only:
247
+ print(json.dumps({"status": "PASS_PREFLIGHT_ONLY", "verified_files": len(verified), "cases": len(records),
248
+ "checkpoint_loaded": False, "model_forward_executed": False}, indent=2))
249
+ return
250
+ if args.checkpoint is None or args.output_dir is None:
251
+ parser.error("--checkpoint and --output-dir are required for inference")
252
+ output = args.output_dir.absolute()
253
+ require(output.parent.is_dir() and not output.exists(), "Output directory must be new with an existing parent")
254
+ output.mkdir()
255
+ report = {"schema": "bertose_portable_two_case_iar_result_v1", "status": "RUNNING", "started_unix": time.time(),
256
+ "scope": config["scope"], "config_sha256": sha256(args.config), "inference_script_sha256": sha256(Path(__file__)),
257
+ "checkpoint_path": str(args.checkpoint.resolve()), "verified_files": verified,
258
+ "limits": ["Two recorded single-linkage inputs only; no general raw-WURCS conversion or corpus parity.",
259
+ "Physical modalities are explicitly absent; their active paths and residue/multitoken fallback are untested.",
260
+ "This is CPU inference, not a historical CUDA numerical replay or a model-quality estimate.",
261
+ "Original component/model guards are retained. Training/static-release launch bootstrap is outside this inference scope."]}
262
+ try:
263
+ import numpy as np
264
+ import torch
265
+ torch.set_num_threads(args.threads)
266
+ torch.set_num_interop_threads(1)
267
+ report["runtime"] = {"python": platform.python_version(), "torch": torch.__version__, "numpy": np.__version__,
268
+ "device": "cpu", "precision": "float32", "threads": args.threads, "host": platform.node()}
269
+ report["phase"] = "strict_full_checkpoint_load"
270
+ model, bindings = load_model(args.checkpoint, config, source)
271
+ report["full_model"] = {"state_entries": len(model.state_dict()), "parameters": sum(p.numel() for p in model.parameters()),
272
+ "strict_load": True, "checkpoint_sha256": config["checkpoint"]["sha256"],
273
+ "component_authorities": bindings}
274
+ report["phase"] = "two_recorded_case_inference_and_reference_comparison"
275
+ report.update(run_cases(model, records, config, readout, output))
276
+ report["status"] = "PASS_PORTABLE_FULL_WA3_TWO_CASE_REFERENCE_CHECK"
277
+ except (Exception, SystemExit) as error:
278
+ report["status"] = "FAIL"
279
+ report["error"] = type(error).__name__ + ": " + str(error)
280
+ raise
281
+ finally:
282
+ report["finished_unix"] = time.time()
283
+ with (output / "inference_report.json").open("x") as handle:
284
+ json.dump(report, handle, indent=2, allow_nan=False)
285
+ handle.write("\n")
286
+ print(json.dumps({"status": report["status"], "report": str(output / "inference_report.json")}, indent=2))
287
+
288
+
289
+ if __name__ == "__main__":
290
+ main()
releases/2026-10-02/iar_inference/original_inference_helpers.py ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Verbatim selected functions from the hash-bound final IAR source.
2
+
3
+ Only these inference helpers are copied. The original module's import-time open
4
+ replacement, Nova paths, fitting functions and main() are not imported or run.
5
+ See config.json for the original source pin and helper names.
6
+ """
7
+ import torch
8
+
9
+ HOOK = {"mask": None, "vec": None}
10
+ CAP = {"h": None}
11
+
12
+ def fail(msg: str):
13
+ raise SystemExit("FAIL-CLOSED: " + msg)
14
+
15
+
16
+ def all_absent(B, dev): # contract from iar_bytype3_20260825_r1/bytype.py (job 12094273)
17
+ return {"ms_token_ids": torch.empty((0, 64), dtype=torch.long, device=dev),
18
+ "ms_attention_mask": torch.zeros((0, 64), dtype=torch.bool, device=dev),
19
+ "ms_input_mask": torch.zeros((0, 64), dtype=torch.bool, device=dev), "ms_labels": None,
20
+ "ms_acquisition_parent_indices": torch.empty((0,), dtype=torch.long, device=dev),
21
+ "ms_parent_available": torch.zeros((B,), dtype=torch.bool, device=dev), "ms_mode": "development",
22
+ "static_token_ids": torch.zeros((B, 1, 4), dtype=torch.long, device=dev),
23
+ "static_attention_mask": torch.zeros((B, 1, 4), dtype=torch.bool, device=dev),
24
+ "static_residue_ids": torch.full((B, 1, 4), -1, dtype=torch.long, device=dev),
25
+ "static_slot_ids": torch.full((B, 1, 4), -1, dtype=torch.long, device=dev),
26
+ "static_state_mask": torch.zeros((B, 1), dtype=torch.bool, device=dev),
27
+ "static_state_ids": torch.full((B, 1), -1, dtype=torch.long, device=dev),
28
+ "static_input_mask": torch.zeros((B, 1, 4), dtype=torch.bool, device=dev), "static_labels": None,
29
+ "static_parent_available": torch.zeros((B,), dtype=torch.bool, device=dev),
30
+ "md_values": torch.zeros((B, 2, 20, 6), dtype=torch.float32, device=dev),
31
+ "md_feature_mask": torch.zeros((B, 2, 20, 6), dtype=torch.bool, device=dev),
32
+ "md_child_present_mask": torch.zeros((B, 2), dtype=torch.bool, device=dev),
33
+ "md_state_row_ids": torch.full((B, 2), -1, dtype=torch.long, device=dev),
34
+ "md_window_input_mask": torch.zeros((B, 2, 20), dtype=torch.bool, device=dev), "md_labels": None,
35
+ "md_parent_available": torch.zeros((B,), dtype=torch.bool, device=dev)}
36
+
37
+
38
+ def _linkage_hook(module, inputs, output):
39
+ m = HOOK["mask"]
40
+ if m is None:
41
+ return None
42
+ if output.dim() == 3 and tuple(output.shape[:2]) == tuple(m.shape):
43
+ idx = m
44
+ elif output.dim() == 2 and output.shape[0] == m.numel():
45
+ idx = m.reshape(-1)
46
+ else:
47
+ fail("linkage embedding output %s does not match mask %s" % (tuple(output.shape), tuple(m.shape)))
48
+ out = output.clone()
49
+ out[idx] = HOOK["vec"].to(out.dtype)
50
+ return out
51
+
52
+
53
+ def find_linkage_embedding(model):
54
+ cands = [(n, m) for n, m in model.named_modules() if isinstance(m, torch.nn.Embedding) and m.num_embeddings == 10]
55
+ named = [(n, m) for n, m in cands if "linkage" in n.lower()]
56
+ if len(named) != 1:
57
+ fail("linkage embedding not unique: %s" % [n for n, _ in cands])
58
+ return named[0]
59
+
60
+
61
+ def _cap_hook(module, inputs, output):
62
+ CAP["h"] = inputs[0].detach()
63
+
64
+
65
+ def find_lm_projection(model):
66
+ cands = [(n, m) for n, m in model.named_modules() if isinstance(m, torch.nn.Linear) and m.out_features == 2114]
67
+ if len(cands) > 1:
68
+ pref = [(n, m) for n, m in cands if any(k in n.lower() for k in ("seq", "mlm", "lm_", "sequence"))]
69
+ cands = pref if len(pref) == 1 else cands
70
+ if len(cands) != 1:
71
+ fail("LM projection not unique: %s" % [n for n, _ in cands])
72
+ return cands[0]
73
+
74
+
75
+ def _gather_h(B, L, bi, pos):
76
+ H = CAP["h"]
77
+ if H.dim() == 3 and H.shape[0] == B and H.shape[1] == L:
78
+ return H[bi, pos]
79
+ if H.dim() == 2 and H.shape[0] == B * L:
80
+ return H.reshape(B, L, -1)[bi, pos]
81
+ fail("unexpected LM-projection input shape %s for batch %d x %d" % (tuple(H.shape), B, L))
82
+
83
+
84
+ def _forward(model, dev, tok, am, rid, bd, lt, neutral_pos):
85
+ HOOK["mask"] = None
86
+ if neutral_pos is not None:
87
+ m = torch.zeros(tok.shape, dtype=torch.bool)
88
+ for bi, p in neutral_pos:
89
+ m[bi, p] = True
90
+ HOOK["mask"] = m.to(dev)
91
+ with torch.inference_mode():
92
+ o = model(seq_token_ids=tok.to(dev), seq_attention_mask=am.bool().to(dev), seq_residue_ids=rid.to(dev),
93
+ seq_branch_depths=bd.to(dev), seq_linkage_types=lt.to(dev),
94
+ seq_labels=None, topology_labels=None, topology_available=None,
95
+ **all_absent(tok.shape[0], torch.device(dev)))
96
+ HOOK["mask"] = None
97
+ return o.sequence_logits.float()
releases/2026-10-02/iar_inference/preparation_validation.json ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "schema": "iar_portable_preparation_validation_v1",
3
+ "status": "PASS_PREPARATION_ONLY_FULL_FORWARD_PENDING",
4
+ "checked_at_utc": "2026-09-30T21:33:17.598488+00:00",
5
+ "python": "3.12.4",
6
+ "syntax": "PASS_2_PYTHON_FILES",
7
+ "small_file_preflight": "PASS",
8
+ "verified_files": 24,
9
+ "recorded_cases": 2,
10
+ "helper_ast_equivalence": "PASS_8_FUNCTIONS",
11
+ "negative_integrity_check": "PASS_CHANGED_FIXTURE_PIN_REJECTED",
12
+ "checkpoint_loaded": false,
13
+ "model_constructed": false,
14
+ "model_forward_executed": false,
15
+ "remote_contact": false,
16
+ "limit": "This receipt validates source/fixture preparation only. Require the independent full-model portable smoke receipt before calling portable inference verified."
17
+ }
releases/2026-10-02/iar_inference/requirements.txt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ # Executed CPU reference versions (Python 3.10.16).
2
+ numpy==2.2.6
3
+ torch==2.9.1
releases/2026-10-02/notebooks/BERTose_workbench.ipynb ADDED
@@ -0,0 +1,583 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cells": [
3
+ {
4
+ "cell_type": "markdown",
5
+ "id": "f77c7ed4",
6
+ "metadata": {},
7
+ "source": [
8
+ "# BERTose workbench\n",
9
+ "\n",
10
+ "Turn WURCS 2.0 glycans into 768-dimensional sequence embeddings, inspect similarity, make a small PCA plot, and export data for your own analysis or application.\n",
11
+ "\n",
12
+ "This notebook uses the released **variant 3** encoder. You can choose variants 1–5. The weights were learned with multimodal supervision; this interface takes WURCS sequence inputs. It does not generate MS, static 3D, or MD inputs.\n",
13
+ "\n",
14
+ "A normal **Run All** embeds six shipped examples and checks the small IAR files. Binding and full IAR are separate opt-in sections. Similarity and PCA are exploratory views, with no fitted downstream head or model-quality claim.\n",
15
+ "\n",
16
+ "The notebook works in Colab, a local Jupyter kernel, or a backend Python environment. Start with a CPU kernel and allow memory for the encoder and activations. Downloads use public Hugging Face repositories; no token is needed for public access."
17
+ ]
18
+ },
19
+ {
20
+ "cell_type": "markdown",
21
+ "id": "86a15c72",
22
+ "metadata": {},
23
+ "source": [
24
+ "## 1. Get the versioned code\n",
25
+ "\n",
26
+ "The bootstrap downloads only `releases/2026-10-02/**` from the Hugging Face code repository. It does not fetch its older code tree or model weights.\n",
27
+ "\n",
28
+ "For an existing checkout, set `BERTose_CODE_DIR` to the directory containing `bertose_workbench.py`. For offline use, also set `BERTose_OFFLINE=1`, `BERTose_INSTALL=0`, and `BERTose_CHECKPOINT` to your downloaded variant checkpoint before starting the kernel. The offline path never installs packages or downloads files. Dependencies must already be available.\n",
29
+ "\n",
30
+ "You can pin code and encoder commits with `BERTose_CODE_REVISION` and `BERTose_ENCODER_REVISION`. A code snapshot and the selected encoder's hash checks provide separate provenance."
31
+ ]
32
+ },
33
+ {
34
+ "cell_type": "code",
35
+ "execution_count": null,
36
+ "id": "07f338df",
37
+ "metadata": {},
38
+ "outputs": [],
39
+ "source": [
40
+ "import os\n",
41
+ "import sys\n",
42
+ "import subprocess\n",
43
+ "import importlib.util\n",
44
+ "from pathlib import Path\n",
45
+ "\n",
46
+ "OFFLINE = os.environ.get(\"BERTose_OFFLINE\", \"0\") == \"1\"\n",
47
+ "INSTALL_DEPENDENCIES = os.environ.get(\"BERTose_INSTALL\", \"1\") == \"1\" and not OFFLINE\n",
48
+ "CODE_REVISION = os.environ.get(\"BERTose_CODE_REVISION\", \"main\")\n",
49
+ "ENCODER_REVISION = os.environ.get(\"BERTose_ENCODER_REVISION\", \"main\")\n",
50
+ "CODE_REPO = \"supanthadey1/bertose-affinose-training-code\"\n",
51
+ "RELEASE_PATH = \"releases/2026-10-02\"\n",
52
+ "\n",
53
+ "local_code = os.environ.get(\"BERTose_CODE_DIR\")\n",
54
+ "if local_code:\n",
55
+ " CODE_DIR = Path(local_code).expanduser().resolve()\n",
56
+ "else:\n",
57
+ " # A notebook opened from its downloaded code/notebooks folder can reuse it.\n",
58
+ " candidates = (Path.cwd(), Path.cwd().parent)\n",
59
+ " CODE_DIR = next((p.resolve() for p in candidates if (p / \"bertose_workbench.py\").is_file()), None)\n",
60
+ " if CODE_DIR is None:\n",
61
+ " if OFFLINE:\n",
62
+ " raise RuntimeError(\"Offline: set BERTose_CODE_DIR to the downloaded code directory.\")\n",
63
+ " if importlib.util.find_spec(\"huggingface_hub\") is None:\n",
64
+ " if not INSTALL_DEPENDENCIES:\n",
65
+ " raise ImportError(\"Install huggingface_hub or set BERTose_CODE_DIR to local code.\")\n",
66
+ " subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"huggingface_hub>=1.1,<2\"], check=True)\n",
67
+ " from huggingface_hub import snapshot_download\n",
68
+ " snapshot = Path(snapshot_download(\n",
69
+ " repo_id=CODE_REPO, repo_type=\"model\", revision=CODE_REVISION,\n",
70
+ " allow_patterns=[f\"{RELEASE_PATH}/**\"],\n",
71
+ " ))\n",
72
+ " CODE_DIR = snapshot / RELEASE_PATH\n",
73
+ "\n",
74
+ "if not (CODE_DIR / \"bertose_workbench.py\").is_file():\n",
75
+ " raise FileNotFoundError(f\"The workbench module is absent from {CODE_DIR}. Check the release revision.\")\n",
76
+ "sys.path.insert(0, str(CODE_DIR))\n",
77
+ "sys.dont_write_bytecode = True\n",
78
+ "print(\"Code directory:\", CODE_DIR)\n",
79
+ "print(\"Offline:\", OFFLINE, \"| package installation:\", INSTALL_DEPENDENCIES)"
80
+ ]
81
+ },
82
+ {
83
+ "cell_type": "markdown",
84
+ "id": "4b53e279",
85
+ "metadata": {},
86
+ "source": [
87
+ "## 2. Prepare the environment\n",
88
+ "\n",
89
+ "The following cell installs `requirements-workbench.txt` in the current kernel's Python when installation is enabled. That file adds Hugging Face access, plotting and notebook support to the encoder dependencies. Restart the kernel if Jupyter requests it after an installation. Installation errors stop the run."
90
+ ]
91
+ },
92
+ {
93
+ "cell_type": "code",
94
+ "execution_count": null,
95
+ "id": "ffe5b4ab",
96
+ "metadata": {},
97
+ "outputs": [],
98
+ "source": [
99
+ "if INSTALL_DEPENDENCIES:\n",
100
+ " subprocess.run([\n",
101
+ " sys.executable, \"-m\", \"pip\", \"install\", \"-r\",\n",
102
+ " str(CODE_DIR / \"requirements-workbench.txt\"),\n",
103
+ " ], check=True)\n",
104
+ "else:\n",
105
+ " print(\"Using the current environment; no packages installed.\")\n",
106
+ "\n",
107
+ "import csv\n",
108
+ "import json\n",
109
+ "import time\n",
110
+ "import shlex\n",
111
+ "import numpy as np\n",
112
+ "import torch\n",
113
+ "import matplotlib.pyplot as plt\n",
114
+ "from bertose_workbench import Encoder, cosine_similarity, nearest_neighbors, pca\n",
115
+ "\n",
116
+ "torch.set_num_threads(4)\n",
117
+ "VARIANT = \"3\"\n",
118
+ "DEVICE = \"cpu\" # Change to \"cuda\" only in a suitable local runtime.\n",
119
+ "BATCH_SIZE = 8\n",
120
+ "checkpoint_env = os.environ.get(\"BERTose_CHECKPOINT\")\n",
121
+ "LOCAL_CHECKPOINT = Path(checkpoint_env).expanduser().resolve() if checkpoint_env else None\n",
122
+ "if OFFLINE and LOCAL_CHECKPOINT is None:\n",
123
+ " raise RuntimeError(\"Offline: set BERTose_CHECKPOINT to the downloaded variant-3 sequence_encoder.pt.\")\n",
124
+ "\n",
125
+ "run_name = time.strftime(\"run_%Y%m%d_%H%M%S\") + f\"_{time.time_ns() % 1_000_000:06d}\"\n",
126
+ "OUTPUT_DIR = Path(os.environ.get(\"BERTose_OUTPUT_DIR\", str(Path.cwd() / \"bertose_outputs\" / run_name))).expanduser().resolve()\n",
127
+ "OUTPUT_DIR.mkdir(parents=True, exist_ok=True)\n",
128
+ "ASSETS_DIR = Path(os.environ.get(\"BERTose_ASSETS_DIR\", str(Path.cwd() / \"bertose_assets\"))).expanduser().resolve()\n",
129
+ "print({\"python\": sys.version.split()[0], \"torch\": torch.__version__, \"numpy\": np.__version__,\n",
130
+ " \"variant\": VARIANT, \"device\": DEVICE, \"batch_size\": BATCH_SIZE, \"threads\": torch.get_num_threads()})\n",
131
+ "print(\"Results:\", OUTPUT_DIR)"
132
+ ]
133
+ },
134
+ {
135
+ "cell_type": "markdown",
136
+ "id": "c8d734d5",
137
+ "metadata": {},
138
+ "source": [
139
+ "## 3. Load once, then embed batches\n",
140
+ "\n",
141
+ "`Encoder.from_pretrained` retrieves the selected sequence export from Hugging Face and verifies it against the release manifest. Supply `local_checkpoint=...` to use an existing file. The same API is usable in a Python script or backend service; load the encoder once per worker.\n",
142
+ "\n",
143
+ "`pooling=\"first\"` returns the START-envelope token state. `pooling=\"mean\"` averages all attended states, including START and END. These are representation choices, not trained binding scores."
144
+ ]
145
+ },
146
+ {
147
+ "cell_type": "code",
148
+ "execution_count": null,
149
+ "id": "d2339c0d",
150
+ "metadata": {},
151
+ "outputs": [],
152
+ "source": [
153
+ "encoder = Encoder.from_pretrained(\n",
154
+ " variant=VARIANT,\n",
155
+ " revision=ENCODER_REVISION,\n",
156
+ " device=DEVICE,\n",
157
+ " local_checkpoint=LOCAL_CHECKPOINT,\n",
158
+ ")\n",
159
+ "print(\"Encoder loaded.\")"
160
+ ]
161
+ },
162
+ {
163
+ "cell_type": "markdown",
164
+ "id": "7f45afc6",
165
+ "metadata": {},
166
+ "source": [
167
+ "## 4. Six real release inputs\n",
168
+ "\n",
169
+ "The first two WURCS strings come from `examples/glycans.csv`. Four distinct glycans come from the released held-out binding example CSVs. We keep their file and row provenance; these six examples are a demonstration, not a representative evaluation set.\n",
170
+ "\n",
171
+ "To try your own glycans, set `CUSTOM_SAMPLES` below to a list of dictionaries containing unique `sample_id` and `wurcs` strings. A single glycan is also supported; the notebook skips nearest-neighbor and PCA views until there are two or more inputs. Use WURCS 2.0, not IUPAC names or protein sequences. Inspect unknown-token and tree-channel diagnostics. The loader rejects overlength inputs rather than truncating them."
172
+ ]
173
+ },
174
+ {
175
+ "cell_type": "code",
176
+ "execution_count": null,
177
+ "id": "1f36c033",
178
+ "metadata": {},
179
+ "outputs": [],
180
+ "source": [
181
+ "CUSTOM_SAMPLES = None # Replace None with your list of sample_id/wurcs dictionaries.\n",
182
+ "samples = []\n",
183
+ "if CUSTOM_SAMPLES is None:\n",
184
+ " seen_wurcs = set()\n",
185
+ " example_sources = [\n",
186
+ " (\"examples/glycans.csv\", \"wurcs\"),\n",
187
+ " (\"bertosebind_inference/examples/glycan_cold.csv\", \"glycan_wurcs\"),\n",
188
+ " (\"bertosebind_inference/examples/protein_cold.csv\", \"glycan_wurcs\"),\n",
189
+ " ]\n",
190
+ " for relative_path, wurcs_column in example_sources:\n",
191
+ " with (CODE_DIR / relative_path).open(newline=\"\") as handle:\n",
192
+ " for source_row, row in enumerate(csv.DictReader(handle), start=2):\n",
193
+ " if row[wurcs_column] not in seen_wurcs:\n",
194
+ " seen_wurcs.add(row[wurcs_column])\n",
195
+ " samples.append({\n",
196
+ " \"sample_id\": row[\"sample_id\"], \"wurcs\": row[wurcs_column],\n",
197
+ " \"source_file\": relative_path, \"source_csv_row\": source_row,\n",
198
+ " })\n",
199
+ " assert len(samples) == 6\n",
200
+ "else:\n",
201
+ " samples = list(CUSTOM_SAMPLES)\n",
202
+ "if not samples:\n",
203
+ " raise ValueError(\"Provide at least one glycan input.\")\n",
204
+ "if any(not row.get(\"sample_id\") for row in samples) or len({row[\"sample_id\"] for row in samples}) != len(samples):\n",
205
+ " raise ValueError(\"Samples require unique, nonempty sample_id values.\")\n",
206
+ "for row in samples:\n",
207
+ " print(row[\"sample_id\"], \"\\n \", row[\"wurcs\"], \"\\n source:\", row.get(\"source_file\", \"custom input\"),\n",
208
+ " \"row\", row.get(\"source_csv_row\", \"\"))"
209
+ ]
210
+ },
211
+ {
212
+ "cell_type": "code",
213
+ "execution_count": null,
214
+ "id": "1114f6ab",
215
+ "metadata": {},
216
+ "outputs": [],
217
+ "source": [
218
+ "wurcs = [row[\"wurcs\"] for row in samples]\n",
219
+ "vectors, diagnostics = encoder.encode_with_diagnostics(\n",
220
+ " wurcs, batch_size=BATCH_SIZE, pooling=\"first\",\n",
221
+ ")\n",
222
+ "assert vectors.shape == (len(samples), 768) and np.isfinite(vectors).all()\n",
223
+ "print(\"Embedding shape:\", vectors.shape, \"| dtype:\", vectors.dtype)\n",
224
+ "for row, diagnostic in zip(samples, diagnostics):\n",
225
+ " print(row[\"sample_id\"], diagnostic)"
226
+ ]
227
+ },
228
+ {
229
+ "cell_type": "markdown",
230
+ "id": "1006eff2",
231
+ "metadata": {},
232
+ "source": [
233
+ "## 5. Compare representations\n",
234
+ "\n",
235
+ "A cosine value describes direction in this representation space. It is not a binding probability, chemical-equivalence test, or validated biological distance. Here the query is the second shipped example and its reference pool excludes the query itself."
236
+ ]
237
+ },
238
+ {
239
+ "cell_type": "code",
240
+ "execution_count": null,
241
+ "id": "712c2527",
242
+ "metadata": {},
243
+ "outputs": [],
244
+ "source": [
245
+ "similarity = cosine_similarity(vectors, vectors)\n",
246
+ "assert similarity.shape == (len(samples), len(samples))\n",
247
+ "print(\"Cosine matrix (rows/columns follow samples):\")\n",
248
+ "print(np.array2string(similarity, precision=4))\n",
249
+ "\n",
250
+ "neighbor_rows = []\n",
251
+ "query_index = min(1, len(samples) - 1)\n",
252
+ "reference_indices = np.asarray([i for i in range(len(samples)) if i != query_index])\n",
253
+ "if len(reference_indices):\n",
254
+ " neighbor_indices, neighbor_scores = nearest_neighbors(\n",
255
+ " vectors[query_index:query_index + 1], vectors[reference_indices], k=min(3, len(reference_indices)),\n",
256
+ " )\n",
257
+ " neighbor_rows = [{\n",
258
+ " \"query_id\": samples[query_index][\"sample_id\"], \"rank\": rank + 1,\n",
259
+ " \"neighbor_id\": samples[int(reference_indices[neighbor])][\"sample_id\"],\n",
260
+ " \"cosine\": float(score),\n",
261
+ " } for rank, (neighbor, score) in enumerate(zip(neighbor_indices[0], neighbor_scores[0]))]\n",
262
+ " for row in neighbor_rows:\n",
263
+ " print(row)\n",
264
+ "else:\n",
265
+ " print(\"Nearest neighbors need at least two inputs; the single embedding is ready.\")"
266
+ ]
267
+ },
268
+ {
269
+ "cell_type": "markdown",
270
+ "id": "3bf46f04",
271
+ "metadata": {},
272
+ "source": [
273
+ "## 6. A small exploratory PCA view\n",
274
+ "\n",
275
+ "PCA is fit only on these six embeddings. The plotted axes and neighborhoods change when the input set or pooling changes. This plot does not measure downstream performance or establish glycan classes."
276
+ ]
277
+ },
278
+ {
279
+ "cell_type": "code",
280
+ "execution_count": null,
281
+ "id": "b97028b5",
282
+ "metadata": {},
283
+ "outputs": [],
284
+ "source": [
285
+ "coordinates = None\n",
286
+ "if len(samples) >= 2:\n",
287
+ " coordinates = pca(vectors, n_components=2)\n",
288
+ " assert coordinates.shape == (len(samples), 2) and np.isfinite(coordinates).all()\n",
289
+ " fig, (ax, key_ax) = plt.subplots(1, 2, figsize=(10, 5), gridspec_kw={\"width_ratios\": [3, 2]})\n",
290
+ " ax.scatter(coordinates[:, 0], coordinates[:, 1], s=60, color=\"#4C78A8\")\n",
291
+ " for i, row in enumerate(samples):\n",
292
+ " ax.annotate(str(i + 1), coordinates[i], xytext=(5, 5), textcoords=\"offset points\", fontsize=10)\n",
293
+ " ax.set(xlabel=\"PC 1\", ylabel=\"PC 2\")\n",
294
+ " ax.grid(alpha=0.2)\n",
295
+ " key_ax.axis(\"off\")\n",
296
+ " key_ax.text(0.0, 0.95, \"Sample key\", va=\"top\", fontsize=11, fontweight=\"bold\")\n",
297
+ " key_ax.text(0.0, 0.85, \"\\n\".join(f\"{i + 1}. {row['sample_id']}\" for i, row in enumerate(samples)),\n",
298
+ " va=\"top\", fontsize=9, linespacing=1.8)\n",
299
+ " key_ax.text(0.0, 0.25, \"Exploratory representation view\", fontsize=9, color=\"0.35\")\n",
300
+ " fig.suptitle(f\"{len(samples)} glycan inputs · variant {VARIANT} · first-token pooling\", fontsize=13)\n",
301
+ " fig.tight_layout(rect=[0, 0, 1, 0.95])\n",
302
+ " fig.savefig(OUTPUT_DIR / \"pca.png\", dpi=180, bbox_inches=\"tight\")\n",
303
+ " plt.show()\n",
304
+ "else:\n",
305
+ " print(\"A two-axis PCA view needs at least two inputs; the single embedding is ready.\")"
306
+ ]
307
+ },
308
+ {
309
+ "cell_type": "markdown",
310
+ "id": "f1ee2c41",
311
+ "metadata": {},
312
+ "source": [
313
+ "## 7. Save inputs, vectors and provenance\n",
314
+ "\n",
315
+ "The NPZ stores all 768 columns plus sample IDs and WURCS. CSV files store the same vectors, PCA coordinates and nearest neighbors. A JSON sidecar records the selected settings, input provenance and diagnostics. These files can be read by another notebook or a backend collaborator."
316
+ ]
317
+ },
318
+ {
319
+ "cell_type": "code",
320
+ "execution_count": null,
321
+ "id": "d24de9e3",
322
+ "metadata": {},
323
+ "outputs": [],
324
+ "source": [
325
+ "input_csv = OUTPUT_DIR / \"glycans.csv\"\n",
326
+ "with input_csv.open(\"w\", newline=\"\") as handle:\n",
327
+ " writer = csv.DictWriter(handle, fieldnames=[\"sample_id\", \"wurcs\"])\n",
328
+ " writer.writeheader()\n",
329
+ " writer.writerows({k: row[k] for k in writer.fieldnames} for row in samples)\n",
330
+ "\n",
331
+ "np.savez_compressed(\n",
332
+ " OUTPUT_DIR / \"embeddings.npz\",\n",
333
+ " sample_id=np.asarray([row[\"sample_id\"] for row in samples]),\n",
334
+ " wurcs=np.asarray(wurcs), embeddings=vectors,\n",
335
+ ")\n",
336
+ "with (OUTPUT_DIR / \"embeddings.csv\").open(\"w\", newline=\"\") as handle:\n",
337
+ " writer = csv.writer(handle)\n",
338
+ " writer.writerow([\"sample_id\"] + [f\"embedding_{i}\" for i in range(vectors.shape[1])])\n",
339
+ " writer.writerows([row[\"sample_id\"], *vector.tolist()] for row, vector in zip(samples, vectors))\n",
340
+ "if coordinates is not None:\n",
341
+ " with (OUTPUT_DIR / \"pca.csv\").open(\"w\", newline=\"\") as handle:\n",
342
+ " writer = csv.writer(handle)\n",
343
+ " writer.writerow([\"sample_id\", \"pc1\", \"pc2\"])\n",
344
+ " writer.writerows([row[\"sample_id\"], *point.tolist()] for row, point in zip(samples, coordinates))\n",
345
+ "with (OUTPUT_DIR / \"neighbors.csv\").open(\"w\", newline=\"\") as handle:\n",
346
+ " writer = csv.DictWriter(handle, fieldnames=[\"query_id\", \"rank\", \"neighbor_id\", \"cosine\"])\n",
347
+ " writer.writeheader()\n",
348
+ " writer.writerows(neighbor_rows)\n",
349
+ "\n",
350
+ "run_metadata = {\n",
351
+ " \"code_repo\": CODE_REPO, \"code_revision_requested\": CODE_REVISION, \"code_dir\": str(CODE_DIR),\n",
352
+ " \"encoder_revision_requested\": ENCODER_REVISION, \"variant\": VARIANT, \"device\": DEVICE,\n",
353
+ " \"checkpoint\": str(encoder.checkpoint), \"encoder_metadata\": encoder.metadata,\n",
354
+ " \"pooling\": \"first\", \"batch_size\": BATCH_SIZE, \"shape\": list(vectors.shape),\n",
355
+ " \"samples\": samples, \"diagnostics\": diagnostics,\n",
356
+ " \"scope\": \"Sequence embeddings and exploratory views; no fitted task head or benchmark evaluation.\",\n",
357
+ "}\n",
358
+ "(OUTPUT_DIR / \"run.json\").write_text(json.dumps(run_metadata, indent=2, allow_nan=False) + \"\\n\")\n",
359
+ "reloaded = np.load(OUTPUT_DIR / \"embeddings.npz\", allow_pickle=False)\n",
360
+ "assert np.array_equal(reloaded[\"embeddings\"], vectors)\n",
361
+ "print(\"Wrote:\", \", \".join(sorted(p.name for p in OUTPUT_DIR.iterdir())))"
362
+ ]
363
+ },
364
+ {
365
+ "cell_type": "markdown",
366
+ "id": "d1fee9ab",
367
+ "metadata": {},
368
+ "source": [
369
+ "## 8. Reuse the API in an application\n",
370
+ "\n",
371
+ "This is an ordinary Python function: adapt its dictionary response to your web framework. The shared `encoder` is already loaded. Preserve diagnostics and model settings in application outputs. Do not call `from_pretrained` for every request."
372
+ ]
373
+ },
374
+ {
375
+ "cell_type": "code",
376
+ "execution_count": null,
377
+ "id": "25b08f63",
378
+ "metadata": {},
379
+ "outputs": [],
380
+ "source": [
381
+ "def embed_records(records, pooling=\"first\"):\n",
382
+ " matrix, info = encoder.encode_with_diagnostics(\n",
383
+ " [row[\"wurcs\"] for row in records], batch_size=BATCH_SIZE, pooling=pooling,\n",
384
+ " )\n",
385
+ " return {\n",
386
+ " \"variant\": VARIANT, \"pooling\": pooling, \"dimensions\": int(matrix.shape[1]),\n",
387
+ " \"results\": [{\"sample_id\": row[\"sample_id\"], \"embedding\": vector.tolist(), \"diagnostics\": diagnostic}\n",
388
+ " for row, vector, diagnostic in zip(records, matrix, info)],\n",
389
+ " }\n",
390
+ "\n",
391
+ "# One real request, using mean pooling to demonstrate the alternative.\n",
392
+ "api_response = embed_records(samples[:1], pooling=\"mean\")\n",
393
+ "assert len(api_response[\"results\"][0][\"embedding\"]) == 768\n",
394
+ "print({\"variant\": api_response[\"variant\"], \"pooling\": api_response[\"pooling\"],\n",
395
+ " \"dimensions\": api_response[\"dimensions\"], \"sample_id\": api_response[\"results\"][0][\"sample_id\"],\n",
396
+ " \"diagnostics\": api_response[\"results\"][0][\"diagnostics\"]})"
397
+ ]
398
+ },
399
+ {
400
+ "cell_type": "markdown",
401
+ "id": "5cf5688a",
402
+ "metadata": {},
403
+ "source": [
404
+ "## 9. Embed an input CSV from the terminal\n",
405
+ "\n",
406
+ "`embed.py` is the released CSV interface. Input columns are `sample_id,wurcs`; sample IDs must be unique. The optional cell below runs it on our saved input and compares its vectors with the API output. The CLI requires a new output path and writes a JSON sidecar. It reuses the already verified checkpoint loaded by the API."
407
+ ]
408
+ },
409
+ {
410
+ "cell_type": "code",
411
+ "execution_count": null,
412
+ "id": "ea4276f0",
413
+ "metadata": {},
414
+ "outputs": [],
415
+ "source": [
416
+ "RUN_EMBED_CLI = False # Set True to execute the CLI demonstration.\n",
417
+ "if RUN_EMBED_CLI:\n",
418
+ " cli_checkpoint = encoder.checkpoint\n",
419
+ " cli_output = OUTPUT_DIR / f\"cli_embeddings_{time.time_ns()}.npz\"\n",
420
+ " cli_command = [\n",
421
+ " sys.executable, str(CODE_DIR / \"embed.py\"), \"--checkpoint\", str(cli_checkpoint),\n",
422
+ " \"--variant\", VARIANT, \"--input\", str(input_csv), \"--output\", str(cli_output),\n",
423
+ " \"--pooling\", \"first\", \"--batch-size\", str(BATCH_SIZE), \"--device\", DEVICE, \"--threads\", \"4\",\n",
424
+ " ]\n",
425
+ " print(shlex.join(cli_command))\n",
426
+ " subprocess.run(cli_command, check=True, cwd=CODE_DIR)\n",
427
+ " cli_vectors = np.load(cli_output, allow_pickle=False)[\"embeddings\"]\n",
428
+ " assert np.allclose(cli_vectors, vectors, atol=1e-6, rtol=1e-6)\n",
429
+ " print(\"CLI/API embeddings agree.\")\n",
430
+ "else:\n",
431
+ " print(\"CLI example is ready; set RUN_EMBED_CLI=True to run it.\")"
432
+ ]
433
+ },
434
+ {
435
+ "cell_type": "markdown",
436
+ "id": "d932e5ba",
437
+ "metadata": {},
438
+ "source": [
439
+ "## 10. Optional: fitted BERToseBind predictions\n",
440
+ "\n",
441
+ "Set `RUN_BINDING=True` only when you want to download the fitted checkpoint, eight pinned glycan feature/index files and the **2.33 GB protein HDF5 cache**. The downloader fetches the selected fit, not all nine. The default `mo_A_glycan_s42` uses the glycan-cold example CSV. All supported cell IDs and their regimes are in `bertosebind_inference/config.json`; the cell chooses the matching CSV from that configuration.\n",
442
+ "\n",
443
+ "The runnable interface supports pairs already represented in the pinned released glycan features and protein cache. It does not build inputs for arbitrary new proteins/glycans. Its regression score is not a calibrated binding probability or affinity constant. Two example predictions check loading; they do not evaluate model quality.\n",
444
+ "\n",
445
+ "To reuse downloaded assets offline, set `BERTose_BINDING_PATHS` to the downloader's `paths_binding.json`. Paths can point outside the notebook folder. This optional section may require `requirements-analysis.txt` (`h5py` and `pandas`); installation stays off in offline mode."
446
+ ]
447
+ },
448
+ {
449
+ "cell_type": "code",
450
+ "execution_count": null,
451
+ "id": "5b5397f3",
452
+ "metadata": {},
453
+ "outputs": [],
454
+ "source": [
455
+ "RUN_BINDING = False # Explicit opt-in to the large binding assets and computation.\n",
456
+ "BINDING_CELL_ID = \"mo_A_glycan_s42\"\n",
457
+ "binding_config = json.loads((CODE_DIR / \"bertosebind_inference/config.json\").read_text())\n",
458
+ "if BINDING_CELL_ID not in binding_config[\"checkpoints\"]:\n",
459
+ " raise ValueError(\"Choose a cell ID from bertosebind_inference/config.json.\")\n",
460
+ "regime = binding_config[\"checkpoints\"][BINDING_CELL_ID][\"regime\"]\n",
461
+ "example_name = {\"test_glycan_cold\": \"glycan_cold.csv\", \"test_protein_cold\": \"protein_cold.csv\",\n",
462
+ " \"test_doubly_cold\": \"doubly_cold.csv\"}[regime]\n",
463
+ "binding_pairs = CODE_DIR / \"bertosebind_inference/examples\" / example_name\n",
464
+ "\n",
465
+ "if RUN_BINDING:\n",
466
+ " if INSTALL_DEPENDENCIES:\n",
467
+ " subprocess.run([sys.executable, \"-m\", \"pip\", \"install\", \"-r\", str(CODE_DIR / \"requirements-analysis.txt\")], check=True)\n",
468
+ " binding_paths_file = Path(os.environ.get(\"BERTose_BINDING_PATHS\", str(ASSETS_DIR / \"paths_binding.json\"))).expanduser()\n",
469
+ " if not binding_paths_file.is_file():\n",
470
+ " if OFFLINE:\n",
471
+ " raise FileNotFoundError(\"Offline binding: provide BERTose_BINDING_PATHS with existing assets.\")\n",
472
+ " subprocess.run([\n",
473
+ " sys.executable, str(CODE_DIR / \"download_assets.py\"), \"--task\", \"binding\",\n",
474
+ " \"--cell-id\", BINDING_CELL_ID, \"--output-dir\", str(ASSETS_DIR),\n",
475
+ " ], check=True)\n",
476
+ " binding_paths = json.loads(binding_paths_file.read_text())\n",
477
+ " if binding_paths[\"cell_id\"] != BINDING_CELL_ID:\n",
478
+ " raise ValueError(\"The downloaded checkpoint cell ID does not match BINDING_CELL_ID.\")\n",
479
+ " binding_output = OUTPUT_DIR / f\"binding_{BINDING_CELL_ID}_{time.time_ns()}.json\"\n",
480
+ " binding_command = [\n",
481
+ " sys.executable, \"-m\", \"bertosebind_inference.predict\",\n",
482
+ " \"--checkpoint\", binding_paths[\"checkpoint\"], \"--cell-id\", BINDING_CELL_ID,\n",
483
+ " \"--pairs\", str(binding_pairs), \"--features-dir\", binding_paths[\"features_dir\"],\n",
484
+ " \"--protein-h5\", binding_paths[\"protein_h5\"], \"--batch-size\", \"8\", \"--threads\", \"4\",\n",
485
+ " \"--output\", str(binding_output),\n",
486
+ " ]\n",
487
+ " print(shlex.join(binding_command))\n",
488
+ " subprocess.run(binding_command, check=True, cwd=CODE_DIR)\n",
489
+ " binding_result = json.loads(binding_output.read_text())\n",
490
+ " print(\"Status:\", binding_result[\"status\"])\n",
491
+ " for prediction in binding_result[\"predictions\"]:\n",
492
+ " print({key: prediction[key] for key in (\"sample_id\", \"protein_id\", \"prediction\")})\n",
493
+ "else:\n",
494
+ " print(\"Binding remains off. Selected fit:\", BINDING_CELL_ID, \"| regime:\", regime)\n",
495
+ " print(\"Pairs:\", binding_pairs)"
496
+ ]
497
+ },
498
+ {
499
+ "cell_type": "markdown",
500
+ "id": "df1613ea",
501
+ "metadata": {},
502
+ "source": [
503
+ "## 11. Optional: IAR on the two released cases\n",
504
+ "\n",
505
+ "The released IAR runner accepts exactly two byte-pinned single-linkage fixtures: one benchmark case and one grey-seal case. Raw WURCS is retained as provenance; the runner does not convert arbitrary user WURCS into new masking cases. It uses the full frozen WA3 checkpoint and saved linkage readout with physical modality carriers absent.\n",
506
+ "\n",
507
+ "The small-file preflight below is safe to run with the notebook. It verifies source, metadata and fixture bytes; it does not load the full checkpoint or execute a model forward. Setting `RUN_IAR=True` separately downloads the full checkpoint and runs both recorded cases. Allow several GB of memory. It writes a new result directory and compares hidden features and predictions against the released CPU reference. The report states acceptance or abstention for each case; passing these checks is not completion-accuracy evaluation.\n",
508
+ "\n",
509
+ "For offline inference, set `BERTose_IAR_PATHS` to an existing `paths_iar.json` produced by the downloader."
510
+ ]
511
+ },
512
+ {
513
+ "cell_type": "code",
514
+ "execution_count": null,
515
+ "id": "f20a25f6",
516
+ "metadata": {},
517
+ "outputs": [],
518
+ "source": [
519
+ "RUN_IAR = False # Explicit opt-in to the full checkpoint and two model cases.\n",
520
+ "preflight_command = [sys.executable, str(CODE_DIR / \"iar_inference/infer.py\"), \"--preflight-only\"]\n",
521
+ "preflight = subprocess.run(preflight_command, check=True, text=True, capture_output=True, cwd=CODE_DIR)\n",
522
+ "print(preflight.stdout)\n",
523
+ "\n",
524
+ "if RUN_IAR:\n",
525
+ " iar_paths_file = Path(os.environ.get(\"BERTose_IAR_PATHS\", str(ASSETS_DIR / \"paths_iar.json\"))).expanduser()\n",
526
+ " if not iar_paths_file.is_file():\n",
527
+ " if OFFLINE:\n",
528
+ " raise FileNotFoundError(\"Offline IAR: provide BERTose_IAR_PATHS with the existing full checkpoint.\")\n",
529
+ " subprocess.run([\n",
530
+ " sys.executable, str(CODE_DIR / \"download_assets.py\"), \"--task\", \"iar\",\n",
531
+ " \"--output-dir\", str(ASSETS_DIR),\n",
532
+ " ], check=True)\n",
533
+ " iar_paths = json.loads(iar_paths_file.read_text())\n",
534
+ " iar_output = OUTPUT_DIR / f\"iar_two_cases_{time.time_ns()}\"\n",
535
+ " iar_command = [\n",
536
+ " sys.executable, str(CODE_DIR / \"iar_inference/infer.py\"),\n",
537
+ " \"--checkpoint\", iar_paths[\"checkpoint\"], \"--output-dir\", str(iar_output), \"--threads\", \"4\",\n",
538
+ " ]\n",
539
+ " print(shlex.join(iar_command))\n",
540
+ " subprocess.run(iar_command, check=True, cwd=CODE_DIR)\n",
541
+ " iar_result = json.loads((iar_output / \"inference_report.json\").read_text())\n",
542
+ " print(\"Status:\", iar_result[\"status\"])\n",
543
+ " for case in iar_result[\"cases\"]:\n",
544
+ " print({key: case[key] for key in (\"dataset\", \"case_id\", \"final_token\", \"abstained\")})\n",
545
+ "else:\n",
546
+ " print(\"Full IAR remains off; only the small-file preflight ran.\")"
547
+ ]
548
+ },
549
+ {
550
+ "cell_type": "markdown",
551
+ "id": "aa091d22",
552
+ "metadata": {},
553
+ "source": [
554
+ "## Continue with your inputs\n",
555
+ "\n",
556
+ "- Start with a small WURCS list, inspect diagnostics, then increase the batch size according to memory.\n",
557
+ "- Keep pooling and encoder variant fixed when comparing vectors. Build a reference matrix once and reuse `nearest_neighbors(query_vectors, reference_vectors, k=...)`.\n",
558
+ "- Use `embeddings.npz` for analysis and `embed_records` for an application response. For a CSV pipeline, use the executed `embed.py` interface above.\n",
559
+ "- Use the separate binding and IAR package READMEs for their fixed-input contracts. The embedding API supports new WURCS inputs; the shipped binding/IAR examples have narrower input support."
560
+ ]
561
+ }
562
+ ],
563
+ "metadata": {
564
+ "bertose_workbench": {
565
+ "default_batch_size": 8,
566
+ "default_device": "cpu",
567
+ "default_threads": 4,
568
+ "distributed_outputs": "stripped",
569
+ "release": "2026-10-02"
570
+ },
571
+ "kernelspec": {
572
+ "display_name": "Python 3",
573
+ "language": "python",
574
+ "name": "python3"
575
+ },
576
+ "language_info": {
577
+ "name": "python",
578
+ "version": "3.12"
579
+ }
580
+ },
581
+ "nbformat": 4,
582
+ "nbformat_minor": 5
583
+ }
releases/2026-10-02/provenance/WA3_checkpoint_index.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"cell_id":"A1111","generation":73,"index_sha256":"249bcca15c06644843070cd47c1d62520d2b5cf00e9533ed65311227040ead33","roles":{"best":{"development_loss":0.38572948901364407,"epoch":58,"filename":"review_md20x6_checkpoint_best.pt","sha256":"8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82"},"latest":{"development_loss":0.385906232017773,"epoch":73,"filename":"review_md20x6_checkpoint_latest.pt","sha256":"1a2d27f26d585ffedf67518f3fc769e9be1c71d5943a8d0c9ae3f75d7dd62f30"},"milestones":{"25":{"development_loss":0.39812925026342416,"epoch":25,"filename":"review_md20x6_checkpoint_milestone_025.pt","sha256":"08f5e9e744edbc555191949ddc56fc6d793d61572e0bee434eb50135f539e790"},"50":{"development_loss":0.38745878758199537,"epoch":50,"filename":"review_md20x6_checkpoint_milestone_050.pt","sha256":"1026b01bb50beaaaaa30354eb7b2bcd5307d82689f1f38a4b6d382b1e3b77ffa"}}},"run_identity_sha256":"55dd7ff33d2196cb1da5e215adb79b0880249f8f1e8c541a7fe36f5759c369d5","schema":"bertose_review_md_temporal_20x6_checkpoint_index_20260802_r3"}
releases/2026-10-02/provenance/WA3_final_receipt.json ADDED
@@ -0,0 +1 @@
 
 
1
+ {"development_history":[{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.22662700572537933,"ms":0.8557433197062682,"sequence":0.3059137519524717,"static":0.7551767198470175,"topology":0.030460597622770504},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.25215765373639587,"ms":2.5724487456782112,"sequence":1.5196248877762957,"static":2.5891844269759083,"topology":0.06376307786672697},"total_normalized_loss":0.5028213712413956,"weighted_normalized_losses":{"md":0.45325401145075866,"ms":1.7114866394125363,"sequence":0.15295687597623586,"static":0.18879417996175438,"topology":0.007615149405692626}},"epoch":1,"improved":true,"step_identity_chain_sha256":"8349369e088705ef87b62fa7c6ce3541d2dc06f3988bad23622cfab4bf777499"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.2136726617028679,"ms":0.8150696210605691,"sequence":0.23423114401117145,"static":0.6901099233497171,"topology":0.019501219778012342},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2377439390780074,"ms":2.450179599482438,"sequence":1.1635419253299568,"static":2.366097655659455,"topology":0.04082184501436121},"total_normalized_loss":0.47040058466287843,"weighted_normalized_losses":{"md":0.4273453234057358,"ms":1.6301392421211383,"sequence":0.11711557200558573,"static":0.17252748083742928,"topology":0.0048753049445030856}},"epoch":2,"improved":true,"step_identity_chain_sha256":"962391a900077917fa24893d4e57fa974e048d4e7b2f1840019ebb021dea6002"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.20491723994564162,"ms":0.7871846769190306,"sequence":0.19421473235822365,"static":0.6489628202681939,"topology":0.04007295908558098},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.22800217595181557,"ms":2.3663547095553628,"sequence":0.9647606195560059,"static":2.225021486712623,"topology":0.08388460535698647},"total_normalized_loss":0.45071402894938,"weighted_normalized_losses":{"md":0.40983447989128324,"ms":1.5743693538380612,"sequence":0.09710736617911182,"static":0.16224070506704846,"topology":0.010018239771395245}},"epoch":3,"improved":true,"step_identity_chain_sha256":"3aa01b318689b035ac47537fb3364c6b32599626130c2aae40ec5ca10d2f10b1"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.20059318098000678,"ms":0.7833350264236384,"sequence":0.1740526846396435,"static":0.6144367794664927,"topology":0.010871273858649505},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.22319099045385438,"ms":2.3547822808140313,"sequence":0.8646057579123623,"static":2.1066461649905643,"topology":0.022756805041848484},"total_normalized_loss":0.44224195409167955,"weighted_normalized_losses":{"md":0.40118636196001356,"ms":1.5666700528472768,"sequence":0.08702634231982175,"static":0.15360919486662317,"topology":0.002717818464662376}},"epoch":4,"improved":true,"step_identity_chain_sha256":"be59cdbbc21e7f36a63f3d33c56c24586746adb6784ae7c3896a5d797cd60d85"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19842932505038918,"ms":0.7702185946671242,"sequence":0.16123126231503135,"static":0.5864062674323184,"topology":0.02171640669463639},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.22078336549984892,"ms":2.3153529944347926,"sequence":0.800915412719256,"static":2.010541288699162,"topology":0.04545888915917031},"total_normalized_loss":0.43398842782485625,"weighted_normalized_losses":{"md":0.39685865010077837,"ms":1.5404371893342483,"sequence":0.08061563115751567,"static":0.1466015668580796,"topology":0.005429101673659098}},"epoch":5,"improved":true,"step_identity_chain_sha256":"e90df1029db9d0ff865d415bd64e8e26a458506a2824dd0ed00c2583be939787"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1962911658604727,"ms":0.767122882303372,"sequence":0.15398089224489517,"static":0.5765596184077477,"topology":0.010398237550739037},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21840433214979185,"ms":2.3060469780117274,"sequence":0.7648992390956554,"static":1.9767812565871012,"topology":0.021766599553807216},"total_normalized_loss":0.43011160128795173,"weighted_normalized_losses":{"md":0.3925823317209454,"ms":1.534245764606744,"sequence":0.07699044612244758,"static":0.14413990460193693,"topology":0.0025995593876847593}},"epoch":6,"improved":true,"step_identity_chain_sha256":"842cfd5af25d82c036c7e15af1643caedbf3bb83562d3a55144a041c438a9506"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.194018423453635,"ms":0.7636756649092812,"sequence":0.14634798267436108,"static":0.5698844597821273,"topology":0.0223091175846953},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21587555412079626,"ms":2.295684302829463,"sequence":0.7269828025984444,"static":1.9538949356680038,"topology":0.046699609091960845},"total_normalized_loss":0.4273221124809437,"weighted_normalized_losses":{"md":0.38803684690727,"ms":1.5273513298185624,"sequence":0.07317399133718054,"static":0.14247111494553183,"topology":0.005577279396173825}},"epoch":7,"improved":true,"step_identity_chain_sha256":"7c71e534e6ef7cb575a5ad296663a160fb3f0bcf8d5bbbce2a9596e3ed7174bc"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1930169839550213,"ms":0.7558749452810657,"sequence":0.1399491315920143,"static":0.5620648833879897,"topology":0.00981988587483681},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21476129753199683,"ms":2.272234570928689,"sequence":0.6951965448841485,"static":1.9270848859231555,"topology":0.02055593772104861},"total_normalized_loss":0.4221459233167775,"weighted_normalized_losses":{"md":0.3860339679100426,"ms":1.5117498905621314,"sequence":0.06997456579600715,"static":0.14051622084699741,"topology":0.0024549714687092023}},"epoch":8,"improved":true,"step_identity_chain_sha256":"81ebd06b29d8386df109dbee8ba895db64884466fb61a76a710ee5a3a8ae6fd0"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19406822581608854,"ms":0.7518932710631684,"sequence":0.1362528402339793,"static":0.5518898818467565,"topology":0.009015226965808379},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21593096696458605,"ms":2.260265265867628,"sequence":0.6768352378023567,"static":1.8921990706660943,"topology":0.018871547634290067},"total_normalized_loss":0.420055138215729,"weighted_normalized_losses":{"md":0.3881364516321771,"ms":1.5037865421263368,"sequence":0.06812642011698965,"static":0.13797247046168912,"topology":0.0022538067414520947}},"epoch":9,"improved":true,"step_identity_chain_sha256":"6812ea9698a1e8fa8242d95e3c4df63df9f536847337f3fdba02ed782dd4e07f"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19102172991507627,"ms":0.7505529410653496,"sequence":0.13160407793951492,"static":0.548196846882865,"topology":0.007217244018334658},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21254126830066034,"ms":2.256236101815413,"sequence":0.6537425365591589,"static":1.8795372017743697,"topology":0.015107835309843994},"total_normalized_loss":0.4175609807311818,"weighted_normalized_losses":{"md":0.38204345983015253,"ms":1.5011058821306993,"sequence":0.06580203896975746,"static":0.13704921172071624,"topology":0.0018043110045836645}},"epoch":10,"improved":true,"step_identity_chain_sha256":"244f9ebf8f844fdc582f8b77d68d58f852ae14f87ac5110b30ede39a22e546c5"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19106956913570272,"ms":0.7422393981299115,"sequence":0.12959927503388055,"static":0.5440629670213283,"topology":0.006221932800593297},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21259449684503057,"ms":2.231244772518503,"sequence":0.643783689102829,"static":1.8653638605163814,"topology":0.013024353315681565},"total_normalized_loss":0.41379775940072977,"weighted_normalized_losses":{"md":0.38213913827140544,"ms":1.484478796259823,"sequence":0.06479963751694028,"static":0.13601574175533207,"topology":0.0015554832001483242}},"epoch":11,"improved":true,"step_identity_chain_sha256":"19faa5da708b534a9be44e7b7c20d35f2f66ac5d15d9d680284a7f8b692c7e9f"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19037402365003858,"ms":0.7366234966393759,"sequence":0.12822089536794967,"static":0.5452088458164462,"topology":0.008232884439421842},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21182059473583295,"ms":2.2143628192359004,"sequence":0.6369365956597113,"static":1.869292598589931,"topology":0.017233872364545984},"total_normalized_loss":0.4112931841653541,"weighted_normalized_losses":{"md":0.38074804730007716,"ms":1.4732469932787517,"sequence":0.06411044768397484,"static":0.13630221145411156,"topology":0.0020582211098554605}},"epoch":12,"improved":true,"step_identity_chain_sha256":"9db8b63da1b78fa49d70078c2a3b87a934a5cf570a6742755f3d6ce00885e1f7"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19041639201178032,"ms":0.736960530254102,"sequence":0.12604434886221993,"static":0.5334526507395191,"topology":0.005343901317552914},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2118677361021289,"ms":2.215375975493727,"sequence":0.6261246128102991,"static":1.8289855334835847,"topology":0.011186372639271922},"total_normalized_loss":0.41049503139542853,"weighted_normalized_losses":{"md":0.38083278402356063,"ms":1.473921060508204,"sequence":0.06302217443110997,"static":0.13336316268487977,"topology":0.0013359753293882286}},"epoch":13,"improved":true,"step_identity_chain_sha256":"41ffade6ef912d2969b95a2eedcc3a039106187cdbc564b05bb150a4b1926dea"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18864850129010685,"ms":0.7347006251585065,"sequence":0.125190681186743,"static":0.535602107175571,"topology":0.022280826714292268},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2099006837863086,"ms":2.208582478080844,"sequence":0.6218840233066719,"static":1.8363551186209766,"topology":0.04664038789759145},"total_normalized_loss":0.4097528653926128,"weighted_normalized_losses":{"md":0.3772970025802137,"ms":1.469401250317013,"sequence":0.0625953405933715,"static":0.13390052679389275,"topology":0.005570206678573067}},"epoch":14,"improved":true,"step_identity_chain_sha256":"e1fc779a32a5752f26e197664962d6285d2e86d09a0b301734ec06433cfced47"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18932230534558994,"ms":0.7300639699996679,"sequence":0.12059135975771769,"static":0.5375086685680924,"topology":0.005507952683596159},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2106503951861696,"ms":2.194644235768195,"sequence":0.5990369192918266,"static":1.8428919184677595,"topology":0.011529780872974427},"total_normalized_loss":0.4069644771764594,"weighted_normalized_losses":{"md":0.3786446106911799,"ms":1.4601279399993359,"sequence":0.060295679878858845,"static":0.1343771671420231,"topology":0.0013769881708990397}},"epoch":15,"improved":true,"step_identity_chain_sha256":"528261498391f3cb63bf1f013018798661c55d67e76113418f6d2a8c1e4ce07d"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18979667588370328,"ms":0.729257050377908,"sequence":0.1224965826079112,"static":0.5377193858019084,"topology":0.004572563830447886},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21117820590100242,"ms":2.192218555869727,"sequence":0.6085011033680101,"static":1.8436143795367423,"topology":0.009571734185329138},"total_normalized_loss":0.40698574624705347,"weighted_normalized_losses":{"md":0.37959335176740655,"ms":1.458514100755816,"sequence":0.0612482913039556,"static":0.1344298464504771,"topology":0.0011431409576119714}},"epoch":16,"improved":false,"step_identity_chain_sha256":"94ee181ed0af332478fbc91d6850ea1c5b9fb37055d602f6eb92ecb6f393c058"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.19044398384067177,"ms":0.7253422453133344,"sequence":0.12148093259013977,"static":0.5359335464438555,"topology":0.0063599683910022155},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21189843628639551,"ms":2.180450266072976,"sequence":0.6034558674659781,"static":1.837491484943424,"topology":0.013313302804087004},"total_normalized_loss":0.40557726066235933,"weighted_normalized_losses":{"md":0.38088796768134353,"ms":1.4506844906266687,"sequence":0.060740466295069885,"static":0.13398338661096387,"topology":0.0015899920977505539}},"epoch":17,"improved":true,"step_identity_chain_sha256":"accedf095414e081aba3585d10801cc7152637684e6b1e66d3ba0ae439dd611e"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1888921825736983,"ms":0.7306011329012926,"sequence":0.11935736189567756,"static":0.5428638884042807,"topology":0.00425405742116268},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.21017181696628076,"ms":2.1962590003835745,"sequence":0.5929070416689655,"static":1.86125272255306,"topology":0.0089050056717322},"total_normalized_loss":0.4070889596708363,"weighted_normalized_losses":{"md":0.3777843651473966,"ms":1.4612022658025852,"sequence":0.05967868094783878,"static":0.13571597210107017,"topology":0.00106351435529067}},"epoch":18,"improved":false,"step_identity_chain_sha256":"4748dc16e2e0fb741438ffffe8ea67358decacfada7422972ed9de47e448a950"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18853731601049878,"ms":0.7212504073037117,"sequence":0.11723436805777301,"static":0.5373463378322132,"topology":0.00529378593435542},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20977697293752312,"ms":2.1681497977982307,"sequence":0.5823610814037444,"static":1.8423353544181642,"topology":0.011081466257568041},"total_normalized_loss":0.40277053231979,"weighted_normalized_losses":{"md":0.37707463202099756,"ms":1.4425008146074234,"sequence":0.05861718402888651,"static":0.1343365844580533,"topology":0.001323446483588855}},"epoch":19,"improved":true,"step_identity_chain_sha256":"cf9daba2764d8263ffb79ca7f35556d573366151727cab1c57de3eb41a0b6b31"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18732470522153952,"ms":0.7195666013047894,"sequence":0.11674263354679772,"static":0.5369274246652657,"topology":0.00497569131779692},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20842775557280213,"ms":2.1630881110398934,"sequence":0.5799183929129941,"static":1.8408990767633984,"topology":0.010415599748453847},"total_normalized_loss":0.40152594176436446,"weighted_normalized_losses":{"md":0.37464941044307903,"ms":1.4391332026095789,"sequence":0.05837131677339886,"static":0.13423185616631642,"topology":0.00124392282944923}},"epoch":20,"improved":true,"step_identity_chain_sha256":"3528d61e9a4953b6aa0220d8c4b3496d374e71547d7f57b7064f92d2af5b2893"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1876363582716866,"ms":0.7190431341736717,"sequence":0.11718534196001842,"static":0.5390776018521963,"topology":0.004164038808132024},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2087745178734968,"ms":2.1615145172602657,"sequence":0.5821175445315936,"static":1.8482711330534378,"topology":0.008716569978407598},"total_normalized_loss":0.4015524132071616,"weighted_normalized_losses":{"md":0.3752727165433732,"ms":1.4380862683473434,"sequence":0.05859267098000921,"static":0.13476940046304908,"topology":0.001041009702033006}},"epoch":21,"improved":false,"step_identity_chain_sha256":"0478c6e9854ecd9b251c9e213bc36891fa8d4a8b44144d0effcccacf80043cb8"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18783271945591928,"ms":0.715846598873989,"sequence":0.11771536798749985,"static":0.5461228993038463,"topology":0.004379447172740652},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2089930001119332,"ms":2.151905417156502,"sequence":0.5847504459209208,"static":1.872426504856902,"topology":0.009167483663548718},"total_normalized_loss":0.4007683814545427,"weighted_normalized_losses":{"md":0.37566543891183857,"ms":1.431693197747978,"sequence":0.058857683993749924,"static":0.13653072482596157,"topology":0.001094861793185163}},"epoch":22,"improved":true,"step_identity_chain_sha256":"7ee0957a690bf9f5456ca77b9742b292ae6a5c51b43eff3cb19b9bd8cb8f8929"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1871767668245945,"ms":0.7103727853823291,"sequence":0.11628192666514102,"static":0.5459911714081273,"topology":0.0037820487306240874},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20826315118705482,"ms":2.135450594400152,"sequence":0.5776298340009903,"static":1.8719748651185064,"topology":0.007916951292060883},"total_normalized_loss":0.3981366745562211,"weighted_normalized_losses":{"md":0.374353533649189,"ms":1.4207455707646581,"sequence":0.05814096333257051,"static":0.1364977928520318,"topology":0.0009455121826560218}},"epoch":23,"improved":true,"step_identity_chain_sha256":"2c8d3171c46864ec94861d9996b8811b38ba0d322556d6934fce6395b6ff16c0"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18777199994128557,"ms":0.7049079101886564,"sequence":0.11539469749322528,"static":0.5454564029586294,"topology":0.0036034899744572567},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20892544024501863,"ms":2.1190226410483493,"sequence":0.5732225279475711,"static":1.8701413682626205,"topology":0.0075431747820182676},"total_normalized_loss":0.39606442844795364,"weighted_normalized_losses":{"md":0.37554399988257114,"ms":1.4098158203773128,"sequence":0.05769734874661264,"static":0.13636410073965735,"topology":0.0009008724936143142}},"epoch":24,"improved":true,"step_identity_chain_sha256":"f84a0cf966e10855452bfead8481542f5a9b20cde80b2192359d4f8e68cbf4f6"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1869681200818719,"ms":0.7099487995044106,"sequence":0.11643272414371737,"static":0.5502300342311925,"topology":0.00415416605959564},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20803099936147573,"ms":2.134176051070721,"sequence":0.5783789196501185,"static":1.886508148946153,"topology":0.008695903383435295},"total_normalized_loss":0.39812925026342416,"weighted_normalized_losses":{"md":0.3739362401637438,"ms":1.4198975990088212,"sequence":0.058216362071858686,"static":0.13755750855779814,"topology":0.00103854151489891}},"epoch":25,"improved":false,"step_identity_chain_sha256":"51dd88d5d4db95b3b32fbb7ac9b952ca723bf7e2c5eed0fa35fb5b19c05f868c"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18841552983824386,"ms":0.7033956170602036,"sequence":0.11585168737709899,"static":0.5479196650977544,"topology":0.003713389834364146},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2096414669533398,"ms":2.114476538879861,"sequence":0.5754926226934405,"static":1.8785868616187633,"topology":0.0077732278299449085},"total_normalized_loss":0.3958912802436948,"weighted_normalized_losses":{"md":0.3768310596764877,"ms":1.4067912341204072,"sequence":0.05792584368854949,"static":0.1369799162744386,"topology":0.0009283474585910365}},"epoch":26,"improved":true,"step_identity_chain_sha256":"ea75ef4544cd28fed4e054f41bad2fce6f86774be1051c0350b1e433d612ec03"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18669901981477316,"ms":0.7046144276012817,"sequence":0.11363491391429,"static":0.550115956922508,"topology":0.003343397025061905},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20773158362435176,"ms":2.1181404034703455,"sequence":0.564480812655002,"static":1.8861170256721502,"topology":0.006998722989237778},"total_normalized_loss":0.39556183805522943,"weighted_normalized_losses":{"md":0.3733980396295463,"ms":1.4092288552025634,"sequence":0.056817456957145,"static":0.137528989230627,"topology":0.0008358492562654762}},"epoch":27,"improved":true,"step_identity_chain_sha256":"ee247d44c437c3533ebc85d2582f2877b2d1a5562bc2d3391fed073f0ddedfee"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18680978057918643,"ms":0.7027360981756547,"sequence":0.11532498453928958,"static":0.5581590564718851,"topology":0.003439281182740489},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2078548221342156,"ms":2.11249396012261,"sequence":0.5728762292306127,"static":1.9136934426227188,"topology":0.0071994370096243885},"total_normalized_loss":0.3954307668385967,"weighted_normalized_losses":{"md":0.37361956115837286,"ms":1.4054721963513095,"sequence":0.05766249226964479,"static":0.13953976411797128,"topology":0.0008598202956851223}},"epoch":28,"improved":true,"step_identity_chain_sha256":"2e05d67d34765b01db30476b094440885b15faa753286e940942a504e246f993"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18611718220913312,"ms":0.701507074633941,"sequence":0.11576753504756795,"static":0.5507436567855164,"topology":0.0035271283558496297},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20708419914771276,"ms":2.108799394245805,"sequence":0.5750745964572758,"static":1.8882691453911553,"topology":0.007383327234258153},"total_normalized_loss":0.39433999549905474,"weighted_normalized_losses":{"md":0.37223436441826624,"ms":1.403014149267882,"sequence":0.05788376752378398,"static":0.1376859141963791,"topology":0.0008817820889624074}},"epoch":29,"improved":true,"step_identity_chain_sha256":"e34003c15ef687cafdece48f6eb4640a91a8cdb00f0fee6879a58624bb3874f0"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1870041062465378,"ms":0.6952488676497738,"sequence":0.11350901360033186,"static":0.5610069497587966,"topology":0.00359732086848505},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20807103954477196,"ms":2.0899866073552906,"sequence":0.5638554035347891,"static":1.9234576749598256,"topology":0.007530261010944391},"total_normalized_loss":0.3924823044499219,"weighted_normalized_losses":{"md":0.3740082124930756,"ms":1.3904977352995476,"sequence":0.05675450680016593,"static":0.14025173743969915,"topology":0.0008993302171212625}},"epoch":30,"improved":true,"step_identity_chain_sha256":"c94b9d5bf751a42a5761422aa4c67519abfe51e1c3e2ff7b2186a2c1943fcf9b"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18552926299512953,"ms":0.6984604122216393,"sequence":0.11633959795669416,"static":0.5623446920579128,"topology":0.0035183603877805185},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20643004793958472,"ms":2.0996408268102797,"sequence":0.5779163158259982,"static":1.9280442325656804,"topology":0.007364973272932529},"total_normalized_loss":0.39352298250466167,"weighted_normalized_losses":{"md":0.37105852599025907,"ms":1.3969208244432787,"sequence":0.05816979897834708,"static":0.1405861730144782,"topology":0.0008795900969451296}},"epoch":31,"improved":false,"step_identity_chain_sha256":"22a99ec6e93cdfc3b940b0c535e82b51bcbae5e7499d24baf55db94e31432b4d"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18568012430750563,"ms":0.7001969914321841,"sequence":0.11344239058137431,"static":0.5685290688956781,"topology":0.0029350338868824578},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20659790452157764,"ms":2.1048611550431313,"sequence":0.5635244540529155,"static":1.949247868453895,"topology":0.006143897653894644},"total_normalized_loss":0.3942682904931413,"weighted_normalized_losses":{"md":0.37136024861501127,"ms":1.4003939828643681,"sequence":0.05672119529068716,"static":0.14213226722391953,"topology":0.0007337584717206144}},"epoch":32,"improved":false,"step_identity_chain_sha256":"895e2cc888a7f56cd52ea5a37a973da1ff3b308ce4dcf07bfc24318f2c824749"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18579079378983765,"ms":0.6941709269924471,"sequence":0.11519749822068524,"static":0.5661192489833935,"topology":0.004340516875965144},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20672104146598436,"ms":2.0867462115169597,"sequence":0.572242941641004,"static":1.9409856060923543,"topology":0.009085990989786602},"total_normalized_loss":0.39202742642795035,"weighted_normalized_losses":{"md":0.3715815875796753,"ms":1.3883418539848942,"sequence":0.05759874911034262,"static":0.14152981224584837,"topology":0.001085129218991286}},"epoch":33,"improved":true,"step_identity_chain_sha256":"b231fba6226a93658e7564b44fa90e2c690117d8039abd8dc94a481b032eed83"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18593081767416006,"ms":0.693387096507827,"sequence":0.11454659821670357,"static":0.5614319089508184,"topology":0.0031280227162407697},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2068768397302944,"ms":2.084389939839984,"sequence":0.5690095994352639,"static":1.9249146819002751,"topology":0.006547880592974724},"total_normalized_loss":0.39140982207781816,"weighted_normalized_losses":{"md":0.3718616353483201,"ms":1.386774193015654,"sequence":0.057273299108351786,"static":0.1403579772377046,"topology":0.0007820056790601924}},"epoch":34,"improved":true,"step_identity_chain_sha256":"0b9516f1d44359fc25b265efd05b7ea82c1b915b26529bbebba2297c941d81e6"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18653092448192274,"ms":0.6913025012821786,"sequence":0.11700309612352572,"static":0.5737887574360878,"topology":0.003213575276954297},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20754455152462503,"ms":2.0781234411715435,"sequence":0.5812122393367132,"static":1.9672811357695654,"topology":0.006726967512346149},"total_normalized_loss":0.3916837965536452,"weighted_normalized_losses":{"md":0.3730618489638455,"ms":1.3826050025643573,"sequence":0.05850154806176286,"static":0.14344718935902195,"topology":0.0008033938192385742}},"epoch":35,"improved":false,"step_identity_chain_sha256":"6b030e9056e01db69523e77e658de8319eccfcb688835a2ef86bd664004b6e27"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18587856384316986,"ms":0.6880689560718966,"sequence":0.1145185470539755,"static":0.5619452845015106,"topology":0.002963250731657305},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20681869924796725,"ms":2.068403085629488,"sequence":0.5688702554380078,"static":1.9266748314733604,"topology":0.006202963890638041},"total_normalized_loss":0.3892762894330825,"weighted_normalized_losses":{"md":0.37175712768633973,"ms":1.376137912143793,"sequence":0.05725927352698775,"static":0.14048632112537765,"topology":0.0007408126829143262}},"epoch":36,"improved":true,"step_identity_chain_sha256":"665bd47cce5e02ea2ada4e5fdeec90728c96bf663423dd8b62fc65ad4e3336ad"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1855978427576254,"ms":0.6912036318598846,"sequence":0.11474447700890597,"static":0.570678268483088,"topology":0.00325730043461577},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20650635354998365,"ms":2.077826229945339,"sequence":0.569992561252033,"static":1.9566165729649554,"topology":0.006818497254054803},"total_normalized_loss":0.3908918159937798,"weighted_normalized_losses":{"md":0.3711956855152508,"ms":1.3824072637197693,"sequence":0.057372238504452985,"static":0.142669567120772,"topology":0.0008143251086539425}},"epoch":37,"improved":false,"step_identity_chain_sha256":"59e4358eb8cb3989c99ed96be6b743c92a78cef713baf3b8ec4fc200ac5aae21"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18628226273452947,"ms":0.6894950283626093,"sequence":0.11541384521930931,"static":0.5732782976481989,"topology":0.0028644966366610483},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20726787680708206,"ms":2.072689999463358,"sequence":0.5733176441720491,"static":1.9655309831249406,"topology":0.005996242239051097},"total_normalized_loss":0.39065944067502945,"weighted_normalized_losses":{"md":0.37256452546905894,"ms":1.3789900567252187,"sequence":0.057706922609654654,"static":0.14331957441204973,"topology":0.0007161241591652621}},"epoch":38,"improved":false,"step_identity_chain_sha256":"61ac63140ee97daeb11ff0af98b55e2cc266f7784b21723cd60e04ccb26d536e"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18609527192481407,"ms":0.6879375812347679,"sequence":0.11606297331560542,"static":0.5749748418130596,"topology":0.004197018666955648},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20705982056198813,"ms":2.0680081599231426,"sequence":0.5765421844360633,"static":1.9713477219304267,"topology":0.008785606618208533},"total_normalized_loss":0.3901780316193941,"weighted_normalized_losses":{"md":0.37219054384962813,"ms":1.3758751624695358,"sequence":0.05803148665780271,"static":0.1437437104532649,"topology":0.001049254666738912}},"epoch":39,"improved":false,"step_identity_chain_sha256":"14815a131129fdd53c6afacd0ea26e9c7cd5fc479630dad23a4465d2c7273792"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1860477765532129,"ms":0.6861794792469316,"sequence":0.11415070244626119,"static":0.5796372890932975,"topology":0.003095314515184333},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20700697460292886,"ms":2.0627231321008557,"sequence":0.5670429893633404,"static":1.987333299308969,"topology":0.006479412613565053},"total_normalized_loss":0.389442602745108,"weighted_normalized_losses":{"md":0.3720955531064258,"ms":1.3723589584938631,"sequence":0.057075351223130594,"static":0.14490932227332437,"topology":0.0007738286287960833}},"epoch":40,"improved":false,"step_identity_chain_sha256":"f74344ec081ae3f0e76daae3fa24451a0156b680057b368a8667f24b8d407827"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18539234416764283,"ms":0.691727826314129,"sequence":0.11626568618353424,"static":0.5780178487884755,"topology":0.002925738766876558},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20627770453205094,"ms":2.0794020101299573,"sequence":0.5775491594975343,"static":1.9817809173201393,"topology":0.006124440206996961},"total_normalized_loss":0.39152181618882975,"weighted_normalized_losses":{"md":0.37078468833528566,"ms":1.383455652628258,"sequence":0.05813284309176712,"static":0.14450446219711888,"topology":0.0007314346917191395}},"epoch":41,"improved":false,"step_identity_chain_sha256":"eb5126073313a51f4be295e16d8024995dbe9b86e0f5e81a784022c988690245"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18587224769755226,"ms":0.6882215320340647,"sequence":0.11647381389194182,"static":0.58260749159163,"topology":0.003232723529785487},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20681167155744778,"ms":2.068861744006903,"sequence":0.5785830327494342,"static":1.9975168786640176,"topology":0.006767050492706792},"total_normalized_loss":0.39057690403791173,"weighted_normalized_losses":{"md":0.3717444953951045,"ms":1.3764430640681293,"sequence":0.05823690694597091,"static":0.1456518728979075,"topology":0.0008081808824463718}},"epoch":42,"improved":false,"step_identity_chain_sha256":"676e09381467a3e25fd85135dc74b5227bec02e5d64206f5f2a6bf387f1296fd"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18614700269163967,"ms":0.685320170987359,"sequence":0.1171421065871618,"static":0.5920840246162111,"topology":0.002909834056541092},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20711737905439717,"ms":2.060139966794643,"sequence":0.581902773053666,"static":2.030007938152635,"topology":0.006091146924438054},"total_normalized_loss":0.39005077306395325,"weighted_normalized_losses":{"md":0.37229400538327934,"ms":1.370640341974718,"sequence":0.0585710532935809,"static":0.14802100615405278,"topology":0.000727458514135273}},"epoch":43,"improved":false,"step_identity_chain_sha256":"7ff1a9c0e0fa0e04008787607011e553c3d962cca1f1f4cedde45e1761d375b6"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1852961985002174,"ms":0.6863376928436224,"sequence":0.11782033374489713,"static":0.590915873170764,"topology":0.0026635949468924143},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20617072758180918,"ms":2.0631987377631895,"sequence":0.585271862746046,"static":2.026002836497073,"topology":0.005575695332949085},"total_normalized_loss":0.3901145633179085,"weighted_normalized_losses":{"md":0.3705923970004348,"ms":1.372675385687245,"sequence":0.058910166872448565,"static":0.147728968292691,"topology":0.0006658987367231036}},"epoch":44,"improved":false,"step_identity_chain_sha256":"90f8e0e5218cea022036b7b15872317dd62b7769a369c702fa95d2cdf261f6f2"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1852771771014432,"ms":0.6854490128941951,"sequence":0.11695329923316397,"static":0.5881065115506812,"topology":0.0027799599137752775},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20614956332880974,"ms":2.060527278262929,"sequence":0.5809648735565068,"static":2.0163707130943074,"topology":0.0058192817699651645},"total_normalized_loss":0.38953012949479454,"weighted_normalized_losses":{"md":0.3705543542028864,"ms":1.3708980257883903,"sequence":0.058476649616581985,"static":0.1470266278876703,"topology":0.0006949899784438194}},"epoch":45,"improved":false,"step_identity_chain_sha256":"6fc0204c9d72382b80dbc82c884f4ed3bbdd571aa60e6f3d4132bc84032f5c25"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18515215322101983,"ms":0.683250564808573,"sequence":0.11521552646327139,"static":0.5882545155033189,"topology":0.0027547604911359077},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20601045489269165,"ms":2.0539185266781215,"sequence":0.572332496837336,"static":2.0168781566094864,"topology":0.005766531893949943},"total_normalized_loss":0.388433103657887,"weighted_normalized_losses":{"md":0.37030430644203965,"ms":1.366501129617146,"sequence":0.057607763231635696,"static":0.14706362887582972,"topology":0.0006886901227839769}},"epoch":46,"improved":true,"step_identity_chain_sha256":"c05eda781550a7ce7718d4df2fe6c3a7de66cbabd7e25f3d23cb3984b19b4ffa"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18562786327526232,"ms":0.6840400894463337,"sequence":0.11642953551058739,"static":0.5928588482855713,"topology":0.0033916578212694337},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20653975602674077,"ms":2.0562919155405512,"sequence":0.5783630801325048,"static":2.032664483734133,"topology":0.007099747169544423},"total_normalized_loss":0.3893226599450392,"weighted_normalized_losses":{"md":0.37125572655052463,"ms":1.3680801788926673,"sequence":0.058214767755293696,"static":0.14821471207139283,"topology":0.0008479144553173584}},"epoch":47,"improved":false,"step_identity_chain_sha256":"dfa4f9e50c25e779fe20c46465945d19eacc644643045d20d3ce69bbde8abd11"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1847732815355456,"ms":0.6809981609338026,"sequence":0.11489368631824985,"static":0.5954631925978346,"topology":0.0028269217085146654},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20558890144644393,"ms":2.047147578674224,"sequence":0.570733757679195,"static":2.041593688724935,"topology":0.005917586754385115},"total_normalized_loss":0.38771245133488175,"weighted_normalized_losses":{"md":0.3695465630710912,"ms":1.3619963218676052,"sequence":0.057446843159124926,"static":0.14886579814945866,"topology":0.0007067304271286663}},"epoch":48,"improved":true,"step_identity_chain_sha256":"eb398cc6689a6cfc4580944d4217239ae6ef50ca051c458b18128689efdb8d5f"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18503533879837353,"ms":0.6819155163233396,"sequence":0.11916113116360362,"static":0.5977301799585286,"topology":0.00319209553651353},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20588048075018917,"ms":2.0499052393731887,"sequence":0.5919322665818604,"static":2.049366238137805,"topology":0.006682004035948107},"total_normalized_loss":0.38874256893979775,"weighted_normalized_losses":{"md":0.37007067759674706,"ms":1.3638310326466792,"sequence":0.05958056558180181,"static":0.14943254498963215,"topology":0.0007980238841283825}},"epoch":49,"improved":false,"step_identity_chain_sha256":"cf660140247c0497c5db143523d149b00c9d2a00d5a4a95f18df54b57cf0e4cc"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18459828916343385,"ms":0.6798209047768676,"sequence":0.11901937290670442,"static":0.5930596659260227,"topology":0.00272378837806317},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20539419532202496,"ms":2.0436086306573245,"sequence":0.5912280832169188,"static":2.0333530032470546,"topology":0.005701698062322236},"total_normalized_loss":0.38745878758199537,"weighted_normalized_losses":{"md":0.3691965783268677,"ms":1.3596418095537353,"sequence":0.05950968645335221,"static":0.14826491648150567,"topology":0.0006809470945157925}},"epoch":50,"improved":true,"step_identity_chain_sha256":"a1e41959edfb7e58c20e7c6671743081f886539cda2acb274458f70e0d75133b"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18416298638185008,"ms":0.6839928277328658,"sequence":0.11752657470392035,"static":0.5983071006205644,"topology":0.0031425546130316993},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.204909853538848,"ms":2.056149842172604,"sequence":0.5838126163184877,"static":2.051344257261655,"topology":0.006578300169048262},"total_normalized_loss":0.3890874658779582,"weighted_normalized_losses":{"md":0.36832597276370016,"ms":1.3679856554657317,"sequence":0.058763287351960174,"static":0.1495767751551411,"topology":0.0007856386532579248}},"epoch":51,"improved":false,"step_identity_chain_sha256":"b06726e9afd1de13fd3dd5397fbb33c4e12079a6970199b902832d816ac333e4"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1850617024274701,"ms":0.6803602939273509,"sequence":0.11883763390680872,"static":0.5924614841583618,"topology":0.00247797867838134},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2059098143719069,"ms":2.045230087596152,"sequence":0.5903252957300588,"static":2.031302088703976,"topology":0.005187145353432089},"total_normalized_loss":0.3877995350744464,"weighted_normalized_losses":{"md":0.3701234048549402,"ms":1.3607205878547017,"sequence":0.05941881695340436,"static":0.14811537103959044,"topology":0.000619494669595335}},"epoch":52,"improved":false,"step_identity_chain_sha256":"11ed01719e06709d88c69773df2523b945afc2b94b4b270694cb70b2c15ac0ea"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18370845765909932,"ms":0.6804429154197278,"sequence":0.11740630981809595,"static":0.6040176960551386,"topology":0.003298208957414267},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2044041199175711,"ms":2.0454784559439227,"sequence":0.583215201156672,"static":2.070923495311994,"topology":0.006904130942432061},"total_normalized_loss":0.3877669754639681,"weighted_normalized_losses":{"md":0.36741691531819864,"ms":1.3608858308394556,"sequence":0.058703154909047976,"static":0.15100442401378464,"topology":0.0008245522393535668}},"epoch":53,"improved":false,"step_identity_chain_sha256":"913413f22ca2cd32737d80a3497310d56ee9a3c56c63f4e07cc369a0df62f64b"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18539548567909112,"ms":0.6766529714521453,"sequence":0.11625312833833575,"static":0.6043026706381643,"topology":0.0029920733819102396},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20628119995023134,"ms":2.0340855109087865,"sequence":0.577486778470272,"static":2.071900553705166,"topology":0.006263298258176171},"total_normalized_loss":0.3868094328873319,"weighted_normalized_losses":{"md":0.37079097135818223,"ms":1.3533059429042906,"sequence":0.05812656416916787,"static":0.15107566765954109,"topology":0.0007480183454775599}},"epoch":54,"improved":true,"step_identity_chain_sha256":"5a56dd1131a7e16092e6a90761dcddda7d953a9b02013be21bcdbbf0a802bc40"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18360077804570496,"ms":0.6775535407567341,"sequence":0.1180505303007513,"static":0.6068469521069519,"topology":0.0033234063843095},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20428430966555874,"ms":2.036792710982263,"sequence":0.5864153628767977,"static":2.080623828382069,"topology":0.006956876640762052},"total_normalized_loss":0.3867752984756138,"weighted_normalized_losses":{"md":0.3672015560914099,"ms":1.3551070815134683,"sequence":0.05902526515037565,"static":0.15171173802673799,"topology":0.000830851596077375}},"epoch":55,"improved":true,"step_identity_chain_sha256":"ddb2a2911f734055a9a27af2aac9ecee0676628234a6cc6d2a40783cdd07dada"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18322473303664413,"ms":0.6806532835732804,"sequence":0.11936803171721157,"static":0.6086277957821803,"topology":0.003679147716284187},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20386590133473975,"ms":2.046110843343596,"sequence":0.5929600439490125,"static":2.0867295948730087,"topology":0.007701548906619323},"total_normalized_loss":0.38810335699061416,"weighted_normalized_losses":{"md":0.36644946607328827,"ms":1.3613065671465607,"sequence":0.059684015858605784,"static":0.15215694894554507,"topology":0.0009197869290710468}},"epoch":56,"improved":false,"step_identity_chain_sha256":"35bcd33f3111f1bd29fab1d6ce26847179e59086d510cfd3201a811beb361e21"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18489347082031377,"ms":0.6799477774086968,"sequence":0.12097173139885309,"static":0.6050795141984117,"topology":0.0026783415226312546},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20572263064590274,"ms":2.0439900222908833,"sequence":0.6009264133364207,"static":2.0745640246458437,"topology":0.0056065642958219725},"total_normalized_loss":0.3884215652175417,"weighted_normalized_losses":{"md":0.36978694164062753,"ms":1.3598955548173937,"sequence":0.060485865699426544,"static":0.15126987854960292,"topology":0.0006695853806578136}},"epoch":57,"improved":false,"step_identity_chain_sha256":"0ce3e2612758e7a54a43bd3bb38b91b6b28f56240369c17e10387d65a57fb47f"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18345770502032352,"ms":0.6742300866444119,"sequence":0.11983664951638005,"static":0.6109896249676888,"topology":0.002424522954548807},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20412511875943667,"ms":2.0268020804208735,"sequence":0.5952879003004393,"static":2.094827317148577,"topology":0.005075246646671062},"total_normalized_loss":0.38572948901364407,"weighted_normalized_losses":{"md":0.36691541004064704,"ms":1.3484601732888237,"sequence":0.05991832475819003,"static":0.1527474062419222,"topology":0.0006061307386372017}},"epoch":58,"improved":true,"step_identity_chain_sha256":"bbc4d6ab68e210958fb43e54319fbc9ff2c3adf5fd559e6c903a7bebb9174ffb"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18430541764756114,"ms":0.6772358458843342,"sequence":0.11843834313645311,"static":0.6130100008762871,"topology":0.0025234864641710594},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20506833038791303,"ms":2.035837688889547,"sequence":0.5883418210146559,"static":2.1017543392636653,"topology":0.005282406665268042},"total_normalized_loss":0.38723701409342637,"weighted_normalized_losses":{"md":0.3686108352951223,"ms":1.3544716917686683,"sequence":0.05921917156822656,"static":0.15325250021907177,"topology":0.0006308716160427648}},"epoch":59,"improved":false,"step_identity_chain_sha256":"687971b0d82869252fa7c1b48eef634e327372223f0fb19ca240703d9752ed95"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1838877833078002,"ms":0.6749785765260923,"sequence":0.1211451243655598,"static":0.6121063319860106,"topology":0.0031933095673136393},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20460364748352222,"ms":2.0290521147628797,"sequence":0.6017877419491136,"static":2.0986560374273475,"topology":0.006684545362989639},"total_normalized_loss":0.3864260384477792,"weighted_normalized_losses":{"md":0.3677755666156004,"ms":1.3499571530521846,"sequence":0.0605725621827799,"static":0.15302658299650265,"topology":0.0007983273918284098}},"epoch":60,"improved":false,"step_identity_chain_sha256":"59e42a6d27800dbabab6785e733bdbebd6ef23959a5e0b1308c3cd3ded043300"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18457687056416733,"ms":0.6799754532590466,"sequence":0.12034037085904453,"static":0.6192550061116541,"topology":0.002440056515538208},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2053703638120949,"ms":2.044073218624268,"sequence":0.5977901333119712,"static":2.123165844513815,"topology":0.005107763003414316},"total_normalized_loss":0.3889397197465497,"weighted_normalized_losses":{"md":0.36915374112833466,"ms":1.3599509065180932,"sequence":0.060170185429522266,"static":0.15481375152791352,"topology":0.000610014128884552}},"epoch":61,"improved":false,"step_identity_chain_sha256":"dc2034f12c0777e9868357640285ed0f944cd341aed61e684219241eca93718e"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18451724904843766,"ms":0.6783744721375862,"sequence":0.11993402709049762,"static":0.610575358743474,"topology":0.002646263407398155},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20530402563906702,"ms":2.0392605116093083,"sequence":0.5957716228666721,"static":2.0934069719125956,"topology":0.005539415273181098},"total_normalized_loss":0.3878111722910029,"weighted_normalized_losses":{"md":0.36903449809687533,"ms":1.3567489442751723,"sequence":0.05996701354524881,"static":0.1526438396858685,"topology":0.0006615658518495387}},"epoch":62,"improved":false,"step_identity_chain_sha256":"914a329af955c22b510a20e9a742b47286eed9943d40890667c9b2cd01fd1b44"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.183479316404788,"ms":0.6776447250870751,"sequence":0.11841026921407123,"static":0.6209331289968829,"topology":0.002429041752571319},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20414916477276648,"ms":2.0370668200647524,"sequence":0.5882023639589442,"static":2.128919424473042,"topology":0.005084705833051504},"total_normalized_loss":0.3874587520556251,"weighted_normalized_losses":{"md":0.366958632809576,"ms":1.3552894501741501,"sequence":0.059205134607035616,"static":0.15523328224922073,"topology":0.0006072604381428297}},"epoch":63,"improved":false,"step_identity_chain_sha256":"c73bf6800b9b71249ca8b0567c1bb5a08f63b9a73e146bc7580374ac7f30834d"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1839770862407608,"ms":0.6767613667208217,"sequence":0.11981215230251706,"static":0.6118474175612922,"topology":0.0023987463457327595},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20470301083157091,"ms":2.034411357767912,"sequence":0.5951662105247107,"static":2.097768328393446,"topology":0.005021288548559121},"total_normalized_loss":0.38698890461023594,"weighted_normalized_losses":{"md":0.3679541724815216,"ms":1.3535227334416433,"sequence":0.05990607615125853,"static":0.15296185439032306,"topology":0.0005996865864331899}},"epoch":64,"improved":false,"step_identity_chain_sha256":"44177bb1bdc9d989f02c516b19401fd98bc6e0d74c6ee38ef91634c4447710b3"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1840057954520985,"ms":0.6791053649388362,"sequence":0.12021965637908434,"static":0.6151097989587911,"topology":0.0026189933671735247},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20473495427691787,"ms":2.04145764739349,"sequence":0.5971904847937477,"static":2.1089536667218987,"topology":0.005482330979569865},"total_normalized_loss":0.3881528694105806,"weighted_normalized_losses":{"md":0.368011590904197,"ms":1.3582107298776724,"sequence":0.06010982818954217,"static":0.15377744973969779,"topology":0.0006547483417933812}},"epoch":65,"improved":false,"step_identity_chain_sha256":"d1e4eae03c524e388b26674ded529f15d0ba620a9572a09d488b7fd91d1b337a"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18458304129881448,"ms":0.6778930106022428,"sequence":0.11855682889465703,"static":0.6244757621811947,"topology":0.0024463066961293023},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20537722971038216,"ms":2.037813190789893,"sequence":0.5889303983697586,"static":2.1410656287060936,"topology":0.005120846487745337},"total_normalized_loss":0.3881922070937548,"weighted_normalized_losses":{"md":0.36916608259762895,"ms":1.3557860212044857,"sequence":0.059278414447328516,"static":0.15611894054529868,"topology":0.0006115766740323256}},"epoch":66,"improved":false,"step_identity_chain_sha256":"35f9a9a13062d8eb7dc8563c986c972e4adc7fa99339ff87265f2a48c0de3dbf"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18442390757562716,"ms":0.6800514643494341,"sequence":0.1214192548395407,"static":0.6306108756564948,"topology":0.0031009598053264383},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20520016878977027,"ms":2.044301715452256,"sequence":0.6031494835775972,"static":2.162100361174005,"topology":0.006491229882529015},"total_normalized_loss":0.38961766602706965,"weighted_normalized_losses":{"md":0.3688478151512543,"ms":1.3601029286988682,"sequence":0.06070962741977035,"static":0.1576527189141237,"topology":0.0007752399513316096}},"epoch":67,"improved":false,"step_identity_chain_sha256":"b0f4cd477a45ba36039ed25cd16fc13b90ed5365e1c8ee898084bad7d13ffeca"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18361496530402258,"ms":0.6741262142646623,"sequence":0.12185378527980724,"static":0.6222918734381624,"topology":0.0024981878603235986},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20430009518837794,"ms":2.0264898298115566,"sequence":0.6053080111603258,"static":2.1335779896209632,"topology":0.00522944917352667},"total_normalized_loss":0.38652135342037897,"weighted_normalized_losses":{"md":0.36722993060804515,"ms":1.3482524285293247,"sequence":0.06092689263990362,"static":0.1555729683595406,"topology":0.0006245469650808997}},"epoch":68,"improved":false,"step_identity_chain_sha256":"076ecb1a4f813b50f84d86e9de78407809f775bab6232fff8c4bfbc37eaae592"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18410910225290328,"ms":0.673996083267659,"sequence":0.11852014571671958,"static":0.621748625770358,"topology":0.002154287613960629},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20484989909747253,"ms":2.0260986432111983,"sequence":0.588748174884215,"static":2.131715421079715,"topology":0.0045095638167523015},"total_normalized_loss":0.3862892344491128,"weighted_normalized_losses":{"md":0.36821820450580656,"ms":1.347992166535318,"sequence":0.05926007285835979,"static":0.1554371564425895,"topology":0.0005385719034901572}},"epoch":69,"improved":false,"step_identity_chain_sha256":"bd1b69f073ada6c240df716653e793aa4cdd588fbac71c752c182c77a9fb5607"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18340061655024986,"ms":0.6747456800720724,"sequence":0.12225088358616547,"static":0.6182707426786871,"topology":0.0027239572689114313},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2040615989921296,"ms":2.0283520050720183,"sequence":0.6072805948228267,"static":2.1197912177732077,"topology":0.005702051601029585},"total_normalized_loss":0.3865333420049254,"weighted_normalized_losses":{"md":0.3668012331004997,"ms":1.3494913601441447,"sequence":0.061125441793082734,"static":0.15456768566967177,"topology":0.0006809893172278578}},"epoch":70,"improved":false,"step_identity_chain_sha256":"58b877487b7272d4cb96cca3210d8253569729e85bfecd05b4bf15a815cd074f"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.18375603488669295,"ms":0.675921536063296,"sequence":0.12494299095684128,"static":0.6285623639921295,"topology":0.0021978501680299835},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.2044570569541034,"ms":2.031886744052814,"sequence":0.6206536234458765,"static":2.1550768733459806,"topology":0.004600753180848078},"total_normalized_loss":0.38790333818368766,"weighted_normalized_losses":{"md":0.3675120697733859,"ms":1.351843072126592,"sequence":0.06247149547842064,"static":0.15714059099803238,"topology":0.0005494625420074959}},"epoch":71,"improved":false,"step_identity_chain_sha256":"21fe679e356c863708cd463cb4f97cc5727ed675d96196e10849f99070f5a87d"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1837435376703293,"ms":0.675566786829554,"sequence":0.12207702804374553,"static":0.6313521664754512,"topology":0.002319502583325566},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20444315186478537,"ms":2.0308203328984935,"sequence":0.6064169683678112,"static":2.164641936667343,"topology":0.004855407817806645},"total_normalized_loss":0.3876154160572667,"weighted_normalized_losses":{"md":0.3674870753406586,"ms":1.351133573659108,"sequence":0.06103851402187276,"static":0.1578380416188628,"topology":0.0005798756458313915}},"epoch":72,"improved":false,"step_identity_chain_sha256":"fa7add9defcff3fd01d8bbfc74a36de8ad26fc8ce4d61890a7025e31dd55a327"},{"development":{"native_contract_sha256":"1f18091e8b5cd24c2d4937233064ccfb1522d61f71b0e6a2b4cca0cf7224b025","native_surface_sha256":"c102fca3c4dd0df8ac57172da75b90f1dff0954f526330f545474d47a0059f4a","normalized_losses":{"md":0.1842874184263841,"ms":0.6726172266165018,"sequence":0.1200924884666931,"static":0.6200451426897949,"topology":0.0026573603891926574},"parent_counts":{"md":5066,"ms":580,"sequence":55440,"static":5390,"topology":17650},"r4_provenance_chain_sha256":"377a4ff4acb5c062f86cf190f5598d63d99d10ee85b3640c812817976f4b483b","raw_parent_means":{"md":0.20504830346584982,"ms":2.021953664242555,"sequence":0.5965587788852591,"static":2.1258748916408514,"topology":0.0055626445519697575},"total_normalized_loss":0.385906232017773,"weighted_normalized_losses":{"md":0.3685748368527682,"ms":1.3452344532330036,"sequence":0.06004624423334655,"static":0.15501128567244873,"topology":0.0006643400972981643}},"epoch":73,"improved":false,"step_identity_chain_sha256":"e33e5b3a8e3b44c0f5af6f0ff0ac49320eaadbe28d8254685dc22687a8f0e0df"}],"receipt_sha256":"b595e1691e185649a671284778eeb81e0a37414ce5be7f20ac27d7e9c71cdecc","run_identity":{"attribution_claim":"fixed_sequence_heavy_loss_sensitivity_on_final_md_inclusive_a1111","base_control_cell_id":"A111","base_launch_config_sha256":"2a89d6b54668d4db8f1b9fe857f744d31116eb6d28b2695b31461161c1aea2b9","coefficients":{"md":2.0,"ms":2.0,"sequence":0.5,"static":0.25,"topology":0.25},"completed_epoch_resume":true,"core_source_sha256":"3e96d8092239c2c21f217ad5bae4ee7e07dbe0f7630b213bad468d6b08d3b1c1","evaluation_populations":["all_parent_primary","md_present_secondary"],"heldout_access":false,"hpo":false,"initial_model_state_sha256":"128cefb8648ca4519365ec48c3c8c1525735ef2325faf881b64366bae4c5c7bc","loss_denominator":5,"loss_policy":"WA3_FINAL_MS_MD_HEAVY","manifest_sha256":"eeebc9c7e2cfc701f3ad47e62696ae2247c8caec93d8b034d5f573b701eab2a1","maximum_epochs":100,"md_cell_id":"A1111","md_native_intersection_rosters":{"pretraining_development":{"count":5066,"sha256":"b94e6907dc441597ee7ea098894de9ffd7cfe71a61b9ae3a0489f5e1b73e79d8"},"pretraining_train":{"count":22872,"sha256":"6efa5bd7ff81f9b49aab026d44d613765041dc5ea10e116059c9477650f7403a"}},"md_null_receipt_sha256":"ee6147fb6f3448b27823d0edc4ac2922b5322af7967027b778d0b2da03e92aa6","md_package_summary_sha256":"39c6691d762b1ac2d0bed3464b1ed31e4fbe72b18e218ba4a553f3f94702a9e2","md_training_window_policy":{"fixed_development_seed":0,"pretraining_seed":42,"schema":"bertose_review_md_temporal_20x6_a1111_seeded_md_window_20260811_r1","seed43_legacy_exact":true},"native_adapter_contract":{"affinose_supported":false,"authority_set_sha256":"7804332dfc2be432b45f6a82459f21f0b577fc5c358f16b1fea90ba4cb6bb52f","cells":["A000","A100","A010","A001","A110","A101","A011","A111"],"common_seed":42,"development_manifest_sha256":"4eea50c1bd4eee0ea10062c287f31c619cef5835e2e660facd098698859f7e70","development_parent_count":55440,"development_roster_sha256":"dbadd12a88e29cc1a7ca1da85895d89f0162fa8bc9f3dcf8aebf5b8781a1e698","disabled_branch_contract":"static-disabled cell opens no static package/loader/mask stream/payload","downstream_supported":false,"execution_certified":false,"gpu_launch_authorized":false,"loss_policy":{"adaptive_or_weight_ablation":false,"coefficients":[1,"topology_bit","ms_bit","static_bit"],"denominator":4},"matched_weight_policies":["WA1_FINAL_MD_EQUAL_NORMALIZED","WA2_FINAL_MD_SEQUENCE_HEAVY"],"md_supported":false,"nova_submission_authorized":false,"postfit_static":{"binding_sha256":"4ac795da03db66b1f08ad03839dd4b2432fa6f794e6afe77575ae59127f2289b","compatibility_view_persisted":false,"direct_postfit_producer_source_sha256":"8e57c17fd43f3894f94f980cf70a0d6d7f183cf04c26780c3fe30c1932bb7abb","direct_publication_validated_before_compatibility_view":true,"direct_static_bridge_evidence":{"bridge_evidence_sha256":"908452818740e3a406fca7b20605343679c791ed58162b07453635f4ed85e1dd","compatibility_view_persisted":false,"direct_export_receipt_sha256":"b68da847cd4ccd5b6cbfd65e42a4d4b504fcfae00136851fdc980acb1690ad72","direct_join_receipt_sha256":"7b55cf83bd3175e9a9e0be15fc2a9fc998d0803bc67cb2761f69f612e9b6c921","direct_null_file_sha256":"661d04d76fcf36d116feb117154eac01e6569e377d4245c07653b28696e1227b","direct_null_receipt_sha256":"84d8fdb5e960ed0f695c1618d881fe9dd13062e2ad143e099fc7db6415e54c89","direct_publication_file_sha256":"77ba176a9fab70e75b3c2c423c857d1e266f2021875aea01e15bac3f11f327d1","direct_publication_receipt_sha256":"654faac335def8eccb7c50eb8a7d871036bf2a8c223e61f0cb11027830869a84","direct_token_file_sha256":"f9a032f0bd3ad47b99c8a3df2fc46596500e13baf0e77f4b39d26a8351dc7323","direct_token_package_semantic_sha256":"b4c0adb42cc2444e91611c4492c8d385c10fe706a862dbe9b51c144aad80f9e7","native_ablation_data_binding_authorized_by_source_publication":false,"nova_submission_authorized_by_source_publication":false,"official_submitted_artifacts_modified":false,"owner_amendment_file_sha256":"876a3746653254a812d5607d1115abfa112fcefb0772b3828efcb26865dfaf7d","owner_amendment_receipt_sha256":"672d247a416280617634b8f51c7c69716fd7a6fe010db4109fe634b25c531b93","schema":"bertose_review_a5_native_eight_direct_static_bridge_20260724_r1","writer_source_sha256":"ceffd07edb6b8b322baecd95e023053ad8c783cc97cdb2edfc03c0b4fdeeaab6"},"direct_static_bridge_source_sha256":"c71ba654568a24993bc1733b26b6863b11e5c198c53871a6183b485ab96540f0","masker_binding_sha256":"c3373ddbe58ebe15cf6e941f48ea71bcb569fe14b94f394090ffbdaf12faad36","max_static_tokens":192,"nova_submission_authorized":false,"null_receipt_sha256":"661d04d76fcf36d116feb117154eac01e6569e377d4245c07653b28696e1227b","null_scale":3.4285808327094895,"null_scale_hex":"0x1.b6dbbc9a17009p+1","parent_counts":{"pretraining_development":5390,"pretraining_train":22980},"predecessor_compatibility_binding_sha256":"3105b52ff97f720a02be38fb24433087eddd19b4da362fc601a8e7540bb29d25","publication_receipt_semantic_sha256":"a79630e44bba95c7bec3bc59235dc4d8f5012968049e591bd127a26c52063803","publication_receipt_sha256":"77ba176a9fab70e75b3c2c423c857d1e266f2021875aea01e15bac3f11f327d1","release_receipt_sha256":"261099ec7bf32cd72e261ea95d0631702b46cb53cc19085827ed368ba3e3bb63","residue_counts":{"pretraining_development":51237,"pretraining_train":205801},"schema":"bertose_review_a5_native_eight_direct_static_runtime_binding_20260724_r8","state_counts":{"pretraining_development":7964,"pretraining_train":34408},"support_binding_sha256":"38c47a7c323b29bfe9c6a76668de45370789687e50663b0acf52b25bc5f96779","support_sha256_by_slot":["427c4b27091096e0260bb3ad896db841b4b41e9c615bec24cc4330e3e4ddf158","fc55a9e195344141ed497f5a59fa8ab8fcd58f04ccb21c6b7f5a9166cb8d19b6","da96778691370fa5a1c59e471af23113f72c1a93bc277f40d8ea05bdf4aa4720","db2889304eef19132309f70744dc696d2b1bb344473296b5fd7fae48afca8f5c"],"token_count":1028152,"token_export_receipt_sha256":"0e851404deed73e8557ea385a18f9d340c333f259d6c5d171fedc49b09959699","token_null_receipt_sha256":"6333289557fb1fc23324351f30b546a493ef1f2e6ea71efde49a812d3d38bb54","token_package_semantic_sha256":"f434b2cb0ecea84053563faeed700ffd351658e3bb79c8daa4b833404d6691be","token_package_sha256":"f9a032f0bd3ad47b99c8a3df2fc46596500e13baf0e77f4b39d26a8351dc7323","training_authorized":false},"predecessor_adapter_r2_receipt_sha256":"3b2118e128a5550ba4918b4f4f2c6d8045e319b7cff37b76d74dce6b3220c9f0","predecessor_adapter_r2_source_sha256":"9f8f167148aa234df413e4c3d4e1846881781c8e5a19bd91b0b178b260a3a537","predecessor_common_seed":43,"processing_contract":{"ms":"frozen MS-V3 train selection and mask streams parameterized by the declared upstream seed; fixed development seed 0","sequence":"frozen R4 train stream parameterized by the declared upstream seed; fixed development seed 0","static":"frozen final static token stream parameterized by the declared upstream seed; fixed development seed 0","topology":"R4-bound original-residue BFS matrix with receipt-bound inverse-multiplicity equal-parent loss"},"receipt_sha256":"77ba176a9fab70e75b3c2c423c857d1e266f2021875aea01e15bac3f11f327d1","schema":"bertose_review_md_temporal_20x6_a1111_wa2_seed_adapter_20260811_r1","scientific_launch_authorized":false,"seed_parameterization":{"fixed_development_seed":0,"model_rng_seed":42,"ms_train_selection_and_mask_seed":42,"r5_epoch_plan_seed":42,"sequence_train_mask_seed":42,"static_train_mask_seed":42},"sequence_topology_r4":{"bfs_replay_receipt_sha256":"f20a1dd444f2760ccd8605d53ad716ceb55112fd32d3409826bcaf78ff2c6c3d","predecessor_receipt_sha256":"53b058a2032c424718a96757b3aa63df5160c20910001fcb128b0ace03a13a4c","predecessor_relative_path":"review/multimodal/outputs/review_a5_official_ragged_sequence_topology_runtime_20260721_r3","provenance_fields":["sequence_rows","roles","seq_token_categories","topology_rows","topology_residue_counts","topology_target_counts"],"r4_masker_only_checkpoint_restore":true,"r4_store_collation_required":true,"rows_only_through_r4_store_sample":true,"runtime_receipt_sha256":"654f5b9728c3dfd2588c4b570b0e3a1bcab8feb366af28eeead36f9beb37d921","runtime_sha256":"efc56a84cb82f30c0566da83a38bf0f02fdd507e02d116d827c1bbd827151ecf","store_manifest_identity_sha256":"7bd44c2f15715924bce78c7bbdbe57d40fc69aaf90ed834e17d9632bded0fc17","topology_loss_source_sha256":"64eaf9174f523ae909c84987049d071bf3869ca8e83919f14f0572ef77b0530e"},"source_sha256":"4430560565a18da0e68700d60526d3dec15fabd79854111d1e85deb59e2381c8","static_final_token_package_bound":true,"status":"WA2_SEED_PARAMETERIZED_SUCCESSOR","successor_source_sha256":"dd74253863f39150af4ddc7e8d48c0a366e1f327c30aa50e0748078330b97b85","train_manifest_sha256":"13b7ebc78b22baa1a38d2ae40da202dcc1e20e56f44220b4d4d5003ca5f3b8f2","train_parent_count":221827,"train_roster_sha256":"2a7c8121c9ec88418e930cdec47302b7ef2b4d92e01fafa523218461a249de6e","training_authorized":false,"weight_ablation_supported":false,"world_size":1},"native_adapter_contract_sha256":"ad9d49ba49f69d17e8f462930179af4832f741edf6447b880944eef0988423bd","native_launch_sha256":"30082379d47318f5d9e17fafad4aa018e7e9f0f0bf1aac559178d46784fe4b9f","optimizer_contract_sha256":"4c4498fd71ea6a14e654ee7a1e6737bcd578ee328ec73de731cf8da12396c6c0","owner_decision_sha256":"2c7fdb2f522b52fd0692fac9fce8ef5b0a202d1b418b1b9f3570ad7ea9944b83","parameter_summary":{"four_slot_fusion_parameters":2952192,"md_branch_parameters":179206,"total_parameters":138487995,"trainable_parameters":138487995},"patience":15,"precision":"fp32_no_amp","pretraining_seed":42,"runner_source_sha256":"dd74253863f39150af4ddc7e8d48c0a366e1f327c30aa50e0748078330b97b85","schema":"bertose_review_md_temporal_20x6_a1111_wa2_training_20260811_r1","selection_rule":"strict_lower_earliest_tie","semantic_array_id":"md20x6-a1111-wa345-20260817-r1","static_binding_sha256":"4ac795da03db66b1f08ad03839dd4b2432fa6f794e6afe77575ae59127f2289b"},"run_identity_sha256":"55dd7ff33d2196cb1da5e215adb79b0880249f8f1e8c541a7fe36f5759c369d5","schema":"bertose_review_md_temporal_20x6_a1111_wa2_final_20260811_r1","selection":{"best_epoch":58,"best_loss":0.38572948901364407,"completed_epochs":73,"epochs_without_improvement":15,"stop_reason":"patience_exhausted"},"status":"PASS_TRAINING_COMPLETE","terminal_attempt_identity":{"actual_slurm_route":{"account":"ratul-scavenger","partition":"scavenger","qos":"scavenger"},"attempt_sha256":"f299e47d3aaf076a1fc97d2c6ff03e62c0f913bb5ccd97098476e2f02aa302f4","device":{"compute_capability":[9,0],"cuda_visible_devices":"0","name":"NVIDIA H200 NVL","total_memory_bytes":150109880320},"slurm_array_job_id":"12042738","slurm_array_task_id":"0","slurm_job_id":"12042740","slurm_restart_count":1}}
releases/2026-10-02/provenance/affinose_arms_on_test_20260909.json ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "bob_B_protein_s43": {
3
+ "stratum": "test_protein_cold",
4
+ "seed": 43,
5
+ "test_macro": 0.24463542038572242,
6
+ "test_pooled": 0.26184445034413995,
7
+ "assays": 246,
8
+ "rows": 38539,
9
+ "dir": "affinose_bob_20260909_bob_B_protein_s43"
10
+ },
11
+ "bob_B_protein_s44": {
12
+ "stratum": "test_protein_cold",
13
+ "seed": 44,
14
+ "test_macro": 0.24516174432219207,
15
+ "test_pooled": 0.2730962401378244,
16
+ "assays": 246,
17
+ "rows": 38539,
18
+ "dir": "affinose_bob_20260909_bob_B_protein_s44"
19
+ },
20
+ "mo_A_glycan_s42": {
21
+ "stratum": "test_glycan_cold",
22
+ "seed": 42,
23
+ "test_macro": 0.33405850648525903,
24
+ "test_pooled": 0.3519114017717813,
25
+ "assays": 1767,
26
+ "rows": 37295,
27
+ "dir": "affinose_mo_20260908_mo_A_glycan_s42"
28
+ },
29
+ "mo_C_doubly_s42": {
30
+ "stratum": "test_doubly_cold",
31
+ "seed": 42,
32
+ "test_macro": 0.17732109020146009,
33
+ "test_pooled": 0.19643520103251555,
34
+ "assays": 212,
35
+ "rows": 3691,
36
+ "dir": "affinose_mo_20260908_mo_C_doubly_s42"
37
+ },
38
+ "mof_B_protein_s43": {
39
+ "stratum": "test_protein_cold",
40
+ "seed": 43,
41
+ "test_macro": 0.24512556467979302,
42
+ "test_pooled": 0.2699137753848991,
43
+ "assays": 246,
44
+ "rows": 38539,
45
+ "dir": "affinose_mof_20260909_mof_B_protein_s43"
46
+ },
47
+ "mof_B_protein_s44": {
48
+ "stratum": "test_protein_cold",
49
+ "seed": 44,
50
+ "test_macro": 0.24489399647831056,
51
+ "test_pooled": 0.2547891998912819,
52
+ "assays": 246,
53
+ "rows": 38539,
54
+ "dir": "affinose_mof_20260909_mof_B_protein_s44"
55
+ },
56
+ "mof_C_doubly_s43": {
57
+ "stratum": "test_doubly_cold",
58
+ "seed": 43,
59
+ "test_macro": 0.1839765095395252,
60
+ "test_pooled": 0.2208411762069753,
61
+ "assays": 212,
62
+ "rows": 3691,
63
+ "dir": "affinose_mof_20260909_mof_C_doubly_s43"
64
+ },
65
+ "mof_C_doubly_s44": {
66
+ "stratum": "test_doubly_cold",
67
+ "seed": 44,
68
+ "test_macro": 0.17430173158048537,
69
+ "test_pooled": 0.19176702047106786,
70
+ "assays": 212,
71
+ "rows": 3691,
72
+ "dir": "affinose_mof_20260909_mof_C_doubly_s44"
73
+ },
74
+ "bo_A_glycan_s42": {
75
+ "stratum": "test_glycan_cold",
76
+ "seed": 42,
77
+ "test_macro": 0.2882683038203673,
78
+ "test_pooled": 0.3136364354792883,
79
+ "assays": 1767,
80
+ "rows": 37295,
81
+ "dir": "affinose_sa_20260908_bo_A_glycan_s42"
82
+ },
83
+ "bo_B_protein_s42": {
84
+ "stratum": "test_protein_cold",
85
+ "seed": 42,
86
+ "test_macro": 0.2522396003161021,
87
+ "test_pooled": 0.2653369332383739,
88
+ "assays": 246,
89
+ "rows": 38539,
90
+ "dir": "affinose_sa_20260908_bo_B_protein_s42"
91
+ },
92
+ "bo_C_doubly_s42": {
93
+ "stratum": "test_doubly_cold",
94
+ "seed": 42,
95
+ "test_macro": 0.16472937852334543,
96
+ "test_pooled": 0.1926274802936194,
97
+ "assays": 212,
98
+ "rows": 3691,
99
+ "dir": "affinose_sa_20260908_bo_C_doubly_s42"
100
+ },
101
+ "boa_A_glycan_s43": {
102
+ "stratum": "test_glycan_cold",
103
+ "seed": 43,
104
+ "test_macro": 0.3374435261201508,
105
+ "test_pooled": 0.36037024272127394,
106
+ "assays": 1767,
107
+ "rows": 37295,
108
+ "dir": "affinose_sa_20260908_boa_A_glycan_s43"
109
+ },
110
+ "boa_A_glycan_s44": {
111
+ "stratum": "test_glycan_cold",
112
+ "seed": 44,
113
+ "test_macro": 0.30969468493798785,
114
+ "test_pooled": 0.32123919236737947,
115
+ "assays": 1767,
116
+ "rows": 37295,
117
+ "dir": "affinose_sa_20260908_boa_A_glycan_s44"
118
+ },
119
+ "boc_C_doubly_s43": {
120
+ "stratum": "test_doubly_cold",
121
+ "seed": 43,
122
+ "test_macro": 0.16514492967071465,
123
+ "test_pooled": 0.1791689218089693,
124
+ "assays": 212,
125
+ "rows": 3691,
126
+ "dir": "affinose_sa_20260908_boc_C_doubly_s43"
127
+ },
128
+ "boc_C_doubly_s44": {
129
+ "stratum": "test_doubly_cold",
130
+ "seed": 44,
131
+ "test_macro": 0.20300180090714,
132
+ "test_pooled": 0.23511093681840797,
133
+ "assays": 212,
134
+ "rows": 3691,
135
+ "dir": "affinose_sa_20260908_boc_C_doubly_s44"
136
+ },
137
+ "mc_A_glycan_s42": {
138
+ "stratum": "test_glycan_cold",
139
+ "seed": 42,
140
+ "test_macro": 0.27841973422388244,
141
+ "test_pooled": 0.3022162276393939,
142
+ "assays": 1767,
143
+ "rows": 37295,
144
+ "dir": "affinose_sa_20260908_mc_A_glycan_s42"
145
+ },
146
+ "mc_B_protein_s42": {
147
+ "stratum": "test_protein_cold",
148
+ "seed": 42,
149
+ "test_macro": 0.25218496294299203,
150
+ "test_pooled": 0.26171877856861664,
151
+ "assays": 246,
152
+ "rows": 38539,
153
+ "dir": "affinose_sa_20260908_mc_B_protein_s42"
154
+ },
155
+ "mc_C_doubly_s42": {
156
+ "stratum": "test_doubly_cold",
157
+ "seed": 42,
158
+ "test_macro": 0.15450571314627123,
159
+ "test_pooled": 0.1575716723657401,
160
+ "assays": 212,
161
+ "rows": 3691,
162
+ "dir": "affinose_sa_20260908_mc_C_doubly_s42"
163
+ },
164
+ "mo_B_protein_s42": {
165
+ "stratum": "test_protein_cold",
166
+ "seed": 42,
167
+ "test_macro": 0.23260289913763402,
168
+ "test_pooled": 0.26884245311406896,
169
+ "assays": 246,
170
+ "rows": 38539,
171
+ "dir": "affinose_sa_20260908_mo_B_protein_s42"
172
+ },
173
+ "moc_A_glycan_s43": {
174
+ "stratum": "test_glycan_cold",
175
+ "seed": 43,
176
+ "test_macro": 0.318371586837939,
177
+ "test_pooled": 0.3491029922366602,
178
+ "assays": 1767,
179
+ "rows": 37295,
180
+ "dir": "affinose_sa_20260908_moc_A_glycan_s43"
181
+ },
182
+ "moc_A_glycan_s44": {
183
+ "stratum": "test_glycan_cold",
184
+ "seed": 44,
185
+ "test_macro": 0.3354799039624282,
186
+ "test_pooled": 0.3562564894744605,
187
+ "assays": 1767,
188
+ "rows": 37295,
189
+ "dir": "affinose_sa_20260908_moc_A_glycan_s44"
190
+ },
191
+ "sa_A_glycan_s42": {
192
+ "stratum": "test_glycan_cold",
193
+ "seed": 42,
194
+ "test_macro": 0.28116041536475034,
195
+ "test_pooled": 0.2995676811524887,
196
+ "assays": 1767,
197
+ "rows": 37295,
198
+ "dir": "affinose_sa_20260908_sa_A_glycan_s42"
199
+ },
200
+ "sa_C_doubly_s42": {
201
+ "stratum": "test_doubly_cold",
202
+ "seed": 42,
203
+ "test_macro": 0.14451918425842353,
204
+ "test_pooled": 0.16719428719500484,
205
+ "assays": 212,
206
+ "rows": 3691,
207
+ "dir": "affinose_sa_20260908_sa_C_doubly_s42"
208
+ },
209
+ "swe_A_glycan_s42": {
210
+ "stratum": "test_glycan_cold",
211
+ "seed": 42,
212
+ "test_macro": 0.2919377626107431,
213
+ "test_pooled": 0.3060559058312503,
214
+ "assays": 1767,
215
+ "rows": 37295,
216
+ "dir": "affinose_swe_20260908_swe_A_glycan_s42"
217
+ },
218
+ "swe_C_doubly_s42": {
219
+ "stratum": "test_doubly_cold",
220
+ "seed": 42,
221
+ "test_macro": 0.12325096614703701,
222
+ "test_pooled": 0.13077810558700573,
223
+ "assays": 212,
224
+ "rows": 3691,
225
+ "dir": "affinose_swe_20260908_swe_C_doubly_s42"
226
+ },
227
+ "sz2_A_glycan_s42": {
228
+ "stratum": "test_glycan_cold",
229
+ "seed": 42,
230
+ "test_macro": 0.28711041488202854,
231
+ "test_pooled": 0.3024540500744863,
232
+ "assays": 1767,
233
+ "rows": 37295,
234
+ "dir": "affinose_sz2_20260908_sz2_A_glycan_s42"
235
+ },
236
+ "sz2_C_doubly_s42": {
237
+ "stratum": "test_doubly_cold",
238
+ "seed": 42,
239
+ "test_macro": 0.1490739924202869,
240
+ "test_pooled": 0.166626209128426,
241
+ "assays": 212,
242
+ "rows": 3691,
243
+ "dir": "affinose_sz2_20260908_sz2_C_doubly_s42"
244
+ },
245
+ "szc_C_doubly_s43": {
246
+ "stratum": "test_doubly_cold",
247
+ "seed": 43,
248
+ "test_macro": 0.13519544496948036,
249
+ "test_pooled": 0.1757217730192651,
250
+ "assays": 212,
251
+ "rows": 3691,
252
+ "dir": "affinose_szc_20260908_szc_C_doubly_s43"
253
+ },
254
+ "szc_C_doubly_s44": {
255
+ "stratum": "test_doubly_cold",
256
+ "seed": 44,
257
+ "test_macro": 0.12914485639778248,
258
+ "test_pooled": 0.1359993175882641,
259
+ "assays": 212,
260
+ "rows": 3691,
261
+ "dir": "affinose_szc_20260908_szc_C_doubly_s44"
262
+ }
263
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_A_glycan_s42/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mo_A_glycan_s42",
3
+ "fingerprint": "cbcc86152b41f424bf9d201d53843e0fdeb2cd9f934e5a1d3f37fe369c35f874",
4
+ "manifest_sha256": "mo_20260908_moA_glycan",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_A_glycan_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_mo_20260908_mo_A_glycan_s42",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 42,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_A_glycan_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_glycan_cold",
42
+ "test_stratum": "val_glycan_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 484491,
45
+ "expected_val_rows": 36745,
46
+ "expected_test_rows": 36745,
47
+ "manifest_sha256": "mo_20260908_moA_glycan",
48
+ "cell_id": "mo_A_glycan_s42",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moA_glycan_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "aa2c0104eb4ac96541081eaa2f0858a6b8575d2c83ce869822ee5107d12ddcb9",
60
+ "roles_tsv": "fd43b4aac814f670519120a012a8ef774a2d4c651bcdc3c34488ac7bed2a9a72",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "9256821b6057a857ec009868cda30c10d1133790ca283edf7b36bd04e0c784ad",
87
+ "val": "dba86d01797d9f2a30176028da2d07c3f784d8f013faf2ade70391081a20c846",
88
+ "test": "dba86d01797d9f2a30176028da2d07c3f784d8f013faf2ade70391081a20c846"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "9256821b6057a857ec009868cda30c10d1133790ca283edf7b36bd04e0c784ad",
93
+ "val": "dba86d01797d9f2a30176028da2d07c3f784d8f013faf2ade70391081a20c846",
94
+ "test": "dba86d01797d9f2a30176028da2d07c3f784d8f013faf2ade70391081a20c846"
95
+ },
96
+ "slurm_job_id": "12247963",
97
+ "slurm_array_task_id": "0",
98
+ "slurm_restart_count": null,
99
+ "node": "nova20-amp-1",
100
+ "gpu": "NVIDIA A100-PCIE-40GB"
101
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_A_glycan_s42/results.json ADDED
@@ -0,0 +1,339 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task_type": "regression",
3
+ "architecture": "cross-attention=True pooling=mean interaction=concat + live Bertose",
4
+ "best_metric": 0.3518440936915808,
5
+ "best_metric_is": "validation",
6
+ "best_epoch": 8,
7
+ "early_stopped": true,
8
+ "epochs_ran": 19,
9
+ "test_metrics": {
10
+ "spearman": 0.40237059401182945,
11
+ "pearson": 0.4124835133552551,
12
+ "mse": 0.07252673804759979,
13
+ "macro_spearman": 0.3518440936915808,
14
+ "macro_assays_eligible": 1692,
15
+ "macro_assays_total": 1693
16
+ },
17
+ "test_loss": 0.07250597555225989,
18
+ "history": [
19
+ {
20
+ "epoch": 1,
21
+ "train_loss": 0.08178654776577449,
22
+ "val_loss": 0.07449441553264519,
23
+ "val_metrics": {
24
+ "spearman": 0.33265511442497175,
25
+ "pearson": 0.32712677121162415,
26
+ "mse": 0.07448939234018326,
27
+ "macro_spearman": 0.2746683991825071,
28
+ "macro_assays_eligible": 1692,
29
+ "macro_assays_total": 1693
30
+ },
31
+ "lr_encoder": 7.600383347137149e-06,
32
+ "lr_head": 3.8001916735685747e-05
33
+ },
34
+ {
35
+ "epoch": 2,
36
+ "train_loss": 0.07405700735830484,
37
+ "val_loss": 0.07659707693066983,
38
+ "val_metrics": {
39
+ "spearman": 0.3720591600427688,
40
+ "pearson": 0.37388962507247925,
41
+ "mse": 0.07660496979951859,
42
+ "macro_spearman": 0.3207894429302261,
43
+ "macro_assays_eligible": 1692,
44
+ "macro_assays_total": 1693
45
+ },
46
+ "lr_encoder": 9.992405607129421e-06,
47
+ "lr_head": 4.996202803564711e-05
48
+ },
49
+ {
50
+ "epoch": 3,
51
+ "train_loss": 0.06950348176932596,
52
+ "val_loss": 0.0758063778154766,
53
+ "val_metrics": {
54
+ "spearman": 0.3790002388657775,
55
+ "pearson": 0.38117125630378723,
56
+ "mse": 0.07581878453493118,
57
+ "macro_spearman": 0.32246448806993605,
58
+ "macro_assays_eligible": 1692,
59
+ "macro_assays_total": 1693
60
+ },
61
+ "lr_encoder": 9.931800798174588e-06,
62
+ "lr_head": 4.965900399087294e-05
63
+ },
64
+ {
65
+ "epoch": 4,
66
+ "train_loss": 0.06677828642780369,
67
+ "val_loss": 0.07234074125103684,
68
+ "val_metrics": {
69
+ "spearman": 0.3830188559921474,
70
+ "pearson": 0.39124181866645813,
71
+ "mse": 0.07236022502183914,
72
+ "macro_spearman": 0.3261992361563057,
73
+ "macro_assays_eligible": 1692,
74
+ "macro_assays_total": 1693
75
+ },
76
+ "lr_encoder": 9.811330851097227e-06,
77
+ "lr_head": 4.905665425548613e-05
78
+ },
79
+ {
80
+ "epoch": 5,
81
+ "train_loss": 0.06486930890547266,
82
+ "val_loss": 0.07428531616511191,
83
+ "val_metrics": {
84
+ "spearman": 0.4039017349602038,
85
+ "pearson": 0.40416157245635986,
86
+ "mse": 0.0743052139878273,
87
+ "macro_spearman": 0.34451370150779037,
88
+ "macro_assays_eligible": 1692,
89
+ "macro_assays_total": 1693
90
+ },
91
+ "lr_encoder": 9.632458107861948e-06,
92
+ "lr_head": 4.816229053930974e-05
93
+ },
94
+ {
95
+ "epoch": 6,
96
+ "train_loss": 0.06333981823825936,
97
+ "val_loss": 0.07813900106264728,
98
+ "val_metrics": {
99
+ "spearman": 0.36745023533302545,
100
+ "pearson": 0.3778190016746521,
101
+ "mse": 0.07817018032073975,
102
+ "macro_spearman": 0.32126358975578523,
103
+ "macro_assays_eligible": 1692,
104
+ "macro_assays_total": 1693
105
+ },
106
+ "lr_encoder": 9.397353841264184e-06,
107
+ "lr_head": 4.698676920632092e-05
108
+ },
109
+ {
110
+ "epoch": 7,
111
+ "train_loss": 0.06192197361992377,
112
+ "val_loss": 0.07327253127987744,
113
+ "val_metrics": {
114
+ "spearman": 0.40534431983142905,
115
+ "pearson": 0.4101991355419159,
116
+ "mse": 0.07328689843416214,
117
+ "macro_spearman": 0.3481927746525873,
118
+ "macro_assays_eligible": 1692,
119
+ "macro_assays_total": 1693
120
+ },
121
+ "lr_encoder": 9.108871898619753e-06,
122
+ "lr_head": 4.554435949309877e-05
123
+ },
124
+ {
125
+ "epoch": 8,
126
+ "train_loss": 0.0606018938901666,
127
+ "val_loss": 0.07376345989818776,
128
+ "val_metrics": {
129
+ "spearman": 0.3837663608799417,
130
+ "pearson": 0.3917231857776642,
131
+ "mse": 0.07378335297107697,
132
+ "macro_spearman": 0.34328721248155164,
133
+ "macro_assays_eligible": 1692,
134
+ "macro_assays_total": 1693
135
+ },
136
+ "lr_encoder": 8.770514059924366e-06,
137
+ "lr_head": 4.385257029962183e-05
138
+ },
139
+ {
140
+ "epoch": 9,
141
+ "train_loss": 0.05928352163667491,
142
+ "val_loss": 0.07250597555225989,
143
+ "val_metrics": {
144
+ "spearman": 0.40237059401182945,
145
+ "pearson": 0.4124835133552551,
146
+ "mse": 0.07252673804759979,
147
+ "macro_spearman": 0.3518440936915808,
148
+ "macro_assays_eligible": 1692,
149
+ "macro_assays_total": 1693
150
+ },
151
+ "lr_encoder": 8.386387530988106e-06,
152
+ "lr_head": 4.193193765494053e-05
153
+ },
154
+ {
155
+ "epoch": 10,
156
+ "train_loss": 0.057978798184743385,
157
+ "val_loss": 0.07709632041475843,
158
+ "val_metrics": {
159
+ "spearman": 0.3843505992519891,
160
+ "pearson": 0.3893249034881592,
161
+ "mse": 0.07710817456245422,
162
+ "macro_spearman": 0.3388635804374225,
163
+ "macro_assays_eligible": 1692,
164
+ "macro_assays_total": 1693
165
+ },
166
+ "lr_encoder": 7.961155087520675e-06,
167
+ "lr_head": 3.980577543760337e-05
168
+ },
169
+ {
170
+ "epoch": 11,
171
+ "train_loss": 0.05657076605068323,
172
+ "val_loss": 0.07423298842751067,
173
+ "val_metrics": {
174
+ "spearman": 0.3998620770684936,
175
+ "pearson": 0.4072037935256958,
176
+ "mse": 0.07424739003181458,
177
+ "macro_spearman": 0.34683370121850293,
178
+ "macro_assays_eligible": 1692,
179
+ "macro_assays_total": 1693
180
+ },
181
+ "lr_encoder": 7.499978475350636e-06,
182
+ "lr_head": 3.7499892376753175e-05
183
+ },
184
+ {
185
+ "epoch": 12,
186
+ "train_loss": 0.05521496029820552,
187
+ "val_loss": 0.07893912996399703,
188
+ "val_metrics": {
189
+ "spearman": 0.39539492684739164,
190
+ "pearson": 0.40234139561653137,
191
+ "mse": 0.07895530760288239,
192
+ "macro_spearman": 0.34484224357882526,
193
+ "macro_assays_eligible": 1692,
194
+ "macro_assays_total": 1693
195
+ },
196
+ "lr_encoder": 7.0084557538233345e-06,
197
+ "lr_head": 3.504227876911667e-05
198
+ },
199
+ {
200
+ "epoch": 13,
201
+ "train_loss": 0.05384385342549856,
202
+ "val_loss": 0.07758754271336282,
203
+ "val_metrics": {
204
+ "spearman": 0.379476937601036,
205
+ "pearson": 0.38807427883148193,
206
+ "mse": 0.07760068774223328,
207
+ "macro_spearman": 0.33086376694394215,
208
+ "macro_assays_eligible": 1692,
209
+ "macro_assays_total": 1693
210
+ },
211
+ "lr_encoder": 6.492553342943792e-06,
212
+ "lr_head": 3.246276671471896e-05
213
+ },
214
+ {
215
+ "epoch": 14,
216
+ "train_loss": 0.05236243655586875,
217
+ "val_loss": 0.08031580967745955,
218
+ "val_metrics": {
219
+ "spearman": 0.3715908296671469,
220
+ "pearson": 0.383950412273407,
221
+ "mse": 0.08032692223787308,
222
+ "macro_spearman": 0.3383192853501629,
223
+ "macro_assays_eligible": 1692,
224
+ "macro_assays_total": 1693
225
+ },
226
+ "lr_encoder": 5.958533599120159e-06,
227
+ "lr_head": 2.979266799560079e-05
228
+ },
229
+ {
230
+ "epoch": 15,
231
+ "train_loss": 0.050935477340176526,
232
+ "val_loss": 0.08091087176993787,
233
+ "val_metrics": {
234
+ "spearman": 0.3878922278405009,
235
+ "pearson": 0.39411112666130066,
236
+ "mse": 0.08091336488723755,
237
+ "macro_spearman": 0.33990795245997407,
238
+ "macro_assays_eligible": 1692,
239
+ "macro_assays_total": 1693
240
+ },
241
+ "lr_encoder": 5.412878798640137e-06,
242
+ "lr_head": 2.7064393993200682e-05
243
+ },
244
+ {
245
+ "epoch": 16,
246
+ "train_loss": 0.049360674873292626,
247
+ "val_loss": 0.07987832513397107,
248
+ "val_metrics": {
249
+ "spearman": 0.38379380252351775,
250
+ "pearson": 0.38983529806137085,
251
+ "mse": 0.07989616692066193,
252
+ "macro_spearman": 0.3426433799929684,
253
+ "macro_assays_eligible": 1692,
254
+ "macro_assays_total": 1693
255
+ },
256
+ "lr_encoder": 4.862212451617974e-06,
257
+ "lr_head": 2.4311062258089866e-05
258
+ },
259
+ {
260
+ "epoch": 17,
261
+ "train_loss": 0.04789994738429645,
262
+ "val_loss": 0.08231175134041135,
263
+ "val_metrics": {
264
+ "spearman": 0.3703845261663136,
265
+ "pearson": 0.37726807594299316,
266
+ "mse": 0.08232666552066803,
267
+ "macro_spearman": 0.32730402321783136,
268
+ "macro_assays_eligible": 1692,
269
+ "macro_assays_total": 1693
270
+ },
271
+ "lr_encoder": 4.3132189015541166e-06,
272
+ "lr_head": 2.156609450777058e-05
273
+ },
274
+ {
275
+ "epoch": 18,
276
+ "train_loss": 0.046404292282118356,
277
+ "val_loss": 0.08147809532405346,
278
+ "val_metrics": {
279
+ "spearman": 0.3681362465685154,
280
+ "pearson": 0.37861087918281555,
281
+ "mse": 0.08149037510156631,
282
+ "macro_spearman": 0.32856892560631556,
283
+ "macro_assays_eligible": 1692,
284
+ "macro_assays_total": 1693
285
+ },
286
+ "lr_encoder": 3.772562186459968e-06,
287
+ "lr_head": 1.886281093229984e-05
288
+ },
289
+ {
290
+ "epoch": 19,
291
+ "train_loss": 0.0449393058915869,
292
+ "val_loss": 0.08430918386843124,
293
+ "val_metrics": {
294
+ "spearman": 0.3660089664694847,
295
+ "pearson": 0.37466129660606384,
296
+ "mse": 0.08431710302829742,
297
+ "macro_spearman": 0.32766400341899166,
298
+ "macro_assays_eligible": 1692,
299
+ "macro_assays_total": 1693
300
+ },
301
+ "lr_encoder": 3.246805146463778e-06,
302
+ "lr_head": 1.6234025732318888e-05
303
+ }
304
+ ],
305
+ "fingerprint": "cbcc86152b41f424bf9d201d53843e0fdeb2cd9f934e5a1d3f37fe369c35f874",
306
+ "input_hashes": {
307
+ "csv": "aa2c0104eb4ac96541081eaa2f0858a6b8575d2c83ce869822ee5107d12ddcb9",
308
+ "roles_tsv": "fd43b4aac814f670519120a012a8ef774a2d4c651bcdc3c34488ac7bed2a9a72",
309
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
310
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
311
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
312
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
313
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
314
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
315
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
316
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
317
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
318
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
319
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
320
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
321
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
322
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
323
+ "_imported_modules": {
324
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
325
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
326
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
327
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
328
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
329
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
330
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
331
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
332
+ },
333
+ "_roster_digests": {
334
+ "train": "9256821b6057a857ec009868cda30c10d1133790ca283edf7b36bd04e0c784ad",
335
+ "val": "dba86d01797d9f2a30176028da2d07c3f784d8f013faf2ade70391081a20c846",
336
+ "test": "dba86d01797d9f2a30176028da2d07c3f784d8f013faf2ade70391081a20c846"
337
+ }
338
+ }
339
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_C_doubly_s42/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mo_C_doubly_s42",
3
+ "fingerprint": "20f398ef9542391b9fa12a01e6db7ad58ced7587299a05dfd707be73ddbb9ec6",
4
+ "manifest_sha256": "mo_20260908_moC_doubly",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_C_doubly_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_mo_20260908_mo_C_doubly_s42",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 42,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_C_doubly_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_doubly_cold",
42
+ "test_stratum": "val_doubly_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 406483,
45
+ "expected_val_rows": 3822,
46
+ "expected_test_rows": 3822,
47
+ "manifest_sha256": "mo_20260908_moC_doubly",
48
+ "cell_id": "mo_C_doubly_s42",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moC_doubly_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "e43e150423cb998a77037ad6d82c158a65431b59c46b168febf263ad36267df4",
60
+ "roles_tsv": "e82fb6d73d3ce3ff33e32bd0a8d2e81a0aa4b19b8819bf18339c4aa0382dc5fd",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
87
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
88
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
93
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
94
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
95
+ },
96
+ "slurm_job_id": "12247962",
97
+ "slurm_array_task_id": "2",
98
+ "slurm_restart_count": null,
99
+ "node": "nova22-gpu-2",
100
+ "gpu": "NVIDIA A100 80GB PCIe"
101
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mo_20260908_mo_C_doubly_s42/results.json ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task_type": "regression",
3
+ "architecture": "cross-attention=True pooling=mean interaction=concat + live Bertose",
4
+ "best_metric": 0.18989928027597247,
5
+ "best_metric_is": "validation",
6
+ "best_epoch": 5,
7
+ "early_stopped": true,
8
+ "epochs_ran": 16,
9
+ "test_metrics": {
10
+ "spearman": 0.23230760714549056,
11
+ "pearson": 0.22095870971679688,
12
+ "mse": 0.0810273140668869,
13
+ "macro_spearman": 0.18989928027597247,
14
+ "macro_assays_eligible": 218,
15
+ "macro_assays_total": 218
16
+ },
17
+ "test_loss": 0.08108399153376619,
18
+ "history": [
19
+ {
20
+ "epoch": 1,
21
+ "train_loss": 0.0820725598469075,
22
+ "val_loss": 0.07980532947306831,
23
+ "val_metrics": {
24
+ "spearman": 0.21861695636332937,
25
+ "pearson": 0.21602514386177063,
26
+ "mse": 0.07971526682376862,
27
+ "macro_spearman": 0.1879467160935374,
28
+ "macro_assays_eligible": 218,
29
+ "macro_assays_total": 218
30
+ },
31
+ "lr_encoder": 7.600456921860401e-06,
32
+ "lr_head": 3.800228460930201e-05
33
+ },
34
+ {
35
+ "epoch": 2,
36
+ "train_loss": 0.07351148931354597,
37
+ "val_loss": 0.08033600815882286,
38
+ "val_metrics": {
39
+ "spearman": 0.2209620955843994,
40
+ "pearson": 0.22815755009651184,
41
+ "mse": 0.08022104948759079,
42
+ "macro_spearman": 0.18053212057010906,
43
+ "macro_assays_eligible": 218,
44
+ "macro_assays_total": 218
45
+ },
46
+ "lr_encoder": 9.992405222215124e-06,
47
+ "lr_head": 4.996202611107562e-05
48
+ },
49
+ {
50
+ "epoch": 3,
51
+ "train_loss": 0.0686865865012365,
52
+ "val_loss": 0.08297244912634293,
53
+ "val_metrics": {
54
+ "spearman": 0.2323234331604067,
55
+ "pearson": 0.229558527469635,
56
+ "mse": 0.08294177800416946,
57
+ "macro_spearman": 0.18236981617035328,
58
+ "macro_assays_eligible": 218,
59
+ "macro_assays_total": 218
60
+ },
61
+ "lr_encoder": 9.93179964821535e-06,
62
+ "lr_head": 4.9658998241076745e-05
63
+ },
64
+ {
65
+ "epoch": 4,
66
+ "train_loss": 0.06548485753726821,
67
+ "val_loss": 0.08214062657207251,
68
+ "val_metrics": {
69
+ "spearman": 0.21538099756417373,
70
+ "pearson": 0.21252787113189697,
71
+ "mse": 0.08202281594276428,
72
+ "macro_spearman": 0.17564361108553003,
73
+ "macro_assays_eligible": 218,
74
+ "macro_assays_total": 218
75
+ },
76
+ "lr_encoder": 9.811328950051998e-06,
77
+ "lr_head": 4.905664475025999e-05
78
+ },
79
+ {
80
+ "epoch": 5,
81
+ "train_loss": 0.06347042579143139,
82
+ "val_loss": 0.07959065570806463,
83
+ "val_metrics": {
84
+ "spearman": 0.22659004415366746,
85
+ "pearson": 0.22342076897621155,
86
+ "mse": 0.07952506840229034,
87
+ "macro_spearman": 0.18600536931930212,
88
+ "macro_assays_eligible": 218,
89
+ "macro_assays_total": 218
90
+ },
91
+ "lr_encoder": 9.632455478806839e-06,
92
+ "lr_head": 4.816227739403419e-05
93
+ },
94
+ {
95
+ "epoch": 6,
96
+ "train_loss": 0.06186302897038609,
97
+ "val_loss": 0.08108399153376619,
98
+ "val_metrics": {
99
+ "spearman": 0.23230760714549056,
100
+ "pearson": 0.22095870971679688,
101
+ "mse": 0.0810273140668869,
102
+ "macro_spearman": 0.18989928027597247,
103
+ "macro_assays_eligible": 218,
104
+ "macro_assays_total": 218
105
+ },
106
+ "lr_encoder": 9.397350516112365e-06,
107
+ "lr_head": 4.698675258056182e-05
108
+ },
109
+ {
110
+ "epoch": 7,
111
+ "train_loss": 0.06046433661505199,
112
+ "val_loss": 0.08360768208901087,
113
+ "val_metrics": {
114
+ "spearman": 0.20523514826535216,
115
+ "pearson": 0.20831626653671265,
116
+ "mse": 0.0835791826248169,
117
+ "macro_spearman": 0.17609303468973836,
118
+ "macro_assays_eligible": 218,
119
+ "macro_assays_total": 218
120
+ },
121
+ "lr_encoder": 9.108867917734062e-06,
122
+ "lr_head": 4.554433958867031e-05
123
+ },
124
+ {
125
+ "epoch": 8,
126
+ "train_loss": 0.05909844870552025,
127
+ "val_loss": 0.0824368055909872,
128
+ "val_metrics": {
129
+ "spearman": 0.20461729443087306,
130
+ "pearson": 0.20222266018390656,
131
+ "mse": 0.08249732106924057,
132
+ "macro_spearman": 0.17707328238386544,
133
+ "macro_assays_eligible": 218,
134
+ "macro_assays_total": 218
135
+ },
136
+ "lr_encoder": 8.770509471627362e-06,
137
+ "lr_head": 4.385254735813681e-05
138
+ },
139
+ {
140
+ "epoch": 9,
141
+ "train_loss": 0.057828584789735946,
142
+ "val_loss": 0.08604417247697711,
143
+ "val_metrics": {
144
+ "spearman": 0.20312934852690515,
145
+ "pearson": 0.2025664895772934,
146
+ "mse": 0.08613406866788864,
147
+ "macro_spearman": 0.16849112239141736,
148
+ "macro_assays_eligible": 218,
149
+ "macro_assays_total": 218
150
+ },
151
+ "lr_encoder": 8.3863823909755e-06,
152
+ "lr_head": 4.1931911954877495e-05
153
+ },
154
+ {
155
+ "epoch": 10,
156
+ "train_loss": 0.056629490814496,
157
+ "val_loss": 0.08503294388453166,
158
+ "val_metrics": {
159
+ "spearman": 0.20220513112798374,
160
+ "pearson": 0.20082733035087585,
161
+ "mse": 0.08514758199453354,
162
+ "macro_spearman": 0.16524417323252355,
163
+ "macro_assays_eligible": 218,
164
+ "macro_assays_total": 218
165
+ },
166
+ "lr_encoder": 7.961149458185257e-06,
167
+ "lr_head": 3.9805747290926286e-05
168
+ },
169
+ {
170
+ "epoch": 11,
171
+ "train_loss": 0.05531735030949904,
172
+ "val_loss": 0.08600124850248297,
173
+ "val_metrics": {
174
+ "spearman": 0.198682925808775,
175
+ "pearson": 0.1959197223186493,
176
+ "mse": 0.08607515692710876,
177
+ "macro_spearman": 0.17737299060322365,
178
+ "macro_assays_eligible": 218,
179
+ "macro_assays_total": 218
180
+ },
181
+ "lr_encoder": 7.499972425024911e-06,
182
+ "lr_head": 3.7499862125124546e-05
183
+ },
184
+ {
185
+ "epoch": 12,
186
+ "train_loss": 0.05402435923889215,
187
+ "val_loss": 0.09162988619258007,
188
+ "val_metrics": {
189
+ "spearman": 0.16928709292866903,
190
+ "pearson": 0.17052659392356873,
191
+ "mse": 0.09164858609437943,
192
+ "macro_spearman": 0.12841904491089645,
193
+ "macro_assays_eligible": 218,
194
+ "macro_assays_total": 218
195
+ },
196
+ "lr_encoder": 7.008449355950063e-06,
197
+ "lr_head": 3.504224677975031e-05
198
+ },
199
+ {
200
+ "epoch": 13,
201
+ "train_loss": 0.052670215866714304,
202
+ "val_loss": 0.09579282198101283,
203
+ "val_metrics": {
204
+ "spearman": 0.15138558513405728,
205
+ "pearson": 0.15384550392627716,
206
+ "mse": 0.09586914628744125,
207
+ "macro_spearman": 0.12292897648647666,
208
+ "macro_assays_eligible": 218,
209
+ "macro_assays_total": 218
210
+ },
211
+ "lr_encoder": 6.492546675184488e-06,
212
+ "lr_head": 3.246273337592243e-05
213
+ },
214
+ {
215
+ "epoch": 14,
216
+ "train_loss": 0.05133482151828506,
217
+ "val_loss": 0.0983895618778964,
218
+ "val_metrics": {
219
+ "spearman": 0.16096614573204776,
220
+ "pearson": 0.16646699607372284,
221
+ "mse": 0.09847484529018402,
222
+ "macro_spearman": 0.1471614747891466,
223
+ "macro_assays_eligible": 218,
224
+ "macro_assays_total": 218
225
+ },
226
+ "lr_encoder": 5.958526742412386e-06,
227
+ "lr_head": 2.9792633712061927e-05
228
+ },
229
+ {
230
+ "epoch": 15,
231
+ "train_loss": 0.04995875641009164,
232
+ "val_loss": 0.10370649403582016,
233
+ "val_metrics": {
234
+ "spearman": 0.1388411585409256,
235
+ "pearson": 0.15041641891002655,
236
+ "mse": 0.10373455286026001,
237
+ "macro_spearman": 0.11630659583110417,
238
+ "macro_assays_eligible": 218,
239
+ "macro_assays_total": 218
240
+ },
241
+ "lr_encoder": 5.412871836215041e-06,
242
+ "lr_head": 2.70643591810752e-05
243
+ },
244
+ {
245
+ "epoch": 16,
246
+ "train_loss": 0.04850863079172903,
247
+ "val_loss": 0.09986912375316023,
248
+ "val_metrics": {
249
+ "spearman": 0.1322993495504737,
250
+ "pearson": 0.13906846940517426,
251
+ "mse": 0.09997473657131195,
252
+ "macro_spearman": 0.10992753432795928,
253
+ "macro_assays_eligible": 218,
254
+ "macro_assays_total": 218
255
+ },
256
+ "lr_encoder": 4.862205467989965e-06,
257
+ "lr_head": 2.431102733994982e-05
258
+ }
259
+ ],
260
+ "fingerprint": "20f398ef9542391b9fa12a01e6db7ad58ced7587299a05dfd707be73ddbb9ec6",
261
+ "input_hashes": {
262
+ "csv": "e43e150423cb998a77037ad6d82c158a65431b59c46b168febf263ad36267df4",
263
+ "roles_tsv": "e82fb6d73d3ce3ff33e32bd0a8d2e81a0aa4b19b8819bf18339c4aa0382dc5fd",
264
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
265
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
266
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
267
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
268
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
269
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
270
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
271
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
272
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
273
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
274
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
275
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
276
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
277
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
278
+ "_imported_modules": {
279
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
280
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
281
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
282
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
283
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
284
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
285
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
286
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
287
+ },
288
+ "_roster_digests": {
289
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
290
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
291
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
292
+ }
293
+ }
294
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s43/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mof_B_protein_s43",
3
+ "fingerprint": "10fbf6df61355097aaa03b5b9db42822af419b2c1696866a589fca9efe80bbb9",
4
+ "manifest_sha256": "mof_20260909_moB_protein",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_B_protein_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_mof_20260909_mof_B_protein_s43",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 43,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_B_protein_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_protein_cold",
42
+ "test_stratum": "val_protein_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 480431,
45
+ "expected_val_rows": 39477,
46
+ "expected_test_rows": 39477,
47
+ "manifest_sha256": "mof_20260909_moB_protein",
48
+ "cell_id": "mof_B_protein_s43",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moB_protein_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "db08ac7409ae5f119a706f8acb7c486154f10f70f59571fa7306178032ec6990",
60
+ "roles_tsv": "6ae90f6fed8e1874479b007d1537531b1a9f9f4db5a89855a9d48947ee0a88bc",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
87
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
88
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
93
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
94
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
95
+ },
96
+ "slurm_job_id": "12252895",
97
+ "slurm_array_task_id": "0",
98
+ "slurm_restart_count": null,
99
+ "node": "nova20-amp-1",
100
+ "gpu": "NVIDIA A100-PCIE-40GB"
101
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s43/results.json ADDED
@@ -0,0 +1,294 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task_type": "regression",
3
+ "architecture": "cross-attention=True pooling=mean interaction=concat + live Bertose",
4
+ "best_metric": 0.2616803241549893,
5
+ "best_metric_is": "validation",
6
+ "best_epoch": 5,
7
+ "early_stopped": true,
8
+ "epochs_ran": 16,
9
+ "test_metrics": {
10
+ "spearman": 0.26465318005549066,
11
+ "pearson": 0.28739896416664124,
12
+ "mse": 0.07721278071403503,
13
+ "macro_spearman": 0.2616803241549893,
14
+ "macro_assays_eligible": 246,
15
+ "macro_assays_total": 246
16
+ },
17
+ "test_loss": 0.07721429514265475,
18
+ "history": [
19
+ {
20
+ "epoch": 1,
21
+ "train_loss": 0.07954162840542994,
22
+ "val_loss": 0.07782782489325205,
23
+ "val_metrics": {
24
+ "spearman": 0.22831785172608393,
25
+ "pearson": 0.2535228133201599,
26
+ "mse": 0.0778268352150917,
27
+ "macro_spearman": 0.19343641142546214,
28
+ "macro_assays_eligible": 246,
29
+ "macro_assays_total": 246
30
+ },
31
+ "lr_encoder": 7.600386589840024e-06,
32
+ "lr_head": 3.8001932949200124e-05
33
+ },
34
+ {
35
+ "epoch": 2,
36
+ "train_loss": 0.0705886414306581,
37
+ "val_loss": 0.07796654568281706,
38
+ "val_metrics": {
39
+ "spearman": 0.24540228051404406,
40
+ "pearson": 0.26456552743911743,
41
+ "mse": 0.0779670774936676,
42
+ "macro_spearman": 0.2274026008849437,
43
+ "macro_assays_eligible": 246,
44
+ "macro_assays_total": 246
45
+ },
46
+ "lr_encoder": 9.992405590165015e-06,
47
+ "lr_head": 4.996202795082508e-05
48
+ },
49
+ {
50
+ "epoch": 3,
51
+ "train_loss": 0.06549934623372175,
52
+ "val_loss": 0.07865211885042091,
53
+ "val_metrics": {
54
+ "spearman": 0.25215879998564106,
55
+ "pearson": 0.2773054242134094,
56
+ "mse": 0.07865151017904282,
57
+ "macro_spearman": 0.2467353133105788,
58
+ "macro_assays_eligible": 246,
59
+ "macro_assays_total": 246
60
+ },
61
+ "lr_encoder": 9.931800747491785e-06,
62
+ "lr_head": 4.965900373745893e-05
63
+ },
64
+ {
65
+ "epoch": 4,
66
+ "train_loss": 0.06254243126504527,
67
+ "val_loss": 0.07637372648575898,
68
+ "val_metrics": {
69
+ "spearman": 0.2581918487150839,
70
+ "pearson": 0.27826398611068726,
71
+ "mse": 0.0763733983039856,
72
+ "macro_spearman": 0.2570732451916451,
73
+ "macro_assays_eligible": 246,
74
+ "macro_assays_total": 246
75
+ },
76
+ "lr_encoder": 9.811330767311252e-06,
77
+ "lr_head": 4.905665383655626e-05
78
+ },
79
+ {
80
+ "epoch": 5,
81
+ "train_loss": 0.06060447233149826,
82
+ "val_loss": 0.07684949162601822,
83
+ "val_metrics": {
84
+ "spearman": 0.2612953255933512,
85
+ "pearson": 0.2877562940120697,
86
+ "mse": 0.07684440910816193,
87
+ "macro_spearman": 0.25594701858636026,
88
+ "macro_assays_eligible": 246,
89
+ "macro_assays_total": 246
90
+ },
91
+ "lr_encoder": 9.632457991989846e-06,
92
+ "lr_head": 4.816228995994923e-05
93
+ },
94
+ {
95
+ "epoch": 6,
96
+ "train_loss": 0.059014895508832266,
97
+ "val_loss": 0.07721429514265475,
98
+ "val_metrics": {
99
+ "spearman": 0.26465318005549066,
100
+ "pearson": 0.28739896416664124,
101
+ "mse": 0.07721278071403503,
102
+ "macro_spearman": 0.2616803241549893,
103
+ "macro_assays_eligible": 246,
104
+ "macro_assays_total": 246
105
+ },
106
+ "lr_encoder": 9.397353694712487e-06,
107
+ "lr_head": 4.6986768473562434e-05
108
+ },
109
+ {
110
+ "epoch": 7,
111
+ "train_loss": 0.05770590116580162,
112
+ "val_loss": 0.07777405114817272,
113
+ "val_metrics": {
114
+ "spearman": 0.25557701527812,
115
+ "pearson": 0.2826829254627228,
116
+ "mse": 0.07777171581983566,
117
+ "macro_spearman": 0.25234290869807796,
118
+ "macro_assays_eligible": 246,
119
+ "macro_assays_total": 246
120
+ },
121
+ "lr_encoder": 9.108871723167402e-06,
122
+ "lr_head": 4.554435861583701e-05
123
+ },
124
+ {
125
+ "epoch": 8,
126
+ "train_loss": 0.05641176031927989,
127
+ "val_loss": 0.07836433754391618,
128
+ "val_metrics": {
129
+ "spearman": 0.252197774483041,
130
+ "pearson": 0.2744917571544647,
131
+ "mse": 0.07836487889289856,
132
+ "macro_spearman": 0.25088632948434425,
133
+ "macro_assays_eligible": 246,
134
+ "macro_assays_total": 246
135
+ },
136
+ "lr_encoder": 8.770513857701111e-06,
137
+ "lr_head": 4.385256928850556e-05
138
+ },
139
+ {
140
+ "epoch": 9,
141
+ "train_loss": 0.05523392668176769,
142
+ "val_loss": 0.07975887371350919,
143
+ "val_metrics": {
144
+ "spearman": 0.2480565658809454,
145
+ "pearson": 0.2695157825946808,
146
+ "mse": 0.07976102828979492,
147
+ "macro_spearman": 0.24774700422451598,
148
+ "macro_assays_eligible": 246,
149
+ "macro_assays_total": 246
150
+ },
151
+ "lr_encoder": 8.386387304448665e-06,
152
+ "lr_head": 4.193193652224333e-05
153
+ },
154
+ {
155
+ "epoch": 10,
156
+ "train_loss": 0.05410393620964645,
157
+ "val_loss": 0.07961486578983899,
158
+ "val_metrics": {
159
+ "spearman": 0.24158881897101642,
160
+ "pearson": 0.265257865190506,
161
+ "mse": 0.07961051166057587,
162
+ "macro_spearman": 0.24260551808764666,
163
+ "macro_assays_eligible": 246,
164
+ "macro_assays_total": 246
165
+ },
166
+ "lr_encoder": 7.961154839414931e-06,
167
+ "lr_head": 3.9805774197074656e-05
168
+ },
169
+ {
170
+ "epoch": 11,
171
+ "train_loss": 0.052832345262743496,
172
+ "val_loss": 0.07933218951217794,
173
+ "val_metrics": {
174
+ "spearman": 0.24484638376952045,
175
+ "pearson": 0.2682126760482788,
176
+ "mse": 0.07932918518781662,
177
+ "macro_spearman": 0.24749593452745058,
178
+ "macro_assays_eligible": 246,
179
+ "macro_assays_total": 246
180
+ },
181
+ "lr_encoder": 7.4999782086902555e-06,
182
+ "lr_head": 3.749989104345127e-05
183
+ },
184
+ {
185
+ "epoch": 12,
186
+ "train_loss": 0.05168314740412603,
187
+ "val_loss": 0.08014735692396542,
188
+ "val_metrics": {
189
+ "spearman": 0.23723716672899273,
190
+ "pearson": 0.25973254442214966,
191
+ "mse": 0.08014728873968124,
192
+ "macro_spearman": 0.2429886585492663,
193
+ "macro_assays_eligible": 246,
194
+ "macro_assays_total": 246
195
+ },
196
+ "lr_encoder": 7.008455471845214e-06,
197
+ "lr_head": 3.504227735922606e-05
198
+ },
199
+ {
200
+ "epoch": 13,
201
+ "train_loss": 0.05040372731105789,
202
+ "val_loss": 0.08099161927260737,
203
+ "val_metrics": {
204
+ "spearman": 0.2331638007735348,
205
+ "pearson": 0.2553894519805908,
206
+ "mse": 0.08098489046096802,
207
+ "macro_spearman": 0.23570626741202766,
208
+ "macro_assays_eligible": 246,
209
+ "macro_assays_total": 246
210
+ },
211
+ "lr_encoder": 6.4925530490707626e-06,
212
+ "lr_head": 3.246276524535381e-05
213
+ },
214
+ {
215
+ "epoch": 14,
216
+ "train_loss": 0.04912812885086062,
217
+ "val_loss": 0.08161971862224117,
218
+ "val_metrics": {
219
+ "spearman": 0.23137538105705088,
220
+ "pearson": 0.24989166855812073,
221
+ "mse": 0.08161360770463943,
222
+ "macro_spearman": 0.23251248469650745,
223
+ "macro_assays_eligible": 246,
224
+ "macro_assays_total": 246
225
+ },
226
+ "lr_encoder": 5.958533296919441e-06,
227
+ "lr_head": 2.97926664845972e-05
228
+ },
229
+ {
230
+ "epoch": 15,
231
+ "train_loss": 0.047906441031257345,
232
+ "val_loss": 0.08224580197095437,
233
+ "val_metrics": {
234
+ "spearman": 0.23034203959773117,
235
+ "pearson": 0.2500419318675995,
236
+ "mse": 0.08224118500947952,
237
+ "macro_spearman": 0.23809430388858327,
238
+ "macro_assays_eligible": 246,
239
+ "macro_assays_total": 246
240
+ },
241
+ "lr_encoder": 5.41287849178004e-06,
242
+ "lr_head": 2.7064392458900194e-05
243
+ },
244
+ {
245
+ "epoch": 16,
246
+ "train_loss": 0.046632020503746034,
247
+ "val_loss": 0.08478603510841172,
248
+ "val_metrics": {
249
+ "spearman": 0.22068337270420055,
250
+ "pearson": 0.23975464701652527,
251
+ "mse": 0.08478289842605591,
252
+ "macro_spearman": 0.22897272805292576,
253
+ "macro_assays_eligible": 246,
254
+ "macro_assays_total": 246
255
+ },
256
+ "lr_encoder": 4.862212143823363e-06,
257
+ "lr_head": 2.4311060719116812e-05
258
+ }
259
+ ],
260
+ "fingerprint": "10fbf6df61355097aaa03b5b9db42822af419b2c1696866a589fca9efe80bbb9",
261
+ "input_hashes": {
262
+ "csv": "db08ac7409ae5f119a706f8acb7c486154f10f70f59571fa7306178032ec6990",
263
+ "roles_tsv": "6ae90f6fed8e1874479b007d1537531b1a9f9f4db5a89855a9d48947ee0a88bc",
264
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
265
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
266
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
267
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
268
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
269
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
270
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
271
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
272
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
273
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
274
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
275
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
276
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
277
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
278
+ "_imported_modules": {
279
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
280
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
281
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
282
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
283
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
284
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
285
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
286
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
287
+ },
288
+ "_roster_digests": {
289
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
290
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
291
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
292
+ }
293
+ }
294
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s44/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mof_B_protein_s44",
3
+ "fingerprint": "b50035032c9c5017afd6328b7c1e4ab3f6ff7eeb8a1d126c918c5567e207115e",
4
+ "manifest_sha256": "mof_20260909_moB_protein",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_B_protein_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_mof_20260909_mof_B_protein_s44",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 44,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_B_protein_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_protein_cold",
42
+ "test_stratum": "val_protein_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 480431,
45
+ "expected_val_rows": 39477,
46
+ "expected_test_rows": 39477,
47
+ "manifest_sha256": "mof_20260909_moB_protein",
48
+ "cell_id": "mof_B_protein_s44",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moB_protein_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "db08ac7409ae5f119a706f8acb7c486154f10f70f59571fa7306178032ec6990",
60
+ "roles_tsv": "6ae90f6fed8e1874479b007d1537531b1a9f9f4db5a89855a9d48947ee0a88bc",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
87
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
88
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
93
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
94
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
95
+ },
96
+ "slurm_job_id": "12252896",
97
+ "slurm_array_task_id": "1",
98
+ "slurm_restart_count": null,
99
+ "node": "nova20-amp-1",
100
+ "gpu": "NVIDIA A100-PCIE-40GB"
101
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_B_protein_s44/results.json ADDED
@@ -0,0 +1,324 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task_type": "regression",
3
+ "architecture": "cross-attention=True pooling=mean interaction=concat + live Bertose",
4
+ "best_metric": 0.2619743701834269,
5
+ "best_metric_is": "validation",
6
+ "best_epoch": 7,
7
+ "early_stopped": true,
8
+ "epochs_ran": 18,
9
+ "test_metrics": {
10
+ "spearman": 0.2522903089693993,
11
+ "pearson": 0.27707183361053467,
12
+ "mse": 0.07991175353527069,
13
+ "macro_spearman": 0.2619743701834269,
14
+ "macro_assays_eligible": 246,
15
+ "macro_assays_total": 246
16
+ },
17
+ "test_loss": 0.07990999335522735,
18
+ "history": [
19
+ {
20
+ "epoch": 1,
21
+ "train_loss": 0.07981738362739721,
22
+ "val_loss": 0.07805284763032773,
23
+ "val_metrics": {
24
+ "spearman": 0.22559178242900987,
25
+ "pearson": 0.2486092448234558,
26
+ "mse": 0.07805142551660538,
27
+ "macro_spearman": 0.20130664561426273,
28
+ "macro_assays_eligible": 246,
29
+ "macro_assays_total": 246
30
+ },
31
+ "lr_encoder": 7.600386589840024e-06,
32
+ "lr_head": 3.8001932949200124e-05
33
+ },
34
+ {
35
+ "epoch": 2,
36
+ "train_loss": 0.07078751392609924,
37
+ "val_loss": 0.07947275961931898,
38
+ "val_metrics": {
39
+ "spearman": 0.24995568370609297,
40
+ "pearson": 0.28168702125549316,
41
+ "mse": 0.07947273552417755,
42
+ "macro_spearman": 0.23893393270644228,
43
+ "macro_assays_eligible": 246,
44
+ "macro_assays_total": 246
45
+ },
46
+ "lr_encoder": 9.992405590165015e-06,
47
+ "lr_head": 4.996202795082508e-05
48
+ },
49
+ {
50
+ "epoch": 3,
51
+ "train_loss": 0.0660612302130991,
52
+ "val_loss": 0.07649334600554535,
53
+ "val_metrics": {
54
+ "spearman": 0.2534524155438286,
55
+ "pearson": 0.283633828163147,
56
+ "mse": 0.07649430632591248,
57
+ "macro_spearman": 0.25244388911037446,
58
+ "macro_assays_eligible": 246,
59
+ "macro_assays_total": 246
60
+ },
61
+ "lr_encoder": 9.931800747491785e-06,
62
+ "lr_head": 4.965900373745893e-05
63
+ },
64
+ {
65
+ "epoch": 4,
66
+ "train_loss": 0.06302868131366045,
67
+ "val_loss": 0.07701158229068429,
68
+ "val_metrics": {
69
+ "spearman": 0.25958664299609535,
70
+ "pearson": 0.2883581519126892,
71
+ "mse": 0.07701101154088974,
72
+ "macro_spearman": 0.2571741323432227,
73
+ "macro_assays_eligible": 246,
74
+ "macro_assays_total": 246
75
+ },
76
+ "lr_encoder": 9.811330767311252e-06,
77
+ "lr_head": 4.905665383655626e-05
78
+ },
79
+ {
80
+ "epoch": 5,
81
+ "train_loss": 0.06085977359548701,
82
+ "val_loss": 0.07774806868985319,
83
+ "val_metrics": {
84
+ "spearman": 0.25843513345166563,
85
+ "pearson": 0.28806793689727783,
86
+ "mse": 0.07775115966796875,
87
+ "macro_spearman": 0.25360786179010825,
88
+ "macro_assays_eligible": 246,
89
+ "macro_assays_total": 246
90
+ },
91
+ "lr_encoder": 9.632457991989846e-06,
92
+ "lr_head": 4.816228995994923e-05
93
+ },
94
+ {
95
+ "epoch": 6,
96
+ "train_loss": 0.0592839516819901,
97
+ "val_loss": 0.07740682547208604,
98
+ "val_metrics": {
99
+ "spearman": 0.2630817552232598,
100
+ "pearson": 0.287427693605423,
101
+ "mse": 0.07740595936775208,
102
+ "macro_spearman": 0.26120600347796635,
103
+ "macro_assays_eligible": 246,
104
+ "macro_assays_total": 246
105
+ },
106
+ "lr_encoder": 9.397353694712487e-06,
107
+ "lr_head": 4.6986768473562434e-05
108
+ },
109
+ {
110
+ "epoch": 7,
111
+ "train_loss": 0.057865291745236296,
112
+ "val_loss": 0.07692075606037047,
113
+ "val_metrics": {
114
+ "spearman": 0.25688249514777667,
115
+ "pearson": 0.28290003538131714,
116
+ "mse": 0.07692264020442963,
117
+ "macro_spearman": 0.2534613235639173,
118
+ "macro_assays_eligible": 246,
119
+ "macro_assays_total": 246
120
+ },
121
+ "lr_encoder": 9.108871723167402e-06,
122
+ "lr_head": 4.554435861583701e-05
123
+ },
124
+ {
125
+ "epoch": 8,
126
+ "train_loss": 0.0565786789235537,
127
+ "val_loss": 0.07990999335522735,
128
+ "val_metrics": {
129
+ "spearman": 0.2522903089693993,
130
+ "pearson": 0.27707183361053467,
131
+ "mse": 0.07991175353527069,
132
+ "macro_spearman": 0.2619743701834269,
133
+ "macro_assays_eligible": 246,
134
+ "macro_assays_total": 246
135
+ },
136
+ "lr_encoder": 8.770513857701111e-06,
137
+ "lr_head": 4.385256928850556e-05
138
+ },
139
+ {
140
+ "epoch": 9,
141
+ "train_loss": 0.05532620246784516,
142
+ "val_loss": 0.07763330655163411,
143
+ "val_metrics": {
144
+ "spearman": 0.25102246262105943,
145
+ "pearson": 0.2788107693195343,
146
+ "mse": 0.07763587683439255,
147
+ "macro_spearman": 0.2544694050064656,
148
+ "macro_assays_eligible": 246,
149
+ "macro_assays_total": 246
150
+ },
151
+ "lr_encoder": 8.386387304448665e-06,
152
+ "lr_head": 4.193193652224333e-05
153
+ },
154
+ {
155
+ "epoch": 10,
156
+ "train_loss": 0.05405828924989818,
157
+ "val_loss": 0.07833248711337533,
158
+ "val_metrics": {
159
+ "spearman": 0.2438800982818175,
160
+ "pearson": 0.2745583653450012,
161
+ "mse": 0.07832974940538406,
162
+ "macro_spearman": 0.2523447986921982,
163
+ "macro_assays_eligible": 246,
164
+ "macro_assays_total": 246
165
+ },
166
+ "lr_encoder": 7.961154839414931e-06,
167
+ "lr_head": 3.9805774197074656e-05
168
+ },
169
+ {
170
+ "epoch": 11,
171
+ "train_loss": 0.0528427298379628,
172
+ "val_loss": 0.08058726410056967,
173
+ "val_metrics": {
174
+ "spearman": 0.23660560596604926,
175
+ "pearson": 0.26180699467658997,
176
+ "mse": 0.08058898150920868,
177
+ "macro_spearman": 0.23424278398802012,
178
+ "macro_assays_eligible": 246,
179
+ "macro_assays_total": 246
180
+ },
181
+ "lr_encoder": 7.4999782086902555e-06,
182
+ "lr_head": 3.749989104345127e-05
183
+ },
184
+ {
185
+ "epoch": 12,
186
+ "train_loss": 0.05164120228723162,
187
+ "val_loss": 0.07976167026538902,
188
+ "val_metrics": {
189
+ "spearman": 0.23261825638111633,
190
+ "pearson": 0.2558090090751648,
191
+ "mse": 0.07976232469081879,
192
+ "macro_spearman": 0.23266531292423337,
193
+ "macro_assays_eligible": 246,
194
+ "macro_assays_total": 246
195
+ },
196
+ "lr_encoder": 7.008455471845214e-06,
197
+ "lr_head": 3.504227735922606e-05
198
+ },
199
+ {
200
+ "epoch": 13,
201
+ "train_loss": 0.0503626140305333,
202
+ "val_loss": 0.08149432827457423,
203
+ "val_metrics": {
204
+ "spearman": 0.2278419512374733,
205
+ "pearson": 0.2485179454088211,
206
+ "mse": 0.08149101585149765,
207
+ "macro_spearman": 0.23217940116093283,
208
+ "macro_assays_eligible": 246,
209
+ "macro_assays_total": 246
210
+ },
211
+ "lr_encoder": 6.4925530490707626e-06,
212
+ "lr_head": 3.246276524535381e-05
213
+ },
214
+ {
215
+ "epoch": 14,
216
+ "train_loss": 0.04906632403745221,
217
+ "val_loss": 0.08259034495818847,
218
+ "val_metrics": {
219
+ "spearman": 0.21830880884551201,
220
+ "pearson": 0.24247127771377563,
221
+ "mse": 0.08259105682373047,
222
+ "macro_spearman": 0.22567405660753373,
223
+ "macro_assays_eligible": 246,
224
+ "macro_assays_total": 246
225
+ },
226
+ "lr_encoder": 5.958533296919441e-06,
227
+ "lr_head": 2.97926664845972e-05
228
+ },
229
+ {
230
+ "epoch": 15,
231
+ "train_loss": 0.047798247342370724,
232
+ "val_loss": 0.0822906217340084,
233
+ "val_metrics": {
234
+ "spearman": 0.22129417925423603,
235
+ "pearson": 0.24620899558067322,
236
+ "mse": 0.08228731155395508,
237
+ "macro_spearman": 0.224604572584952,
238
+ "macro_assays_eligible": 246,
239
+ "macro_assays_total": 246
240
+ },
241
+ "lr_encoder": 5.41287849178004e-06,
242
+ "lr_head": 2.7064392458900194e-05
243
+ },
244
+ {
245
+ "epoch": 16,
246
+ "train_loss": 0.046463664239008225,
247
+ "val_loss": 0.08506318003035829,
248
+ "val_metrics": {
249
+ "spearman": 0.2028253755057865,
250
+ "pearson": 0.2229027897119522,
251
+ "mse": 0.08505822718143463,
252
+ "macro_spearman": 0.21227674417314193,
253
+ "macro_assays_eligible": 246,
254
+ "macro_assays_total": 246
255
+ },
256
+ "lr_encoder": 4.862212143823363e-06,
257
+ "lr_head": 2.4311060719116812e-05
258
+ },
259
+ {
260
+ "epoch": 17,
261
+ "train_loss": 0.0452233674025144,
262
+ "val_loss": 0.08821694987767181,
263
+ "val_metrics": {
264
+ "spearman": 0.20229581542185188,
265
+ "pearson": 0.22442831099033356,
266
+ "mse": 0.08821789920330048,
267
+ "macro_spearman": 0.20634215615444548,
268
+ "macro_assays_eligible": 246,
269
+ "macro_assays_total": 246
270
+ },
271
+ "lr_encoder": 4.3132185965612014e-06,
272
+ "lr_head": 2.1566092982806e-05
273
+ },
274
+ {
275
+ "epoch": 18,
276
+ "train_loss": 0.04388836729720501,
277
+ "val_loss": 0.09027578428121981,
278
+ "val_metrics": {
279
+ "spearman": 0.193795332135738,
280
+ "pearson": 0.2129238247871399,
281
+ "mse": 0.09027887880802155,
282
+ "macro_spearman": 0.20139275176325294,
283
+ "macro_assays_eligible": 246,
284
+ "macro_assays_total": 246
285
+ },
286
+ "lr_encoder": 3.7725618879709492e-06,
287
+ "lr_head": 1.8862809439854746e-05
288
+ }
289
+ ],
290
+ "fingerprint": "b50035032c9c5017afd6328b7c1e4ab3f6ff7eeb8a1d126c918c5567e207115e",
291
+ "input_hashes": {
292
+ "csv": "db08ac7409ae5f119a706f8acb7c486154f10f70f59571fa7306178032ec6990",
293
+ "roles_tsv": "6ae90f6fed8e1874479b007d1537531b1a9f9f4db5a89855a9d48947ee0a88bc",
294
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
295
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
296
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
297
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
298
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
299
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
300
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
301
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
302
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
303
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
304
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
305
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
306
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
307
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
308
+ "_imported_modules": {
309
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
310
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
311
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
312
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
313
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
314
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
315
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
316
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
317
+ },
318
+ "_roster_digests": {
319
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
320
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
321
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
322
+ }
323
+ }
324
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s43/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mof_C_doubly_s43",
3
+ "fingerprint": "788c8377637edb8cc4ba7bde154a1663f648db547dd0bc721c369dab7d8a8d5a",
4
+ "manifest_sha256": "mof_20260909_moC_doubly",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_C_doubly_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_mof_20260909_mof_C_doubly_s43",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 43,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_C_doubly_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_doubly_cold",
42
+ "test_stratum": "val_doubly_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 406483,
45
+ "expected_val_rows": 3822,
46
+ "expected_test_rows": 3822,
47
+ "manifest_sha256": "mof_20260909_moC_doubly",
48
+ "cell_id": "mof_C_doubly_s43",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moC_doubly_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "e43e150423cb998a77037ad6d82c158a65431b59c46b168febf263ad36267df4",
60
+ "roles_tsv": "e82fb6d73d3ce3ff33e32bd0a8d2e81a0aa4b19b8819bf18339c4aa0382dc5fd",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
87
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
88
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
93
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
94
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
95
+ },
96
+ "slurm_job_id": "12252897",
97
+ "slurm_array_task_id": "2",
98
+ "slurm_restart_count": null,
99
+ "node": "nova21-amp-3",
100
+ "gpu": "NVIDIA A100-PCIE-40GB"
101
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s43/results.json ADDED
@@ -0,0 +1,234 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task_type": "regression",
3
+ "architecture": "cross-attention=True pooling=mean interaction=concat + live Bertose",
4
+ "best_metric": 0.2278161550103475,
5
+ "best_metric_is": "validation",
6
+ "best_epoch": 1,
7
+ "early_stopped": true,
8
+ "epochs_ran": 12,
9
+ "test_metrics": {
10
+ "spearman": 0.24371231429579024,
11
+ "pearson": 0.24978864192962646,
12
+ "mse": 0.07957296073436737,
13
+ "macro_spearman": 0.2278161550103475,
14
+ "macro_assays_eligible": 218,
15
+ "macro_assays_total": 218
16
+ },
17
+ "test_loss": 0.07959918103491266,
18
+ "history": [
19
+ {
20
+ "epoch": 1,
21
+ "train_loss": 0.08217019368176276,
22
+ "val_loss": 0.080513754611214,
23
+ "val_metrics": {
24
+ "spearman": 0.18197313252266425,
25
+ "pearson": 0.19518351554870605,
26
+ "mse": 0.08036746084690094,
27
+ "macro_spearman": 0.13920416257310053,
28
+ "macro_assays_eligible": 218,
29
+ "macro_assays_total": 218
30
+ },
31
+ "lr_encoder": 7.600456921860401e-06,
32
+ "lr_head": 3.800228460930201e-05
33
+ },
34
+ {
35
+ "epoch": 2,
36
+ "train_loss": 0.07321907142764479,
37
+ "val_loss": 0.07959918103491266,
38
+ "val_metrics": {
39
+ "spearman": 0.24371231429579024,
40
+ "pearson": 0.24978864192962646,
41
+ "mse": 0.07957296073436737,
42
+ "macro_spearman": 0.2278161550103475,
43
+ "macro_assays_eligible": 218,
44
+ "macro_assays_total": 218
45
+ },
46
+ "lr_encoder": 9.992405222215124e-06,
47
+ "lr_head": 4.996202611107562e-05
48
+ },
49
+ {
50
+ "epoch": 3,
51
+ "train_loss": 0.06850780602590001,
52
+ "val_loss": 0.08010787678261598,
53
+ "val_metrics": {
54
+ "spearman": 0.22582728258155255,
55
+ "pearson": 0.22293958067893982,
56
+ "mse": 0.07997935265302658,
57
+ "macro_spearman": 0.18212787907544617,
58
+ "macro_assays_eligible": 218,
59
+ "macro_assays_total": 218
60
+ },
61
+ "lr_encoder": 9.93179964821535e-06,
62
+ "lr_head": 4.9658998241076745e-05
63
+ },
64
+ {
65
+ "epoch": 4,
66
+ "train_loss": 0.06562513827228887,
67
+ "val_loss": 0.08135794155920545,
68
+ "val_metrics": {
69
+ "spearman": 0.20973701944665502,
70
+ "pearson": 0.2167285680770874,
71
+ "mse": 0.08122111111879349,
72
+ "macro_spearman": 0.1680262191228166,
73
+ "macro_assays_eligible": 218,
74
+ "macro_assays_total": 218
75
+ },
76
+ "lr_encoder": 9.811328950051998e-06,
77
+ "lr_head": 4.905664475025999e-05
78
+ },
79
+ {
80
+ "epoch": 5,
81
+ "train_loss": 0.06357526822885308,
82
+ "val_loss": 0.08136990948890646,
83
+ "val_metrics": {
84
+ "spearman": 0.2147511192432372,
85
+ "pearson": 0.20993298292160034,
86
+ "mse": 0.0812588557600975,
87
+ "macro_spearman": 0.17879476431653582,
88
+ "macro_assays_eligible": 218,
89
+ "macro_assays_total": 218
90
+ },
91
+ "lr_encoder": 9.632455478806839e-06,
92
+ "lr_head": 4.816227739403419e-05
93
+ },
94
+ {
95
+ "epoch": 6,
96
+ "train_loss": 0.06198898692786375,
97
+ "val_loss": 0.08343257292484244,
98
+ "val_metrics": {
99
+ "spearman": 0.1942465614715496,
100
+ "pearson": 0.19803641736507416,
101
+ "mse": 0.08329526335000992,
102
+ "macro_spearman": 0.1478931661753686,
103
+ "macro_assays_eligible": 218,
104
+ "macro_assays_total": 218
105
+ },
106
+ "lr_encoder": 9.397350516112365e-06,
107
+ "lr_head": 4.698675258056182e-05
108
+ },
109
+ {
110
+ "epoch": 7,
111
+ "train_loss": 0.06052576813641251,
112
+ "val_loss": 0.08325061357269685,
113
+ "val_metrics": {
114
+ "spearman": 0.19448283762838364,
115
+ "pearson": 0.20482441782951355,
116
+ "mse": 0.08314784616231918,
117
+ "macro_spearman": 0.14600984283451282,
118
+ "macro_assays_eligible": 218,
119
+ "macro_assays_total": 218
120
+ },
121
+ "lr_encoder": 9.108867917734062e-06,
122
+ "lr_head": 4.554433958867031e-05
123
+ },
124
+ {
125
+ "epoch": 8,
126
+ "train_loss": 0.059245914784017056,
127
+ "val_loss": 0.08221773185456792,
128
+ "val_metrics": {
129
+ "spearman": 0.21589636690321976,
130
+ "pearson": 0.22401371598243713,
131
+ "mse": 0.08215607702732086,
132
+ "macro_spearman": 0.19273642448412445,
133
+ "macro_assays_eligible": 218,
134
+ "macro_assays_total": 218
135
+ },
136
+ "lr_encoder": 8.770509471627362e-06,
137
+ "lr_head": 4.385254735813681e-05
138
+ },
139
+ {
140
+ "epoch": 9,
141
+ "train_loss": 0.05797236338407353,
142
+ "val_loss": 0.08278422035897771,
143
+ "val_metrics": {
144
+ "spearman": 0.20207701461652208,
145
+ "pearson": 0.2028491199016571,
146
+ "mse": 0.08269399404525757,
147
+ "macro_spearman": 0.18161633571304941,
148
+ "macro_assays_eligible": 218,
149
+ "macro_assays_total": 218
150
+ },
151
+ "lr_encoder": 8.3863823909755e-06,
152
+ "lr_head": 4.1931911954877495e-05
153
+ },
154
+ {
155
+ "epoch": 10,
156
+ "train_loss": 0.05678273152126263,
157
+ "val_loss": 0.08959366163859765,
158
+ "val_metrics": {
159
+ "spearman": 0.16949282307127173,
160
+ "pearson": 0.17482900619506836,
161
+ "mse": 0.08958856016397476,
162
+ "macro_spearman": 0.15269489273804726,
163
+ "macro_assays_eligible": 218,
164
+ "macro_assays_total": 218
165
+ },
166
+ "lr_encoder": 7.961149458185257e-06,
167
+ "lr_head": 3.9805747290926286e-05
168
+ },
169
+ {
170
+ "epoch": 11,
171
+ "train_loss": 0.055409021762098595,
172
+ "val_loss": 0.08639660604918996,
173
+ "val_metrics": {
174
+ "spearman": 0.17912118331172416,
175
+ "pearson": 0.1813514530658722,
176
+ "mse": 0.08641572296619415,
177
+ "macro_spearman": 0.14887737256820655,
178
+ "macro_assays_eligible": 218,
179
+ "macro_assays_total": 218
180
+ },
181
+ "lr_encoder": 7.499972425024911e-06,
182
+ "lr_head": 3.7499862125124546e-05
183
+ },
184
+ {
185
+ "epoch": 12,
186
+ "train_loss": 0.05418257805815219,
187
+ "val_loss": 0.09123583193868398,
188
+ "val_metrics": {
189
+ "spearman": 0.17283292579872125,
190
+ "pearson": 0.17656531929969788,
191
+ "mse": 0.0911729484796524,
192
+ "macro_spearman": 0.1595232903648669,
193
+ "macro_assays_eligible": 218,
194
+ "macro_assays_total": 218
195
+ },
196
+ "lr_encoder": 7.008449355950063e-06,
197
+ "lr_head": 3.504224677975031e-05
198
+ }
199
+ ],
200
+ "fingerprint": "788c8377637edb8cc4ba7bde154a1663f648db547dd0bc721c369dab7d8a8d5a",
201
+ "input_hashes": {
202
+ "csv": "e43e150423cb998a77037ad6d82c158a65431b59c46b168febf263ad36267df4",
203
+ "roles_tsv": "e82fb6d73d3ce3ff33e32bd0a8d2e81a0aa4b19b8819bf18339c4aa0382dc5fd",
204
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
205
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
206
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
207
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
208
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
209
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
210
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
211
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
212
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
213
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
214
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
215
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
216
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
217
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
218
+ "_imported_modules": {
219
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
220
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
221
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
222
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
223
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
224
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
225
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
226
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
227
+ },
228
+ "_roster_digests": {
229
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
230
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
231
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
232
+ }
233
+ }
234
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s44/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mof_C_doubly_s44",
3
+ "fingerprint": "feb493f3bf5da120bfada3ab639b04a9e0cd89d0f00633e01c99a89664971d49",
4
+ "manifest_sha256": "mof_20260909_moC_doubly",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_C_doubly_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_mof_20260909_mof_C_doubly_s44",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 44,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_C_doubly_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_doubly_cold",
42
+ "test_stratum": "val_doubly_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 406483,
45
+ "expected_val_rows": 3822,
46
+ "expected_test_rows": 3822,
47
+ "manifest_sha256": "mof_20260909_moC_doubly",
48
+ "cell_id": "mof_C_doubly_s44",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moC_doubly_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "e43e150423cb998a77037ad6d82c158a65431b59c46b168febf263ad36267df4",
60
+ "roles_tsv": "e82fb6d73d3ce3ff33e32bd0a8d2e81a0aa4b19b8819bf18339c4aa0382dc5fd",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
87
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
88
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
93
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
94
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
95
+ },
96
+ "slurm_job_id": "12252894",
97
+ "slurm_array_task_id": "3",
98
+ "slurm_restart_count": null,
99
+ "node": "nova21-amp-3",
100
+ "gpu": "NVIDIA A100-PCIE-40GB"
101
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_mof_20260909_mof_C_doubly_s44/results.json ADDED
@@ -0,0 +1,264 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task_type": "regression",
3
+ "architecture": "cross-attention=True pooling=mean interaction=concat + live Bertose",
4
+ "best_metric": 0.21062218506431446,
5
+ "best_metric_is": "validation",
6
+ "best_epoch": 3,
7
+ "early_stopped": true,
8
+ "epochs_ran": 14,
9
+ "test_metrics": {
10
+ "spearman": 0.23531327082857056,
11
+ "pearson": 0.23060324788093567,
12
+ "mse": 0.08090551197528839,
13
+ "macro_spearman": 0.21062218506431446,
14
+ "macro_assays_eligible": 218,
15
+ "macro_assays_total": 218
16
+ },
17
+ "test_loss": 0.08097927542403341,
18
+ "history": [
19
+ {
20
+ "epoch": 1,
21
+ "train_loss": 0.0823811714034573,
22
+ "val_loss": 0.08316954768573244,
23
+ "val_metrics": {
24
+ "spearman": 0.19923863389392296,
25
+ "pearson": 0.20312944054603577,
26
+ "mse": 0.08306817710399628,
27
+ "macro_spearman": 0.14979364049573743,
28
+ "macro_assays_eligible": 218,
29
+ "macro_assays_total": 218
30
+ },
31
+ "lr_encoder": 7.600456921860401e-06,
32
+ "lr_head": 3.800228460930201e-05
33
+ },
34
+ {
35
+ "epoch": 2,
36
+ "train_loss": 0.07352196992131037,
37
+ "val_loss": 0.08194014184797803,
38
+ "val_metrics": {
39
+ "spearman": 0.23002782642760716,
40
+ "pearson": 0.23304009437561035,
41
+ "mse": 0.08179230988025665,
42
+ "macro_spearman": 0.19135672277353788,
43
+ "macro_assays_eligible": 218,
44
+ "macro_assays_total": 218
45
+ },
46
+ "lr_encoder": 9.992405222215124e-06,
47
+ "lr_head": 4.996202611107562e-05
48
+ },
49
+ {
50
+ "epoch": 3,
51
+ "train_loss": 0.06890469362752077,
52
+ "val_loss": 0.08131420062854885,
53
+ "val_metrics": {
54
+ "spearman": 0.2344376640032703,
55
+ "pearson": 0.23378324508666992,
56
+ "mse": 0.08125484734773636,
57
+ "macro_spearman": 0.19768865575292732,
58
+ "macro_assays_eligible": 218,
59
+ "macro_assays_total": 218
60
+ },
61
+ "lr_encoder": 9.93179964821535e-06,
62
+ "lr_head": 4.9658998241076745e-05
63
+ },
64
+ {
65
+ "epoch": 4,
66
+ "train_loss": 0.06594055111535461,
67
+ "val_loss": 0.08097927542403341,
68
+ "val_metrics": {
69
+ "spearman": 0.23531327082857056,
70
+ "pearson": 0.23060324788093567,
71
+ "mse": 0.08090551197528839,
72
+ "macro_spearman": 0.21062218506431446,
73
+ "macro_assays_eligible": 218,
74
+ "macro_assays_total": 218
75
+ },
76
+ "lr_encoder": 9.811328950051998e-06,
77
+ "lr_head": 4.905664475025999e-05
78
+ },
79
+ {
80
+ "epoch": 5,
81
+ "train_loss": 0.06386762665527351,
82
+ "val_loss": 0.08328413100292285,
83
+ "val_metrics": {
84
+ "spearman": 0.21344337077969475,
85
+ "pearson": 0.21080267429351807,
86
+ "mse": 0.08322741836309433,
87
+ "macro_spearman": 0.1906273111501897,
88
+ "macro_assays_eligible": 218,
89
+ "macro_assays_total": 218
90
+ },
91
+ "lr_encoder": 9.632455478806839e-06,
92
+ "lr_head": 4.816227739403419e-05
93
+ },
94
+ {
95
+ "epoch": 6,
96
+ "train_loss": 0.062316317219027036,
97
+ "val_loss": 0.08544459746529659,
98
+ "val_metrics": {
99
+ "spearman": 0.21378404878560708,
100
+ "pearson": 0.21021392941474915,
101
+ "mse": 0.08542674779891968,
102
+ "macro_spearman": 0.1841262975097498,
103
+ "macro_assays_eligible": 218,
104
+ "macro_assays_total": 218
105
+ },
106
+ "lr_encoder": 9.397350516112365e-06,
107
+ "lr_head": 4.698675258056182e-05
108
+ },
109
+ {
110
+ "epoch": 7,
111
+ "train_loss": 0.06089970052883347,
112
+ "val_loss": 0.08304940707360704,
113
+ "val_metrics": {
114
+ "spearman": 0.2142924318507679,
115
+ "pearson": 0.20584237575531006,
116
+ "mse": 0.08305490761995316,
117
+ "macro_spearman": 0.20147768236632793,
118
+ "macro_assays_eligible": 218,
119
+ "macro_assays_total": 218
120
+ },
121
+ "lr_encoder": 9.108867917734062e-06,
122
+ "lr_head": 4.554433958867031e-05
123
+ },
124
+ {
125
+ "epoch": 8,
126
+ "train_loss": 0.05954349656158016,
127
+ "val_loss": 0.0856317674741149,
128
+ "val_metrics": {
129
+ "spearman": 0.20605229263914857,
130
+ "pearson": 0.20229709148406982,
131
+ "mse": 0.0855521708726883,
132
+ "macro_spearman": 0.15801575910924295,
133
+ "macro_assays_eligible": 218,
134
+ "macro_assays_total": 218
135
+ },
136
+ "lr_encoder": 8.770509471627362e-06,
137
+ "lr_head": 4.385254735813681e-05
138
+ },
139
+ {
140
+ "epoch": 9,
141
+ "train_loss": 0.05828532947726342,
142
+ "val_loss": 0.09087766061226527,
143
+ "val_metrics": {
144
+ "spearman": 0.17898921602789444,
145
+ "pearson": 0.17833471298217773,
146
+ "mse": 0.0906767025589943,
147
+ "macro_spearman": 0.14823934961313195,
148
+ "macro_assays_eligible": 218,
149
+ "macro_assays_total": 218
150
+ },
151
+ "lr_encoder": 8.3863823909755e-06,
152
+ "lr_head": 4.1931911954877495e-05
153
+ },
154
+ {
155
+ "epoch": 10,
156
+ "train_loss": 0.0570728823543768,
157
+ "val_loss": 0.08991672083114584,
158
+ "val_metrics": {
159
+ "spearman": 0.18307338675239052,
160
+ "pearson": 0.18055406212806702,
161
+ "mse": 0.08965384215116501,
162
+ "macro_spearman": 0.1408365513344764,
163
+ "macro_assays_eligible": 218,
164
+ "macro_assays_total": 218
165
+ },
166
+ "lr_encoder": 7.961149458185257e-06,
167
+ "lr_head": 3.9805747290926286e-05
168
+ },
169
+ {
170
+ "epoch": 11,
171
+ "train_loss": 0.0557709243132436,
172
+ "val_loss": 0.09227961363891761,
173
+ "val_metrics": {
174
+ "spearman": 0.16599375190389218,
175
+ "pearson": 0.1591390073299408,
176
+ "mse": 0.09232576191425323,
177
+ "macro_spearman": 0.1580101290572493,
178
+ "macro_assays_eligible": 218,
179
+ "macro_assays_total": 218
180
+ },
181
+ "lr_encoder": 7.499972425024911e-06,
182
+ "lr_head": 3.7499862125124546e-05
183
+ },
184
+ {
185
+ "epoch": 12,
186
+ "train_loss": 0.05454512665595613,
187
+ "val_loss": 0.09188498885681232,
188
+ "val_metrics": {
189
+ "spearman": 0.17033978684464768,
190
+ "pearson": 0.1680733561515808,
191
+ "mse": 0.09179382771253586,
192
+ "macro_spearman": 0.14625690534575364,
193
+ "macro_assays_eligible": 218,
194
+ "macro_assays_total": 218
195
+ },
196
+ "lr_encoder": 7.008449355950063e-06,
197
+ "lr_head": 3.504224677975031e-05
198
+ },
199
+ {
200
+ "epoch": 13,
201
+ "train_loss": 0.0532016348550355,
202
+ "val_loss": 0.0945957535567383,
203
+ "val_metrics": {
204
+ "spearman": 0.15632833853849673,
205
+ "pearson": 0.15771374106407166,
206
+ "mse": 0.09457787126302719,
207
+ "macro_spearman": 0.14414718978591468,
208
+ "macro_assays_eligible": 218,
209
+ "macro_assays_total": 218
210
+ },
211
+ "lr_encoder": 6.492546675184488e-06,
212
+ "lr_head": 3.246273337592243e-05
213
+ },
214
+ {
215
+ "epoch": 14,
216
+ "train_loss": 0.05184374296895343,
217
+ "val_loss": 0.09580712212870518,
218
+ "val_metrics": {
219
+ "spearman": 0.1508608569678843,
220
+ "pearson": 0.14708638191223145,
221
+ "mse": 0.09580221027135849,
222
+ "macro_spearman": 0.13152231787081284,
223
+ "macro_assays_eligible": 218,
224
+ "macro_assays_total": 218
225
+ },
226
+ "lr_encoder": 5.958526742412386e-06,
227
+ "lr_head": 2.9792633712061927e-05
228
+ }
229
+ ],
230
+ "fingerprint": "feb493f3bf5da120bfada3ab639b04a9e0cd89d0f00633e01c99a89664971d49",
231
+ "input_hashes": {
232
+ "csv": "e43e150423cb998a77037ad6d82c158a65431b59c46b168febf263ad36267df4",
233
+ "roles_tsv": "e82fb6d73d3ce3ff33e32bd0a8d2e81a0aa4b19b8819bf18339c4aa0382dc5fd",
234
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
235
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
236
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
237
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
238
+ "training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
239
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
240
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
241
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
242
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
243
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
244
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
245
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
246
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
247
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
248
+ "_imported_modules": {
249
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "6f2ac53d3af2002a8eb42f3b4dc046659a261cdf6fe5eef158724c3527610949",
250
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
251
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
252
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
253
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
254
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
255
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
256
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
257
+ },
258
+ "_roster_digests": {
259
+ "train": "f6ec937bb55813139ed1b26e07953c95d78fd1fe99ccec1f03bb74c09b7960f5",
260
+ "val": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391",
261
+ "test": "c537ff22ab74b5b6c6ec94e76ef3de2b6ffc1e17086c11657e5f29b0d88da391"
262
+ }
263
+ }
264
+ }
releases/2026-10-02/provenance/affinose_cells/affinose_sa_20260908_mo_B_protein_s42/receipt.json ADDED
@@ -0,0 +1,101 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "cell_id": "mo_B_protein_s42",
3
+ "fingerprint": "b01428c0a518f3d479eae47e01fb9f753829678e902ef19b2318139159d22b05",
4
+ "manifest_sha256": "mo_20260908_moB_protein",
5
+ "args": {
6
+ "csv_path": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_B_protein_binding.csv",
7
+ "split_path": "/work/ratul1/supantha/Bertint/v2_artifacts/affinose_v2_splits_direct_edit_guarded.json",
8
+ "protein_emb_path": "/work/ratul1/supantha/Bertint/affinose_v2_esmc_by_seqhash_r2.h5",
9
+ "vocab_path": "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/data/review_sequences_a5_non_iar_later_role_official_array_v4_20260721_r1/review_bpe_vocabulary.json",
10
+ "bertose_checkpoint": "/work/ratul1/supantha/bertose_a5_static_precache_20260723_r2c4_closure31_py310_3c8d5299a4af15bc/writing/review/multimodal/outputs/a5_wa345_pretraining_20260817_r1/production/review_md20x6_md20x6-a1111-wa345-20260817-r1_A1111_wa3_seed42/review_checkpoints/review_md20x6_checkpoint_best.pt",
11
+ "output_dir": "/work/ratul1/supantha/Bertint/checkpoints/affinose_sa_20260908_mo_B_protein_s42",
12
+ "freeze_layers": 4,
13
+ "shared_dim": 512,
14
+ "num_cross_layers": 2,
15
+ "num_heads": 8,
16
+ "swe_slices": 512,
17
+ "dropout": 0.1,
18
+ "protein_dim": 960,
19
+ "separate_swe": false,
20
+ "lr_encoder": 1e-05,
21
+ "lr_head": 5e-05,
22
+ "weight_decay": 0.01,
23
+ "max_grad_norm": 1.0,
24
+ "batch_size": 32,
25
+ "epochs": 30,
26
+ "patience": 10,
27
+ "max_glycan_length": 256,
28
+ "max_protein_length": 1024,
29
+ "protein_mode": "real",
30
+ "ranking_loss": false,
31
+ "assays_per_batch": 4,
32
+ "rows_per_assay": 8,
33
+ "protein_binding_channel": false,
34
+ "glycan_only": false,
35
+ "protein_attentive_pool": false,
36
+ "motif_additive_dim": 165,
37
+ "glycan_size_channel": false,
38
+ "seed": 42,
39
+ "roles_tsv": "/work/ratul1/supantha/Bertint/v2_artifacts/review_affinose_B_protein_roles.tsv.gz",
40
+ "train_stratum": "fit_train",
41
+ "val_stratum": "val_protein_cold",
42
+ "test_stratum": "val_protein_cold",
43
+ "features_dir": "/work/ratul1/supantha/Bertint/data/review_affinose_glycan_features_20260824_r1",
44
+ "expected_train_rows": 480431,
45
+ "expected_val_rows": 39477,
46
+ "expected_test_rows": 39477,
47
+ "manifest_sha256": "mo_20260908_moB_protein",
48
+ "cell_id": "mo_B_protein_s42",
49
+ "input_hashes": "/work/ratul1/supantha/Bertint/manifests/affinose_input_hashes_moB_protein_20260908.json",
50
+ "warmup_pct": 0.05,
51
+ "target_col": "target_rank",
52
+ "checkpoint_interval": 1,
53
+ "resume": false,
54
+ "pooling_mode": "mean",
55
+ "interaction_mode": "concat",
56
+ "no_cross_attention": false
57
+ },
58
+ "input_hashes": {
59
+ "csv": "db08ac7409ae5f119a706f8acb7c486154f10f70f59571fa7306178032ec6990",
60
+ "roles_tsv": "6ae90f6fed8e1874479b007d1537531b1a9f9f4db5a89855a9d48947ee0a88bc",
61
+ "protein_emb_h5": "529ae25ecac7c03d0fc33f8f52f6cacec2db5103f27088d25e9adc06133fdef4",
62
+ "vocabulary": "918c73a55adeb603614112d75bb02033e5823fd9c8a70d43f6c0a4a9c78e360b",
63
+ "upstream_checkpoint": "8e7d95ee492df6d843f495dd4708c97ee16a947e75b5cd8f83b23e4f4a0d6d82",
64
+ "dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
65
+ "training_v8.py": "b7cb8a39e167f2cc9e35476dcdcad56320b02c1898d13db3236a41bbb2ed4852",
66
+ "bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
67
+ "a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98",
68
+ "feature_token_ids": "9dbdf73149ce8a3a1d4e1a5322f8a3de68e0f2e1a2e6a73733641e02a9b32ccd",
69
+ "feature_branch_depths": "afa8b69797891b529b212da41726c1d1269e06f26396370e2fcc0b77f25a5086",
70
+ "feature_linkage_types": "3440fc0db96dc3ecdfa315e390fe354c398ded75db8b9017569db7bf6c61610f",
71
+ "feature_attention_mask": "43cdb6ccd9c5e5ecc46aed5ed0c29801144526486e89ba3f611537cea13e8282",
72
+ "feature_tree_channels_valid": "0c867a6e6fdf3a0b72d5fdb76023556d998ad286ef14f0f8f661d760b8f18a2c",
73
+ "feature_motif_features": "d88c2d1342d27f572a27ec030579341a59d72bad89d552695d4db419ff3ceae2",
74
+ "feature_index": "986c4282f79ba621afdd4ad8cfe58d29d79d819946e75d0eb2d9c2316d637a3d",
75
+ "_imported_modules": {
76
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/training_v8.py": "b7cb8a39e167f2cc9e35476dcdcad56320b02c1898d13db3236a41bbb2ed4852",
77
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/bertint_v8.py": "4df5fcf330bbb4d7d766a141179a5fea76a4e75fc9eef0e403cb567d97dd4564",
78
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/dataset_v8.py": "0cafa57d2b468e1c5ac9d0e9285565de05500fc78ebac467cd09aeceb0e1b6c4",
79
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/__init__.py": "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855",
80
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/glycan_bert.py": "b69f14c9976951325e3a0a4e8107a16126e67d410e966650f513f1f538a732bb",
81
+ "/work/ratul1/supantha/glycan-SD-VS/bert_training_v3/v3.1_cluster_training/bert_training_v4/model/multimodal_glycan_bert_v3.py": "83fde1c18cd6547a6ea026017296b5c94d209ac8f0e125057f03d507b3b65190",
82
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a5_tokenizer_shim.py": "771759e04c6c8aabdb928c7a2396fc07af3aefb917b857e07c5c2a0dd77ae96e",
83
+ "/work/ratul1/supantha/Bertint/model_motif_20260908/a1111_encoder_adapter.py": "52c790c24e10061b5890260aad39cefdb6c2c615b8b9c167cc23d86393e41d98"
84
+ },
85
+ "_roster_digests": {
86
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
87
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
88
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
89
+ }
90
+ },
91
+ "roster_digests": {
92
+ "train": "fb872366d7c824122b62d484a9a90f41ac7b7998b4213bfbdc03702086090c27",
93
+ "val": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713",
94
+ "test": "9f258e1549c5fc1d1f21a2204f0dbcc46b0f3af489f8845309bcc0d6a3e78713"
95
+ },
96
+ "slurm_job_id": "12246887",
97
+ "slurm_array_task_id": "1",
98
+ "slurm_restart_count": null,
99
+ "node": "nova22-amp-4",
100
+ "gpu": "NVIDIA A100-PCIE-40GB"
101
+ }