polystep 0.13.0__tar.gz → 1.1.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 (176) hide show
  1. polystep-1.1.0/CHANGELOG.md +186 -0
  2. {polystep-0.13.0 → polystep-1.1.0}/CITATION.cff +4 -4
  3. polystep-1.1.0/CONTRIBUTING.md +65 -0
  4. polystep-1.1.0/LIMITATIONS.md +162 -0
  5. {polystep-0.13.0 → polystep-1.1.0}/PKG-INFO +79 -43
  6. polystep-1.1.0/README.md +219 -0
  7. polystep-1.1.0/docs/api_overview.md +527 -0
  8. polystep-1.1.0/docs/determinism.md +71 -0
  9. polystep-1.1.0/docs/performance.md +439 -0
  10. polystep-1.1.0/docs/reproducibility.md +175 -0
  11. {polystep-0.13.0 → polystep-1.1.0}/pyproject.toml +3 -14
  12. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/__init__.py +4 -65
  13. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/_compiled.py +17 -54
  14. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/_serialization.py +31 -77
  15. polystep-1.1.0/src/polystep/_step_blockwise.py +362 -0
  16. polystep-1.1.0/src/polystep/_step_core.py +190 -0
  17. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/_step_momentum.py +1 -11
  18. polystep-1.1.0/src/polystep/_step_monolithic.py +1117 -0
  19. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/adaptive_subspace.py +44 -72
  20. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/api.py +85 -71
  21. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/ask_tell.py +18 -16
  22. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/baselines/core.py +16 -14
  23. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/baselines/methods.py +27 -37
  24. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/__init__.py +0 -2
  25. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/rl/cartpole.py +8 -59
  26. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/rl/gym_evaluator.py +47 -51
  27. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/rl/policies.py +1 -4
  28. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/threads.py +1 -2
  29. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/utils.py +44 -51
  30. polystep-1.1.0/src/polystep/blockwise.py +135 -0
  31. polystep-1.1.0/src/polystep/cost_nn.py +2603 -0
  32. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/costs.py +20 -14
  33. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/dynamics.py +1 -3
  34. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/epsilon.py +40 -19
  35. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/factored_subspace.py +16 -26
  36. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/geometry.py +17 -42
  37. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/hybrid_subspace.py +143 -213
  38. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/layers/attention.py +43 -16
  39. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/optimizer.py +439 -588
  40. polystep-1.1.0/src/polystep/projection/sparse.py +249 -0
  41. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/quadratic_model.py +29 -21
  42. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solver.py +71 -102
  43. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/__init__.py +0 -2
  44. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/_shared.py +69 -41
  45. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/base.py +8 -2
  46. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/greedy.py +11 -31
  47. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/kl_softmax.py +62 -45
  48. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/sinkhorn.py +57 -69
  49. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/solvers/softmax.py +16 -3
  50. polystep-1.1.0/src/polystep/subspace.py +113 -0
  51. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/transform.py +118 -63
  52. {polystep-0.13.0 → polystep-1.1.0}/src/polystep.egg-info/PKG-INFO +79 -43
  53. {polystep-0.13.0 → polystep-1.1.0}/src/polystep.egg-info/SOURCES.txt +1 -10
  54. {polystep-0.13.0 → polystep-1.1.0}/src/polystep.egg-info/requires.txt +1 -4
  55. {polystep-0.13.0 → polystep-1.1.0}/tests/conftest.py +19 -98
  56. {polystep-0.13.0 → polystep-1.1.0}/tests/test_ablation_solvers.py +77 -99
  57. polystep-1.1.0/tests/test_adaptive_subspace.py +928 -0
  58. {polystep-0.13.0 → polystep-1.1.0}/tests/test_amortization.py +152 -41
  59. polystep-1.1.0/tests/test_api.py +497 -0
  60. polystep-1.1.0/tests/test_ask_tell.py +251 -0
  61. {polystep-0.13.0 → polystep-1.1.0}/tests/test_baseline_fairness.py +70 -81
  62. {polystep-0.13.0 → polystep-1.1.0}/tests/test_baselines.py +97 -20
  63. {polystep-0.13.0 → polystep-1.1.0}/tests/test_benchmarks.py +85 -3
  64. polystep-1.1.0/tests/test_biased_rotation.py +109 -0
  65. polystep-1.1.0/tests/test_blockwise.py +337 -0
  66. {polystep-0.13.0 → polystep-1.1.0}/tests/test_candidate_autocast.py +5 -7
  67. {polystep-0.13.0 → polystep-1.1.0}/tests/test_checkpoint_resume.py +284 -96
  68. {polystep-0.13.0 → polystep-1.1.0}/tests/test_compiled.py +46 -16
  69. {polystep-0.13.0 → polystep-1.1.0}/tests/test_controlled.py +1 -6
  70. polystep-1.1.0/tests/test_cost_nn.py +1432 -0
  71. polystep-1.1.0/tests/test_costs.py +134 -0
  72. {polystep-0.13.0 → polystep-1.1.0}/tests/test_determinism.py +15 -14
  73. polystep-1.1.0/tests/test_dfo.py +1015 -0
  74. {polystep-0.13.0 → polystep-1.1.0}/tests/test_diagnostics.py +6 -6
  75. {polystep-0.13.0 → polystep-1.1.0}/tests/test_dynamics.py +1 -1
  76. {polystep-0.13.0 → polystep-1.1.0}/tests/test_end_to_end_learning.py +2 -2
  77. {polystep-0.13.0 → polystep-1.1.0}/tests/test_epsilon.py +119 -26
  78. {polystep-0.13.0 → polystep-1.1.0}/tests/test_evaluator_compile_forward.py +17 -3
  79. polystep-1.1.0/tests/test_factored_subspace.py +276 -0
  80. polystep-1.1.0/tests/test_fastpath_parity.py +438 -0
  81. {polystep-0.13.0 → polystep-1.1.0}/tests/test_fused_projection.py +45 -17
  82. {polystep-0.13.0 → polystep-1.1.0}/tests/test_geometry.py +40 -41
  83. {polystep-0.13.0 → polystep-1.1.0}/tests/test_hybrid_subspace.py +335 -125
  84. {polystep-0.13.0 → polystep-1.1.0}/tests/test_input_validation.py +103 -47
  85. {polystep-0.13.0 → polystep-1.1.0}/tests/test_integration.py +43 -51
  86. {polystep-0.13.0 → polystep-1.1.0}/tests/test_kl_softmax.py +169 -9
  87. polystep-1.1.0/tests/test_layers.py +548 -0
  88. {polystep-0.13.0 → polystep-1.1.0}/tests/test_math_guards.py +107 -47
  89. polystep-1.1.0/tests/test_mixed_precision.py +844 -0
  90. {polystep-0.13.0 → polystep-1.1.0}/tests/test_mnist.py +57 -44
  91. {polystep-0.13.0 → polystep-1.1.0}/tests/test_no_test_set_leakage.py +5 -15
  92. polystep-1.1.0/tests/test_nondiff_data.py +93 -0
  93. {polystep-0.13.0 → polystep-1.1.0}/tests/test_nondiff_models.py +6 -152
  94. {polystep-0.13.0 → polystep-1.1.0}/tests/test_numerical_stress.py +5 -56
  95. {polystep-0.13.0 → polystep-1.1.0}/tests/test_objectives.py +16 -42
  96. polystep-1.1.0/tests/test_optimizer.py +968 -0
  97. {polystep-0.13.0 → polystep-1.1.0}/tests/test_per_sample.py +4 -3
  98. {polystep-0.13.0 → polystep-1.1.0}/tests/test_practical.py +0 -8
  99. {polystep-0.13.0 → polystep-1.1.0}/tests/test_probe_jitter.py +74 -38
  100. {polystep-0.13.0 → polystep-1.1.0}/tests/test_quadratic_model.py +152 -87
  101. {polystep-0.13.0 → polystep-1.1.0}/tests/test_rl_benchmarks.py +156 -5
  102. {polystep-0.13.0 → polystep-1.1.0}/tests/test_search_experiments.py +23 -4
  103. polystep-1.1.0/tests/test_sequential_fast_path.py +542 -0
  104. {polystep-0.13.0 → polystep-1.1.0}/tests/test_sinkhorn.py +227 -101
  105. {polystep-0.13.0 → polystep-1.1.0}/tests/test_sinkhorn_numerics.py +21 -10
  106. polystep-1.1.0/tests/test_site_vmap.py +2088 -0
  107. {polystep-0.13.0 → polystep-1.1.0}/tests/test_softmax.py +40 -23
  108. {polystep-0.13.0 → polystep-1.1.0}/tests/test_softmax_integration.py +18 -59
  109. {polystep-0.13.0 → polystep-1.1.0}/tests/test_sparse_delta_evaluator.py +309 -35
  110. {polystep-0.13.0 → polystep-1.1.0}/tests/test_sparse_projection.py +116 -32
  111. {polystep-0.13.0 → polystep-1.1.0}/tests/test_subspace_blockwise.py +132 -194
  112. {polystep-0.13.0 → polystep-1.1.0}/tests/test_subspace_contract.py +10 -9
  113. {polystep-0.13.0 → polystep-1.1.0}/tests/test_subspace_delta.py +100 -64
  114. {polystep-0.13.0 → polystep-1.1.0}/tests/test_subspace_gain.py +2 -2
  115. {polystep-0.13.0 → polystep-1.1.0}/tests/test_timeseries_val_selection.py +4 -8
  116. polystep-1.1.0/tests/test_transform.py +615 -0
  117. {polystep-0.13.0 → polystep-1.1.0}/tests/test_trust_region.py +138 -70
  118. {polystep-0.13.0 → polystep-1.1.0}/tests/test_tuning_sweep.py +21 -42
  119. polystep-0.13.0/CHANGELOG.md +0 -103
  120. polystep-0.13.0/CONTRIBUTING.md +0 -54
  121. polystep-0.13.0/LIMITATIONS.md +0 -92
  122. polystep-0.13.0/README.md +0 -180
  123. polystep-0.13.0/docs/api_overview.md +0 -217
  124. polystep-0.13.0/docs/determinism.md +0 -31
  125. polystep-0.13.0/docs/performance.md +0 -133
  126. polystep-0.13.0/docs/reproducibility.md +0 -80
  127. polystep-0.13.0/src/polystep/_step_blockwise.py +0 -648
  128. polystep-0.13.0/src/polystep/_step_core.py +0 -112
  129. polystep-0.13.0/src/polystep/_step_monolithic.py +0 -1295
  130. polystep-0.13.0/src/polystep/blockwise.py +0 -276
  131. polystep-0.13.0/src/polystep/cma.py +0 -117
  132. polystep-0.13.0/src/polystep/cma_subspace.py +0 -95
  133. polystep-0.13.0/src/polystep/cost_nn.py +0 -1663
  134. polystep-0.13.0/src/polystep/projection/sparse.py +0 -208
  135. polystep-0.13.0/src/polystep/solvers/tempered_softmax.py +0 -31
  136. polystep-0.13.0/src/polystep/subspace.py +0 -63
  137. polystep-0.13.0/tests/test_adaptive_subspace.py +0 -491
  138. polystep-0.13.0/tests/test_api.py +0 -312
  139. polystep-0.13.0/tests/test_ask_tell.py +0 -100
  140. polystep-0.13.0/tests/test_biased_rotation.py +0 -82
  141. polystep-0.13.0/tests/test_blockwise.py +0 -219
  142. polystep-0.13.0/tests/test_cma.py +0 -319
  143. polystep-0.13.0/tests/test_cma_subspace.py +0 -380
  144. polystep-0.13.0/tests/test_cost_nn.py +0 -519
  145. polystep-0.13.0/tests/test_costs.py +0 -63
  146. polystep-0.13.0/tests/test_dfo.py +0 -547
  147. polystep-0.13.0/tests/test_evaluator_regressions.py +0 -367
  148. polystep-0.13.0/tests/test_factored_subspace.py +0 -107
  149. polystep-0.13.0/tests/test_fastpath_parity.py +0 -168
  150. polystep-0.13.0/tests/test_layers.py +0 -254
  151. polystep-0.13.0/tests/test_mixed_precision.py +0 -496
  152. polystep-0.13.0/tests/test_nondiff_data.py +0 -151
  153. polystep-0.13.0/tests/test_optimizer.py +0 -758
  154. polystep-0.13.0/tests/test_regressions.py +0 -1105
  155. polystep-0.13.0/tests/test_sequential_fast_path.py +0 -353
  156. polystep-0.13.0/tests/test_silent_failures.py +0 -748
  157. polystep-0.13.0/tests/test_site_vmap.py +0 -382
  158. polystep-0.13.0/tests/test_transform.py +0 -282
  159. polystep-0.13.0/tests/test_vmap_layer_compatibility.py +0 -239
  160. {polystep-0.13.0 → polystep-1.1.0}/LICENSE +0 -0
  161. {polystep-0.13.0 → polystep-1.1.0}/MANIFEST.in +0 -0
  162. {polystep-0.13.0 → polystep-1.1.0}/setup.cfg +0 -0
  163. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/baselines/__init__.py +0 -0
  164. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/rl/__init__.py +0 -0
  165. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/benchmarks/rl/metrics.py +0 -0
  166. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/layers/__init__.py +0 -0
  167. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/layers/rnn.py +0 -0
  168. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/objectives/__init__.py +0 -0
  169. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/objectives/base.py +0 -0
  170. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/objectives/synthetic.py +0 -0
  171. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/projection/__init__.py +0 -0
  172. {polystep-0.13.0 → polystep-1.1.0}/src/polystep/py.typed +0 -0
  173. {polystep-0.13.0 → polystep-1.1.0}/src/polystep.egg-info/dependency_links.txt +0 -0
  174. {polystep-0.13.0 → polystep-1.1.0}/src/polystep.egg-info/top_level.txt +0 -0
  175. {polystep-0.13.0 → polystep-1.1.0}/tests/__init__.py +0 -0
  176. {polystep-0.13.0 → polystep-1.1.0}/tests/test_sparse_integration.py +0 -0
