|
Packit |
ea1746 |
// Ceres Solver - A fast non-linear least squares minimizer
|
|
Packit |
ea1746 |
// Copyright 2015 Google Inc. All rights reserved.
|
|
Packit |
ea1746 |
// http://ceres-solver.org/
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Redistribution and use in source and binary forms, with or without
|
|
Packit |
ea1746 |
// modification, are permitted provided that the following conditions are met:
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// * Redistributions of source code must retain the above copyright notice,
|
|
Packit |
ea1746 |
// this list of conditions and the following disclaimer.
|
|
Packit |
ea1746 |
// * Redistributions in binary form must reproduce the above copyright notice,
|
|
Packit |
ea1746 |
// this list of conditions and the following disclaimer in the documentation
|
|
Packit |
ea1746 |
// and/or other materials provided with the distribution.
|
|
Packit |
ea1746 |
// * Neither the name of Google Inc. nor the names of its contributors may be
|
|
Packit |
ea1746 |
// used to endorse or promote products derived from this software without
|
|
Packit |
ea1746 |
// specific prior written permission.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
Packit |
ea1746 |
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
Packit |
ea1746 |
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
Packit |
ea1746 |
// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
|
|
Packit |
ea1746 |
// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
Packit |
ea1746 |
// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
Packit |
ea1746 |
// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
Packit |
ea1746 |
// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
Packit |
ea1746 |
// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
Packit |
ea1746 |
// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
Packit |
ea1746 |
// POSSIBILITY OF SUCH DAMAGE.
|
|
Packit |
ea1746 |
//
|
|
Packit |
ea1746 |
// Author: sameeragarwal@google.com (Sameer Agarwal)
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include "ceres/corrector.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
#include <algorithm>
|
|
Packit |
ea1746 |
#include <cmath>
|
|
Packit |
ea1746 |
#include <cstring>
|
|
Packit |
ea1746 |
#include <cstdlib>
|
|
Packit |
ea1746 |
#include "gtest/gtest.h"
|
|
Packit |
ea1746 |
#include "ceres/random.h"
|
|
Packit |
ea1746 |
#include "ceres/internal/eigen.h"
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
namespace ceres {
|
|
Packit |
ea1746 |
namespace internal {
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// If rho[1] is zero, the Corrector constructor should crash.
|
|
Packit |
ea1746 |
TEST(Corrector, ZeroGradientDeathTest) {
|
|
Packit |
ea1746 |
const double kRho[] = {0.0, 0.0, 1.0};
|
|
Packit |
ea1746 |
EXPECT_DEATH_IF_SUPPORTED({Corrector c(1.0, kRho);},
|
|
Packit |
ea1746 |
".*");
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// If rho[1] is negative, the Corrector constructor should crash.
|
|
Packit |
ea1746 |
TEST(Corrector, NegativeGradientDeathTest) {
|
|
Packit |
ea1746 |
const double kRho[] = {0.0, -0.1, 1.0};
|
|
Packit |
ea1746 |
EXPECT_DEATH_IF_SUPPORTED({Corrector c(1.0, kRho);},
|
|
Packit |
ea1746 |
".*");
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TEST(Corrector, ScalarCorrection) {
|
|
Packit |
ea1746 |
double residuals = sqrt(3.0);
|
|
Packit |
ea1746 |
double jacobian = 10.0;
|
|
Packit |
ea1746 |
double sq_norm = residuals * residuals;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double kRho[] = {sq_norm, 0.1, -0.01};
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// In light of the rho'' < 0 clamping now implemented in
|
|
Packit |
ea1746 |
// corrector.cc, alpha = 0 whenever rho'' < 0.
|
|
Packit |
ea1746 |
const double kAlpha = 0.0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Thus the expected value of the residual is
|
|
Packit |
ea1746 |
// residual[i] * sqrt(kRho[1]) / (1.0 - kAlpha).
|
|
Packit |
ea1746 |
const double kExpectedResidual =
|
|
Packit |
ea1746 |
residuals * sqrt(kRho[1]) / (1 - kAlpha);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// The jacobian in this case will be
|
|
Packit |
ea1746 |
// sqrt(kRho[1]) * (1 - kAlpha) * jacobian.
|
|
Packit |
ea1746 |
const double kExpectedJacobian = sqrt(kRho[1]) * (1 - kAlpha) * jacobian;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
Corrector c(sq_norm, kRho);
|
|
Packit |
ea1746 |
c.CorrectJacobian(1.0, 1.0, &residuals, &jacobian);
|
|
Packit |
ea1746 |
c.CorrectResiduals(1.0, &residuals);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR(residuals, kExpectedResidual, 1e-6);
|
|
Packit |
ea1746 |
ASSERT_NEAR(kExpectedJacobian, jacobian, 1e-6);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TEST(Corrector, ScalarCorrectionZeroResidual) {
|
|
Packit |
ea1746 |
double residuals = 0.0;
|
|
Packit |
ea1746 |
double jacobian = 10.0;
|
|
Packit |
ea1746 |
double sq_norm = residuals * residuals;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double kRho[] = {0.0, 0.1, -0.01};
|
|
Packit |
ea1746 |
Corrector c(sq_norm, kRho);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// The alpha equation is
|
|
Packit |
ea1746 |
// 1/2 alpha^2 - alpha + 0.0 = 0.
|
|
Packit |
ea1746 |
// i.e. alpha = 1.0 - sqrt(1.0).
|
|
Packit |
ea1746 |
// alpha = 0.0.
|
|
Packit |
ea1746 |
// Thus the expected value of the residual is
|
|
Packit |
ea1746 |
// residual[i] * sqrt(kRho[1])
|
|
Packit |
ea1746 |
const double kExpectedResidual = residuals * sqrt(kRho[1]);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// The jacobian in this case will be
|
|
Packit |
ea1746 |
// sqrt(kRho[1]) * jacobian.
|
|
Packit |
ea1746 |
const double kExpectedJacobian = sqrt(kRho[1]) * jacobian;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
c.CorrectJacobian(1, 1, &residuals, &jacobian);
|
|
Packit |
ea1746 |
c.CorrectResiduals(1, &residuals);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR(residuals, kExpectedResidual, 1e-6);
|
|
Packit |
ea1746 |
ASSERT_NEAR(kExpectedJacobian, jacobian, 1e-6);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Scaling behaviour for one dimensional functions.
|
|
Packit |
ea1746 |
TEST(Corrector, ScalarCorrectionAlphaClamped) {
|
|
Packit |
ea1746 |
double residuals = sqrt(3.0);
|
|
Packit |
ea1746 |
double jacobian = 10.0;
|
|
Packit |
ea1746 |
double sq_norm = residuals * residuals;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double kRho[] = {3, 0.1, -0.1};
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// rho[2] < 0 -> alpha = 0.0
|
|
Packit |
ea1746 |
const double kAlpha = 0.0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Thus the expected value of the residual is
|
|
Packit |
ea1746 |
// residual[i] * sqrt(kRho[1]) / (1.0 - kAlpha).
|
|
Packit |
ea1746 |
const double kExpectedResidual =
|
|
Packit |
ea1746 |
residuals * sqrt(kRho[1]) / (1.0 - kAlpha);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// The jacobian in this case will be scaled by
|
|
Packit |
ea1746 |
// sqrt(rho[1]) * (1 - alpha) * J.
|
|
Packit |
ea1746 |
const double kExpectedJacobian = sqrt(kRho[1]) *
|
|
Packit |
ea1746 |
(1.0 - kAlpha) * jacobian;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
Corrector c(sq_norm, kRho);
|
|
Packit |
ea1746 |
c.CorrectJacobian(1, 1, &residuals, &jacobian);
|
|
Packit |
ea1746 |
c.CorrectResiduals(1, &residuals);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR(residuals, kExpectedResidual, 1e-6);
|
|
Packit |
ea1746 |
ASSERT_NEAR(kExpectedJacobian, jacobian, 1e-6);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Test that the corrected multidimensional residual and jacobians
|
|
Packit |
ea1746 |
// match the expected values and the resulting modified normal
|
|
Packit |
ea1746 |
// equations match the robustified gauss newton approximation.
|
|
Packit |
ea1746 |
TEST(Corrector, MultidimensionalGaussNewtonApproximation) {
|
|
Packit |
ea1746 |
double residuals[3];
|
|
Packit |
ea1746 |
double jacobian[2 * 3];
|
|
Packit |
ea1746 |
double rho[3];
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Eigen matrix references for linear algebra.
|
|
Packit |
ea1746 |
MatrixRef jac(jacobian, 3, 2);
|
|
Packit |
ea1746 |
VectorRef res(residuals, 3);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Ground truth values of the modified jacobian and residuals.
|
|
Packit |
ea1746 |
Matrix g_jac(3, 2);
|
|
Packit |
ea1746 |
Vector g_res(3);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Ground truth values of the robustified Gauss-Newton
|
|
Packit |
ea1746 |
// approximation.
|
|
Packit |
ea1746 |
Matrix g_hess(2, 2);
|
|
Packit |
ea1746 |
Vector g_grad(2);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Corrected hessian and gradient implied by the modified jacobian
|
|
Packit |
ea1746 |
// and hessians.
|
|
Packit |
ea1746 |
Matrix c_hess(2, 2);
|
|
Packit |
ea1746 |
Vector c_grad(2);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
srand(5);
|
|
Packit |
ea1746 |
for (int iter = 0; iter < 10000; ++iter) {
|
|
Packit |
ea1746 |
// Initialize the jacobian and residual.
|
|
Packit |
ea1746 |
for (int i = 0; i < 2 * 3; ++i)
|
|
Packit |
ea1746 |
jacobian[i] = RandDouble();
|
|
Packit |
ea1746 |
for (int i = 0; i < 3; ++i)
|
|
Packit |
ea1746 |
residuals[i] = RandDouble();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double sq_norm = res.dot(res);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
rho[0] = sq_norm;
|
|
Packit |
ea1746 |
rho[1] = RandDouble();
|
|
Packit |
ea1746 |
rho[2] = 2.0 * RandDouble() - 1.0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// If rho[2] > 0, then the curvature correction to the correction
|
|
Packit |
ea1746 |
// and the gauss newton approximation will match. Otherwise, we
|
|
Packit |
ea1746 |
// will clamp alpha to 0.
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double kD = 1 + 2 * rho[2] / rho[1] * sq_norm;
|
|
Packit |
ea1746 |
const double kAlpha = (rho[2] > 0.0) ? 1 - sqrt(kD) : 0.0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Ground truth values.
|
|
Packit |
ea1746 |
g_res = sqrt(rho[1]) / (1.0 - kAlpha) * res;
|
|
Packit |
ea1746 |
g_jac = sqrt(rho[1]) * (jac - kAlpha / sq_norm *
|
|
Packit |
ea1746 |
res * res.transpose() * jac);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
g_grad = rho[1] * jac.transpose() * res;
|
|
Packit |
ea1746 |
g_hess = rho[1] * jac.transpose() * jac +
|
|
Packit |
ea1746 |
2.0 * rho[2] * jac.transpose() * res * res.transpose() * jac;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
Corrector c(sq_norm, rho);
|
|
Packit |
ea1746 |
c.CorrectJacobian(3, 2, residuals, jacobian);
|
|
Packit |
ea1746 |
c.CorrectResiduals(3, residuals);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Corrected gradient and hessian.
|
|
Packit |
ea1746 |
c_grad = jac.transpose() * res;
|
|
Packit |
ea1746 |
c_hess = jac.transpose() * jac;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_res - res).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_jac - jac).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_grad - c_grad).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
TEST(Corrector, MultidimensionalGaussNewtonApproximationZeroResidual) {
|
|
Packit |
ea1746 |
double residuals[3];
|
|
Packit |
ea1746 |
double jacobian[2 * 3];
|
|
Packit |
ea1746 |
double rho[3];
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Eigen matrix references for linear algebra.
|
|
Packit |
ea1746 |
MatrixRef jac(jacobian, 3, 2);
|
|
Packit |
ea1746 |
VectorRef res(residuals, 3);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Ground truth values of the modified jacobian and residuals.
|
|
Packit |
ea1746 |
Matrix g_jac(3, 2);
|
|
Packit |
ea1746 |
Vector g_res(3);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Ground truth values of the robustified Gauss-Newton
|
|
Packit |
ea1746 |
// approximation.
|
|
Packit |
ea1746 |
Matrix g_hess(2, 2);
|
|
Packit |
ea1746 |
Vector g_grad(2);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Corrected hessian and gradient implied by the modified jacobian
|
|
Packit |
ea1746 |
// and hessians.
|
|
Packit |
ea1746 |
Matrix c_hess(2, 2);
|
|
Packit |
ea1746 |
Vector c_grad(2);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
srand(5);
|
|
Packit |
ea1746 |
for (int iter = 0; iter < 10000; ++iter) {
|
|
Packit |
ea1746 |
// Initialize the jacobian.
|
|
Packit |
ea1746 |
for (int i = 0; i < 2 * 3; ++i)
|
|
Packit |
ea1746 |
jacobian[i] = RandDouble();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Zero residuals
|
|
Packit |
ea1746 |
res.setZero();
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
const double sq_norm = res.dot(res);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
rho[0] = sq_norm;
|
|
Packit |
ea1746 |
rho[1] = RandDouble();
|
|
Packit |
ea1746 |
rho[2] = 2 * RandDouble() - 1.0;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Ground truth values.
|
|
Packit |
ea1746 |
g_res = sqrt(rho[1]) * res;
|
|
Packit |
ea1746 |
g_jac = sqrt(rho[1]) * jac;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
g_grad = rho[1] * jac.transpose() * res;
|
|
Packit |
ea1746 |
g_hess = rho[1] * jac.transpose() * jac +
|
|
Packit |
ea1746 |
2.0 * rho[2] * jac.transpose() * res * res.transpose() * jac;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
Corrector c(sq_norm, rho);
|
|
Packit |
ea1746 |
c.CorrectJacobian(3, 2, residuals, jacobian);
|
|
Packit |
ea1746 |
c.CorrectResiduals(3, residuals);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
// Corrected gradient and hessian.
|
|
Packit |
ea1746 |
c_grad = jac.transpose() * res;
|
|
Packit |
ea1746 |
c_hess = jac.transpose() * jac;
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_res - res).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_jac - jac).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_grad - c_grad).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
ASSERT_NEAR((g_hess - c_hess).norm(), 0.0, 1e-10);
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
}
|
|
Packit |
ea1746 |
|
|
Packit |
ea1746 |
} // namespace internal
|
|
Packit |
ea1746 |
} // namespace ceres
|