Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solvers_proto_validation.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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#include <string>
19
20#include "absl/status/status.h"
21#include "absl/strings/str_cat.h"
22#include "absl/strings/string_view.h"
24#include "ortools/pdlp/solvers.pb.h"
25
27
28using ::absl::InvalidArgumentError;
29using ::absl::OkStatus;
30
31const double kTinyDouble = 1.0e-50;
32const double kHugeDouble = 1.0e50;
33
34absl::Status CheckNonNegative(const double value,
35 const absl::string_view name) {
36 if (std::isnan(value)) {
37 return InvalidArgumentError(absl::StrCat(name, " is NAN"));
38 }
39 if (value < 0) {
40 return InvalidArgumentError(absl::StrCat(name, " must be non-negative"));
41 }
42 return absl::OkStatus();
43}
44
45absl::Status ValidateTerminationCriteria(const TerminationCriteria& criteria) {
46 if (criteria.optimality_norm() != OPTIMALITY_NORM_L_INF &&
47 criteria.optimality_norm() != OPTIMALITY_NORM_L2 &&
48 criteria.optimality_norm() != OPTIMALITY_NORM_L_INF_COMPONENTWISE) {
49 return InvalidArgumentError("invalid value for optimality_norm");
50 }
51 if (criteria.has_detailed_optimality_criteria() ||
52 criteria.has_simple_optimality_criteria()) {
53 if (criteria.has_eps_optimal_absolute()) {
54 return InvalidArgumentError(
55 "eps_optimal_absolute should not be set if "
56 "detailed_optimality_criteria or simple_optimality_criteria is used");
57 }
58 if (criteria.has_eps_optimal_relative()) {
59 return InvalidArgumentError(
60 "eps_optimal_relative should not be set if "
61 "detailed_optimality_criteria or simple_optimality_criteria is used");
62 }
63 }
64 if (criteria.has_detailed_optimality_criteria()) {
66 criteria.detailed_optimality_criteria()
67 .eps_optimal_primal_residual_absolute(),
68 "detailed_optimality_criteria.eps_optimal_primal_residual_absolute"));
70 criteria.detailed_optimality_criteria()
71 .eps_optimal_primal_residual_relative(),
72 "detailed_optimality_criteria.eps_optimal_primal_residual_relative"));
74 criteria.detailed_optimality_criteria()
75 .eps_optimal_dual_residual_absolute(),
76 "detailed_optimality_criteria.eps_optimal_dual_residual_absolute"));
78 criteria.detailed_optimality_criteria()
79 .eps_optimal_dual_residual_relative(),
80 "detailed_optimality_criteria.eps_optimal_dual_residual_relative"));
82 criteria.detailed_optimality_criteria()
83 .eps_optimal_objective_gap_absolute(),
84 "detailed_optimality_criteria.eps_optimal_objective_gap_absolute"));
86 criteria.detailed_optimality_criteria()
87 .eps_optimal_objective_gap_relative(),
88 "detailed_optimality_criteria.eps_optimal_objective_gap_relative"));
89 } else if (criteria.has_simple_optimality_criteria()) {
91 criteria.simple_optimality_criteria().eps_optimal_absolute(),
92 "simple_optimality_criteria.eps_optimal_absolute"));
94 criteria.simple_optimality_criteria().eps_optimal_relative(),
95 "simple_optimality_criteria.eps_optimal_relative"));
96 } else {
97 RETURN_IF_ERROR(CheckNonNegative(criteria.eps_optimal_absolute(),
98 "eps_optimal_absolute"));
99 RETURN_IF_ERROR(CheckNonNegative(criteria.eps_optimal_relative(),
100 "eps_optimal_relative"));
101 }
102 RETURN_IF_ERROR(CheckNonNegative(criteria.eps_primal_infeasible(),
103 "eps_primal_infeasible"));
105 CheckNonNegative(criteria.eps_dual_infeasible(), "eps_dual_infeasible"));
107 CheckNonNegative(criteria.eps_dual_infeasible(), "eps_dual_infeasible"));
109 CheckNonNegative(criteria.time_sec_limit(), "time_sec_limit"));
110
111 if (criteria.iteration_limit() < 0) {
112 return InvalidArgumentError("iteration_limit must be non-negative");
113 }
114 RETURN_IF_ERROR(CheckNonNegative(criteria.kkt_matrix_pass_limit(),
115 "kkt_matrix_pass_limit"));
116
117 return OkStatus();
118}
119
121 const AdaptiveLinesearchParams& params) {
122 if (std::isnan(params.step_size_reduction_exponent())) {
123 return InvalidArgumentError("step_size_reduction_exponent is NAN");
124 }
125 if (params.step_size_reduction_exponent() < 0.1 ||
126 params.step_size_reduction_exponent() > 1.0) {
127 return InvalidArgumentError(
128 "step_size_reduction_exponent must be between 0.1 and 1.0 inclusive");
129 }
130 if (std::isnan(params.step_size_growth_exponent())) {
131 return InvalidArgumentError("step_size_growth_exponent is NAN");
132 }
133 if (params.step_size_growth_exponent() < 0.1 ||
134 params.step_size_growth_exponent() > 1.0) {
135 return InvalidArgumentError(
136 "step_size_growth_exponent must be between 0.1 and 1.0 inclusive");
137 }
138 return OkStatus();
139}
140
141absl::Status ValidateMalitskyPockParams(const MalitskyPockParams& params) {
142 if (std::isnan(params.step_size_downscaling_factor())) {
143 return InvalidArgumentError("step_size_downscaling_factor is NAN");
144 }
145 if (params.step_size_downscaling_factor() <= kTinyDouble ||
146 params.step_size_downscaling_factor() >= 1) {
147 return InvalidArgumentError(
148 absl::StrCat("step_size_downscaling_factor must be between ",
149 kTinyDouble, " and 1 exclusive"));
150 }
151 if (std::isnan(params.linesearch_contraction_factor())) {
152 return InvalidArgumentError("linesearch_contraction_factor is NAN");
153 }
154 if (params.linesearch_contraction_factor() <= 0 ||
155 params.linesearch_contraction_factor() >= 1) {
156 return InvalidArgumentError(
157 "linesearch_contraction_factor must be between 0 and 1 exclusive");
158 }
159 if (std::isnan(params.step_size_interpolation())) {
160 return InvalidArgumentError("step_size_interpolation is NAN");
161 }
162 if (params.step_size_interpolation() < 0 ||
163 params.step_size_interpolation() >= kHugeDouble) {
164 return InvalidArgumentError(absl::StrCat(
165 "step_size_interpolation must be non-negative and less than ",
166 kHugeDouble));
167 }
168 return OkStatus();
169}
170
172 const PrimalDualHybridGradientParams& params) {
173 RETURN_IF_ERROR(ValidateTerminationCriteria(params.termination_criteria()))
174 << "termination_criteria invalid";
175 if (params.num_threads() <= 0) {
176 return InvalidArgumentError("num_threads must be positive");
177 }
178 if (params.verbosity_level() < 0) {
179 return InvalidArgumentError("verbosity_level must be non-negative");
180 }
181 if (params.log_interval_seconds() < 0.0) {
182 return InvalidArgumentError("log_interval_seconds must be non-negative");
183 }
184 if (std::isnan(params.log_interval_seconds())) {
185 return InvalidArgumentError("log_interval_seconds is NAN");
186 }
187 if (params.major_iteration_frequency() <= 0) {
188 return InvalidArgumentError("major_iteration_frequency must be positive");
189 }
190 if (params.termination_check_frequency() <= 0) {
191 return InvalidArgumentError("termination_check_frequency must be positive");
192 }
193 if (params.restart_strategy() !=
194 PrimalDualHybridGradientParams::NO_RESTARTS &&
195 params.restart_strategy() !=
196 PrimalDualHybridGradientParams::EVERY_MAJOR_ITERATION &&
197 params.restart_strategy() !=
198 PrimalDualHybridGradientParams::ADAPTIVE_HEURISTIC &&
199 params.restart_strategy() !=
200 PrimalDualHybridGradientParams::ADAPTIVE_DISTANCE_BASED) {
201 return InvalidArgumentError("invalid restart_strategy");
202 }
203 if (std::isnan(params.primal_weight_update_smoothing())) {
204 return InvalidArgumentError("primal_weight_update_smoothing is NAN");
205 }
206 if (params.primal_weight_update_smoothing() < 0 ||
207 params.primal_weight_update_smoothing() > 1) {
208 return InvalidArgumentError(
209 "primal_weight_update_smoothing must be between 0 and 1 inclusive");
210 }
211 if (std::isnan(params.initial_primal_weight())) {
212 return InvalidArgumentError("initial_primal_weight is NAN");
213 }
214 if (params.has_initial_primal_weight() &&
215 (params.initial_primal_weight() <= kTinyDouble ||
216 params.initial_primal_weight() >= kHugeDouble)) {
217 return InvalidArgumentError(
218 absl::StrCat("initial_primal_weight must be between ", kTinyDouble,
219 " and ", kHugeDouble, " if specified"));
220 }
221 if (params.l_inf_ruiz_iterations() < 0) {
222 return InvalidArgumentError("l_inf_ruiz_iterations must be non-negative");
223 }
224 if (params.l_inf_ruiz_iterations() > 100) {
225 return InvalidArgumentError("l_inf_ruiz_iterations must be at most 100");
226 }
227 if (std::isnan(params.sufficient_reduction_for_restart())) {
228 return InvalidArgumentError("sufficient_reduction_for_restart is NAN");
229 }
230 if (params.sufficient_reduction_for_restart() <= 0 ||
231 params.sufficient_reduction_for_restart() >= 1) {
232 return InvalidArgumentError(
233 "sufficient_reduction_for_restart must be between 0 and 1 exclusive");
234 }
235 if (std::isnan(params.necessary_reduction_for_restart())) {
236 return InvalidArgumentError("necessary_reduction_for_restart is NAN");
237 }
238 if (params.necessary_reduction_for_restart() <
239 params.sufficient_reduction_for_restart() ||
240 params.necessary_reduction_for_restart() >= 1) {
241 return InvalidArgumentError(
242 "necessary_reduction_for_restart must be in the interval "
243 "[sufficient_reduction_for_restart, 1)");
244 }
245 if (params.linesearch_rule() !=
246 PrimalDualHybridGradientParams::ADAPTIVE_LINESEARCH_RULE &&
247 params.linesearch_rule() !=
248 PrimalDualHybridGradientParams::MALITSKY_POCK_LINESEARCH_RULE &&
249 params.linesearch_rule() !=
250 PrimalDualHybridGradientParams::CONSTANT_STEP_SIZE_RULE) {
251 return InvalidArgumentError("invalid linesearch_rule");
252 }
254 ValidateAdaptiveLinesearchParams(params.adaptive_linesearch_parameters()))
255 << "adaptive_linesearch_parameters invalid";
256 RETURN_IF_ERROR(ValidateMalitskyPockParams(params.malitsky_pock_parameters()))
257 << "malitsky_pock_parameters invalid";
258 if (std::isnan(params.initial_step_size_scaling())) {
259 return InvalidArgumentError("initial_step_size_scaling is NAN");
260 }
261 if (params.initial_step_size_scaling() <= kTinyDouble ||
262 params.initial_step_size_scaling() >= kHugeDouble) {
263 return InvalidArgumentError(
264 absl::StrCat("initial_step_size_scaling must be between ", kTinyDouble,
265 " and ", kHugeDouble));
266 }
267
268 if (std::isnan(params.infinite_constraint_bound_threshold())) {
269 return InvalidArgumentError("infinite_constraint_bound_threshold is NAN");
270 }
271 if (params.infinite_constraint_bound_threshold() <= 0.0) {
272 return InvalidArgumentError(
273 "infinite_constraint_bound_threshold must be positive");
274 }
275 if (std::isnan(params.diagonal_qp_trust_region_solver_tolerance())) {
276 return InvalidArgumentError(
277 "diagonal_qp_trust_region_solver_tolerance is NAN");
278 }
279 if (params.diagonal_qp_trust_region_solver_tolerance() <
280 10 * std::numeric_limits<double>::epsilon()) {
281 return InvalidArgumentError(absl::StrCat(
282 "diagonal_qp_trust_region_solver_tolerance must be at least ",
283 10 * std::numeric_limits<double>::epsilon()));
284 }
285 if (params.use_feasibility_polishing() &&
286 params.handle_some_primal_gradients_on_finite_bounds_as_residuals()) {
287 return InvalidArgumentError(
288 "use_feasibility_polishing requires "
289 "!handle_some_primal_gradients_on_finite_bounds_as_residuals");
290 }
291 if (params.use_feasibility_polishing() &&
292 params.presolve_options().use_glop()) {
293 return InvalidArgumentError(
294 "use_feasibility_polishing and glop presolve can not be used "
295 "together.");
296 }
297 return OkStatus();
298}
299
300} // namespace operations_research::pdlp
#define RETURN_IF_ERROR(expr)
const std::string name
A name for logging purposes.
int64_t value
Validation utilities for solvers.proto.
absl::Status ValidatePrimalDualHybridGradientParams(const PrimalDualHybridGradientParams &params)
absl::Status ValidateAdaptiveLinesearchParams(const AdaptiveLinesearchParams &params)
absl::Status CheckNonNegative(const double value, const absl::string_view name)
absl::Status ValidateMalitskyPockParams(const MalitskyPockParams &params)
absl::Status ValidateTerminationCriteria(const TerminationCriteria &criteria)