| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173 | // Ceres Solver - A fast non-linear least squares minimizer// Copyright 2018 Google Inc. All rights reserved.// http://ceres-solver.org///// Redistribution and use in source and binary forms, with or without// modification, are permitted provided that the following conditions are met://// * Redistributions of source code must retain the above copyright notice,//   this list of conditions and the following disclaimer.// * Redistributions in binary form must reproduce the above copyright notice,//   this list of conditions and the following disclaimer in the documentation//   and/or other materials provided with the distribution.// * Neither the name of Google Inc. nor the names of its contributors may be//   used to endorse or promote products derived from this software without//   specific prior written permission.//// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE// POSSIBILITY OF SUCH DAMAGE.//// Author: sameeragarwal@google.com (Sameer Agarwal)#include "ceres/iterative_refiner.h"#include "Eigen/Dense"#include "ceres/internal/eigen.h"#include "ceres/sparse_cholesky.h"#include "ceres/sparse_matrix.h"#include "glog/logging.h"#include "gtest/gtest.h"namespace ceres {namespace internal {// Macros to help us define virtual methods which we do not expect to// use/call in this test.#define DO_NOT_CALL \  { LOG(FATAL) << "DO NOT CALL"; }#define DO_NOT_CALL_WITH_RETURN(x) \  {                                \    LOG(FATAL) << "DO NOT CALL";   \    return x;                      \  }// A fake SparseMatrix, which uses an Eigen matrix to do the real work.class FakeSparseMatrix : public SparseMatrix { public:  FakeSparseMatrix(const Matrix& m) : m_(m) {}  virtual ~FakeSparseMatrix() {}  // y += Ax  void RightMultiply(const double* x, double* y) const final {    VectorRef(y, m_.cols()) += m_ * ConstVectorRef(x, m_.cols());  }  // y += A'x  void LeftMultiply(const double* x, double* y) const final {    // We will assume that this is a symmetric matrix.    RightMultiply(x, y);  }  double* mutable_values() final { return m_.data(); }  const double* values() const final { return m_.data(); }  int num_rows() const final { return m_.cols(); }  int num_cols() const final { return m_.cols(); }  int num_nonzeros() const final { return m_.cols() * m_.cols(); }  // The following methods are not needed for tests in this file.  void SquaredColumnNorm(double* x) const final DO_NOT_CALL;  void ScaleColumns(const double* scale) final DO_NOT_CALL;  void SetZero() final DO_NOT_CALL;  void ToDenseMatrix(Matrix* dense_matrix) const final DO_NOT_CALL;  void ToTextFile(FILE* file) const final DO_NOT_CALL; private:  Matrix m_;};// A fake SparseCholesky which uses Eigen's Cholesky factorization to// do the real work. The template parameter allows us to work in// doubles or floats, even though the source matrix is double.template <typename Scalar>class FakeSparseCholesky : public SparseCholesky { public:  FakeSparseCholesky(const Matrix& lhs) { lhs_ = lhs.cast<Scalar>(); }  virtual ~FakeSparseCholesky() {}  LinearSolverTerminationType Solve(const double* rhs_ptr,                                            double* solution_ptr,                                            std::string* message) final {    const int num_cols = lhs_.cols();    VectorRef solution(solution_ptr, num_cols);    ConstVectorRef rhs(rhs_ptr, num_cols);    solution = lhs_.llt().solve(rhs.cast<Scalar>()).template cast<double>();    return LINEAR_SOLVER_SUCCESS;  }  // The following methods are not needed for tests in this file.  CompressedRowSparseMatrix::StorageType StorageType() const final      DO_NOT_CALL_WITH_RETURN(CompressedRowSparseMatrix::UPPER_TRIANGULAR);  LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,                                                std::string* message) final      DO_NOT_CALL_WITH_RETURN(LINEAR_SOLVER_FAILURE);  LinearSolverTerminationType FactorAndSolve(      CompressedRowSparseMatrix* lhs,      const double* rhs,      double* solution,      std::string* message) final DO_NOT_CALL_WITH_RETURN(LINEAR_SOLVER_FAILURE); private:  Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> lhs_;};#undef DO_NOT_CALL#undef DO_NOT_CALL_WITH_RETURNclass IterativeRefinerTest : public ::testing::Test { public:  void SetUp() {    num_cols_ = 5;    max_num_iterations_ = 30;    Matrix m(num_cols_, num_cols_);    m.setRandom();    lhs_ = m * m.transpose();    solution_.resize(num_cols_);    solution_.setRandom();    rhs_ = lhs_ * solution_;  }; protected:  int num_cols_;  int max_num_iterations_;  Matrix lhs_;  Vector rhs_, solution_;};TEST_F(IterativeRefinerTest, RandomSolutionWithExactFactorizationConverges) {  FakeSparseMatrix lhs(lhs_);  FakeSparseCholesky<double> sparse_cholesky(lhs_);  IterativeRefiner refiner(max_num_iterations_);  Vector refined_solution(num_cols_);  refined_solution.setRandom();  refiner.Refine(lhs, rhs_.data(), &sparse_cholesky, refined_solution.data());  EXPECT_NEAR((lhs_ * refined_solution - rhs_).norm(),              0.0,              std::numeric_limits<double>::epsilon() * 10);}TEST_F(IterativeRefinerTest,       RandomSolutionWithApproximationFactorizationConverges) {  FakeSparseMatrix lhs(lhs_);  // Use a single precision Cholesky factorization of the double  // precision matrix. This will give us an approximate factorization.  FakeSparseCholesky<float> sparse_cholesky(lhs_);  IterativeRefiner refiner(max_num_iterations_);  Vector refined_solution(num_cols_);  refined_solution.setRandom();  refiner.Refine(lhs, rhs_.data(), &sparse_cholesky, refined_solution.data());  EXPECT_NEAR((lhs_ * refined_solution - rhs_).norm(),              0.0,              std::numeric_limits<double>::epsilon() * 10);}}  // namespace internal}  // namespace ceres
 |