OR-Tools  9.3
solve_stats_validator.cc
Go to the documentation of this file.
1// Copyright 2010-2021 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/status/statusor.h"
22#include "absl/strings/str_cat.h"
23#include "absl/time/time.h"
26#include "ortools/math_opt/result.pb.h"
29
30namespace operations_research {
31namespace math_opt {
32namespace {
33
34absl::Status ValidateFeasibilityStatus(const FeasibilityStatusProto& status) {
35 if (!FeasibilityStatusProto_IsValid(status)) {
36 return absl::InvalidArgumentError(absl::StrCat("invalid status ", status));
37 }
38 if (status == FEASIBILITY_STATUS_UNSPECIFIED) {
39 return absl::InvalidArgumentError(
40 "invalid status FEASIBILITY_STATUS_UNSPECIFIED");
41 }
42 return absl::OkStatus();
43}
44} // namespace
45
46absl::Status ValidateProblemStatus(const ProblemStatusProto& status) {
47 RETURN_IF_ERROR(ValidateFeasibilityStatus(status.primal_status()))
48 << "invalid primal_status";
49 RETURN_IF_ERROR(ValidateFeasibilityStatus(status.dual_status()))
50 << "invalid dual_status";
51 if (status.primal_or_dual_infeasible() &&
52 (status.primal_status() != FEASIBILITY_STATUS_UNDETERMINED ||
53 status.dual_status() != FEASIBILITY_STATUS_UNDETERMINED)) {
54 return absl::InvalidArgumentError(absl::StrCat(
55 "primal_or_dual_infeasible can be true only when primal status = dual "
56 "status = FEASIBILITY_STATUS_UNDETERMINED, and we have primal status "
57 "= ",
58 ProtoEnumToString(status.primal_status()),
59 " and dual status = ", ProtoEnumToString(status.dual_status())));
60 }
61 return absl::OkStatus();
62}
63
64// Assumes ValidateProblemStatus(status) is ok.
65absl::Status CheckPrimalStatusIs(const ProblemStatusProto& status,
66 const FeasibilityStatusProto required_status) {
67 const FeasibilityStatusProto actual_status = status.primal_status();
68 if (actual_status == required_status) {
69 return absl::OkStatus();
70 }
71 return absl::InvalidArgumentError(
72 absl::StrCat("expected problem_status.primal_status = ",
73 ProtoEnumToString(required_status), ", but was ",
74 ProtoEnumToString(actual_status)));
75}
76
77// Assumes ValidateProblemStatus(status) is ok.
79 const ProblemStatusProto& status,
80 const FeasibilityStatusProto forbidden_status) {
81 const FeasibilityStatusProto actual_status = status.primal_status();
82 if (actual_status != forbidden_status) {
83 return absl::OkStatus();
84 }
85 return absl::InvalidArgumentError(
86 absl::StrCat("expected problem_status.primal_status != ",
87 ProtoEnumToString(forbidden_status)));
88}
89
90// Assumes ValidateProblemStatus(status) is ok.
92 const ProblemStatusProto& status,
93 const FeasibilityStatusProto forbidden_status) {
94 const FeasibilityStatusProto actual_status = status.dual_status();
95 if (actual_status != forbidden_status) {
96 return absl::OkStatus();
97 }
98 return absl::InvalidArgumentError(
99 absl::StrCat("expected problem_status.dual_status != ",
100 ProtoEnumToString(forbidden_status)));
101}
102
103// Assumes ValidateProblemStatus(status) is ok.
104absl::Status CheckDualStatusIs(const ProblemStatusProto& status,
105 const FeasibilityStatusProto required_status,
106 const bool primal_or_dual_infeasible_also_ok) {
107 const FeasibilityStatusProto actual_status = status.dual_status();
108 if (actual_status == required_status) {
109 return absl::OkStatus();
110 }
111 if (primal_or_dual_infeasible_also_ok && status.primal_or_dual_infeasible()) {
112 // ValidateProblemStatus call above guarantees primal and dual statuses
113 // are FEASIBILITY_STATUS_UNDETERMINED here.
114 return absl::OkStatus();
115 }
116 if (primal_or_dual_infeasible_also_ok) {
117 return absl::InvalidArgumentError(absl::StrCat(
118 "expected either problem_status.dual_status = ",
119 ProtoEnumToString(required_status), " (and was ",
120 ProtoEnumToString(actual_status),
121 ") or problem_status.primal_or_dual_infeasible = true (and "
122 "was false)"));
123 }
124 return absl::InvalidArgumentError(
125 absl::StrCat("expected problem_status.dual_status = ",
126 ProtoEnumToString(required_status), ", but was ",
127 ProtoEnumToString(actual_status)));
128}
129
130namespace {
131// Assumes ValidateSolveStats(solve_stats) is ok.
132absl::Status ValidateSolveStatsConsistency(const SolveStatsProto& solve_stats) {
133 // TODO(b/204457524): refine validator once optimization direction is in
134 // model summary (i.e. avoid the absl::abs).
135 if (solve_stats.problem_status().primal_or_dual_infeasible() &&
136 std::isfinite(solve_stats.best_primal_bound())) {
137 return absl::InvalidArgumentError(
138 "best_primal_bound is finite, but problem status is "
139 "primal_or_dual_infeasible");
140 }
141 if (solve_stats.problem_status().primal_or_dual_infeasible() &&
142 std::isfinite(solve_stats.best_dual_bound())) {
143 return absl::InvalidArgumentError(
144 "best_dual_bound is finite, but problem status is "
145 "primal_or_dual_infeasible");
146 }
147 if (solve_stats.problem_status().primal_status() !=
148 FEASIBILITY_STATUS_FEASIBLE &&
149 std::isfinite(solve_stats.best_primal_bound())) {
150 return absl::InvalidArgumentError(
151 absl::StrCat("best_primal_bound is finite, but primal_status is not "
152 "feasible (primal_status = ",
153 solve_stats.problem_status().primal_status(), ")"));
154 }
155 if (solve_stats.problem_status().dual_status() !=
156 FEASIBILITY_STATUS_FEASIBLE &&
157 std::isfinite(solve_stats.best_dual_bound())) {
158 return absl::InvalidArgumentError(
159 absl::StrCat("best_dual_bound is finite, but dual_status is not "
160 "feasible (dual_status = ",
161 solve_stats.problem_status().dual_status(), ")"));
162 }
163 return absl::OkStatus();
164}
165} // namespace
166
167absl::Status ValidateSolveStats(const SolveStatsProto& solve_stats) {
168 const absl::StatusOr<absl::Duration> solve_time =
169 util_time::DecodeGoogleApiProto(solve_stats.solve_time());
170 if (!solve_time.ok()) {
171 return absl::InvalidArgumentError(
172 absl::StrCat("invalid solve_time, ", solve_time.status().message()));
173 }
174 if (solve_time.value() < absl::ZeroDuration()) {
175 return absl::InvalidArgumentError("solve_time must be non-negative");
176 }
177 if (solve_stats.simplex_iterations() < 0) {
178 return absl::InvalidArgumentError(
179 "simplex_iterations must be non-negative");
180 }
181 if (solve_stats.barrier_iterations() < 0) {
182 return absl::InvalidArgumentError(
183 "barrier_iterations must be non-negative");
184 }
185 if (solve_stats.node_count() < 0) {
186 return absl::InvalidArgumentError("node_count must be non-negative");
187 }
188 RETURN_IF_ERROR(ValidateProblemStatus(solve_stats.problem_status()));
189 const DoubleOptions nonan;
190 RETURN_IF_ERROR(CheckScalar(solve_stats.best_primal_bound(), nonan))
191 << "in best_primal_bound";
192 RETURN_IF_ERROR(CheckScalar(solve_stats.best_dual_bound(), nonan))
193 << "in best_dual_bound";
194 RETURN_IF_ERROR(ValidateSolveStatsConsistency(solve_stats));
195 return absl::OkStatus();
196}
197
198} // namespace math_opt
199} // namespace operations_research
#define RETURN_IF_ERROR(expr)
absl::Status status
Definition: g_gurobi.cc:35
absl::Status CheckScalar(const double value, const DoubleOptions &options)
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 ValidateProblemStatus(const ProblemStatusProto &status)
absl::Status CheckDualStatusIsNot(const ProblemStatusProto &status, const FeasibilityStatusProto forbidden_status)
absl::Status CheckPrimalStatusIsNot(const ProblemStatusProto &status, const FeasibilityStatusProto forbidden_status)
absl::Status ValidateSolveStats(const SolveStatsProto &solve_stats)
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:42