blob: b50327cb13838d16b7664752a4569622243b62ca [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/internal/autodiff.h"
32
Austin Schuh3de38b02024-06-25 18:25:10 -070033#include <algorithm>
34#include <iterator>
35#include <random>
36
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080037#include "gtest/gtest.h"
Austin Schuh70cc9552019-01-21 19:46:48 -080038
Austin Schuh3de38b02024-06-25 18:25:10 -070039namespace ceres::internal {
Austin Schuh70cc9552019-01-21 19:46:48 -080040
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080041template <typename T>
42inline T& RowMajorAccess(T* base, int rows, int cols, int i, int j) {
Austin Schuh70cc9552019-01-21 19:46:48 -080043 return base[cols * i + j];
44}
45
46// Do (symmetric) finite differencing using the given function object 'b' of
47// type 'B' and scalar type 'T' with step size 'del'.
48//
49// The type B should have a signature
50//
51// bool operator()(T const *, T *) const;
52//
53// which maps a vector of parameters to a vector of outputs.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -080054template <typename B, typename T, int M, int N>
55inline bool SymmetricDiff(const B& b,
56 const T par[N],
57 T del, // step size.
58 T fun[M],
59 T jac[M * N]) { // row-major.
Austin Schuh70cc9552019-01-21 19:46:48 -080060 if (!b(par, fun)) {
61 return false;
62 }
63
64 // Temporary parameter vector.
65 T tmp_par[N];
66 for (int j = 0; j < N; ++j) {
67 tmp_par[j] = par[j];
68 }
69
70 // For each dimension, we do one forward step and one backward step in
71 // parameter space, and store the output vector vectors in these vectors.
72 T fwd_fun[M];
73 T bwd_fun[M];
74
75 for (int j = 0; j < N; ++j) {
76 // Forward step.
77 tmp_par[j] = par[j] + del;
78 if (!b(tmp_par, fwd_fun)) {
79 return false;
80 }
81
82 // Backward step.
83 tmp_par[j] = par[j] - del;
84 if (!b(tmp_par, bwd_fun)) {
85 return false;
86 }
87
88 // Symmetric differencing:
89 // f'(a) = (f(a + h) - f(a - h)) / (2 h)
90 for (int i = 0; i < M; ++i) {
91 RowMajorAccess(jac, M, N, i, j) =
92 (fwd_fun[i] - bwd_fun[i]) / (T(2) * del);
93 }
94
95 // Restore our temporary vector.
96 tmp_par[j] = par[j];
97 }
98
99 return true;
100}
101
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800102template <typename A>
103inline void QuaternionToScaledRotation(A const q[4], A R[3 * 3]) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800104 // Make convenient names for elements of q.
105 A a = q[0];
106 A b = q[1];
107 A c = q[2];
108 A d = q[3];
109 // This is not to eliminate common sub-expression, but to
110 // make the lines shorter so that they fit in 80 columns!
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800111 A aa = a * a;
112 A ab = a * b;
113 A ac = a * c;
114 A ad = a * d;
115 A bb = b * b;
116 A bc = b * c;
117 A bd = b * d;
118 A cc = c * c;
119 A cd = c * d;
120 A dd = d * d;
Austin Schuh70cc9552019-01-21 19:46:48 -0800121#define R(i, j) RowMajorAccess(R, 3, 3, (i), (j))
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800122 R(0, 0) = aa + bb - cc - dd;
123 R(0, 1) = A(2) * (bc - ad);
124 R(0, 2) = A(2) * (ac + bd); // NOLINT
125 R(1, 0) = A(2) * (ad + bc);
126 R(1, 1) = aa - bb + cc - dd;
127 R(1, 2) = A(2) * (cd - ab); // NOLINT
128 R(2, 0) = A(2) * (bd - ac);
129 R(2, 1) = A(2) * (ab + cd);
130 R(2, 2) = aa - bb - cc + dd; // NOLINT
Austin Schuh70cc9552019-01-21 19:46:48 -0800131#undef R
132}
133
134// A structure for projecting a 3x4 camera matrix and a
135// homogeneous 3D point, to a 2D inhomogeneous point.
136struct Projective {
137 // Function that takes P and X as separate vectors:
138 // P, X -> x
139 template <typename A>
140 bool operator()(A const P[12], A const X[4], A x[2]) const {
141 A PX[3];
142 for (int i = 0; i < 3; ++i) {
143 PX[i] = RowMajorAccess(P, 3, 4, i, 0) * X[0] +
144 RowMajorAccess(P, 3, 4, i, 1) * X[1] +
145 RowMajorAccess(P, 3, 4, i, 2) * X[2] +
146 RowMajorAccess(P, 3, 4, i, 3) * X[3];
147 }
148 if (PX[2] != 0.0) {
149 x[0] = PX[0] / PX[2];
150 x[1] = PX[1] / PX[2];
151 return true;
152 }
153 return false;
154 }
155
156 // Version that takes P and X packed in one vector:
157 //
158 // (P, X) -> x
159 //
160 template <typename A>
161 bool operator()(A const P_X[12 + 4], A x[2]) const {
162 return operator()(P_X + 0, P_X + 12, x);
163 }
164};
165
166// Test projective camera model projector.
167TEST(AutoDiff, ProjectiveCameraModel) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800168 double const tol = 1e-10; // floating-point tolerance.
169 double const del = 1e-4; // finite-difference step.
170 double const err = 1e-6; // finite-difference tolerance.
171
172 Projective b;
Austin Schuh3de38b02024-06-25 18:25:10 -0700173 std::mt19937 prng;
174 std::uniform_real_distribution<double> uniform01(0.0, 1.0);
Austin Schuh70cc9552019-01-21 19:46:48 -0800175
176 // Make random P and X, in a single vector.
177 double PX[12 + 4];
Austin Schuh3de38b02024-06-25 18:25:10 -0700178 std::generate(std::begin(PX), std::end(PX), [&prng, &uniform01] {
179 return uniform01(prng);
180 });
Austin Schuh70cc9552019-01-21 19:46:48 -0800181
182 // Handy names for the P and X parts.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800183 double* P = PX + 0;
184 double* X = PX + 12;
Austin Schuh70cc9552019-01-21 19:46:48 -0800185
186 // Apply the mapping, to get image point b_x.
187 double b_x[2];
188 b(P, X, b_x);
189
190 // Use finite differencing to estimate the Jacobian.
191 double fd_x[2];
192 double fd_J[2 * (12 + 4)];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800193 ASSERT_TRUE(
194 (SymmetricDiff<Projective, double, 2, 12 + 4>(b, PX, del, fd_x, fd_J)));
Austin Schuh70cc9552019-01-21 19:46:48 -0800195
196 for (int i = 0; i < 2; ++i) {
197 ASSERT_NEAR(fd_x[i], b_x[i], tol);
198 }
199
200 // Use automatic differentiation to compute the Jacobian.
201 double ad_x1[2];
202 double J_PX[2 * (12 + 4)];
203 {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800204 double* parameters[] = {PX};
205 double* jacobians[] = {J_PX};
206 ASSERT_TRUE((AutoDifferentiate<2, StaticParameterDims<12 + 4>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800207 b, parameters, 2, ad_x1, jacobians)));
208
209 for (int i = 0; i < 2; ++i) {
210 ASSERT_NEAR(ad_x1[i], b_x[i], tol);
211 }
212 }
213
214 // Use automatic differentiation (again), with two arguments.
215 {
216 double ad_x2[2];
217 double J_P[2 * 12];
218 double J_X[2 * 4];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800219 double* parameters[] = {P, X};
220 double* jacobians[] = {J_P, J_X};
221 ASSERT_TRUE((AutoDifferentiate<2, StaticParameterDims<12, 4>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800222 b, parameters, 2, ad_x2, jacobians)));
223
224 for (int i = 0; i < 2; ++i) {
225 ASSERT_NEAR(ad_x2[i], b_x[i], tol);
226 }
227
228 // Now compare the jacobians we got.
229 for (int i = 0; i < 2; ++i) {
230 for (int j = 0; j < 12 + 4; ++j) {
231 ASSERT_NEAR(J_PX[(12 + 4) * i + j], fd_J[(12 + 4) * i + j], err);
232 }
233
234 for (int j = 0; j < 12; ++j) {
235 ASSERT_NEAR(J_PX[(12 + 4) * i + j], J_P[12 * i + j], tol);
236 }
237 for (int j = 0; j < 4; ++j) {
238 ASSERT_NEAR(J_PX[(12 + 4) * i + 12 + j], J_X[4 * i + j], tol);
239 }
240 }
241 }
242}
243
244// Object to implement the projection by a calibrated camera.
245struct Metric {
246 // The mapping is
247 //
248 // q, c, X -> x = dehomg(R(q) (X - c))
249 //
250 // where q is a quaternion and c is the center of projection.
251 //
252 // This function takes three input vectors.
253 template <typename A>
254 bool operator()(A const q[4], A const c[3], A const X[3], A x[2]) const {
255 A R[3 * 3];
256 QuaternionToScaledRotation(q, R);
257
258 // Convert the quaternion mapping all the way to projective matrix.
259 A P[3 * 4];
260
261 // Set P(:, 1:3) = R
262 for (int i = 0; i < 3; ++i) {
263 for (int j = 0; j < 3; ++j) {
264 RowMajorAccess(P, 3, 4, i, j) = RowMajorAccess(R, 3, 3, i, j);
265 }
266 }
267
268 // Set P(:, 4) = - R c
269 for (int i = 0; i < 3; ++i) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800270 RowMajorAccess(P, 3, 4, i, 3) = -(RowMajorAccess(R, 3, 3, i, 0) * c[0] +
271 RowMajorAccess(R, 3, 3, i, 1) * c[1] +
272 RowMajorAccess(R, 3, 3, i, 2) * c[2]);
Austin Schuh70cc9552019-01-21 19:46:48 -0800273 }
274
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800275 A X1[4] = {X[0], X[1], X[2], A(1)};
Austin Schuh70cc9552019-01-21 19:46:48 -0800276 Projective p;
277 return p(P, X1, x);
278 }
279
280 // A version that takes a single vector.
281 template <typename A>
282 bool operator()(A const q_c_X[4 + 3 + 3], A x[2]) const {
283 return operator()(q_c_X, q_c_X + 4, q_c_X + 4 + 3, x);
284 }
285};
286
287// This test is similar in structure to the previous one.
288TEST(AutoDiff, Metric) {
Austin Schuh70cc9552019-01-21 19:46:48 -0800289 double const tol = 1e-10; // floating-point tolerance.
290 double const del = 1e-4; // finite-difference step.
Austin Schuh3de38b02024-06-25 18:25:10 -0700291 double const err = 2e-5; // finite-difference tolerance.
Austin Schuh70cc9552019-01-21 19:46:48 -0800292
293 Metric b;
294
295 // Make random parameter vector.
296 double qcX[4 + 3 + 3];
Austin Schuh3de38b02024-06-25 18:25:10 -0700297 std::mt19937 prng;
298 std::uniform_real_distribution<double> uniform01(0.0, 1.0);
299
300 std::generate(std::begin(qcX), std::end(qcX), [&prng, &uniform01] {
301 return uniform01(prng);
302 });
Austin Schuh70cc9552019-01-21 19:46:48 -0800303
304 // Handy names.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800305 double* q = qcX;
306 double* c = qcX + 4;
307 double* X = qcX + 4 + 3;
Austin Schuh70cc9552019-01-21 19:46:48 -0800308
309 // Compute projection, b_x.
310 double b_x[2];
311 ASSERT_TRUE(b(q, c, X, b_x));
312
313 // Finite differencing estimate of Jacobian.
314 double fd_x[2];
315 double fd_J[2 * (4 + 3 + 3)];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800316 ASSERT_TRUE(
317 (SymmetricDiff<Metric, double, 2, 4 + 3 + 3>(b, qcX, del, fd_x, fd_J)));
Austin Schuh70cc9552019-01-21 19:46:48 -0800318
319 for (int i = 0; i < 2; ++i) {
320 ASSERT_NEAR(fd_x[i], b_x[i], tol);
321 }
322
323 // Automatic differentiation.
324 double ad_x[2];
325 double J_q[2 * 4];
326 double J_c[2 * 3];
327 double J_X[2 * 3];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800328 double* parameters[] = {q, c, X};
329 double* jacobians[] = {J_q, J_c, J_X};
330 ASSERT_TRUE((AutoDifferentiate<2, StaticParameterDims<4, 3, 3>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800331 b, parameters, 2, ad_x, jacobians)));
332
333 for (int i = 0; i < 2; ++i) {
334 ASSERT_NEAR(ad_x[i], b_x[i], tol);
335 }
336
337 // Compare the pieces.
338 for (int i = 0; i < 2; ++i) {
339 for (int j = 0; j < 4; ++j) {
340 ASSERT_NEAR(J_q[4 * i + j], fd_J[(4 + 3 + 3) * i + j], err);
341 }
342 for (int j = 0; j < 3; ++j) {
343 ASSERT_NEAR(J_c[3 * i + j], fd_J[(4 + 3 + 3) * i + j + 4], err);
344 }
345 for (int j = 0; j < 3; ++j) {
346 ASSERT_NEAR(J_X[3 * i + j], fd_J[(4 + 3 + 3) * i + j + 4 + 3], err);
347 }
348 }
349}
350
351struct VaryingResidualFunctor {
352 template <typename T>
353 bool operator()(const T x[2], T* y) const {
354 for (int i = 0; i < num_residuals; ++i) {
355 y[i] = T(i) * x[0] * x[1] * x[1];
356 }
357 return true;
358 }
359
360 int num_residuals;
361};
362
363TEST(AutoDiff, VaryingNumberOfResidualsForOneCostFunctorType) {
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800364 double x[2] = {1.0, 5.5};
365 double* parameters[] = {x};
Austin Schuh70cc9552019-01-21 19:46:48 -0800366 const int kMaxResiduals = 10;
367 double J_x[2 * kMaxResiduals];
368 double residuals[kMaxResiduals];
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800369 double* jacobians[] = {J_x};
Austin Schuh70cc9552019-01-21 19:46:48 -0800370
371 // Use a single functor, but tweak it to produce different numbers of
372 // residuals.
373 VaryingResidualFunctor functor;
374
375 for (int num_residuals = 1; num_residuals < kMaxResiduals; ++num_residuals) {
376 // Tweak the number of residuals to produce.
377 functor.num_residuals = num_residuals;
378
379 // Run autodiff with the new number of residuals.
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800380 ASSERT_TRUE((AutoDifferentiate<DYNAMIC, StaticParameterDims<2>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800381 functor, parameters, num_residuals, residuals, jacobians)));
382
383 const double kTolerance = 1e-14;
384 for (int i = 0; i < num_residuals; ++i) {
385 EXPECT_NEAR(J_x[2 * i + 0], i * x[1] * x[1], kTolerance) << "i: " << i;
386 EXPECT_NEAR(J_x[2 * i + 1], 2 * i * x[0] * x[1], kTolerance)
387 << "i: " << i;
388 }
389 }
390}
391
392struct Residual1Param {
393 template <typename T>
394 bool operator()(const T* x0, T* y) const {
395 y[0] = *x0;
396 return true;
397 }
398};
399
400struct Residual2Param {
401 template <typename T>
402 bool operator()(const T* x0, const T* x1, T* y) const {
403 y[0] = *x0 + pow(*x1, 2);
404 return true;
405 }
406};
407
408struct Residual3Param {
409 template <typename T>
410 bool operator()(const T* x0, const T* x1, const T* x2, T* y) const {
411 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3);
412 return true;
413 }
414};
415
416struct Residual4Param {
417 template <typename T>
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800418 bool operator()(
419 const T* x0, const T* x1, const T* x2, const T* x3, T* y) const {
Austin Schuh70cc9552019-01-21 19:46:48 -0800420 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4);
421 return true;
422 }
423};
424
425struct Residual5Param {
426 template <typename T>
427 bool operator()(const T* x0,
428 const T* x1,
429 const T* x2,
430 const T* x3,
431 const T* x4,
432 T* y) const {
433 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5);
434 return true;
435 }
436};
437
438struct Residual6Param {
439 template <typename T>
440 bool operator()(const T* x0,
441 const T* x1,
442 const T* x2,
443 const T* x3,
444 const T* x4,
445 const T* x5,
446 T* y) const {
447 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800448 pow(*x5, 6);
Austin Schuh70cc9552019-01-21 19:46:48 -0800449 return true;
450 }
451};
452
453struct Residual7Param {
454 template <typename T>
455 bool operator()(const T* x0,
456 const T* x1,
457 const T* x2,
458 const T* x3,
459 const T* x4,
460 const T* x5,
461 const T* x6,
462 T* y) const {
463 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800464 pow(*x5, 6) + pow(*x6, 7);
Austin Schuh70cc9552019-01-21 19:46:48 -0800465 return true;
466 }
467};
468
469struct Residual8Param {
470 template <typename T>
471 bool operator()(const T* x0,
472 const T* x1,
473 const T* x2,
474 const T* x3,
475 const T* x4,
476 const T* x5,
477 const T* x6,
478 const T* x7,
479 T* y) const {
480 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800481 pow(*x5, 6) + pow(*x6, 7) + pow(*x7, 8);
Austin Schuh70cc9552019-01-21 19:46:48 -0800482 return true;
483 }
484};
485
486struct Residual9Param {
487 template <typename T>
488 bool operator()(const T* x0,
489 const T* x1,
490 const T* x2,
491 const T* x3,
492 const T* x4,
493 const T* x5,
494 const T* x6,
495 const T* x7,
496 const T* x8,
497 T* y) const {
498 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800499 pow(*x5, 6) + pow(*x6, 7) + pow(*x7, 8) + pow(*x8, 9);
Austin Schuh70cc9552019-01-21 19:46:48 -0800500 return true;
501 }
502};
503
504struct Residual10Param {
505 template <typename T>
506 bool operator()(const T* x0,
507 const T* x1,
508 const T* x2,
509 const T* x3,
510 const T* x4,
511 const T* x5,
512 const T* x6,
513 const T* x7,
514 const T* x8,
515 const T* x9,
516 T* y) const {
517 y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800518 pow(*x5, 6) + pow(*x6, 7) + pow(*x7, 8) + pow(*x8, 9) + pow(*x9, 10);
Austin Schuh70cc9552019-01-21 19:46:48 -0800519 return true;
520 }
521};
522
523TEST(AutoDiff, VariadicAutoDiff) {
524 double x[10];
525 double residual = 0;
526 double* parameters[10];
527 double jacobian_values[10];
528 double* jacobians[10];
529
530 for (int i = 0; i < 10; ++i) {
531 x[i] = 2.0;
532 parameters[i] = x + i;
533 jacobians[i] = jacobian_values + i;
534 }
535
536 {
537 Residual1Param functor;
538 int num_variables = 1;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800539 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800540 functor, parameters, 1, &residual, jacobians)));
541 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
542 for (int i = 0; i < num_variables; ++i) {
543 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
544 }
545 }
546
547 {
548 Residual2Param functor;
549 int num_variables = 2;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800550 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800551 functor, parameters, 1, &residual, jacobians)));
552 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
553 for (int i = 0; i < num_variables; ++i) {
554 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
555 }
556 }
557
558 {
559 Residual3Param functor;
560 int num_variables = 3;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800561 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800562 functor, parameters, 1, &residual, jacobians)));
563 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
564 for (int i = 0; i < num_variables; ++i) {
565 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
566 }
567 }
568
569 {
570 Residual4Param functor;
571 int num_variables = 4;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800572 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800573 functor, parameters, 1, &residual, jacobians)));
574 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
575 for (int i = 0; i < num_variables; ++i) {
576 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
577 }
578 }
579
580 {
581 Residual5Param functor;
582 int num_variables = 5;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800583 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800584 functor, parameters, 1, &residual, jacobians)));
585 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
586 for (int i = 0; i < num_variables; ++i) {
587 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
588 }
589 }
590
591 {
592 Residual6Param functor;
593 int num_variables = 6;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800594 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800595 functor, parameters, 1, &residual, jacobians)));
596 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
597 for (int i = 0; i < num_variables; ++i) {
598 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
599 }
600 }
601
602 {
603 Residual7Param functor;
604 int num_variables = 7;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800605 EXPECT_TRUE((AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1, 1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800606 functor, parameters, 1, &residual, jacobians)));
607 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
608 for (int i = 0; i < num_variables; ++i) {
609 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
610 }
611 }
612
613 {
614 Residual8Param functor;
615 int num_variables = 8;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800616 EXPECT_TRUE(
617 (AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1, 1, 1, 1, 1>>(
618 functor, parameters, 1, &residual, jacobians)));
Austin Schuh70cc9552019-01-21 19:46:48 -0800619 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
620 for (int i = 0; i < num_variables; ++i) {
621 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
622 }
623 }
624
625 {
626 Residual9Param functor;
627 int num_variables = 9;
628 EXPECT_TRUE(
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800629 (AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1, 1, 1, 1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800630 functor, parameters, 1, &residual, jacobians)));
631 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
632 for (int i = 0; i < num_variables; ++i) {
633 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
634 }
635 }
636
637 {
638 Residual10Param functor;
639 int num_variables = 10;
Austin Schuh1d1e6ea2020-12-23 21:56:30 -0800640 EXPECT_TRUE((
641 AutoDifferentiate<1, StaticParameterDims<1, 1, 1, 1, 1, 1, 1, 1, 1, 1>>(
Austin Schuh70cc9552019-01-21 19:46:48 -0800642 functor, parameters, 1, &residual, jacobians)));
643 EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
644 for (int i = 0; i < num_variables; ++i) {
645 EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
646 }
647 }
648}
649
650// This is fragile test that triggers the alignment bug on
651// i686-apple-darwin10-llvm-g++-4.2 (GCC) 4.2.1. It is quite possible,
652// that other combinations of operating system + compiler will
653// re-arrange the operations in this test.
654//
655// But this is the best (and only) way we know of to trigger this
656// problem for now. A more robust solution that guarantees the
657// alignment of Eigen types used for automatic differentiation would
658// be nice.
659TEST(AutoDiff, AlignedAllocationTest) {
660 // This int is needed to allocate 16 bits on the stack, so that the
661 // next allocation is not aligned by default.
662 char y = 0;
663
664 // This is needed to prevent the compiler from optimizing y out of
665 // this function.
666 y += 1;
667
Austin Schuh3de38b02024-06-25 18:25:10 -0700668 using JetT = Jet<double, 2>;
Austin Schuh70cc9552019-01-21 19:46:48 -0800669 FixedArray<JetT, (256 * 7) / sizeof(JetT)> x(3);
670
671 // Need this to makes sure that x does not get optimized out.
672 x[0] = x[0] + JetT(1.0);
673}
674
Austin Schuh3de38b02024-06-25 18:25:10 -0700675} // namespace ceres::internal