File size: 19,710 Bytes
ab9bea9
 
 
 
 
53b24bb
ab9bea9
1117e49
da1c241
1117e49
320d7f0
 
da1c241
3bba754
ab9bea9
 
53b24bb
ab9bea9
53b24bb
320d7f0
 
53b24bb
 
 
ab9bea9
3bba754
 
53b24bb
ab9bea9
da1c241
 
1117e49
 
da1c241
 
ab9bea9
da1c241
 
 
 
ab9bea9
da1c241
53b24bb
da1c241
 
ab9bea9
53b24bb
 
 
320d7f0
53b24bb
 
 
 
 
 
320d7f0
53b24bb
 
 
 
 
 
 
 
 
 
 
320d7f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53b24bb
 
 
320d7f0
 
 
53b24bb
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
320d7f0
53b24bb
 
 
 
 
 
 
 
 
 
 
 
 
 
320d7f0
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
53b24bb
 
 
 
 
 
 
 
 
 
 
 
 
da1c241
ab9bea9
da1c241
53b24bb
ab9bea9
 
da1c241
 
7611e15
3bba754
da1c241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1117e49
da1c241
 
 
 
 
 
 
 
 
1117e49
da1c241
 
1117e49
 
da1c241
 
 
ab9bea9
 
da1c241
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ab9bea9
 
1117e49
 
ab9bea9
 
da1c241
 
 
 
 
ab9bea9
 
1117e49
da1c241
 
 
 
 
 
 
1117e49
da1c241
 
ab9bea9
 
1117e49
da1c241
 
 
 
ab9bea9
da1c241
 
53b24bb
ab9bea9
3bba754
 
53b24bb
7611e15
 
3bba754
7611e15
 
1117e49
 
7611e15
 
3bba754
7611e15
3bba754
 
7611e15
 
3bba754
 
7611e15
 
 
 
3bba754
ab9bea9
 
da1c241
 
1117e49
da1c241
1117e49
da1c241
 
 
 
 
 
ab9bea9
1117e49
 
 
 
 
53b24bb
 
ab9bea9
da1c241
 
 
 
ab9bea9
da1c241
 
53b24bb
da1c241
 
 
 
 
 
 
 
 
 
 
7611e15
 
da1c241
 
53b24bb
 
 
1117e49
da1c241
7611e15
1117e49
 
 
 
da1c241
 
3bba754
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
---
library_name: pytorch
tags:
  - chemistry
  - molecular-generation
  - scaffold-decoration
  - flow-matching
  - qm9
  - geom-drugs
  - chembl3d
  - crossdocked
  - structure-based-drug-design
  - 3d
  - arxiv:2606.07239
---

# Morph: 3D molecular generation and scaffold decoration

