visibility_based_preconditioner_test.cc 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342
  1. // Ceres Solver - A fast non-linear least squares minimizer
  2. // Copyright 2023 Google Inc. All rights reserved.
  3. // http://ceres-solver.org/
  4. //
  5. // Redistribution and use in source and binary forms, with or without
  6. // modification, are permitted provided that the following conditions are met:
  7. //
  8. // * Redistributions of source code must retain the above copyright notice,
  9. // this list of conditions and the following disclaimer.
  10. // * Redistributions in binary form must reproduce the above copyright notice,
  11. // this list of conditions and the following disclaimer in the documentation
  12. // and/or other materials provided with the distribution.
  13. // * Neither the name of Google Inc. nor the names of its contributors may be
  14. // used to endorse or promote products derived from this software without
  15. // specific prior written permission.
  16. //
  17. // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
  18. // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
  19. // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
  20. // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
  21. // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  22. // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
  23. // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
  24. // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
  25. // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
  26. // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
  27. // POSSIBILITY OF SUCH DAMAGE.
  28. //
  29. // Author: sameeragarwal@google.com (Sameer Agarwal)
  30. #include "ceres/visibility_based_preconditioner.h"
  31. #include <memory>
  32. #include "Eigen/Dense"
  33. #include "ceres/block_random_access_dense_matrix.h"
  34. #include "ceres/block_random_access_sparse_matrix.h"
  35. #include "ceres/block_sparse_matrix.h"
  36. #include "ceres/casts.h"
  37. #include "ceres/file.h"
  38. #include "ceres/internal/eigen.h"
  39. #include "ceres/linear_least_squares_problems.h"
  40. #include "ceres/schur_eliminator.h"
  41. #include "ceres/stringprintf.h"
  42. #include "ceres/test_util.h"
  43. #include "ceres/types.h"
  44. #include "glog/logging.h"
  45. #include "gtest/gtest.h"
  46. namespace ceres::internal {
  47. // TODO(sameeragarwal): Re-enable this test once serialization is
  48. // working again.
  49. // using testing::AssertionResult;
  50. // using testing::AssertionSuccess;
  51. // using testing::AssertionFailure;
  52. // static const double kTolerance = 1e-12;
  53. // class VisibilityBasedPreconditionerTest : public ::testing::Test {
  54. // public:
  55. // static const int kCameraSize = 9;
  56. // protected:
  57. // void SetUp() {
  58. // string input_file = TestFileAbsolutePath("problem-6-1384-000.lsqp");
  59. // std::unique_ptr<LinearLeastSquaresProblem> problem =
  60. // CreateLinearLeastSquaresProblemFromFile(input_file));
  61. // A_.reset(down_cast<BlockSparseMatrix*>(problem->A.release()));
  62. // b_.reset(problem->b.release());
  63. // D_.reset(problem->D.release());
  64. // const CompressedRowBlockStructure* bs =
  65. // CHECK_NOTNULL(A_->block_structure());
  66. // const int num_col_blocks = bs->cols.size();
  67. // num_cols_ = A_->num_cols();
  68. // num_rows_ = A_->num_rows();
  69. // num_eliminate_blocks_ = problem->num_eliminate_blocks;
  70. // num_camera_blocks_ = num_col_blocks - num_eliminate_blocks_;
  71. // options_.elimination_groups.push_back(num_eliminate_blocks_);
  72. // options_.elimination_groups.push_back(
  73. // A_->block_structure()->cols.size() - num_eliminate_blocks_);
  74. // vector<int> blocks(num_col_blocks - num_eliminate_blocks_, 0);
  75. // for (int i = num_eliminate_blocks_; i < num_col_blocks; ++i) {
  76. // blocks[i - num_eliminate_blocks_] = bs->cols[i].size;
  77. // }
  78. // // The input matrix is a real jacobian and fairly poorly
  79. // // conditioned. Setting D to a large constant makes the normal
  80. // // equations better conditioned and makes the tests below better
  81. // // conditioned.
  82. // VectorRef(D_.get(), num_cols_).setConstant(10.0);
  83. // schur_complement_ =
  84. // std::make_unique<BlockRandomAccessDenseMatrix>(blocks);
  85. // Vector rhs(schur_complement_->num_rows());
  86. // std::unique_ptr<SchurEliminatorBase> eliminator;
  87. // LinearSolver::Options eliminator_options;
  88. // eliminator_options.elimination_groups = options_.elimination_groups;
  89. // eliminator_options.num_threads = options_.num_threads;
  90. // eliminator = SchurEliminatorBase::Create(eliminator_options);
  91. // eliminator->Init(num_eliminate_blocks_, bs);
  92. // eliminator->Eliminate(A_.get(), b_.get(), D_.get(),
  93. // schur_complement_.get(), rhs.data());
  94. // }
  95. // AssertionResult IsSparsityStructureValid() {
  96. // preconditioner_->InitStorage(*A_->block_structure());
  97. // const std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
  98. // get_cluster_pairs(); const vector<int>& cluster_membership =
  99. // get_cluster_membership();
  100. // for (int i = 0; i < num_camera_blocks_; ++i) {
  101. // for (int j = i; j < num_camera_blocks_; ++j) {
  102. // if (cluster_pairs.count(make_pair(cluster_membership[i],
  103. // cluster_membership[j]))) {
  104. // if (!IsBlockPairInPreconditioner(i, j)) {
  105. // return AssertionFailure()
  106. // << "block pair (" << i << "," << j << "missing";
  107. // }
  108. // } else {
  109. // if (IsBlockPairInPreconditioner(i, j)) {
  110. // return AssertionFailure()
  111. // << "block pair (" << i << "," << j << "should not be present";
  112. // }
  113. // }
  114. // }
  115. // }
  116. // return AssertionSuccess();
  117. // }
  118. // AssertionResult PreconditionerValuesMatch() {
  119. // preconditioner_->Update(*A_, D_.get());
  120. // const std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
  121. // get_cluster_pairs(); const BlockRandomAccessSparseMatrix* m = get_m();
  122. // Matrix preconditioner_matrix;
  123. // m->matrix()->ToDenseMatrix(&preconditioner_matrix);
  124. // ConstMatrixRef full_schur_complement(schur_complement_->values(),
  125. // m->num_rows(),
  126. // m->num_rows());
  127. // const int num_clusters = get_num_clusters();
  128. // const int kDiagonalBlockSize =
  129. // kCameraSize * num_camera_blocks_ / num_clusters;
  130. // for (int i = 0; i < num_clusters; ++i) {
  131. // for (int j = i; j < num_clusters; ++j) {
  132. // double diff = 0.0;
  133. // if (cluster_pairs.count(make_pair(i, j))) {
  134. // diff =
  135. // (preconditioner_matrix.block(kDiagonalBlockSize * i,
  136. // kDiagonalBlockSize * j,
  137. // kDiagonalBlockSize,
  138. // kDiagonalBlockSize) -
  139. // full_schur_complement.block(kDiagonalBlockSize * i,
  140. // kDiagonalBlockSize * j,
  141. // kDiagonalBlockSize,
  142. // kDiagonalBlockSize)).norm();
  143. // } else {
  144. // diff = preconditioner_matrix.block(kDiagonalBlockSize * i,
  145. // kDiagonalBlockSize * j,
  146. // kDiagonalBlockSize,
  147. // kDiagonalBlockSize).norm();
  148. // }
  149. // if (diff > kTolerance) {
  150. // return AssertionFailure()
  151. // << "Preconditioner block " << i << " " << j << " differs "
  152. // << "from expected value by " << diff;
  153. // }
  154. // }
  155. // }
  156. // return AssertionSuccess();
  157. // }
  158. // // Accessors
  159. // int get_num_blocks() { return preconditioner_->num_blocks_; }
  160. // int get_num_clusters() { return preconditioner_->num_clusters_; }
  161. // int* get_mutable_num_clusters() { return &preconditioner_->num_clusters_; }
  162. // const vector<int>& get_block_size() {
  163. // return preconditioner_->block_size_; }
  164. // vector<int>* get_mutable_block_size() {
  165. // return &preconditioner_->block_size_; }
  166. // const vector<int>& get_cluster_membership() {
  167. // return preconditioner_->cluster_membership_;
  168. // }
  169. // vector<int>* get_mutable_cluster_membership() {
  170. // return &preconditioner_->cluster_membership_;
  171. // }
  172. // const set<pair<int, int>>& get_block_pairs() {
  173. // return preconditioner_->block_pairs_;
  174. // }
  175. // set<pair<int, int>>* get_mutable_block_pairs() {
  176. // return &preconditioner_->block_pairs_;
  177. // }
  178. // const std::unordered_set<pair<int, int>, pair_hash>& get_cluster_pairs() {
  179. // return preconditioner_->cluster_pairs_;
  180. // }
  181. // std::unordered_set<pair<int, int>, pair_hash>* get_mutable_cluster_pairs()
  182. // {
  183. // return &preconditioner_->cluster_pairs_;
  184. // }
  185. // bool IsBlockPairInPreconditioner(const int block1, const int block2) {
  186. // return preconditioner_->IsBlockPairInPreconditioner(block1, block2);
  187. // }
  188. // bool IsBlockPairOffDiagonal(const int block1, const int block2) {
  189. // return preconditioner_->IsBlockPairOffDiagonal(block1, block2);
  190. // }
  191. // const BlockRandomAccessSparseMatrix* get_m() {
  192. // return preconditioner_->m_.get();
  193. // }
  194. // int num_rows_;
  195. // int num_cols_;
  196. // int num_eliminate_blocks_;
  197. // int num_camera_blocks_;
  198. // std::unique_ptr<BlockSparseMatrix> A_;
  199. // std::unique_ptr<double[]> b_;
  200. // std::unique_ptr<double[]> D_;
  201. // Preconditioner::Options options_;
  202. // std::unique_ptr<VisibilityBasedPreconditioner> preconditioner_;
  203. // std::unique_ptr<BlockRandomAccessDenseMatrix> schur_complement_;
  204. // };
  205. // TEST_F(VisibilityBasedPreconditionerTest, OneClusterClusterJacobi) {
  206. // options_.type = CLUSTER_JACOBI;
  207. // preconditioner_ =
  208. // std::make_unique<VisibilityBasedPreconditioner>(
  209. // *A_->block_structure(), options_);
  210. // // Override the clustering to be a single clustering containing all
  211. // // the cameras.
  212. // vector<int>& cluster_membership = *get_mutable_cluster_membership();
  213. // for (int i = 0; i < num_camera_blocks_; ++i) {
  214. // cluster_membership[i] = 0;
  215. // }
  216. // *get_mutable_num_clusters() = 1;
  217. // std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
  218. // *get_mutable_cluster_pairs(); cluster_pairs.clear();
  219. // cluster_pairs.insert(make_pair(0, 0));
  220. // EXPECT_TRUE(IsSparsityStructureValid());
  221. // EXPECT_TRUE(PreconditionerValuesMatch());
  222. // // Multiplication by the inverse of the preconditioner.
  223. // const int num_rows = schur_complement_->num_rows();
  224. // ConstMatrixRef full_schur_complement(schur_complement_->values(),
  225. // num_rows,
  226. // num_rows);
  227. // Vector x(num_rows);
  228. // Vector y(num_rows);
  229. // Vector z(num_rows);
  230. // for (int i = 0; i < num_rows; ++i) {
  231. // x.setZero();
  232. // y.setZero();
  233. // z.setZero();
  234. // x[i] = 1.0;
  235. // preconditioner_->RightMultiplyAndAccumulate(x.data(), y.data());
  236. // z = full_schur_complement
  237. // .selfadjointView<Eigen::Upper>()
  238. // .llt().solve(x);
  239. // double max_relative_difference =
  240. // ((y - z).array() / z.array()).matrix().lpNorm<Eigen::Infinity>();
  241. // EXPECT_NEAR(max_relative_difference, 0.0, kTolerance);
  242. // }
  243. // }
  244. // TEST_F(VisibilityBasedPreconditionerTest, ClusterJacobi) {
  245. // options_.type = CLUSTER_JACOBI;
  246. // preconditioner_ =
  247. // std::make_unique<VisibilityBasedPreconditioner>(*A_->block_structure(),
  248. // options_);
  249. // // Override the clustering to be equal number of cameras.
  250. // vector<int>& cluster_membership = *get_mutable_cluster_membership();
  251. // cluster_membership.resize(num_camera_blocks_);
  252. // static const int kNumClusters = 3;
  253. // for (int i = 0; i < num_camera_blocks_; ++i) {
  254. // cluster_membership[i] = (i * kNumClusters) / num_camera_blocks_;
  255. // }
  256. // *get_mutable_num_clusters() = kNumClusters;
  257. // std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
  258. // *get_mutable_cluster_pairs(); cluster_pairs.clear(); for (int i = 0; i <
  259. // kNumClusters; ++i) {
  260. // cluster_pairs.insert(make_pair(i, i));
  261. // }
  262. // EXPECT_TRUE(IsSparsityStructureValid());
  263. // EXPECT_TRUE(PreconditionerValuesMatch());
  264. // }
  265. // TEST_F(VisibilityBasedPreconditionerTest, ClusterTridiagonal) {
  266. // options_.type = CLUSTER_TRIDIAGONAL;
  267. // preconditioner_ =
  268. // std::make_unique<VisibilityBasedPreconditioner>(*A_->block_structure(),
  269. // options_);
  270. // static const int kNumClusters = 3;
  271. // // Override the clustering to be 3 clusters.
  272. // vector<int>& cluster_membership = *get_mutable_cluster_membership();
  273. // cluster_membership.resize(num_camera_blocks_);
  274. // for (int i = 0; i < num_camera_blocks_; ++i) {
  275. // cluster_membership[i] = (i * kNumClusters) / num_camera_blocks_;
  276. // }
  277. // *get_mutable_num_clusters() = kNumClusters;
  278. // // Spanning forest has structure 0-1 2
  279. // std::unordered_set<pair<int, int>, pair_hash>& cluster_pairs =
  280. // *get_mutable_cluster_pairs(); cluster_pairs.clear(); for (int i = 0; i <
  281. // kNumClusters; ++i) {
  282. // cluster_pairs.insert(make_pair(i, i));
  283. // }
  284. // cluster_pairs.insert(make_pair(0, 1));
  285. // EXPECT_TRUE(IsSparsityStructureValid());
  286. // EXPECT_TRUE(PreconditionerValuesMatch());
  287. // }
  288. } // namespace ceres::internal