Fix handling of constant blocks when reordering
The reordering code assumed that the parameter_block->index()
field is always set; this is not true. For fixed blocks the index
may have an arbitrary value. This changes the code to ignore fixed
blocks properly.
diff --git a/internal/ceres/solver_impl.cc b/internal/ceres/solver_impl.cc
index bb59b5f..834ec0d 100644
--- a/internal/ceres/solver_impl.cc
+++ b/internal/ceres/solver_impl.cc
@@ -582,10 +582,13 @@
int min_parameter_block_position = num_eliminate_blocks;
for (int i = 0; i < residual_block->NumParameterBlocks(); ++i) {
ParameterBlock* parameter_block = residual_block->parameter_blocks()[i];
- DCHECK_NE(parameter_block->index(), -1)
- << "Did you forget to call Program::SetParameterOffsetsAndIndex()?";
- min_parameter_block_position = std::min(parameter_block->index(),
- min_parameter_block_position);
+ if (!parameter_block->IsConstant()) {
+ CHECK_NE(parameter_block->index(), -1)
+ << "Did you forget to call Program::SetParameterOffsetsAndIndex()? "
+ << "This is a Ceres bug; please contact the developers!";
+ min_parameter_block_position = std::min(parameter_block->index(),
+ min_parameter_block_position);
+ }
}
return min_parameter_block_position;
}
diff --git a/internal/ceres/solver_impl_test.cc b/internal/ceres/solver_impl_test.cc
index 99733a2..2abca63 100644
--- a/internal/ceres/solver_impl_test.cc
+++ b/internal/ceres/solver_impl_test.cc
@@ -209,6 +209,7 @@
EXPECT_TRUE(SolverImpl::MaybeReorderResidualBlocks(options,
problem.mutable_program(),
&error));
+ EXPECT_EQ(current_residual_blocks.size(), residual_blocks.size());
for (int i = 0; i < current_residual_blocks.size(); ++i) {
EXPECT_EQ(current_residual_blocks[i], residual_blocks[i]);
}
@@ -281,11 +282,80 @@
EXPECT_TRUE(SolverImpl::MaybeReorderResidualBlocks(options,
problem.mutable_program(),
&error));
+ EXPECT_EQ(residual_blocks.size(), expected_residual_blocks.size());
for (int i = 0; i < expected_residual_blocks.size(); ++i) {
EXPECT_EQ(residual_blocks[i], expected_residual_blocks[i]);
}
}
+TEST(SolverImpl, ReorderResidualBlockNormalFunctionWithFixedBlocks) {
+ ProblemImpl problem;
+ double x;
+ double y;
+ double z;
+
+ problem.AddParameterBlock(&x, 1);
+ problem.AddParameterBlock(&y, 1);
+ problem.AddParameterBlock(&z, 1);
+
+ // Set one parameter block constant.
+ problem.SetParameterBlockConstant(&z);
+
+ // Mark residuals for x's row block with "x" for readability.
+ problem.AddResidualBlock(new UnaryCostFunction(), NULL, &x); // 0 x
+ problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &x); // 1 x
+ problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 2
+ problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 3
+ problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z); // 4 x
+ problem.AddResidualBlock(new BinaryCostFunction(), NULL, &z, &y); // 5
+ problem.AddResidualBlock(new BinaryCostFunction(), NULL, &x, &z); // 6 x
+ problem.AddResidualBlock(new UnaryCostFunction(), NULL, &y); // 7
+
+ Solver::Options options;
+ options.linear_solver_type = DENSE_SCHUR;
+ options.num_eliminate_blocks = 2;
+
+ // Create the reduced program. This should remove the fixed block "z",
+ // marking the index to -1 at the same time. x and y also get indices.
+ string error;
+ scoped_ptr<Program> reduced_program(
+ SolverImpl::CreateReducedProgram(&options, &problem, &error));
+
+ const vector<ResidualBlock*>& residual_blocks =
+ problem.program().residual_blocks();
+
+ // This is a bit fragile, but it serves the purpose. We know the
+ // bucketing algorithm that the reordering function uses, so we
+ // expect the order for residual blocks for each e_block to be
+ // filled in reverse.
+
+ vector<ResidualBlock*> expected_residual_blocks;
+
+ // Row block for residuals involving "x". These are marked "x" in the block
+ // of code calling AddResidual() above.
+ expected_residual_blocks.push_back(residual_blocks[6]);
+ expected_residual_blocks.push_back(residual_blocks[4]);
+ expected_residual_blocks.push_back(residual_blocks[1]);
+ expected_residual_blocks.push_back(residual_blocks[0]);
+
+ // Row block for residuals involving "y".
+ expected_residual_blocks.push_back(residual_blocks[7]);
+ expected_residual_blocks.push_back(residual_blocks[5]);
+ expected_residual_blocks.push_back(residual_blocks[3]);
+ expected_residual_blocks.push_back(residual_blocks[2]);
+
+ EXPECT_TRUE(SolverImpl::MaybeReorderResidualBlocks(options,
+ reduced_program.get(),
+ &error));
+
+ EXPECT_EQ(reduced_program->residual_blocks().size(),
+ expected_residual_blocks.size());
+ for (int i = 0; i < expected_residual_blocks.size(); ++i) {
+ EXPECT_EQ(reduced_program->residual_blocks()[i],
+ expected_residual_blocks[i]);
+ }
+}
+
TEST(SolverImpl, ApplyUserOrderingOrderingTooSmall) {
ProblemImpl problem;
double x;