problem_test.cc 80 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312
  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. // keir@google.com (Keir Mierle)
  31. #include "ceres/problem.h"
  32. #include <memory>
  33. #include <string>
  34. #include <vector>
  35. #include "ceres/autodiff_cost_function.h"
  36. #include "ceres/casts.h"
  37. #include "ceres/cost_function.h"
  38. #include "ceres/crs_matrix.h"
  39. #include "ceres/evaluator_test_utils.h"
  40. #include "ceres/internal/eigen.h"
  41. #include "ceres/loss_function.h"
  42. #include "ceres/map_util.h"
  43. #include "ceres/parameter_block.h"
  44. #include "ceres/problem_impl.h"
  45. #include "ceres/program.h"
  46. #include "ceres/sized_cost_function.h"
  47. #include "ceres/sparse_matrix.h"
  48. #include "ceres/types.h"
  49. #include "gmock/gmock.h"
  50. #include "gtest/gtest.h"
  51. namespace ceres::internal {
  52. // The following three classes are for the purposes of defining
  53. // function signatures. They have dummy Evaluate functions.
  54. // Trivial cost function that accepts a single argument.
  55. class UnaryCostFunction : public CostFunction {
  56. public:
  57. UnaryCostFunction(int num_residuals, int32_t parameter_block_size) {
  58. set_num_residuals(num_residuals);
  59. mutable_parameter_block_sizes()->push_back(parameter_block_size);
  60. }
  61. bool Evaluate(double const* const* parameters,
  62. double* residuals,
  63. double** jacobians) const final {
  64. for (int i = 0; i < num_residuals(); ++i) {
  65. residuals[i] = 1;
  66. }
  67. return true;
  68. }
  69. };
  70. // Trivial cost function that accepts two arguments.
  71. class BinaryCostFunction : public CostFunction {
  72. public:
  73. BinaryCostFunction(int num_residuals,
  74. int32_t parameter_block1_size,
  75. int32_t parameter_block2_size) {
  76. set_num_residuals(num_residuals);
  77. mutable_parameter_block_sizes()->push_back(parameter_block1_size);
  78. mutable_parameter_block_sizes()->push_back(parameter_block2_size);
  79. }
  80. bool Evaluate(double const* const* parameters,
  81. double* residuals,
  82. double** jacobians) const final {
  83. for (int i = 0; i < num_residuals(); ++i) {
  84. residuals[i] = 2;
  85. }
  86. return true;
  87. }
  88. };
  89. // Trivial cost function that accepts three arguments.
  90. class TernaryCostFunction : public CostFunction {
  91. public:
  92. TernaryCostFunction(int num_residuals,
  93. int32_t parameter_block1_size,
  94. int32_t parameter_block2_size,
  95. int32_t parameter_block3_size) {
  96. set_num_residuals(num_residuals);
  97. mutable_parameter_block_sizes()->push_back(parameter_block1_size);
  98. mutable_parameter_block_sizes()->push_back(parameter_block2_size);
  99. mutable_parameter_block_sizes()->push_back(parameter_block3_size);
  100. }
  101. bool Evaluate(double const* const* parameters,
  102. double* residuals,
  103. double** jacobians) const final {
  104. for (int i = 0; i < num_residuals(); ++i) {
  105. residuals[i] = 3;
  106. }
  107. return true;
  108. }
  109. };
  110. TEST(Problem, MoveConstructor) {
  111. Problem src;
  112. double x;
  113. src.AddParameterBlock(&x, 1);
  114. Problem dst(std::move(src));
  115. EXPECT_TRUE(dst.HasParameterBlock(&x));
  116. }
  117. TEST(Problem, MoveAssignment) {
  118. Problem src;
  119. double x;
  120. src.AddParameterBlock(&x, 1);
  121. Problem dst;
  122. dst = std::move(src);
  123. EXPECT_TRUE(dst.HasParameterBlock(&x));
  124. }
  125. TEST(Problem, AddResidualWithNullCostFunctionDies) {
  126. double x[3], y[4], z[5];
  127. Problem problem;
  128. problem.AddParameterBlock(x, 3);
  129. problem.AddParameterBlock(y, 4);
  130. problem.AddParameterBlock(z, 5);
  131. EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(nullptr, nullptr, x),
  132. "cost_function != nullptr");
  133. }
  134. TEST(Problem, AddResidualWithIncorrectNumberOfParameterBlocksDies) {
  135. double x[3], y[4], z[5];
  136. Problem problem;
  137. problem.AddParameterBlock(x, 3);
  138. problem.AddParameterBlock(y, 4);
  139. problem.AddParameterBlock(z, 5);
  140. // UnaryCostFunction takes only one parameter, but two are passed.
  141. EXPECT_DEATH_IF_SUPPORTED(
  142. problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x, y),
  143. "num_parameter_blocks");
  144. }
  145. TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) {
  146. double x[3];
  147. Problem problem;
  148. problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
  149. EXPECT_DEATH_IF_SUPPORTED(
  150. problem.AddResidualBlock(
  151. new UnaryCostFunction(2, 4 /* 4 != 3 */), nullptr, x),
  152. "different block sizes");
  153. }
  154. TEST(Problem, AddResidualWithDuplicateParametersDies) {
  155. double x[3], z[5];
  156. Problem problem;
  157. EXPECT_DEATH_IF_SUPPORTED(
  158. problem.AddResidualBlock(new BinaryCostFunction(2, 3, 3), nullptr, x, x),
  159. "Duplicate parameter blocks");
  160. EXPECT_DEATH_IF_SUPPORTED(
  161. problem.AddResidualBlock(
  162. new TernaryCostFunction(1, 5, 3, 5), nullptr, z, x, z),
  163. "Duplicate parameter blocks");
  164. }
  165. TEST(Problem, AddResidualWithIncorrectSizesOfParameterBlockDies) {
  166. double x[3], y[4], z[5];
  167. Problem problem;
  168. problem.AddParameterBlock(x, 3);
  169. problem.AddParameterBlock(y, 4);
  170. problem.AddParameterBlock(z, 5);
  171. // The cost function expects the size of the second parameter, z, to be 4
  172. // instead of 5 as declared above. This is fatal.
  173. EXPECT_DEATH_IF_SUPPORTED(
  174. problem.AddResidualBlock(new BinaryCostFunction(2, 3, 4), nullptr, x, z),
  175. "different block sizes");
  176. }
  177. TEST(Problem, AddResidualAddsDuplicatedParametersOnlyOnce) {
  178. double x[3], y[4], z[5];
  179. Problem problem;
  180. problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
  181. problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
  182. problem.AddResidualBlock(new UnaryCostFunction(2, 4), nullptr, y);
  183. problem.AddResidualBlock(new UnaryCostFunction(2, 5), nullptr, z);
  184. EXPECT_EQ(3, problem.NumParameterBlocks());
  185. EXPECT_EQ(12, problem.NumParameters());
  186. }
  187. TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) {
  188. double x[3], y[4];
  189. Problem problem;
  190. problem.AddParameterBlock(x, 3);
  191. problem.AddParameterBlock(y, 4);
  192. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4),
  193. "different block sizes");
  194. }
  195. static double* IntToPtr(int i) {
  196. return reinterpret_cast<double*>(sizeof(double) * i); // NOLINT
  197. }
  198. TEST(Problem, AddParameterWithAliasedParametersDies) {
  199. // Layout is
  200. //
  201. // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
  202. // [x] x x x x [y] y y
  203. // o==o==o o==o==o o==o
  204. // o--o--o o--o--o o--o o--o--o
  205. //
  206. // Parameter block additions are tested as listed above; expected successful
  207. // ones marked with o==o and aliasing ones marked with o--o.
  208. Problem problem;
  209. problem.AddParameterBlock(IntToPtr(5), 5); // x
  210. problem.AddParameterBlock(IntToPtr(13), 3); // y
  211. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(4), 2),
  212. "Aliasing detected");
  213. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(4), 3),
  214. "Aliasing detected");
  215. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(4), 9),
  216. "Aliasing detected");
  217. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(8), 3),
  218. "Aliasing detected");
  219. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(12), 2),
  220. "Aliasing detected");
  221. EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(14), 3),
  222. "Aliasing detected");
  223. // These ones should work.
  224. problem.AddParameterBlock(IntToPtr(2), 3);
  225. problem.AddParameterBlock(IntToPtr(10), 3);
  226. problem.AddParameterBlock(IntToPtr(16), 2);
  227. ASSERT_EQ(5, problem.NumParameterBlocks());
  228. }
  229. TEST(Problem, AddParameterIgnoresDuplicateCalls) {
  230. double x[3], y[4];
  231. Problem problem;
  232. problem.AddParameterBlock(x, 3);
  233. problem.AddParameterBlock(y, 4);
  234. // Creating parameter blocks multiple times is ignored.
  235. problem.AddParameterBlock(x, 3);
  236. problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
  237. // ... even repeatedly.
  238. problem.AddParameterBlock(x, 3);
  239. problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
  240. // More parameters are fine.
  241. problem.AddParameterBlock(y, 4);
  242. problem.AddResidualBlock(new UnaryCostFunction(2, 4), nullptr, y);
  243. EXPECT_EQ(2, problem.NumParameterBlocks());
  244. EXPECT_EQ(7, problem.NumParameters());
  245. }
  246. class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> {
  247. public:
  248. explicit DestructorCountingCostFunction(int* num_destructions)
  249. : num_destructions_(num_destructions) {}
  250. ~DestructorCountingCostFunction() override { *num_destructions_ += 1; }
  251. bool Evaluate(double const* const* parameters,
  252. double* residuals,
  253. double** jacobians) const final {
  254. return true;
  255. }
  256. private:
  257. int* num_destructions_;
  258. };
  259. TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) {
  260. double y[4], z[5];
  261. int num_destructions = 0;
  262. // Add a cost function multiple times and check to make sure that
  263. // the destructor on the cost function is only called once.
  264. {
  265. Problem problem;
  266. problem.AddParameterBlock(y, 4);
  267. problem.AddParameterBlock(z, 5);
  268. CostFunction* cost = new DestructorCountingCostFunction(&num_destructions);
  269. problem.AddResidualBlock(cost, nullptr, y, z);
  270. problem.AddResidualBlock(cost, nullptr, y, z);
  271. problem.AddResidualBlock(cost, nullptr, y, z);
  272. EXPECT_EQ(3, problem.NumResidualBlocks());
  273. }
  274. // Check that the destructor was called only once.
  275. CHECK_EQ(num_destructions, 1);
  276. }
  277. TEST(Problem, GetCostFunctionForResidualBlock) {
  278. double x[3];
  279. Problem problem;
  280. CostFunction* cost_function = new UnaryCostFunction(2, 3);
  281. const ResidualBlockId residual_block =
  282. problem.AddResidualBlock(cost_function, nullptr, x);
  283. EXPECT_EQ(problem.GetCostFunctionForResidualBlock(residual_block),
  284. cost_function);
  285. EXPECT_TRUE(problem.GetLossFunctionForResidualBlock(residual_block) ==
  286. nullptr);
  287. }
  288. TEST(Problem, GetLossFunctionForResidualBlock) {
  289. double x[3];
  290. Problem problem;
  291. CostFunction* cost_function = new UnaryCostFunction(2, 3);
  292. LossFunction* loss_function = new TrivialLoss();
  293. const ResidualBlockId residual_block =
  294. problem.AddResidualBlock(cost_function, loss_function, x);
  295. EXPECT_EQ(problem.GetCostFunctionForResidualBlock(residual_block),
  296. cost_function);
  297. EXPECT_EQ(problem.GetLossFunctionForResidualBlock(residual_block),
  298. loss_function);
  299. }
  300. TEST(Problem, CostFunctionsAreDeletedEvenWithRemovals) {
  301. double y[4], z[5], w[4];
  302. int num_destructions = 0;
  303. {
  304. Problem problem;
  305. problem.AddParameterBlock(y, 4);
  306. problem.AddParameterBlock(z, 5);
  307. CostFunction* cost_yz =
  308. new DestructorCountingCostFunction(&num_destructions);
  309. CostFunction* cost_wz =
  310. new DestructorCountingCostFunction(&num_destructions);
  311. ResidualBlock* r_yz = problem.AddResidualBlock(cost_yz, nullptr, y, z);
  312. ResidualBlock* r_wz = problem.AddResidualBlock(cost_wz, nullptr, w, z);
  313. EXPECT_EQ(2, problem.NumResidualBlocks());
  314. problem.RemoveResidualBlock(r_yz);
  315. CHECK_EQ(num_destructions, 1);
  316. problem.RemoveResidualBlock(r_wz);
  317. CHECK_EQ(num_destructions, 2);
  318. EXPECT_EQ(0, problem.NumResidualBlocks());
  319. }
  320. CHECK_EQ(num_destructions, 2);
  321. }
  322. // Make the dynamic problem tests (e.g. for removing residual blocks)
  323. // parameterized on whether the low-latency mode is enabled or not.
  324. //
  325. // This tests against ProblemImpl instead of Problem in order to inspect the
  326. // state of the resulting Program; this is difficult with only the thin Problem
  327. // interface.
  328. struct DynamicProblem : public ::testing::TestWithParam<bool> {
  329. DynamicProblem() {
  330. Problem::Options options;
  331. options.enable_fast_removal = GetParam();
  332. problem = std::make_unique<ProblemImpl>(options);
  333. }
  334. ParameterBlock* GetParameterBlock(int block) {
  335. return problem->program().parameter_blocks()[block];
  336. }
  337. ResidualBlock* GetResidualBlock(int block) {
  338. return problem->program().residual_blocks()[block];
  339. }
  340. bool HasResidualBlock(ResidualBlock* residual_block) {
  341. bool have_residual_block = true;
  342. if (GetParam()) {
  343. have_residual_block &=
  344. (problem->residual_block_set().find(residual_block) !=
  345. problem->residual_block_set().end());
  346. }
  347. have_residual_block &=
  348. find(problem->program().residual_blocks().begin(),
  349. problem->program().residual_blocks().end(),
  350. residual_block) != problem->program().residual_blocks().end();
  351. return have_residual_block;
  352. }
  353. int NumResidualBlocks() {
  354. // Verify that the hash set of residuals is maintained consistently.
  355. if (GetParam()) {
  356. EXPECT_EQ(problem->residual_block_set().size(),
  357. problem->NumResidualBlocks());
  358. }
  359. return problem->NumResidualBlocks();
  360. }
  361. // The next block of functions until the end are only for testing the
  362. // residual block removals.
  363. void ExpectParameterBlockContainsResidualBlock(
  364. double* values, ResidualBlock* residual_block) {
  365. ParameterBlock* parameter_block =
  366. FindOrDie(problem->parameter_map(), values);
  367. EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()),
  368. residual_block));
  369. }
  370. void ExpectSize(double* values, int size) {
  371. ParameterBlock* parameter_block =
  372. FindOrDie(problem->parameter_map(), values);
  373. EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size());
  374. }
  375. // Degenerate case.
  376. void ExpectParameterBlockContains(double* values) { ExpectSize(values, 0); }
  377. void ExpectParameterBlockContains(double* values, ResidualBlock* r1) {
  378. ExpectSize(values, 1);
  379. ExpectParameterBlockContainsResidualBlock(values, r1);
  380. }
  381. void ExpectParameterBlockContains(double* values,
  382. ResidualBlock* r1,
  383. ResidualBlock* r2) {
  384. ExpectSize(values, 2);
  385. ExpectParameterBlockContainsResidualBlock(values, r1);
  386. ExpectParameterBlockContainsResidualBlock(values, r2);
  387. }
  388. void ExpectParameterBlockContains(double* values,
  389. ResidualBlock* r1,
  390. ResidualBlock* r2,
  391. ResidualBlock* r3) {
  392. ExpectSize(values, 3);
  393. ExpectParameterBlockContainsResidualBlock(values, r1);
  394. ExpectParameterBlockContainsResidualBlock(values, r2);
  395. ExpectParameterBlockContainsResidualBlock(values, r3);
  396. }
  397. void ExpectParameterBlockContains(double* values,
  398. ResidualBlock* r1,
  399. ResidualBlock* r2,
  400. ResidualBlock* r3,
  401. ResidualBlock* r4) {
  402. ExpectSize(values, 4);
  403. ExpectParameterBlockContainsResidualBlock(values, r1);
  404. ExpectParameterBlockContainsResidualBlock(values, r2);
  405. ExpectParameterBlockContainsResidualBlock(values, r3);
  406. ExpectParameterBlockContainsResidualBlock(values, r4);
  407. }
  408. std::unique_ptr<ProblemImpl> problem;
  409. double y[4], z[5], w[3];
  410. };
  411. TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) {
  412. double x[3];
  413. double y[2];
  414. Problem problem;
  415. problem.AddParameterBlock(x, 3);
  416. EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y),
  417. "Parameter block not found:");
  418. }
  419. TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) {
  420. double x[3];
  421. double y[2];
  422. Problem problem;
  423. problem.AddParameterBlock(x, 3);
  424. EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y),
  425. "Parameter block not found:");
  426. }
  427. TEST(Problem, IsParameterBlockConstant) {
  428. double x1[3];
  429. double x2[3];
  430. Problem problem;
  431. problem.AddParameterBlock(x1, 3);
  432. problem.AddParameterBlock(x2, 3);
  433. EXPECT_FALSE(problem.IsParameterBlockConstant(x1));
  434. EXPECT_FALSE(problem.IsParameterBlockConstant(x2));
  435. problem.SetParameterBlockConstant(x1);
  436. EXPECT_TRUE(problem.IsParameterBlockConstant(x1));
  437. EXPECT_FALSE(problem.IsParameterBlockConstant(x2));
  438. problem.SetParameterBlockConstant(x2);
  439. EXPECT_TRUE(problem.IsParameterBlockConstant(x1));
  440. EXPECT_TRUE(problem.IsParameterBlockConstant(x2));
  441. problem.SetParameterBlockVariable(x1);
  442. EXPECT_FALSE(problem.IsParameterBlockConstant(x1));
  443. EXPECT_TRUE(problem.IsParameterBlockConstant(x2));
  444. }
  445. TEST(Problem, IsParameterBlockConstantWithUnknownPtrDies) {
  446. double x[3];
  447. double y[2];
  448. Problem problem;
  449. problem.AddParameterBlock(x, 3);
  450. EXPECT_DEATH_IF_SUPPORTED(problem.IsParameterBlockConstant(y),
  451. "Parameter block not found:");
  452. }
  453. TEST(Problem, SetManifoldWithUnknownPtrDies) {
  454. double x[3];
  455. double y[2];
  456. Problem problem;
  457. problem.AddParameterBlock(x, 3);
  458. EXPECT_DEATH_IF_SUPPORTED(problem.SetManifold(y, new EuclideanManifold<3>),
  459. "Parameter block not found:");
  460. }
  461. TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) {
  462. double x[3];
  463. double y[2];
  464. Problem problem;
  465. problem.AddParameterBlock(x, 3);
  466. EXPECT_DEATH_IF_SUPPORTED(problem.RemoveParameterBlock(y),
  467. "Parameter block not found:");
  468. }
  469. TEST(Problem, GetManifold) {
  470. double x[3];
  471. double y[2];
  472. Problem problem;
  473. problem.AddParameterBlock(x, 3);
  474. problem.AddParameterBlock(y, 2);
  475. Manifold* manifold = new EuclideanManifold<3>;
  476. problem.SetManifold(x, manifold);
  477. EXPECT_EQ(problem.GetManifold(x), manifold);
  478. EXPECT_TRUE(problem.GetManifold(y) == nullptr);
  479. }
  480. TEST(Problem, HasManifold) {
  481. double x[3];
  482. double y[2];
  483. Problem problem;
  484. problem.AddParameterBlock(x, 3);
  485. problem.AddParameterBlock(y, 2);
  486. Manifold* manifold = new EuclideanManifold<3>;
  487. problem.SetManifold(x, manifold);
  488. EXPECT_TRUE(problem.HasManifold(x));
  489. EXPECT_FALSE(problem.HasManifold(y));
  490. }
  491. TEST(Problem, RepeatedAddParameterBlockResetsManifold) {
  492. double x[4];
  493. double y[2];
  494. Problem problem;
  495. problem.AddParameterBlock(x, 4, new SubsetManifold(4, {0, 1}));
  496. problem.AddParameterBlock(y, 2);
  497. EXPECT_FALSE(problem.HasManifold(y));
  498. EXPECT_TRUE(problem.HasManifold(x));
  499. EXPECT_EQ(problem.ParameterBlockSize(x), 4);
  500. EXPECT_EQ(problem.ParameterBlockTangentSize(x), 2);
  501. EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 4);
  502. EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 2);
  503. problem.AddParameterBlock(x, 4, static_cast<Manifold*>(nullptr));
  504. EXPECT_FALSE(problem.HasManifold(x));
  505. EXPECT_EQ(problem.ParameterBlockSize(x), 4);
  506. EXPECT_EQ(problem.ParameterBlockTangentSize(x), 4);
  507. EXPECT_EQ(problem.GetManifold(x), nullptr);
  508. problem.AddParameterBlock(x, 4, new SubsetManifold(4, {0, 1, 2}));
  509. problem.AddParameterBlock(y, 2);
  510. EXPECT_TRUE(problem.HasManifold(x));
  511. EXPECT_EQ(problem.ParameterBlockSize(x), 4);
  512. EXPECT_EQ(problem.ParameterBlockTangentSize(x), 1);
  513. EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 4);
  514. EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 1);
  515. }
  516. TEST(Problem, ParameterBlockQueryTestUsingManifold) {
  517. double x[3];
  518. double y[4];
  519. Problem problem;
  520. problem.AddParameterBlock(x, 3);
  521. problem.AddParameterBlock(y, 4);
  522. std::vector<int> constant_parameters;
  523. constant_parameters.push_back(0);
  524. problem.SetManifold(x, new SubsetManifold(3, constant_parameters));
  525. EXPECT_EQ(problem.ParameterBlockSize(x), 3);
  526. EXPECT_EQ(problem.ParameterBlockTangentSize(x), 2);
  527. EXPECT_EQ(problem.ParameterBlockTangentSize(y), 4);
  528. std::vector<double*> parameter_blocks;
  529. problem.GetParameterBlocks(&parameter_blocks);
  530. EXPECT_EQ(parameter_blocks.size(), 2);
  531. EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);
  532. EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);
  533. EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);
  534. EXPECT_TRUE(problem.HasParameterBlock(x));
  535. problem.RemoveParameterBlock(x);
  536. EXPECT_FALSE(problem.HasParameterBlock(x));
  537. problem.GetParameterBlocks(&parameter_blocks);
  538. EXPECT_EQ(parameter_blocks.size(), 1);
  539. EXPECT_TRUE(parameter_blocks[0] == y);
  540. }
  541. TEST(Problem, ParameterBlockQueryTest) {
  542. double x[3];
  543. double y[4];
  544. Problem problem;
  545. problem.AddParameterBlock(x, 3);
  546. problem.AddParameterBlock(y, 4);
  547. std::vector<int> constant_parameters;
  548. constant_parameters.push_back(0);
  549. problem.SetManifold(x, new SubsetManifold(3, constant_parameters));
  550. EXPECT_EQ(problem.ParameterBlockSize(x), 3);
  551. EXPECT_EQ(problem.ParameterBlockTangentSize(x), 2);
  552. EXPECT_EQ(problem.ParameterBlockTangentSize(y), 4);
  553. std::vector<double*> parameter_blocks;
  554. problem.GetParameterBlocks(&parameter_blocks);
  555. EXPECT_EQ(parameter_blocks.size(), 2);
  556. EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);
  557. EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);
  558. EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);
  559. EXPECT_TRUE(problem.HasParameterBlock(x));
  560. problem.RemoveParameterBlock(x);
  561. EXPECT_FALSE(problem.HasParameterBlock(x));
  562. problem.GetParameterBlocks(&parameter_blocks);
  563. EXPECT_EQ(parameter_blocks.size(), 1);
  564. EXPECT_TRUE(parameter_blocks[0] == y);
  565. }
  566. TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) {
  567. problem->AddParameterBlock(y, 4);
  568. problem->AddParameterBlock(z, 5);
  569. problem->AddParameterBlock(w, 3);
  570. ASSERT_EQ(3, problem->NumParameterBlocks());
  571. ASSERT_EQ(0, NumResidualBlocks());
  572. EXPECT_EQ(y, GetParameterBlock(0)->user_state());
  573. EXPECT_EQ(z, GetParameterBlock(1)->user_state());
  574. EXPECT_EQ(w, GetParameterBlock(2)->user_state());
  575. // w is at the end, which might break the swapping logic so try adding and
  576. // removing it.
  577. problem->RemoveParameterBlock(w);
  578. ASSERT_EQ(2, problem->NumParameterBlocks());
  579. ASSERT_EQ(0, NumResidualBlocks());
  580. EXPECT_EQ(y, GetParameterBlock(0)->user_state());
  581. EXPECT_EQ(z, GetParameterBlock(1)->user_state());
  582. problem->AddParameterBlock(w, 3);
  583. ASSERT_EQ(3, problem->NumParameterBlocks());
  584. ASSERT_EQ(0, NumResidualBlocks());
  585. EXPECT_EQ(y, GetParameterBlock(0)->user_state());
  586. EXPECT_EQ(z, GetParameterBlock(1)->user_state());
  587. EXPECT_EQ(w, GetParameterBlock(2)->user_state());
  588. // Now remove z, which is in the middle, and add it back.
  589. problem->RemoveParameterBlock(z);
  590. ASSERT_EQ(2, problem->NumParameterBlocks());
  591. ASSERT_EQ(0, NumResidualBlocks());
  592. EXPECT_EQ(y, GetParameterBlock(0)->user_state());
  593. EXPECT_EQ(w, GetParameterBlock(1)->user_state());
  594. problem->AddParameterBlock(z, 5);
  595. ASSERT_EQ(3, problem->NumParameterBlocks());
  596. ASSERT_EQ(0, NumResidualBlocks());
  597. EXPECT_EQ(y, GetParameterBlock(0)->user_state());
  598. EXPECT_EQ(w, GetParameterBlock(1)->user_state());
  599. EXPECT_EQ(z, GetParameterBlock(2)->user_state());
  600. // Now remove everything.
  601. // y
  602. problem->RemoveParameterBlock(y);
  603. ASSERT_EQ(2, problem->NumParameterBlocks());
  604. ASSERT_EQ(0, NumResidualBlocks());
  605. EXPECT_EQ(z, GetParameterBlock(0)->user_state());
  606. EXPECT_EQ(w, GetParameterBlock(1)->user_state());
  607. // z
  608. problem->RemoveParameterBlock(z);
  609. ASSERT_EQ(1, problem->NumParameterBlocks());
  610. ASSERT_EQ(0, NumResidualBlocks());
  611. EXPECT_EQ(w, GetParameterBlock(0)->user_state());
  612. // w
  613. problem->RemoveParameterBlock(w);
  614. EXPECT_EQ(0, problem->NumParameterBlocks());
  615. EXPECT_EQ(0, NumResidualBlocks());
  616. }
  617. TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) {
  618. problem->AddParameterBlock(y, 4);
  619. problem->AddParameterBlock(z, 5);
  620. problem->AddParameterBlock(w, 3);
  621. ASSERT_EQ(3, problem->NumParameterBlocks());
  622. ASSERT_EQ(0, NumResidualBlocks());
  623. EXPECT_EQ(y, GetParameterBlock(0)->user_state());
  624. EXPECT_EQ(z, GetParameterBlock(1)->user_state());
  625. EXPECT_EQ(w, GetParameterBlock(2)->user_state());
  626. // clang-format off
  627. // Add all combinations of cost functions.
  628. CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
  629. CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
  630. CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
  631. CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
  632. CostFunction* cost_y = new UnaryCostFunction (1, 4);
  633. CostFunction* cost_z = new UnaryCostFunction (1, 5);
  634. CostFunction* cost_w = new UnaryCostFunction (1, 3);
  635. ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
  636. ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
  637. ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
  638. ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
  639. ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
  640. ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
  641. ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
  642. EXPECT_EQ(3, problem->NumParameterBlocks());
  643. EXPECT_EQ(7, NumResidualBlocks());
  644. // Remove w, which should remove r_yzw, r_yw, r_zw, r_w.
  645. problem->RemoveParameterBlock(w);
  646. ASSERT_EQ(2, problem->NumParameterBlocks());
  647. ASSERT_EQ(3, NumResidualBlocks());
  648. ASSERT_FALSE(HasResidualBlock(r_yzw));
  649. ASSERT_TRUE (HasResidualBlock(r_yz ));
  650. ASSERT_FALSE(HasResidualBlock(r_yw ));
  651. ASSERT_FALSE(HasResidualBlock(r_zw ));
  652. ASSERT_TRUE (HasResidualBlock(r_y ));
  653. ASSERT_TRUE (HasResidualBlock(r_z ));
  654. ASSERT_FALSE(HasResidualBlock(r_w ));
  655. // Remove z, which will remove almost everything else.
  656. problem->RemoveParameterBlock(z);
  657. ASSERT_EQ(1, problem->NumParameterBlocks());
  658. ASSERT_EQ(1, NumResidualBlocks());
  659. ASSERT_FALSE(HasResidualBlock(r_yzw));
  660. ASSERT_FALSE(HasResidualBlock(r_yz ));
  661. ASSERT_FALSE(HasResidualBlock(r_yw ));
  662. ASSERT_FALSE(HasResidualBlock(r_zw ));
  663. ASSERT_TRUE (HasResidualBlock(r_y ));
  664. ASSERT_FALSE(HasResidualBlock(r_z ));
  665. ASSERT_FALSE(HasResidualBlock(r_w ));
  666. // Remove y; all gone.
  667. problem->RemoveParameterBlock(y);
  668. EXPECT_EQ(0, problem->NumParameterBlocks());
  669. EXPECT_EQ(0, NumResidualBlocks());
  670. // clang-format on
  671. }
  672. TEST_P(DynamicProblem, RemoveResidualBlock) {
  673. problem->AddParameterBlock(y, 4);
  674. problem->AddParameterBlock(z, 5);
  675. problem->AddParameterBlock(w, 3);
  676. // clang-format off
  677. // Add all combinations of cost functions.
  678. CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
  679. CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
  680. CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
  681. CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
  682. CostFunction* cost_y = new UnaryCostFunction (1, 4);
  683. CostFunction* cost_z = new UnaryCostFunction (1, 5);
  684. CostFunction* cost_w = new UnaryCostFunction (1, 3);
  685. ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
  686. ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
  687. ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
  688. ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
  689. ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
  690. ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
  691. ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
  692. if (GetParam()) {
  693. // In this test parameterization, there should be back-pointers from the
  694. // parameter blocks to the residual blocks.
  695. ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y);
  696. ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z);
  697. ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w);
  698. } else {
  699. // Otherwise, nothing.
  700. EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == nullptr);
  701. EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == nullptr);
  702. EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == nullptr);
  703. }
  704. EXPECT_EQ(3, problem->NumParameterBlocks());
  705. EXPECT_EQ(7, NumResidualBlocks());
  706. // Remove each residual and check the state after each removal.
  707. // Remove r_yzw.
  708. problem->RemoveResidualBlock(r_yzw);
  709. ASSERT_EQ(3, problem->NumParameterBlocks());
  710. ASSERT_EQ(6, NumResidualBlocks());
  711. if (GetParam()) {
  712. ExpectParameterBlockContains(y, r_yz, r_yw, r_y);
  713. ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
  714. ExpectParameterBlockContains(w, r_yw, r_zw, r_w);
  715. }
  716. ASSERT_TRUE (HasResidualBlock(r_yz ));
  717. ASSERT_TRUE (HasResidualBlock(r_yw ));
  718. ASSERT_TRUE (HasResidualBlock(r_zw ));
  719. ASSERT_TRUE (HasResidualBlock(r_y ));
  720. ASSERT_TRUE (HasResidualBlock(r_z ));
  721. ASSERT_TRUE (HasResidualBlock(r_w ));
  722. // Remove r_yw.
  723. problem->RemoveResidualBlock(r_yw);
  724. ASSERT_EQ(3, problem->NumParameterBlocks());
  725. ASSERT_EQ(5, NumResidualBlocks());
  726. if (GetParam()) {
  727. ExpectParameterBlockContains(y, r_yz, r_y);
  728. ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
  729. ExpectParameterBlockContains(w, r_zw, r_w);
  730. }
  731. ASSERT_TRUE (HasResidualBlock(r_yz ));
  732. ASSERT_TRUE (HasResidualBlock(r_zw ));
  733. ASSERT_TRUE (HasResidualBlock(r_y ));
  734. ASSERT_TRUE (HasResidualBlock(r_z ));
  735. ASSERT_TRUE (HasResidualBlock(r_w ));
  736. // Remove r_zw.
  737. problem->RemoveResidualBlock(r_zw);
  738. ASSERT_EQ(3, problem->NumParameterBlocks());
  739. ASSERT_EQ(4, NumResidualBlocks());
  740. if (GetParam()) {
  741. ExpectParameterBlockContains(y, r_yz, r_y);
  742. ExpectParameterBlockContains(z, r_yz, r_z);
  743. ExpectParameterBlockContains(w, r_w);
  744. }
  745. ASSERT_TRUE (HasResidualBlock(r_yz ));
  746. ASSERT_TRUE (HasResidualBlock(r_y ));
  747. ASSERT_TRUE (HasResidualBlock(r_z ));
  748. ASSERT_TRUE (HasResidualBlock(r_w ));
  749. // Remove r_w.
  750. problem->RemoveResidualBlock(r_w);
  751. ASSERT_EQ(3, problem->NumParameterBlocks());
  752. ASSERT_EQ(3, NumResidualBlocks());
  753. if (GetParam()) {
  754. ExpectParameterBlockContains(y, r_yz, r_y);
  755. ExpectParameterBlockContains(z, r_yz, r_z);
  756. ExpectParameterBlockContains(w);
  757. }
  758. ASSERT_TRUE (HasResidualBlock(r_yz ));
  759. ASSERT_TRUE (HasResidualBlock(r_y ));
  760. ASSERT_TRUE (HasResidualBlock(r_z ));
  761. // Remove r_yz.
  762. problem->RemoveResidualBlock(r_yz);
  763. ASSERT_EQ(3, problem->NumParameterBlocks());
  764. ASSERT_EQ(2, NumResidualBlocks());
  765. if (GetParam()) {
  766. ExpectParameterBlockContains(y, r_y);
  767. ExpectParameterBlockContains(z, r_z);
  768. ExpectParameterBlockContains(w);
  769. }
  770. ASSERT_TRUE (HasResidualBlock(r_y ));
  771. ASSERT_TRUE (HasResidualBlock(r_z ));
  772. // Remove the last two.
  773. problem->RemoveResidualBlock(r_z);
  774. problem->RemoveResidualBlock(r_y);
  775. ASSERT_EQ(3, problem->NumParameterBlocks());
  776. ASSERT_EQ(0, NumResidualBlocks());
  777. if (GetParam()) {
  778. ExpectParameterBlockContains(y);
  779. ExpectParameterBlockContains(z);
  780. ExpectParameterBlockContains(w);
  781. }
  782. // clang-format on
  783. }
  784. TEST_P(DynamicProblem, RemoveInvalidResidualBlockDies) {
  785. problem->AddParameterBlock(y, 4);
  786. problem->AddParameterBlock(z, 5);
  787. problem->AddParameterBlock(w, 3);
  788. // clang-format off
  789. // Add all combinations of cost functions.
  790. CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
  791. CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
  792. CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
  793. CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
  794. CostFunction* cost_y = new UnaryCostFunction (1, 4);
  795. CostFunction* cost_z = new UnaryCostFunction (1, 5);
  796. CostFunction* cost_w = new UnaryCostFunction (1, 3);
  797. ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
  798. ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
  799. ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
  800. ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
  801. ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
  802. ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
  803. ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
  804. // clang-format on
  805. // Remove r_yzw.
  806. problem->RemoveResidualBlock(r_yzw);
  807. ASSERT_EQ(3, problem->NumParameterBlocks());
  808. ASSERT_EQ(6, NumResidualBlocks());
  809. // Attempt to remove r_yzw again.
  810. EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yzw), "not found");
  811. // Attempt to remove a cast pointer never added as a residual.
  812. int trash_memory = 1234;
  813. auto* invalid_residual = reinterpret_cast<ResidualBlock*>(&trash_memory);
  814. EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(invalid_residual),
  815. "not found");
  816. // Remove a parameter block, which in turn removes the dependent residuals
  817. // then attempt to remove them directly.
  818. problem->RemoveParameterBlock(z);
  819. ASSERT_EQ(2, problem->NumParameterBlocks());
  820. ASSERT_EQ(3, NumResidualBlocks());
  821. EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yz), "not found");
  822. EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_zw), "not found");
  823. EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_z), "not found");
  824. problem->RemoveResidualBlock(r_yw);
  825. problem->RemoveResidualBlock(r_w);
  826. problem->RemoveResidualBlock(r_y);
  827. }
  828. // Check that a null-terminated array, a, has the same elements as b.
  829. template <typename T>
  830. void ExpectVectorContainsUnordered(const T* a, const std::vector<T>& b) {
  831. // Compute the size of a.
  832. int size = 0;
  833. while (a[size]) {
  834. ++size;
  835. }
  836. ASSERT_EQ(size, b.size());
  837. // Sort a.
  838. std::vector<T> a_sorted(size);
  839. copy(a, a + size, a_sorted.begin());
  840. sort(a_sorted.begin(), a_sorted.end());
  841. // Sort b.
  842. std::vector<T> b_sorted(b);
  843. sort(b_sorted.begin(), b_sorted.end());
  844. // Compare.
  845. for (int i = 0; i < size; ++i) {
  846. EXPECT_EQ(a_sorted[i], b_sorted[i]);
  847. }
  848. }
  849. static void ExpectProblemHasResidualBlocks(
  850. const ProblemImpl& problem,
  851. const ResidualBlockId* expected_residual_blocks) {
  852. std::vector<ResidualBlockId> residual_blocks;
  853. problem.GetResidualBlocks(&residual_blocks);
  854. ExpectVectorContainsUnordered(expected_residual_blocks, residual_blocks);
  855. }
  856. TEST_P(DynamicProblem, GetXXXBlocksForYYYBlock) {
  857. problem->AddParameterBlock(y, 4);
  858. problem->AddParameterBlock(z, 5);
  859. problem->AddParameterBlock(w, 3);
  860. // clang-format off
  861. // Add all combinations of cost functions.
  862. CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
  863. CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
  864. CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
  865. CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
  866. CostFunction* cost_y = new UnaryCostFunction (1, 4);
  867. CostFunction* cost_z = new UnaryCostFunction (1, 5);
  868. CostFunction* cost_w = new UnaryCostFunction (1, 3);
  869. ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
  870. {
  871. ResidualBlockId expected_residuals[] = {r_yzw, nullptr};
  872. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  873. }
  874. ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
  875. {
  876. ResidualBlockId expected_residuals[] = {r_yzw, r_yz, nullptr};
  877. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  878. }
  879. ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
  880. {
  881. ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, nullptr};
  882. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  883. }
  884. ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
  885. {
  886. ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, nullptr};
  887. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  888. }
  889. ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
  890. {
  891. ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, r_y, nullptr};
  892. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  893. }
  894. ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
  895. {
  896. ResidualBlock *expected_residuals[] = {
  897. r_yzw, r_yz, r_yw, r_zw, r_y, r_z, nullptr
  898. };
  899. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  900. }
  901. ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
  902. {
  903. ResidualBlock *expected_residuals[] = {
  904. r_yzw, r_yz, r_yw, r_zw, r_y, r_z, r_w, nullptr
  905. };
  906. ExpectProblemHasResidualBlocks(*problem, expected_residuals);
  907. }
  908. std::vector<double*> parameter_blocks;
  909. std::vector<ResidualBlockId> residual_blocks;
  910. // Check GetResidualBlocksForParameterBlock() for all parameter blocks.
  911. struct GetResidualBlocksForParameterBlockTestCase {
  912. double* parameter_block;
  913. ResidualBlockId expected_residual_blocks[10];
  914. };
  915. GetResidualBlocksForParameterBlockTestCase get_residual_blocks_cases[] = {
  916. { y, { r_yzw, r_yz, r_yw, r_y, nullptr} },
  917. { z, { r_yzw, r_yz, r_zw, r_z, nullptr} },
  918. { w, { r_yzw, r_yw, r_zw, r_w, nullptr} },
  919. { nullptr, { nullptr } }
  920. };
  921. for (int i = 0; get_residual_blocks_cases[i].parameter_block; ++i) {
  922. problem->GetResidualBlocksForParameterBlock(
  923. get_residual_blocks_cases[i].parameter_block,
  924. &residual_blocks);
  925. ExpectVectorContainsUnordered(
  926. get_residual_blocks_cases[i].expected_residual_blocks,
  927. residual_blocks);
  928. }
  929. // Check GetParameterBlocksForResidualBlock() for all residual blocks.
  930. struct GetParameterBlocksForResidualBlockTestCase {
  931. ResidualBlockId residual_block;
  932. double* expected_parameter_blocks[10];
  933. };
  934. GetParameterBlocksForResidualBlockTestCase get_parameter_blocks_cases[] = {
  935. { r_yzw, { y, z, w, nullptr } },
  936. { r_yz , { y, z, nullptr } },
  937. { r_yw , { y, w, nullptr } },
  938. { r_zw , { z, w, nullptr } },
  939. { r_y , { y, nullptr } },
  940. { r_z , { z, nullptr } },
  941. { r_w , { w, nullptr } },
  942. { nullptr, { nullptr } }
  943. };
  944. for (int i = 0; get_parameter_blocks_cases[i].residual_block; ++i) {
  945. problem->GetParameterBlocksForResidualBlock(
  946. get_parameter_blocks_cases[i].residual_block,
  947. &parameter_blocks);
  948. ExpectVectorContainsUnordered(
  949. get_parameter_blocks_cases[i].expected_parameter_blocks,
  950. parameter_blocks);
  951. }
  952. // clang-format on
  953. }
  954. INSTANTIATE_TEST_SUITE_P(OptionsInstantiation,
  955. DynamicProblem,
  956. ::testing::Values(true, false));
  957. // Test for Problem::Evaluate
  958. // r_i = i - (j + 1) * x_ij^2
  959. template <int kNumResiduals, int kNumParameterBlocks>
  960. class QuadraticCostFunction : public CostFunction {
  961. public:
  962. QuadraticCostFunction() {
  963. CHECK_GT(kNumResiduals, 0);
  964. CHECK_GT(kNumParameterBlocks, 0);
  965. set_num_residuals(kNumResiduals);
  966. for (int i = 0; i < kNumParameterBlocks; ++i) {
  967. mutable_parameter_block_sizes()->push_back(kNumResiduals);
  968. }
  969. }
  970. bool Evaluate(double const* const* parameters,
  971. double* residuals,
  972. double** jacobians) const final {
  973. for (int i = 0; i < kNumResiduals; ++i) {
  974. residuals[i] = i;
  975. for (int j = 0; j < kNumParameterBlocks; ++j) {
  976. residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i];
  977. }
  978. }
  979. if (jacobians == nullptr) {
  980. return true;
  981. }
  982. for (int j = 0; j < kNumParameterBlocks; ++j) {
  983. if (jacobians[j] != nullptr) {
  984. MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) =
  985. (-2.0 * (j + 1.0) * ConstVectorRef(parameters[j], kNumResiduals))
  986. .asDiagonal();
  987. }
  988. }
  989. return true;
  990. }
  991. };
  992. // Convert a CRSMatrix to a dense Eigen matrix.
  993. static void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) {
  994. CHECK(output != nullptr);
  995. Matrix& m = *output;
  996. m.resize(input.num_rows, input.num_cols);
  997. m.setZero();
  998. for (int row = 0; row < input.num_rows; ++row) {
  999. for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) {
  1000. const int col = input.cols[j];
  1001. m(row, col) = input.values[j];
  1002. }
  1003. }
  1004. }
  1005. class ProblemEvaluateTest : public ::testing::Test {
  1006. protected:
  1007. void SetUp() override {
  1008. for (int i = 0; i < 6; ++i) {
  1009. parameters_[i] = static_cast<double>(i + 1);
  1010. }
  1011. parameter_blocks_.push_back(parameters_);
  1012. parameter_blocks_.push_back(parameters_ + 2);
  1013. parameter_blocks_.push_back(parameters_ + 4);
  1014. CostFunction* cost_function = new QuadraticCostFunction<2, 2>;
  1015. // f(x, y)
  1016. residual_blocks_.push_back(problem_.AddResidualBlock(
  1017. cost_function, nullptr, parameters_, parameters_ + 2));
  1018. // g(y, z)
  1019. residual_blocks_.push_back(problem_.AddResidualBlock(
  1020. cost_function, nullptr, parameters_ + 2, parameters_ + 4));
  1021. // h(z, x)
  1022. residual_blocks_.push_back(problem_.AddResidualBlock(
  1023. cost_function, nullptr, parameters_ + 4, parameters_));
  1024. }
  1025. void TearDown() override { EXPECT_TRUE(problem_.program().IsValid()); }
  1026. void EvaluateAndCompare(const Problem::EvaluateOptions& options,
  1027. const int expected_num_rows,
  1028. const int expected_num_cols,
  1029. const double expected_cost,
  1030. const double* expected_residuals,
  1031. const double* expected_gradient,
  1032. const double* expected_jacobian) {
  1033. double cost;
  1034. std::vector<double> residuals;
  1035. std::vector<double> gradient;
  1036. CRSMatrix jacobian;
  1037. EXPECT_TRUE(
  1038. problem_.Evaluate(options,
  1039. &cost,
  1040. expected_residuals != nullptr ? &residuals : nullptr,
  1041. expected_gradient != nullptr ? &gradient : nullptr,
  1042. expected_jacobian != nullptr ? &jacobian : nullptr));
  1043. if (expected_residuals != nullptr) {
  1044. EXPECT_EQ(residuals.size(), expected_num_rows);
  1045. }
  1046. if (expected_gradient != nullptr) {
  1047. EXPECT_EQ(gradient.size(), expected_num_cols);
  1048. }
  1049. if (expected_jacobian != nullptr) {
  1050. EXPECT_EQ(jacobian.num_rows, expected_num_rows);
  1051. EXPECT_EQ(jacobian.num_cols, expected_num_cols);
  1052. }
  1053. Matrix dense_jacobian;
  1054. if (expected_jacobian != nullptr) {
  1055. CRSToDenseMatrix(jacobian, &dense_jacobian);
  1056. }
  1057. CompareEvaluations(expected_num_rows,
  1058. expected_num_cols,
  1059. expected_cost,
  1060. expected_residuals,
  1061. expected_gradient,
  1062. expected_jacobian,
  1063. cost,
  1064. !residuals.empty() ? &residuals[0] : nullptr,
  1065. !gradient.empty() ? &gradient[0] : nullptr,
  1066. dense_jacobian.data());
  1067. }
  1068. void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options,
  1069. const ExpectedEvaluation& expected) {
  1070. for (int i = 0; i < 8; ++i) {
  1071. EvaluateAndCompare(options,
  1072. expected.num_rows,
  1073. expected.num_cols,
  1074. expected.cost,
  1075. (i & 1) ? expected.residuals : nullptr,
  1076. (i & 2) ? expected.gradient : nullptr,
  1077. (i & 4) ? expected.jacobian : nullptr);
  1078. }
  1079. }
  1080. ProblemImpl problem_;
  1081. double parameters_[6];
  1082. std::vector<double*> parameter_blocks_;
  1083. std::vector<ResidualBlockId> residual_blocks_;
  1084. };
  1085. TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) {
  1086. // clang-format off
  1087. ExpectedEvaluation expected = {
  1088. // Rows/columns
  1089. 6, 6,
  1090. // Cost
  1091. 7607.0,
  1092. // Residuals
  1093. { -19.0, -35.0, // f
  1094. -59.0, -87.0, // g
  1095. -27.0, -43.0 // h
  1096. },
  1097. // Gradient
  1098. { 146.0, 484.0, // x
  1099. 582.0, 1256.0, // y
  1100. 1450.0, 2604.0, // z
  1101. },
  1102. // Jacobian
  1103. // x y z
  1104. { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
  1105. 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
  1106. /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
  1107. 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
  1108. /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
  1109. 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
  1110. }
  1111. };
  1112. // clang-format on
  1113. CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
  1114. }
  1115. TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) {
  1116. // clang-format off
  1117. ExpectedEvaluation expected = {
  1118. // Rows/columns
  1119. 6, 6,
  1120. // Cost
  1121. 7607.0,
  1122. // Residuals
  1123. { -19.0, -35.0, // f
  1124. -59.0, -87.0, // g
  1125. -27.0, -43.0 // h
  1126. },
  1127. // Gradient
  1128. { 146.0, 484.0, // x
  1129. 582.0, 1256.0, // y
  1130. 1450.0, 2604.0, // z
  1131. },
  1132. // Jacobian
  1133. // x y z
  1134. { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
  1135. 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
  1136. /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
  1137. 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
  1138. /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
  1139. 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
  1140. }
  1141. };
  1142. // clang-format on
  1143. Problem::EvaluateOptions evaluate_options;
  1144. evaluate_options.parameter_blocks = parameter_blocks_;
  1145. evaluate_options.residual_blocks = residual_blocks_;
  1146. CheckAllEvaluationCombinations(evaluate_options, expected);
  1147. }
  1148. TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) {
  1149. // clang-format off
  1150. ExpectedEvaluation expected = {
  1151. // Rows/columns
  1152. 6, 6,
  1153. // Cost
  1154. 7607.0,
  1155. // Residuals
  1156. { -19.0, -35.0, // f
  1157. -27.0, -43.0, // h
  1158. -59.0, -87.0 // g
  1159. },
  1160. // Gradient
  1161. { 146.0, 484.0, // x
  1162. 582.0, 1256.0, // y
  1163. 1450.0, 2604.0, // z
  1164. },
  1165. // Jacobian
  1166. // x y z
  1167. { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
  1168. 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
  1169. /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
  1170. 0.0, -8.0, 0.0, 0.0, 0.0, -12.0,
  1171. /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
  1172. 0.0, 0.0, 0.0, -8.0, 0.0, -24.0
  1173. }
  1174. };
  1175. // clang-format on
  1176. Problem::EvaluateOptions evaluate_options;
  1177. evaluate_options.parameter_blocks = parameter_blocks_;
  1178. // f, h, g
  1179. evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
  1180. evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
  1181. evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
  1182. CheckAllEvaluationCombinations(evaluate_options, expected);
  1183. }
  1184. TEST_F(ProblemEvaluateTest,
  1185. ReorderedResidualBlocksAndReorderedParameterBlocks) {
  1186. // clang-format off
  1187. ExpectedEvaluation expected = {
  1188. // Rows/columns
  1189. 6, 6,
  1190. // Cost
  1191. 7607.0,
  1192. // Residuals
  1193. { -19.0, -35.0, // f
  1194. -27.0, -43.0, // h
  1195. -59.0, -87.0 // g
  1196. },
  1197. // Gradient
  1198. { 1450.0, 2604.0, // z
  1199. 582.0, 1256.0, // y
  1200. 146.0, 484.0, // x
  1201. },
  1202. // Jacobian
  1203. // z y x
  1204. { /* f(x, y) */ 0.0, 0.0, -12.0, 0.0, -2.0, 0.0,
  1205. 0.0, 0.0, 0.0, -16.0, 0.0, -4.0,
  1206. /* h(z, x) */ -10.0, 0.0, 0.0, 0.0, -4.0, 0.0,
  1207. 0.0, -12.0, 0.0, 0.0, 0.0, -8.0,
  1208. /* g(y, z) */ -20.0, 0.0, -6.0, 0.0, 0.0, 0.0,
  1209. 0.0, -24.0, 0.0, -8.0, 0.0, 0.0
  1210. }
  1211. };
  1212. // clang-format on
  1213. Problem::EvaluateOptions evaluate_options;
  1214. // z, y, x
  1215. evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
  1216. evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]);
  1217. evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
  1218. // f, h, g
  1219. evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
  1220. evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
  1221. evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
  1222. CheckAllEvaluationCombinations(evaluate_options, expected);
  1223. }
  1224. TEST_F(ProblemEvaluateTest, ConstantParameterBlock) {
  1225. // clang-format off
  1226. ExpectedEvaluation expected = {
  1227. // Rows/columns
  1228. 6, 6,
  1229. // Cost
  1230. 7607.0,
  1231. // Residuals
  1232. { -19.0, -35.0, // f
  1233. -59.0, -87.0, // g
  1234. -27.0, -43.0 // h
  1235. },
  1236. // Gradient
  1237. { 146.0, 484.0, // x
  1238. 0.0, 0.0, // y
  1239. 1450.0, 2604.0, // z
  1240. },
  1241. // Jacobian
  1242. // x y z
  1243. { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
  1244. 0.0, -4.0, 0.0, 0.0, 0.0, 0.0,
  1245. /* g(y, z) */ 0.0, 0.0, 0.0, 0.0, -20.0, 0.0,
  1246. 0.0, 0.0, 0.0, 0.0, 0.0, -24.0,
  1247. /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
  1248. 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
  1249. }
  1250. };
  1251. // clang-format on
  1252. problem_.SetParameterBlockConstant(parameters_ + 2);
  1253. CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
  1254. }
  1255. TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) {
  1256. // clang-format off
  1257. ExpectedEvaluation expected = {
  1258. // Rows/columns
  1259. 4, 6,
  1260. // Cost
  1261. 2082.0,
  1262. // Residuals
  1263. { -19.0, -35.0, // f
  1264. -27.0, -43.0 // h
  1265. },
  1266. // Gradient
  1267. { 146.0, 484.0, // x
  1268. 228.0, 560.0, // y
  1269. 270.0, 516.0, // z
  1270. },
  1271. // Jacobian
  1272. // x y z
  1273. { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
  1274. 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
  1275. /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
  1276. 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
  1277. }
  1278. };
  1279. // clang-format on
  1280. Problem::EvaluateOptions evaluate_options;
  1281. evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
  1282. evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
  1283. CheckAllEvaluationCombinations(evaluate_options, expected);
  1284. }
  1285. TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) {
  1286. // clang-format off
  1287. ExpectedEvaluation expected = {
  1288. // Rows/columns
  1289. 6, 4,
  1290. // Cost
  1291. 7607.0,
  1292. // Residuals
  1293. { -19.0, -35.0, // f
  1294. -59.0, -87.0, // g
  1295. -27.0, -43.0 // h
  1296. },
  1297. // Gradient
  1298. { 146.0, 484.0, // x
  1299. 1450.0, 2604.0, // z
  1300. },
  1301. // Jacobian
  1302. // x z
  1303. { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
  1304. 0.0, -4.0, 0.0, 0.0,
  1305. /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
  1306. 0.0, 0.0, 0.0, -24.0,
  1307. /* h(z, x) */ -4.0, 0.0, -10.0, 0.0,
  1308. 0.0, -8.0, 0.0, -12.0
  1309. }
  1310. };
  1311. // clang-format on
  1312. Problem::EvaluateOptions evaluate_options;
  1313. // x, z
  1314. evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
  1315. evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
  1316. evaluate_options.residual_blocks = residual_blocks_;
  1317. CheckAllEvaluationCombinations(evaluate_options, expected);
  1318. }
  1319. TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) {
  1320. // clang-format off
  1321. ExpectedEvaluation expected = {
  1322. // Rows/columns
  1323. 4, 4,
  1324. // Cost
  1325. 6318.0,
  1326. // Residuals
  1327. { -19.0, -35.0, // f
  1328. -59.0, -87.0, // g
  1329. },
  1330. // Gradient
  1331. { 38.0, 140.0, // x
  1332. 1180.0, 2088.0, // z
  1333. },
  1334. // Jacobian
  1335. // x z
  1336. { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
  1337. 0.0, -4.0, 0.0, 0.0,
  1338. /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
  1339. 0.0, 0.0, 0.0, -24.0,
  1340. }
  1341. };
  1342. // clang-format on
  1343. Problem::EvaluateOptions evaluate_options;
  1344. // x, z
  1345. evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
  1346. evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
  1347. evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
  1348. evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
  1349. CheckAllEvaluationCombinations(evaluate_options, expected);
  1350. }
  1351. TEST_F(ProblemEvaluateTest, Manifold) {
  1352. // clang-format off
  1353. ExpectedEvaluation expected = {
  1354. // Rows/columns
  1355. 6, 5,
  1356. // Cost
  1357. 7607.0,
  1358. // Residuals
  1359. { -19.0, -35.0, // f
  1360. -59.0, -87.0, // g
  1361. -27.0, -43.0 // h
  1362. },
  1363. // Gradient
  1364. { 146.0, 484.0, // x
  1365. 1256.0, // y with SubsetManifold
  1366. 1450.0, 2604.0, // z
  1367. },
  1368. // Jacobian
  1369. // x y z
  1370. { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0,
  1371. 0.0, -4.0, -16.0, 0.0, 0.0,
  1372. /* g(y, z) */ 0.0, 0.0, 0.0, -20.0, 0.0,
  1373. 0.0, 0.0, -8.0, 0.0, -24.0,
  1374. /* h(z, x) */ -4.0, 0.0, 0.0, -10.0, 0.0,
  1375. 0.0, -8.0, 0.0, 0.0, -12.0
  1376. }
  1377. };
  1378. // clang-format on
  1379. std::vector<int> constant_parameters;
  1380. constant_parameters.push_back(0);
  1381. problem_.SetManifold(parameters_ + 2,
  1382. new SubsetManifold(2, constant_parameters));
  1383. CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
  1384. }
  1385. struct IdentityFunctor {
  1386. template <typename T>
  1387. bool operator()(const T* x, const T* y, T* residuals) const {
  1388. residuals[0] = x[0];
  1389. residuals[1] = x[1];
  1390. residuals[2] = y[0];
  1391. residuals[3] = y[1];
  1392. residuals[4] = y[2];
  1393. return true;
  1394. }
  1395. static CostFunction* Create() {
  1396. return new AutoDiffCostFunction<IdentityFunctor, 5, 2, 3>(
  1397. new IdentityFunctor);
  1398. }
  1399. };
  1400. class ProblemEvaluateResidualBlockTest : public ::testing::Test {
  1401. public:
  1402. static constexpr bool kApplyLossFunction = true;
  1403. static constexpr bool kDoNotApplyLossFunction = false;
  1404. static constexpr bool kNewPoint = true;
  1405. static constexpr bool kNotNewPoint = false;
  1406. static double loss_function_scale_;
  1407. protected:
  1408. ProblemImpl problem_;
  1409. double x_[2] = {1, 2};
  1410. double y_[3] = {1, 2, 3};
  1411. };
  1412. double ProblemEvaluateResidualBlockTest::loss_function_scale_ = 2.0;
  1413. TEST_F(ProblemEvaluateResidualBlockTest,
  1414. OneResidualBlockNoLossFunctionFullEval) {
  1415. ResidualBlockId residual_block_id =
  1416. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1417. Vector expected_f(5);
  1418. expected_f << 1, 2, 1, 2, 3;
  1419. Matrix expected_dfdx = Matrix::Zero(5, 2);
  1420. expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
  1421. Matrix expected_dfdy = Matrix::Zero(5, 3);
  1422. expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
  1423. double expected_cost = expected_f.squaredNorm() / 2.0;
  1424. double actual_cost;
  1425. Vector actual_f(5);
  1426. Matrix actual_dfdx(5, 2);
  1427. Matrix actual_dfdy(5, 3);
  1428. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1429. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1430. kApplyLossFunction,
  1431. kNewPoint,
  1432. &actual_cost,
  1433. actual_f.data(),
  1434. jacobians));
  1435. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1436. 0,
  1437. std::numeric_limits<double>::epsilon())
  1438. << actual_cost;
  1439. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1440. 0,
  1441. std::numeric_limits<double>::epsilon())
  1442. << actual_f;
  1443. EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
  1444. 0,
  1445. std::numeric_limits<double>::epsilon())
  1446. << actual_dfdx;
  1447. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1448. 0,
  1449. std::numeric_limits<double>::epsilon())
  1450. << actual_dfdy;
  1451. }
  1452. TEST_F(ProblemEvaluateResidualBlockTest,
  1453. OneResidualBlockNoLossFunctionNullEval) {
  1454. ResidualBlockId residual_block_id =
  1455. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1456. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1457. kApplyLossFunction,
  1458. kNewPoint,
  1459. nullptr,
  1460. nullptr,
  1461. nullptr));
  1462. }
  1463. TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockNoLossFunctionCost) {
  1464. ResidualBlockId residual_block_id =
  1465. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1466. Vector expected_f(5);
  1467. expected_f << 1, 2, 1, 2, 3;
  1468. double expected_cost = expected_f.squaredNorm() / 2.0;
  1469. double actual_cost;
  1470. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1471. kApplyLossFunction,
  1472. kNewPoint,
  1473. &actual_cost,
  1474. nullptr,
  1475. nullptr));
  1476. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1477. 0,
  1478. std::numeric_limits<double>::epsilon())
  1479. << actual_cost;
  1480. }
  1481. TEST_F(ProblemEvaluateResidualBlockTest,
  1482. OneResidualBlockNoLossFunctionCostAndResidual) {
  1483. ResidualBlockId residual_block_id =
  1484. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1485. Vector expected_f(5);
  1486. expected_f << 1, 2, 1, 2, 3;
  1487. double expected_cost = expected_f.squaredNorm() / 2.0;
  1488. double actual_cost;
  1489. Vector actual_f(5);
  1490. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1491. kApplyLossFunction,
  1492. kNewPoint,
  1493. &actual_cost,
  1494. actual_f.data(),
  1495. nullptr));
  1496. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1497. 0,
  1498. std::numeric_limits<double>::epsilon())
  1499. << actual_cost;
  1500. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1501. 0,
  1502. std::numeric_limits<double>::epsilon())
  1503. << actual_f;
  1504. }
  1505. TEST_F(ProblemEvaluateResidualBlockTest,
  1506. OneResidualBlockNoLossFunctionCostResidualAndOneJacobian) {
  1507. ResidualBlockId residual_block_id =
  1508. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1509. Vector expected_f(5);
  1510. expected_f << 1, 2, 1, 2, 3;
  1511. Matrix expected_dfdx = Matrix::Zero(5, 2);
  1512. expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
  1513. double expected_cost = expected_f.squaredNorm() / 2.0;
  1514. double actual_cost;
  1515. Vector actual_f(5);
  1516. Matrix actual_dfdx(5, 2);
  1517. double* jacobians[2] = {actual_dfdx.data(), nullptr};
  1518. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1519. kApplyLossFunction,
  1520. kNewPoint,
  1521. &actual_cost,
  1522. actual_f.data(),
  1523. jacobians));
  1524. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1525. 0,
  1526. std::numeric_limits<double>::epsilon())
  1527. << actual_cost;
  1528. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1529. 0,
  1530. std::numeric_limits<double>::epsilon())
  1531. << actual_f;
  1532. EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
  1533. 0,
  1534. std::numeric_limits<double>::epsilon())
  1535. << actual_dfdx;
  1536. }
  1537. TEST_F(ProblemEvaluateResidualBlockTest,
  1538. OneResidualBlockNoLossFunctionResidual) {
  1539. ResidualBlockId residual_block_id =
  1540. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1541. Vector expected_f(5);
  1542. expected_f << 1, 2, 1, 2, 3;
  1543. Vector actual_f(5);
  1544. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1545. kApplyLossFunction,
  1546. kNewPoint,
  1547. nullptr,
  1548. actual_f.data(),
  1549. nullptr));
  1550. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1551. 0,
  1552. std::numeric_limits<double>::epsilon())
  1553. << actual_f;
  1554. }
  1555. TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockWithLossFunction) {
  1556. ResidualBlockId residual_block_id =
  1557. problem_.AddResidualBlock(IdentityFunctor::Create(),
  1558. new ScaledLoss(nullptr, 2.0, TAKE_OWNERSHIP),
  1559. x_,
  1560. y_);
  1561. Vector expected_f(5);
  1562. expected_f << 1, 2, 1, 2, 3;
  1563. expected_f *= std::sqrt(loss_function_scale_);
  1564. Matrix expected_dfdx = Matrix::Zero(5, 2);
  1565. expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
  1566. expected_dfdx *= std::sqrt(loss_function_scale_);
  1567. Matrix expected_dfdy = Matrix::Zero(5, 3);
  1568. expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
  1569. expected_dfdy *= std::sqrt(loss_function_scale_);
  1570. double expected_cost = expected_f.squaredNorm() / 2.0;
  1571. double actual_cost;
  1572. Vector actual_f(5);
  1573. Matrix actual_dfdx(5, 2);
  1574. Matrix actual_dfdy(5, 3);
  1575. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1576. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1577. kApplyLossFunction,
  1578. kNewPoint,
  1579. &actual_cost,
  1580. actual_f.data(),
  1581. jacobians));
  1582. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1583. 0,
  1584. std::numeric_limits<double>::epsilon())
  1585. << actual_cost;
  1586. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1587. 0,
  1588. std::numeric_limits<double>::epsilon())
  1589. << actual_f;
  1590. EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
  1591. 0,
  1592. std::numeric_limits<double>::epsilon())
  1593. << actual_dfdx;
  1594. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1595. 0,
  1596. std::numeric_limits<double>::epsilon())
  1597. << actual_dfdy;
  1598. }
  1599. TEST_F(ProblemEvaluateResidualBlockTest,
  1600. OneResidualBlockWithLossFunctionDisabled) {
  1601. ResidualBlockId residual_block_id =
  1602. problem_.AddResidualBlock(IdentityFunctor::Create(),
  1603. new ScaledLoss(nullptr, 2.0, TAKE_OWNERSHIP),
  1604. x_,
  1605. y_);
  1606. Vector expected_f(5);
  1607. expected_f << 1, 2, 1, 2, 3;
  1608. Matrix expected_dfdx = Matrix::Zero(5, 2);
  1609. expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
  1610. Matrix expected_dfdy = Matrix::Zero(5, 3);
  1611. expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
  1612. double expected_cost = expected_f.squaredNorm() / 2.0;
  1613. double actual_cost;
  1614. Vector actual_f(5);
  1615. Matrix actual_dfdx(5, 2);
  1616. Matrix actual_dfdy(5, 3);
  1617. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1618. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1619. kDoNotApplyLossFunction,
  1620. kNewPoint,
  1621. &actual_cost,
  1622. actual_f.data(),
  1623. jacobians));
  1624. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1625. 0,
  1626. std::numeric_limits<double>::epsilon())
  1627. << actual_cost;
  1628. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1629. 0,
  1630. std::numeric_limits<double>::epsilon())
  1631. << actual_f;
  1632. EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
  1633. 0,
  1634. std::numeric_limits<double>::epsilon())
  1635. << actual_dfdx;
  1636. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1637. 0,
  1638. std::numeric_limits<double>::epsilon())
  1639. << actual_dfdy;
  1640. }
  1641. TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockWithOneManifold) {
  1642. ResidualBlockId residual_block_id =
  1643. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1644. problem_.SetManifold(x_, new SubsetManifold(2, {1}));
  1645. Vector expected_f(5);
  1646. expected_f << 1, 2, 1, 2, 3;
  1647. Matrix expected_dfdx = Matrix::Zero(5, 1);
  1648. expected_dfdx.block(0, 0, 1, 1) = Matrix::Identity(1, 1);
  1649. Matrix expected_dfdy = Matrix::Zero(5, 3);
  1650. expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
  1651. double expected_cost = expected_f.squaredNorm() / 2.0;
  1652. double actual_cost;
  1653. Vector actual_f(5);
  1654. Matrix actual_dfdx(5, 1);
  1655. Matrix actual_dfdy(5, 3);
  1656. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1657. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1658. kApplyLossFunction,
  1659. kNewPoint,
  1660. &actual_cost,
  1661. actual_f.data(),
  1662. jacobians));
  1663. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1664. 0,
  1665. std::numeric_limits<double>::epsilon())
  1666. << actual_cost;
  1667. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1668. 0,
  1669. std::numeric_limits<double>::epsilon())
  1670. << actual_f;
  1671. EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
  1672. 0,
  1673. std::numeric_limits<double>::epsilon())
  1674. << actual_dfdx;
  1675. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1676. 0,
  1677. std::numeric_limits<double>::epsilon())
  1678. << actual_dfdy;
  1679. }
  1680. TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockWithTwoManifolds) {
  1681. ResidualBlockId residual_block_id =
  1682. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1683. problem_.SetManifold(x_, new SubsetManifold(2, {1}));
  1684. problem_.SetManifold(y_, new SubsetManifold(3, {2}));
  1685. Vector expected_f(5);
  1686. expected_f << 1, 2, 1, 2, 3;
  1687. Matrix expected_dfdx = Matrix::Zero(5, 1);
  1688. expected_dfdx.block(0, 0, 1, 1) = Matrix::Identity(1, 1);
  1689. Matrix expected_dfdy = Matrix::Zero(5, 2);
  1690. expected_dfdy.block(2, 0, 2, 2) = Matrix::Identity(2, 2);
  1691. double expected_cost = expected_f.squaredNorm() / 2.0;
  1692. double actual_cost;
  1693. Vector actual_f(5);
  1694. Matrix actual_dfdx(5, 1);
  1695. Matrix actual_dfdy(5, 2);
  1696. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1697. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1698. kApplyLossFunction,
  1699. kNewPoint,
  1700. &actual_cost,
  1701. actual_f.data(),
  1702. jacobians));
  1703. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1704. 0,
  1705. std::numeric_limits<double>::epsilon())
  1706. << actual_cost;
  1707. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1708. 0,
  1709. std::numeric_limits<double>::epsilon())
  1710. << actual_f;
  1711. EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
  1712. 0,
  1713. std::numeric_limits<double>::epsilon())
  1714. << actual_dfdx;
  1715. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1716. 0,
  1717. std::numeric_limits<double>::epsilon())
  1718. << actual_dfdy;
  1719. }
  1720. TEST_F(ProblemEvaluateResidualBlockTest,
  1721. OneResidualBlockWithOneConstantParameterBlock) {
  1722. ResidualBlockId residual_block_id =
  1723. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1724. problem_.SetParameterBlockConstant(x_);
  1725. Vector expected_f(5);
  1726. expected_f << 1, 2, 1, 2, 3;
  1727. Matrix expected_dfdy = Matrix::Zero(5, 3);
  1728. expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
  1729. double expected_cost = expected_f.squaredNorm() / 2.0;
  1730. double actual_cost;
  1731. Vector actual_f(5);
  1732. Matrix actual_dfdx(5, 2);
  1733. Matrix actual_dfdy(5, 3);
  1734. // Try evaluating both Jacobians, this should fail.
  1735. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1736. EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
  1737. kApplyLossFunction,
  1738. kNewPoint,
  1739. &actual_cost,
  1740. actual_f.data(),
  1741. jacobians));
  1742. jacobians[0] = nullptr;
  1743. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1744. kApplyLossFunction,
  1745. kNewPoint,
  1746. &actual_cost,
  1747. actual_f.data(),
  1748. jacobians));
  1749. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1750. 0,
  1751. std::numeric_limits<double>::epsilon())
  1752. << actual_cost;
  1753. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1754. 0,
  1755. std::numeric_limits<double>::epsilon())
  1756. << actual_f;
  1757. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1758. 0,
  1759. std::numeric_limits<double>::epsilon())
  1760. << actual_dfdy;
  1761. }
  1762. TEST_F(ProblemEvaluateResidualBlockTest,
  1763. OneResidualBlockWithAllConstantParameterBlocks) {
  1764. ResidualBlockId residual_block_id =
  1765. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1766. problem_.SetParameterBlockConstant(x_);
  1767. problem_.SetParameterBlockConstant(y_);
  1768. Vector expected_f(5);
  1769. expected_f << 1, 2, 1, 2, 3;
  1770. double expected_cost = expected_f.squaredNorm() / 2.0;
  1771. double actual_cost;
  1772. Vector actual_f(5);
  1773. Matrix actual_dfdx(5, 2);
  1774. Matrix actual_dfdy(5, 3);
  1775. // Try evaluating with one or more Jacobians, this should fail.
  1776. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1777. EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
  1778. kApplyLossFunction,
  1779. kNewPoint,
  1780. &actual_cost,
  1781. actual_f.data(),
  1782. jacobians));
  1783. jacobians[0] = nullptr;
  1784. EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
  1785. kApplyLossFunction,
  1786. kNewPoint,
  1787. &actual_cost,
  1788. actual_f.data(),
  1789. jacobians));
  1790. jacobians[1] = nullptr;
  1791. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1792. kApplyLossFunction,
  1793. kNewPoint,
  1794. &actual_cost,
  1795. actual_f.data(),
  1796. jacobians));
  1797. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1798. 0,
  1799. std::numeric_limits<double>::epsilon())
  1800. << actual_cost;
  1801. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1802. 0,
  1803. std::numeric_limits<double>::epsilon())
  1804. << actual_f;
  1805. }
  1806. TEST_F(ProblemEvaluateResidualBlockTest,
  1807. OneResidualBlockWithOneParameterBlockConstantAndParameterBlockChanged) {
  1808. ResidualBlockId residual_block_id =
  1809. problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1810. problem_.SetParameterBlockConstant(x_);
  1811. x_[0] = 2;
  1812. y_[2] = 1;
  1813. Vector expected_f(5);
  1814. expected_f << 2, 2, 1, 2, 1;
  1815. Matrix expected_dfdy = Matrix::Zero(5, 3);
  1816. expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
  1817. double expected_cost = expected_f.squaredNorm() / 2.0;
  1818. double actual_cost;
  1819. Vector actual_f(5);
  1820. Matrix actual_dfdx(5, 2);
  1821. Matrix actual_dfdy(5, 3);
  1822. // Try evaluating with one or more Jacobians, this should fail.
  1823. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1824. EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
  1825. kApplyLossFunction,
  1826. kNewPoint,
  1827. &actual_cost,
  1828. actual_f.data(),
  1829. jacobians));
  1830. jacobians[0] = nullptr;
  1831. EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
  1832. kApplyLossFunction,
  1833. kNewPoint,
  1834. &actual_cost,
  1835. actual_f.data(),
  1836. jacobians));
  1837. EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
  1838. 0,
  1839. std::numeric_limits<double>::epsilon())
  1840. << actual_cost;
  1841. EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
  1842. 0,
  1843. std::numeric_limits<double>::epsilon())
  1844. << actual_f;
  1845. EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
  1846. 0,
  1847. std::numeric_limits<double>::epsilon())
  1848. << actual_dfdy;
  1849. }
  1850. TEST(Problem, SetAndGetParameterLowerBound) {
  1851. Problem problem;
  1852. double x[] = {1.0, 2.0};
  1853. problem.AddParameterBlock(x, 2);
  1854. EXPECT_EQ(problem.GetParameterLowerBound(x, 0),
  1855. -std::numeric_limits<double>::max());
  1856. EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
  1857. -std::numeric_limits<double>::max());
  1858. problem.SetParameterLowerBound(x, 0, -1.0);
  1859. EXPECT_EQ(problem.GetParameterLowerBound(x, 0), -1.0);
  1860. EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
  1861. -std::numeric_limits<double>::max());
  1862. problem.SetParameterLowerBound(x, 0, -2.0);
  1863. EXPECT_EQ(problem.GetParameterLowerBound(x, 0), -2.0);
  1864. EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
  1865. -std::numeric_limits<double>::max());
  1866. problem.SetParameterLowerBound(x, 0, -std::numeric_limits<double>::max());
  1867. EXPECT_EQ(problem.GetParameterLowerBound(x, 0),
  1868. -std::numeric_limits<double>::max());
  1869. EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
  1870. -std::numeric_limits<double>::max());
  1871. }
  1872. TEST(Problem, SetAndGetParameterUpperBound) {
  1873. Problem problem;
  1874. double x[] = {1.0, 2.0};
  1875. problem.AddParameterBlock(x, 2);
  1876. EXPECT_EQ(problem.GetParameterUpperBound(x, 0),
  1877. std::numeric_limits<double>::max());
  1878. EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
  1879. std::numeric_limits<double>::max());
  1880. problem.SetParameterUpperBound(x, 0, -1.0);
  1881. EXPECT_EQ(problem.GetParameterUpperBound(x, 0), -1.0);
  1882. EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
  1883. std::numeric_limits<double>::max());
  1884. problem.SetParameterUpperBound(x, 0, -2.0);
  1885. EXPECT_EQ(problem.GetParameterUpperBound(x, 0), -2.0);
  1886. EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
  1887. std::numeric_limits<double>::max());
  1888. problem.SetParameterUpperBound(x, 0, std::numeric_limits<double>::max());
  1889. EXPECT_EQ(problem.GetParameterUpperBound(x, 0),
  1890. std::numeric_limits<double>::max());
  1891. EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
  1892. std::numeric_limits<double>::max());
  1893. }
  1894. TEST(Problem, SetManifoldTwice) {
  1895. Problem problem;
  1896. double x[] = {1.0, 2.0, 3.0};
  1897. problem.AddParameterBlock(x, 3);
  1898. problem.SetManifold(x, new SubsetManifold(3, {1}));
  1899. EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 3);
  1900. EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 2);
  1901. problem.SetManifold(x, new SubsetManifold(3, {0, 1}));
  1902. EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 3);
  1903. EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 1);
  1904. }
  1905. TEST(Problem, SetManifoldAndThenClearItWithNull) {
  1906. Problem problem;
  1907. double x[] = {1.0, 2.0, 3.0};
  1908. problem.AddParameterBlock(x, 3);
  1909. problem.SetManifold(x, new SubsetManifold(3, {1}));
  1910. EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 3);
  1911. EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 2);
  1912. problem.SetManifold(x, nullptr);
  1913. EXPECT_EQ(problem.GetManifold(x), nullptr);
  1914. EXPECT_EQ(problem.ParameterBlockTangentSize(x), 3);
  1915. EXPECT_EQ(problem.ParameterBlockSize(x), 3);
  1916. }
  1917. TEST(Solver, ZeroTangentSizedManifoldMeansParameterBlockIsConstant) {
  1918. double x = 0.0;
  1919. double y = 1.0;
  1920. Problem problem;
  1921. problem.AddResidualBlock(new BinaryCostFunction(1, 1, 1), nullptr, &x, &y);
  1922. problem.SetManifold(&y, new SubsetManifold(1, {0}));
  1923. EXPECT_TRUE(problem.IsParameterBlockConstant(&y));
  1924. }
  1925. class MockEvaluationCallback : public EvaluationCallback {
  1926. public:
  1927. MOCK_METHOD2(PrepareForEvaluation, void(bool, bool));
  1928. };
  1929. TEST(ProblemEvaluate, CallsEvaluationCallbackWithoutJacobian) {
  1930. constexpr bool kDoNotComputeJacobians = false;
  1931. constexpr bool kNewPoint = true;
  1932. MockEvaluationCallback evaluation_callback;
  1933. EXPECT_CALL(evaluation_callback,
  1934. PrepareForEvaluation(kDoNotComputeJacobians, kNewPoint))
  1935. .Times(1);
  1936. Problem::Options options;
  1937. options.evaluation_callback = &evaluation_callback;
  1938. ProblemImpl problem(options);
  1939. double x_[2] = {1, 2};
  1940. double y_[3] = {1, 2, 3};
  1941. problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1942. double actual_cost;
  1943. EXPECT_TRUE(problem.Evaluate(
  1944. Problem::EvaluateOptions(), &actual_cost, nullptr, nullptr, nullptr));
  1945. }
  1946. TEST(ProblemEvaluate, CallsEvaluationCallbackWithJacobian) {
  1947. constexpr bool kComputeJacobians = true;
  1948. constexpr bool kNewPoint = true;
  1949. MockEvaluationCallback evaluation_callback;
  1950. EXPECT_CALL(evaluation_callback,
  1951. PrepareForEvaluation(kComputeJacobians, kNewPoint))
  1952. .Times(1);
  1953. Problem::Options options;
  1954. options.evaluation_callback = &evaluation_callback;
  1955. ProblemImpl problem(options);
  1956. double x_[2] = {1, 2};
  1957. double y_[3] = {1, 2, 3};
  1958. problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1959. double actual_cost;
  1960. ceres::CRSMatrix jacobian;
  1961. EXPECT_TRUE(problem.Evaluate(
  1962. Problem::EvaluateOptions(), &actual_cost, nullptr, nullptr, &jacobian));
  1963. }
  1964. TEST(ProblemEvaluateResidualBlock, NewPointCallsEvaluationCallback) {
  1965. constexpr bool kComputeJacobians = true;
  1966. constexpr bool kNewPoint = true;
  1967. MockEvaluationCallback evaluation_callback;
  1968. EXPECT_CALL(evaluation_callback,
  1969. PrepareForEvaluation(kComputeJacobians, kNewPoint))
  1970. .Times(1);
  1971. Problem::Options options;
  1972. options.evaluation_callback = &evaluation_callback;
  1973. ProblemImpl problem(options);
  1974. double x_[2] = {1, 2};
  1975. double y_[3] = {1, 2, 3};
  1976. ResidualBlockId residual_block_id =
  1977. problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  1978. double actual_cost;
  1979. Vector actual_f(5);
  1980. Matrix actual_dfdx(5, 2);
  1981. Matrix actual_dfdy(5, 3);
  1982. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  1983. EXPECT_TRUE(problem.EvaluateResidualBlock(
  1984. residual_block_id, true, true, &actual_cost, actual_f.data(), jacobians));
  1985. }
  1986. TEST(ProblemEvaluateResidualBlock, OldPointCallsEvaluationCallback) {
  1987. constexpr bool kComputeJacobians = true;
  1988. constexpr bool kOldPoint = false;
  1989. MockEvaluationCallback evaluation_callback;
  1990. EXPECT_CALL(evaluation_callback,
  1991. PrepareForEvaluation(kComputeJacobians, kOldPoint))
  1992. .Times(1);
  1993. Problem::Options options;
  1994. options.evaluation_callback = &evaluation_callback;
  1995. ProblemImpl problem(options);
  1996. double x_[2] = {1, 2};
  1997. double y_[3] = {1, 2, 3};
  1998. ResidualBlockId residual_block_id =
  1999. problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
  2000. double actual_cost;
  2001. Vector actual_f(5);
  2002. Matrix actual_dfdx(5, 2);
  2003. Matrix actual_dfdy(5, 3);
  2004. double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
  2005. EXPECT_TRUE(problem.EvaluateResidualBlock(residual_block_id,
  2006. true,
  2007. false,
  2008. &actual_cost,
  2009. actual_f.data(),
  2010. jacobians));
  2011. }
  2012. } // namespace ceres::internal