Support varying numbers of residuals in autodiff.
This commit modifies the only function in autodiff that takes a
templated number of outputs (i.e. residuals) and makes that
template parameter a normal parameter. With that change, it
is a trivial matter to support a dynamic number of residuals.
The API for dynamic residuals is to pass a fake number of
residuals as the second template argument to
AutoDiffCostFunction, and to pass the real number of
parameters as a second constructor argument.
diff --git a/internal/ceres/autodiff_test.cc b/internal/ceres/autodiff_test.cc
index e9f0c5e..86861ee 100644
--- a/internal/ceres/autodiff_test.cc
+++ b/internal/ceres/autodiff_test.cc
@@ -37,7 +37,7 @@
namespace internal {
template <typename T> inline
-T &RowMajor(T *base, int rows, int cols, int i, int j) {
+T &RowMajorAccess(T *base, int rows, int cols, int i, int j) {
return base[cols * i + j];
}
@@ -86,7 +86,7 @@
// Symmetric differencing:
// f'(a) = (f(a + h) - f(a - h)) / (2 h)
for (int i = 0; i < M; ++i) {
- RowMajor(jac, M, N, i, j) =
+ RowMajorAccess(jac, M, N, i, j) =
(fwd_fun[i] - bwd_fun[i]) / (T(2) * del);
}
@@ -116,7 +116,7 @@
A cc = c*c;
A cd = c*d;
A dd = d*d;
-#define R(i, j) RowMajor(R, 3, 3, (i), (j))
+#define R(i, j) RowMajorAccess(R, 3, 3, (i), (j))
R(0, 0) = aa+bb-cc-dd; R(0, 1) = A(2)*(bc-ad); R(0, 2) = A(2)*(ac+bd); // NOLINT
R(1, 0) = A(2)*(ad+bc); R(1, 1) = aa-bb+cc-dd; R(1, 2) = A(2)*(cd-ab); // NOLINT
R(2, 0) = A(2)*(bd-ac); R(2, 1) = A(2)*(ab+cd); R(2, 2) = aa-bb-cc+dd; // NOLINT
@@ -132,10 +132,10 @@
bool operator()(A const P[12], A const X[4], A x[2]) const {
A PX[3];
for (int i = 0; i < 3; ++i) {
- PX[i] = RowMajor(P, 3, 4, i, 0) * X[0] +
- RowMajor(P, 3, 4, i, 1) * X[1] +
- RowMajor(P, 3, 4, i, 2) * X[2] +
- RowMajor(P, 3, 4, i, 3) * X[3];
+ PX[i] = RowMajorAccess(P, 3, 4, i, 0) * X[0] +
+ RowMajorAccess(P, 3, 4, i, 1) * X[1] +
+ RowMajorAccess(P, 3, 4, i, 2) * X[2] +
+ RowMajorAccess(P, 3, 4, i, 3) * X[3];
}
if (PX[2] != 0.0) {
x[0] = PX[0] / PX[2];
@@ -194,8 +194,8 @@
{
double *parameters[] = { PX };
double *jacobians[] = { J_PX };
- ASSERT_TRUE((AutoDiff<Projective, double, 2, 12 + 4>::Differentiate(
- b, parameters, ad_x1, jacobians)));
+ ASSERT_TRUE((AutoDiff<Projective, double, 12 + 4>::Differentiate(
+ b, parameters, 2, ad_x1, jacobians)));
for (int i = 0; i < 2; ++i) {
ASSERT_NEAR(ad_x1[i], b_x[i], tol);
@@ -209,8 +209,8 @@
double J_X[2 * 4];
double *parameters[] = { P, X };
double *jacobians[] = { J_P, J_X };
- ASSERT_TRUE((AutoDiff<Projective, double, 2, 12, 4>::Differentiate(
- b, parameters, ad_x2, jacobians)));
+ ASSERT_TRUE((AutoDiff<Projective, double, 12, 4>::Differentiate(
+ b, parameters, 2, ad_x2, jacobians)));
for (int i = 0; i < 2; ++i) {
ASSERT_NEAR(ad_x2[i], b_x[i], tol);
@@ -252,16 +252,16 @@
// Set P(:, 1:3) = R
for (int i = 0; i < 3; ++i) {
for (int j = 0; j < 3; ++j) {
- RowMajor(P, 3, 4, i, j) = RowMajor(R, 3, 3, i, j);
+ RowMajorAccess(P, 3, 4, i, j) = RowMajorAccess(R, 3, 3, i, j);
}
}
// Set P(:, 4) = - R c
for (int i = 0; i < 3; ++i) {
- RowMajor(P, 3, 4, i, 3) =
- - (RowMajor(R, 3, 3, i, 0) * c[0] +
- RowMajor(R, 3, 3, i, 1) * c[1] +
- RowMajor(R, 3, 3, i, 2) * c[2]);
+ RowMajorAccess(P, 3, 4, i, 3) =
+ - (RowMajorAccess(R, 3, 3, i, 0) * c[0] +
+ RowMajorAccess(R, 3, 3, i, 1) * c[1] +
+ RowMajorAccess(R, 3, 3, i, 2) * c[2]);
}
A X1[4] = { X[0], X[1], X[2], A(1) };
@@ -316,8 +316,8 @@
double J_X[2 * 3];
double *parameters[] = { q, c, X };
double *jacobians[] = { J_q, J_c, J_X };
- ASSERT_TRUE((AutoDiff<Metric, double, 2, 4, 3, 3>::Differentiate(
- b, parameters, ad_x, jacobians)));
+ ASSERT_TRUE((AutoDiff<Metric, double, 4, 3, 3>::Differentiate(
+ b, parameters, 2, ad_x, jacobians)));
for (int i = 0; i < 2; ++i) {
ASSERT_NEAR(ad_x[i], b_x[i], tol);
@@ -337,5 +337,45 @@
}
}
+struct VaryingResidualFunctor {
+ template <typename T>
+ bool operator()(const T x[2], T* y) const {
+ for (int i = 0; i < num_residuals; ++i) {
+ y[i] = T(i) * x[0] * x[1] * x[1];
+ }
+ return true;
+ }
+
+ int num_residuals;
+};
+
+TEST(AutoDiff, VaryingNumberOfResidualsForOneCostFunctorType) {
+ double x[2] = { 1.0, 5.5 };
+ double *parameters[] = { x };
+ const int kMaxResiduals = 10;
+ double J_x[2 * kMaxResiduals];
+ double residuals[kMaxResiduals];
+ double *jacobians[] = { J_x };
+
+ // Use a single functor, but tweak it to produce different numbers of
+ // residuals.
+ VaryingResidualFunctor functor;
+
+ for (int num_residuals = 0; num_residuals < kMaxResiduals; ++num_residuals) {
+ // Tweak the number of residuals to produce.
+ functor.num_residuals = num_residuals;
+
+ // Run autodiff with the new number of residuals.
+ ASSERT_TRUE((AutoDiff<VaryingResidualFunctor, double, 2>::Differentiate(
+ functor, parameters, num_residuals, residuals, jacobians)));
+
+ const double kTolerance = 1e-14;
+ for (int i = 0; i < num_residuals; ++i) {
+ EXPECT_NEAR(J_x[2 * i + 0], i * x[1] * x[1], kTolerance) << "i: " << i;
+ EXPECT_NEAR(J_x[2 * i + 1], 2 * i * x[0] * x[1], kTolerance) << "i: " << i;
+ }
+ }
+}
+
} // namespace internal
} // namespace ceres