19#include "absl/types/optional.h"
21#include "ortools/pdlp/solve_log.pb.h"
22#include "ortools/pdlp/solvers.pb.h"
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());
35 std::abs(stats.primal_objective() - stats.dual_objective());
38 double primal_err_baseline;
40 double dual_err_baseline;
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;
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;
56 LOG(
FATAL) <<
"Invalid optimality_norm value "
57 << OptimalityNorm_Name(optimality_norm);
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;
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;
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() <=
91 const TerminationCriteria& criteria,
const IterationStats& stats,
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()};
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()};
110 if (DualInfeasibilityCriteriaMet(criteria.eps_dual_infeasible(),
111 infeasibility_stats)) {
113 .
reason = TERMINATION_REASON_DUAL_INFEASIBLE,
114 .type = infeasibility_stats.candidate_type()};
117 if (stats.iteration_number() >= criteria.iteration_limit()) {
119 .
reason = TERMINATION_REASON_ITERATION_LIMIT, .type = POINT_TYPE_NONE};
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};
127 if (stats.cumulative_time_sec() >= criteria.time_sec_limit()) {
129 .
reason = TERMINATION_REASON_TIME_LIMIT, .type = POINT_TYPE_NONE};
131 if (force_numerical_termination) {
133 .
reason = TERMINATION_REASON_NUMERICAL_ERROR, .type = POINT_TYPE_NONE};
135 return absl::nullopt;
139 const QuadraticProgramStats& stats) {
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()};
148 const double eps_optimal_absolute,
const double eps_optimal_relative,
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() /
159 stats.l2_primal_residual() /
162 stats.l_inf_dual_residual() /
165 stats.l2_dual_residual() /
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();
absl::optional< TerminationReasonAndPointType > CheckTerminationCriteria(const TerminationCriteria &criteria, const IterationStats &stats, const QuadraticProgramBoundNorms &bound_norms, const bool force_numerical_termination)
RelativeConvergenceInformation ComputeRelativeResiduals(const double eps_optimal_absolute, const double eps_optimal_relative, const QuadraticProgramBoundNorms &norms, const ConvergenceInformation &stats)
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