| // -*- coding: utf-8 | 
 | // vim: set fileencoding=utf-8 | 
 |  | 
 | // This file is part of Eigen, a lightweight C++ template library | 
 | // for linear algebra. | 
 | // | 
 | // Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org> | 
 | // | 
 | // This Source Code Form is subject to the terms of the Mozilla | 
 | // Public License v. 2.0. If a copy of the MPL was not distributed | 
 | // with this file, You can obtain one at http://mozilla.org/MPL/2.0/. | 
 |  | 
 | #ifndef EIGEN_HYBRIDNONLINEARSOLVER_H | 
 | #define EIGEN_HYBRIDNONLINEARSOLVER_H | 
 |  | 
 | // IWYU pragma: private | 
 | #include "./InternalHeaderCheck.h" | 
 |  | 
 | namespace Eigen { | 
 |  | 
 | namespace HybridNonLinearSolverSpace { | 
 | enum Status { | 
 |   Running = -1, | 
 |   ImproperInputParameters = 0, | 
 |   RelativeErrorTooSmall = 1, | 
 |   TooManyFunctionEvaluation = 2, | 
 |   TolTooSmall = 3, | 
 |   NotMakingProgressJacobian = 4, | 
 |   NotMakingProgressIterations = 5, | 
 |   UserAsked = 6 | 
 | }; | 
 | } | 
 |  | 
 | /** | 
 |  * \ingroup NonLinearOptimization_Module | 
 |  * \brief Finds a zero of a system of n | 
 |  * nonlinear functions in n variables by a modification of the Powell | 
 |  * hybrid method ("dogleg"). | 
 |  * | 
 |  * The user must provide a subroutine which calculates the | 
 |  * functions. The Jacobian is either provided by the user, or approximated | 
 |  * using a forward-difference method. | 
 |  * | 
 |  */ | 
 | template <typename FunctorType, typename Scalar = double> | 
 | class HybridNonLinearSolver { | 
 |  public: | 
 |   typedef DenseIndex Index; | 
 |  | 
 |   HybridNonLinearSolver(FunctorType &_functor) : functor(_functor) { | 
 |     nfev = njev = iter = 0; | 
 |     fnorm = 0.; | 
 |     useExternalScaling = false; | 
 |   } | 
 |  | 
 |   struct Parameters { | 
 |     Parameters() | 
 |         : factor(Scalar(100.)), | 
 |           maxfev(1000), | 
 |           xtol(numext::sqrt(NumTraits<Scalar>::epsilon())), | 
 |           nb_of_subdiagonals(-1), | 
 |           nb_of_superdiagonals(-1), | 
 |           epsfcn(Scalar(0.)) {} | 
 |     Scalar factor; | 
 |     Index maxfev;  // maximum number of function evaluation | 
 |     Scalar xtol; | 
 |     Index nb_of_subdiagonals; | 
 |     Index nb_of_superdiagonals; | 
 |     Scalar epsfcn; | 
 |   }; | 
 |   typedef Matrix<Scalar, Dynamic, 1> FVectorType; | 
 |   typedef Matrix<Scalar, Dynamic, Dynamic> JacobianType; | 
 |   /* TODO: if eigen provides a triangular storage, use it here */ | 
 |   typedef Matrix<Scalar, Dynamic, Dynamic> UpperTriangularType; | 
 |  | 
 |   HybridNonLinearSolverSpace::Status hybrj1(FVectorType &x, | 
 |                                             const Scalar tol = numext::sqrt(NumTraits<Scalar>::epsilon())); | 
 |  | 
 |   HybridNonLinearSolverSpace::Status solveInit(FVectorType &x); | 
 |   HybridNonLinearSolverSpace::Status solveOneStep(FVectorType &x); | 
 |   HybridNonLinearSolverSpace::Status solve(FVectorType &x); | 
 |  | 
 |   HybridNonLinearSolverSpace::Status hybrd1(FVectorType &x, | 
 |                                             const Scalar tol = numext::sqrt(NumTraits<Scalar>::epsilon())); | 
 |  | 
 |   HybridNonLinearSolverSpace::Status solveNumericalDiffInit(FVectorType &x); | 
 |   HybridNonLinearSolverSpace::Status solveNumericalDiffOneStep(FVectorType &x); | 
 |   HybridNonLinearSolverSpace::Status solveNumericalDiff(FVectorType &x); | 
 |  | 
 |   void resetParameters(void) { parameters = Parameters(); } | 
 |   Parameters parameters; | 
 |   FVectorType fvec, qtf, diag; | 
 |   JacobianType fjac; | 
 |   UpperTriangularType R; | 
 |   Index nfev; | 
 |   Index njev; | 
 |   Index iter; | 
 |   Scalar fnorm; | 
 |   bool useExternalScaling; | 
 |  | 
 |  private: | 
 |   FunctorType &functor; | 
 |   Index n; | 
 |   Scalar sum; | 
 |   bool sing; | 
 |   Scalar temp; | 
 |   Scalar delta; | 
 |   bool jeval; | 
 |   Index ncsuc; | 
 |   Scalar ratio; | 
 |   Scalar pnorm, xnorm, fnorm1; | 
 |   Index nslow1, nslow2; | 
 |   Index ncfail; | 
 |   Scalar actred, prered; | 
 |   FVectorType wa1, wa2, wa3, wa4; | 
 |  | 
 |   HybridNonLinearSolver &operator=(const HybridNonLinearSolver &); | 
 | }; | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::hybrj1(FVectorType &x, | 
 |                                                                                       const Scalar tol) { | 
 |   n = x.size(); | 
 |  | 
 |   /* check the input parameters for errors. */ | 
 |   if (n <= 0 || tol < 0.) return HybridNonLinearSolverSpace::ImproperInputParameters; | 
 |  | 
 |   resetParameters(); | 
 |   parameters.maxfev = 100 * (n + 1); | 
 |   parameters.xtol = tol; | 
 |   diag.setConstant(n, 1.); | 
 |   useExternalScaling = true; | 
 |   return solve(x); | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveInit(FVectorType &x) { | 
 |   n = x.size(); | 
 |  | 
 |   wa1.resize(n); | 
 |   wa2.resize(n); | 
 |   wa3.resize(n); | 
 |   wa4.resize(n); | 
 |   fvec.resize(n); | 
 |   qtf.resize(n); | 
 |   fjac.resize(n, n); | 
 |   if (!useExternalScaling) diag.resize(n); | 
 |   eigen_assert((!useExternalScaling || diag.size() == n) && | 
 |                "When useExternalScaling is set, the caller must provide a valid 'diag'"); | 
 |  | 
 |   /* Function Body */ | 
 |   nfev = 0; | 
 |   njev = 0; | 
 |  | 
 |   /*     check the input parameters for errors. */ | 
 |   if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.factor <= 0.) | 
 |     return HybridNonLinearSolverSpace::ImproperInputParameters; | 
 |   if (useExternalScaling) | 
 |     for (Index j = 0; j < n; ++j) | 
 |       if (diag[j] <= 0.) return HybridNonLinearSolverSpace::ImproperInputParameters; | 
 |  | 
 |   /*     evaluate the function at the starting point */ | 
 |   /*     and calculate its norm. */ | 
 |   nfev = 1; | 
 |   if (functor(x, fvec) < 0) return HybridNonLinearSolverSpace::UserAsked; | 
 |   fnorm = fvec.stableNorm(); | 
 |  | 
 |   /*     initialize iteration counter and monitors. */ | 
 |   iter = 1; | 
 |   ncsuc = 0; | 
 |   ncfail = 0; | 
 |   nslow1 = 0; | 
 |   nslow2 = 0; | 
 |  | 
 |   return HybridNonLinearSolverSpace::Running; | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveOneStep(FVectorType &x) { | 
 |   using std::abs; | 
 |  | 
 |   eigen_assert(x.size() == n);  // check the caller is not cheating us | 
 |  | 
 |   Index j; | 
 |   std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n); | 
 |  | 
 |   jeval = true; | 
 |  | 
 |   /* calculate the jacobian matrix. */ | 
 |   if (functor.df(x, fjac) < 0) return HybridNonLinearSolverSpace::UserAsked; | 
 |   ++njev; | 
 |  | 
 |   wa2 = fjac.colwise().blueNorm(); | 
 |  | 
 |   /* on the first iteration and if external scaling is not used, scale according */ | 
 |   /* to the norms of the columns of the initial jacobian. */ | 
 |   if (iter == 1) { | 
 |     if (!useExternalScaling) | 
 |       for (j = 0; j < n; ++j) diag[j] = (wa2[j] == 0.) ? 1. : wa2[j]; | 
 |  | 
 |     /* on the first iteration, calculate the norm of the scaled x */ | 
 |     /* and initialize the step bound delta. */ | 
 |     xnorm = diag.cwiseProduct(x).stableNorm(); | 
 |     delta = parameters.factor * xnorm; | 
 |     if (delta == 0.) delta = parameters.factor; | 
 |   } | 
 |  | 
 |   /* compute the qr factorization of the jacobian. */ | 
 |   HouseholderQR<JacobianType> qrfac(fjac);  // no pivoting: | 
 |  | 
 |   /* copy the triangular factor of the qr factorization into r. */ | 
 |   R = qrfac.matrixQR(); | 
 |  | 
 |   /* accumulate the orthogonal factor in fjac. */ | 
 |   fjac = qrfac.householderQ(); | 
 |  | 
 |   /* form (q transpose)*fvec and store in qtf. */ | 
 |   qtf = fjac.transpose() * fvec; | 
 |  | 
 |   /* rescale if necessary. */ | 
 |   if (!useExternalScaling) diag = diag.cwiseMax(wa2); | 
 |  | 
 |   while (true) { | 
 |     /* determine the direction p. */ | 
 |     internal::dogleg<Scalar>(R, diag, qtf, delta, wa1); | 
 |  | 
 |     /* store the direction p and x + p. calculate the norm of p. */ | 
 |     wa1 = -wa1; | 
 |     wa2 = x + wa1; | 
 |     pnorm = diag.cwiseProduct(wa1).stableNorm(); | 
 |  | 
 |     /* on the first iteration, adjust the initial step bound. */ | 
 |     if (iter == 1) delta = (std::min)(delta, pnorm); | 
 |  | 
 |     /* evaluate the function at x + p and calculate its norm. */ | 
 |     if (functor(wa2, wa4) < 0) return HybridNonLinearSolverSpace::UserAsked; | 
 |     ++nfev; | 
 |     fnorm1 = wa4.stableNorm(); | 
 |  | 
 |     /* compute the scaled actual reduction. */ | 
 |     actred = -1.; | 
 |     if (fnorm1 < fnorm) /* Computing 2nd power */ | 
 |       actred = 1. - numext::abs2(fnorm1 / fnorm); | 
 |  | 
 |     /* compute the scaled predicted reduction. */ | 
 |     wa3 = R.template triangularView<Upper>() * wa1 + qtf; | 
 |     temp = wa3.stableNorm(); | 
 |     prered = 0.; | 
 |     if (temp < fnorm) /* Computing 2nd power */ | 
 |       prered = 1. - numext::abs2(temp / fnorm); | 
 |  | 
 |     /* compute the ratio of the actual to the predicted reduction. */ | 
 |     ratio = 0.; | 
 |     if (prered > 0.) ratio = actred / prered; | 
 |  | 
 |     /* update the step bound. */ | 
 |     if (ratio < Scalar(.1)) { | 
 |       ncsuc = 0; | 
 |       ++ncfail; | 
 |       delta = Scalar(.5) * delta; | 
 |     } else { | 
 |       ncfail = 0; | 
 |       ++ncsuc; | 
 |       if (ratio >= Scalar(.5) || ncsuc > 1) delta = (std::max)(delta, pnorm / Scalar(.5)); | 
 |       if (abs(ratio - 1.) <= Scalar(.1)) { | 
 |         delta = pnorm / Scalar(.5); | 
 |       } | 
 |     } | 
 |  | 
 |     /* test for successful iteration. */ | 
 |     if (ratio >= Scalar(1e-4)) { | 
 |       /* successful iteration. update x, fvec, and their norms. */ | 
 |       x = wa2; | 
 |       wa2 = diag.cwiseProduct(x); | 
 |       fvec = wa4; | 
 |       xnorm = wa2.stableNorm(); | 
 |       fnorm = fnorm1; | 
 |       ++iter; | 
 |     } | 
 |  | 
 |     /* determine the progress of the iteration. */ | 
 |     ++nslow1; | 
 |     if (actred >= Scalar(.001)) nslow1 = 0; | 
 |     if (jeval) ++nslow2; | 
 |     if (actred >= Scalar(.1)) nslow2 = 0; | 
 |  | 
 |     /* test for convergence. */ | 
 |     if (delta <= parameters.xtol * xnorm || fnorm == 0.) return HybridNonLinearSolverSpace::RelativeErrorTooSmall; | 
 |  | 
 |     /* tests for termination and stringent tolerances. */ | 
 |     if (nfev >= parameters.maxfev) return HybridNonLinearSolverSpace::TooManyFunctionEvaluation; | 
 |     if (Scalar(.1) * (std::max)(Scalar(.1) * delta, pnorm) <= NumTraits<Scalar>::epsilon() * xnorm) | 
 |       return HybridNonLinearSolverSpace::TolTooSmall; | 
 |     if (nslow2 == 5) return HybridNonLinearSolverSpace::NotMakingProgressJacobian; | 
 |     if (nslow1 == 10) return HybridNonLinearSolverSpace::NotMakingProgressIterations; | 
 |  | 
 |     /* criterion for recalculating jacobian. */ | 
 |     if (ncfail == 2) break;  // leave inner loop and go for the next outer loop iteration | 
 |  | 
 |     /* calculate the rank one modification to the jacobian */ | 
 |     /* and update qtf if necessary. */ | 
 |     wa1 = diag.cwiseProduct(diag.cwiseProduct(wa1) / pnorm); | 
 |     wa2 = fjac.transpose() * wa4; | 
 |     if (ratio >= Scalar(1e-4)) qtf = wa2; | 
 |     wa2 = (wa2 - wa3) / pnorm; | 
 |  | 
 |     /* compute the qr factorization of the updated jacobian. */ | 
 |     internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing); | 
 |     internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens); | 
 |     internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens); | 
 |  | 
 |     jeval = false; | 
 |   } | 
 |   return HybridNonLinearSolverSpace::Running; | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solve(FVectorType &x) { | 
 |   HybridNonLinearSolverSpace::Status status = solveInit(x); | 
 |   if (status == HybridNonLinearSolverSpace::ImproperInputParameters) return status; | 
 |   while (status == HybridNonLinearSolverSpace::Running) status = solveOneStep(x); | 
 |   return status; | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::hybrd1(FVectorType &x, | 
 |                                                                                       const Scalar tol) { | 
 |   n = x.size(); | 
 |  | 
 |   /* check the input parameters for errors. */ | 
 |   if (n <= 0 || tol < 0.) return HybridNonLinearSolverSpace::ImproperInputParameters; | 
 |  | 
 |   resetParameters(); | 
 |   parameters.maxfev = 200 * (n + 1); | 
 |   parameters.xtol = tol; | 
 |  | 
 |   diag.setConstant(n, 1.); | 
 |   useExternalScaling = true; | 
 |   return solveNumericalDiff(x); | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiffInit(FVectorType &x) { | 
 |   n = x.size(); | 
 |  | 
 |   if (parameters.nb_of_subdiagonals < 0) parameters.nb_of_subdiagonals = n - 1; | 
 |   if (parameters.nb_of_superdiagonals < 0) parameters.nb_of_superdiagonals = n - 1; | 
 |  | 
 |   wa1.resize(n); | 
 |   wa2.resize(n); | 
 |   wa3.resize(n); | 
 |   wa4.resize(n); | 
 |   qtf.resize(n); | 
 |   fjac.resize(n, n); | 
 |   fvec.resize(n); | 
 |   if (!useExternalScaling) diag.resize(n); | 
 |   eigen_assert((!useExternalScaling || diag.size() == n) && | 
 |                "When useExternalScaling is set, the caller must provide a valid 'diag'"); | 
 |  | 
 |   /* Function Body */ | 
 |   nfev = 0; | 
 |   njev = 0; | 
 |  | 
 |   /*     check the input parameters for errors. */ | 
 |   if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.nb_of_subdiagonals < 0 || | 
 |       parameters.nb_of_superdiagonals < 0 || parameters.factor <= 0.) | 
 |     return HybridNonLinearSolverSpace::ImproperInputParameters; | 
 |   if (useExternalScaling) | 
 |     for (Index j = 0; j < n; ++j) | 
 |       if (diag[j] <= 0.) return HybridNonLinearSolverSpace::ImproperInputParameters; | 
 |  | 
 |   /*     evaluate the function at the starting point */ | 
 |   /*     and calculate its norm. */ | 
 |   nfev = 1; | 
 |   if (functor(x, fvec) < 0) return HybridNonLinearSolverSpace::UserAsked; | 
 |   fnorm = fvec.stableNorm(); | 
 |  | 
 |   /*     initialize iteration counter and monitors. */ | 
 |   iter = 1; | 
 |   ncsuc = 0; | 
 |   ncfail = 0; | 
 |   nslow1 = 0; | 
 |   nslow2 = 0; | 
 |  | 
 |   return HybridNonLinearSolverSpace::Running; | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiffOneStep( | 
 |     FVectorType &x) { | 
 |   using std::abs; | 
 |   using std::sqrt; | 
 |  | 
 |   eigen_assert(x.size() == n);  // check the caller is not cheating us | 
 |  | 
 |   Index j; | 
 |   std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n); | 
 |  | 
 |   jeval = true; | 
 |   if (parameters.nb_of_subdiagonals < 0) parameters.nb_of_subdiagonals = n - 1; | 
 |   if (parameters.nb_of_superdiagonals < 0) parameters.nb_of_superdiagonals = n - 1; | 
 |  | 
 |   /* calculate the jacobian matrix. */ | 
 |   if (internal::fdjac1(functor, x, fvec, fjac, parameters.nb_of_subdiagonals, parameters.nb_of_superdiagonals, | 
 |                        parameters.epsfcn) < 0) | 
 |     return HybridNonLinearSolverSpace::UserAsked; | 
 |   nfev += (std::min)(parameters.nb_of_subdiagonals + parameters.nb_of_superdiagonals + 1, n); | 
 |  | 
 |   wa2 = fjac.colwise().blueNorm(); | 
 |  | 
 |   /* on the first iteration and if external scaling is not used, scale according */ | 
 |   /* to the norms of the columns of the initial jacobian. */ | 
 |   if (iter == 1) { | 
 |     if (!useExternalScaling) | 
 |       for (j = 0; j < n; ++j) diag[j] = (wa2[j] == 0.) ? 1. : wa2[j]; | 
 |  | 
 |     /* on the first iteration, calculate the norm of the scaled x */ | 
 |     /* and initialize the step bound delta. */ | 
 |     xnorm = diag.cwiseProduct(x).stableNorm(); | 
 |     delta = parameters.factor * xnorm; | 
 |     if (delta == 0.) delta = parameters.factor; | 
 |   } | 
 |  | 
 |   /* compute the qr factorization of the jacobian. */ | 
 |   HouseholderQR<JacobianType> qrfac(fjac);  // no pivoting: | 
 |  | 
 |   /* copy the triangular factor of the qr factorization into r. */ | 
 |   R = qrfac.matrixQR(); | 
 |  | 
 |   /* accumulate the orthogonal factor in fjac. */ | 
 |   fjac = qrfac.householderQ(); | 
 |  | 
 |   /* form (q transpose)*fvec and store in qtf. */ | 
 |   qtf = fjac.transpose() * fvec; | 
 |  | 
 |   /* rescale if necessary. */ | 
 |   if (!useExternalScaling) diag = diag.cwiseMax(wa2); | 
 |  | 
 |   while (true) { | 
 |     /* determine the direction p. */ | 
 |     internal::dogleg<Scalar>(R, diag, qtf, delta, wa1); | 
 |  | 
 |     /* store the direction p and x + p. calculate the norm of p. */ | 
 |     wa1 = -wa1; | 
 |     wa2 = x + wa1; | 
 |     pnorm = diag.cwiseProduct(wa1).stableNorm(); | 
 |  | 
 |     /* on the first iteration, adjust the initial step bound. */ | 
 |     if (iter == 1) delta = (std::min)(delta, pnorm); | 
 |  | 
 |     /* evaluate the function at x + p and calculate its norm. */ | 
 |     if (functor(wa2, wa4) < 0) return HybridNonLinearSolverSpace::UserAsked; | 
 |     ++nfev; | 
 |     fnorm1 = wa4.stableNorm(); | 
 |  | 
 |     /* compute the scaled actual reduction. */ | 
 |     actred = -1.; | 
 |     if (fnorm1 < fnorm) /* Computing 2nd power */ | 
 |       actred = 1. - numext::abs2(fnorm1 / fnorm); | 
 |  | 
 |     /* compute the scaled predicted reduction. */ | 
 |     wa3 = R.template triangularView<Upper>() * wa1 + qtf; | 
 |     temp = wa3.stableNorm(); | 
 |     prered = 0.; | 
 |     if (temp < fnorm) /* Computing 2nd power */ | 
 |       prered = 1. - numext::abs2(temp / fnorm); | 
 |  | 
 |     /* compute the ratio of the actual to the predicted reduction. */ | 
 |     ratio = 0.; | 
 |     if (prered > 0.) ratio = actred / prered; | 
 |  | 
 |     /* update the step bound. */ | 
 |     if (ratio < Scalar(.1)) { | 
 |       ncsuc = 0; | 
 |       ++ncfail; | 
 |       delta = Scalar(.5) * delta; | 
 |     } else { | 
 |       ncfail = 0; | 
 |       ++ncsuc; | 
 |       if (ratio >= Scalar(.5) || ncsuc > 1) delta = (std::max)(delta, pnorm / Scalar(.5)); | 
 |       if (abs(ratio - 1.) <= Scalar(.1)) { | 
 |         delta = pnorm / Scalar(.5); | 
 |       } | 
 |     } | 
 |  | 
 |     /* test for successful iteration. */ | 
 |     if (ratio >= Scalar(1e-4)) { | 
 |       /* successful iteration. update x, fvec, and their norms. */ | 
 |       x = wa2; | 
 |       wa2 = diag.cwiseProduct(x); | 
 |       fvec = wa4; | 
 |       xnorm = wa2.stableNorm(); | 
 |       fnorm = fnorm1; | 
 |       ++iter; | 
 |     } | 
 |  | 
 |     /* determine the progress of the iteration. */ | 
 |     ++nslow1; | 
 |     if (actred >= Scalar(.001)) nslow1 = 0; | 
 |     if (jeval) ++nslow2; | 
 |     if (actred >= Scalar(.1)) nslow2 = 0; | 
 |  | 
 |     /* test for convergence. */ | 
 |     if (delta <= parameters.xtol * xnorm || fnorm == 0.) return HybridNonLinearSolverSpace::RelativeErrorTooSmall; | 
 |  | 
 |     /* tests for termination and stringent tolerances. */ | 
 |     if (nfev >= parameters.maxfev) return HybridNonLinearSolverSpace::TooManyFunctionEvaluation; | 
 |     if (Scalar(.1) * (std::max)(Scalar(.1) * delta, pnorm) <= NumTraits<Scalar>::epsilon() * xnorm) | 
 |       return HybridNonLinearSolverSpace::TolTooSmall; | 
 |     if (nslow2 == 5) return HybridNonLinearSolverSpace::NotMakingProgressJacobian; | 
 |     if (nslow1 == 10) return HybridNonLinearSolverSpace::NotMakingProgressIterations; | 
 |  | 
 |     /* criterion for recalculating jacobian. */ | 
 |     if (ncfail == 2) break;  // leave inner loop and go for the next outer loop iteration | 
 |  | 
 |     /* calculate the rank one modification to the jacobian */ | 
 |     /* and update qtf if necessary. */ | 
 |     wa1 = diag.cwiseProduct(diag.cwiseProduct(wa1) / pnorm); | 
 |     wa2 = fjac.transpose() * wa4; | 
 |     if (ratio >= Scalar(1e-4)) qtf = wa2; | 
 |     wa2 = (wa2 - wa3) / pnorm; | 
 |  | 
 |     /* compute the qr factorization of the updated jacobian. */ | 
 |     internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing); | 
 |     internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens); | 
 |     internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens); | 
 |  | 
 |     jeval = false; | 
 |   } | 
 |   return HybridNonLinearSolverSpace::Running; | 
 | } | 
 |  | 
 | template <typename FunctorType, typename Scalar> | 
 | HybridNonLinearSolverSpace::Status HybridNonLinearSolver<FunctorType, Scalar>::solveNumericalDiff(FVectorType &x) { | 
 |   HybridNonLinearSolverSpace::Status status = solveNumericalDiffInit(x); | 
 |   if (status == HybridNonLinearSolverSpace::ImproperInputParameters) return status; | 
 |   while (status == HybridNonLinearSolverSpace::Running) status = solveNumericalDiffOneStep(x); | 
 |   return status; | 
 | } | 
 |  | 
 | }  // end namespace Eigen | 
 |  | 
 | #endif  // EIGEN_HYBRIDNONLINEARSOLVER_H | 
 |  | 
 | // vim: ai ts=4 sts=4 et sw=4 |