123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398 |
- #ifndef CERES_PUBLIC_TINY_SOLVER_H_
- #define CERES_PUBLIC_TINY_SOLVER_H_
- #include <cassert>
- #include <cmath>
- #include "Eigen/Dense"
- namespace ceres {
- template <typename Function,
- typename LinearSolver =
- Eigen::LDLT<Eigen::Matrix<typename Function::Scalar,
- Function::NUM_PARAMETERS,
- Function::NUM_PARAMETERS>>>
- class TinySolver {
- public:
-
-
- EIGEN_MAKE_ALIGNED_OPERATOR_NEW
- enum {
- NUM_RESIDUALS = Function::NUM_RESIDUALS,
- NUM_PARAMETERS = Function::NUM_PARAMETERS
- };
- using Scalar = typename Function::Scalar;
- using Parameters = typename Eigen::Matrix<Scalar, NUM_PARAMETERS, 1>;
- enum Status {
-
- GRADIENT_TOO_SMALL,
-
- RELATIVE_STEP_SIZE_TOO_SMALL,
-
- COST_TOO_SMALL,
-
- HIT_MAX_ITERATIONS,
-
- COST_CHANGE_TOO_SMALL,
-
- };
- struct Options {
- int max_num_iterations = 50;
-
- Scalar gradient_tolerance = 1e-10;
-
- Scalar parameter_tolerance = 1e-8;
-
- Scalar function_tolerance = 1e-6;
-
- Scalar cost_threshold = std::numeric_limits<Scalar>::epsilon();
- Scalar initial_trust_region_radius = 1e4;
- };
- struct Summary {
-
- Scalar initial_cost = -1;
-
- Scalar final_cost = -1;
-
- Scalar gradient_max_norm = -1;
- int iterations = -1;
- Status status = HIT_MAX_ITERATIONS;
- };
- bool Update(const Function& function, const Parameters& x) {
- if (!function(x.data(), residuals_.data(), jacobian_.data())) {
- return false;
- }
- residuals_ = -residuals_;
-
-
- if (summary.iterations == 0) {
-
-
-
-
- jacobi_scaling_ = 1.0 / (1.0 + jacobian_.colwise().norm().array());
- }
-
-
-
-
-
- jacobian_ = jacobian_ * jacobi_scaling_.asDiagonal();
- jtj_ = jacobian_.transpose() * jacobian_;
- g_ = jacobian_.transpose() * residuals_;
- summary.gradient_max_norm = g_.array().abs().maxCoeff();
- cost_ = residuals_.squaredNorm() / 2;
- return true;
- }
- const Summary& Solve(const Function& function, Parameters* x_and_min) {
- Initialize<NUM_RESIDUALS, NUM_PARAMETERS>(function);
- assert(x_and_min);
- Parameters& x = *x_and_min;
- summary = Summary();
- summary.iterations = 0;
-
- Update(function, x);
- summary.initial_cost = cost_;
- summary.final_cost = cost_;
- if (summary.gradient_max_norm < options.gradient_tolerance) {
- summary.status = GRADIENT_TOO_SMALL;
- return summary;
- }
- if (cost_ < options.cost_threshold) {
- summary.status = COST_TOO_SMALL;
- return summary;
- }
- Scalar u = 1.0 / options.initial_trust_region_radius;
- Scalar v = 2;
- for (summary.iterations = 1;
- summary.iterations < options.max_num_iterations;
- summary.iterations++) {
- jtj_regularized_ = jtj_;
- const Scalar min_diagonal = 1e-6;
- const Scalar max_diagonal = 1e32;
- for (int i = 0; i < dx_.rows(); ++i) {
- jtj_regularized_(i, i) +=
- u * (std::min)((std::max)(jtj_(i, i), min_diagonal), max_diagonal);
- }
-
- linear_solver_.compute(jtj_regularized_);
- lm_step_ = linear_solver_.solve(g_);
- dx_ = jacobi_scaling_.asDiagonal() * lm_step_;
-
-
- const Scalar parameter_tolerance =
- options.parameter_tolerance *
- (x.norm() + options.parameter_tolerance);
- if (dx_.norm() < parameter_tolerance) {
- summary.status = RELATIVE_STEP_SIZE_TOO_SMALL;
- break;
- }
- x_new_ = x + dx_;
-
-
- function(&x_new_[0], &f_x_new_[0], nullptr);
- const Scalar cost_change = (2 * cost_ - f_x_new_.squaredNorm());
-
- const Scalar model_cost_change = lm_step_.dot(2 * g_ - jtj_ * lm_step_);
-
-
-
- Scalar rho(cost_change / model_cost_change);
- if (rho > 0) {
-
-
- x = x_new_;
- if (std::abs(cost_change) < options.function_tolerance) {
- cost_ = f_x_new_.squaredNorm() / 2;
- summary.status = COST_CHANGE_TOO_SMALL;
- break;
- }
-
- Update(function, x);
- if (summary.gradient_max_norm < options.gradient_tolerance) {
- summary.status = GRADIENT_TOO_SMALL;
- break;
- }
- if (cost_ < options.cost_threshold) {
- summary.status = COST_TOO_SMALL;
- break;
- }
- Scalar tmp = Scalar(2 * rho - 1);
- u = u * (std::max)(Scalar(1 / 3.), Scalar(1) - tmp * tmp * tmp);
- v = 2;
- } else {
-
-
-
- if (std::abs(cost_change) < options.function_tolerance) {
-
- summary.status = COST_CHANGE_TOO_SMALL;
- break;
- }
-
- u *= v;
- v *= 2;
- }
- }
- summary.final_cost = cost_;
- return summary;
- }
- Options options;
- Summary summary;
- private:
-
-
- LinearSolver linear_solver_;
- Scalar cost_;
- Parameters dx_, x_new_, g_, jacobi_scaling_, lm_step_;
- Eigen::Matrix<Scalar, NUM_RESIDUALS, 1> residuals_, f_x_new_;
- Eigen::Matrix<Scalar, NUM_RESIDUALS, NUM_PARAMETERS> jacobian_;
- Eigen::Matrix<Scalar, NUM_PARAMETERS, NUM_PARAMETERS> jtj_, jtj_regularized_;
-
- template <bool Condition, typename T>
- struct enable_if;
- template <typename T>
- struct enable_if<true, T> {
- using type = T;
- };
-
- template <int R, int P>
- typename enable_if<(R == Eigen::Dynamic && P == Eigen::Dynamic), void>::type
- Initialize(const Function& function) {
- Initialize(function.NumResiduals(), function.NumParameters());
- }
-
-
- template <int R, int P>
- typename enable_if<(R == Eigen::Dynamic && P != Eigen::Dynamic), void>::type
- Initialize(const Function& function) {
- Initialize(function.NumResiduals(), P);
- }
-
-
- template <int R, int P>
- typename enable_if<(R != Eigen::Dynamic && P == Eigen::Dynamic), void>::type
- Initialize(const Function& function) {
- Initialize(R, function.NumParameters());
- }
-
- template <int R, int P>
- typename enable_if<(R != Eigen::Dynamic && P != Eigen::Dynamic), void>::type
- Initialize(const Function& ) {}
- void Initialize(int num_residuals, int num_parameters) {
- dx_.resize(num_parameters);
- x_new_.resize(num_parameters);
- g_.resize(num_parameters);
- jacobi_scaling_.resize(num_parameters);
- lm_step_.resize(num_parameters);
- residuals_.resize(num_residuals);
- f_x_new_.resize(num_residuals);
- jacobian_.resize(num_residuals, num_parameters);
- jtj_.resize(num_parameters, num_parameters);
- jtj_regularized_.resize(num_parameters, num_parameters);
- }
- };
- }
- #endif
|