5#ifndef LINE_UTIL_LEVMAR_H
6#define LINE_UTIL_LEVMAR_H
102namespace levmardetail {
105T sum_squares(
const std::vector<T>& r) {
107 for (std::size_t i = 0; i < r.size(); ++i) s += r[i] * r[i];
112T norm2(
const std::vector<T>& v) {
114 return sqrt(sum_squares(v));
132template <
class T,
class F>
135 "levmar requires transcendental arithmetic (it stops on tolerances)");
136 const std::size_t n = x.size();
140 std::vector<T> xp = x;
141 for (std::size_t j = 0; j < n; ++j) {
143 const T scale = ax > one ? ax : one;
144 const T h = diff_step * scale;
146 const std::vector<T> rp = f(xp);
148 const std::vector<T> rm = f(xp);
150 if (rp.size() != m || rm.size() != m)
151 throw InputError(
"levmar_jacobian_fd: residual length changed between evaluations");
152 const T den = two * h;
153 for (std::size_t i = 0; i < m; ++i) J(i, j) = (rp[i] - rm[i]) / den;
167template <
class T,
class F,
class J>
171 "levmar requires transcendental arithmetic (it stops on tolerances)");
173 const std::size_t n = x0.size();
174 if (n == 0)
throw InputError(
"levmar: no variables");
175 if (m == 0)
throw InputError(
"levmar: no residuals");
180 if (res.
residual.size() != m)
throw InputError(
"levmar: residual length disagrees with m");
186 T lambda =
opt.lambda0;
188 unsigned tol_hits = 0;
190 for (
unsigned it = 0; it <
opt.max_iter; ++it) {
193 if (Jm.
rows() != m || Jm.
cols() != n)
throw InputError(
"levmar: Jacobian has wrong shape");
196 std::vector<T> g(n, zero);
197 for (std::size_t j = 0; j < n; ++j) {
199 for (std::size_t i = 0; i < m; ++i) s += Jm(i, j) * res.
residual[i];
203 for (std::size_t j = 0; j < n; ++j) {
205 if (a > gmax) gmax = a;
207 if (gmax <=
opt.gtol) {
213 for (std::size_t j = 0; j < n; ++j)
214 for (std::size_t k = j; k < n; ++k) {
216 for (std::size_t i = 0; i < m; ++i) s += Jm(i, j) * Jm(i, k);
223 for (std::size_t j = 0; j < n; ++j)
224 if (A(j, j) > dmax) dmax = A(j, j);
227 bool accepted =
false;
228 while (!accepted && lambda <=
opt.lambda_max) {
230 for (std::size_t j = 0; j < n; ++j) Aug(j, j) = A(j, j) + lambda * dmax;
231 std::vector<T> rhs(n);
232 for (std::size_t j = 0; j < n; ++j) rhs[j] = -g[j];
237 dx =
solve(Aug, rhs);
242 lambda *=
opt.lambda_increase;
246 std::vector<T> xn(n);
247 for (std::size_t j = 0; j < n; ++j) xn[j] = res.
x[j] + dx[j];
248 const std::vector<T> rn = f(xn);
250 if (rn.size() != m)
throw InputError(
"levmar: residual length changed between calls");
251 const T
sn = levmardetail::sum_squares(rn);
254 const T dnorm = levmardetail::norm2(dx);
255 const T xnorm = levmardetail::norm2(res.
x);
256 const T dssq = res.
ssq -
sn;
257 const bool ftol_met = dssq <=
opt.ftol * res.
ssq;
258 const bool xtol_met = dnorm <=
opt.xtol * (xnorm +
opt.xtol);
262 lambda *=
opt.lambda_decrease;
264 if (ftol_met || xtol_met) {
274 lambda *=
opt.lambda_increase;
284 if (res.
ssq == zero) {
300template <
class T,
class F>
303 const T step =
opt.diff_step;
306 std::shared_ptr<unsigned> extra = std::make_shared<unsigned>(0u);
309 [f, m, step, extra](
const std::vector<T>& x) {
310 *extra += 2u *
static_cast<unsigned>(x.size());
319template <
class T,
class F>
The algorithm cannot proceed on this instance (singular matrix, ...).
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
Matrix< T > levmar_jacobian_fd(F f, const std::vector< T > &x, std::size_t m, const T &diff_step)
Central-difference Jacobian of r at x.
LevmarResult< T > levmar_jac(F f, J jac, const std::vector< T > &x0, std::size_t m, const LevmarOptions< T > &opt)
Levenberg-Marquardt with a caller-supplied Jacobian.
LevmarResult< T > levmar(F f, const std::vector< T > &x0, std::size_t m, const LevmarOptions< T > &opt)
Levenberg-Marquardt with a central-difference Jacobian.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
LevmarOptions< T > levmar_defaults()
MINPACK-like defaults, with a central-difference step of eps^(1/3).
Number-type abstraction for the templated API port.
Tuning of the Levenberg-Marquardt iteration.
T xtol
stop when the relative step length falls below this
T ftol
stop when the relative decrease of S falls below this
T lambda_max
give up on the iteration once lambda exceeds this
unsigned max_iter
cap on accepted-or-rejected outer iterations
T diff_step
relative step of the central-difference Jacobian
T gtol
stop when max|J^T r| falls below this
T lambda_increase
factor applied to lambda after a rejected step
T lambda_decrease
factor applied to lambda after an accepted step
Outcome of a least-squares solve.
std::vector< T > x
best point found
T ssq
sum of squares at x, the objective value
std::vector< T > residual
r(x)
bool converged
a tolerance was met before the caps
unsigned iterations
outer iterations performed
unsigned evaluations
residual evaluations, differencing included