19 #include "absl/status/status.h"
20 #include "absl/strings/str_cat.h"
23 #include "ortools/math_opt/model_parameters.pb.h"
24 #include "ortools/math_opt/result.pb.h"
25 #include "ortools/math_opt/solution.pb.h"
34 bool HasPrimalFeasibleSolution(
const SolutionProto& solution) {
35 return solution.has_primal_solution() &&
36 solution.primal_solution().feasibility_status() ==
37 SOLUTION_STATUS_FEASIBLE;
40 bool HasPrimalFeasibleSolution(
const SolveResultProto& result) {
42 return !result.solutions().empty() &&
43 HasPrimalFeasibleSolution(result.solutions(0));
46 bool HasDualFeasibleSolution(
const SolutionProto& solution) {
47 return solution.has_dual_solution() &&
48 solution.dual_solution().feasibility_status() ==
49 SOLUTION_STATUS_FEASIBLE;
52 bool HasDualFeasibleSolution(
const SolveResultProto& result) {
53 for (
const auto& solution : result.solutions()) {
54 if (HasDualFeasibleSolution(solution)) {
61 absl::Status ValidateSolutions(
62 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
64 const ModelSummary& model_summary) {
66 for (
int i = 0; i < solutions.size(); ++i) {
68 <<
"invalid solutions[" << i <<
"]";
71 if (solutions.empty())
return absl::OkStatus();
75 bool previous_primal_feasible = HasPrimalFeasibleSolution(solutions[0]);
76 bool previous_dual_feasible = HasDualFeasibleSolution(solutions[0]);
77 for (
int i = 1; i < solutions.size(); ++i) {
78 const bool current_primal_feasible =
79 HasPrimalFeasibleSolution(solutions[i]);
80 const bool current_dual_feasible = HasDualFeasibleSolution(solutions[i]);
82 if (current_primal_feasible && !previous_primal_feasible) {
83 return absl::InvalidArgumentError(
84 "primal solution ordering not satisfied");
91 if (current_dual_feasible && !previous_dual_feasible) {
92 if (!(previous_primal_feasible && !current_primal_feasible)) {
93 return absl::InvalidArgumentError(
94 "dual solution ordering not satisfied");
97 previous_primal_feasible = current_primal_feasible;
98 previous_dual_feasible = current_dual_feasible;
100 return absl::OkStatus();
103 absl::Status RequireNoPrimalFeasibleSolution(
const SolveResultProto& result) {
104 if (HasPrimalFeasibleSolution(result)) {
105 return absl::InvalidArgumentError(
106 "expected no primal feasible solution, but one was returned");
109 return absl::OkStatus();
112 absl::Status RequireNoDualFeasibleSolution(
const SolveResultProto& result) {
113 if (HasDualFeasibleSolution(result)) {
114 return absl::InvalidArgumentError(
115 "expected no dual feasible solution, but one was returned");
118 return absl::OkStatus();
123 if (termination.reason() == TERMINATION_REASON_UNSPECIFIED) {
124 return absl::InvalidArgumentError(
"termination reason must be specified");
126 if (termination.reason() == TERMINATION_REASON_FEASIBLE ||
127 termination.reason() == TERMINATION_REASON_NO_SOLUTION_FOUND) {
128 if (termination.limit() == LIMIT_UNSPECIFIED) {
129 return absl::InvalidArgumentError(
131 ", limit must be specified"));
133 if (termination.limit() == LIMIT_CUTOFF &&
134 termination.reason() == TERMINATION_REASON_FEASIBLE) {
135 return absl::InvalidArgumentError(
136 "For LIMIT_CUTOFF expected no solutions");
139 if (termination.limit() != LIMIT_UNSPECIFIED) {
140 return absl::InvalidArgumentError(
142 ", limit should be unspecified, but was set to: ",
146 return absl::OkStatus();
150 if (!HasPrimalFeasibleSolution(result)) {
151 return absl::InvalidArgumentError(
152 "primal feasible solution expected, but not found");
155 return absl::OkStatus();
159 const SolveResultProto& result) {
160 if (result.solve_stats().problem_status().primal_status() !=
161 FEASIBILITY_STATUS_FEASIBLE &&
162 HasPrimalFeasibleSolution(result)) {
163 return absl::InvalidArgumentError(
164 "primal feasibility status is not FEASIBILITY_STATUS_FEASIBLE, but "
165 "primal feasible solution is returned.");
167 return absl::OkStatus();
171 const SolveResultProto& result) {
172 if (result.solve_stats().problem_status().dual_status() !=
173 FEASIBILITY_STATUS_FEASIBLE &&
174 HasDualFeasibleSolution(result)) {
175 return absl::InvalidArgumentError(
176 "dual feasibility status is not FEASIBILITY_STATUS_FEASIBLE, but "
177 "dual feasible solution is returned.");
179 return absl::OkStatus();
185 const ProblemStatusProto
status = result.solve_stats().problem_status();
186 switch (result.termination().reason()) {
187 case TERMINATION_REASON_OPTIMAL:
193 return absl::OkStatus();
194 case TERMINATION_REASON_INFEASIBLE:
201 return absl::OkStatus();
202 case TERMINATION_REASON_UNBOUNDED:
208 return absl::OkStatus();
209 case TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED:
224 return absl::OkStatus();
225 case TERMINATION_REASON_IMPRECISE:
227 return absl::OkStatus();
228 case TERMINATION_REASON_FEASIBLE:
239 return absl::OkStatus();
240 case TERMINATION_REASON_NO_SOLUTION_FOUND:
249 return absl::OkStatus();
250 case TERMINATION_REASON_NUMERICAL_ERROR:
251 case TERMINATION_REASON_OTHER_ERROR: {
256 if (!result.solutions().empty()) {
257 return absl::InvalidArgumentError(
258 absl::StrCat(
"termination reason is ",
260 ", but solutions are available"));
262 if (result.solve_stats().problem_status().primal_or_dual_infeasible()) {
263 return absl::InvalidArgumentError(absl::StrCat(
264 "termination reason is ",
266 ", but solve_stats.problem_status.primal_or_dual_infeasible = "
271 return absl::OkStatus();
274 <<
" not implemented";
277 return absl::OkStatus();
286 ValidateSolutions(result.solutions(),
parameters, model_summary));
288 if (result.primal_rays_size() > 0 &&
289 result.solve_stats().problem_status().dual_status() ==
290 FEASIBILITY_STATUS_FEASIBLE) {
291 return absl::InvalidArgumentError(
292 "solve_stats.problem_status.dual_status = FEASIBILITY_STATUS_FEASIBLE, "
293 "but a primal ray is returned");
295 for (
int i = 0; i < result.primal_rays_size(); ++i) {
299 <<
"Invalid primal_rays[" << i <<
"]";
301 if (result.dual_rays_size() > 0 &&
302 result.solve_stats().problem_status().primal_status() ==
303 FEASIBILITY_STATUS_FEASIBLE) {
304 return absl::InvalidArgumentError(
305 "solve_stats.problem_status.primal_status = "
306 "FEASIBILITY_STATUS_FEASIBLE, but a dual ray is returned");
308 for (
int i = 0; i < result.dual_rays_size(); ++i) {
311 <<
"Invalid dual_rays[" << i <<
"]";
315 <<
"inconsistent termination reason "
318 return absl::OkStatus();
#define RETURN_IF_ERROR(expr)
absl::Status ValidateResult(const SolveResultProto &result, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status CheckDualSolutionAndStatusConsistency(const SolveResultProto &result)
absl::Status ValidateTerminationConsistency(const SolveResultProto &result)
absl::Status CheckPrimalStatusIs(const ProblemStatusProto &status, const FeasibilityStatusProto required_status)
absl::Status ValidateTermination(const TerminationProto &termination)
absl::Status CheckDualStatusIs(const ProblemStatusProto &status, const FeasibilityStatusProto required_status, const bool primal_or_dual_infeasible_also_ok)
absl::Status CheckPrimalSolutionAndStatusConsistency(const SolveResultProto &result)
absl::Status ValidatePrimalRay(const PrimalRayProto &primal_ray, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
absl::Status CheckDualStatusIsNot(const ProblemStatusProto &status, const FeasibilityStatusProto forbidden_status)
absl::Status CheckHasPrimalSolution(const SolveResultProto &result)
absl::Status CheckPrimalStatusIsNot(const ProblemStatusProto &status, const FeasibilityStatusProto forbidden_status)
absl::Status ValidateSolveStats(const SolveStatsProto &solve_stats)
absl::Status ValidateDualRay(const DualRayProto &dual_ray, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status ValidateSolution(const SolutionProto &solution, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)