13#ifndef EIGEN_HYBRIDNONLINEARSOLVER_H
14#define EIGEN_HYBRIDNONLINEARSOLVER_H
17#include "./InternalHeaderCheck.h"
21namespace HybridNonLinearSolverSpace {
24 ImproperInputParameters = 0,
25 RelativeErrorTooSmall = 1,
26 TooManyFunctionEvaluation = 2,
28 NotMakingProgressJacobian = 4,
29 NotMakingProgressIterations = 5,
45template <
typename FunctorType,
typename Scalar =
double>
46class HybridNonLinearSolver {
48 typedef DenseIndex Index;
50 HybridNonLinearSolver(FunctorType &_functor) : functor(_functor) {
51 nfev = njev = iter = 0;
53 useExternalScaling =
false;
61 nb_of_subdiagonals(-1),
62 nb_of_superdiagonals(-1),
67 Index nb_of_subdiagonals;
68 Index nb_of_superdiagonals;
76 HybridNonLinearSolverSpace::Status hybrj1(FVectorType &x,
79 HybridNonLinearSolverSpace::Status solveInit(FVectorType &x);
80 HybridNonLinearSolverSpace::Status solveOneStep(FVectorType &x);
81 HybridNonLinearSolverSpace::Status solve(FVectorType &x);
83 HybridNonLinearSolverSpace::Status hybrd1(FVectorType &x,
86 HybridNonLinearSolverSpace::Status solveNumericalDiffInit(FVectorType &x);
87 HybridNonLinearSolverSpace::Status solveNumericalDiffOneStep(FVectorType &x);
88 HybridNonLinearSolverSpace::Status solveNumericalDiff(FVectorType &x);
90 void resetParameters(
void) { parameters = Parameters(); }
91 Parameters parameters;
92 FVectorType fvec, qtf, diag;
94 UpperTriangularType R;
99 bool useExternalScaling;
102 FunctorType &functor;
111 Scalar pnorm, xnorm, fnorm1;
112 Index nslow1, nslow2;
115 FVectorType wa1, wa2, wa3, wa4;
117 HybridNonLinearSolver &operator=(
const HybridNonLinearSolver &);
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 = 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 = R.template triangularView<Upper>() * wa1 + qtf;
247 temp = wa3.stableNorm();
250 prered = 1. - numext::abs2(temp / fnorm);
254 if (prered > 0.) ratio = actred / prered;
260 delta =
Scalar(.5) * delta;
264 if (ratio >=
Scalar(.5) || ncsuc > 1) delta = (std::max)(delta, pnorm /
Scalar(.5));
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 = 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>(n, n, fjac.data(), v_givens, w_givens);
310 internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens);
314 return HybridNonLinearSolverSpace::Running;
317template <
typename FunctorType,
typename Scalar>
318HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solve(FVectorType &x) {
319 HybridNonLinearSolverSpace::Status status = solveInit(x);
320 if (status == HybridNonLinearSolverSpace::ImproperInputParameters)
return status;
321 while (status == HybridNonLinearSolverSpace::Running) status = solveOneStep(x);
325template <
typename FunctorType,
typename Scalar>
326HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::hybrd1(FVectorType &x,
331 if (n <= 0 || tol < 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
334 parameters.maxfev = 200 * (n + 1);
335 parameters.xtol = tol;
337 diag.setConstant(n, 1.);
338 useExternalScaling =
true;
339 return solveNumericalDiff(x);
342template <
typename FunctorType,
typename Scalar>
343HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiffInit(FVectorType &x) {
346 if (parameters.nb_of_subdiagonals < 0) parameters.nb_of_subdiagonals = n - 1;
347 if (parameters.nb_of_superdiagonals < 0) parameters.nb_of_superdiagonals = n - 1;
356 if (!useExternalScaling) diag.resize(n);
357 eigen_assert((!useExternalScaling || diag.size() == n) &&
358 "When useExternalScaling is set, the caller must provide a valid 'diag'");
365 if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.nb_of_subdiagonals < 0 ||
366 parameters.nb_of_superdiagonals < 0 || parameters.factor <= 0.)
367 return HybridNonLinearSolverSpace::ImproperInputParameters;
368 if (useExternalScaling)
369 for (
Index j = 0; j < n; ++j)
370 if (diag[j] <= 0.)
return HybridNonLinearSolverSpace::ImproperInputParameters;
375 if (functor(x, fvec) < 0)
return HybridNonLinearSolverSpace::UserAsked;
376 fnorm = fvec.stableNorm();
385 return HybridNonLinearSolverSpace::Running;
388template <
typename FunctorType,
typename Scalar>
389HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiffOneStep(
394 eigen_assert(x.size() == n);
397 std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
400 if (parameters.nb_of_subdiagonals < 0) parameters.nb_of_subdiagonals = n - 1;
401 if (parameters.nb_of_superdiagonals < 0) parameters.nb_of_superdiagonals = n - 1;
404 if (internal::fdjac1(functor, x, fvec, fjac, parameters.nb_of_subdiagonals, parameters.nb_of_superdiagonals,
405 parameters.epsfcn) < 0)
406 return HybridNonLinearSolverSpace::UserAsked;
407 nfev += (std::min)(parameters.nb_of_subdiagonals + parameters.nb_of_superdiagonals + 1, n);
409 wa2 = fjac.colwise().blueNorm();
414 if (!useExternalScaling)
415 for (j = 0; j < n; ++j) diag[j] = (wa2[j] == 0.) ? 1. : wa2[j];
419 xnorm = diag.cwiseProduct(x).stableNorm();
420 delta = parameters.factor * xnorm;
421 if (delta == 0.) delta = parameters.factor;
428 R = qrfac.matrixQR();
431 fjac = qrfac.householderQ();
434 qtf = fjac.transpose() * fvec;
437 if (!useExternalScaling) diag = diag.cwiseMax(wa2);
441 internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
446 pnorm = diag.cwiseProduct(wa1).stableNorm();
449 if (iter == 1) delta = (std::min)(delta, pnorm);
452 if (functor(wa2, wa4) < 0)
return HybridNonLinearSolverSpace::UserAsked;
454 fnorm1 = wa4.stableNorm();
459 actred = 1. - numext::abs2(fnorm1 / fnorm);
462 wa3 = R.template triangularView<Upper>() * wa1 + qtf;
463 temp = wa3.stableNorm();
466 prered = 1. - numext::abs2(temp / fnorm);
470 if (prered > 0.) ratio = actred / prered;
476 delta =
Scalar(.5) * delta;
480 if (ratio >=
Scalar(.5) || ncsuc > 1) delta = (std::max)(delta, pnorm /
Scalar(.5));
482 delta = pnorm /
Scalar(.5);
487 if (ratio >=
Scalar(1e-4)) {
490 wa2 = diag.cwiseProduct(x);
492 xnorm = wa2.stableNorm();
499 if (actred >=
Scalar(.001)) nslow1 = 0;
501 if (actred >=
Scalar(.1)) nslow2 = 0;
504 if (delta <= parameters.xtol * xnorm || fnorm == 0.)
return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
507 if (nfev >= parameters.maxfev)
return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
509 return HybridNonLinearSolverSpace::TolTooSmall;
510 if (nslow2 == 5)
return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
511 if (nslow1 == 10)
return HybridNonLinearSolverSpace::NotMakingProgressIterations;
514 if (ncfail == 2)
break;
518 wa1 = diag.cwiseProduct(diag.cwiseProduct(wa1) / pnorm);
519 wa2 = fjac.transpose() * wa4;
520 if (ratio >=
Scalar(1e-4)) qtf = wa2;
521 wa2 = (wa2 - wa3) / pnorm;
524 internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
525 internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens);
526 internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens);
530 return HybridNonLinearSolverSpace::Running;
533template <
typename FunctorType,
typename Scalar>
534HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiff(FVectorType &x) {
535 HybridNonLinearSolverSpace::Status status = solveNumericalDiffInit(x);
536 if (status == HybridNonLinearSolverSpace::ImproperInputParameters)
return status;
537 while (status == HybridNonLinearSolverSpace::Running) status = solveNumericalDiffOneStep(x);
Namespace containing all symbols from the Eigen library.
const Eigen::CwiseUnaryOp< Eigen::internal::scalar_abs_op< typename Derived::Scalar >, const Derived > abs(const Eigen::ArrayBase< Derived > &x)
EIGEN_DEFAULT_DENSE_INDEX_TYPE Index