pdft 0.2.1__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.
- pdft-0.2.1/.claude/CLAUDE.md +184 -0
- pdft-0.2.1/.github/workflows/ci.yml +26 -0
- pdft-0.2.1/.github/workflows/publish.yml +63 -0
- pdft-0.2.1/.github/workflows/verify-upstream-pin.yml +28 -0
- pdft-0.2.1/.gitignore +59 -0
- pdft-0.2.1/.pre-commit-config.yaml +7 -0
- pdft-0.2.1/LICENSE +25 -0
- pdft-0.2.1/Makefile +8 -0
- pdft-0.2.1/PKG-INFO +100 -0
- pdft-0.2.1/README.md +41 -0
- pdft-0.2.1/examples/basis_demo.py +49 -0
- pdft-0.2.1/examples/mera_demo.py +51 -0
- pdft-0.2.1/examples/optimizer_benchmark.py +50 -0
- pdft-0.2.1/pyproject.toml +63 -0
- pdft-0.2.1/reference/README.md +27 -0
- pdft-0.2.1/reference/goldens/adam_trajectory_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/basis_roundtrip.npz +0 -0
- pdft-0.2.1/reference/goldens/basis_roundtrip_hash.txt +1 -0
- pdft-0.2.1/reference/goldens/compression_roundtrip_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/entangled_qft_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/ft_mat_roundtrip.npz +0 -0
- pdft-0.2.1/reference/goldens/loss_values.npz +0 -0
- pdft-0.2.1/reference/goldens/manifest.json +24 -0
- pdft-0.2.1/reference/goldens/manifold_project_retract.npz +0 -0
- pdft-0.2.1/reference/goldens/mera_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/phase_extraction_3x3.npz +0 -0
- pdft-0.2.1/reference/goldens/qft_basis_trained.json +117 -0
- pdft-0.2.1/reference/goldens/qft_code_16x16.npz +0 -0
- pdft-0.2.1/reference/goldens/qft_code_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/tebd_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/train_trajectory_4x4.npz +0 -0
- pdft-0.2.1/reference/goldens/train_trajectory_4x4_long.npz +0 -0
- pdft-0.2.1/reference/julia/Manifest.toml +2244 -0
- pdft-0.2.1/reference/julia/Project.toml +13 -0
- pdft-0.2.1/reference/julia/generate_goldens.jl +328 -0
- pdft-0.2.1/src/pdft/__init__.py +86 -0
- pdft-0.2.1/src/pdft/bases/__init__.py +40 -0
- pdft-0.2.1/src/pdft/bases/base.py +470 -0
- pdft-0.2.1/src/pdft/bases/block/__init__.py +12 -0
- pdft-0.2.1/src/pdft/bases/block/block.py +261 -0
- pdft-0.2.1/src/pdft/bases/block/real_rich.py +164 -0
- pdft-0.2.1/src/pdft/bases/block/rich.py +265 -0
- pdft-0.2.1/src/pdft/bases/circuit/__init__.py +15 -0
- pdft-0.2.1/src/pdft/bases/circuit/entangled_qft.py +137 -0
- pdft-0.2.1/src/pdft/bases/circuit/mera.py +167 -0
- pdft-0.2.1/src/pdft/bases/circuit/qft.py +84 -0
- pdft-0.2.1/src/pdft/bases/circuit/tebd.py +130 -0
- pdft-0.2.1/src/pdft/circuit/__init__.py +34 -0
- pdft-0.2.1/src/pdft/circuit/builder.py +242 -0
- pdft-0.2.1/src/pdft/circuit/cache.py +52 -0
- pdft-0.2.1/src/pdft/io/__init__.py +39 -0
- pdft-0.2.1/src/pdft/io/compression.py +189 -0
- pdft-0.2.1/src/pdft/io/serialize.py +221 -0
- pdft-0.2.1/src/pdft/loss.py +159 -0
- pdft-0.2.1/src/pdft/manifolds.py +350 -0
- pdft-0.2.1/src/pdft/optimizers/__init__.py +19 -0
- pdft-0.2.1/src/pdft/optimizers/adam.py +86 -0
- pdft-0.2.1/src/pdft/optimizers/core.py +61 -0
- pdft-0.2.1/src/pdft/optimizers/gd.py +68 -0
- pdft-0.2.1/src/pdft/optimizers/loop.py +104 -0
- pdft-0.2.1/src/pdft/profiling.py +292 -0
- pdft-0.2.1/src/pdft/training/__init__.py +19 -0
- pdft-0.2.1/src/pdft/training/adam_step.py +131 -0
- pdft-0.2.1/src/pdft/training/batched.py +341 -0
- pdft-0.2.1/src/pdft/training/eval_loop.py +57 -0
- pdft-0.2.1/src/pdft/training/result.py +17 -0
- pdft-0.2.1/src/pdft/training/schedules.py +27 -0
- pdft-0.2.1/src/pdft/training/single.py +69 -0
- pdft-0.2.1/src/pdft/viz/__init__.py +24 -0
- pdft-0.2.1/src/pdft/viz/circuit.py +85 -0
- pdft-0.2.1/src/pdft/viz/loss.py +119 -0
- pdft-0.2.1/tests/__init__.py +0 -0
- pdft-0.2.1/tests/bases/__init__.py +0 -0
- pdft-0.2.1/tests/bases/block/__init__.py +0 -0
- pdft-0.2.1/tests/bases/block/test_block.py +217 -0
- pdft-0.2.1/tests/bases/block/test_real_rich.py +132 -0
- pdft-0.2.1/tests/bases/block/test_rich.py +211 -0
- pdft-0.2.1/tests/bases/circuit/__init__.py +0 -0
- pdft-0.2.1/tests/bases/circuit/test_qft.py +90 -0
- pdft-0.2.1/tests/bases/conftest.py +11 -0
- pdft-0.2.1/tests/bases/test_base.py +66 -0
- pdft-0.2.1/tests/bases/test_new_bases.py +170 -0
- pdft-0.2.1/tests/bases/test_phase_extraction.py +63 -0
- pdft-0.2.1/tests/circuit/__init__.py +0 -0
- pdft-0.2.1/tests/circuit/test_cache.py +23 -0
- pdft-0.2.1/tests/conftest.py +31 -0
- pdft-0.2.1/tests/io/__init__.py +0 -0
- pdft-0.2.1/tests/io/conftest.py +9 -0
- pdft-0.2.1/tests/io/test_compression.py +101 -0
- pdft-0.2.1/tests/io/test_serialize.py +106 -0
- pdft-0.2.1/tests/optimizers/__init__.py +0 -0
- pdft-0.2.1/tests/optimizers/conftest.py +19 -0
- pdft-0.2.1/tests/optimizers/test_adam.py +65 -0
- pdft-0.2.1/tests/optimizers/test_gd.py +57 -0
- pdft-0.2.1/tests/parity/__init__.py +0 -0
- pdft-0.2.1/tests/parity/conftest.py +21 -0
- pdft-0.2.1/tests/parity/test_adam.py +41 -0
- pdft-0.2.1/tests/parity/test_compression.py +48 -0
- pdft-0.2.1/tests/parity/test_long_run.py +65 -0
- pdft-0.2.1/tests/parity/test_loss.py +16 -0
- pdft-0.2.1/tests/parity/test_manifolds.py +20 -0
- pdft-0.2.1/tests/parity/test_new_bases.py +53 -0
- pdft-0.2.1/tests/parity/test_qft.py +54 -0
- pdft-0.2.1/tests/parity/test_scale.py +28 -0
- pdft-0.2.1/tests/parity/test_serialize.py +50 -0
- pdft-0.2.1/tests/parity/test_training.py +41 -0
- pdft-0.2.1/tests/test_basis_seeded_init.py +118 -0
- pdft-0.2.1/tests/test_loss.py +45 -0
- pdft-0.2.1/tests/test_manifolds.py +129 -0
- pdft-0.2.1/tests/test_profiling.py +198 -0
- pdft-0.2.1/tests/test_smoke.py +11 -0
- pdft-0.2.1/tests/training/__init__.py +0 -0
- pdft-0.2.1/tests/training/conftest.py +16 -0
- pdft-0.2.1/tests/training/test_batched.py +275 -0
- pdft-0.2.1/tests/training/test_eval_loop.py +106 -0
- pdft-0.2.1/tests/training/test_integration.py +58 -0
- pdft-0.2.1/tests/training/test_single.py +60 -0
- pdft-0.2.1/tests/viz/__init__.py +0 -0
- pdft-0.2.1/tests/viz/test_loss.py +44 -0
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Project-specific guidance for Claude Code working in this repo. Read this before making non-trivial changes — several conventions are *not* discoverable from the code without painful debugging, and at least one is documented here only because the original Python port spent hours rediscovering it.
|
|
4
|
+
|
|
5
|
+
## What this is
|
|
6
|
+
|
|
7
|
+
`pdft` is a faithful Python port of [ParametricDFT.jl](https://github.com/nzy1997/ParametricDFT.jl) using JAX. Goal: a Python user can reproduce Julia results bit-for-bit (or within documented tolerances) on the same input. **Behavior parity with Julia is the primary correctness criterion** — never sacrifice it for Pythonic-ness.
|
|
8
|
+
|
|
9
|
+
- Design spec: `docs/superpowers/specs/2026-04-24-pdft-migration-design.md`
|
|
10
|
+
- Roadmap: GitHub issue #1
|
|
11
|
+
- Reliability hardening backlog: GitHub issue #2
|
|
12
|
+
- Upstream is pinned to `nzy1997/ParametricDFT.jl@a201a27e47df2f0f3ab460f83d49b6e5f5d1e9ef`. The pin lives in two places that must stay in sync: `reference/julia/generate_goldens.jl` (`UPSTREAM_SHA`) and `src/pdft/__init__.py` (`__upstream_ref__`).
|
|
13
|
+
|
|
14
|
+
## Critical conventions (do not break)
|
|
15
|
+
|
|
16
|
+
These are the non-obvious invariants that took the original port multiple iterations to find. Violating any of them silently breaks parity with Julia.
|
|
17
|
+
|
|
18
|
+
### 1. Wirtinger gradient conjugation
|
|
19
|
+
|
|
20
|
+
**JAX returns `∂f/∂z̄` while Julia's Zygote returns `∂f/∂z`** for real-valued functions of complex inputs. They are complex conjugates. `optimize()` in `src/pdft/optimizers.py` conjugates `raw_grads` immediately after `grad_fn(...)`:
|
|
21
|
+
|
|
22
|
+
```python
|
|
23
|
+
raw_grads = grad_fn(state.current_tensors)
|
|
24
|
+
raw_grads = [jnp.conj(g) for g in raw_grads] # MUST stay
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
Without this line, GD trajectories drift ~10% over 50 steps and Adam outright diverges. Any new optimizer added to this module must apply the same conjugation, or define a custom JAX `vjp` that matches Julia's convention.
|
|
28
|
+
|
|
29
|
+
### 2. Yao little-endian qubit ordering
|
|
30
|
+
|
|
31
|
+
`pic.reshape((2,)*(m+n))` gives axes in big-endian bit order, but Julia's Yao treats qubit 1 as the LSB. `_circuit.build_circuit_einsum` reverses qubit-to-axis mapping within each block:
|
|
32
|
+
|
|
33
|
+
```python
|
|
34
|
+
row_pic = [input_labels[q - 1] for q in range(m, 0, -1)] # axis 0 ↔ qubit m
|
|
35
|
+
col_pic = [input_labels[q - 1] for q in range(m + n, m, -1)]
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Same reversal applies to `out_labels`. Don't undo this without first making `ft_mat` parity tests fail in a controlled way.
|
|
39
|
+
|
|
40
|
+
### 3. Compact 2×2 CP tensor representation
|
|
41
|
+
|
|
42
|
+
A controlled-phase gate is `diag(1, 1, 1, exp(iφ))` as a 4×4 matrix, but Yao's `yao2einsum` emits the **compact 2×2 form**:
|
|
43
|
+
|
|
44
|
+
```python
|
|
45
|
+
controlled_phase_diag(φ) = [[1, 1], [1, exp(iφ)]]
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
This shares the wire labels of control and target qubits and does NOT introduce new labels (the gate is diagonal so each wire's value is unchanged; only a multiplicative phase is injected). All circuit modules use this representation; never substitute the 4×4 form even though it would be mathematically equivalent — Julia goldens won't match.
|
|
49
|
+
|
|
50
|
+
### 4. Hadamard-first tensor sort
|
|
51
|
+
|
|
52
|
+
Julia's `qft_code` does `perm_vec = sortperm(tn.tensors, by=x -> !(x ≈ mat(H)))` — Hadamards come first, CPs after. Python's `_circuit.build_circuit_einsum` applies the same sort. The einsum is rebuilt with the permuted tensor list AND permuted subscripts so it stays valid. **JSON serialization assumes this order**; cross-language interop breaks if you change it.
|
|
53
|
+
|
|
54
|
+
### 5. JAX x64 mode at import time
|
|
55
|
+
|
|
56
|
+
`src/pdft/__init__.py` calls `jax.config.update("jax_enable_x64", True)` before any other import. `tests/conftest.py` does the same. Without x64, JAX uses complex64 / float32 and parity tolerances become unreachable. Any new test file that uses `jax.numpy` directly without importing `pdft` first must include the same call (or import pdft to force it).
|
|
57
|
+
|
|
58
|
+
### 6. Column-major iteration for cross-language hashes
|
|
59
|
+
|
|
60
|
+
`io_json.basis_hash` and `compression` use **Julia 1-based column-major** flat indices. NumPy is row-major by default. Use `array.flatten(order="F")` or `np.unravel_index(..., order="F")`. Never use plain `array.flatten()` for serialization.
|
|
61
|
+
|
|
62
|
+
### 7. Julia-compatible float formatting
|
|
63
|
+
|
|
64
|
+
Python's `repr(5e-7)` gives `"5e-07"`; Julia's `string(5e-7)` gives `"5.0e-7"`. `io_json._format_float_julia_like` post-processes `repr()` to match Julia. Use it (not `repr` or `str`) for any value that participates in a cross-language hash or JSON byte-comparison.
|
|
65
|
+
|
|
66
|
+
### 8. L1-cusp horizon for trajectory parity
|
|
67
|
+
|
|
68
|
+
GD on L1 loss is bit-exact for ~50 steps and stays within `atol=1e-3` over 200 steps. Beyond that, FP accumulation eventually pushes Python and Julia across the same loss cusp at slightly different alphas in Armijo line search. Both still converge to the same loss basin. Don't tighten `tests/test_parity_long_run.py` to `atol=1e-10` over 200 steps — this is mathematically expected on non-smooth losses, not a bug. Smooth losses (MSE without truncation) don't have this problem.
|
|
69
|
+
|
|
70
|
+
### 9. Phase extractors classify by tensor shape, not gate-list metadata
|
|
71
|
+
|
|
72
|
+
`get_*_gate_indices` walks the tensor list and tags any 2×2 tensor whose four entries have unit-modulus magnitudes (within `atol=0.15`) as a CP gate, then returns the LAST `n_gates` such positions. After training, individual entries can drift slightly off the unit circle; the moderate tolerance accommodates that. If you tighten the tolerance, do so in tandem with a regression test on a trained basis.
|
|
73
|
+
|
|
74
|
+
## Repo layout
|
|
75
|
+
|
|
76
|
+
```
|
|
77
|
+
src/pdft/
|
|
78
|
+
├── manifolds.py Mathematical core (UnitaryManifold, PhaseManifold, batched ops)
|
|
79
|
+
├── loss.py L1Norm, MSELoss, topk_truncate, loss_function (public)
|
|
80
|
+
├── profiling.py Cross-cutting profiling helpers
|
|
81
|
+
├── bases/
|
|
82
|
+
│ ├── base.py AbstractSparseBasis + bases_allclose + 4 basis dataclasses
|
|
83
|
+
│ ├── circuit/ QFT, EntangledQFT, TEBD, MERA (full circuit topologies)
|
|
84
|
+
│ └── block/ Blocked, Rich, RealRich (parameter-efficient)
|
|
85
|
+
├── circuit/ Einsum builder (builder.py) + JIT closure cache (cache.py)
|
|
86
|
+
├── optimizers/ core, gd (RiemannianGD + Armijo), adam (RiemannianAdam), loop
|
|
87
|
+
├── training/ schedules, single (train_basis), batched, adam_step, eval_loop
|
|
88
|
+
├── io/ serialize (JSON), compression
|
|
89
|
+
└── viz/ loss (matplotlib loss plots), circuit (schematic)
|
|
90
|
+
|
|
91
|
+
reference/julia/ Julia harness — needed only to regenerate goldens
|
|
92
|
+
reference/goldens/ Committed .npz + .json files (<200 KB total)
|
|
93
|
+
examples/ 3 runnable demos, each <10s
|
|
94
|
+
tests/ pytest; mirrors src/pdft/ layout (tests/bases/, tests/optimizers/, ...)
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Benchmarks live in a separate repo: https://github.com/zazabap/pdft-benchmarks (split from this repo at pdft v0.2.0; pinned via its `pyproject.toml`).
|
|
98
|
+
|
|
99
|
+
`DCTBasis` was removed during the modular-src refactor (PR #11). Use a parametric basis (`RichBasis` / `RealRichBasis`) for similar use cases.
|
|
100
|
+
|
|
101
|
+
`pdft/__init__.py` re-exports a small set of most-used names (basis classes,
|
|
102
|
+
trainer, optimizer, loss). Less-public names live in their subpackages —
|
|
103
|
+
e.g. `pdft.io.save_basis`, `pdft.manifolds.UnitaryManifold`,
|
|
104
|
+
`pdft.profiling.profile_training`, `pdft.bases.circuit.qft_code`.
|
|
105
|
+
|
|
106
|
+
`AbstractRiemannianOptimizer` is `RiemannianGD | RiemannianAdam` — a structural union, not a Protocol. Adding a third optimizer means extending that union *and* adding an `isinstance` branch in `optimize()`.
|
|
107
|
+
|
|
108
|
+
`train_basis` is generic over basis type via JAX pytree flatten/unflatten. The convention: each registered basis pytree must have leaves ordered as `tuple(tensors) + tuple(inv_tensors)`, with everything else (m, n, code, inv_code, counts) in aux data. New basis types must follow this convention.
|
|
109
|
+
|
|
110
|
+
## Dev workflow
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
# Install (Python 3.11+; conda env at /opt/conda/envs/pdft on the dev box)
|
|
114
|
+
pip install -e ".[dev]"
|
|
115
|
+
|
|
116
|
+
# Run tests
|
|
117
|
+
pytest # full suite
|
|
118
|
+
pytest --cov=pdft --cov-fail-under=90 # CI gate
|
|
119
|
+
pytest tests/test_parity_*.py # parity-only
|
|
120
|
+
|
|
121
|
+
# Lint (CI fails if this is dirty — check before pushing)
|
|
122
|
+
ruff check src tests
|
|
123
|
+
ruff format src tests
|
|
124
|
+
|
|
125
|
+
# Run examples
|
|
126
|
+
python examples/basis_demo.py
|
|
127
|
+
python examples/optimizer_benchmark.py
|
|
128
|
+
python examples/mera_demo.py
|
|
129
|
+
|
|
130
|
+
# Regenerate Julia goldens (requires Julia 1.10+)
|
|
131
|
+
make goldens
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
After regenerating goldens: also update `__upstream_ref__` in `src/pdft/__init__.py` to match `manifest.json`'s `upstream_sha`.
|
|
135
|
+
|
|
136
|
+
## Tests and parity
|
|
137
|
+
|
|
138
|
+
Three layers (per spec section 7):
|
|
139
|
+
|
|
140
|
+
1. **Parity tests** (`tests/test_parity_*.py`) — load committed `.npz` / `.json` goldens from `reference/goldens/` and assert Python matches Julia. These are the load-bearing correctness tests.
|
|
141
|
+
2. **Property tests** (`tests/test_<module>.py`) — math-invariant checks (unitarity preserved, round-trip identity, monotone descent, …). Don't depend on Julia.
|
|
142
|
+
3. **Smoke / integration** (`tests/test_smoke.py`, `tests/test_training_integration.py`).
|
|
143
|
+
|
|
144
|
+
Coverage gate is `--cov-fail-under=90`. Don't add tests that reduce per-module coverage below the line; if a new module legitimately needs more code, also add the property tests for it.
|
|
145
|
+
|
|
146
|
+
`tests/conftest.py` enables x64. Don't import jax in a test before pdft unless the test also calls `jax.config.update("jax_enable_x64", True)`.
|
|
147
|
+
|
|
148
|
+
## When to break parity
|
|
149
|
+
|
|
150
|
+
The default answer is "never". The only acceptable cases:
|
|
151
|
+
|
|
152
|
+
- **Float-precision noise**: test tolerances are documented per case; if you cross a tolerance boundary, investigate before relaxing.
|
|
153
|
+
- **Tie-breaking divergences** (e.g. argpartition vs partialsortperm): document and assert downstream behavior matches instead.
|
|
154
|
+
|
|
155
|
+
If you find another mismatch:
|
|
156
|
+
1. Diagnose with `reference/julia/` (write a small Julia script to dump intermediate values).
|
|
157
|
+
2. Compare to Python at the same intermediate point.
|
|
158
|
+
3. **The bug is in Python**, almost always.
|
|
159
|
+
4. If the bug is in upstream Julia, file an issue there and document the divergence in this file.
|
|
160
|
+
|
|
161
|
+
## CI gotchas
|
|
162
|
+
|
|
163
|
+
- Ruff is strict (`F401`, `F841`, `E741`, `E731` all errors). Auto-`--fix` handles most.
|
|
164
|
+
- Coverage gate runs only on the actual test suite, not examples; don't rely on examples for coverage.
|
|
165
|
+
- The `verify-upstream-pin.yml` workflow runs only on PRs touching `reference/`. It uses the GitHub API to confirm the pinned sha exists in upstream — don't push a sha that's only in a fork.
|
|
166
|
+
- The matrix is 3.11 / 3.12 / 3.13. JAX dropped 3.10 in 0.10.0; do not lower the floor.
|
|
167
|
+
|
|
168
|
+
## What NOT to do
|
|
169
|
+
|
|
170
|
+
- **Don't add `optax`** as a dependency. The optimizer logic is hand-rolled to match Julia's exact moment-update math. `optax`'s defaults and FP order will diverge from Julia.
|
|
171
|
+
- **Don't switch from `jnp.einsum_path("greedy")` to `"optimal"`.** "Optimal" is exponential in tensor count and hangs on the 3×3 QFT (12 tensors).
|
|
172
|
+
- **Don't add explicit JIT to `train_basis`.** It calls a basis-typed loss closure with Python-list pytrees; JIT decisions are best left to inner functions where the static-vs-leaf split is clearer.
|
|
173
|
+
- **Don't introduce backwards-compat shims** for the JSON schema. We're at v0.1.0; if the schema changes, bump the version and regenerate goldens.
|
|
174
|
+
- **Don't add ML scaffolding** (no DataLoader, no Trainer-like classes, no Lightning). Upstream is one-target-image-at-a-time and we mirror that. Batched training is open work in #2.
|
|
175
|
+
- **Don't run examples in CI.** They write to `out/` (gitignored) and are not coverage-relevant.
|
|
176
|
+
|
|
177
|
+
## When making changes
|
|
178
|
+
|
|
179
|
+
1. Make the change.
|
|
180
|
+
2. Run `ruff check src tests` and `ruff format src tests`.
|
|
181
|
+
3. Run `pytest --cov=pdft --cov-fail-under=90`.
|
|
182
|
+
4. If you touched a circuit, basis, optimizer, or io_json module: also run the relevant `tests/test_parity_*.py` to confirm Julia-parity hasn't drifted.
|
|
183
|
+
5. If you changed the upstream pin: update both `reference/julia/generate_goldens.jl` (`UPSTREAM_SHA`) and `src/pdft/__init__.py` (`__upstream_ref__`), regenerate goldens, and verify all parity tests still pass.
|
|
184
|
+
6. Push and watch the GitHub Actions matrix — three Python versions must all be green before merging.
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
test:
|
|
10
|
+
runs-on: ubuntu-latest
|
|
11
|
+
strategy:
|
|
12
|
+
fail-fast: false
|
|
13
|
+
matrix:
|
|
14
|
+
python-version: ["3.11", "3.12", "3.13"]
|
|
15
|
+
steps:
|
|
16
|
+
- uses: actions/checkout@v4
|
|
17
|
+
- uses: actions/setup-python@v5
|
|
18
|
+
with:
|
|
19
|
+
python-version: ${{ matrix.python-version }}
|
|
20
|
+
cache: pip
|
|
21
|
+
- name: Install
|
|
22
|
+
run: pip install -e ".[dev]"
|
|
23
|
+
- name: Ruff lint
|
|
24
|
+
run: ruff check src tests
|
|
25
|
+
- name: Pytest
|
|
26
|
+
run: pytest --cov=pdft --cov-report=term --cov-fail-under=90
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
name: publish
|
|
2
|
+
|
|
3
|
+
# Builds and publishes pdft to PyPI on every v*.*.* tag push.
|
|
4
|
+
#
|
|
5
|
+
# Uses PyPI Trusted Publishers (OIDC) — NO API token stored in GitHub
|
|
6
|
+
# secrets. To activate: on https://pypi.org/manage/project/pdft/settings/publishing/
|
|
7
|
+
# add a Trusted Publisher with:
|
|
8
|
+
# PyPI Project Name: pdft
|
|
9
|
+
# Owner: zazabap
|
|
10
|
+
# Repository name: pdft
|
|
11
|
+
# Workflow name: publish.yml
|
|
12
|
+
# Environment name: pypi
|
|
13
|
+
#
|
|
14
|
+
# After the first successful publish, future releases are:
|
|
15
|
+
# git tag v0.2.1 && git push --tags
|
|
16
|
+
#
|
|
17
|
+
# Docs: https://docs.pypi.org/trusted-publishers/
|
|
18
|
+
|
|
19
|
+
on:
|
|
20
|
+
push:
|
|
21
|
+
tags:
|
|
22
|
+
- "v*.*.*"
|
|
23
|
+
|
|
24
|
+
jobs:
|
|
25
|
+
build:
|
|
26
|
+
name: Build sdist + wheel
|
|
27
|
+
runs-on: ubuntu-latest
|
|
28
|
+
steps:
|
|
29
|
+
- uses: actions/checkout@v4
|
|
30
|
+
- uses: actions/setup-python@v5
|
|
31
|
+
with:
|
|
32
|
+
python-version: "3.12"
|
|
33
|
+
- name: Install build backend
|
|
34
|
+
run: python -m pip install --upgrade pip build
|
|
35
|
+
- name: Verify pyproject version matches the git tag
|
|
36
|
+
run: |
|
|
37
|
+
tag="${GITHUB_REF#refs/tags/v}"
|
|
38
|
+
file_ver=$(python -c "import tomllib; print(tomllib.load(open('pyproject.toml','rb'))['project']['version'])")
|
|
39
|
+
if [ "$tag" != "$file_ver" ]; then
|
|
40
|
+
echo "::error::Tag v$tag does not match pyproject.toml version $file_ver"
|
|
41
|
+
exit 1
|
|
42
|
+
fi
|
|
43
|
+
- name: Build
|
|
44
|
+
run: python -m build
|
|
45
|
+
- name: Upload artifacts
|
|
46
|
+
uses: actions/upload-artifact@v4
|
|
47
|
+
with:
|
|
48
|
+
name: dist
|
|
49
|
+
path: dist/
|
|
50
|
+
|
|
51
|
+
publish:
|
|
52
|
+
name: Publish to PyPI
|
|
53
|
+
needs: build
|
|
54
|
+
runs-on: ubuntu-latest
|
|
55
|
+
environment: pypi
|
|
56
|
+
permissions:
|
|
57
|
+
id-token: write # mandatory for Trusted Publisher OIDC
|
|
58
|
+
steps:
|
|
59
|
+
- uses: actions/download-artifact@v4
|
|
60
|
+
with:
|
|
61
|
+
name: dist
|
|
62
|
+
path: dist/
|
|
63
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: Verify upstream pin
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
pull_request:
|
|
5
|
+
paths:
|
|
6
|
+
- "reference/**"
|
|
7
|
+
- ".github/workflows/verify-upstream-pin.yml"
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
verify:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- name: Extract pinned sha
|
|
15
|
+
id: pin
|
|
16
|
+
run: |
|
|
17
|
+
sha=$(grep -oP 'UPSTREAM_SHA = "\K[0-9a-f]+' reference/julia/generate_goldens.jl | head -n1)
|
|
18
|
+
echo "sha=$sha" >> "$GITHUB_OUTPUT"
|
|
19
|
+
- name: Assert sha exists upstream
|
|
20
|
+
env:
|
|
21
|
+
GH_TOKEN: ${{ github.token }}
|
|
22
|
+
run: |
|
|
23
|
+
sha="${{ steps.pin.outputs.sha }}"
|
|
24
|
+
if [ -z "$sha" ]; then
|
|
25
|
+
echo "Could not extract UPSTREAM_SHA from generate_goldens.jl"
|
|
26
|
+
exit 1
|
|
27
|
+
fi
|
|
28
|
+
gh api "repos/nzy1997/ParametricDFT.jl/commits/$sha" >/dev/null
|
pdft-0.2.1/.gitignore
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# Byte-compiled / optimized / DLL files
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
|
|
6
|
+
# Distribution / packaging
|
|
7
|
+
.Python
|
|
8
|
+
build/
|
|
9
|
+
develop-eggs/
|
|
10
|
+
dist/
|
|
11
|
+
downloads/
|
|
12
|
+
eggs/
|
|
13
|
+
.eggs/
|
|
14
|
+
lib/
|
|
15
|
+
lib64/
|
|
16
|
+
parts/
|
|
17
|
+
sdist/
|
|
18
|
+
var/
|
|
19
|
+
wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
*.egg
|
|
22
|
+
MANIFEST
|
|
23
|
+
|
|
24
|
+
# Virtual environments
|
|
25
|
+
.venv/
|
|
26
|
+
venv/
|
|
27
|
+
env/
|
|
28
|
+
ENV/
|
|
29
|
+
|
|
30
|
+
# Tooling caches
|
|
31
|
+
.pytest_cache/
|
|
32
|
+
.ruff_cache/
|
|
33
|
+
.mypy_cache/
|
|
34
|
+
.tox/
|
|
35
|
+
.coverage
|
|
36
|
+
.coverage.*
|
|
37
|
+
htmlcov/
|
|
38
|
+
coverage.xml
|
|
39
|
+
*.cover
|
|
40
|
+
|
|
41
|
+
# Jupyter
|
|
42
|
+
.ipynb_checkpoints/
|
|
43
|
+
|
|
44
|
+
# Editors / OS
|
|
45
|
+
.idea/
|
|
46
|
+
.vscode/
|
|
47
|
+
.DS_Store
|
|
48
|
+
*.swp
|
|
49
|
+
|
|
50
|
+
# Claude Code — track CLAUDE.md, ignore everything else
|
|
51
|
+
.claude/*
|
|
52
|
+
!.claude/CLAUDE.md
|
|
53
|
+
|
|
54
|
+
# Example outputs
|
|
55
|
+
out/
|
|
56
|
+
/out/
|
|
57
|
+
|
|
58
|
+
# Benchmark outputs
|
|
59
|
+
benchmarks/results/
|
pdft-0.2.1/LICENSE
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 zazabap
|
|
4
|
+
|
|
5
|
+
Portions of this project are a Python port of ParametricDFT.jl
|
|
6
|
+
(https://github.com/nzy1997/ParametricDFT.jl), Copyright (c) 2025 nzy1997,
|
|
7
|
+
originally released under the MIT License.
|
|
8
|
+
|
|
9
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
10
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
11
|
+
in the Software without restriction, including without limitation the rights
|
|
12
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
13
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
14
|
+
furnished to do so, subject to the following conditions:
|
|
15
|
+
|
|
16
|
+
The above copyright notice and this permission notice shall be included in all
|
|
17
|
+
copies or substantial portions of the Software.
|
|
18
|
+
|
|
19
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
20
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
21
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
22
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
23
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
24
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
25
|
+
SOFTWARE.
|
pdft-0.2.1/Makefile
ADDED
pdft-0.2.1/PKG-INFO
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: pdft
|
|
3
|
+
Version: 0.2.1
|
|
4
|
+
Summary: Python port of ParametricDFT.jl: learning parametric quantum Fourier transforms via manifold optimization.
|
|
5
|
+
Project-URL: Homepage, https://github.com/zazabap/pdft
|
|
6
|
+
Project-URL: Issues, https://github.com/zazabap/pdft/issues
|
|
7
|
+
Project-URL: Upstream, https://github.com/nzy1997/ParametricDFT.jl
|
|
8
|
+
Author: zazabap
|
|
9
|
+
License: MIT License
|
|
10
|
+
|
|
11
|
+
Copyright (c) 2026 zazabap
|
|
12
|
+
|
|
13
|
+
Portions of this project are a Python port of ParametricDFT.jl
|
|
14
|
+
(https://github.com/nzy1997/ParametricDFT.jl), Copyright (c) 2025 nzy1997,
|
|
15
|
+
originally released under the MIT License.
|
|
16
|
+
|
|
17
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
18
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
19
|
+
in the Software without restriction, including without limitation the rights
|
|
20
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
21
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
22
|
+
furnished to do so, subject to the following conditions:
|
|
23
|
+
|
|
24
|
+
The above copyright notice and this permission notice shall be included in all
|
|
25
|
+
copies or substantial portions of the Software.
|
|
26
|
+
|
|
27
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
28
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
29
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
30
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
31
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
32
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
33
|
+
SOFTWARE.
|
|
34
|
+
License-File: LICENSE
|
|
35
|
+
Keywords: dft,fourier,jax,manifold-optimization,quantum,variational
|
|
36
|
+
Classifier: Development Status :: 2 - Pre-Alpha
|
|
37
|
+
Classifier: Intended Audience :: Science/Research
|
|
38
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
39
|
+
Classifier: Programming Language :: Python :: 3
|
|
40
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
41
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
42
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
43
|
+
Classifier: Topic :: Scientific/Engineering :: Physics
|
|
44
|
+
Requires-Python: >=3.11
|
|
45
|
+
Requires-Dist: jax>=0.10.0
|
|
46
|
+
Requires-Dist: typing-extensions>=4.5
|
|
47
|
+
Provides-Extra: dev
|
|
48
|
+
Requires-Dist: jaxtyping>=0.2; extra == 'dev'
|
|
49
|
+
Requires-Dist: matplotlib>=3.8; extra == 'dev'
|
|
50
|
+
Requires-Dist: numpy>=1.26; extra == 'dev'
|
|
51
|
+
Requires-Dist: pytest-cov>=4; extra == 'dev'
|
|
52
|
+
Requires-Dist: pytest>=7; extra == 'dev'
|
|
53
|
+
Requires-Dist: ruff>=0.5; extra == 'dev'
|
|
54
|
+
Provides-Extra: gpu
|
|
55
|
+
Requires-Dist: jax[cuda12]>=0.10.0; extra == 'gpu'
|
|
56
|
+
Provides-Extra: plot
|
|
57
|
+
Requires-Dist: matplotlib>=3.8; extra == 'plot'
|
|
58
|
+
Description-Content-Type: text/markdown
|
|
59
|
+
|
|
60
|
+
# pdft
|
|
61
|
+
|
|
62
|
+
A Python port of [ParametricDFT.jl](https://github.com/nzy1997/ParametricDFT.jl):
|
|
63
|
+
learning parametric quantum Fourier transforms via manifold optimization. The
|
|
64
|
+
package implements a variational approach that approximates the Discrete
|
|
65
|
+
Fourier Transform (DFT) with parameterized quantum circuits.
|
|
66
|
+
|
|
67
|
+
> Status: early scaffold. The Julia package is the reference implementation;
|
|
68
|
+
> this repository will grow the Python equivalent incrementally.
|
|
69
|
+
|
|
70
|
+
## Installation
|
|
71
|
+
|
|
72
|
+
Once published on PyPI:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
pip install pdft
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
From source:
|
|
79
|
+
|
|
80
|
+
```bash
|
|
81
|
+
git clone https://github.com/zazabap/pdft.git
|
|
82
|
+
cd pdft
|
|
83
|
+
pip install -e ".[dev]"
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Quick start
|
|
87
|
+
|
|
88
|
+
_(coming soon — mirrors the `make example` demo in the upstream Julia
|
|
89
|
+
package)_
|
|
90
|
+
|
|
91
|
+
## Background
|
|
92
|
+
|
|
93
|
+
See the upstream notes for the theory:
|
|
94
|
+
- [`note/stepbystep.pdf`](https://github.com/nzy1997/ParametricDFT.jl/blob/main/note/stepbystep.pdf)
|
|
95
|
+
- [`note/main.pdf`](https://github.com/nzy1997/ParametricDFT.jl/blob/main/note/main.pdf)
|
|
96
|
+
|
|
97
|
+
## License
|
|
98
|
+
|
|
99
|
+
MIT. See [LICENSE](LICENSE). This project is a derivative port of
|
|
100
|
+
ParametricDFT.jl (Copyright © 2025 nzy1997, MIT).
|
pdft-0.2.1/README.md
ADDED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# pdft
|
|
2
|
+
|
|
3
|
+
A Python port of [ParametricDFT.jl](https://github.com/nzy1997/ParametricDFT.jl):
|
|
4
|
+
learning parametric quantum Fourier transforms via manifold optimization. The
|
|
5
|
+
package implements a variational approach that approximates the Discrete
|
|
6
|
+
Fourier Transform (DFT) with parameterized quantum circuits.
|
|
7
|
+
|
|
8
|
+
> Status: early scaffold. The Julia package is the reference implementation;
|
|
9
|
+
> this repository will grow the Python equivalent incrementally.
|
|
10
|
+
|
|
11
|
+
## Installation
|
|
12
|
+
|
|
13
|
+
Once published on PyPI:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
pip install pdft
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
From source:
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
git clone https://github.com/zazabap/pdft.git
|
|
23
|
+
cd pdft
|
|
24
|
+
pip install -e ".[dev]"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick start
|
|
28
|
+
|
|
29
|
+
_(coming soon — mirrors the `make example` demo in the upstream Julia
|
|
30
|
+
package)_
|
|
31
|
+
|
|
32
|
+
## Background
|
|
33
|
+
|
|
34
|
+
See the upstream notes for the theory:
|
|
35
|
+
- [`note/stepbystep.pdf`](https://github.com/nzy1997/ParametricDFT.jl/blob/main/note/stepbystep.pdf)
|
|
36
|
+
- [`note/main.pdf`](https://github.com/nzy1997/ParametricDFT.jl/blob/main/note/main.pdf)
|
|
37
|
+
|
|
38
|
+
## License
|
|
39
|
+
|
|
40
|
+
MIT. See [LICENSE](LICENSE). This project is a derivative port of
|
|
41
|
+
ParametricDFT.jl (Copyright © 2025 nzy1997, MIT).
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
"""basis_demo.py — train a QFTBasis on a random 4x4 image and plot the loss.
|
|
2
|
+
|
|
3
|
+
Run: python examples/basis_demo.py
|
|
4
|
+
Requires: pdft + pdft[plot] extra.
|
|
5
|
+
"""
|
|
6
|
+
from __future__ import annotations
|
|
7
|
+
|
|
8
|
+
import time
|
|
9
|
+
from pathlib import Path
|
|
10
|
+
|
|
11
|
+
import jax
|
|
12
|
+
import jax.numpy as jnp
|
|
13
|
+
|
|
14
|
+
import pdft
|
|
15
|
+
from pdft.viz import TrainingHistory, plot_training_loss
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def main(out_dir: str | Path = "out") -> None:
|
|
19
|
+
out = Path(out_dir)
|
|
20
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
21
|
+
|
|
22
|
+
target = jax.random.normal(jax.random.PRNGKey(7), (4, 4)).astype(jnp.complex128)
|
|
23
|
+
basis = pdft.QFTBasis(m=2, n=2)
|
|
24
|
+
|
|
25
|
+
print(f"Training QFTBasis on a 4x4 target image for 50 steps with RiemannianGD...")
|
|
26
|
+
t0 = time.perf_counter()
|
|
27
|
+
result = pdft.train_basis(
|
|
28
|
+
basis,
|
|
29
|
+
target=target,
|
|
30
|
+
loss=pdft.L1Norm(),
|
|
31
|
+
optimizer=pdft.RiemannianGD(lr=0.01),
|
|
32
|
+
steps=50,
|
|
33
|
+
seed=0,
|
|
34
|
+
)
|
|
35
|
+
elapsed = time.perf_counter() - t0
|
|
36
|
+
print(f"done in {elapsed:.2f}s")
|
|
37
|
+
print(f" initial loss: {result.loss_history[0]:.4f}")
|
|
38
|
+
print(f" final loss: {result.loss_history[-1]:.4f}")
|
|
39
|
+
print(f" reduction : {result.loss_history[0] - result.loss_history[-1]:.4f}")
|
|
40
|
+
|
|
41
|
+
hist = TrainingHistory(losses=result.loss_history, label="RiemannianGD")
|
|
42
|
+
fig_path = out / "basis_demo_loss.png"
|
|
43
|
+
plot_training_loss(hist, output_path=fig_path, smooth_alpha=0.2,
|
|
44
|
+
title="QFTBasis training loss (L1, RiemannianGD, lr=0.01)")
|
|
45
|
+
print(f" wrote plot: {fig_path}")
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
if __name__ == "__main__":
|
|
49
|
+
main()
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
"""mera_demo.py — compare QFTBasis, EntangledQFTBasis, TEBDBasis, MERABasis on a single target.
|
|
2
|
+
|
|
3
|
+
Run: python examples/mera_demo.py
|
|
4
|
+
"""
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import time
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
import jax
|
|
11
|
+
import jax.numpy as jnp
|
|
12
|
+
|
|
13
|
+
import pdft
|
|
14
|
+
from pdft.viz import TrainingHistory, plot_training_comparison
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def main(out_dir: str | Path = "out") -> None:
|
|
18
|
+
out = Path(out_dir)
|
|
19
|
+
out.mkdir(parents=True, exist_ok=True)
|
|
20
|
+
|
|
21
|
+
target = jax.random.normal(jax.random.PRNGKey(11), (4, 4)).astype(jnp.complex128)
|
|
22
|
+
|
|
23
|
+
histories: list[TrainingHistory] = []
|
|
24
|
+
for label, basis in [
|
|
25
|
+
("QFTBasis", pdft.QFTBasis(m=2, n=2)),
|
|
26
|
+
("EntangledQFTBasis", pdft.EntangledQFTBasis(m=2, n=2)),
|
|
27
|
+
("TEBDBasis", pdft.TEBDBasis(m=2, n=2)),
|
|
28
|
+
("MERABasis", pdft.MERABasis(m=2, n=2)),
|
|
29
|
+
]:
|
|
30
|
+
print(f"Training {label}...")
|
|
31
|
+
t0 = time.perf_counter()
|
|
32
|
+
result = pdft.train_basis(
|
|
33
|
+
basis,
|
|
34
|
+
target=target,
|
|
35
|
+
loss=pdft.L1Norm(),
|
|
36
|
+
optimizer=pdft.RiemannianAdam(lr=0.01),
|
|
37
|
+
steps=40,
|
|
38
|
+
seed=0,
|
|
39
|
+
)
|
|
40
|
+
elapsed = time.perf_counter() - t0
|
|
41
|
+
print(f" {elapsed:.2f}s, initial {result.loss_history[0]:.4f}, final {result.loss_history[-1]:.4f}")
|
|
42
|
+
histories.append(TrainingHistory(losses=result.loss_history, label=label))
|
|
43
|
+
|
|
44
|
+
fig_path = out / "mera_demo.png"
|
|
45
|
+
plot_training_comparison(histories, output_path=fig_path,
|
|
46
|
+
title="Four bases on the same 4x4 target (RiemannianAdam, L1 loss)")
|
|
47
|
+
print(f"wrote: {fig_path}")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
if __name__ == "__main__":
|
|
51
|
+
main()
|