blob: b62b56a90cd6f5aaf1073b88b7c172605598ea29 [file] [log] [blame]
Keir Mierle3130b3c2013-02-11 19:39:29 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 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: thadh@gmail.com (Thad Hughes)
30// mierle@gmail.com (Keir Mierle)
31// sameeragarwal@google.com (Sameer Agarwal)
32
33#include "ceres/dynamic_autodiff_cost_function.h"
34
35#include <cstddef>
36
37#include "gtest/gtest.h"
38
39namespace ceres {
40namespace internal {
41
42// Takes 2 parameter blocks:
43// parameters[0] is size 10.
44// parameters[1] is size 5.
45// Emits 21 residuals:
46// A: i - parameters[0][i], for i in [0,10) -- this is 10 residuals
47// B: parameters[0][i] - i, for i in [0,10) -- this is another 10.
48// C: sum(parameters[0][i]^2 - 8*parameters[0][i]) + sum(parameters[1][i])
49class MyCostFunctor {
50 public:
51 template <typename T>
52 bool operator()(T const* const* parameters, T* residuals) const {
53 const T* params0 = parameters[0];
54 int r = 0;
55 for (int i = 0; i < 10; ++i) {
56 residuals[r++] = T(i) - params0[i];
57 residuals[r++] = params0[i] - T(i);
58 }
59
60 T c_residual(0.0);
61 for (int i = 0; i < 10; ++i) {
62 c_residual += pow(params0[i], 2) - T(8) * params0[i];
63 }
64
65 const T* 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
74TEST(DynamicAutodiffCostFunctionTest, TestResiduals) {
75 vector<double> param_block_0(10, 0.0);
76 vector<double> param_block_1(5, 0.0);
77 DynamicAutoDiffCostFunction<MyCostFunctor, 3> 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] = &param_block_0[0];
87 parameter_blocks[1] = &param_block_1[0];
88 EXPECT_TRUE(cost_function.Evaluate(&parameter_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
98TEST(DynamicAutodiffCostFunctionTest, TestJacobian) {
99 // Test the residual counting.
100 vector<double> param_block_0(10, 0.0);
101 for (int i = 0; i < 10; ++i) {
102 param_block_0[i] = 2 * i;
103 }
104 vector<double> param_block_1(5, 0.0);
105 DynamicAutoDiffCostFunction<MyCostFunctor, 3> cost_function(
106 new MyCostFunctor());
107 cost_function.AddParameterBlock(param_block_0.size());
108 cost_function.AddParameterBlock(param_block_1.size());
109 cost_function.SetNumResiduals(21);
110
111 // Prepare the residuals.
112 vector<double> residuals(21, -100000);
113
114 // Prepare the parameters.
115 vector<double*> parameter_blocks(2);
116 parameter_blocks[0] = &param_block_0[0];
117 parameter_blocks[1] = &param_block_1[0];
118
119 // Prepare the jacobian.
120 vector<vector<double> > jacobian_vect(2);
121 jacobian_vect[0].resize(21 * 10, -100000);
122 jacobian_vect[1].resize(21 * 5, -100000);
123 vector<double*> jacobian;
124 jacobian.push_back(jacobian_vect[0].data());
125 jacobian.push_back(jacobian_vect[1].data());
126
127 // Test jacobian computation.
128 EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
129 residuals.data(),
130 jacobian.data()));
131
132 for (int r = 0; r < 10; ++r) {
133 EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
134 EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
135 }
136 EXPECT_EQ(420, residuals.at(20));
137 for (int p = 0; p < 10; ++p) {
138 // Check "A" Jacobian.
139 EXPECT_EQ(-1.0, jacobian_vect[0][2*p * 10 + p]);
140 // Check "B" Jacobian.
141 EXPECT_EQ(+1.0, jacobian_vect[0][(2*p+1) * 10 + p]);
142 jacobian_vect[0][2*p * 10 + p] = 0.0;
143 jacobian_vect[0][(2*p+1) * 10 + p] = 0.0;
144 }
145
146 // Check "C" Jacobian for first parameter block.
147 for (int p = 0; p < 10; ++p) {
148 EXPECT_EQ(4 * p - 8, jacobian_vect[0][20 * 10 + p]);
149 jacobian_vect[0][20 * 10 + p] = 0.0;
150 }
151 for (int i = 0; i < jacobian_vect[0].size(); ++i) {
152 EXPECT_EQ(0.0, jacobian_vect[0][i]);
153 }
154
155 // Check "C" Jacobian for second parameter block.
156 for (int p = 0; p < 5; ++p) {
157 EXPECT_EQ(1.0, jacobian_vect[1][20 * 5 + p]);
158 jacobian_vect[1][20 * 5 + p] = 0.0;
159 }
160 for (int i = 0; i < jacobian_vect[1].size(); ++i) {
161 EXPECT_EQ(0.0, jacobian_vect[1][i]);
162 }
163}
164
Sameer Agarwal974513a2013-02-12 14:22:40 -0800165TEST(DynamicAutodiffCostFunctionTest, JacobianWithFirstParameterBlockConstant) {
166 // Test the residual counting.
167 vector<double> param_block_0(10, 0.0);
168 for (int i = 0; i < 10; ++i) {
169 param_block_0[i] = 2 * i;
170 }
171 vector<double> param_block_1(5, 0.0);
172 DynamicAutoDiffCostFunction<MyCostFunctor, 3> cost_function(
173 new MyCostFunctor());
174 cost_function.AddParameterBlock(param_block_0.size());
175 cost_function.AddParameterBlock(param_block_1.size());
176 cost_function.SetNumResiduals(21);
177
178 // Prepare the residuals.
179 vector<double> residuals(21, -100000);
180
181 // Prepare the parameters.
182 vector<double*> parameter_blocks(2);
183 parameter_blocks[0] = &param_block_0[0];
184 parameter_blocks[1] = &param_block_1[0];
185
186 // Prepare the jacobian.
187 vector<vector<double> > jacobian_vect(2);
188 jacobian_vect[0].resize(21 * 10, -100000);
189 jacobian_vect[1].resize(21 * 5, -100000);
190 vector<double*> jacobian;
191 jacobian.push_back(NULL);
192 jacobian.push_back(jacobian_vect[1].data());
193
194 // Test jacobian computation.
195 EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
196 residuals.data(),
197 jacobian.data()));
198
199 for (int r = 0; r < 10; ++r) {
200 EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
201 EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
202 }
203 EXPECT_EQ(420, residuals.at(20));
204
205 // Check "C" Jacobian for second parameter block.
206 for (int p = 0; p < 5; ++p) {
207 EXPECT_EQ(1.0, jacobian_vect[1][20 * 5 + p]);
208 jacobian_vect[1][20 * 5 + p] = 0.0;
209 }
210 for (int i = 0; i < jacobian_vect[1].size(); ++i) {
211 EXPECT_EQ(0.0, jacobian_vect[1][i]);
212 }
213}
214
215TEST(DynamicAutodiffCostFunctionTest, JacobianWithSecondParameterBlockConstant) {
216 // Test the residual counting.
217 vector<double> param_block_0(10, 0.0);
218 for (int i = 0; i < 10; ++i) {
219 param_block_0[i] = 2 * i;
220 }
221 vector<double> param_block_1(5, 0.0);
222 DynamicAutoDiffCostFunction<MyCostFunctor, 3> cost_function(
223 new MyCostFunctor());
224 cost_function.AddParameterBlock(param_block_0.size());
225 cost_function.AddParameterBlock(param_block_1.size());
226 cost_function.SetNumResiduals(21);
227
228 // Prepare the residuals.
229 vector<double> residuals(21, -100000);
230
231 // Prepare the parameters.
232 vector<double*> parameter_blocks(2);
233 parameter_blocks[0] = &param_block_0[0];
234 parameter_blocks[1] = &param_block_1[0];
235
236 // Prepare the jacobian.
237 vector<vector<double> > jacobian_vect(2);
238 jacobian_vect[0].resize(21 * 10, -100000);
239 jacobian_vect[1].resize(21 * 5, -100000);
240 vector<double*> jacobian;
241 jacobian.push_back(jacobian_vect[0].data());
242 jacobian.push_back(NULL);
243
244 // Test jacobian computation.
245 EXPECT_TRUE(cost_function.Evaluate(parameter_blocks.data(),
246 residuals.data(),
247 jacobian.data()));
248
249 for (int r = 0; r < 10; ++r) {
250 EXPECT_EQ(-1.0 * r, residuals.at(r * 2));
251 EXPECT_EQ(+1.0 * r, residuals.at(r * 2 + 1));
252 }
253 EXPECT_EQ(420, residuals.at(20));
254 for (int p = 0; p < 10; ++p) {
255 // Check "A" Jacobian.
256 EXPECT_EQ(-1.0, jacobian_vect[0][2*p * 10 + p]);
257 // Check "B" Jacobian.
258 EXPECT_EQ(+1.0, jacobian_vect[0][(2*p+1) * 10 + p]);
259 jacobian_vect[0][2*p * 10 + p] = 0.0;
260 jacobian_vect[0][(2*p+1) * 10 + p] = 0.0;
261 }
262
263 // Check "C" Jacobian for first parameter block.
264 for (int p = 0; p < 10; ++p) {
265 EXPECT_EQ(4 * p - 8, jacobian_vect[0][20 * 10 + p]);
266 jacobian_vect[0][20 * 10 + p] = 0.0;
267 }
268 for (int i = 0; i < jacobian_vect[0].size(); ++i) {
269 EXPECT_EQ(0.0, jacobian_vect[0][i]);
270 }
271}
272
Keir Mierle3130b3c2013-02-11 19:39:29 -0800273} // namespace internal
274} // namespace ceres