Skip to main content

rustymath_core/
optimize.rs

1//! Minimizing a function of several variables, and solving equations, nonlinear or linear.
2//!
3//! [`minimize`] runs one of five methods on an [`Objective`]. [`solve`] finds a zero of a [`Mapping`], or its least-squares solution where it gives more values than it has variables: by Brent's method for one equation in one variable, and by Levenberg–Marquardt otherwise. [`solve_linear`] solves A x = b by a Krylov method, where a [`Mapping`] gives A's products with vectors, and [`solve_affine`] solves f(x) = 0 the same way for an affine `f`, through f's values alone. [`Options`] gives the method, the bounds and when to stop. Where it gives no method, [`Method::choose`] picks one for the [`Task`].
4//!
5//! [`minimize`] and [`solve`] start from the start point moved inside the bounds, evaluate the function only inside them, and return the best point they evaluated. A NaN value counts as +∞. An error from the function stops the search and comes back as the front's error. A search that uses up its evaluations returns its best point with `converged` false.
6//!
7//! The Krylov methods use A only through its products. Conjugate gradients (CG) solves a symmetric positive definite system, MINRES a symmetric one, and GMRES any square one. Least squares runs CG or MINRES on the normal equations AᵀA x = Aᵀb, with one product with A and one with Aᵀ for each product with AᵀA. MINRES there gives the iterates of Fong and Saunders' LSMR, and CG those of CGLS, which match Paige and Saunders' LSQR. Running on AᵀA squares A's condition number. On ill-conditioned problems that costs some accuracy next to LSMR's and LSQR's own Golub–Kahan bidiagonalization, which works on A itself. A mapping's [`Mapping::precondition`] gives a preconditioner, an approximation to the inverse of the matrix the method runs on, which cuts the steps a solve needs. CG and MINRES apply it on each step, and GMRES on the right. CG and MINRES both build on the Lanczos process. MINRES and GMRES both track the residual through plane rotations, the same ones the tridiagonal and bidiagonal iterations use.
8use crate::{Native, linalg::{Factor, axpy, dot, givens, lstsq}, random::{Key, unit}};
9use rand::Rng;
10use std::collections::VecDeque;
11
12/// A function of a slice of `F` to minimize. A closure that returns a `Result` is one. [`Differentiable`] adds a gradient.
13pub trait Objective<F> {
14    /// What stops an evaluation, such as an error in the caller's function or an interrupt.
15    type Error;
16    /// The value at `x`.
17    fn value(&mut self, x: &[F]) -> Result<F, Self::Error>;
18    /// Whether `values` evaluates a batch of points in one call. Nelder–Mead evaluates its first simplex and each shrink as a batch, and differential evolution each generation. Without `batch`, a search calls `value` on each point.
19    fn batch(&self) -> bool { false }
20    /// The value at each row of `xs`, into `out`, with `xs.len() / out.len()` items to a row. Methods call it only where `batch` is true.
21    fn values(&mut self, xs: &[F], out: &mut [F]) -> Result<(), Self::Error> { let _ = (xs, out); unreachable!("this objective evaluates no batches") }
22    /// Whether `gradient` gives the gradient. L-BFGS needs one, and [`Method::choose`] picks L-BFGS for an objective that has one.
23    fn has_gradient(&self) -> bool { false }
24    /// The gradient at `x`, into `g`. Methods call it only where `has_gradient` is true.
25    fn gradient(&mut self, x: &[F], g: &mut [F]) -> Result<(), Self::Error> { let _ = (x, g); unreachable!("this objective has no gradient") }
26}
27
28impl<F, E, V: FnMut(&[F]) -> Result<F, E>> Objective<F> for V {
29    type Error = E;
30    fn value(&mut self, x: &[F]) -> Result<F, E> { self(x) }
31}
32
33/// A function from a slice of `F` to a slice of `F`, for [`solve`] and [`solve_linear`]. A closure that writes the values into its second argument and returns a `Result` is one. [`Differentiable`] adds vector–Jacobian products.
34pub trait Mapping<F> {
35    /// What stops an evaluation, as for [`Objective`].
36    type Error;
37    /// The values at `x`, into `out`.
38    fn value(&mut self, x: &[F], out: &mut [F]) -> Result<(), Self::Error>;
39    /// Whether `vjp` gives vector–Jacobian products. Least squares in [`solve_linear`] needs them.
40    fn has_vjp(&self) -> bool { false }
41    /// uᵀJ, the product of `u` with the Jacobian at `x`, into `out`. For a linear mapping it's the product of the transpose with `u`, at any `x`. Methods call it only where `has_vjp` is true.
42    fn vjp(&mut self, u: &[F], x: &[F], out: &mut [F]) -> Result<(), Self::Error> {
43        let _ = (u, x, out);
44        unreachable!("this mapping has no vector–Jacobian product")
45    }
46    /// Whether `jvp` gives Jacobian–vector products.
47    fn has_jvp(&self) -> bool { false }
48    /// Jv, the product of the Jacobian at `x` with `v`, into `out`. For a linear mapping it's the product of the matrix with `v`, at any `x`. It gives `false` where it declines, as for a function it can't differentiate at `x`, and the caller then takes differences instead. Methods call it only where `has_jvp` is true.
49    fn jvp(&mut self, v: &[F], x: &[F], out: &mut [F]) -> Result<bool, Self::Error> {
50        let _ = (v, x, out);
51        unreachable!("this mapping has no Jacobian–vector product")
52    }
53    /// The Jacobian at `x`, with a row of `x.len()` items for each value, into `j`, or `false` where Levenberg–Marquardt should take forward differences instead. By default, a mapping builds it from `vjp`, one row per product, when it has no more values than variables or no `jvp`. Otherwise it builds it from `jvp`, one column per product. Each takes no more calls than differences do. It gives `false` for a mapping with neither, or where `jvp` declines.
54    fn jacobian(&mut self, x: &[F], j: &mut [F]) -> Result<bool, Self::Error> where F: Native {
55        let n = x.len();
56        if n == 0 { return Ok(false); }
57        let m = j.len() / n;
58        if self.has_vjp() && (m <= n || !self.has_jvp()) {
59            let mut u = vec![F::zero(); m];
60            for (i, row) in j.chunks_exact_mut(n).enumerate() {
61                u[i] = F::one();
62                self.vjp(&u, x, row)?;
63                u[i] = F::zero();
64            }
65            return Ok(true);
66        }
67        if !self.has_jvp() { return Ok(false); }
68        let (mut v, mut column) = (vec![F::zero(); n], vec![F::zero(); m]);
69        for c in 0..n {
70            v[c] = F::one();
71            if !self.jvp(&v, x, &mut column)? { return Ok(false); }
72            v[c] = F::zero();
73            for (i, &value) in column.iter().enumerate() { j[i * n + c] = value; }
74        }
75        Ok(true)
76    }
77    /// Whether `precondition` gives a preconditioner's products. [`solve_linear`] runs without one otherwise.
78    fn has_preconditioner(&self) -> bool { false }
79    /// The product with `v` of a preconditioner for [`solve_linear`], into `out`: M, an approximation to the inverse of the matrix, or of AᵀA for least squares. CG and MINRES need M symmetric positive definite. Methods call it only where `has_preconditioner` is true.
80    fn precondition(&mut self, v: &[F], out: &mut [F]) -> Result<(), Self::Error> { let _ = (v, out); unreachable!("this mapping has no preconditioner") }
81}
82
83impl<F, E, V: FnMut(&[F], &mut [F]) -> Result<(), E>> Mapping<F> for V {
84    type Error = E;
85    fn value(&mut self, x: &[F], out: &mut [F]) -> Result<(), E> { self(x, out) }
86}
87
88/// A function and its derivative, from two closures. As an [`Objective`], `value` gives the value and `derivative` writes the gradient. As a [`Mapping`], `value` writes the values and `derivative` takes `(u, x, out)` and writes the vector–Jacobian product.
89pub struct Differentiable<V, G> { pub value: V, pub derivative: G }
90
91impl<F, E, V: FnMut(&[F]) -> Result<F, E>, G: FnMut(&[F], &mut [F]) -> Result<(), E>> Objective<F> for Differentiable<V, G> {
92    type Error = E;
93    fn value(&mut self, x: &[F]) -> Result<F, E> { (self.value)(x) }
94    fn has_gradient(&self) -> bool { true }
95    fn gradient(&mut self, x: &[F], g: &mut [F]) -> Result<(), E> { (self.derivative)(x, g) }
96}
97
98impl<F, E, V: FnMut(&[F], &mut [F]) -> Result<(), E>, G: FnMut(&[F], &[F], &mut [F]) -> Result<(), E>> Mapping<F> for Differentiable<V, G> {
99    type Error = E;
100    fn value(&mut self, x: &[F], out: &mut [F]) -> Result<(), E> { (self.value)(x, out) }
101    fn has_vjp(&self) -> bool { true }
102    fn vjp(&mut self, u: &[F], x: &[F], out: &mut [F]) -> Result<(), E> { (self.derivative)(u, x, out) }
103}
104
105/// The methods [`minimize`], [`solve`] and [`solve_linear`] run.
106#[derive(Clone, Copy, Debug, PartialEq, Eq)]
107pub enum Method {
108    /// Brent's method, for one variable. Minimizing, it brackets a minimum with growing steps from the start, which stop at a bound, then takes golden-section steps, and parabolic steps where they shrink the interval fast enough. Solving one equation, it takes finite bounds as its bracket. Otherwise it steps outward from the start, extending the end whose value is nearer zero by 1.6 times the bracket's width, until the values' signs differ. A step that lands where the value is NaN moves halfway back toward its end until the value is defined. A side stops stepping at a bound, or where it can't step to a defined value. Inverse quadratic interpolation and secant steps then shrink the bracket, with bisection wherever they don't shrink it fast enough.
109    Brent,
110    /// Nelder–Mead's simplex, with Gao and Han's coefficients for the number of variables. It restarts from its best point until a restart no longer lowers the value.
111    NelderMead,
112    /// Powell's 1964 method of conjugate directions: Brent's method along each direction in turn, then along the step they made together, which replaces the direction of largest decrease where Powell's test accepts it.
113    Powell,
114    /// L-BFGS with the last 10 steps and a strong Wolfe line search, for an objective with a gradient. It takes no bounds.
115    Lbfgs,
116    /// Differential evolution within finite bounds, rand/1/bin with crossover 0.7 and a mutation factor drawn from 0.5 to 1 each generation. The start is one member, and the rest are uniform within the bounds. It stops when the members' values lie within 1% of the best, or their points within 1% of the bounds' width in every variable. Powell's method then polishes the best point.
117    Evolution,
118    /// Levenberg–Marquardt, for a zero or least-squares solution of nonlinear equations. Each step solves the linear least-squares problem of the Jacobian, damped with Marquardt's scale for each variable, through `linalg::lstsq`. Nielsen's rule adjusts the damping after each step. The Jacobian comes from [`Mapping::jacobian`], or else from forward differences, at one evaluation for each variable. It stops when a step moves the point by less than `tolerance`, relative, or when the values are orthogonal to every column of the Jacobian within `tolerance`. It fails where the values are infinite or the Jacobian isn't finite. It takes no bounds.
119    LevenbergMarquardt,
120    /// Conjugate gradients, for a symmetric positive definite matrix. On the normal equations of least squares it gives the iterates of CGLS, which match Paige and Saunders' LSQR.
121    Cg,
122    /// MINRES, for a symmetric matrix, definite or not. On the normal equations of least squares it gives the iterates of Fong and Saunders' LSMR.
123    Minres,
124    /// GMRES, restarted every [`GMRES_RESTART`] steps, for any square matrix.
125    Gmres,
126}
127
128impl Method {
129    /// Each method's name, which `FromStr` reads, ignoring case, and `Display` writes.
130    pub const NAMES: [(&'static str, Method); 9] = [
131        ("brent", Method::Brent), ("nelder-mead", Method::NelderMead), ("powell", Method::Powell), ("l-bfgs", Method::Lbfgs),
132        ("differential-evolution", Method::Evolution), ("levenberg-marquardt", Method::LevenbergMarquardt), ("cg", Method::Cg), ("minres", Method::Minres),
133        ("gmres", Method::Gmres),
134    ];
135
136    /// The method a front runs for `task` on `variables` variables when [`Options::method`] is `None`. Minimizing, that's Brent's method for one variable, L-BFGS for an objective with a gradient and no bounds, Powell's method within bounds, and Nelder–Mead otherwise. Solving, it's Brent's method for one equation in one variable, and Levenberg–Marquardt otherwise. Solving linear equations, it's MINRES for least squares, and GMRES for a square system.
137    pub fn choose(task: Task, variables: usize, bounded: bool) -> Method {
138        match task {
139            Task::Minimize { gradient } => {
140                if variables == 1 { Method::Brent }
141                else if gradient && !bounded { Method::Lbfgs }
142                else if bounded { Method::Powell }
143                else { Method::NelderMead }
144            }
145            Task::Solve { residuals } => { if variables == 1 && residuals == 1 { Method::Brent } else { Method::LevenbergMarquardt } }
146            Task::Linear { .. } => { if task.least_squares(variables) { Method::Minres } else { Method::Gmres } }
147        }
148    }
149}
150
151impl std::str::FromStr for Method {
152    type Err = crate::error::Error;
153    fn from_str(name: &str) -> Result<Method, Self::Err> {
154        let found = Method::NAMES.iter().find(|(key, _)| key.eq_ignore_ascii_case(name));
155        let unknown =
156            || crate::error::Error::new(crate::error::Kind::Domain, format!("unknown method: use one of {}", Method::NAMES.map(|(key, _)| key).join(", ")));
157        found.map(|&(_, method)| method).ok_or_else(unknown)
158    }
159}
160
161impl std::fmt::Display for Method {
162    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
163        f.write_str(Method::NAMES.iter().find(|&&(_, method)| method == *self).expect("every method has a name").0)
164    }
165}
166
167/// What a front is for, which decides the methods that suit it.
168#[derive(Clone, Copy, Debug, PartialEq, Eq)]
169pub enum Task {
170    /// [`minimize`], for an objective with or without a gradient.
171    Minimize { gradient: bool },
172    /// [`solve`], for a mapping that gives `residuals` values.
173    Solve { residuals: usize },
174    /// [`solve_linear`], for `equations` equations, with or without products with the matrix's transpose.
175    Linear { equations: usize, adjoint: bool },
176}
177
178impl Task {
179    /// Whether [`solve_linear`] solves least squares for `variables` unknowns: for a system that isn't square, or whose matrix gives products with its transpose.
180    pub fn least_squares(&self, variables: usize) -> bool { matches!(*self, Task::Linear { equations, adjoint } if equations != variables || adjoint) }
181}
182
183/// The least and greatest value of each variable. An infinite limit leaves that side open.
184#[derive(Clone, Copy, Debug)]
185pub struct Bounds<'a, F> { pub lower: &'a [F], pub upper: &'a [F] }
186
187impl<F: Native> Bounds<'_, F> {
188    /// Moves `x` inside the bounds.
189    fn clamp(&self, x: &mut [F]) { for ((x, &l), &u) in x.iter_mut().zip(self.lower).zip(self.upper) { *x = x.max(l).min(u) } }
190
191    /// The range of `t` that keeps `x + t d` inside the bounds.
192    fn along(&self, x: &[F], d: &[F]) -> (F, F) {
193        let mut range = (F::neg_infinity(), F::infinity());
194        for i in (0..x.len()).filter(|&i| d[i] != F::zero()) {
195            let (p, q) = ((self.lower[i] - x[i]) / d[i], (self.upper[i] - x[i]) / d[i]);
196            range = (range.0.max(p.min(q)), range.1.min(p.max(q)));
197        }
198        range
199    }
200}
201
202/// What a front runs and when it stops. `Options::default()` lets [`Method::choose`] pick the method and leaves every variable unbounded.
203#[derive(Clone, Copy, Debug)]
204pub struct Options<'a, F> {
205    /// The method, or `None` for the one [`Method::choose`] picks.
206    pub method: Option<Method>,
207    /// Limits on the variables. L-BFGS, Levenberg–Marquardt and the Krylov methods take none, and differential evolution needs finite ones. For Brent's method solving an equation, finite limits are the bracket.
208    pub bounds: Option<Bounds<'a, F>>,
209    /// The relative change at which a method stops, or `None` for √ε of `F`. A Krylov method stops when its residual's norm falls to `tolerance` times its right-hand side's.
210    pub tolerance: Option<F>,
211    /// The most evaluations of the function, or `None` for 1000 for each variable, plus 1000, and for differential evolution another 1000 for each member of its population. For [`solve_linear`], the most products with the matrix and its transpose, or `None` for 20 for each unknown, plus 20.
212    pub evaluations: Option<usize>,
213    /// Differential evolution's population, or `None` for 15 for each variable, and at least 5.
214    pub population: Option<usize>,
215    /// The seed of differential evolution's generator.
216    pub seed: u64,
217}
218
219impl<F> Default for Options<'_, F> {
220    fn default() -> Self { Options { method: None, bounds: None, tolerance: None, evaluations: None, population: None, seed: 0 } }
221}
222
223impl<F: Native> Options<'_, F> {
224    /// The method a front runs for `task` on `variables` variables: `method`, or the one [`Method::choose`] picks.
225    pub fn method_for(&self, task: Task, variables: usize) -> Method { self.method.unwrap_or_else(|| Method::choose(task, variables, self.bounds.is_some())) }
226
227    /// Why a front would reject these options for `task` on `variables` variables: a Domain error with the reason. A binding calls it first and reports the error.
228    pub fn check(&self, task: Task, variables: usize) -> Result<(), crate::error::Error> {
229        self.problem(task, variables).map_err(|reason| crate::error::Error::new(crate::error::Kind::Domain, reason))
230    }
231
232    fn problem(&self, task: Task, variables: usize) -> Result<(), &'static str> {
233        use Method::*;
234        let bounds = self.bounds;
235        if bounds.is_some_and(|b| b.lower.len() != variables || b.upper.len() != variables) {
236            return Err("bounds need a lower and an upper limit for each variable");
237        }
238        if bounds.is_some_and(|b| b.lower.iter().zip(b.upper).any(|(l, u)| !(l <= u))) { return Err("each lower limit must be at most its upper limit"); }
239        if self.tolerance.is_some_and(|t| !(t > F::zero() && t.is_finite())) { return Err("the tolerance must be a positive number"); }
240        let method = self.method_for(task, variables);
241        match task {
242            Task::Minimize { gradient } => match method {
243                Brent if variables > 1 => Err("Brent's method takes one variable"), Lbfgs if !gradient => Err("L-BFGS needs an objective with a gradient"),
244                Lbfgs if bounds.is_some() => Err("L-BFGS takes no bounds"),
245                Evolution if !bounds.is_some_and(|b| b.lower.iter().chain(b.upper).all(|v| v.is_finite())) => Err("differential evolution needs finite bounds"),
246                Evolution if self.population.is_some_and(|p| p < 4) => Err("differential evolution needs a population of at least 4"),
247                Brent | NelderMead | Powell | Lbfgs | Evolution => Ok(()), _ => Err("that method solves equations, and doesn't minimize")
248            },
249            Task::Solve { residuals } => match method {
250                Brent if variables != 1 || residuals != 1 => Err("Brent's method solves one equation in one variable"),
251                LevenbergMarquardt if bounds.is_some() => Err("Levenberg–Marquardt takes no bounds"), Brent | LevenbergMarquardt => Ok(()),
252                _ => Err("that method doesn't solve nonlinear equations")
253            },
254            Task::Linear { equations, adjoint } => match method {
255                Cg | Minres | Gmres if bounds.is_some() => Err("linear solving takes no bounds"),
256                Cg | Minres | Gmres if equations != variables && !adjoint => Err("least squares needs products with the transpose"),
257                Cg | Minres | Gmres => Ok(()), _ => Err("that method doesn't solve linear equations")
258            },
259        }
260    }
261
262    /// The method and the tolerance a front runs with for `task` on `variables` variables. It panics where [`Options::check`] rejects the options.
263    fn settings(&self, task: Task, variables: usize) -> (Method, F) {
264        if let Err(problem) = self.check(task, variables) { panic!("{problem}") }
265        (self.method_for(task, variables), self.tolerance.unwrap_or_else(|| F::epsilon().sqrt()))
266    }
267}
268
269/// What a front found.
270#[derive(Clone, Debug, PartialEq)]
271pub struct Found<X, F> {
272    /// The best point evaluated. For [`solve_linear`], the last iterate.
273    pub x: X,
274    /// The function's value at `x`. For [`solve`] and [`solve_linear`], the norm of the residuals there.
275    pub value: F,
276    /// How many times the function was evaluated. For [`solve_linear`], how many products with the matrix and its transpose, including one for the residual at `x`.
277    pub evaluations: usize,
278    /// Whether the method's stopping test passed before the evaluations ran out.
279    pub converged: bool,
280}
281
282/// The minimum of `objective` from `start`, by the method [`Options::method_for`] gives.
283///
284/// # Panics
285///
286/// If [`Options::check`] rejects the options.
287pub fn minimize<F: Native, O: Objective<F>>(objective: &mut O, start: &[F], options: &Options<'_, F>) -> Result<Found<Vec<F>, F>, O::Error> {
288    let (n, task) = (start.len(), Task::Minimize { gradient: objective.has_gradient() });
289    let (method, tolerance) = options.settings(task, n);
290    let population = options.population.unwrap_or((15 * n).max(5));
291    let limit = options.evaluations.unwrap_or(1000 * (n + 1) + if method == Method::Evolution { 1000 * population } else { 0 });
292    let mut search = Search::new(objective, start, options.bounds, limit);
293    let outcome = match method {
294        _ if n == 0 => search.value(&mut []).map(|_| true), Method::Brent => brent_method(&mut search, tolerance),
295        Method::NelderMead => nelder_mead(&mut search, tolerance), Method::Powell => powell(&mut search, tolerance),
296        Method::Lbfgs => lbfgs(&mut search, tolerance), Method::Evolution => evolution(&mut search, population, tolerance, options.seed),
297        _ => unreachable!("`check` accepts only methods that minimize")
298    };
299    passed(outcome).map(|converged| search.found(converged))
300}
301
302/// A zero of `mapping` from `start`, by the method [`Options::method_for`] gives. `first` holds `mapping`'s values at `start`, which the caller computes. They give the number of values, and count as the search's first evaluation. Where `mapping` gives more values than it has variables, the result is the least-squares solution, and where it gives fewer, a nearby solution.
303///
304/// # Panics
305///
306/// If [`Options::check`] rejects the options.
307pub fn solve<F: Native + Factor, M: Mapping<F>>(mapping: &mut M, start: &[F], first: &[F], options: &Options<'_, F>) -> Result<Found<Vec<F>, F>, M::Error> {
308    let (n, task) = (start.len(), Task::Solve { residuals: first.len() });
309    let (method, tolerance) = options.settings(task, n);
310    let mut residual = Norm { mapping, r: first.to_vec() };
311    let mut search = Search::new(&mut residual, start, options.bounds, options.evaluations.unwrap_or(1000 * (n + 1)));
312    search.seed(start, norm(first));
313    let outcome = match method {
314        _ if n == 0 => Ok(true), Method::Brent => root(&mut search, (start[0], first[0]), tolerance), _ => levenberg_marquardt(&mut search, tolerance)
315    };
316    passed(outcome).map(|converged| search.found(converged))
317}
318
319/// The solution of A x = `b` from `start`, by the method [`Options::method_for`] gives. `a` gives A's products with vectors, and `b` has one item for each equation. A system that isn't square, or whose `a` gives products with A's transpose through [`Mapping::vjp`], gets its least-squares solution, from the normal equations AᵀA x = Aᵀb. A right-hand side of zeros gives zeros. `a`'s [`Mapping::precondition`] gives the products of a preconditioner M, which approximates A⁻¹, or (AᵀA)⁻¹ for least squares. CG and MINRES need M symmetric positive definite, and GMRES applies it on the right. A good M cuts the products a solve needs.
320///
321/// # Panics
322///
323/// If [`Options::check`] rejects the options.
324pub fn solve_linear<F: Native, M: Mapping<F>>(a: &mut M, b: &[F], start: &[F], options: &Options<'_, F>) -> Result<Found<Vec<F>, F>, M::Error> {
325    let (m, n) = (b.len(), start.len());
326    let task = Task::Linear { equations: m, adjoint: a.has_vjp() };
327    let (method, tolerance) = options.settings(task, n);
328    let limit = options.evaluations.unwrap_or(20 * (n + 1));
329    let mut op = Products { a, at: start, normal: task.least_squares(n), inner: vec![F::zero(); m], products: 0, limit };
330    let mut x = start.to_vec();
331    let outcome = op.rhs(b).and_then(|rhs| {
332        if norm(&rhs) == F::zero() { x.fill(F::zero()); return Ok(true); }
333        match method {
334            Method::Cg => cg(&mut op, &rhs, &mut x, tolerance), Method::Minres => minres(&mut op, &rhs, &mut x, tolerance),
335            _ => gmres(&mut op, &rhs, &mut x, tolerance)
336        }
337    });
338    let converged = passed(outcome)?;
339    op.a.value(&x, &mut op.inner)?;
340    for (r, &v) in op.inner.iter_mut().zip(b) { *r = v - *r }
341    Ok(Found { value: norm(&op.inner), x, evaluations: op.products + 1, converged })
342}
343
344/// The solution of f(x) = 0 for an affine `f`, f(x) = A x − b, from `start`, by [`solve_linear`] on products that it computes from `f`'s values alone. `first` holds f(`start`), which the caller computes. It gives the number of residuals, and serves as f(0) where `start` is zero. f(0) gives −b. Each product A v is (f(t v) − f(0)) ÷ t, for the power of two t that makes t |A v| about 16 |b|. The subtraction of f(0) then loses no digits, however large b is. `f`'s `vjp` gives the transpose's products, and its `precondition` the preconditioner's. A product evaluates `f` once, or twice where the ratio |A v| ÷ |v| from the last product gave too small a t. `evaluations` counts products, as for `solve_linear`.
345///
346/// # Panics
347///
348/// If [`Options::check`] rejects the options.
349pub fn solve_affine<F: Native, M: Mapping<F>>(f: &mut M, start: &[F], first: &[F], options: &Options<'_, F>) -> Result<Found<Vec<F>, F>, M::Error> {
350    let f0 = if start.iter().all(|&x| x == F::zero()) { first.to_vec() } else {
351        let mut f0 = vec![F::zero(); first.len()];
352        f.value(&vec![F::zero(); start.len()], &mut f0)?;
353        f0
354    };
355    let b: Vec<F> = f0.iter().map(|&v| -v).collect();
356    let mut a = Affine { f, size: norm(&b), f0, ratio: None, scaled: Vec::with_capacity(start.len()) };
357    solve_linear(&mut a, &b, start, options)
358}
359
360/// Why a method stopped before its stopping test passed: the function's error, or the evaluations ran out.
361enum Halt<E> { Error(E), Spent }
362
363/// Whether a method's stopping test passed, from how it ended: `false` where the evaluations ran out, or the error that stopped it.
364fn passed<E>(outcome: Result<bool, Halt<E>>) -> Result<bool, E> {
365    match outcome { Ok(converged) => Ok(converged), Err(Halt::Spent) => Ok(false), Err(Halt::Error(e)) => Err(e) }
366}
367
368/// The objective as every method sees it: each value counted against the limit, taken at the caller's point after moving it inside the bounds, with NaN as +∞, and the best point so far kept.
369struct Search<'a, F, O> { objective: &'a mut O, bounds: Option<Bounds<'a, F>>, limit: usize, evaluations: usize, best: Vec<F>, least: F }
370
371impl<'a, F: Native, O: Objective<F>> Search<'a, F, O> {
372    fn new(objective: &'a mut O, start: &[F], bounds: Option<Bounds<'a, F>>, limit: usize) -> Self {
373        let mut best = start.to_vec();
374        if let Some(b) = bounds { b.clamp(&mut best) }
375        Search { objective, bounds, limit, evaluations: 0, best, least: F::infinity() }
376    }
377
378    /// Counts `k` evaluations, and moves each row of `xs` inside the bounds.
379    fn prepare(&mut self, xs: &mut [F], k: usize) -> Result<(), Halt<O::Error>> {
380        if self.evaluations + k > self.limit { return Err(Halt::Spent); }
381        self.evaluations += k;
382        if let Some(b) = self.bounds { for x in xs.chunks_exact_mut(self.best.len()) { b.clamp(x) } }
383        Ok(())
384    }
385
386    /// `y` with NaN as +∞, after keeping `x` as the best point if `y` is the least value so far.
387    fn record(&mut self, y: F, x: &[F]) -> F {
388        let y = if y.is_nan() { F::infinity() } else { y };
389        if y < self.least {
390            self.least = y;
391            self.best.copy_from_slice(x);
392        }
393        y
394    }
395
396    /// Counts the caller's evaluation at `start`, which gave `y`, and keeps `start` as the best point where it lies within the bounds.
397    fn seed(&mut self, start: &[F], y: F) { self.evaluations += 1; if start == self.best { self.record(y, start); } }
398
399    /// The value at `x`, after moving `x` inside the bounds.
400    fn value(&mut self, x: &mut [F]) -> Result<F, Halt<O::Error>> {
401        self.prepare(x, 1)?;
402        let y = self.objective.value(x).map_err(Halt::Error)?;
403        Ok(self.record(y, x))
404    }
405
406    /// The values at the rows of `xs`, into `out`, after moving each row inside the bounds: through one call of `values` where the objective evaluates batches, and otherwise through `value` on each row.
407    fn values(&mut self, xs: &mut [F], out: &mut [F]) -> Result<(), Halt<O::Error>> {
408        self.prepare(xs, out.len())?;
409        let n = self.best.len();
410        if self.objective.batch() { self.objective.values(xs, out).map_err(Halt::Error)? } else {
411            for (y, x) in out.iter_mut().zip(xs.chunks_exact(n)) { *y = self.objective.value(x).map_err(Halt::Error)? }
412        }
413        for (y, x) in out.iter_mut().zip(xs.chunks_exact(n)) { *y = self.record(*y, x) }
414        Ok(())
415    }
416
417    fn gradient(&mut self, x: &[F], g: &mut [F]) -> Result<(), Halt<O::Error>> { self.objective.gradient(x, g).map_err(Halt::Error) }
418
419    fn found(self, converged: bool) -> Found<Vec<F>, F> { Found { x: self.best, value: self.least, evaluations: self.evaluations, converged } }
420}
421
422/// The largest magnitude in `x`.
423fn largest<F: Native>(x: &[F]) -> F { x.iter().fold(F::zero(), |m, v| m.max(v.abs())) }
424
425/// The first step a method takes in a variable at `x`: a tenth of its magnitude, and at least 0.1.
426fn first_step<F: Native>(x: F) -> F { F::narrow(0.1) * x.abs().max(F::one()) }
427
428/// Minimizes along `d` from `x`, where the value is `fx`, moving `x` to the minimum and returning its value. The first step is `d` itself, and every step stays inside the bounds. `trial` is scratch of `x`'s length.
429fn line<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, x: &mut [F], d: &[F], fx: F, tolerance: F, trial: &mut [F]) -> Result<F, Halt<O::Error>> {
430    let (lo, hi) = search.bounds.map_or((F::neg_infinity(), F::infinity()), |b| b.along(x, d));
431    let floor = tolerance * (largest(x) + tolerance) / largest(d);
432    let (t, ft) = {
433        let mut phi = |t: F| {
434            trial.copy_from_slice(x);
435            axpy(trial, t, d);
436            search.value(trial)
437        };
438        least_along(&mut phi, fx, lo, hi, tolerance, floor)?
439    };
440    axpy(x, t, d);
441    Ok(ft)
442}
443
444/// The least value of `phi` for t from `lo` to `hi`, which contain 0, where `phi` is `f0`. Steps that grow by the golden ratio go downhill from 0, first by 1, until `phi` rises or a step reaches a limit. Brent's method then searches the bracket. Returns t and `phi(t)`.
445fn least_along<F: Native, H>(phi: &mut impl FnMut(F) -> Result<F, H>, f0: F, lo: F, hi: F, tolerance: F, floor: F) -> Result<(F, F), H> {
446    let within = |t: F| t.max(lo).min(hi);
447    let mut a = F::zero();
448    let mut b = within(F::one());
449    if b == a { b = within(-F::one()) }
450    if b == a { return Ok((a, f0)); }
451    let mut fb = phi(b)?;
452    if fb > f0 { (a, b, fb) = (b, a, f0) }
453    loop {
454        let c = within(b + F::narrow(1.618_033_988_749_895) * (b - a));
455        if c == b || !c.is_finite() { return Ok((b, fb)); }
456        let fc = phi(c)?;
457        if fc >= fb { return brent(phi, a.min(c), a.max(c), b, fb, tolerance, floor); }
458        (a, b, fb) = (b, c, fc);
459    }
460}
461
462/// Brent's minimization of `phi` between `a` and `b`, from `x` between them, where `phi` is `fx`: golden-section steps, and parabolic steps where they fall inside the interval and shrink it fast enough. It stops when the interval around the best point is within `tolerance` of that point, relative, plus `floor`.
463#[allow(clippy::too_many_arguments)]
464fn brent<F: Native, H>(phi: &mut impl FnMut(F) -> Result<F, H>, mut a: F, mut b: F, mut x: F, mut fx: F, tolerance: F, floor: F) -> Result<(F, F), H> {
465    let (half, two, golden) = (F::narrow(0.5), F::narrow(2.0), F::narrow(0.381_966_011_250_105_1));
466    let (mut v, mut w, mut fv, mut fw) = (x, x, fx, fx);
467    let (mut d, mut e) = (F::zero(), F::zero());
468    loop {
469        let m = half * (a + b);
470        let tol1 = tolerance * x.abs() + floor;
471        let tol2 = two * tol1;
472        if (x - m).abs() <= tol2 - half * (b - a) { return Ok((x, fx)); }
473        let mut parabolic = false;
474        if e.abs() > tol1 {
475            let r = (x - w) * (fx - fv);
476            let q = (x - v) * (fx - fw);
477            let mut p = (x - v) * q - (x - w) * r;
478            let mut q = two * (q - r);
479            if q > F::zero() { p = -p }
480            else { q = -q }
481            if p.abs() < (half * q * e).abs() && p > q * (a - x) && p < q * (b - x) {
482                e = d;
483                d = p / q;
484                if x + d - a < tol2 || b - (x + d) < tol2 { d = tol1.copysign(m - x) }
485                parabolic = true;
486            }
487        }
488        if !parabolic {
489            e = if x >= m { a - x } else { b - x };
490            d = golden * e;
491        }
492        let u = if d.abs() >= tol1 { x + d } else { x + tol1.copysign(d) };
493        let fu = phi(u)?;
494        if fu <= fx {
495            if u >= x { a = x }
496            else { b = x }
497            (v, fv, w, fw, x, fx) = (w, fw, x, fx, u, fu);
498        } else {
499            if u < x { a = u }
500            else { b = u }
501            if fu <= fw || w == x { (v, fv, w, fw) = (w, fw, u, fu) } else if fu <= fv || v == x || v == w { (v, fv) = (u, fu) }
502        }
503    }
504}
505
506/// Brent's method on one variable: one line search from the start.
507fn brent_method<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, tolerance: F) -> Result<bool, Halt<O::Error>> {
508    let mut x = search.best.clone();
509    let fx = search.value(&mut x)?;
510    let d = [first_step(x[0])];
511    line(search, &mut x, &d, fx, tolerance, &mut [F::zero()])?;
512    Ok(true)
513}
514
515/// Nelder–Mead from the best point so far, restarting from its best point until a restart no longer lowers the value by more than `tolerance`, relative.
516fn nelder_mead<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, tolerance: F) -> Result<bool, Halt<O::Error>> {
517    let mut x = search.best.clone();
518    simplex(search, &x, tolerance)?;
519    loop {
520        let before = search.least;
521        x.copy_from_slice(&search.best);
522        simplex(search, &x, tolerance)?;
523        if before - search.least <= tolerance * (before.abs() + tolerance) { return Ok(true); }
524    }
525}
526
527/// One run of Nelder–Mead's simplex from `x`, until its values lie within `tolerance` of the best value and its points within `tolerance` of the best point, relative. Every move puts the worst vertex at a point on the line from the centroid of the others through it.
528fn simplex<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, x: &[F], tolerance: F) -> Result<(), Halt<O::Error>> {
529    let n = x.len();
530    let (one, k) = (F::one(), F::narrow(n.max(2) as f64));
531    let (expand, contract, shrink) = (one + F::narrow(2.0) / k, F::narrow(0.75) - one / (F::narrow(2.0) * k), one - one / k);
532    let mut points = Vec::with_capacity((n + 1) * n);
533    for i in 0..=n {
534        points.extend_from_slice(x);
535        if i > 0 {
536            let (j, s) = (i - 1, first_step(x[i - 1]));
537            let down = search.bounds.is_some_and(|b| x[j] + s > b.upper[j] && b.upper[j] - x[j] < x[j] - b.lower[j]);
538            points[i * n + j] = if down { x[j] - s } else { x[j] + s };
539        }
540    }
541    let mut values = vec![F::zero(); n + 1];
542    search.values(&mut points, &mut values)?;
543    let (mut centroid, mut trial, mut other) = (vec![F::zero(); n], vec![F::zero(); n], vec![F::zero(); n]);
544    loop {
545        let (b, s, w) = ranks(&values);
546        let best = &points[b * n..][..n];
547        let spread = (0..=n).map(|i| (0..n).fold(F::zero(), |m, j| m.max((points[i * n + j] - best[j]).abs()))).fold(F::zero(), F::max);
548        if values[w] - values[b] <= tolerance * (values[b].abs() + tolerance) && spread <= tolerance * (largest(best) + tolerance) { return Ok(()); }
549        centroid.fill(F::zero());
550        for i in (0..=n).filter(|&i| i != w) { axpy(&mut centroid, one / F::narrow(n as f64), &points[i * n..][..n]) }
551        let worst = w * n..(w + 1) * n;
552        let mut moved = |out: &mut [F], t: F| {
553            for j in 0..n { out[j] = centroid[j] + t * (points[w * n + j] - centroid[j]) }
554            search.value(out)
555        };
556        let fr = moved(&mut trial, -one)?;
557        let (point, value) = if fr < values[b] {
558            let fe = moved(&mut other, -expand)?;
559            if fe < fr { (&other, fe) } else { (&trial, fr) }
560        } else if fr < values[s] { (&trial, fr) } else {
561            let fc = moved(&mut other, if fr < values[w] { -contract } else { contract })?;
562            if fc >= fr.min(values[w]) {
563                swap_rows(&mut points, b, 0, n);
564                values.swap(b, 0);
565                let (first, rest) = points.split_at_mut(n);
566                for row in rest.chunks_exact_mut(n) { for j in 0..n { row[j] = first[j] + shrink * (row[j] - first[j]) } }
567                search.values(&mut points[n..], &mut values[1..])?;
568                continue;
569            }
570            (&other, fc)
571        };
572        points[worst].copy_from_slice(point);
573        values[w] = value;
574    }
575}
576
577/// The positions of the least, second greatest and greatest of `values`, which has at least two.
578fn ranks<F: Native>(values: &[F]) -> (usize, usize, usize) {
579    let (mut b, mut w) = (0, 0);
580    for (i, &v) in values.iter().enumerate() { if v < values[b] { b = i } if v > values[w] { w = i } }
581    if b == w { w = (b + 1) % values.len() }
582    let s = (0..values.len()).filter(|&i| i != w).fold(b, |s, i| if values[i] > values[s] { i } else { s });
583    (b, s, w)
584}
585
586/// Swaps rows `i` and `j` of `n` items each.
587fn swap_rows<F>(x: &mut [F], i: usize, j: usize, n: usize) { if i != j { for k in 0..n { x.swap(i * n + k, j * n + k) } } }
588
589/// Powell's conjugate directions from the best point so far: a line search along each direction in turn, then along the step they made together where Powell's test accepts it, which replaces the direction of largest decrease. A replaced direction can point out of an active bound, so a round along replaced directions that lowers the value by less than `tolerance`, relative, restarts from the coordinate directions. Such a round along the coordinate directions ends the search.
590fn powell<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, tolerance: F) -> Result<bool, Halt<O::Error>> {
591    let n = search.best.len();
592    let two = F::narrow(2.0);
593    let mut x = search.best.clone();
594    let mut fx = search.value(&mut x)?;
595    let coordinates = |directions: &mut [F], x: &[F]| {
596        directions.fill(F::zero());
597        for i in 0..n { directions[i * n + i] = first_step(x[i]) }
598    };
599    let mut directions = vec![F::zero(); n * n];
600    coordinates(&mut directions, &x);
601    let mut coordinate = true;
602    let (mut start, mut step, mut trial) = (x.clone(), vec![F::zero(); n], vec![F::zero(); n]);
603    loop {
604        start.copy_from_slice(&x);
605        let f0 = fx;
606        let (mut largest_drop, mut drop) = (0, F::zero());
607        for i in 0..n {
608            let before = fx;
609            fx = line(search, &mut x, &directions[i * n..][..n], fx, tolerance, &mut trial)?;
610            if before - fx > drop { (largest_drop, drop) = (i, before - fx) }
611        }
612        if two * (f0 - fx) <= tolerance * (f0.abs() + fx.abs() + tolerance) {
613            if coordinate { return Ok(true); }
614            coordinates(&mut directions, &x);
615            coordinate = true;
616            continue;
617        }
618        for j in 0..n { (step[j], trial[j]) = (x[j] - start[j], two * x[j] - start[j]) }
619        let fe = search.value(&mut trial)?;
620        let square = |v: F| v * v;
621        if fe < f0 && two * (f0 - two * fx + fe) * square(f0 - fx - drop) < drop * square(f0 - fe) {
622            fx = line(search, &mut x, &step, fx, tolerance, &mut trial)?;
623            directions.copy_within((largest_drop + 1) * n.., largest_drop * n);
624            directions[(n - 1) * n..].copy_from_slice(&step);
625            coordinate = false;
626        }
627    }
628}
629
630/// L-BFGS from the best point so far. It stops when a step lowers the value by less than `tolerance`, relative, when the gradient is zero, or when no step along steepest descent lowers the value.
631fn lbfgs<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, tolerance: F) -> Result<bool, Halt<O::Error>> {
632    const MEMORY: usize = 10;
633    let n = search.best.len();
634    let mut x = search.best.clone();
635    let mut fx = search.value(&mut x)?;
636    let mut g = vec![F::zero(); n];
637    search.gradient(&x, &mut g)?;
638    let mut history: VecDeque<(Vec<F>, Vec<F>, F)> = VecDeque::with_capacity(MEMORY);
639    let (mut d, mut xn, mut gn, mut weights) = (vec![F::zero(); n], vec![F::zero(); n], vec![F::zero(); n], [F::zero(); MEMORY]);
640    loop {
641        if largest(&g) == F::zero() { return Ok(true); }
642        d.copy_from_slice(&g);
643        for (k, (s, y, rho)) in history.iter().enumerate().rev() {
644            weights[k] = *rho * dot(s, &d);
645            axpy(&mut d, -weights[k], y);
646        }
647        let scale = history.back().map_or_else(|| F::one() / largest(&g), |(s, y, _)| dot(s, y) / dot(y, y));
648        d.iter_mut().for_each(|v| *v = -*v * scale);
649        for (k, (s, y, rho)) in history.iter().enumerate() {
650            let b = *rho * dot(y, &d);
651            axpy(&mut d, -weights[k] - b, s);
652        }
653        let mut slope = dot(&g, &d);
654        if slope >= F::zero() {
655            history.clear();
656            let scale = F::one() / largest(&g);
657            for j in 0..n { d[j] = -g[j] * scale }
658            slope = dot(&g, &d);
659        }
660        let Some(fnew) = wolfe(search, &x, fx, &d, slope, &mut xn, &mut gn)? else {
661            if history.is_empty() { return Ok(true); }
662            history.clear();
663            continue;
664        };
665        let (mut s, mut y, _) = if history.len() == MEMORY { history.pop_front().unwrap() } else { (vec![F::zero(); n], vec![F::zero(); n], F::zero()) };
666        for j in 0..n { (s[j], y[j]) = (xn[j] - x[j], gn[j] - g[j]) }
667        let sy = dot(&s, &y);
668        if sy > F::epsilon() * dot(&y, &y) { history.push_back((s, y, F::one() / sy)) }
669        let decrease = fx - fnew;
670        (x, xn, g, gn, fx) = (xn, x, gn, g, fnew);
671        if decrease <= tolerance * (fx.abs() + tolerance) { return Ok(true); }
672    }
673}
674
675/// A step α along `d` from `x`, where the value is `fx` and its slope along `d` is `slope`, that meets the strong Wolfe conditions: it lowers the value by at least 10⁻⁴ α `slope`, and leaves at most 0.9 of the slope's magnitude. Nocedal and Wright's algorithms 3.5 and 3.6 find it, with steps that double and then a zoom by safeguarded quadratic interpolation. The new value is returned, with the point in `xn` and its gradient in `gn`. `None` means no step lowered the value.
676#[allow(clippy::too_many_arguments)]
677fn wolfe<F: Native, O: Objective<F>>(
678    search: &mut Search<'_, F, O>, x: &[F], fx: F, d: &[F], slope: F, xn: &mut [F], gn: &mut [F]) -> Result<Option<F>, Halt<O::Error>> {
679    const STEPS: usize = 30;
680    let (c1, c2, half, tenth) = (F::narrow(1e-4), F::narrow(0.9), F::narrow(0.5), F::narrow(0.1));
681    let probe = |search: &mut Search<'_, F, O>, alpha: F, xn: &mut [F]| {
682        xn.copy_from_slice(x);
683        axpy(xn, alpha, d);
684        search.value(xn)
685    };
686    let sufficient = |alpha: F, value: F| value <= fx + c1 * alpha * slope;
687    let (mut lo, mut flo, mut slo) = (F::zero(), fx, slope);
688    let mut bracket = None;
689    let mut alpha = F::one();
690    for i in 0..STEPS {
691        let fa = probe(search, alpha, xn)?;
692        if !sufficient(alpha, fa) || (i > 0 && fa >= flo) { bracket = Some((alpha, fa)); break; }
693        search.gradient(xn, gn)?;
694        let sa = dot(gn, d);
695        if sa.abs() <= -c2 * slope { return Ok(Some(fa)); }
696        if sa >= F::zero() {
697            bracket = Some((lo, flo));
698            (lo, flo, slo) = (alpha, fa, sa);
699            break;
700        }
701        (lo, flo, slo) = (alpha, fa, sa);
702        alpha = alpha + alpha;
703    }
704    if let Some((mut hi, mut fhi)) = bracket {
705        for _ in 0..STEPS {
706            let width = hi - lo;
707            if width.abs() <= F::epsilon() * lo.abs() { break; }
708            let curve = fhi - flo - slo * width;
709            let guess = if curve > F::zero() { lo - slo * width * width / (curve + curve) } else { lo + half * width };
710            let (p, q) = (lo + tenth * width, hi - tenth * width);
711            let alpha = guess.max(p.min(q)).min(p.max(q));
712            let fa = probe(search, alpha, xn)?;
713            if !sufficient(alpha, fa) || fa >= flo { (hi, fhi) = (alpha, fa); continue; }
714            search.gradient(xn, gn)?;
715            let sa = dot(gn, d);
716            if sa.abs() <= -c2 * slope { return Ok(Some(fa)); }
717            if sa * width >= F::zero() { (hi, fhi) = (lo, flo) }
718            (lo, flo, slo) = (alpha, fa, sa);
719        }
720    }
721    if lo == F::zero() { return Ok(None); }
722    let flo = probe(search, lo, xn)?;
723    search.gradient(xn, gn)?;
724    Ok(Some(flo))
725}
726
727/// Differential evolution within the bounds, then Powell's method from its best point. [`Method::Evolution`] describes it.
728fn evolution<F: Native, O: Objective<F>>(search: &mut Search<'_, F, O>, population: usize, tolerance: F, seed: u64) -> Result<bool, Halt<O::Error>> {
729    const SETTLED: f64 = 0.01;
730    let Some(Bounds { lower, upper }) = search.bounds else { unreachable!() };
731    let n = search.best.len();
732    let mut rng = Key::seeded(seed).stream(0).at(0);
733    let mut uniform = move || F::narrow(unit(rng.next_u64()));
734    let mut members = Vec::with_capacity(population * n);
735    members.extend_from_slice(&search.best);
736    for _ in 1..population { for j in 0..n { members.push(lower[j] + (upper[j] - lower[j]) * uniform()) } }
737    let mut values = vec![F::zero(); population];
738    search.values(&mut members, &mut values)?;
739    let (mut trials, mut tried) = (members.clone(), values.clone());
740    let (half, settled) = (F::narrow(0.5), F::narrow(SETTLED));
741    let pick = |u: F, below: usize| ((u * F::narrow(below as f64)).to_usize().unwrap_or(0)).min(below - 1);
742    loop {
743        let (least, most) = values.iter().fold((F::infinity(), F::neg_infinity()), |(l, m), &v| (l.min(v), m.max(v)));
744        let gathered = (0..n).all(|j| {
745            let (l, m) = (0..population).fold((F::infinity(), F::neg_infinity()), |(l, m), i| (l.min(members[i * n + j]), m.max(members[i * n + j])));
746            m - l <= settled * (upper[j] - lower[j])
747        });
748        if most - least <= settled * least.abs() || gathered { break; }
749        let factor = half + half * uniform();
750        for i in 0..population {
751            let mut chosen = [i; 4];
752            for c in 1..4 { chosen[c] = loop { let r = pick(uniform(), population); if !chosen[..c].contains(&r) { break r; } }; }
753            let [_, p, q, r] = chosen;
754            let crossed = pick(uniform(), n);
755            for j in 0..n {
756                let parent = members[i * n + j];
757                trials[i * n + j] = if j == crossed || uniform() < F::narrow(0.7) {
758                    let v = members[p * n + j] + factor * (members[q * n + j] - members[r * n + j]);
759                    if v < lower[j] { half * (parent + lower[j]) } else if v > upper[j] { half * (parent + upper[j]) } else { v }
760                } else { parent };
761            }
762        }
763        search.values(&mut trials, &mut tried)?;
764        for i in 0..population {
765            if tried[i] <= values[i] {
766                values[i] = tried[i];
767                members[i * n..][..n].copy_from_slice(&trials[i * n..][..n]);
768            }
769        }
770    }
771    powell(search, tolerance)
772}
773
774/// A [`Mapping`] as an [`Objective`] whose value is the norm of the mapping's values, which it keeps in `r`. [`solve`]'s methods run on a [`Search`] of it.
775struct Norm<'m, F, M> { mapping: &'m mut M, r: Vec<F> }
776
777impl<F: Native, M: Mapping<F>> Objective<F> for Norm<'_, F, M> {
778    type Error = M::Error;
779    fn value(&mut self, x: &[F]) -> Result<F, M::Error> { self.mapping.value(x, &mut self.r)?; Ok(norm(&self.r)) }
780}
781
782/// The Euclidean norm of `x`, with no scaling.
783fn norm<F: Native>(x: &[F]) -> F { dot(x, x).sqrt() }
784
785/// The point `x`, moved inside the bounds, and the value there of a mapping of one variable to one value.
786fn at<F: Native, M: Mapping<F>>(search: &mut Search<'_, F, Norm<'_, F, M>>, x: F) -> Result<(F, F), Halt<M::Error>> {
787    let mut p = [x];
788    search.value(&mut p)?;
789    Ok((p[0], search.objective.r[0]))
790}
791
792/// Brent's method for a zero of one equation in one variable, which [`Method::Brent`] describes, from `start`, a point and its value. It stops when the bracket is within `tolerance` of its end nearer zero, relative, or within `tolerance`² near zero. It fails where it finds no change of sign.
793fn root<F: Native, M: Mapping<F>>(search: &mut Search<'_, F, Norm<'_, F, M>>, start: (F, F), tolerance: F) -> Result<bool, Halt<M::Error>> {
794    let (half, two, three, grow) = (F::narrow(0.5), F::narrow(2.0), F::narrow(3.0), F::narrow(1.6));
795    let differ = |fa: F, fb: F| (fa > F::zero()) != (fb > F::zero());
796    let ends = search.bounds.map(|b| (b.lower[0], b.upper[0])).filter(|(l, u)| l.is_finite() && u.is_finite());
797    // `start` holds the caller's evaluation, which `solve` has counted. A point that equals it needs no evaluation of its own.
798    let ((mut a, mut fa), (mut b, mut fb)) = match ends {
799        Some((l, u)) => (if l == start.0 { start } else { at(search, l)? }, if u == start.0 { start } else { at(search, u)? }),
800        None => { let from = if search.best[0] == start.0 { start } else { at(search, search.best[0])? }; (from, from) }
801    };
802    // Whether each end can still step outward.
803    let (mut low, mut high) = (ends.is_none(), ends.is_none());
804    while fa != F::zero() && fb != F::zero() && !differ(fa, fb) {
805        if !low && !high { return Ok(false); }
806        let down = low && (!high || fa.abs() < fb.abs());
807        let width = if b > a { b - a } else { first_step(a) };
808        let end = if down { a } else { b };
809        let mut target = if down { a - grow * width } else { b + grow * width };
810        let (x, fx) = loop {
811            let (x, fx) = at(search, target)?;
812            let inward = end + half * (target - end);
813            if (!fx.is_nan() && x.is_finite()) || inward == end || inward == target { break (x, fx); }
814            target = inward;
815        };
816        let undefined = fx.is_nan() || !x.is_finite();
817        if !undefined { if down { (a, fa) = (x, fx) } else { (b, fb) = (x, fx) } }
818        if undefined || x != target { if down { low = false } else { high = false } }
819    }
820    if fa == F::zero() || fb == F::zero() { return Ok(true); }
821    if fa.abs() < fb.abs() { (a, b, fa, fb) = (b, a, fb, fa) }
822    let (mut c, mut fc) = (a, fa);
823    let (mut d, mut e) = (b - a, b - a);
824    loop {
825        if !differ(fb, fc) { (c, fc, d, e) = (a, fa, b - a, b - a) }
826        if fc.abs() < fb.abs() { (a, b, c, fa, fb, fc) = (b, c, b, fb, fc, fb) }
827        let tol1 = half * tolerance * (b.abs() + tolerance);
828        let m = half * (c - b);
829        if m.abs() <= tol1 || fb == F::zero() { return Ok(true); }
830        if e.abs() >= tol1 && fa.abs() > fb.abs() {
831            let s = fb / fa;
832            let (mut p, mut q) = if a == c { (two * m * s, F::one() - s) } else {
833                let (q, r) = (fa / fc, fb / fc);
834                (s * (two * m * q * (q - r) - (b - a) * (r - F::one())), (q - F::one()) * (r - F::one()) * (s - F::one()))
835            };
836            if p > F::zero() { q = -q }
837            else { p = -p }
838            if two * p < (three * m * q - (tol1 * q).abs()).min((e * q).abs()) { (e, d) = (d, p / q) } else { (d, e) = (m, m) }
839        } else { (d, e) = (m, m); }
840        (a, fa) = (b, fb);
841        b = b + if d.abs() > tol1 { d } else { tol1.copysign(m) };
842        fb = at(search, b)?.1;
843    }
844}
845
846/// Levenberg–Marquardt from the start, whose values `solve` has recorded, which [`Method::LevenbergMarquardt`] describes.
847fn levenberg_marquardt<F: Native + Factor, M: Mapping<F>>(search: &mut Search<'_, F, Norm<'_, F, M>>, tolerance: F) -> Result<bool, Halt<M::Error>> {
848    let (n, m) = (search.best.len(), search.objective.r.len());
849    let (one, two) = (F::one(), F::narrow(2.0));
850    let mut x = search.best.clone();
851    let mut fx = search.least;
852    let mut r = search.objective.r.clone();
853    // The Jacobian, and the damped problem [J; √λ D] δ = [-r; 0] that each step solves.
854    let (mut j, mut damped, mut target) = (vec![F::zero(); m * n], vec![F::zero(); (m + n) * n], vec![F::zero(); m + n]);
855    let (mut scale, mut trial, mut fit) = (vec![F::zero(); n], vec![F::zero(); n], vec![F::zero(); m]);
856    // D holds each column's largest norm so far, so λ is relative to JᵀJ's diagonal and starts at Marquardt's 10⁻³.
857    let (mut damping, mut growth) = (F::narrow(1e-3), two);
858    loop {
859        if fx == F::zero() { return Ok(true); }
860        jacobian(search, &x, &r, &mut j)?;
861        let mut cosine = F::zero();
862        for c in 0..n {
863            let (mut length, mut product) = (F::zero(), F::zero());
864            for i in 0..m { (length, product) = (length + j[i * n + c] * j[i * n + c], product + j[i * n + c] * r[i]) }
865            let length = length.sqrt();
866            // Infinite residuals, or a Jacobian with NaN or infinite items, leave no step to take and no gradient to test.
867            if !(length.is_finite() && product.is_finite()) { return Ok(false); }
868            if length > F::zero() { cosine = cosine.max(product.abs() / (length * fx)) }
869            scale[c] = scale[c].max(length);
870        }
871        if cosine <= tolerance { return Ok(true); }
872        loop {
873            damped[..m * n].copy_from_slice(&j);
874            damped[m * n..].fill(F::zero());
875            for c in 0..n { damped[(m + c) * n + c] = damping.sqrt() * scale[c] }
876            for i in 0..m { target[i] = -r[i] }
877            target[m..].fill(F::zero());
878            let (step, _) = lstsq(&damped, &target, [m + n, n], 1);
879            for c in 0..n { trial[c] = x[c] + step[c] }
880            let ft = search.value(&mut trial)?;
881            let small = norm(&step) <= tolerance * (norm(&x) + tolerance);
882            if ft < fx {
883                for i in 0..m { fit[i] = r[i] + dot(&j[i * n..][..n], &step) }
884                let predicted = fx * fx - dot(&fit, &fit);
885                let ratio = if predicted > F::zero() { (fx * fx - ft * ft) / predicted } else { one };
886                let swing = two * ratio - one;
887                damping = damping * F::narrow(1.0 / 3.0).max(one - swing * swing * swing);
888                growth = two;
889                x.copy_from_slice(&trial);
890                r.copy_from_slice(&search.objective.r);
891                fx = ft;
892                if small { return Ok(true); }
893                break;
894            }
895            if small { return Ok(true); }
896            damping = damping * growth;
897            growth = growth * two;
898        }
899    }
900}
901
902/// The Jacobian at `x`, where the values are `r`, into `j`: the mapping's own, or forward differences, which take one evaluation for each variable.
903fn jacobian<F: Native, M: Mapping<F>>(search: &mut Search<'_, F, Norm<'_, F, M>>, x: &[F], r: &[F], j: &mut [F]) -> Result<(), Halt<M::Error>> {
904    if search.objective.mapping.jacobian(x, j).map_err(Halt::Error)? { return Ok(()); }
905    let n = x.len();
906    let mut shifted = x.to_vec();
907    for c in 0..n {
908        shifted[c] = x[c] + F::epsilon().sqrt() * x[c].abs().max(F::one());
909        let h = shifted[c] - x[c];
910        search.value(&mut shifted)?;
911        for (i, (&after, &before)) in search.objective.r.iter().zip(r).enumerate() { j[i * n + c] = (after - before) / h }
912        shifted[c] = x[c];
913    }
914    Ok(())
915}
916
917/// The products A v of an affine `f`, f(x) = A x − b, from `f`'s values, as [`solve_affine`] computes them. It passes on `f`'s vector–Jacobian products and preconditioner.
918struct Affine<'m, F, M> {
919    f: &'m mut M,
920    /// f(0), which is −b.
921    f0: Vec<F>,
922    /// The norm of b.
923    size: F,
924    /// |A v| ÷ |v| at the last product, which gives the next t.
925    ratio: Option<F>,
926    /// t v, the point at which `f` is evaluated.
927    scaled: Vec<F>,
928}
929
930impl<F: Native, M: Mapping<F>> Affine<'_, F, M> {
931    /// The power of two t that makes t |A v| about 16 |b|, for a `v` of norm `nv` where |A v| ÷ |v| is `ratio`, or 1 where b is zero.
932    fn scale(&self, nv: F, ratio: F) -> F {
933        if self.size == F::zero() { return F::one(); }
934        let t = (F::narrow(16.0) * self.size / (ratio * nv)).log2().round().exp2();
935        if t.is_finite() && t > F::zero() { t } else { F::one() }
936    }
937
938    /// (f(t v) − f(0)) ÷ t, into `out`.
939    fn at(&mut self, v: &[F], t: F, out: &mut [F]) -> Result<(), M::Error> {
940        self.scaled.clear();
941        self.scaled.extend(v.iter().map(|&x| x * t));
942        self.f.value(&self.scaled, out)?;
943        for (o, &z) in out.iter_mut().zip(&self.f0) { *o = (*o - z) / t }
944        Ok(())
945    }
946}
947
948impl<F: Native, M: Mapping<F>> Mapping<F> for Affine<'_, F, M> {
949    type Error = M::Error;
950    /// A v, computed again with a corrected t where the t from the last product's ratio made t |A v| smaller than |b|.
951    fn value(&mut self, v: &[F], out: &mut [F]) -> Result<(), M::Error> {
952        let nv = norm(v);
953        if nv == F::zero() { out.fill(F::zero()); return Ok(()); }
954        let t = self.scale(nv, self.ratio.unwrap_or(F::one()));
955        self.at(v, t, out)?;
956        let mut av = norm(out);
957        if av > F::zero() && t * av < self.size {
958            self.at(v, self.scale(nv, av / nv), out)?;
959            av = norm(out);
960        }
961        if av > F::zero() { self.ratio = Some(av / nv) }
962        Ok(())
963    }
964    fn has_vjp(&self) -> bool { self.f.has_vjp() }
965    fn vjp(&mut self, u: &[F], x: &[F], out: &mut [F]) -> Result<(), M::Error> { self.f.vjp(u, x, out) }
966    fn has_preconditioner(&self) -> bool { self.f.has_preconditioner() }
967    fn precondition(&mut self, v: &[F], out: &mut [F]) -> Result<(), M::Error> { self.f.precondition(v, out) }
968}
969
970/// The operator a Krylov method runs on, the matrix or AᵀA for least squares, with each product counted against the limit.
971struct Products<'a, F, M> {
972    a: &'a mut M,
973    /// The point at which `vjp` gives the transpose's products.
974    at: &'a [F],
975    normal: bool,
976    /// The matrix's product inside a product with AᵀA.
977    inner: Vec<F>,
978    products: usize,
979    limit: usize,
980}
981
982impl<F: Native, M: Mapping<F>> Products<'_, F, M> {
983    /// Counts `k` products against the limit.
984    fn count(&mut self, k: usize) -> Result<(), Halt<M::Error>> {
985        if self.products + k > self.limit { return Err(Halt::Spent); }
986        self.products += k;
987        Ok(())
988    }
989
990    /// The operator's product with `v`, into `out`.
991    fn apply(&mut self, v: &[F], out: &mut [F]) -> Result<(), Halt<M::Error>> {
992        if !self.normal { self.count(1)?; return self.a.value(v, out).map_err(Halt::Error); }
993        self.count(2)?;
994        self.a.value(v, &mut self.inner).map_err(Halt::Error)?;
995        self.a.vjp(&self.inner, self.at, out).map_err(Halt::Error)
996    }
997
998    /// M·v, the preconditioner's product with `v`, written into `out`, or `v` itself where the mapping has no preconditioner. It counts no product.
999    fn precondition<'v>(&mut self, v: &'v [F], out: &'v mut [F]) -> Result<&'v [F], Halt<M::Error>> {
1000        if !self.a.has_preconditioner() { return Ok(v); }
1001        self.a.precondition(v, out).map_err(Halt::Error)?;
1002        Ok(out)
1003    }
1004
1005    /// The right-hand side of the system the method runs on: `b`, or Aᵀb for least squares.
1006    fn rhs(&mut self, b: &[F]) -> Result<Vec<F>, Halt<M::Error>> {
1007        if !self.normal { return Ok(b.to_vec()); }
1008        self.count(1)?;
1009        let mut v = vec![F::zero(); self.at.len()];
1010        self.a.vjp(b, self.at, &mut v).map_err(Halt::Error)?;
1011        Ok(v)
1012    }
1013
1014    /// `rhs` minus the operator's product with `x`, into `r`.
1015    fn residual(&mut self, rhs: &[F], x: &[F], r: &mut [F]) -> Result<(), Halt<M::Error>> {
1016        self.apply(x, r)?;
1017        for (r, &b) in r.iter_mut().zip(rhs) { *r = b - *r }
1018        Ok(())
1019    }
1020}
1021
1022/// Conjugate gradients on `op` from `x`, with `op`'s preconditioner. It stops when the residual's norm falls to `tolerance` times `rhs`'s, or where the operator or the preconditioner turns out not to be positive definite.
1023fn cg<F: Native, M: Mapping<F>>(op: &mut Products<'_, F, M>, rhs: &[F], x: &mut [F], tolerance: F) -> Result<bool, Halt<M::Error>> {
1024    let (n, zero, goal) = (x.len(), F::zero(), tolerance * norm(rhs));
1025    let (mut r, mut z, mut q) = (vec![zero; n], vec![zero; n], vec![zero; n]);
1026    op.residual(rhs, x, &mut r)?;
1027    let mut p = op.precondition(&r, &mut z)?.to_vec();
1028    let mut rz = dot(&r, &p);
1029    loop {
1030        let size = norm(&r);
1031        if !(size > goal) { return Ok(size <= goal); }
1032        if !(rz > zero) { return Ok(false); }
1033        op.apply(&p, &mut q)?;
1034        let pq = dot(&p, &q);
1035        if !(pq > zero) { return Ok(false); }
1036        let alpha = rz / pq;
1037        axpy(x, alpha, &p);
1038        axpy(&mut r, -alpha, &q);
1039        let mz = op.precondition(&r, &mut z)?;
1040        let next = dot(&r, mz);
1041        let beta = next / rz;
1042        rz = next;
1043        for i in 0..n { p[i] = mz[i] + beta * p[i] }
1044    }
1045}
1046
1047/// MINRES on the symmetric `op` from `x`, with `op`'s preconditioner, as Paige and Saunders give it: the Lanczos process, with plane rotations that track the residual's norm. That norm is the preconditioner's, √(rᵀMr), which is the residual's own without one. It stops when that norm falls to `tolerance` times `rhs`'s in the same norm.
1048fn minres<F: Native, M: Mapping<F>>(op: &mut Products<'_, F, M>, rhs: &[F], x: &mut [F], tolerance: F) -> Result<bool, Halt<M::Error>> {
1049    let (n, zero) = (x.len(), F::zero());
1050    let (mut r1, mut y, mut v) = (vec![zero; n], vec![zero; n], vec![zero; n]);
1051    let goal = tolerance * dot(rhs, op.precondition(rhs, &mut v)?).sqrt();
1052    op.residual(rhs, x, &mut r1)?;
1053    let my = op.precondition(&r1, &mut y)?;
1054    let mut beta = dot(&r1, my).sqrt();
1055    if !(beta > goal) { return Ok(beta <= goal); }
1056    for i in 0..n { v[i] = my[i] / beta }
1057    let mut r2 = r1.clone();
1058    let (mut w, mut w1, mut w2) = (vec![zero; n], vec![zero; n], vec![zero; n]);
1059    let (mut oldb, mut dbar, mut epsln, mut phibar, mut cs, mut sn) = (zero, zero, zero, beta, -F::one(), zero);
1060    loop {
1061        op.apply(&v, &mut y)?;
1062        if oldb > zero { axpy(&mut y, -beta / oldb, &r1) }
1063        let alfa = dot(&v, &y);
1064        axpy(&mut y, -alfa / beta, &r2);
1065        std::mem::swap(&mut r1, &mut r2);
1066        r2.copy_from_slice(&y);
1067        let my = op.precondition(&r2, &mut y)?;
1068        (oldb, beta) = (beta, dot(&r2, my).sqrt());
1069        let oldeps = epsln;
1070        let delta = cs * dbar + sn * alfa;
1071        let gbar = sn * dbar - cs * alfa;
1072        (epsln, dbar) = (sn * beta, -cs * beta);
1073        let (c, s, gamma) = givens(gbar, beta);
1074        if gamma == zero { return Ok(false); }
1075        (cs, sn) = (c, s);
1076        let phi = cs * phibar;
1077        phibar = sn * phibar;
1078        std::mem::swap(&mut w1, &mut w2);
1079        std::mem::swap(&mut w2, &mut w);
1080        for i in 0..n { w[i] = (v[i] - oldeps * w1[i] - delta * w2[i]) / gamma }
1081        axpy(x, phi, &w);
1082        if !(phibar > goal) { return Ok(phibar <= goal); }
1083        for i in 0..n { v[i] = my[i] / beta }
1084    }
1085}
1086
1087/// The most steps GMRES takes between restarts. A system with fewer unknowns restarts after one step for each unknown.
1088pub const GMRES_RESTART: usize = 20;
1089
1090/// GMRES on `op` from `x`, restarted every [`GMRES_RESTART`] steps, with `op`'s preconditioner on the right: it runs on the operator times the preconditioner, which leaves the residual the operator's own. That's the Arnoldi process with modified Gram–Schmidt, and plane rotations that turn its Hessenberg matrix into a triangle and track the residual's norm. It stops when that norm falls to `tolerance` times `rhs`'s, or where the Krylov space stops growing.
1091fn gmres<F: Native, M: Mapping<F>>(op: &mut Products<'_, F, M>, rhs: &[F], x: &mut [F], tolerance: F) -> Result<bool, Halt<M::Error>> {
1092    let (n, zero, goal) = (x.len(), F::zero(), tolerance * norm(rhs));
1093    let k = GMRES_RESTART.min(n);
1094    let (mut basis, mut h) = (vec![zero; (k + 1) * n], vec![zero; k * k]);
1095    let (mut turns, mut g, mut y) = (vec![(zero, zero); k], vec![zero; k + 1], vec![zero; k]);
1096    let (mut w, mut z) = (vec![zero; n], vec![zero; n]);
1097    loop {
1098        op.residual(rhs, x, &mut basis[..n])?;
1099        let beta = norm(&basis[..n]);
1100        if !(beta > goal) { return Ok(beta <= goal); }
1101        for v in &mut basis[..n] { *v = *v / beta }
1102        g.fill(zero);
1103        g[0] = beta;
1104        let (mut steps, mut ended) = (0, false);
1105        while steps < k && !ended {
1106            let j = steps;
1107            let mv = op.precondition(&basis[j * n..][..n], &mut z)?;
1108            op.apply(mv, &mut w)?;
1109            for i in 0..=j {
1110                let hij = dot(&w, &basis[i * n..][..n]);
1111                h[i * k + j] = hij;
1112                axpy(&mut w, -hij, &basis[i * n..][..n]);
1113            }
1114            let next = norm(&w);
1115            for (i, &(c, s)) in turns[..j].iter().enumerate() {
1116                let (p, q) = (h[i * k + j], h[(i + 1) * k + j]);
1117                (h[i * k + j], h[(i + 1) * k + j]) = (c * p + s * q, c * q - s * p);
1118            }
1119            let (c, s, r) = givens(h[j * k + j], next);
1120            if r == zero { break; }
1121            (turns[j], h[j * k + j]) = ((c, s), r);
1122            (g[j], g[j + 1]) = (c * g[j], -s * g[j]);
1123            steps += 1;
1124            ended = !(g[steps].abs() > goal) || next == zero;
1125            if !ended { for i in 0..n { basis[steps * n + i] = w[i] / next } }
1126        }
1127        for i in (0..steps).rev() {
1128            let t = (i + 1..steps).fold(g[i], |t, l| t - h[i * k + l] * y[l]);
1129            y[i] = t / h[i * k + i];
1130        }
1131        z.fill(zero);
1132        for i in 0..steps { axpy(&mut z, y[i], &basis[i * n..][..n]) }
1133        let step = op.precondition(&z, &mut w)?;
1134        axpy(x, F::one(), step);
1135        if ended || steps < k { return Ok(g[steps].abs() <= goal); }
1136    }
1137}