19#include "absl/status/status.h"
20#include "absl/strings/str_cat.h"
22#include "ortools/math_opt/model_parameters.pb.h"
23#include "ortools/math_opt/result.pb.h"
24#include "ortools/math_opt/solution.pb.h"
34constexpr double kInf = std::numeric_limits<double>::infinity();
36absl::Status ValidateSolutionStatus(
const SolutionStatusProto&
status) {
37 if (!SolutionStatusProto_IsValid(
status)) {
38 return absl::InvalidArgumentError(absl::StrCat(
"status = ",
status));
40 if (
status == SOLUTION_STATUS_UNSPECIFIED) {
41 return absl::InvalidArgumentError(
"status = SOLUTION_STATUS_UNSPECIFIED");
43 return absl::OkStatus();
46absl::Status ValidateTermination(
const TerminationProto& termination) {
47 if (termination.reason() == TERMINATION_REASON_UNSPECIFIED) {
48 return absl::InvalidArgumentError(
"termination reason must be specified");
50 if (termination.reason() == TERMINATION_REASON_FEASIBLE ||
51 termination.reason() == TERMINATION_REASON_NO_SOLUTION_FOUND) {
52 if (termination.limit() == LIMIT_UNSPECIFIED) {
53 return absl::InvalidArgumentError(
55 ", limit must be specified"));
57 if (termination.limit() == LIMIT_CUTOFF &&
58 termination.reason() == TERMINATION_REASON_FEASIBLE) {
59 return absl::InvalidArgumentError(
60 "For LIMIT_CUTOFF expected no solutions");
63 if (termination.limit() != LIMIT_UNSPECIFIED) {
64 return absl::InvalidArgumentError(
66 ", limit should be unspecified, but was set to: ",
70 return absl::OkStatus();
73bool HasPrimalFeasibleSolution(
const SolutionProto& solution) {
74 return solution.has_primal_solution() &&
75 solution.primal_solution().feasibility_status() ==
76 SOLUTION_STATUS_FEASIBLE;
79bool HasPrimalFeasibleSolution(
const SolveResultProto& result) {
81 return !result.solutions().empty() &&
82 HasPrimalFeasibleSolution(result.solutions(0));
85bool HasDualFeasibleSolution(
const SolutionProto& solution) {
86 return solution.has_dual_solution() &&
87 solution.dual_solution().feasibility_status() ==
88 SOLUTION_STATUS_FEASIBLE;
91bool HasDualFeasibleSolution(
const SolveResultProto& result) {
92 for (
const auto& solution : result.solutions()) {
93 if (HasDualFeasibleSolution(solution)) {
100absl::Status ValidateSolutions(
101 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
103 const ModelSummary& model_summary) {
105 for (
int i = 0; i < solutions.size(); ++i) {
107 <<
"invalid solutions[" << i <<
"]";
110 if (solutions.empty())
return absl::OkStatus();
114 bool previous_primal_feasible = HasPrimalFeasibleSolution(solutions[0]);
115 bool previous_dual_feasible = HasDualFeasibleSolution(solutions[0]);
116 for (
int i = 1; i < solutions.size(); ++i) {
117 const bool current_primal_feasible =
118 HasPrimalFeasibleSolution(solutions[i]);
119 const bool current_dual_feasible = HasDualFeasibleSolution(solutions[i]);
121 if (current_primal_feasible && !previous_primal_feasible) {
122 return absl::InvalidArgumentError(
123 "primal solution ordering not satisfied");
130 if (current_dual_feasible && !previous_dual_feasible) {
131 if (!(previous_primal_feasible && !current_primal_feasible)) {
132 return absl::InvalidArgumentError(
133 "dual solution ordering not satisfied");
136 previous_primal_feasible = current_primal_feasible;
137 previous_dual_feasible = current_dual_feasible;
139 return absl::OkStatus();
142absl::Status RequireNoPrimalFeasibleSolution(
const SolveResultProto& result) {
143 if (HasPrimalFeasibleSolution(result)) {
144 return absl::InvalidArgumentError(
145 "expected no primal feasible solution, but one was returned");
148 return absl::OkStatus();
151absl::Status RequireNoDualFeasibleSolution(
const SolveResultProto& result) {
152 if (HasDualFeasibleSolution(result)) {
153 return absl::InvalidArgumentError(
154 "expected no dual feasible solution, but one was returned");
157 return absl::OkStatus();
162 if (!HasPrimalFeasibleSolution(result)) {
163 return absl::InvalidArgumentError(
164 "primal feasible solution expected, but not found");
167 return absl::OkStatus();
171 const SolveResultProto& result) {
172 if (result.solve_stats().problem_status().primal_status() !=
173 FEASIBILITY_STATUS_FEASIBLE &&
174 HasPrimalFeasibleSolution(result)) {
175 return absl::InvalidArgumentError(
176 "primal feasibility status is not FEASIBILITY_STATUS_FEASIBLE, but "
177 "primal feasible solution is returned.");
179 return absl::OkStatus();
183 const SolveResultProto& result) {
184 if (result.solve_stats().problem_status().dual_status() !=
185 FEASIBILITY_STATUS_FEASIBLE &&
186 HasDualFeasibleSolution(result)) {
187 return absl::InvalidArgumentError(
188 "dual feasibility status is not FEASIBILITY_STATUS_FEASIBLE, but "
189 "dual feasible solution is returned.");
191 return absl::OkStatus();
198 const ProblemStatusProto
status = result.solve_stats().problem_status();
199 switch (result.termination().reason()) {
200 case TERMINATION_REASON_OPTIMAL:
206 return absl::OkStatus();
208 case TERMINATION_REASON_INFEASIBLE:
215 return absl::OkStatus();
216 case TERMINATION_REASON_UNBOUNDED:
222 return absl::OkStatus();
223 case TERMINATION_REASON_INFEASIBLE_OR_UNBOUNDED:
238 return absl::OkStatus();
239 case TERMINATION_REASON_IMPRECISE:
241 return absl::OkStatus();
242 case TERMINATION_REASON_FEASIBLE:
253 return absl::OkStatus();
254 case TERMINATION_REASON_NO_SOLUTION_FOUND:
263 return absl::OkStatus();
264 case TERMINATION_REASON_NUMERICAL_ERROR:
265 case TERMINATION_REASON_OTHER_ERROR: {
270 if (!result.solutions().empty()) {
271 return absl::InvalidArgumentError(
272 absl::StrCat(
"termination reason is ",
274 ", but solutions are available"));
276 if (result.solve_stats().problem_status().primal_or_dual_infeasible()) {
277 return absl::InvalidArgumentError(absl::StrCat(
278 "termination reason is ",
280 ", but solve_stats.problem_status.primal_or_dual_infeasible = "
285 return absl::OkStatus();
288 <<
" not implemented";
291 return absl::OkStatus();
300 ValidateSolutions(result.solutions(),
parameters, model_summary));
302 for (
int i = 0; i < result.primal_rays_size(); ++i) {
306 <<
"Invalid primal_rays[" << i <<
"]";
308 for (
int i = 0; i < result.dual_rays_size(); ++i) {
311 <<
"Invalid dual_rays[" << i <<
"]";
315 <<
"inconsistent termination reason "
318 return absl::OkStatus();
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 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)
#define RETURN_IF_ERROR(expr)