Fix DynamicAutoDiffCostFunction
Changed DynamicAutoDiffCostFunction to handle multiple derivative
sections as opposed to just a single contiguous block.
In the previous implementation it was assumed that non-constant
parameters occur in a single contiguous block so that constant
parameters could NOT lie between non-constant parameters. Previously,
start_derivative_section was first set as soon as the first
non-constant parameter block (marked by jacobians[i] != NULL) was
encountered. After this, entries in input_jets[parameter_cursor].v were
accessed with `parameter_cursor - start_derivative_section`. For
contiguous non-constant parameter blocks this is fine, but if constant
parameter blocks fall between then this indexing is incorrect because
`parameter_cursor - start_derivative_section` can go out of bounds.
For a concrete example, take a cost function with three parameter
blocks, each of size 1 and with the center block fixed. Assume that
Stride=1 so that two passes are required. On the first pass
start_derivative_section=0, and the first variable block is handled
correctly. At the end of the first pass end_derivative_section=1, so
for the second pass start_derivative_section=1. Now comes the problem.
When parameter_cursor=1, parameter_cursor >= start_derivative_section
so jacobian[1] is checked to be NULL. Since it is NULL (second
parameter block is constant) then nothing is done and
active_parameter_count is NOT incremented. Next, when
parameter_cursor=2, parameter_cursor >= start_derivative_section and
jacobian[2] is checked. Since it is not NULL then
input_jets[parameter_cursor].v[parameter_cursor -
start_derivative_section] is set to 1.0, BUT parameter_cursor -
start_derivative_section = 2 - 1 = 1 which is out of bounds
(input_jets[parameter_cursor].v is only of size Stride=1).
The proposed solution records the start of each contiguous block of
non-constant parameters and indexing into
input_jets[parameter_cursor].v is independent of parameter_cursor.
Change-Id: I388ab6a0bafa35d317491135ec6fe980453ff888
diff --git a/include/ceres/dynamic_autodiff_cost_function.h b/include/ceres/dynamic_autodiff_cost_function.h
index 38bdb0a..5d8f188 100644
--- a/include/ceres/dynamic_autodiff_cost_function.h
+++ b/include/ceres/dynamic_autodiff_cost_function.h
@@ -126,17 +126,28 @@
vector<Jet<double, Stride>* > jet_parameters(num_parameter_blocks,
static_cast<Jet<double, Stride>* >(NULL));
int num_active_parameters = 0;
- int start_derivative_section = -1;
- for (int i = 0, parameter_cursor = 0; i < num_parameter_blocks; ++i) {
+
+ // To handle constant parameters between non-constant parameter blocks, the
+ // start position --- a raw parameter index --- of each contiguous block of
+ // non-constant parameters is recorded in start_derivative_section.
+ vector<int> start_derivative_section;
+ bool in_derivative_section = false;
+ int parameter_cursor = 0;
+
+ // Discover the derivative sections and set the parameter values.
+ for (int i = 0; i < num_parameter_blocks; ++i) {
jet_parameters[i] = &input_jets[parameter_cursor];
const int parameter_block_size = parameter_block_sizes()[i];
if (jacobians[i] != NULL) {
- start_derivative_section =
- (start_derivative_section == -1)
- ? parameter_cursor
- : start_derivative_section;
+ if (!in_derivative_section) {
+ start_derivative_section.push_back(parameter_cursor);
+ in_derivative_section = true;
+ }
+
num_active_parameters += parameter_block_size;
+ } else {
+ in_derivative_section = false;
}
for (int j = 0; j < parameter_block_size; ++j, parameter_cursor++) {
@@ -144,29 +155,54 @@
}
}
+ // When `num_active_parameters % Stride != 0` then it can be the case
+ // that `active_parameter_count < Stride` while parameter_cursor is less
+ // than the total number of parameters and with no remaining non-constant
+ // parameter blocks. Pushing parameter_cursor (the total number of
+ // parameters) as a final entry to start_derivative_section is required
+ // because if a constant parameter block is encountered after the
+ // last non-constant block then current_derivative_section is incremented
+ // and would otherwise index an invalid position in
+ // start_derivative_section. Setting the final element to the total number
+ // of parameters means that this can only happen at most once in the loop
+ // below.
+ start_derivative_section.push_back(parameter_cursor);
+
// Evaluate all of the strides. Each stride is a chunk of the derivative to
// evaluate, typically some size proportional to the size of the SIMD
// registers of the CPU.
int num_strides = static_cast<int>(ceil(num_active_parameters /
static_cast<float>(Stride)));
+ int current_derivative_section = 0;
+ int current_derivative_section_cursor = 0;
+
for (int pass = 0; pass < num_strides; ++pass) {
// Set most of the jet components to zero, except for
// non-constant #Stride parameters.
+ const int initial_derivative_section = current_derivative_section;
+ const int initial_derivative_section_cursor =
+ current_derivative_section_cursor;
+
int active_parameter_count = 0;
- int end_derivative_section = start_derivative_section;
- for (int i = 0, parameter_cursor = 0; i < num_parameter_blocks; ++i) {
+ parameter_cursor = 0;
+
+ for (int i = 0; i < num_parameter_blocks; ++i) {
for (int j = 0; j < parameter_block_sizes()[i];
++j, parameter_cursor++) {
input_jets[parameter_cursor].v.setZero();
- if (parameter_cursor >= start_derivative_section &&
- active_parameter_count < Stride) {
+ if (active_parameter_count < Stride &&
+ parameter_cursor >= (
+ start_derivative_section[current_derivative_section] +
+ current_derivative_section_cursor)) {
if (jacobians[i] != NULL) {
- input_jets[parameter_cursor]
- .v[parameter_cursor - start_derivative_section] = 1.0;
+ input_jets[parameter_cursor].v[active_parameter_count] = 1.0;
++active_parameter_count;
+ ++current_derivative_section_cursor;
+ } else {
+ ++current_derivative_section;
+ current_derivative_section_cursor = 0;
}
- ++end_derivative_section;
}
}
}
@@ -177,18 +213,27 @@
// Copy the pieces of the jacobians into their final place.
active_parameter_count = 0;
+
+ current_derivative_section = initial_derivative_section;
+ current_derivative_section_cursor = initial_derivative_section_cursor;
+
for (int i = 0, parameter_cursor = 0; i < num_parameter_blocks; ++i) {
for (int j = 0; j < parameter_block_sizes()[i];
++j, parameter_cursor++) {
- if (parameter_cursor >= start_derivative_section &&
- active_parameter_count < Stride) {
+ if (active_parameter_count < Stride &&
+ parameter_cursor >= (
+ start_derivative_section[current_derivative_section] +
+ current_derivative_section_cursor)) {
if (jacobians[i] != NULL) {
for (int k = 0; k < num_residuals(); ++k) {
jacobians[i][k * parameter_block_sizes()[i] + j] =
- output_jets[k].v[parameter_cursor -
- start_derivative_section];
+ output_jets[k].v[active_parameter_count];
}
++active_parameter_count;
+ ++current_derivative_section_cursor;
+ } else {
+ ++current_derivative_section;
+ current_derivative_section_cursor = 0;
}
}
}
@@ -201,8 +246,6 @@
residuals[k] = output_jets[k].a;
}
}
-
- start_derivative_section = end_derivative_section;
}
return true;
}
diff --git a/internal/ceres/dynamic_autodiff_cost_function_test.cc b/internal/ceres/dynamic_autodiff_cost_function_test.cc
index b62b56a..a42a374 100644
--- a/internal/ceres/dynamic_autodiff_cost_function_test.cc
+++ b/internal/ceres/dynamic_autodiff_cost_function_test.cc
@@ -31,6 +31,7 @@
// sameeragarwal@google.com (Sameer Agarwal)
#include "ceres/dynamic_autodiff_cost_function.h"
+#include "ceres/internal/scoped_ptr.h"
#include <cstddef>
@@ -270,5 +271,504 @@
}
}
+// Takes 3 parameter blocks:
+// parameters[0] (x) is size 1.
+// parameters[1] (y) is size 2.
+// parameters[2] (z) is size 3.
+// Emits 7 residuals:
+// A: x[0] (= sum_x)
+// B: y[0] + 2.0 * y[1] (= sum_y)
+// C: z[0] + 3.0 * z[1] + 6.0 * z[2] (= sum_z)
+// D: sum_x * sum_y
+// E: sum_y * sum_z
+// F: sum_x * sum_z
+// G: sum_x * sum_y * sum_z
+class MyThreeParameterCostFunctor {
+ public:
+ template <typename T>
+ bool operator()(T const* const* parameters, T* residuals) const {
+ const T* x = parameters[0];
+ const T* y = parameters[1];
+ const T* z = parameters[2];
+
+ T sum_x = x[0];
+ T sum_y = y[0] + 2.0 * y[1];
+ T sum_z = z[0] + 3.0 * z[1] + 6.0 * z[2];
+
+ residuals[0] = sum_x;
+ residuals[1] = sum_y;
+ residuals[2] = sum_z;
+ residuals[3] = sum_x * sum_y;
+ residuals[4] = sum_y * sum_z;
+ residuals[5] = sum_x * sum_z;
+ residuals[6] = sum_x * sum_y * sum_z;
+ return true;
+ }
+};
+
+class ThreeParameterCostFunctorTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Prepare the parameters.
+ x_.resize(1);
+ x_[0] = 0.0;
+
+ y_.resize(2);
+ y_[0] = 1.0;
+ y_[1] = 3.0;
+
+ z_.resize(3);
+ z_[0] = 2.0;
+ z_[1] = 4.0;
+ z_[2] = 6.0;
+
+ parameter_blocks_.resize(3);
+ parameter_blocks_[0] = &x_[0];
+ parameter_blocks_[1] = &y_[0];
+ parameter_blocks_[2] = &z_[0];
+
+ // Prepare the cost function.
+ typedef DynamicAutoDiffCostFunction<MyThreeParameterCostFunctor, 3>
+ DynamicMyThreeParameterCostFunction;
+ DynamicMyThreeParameterCostFunction * cost_function =
+ new DynamicMyThreeParameterCostFunction(
+ new MyThreeParameterCostFunctor());
+ cost_function->AddParameterBlock(1);
+ cost_function->AddParameterBlock(2);
+ cost_function->AddParameterBlock(3);
+ cost_function->SetNumResiduals(7);
+
+ cost_function_.reset(cost_function);
+
+ // Setup jacobian data.
+ jacobian_vect_.resize(3);
+ jacobian_vect_[0].resize(7 * x_.size(), -100000);
+ jacobian_vect_[1].resize(7 * y_.size(), -100000);
+ jacobian_vect_[2].resize(7 * z_.size(), -100000);
+
+ // Prepare the expected residuals.
+ const double sum_x = x_[0];
+ const double sum_y = y_[0] + 2.0 * y_[1];
+ const double sum_z = z_[0] + 3.0 * z_[1] + 6.0 * z_[2];
+
+ expected_residuals_.resize(7);
+ expected_residuals_[0] = sum_x;
+ expected_residuals_[1] = sum_y;
+ expected_residuals_[2] = sum_z;
+ expected_residuals_[3] = sum_x * sum_y;
+ expected_residuals_[4] = sum_y * sum_z;
+ expected_residuals_[5] = sum_x * sum_z;
+ expected_residuals_[6] = sum_x * sum_y * sum_z;
+
+ // Prepare the expected jacobian entries.
+ expected_jacobian_x_.resize(7);
+ expected_jacobian_x_[0] = 1.0;
+ expected_jacobian_x_[1] = 0.0;
+ expected_jacobian_x_[2] = 0.0;
+ expected_jacobian_x_[3] = sum_y;
+ expected_jacobian_x_[4] = 0.0;
+ expected_jacobian_x_[5] = sum_z;
+ expected_jacobian_x_[6] = sum_y * sum_z;
+
+ expected_jacobian_y_.resize(14);
+ expected_jacobian_y_[0] = 0.0;
+ expected_jacobian_y_[1] = 0.0;
+ expected_jacobian_y_[2] = 1.0;
+ expected_jacobian_y_[3] = 2.0;
+ expected_jacobian_y_[4] = 0.0;
+ expected_jacobian_y_[5] = 0.0;
+ expected_jacobian_y_[6] = sum_x;
+ expected_jacobian_y_[7] = 2.0 * sum_x;
+ expected_jacobian_y_[8] = sum_z;
+ expected_jacobian_y_[9] = 2.0 * sum_z;
+ expected_jacobian_y_[10] = 0.0;
+ expected_jacobian_y_[11] = 0.0;
+ expected_jacobian_y_[12] = sum_x * sum_z;
+ expected_jacobian_y_[13] = 2.0 * sum_x * sum_z;
+
+ expected_jacobian_z_.resize(21);
+ expected_jacobian_z_[0] = 0.0;
+ expected_jacobian_z_[1] = 0.0;
+ expected_jacobian_z_[2] = 0.0;
+ expected_jacobian_z_[3] = 0.0;
+ expected_jacobian_z_[4] = 0.0;
+ expected_jacobian_z_[5] = 0.0;
+ expected_jacobian_z_[6] = 1.0;
+ expected_jacobian_z_[7] = 3.0;
+ expected_jacobian_z_[8] = 6.0;
+ expected_jacobian_z_[9] = 0.0;
+ expected_jacobian_z_[10] = 0.0;
+ expected_jacobian_z_[11] = 0.0;
+ expected_jacobian_z_[12] = sum_y;
+ expected_jacobian_z_[13] = 3.0 * sum_y;
+ expected_jacobian_z_[14] = 6.0 * sum_y;
+ expected_jacobian_z_[15] = sum_x;
+ expected_jacobian_z_[16] = 3.0 * sum_x;
+ expected_jacobian_z_[17] = 6.0 * sum_x;
+ expected_jacobian_z_[18] = sum_x * sum_y;
+ expected_jacobian_z_[19] = 3.0 * sum_x * sum_y;
+ expected_jacobian_z_[20] = 6.0 * sum_x * sum_y;
+ }
+
+ protected:
+ vector<double> x_;
+ vector<double> y_;
+ vector<double> z_;
+
+ vector<double*> parameter_blocks_;
+
+ scoped_ptr<CostFunction> cost_function_;
+
+ vector<vector<double> > jacobian_vect_;
+
+ vector<double> expected_residuals_;
+
+ vector<double> expected_jacobian_x_;
+ vector<double> expected_jacobian_y_;
+ vector<double> expected_jacobian_z_;
+};
+
+TEST_F(ThreeParameterCostFunctorTest, TestThreeParameterResiduals) {
+ vector<double> residuals(7, -100000);
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ NULL));
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+}
+
+TEST_F(ThreeParameterCostFunctorTest, TestThreeParameterJacobian) {
+ vector<double> residuals(7, -100000);
+
+ vector<double*> jacobian;
+ jacobian.push_back(jacobian_vect_[0].data());
+ jacobian.push_back(jacobian_vect_[1].data());
+ jacobian.push_back(jacobian_vect_[2].data());
+
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ jacobian.data()));
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_jacobian_x_[i], jacobian[0][i]);
+ }
+
+ for (int i = 0; i < 14; ++i) {
+ EXPECT_EQ(expected_jacobian_y_[i], jacobian[1][i]);
+ }
+
+ for (int i = 0; i < 21; ++i) {
+ EXPECT_EQ(expected_jacobian_z_[i], jacobian[2][i]);
+ }
+}
+
+TEST_F(ThreeParameterCostFunctorTest,
+ ThreeParameterJacobianWithFirstAndLastParameterBlockConstant) {
+ vector<double> residuals(7, -100000);
+
+ vector<double*> jacobian;
+ jacobian.push_back(NULL);
+ jacobian.push_back(jacobian_vect_[1].data());
+ jacobian.push_back(NULL);
+
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ jacobian.data()));
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+
+ for (int i = 0; i < 14; ++i) {
+ EXPECT_EQ(expected_jacobian_y_[i], jacobian[1][i]);
+ }
+}
+
+TEST_F(ThreeParameterCostFunctorTest,
+ ThreeParameterJacobianWithSecondParameterBlockConstant) {
+ vector<double> residuals(7, -100000);
+
+ vector<double*> jacobian;
+ jacobian.push_back(jacobian_vect_[0].data());
+ jacobian.push_back(NULL);
+ jacobian.push_back(jacobian_vect_[2].data());
+
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ jacobian.data()));
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_jacobian_x_[i], jacobian[0][i]);
+ }
+
+ for (int i = 0; i < 21; ++i) {
+ EXPECT_EQ(expected_jacobian_z_[i], jacobian[2][i]);
+ }
+}
+
+// Takes 6 parameter blocks all of size 1:
+// x0, y0, y1, z0, z1, z2
+// Same 7 residuals as MyThreeParameterCostFunctor.
+// Naming convention for tests is (V)ariable and (C)onstant.
+class MySixParameterCostFunctor {
+ public:
+ template <typename T>
+ bool operator()(T const* const* parameters, T* residuals) const {
+ const T* x0 = parameters[0];
+ const T* y0 = parameters[1];
+ const T* y1 = parameters[2];
+ const T* z0 = parameters[3];
+ const T* z1 = parameters[4];
+ const T* z2 = parameters[5];
+
+ T sum_x = x0[0];
+ T sum_y = y0[0] + 2.0 * y1[0];
+ T sum_z = z0[0] + 3.0 * z1[0] + 6.0 * z2[0];
+
+ residuals[0] = sum_x;
+ residuals[1] = sum_y;
+ residuals[2] = sum_z;
+ residuals[3] = sum_x * sum_y;
+ residuals[4] = sum_y * sum_z;
+ residuals[5] = sum_x * sum_z;
+ residuals[6] = sum_x * sum_y * sum_z;
+ return true;
+ }
+};
+
+class SixParameterCostFunctorTest : public ::testing::Test {
+ protected:
+ virtual void SetUp() {
+ // Prepare the parameters.
+ x0_ = 0.0;
+ y0_ = 1.0;
+ y1_ = 3.0;
+ z0_ = 2.0;
+ z1_ = 4.0;
+ z2_ = 6.0;
+
+ parameter_blocks_.resize(6);
+ parameter_blocks_[0] = &x0_;
+ parameter_blocks_[1] = &y0_;
+ parameter_blocks_[2] = &y1_;
+ parameter_blocks_[3] = &z0_;
+ parameter_blocks_[4] = &z1_;
+ parameter_blocks_[5] = &z2_;
+
+ // Prepare the cost function.
+ typedef DynamicAutoDiffCostFunction<MySixParameterCostFunctor, 3>
+ DynamicMySixParameterCostFunction;
+ DynamicMySixParameterCostFunction * cost_function =
+ new DynamicMySixParameterCostFunction(
+ new MySixParameterCostFunctor());
+ for (int i = 0; i < 6; ++i) {
+ cost_function->AddParameterBlock(1);
+ }
+ cost_function->SetNumResiduals(7);
+
+ cost_function_.reset(cost_function);
+
+ // Setup jacobian data.
+ jacobian_vect_.resize(6);
+ for (int i = 0; i < 6; ++i) {
+ jacobian_vect_[i].resize(7, -100000);
+ }
+
+ // Prepare the expected residuals.
+ const double sum_x = x0_;
+ const double sum_y = y0_ + 2.0 * y1_;
+ const double sum_z = z0_ + 3.0 * z1_ + 6.0 * z2_;
+
+ expected_residuals_.resize(7);
+ expected_residuals_[0] = sum_x;
+ expected_residuals_[1] = sum_y;
+ expected_residuals_[2] = sum_z;
+ expected_residuals_[3] = sum_x * sum_y;
+ expected_residuals_[4] = sum_y * sum_z;
+ expected_residuals_[5] = sum_x * sum_z;
+ expected_residuals_[6] = sum_x * sum_y * sum_z;
+
+ // Prepare the expected jacobian entries.
+ expected_jacobians_.resize(6);
+ expected_jacobians_[0].resize(7);
+ expected_jacobians_[0][0] = 1.0;
+ expected_jacobians_[0][1] = 0.0;
+ expected_jacobians_[0][2] = 0.0;
+ expected_jacobians_[0][3] = sum_y;
+ expected_jacobians_[0][4] = 0.0;
+ expected_jacobians_[0][5] = sum_z;
+ expected_jacobians_[0][6] = sum_y * sum_z;
+
+ expected_jacobians_[1].resize(7);
+ expected_jacobians_[1][0] = 0.0;
+ expected_jacobians_[1][1] = 1.0;
+ expected_jacobians_[1][2] = 0.0;
+ expected_jacobians_[1][3] = sum_x;
+ expected_jacobians_[1][4] = sum_z;
+ expected_jacobians_[1][5] = 0.0;
+ expected_jacobians_[1][6] = sum_x * sum_z;
+
+ expected_jacobians_[2].resize(7);
+ expected_jacobians_[2][0] = 0.0;
+ expected_jacobians_[2][1] = 2.0;
+ expected_jacobians_[2][2] = 0.0;
+ expected_jacobians_[2][3] = 2.0 * sum_x;
+ expected_jacobians_[2][4] = 2.0 * sum_z;
+ expected_jacobians_[2][5] = 0.0;
+ expected_jacobians_[2][6] = 2.0 * sum_x * sum_z;
+
+ expected_jacobians_[3].resize(7);
+ expected_jacobians_[3][0] = 0.0;
+ expected_jacobians_[3][1] = 0.0;
+ expected_jacobians_[3][2] = 1.0;
+ expected_jacobians_[3][3] = 0.0;
+ expected_jacobians_[3][4] = sum_y;
+ expected_jacobians_[3][5] = sum_x;
+ expected_jacobians_[3][6] = sum_x * sum_y;
+
+ expected_jacobians_[4].resize(7);
+ expected_jacobians_[4][0] = 0.0;
+ expected_jacobians_[4][1] = 0.0;
+ expected_jacobians_[4][2] = 3.0;
+ expected_jacobians_[4][3] = 0.0;
+ expected_jacobians_[4][4] = 3.0 * sum_y;
+ expected_jacobians_[4][5] = 3.0 * sum_x;
+ expected_jacobians_[4][6] = 3.0 * sum_x * sum_y;
+
+ expected_jacobians_[5].resize(7);
+ expected_jacobians_[5][0] = 0.0;
+ expected_jacobians_[5][1] = 0.0;
+ expected_jacobians_[5][2] = 6.0;
+ expected_jacobians_[5][3] = 0.0;
+ expected_jacobians_[5][4] = 6.0 * sum_y;
+ expected_jacobians_[5][5] = 6.0 * sum_x;
+ expected_jacobians_[5][6] = 6.0 * sum_x * sum_y;
+ }
+
+ protected:
+ double x0_;
+ double y0_;
+ double y1_;
+ double z0_;
+ double z1_;
+ double z2_;
+
+ vector<double*> parameter_blocks_;
+
+ scoped_ptr<CostFunction> cost_function_;
+
+ vector<vector<double> > jacobian_vect_;
+
+ vector<double> expected_residuals_;
+ vector<vector<double> > expected_jacobians_;
+};
+
+TEST_F(SixParameterCostFunctorTest, TestSixParameterResiduals) {
+ vector<double> residuals(7, -100000);
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ NULL));
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+}
+
+TEST_F(SixParameterCostFunctorTest, TestSixParameterJacobian) {
+ vector<double> residuals(7, -100000);
+
+ vector<double*> jacobian;
+ jacobian.push_back(jacobian_vect_[0].data());
+ jacobian.push_back(jacobian_vect_[1].data());
+ jacobian.push_back(jacobian_vect_[2].data());
+ jacobian.push_back(jacobian_vect_[3].data());
+ jacobian.push_back(jacobian_vect_[4].data());
+ jacobian.push_back(jacobian_vect_[5].data());
+
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ jacobian.data()));
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ for (int j = 0; j < 7; ++j) {
+ EXPECT_EQ(expected_jacobians_[i][j], jacobian[i][j]);
+ }
+ }
+}
+
+TEST_F(SixParameterCostFunctorTest, TestSixParameterJacobianVVCVVC) {
+ vector<double> residuals(7, -100000);
+
+ vector<double*> jacobian;
+ jacobian.push_back(jacobian_vect_[0].data());
+ jacobian.push_back(jacobian_vect_[1].data());
+ jacobian.push_back(NULL);
+ jacobian.push_back(jacobian_vect_[3].data());
+ jacobian.push_back(jacobian_vect_[4].data());
+ jacobian.push_back(NULL);
+
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ jacobian.data()));
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ // Skip the constant variables.
+ if (i == 2 || i == 5) {
+ continue;
+ }
+
+ for (int j = 0; j < 7; ++j) {
+ EXPECT_EQ(expected_jacobians_[i][j], jacobian[i][j]);
+ }
+ }
+}
+
+TEST_F(SixParameterCostFunctorTest, TestSixParameterJacobianVCCVCV) {
+ vector<double> residuals(7, -100000);
+
+ vector<double*> jacobian;
+ jacobian.push_back(jacobian_vect_[0].data());
+ jacobian.push_back(NULL);
+ jacobian.push_back(NULL);
+ jacobian.push_back(jacobian_vect_[3].data());
+ jacobian.push_back(NULL);
+ jacobian.push_back(jacobian_vect_[5].data());
+
+ EXPECT_TRUE(cost_function_->Evaluate(parameter_blocks_.data(),
+ residuals.data(),
+ jacobian.data()));
+
+ for (int i = 0; i < 7; ++i) {
+ EXPECT_EQ(expected_residuals_[i], residuals[i]);
+ }
+
+ for (int i = 0; i < 6; ++i) {
+ // Skip the constant variables.
+ if (i == 1 || i == 2 || i == 4) {
+ continue;
+ }
+
+ for (int j = 0; j < 7; ++j) {
+ EXPECT_EQ(expected_jacobians_[i][j], jacobian[i][j]);
+ }
+ }
+}
+
} // namespace internal
} // namespace ceres