Checkpoints for **unconditional 3D molecular generation** on **QM9**,
**GEOM-Drugs** and **ChEMBL3D**, plus **GEOM-Drugs scaffold decoration**
and **CrossDocked scaffold decoration in protein pockets**. The releases include the configurations,
vocabularies and training distributions needed for their inference workflows.
Unconditional generation requires no training dataset; scaffold decoration uses
a supplied scaffold and the dedicated [scaffold bundle](#scaffold-decoration).

See our [paper](https://arxiv.org/abs/2606.07239), *Generative Molecular Morphing for Flexible-Size Design via Unbalanced Optimal Transport*.

## Unconditional checkpoints

| Dataset | Model name | Hydrogen representation | Completed epochs | Files |
| --- | --- | --- | --- | --- |
| QM9 | `qm9-explicit-h` | Explicit H atoms | 1,000, 1,500 | [Bundle](https://huggingface.co/maltefranke/morph/tree/main/qm9/explicit_h) |
| GEOM-Drugs | `geom-explicit-h` | Explicit H atoms | 50, 100 | [Bundle](https://huggingface.co/maltefranke/morph/tree/main/geom/explicit_h) |
| ChEMBL3D | `chembl3d-explicit-h` | Explicit H atoms | 200, 300, 400 | [Bundle](https://huggingface.co/maltefranke/morph/tree/main/chembl3d/explicit_h) |
| ChEMBL3D | `chembl3d-implicit-h` | Heavy atoms; implicit H | 100, 200, 300 | [Bundle](https://huggingface.co/maltefranke/morph/tree/main/chembl3d/implicit_h) |

Epoch numbers count **completed training epochs**: `epoch_050.ckpt` means 50
completed epochs and stores the zero-based Lightning counter `epoch=49`.
Checkpoints are original training files containing current weights, EMA weights
and optimizer state.

The [catalog](https://huggingface.co/maltefranke/morph/blob/main/catalog.json)
is the source of truth for unconditional model names and epochs. Each entry pins an
immutable release revision so its weights and supporting files stay together.
New catalog entries are discovered without updating the Python package.

## Scaffold decoration

**GEOM-Drugs scaffold decoration is also available:** the
[`geom-scaffold-murcko` bundle](https://huggingface.co/maltefranke/morph/tree/main/geom/scaffold_murcko_substituent_lora_r16)
contains the checkpoint after **25 fine-tuning epochs**, initialized from the
GEOM explicit-H epoch-100 EMA weights. It uses **rank-16 LoRA**, trainable output
heads and substituent-constrained insertion supervision.

| Model | Hydrogen representation | Completed fine-tuning epochs | Checkpoint |
| --- | --- | ---: | --- |
| `geom-scaffold-murcko` | Explicit H atoms | 25 | [epoch_025.ckpt](https://huggingface.co/maltefranke/morph/resolve/main/geom/scaffold_murcko_substituent_lora_r16/epoch_025.ckpt) |

The model generates decorations conditioned on a Murcko scaffold. Scaffold atom
identities, internal bonds and charges are preserved; scaffold positions can move.
This conditional checkpoint requires the scaffold bundle's saved configuration and
runtime. It is loaded through that workflow, outside the unconditional `morph generate`
interface and catalog.

The bundle includes the original checkpoint, training configuration and source,
portable configuration, vocabularies, distributions, evaluation runtime and fixed
validation/test scaffold panels. See its [usage and reproduction instructions](https://huggingface.co/maltefranke/morph/blob/main/geom/scaffold_murcko_substituent_lora_r16/README.md).


### Scaffold decoration in a protein pocket

[`crossdocked-scaffold`](https://huggingface.co/maltefranke/morph/tree/main/crossdocked/scaffold)
is the evaluated CrossDocked checkpoint after **100 fine-tuning epochs**. It uses
implicit ligand hydrogens, rank-8 LoRA and 10% in-vacuum ChEMBL3D replay.

| Model | Hydrogen representation | Completed fine-tuning epochs | Checkpoint |
| --- | --- | ---: | --- |
| `crossdocked-scaffold` | Heavy atoms; implicit H | 100 | [epoch_100.ckpt](https://huggingface.co/maltefranke/morph/resolve/main/crossdocked/scaffold/epoch_100.ckpt) |

The bundle contains the evaluated checkpoint, portable configuration,
vocabulary/statistics assets and complete metrics table.
Use the [pocket evaluation workflow](https://huggingface.co/maltefranke/morph/blob/main/crossdocked/scaffold/README.md)
with a scaffold and protein pocket. Like the GEOM scaffold model, it is separate
from the unconditional CLI/catalog.

## Evaluation results

Values are **mean ± sample standard deviation** across seeds **42, 43 and 44**
(`ddof=1`). Rates are percentages; diversity and similarity are on a 0–1 scale.
Vina scores are in kcal/mol.
All evaluations use EMA weights and 100 integration steps.

<details>
<summary><strong>Unconditional generation — all 10 checkpoints</strong></summary>

**10,000 generation attempts per seed and checkpoint** (30,000 per checkpoint).
PoseBusters uses the full offline `mol` checks, including flatness and internal energy,
on the generated structures.

| Model | Completed epochs | Novelty (%) | PoseBusters validity (%) | Diversity (0–1) | Uniqueness (%) | Connected-sanitization validity (%) |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| QM9 · explicit H | 1,000 | 24.99 ± 0.50 | 93.19 ± 0.30 | 0.8969 ± 0.0003 | 94.77 ± 0.19 | 98.99 ± 0.14 |
| QM9 · explicit H | 1,500 | 22.96 ± 0.23 | 93.53 ± 0.19 | 0.8966 ± 0.0001 | 94.91 ± 0.11 | 99.14 ± 0.08 |
| GEOM-Drugs · explicit H | 50 | 99.77 ± 0.02 | 88.21 ± 0.39 | 0.8855 ± 0.0004 | 100.00 ± 0.00 | 95.22 ± 0.35 |
| GEOM-Drugs · explicit H | 100 | 99.75 ± 0.07 | 88.30 ± 0.24 | 0.8848 ± 0.0001 | 100.00 ± 0.00 | 95.46 ± 0.31 |
| ChEMBL3D · explicit H | 200 | 99.69 ± 0.05 | 91.46 ± 0.24 | 0.8953 ± 0.0002 | 99.99 ± 0.02 | 95.80 ± 0.17 |
| ChEMBL3D · explicit H | 300 | 99.75 ± 0.05 | 91.38 ± 0.16 | 0.8956 ± 0.0005 | 100.00 ± 0.00 | 95.72 ± 0.15 |
| ChEMBL3D · explicit H | 400 | 99.73 ± 0.02 | 91.21 ± 0.37 | 0.8953 ± 0.0001 | 100.00 ± 0.00 | 95.41 ± 0.16 |
| ChEMBL3D · implicit H | 100 | 99.72 ± 0.04 | 94.61 ± 0.43 | 0.9141 ± 0.0002 | 100.00 ± 0.00 | 98.60 ± 0.08 |
| ChEMBL3D · implicit H | 200 | 99.64 ± 0.10 | 95.27 ± 0.16 | 0.9140 ± 0.0002 | 100.00 ± 0.01 | 98.87 ± 0.18 |
| ChEMBL3D · implicit H | 300 | 99.61 ± 0.04 | 95.32 ± 0.09 | 0.9142 ± 0.0001 | 100.00 ± 0.00 | 98.80 ± 0.07 |

Novelty is measured against each checkpoint's training split. Novelty and uniqueness
use canonical, H-stripped molecular identities with stereochemistry ignored.

</details>

<details>
<summary><strong>Scaffold decoration — GEOM-Drugs, 25 fine-tuning epochs</strong></summary>

**512 held-out test scaffolds × 8 generation attempts per scaffold per seed**
(4,096 per seed; 12,288 total), using the fixed released Murcko scaffold panel.
The generated coordinates are not relaxed.

| Model | Completed epochs | Novelty (%) | PoseBusters validity (%) | Diversity (0–1) | Uniqueness (%) | Connected-sanitization validity (%) |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| `geom-scaffold-murcko` | 25 (fine-tuning) | 97.52 ± 0.10 | 94.10 ± 0.19 | 0.9076 ± 0.0004 | 63.54 ± 0.59 | 97.99 ± 0.20 |

**PoseBusters protocol:** the native scaffold evaluation checks chemistry and distance
geometry, excluding the full offline energy and flatness checks. The value above is
`posebusters/validity`, the strict all-check pass rate for that configuration; it does
not apply the scaffold-aware inherited-clash exemption. It is therefore **not directly
comparable** to the full offline PoseBusters scores in the unconditional table.
Novelty, diversity and uniqueness describe the pooled generated molecules within each
seed, across all 512 scaffolds; uniqueness is not averaged per scaffold.

See the [scaffold bundle documentation](https://huggingface.co/maltefranke/morph/blob/main/geom/scaffold_murcko_substituent_lora_r16/README.md)
for the saved evaluation runtime and test panel.

</details>

<details>
<summary><strong>Scaffold decoration in a pocket — CrossDocked, 100 fine-tuning epochs</strong></summary>

`crossdocked-scaffold`, evaluated on the published DiffDec single- and multi-decoration
test panels, with **100 attempts per condition per seed** (43,500 total).

| Panel | Conditions | Attempts per seed | Validity (%) | Uniqueness (%) | Similarity (0–1) | Recovery (%) | Vina score (kcal/mol) | High affinity (%) |
| --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
| Single | 43 | 4300 | 88.72 ± 0.71 | 53.32 ± 1.02 | 0.8157 ± 0.0042 | 39.00 ± 1.15 | -8.041 ± 0.007 | 44.18 ± 3.67 |
| Multi | 102 | 10200 | 89.80 ± 0.16 | 66.43 ± 0.38 | 0.7671 ± 0.0032 | 37.83 ± 5.33 | -8.088 ± 0.010 | 40.33 ± 3.14 |

Mean ± sample standard deviation across seeds 42/43/44. The six columns use
DiffDec's grouping and formulas; validity and uniqueness have different definitions
from the unconditional and GEOM scaffold tables. Docking re-embeds and UFF-minimizes
prediction SMILES, so it does not score the original generated coordinates.
Invalid predictions are replaced with the scaffold for docking. Reproduction
differences include reconstructed reference grouping strings, predicted bond
topology, newer RDKit and deterministic embedding seeds.

See the [full CSV, including individual seeds and docking failures](https://huggingface.co/maltefranke/morph/blob/main/crossdocked/scaffold/summary.csv)
and [protocol and reproduction notes](https://huggingface.co/maltefranke/morph/blob/main/crossdocked/scaffold/README.md).

</details>

Metric definitions for the unconditional and GEOM scaffold tables:

- **Novelty:** fraction of generated, SMILES-convertible molecules absent from the
  corresponding training reference, retaining duplicates in the denominator.
- **PoseBusters validity:** fraction of all generation attempts passing every enabled
  PoseBusters check; missing checks and failed molecule conversions count as failures.
- **Diversity:** mean pairwise Tanimoto distance between ECFP6 fingerprints (radius 3,
  2,048 bits), retaining duplicates among fingerprintable molecules.
- **Uniqueness:** distinct canonical molecular identities divided by all
  SMILES-convertible molecules within a seed.
- **Connected-sanitization validity:** fraction of all attempts producing connected,
  RDKit-sanitizable molecules, with no additional charge or radical restriction
  (`connected-sanitization-validity`).

## Install and authenticate

The runtime bundled with GEOM includes the shared inference interface and catalog
discovery for all models in the unconditional checkpoint table. Install it in a Python environment:

```bash
python -m pip install 'huggingface_hub>=1.0,<2'
hf auth login
hf download maltefranke/morph \
  --local-dir morph-runtime \
  --include 'geom/explicit_h/runtime/*'
python -m pip install ./morph-runtime/geom/explicit_h/runtime
morph models list
```

Authentication requires an account with access to this model repository.
Python >=3.11 and a compatible PyTorch 2.9 installation are required; CUDA
generation needs a CUDA-capable PyTorch build. Dependency constraints and tested
versions are included in the runtime. An existing Morph environment with catalog
discovery can use the examples directly. On a cluster, run model loading and
generation inside a compute allocation.

## Load and generate from Python

```python
from morph.inference import load_pretrained

model = load_pretrained("geom-explicit-h", epoch=100, device="cuda")

trajectories, validity, vocab, coordinate_std = model.generate(
    n_mols=64,
    batch_size=16,
    seed=42,
    return_traj=False,
)
```

Use `device="cpu"` for CPU inference. The same interface loads QM9 and ChEMBL3D:

```python
model = load_pretrained("qm9-explicit-h", epoch=1500, device="cuda")
# Use epoch=1000 for the earlier QM9 checkpoint.
model = load_pretrained("chembl3d-explicit-h", epoch=400, device="cuda")
# Or choose the heavy-atom model:
model = load_pretrained("chembl3d-implicit-h", epoch=300, device="cuda")
```

Loading downloads only the selected checkpoint and its six required asset files,
then reuses the Hugging Face cache. Each checkpoint is approximately 510 MB.
Omitting `epoch` selects the latest published epoch for the chosen model.

**EMA weights are used by default.** The inference loader constructs one network
and loads only the selected weights; the unselected network and optimizer tensors
are not materialized from the memory-mapped checkpoint. To select current
training weights, pass `use_ema=False` to `load_pretrained`.

Generation defaults to 100 integration steps. Each attempt remains in the returned
lists, including invalid molecules. `trajectories[i][-1]` is its final graph and
`validity[i]` is its RDKit validity result. Pass `return_traj=True` to retain all
integration frames. Returned coordinates are already in **Angstrom**; do not
multiply them by `coordinate_std` again.

## CLI and downloads

Generate GEOM-Drugs molecules and save valid structures to SDF:

```bash
morph generate --model geom-explicit-h --epoch 100 \
  --device cuda --batch 64 --batch-size 16 --output geom-e100.sdf
```

`--batch` is the total number of generation attempts. Add `--save-trajs` to save
trajectories and validity flags beside the SDF. `--no-ema` selects current
weights, and `--steps` and `--seed` control sampling.

Download a portable bundle without loading the model:

```bash
morph models download geom-explicit-h --epoch 100 --local-dir morph-models
```

Load that local bundle without network access or catalog lookup:

```python
model = load_pretrained(
    weights_dir="morph-models/geom/explicit_h",
    epoch=100,
    device="cuda",
)
```

Use another model name from the table for QM9 or ChEMBL3D. The interface also accepts
direct Hub repository IDs with `subfolder` and `revision`. Pass `cache_dir` to
choose a cache location, or `local_files_only=True` to load a previously cached
release without network access. The corresponding CLI options are `--cache-dir`
and `--offline`.

The root `sample.py` remains available as the archived ChEMBL3D sampling entry
point with its corresponding bundled runtimes. The shared interface above
supports all four published unconditional model variants.

## Reproducibility

Each unconditional model has a **training bundle** with its original training configuration,
launch settings, frozen source, dependency versions and data references.
The bundles live alongside the corresponding model files on Hugging Face:

| Model | Configuration and source |
| --- | --- |
| QM9, explicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/qm9/explicit_h/training_bundle.tar.gz) |
| GEOM-Drugs, explicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/geom/explicit_h/training_bundle.tar.gz) |
| ChEMBL3D, explicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/chembl3d/explicit_h/training_bundle.tar.gz) |
| ChEMBL3D, implicit H | [Training bundle](https://huggingface.co/maltefranke/morph/resolve/main/chembl3d/implicit_h/training_bundle.tar.gz) |

For example, download and unpack the GEOM training bundle:

```bash
hf download maltefranke/morph geom/explicit_h/training_bundle.tar.gz --local-dir .
tar -xzf geom/explicit_h/training_bundle.tar.gz
```

Each bundle also includes a portable training recipe; its changes from the
original run are recorded alongside it. See the [training bundle guide](reproduction/README.md)
for setup, data requirements and resuming. Version identifiers and checksums
are recorded in the release metadata and bundle manifests.

## Hydrogen handling and training data

| Dataset | Training filter | Representation |
| --- | --- | --- |
| QM9 | Original QM9 split | Explicit-H variant |
| GEOM-Drugs | At most 72 total atoms, including H | Explicit-H variant |
| ChEMBL3D | At most 100 total atoms, including H before hydrogen removal | Explicit-H and implicit-H variants |

Both ChEMBL3D variants use the same split membership: 1,474,008 training,
184,295 validation and 184,242 test molecules. The implicit-H model predicts
heavy atoms, bonds and formal charges; RDKit infers hydrogens during conversion
and validity checking. Its geometry statistics and coordinate normalization were
recomputed for the heavy-atom representation.

QM9 uses 100,000 training, 20,000 validation and 12,947 test molecules. Its run
started from scratch, validates every epoch and retains checkpoints every 25 epochs.

The GEOM-Drugs epoch 50 and 100 checkpoints come from the run resumed from the
best checkpoint after 48 completed epochs. The resumed run validates every epoch and retains
checkpoints every fifth epoch. The published unconditional epochs are listed in the catalog. The scaffold-decoration
checkpoint adds 25 fine-tuning epochs on top of the epoch-100 model.

Explicit-H models generate H atoms as graph vertices. Every model uses its own
ordered vocabularies, distributions and saved configuration; keep those files
together. The training atom limits describe dataset filtering and do not impose
a hard atom-count limit during generation.

## Bundle contents and verification

Each unconditional model directory contains:

- `epoch_NNN.ckpt`: the published original training checkpoints.
- `config.yaml`: portable inference configuration with dataset loading disabled.
- `atom_tokens.txt`, `edge_tokens.txt`, `charge_tokens.txt`: ordered vocabularies.
- `distributions.pt`: original training statistics, including atom counts,
  coordinate scale, type frequencies and geometry histograms.
- `distributions.json`: a readable export of those statistics.
- `metadata.json`: dataset, hydrogen mode, epochs, optimizer steps, original
  filenames and checkpoint SHA-256 hashes.
- `training_config.yaml`: the original training configuration for provenance.
- `runtime/`: archived source, configs and dependency specifications.
- `training_bundle.tar.gz`: original run configuration, source, environment and a
  portable training recipe; `training_bundle.json` records its checksum and provenance.
- `validation.json`: results of checkpoint loading and sampling checks.

The published unconditional bundles were checked for strict current/EMA weight loading
and end-to-end sampling. These functionality checks are separate from the
[generation benchmarks above](#evaluation-results).
The GEOM and QM9 releases also passed remote file-size and hash verification.

The [ChEMBL3D release manifest](https://huggingface.co/maltefranke/morph/blob/main/manifest.json)
describes the original ChEMBL3D release, including its historical model card.
[QM9](https://huggingface.co/maltefranke/morph/blob/main/qm9/explicit_h/manifest.json) and
[GEOM-Drugs](https://huggingface.co/maltefranke/morph/blob/main/geom/explicit_h/manifest.json)
have their own bundle manifests.
Original training configs contain historical cluster paths; use `config.yaml`
through the inference loader for portable generation.

## Citation

If you use Morph, please cite our [paper](https://arxiv.org/abs/2606.07239):

```bibtex
@misc{franke2026generativemolecularmorphingflexiblesize,
      title={Generative Molecular Morphing for Flexible-Size Design via Unbalanced Optimal Transport},
      author={Malte Franke and Stefan P. Schmid and Zarko Ivkovic and Kjell Jorner and Andreas Krause},
      year={2026},
      eprint={2606.07239},
      archivePrefix={arXiv},
      primaryClass={cs.LG},
      url={https://arxiv.org/abs/2606.07239},
}
```

Machine-readable citation metadata is available in [CITATION.cff](CITATION.cff).