OR-Tools  9.6
solvers_proto_validation.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 
18 #include "absl/status/status.h"
19 #include "absl/strings/str_cat.h"
20 #include "absl/strings/string_view.h"
22 #include "ortools/pdlp/solvers.pb.h"
23 
24 namespace operations_research::pdlp {
25 
26 using ::absl::InvalidArgumentError;
27 using ::absl::OkStatus;
28 
29 absl::Status CheckNonNegative(const double value,
30  const absl::string_view name) {
31  if (std::isnan(value)) {
32  return InvalidArgumentError(absl::StrCat(name, " is NAN"));
33  }
34  if (value < 0) {
35  return InvalidArgumentError(absl::StrCat(name, " must be non-negative"));
36  }
37  return absl::OkStatus();
38 }
39 
40 absl::Status ValidateTerminationCriteria(const TerminationCriteria& criteria) {
41  if (criteria.optimality_norm() != OPTIMALITY_NORM_L_INF &&
42  criteria.optimality_norm() != OPTIMALITY_NORM_L2 &&
43  criteria.optimality_norm() != OPTIMALITY_NORM_L_INF_COMPONENTWISE) {
44  return InvalidArgumentError("invalid value for optimality_norm");
45  }
46  if (criteria.has_detailed_optimality_criteria() ||
47  criteria.has_simple_optimality_criteria()) {
48  if (criteria.has_eps_optimal_absolute()) {
49  return InvalidArgumentError(
50  "eps_optimal_absolute should not be set if "
51  "detailed_optimality_criteria or simple_optimality_criteria is used");
52  }
53  if (criteria.has_eps_optimal_relative()) {
54  return InvalidArgumentError(
55  "eps_optimal_relative should not be set if "
56  "detailed_optimality_criteria or simple_optimality_criteria is used");
57  }
58  }
59  if (criteria.has_detailed_optimality_criteria()) {
61  criteria.detailed_optimality_criteria()
62  .eps_optimal_primal_residual_absolute(),
63  "detailed_optimality_criteria.eps_optimal_primal_residual_absolute"));
65  criteria.detailed_optimality_criteria()
66  .eps_optimal_primal_residual_relative(),
67  "detailed_optimality_criteria.eps_optimal_primal_residual_relative"));
69  criteria.detailed_optimality_criteria()
70  .eps_optimal_dual_residual_absolute(),
71  "detailed_optimality_criteria.eps_optimal_dual_residual_absolute"));
73  criteria.detailed_optimality_criteria()
74  .eps_optimal_dual_residual_relative(),
75  "detailed_optimality_criteria.eps_optimal_dual_residual_relative"));
77  criteria.detailed_optimality_criteria()
78  .eps_optimal_objective_gap_absolute(),
79  "detailed_optimality_criteria.eps_optimal_objective_gap_absolute"));
81  criteria.detailed_optimality_criteria()
82  .eps_optimal_objective_gap_relative(),
83  "detailed_optimality_criteria.eps_optimal_objective_gap_relative"));
84  } else if (criteria.has_simple_optimality_criteria()) {
86  criteria.simple_optimality_criteria().eps_optimal_absolute(),
87  "simple_optimality_criteria.eps_optimal_absolute"));
89  criteria.simple_optimality_criteria().eps_optimal_relative(),
90  "simple_optimality_criteria.eps_optimal_relative"));
91  } else {
92  RETURN_IF_ERROR(CheckNonNegative(criteria.eps_optimal_absolute(),
93  "eps_optimal_absolute"));
94  RETURN_IF_ERROR(CheckNonNegative(criteria.eps_optimal_relative(),
95  "eps_optimal_relative"));
96  }
97  RETURN_IF_ERROR(CheckNonNegative(criteria.eps_primal_infeasible(),
98  "eps_primal_infeasible"));
100  CheckNonNegative(criteria.eps_dual_infeasible(), "eps_dual_infeasible"));
102  CheckNonNegative(criteria.eps_dual_infeasible(), "eps_dual_infeasible"));
104  CheckNonNegative(criteria.time_sec_limit(), "time_sec_limit"));
105 
106  if (criteria.iteration_limit() < 0) {
107  return InvalidArgumentError("iteration_limit must be non-negative");
108  }
109  RETURN_IF_ERROR(CheckNonNegative(criteria.kkt_matrix_pass_limit(),
110  "kkt_matrix_pass_limit"));
111 
112  return OkStatus();
113 }
114 
116  const AdaptiveLinesearchParams& params) {
117  if (std::isnan(params.step_size_reduction_exponent())) {
118  return InvalidArgumentError("step_size_reduction_exponent is NAN");
119  }
120  if (params.step_size_reduction_exponent() <= 0) {
121  return InvalidArgumentError(
122  "step_size_reduction_exponent must be positive");
123  }
124  if (std::isnan(params.step_size_growth_exponent())) {
125  return InvalidArgumentError("step_size_growth_exponent is NAN");
126  }
127  if (params.step_size_growth_exponent() <= 0) {
128  return InvalidArgumentError("step_size_growth_exponent must be positive");
129  }
130  return OkStatus();
131 }
132 
133 absl::Status ValidateMalitskyPockParams(const MalitskyPockParams& params) {
134  if (std::isnan(params.step_size_downscaling_factor())) {
135  return InvalidArgumentError("step_size_downscaling_factor is NAN");
136  }
137  if (params.step_size_downscaling_factor() <= 0 ||
138  params.step_size_downscaling_factor() >= 1) {
139  return InvalidArgumentError(
140  "step_size_downscaling_factor must be between 0 and 1 exclusive");
141  }
142  if (std::isnan(params.linesearch_contraction_factor())) {
143  return InvalidArgumentError("linesearch_contraction_factor is NAN");
144  }
145  if (params.linesearch_contraction_factor() <= 0 ||
146  params.linesearch_contraction_factor() >= 1) {
147  return InvalidArgumentError(
148  "linesearch_contraction_factor must be between 0 and 1 exclusive");
149  }
150  if (std::isnan(params.step_size_interpolation())) {
151  return InvalidArgumentError("step_size_interpolation is NAN");
152  }
153  if (params.step_size_interpolation() < 0) {
154  return InvalidArgumentError("step_size_interpolation must be non-negative");
155  }
156  return OkStatus();
157 }
158 
160  const PrimalDualHybridGradientParams& params) {
161  RETURN_IF_ERROR(ValidateTerminationCriteria(params.termination_criteria()))
162  << "termination_criteria invalid";
163  if (params.num_threads() <= 0) {
164  return InvalidArgumentError("num_threads must be positive");
165  }
166  if (params.verbosity_level() < 0) {
167  return InvalidArgumentError("verbosity_level must be non-negative");
168  }
169  if (params.major_iteration_frequency() <= 0) {
170  return InvalidArgumentError("major_iteration_frequency must be positive");
171  }
172  if (params.termination_check_frequency() <= 0) {
173  return InvalidArgumentError("termination_check_frequency must be positive");
174  }
175  if (params.restart_strategy() !=
176  PrimalDualHybridGradientParams::NO_RESTARTS &&
177  params.restart_strategy() !=
178  PrimalDualHybridGradientParams::EVERY_MAJOR_ITERATION &&
179  params.restart_strategy() !=
180  PrimalDualHybridGradientParams::ADAPTIVE_HEURISTIC &&
181  params.restart_strategy() !=
182  PrimalDualHybridGradientParams::ADAPTIVE_DISTANCE_BASED) {
183  return InvalidArgumentError("invalid restart_strategy");
184  }
185  if (std::isnan(params.primal_weight_update_smoothing())) {
186  return InvalidArgumentError("primal_weight_update_smoothing is NAN");
187  }
188  if (params.primal_weight_update_smoothing() < 0 ||
189  params.primal_weight_update_smoothing() > 1) {
190  return InvalidArgumentError(
191  "primal_weight_update_smoothing must be between 0 and 1 inclusive");
192  }
193  if (std::isnan(params.initial_primal_weight())) {
194  return InvalidArgumentError("initial_primal_weight is NAN");
195  }
196  if (params.has_initial_primal_weight() &&
197  params.initial_primal_weight() <= 0) {
198  return InvalidArgumentError(
199  "initial_primal_weight must be positive if specified");
200  }
201  if (params.l_inf_ruiz_iterations() < 0) {
202  return InvalidArgumentError("l_inf_ruiz_iterations must be non-negative");
203  }
204  if (params.l_inf_ruiz_iterations() > 100) {
205  return InvalidArgumentError("l_inf_ruiz_iterations must be at most 100");
206  }
207  if (std::isnan(params.sufficient_reduction_for_restart())) {
208  return InvalidArgumentError("sufficient_reduction_for_restart is NAN");
209  }
210  if (params.sufficient_reduction_for_restart() <= 0 ||
211  params.sufficient_reduction_for_restart() >= 1) {
212  return InvalidArgumentError(
213  "sufficient_reduction_for_restart must be between 0 and 1 exclusive");
214  }
215  if (std::isnan(params.necessary_reduction_for_restart())) {
216  return InvalidArgumentError("necessary_reduction_for_restart is NAN");
217  }
218  if (params.necessary_reduction_for_restart() <
219  params.sufficient_reduction_for_restart() ||
220  params.necessary_reduction_for_restart() >= 1) {
221  return InvalidArgumentError(
222  "necessary_reduction_for_restart must be in the interval "
223  "[sufficient_reduction_for_restart, 1)");
224  }
225  if (params.linesearch_rule() !=
226  PrimalDualHybridGradientParams::ADAPTIVE_LINESEARCH_RULE &&
227  params.linesearch_rule() !=
228  PrimalDualHybridGradientParams::MALITSKY_POCK_LINESEARCH_RULE &&
229  params.linesearch_rule() !=
230  PrimalDualHybridGradientParams::CONSTANT_STEP_SIZE_RULE) {
231  return InvalidArgumentError("invalid linesearch_rule");
232  }
234  ValidateAdaptiveLinesearchParams(params.adaptive_linesearch_parameters()))
235  << "adaptive_linesearch_parameters invalid";
236  RETURN_IF_ERROR(ValidateMalitskyPockParams(params.malitsky_pock_parameters()))
237  << "malitsky_pock_parameters invalid";
238  if (std::isnan(params.initial_step_size_scaling())) {
239  return InvalidArgumentError("initial_step_size_scaling is NAN");
240  }
241  if (params.initial_step_size_scaling() <= 0.0) {
242  return InvalidArgumentError("initial_step_size_scaling must be positive");
243  }
244 
245  if (std::isnan(params.infinite_constraint_bound_threshold())) {
246  return InvalidArgumentError("infinite_constraint_bound_threshold is NAN");
247  }
248  if (params.infinite_constraint_bound_threshold() < 0.0) {
249  return InvalidArgumentError(
250  "infinite_constraint_bound_threshold must be non-negative");
251  }
252  if (std::isnan(params.diagonal_qp_trust_region_solver_tolerance())) {
253  return InvalidArgumentError(
254  "diagonal_qp_trust_region_solver_tolerance is NAN");
255  }
256  if (params.diagonal_qp_trust_region_solver_tolerance() < 0.0) {
257  return InvalidArgumentError(
258  "diagonal_qp_trust_region_solver_tolerance must be non-negative");
259  }
260  return OkStatus();
261 }
262 
263 } // namespace operations_research::pdlp
#define RETURN_IF_ERROR(expr)
const std::string name
int64_t value
absl::Status ValidateTerminationCriteria(const TerminationCriteria &criteria)
absl::Status CheckNonNegative(const double value, const absl::string_view name)
absl::Status ValidateMalitskyPockParams(const MalitskyPockParams &params)
absl::Status ValidateAdaptiveLinesearchParams(const AdaptiveLinesearchParams &params)
absl::Status ValidatePrimalDualHybridGradientParams(const PrimalDualHybridGradientParams &params)