blob: 3c05f4843f7901f5937673938a93d981e462a34c [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>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080034
Austin Schuh70cc9552019-01-21 19:46:48 -080035#include "ceres/internal/eigen.h"
36#include "ceres/local_parameterization.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080037#include "ceres/parameter_block.h"
38#include "ceres/sized_cost_function.h"
39#include "gtest/gtest.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080040
41namespace ceres {
42namespace internal {
43
44using std::vector;
45
46// Trivial cost function that accepts three arguments.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080047class TernaryCostFunction : public CostFunction {
Austin Schuh70cc9552019-01-21 19:46:48 -080048 public:
49 TernaryCostFunction(int num_residuals,
50 int32_t parameter_block1_size,
51 int32_t parameter_block2_size,
52 int32_t parameter_block3_size) {
53 set_num_residuals(num_residuals);
54 mutable_parameter_block_sizes()->push_back(parameter_block1_size);
55 mutable_parameter_block_sizes()->push_back(parameter_block2_size);
56 mutable_parameter_block_sizes()->push_back(parameter_block3_size);
57 }
58
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080059 bool Evaluate(double const* const* parameters,
60 double* residuals,
61 double** jacobians) const final {
Austin Schuh70cc9552019-01-21 19:46:48 -080062 for (int i = 0; i < num_residuals(); ++i) {
63 residuals[i] = i;
64 }
65 if (jacobians) {
66 for (int k = 0; k < 3; ++k) {
67 if (jacobians[k] != NULL) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080068 MatrixRef jacobian(
69 jacobians[k], num_residuals(), parameter_block_sizes()[k]);
Austin Schuh70cc9552019-01-21 19:46:48 -080070 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);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800112 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800113
114 // Verify cost and residual evaluation.
115 double residuals[3];
116 residual_block.Evaluate(true, &cost, residuals, NULL, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800117 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800118 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
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800134 double* jacobian_ptrs[3] = {
135 jacobian_rx.data(), jacobian_ry.data(), jacobian_rz.data()};
Austin Schuh70cc9552019-01-21 19:46:48 -0800136
137 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800138 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800139 EXPECT_EQ(0.0, residuals[0]);
140 EXPECT_EQ(1.0, residuals[1]);
141 EXPECT_EQ(2.0, residuals[2]);
142
143 EXPECT_TRUE((jacobian_rx.array() == 0.0).all()) << "\n" << jacobian_rx;
144 EXPECT_TRUE((jacobian_ry.array() == 1.0).all()) << "\n" << jacobian_ry;
145 EXPECT_TRUE((jacobian_rz.array() == 2.0).all()) << "\n" << jacobian_rz;
146
147 // Verify cost, residual, and partial jacobian evaluation.
148 cost = 0.0;
149 VectorRef(residuals, 3).setConstant(0.0);
150 jacobian_rx.setConstant(-1.0);
151 jacobian_ry.setConstant(-1.0);
152 jacobian_rz.setConstant(-1.0);
153
154 jacobian_ptrs[1] = NULL; // Don't compute the jacobian for y.
155
156 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800157 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800158 EXPECT_EQ(0.0, residuals[0]);
159 EXPECT_EQ(1.0, residuals[1]);
160 EXPECT_EQ(2.0, residuals[2]);
161
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800162 // clang-format off
Austin Schuh70cc9552019-01-21 19:46:48 -0800163 EXPECT_TRUE((jacobian_rx.array() == 0.0).all()) << "\n" << jacobian_rx;
164 EXPECT_TRUE((jacobian_ry.array() == -1.0).all()) << "\n" << jacobian_ry;
165 EXPECT_TRUE((jacobian_rz.array() == 2.0).all()) << "\n" << jacobian_rz;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800166 // clang-format on
Austin Schuh70cc9552019-01-21 19:46:48 -0800167}
168
169// Trivial cost function that accepts three arguments.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800170class LocallyParameterizedCostFunction : public SizedCostFunction<3, 2, 3, 4> {
Austin Schuh70cc9552019-01-21 19:46:48 -0800171 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800172 bool Evaluate(double const* const* parameters,
173 double* residuals,
174 double** jacobians) const final {
Austin Schuh70cc9552019-01-21 19:46:48 -0800175 for (int i = 0; i < num_residuals(); ++i) {
176 residuals[i] = i;
177 }
178 if (jacobians) {
179 for (int k = 0; k < 3; ++k) {
180 // The jacobians here are full sized, but they are transformed in the
181 // evaluator into the "local" jacobian. In the tests, the "subset
182 // constant" parameterization is used, which should pick out columns
183 // from these jacobians. Put values in the jacobian that make this
184 // obvious; in particular, make the jacobians like this:
185 //
186 // 0 1 2 3 4 ...
187 // 0 1 2 3 4 ...
188 // 0 1 2 3 4 ...
189 //
190 if (jacobians[k] != NULL) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800191 MatrixRef jacobian(
192 jacobians[k], num_residuals(), parameter_block_sizes()[k]);
Austin Schuh70cc9552019-01-21 19:46:48 -0800193 for (int j = 0; j < k + 2; ++j) {
194 jacobian.col(j).setConstant(j);
195 }
196 }
197 }
198 }
199 return true;
200 }
201};
202
203TEST(ResidualBlock, EvaluteWithLocalParameterizations) {
204 double scratch[64];
205
206 // Prepare the parameter blocks.
207 double values_x[2];
208 ParameterBlock x(values_x, 2, -1);
209
210 double values_y[3];
211 ParameterBlock y(values_y, 3, -1);
212
213 double values_z[4];
214 ParameterBlock z(values_z, 4, -1);
215
216 vector<ParameterBlock*> parameters;
217 parameters.push_back(&x);
218 parameters.push_back(&y);
219 parameters.push_back(&z);
220
221 // Make x have the first component fixed.
222 vector<int> x_fixed;
223 x_fixed.push_back(0);
224 SubsetParameterization x_parameterization(2, x_fixed);
225 x.SetParameterization(&x_parameterization);
226
227 // Make z have the last and last component fixed.
228 vector<int> z_fixed;
229 z_fixed.push_back(2);
230 SubsetParameterization z_parameterization(4, z_fixed);
231 z.SetParameterization(&z_parameterization);
232
233 LocallyParameterizedCostFunction cost_function;
234
235 // Create the object under tests.
236 ResidualBlock residual_block(&cost_function, NULL, parameters, -1);
237
238 // Verify getters.
239 EXPECT_EQ(&cost_function, residual_block.cost_function());
240 EXPECT_EQ(NULL, residual_block.loss_function());
241 EXPECT_EQ(parameters[0], residual_block.parameter_blocks()[0]);
242 EXPECT_EQ(parameters[1], residual_block.parameter_blocks()[1]);
243 EXPECT_EQ(parameters[2], residual_block.parameter_blocks()[2]);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800244 EXPECT_EQ(3 * (2 + 4) + 3, residual_block.NumScratchDoublesForEvaluate());
Austin Schuh70cc9552019-01-21 19:46:48 -0800245
246 // Verify cost-only evaluation.
247 double cost;
248 residual_block.Evaluate(true, &cost, NULL, NULL, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800249 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800250
251 // Verify cost and residual evaluation.
252 double residuals[3];
253 residual_block.Evaluate(true, &cost, residuals, NULL, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800254 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800255 EXPECT_EQ(0.0, residuals[0]);
256 EXPECT_EQ(1.0, residuals[1]);
257 EXPECT_EQ(2.0, residuals[2]);
258
259 // Verify cost, residual, and jacobian evaluation.
260 cost = 0.0;
261 VectorRef(residuals, 3).setConstant(0.0);
262
263 Matrix jacobian_rx(3, 1); // Since the first element is fixed.
264 Matrix jacobian_ry(3, 3);
265 Matrix jacobian_rz(3, 3); // Since the third element is fixed.
266
267 jacobian_rx.setConstant(-1.0);
268 jacobian_ry.setConstant(-1.0);
269 jacobian_rz.setConstant(-1.0);
270
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800271 double* jacobian_ptrs[3] = {
272 jacobian_rx.data(), jacobian_ry.data(), jacobian_rz.data()};
Austin Schuh70cc9552019-01-21 19:46:48 -0800273
274 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800275 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800276 EXPECT_EQ(0.0, residuals[0]);
277 EXPECT_EQ(1.0, residuals[1]);
278 EXPECT_EQ(2.0, residuals[2]);
279
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800280 // clang-format off
281
Austin Schuh70cc9552019-01-21 19:46:48 -0800282 Matrix expected_jacobian_rx(3, 1);
283 expected_jacobian_rx << 1.0, 1.0, 1.0;
284
285 Matrix expected_jacobian_ry(3, 3);
286 expected_jacobian_ry << 0.0, 1.0, 2.0,
287 0.0, 1.0, 2.0,
288 0.0, 1.0, 2.0;
289
290 Matrix expected_jacobian_rz(3, 3);
291 expected_jacobian_rz << 0.0, 1.0, /* 2.0, */ 3.0, // 3rd parameter constant.
292 0.0, 1.0, /* 2.0, */ 3.0,
293 0.0, 1.0, /* 2.0, */ 3.0;
294
295 EXPECT_EQ(expected_jacobian_rx, jacobian_rx)
296 << "\nExpected:\n" << expected_jacobian_rx
297 << "\nActual:\n" << jacobian_rx;
298 EXPECT_EQ(expected_jacobian_ry, jacobian_ry)
299 << "\nExpected:\n" << expected_jacobian_ry
300 << "\nActual:\n" << jacobian_ry;
301 EXPECT_EQ(expected_jacobian_rz, jacobian_rz)
302 << "\nExpected:\n " << expected_jacobian_rz
303 << "\nActual:\n" << jacobian_rz;
304
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800305 // clang-format on
306
Austin Schuh70cc9552019-01-21 19:46:48 -0800307 // Verify cost, residual, and partial jacobian evaluation.
308 cost = 0.0;
309 VectorRef(residuals, 3).setConstant(0.0);
310 jacobian_rx.setConstant(-1.0);
311 jacobian_ry.setConstant(-1.0);
312 jacobian_rz.setConstant(-1.0);
313
314 jacobian_ptrs[1] = NULL; // Don't compute the jacobian for y.
315
316 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800317 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800318 EXPECT_EQ(0.0, residuals[0]);
319 EXPECT_EQ(1.0, residuals[1]);
320 EXPECT_EQ(2.0, residuals[2]);
321
322 EXPECT_EQ(expected_jacobian_rx, jacobian_rx);
323 EXPECT_TRUE((jacobian_ry.array() == -1.0).all()) << "\n" << jacobian_ry;
324 EXPECT_EQ(expected_jacobian_rz, jacobian_rz);
325}
326
327} // namespace internal
328} // namespace ceres