polystep 0.7.0__py3-none-any.whl
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/__init__.py +176 -0
- polystep/_compiled.py +334 -0
- polystep/_step_blockwise.py +994 -0
- polystep/_step_momentum.py +128 -0
- polystep/_step_monolithic.py +1070 -0
- polystep/adaptive_subspace.py +762 -0
- polystep/api.py +307 -0
- polystep/ask_tell.py +164 -0
- polystep/benchmarks/__init__.py +62 -0
- polystep/benchmarks/baselines.py +604 -0
- polystep/benchmarks/rl/__init__.py +13 -0
- polystep/benchmarks/rl/cartpole.py +260 -0
- polystep/benchmarks/rl/gym_evaluator.py +304 -0
- polystep/benchmarks/rl/metrics.py +53 -0
- polystep/benchmarks/rl/policies.py +133 -0
- polystep/benchmarks/utils.py +1262 -0
- polystep/blockwise.py +549 -0
- polystep/cma.py +400 -0
- polystep/cma_subspace.py +404 -0
- polystep/cost_nn.py +633 -0
- polystep/costs.py +86 -0
- polystep/dynamics.py +138 -0
- polystep/epsilon.py +217 -0
- polystep/geometry.py +410 -0
- polystep/hybrid_subspace.py +1335 -0
- polystep/layers/__init__.py +16 -0
- polystep/layers/attention.py +243 -0
- polystep/layers/rnn.py +276 -0
- polystep/objectives/__init__.py +25 -0
- polystep/objectives/base.py +77 -0
- polystep/objectives/synthetic.py +232 -0
- polystep/optimizer.py +1405 -0
- polystep/projection/__init__.py +5 -0
- polystep/projection/sparse.py +291 -0
- polystep/py.typed +0 -0
- polystep/quadratic_model.py +285 -0
- polystep/solver.py +654 -0
- polystep/solvers/__init__.py +34 -0
- polystep/solvers/_prelude.py +129 -0
- polystep/solvers/base.py +77 -0
- polystep/solvers/greedy.py +159 -0
- polystep/solvers/kl_softmax.py +200 -0
- polystep/solvers/sinkhorn.py +522 -0
- polystep/solvers/softmax.py +151 -0
- polystep/solvers/tempered_softmax.py +97 -0
- polystep/subspace.py +694 -0
- polystep/transform.py +365 -0
- polystep-0.7.0.dist-info/METADATA +285 -0
- polystep-0.7.0.dist-info/RECORD +52 -0
- polystep-0.7.0.dist-info/WHEEL +5 -0
- polystep-0.7.0.dist-info/licenses/LICENSE +191 -0
- polystep-0.7.0.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"""TemperedSoftmaxSolver: softmax weighting with a fixed temperature.
|
|
2
|
+
|
|
3
|
+
Like ``SoftmaxSolver`` but uses a separate temperature parameter ``tau``
|
|
4
|
+
instead of the optimizer's epsilon schedule. This decouples the softmax
|
|
5
|
+
sharpness from the entropic regularization so the ablation can sweep
|
|
6
|
+
tau independently.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass
|
|
10
|
+
from typing import Optional, Union
|
|
11
|
+
|
|
12
|
+
import torch
|
|
13
|
+
|
|
14
|
+
from ..costs import scale_cost_matrix
|
|
15
|
+
from ._prelude import align_marginal, recenter_cost, sanitize_cost, validate_positive
|
|
16
|
+
from .base import SolverResult
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
@dataclass
|
|
20
|
+
class TemperedSoftmaxSolver:
|
|
21
|
+
"""Softmax solver with a fixed temperature independent of epsilon.
|
|
22
|
+
|
|
23
|
+
Computes transport weights via ``softmax(-C / tau)`` where ``tau`` is
|
|
24
|
+
set once at construction and NOT overridden by the optimizer's per-step
|
|
25
|
+
epsilon. The ``epsilon`` attribute is accepted for API compatibility
|
|
26
|
+
but ignored in ``solve()``.
|
|
27
|
+
|
|
28
|
+
Attributes:
|
|
29
|
+
epsilon: Accepted for API compatibility; overridden per-step by the
|
|
30
|
+
optimizer but NOT used in the softmax computation.
|
|
31
|
+
tau: Fixed temperature for the softmax. Lower tau = sharper weights.
|
|
32
|
+
compile: Accepted for API compatibility; unused.
|
|
33
|
+
"""
|
|
34
|
+
|
|
35
|
+
epsilon: float = 0.1
|
|
36
|
+
tau: float = 1.0
|
|
37
|
+
compile: bool = False
|
|
38
|
+
|
|
39
|
+
def solve(
|
|
40
|
+
self,
|
|
41
|
+
cost_matrix: torch.Tensor,
|
|
42
|
+
a: Optional[torch.Tensor] = None,
|
|
43
|
+
b: Optional[torch.Tensor] = None,
|
|
44
|
+
init_f: Optional[torch.Tensor] = None,
|
|
45
|
+
init_g: Optional[torch.Tensor] = None,
|
|
46
|
+
scale_cost: Optional[Union[str, float]] = None,
|
|
47
|
+
) -> SolverResult:
|
|
48
|
+
"""Compute softmax weights using fixed temperature tau.
|
|
49
|
+
|
|
50
|
+
Args:
|
|
51
|
+
cost_matrix: Cost matrix C of shape (P, V).
|
|
52
|
+
a: Source marginal of shape (P,). Defaults to uniform 1/P.
|
|
53
|
+
b: Target marginal (accepted but ignored).
|
|
54
|
+
init_f: Warm-start dual potential (accepted but ignored).
|
|
55
|
+
init_g: Warm-start dual potential (accepted but ignored).
|
|
56
|
+
scale_cost: Cost scaling: 'mean', 'max_cost', or float divisor.
|
|
57
|
+
|
|
58
|
+
Returns:
|
|
59
|
+
SolverResult with transport matrix, cost, and metadata.
|
|
60
|
+
|
|
61
|
+
Raises:
|
|
62
|
+
ValueError: If tau <= 0.
|
|
63
|
+
"""
|
|
64
|
+
validate_positive(
|
|
65
|
+
self.tau,
|
|
66
|
+
"tau",
|
|
67
|
+
"tau is the temperature in softmax(-C/tau).",
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
P, V = cost_matrix.shape
|
|
71
|
+
cost_matrix = sanitize_cost(cost_matrix) # FP32 promote + finite costs
|
|
72
|
+
device, dtype = cost_matrix.device, cost_matrix.dtype
|
|
73
|
+
a = align_marginal(a, P, device, dtype)
|
|
74
|
+
|
|
75
|
+
C = scale_cost_matrix(cost_matrix, scale_cost)
|
|
76
|
+
# softmax is shift-invariant; recentering keeps min(C)=0 so -C/tau cannot
|
|
77
|
+
# form a +inf logit at tiny tau.
|
|
78
|
+
C, cost_shift = recenter_cost(C)
|
|
79
|
+
|
|
80
|
+
# Use fixed tau (NOT self.epsilon) for the softmax. Pin inside an
|
|
81
|
+
# autocast-disabled FP32 region so an outer mixed-precision context
|
|
82
|
+
# can't downcast -C/tau before softmax subtracts the row max.
|
|
83
|
+
with torch.amp.autocast("cuda", enabled=False), torch.amp.autocast("cpu", enabled=False):
|
|
84
|
+
W = torch.softmax(-C / self.tau, dim=-1)
|
|
85
|
+
transport = W * a.unsqueeze(-1)
|
|
86
|
+
# Undo the recenter shift so the reported cost is <C_scaled, transport>.
|
|
87
|
+
ent_cost = ((C * transport).sum() + cost_shift * a.sum()).item()
|
|
88
|
+
|
|
89
|
+
return SolverResult(
|
|
90
|
+
matrix=transport,
|
|
91
|
+
cost=ent_cost,
|
|
92
|
+
f=None,
|
|
93
|
+
g=None,
|
|
94
|
+
converged=True,
|
|
95
|
+
n_iters=1,
|
|
96
|
+
ent_reg_cost=ent_cost,
|
|
97
|
+
)
|