maltefranke commited on
Commit
3bba754
·
verified ·
1 Parent(s): d0332f1

Document pinned inference and reproducible training recipes; add paper citation

Browse files
Files changed (3) hide show
  1. CITATION.cff +35 -0
  2. README.md +103 -1
  3. reproduction/README.md +178 -0
CITATION.cff ADDED
@@ -0,0 +1,35 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ cff-version: 1.2.0
2
+ message: "If you use Morph, please cite the accompanying paper."
3
+ type: software
4
+ title: Morph
5
+ authors:
6
+ - family-names: Franke
7
+ given-names: Malte
8
+ - family-names: Schmid
9
+ given-names: Stefan P.
10
+ - family-names: Ivkovic
11
+ given-names: Zarko
12
+ - family-names: Jorner
13
+ given-names: Kjell
14
+ - family-names: Krause
15
+ given-names: Andreas
16
+ repository-code: "https://github.com/maltefranke/morph-chem"
17
+ preferred-citation:
18
+ type: generic
19
+ title: "Generative Molecular Morphing for Flexible-Size Design via Unbalanced Optimal Transport"
20
+ authors:
21
+ - family-names: Franke
22
+ given-names: Malte
23
+ - family-names: Schmid
24
+ given-names: Stefan P.
25
+ - family-names: Ivkovic
26
+ given-names: Zarko
27
+ - family-names: Jorner
28
+ given-names: Kjell
29
+ - family-names: Krause
30
+ given-names: Andreas
31
+ year: 2026
32
+ url: "https://arxiv.org/abs/2606.07239"
33
+ identifiers:
34
+ - type: other
35
+ value: "arXiv:2606.07239"
README.md CHANGED
@@ -7,6 +7,7 @@ tags:
7
  - chembl3d
8
  - geom-drugs
9
  - 3d
 
10
  ---
11
 
12
  # Morph: 3D molecular generation
@@ -16,6 +17,8 @@ Unconditional molecular generation checkpoints trained on **ChEMBL3D** and
16
  needed for inference. No training dataset is required to load a model or generate
17
  molecules.
18
 
 
 
19
  ## Available checkpoints
20
 
21
  | Dataset | Model name | Hydrogen representation | Completed epochs | Files |
@@ -42,7 +45,8 @@ discovery for all models in the table. Install it in a Python environment:
42
  ```bash
43
  python -m pip install 'huggingface_hub>=1.0,<2'
44
  hf auth login
45
- hf download maltefranke/morph --local-dir morph-runtime \
 
46
  --include 'geom/explicit_h/runtime/*'
47
  python -m pip install ./morph-runtime/geom/explicit_h/runtime
48
  morph models list
@@ -132,6 +136,86 @@ The root `sample.py` remains available as the archived ChEMBL3D sampling entry
132
  point with its corresponding bundled runtimes. The shared interface above
133
  supports all three published model variants.
134
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
135
  ## Hydrogen handling and training data
136
 
137
  | Dataset | Training filter | Representation |
@@ -181,3 +265,21 @@ historical model card. GEOM has its own
181
  [bundle manifest](https://huggingface.co/maltefranke/morph/blob/b839af108eaade757a5d2d668d05498e8c733c1e/geom/explicit_h/manifest.json).
182
  Original training configs contain historical cluster paths; use `config.yaml`
183
  through the inference loader for portable generation.
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
7
  - chembl3d
8
  - geom-drugs
9
  - 3d
10
+ - arxiv:2606.07239
11
  ---
12
 
13
  # Morph: 3D molecular generation
 
17
  needed for inference. No training dataset is required to load a model or generate
18
  molecules.
19
 
20
+ See our [paper](https://arxiv.org/abs/2606.07239), *Generative Molecular Morphing for Flexible-Size Design via Unbalanced Optimal Transport*.
21
+
22
  ## Available checkpoints
23
 
24
  | Dataset | Model name | Hydrogen representation | Completed epochs | Files |
 
45
  ```bash
46
  python -m pip install 'huggingface_hub>=1.0,<2'
47
  hf auth login
48
+ hf download maltefranke/morph --revision da1c241d83f2628b172fc510e1bc724ef4429db2 \
49
+ --local-dir morph-runtime \
50
  --include 'geom/explicit_h/runtime/*'
