21#include "ortools/pdlp/solve_log.pb.h"
22#include "ortools/pdlp/solvers.pb.h"
27 const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
28 const ConvergenceInformation& stats) {
29 if (std::isinf(optimality_criteria.eps_optimal_objective_gap_absolute()) ||
30 std::isinf(optimality_criteria.eps_optimal_objective_gap_relative())) {
33 const double abs_obj =
34 std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
36 std::abs(stats.primal_objective() - stats.dual_objective());
37 return std::isfinite(abs_obj) &&
38 gap <= optimality_criteria.eps_optimal_objective_gap_absolute() +
39 optimality_criteria.eps_optimal_objective_gap_relative() *
44 const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
45 const ConvergenceInformation& stats,
const OptimalityNorm optimality_norm,
48 double primal_err_baseline;
50 double dual_err_baseline;
51 double primal_absolute_epsilon =
52 optimality_criteria.eps_optimal_primal_residual_absolute();
53 double dual_absolute_epsilon =
54 optimality_criteria.eps_optimal_dual_residual_absolute();
56 switch (optimality_norm) {
57 case OPTIMALITY_NORM_L_INF:
58 primal_err = stats.l_inf_primal_residual();
60 dual_err = stats.l_inf_dual_residual();
63 case OPTIMALITY_NORM_L2:
64 primal_err = stats.l2_primal_residual();
66 dual_err = stats.l2_dual_residual();
69 case OPTIMALITY_NORM_L_INF_COMPONENTWISE:
70 primal_err = stats.l_inf_componentwise_primal_residual();
71 primal_err_baseline = 1.0;
72 primal_absolute_epsilon = 0.0;
73 dual_err = stats.l_inf_componentwise_dual_residual();
74 dual_err_baseline = 1.0;
75 dual_absolute_epsilon = 0.0;
78 LOG(FATAL) <<
"Invalid optimality_norm value "
79 << OptimalityNorm_Name(optimality_norm);
82 const bool primal_err_ok =
83 std::isinf(optimality_criteria.eps_optimal_primal_residual_absolute()) ||
84 std::isinf(optimality_criteria.eps_optimal_primal_residual_relative()) ||
86 primal_absolute_epsilon +
87 optimality_criteria.eps_optimal_primal_residual_relative() *
89 const bool dual_err_ok =
90 std::isinf(optimality_criteria.eps_optimal_dual_residual_absolute()) ||
91 std::isinf(optimality_criteria.eps_optimal_dual_residual_relative()) ||
92 dual_err <= dual_absolute_epsilon +
93 optimality_criteria.eps_optimal_dual_residual_relative() *
95 return primal_err_ok && dual_err_ok &&
104bool PrimalInfeasibilityCriteriaMet(
double eps_primal_infeasible,
105 const InfeasibilityInformation& stats) {
106 if (stats.dual_ray_objective() <= 0.0)
return false;
107 return stats.max_dual_ray_infeasibility() / stats.dual_ray_objective() <=
108 eps_primal_infeasible;
113bool DualInfeasibilityCriteriaMet(
double eps_dual_infeasible,
114 const InfeasibilityInformation& stats) {
115 if (stats.primal_ray_linear_objective() >= 0.0)
return false;
116 return (stats.max_primal_ray_infeasibility() /
117 -stats.primal_ray_linear_objective() <=
118 eps_dual_infeasible) &&
119 (stats.primal_ray_quadratic_norm() /
120 -stats.primal_ray_linear_objective() <=
121 eps_dual_infeasible);
127 const TerminationCriteria& termination_criteria) {
128 if (termination_criteria.has_detailed_optimality_criteria()) {
129 return termination_criteria.detailed_optimality_criteria();
131 TerminationCriteria::SimpleOptimalityCriteria simple_criteria;
132 if (termination_criteria.has_simple_optimality_criteria()) {
133 simple_criteria = termination_criteria.simple_optimality_criteria();
135 simple_criteria.set_eps_optimal_absolute(
136 termination_criteria.eps_optimal_absolute());
137 simple_criteria.set_eps_optimal_relative(
138 termination_criteria.eps_optimal_relative());
144 const TerminationCriteria::SimpleOptimalityCriteria& simple_criteria) {
145 TerminationCriteria::DetailedOptimalityCriteria result;
146 result.set_eps_optimal_primal_residual_absolute(
147 simple_criteria.eps_optimal_absolute());
148 result.set_eps_optimal_primal_residual_relative(
149 simple_criteria.eps_optimal_relative());
150 result.set_eps_optimal_dual_residual_absolute(
151 simple_criteria.eps_optimal_absolute());
152 result.set_eps_optimal_dual_residual_relative(
153 simple_criteria.eps_optimal_relative());
154 result.set_eps_optimal_objective_gap_absolute(
155 simple_criteria.eps_optimal_absolute());
156 result.set_eps_optimal_objective_gap_relative(
157 simple_criteria.eps_optimal_relative());
162 const TerminationCriteria& criteria,
const IterationStats& stats,
163 const std::atomic<bool>* interrupt_solve) {
164 if (stats.iteration_number() >= criteria.iteration_limit()) {
166 .reason = TERMINATION_REASON_ITERATION_LIMIT, .type = POINT_TYPE_NONE};
168 if (stats.cumulative_kkt_matrix_passes() >=
169 criteria.kkt_matrix_pass_limit()) {
171 .reason = TERMINATION_REASON_KKT_MATRIX_PASS_LIMIT,
172 .type = POINT_TYPE_NONE};
174 if (stats.cumulative_time_sec() >= criteria.time_sec_limit()) {
176 .reason = TERMINATION_REASON_TIME_LIMIT, .type = POINT_TYPE_NONE};
178 if (interrupt_solve !=
nullptr && interrupt_solve->load() ==
true) {
180 .reason = TERMINATION_REASON_INTERRUPTED_BY_USER,
181 .type = POINT_TYPE_NONE};
187 const TerminationCriteria& criteria,
const IterationStats& stats,
189 const bool force_numerical_termination) {
190 TerminationCriteria::DetailedOptimalityCriteria optimality_criteria =
192 for (
const auto& convergence_stats : stats.convergence_information()) {
194 criteria.optimality_norm(), bound_norms)) {
196 .reason = TERMINATION_REASON_OPTIMAL,
197 .type = convergence_stats.candidate_type()};
200 for (
const auto& infeasibility_stats : stats.infeasibility_information()) {
201 if (PrimalInfeasibilityCriteriaMet(criteria.eps_primal_infeasible(),
202 infeasibility_stats)) {
204 .reason = TERMINATION_REASON_PRIMAL_INFEASIBLE,
205 .type = infeasibility_stats.candidate_type()};
207 if (DualInfeasibilityCriteriaMet(criteria.eps_dual_infeasible(),
208 infeasibility_stats)) {
210 .reason = TERMINATION_REASON_DUAL_INFEASIBLE,
211 .type = infeasibility_stats.candidate_type()};
214 if (force_numerical_termination) {
216 .reason = TERMINATION_REASON_NUMERICAL_ERROR, .type = POINT_TYPE_NONE};
222 const QuadraticProgramStats& stats) {
224 .l2_norm_primal_linear_objective = stats.objective_vector_l2_norm(),
225 .l2_norm_constraint_bounds = stats.combined_bounds_l2_norm(),
226 .l_inf_norm_primal_linear_objective = stats.objective_vector_abs_max(),
227 .l_inf_norm_constraint_bounds = stats.combined_bounds_max()};
231 const double epsilon_relative) {
234 return (epsilon_absolute == epsilon_relative)
236 : epsilon_absolute / epsilon_relative;
240 const TerminationCriteria::DetailedOptimalityCriteria& optimality_criteria,
241 const ConvergenceInformation& stats,
243 const double eps_ratio_primal =
244 EpsilonRatio(optimality_criteria.eps_optimal_primal_residual_absolute(),
245 optimality_criteria.eps_optimal_primal_residual_relative());
246 const double eps_ratio_dual =
247 EpsilonRatio(optimality_criteria.eps_optimal_dual_residual_absolute(),
248 optimality_criteria.eps_optimal_dual_residual_relative());
249 const double eps_ratio_gap =
250 EpsilonRatio(optimality_criteria.eps_optimal_objective_gap_absolute(),
251 optimality_criteria.eps_optimal_objective_gap_relative());
254 stats.l_inf_primal_residual() /
257 stats.l2_primal_residual() /
260 stats.l_inf_dual_residual() /
263 stats.l2_dual_residual() /
265 const double abs_obj =
266 std::abs(stats.primal_objective()) + std::abs(stats.dual_objective());
267 const double gap = stats.primal_objective() - stats.dual_objective();
Validation utilities for solvers.proto.
std::optional< TerminationReasonAndPointType > CheckSimpleTerminationCriteria(const TerminationCriteria &criteria, const IterationStats &stats, const std::atomic< bool > *interrupt_solve)
TerminationCriteria::DetailedOptimalityCriteria EffectiveOptimalityCriteria(const TerminationCriteria &termination_criteria)
Computes the effective optimality criteria for a TerminationCriteria.
QuadraticProgramBoundNorms BoundNormsFromProblemStats(const QuadraticProgramStats &stats)
double EpsilonRatio(const double epsilon_absolute, const double epsilon_relative)
std::optional< TerminationReasonAndPointType > CheckIterateTerminationCriteria(const TerminationCriteria &criteria, const IterationStats &stats, const QuadraticProgramBoundNorms &bound_norms, const bool force_numerical_termination)
bool OptimalityCriteriaMet(const TerminationCriteria::DetailedOptimalityCriteria &optimality_criteria, const ConvergenceInformation &stats, const OptimalityNorm optimality_norm, const QuadraticProgramBoundNorms &bound_norms)
Determines if the optimality criteria are met.
bool ObjectiveGapMet(const TerminationCriteria::DetailedOptimalityCriteria &optimality_criteria, const ConvergenceInformation &stats)
RelativeConvergenceInformation ComputeRelativeResiduals(const TerminationCriteria::DetailedOptimalityCriteria &optimality_criteria, const ConvergenceInformation &stats, const QuadraticProgramBoundNorms &bound_norms)
double l_inf_norm_constraint_bounds
double l_inf_norm_primal_linear_objective
double l2_norm_constraint_bounds
double l2_norm_primal_linear_objective