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
F1
depends on parametersx1
andx2
and residualF2
depends onx3
andx4
, will the cost ofF
with respect tox1
impact 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!