jflows 0.5.3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- jflows-0.5.3/LICENSE +21 -0
- jflows-0.5.3/PKG-INFO +254 -0
- jflows-0.5.3/README.md +224 -0
- jflows-0.5.3/jflows/__init__.py +93 -0
- jflows-0.5.3/jflows/artifacts.py +46 -0
- jflows-0.5.3/jflows/boltzmann/__init__.py +1069 -0
- jflows-0.5.3/jflows/boltzmann/load.py +188 -0
- jflows-0.5.3/jflows/boltzmann/write.py +142 -0
- jflows-0.5.3/jflows/core/__init__.py +13 -0
- jflows-0.5.3/jflows/core/flows.py +507 -0
- jflows-0.5.3/jflows/core/nn.py +231 -0
- jflows-0.5.3/jflows/core/numerics.py +228 -0
- jflows-0.5.3/jflows/core/otflow.py +357 -0
- jflows-0.5.3/jflows/core/transforms.py +861 -0
- jflows-0.5.3/jflows/flow.py +735 -0
- jflows-0.5.3/jflows/loss.py +167 -0
- jflows-0.5.3/jflows/potential.py +538 -0
- jflows-0.5.3/jflows/train.py +608 -0
- jflows-0.5.3/jflows/utils/__init__.py +95 -0
- jflows-0.5.3/jflows/utils/anneal.py +299 -0
- jflows-0.5.3/jflows/utils/metrics.py +334 -0
- jflows-0.5.3/jflows/utils/optimization.py +447 -0
- jflows-0.5.3/jflows/utils/quench.py +91 -0
- jflows-0.5.3/jflows/utils/rejuvenation.py +404 -0
- jflows-0.5.3/jflows/version.py +3 -0
- jflows-0.5.3/jflows.egg-info/PKG-INFO +254 -0
- jflows-0.5.3/jflows.egg-info/SOURCES.txt +30 -0
- jflows-0.5.3/jflows.egg-info/dependency_links.txt +1 -0
- jflows-0.5.3/jflows.egg-info/requires.txt +6 -0
- jflows-0.5.3/jflows.egg-info/top_level.txt +1 -0
- jflows-0.5.3/pyproject.toml +50 -0
- jflows-0.5.3/setup.cfg +4 -0
jflows-0.5.3/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Xuda
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
jflows-0.5.3/PKG-INFO
ADDED
|
@@ -0,0 +1,254 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: jflows
|
|
3
|
+
Version: 0.5.3
|
|
4
|
+
Summary: JAX normalizing flows for unconditional energy-based sampling and Boltzmann generators.
|
|
5
|
+
Author-email: Xuda <abneryepku@gmail.com>
|
|
6
|
+
License-Expression: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/xuda-ye-math/jflows
|
|
8
|
+
Keywords: normalizing-flows,boltzmann-generator,jax,equinox,sampling,smc,neural-spline-flows,ot-flow
|
|
9
|
+
Classifier: Development Status :: 3 - Alpha
|
|
10
|
+
Classifier: Intended Audience :: Science/Research
|
|
11
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
12
|
+
Classifier: Programming Language :: Python :: 3
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
17
|
+
Classifier: Topic :: Scientific/Engineering
|
|
18
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: jax>=0.6.0
|
|
25
|
+
Requires-Dist: equinox>=0.13.0
|
|
26
|
+
Requires-Dist: numpy>=2.0.0
|
|
27
|
+
Provides-Extra: examples
|
|
28
|
+
Requires-Dist: matplotlib>=3.8; extra == "examples"
|
|
29
|
+
Dynamic: license-file
|
|
30
|
+
|
|
31
|
+
<p align="center"><img src="jflows.png" alt="jflows banner" width="800px"></p>
|
|
32
|
+
<p align="center"><sub><em>Banner designed by ChatGPT: the character Jax from "The Amazing Digital Circus", a nod to jflows being built on Google JAX.</em></sub></p>
|
|
33
|
+
|
|
34
|
+
# jflows
|
|
35
|
+
|
|
36
|
+
`jflows` is a JAX/Equinox package for unconditional normalizing flows,
|
|
37
|
+
energy-based sampling, and staged Boltzmann generators. It provides a small
|
|
38
|
+
public API for building flows, defining unnormalized target energies, training
|
|
39
|
+
one transport stage, and connecting accepted stages into a complete source-to-
|
|
40
|
+
target sampler.
|
|
41
|
+
|
|
42
|
+
> **Status: experimental.** The package is developed and tested on Linux with
|
|
43
|
+
> NVIDIA CUDA JAX. JAX GPU is not supported on Windows, including WSL.
|
|
44
|
+
>
|
|
45
|
+
> This project was developed with Codex.
|
|
46
|
+
|
|
47
|
+
## Core features
|
|
48
|
+
|
|
49
|
+
- **First-class spline flows.** `NSF` models bounded Euclidean boxes and
|
|
50
|
+
`NCSF` models periodic boxes or tori. `CNF`, `OTFlow`, and `RealNVP` provide
|
|
51
|
+
complementary continuous and affine-coupling architectures.
|
|
52
|
+
- **One immutable flow interface.** Every flow provides `flow(x)`,
|
|
53
|
+
`flow.call_and_ladj(x)`, `flow.inv(y)`, and `flow.inv_and_ladj(y)` and works
|
|
54
|
+
as an Equinox pytree under JIT, vectorization, and differentiation.
|
|
55
|
+
- **Unified energy interface.** A `Potential` maps `[N,d]` samples to `[N]`
|
|
56
|
+
energies for densities proportional to `exp(-U)`. Built-in uniform, Gaussian,
|
|
57
|
+
and Gaussian-mixture potentials support explicit-key sampling, while
|
|
58
|
+
`potential_from` wraps a plain batched energy function.
|
|
59
|
+
- **Potential algebra.** Addition, subtraction, scaling, and
|
|
60
|
+
`linear_combination` construct bridge energies without introducing a second
|
|
61
|
+
target abstraction.
|
|
62
|
+
- **Explicit randomness.** Every random constructor and sampler takes a JAX
|
|
63
|
+
PRNG key. There is no package-global random state.
|
|
64
|
+
- **Sampling and diagnostics.** Importance weights, normalized ESS, coverage,
|
|
65
|
+
resampling, Langevin/MALA, stochastic Heun, HMC, SMC, flow-proposal AIS,
|
|
66
|
+
L-BFGS, AdamW, and quench-and-temper are available from `jflows.utils`.
|
|
67
|
+
- **Runtime backend report.** `jflows.backend()` lists CPU and installed JAX
|
|
68
|
+
accelerator plugins backed by visible hardware, identifies the configured
|
|
69
|
+
runtime, and reports JAX/Equinox versions and accelerator models without
|
|
70
|
+
creating a JAX device client or performing device computation.
|
|
71
|
+
- **Progressive training objectives.** Reverse KL, forward KL, KLX, and KLXX
|
|
72
|
+
share one validation convention while adding progressively richer
|
|
73
|
+
target-batch and density-ratio information.
|
|
74
|
+
- **Complete-stage persistence.** Boltzmann computation is separate from
|
|
75
|
+
optional stage writers/loaders, so an interrupted stage can be recomputed
|
|
76
|
+
from the last published population.
|
|
77
|
+
|
|
78
|
+
Flows are immutable. Identity initialization returns a new object:
|
|
79
|
+
|
|
80
|
+
```python
|
|
81
|
+
flow = flow.zeros()
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
`OTFlow` is the training exception: use `flow.near_identity()` so its
|
|
85
|
+
quadratic factor retains a nonzero gradient. Keep `OTFlow.zeros()` for an exact
|
|
86
|
+
identity map.
|
|
87
|
+
|
|
88
|
+
## Three-level interface
|
|
89
|
+
|
|
90
|
+
The public API is deliberately arranged from basic numerical components to a
|
|
91
|
+
complete staged generator.
|
|
92
|
+
|
|
93
|
+
### LOW — building blocks
|
|
94
|
+
|
|
95
|
+
The low level contains flows, potentials, per-sample losses, metrics, MCMC,
|
|
96
|
+
SMC/AIS, optimization, and quench-and-temper:
|
|
97
|
+
|
|
98
|
+
```python
|
|
99
|
+
from jflows.flow import NSF, NCSF, CNF, OTFlow, RealNVP
|
|
100
|
+
from jflows.potential import Potential, Nlog_Gaussian, potential_from
|
|
101
|
+
from jflows.loss import reverse_KL_F, forward_KL_G, forward_KLX_G, forward_X_G
|
|
102
|
+
from jflows import backend
|
|
103
|
+
from jflows.utils import compute_ESS_log, importance_weights_log, langevin, smc
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
`F` denotes the source-to-target map and `G = F^{-1}` the target-to-source
|
|
107
|
+
map. An `_F` or `_G` suffix fixes the native training direction.
|
|
108
|
+
|
|
109
|
+
### MEDIUM — one-stage training
|
|
110
|
+
|
|
111
|
+
The medium level trains one map between a source and target potential:
|
|
112
|
+
|
|
113
|
+
```python
|
|
114
|
+
from jflows.train import (
|
|
115
|
+
Monitor,
|
|
116
|
+
train_reverse_KL_F,
|
|
117
|
+
train_forward_KL_G,
|
|
118
|
+
train_forward_KLX_G,
|
|
119
|
+
train_forward_KLXX_G,
|
|
120
|
+
)
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Every trainer returns a new flow and its optimizer-batch ESS history. Simple
|
|
124
|
+
flow, sample, and history serialization lives in `jflows.artifacts`.
|
|
125
|
+
|
|
126
|
+
### HIGH — staged Boltzmann generation
|
|
127
|
+
|
|
128
|
+
The high level connects accepted bridge stages and compares every trained map
|
|
129
|
+
with an exact identity fallback using full-validation incremental ESS:
|
|
130
|
+
|
|
131
|
+
```python
|
|
132
|
+
from jflows.boltzmann import (
|
|
133
|
+
boltzmann_identity,
|
|
134
|
+
boltzmann_reverse_KL_F,
|
|
135
|
+
boltzmann_forward_KL_G,
|
|
136
|
+
boltzmann_forward_KLX_G,
|
|
137
|
+
boltzmann_forward_KLXX_G,
|
|
138
|
+
)
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
- identity runs the adaptive-staging controller for stage selection, ESS
|
|
142
|
+
gating, resampling, and rejuvenation with no flow or optimizer;
|
|
143
|
+
- reverse KL is the simplest source-sampled F baseline;
|
|
144
|
+
- forward KL manufactures target-side batches and trains G;
|
|
145
|
+
- KLX adds target-measure log-weight variation control; and
|
|
146
|
+
- KLXX adds a quench-and-temper/proposal mixture for wider mode and leakage
|
|
147
|
+
regularization.
|
|
148
|
+
|
|
149
|
+
The four trained generators compare their candidate with identity. The
|
|
150
|
+
standalone `boltzmann_identity` function instead advances every accepted
|
|
151
|
+
stage by exact identity reweighting, so it requires no flow, batch size,
|
|
152
|
+
training steps, or learning rate. Complete-stage persistence is exposed
|
|
153
|
+
separately through `jflows.boltzmann.write` and `jflows.boltzmann.load`.
|
|
154
|
+
|
|
155
|
+
## Minimal setup
|
|
156
|
+
|
|
157
|
+
```bash
|
|
158
|
+
pip install "jax[cuda13]" equinox
|
|
159
|
+
pip install -e .
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
```python
|
|
163
|
+
import jflows
|
|
164
|
+
|
|
165
|
+
jflows.backend()
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
A minimal training setup is:
|
|
169
|
+
|
|
170
|
+
```python
|
|
171
|
+
import jax
|
|
172
|
+
|
|
173
|
+
from jflows.flow import NSF
|
|
174
|
+
from jflows.potential import Nlog_Gaussian, potential_from
|
|
175
|
+
from jflows.train import train_forward_KLX_G
|
|
176
|
+
from jflows.utils import compute_ESS_log, importance_weights_log
|
|
177
|
+
|
|
178
|
+
source = Nlog_Gaussian([0.0, 0.0], [1.0, 1.0])
|
|
179
|
+
target = potential_from(
|
|
180
|
+
lambda x: 10.0 * (jax.numpy.linalg.norm(x, axis=-1) - 2.0) ** 2
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
flow = NSF(
|
|
184
|
+
jax.random.key(0),
|
|
185
|
+
[-4.0, -4.0],
|
|
186
|
+
[4.0, 4.0],
|
|
187
|
+
bins=16,
|
|
188
|
+
transforms=4,
|
|
189
|
+
).zeros()
|
|
190
|
+
x_valid = source.samples(jax.random.key(1), 20000)
|
|
191
|
+
|
|
192
|
+
flow, batch_ess = train_forward_KLX_G(
|
|
193
|
+
x_valid,
|
|
194
|
+
source,
|
|
195
|
+
target,
|
|
196
|
+
flow,
|
|
197
|
+
batch_size=500,
|
|
198
|
+
train_steps=200,
|
|
199
|
+
lr=1e-3,
|
|
200
|
+
ladder=1,
|
|
201
|
+
mc_dt=1e-3,
|
|
202
|
+
mc_steps=50,
|
|
203
|
+
coeff_lambda=1.0,
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
log_weights = importance_weights_log(
|
|
207
|
+
x_valid, source, target, flow, type="G"
|
|
208
|
+
)
|
|
209
|
+
validation_ess = compute_ESS_log(log_weights)
|
|
210
|
+
y_valid = flow.inv(x_valid)
|
|
211
|
+
```
|
|
212
|
+
|
|
213
|
+
## Repository map
|
|
214
|
+
|
|
215
|
+
```text
|
|
216
|
+
jflows/
|
|
217
|
+
├── doc/
|
|
218
|
+
│ ├── README.md # detailed documentation map
|
|
219
|
+
│ ├── 01-low-level.md
|
|
220
|
+
│ ├── 02-medium-level.md
|
|
221
|
+
│ ├── 03-high-level.md
|
|
222
|
+
│ ├── 04-smoke-tests.md
|
|
223
|
+
│ └── 05-examples.md
|
|
224
|
+
├── jflows/
|
|
225
|
+
│ ├── flow.py # LOW
|
|
226
|
+
│ ├── potential.py # LOW
|
|
227
|
+
│ ├── loss.py # LOW
|
|
228
|
+
│ ├── utils/ # LOW
|
|
229
|
+
│ ├── train.py # MEDIUM
|
|
230
|
+
│ ├── artifacts.py # MEDIUM
|
|
231
|
+
│ ├── boltzmann/ # HIGH
|
|
232
|
+
│ └── core/ # private implementation
|
|
233
|
+
├── smoke/ # narrow executable contracts
|
|
234
|
+
├── example/ # complete workflows and results
|
|
235
|
+
└── README.md # this concise project introduction
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Read next
|
|
239
|
+
|
|
240
|
+
- **Detailed interface documentation:** [`doc/`](doc/README.md)
|
|
241
|
+
- **Executable API contracts:** [`smoke/`](smoke/)
|
|
242
|
+
- **Complete scripts, figures, and numerical results:**
|
|
243
|
+
[`example/`](example/) and [`example/results.md`](example/results.md)
|
|
244
|
+
|
|
245
|
+
The detailed documentation gives exact signatures, direction conventions,
|
|
246
|
+
return values, memory controls, stage records, and persistence behavior. Smoke
|
|
247
|
+
tests establish narrow software contracts; examples demonstrate end-to-end
|
|
248
|
+
scientific workflows.
|
|
249
|
+
|
|
250
|
+
## Acknowledgements
|
|
251
|
+
|
|
252
|
+
`jflows` is strongly inspired by [zuko](https://github.com/probabilists/zuko).
|
|
253
|
+
The private flow, transform, and masked-MLP machinery in `jflows.core` is a
|
|
254
|
+
stripped-down port of zuko's clean and composable transform design.
|
jflows-0.5.3/README.md
ADDED
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
<p align="center"><img src="jflows.png" alt="jflows banner" width="800px"></p>
|
|
2
|
+
<p align="center"><sub><em>Banner designed by ChatGPT: the character Jax from "The Amazing Digital Circus", a nod to jflows being built on Google JAX.</em></sub></p>
|
|
3
|
+
|
|
4
|
+
# jflows
|
|
5
|
+
|
|
6
|
+
`jflows` is a JAX/Equinox package for unconditional normalizing flows,
|
|
7
|
+
energy-based sampling, and staged Boltzmann generators. It provides a small
|
|
8
|
+
public API for building flows, defining unnormalized target energies, training
|
|
9
|
+
one transport stage, and connecting accepted stages into a complete source-to-
|
|
10
|
+
target sampler.
|
|
11
|
+
|
|
12
|
+
> **Status: experimental.** The package is developed and tested on Linux with
|
|
13
|
+
> NVIDIA CUDA JAX. JAX GPU is not supported on Windows, including WSL.
|
|
14
|
+
>
|
|
15
|
+
> This project was developed with Codex.
|
|
16
|
+
|
|
17
|
+
## Core features
|
|
18
|
+
|
|
19
|
+
- **First-class spline flows.** `NSF` models bounded Euclidean boxes and
|
|
20
|
+
`NCSF` models periodic boxes or tori. `CNF`, `OTFlow`, and `RealNVP` provide
|
|
21
|
+
complementary continuous and affine-coupling architectures.
|
|
22
|
+
- **One immutable flow interface.** Every flow provides `flow(x)`,
|
|
23
|
+
`flow.call_and_ladj(x)`, `flow.inv(y)`, and `flow.inv_and_ladj(y)` and works
|
|
24
|
+
as an Equinox pytree under JIT, vectorization, and differentiation.
|
|
25
|
+
- **Unified energy interface.** A `Potential` maps `[N,d]` samples to `[N]`
|
|
26
|
+
energies for densities proportional to `exp(-U)`. Built-in uniform, Gaussian,
|
|
27
|
+
and Gaussian-mixture potentials support explicit-key sampling, while
|
|
28
|
+
`potential_from` wraps a plain batched energy function.
|
|
29
|
+
- **Potential algebra.** Addition, subtraction, scaling, and
|
|
30
|
+
`linear_combination` construct bridge energies without introducing a second
|
|
31
|
+
target abstraction.
|
|
32
|
+
- **Explicit randomness.** Every random constructor and sampler takes a JAX
|
|
33
|
+
PRNG key. There is no package-global random state.
|
|
34
|
+
- **Sampling and diagnostics.** Importance weights, normalized ESS, coverage,
|
|
35
|
+
resampling, Langevin/MALA, stochastic Heun, HMC, SMC, flow-proposal AIS,
|
|
36
|
+
L-BFGS, AdamW, and quench-and-temper are available from `jflows.utils`.
|
|
37
|
+
- **Runtime backend report.** `jflows.backend()` lists CPU and installed JAX
|
|
38
|
+
accelerator plugins backed by visible hardware, identifies the configured
|
|
39
|
+
runtime, and reports JAX/Equinox versions and accelerator models without
|
|
40
|
+
creating a JAX device client or performing device computation.
|
|
41
|
+
- **Progressive training objectives.** Reverse KL, forward KL, KLX, and KLXX
|
|
42
|
+
share one validation convention while adding progressively richer
|
|
43
|
+
target-batch and density-ratio information.
|
|
44
|
+
- **Complete-stage persistence.** Boltzmann computation is separate from
|
|
45
|
+
optional stage writers/loaders, so an interrupted stage can be recomputed
|
|
46
|
+
from the last published population.
|
|
47
|
+
|
|
48
|
+
Flows are immutable. Identity initialization returns a new object:
|
|
49
|
+
|
|
50
|
+
```python
|
|
51
|
+
flow = flow.zeros()
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
`OTFlow` is the training exception: use `flow.near_identity()` so its
|
|
55
|
+
quadratic factor retains a nonzero gradient. Keep `OTFlow.zeros()` for an exact
|
|
56
|
+
identity map.
|
|
57
|
+
|
|
58
|
+
## Three-level interface
|
|
59
|
+
|
|
60
|
+
The public API is deliberately arranged from basic numerical components to a
|
|
61
|
+
complete staged generator.
|
|
62
|
+
|
|
63
|
+
### LOW — building blocks
|
|
64
|
+
|
|
65
|
+
The low level contains flows, potentials, per-sample losses, metrics, MCMC,
|
|
66
|
+
SMC/AIS, optimization, and quench-and-temper:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from jflows.flow import NSF, NCSF, CNF, OTFlow, RealNVP
|
|
70
|
+
from jflows.potential import Potential, Nlog_Gaussian, potential_from
|
|
71
|
+
from jflows.loss import reverse_KL_F, forward_KL_G, forward_KLX_G, forward_X_G
|
|
72
|
+
from jflows import backend
|
|
73
|
+
from jflows.utils import compute_ESS_log, importance_weights_log, langevin, smc
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
`F` denotes the source-to-target map and `G = F^{-1}` the target-to-source
|
|
77
|
+
map. An `_F` or `_G` suffix fixes the native training direction.
|
|
78
|
+
|
|
79
|
+
### MEDIUM — one-stage training
|
|
80
|
+
|
|
81
|
+
The medium level trains one map between a source and target potential:
|
|
82
|
+
|
|
83
|
+
```python
|
|
84
|
+
from jflows.train import (
|
|
85
|
+
Monitor,
|
|
86
|
+
train_reverse_KL_F,
|
|
87
|
+
train_forward_KL_G,
|
|
88
|
+
train_forward_KLX_G,
|
|
89
|
+
train_forward_KLXX_G,
|
|
90
|
+
)
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Every trainer returns a new flow and its optimizer-batch ESS history. Simple
|
|
94
|
+
flow, sample, and history serialization lives in `jflows.artifacts`.
|
|
95
|
+
|
|
96
|
+
### HIGH — staged Boltzmann generation
|
|
97
|
+
|
|
98
|
+
The high level connects accepted bridge stages and compares every trained map
|
|
99
|
+
with an exact identity fallback using full-validation incremental ESS:
|
|
100
|
+
|
|
101
|
+
```python
|
|
102
|
+
from jflows.boltzmann import (
|
|
103
|
+
boltzmann_identity,
|
|
104
|
+
boltzmann_reverse_KL_F,
|
|
105
|
+
boltzmann_forward_KL_G,
|
|
106
|
+
boltzmann_forward_KLX_G,
|
|
107
|
+
boltzmann_forward_KLXX_G,
|
|
108
|
+
)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
- identity runs the adaptive-staging controller for stage selection, ESS
|
|
112
|
+
gating, resampling, and rejuvenation with no flow or optimizer;
|
|
113
|
+
- reverse KL is the simplest source-sampled F baseline;
|
|
114
|
+
- forward KL manufactures target-side batches and trains G;
|
|
115
|
+
- KLX adds target-measure log-weight variation control; and
|
|
116
|
+
- KLXX adds a quench-and-temper/proposal mixture for wider mode and leakage
|
|
117
|
+
regularization.
|
|
118
|
+
|
|
119
|
+
The four trained generators compare their candidate with identity. The
|
|
120
|
+
standalone `boltzmann_identity` function instead advances every accepted
|
|
121
|
+
stage by exact identity reweighting, so it requires no flow, batch size,
|
|
122
|
+
training steps, or learning rate. Complete-stage persistence is exposed
|
|
123
|
+
separately through `jflows.boltzmann.write` and `jflows.boltzmann.load`.
|
|
124
|
+
|
|
125
|
+
## Minimal setup
|
|
126
|
+
|
|
127
|
+
```bash
|
|
128
|
+
pip install "jax[cuda13]" equinox
|
|
129
|
+
pip install -e .
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
```python
|
|
133
|
+
import jflows
|
|
134
|
+
|
|
135
|
+
jflows.backend()
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
A minimal training setup is:
|
|
139
|
+
|
|
140
|
+
```python
|
|
141
|
+
import jax
|
|
142
|
+
|
|
143
|
+
from jflows.flow import NSF
|
|
144
|
+
from jflows.potential import Nlog_Gaussian, potential_from
|
|
145
|
+
from jflows.train import train_forward_KLX_G
|
|
146
|
+
from jflows.utils import compute_ESS_log, importance_weights_log
|
|
147
|
+
|
|
148
|
+
source = Nlog_Gaussian([0.0, 0.0], [1.0, 1.0])
|
|
149
|
+
target = potential_from(
|
|
150
|
+
lambda x: 10.0 * (jax.numpy.linalg.norm(x, axis=-1) - 2.0) ** 2
|
|
151
|
+
)
|
|
152
|
+
|
|
153
|
+
flow = NSF(
|
|
154
|
+
jax.random.key(0),
|
|
155
|
+
[-4.0, -4.0],
|
|
156
|
+
[4.0, 4.0],
|
|
157
|
+
bins=16,
|
|
158
|
+
transforms=4,
|
|
159
|
+
).zeros()
|
|
160
|
+
x_valid = source.samples(jax.random.key(1), 20000)
|
|
161
|
+
|
|
162
|
+
flow, batch_ess = train_forward_KLX_G(
|
|
163
|
+
x_valid,
|
|
164
|
+
source,
|
|
165
|
+
target,
|
|
166
|
+
flow,
|
|
167
|
+
batch_size=500,
|
|
168
|
+
train_steps=200,
|
|
169
|
+
lr=1e-3,
|
|
170
|
+
ladder=1,
|
|
171
|
+
mc_dt=1e-3,
|
|
172
|
+
mc_steps=50,
|
|
173
|
+
coeff_lambda=1.0,
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
log_weights = importance_weights_log(
|
|
177
|
+
x_valid, source, target, flow, type="G"
|
|
178
|
+
)
|
|
179
|
+
validation_ess = compute_ESS_log(log_weights)
|
|
180
|
+
y_valid = flow.inv(x_valid)
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## Repository map
|
|
184
|
+
|
|
185
|
+
```text
|
|
186
|
+
jflows/
|
|
187
|
+
├── doc/
|
|
188
|
+
│ ├── README.md # detailed documentation map
|
|
189
|
+
│ ├── 01-low-level.md
|
|
190
|
+
│ ├── 02-medium-level.md
|
|
191
|
+
│ ├── 03-high-level.md
|
|
192
|
+
│ ├── 04-smoke-tests.md
|
|
193
|
+
│ └── 05-examples.md
|
|
194
|
+
├── jflows/
|
|
195
|
+
│ ├── flow.py # LOW
|
|
196
|
+
│ ├── potential.py # LOW
|
|
197
|
+
│ ├── loss.py # LOW
|
|
198
|
+
│ ├── utils/ # LOW
|
|
199
|
+
│ ├── train.py # MEDIUM
|
|
200
|
+
│ ├── artifacts.py # MEDIUM
|
|
201
|
+
│ ├── boltzmann/ # HIGH
|
|
202
|
+
│ └── core/ # private implementation
|
|
203
|
+
├── smoke/ # narrow executable contracts
|
|
204
|
+
├── example/ # complete workflows and results
|
|
205
|
+
└── README.md # this concise project introduction
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Read next
|
|
209
|
+
|
|
210
|
+
- **Detailed interface documentation:** [`doc/`](doc/README.md)
|
|
211
|
+
- **Executable API contracts:** [`smoke/`](smoke/)
|
|
212
|
+
- **Complete scripts, figures, and numerical results:**
|
|
213
|
+
[`example/`](example/) and [`example/results.md`](example/results.md)
|
|
214
|
+
|
|
215
|
+
The detailed documentation gives exact signatures, direction conventions,
|
|
216
|
+
return values, memory controls, stage records, and persistence behavior. Smoke
|
|
217
|
+
tests establish narrow software contracts; examples demonstrate end-to-end
|
|
218
|
+
scientific workflows.
|
|
219
|
+
|
|
220
|
+
## Acknowledgements
|
|
221
|
+
|
|
222
|
+
`jflows` is strongly inspired by [zuko](https://github.com/probabilists/zuko).
|
|
223
|
+
The private flow, transform, and masked-MLP machinery in `jflows.core` is a
|
|
224
|
+
stripped-down port of zuko's clean and composable transform design.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
"""jflows — JAX normalizing flows for unconditional energy-based sampling.
|
|
2
|
+
|
|
3
|
+
Built on JAX and equinox.
|
|
4
|
+
|
|
5
|
+
Public surface:
|
|
6
|
+
|
|
7
|
+
jflows.flow : Flow, NSF, NCSF, CNF, OTFlow, RealNVP, ComposedTransform
|
|
8
|
+
jflows.potential : Potential, potential_from, Nlog_Uniform, Nlog_Gaussian,
|
|
9
|
+
Nlog_Gaussian_Mixture, linear_combination (+ the operator
|
|
10
|
+
algebra c*U, U+V, U-V, -U, U/c, sum([...]))
|
|
11
|
+
jflows.loss : reverse_KL_F, forward_KL_G, forward_KLX_G,
|
|
12
|
+
forward_X_G
|
|
13
|
+
jflows.train : Monitor and all train_* stage drivers
|
|
14
|
+
jflows.boltzmann : adaptive-staging and fixed-schedule boltzmann_* generators
|
|
15
|
+
jflows.artifacts : save and load medium-level training outputs
|
|
16
|
+
jflows.backend : selected/available JAX backends and accelerator model
|
|
17
|
+
jflows.utils : metrics / optimization / rejuvenation / anneal / quench
|
|
18
|
+
|
|
19
|
+
Internals (`jflows.core.*`) adapt zuko's clean flow and transform design.
|
|
20
|
+
The public computation API uses explicit PRNG keys and takes `Flow` objects
|
|
21
|
+
directly for losses, importance weights, AIS, and training.
|
|
22
|
+
"""
|
|
23
|
+
|
|
24
|
+
from importlib.metadata import entry_points
|
|
25
|
+
from importlib.util import find_spec
|
|
26
|
+
import os
|
|
27
|
+
from shutil import which
|
|
28
|
+
import subprocess
|
|
29
|
+
|
|
30
|
+
import equinox
|
|
31
|
+
import jax
|
|
32
|
+
|
|
33
|
+
from . import artifacts, boltzmann, flow, loss, potential, train, utils
|
|
34
|
+
from .version import __version__
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def backend():
|
|
38
|
+
"""Print the available JAX backends and selected accelerator."""
|
|
39
|
+
plugins = tuple(item.name.lower() for item in entry_points(group="jax_plugins"))
|
|
40
|
+
available = ["CPU"]
|
|
41
|
+
details = ["- CPU — cpu"]
|
|
42
|
+
|
|
43
|
+
cuda_visible = os.environ.get("CUDA_VISIBLE_DEVICES") not in ("", "-1")
|
|
44
|
+
if cuda_visible and any("cuda" in name for name in plugins) and which("nvidia-smi"):
|
|
45
|
+
result = subprocess.run(
|
|
46
|
+
["nvidia-smi", "--query-gpu=name", "--format=csv,noheader"],
|
|
47
|
+
capture_output=True,
|
|
48
|
+
text=True,
|
|
49
|
+
)
|
|
50
|
+
models = tuple(
|
|
51
|
+
dict.fromkeys(line for line in result.stdout.splitlines() if line)
|
|
52
|
+
)
|
|
53
|
+
if result.returncode == 0 and models:
|
|
54
|
+
available.append("CUDA")
|
|
55
|
+
details.append(f"- CUDA — {', '.join(models)}")
|
|
56
|
+
|
|
57
|
+
rocm_visible = os.environ.get("ROCR_VISIBLE_DEVICES") not in ("", "-1")
|
|
58
|
+
if rocm_visible and any("rocm" in name for name in plugins) and which("rocm-smi"):
|
|
59
|
+
available.append("ROCm")
|
|
60
|
+
details.append("- ROCm")
|
|
61
|
+
|
|
62
|
+
if find_spec("libtpu"):
|
|
63
|
+
available.append("TPU")
|
|
64
|
+
details.append("- TPU")
|
|
65
|
+
|
|
66
|
+
configured = os.environ.get("JAX_PLATFORMS", "").split(",")[0].lower()
|
|
67
|
+
selected = {"cpu": "CPU", "cuda": "CUDA", "rocm": "ROCm", "tpu": "TPU"}.get(
|
|
68
|
+
configured
|
|
69
|
+
)
|
|
70
|
+
if selected not in available:
|
|
71
|
+
selected = next(
|
|
72
|
+
name for name in ("CUDA", "ROCm", "TPU", "CPU") if name in available
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
print(f"JAX {jax.__version__}")
|
|
76
|
+
print(f"Equinox {equinox.__version__}")
|
|
77
|
+
print(f"Selected backend: {selected}")
|
|
78
|
+
print(f"Available backends: {', '.join(available)}")
|
|
79
|
+
for detail in details:
|
|
80
|
+
print(detail)
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
__all__ = [
|
|
84
|
+
"__version__",
|
|
85
|
+
"artifacts",
|
|
86
|
+
"backend",
|
|
87
|
+
"boltzmann",
|
|
88
|
+
"flow",
|
|
89
|
+
"loss",
|
|
90
|
+
"potential",
|
|
91
|
+
"train",
|
|
92
|
+
"utils",
|
|
93
|
+
]
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Save and load medium-level training outputs."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import equinox as eqx
|
|
6
|
+
import numpy as np
|
|
7
|
+
|
|
8
|
+
__all__ = [
|
|
9
|
+
"load_flow",
|
|
10
|
+
"load_history",
|
|
11
|
+
"load_samples",
|
|
12
|
+
"save_flow",
|
|
13
|
+
"save_history",
|
|
14
|
+
"save_samples",
|
|
15
|
+
]
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def save_flow(path, flow) -> None:
|
|
19
|
+
"""Save one flow's array leaves."""
|
|
20
|
+
eqx.tree_serialise_leaves(Path(path), flow)
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
def load_flow(path, template):
|
|
24
|
+
"""Load flow leaves into a matching template."""
|
|
25
|
+
return eqx.tree_deserialise_leaves(Path(path), template)
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def save_samples(path, samples) -> None:
|
|
29
|
+
"""Save one sample array."""
|
|
30
|
+
np.save(Path(path), np.asarray(samples), allow_pickle=False)
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def load_samples(path):
|
|
34
|
+
"""Load one sample array."""
|
|
35
|
+
return np.load(Path(path), allow_pickle=False)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def save_history(path, **history) -> None:
|
|
39
|
+
"""Save named training-history arrays."""
|
|
40
|
+
np.savez(Path(path), **history)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
def load_history(path) -> dict:
|
|
44
|
+
"""Load named training-history arrays."""
|
|
45
|
+
with np.load(Path(path), allow_pickle=False) as history:
|
|
46
|
+
return {name: history[name].copy() for name in history.files}
|