NULL-jacobians are handled correctly in generated autodiff code The autodiff code generator is extended in the following ways: - Evaluate the complete jacobian into a temporary array - Copy the temporary jacobian to the result if != NULL Change-Id: If5ed0bff3b31e93a2d7d5dc718862cb648dba474
diff --git a/include/ceres/codegen/autodiff.h b/include/ceres/codegen/autodiff.h index 3160bb3..0a2a65d 100644 --- a/include/ceres/codegen/autodiff.h +++ b/include/ceres/codegen/autodiff.h
@@ -131,7 +131,7 @@ // jacobians[0][1] = v_352; // ... for (int i = 0, total_param_id = 0; i < kNumParameterBlocks; - ++i, total_param_id += ParameterDims::GetDim(i)) { + total_param_id += ParameterDims::GetDim(i), ++i) { for (int r = 0; r < kNumResiduals; ++r) { for (int j = 0; j < ParameterDims::GetDim(i); ++j) { internal::MakeOutput( @@ -191,20 +191,65 @@ // EvaluateResidualAndJacobian. This combined function is compatible to // CostFunction::Evaluate. Therefore the generated code can be directly used // in SizedCostFunctions. - output.emplace_back("bool Evaluate(double const* const* parameters,"); output.emplace_back(" double* residuals,"); - output.emplace_back(" double** jacobians)"); - output.emplace_back("{"); - output.emplace_back(" if (residuals && jacobians) {"); - output.emplace_back(" EvaluateResidualAndJacobian("); - output.emplace_back(" parameters,"); - output.emplace_back(" residuals,"); - output.emplace_back(" jacobians);"); + output.emplace_back(" double** jacobians) {"); + output.emplace_back(" if (jacobians) {"); + + // Create a tmp array of all jacobians and use it for evaluation. + // The generated code for a <2,3,1,2> cost functor is: + // double jacobians_data[6]; + // double* jacobians_ptrs[] = { + // jacobians_data + 0, + // jacobians_data + 6, + // jacobians_data + 8, + // }; + output.emplace_back(" double jacobians_data[" + + std::to_string(kNumParameters * kNumResiduals) + "];"); + output.emplace_back(" double* jacobians_ptrs[] = {"); + for (int i = 0, total_param_id = 0; i < kNumParameterBlocks; + total_param_id += ParameterDims::GetDim(i), ++i) { + output.emplace_back(" jacobians_data + " + + std::to_string(kNumResiduals * total_param_id) + ","); + } + output.emplace_back(" };"); + + // Evaluate into the tmp array. + output.emplace_back( + " EvaluateResidualAndJacobian(parameters, residuals, " + "jacobians_ptrs);"); + + // Copy the computed jacobians into the output array. Add an if-statement to + // test for null-jacobians. The generated code for a <2,3,1,2> cost functor + // is: + // if (jacobians[0]) { + // for (int i = 0; i < 6; ++i) { + // jacobians[0][i] = jacobians_tmp[0][i]; + // } + // } + // if (jacobians[1]) { + // for (int i = 0; i < 2; ++i) { + // jacobians[1][i] = jacobians_tmp[1][i]; + // } + // } + // if (jacobians[2]) { + // for (int i = 0; i < 4; ++i) { + // jacobians[2][i] = jacobians_tmp[2][i]; + // } + // } + for (int i = 0; i < kNumParameterBlocks; ++i) { + output.emplace_back(" if (jacobians[" + std::to_string(i) + "]) {"); + output.emplace_back( + " for (int i = 0; i < " + + std::to_string(ParameterDims::GetDim(i) * kNumResiduals) + "; ++i) {"); + output.emplace_back(" jacobians[" + std::to_string(i) + + "][i] = jacobians_ptrs[" + std::to_string(i) + "][i];"); + output.emplace_back(" }"); + output.emplace_back(" }"); + } + output.emplace_back(" return true;"); output.emplace_back(" }"); - output.emplace_back(" else if (residuals) {"); - output.emplace_back(" EvaluateResidual(parameters,residuals);"); - output.emplace_back(" }"); + output.emplace_back(" EvaluateResidual(parameters, residuals);"); output.emplace_back(" return true;"); output.emplace_back("}");