Fix a variadic evaluation bug in AutoDiff.
Thanks to Julien Michot for reporting.
Change-Id: I322cd177e85c18ba3fbee56149696d4140f79c68
diff --git a/include/ceres/internal/autodiff.h b/include/ceres/internal/autodiff.h
index 581e881..3497a90 100644
--- a/include/ceres/internal/autodiff.h
+++ b/include/ceres/internal/autodiff.h
@@ -390,7 +390,7 @@
x.get() + jet8,
x.get() + jet9,
};
- JetT *output = x.get() + jet6;
+ JetT* output = x.get() + jet9;
#define CERES_MAKE_1ST_ORDER_PERTURBATION(i) \
if (N ## i) { \
diff --git a/internal/ceres/autodiff_test.cc b/internal/ceres/autodiff_test.cc
index d0bf97a..1c56c22 100644
--- a/internal/ceres/autodiff_test.cc
+++ b/internal/ceres/autodiff_test.cc
@@ -377,6 +377,276 @@
}
}
+struct Residual1Param {
+ template <typename T>
+ bool operator()(const T* x0, T* y) const {
+ y[0] = *x0;
+ return true;
+ }
+};
+
+struct Residual2Param {
+ template <typename T>
+ bool operator()(const T* x0, const T* x1, T* y) const {
+ y[0] = *x0 + pow(*x1, 2);
+ return true;
+ }
+};
+
+struct Residual3Param {
+ template <typename T>
+ bool operator()(const T* x0, const T* x1, const T* x2, T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3);
+ return true;
+ }
+};
+
+struct Residual4Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4);
+ return true;
+ }
+};
+
+struct Residual5Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ const T* x4,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5);
+ return true;
+ }
+};
+
+struct Residual6Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ const T* x4,
+ const T* x5,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
+ pow(*x5, 6);
+ return true;
+ }
+};
+
+struct Residual7Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ const T* x4,
+ const T* x5,
+ const T* x6,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
+ pow(*x5, 6) + pow(*x6, 7);
+ return true;
+ }
+};
+
+struct Residual8Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ const T* x4,
+ const T* x5,
+ const T* x6,
+ const T* x7,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
+ pow(*x5, 6) + pow(*x6, 7) + pow(*x7, 8);
+ return true;
+ }
+};
+
+struct Residual9Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ const T* x4,
+ const T* x5,
+ const T* x6,
+ const T* x7,
+ const T* x8,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
+ pow(*x5, 6) + pow(*x6, 7) + pow(*x7, 8) + pow(*x8, 9);
+ return true;
+ }
+};
+
+struct Residual10Param {
+ template <typename T>
+ bool operator()(const T* x0,
+ const T* x1,
+ const T* x2,
+ const T* x3,
+ const T* x4,
+ const T* x5,
+ const T* x6,
+ const T* x7,
+ const T* x8,
+ const T* x9,
+ T* y) const {
+ y[0] = *x0 + pow(*x1, 2) + pow(*x2, 3) + pow(*x3, 4) + pow(*x4, 5) +
+ pow(*x5, 6) + pow(*x6, 7) + pow(*x7, 8) + pow(*x8, 9) + pow(*x9, 10);
+ return true;
+ }
+};
+
+TEST(AutoDiff, VariadicAutoDiff) {
+ double x[10];
+ double residual = 0;
+ double* parameters[10];
+ double jacobian_values[10];
+ double* jacobians[10];
+
+ for (int i = 0; i < 10; ++i) {
+ x[i] = 2.0;
+ parameters[i] = x + i;
+ jacobians[i] = jacobian_values + i;
+ }
+
+ {
+ Residual1Param functor;
+ int num_variables = 1;
+ EXPECT_TRUE((AutoDiff<Residual1Param, double, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual2Param functor;
+ int num_variables = 2;
+ EXPECT_TRUE((AutoDiff<Residual2Param, double, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual3Param functor;
+ int num_variables = 3;
+ EXPECT_TRUE((AutoDiff<Residual3Param, double, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual4Param functor;
+ int num_variables = 4;
+ EXPECT_TRUE((AutoDiff<Residual4Param, double, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual5Param functor;
+ int num_variables = 5;
+ EXPECT_TRUE((AutoDiff<Residual5Param, double, 1, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual6Param functor;
+ int num_variables = 6;
+ EXPECT_TRUE((AutoDiff<Residual6Param,
+ double,
+ 1, 1, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual7Param functor;
+ int num_variables = 7;
+ EXPECT_TRUE((AutoDiff<Residual7Param,
+ double,
+ 1, 1, 1, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual8Param functor;
+ int num_variables = 8;
+ EXPECT_TRUE((AutoDiff<
+ Residual8Param,
+ double, 1, 1, 1, 1, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual9Param functor;
+ int num_variables = 9;
+ EXPECT_TRUE((AutoDiff<
+ Residual9Param,
+ double,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+
+ {
+ Residual10Param functor;
+ int num_variables = 10;
+ EXPECT_TRUE((AutoDiff<
+ Residual10Param,
+ double,
+ 1, 1, 1, 1, 1, 1, 1, 1, 1, 1>::Differentiate(
+ functor, parameters, 1, &residual, jacobians)));
+ EXPECT_EQ(residual, pow(2, num_variables + 1) - 2);
+ for (int i = 0; i < num_variables; ++i) {
+ EXPECT_EQ(jacobian_values[i], (i + 1) * pow(2, i));
+ }
+ }
+}
+
// This is fragile test that triggers the alignment bug on
// i686-apple-darwin10-llvm-g++-4.2 (GCC) 4.2.1. It is quite possible,
// that other combinations of operating system + compiler will