14#ifndef EIGEN_HYBRIDNONLINEARSOLVER_H
15#define EIGEN_HYBRIDNONLINEARSOLVER_H
18#include "./InternalHeaderCheck.h"
22namespace HybridNonLinearSolverSpace {
25 ImproperInputParameters = 0,
26 RelativeErrorTooSmall = 1,
27 TooManyFunctionEvaluation = 2,
29 NotMakingProgressJacobian = 4,
30 NotMakingProgressIterations = 5,
46template <
typename FunctorType,
typename Scalar =
double>
47class HybridNonLinearSolver {
49 typedef DenseIndex Index;
51 HybridNonLinearSolver(FunctorType &_functor) : functor(_functor) {
52 nfev = njev = iter = 0;
54 useExternalScaling =
false;
59 : factor(Scalar(100.)),
62 nb_of_subdiagonals(-1),
63 nb_of_superdiagonals(-1),
68 Index nb_of_subdiagonals;
69 Index nb_of_superdiagonals;
77 HybridNonLinearSolverSpace::Status hybrj1(FVectorType &x,
80 HybridNonLinearSolverSpace::Status solveInit(FVectorType &x);
81 HybridNonLinearSolverSpace::Status solveOneStep(FVectorType &x);
82 HybridNonLinearSolverSpace::Status solve(FVectorType &x);
84 HybridNonLinearSolverSpace::Status hybrd1(FVectorType &x,
87 HybridNonLinearSolverSpace::Status solveNumericalDiffInit(FVectorType &x);
88 HybridNonLinearSolverSpace::Status solveNumericalDiffOneStep(FVectorType &x);
89 HybridNonLinearSolverSpace::Status solveNumericalDiff(FVectorType &x);
91 void resetParameters(
void) { parameters = Parameters(); }
92 Parameters parameters;
93 FVectorType fvec, qtf, diag;
95 UpperTriangularType R;
100 bool useExternalScaling;
103 FunctorType &functor;
111 Scalar pnorm, xnorm, fnorm1;
112 Index nslow1, nslow2;
114 Scalar actred, prered;
115 FVectorType wa1, wa2, wa3, wa4;
117 HybridNonLinearSolver &operator=(
const HybridNonLinearSolver &) =
delete;
120template <
typename FunctorType,
typename Scalar>
121HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::hybrj1(FVectorType &x,
126 if (n <= 0 || tol < 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
129 parameters.maxfev = 100 * (n + 1);
130 parameters.xtol = tol;
131 diag.setConstant(n, 1.);
132 useExternalScaling =
true;
136template <
typename FunctorType,
typename Scalar>
137HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveInit(FVectorType &x) {
147 if (!useExternalScaling) diag.resize(n);
148 eigen_assert((!useExternalScaling || diag.size() == n) &&
149 "When useExternalScaling is set, the caller must provide a valid 'diag'");
156 if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.factor <= 0.)
157 return HybridNonLinearSolverSpace::ImproperInputParameters;
158 if (useExternalScaling)
159 for (Index j = 0; j < n; ++j)
160 if (diag[j] <= 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
165 if (functor(x, fvec) < 0)
return HybridNonLinearSolverSpace::UserAsked;
166 fnorm = fvec.stableNorm();
175 return HybridNonLinearSolverSpace::Running;
178template <
typename FunctorType,
typename Scalar>
179HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveOneStep(FVectorType &x) {
182 eigen_assert(x.size() == n);
185 std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
190 if (functor.df(x, fjac) < 0)
return HybridNonLinearSolverSpace::UserAsked;
193 wa2 = fjac.colwise().blueNorm();
198 if (!useExternalScaling)
199 for (j = 0; j < n; ++j) diag[j] = (wa2[j] == 0.) ? 1. : wa2[j];
203 xnorm = diag.cwiseProduct(x).stableNorm();
204 delta = parameters.factor * xnorm;
205 if (delta == 0.) delta = parameters.factor;
212 R = qrfac.matrixQR();
215 fjac = qrfac.householderQ();
218 qtf.noalias() = fjac.transpose() * fvec;
221 if (!useExternalScaling) diag = diag.cwiseMax(wa2);
225 internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
230 pnorm = diag.cwiseProduct(wa1).stableNorm();
233 if (iter == 1) delta = (std::min)(delta, pnorm);
236 if (functor(wa2, wa4) < 0)
return HybridNonLinearSolverSpace::UserAsked;
238 fnorm1 = wa4.stableNorm();
243 actred = 1. - numext::abs2(fnorm1 / fnorm);
246 wa3.noalias() = R.template triangularView<Upper>() * wa1 + qtf;
247 temp = wa3.stableNorm();
250 prered = 1. - numext::abs2(temp / fnorm);
254 if (prered > 0.) ratio = actred / prered;
257 if (ratio < Scalar(.1)) {
260 delta = Scalar(.5) * delta;
264 if (ratio >= Scalar(.5) || ncsuc > 1) delta = (std::max)(delta, pnorm / Scalar(.5));
265 if (abs(ratio - 1.) <= Scalar(.1)) {
266 delta = pnorm / Scalar(.5);
271 if (ratio >= Scalar(1e-4)) {
274 wa2 = diag.cwiseProduct(x);
276 xnorm = wa2.stableNorm();
283 if (actred >= Scalar(.001)) nslow1 = 0;
285 if (actred >= Scalar(.1)) nslow2 = 0;
288 if (delta <= parameters.xtol * xnorm || fnorm == 0.)
return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
291 if (nfev >= parameters.maxfev)
return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
293 return HybridNonLinearSolverSpace::TolTooSmall;
294 if (nslow2 == 5)
return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
295 if (nslow1 == 10)
return HybridNonLinearSolverSpace::NotMakingProgressIterations;
298 if (ncfail == 2)
break;
302 wa1 = diag.cwiseProduct(diag.cwiseProduct(wa1) / pnorm);
303 wa2.noalias() = fjac.transpose() * wa4;
304 if (ratio >= Scalar(1e-4)) qtf = wa2;
305 wa2 = (wa2 - wa3) / pnorm;
308 internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
309 internal::r1mpyq<Scalar>(fjac, v_givens, w_givens);
311 internal::r1mpyq<Scalar>(qtf_row, v_givens, w_givens);
315 return HybridNonLinearSolverSpace::Running;
318template <
typename FunctorType,
typename Scalar>
319HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solve(FVectorType &x) {
320 HybridNonLinearSolverSpace::Status status = solveInit(x);
321 if (status == HybridNonLinearSolverSpace::ImproperInputParameters)
return status;
322 while (status == HybridNonLinearSolverSpace::Running) status = solveOneStep(x);
326template <
typename FunctorType,
typename Scalar>
327HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::hybrd1(FVectorType &x,
332 if (n <= 0 || tol < 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
335 parameters.maxfev = 200 * (n + 1);
336 parameters.xtol = tol;
338 diag.setConstant(n, 1.);
339 useExternalScaling =
true;
340 return solveNumericalDiff(x);
343template <
typename FunctorType,
typename Scalar>
344HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiffInit(FVectorType &x) {
347 if (parameters.nb_of_subdiagonals < 0) parameters.nb_of_subdiagonals = n - 1;
348 if (parameters.nb_of_superdiagonals < 0) parameters.nb_of_superdiagonals = n - 1;
357 if (!useExternalScaling) diag.resize(n);
358 eigen_assert((!useExternalScaling || diag.size() == n) &&
359 "When useExternalScaling is set, the caller must provide a valid 'diag'");
366 if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.nb_of_subdiagonals < 0 ||
367 parameters.nb_of_superdiagonals < 0 || parameters.factor <= 0.)
368 return HybridNonLinearSolverSpace::ImproperInputParameters;
369 if (useExternalScaling)
370 for (Index j = 0; j < n; ++j)
371 if (diag[j] <= 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
376 if (functor(x, fvec) < 0)
return HybridNonLinearSolverSpace::UserAsked;
377 fnorm = fvec.stableNorm();
386 return HybridNonLinearSolverSpace::Running;
389template <
typename FunctorType,
typename Scalar>
390HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiffOneStep(
395 eigen_assert(x.size() == n);
398 std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
401 if (parameters.nb_of_subdiagonals < 0) parameters.nb_of_subdiagonals = n - 1;
402 if (parameters.nb_of_superdiagonals < 0) parameters.nb_of_superdiagonals = n - 1;
405 if (internal::fdjac1(functor, x, fvec, fjac, parameters.nb_of_subdiagonals, parameters.nb_of_superdiagonals,
406 parameters.epsfcn) < 0)
407 return HybridNonLinearSolverSpace::UserAsked;
408 nfev += (std::min)(parameters.nb_of_subdiagonals + parameters.nb_of_superdiagonals + 1, n);
410 wa2 = fjac.colwise().blueNorm();
415 if (!useExternalScaling)
416 for (j = 0; j < n; ++j) diag[j] = (wa2[j] == 0.) ? 1. : wa2[j];
420 xnorm = diag.cwiseProduct(x).stableNorm();
421 delta = parameters.factor * xnorm;
422 if (delta == 0.) delta = parameters.factor;
429 R = qrfac.matrixQR();
432 fjac = qrfac.householderQ();
435 qtf.noalias() = fjac.transpose() * fvec;
438 if (!useExternalScaling) diag = diag.cwiseMax(wa2);
442 internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
447 pnorm = diag.cwiseProduct(wa1).stableNorm();
450 if (iter == 1) delta = (std::min)(delta, pnorm);
453 if (functor(wa2, wa4) < 0)
return HybridNonLinearSolverSpace::UserAsked;
455 fnorm1 = wa4.stableNorm();
460 actred = 1. - numext::abs2(fnorm1 / fnorm);
463 wa3.noalias() = R.template triangularView<Upper>() * wa1 + qtf;
464 temp = wa3.stableNorm();
467 prered = 1. - numext::abs2(temp / fnorm);
471 if (prered > 0.) ratio = actred / prered;
474 if (ratio < Scalar(.1)) {
477 delta = Scalar(.5) * delta;
481 if (ratio >= Scalar(.5) || ncsuc > 1) delta = (std::max)(delta, pnorm / Scalar(.5));
482 if (abs(ratio - 1.) <= Scalar(.1)) {
483 delta = pnorm / Scalar(.5);
488 if (ratio >= Scalar(1e-4)) {
491 wa2 = diag.cwiseProduct(x);
493 xnorm = wa2.stableNorm();
500 if (actred >= Scalar(.001)) nslow1 = 0;
502 if (actred >= Scalar(.1)) nslow2 = 0;
505 if (delta <= parameters.xtol * xnorm || fnorm == 0.)
return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
508 if (nfev >= parameters.maxfev)
return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
510 return HybridNonLinearSolverSpace::TolTooSmall;
511 if (nslow2 == 5)
return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
512 if (nslow1 == 10)
return HybridNonLinearSolverSpace::NotMakingProgressIterations;
515 if (ncfail == 2)
break;
519 wa1 = diag.cwiseProduct(diag.cwiseProduct(wa1) / pnorm);
520 wa2.noalias() = fjac.transpose() * wa4;
521 if (ratio >= Scalar(1e-4)) qtf = wa2;
522 wa2 = (wa2 - wa3) / pnorm;
525 internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
526 internal::r1mpyq<Scalar>(fjac, v_givens, w_givens);
528 internal::r1mpyq<Scalar>(qtf_row, v_givens, w_givens);
532 return HybridNonLinearSolverSpace::Running;
535template <
typename FunctorType,
typename Scalar>
536HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiff(FVectorType &x) {
537 HybridNonLinearSolverSpace::Status status = solveNumericalDiffInit(x);
538 if (status == HybridNonLinearSolverSpace::ImproperInputParameters)
return status;
539 while (status == HybridNonLinearSolverSpace::Running) status = solveNumericalDiffOneStep(x);
Namespace containing all symbols from the Eigen library.