diff --git a/docs/source/automatic_derivatives.rst b/docs/source/automatic_derivatives.rst index 26fceb046..3fe07275a 100644 --- a/docs/source/automatic_derivatives.rst +++ b/docs/source/automatic_derivatives.rst @@ -37,9 +37,7 @@ implements an automatically differentiated ``CostFunction`` for `Rat43 }; - CostFunction* cost_function = - new AutoDiffCostFunction( - new Rat43CostFunctor(x, y)); + auto* cost_function = new AutoDiffCostFunction(x, y); Notice that compared to numeric differentiation, the only difference when defining the functor for use with automatic differentiation is diff --git a/docs/source/gradient_tutorial.rst b/docs/source/gradient_tutorial.rst index 03bd7f182..2af44e114 100644 --- a/docs/source/gradient_tutorial.rst +++ b/docs/source/gradient_tutorial.rst @@ -47,8 +47,7 @@ in Ceres. static ceres::FirstOrderFunction* Create() { constexpr int kNumParameters = 2; - return new ceres::AutoDiffFirstOrderFunction( - new Rosenbrock); + return new ceres::AutoDiffFirstOrderFunction(); } }; @@ -161,8 +160,7 @@ follows [#f2]_. constexpr int kNumParameters = 2; return new ceres::NumericDiffFirstOrderFunction( - 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 { diff --git a/docs/source/interfacing_with_autodiff.rst b/docs/source/interfacing_with_autodiff.rst index 59bc79853..fa05835e9 100644 --- a/docs/source/interfacing_with_autodiff.rst +++ b/docs/source/interfacing_with_autodiff.rst @@ -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( - new ComputeDistortionValueFunctor))); + compute_distortion = std::make_unique>( + std::make_unique + >() + ); } template @@ -140,7 +141,7 @@ An implementation of the above three steps looks as follows: double x[2]; double y[2]; - std::unique_ptr > compute_distortion; + std::unique_ptr> compute_distortion; }; diff --git a/docs/source/nnls_modeling.rst b/docs/source/nnls_modeling.rst index 512834c8c..71d31e00a 100644 --- a/docs/source/nnls_modeling.rst +++ b/docs/source/nnls_modeling.rst @@ -181,12 +181,19 @@ the corresponding accessors. This information will be verified by the class AutoDiffCostFunction : public SizedCostFunction { public: - AutoDiffCostFunction(CostFunctor* functor, ownership = TAKE_OWNERSHIP); + // Instantiate CostFunctor using the supplied arguments. + template + explicit AutoDiffCostFunction(Args&& ...args); + explicit AutoDiffCostFunction(std::unique_ptr 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 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( - new MyScalarCostFunctor(1.0)); ^ ^ ^ + auto* cost_function + = new AutoDiffCostFunction(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( &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( - new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^ + auto functor = std::make_unique(1.0); + auto* cost_function + = new AutoDiffCostFunction( + 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* cost_function = - new DynamicAutoDiffCostFunction( - new MyCostFunctor()); + auto* cost_function = new DynamicAutoDiffCostFunction(); 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( - new MyScalarCostFunctor(1.0)); ^ ^ ^ ^ + auto* cost_function + = new NumericDiffCostFunction(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( - 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(1.0); + auto* cost_function + = new NumericDiffCostFunction( + 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( - new MyCostFunction(...), TAKE_OWNERSHIP); + auto* cost_function + = new NumericDiffCostFunction(...); 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* cost_function = - new DynamicNumericDiffCostFunction(new MyCostFunctor); + auto cost_function = std::make_unique>(); 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(observation)) { } template @@ -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( - new IntrinsicProjection(observation))) {} + std::make_unique>()) {} template 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(observation)) { } template @@ -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(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, EuclideanManifold<3>> se3 {std::make_unique(), 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++ diff --git a/docs/source/nnls_tutorial.rst b/docs/source/nnls_tutorial.rst index 66728e01b..6de800e59 100644 --- a/docs/source/nnls_tutorial.rst +++ b/docs/source/nnls_tutorial.rst @@ -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(new CostFunctor); + new AutoDiffCostFunction(); 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( - new NumericDiffCostFunctor); + new NumericDiffCostFunction(); 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(new CostFunctor); + new AutoDiffCostFunction(); 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(new F1), nullptr, &x1, &x2); + new AutoDiffCostFunction(), nullptr, &x1, &x2); problem.AddResidualBlock( - new AutoDiffCostFunction(new F2), nullptr, &x3, &x4); + new AutoDiffCostFunction(), nullptr, &x3, &x4); problem.AddResidualBlock( - new AutoDiffCostFunction(new F3), nullptr, &x2, &x3); + new AutoDiffCostFunction(), nullptr, &x2, &x3); problem.AddResidualBlock( - new AutoDiffCostFunction(new F4), nullptr, &x1, &x4); + new AutoDiffCostFunction(), 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( - new ExponentialResidual(data[2 * i], data[2 * i + 1])); + new AutoDiffCostFunction + (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( - new SnavelyReprojectionError(observed_x, observed_y))); + return new ceres::AutoDiffCostFunction + (observed_x, observed_y); } double observed_x; diff --git a/docs/source/numerical_derivatives.rst b/docs/source/numerical_derivatives.rst index 609c84b69..8d7fb3a6a 100644 --- a/docs/source/numerical_derivatives.rst +++ b/docs/source/numerical_derivatives.rst @@ -61,8 +61,7 @@ Ceres Solver. This is done in two steps: } CostFunction* cost_function = - new NumericDiffCostFunction( - new Rat43CostFunctor(x, y)); + new NumericDiffCostFunction(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 diff --git a/examples/bicubic_interpolation.cc b/examples/bicubic_interpolation.cc index 97f5c3c64..21b3c7edf 100644 --- a/examples/bicubic_interpolation.cc +++ b/examples/bicubic_interpolation.cc @@ -73,7 +73,7 @@ struct AutoDiffBiCubicCost { const Eigen::Vector2d& point, double value) { return new ceres::AutoDiffCostFunction( - new AutoDiffBiCubicCost(interpolator, point, value)); + interpolator, point, value); } const Eigen::Vector2d point_; diff --git a/examples/circle_fit.cc b/examples/circle_fit.cc index 2d398c48e..fd848d91e 100644 --- a/examples/circle_fit.cc +++ b/examples/circle_fit.cc @@ -133,8 +133,8 @@ int main(int argc, char** argv) { int num_points = 0; while (scanf("%lf %lf\n", &xx, &yy) == 2) { ceres::CostFunction* cost = - new ceres::AutoDiffCostFunction( - new DistanceFromCircleCost(xx, yy)); + new ceres::AutoDiffCostFunction(xx, + yy); problem.AddResidualBlock(cost, loss, &x, &y, &m); num_points++; } diff --git a/examples/curve_fitting.cc b/examples/curve_fitting.cc index c6b3a6b2e..105402e90 100644 --- a/examples/curve_fitting.cc +++ b/examples/curve_fitting.cc @@ -143,7 +143,7 @@ int main(int argc, char** argv) { for (int i = 0; i < kNumObservations; ++i) { problem.AddResidualBlock( new ceres::AutoDiffCostFunction( - new ExponentialResidual(data[2 * i], data[2 * i + 1])), + data[2 * i], data[2 * i + 1]), nullptr, &m, &c); diff --git a/examples/ellipse_approximation.cc b/examples/ellipse_approximation.cc index eefc72d54..6fa8f1cda 100644 --- a/examples/ellipse_approximation.cc +++ b/examples/ellipse_approximation.cc @@ -354,7 +354,7 @@ class EuclideanDistanceFunctor { static ceres::CostFunction* Create(const double sqrt_weight) { return new ceres::AutoDiffCostFunction( - new EuclideanDistanceFunctor(sqrt_weight)); + sqrt_weight); } private: diff --git a/examples/helloworld.cc b/examples/helloworld.cc index c9972fd3c..40c2f2c31 100644 --- a/examples/helloworld.cc +++ b/examples/helloworld.cc @@ -62,7 +62,7 @@ int main(int argc, char** argv) { // Set up the only cost function (also known as residual). This uses // auto-differentiation to obtain the derivative (jacobian). ceres::CostFunction* cost_function = - new ceres::AutoDiffCostFunction(new CostFunctor); + new ceres::AutoDiffCostFunction(); problem.AddResidualBlock(cost_function, nullptr, &x); // Run the solver! diff --git a/examples/iteration_callback_example.cc b/examples/iteration_callback_example.cc index 98e24f1ab..0be2f36c3 100644 --- a/examples/iteration_callback_example.cc +++ b/examples/iteration_callback_example.cc @@ -168,7 +168,7 @@ int main(int argc, char** argv) { for (int i = 0; i < kNumObservations; ++i) { problem.AddResidualBlock( new ceres::AutoDiffCostFunction( - new ExponentialResidual(data[2 * i], data[2 * i + 1])), + data[2 * i], data[2 * i + 1]), nullptr, &m, &c); diff --git a/examples/libmv_homography.cc b/examples/libmv_homography.cc index 1cadb4638..b7c9eda8b 100644 --- a/examples/libmv_homography.cc +++ b/examples/libmv_homography.cc @@ -327,14 +327,10 @@ bool EstimateHomography2DFromCorrespondences( // Step 2: Refine matrix using Ceres minimizer. ceres::Problem problem; for (int i = 0; i < x1.cols(); i++) { - auto* homography_symmetric_geometric_cost_function = - new HomographySymmetricGeometricCostFunctor(x1.col(i), x2.col(i)); - problem.AddResidualBlock( new ceres::AutoDiffCostFunction( - homography_symmetric_geometric_cost_function), + 9>(x1.col(i), x2.col(i)), nullptr, H->data()); } diff --git a/examples/more_garbow_hillstrom.cc b/examples/more_garbow_hillstrom.cc index b2fe61ba2..f15e576e3 100644 --- a/examples/more_garbow_hillstrom.cc +++ b/examples/more_garbow_hillstrom.cc @@ -81,46 +81,47 @@ static void SetNumericDiffOptions(ceres::NumericDiffOptions* options) { CERES_GET_FLAG(FLAGS_ridders_extrapolations); } -#define BEGIN_MGH_PROBLEM(name, num_parameters, num_residuals) \ - struct name { \ - static constexpr int kNumParameters = num_parameters; \ - static const double initial_x[kNumParameters]; \ - static const double lower_bounds[kNumParameters]; \ - static const double upper_bounds[kNumParameters]; \ - static const double constrained_optimal_cost; \ - static const double unconstrained_optimal_cost; \ - static CostFunction* Create() { \ - if (CERES_GET_FLAG(FLAGS_use_numeric_diff)) { \ - ceres::NumericDiffOptions options; \ - SetNumericDiffOptions(&options); \ - if (CERES_GET_FLAG(FLAGS_numeric_diff_method) == "central") { \ - return new NumericDiffCostFunction( \ - new name, ceres::TAKE_OWNERSHIP, num_residuals, options); \ - } else if (CERES_GET_FLAG(FLAGS_numeric_diff_method) == "forward") { \ - return new NumericDiffCostFunction( \ - new name, ceres::TAKE_OWNERSHIP, num_residuals, options); \ - } else if (CERES_GET_FLAG(FLAGS_numeric_diff_method) == "ridders") { \ - return new NumericDiffCostFunction( \ - new name, ceres::TAKE_OWNERSHIP, num_residuals, options); \ - } else { \ - LOG(ERROR) << "Invalid numeric diff method specified"; \ - return nullptr; \ - } \ - } else { \ - return new AutoDiffCostFunction( \ - new name); \ - } \ - } \ - template \ +#define BEGIN_MGH_PROBLEM(name, num_parameters, num_residuals) \ + struct name { \ + static constexpr int kNumParameters = num_parameters; \ + static const double initial_x[kNumParameters]; \ + static const double lower_bounds[kNumParameters]; \ + static const double upper_bounds[kNumParameters]; \ + static const double constrained_optimal_cost; \ + static const double unconstrained_optimal_cost; \ + static CostFunction* Create() { \ + if (CERES_GET_FLAG(FLAGS_use_numeric_diff)) { \ + ceres::NumericDiffOptions options; \ + SetNumericDiffOptions(&options); \ + if (CERES_GET_FLAG(FLAGS_numeric_diff_method) == "central") { \ + return new NumericDiffCostFunction( \ + new name, ceres::TAKE_OWNERSHIP, num_residuals, options); \ + } else if (CERES_GET_FLAG(FLAGS_numeric_diff_method) == "forward") { \ + return new NumericDiffCostFunction( \ + new name, ceres::TAKE_OWNERSHIP, num_residuals, options); \ + } else if (CERES_GET_FLAG(FLAGS_numeric_diff_method) == "ridders") { \ + return new NumericDiffCostFunction( \ + new name, ceres::TAKE_OWNERSHIP, num_residuals, options); \ + } else { \ + LOG(ERROR) << "Invalid numeric diff method specified"; \ + return nullptr; \ + } \ + } else { \ + return new AutoDiffCostFunction(); \ + } \ + } \ + template \ bool operator()(const T* const x, T* residual) const { // clang-format off diff --git a/examples/powell.cc b/examples/powell.cc index 80de4236c..a4ca1b7ce 100644 --- a/examples/powell.cc +++ b/examples/powell.cc @@ -104,13 +104,13 @@ int main(int argc, char** argv) { // wrapper to get the derivatives automatically. The parameters, x1 through // x4, are modified in place. problem.AddResidualBlock( - new ceres::AutoDiffCostFunction(new F1), nullptr, &x1, &x2); + new ceres::AutoDiffCostFunction(), nullptr, &x1, &x2); problem.AddResidualBlock( - new ceres::AutoDiffCostFunction(new F2), nullptr, &x3, &x4); + new ceres::AutoDiffCostFunction(), nullptr, &x3, &x4); problem.AddResidualBlock( - new ceres::AutoDiffCostFunction(new F3), nullptr, &x2, &x3); + new ceres::AutoDiffCostFunction(), nullptr, &x2, &x3); problem.AddResidualBlock( - new ceres::AutoDiffCostFunction(new F4), nullptr, &x1, &x4); + new ceres::AutoDiffCostFunction(), nullptr, &x1, &x4); ceres::Solver::Options options; LOG_IF(FATAL, diff --git a/examples/robust_curve_fitting.cc b/examples/robust_curve_fitting.cc index 8759a46f5..e08b0dfc3 100644 --- a/examples/robust_curve_fitting.cc +++ b/examples/robust_curve_fitting.cc @@ -147,7 +147,7 @@ int main(int argc, char** argv) { for (int i = 0; i < kNumObservations; ++i) { ceres::CostFunction* cost_function = new ceres::AutoDiffCostFunction( - new ExponentialResidual(data[2 * i], data[2 * i + 1])); + data[2 * i], data[2 * i + 1]); problem.AddResidualBlock(cost_function, new ceres::CauchyLoss(0.5), &m, &c); } diff --git a/examples/sampled_function/sampled_function.cc b/examples/sampled_function/sampled_function.cc index 4cc3f11c9..40e9c1f0d 100644 --- a/examples/sampled_function/sampled_function.cc +++ b/examples/sampled_function/sampled_function.cc @@ -51,7 +51,7 @@ struct InterpolatedCostFunctor { static ceres::CostFunction* Create(const Interpolator& interpolator) { return new ceres::AutoDiffCostFunction( - new InterpolatedCostFunctor(interpolator)); + interpolator); } private: diff --git a/examples/simple_bundle_adjuster.cc b/examples/simple_bundle_adjuster.cc index 5dbdd9d4a..bb0ba1c77 100644 --- a/examples/simple_bundle_adjuster.cc +++ b/examples/simple_bundle_adjuster.cc @@ -164,8 +164,8 @@ struct SnavelyReprojectionError { // the client code. static ceres::CostFunction* Create(const double observed_x, const double observed_y) { - return (new ceres::AutoDiffCostFunction( - new SnavelyReprojectionError(observed_x, observed_y))); + return new ceres::AutoDiffCostFunction( + observed_x, observed_y); } double observed_x; diff --git a/examples/slam/pose_graph_2d/pose_graph_2d_error_term.h b/examples/slam/pose_graph_2d/pose_graph_2d_error_term.h index 6ca1e604a..3d34f8d3b 100644 --- a/examples/slam/pose_graph_2d/pose_graph_2d_error_term.h +++ b/examples/slam/pose_graph_2d/pose_graph_2d_error_term.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -34,9 +34,9 @@ #define CERES_EXAMPLES_POSE_GRAPH_2D_POSE_GRAPH_2D_ERROR_TERM_H_ #include "Eigen/Core" +#include "ceres/autodiff_cost_function.h" -namespace ceres { -namespace examples { +namespace ceres::examples { template Eigen::Matrix RotationMatrix2D(T yaw_radians) { @@ -96,10 +96,9 @@ class PoseGraph2dErrorTerm { double y_ab, double yaw_ab_radians, const Eigen::Matrix3d& sqrt_information) { - return (new ceres:: - AutoDiffCostFunction( - new PoseGraph2dErrorTerm( - x_ab, y_ab, yaw_ab_radians, sqrt_information))); + return new ceres:: + AutoDiffCostFunction( + x_ab, y_ab, yaw_ab_radians, sqrt_information); } EIGEN_MAKE_ALIGNED_OPERATOR_NEW @@ -113,7 +112,6 @@ class PoseGraph2dErrorTerm { const Eigen::Matrix3d sqrt_information_; }; -} // namespace examples -} // namespace ceres +} // namespace ceres::examples #endif // CERES_EXAMPLES_POSE_GRAPH_2D_POSE_GRAPH_2D_ERROR_TERM_H_ diff --git a/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h b/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h index e072678fa..b1c01388b 100644 --- a/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h +++ b/examples/slam/pose_graph_3d/pose_graph_3d_error_term.h @@ -116,7 +116,7 @@ class PoseGraph3dErrorTerm { const Pose3d& t_ab_measured, const Eigen::Matrix& sqrt_information) { return new ceres::AutoDiffCostFunction( - new PoseGraph3dErrorTerm(t_ab_measured, sqrt_information)); + t_ab_measured, sqrt_information); } EIGEN_MAKE_ALIGNED_OPERATOR_NEW diff --git a/examples/snavely_reprojection_error.h b/examples/snavely_reprojection_error.h index ff3f25afa..aaf0c6ca2 100644 --- a/examples/snavely_reprojection_error.h +++ b/examples/snavely_reprojection_error.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -41,10 +41,10 @@ #ifndef CERES_EXAMPLES_SNAVELY_REPROJECTION_ERROR_H_ #define CERES_EXAMPLES_SNAVELY_REPROJECTION_ERROR_H_ +#include "ceres/autodiff_cost_function.h" #include "ceres/rotation.h" -namespace ceres { -namespace examples { +namespace ceres::examples { // Templated pinhole camera model for used with Ceres. The camera is // parameterized using 9 parameters: 3 for rotation, 3 for translation, 1 for @@ -95,8 +95,8 @@ struct SnavelyReprojectionError { // the client code. static ceres::CostFunction* Create(const double observed_x, const double observed_y) { - return (new ceres::AutoDiffCostFunction( - new SnavelyReprojectionError(observed_x, observed_y))); + return new ceres::AutoDiffCostFunction( + observed_x, observed_y); } double observed_x; @@ -160,20 +160,15 @@ struct SnavelyReprojectionErrorWithQuaternions { // the client code. static ceres::CostFunction* Create(const double observed_x, const double observed_y) { - return ( - new ceres::AutoDiffCostFunction( - new SnavelyReprojectionErrorWithQuaternions(observed_x, - observed_y))); + return new ceres:: + AutoDiffCostFunction( + observed_x, observed_y); } double observed_x; double observed_y; }; -} // namespace examples -} // namespace ceres +} // namespace ceres::examples #endif // CERES_EXAMPLES_SNAVELY_REPROJECTION_ERROR_H_ diff --git a/include/ceres/autodiff_cost_function.h b/include/ceres/autodiff_cost_function.h index 7e2fa711a..878b2ecaa 100644 --- a/include/ceres/autodiff_cost_function.h +++ b/include/ceres/autodiff_cost_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -82,9 +82,9 @@ // Then given this class definition, the auto differentiated cost function for // it can be constructed as follows. // -// CostFunction* cost_function -// = new AutoDiffCostFunction( -// new MyScalarCostFunctor(1.0)); ^ ^ ^ +// auto* cost_function +// = new AutoDiffCostFunction(1.0); +// ^ ^ ^ // | | | // Dimension of residual -----+ | | // Dimension of x ---------------+ | @@ -99,9 +99,11 @@ // AutoDiffCostFunction also supports cost functions with a // runtime-determined number of residuals. For example: // -// CostFunction* cost_function -// = new AutoDiffCostFunction( -// new CostFunctorWithDynamicNumResiduals(1.0), ^ ^ ^ +// auto functor = std::make_unique(1.0); +// auto* cost_function +// = new AutoDiffCostFunction( +// std::move(functor), ^ ^ ^ // runtime_number_of_residuals); <----+ | | | // | | | | // | | | | @@ -126,11 +128,11 @@ #define CERES_PUBLIC_AUTODIFF_COST_FUNCTION_H_ #include +#include #include "ceres/internal/autodiff.h" #include "ceres/sized_cost_function.h" #include "ceres/types.h" -#include "glog/logging.h" namespace ceres { @@ -156,13 +158,31 @@ class AutoDiffCostFunction final public: // Takes ownership of functor by default. Uses the template-provided // value for the number of residuals ("kNumResiduals"). + explicit AutoDiffCostFunction(std::unique_ptr functor) + : AutoDiffCostFunction{std::move(functor), TAKE_OWNERSHIP, FIXED_INIT} {} + + // Constructs the CostFunctor on the heap and takes the ownership. + // Invocable only if the number of residuals is known at compile-time. + template >* = + nullptr> + explicit AutoDiffCostFunction(Args&&... args) + // NOTE We explicitly use direct initialization using parentheses instead + // of uniform initialization using braces to avoid narrowing conversion + // warnings. + : AutoDiffCostFunction{ + std::make_unique(std::forward(args)...)} {} + + AutoDiffCostFunction(std::unique_ptr functor, int num_residuals) + : AutoDiffCostFunction{ + std::move(functor), num_residuals, TAKE_OWNERSHIP, DYNAMIC_INIT} {} + explicit AutoDiffCostFunction(CostFunctor* functor, Ownership ownership = TAKE_OWNERSHIP) - : functor_(functor), ownership_(ownership) { - static_assert(kNumResiduals != DYNAMIC, - "Can't run the fixed-size constructor if the number of " - "residuals is set to ceres::DYNAMIC."); - } + : AutoDiffCostFunction{ + std::unique_ptr{functor}, ownership, FIXED_INIT} {} // Takes ownership of functor by default. Ignores the template-provided // kNumResiduals in favor of the "num_residuals" argument provided. @@ -172,17 +192,18 @@ class AutoDiffCostFunction final AutoDiffCostFunction(CostFunctor* functor, int num_residuals, Ownership ownership = TAKE_OWNERSHIP) - : functor_(functor), ownership_(ownership) { - static_assert(kNumResiduals == DYNAMIC, - "Can't run the dynamic-size constructor if the number of " - "residuals is not ceres::DYNAMIC."); - SizedCostFunction::set_num_residuals(num_residuals); - } + : AutoDiffCostFunction{std::unique_ptr{functor}, + num_residuals, + ownership, + DYNAMIC_INIT} {} - AutoDiffCostFunction(AutoDiffCostFunction&& other) - : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} + AutoDiffCostFunction(AutoDiffCostFunction&& other) noexcept = default; + AutoDiffCostFunction& operator=(AutoDiffCostFunction&& other) noexcept = + default; + AutoDiffCostFunction(const AutoDiffCostFunction& other) = delete; + AutoDiffCostFunction& operator=(const AutoDiffCostFunction& other) = delete; - virtual ~AutoDiffCostFunction() { + ~AutoDiffCostFunction() override { // Manually release pointer if configured to not take ownership rather than // deleting only if ownership is taken. // This is to stay maximally compatible to old user code which may have @@ -204,7 +225,7 @@ class AutoDiffCostFunction final using ParameterDims = typename SizedCostFunction::ParameterDims; - if (!jacobians) { + if (jacobians == nullptr) { return internal::VariadicEvaluate( *functor_, parameters, residuals); } @@ -219,6 +240,33 @@ class AutoDiffCostFunction final const CostFunctor& functor() const { return *functor_; } private: + // Tags used to differentiate between dynamic and fixed size constructor + // delegate invocations. + static constexpr std::integral_constant DYNAMIC_INIT{}; + static constexpr std::integral_constant FIXED_INIT{}; + + template + AutoDiffCostFunction(std::unique_ptr functor, + int num_residuals, + Ownership ownership, + InitTag /*unused*/) + : functor_{std::move(functor)}, ownership_{ownership} { + static_assert(kNumResiduals == FIXED_INIT, + "Can't run the fixed-size constructor if the number of " + "residuals is set to ceres::DYNAMIC."); + + if constexpr (InitTag::value == DYNAMIC_INIT) { + SizedCostFunction::set_num_residuals(num_residuals); + } + } + + template + AutoDiffCostFunction(std::unique_ptr functor, + Ownership ownership, + InitTag tag) + : AutoDiffCostFunction{ + std::move(functor), kNumResiduals, ownership, tag} {} + std::unique_ptr functor_; Ownership ownership_; }; diff --git a/include/ceres/autodiff_first_order_function.h b/include/ceres/autodiff_first_order_function.h index de7e8f124..6cd1b1393 100644 --- a/include/ceres/autodiff_first_order_function.h +++ b/include/ceres/autodiff_first_order_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -32,6 +32,7 @@ #define CERES_PUBLIC_AUTODIFF_FIRST_ORDER_FUNCTION_H_ #include +#include #include "ceres/first_order_function.h" #include "ceres/internal/eigen.h" @@ -106,10 +107,22 @@ class AutoDiffFirstOrderFunction final : public FirstOrderFunction { public: // Takes ownership of functor. explicit AutoDiffFirstOrderFunction(FirstOrderFunctor* functor) - : functor_(functor) { + : AutoDiffFirstOrderFunction{ + std::unique_ptr{functor}} {} + + explicit AutoDiffFirstOrderFunction( + std::unique_ptr functor) + : functor_(std::move(functor)) { static_assert(kNumParameters > 0, "kNumParameters must be positive"); } + template >* = nullptr> + explicit AutoDiffFirstOrderFunction(Args&&... args) + : AutoDiffFirstOrderFunction{ + std::make_unique(std::forward(args)...)} {} + bool Evaluate(const double* const parameters, double* cost, double* gradient) const override { diff --git a/include/ceres/cost_function.h b/include/ceres/cost_function.h index 79d491287..2e5b1dd59 100644 --- a/include/ceres/cost_function.h +++ b/include/ceres/cost_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -65,7 +65,7 @@ class CERES_EXPORT CostFunction { public: CostFunction(); CostFunction(const CostFunction&) = delete; - void operator=(const CostFunction&) = delete; + CostFunction& operator=(const CostFunction&) = delete; virtual ~CostFunction(); @@ -124,6 +124,10 @@ class CERES_EXPORT CostFunction { int num_residuals() const { return num_residuals_; } protected: + // Prevent moving through the base class + CostFunction(CostFunction&& other) noexcept; + CostFunction& operator=(CostFunction&& other) noexcept; + std::vector* mutable_parameter_block_sizes() { return ¶meter_block_sizes_; } diff --git a/include/ceres/cost_function_to_functor.h b/include/ceres/cost_function_to_functor.h index e9592ed57..573508e9c 100644 --- a/include/ceres/cost_function_to_functor.h +++ b/include/ceres/cost_function_to_functor.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -94,8 +94,6 @@ #include "ceres/cost_function.h" #include "ceres/dynamic_cost_function_to_functor.h" -#include "ceres/internal/export.h" -#include "ceres/internal/fixed_array.h" #include "ceres/internal/parameter_dims.h" #include "ceres/types.h" #include "glog/logging.h" @@ -107,12 +105,16 @@ class CostFunctionToFunctor { public: // Takes ownership of cost_function. explicit CostFunctionToFunctor(CostFunction* cost_function) - : cost_functor_(cost_function) { - CHECK(cost_function != nullptr); + : CostFunctionToFunctor{std::unique_ptr{cost_function}} {} + + // Takes ownership of cost_function. + explicit CostFunctionToFunctor(std::unique_ptr cost_function) + : cost_functor_(std::move(cost_function)) { + CHECK(cost_functor_.function() != nullptr); CHECK(kNumResiduals > 0 || kNumResiduals == DYNAMIC); const std::vector& parameter_block_sizes = - cost_function->parameter_block_sizes(); + cost_functor_.function()->parameter_block_sizes(); const int num_parameter_blocks = ParameterDims::kNumParameterBlocks; CHECK_EQ(static_cast(parameter_block_sizes.size()), num_parameter_blocks); diff --git a/include/ceres/dynamic_autodiff_cost_function.h b/include/ceres/dynamic_autodiff_cost_function.h index e47f32f60..2b8724dbb 100644 --- a/include/ceres/dynamic_autodiff_cost_function.h +++ b/include/ceres/dynamic_autodiff_cost_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -35,6 +35,7 @@ #include #include #include +#include #include #include "ceres/dynamic_cost_function.h" @@ -65,8 +66,7 @@ namespace ceres { // also specify the sizes after creating the dynamic autodiff cost // function. For example: // -// DynamicAutoDiffCostFunction cost_function( -// new MyCostFunctor()); +// DynamicAutoDiffCostFunction cost_function; // cost_function.AddParameterBlock(5); // cost_function.AddParameterBlock(10); // cost_function.SetNumResiduals(21); @@ -79,13 +79,34 @@ namespace ceres { template class DynamicAutoDiffCostFunction final : public DynamicCostFunction { public: + // Constructs the CostFunctor on the heap and takes the ownership. + template >* = + nullptr> + explicit DynamicAutoDiffCostFunction(Args&&... args) + // NOTE We explicitly use direct initialization using parentheses instead + // of uniform initialization using braces to avoid narrowing conversion + // warnings. + : DynamicAutoDiffCostFunction{ + std::make_unique(std::forward(args)...)} {} + // Takes ownership by default. explicit DynamicAutoDiffCostFunction(CostFunctor* functor, Ownership ownership = TAKE_OWNERSHIP) - : functor_(functor), ownership_(ownership) {} + : DynamicAutoDiffCostFunction{std::unique_ptr{functor}, + ownership} {} - DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) - : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} + explicit DynamicAutoDiffCostFunction(std::unique_ptr functor) + : DynamicAutoDiffCostFunction{std::move(functor), TAKE_OWNERSHIP} {} + + DynamicAutoDiffCostFunction(const DynamicAutoDiffCostFunction& other) = + delete; + DynamicAutoDiffCostFunction& operator=( + const DynamicAutoDiffCostFunction& other) = delete; + DynamicAutoDiffCostFunction(DynamicAutoDiffCostFunction&& other) noexcept = + default; + DynamicAutoDiffCostFunction& operator=( + DynamicAutoDiffCostFunction&& other) noexcept = default; ~DynamicAutoDiffCostFunction() override { // Manually release pointer if configured to not take ownership @@ -267,6 +288,10 @@ class DynamicAutoDiffCostFunction final : public DynamicCostFunction { const CostFunctor& functor() const { return *functor_; } private: + explicit DynamicAutoDiffCostFunction(std::unique_ptr functor, + Ownership ownership) + : functor_(std::move(functor)), ownership_(ownership) {} + std::unique_ptr functor_; Ownership ownership_; }; @@ -276,10 +301,17 @@ class DynamicAutoDiffCostFunction final : public DynamicCostFunction { // instantiated as follows: // // new DynamicAutoDiffCostFunction{new MyCostFunctor{}}; +// new DynamicAutoDiffCostFunction{std::make_unique()}; // template +DynamicAutoDiffCostFunction(CostFunctor* functor) + -> DynamicAutoDiffCostFunction; +template DynamicAutoDiffCostFunction(CostFunctor* functor, Ownership ownership) -> DynamicAutoDiffCostFunction; +template +DynamicAutoDiffCostFunction(std::unique_ptr functor) + -> DynamicAutoDiffCostFunction; } // namespace ceres diff --git a/include/ceres/dynamic_cost_function_to_functor.h b/include/ceres/dynamic_cost_function_to_functor.h index cd124a2e2..45ed90f70 100644 --- a/include/ceres/dynamic_cost_function_to_functor.h +++ b/include/ceres/dynamic_cost_function_to_functor.h @@ -106,8 +106,14 @@ class CERES_EXPORT DynamicCostFunctionToFunctor { public: // Takes ownership of cost_function. explicit DynamicCostFunctionToFunctor(CostFunction* cost_function) - : cost_function_(cost_function) { - CHECK(cost_function != nullptr); + : DynamicCostFunctionToFunctor{ + std::unique_ptr{cost_function}} {} + + // Takes ownership of cost_function. + explicit DynamicCostFunctionToFunctor( + std::unique_ptr cost_function) + : cost_function_(std::move(cost_function)) { + CHECK(cost_function_ != nullptr); } bool operator()(double const* const* parameters, double* residuals) const { @@ -183,6 +189,8 @@ class CERES_EXPORT DynamicCostFunctionToFunctor { return true; } + CostFunction* function() const noexcept { return cost_function_.get(); } + private: std::unique_ptr cost_function_; }; diff --git a/include/ceres/dynamic_numeric_diff_cost_function.h b/include/ceres/dynamic_numeric_diff_cost_function.h index d9cd945b5..1ce384f26 100644 --- a/include/ceres/dynamic_numeric_diff_cost_function.h +++ b/include/ceres/dynamic_numeric_diff_cost_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -37,6 +37,7 @@ #include #include #include +#include #include #include "ceres/dynamic_cost_function.h" @@ -71,8 +72,7 @@ namespace ceres { // also specify the sizes after creating the // DynamicNumericDiffCostFunction. For example: // -// DynamicAutoDiffCostFunction cost_function( -// new MyCostFunctor()); +// DynamicAutoDiffCostFunction cost_function; // cost_function.AddParameterBlock(5); // cost_function.AddParameterBlock(10); // cost_function.SetNumResiduals(21); @@ -83,10 +83,34 @@ class DynamicNumericDiffCostFunction final : public DynamicCostFunction { const CostFunctor* functor, Ownership ownership = TAKE_OWNERSHIP, const NumericDiffOptions& options = NumericDiffOptions()) - : functor_(functor), ownership_(ownership), options_(options) {} + : DynamicNumericDiffCostFunction{ + std::unique_ptr{functor}, ownership, options} {} - DynamicNumericDiffCostFunction(DynamicNumericDiffCostFunction&& other) - : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} + explicit DynamicNumericDiffCostFunction( + std::unique_ptr functor, + const NumericDiffOptions& options = NumericDiffOptions()) + : DynamicNumericDiffCostFunction{ + std::move(functor), TAKE_OWNERSHIP, options} {} + + // Constructs the CostFunctor on the heap and takes the ownership. + template >* = + nullptr> + explicit DynamicNumericDiffCostFunction(Args&&... args) + // NOTE We explicitly use direct initialization using parentheses instead + // of uniform initialization using braces to avoid narrowing conversion + // warnings. + : DynamicNumericDiffCostFunction{ + std::make_unique(std::forward(args)...)} {} + + DynamicNumericDiffCostFunction(const DynamicNumericDiffCostFunction&) = + delete; + DynamicNumericDiffCostFunction& operator=( + const DynamicNumericDiffCostFunction&) = delete; + DynamicNumericDiffCostFunction( + DynamicNumericDiffCostFunction&& other) noexcept = default; + DynamicNumericDiffCostFunction& operator=( + DynamicNumericDiffCostFunction&& other) noexcept = default; ~DynamicNumericDiffCostFunction() override { if (ownership_ != TAKE_OWNERSHIP) { @@ -118,7 +142,7 @@ class DynamicNumericDiffCostFunction final : public DynamicCostFunction { int parameters_size = accumulate(block_sizes.begin(), block_sizes.end(), 0); std::vector parameters_copy(parameters_size); std::vector parameters_references_copy(block_sizes.size()); - parameters_references_copy[0] = ¶meters_copy[0]; + parameters_references_copy[0] = parameters_copy.data(); for (size_t block = 1; block < block_sizes.size(); ++block) { parameters_references_copy[block] = parameters_references_copy[block - 1] + block_sizes[block - 1]; @@ -139,14 +163,15 @@ class DynamicNumericDiffCostFunction final : public DynamicCostFunction { internal::DynamicParameterDims, ceres::DYNAMIC, ceres::DYNAMIC>:: - EvaluateJacobianForParameterBlock(functor_.get(), - residuals, - options_, - this->num_residuals(), - block, - block_sizes[block], - ¶meters_references_copy[0], - jacobians[block])) { + EvaluateJacobianForParameterBlock( + functor_.get(), + residuals, + options_, + this->num_residuals(), + block, + block_sizes[block], + parameters_references_copy.data(), + jacobians[block])) { return false; } } @@ -154,11 +179,45 @@ class DynamicNumericDiffCostFunction final : public DynamicCostFunction { } private: + explicit DynamicNumericDiffCostFunction( + std::unique_ptr functor, + Ownership ownership, + const NumericDiffOptions& options) + : functor_(std::move(functor)), + ownership_(ownership), + options_(options) {} + std::unique_ptr functor_; Ownership ownership_; NumericDiffOptions options_; }; +// Deduction guide that allows the user to avoid explicitly specifying the +// template parameter of DynamicNumericDiffCostFunction. The class can instead +// be instantiated as follows: +// +// new DynamicNumericDiffCostFunction{new MyCostFunctor{}}; +// new DynamicNumericDiffCostFunction{std::make_unique()}; +// +template +DynamicNumericDiffCostFunction(CostFunctor* functor) + -> DynamicNumericDiffCostFunction; +template +DynamicNumericDiffCostFunction(CostFunctor* functor, Ownership ownership) + -> DynamicNumericDiffCostFunction; +template +DynamicNumericDiffCostFunction(CostFunctor* functor, + Ownership ownership, + const NumericDiffOptions& options) + -> DynamicNumericDiffCostFunction; +template +DynamicNumericDiffCostFunction(std::unique_ptr functor) + -> DynamicNumericDiffCostFunction; +template +DynamicNumericDiffCostFunction(std::unique_ptr functor, + const NumericDiffOptions& options) + -> DynamicNumericDiffCostFunction; + } // namespace ceres #endif // CERES_PUBLIC_DYNAMIC_AUTODIFF_COST_FUNCTION_H_ diff --git a/include/ceres/numeric_diff_cost_function.h b/include/ceres/numeric_diff_cost_function.h index 00a7d53e3..f2a377b47 100644 --- a/include/ceres/numeric_diff_cost_function.h +++ b/include/ceres/numeric_diff_cost_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -149,9 +149,8 @@ // The numerically differentiated version of a cost function for a cost function // can be constructed as follows: // -// CostFunction* cost_function -// = new NumericDiffCostFunction( -// new MyCostFunction(...), TAKE_OWNERSHIP); +// auto* cost_function +// = new NumericDiffCostFunction(); // // where MyCostFunction has 1 residual and 2 parameter blocks with sizes 4 and 8 // respectively. Look at the tests for a more detailed example. @@ -163,6 +162,7 @@ #include #include +#include #include "Eigen/Dense" #include "ceres/cost_function.h" @@ -171,7 +171,6 @@ #include "ceres/numeric_diff_options.h" #include "ceres/sized_cost_function.h" #include "ceres/types.h" -#include "glog/logging.h" namespace ceres { @@ -187,16 +186,40 @@ class NumericDiffCostFunction final Ownership ownership = TAKE_OWNERSHIP, int num_residuals = kNumResiduals, const NumericDiffOptions& options = NumericDiffOptions()) - : functor_(functor), ownership_(ownership), options_(options) { - if (kNumResiduals == DYNAMIC) { - SizedCostFunction::set_num_residuals(num_residuals); - } - } + : NumericDiffCostFunction{std::unique_ptr{functor}, + ownership, + num_residuals, + options} {} - NumericDiffCostFunction(NumericDiffCostFunction&& other) - : functor_(std::move(other.functor_)), ownership_(other.ownership_) {} + explicit NumericDiffCostFunction( + std::unique_ptr functor, + int num_residuals = kNumResiduals, + const NumericDiffOptions& options = NumericDiffOptions()) + : NumericDiffCostFunction{ + std::move(functor), TAKE_OWNERSHIP, num_residuals, options} {} - virtual ~NumericDiffCostFunction() { + // Constructs the CostFunctor on the heap and takes the ownership. + // Invocable only if the number of residuals is known at compile-time. + template >* = + nullptr> + explicit NumericDiffCostFunction(Args&&... args) + // NOTE We explicitly use direct initialization using parentheses instead + // of uniform initialization using braces to avoid narrowing conversion + // warnings. + : NumericDiffCostFunction{ + std::make_unique(std::forward(args)...), + TAKE_OWNERSHIP} {} + + NumericDiffCostFunction(NumericDiffCostFunction&& other) noexcept = default; + NumericDiffCostFunction& operator=(NumericDiffCostFunction&& other) noexcept = + default; + NumericDiffCostFunction(const NumericDiffCostFunction&) = delete; + NumericDiffCostFunction& operator=(const NumericDiffCostFunction&) = delete; + + ~NumericDiffCostFunction() override { if (ownership_ != TAKE_OWNERSHIP) { functor_.release(); } @@ -250,6 +273,16 @@ class NumericDiffCostFunction final const CostFunctor& functor() const { return *functor_; } private: + explicit NumericDiffCostFunction(std::unique_ptr functor, + Ownership ownership, + [[maybe_unused]] int num_residuals, + const NumericDiffOptions& options) + : functor_(std::move(functor)), ownership_(ownership), options_(options) { + if constexpr (kNumResiduals == DYNAMIC) { + SizedCostFunction::set_num_residuals(num_residuals); + } + } + std::unique_ptr functor_; Ownership ownership_; NumericDiffOptions options_; diff --git a/include/ceres/numeric_diff_first_order_function.h b/include/ceres/numeric_diff_first_order_function.h index ccd420cfb..525f197c1 100644 --- a/include/ceres/numeric_diff_first_order_function.h +++ b/include/ceres/numeric_diff_first_order_function.h @@ -33,6 +33,8 @@ #include #include +#include +#include #include "ceres/first_order_function.h" #include "ceres/internal/eigen.h" @@ -115,21 +117,41 @@ template class NumericDiffFirstOrderFunction final : public FirstOrderFunction { public: + template >* = nullptr> + explicit NumericDiffFirstOrderFunction(Args&&... args) + : NumericDiffFirstOrderFunction{std::make_unique( + std::forward(args)...)} {} + + NumericDiffFirstOrderFunction(const NumericDiffFirstOrderFunction&) = delete; + NumericDiffFirstOrderFunction& operator=( + const NumericDiffFirstOrderFunction&) = delete; + NumericDiffFirstOrderFunction( + NumericDiffFirstOrderFunction&& other) noexcept = default; + NumericDiffFirstOrderFunction& operator=( + NumericDiffFirstOrderFunction&& other) noexcept = default; + // Constructor for the case where the parameter size is known at compile time. explicit NumericDiffFirstOrderFunction( FirstOrderFunctor* functor, Ownership ownership = TAKE_OWNERSHIP, const NumericDiffOptions& options = NumericDiffOptions()) - : functor_(functor), - num_parameters_(kNumParameters), - ownership_(ownership), - options_(options) { - static_assert(kNumParameters != DYNAMIC, - "Number of parameters must be static when defined via the " - "template parameter. Use the other constructor for " - "dynamically sized functions."); - static_assert(kNumParameters > 0, "kNumParameters must be positive"); - } + : NumericDiffFirstOrderFunction{ + std::unique_ptr{functor}, + kNumParameters, + ownership, + options, + FIXED_INIT} {} + + // Constructor for the case where the parameter size is known at compile time. + explicit NumericDiffFirstOrderFunction( + std::unique_ptr functor, + const NumericDiffOptions& options = NumericDiffOptions()) + : NumericDiffFirstOrderFunction{ + std::move(functor), kNumParameters, TAKE_OWNERSHIP, FIXED_INIT} {} // Constructor for the case where the parameter size is specified at run time. explicit NumericDiffFirstOrderFunction( @@ -137,17 +159,24 @@ class NumericDiffFirstOrderFunction final : public FirstOrderFunction { int num_parameters, Ownership ownership = TAKE_OWNERSHIP, const NumericDiffOptions& options = NumericDiffOptions()) - : functor_(functor), - num_parameters_(num_parameters), - ownership_(ownership), - options_(options) { - static_assert( - kNumParameters == DYNAMIC, - "Template parameter must be DYNAMIC when using this constructor. If " - "you want to provide the number of parameters statically use the other " - "constructor."); - CHECK_GT(num_parameters, 0); - } + : NumericDiffFirstOrderFunction{ + std::unique_ptr{functor}, + num_parameters, + ownership, + options, + DYNAMIC_INIT} {} + + // Constructor for the case where the parameter size is specified at run time. + explicit NumericDiffFirstOrderFunction( + std::unique_ptr functor, + int num_parameters, + Ownership ownership = TAKE_OWNERSHIP, + const NumericDiffOptions& options = NumericDiffOptions()) + : NumericDiffFirstOrderFunction{std::move(functor), + num_parameters, + ownership, + options, + DYNAMIC_INIT} {} ~NumericDiffFirstOrderFunction() override { if (ownership_ != TAKE_OWNERSHIP) { @@ -205,10 +234,36 @@ class NumericDiffFirstOrderFunction final : public FirstOrderFunction { const FirstOrderFunctor& functor() const { return *functor_; } private: + // Tags used to differentiate between dynamic and fixed size constructor + // delegate invocations. + static constexpr std::integral_constant DYNAMIC_INIT{}; + static constexpr std::integral_constant FIXED_INIT{}; + + template + explicit NumericDiffFirstOrderFunction( + std::unique_ptr functor, + int num_parameters, + Ownership ownership, + const NumericDiffOptions& options, + InitTag /*unused*/) + : functor_(std::move(functor)), + num_parameters_(num_parameters), + ownership_(ownership), + options_(options) { + static_assert( + kNumParameters == FIXED_INIT, + "Template parameter must be DYNAMIC when using this constructor. If " + "you want to provide the number of parameters statically use the other " + "constructor."); + if constexpr (InitTag::value == DYNAMIC_INIT) { + CHECK_GT(num_parameters, 0); + } + } + std::unique_ptr functor_; - const int num_parameters_; - const Ownership ownership_; - const NumericDiffOptions options_; + int num_parameters_; + Ownership ownership_; + NumericDiffOptions options_; }; } // namespace ceres diff --git a/include/ceres/sized_cost_function.h b/include/ceres/sized_cost_function.h index d594cfe7a..8928c19c4 100644 --- a/include/ceres/sized_cost_function.h +++ b/include/ceres/sized_cost_function.h @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -38,9 +38,10 @@ #ifndef CERES_PUBLIC_SIZED_COST_FUNCTION_H_ #define CERES_PUBLIC_SIZED_COST_FUNCTION_H_ +#include + #include "ceres/cost_function.h" #include "ceres/types.h" -#include "glog/logging.h" #include "internal/parameter_dims.h" namespace ceres { @@ -58,7 +59,7 @@ class SizedCostFunction : public CostFunction { SizedCostFunction() { set_num_residuals(kNumResiduals); - *mutable_parameter_block_sizes() = std::vector{Ns...}; + *mutable_parameter_block_sizes() = std::initializer_list{Ns...}; } // Subclasses must implement Evaluate(). diff --git a/internal/ceres/autodiff_cost_function_test.cc b/internal/ceres/autodiff_cost_function_test.cc index f4564862f..dca67bacf 100644 --- a/internal/ceres/autodiff_cost_function_test.cc +++ b/internal/ceres/autodiff_cost_function_test.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -178,4 +178,24 @@ TEST(AutoDiffCostFunction, PartiallyFilledResidualShouldFailEvaluation) { EXPECT_FALSE(IsArrayValid(2, residuals)); } +TEST(AutodiffCostFunction, ArgumentForwarding) { + // No narrowing conversion warning should be emitted + auto cost_function1 = + std::make_unique>(1); + auto cost_function2 = + std::make_unique>(2.0); + // Default constructible functor + auto cost_function3 = + std::make_unique>(); +} + +TEST(AutodiffCostFunction, UniquePtrCtor) { + auto cost_function1 = + std::make_unique>( + std::make_unique(1)); + auto cost_function2 = + std::make_unique>( + std::make_unique(2.0)); +} + } // namespace ceres::internal diff --git a/internal/ceres/cost_function.cc b/internal/ceres/cost_function.cc index abd53dde0..543348f85 100644 --- a/internal/ceres/cost_function.cc +++ b/internal/ceres/cost_function.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -33,6 +33,8 @@ namespace ceres { +CostFunction::CostFunction(CostFunction&& other) noexcept = default; +CostFunction& CostFunction::operator=(CostFunction&& other) noexcept = default; CostFunction::CostFunction() : num_residuals_(0) {} CostFunction::~CostFunction() = default; diff --git a/internal/ceres/cost_function_to_functor_test.cc b/internal/ceres/cost_function_to_functor_test.cc index 817d6f341..c3fed66f5 100644 --- a/internal/ceres/cost_function_to_functor_test.cc +++ b/internal/ceres/cost_function_to_functor_test.cc @@ -391,4 +391,39 @@ TEST(CostFunctionToFunctor, DynamicCostFunctionToFunctor) { ExpectCostFunctionsAreEqual(cost_function, *actual_cost_function); } +TEST(CostFunctionToFunctor, UniquePtrArgumentForwarding) { + auto cost_function = std::make_unique< + AutoDiffCostFunction, + ceres::DYNAMIC, + 2, + 2>>( + std::make_unique>( + std::make_unique< + AutoDiffCostFunction>()), + 2); + + auto actual_cost_function = std::make_unique< + AutoDiffCostFunction>(); + ExpectCostFunctionsAreEqual(*cost_function, *actual_cost_function); +} + +TEST(CostFunctionToFunctor, DynamicCostFunctionToFunctorUniquePtr) { + auto actual_cost_function = std::make_unique< + DynamicAutoDiffCostFunction>(); + actual_cost_function->AddParameterBlock(2); + actual_cost_function->AddParameterBlock(2); + actual_cost_function->SetNumResiduals(2); + + // Use deduction guides for a more compact variable definition + DynamicAutoDiffCostFunction cost_function( + std::make_unique( + std::move(actual_cost_function))); + cost_function.AddParameterBlock(2); + cost_function.AddParameterBlock(2); + cost_function.SetNumResiduals(2); + + ExpectCostFunctionsAreEqual(cost_function, + *cost_function.functor().function()); +} + } // namespace ceres::internal diff --git a/internal/ceres/dynamic_autodiff_cost_function_test.cc b/internal/ceres/dynamic_autodiff_cost_function_test.cc index 1cf83a59e..d42b3e902 100644 --- a/internal/ceres/dynamic_autodiff_cost_function_test.cc +++ b/internal/ceres/dynamic_autodiff_cost_function_test.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -806,6 +806,16 @@ TEST(DynamicAutoDiffCostFunction, TEST(DynamicAutoDiffCostFunctionTest, DeductionTemplateCompilationTest) { // Ensure deduction guide to be working (void)DynamicAutoDiffCostFunction(new MyCostFunctor()); + (void)DynamicAutoDiffCostFunction(new MyCostFunctor(), TAKE_OWNERSHIP); + (void)DynamicAutoDiffCostFunction(std::make_unique()); +} + +TEST(DynamicAutoDiffCostFunctionTest, ArgumentForwarding) { + (void)DynamicAutoDiffCostFunction(); +} + +TEST(DynamicAutoDiffCostFunctionTest, UniquePtr) { + (void)DynamicAutoDiffCostFunction(std::make_unique()); } } // namespace ceres::internal diff --git a/internal/ceres/dynamic_numeric_diff_cost_function_test.cc b/internal/ceres/dynamic_numeric_diff_cost_function_test.cc index aba90e27e..4f408ed89 100644 --- a/internal/ceres/dynamic_numeric_diff_cost_function_test.cc +++ b/internal/ceres/dynamic_numeric_diff_cost_function_test.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -508,4 +508,24 @@ TEST_F(ThreeParameterCostFunctorTest, } } +TEST(DynamicNumericdiffCostFunctionTest, DeductionTemplateCompilationTest) { + // Ensure deduction guide to be working + (void)DynamicNumericDiffCostFunction{std::make_unique()}; + (void)DynamicNumericDiffCostFunction{std::make_unique(), + NumericDiffOptions{}}; + (void)DynamicNumericDiffCostFunction{new MyCostFunctor}; + (void)DynamicNumericDiffCostFunction{new MyCostFunctor, TAKE_OWNERSHIP}; + (void)DynamicNumericDiffCostFunction{ + new MyCostFunctor, TAKE_OWNERSHIP, NumericDiffOptions{}}; +} + +TEST(DynamicNumericdiffCostFunctionTest, ArgumentForwarding) { + (void)DynamicNumericDiffCostFunction(); +} + +TEST(DynamicAutoDiffCostFunctionTest, UniquePtr) { + (void)DynamicNumericDiffCostFunction( + std::make_unique()); +} + } // namespace ceres::internal diff --git a/internal/ceres/numeric_diff_cost_function_test.cc b/internal/ceres/numeric_diff_cost_function_test.cc index 46955484b..0c9074a10 100644 --- a/internal/ceres/numeric_diff_cost_function_test.cc +++ b/internal/ceres/numeric_diff_cost_function_test.cc @@ -1,5 +1,5 @@ // Ceres Solver - A fast non-linear least squares minimizer -// Copyright 2023 Google Inc. All rights reserved. +// Copyright 2024 Google Inc. All rights reserved. // http://ceres-solver.org/ // // Redistribution and use in source and binary forms, with or without @@ -46,8 +46,7 @@ #include "glog/logging.h" #include "gtest/gtest.h" -namespace ceres { -namespace internal { +namespace ceres::internal { TEST(NumericDiffCostFunction, EasyCaseFunctorCentralDifferences) { auto cost_function = @@ -438,5 +437,28 @@ TEST(NumericDiffCostFunction, ParameterBlockConstant) { } } -} // namespace internal -} // namespace ceres +struct MultiArgFunctor { + explicit MultiArgFunctor(int a, double c) {} + template + bool operator()(const T* params, T* residuals) const noexcept { + return false; + } +}; + +TEST(NumericDiffCostFunction, ArgumentForwarding) { + auto cost_function1 = std::make_unique< + NumericDiffCostFunction>(); + auto cost_function2 = + std::make_unique>( + 1, 2); +} + +TEST(NumericDiffCostFunction, UniquePtrCtor) { + auto cost_function1 = + std::make_unique>( + std::make_unique()); + auto cost_function2 = std::make_unique< + NumericDiffCostFunction>(); +} + +} // namespace ceres::internal