12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312 |
- // Ceres Solver - A fast non-linear least squares minimizer
- // Copyright 2023 Google Inc. All rights reserved.
- // http://ceres-solver.org/
- //
- // Redistribution and use in source and binary forms, with or without
- // modification, are permitted provided that the following conditions are met:
- //
- // * Redistributions of source code must retain the above copyright notice,
- // this list of conditions and the following disclaimer.
- // * Redistributions in binary form must reproduce the above copyright notice,
- // this list of conditions and the following disclaimer in the documentation
- // and/or other materials provided with the distribution.
- // * Neither the name of Google Inc. nor the names of its contributors may be
- // used to endorse or promote products derived from this software without
- // specific prior written permission.
- //
- // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
- // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
- // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
- // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
- // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
- // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
- // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
- // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
- // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
- // POSSIBILITY OF SUCH DAMAGE.
- //
- // Author: sameeragarwal@google.com (Sameer Agarwal)
- // keir@google.com (Keir Mierle)
- #include "ceres/problem.h"
- #include <memory>
- #include <string>
- #include <vector>
- #include "ceres/autodiff_cost_function.h"
- #include "ceres/casts.h"
- #include "ceres/cost_function.h"
- #include "ceres/crs_matrix.h"
- #include "ceres/evaluator_test_utils.h"
- #include "ceres/internal/eigen.h"
- #include "ceres/loss_function.h"
- #include "ceres/map_util.h"
- #include "ceres/parameter_block.h"
- #include "ceres/problem_impl.h"
- #include "ceres/program.h"
- #include "ceres/sized_cost_function.h"
- #include "ceres/sparse_matrix.h"
- #include "ceres/types.h"
- #include "gmock/gmock.h"
- #include "gtest/gtest.h"
- namespace ceres::internal {
- // The following three classes are for the purposes of defining
- // function signatures. They have dummy Evaluate functions.
- // Trivial cost function that accepts a single argument.
- class UnaryCostFunction : public CostFunction {
- public:
- UnaryCostFunction(int num_residuals, int32_t parameter_block_size) {
- set_num_residuals(num_residuals);
- mutable_parameter_block_sizes()->push_back(parameter_block_size);
- }
- bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const final {
- for (int i = 0; i < num_residuals(); ++i) {
- residuals[i] = 1;
- }
- return true;
- }
- };
- // Trivial cost function that accepts two arguments.
- class BinaryCostFunction : public CostFunction {
- public:
- BinaryCostFunction(int num_residuals,
- int32_t parameter_block1_size,
- int32_t parameter_block2_size) {
- set_num_residuals(num_residuals);
- mutable_parameter_block_sizes()->push_back(parameter_block1_size);
- mutable_parameter_block_sizes()->push_back(parameter_block2_size);
- }
- bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const final {
- for (int i = 0; i < num_residuals(); ++i) {
- residuals[i] = 2;
- }
- return true;
- }
- };
- // Trivial cost function that accepts three arguments.
- class TernaryCostFunction : public CostFunction {
- public:
- TernaryCostFunction(int num_residuals,
- int32_t parameter_block1_size,
- int32_t parameter_block2_size,
- int32_t parameter_block3_size) {
- set_num_residuals(num_residuals);
- mutable_parameter_block_sizes()->push_back(parameter_block1_size);
- mutable_parameter_block_sizes()->push_back(parameter_block2_size);
- mutable_parameter_block_sizes()->push_back(parameter_block3_size);
- }
- bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const final {
- for (int i = 0; i < num_residuals(); ++i) {
- residuals[i] = 3;
- }
- return true;
- }
- };
- TEST(Problem, MoveConstructor) {
- Problem src;
- double x;
- src.AddParameterBlock(&x, 1);
- Problem dst(std::move(src));
- EXPECT_TRUE(dst.HasParameterBlock(&x));
- }
- TEST(Problem, MoveAssignment) {
- Problem src;
- double x;
- src.AddParameterBlock(&x, 1);
- Problem dst;
- dst = std::move(src);
- EXPECT_TRUE(dst.HasParameterBlock(&x));
- }
- TEST(Problem, AddResidualWithNullCostFunctionDies) {
- double x[3], y[4], z[5];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- problem.AddParameterBlock(z, 5);
- EXPECT_DEATH_IF_SUPPORTED(problem.AddResidualBlock(nullptr, nullptr, x),
- "cost_function != nullptr");
- }
- TEST(Problem, AddResidualWithIncorrectNumberOfParameterBlocksDies) {
- double x[3], y[4], z[5];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- problem.AddParameterBlock(z, 5);
- // UnaryCostFunction takes only one parameter, but two are passed.
- EXPECT_DEATH_IF_SUPPORTED(
- problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x, y),
- "num_parameter_blocks");
- }
- TEST(Problem, AddResidualWithDifferentSizesOnTheSameVariableDies) {
- double x[3];
- Problem problem;
- problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
- EXPECT_DEATH_IF_SUPPORTED(
- problem.AddResidualBlock(
- new UnaryCostFunction(2, 4 /* 4 != 3 */), nullptr, x),
- "different block sizes");
- }
- TEST(Problem, AddResidualWithDuplicateParametersDies) {
- double x[3], z[5];
- Problem problem;
- EXPECT_DEATH_IF_SUPPORTED(
- problem.AddResidualBlock(new BinaryCostFunction(2, 3, 3), nullptr, x, x),
- "Duplicate parameter blocks");
- EXPECT_DEATH_IF_SUPPORTED(
- problem.AddResidualBlock(
- new TernaryCostFunction(1, 5, 3, 5), nullptr, z, x, z),
- "Duplicate parameter blocks");
- }
- TEST(Problem, AddResidualWithIncorrectSizesOfParameterBlockDies) {
- double x[3], y[4], z[5];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- problem.AddParameterBlock(z, 5);
- // The cost function expects the size of the second parameter, z, to be 4
- // instead of 5 as declared above. This is fatal.
- EXPECT_DEATH_IF_SUPPORTED(
- problem.AddResidualBlock(new BinaryCostFunction(2, 3, 4), nullptr, x, z),
- "different block sizes");
- }
- TEST(Problem, AddResidualAddsDuplicatedParametersOnlyOnce) {
- double x[3], y[4], z[5];
- Problem problem;
- problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
- problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
- problem.AddResidualBlock(new UnaryCostFunction(2, 4), nullptr, y);
- problem.AddResidualBlock(new UnaryCostFunction(2, 5), nullptr, z);
- EXPECT_EQ(3, problem.NumParameterBlocks());
- EXPECT_EQ(12, problem.NumParameters());
- }
- TEST(Problem, AddParameterWithDifferentSizesOnTheSameVariableDies) {
- double x[3], y[4];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(x, 4),
- "different block sizes");
- }
- static double* IntToPtr(int i) {
- return reinterpret_cast<double*>(sizeof(double) * i); // NOLINT
- }
- TEST(Problem, AddParameterWithAliasedParametersDies) {
- // Layout is
- //
- // 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
- // [x] x x x x [y] y y
- // o==o==o o==o==o o==o
- // o--o--o o--o--o o--o o--o--o
- //
- // Parameter block additions are tested as listed above; expected successful
- // ones marked with o==o and aliasing ones marked with o--o.
- Problem problem;
- problem.AddParameterBlock(IntToPtr(5), 5); // x
- problem.AddParameterBlock(IntToPtr(13), 3); // y
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(4), 2),
- "Aliasing detected");
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(4), 3),
- "Aliasing detected");
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(4), 9),
- "Aliasing detected");
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(8), 3),
- "Aliasing detected");
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(12), 2),
- "Aliasing detected");
- EXPECT_DEATH_IF_SUPPORTED(problem.AddParameterBlock(IntToPtr(14), 3),
- "Aliasing detected");
- // These ones should work.
- problem.AddParameterBlock(IntToPtr(2), 3);
- problem.AddParameterBlock(IntToPtr(10), 3);
- problem.AddParameterBlock(IntToPtr(16), 2);
- ASSERT_EQ(5, problem.NumParameterBlocks());
- }
- TEST(Problem, AddParameterIgnoresDuplicateCalls) {
- double x[3], y[4];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- // Creating parameter blocks multiple times is ignored.
- problem.AddParameterBlock(x, 3);
- problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
- // ... even repeatedly.
- problem.AddParameterBlock(x, 3);
- problem.AddResidualBlock(new UnaryCostFunction(2, 3), nullptr, x);
- // More parameters are fine.
- problem.AddParameterBlock(y, 4);
- problem.AddResidualBlock(new UnaryCostFunction(2, 4), nullptr, y);
- EXPECT_EQ(2, problem.NumParameterBlocks());
- EXPECT_EQ(7, problem.NumParameters());
- }
- class DestructorCountingCostFunction : public SizedCostFunction<3, 4, 5> {
- public:
- explicit DestructorCountingCostFunction(int* num_destructions)
- : num_destructions_(num_destructions) {}
- ~DestructorCountingCostFunction() override { *num_destructions_ += 1; }
- bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const final {
- return true;
- }
- private:
- int* num_destructions_;
- };
- TEST(Problem, ReusedCostFunctionsAreOnlyDeletedOnce) {
- double y[4], z[5];
- int num_destructions = 0;
- // Add a cost function multiple times and check to make sure that
- // the destructor on the cost function is only called once.
- {
- Problem problem;
- problem.AddParameterBlock(y, 4);
- problem.AddParameterBlock(z, 5);
- CostFunction* cost = new DestructorCountingCostFunction(&num_destructions);
- problem.AddResidualBlock(cost, nullptr, y, z);
- problem.AddResidualBlock(cost, nullptr, y, z);
- problem.AddResidualBlock(cost, nullptr, y, z);
- EXPECT_EQ(3, problem.NumResidualBlocks());
- }
- // Check that the destructor was called only once.
- CHECK_EQ(num_destructions, 1);
- }
- TEST(Problem, GetCostFunctionForResidualBlock) {
- double x[3];
- Problem problem;
- CostFunction* cost_function = new UnaryCostFunction(2, 3);
- const ResidualBlockId residual_block =
- problem.AddResidualBlock(cost_function, nullptr, x);
- EXPECT_EQ(problem.GetCostFunctionForResidualBlock(residual_block),
- cost_function);
- EXPECT_TRUE(problem.GetLossFunctionForResidualBlock(residual_block) ==
- nullptr);
- }
- TEST(Problem, GetLossFunctionForResidualBlock) {
- double x[3];
- Problem problem;
- CostFunction* cost_function = new UnaryCostFunction(2, 3);
- LossFunction* loss_function = new TrivialLoss();
- const ResidualBlockId residual_block =
- problem.AddResidualBlock(cost_function, loss_function, x);
- EXPECT_EQ(problem.GetCostFunctionForResidualBlock(residual_block),
- cost_function);
- EXPECT_EQ(problem.GetLossFunctionForResidualBlock(residual_block),
- loss_function);
- }
- TEST(Problem, CostFunctionsAreDeletedEvenWithRemovals) {
- double y[4], z[5], w[4];
- int num_destructions = 0;
- {
- Problem problem;
- problem.AddParameterBlock(y, 4);
- problem.AddParameterBlock(z, 5);
- CostFunction* cost_yz =
- new DestructorCountingCostFunction(&num_destructions);
- CostFunction* cost_wz =
- new DestructorCountingCostFunction(&num_destructions);
- ResidualBlock* r_yz = problem.AddResidualBlock(cost_yz, nullptr, y, z);
- ResidualBlock* r_wz = problem.AddResidualBlock(cost_wz, nullptr, w, z);
- EXPECT_EQ(2, problem.NumResidualBlocks());
- problem.RemoveResidualBlock(r_yz);
- CHECK_EQ(num_destructions, 1);
- problem.RemoveResidualBlock(r_wz);
- CHECK_EQ(num_destructions, 2);
- EXPECT_EQ(0, problem.NumResidualBlocks());
- }
- CHECK_EQ(num_destructions, 2);
- }
- // Make the dynamic problem tests (e.g. for removing residual blocks)
- // parameterized on whether the low-latency mode is enabled or not.
- //
- // This tests against ProblemImpl instead of Problem in order to inspect the
- // state of the resulting Program; this is difficult with only the thin Problem
- // interface.
- struct DynamicProblem : public ::testing::TestWithParam<bool> {
- DynamicProblem() {
- Problem::Options options;
- options.enable_fast_removal = GetParam();
- problem = std::make_unique<ProblemImpl>(options);
- }
- ParameterBlock* GetParameterBlock(int block) {
- return problem->program().parameter_blocks()[block];
- }
- ResidualBlock* GetResidualBlock(int block) {
- return problem->program().residual_blocks()[block];
- }
- bool HasResidualBlock(ResidualBlock* residual_block) {
- bool have_residual_block = true;
- if (GetParam()) {
- have_residual_block &=
- (problem->residual_block_set().find(residual_block) !=
- problem->residual_block_set().end());
- }
- have_residual_block &=
- find(problem->program().residual_blocks().begin(),
- problem->program().residual_blocks().end(),
- residual_block) != problem->program().residual_blocks().end();
- return have_residual_block;
- }
- int NumResidualBlocks() {
- // Verify that the hash set of residuals is maintained consistently.
- if (GetParam()) {
- EXPECT_EQ(problem->residual_block_set().size(),
- problem->NumResidualBlocks());
- }
- return problem->NumResidualBlocks();
- }
- // The next block of functions until the end are only for testing the
- // residual block removals.
- void ExpectParameterBlockContainsResidualBlock(
- double* values, ResidualBlock* residual_block) {
- ParameterBlock* parameter_block =
- FindOrDie(problem->parameter_map(), values);
- EXPECT_TRUE(ContainsKey(*(parameter_block->mutable_residual_blocks()),
- residual_block));
- }
- void ExpectSize(double* values, int size) {
- ParameterBlock* parameter_block =
- FindOrDie(problem->parameter_map(), values);
- EXPECT_EQ(size, parameter_block->mutable_residual_blocks()->size());
- }
- // Degenerate case.
- void ExpectParameterBlockContains(double* values) { ExpectSize(values, 0); }
- void ExpectParameterBlockContains(double* values, ResidualBlock* r1) {
- ExpectSize(values, 1);
- ExpectParameterBlockContainsResidualBlock(values, r1);
- }
- void ExpectParameterBlockContains(double* values,
- ResidualBlock* r1,
- ResidualBlock* r2) {
- ExpectSize(values, 2);
- ExpectParameterBlockContainsResidualBlock(values, r1);
- ExpectParameterBlockContainsResidualBlock(values, r2);
- }
- void ExpectParameterBlockContains(double* values,
- ResidualBlock* r1,
- ResidualBlock* r2,
- ResidualBlock* r3) {
- ExpectSize(values, 3);
- ExpectParameterBlockContainsResidualBlock(values, r1);
- ExpectParameterBlockContainsResidualBlock(values, r2);
- ExpectParameterBlockContainsResidualBlock(values, r3);
- }
- void ExpectParameterBlockContains(double* values,
- ResidualBlock* r1,
- ResidualBlock* r2,
- ResidualBlock* r3,
- ResidualBlock* r4) {
- ExpectSize(values, 4);
- ExpectParameterBlockContainsResidualBlock(values, r1);
- ExpectParameterBlockContainsResidualBlock(values, r2);
- ExpectParameterBlockContainsResidualBlock(values, r3);
- ExpectParameterBlockContainsResidualBlock(values, r4);
- }
- std::unique_ptr<ProblemImpl> problem;
- double y[4], z[5], w[3];
- };
- TEST(Problem, SetParameterBlockConstantWithUnknownPtrDies) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockConstant(y),
- "Parameter block not found:");
- }
- TEST(Problem, SetParameterBlockVariableWithUnknownPtrDies) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- EXPECT_DEATH_IF_SUPPORTED(problem.SetParameterBlockVariable(y),
- "Parameter block not found:");
- }
- TEST(Problem, IsParameterBlockConstant) {
- double x1[3];
- double x2[3];
- Problem problem;
- problem.AddParameterBlock(x1, 3);
- problem.AddParameterBlock(x2, 3);
- EXPECT_FALSE(problem.IsParameterBlockConstant(x1));
- EXPECT_FALSE(problem.IsParameterBlockConstant(x2));
- problem.SetParameterBlockConstant(x1);
- EXPECT_TRUE(problem.IsParameterBlockConstant(x1));
- EXPECT_FALSE(problem.IsParameterBlockConstant(x2));
- problem.SetParameterBlockConstant(x2);
- EXPECT_TRUE(problem.IsParameterBlockConstant(x1));
- EXPECT_TRUE(problem.IsParameterBlockConstant(x2));
- problem.SetParameterBlockVariable(x1);
- EXPECT_FALSE(problem.IsParameterBlockConstant(x1));
- EXPECT_TRUE(problem.IsParameterBlockConstant(x2));
- }
- TEST(Problem, IsParameterBlockConstantWithUnknownPtrDies) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- EXPECT_DEATH_IF_SUPPORTED(problem.IsParameterBlockConstant(y),
- "Parameter block not found:");
- }
- TEST(Problem, SetManifoldWithUnknownPtrDies) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- EXPECT_DEATH_IF_SUPPORTED(problem.SetManifold(y, new EuclideanManifold<3>),
- "Parameter block not found:");
- }
- TEST(Problem, RemoveParameterBlockWithUnknownPtrDies) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- EXPECT_DEATH_IF_SUPPORTED(problem.RemoveParameterBlock(y),
- "Parameter block not found:");
- }
- TEST(Problem, GetManifold) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 2);
- Manifold* manifold = new EuclideanManifold<3>;
- problem.SetManifold(x, manifold);
- EXPECT_EQ(problem.GetManifold(x), manifold);
- EXPECT_TRUE(problem.GetManifold(y) == nullptr);
- }
- TEST(Problem, HasManifold) {
- double x[3];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 2);
- Manifold* manifold = new EuclideanManifold<3>;
- problem.SetManifold(x, manifold);
- EXPECT_TRUE(problem.HasManifold(x));
- EXPECT_FALSE(problem.HasManifold(y));
- }
- TEST(Problem, RepeatedAddParameterBlockResetsManifold) {
- double x[4];
- double y[2];
- Problem problem;
- problem.AddParameterBlock(x, 4, new SubsetManifold(4, {0, 1}));
- problem.AddParameterBlock(y, 2);
- EXPECT_FALSE(problem.HasManifold(y));
- EXPECT_TRUE(problem.HasManifold(x));
- EXPECT_EQ(problem.ParameterBlockSize(x), 4);
- EXPECT_EQ(problem.ParameterBlockTangentSize(x), 2);
- EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 4);
- EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 2);
- problem.AddParameterBlock(x, 4, static_cast<Manifold*>(nullptr));
- EXPECT_FALSE(problem.HasManifold(x));
- EXPECT_EQ(problem.ParameterBlockSize(x), 4);
- EXPECT_EQ(problem.ParameterBlockTangentSize(x), 4);
- EXPECT_EQ(problem.GetManifold(x), nullptr);
- problem.AddParameterBlock(x, 4, new SubsetManifold(4, {0, 1, 2}));
- problem.AddParameterBlock(y, 2);
- EXPECT_TRUE(problem.HasManifold(x));
- EXPECT_EQ(problem.ParameterBlockSize(x), 4);
- EXPECT_EQ(problem.ParameterBlockTangentSize(x), 1);
- EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 4);
- EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 1);
- }
- TEST(Problem, ParameterBlockQueryTestUsingManifold) {
- double x[3];
- double y[4];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- std::vector<int> constant_parameters;
- constant_parameters.push_back(0);
- problem.SetManifold(x, new SubsetManifold(3, constant_parameters));
- EXPECT_EQ(problem.ParameterBlockSize(x), 3);
- EXPECT_EQ(problem.ParameterBlockTangentSize(x), 2);
- EXPECT_EQ(problem.ParameterBlockTangentSize(y), 4);
- std::vector<double*> parameter_blocks;
- problem.GetParameterBlocks(¶meter_blocks);
- EXPECT_EQ(parameter_blocks.size(), 2);
- EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);
- EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);
- EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);
- EXPECT_TRUE(problem.HasParameterBlock(x));
- problem.RemoveParameterBlock(x);
- EXPECT_FALSE(problem.HasParameterBlock(x));
- problem.GetParameterBlocks(¶meter_blocks);
- EXPECT_EQ(parameter_blocks.size(), 1);
- EXPECT_TRUE(parameter_blocks[0] == y);
- }
- TEST(Problem, ParameterBlockQueryTest) {
- double x[3];
- double y[4];
- Problem problem;
- problem.AddParameterBlock(x, 3);
- problem.AddParameterBlock(y, 4);
- std::vector<int> constant_parameters;
- constant_parameters.push_back(0);
- problem.SetManifold(x, new SubsetManifold(3, constant_parameters));
- EXPECT_EQ(problem.ParameterBlockSize(x), 3);
- EXPECT_EQ(problem.ParameterBlockTangentSize(x), 2);
- EXPECT_EQ(problem.ParameterBlockTangentSize(y), 4);
- std::vector<double*> parameter_blocks;
- problem.GetParameterBlocks(¶meter_blocks);
- EXPECT_EQ(parameter_blocks.size(), 2);
- EXPECT_NE(parameter_blocks[0], parameter_blocks[1]);
- EXPECT_TRUE(parameter_blocks[0] == x || parameter_blocks[0] == y);
- EXPECT_TRUE(parameter_blocks[1] == x || parameter_blocks[1] == y);
- EXPECT_TRUE(problem.HasParameterBlock(x));
- problem.RemoveParameterBlock(x);
- EXPECT_FALSE(problem.HasParameterBlock(x));
- problem.GetParameterBlocks(¶meter_blocks);
- EXPECT_EQ(parameter_blocks.size(), 1);
- EXPECT_TRUE(parameter_blocks[0] == y);
- }
- TEST_P(DynamicProblem, RemoveParameterBlockWithNoResiduals) {
- problem->AddParameterBlock(y, 4);
- problem->AddParameterBlock(z, 5);
- problem->AddParameterBlock(w, 3);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(y, GetParameterBlock(0)->user_state());
- EXPECT_EQ(z, GetParameterBlock(1)->user_state());
- EXPECT_EQ(w, GetParameterBlock(2)->user_state());
- // w is at the end, which might break the swapping logic so try adding and
- // removing it.
- problem->RemoveParameterBlock(w);
- ASSERT_EQ(2, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(y, GetParameterBlock(0)->user_state());
- EXPECT_EQ(z, GetParameterBlock(1)->user_state());
- problem->AddParameterBlock(w, 3);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(y, GetParameterBlock(0)->user_state());
- EXPECT_EQ(z, GetParameterBlock(1)->user_state());
- EXPECT_EQ(w, GetParameterBlock(2)->user_state());
- // Now remove z, which is in the middle, and add it back.
- problem->RemoveParameterBlock(z);
- ASSERT_EQ(2, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(y, GetParameterBlock(0)->user_state());
- EXPECT_EQ(w, GetParameterBlock(1)->user_state());
- problem->AddParameterBlock(z, 5);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(y, GetParameterBlock(0)->user_state());
- EXPECT_EQ(w, GetParameterBlock(1)->user_state());
- EXPECT_EQ(z, GetParameterBlock(2)->user_state());
- // Now remove everything.
- // y
- problem->RemoveParameterBlock(y);
- ASSERT_EQ(2, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(z, GetParameterBlock(0)->user_state());
- EXPECT_EQ(w, GetParameterBlock(1)->user_state());
- // z
- problem->RemoveParameterBlock(z);
- ASSERT_EQ(1, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(w, GetParameterBlock(0)->user_state());
- // w
- problem->RemoveParameterBlock(w);
- EXPECT_EQ(0, problem->NumParameterBlocks());
- EXPECT_EQ(0, NumResidualBlocks());
- }
- TEST_P(DynamicProblem, RemoveParameterBlockWithResiduals) {
- problem->AddParameterBlock(y, 4);
- problem->AddParameterBlock(z, 5);
- problem->AddParameterBlock(w, 3);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- EXPECT_EQ(y, GetParameterBlock(0)->user_state());
- EXPECT_EQ(z, GetParameterBlock(1)->user_state());
- EXPECT_EQ(w, GetParameterBlock(2)->user_state());
- // clang-format off
- // Add all combinations of cost functions.
- CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
- CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
- CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
- CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
- CostFunction* cost_y = new UnaryCostFunction (1, 4);
- CostFunction* cost_z = new UnaryCostFunction (1, 5);
- CostFunction* cost_w = new UnaryCostFunction (1, 3);
- ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
- ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
- ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
- ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
- ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
- ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
- ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
- EXPECT_EQ(3, problem->NumParameterBlocks());
- EXPECT_EQ(7, NumResidualBlocks());
- // Remove w, which should remove r_yzw, r_yw, r_zw, r_w.
- problem->RemoveParameterBlock(w);
- ASSERT_EQ(2, problem->NumParameterBlocks());
- ASSERT_EQ(3, NumResidualBlocks());
- ASSERT_FALSE(HasResidualBlock(r_yzw));
- ASSERT_TRUE (HasResidualBlock(r_yz ));
- ASSERT_FALSE(HasResidualBlock(r_yw ));
- ASSERT_FALSE(HasResidualBlock(r_zw ));
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_TRUE (HasResidualBlock(r_z ));
- ASSERT_FALSE(HasResidualBlock(r_w ));
- // Remove z, which will remove almost everything else.
- problem->RemoveParameterBlock(z);
- ASSERT_EQ(1, problem->NumParameterBlocks());
- ASSERT_EQ(1, NumResidualBlocks());
- ASSERT_FALSE(HasResidualBlock(r_yzw));
- ASSERT_FALSE(HasResidualBlock(r_yz ));
- ASSERT_FALSE(HasResidualBlock(r_yw ));
- ASSERT_FALSE(HasResidualBlock(r_zw ));
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_FALSE(HasResidualBlock(r_z ));
- ASSERT_FALSE(HasResidualBlock(r_w ));
- // Remove y; all gone.
- problem->RemoveParameterBlock(y);
- EXPECT_EQ(0, problem->NumParameterBlocks());
- EXPECT_EQ(0, NumResidualBlocks());
- // clang-format on
- }
- TEST_P(DynamicProblem, RemoveResidualBlock) {
- problem->AddParameterBlock(y, 4);
- problem->AddParameterBlock(z, 5);
- problem->AddParameterBlock(w, 3);
- // clang-format off
- // Add all combinations of cost functions.
- CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
- CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
- CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
- CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
- CostFunction* cost_y = new UnaryCostFunction (1, 4);
- CostFunction* cost_z = new UnaryCostFunction (1, 5);
- CostFunction* cost_w = new UnaryCostFunction (1, 3);
- ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
- ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
- ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
- ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
- ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
- ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
- ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
- if (GetParam()) {
- // In this test parameterization, there should be back-pointers from the
- // parameter blocks to the residual blocks.
- ExpectParameterBlockContains(y, r_yzw, r_yz, r_yw, r_y);
- ExpectParameterBlockContains(z, r_yzw, r_yz, r_zw, r_z);
- ExpectParameterBlockContains(w, r_yzw, r_yw, r_zw, r_w);
- } else {
- // Otherwise, nothing.
- EXPECT_TRUE(GetParameterBlock(0)->mutable_residual_blocks() == nullptr);
- EXPECT_TRUE(GetParameterBlock(1)->mutable_residual_blocks() == nullptr);
- EXPECT_TRUE(GetParameterBlock(2)->mutable_residual_blocks() == nullptr);
- }
- EXPECT_EQ(3, problem->NumParameterBlocks());
- EXPECT_EQ(7, NumResidualBlocks());
- // Remove each residual and check the state after each removal.
- // Remove r_yzw.
- problem->RemoveResidualBlock(r_yzw);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(6, NumResidualBlocks());
- if (GetParam()) {
- ExpectParameterBlockContains(y, r_yz, r_yw, r_y);
- ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
- ExpectParameterBlockContains(w, r_yw, r_zw, r_w);
- }
- ASSERT_TRUE (HasResidualBlock(r_yz ));
- ASSERT_TRUE (HasResidualBlock(r_yw ));
- ASSERT_TRUE (HasResidualBlock(r_zw ));
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_TRUE (HasResidualBlock(r_z ));
- ASSERT_TRUE (HasResidualBlock(r_w ));
- // Remove r_yw.
- problem->RemoveResidualBlock(r_yw);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(5, NumResidualBlocks());
- if (GetParam()) {
- ExpectParameterBlockContains(y, r_yz, r_y);
- ExpectParameterBlockContains(z, r_yz, r_zw, r_z);
- ExpectParameterBlockContains(w, r_zw, r_w);
- }
- ASSERT_TRUE (HasResidualBlock(r_yz ));
- ASSERT_TRUE (HasResidualBlock(r_zw ));
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_TRUE (HasResidualBlock(r_z ));
- ASSERT_TRUE (HasResidualBlock(r_w ));
- // Remove r_zw.
- problem->RemoveResidualBlock(r_zw);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(4, NumResidualBlocks());
- if (GetParam()) {
- ExpectParameterBlockContains(y, r_yz, r_y);
- ExpectParameterBlockContains(z, r_yz, r_z);
- ExpectParameterBlockContains(w, r_w);
- }
- ASSERT_TRUE (HasResidualBlock(r_yz ));
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_TRUE (HasResidualBlock(r_z ));
- ASSERT_TRUE (HasResidualBlock(r_w ));
- // Remove r_w.
- problem->RemoveResidualBlock(r_w);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(3, NumResidualBlocks());
- if (GetParam()) {
- ExpectParameterBlockContains(y, r_yz, r_y);
- ExpectParameterBlockContains(z, r_yz, r_z);
- ExpectParameterBlockContains(w);
- }
- ASSERT_TRUE (HasResidualBlock(r_yz ));
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_TRUE (HasResidualBlock(r_z ));
- // Remove r_yz.
- problem->RemoveResidualBlock(r_yz);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(2, NumResidualBlocks());
- if (GetParam()) {
- ExpectParameterBlockContains(y, r_y);
- ExpectParameterBlockContains(z, r_z);
- ExpectParameterBlockContains(w);
- }
- ASSERT_TRUE (HasResidualBlock(r_y ));
- ASSERT_TRUE (HasResidualBlock(r_z ));
- // Remove the last two.
- problem->RemoveResidualBlock(r_z);
- problem->RemoveResidualBlock(r_y);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(0, NumResidualBlocks());
- if (GetParam()) {
- ExpectParameterBlockContains(y);
- ExpectParameterBlockContains(z);
- ExpectParameterBlockContains(w);
- }
- // clang-format on
- }
- TEST_P(DynamicProblem, RemoveInvalidResidualBlockDies) {
- problem->AddParameterBlock(y, 4);
- problem->AddParameterBlock(z, 5);
- problem->AddParameterBlock(w, 3);
- // clang-format off
- // Add all combinations of cost functions.
- CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
- CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
- CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
- CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
- CostFunction* cost_y = new UnaryCostFunction (1, 4);
- CostFunction* cost_z = new UnaryCostFunction (1, 5);
- CostFunction* cost_w = new UnaryCostFunction (1, 3);
- ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
- ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
- ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
- ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
- ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
- ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
- ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
- // clang-format on
- // Remove r_yzw.
- problem->RemoveResidualBlock(r_yzw);
- ASSERT_EQ(3, problem->NumParameterBlocks());
- ASSERT_EQ(6, NumResidualBlocks());
- // Attempt to remove r_yzw again.
- EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yzw), "not found");
- // Attempt to remove a cast pointer never added as a residual.
- int trash_memory = 1234;
- auto* invalid_residual = reinterpret_cast<ResidualBlock*>(&trash_memory);
- EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(invalid_residual),
- "not found");
- // Remove a parameter block, which in turn removes the dependent residuals
- // then attempt to remove them directly.
- problem->RemoveParameterBlock(z);
- ASSERT_EQ(2, problem->NumParameterBlocks());
- ASSERT_EQ(3, NumResidualBlocks());
- EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_yz), "not found");
- EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_zw), "not found");
- EXPECT_DEATH_IF_SUPPORTED(problem->RemoveResidualBlock(r_z), "not found");
- problem->RemoveResidualBlock(r_yw);
- problem->RemoveResidualBlock(r_w);
- problem->RemoveResidualBlock(r_y);
- }
- // Check that a null-terminated array, a, has the same elements as b.
- template <typename T>
- void ExpectVectorContainsUnordered(const T* a, const std::vector<T>& b) {
- // Compute the size of a.
- int size = 0;
- while (a[size]) {
- ++size;
- }
- ASSERT_EQ(size, b.size());
- // Sort a.
- std::vector<T> a_sorted(size);
- copy(a, a + size, a_sorted.begin());
- sort(a_sorted.begin(), a_sorted.end());
- // Sort b.
- std::vector<T> b_sorted(b);
- sort(b_sorted.begin(), b_sorted.end());
- // Compare.
- for (int i = 0; i < size; ++i) {
- EXPECT_EQ(a_sorted[i], b_sorted[i]);
- }
- }
- static void ExpectProblemHasResidualBlocks(
- const ProblemImpl& problem,
- const ResidualBlockId* expected_residual_blocks) {
- std::vector<ResidualBlockId> residual_blocks;
- problem.GetResidualBlocks(&residual_blocks);
- ExpectVectorContainsUnordered(expected_residual_blocks, residual_blocks);
- }
- TEST_P(DynamicProblem, GetXXXBlocksForYYYBlock) {
- problem->AddParameterBlock(y, 4);
- problem->AddParameterBlock(z, 5);
- problem->AddParameterBlock(w, 3);
- // clang-format off
- // Add all combinations of cost functions.
- CostFunction* cost_yzw = new TernaryCostFunction(1, 4, 5, 3);
- CostFunction* cost_yz = new BinaryCostFunction (1, 4, 5);
- CostFunction* cost_yw = new BinaryCostFunction (1, 4, 3);
- CostFunction* cost_zw = new BinaryCostFunction (1, 5, 3);
- CostFunction* cost_y = new UnaryCostFunction (1, 4);
- CostFunction* cost_z = new UnaryCostFunction (1, 5);
- CostFunction* cost_w = new UnaryCostFunction (1, 3);
- ResidualBlock* r_yzw = problem->AddResidualBlock(cost_yzw, nullptr, y, z, w);
- {
- ResidualBlockId expected_residuals[] = {r_yzw, nullptr};
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- ResidualBlock* r_yz = problem->AddResidualBlock(cost_yz, nullptr, y, z);
- {
- ResidualBlockId expected_residuals[] = {r_yzw, r_yz, nullptr};
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- ResidualBlock* r_yw = problem->AddResidualBlock(cost_yw, nullptr, y, w);
- {
- ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, nullptr};
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- ResidualBlock* r_zw = problem->AddResidualBlock(cost_zw, nullptr, z, w);
- {
- ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, nullptr};
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- ResidualBlock* r_y = problem->AddResidualBlock(cost_y, nullptr, y);
- {
- ResidualBlock *expected_residuals[] = {r_yzw, r_yz, r_yw, r_zw, r_y, nullptr};
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- ResidualBlock* r_z = problem->AddResidualBlock(cost_z, nullptr, z);
- {
- ResidualBlock *expected_residuals[] = {
- r_yzw, r_yz, r_yw, r_zw, r_y, r_z, nullptr
- };
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- ResidualBlock* r_w = problem->AddResidualBlock(cost_w, nullptr, w);
- {
- ResidualBlock *expected_residuals[] = {
- r_yzw, r_yz, r_yw, r_zw, r_y, r_z, r_w, nullptr
- };
- ExpectProblemHasResidualBlocks(*problem, expected_residuals);
- }
- std::vector<double*> parameter_blocks;
- std::vector<ResidualBlockId> residual_blocks;
- // Check GetResidualBlocksForParameterBlock() for all parameter blocks.
- struct GetResidualBlocksForParameterBlockTestCase {
- double* parameter_block;
- ResidualBlockId expected_residual_blocks[10];
- };
- GetResidualBlocksForParameterBlockTestCase get_residual_blocks_cases[] = {
- { y, { r_yzw, r_yz, r_yw, r_y, nullptr} },
- { z, { r_yzw, r_yz, r_zw, r_z, nullptr} },
- { w, { r_yzw, r_yw, r_zw, r_w, nullptr} },
- { nullptr, { nullptr } }
- };
- for (int i = 0; get_residual_blocks_cases[i].parameter_block; ++i) {
- problem->GetResidualBlocksForParameterBlock(
- get_residual_blocks_cases[i].parameter_block,
- &residual_blocks);
- ExpectVectorContainsUnordered(
- get_residual_blocks_cases[i].expected_residual_blocks,
- residual_blocks);
- }
- // Check GetParameterBlocksForResidualBlock() for all residual blocks.
- struct GetParameterBlocksForResidualBlockTestCase {
- ResidualBlockId residual_block;
- double* expected_parameter_blocks[10];
- };
- GetParameterBlocksForResidualBlockTestCase get_parameter_blocks_cases[] = {
- { r_yzw, { y, z, w, nullptr } },
- { r_yz , { y, z, nullptr } },
- { r_yw , { y, w, nullptr } },
- { r_zw , { z, w, nullptr } },
- { r_y , { y, nullptr } },
- { r_z , { z, nullptr } },
- { r_w , { w, nullptr } },
- { nullptr, { nullptr } }
- };
- for (int i = 0; get_parameter_blocks_cases[i].residual_block; ++i) {
- problem->GetParameterBlocksForResidualBlock(
- get_parameter_blocks_cases[i].residual_block,
- ¶meter_blocks);
- ExpectVectorContainsUnordered(
- get_parameter_blocks_cases[i].expected_parameter_blocks,
- parameter_blocks);
- }
- // clang-format on
- }
- INSTANTIATE_TEST_SUITE_P(OptionsInstantiation,
- DynamicProblem,
- ::testing::Values(true, false));
- // Test for Problem::Evaluate
- // r_i = i - (j + 1) * x_ij^2
- template <int kNumResiduals, int kNumParameterBlocks>
- class QuadraticCostFunction : public CostFunction {
- public:
- QuadraticCostFunction() {
- CHECK_GT(kNumResiduals, 0);
- CHECK_GT(kNumParameterBlocks, 0);
- set_num_residuals(kNumResiduals);
- for (int i = 0; i < kNumParameterBlocks; ++i) {
- mutable_parameter_block_sizes()->push_back(kNumResiduals);
- }
- }
- bool Evaluate(double const* const* parameters,
- double* residuals,
- double** jacobians) const final {
- for (int i = 0; i < kNumResiduals; ++i) {
- residuals[i] = i;
- for (int j = 0; j < kNumParameterBlocks; ++j) {
- residuals[i] -= (j + 1.0) * parameters[j][i] * parameters[j][i];
- }
- }
- if (jacobians == nullptr) {
- return true;
- }
- for (int j = 0; j < kNumParameterBlocks; ++j) {
- if (jacobians[j] != nullptr) {
- MatrixRef(jacobians[j], kNumResiduals, kNumResiduals) =
- (-2.0 * (j + 1.0) * ConstVectorRef(parameters[j], kNumResiduals))
- .asDiagonal();
- }
- }
- return true;
- }
- };
- // Convert a CRSMatrix to a dense Eigen matrix.
- static void CRSToDenseMatrix(const CRSMatrix& input, Matrix* output) {
- CHECK(output != nullptr);
- Matrix& m = *output;
- m.resize(input.num_rows, input.num_cols);
- m.setZero();
- for (int row = 0; row < input.num_rows; ++row) {
- for (int j = input.rows[row]; j < input.rows[row + 1]; ++j) {
- const int col = input.cols[j];
- m(row, col) = input.values[j];
- }
- }
- }
- class ProblemEvaluateTest : public ::testing::Test {
- protected:
- void SetUp() override {
- for (int i = 0; i < 6; ++i) {
- parameters_[i] = static_cast<double>(i + 1);
- }
- parameter_blocks_.push_back(parameters_);
- parameter_blocks_.push_back(parameters_ + 2);
- parameter_blocks_.push_back(parameters_ + 4);
- CostFunction* cost_function = new QuadraticCostFunction<2, 2>;
- // f(x, y)
- residual_blocks_.push_back(problem_.AddResidualBlock(
- cost_function, nullptr, parameters_, parameters_ + 2));
- // g(y, z)
- residual_blocks_.push_back(problem_.AddResidualBlock(
- cost_function, nullptr, parameters_ + 2, parameters_ + 4));
- // h(z, x)
- residual_blocks_.push_back(problem_.AddResidualBlock(
- cost_function, nullptr, parameters_ + 4, parameters_));
- }
- void TearDown() override { EXPECT_TRUE(problem_.program().IsValid()); }
- void EvaluateAndCompare(const Problem::EvaluateOptions& options,
- const int expected_num_rows,
- const int expected_num_cols,
- const double expected_cost,
- const double* expected_residuals,
- const double* expected_gradient,
- const double* expected_jacobian) {
- double cost;
- std::vector<double> residuals;
- std::vector<double> gradient;
- CRSMatrix jacobian;
- EXPECT_TRUE(
- problem_.Evaluate(options,
- &cost,
- expected_residuals != nullptr ? &residuals : nullptr,
- expected_gradient != nullptr ? &gradient : nullptr,
- expected_jacobian != nullptr ? &jacobian : nullptr));
- if (expected_residuals != nullptr) {
- EXPECT_EQ(residuals.size(), expected_num_rows);
- }
- if (expected_gradient != nullptr) {
- EXPECT_EQ(gradient.size(), expected_num_cols);
- }
- if (expected_jacobian != nullptr) {
- EXPECT_EQ(jacobian.num_rows, expected_num_rows);
- EXPECT_EQ(jacobian.num_cols, expected_num_cols);
- }
- Matrix dense_jacobian;
- if (expected_jacobian != nullptr) {
- CRSToDenseMatrix(jacobian, &dense_jacobian);
- }
- CompareEvaluations(expected_num_rows,
- expected_num_cols,
- expected_cost,
- expected_residuals,
- expected_gradient,
- expected_jacobian,
- cost,
- !residuals.empty() ? &residuals[0] : nullptr,
- !gradient.empty() ? &gradient[0] : nullptr,
- dense_jacobian.data());
- }
- void CheckAllEvaluationCombinations(const Problem::EvaluateOptions& options,
- const ExpectedEvaluation& expected) {
- for (int i = 0; i < 8; ++i) {
- EvaluateAndCompare(options,
- expected.num_rows,
- expected.num_cols,
- expected.cost,
- (i & 1) ? expected.residuals : nullptr,
- (i & 2) ? expected.gradient : nullptr,
- (i & 4) ? expected.jacobian : nullptr);
- }
- }
- ProblemImpl problem_;
- double parameters_[6];
- std::vector<double*> parameter_blocks_;
- std::vector<ResidualBlockId> residual_blocks_;
- };
- TEST_F(ProblemEvaluateTest, MultipleParameterAndResidualBlocks) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 6,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -59.0, -87.0, // g
- -27.0, -43.0 // h
- },
- // Gradient
- { 146.0, 484.0, // x
- 582.0, 1256.0, // y
- 1450.0, 2604.0, // z
- },
- // Jacobian
- // x y z
- { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
- /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
- /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
- }
- };
- // clang-format on
- CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
- }
- TEST_F(ProblemEvaluateTest, ParameterAndResidualBlocksPassedInOptions) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 6,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -59.0, -87.0, // g
- -27.0, -43.0 // h
- },
- // Gradient
- { 146.0, 484.0, // x
- 582.0, 1256.0, // y
- 1450.0, 2604.0, // z
- },
- // Jacobian
- // x y z
- { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
- /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, 0.0, -8.0, 0.0, -24.0,
- /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
- }
- };
- // clang-format on
- Problem::EvaluateOptions evaluate_options;
- evaluate_options.parameter_blocks = parameter_blocks_;
- evaluate_options.residual_blocks = residual_blocks_;
- CheckAllEvaluationCombinations(evaluate_options, expected);
- }
- TEST_F(ProblemEvaluateTest, ReorderedResidualBlocks) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 6,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -27.0, -43.0, // h
- -59.0, -87.0 // g
- },
- // Gradient
- { 146.0, 484.0, // x
- 582.0, 1256.0, // y
- 1450.0, 2604.0, // z
- },
- // Jacobian
- // x y z
- { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
- /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, 0.0, 0.0, -12.0,
- /* g(y, z) */ 0.0, 0.0, -6.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, 0.0, -8.0, 0.0, -24.0
- }
- };
- // clang-format on
- Problem::EvaluateOptions evaluate_options;
- evaluate_options.parameter_blocks = parameter_blocks_;
- // f, h, g
- evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
- CheckAllEvaluationCombinations(evaluate_options, expected);
- }
- TEST_F(ProblemEvaluateTest,
- ReorderedResidualBlocksAndReorderedParameterBlocks) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 6,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -27.0, -43.0, // h
- -59.0, -87.0 // g
- },
- // Gradient
- { 1450.0, 2604.0, // z
- 582.0, 1256.0, // y
- 146.0, 484.0, // x
- },
- // Jacobian
- // z y x
- { /* f(x, y) */ 0.0, 0.0, -12.0, 0.0, -2.0, 0.0,
- 0.0, 0.0, 0.0, -16.0, 0.0, -4.0,
- /* h(z, x) */ -10.0, 0.0, 0.0, 0.0, -4.0, 0.0,
- 0.0, -12.0, 0.0, 0.0, 0.0, -8.0,
- /* g(y, z) */ -20.0, 0.0, -6.0, 0.0, 0.0, 0.0,
- 0.0, -24.0, 0.0, -8.0, 0.0, 0.0
- }
- };
- // clang-format on
- Problem::EvaluateOptions evaluate_options;
- // z, y, x
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[1]);
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
- // f, h, g
- evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
- CheckAllEvaluationCombinations(evaluate_options, expected);
- }
- TEST_F(ProblemEvaluateTest, ConstantParameterBlock) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 6,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -59.0, -87.0, // g
- -27.0, -43.0 // h
- },
- // Gradient
- { 146.0, 484.0, // x
- 0.0, 0.0, // y
- 1450.0, 2604.0, // z
- },
- // Jacobian
- // x y z
- { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, 0.0, 0.0, 0.0,
- /* g(y, z) */ 0.0, 0.0, 0.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, 0.0, 0.0, 0.0, -24.0,
- /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
- }
- };
- // clang-format on
- problem_.SetParameterBlockConstant(parameters_ + 2);
- CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
- }
- TEST_F(ProblemEvaluateTest, ExcludedAResidualBlock) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 4, 6,
- // Cost
- 2082.0,
- // Residuals
- { -19.0, -35.0, // f
- -27.0, -43.0 // h
- },
- // Gradient
- { 146.0, 484.0, // x
- 228.0, 560.0, // y
- 270.0, 516.0, // z
- },
- // Jacobian
- // x y z
- { /* f(x, y) */ -2.0, 0.0, -12.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, -16.0, 0.0, 0.0,
- /* h(z, x) */ -4.0, 0.0, 0.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, 0.0, 0.0, -12.0
- }
- };
- // clang-format on
- Problem::EvaluateOptions evaluate_options;
- evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[2]);
- CheckAllEvaluationCombinations(evaluate_options, expected);
- }
- TEST_F(ProblemEvaluateTest, ExcludedParameterBlock) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 4,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -59.0, -87.0, // g
- -27.0, -43.0 // h
- },
- // Gradient
- { 146.0, 484.0, // x
- 1450.0, 2604.0, // z
- },
- // Jacobian
- // x z
- { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, 0.0,
- /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, 0.0, -24.0,
- /* h(z, x) */ -4.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, -12.0
- }
- };
- // clang-format on
- Problem::EvaluateOptions evaluate_options;
- // x, z
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
- evaluate_options.residual_blocks = residual_blocks_;
- CheckAllEvaluationCombinations(evaluate_options, expected);
- }
- TEST_F(ProblemEvaluateTest, ExcludedParameterBlockAndExcludedResidualBlock) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 4, 4,
- // Cost
- 6318.0,
- // Residuals
- { -19.0, -35.0, // f
- -59.0, -87.0, // g
- },
- // Gradient
- { 38.0, 140.0, // x
- 1180.0, 2088.0, // z
- },
- // Jacobian
- // x z
- { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, 0.0, 0.0,
- /* g(y, z) */ 0.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, 0.0, -24.0,
- }
- };
- // clang-format on
- Problem::EvaluateOptions evaluate_options;
- // x, z
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[0]);
- evaluate_options.parameter_blocks.push_back(parameter_blocks_[2]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[0]);
- evaluate_options.residual_blocks.push_back(residual_blocks_[1]);
- CheckAllEvaluationCombinations(evaluate_options, expected);
- }
- TEST_F(ProblemEvaluateTest, Manifold) {
- // clang-format off
- ExpectedEvaluation expected = {
- // Rows/columns
- 6, 5,
- // Cost
- 7607.0,
- // Residuals
- { -19.0, -35.0, // f
- -59.0, -87.0, // g
- -27.0, -43.0 // h
- },
- // Gradient
- { 146.0, 484.0, // x
- 1256.0, // y with SubsetManifold
- 1450.0, 2604.0, // z
- },
- // Jacobian
- // x y z
- { /* f(x, y) */ -2.0, 0.0, 0.0, 0.0, 0.0,
- 0.0, -4.0, -16.0, 0.0, 0.0,
- /* g(y, z) */ 0.0, 0.0, 0.0, -20.0, 0.0,
- 0.0, 0.0, -8.0, 0.0, -24.0,
- /* h(z, x) */ -4.0, 0.0, 0.0, -10.0, 0.0,
- 0.0, -8.0, 0.0, 0.0, -12.0
- }
- };
- // clang-format on
- std::vector<int> constant_parameters;
- constant_parameters.push_back(0);
- problem_.SetManifold(parameters_ + 2,
- new SubsetManifold(2, constant_parameters));
- CheckAllEvaluationCombinations(Problem::EvaluateOptions(), expected);
- }
- struct IdentityFunctor {
- template <typename T>
- bool operator()(const T* x, const T* y, T* residuals) const {
- residuals[0] = x[0];
- residuals[1] = x[1];
- residuals[2] = y[0];
- residuals[3] = y[1];
- residuals[4] = y[2];
- return true;
- }
- static CostFunction* Create() {
- return new AutoDiffCostFunction<IdentityFunctor, 5, 2, 3>(
- new IdentityFunctor);
- }
- };
- class ProblemEvaluateResidualBlockTest : public ::testing::Test {
- public:
- static constexpr bool kApplyLossFunction = true;
- static constexpr bool kDoNotApplyLossFunction = false;
- static constexpr bool kNewPoint = true;
- static constexpr bool kNotNewPoint = false;
- static double loss_function_scale_;
- protected:
- ProblemImpl problem_;
- double x_[2] = {1, 2};
- double y_[3] = {1, 2, 3};
- };
- double ProblemEvaluateResidualBlockTest::loss_function_scale_ = 2.0;
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockNoLossFunctionFullEval) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Matrix expected_dfdx = Matrix::Zero(5, 2);
- expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
- Matrix expected_dfdy = Matrix::Zero(5, 3);
- expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdx;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockNoLossFunctionNullEval) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- nullptr,
- nullptr,
- nullptr));
- }
- TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockNoLossFunctionCost) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- nullptr,
- nullptr));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockNoLossFunctionCostAndResidual) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- nullptr));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockNoLossFunctionCostResidualAndOneJacobian) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Matrix expected_dfdx = Matrix::Zero(5, 2);
- expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- double* jacobians[2] = {actual_dfdx.data(), nullptr};
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdx;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockNoLossFunctionResidual) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Vector actual_f(5);
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- nullptr,
- actual_f.data(),
- nullptr));
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- }
- TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockWithLossFunction) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(),
- new ScaledLoss(nullptr, 2.0, TAKE_OWNERSHIP),
- x_,
- y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- expected_f *= std::sqrt(loss_function_scale_);
- Matrix expected_dfdx = Matrix::Zero(5, 2);
- expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
- expected_dfdx *= std::sqrt(loss_function_scale_);
- Matrix expected_dfdy = Matrix::Zero(5, 3);
- expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
- expected_dfdy *= std::sqrt(loss_function_scale_);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdx;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockWithLossFunctionDisabled) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(),
- new ScaledLoss(nullptr, 2.0, TAKE_OWNERSHIP),
- x_,
- y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Matrix expected_dfdx = Matrix::Zero(5, 2);
- expected_dfdx.block(0, 0, 2, 2) = Matrix::Identity(2, 2);
- Matrix expected_dfdy = Matrix::Zero(5, 3);
- expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kDoNotApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdx;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockWithOneManifold) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- problem_.SetManifold(x_, new SubsetManifold(2, {1}));
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Matrix expected_dfdx = Matrix::Zero(5, 1);
- expected_dfdx.block(0, 0, 1, 1) = Matrix::Identity(1, 1);
- Matrix expected_dfdy = Matrix::Zero(5, 3);
- expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 1);
- Matrix actual_dfdy(5, 3);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdx;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST_F(ProblemEvaluateResidualBlockTest, OneResidualBlockWithTwoManifolds) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- problem_.SetManifold(x_, new SubsetManifold(2, {1}));
- problem_.SetManifold(y_, new SubsetManifold(3, {2}));
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Matrix expected_dfdx = Matrix::Zero(5, 1);
- expected_dfdx.block(0, 0, 1, 1) = Matrix::Identity(1, 1);
- Matrix expected_dfdy = Matrix::Zero(5, 2);
- expected_dfdy.block(2, 0, 2, 2) = Matrix::Identity(2, 2);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 1);
- Matrix actual_dfdy(5, 2);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdx - actual_dfdx).norm() / actual_dfdx.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdx;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockWithOneConstantParameterBlock) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- problem_.SetParameterBlockConstant(x_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- Matrix expected_dfdy = Matrix::Zero(5, 3);
- expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- // Try evaluating both Jacobians, this should fail.
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- jacobians[0] = nullptr;
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockWithAllConstantParameterBlocks) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- problem_.SetParameterBlockConstant(x_);
- problem_.SetParameterBlockConstant(y_);
- Vector expected_f(5);
- expected_f << 1, 2, 1, 2, 3;
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- // Try evaluating with one or more Jacobians, this should fail.
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- jacobians[0] = nullptr;
- EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- jacobians[1] = nullptr;
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- }
- TEST_F(ProblemEvaluateResidualBlockTest,
- OneResidualBlockWithOneParameterBlockConstantAndParameterBlockChanged) {
- ResidualBlockId residual_block_id =
- problem_.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- problem_.SetParameterBlockConstant(x_);
- x_[0] = 2;
- y_[2] = 1;
- Vector expected_f(5);
- expected_f << 2, 2, 1, 2, 1;
- Matrix expected_dfdy = Matrix::Zero(5, 3);
- expected_dfdy.block(2, 0, 3, 3) = Matrix::Identity(3, 3);
- double expected_cost = expected_f.squaredNorm() / 2.0;
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- // Try evaluating with one or more Jacobians, this should fail.
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_FALSE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- jacobians[0] = nullptr;
- EXPECT_TRUE(problem_.EvaluateResidualBlock(residual_block_id,
- kApplyLossFunction,
- kNewPoint,
- &actual_cost,
- actual_f.data(),
- jacobians));
- EXPECT_NEAR(std::abs(expected_cost - actual_cost) / actual_cost,
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_cost;
- EXPECT_NEAR((expected_f - actual_f).norm() / actual_f.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_f;
- EXPECT_NEAR((expected_dfdy - actual_dfdy).norm() / actual_dfdy.norm(),
- 0,
- std::numeric_limits<double>::epsilon())
- << actual_dfdy;
- }
- TEST(Problem, SetAndGetParameterLowerBound) {
- Problem problem;
- double x[] = {1.0, 2.0};
- problem.AddParameterBlock(x, 2);
- EXPECT_EQ(problem.GetParameterLowerBound(x, 0),
- -std::numeric_limits<double>::max());
- EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
- -std::numeric_limits<double>::max());
- problem.SetParameterLowerBound(x, 0, -1.0);
- EXPECT_EQ(problem.GetParameterLowerBound(x, 0), -1.0);
- EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
- -std::numeric_limits<double>::max());
- problem.SetParameterLowerBound(x, 0, -2.0);
- EXPECT_EQ(problem.GetParameterLowerBound(x, 0), -2.0);
- EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
- -std::numeric_limits<double>::max());
- problem.SetParameterLowerBound(x, 0, -std::numeric_limits<double>::max());
- EXPECT_EQ(problem.GetParameterLowerBound(x, 0),
- -std::numeric_limits<double>::max());
- EXPECT_EQ(problem.GetParameterLowerBound(x, 1),
- -std::numeric_limits<double>::max());
- }
- TEST(Problem, SetAndGetParameterUpperBound) {
- Problem problem;
- double x[] = {1.0, 2.0};
- problem.AddParameterBlock(x, 2);
- EXPECT_EQ(problem.GetParameterUpperBound(x, 0),
- std::numeric_limits<double>::max());
- EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
- std::numeric_limits<double>::max());
- problem.SetParameterUpperBound(x, 0, -1.0);
- EXPECT_EQ(problem.GetParameterUpperBound(x, 0), -1.0);
- EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
- std::numeric_limits<double>::max());
- problem.SetParameterUpperBound(x, 0, -2.0);
- EXPECT_EQ(problem.GetParameterUpperBound(x, 0), -2.0);
- EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
- std::numeric_limits<double>::max());
- problem.SetParameterUpperBound(x, 0, std::numeric_limits<double>::max());
- EXPECT_EQ(problem.GetParameterUpperBound(x, 0),
- std::numeric_limits<double>::max());
- EXPECT_EQ(problem.GetParameterUpperBound(x, 1),
- std::numeric_limits<double>::max());
- }
- TEST(Problem, SetManifoldTwice) {
- Problem problem;
- double x[] = {1.0, 2.0, 3.0};
- problem.AddParameterBlock(x, 3);
- problem.SetManifold(x, new SubsetManifold(3, {1}));
- EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 3);
- EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 2);
- problem.SetManifold(x, new SubsetManifold(3, {0, 1}));
- EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 3);
- EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 1);
- }
- TEST(Problem, SetManifoldAndThenClearItWithNull) {
- Problem problem;
- double x[] = {1.0, 2.0, 3.0};
- problem.AddParameterBlock(x, 3);
- problem.SetManifold(x, new SubsetManifold(3, {1}));
- EXPECT_EQ(problem.GetManifold(x)->AmbientSize(), 3);
- EXPECT_EQ(problem.GetManifold(x)->TangentSize(), 2);
- problem.SetManifold(x, nullptr);
- EXPECT_EQ(problem.GetManifold(x), nullptr);
- EXPECT_EQ(problem.ParameterBlockTangentSize(x), 3);
- EXPECT_EQ(problem.ParameterBlockSize(x), 3);
- }
- TEST(Solver, ZeroTangentSizedManifoldMeansParameterBlockIsConstant) {
- double x = 0.0;
- double y = 1.0;
- Problem problem;
- problem.AddResidualBlock(new BinaryCostFunction(1, 1, 1), nullptr, &x, &y);
- problem.SetManifold(&y, new SubsetManifold(1, {0}));
- EXPECT_TRUE(problem.IsParameterBlockConstant(&y));
- }
- class MockEvaluationCallback : public EvaluationCallback {
- public:
- MOCK_METHOD2(PrepareForEvaluation, void(bool, bool));
- };
- TEST(ProblemEvaluate, CallsEvaluationCallbackWithoutJacobian) {
- constexpr bool kDoNotComputeJacobians = false;
- constexpr bool kNewPoint = true;
- MockEvaluationCallback evaluation_callback;
- EXPECT_CALL(evaluation_callback,
- PrepareForEvaluation(kDoNotComputeJacobians, kNewPoint))
- .Times(1);
- Problem::Options options;
- options.evaluation_callback = &evaluation_callback;
- ProblemImpl problem(options);
- double x_[2] = {1, 2};
- double y_[3] = {1, 2, 3};
- problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- double actual_cost;
- EXPECT_TRUE(problem.Evaluate(
- Problem::EvaluateOptions(), &actual_cost, nullptr, nullptr, nullptr));
- }
- TEST(ProblemEvaluate, CallsEvaluationCallbackWithJacobian) {
- constexpr bool kComputeJacobians = true;
- constexpr bool kNewPoint = true;
- MockEvaluationCallback evaluation_callback;
- EXPECT_CALL(evaluation_callback,
- PrepareForEvaluation(kComputeJacobians, kNewPoint))
- .Times(1);
- Problem::Options options;
- options.evaluation_callback = &evaluation_callback;
- ProblemImpl problem(options);
- double x_[2] = {1, 2};
- double y_[3] = {1, 2, 3};
- problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- double actual_cost;
- ceres::CRSMatrix jacobian;
- EXPECT_TRUE(problem.Evaluate(
- Problem::EvaluateOptions(), &actual_cost, nullptr, nullptr, &jacobian));
- }
- TEST(ProblemEvaluateResidualBlock, NewPointCallsEvaluationCallback) {
- constexpr bool kComputeJacobians = true;
- constexpr bool kNewPoint = true;
- MockEvaluationCallback evaluation_callback;
- EXPECT_CALL(evaluation_callback,
- PrepareForEvaluation(kComputeJacobians, kNewPoint))
- .Times(1);
- Problem::Options options;
- options.evaluation_callback = &evaluation_callback;
- ProblemImpl problem(options);
- double x_[2] = {1, 2};
- double y_[3] = {1, 2, 3};
- ResidualBlockId residual_block_id =
- problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem.EvaluateResidualBlock(
- residual_block_id, true, true, &actual_cost, actual_f.data(), jacobians));
- }
- TEST(ProblemEvaluateResidualBlock, OldPointCallsEvaluationCallback) {
- constexpr bool kComputeJacobians = true;
- constexpr bool kOldPoint = false;
- MockEvaluationCallback evaluation_callback;
- EXPECT_CALL(evaluation_callback,
- PrepareForEvaluation(kComputeJacobians, kOldPoint))
- .Times(1);
- Problem::Options options;
- options.evaluation_callback = &evaluation_callback;
- ProblemImpl problem(options);
- double x_[2] = {1, 2};
- double y_[3] = {1, 2, 3};
- ResidualBlockId residual_block_id =
- problem.AddResidualBlock(IdentityFunctor::Create(), nullptr, x_, y_);
- double actual_cost;
- Vector actual_f(5);
- Matrix actual_dfdx(5, 2);
- Matrix actual_dfdy(5, 3);
- double* jacobians[2] = {actual_dfdx.data(), actual_dfdy.data()};
- EXPECT_TRUE(problem.EvaluateResidualBlock(residual_block_id,
- true,
- false,
- &actual_cost,
- actual_f.data(),
- jacobians));
- }
- } // namespace ceres::internal
|