22 #include "ortools/pdlp/solve_log.pb.h"
23 #include "ortools/pdlp/solvers.pb.h"
28 const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
29 const ConvergenceInformation& stats) {
30 if (std::isinf(optimality_criteria.eps_optimal_objective_gap_absolute()) ||
31 std::isinf(optimality_criteria.eps_optimal_objective_gap_relative())) {
34 const double abs_obj =
35 std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
37 std::abs(stats.primal_objective() - stats.dual_objective());
38 return std::isfinite(abs_obj) &&
39 gap <= optimality_criteria.eps_optimal_objective_gap_absolute() +
40 optimality_criteria.eps_optimal_objective_gap_relative() *
45 const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
46 const ConvergenceInformation& stats,
const OptimalityNorm optimality_norm,
49 double primal_err_baseline;
51 double dual_err_baseline;
52 double primal_absolute_epsilon =
53 optimality_criteria.eps_optimal_primal_residual_absolute();
54 double dual_absolute_epsilon =
55 optimality_criteria.eps_optimal_dual_residual_absolute();
57 switch (optimality_norm) {
58 case OPTIMALITY_NORM_L_INF:
59 primal_err = stats.l_inf_primal_residual();
61 dual_err = stats.l_inf_dual_residual();
64 case OPTIMALITY_NORM_L2:
65 primal_err = stats.l2_primal_residual();
67 dual_err = stats.l2_dual_residual();
70 case OPTIMALITY_NORM_L_INF_COMPONENTWISE:
71 primal_err = stats.l_inf_componentwise_primal_residual();
72 primal_err_baseline = 1.0;
73 primal_absolute_epsilon = 0.0;
74 dual_err = stats.l_inf_componentwise_dual_residual();
75 dual_err_baseline = 1.0;
76 dual_absolute_epsilon = 0.0;
79 LOG(FATAL) <<
"Invalid optimality_norm value "
80 << OptimalityNorm_Name(optimality_norm);
83 const bool primal_err_ok =
84 std::isinf(optimality_criteria.eps_optimal_primal_residual_absolute()) ||
85 std::isinf(optimality_criteria.eps_optimal_primal_residual_relative()) ||
87 primal_absolute_epsilon +
88 optimality_criteria.eps_optimal_primal_residual_relative() *
90 const bool dual_err_ok =
91 std::isinf(optimality_criteria.eps_optimal_dual_residual_absolute()) ||
92 std::isinf(optimality_criteria.eps_optimal_dual_residual_relative()) ||
93 dual_err <= dual_absolute_epsilon +
94 optimality_criteria.eps_optimal_dual_residual_relative() *
96 return primal_err_ok && dual_err_ok &&
105 bool PrimalInfeasibilityCriteriaMet(
double eps_primal_infeasible,
106 const InfeasibilityInformation& stats) {
107 if (stats.dual_ray_objective() <= 0.0)
return false;
108 return stats.max_dual_ray_infeasibility() / stats.dual_ray_objective() <=
109 eps_primal_infeasible;
114 bool DualInfeasibilityCriteriaMet(
double eps_dual_infeasible,
115 const InfeasibilityInformation& stats) {
116 if (stats.primal_ray_linear_objective() >= 0.0)
return false;
117 return (stats.max_primal_ray_infeasibility() /
118 -stats.primal_ray_linear_objective() <=
119 eps_dual_infeasible) &&
120 (stats.primal_ray_quadratic_norm() /
121 -stats.primal_ray_linear_objective() <=
122 eps_dual_infeasible);
128 const TerminationCriteria& termination_criteria) {
129 if (termination_criteria.has_detailed_optimality_criteria()) {
130 return termination_criteria.detailed_optimality_criteria();
132 TerminationCriteria::SimpleOptimalityCriteria simple_criteria;
133 if (termination_criteria.has_simple_optimality_criteria()) {
134 simple_criteria = termination_criteria.simple_optimality_criteria();
136 simple_criteria.set_eps_optimal_absolute(
137 termination_criteria.eps_optimal_absolute());
138 simple_criteria.set_eps_optimal_relative(
139 termination_criteria.eps_optimal_relative());
145 const TerminationCriteria::SimpleOptimalityCriteria& simple_criteria) {
146 TerminationCriteria::DetailedOptimalityCriteria result;
147 result.set_eps_optimal_primal_residual_absolute(
148 simple_criteria.eps_optimal_absolute());
149 result.set_eps_optimal_primal_residual_relative(
150 simple_criteria.eps_optimal_relative());
151 result.set_eps_optimal_dual_residual_absolute(
152 simple_criteria.eps_optimal_absolute());
153 result.set_eps_optimal_dual_residual_relative(
154 simple_criteria.eps_optimal_relative());
155 result.set_eps_optimal_objective_gap_absolute(
156 simple_criteria.eps_optimal_absolute());
157 result.set_eps_optimal_objective_gap_relative(
158 simple_criteria.eps_optimal_relative());
163 const TerminationCriteria& criteria,
const IterationStats& stats,
164 const std::atomic<bool>* interrupt_solve) {
165 if (stats.iteration_number() >= criteria.iteration_limit()) {
167 .
reason = TERMINATION_REASON_ITERATION_LIMIT, .type = POINT_TYPE_NONE};
169 if (stats.cumulative_kkt_matrix_passes() >=
170 criteria.kkt_matrix_pass_limit()) {
172 .
reason = TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT,
173 .type = POINT_TYPE_NONE};
175 if (stats.cumulative_time_sec() >= criteria.time_sec_limit()) {
177 .
reason = TERMINATION_REASON_TIME_LIMIT, .type = POINT_TYPE_NONE};
179 if (interrupt_solve !=
nullptr && interrupt_solve->load() ==
true) {
181 .
reason = TERMINATION_REASON_INTERRUPTED_BY_USER,
182 .type = POINT_TYPE_NONE};
188 const TerminationCriteria& criteria,
const IterationStats& stats,
190 const bool force_numerical_termination) {
191 TerminationCriteria::DetailedOptimalityCriteria optimality_criteria =
193 for (
const auto& convergence_stats : stats.convergence_information()) {
195 criteria.optimality_norm(), bound_norms)) {
197 .
reason = TERMINATION_REASON_OPTIMAL,
198 .type = convergence_stats.candidate_type()};
201 for (
const auto& infeasibility_stats : stats.infeasibility_information()) {
202 if (PrimalInfeasibilityCriteriaMet(criteria.eps_primal_infeasible(),
203 infeasibility_stats)) {
205 .
reason = TERMINATION_REASON_PRIMAL_INFEASIBLE,
206 .type = infeasibility_stats.candidate_type()};
208 if (DualInfeasibilityCriteriaMet(criteria.eps_dual_infeasible(),
209 infeasibility_stats)) {
211 .
reason = TERMINATION_REASON_DUAL_INFEASIBLE,
212 .type = infeasibility_stats.candidate_type()};
215 if (force_numerical_termination) {
217 .
reason = TERMINATION_REASON_NUMERICAL_ERROR, .type = POINT_TYPE_NONE};
223 const QuadraticProgramStats& stats) {
226 .l2_norm_constraint_bounds = stats.combined_bounds_l2_norm(),
227 .l_inf_norm_primal_linear_objective = stats.objective_vector_abs_max(),
228 .l_inf_norm_constraint_bounds = stats.combined_bounds_max()};
232 const double epsilon_relative) {
235 return (epsilon_absolute == epsilon_relative)
237 : epsilon_absolute / epsilon_relative;
241 const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
242 const ConvergenceInformation& stats,
244 const double eps_ratio_primal =
245 EpsilonRatio(optimality_criteria.eps_optimal_primal_residual_absolute(),
246 optimality_criteria.eps_optimal_primal_residual_relative());
247 const double eps_ratio_dual =
248 EpsilonRatio(optimality_criteria.eps_optimal_dual_residual_absolute(),
249 optimality_criteria.eps_optimal_dual_residual_relative());
250 const double eps_ratio_gap =
251 EpsilonRatio(optimality_criteria.eps_optimal_objective_gap_absolute(),
252 optimality_criteria.eps_optimal_objective_gap_relative());
255 stats.l_inf_primal_residual() /
258 stats.l2_primal_residual() /
261 stats.l_inf_dual_residual() /
264 stats.l2_dual_residual() /
266 const double abs_obj =
267 std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
268 const double gap = stats.primal_objective() - stats.dual_objective();
double EpsilonRatio(const double epsilon_absolute, const double epsilon_relative)
bool OptimalityCriteriaMet(const TerminationCriteria::DetailedOptimalityCriteria &optimality_criteria, const ConvergenceInformation &stats, const OptimalityNorm optimality_norm, const QuadraticProgramBoundNorms &bound_norms)
bool ObjectiveGapMet(const TerminationCriteria::DetailedOptimalityCriteria &optimality_criteria, const ConvergenceInformation &stats)
TerminationCriteria::DetailedOptimalityCriteria EffectiveOptimalityCriteria(const TerminationCriteria &termination_criteria)
std::optional< TerminationReasonAndPointType > CheckIterateTerminationCriteria(const TerminationCriteria &criteria, const IterationStats &stats, const QuadraticProgramBoundNorms &bound_norms, const bool force_numerical_termination)
RelativeConvergenceInformation ComputeRelativeResiduals(const TerminationCriteria::DetailedOptimalityCriteria &optimality_criteria, const ConvergenceInformation &stats, const QuadraticProgramBoundNorms &bound_norms)
std::optional< TerminationReasonAndPointType > CheckSimpleTerminationCriteria(const TerminationCriteria &criteria, const IterationStats &stats, const std::atomic< bool > *interrupt_solve)
QuadraticProgramBoundNorms BoundNormsFromProblemStats(const QuadraticProgramStats &stats)
double l_inf_norm_constraint_bounds
double l2_norm_primal_linear_objective
double l_inf_norm_primal_linear_objective
double l2_norm_constraint_bounds