polystep 0.11.0__tar.gz → 0.13.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.
- polystep-0.13.0/CHANGELOG.md +103 -0
- {polystep-0.11.0 → polystep-0.13.0}/CITATION.cff +2 -2
- polystep-0.13.0/CONTRIBUTING.md +54 -0
- polystep-0.13.0/LIMITATIONS.md +92 -0
- polystep-0.13.0/PKG-INFO +237 -0
- polystep-0.13.0/README.md +180 -0
- polystep-0.13.0/docs/api_overview.md +217 -0
- polystep-0.13.0/docs/determinism.md +31 -0
- polystep-0.13.0/docs/performance.md +133 -0
- polystep-0.13.0/docs/reproducibility.md +80 -0
- {polystep-0.11.0 → polystep-0.13.0}/pyproject.toml +1 -2
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/__init__.py +1 -1
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/_serialization.py +9 -1
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/_step_monolithic.py +37 -47
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/ask_tell.py +3 -3
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/baselines/core.py +11 -4
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/utils.py +5 -4
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/cost_nn.py +142 -47
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/factored_subspace.py +2 -1
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/layers/attention.py +32 -18
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/optimizer.py +20 -25
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/projection/sparse.py +8 -4
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/quadratic_model.py +14 -8
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solver.py +5 -5
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/_shared.py +5 -4
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/greedy.py +3 -1
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/kl_softmax.py +12 -12
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/sinkhorn.py +1 -5
- polystep-0.13.0/src/polystep.egg-info/PKG-INFO +237 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep.egg-info/SOURCES.txt +4 -1
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep.egg-info/requires.txt +1 -2
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_ablation_solvers.py +14 -55
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_baseline_fairness.py +2 -90
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_baselines.py +16 -18
- polystep-0.13.0/tests/test_controlled.py +65 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_dfo.py +1 -5
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_end_to_end_learning.py +0 -40
- polystep-0.13.0/tests/test_evaluator_regressions.py +367 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_fused_projection.py +2 -2
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_input_validation.py +7 -6
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_kl_softmax.py +18 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_mnist.py +19 -0
- polystep-0.13.0/tests/test_no_test_set_leakage.py +109 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_nondiff_models.py +1 -2
- polystep-0.13.0/tests/test_practical.py +73 -0
- polystep-0.13.0/tests/test_search_experiments.py +70 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_silent_failures.py +8 -4
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_softmax.py +1 -46
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_subspace_contract.py +9 -30
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_subspace_delta.py +1 -1
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_timeseries_val_selection.py +1 -1
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_trust_region.py +14 -16
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_tuning_sweep.py +42 -48
- polystep-0.11.0/CHANGELOG.md +0 -669
- polystep-0.11.0/CONTRIBUTING.md +0 -91
- polystep-0.11.0/LIMITATIONS.md +0 -236
- polystep-0.11.0/PKG-INFO +0 -420
- polystep-0.11.0/README.md +0 -362
- polystep-0.11.0/docs/api_overview.md +0 -380
- polystep-0.11.0/docs/determinism.md +0 -79
- polystep-0.11.0/docs/performance.md +0 -525
- polystep-0.11.0/docs/reproducibility.md +0 -121
- polystep-0.11.0/src/polystep.egg-info/PKG-INFO +0 -420
- polystep-0.11.0/tests/test_no_test_set_leakage.py +0 -165
- polystep-0.11.0/tests/test_sweep_protocol.py +0 -214
- {polystep-0.11.0 → polystep-0.13.0}/LICENSE +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/MANIFEST.in +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/setup.cfg +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/_compiled.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/_step_blockwise.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/_step_core.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/_step_momentum.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/adaptive_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/api.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/baselines/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/baselines/methods.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/rl/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/rl/cartpole.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/rl/gym_evaluator.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/rl/metrics.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/rl/policies.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/benchmarks/threads.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/blockwise.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/cma.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/cma_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/costs.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/dynamics.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/epsilon.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/geometry.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/hybrid_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/layers/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/layers/rnn.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/objectives/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/objectives/base.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/objectives/synthetic.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/projection/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/py.typed +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/base.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/softmax.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/solvers/tempered_softmax.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep/transform.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep.egg-info/dependency_links.txt +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/src/polystep.egg-info/top_level.txt +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/__init__.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/conftest.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_adaptive_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_amortization.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_api.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_ask_tell.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_benchmarks.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_biased_rotation.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_blockwise.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_candidate_autocast.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_checkpoint_resume.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_cma.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_cma_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_compiled.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_cost_nn.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_costs.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_determinism.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_diagnostics.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_dynamics.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_epsilon.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_evaluator_compile_forward.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_factored_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_fastpath_parity.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_geometry.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_hybrid_subspace.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_integration.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_layers.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_math_guards.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_mixed_precision.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_nondiff_data.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_numerical_stress.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_objectives.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_optimizer.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_per_sample.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_probe_jitter.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_quadratic_model.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_regressions.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_rl_benchmarks.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_sequential_fast_path.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_sinkhorn.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_sinkhorn_numerics.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_site_vmap.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_softmax_integration.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_sparse_delta_evaluator.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_sparse_integration.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_sparse_projection.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_subspace_blockwise.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_subspace_gain.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_transform.py +0 -0
- {polystep-0.11.0 → polystep-0.13.0}/tests/test_vmap_layer_compatibility.py +0 -0
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 0.13.0 - 2026-09-22
|
|
4
|
+
|
|
5
|
+
- Fix KL row mass, nonfinite inputs, evaluator hooks, and chunk handling.
|
|
6
|
+
- Reuse shared MLP forward work and load only requested MNIST images.
|
|
7
|
+
- Fix example checkpoint selection and evaluation budgets; trim demo dependencies.
|
|
8
|
+
|
|
9
|
+
## 0.12.0 - 2026-09-17
|
|
10
|
+
|
|
11
|
+
- Fix cross-entropy targets, stale caches, attention masks, nonfinite best values,
|
|
12
|
+
and mixed-dtype evaluation.
|
|
13
|
+
- Correct simplex gradients and whole-model trust predictions; migrate checkpoints
|
|
14
|
+
to format 5.
|
|
15
|
+
- Reuse MLP prefixes, use native CPU attention, and compile site-local evaluation.
|
|
16
|
+
- Average tied baseline ranks; add controlled experiments and forward benchmarks.
|
|
17
|
+
- Validate sparse dimensions and factored ranks; simplify documentation.
|
|
18
|
+
|
|
19
|
+
## 0.11.0 - 2026-08-14
|
|
20
|
+
|
|
21
|
+
- Remove `LowRankSubspace`, `FactorSpec`, and `LinearSubspace`; use `HybridSubspace`
|
|
22
|
+
or `FactoredSubspace`. Replace `absorb_every` with `absorb_mode`.
|
|
23
|
+
- Remove `polystep.benchmarks.baselines`, `evotorch`, `SinkhornSolver.solve(init_eps)`,
|
|
24
|
+
and solver warm-start methods. `CMAAdaptiveSubspace.base` is removed.
|
|
25
|
+
- Require `dim` in `probe_scale_of`; correct baseline probe scaling.
|
|
26
|
+
- Fix nonfinite costs, zero-mass rows, quadratic evaluation counts, and resume state.
|
|
27
|
+
- Add tight-frame quadratic fits and cache radius-jitter sampling.
|
|
28
|
+
|
|
29
|
+
## 0.10.1 - 2026-08-01
|
|
30
|
+
|
|
31
|
+
- Require `objective_token` for probe reuse and reject invalid optimizer settings.
|
|
32
|
+
- Fix blockwise evaluation, checkpoint aliasing, dtype changes, and tied weights.
|
|
33
|
+
- Add site-local evaluation to blockwise mode and reduce subspace allocations.
|
|
34
|
+
|
|
35
|
+
## 0.10.0 - 2026-07-30
|
|
36
|
+
|
|
37
|
+
- Default to simplex sampling; retune radii when upgrading.
|
|
38
|
+
- Change seeded rotation streams and clear momentum on basis changes.
|
|
39
|
+
- Enable forward compilation on the in-place path by default.
|
|
40
|
+
- Remove `projection_mode`, `use_csa`, low-level block strategies, and unused exports.
|
|
41
|
+
- Move NumPy to optional extras; validate schedules and constructor arguments.
|
|
42
|
+
|
|
43
|
+
## 0.9.0 - 2026-07-27
|
|
44
|
+
|
|
45
|
+
- Add sparse-delta evaluation, `FactoredSubspace`, greedy solvers, and evaluator
|
|
46
|
+
registration helpers.
|
|
47
|
+
- Fix probe reuse, screening, subspace state, tied weights, and best-weight restoration.
|
|
48
|
+
- Extend checkpoint state and reduce projection and evaluation allocations.
|
|
49
|
+
|
|
50
|
+
## 0.8.0 - 2026-07-24
|
|
51
|
+
|
|
52
|
+
- Add optimizer checkpointing and forward-compilation controls.
|
|
53
|
+
- Fix mixed precision, scheduled epsilon, and invalid solver inputs.
|
|
54
|
+
|
|
55
|
+
## 0.7.0 - 2026-07-20
|
|
56
|
+
|
|
57
|
+
- Publish on PyPI and reuse blockwise candidate buffers.
|
|
58
|
+
- Fix constant-offset cost stability and biased rotations.
|
|
59
|
+
|
|
60
|
+
## 0.6.1 - 2026-07-15
|
|
61
|
+
|
|
62
|
+
- Add the hard decision-tree example.
|
|
63
|
+
- Fix mixed precision, barycentric normalization, and candidate memory estimates.
|
|
64
|
+
- Cache hybrid projections between rotations.
|
|
65
|
+
|
|
66
|
+
## 0.6.0 - 2026-07-12
|
|
67
|
+
|
|
68
|
+
- Add direct F1 optimization and optimizer-variant experiments.
|
|
69
|
+
- Fix CMA scaling, solver validation, and trust-region state.
|
|
70
|
+
|
|
71
|
+
## 0.5.0 - 2026-07-07
|
|
72
|
+
|
|
73
|
+
- Add `PolyStepES`, `minimize`, and the binary-network example.
|
|
74
|
+
- Fix Anderson acceleration, Newton selection, screening, and step adaptation.
|
|
75
|
+
|
|
76
|
+
## 0.4.0 - 2026-06-21
|
|
77
|
+
|
|
78
|
+
- Share solver cost handling and remove low-rank Sinkhorn.
|
|
79
|
+
- Fix cost scaling, dual recentering, and activation semantics.
|
|
80
|
+
|
|
81
|
+
## 0.3.0 - 2026-05-27
|
|
82
|
+
|
|
83
|
+
- Reduce GPU synchronization in solvers and blockwise updates.
|
|
84
|
+
- Fix CMA rotation forwarding and API examples.
|
|
85
|
+
|
|
86
|
+
## 0.2.2 - 2026-05-21
|
|
87
|
+
|
|
88
|
+
- Use unit-radius vertex generators with keyword-only `radius`.
|
|
89
|
+
- Fix CMA transport weighting and stale API examples.
|
|
90
|
+
|
|
91
|
+
## 0.2.1 - 2026-05-14
|
|
92
|
+
|
|
93
|
+
- Add the Loihi-style SNN adaptation example.
|
|
94
|
+
|
|
95
|
+
## 0.2.0 - 2026-05-14
|
|
96
|
+
|
|
97
|
+
- Require Python 3.11, PyTorch 2.8, and NumPy 2.0 for optional extras.
|
|
98
|
+
- Reduce solver synchronization and use inference mode for evaluation.
|
|
99
|
+
|
|
100
|
+
## 0.1.0 - 2026-05-03
|
|
101
|
+
|
|
102
|
+
- Initial release: gradient-free training, OT solvers, subspaces, vmap-compatible
|
|
103
|
+
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.
|
|
10
|
-
date-released: "2026-
|
|
9
|
+
version: 0.13.0
|
|
10
|
+
date-released: "2026-09-22"
|
|
11
11
|
repository-code: "https://github.com/anindex/polystep"
|
|
12
12
|
keywords:
|
|
13
13
|
- gradient-free optimization
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
Install the development dependencies and run the same checks as CI:
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
pip install -e ".[dev]"
|
|
7
|
+
ruff check .
|
|
8
|
+
ruff format --check .
|
|
9
|
+
pytest tests/ -q
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Create a branch, make the change, add regression coverage where needed, and open a
|
|
13
|
+
pull request. Keep comments specific and public APIs typed.
|
|
14
|
+
|
|
15
|
+
## Tests
|
|
16
|
+
|
|
17
|
+
The default suite excludes `slow` and `gpu` tests. To select them:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
pytest tests/ -m slow
|
|
21
|
+
pytest tests/ -m gpu
|
|
22
|
+
pytest tests/ -m ""
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Tests use one CPU thread by default; `POLYSTEP_TEST_THREADS` overrides it. Slow tests
|
|
26
|
+
may download MNIST. Tests importing `experiments/` must skip when that directory is
|
|
27
|
+
absent, since it is excluded from distributions.
|
|
28
|
+
|
|
29
|
+
Pushes and pull requests test Python 3.11 with PyTorch 2.8 and Python 3.14 with
|
|
30
|
+
current PyTorch. Scheduled and manual runs add Python 3.12/3.13, coverage, real-data
|
|
31
|
+
learning checks, and the shipped tests against the installed wheel. Every run
|
|
32
|
+
builds both distributions and checks the wheel's imports and type marker.
|
|
33
|
+
|
|
34
|
+
Before a release:
|
|
35
|
+
|
|
36
|
+
```bash
|
|
37
|
+
uv build
|
|
38
|
+
uvx twine check --strict dist/*
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## Releases
|
|
42
|
+
|
|
43
|
+
Update `polystep.__version__`, the version and date in `CITATION.cff`, and
|
|
44
|
+
`CHANGELOG.md`. `pyproject.toml` reads the package version automatically.
|
|
45
|
+
|
|
46
|
+
Publishing a GitHub Release tagged `vX.Y.Z` triggers the PyPI workflow. It checks
|
|
47
|
+
version consistency, lint, tests, and package metadata before publishing. The
|
|
48
|
+
release also runs the shipped tests against the wheel. Only the separate publish
|
|
49
|
+
job has OIDC permission; it uses the `pypi` environment and PyPI Trusted Publishing.
|
|
50
|
+
|
|
51
|
+
## Issues
|
|
52
|
+
|
|
53
|
+
Include a minimal reproduction, the traceback, Python and PyTorch versions, and
|
|
54
|
+
hardware details relevant to the failure.
|
|
@@ -0,0 +1,92 @@
|
|
|
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 perturbations to the input directions in its fixed
|
|
14
|
+
factor. Rotation changes those directions. Retune radii when switching subspaces.
|
|
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.
|
|
24
|
+
|
|
25
|
+
## Evaluation
|
|
26
|
+
|
|
27
|
+
- `step()` expects `closure(batched_params) -> losses` with shape `(N,)`.
|
|
28
|
+
- Manual loops must register the evaluator and current batch to enable fast paths.
|
|
29
|
+
The evaluator and closure must compute the same objective.
|
|
30
|
+
- Mixed-dtype models require `HybridSubspace` or `FactoredSubspace`. Full-space and
|
|
31
|
+
global-subspace coordinates use one dtype and reject mixed parameter dtypes.
|
|
32
|
+
- `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.
|
|
39
|
+
- 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.
|
|
43
|
+
- The library does not distribute optimizer state across multiple GPUs.
|
|
44
|
+
|
|
45
|
+
## Attention and recurrent layers
|
|
46
|
+
|
|
47
|
+
[`VmapSafeMultiHeadAttention`](src/polystep/layers/attention.py) requires batch-first
|
|
48
|
+
inputs and `kdim == vdim == embed_dim`. It does not support `add_bias_kv`,
|
|
49
|
+
`add_zero_attn`, returned attention weights, or `is_causal=True`; supply an explicit
|
|
50
|
+
causal mask. It returns an output tensor, not an `(output, weights)` tuple.
|
|
51
|
+
|
|
52
|
+
Boolean masks use `True` for masked positions; float masks are additive. Attention
|
|
53
|
+
masks accept `(query, key)`, `(batch * heads, query, key)`, and broadcastable 4D shapes.
|
|
54
|
+
The wrapper also accepts `(batch, query, key)` masks.
|
|
55
|
+
|
|
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.
|
|
59
|
+
|
|
60
|
+
[`VmapSafeLSTM`](src/polystep/layers/rnn.py) requires batch-first dense inputs. It does
|
|
61
|
+
not support bidirectional recurrence, projection, or `PackedSequence`. Its explicit
|
|
62
|
+
cell computations can be slower than fused cuDNN recurrence.
|
|
63
|
+
|
|
64
|
+
## Solvers and mathematical scope
|
|
65
|
+
|
|
66
|
+
`SoftmaxSolver` enforces source row sums only; it ignores the target marginal and
|
|
67
|
+
warns for a nonuniform one. Use `SinkhornSolver` to enforce both marginals.
|
|
68
|
+
Independent particle rotations mean Sinkhorn's shared column index does not identify
|
|
69
|
+
a common direction across particles.
|
|
70
|
+
|
|
71
|
+
Sinkhorn requires `omega` in `[0.5, 1.95]`. Anderson acceleration and adaptive omega
|
|
72
|
+
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'`.
|
|
77
|
+
|
|
78
|
+
Quadratic fits are finite-radius models. Simplex gradients can be biased on
|
|
79
|
+
anisotropic quadratics, and block models omit cross-block curvature. A fit does not
|
|
80
|
+
supply a classical derivative at a discontinuity. The deferred radius controller
|
|
81
|
+
does not perform a trust-region acceptance test.
|
|
82
|
+
|
|
83
|
+
## Benchmark comparisons
|
|
84
|
+
|
|
85
|
+
The architecture tables use validation-selected checkpoints. `--allow-test-leakage`
|
|
86
|
+
is an explicit opt-in for test-selected runs, which standard aggregation rejects.
|
|
87
|
+
|
|
88
|
+
The SNN surrogate-gradient baseline has substantial variation across seeds. MAX-SAT
|
|
89
|
+
search heuristics use flip budgets, which are not equivalent to neural candidate
|
|
90
|
+
budgets. Head-only GPT-2 experiments do not establish full-model fine-tuning results.
|
|
91
|
+
|
|
92
|
+
See the [experiment index](experiments/EXPERIMENT_INDEX.md) for protocols and results.
|
polystep-0.13.0/PKG-INFO
ADDED
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: polystep
|
|
3
|
+
Version: 0.13.0
|
|
4
|
+
Summary: PyTorch PolyStep Optimizer - gradient-free neural network training via entropic optimal transport
|
|
5
|
+
Author: An T. Le
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/anindex/polystep
|
|
8
|
+
Project-URL: Repository, https://github.com/anindex/polystep
|
|
9
|
+
Project-URL: Documentation, https://github.com/anindex/polystep/blob/main/docs/api_overview.md
|
|
10
|
+
Project-URL: Changelog, https://github.com/anindex/polystep/blob/main/CHANGELOG.md
|
|
11
|
+
Project-URL: Issues, https://github.com/anindex/polystep/issues
|
|
12
|
+
Keywords: pytorch,optimal-transport,gradient-free,sinkhorn,polystep,optimization,neural-network
|
|
13
|
+
Classifier: Development Status :: 4 - Beta
|
|
14
|
+
Classifier: Intended Audience :: Science/Research
|
|
15
|
+
Classifier: Intended Audience :: Developers
|
|
16
|
+
Classifier: Operating System :: OS Independent
|
|
17
|
+
Classifier: Programming Language :: Python :: 3
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
22
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
23
|
+
Classifier: Typing :: Typed
|
|
24
|
+
Requires-Python: >=3.11
|
|
25
|
+
Description-Content-Type: text/markdown
|
|
26
|
+
License-File: LICENSE
|
|
27
|
+
Requires-Dist: torch>=2.8
|
|
28
|
+
Provides-Extra: examples
|
|
29
|
+
Requires-Dist: numpy>=2.0; extra == "examples"
|
|
30
|
+
Requires-Dist: matplotlib>=3.8; extra == "examples"
|
|
31
|
+
Requires-Dist: Pillow>=10.0; extra == "examples"
|
|
32
|
+
Requires-Dist: gymnasium[classic-control]>=0.29; extra == "examples"
|
|
33
|
+
Provides-Extra: dev
|
|
34
|
+
Requires-Dist: numpy>=2.0; extra == "dev"
|
|
35
|
+
Requires-Dist: setuptools>=77; extra == "dev"
|
|
36
|
+
Requires-Dist: pytest>=8.0; extra == "dev"
|
|
37
|
+
Requires-Dist: pytest-timeout>=2.2; extra == "dev"
|
|
38
|
+
Requires-Dist: pytest-rerunfailures>=14.0; extra == "dev"
|
|
39
|
+
Requires-Dist: pytest-cov>=5.0; extra == "dev"
|
|
40
|
+
Requires-Dist: pytest-xdist>=3.5; extra == "dev"
|
|
41
|
+
Requires-Dist: ruff==0.15.13; extra == "dev"
|
|
42
|
+
Provides-Extra: experiments
|
|
43
|
+
Requires-Dist: numpy>=2.0; extra == "experiments"
|
|
44
|
+
Requires-Dist: pandas>=2.0; extra == "experiments"
|
|
45
|
+
Requires-Dist: python-sat; extra == "experiments"
|
|
46
|
+
Requires-Dist: torchvision>=0.25; extra == "experiments"
|
|
47
|
+
Requires-Dist: cma>=3.3; extra == "experiments"
|
|
48
|
+
Requires-Dist: snntorch>=0.7; extra == "experiments"
|
|
49
|
+
Requires-Dist: datasets>=2.14; extra == "experiments"
|
|
50
|
+
Requires-Dist: transformers>=4.40; extra == "experiments"
|
|
51
|
+
Requires-Dist: gymnasium>=0.29; extra == "experiments"
|
|
52
|
+
Provides-Extra: rl
|
|
53
|
+
Requires-Dist: gymnasium[box2d]>=0.29; extra == "rl"
|
|
54
|
+
Requires-Dist: stable-baselines3>=2.3; extra == "rl"
|
|
55
|
+
Requires-Dist: swig>=4.2; extra == "rl"
|
|
56
|
+
Dynamic: license-file
|
|
57
|
+
|
|
58
|
+
# polystep
|
|
59
|
+
|
|
60
|
+
[](https://pypi.org/project/polystep/)
|
|
61
|
+
[](https://arxiv.org/abs/2605.01928)
|
|
62
|
+
[](https://www.python.org/downloads/)
|
|
63
|
+
[](https://pytorch.org/)
|
|
64
|
+
[](https://github.com/anindex/polystep/blob/main/LICENSE)
|
|
65
|
+
|
|
66
|
+
Gradient-free neural network training via optimal transport.
|
|
67
|
+
|
|
68
|
+
Accepted at [Transactions on Machine Learning Research (TMLR)](https://openreview.net/forum?id=8mlcqTTMuU).
|
|
69
|
+
|
|
70
|
+
PolyStep evaluates finite-radius parameter perturbations and updates parameters
|
|
71
|
+
through a weighted barycenter. It supports spiking networks, quantized layers, hard
|
|
72
|
+
routing, and other models without useful gradients. For differentiable models,
|
|
73
|
+
backpropagation is usually faster.
|
|
74
|
+
|
|
75
|
+
[Paper](https://arxiv.org/abs/2605.01928) ·
|
|
76
|
+
[Interactive visualization](https://vietngth.github.io/polystep-visualization/) ·
|
|
77
|
+
[API reference](docs/api_overview.md)
|
|
78
|
+
|
|
79
|
+
<p align="center">
|
|
80
|
+
<img src="https://raw.githubusercontent.com/anindex/polystep/main/docs/figures/method_diagram.png" width="840"
|
|
81
|
+
alt="PolyStep: search coordinates, polytope probes, cost evaluation, soft assignment, and a barycentric update.">
|
|
82
|
+
</p>
|
|
83
|
+
|
|
84
|
+
## Installation
|
|
85
|
+
|
|
86
|
+
```bash
|
|
87
|
+
pip install polystep
|
|
88
|
+
pip install "polystep[examples]" # optional demo dependencies
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
Python 3.11+ and PyTorch 2.8+ are required. Choose a CUDA build compatible with your
|
|
92
|
+
driver from the [PyTorch installation guide](https://pytorch.org/get-started/locally/).
|
|
93
|
+
For development, install `pip install -e ".[dev]"` from a checkout.
|
|
94
|
+
|
|
95
|
+
## Quickstart
|
|
96
|
+
|
|
97
|
+
```python
|
|
98
|
+
import torch
|
|
99
|
+
from torch import nn
|
|
100
|
+
from torch.utils.data import DataLoader, TensorDataset
|
|
101
|
+
from polystep import HybridSubspace, PolyStepOptimizer, TrainConfig, train
|
|
102
|
+
from polystep.transform import ParamLayout
|
|
103
|
+
|
|
104
|
+
model = nn.Sequential(nn.Linear(16, 32), nn.ReLU(), nn.Linear(32, 3))
|
|
105
|
+
loader = DataLoader(
|
|
106
|
+
TensorDataset(torch.randn(256, 16), torch.randint(0, 3, (256,))),
|
|
107
|
+
batch_size=32,
|
|
108
|
+
)
|
|
109
|
+
subspace = HybridSubspace.from_layout(
|
|
110
|
+
ParamLayout.from_module(model), rank=4, max_subspace_dim=128,
|
|
111
|
+
)
|
|
112
|
+
optimizer = PolyStepOptimizer(model, subspace=subspace, solver="softmax")
|
|
113
|
+
train(model, loader, nn.CrossEntropyLoss(), optimizer, TrainConfig(epochs=5))
|
|
114
|
+
```
|
|
115
|
+
|
|
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.
|
|
120
|
+
|
|
121
|
+
### Ask/tell
|
|
122
|
+
|
|
123
|
+
```python
|
|
124
|
+
from polystep import PolyStepES
|
|
125
|
+
|
|
126
|
+
es = PolyStepES(dim=20, epsilon=0.1, step_radius=0.3)
|
|
127
|
+
for _ in range(300):
|
|
128
|
+
candidates = es.ask()
|
|
129
|
+
es.tell(candidates.square().sum(dim=1))
|
|
130
|
+
print(es.best_fitness, es.best_solution)
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
### Synthetic objectives
|
|
134
|
+
|
|
135
|
+
```python
|
|
136
|
+
from polystep import Ackley
|
|
137
|
+
from polystep.solver import PolyStep
|
|
138
|
+
|
|
139
|
+
solver = PolyStep.create(Ackley(dim=10), epsilon=0.5, max_iterations=50)
|
|
140
|
+
state = solver.run(torch.randn(100, 10))
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## Method
|
|
144
|
+
|
|
145
|
+
Each step samples a rotated polytope, evaluates its vertices, computes transport
|
|
146
|
+
weights, and moves to their barycenter. Subspaces reduce the number of coordinates
|
|
147
|
+
searched. Softmax treats blocks independently; Sinkhorn also constrains target mass.
|
|
148
|
+
|
|
149
|
+
Finite-radius probes can cross flat regions of discontinuous losses. Performance
|
|
150
|
+
depends on the radius, representation, and evaluation budget. PolyStep's optional
|
|
151
|
+
quadratic models and radius controllers are implementation heuristics; they do not
|
|
152
|
+
inherit classical direct-search or trust-region guarantees.
|
|
153
|
+
|
|
154
|
+
## Baselines
|
|
155
|
+
|
|
156
|
+
`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:
|
|
159
|
+
|
|
160
|
+
```python
|
|
161
|
+
from polystep.baselines import Objective, openai_es
|
|
162
|
+
|
|
163
|
+
objective = Objective(lambda x: x.square().sum(1), dim=64, budget=10_000)
|
|
164
|
+
result = openai_es(objective, sigma=0.05, lr=0.1, popsize=32)
|
|
165
|
+
```
|
|
166
|
+
|
|
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.
|
|
170
|
+
|
|
171
|
+
## Benchmarks
|
|
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).
|
|
176
|
+
|
|
177
|
+
<!-- BENCH:START -->
|
|
178
|
+
### Non-differentiable tasks
|
|
179
|
+
|
|
180
|
+
Test accuracy %. A dash means the result is not in this release.
|
|
181
|
+
|
|
182
|
+
| Task | PolyStep | CMA-ES | OpenAI-ES | SPSA | Adam (surrogate) | Non-diff op |
|
|
183
|
+
|---|---|---|---|---|---|---|
|
|
184
|
+
| SNN/LIF (MNIST) | 93.0 ± 0.2 | 77.1 ± 13.3 | 79.6 ± 5.2 | 53.9 ± 3.0 | 86.3 ± 10.6 | `threshold()` |
|
|
185
|
+
| Int8 quantized | 97.0 ± 0.1 | 91.6 ± 0.4 | 94.4 ± 0.2 | 82.5 ± 0.5 | 97.8 ± 0.1 | `round()` |
|
|
186
|
+
| Argmax attention | 86.6 ± 0.4 | 79.5 ± 0.5 | 80.0 ± 0.4 | 67.4 ± 1.3 | 88.6 ± 0.1 | `argmax()` |
|
|
187
|
+
| Staircase activation | 94.3 ± 0.1 | 89.2 ± 0.4 | 88.9 ± 0.5 | 45.2 ± 4.2 | 97.5 ± 0.1 | `floor()` |
|
|
188
|
+
| Hard MoE routing | 90.6 ± 0.2 | 77.8 ± 1.5 | 82.5 ± 0.8 | 25.4 ± 3.5 | - | `argmax()` |
|
|
189
|
+
|
|
190
|
+
### MAX-SAT (% clauses satisfied)
|
|
191
|
+
|
|
192
|
+
| Variables | PolyStep | CMA-ES | OpenAI-ES | probSAT | RC2 |
|
|
193
|
+
|---|---|---|---|---|---|
|
|
194
|
+
| 100 | 98.0 ± 0.7 | 99.0 ± 0.3 | 99.4 ± 0.1 | 99.8 | 99.8 |
|
|
195
|
+
| 5,000 | 98.1 ± 0.1 | 95.2 ± 0.2 | 92.9 ± 0.2 | 99.9 | timeout |
|
|
196
|
+
| 100,000 | 98.1 ± 0.0 | 90.7 ± 0.1 | 88.9 ± 0.0 | 99.6 | timeout |
|
|
197
|
+
|
|
198
|
+
### Differentiable sanity checks
|
|
199
|
+
|
|
200
|
+
| Task | PolyStep | Adam |
|
|
201
|
+
|---|---|---|
|
|
202
|
+
| MNIST (2-layer MLP) | 96.8 ± 0.1 | 97.7 ± 0.1 |
|
|
203
|
+
| ETTh1 (LSTM, MSE; lower is better) | 0.253 ± 0.023 | 0.247 ± 0.023 |
|
|
204
|
+
<!-- BENCH:END -->
|
|
205
|
+
|
|
206
|
+
The SNN surrogate-gradient baseline varies substantially across seeds. Adam leads
|
|
207
|
+
on the differentiable tasks, and specialized solvers lead on MAX-SAT. See
|
|
208
|
+
[limitations](LIMITATIONS.md) for unsupported configurations and comparison limits.
|
|
209
|
+
|
|
210
|
+
## Documentation
|
|
211
|
+
|
|
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)
|
|
218
|
+
|
|
219
|
+
## Citation
|
|
220
|
+
|
|
221
|
+
```bibtex
|
|
222
|
+
@article{le2026training,
|
|
223
|
+
title={Training Non-Differentiable Networks via Optimal Transport},
|
|
224
|
+
author={Le, An T},
|
|
225
|
+
journal={arXiv preprint arXiv:2605.01928},
|
|
226
|
+
year={2026}
|
|
227
|
+
}
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
## Acknowledgments
|
|
231
|
+
|
|
232
|
+
Thanks to [Viet T. Nguyen](https://vietngth.github.io/) for the
|
|
233
|
+
[interactive visualization](https://vietngth.github.io/polystep-visualization/).
|
|
234
|
+
|
|
235
|
+
## License
|
|
236
|
+
|
|
237
|
+
[Apache-2.0](LICENSE).
|
|
@@ -0,0 +1,180 @@
|
|
|
1
|
+
# polystep
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/polystep/)
|
|
4
|
+
[](https://arxiv.org/abs/2605.01928)
|
|
5
|
+
[](https://www.python.org/downloads/)
|
|
6
|
+
[](https://pytorch.org/)
|
|
7
|
+
[](https://github.com/anindex/polystep/blob/main/LICENSE)
|
|
8
|
+
|
|
9
|
+
Gradient-free neural network training via optimal transport.
|
|
10
|
+
|
|
11
|
+
Accepted at [Transactions on Machine Learning Research (TMLR)](https://openreview.net/forum?id=8mlcqTTMuU).
|
|
12
|
+
|
|
13
|
+
PolyStep evaluates finite-radius parameter perturbations and updates parameters
|
|
14
|
+
through a weighted barycenter. It supports spiking networks, quantized layers, hard
|
|
15
|
+
routing, and other models without useful gradients. For differentiable models,
|
|
16
|
+
backpropagation is usually faster.
|
|
17
|
+
|
|
18
|
+
[Paper](https://arxiv.org/abs/2605.01928) ·
|
|
19
|
+
[Interactive visualization](https://vietngth.github.io/polystep-visualization/) ·
|
|
20
|
+
[API reference](docs/api_overview.md)
|
|
21
|
+
|
|
22
|
+
<p align="center">
|
|
23
|
+
<img src="https://raw.githubusercontent.com/anindex/polystep/main/docs/figures/method_diagram.png" width="840"
|
|
24
|
+
alt="PolyStep: search coordinates, polytope probes, cost evaluation, soft assignment, and a barycentric update.">
|
|
25
|
+
</p>
|
|
26
|
+
|
|
27
|
+
## Installation
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
pip install polystep
|
|
31
|
+
pip install "polystep[examples]" # optional demo dependencies
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Python 3.11+ and PyTorch 2.8+ are required. Choose a CUDA build compatible with your
|
|
35
|
+
driver from the [PyTorch installation guide](https://pytorch.org/get-started/locally/).
|
|
36
|
+
For development, install `pip install -e ".[dev]"` from a checkout.
|
|
37
|
+
|
|
38
|
+
## Quickstart
|
|
39
|
+
|
|
40
|
+
```python
|
|
41
|
+
import torch
|
|
42
|
+
from torch import nn
|
|
43
|
+
from torch.utils.data import DataLoader, TensorDataset
|
|
44
|
+
from polystep import HybridSubspace, PolyStepOptimizer, TrainConfig, train
|
|
45
|
+
from polystep.transform import ParamLayout
|
|
46
|
+
|
|
47
|
+
model = nn.Sequential(nn.Linear(16, 32), nn.ReLU(), nn.Linear(32, 3))
|
|
48
|
+
loader = DataLoader(
|
|
49
|
+
TensorDataset(torch.randn(256, 16), torch.randint(0, 3, (256,))),
|
|
50
|
+
batch_size=32,
|
|
51
|
+
)
|
|
52
|
+
subspace = HybridSubspace.from_layout(
|
|
53
|
+
ParamLayout.from_module(model), rank=4, max_subspace_dim=128,
|
|
54
|
+
)
|
|
55
|
+
optimizer = PolyStepOptimizer(model, subspace=subspace, solver="softmax")
|
|
56
|
+
train(model, loader, nn.CrossEntropyLoss(), optimizer, TrainConfig(epochs=5))
|
|
57
|
+
```
|
|
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.
|
|
63
|
+
|
|
64
|
+
### Ask/tell
|
|
65
|
+
|
|
66
|
+
```python
|
|
67
|
+
from polystep import PolyStepES
|
|
68
|
+
|
|
69
|
+
es = PolyStepES(dim=20, epsilon=0.1, step_radius=0.3)
|
|
70
|
+
for _ in range(300):
|
|
71
|
+
candidates = es.ask()
|
|
72
|
+
es.tell(candidates.square().sum(dim=1))
|
|
73
|
+
print(es.best_fitness, es.best_solution)
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
### Synthetic objectives
|
|
77
|
+
|
|
78
|
+
```python
|
|
79
|
+
from polystep import Ackley
|
|
80
|
+
from polystep.solver import PolyStep
|
|
81
|
+
|
|
82
|
+
solver = PolyStep.create(Ackley(dim=10), epsilon=0.5, max_iterations=50)
|
|
83
|
+
state = solver.run(torch.randn(100, 10))
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
## Method
|
|
87
|
+
|
|
88
|
+
Each step samples a rotated polytope, evaluates its vertices, computes transport
|
|
89
|
+
weights, and moves to their barycenter. Subspaces reduce the number of coordinates
|
|
90
|
+
searched. Softmax treats blocks independently; Sinkhorn also constrains target mass.
|
|
91
|
+
|
|
92
|
+
Finite-radius probes can cross flat regions of discontinuous losses. Performance
|
|
93
|
+
depends on the radius, representation, and evaluation budget. PolyStep's optional
|
|
94
|
+
quadratic models and radius controllers are implementation heuristics; they do not
|
|
95
|
+
inherit classical direct-search or trust-region guarantees.
|
|
96
|
+
|
|
97
|
+
## Baselines
|
|
98
|
+
|
|
99
|
+
`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:
|
|
102
|
+
|
|
103
|
+
```python
|
|
104
|
+
from polystep.baselines import Objective, openai_es
|
|
105
|
+
|
|
106
|
+
objective = Objective(lambda x: x.square().sum(1), dim=64, budget=10_000)
|
|
107
|
+
result = openai_es(objective, sigma=0.05, lr=0.1, popsize=32)
|
|
108
|
+
```
|
|
109
|
+
|
|
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
|
+
|
|
114
|
+
## Benchmarks
|
|
115
|
+
|
|
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).
|
|
119
|
+
|
|
120
|
+
<!-- BENCH:START -->
|
|
121
|
+
### Non-differentiable tasks
|
|
122
|
+
|
|
123
|
+
Test accuracy %. A dash means the result is not in this release.
|
|
124
|
+
|
|
125
|
+
| Task | PolyStep | CMA-ES | OpenAI-ES | SPSA | Adam (surrogate) | Non-diff op |
|
|
126
|
+
|---|---|---|---|---|---|---|
|
|
127
|
+
| SNN/LIF (MNIST) | 93.0 ± 0.2 | 77.1 ± 13.3 | 79.6 ± 5.2 | 53.9 ± 3.0 | 86.3 ± 10.6 | `threshold()` |
|
|
128
|
+
| Int8 quantized | 97.0 ± 0.1 | 91.6 ± 0.4 | 94.4 ± 0.2 | 82.5 ± 0.5 | 97.8 ± 0.1 | `round()` |
|
|
129
|
+
| Argmax attention | 86.6 ± 0.4 | 79.5 ± 0.5 | 80.0 ± 0.4 | 67.4 ± 1.3 | 88.6 ± 0.1 | `argmax()` |
|
|
130
|
+
| Staircase activation | 94.3 ± 0.1 | 89.2 ± 0.4 | 88.9 ± 0.5 | 45.2 ± 4.2 | 97.5 ± 0.1 | `floor()` |
|
|
131
|
+
| Hard MoE routing | 90.6 ± 0.2 | 77.8 ± 1.5 | 82.5 ± 0.8 | 25.4 ± 3.5 | - | `argmax()` |
|
|
132
|
+
|
|
133
|
+
### MAX-SAT (% clauses satisfied)
|
|
134
|
+
|
|
135
|
+
| Variables | PolyStep | CMA-ES | OpenAI-ES | probSAT | RC2 |
|
|
136
|
+
|---|---|---|---|---|---|
|
|
137
|
+
| 100 | 98.0 ± 0.7 | 99.0 ± 0.3 | 99.4 ± 0.1 | 99.8 | 99.8 |
|
|
138
|
+
| 5,000 | 98.1 ± 0.1 | 95.2 ± 0.2 | 92.9 ± 0.2 | 99.9 | timeout |
|
|
139
|
+
| 100,000 | 98.1 ± 0.0 | 90.7 ± 0.1 | 88.9 ± 0.0 | 99.6 | timeout |
|
|
140
|
+
|
|
141
|
+
### Differentiable sanity checks
|
|
142
|
+
|
|
143
|
+
| Task | PolyStep | Adam |
|
|
144
|
+
|---|---|---|
|
|
145
|
+
| MNIST (2-layer MLP) | 96.8 ± 0.1 | 97.7 ± 0.1 |
|
|
146
|
+
| ETTh1 (LSTM, MSE; lower is better) | 0.253 ± 0.023 | 0.247 ± 0.023 |
|
|
147
|
+
<!-- BENCH:END -->
|
|
148
|
+
|
|
149
|
+
The SNN surrogate-gradient baseline varies substantially across seeds. Adam leads
|
|
150
|
+
on the differentiable tasks, and specialized solvers lead on MAX-SAT. See
|
|
151
|
+
[limitations](LIMITATIONS.md) for unsupported configurations and comparison limits.
|
|
152
|
+
|
|
153
|
+
## Documentation
|
|
154
|
+
|
|
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)
|
|
161
|
+
|
|
162
|
+
## Citation
|
|
163
|
+
|
|
164
|
+
```bibtex
|
|
165
|
+
@article{le2026training,
|
|
166
|
+
title={Training Non-Differentiable Networks via Optimal Transport},
|
|
167
|
+
author={Le, An T},
|
|
168
|
+
journal={arXiv preprint arXiv:2605.01928},
|
|
169
|
+
year={2026}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Acknowledgments
|
|
174
|
+
|
|
175
|
+
Thanks to [Viet T. Nguyen](https://vietngth.github.io/) for the
|
|
176
|
+
[interactive visualization](https://vietngth.github.io/polystep-visualization/).
|
|
177
|
+
|
|
178
|
+
## License
|
|
179
|
+
|
|
180
|
+
[Apache-2.0](LICENSE).
|