maltefranke commited on
Commit
a52834f
·
verified ·
1 Parent(s): 1117e49

Release GEOM scaffold substituent LoRA rank-16 checkpoint after 25 epochs

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. geom/scaffold_murcko_substituent_lora_r16/README.md +40 -0
  2. geom/scaffold_murcko_substituent_lora_r16/atom_tokens.txt +10 -0
  3. geom/scaffold_murcko_substituent_lora_r16/charge_tokens.txt +5 -0
  4. geom/scaffold_murcko_substituent_lora_r16/config.yaml +461 -0
  5. geom/scaffold_murcko_substituent_lora_r16/distributions.json +0 -0
  6. geom/scaffold_murcko_substituent_lora_r16/distributions.pt +3 -0
  7. geom/scaffold_murcko_substituent_lora_r16/edge_tokens.txt +4 -0
  8. geom/scaffold_murcko_substituent_lora_r16/epoch_025.ckpt +3 -0
  9. geom/scaffold_murcko_substituent_lora_r16/evaluate.py +221 -0
  10. geom/scaffold_murcko_substituent_lora_r16/manifest.json +750 -0
  11. geom/scaffold_murcko_substituent_lora_r16/metadata.json +41 -0
  12. geom/scaffold_murcko_substituent_lora_r16/panels/test_murcko_512_seed42.json +0 -0
  13. geom/scaffold_murcko_substituent_lora_r16/panels/val_murcko_512_seed42.json +0 -0
  14. geom/scaffold_murcko_substituent_lora_r16/panels/verification.json +10 -0
  15. geom/scaffold_murcko_substituent_lora_r16/runtime/README.md +202 -0
  16. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/callbacks/callbacks.yaml +20 -0
  17. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/chembl3d.yaml +8 -0
  18. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/implicit_h.yaml +5 -0
  19. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/max100.yaml +7 -0
  20. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/max100_implicit_h.yaml +5 -0
  21. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/crossdocked/finetune.yaml +67 -0
  22. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/crossdocked/scaffold_decoration.yaml +31 -0
  23. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/geom/geom.yaml +11 -0
  24. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/geom/implicit_h.yaml +5 -0
  25. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/geom/scaffold_decoration.yaml +38 -0
  26. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/molecular.yaml +136 -0
  27. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/qm9/qm9.yaml +106 -0
  28. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/qm9/scaffold_decoration.yaml +32 -0
  29. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/default.yaml +25 -0
  30. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/chembl3d.yaml +5 -0
  31. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/implicit_h.yaml +5 -0
  32. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/implicit_h_2x4090.yaml +10 -0
  33. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/train_2x4090.yaml +40 -0
  34. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/core.yaml +13 -0
  35. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/crossdocked/finetune.yaml +16 -0
  36. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/crossdocked/scaffold_decoration.yaml +9 -0
  37. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/crossdocked/scaffold_decoration_2x4090.yaml +51 -0
  38. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/geom.yaml +20 -0
  39. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/implicit_h.yaml +5 -0
  40. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/scaffold_decoration.yaml +39 -0
  41. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/scaffold_decoration_2x4090.yaml +27 -0
  42. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/scaffold_decoration_scratch_2x4090.yaml +44 -0
  43. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/qm9/qm9.yaml +20 -0
  44. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/qm9/scaffold_decoration.yaml +27 -0
  45. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/qm9/train_2x4090.yaml +29 -0
  46. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/embeddings/geom.yaml +28 -0
  47. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/embeddings/qm9.yaml +29 -0
  48. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/heads/head.yaml +75 -0
  49. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/insertion_head/gmm.yaml +16 -0
  50. geom/scaffold_murcko_substituent_lora_r16/runtime/configs/insertion_head/unimodal.yaml +13 -0
geom/scaffold_murcko_substituent_lora_r16/README.md ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GEOM scaffold decoration: 25 completed fine-tuning epochs
2
+
3
+ This is the original checkpoint from the latest GEOM Murcko scaffold fine-tuning run,
4
+ with rank-16 LoRA, trainable output heads, and substituent-constrained insertion
5
+ supervision. It starts from GEOM explicit-H epoch 100 EMA weights. The stored
6
+ Lightning epoch is 24; 25 fine-tuning epochs have completed (199,100 steps).
7
+
8
+ This conditional checkpoint requires a scaffold. Use its saved architecture and
9
+ configuration; the unconditional `morph generate` interface/catalog does not load it.
10
+ Both current and EMA networks and optimizer state are retained. EMA is used for evaluation.
11
+ Scaffold atom identities, internal bonds and charges are preserved; positions can move.
12
+ Substituent assignment affects training supervision and does not require hidden target
13
+ bonds at generation time.
14
+
15
+ ## Files and reproduction
16
+
17
+ - `epoch_025.ckpt`: unmodified original checkpoint; `metadata.json` records its SHA-256.
18
+ - `training_config.yaml`: original composed training configuration, including provenance paths.
19
+ - `config.yaml`: equivalent configuration with portable asset/data paths and base loading disabled.
20
+ - Ordered atom/edge/charge vocabularies and `distributions.pt`: exact model assets.
21
+ - `panels/`: fixed 512-scaffold validation and test selections, with eight attempts per scaffold.
22
+ - `training_source.tar.gz`: original frozen training source, training manifest, and insertion-assignment audit.
23
+ - `runtime/`: evaluation source snapshot, including corrected raw-graph RDKit stability.
24
+ - `evaluate.py`: evaluation/preflight/aggregation driver; `manifest.json` hashes every release file.
25
+
26
+ In an existing compatible Morph environment, set `PYTHONPATH` to `runtime/src:runtime`
27
+ and run `evaluate.py preflight --bundle <this-folder> --data-root <GEOM-root>
28
+ --output <results-folder>`. The GEOM root must contain the matching processed data and
29
+ scaffold caches. The script sets portable asset environment variables itself.
30
+
31
+ The cluster evaluation uses the held-out **test panel, 512 × 8 attempts × seeds 42/43/44**,
32
+ 100 integration steps, EMA weights, and no inference scaffold jitter. It reports native
33
+ scaffold metrics, strict and scaffold-aware PoseBusters, raw-graph stability, chemistry,
34
+ and distribution/geometry diagnostics. Native PoseBusters omits the separate full offline
35
+ energy/flatness checks. All scalar metrics are aggregated into one `summary.csv`.
36
+
37
+ The evaluation driver requires a completed upload-verification JSON before generation;
38
+ this is a provenance guard for this release. Generated coordinates are not relaxed by this
39
+ scaffold evaluation. The checkpoint/configuration hashes and original training archive
40
+ allow the training and evaluation implementations to be distinguished.
geom/scaffold_murcko_substituent_lora_r16/atom_tokens.txt ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ Br
2
+ C
3
+ Cl
4
+ F
5
+ H
6
+ I
7
+ N
8
+ O
9
+ P
10
+ S
geom/scaffold_murcko_substituent_lora_r16/charge_tokens.txt ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ -2
2
+ -1
3
+ 0
4
+ 1
5
+ 2
geom/scaffold_murcko_substituent_lora_r16/config.yaml ADDED
@@ -0,0 +1,461 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ representation: molecule
2
+ seed: 42
3
+ heads:
4
+ heads:
5
+ _target_: morph.model.layers.heads.MultiHeadModule
6
+ output_init_std: ${model.head_initialization.output_std}
7
+ independent_adapters: ${oc.select:model.head_independent_adapters,false}
8
+ heads_configs:
9
+ graph_aggregation: mean
10
+ node_heads:
11
+ atom_type_head:
12
+ input_dim: ${model.head_node_dim}
13
+ output_dim: ${len:${data.vocab.atom_tokens}}
14
+ adapter_dim: ${model.head_node_adapter_dim}
15
+ group: state
16
+ charge_head:
17
+ input_dim: ${model.head_node_dim}
18
+ output_dim: ${len:${data.vocab.charge_tokens}}
19
+ adapter_dim: ${model.head_node_adapter_dim}
20
+ group: state
21
+ ins_rate_head:
22
+ input_dim: ${model.head_node_dim}
23
+ output_dim: 1
24
+ adapter_dim: ${model.head_node_adapter_dim}
25
+ group: edit
26
+ initial_rate: ${model.head_initialization.ins_rate}
27
+ do_del_head:
28
+ input_dim: ${model.head_node_dim}
29
+ output_dim: 1
30
+ adapter_dim: ${model.head_node_adapter_dim}
31
+ group: edit
32
+ initial_probability: ${model.head_initialization.do_del_probability}
33
+ do_sub_a_head:
34
+ input_dim: ${model.head_node_dim}
35
+ output_dim: 1
36
+ adapter_dim: ${model.head_node_adapter_dim}
37
+ group: edit
38
+ initial_probability: ${model.head_initialization.do_sub_a_probability}
39
+ edge_heads:
40
+ edge_type_head:
41
+ input_dim: ${model.head_edge_dim}
42
+ output_dim: ${len:${data.vocab.edge_tokens}}
43
+ adapter_dim: ${model.head_edge_adapter_dim}
44
+ group: state
45
+ do_sub_e_head:
46
+ input_dim: ${model.head_edge_dim}
47
+ output_dim: 1
48
+ adapter_dim: ${model.head_edge_adapter_dim}
49
+ group: state
50
+ initial_probability: ${model.head_initialization.do_sub_e_probability}
51
+ ins_edge_head:
52
+ _target_: morph.model.layers.heads.InsertionEdgeHead
53
+ _recursive_: false
54
+ hidden_dim: ${model.head_node_dim}
55
+ readout_dim: ${model.head_insertion_edge_dim}
56
+ output_init_std: ${model.head_initialization.output_std}
57
+ n_edge_types: ${len:${data.vocab.edge_tokens}}
58
+ n_atom_types: ${len:${data.vocab.atom_tokens}}
59
+ n_charge_types: ${len:${data.vocab.charge_tokens}}
60
+ rbf_embedding_args:
61
+ _target_: morph.model.layers.embedding.RBFEmbedding
62
+ num_rbf: 128
63
+ rbf_dmax: 3.5
64
+ out_dim: 64
65
+ dropout: 0.0
66
+ trainable: false
67
+ insertion_head:
68
+ mode: unimodal
69
+ dimension: 3
70
+ num_components: 1
71
+ model:
72
+ _target_: morph.model.layers.heads.UnimodalInsertionHead
73
+ hidden_dim: ${model.head_node_dim}
74
+ readout_dim: ${model.head_insertion_adapter_dim}
75
+ output_init_std: ${model.head_initialization.output_std}
76
+ independent_adapters: ${oc.select:model.head_independent_adapters,false}
77
+ N_a: ${len:${data.vocab.atom_tokens}}
78
+ N_c: ${len:${data.vocab.charge_tokens}}
79
+ model:
80
+ module:
81
+ optimizer_config:
82
+ implementation: torch
83
+ optimizer:
84
+ execution: ${oc.env:MORPH_MUON_EXECUTION,native}
85
+ lr: 0.001
86
+ momentum: 0.95
87
+ weight_decay: 0.0
88
+ nesterov: true
89
+ ns_steps: 5
90
+ eps: 1.0e-07
91
+ adjust_lr_fn: original
92
+ aux_optimizer:
93
+ lr: 0.0001
94
+ betas:
95
+ - 0.9
96
+ - 0.95
97
+ eps: 1.0e-10
98
+ weight_decay: 0.0
99
+ gradient_clip_val: 1.0
100
+ gradient_clip_algorithm: norm
101
+ scheduler:
102
+ _target_: morph.utils.lr_schedulers.MultiGroupCosineWarmupLR
103
+ warmup_steps: 2000
104
+ total_steps: 200000
105
+ min_lr_fraction: 0.05
106
+ last_epoch: -1
107
+ interval: step
108
+ monitor: null
109
+ loss_weights:
110
+ do_sub_a: 0.2
111
+ sub_a_class: 0.03
112
+ do_sub_e: 0.25
113
+ sub_e_class: 0.5
114
+ do_del: 0.4
115
+ ins_rate: 0.15
116
+ ins_state: 0.01
117
+ ins_e: 0.4
118
+ x: 2.0
119
+ c: 0.5
120
+ ins_e_ii: 0.03
121
+ _target_: morph.model.finetune.scaffold.LightningModuleRatesScaffoldFinetune
122
+ insertion_head_mode: ${insertion_head.mode}
123
+ ins_noise_scale: ${data.ins_noise_scale}
124
+ ema_decay: 0.999
125
+ ema_warmup_steps: 5000
126
+ use_ema_for_eval: true
127
+ time_dist: ${if:${eq:${model.sample_time_strategy}, log}, ${model.beta_sched},
128
+ ${model.linear_sched}}
129
+ integrator: ${model.integrator}
130
+ vocab: ${data.vocab}
131
+ use_learnable_loss_weights: false
132
+ use_time_weights: false
133
+ model:
134
+ _target_: morph.model.finetune.scaffold.ScaffoldConditionalDiTBackboneWithHeads
135
+ _recursive_: false
136
+ atom_type_embedding_args: ${embeddings.atom_type_embedding}
137
+ edge_type_embedding_args: ${embeddings.edge_type_embedding}
138
+ time_embedding_args: ${embeddings.time_embedding}
139
+ node_count_embedding_args: ${embeddings.node_count_embedding}
140
+ incident_bond_embedding: false
141
+ backbone_model_args: ${model.backbone}
142
+ heads_args: ${heads.heads}
143
+ insertion_head_args: ${insertion_head.model}
144
+ ins_edge_head_args: ${heads.ins_edge_head}
145
+ xattn_encoder_args: ${model.xattn_encoder}
146
+ use_distance_bias: true
147
+ pretrained_ckpt: null
148
+ pretrained_prefer_ema: true
149
+ freeze_pretrained_backbone: true
150
+ freeze_output_heads: false
151
+ exclude_scaffold_from_sub_loss: true
152
+ exclude_scaffold_from_x_loss: false
153
+ freeze_keep_positions: false
154
+ freeze_keep_charges: true
155
+ rotate: true
156
+ n_augmentations: 1
157
+ integrator: ${integrator.integrator}
158
+ beta_sched:
159
+ _target_: torch.distributions.Beta
160
+ concentration1: 2.0
161
+ concentration0: 1.0
162
+ linear_sched:
163
+ _target_: torch.distributions.Uniform
164
+ low: 0.0
165
+ high: 1.0
166
+ sample_time_strategy: linear
167
+ head_node_dim: ${model.backbone.out_node_nf}
168
+ head_edge_dim: ${model.backbone.out_edge_nf}
169
+ head_node_adapter_dim: 256
170
+ head_edge_adapter_dim: 256
171
+ head_insertion_adapter_dim: 256
172
+ head_insertion_edge_dim: 256
173
+ head_independent_adapters: true
174
+ head_initialization:
175
+ output_std: 0.001
176
+ coordinate_output_std: 0.0
177
+ do_del_probability: 0.5
178
+ do_sub_a_probability: 0.5
179
+ do_sub_e_probability: 0.5
180
+ ins_rate: 0.6931471805599453
181
+ token_weighting:
182
+ type_loss_token_weights: uniform
183
+ weight_alpha: 0.4
184
+ lora:
185
+ enabled: true
186
+ rank: 16
187
+ alpha: 16.0
188
+ dropout: 0.0
189
+ targets:
190
+ - qkv
191
+ - proj
192
+ - mlp.gate_value
193
+ - mlp.out_proj
194
+ freeze_base: true
195
+ backbone:
196
+ _target_: morph.model.backbones.conditional_dit.ConditionalDiTBackbone
197
+ _recursive_: false
198
+ d_input: ${embeddings.atom_type_embedding.out_dim}
199
+ d_cond: ${oc.eval:"${embeddings.time_embedding.out_dim} + ${embeddings.node_count_embedding.out_dim}"}
200
+ d_model: 512
201
+ out_node_nf: 256
202
+ out_edge_nf: 256
203
+ num_layers: 8
204
+ nhead: 16
205
+ mlp_ratio: 4.0
206
+ ffn_hidden_dim: null
207
+ ffn_multiple_of: 64
208
+ qk_norm: true
209
+ coordinate_output_init_std: ${model.head_initialization.coordinate_output_std}
210
+ center_coordinate_output: true
211
+ in_edge_nf: ${embeddings.edge_type_embedding.out_dim}
212
+ edge_pair_dim: 256
213
+ pair_bias_hidden_dim: 32
214
+ num_atom_types: ${len:${data.vocab.atom_tokens}}
215
+ lora: ${model.lora}
216
+ rbf_embedding_args:
217
+ _target_: morph.model.layers.embedding.RBFEmbedding
218
+ num_rbf: 128
219
+ rbf_dmax: 3.5
220
+ out_dim: 64
221
+ dropout: 0.0
222
+ trainable: false
223
+ ctx_dim: 128
224
+ use_cross_attn: true
225
+ use_node_cond: true
226
+ use_edge_cond: true
227
+ xattn_encoder:
228
+ _target_: morph.model.finetune.base.LightweightContextEncoder
229
+ num_atom_types: ${len:${data.vocab.atom_tokens}}
230
+ num_charges: ${len:${data.vocab.charge_tokens}}
231
+ atom_embed_dim: 64
232
+ charge_embed_dim: 16
233
+ pos_embed_dim: 32
234
+ ctx_dim: ${model.backbone.ctx_dim}
235
+ num_attn_layers: 4
236
+ nhead: 8
237
+ mlp_ratio: 4.0
238
+ dropout: 0.0
239
+ use_pair_bias: true
240
+ num_edge_types: ${len:${data.vocab.edge_tokens}}
241
+ edge_embed_dim: 32
242
+ num_rbf: 32
243
+ rbf_dmax: 5.0
244
+ pair_bias_hidden_dim: 32
245
+ schedule:
246
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
247
+ shift: 0.8
248
+ trainer:
249
+ trainer:
250
+ deterministic: warn
251
+ devices: 2
252
+ accelerator: gpu
253
+ strategy: ddp_find_unused_parameters_false
254
+ precision: bf16-mixed
255
+ max_epochs: 50
256
+ accumulate_grad_batches: 1
257
+ num_sanity_val_steps: 0
258
+ gradient_clip_val: null
259
+ gradient_clip_algorithm: norm
260
+ check_val_every_n_epoch: 1
261
+ num_val_samples: 4096
262
+ limit_val_batches: null
263
+ limit_predict_batches: 4
264
+ enable_progress_bar: false
265
+ max_time:
266
+ hours: 0
267
+ seconds: 169927
268
+ do_train: true
269
+ do_validate: false
270
+ do_predict: false
271
+ checkpoint_path: null
272
+ final_checkpoint_path: final.ckpt
273
+ callbacks: {}
274
+ logging:
275
+ enabled: false
276
+ project: morph-geom-scaffold-decoration
277
+ name: ${oc.env:MORPH_RUN_ID,base}
278
+ save_dir: .
279
+ offline: false
280
+ group: geom-e100-murcko-original-50ep-20261010
281
+ tags:
282
+ - substituent-insertion
283
+ - rank-16
284
+ - scaffold-decoration
285
+ - bemis-murcko
286
+ - geom-e100
287
+ - lora
288
+ - trainable-heads
289
+ - movable-scaffold
290
+ - jitter-0.1A
291
+ - scaffold-metrics
292
+ integrator:
293
+ integrator:
294
+ _target_: morph.flow_matching.integration.RateIntegrator
295
+ vocab: ${data.vocab}
296
+ distributions: None
297
+ insertion_head_mode: ${insertion_head.mode}
298
+ insertion_dimension: ${insertion_head.dimension}
299
+ insertion_num_components: ${insertion_head.num_components}
300
+ time_strategy: log
301
+ time_beta_alpha: 2.0
302
+ time_beta_beta: 1.0
303
+ num_integration_steps: 100
304
+ ins_noise_scale: ${data.ins_noise_scale}
305
+ renoise_insertions: true
306
+ exact_schedule_integration: true
307
+ del_schedule: ${data.interpolator.del_schedule}
308
+ ins_schedule: ${data.interpolator.ins_schedule}
309
+ sub_schedule: ${data.interpolator.sub_schedule}
310
+ sub_e_schedule: ${data.interpolator.sub_e_schedule}
311
+ data:
312
+ root_path: ${oc.env:PROJECT_ROOT}/data
313
+ preprocess_workers: 10
314
+ preprocess_chunk_size: 128
315
+ statistics_workers: ${data.preprocess_workers}
316
+ hydrogen_mode: explicit
317
+ cache_name: ${data.dataset_name}${if:${eq:${data.hydrogen_mode},implicit},_implicit_h,''}
318
+ source_dir: ${oc.env:GEOM_SOURCE_DIR,null}
319
+ max_atoms: 72
320
+ allowed_atom_symbols:
321
+ - H
322
+ - C
323
+ - 'N'
324
+ - O
325
+ - S
326
+ - F
327
+ - Cl
328
+ - Br
329
+ - P
330
+ - I
331
+ n_atoms_strategy: uniform
332
+ uniform_n_atoms_padding: 5
333
+ optimal_transport: equivariant
334
+ ins_noise_scale: 0.65
335
+ move_noise_scale: 0.5
336
+ allow_charged: true
337
+ preprocessing:
338
+ _target_: morph.dataset.core.preprocessing.Preprocessing
339
+ _recursive_: false
340
+ root: ${oc.env:MORPH_GEOM_ROOT}
341
+ num_workers: ${data.statistics_workers}
342
+ train_dataset: ${data.datamodule.datasets.train}
343
+ atom_tokens_path: ${oc.env:MORPH_SCAFFOLD_BUNDLE}/atom_tokens.txt
344
+ edge_tokens_path: ${oc.env:MORPH_SCAFFOLD_BUNDLE}/edge_tokens.txt
345
+ charge_tokens_path: ${oc.env:MORPH_SCAFFOLD_BUNDLE}/charge_tokens.txt
346
+ distributions_path: ${oc.env:MORPH_SCAFFOLD_BUNDLE}/distributions.pt
347
+ vocab: null
348
+ interpolator:
349
+ _target_: morph.flow_matching.interpolation.Interpolator
350
+ distributions: null
351
+ optimal_transport: ${data.optimal_transport}
352
+ ins_noise_scale: ${data.ins_noise_scale}
353
+ move_noise_scale: ${data.move_noise_scale}
354
+ atom_type_noise_prob: 0.0
355
+ pre_align_iters: 2
356
+ c_move: 1.0
357
+ c_sub: 0.0
358
+ c_ins: 6.125
359
+ c_del: 6.125
360
+ del_schedule: ${schedule}
361
+ ins_schedule: ${schedule}
362
+ sub_schedule:
363
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
364
+ shift: 1.5
365
+ sub_e_schedule:
366
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
367
+ shift: 1.5
368
+ jitter_scale: ${data.jitter_scale}
369
+ insertion_assignment: ${data.insertion_assignment}
370
+ datamodule:
371
+ _target_: morph.dataset.conditional.scaffold.datamodule.ScaffoldLightningDataModule
372
+ vocab: ${data.vocab}
373
+ distributions: null
374
+ representation: ${representation}
375
+ datasets:
376
+ train:
377
+ _target_: ${data.dataset_target}
378
+ root: ${data.preprocessing.root}
379
+ preprocess_workers: ${data.preprocess_workers}
380
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
381
+ hydrogen_mode: ${data.hydrogen_mode}
382
+ source_dir: ${data.source_dir}
383
+ max_atoms: ${data.max_atoms}
384
+ allowed_atom_symbols: ${data.allowed_atom_symbols}
385
+ vocab: ${data.vocab}
386
+ distributions: null
387
+ val:
388
+ - _target_: ${data.dataset_target}
389
+ root: ${data.preprocessing.root}
390
+ preprocess_workers: ${data.preprocess_workers}
391
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
392
+ hydrogen_mode: ${data.hydrogen_mode}
393
+ source_dir: ${data.source_dir}
394
+ max_atoms: ${data.max_atoms}
395
+ allowed_atom_symbols: ${data.allowed_atom_symbols}
396
+ vocab: ${data.vocab}
397
+ distributions: null
398
+ test:
399
+ - _target_: ${data.dataset_target}
400
+ root: ${data.preprocessing.root}
401
+ preprocess_workers: ${data.preprocess_workers}
402
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
403
+ hydrogen_mode: ${data.hydrogen_mode}
404
+ source_dir: ${data.source_dir}
405
+ max_atoms: ${data.max_atoms}
406
+ allowed_atom_symbols: ${data.allowed_atom_symbols}
407
+ vocab: ${data.vocab}
408
+ distributions: null
409
+ interpolator: ${data.interpolator}
410
+ rotate: ${model.rotate}
411
+ n_augmentations: ${model.n_augmentations}
412
+ num_workers:
413
+ train: 20
414
+ val: 0
415
+ test: 0
416
+ batch_size:
417
+ train: 320
418
+ val: 32
419
+ test: 32
420
+ n_atoms_strategy: ${data.n_atoms_strategy}
421
+ uniform_n_atoms_padding: ${data.uniform_n_atoms_padding}
422
+ time_dist: ${model.module.time_dist}
423
+ scaffold_groups_dir: ${data.scaffold_groups_dir}
424
+ scaffold_kind: ${data.scaffold_kind}
425
+ enumerate_slicings: ${data.enumerate_slicings}
426
+ eval_panel: ${data.eval_panel}
427
+ train_prefetch_factor: 1
428
+ dataset_name: geom
429
+ dataset_target: morph.dataset.unconditional.geom.FlowMatchingGEOMDataset
430
+ scaffold_groups_dir: ${data.preprocessing.root}/processed/scaffold_groups
431
+ scaffold_kind: murcko
432
+ jitter_scale: 0.1
433
+ insertion_assignment: substituent
434
+ enumerate_slicings: false
435
+ eval_panel:
436
+ directory: ${oc.env:MORPH_SCAFFOLD_BUNDLE}/panels
437
+ n_scaffolds: 512
438
+ samples_per_scaffold: 8
439
+ seed: 42
440
+ embeddings:
441
+ atom_type_embedding:
442
+ _target_: morph.model.layers.embedding.Embedding
443
+ num_embeddings: ${len:${data.vocab.atom_tokens}}
444
+ embedding_dim: 64
445
+ out_dim: 128
446
+ edge_type_embedding:
447
+ _target_: morph.model.layers.embedding.Embedding
448
+ num_embeddings: ${len:${data.vocab.edge_tokens}}
449
+ embedding_dim: 64
450
+ out_dim: 128
451
+ time_embedding:
452
+ _target_: morph.model.layers.embedding.TimeEmbedding
453
+ embedding_dim: 128
454
+ out_dim: 128
455
+ node_count_embedding:
456
+ _target_: morph.model.layers.embedding.ExtrapolatableCountEmbedding
457
+ embedding_dim: 64
458
+ out_dim: 128
459
+ min_period: 128.0
460
+ max_period: 1024.0
461
+ max_count: 96.0
geom/scaffold_murcko_substituent_lora_r16/distributions.json ADDED
The diff for this file is too large to render. See raw diff
 
