File size: 11,214 Bytes
4947683
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
<div align="center">

# One Path to Model Them All: Learnable-Time Flow Matching for Crystal Structure and Energy Prediction

[![Paper](https://img.shields.io/badge/Paper-ACM%20DL-B31B1B.svg)](https://dl.acm.org/doi/pdf/10.1145/3770855.3818902)
[![Checkpoints](https://img.shields.io/badge/%F0%9F%A4%97%20Checkpoints-Wthinker%2FUniPath-FFD21E.svg)](https://huggingface.co/Wthinker/UniPath)
[![Datasets](https://img.shields.io/badge/%F0%9F%A4%97%20Datasets-Wthinker%2FUniPath-FFD21E.svg)](https://huggingface.co/datasets/Wthinker/UniPath)

<a href="https://huggingface.co/BAAI/AIDD/resolve/main/UniPath/assets/UniPath_Poster.png"><img src="https://huggingface.co/BAAI/AIDD/resolve/main/UniPath/assets/UniPath_Poster.png" alt="UniPath KDD 2026 poster" width="900"></a>

</div>

- [Setup](#1-setup) · [Download](#2-download-checkpoints-and-data) · [Sampling](#3-run-sampling) · [Results](#4-results) · [Training](#5-training-from-scratch) · [Citation](#citation) · [Contact](#contact)

---

## 1. Setup

```bash
git clone <repo_url>
cd crystal-uniug

conda env create -f environment.yml
conda activate crystal_uniug

pip install -e remote/cdvae
pip install -e remote/DiffCSP-official
pip install -e remote/riemannian-fm
pip install -e .
```

Then set four environment variables. The easiest way is to copy them into a `.env` file at the project root and `source .env`:

```bash
export PROJECT_ROOT="/path/to/crystal-uniug"
export HYDRA_JOBS="/path/to/crystal-uniug"
export WANDB_DIR="/path/to/crystal-uniug"
export DATA_DIR="/path/to/data_download/dataset"   # set in step 2
```

`PROJECT_ROOT` is required: the training and sampling scripts `cd` to it and resolve
checkpoint paths against it.

Experiment logging via [SwanLab](https://swanlab.cn) is optional and off by default. To enable it,
export your own key before running any script:

```bash
export SWANLAB_API_KEY="<your-key>"
```

---

## 2. Download Checkpoints and Data

Both are hosted on HuggingFace:

- Checkpoints: [`Wthinker/UniPath`](https://huggingface.co/Wthinker/UniPath) (model repo, ~870 MB)
- Datasets: [`Wthinker/UniPath`](https://huggingface.co/datasets/Wthinker/UniPath) (dataset repo, ~22 GB)

If `huggingface.co` is unreachable, set `export HF_ENDPOINT=https://hf-mirror.com` first.

```bash
# Checkpoints -> ckpt_download/ckpt/
hf download Wthinker/UniPath --repo-type model --local-dir ./ckpt_download

# Datasets -> data_download/dataset/
hf download Wthinker/UniPath --repo-type dataset --local-dir ./data_download

export DATA_DIR="$PWD/data_download/dataset"
```

### Checkpoint paths

| Name | Path under `ckpt_download/ckpt/` |
|---|---|
| `mpts52_PathRefine` | `mpts52_PathRefine/every_n_epochs/epoch=149-step=80976.ckpt` |
| `mpts52_uns_PathRefine` | `mpts52_uns_PathRefine/every_n_epochs/epoch=149-step=36225.ckpt` |
| `mp20_PathRefine` | `mp20_PathRefine/every_n_epochs/epoch=499-step=190587.ckpt` |
| `mp20_uns_PathRefine` | `mp20_uns_PathRefine/every_n_epochs/epoch=299-step=87163.ckpt` |

> ⚠️ **Do not move a `.ckpt` out of its directory.** Model loading resolves the architecture by
> walking up the directory tree to find the sibling `.hydra/config.yaml`. Moving the file alone
> will fail with `Failed to find hydra config!`.

### Expected data layout

Each experiment needs one **CSP** dataset plus one **MD** dataset:

```
$DATA_DIR/
├── mpts_52/                 # CSP — both mpts52 checkpoints
├── mp_20/                   # CSP — both mp20 checkpoints
├── mptsubmpts52/            # MD  — mpts52_PathRefine
├── mptsubmpts52_uns_V0/     # MD  — mpts52_uns_PathRefine
├── mptsubmp20_v0/           # MD  — mp20_PathRefine
└── mptsubmp20_uns_V0/       # MD  — mp20_uns_PathRefine
```

CSP dirs contain `{train,val,test}.csv` + `{train,val,test}_ori.pt`; MD dirs contain `{train,val}.csv` + `{train,val}_ori.pt`.

> Sampling only predicts on the test split, but the data module still loads the **train** split to
> compute normalization scalers, and the MD splits because these are joint CSP+MD models. All of the
> above must be present.

---

## 3. Run Sampling

Sampling runs four steps in sequence. Set `CKPT` to any checkpoint from the table above:

```bash
CKPT=./ckpt_download/ckpt/mp20_PathRefine/every_n_epochs/epoch=499-step=190587.ckpt
SUBDIR=reproduce_1

python ./scripts_model/evaluate.py reconstruct ${CKPT} \
    --subdir ${SUBDIR} --inference_anneal_slope 10.0 \
    --stage test --num_evals 1 --batch_size 1024 --sample_way flow --single_gpu && \
python ./scripts_model/evaluate.py consolidate ${CKPT} --subdir ${SUBDIR} && \
python ./scripts_model/evaluate.py old_eval_metrics ${CKPT} --subdir ${SUBDIR} --stage test --do_not_log_wandb && \
python ./scripts_model/evaluate.py lattice_metrics ${CKPT} --subdir ${SUBDIR} --stage test
```

To do all four checkpoints, loop over them:

```bash
for name in mpts52_PathRefine mpts52_uns_PathRefine mp20_PathRefine mp20_uns_PathRefine; do
    CKPT=$(ls ./ckpt_download/ckpt/${name}/every_n_epochs/*.ckpt)
    echo "=== ${name} ==="
    python ./scripts_model/evaluate.py reconstruct   "${CKPT}" --subdir reproduce_1 \
        --inference_anneal_slope 10.0 --stage test --num_evals 1 \
        --batch_size 1024 --sample_way flow --single_gpu       && \
    python ./scripts_model/evaluate.py consolidate      "${CKPT}" --subdir reproduce_1 && \
    python ./scripts_model/evaluate.py old_eval_metrics "${CKPT}" --subdir reproduce_1 --stage test --do_not_log_wandb && \
    python ./scripts_model/evaluate.py lattice_metrics  "${CKPT}" --subdir reproduce_1 --stage test
done
```

A ready-made template is also available at `UniPath/script/sample.sh` — edit `ckpt` and `target_dir_name`, then `bash UniPath/script/sample.sh --run_id 1`.

### Key flags

| Flag | Meaning |
|---|---|
| `--inference_anneal_slope` | annealing slope for flow inference (`10.0` reproduces the paper) |
| `--batch_size` | sampling batch size (`1024`) |
| `--sample_way flow` | flow-based sampler |
| `--num_evals` | number of sampling repeats per structure (`1`) |
| `--single_gpu` | run on one GPU; **omit** for multi-GPU DDP |

> **Runtime:** `reconstruct` dominates — roughly 1.5 h (mp_20) to 3.5 h (mpts_52) per checkpoint on a
> single A100. The other three steps take a few minutes each.

---

## 4. Results

Each run writes to `<ckpt_dir>/<SUBDIR>/`:

| File | Contents |
|---|---|
| `old_eval_metrics_reconstruct_multi.json` | `match_rate`, `rms_dist` (multi-eval) |
| `old_eval_metrics_reconstruct_single.json` | `match_rate`, `rms_dist` (single-eval) |
| `consolidated_reconstruct.pt` | consolidated raw predictions |
| `lengths.png` / `angles.png` | lattice parameter distributions |

Compare the reported `match_rate` and `rms_dist` against the values in the paper. With
`--num_evals 1` the multi- and single-eval JSONs are identical.

---

## 5. Training from Scratch

Only needed if you want to retrain rather than use the released checkpoints. Budget several GPU-days
per checkpoint.

### Script layout

```
UniPath/script/uni_train/main/
├── mpts52/       -> mpts52_PathRefine       (3 stages)
├── mpts52_uns/   -> mpts52_uns_PathRefine   (3 stages)
├── mp20/         -> mp20_PathRefine         (2 stages)
└── mp20_uns/     -> mp20_uns_PathRefine     (2 stages)
```

Outputs are written to `runs/trash/main/<experiment_name>/`.

### Launching

Scripts launch `UniPath/run_uni.py` through `torchrun` and read the standard distributed variables
(`WORLD_SIZE` = nodes, `NPROC_PER_NODE` = GPUs per node, `RANK`, `MASTER_ADDR`, `MASTER_PORT`).
Each script declares a **total** batch size and divides it by the world size, so the per-device batch
size adapts to your GPU count — but the total must be divisible by it.

Single-node, 4 GPUs:

```bash
export WORLD_SIZE=1 NPROC_PER_NODE=4 RANK=0
export MASTER_ADDR=127.0.0.1 MASTER_PORT=29500
```

Each script `cd`s to the project root and sources `UniPath/running_setup.sh`, so it can be invoked from anywhere.

### Training order

Each checkpoint is produced by a **multi-stage pipeline**: every stage after the first warm-starts
from the previous stage's checkpoint (`partial_ckpt_load=True`, a partial weight load rather than a
Lightning full-state resume). Stages must therefore run **in order**.

<details>
<summary><b>mpts52_PathRefine</b> — 3 stages</summary>

```bash
T=UniPath/script/uni_train/main/mpts52

# 1. Joint CSP+MD pretraining, from scratch (150 epochs)
bash $T/uni_E_tclearn_freeze.sh

# 2. Unsupervised-flow path refinement, warm-started from stage 1 @ epoch 109
bash $T/mpts52_mptsub_path_refine_wunstune.sh --w_uns 0.1

# 3. + consistency loss, warm-started from stage 2 @ epoch 129   -> RELEASED
bash $T/mpts52_mptsub_path_refine_consist_wunstune.sh --w_uns 0.1 --w_consist 0.05
```
</details>

<details>
<summary><b>mpts52_uns_PathRefine</b> — 3 stages</summary>

```bash
T=UniPath/script/uni_train/main/mpts52_uns

bash $T/uni_E_tclearn_freeze.sh
bash $T/mpts52_uns_path_refine_wunstune.sh --w_uns 0.02
bash $T/mpts52_uns_path_refine_consist_wunstune.sh --w_uns 0.02 --w_consist 0.02   # -> RELEASED
```
</details>

<details>
<summary><b>mp20_PathRefine</b> — 2 stages</summary>

```bash
T=UniPath/script/uni_train/main/mp20

bash $T/bz256_tclearn_freeze_epoch500.sh
bash $T/mp20_wtune.sh --w_uns 0.001 --w_consist 0.01   # -> RELEASED
```
</details>

<details>
<summary><b>mp20_uns_PathRefine</b> — 2 stages</summary>

```bash
T=UniPath/script/uni_train/main/mp20_uns

bash $T/uni_E_tclearn_freeze.sh
bash $T/mp20_uns_wunte.sh --w_uns 0.02 --w_consist 0.5   # -> RELEASED
```
</details>

`--w_uns` sets the unsupervised-flow loss weight, `--w_consist` the consistency loss weight. The
values above are the ones that produced the released checkpoints.

### Before you train

- **`partial_ckpt_path` is built from `${PROJECT_ROOT}`** in each warm-starting script, so exporting
  `PROJECT_ROOT` is enough — no need to edit the paths by hand.
- **Parent checkpoints are referenced by exact filename** (e.g. `epoch=109-step=424160.ckpt`). The
  step count depends on your world size and batch size, so a retrained parent produces a *different*
  filename — update the referencing script accordingly.
- **`runs/` is gitignored**, so training outputs are never committed.

### Sampling your own checkpoint

Use the Section 3 commands with `CKPT` pointing into your run directory, keeping the checkpoint beside its `.hydra/config.yaml`:

```bash
CKPT=runs/trash/main/mpts52_PathRefine_wuns0.1_wc0.05_e109_e129/every_n_epochs/epoch=149-step=80976.ckpt
```

---

## Citation

If you find this work useful, please cite:

```bibtex
@inproceedings{li2026one,
  title={One Path to Model Them All: Learnable-Time Flow Matching for Crystal Structure and Energy Prediction},
  author={Li, Songyou and Li, Mingze and Liu, Qianpu and Cen, Jiacheng and Wu, Liming and Xu, Tingyang and Rong, Yu and Zhao, Deli and Huang, Wenbing},
  booktitle={Proceedings of the 32nd ACM SIGKDD Conference on Knowledge Discovery and Data Mining V. 2},
  pages={11351--11362},
  year={2026}
}
```

---

## Contact

Questions and issues are welcome — open a GitHub issue or reach out to
[songyou_li@126.com](mailto:songyou_li@126.com).