blob: 3a33be7868461cc67481ae68ca9cd8d4a98aa7ff [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
2// Copyright 2015 Google Inc. All rights reserved.
3// http://ceres-solver.org/
4//
5// Redistribution and use in source and binary forms, with or without
6// modification, are permitted provided that the following conditions are met:
7//
8// * Redistributions of source code must retain the above copyright notice,
9// this list of conditions and the following disclaimer.
10// * Redistributions in binary form must reproduce the above copyright notice,
11// this list of conditions and the following disclaimer in the documentation
12// and/or other materials provided with the distribution.
13// * Neither the name of Google Inc. nor the names of its contributors may be
14// used to endorse or promote products derived from this software without
15// specific prior written permission.
16//
17// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
18// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
20// ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE
21// LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22// CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23// SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24// INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25// CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26// ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27// POSSIBILITY OF SUCH DAMAGE.
28//
29// Author: keir@google.com (Keir Mierle)
30
31#include "ceres/residual_block.h"
32
33#include <cstdint>
34#include "gtest/gtest.h"
35#include "ceres/parameter_block.h"
36#include "ceres/sized_cost_function.h"
37#include "ceres/internal/eigen.h"
38#include "ceres/local_parameterization.h"
39
40namespace ceres {
41namespace internal {
42
43using std::vector;
44
45// Trivial cost function that accepts three arguments.
46class TernaryCostFunction: public CostFunction {
47 public:
48 TernaryCostFunction(int num_residuals,
49 int32_t parameter_block1_size,
50 int32_t parameter_block2_size,
51 int32_t parameter_block3_size) {
52 set_num_residuals(num_residuals);
53 mutable_parameter_block_sizes()->push_back(parameter_block1_size);
54 mutable_parameter_block_sizes()->push_back(parameter_block2_size);
55 mutable_parameter_block_sizes()->push_back(parameter_block3_size);
56 }
57
58 virtual bool Evaluate(double const* const* parameters,
59 double* residuals,
60 double** jacobians) const {
61 for (int i = 0; i < num_residuals(); ++i) {
62 residuals[i] = i;
63 }
64 if (jacobians) {
65 for (int k = 0; k < 3; ++k) {
66 if (jacobians[k] != NULL) {
67 MatrixRef jacobian(jacobians[k],
68 num_residuals(),
69 parameter_block_sizes()[k]);
70 jacobian.setConstant(k);
71 }
72 }
73 }
74 return true;
75 }
76};
77
78TEST(ResidualBlock, EvaluteWithNoLossFunctionOrLocalParameterizations) {
79 double scratch[64];
80
81 // Prepare the parameter blocks.
82 double values_x[2];
83 ParameterBlock x(values_x, 2, -1);
84
85 double values_y[3];
86 ParameterBlock y(values_y, 3, -1);
87
88 double values_z[4];
89 ParameterBlock z(values_z, 4, -1);
90
91 vector<ParameterBlock*> parameters;
92 parameters.push_back(&x);
93 parameters.push_back(&y);
94 parameters.push_back(&z);
95
96 TernaryCostFunction cost_function(3, 2, 3, 4);
97
98 // Create the object under tests.
99 ResidualBlock residual_block(&cost_function, NULL, parameters, -1);
100
101 // Verify getters.
102 EXPECT_EQ(&cost_function, residual_block.cost_function());
103 EXPECT_EQ(NULL, residual_block.loss_function());
104 EXPECT_EQ(parameters[0], residual_block.parameter_blocks()[0]);
105 EXPECT_EQ(parameters[1], residual_block.parameter_blocks()[1]);
106 EXPECT_EQ(parameters[2], residual_block.parameter_blocks()[2]);
107 EXPECT_EQ(3, residual_block.NumScratchDoublesForEvaluate());
108
109 // Verify cost-only evaluation.
110 double cost;
111 residual_block.Evaluate(true, &cost, NULL, NULL, scratch);
112 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
113
114 // Verify cost and residual evaluation.
115 double residuals[3];
116 residual_block.Evaluate(true, &cost, residuals, NULL, scratch);
117 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
118 EXPECT_EQ(0.0, residuals[0]);
119 EXPECT_EQ(1.0, residuals[1]);
120 EXPECT_EQ(2.0, residuals[2]);
121
122 // Verify cost, residual, and jacobian evaluation.
123 cost = 0.0;
124 VectorRef(residuals, 3).setConstant(0.0);
125
126 Matrix jacobian_rx(3, 2);
127 Matrix jacobian_ry(3, 3);
128 Matrix jacobian_rz(3, 4);
129
130 jacobian_rx.setConstant(-1.0);
131 jacobian_ry.setConstant(-1.0);
132 jacobian_rz.setConstant(-1.0);
133
134 double *jacobian_ptrs[3] = {
135 jacobian_rx.data(),
136 jacobian_ry.data(),
137 jacobian_rz.data()
138 };
139
140 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
141 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
142 EXPECT_EQ(0.0, residuals[0]);
143 EXPECT_EQ(1.0, residuals[1]);
144 EXPECT_EQ(2.0, residuals[2]);
145
146 EXPECT_TRUE((jacobian_rx.array() == 0.0).all()) << "\n" << jacobian_rx;
147 EXPECT_TRUE((jacobian_ry.array() == 1.0).all()) << "\n" << jacobian_ry;
148 EXPECT_TRUE((jacobian_rz.array() == 2.0).all()) << "\n" << jacobian_rz;
149
150 // Verify cost, residual, and partial jacobian evaluation.
151 cost = 0.0;
152 VectorRef(residuals, 3).setConstant(0.0);
153 jacobian_rx.setConstant(-1.0);
154 jacobian_ry.setConstant(-1.0);
155 jacobian_rz.setConstant(-1.0);
156
157 jacobian_ptrs[1] = NULL; // Don't compute the jacobian for y.
158
159 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
160 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
161 EXPECT_EQ(0.0, residuals[0]);
162 EXPECT_EQ(1.0, residuals[1]);
163 EXPECT_EQ(2.0, residuals[2]);
164
165 EXPECT_TRUE((jacobian_rx.array() == 0.0).all()) << "\n" << jacobian_rx;
166 EXPECT_TRUE((jacobian_ry.array() == -1.0).all()) << "\n" << jacobian_ry;
167 EXPECT_TRUE((jacobian_rz.array() == 2.0).all()) << "\n" << jacobian_rz;
168}
169
170// Trivial cost function that accepts three arguments.
171class LocallyParameterizedCostFunction: public SizedCostFunction<3, 2, 3, 4> {
172 public:
173 virtual bool Evaluate(double const* const* parameters,
174 double* residuals,
175 double** jacobians) const {
176 for (int i = 0; i < num_residuals(); ++i) {
177 residuals[i] = i;
178 }
179 if (jacobians) {
180 for (int k = 0; k < 3; ++k) {
181 // The jacobians here are full sized, but they are transformed in the
182 // evaluator into the "local" jacobian. In the tests, the "subset
183 // constant" parameterization is used, which should pick out columns
184 // from these jacobians. Put values in the jacobian that make this
185 // obvious; in particular, make the jacobians like this:
186 //
187 // 0 1 2 3 4 ...
188 // 0 1 2 3 4 ...
189 // 0 1 2 3 4 ...
190 //
191 if (jacobians[k] != NULL) {
192 MatrixRef jacobian(jacobians[k],
193 num_residuals(),
194 parameter_block_sizes()[k]);
195 for (int j = 0; j < k + 2; ++j) {
196 jacobian.col(j).setConstant(j);
197 }
198 }
199 }
200 }
201 return true;
202 }
203};
204
205TEST(ResidualBlock, EvaluteWithLocalParameterizations) {
206 double scratch[64];
207
208 // Prepare the parameter blocks.
209 double values_x[2];
210 ParameterBlock x(values_x, 2, -1);
211
212 double values_y[3];
213 ParameterBlock y(values_y, 3, -1);
214
215 double values_z[4];
216 ParameterBlock z(values_z, 4, -1);
217
218 vector<ParameterBlock*> parameters;
219 parameters.push_back(&x);
220 parameters.push_back(&y);
221 parameters.push_back(&z);
222
223 // Make x have the first component fixed.
224 vector<int> x_fixed;
225 x_fixed.push_back(0);
226 SubsetParameterization x_parameterization(2, x_fixed);
227 x.SetParameterization(&x_parameterization);
228
229 // Make z have the last and last component fixed.
230 vector<int> z_fixed;
231 z_fixed.push_back(2);
232 SubsetParameterization z_parameterization(4, z_fixed);
233 z.SetParameterization(&z_parameterization);
234
235 LocallyParameterizedCostFunction cost_function;
236
237 // Create the object under tests.
238 ResidualBlock residual_block(&cost_function, NULL, parameters, -1);
239
240 // Verify getters.
241 EXPECT_EQ(&cost_function, residual_block.cost_function());
242 EXPECT_EQ(NULL, residual_block.loss_function());
243 EXPECT_EQ(parameters[0], residual_block.parameter_blocks()[0]);
244 EXPECT_EQ(parameters[1], residual_block.parameter_blocks()[1]);
245 EXPECT_EQ(parameters[2], residual_block.parameter_blocks()[2]);
246 EXPECT_EQ(3*(2 + 4) + 3, residual_block.NumScratchDoublesForEvaluate());
247
248 // Verify cost-only evaluation.
249 double cost;
250 residual_block.Evaluate(true, &cost, NULL, NULL, scratch);
251 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
252
253 // Verify cost and residual evaluation.
254 double residuals[3];
255 residual_block.Evaluate(true, &cost, residuals, NULL, scratch);
256 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
257 EXPECT_EQ(0.0, residuals[0]);
258 EXPECT_EQ(1.0, residuals[1]);
259 EXPECT_EQ(2.0, residuals[2]);
260
261 // Verify cost, residual, and jacobian evaluation.
262 cost = 0.0;
263 VectorRef(residuals, 3).setConstant(0.0);
264
265 Matrix jacobian_rx(3, 1); // Since the first element is fixed.
266 Matrix jacobian_ry(3, 3);
267 Matrix jacobian_rz(3, 3); // Since the third element is fixed.
268
269 jacobian_rx.setConstant(-1.0);
270 jacobian_ry.setConstant(-1.0);
271 jacobian_rz.setConstant(-1.0);
272
273 double *jacobian_ptrs[3] = {
274 jacobian_rx.data(),
275 jacobian_ry.data(),
276 jacobian_rz.data()
277 };
278
279 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
280 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
281 EXPECT_EQ(0.0, residuals[0]);
282 EXPECT_EQ(1.0, residuals[1]);
283 EXPECT_EQ(2.0, residuals[2]);
284
285 Matrix expected_jacobian_rx(3, 1);
286 expected_jacobian_rx << 1.0, 1.0, 1.0;
287
288 Matrix expected_jacobian_ry(3, 3);
289 expected_jacobian_ry << 0.0, 1.0, 2.0,
290 0.0, 1.0, 2.0,
291 0.0, 1.0, 2.0;
292
293 Matrix expected_jacobian_rz(3, 3);
294 expected_jacobian_rz << 0.0, 1.0, /* 2.0, */ 3.0, // 3rd parameter constant.
295 0.0, 1.0, /* 2.0, */ 3.0,
296 0.0, 1.0, /* 2.0, */ 3.0;
297
298 EXPECT_EQ(expected_jacobian_rx, jacobian_rx)
299 << "\nExpected:\n" << expected_jacobian_rx
300 << "\nActual:\n" << jacobian_rx;
301 EXPECT_EQ(expected_jacobian_ry, jacobian_ry)
302 << "\nExpected:\n" << expected_jacobian_ry
303 << "\nActual:\n" << jacobian_ry;
304 EXPECT_EQ(expected_jacobian_rz, jacobian_rz)
305 << "\nExpected:\n " << expected_jacobian_rz
306 << "\nActual:\n" << jacobian_rz;
307
308 // Verify cost, residual, and partial jacobian evaluation.
309 cost = 0.0;
310 VectorRef(residuals, 3).setConstant(0.0);
311 jacobian_rx.setConstant(-1.0);
312 jacobian_ry.setConstant(-1.0);
313 jacobian_rz.setConstant(-1.0);
314
315 jacobian_ptrs[1] = NULL; // Don't compute the jacobian for y.
316
317 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
318 EXPECT_EQ(0.5 * (0*0 + 1*1 + 2*2), cost);
319 EXPECT_EQ(0.0, residuals[0]);
320 EXPECT_EQ(1.0, residuals[1]);
321 EXPECT_EQ(2.0, residuals[2]);
322
323 EXPECT_EQ(expected_jacobian_rx, jacobian_rx);
324 EXPECT_TRUE((jacobian_ry.array() == -1.0).all()) << "\n" << jacobian_ry;
325 EXPECT_EQ(expected_jacobian_rz, jacobian_rz);
326}
327
328} // namespace internal
329} // namespace ceres