geom/scaffold_murcko_substituent_lora_r16/distributions.pt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:40a31704ab58d0349daff35083d43dcf4771ef368fd9c8a8503c94fe844016c5
3
+ size 63581
geom/scaffold_murcko_substituent_lora_r16/edge_tokens.txt ADDED
@@ -0,0 +1,4 @@
 
 
 
 
 
1
+ <NO_BOND>
2
+ 1
3
+ 2
4
+ 3
geom/scaffold_murcko_substituent_lora_r16/epoch_025.ckpt ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:0f3b9d75897c951fdfe437bb10735fb371ea2f5e9ea982471c3c7bd73a160484
3
+ size 638268597
geom/scaffold_murcko_substituent_lora_r16/evaluate.py ADDED
@@ -0,0 +1,221 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ """Evaluate the released scaffold checkpoint on a fixed panel for three seeds."""
2
+ from __future__ import annotations
3
+
4
+ import argparse
5
+ import csv
6
+ import hashlib
7
+ import json
8
+ import math
9
+ import os
10
+ from pathlib import Path
11
+ import statistics
12
+ import time
13
+ import types
14
+
15
+ import pytorch_lightning as pl
16
+ import torch
17
+ from omegaconf import OmegaConf
18
+ from rdkit import Chem, rdBase
19
+
20
+ import morph.config # noqa: F401
21
+ from morph.inference.generate import _build_module, load_checkpoint
22
+
23
+
24
+ def sha(path):
25
+ with Path(path).open("rb") as f:
26
+ return hashlib.file_digest(f, "sha256").hexdigest()
27
+
28
+
29
+ def clean(value):
30
+ if isinstance(value, torch.Tensor):
31
+ return clean(value.item())
32
+ if isinstance(value, float) and not math.isfinite(value):
33
+ return None
34
+ if isinstance(value, dict):
35
+ return {key: clean(item) for key, item in value.items()}
36
+ if isinstance(value, (list, tuple)):
37
+ return [clean(item) for item in value]
38
+ return value
39
+
40
+
41
+ def write_json(path, value):
42
+ path.write_text(json.dumps(clean(value), indent=2, allow_nan=False) + "\n")
43
+
44
+
45
+ def configuration(bundle, data_root, seed, output):
46
+ os.environ["MORPH_SCAFFOLD_BUNDLE"] = str(bundle)
47
+ os.environ["MORPH_GEOM_ROOT"] = str(data_root)
48
+ os.environ.setdefault("PROJECT_ROOT", str(bundle / "runtime"))
49
+ os.environ.setdefault("HYDRA_JOBS", str(output))
50
+ cfg = OmegaConf.load(bundle / "config.yaml")
51
+ OmegaConf.set_struct(cfg, False)
52
+ cfg.seed = seed
53
+ cfg.callbacks = {}
54
+ cfg.logging.enabled = False
55
+ cfg.trainer.do_train = False
56
+ cfg.trainer.do_validate = True
57
+ cfg.trainer.do_predict = False
58
+ cfg.trainer.trainer.devices = 1
59
+ cfg.trainer.trainer.strategy = "auto"
60
+ cfg.trainer.trainer.enable_checkpointing = False
61
+ cfg.trainer.trainer.num_val_samples = 4096
62
+ cfg.trainer.trainer.limit_val_batches = 128
63
+ cfg.trainer.trainer.default_root_dir = str(output)
64
+ cfg.trainer.trainer.pop("max_time", None)
65
+ cfg.data.datamodule.num_workers.train = 0
66
+ cfg.data.datamodule.num_workers.val = 0
67
+ cfg.data.datamodule.num_workers.test = 0
68
+ return cfg
69
+
70
+
71
+ def preflight(args):
72
+ torch.set_num_threads(1)
73
+ bundle = args.bundle.resolve()
74
+ metadata = json.loads((bundle / "metadata.json").read_text())
75
+ checkpoint = bundle / "epoch_025.ckpt"
76
+ assert sha(checkpoint) == metadata["checkpoint_sha256"]
77
+ raw = torch.load(checkpoint, map_location="cpu", weights_only=False, mmap=True)
78
+ assert raw["epoch"] == 24 and raw["global_step"] == 199100
79
+ assert any("lora_" in key for key in raw["state_dict"])
80
+ cfg = configuration(bundle, args.data_root.resolve(), 42, args.output.resolve())
81
+ assert cfg.model.lora.rank == 16 and cfg.data.insertion_assignment == "substituent"
82
+ assert cfg.model.module.pretrained_ckpt is None and cfg.model.module.use_ema_for_eval
83
+ assert cfg.model.module.freeze_keep_charges and not cfg.model.module.freeze_keep_positions
84
+ module, vocab, distributions = _build_module(cfg)
85
+ load_checkpoint(module, checkpoint)
86
+ assert len(vocab.atom_tokens) == 10
87
+ for name, expected in metadata["panel_sha256"].items():
88
+ assert sha(bundle / "panels" / name) == expected
89
+ write_json(args.output / "preflight.json", {
90
+ "status": "passed", "checkpoint_sha256": metadata["checkpoint_sha256"],
91
+ "epoch_zero_based": raw["epoch"], "completed_finetuning_epochs": 25,
92
+ "global_step": raw["global_step"], "strict_state_dict_load": True,
93
+ "ema_available": any(k.startswith("model_ema.") for k in raw["state_dict"]),
94
+ "lora_rank": 16, "insertion_assignment": "substituent",
95
+ "atom_tokens": list(vocab.atom_tokens), "rdkit_version": rdBase.rdkitVersion,
96
+ })
97
+ print("Preflight passed: epoch 25, rank-16 LoRA, strict checkpoint load, verified panels.", flush=True)
98
+
99
+
100
+ class Population(pl.Callback):
101
+ def __init__(self, output):
102
+ self.output = output
103
+
104
+ def on_validation_batch_end(self, trainer, module, outputs, batch, batch_idx, dataloader_idx=0):
105
+ if batch_idx % 16 == 0:
106
+ print(f"Generated {len(module._scaffold_rows)}/4096 attempts", flush=True)
107
+
108
+ def on_validation_epoch_end(self, trainer, module):
109
+ rows = module._scaffold_rows
110
+ assert len(rows) == 4096 and {r["sample_id"] for r in rows} == set(range(4096))
111
+ assert len({r["scaffold_id"] for r in rows}) == 512
112
+ assert len(module._validation_rdkit_mols) == len(rows)
113
+ with (self.output / "attempts.jsonl").open("w") as f, Chem.SDWriter(str(self.output / "generated.sdf")) as writer:
114
+ for row, mol in zip(rows, module._validation_rdkit_mols, strict=True):
115
+ entry = dict(row)
116
+ entry["sdf_written"] = False
117
+ if mol is not None:
118
+ mol.SetProp("_Name", f"sample_{row['sample_id']:05d}")
119
+ try:
120
+ writer.write(mol)
121
+ entry["sdf_written"] = True
122
+ except Exception as exc:
123
+ entry["sdf_error"] = str(exc)
124
+ f.write(json.dumps(clean(entry), allow_nan=False) + "\n")
125
+ print("Saved all attempts and original generated SDFs; computing scaffold and chemistry scores.", flush=True)
126
+
127
+
128
+ def evaluate(args):
129
+ from run import setup
130
+
131
+ start = time.monotonic()
132
+ bundle = args.bundle.resolve()
133
+ work = args.output.resolve()
134
+ upload = json.loads((work / "upload_verification.json").read_text())
135
+ assert upload["status"] == "passed", "Upload must be verified before evaluation"
136
+ output = work / f"seed_{args.seed}"
137
+ output.mkdir(parents=True, exist_ok=False)
138
+ (output / "chunks").mkdir()
139
+ metadata = json.loads((bundle / "metadata.json").read_text())
140
+ assert sha(bundle / "epoch_025.ckpt") == metadata["checkpoint_sha256"]
141
+ cfg = configuration(bundle, args.data_root.resolve(), args.seed, output)
142
+ module, datamodule, trainer = setup(cfg, extra_callbacks=[Population(output)])
143
+ datamodule.setup("test")
144
+ panel = datamodule.test_datasets[0]
145
+ assert len(panel) == 4096 and len(panel.entries) == 512 and panel.samples_per_scaffold == 8
146
+ assert Path(panel.path).name == "test_murcko_512_seed42.json"
147
+ assert sha(panel.path) == metadata["panel_sha256"]["test_murcko_512_seed42.json"]
148
+ assert module.use_ema_for_eval
149
+ # Save every raw graph before sanitization failures can remove it from SDFs.
150
+ original_update = module._update_task_metrics
151
+ chunks = []
152
+
153
+ def capture(self, context, mols, rdkit_mols):
154
+ path = output / "chunks" / f"batch_{len(chunks):04d}.pt"
155
+ torch.save(mols, path)
156
+ chunks.append(path.name)
157
+ return original_update(context, mols, rdkit_mols)
158
+
159
+ module._update_task_metrics = types.MethodType(capture, module)
160
+ OmegaConf.save(cfg, output / "config.yaml", resolve=True)
161
+ values = trainer.validate(module, dataloaders=datamodule.test_dataloader(), ckpt_path=str(bundle / "epoch_025.ckpt"))
162
+ assert len(values) == 1
163
+ metrics = {k.removeprefix("val/"): v for k, v in values[0].items()}
164
+ required = {"validity", "novelty", "rdkit-atom-stability", "rdkit-molecule-stability",
165
+ "posebusters/validity", "scaffold/posebusters/validity", "scaffold/retention",
166
+ "scaffold/recovery", "scaffold/unique_yield", "scaffold/rmsd_aligned"}
167
+ assert required <= metrics.keys(), sorted(required - metrics.keys())
168
+ for key, value in {"n_scaffolds": 512, "n_samples": 4096, "min_samples_per_scaffold": 8, "max_samples_per_scaffold": 8}.items():
169
+ assert metrics[f"scaffold/{key}"] == value
170
+ assert len(chunks) == 128
171
+ report = {
172
+ "status": "completed", "split": "test", "seed": args.seed,
173
+ "n_attempts": 4096, "n_scaffolds": 512, "samples_per_scaffold": 8,
174
+ "weights": "EMA", "integration_steps": 100, "checkpoint_sha256": metadata["checkpoint_sha256"],
175
+ "uploaded_revision": upload["revision"], "metrics": metrics,
176
+ "rdkit_stability_population": "all raw generated graphs",
177
+ "posebusters_protocol": "native scaffold evaluation: chemistry and distance geometry; scaffold-aware inherited-clash attribution; excludes full offline energy and flatness",
178
+ "raw_graph_chunks": chunks, "panel_sha256": sha(panel.path),
179
+ "elapsed_seconds": time.monotonic()-start, "job_id": os.environ.get("SLURM_JOB_ID"),
180
+ }
181
+ write_json(output / "metrics.json", report)
182
+ print(json.dumps(clean(report), indent=2), flush=True)
183
+
184
+
185
+ def aggregate(args):
186
+ work = args.output.resolve()
187
+ reports = [json.loads((work / f"seed_{seed}/metrics.json").read_text()) for seed in (42, 43, 44)]
188
+ assert all(r["status"] == "completed" for r in reports)
189
+ keys = set(reports[0]["metrics"])
190
+ assert all(set(r["metrics"]) == keys for r in reports)
191
+ assert len({r["checkpoint_sha256"] for r in reports}) == 1
192
+ assert len({r["panel_sha256"] for r in reports}) == 1
193
+ with (work / "summary.csv").open("w", newline="") as f:
194
+ writer = csv.DictWriter(f, fieldnames=["model", "completed_epochs", "split", "metric", "seed_42", "seed_43", "seed_44", "mean", "std", "n_seeds"])
195
+ writer.writeheader()
196
+ for key in sorted(keys):
197
+ values = [r["metrics"][key] for r in reports]
198
+ finite = [x for x in values if x is not None and math.isfinite(x)]
199
+ writer.writerow({"model": "geom-scaffold-murcko-substituent-lora-r16", "completed_epochs": 25,
200
+ "split": "test", "metric": key, **dict(zip(["seed_42", "seed_43", "seed_44"], values)),
201
+ "mean": statistics.mean(finite) if finite else None,
202
+ "std": statistics.stdev(finite) if len(finite)>1 else None, "n_seeds": len(finite)})
203
+ write_json(work / "verification.json", {"status": "passed", "n_seeds": 3, "n_attempts": 12288, "n_metrics": len(keys),
204
+ "summary_sha256": sha(work / "summary.csv"), "checkpoint_sha256": reports[0]["checkpoint_sha256"]})
205
+ print(f"Verified {len(keys)} metrics across 3 seeds; all metrics in {work / 'summary.csv'}", flush=True)
206
+
207
+
208
+ def main():
209
+ p = argparse.ArgumentParser()
210
+ p.add_argument("mode", choices=["preflight", "evaluate", "aggregate"])
211
+ p.add_argument("--bundle", type=Path, required=True)
212
+ p.add_argument("--data-root", type=Path, required=True)
213
+ p.add_argument("--output", type=Path, required=True)
214
+ p.add_argument("--seed", type=int, choices=[42, 43, 44], default=42)
215
+ args = p.parse_args()
216
+ args.output.mkdir(parents=True, exist_ok=True)
217
+ {"preflight": preflight, "evaluate": evaluate, "aggregate": aggregate}[args.mode](args)
218
+
219
+
220
+ if __name__ == "__main__":
221
+ main()
geom/scaffold_murcko_substituent_lora_r16/manifest.json ADDED
@@ -0,0 +1,750 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "README.md": {
3
+ "bytes": 2752,
4
+ "sha256": "3879125c6ede15b8b268f316c5c2163b1af0fb795582c3e4685acb029f52b74f"
5
+ },
6
+ "atom_tokens.txt": {
7
+ "bytes": 22,
8
+ "sha256": "c9b712563ad2509593aa48fddbb5145aa42a70c4791255b8eea2984f549cae6a"
9
+ },
10
+ "charge_tokens.txt": {
11
+ "bytes": 12,
12
+ "sha256": "e2533aa090751f1006a74d165aa2d4a7ac23dbf73a65eb624c2a81216cc7dff5"
13
+ },
14
+ "config.yaml": {
15
+ "bytes": 14715,
16
+ "sha256": "9db9f6c09bc55fb7b39b3b3b37155cefcf3255a8c5eb899fd80c08edf71aa7f1"
17
+ },
18
+ "distributions.json": {
19
+ "bytes": 220286,
20
+ "sha256": "17bcf85c09486e5070aa8b3be4f4411fa01e6b3cf1b39c5488a5f46149f52e0f"
21
+ },
22
+ "distributions.pt": {
23
+ "bytes": 63581,
24
+ "sha256": "40a31704ab58d0349daff35083d43dcf4771ef368fd9c8a8503c94fe844016c5"
25
+ },
26
+ "edge_tokens.txt": {
27
+ "bytes": 16,
28
+ "sha256": "42b635ef938d2ec5a601044e4b01f3f84ed121f1bfaac984d86f180a8ccba3fd"
29
+ },
30
+ "epoch_025.ckpt": {
31
+ "bytes": 638268597,
32
+ "sha256": "0f3b9d75897c951fdfe437bb10735fb371ea2f5e9ea982471c3c7bd73a160484"
33
+ },
34
+ "evaluate.py": {
35
+ "bytes": 10649,
36
+ "sha256": "ad57177b2289610ec10983c9f20480adca7e393166ffec78e9641d3546bc433c"
37
+ },
38
+ "metadata.json": {
39
+ "bytes": 1511,
40
+ "sha256": "f1200bd618873ce5ba3e1d0a3569cd852c6b1fbe6c7523863dfb567541228c1d"
41
+ },
42
+ "panels/test_murcko_512_seed42.json": {
43
+ "bytes": 226522,
44
+ "sha256": "5df2f8b632dd261098ee86581b748487163b10893d2a4462f3e52cd25130905b"
45
+ },
46
+ "panels/val_murcko_512_seed42.json": {
47
+ "bytes": 224341,
48
+ "sha256": "b30a2063ba1c74414a2bda387f9dc42ade8c82540d6ec36c58956c12bee64e44"
49
+ },
50
+ "panels/verification.json": {
51
+ "bytes": 282,
52
+ "sha256": "b8a24405b78b49524bd09fd2593f8686b7910680709aed1956b140c45ffbd342"
53
+ },
54
+ "runtime/README.md": {
55
+ "bytes": 8819,
56
+ "sha256": "5214473be19c41f94f4bace3188e2620cbff69610ee15d495aa97934bd7b30eb"
57
+ },
58
+ "runtime/configs/callbacks/callbacks.yaml": {
59
+ "bytes": 699,
60
+ "sha256": "dce0adebf5956dce687d64a074238f9c12866373bfe21df6658d967d6bfd1f90"
61
+ },
62
+ "runtime/configs/data/chembl3d/chembl3d.yaml": {
63
+ "bytes": 278,
64
+ "sha256": "a96974d90353a850c4a1d0bac27de5f1fc8724b81cc5bbc122aab6f76f3051c5"
65
+ },
66
+ "runtime/configs/data/chembl3d/implicit_h.yaml": {
67
+ "bytes": 81,
68
+ "sha256": "08da196d0288cf1fe4dba8dd407c49d9bee003607504beb59f79e5d6b9eaef0c"
69
+ },
70
+ "runtime/configs/data/chembl3d/max100.yaml": {
71
+ "bytes": 246,
72
+ "sha256": "052dba7b3f3efce990019518823476bd6bf1381bb5f606822c9fc7a485e6d0bf"
73
+ },
74
+ "runtime/configs/data/chembl3d/max100_implicit_h.yaml": {
75
+ "bytes": 79,
76
+ "sha256": "152325f11c7d1500469d4ee381100ae12247e6a2e0b90e5ef7501b405144bb2d"
77
+ },
78
+ "runtime/configs/data/crossdocked/finetune.yaml": {
79
+ "bytes": 2709,
80
+ "sha256": "ba3da996e4c0fd720d6403974f3a3ae851d5defb32a705e931a7bf3f031421f4"
81
+ },
82
+ "runtime/configs/data/crossdocked/scaffold_decoration.yaml": {
83
+ "bytes": 1171,
84
+ "sha256": "4498b88a65912705d49b4a3bf583f41329bbf9637f7250ee85bfbd677a8cb22d"
85
+ },
86
+ "runtime/configs/data/geom/geom.yaml": {
87
+ "bytes": 428,
88
+ "sha256": "736c04e86c029bed65ce3ed022f937dd418ddc70d4b9fc064b38d979bf29ee6d"
89
+ },
90
+ "runtime/configs/data/geom/implicit_h.yaml": {
91
+ "bytes": 73,
92
+ "sha256": "c6b3ff5496af997de0c28e5aa7265903d6d0c8b59aab001ab21d6a64baeb772a"
93
+ },
94
+ "runtime/configs/data/geom/scaffold_decoration.yaml": {
95
+ "bytes": 1630,
96
+ "sha256": "46ca8472be2cd145e526555c0130ccfbfa797c056f4000a9db382dc00edfbed4"
97
+ },
98
+ "runtime/configs/data/molecular.yaml": {
99
+ "bytes": 5005,
100
+ "sha256": "f79ade5ddbf7d08f2eecbb2aa9c3e6b182b8a8e7e48960aff5e5edda5601bdc9"
101
+ },
102
+ "runtime/configs/data/qm9/qm9.yaml": {
103
+ "bytes": 3648,
104
+ "sha256": "3beea93868a1e889b049dcc2ccbc3b7489fa4672e964138654bc512ea01ab9f2"
105
+ },
106
+ "runtime/configs/data/qm9/scaffold_decoration.yaml": {
107
+ "bytes": 1202,
108
+ "sha256": "54cf67421a15625ed9e53e1cb959dc5210106004715cbf6169c9d1bce0a87cfa"
109
+ },
110
+ "runtime/configs/default.yaml": {
111
+ "bytes": 1294,
112
+ "sha256": "bcf9744acbf53afa4305ffd6e9cf95e6f99571750488eaa8cad85896a51aca66"
113
+ },
114
+ "runtime/configs/defaults/chembl3d/chembl3d.yaml": {
115
+ "bytes": 90,
116
+ "sha256": "f09553bc670e6c87ac8c1f8bbd0a8df73f1e83b02a3dcf9d6d06b915ac3bce93"
117
+ },
118
+ "runtime/configs/defaults/chembl3d/implicit_h.yaml": {
119
+ "bytes": 102,
120
+ "sha256": "a0ee1440984eb4c9d3c96a5d1c18df9f59b8e42c499f8a340ccd8deb8c454cd8"
121
+ },
122
+ "runtime/configs/defaults/chembl3d/implicit_h_2x4090.yaml": {
123
+ "bytes": 331,
124
+ "sha256": "518571405333a40e1602ee4c77fd153b56fd53e77aaa2aaf530796142f58d4b8"
125
+ },
126
+ "runtime/configs/defaults/chembl3d/train_2x4090.yaml": {
127
+ "bytes": 826,
128
+ "sha256": "0e6b01b264ab1ae70ae7f144ecd0a03d242e24f02feb087e3d5e2b7bc24790da"
129
+ },
130
+ "runtime/configs/defaults/core.yaml": {
131
+ "bytes": 431,
132
+ "sha256": "0dcfb68e2fece1dc277f647d3b3769af882cf8175e0c3b479bc50029e2538b1c"
133
+ },
134
+ "runtime/configs/defaults/crossdocked/finetune.yaml": {
135
+ "bytes": 364,
136
+ "sha256": "d244463c3049387369b5d1d709986246e7cc6a77218a2147df0820b9fd8c5173"
137
+ },
138
+ "runtime/configs/defaults/crossdocked/scaffold_decoration.yaml": {
139
+ "bytes": 240,
140
+ "sha256": "db421328b9df620fee7f19c99cf10bc6a492f6eb81e17df67e72c7f1bd1da70f"
141
+ },
142
+ "runtime/configs/defaults/crossdocked/scaffold_decoration_2x4090.yaml": {
143
+ "bytes": 1187,
144
+ "sha256": "e6b3e0d004213db5d1291db43c21e8ada0f8b3be4b634a2615eb3a0fb58c7d23"
145
+ },
146
+ "runtime/configs/defaults/geom/geom.yaml": {
147
+ "bytes": 425,
148
+ "sha256": "f447a1bdd39e211b5cf43c4cce56e708da6472d58f79e87885621ec4899c1c8e"
149
+ },
150
+ "runtime/configs/defaults/geom/implicit_h.yaml": {
151
+ "bytes": 98,
152
+ "sha256": "9d3a188e6bd2a2b01609ba0d652184bb8a2f7e27fd89d4a3031568051cae605e"
153
+ },
154
+ "runtime/configs/defaults/geom/scaffold_decoration.yaml": {
155
+ "bytes": 1192,
156
+ "sha256": "dda529d12a8dfd3131dddc3baf7893ea6443c5f9fc9736704d8198f669ae4b7b"
157
+ },
158
+ "runtime/configs/defaults/geom/scaffold_decoration_2x4090.yaml": {
159
+ "bytes": 692,
160
+ "sha256": "301bf8d7446a6d9c10064ffc6c73b704ef15b4fe9bcc48aa8ce89ff732c632a1"
161
+ },
162
+ "runtime/configs/defaults/geom/scaffold_decoration_scratch_2x4090.yaml": {
163
+ "bytes": 955,
164
+ "sha256": "728e19428775722e8107f5ee069be1f9533bd43b22061648978369f686bad887"
165
+ },
166
+ "runtime/configs/defaults/qm9/qm9.yaml": {
167
+ "bytes": 429,
168
+ "sha256": "46070ea792300c0817c0acdff256181b21ef17dfe9f72070bb12da27dc28d534"
169
+ },
170
+ "runtime/configs/defaults/qm9/scaffold_decoration.yaml": {
171
+ "bytes": 694,
172
+ "sha256": "0fecd61454aec70caf87aa397e01eb0eaa2eb5072256d75c6c541196c5aedaa4"
173
+ },
174
+ "runtime/configs/defaults/qm9/train_2x4090.yaml": {
175
+ "bytes": 554,
176
+ "sha256": "43a835246afb7126786e8ffa71c8b80ed9db1ef3a16e5240f1eec7882a4ef5ba"
177
+ },
178
+ "runtime/configs/embeddings/geom.yaml": {
179
+ "bytes": 803,
180
+ "sha256": "06793f4d446d899197e3db28d064592af9049612ee78cb0718a63e3d6fdc3151"
181
+ },
182
+ "runtime/configs/embeddings/qm9.yaml": {
183
+ "bytes": 849,
184
+ "sha256": "de57af7f1ff9b44dbb952f852c0177789196105b77f396298ff8e0218c0626b7"
185
+ },
186
+ "runtime/configs/heads/head.yaml": {
187
+ "bytes": 2475,
188
+ "sha256": "238e2346041e0aedccd16a6ed4066e163004c0e45a2cbc6fd1f6d17e16eb44ed"
189
+ },
190
+ "runtime/configs/insertion_head/gmm.yaml": {
191
+ "bytes": 488,
192
+ "sha256": "1e26914eed9b4971e0f6ff0b61bbdb34103233d346719a55ba6273dd3d785c05"
193
+ },
194
+ "runtime/configs/insertion_head/unimodal.yaml": {
195
+ "bytes": 495,
196
+ "sha256": "547a6391c3b341e3e369f8f4c7aab9752c67dfbbb533388d25956e9011b305e8"
197
+ },
198
+ "runtime/configs/integrator/integrator.yaml": {
199
+ "bytes": 1580,
200
+ "sha256": "8f0d9aafb0b2727cd58154e1fd97df693c8b7d4f5a3fb5efcb9188553f54660f"
201
+ },
202
+ "runtime/configs/logging/wandb_logger.yaml": {
203
+ "bytes": 139,
204
+ "sha256": "a9a2787e048d4eb70f2ff3c49d4355f1a5a382047c8619203b1f9131d829928d"
205
+ },
206
+ "runtime/configs/loss_weights/calibrated_geom.yaml": {
207
+ "bytes": 644,
208
+ "sha256": "d4763c84aa9e1eb77fb2b456384332a4b3a84b072d560918b2357222a43f4ca1"
209
+ },
210
+ "runtime/configs/loss_weights/calibrated_geom_frozen.yaml": {
211
+ "bytes": 1141,
212
+ "sha256": "a08acb61d67a852051dbded08f8bb67f21101888fd0a22a96a522781d28c570d"
213
+ },
214
+ "runtime/configs/loss_weights/tuned_weights.yaml": {
215
+ "bytes": 420,
216
+ "sha256": "2bb5884465666d6fc638f787fd79257e4126c69a5cc55b55615fc45e02e65894"
217
+ },
218
+ "runtime/configs/loss_weights/uniform.yaml": {
219
+ "bytes": 336,
220
+ "sha256": "3c14d6443048f69d436fe03792703a33c67f368ecbb349de2fd145da4c96eabc"
221
+ },
222
+ "runtime/configs/model/backbones/dit.yaml": {
223
+ "bytes": 4039,
224
+ "sha256": "65b0f08be8fb307b8b01e7b460fd083fe60d48148fbece378d7e5a23dd4a9fb0"
225
+ },
226
+ "runtime/configs/model/finetune/lora_finetune.yaml": {
227
+ "bytes": 443,
228
+ "sha256": "973cb978622d19e08a762acf9e918a2279f02ad8501b33d3adb3476ca450726c"
229
+ },
230
+ "runtime/configs/model/finetune/pocket_finetune.yaml": {
231
+ "bytes": 959,
232
+ "sha256": "4d17768a2a884b6e083f06103c5220a737f68cfea87fa4882f112d79a3e54bfa"
233
+ },
234
+ "runtime/configs/model/finetune/scaffold_finetune.yaml": {
235
+ "bytes": 6097,
236
+ "sha256": "dbe7adc88454cc52ea4aab9d420b93d5efa78e03003e5e85de6b6733fa1046b3"
237
+ },
238
+ "runtime/configs/model/finetune/scaffold_pocket_finetune.yaml": {
239
+ "bytes": 634,
240
+ "sha256": "b09d57280abc777773bba4c3d1b8c947b85b2fa73c2e7ea21924c49691a98d93"
241
+ },
242
+ "runtime/configs/optimizer/muon_aux_adam.yaml": {
243
+ "bytes": 1212,
244
+ "sha256": "3b0a6189f3ba81f85f7d26c6f6ad7f64dcb25098a14e411211d7a62983a7180c"
245
+ },
246
+ "runtime/configs/schedule/beta.yaml": {
247
+ "bytes": 78,
248
+ "sha256": "75c485edfd70016b255282adcf5de25dfb95c9e32bb6587017fb8935ffe64eaa"
249
+ },
250
+ "runtime/configs/schedule/cubic.yaml": {
251
+ "bytes": 54,
252
+ "sha256": "a998a2d3390ab5daf4df079852ae8daa907687cf059f55c500409c9493949c0c"
253
+ },
254
+ "runtime/configs/schedule/fast_power.yaml": {
255
+ "bytes": 126,
256
+ "sha256": "22f36106bd43ca253ffea412e00539f2b07efbc49109b97c1232df6a827a2baf"
257
+ },
258
+ "runtime/configs/schedule/linear.yaml": {
259
+ "bytes": 55,
260
+ "sha256": "f7881af26080ab5569e67b39f6eae2733c033b9623020e6271959f09fac45fa0"
261
+ },
262
+ "runtime/configs/schedule/smoothstep.yaml": {
263
+ "bytes": 70,
264
+ "sha256": "f6e8651953d93f0d18c997d40c199b3d80ddd352a2041cc51e29adc9a7f22778"
265
+ },
266
+ "runtime/configs/trainer/trainer.yaml": {
267
+ "bytes": 1402,
268
+ "sha256": "6f78e83ad46529bee7a2878b20727ff403420a515c1629bd73a608a2fb654f5f"
269
+ },
270
+ "runtime/pyproject.toml": {
271
+ "bytes": 4537,
272
+ "sha256": "d88a9882c2e441ce342ac72907b38585a6d9f8e45474b9417020bb3ecfa47ef2"
273
+ },
274
+ "runtime/run.py": {
275
+ "bytes": 9958,
276
+ "sha256": "1911e4fc827f2dd0344ab5a508faeb43cd364fc88c3a547223e34704aa7142c0"
277
+ },
278
+ "runtime/src/morph/__init__.py": {
279
+ "bytes": 79,
280
+ "sha256": "bb402ea5c6f79e569205cc2bf4168d06430753ce8499ca4ffb8614743626fd59"
281
+ },
282
+ "runtime/src/morph/__main__.py": {
283
+ "bytes": 149,
284
+ "sha256": "95a7983479559ca2b15c7afa3881b3142ef4636583501184469b972df7646d4f"
285
+ },
286
+ "runtime/src/morph/_vendor/__init__.py": {
287
+ "bytes": 72,
288
+ "sha256": "8480cd966a03870a7142e1763094bf00e221d2c3c2b1af1d4d458fb8b7f37a20"
289
+ },
290
+ "runtime/src/morph/_vendor/geom_drugs_preprocessing.py": {
291
+ "bytes": 11328,
292
+ "sha256": "33a9c21d29df269a5f342a0b0e8bced0fc620559f0cb3a7a4cfabd5c369ceccb"
293
+ },
294
+ "runtime/src/morph/_vendor/muon.py": {
295
+ "bytes": 16405,
296
+ "sha256": "c61af8c2c4a635cc682b84e279851cb54fedc6fb935d8eb11aafbeb1c5e81fc6"
297
+ },
298
+ "runtime/src/morph/cli.py": {
299
+ "bytes": 9723,
300
+ "sha256": "27eee3fd6d6410747f12b19b670a74f50a51b842a50b1eff4e75c46d0d08a986"
301
+ },
302
+ "runtime/src/morph/config.py": {
303
+ "bytes": 704,
304
+ "sha256": "bd3562c030611f87cc514308f155ad93740dd59927732f71b2e15009012d78ff"
305
+ },
306
+ "runtime/src/morph/dataset/__init__.py": {
307
+ "bytes": 620,
308
+ "sha256": "44ae493354fed0fec4b410f46e0c1fb0351b5fbe5f404ff50f77181e60a39cee"
309
+ },
310
+ "runtime/src/morph/dataset/conditional/__init__.py": {
311
+ "bytes": 316,
312
+ "sha256": "8152efe1b0878297add6646862f56304e38459b894a77817adbd652bee7e334a"
313
+ },
314
+ "runtime/src/morph/dataset/conditional/pocket/README.md": {
315
+ "bytes": 19246,
316
+ "sha256": "2d2d8e20160e5554b40cc54258e43a05938c40bd5232565b47494b94259e562c"
317
+ },
318
+ "runtime/src/morph/dataset/conditional/pocket/__init__.py": {
319
+ "bytes": 190,
320
+ "sha256": "c69f5024a5a90a02f08b31402e16e6eac8598b55e9c6433bda72c0394abd0963"
321
+ },
322
+ "runtime/src/morph/dataset/conditional/pocket/build.py": {
323
+ "bytes": 13844,
324
+ "sha256": "fa645b28b00a39b8abd3550d3e4d2988b6c89e8cd5fac4b52e764877d8661759"
325
+ },
326
+ "runtime/src/morph/dataset/conditional/pocket/crossdocked.py": {
327
+ "bytes": 12798,
328
+ "sha256": "9eab93363712132e5b1062d808bc935558b35fb5869ef6b2a638aef98470067b"
329
+ },
330
+ "runtime/src/morph/dataset/conditional/pocket/datamodule.py": {
331
+ "bytes": 4261,
332
+ "sha256": "cfdcedea2b96b96261af5a17eb4dddc7f471e2b0b7b444b630139d3103f82349"
333
+ },
334
+ "runtime/src/morph/dataset/conditional/pocket/evaluation.py": {
335
+ "bytes": 9050,
336
+ "sha256": "270574b07aa19667f97336a6a38e6e9c93a7e09fde82f80d409640b27762adc6"
337
+ },
338
+ "runtime/src/morph/dataset/conditional/pocket/processing.py": {
339
+ "bytes": 7734,
340
+ "sha256": "eb9059ea7e240995992a86920e9e4e77b1fa6130d63327fa82a86239ff766250"
341
+ },
342
+ "runtime/src/morph/dataset/conditional/pocket/scaffold_pocket.py": {
343
+ "bytes": 5518,
344
+ "sha256": "987fc333b34e3e2d25ed7407a5228696573f1c39ef93901c201f7957dde4fa39"
345
+ },
346
+ "runtime/src/morph/dataset/conditional/replay.py": {
347
+ "bytes": 7061,
348
+ "sha256": "fc8ef66f1be371c4252015d8f4b7e555a976e669cf3b2606a44403552bfc0554"
349
+ },
350
+ "runtime/src/morph/dataset/conditional/scaffold/__init__.py": {
351
+ "bytes": 229,
352
+ "sha256": "ef9b1d37a3a18e820dd1b9afefe1849d066d70cbdd93a02b87c09f76e0a9b3b0"
353
+ },
354
+ "runtime/src/morph/dataset/conditional/scaffold/cutters.py": {
355
+ "bytes": 15539,
356
+ "sha256": "08c3992621a3f930d2595361a86e13280f2f0045c6875eb5c0045ccb9946e5bf"
357
+ },
358
+ "runtime/src/morph/dataset/conditional/scaffold/datamodule.py": {
359
+ "bytes": 2182,
360
+ "sha256": "3a1c4ffb8599970e6fe81cd9d462dcabcb44091e446b4eb045868022cb1b19a1"
361
+ },
362
+ "runtime/src/morph/dataset/conditional/scaffold/libinvent_filter_conditions.json": {
363
+ "bytes": 560,
364
+ "sha256": "ac33112e64f47991be40259aa694219a7f1d47f36a4a130cf329aa98205529b2"
365
+ },
366
+ "runtime/src/morph/dataset/conditional/scaffold/libinvent_reactions.smirks": {
367
+ "bytes": 4272,
368
+ "sha256": "0eaf5444865fef1c5c6d5b87ec97f9480a4765fde97cfa6dbbe57d718f9f74ef"
369
+ },
370
+ "runtime/src/morph/dataset/conditional/scaffold/lmdb.py": {
371
+ "bytes": 4864,
372
+ "sha256": "18ee5fcb3fd7ecad0b0b5e1f2bb25e5196e05e492a5e061ef17139a2efcdadc3"
373
+ },
374
+ "runtime/src/morph/dataset/conditional/scaffold/panel.py": {
375
+ "bytes": 9440,
376
+ "sha256": "54aa9377cfd0b3a6883adc6955b7b7164bfc030a20fd901d75c221f551b61036"
377
+ },
378
+ "runtime/src/morph/dataset/conditional/scaffold/wrappers.py": {
379
+ "bytes": 8473,
380
+ "sha256": "69d58714aef4dbf3d4fb953ff62befc64f021eb9afc5fbc04dd9e7f7f8638ecc"
381
+ },
382
+ "runtime/src/morph/dataset/core/__init__.py": {
383
+ "bytes": 376,
384
+ "sha256": "7bab9c675f0850efc50e14a9898accfde5f8d1c1e2420414ac8c0986822d27e0"
385
+ },
386
+ "runtime/src/morph/dataset/core/atomic_data.py": {
387
+ "bytes": 708,
388
+ "sha256": "7d59ec16ed5b3cf82b57b49dd4d7799862709353a65f4d473d4571ba91b86fa6"
389
+ },
390
+ "runtime/src/morph/dataset/core/batch.py": {
391
+ "bytes": 23924,
392
+ "sha256": "7b2b2afd749b621b1ca890d192e4dc9378b61ead9ef34b90f04dc449e6bad369"
393
+ },
394
+ "runtime/src/morph/dataset/core/containers.py": {
395
+ "bytes": 19637,
396
+ "sha256": "adae656c3fbbe5cfe32f378a419c7e1076f653163ffe7d2c1d1d0abc71a99cd7"
397
+ },
398
+ "runtime/src/morph/dataset/core/conversion.py": {
399
+ "bytes": 2224,
400
+ "sha256": "0842565c503048a86a517c202dd0cc7c07e9296a841601b178ec38c43f903a6a"
401
+ },
402
+ "runtime/src/morph/dataset/core/flow_matching_wrapper.py": {
403
+ "bytes": 8406,
404
+ "sha256": "650d791f765f90f5f86353d3edbeb65f2d44bc9089b9123ec407c970c144beb0"
405
+ },
406
+ "runtime/src/morph/dataset/core/insertion_data.py": {
407
+ "bytes": 14393,
408
+ "sha256": "7e99e4d59b29ea537708b5f7887ed3e61b4b31ebf80d55fe5a0c6823ff8a439c"
409
+ },
410
+ "runtime/src/morph/dataset/core/insertion_targets.py": {
411
+ "bytes": 6614,
412
+ "sha256": "2e62e89125310572735d916e97c8a544cfb3eb76fb167ecd134b5f8b0424df8c"
413
+ },
414
+ "runtime/src/morph/dataset/core/lightning_datamodule.py": {
415
+ "bytes": 7495,
416
+ "sha256": "1facb3c332fd3d306631c84aede122be89b54bafbd17eeab25981b1e4a6af7f3"
417
+ },
418
+ "runtime/src/morph/dataset/core/lmdb.py": {
419
+ "bytes": 10111,
420
+ "sha256": "748b7a10f8fb20f74c267de393cc3e1933ea86debde99d8fdaaef94e0a9afd29"
421
+ },
422
+ "runtime/src/morph/dataset/core/preprocessing.py": {
423
+ "bytes": 20145,
424
+ "sha256": "da9cffd6beb6da63ee749dd8b60d4d0509bcd937778014739b04ffc599dde066"
425
+ },
426
+ "runtime/src/morph/dataset/core/properties.py": {
427
+ "bytes": 1952,
428
+ "sha256": "4dc63574f5fde284245078adf50fb2d8a9ac16443b8276154c03b7703cc2e937"
429
+ },
430
+ "runtime/src/morph/dataset/core/representation.py": {
431
+ "bytes": 6591,
432
+ "sha256": "0ecac55e1faf9748826ec8f4c7f1a274263214ea3f68cc69de7047b55dd6a65f"
433
+ },
434
+ "runtime/src/morph/dataset/core/tokenized_decode.py": {
435
+ "bytes": 6559,
436
+ "sha256": "483afb3d468113da33cc8699998e6439fcde2264ee7ca479724206360584a2b8"
437
+ },
438
+ "runtime/src/morph/dataset/core/vocab.py": {
439
+ "bytes": 2236,
440
+ "sha256": "f8618488f7bcdc8a58d8b9f3443b764eac60985a33ead1bcc2d17d3bdc213329"
441
+ },
442
+ "runtime/src/morph/dataset/unconditional/__init__.py": {
443
+ "bytes": 403,
444
+ "sha256": "d7cd3b85745ad1a4d884c0a11a64b3ab9cedcc64e30f604dbe83f5beb14fa97b"
445
+ },
446
+ "runtime/src/morph/dataset/unconditional/chembl3d.py": {
447
+ "bytes": 9854,
448
+ "sha256": "d5cfd1ee4ddd3085b95b9a678d761d4f0beef99ac5ecfb21d3dfb3cae59653eb"
449
+ },
450
+ "runtime/src/morph/dataset/unconditional/geom.py": {
451
+ "bytes": 4071,
452
+ "sha256": "12bc792c2a61b3e6c29ae265b9997ba610765e268408e873918b5a65225945be"
453
+ },
454
+ "runtime/src/morph/dataset/unconditional/qm9.py": {
455
+ "bytes": 9799,
456
+ "sha256": "a23fdb22e86597ae19cf4b8435f82ffcb9c16c32d0dc729a5ca1019aef8fd78a"
457
+ },
458
+ "runtime/src/morph/flow_matching/__init__.py": {
459
+ "bytes": 78,
460
+ "sha256": "674f4630aa8421d7e75c913741c6efb1a163fbc38ce909c6cdb011f9ba9ef48e"
461
+ },
462
+ "runtime/src/morph/flow_matching/assignment.py": {
463
+ "bytes": 16050,
464
+ "sha256": "b6ad66538f5bd7f14e0eee2a0ffd25a4110d1504e057a0a9eb63a354d9f6aadb"
465
+ },
466
+ "runtime/src/morph/flow_matching/integration.py": {
467
+ "bytes": 33356,
468
+ "sha256": "f64b7b9ed2a79c11932b72e498e57eef924b5c85a05aa6893ddfe11e2d4595e9"
469
+ },
470
+ "runtime/src/morph/flow_matching/interpolation.py": {
471
+ "bytes": 23963,
472
+ "sha256": "81939bb12a705f92394d1d2b78d4cf13b592bb20a129524020cc5236074c8072"
473
+ },
474
+ "runtime/src/morph/flow_matching/prior.py": {
475
+ "bytes": 2389,
476
+ "sha256": "0f9b77274238b23be7a2ad88ee8e6dcbb785817fcfa3a35aa863b489d2a6e05d"
477
+ },
478
+ "runtime/src/morph/flow_matching/sampling_step.py": {
479
+ "bytes": 2806,
480
+ "sha256": "c768682a47da1b7852838e20b39317528717fc0485cbc35030b3ca71d7d845bb"
481
+ },
482
+ "runtime/src/morph/flow_matching/scaffold_insertion.py": {
483
+ "bytes": 2886,
484
+ "sha256": "18e0d7093dfaab98b253d202c4fc4649b8b8de96c9a22144c5eeb5041f73aef8"
485
+ },
486
+ "runtime/src/morph/flow_matching/schedules.py": {
487
+ "bytes": 6776,
488
+ "sha256": "6fd8396a05052ae7831babc75ca40a7e748fb9edffe82bb2559533afa949b2a9"
489
+ },
490
+ "runtime/src/morph/inference/__init__.py": {
491
+ "bytes": 732,
492
+ "sha256": "63047340b4c7ed784e969b7d54035ab737282ee2acc6eeb26ba6cb3d52b4f573"
493
+ },
494
+ "runtime/src/morph/inference/bundles.py": {
495
+ "bytes": 6395,
496
+ "sha256": "f6e2d46e6e428452a58208e78c83da01d6c786f10f7062643a093fb6750dafe6"
497
+ },
498
+ "runtime/src/morph/inference/catalog.py": {
499
+ "bytes": 6215,
500
+ "sha256": "53a27b86d39641fd4964da82f4b1875598a0cdb1533f981074c6b7a14b4174b7"
501
+ },
502
+ "runtime/src/morph/inference/generate.py": {
503
+ "bytes": 11889,
504
+ "sha256": "c3181fbf6187cf22b476431ed7c0fdea4c74ea8e97acf270f486318e43646d19"
505
+ },
506
+ "runtime/src/morph/inference/hub.py": {
507
+ "bytes": 9739,
508
+ "sha256": "fa1f43937d66f0ab4b7b9c11ed749aef8d23444b2df3257c36ff72ecd3b821ca"
509
+ },
510
+ "runtime/src/morph/inference/module.py": {
511
+ "bytes": 3820,
512
+ "sha256": "415e0f9c624c191855874b8dd88d5e7aa18941632bc403a9c0b685bbf74f1d60"
513
+ },
514
+ "runtime/src/morph/inference/registry.py": {
515
+ "bytes": 4314,
516
+ "sha256": "8ca3e4246e00707ed0d646e6e760acebd5e3d3443e5e8d5f71f3b54653993fa9"
517
+ },
518
+ "runtime/src/morph/model/README.md": {
519
+ "bytes": 10319,
520
+ "sha256": "9c06b5b90300f7449048640417116aa8869dc1a75f106cbdd9344a89f6e446d3"
521
+ },
522
+ "runtime/src/morph/model/__init__.py": {
523
+ "bytes": 80,
524
+ "sha256": "1c7c3875387d5ba7f9166a804ea5a7ef7bd469728c1ad5c5003150d44e33be51"
525
+ },
526
+ "runtime/src/morph/model/backbones/__init__.py": {
527
+ "bytes": 64,
528
+ "sha256": "a1a1419590c11aa6ed429f8b6b293470e30fe767f5571d2517b0883efb071e0c"
529
+ },
530
+ "runtime/src/morph/model/backbones/conditional_dit.py": {
531
+ "bytes": 25614,
532
+ "sha256": "15aacd0afd334a6d296556a00e5e8849a5dbf021b37c55f1f96af6c9fe706c80"
533
+ },
534
+ "runtime/src/morph/model/backbones/dit.py": {
535
+ "bytes": 31994,
536
+ "sha256": "7d9e1f25cc4174bb9f2e03f57679c4b2d28a292740e3833ac38870d172bbe805"
537
+ },
538
+ "runtime/src/morph/model/build.py": {
539
+ "bytes": 3442,
540
+ "sha256": "e69f4231815f09c094d787f11edc49e222cbdc970edc6e416b6b94a9b88d4b21"
541
+ },
542
+ "runtime/src/morph/model/finetune/__init__.py": {
543
+ "bytes": 282,
544
+ "sha256": "8746b1e29e5a3b3b1805389257270b90f5d30995783f2cf41b9fb533c485fc0c"
545
+ },
546
+ "runtime/src/morph/model/finetune/base/__init__.py": {
547
+ "bytes": 803,
548
+ "sha256": "6e0ded0b3a5c2a76ccb85b05cf63bcc54e145bd1be07d933a737d6151433d856"
549
+ },
550
+ "runtime/src/morph/model/finetune/base/conditional_backbone.py": {
551
+ "bytes": 2853,
552
+ "sha256": "1353e96443eacf3ce88473a30d02ab4f1c8b6a9c90763ea61c58d353b806b378"
553
+ },
554
+ "runtime/src/morph/model/finetune/base/context_encoder.py": {
555
+ "bytes": 13655,
556
+ "sha256": "095784c710fd921c9da4114dd867df636513a696beece28cfa9a79e1d8193320"
557
+ },
558
+ "runtime/src/morph/model/finetune/base/finetune_base.py": {
559
+ "bytes": 10575,
560
+ "sha256": "06b38586346545b6ff19e689944d4c5a6da04431d0b10eefc58c8ee43e88ef46"
561
+ },
562
+ "runtime/src/morph/model/finetune/pocket/__init__.py": {
563
+ "bytes": 455,
564
+ "sha256": "bd83b0aec910454a989214169a9160ef744ec1f115044fceba8fcb3d1d208edd"
565
+ },
566
+ "runtime/src/morph/model/finetune/pocket/pocket_context.py": {
567
+ "bytes": 2913,
568
+ "sha256": "c9d00636bbaa7ba413f4299894e6db28b82e02680005d261d1f7cb52df26ea91"
569
+ },
570
+ "runtime/src/morph/model/finetune/pocket/scaffold_pocket_context.py": {
571
+ "bytes": 1476,
572
+ "sha256": "7c5e3424c7b68ea1dff5d516c1010e0fd5bb417966262eea3896908b6971847f"
573
+ },
574
+ "runtime/src/morph/model/finetune/scaffold/__init__.py": {
575
+ "bytes": 366,
576
+ "sha256": "6345e71c8b03ee5eb0dcd105c0532acf73a1795bf6360b2847be600bcecd40ba"
577
+ },
578
+ "runtime/src/morph/model/finetune/scaffold/scaffold.py": {
579
+ "bytes": 5643,
580
+ "sha256": "54c27926aff97448521be3cd28e8560dda696ae1f9208dbbe3ad79a8e856597f"
581
+ },
582
+ "runtime/src/morph/model/finetune/scaffold/scaffold_context.py": {
583
+ "bytes": 6086,
584
+ "sha256": "d9446254770e3c3ea2c634a0636ab82a718514a7cb69760d756e9235cfbe2814"
585
+ },
586
+ "runtime/src/morph/model/layers/__init__.py": {
587
+ "bytes": 189,
588
+ "sha256": "79671fbdea358c330a45b7fd3d842119b32b1d548804b3d441bbfb7dbd703b69"
589
+ },
590
+ "runtime/src/morph/model/layers/attention.py": {
591
+ "bytes": 22828,
592
+ "sha256": "366f4b7618097715e5a50acbcf7fae3f1fe7015810e6dfd69ce2e657862cdfba"
593
+ },
594
+ "runtime/src/morph/model/layers/embedding.py": {
595
+ "bytes": 14356,
596
+ "sha256": "efe924190da5904e05495f421dcc313134cebe0dee9bf665060e1e5781cd95ef"
597
+ },
598
+ "runtime/src/morph/model/layers/gmm.py": {
599
+ "bytes": 6753,
600
+ "sha256": "628e90c73bdbb3e70104214f80bbf8a94896ddbe22a9a3fc40cce34b20d2565f"
601
+ },
602
+ "runtime/src/morph/model/layers/heads.py": {
603
+ "bytes": 30913,
604
+ "sha256": "7113c29a6be81c93ca980dc19d0847d2015ac36702683e4f1adedd549aed46ae"
605
+ },
606
+ "runtime/src/morph/model/layers/kernels.py": {
607
+ "bytes": 12483,
608
+ "sha256": "be0c52b69c7612946244755e2865de0491440a41163cf828168eb750a8f929cf"
609
+ },
610
+ "runtime/src/morph/model/layers/lora.py": {
611
+ "bytes": 5947,
612
+ "sha256": "7194750605e7b5df9a02e4c4a39cf29c982f117c40198c99c73211c85ecdfe4b"
613
+ },
614
+ "runtime/src/morph/model/lightning/__init__.py": {
615
+ "bytes": 339,
616
+ "sha256": "c53943a4eb1e45568cebfb538655cc5eb7ead46284bbcd515800102c89177e52"
617
+ },
618
+ "runtime/src/morph/model/lightning/_optimizer.py": {
619
+ "bytes": 19578,
620
+ "sha256": "f77519db8d3458c05aec1543cb4cd487995cf398abcc71b62ea1835cb132165e"
621
+ },
622
+ "runtime/src/morph/model/lightning/_sampling.py": {
623
+ "bytes": 15588,
624
+ "sha256": "ab5f423a6910939552a62c2da645ea1e9afcd763bc8b7e0dbfc75953825a9dca"
625
+ },
626
+ "runtime/src/morph/model/lightning/_setup.py": {
627
+ "bytes": 6537,
628
+ "sha256": "835f510094110c4dbc590a88d9b94d41474ae87b442785a949328feda5b9afce"
629
+ },
630
+ "runtime/src/morph/model/lightning/_training.py": {
631
+ "bytes": 2747,
632
+ "sha256": "2d298e8b0833cce18e4bdef76e67bf81b49a9fdca9920868e2e4083fcc398fc4"
633
+ },
634
+ "runtime/src/morph/model/lightning/_validation.py": {
635
+ "bytes": 12558,
636
+ "sha256": "82c9e1a9e2c54827c7ec92dcdb857c6d2bf050b73416ff028e022c86a2710a75"
637
+ },
638
+ "runtime/src/morph/model/lightning/base.py": {
639
+ "bytes": 9294,
640
+ "sha256": "ce9159e7b387ece0a3ce010b164f675b81980d2f617b5b05d00f96695ee33e28"
641
+ },
642
+ "runtime/src/morph/model/losses/__init__.py": {
643
+ "bytes": 393,
644
+ "sha256": "dc20020e6686703a5b0abd0e77b451fc4beace021f22fd6f92d988a48cb4b526"
645
+ },
646
+ "runtime/src/morph/model/losses/flow.py": {
647
+ "bytes": 21766,
648
+ "sha256": "a8e63b363e8e800c5d22d80454999bd8c5d91bb75ff2f9842800bed8e3377e04"
649
+ },
650
+ "runtime/src/morph/model/losses/functional.py": {
651
+ "bytes": 11765,
652
+ "sha256": "42ae52f604a0cb8b7fe08955cfd273945ca068727994f20b02c80cdee3a00e1b"
653
+ },
654
+ "runtime/src/morph/utils/__init__.py": {
655
+ "bytes": 78,
656
+ "sha256": "eb76ca86f046931eaea693a696317e8b3ce0b8ad8695a92e3744fd1892f2b9b8"
657
+ },
658
+ "runtime/src/morph/utils/checkpoints.py": {
659
+ "bytes": 2301,
660
+ "sha256": "750302eb039e3947a5d4c84e4e7d04eb46e974cc0031ef1d197a3573e20eae4b"
661
+ },
662
+ "runtime/src/morph/utils/edge_alignment.py": {
663
+ "bytes": 4896,
664
+ "sha256": "0edcde93cfa3a2d825e783ea5cf64715aa248e59ffb8c6a23d4490d74976a71e"
665
+ },
666
+ "runtime/src/morph/utils/graph_math.py": {
667
+ "bytes": 6755,
668
+ "sha256": "2bae326c3538d757aac66dcfc4a0b5d023b17bd987d814d3dab9a57f9ce60552"
669
+ },
670
+ "runtime/src/morph/utils/loss_accumulation.py": {
671
+ "bytes": 15115,
672
+ "sha256": "0f3eca42145e715290576dd86f4bcfb1553714ec4cabeda9246cfaf1e4f4323c"
673
+ },
674
+ "runtime/src/morph/utils/lr_schedulers.py": {
675
+ "bytes": 5554,
676
+ "sha256": "b954c12df413ca6676263ef1af960e2a4ff52692acfd4223e72a6e02122a3638"
677
+ },
678
+ "runtime/src/morph/utils/metrics/DIFFDEC_LICENSE": {
679
+ "bytes": 1074,
680
+ "sha256": "729bec7640e96b6357728566e43ac7dec9b7393f15c8ee96edf1e699c00e91f1"
681
+ },
682
+ "runtime/src/morph/utils/metrics/__init__.py": {
683
+ "bytes": 7120,
684
+ "sha256": "ab3cfce2cc6ce7d7cdacedefe91d195c6783e0d6d21762beb6cb4446f47deb32"
685
+ },
686
+ "runtime/src/morph/utils/metrics/chemistry.py": {
687
+ "bytes": 36408,
688
+ "sha256": "db05302c87b6880167be67dc1d5f7b35924bb84f6a7a0b5b32d2d62e54a00735"
689
+ },
690
+ "runtime/src/morph/utils/metrics/denovo.py": {
691
+ "bytes": 26664,
692
+ "sha256": "57517d1124c6067a1682e13f6faa439ecbceb3d8c12ea0d13f6da0db503cd5c9"
693
+ },
694
+ "runtime/src/morph/utils/metrics/diffdec.py": {
695
+ "bytes": 7879,
696
+ "sha256": "37d334fb306afd114de2f50ee4ad5dc48f7565adaa6a077a08e01fc1460599c8"
697
+ },
698
+ "runtime/src/morph/utils/metrics/distribution.py": {
699
+ "bytes": 3348,
700
+ "sha256": "0a6c25c4e65843308059a3effc44e1072837e747926074c1d7c47ba7ee72cb46"
701
+ },
702
+ "runtime/src/morph/utils/metrics/plotting.py": {
703
+ "bytes": 4270,
704
+ "sha256": "686d491d7fbc83cf35f772649a869dc1a271966d2a4fa6162d6e2334db735723"
705
+ },
706
+ "runtime/src/morph/utils/metrics/posebusters_energy.py": {
707
+ "bytes": 3567,
708
+ "sha256": "a705c1baa499a5a4e95329f941f6c762fb49feb505819ed0a35b34daf46ac521"
709
+ },
710
+ "runtime/src/morph/utils/metrics/posecheck_strain.py": {
711
+ "bytes": 6531,
712
+ "sha256": "1fd8340484f542d6a9abce041b8caee20901b9d704bc354f37baf67c76d156a2"
713
+ },
714
+ "runtime/src/morph/utils/metrics/scaffold.py": {
715
+ "bytes": 6312,
716
+ "sha256": "44965940eb17c661734ab0a816123ac5e50a33103aaca217596cca697fd8be6d"
717
+ },
718
+ "runtime/src/morph/utils/metrics/scaffold_posebusters.py": {
719
+ "bytes": 3825,
720
+ "sha256": "d5372df116119a55074cab7ceae085568be4edbb0c932b0961a1ed19bb453f83"
721
+ },
722
+ "runtime/src/morph/utils/metrics/tensor.py": {
723
+ "bytes": 13503,
724
+ "sha256": "3fcb867cde5d47f81725acc3b862fc56af5ecce23dee3a11ebe6d05c1772c6fe"
725
+ },
726
+ "runtime/src/morph/utils/rdkit_utils.py": {
727
+ "bytes": 9053,
728
+ "sha256": "0da70d65eb85d9906e4b611ffa087a870f6133df6ea99a9b21a35b55ad57ffc5"
729
+ },
730
+ "runtime/src/morph/utils/run_config.py": {
731
+ "bytes": 8025,
732
+ "sha256": "0c289120887a051379523af528ebc9310f356f3b92efb898ddaf773dbf698aa2"
733
+ },
734
+ "runtime/src/morph/utils/vocab_utils.py": {
735
+ "bytes": 6343,
736
+ "sha256": "7b5506971b5092865b86311173b24d7fb70a881f123c57fd8696c412a25d618b"
737
+ },
738
+ "runtime/uv.lock": {
739
+ "bytes": 615473,
740
+ "sha256": "4b85cc8b6e28d7fe42533df16cc9633d909fd91296b38b3d8ebce3dbbf8317aa"
741
+ },
742
+ "training_config.yaml": {
743
+ "bytes": 15526,
744
+ "sha256": "7243310c19f0d90399db52845e8ad0446d66e303b64863cbce444058e1bf06bf"
745
+ },
746
+ "training_source.tar.gz": {
747
+ "bytes": 510894,
748
+ "sha256": "f2ee1bf919ad17d664f0614252f43bdcf39b2474e5a1069a37429a520adb8a2f"
749
+ }
750
+ }
geom/scaffold_murcko_substituent_lora_r16/metadata.json ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "task": "scaffold_decoration",
3
+ "dataset": "GEOM-Drugs",
4
+ "hydrogens": "explicit",
5
+ "completed_epochs": 25,
6
+ "checkpoint_epoch_zero_based": 24,
7
+ "global_step": 199100,
8
+ "checkpoint_sha256": "0f3b9d75897c951fdfe437bb10735fb371ea2f5e9ea982471c3c7bd73a160484",
9
+ "checkpoint_bytes": 638268597,
10
+ "original_checkpoint": "/cluster/project/krause/frankem/morph-chem/logs/training/geom-scaffold-substituent-lora-r16-20261012T095326Z/run/every_n_epochs/epoch=24-step=199100.ckpt",
11
+ "training_job_id": 16886672,
12
+ "model": "geom-scaffold-murcko-substituent-lora-r16",
13
+ "lora_rank": 16,
14
+ "lora_alpha": 16,
15
+ "base_model": "geom-explicit-h",
16
+ "base_completed_epochs": 100,
17
+ "base_checkpoint_sha256": "0ab5ad7247b5518858c8cb6aa7c4102352e8fe09cbb6744c33a4c784c85b8587",
18
+ "insertion_assignment": "substituent",
19
+ "freeze_keep_positions": false,
20
+ "freeze_keep_charges": true,
21
+ "default_eval_weights": "ema",
22
+ "integration_steps": 100,
23
+ "original_checkpoint_including_optimizer": true,
24
+ "source_training_archive": "training_source.tar.gz",
25
+ "evaluation_runtime": "runtime",
26
+ "panel_sha256": {
27
+ "val_murcko_512_seed42.json": "b30a2063ba1c74414a2bda387f9dc42ade8c82540d6ec36c58956c12bee64e44",
28
+ "test_murcko_512_seed42.json": "5df2f8b632dd261098ee86581b748487163b10893d2a4462f3e52cd25130905b"
29
+ },
30
+ "evaluation_protocol": {
31
+ "split": "test",
32
+ "n_scaffolds": 512,
33
+ "samples_per_scaffold": 8,
34
+ "seeds": [
35
+ 42,
36
+ 43,
37
+ 44
38
+ ]
39
+ },
40
+ "not_unconditional_catalog_entry": true
41
+ }
geom/scaffold_murcko_substituent_lora_r16/panels/test_murcko_512_seed42.json ADDED
The diff for this file is too large to render. See raw diff
 
