blob: d81858cca479211684c994bf751e777e65422bf8 [file] [log] [blame]
Keir Mierle8ebb0732012-04-30 23:09:08 -07001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2010, 2011, 2012 Google Inc. All rights reserved.
3// http://code.google.com/p/ceres-solver/
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
31#include "gtest/gtest.h"
Keir Mierlef7471832012-06-14 11:31:53 -070032#include "ceres/autodiff_cost_function.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070033#include "ceres/linear_solver.h"
Sameer Agarwal2c94eed2012-10-01 16:34:37 -070034#include "ceres/ordered_groups.h"
Keir Mierle8ebb0732012-04-30 23:09:08 -070035#include "ceres/parameter_block.h"
36#include "ceres/problem_impl.h"
37#include "ceres/program.h"
38#include "ceres/residual_block.h"
39#include "ceres/solver_impl.h"
40#include "ceres/sized_cost_function.h"
41
Keir Mierle8ebb0732012-04-30 23:09:08 -070042namespace ceres {
43namespace internal {
44
Markus Moll8de78db2012-07-14 15:49:11 +020045// A cost function that sipmply returns its argument.
46class UnaryIdentityCostFunction : public SizedCostFunction<1, 1> {
47 public:
48 virtual bool Evaluate(double const* const* parameters,
49 double* residuals,
50 double** jacobians) const {
51 residuals[0] = parameters[0][0];
52 if (jacobians != NULL && jacobians[0] != NULL) {
53 jacobians[0][0] = 1.0;
54 }
55 return true;
56 }
57};
58
Keir Mierle8ebb0732012-04-30 23:09:08 -070059// Templated base class for the CostFunction signatures.
60template <int kNumResiduals, int N0, int N1, int N2>
61class MockCostFunctionBase : public
62SizedCostFunction<kNumResiduals, N0, N1, N2> {
63 public:
64 virtual bool Evaluate(double const* const* parameters,
65 double* residuals,
66 double** jacobians) const {
67 // Do nothing. This is never called.
68 return true;
69 }
70};
71
72class UnaryCostFunction : public MockCostFunctionBase<2, 1, 0, 0> {};
73class BinaryCostFunction : public MockCostFunctionBase<2, 1, 1, 0> {};
74class TernaryCostFunction : public MockCostFunctionBase<2, 1, 1, 1> {};
75
76TEST(SolverImpl, RemoveFixedBlocksNothingConstant) {
77 ProblemImpl problem;
78 double x;
79 double y;
80 double z;
81
82 problem.AddParameterBlock(&x, 1);
83 problem.AddParameterBlock(&y, 1);
84 problem.AddParameterBlock(&z, 1);
85 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
86 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
87 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
88
89 string error;
90 {
Sameer Agarwal2c94eed2012-10-01 16:34:37 -070091 ParameterBlockOrdering ordering;
92 ordering.AddElementToGroup(&x, 0);
93 ordering.AddElementToGroup(&y, 0);
94 ordering.AddElementToGroup(&z, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -070095
Keir Mierle8ebb0732012-04-30 23:09:08 -070096 Program program(*problem.mutable_program());
97 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -070098 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +020099 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700100 &error));
101 EXPECT_EQ(program.NumParameterBlocks(), 3);
102 EXPECT_EQ(program.NumResidualBlocks(), 3);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700103 EXPECT_EQ(ordering.NumElements(), 3);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700104 }
105}
106
107TEST(SolverImpl, RemoveFixedBlocksAllParameterBlocksConstant) {
108 ProblemImpl problem;
109 double x;
110
111 problem.AddParameterBlock(&x, 1);
112 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
113 problem.SetParameterBlockConstant(&x);
114
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700115 ParameterBlockOrdering ordering;
116 ordering.AddElementToGroup(&x, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700117
Keir Mierle8ebb0732012-04-30 23:09:08 -0700118 Program program(problem.program());
119 string error;
120 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700121 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200122 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700123 &error));
124 EXPECT_EQ(program.NumParameterBlocks(), 0);
125 EXPECT_EQ(program.NumResidualBlocks(), 0);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700126 EXPECT_EQ(ordering.NumElements(), 0);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700127}
128
129TEST(SolverImpl, RemoveFixedBlocksNoResidualBlocks) {
130 ProblemImpl problem;
131 double x;
132 double y;
133 double z;
134
135 problem.AddParameterBlock(&x, 1);
136 problem.AddParameterBlock(&y, 1);
137 problem.AddParameterBlock(&z, 1);
138
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700139 ParameterBlockOrdering ordering;
140 ordering.AddElementToGroup(&x, 0);
141 ordering.AddElementToGroup(&y, 0);
142 ordering.AddElementToGroup(&z, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700143
144
Keir Mierle8ebb0732012-04-30 23:09:08 -0700145 Program program(problem.program());
146 string error;
147 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700148 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200149 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700150 &error));
151 EXPECT_EQ(program.NumParameterBlocks(), 0);
152 EXPECT_EQ(program.NumResidualBlocks(), 0);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700153 EXPECT_EQ(ordering.NumElements(), 0);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700154}
155
156TEST(SolverImpl, RemoveFixedBlocksOneParameterBlockConstant) {
157 ProblemImpl problem;
158 double x;
159 double y;
160 double z;
161
162 problem.AddParameterBlock(&x, 1);
163 problem.AddParameterBlock(&y, 1);
164 problem.AddParameterBlock(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700165
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700166 ParameterBlockOrdering ordering;
167 ordering.AddElementToGroup(&x, 0);
168 ordering.AddElementToGroup(&y, 0);
169 ordering.AddElementToGroup(&z, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700170
Keir Mierle8ebb0732012-04-30 23:09:08 -0700171 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
172 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
173 problem.SetParameterBlockConstant(&x);
174
Sameer Agarwal65625f72012-09-17 12:06:57 -0700175
Keir Mierle8ebb0732012-04-30 23:09:08 -0700176 Program program(problem.program());
177 string error;
178 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700179 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200180 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700181 &error));
182 EXPECT_EQ(program.NumParameterBlocks(), 1);
183 EXPECT_EQ(program.NumResidualBlocks(), 1);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700184 EXPECT_EQ(ordering.NumElements(), 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700185}
186
187TEST(SolverImpl, RemoveFixedBlocksNumEliminateBlocks) {
188 ProblemImpl problem;
189 double x;
190 double y;
191 double z;
192
193 problem.AddParameterBlock(&x, 1);
194 problem.AddParameterBlock(&y, 1);
195 problem.AddParameterBlock(&z, 1);
196 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
197 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
198 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
199 problem.SetParameterBlockConstant(&x);
200
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700201 ParameterBlockOrdering ordering;
202 ordering.AddElementToGroup(&x, 0);
203 ordering.AddElementToGroup(&y, 0);
204 ordering.AddElementToGroup(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700205
Keir Mierle8ebb0732012-04-30 23:09:08 -0700206 Program program(problem.program());
207 string error;
208 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700209 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200210 NULL,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700211 &error));
212 EXPECT_EQ(program.NumParameterBlocks(), 2);
213 EXPECT_EQ(program.NumResidualBlocks(), 2);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700214 EXPECT_EQ(ordering.NumElements(), 2);
215 EXPECT_EQ(ordering.GroupId(&y), 0);
216 EXPECT_EQ(ordering.GroupId(&z), 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700217}
218
Markus Moll8de78db2012-07-14 15:49:11 +0200219TEST(SolverImpl, RemoveFixedBlocksFixedCost) {
220 ProblemImpl problem;
221 double x = 1.23;
222 double y = 4.56;
223 double z = 7.89;
224
225 problem.AddParameterBlock(&x, 1);
226 problem.AddParameterBlock(&y, 1);
227 problem.AddParameterBlock(&z, 1);
228 problem.AddResidualBlock(new UnaryIdentityCostFunction(), NULL, &x);
229 problem.AddResidualBlock(new TernaryCostFunction(), NULL, &x, &y, &z);
230 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
231 problem.SetParameterBlockConstant(&x);
232
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700233 ParameterBlockOrdering ordering;
234 ordering.AddElementToGroup(&x, 0);
235 ordering.AddElementToGroup(&y, 0);
236 ordering.AddElementToGroup(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700237
Markus Moll8de78db2012-07-14 15:49:11 +0200238 double fixed_cost = 0.0;
239 Program program(problem.program());
240
241 double expected_fixed_cost;
242 ResidualBlock *expected_removed_block = program.residual_blocks()[0];
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800243 scoped_array<double> scratch(
244 new double[expected_removed_block->NumScratchDoublesForEvaluate()]);
Sameer Agarwal039ff072013-02-26 09:15:39 -0800245 expected_removed_block->Evaluate(true,
246 &expected_fixed_cost,
Sameer Agarwal509f68c2013-02-20 01:39:03 -0800247 NULL,
248 NULL,
249 scratch.get());
Markus Moll8de78db2012-07-14 15:49:11 +0200250
251 string error;
252 EXPECT_TRUE(SolverImpl::RemoveFixedBlocksFromProgram(&program,
Sameer Agarwal65625f72012-09-17 12:06:57 -0700253 &ordering,
Markus Moll8de78db2012-07-14 15:49:11 +0200254 &fixed_cost,
255 &error));
256 EXPECT_EQ(program.NumParameterBlocks(), 2);
257 EXPECT_EQ(program.NumResidualBlocks(), 2);
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700258 EXPECT_EQ(ordering.NumElements(), 2);
259 EXPECT_EQ(ordering.GroupId(&y), 0);
260 EXPECT_EQ(ordering.GroupId(&z), 1);
Markus Moll8de78db2012-07-14 15:49:11 +0200261 EXPECT_DOUBLE_EQ(fixed_cost, expected_fixed_cost);
262}
263
Keir Mierle8ebb0732012-04-30 23:09:08 -0700264TEST(SolverImpl, ReorderResidualBlockNormalFunction) {
265 ProblemImpl problem;
266 double x;
267 double y;
268 double z;
269
270 problem.AddParameterBlock(&x, 1);
271 problem.AddParameterBlock(&y, 1);
272 problem.AddParameterBlock(&z, 1);
273
274 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
275 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x);
276 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
277 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &z);
278 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &y);
279 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);
280
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700281 ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
282 ordering->AddElementToGroup(&x, 0);
283 ordering->AddElementToGroup(&y, 0);
284 ordering->AddElementToGroup(&z, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700285
Keir Mierle8ebb0732012-04-30 23:09:08 -0700286 Solver::Options options;
287 options.linear_solver_type = DENSE_SCHUR;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700288 options.linear_solver_ordering = ordering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700289
290 const vector<ResidualBlock*>& residual_blocks =
291 problem.program().residual_blocks();
292
293 vector<ResidualBlock*> expected_residual_blocks;
294
295 // This is a bit fragile, but it serves the purpose. We know the
296 // bucketing algorithm that the reordering function uses, so we
297 // expect the order for residual blocks for each e_block to be
298 // filled in reverse.
299 expected_residual_blocks.push_back(residual_blocks[4]);
300 expected_residual_blocks.push_back(residual_blocks[1]);
301 expected_residual_blocks.push_back(residual_blocks[0]);
302 expected_residual_blocks.push_back(residual_blocks[5]);
303 expected_residual_blocks.push_back(residual_blocks[2]);
304 expected_residual_blocks.push_back(residual_blocks[3]);
305
306 Program* program = problem.mutable_program();
307 program->SetParameterOffsetsAndIndex();
308
309 string error;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700310 EXPECT_TRUE(SolverImpl::LexicographicallyOrderResidualBlocks(
311 2,
312 problem.mutable_program(),
313 &error));
Keir Mierle32de18d2012-05-13 16:45:05 -0700314 EXPECT_EQ(residual_blocks.size(), expected_residual_blocks.size());
Keir Mierle8ebb0732012-04-30 23:09:08 -0700315 for (int i = 0; i < expected_residual_blocks.size(); ++i) {
316 EXPECT_EQ(residual_blocks[i], expected_residual_blocks[i]);
317 }
318}
319
Keir Mierle32de18d2012-05-13 16:45:05 -0700320TEST(SolverImpl, ReorderResidualBlockNormalFunctionWithFixedBlocks) {
321 ProblemImpl problem;
322 double x;
323 double y;
324 double z;
325
326 problem.AddParameterBlock(&x, 1);
327 problem.AddParameterBlock(&y, 1);
328 problem.AddParameterBlock(&z, 1);
329
330 // Set one parameter block constant.
331 problem.SetParameterBlockConstant(&z);
332
333 // Mark residuals for x's row block with "x" for readability.
334 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x); // 0 x
335 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x); // 1 x
336 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 2
337 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 3
338 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z); // 4 x
339 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 5
340 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z); // 6 x
341 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y); // 7
342
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700343 ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
344 ordering->AddElementToGroup(&x, 0);
345 ordering->AddElementToGroup(&z, 0);
346 ordering->AddElementToGroup(&y, 1);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700347
Keir Mierle32de18d2012-05-13 16:45:05 -0700348 Solver::Options options;
349 options.linear_solver_type = DENSE_SCHUR;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700350 options.linear_solver_ordering = ordering;
Keir Mierle32de18d2012-05-13 16:45:05 -0700351
352 // Create the reduced program. This should remove the fixed block "z",
353 // marking the index to -1 at the same time. x and y also get indices.
354 string error;
355 scoped_ptr<Program> reduced_program(
Markus Moll8de78db2012-07-14 15:49:11 +0200356 SolverImpl::CreateReducedProgram(&options, &problem, NULL, &error));
Keir Mierle32de18d2012-05-13 16:45:05 -0700357
358 const vector<ResidualBlock*>& residual_blocks =
359 problem.program().residual_blocks();
360
361 // This is a bit fragile, but it serves the purpose. We know the
362 // bucketing algorithm that the reordering function uses, so we
363 // expect the order for residual blocks for each e_block to be
364 // filled in reverse.
365
366 vector<ResidualBlock*> expected_residual_blocks;
367
368 // Row block for residuals involving "x". These are marked "x" in the block
369 // of code calling AddResidual() above.
370 expected_residual_blocks.push_back(residual_blocks[6]);
371 expected_residual_blocks.push_back(residual_blocks[4]);
372 expected_residual_blocks.push_back(residual_blocks[1]);
373 expected_residual_blocks.push_back(residual_blocks[0]);
374
375 // Row block for residuals involving "y".
376 expected_residual_blocks.push_back(residual_blocks[7]);
377 expected_residual_blocks.push_back(residual_blocks[5]);
378 expected_residual_blocks.push_back(residual_blocks[3]);
379 expected_residual_blocks.push_back(residual_blocks[2]);
380
Keir Mierle32de18d2012-05-13 16:45:05 -0700381 EXPECT_EQ(reduced_program->residual_blocks().size(),
382 expected_residual_blocks.size());
383 for (int i = 0; i < expected_residual_blocks.size(); ++i) {
384 EXPECT_EQ(reduced_program->residual_blocks()[i],
385 expected_residual_blocks[i]);
386 }
387}
388
Sameer Agarwalc1ffad62012-10-05 16:29:37 -0700389TEST(SolverImpl, AutomaticSchurReorderingRespectsConstantBlocks) {
390 ProblemImpl problem;
391 double x;
392 double y;
393 double z;
394
395 problem.AddParameterBlock(&x, 1);
396 problem.AddParameterBlock(&y, 1);
397 problem.AddParameterBlock(&z, 1);
398
399 // Set one parameter block constant.
400 problem.SetParameterBlockConstant(&z);
401
402 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x);
403 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x);
404 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
405 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
406 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z);
407 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y);
408 problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z);
409 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y);
410 problem.AddResidualBlock(new UnaryCostFunction(), NULL, &z);
411
412 ParameterBlockOrdering* ordering = new ParameterBlockOrdering;
413 ordering->AddElementToGroup(&x, 0);
414 ordering->AddElementToGroup(&z, 0);
415 ordering->AddElementToGroup(&y, 0);
416
417 Solver::Options options;
418 options.linear_solver_type = DENSE_SCHUR;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700419 options.linear_solver_ordering = ordering;
Sameer Agarwalc1ffad62012-10-05 16:29:37 -0700420
421 string error;
422 scoped_ptr<Program> reduced_program(
423 SolverImpl::CreateReducedProgram(&options, &problem, NULL, &error));
424
425 const vector<ResidualBlock*>& residual_blocks =
426 reduced_program->residual_blocks();
427 const vector<ParameterBlock*>& parameter_blocks =
428 reduced_program->parameter_blocks();
429
430 const vector<ResidualBlock*>& original_residual_blocks =
431 problem.program().residual_blocks();
432
433 EXPECT_EQ(residual_blocks.size(), 8);
434 EXPECT_EQ(reduced_program->parameter_blocks().size(), 2);
435
436 // Verify that right parmeter block and the residual blocks have
437 // been removed.
438 for (int i = 0; i < 8; ++i) {
439 EXPECT_NE(residual_blocks[i], original_residual_blocks.back());
440 }
441 for (int i = 0; i < 2; ++i) {
442 EXPECT_NE(parameter_blocks[i]->mutable_user_state(), &z);
443 }
444}
445
Keir Mierle8ebb0732012-04-30 23:09:08 -0700446TEST(SolverImpl, ApplyUserOrderingOrderingTooSmall) {
447 ProblemImpl problem;
448 double x;
449 double y;
450 double z;
451
452 problem.AddParameterBlock(&x, 1);
453 problem.AddParameterBlock(&y, 1);
454 problem.AddParameterBlock(&z, 1);
455
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700456 ParameterBlockOrdering ordering;
457 ordering.AddElementToGroup(&x, 0);
458 ordering.AddElementToGroup(&y, 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700459
460 Program program(problem.program());
461 string error;
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700462 EXPECT_FALSE(SolverImpl::ApplyUserOrdering(problem.parameter_map(),
Sameer Agarwal65625f72012-09-17 12:06:57 -0700463 &ordering,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700464 &program,
465 &error));
466}
467
Keir Mierle8ebb0732012-04-30 23:09:08 -0700468TEST(SolverImpl, ApplyUserOrderingNormal) {
469 ProblemImpl problem;
470 double x;
471 double y;
472 double z;
473
474 problem.AddParameterBlock(&x, 1);
475 problem.AddParameterBlock(&y, 1);
476 problem.AddParameterBlock(&z, 1);
477
Sameer Agarwal2c94eed2012-10-01 16:34:37 -0700478 ParameterBlockOrdering ordering;
479 ordering.AddElementToGroup(&x, 0);
480 ordering.AddElementToGroup(&y, 2);
481 ordering.AddElementToGroup(&z, 1);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700482
483 Program* program = problem.mutable_program();
484 string error;
485
Sameer Agarwal9123e2f2012-09-18 21:49:06 -0700486 EXPECT_TRUE(SolverImpl::ApplyUserOrdering(problem.parameter_map(),
Sameer Agarwal65625f72012-09-17 12:06:57 -0700487 &ordering,
Keir Mierle8ebb0732012-04-30 23:09:08 -0700488 program,
489 &error));
490 const vector<ParameterBlock*>& parameter_blocks = program->parameter_blocks();
491
492 EXPECT_EQ(parameter_blocks.size(), 3);
493 EXPECT_EQ(parameter_blocks[0]->user_state(), &x);
494 EXPECT_EQ(parameter_blocks[1]->user_state(), &z);
495 EXPECT_EQ(parameter_blocks[2]->user_state(), &y);
496}
497
Sameer Agarwalb0518732012-05-29 00:27:57 -0700498#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
Keir Mierle8ebb0732012-04-30 23:09:08 -0700499TEST(SolverImpl, CreateLinearSolverNoSuiteSparse) {
500 Solver::Options options;
501 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwal0b5df702012-11-28 15:53:07 -0800502 // CreateLinearSolver assumes a non-empty ordering.
503 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700504 string error;
505 EXPECT_FALSE(SolverImpl::CreateLinearSolver(&options, &error));
506}
Sameer Agarwalb0518732012-05-29 00:27:57 -0700507#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700508
509TEST(SolverImpl, CreateLinearSolverNegativeMaxNumIterations) {
510 Solver::Options options;
511 options.linear_solver_type = DENSE_QR;
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -0700512 options.max_linear_solver_iterations = -1;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700513 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700514 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700515 string error;
516 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
517 static_cast<LinearSolver*>(NULL));
518}
519
520TEST(SolverImpl, CreateLinearSolverNegativeMinNumIterations) {
521 Solver::Options options;
522 options.linear_solver_type = DENSE_QR;
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -0700523 options.min_linear_solver_iterations = -1;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700524 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700525 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700526 string error;
527 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
528 static_cast<LinearSolver*>(NULL));
529}
530
531TEST(SolverImpl, CreateLinearSolverMaxLessThanMinIterations) {
532 Solver::Options options;
533 options.linear_solver_type = DENSE_QR;
Sameer Agarwaleeedd2e2013-07-07 23:04:31 -0700534 options.min_linear_solver_iterations = 10;
535 options.max_linear_solver_iterations = 5;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700536 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700537 string error;
538 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
539 static_cast<LinearSolver*>(NULL));
540}
541
Keir Mierle8ebb0732012-04-30 23:09:08 -0700542TEST(SolverImpl, CreateLinearSolverDenseSchurMultipleThreads) {
543 Solver::Options options;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700544 options.linear_solver_type = DENSE_SCHUR;
545 options.num_linear_solver_threads = 2;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700546 // The Schur type solvers can only be created with the Ordering
547 // contains at least one elimination group.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700548 options.linear_solver_ordering = new ParameterBlockOrdering;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700549 double x;
550 double y;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700551 options.linear_solver_ordering->AddElementToGroup(&x, 0);
552 options.linear_solver_ordering->AddElementToGroup(&y, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700553
Keir Mierle8ebb0732012-04-30 23:09:08 -0700554 string error;
555 scoped_ptr<LinearSolver> solver(
556 SolverImpl::CreateLinearSolver(&options, &error));
557 EXPECT_TRUE(solver != NULL);
558 EXPECT_EQ(options.linear_solver_type, DENSE_SCHUR);
Sameer Agarwala363a7b2013-03-03 18:06:00 -0800559 EXPECT_EQ(options.num_linear_solver_threads, 2);
Keir Mierle8ebb0732012-04-30 23:09:08 -0700560}
561
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700562TEST(SolverImpl, CreateIterativeLinearSolverForDogleg) {
563 Solver::Options options;
564 options.trust_region_strategy_type = DOGLEG;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700565 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700566 options.linear_solver_ordering = new ParameterBlockOrdering;
Sameer Agarwal5ecd2512012-06-17 16:34:03 -0700567 string error;
568 options.linear_solver_type = ITERATIVE_SCHUR;
569 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
570 static_cast<LinearSolver*>(NULL));
571
572 options.linear_solver_type = CGNR;
573 EXPECT_EQ(SolverImpl::CreateLinearSolver(&options, &error),
574 static_cast<LinearSolver*>(NULL));
575}
576
Keir Mierle8ebb0732012-04-30 23:09:08 -0700577TEST(SolverImpl, CreateLinearSolverNormalOperation) {
578 Solver::Options options;
579 scoped_ptr<LinearSolver> solver;
580 options.linear_solver_type = DENSE_QR;
Sameer Agarwal65625f72012-09-17 12:06:57 -0700581 // CreateLinearSolver assumes a non-empty ordering.
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700582 options.linear_solver_ordering = new ParameterBlockOrdering;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700583 string error;
584 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
585 EXPECT_EQ(options.linear_solver_type, DENSE_QR);
586 EXPECT_TRUE(solver.get() != NULL);
587
Sameer Agarwalb9f15a52012-08-18 13:06:19 -0700588 options.linear_solver_type = DENSE_NORMAL_CHOLESKY;
589 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
590 EXPECT_EQ(options.linear_solver_type, DENSE_NORMAL_CHOLESKY);
591 EXPECT_TRUE(solver.get() != NULL);
592
Keir Mierle8ebb0732012-04-30 23:09:08 -0700593#ifndef CERES_NO_SUITESPARSE
594 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
Sameer Agarwalb0518732012-05-29 00:27:57 -0700595 options.sparse_linear_algebra_library = SUITE_SPARSE;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700596 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
597 EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
598 EXPECT_TRUE(solver.get() != NULL);
Sameer Agarwalb0518732012-05-29 00:27:57 -0700599#endif
600
601#ifndef CERES_NO_CXSPARSE
602 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
603 options.sparse_linear_algebra_library = CX_SPARSE;
604 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
605 EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
606 EXPECT_TRUE(solver.get() != NULL);
607#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700608
Sameer Agarwal65625f72012-09-17 12:06:57 -0700609 double x;
610 double y;
Sameer Agarwal68b32a92012-10-06 23:10:51 -0700611 options.linear_solver_ordering->AddElementToGroup(&x, 0);
612 options.linear_solver_ordering->AddElementToGroup(&y, 0);
Sameer Agarwal65625f72012-09-17 12:06:57 -0700613
Keir Mierle8ebb0732012-04-30 23:09:08 -0700614 options.linear_solver_type = DENSE_SCHUR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700615 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
616 EXPECT_EQ(options.linear_solver_type, DENSE_SCHUR);
617 EXPECT_TRUE(solver.get() != NULL);
618
619 options.linear_solver_type = SPARSE_SCHUR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700620 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
Sameer Agarwalb0518732012-05-29 00:27:57 -0700621
622#if defined(CERES_NO_SUITESPARSE) && defined(CERES_NO_CXSPARSE)
623 EXPECT_TRUE(SolverImpl::CreateLinearSolver(&options, &error) == NULL);
624#else
Keir Mierle8ebb0732012-04-30 23:09:08 -0700625 EXPECT_TRUE(solver.get() != NULL);
626 EXPECT_EQ(options.linear_solver_type, SPARSE_SCHUR);
Sameer Agarwalb0518732012-05-29 00:27:57 -0700627#endif
Keir Mierle8ebb0732012-04-30 23:09:08 -0700628
629 options.linear_solver_type = ITERATIVE_SCHUR;
Keir Mierle8ebb0732012-04-30 23:09:08 -0700630 solver.reset(SolverImpl::CreateLinearSolver(&options, &error));
631 EXPECT_EQ(options.linear_solver_type, ITERATIVE_SCHUR);
632 EXPECT_TRUE(solver.get() != NULL);
633}
634
Keir Mierlef7471832012-06-14 11:31:53 -0700635struct QuadraticCostFunction {
636 template <typename T> bool operator()(const T* const x,
637 T* residual) const {
638 residual[0] = T(5.0) - *x;
639 return true;
640 }
641};
642
643struct RememberingCallback : public IterationCallback {
Sameer Agarwal4997cbc2012-07-02 12:44:34 -0700644 explicit RememberingCallback(double *x) : calls(0), x(x) {}
Keir Mierlef7471832012-06-14 11:31:53 -0700645 virtual ~RememberingCallback() {}
646 virtual CallbackReturnType operator()(const IterationSummary& summary) {
647 x_values.push_back(*x);
648 return SOLVER_CONTINUE;
649 }
650 int calls;
651 double *x;
652 vector<double> x_values;
653};
654
655TEST(SolverImpl, UpdateStateEveryIterationOption) {
656 double x = 50.0;
657 const double original_x = x;
658
659 scoped_ptr<CostFunction> cost_function(
660 new AutoDiffCostFunction<QuadraticCostFunction, 1, 1>(
661 new QuadraticCostFunction));
662
663 Problem::Options problem_options;
664 problem_options.cost_function_ownership = DO_NOT_TAKE_OWNERSHIP;
Keir Mierle6196cba2012-06-18 11:03:40 -0700665 ProblemImpl problem(problem_options);
Keir Mierlef7471832012-06-14 11:31:53 -0700666 problem.AddResidualBlock(cost_function.get(), NULL, &x);
667
668 Solver::Options options;
669 options.linear_solver_type = DENSE_QR;
670
671 RememberingCallback callback(&x);
672 options.callbacks.push_back(&callback);
673
674 Solver::Summary summary;
675
676 int num_iterations;
677
678 // First try: no updating.
679 SolverImpl::Solve(options, &problem, &summary);
680 num_iterations = summary.num_successful_steps +
681 summary.num_unsuccessful_steps;
682 EXPECT_GT(num_iterations, 1);
683 for (int i = 0; i < callback.x_values.size(); ++i) {
684 EXPECT_EQ(50.0, callback.x_values[i]);
685 }
686
687 // Second try: with updating
688 x = 50.0;
689 options.update_state_every_iteration = true;
690 callback.x_values.clear();
691 SolverImpl::Solve(options, &problem, &summary);
692 num_iterations = summary.num_successful_steps +
693 summary.num_unsuccessful_steps;
694 EXPECT_GT(num_iterations, 1);
695 EXPECT_EQ(original_x, callback.x_values[0]);
696 EXPECT_NE(original_x, callback.x_values[1]);
697}
698
Keir Mierle6196cba2012-06-18 11:03:40 -0700699// The parameters must be in separate blocks so that they can be individually
700// set constant or not.
701struct Quadratic4DCostFunction {
702 template <typename T> bool operator()(const T* const x,
703 const T* const y,
704 const T* const z,
705 const T* const w,
706 T* residual) const {
707 // A 4-dimension axis-aligned quadratic.
708 residual[0] = T(10.0) - *x +
709 T(20.0) - *y +
710 T(30.0) - *z +
711 T(40.0) - *w;
712 return true;
713 }
714};
715
716TEST(SolverImpl, ConstantParameterBlocksDoNotChangeAndStateInvariantKept) {
717 double x = 50.0;
718 double y = 50.0;
719 double z = 50.0;
720 double w = 50.0;
721 const double original_x = 50.0;
722 const double original_y = 50.0;
723 const double original_z = 50.0;
724 const double original_w = 50.0;
725
726 scoped_ptr<CostFunction> cost_function(
727 new AutoDiffCostFunction<Quadratic4DCostFunction, 1, 1, 1, 1, 1>(
728 new Quadratic4DCostFunction));
729
730 Problem::Options problem_options;
731 problem_options.cost_function_ownership = DO_NOT_TAKE_OWNERSHIP;
732
733 ProblemImpl problem(problem_options);
734 problem.AddResidualBlock(cost_function.get(), NULL, &x, &y, &z, &w);
735 problem.SetParameterBlockConstant(&x);
736 problem.SetParameterBlockConstant(&w);
737
738 Solver::Options options;
739 options.linear_solver_type = DENSE_QR;
740
741 Solver::Summary summary;
742 SolverImpl::Solve(options, &problem, &summary);
743
744 // Verify only the non-constant parameters were mutated.
745 EXPECT_EQ(original_x, x);
746 EXPECT_NE(original_y, y);
747 EXPECT_NE(original_z, z);
748 EXPECT_EQ(original_w, w);
749
750 // Check that the parameter block state pointers are pointing back at the
751 // user state, instead of inside a random temporary vector made by Solve().
752 EXPECT_EQ(&x, problem.program().parameter_blocks()[0]->state());
753 EXPECT_EQ(&y, problem.program().parameter_blocks()[1]->state());
754 EXPECT_EQ(&z, problem.program().parameter_blocks()[2]->state());
755 EXPECT_EQ(&w, problem.program().parameter_blocks()[3]->state());
756}
757
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700758TEST(SolverImpl, NoParameterBlocks) {
759 ProblemImpl problem_impl;
760 Solver::Options options;
761 Solver::Summary summary;
762 SolverImpl::Solve(options, &problem_impl, &summary);
763 EXPECT_EQ(summary.termination_type, DID_NOT_RUN);
764 EXPECT_EQ(summary.error, "Problem contains no parameter blocks.");
765}
766
767TEST(SolverImpl, NoResiduals) {
768 ProblemImpl problem_impl;
769 Solver::Options options;
770 Solver::Summary summary;
771 double x = 1;
772 problem_impl.AddParameterBlock(&x, 1);
773 SolverImpl::Solve(options, &problem_impl, &summary);
774 EXPECT_EQ(summary.termination_type, DID_NOT_RUN);
775 EXPECT_EQ(summary.error, "Problem contains no residual blocks.");
776}
777
Sameer Agarwal67a107b2012-10-08 14:35:41 -0700778
779TEST(SolverImpl, ProblemIsConstant) {
780 ProblemImpl problem_impl;
781 Solver::Options options;
782 Solver::Summary summary;
783 double x = 1;
784 problem_impl.AddResidualBlock(new UnaryIdentityCostFunction, NULL, &x);
785 problem_impl.SetParameterBlockConstant(&x);
786 SolverImpl::Solve(options, &problem_impl, &summary);
787 EXPECT_EQ(summary.termination_type, FUNCTION_TOLERANCE);
788 EXPECT_EQ(summary.initial_cost, 1.0 / 2.0);
789 EXPECT_EQ(summary.final_cost, 1.0 / 2.0);
790}
791
Sameer Agarwalc8f07902013-04-19 08:01:04 -0700792TEST(SolverImpl, AlternateLinearSolverForSchurTypeLinearSolver) {
793 Solver::Options options;
794
795 options.linear_solver_type = DENSE_QR;
796 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
797 EXPECT_EQ(options.linear_solver_type, DENSE_QR);
798
799 options.linear_solver_type = DENSE_NORMAL_CHOLESKY;
800 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
801 EXPECT_EQ(options.linear_solver_type, DENSE_NORMAL_CHOLESKY);
802
803 options.linear_solver_type = SPARSE_NORMAL_CHOLESKY;
804 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
805 EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
806
807 options.linear_solver_type = CGNR;
808 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
809 EXPECT_EQ(options.linear_solver_type, CGNR);
810
811 options.linear_solver_type = DENSE_SCHUR;
812 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
813 EXPECT_EQ(options.linear_solver_type, DENSE_QR);
814
815 options.linear_solver_type = SPARSE_SCHUR;
816 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
817 EXPECT_EQ(options.linear_solver_type, SPARSE_NORMAL_CHOLESKY);
818
819 options.linear_solver_type = ITERATIVE_SCHUR;
820 options.preconditioner_type = IDENTITY;
821 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
822 EXPECT_EQ(options.linear_solver_type, CGNR);
823 EXPECT_EQ(options.preconditioner_type, IDENTITY);
824
825 options.linear_solver_type = ITERATIVE_SCHUR;
826 options.preconditioner_type = JACOBI;
827 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
828 EXPECT_EQ(options.linear_solver_type, CGNR);
829 EXPECT_EQ(options.preconditioner_type, JACOBI);
830
831 options.linear_solver_type = ITERATIVE_SCHUR;
832 options.preconditioner_type = SCHUR_JACOBI;
833 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
834 EXPECT_EQ(options.linear_solver_type, CGNR);
835 EXPECT_EQ(options.preconditioner_type, JACOBI);
836
837 options.linear_solver_type = ITERATIVE_SCHUR;
838 options.preconditioner_type = CLUSTER_JACOBI;
839 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
840 EXPECT_EQ(options.linear_solver_type, CGNR);
841 EXPECT_EQ(options.preconditioner_type, JACOBI);
842
843 options.linear_solver_type = ITERATIVE_SCHUR;
844 options.preconditioner_type = CLUSTER_TRIDIAGONAL;
845 SolverImpl::AlternateLinearSolverForSchurTypeLinearSolver(&options);
846 EXPECT_EQ(options.linear_solver_type, CGNR);
847 EXPECT_EQ(options.preconditioner_type, JACOBI);
848}
Sameer Agarwald5b93bf2013-04-26 21:17:49 -0700849
850TEST(SolverImpl, CreateJacobianBlockSparsityTranspose) {
851 ProblemImpl problem;
852 double x[2];
853 double y[3];
854 double z;
855
856 problem.AddParameterBlock(x, 2);
857 problem.AddParameterBlock(y, 3);
858 problem.AddParameterBlock(&z, 1);
859
860 problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 0, 0>(), NULL, x);
861 problem.AddResidualBlock(new MockCostFunctionBase<3, 1, 2, 0>(), NULL, &z, x);
862 problem.AddResidualBlock(new MockCostFunctionBase<4, 1, 3, 0>(), NULL, &z, y);
863 problem.AddResidualBlock(new MockCostFunctionBase<5, 1, 3, 0>(), NULL, &z, y);
864 problem.AddResidualBlock(new MockCostFunctionBase<1, 2, 1, 0>(), NULL, x, &z);
865 problem.AddResidualBlock(new MockCostFunctionBase<2, 1, 3, 0>(), NULL, &z, y);
866 problem.AddResidualBlock(new MockCostFunctionBase<2, 2, 1, 0>(), NULL, x, &z);
867 problem.AddResidualBlock(new MockCostFunctionBase<1, 3, 0, 0>(), NULL, y);
868
869 TripletSparseMatrix expected_block_sparse_jacobian(3, 8, 14);
870 {
871 int* rows = expected_block_sparse_jacobian.mutable_rows();
872 int* cols = expected_block_sparse_jacobian.mutable_cols();
873 double* values = expected_block_sparse_jacobian.mutable_values();
874 rows[0] = 0;
875 cols[0] = 0;
876
877 rows[1] = 2;
878 cols[1] = 1;
879 rows[2] = 0;
880 cols[2] = 1;
881
882 rows[3] = 2;
883 cols[3] = 2;
884 rows[4] = 1;
885 cols[4] = 2;
886
887 rows[5] = 2;
888 cols[5] = 3;
889 rows[6] = 1;
890 cols[6] = 3;
891
892 rows[7] = 0;
893 cols[7] = 4;
894 rows[8] = 2;
895 cols[8] = 4;
896
897 rows[9] = 2;
898 cols[9] = 5;
899 rows[10] = 1;
900 cols[10] = 5;
901
902 rows[11] = 0;
903 cols[11] = 6;
904 rows[12] = 2;
905 cols[12] = 6;
906
907 rows[13] = 1;
908 cols[13] = 7;
909 fill(values, values + 14, 1.0);
910 expected_block_sparse_jacobian.set_num_nonzeros(14);
911 }
912
913 Program* program = problem.mutable_program();
914 program->SetParameterOffsetsAndIndex();
915
916 scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
917 SolverImpl::CreateJacobianBlockSparsityTranspose(program));
918
919 Matrix expected_dense_jacobian;
920 expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
921
922 Matrix actual_dense_jacobian;
923 actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
924 EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
925}
926
Sameer Agarwal5f433c82013-06-13 06:57:58 -0700927template <int kNumResiduals, int kNumParameterBlocks>
928class NumParameterBlocksCostFunction : public CostFunction {
929 public:
930 NumParameterBlocksCostFunction() {
931 set_num_residuals(kNumResiduals);
932 for (int i = 0; i < kNumParameterBlocks; ++i) {
933 mutable_parameter_block_sizes()->push_back(1);
934 }
935 }
936
937 virtual ~NumParameterBlocksCostFunction() {
938 }
939
940 virtual bool Evaluate(double const* const* parameters,
941 double* residuals,
942 double** jacobians) const {
943 return true;
944 }
945};
946
947TEST(SolverImpl, ReallocationInCreateJacobianBlockSparsityTranspose) {
948 // CreateJacobianBlockSparsityTranspose starts with a conservative
949 // estimate of the size of the sparsity pattern. This test ensures
950 // that when those estimates are violated, the reallocation/resizing
951 // logic works correctly.
952
953 ProblemImpl problem;
954 double x[20];
955
956 vector<double*> parameter_blocks;
957 for (int i = 0; i < 20; ++i) {
958 problem.AddParameterBlock(x + i, 1);
959 parameter_blocks.push_back(x + i);
960 }
961
962 problem.AddResidualBlock(new NumParameterBlocksCostFunction<1, 20>(),
963 NULL,
964 parameter_blocks);
965
966 TripletSparseMatrix expected_block_sparse_jacobian(20, 1, 20);
967 {
968 int* rows = expected_block_sparse_jacobian.mutable_rows();
969 int* cols = expected_block_sparse_jacobian.mutable_cols();
970 for (int i = 0; i < 20; ++i) {
971 rows[i] = i;
972 cols[i] = 0;
973 }
974
975 double* values = expected_block_sparse_jacobian.mutable_values();
976 fill(values, values + 20, 1.0);
977 expected_block_sparse_jacobian.set_num_nonzeros(20);
978 }
979
980 Program* program = problem.mutable_program();
981 program->SetParameterOffsetsAndIndex();
982
983 scoped_ptr<TripletSparseMatrix> actual_block_sparse_jacobian(
984 SolverImpl::CreateJacobianBlockSparsityTranspose(program));
985
986 Matrix expected_dense_jacobian;
987 expected_block_sparse_jacobian.ToDenseMatrix(&expected_dense_jacobian);
988
989 Matrix actual_dense_jacobian;
990 actual_block_sparse_jacobian->ToDenseMatrix(&actual_dense_jacobian);
991 EXPECT_EQ((expected_dense_jacobian - actual_dense_jacobian).norm(), 0.0);
992}
993
Keir Mierle8ebb0732012-04-30 23:09:08 -0700994} // namespace internal
995} // namespace ceres