| // Ceres Solver - A fast non-linear least squares minimizer | |
| // Copyright 2023 Google Inc. All rights reserved. | |
| // http://ceres-solver.org/ | |
| // | |
| // Redistribution and use in source and binary forms, with or without | |
| // modification, are permitted provided that the following conditions are met: | |
| // | |
| // * Redistributions of source code must retain the above copyright notice, | |
| // this list of conditions and the following disclaimer. | |
| // * Redistributions in binary form must reproduce the above copyright notice, | |
| // this list of conditions and the following disclaimer in the documentation | |
| // and/or other materials provided with the distribution. | |
| // * Neither the name of Google Inc. nor the names of its contributors may be | |
| // used to endorse or promote products derived from this software without | |
| // specific prior written permission. | |
| // | |
| // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" | |
| // AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | |
| // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | |
| // ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE | |
| // LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR | |
| // CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF | |
| // SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS | |
| // INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN | |
| // CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) | |
| // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE | |
| // POSSIBILITY OF SUCH DAMAGE. | |
| // | |
| // Author: sameeragarwal@google.com (Sameer Agarwal) | |
| #include "ceres/trust_region_step_evaluator.h" | |
| #include <algorithm> | |
| #include <limits> | |
| namespace ceres::internal { | |
| TrustRegionStepEvaluator::TrustRegionStepEvaluator( | |
| const double initial_cost, const int max_consecutive_nonmonotonic_steps) | |
| : max_consecutive_nonmonotonic_steps_(max_consecutive_nonmonotonic_steps), | |
| minimum_cost_(initial_cost), | |
| current_cost_(initial_cost), | |
| reference_cost_(initial_cost), | |
| candidate_cost_(initial_cost), | |
| accumulated_reference_model_cost_change_(0.0), | |
| accumulated_candidate_model_cost_change_(0.0), | |
| num_consecutive_nonmonotonic_steps_(0) {} | |
| double TrustRegionStepEvaluator::StepQuality( | |
| const double cost, const double model_cost_change) const { | |
| // If the function evaluation for this step was a failure, in which | |
| // case the TrustRegionMinimizer would have set the cost to | |
| // std::numeric_limits<double>::max(). In this case, the division by | |
| // model_cost_change can result in an overflow. To prevent that from | |
| // happening, we will deal with this case explicitly. | |
| if (cost >= std::numeric_limits<double>::max()) { | |
| return std::numeric_limits<double>::lowest(); | |
| } | |
| const double relative_decrease = (current_cost_ - cost) / model_cost_change; | |
| const double historical_relative_decrease = | |
| (reference_cost_ - cost) / | |
| (accumulated_reference_model_cost_change_ + model_cost_change); | |
| return std::max(relative_decrease, historical_relative_decrease); | |
| } | |
| void TrustRegionStepEvaluator::StepAccepted(const double cost, | |
| const double model_cost_change) { | |
| // Algorithm 10.1.2 from Trust Region Methods by Conn, Gould & | |
| // Toint. | |
| // | |
| // Step 3a | |
| current_cost_ = cost; | |
| accumulated_candidate_model_cost_change_ += model_cost_change; | |
| accumulated_reference_model_cost_change_ += model_cost_change; | |
| // Step 3b. | |
| if (current_cost_ < minimum_cost_) { | |
| minimum_cost_ = current_cost_; | |
| num_consecutive_nonmonotonic_steps_ = 0; | |
| candidate_cost_ = current_cost_; | |
| accumulated_candidate_model_cost_change_ = 0.0; | |
| } else { | |
| // Step 3c. | |
| ++num_consecutive_nonmonotonic_steps_; | |
| if (current_cost_ > candidate_cost_) { | |
| candidate_cost_ = current_cost_; | |
| accumulated_candidate_model_cost_change_ = 0.0; | |
| } | |
| } | |
| // Step 3d. | |
| // | |
| // At this point we have made too many non-monotonic steps and | |
| // we are going to reset the value of the reference iterate so | |
| // as to force the algorithm to descend. | |
| // | |
| // Note: In the original algorithm by Toint, this step was only | |
| // executed if the step was non-monotonic, but that would not handle | |
| // the case of max_consecutive_nonmonotonic_steps = 0. The small | |
| // modification of doing this always handles that corner case | |
| // correctly. | |
| if (num_consecutive_nonmonotonic_steps_ == | |
| max_consecutive_nonmonotonic_steps_) { | |
| reference_cost_ = candidate_cost_; | |
| accumulated_reference_model_cost_change_ = | |
| accumulated_candidate_model_cost_change_; | |
| } | |
| } | |
| } // namespace ceres::internal |