polystep 0.13.0__tar.gz → 1.0.0__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.
Files changed (162) hide show
  1. {polystep-0.13.0 → polystep-1.0.0}/CHANGELOG.md +32 -0
  2. {polystep-0.13.0 → polystep-1.0.0}/CITATION.cff +4 -4
  3. polystep-1.0.0/CONTRIBUTING.md +65 -0
  4. {polystep-0.13.0 → polystep-1.0.0}/LIMITATIONS.md +49 -25
  5. {polystep-0.13.0 → polystep-1.0.0}/PKG-INFO +46 -33
  6. {polystep-0.13.0 → polystep-1.0.0}/README.md +43 -28
  7. polystep-1.0.0/docs/api_overview.md +390 -0
  8. polystep-1.0.0/docs/determinism.md +61 -0
  9. polystep-1.0.0/docs/performance.md +237 -0
  10. polystep-1.0.0/docs/reproducibility.md +152 -0
  11. {polystep-0.13.0 → polystep-1.0.0}/pyproject.toml +4 -9
  12. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/__init__.py +5 -48
  13. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/_compiled.py +17 -54
  14. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/_serialization.py +30 -77
  15. polystep-1.0.0/src/polystep/_step_blockwise.py +360 -0
  16. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/_step_core.py +74 -15
  17. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/_step_momentum.py +1 -9
  18. polystep-1.0.0/src/polystep/_step_monolithic.py +1084 -0
  19. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/adaptive_subspace.py +42 -59
  20. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/api.py +15 -26
  21. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/ask_tell.py +7 -6
  22. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/baselines/core.py +9 -13
  23. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/baselines/methods.py +22 -32
  24. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/__init__.py +0 -2
  25. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/rl/cartpole.py +8 -59
  26. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/rl/gym_evaluator.py +46 -49
  27. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/rl/policies.py +0 -2
  28. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/utils.py +15 -26
  29. polystep-1.0.0/src/polystep/blockwise.py +135 -0
  30. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/cost_nn.py +608 -287
  31. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/costs.py +15 -14
  32. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/dynamics.py +0 -2
  33. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/epsilon.py +23 -7
  34. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/factored_subspace.py +9 -19
  35. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/geometry.py +16 -36
  36. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/hybrid_subspace.py +89 -178
  37. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/layers/attention.py +9 -8
  38. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/optimizer.py +275 -542
  39. polystep-1.0.0/src/polystep/projection/sparse.py +249 -0
  40. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solver.py +52 -94
  41. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/__init__.py +0 -2
  42. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/_shared.py +35 -41
  43. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/base.py +8 -2
  44. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/greedy.py +11 -31
  45. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/kl_softmax.py +19 -19
  46. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/sinkhorn.py +29 -62
  47. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/solvers/softmax.py +16 -3
  48. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/subspace.py +21 -2
  49. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/transform.py +4 -13
  50. {polystep-0.13.0 → polystep-1.0.0}/src/polystep.egg-info/PKG-INFO +46 -33
  51. {polystep-0.13.0 → polystep-1.0.0}/src/polystep.egg-info/SOURCES.txt +1 -10
  52. {polystep-0.13.0 → polystep-1.0.0}/src/polystep.egg-info/requires.txt +1 -3
  53. {polystep-0.13.0 → polystep-1.0.0}/tests/conftest.py +17 -97
  54. {polystep-0.13.0 → polystep-1.0.0}/tests/test_ablation_solvers.py +71 -98
  55. {polystep-0.13.0 → polystep-1.0.0}/tests/test_adaptive_subspace.py +297 -37
  56. {polystep-0.13.0 → polystep-1.0.0}/tests/test_amortization.py +98 -41
  57. {polystep-0.13.0 → polystep-1.0.0}/tests/test_api.py +91 -68
  58. polystep-1.0.0/tests/test_ask_tell.py +172 -0
  59. {polystep-0.13.0 → polystep-1.0.0}/tests/test_baseline_fairness.py +68 -55
  60. {polystep-0.13.0 → polystep-1.0.0}/tests/test_baselines.py +71 -6
  61. {polystep-0.13.0 → polystep-1.0.0}/tests/test_benchmarks.py +2 -2
  62. polystep-1.0.0/tests/test_biased_rotation.py +145 -0
  63. polystep-1.0.0/tests/test_blockwise.py +330 -0
  64. {polystep-0.13.0 → polystep-1.0.0}/tests/test_candidate_autocast.py +5 -7
  65. {polystep-0.13.0 → polystep-1.0.0}/tests/test_checkpoint_resume.py +94 -91
  66. {polystep-0.13.0 → polystep-1.0.0}/tests/test_compiled.py +47 -16
  67. {polystep-0.13.0 → polystep-1.0.0}/tests/test_controlled.py +1 -6
  68. {polystep-0.13.0 → polystep-1.0.0}/tests/test_cost_nn.py +124 -34
  69. polystep-1.0.0/tests/test_costs.py +134 -0
  70. {polystep-0.13.0 → polystep-1.0.0}/tests/test_determinism.py +15 -14
  71. {polystep-0.13.0 → polystep-1.0.0}/tests/test_dfo.py +403 -91
  72. {polystep-0.13.0 → polystep-1.0.0}/tests/test_diagnostics.py +6 -6
  73. {polystep-0.13.0 → polystep-1.0.0}/tests/test_dynamics.py +1 -1
  74. {polystep-0.13.0 → polystep-1.0.0}/tests/test_end_to_end_learning.py +2 -2
  75. {polystep-0.13.0 → polystep-1.0.0}/tests/test_epsilon.py +73 -22
  76. {polystep-0.13.0 → polystep-1.0.0}/tests/test_evaluator_compile_forward.py +14 -1
  77. polystep-1.0.0/tests/test_factored_subspace.py +226 -0
  78. polystep-1.0.0/tests/test_fastpath_parity.py +426 -0
  79. {polystep-0.13.0 → polystep-1.0.0}/tests/test_fused_projection.py +13 -17
  80. {polystep-0.13.0 → polystep-1.0.0}/tests/test_geometry.py +40 -41
  81. {polystep-0.13.0 → polystep-1.0.0}/tests/test_hybrid_subspace.py +237 -120
  82. {polystep-0.13.0 → polystep-1.0.0}/tests/test_input_validation.py +65 -28
  83. {polystep-0.13.0 → polystep-1.0.0}/tests/test_integration.py +44 -48
  84. {polystep-0.13.0 → polystep-1.0.0}/tests/test_kl_softmax.py +52 -9
  85. {polystep-0.13.0 → polystep-1.0.0}/tests/test_layers.py +159 -14
  86. {polystep-0.13.0 → polystep-1.0.0}/tests/test_math_guards.py +87 -47
  87. polystep-1.0.0/tests/test_mixed_precision.py +779 -0
  88. {polystep-0.13.0 → polystep-1.0.0}/tests/test_mnist.py +52 -38
  89. {polystep-0.13.0 → polystep-1.0.0}/tests/test_no_test_set_leakage.py +5 -9
  90. polystep-1.0.0/tests/test_nondiff_data.py +86 -0
  91. {polystep-0.13.0 → polystep-1.0.0}/tests/test_nondiff_models.py +2 -145
  92. {polystep-0.13.0 → polystep-1.0.0}/tests/test_numerical_stress.py +5 -56
  93. {polystep-0.13.0 → polystep-1.0.0}/tests/test_objectives.py +16 -42
  94. {polystep-0.13.0 → polystep-1.0.0}/tests/test_optimizer.py +369 -303
  95. {polystep-0.13.0 → polystep-1.0.0}/tests/test_per_sample.py +2 -2
  96. {polystep-0.13.0 → polystep-1.0.0}/tests/test_practical.py +0 -8
  97. {polystep-0.13.0 → polystep-1.0.0}/tests/test_probe_jitter.py +79 -33
  98. {polystep-0.13.0 → polystep-1.0.0}/tests/test_quadratic_model.py +101 -83
  99. {polystep-0.13.0 → polystep-1.0.0}/tests/test_rl_benchmarks.py +156 -4
  100. {polystep-0.13.0 → polystep-1.0.0}/tests/test_search_experiments.py +23 -4
  101. {polystep-0.13.0 → polystep-1.0.0}/tests/test_sequential_fast_path.py +30 -34
  102. {polystep-0.13.0 → polystep-1.0.0}/tests/test_sinkhorn.py +124 -94
  103. {polystep-0.13.0 → polystep-1.0.0}/tests/test_sinkhorn_numerics.py +19 -8
  104. polystep-1.0.0/tests/test_site_vmap.py +1192 -0
  105. {polystep-0.13.0 → polystep-1.0.0}/tests/test_softmax.py +38 -21
  106. {polystep-0.13.0 → polystep-1.0.0}/tests/test_softmax_integration.py +18 -46
  107. {polystep-0.13.0 → polystep-1.0.0}/tests/test_sparse_delta_evaluator.py +127 -31
  108. {polystep-0.13.0 → polystep-1.0.0}/tests/test_sparse_projection.py +115 -30
  109. {polystep-0.13.0 → polystep-1.0.0}/tests/test_subspace_blockwise.py +132 -190
  110. {polystep-0.13.0 → polystep-1.0.0}/tests/test_subspace_contract.py +5 -9
  111. {polystep-0.13.0 → polystep-1.0.0}/tests/test_subspace_delta.py +66 -64
  112. {polystep-0.13.0 → polystep-1.0.0}/tests/test_subspace_gain.py +2 -2
  113. {polystep-0.13.0 → polystep-1.0.0}/tests/test_timeseries_val_selection.py +4 -8
  114. {polystep-0.13.0 → polystep-1.0.0}/tests/test_transform.py +112 -12
  115. {polystep-0.13.0 → polystep-1.0.0}/tests/test_trust_region.py +110 -55
  116. {polystep-0.13.0 → polystep-1.0.0}/tests/test_tuning_sweep.py +21 -26
  117. polystep-0.13.0/CONTRIBUTING.md +0 -54
  118. polystep-0.13.0/docs/api_overview.md +0 -217
  119. polystep-0.13.0/docs/determinism.md +0 -31
  120. polystep-0.13.0/docs/performance.md +0 -133
  121. polystep-0.13.0/docs/reproducibility.md +0 -80
  122. polystep-0.13.0/src/polystep/_step_blockwise.py +0 -648
  123. polystep-0.13.0/src/polystep/_step_monolithic.py +0 -1295
  124. polystep-0.13.0/src/polystep/blockwise.py +0 -276
  125. polystep-0.13.0/src/polystep/cma.py +0 -117
  126. polystep-0.13.0/src/polystep/cma_subspace.py +0 -95
  127. polystep-0.13.0/src/polystep/projection/sparse.py +0 -208
  128. polystep-0.13.0/src/polystep/solvers/tempered_softmax.py +0 -31
  129. polystep-0.13.0/tests/test_ask_tell.py +0 -100
  130. polystep-0.13.0/tests/test_biased_rotation.py +0 -82
  131. polystep-0.13.0/tests/test_blockwise.py +0 -219
  132. polystep-0.13.0/tests/test_cma.py +0 -319
  133. polystep-0.13.0/tests/test_cma_subspace.py +0 -380
  134. polystep-0.13.0/tests/test_costs.py +0 -63
  135. polystep-0.13.0/tests/test_evaluator_regressions.py +0 -367
  136. polystep-0.13.0/tests/test_factored_subspace.py +0 -107
  137. polystep-0.13.0/tests/test_fastpath_parity.py +0 -168
  138. polystep-0.13.0/tests/test_mixed_precision.py +0 -496
  139. polystep-0.13.0/tests/test_nondiff_data.py +0 -151
  140. polystep-0.13.0/tests/test_regressions.py +0 -1105
  141. polystep-0.13.0/tests/test_silent_failures.py +0 -748
  142. polystep-0.13.0/tests/test_site_vmap.py +0 -382
  143. polystep-0.13.0/tests/test_vmap_layer_compatibility.py +0 -239
  144. {polystep-0.13.0 → polystep-1.0.0}/LICENSE +0 -0
  145. {polystep-0.13.0 → polystep-1.0.0}/MANIFEST.in +0 -0
  146. {polystep-0.13.0 → polystep-1.0.0}/setup.cfg +0 -0
  147. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/baselines/__init__.py +0 -0
  148. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/rl/__init__.py +0 -0
  149. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/rl/metrics.py +0 -0
  150. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/benchmarks/threads.py +0 -0
  151. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/layers/__init__.py +0 -0
  152. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/layers/rnn.py +0 -0
  153. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/objectives/__init__.py +0 -0
  154. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/objectives/base.py +0 -0
  155. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/objectives/synthetic.py +0 -0
  156. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/projection/__init__.py +0 -0
  157. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/py.typed +0 -0
  158. {polystep-0.13.0 → polystep-1.0.0}/src/polystep/quadratic_model.py +0 -0
  159. {polystep-0.13.0 → polystep-1.0.0}/src/polystep.egg-info/dependency_links.txt +0 -0
  160. {polystep-0.13.0 → polystep-1.0.0}/src/polystep.egg-info/top_level.txt +0 -0
  161. {polystep-0.13.0 → polystep-1.0.0}/tests/__init__.py +0 -0
  162. {polystep-0.13.0 → polystep-1.0.0}/tests/test_sparse_integration.py +0 -0
