OR-Tools  9.6
solve_stats_validator.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 #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/base/protoutil.h"
26 #include "ortools/math_opt/result.pb.h"
29 
30 namespace operations_research {
31 namespace math_opt {
32 namespace {
33 
34 absl::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 
46 absl::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.
65 absl::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.
91 absl::Status CheckDualStatusIsNot(
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.
104 absl::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 
130 namespace {
131 // Assumes ValidateSolveStats(solve_stats) is ok.
132 absl::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 
167 absl::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:41
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