I'm going through the Ceres Solver tutorial.
Powell's Function
Powell's function maps from R^4 -> R^4, so it seems intuitive to define one residual block that takes in a 4-element array x and fill in a 4-element array residual.
Instead, the example in the tutorial defines 4 different residual blocks that map R^2 -> R^1.
Of course if we are trying to minimize 1/2 || F(x) ||^2, then minimizing each element of F will implicitly yield the same solution as minimizing 1/2 || F(x) ||^2 directly (ie. my suggestion is to return a single residual vector F instead of F1...F4 separately). (I have verified this by using the below cost functor).
struct F {
template<typename T>
bool operator() (const T* const x, T* residual) const {
residual[0] = x[0] + 10.0 * x[1];
residual[1] = sqrt(5.0) * (x[2] - x[3]);
residual[2] = (x[1] - 2.0*x[2]) * (x[1] - 2.0*x[2]);
residual[3] = T(sqrt(10.0)) * (x[0] - x[3]) * (x[0] - x[3]);
return true;
}
};
What's the advantage of defining separate residual blocks (and implicitly parameter blocks) for each element the residual vector
F?If residual
F1depends on parametersx1andx2and residualF2depends onx3andx4, will the cost ofFwith respect tox1impact the value ofx3?
Curve Fitting
The other example attempts to find parameters m and c to curve y=e^(mx + c).
It defines some ExponentialResidual which simply outputs T(y_) - exp(m[0] * T(x_) + c[0]) where (x_, y_) is a data point.
They then proceed to add one residual block for every observation
double m = 0.0;
double c = 0.0;
Problem problem;
for (int i = 0; i < kNumObservations; ++i) {
CostFunction* cost_function =
new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>(
new ExponentialResidual(data[2 * i], data[2 * i + 1]));
problem.AddResidualBlock(cost_function, NULL, &m, &c);
}
- Although I was too lazy to reproduce this example myself, I suspect that this could also be accomplished with just one residual block that maps
R^2 -> R^1, where the 1D residual is just the sum of allT(y_) - exp(m[0] * T(x_) + c[0])for all(x_, y_)? Was it necessary to define a residual block for every observation?
Thanks for reading this long-is post!