blob: 280333ebc0f240eb43e7cebde6a78cd09e6d2fc2 [file] [edit]
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2009 Thomas Capricelli <orzel@freehackers.org>
// Copyright (C) 2012 desire Nuentsa <desire.nuentsa_wakam@inria.fr
//
// 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/.
// SPDX-License-Identifier: MPL-2.0
// FIXME: These tests all check for hard-coded values. Ideally, parameters and start estimates should be randomized.
#include <stdio.h>
#include "main.h"
#include <contrib/Eigen/LevenbergMarquardt>
// This disables some useless Warnings on MSVC.
// It is intended to be done for this test only.
#include <Eigen/src/Core/util/DisableStupidWarnings.h>
using std::sqrt;
using LmFunctorBase = DenseFunctor<double>;
#include "lm_test_functors.h"
// tolerance for checking number of iterations
#define LM_EVAL_COUNT_TOL 2
void testLmder1() {
int n = 3, info;
VectorXd x;
/* the following starting values provide a rough fit. */
x.setConstant(n, 1.);
// do the computation
lmder_functor functor;
LevenbergMarquardt<lmder_functor> lm(functor);
info = lm.lmder1(x);
EIGEN_UNUSED_VARIABLE(info); // check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 6);
// VERIFY_IS_EQUAL(lm.njev(), 5);
// check norm
VERIFY_IS_APPROX(lm.fvec().blueNorm(), 0.09063596);
// check x
VectorXd x_ref(n);
x_ref << 0.08241058, 1.133037, 2.343695;
VERIFY_IS_APPROX(x, x_ref);
}
void testLmder() {
const int m = 15, n = 3;
int info;
double fnorm, covfac;
VectorXd x;
/* the following starting values provide a rough fit. */
x.setConstant(n, 1.);
// do the computation
lmder_functor functor;
LevenbergMarquardt<lmder_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return values
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 6);
// VERIFY_IS_EQUAL(lm.njev(), 5);
// check norm
fnorm = lm.fvec().blueNorm();
VERIFY_IS_APPROX(fnorm, 0.09063596);
// check x
VectorXd x_ref(n);
x_ref << 0.08241058, 1.133037, 2.343695;
VERIFY_IS_APPROX(x, x_ref);
// check covariance
covfac = fnorm * fnorm / (m - n);
internal::covar(lm.matrixR(), lm.permutation().indices()); // TODO : move this as a function of lm
MatrixXd cov_ref(n, n);
cov_ref << 0.0001531202, 0.002869941, -0.002656662, 0.002869941, 0.09480935, -0.09098995, -0.002656662, -0.09098995,
0.08778727;
// std::cout << fjac*covfac << std::endl;
MatrixXd cov;
cov = covfac * lm.matrixR().topLeftCorner<n, n>();
VERIFY_IS_APPROX(cov, cov_ref);
// TODO: why isn't this allowed ? :
// VERIFY_IS_APPROX( covfac*fjac.topLeftCorner<n,n>() , cov_ref);
}
void testLmdif1() {
const int n = 3;
int info;
VectorXd x(n), fvec(15);
/* the following starting values provide a rough fit. */
x.setConstant(n, 1.);
// do the computation
lmdif_functor functor;
DenseIndex nfev;
info = LevenbergMarquardt<lmdif_functor>::lmdif1(functor, x, &nfev);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(nfev, 26);
// check norm
functor(x, fvec);
VERIFY_IS_APPROX(fvec.blueNorm(), 0.09063596);
// check x
VectorXd x_ref(n);
x_ref << 0.0824106, 1.1330366, 2.3436947;
VERIFY_IS_APPROX(x, x_ref);
}
void testLmdif() {
const int m = 15, n = 3;
int info;
double fnorm, covfac;
VectorXd x(n);
/* the following starting values provide a rough fit. */
x.setConstant(n, 1.);
// do the computation
lmdif_functor functor;
NumericalDiff<lmdif_functor> numDiff(functor);
LevenbergMarquardt<NumericalDiff<lmdif_functor> > lm(numDiff);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return values
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 26);
// check norm
fnorm = lm.fvec().blueNorm();
VERIFY_IS_APPROX(fnorm, 0.09063596);
// check x
VectorXd x_ref(n);
x_ref << 0.08241058, 1.133037, 2.343695;
VERIFY_IS_APPROX(x, x_ref);
// check covariance
covfac = fnorm * fnorm / (m - n);
internal::covar(lm.matrixR(), lm.permutation().indices()); // TODO : move this as a function of lm
MatrixXd cov_ref(n, n);
cov_ref << 0.0001531202, 0.002869942, -0.002656662, 0.002869942, 0.09480937, -0.09098997, -0.002656662, -0.09098997,
0.08778729;
// std::cout << fjac*covfac << std::endl;
MatrixXd cov;
cov = covfac * lm.matrixR().topLeftCorner<n, n>();
VERIFY_IS_APPROX(cov, cov_ref);
// TODO: why isn't this allowed ? :
// VERIFY_IS_APPROX( covfac*fjac.topLeftCorner<n,n>() , cov_ref);
}
// http://www.itl.nist.gov/div898/strd/nls/data/chwirut2.shtml
void testNistChwirut2(void) {
const int n = 3;
LevenbergMarquardtSpace::Status info;
VectorXd x(n);
/*
* First try
*/
x << 0.1, 0.01, 0.02;
// do the computation
chwirut2_functor functor;
LevenbergMarquardt<chwirut2_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 10);
// VERIFY_IS_EQUAL(lm.njev(), 8);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.1304802941E+02);
// check x
VERIFY_IS_APPROX(x[0], 1.6657666537E-01);
VERIFY_IS_APPROX(x[1], 5.1653291286E-03);
VERIFY_IS_APPROX(x[2], 1.2150007096E-02);
/*
* Second try
*/
x << 0.15, 0.008, 0.010;
// do the computation
lm.resetParameters();
lm.setFtol(1.E6 * NumTraits<double>::epsilon());
lm.setXtol(1.E6 * NumTraits<double>::epsilon());
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 7);
// VERIFY_IS_EQUAL(lm.njev(), 6);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.1304802941E+02);
// check x
VERIFY_IS_APPROX(x[0], 1.6657666537E-01);
VERIFY_IS_APPROX(x[1], 5.1653291286E-03);
VERIFY_IS_APPROX(x[2], 1.2150007096E-02);
}
// http://www.itl.nist.gov/div898/strd/nls/data/misra1a.shtml
void testNistMisra1a(void) {
const int n = 2;
int info;
VectorXd x(n);
/*
* First try
*/
x << 500., 0.0001;
// do the computation
misra1a_functor functor;
LevenbergMarquardt<misra1a_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 19);
// VERIFY_IS_EQUAL(lm.njev(), 15);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.2455138894E-01);
// check x
VERIFY_IS_APPROX(x[0], 2.3894212918E+02);
VERIFY_IS_APPROX(x[1], 5.5015643181E-04);
/*
* Second try
*/
x << 250., 0.0005;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 5);
// VERIFY_IS_EQUAL(lm.njev(), 4);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.2455138894E-01);
// check x
VERIFY_IS_APPROX(x[0], 2.3894212918E+02);
VERIFY_IS_APPROX(x[1], 5.5015643181E-04);
}
// http://www.itl.nist.gov/div898/strd/nls/data/hahn1.shtml
void testNistHahn1(void) {
const int n = 7;
int info;
VectorXd x(n);
/*
* First try
*/
x << 10., -1., .05, -.00001, -.05, .001, -.000001;
// do the computation
hahn1_functor functor;
LevenbergMarquardt<hahn1_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 11);
// VERIFY_IS_EQUAL(lm.njev(), 10);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.5324382854E+00);
// check x
VERIFY_IS_APPROX(x[0], 1.0776351733E+00);
VERIFY_IS_APPROX(x[1], -1.2269296921E-01);
VERIFY_IS_APPROX(x[2], 4.0863750610E-03);
VERIFY_IS_APPROX(x[3], -1.426264e-06); // should be : -1.4262662514E-06
VERIFY_IS_APPROX(x[4], -5.7609940901E-03);
VERIFY_IS_APPROX(x[5], 2.4053735503E-04);
VERIFY_IS_APPROX(x[6], -1.2314450199E-07);
/*
* Second try
*/
x << .1, -.1, .005, -.000001, -.005, .0001, -.0000001;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 11);
// VERIFY_IS_EQUAL(lm.njev(), 10);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.5324382854E+00);
// check x
VERIFY_IS_APPROX(x[0], 1.077640); // should be : 1.0776351733E+00
VERIFY_IS_APPROX(x[1], -0.1226933); // should be : -1.2269296921E-01
VERIFY_IS_APPROX(x[2], 0.004086383); // should be : 4.0863750610E-03
VERIFY_IS_APPROX(x[3], -1.426277e-06); // should be : -1.4262662514E-06
VERIFY_IS_APPROX(x[4], -5.7609940901E-03);
VERIFY_IS_APPROX(x[5], 0.00024053772); // should be : 2.4053735503E-04
VERIFY_IS_APPROX(x[6], -1.231450e-07); // should be : -1.2314450199E-07
}
// http://www.itl.nist.gov/div898/strd/nls/data/misra1d.shtml
void testNistMisra1d(void) {
const int n = 2;
int info;
VectorXd x(n);
/*
* First try
*/
x << 500., 0.0001;
// do the computation
misra1d_functor functor;
LevenbergMarquardt<misra1d_functor> lm(functor);
info = lm.minimize(x);
// The exit must pass the xtol test, which on a rejected last step needs xnorm carried over from the last
// accepted one. Whether the ftol test passes too depends on actred / prered with both at roundoff level
// (~1e-14): x86 exits with 3, ARMv7 NEON and SVE with 2.
VERIFY(info == LevenbergMarquardtSpace::RelativeErrorTooSmall ||
info == LevenbergMarquardtSpace::RelativeErrorAndReductionTooSmall);
// VERIFY_IS_EQUAL(lm.nfev(), 9);
// VERIFY_IS_EQUAL(lm.njev(), 7);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.6419295283E-02);
// check x
VERIFY_IS_APPROX(x[0], 4.3736970754E+02);
VERIFY_IS_APPROX(x[1], 3.0227324449E-04);
/*
* Second try
*/
x << 450., 0.0003;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 4);
// VERIFY_IS_EQUAL(lm.njev(), 3);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.6419295283E-02);
// check x
VERIFY_IS_APPROX(x[0], 4.3736970754E+02);
VERIFY_IS_APPROX(x[1], 3.0227324449E-04);
}
// http://www.itl.nist.gov/div898/strd/nls/data/lanczos1.shtml
void testNistLanczos1(void) {
const int n = 6;
LevenbergMarquardtSpace::Status info;
VectorXd x(n);
/*
* First try
*/
x << 1.2, 0.3, 5.6, 5.5, 6.5, 7.6;
// do the computation
lanczos1_functor functor;
LevenbergMarquardt<lanczos1_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, LevenbergMarquardtSpace::RelativeErrorTooSmall);
// VERIFY_IS_EQUAL(lm.nfev(), 79);
// VERIFY_IS_EQUAL(lm.njev(), 72);
// check norm^2
VERIFY(lm.fvec().squaredNorm() <= 1.44E-25);
// check x
VERIFY_IS_APPROX(x[0], 9.5100000027E-02);
VERIFY_IS_APPROX(x[1], 1.0000000001E+00);
VERIFY_IS_APPROX(x[2], 8.6070000013E-01);
VERIFY_IS_APPROX(x[3], 3.0000000002E+00);
VERIFY_IS_APPROX(x[4], 1.5575999998E+00);
VERIFY_IS_APPROX(x[5], 5.0000000001E+00);
/*
* Second try
*/
x << 0.5, 0.7, 3.6, 4.2, 4., 6.3;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, LevenbergMarquardtSpace::RelativeErrorTooSmall);
// VERIFY_IS_EQUAL(lm.nfev(), 9);
// VERIFY_IS_EQUAL(lm.njev(), 8);
// check norm^2
VERIFY(lm.fvec().squaredNorm() <= 1.44E-25);
// check x
VERIFY_IS_APPROX(x[0], 9.5100000027E-02);
VERIFY_IS_APPROX(x[1], 1.0000000001E+00);
VERIFY_IS_APPROX(x[2], 8.6070000013E-01);
VERIFY_IS_APPROX(x[3], 3.0000000002E+00);
VERIFY_IS_APPROX(x[4], 1.5575999998E+00);
VERIFY_IS_APPROX(x[5], 5.0000000001E+00);
}
// http://www.itl.nist.gov/div898/strd/nls/data/ratkowsky2.shtml
void testNistRat42(void) {
const int n = 3;
LevenbergMarquardtSpace::Status info;
VectorXd x(n);
/*
* First try
*/
x << 100., 1., 0.1;
// do the computation
rat42_functor functor;
LevenbergMarquardt<rat42_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, LevenbergMarquardtSpace::RelativeReductionTooSmall);
// VERIFY_IS_EQUAL(lm.nfev(), 10);
// VERIFY_IS_EQUAL(lm.njev(), 8);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.0565229338E+00);
// check x
VERIFY_IS_APPROX(x[0], 7.2462237576E+01);
VERIFY_IS_APPROX(x[1], 2.6180768402E+00);
VERIFY_IS_APPROX(x[2], 6.7359200066E-02);
/*
* Second try
*/
x << 75., 2.5, 0.07;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, LevenbergMarquardtSpace::RelativeReductionTooSmall);
// VERIFY_IS_EQUAL(lm.nfev(), 6);
// VERIFY_IS_EQUAL(lm.njev(), 5);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.0565229338E+00);
// check x
VERIFY_IS_APPROX(x[0], 7.2462237576E+01);
VERIFY_IS_APPROX(x[1], 2.6180768402E+00);
VERIFY_IS_APPROX(x[2], 6.7359200066E-02);
}
// http://www.itl.nist.gov/div898/strd/nls/data/mgh10.shtml
void testNistMGH10(void) {
const int n = 3;
LevenbergMarquardtSpace::Status info;
VectorXd x(n);
/*
* First try
*/
x << 2., 400000., 25000.;
// do the computation
MGH10_functor functor;
LevenbergMarquardt<MGH10_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info); // ++g_test_level;
// VERIFY_IS_EQUAL(info, LevenbergMarquardtSpace::RelativeReductionTooSmall);
// --g_test_level;
// was: VERIFY_IS_EQUAL(info, 1);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7945855171E+01);
// check x
VERIFY_IS_APPROX(x[0], 5.6096364710E-03);
VERIFY_IS_APPROX(x[1], 6.1813463463E+03);
VERIFY_IS_APPROX(x[2], 3.4522363462E+02);
// check return value
// ++g_test_level;
// VERIFY_IS_EQUAL(lm.nfev(), 284 );
// VERIFY_IS_EQUAL(lm.njev(), 249 );
// --g_test_level;
VERIFY(lm.nfev() < 284 * LM_EVAL_COUNT_TOL);
VERIFY(lm.njev() < 249 * LM_EVAL_COUNT_TOL);
/*
* Second try
*/
x << 0.02, 4000., 250.;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info); // ++g_test_level;
// VERIFY_IS_EQUAL(info, LevenbergMarquardtSpace::RelativeReductionTooSmall);
// // was: VERIFY_IS_EQUAL(info, 1);
// --g_test_level;
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7945855171E+01);
// check x
VERIFY_IS_APPROX(x[0], 5.6096364710E-03);
VERIFY_IS_APPROX(x[1], 6.1813463463E+03);
VERIFY_IS_APPROX(x[2], 3.4522363462E+02);
// check return value
// ++g_test_level;
// VERIFY_IS_EQUAL(lm.nfev(), 126);
// VERIFY_IS_EQUAL(lm.njev(), 116);
// --g_test_level;
VERIFY(lm.nfev() < 126 * LM_EVAL_COUNT_TOL);
VERIFY(lm.njev() < 116 * LM_EVAL_COUNT_TOL);
}
// http://www.itl.nist.gov/div898/strd/nls/data/boxbod.shtml
void testNistBoxBOD(void) {
const int n = 2;
int info;
VectorXd x(n);
/*
* First try
*/
x << 1., 1.;
// do the computation
BoxBOD_functor functor;
LevenbergMarquardt<BoxBOD_functor> lm(functor);
lm.setFtol(1.E6 * NumTraits<double>::epsilon());
lm.setXtol(1.E6 * NumTraits<double>::epsilon());
lm.setFactor(10);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.1680088766E+03);
// check x
VERIFY_IS_APPROX(x[0], 2.1380940889E+02);
VERIFY_IS_APPROX(x[1], 5.4723748542E-01);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY(lm.nfev() < 31); // 31
// VERIFY(lm.njev() < 25); // 25
/*
* Second try
*/
x << 100., 0.75;
// do the computation
lm.resetParameters();
lm.setFtol(NumTraits<double>::epsilon());
lm.setXtol(NumTraits<double>::epsilon());
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// ++g_test_level;
// VERIFY_IS_EQUAL(lm.nfev(), 16 );
// VERIFY_IS_EQUAL(lm.njev(), 15 );
// --g_test_level;
VERIFY(lm.nfev() < 16 * LM_EVAL_COUNT_TOL);
VERIFY(lm.njev() < 15 * LM_EVAL_COUNT_TOL);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.1680088766E+03);
// check x
VERIFY_IS_APPROX(x[0], 2.1380940889E+02);
VERIFY_IS_APPROX(x[1], 5.4723748542E-01);
}
// http://www.itl.nist.gov/div898/strd/nls/data/mgh17.shtml
void testNistMGH17(void) {
const int n = 5;
int info;
VectorXd x(n);
/*
* First try
*/
x << 50., 150., -100., 1., 2.;
// do the computation
MGH17_functor functor;
LevenbergMarquardt<MGH17_functor> lm(functor);
lm.setFtol(NumTraits<double>::epsilon());
lm.setXtol(NumTraits<double>::epsilon());
lm.setMaxfev(1000);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.4648946975E-05);
// check x
VERIFY_IS_APPROX(x[0], 3.7541005211E-01);
VERIFY_IS_APPROX(x[1], 1.9358469127E+00);
VERIFY_IS_APPROX(x[2], -1.4646871366E+00);
VERIFY_IS_APPROX(x[3], 1.2867534640E-02);
VERIFY_IS_APPROX(x[4], 2.2122699662E-02);
// check return value
// VERIFY_IS_EQUAL(info, 2); //FIXME Use (lm.info() == Success)
// VERIFY(lm.nfev() < 700 ); // 602
// VERIFY(lm.njev() < 600 ); // 545
/*
* Second try
*/
x << 0.5, 1.5, -1, 0.01, 0.02;
// do the computation
lm.resetParameters();
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 18);
// VERIFY_IS_EQUAL(lm.njev(), 15);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.4648946975E-05);
// check x
VERIFY_IS_APPROX(x[0], 3.7541005211E-01);
VERIFY_IS_APPROX(x[1], 1.9358469127E+00);
VERIFY_IS_APPROX(x[2], -1.4646871366E+00);
VERIFY_IS_APPROX(x[3], 1.2867534640E-02);
VERIFY_IS_APPROX(x[4], 2.2122699662E-02);
}
// http://www.itl.nist.gov/div898/strd/nls/data/mgh09.shtml
void testNistMGH09(void) {
const int n = 4;
int info;
VectorXd x(n);
/*
* First try
*/
x << 25., 39, 41.5, 39.;
// do the computation
MGH09_functor functor;
LevenbergMarquardt<MGH09_functor> lm(functor);
lm.setMaxfev(1000);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 3.0750560385E-04);
// check x
VERIFY_IS_APPROX(x[0], 0.1928077089); // should be 1.9280693458E-01
VERIFY_IS_APPROX(x[1], 0.19126423573); // should be 1.9128232873E-01
VERIFY_IS_APPROX(x[2], 0.12305309914); // should be 1.2305650693E-01
VERIFY_IS_APPROX(x[3], 0.13605395375); // should be 1.3606233068E-01
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY(lm.nfev() < 510 ); // 490
// VERIFY(lm.njev() < 400 ); // 376
/*
* Second try
*/
x << 0.25, 0.39, 0.415, 0.39;
// do the computation
lm.resetParameters();
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 18);
// VERIFY_IS_EQUAL(lm.njev(), 16);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 3.0750560385E-04);
// check x
VERIFY_IS_APPROX(x[0], 0.19280781); // should be 1.9280693458E-01
VERIFY_IS_APPROX(x[1], 0.19126265); // should be 1.9128232873E-01
VERIFY_IS_APPROX(x[2], 0.12305280); // should be 1.2305650693E-01
VERIFY_IS_APPROX(x[3], 0.13605322); // should be 1.3606233068E-01
}
// http://www.itl.nist.gov/div898/strd/nls/data/bennett5.shtml
void testNistBennett5(void) {
const int n = 3;
int info;
VectorXd x(n);
/*
* First try
*/
x << -2000., 50., 0.8;
// do the computation
Bennett5_functor functor;
LevenbergMarquardt<Bennett5_functor> lm(functor);
lm.setMaxfev(1000);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 758);
// VERIFY_IS_EQUAL(lm.njev(), 744);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.2404744073E-04);
// check x
VERIFY_IS_APPROX(x[0], -2.5235058043E+03);
VERIFY_IS_APPROX(x[1], 4.6736564644E+01);
VERIFY_IS_APPROX(x[2], 9.3218483193E-01);
/*
* Second try
*/
x << -1500., 45., 0.85;
// do the computation
lm.resetParameters();
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 203);
// VERIFY_IS_EQUAL(lm.njev(), 192);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.2404744073E-04);
// check x
VERIFY_IS_APPROX(x[0], -2523.3007865); // should be -2.5235058043E+03
VERIFY_IS_APPROX(x[1], 46.735705771); // should be 4.6736564644E+01);
VERIFY_IS_APPROX(x[2], 0.93219881891); // should be 9.3218483193E-01);
}
// http://www.itl.nist.gov/div898/strd/nls/data/thurber.shtml
void testNistThurber(void) {
const int n = 7;
int info;
VectorXd x(n);
/*
* First try
*/
x << 1000, 1000, 400, 40, 0.7, 0.3, 0.0;
// do the computation
thurber_functor functor;
LevenbergMarquardt<thurber_functor> lm(functor);
lm.setFtol(1.E4 * NumTraits<double>::epsilon());
lm.setXtol(1.E4 * NumTraits<double>::epsilon());
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 39);
// VERIFY_IS_EQUAL(lm.njev(), 36);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.6427082397E+03);
// check x
VERIFY_IS_APPROX(x[0], 1.2881396800E+03);
VERIFY_IS_APPROX(x[1], 1.4910792535E+03);
VERIFY_IS_APPROX(x[2], 5.8323836877E+02);
VERIFY_IS_APPROX(x[3], 7.5416644291E+01);
VERIFY_IS_APPROX(x[4], 9.6629502864E-01);
VERIFY_IS_APPROX(x[5], 3.9797285797E-01);
VERIFY_IS_APPROX(x[6], 4.9727297349E-02);
/*
* Second try
*/
x << 1300, 1500, 500, 75, 1, 0.4, 0.05;
// do the computation
lm.resetParameters();
lm.setFtol(1.E4 * NumTraits<double>::epsilon());
lm.setXtol(1.E4 * NumTraits<double>::epsilon());
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 29);
// VERIFY_IS_EQUAL(lm.njev(), 28);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 5.6427082397E+03);
// check x
VERIFY_IS_APPROX(x[0], 1.2881396800E+03);
VERIFY_IS_APPROX(x[1], 1.4910792535E+03);
VERIFY_IS_APPROX(x[2], 5.8323836877E+02);
VERIFY_IS_APPROX(x[3], 7.5416644291E+01);
VERIFY_IS_APPROX(x[4], 9.6629502864E-01);
VERIFY_IS_APPROX(x[5], 3.9797285797E-01);
VERIFY_IS_APPROX(x[6], 4.9727297349E-02);
}
// http://www.itl.nist.gov/div898/strd/nls/data/ratkowsky3.shtml
void testNistRat43(void) {
const int n = 4;
int info;
VectorXd x(n);
/*
* First try
*/
x << 100., 10., 1., 1.;
// do the computation
rat43_functor functor;
LevenbergMarquardt<rat43_functor> lm(functor);
lm.setFtol(1.E6 * NumTraits<double>::epsilon());
lm.setXtol(1.E6 * NumTraits<double>::epsilon());
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 27);
// VERIFY_IS_EQUAL(lm.njev(), 20);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7864049080E+03);
// check x
VERIFY_IS_APPROX(x[0], 6.9964151270E+02);
VERIFY_IS_APPROX(x[1], 5.2771253025E+00);
VERIFY_IS_APPROX(x[2], 7.5962938329E-01);
VERIFY_IS_APPROX(x[3], 1.2792483859E+00);
/*
* Second try
*/
x << 700., 5., 0.75, 1.3;
// do the computation
lm.resetParameters();
lm.setFtol(1.E5 * NumTraits<double>::epsilon());
lm.setXtol(1.E5 * NumTraits<double>::epsilon());
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 9);
// VERIFY_IS_EQUAL(lm.njev(), 8);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 8.7864049080E+03);
// check x
VERIFY_IS_APPROX(x[0], 6.9964151270E+02);
VERIFY_IS_APPROX(x[1], 5.2771253025E+00);
VERIFY_IS_APPROX(x[2], 7.5962938329E-01);
VERIFY_IS_APPROX(x[3], 1.2792483859E+00);
}
// http://www.itl.nist.gov/div898/strd/nls/data/eckerle4.shtml
void testNistEckerle4(void) {
const int n = 3;
int info;
VectorXd x(n);
/*
* First try
*/
x << 1., 10., 500.;
// do the computation
eckerle4_functor functor;
LevenbergMarquardt<eckerle4_functor> lm(functor);
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 18);
// VERIFY_IS_EQUAL(lm.njev(), 15);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.4635887487E-03);
// check x
VERIFY_IS_APPROX(x[0], 1.5543827178);
VERIFY_IS_APPROX(x[1], 4.0888321754);
VERIFY_IS_APPROX(x[2], 4.5154121844E+02);
/*
* Second try
*/
x << 1.5, 5., 450.;
// do the computation
info = lm.minimize(x);
EIGEN_UNUSED_VARIABLE(info);
// check return value
// VERIFY_IS_EQUAL(info, 1);
// VERIFY_IS_EQUAL(lm.nfev(), 7);
// VERIFY_IS_EQUAL(lm.njev(), 6);
// check norm^2
VERIFY_IS_APPROX(lm.fvec().squaredNorm(), 1.4635887487E-03);
// check x
VERIFY_IS_APPROX(x[0], 1.5543827178);
VERIFY_IS_APPROX(x[1], 4.0888321754);
VERIFY_IS_APPROX(x[2], 4.5154121844E+02);
}
// covar() takes the rank to end at the first pivot below tol * |r(0,0)| (MINPACK) and zeroes the rest;
// a NaN pivot is not negligible, so it reaches the result instead of reading as zero variance.
void testCovarRankDeficient() {
MatrixXd r(3, 3);
r << 2, 1, 1, 0, 1e-20, 1, 0, 0, 3;
VectorXi ipvt(3);
ipvt << 0, 1, 2;
internal::covar(r, ipvt);
MatrixXd expected = MatrixXd::Zero(3, 3);
expected(0, 0) = 0.25;
VERIFY_IS_APPROX(r, expected);
r << 2, 1, 1, 0, NumTraits<double>::quiet_NaN(), 1, 0, 0, 3;
internal::covar(r, ipvt);
VERIFY(r.hasNaN());
}
// lmqrsolv() must minimize ||[R; D] z - [qtb; 0]|| and leave the eliminated
// factor in the strict lower triangle of s for lmpar2(). Issue #657: the QR is
// rank-revealing, so R carries zeros above the diagonal, and the Givens
// rotation that eliminates a row of D acts on the whole row: it rotates sdiag
// against those zeros and fills them in.
void testLmqrsolv() {
const Index n = 6;
// An upper triangular factor with zeros above the diagonal.
MatrixXd r = MatrixXd::Zero(n, n);
for (Index i = 0; i < n; ++i)
for (Index j = i; j < n; ++j)
r(i, j) = ((i == 0 && j == 3) || (i == 1 && j == 4)) ? 0.0 : 1.0 + 0.5 * double(i + 1) * double(j + 1);
VectorXd diag(n), qtb(n);
for (Index i = 0; i < n; ++i) {
diag(i) = 0.5 + 0.25 * double(i);
qtb(i) = 1.0 - 0.3 * double(i);
}
PermutationMatrix<Dynamic, Dynamic, int> perm(n);
perm.setIdentity();
MatrixXd s = r;
VectorXd x(n), sdiag(n);
internal::lmqrsolv(s, perm, diag, qtb, x, sdiag);
MatrixXd augmented(2 * n, n);
augmented.topRows(n) = r;
augmented.bottomRows(n) = diag.asDiagonal();
VectorXd rhs = VectorXd::Zero(2 * n);
rhs.head(n) = qtb;
VERIFY_IS_APPROX(x, augmented.colPivHouseholderQr().solve(rhs));
// The upper triangle and the diagonal of s are restored. lmpar2() reads the
// eliminated factor S out of the strict lower triangle of s, transposed, with
// its diagonal in sdiag; S is the triangular factor of [R; D].
VERIFY_IS_APPROX(MatrixXd(s.triangularView<Upper>()), r);
MatrixXd eliminated = MatrixXd(s.triangularView<StrictlyLower>()).transpose();
eliminated.diagonal() = sdiag;
VERIFY_IS_APPROX(MatrixXd(eliminated.transpose() * eliminated),
MatrixXd(r.transpose() * r + MatrixXd(diag.cwiseAbs2().asDiagonal())));
}
// Exponential decay with two exactly collinear offsets x2 and x3, so the
// Jacobian is rank deficient and the triangular factor of its sparse QR loses
// its last diagonal entry. Issue #657: the sparse solver then asserted inside
// Eigen::internal::sparse_solve_triangular_selector.
struct collinear_offsets_functor : SparseFunctor<double, int> {
collinear_offsets_functor(int m) : SparseFunctor<double, int>(4, m), m_t(m), m_y(m) {
for (int i = 0; i < m; ++i) {
m_t(i) = 0.05 * i;
m_y(i) = 2.5 * std::exp(-0.7 * m_t(i)) + 0.3;
}
}
int operator()(const VectorXd &x, VectorXd &fvec) {
for (int i = 0; i < values(); ++i) fvec(i) = x(0) * std::exp(-x(1) * m_t(i)) + x(2) + x(3) - m_y(i);
return 0;
}
int df(const VectorXd &x, JacobianType &jac) {
std::vector<Triplet<double> > triplets;
for (int i = 0; i < values(); ++i) {
const double e = std::exp(-x(1) * m_t(i));
triplets.push_back(Triplet<double>(i, 0, e));
triplets.push_back(Triplet<double>(i, 1, -x(0) * m_t(i) * e));
triplets.push_back(Triplet<double>(i, 2, 1.0));
triplets.push_back(Triplet<double>(i, 3, 1.0));
}
jac.resize(values(), inputs());
jac.setFromTriplets(triplets.begin(), triplets.end());
return 0;
}
VectorXd m_t, m_y;
};
void testSparseFunctor() {
collinear_offsets_functor functor(30);
LevenbergMarquardt<collinear_offsets_functor> lm(functor);
VectorXd x(4);
x << 5.0, 5.0, 5.0, 5.0;
const LevenbergMarquardtSpace::Status info = lm.minimize(x);
VERIFY(info > LevenbergMarquardtSpace::ImproperInputParameters &&
info < LevenbergMarquardtSpace::TooManyFunctionEvaluation);
// x2 and x3 are determined only through their sum.
VERIFY_IS_APPROX(x(0), 2.5);
VERIFY_IS_APPROX(x(1), 0.7);
VERIFY_IS_APPROX(x(2) + x(3), 0.3);
VERIFY(lm.fnorm() <= 1e3 * NumTraits<double>::epsilon() * functor.m_y.norm());
}
EIGEN_DECLARE_TEST(levenberg_marquardt) {
CALL_SUBTEST(testLmqrsolv());
CALL_SUBTEST(testCovarRankDeficient());
CALL_SUBTEST(testSparseFunctor());
// Tests using the examples provided by (c)minpack
CALL_SUBTEST(testLmder1());
CALL_SUBTEST(testLmder());
CALL_SUBTEST(testLmdif1());
// CALL_SUBTEST(testLmstr1());
// CALL_SUBTEST(testLmstr());
CALL_SUBTEST(testLmdif());
// NIST tests, level of difficulty = "Lower"
CALL_SUBTEST(testNistMisra1a());
CALL_SUBTEST(testNistChwirut2());
// NIST tests, level of difficulty = "Average"
CALL_SUBTEST(testNistHahn1());
CALL_SUBTEST(testNistMisra1d());
CALL_SUBTEST(testNistMGH17());
CALL_SUBTEST(testNistLanczos1());
// // NIST tests, level of difficulty = "Higher"
CALL_SUBTEST(testNistRat42());
CALL_SUBTEST(testNistMGH10());
CALL_SUBTEST(testNistBoxBOD());
// CALL_SUBTEST(testNistMGH09());
CALL_SUBTEST(testNistBennett5());
CALL_SUBTEST(testNistThurber());
CALL_SUBTEST(testNistRat43());
CALL_SUBTEST(testNistEckerle4());
}