python-somax 1.0.1__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.
- python_somax-1.1.0/PKG-INFO +182 -0
- python_somax-1.1.0/README.md +138 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/pyproject.toml +6 -5
- python_somax-1.1.0/src/python_somax.egg-info/PKG-INFO +182 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/python_somax.egg-info/SOURCES.txt +6 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/python_somax.egg-info/requires.txt +6 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/__init__.py +6 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/assembler.py +17 -7
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/curvature/__init__.py +2 -0
- python_somax-1.1.0/src/somax/curvature/base.py +140 -0
- python_somax-1.1.0/src/somax/curvature/fgnha_ce.py +338 -0
- python_somax-1.1.0/src/somax/curvature/ggn_ce.py +281 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/curvature/ggn_mse.py +57 -16
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/curvature/hessian.py +24 -4
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/curvature/with_estimators.py +29 -4
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/damping/__init__.py +4 -1
- python_somax-1.1.0/src/somax/damping/scheduled.py +63 -0
- python_somax-1.1.0/src/somax/damping/trust_region.py +162 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/estimators/__init__.py +6 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/estimators/base.py +4 -2
- python_somax-1.1.0/src/somax/estimators/diagpp.py +251 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/estimators/gnb_ce.py +8 -7
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/estimators/hutchinson.py +8 -16
- python_somax-1.1.0/src/somax/estimators/lanczos.py +94 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/executor.py +239 -145
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/__init__.py +6 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/adahessian.py +0 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/direct_methods.py +55 -8
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/egn.py +118 -24
- python_somax-1.1.0/src/somax/methods/fgn.py +168 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/newton_cg.py +7 -7
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/sgn.py +21 -17
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/sophia_g.py +5 -4
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/methods/sophia_h.py +0 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/metrics.py +11 -7
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/optax.py +3 -3
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/planner.py +47 -52
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/preconditioners/__init__.py +1 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/preconditioners/base.py +1 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/preconditioners/diag_ema.py +6 -4
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/solvers/__init__.py +2 -2
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/solvers/base.py +1 -1
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/solvers/cg.py +100 -52
- python_somax-1.1.0/src/somax/solvers/direct.py +204 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/solvers/row_cg.py +14 -38
- python_somax-1.1.0/src/somax/solvers/row_cg_lifted.py +183 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/solvers/row_common.py +9 -6
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/specs.py +60 -26
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/types.py +21 -2
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/utils.py +3 -10
- python_somax-1.0.1/PKG-INFO +0 -202
- python_somax-1.0.1/README.md +0 -162
- python_somax-1.0.1/src/python_somax.egg-info/PKG-INFO +0 -202
- python_somax-1.0.1/src/somax/curvature/base.py +0 -56
- python_somax-1.0.1/src/somax/curvature/ggn_ce.py +0 -183
- python_somax-1.0.1/src/somax/damping/trust_region.py +0 -115
- python_somax-1.0.1/src/somax/solvers/direct.py +0 -55
- python_somax-1.0.1/src/somax/solvers/row_cholesky.py +0 -78
- {python_somax-1.0.1 → python_somax-1.1.0}/LICENSE +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/setup.cfg +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/python_somax.egg-info/dependency_links.txt +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/python_somax.egg-info/top_level.txt +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/damping/base.py +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/damping/constant.py +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/preconditioners/diag_direct.py +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/preconditioners/identity.py +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/presets.py +0 -0
- {python_somax-1.0.1 → python_somax-1.1.0}/src/somax/solvers/identity.py +0 -0
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-somax
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Composable Second-Order Optimization for JAX and Optax.
|
|
5
|
+
Author: Nick Korbit
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/cor3bit/somax
|
|
8
|
+
Project-URL: Repository, https://github.com/cor3bit/somax
|
|
9
|
+
Project-URL: Issues, https://github.com/cor3bit/somax/issues
|
|
10
|
+
Project-URL: Releases, https://github.com/cor3bit/somax/releases
|
|
11
|
+
Project-URL: Documentation, https://github.com/cor3bit/somax/tree/main/docs
|
|
12
|
+
Project-URL: Paper, https://arxiv.org/abs/2603.25976
|
|
13
|
+
Keywords: jax,optax,optimization,second-order,gauss-newton,hessian,machine-learning
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: jax>=0.4.20
|
|
25
|
+
Requires-Dist: optax>=0.1.7
|
|
26
|
+
Requires-Dist: flax>=0.7.5
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
29
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
33
|
+
Requires-Dist: pyright>=1.1; extra == "dev"
|
|
34
|
+
Requires-Dist: pre-commit>=3; extra == "dev"
|
|
35
|
+
Requires-Dist: chex>=0.1.8; extra == "dev"
|
|
36
|
+
Provides-Extra: docs
|
|
37
|
+
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
38
|
+
Requires-Dist: mkdocs-material>=9; extra == "docs"
|
|
39
|
+
Provides-Extra: matfree
|
|
40
|
+
Requires-Dist: matfree; extra == "matfree"
|
|
41
|
+
Provides-Extra: lineax
|
|
42
|
+
Requires-Dist: lineax; extra == "lineax"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
<h1 align="center">Somax</h1>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<img src="assets/somax_logo_mini.png" alt="Somax logo" width="250" />
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<strong>Second-Order Methods in JAX</strong>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Somax is a JAX-native library for composable second-order optimization.
|
|
58
|
+
It combines curvature operators, solvers, damping, preconditioners,
|
|
59
|
+
and update transforms into flexible optimization methods.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Features:
|
|
63
|
+
- **Composable:** combine curvature, solvers, damping, preconditioners, and update rules.
|
|
64
|
+
- **Matrix-free:** compute curvature products without forming dense matrices.
|
|
65
|
+
- **JAX-first:** JIT-compiled steps with explicit control over execution.
|
|
66
|
+
- **Optax integration:** apply standard Optax transformations to computed directions.
|
|
67
|
+
- **Research-friendly:** swap components, run ablations, and prototype new methods.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
> The catfish in the logo is a small nod to *som*, the Belarusian word for catfish.
|
|
73
|
+
> A quiet bottom-dweller, but not a first-order creature.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Installation
|
|
81
|
+
|
|
82
|
+
Install [JAX](https://docs.jax.dev/en/latest/installation.html) for your hardware, then:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install python-somax
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For development:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/cor3bit/somax.git
|
|
92
|
+
cd somax
|
|
93
|
+
pip install -e ".[dev]"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The `lineax` package is optional and needed only for CG with `backend="lineax"`.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Quickstart
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
A minimal stochastic Gauss-Newton training loop for multiclass classification:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import jax
|
|
110
|
+
import jax.numpy as jnp
|
|
111
|
+
import somax
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def predict_fn(params, x):
|
|
115
|
+
h = jnp.tanh(x @ params["W1"] + params["b1"])
|
|
116
|
+
return h @ params["W2"] + params["b2"]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
rng = jax.random.PRNGKey(0)
|
|
120
|
+
k1, k2, k3, k4 = jax.random.split(rng, 4)
|
|
121
|
+
|
|
122
|
+
params = {
|
|
123
|
+
"W1": 0.1 * jax.random.normal(k1, (16, 32)),
|
|
124
|
+
"b1": jnp.zeros((32,)),
|
|
125
|
+
"W2": 0.1 * jax.random.normal(k2, (32, 10)),
|
|
126
|
+
"b2": jnp.zeros((10,)),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
batch = {
|
|
130
|
+
"x": jax.random.normal(k3, (64, 16)),
|
|
131
|
+
"y": jax.random.randint(k4, (64,), 0, 10),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
method = somax.sgn_ce(
|
|
135
|
+
predict_fn=predict_fn,
|
|
136
|
+
learning_rate=0.1,
|
|
137
|
+
lam0=1.0,
|
|
138
|
+
maxiter=10,
|
|
139
|
+
tol=1e-4,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
state = method.init(params)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@jax.jit
|
|
146
|
+
def train_step(params, batch, state, rng):
|
|
147
|
+
return method.step(params, batch, state, rng)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
for step in range(10):
|
|
151
|
+
step_rng = jax.random.fold_in(rng, step)
|
|
152
|
+
params, state, info = train_step(params, batch, state, step_rng)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
For more examples, see [Usage: A paper in a few lines](docs/USAGE.md).
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
## Citation
|
|
161
|
+
|
|
162
|
+
Mikalai Korbit and Mario Zanon,
|
|
163
|
+
"Second-Order, First-Class: A Composable Stack for Curvature-Aware Training",
|
|
164
|
+
ECML PKDD 2026, [arXiv](https://arxiv.org/abs/2603.25976).
|
|
165
|
+
|
|
166
|
+
```bibtex
|
|
167
|
+
@inproceedings{korbit2026second,
|
|
168
|
+
title={Second-Order, First-Class: A Composable Stack for Curvature-Aware Training},
|
|
169
|
+
author={Korbit, Mikalai and Zanon, Mario},
|
|
170
|
+
booktitle={Joint European Conference on Machine Learning and Knowledge Discovery in Databases},
|
|
171
|
+
pages={90--105},
|
|
172
|
+
year={2026},
|
|
173
|
+
organization={Springer}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
Apache-2.0
|
|
182
|
+
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
<h1 align="center">Somax</h1>
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/somax_logo_mini.png" alt="Somax logo" width="250" />
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
<strong>Second-Order Methods in JAX</strong>
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
Somax is a JAX-native library for composable second-order optimization.
|
|
14
|
+
It combines curvature operators, solvers, damping, preconditioners,
|
|
15
|
+
and update transforms into flexible optimization methods.
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
Features:
|
|
19
|
+
- **Composable:** combine curvature, solvers, damping, preconditioners, and update rules.
|
|
20
|
+
- **Matrix-free:** compute curvature products without forming dense matrices.
|
|
21
|
+
- **JAX-first:** JIT-compiled steps with explicit control over execution.
|
|
22
|
+
- **Optax integration:** apply standard Optax transformations to computed directions.
|
|
23
|
+
- **Research-friendly:** swap components, run ablations, and prototype new methods.
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
> The catfish in the logo is a small nod to *som*, the Belarusian word for catfish.
|
|
29
|
+
> A quiet bottom-dweller, but not a first-order creature.
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
## Installation
|
|
37
|
+
|
|
38
|
+
Install [JAX](https://docs.jax.dev/en/latest/installation.html) for your hardware, then:
|
|
39
|
+
|
|
40
|
+
```bash
|
|
41
|
+
pip install python-somax
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
For development:
|
|
45
|
+
|
|
46
|
+
```bash
|
|
47
|
+
git clone https://github.com/cor3bit/somax.git
|
|
48
|
+
cd somax
|
|
49
|
+
pip install -e ".[dev]"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
The `lineax` package is optional and needed only for CG with `backend="lineax"`.
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
## Quickstart
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
A minimal stochastic Gauss-Newton training loop for multiclass classification:
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
import jax
|
|
66
|
+
import jax.numpy as jnp
|
|
67
|
+
import somax
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def predict_fn(params, x):
|
|
71
|
+
h = jnp.tanh(x @ params["W1"] + params["b1"])
|
|
72
|
+
return h @ params["W2"] + params["b2"]
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
rng = jax.random.PRNGKey(0)
|
|
76
|
+
k1, k2, k3, k4 = jax.random.split(rng, 4)
|
|
77
|
+
|
|
78
|
+
params = {
|
|
79
|
+
"W1": 0.1 * jax.random.normal(k1, (16, 32)),
|
|
80
|
+
"b1": jnp.zeros((32,)),
|
|
81
|
+
"W2": 0.1 * jax.random.normal(k2, (32, 10)),
|
|
82
|
+
"b2": jnp.zeros((10,)),
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
batch = {
|
|
86
|
+
"x": jax.random.normal(k3, (64, 16)),
|
|
87
|
+
"y": jax.random.randint(k4, (64,), 0, 10),
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
method = somax.sgn_ce(
|
|
91
|
+
predict_fn=predict_fn,
|
|
92
|
+
learning_rate=0.1,
|
|
93
|
+
lam0=1.0,
|
|
94
|
+
maxiter=10,
|
|
95
|
+
tol=1e-4,
|
|
96
|
+
)
|
|
97
|
+
|
|
98
|
+
state = method.init(params)
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
@jax.jit
|
|
102
|
+
def train_step(params, batch, state, rng):
|
|
103
|
+
return method.step(params, batch, state, rng)
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
for step in range(10):
|
|
107
|
+
step_rng = jax.random.fold_in(rng, step)
|
|
108
|
+
params, state, info = train_step(params, batch, state, step_rng)
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
For more examples, see [Usage: A paper in a few lines](docs/USAGE.md).
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
|
|
115
|
+
|
|
116
|
+
## Citation
|
|
117
|
+
|
|
118
|
+
Mikalai Korbit and Mario Zanon,
|
|
119
|
+
"Second-Order, First-Class: A Composable Stack for Curvature-Aware Training",
|
|
120
|
+
ECML PKDD 2026, [arXiv](https://arxiv.org/abs/2603.25976).
|
|
121
|
+
|
|
122
|
+
```bibtex
|
|
123
|
+
@inproceedings{korbit2026second,
|
|
124
|
+
title={Second-Order, First-Class: A Composable Stack for Curvature-Aware Training},
|
|
125
|
+
author={Korbit, Mikalai and Zanon, Mario},
|
|
126
|
+
booktitle={Joint European Conference on Machine Learning and Knowledge Discovery in Databases},
|
|
127
|
+
pages={90--105},
|
|
128
|
+
year={2026},
|
|
129
|
+
organization={Springer}
|
|
130
|
+
}
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
## License
|
|
136
|
+
|
|
137
|
+
Apache-2.0
|
|
138
|
+
|
|
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "python-somax"
|
|
7
|
-
version = "1.0
|
|
7
|
+
version = "1.1.0"
|
|
8
8
|
description = "Composable Second-Order Optimization for JAX and Optax."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.11"
|
|
@@ -17,7 +17,6 @@ dependencies = [
|
|
|
17
17
|
"jax>=0.4.20",
|
|
18
18
|
"optax>=0.1.7",
|
|
19
19
|
"flax>=0.7.5",
|
|
20
|
-
"numpy>=1.23",
|
|
21
20
|
]
|
|
22
21
|
keywords = [
|
|
23
22
|
"jax",
|
|
@@ -29,7 +28,7 @@ keywords = [
|
|
|
29
28
|
"machine-learning",
|
|
30
29
|
]
|
|
31
30
|
classifiers = [
|
|
32
|
-
"Development Status ::
|
|
31
|
+
"Development Status :: 4 - Beta",
|
|
33
32
|
"Intended Audience :: Science/Research",
|
|
34
33
|
"Programming Language :: Python :: 3",
|
|
35
34
|
"Programming Language :: Python :: 3.11",
|
|
@@ -43,7 +42,7 @@ Homepage = "https://github.com/cor3bit/somax"
|
|
|
43
42
|
Repository = "https://github.com/cor3bit/somax"
|
|
44
43
|
Issues = "https://github.com/cor3bit/somax/issues"
|
|
45
44
|
Releases = "https://github.com/cor3bit/somax/releases"
|
|
46
|
-
|
|
45
|
+
Documentation = "https://github.com/cor3bit/somax/tree/main/docs"
|
|
47
46
|
Paper = "https://arxiv.org/abs/2603.25976"
|
|
48
47
|
|
|
49
48
|
[project.optional-dependencies]
|
|
@@ -61,6 +60,8 @@ docs = [
|
|
|
61
60
|
"mkdocs>=1.5",
|
|
62
61
|
"mkdocs-material>=9",
|
|
63
62
|
]
|
|
63
|
+
matfree = ["matfree"]
|
|
64
|
+
lineax = ["lineax"]
|
|
64
65
|
|
|
65
66
|
[tool.setuptools.packages.find]
|
|
66
67
|
where = ["src"]
|
|
@@ -90,4 +91,4 @@ typeCheckingMode = "basic"
|
|
|
90
91
|
[tool.pytest.ini_options]
|
|
91
92
|
testpaths = ["tests"]
|
|
92
93
|
addopts = "-q --strict-markers"
|
|
93
|
-
xfail_strict = true
|
|
94
|
+
xfail_strict = true
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: python-somax
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Composable Second-Order Optimization for JAX and Optax.
|
|
5
|
+
Author: Nick Korbit
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/cor3bit/somax
|
|
8
|
+
Project-URL: Repository, https://github.com/cor3bit/somax
|
|
9
|
+
Project-URL: Issues, https://github.com/cor3bit/somax/issues
|
|
10
|
+
Project-URL: Releases, https://github.com/cor3bit/somax/releases
|
|
11
|
+
Project-URL: Documentation, https://github.com/cor3bit/somax/tree/main/docs
|
|
12
|
+
Project-URL: Paper, https://arxiv.org/abs/2603.25976
|
|
13
|
+
Keywords: jax,optax,optimization,second-order,gauss-newton,hessian,machine-learning
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Intended Audience :: Science/Research
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Mathematics
|
|
21
|
+
Requires-Python: >=3.11
|
|
22
|
+
Description-Content-Type: text/markdown
|
|
23
|
+
License-File: LICENSE
|
|
24
|
+
Requires-Dist: jax>=0.4.20
|
|
25
|
+
Requires-Dist: optax>=0.1.7
|
|
26
|
+
Requires-Dist: flax>=0.7.5
|
|
27
|
+
Provides-Extra: dev
|
|
28
|
+
Requires-Dist: build>=1.2; extra == "dev"
|
|
29
|
+
Requires-Dist: twine>=5; extra == "dev"
|
|
30
|
+
Requires-Dist: pytest>=8; extra == "dev"
|
|
31
|
+
Requires-Dist: pytest-cov>=5; extra == "dev"
|
|
32
|
+
Requires-Dist: ruff>=0.5; extra == "dev"
|
|
33
|
+
Requires-Dist: pyright>=1.1; extra == "dev"
|
|
34
|
+
Requires-Dist: pre-commit>=3; extra == "dev"
|
|
35
|
+
Requires-Dist: chex>=0.1.8; extra == "dev"
|
|
36
|
+
Provides-Extra: docs
|
|
37
|
+
Requires-Dist: mkdocs>=1.5; extra == "docs"
|
|
38
|
+
Requires-Dist: mkdocs-material>=9; extra == "docs"
|
|
39
|
+
Provides-Extra: matfree
|
|
40
|
+
Requires-Dist: matfree; extra == "matfree"
|
|
41
|
+
Provides-Extra: lineax
|
|
42
|
+
Requires-Dist: lineax; extra == "lineax"
|
|
43
|
+
Dynamic: license-file
|
|
44
|
+
|
|
45
|
+
<h1 align="center">Somax</h1>
|
|
46
|
+
|
|
47
|
+
<p align="center">
|
|
48
|
+
<img src="assets/somax_logo_mini.png" alt="Somax logo" width="250" />
|
|
49
|
+
</p>
|
|
50
|
+
|
|
51
|
+
<p align="center">
|
|
52
|
+
<strong>Second-Order Methods in JAX</strong>
|
|
53
|
+
</p>
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
Somax is a JAX-native library for composable second-order optimization.
|
|
58
|
+
It combines curvature operators, solvers, damping, preconditioners,
|
|
59
|
+
and update transforms into flexible optimization methods.
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
Features:
|
|
63
|
+
- **Composable:** combine curvature, solvers, damping, preconditioners, and update rules.
|
|
64
|
+
- **Matrix-free:** compute curvature products without forming dense matrices.
|
|
65
|
+
- **JAX-first:** JIT-compiled steps with explicit control over execution.
|
|
66
|
+
- **Optax integration:** apply standard Optax transformations to computed directions.
|
|
67
|
+
- **Research-friendly:** swap components, run ablations, and prototype new methods.
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
|
|
72
|
+
> The catfish in the logo is a small nod to *som*, the Belarusian word for catfish.
|
|
73
|
+
> A quiet bottom-dweller, but not a first-order creature.
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
## Installation
|
|
81
|
+
|
|
82
|
+
Install [JAX](https://docs.jax.dev/en/latest/installation.html) for your hardware, then:
|
|
83
|
+
|
|
84
|
+
```bash
|
|
85
|
+
pip install python-somax
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
For development:
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
git clone https://github.com/cor3bit/somax.git
|
|
92
|
+
cd somax
|
|
93
|
+
pip install -e ".[dev]"
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The `lineax` package is optional and needed only for CG with `backend="lineax"`.
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
## Quickstart
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
A minimal stochastic Gauss-Newton training loop for multiclass classification:
|
|
107
|
+
|
|
108
|
+
```python
|
|
109
|
+
import jax
|
|
110
|
+
import jax.numpy as jnp
|
|
111
|
+
import somax
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def predict_fn(params, x):
|
|
115
|
+
h = jnp.tanh(x @ params["W1"] + params["b1"])
|
|
116
|
+
return h @ params["W2"] + params["b2"]
|
|
117
|
+
|
|
118
|
+
|
|
119
|
+
rng = jax.random.PRNGKey(0)
|
|
120
|
+
k1, k2, k3, k4 = jax.random.split(rng, 4)
|
|
121
|
+
|
|
122
|
+
params = {
|
|
123
|
+
"W1": 0.1 * jax.random.normal(k1, (16, 32)),
|
|
124
|
+
"b1": jnp.zeros((32,)),
|
|
125
|
+
"W2": 0.1 * jax.random.normal(k2, (32, 10)),
|
|
126
|
+
"b2": jnp.zeros((10,)),
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
batch = {
|
|
130
|
+
"x": jax.random.normal(k3, (64, 16)),
|
|
131
|
+
"y": jax.random.randint(k4, (64,), 0, 10),
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
method = somax.sgn_ce(
|
|
135
|
+
predict_fn=predict_fn,
|
|
136
|
+
learning_rate=0.1,
|
|
137
|
+
lam0=1.0,
|
|
138
|
+
maxiter=10,
|
|
139
|
+
tol=1e-4,
|
|
140
|
+
)
|
|
141
|
+
|
|
142
|
+
state = method.init(params)
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
@jax.jit
|
|
146
|
+
def train_step(params, batch, state, rng):
|
|
147
|
+
return method.step(params, batch, state, rng)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
for step in range(10):
|
|
151
|
+
step_rng = jax.random.fold_in(rng, step)
|
|
152
|
+
params, state, info = train_step(params, batch, state, step_rng)
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
For more examples, see [Usage: A paper in a few lines](docs/USAGE.md).
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
## Citation
|
|
161
|
+
|
|
162
|
+
Mikalai Korbit and Mario Zanon,
|
|
163
|
+
"Second-Order, First-Class: A Composable Stack for Curvature-Aware Training",
|
|
164
|
+
ECML PKDD 2026, [arXiv](https://arxiv.org/abs/2603.25976).
|
|
165
|
+
|
|
166
|
+
```bibtex
|
|
167
|
+
@inproceedings{korbit2026second,
|
|
168
|
+
title={Second-Order, First-Class: A Composable Stack for Curvature-Aware Training},
|
|
169
|
+
author={Korbit, Mikalai and Zanon, Mario},
|
|
170
|
+
booktitle={Joint European Conference on Machine Learning and Knowledge Discovery in Databases},
|
|
171
|
+
pages={90--105},
|
|
172
|
+
year={2026},
|
|
173
|
+
organization={Springer}
|
|
174
|
+
}
|
|
175
|
+
```
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
|
|
179
|
+
## License
|
|
180
|
+
|
|
181
|
+
Apache-2.0
|
|
182
|
+
|
|
@@ -18,6 +18,7 @@ src/somax/types.py
|
|
|
18
18
|
src/somax/utils.py
|
|
19
19
|
src/somax/curvature/__init__.py
|
|
20
20
|
src/somax/curvature/base.py
|
|
21
|
+
src/somax/curvature/fgnha_ce.py
|
|
21
22
|
src/somax/curvature/ggn_ce.py
|
|
22
23
|
src/somax/curvature/ggn_mse.py
|
|
23
24
|
src/somax/curvature/hessian.py
|
|
@@ -25,15 +26,19 @@ src/somax/curvature/with_estimators.py
|
|
|
25
26
|
src/somax/damping/__init__.py
|
|
26
27
|
src/somax/damping/base.py
|
|
27
28
|
src/somax/damping/constant.py
|
|
29
|
+
src/somax/damping/scheduled.py
|
|
28
30
|
src/somax/damping/trust_region.py
|
|
29
31
|
src/somax/estimators/__init__.py
|
|
30
32
|
src/somax/estimators/base.py
|
|
33
|
+
src/somax/estimators/diagpp.py
|
|
31
34
|
src/somax/estimators/gnb_ce.py
|
|
32
35
|
src/somax/estimators/hutchinson.py
|
|
36
|
+
src/somax/estimators/lanczos.py
|
|
33
37
|
src/somax/methods/__init__.py
|
|
34
38
|
src/somax/methods/adahessian.py
|
|
35
39
|
src/somax/methods/direct_methods.py
|
|
36
40
|
src/somax/methods/egn.py
|
|
41
|
+
src/somax/methods/fgn.py
|
|
37
42
|
src/somax/methods/newton_cg.py
|
|
38
43
|
src/somax/methods/sgn.py
|
|
39
44
|
src/somax/methods/sophia_g.py
|
|
@@ -49,5 +54,5 @@ src/somax/solvers/cg.py
|
|
|
49
54
|
src/somax/solvers/direct.py
|
|
50
55
|
src/somax/solvers/identity.py
|
|
51
56
|
src/somax/solvers/row_cg.py
|
|
52
|
-
src/somax/solvers/
|
|
57
|
+
src/somax/solvers/row_cg_lifted.py
|
|
53
58
|
src/somax/solvers/row_common.py
|
|
@@ -20,9 +20,12 @@ from .methods import (
|
|
|
20
20
|
adahessian,
|
|
21
21
|
ggn_direct_mse,
|
|
22
22
|
ggn_direct_ce,
|
|
23
|
+
fgn_direct_ce,
|
|
23
24
|
newton_direct,
|
|
24
25
|
egn_ce,
|
|
25
26
|
egn_mse,
|
|
27
|
+
fgn_ce,
|
|
28
|
+
fgn_ce_exact,
|
|
26
29
|
newton_cg,
|
|
27
30
|
sgn_mse,
|
|
28
31
|
sgn_ce,
|
|
@@ -47,9 +50,12 @@ __all__ = [
|
|
|
47
50
|
"adahessian",
|
|
48
51
|
"ggn_direct_mse",
|
|
49
52
|
"ggn_direct_ce",
|
|
53
|
+
"fgn_direct_ce",
|
|
50
54
|
"newton_direct",
|
|
51
55
|
"egn_ce",
|
|
52
56
|
"egn_mse",
|
|
57
|
+
"fgn_ce",
|
|
58
|
+
"fgn_ce_exact",
|
|
53
59
|
"newton_cg",
|
|
54
60
|
"sgn_mse",
|
|
55
61
|
"sgn_ce",
|
|
@@ -33,6 +33,8 @@ from .types import Array, Batch, Params, PRNGKey, StepInfo, ScalarOrSchedule, Gr
|
|
|
33
33
|
class SecondOrderState:
|
|
34
34
|
"""Single state tree owned by Somax.
|
|
35
35
|
|
|
36
|
+
model_state: optional non-parameter model state, such as BatchNorm running
|
|
37
|
+
statistics. It is updated once by the curvature primal each outer step.
|
|
36
38
|
opt_state: post-direction tx state (Optax state or custom).
|
|
37
39
|
method_state: optional extra state for method-specific algorithms (eg AdaHessian EMAs).
|
|
38
40
|
"""
|
|
@@ -42,13 +44,14 @@ class SecondOrderState:
|
|
|
42
44
|
solver_state: Any
|
|
43
45
|
opt_state: Any
|
|
44
46
|
method_state: Any # optional extension point (eg AdaHessian)
|
|
47
|
+
model_state: Any = ()
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
class SecondOrderMethod:
|
|
48
|
-
"""Runnable Somax method: init/
|
|
51
|
+
"""Runnable Somax method: init/step.
|
|
49
52
|
|
|
50
|
-
|
|
51
|
-
init cstate -> grad/row_op -> solve direction -> tx.update -> apply -> (optional
|
|
53
|
+
step(...) performs one fused step:
|
|
54
|
+
init cstate -> grad/row_op -> solve direction -> tx.update -> apply -> (optional gain ratio) -> damping.update
|
|
52
55
|
"""
|
|
53
56
|
|
|
54
57
|
def __init__(
|
|
@@ -68,11 +71,18 @@ class SecondOrderMethod:
|
|
|
68
71
|
self.plan = plan
|
|
69
72
|
self.tx = tx
|
|
70
73
|
|
|
71
|
-
def init(
|
|
74
|
+
def init(
|
|
75
|
+
self,
|
|
76
|
+
params: Params,
|
|
77
|
+
*,
|
|
78
|
+
model_state: Any = (),
|
|
79
|
+
) -> SecondOrderState:
|
|
80
|
+
"""Initialize optimizer and optional non-parameter model state."""
|
|
72
81
|
m_t = jax.tree_util.tree_map(jnp.zeros_like, params) if self.plan.want.want_m else ()
|
|
73
82
|
|
|
74
83
|
return SecondOrderState(
|
|
75
84
|
step=jnp.asarray(0, jnp.int32),
|
|
85
|
+
model_state=model_state,
|
|
76
86
|
damp_state=self.damping.init(),
|
|
77
87
|
precond_state=self.precond.init(params),
|
|
78
88
|
solver_state=self.solver.init(params),
|
|
@@ -118,8 +128,8 @@ def assemble(
|
|
|
118
128
|
estimator: Optional[EstimatorSpec] = None,
|
|
119
129
|
telemetry: Optional[TelemetrySpec] = None,
|
|
120
130
|
|
|
121
|
-
# post-direction application
|
|
122
|
-
tx: GradientTransformation | None = None, # full optax chain
|
|
131
|
+
# post-direction application (owned)
|
|
132
|
+
tx: GradientTransformation | None = None, # full optax chain (expert); if None, we build from knobs below
|
|
123
133
|
learning_rate: ScalarOrSchedule = 1.0,
|
|
124
134
|
clip_norm: float | None = None,
|
|
125
135
|
weight_decay: float | None = None,
|
|
@@ -137,7 +147,7 @@ def assemble(
|
|
|
137
147
|
# Build plan from components
|
|
138
148
|
plan = build_plan(damping=dp, solver=slv, precond=pp, telemetry_reqs=tlm_reqs)
|
|
139
149
|
|
|
140
|
-
# Build owned post-direction tx
|
|
150
|
+
# Build owned post-direction tx (identity/sign+lr/clip/wd), unless user provided a full tx.
|
|
141
151
|
optax_chain = build_optax_tx(
|
|
142
152
|
tx=tx,
|
|
143
153
|
learning_rate=learning_rate,
|