covariance_test.cc 44 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361
  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/covariance.h"
  31. #include <algorithm>
  32. #include <cmath>
  33. #include <cstdint>
  34. #include <map>
  35. #include <memory>
  36. #include <utility>
  37. #include <vector>
  38. #include "ceres/autodiff_cost_function.h"
  39. #include "ceres/compressed_row_sparse_matrix.h"
  40. #include "ceres/cost_function.h"
  41. #include "ceres/covariance_impl.h"
  42. #include "ceres/internal/config.h"
  43. #include "ceres/manifold.h"
  44. #include "ceres/map_util.h"
  45. #include "ceres/problem_impl.h"
  46. #include "gtest/gtest.h"
  47. namespace ceres {
  48. namespace internal {
  49. class UnaryCostFunction : public CostFunction {
  50. public:
  51. UnaryCostFunction(const int num_residuals,
  52. const int32_t parameter_block_size,
  53. const double* jacobian)
  54. : jacobian_(jacobian, jacobian + num_residuals * parameter_block_size) {
  55. set_num_residuals(num_residuals);
  56. mutable_parameter_block_sizes()->push_back(parameter_block_size);
  57. }
  58. bool Evaluate(double const* const* parameters,
  59. double* residuals,
  60. double** jacobians) const final {
  61. for (int i = 0; i < num_residuals(); ++i) {
  62. residuals[i] = 1;
  63. }
  64. if (jacobians == nullptr) {
  65. return true;
  66. }
  67. if (jacobians[0] != nullptr) {
  68. std::copy(jacobian_.begin(), jacobian_.end(), jacobians[0]);
  69. }
  70. return true;
  71. }
  72. private:
  73. std::vector<double> jacobian_;
  74. };
  75. class BinaryCostFunction : public CostFunction {
  76. public:
  77. BinaryCostFunction(const int num_residuals,
  78. const int32_t parameter_block1_size,
  79. const int32_t parameter_block2_size,
  80. const double* jacobian1,
  81. const double* jacobian2)
  82. : jacobian1_(jacobian1,
  83. jacobian1 + num_residuals * parameter_block1_size),
  84. jacobian2_(jacobian2,
  85. jacobian2 + num_residuals * parameter_block2_size) {
  86. set_num_residuals(num_residuals);
  87. mutable_parameter_block_sizes()->push_back(parameter_block1_size);
  88. mutable_parameter_block_sizes()->push_back(parameter_block2_size);
  89. }
  90. bool Evaluate(double const* const* parameters,
  91. double* residuals,
  92. double** jacobians) const final {
  93. for (int i = 0; i < num_residuals(); ++i) {
  94. residuals[i] = 2;
  95. }
  96. if (jacobians == nullptr) {
  97. return true;
  98. }
  99. if (jacobians[0] != nullptr) {
  100. std::copy(jacobian1_.begin(), jacobian1_.end(), jacobians[0]);
  101. }
  102. if (jacobians[1] != nullptr) {
  103. std::copy(jacobian2_.begin(), jacobian2_.end(), jacobians[1]);
  104. }
  105. return true;
  106. }
  107. private:
  108. std::vector<double> jacobian1_;
  109. std::vector<double> jacobian2_;
  110. };
  111. TEST(CovarianceImpl, ComputeCovarianceSparsity) {
  112. double parameters[10];
  113. double* block1 = parameters;
  114. double* block2 = block1 + 1;
  115. double* block3 = block2 + 2;
  116. double* block4 = block3 + 3;
  117. ProblemImpl problem;
  118. // Add in random order
  119. Vector junk_jacobian = Vector::Zero(10);
  120. problem.AddResidualBlock(
  121. new UnaryCostFunction(1, 1, junk_jacobian.data()), nullptr, block1);
  122. problem.AddResidualBlock(
  123. new UnaryCostFunction(1, 4, junk_jacobian.data()), nullptr, block4);
  124. problem.AddResidualBlock(
  125. new UnaryCostFunction(1, 3, junk_jacobian.data()), nullptr, block3);
  126. problem.AddResidualBlock(
  127. new UnaryCostFunction(1, 2, junk_jacobian.data()), nullptr, block2);
  128. // Sparsity pattern
  129. //
  130. // Note that the problem structure does not imply this sparsity
  131. // pattern since all the residual blocks are unary. But the
  132. // ComputeCovarianceSparsity function in its current incarnation
  133. // does not pay attention to this fact and only looks at the
  134. // parameter block pairs that the user provides.
  135. //
  136. // X . . . . . X X X X
  137. // . X X X X X . . . .
  138. // . X X X X X . . . .
  139. // . . . X X X . . . .
  140. // . . . X X X . . . .
  141. // . . . X X X . . . .
  142. // . . . . . . X X X X
  143. // . . . . . . X X X X
  144. // . . . . . . X X X X
  145. // . . . . . . X X X X
  146. // clang-format off
  147. int expected_rows[] = {0, 5, 10, 15, 18, 21, 24, 28, 32, 36, 40};
  148. int expected_cols[] = {0, 6, 7, 8, 9,
  149. 1, 2, 3, 4, 5,
  150. 1, 2, 3, 4, 5,
  151. 3, 4, 5,
  152. 3, 4, 5,
  153. 3, 4, 5,
  154. 6, 7, 8, 9,
  155. 6, 7, 8, 9,
  156. 6, 7, 8, 9,
  157. 6, 7, 8, 9};
  158. // clang-format on
  159. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  160. covariance_blocks.emplace_back(block1, block1);
  161. covariance_blocks.emplace_back(block4, block4);
  162. covariance_blocks.emplace_back(block2, block2);
  163. covariance_blocks.emplace_back(block3, block3);
  164. covariance_blocks.emplace_back(block2, block3);
  165. covariance_blocks.emplace_back(block4, block1); // reversed
  166. Covariance::Options options;
  167. CovarianceImpl covariance_impl(options);
  168. EXPECT_TRUE(
  169. covariance_impl.ComputeCovarianceSparsity(covariance_blocks, &problem));
  170. const CompressedRowSparseMatrix* crsm = covariance_impl.covariance_matrix();
  171. EXPECT_EQ(crsm->num_rows(), 10);
  172. EXPECT_EQ(crsm->num_cols(), 10);
  173. EXPECT_EQ(crsm->num_nonzeros(), 40);
  174. const int* rows = crsm->rows();
  175. for (int r = 0; r < crsm->num_rows() + 1; ++r) {
  176. EXPECT_EQ(rows[r], expected_rows[r])
  177. << r << " " << rows[r] << " " << expected_rows[r];
  178. }
  179. const int* cols = crsm->cols();
  180. for (int c = 0; c < crsm->num_nonzeros(); ++c) {
  181. EXPECT_EQ(cols[c], expected_cols[c])
  182. << c << " " << cols[c] << " " << expected_cols[c];
  183. }
  184. }
  185. TEST(CovarianceImpl, ComputeCovarianceSparsityWithConstantParameterBlock) {
  186. double parameters[10];
  187. double* block1 = parameters;
  188. double* block2 = block1 + 1;
  189. double* block3 = block2 + 2;
  190. double* block4 = block3 + 3;
  191. ProblemImpl problem;
  192. // Add in random order
  193. Vector junk_jacobian = Vector::Zero(10);
  194. problem.AddResidualBlock(
  195. new UnaryCostFunction(1, 1, junk_jacobian.data()), nullptr, block1);
  196. problem.AddResidualBlock(
  197. new UnaryCostFunction(1, 4, junk_jacobian.data()), nullptr, block4);
  198. problem.AddResidualBlock(
  199. new UnaryCostFunction(1, 3, junk_jacobian.data()), nullptr, block3);
  200. problem.AddResidualBlock(
  201. new UnaryCostFunction(1, 2, junk_jacobian.data()), nullptr, block2);
  202. problem.SetParameterBlockConstant(block3);
  203. // Sparsity pattern
  204. //
  205. // Note that the problem structure does not imply this sparsity
  206. // pattern since all the residual blocks are unary. But the
  207. // ComputeCovarianceSparsity function in its current incarnation
  208. // does not pay attention to this fact and only looks at the
  209. // parameter block pairs that the user provides.
  210. //
  211. // X . . X X X X
  212. // . X X . . . .
  213. // . X X . . . .
  214. // . . . X X X X
  215. // . . . X X X X
  216. // . . . X X X X
  217. // . . . X X X X
  218. // clang-format off
  219. int expected_rows[] = {0, 5, 7, 9, 13, 17, 21, 25};
  220. int expected_cols[] = {0, 3, 4, 5, 6,
  221. 1, 2,
  222. 1, 2,
  223. 3, 4, 5, 6,
  224. 3, 4, 5, 6,
  225. 3, 4, 5, 6,
  226. 3, 4, 5, 6};
  227. // clang-format on
  228. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  229. covariance_blocks.emplace_back(block1, block1);
  230. covariance_blocks.emplace_back(block4, block4);
  231. covariance_blocks.emplace_back(block2, block2);
  232. covariance_blocks.emplace_back(block3, block3);
  233. covariance_blocks.emplace_back(block2, block3);
  234. covariance_blocks.emplace_back(block4, block1); // reversed
  235. Covariance::Options options;
  236. CovarianceImpl covariance_impl(options);
  237. EXPECT_TRUE(
  238. covariance_impl.ComputeCovarianceSparsity(covariance_blocks, &problem));
  239. const CompressedRowSparseMatrix* crsm = covariance_impl.covariance_matrix();
  240. EXPECT_EQ(crsm->num_rows(), 7);
  241. EXPECT_EQ(crsm->num_cols(), 7);
  242. EXPECT_EQ(crsm->num_nonzeros(), 25);
  243. const int* rows = crsm->rows();
  244. for (int r = 0; r < crsm->num_rows() + 1; ++r) {
  245. EXPECT_EQ(rows[r], expected_rows[r])
  246. << r << " " << rows[r] << " " << expected_rows[r];
  247. }
  248. const int* cols = crsm->cols();
  249. for (int c = 0; c < crsm->num_nonzeros(); ++c) {
  250. EXPECT_EQ(cols[c], expected_cols[c])
  251. << c << " " << cols[c] << " " << expected_cols[c];
  252. }
  253. }
  254. TEST(CovarianceImpl, ComputeCovarianceSparsityWithFreeParameterBlock) {
  255. double parameters[10];
  256. double* block1 = parameters;
  257. double* block2 = block1 + 1;
  258. double* block3 = block2 + 2;
  259. double* block4 = block3 + 3;
  260. ProblemImpl problem;
  261. // Add in random order
  262. Vector junk_jacobian = Vector::Zero(10);
  263. problem.AddResidualBlock(
  264. new UnaryCostFunction(1, 1, junk_jacobian.data()), nullptr, block1);
  265. problem.AddResidualBlock(
  266. new UnaryCostFunction(1, 4, junk_jacobian.data()), nullptr, block4);
  267. problem.AddParameterBlock(block3, 3);
  268. problem.AddResidualBlock(
  269. new UnaryCostFunction(1, 2, junk_jacobian.data()), nullptr, block2);
  270. // Sparsity pattern
  271. //
  272. // Note that the problem structure does not imply this sparsity
  273. // pattern since all the residual blocks are unary. But the
  274. // ComputeCovarianceSparsity function in its current incarnation
  275. // does not pay attention to this fact and only looks at the
  276. // parameter block pairs that the user provides.
  277. //
  278. // X . . X X X X
  279. // . X X . . . .
  280. // . X X . . . .
  281. // . . . X X X X
  282. // . . . X X X X
  283. // . . . X X X X
  284. // . . . X X X X
  285. // clang-format off
  286. int expected_rows[] = {0, 5, 7, 9, 13, 17, 21, 25};
  287. int expected_cols[] = {0, 3, 4, 5, 6,
  288. 1, 2,
  289. 1, 2,
  290. 3, 4, 5, 6,
  291. 3, 4, 5, 6,
  292. 3, 4, 5, 6,
  293. 3, 4, 5, 6};
  294. // clang-format on
  295. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  296. covariance_blocks.emplace_back(block1, block1);
  297. covariance_blocks.emplace_back(block4, block4);
  298. covariance_blocks.emplace_back(block2, block2);
  299. covariance_blocks.emplace_back(block3, block3);
  300. covariance_blocks.emplace_back(block2, block3);
  301. covariance_blocks.emplace_back(block4, block1); // reversed
  302. Covariance::Options options;
  303. CovarianceImpl covariance_impl(options);
  304. EXPECT_TRUE(
  305. covariance_impl.ComputeCovarianceSparsity(covariance_blocks, &problem));
  306. const CompressedRowSparseMatrix* crsm = covariance_impl.covariance_matrix();
  307. EXPECT_EQ(crsm->num_rows(), 7);
  308. EXPECT_EQ(crsm->num_cols(), 7);
  309. EXPECT_EQ(crsm->num_nonzeros(), 25);
  310. const int* rows = crsm->rows();
  311. for (int r = 0; r < crsm->num_rows() + 1; ++r) {
  312. EXPECT_EQ(rows[r], expected_rows[r])
  313. << r << " " << rows[r] << " " << expected_rows[r];
  314. }
  315. const int* cols = crsm->cols();
  316. for (int c = 0; c < crsm->num_nonzeros(); ++c) {
  317. EXPECT_EQ(cols[c], expected_cols[c])
  318. << c << " " << cols[c] << " " << expected_cols[c];
  319. }
  320. }
  321. // x_plus_delta = delta * x;
  322. class PolynomialManifold : public Manifold {
  323. public:
  324. bool Plus(const double* x,
  325. const double* delta,
  326. double* x_plus_delta) const final {
  327. x_plus_delta[0] = delta[0] * x[0];
  328. x_plus_delta[1] = delta[0] * x[1];
  329. return true;
  330. }
  331. bool Minus(const double* y, const double* x, double* y_minus_x) const final {
  332. LOG(FATAL) << "Should not be called";
  333. return true;
  334. }
  335. bool PlusJacobian(const double* x, double* jacobian) const final {
  336. jacobian[0] = x[0];
  337. jacobian[1] = x[1];
  338. return true;
  339. }
  340. bool MinusJacobian(const double* x, double* jacobian) const final {
  341. LOG(FATAL) << "Should not be called";
  342. return true;
  343. }
  344. int AmbientSize() const final { return 2; }
  345. int TangentSize() const final { return 1; }
  346. };
  347. class CovarianceTest : public ::testing::Test {
  348. protected:
  349. // TODO(sameeragarwal): Investigate if this should be an ordered or an
  350. // unordered map.
  351. using BoundsMap = std::map<const double*, std::pair<int, int>>;
  352. void SetUp() override {
  353. double* x = parameters_;
  354. double* y = x + 2;
  355. double* z = y + 3;
  356. x[0] = 1;
  357. x[1] = 1;
  358. y[0] = 2;
  359. y[1] = 2;
  360. y[2] = 2;
  361. z[0] = 3;
  362. {
  363. double jacobian[] = {1.0, 0.0, 0.0, 1.0};
  364. problem_.AddResidualBlock(
  365. new UnaryCostFunction(2, 2, jacobian), nullptr, x);
  366. }
  367. {
  368. double jacobian[] = {2.0, 0.0, 0.0, 0.0, 2.0, 0.0, 0.0, 0.0, 2.0};
  369. problem_.AddResidualBlock(
  370. new UnaryCostFunction(3, 3, jacobian), nullptr, y);
  371. }
  372. {
  373. double jacobian = 5.0;
  374. problem_.AddResidualBlock(
  375. new UnaryCostFunction(1, 1, &jacobian), nullptr, z);
  376. }
  377. {
  378. double jacobian1[] = {1.0, 2.0, 3.0};
  379. double jacobian2[] = {-5.0, -6.0};
  380. problem_.AddResidualBlock(
  381. new BinaryCostFunction(1, 3, 2, jacobian1, jacobian2), nullptr, y, x);
  382. }
  383. {
  384. double jacobian1[] = {2.0};
  385. double jacobian2[] = {3.0, -2.0};
  386. problem_.AddResidualBlock(
  387. new BinaryCostFunction(1, 1, 2, jacobian1, jacobian2), nullptr, z, x);
  388. }
  389. all_covariance_blocks_.emplace_back(x, x);
  390. all_covariance_blocks_.emplace_back(y, y);
  391. all_covariance_blocks_.emplace_back(z, z);
  392. all_covariance_blocks_.emplace_back(x, y);
  393. all_covariance_blocks_.emplace_back(x, z);
  394. all_covariance_blocks_.emplace_back(y, z);
  395. column_bounds_[x] = std::make_pair(0, 2);
  396. column_bounds_[y] = std::make_pair(2, 5);
  397. column_bounds_[z] = std::make_pair(5, 6);
  398. }
  399. // Computes covariance in ambient space.
  400. void ComputeAndCompareCovarianceBlocks(const Covariance::Options& options,
  401. const double* expected_covariance) {
  402. ComputeAndCompareCovarianceBlocksInTangentOrAmbientSpace(
  403. options,
  404. true, // ambient
  405. expected_covariance);
  406. }
  407. // Computes covariance in tangent space.
  408. void ComputeAndCompareCovarianceBlocksInTangentSpace(
  409. const Covariance::Options& options, const double* expected_covariance) {
  410. ComputeAndCompareCovarianceBlocksInTangentOrAmbientSpace(
  411. options,
  412. false, // tangent
  413. expected_covariance);
  414. }
  415. void ComputeAndCompareCovarianceBlocksInTangentOrAmbientSpace(
  416. const Covariance::Options& options,
  417. bool lift_covariance_to_ambient_space,
  418. const double* expected_covariance) {
  419. // Generate all possible combination of block pairs and check if the
  420. // covariance computation is correct.
  421. for (int i = 0; i <= 64; ++i) {
  422. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  423. if (i & 1) {
  424. covariance_blocks.push_back(all_covariance_blocks_[0]);
  425. }
  426. if (i & 2) {
  427. covariance_blocks.push_back(all_covariance_blocks_[1]);
  428. }
  429. if (i & 4) {
  430. covariance_blocks.push_back(all_covariance_blocks_[2]);
  431. }
  432. if (i & 8) {
  433. covariance_blocks.push_back(all_covariance_blocks_[3]);
  434. }
  435. if (i & 16) {
  436. covariance_blocks.push_back(all_covariance_blocks_[4]);
  437. }
  438. if (i & 32) {
  439. covariance_blocks.push_back(all_covariance_blocks_[5]);
  440. }
  441. Covariance covariance(options);
  442. EXPECT_TRUE(covariance.Compute(covariance_blocks, &problem_));
  443. for (auto& covariance_block : covariance_blocks) {
  444. const double* block1 = covariance_block.first;
  445. const double* block2 = covariance_block.second;
  446. // block1, block2
  447. GetCovarianceBlockAndCompare(block1,
  448. block2,
  449. lift_covariance_to_ambient_space,
  450. covariance,
  451. expected_covariance);
  452. // block2, block1
  453. GetCovarianceBlockAndCompare(block2,
  454. block1,
  455. lift_covariance_to_ambient_space,
  456. covariance,
  457. expected_covariance);
  458. }
  459. }
  460. }
  461. void GetCovarianceBlockAndCompare(const double* block1,
  462. const double* block2,
  463. bool lift_covariance_to_ambient_space,
  464. const Covariance& covariance,
  465. const double* expected_covariance) {
  466. const BoundsMap& column_bounds = lift_covariance_to_ambient_space
  467. ? column_bounds_
  468. : local_column_bounds_;
  469. const int row_begin = FindOrDie(column_bounds, block1).first;
  470. const int row_end = FindOrDie(column_bounds, block1).second;
  471. const int col_begin = FindOrDie(column_bounds, block2).first;
  472. const int col_end = FindOrDie(column_bounds, block2).second;
  473. Matrix actual(row_end - row_begin, col_end - col_begin);
  474. if (lift_covariance_to_ambient_space) {
  475. EXPECT_TRUE(covariance.GetCovarianceBlock(block1, block2, actual.data()));
  476. } else {
  477. EXPECT_TRUE(covariance.GetCovarianceBlockInTangentSpace(
  478. block1, block2, actual.data()));
  479. }
  480. int dof = 0; // degrees of freedom = sum of LocalSize()s
  481. for (const auto& bound : column_bounds) {
  482. dof = std::max(dof, bound.second.second);
  483. }
  484. ConstMatrixRef expected(expected_covariance, dof, dof);
  485. double diff_norm =
  486. (expected.block(
  487. row_begin, col_begin, row_end - row_begin, col_end - col_begin) -
  488. actual)
  489. .norm();
  490. diff_norm /= (row_end - row_begin) * (col_end - col_begin);
  491. const double kTolerance = 1e-5;
  492. EXPECT_NEAR(diff_norm, 0.0, kTolerance)
  493. << "rows: " << row_begin << " " << row_end << " "
  494. << "cols: " << col_begin << " " << col_end << " "
  495. << "\n\n expected: \n "
  496. << expected.block(
  497. row_begin, col_begin, row_end - row_begin, col_end - col_begin)
  498. << "\n\n actual: \n " << actual << "\n\n full expected: \n"
  499. << expected;
  500. }
  501. double parameters_[6];
  502. Problem problem_;
  503. std::vector<std::pair<const double*, const double*>> all_covariance_blocks_;
  504. BoundsMap column_bounds_;
  505. BoundsMap local_column_bounds_;
  506. };
  507. TEST_F(CovarianceTest, NormalBehavior) {
  508. // J
  509. //
  510. // 1 0 0 0 0 0
  511. // 0 1 0 0 0 0
  512. // 0 0 2 0 0 0
  513. // 0 0 0 2 0 0
  514. // 0 0 0 0 2 0
  515. // 0 0 0 0 0 5
  516. // -5 -6 1 2 3 0
  517. // 3 -2 0 0 0 2
  518. // J'J
  519. //
  520. // 35 24 -5 -10 -15 6
  521. // 24 41 -6 -12 -18 -4
  522. // -5 -6 5 2 3 0
  523. // -10 -12 2 8 6 0
  524. // -15 -18 3 6 13 0
  525. // 6 -4 0 0 0 29
  526. // inv(J'J) computed using octave.
  527. // clang-format off
  528. double expected_covariance[] = {
  529. 7.0747e-02, -8.4923e-03, 1.6821e-02, 3.3643e-02, 5.0464e-02, -1.5809e-02, // NOLINT
  530. -8.4923e-03, 8.1352e-02, 2.4758e-02, 4.9517e-02, 7.4275e-02, 1.2978e-02, // NOLINT
  531. 1.6821e-02, 2.4758e-02, 2.4904e-01, -1.9271e-03, -2.8906e-03, -6.5325e-05, // NOLINT
  532. 3.3643e-02, 4.9517e-02, -1.9271e-03, 2.4615e-01, -5.7813e-03, -1.3065e-04, // NOLINT
  533. 5.0464e-02, 7.4275e-02, -2.8906e-03, -5.7813e-03, 2.4133e-01, -1.9598e-04, // NOLINT
  534. -1.5809e-02, 1.2978e-02, -6.5325e-05, -1.3065e-04, -1.9598e-04, 3.9544e-02, // NOLINT
  535. };
  536. // clang-format on
  537. Covariance::Options options;
  538. #ifndef CERES_NO_SUITESPARSE
  539. options.algorithm_type = SPARSE_QR;
  540. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  541. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  542. #endif
  543. options.algorithm_type = DENSE_SVD;
  544. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  545. options.algorithm_type = SPARSE_QR;
  546. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  547. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  548. }
  549. TEST_F(CovarianceTest, ThreadedNormalBehavior) {
  550. // J
  551. //
  552. // 1 0 0 0 0 0
  553. // 0 1 0 0 0 0
  554. // 0 0 2 0 0 0
  555. // 0 0 0 2 0 0
  556. // 0 0 0 0 2 0
  557. // 0 0 0 0 0 5
  558. // -5 -6 1 2 3 0
  559. // 3 -2 0 0 0 2
  560. // J'J
  561. //
  562. // 35 24 -5 -10 -15 6
  563. // 24 41 -6 -12 -18 -4
  564. // -5 -6 5 2 3 0
  565. // -10 -12 2 8 6 0
  566. // -15 -18 3 6 13 0
  567. // 6 -4 0 0 0 29
  568. // inv(J'J) computed using octave.
  569. // clang-format off
  570. double expected_covariance[] = {
  571. 7.0747e-02, -8.4923e-03, 1.6821e-02, 3.3643e-02, 5.0464e-02, -1.5809e-02, // NOLINT
  572. -8.4923e-03, 8.1352e-02, 2.4758e-02, 4.9517e-02, 7.4275e-02, 1.2978e-02, // NOLINT
  573. 1.6821e-02, 2.4758e-02, 2.4904e-01, -1.9271e-03, -2.8906e-03, -6.5325e-05, // NOLINT
  574. 3.3643e-02, 4.9517e-02, -1.9271e-03, 2.4615e-01, -5.7813e-03, -1.3065e-04, // NOLINT
  575. 5.0464e-02, 7.4275e-02, -2.8906e-03, -5.7813e-03, 2.4133e-01, -1.9598e-04, // NOLINT
  576. -1.5809e-02, 1.2978e-02, -6.5325e-05, -1.3065e-04, -1.9598e-04, 3.9544e-02, // NOLINT
  577. };
  578. // clang-format on
  579. Covariance::Options options;
  580. options.num_threads = 4;
  581. #ifndef CERES_NO_SUITESPARSE
  582. options.algorithm_type = SPARSE_QR;
  583. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  584. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  585. #endif
  586. options.algorithm_type = DENSE_SVD;
  587. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  588. options.algorithm_type = SPARSE_QR;
  589. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  590. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  591. }
  592. TEST_F(CovarianceTest, ConstantParameterBlock) {
  593. problem_.SetParameterBlockConstant(parameters_);
  594. // J
  595. //
  596. // 0 0 0 0 0 0
  597. // 0 0 0 0 0 0
  598. // 0 0 2 0 0 0
  599. // 0 0 0 2 0 0
  600. // 0 0 0 0 2 0
  601. // 0 0 0 0 0 5
  602. // 0 0 1 2 3 0
  603. // 0 0 0 0 0 2
  604. // J'J
  605. //
  606. // 0 0 0 0 0 0
  607. // 0 0 0 0 0 0
  608. // 0 0 5 2 3 0
  609. // 0 0 2 8 6 0
  610. // 0 0 3 6 13 0
  611. // 0 0 0 0 0 29
  612. // pinv(J'J) computed using octave.
  613. // clang-format off
  614. double expected_covariance[] = {
  615. 0, 0, 0, 0, 0, 0, // NOLINT
  616. 0, 0, 0, 0, 0, 0, // NOLINT
  617. 0, 0, 0.23611, -0.02778, -0.04167, -0.00000, // NOLINT
  618. 0, 0, -0.02778, 0.19444, -0.08333, -0.00000, // NOLINT
  619. 0, 0, -0.04167, -0.08333, 0.12500, -0.00000, // NOLINT
  620. 0, 0, -0.00000, -0.00000, -0.00000, 0.03448 // NOLINT
  621. // clang-format on
  622. };
  623. Covariance::Options options;
  624. #ifndef CERES_NO_SUITESPARSE
  625. options.algorithm_type = SPARSE_QR;
  626. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  627. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  628. #endif
  629. options.algorithm_type = DENSE_SVD;
  630. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  631. options.algorithm_type = SPARSE_QR;
  632. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  633. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  634. }
  635. TEST_F(CovarianceTest, Manifold) {
  636. double* x = parameters_;
  637. double* y = x + 2;
  638. problem_.SetManifold(x, new PolynomialManifold);
  639. std::vector<int> subset;
  640. subset.push_back(2);
  641. problem_.SetManifold(y, new SubsetManifold(3, subset));
  642. // Raw Jacobian: J
  643. //
  644. // 1 0 0 0 0 0
  645. // 0 1 0 0 0 0
  646. // 0 0 2 0 0 0
  647. // 0 0 0 2 0 0
  648. // 0 0 0 0 2 0
  649. // 0 0 0 0 0 5
  650. // -5 -6 1 2 3 0
  651. // 3 -2 0 0 0 2
  652. // Local to global jacobian: A
  653. //
  654. // 1 0 0 0
  655. // 1 0 0 0
  656. // 0 1 0 0
  657. // 0 0 1 0
  658. // 0 0 0 0
  659. // 0 0 0 1
  660. // A * inv((J*A)'*(J*A)) * A'
  661. // Computed using octave.
  662. // clang-format off
  663. double expected_covariance[] = {
  664. 0.01766, 0.01766, 0.02158, 0.04316, 0.00000, -0.00122,
  665. 0.01766, 0.01766, 0.02158, 0.04316, 0.00000, -0.00122,
  666. 0.02158, 0.02158, 0.24860, -0.00281, 0.00000, -0.00149,
  667. 0.04316, 0.04316, -0.00281, 0.24439, 0.00000, -0.00298,
  668. 0.00000, 0.00000, 0.00000, 0.00000, 0.00000, 0.00000,
  669. -0.00122, -0.00122, -0.00149, -0.00298, 0.00000, 0.03457
  670. };
  671. // clang-format on
  672. Covariance::Options options;
  673. #ifndef CERES_NO_SUITESPARSE
  674. options.algorithm_type = SPARSE_QR;
  675. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  676. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  677. #endif
  678. options.algorithm_type = DENSE_SVD;
  679. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  680. options.algorithm_type = SPARSE_QR;
  681. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  682. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  683. }
  684. TEST_F(CovarianceTest, ManifoldInTangentSpace) {
  685. double* x = parameters_;
  686. double* y = x + 2;
  687. double* z = y + 3;
  688. problem_.SetManifold(x, new PolynomialManifold);
  689. std::vector<int> subset;
  690. subset.push_back(2);
  691. problem_.SetManifold(y, new SubsetManifold(3, subset));
  692. local_column_bounds_[x] = std::make_pair(0, 1);
  693. local_column_bounds_[y] = std::make_pair(1, 3);
  694. local_column_bounds_[z] = std::make_pair(3, 4);
  695. // Raw Jacobian: J
  696. //
  697. // 1 0 0 0 0 0
  698. // 0 1 0 0 0 0
  699. // 0 0 2 0 0 0
  700. // 0 0 0 2 0 0
  701. // 0 0 0 0 2 0
  702. // 0 0 0 0 0 5
  703. // -5 -6 1 2 3 0
  704. // 3 -2 0 0 0 2
  705. // Local to global jacobian: A
  706. //
  707. // 1 0 0 0
  708. // 1 0 0 0
  709. // 0 1 0 0
  710. // 0 0 1 0
  711. // 0 0 0 0
  712. // 0 0 0 1
  713. // inv((J*A)'*(J*A))
  714. // Computed using octave.
  715. // clang-format off
  716. double expected_covariance[] = {
  717. 0.01766, 0.02158, 0.04316, -0.00122,
  718. 0.02158, 0.24860, -0.00281, -0.00149,
  719. 0.04316, -0.00281, 0.24439, -0.00298,
  720. -0.00122, -0.00149, -0.00298, 0.03457 // NOLINT
  721. };
  722. // clang-format on
  723. Covariance::Options options;
  724. #ifndef CERES_NO_SUITESPARSE
  725. options.algorithm_type = SPARSE_QR;
  726. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  727. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  728. #endif
  729. options.algorithm_type = DENSE_SVD;
  730. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  731. options.algorithm_type = SPARSE_QR;
  732. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  733. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  734. }
  735. TEST_F(CovarianceTest, ManifoldInTangentSpaceWithConstantBlocks) {
  736. double* x = parameters_;
  737. double* y = x + 2;
  738. double* z = y + 3;
  739. problem_.SetManifold(x, new PolynomialManifold);
  740. problem_.SetParameterBlockConstant(x);
  741. std::vector<int> subset;
  742. subset.push_back(2);
  743. problem_.SetManifold(y, new SubsetManifold(3, subset));
  744. problem_.SetParameterBlockConstant(y);
  745. local_column_bounds_[x] = std::make_pair(0, 1);
  746. local_column_bounds_[y] = std::make_pair(1, 3);
  747. local_column_bounds_[z] = std::make_pair(3, 4);
  748. // Raw Jacobian: J
  749. //
  750. // 1 0 0 0 0 0
  751. // 0 1 0 0 0 0
  752. // 0 0 2 0 0 0
  753. // 0 0 0 2 0 0
  754. // 0 0 0 0 2 0
  755. // 0 0 0 0 0 5
  756. // -5 -6 1 2 3 0
  757. // 3 -2 0 0 0 2
  758. // Local to global jacobian: A
  759. //
  760. // 0 0 0 0
  761. // 0 0 0 0
  762. // 0 0 0 0
  763. // 0 0 0 0
  764. // 0 0 0 0
  765. // 0 0 0 1
  766. // pinv((J*A)'*(J*A))
  767. // Computed using octave.
  768. // clang-format off
  769. double expected_covariance[] = {
  770. 0.0, 0.0, 0.0, 0.0,
  771. 0.0, 0.0, 0.0, 0.0,
  772. 0.0, 0.0, 0.0, 0.0,
  773. 0.0, 0.0, 0.0, 0.034482 // NOLINT
  774. };
  775. // clang-format on
  776. Covariance::Options options;
  777. #ifndef CERES_NO_SUITESPARSE
  778. options.algorithm_type = SPARSE_QR;
  779. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  780. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  781. #endif
  782. options.algorithm_type = DENSE_SVD;
  783. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  784. options.algorithm_type = SPARSE_QR;
  785. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  786. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  787. }
  788. TEST_F(CovarianceTest, TruncatedRank) {
  789. // J
  790. //
  791. // 1 0 0 0 0 0
  792. // 0 1 0 0 0 0
  793. // 0 0 2 0 0 0
  794. // 0 0 0 2 0 0
  795. // 0 0 0 0 2 0
  796. // 0 0 0 0 0 5
  797. // -5 -6 1 2 3 0
  798. // 3 -2 0 0 0 2
  799. // J'J
  800. //
  801. // 35 24 -5 -10 -15 6
  802. // 24 41 -6 -12 -18 -4
  803. // -5 -6 5 2 3 0
  804. // -10 -12 2 8 6 0
  805. // -15 -18 3 6 13 0
  806. // 6 -4 0 0 0 29
  807. // 3.4142 is the smallest eigenvalue of J'J. The following matrix
  808. // was obtained by dropping the eigenvector corresponding to this
  809. // eigenvalue.
  810. // clang-format off
  811. double expected_covariance[] = {
  812. 5.4135e-02, -3.5121e-02, 1.7257e-04, 3.4514e-04, 5.1771e-04, -1.6076e-02, // NOLINT
  813. -3.5121e-02, 3.8667e-02, -1.9288e-03, -3.8576e-03, -5.7864e-03, 1.2549e-02, // NOLINT
  814. 1.7257e-04, -1.9288e-03, 2.3235e-01, -3.5297e-02, -5.2946e-02, -3.3329e-04, // NOLINT
  815. 3.4514e-04, -3.8576e-03, -3.5297e-02, 1.7941e-01, -1.0589e-01, -6.6659e-04, // NOLINT
  816. 5.1771e-04, -5.7864e-03, -5.2946e-02, -1.0589e-01, 9.1162e-02, -9.9988e-04, // NOLINT
  817. -1.6076e-02, 1.2549e-02, -3.3329e-04, -6.6659e-04, -9.9988e-04, 3.9539e-02 // NOLINT
  818. };
  819. // clang-format on
  820. {
  821. Covariance::Options options;
  822. options.algorithm_type = DENSE_SVD;
  823. // Force dropping of the smallest eigenvector.
  824. options.null_space_rank = 1;
  825. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  826. }
  827. {
  828. Covariance::Options options;
  829. options.algorithm_type = DENSE_SVD;
  830. // Force dropping of the smallest eigenvector via the ratio but
  831. // automatic truncation.
  832. options.min_reciprocal_condition_number = 0.044494;
  833. options.null_space_rank = -1;
  834. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  835. }
  836. }
  837. TEST_F(CovarianceTest, DenseCovarianceMatrixFromSetOfParameters) {
  838. Covariance::Options options;
  839. Covariance covariance(options);
  840. double* x = parameters_;
  841. double* y = x + 2;
  842. double* z = y + 3;
  843. std::vector<const double*> parameter_blocks;
  844. parameter_blocks.push_back(x);
  845. parameter_blocks.push_back(y);
  846. parameter_blocks.push_back(z);
  847. covariance.Compute(parameter_blocks, &problem_);
  848. double expected_covariance[36];
  849. covariance.GetCovarianceMatrix(parameter_blocks, expected_covariance);
  850. #ifndef CERES_NO_SUITESPARSE
  851. options.algorithm_type = SPARSE_QR;
  852. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  853. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  854. #endif
  855. options.algorithm_type = DENSE_SVD;
  856. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  857. options.algorithm_type = SPARSE_QR;
  858. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  859. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  860. }
  861. TEST_F(CovarianceTest, DenseCovarianceMatrixFromSetOfParametersThreaded) {
  862. Covariance::Options options;
  863. options.num_threads = 4;
  864. Covariance covariance(options);
  865. double* x = parameters_;
  866. double* y = x + 2;
  867. double* z = y + 3;
  868. std::vector<const double*> parameter_blocks;
  869. parameter_blocks.push_back(x);
  870. parameter_blocks.push_back(y);
  871. parameter_blocks.push_back(z);
  872. covariance.Compute(parameter_blocks, &problem_);
  873. double expected_covariance[36];
  874. covariance.GetCovarianceMatrix(parameter_blocks, expected_covariance);
  875. #ifndef CERES_NO_SUITESPARSE
  876. options.algorithm_type = SPARSE_QR;
  877. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  878. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  879. #endif
  880. options.algorithm_type = DENSE_SVD;
  881. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  882. options.algorithm_type = SPARSE_QR;
  883. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  884. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  885. }
  886. TEST_F(CovarianceTest, DenseCovarianceMatrixFromSetOfParametersInTangentSpace) {
  887. Covariance::Options options;
  888. Covariance covariance(options);
  889. double* x = parameters_;
  890. double* y = x + 2;
  891. double* z = y + 3;
  892. problem_.SetManifold(x, new PolynomialManifold);
  893. std::vector<int> subset;
  894. subset.push_back(2);
  895. problem_.SetManifold(y, new SubsetManifold(3, subset));
  896. local_column_bounds_[x] = std::make_pair(0, 1);
  897. local_column_bounds_[y] = std::make_pair(1, 3);
  898. local_column_bounds_[z] = std::make_pair(3, 4);
  899. std::vector<const double*> parameter_blocks;
  900. parameter_blocks.push_back(x);
  901. parameter_blocks.push_back(y);
  902. parameter_blocks.push_back(z);
  903. covariance.Compute(parameter_blocks, &problem_);
  904. double expected_covariance[16];
  905. covariance.GetCovarianceMatrixInTangentSpace(parameter_blocks,
  906. expected_covariance);
  907. #ifndef CERES_NO_SUITESPARSE
  908. options.algorithm_type = SPARSE_QR;
  909. options.sparse_linear_algebra_library_type = SUITE_SPARSE;
  910. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  911. #endif
  912. options.algorithm_type = DENSE_SVD;
  913. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  914. options.algorithm_type = SPARSE_QR;
  915. options.sparse_linear_algebra_library_type = EIGEN_SPARSE;
  916. ComputeAndCompareCovarianceBlocksInTangentSpace(options, expected_covariance);
  917. }
  918. TEST_F(CovarianceTest, ComputeCovarianceFailure) {
  919. Covariance::Options options;
  920. Covariance covariance(options);
  921. double* x = parameters_;
  922. double* y = x + 2;
  923. std::vector<const double*> parameter_blocks;
  924. parameter_blocks.push_back(x);
  925. parameter_blocks.push_back(x);
  926. parameter_blocks.push_back(y);
  927. parameter_blocks.push_back(y);
  928. EXPECT_DEATH_IF_SUPPORTED(covariance.Compute(parameter_blocks, &problem_),
  929. "Covariance::Compute called with duplicate blocks "
  930. "at indices \\(0, 1\\) and \\(2, 3\\)");
  931. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  932. covariance_blocks.emplace_back(x, x);
  933. covariance_blocks.emplace_back(x, x);
  934. covariance_blocks.emplace_back(y, y);
  935. covariance_blocks.emplace_back(y, y);
  936. EXPECT_DEATH_IF_SUPPORTED(covariance.Compute(covariance_blocks, &problem_),
  937. "Covariance::Compute called with duplicate blocks "
  938. "at indices \\(0, 1\\) and \\(2, 3\\)");
  939. }
  940. class RankDeficientCovarianceTest : public CovarianceTest {
  941. protected:
  942. void SetUp() final {
  943. double* x = parameters_;
  944. double* y = x + 2;
  945. double* z = y + 3;
  946. {
  947. double jacobian[] = {1.0, 0.0, 0.0, 1.0};
  948. problem_.AddResidualBlock(
  949. new UnaryCostFunction(2, 2, jacobian), nullptr, x);
  950. }
  951. {
  952. double jacobian[] = {0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0};
  953. problem_.AddResidualBlock(
  954. new UnaryCostFunction(3, 3, jacobian), nullptr, y);
  955. }
  956. {
  957. double jacobian = 5.0;
  958. problem_.AddResidualBlock(
  959. new UnaryCostFunction(1, 1, &jacobian), nullptr, z);
  960. }
  961. {
  962. double jacobian1[] = {0.0, 0.0, 0.0};
  963. double jacobian2[] = {-5.0, -6.0};
  964. problem_.AddResidualBlock(
  965. new BinaryCostFunction(1, 3, 2, jacobian1, jacobian2), nullptr, y, x);
  966. }
  967. {
  968. double jacobian1[] = {2.0};
  969. double jacobian2[] = {3.0, -2.0};
  970. problem_.AddResidualBlock(
  971. new BinaryCostFunction(1, 1, 2, jacobian1, jacobian2), nullptr, z, x);
  972. }
  973. all_covariance_blocks_.emplace_back(x, x);
  974. all_covariance_blocks_.emplace_back(y, y);
  975. all_covariance_blocks_.emplace_back(z, z);
  976. all_covariance_blocks_.emplace_back(x, y);
  977. all_covariance_blocks_.emplace_back(x, z);
  978. all_covariance_blocks_.emplace_back(y, z);
  979. column_bounds_[x] = std::make_pair(0, 2);
  980. column_bounds_[y] = std::make_pair(2, 5);
  981. column_bounds_[z] = std::make_pair(5, 6);
  982. }
  983. };
  984. TEST_F(RankDeficientCovarianceTest, AutomaticTruncation) {
  985. // J
  986. //
  987. // 1 0 0 0 0 0
  988. // 0 1 0 0 0 0
  989. // 0 0 0 0 0 0
  990. // 0 0 0 0 0 0
  991. // 0 0 0 0 0 0
  992. // 0 0 0 0 0 5
  993. // -5 -6 0 0 0 0
  994. // 3 -2 0 0 0 2
  995. // J'J
  996. //
  997. // 35 24 0 0 0 6
  998. // 24 41 0 0 0 -4
  999. // 0 0 0 0 0 0
  1000. // 0 0 0 0 0 0
  1001. // 0 0 0 0 0 0
  1002. // 6 -4 0 0 0 29
  1003. // pinv(J'J) computed using octave.
  1004. // clang-format off
  1005. double expected_covariance[] = {
  1006. 0.053998, -0.033145, 0.000000, 0.000000, 0.000000, -0.015744,
  1007. -0.033145, 0.045067, 0.000000, 0.000000, 0.000000, 0.013074,
  1008. 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
  1009. 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
  1010. 0.000000, 0.000000, 0.000000, 0.000000, 0.000000, 0.000000,
  1011. -0.015744, 0.013074, 0.000000, 0.000000, 0.000000, 0.039543
  1012. };
  1013. // clang-format on
  1014. Covariance::Options options;
  1015. options.algorithm_type = DENSE_SVD;
  1016. options.null_space_rank = -1;
  1017. ComputeAndCompareCovarianceBlocks(options, expected_covariance);
  1018. }
  1019. struct LinearCostFunction {
  1020. template <typename T>
  1021. bool operator()(const T* x, const T* y, T* residual) const {
  1022. residual[0] = T(10.0) - *x;
  1023. residual[1] = T(5.0) - *y;
  1024. return true;
  1025. }
  1026. static CostFunction* Create() {
  1027. return new AutoDiffCostFunction<LinearCostFunction, 2, 1, 1>(
  1028. new LinearCostFunction);
  1029. }
  1030. };
  1031. TEST(Covariance, ZeroSizedManifoldGetCovariance) {
  1032. double x = 0.0;
  1033. double y = 1.0;
  1034. Problem problem;
  1035. problem.AddResidualBlock(LinearCostFunction::Create(), nullptr, &x, &y);
  1036. problem.SetManifold(&y, new SubsetManifold(1, {0}));
  1037. // J = [-1 0]
  1038. // [ 0 0]
  1039. Covariance::Options options;
  1040. options.algorithm_type = DENSE_SVD;
  1041. Covariance covariance(options);
  1042. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  1043. covariance_blocks.emplace_back(&x, &x);
  1044. covariance_blocks.emplace_back(&x, &y);
  1045. covariance_blocks.emplace_back(&y, &x);
  1046. covariance_blocks.emplace_back(&y, &y);
  1047. EXPECT_TRUE(covariance.Compute(covariance_blocks, &problem));
  1048. double value = -1;
  1049. covariance.GetCovarianceBlock(&x, &x, &value);
  1050. EXPECT_NEAR(value, 1.0, std::numeric_limits<double>::epsilon());
  1051. value = -1;
  1052. covariance.GetCovarianceBlock(&x, &y, &value);
  1053. EXPECT_NEAR(value, 0.0, std::numeric_limits<double>::epsilon());
  1054. value = -1;
  1055. covariance.GetCovarianceBlock(&y, &x, &value);
  1056. EXPECT_NEAR(value, 0.0, std::numeric_limits<double>::epsilon());
  1057. value = -1;
  1058. covariance.GetCovarianceBlock(&y, &y, &value);
  1059. EXPECT_NEAR(value, 0.0, std::numeric_limits<double>::epsilon());
  1060. }
  1061. TEST(Covariance, ZeroSizedManifoldGetCovarianceInTangentSpace) {
  1062. double x = 0.0;
  1063. double y = 1.0;
  1064. Problem problem;
  1065. problem.AddResidualBlock(LinearCostFunction::Create(), nullptr, &x, &y);
  1066. problem.SetManifold(&y, new SubsetManifold(1, {0}));
  1067. // J = [-1 0]
  1068. // [ 0 0]
  1069. Covariance::Options options;
  1070. options.algorithm_type = DENSE_SVD;
  1071. Covariance covariance(options);
  1072. std::vector<std::pair<const double*, const double*>> covariance_blocks;
  1073. covariance_blocks.emplace_back(&x, &x);
  1074. covariance_blocks.emplace_back(&x, &y);
  1075. covariance_blocks.emplace_back(&y, &x);
  1076. covariance_blocks.emplace_back(&y, &y);
  1077. EXPECT_TRUE(covariance.Compute(covariance_blocks, &problem));
  1078. double value = -1;
  1079. covariance.GetCovarianceBlockInTangentSpace(&x, &x, &value);
  1080. EXPECT_NEAR(value, 1.0, std::numeric_limits<double>::epsilon());
  1081. value = -1;
  1082. // The following three calls, should not touch this value, since the
  1083. // tangent space is of size zero
  1084. covariance.GetCovarianceBlockInTangentSpace(&x, &y, &value);
  1085. EXPECT_EQ(value, -1);
  1086. covariance.GetCovarianceBlockInTangentSpace(&y, &x, &value);
  1087. EXPECT_EQ(value, -1);
  1088. covariance.GetCovarianceBlockInTangentSpace(&y, &y, &value);
  1089. EXPECT_EQ(value, -1);
  1090. }
  1091. class LargeScaleCovarianceTest : public ::testing::Test {
  1092. protected:
  1093. void SetUp() final {
  1094. num_parameter_blocks_ = 2000;
  1095. parameter_block_size_ = 5;
  1096. parameters_ = std::make_unique<double[]>(parameter_block_size_ *
  1097. num_parameter_blocks_);
  1098. Matrix jacobian(parameter_block_size_, parameter_block_size_);
  1099. for (int i = 0; i < num_parameter_blocks_; ++i) {
  1100. jacobian.setIdentity();
  1101. jacobian *= (i + 1);
  1102. double* block_i = parameters_.get() + i * parameter_block_size_;
  1103. problem_.AddResidualBlock(
  1104. new UnaryCostFunction(
  1105. parameter_block_size_, parameter_block_size_, jacobian.data()),
  1106. nullptr,
  1107. block_i);
  1108. for (int j = i; j < num_parameter_blocks_; ++j) {
  1109. double* block_j = parameters_.get() + j * parameter_block_size_;
  1110. all_covariance_blocks_.emplace_back(block_i, block_j);
  1111. }
  1112. }
  1113. }
  1114. void ComputeAndCompare(
  1115. CovarianceAlgorithmType algorithm_type,
  1116. SparseLinearAlgebraLibraryType sparse_linear_algebra_library_type,
  1117. int num_threads) {
  1118. Covariance::Options options;
  1119. options.algorithm_type = algorithm_type;
  1120. options.sparse_linear_algebra_library_type =
  1121. sparse_linear_algebra_library_type;
  1122. options.num_threads = num_threads;
  1123. Covariance covariance(options);
  1124. EXPECT_TRUE(covariance.Compute(all_covariance_blocks_, &problem_));
  1125. Matrix expected(parameter_block_size_, parameter_block_size_);
  1126. Matrix actual(parameter_block_size_, parameter_block_size_);
  1127. const double kTolerance = 1e-16;
  1128. for (int i = 0; i < num_parameter_blocks_; ++i) {
  1129. expected.setIdentity();
  1130. expected /= (i + 1.0) * (i + 1.0);
  1131. double* block_i = parameters_.get() + i * parameter_block_size_;
  1132. covariance.GetCovarianceBlock(block_i, block_i, actual.data());
  1133. EXPECT_NEAR((expected - actual).norm(), 0.0, kTolerance)
  1134. << "block: " << i << ", " << i << "\n"
  1135. << "expected: \n"
  1136. << expected << "\n"
  1137. << "actual: \n"
  1138. << actual;
  1139. expected.setZero();
  1140. for (int j = i + 1; j < num_parameter_blocks_; ++j) {
  1141. double* block_j = parameters_.get() + j * parameter_block_size_;
  1142. covariance.GetCovarianceBlock(block_i, block_j, actual.data());
  1143. EXPECT_NEAR((expected - actual).norm(), 0.0, kTolerance)
  1144. << "block: " << i << ", " << j << "\n"
  1145. << "expected: \n"
  1146. << expected << "\n"
  1147. << "actual: \n"
  1148. << actual;
  1149. }
  1150. }
  1151. }
  1152. std::unique_ptr<double[]> parameters_;
  1153. int parameter_block_size_;
  1154. int num_parameter_blocks_;
  1155. Problem problem_;
  1156. std::vector<std::pair<const double*, const double*>> all_covariance_blocks_;
  1157. };
  1158. #if !defined(CERES_NO_SUITESPARSE)
  1159. TEST_F(LargeScaleCovarianceTest, Parallel) {
  1160. ComputeAndCompare(SPARSE_QR, SUITE_SPARSE, 4);
  1161. }
  1162. #endif // !defined(CERES_NO_SUITESPARSE)
  1163. } // namespace internal
  1164. } // namespace ceres