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