1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
//! Math abstraction the solvers depend on.
//!
//! Two tiers per `CONTRIBUTING.md` tenet 5:
//!
//! - **Vector tier** (this module): small ops every backend can implement
//! well — [`ScaledAdd`], [`NormSquared`], [`NormInfinity`], [`Dot`],
//! [`NegInPlace`]. Backend-generic solvers (gradient descent,
//! Nelder-Mead) bound on these.
//! - **`linalg` tier**: LA-heavy ops — [`MatVec`],
//! [`MatTransposeVec`], [`GramMatrix`], [`LinearSolveSpd`],
//! [`LinearSolveLstsq`] — that only the matrix-capable backends
//! (nalgebra, faer; sparse counterparts in S2b) implement. LA-heavy
//! solvers (Gauss-Newton, LM) bound on these so other backends
//! produce compile-time errors instead of runtime surprises. The two
//! matvec ops ([`MatVec`], [`MatTransposeVec`]) are the exception: they
//! are *also* implemented for the `Vec<f64>` backend (via the
//! hand-rolled [`DenseMatrix`]) and `ndarray::Array2<f64>`, so the
//! linear-constraint solvers run on every backend. [`SymmetricEigen`] is
//! a second exception: [`DenseMatrix`] implements it via a pure-Rust
//! cyclic Jacobi eigensolver (`dense_eig`), so CMA-ES runs on the default
//! backend too. The SPD solve [`LinearSolveSpd`] and its companion
//! [`GramMatrix`] (plus the `pub(crate)` `AddDiagonalVectorInPlace`) are a
//! third: [`DenseMatrix`] implements them via a pure-Rust Cholesky
//! (`dense_chol`), so Gauss-Newton and Levenberg-Marquardt run on the
//! default backend. The QR least-squares solve [`LinearSolveLstsq`] (and the
//! trust-region-reflective `MaxDiagonal`) stay nalgebra/faer-only.
/// Scalar element type for vectors and matrices in the math layer.
///
/// Bundles every bound the rest of the crate needs from a scalar so call sites
/// can write `F: Scalar` instead of repeating the trait list. `f64` and `f32`
/// both satisfy it; user code never needs to implement it directly (the
/// blanket impl picks up any type that meets the bounds).
///
/// The constituent bounds:
///
/// - [`num_traits::Float`] — arithmetic, `epsilon`, `infinity`, `is_finite`,
/// `sqrt`, `powf`, … plus `Copy` and `PartialOrd` transitively.
/// - [`num_traits::FromPrimitive`] — `from_f64(...)` so tolerance defaults
/// (`1e-4`, `1e-8`, …) can be expressed at any `F` without sprinkling
/// `as` casts. Use [`F::from_f64(lit).unwrap()`](num_traits::FromPrimitive::from_f64)
/// at the construction site of any literal that doesn't have a Float
/// constructor of its own (e.g. `F::epsilon()` is fine as-is).
/// - [`std::iter::Sum`] — for the natural `.iter().map(...).sum()` pattern
/// used in raw test-problem functions (Sphere, Rosenbrock, …).
/// - [`std::fmt::Debug`] — so solver / state structs can `#[derive(Debug)]`.
/// - [`Default`] — matches `f64`'s `Default = 0.0` so generic states can
/// `Option<F>::default()` cleanly.
/// - `'static` — matches `f64`'s implicit `'static` so the bound doesn't
/// force lifetime plumbing through every solver.
/// In-place `self ← self + scalar · other`. Backend-generic vector update.
///
/// The scalar type defaults to `f64` so the legacy `ScaledAdd<f64>` bound
/// keeps working unchanged while individual backends start to broaden into
/// other `F: Scalar`.
/// `‖x‖₂² = Σ xᵢ²`. Avoids the `sqrt` cost when the squared form is
/// what's actually needed (most quadratic-cost convergence checks).
///
/// `F` defaults to `f64` so existing `V: NormSquared` bounds keep
/// resolving to the `f64` impl unchanged.
/// `‖x‖_∞ = maxᵢ |xᵢ|`. Used by first-order optimality stopping rules
/// (e.g. `‖∇f‖_∞ ≤ tol`).
///
/// `F` defaults to `f64`; see [`NormSquared`] for the rationale.
/// Inner product of two same-shaped values. Used by line searches that take
/// an explicit search direction (Armijo and curvature checks both need
/// `gᵀd`). Generalizes `NormSquared`: `x.norm_squared() == x.dot(x)`.
///
/// `F` defaults to `f64`; see [`NormSquared`] for the rationale.
/// In-place negation. Lets solvers compute `direction = -gradient` in a
/// backend-generic way without allocating per-iteration scratch types.
/// In-place scalar multiplication `self ← scalar · self`. Used by
/// CMA-ES to update the cumulation paths (`p_σ ← (1−c_σ) p_σ + …`,
/// Hansen 2016 eq. 31) and the covariance matrix
/// (`C ← (1 + c_1 δ_h − c_1 − c_µ Σ w_j) C + …`, eq. 47) without
/// allocating a clone per iteration.
///
/// `ScaledAdd<f64>` already covers `self ← self + s · other`; the
/// borrow checker forbids `self.scaled_add(s, &self)`, so an honest
/// in-place scale needs its own trait.
///
/// `F` defaults to `f64`; see [`NormSquared`] for the rationale.
/// Number of components in a 1-D vector. Used by CMA-ES to derive the
/// search-space dimension `n` from a template vector at solver
/// construction time, so callers don't have to thread `n` separately
/// from the initial mean. Method named `vec_len` to avoid colliding
/// with the inherent `len()` methods on `Vec`, `DVector`, `Array1`,
/// `Col`.
/// In-place componentwise multiplication `self[i] ← self[i] · other[i]`.
/// CMA-ES uses this to apply the diagonal `D` (sqrt-eigenvalue) factor:
/// the sampling step `y_k = B D z_k` is `z.component_mul_assign(&d);
/// y = B.matvec(&z)`, and the conjugate-path step `C^{−1/2} v =
/// B (1/d ⊙ Bᵀv)` is the same pattern with `1/d`.
/// In-place componentwise maximum `self[i] ← max(self[i], other[i])`.
/// Levenberg-Marquardt uses this to maintain the monotone running-max
/// scaling diagonal `D_k = max(D_{k−1}, diag(JᵀJ))` of MINPACK-style
/// Marquardt damping (Moré 1978): a parameter whose column curvature
/// momentarily drops doesn't lose the damping floor accumulated from
/// earlier iterations.
pub
/// In-place componentwise division `self[i] ← self[i] / other[i]`. The
/// counterpart of [`ComponentMulAssign`]. Levenberg-Marquardt forms the
/// MINPACK `gtol` measure — the per-column cosine `g_j² / (JᵀJ)ⱼⱼ` —
/// with this.
///
/// # Contract
///
/// - **Caller must:** ensure `other[i] ≠ 0` for every `i`; division by a
/// zero divisor yields a non-finite value that propagates. (LM floors
/// the divisor away from zero with [`FloorZerosInPlace`] first.)
pub
/// In-place floor of non-positive entries to a positive `value`,
/// leaving strictly-positive entries untouched
/// (`self[i] ← value` where `self[i] ≤ 0`, else unchanged).
///
/// This is *not* a blanket lower-clamp: a legitimately small positive
/// entry keeps its value. It exists for MINPACK's zero-column guard in
/// Marquardt-scaled Levenberg-Marquardt — a Jacobian column that is
/// entirely zero gives `diag(JᵀJ)ⱼ = 0`, which would make the damping
/// `μ·D` vanish on that coordinate and leave the normal-equations
/// matrix singular there. MINPACK sets such a column's scale to `1`
/// (lmder, `mode = 1`); flooring zeros to `1` reproduces that, so a
/// fully-insensitive parameter simply stays put instead of failing the
/// Cholesky.
///
/// `F` defaults to `f64`; see [`NormSquared`] for the rationale.
pub
/// Per-component scalar read/write on a 1-D vector backend. The minimal
/// access finite-difference differentiation needs: perturb one coordinate
/// of a parameter vector and read derivative values back out
/// ([`crate::core::numdiff`]).
///
/// Methods are named `get_scalar` / `set_scalar` rather than `get` / `set`
/// to dodge the inherent `slice::get -> Option<&T>` (which would shadow a
/// trait `get` at call sites on a concrete `Vec`) and the `Index` /
/// `IndexMut` traits — the same defensive convention as
/// [`VectorLen::vec_len`].
///
/// `F` defaults to `f64`; see [`NormSquared`] for the rationale.
///
/// # Contract
///
/// - **Caller must:** pass `i < self.vec_len()`. Backends index directly and
/// panic on out-of-bounds, matching the underlying `Index` impls.
pub use ClampInPlace;
pub use DenseMatrix;
pub use ;
pub use ;
// Per-solver plumbing ops: expressed as traits for backend portability, but
// they carry no meaning outside one shipped solver's internals (diagonal
// pokes, rank-one updates, the Coleman-Li affine scaling). Kept `pub(crate)`
// so they stay off the frozen public surface — promote to `pub` on a concrete
// external request (promotion is non-breaking; the reverse is not).
pub use BoxAffineScaling;
pub use ;