00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013 #ifndef EIGEN_HYBRIDNONLINEARSOLVER_H
00014 #define EIGEN_HYBRIDNONLINEARSOLVER_H
00015
00016 namespace Eigen {
00017
00018 namespace HybridNonLinearSolverSpace {
00019 enum Status {
00020 Running = -1,
00021 ImproperInputParameters = 0,
00022 RelativeErrorTooSmall = 1,
00023 TooManyFunctionEvaluation = 2,
00024 TolTooSmall = 3,
00025 NotMakingProgressJacobian = 4,
00026 NotMakingProgressIterations = 5,
00027 UserAsked = 6
00028 };
00029 }
00030
00042 template<typename FunctorType, typename Scalar=double>
00043 class HybridNonLinearSolver
00044 {
00045 public:
00046 typedef DenseIndex Index;
00047
00048 HybridNonLinearSolver(FunctorType &_functor)
00049 : functor(_functor) { nfev=njev=iter = 0; fnorm= 0.; useExternalScaling=false;}
00050
00051 struct Parameters {
00052 Parameters()
00053 : factor(Scalar(100.))
00054 , maxfev(1000)
00055 , xtol(std::sqrt(NumTraits<Scalar>::epsilon()))
00056 , nb_of_subdiagonals(-1)
00057 , nb_of_superdiagonals(-1)
00058 , epsfcn(Scalar(0.)) {}
00059 Scalar factor;
00060 Index maxfev;
00061 Scalar xtol;
00062 Index nb_of_subdiagonals;
00063 Index nb_of_superdiagonals;
00064 Scalar epsfcn;
00065 };
00066 typedef Matrix< Scalar, Dynamic, 1 > FVectorType;
00067 typedef Matrix< Scalar, Dynamic, Dynamic > JacobianType;
00068
00069 typedef Matrix< Scalar, Dynamic, Dynamic > UpperTriangularType;
00070
00071 HybridNonLinearSolverSpace::Status hybrj1(
00072 FVectorType &x,
00073 const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
00074 );
00075
00076 HybridNonLinearSolverSpace::Status solveInit(FVectorType &x);
00077 HybridNonLinearSolverSpace::Status solveOneStep(FVectorType &x);
00078 HybridNonLinearSolverSpace::Status solve(FVectorType &x);
00079
00080 HybridNonLinearSolverSpace::Status hybrd1(
00081 FVectorType &x,
00082 const Scalar tol = std::sqrt(NumTraits<Scalar>::epsilon())
00083 );
00084
00085 HybridNonLinearSolverSpace::Status solveNumericalDiffInit(FVectorType &x);
00086 HybridNonLinearSolverSpace::Status solveNumericalDiffOneStep(FVectorType &x);
00087 HybridNonLinearSolverSpace::Status solveNumericalDiff(FVectorType &x);
00088
00089 void resetParameters(void) { parameters = Parameters(); }
00090 Parameters parameters;
00091 FVectorType fvec, qtf, diag;
00092 JacobianType fjac;
00093 UpperTriangularType R;
00094 Index nfev;
00095 Index njev;
00096 Index iter;
00097 Scalar fnorm;
00098 bool useExternalScaling;
00099 private:
00100 FunctorType &functor;
00101 Index n;
00102 Scalar sum;
00103 bool sing;
00104 Scalar temp;
00105 Scalar delta;
00106 bool jeval;
00107 Index ncsuc;
00108 Scalar ratio;
00109 Scalar pnorm, xnorm, fnorm1;
00110 Index nslow1, nslow2;
00111 Index ncfail;
00112 Scalar actred, prered;
00113 FVectorType wa1, wa2, wa3, wa4;
00114
00115 HybridNonLinearSolver& operator=(const HybridNonLinearSolver&);
00116 };
00117
00118
00119
00120 template<typename FunctorType, typename Scalar>
00121 HybridNonLinearSolverSpace::Status
00122 HybridNonLinearSolver<FunctorType,Scalar>::hybrj1(
00123 FVectorType &x,
00124 const Scalar tol
00125 )
00126 {
00127 n = x.size();
00128
00129
00130 if (n <= 0 || tol < 0.)
00131 return HybridNonLinearSolverSpace::ImproperInputParameters;
00132
00133 resetParameters();
00134 parameters.maxfev = 100*(n+1);
00135 parameters.xtol = tol;
00136 diag.setConstant(n, 1.);
00137 useExternalScaling = true;
00138 return solve(x);
00139 }
00140
00141 template<typename FunctorType, typename Scalar>
00142 HybridNonLinearSolverSpace::Status
00143 HybridNonLinearSolver<FunctorType,Scalar>::solveInit(FVectorType &x)
00144 {
00145 n = x.size();
00146
00147 wa1.resize(n); wa2.resize(n); wa3.resize(n); wa4.resize(n);
00148 fvec.resize(n);
00149 qtf.resize(n);
00150 fjac.resize(n, n);
00151 if (!useExternalScaling)
00152 diag.resize(n);
00153 eigen_assert( (!useExternalScaling || diag.size()==n) || "When useExternalScaling is set, the caller must provide a valid 'diag'");
00154
00155
00156 nfev = 0;
00157 njev = 0;
00158
00159
00160 if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.factor <= 0. )
00161 return HybridNonLinearSolverSpace::ImproperInputParameters;
00162 if (useExternalScaling)
00163 for (Index j = 0; j < n; ++j)
00164 if (diag[j] <= 0.)
00165 return HybridNonLinearSolverSpace::ImproperInputParameters;
00166
00167
00168
00169 nfev = 1;
00170 if ( functor(x, fvec) < 0)
00171 return HybridNonLinearSolverSpace::UserAsked;
00172 fnorm = fvec.stableNorm();
00173
00174
00175 iter = 1;
00176 ncsuc = 0;
00177 ncfail = 0;
00178 nslow1 = 0;
00179 nslow2 = 0;
00180
00181 return HybridNonLinearSolverSpace::Running;
00182 }
00183
00184 template<typename FunctorType, typename Scalar>
00185 HybridNonLinearSolverSpace::Status
00186 HybridNonLinearSolver<FunctorType,Scalar>::solveOneStep(FVectorType &x)
00187 {
00188 using std::abs;
00189
00190 eigen_assert(x.size()==n);
00191
00192 Index j;
00193 std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
00194
00195 jeval = true;
00196
00197
00198 if ( functor.df(x, fjac) < 0)
00199 return HybridNonLinearSolverSpace::UserAsked;
00200 ++njev;
00201
00202 wa2 = fjac.colwise().blueNorm();
00203
00204
00205
00206 if (iter == 1) {
00207 if (!useExternalScaling)
00208 for (j = 0; j < n; ++j)
00209 diag[j] = (wa2[j]==0.) ? 1. : wa2[j];
00210
00211
00212
00213 xnorm = diag.cwiseProduct(x).stableNorm();
00214 delta = parameters.factor * xnorm;
00215 if (delta == 0.)
00216 delta = parameters.factor;
00217 }
00218
00219
00220 HouseholderQR<JacobianType> qrfac(fjac);
00221
00222
00223 R = qrfac.matrixQR();
00224
00225
00226 fjac = qrfac.householderQ();
00227
00228
00229 qtf = fjac.transpose() * fvec;
00230
00231
00232 if (!useExternalScaling)
00233 diag = diag.cwiseMax(wa2);
00234
00235 while (true) {
00236
00237 internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
00238
00239
00240 wa1 = -wa1;
00241 wa2 = x + wa1;
00242 pnorm = diag.cwiseProduct(wa1).stableNorm();
00243
00244
00245 if (iter == 1)
00246 delta = (std::min)(delta,pnorm);
00247
00248
00249 if ( functor(wa2, wa4) < 0)
00250 return HybridNonLinearSolverSpace::UserAsked;
00251 ++nfev;
00252 fnorm1 = wa4.stableNorm();
00253
00254
00255 actred = -1.;
00256 if (fnorm1 < fnorm)
00257 actred = 1. - numext::abs2(fnorm1 / fnorm);
00258
00259
00260 wa3 = R.template triangularView<Upper>()*wa1 + qtf;
00261 temp = wa3.stableNorm();
00262 prered = 0.;
00263 if (temp < fnorm)
00264 prered = 1. - numext::abs2(temp / fnorm);
00265
00266
00267 ratio = 0.;
00268 if (prered > 0.)
00269 ratio = actred / prered;
00270
00271
00272 if (ratio < Scalar(.1)) {
00273 ncsuc = 0;
00274 ++ncfail;
00275 delta = Scalar(.5) * delta;
00276 } else {
00277 ncfail = 0;
00278 ++ncsuc;
00279 if (ratio >= Scalar(.5) || ncsuc > 1)
00280 delta = (std::max)(delta, pnorm / Scalar(.5));
00281 if (abs(ratio - 1.) <= Scalar(.1)) {
00282 delta = pnorm / Scalar(.5);
00283 }
00284 }
00285
00286
00287 if (ratio >= Scalar(1e-4)) {
00288
00289 x = wa2;
00290 wa2 = diag.cwiseProduct(x);
00291 fvec = wa4;
00292 xnorm = wa2.stableNorm();
00293 fnorm = fnorm1;
00294 ++iter;
00295 }
00296
00297
00298 ++nslow1;
00299 if (actred >= Scalar(.001))
00300 nslow1 = 0;
00301 if (jeval)
00302 ++nslow2;
00303 if (actred >= Scalar(.1))
00304 nslow2 = 0;
00305
00306
00307 if (delta <= parameters.xtol * xnorm || fnorm == 0.)
00308 return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
00309
00310
00311 if (nfev >= parameters.maxfev)
00312 return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
00313 if (Scalar(.1) * (std::max)(Scalar(.1) * delta, pnorm) <= NumTraits<Scalar>::epsilon() * xnorm)
00314 return HybridNonLinearSolverSpace::TolTooSmall;
00315 if (nslow2 == 5)
00316 return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
00317 if (nslow1 == 10)
00318 return HybridNonLinearSolverSpace::NotMakingProgressIterations;
00319
00320
00321 if (ncfail == 2)
00322 break;
00323
00324
00325
00326 wa1 = diag.cwiseProduct( diag.cwiseProduct(wa1)/pnorm );
00327 wa2 = fjac.transpose() * wa4;
00328 if (ratio >= Scalar(1e-4))
00329 qtf = wa2;
00330 wa2 = (wa2-wa3)/pnorm;
00331
00332
00333 internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
00334 internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens);
00335 internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens);
00336
00337 jeval = false;
00338 }
00339 return HybridNonLinearSolverSpace::Running;
00340 }
00341
00342 template<typename FunctorType, typename Scalar>
00343 HybridNonLinearSolverSpace::Status
00344 HybridNonLinearSolver<FunctorType,Scalar>::solve(FVectorType &x)
00345 {
00346 HybridNonLinearSolverSpace::Status status = solveInit(x);
00347 if (status==HybridNonLinearSolverSpace::ImproperInputParameters)
00348 return status;
00349 while (status==HybridNonLinearSolverSpace::Running)
00350 status = solveOneStep(x);
00351 return status;
00352 }
00353
00354
00355
00356 template<typename FunctorType, typename Scalar>
00357 HybridNonLinearSolverSpace::Status
00358 HybridNonLinearSolver<FunctorType,Scalar>::hybrd1(
00359 FVectorType &x,
00360 const Scalar tol
00361 )
00362 {
00363 n = x.size();
00364
00365
00366 if (n <= 0 || tol < 0.)
00367 return HybridNonLinearSolverSpace::ImproperInputParameters;
00368
00369 resetParameters();
00370 parameters.maxfev = 200*(n+1);
00371 parameters.xtol = tol;
00372
00373 diag.setConstant(n, 1.);
00374 useExternalScaling = true;
00375 return solveNumericalDiff(x);
00376 }
00377
00378 template<typename FunctorType, typename Scalar>
00379 HybridNonLinearSolverSpace::Status
00380 HybridNonLinearSolver<FunctorType,Scalar>::solveNumericalDiffInit(FVectorType &x)
00381 {
00382 n = x.size();
00383
00384 if (parameters.nb_of_subdiagonals<0) parameters.nb_of_subdiagonals= n-1;
00385 if (parameters.nb_of_superdiagonals<0) parameters.nb_of_superdiagonals= n-1;
00386
00387 wa1.resize(n); wa2.resize(n); wa3.resize(n); wa4.resize(n);
00388 qtf.resize(n);
00389 fjac.resize(n, n);
00390 fvec.resize(n);
00391 if (!useExternalScaling)
00392 diag.resize(n);
00393 eigen_assert( (!useExternalScaling || diag.size()==n) || "When useExternalScaling is set, the caller must provide a valid 'diag'");
00394
00395
00396 nfev = 0;
00397 njev = 0;
00398
00399
00400 if (n <= 0 || parameters.xtol < 0. || parameters.maxfev <= 0 || parameters.nb_of_subdiagonals< 0 || parameters.nb_of_superdiagonals< 0 || parameters.factor <= 0. )
00401 return HybridNonLinearSolverSpace::ImproperInputParameters;
00402 if (useExternalScaling)
00403 for (Index j = 0; j < n; ++j)
00404 if (diag[j] <= 0.)
00405 return HybridNonLinearSolverSpace::ImproperInputParameters;
00406
00407
00408
00409 nfev = 1;
00410 if ( functor(x, fvec) < 0)
00411 return HybridNonLinearSolverSpace::UserAsked;
00412 fnorm = fvec.stableNorm();
00413
00414
00415 iter = 1;
00416 ncsuc = 0;
00417 ncfail = 0;
00418 nslow1 = 0;
00419 nslow2 = 0;
00420
00421 return HybridNonLinearSolverSpace::Running;
00422 }
00423
00424 template<typename FunctorType, typename Scalar>
00425 HybridNonLinearSolverSpace::Status
00426 HybridNonLinearSolver<FunctorType,Scalar>::solveNumericalDiffOneStep(FVectorType &x)
00427 {
00428 using std::sqrt;
00429 using std::abs;
00430
00431 assert(x.size()==n);
00432
00433 Index j;
00434 std::vector<JacobiRotation<Scalar> > v_givens(n), w_givens(n);
00435
00436 jeval = true;
00437 if (parameters.nb_of_subdiagonals<0) parameters.nb_of_subdiagonals= n-1;
00438 if (parameters.nb_of_superdiagonals<0) parameters.nb_of_superdiagonals= n-1;
00439
00440
00441 if (internal::fdjac1(functor, x, fvec, fjac, parameters.nb_of_subdiagonals, parameters.nb_of_superdiagonals, parameters.epsfcn) <0)
00442 return HybridNonLinearSolverSpace::UserAsked;
00443 nfev += (std::min)(parameters.nb_of_subdiagonals+parameters.nb_of_superdiagonals+ 1, n);
00444
00445 wa2 = fjac.colwise().blueNorm();
00446
00447
00448
00449 if (iter == 1) {
00450 if (!useExternalScaling)
00451 for (j = 0; j < n; ++j)
00452 diag[j] = (wa2[j]==0.) ? 1. : wa2[j];
00453
00454
00455
00456 xnorm = diag.cwiseProduct(x).stableNorm();
00457 delta = parameters.factor * xnorm;
00458 if (delta == 0.)
00459 delta = parameters.factor;
00460 }
00461
00462
00463 HouseholderQR<JacobianType> qrfac(fjac);
00464
00465
00466 R = qrfac.matrixQR();
00467
00468
00469 fjac = qrfac.householderQ();
00470
00471
00472 qtf = fjac.transpose() * fvec;
00473
00474
00475 if (!useExternalScaling)
00476 diag = diag.cwiseMax(wa2);
00477
00478 while (true) {
00479
00480 internal::dogleg<Scalar>(R, diag, qtf, delta, wa1);
00481
00482
00483 wa1 = -wa1;
00484 wa2 = x + wa1;
00485 pnorm = diag.cwiseProduct(wa1).stableNorm();
00486
00487
00488 if (iter == 1)
00489 delta = (std::min)(delta,pnorm);
00490
00491
00492 if ( functor(wa2, wa4) < 0)
00493 return HybridNonLinearSolverSpace::UserAsked;
00494 ++nfev;
00495 fnorm1 = wa4.stableNorm();
00496
00497
00498 actred = -1.;
00499 if (fnorm1 < fnorm)
00500 actred = 1. - numext::abs2(fnorm1 / fnorm);
00501
00502
00503 wa3 = R.template triangularView<Upper>()*wa1 + qtf;
00504 temp = wa3.stableNorm();
00505 prered = 0.;
00506 if (temp < fnorm)
00507 prered = 1. - numext::abs2(temp / fnorm);
00508
00509
00510 ratio = 0.;
00511 if (prered > 0.)
00512 ratio = actred / prered;
00513
00514
00515 if (ratio < Scalar(.1)) {
00516 ncsuc = 0;
00517 ++ncfail;
00518 delta = Scalar(.5) * delta;
00519 } else {
00520 ncfail = 0;
00521 ++ncsuc;
00522 if (ratio >= Scalar(.5) || ncsuc > 1)
00523 delta = (std::max)(delta, pnorm / Scalar(.5));
00524 if (abs(ratio - 1.) <= Scalar(.1)) {
00525 delta = pnorm / Scalar(.5);
00526 }
00527 }
00528
00529
00530 if (ratio >= Scalar(1e-4)) {
00531
00532 x = wa2;
00533 wa2 = diag.cwiseProduct(x);
00534 fvec = wa4;
00535 xnorm = wa2.stableNorm();
00536 fnorm = fnorm1;
00537 ++iter;
00538 }
00539
00540
00541 ++nslow1;
00542 if (actred >= Scalar(.001))
00543 nslow1 = 0;
00544 if (jeval)
00545 ++nslow2;
00546 if (actred >= Scalar(.1))
00547 nslow2 = 0;
00548
00549
00550 if (delta <= parameters.xtol * xnorm || fnorm == 0.)
00551 return HybridNonLinearSolverSpace::RelativeErrorTooSmall;
00552
00553
00554 if (nfev >= parameters.maxfev)
00555 return HybridNonLinearSolverSpace::TooManyFunctionEvaluation;
00556 if (Scalar(.1) * (std::max)(Scalar(.1) * delta, pnorm) <= NumTraits<Scalar>::epsilon() * xnorm)
00557 return HybridNonLinearSolverSpace::TolTooSmall;
00558 if (nslow2 == 5)
00559 return HybridNonLinearSolverSpace::NotMakingProgressJacobian;
00560 if (nslow1 == 10)
00561 return HybridNonLinearSolverSpace::NotMakingProgressIterations;
00562
00563
00564 if (ncfail == 2)
00565 break;
00566
00567
00568
00569 wa1 = diag.cwiseProduct( diag.cwiseProduct(wa1)/pnorm );
00570 wa2 = fjac.transpose() * wa4;
00571 if (ratio >= Scalar(1e-4))
00572 qtf = wa2;
00573 wa2 = (wa2-wa3)/pnorm;
00574
00575
00576 internal::r1updt<Scalar>(R, wa1, v_givens, w_givens, wa2, wa3, &sing);
00577 internal::r1mpyq<Scalar>(n, n, fjac.data(), v_givens, w_givens);
00578 internal::r1mpyq<Scalar>(1, n, qtf.data(), v_givens, w_givens);
00579
00580 jeval = false;
00581 }
00582 return HybridNonLinearSolverSpace::Running;
00583 }
00584
00585 template<typename FunctorType, typename Scalar>
00586 HybridNonLinearSolverSpace::Status
00587 HybridNonLinearSolver<FunctorType,Scalar>::solveNumericalDiff(FVectorType &x)
00588 {
00589 HybridNonLinearSolverSpace::Status status = solveNumericalDiffInit(x);
00590 if (status==HybridNonLinearSolverSpace::ImproperInputParameters)
00591 return status;
00592 while (status==HybridNonLinearSolverSpace::Running)
00593 status = solveNumericalDiffOneStep(x);
00594 return status;
00595 }
00596
00597 }
00598
00599 #endif // EIGEN_HYBRIDNONLINEARSOLVER_H
00600
00601