OR-Tools  9.3
termination.cc
Go to the documentation of this file.
1// Copyright 2010-2021 Google LLC
2// Licensed under the Apache License, Version 2.0 (the "License");
3// you may not use this file except in compliance with the License.
4// You may obtain a copy of the License at
5//
6// http://www.apache.org/licenses/LICENSE-2.0
7//
8// Unless required by applicable law or agreed to in writing, software
9// distributed under the License is distributed on an "AS IS" BASIS,
10// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
11// See the License for the specific language governing permissions and
12// limitations under the License.
13
15
16#include <cmath>
17#include <limits>
18
19#include "absl/types/optional.h"
21#include "ortools/pdlp/solve_log.pb.h"
22#include "ortools/pdlp/solvers.pb.h"
23
25
26namespace {
27
28bool OptimalityCriteriaMet(const OptimalityNorm optimality_norm,
29 const double abs_tol, const double rel_tol,
30 const ConvergenceInformation& stats,
31 const QuadraticProgramBoundNorms& bound_norms) {
32 const double abs_obj =
33 std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
34 const double gap =
35 std::abs(stats.primal_objective() - stats.dual_objective());
36
37 double primal_err;
38 double primal_err_baseline;
39 double dual_err;
40 double dual_err_baseline;
41
42 switch (optimality_norm) {
43 case OPTIMALITY_NORM_L_INF:
44 primal_err = stats.l_inf_primal_residual();
45 primal_err_baseline = bound_norms.l_inf_norm_constraint_bounds;
46 dual_err = stats.l_inf_dual_residual();
47 dual_err_baseline = bound_norms.l_inf_norm_primal_linear_objective;
48 break;
49 case OPTIMALITY_NORM_L2:
50 primal_err = stats.l2_primal_residual();
51 primal_err_baseline = bound_norms.l2_norm_constraint_bounds;
52 dual_err = stats.l2_dual_residual();
53 dual_err_baseline = bound_norms.l2_norm_primal_linear_objective;
54 break;
55 default:
56 LOG(FATAL) << "Invalid optimality_norm value "
57 << OptimalityNorm_Name(optimality_norm);
58 }
59
60 return dual_err <= abs_tol + rel_tol * dual_err_baseline &&
61 primal_err <= abs_tol + rel_tol * primal_err_baseline &&
62 std::isfinite(abs_obj) && gap <= abs_tol + rel_tol * abs_obj;
63}
64
65// Checks if the criteria for primal infeasibility are approximately
66// satisfied; see https://developers.google.com/optimization/lp/pdlp_math for
67// more details.
68bool PrimalInfeasibilityCriteriaMet(double eps_primal_infeasible,
69 const InfeasibilityInformation& stats) {
70 if (stats.dual_ray_objective() <= 0.0) return false;
71 return stats.max_dual_ray_infeasibility() / stats.dual_ray_objective() <=
72 eps_primal_infeasible;
73}
74
75// Checks if the criteria for dual infeasibility are approximately satisfied;
76// see https://developers.google.com/optimization/lp/pdlp_math for more details.
77bool DualInfeasibilityCriteriaMet(double eps_dual_infeasible,
78 const InfeasibilityInformation& stats) {
79 if (stats.primal_ray_linear_objective() >= 0.0) return false;
80 return (stats.max_primal_ray_infeasibility() /
81 -stats.primal_ray_linear_objective() <=
82 eps_dual_infeasible) &&
83 (stats.primal_ray_quadratic_norm() /
84 -stats.primal_ray_linear_objective() <=
85 eps_dual_infeasible);
86}
87
88} // namespace
89
90absl::optional<TerminationReasonAndPointType> CheckTerminationCriteria(
91 const TerminationCriteria& criteria, const IterationStats& stats,
92 const QuadraticProgramBoundNorms& bound_norms,
93 const bool force_numerical_termination) {
94 for (const auto& convergence_stats : stats.convergence_information()) {
95 if (OptimalityCriteriaMet(
96 criteria.optimality_norm(), criteria.eps_optimal_absolute(),
97 criteria.eps_optimal_relative(), convergence_stats, bound_norms)) {
99 .reason = TERMINATION_REASON_OPTIMAL,
100 .type = convergence_stats.candidate_type()};
101 }
102 }
103 for (const auto& infeasibility_stats : stats.infeasibility_information()) {
104 if (PrimalInfeasibilityCriteriaMet(criteria.eps_primal_infeasible(),
105 infeasibility_stats)) {
107 .reason = TERMINATION_REASON_PRIMAL_INFEASIBLE,
108 .type = infeasibility_stats.candidate_type()};
109 }
110 if (DualInfeasibilityCriteriaMet(criteria.eps_dual_infeasible(),
111 infeasibility_stats)) {
113 .reason = TERMINATION_REASON_DUAL_INFEASIBLE,
114 .type = infeasibility_stats.candidate_type()};
115 }
116 }
117 if (stats.iteration_number() >= criteria.iteration_limit()) {
119 .reason = TERMINATION_REASON_ITERATION_LIMIT, .type = POINT_TYPE_NONE};
120 }
121 if (stats.cumulative_kkt_matrix_passes() >=
122 criteria.kkt_matrix_pass_limit()) {
124 .reason = TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT,
125 .type = POINT_TYPE_NONE};
126 }
127 if (stats.cumulative_time_sec() >= criteria.time_sec_limit()) {
129 .reason = TERMINATION_REASON_TIME_LIMIT, .type = POINT_TYPE_NONE};
130 }
131 if (force_numerical_termination) {
133 .reason = TERMINATION_REASON_NUMERICAL_ERROR, .type = POINT_TYPE_NONE};
134 }
135 return absl::nullopt;
136}
137
139 const QuadraticProgramStats& stats) {
140 return {
141 .l2_norm_primal_linear_objective = stats.objective_vector_l2_norm(),
142 .l2_norm_constraint_bounds = stats.combined_bounds_l2_norm(),
143 .l_inf_norm_primal_linear_objective = stats.objective_vector_abs_max(),
144 .l_inf_norm_constraint_bounds = stats.combined_bounds_max()};
145}
146
148 const double eps_optimal_absolute, const double eps_optimal_relative,
149 const QuadraticProgramBoundNorms& norms,
150 const ConvergenceInformation& stats) {
151 const double eps_ratio = eps_optimal_relative == 0.0
152 ? std::numeric_limits<double>::infinity()
153 : eps_optimal_absolute / eps_optimal_relative;
156 stats.l_inf_primal_residual() /
157 (eps_ratio + norms.l_inf_norm_constraint_bounds);
159 stats.l2_primal_residual() /
160 (eps_ratio + norms.l2_norm_constraint_bounds);
162 stats.l_inf_dual_residual() /
163 (eps_ratio + norms.l_inf_norm_primal_linear_objective);
165 stats.l2_dual_residual() /
166 (eps_ratio + norms.l2_norm_primal_linear_objective);
167 const double abs_obj =
168 std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
169 const double gap = stats.primal_objective() - stats.dual_objective();
170 info.relative_optimality_gap = gap / (eps_ratio + abs_obj);
171
172 return info;
173}
174
175} // namespace operations_research::pdlp
#define LOG(severity)
Definition: base/logging.h:420
const int FATAL
Definition: log_severity.h:32
absl::optional< TerminationReasonAndPointType > CheckTerminationCriteria(const TerminationCriteria &criteria, const IterationStats &stats, const QuadraticProgramBoundNorms &bound_norms, const bool force_numerical_termination)
Definition: termination.cc:90
RelativeConvergenceInformation ComputeRelativeResiduals(const double eps_optimal_absolute, const double eps_optimal_relative, const QuadraticProgramBoundNorms &norms, const ConvergenceInformation &stats)
Definition: termination.cc:147
QuadraticProgramBoundNorms BoundNormsFromProblemStats(const QuadraticProgramStats &stats)
Definition: termination.cc:138