blob: 8040136dd255dc430d829c9478c3823118cb8266 [file] [log] [blame]
Austin Schuh70cc9552019-01-21 19:46:48 -08001// Ceres Solver - A fast non-linear least squares minimizer
Austin Schuh3de38b02024-06-25 18:25:10 -07002// Copyright 2023 Google Inc. All rights reserved.
Austin Schuh70cc9552019-01-21 19:46:48 -08003// 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 Schuh3de38b02024-06-25 18:25:10 -070034#include <string>
35#include <vector>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080036
Austin Schuh70cc9552019-01-21 19:46:48 -080037#include "ceres/internal/eigen.h"
Austin Schuh3de38b02024-06-25 18:25:10 -070038#include "ceres/manifold.h"
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080039#include "ceres/parameter_block.h"
40#include "ceres/sized_cost_function.h"
41#include "gtest/gtest.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080042
Austin Schuh3de38b02024-06-25 18:25:10 -070043namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -080044
45// Trivial cost function that accepts three arguments.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080046class TernaryCostFunction : public CostFunction {
Austin Schuh70cc9552019-01-21 19:46:48 -080047 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
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080058 bool Evaluate(double const* const* parameters,
59 double* residuals,
60 double** jacobians) const final {
Austin Schuh70cc9552019-01-21 19:46:48 -080061 for (int i = 0; i < num_residuals(); ++i) {
62 residuals[i] = i;
63 }
64 if (jacobians) {
65 for (int k = 0; k < 3; ++k) {
Austin Schuh3de38b02024-06-25 18:25:10 -070066 if (jacobians[k] != nullptr) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080067 MatrixRef jacobian(
68 jacobians[k], num_residuals(), parameter_block_sizes()[k]);
Austin Schuh70cc9552019-01-21 19:46:48 -080069 jacobian.setConstant(k);
70 }
71 }
72 }
73 return true;
74 }
75};
76
Austin Schuh3de38b02024-06-25 18:25:10 -070077TEST(ResidualBlock, EvaluateWithNoLossFunctionOrManifolds) {
Austin Schuh70cc9552019-01-21 19:46:48 -080078 double scratch[64];
79
80 // Prepare the parameter blocks.
81 double values_x[2];
82 ParameterBlock x(values_x, 2, -1);
83
84 double values_y[3];
85 ParameterBlock y(values_y, 3, -1);
86
87 double values_z[4];
88 ParameterBlock z(values_z, 4, -1);
89
Austin Schuh3de38b02024-06-25 18:25:10 -070090 std::vector<ParameterBlock*> parameters;
Austin Schuh70cc9552019-01-21 19:46:48 -080091 parameters.push_back(&x);
92 parameters.push_back(&y);
93 parameters.push_back(&z);
94
95 TernaryCostFunction cost_function(3, 2, 3, 4);
96
97 // Create the object under tests.
Austin Schuh3de38b02024-06-25 18:25:10 -070098 ResidualBlock residual_block(&cost_function, nullptr, parameters, -1);
Austin Schuh70cc9552019-01-21 19:46:48 -080099
100 // Verify getters.
101 EXPECT_EQ(&cost_function, residual_block.cost_function());
Austin Schuh3de38b02024-06-25 18:25:10 -0700102 EXPECT_EQ(nullptr, residual_block.loss_function());
Austin Schuh70cc9552019-01-21 19:46:48 -0800103 EXPECT_EQ(parameters[0], residual_block.parameter_blocks()[0]);
104 EXPECT_EQ(parameters[1], residual_block.parameter_blocks()[1]);
105 EXPECT_EQ(parameters[2], residual_block.parameter_blocks()[2]);
106 EXPECT_EQ(3, residual_block.NumScratchDoublesForEvaluate());
107
108 // Verify cost-only evaluation.
109 double cost;
Austin Schuh3de38b02024-06-25 18:25:10 -0700110 residual_block.Evaluate(true, &cost, nullptr, nullptr, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800111 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800112
113 // Verify cost and residual evaluation.
114 double residuals[3];
Austin Schuh3de38b02024-06-25 18:25:10 -0700115 residual_block.Evaluate(true, &cost, residuals, nullptr, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800116 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800117 EXPECT_EQ(0.0, residuals[0]);
118 EXPECT_EQ(1.0, residuals[1]);
119 EXPECT_EQ(2.0, residuals[2]);
120
121 // Verify cost, residual, and jacobian evaluation.
122 cost = 0.0;
123 VectorRef(residuals, 3).setConstant(0.0);
124
125 Matrix jacobian_rx(3, 2);
126 Matrix jacobian_ry(3, 3);
127 Matrix jacobian_rz(3, 4);
128
129 jacobian_rx.setConstant(-1.0);
130 jacobian_ry.setConstant(-1.0);
131 jacobian_rz.setConstant(-1.0);
132
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800133 double* jacobian_ptrs[3] = {
134 jacobian_rx.data(), jacobian_ry.data(), jacobian_rz.data()};
Austin Schuh70cc9552019-01-21 19:46:48 -0800135
136 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800137 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800138 EXPECT_EQ(0.0, residuals[0]);
139 EXPECT_EQ(1.0, residuals[1]);
140 EXPECT_EQ(2.0, residuals[2]);
141
142 EXPECT_TRUE((jacobian_rx.array() == 0.0).all()) << "\n" << jacobian_rx;
143 EXPECT_TRUE((jacobian_ry.array() == 1.0).all()) << "\n" << jacobian_ry;
144 EXPECT_TRUE((jacobian_rz.array() == 2.0).all()) << "\n" << jacobian_rz;
145
146 // Verify cost, residual, and partial jacobian evaluation.
147 cost = 0.0;
148 VectorRef(residuals, 3).setConstant(0.0);
149 jacobian_rx.setConstant(-1.0);
150 jacobian_ry.setConstant(-1.0);
151 jacobian_rz.setConstant(-1.0);
152
Austin Schuh3de38b02024-06-25 18:25:10 -0700153 jacobian_ptrs[1] = nullptr; // Don't compute the jacobian for y.
Austin Schuh70cc9552019-01-21 19:46:48 -0800154
155 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800156 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800157 EXPECT_EQ(0.0, residuals[0]);
158 EXPECT_EQ(1.0, residuals[1]);
159 EXPECT_EQ(2.0, residuals[2]);
160
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800161 // clang-format off
Austin Schuh70cc9552019-01-21 19:46:48 -0800162 EXPECT_TRUE((jacobian_rx.array() == 0.0).all()) << "\n" << jacobian_rx;
163 EXPECT_TRUE((jacobian_ry.array() == -1.0).all()) << "\n" << jacobian_ry;
164 EXPECT_TRUE((jacobian_rz.array() == 2.0).all()) << "\n" << jacobian_rz;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800165 // clang-format on
Austin Schuh70cc9552019-01-21 19:46:48 -0800166}
167
168// Trivial cost function that accepts three arguments.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800169class LocallyParameterizedCostFunction : public SizedCostFunction<3, 2, 3, 4> {
Austin Schuh70cc9552019-01-21 19:46:48 -0800170 public:
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800171 bool Evaluate(double const* const* parameters,
172 double* residuals,
173 double** jacobians) const final {
Austin Schuh70cc9552019-01-21 19:46:48 -0800174 for (int i = 0; i < num_residuals(); ++i) {
175 residuals[i] = i;
176 }
177 if (jacobians) {
178 for (int k = 0; k < 3; ++k) {
179 // The jacobians here are full sized, but they are transformed in the
Austin Schuh3de38b02024-06-25 18:25:10 -0700180 // evaluator into the "local" jacobian. In the tests, the
181 // "SubsetManifold" is used, which should pick out columns from these
182 // jacobians. Put values in the jacobian that make this obvious; in
183 // particular, make the jacobians like this:
Austin Schuh70cc9552019-01-21 19:46:48 -0800184 //
185 // 0 1 2 3 4 ...
186 // 0 1 2 3 4 ...
187 // 0 1 2 3 4 ...
188 //
Austin Schuh3de38b02024-06-25 18:25:10 -0700189 if (jacobians[k] != nullptr) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800190 MatrixRef jacobian(
191 jacobians[k], num_residuals(), parameter_block_sizes()[k]);
Austin Schuh70cc9552019-01-21 19:46:48 -0800192 for (int j = 0; j < k + 2; ++j) {
193 jacobian.col(j).setConstant(j);
194 }
195 }
196 }
197 }
198 return true;
199 }
200};
201
Austin Schuh3de38b02024-06-25 18:25:10 -0700202TEST(ResidualBlock, EvaluateWithManifolds) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800203 double scratch[64];
204
205 // Prepare the parameter blocks.
206 double values_x[2];
207 ParameterBlock x(values_x, 2, -1);
208
209 double values_y[3];
210 ParameterBlock y(values_y, 3, -1);
211
212 double values_z[4];
213 ParameterBlock z(values_z, 4, -1);
214
Austin Schuh3de38b02024-06-25 18:25:10 -0700215 std::vector<ParameterBlock*> parameters;
Austin Schuh70cc9552019-01-21 19:46:48 -0800216 parameters.push_back(&x);
217 parameters.push_back(&y);
218 parameters.push_back(&z);
219
220 // Make x have the first component fixed.
Austin Schuh3de38b02024-06-25 18:25:10 -0700221 std::vector<int> x_fixed;
Austin Schuh70cc9552019-01-21 19:46:48 -0800222 x_fixed.push_back(0);
Austin Schuh3de38b02024-06-25 18:25:10 -0700223 SubsetManifold x_manifold(2, x_fixed);
224 x.SetManifold(&x_manifold);
Austin Schuh70cc9552019-01-21 19:46:48 -0800225
226 // Make z have the last and last component fixed.
Austin Schuh3de38b02024-06-25 18:25:10 -0700227 std::vector<int> z_fixed;
Austin Schuh70cc9552019-01-21 19:46:48 -0800228 z_fixed.push_back(2);
Austin Schuh3de38b02024-06-25 18:25:10 -0700229 SubsetManifold z_manifold(4, z_fixed);
230 z.SetManifold(&z_manifold);
Austin Schuh70cc9552019-01-21 19:46:48 -0800231
232 LocallyParameterizedCostFunction cost_function;
233
234 // Create the object under tests.
Austin Schuh3de38b02024-06-25 18:25:10 -0700235 ResidualBlock residual_block(&cost_function, nullptr, parameters, -1);
Austin Schuh70cc9552019-01-21 19:46:48 -0800236
237 // Verify getters.
238 EXPECT_EQ(&cost_function, residual_block.cost_function());
Austin Schuh3de38b02024-06-25 18:25:10 -0700239 EXPECT_EQ(nullptr, residual_block.loss_function());
Austin Schuh70cc9552019-01-21 19:46:48 -0800240 EXPECT_EQ(parameters[0], residual_block.parameter_blocks()[0]);
241 EXPECT_EQ(parameters[1], residual_block.parameter_blocks()[1]);
242 EXPECT_EQ(parameters[2], residual_block.parameter_blocks()[2]);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800243 EXPECT_EQ(3 * (2 + 4) + 3, residual_block.NumScratchDoublesForEvaluate());
Austin Schuh70cc9552019-01-21 19:46:48 -0800244
245 // Verify cost-only evaluation.
246 double cost;
Austin Schuh3de38b02024-06-25 18:25:10 -0700247 residual_block.Evaluate(true, &cost, nullptr, nullptr, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800248 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800249
250 // Verify cost and residual evaluation.
251 double residuals[3];
Austin Schuh3de38b02024-06-25 18:25:10 -0700252 residual_block.Evaluate(true, &cost, residuals, nullptr, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800253 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800254 EXPECT_EQ(0.0, residuals[0]);
255 EXPECT_EQ(1.0, residuals[1]);
256 EXPECT_EQ(2.0, residuals[2]);
257
258 // Verify cost, residual, and jacobian evaluation.
259 cost = 0.0;
260 VectorRef(residuals, 3).setConstant(0.0);
261
262 Matrix jacobian_rx(3, 1); // Since the first element is fixed.
263 Matrix jacobian_ry(3, 3);
264 Matrix jacobian_rz(3, 3); // Since the third element is fixed.
265
266 jacobian_rx.setConstant(-1.0);
267 jacobian_ry.setConstant(-1.0);
268 jacobian_rz.setConstant(-1.0);
269
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800270 double* jacobian_ptrs[3] = {
271 jacobian_rx.data(), jacobian_ry.data(), jacobian_rz.data()};
Austin Schuh70cc9552019-01-21 19:46:48 -0800272
273 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800274 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800275 EXPECT_EQ(0.0, residuals[0]);
276 EXPECT_EQ(1.0, residuals[1]);
277 EXPECT_EQ(2.0, residuals[2]);
278
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800279 // clang-format off
280
Austin Schuh70cc9552019-01-21 19:46:48 -0800281 Matrix expected_jacobian_rx(3, 1);
282 expected_jacobian_rx << 1.0, 1.0, 1.0;
283
284 Matrix expected_jacobian_ry(3, 3);
285 expected_jacobian_ry << 0.0, 1.0, 2.0,
286 0.0, 1.0, 2.0,
287 0.0, 1.0, 2.0;
288
289 Matrix expected_jacobian_rz(3, 3);
290 expected_jacobian_rz << 0.0, 1.0, /* 2.0, */ 3.0, // 3rd parameter constant.
291 0.0, 1.0, /* 2.0, */ 3.0,
292 0.0, 1.0, /* 2.0, */ 3.0;
293
294 EXPECT_EQ(expected_jacobian_rx, jacobian_rx)
295 << "\nExpected:\n" << expected_jacobian_rx
296 << "\nActual:\n" << jacobian_rx;
297 EXPECT_EQ(expected_jacobian_ry, jacobian_ry)
298 << "\nExpected:\n" << expected_jacobian_ry
299 << "\nActual:\n" << jacobian_ry;
300 EXPECT_EQ(expected_jacobian_rz, jacobian_rz)
301 << "\nExpected:\n " << expected_jacobian_rz
302 << "\nActual:\n" << jacobian_rz;
303
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800304 // clang-format on
305
Austin Schuh70cc9552019-01-21 19:46:48 -0800306 // Verify cost, residual, and partial jacobian evaluation.
307 cost = 0.0;
308 VectorRef(residuals, 3).setConstant(0.0);
309 jacobian_rx.setConstant(-1.0);
310 jacobian_ry.setConstant(-1.0);
311 jacobian_rz.setConstant(-1.0);
312
Austin Schuh3de38b02024-06-25 18:25:10 -0700313 jacobian_ptrs[1] = nullptr; // Don't compute the jacobian for y.
Austin Schuh70cc9552019-01-21 19:46:48 -0800314
315 residual_block.Evaluate(true, &cost, residuals, jacobian_ptrs, scratch);
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800316 EXPECT_EQ(0.5 * (0 * 0 + 1 * 1 + 2 * 2), cost);
Austin Schuh70cc9552019-01-21 19:46:48 -0800317 EXPECT_EQ(0.0, residuals[0]);
318 EXPECT_EQ(1.0, residuals[1]);
319 EXPECT_EQ(2.0, residuals[2]);
320
321 EXPECT_EQ(expected_jacobian_rx, jacobian_rx);
322 EXPECT_TRUE((jacobian_ry.array() == -1.0).all()) << "\n" << jacobian_ry;
323 EXPECT_EQ(expected_jacobian_rz, jacobian_rz);
324}
325
Austin Schuh3de38b02024-06-25 18:25:10 -0700326} // namespace ceres::internal