Simplify instantiation of cost functions and their functors

If arguments are passed to a cost function that can be used to construct
the functor, the latter will be instantiated by the cost function using
std::make_unique to ensure exception safety. This not only avoids static
analysis warnings caused by calling new but also spelling the cost
functor type name multiple times.

Also expand deduction guides for instantiating
Dynamic(Auto|Numeric)DiffCostFunction from std::unique_ptr enabled
constructor overloads.

Finally, make CostFunction default move constructible and assignable but
only through derived classes. This in turn allows derived classes to be
movable without relying on custom implementations of corresponding
operators.

Change-Id: Idee8b9871d862bc9f9f8b5a8d0bedc52863e93c0
This commit is contained in:
Sergiu Deitsch
2023-06-10 21:01:25 +02:00
parent 8b88a9ab49
commit 91773746be
37 changed files with 613 additions and 262 deletions
+1 -3
View File
@@ -37,9 +37,7 @@ implements an automatically differentiated ``CostFunction`` for `Rat43
};
CostFunction* cost_function =
new AutoDiffCostFunction<Rat43CostFunctor, 1, 4>(
new Rat43CostFunctor(x, y));
auto* cost_function = new AutoDiffCostFunction<Rat43CostFunctor, 1, 4>(x, y);
Notice that compared to numeric differentiation, the only difference
when defining the functor for use with automatic differentiation is
+2 -6
View File
@@ -47,8 +47,7 @@ in Ceres.
static ceres::FirstOrderFunction* Create() {
constexpr int kNumParameters = 2;
return new ceres::AutoDiffFirstOrderFunction<Rosenbrock, kNumParameters>(
new Rosenbrock);
return new ceres::AutoDiffFirstOrderFunction<Rosenbrock, kNumParameters>();
}
};
@@ -161,8 +160,7 @@ follows [#f2]_.
constexpr int kNumParameters = 2;
return new ceres::NumericDiffFirstOrderFunction<Rosenbrock,
ceres::CENTRAL,
kNumParameters>(
new Rosenbrock);
kNumParameters>();
}
};
@@ -177,8 +175,6 @@ non-linear least squares problems [#f3]_.
// f(x,y) = (1-x)^2 + 100(y - x^2)^2;
class Rosenbrock final : public ceres::FirstOrderFunction {
public:
~Rosenbrock() override {}
bool Evaluate(const double* parameters,
double* cost,
double* gradient) const override {
+8 -7
View File
@@ -118,12 +118,13 @@ An implementation of the above three steps looks as follows:
y[0] = y_in[0];
y[1] = y_in[1];
compute_distortion.reset(new ceres::CostFunctionToFunctor<1, 1>(
new ceres::NumericDiffCostFunction<ComputeDistortionValueFunctor,
ceres::CENTRAL,
1,
1>(
new ComputeDistortionValueFunctor)));
compute_distortion = std::make_unique<ceres::CostFunctionToFunctor<1, 1>>(
std::make_unique<ceres::NumericDiffCostFunction<
ComputeDistortionValueFunctor
, ceres::CENTRAL, 1, 1
>
>()
);
}
template <typename T>
@@ -140,7 +141,7 @@ An implementation of the above three steps looks as follows:
double x[2];
double y[2];
std::unique_ptr<ceres::CostFunctionToFunctor<1, 1> > compute_distortion;
std::unique_ptr<ceres::CostFunctionToFunctor<1, 1>> compute_distortion;
};
+47 -43
View File
@@ -181,12 +181,19 @@ the corresponding accessors. This information will be verified by the
class AutoDiffCostFunction : public
SizedCostFunction<kNumResiduals, Ns> {
public:
AutoDiffCostFunction(CostFunctor* functor, ownership = TAKE_OWNERSHIP);
// Instantiate CostFunctor using the supplied arguments.
template<class ...Args>
explicit AutoDiffCostFunction(Args&& ...args);
explicit AutoDiffCostFunction(std::unique_ptr<CostFunctor> functor);
explicit AutoDiffCostFunction(CostFunctor* functor, ownership = TAKE_OWNERSHIP);
// Ignore the template parameter kNumResiduals and use
// num_residuals instead.
AutoDiffCostFunction(CostFunctor* functor,
int num_residuals,
ownership = TAKE_OWNERSHIP);
AutoDiffCostFunction(std::unique_ptr<CostFunctor> functor,
int num_residuals);
};
To get an auto differentiated cost function, you must define a
@@ -244,9 +251,9 @@ the corresponding accessors. This information will be verified by the
.. code-block:: c++
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
new MyScalarCostFunctor(1.0)); ^ ^ ^
auto* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(1.0);
^ ^ ^
| | |
Dimension of residual ------+ | |
Dimension of x ----------------+ |
@@ -272,7 +279,7 @@ the corresponding accessors. This information will be verified by the
.. code-block:: c++
MyScalarCostFunctor functor(1.0)
CostFunction* cost_function
auto* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, 1, 2, 2>(
&functor, DO_NOT_TAKE_OWNERSHIP);
@@ -281,9 +288,11 @@ the corresponding accessors. This information will be verified by the
.. code-block:: c++
CostFunction* cost_function
= new AutoDiffCostFunction<MyScalarCostFunctor, DYNAMIC, 2, 2>(
new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^
auto functor = std::make_unique<CostFunctorWithDynamicNumResiduals>(1.0);
auto* cost_function
= new AutoDiffCostFunction<CostFunctorWithDynamicNumResiduals,
DYNAMIC, 2, 2>(
std::move(functor), ^ ^ ^
runtime_number_of_residuals); <----+ | | |
| | | |
| | | |
@@ -336,9 +345,7 @@ the corresponding accessors. This information will be verified by the
.. code-block:: c++
DynamicAutoDiffCostFunction<MyCostFunctor, 4>* cost_function =
new DynamicAutoDiffCostFunction<MyCostFunctor, 4>(
new MyCostFunctor());
auto* cost_function = new DynamicAutoDiffCostFunction<MyCostFunctor, 4>();
cost_function->AddParameterBlock(5);
cost_function->AddParameterBlock(10);
cost_function->SetNumResiduals(21);
@@ -443,9 +450,9 @@ the corresponding accessors. This information will be verified by the
.. code-block:: c++
CostFunction* cost_function
= new NumericDiffCostFunction<MyScalarCostFunctor, CENTRAL, 1, 2, 2>(
new MyScalarCostFunctor(1.0)); ^ ^ ^ ^
auto* cost_function
= new NumericDiffCostFunction<MyScalarCostFunctor, CENTRAL, 1, 2, 2>(1.0)
^ ^ ^ ^
| | | |
Finite Differencing Scheme -+ | | |
Dimension of residual ------------+ | |
@@ -465,17 +472,18 @@ the corresponding accessors. This information will be verified by the
.. code-block:: c++
CostFunction* cost_function
= new NumericDiffCostFunction<MyScalarCostFunctor, CENTRAL, DYNAMIC, 2, 2>(
new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^
TAKE_OWNERSHIP, | | |
runtime_number_of_residuals); <----+ | | |
| | | |
| | | |
Actual number of residuals ------+ | | |
Indicate dynamic number of residuals --------------------+ | |
Dimension of x ------------------------------------------------+ |
Dimension of y ---------------------------------------------------+
auto functor = std::make_unique<CostFunctorWithDynamicNumResiduals>(1.0);
auto* cost_function
= new NumericDiffCostFunction<CostFunctorWithDynamicNumResiduals,
CENTRAL, DYNAMIC, 2, 2>(
std::move(functor), ^ ^ ^
runtime_number_of_residuals); <----+ | | |
| | | |
| | | |
Actual number of residuals ------+ | | |
Indicate dynamic number of residuals --------+ | |
Dimension of x ------------------------------------+ |
Dimension of y ---------------------------------------+
There are three available numeric differentiation schemes in ceres-solver:
@@ -569,9 +577,8 @@ Numeric Differentiation & Manifolds
.. code-block:: c++
CostFunction* cost_function
= new NumericDiffCostFunction<MyCostFunction, CENTRAL, 1, 4, 8>(
new MyCostFunction(...), TAKE_OWNERSHIP);
auto* cost_function
= new NumericDiffCostFunction<MyCostFunction, CENTRAL, 1, 4, 8>(...);
where ``MyCostFunction`` has 1 residual and 2 parameter blocks with
sizes 4 and 8 respectively. Look at the tests for a more detailed
@@ -612,8 +619,7 @@ Numeric Differentiation & Manifolds
.. code-block:: c++
DynamicNumericDiffCostFunction<MyCostFunctor>* cost_function =
new DynamicNumericDiffCostFunction<MyCostFunctor>(new MyCostFunctor);
auto cost_function = std::make_unique<DynamicNumericDiffCostFunction<MyCostFunctor>>();
cost_function->AddParameterBlock(5);
cost_function->AddParameterBlock(10);
cost_function->SetNumResiduals(21);
@@ -672,8 +678,8 @@ Numeric Differentiation & Manifolds
.. code-block:: c++
struct CameraProjection {
CameraProjection(double* observation)
: intrinsic_projection_(new IntrinsicProjection(observation)) {
explicit CameraProjection(double* observation)
: intrinsic_projection_(std::make_unique<IntrinsicProjection>(observation)) {
}
template <typename T>
@@ -691,7 +697,7 @@ Numeric Differentiation & Manifolds
}
private:
CostFunctionToFunctor<2,5,3> intrinsic_projection_;
CostFunctionToFunctor<2, 5, 3> intrinsic_projection_;
};
Note that :class:`CostFunctionToFunctor` takes ownership of the
@@ -733,10 +739,9 @@ Numeric Differentiation & Manifolds
.. code-block:: c++
struct CameraProjection {
CameraProjection(double* observation)
explicit CameraProjection(double* observation)
: intrinsic_projection_(
new NumericDiffCostFunction<IntrinsicProjection, CENTRAL, 2, 5, 3>(
new IntrinsicProjection(observation))) {}
std::make_unique<NumericDiffCostFunction<IntrinsicProjection, CENTRAL, 2, 5, 3>>()) {}
template <typename T>
bool operator()(const T* rotation,
@@ -794,8 +799,8 @@ Numeric Differentiation & Manifolds
.. code-block:: c++
struct CameraProjection {
CameraProjection(double* observation)
: intrinsic_projection_(new IntrinsicProjection(observation)) {
explicit CameraProjection(double* observation)
: intrinsic_projection_(std::make_unique<IntrinsicProjection>(observation)) {
}
template <typename T>
@@ -1122,9 +1127,8 @@ their shape graphically. More details can be found in
// Add parameter blocks
CostFunction* cost_function =
new AutoDiffCostFunction < UW_Camera_Mapper, 2, 9, 3>(
new UW_Camera_Mapper(feature_x, feature_y));
auto* cost_function =
new AutoDiffCostFunction<UW_Camera_Mapper, 2, 9, 3>(feature_x, feature_y);
LossFunctionWrapper* loss_function(new HuberLoss(1.0), TAKE_OWNERSHIP);
problem.AddResidualBlock(cost_function, loss_function, parameters);
@@ -1556,8 +1560,8 @@ In advanced use cases, manifolds can be dynamically allocated and passed as (sma
ProductManifold<std::unique_ptr<QuaternionManifold>, EuclideanManifold<3>> se3
{std::make_unique<QuaternionManifold>(), EuclideanManifold<3>{}};
In C++17, the template parameters can be left out as they are automatically
deduced making the initialization much simpler:
The template parameters can also be left out as they are deduced automatically
making the initialization much simpler:
.. code-block:: c++
+11 -12
View File
@@ -112,7 +112,7 @@ Ceres solve it.
// Set up the only cost function (also known as residual). This uses
// auto-differentiation to obtain the derivative (jacobian).
CostFunction* cost_function =
new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
new AutoDiffCostFunction<CostFunctor, 1, 1>();
problem.AddResidualBlock(cost_function, nullptr, &x);
// Run the solver!
@@ -212,8 +212,7 @@ Which is added to the :class:`Problem` as:
.. code-block:: c++
CostFunction* cost_function =
new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>(
new NumericDiffCostFunctor);
new NumericDiffCostFunction<NumericDiffCostFunctor, ceres::CENTRAL, 1, 1>();
problem.AddResidualBlock(cost_function, nullptr, &x);
Notice the parallel from when we were using automatic differentiation
@@ -221,7 +220,7 @@ Notice the parallel from when we were using automatic differentiation
.. code-block:: c++
CostFunction* cost_function =
new AutoDiffCostFunction<CostFunctor, 1, 1>(new CostFunctor);
new AutoDiffCostFunction<CostFunctor, 1, 1>();
problem.AddResidualBlock(cost_function, nullptr, &x);
The construction looks almost identical to the one used for automatic
@@ -360,13 +359,13 @@ respectively. Using these, the problem can be constructed as follows:
// Add residual terms to the problem using the autodiff
// wrapper to get the derivatives automatically.
problem.AddResidualBlock(
new AutoDiffCostFunction<F1, 1, 1, 1>(new F1), nullptr, &x1, &x2);
new AutoDiffCostFunction<F1, 1, 1, 1>(), nullptr, &x1, &x2);
problem.AddResidualBlock(
new AutoDiffCostFunction<F2, 1, 1, 1>(new F2), nullptr, &x3, &x4);
new AutoDiffCostFunction<F2, 1, 1, 1>(), nullptr, &x3, &x4);
problem.AddResidualBlock(
new AutoDiffCostFunction<F3, 1, 1, 1>(new F3), nullptr, &x2, &x3);
new AutoDiffCostFunction<F3, 1, 1, 1>(), nullptr, &x2, &x3);
problem.AddResidualBlock(
new AutoDiffCostFunction<F4, 1, 1, 1>(new F4), nullptr, &x1, &x4);
new AutoDiffCostFunction<F4, 1, 1, 1>(), nullptr, &x1, &x4);
Note that each ``ResidualBlock`` only depends on the two parameters
@@ -499,8 +498,8 @@ Assuming the observations are in a :math:`2n` sized array called
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]));
new AutoDiffCostFunction<ExponentialResidual, 1, 1, 1>
(data[2 * i], data[2 * i + 1]);
problem.AddResidualBlock(cost_function, nullptr, &m, &c);
}
@@ -675,8 +674,8 @@ The details of this camera model can be found the `Bundler homepage
// the client code.
static ceres::CostFunction* Create(const double observed_x,
const double observed_y) {
return (new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>(
new SnavelyReprojectionError(observed_x, observed_y)));
return new ceres::AutoDiffCostFunction<SnavelyReprojectionError, 2, 9, 3>
(observed_x, observed_y);
}
double observed_x;
+1 -2
View File
@@ -61,8 +61,7 @@ Ceres Solver. This is done in two steps:
}
CostFunction* cost_function =
new NumericDiffCostFunction<Rat43CostFunctor, FORWARD, 1, 4>(
new Rat43CostFunctor(x, y));
new NumericDiffCostFunction<Rat43CostFunctor, FORWARD, 1, 4>(x, y);
This is about the minimum amount of work one can expect to do to
define the cost function. The only thing that the user needs to do is