geom/scaffold_murcko_substituent_lora_r16/panels/val_murcko_512_seed42.json ADDED
The diff for this file is too large to render. See raw diff
 
geom/scaffold_murcko_substituent_lora_r16/panels/verification.json ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ {
2
+ "panel": "val_murcko_512_seed42.json",
3
+ "samples": 4096,
4
+ "scaffolds": 512,
5
+ "samples_per_scaffold": 8,
6
+ "two_rank_disjoint_coverage": true,
7
+ "reference_roundtrip_checks": 16,
8
+ "all_reference_retention_rmsd_recovery_checks_passed": true,
9
+ "model_generation_run": false
10
+ }
geom/scaffold_murcko_substituent_lora_r16/runtime/README.md ADDED
@@ -0,0 +1,202 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Morph
2
+
3
+ Flow-matching molecular generation on QM9, GEOM-Drugs, and ChEMBL3D, including
4
+ scaffold-decoration fine-tuning. A molecular diffusion transformer (DiT)
5
+ predicts clean coordinates, atom and bond types, charges, and edit posteriors.
6
+ The sampler combines those predictions with time schedules to move, insert,
7
+ delete, and substitute atoms and bonds.
8
+
9
+ ## Pretrained models
10
+
11
+ [Checkpoints on Hugging Face](https://huggingface.co/maltefranke/morph)
12
+ include QM9 with explicit hydrogens (epochs 1,000 and 1,500), GEOM-Drugs with
13
+ explicit hydrogens (epochs 50 and 100), and ChEMBL3D with explicit hydrogens
14
+ (epochs 200, 300, 400) or implicit hydrogens (epochs 100, 200, 300). Download and generate without preparing a dataset:
15
+
16
+ ```bash
17
+ uv sync --locked
18
+ source .venv/bin/activate
19
+ hf auth login
20
+ morph models list
21
+ morph models download chembl3d-implicit-h --epoch 300
22
+ # Run generation on a compute worker:
23
+ morph generate --model chembl3d-implicit-h --epoch 300 \
24
+ --device cuda --batch 64 --batch-size 16 --output molecules.sdf
25
+ ```
26
+
27
+ Python loading is equally direct:
28
+
29
+ ```python
30
+ from morph.inference import load_pretrained
31
+
32
+ model = load_pretrained("chembl3d-implicit-h", epoch=300, device="cuda")
33
+ ```
34
+
35
+ The loader fetches the selected weights, config, vocabularies and distributions
36
+ and constructs only one network (EMA by default; `use_ema=False` selects current
37
+ weights). `morph models list` discovers shortcuts from the Hub's `catalog.json`;
38
+ `--offline` reads the cached catalog. Adding a release requires updating that
39
+ manifest, with each entry pointing to its model release. You can also pass any
40
+ Hugging Face `owner/repo` with `subfolder` and `revision`, or load a local bundle
41
+ with `load_pretrained(weights_dir="/path/to/bundle")`. See [inference](docs/inference.md) for sampling,
42
+ offline use, checkpoint selection and adding new model releases.
43
+
44
+ ## Reproducibility
45
+
46
+ Each model has a **training bundle** with its original training configuration,
47
+ launch settings, frozen source, dependency versions and data references.
48
+ The bundles live alongside the corresponding model files on Hugging Face:
49
+
50
+ | Model | Configuration and source |
51
+ | --- | --- |
52
+ | QM9, explicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/qm9/explicit_h/training_bundle.tar.gz) |
53
+ | GEOM-Drugs, explicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/geom/explicit_h/training_bundle.tar.gz) |
54
+ | ChEMBL3D, explicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/chembl3d/explicit_h/training_bundle.tar.gz) |
55
+ | ChEMBL3D, implicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/chembl3d/implicit_h/training_bundle.tar.gz) |
56
+
57
+ For example, download and unpack the GEOM training bundle:
58
+
59
+ ```bash
60
+ hf download maltefranke/morph geom/explicit_h/training_bundle.tar.gz --local-dir .
61
+ tar -xzf geom/explicit_h/training_bundle.tar.gz
62
+ ```
63
+
64
+ Each bundle also includes a portable training recipe; its changes from the
65
+ original run are recorded alongside it. See the [training bundle guide](docs/reproducibility.md)
66
+ for setup, data requirements and resuming. Version identifiers and checksums
67
+ are recorded in the release metadata and bundle manifests.
68
+
69
+ ## Training quick start
70
+
71
+ Preprocessed splits, vocabularies, and statistics are available in the public
72
+ [Morph data repository](https://huggingface.co/datasets/maltefranke/morph-data).
73
+ Download a cache into this checkout and skip preprocessing:
74
+
75
+ ```bash
76
+ hf download maltefranke/morph-data --type dataset --include 'qm9/**' --local-dir data
77
+ ```
78
+
79
+ Replace `qm9/**` with `geom/**`, `chembl3d/**`, `chembl3d_max100/**`, or
80
+ `chembl3d_max100_implicit_h/**` as needed. The released ChEMBL3D checkpoints use
81
+ the two `max100` caches; the dataset card documents the matching training bundles.
82
+
83
+ Install [uv](https://docs.astral.sh/uv/) and use Python 3.11 or newer.
84
+ From the repository root:
85
+
86
+ ```bash
87
+ bash setup_env.sh
88
+ source .venv/bin/activate
89
+ source .env
90
+
91
+ # Choose qm9, geom, or chembl3d; the workflow is the same for each.
92
+ dataset=chembl3d
93
+ python scripts/preprocess.py "$dataset"
94
+ python run.py defaults="$dataset/$dataset"
95
+ ```
96
+
97
+ Setup installs the versions in `uv.lock` and creates `.env` only when it is
98
+ absent. Local outputs default to `outputs/`. To choose another artifact root,
99
+ set `MORPH_SCRATCH` before running setup. Training uses W&B by default; add
100
+ `logging.enabled=false` for a local run without W&B logging.
101
+
102
+ `scripts/preprocess.py` downloads missing source files into `data/<dataset>/raw/`,
103
+ processes the train/validation/test splits, and computes vocabularies and training
104
+ statistics in `data/<dataset>/processed/`. Rerunning it reuses completed files.
105
+ No manual download or source directory is required. Run preprocessing once on a
106
+ CPU worker before starting distributed training; add `--workers 20` to choose
107
+ the number of preprocessing workers.
108
+
109
+ GEOM and ChEMBL3D support heavy-atom graphs with implicit hydrogens. Choose
110
+ `--hydrogen-mode implicit` when preprocessing, then select the matching recipe:
111
+
112
+ ```bash
113
+ python scripts/preprocess.py "$dataset" --hydrogen-mode implicit
114
+ python run.py defaults="$dataset/implicit_h"
115
+ ```
116
+
117
+ Set `dataset` to `geom` or `chembl3d` for this option. It uses a separate
118
+ `data/<dataset>_implicit_h/` cache with its own vocabularies and statistics.
119
+ See [training and data options](docs/training.md) for filters and optional local
120
+ source overrides.
121
+
122
+ GPU training compiles both current and EMA models by default. For short smoke
123
+ tests, use `MORPH_COMPILE=0`. The full training defaults require a GPU; a small
124
+ CPU run can be requested explicitly:
125
+
126
+ ```bash
127
+ MORPH_COMPILE=0 python run.py logging.enabled=false \
128
+ trainer.trainer.accelerator=cpu trainer.trainer.devices=1 \
129
+ trainer.trainer.strategy=auto integrator.integrator.num_integration_steps=2 \
130
+ trainer.trainer.precision=32-true +trainer.trainer.fast_dev_run=true \
131
+ data.datamodule.num_workers.train=0 data.datamodule.batch_size.train=2 \
132
+ data.datamodule.batch_size.val=2 data.datamodule.batch_size.test=2
133
+ ```
134
+
135
+ ## Scaffold decoration
136
+
137
+ Fine-tune a pretrained unconditional DiT while conditioning on retained atoms:
138
+
139
+ ```bash
140
+ python run.py defaults=qm9/scaffold_decoration \
141
+ model.module.pretrained_ckpt=/path/to/unconditional.ckpt \
142
+ data.scaffold_kind=rgroup
143
+ ```
144
+
145
+ Supported retained structures are Murcko scaffolds, BRICS fragments (`rgroup`),
146
+ heavy atoms, and fragments for linker generation. See
147
+ [training and fine-tuning](docs/training.md) for their semantics and LoRA options.
148
+
149
+ ## Reading the implementation
150
+
151
+ The main data path is:
152
+
153
+ ```text
154
+ Dataset → prior/target alignment → interpolation → collated training batch
155
+ → DiT endpoint/edit predictions → per-graph losses → optimizer/EMA
156
+ ```
157
+
158
+ Sampling repeatedly decodes predictions, applies task masks, integrates edits,
159
+ and recenters coordinates. Output coordinates are restored to Angstroms.
160
+
161
+ | Entry point | Purpose |
162
+ | --- | --- |
163
+ | [run.py](run.py) | Training, validation, and prediction orchestration |
164
+ | [scripts/preprocess.py](scripts/preprocess.py) | Automatic dataset download, processing and statistics |
165
+ | [model/build.py](src/morph/model/build.py) | Shared model and metric construction |
166
+ | [flow_matching/](src/morph/flow_matching/) | Assignment, interpolation, schedules, and integration |
167
+ | [dataset/core/](src/morph/dataset/core/) | Molecular batches and structured insertion targets |
168
+ | [model/lightning/](src/morph/model/lightning/) | Training lifecycle, losses, sampling, and EMA |
169
+ | [configs/](configs/) | Dataset and model recipes |
170
+ | [tests/model/](tests/model/) | Regression tests grouped by subject |
171
+
172
+ The [model contract](src/morph/model/README.md) documents tensor shapes,
173
+ coordinate conventions, conditioning ports, and the loss-to-head mapping.
174
+
175
+ ## Further documentation
176
+
177
+ - [Training, configurations, and fine-tuning](docs/training.md)
178
+ - [Inference and model bundles](docs/inference.md)
179
+ - [Reproducing released models and training runs](docs/reproducibility.md)
180
+ - [Runtime, compilation, and performance](docs/performance.md)
181
+ - [Euler cluster setup and SLURM](docs/euler.md)
182
+ - [Development and regression checks](docs/development.md)
183
+ - [Release metadata](docs/release.md)
184
+
185
+ ## Citation
186
+
187
+ If you use Morph, please cite our [paper](https://arxiv.org/abs/2606.07239):
188
+
189
+ ```bibtex
190
+ @misc{franke2026generativemolecularmorphingflexiblesize,
191
+ title={Generative Molecular Morphing for Flexible-Size Design via Unbalanced Optimal Transport},
192
+ author={Malte Franke and Stefan P. Schmid and Zarko Ivkovic and Kjell Jorner and Andreas Krause},
193
+ year={2026},
194
+ eprint={2606.07239},
195
+ archivePrefix={arXiv},
196
+ primaryClass={cs.LG},
197
+ url={https://arxiv.org/abs/2606.07239},
198
+ }
199
+ ```
200
+
201
+ Machine-readable citation metadata is available in [CITATION.cff](CITATION.cff).
202
+ License details are pending.
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/callbacks/callbacks.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+
3
+ ## Put your callback args here
4
+ callbacks:
5
+ monitor_metric: ${if:${oc.eval:"'${representation}' in ('geometric_graph', 'molecule')"},val/validity,val/batch/pair_dist_l1}
6
+ monitor_metric_mode: ${if:${oc.eval:"'${representation}' in ('geometric_graph', 'molecule')"},max,min}
7
+
8
+ # model_checkpoints -> best-by-metric (top-1, monitored)
9
+ # every_n_epochs_checkpoint -> latest (no monitor; overwrites every epoch)
10
+ model_checkpoints:
11
+ save_top_k: 1
12
+ save_last: False
13
+ verbose: False
14
+
15
+ every_n_epochs_checkpoint:
16
+ every_n_epochs: 1
17
+ save_top_k: 1
18
+ save_last: False
19
+ verbose: False
20
+ save_on_train_epoch_end: True # Force per-train-epoch firing
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/chembl3d.yaml ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ # LoQI ChEMBL3D source format, without dataset-specific size/element filters.
2
+ defaults:
3
+ - /data/molecular@_here_
4
+ - _self_
5
+
6
+ dataset_name: chembl3d
7
+ dataset_target: morph.dataset.unconditional.chembl3d.FlowMatchingChEMBL3DDataset
8
+ source_dir: ${oc.env:CHEMBL3D_SOURCE_DIR,null}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/implicit_h.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ defaults:
2
+ - /data/chembl3d/chembl3d@_here_
3
+ - _self_
4
+
5
+ hydrogen_mode: implicit
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/max100.yaml ADDED
@@ -0,0 +1,7 @@
 
 
 
 
 
 
 
 
1
+ # Inclusive total-atom limit, applied before hydrogen removal.
2
+ defaults:
3
+ - /data/chembl3d/chembl3d@_here_
4
+ - _self_
5
+
6
+ max_atoms: 100
7
+ cache_name: ${data.dataset_name}_max${data.max_atoms}${if:${eq:${data.hydrogen_mode},implicit},_implicit_h,''}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/chembl3d/max100_implicit_h.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ defaults:
2
+ - /data/chembl3d/max100@_here_
3
+ - _self_
4
+
5
+ hydrogen_mode: implicit
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/crossdocked/finetune.yaml ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - /data/molecular@_here_
3
+ - _self_
4
+
5
+ dataset_name: crossdocked
6
+ dataset_target: morph.dataset.conditional.pocket.CrossDockedPocketDataset
7
+ hydrogen_mode: implicit
8
+ root: ${data.root_path}/crossdocked
9
+ source_dir: ${oc.env:MORPH_CROSSDOCKED_RAW,null}
10
+ pretrained_assets_root: ${oc.env:MORPH_PRETRAINED_ASSETS}
11
+ pretrain_root: ${data.root_path}/chembl3d_implicit_h
12
+ pretrain_source_dir: ${oc.env:CHEMBL3D_SOURCE_DIR,null}
13
+ pretrain_max_atoms: null
14
+ pretrain_allowed_atom_symbols: null
15
+ finetune_fraction: 0.5 # p / 100, sampled per example with replacement
16
+ train_epoch_size: null # source draws per epoch; defaults to retained fine-tuning size
17
+ pocket_vocab: null # Filled from TRAIN pocket elements during datamodule setup.
18
+ max_pocket_atoms: 512
19
+ val_fraction: 0.02
20
+
21
+ # Load the exact artifacts associated with the pretrained checkpoint.
22
+ # Never discover a new ligand vocabulary/normalization for fine-tuning.
23
+ preprocessing:
24
+ root: ${data.pretrained_assets_root}
25
+ train_dataset: null
26
+ atom_tokens_path: ${data.preprocessing.root}/atom_tokens.txt
27
+ edge_tokens_path: ${data.preprocessing.root}/edge_tokens.txt
28
+ charge_tokens_path: ${data.preprocessing.root}/charge_tokens.txt
29
+ distributions_path: ${data.preprocessing.root}/distributions.pt
30
+
31
+ datamodule:
32
+ _target_: morph.dataset.conditional.pocket.datamodule.PocketLightningDataModule
33
+ pocket_vocab: ${data.pocket_vocab}
34
+ finetune_fraction: ${data.finetune_fraction}
35
+ train_epoch_size: ${data.train_epoch_size}
36
+ mix_seed: ${seed}
37
+ pretrain_dataset:
38
+ _target_: morph.dataset.unconditional.chembl3d.FlowMatchingChEMBL3DDataset
39
+ root: ${data.pretrain_root}
40
+ source_dir: ${data.pretrain_source_dir}
41
+ hydrogen_mode: ${data.hydrogen_mode}
42
+ max_atoms: ${data.pretrain_max_atoms}
43
+ allowed_atom_symbols: ${data.pretrain_allowed_atom_symbols}
44
+ preprocess_workers: ${data.preprocess_workers}
45
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
46
+ datasets:
47
+ train:
48
+ root: ${data.root}
49
+ allowed_atom_symbols: null
50
+ max_pocket_atoms: ${data.max_pocket_atoms}
51
+ val_fraction: ${data.val_fraction}
52
+ val:
53
+ - _target_: ${data.dataset_target}
54
+ root: ${data.root}
55
+ source_dir: ${data.source_dir}
56
+ hydrogen_mode: ${data.hydrogen_mode}
57
+ max_pocket_atoms: ${data.max_pocket_atoms}
58
+ val_fraction: ${data.val_fraction}
59
+ preprocess_workers: ${data.preprocess_workers}
60
+ test:
61
+ - _target_: ${data.dataset_target}
62
+ root: ${data.root}
63
+ source_dir: ${data.source_dir}
64
+ hydrogen_mode: ${data.hydrogen_mode}
65
+ max_pocket_atoms: ${data.max_pocket_atoms}
66
+ val_fraction: ${data.val_fraction}
67
+ preprocess_workers: ${data.preprocess_workers}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/crossdocked/scaffold_decoration.yaml ADDED
@@ -0,0 +1,31 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - /data/crossdocked/finetune@_here_
3
+ - _self_
4
+
5
+ # Morph's DiffDec task uses LibINVENT reaction cuts. Other shared cutters can
6
+ # be selected explicitly (murcko, rgroup, linker).
7
+ scaffold_kind: libinvent
8
+ scaffold_groups_dir: ${data.root}/scaffold_groups
9
+ enumerate_slicings: true
10
+ # Fixed scaffold/pocket pairs with repeated draws enable retention, recovery,
11
+ # within-condition uniqueness/diversity, RMSD, and scaffold-aware PoseBusters.
12
+ # Unlike a topology-only panel, identical scaffolds in different pockets remain
13
+ # distinct conditions. Splits with fewer pairs use all of them.
14
+ eval_panel:
15
+ n_scaffolds: 512
16
+ samples_per_scaffold: 8
17
+ seed: 42
18
+ # Denoise movable scaffold coordinates; physical Angstroms, training only.
19
+ jitter_scale: 0.1
20
+ insertion_assignment: nearest
21
+ interpolator:
22
+ insertion_assignment: ${data.insertion_assignment}
23
+ jitter_scale: ${data.jitter_scale}
24
+
25
+ datamodule:
26
+ _target_: morph.dataset.conditional.pocket.scaffold_pocket.ScaffoldPocketDataModule
27
+ scaffold_groups_dir: ${data.scaffold_groups_dir}
28
+ scaffold_kind: ${data.scaffold_kind}
29
+ enumerate_slicings: ${data.enumerate_slicings}
30
+ eval_panel: ${data.eval_panel}
31
+ rotate: true
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/geom/geom.yaml ADDED
@@ -0,0 +1,11 @@
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GEOM-Drugs source format and default molecule subset.
2
+ defaults:
3
+ - /data/molecular@_here_
4
+ - _self_
5
+
6
+ dataset_name: geom
7
+ dataset_target: morph.dataset.unconditional.geom.FlowMatchingGEOMDataset
8
+ source_dir: ${oc.env:GEOM_SOURCE_DIR,null} # Optional prefiltered pickle directory.
9
+ # Preserve the established GEOM subset. Limits apply before hydrogen removal.
10
+ max_atoms: 72
11
+ allowed_atom_symbols: [H, C, N, O, S, F, Cl, Br, P, I]
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/geom/implicit_h.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ defaults:
2
+ - /data/geom/geom@_here_
3
+ - _self_
4
+
5
+ hydrogen_mode: implicit
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/geom/scaffold_decoration.yaml ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # GEOM-Drugs data config for the **scaffold decoration** task.
2
+ #
3
+ # Mirrors configs/data/qm9/scaffold_decoration.yaml but on GEOM-Drugs: source
4
+ # is the selected scaffold of each target; target is the full molecule. The model
5
+ # learns to grow decoration atoms while denoising the retained scaffold.
6
+ #
7
+ # Inherits from configs/data/geom/geom.yaml and overrides:
8
+ # - datamodule._target_ -> ScaffoldLightningDataModule
9
+ #
10
+ # The scaffold cache is built lazily into ``scaffold_groups_dir`` on first run
11
+ # (one file per split and scaffold kind) and reused afterwards.
12
+ defaults:
13
+ - /data/geom/geom@_here_
14
+ - _self_
15
+
16
+ scaffold_groups_dir: ${data.preprocessing.root}/processed/scaffold_groups
17
+ scaffold_kind: rgroup # murcko, rgroup (BRICS), heavy (generate H), linker
18
+ # Physical Angstroms, per coordinate axis. Used only by training interpolation.
19
+ jitter_scale: 0.1
20
+ # "substituent" restricts insertion parents to the target branch or its
21
+ # scaffold attachments. This changes training supervision, not atom OT.
22
+ insertion_assignment: nearest
23
+ interpolator:
24
+ jitter_scale: ${data.jitter_scale}
25
+ insertion_assignment: ${data.insertion_assignment}
26
+ # Exhaustive evaluation when eval_panel=null; training always draws randomly.
27
+ enumerate_slicings: false
28
+ eval_panel:
29
+ directory: ${data.scaffold_groups_dir}/eval_panels
30
+ n_scaffolds: 512
31
+ samples_per_scaffold: 8
32
+ seed: 42
33
+ datamodule:
34
+ _target_: morph.dataset.conditional.scaffold.datamodule.ScaffoldLightningDataModule
35
+ scaffold_groups_dir: ${data.scaffold_groups_dir}
36
+ scaffold_kind: ${data.scaffold_kind}
37
+ enumerate_slicings: ${data.enumerate_slicings}
38
+ eval_panel: ${data.eval_panel}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/molecular.yaml ADDED
@@ -0,0 +1,136 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Shared drug-like molecular training and preprocessing configuration.
2
+
3
+ root_path: ${oc.env:PROJECT_ROOT}/data
4
+ preprocess_workers: 10
5
+ preprocess_chunk_size: 128
6
+ statistics_workers: ${data.preprocess_workers}
7
+ hydrogen_mode: explicit
8
+ # The mode is part of the cache path; changing it discovers fresh vocab/statistics.
9
+ cache_name: ${data.dataset_name}${if:${eq:${data.hydrogen_mode},implicit},_implicit_h,''}
10
+ source_dir: null
11
+ max_atoms: null
12
+ allowed_atom_symbols: null
13
+
14
+ n_atoms_strategy: "uniform" # "approx", "empirical", "learned"
15
+ # Uniform source-count support padding; explicit so inference and training can
16
+ # be matched or ablated without code changes.
17
+ uniform_n_atoms_padding: 5
18
+ optimal_transport: "equivariant"
19
+ # Physical Angstroms (converted to normalized coordinates internally). These
20
+ # reproduce the former normalized 0.25 / 0.2 scales at GEOM's training-set std.
21
+ # Physical Angstroms. Insertion bridge noise vanishes as (1-t); move noise is
22
+ # the persistent training-time jitter floor for matched atoms and insertions
23
+ # that are already present.
24
+ ins_noise_scale: 0.65
25
+ move_noise_scale: 0.5
26
+
27
+ # Drug-like datasets contain charged species; allow them in validity metrics.
28
+ allow_charged: true
29
+
30
+
31
+ # Preprocessing configuration - computes tokens from data and distributions from training dataset only
32
+ preprocessing:
33
+ _target_: morph.dataset.core.preprocessing.Preprocessing
34
+ _recursive_: false
35
+ root: ${data.root_path}/${data.cache_name}
36
+ num_workers: ${data.statistics_workers}
37
+ train_dataset: ${data.datamodule.datasets.train}
38
+ atom_tokens_path: ${data.preprocessing.root}/processed/atom_tokens.txt
39
+ edge_tokens_path: ${data.preprocessing.root}/processed/edge_tokens.txt
40
+ charge_tokens_path: ${data.preprocessing.root}/processed/charge_tokens.txt
41
+ distributions_path: ${data.preprocessing.root}/processed/distributions.pt
42
+ # Tokens and distributions will be discovered from data and saved to files
43
+ # On subsequent runs, they will be loaded from the files
44
+
45
+ vocab: null
46
+
47
+ interpolator:
48
+ _target_: morph.flow_matching.interpolation.Interpolator
49
+ distributions: null
50
+ optimal_transport: ${data.optimal_transport}
51
+ ins_noise_scale: ${data.ins_noise_scale}
52
+ move_noise_scale: ${data.move_noise_scale}
53
+ # Training-only categorical corruption; pairs are scaled per molecule to
54
+ # match the expected number of changed atoms. Clean targets stay unchanged.
55
+ atom_type_noise_prob: 0.0
56
+ # OT matching (cost always squared-Euclidean; move cost in Angstrom). c_move
57
+ # scales the squared move distance (A^2); c_ins/c_del/c_sub are fixed A^2 costs,
58
+ # directly comparable. At 6.125 each, delete+insert costs 3.5^2, so atoms
59
+ # farther than 3.5 A apart are left unmatched by default.
60
+ pre_align_iters: 2 # equivariant-OT inner loop (Hungarian+Kabsch) iters
61
+ c_move: 1.0
62
+ c_sub: 0.0
63
+ c_ins: 6.125
64
+ c_del: 6.125
65
+ del_schedule: ${schedule}
66
+ ins_schedule: ${schedule}
67
+ sub_schedule:
68
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
69
+ shift: 1.5
70
+ sub_e_schedule:
71
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
72
+ shift: 1.5
73
+
74
+ datamodule:
75
+ _target_: morph.dataset.core.lightning_datamodule.LightningDataModule
76
+ # tokens and distributions will be passed from preprocessing in run.py
77
+
78
+ vocab: ${data.vocab}
79
+ distributions: null
80
+ representation: ${representation}
81
+
82
+ datasets:
83
+ train:
84
+ _target_: ${data.dataset_target}
85
+ root: ${data.preprocessing.root}
86
+ preprocess_workers: ${data.preprocess_workers}
87
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
88
+ hydrogen_mode: ${data.hydrogen_mode}
89
+ source_dir: ${data.source_dir}
90
+ max_atoms: ${data.max_atoms}
91
+ allowed_atom_symbols: ${data.allowed_atom_symbols}
92
+ vocab: ${data.vocab}
93
+ distributions: null
94
+
95
+ val:
96
+ - _target_: ${data.dataset_target}
97
+ root: ${data.preprocessing.root}
98
+ preprocess_workers: ${data.preprocess_workers}
99
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
100
+ hydrogen_mode: ${data.hydrogen_mode}
101
+ source_dir: ${data.source_dir}
102
+ max_atoms: ${data.max_atoms}
103
+ allowed_atom_symbols: ${data.allowed_atom_symbols}
104
+ vocab: ${data.vocab}
105
+ distributions: null
106
+
107
+ test:
108
+ - _target_: ${data.dataset_target}
109
+ root: ${data.preprocessing.root}
110
+ preprocess_workers: ${data.preprocess_workers}
111
+ preprocess_chunk_size: ${data.preprocess_chunk_size}
112
+ hydrogen_mode: ${data.hydrogen_mode}
113
+ source_dir: ${data.source_dir}
114
+ max_atoms: ${data.max_atoms}
115
+ allowed_atom_symbols: ${data.allowed_atom_symbols}
116
+ vocab: ${data.vocab}
117
+ distributions: null
118
+
119
+ interpolator: ${data.interpolator}
120
+
121
+ rotate: ${model.rotate}
122
+ n_augmentations: ${model.n_augmentations}
123
+
124
+ num_workers:
125
+ train: 10
126
+ val: 0
127
+ test: 0
128
+
129
+ batch_size:
130
+ train: 320
131
+ val: 128
132
+ test: 64
133
+
134
+ n_atoms_strategy: ${data.n_atoms_strategy}
135
+ uniform_n_atoms_padding: ${data.uniform_n_atoms_padding}
136
+ time_dist: ${model.module.time_dist}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/qm9/qm9.yaml ADDED
@@ -0,0 +1,106 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ## QM9 data and preprocessing configuration.
2
+
3
+ dataset_name: qm9
4
+ root_path: ${oc.env:PROJECT_ROOT}/data
5
+ preprocess_workers: 0
6
+
7
+ n_atoms_strategy: "uniform" # "approx", "empirical", "learned"
8
+ # Expand both ends of the empirical count support for the uniform flow source.
9
+ # QM9's minimum is already the hard lower bound of 3, so positive padding is
10
+ # necessarily asymmetric; set to 0 for an unpadded 3--29 diagnostic prior.
11
+ uniform_n_atoms_padding: 5
12
+ optimal_transport: "equivariant"
13
+ # Physical Angstroms (converted to normalized coordinates internally). Insertion
14
+ # bridge noise vanishes as (1-t); move noise is the persistent training-time
15
+ # jitter floor for matched atoms and insertions that are already present.
16
+ ins_noise_scale: 0.35
17
+ move_noise_scale: 0.3
18
+
19
+ # QM9 contains only neutral molecules; charged species are treated as invalid.
20
+ allow_charged: false
21
+
22
+
23
+ # Preprocessing configuration - computes tokens from data and distributions from training dataset only
24
+ preprocessing:
25
+ _target_: morph.dataset.core.preprocessing.Preprocessing
26
+ _recursive_: false
27
+ root: ${data.root_path}/qm9
28
+ train_dataset: ${data.datamodule.datasets.train}
29
+ # Canonical (representation-agnostic) cache. One preprocessing per dataset;
30
+ # representation-specific projection happens at runtime in the wrapper.
31
+ atom_tokens_path: ${data.preprocessing.root}/processed/atom_tokens.txt
32
+ edge_tokens_path: ${data.preprocessing.root}/processed/edge_tokens.txt
33
+ charge_tokens_path: ${data.preprocessing.root}/processed/charge_tokens.txt
34
+ distributions_path: ${data.preprocessing.root}/processed/distributions.pt
35
+
36
+ vocab: None
37
+
38
+ interpolator:
39
+ _target_: morph.flow_matching.interpolation.Interpolator
40
+ distributions: None
41
+ optimal_transport: ${data.optimal_transport}
42
+ ins_noise_scale: ${data.ins_noise_scale}
43
+ move_noise_scale: ${data.move_noise_scale}
44
+ # OT matching (cost always squared-Euclidean; move cost in Angstrom). c_move
45
+ # scales the squared move distance (A^2); c_ins/c_del/c_sub are fixed A^2 costs,
46
+ # directly comparable. At 6.125 each, delete+insert costs 3.5^2, so atoms
47
+ # farther than 3.5 A apart are left unmatched by default.
48
+ pre_align_iters: 2 # equivariant-OT inner loop (Hungarian+Kabsch) iters
49
+ c_move: 1.0
50
+ c_sub: 0.0
51
+ c_ins: 6.125
52
+ c_del: 6.125
53
+ del_schedule: ${schedule}
54
+ ins_schedule: ${schedule}
55
+ sub_schedule:
56
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
57
+ shift: 1.5
58
+ sub_e_schedule:
59
+ _target_: morph.flow_matching.schedules.SmoothstepSchedule
60
+ shift: 1.5
61
+
62
+ datamodule:
63
+ _target_: morph.dataset.core.lightning_datamodule.LightningDataModule
64
+ # tokens and distributions will be passed from preprocessing in run.py
65
+
66
+ vocab: ${data.vocab}
67
+ distributions: None
68
+ representation: ${representation}
69
+
70
+ datasets:
71
+ train:
72
+ _target_: morph.dataset.unconditional.qm9.FlowMatchingQM9Dataset
73
+ root: ${data.root_path}/qm9
74
+ vocab: ${data.vocab}
75
+ distributions: None
76
+
77
+ val:
78
+ - _target_: morph.dataset.unconditional.qm9.FlowMatchingQM9Dataset
79
+ root: ${data.root_path}/qm9
80
+ vocab: ${data.vocab}
81
+ distributions: None
82
+
83
+ test:
84
+ - _target_: morph.dataset.unconditional.qm9.FlowMatchingQM9Dataset
85
+ root: ${data.root_path}/qm9
86
+ vocab: ${data.vocab}
87
+ distributions: None
88
+
89
+ interpolator: ${data.interpolator}
90
+
91
+ rotate: ${model.rotate}
92
+ n_augmentations: ${model.n_augmentations}
93
+
94
+ num_workers:
95
+ train: 10
96
+ val: 0
97
+ test: 0
98
+
99
+ batch_size:
100
+ train: 1024
101
+ val: 128
102
+ test: 32
103
+
104
+ n_atoms_strategy: ${data.n_atoms_strategy}
105
+ uniform_n_atoms_padding: ${data.uniform_n_atoms_padding}
106
+ time_dist: ${model.module.time_dist}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/data/qm9/scaffold_decoration.yaml ADDED
@@ -0,0 +1,32 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # QM9 data config for the **scaffold decoration** task.
2
+ #
3
+ # Source is the selected scaffold of each target; target is the full molecule.
4
+ # The model learns to grow the molecule by inserting decoration atoms
5
+ # while denoising the retained scaffold.
6
+ #
7
+ # Inherits from configs/data/qm9/qm9.yaml and overrides:
8
+ # - datamodule._target_ → ScaffoldLightningDataModule
9
+ #
10
+ defaults:
11
+ - /data/qm9/qm9@_here_
12
+ - _self_
13
+
14
+ scaffold_groups_dir: ${data.root_path}/qm9/processed/scaffold_groups
15
+ scaffold_kind: rgroup # murcko, rgroup (BRICS), heavy (generate H), linker
16
+ # Physical Angstroms, per coordinate axis. Used only by training interpolation.
17
+ jitter_scale: 0.1
18
+ interpolator:
19
+ jitter_scale: ${data.jitter_scale}
20
+ # Exhaustive evaluation when eval_panel=null; training always draws randomly.
21
+ enumerate_slicings: false
22
+ eval_panel:
23
+ directory: ${data.scaffold_groups_dir}/eval_panels
24
+ n_scaffolds: 512
25
+ samples_per_scaffold: 8
26
+ seed: 42
27
+ datamodule:
28
+ _target_: morph.dataset.conditional.scaffold.datamodule.ScaffoldLightningDataModule
29
+ scaffold_groups_dir: ${data.scaffold_groups_dir}
30
+ scaffold_kind: ${data.scaffold_kind}
31
+ enumerate_slicings: ${data.enumerate_slicings}
32
+ eval_panel: ${data.eval_panel}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/default.yaml ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ defaults:
2
+ - _self_
3
+ # Single-knob presets bundle
4
+ # `data` + `embeddings` (with dataset-tuned ExtrapolatableCount params)
5
+ # plus the shared `core` group selections. Override individual groups as
6
+ # usual, e.g. `defaults=geom/geom model=backbones/dit embeddings=qm9`.
7
+ - defaults: qm9/qm9
8
+
9
+ # What the model trains on. Datasets declare their CAPABILITIES; run.py validates
10
+ # the requested mode against them before preprocessing.
11
+ # pointcloud — x, a only (charges and edges are dummy singletons)
12
+ # charged_pointcloud — x, a, c (edges are dummy singletons)
13
+ # geometric_graph — x, a, edge_index, e (charges are dummy neutral)
14
+ # molecule — full chemistry (current behavior)
15
+ representation: molecule
16
+ seed: 42
17
+
18
+ # Run directory: <root>/<dataset>/<cfg_method>/<date>/<wandb_id>. Root is
19
+ # MORPH_RUN_ROOT (scratch, set in .env) so every run's outputs/checkpoints/wandb
20
+ # land off-repo; falls back to <cwd>/outputs for ad-hoc runs with no env set.
21
+ # wandb id is MORPH_RUN_ID (same as WANDB_RUN_ID). Set in the shell before
22
+ # torchrun, or set automatically for single-process runs in run.py before Hydra composes.
23
+ hydra:
24
+ run:
25
+ dir: ${oc.env:MORPH_RUN_ROOT,${hydra:runtime.cwd}/outputs}/${hydra:runtime.choices.data}/${now:%Y-%m-%d}/${oc.env:MORPH_RUN_ID}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/chembl3d.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - core
4
+ - /data: chembl3d/chembl3d
5
+ - /embeddings: geom
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/implicit_h.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/core
4
+ - /data: chembl3d/implicit_h
5
+ - /embeddings: geom
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/implicit_h_2x4090.yaml ADDED
@@ -0,0 +1,10 @@
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/chembl3d/train_2x4090
4
+ - override /data: chembl3d/max100_implicit_h
5
+ - _self_
6
+
7
+ # Train from scratch: the atom vocabulary/statistics differ from the H model.
8
+ # Preserve global batch 640, eval every epoch, both checkpoint callbacks every 5.
9
+ logging:
10
+ project: morph-chembl3d-implicit-h
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/chembl3d/train_2x4090.yaml ADDED
@@ -0,0 +1,40 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/core
4
+ - /data: chembl3d/max100
5
+ - /embeddings: geom
6
+ - _self_
7
+
8
+ # Profiled on max100 ChEMBL: 2 GPUs * 320 * 1 = 640 effective global batch.
9
+ data:
10
+ datamodule:
11
+ train_prefetch_factor: 1
12
+ batch_size:
13
+ train: 320
14
+ val: 32
15
+ test: 32
16
+ num_workers:
17
+ train: 20
18
+ val: 2
19
+ test: 2
20
+
21
+ trainer:
22
+ trainer:
23
+ accelerator: gpu
24
+ devices: 2
25
+ accumulate_grad_batches: 1
26
+ max_epochs: -1
27
+ check_val_every_n_epoch: 1
28
+ num_val_samples: 4096
29
+ # Validation runs during fit; leave wall-clock headroom for final checkpointing.
30
+ do_validate: false
31
+ do_predict: false
32
+ final_checkpoint_path: final.ckpt
33
+
34
+ callbacks:
35
+ model_checkpoints:
36
+ every_n_epochs: 5
37
+ every_n_epochs_checkpoint:
38
+ every_n_epochs: 5
39
+ save_top_k: -1
40
+ save_last: link
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/core.yaml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ # Shared default groups that don't change between QM9 / GEOM runs.
3
+ # Dataset-specific presets (defaults/qm9/qm9.yaml, defaults/geom/geom.yaml) inherit
4
+ # from this and only add `data` + `embeddings`.
5
+ defaults:
6
+ - /heads: head
7
+ - /insertion_head: unimodal
8
+ - /model: backbones/dit
9
+ - /schedule: smoothstep
10
+ - /trainer: trainer
11
+ - /callbacks: callbacks
12
+ - /logging: wandb_logger
13
+ - /integrator: integrator
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/crossdocked/finetune.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/core
4
+ - /data: crossdocked/finetune
5
+ - /embeddings: geom
6
+ - override /model: finetune/pocket_finetune
7
+ - _self_
8
+
9
+ trainer:
10
+ trainer:
11
+ # ReplaySampler already partitions its stream between DDP ranks.
12
+ use_distributed_sampler: false
13
+ check_val_every_n_epoch: 1
14
+
15
+ logging:
16
+ project: morph-crossdocked-finetune
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/crossdocked/scaffold_decoration.yaml ADDED
@@ -0,0 +1,9 @@
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/crossdocked/finetune
4
+ - override /data: crossdocked/scaffold_decoration
5
+ - override /model: finetune/scaffold_pocket_finetune
6
+ - _self_
7
+
8
+ logging:
9
+ project: morph-crossdocked-scaffold-finetune
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/crossdocked/scaffold_decoration_2x4090.yaml ADDED
@@ -0,0 +1,51 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/crossdocked/scaffold_decoration
4
+ - _self_
5
+
6
+ data:
7
+ pretrain_root: ${data.root_path}/chembl3d_max100_implicit_h
8
+ pretrain_max_atoms: 100
9
+ finetune_fraction: 0.5
10
+ # 400 optimizer updates per epoch at global batch 256.
11
+ train_epoch_size: 102400
12
+ datamodule:
13
+ train_prefetch_factor: 1
14
+ batch_size:
15
+ train: 32
16
+ val: 8
17
+ test: 8
18
+ num_workers:
19
+ train: 6
20
+ val: 2
21
+ test: 2
22
+
23
+ model:
24
+ module:
25
+ # LoRA, conditioning modules, and complete prediction heads train with replay.
26
+ # Peak Muon/Adam rates are inherited from scaffold_pocket_finetune.
27
+ optimizer_config:
28
+ scheduler:
29
+ warmup_steps: 1000
30
+ total_steps: 40000
31
+
32
+ trainer:
33
+ trainer:
34
+ accelerator: gpu
35
+ devices: 2
36
+ accumulate_grad_batches: 4
37
+ max_epochs: 100
38
+ check_val_every_n_epoch: 5
39
+ # Evaluate the complete repeated panel; truncation biases conditional scores.
40
+ num_val_samples: null
41
+ do_validate: false
42
+ do_predict: false
43
+ final_checkpoint_path: final.ckpt
44
+
45
+ callbacks:
46
+ model_checkpoints:
47
+ every_n_epochs: 5
48
+ every_n_epochs_checkpoint:
49
+ every_n_epochs: 5
50
+ save_top_k: -1
51
+ save_last: link
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/geom.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ # GEOM preset: shared core + GEOM data and embeddings.
3
+ defaults:
4
+ - core
5
+ - /data: geom/geom
6
+ - /embeddings: geom
7
+ - _self_
8
+
9
+ # Evaluate every epoch; keep every fifth completed epoch for releases/resuming.
10
+ trainer:
11
+ trainer:
12
+ check_val_every_n_epoch: 1
13
+
14
+ callbacks:
15
+ model_checkpoints:
16
+ every_n_epochs: 5
17
+ every_n_epochs_checkpoint:
18
+ every_n_epochs: 5
19
+ save_top_k: -1
20
+ save_last: link
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/implicit_h.yaml ADDED
@@ -0,0 +1,5 @@
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/core
4
+ - /data: geom/implicit_h
5
+ - /embeddings: geom
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/scaffold_decoration.yaml ADDED
@@ -0,0 +1,39 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ # GEOM-Drugs **scaffold decoration** fine-tune preset.
3
+ #
4
+ # Mirrors defaults/qm9/scaffold_decoration.yaml on GEOM-Drugs. Bare-scaffold
5
+ # source -> full-molecule target: the model learns to grow a molecule by
6
+ # inserting decoration atoms while allowing scaffold coordinates to move.
7
+ #
8
+ # Differences from the QM9 scaffold preset:
9
+ # - data -> geom/scaffold_decoration (GEOM base datasets)
10
+ # - embeddings -> geom
11
+ #
12
+ # Reuses the scaffold_finetune model config (loads the pretrained unconditional
13
+ # DiT into the clean lane + adds the node/edge conditioning and the gated
14
+ # cross-attn blocks).
15
+ #
16
+ # Pair with a GEOM unconditional DiT checkpoint via
17
+ # ``model.module.pretrained_ckpt=...``.
18
+ defaults:
19
+ - /defaults/core
20
+ - /data: geom/scaffold_decoration
21
+ - /embeddings: geom
22
+ - override /model: finetune/scaffold_finetune
23
+ - _self_
24
+
25
+ # Evaluate each completed epoch; retain checkpoints every five epochs.
26
+ trainer:
27
+ trainer:
28
+ check_val_every_n_epoch: 1
29
+
30
+ callbacks:
31
+ model_checkpoints:
32
+ every_n_epochs: 5
33
+ every_n_epochs_checkpoint:
34
+ every_n_epochs: 5
35
+ save_top_k: -1
36
+ save_last: link
37
+
38
+ logging:
39
+ project: morph-${data.dataset_name}-scaffold-decoration
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/scaffold_decoration_2x4090.yaml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/geom/scaffold_decoration
4
+ - _self_
5
+
6
+ # Starting point for future GEOM scaffold runs on two 24-GiB GPUs.
7
+ # The epoch-100 Murcko runs used 160/GPU with accumulation 2; reported GPU
8
+ # memory usage was only 33% (full) / 29% (LoRA). Double the microbatch while
9
+ # preserving the effective batch: 2 GPUs * 320 * 1 = 640.
10
+ # Profile peak memory and throughput before choosing still larger batches.
11
+ data:
12
+ datamodule:
13
+ train_prefetch_factor: 1
14
+ batch_size:
15
+ train: 320
16
+ val: 32
17
+ test: 32
18
+ num_workers:
19
+ train: 20
20
+ val: 0
21
+ test: 0
22
+
23
+ trainer:
24
+ trainer:
25
+ accelerator: gpu
26
+ devices: 2
27
+ accumulate_grad_batches: 1
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/geom/scaffold_decoration_scratch_2x4090.yaml ADDED
@@ -0,0 +1,44 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ # Train GEOM-Drugs Murcko decoration from scratch, using the scaffold protocol.
3
+ defaults:
4
+ - /defaults/geom/scaffold_decoration_2x4090
5
+ - _self_
6
+
7
+ seed: 42
8
+
9
+ data:
10
+ scaffold_kind: murcko
11
+ jitter_scale: 0.1
12
+
13
+ model:
14
+ module:
15
+ pretrained_ckpt: null
16
+ freeze_pretrained_backbone: false
17
+ freeze_output_heads: false
18
+ freeze_keep_positions: false
19
+ exclude_scaffold_from_x_loss: false
20
+ optimizer_config:
21
+ optimizer:
22
+ lr: 1e-3
23
+ aux_optimizer:
24
+ lr: 1e-4
25
+ lora:
26
+ enabled: false
27
+ freeze_base: false
28
+
29
+ trainer:
30
+ trainer:
31
+ max_epochs: 50
32
+ check_val_every_n_epoch: 1
33
+ num_val_samples: 4096
34
+ enable_progress_bar: false
35
+ max_time:
36
+ hours: 47
37
+ checkpoint_path: null
38
+ final_checkpoint_path: final.ckpt
39
+ do_validate: false
40
+ do_predict: false
41
+
42
+ logging:
43
+ group: geom-murcko-scratch
44
+ tags: [geom, explicit-h, murcko, scaffold-decoration, scratch, full-model, movable, jitter-0.1A]
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/qm9/qm9.yaml ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ # QM9 preset: shared core + QM9 data and embeddings.
3
+ defaults:
4
+ - /defaults/core
5
+ - /data: qm9/qm9
6
+ - /embeddings: qm9
7
+ - _self_
8
+
9
+ trainer:
10
+ trainer:
11
+ check_val_every_n_epoch: 1
12
+
13
+ # Keep the full 25-epoch checkpoint history for model releases and resuming.
14
+ callbacks:
15
+ model_checkpoints:
16
+ every_n_epochs: 25
17
+ every_n_epochs_checkpoint:
18
+ every_n_epochs: 25
19
+ save_top_k: -1
20
+ save_last: link
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/qm9/scaffold_decoration.yaml ADDED
@@ -0,0 +1,27 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ # QM9 **scaffold decoration** fine-tune preset.
3
+ #
4
+ # Bare-scaffold source → full-molecule target. The model learns to grow
5
+ # a molecule by inserting decoration atoms while scaffold coordinates may move.
6
+ defaults:
7
+ - core
8
+ - /data: qm9/scaffold_decoration
9
+ - /embeddings: qm9
10
+ - override /model: finetune/scaffold_finetune
11
+ - _self_
12
+
13
+ # Evaluate each completed epoch; retain checkpoints every five epochs.
14
+ trainer:
15
+ trainer:
16
+ check_val_every_n_epoch: 1
17
+
18
+ callbacks:
19
+ model_checkpoints:
20
+ every_n_epochs: 5
21
+ every_n_epochs_checkpoint:
22
+ every_n_epochs: 5
23
+ save_top_k: -1
24
+ save_last: link
25
+
26
+ logging:
27
+ project: morph-${data.dataset_name}-scaffold-decoration
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/defaults/qm9/train_2x4090.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # @package _global_
2
+ defaults:
3
+ - /defaults/qm9/qm9
4
+ - _self_
5
+
6
+ # Preserve the established QM9 effective batch: 2 GPUs * 1024 * 2 = 4096.
7
+ data:
8
+ datamodule:
9
+ train_prefetch_factor: 1
10
+ batch_size:
11
+ train: 1024
12
+ val: 128
13
+ test: 32
14
+ num_workers:
15
+ train: 20
16
+ val: 2
17
+ test: 2
18
+
19
+ trainer:
20
+ trainer:
21
+ accelerator: gpu
22
+ devices: 2
23
+ accumulate_grad_batches: 2
24
+ max_epochs: 3000
25
+ check_val_every_n_epoch: 1
26
+ num_val_samples: 4096
27
+ do_validate: false
28
+ do_predict: false
29
+ final_checkpoint_path: final.ckpt
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/embeddings/geom.yaml ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Shared embedding stack tuned for GEOM-Drugs.
2
+ # - node_count_embedding uses ExtrapolatableCountEmbedding with periods/max_count
3
+ # scaled up for the much larger atom counts in GEOM-Drugs.
4
+
5
+ atom_type_embedding:
6
+ _target_: morph.model.layers.embedding.Embedding
7
+ num_embeddings: ${len:${data.vocab.atom_tokens}}
8
+ embedding_dim: 64
9
+ out_dim: 128
10
+
11
+ edge_type_embedding:
12
+ _target_: morph.model.layers.embedding.Embedding
13
+ num_embeddings: ${len:${data.vocab.edge_tokens}}
14
+ embedding_dim: 64
15
+ out_dim: 128
16
+
17
+ time_embedding:
18
+ _target_: morph.model.layers.embedding.TimeEmbedding
19
+ embedding_dim: 128
20
+ out_dim: 128
21
+
22
+ node_count_embedding:
23
+ _target_: morph.model.layers.embedding.ExtrapolatableCountEmbedding
24
+ embedding_dim: 64
25
+ out_dim: 128
26
+ min_period: 128.0
27
+ max_period: 1024.0
28
+ max_count: 96.0
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/embeddings/qm9.yaml ADDED
@@ -0,0 +1,29 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Shared embedding stack tuned for QM9.
2
+ # - node_count_embedding uses ExtrapolatableCountEmbedding so the model can
3
+ # generalise slightly outside the training atom-count support.
4
+ # - Defaults sized for QM9 (training counts in [3, 29]).
5
+
6
+ atom_type_embedding:
7
+ _target_: morph.model.layers.embedding.Embedding
8
+ num_embeddings: ${len:${data.vocab.atom_tokens}}
9
+ embedding_dim: 64
10
+ out_dim: 128
11
+
12
+ edge_type_embedding:
13
+ _target_: morph.model.layers.embedding.Embedding
14
+ num_embeddings: ${len:${data.vocab.edge_tokens}}
15
+ embedding_dim: 64
16
+ out_dim: 128
17
+
18
+ time_embedding:
19
+ _target_: morph.model.layers.embedding.TimeEmbedding
20
+ embedding_dim: 128
21
+ out_dim: 128
22
+
23
+ node_count_embedding:
24
+ _target_: morph.model.layers.embedding.ExtrapolatableCountEmbedding
25
+ embedding_dim: 64
26
+ out_dim: 128
27
+ min_period: 64.0
28
+ max_period: 512.0
29
+ max_count: 32.0
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/heads/head.yaml ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Standard molecular DiT readouts. The insertion-content head is an independent
2
+ # Hydra choice (``insertion_head=unimodal`` by default, or ``=gmm``).
3
+
4
+ heads:
5
+ _target_: morph.model.layers.heads.MultiHeadModule
6
+ output_init_std: ${model.head_initialization.output_std}
7
+ independent_adapters: ${oc.select:model.head_independent_adapters,false}
8
+
9
+ heads_configs:
10
+ graph_aggregation: mean
11
+
12
+ node_heads:
13
+ atom_type_head:
14
+ input_dim: ${model.head_node_dim}
15
+ output_dim: ${len:${data.vocab.atom_tokens}}
16
+ adapter_dim: ${model.head_node_adapter_dim}
17
+ group: state
18
+
19
+ charge_head:
20
+ input_dim: ${model.head_node_dim}
21
+ output_dim: ${len:${data.vocab.charge_tokens}}
22
+ adapter_dim: ${model.head_node_adapter_dim}
23
+ group: state
24
+
25
+ ins_rate_head:
26
+ input_dim: ${model.head_node_dim}
27
+ output_dim: 1
28
+ adapter_dim: ${model.head_node_adapter_dim}
29
+ group: edit
30
+ initial_rate: ${model.head_initialization.ins_rate}
31
+
32
+ do_del_head:
33
+ input_dim: ${model.head_node_dim}
34
+ output_dim: 1
35
+ adapter_dim: ${model.head_node_adapter_dim}
36
+ group: edit
37
+ initial_probability: ${model.head_initialization.do_del_probability}
38
+
39
+ do_sub_a_head:
40
+ input_dim: ${model.head_node_dim}
41
+ output_dim: 1
42
+ adapter_dim: ${model.head_node_adapter_dim}
43
+ group: edit
44
+ initial_probability: ${model.head_initialization.do_sub_a_probability}
45
+
46
+ edge_heads:
47
+ edge_type_head:
48
+ input_dim: ${model.head_edge_dim}
49
+ output_dim: ${len:${data.vocab.edge_tokens}}
50
+ adapter_dim: ${model.head_edge_adapter_dim}
51
+ group: state
52
+
53
+ do_sub_e_head:
54
+ input_dim: ${model.head_edge_dim}
55
+ output_dim: 1
56
+ adapter_dim: ${model.head_edge_adapter_dim}
57
+ group: state
58
+ initial_probability: ${model.head_initialization.do_sub_e_probability}
59
+
60
+ ins_edge_head:
61
+ _target_: morph.model.layers.heads.InsertionEdgeHead
62
+ _recursive_: false
63
+ hidden_dim: ${model.head_node_dim}
64
+ readout_dim: ${model.head_insertion_edge_dim}
65
+ output_init_std: ${model.head_initialization.output_std}
66
+ n_edge_types: ${len:${data.vocab.edge_tokens}}
67
+ n_atom_types: ${len:${data.vocab.atom_tokens}}
68
+ n_charge_types: ${len:${data.vocab.charge_tokens}}
69
+ rbf_embedding_args:
70
+ _target_: morph.model.layers.embedding.RBFEmbedding
71
+ num_rbf: 128
72
+ rbf_dmax: 3.5
73
+ out_dim: 64
74
+ dropout: 0.0
75
+ trainable: false
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/insertion_head/gmm.yaml ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Optional mixture-density insertion-content head.
2
+ mode: gmm
3
+ dimension: 3
4
+ num_components: 10
5
+
6
+ model:
7
+ _target_: morph.model.layers.heads.GMMInsertionHead
8
+ hidden_dim: ${model.head_node_dim}
9
+ readout_dim: ${model.head_insertion_adapter_dim}
10
+ output_init_std: ${model.head_initialization.output_std}
11
+ num_components: ${insertion_head.num_components}
12
+ sigma_min: 0.05
13
+ sigma_max: 2.0
14
+ sigma_init: 0.5
15
+ N_a: ${len:${data.vocab.atom_tokens}}
16
+ N_c: ${len:${data.vocab.charge_tokens}}
geom/scaffold_murcko_substituent_lora_r16/runtime/configs/insertion_head/unimodal.yaml ADDED
@@ -0,0 +1,13 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Default insertion-content head: one nearest pending atom per spawning node.
2
+ mode: unimodal
3
+ dimension: 3
4
+ num_components: 1
5
+
6
+ model:
7
+ _target_: morph.model.layers.heads.UnimodalInsertionHead
8
+ hidden_dim: ${model.head_node_dim}
9
+ readout_dim: ${model.head_insertion_adapter_dim}
10
+ output_init_std: ${model.head_initialization.output_std}
11
+ independent_adapters: ${oc.select:model.head_independent_adapters,false}
12
+ N_a: ${len:${data.vocab.atom_tokens}}
13
+ N_c: ${len:${data.vocab.charge_tokens}}