@@ -0,0 +1,186 @@
1
+ # Changelog
2
+
3
+ ## 1.1.0 - 2026-10-24
4
+
5
+ ### Features and performance
6
+
7
+ - Add `step_batch(inputs, targets, loss_fn=...)` for supervised training. It shares
8
+ the fast evaluation paths with `train()`; both accept tuple and dict inputs.
9
+ `register_evaluator` now returns a closure for the registered batch.
10
+ - Add `is_causal` support to `VmapSafeMultiHeadAttention`.
11
+ - Extend fast MLP evaluation to functional losses and traceable custom forwards.
12
+ Reuse unchanged forward work, reduce CPU cross-entropy overhead, and replay
13
+ site-local CUDA forwards with CUDA graphs.
14
+ - Measured full-space MLP steps are 1.9–3.3x faster on one CPU thread and 2.6x on a
15
+ shared RTX 5090 than 1.0.0. Tiny tied-weight models can be slower on CUDA. See
16
+ [performance](docs/performance.md) for the evaluation paths and tradeoffs.
17
+ - Refresh the examples and their metrics, clarify simulated Loihi adaptation, and
18
+ add SNN training and CartPole GIFs. The MAX-SAT example reaches about 98% SAT with
19
+ 600 default steps instead of 1500.
20
+
21
+ ### Fixes
22
+
23
+ - Restore forward-written buffers between scoring calls and vmap chunks, even on
24
+ failure, and preserve scores that share those buffers. Protect frozen embedding
25
+ weights changed by `max_norm`, reject evaluators for another model, and follow
26
+ model dtype and device changes.
27
+ - Preserve finite-difference gradients and curvature at small probe radii. Correct
28
+ KL zero-support handling and marginal diagnostics, including tiny target masses.
29
+ - Fix CUDA rotation for large adaptive subspaces, carry FP16/BF16 absorb rounding
30
+ error forward, and correct biased rotation, screening, amortized schedules,
31
+ epsilon restarts, half-precision attention, and loss precision in ES and baselines.
32
+ - Fall back to sequential scoring when vmap cannot batch a model, including stock
33
+ `nn.LSTM`.
34
+
35
+ ### Upgrade notes
36
+
37
+ - Pass the model to `PolyStepOptimizer`, not `model.parameters()`. Rebuild the
38
+ optimizer if its trainable parameter set changes. Separate parameters or buffers
39
+ that share trainable storage now raise `ValueError`; tie weights by assigning
40
+ the same Parameter, or register a clone.
41
+ - `FactoredSubspace` redraws `B` every 5 OT steps by default; set
42
+ `rotation_interval=0` for the old behavior. Hybrid and factored seeded bases change.
43
+ - `SinkhornSolver` and `KLSoftmaxSolver` default to `threshold=3e-5`, using relative
44
+ marginal error and the dual step divided by epsilon, respectively. Stalled solves
45
+ above the threshold report `converged=False`.
46
+ - `train()` uses `step_batch`; `restore_best` compares weights on the first training
47
+ batch. Seeded results can differ from 1.0.
48
+ - For compiled hybrid/factored runs, rebuild the optimizer from saved model weights:
49
+ checkpoint parameter names no longer contain the `_orig_mod.` prefix. The
50
+ [upgrade guide](docs/api_overview.md#upgrading-from-10) covers all compatibility
51
+ changes; [reproducibility](docs/reproducibility.md#changes-since-the-published-tables)
52
+ lists the affected published results.
53
+
54
+ ## 1.0.0 - 2026-10-13
55
+
56
+ ### Upgrade notes
57
+
58
+ - Use `PolyStepOptimizer` for neural networks and `PolyStep` for standalone objectives.
59
+ - Replace diagonal CMA with `AdaptiveSubspace`, tempered softmax with `SoftmaxSolver`,
60
+ and `auto_epsilon` with `ProgressiveEpsilon`. Probe reuse has been removed.
61
+ - Checkpoints use format 6. Rebuild the optimizer from saved model weights when
62
+ upgrading from 0.13; older optimizer checkpoints cannot be loaded.
63
+ - Some low-level exports and experiment helpers moved or were removed. The
64
+ [migration guide](docs/api_overview.md#upgrading-from-013) covers the main API
65
+ replacements and checkpoint requirements.
66
+
67
+ ### Fixes
68
+
69
+ - Preserve live model weights, buffers, momentum, and trust-region state across
70
+ evaluation, subspace updates, and checkpoint restoration.
71
+ - Handle nonfinite costs, tiny transport rows, integer losses, and half-precision
72
+ reductions consistently. Sparse CUDA projections repeat in deterministic mode.
73
+ - Accept device strings in rotation sampling and float32 masks in half-precision
74
+ CUDA attention.
75
+ - Correct baseline budgets, tied ranks, rollout accounting, and download handling.
76
+
77
+ ### Performance and examples
78
+
79
+ - Reuse unchanged forward work, update only affected weights, and size candidate
80
+ chunks from measured activations. Speed up blockwise, factored, and adaptive steps.
81
+ - Reduce sparse projection storage and host synchronization; compile Sinkhorn loops.
82
+ - Refresh all 11 examples, their evaluation paths, and their documented settings.
83
+ See [performance](docs/performance.md) for measurements and tradeoffs.
84
+ - Consolidate redundant tests and keep release validation focused on the built wheel.
85
+
86
+ ## 0.13.0 - 2026-09-22
87
+
88
+ - Fix KL row mass, nonfinite inputs, evaluator hooks, and chunk handling.
89
+ - Reuse shared MLP forward work and load only requested MNIST images.
90
+ - Fix example checkpoint selection and evaluation budgets; trim demo dependencies.
91
+
92
+ ## 0.12.0 - 2026-09-17
93
+
94
+ - Fix cross-entropy targets, stale caches, attention masks, nonfinite best values,
95
+ and mixed-dtype evaluation.
96
+ - Correct simplex gradients and whole-model trust predictions; migrate checkpoints
97
+ to format 5.
98
+ - Reuse MLP prefixes, use native CPU attention, and compile site-local evaluation.
99
+ - Average tied baseline ranks; add controlled experiments and forward benchmarks.
100
+ - Validate sparse dimensions and factored ranks; simplify documentation.
101
+
102
+ ## 0.11.0 - 2026-08-14
103
+
104
+ - Remove `LowRankSubspace`, `FactorSpec`, and `LinearSubspace`; use `HybridSubspace`
105
+ or `FactoredSubspace`. Replace `absorb_every` with `absorb_mode`.
106
+ - Remove `polystep.benchmarks.baselines`, `evotorch`, `SinkhornSolver.solve(init_eps)`,
107
+ and solver warm-start methods. `CMAAdaptiveSubspace.base` is removed.
108
+ - Require `dim` in `probe_scale_of`; correct baseline probe scaling.
109
+ - Fix nonfinite costs, zero-mass rows, quadratic evaluation counts, and resume state.
110
+ - Add tight-frame quadratic fits and cache radius-jitter sampling.
111
+
112
+ ## 0.10.1 - 2026-08-01
113
+
114
+ - Require `objective_token` for probe reuse and reject invalid optimizer settings.
115
+ - Fix blockwise evaluation, checkpoint aliasing, dtype changes, and tied weights.
116
+ - Add site-local evaluation to blockwise mode and reduce subspace allocations.
117
+
118
+ ## 0.10.0 - 2026-07-30
119
+
120
+ - Default to simplex sampling; retune radii when upgrading.
121
+ - Change seeded rotation streams and clear momentum on basis changes.
122
+ - Enable forward compilation on the in-place path by default.
123
+ - Remove `projection_mode`, `use_csa`, low-level block strategies, and unused exports.
124
+ - Move NumPy to optional extras; validate schedules and constructor arguments.
125
+
126
+ ## 0.9.0 - 2026-07-27
127
+
128
+ - Add sparse-delta evaluation, `FactoredSubspace`, greedy solvers, and evaluator
129
+ registration helpers.
130
+ - Fix probe reuse, screening, subspace state, tied weights, and best-weight restoration.
131
+ - Extend checkpoint state and reduce projection and evaluation allocations.
132
+
133
+ ## 0.8.0 - 2026-07-24
134
+
135
+ - Add optimizer checkpointing and forward-compilation controls.
136
+ - Fix mixed precision, scheduled epsilon, and invalid solver inputs.
137
+
138
+ ## 0.7.0 - 2026-07-20
139
+
140
+ - Publish on PyPI and reuse blockwise candidate buffers.
141
+ - Fix constant-offset cost stability and biased rotations.
142
+
143
+ ## 0.6.1 - 2026-07-15
144
+
145
+ - Add the hard decision-tree example.
146
+ - Fix mixed precision, barycentric normalization, and candidate memory estimates.
147
+ - Cache hybrid projections between rotations.
148
+
149
+ ## 0.6.0 - 2026-07-12
150
+
151
+ - Add direct F1 optimization and optimizer-variant experiments.
152
+ - Fix CMA scaling, solver validation, and trust-region state.
153
+
154
+ ## 0.5.0 - 2026-07-07
155
+
156
+ - Add `PolyStepES`, `minimize`, and the binary-network example.
157
+ - Fix Anderson acceleration, Newton selection, screening, and step adaptation.
158
+
159
+ ## 0.4.0 - 2026-06-21
160
+
161
+ - Share solver cost handling and remove low-rank Sinkhorn.
162
+ - Fix cost scaling, dual recentering, and activation semantics.
163
+
164
+ ## 0.3.0 - 2026-05-27
165
+
166
+ - Reduce GPU synchronization in solvers and blockwise updates.
167
+ - Fix CMA rotation forwarding and API examples.
168
+
169
+ ## 0.2.2 - 2026-05-21
170
+
171
+ - Use unit-radius vertex generators with keyword-only `radius`.
172
+ - Fix CMA transport weighting and stale API examples.
173
+
174
+ ## 0.2.1 - 2026-05-14
175
+
176
+ - Add the Loihi-style SNN adaptation example.
177
+
178
+ ## 0.2.0 - 2026-05-14
179
+
180
+ - Require Python 3.11, PyTorch 2.8, and NumPy 2.0 for optional extras.
181
+ - Reduce solver synchronization and use inference mode for evaluation.
182
+
183
+ ## 0.1.0 - 2026-05-03
184
+
185
+ - Initial release: gradient-free training, OT solvers, subspaces, vmap-compatible
186
+ layers, examples, and experiment runners.
@@ -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.1.0
10
+ date-released: "2026-10-24"
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
+ From the repository root, install the development dependencies and 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 --maxprocesses=4
10
+ ```
11
+
12
+ A bug fix should have a small regression test of observable behavior. Extend an
13
+ existing case when it already covers the same path; avoid separate tests for trivial
14
+ defaults, getters, or repeated shapes. Keep public APIs typed. Comments should
15
+ explain constraints, numerical choices, or non-obvious behavior.
16
+
17
+ ## Tests
18
+
19
+ The default suite excludes `slow` and `gpu` tests. To select them:
20
+
21
+ ```bash
22
+ pytest tests/ -m slow
23
+ pytest tests/ -m gpu
24
+ pytest tests/ -m ""
25
+ ```
26
+
27
+ Capping parallel test workers at four limits startup and memory costs. The fast
28
+ suite uses one CPU thread per worker; `POLYSTEP_TEST_THREADS` overrides it. Slow
29
+ tests use `ncpu - 8` threads, clamped to 2-16 and split across xdist workers, and may
30
+ download datasets. Keep downloads and training-quality checks in the slow suite;
31
+ use synthetic inputs for unit tests.
32
+
33
+ Tests importing `experiments/` must skip when that directory is absent, since it is
34
+ excluded from distributions. Install `pip install -e ".[dev,experiments]"` to run
35
+ tests that need optional experiment dependencies. Missing optional dependencies and
36
+ unavailable hardware are valid reasons to skip a test; unsupported parameter pairs
37
+ should not be collected. Tests run without automatic retries.
38
+
39
+ Pushes and pull requests test Python 3.11 with PyTorch 2.8 and Python 3.14 with
40
+ current PyTorch. Scheduled and manual runs add Python 3.12/3.13, coverage, optional
41
+ experiment dependencies, real-data learning checks, and the shipped tests against
42
+ the installed wheel. Every run builds both distributions and checks the wheel's
43
+ imports and type marker.
44
+
45
+ ## Releases
46
+
47
+ Update `polystep.__version__`. Set the same version and the release date in
48
+ `CITATION.cff`. Add a `CHANGELOG.md` entry with the heading `## X.Y.Z - YYYY-MM-DD`.
49
+ `pyproject.toml` reads the package version automatically.
50
+ If the project's maturity changes, update its `Development Status` classifier.
51
+
52
+ ```bash
53
+ uv build
54
+ uvx twine check --strict dist/*
55
+ ```
56
+
57
+ After the full manual CI workflow passes on the release commit, publish a GitHub
58
+ Release tagged `vX.Y.Z`. The release workflow checks versions, lint, metadata, and
59
+ the installed wheel before publishing. Only the publish job has OIDC permission;
60
+ it uses the `pypi` environment 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.
@@ -0,0 +1,162 @@
1
+ # Limitations
2
+
3
+ ## Cost and search space
4
+
5
+ PolyStep needs many candidate forwards per update. Differentiable models usually
6
+ train faster with backpropagation. Small subspaces reduce cost but may exclude useful
7
+ directions; very small projection ratios can fail on large models.
8
+
9
+ `HybridSubspace` caps each layer at its parameter count. Large dense projections may
10
+ use sparse signed columns, which are not orthonormal. Claims requiring an orthogonal
11
+ projection do not apply to those columns.
12
+
13
+ `FactoredSubspace` restricts each step to the input directions spanned by its factor
14
+ `B`. It redraws `B` every 5 OT steps by default; with `rotation_interval=0` the whole
15
+ run stays in one set of directions. Retune radii when switching subspaces.
16
+
17
+ A basis change clears momentum, the `amortize_steps` coasting direction and the
18
+ `biased_rotation` direction, so these options need a rotation interval longer than one
19
+ step. `AdaptiveSubspace` rotates every step by default, and the optimizer warns when
20
+ any subspace combines these options with `rotation_interval=1`. `FactoredSubspace`
21
+ clears them every 5 OT steps without a warning. Under `amortize_steps`,
22
+ `rotation_interval` and `absorb_interval` count OT steps only.
23
+
24
+ Heavy-ball momentum can amplify the barycentric displacement; `step_radius` bounds the
25
+ displacement before momentum.
26
+
27
+ ## Evaluation
28
+
29
+ - `step()` expects `closure(batched_params) -> losses` with shape `(N,)`.
30
+ The candidate tensors may be expanded views or share storage that the next chunk
31
+ overwrites, so treat them as read-only and clone any you keep after the closure
32
+ returns.
33
+ - `step_batch()` and `train()` register each batch. A manual `step(closure)` loop must
34
+ register the evaluator and current batch to use the fast paths. The evaluator's
35
+ loss and the closure must compute the same objective.
36
+ - Multi-input batches use vmap and the site-local path; the batched-matmul, delta and
37
+ factored paths and multifidelity screening need one input tensor.
38
+ - Tied weights must be the same Parameter object, and buffers may not share memory
39
+ with trainable parameters. Register a clone or compute the tensor in forward instead.
40
+ - `NNCostEvaluator`, `step_batch()` and `train()` score candidates in eval mode:
41
+ standard dropout is off and batch-norm layers do not update their running statistics.
42
+ A custom closure that runs the model in train mode compares candidates under
43
+ different dropout masks.
44
+ - The evaluator restores buffers written by the forward, such as a call counter,
45
+ after each batched chunk and each candidate in the sequential and in-place loops.
46
+ Embedding weights changed by `max_norm` are restored even when frozen. Update
47
+ persistent state outside the closure.
48
+ - Writes that leave the tensor version counter unchanged, such as
49
+ `F.batch_norm(training=True)` running statistics or writes through `.data`, are found
50
+ by comparing values after the first forward and the first candidate of each
51
+ sequential, in-place or chunked vmap sweep. A write first triggered after those
52
+ checks can go undetected and persist into later evaluations.
53
+ - Float inputs are cast to the dtype of the model's first floating weight only when
54
+ the forward raises on them as given. The evaluator checks this with one extra
55
+ forward per input dtype and puts back the buffers it changed. The delta and
56
+ factored paths, which accept plain Linear stacks only, always cast.
57
+ - Mixed-dtype models require `HybridSubspace` or `FactoredSubspace`. Full-space and
58
+ global-subspace coordinates use one dtype and reject mixed parameter dtypes.
59
+ - `mixed_precision=True` uses BF16 geometry and forwards; solver costs use FP32.
60
+ Models needing autocast internally must provide it. Half-precision candidate outputs
61
+ reduce in FP32 only under the builtin cross-entropy, MSE and L1 losses; other losses
62
+ may round nearby candidate scores to the same value. With FP16 or BF16 parameters,
63
+ `FactoredSubspace` materializes every candidate, and the fast paths can differ from
64
+ the model's own Linear output by about one unit in the last place (ulp), enough to
65
+ flip a hard threshold. `use_inplace=True` scores with the model's forward. See
66
+ [precision](docs/performance.md#compilation-and-precision).
67
+ - `step_batch()` and `train()` reject `trust_region`. See
68
+ [radius control](docs/api_overview.md#quadratic-model-and-radius-control) for its
69
+ manual-step use.
70
+ - `trust_region` and `use_adaptive_radius` both scale the step. Choose one; the
71
+ optimizer warns when both are on.
72
+ - Multifidelity screening imputes dropped costs using an additive offset. This can
73
+ bias the full-fidelity cost estimate.
74
+ - Full-space blockwise mode does not support every monolithic adaptation option;
75
+ unsupported options warn at construction.
76
+ - Automatic chunk sizing counts outputs of layers with parameters. On a site-local
77
+ path, it counts all module outputs from the perturbed layer onward, provided the
78
+ forward neither draws random numbers nor reads the perturbed parameter before
79
+ calling its layer. Attention scores and other temporary tensors inside a module's
80
+ forward are not counted. Set `chunk_size` by hand if such a model runs out of memory.
81
+ Under vmap, CUDA attention materializes the scores of every candidate in the chunk.
82
+ `PolyStepOptimizer(chunk_size=...)` caps the candidates scored per step chunk, and
83
+ `NNCostEvaluator(chunk_size=...)` caps how many of them each vmap call batches.
84
+ - On CUDA the site-local path replays CUDA graphs, which freeze Python-side state the
85
+ forward reads, such as a Python counter or a flag. Pass `compile_forward=False` for
86
+ such a forward.
87
+ - The library does not distribute optimizer state across multiple GPUs.
88
+
89
+ ## Attention and recurrent layers
90
+
91
+ [`VmapSafeMultiHeadAttention`](src/polystep/layers/attention.py) requires batch-first
92
+ inputs and `kdim == vdim == embed_dim`. It does not support `add_bias_kv`,
93
+ `add_zero_attn`, or returned attention weights. `is_causal` is a hint that needs
94
+ `attn_mask`, as in `nn.MultiheadAttention`. It returns an output tensor, not an
95
+ `(output, weights)` tuple.
96
+
97
+ Boolean masks use `True` for masked positions; float masks are additive. Attention
98
+ masks accept `(query, key)`, `(batch * heads, query, key)`, and broadcastable 4D shapes.
99
+ The wrapper also accepts `(batch, query, key)` masks.
100
+
101
+ CPU vmap may emit a batching-fallback warning.
102
+
103
+ Stock `nn.MultiheadAttention` and `nn.TransformerEncoderLayer` work, but vmap has no
104
+ batching rule for their fused kernels, so it runs them once per candidate and PyTorch
105
+ prints a performance-drop warning. On CUDA this makes a step several times slower, and
106
+ the evaluator warns and points to `VmapSafeMultiHeadAttention`. On CPU stock
107
+ `nn.MultiheadAttention` measured faster than `VmapSafeMultiHeadAttention`, so the
108
+ evaluator stays silent there.
109
+
110
+ Stock `nn.LSTM`, `nn.GRU` and `nn.RNN` have no vmap batching rule, so the evaluator
111
+ scores them one candidate at a time in a sequential loop, with a warning. On a small
112
+ CPU model, `NNCostEvaluator(..., use_inplace=True)` ran 1.1 to 1.3 times faster than
113
+ the loop, and `VmapSafeLSTM` 3 to 5 times faster. `step_batch()` and `train()` build
114
+ their own evaluator, so they take the loop for these layers.
115
+
116
+ [`VmapSafeLSTM`](src/polystep/layers/rnn.py) requires batch-first dense inputs. It does
117
+ not support bidirectional recurrence, projection, or `PackedSequence`. Its explicit
118
+ cell computations can be slower than fused cuDNN recurrence.
119
+
120
+ ## Solvers and mathematical scope
121
+
122
+ `SoftmaxSolver` enforces source row sums only; it ignores the target marginal and
123
+ warns for a nonuniform one. Use `SinkhornSolver` to enforce both marginals.
124
+ Independent particle rotations mean Sinkhorn's shared column index does not identify
125
+ a common direction across particles.
126
+
127
+ Sinkhorn requires `omega` in `[0.5, 1.95]`. Anderson acceleration and adaptive omega
128
+ are inactive in fixed-iteration mode (`threshold <= 0`). Sinkhorn and KLSoftmax promote
129
+ FP16/BF16 costs to FP32. Cost differences and reductions must also fit the solver's
130
+ dtype: finite FP32 inputs near its limits can still overflow when recentered or
131
+ averaged. Rescale the objective before evaluation or return FP64 costs in that case.
132
+
133
+ All nonfinite costs are invalid. `min_cost_greedy` and `top_k_mean` skip a vertex
134
+ with any nonfinite probe, and the solve leaves a particle with no feasible vertex
135
+ where it is. The weighted solvers replace each nonfinite cost with twice the largest
136
+ finite magnitude plus one, clamped to the dtype limit. An infeasible candidate can
137
+ therefore retain weight, depending on epsilon. With `scale_cost='mean'` or
138
+ `'max_cost'`, the solvers compute the penalty after scaling. Otherwise, that weight
139
+ also depends on the cost offset.
140
+
141
+ Balanced Sinkhorn sends every column its prescribed mass, even when all of that
142
+ column's candidates are infeasible. The monolithic step records a vertex's cost as
143
+ the mean of its probes with each nonfinite probe already penalized, so with several
144
+ probes a vertex with only some nonfinite probes can still set the best-vertex
145
+ progress that drives the adaptive radius.
146
+
147
+ Quadratic fits are finite-radius models. Simplex gradients can be biased on
148
+ anisotropic quadratics, and block models omit cross-block curvature. A fit does not
149
+ supply a classical derivative at a discontinuity. The deferred radius controller
150
+ does not perform a trust-region acceptance test.
151
+
152
+ ## Benchmark comparisons
153
+
154
+ The architecture tables use validation-selected checkpoints. `--allow-test-leakage`
155
+ is an explicit opt-in for test-selected runs, which standard aggregation rejects.
156
+
157
+ The SNN surrogate-gradient baseline has substantial variation across seeds. MAX-SAT
158
+ search heuristics use flip budgets, which are not equivalent to neural candidate
159
+ budgets. Head-only GPT-2 experiments do not establish full-model fine-tuning results.
160
+
161
+ The [reproducibility guide](docs/reproducibility.md#protocol) gives the protocols, and the
162
+ [experiment index](experiments/EXPERIMENT_INDEX.md) has the results.