1 // Ceres Solver - A fast non-linear least squares minimizer
2 // Copyright 2013 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 // mierle@gmail.com (Keir Mierle)
31
32 #include <cstddef>
33
34 #include "ceres/dynamic_numeric_diff_cost_function.h"
35 #include "ceres/internal/scoped_ptr.h"
36 #include "gtest/gtest.h"
37
38 namespace ceres {
39 namespace internal {
40
41 const double kTolerance = 1e-6;
42
43 // Takes 2 parameter blocks:
44 // parameters[0] is size 10.
45 // parameters[1] is size 5.
46 // Emits 21 residuals:
47 // A: i - parameters[0][i], for i in [0,10) -- this is 10 residuals
48 // B: parameters[0][i] - i, for i in [0,10) -- this is another 10.
49 // C: sum(parameters[0][i]^2 - 8*parameters[0][i]) + sum(parameters[1][i])
50 class MyCostFunctor {
51 public:
operator ()(double const * const * parameters,double * residuals) const52 bool operator()(double const* const* parameters, double* residuals) const {
53 const double* params0 = parameters[0];
54 int r = 0;
55 for (int i = 0; i < 10; ++i) {
56 residuals[r++] = i - params0[i];
57 residuals[r++] = params0[i] - i;
58 }
59
60 double c_residual = 0.0;
61 for (int i = 0; i < 10; ++i) {
62 c_residual += pow(params0[i], 2) - 8.0 * params0[i];
63 }
64
65 const double* params1 = parameters[1];
66 for (int i = 0; i < 5; ++i) {
67 c_residual += params1[i];
68 }
69 residuals[r++] = c_residual;
70 return true;
71 }
72 };
73
TEST(DynamicNumericdiffCostFunctionTest,TestResiduals)74 TEST(DynamicNumericdiffCostFunctionTest, TestResiduals) {
75 vector<double> param_block_0(10, 0.0);
76 vector<double> param_block_1(5, 0.0);
77 DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
78 new MyCostFunctor());
79 cost_function.AddParameterBlock(param_block_0.size());
80 cost_function.AddParameterBlock(param_block_1.size());
81 cost_function.SetNumResiduals(21);
82
83 // Test residual computation.
84 vector<double> residuals(21, -100000);
85 vector<double*> parameter_blocks(2);
86 parameter_blocks[0] = ¶m_block_0[0];
87 parameter_blocks[1] = ¶m_block_1[0];
88 EXPECT_TRUE(cost_function.Evaluate(¶meter_blocks[0],
89 residuals.data(),
90 NULL));
91 for (int r = 0; r < 10; ++r) {
92 EXPECT_EQ(1.0 * r, residuals.at(r * 2));
93 EXPECT_EQ(-1.0 * r, residuals.at(r * 2 + 1));
94 }
95 EXPECT_EQ(0, residuals.at(20));
96 }
97
98
TEST(DynamicNumericdiffCostFunctionTest,TestJacobian)99 TEST(DynamicNumericdiffCostFunctionTest, TestJacobian) {
100 // Test the residual counting.
101 vector<double> param_block_0(10, 0.0);
102 for (int i = 0; i < 10; ++i) {
103 param_block_0[i] = 2 * i;
104 }
105 vector<double> param_block_1(5, 0.0);
106 DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
107 new MyCostFunctor());
108 cost_function.AddParameterBlock(param_block_0.size());
109 cost_function.AddParameterBlock(param_block_1.size());
110 cost_function.SetNumResiduals(21);
111
112 // Prepare the residuals.
113 vector<double> residuals(21, -100000);
114
115 // Prepare the parameters.
116 vector<double*> parameter_blocks(2);
117 parameter_blocks[0] = ¶m_block_0[0];
118 parameter_blocks[1] = ¶m_block_1[0];
119
120 // Prepare the jacobian.
121 vector<vector<double> > jacobian_vect(2);
122 jacobian_vect[0].resize(21 * 10, -100000);
123 jacobian_vect[1].resize(21 * 5, -100000);
124 vector<double*> jacobian;
125 jacobian.push_back(jacobian_vect[0].data());
126 jacobian.push_back(jacobian_vect[1].data());
127
128 // Test jacobian computation.
129 EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
130 residuals.data(),
131 jacobian.data()));
132
133 for (int r = 0; r < 10; ++r) {
134 EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
135 EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
136 }
137 EXPECT_EQ(420, residuals.at(20));
138 for (int p = 0; p < 10; ++p) {
139 // Check "A" Jacobian.
140 EXPECT_NEAR(-1.0, jacobian_vect[0][2*p * 10 + p], kTolerance);
141 // Check "B" Jacobian.
142 EXPECT_NEAR(+1.0, jacobian_vect[0][(2*p+1) * 10 + p], kTolerance);
143 jacobian_vect[0][2*p * 10 + p] = 0.0;
144 jacobian_vect[0][(2*p+1) * 10 + p] = 0.0;
145 }
146
147 // Check "C" Jacobian for first parameter block.
148 for (int p = 0; p < 10; ++p) {
149 EXPECT_NEAR(4 * p - 8, jacobian_vect[0][20 * 10 + p], kTolerance);
150 jacobian_vect[0][20 * 10 + p] = 0.0;
151 }
152 for (int i = 0; i < jacobian_vect[0].size(); ++i) {
153 EXPECT_NEAR(0.0, jacobian_vect[0][i], kTolerance);
154 }
155
156 // Check "C" Jacobian for second parameter block.
157 for (int p = 0; p < 5; ++p) {
158 EXPECT_NEAR(1.0, jacobian_vect[1][20 * 5 + p], kTolerance);
159 jacobian_vect[1][20 * 5 + p] = 0.0;
160 }
161 for (int i = 0; i < jacobian_vect[1].size(); ++i) {
162 EXPECT_NEAR(0.0, jacobian_vect[1][i], kTolerance);
163 }
164 }
165
TEST(DynamicNumericdiffCostFunctionTest,JacobianWithFirstParameterBlockConstant)166 TEST(DynamicNumericdiffCostFunctionTest, JacobianWithFirstParameterBlockConstant) { // NOLINT
167 // Test the residual counting.
168 vector<double> param_block_0(10, 0.0);
169 for (int i = 0; i < 10; ++i) {
170 param_block_0[i] = 2 * i;
171 }
172 vector<double> param_block_1(5, 0.0);
173 DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
174 new MyCostFunctor());
175 cost_function.AddParameterBlock(param_block_0.size());
176 cost_function.AddParameterBlock(param_block_1.size());
177 cost_function.SetNumResiduals(21);
178
179 // Prepare the residuals.
180 vector<double> residuals(21, -100000);
181
182 // Prepare the parameters.
183 vector<double*> parameter_blocks(2);
184 parameter_blocks[0] = ¶m_block_0[0];
185 parameter_blocks[1] = ¶m_block_1[0];
186
187 // Prepare the jacobian.
188 vector<vector<double> > jacobian_vect(2);
189 jacobian_vect[0].resize(21 * 10, -100000);
190 jacobian_vect[1].resize(21 * 5, -100000);
191 vector<double*> jacobian;
192 jacobian.push_back(NULL);
193 jacobian.push_back(jacobian_vect[1].data());
194
195 // Test jacobian computation.
196 EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
197 residuals.data(),
198 jacobian.data()));
199
200 for (int r = 0; r < 10; ++r) {
201 EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
202 EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
203 }
204 EXPECT_EQ(420, residuals.at(20));
205
206 // Check "C" Jacobian for second parameter block.
207 for (int p = 0; p < 5; ++p) {
208 EXPECT_NEAR(1.0, jacobian_vect[1][20 * 5 + p], kTolerance);
209 jacobian_vect[1][20 * 5 + p] = 0.0;
210 }
211 for (int i = 0; i < jacobian_vect[1].size(); ++i) {
212 EXPECT_EQ(0.0, jacobian_vect[1][i]);
213 }
214 }
215
TEST(DynamicNumericdiffCostFunctionTest,JacobianWithSecondParameterBlockConstant)216 TEST(DynamicNumericdiffCostFunctionTest, JacobianWithSecondParameterBlockConstant) { // NOLINT
217 // Test the residual counting.
218 vector<double> param_block_0(10, 0.0);
219 for (int i = 0; i < 10; ++i) {
220 param_block_0[i] = 2 * i;
221 }
222 vector<double> param_block_1(5, 0.0);
223 DynamicNumericDiffCostFunction<MyCostFunctor> cost_function(
224 new MyCostFunctor());
225 cost_function.AddParameterBlock(param_block_0.size());
226 cost_function.AddParameterBlock(param_block_1.size());
227 cost_function.SetNumResiduals(21);
228
229 // Prepare the residuals.
230 vector<double> residuals(21, -100000);
231
232 // Prepare the parameters.
233 vector<double*> parameter_blocks(2);
234 parameter_blocks[0] = ¶m_block_0[0];
235 parameter_blocks[1] = ¶m_block_1[0];
236
237 // Prepare the jacobian.
238 vector<vector<double> > jacobian_vect(2);
239 jacobian_vect[0].resize(21 * 10, -100000);
240 jacobian_vect[1].resize(21 * 5, -100000);
241 vector<double*> jacobian;
242 jacobian.push_back(jacobian_vect[0].data());
243 jacobian.push_back(NULL);
244
245 // Test jacobian computation.
246 EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
247 residuals.data(),
248 jacobian.data()));
249
250 for (int r = 0; r < 10; ++r) {
251 EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
252 EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
253 }
254 EXPECT_EQ(420, residuals.at(20));
255 for (int p = 0; p < 10; ++p) {
256 // Check "A" Jacobian.
257 EXPECT_NEAR(-1.0, jacobian_vect[0][2*p * 10 + p], kTolerance);
258 // Check "B" Jacobian.
259 EXPECT_NEAR(+1.0, jacobian_vect[0][(2*p+1) * 10 + p], kTolerance);
260 jacobian_vect[0][2*p * 10 + p] = 0.0;
261 jacobian_vect[0][(2*p+1) * 10 + p] = 0.0;
262 }
263
264 // Check "C" Jacobian for first parameter block.
265 for (int p = 0; p < 10; ++p) {
266 EXPECT_NEAR(4 * p - 8, jacobian_vect[0][20 * 10 + p], kTolerance);
267 jacobian_vect[0][20 * 10 + p] = 0.0;
268 }
269 for (int i = 0; i < jacobian_vect[0].size(); ++i) {
270 EXPECT_EQ(0.0, jacobian_vect[0][i]);
271 }
272 }
273
274 // Takes 3 parameter blocks:
275 // parameters[0] (x) is size 1.
276 // parameters[1] (y) is size 2.
277 // parameters[2] (z) is size 3.
278 // Emits 7 residuals:
279 // A: x[0] (= sum_x)
280 // B: y[0] + 2.0 * y[1] (= sum_y)
281 // C: z[0] + 3.0 * z[1] + 6.0 * z[2] (= sum_z)
282 // D: sum_x * sum_y
283 // E: sum_y * sum_z
284 // F: sum_x * sum_z
285 // G: sum_x * sum_y * sum_z
286 class MyThreeParameterCostFunctor {
287 public:
288 template <typename T>
operator ()(T const * const * parameters,T * residuals) const289 bool operator()(T const* const* parameters, T* residuals) const {
290 const T* x = parameters[0];
291 const T* y = parameters[1];
292 const T* z = parameters[2];
293
294 T sum_x = x[0];
295 T sum_y = y[0] + 2.0 * y[1];
296 T sum_z = z[0] + 3.0 * z[1] + 6.0 * z[2];
297
298 residuals[0] = sum_x;
299 residuals[1] = sum_y;
300 residuals[2] = sum_z;
301 residuals[3] = sum_x * sum_y;
302 residuals[4] = sum_y * sum_z;
303 residuals[5] = sum_x * sum_z;
304 residuals[6] = sum_x * sum_y * sum_z;
305 return true;
306 }
307 };
308
309 class ThreeParameterCostFunctorTest : public ::testing::Test {
310 protected:
SetUp()311 virtual void SetUp() {
312 // Prepare the parameters.
313 x_.resize(1);
314 x_[0] = 0.0;
315
316 y_.resize(2);
317 y_[0] = 1.0;
318 y_[1] = 3.0;
319
320 z_.resize(3);
321 z_[0] = 2.0;
322 z_[1] = 4.0;
323 z_[2] = 6.0;
324
325 parameter_blocks_.resize(3);
326 parameter_blocks_[0] = &x_[0];
327 parameter_blocks_[1] = &y_[0];
328 parameter_blocks_[2] = &z_[0];
329
330 // Prepare the cost function.
331 typedef DynamicNumericDiffCostFunction<MyThreeParameterCostFunctor>
332 DynamicMyThreeParameterCostFunction;
333 DynamicMyThreeParameterCostFunction * cost_function =
334 new DynamicMyThreeParameterCostFunction(
335 new MyThreeParameterCostFunctor());
336 cost_function->AddParameterBlock(1);
337 cost_function->AddParameterBlock(2);
338 cost_function->AddParameterBlock(3);
339 cost_function->SetNumResiduals(7);
340
341 cost_function_.reset(cost_function);
342
343 // Setup jacobian data.
344 jacobian_vect_.resize(3);
345 jacobian_vect_[0].resize(7 * x_.size(), -100000);
346 jacobian_vect_[1].resize(7 * y_.size(), -100000);
347 jacobian_vect_[2].resize(7 * z_.size(), -100000);
348
349 // Prepare the expected residuals.
350 const double sum_x = x_[0];
351 const double sum_y = y_[0] + 2.0 * y_[1];
352 const double sum_z = z_[0] + 3.0 * z_[1] + 6.0 * z_[2];
353
354 expected_residuals_.resize(7);
355 expected_residuals_[0] = sum_x;
356 expected_residuals_[1] = sum_y;
357 expected_residuals_[2] = sum_z;
358 expected_residuals_[3] = sum_x * sum_y;
359 expected_residuals_[4] = sum_y * sum_z;
360 expected_residuals_[5] = sum_x * sum_z;
361 expected_residuals_[6] = sum_x * sum_y * sum_z;
362
363 // Prepare the expected jacobian entries.
364 expected_jacobian_x_.resize(7);
365 expected_jacobian_x_[0] = 1.0;
366 expected_jacobian_x_[1] = 0.0;
367 expected_jacobian_x_[2] = 0.0;
368 expected_jacobian_x_[3] = sum_y;
369 expected_jacobian_x_[4] = 0.0;
370 expected_jacobian_x_[5] = sum_z;
371 expected_jacobian_x_[6] = sum_y * sum_z;
372
373 expected_jacobian_y_.resize(14);
374 expected_jacobian_y_[0] = 0.0;
375 expected_jacobian_y_[1] = 0.0;
376 expected_jacobian_y_[2] = 1.0;
377 expected_jacobian_y_[3] = 2.0;
378 expected_jacobian_y_[4] = 0.0;
379 expected_jacobian_y_[5] = 0.0;
380 expected_jacobian_y_[6] = sum_x;
381 expected_jacobian_y_[7] = 2.0 * sum_x;
382 expected_jacobian_y_[8] = sum_z;
383 expected_jacobian_y_[9] = 2.0 * sum_z;
384 expected_jacobian_y_[10] = 0.0;
385 expected_jacobian_y_[11] = 0.0;
386 expected_jacobian_y_[12] = sum_x * sum_z;
387 expected_jacobian_y_[13] = 2.0 * sum_x * sum_z;
388
389 expected_jacobian_z_.resize(21);
390 expected_jacobian_z_[0] = 0.0;
391 expected_jacobian_z_[1] = 0.0;
392 expected_jacobian_z_[2] = 0.0;
393 expected_jacobian_z_[3] = 0.0;
394 expected_jacobian_z_[4] = 0.0;
395 expected_jacobian_z_[5] = 0.0;
396 expected_jacobian_z_[6] = 1.0;
397 expected_jacobian_z_[7] = 3.0;
398 expected_jacobian_z_[8] = 6.0;
399 expected_jacobian_z_[9] = 0.0;
400 expected_jacobian_z_[10] = 0.0;
401 expected_jacobian_z_[11] = 0.0;
402 expected_jacobian_z_[12] = sum_y;
403 expected_jacobian_z_[13] = 3.0 * sum_y;
404 expected_jacobian_z_[14] = 6.0 * sum_y;
405 expected_jacobian_z_[15] = sum_x;
406 expected_jacobian_z_[16] = 3.0 * sum_x;
407 expected_jacobian_z_[17] = 6.0 * sum_x;
408 expected_jacobian_z_[18] = sum_x * sum_y;
409 expected_jacobian_z_[19] = 3.0 * sum_x * sum_y;
410 expected_jacobian_z_[20] = 6.0 * sum_x * sum_y;
411 }
412
413 protected:
414 vector<double> x_;
415 vector<double> y_;
416 vector<double> z_;
417
418 vector<double*> parameter_blocks_;
419
420 scoped_ptr<CostFunction> cost_function_;
421
422 vector<vector<double> > jacobian_vect_;
423
424 vector<double> expected_residuals_;
425
426 vector<double> expected_jacobian_x_;
427 vector<double> expected_jacobian_y_;
428 vector<double> expected_jacobian_z_;
429 };
430
TEST_F(ThreeParameterCostFunctorTest,TestThreeParameterResiduals)431 TEST_F(ThreeParameterCostFunctorTest, TestThreeParameterResiduals) {
432 vector<double> residuals(7, -100000);
433 EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
434 residuals.data(),
435 NULL));
436 for (int i = 0; i < 7; ++i) {
437 EXPECT_EQ(expected_residuals_[i], residuals[i]);
438 }
439 }
440
TEST_F(ThreeParameterCostFunctorTest,TestThreeParameterJacobian)441 TEST_F(ThreeParameterCostFunctorTest, TestThreeParameterJacobian) {
442 vector<double> residuals(7, -100000);
443
444 vector<double*> jacobian;
445 jacobian.push_back(jacobian_vect_[0].data());
446 jacobian.push_back(jacobian_vect_[1].data());
447 jacobian.push_back(jacobian_vect_[2].data());
448
449 EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
450 residuals.data(),
451 jacobian.data()));
452
453 for (int i = 0; i < 7; ++i) {
454 EXPECT_EQ(expected_residuals_[i], residuals[i]);
455 }
456
457 for (int i = 0; i < 7; ++i) {
458 EXPECT_NEAR(expected_jacobian_x_[i], jacobian[0][i], kTolerance);
459 }
460
461 for (int i = 0; i < 14; ++i) {
462 EXPECT_NEAR(expected_jacobian_y_[i], jacobian[1][i], kTolerance);
463 }
464
465 for (int i = 0; i < 21; ++i) {
466 EXPECT_NEAR(expected_jacobian_z_[i], jacobian[2][i], kTolerance);
467 }
468 }
469
TEST_F(ThreeParameterCostFunctorTest,ThreeParameterJacobianWithFirstAndLastParameterBlockConstant)470 TEST_F(ThreeParameterCostFunctorTest,
471 ThreeParameterJacobianWithFirstAndLastParameterBlockConstant) {
472 vector<double> residuals(7, -100000);
473
474 vector<double*> jacobian;
475 jacobian.push_back(NULL);
476 jacobian.push_back(jacobian_vect_[1].data());
477 jacobian.push_back(NULL);
478
479 EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
480 residuals.data(),
481 jacobian.data()));
482
483 for (int i = 0; i < 7; ++i) {
484 EXPECT_EQ(expected_residuals_[i], residuals[i]);
485 }
486
487 for (int i = 0; i < 14; ++i) {
488 EXPECT_NEAR(expected_jacobian_y_[i], jacobian[1][i], kTolerance);
489 }
490 }
491
TEST_F(ThreeParameterCostFunctorTest,ThreeParameterJacobianWithSecondParameterBlockConstant)492 TEST_F(ThreeParameterCostFunctorTest,
493 ThreeParameterJacobianWithSecondParameterBlockConstant) {
494 vector<double> residuals(7, -100000);
495
496 vector<double*> jacobian;
497 jacobian.push_back(jacobian_vect_[0].data());
498 jacobian.push_back(NULL);
499 jacobian.push_back(jacobian_vect_[2].data());
500
501 EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
502 residuals.data(),
503 jacobian.data()));
504
505 for (int i = 0; i < 7; ++i) {
506 EXPECT_EQ(expected_residuals_[i], residuals[i]);
507 }
508
509 for (int i = 0; i < 7; ++i) {
510 EXPECT_NEAR(expected_jacobian_x_[i], jacobian[0][i], kTolerance);
511 }
512
513 for (int i = 0; i < 21; ++i) {
514 EXPECT_NEAR(expected_jacobian_z_[i], jacobian[2][i], kTolerance);
515 }
516 }
517
518 } // namespace internal
519 } // namespace ceres
520