51
  python -m pip install ./morph-runtime/geom/explicit_h/runtime
52
  morph models list
 
136
  point with its corresponding bundled runtimes. The shared interface above
137
  supports all three published model variants.
138
 
139
+ ## Reproducibility
140
+
141
+ ### Generate with a fixed release
142
+
143
+ Pin the **runtime code and dependencies** as well as the **model bundle**.
144
+ This installs the archived shared inference runtime, independently of changes
145
+ to this repository or the model catalog. Install [uv](https://docs.astral.sh/uv/)
146
+ and authenticate with an account that can access the model repository:
147
+
148
+ ```bash
149
+ uv tool install 'huggingface_hub==1.33.0'
150
+ hf auth login
151
+ hf download maltefranke/morph \
152
+ --revision da1c241d83f2628b172fc510e1bc724ef4429db2 \
153
+ --include 'geom/explicit_h/runtime/*' --local-dir morph-runtime
154
+ cd morph-runtime/geom/explicit_h/runtime
155
+ uv sync --frozen --no-dev --python 3.13.7
156
+ source .venv/bin/activate
157
+
158
+ # Run on a compute worker. Save all attempts and their validity flags.
159
+ MORPH_COMPILE=0 MORPH_ATTENTION_BACKEND=sdpa MORPH_FLASH_ATTN=0 \
160
+ morph generate --model maltefranke/morph --subfolder geom/explicit_h \
161
+ --revision b839af108eaade757a5d2d668d05498e8c733c1e --epoch 50 \
162
+ --device cuda --ema --steps 100 --seed 42 --batch 64 --batch-size 16 \
163
+ --save-trajs --output geom-e50-seed42.sdf
164
+ uv pip freeze > environment.txt
165
+ nvidia-smi --query-gpu=name,driver_version --format=csv > gpu.csv
166
+ ```
167
+
168
+ Choose another published run by changing these three options:
169
+
170
+ | `--subfolder` | `--revision` | `--epoch` |
171
+ | --- | --- | --- |
172
+ | `chembl3d/explicit_h` | `ab9bea92f499f03450ebc99459fce04461504a41` | `200`, `300`, `400` |
173
+ | `chembl3d/implicit_h` | `ab9bea92f499f03450ebc99459fce04461504a41` | `100`, `200`, `300` |
174
+ | `geom/explicit_h` | `b839af108eaade757a5d2d668d05498e8c733c1e` | `50` |
175
+
176
+ Keep the seed, batch size, integration steps, EMA selection, precision, attention
177
+ backend and software/hardware environment fixed when comparing samples. A seed
178
+ alone does not guarantee identical samples across GPU models or library versions.
179
+ The checkpoint SHA-256 hashes are in each bundle's `metadata.json`.
180
+
181
+ ### Repeat a training recipe
182
+
183
+ The [training archives](https://huggingface.co/maltefranke/morph/tree/main/reproduction)
184
+ contain the original source (including `run.py`), saved training config, launcher,
185
+ dependency versions, and a portable `train.sh`. They use **2 RTX 4090s, seed 42,
186
+ and bf16 mixed precision**. Run installation and training in a compute allocation:
187
+
188
+ ```bash
189
+ # Start from a directory outside the inference environment above.
190
+ run=geom-explicit-h # Or chembl3d-explicit-h, chembl3d-implicit-h, qm9-explicit-h
191
+ hf download maltefranke/morph --revision d0332f1c022345956470bb948ada79e5702ed8dc \
192
+ "reproduction/$run.tar.gz" reproduction/SHA256SUMS --local-dir morph-training
193
+ cd morph-training/reproduction
194
+ sha256sum --ignore-missing -c SHA256SUMS
195
+ tar -xzf "$run.tar.gz"
196
+ cd "$run"
197
+ uv venv --python 3.13.7 .venv
198
+ uv pip install --python .venv/bin/python -r pyproject.toml \
199
+ -c reproduction/requirements-environment.txt
200
+ source .venv/bin/activate
201
+ export MORPH_DATASET_ROOT=/absolute/path/to/geom # Contains processed/
202
+ export MORPH_OUTPUT_DIR="$PWD/outputs/repeat-01"
203
+ bash train.sh
204
+ ```
205
+
206
+ All portable recipes evaluate every epoch and retain every fifth epoch; QM9
207
+ retains every **25th** epoch. The archives start fresh and stop at 400 epochs for
208
+ explicit-H ChEMBL3D, 300 for implicit-H ChEMBL3D, 50 for GEOM, or 3000 for QM9.
209
+ Override the stopping point with `bash train.sh trainer.trainer.max_epochs=100`.
210
+ QM9 currently has a training recipe but no published checkpoint.
211
+
212
+ These commands reproduce the documented setup, not an exact replay of every
213
+ historical run: explicit-H ChEMBL3D resumed at epoch 5 and GEOM at epoch 48;
214
+ the older explicit-H ChEMBL3D run also evaluated every five epochs. Data caches
215
+ and those intermediate resume checkpoints are not included in the archives.
216
+ See [reproducibility details](reproduction/README.md) for the data requirements,
217
+ original settings, environment provenance and resume instructions.
218
+
219
  ## Hydrogen handling and training data
220
 
221
  | Dataset | Training filter | Representation |
 
265
  [bundle manifest](https://huggingface.co/maltefranke/morph/blob/b839af108eaade757a5d2d668d05498e8c733c1e/geom/explicit_h/manifest.json).
266
  Original training configs contain historical cluster paths; use `config.yaml`
267
  through the inference loader for portable generation.
268
+
269
+ ## Citation
270
+
271
+ If you use Morph, please cite our [paper](https://arxiv.org/abs/2606.07239):
272
+
273
+ ```bibtex
274
+ @misc{franke2026generativemolecularmorphingflexiblesize,
275
+ title={Generative Molecular Morphing for Flexible-Size Design via Unbalanced Optimal Transport},
276
+ author={Malte Franke and Stefan P. Schmid and Zarko Ivkovic and Kjell Jorner and Andreas Krause},
277
+ year={2026},
278
+ eprint={2606.07239},
279
+ archivePrefix={arXiv},
280
+ primaryClass={cs.LG},
281
+ url={https://arxiv.org/abs/2606.07239},
282
+ }
283
+ ```
284
+
285
+ Machine-readable citation metadata is available in [CITATION.cff](CITATION.cff).
reproduction/README.md ADDED
@@ -0,0 +1,178 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ # Reproducing models and training runs
2
+
3
+ The [README](../README.md#reproducibility) contains complete commands for pinned
4
+ inference and training. The model repository is
5
+ [maltefranke/morph](https://huggingface.co/maltefranke/morph).
6
+
7
+ ## Published inference
8
+
9
+ Pin both the installed source and the checkpoint/assets. The shared inference
10
+ runtime at Hub revision `da1c241d83f2628b172fc510e1bc724ef4429db2` supports all
11
+ published models; its `uv.lock` pins dependencies. The README installs that
12
+ runtime with Python 3.13.7. Installing a different checkout of Morph changes the
13
+ implementation even when `load_pretrained(..., revision=...)` selects the same
14
+ weights: that argument pins the model bundle, not the installed Python code.
15
+
16
+ With that environment active, this is the Python equivalent of the pinned
17
+ GEOM example. Set the environment variables before importing Morph:
18
+
19
+ ```python
20
+ import os
21
+
22
+ os.environ["MORPH_COMPILE"] = "0"
23
+ os.environ["MORPH_ATTENTION_BACKEND"] = "sdpa"
24
+ os.environ["MORPH_FLASH_ATTN"] = "0"
25
+
26
+ from morph.inference import load_pretrained
27
+
28
+ model = load_pretrained(
29
+ "maltefranke/morph",
30
+ subfolder="geom/explicit_h",
31
+ revision="b839af108eaade757a5d2d668d05498e8c733c1e",
32
+ epoch=50,
33
+ device="cuda",
34
+ steps=100,
35
+ use_ema=True,
36
+ )
37
+ trajectories, validity, vocab, coordinate_std = model.generate(
38
+ n_mols=64, batch_size=16, seed=42, return_traj=True,
39
+ )
40
+ print("Model revision:", model.bundle.revision)
41
+ print("Valid attempts:", sum(validity), "/", len(validity))
42
+ ```
43
+
44
+ Direct repository IDs bypass catalog lookup. To use a short model name while
45
+ freezing discovery, pass a pinned catalog:
46
+
47
+ ```python
48
+ from morph.inference import load_catalog, load_pretrained
49
+
50
+ catalog = load_catalog(
51
+ repo_id="maltefranke/morph",
52
+ revision="377e8b52505d564df8529fe6fef2da9c77f57661",
53
+ )
54
+ model = load_pretrained(
55
+ "chembl3d-implicit-h", catalog=catalog, epoch=300,
56
+ device="cuda", steps=100, use_ema=True,
57
+ )
58
+ ```
59
+
60
+ Changing batch size can change random-number consumption and generated samples.
61
+ Record the generation count, batch size, seed, steps, EMA/current choice,
62
+ precision, backend, source revision, package versions, GPU and driver. The CLI's
63
+ SDF includes valid molecules only; `--save-trajs` preserves every attempt and
64
+ its validity flag. Coordinates are already in Angstrom.
65
+
66
+ Seeds and fixed releases provide controlled comparisons, but CUDA kernels and
67
+ changes to hardware, dependencies or precision can prevent bitwise equality.
68
+ The historical training configuration uses `deterministic: warn`, which is not
69
+ a guarantee that every operation has a deterministic implementation.
70
+
71
+ ## Training archives
72
+
73
+ Download from `reproduction/` at immutable Hub revision `d0332f1c022345956470bb948ada79e5702ed8dc`.
74
+ Each archive contains:
75
+
76
+ - The original `run.py`, `src/`, `configs/`, `pyproject.toml`, and `uv.lock`
77
+ where present in the original source snapshot. Original source files are
78
+ checked against their saved SHA-256 manifest.
79
+ - `reproduction/original_training_config.yaml` and
80
+ `reproduction/original_launcher.sbatch` for historical settings and paths.
81
+ For QM9, the config is from the fresh training run after its successful GPU
82
+ preflight; the launcher records the preflight reuse and allocation time limit.
83
+ - `configs/reproduce.yaml` and `train.sh` for portable fresh training.
84
+ - `reproduction/provenance.json`, `dataset_reference.json`,
85
+ `original_source_manifest.json`, and `manifest.json` for auditability.
86
+ - `reproduction/requirements-environment.txt`: version constraints captured
87
+ from the shared training environment on 2026-10-08. This is not a historical
88
+ per-job environment capture; Python 3.13.7 is the reference interpreter.
89
+
90
+ The archives are roughly 0.2–0.5 MB each and contain no datasets or weights.
91
+ `reproduction/SHA256SUMS` verifies the archives; each archive's manifest verifies
92
+ its files. Preserve the archive and immutable revision with your results.
93
+
94
+ The new launcher records package versions, GPU/driver information, requested
95
+ overrides, recipe provenance and data references in `MORPH_OUTPUT_DIR`.
96
+ Hydra records the effective configuration in `.hydra/`. Choose a new output
97
+ directory for each run, and retain these records alongside checkpoints.
98
+
99
+ | Archive | Batch per GPU | Accumulation | Effective batch | Original validation cadence | Portable validation / save cadence |
100
+ | --- | ---: | ---: | ---: | --- | --- |
101
+ | `chembl3d-explicit-h.tar.gz` | 320 | 1 | 640 | Every 5 epochs | Every 1 / 5 epochs |
102
+ | `chembl3d-implicit-h.tar.gz` | 320 | 1 | 640 | Every epoch | Every 1 / 5 epochs |
103
+ | `geom-explicit-h.tar.gz` | 320 | 1 | 640 | Every epoch | Every 1 / 5 epochs |
104
+ | `qm9-explicit-h.tar.gz` | 1024 | 2 | 4096 | Every epoch | Every 1 / 25 epochs |
105
+
106
+ All recipes use two RTX 4090 GPUs, seed 42, bf16 mixed precision, SDPA attention,
107
+ compiled networks and 4096 generation attempts per validation epoch. GEOM uses
108
+ Muon with CUDA graph execution; ChEMBL3D and QM9 use native Muon execution with
109
+ DDP compiler graph splitting disabled and compilation fallback enabled.
110
+ Environment flags are explicit in each archive's `reproduction/environment.sh`.
111
+
112
+ The portable configs preserve the saved model, optimizer, schedule and batch
113
+ settings. They change data/output paths, disable W&B, remove the historical
114
+ resume path and allocation timer, and use a finite epoch limit. They also apply
115
+ the current validation and checkpoint policy from the table. Those changes are
116
+ listed in `provenance.json`; the historical config is preserved separately.
117
+
118
+ ## Data needed for training
119
+
120
+ Set `MORPH_DATASET_ROOT` to a dataset directory containing `processed/`:
121
+
122
+ | Archive | Dataset directory | Selection / representation |
123
+ | --- | --- | --- |
124
+ | ChEMBL3D explicit H | `chembl3d_max100` | At most 100 total atoms, explicit H |
125
+ | ChEMBL3D implicit H | `chembl3d_max100_implicit_h` | Same source molecules, then remove H |
126
+ | GEOM explicit H | `geom` | At most 72 total atoms, explicit H |
127
+ | QM9 explicit H | `qm9` | Original QM9 split, explicit H |
128
+
129
+ The ChEMBL3D cap counts hydrogens **before removal**. Both variants have
130
+ 1,474,008 / 184,295 / 184,242 train/validation/test molecules. The implicit-H cache
131
+ also repairs stale cached charges and bond representations from the canonical
132
+ source SMILES, then recomputes heavy-atom statistics. It is not interchangeable
133
+ with the explicit-H cache. QM9 has 100,000 / 20,000 / 12,947 molecules.
134
+
135
+ Keep the original processed split files, their molecule/conformer order,
136
+ `*_smiles.txt` files, ordered vocabularies and `distributions.pt` together.
137
+ ChEMBL3D and GEOM use `<split>_data.lmdb/data.mdb`; QM9 uses `<split>.pt`.
138
+ Training uses the training SMILES for novelty metrics as well as the graph cache.
139
+
140
+ The launcher checks vocabulary/statistics SHA-256 hashes and processed split
141
+ file sizes before starting distributed training. **File sizes do not verify
142
+ dataset contents.** The historical release did not record complete dataset
143
+ content hashes, and these caches are not hosted in the model repository.
144
+ Rebuilding from upstream data therefore requires checking split membership,
145
+ conformer selection and ordering; matching an atom cap alone is insufficient.
146
+ For new datasets, use the repository's preprocessing workflow before a
147
+ distributed launch, and preserve the resulting cache. Run dataset hashing on
148
+ a CPU worker if you need a full content manifest for future comparisons.
149
+
150
+ ## Fresh training versus resuming
151
+
152
+ The explicit-H ChEMBL3D release resumed after 5 completed epochs. GEOM epoch 50
153
+ resumed from the best checkpoint after 48 completed epochs. Starting these
154
+ recipes from seed 42 reproduces their setup but does not replay that history.
155
+ The intermediate epoch-5 and epoch-48 checkpoints are not in the Hub release.
156
+ The implicit-H ChEMBL3D run and the QM9 recipe start fresh. QM9 has no published
157
+ checkpoint in this release.
158
+
159
+ To continue from an available full training checkpoint, pass its absolute path
160
+ and the desired **total** completed epoch count to the matching archive:
161
+
162
+ ```bash
163
+ bash train.sh \
164
+ "trainer.checkpoint_path='/absolute/path/to/epoch_050.ckpt'" \
165
+ trainer.trainer.max_epochs=100
166
+ ```
167
+
168
+ Use `trainer.checkpoint_path` to restore optimizer, scheduler, EMA and loop state;
169
+ loading inference weights alone does not resume training. Periodic checkpoint
170
+ filenames use Lightning's zero-based epoch index: index 49 is completed epoch
171
+ 50. A `final.ckpt` written **after** `fit` may already have advanced that counter;
172
+ inspect the completed loop count rather than adding one unconditionally.
173
+
174
+ The portable recipes disable the wall-clock timer. If you add `max_time` when
175
+ resuming, Lightning also restores elapsed timer state: account for that elapsed
176
+ time when setting the new budget. Keep the original source, data order, rank
177
+ count and microbatch settings for the closest continuation; state restoration
178
+ does not guarantee a bitwise replay of all RNG and data-worker behavior.