123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253 |
- // Ceres Solver - A fast non-linear least squares minimizer
- // Copyright 2023 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 <utility>
- #include "Eigen/Dense"
- #include "ceres/dense_cholesky.h"
- #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::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:
- explicit FakeSparseMatrix(Matrix m) : m_(std::move(m)) {}
- // y += Ax
- void RightMultiplyAndAccumulate(const double* x, double* y) const final {
- VectorRef(y, m_.cols()) += m_ * ConstVectorRef(x, m_.cols());
- }
- // y += A'x
- void LeftMultiplyAndAccumulate(const double* x, double* y) const final {
- // We will assume that this is a symmetric matrix.
- RightMultiplyAndAccumulate(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:
- explicit FakeSparseCholesky(const Matrix& lhs) { lhs_ = lhs.cast<Scalar>(); }
- 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);
- auto llt = lhs_.llt();
- CHECK_EQ(llt.info(), Eigen::Success);
- solution = llt.solve(rhs.cast<Scalar>()).template cast<double>();
- return LinearSolverTerminationType::SUCCESS;
- }
- // The following methods are not needed for tests in this file.
- CompressedRowSparseMatrix::StorageType StorageType() const final
- DO_NOT_CALL_WITH_RETURN(
- CompressedRowSparseMatrix::StorageType::UPPER_TRIANGULAR);
- LinearSolverTerminationType Factorize(CompressedRowSparseMatrix* lhs,
- std::string* message) final
- DO_NOT_CALL_WITH_RETURN(LinearSolverTerminationType::FAILURE);
- private:
- Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> lhs_;
- };
- // A fake DenseCholesky 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 FakeDenseCholesky : public DenseCholesky {
- public:
- explicit FakeDenseCholesky(const Matrix& lhs) { lhs_ = lhs.cast<Scalar>(); }
- 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 LinearSolverTerminationType::SUCCESS;
- }
- LinearSolverTerminationType Factorize(int num_cols,
- double* lhs,
- std::string* message) final
- DO_NOT_CALL_WITH_RETURN(LinearSolverTerminationType::FAILURE);
- private:
- Eigen::Matrix<Scalar, Eigen::Dynamic, Eigen::Dynamic> lhs_;
- };
- #undef DO_NOT_CALL
- #undef DO_NOT_CALL_WITH_RETURN
- class SparseIterativeRefinerTest : public ::testing::Test {
- public:
- void SetUp() override {
- 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(SparseIterativeRefinerTest,
- RandomSolutionWithExactFactorizationConverges) {
- FakeSparseMatrix lhs(lhs_);
- FakeSparseCholesky<double> sparse_cholesky(lhs_);
- SparseIterativeRefiner 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(SparseIterativeRefinerTest,
- 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_);
- SparseIterativeRefiner 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);
- }
- class DenseIterativeRefinerTest : public ::testing::Test {
- public:
- void SetUp() override {
- 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(DenseIterativeRefinerTest,
- RandomSolutionWithExactFactorizationConverges) {
- Matrix lhs = lhs_;
- FakeDenseCholesky<double> dense_cholesky(lhs);
- DenseIterativeRefiner refiner(max_num_iterations_);
- Vector refined_solution(num_cols_);
- refined_solution.setRandom();
- refiner.Refine(lhs.cols(),
- lhs.data(),
- rhs_.data(),
- &dense_cholesky,
- refined_solution.data());
- EXPECT_NEAR((lhs_ * refined_solution - rhs_).norm(),
- 0.0,
- std::numeric_limits<double>::epsilon() * 10);
- }
- TEST_F(DenseIterativeRefinerTest,
- RandomSolutionWithApproximationFactorizationConverges) {
- Matrix lhs = lhs_;
- // Use a single precision Cholesky factorization of the double
- // precision matrix. This will give us an approximate factorization.
- FakeDenseCholesky<float> dense_cholesky(lhs_);
- DenseIterativeRefiner refiner(max_num_iterations_);
- Vector refined_solution(num_cols_);
- refined_solution.setRandom();
- refiner.Refine(lhs.cols(),
- lhs.data(),
- rhs_.data(),
- &dense_cholesky,
- refined_solution.data());
- EXPECT_NEAR((lhs_ * refined_solution - rhs_).norm(),
- 0.0,
- std::numeric_limits<double>::epsilon() * 10);
- }
- } // namespace ceres::internal
|