@@ -1,5 +1,37 @@
1
1
  # Changelog
2
2
 
3
+ ## 1.0.0 - 2026-10-13
4
+
5
+ ### Upgrade notes
6
+
7
+ - Use `PolyStepOptimizer` for neural networks and `PolyStep` for standalone objectives.
8
+ - Replace diagonal CMA with `AdaptiveSubspace`, tempered softmax with `SoftmaxSolver`,
9
+ and `auto_epsilon` with `ProgressiveEpsilon`. Probe reuse has been removed.
10
+ - Checkpoints use format 6. Rebuild the optimizer from saved model weights when
11
+ upgrading from 0.13; older optimizer checkpoints cannot be loaded.
12
+ - Some low-level exports and experiment helpers moved or were removed. The
13
+ [migration guide](docs/api_overview.md#upgrading-from-013) lists replacements and
14
+ changes to evaluation, configuration, and callbacks.
15
+
16
+ ### Fixes
17
+
18
+ - Preserve live model weights, buffers, momentum, and trust-region state across
19
+ evaluation, subspace updates, and checkpoint restoration.
20
+ - Handle nonfinite costs, tiny transport rows, integer losses, and half-precision
21
+ reductions consistently. Sparse CUDA projections repeat in deterministic mode.
22
+ - Accept device strings in rotation sampling and float32 masks in half-precision
23
+ CUDA attention.
24
+ - Correct baseline budgets, tied ranks, rollout accounting, and download handling.
25
+
26
+ ### Performance and examples
27
+
28
+ - Reuse unchanged forward work, update only affected weights, and size candidate
29
+ chunks from measured activations. Speed up blockwise, factored, and adaptive steps.
30
+ - Reduce sparse projection storage and host synchronization; compile Sinkhorn loops.
31
+ - Refresh all 11 examples, their evaluation paths, and their documented settings.
32
+ See [performance](docs/performance.md) for measurements and tradeoffs.
33
+ - Consolidate redundant tests and keep release validation focused on the built wheel.
34
+
3
35
  ## 0.13.0 - 2026-09-22
4
36
 
5
37
  - Fix KL row mass, nonfinite inputs, evaluator hooks, and chunk handling.
@@ -6,8 +6,8 @@ authors:
6
6
  - family-names: Le
7
7
  given-names: An T.
8
8
  license: Apache-2.0
9
- version: 0.13.0
10
- date-released: "2026-09-22"
9
+ version: 1.0.0
10
+ date-released: "2026-10-13"
11
11
  repository-code: "https://github.com/anindex/polystep"
12
12
  keywords:
13
13
  - gradient-free optimization
@@ -22,9 +22,9 @@ preferred-citation:
22
22
  authors:
23
23
  - family-names: Le
24
24
  given-names: An T.
25
- journal: "arXiv preprint arXiv:2605.01928"
25
+ journal: "Transactions on Machine Learning Research"
26
26
  year: 2026
27
- url: "https://arxiv.org/abs/2605.01928"
27
+ url: "https://openreview.net/forum?id=8mlcqTTMuU"
28
28
  references:
29
29
  - type: conference-paper
30
30
  title: "Accelerating Motion Planning via Optimal Transport"
@@ -0,0 +1,65 @@
1
+ # Contributing
2
+
3
+ Install the development dependencies. Then run the CI checks:
4
+
5
+ ```bash
6
+ pip install -e ".[dev]"
7
+ ruff check .
8
+ ruff format --check .
9
+ pytest tests/ -q -n auto
10
+ ```
11
+
12
+ Work on a branch. Add a regression test when the change needs one.
13
+ Open a pull request after the checks pass. Test observable behavior.
14
+ Keep one small regression test per bug.
15
+ Extend an existing case when it already covers the same path; avoid separate tests
16
+ for trivial defaults, getters, or repeated shapes. Keep comments specific and public
17
+ APIs typed.
18
+
19
+ ## Tests
20
+
21
+ The default suite excludes `slow` and `gpu` tests. To select them:
22
+
23
+ ```bash
24
+ pytest tests/ -m slow
25
+ pytest tests/ -m gpu
26
+ pytest tests/ -m ""
27
+ ```
28
+
29
+ The fast suite uses one CPU thread; `POLYSTEP_TEST_THREADS` overrides it. Slow tests
30
+ use `ncpu - 8` threads, clamped to 2-16, and may download datasets. Keep downloads
31
+ and training-quality checks in the slow suite; use synthetic inputs for unit tests.
32
+ Tests importing `experiments/` must skip when that directory is absent, since it is
33
+ excluded from distributions.
34
+
35
+ Pushes and pull requests test Python 3.11 with PyTorch 2.8 and Python 3.14 with
36
+ current PyTorch. Scheduled and manual runs add Python 3.12/3.13, coverage, real-data
37
+ learning checks, and the shipped tests against the installed wheel. Every run
38
+ builds both distributions and checks the wheel's imports and type marker.
39
+
40
+ ## Releases
41
+
42
+ Update `polystep.__version__`. Set the same version and the release date in
43
+ `CITATION.cff`. Add a `CHANGELOG.md` entry with the heading `## X.Y.Z - YYYY-MM-DD`.
44
+ `pyproject.toml` reads the package version automatically.
45
+ If the project's maturity changes, update its `Development Status` classifier.
46
+
47
+ Build the distributions. Then check their metadata:
48
+
49
+ ```bash
50
+ uv build
51
+ uvx twine check --strict dist/*
52
+ ```
53
+
54
+ Run the full manual CI workflow on the release commit.
55
+ This covers the Python matrix and repository-only experiment tests.
56
+ After CI passes, publish a GitHub Release with the tag `vX.Y.Z`.
57
+ This triggers the PyPI workflow. The workflow checks version consistency, lint, and
58
+ package metadata. It tests the installed wheel before it publishes the package.
59
+ Only the separate publish job has OIDC permission; it uses the `pypi` environment
60
+ and PyPI Trusted Publishing.
61
+
62
+ ## Issues
63
+
64
+ Include a minimal reproduction, the traceback, Python and PyTorch versions, and
65
+ hardware details relevant to the failure.
@@ -13,33 +13,46 @@ projection do not apply to those columns.
13
13
  `FactoredSubspace` restricts perturbations to the input directions in its fixed
14
14
  factor. Rotation changes those directions. Retune radii when switching subspaces.
15
15
 
16
- Momentum is cleared when the basis changes. `AdaptiveSubspace` rotates every step
17
- by default, so momentum requires a longer rotation interval to accumulate.
18
- Heavy-ball momentum can amplify the barycentric displacement; `step_radius` bounds
19
- the displacement before momentum.
20
-
21
- CMA adaptation keeps its basis fixed between absorbs and normalizes covariance to
22
- `trace(C) = n`. Its default `mu_eff=1` gives rank-one adaptation only. Cumulative
23
- step-size adaptation is not implemented.
16
+ Momentum, the `amortize_steps` coasting direction, and the `biased_rotation`
17
+ direction are cleared when the basis changes. `AdaptiveSubspace` rotates every step
18
+ by default. These options need a longer rotation interval to take effect.
19
+ The optimizer warns when you combine them with rotation on every step.
20
+ Heavy-ball momentum can amplify the barycentric displacement.
21
+ `step_radius` bounds the displacement before momentum.
24
22
 
25
23
  ## Evaluation
26
24
 
27
25
  - `step()` expects `closure(batched_params) -> losses` with shape `(N,)`.
26
+ The candidate tensors may share storage that the next chunk overwrites, so clone
27
+ any you keep after the closure returns.
28
28
  - Manual loops must register the evaluator and current batch to enable fast paths.
29
29
  The evaluator and closure must compute the same objective.
30
+ - `NNCostEvaluator` and `train()` score candidates in eval mode: dropout is off and
31
+ batch-norm running statistics never update. A custom closure that runs the model in
32
+ train mode compares candidates under different dropout masks.
30
33
  - Mixed-dtype models require `HybridSubspace` or `FactoredSubspace`. Full-space and
31
34
  global-subspace coordinates use one dtype and reject mixed parameter dtypes.
32
35
  - `mixed_precision=True` uses BF16 geometry and forwards; solver costs use FP32.
33
- Models needing autocast internally must provide it.
34
- - `adaptive_probes` needs an unchanged whole configuration and `objective_token`.
35
- It cannot reuse losses across changing minibatches.
36
- - Deferred `trust_region` comparisons require the same objective. `train()` rejects
37
- this option; use manual steps on a stationary objective.
38
- - `trust_region` and `use_adaptive_radius` both scale the step. Choose one controller.
36
+ Models needing autocast internally must provide it. Half-precision candidate outputs
37
+ reduce in FP32 only under the builtin cross-entropy, MSE and L1 losses at their
38
+ defaults apart from `reduction`. Other losses may round nearby candidate scores
39
+ to the same value. With FP16 or BF16 parameters, `FactoredSubspace` materializes
40
+ every candidate and runs slower. See
41
+ [precision](docs/performance.md#compilation-and-precision).
42
+ - `train()` rejects `trust_region`. See
43
+ [radius control](docs/api_overview.md#quadratic-model-and-radius-control) for its
44
+ manual-step use.
45
+ - `trust_region` and `use_adaptive_radius` both scale the step. Choose one; the
46
+ optimizer warns when both are on.
39
47
  - Multi-fidelity screening imputes dropped costs using an additive offset. This can
40
- bias the full-fidelity cost estimate and is disabled with quadratic-model options.
41
- - Blockwise mode does not support every monolithic adaptation option; unsupported
42
- options warn at construction.
48
+ bias the full-fidelity cost estimate.
49
+ - Full-space blockwise mode does not support every monolithic adaptation option;
50
+ unsupported options warn at construction.
51
+ - Automatic chunk sizing counts outputs of layers with parameters. On a site-local
52
+ path, it counts all module outputs from the perturbed layer onward, provided the
53
+ forward neither draws random numbers nor reads the perturbed parameter before
54
+ calling its layer. Attention scores and other temporary tensors inside a module's
55
+ forward are not counted. Set `chunk_size` by hand if such a model runs out of memory.
43
56
  - The library does not distribute optimizer state across multiple GPUs.
44
57
 
45
58
  ## Attention and recurrent layers
@@ -53,9 +66,7 @@ Boolean masks use `True` for masked positions; float masks are additive. Attenti
53
66
  masks accept `(query, key)`, `(batch * heads, query, key)`, and broadcastable 4D shapes.
54
67
  The wrapper also accepts `(batch, query, key)` masks.
55
68
 
56
- CPU uses native scaled-dot-product attention; CUDA uses explicit matmuls. CPU vmap
57
- may emit a batching-fallback warning. Evaluate dropout layers in `model.eval()` mode
58
- for deterministic candidate comparisons.
69
+ CPU vmap may emit a batching-fallback warning.
59
70
 
60
71
  [`VmapSafeLSTM`](src/polystep/layers/rnn.py) requires batch-first dense inputs. It does
61
72
  not support bidirectional recurrence, projection, or `PackedSequence`. Its explicit
@@ -70,10 +81,22 @@ a common direction across particles.
70
81
 
71
82
  Sinkhorn requires `omega` in `[0.5, 1.95]`. Anderson acceleration and adaptive omega
72
83
  are inactive in fixed-iteration mode (`threshold <= 0`). Both solver families promote
73
- FP16/BF16 costs to FP32.
74
-
75
- All nonfinite costs are invalid. Their finite replacement can affect global cost
76
- scaling, including `scale_cost='max_cost'`.
84
+ FP16/BF16 costs to FP32. Cost differences and reductions must also fit the solver's
85
+ dtype: finite FP32 inputs near its limits can still overflow when recentered or
86
+ averaged. Rescale the objective before evaluation or return FP64 costs in that case.
87
+
88
+ All nonfinite costs are invalid. `min_cost_greedy` and `top_k_mean` skip a vertex
89
+ with any nonfinite probe, and the solve leaves a particle with no feasible vertex
90
+ where it is. The weighted solvers replace each nonfinite cost with twice the largest
91
+ finite magnitude plus one, clamped to the dtype limit. An infeasible candidate can
92
+ therefore retain weight, depending on epsilon. With `scale_cost='mean'` or
93
+ `'max_cost'`, the solvers compute the penalty after scaling. Otherwise, that weight
94
+ also depends on the cost offset.
95
+ Balanced Sinkhorn sends every column its prescribed mass, even when all of that
96
+ column's candidates are infeasible. The monolithic step records a vertex's cost as
97
+ the mean of its probes with each nonfinite probe already penalized, so with several
98
+ probes a vertex with only some nonfinite probes can still set the best-vertex
99
+ progress that drives the adaptive radius.
77
100
 
78
101
  Quadratic fits are finite-radius models. Simplex gradients can be biased on
79
102
  anisotropic quadratics, and block models omit cross-block curvature. A fit does not
@@ -89,4 +112,5 @@ The SNN surrogate-gradient baseline has substantial variation across seeds. MAX-
89
112
  search heuristics use flip budgets, which are not equivalent to neural candidate
90
113
  budgets. Head-only GPT-2 experiments do not establish full-model fine-tuning results.
91
114
 
92
- See the [experiment index](experiments/EXPERIMENT_INDEX.md) for protocols and results.
115
+ The [reproducibility guide](docs/reproducibility.md#protocol) gives the protocols, and the
116
+ [experiment index](experiments/EXPERIMENT_INDEX.md) has the results.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: polystep
3
- Version: 0.13.0
3
+ Version: 1.0.0
4
4
  Summary: PyTorch PolyStep Optimizer - gradient-free neural network training via entropic optimal transport
5
5
  Author: An T. Le
6
6
  License-Expression: Apache-2.0
@@ -10,7 +10,7 @@ Project-URL: Documentation, https://github.com/anindex/polystep/blob/main/docs/a
10
10
  Project-URL: Changelog, https://github.com/anindex/polystep/blob/main/CHANGELOG.md
11
11
  Project-URL: Issues, https://github.com/anindex/polystep/issues
12
12
  Keywords: pytorch,optimal-transport,gradient-free,sinkhorn,polystep,optimization,neural-network
13
- Classifier: Development Status :: 4 - Beta
13
+ Classifier: Development Status :: 5 - Production/Stable
14
14
  Classifier: Intended Audience :: Science/Research
15
15
  Classifier: Intended Audience :: Developers
16
16
  Classifier: Operating System :: OS Independent
@@ -45,14 +45,12 @@ Requires-Dist: pandas>=2.0; extra == "experiments"
45
45
  Requires-Dist: python-sat; extra == "experiments"
46
46
  Requires-Dist: torchvision>=0.25; extra == "experiments"
47
47
  Requires-Dist: cma>=3.3; extra == "experiments"
48
- Requires-Dist: snntorch>=0.7; extra == "experiments"
49
48
  Requires-Dist: datasets>=2.14; extra == "experiments"
50
49
  Requires-Dist: transformers>=4.40; extra == "experiments"
51
50
  Requires-Dist: gymnasium>=0.29; extra == "experiments"
52
51
  Provides-Extra: rl
53
- Requires-Dist: gymnasium[box2d]>=0.29; extra == "rl"
52
+ Requires-Dist: gymnasium>=0.29; extra == "rl"
54
53
  Requires-Dist: stable-baselines3>=2.3; extra == "rl"
55
- Requires-Dist: swig>=4.2; extra == "rl"
56
54
  Dynamic: license-file
57
55
 
58
56
  # polystep
@@ -74,7 +72,8 @@ backpropagation is usually faster.
74
72
 
75
73
  [Paper](https://arxiv.org/abs/2605.01928) ·
76
74
  [Interactive visualization](https://vietngth.github.io/polystep-visualization/) ·
77
- [API reference](docs/api_overview.md)
75
+ [API reference](https://github.com/anindex/polystep/blob/main/docs/api_overview.md) ·
76
+ [Julia package](https://github.com/anindex/PolyStep.jl)
78
77
 
79
78
  <p align="center">
80
79
  <img src="https://raw.githubusercontent.com/anindex/polystep/main/docs/figures/method_diagram.png" width="840"
@@ -85,7 +84,7 @@ backpropagation is usually faster.
85
84
 
86
85
  ```bash
87
86
  pip install polystep
88
- pip install "polystep[examples]" # optional demo dependencies
87
+ pip install "polystep[examples]" # dependencies for the repository's examples
89
88
  ```
90
89
 
91
90
  Python 3.11+ and PyTorch 2.8+ are required. Choose a CUDA build compatible with your
@@ -98,8 +97,7 @@ For development, install `pip install -e ".[dev]"` from a checkout.
98
97
  import torch
99
98
  from torch import nn
100
99
  from torch.utils.data import DataLoader, TensorDataset
101
- from polystep import HybridSubspace, PolyStepOptimizer, TrainConfig, train
102
- from polystep.transform import ParamLayout
100
+ from polystep import HybridSubspace, ParamLayout, PolyStepOptimizer, TrainConfig, train
103
101
 
104
102
  model = nn.Sequential(nn.Linear(16, 32), nn.ReLU(), nn.Linear(32, 3))
105
103
  loader = DataLoader(
@@ -113,10 +111,11 @@ optimizer = PolyStepOptimizer(model, subspace=subspace, solver="softmax")
113
111
  train(model, loader, nn.CrossEntropyLoss(), optimizer, TrainConfig(epochs=5))
114
112
  ```
115
113
 
116
- Replace the generated data with your dataset. Limit `max_subspace_dim` to control
117
- the forward budget. In manual loops, call `register_evaluator()` before each batch;
118
- `train()` does this automatically. See [performance](docs/performance.md) for
119
- candidate paths, thread counts, compilation, and memory settings.
114
+ Replace the generated data with your dataset. `max_subspace_dim=128` caps the search
115
+ at 128 coordinates, so each step scores 144 candidates. In manual loops, call
116
+ `register_evaluator()` before each batch; `train()` does this automatically. See
117
+ [performance](https://github.com/anindex/polystep/blob/main/docs/performance.md)
118
+ for candidate paths, thread counts, compilation, and memory settings.
120
119
 
121
120
  ### Ask/tell
122
121
 
@@ -133,6 +132,7 @@ print(es.best_fitness, es.best_solution)
133
132
  ### Synthetic objectives
134
133
 
135
134
  ```python
135
+ import torch
136
136
  from polystep import Ackley
137
137
  from polystep.solver import PolyStep
138
138
 
@@ -144,7 +144,8 @@ state = solver.run(torch.randn(100, 10))
144
144
 
145
145
  Each step samples a rotated polytope, evaluates its vertices, computes transport
146
146
  weights, and moves to their barycenter. Subspaces reduce the number of coordinates
147
- searched. Softmax treats blocks independently; Sinkhorn also constrains target mass.
147
+ searched. Softmax normalizes each particle's vertex weights; Sinkhorn also enforces
148
+ the target marginal.
148
149
 
149
150
  Finite-radius probes can cross flat regions of discontinuous losses. Performance
150
151
  depends on the radius, representation, and evaluation budget. PolyStep's optional
@@ -154,8 +155,8 @@ inherit classical direct-search or trust-region guarantees.
154
155
  ## Baselines
155
156
 
156
157
  `polystep.baselines` provides `openai_es`, `spsa`, `mezo`, `random_search`, `eggroll`,
157
- and `cma_es`. One evaluation means one candidate scored, even when candidates are
158
- batched:
158
+ and `cma_es`, which needs `pip install cma`. One evaluation means one candidate
159
+ scored, even when candidates are batched:
159
160
 
160
161
  ```python
161
162
  from polystep.baselines import Objective, openai_es
@@ -164,20 +165,30 @@ objective = Objective(lambda x: x.square().sum(1), dim=64, budget=10_000)
164
165
  result = openai_es(objective, sigma=0.05, lr=0.1, popsize=32)
165
166
  ```
166
167
 
167
- The paper runners support `--fair` for matched search spaces and evaluation budgets.
168
- EGGROLL uses a factored representation to retain matrix perturbations. See the
169
- [reproduction protocol](docs/reproducibility.md) for tuning and budget details.
168
+ The paper runners match search spaces, budgets and tuning as the
169
+ [reproducibility guide](https://github.com/anindex/polystep/blob/main/docs/reproducibility.md#protocol) describes.
170
170
 
171
171
  ## Benchmarks
172
172
 
173
- Five-seed mean ± sample standard deviation. These architecture tables compare
174
- validation-tuned methods at matched optimizer steps. Evaluation- and time-matched
175
- studies are reported separately in the [experiment index](experiments/EXPERIMENT_INDEX.md).
173
+ Five-seed mean ± sample standard deviation, with configurations and checkpoints
174
+ selected on validation data; the probSAT and RC2 columns are single runs. In the
175
+ architecture tables, OpenAI-ES, CMA-ES and EGGROLL get PolyStep's candidate-evaluation
176
+ budget, and SPSA, MeZO and random search get its step count. On argmax attention, hard
177
+ MoE and ETTh1, the population methods instead get 32 evaluations per PolyStep step,
178
+ spent at their own population size, and Adam trains for PolyStep's 30 epochs, except
179
+ 50 on ETTh1. On MAX-SAT, CMA-ES and OpenAI-ES get 50 evaluations per PolyStep step.
180
+ OpenAI-ES scores 50 candidates at each of PolyStep's steps, while CMA-ES doubles its
181
+ population at each restart and takes fewer generations. The tables were recorded
182
+ before 1.0; the
183
+ [reproducibility guide](https://github.com/anindex/polystep/blob/main/docs/reproducibility.md#changes-since-the-published-tables)
184
+ lists the rows that 1.0 changes. The
185
+ [experiment index](https://github.com/anindex/polystep/blob/main/experiments/EXPERIMENT_INDEX.md)
186
+ has the full tables.
176
187
 
177
188
  <!-- BENCH:START -->
178
189
  ### Non-differentiable tasks
179
190
 
180
- Test accuracy %. A dash means the result is not in this release.
191
+ Test accuracy %. A dash means the method was not run on that task.
181
192
 
182
193
  | Task | PolyStep | CMA-ES | OpenAI-ES | SPSA | Adam (surrogate) | Non-diff op |
183
194
  |---|---|---|---|---|---|---|
@@ -205,16 +216,17 @@ Test accuracy %. A dash means the result is not in this release.
205
216
 
206
217
  The SNN surrogate-gradient baseline varies substantially across seeds. Adam leads
207
218
  on the differentiable tasks, and specialized solvers lead on MAX-SAT. See
208
- [limitations](LIMITATIONS.md) for unsupported configurations and comparison limits.
219
+ [limitations](https://github.com/anindex/polystep/blob/main/LIMITATIONS.md)
220
+ for unsupported configurations and comparison limits.
209
221
 
210
222
  ## Documentation
211
223
 
212
- - [API reference](docs/api_overview.md)
213
- - [Performance](docs/performance.md)
214
- - [Examples](examples/README.md)
215
- - [Experiments](experiments/README.md) and [reproduction](docs/reproducibility.md)
216
- - [Determinism](docs/determinism.md)
217
- - [Contributing](CONTRIBUTING.md) and [changelog](CHANGELOG.md)
224
+ - [API reference](https://github.com/anindex/polystep/blob/main/docs/api_overview.md)
225
+ - [Performance](https://github.com/anindex/polystep/blob/main/docs/performance.md)
226
+ - [Examples](https://github.com/anindex/polystep/blob/main/examples/README.md)
227
+ - [Experiments](https://github.com/anindex/polystep/blob/main/experiments/README.md) and [reproduction](https://github.com/anindex/polystep/blob/main/docs/reproducibility.md)
228
+ - [Determinism](https://github.com/anindex/polystep/blob/main/docs/determinism.md)
229
+ - [Contributing](https://github.com/anindex/polystep/blob/main/CONTRIBUTING.md) and [changelog](https://github.com/anindex/polystep/blob/main/CHANGELOG.md)
218
230
 
219
231
  ## Citation
220
232
 
@@ -222,8 +234,9 @@ on the differentiable tasks, and specialized solvers lead on MAX-SAT. See
222
234
  @article{le2026training,
223
235
  title={Training Non-Differentiable Networks via Optimal Transport},
224
236
  author={Le, An T},
225
- journal={arXiv preprint arXiv:2605.01928},
226
- year={2026}
237
+ journal={Transactions on Machine Learning Research},
238
+ year={2026},
239
+ url={https://openreview.net/forum?id=8mlcqTTMuU}
227
240
  }
228
241
  ```
229
242
 
@@ -234,4 +247,4 @@ Thanks to [Viet T. Nguyen](https://vietngth.github.io/) for the
234
247
 
235
248
  ## License
236
249
 
237
- [Apache-2.0](LICENSE).
250
+ [Apache-2.0](https://github.com/anindex/polystep/blob/main/LICENSE).
@@ -17,7 +17,8 @@ backpropagation is usually faster.
17
17
 
18
18
  [Paper](https://arxiv.org/abs/2605.01928) ·
19
19
  [Interactive visualization](https://vietngth.github.io/polystep-visualization/) ·
20
- [API reference](docs/api_overview.md)
20
+ [API reference](https://github.com/anindex/polystep/blob/main/docs/api_overview.md) ·
21
+ [Julia package](https://github.com/anindex/PolyStep.jl)
21
22
 
22
23
  <p align="center">
23
24
  <img src="https://raw.githubusercontent.com/anindex/polystep/main/docs/figures/method_diagram.png" width="840"
@@ -28,7 +29,7 @@ backpropagation is usually faster.
28
29
 
29
30
  ```bash
30
31
  pip install polystep
31
- pip install "polystep[examples]" # optional demo dependencies
32
+ pip install "polystep[examples]" # dependencies for the repository's examples
32
33
  ```
33
34
 
34
35
  Python 3.11+ and PyTorch 2.8+ are required. Choose a CUDA build compatible with your
@@ -41,8 +42,7 @@ For development, install `pip install -e ".[dev]"` from a checkout.
41
42
  import torch
42
43
  from torch import nn
43
44
  from torch.utils.data import DataLoader, TensorDataset
44
- from polystep import HybridSubspace, PolyStepOptimizer, TrainConfig, train
45
- from polystep.transform import ParamLayout
45
+ from polystep import HybridSubspace, ParamLayout, PolyStepOptimizer, TrainConfig, train
46
46
 
47
47
  model = nn.Sequential(nn.Linear(16, 32), nn.ReLU(), nn.Linear(32, 3))
48
48
  loader = DataLoader(
@@ -56,10 +56,11 @@ optimizer = PolyStepOptimizer(model, subspace=subspace, solver="softmax")
56
56
  train(model, loader, nn.CrossEntropyLoss(), optimizer, TrainConfig(epochs=5))
57
57
  ```
58
58
 
59
- Replace the generated data with your dataset. Limit `max_subspace_dim` to control
60
- the forward budget. In manual loops, call `register_evaluator()` before each batch;
61
- `train()` does this automatically. See [performance](docs/performance.md) for
62
- candidate paths, thread counts, compilation, and memory settings.
59
+ Replace the generated data with your dataset. `max_subspace_dim=128` caps the search
60
+ at 128 coordinates, so each step scores 144 candidates. In manual loops, call
61
+ `register_evaluator()` before each batch; `train()` does this automatically. See
62
+ [performance](https://github.com/anindex/polystep/blob/main/docs/performance.md)
63
+ for candidate paths, thread counts, compilation, and memory settings.
63
64
 
64
65
  ### Ask/tell
65
66
 
@@ -76,6 +77,7 @@ print(es.best_fitness, es.best_solution)
76
77
  ### Synthetic objectives
77
78
 
78
79
  ```python
80
+ import torch
79
81
  from polystep import Ackley
80
82
  from polystep.solver import PolyStep
81
83
 
@@ -87,7 +89,8 @@ state = solver.run(torch.randn(100, 10))
87
89
 
88
90
  Each step samples a rotated polytope, evaluates its vertices, computes transport
89
91
  weights, and moves to their barycenter. Subspaces reduce the number of coordinates
90
- searched. Softmax treats blocks independently; Sinkhorn also constrains target mass.
92
+ searched. Softmax normalizes each particle's vertex weights; Sinkhorn also enforces
93
+ the target marginal.
91
94
 
92
95
  Finite-radius probes can cross flat regions of discontinuous losses. Performance
93
96
  depends on the radius, representation, and evaluation budget. PolyStep's optional
@@ -97,8 +100,8 @@ inherit classical direct-search or trust-region guarantees.
97
100
  ## Baselines
98
101
 
99
102
  `polystep.baselines` provides `openai_es`, `spsa`, `mezo`, `random_search`, `eggroll`,
100
- and `cma_es`. One evaluation means one candidate scored, even when candidates are
101
- batched:
103
+ and `cma_es`, which needs `pip install cma`. One evaluation means one candidate
104
+ scored, even when candidates are batched:
102
105
 
103
106
  ```python
104
107
  from polystep.baselines import Objective, openai_es
@@ -107,20 +110,30 @@ objective = Objective(lambda x: x.square().sum(1), dim=64, budget=10_000)
107
110
  result = openai_es(objective, sigma=0.05, lr=0.1, popsize=32)
108
111
  ```
109
112
 
110
- The paper runners support `--fair` for matched search spaces and evaluation budgets.
111
- EGGROLL uses a factored representation to retain matrix perturbations. See the
112
- [reproduction protocol](docs/reproducibility.md) for tuning and budget details.
113
+ The paper runners match search spaces, budgets and tuning as the
114
+ [reproducibility guide](https://github.com/anindex/polystep/blob/main/docs/reproducibility.md#protocol) describes.
113
115
 
114
116
  ## Benchmarks
115
117
 
116
- Five-seed mean ± sample standard deviation. These architecture tables compare
117
- validation-tuned methods at matched optimizer steps. Evaluation- and time-matched
118
- studies are reported separately in the [experiment index](experiments/EXPERIMENT_INDEX.md).
118
+ Five-seed mean ± sample standard deviation, with configurations and checkpoints
119
+ selected on validation data; the probSAT and RC2 columns are single runs. In the
120
+ architecture tables, OpenAI-ES, CMA-ES and EGGROLL get PolyStep's candidate-evaluation
121
+ budget, and SPSA, MeZO and random search get its step count. On argmax attention, hard
122
+ MoE and ETTh1, the population methods instead get 32 evaluations per PolyStep step,
123
+ spent at their own population size, and Adam trains for PolyStep's 30 epochs, except
124
+ 50 on ETTh1. On MAX-SAT, CMA-ES and OpenAI-ES get 50 evaluations per PolyStep step.
125
+ OpenAI-ES scores 50 candidates at each of PolyStep's steps, while CMA-ES doubles its
126
+ population at each restart and takes fewer generations. The tables were recorded
127
+ before 1.0; the
128
+ [reproducibility guide](https://github.com/anindex/polystep/blob/main/docs/reproducibility.md#changes-since-the-published-tables)
129
+ lists the rows that 1.0 changes. The
130
+ [experiment index](https://github.com/anindex/polystep/blob/main/experiments/EXPERIMENT_INDEX.md)
131
+ has the full tables.
119
132
 
120
133
  <!-- BENCH:START -->
121
134
  ### Non-differentiable tasks
122
135
 
123
- Test accuracy %. A dash means the result is not in this release.
136
+ Test accuracy %. A dash means the method was not run on that task.
124
137
 
125
138
  | Task | PolyStep | CMA-ES | OpenAI-ES | SPSA | Adam (surrogate) | Non-diff op |
126
139
  |---|---|---|---|---|---|---|
@@ -148,16 +161,17 @@ Test accuracy %. A dash means the result is not in this release.
148
161
 
149
162
  The SNN surrogate-gradient baseline varies substantially across seeds. Adam leads
150
163
  on the differentiable tasks, and specialized solvers lead on MAX-SAT. See
151
- [limitations](LIMITATIONS.md) for unsupported configurations and comparison limits.
164
+ [limitations](https://github.com/anindex/polystep/blob/main/LIMITATIONS.md)
165
+ for unsupported configurations and comparison limits.
152
166
 
153
167
  ## Documentation
154
168
 
155
- - [API reference](docs/api_overview.md)
156
- - [Performance](docs/performance.md)
157
- - [Examples](examples/README.md)
158
- - [Experiments](experiments/README.md) and [reproduction](docs/reproducibility.md)
159
- - [Determinism](docs/determinism.md)
160
- - [Contributing](CONTRIBUTING.md) and [changelog](CHANGELOG.md)
169
+ - [API reference](https://github.com/anindex/polystep/blob/main/docs/api_overview.md)
170
+ - [Performance](https://github.com/anindex/polystep/blob/main/docs/performance.md)
171
+ - [Examples](https://github.com/anindex/polystep/blob/main/examples/README.md)
172
+ - [Experiments](https://github.com/anindex/polystep/blob/main/experiments/README.md) and [reproduction](https://github.com/anindex/polystep/blob/main/docs/reproducibility.md)
173
+ - [Determinism](https://github.com/anindex/polystep/blob/main/docs/determinism.md)
174
+ - [Contributing](https://github.com/anindex/polystep/blob/main/CONTRIBUTING.md) and [changelog](https://github.com/anindex/polystep/blob/main/CHANGELOG.md)
161
175
 
162
176
  ## Citation
163
177
 
@@ -165,8 +179,9 @@ on the differentiable tasks, and specialized solvers lead on MAX-SAT. See
165
179
  @article{le2026training,
166
180
  title={Training Non-Differentiable Networks via Optimal Transport},
167
181
  author={Le, An T},
168
- journal={arXiv preprint arXiv:2605.01928},
169
- year={2026}
182
+ journal={Transactions on Machine Learning Research},
183
+ year={2026},
184
+ url={https://openreview.net/forum?id=8mlcqTTMuU}
170
185
  }
171
186
  ```
172
187
 
@@ -177,4 +192,4 @@ Thanks to [Viet T. Nguyen](https://vietngth.github.io/) for the
177
192
 
178
193
  ## License
179
194
 
180
- [Apache-2.0](LICENSE).
195
+ [Apache-2.0](https://github.com/anindex/polystep/blob/main/LICENSE).