Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
bounds_and_status_validator.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <ios>
18#include <limits>
19
20#include "absl/status/status.h"
21#include "absl/strings/str_cat.h"
22#include "absl/strings/string_view.h"
25#include "ortools/math_opt/result.pb.h"
28
30namespace {
31constexpr double kInf = std::numeric_limits<double>::infinity();
32} // namespace
33absl::Status ValidateFeasibilityStatus(const FeasibilityStatusProto& status) {
34 if (!FeasibilityStatusProto_IsValid(status)) {
35 return absl::InvalidArgumentError(absl::StrCat("invalid status ", status));
36 }
37 if (status == FEASIBILITY_STATUS_UNSPECIFIED) {
38 return absl::InvalidArgumentError(
39 "invalid status FEASIBILITY_STATUS_UNSPECIFIED");
40 }
41 return absl::OkStatus();
42}
43
44absl::Status ValidateProblemStatus(const ProblemStatusProto& status) {
46 << "invalid primal_status";
48 << "invalid dual_status";
49 if (status.primal_or_dual_infeasible() &&
50 (status.primal_status() != FEASIBILITY_STATUS_UNDETERMINED ||
51 status.dual_status() != FEASIBILITY_STATUS_UNDETERMINED)) {
52 return absl::InvalidArgumentError(absl::StrCat(
53 "primal_or_dual_infeasible can be true only when primal status = dual "
54 "status = FEASIBILITY_STATUS_UNDETERMINED, and we have primal status "
55 "= ",
56 ProtoEnumToString(status.primal_status()),
57 " and dual status = ", ProtoEnumToString(status.dual_status())));
58 }
59 return absl::OkStatus();
60}
61
62// Assumes ValidateProblemStatus(status) is ok.
63absl::Status CheckPrimalStatusIs(const ProblemStatusProto& status,
64 const FeasibilityStatusProto required_status) {
65 const FeasibilityStatusProto actual_status = status.primal_status();
66 if (actual_status == required_status) {
67 return absl::OkStatus();
68 }
69 return absl::InvalidArgumentError(
70 absl::StrCat("expected problem_status.primal_status = ",
71 ProtoEnumToString(required_status), ", but was ",
72 ProtoEnumToString(actual_status)));
73}
74
75// Assumes ValidateProblemStatus(status) is ok.
77 const ProblemStatusProto& status,
78 const FeasibilityStatusProto forbidden_status) {
79 const FeasibilityStatusProto actual_status = status.primal_status();
80 if (actual_status != forbidden_status) {
81 return absl::OkStatus();
82 }
83 return absl::InvalidArgumentError(
84 absl::StrCat("expected problem_status.primal_status != ",
85 ProtoEnumToString(forbidden_status)));
86}
87
88// Assumes ValidateProblemStatus(status) is ok.
90 const ProblemStatusProto& status,
91 const FeasibilityStatusProto forbidden_status) {
92 const FeasibilityStatusProto actual_status = status.dual_status();
93 if (actual_status != forbidden_status) {
94 return absl::OkStatus();
95 }
96 return absl::InvalidArgumentError(
97 absl::StrCat("expected problem_status.dual_status != ",
98 ProtoEnumToString(forbidden_status)));
99}
100
101// Assumes ValidateProblemStatus(status) is ok.
102absl::Status CheckDualStatusIs(const ProblemStatusProto& status,
103 const FeasibilityStatusProto required_status,
104 const bool primal_or_dual_infeasible_also_ok) {
105 const FeasibilityStatusProto actual_status = status.dual_status();
106 if (actual_status == required_status) {
107 return absl::OkStatus();
108 }
109 if (primal_or_dual_infeasible_also_ok && status.primal_or_dual_infeasible()) {
110 // ValidateProblemStatus call above guarantees primal and dual statuses
111 // are FEASIBILITY_STATUS_UNDETERMINED here.
112 return absl::OkStatus();
113 }
114 if (primal_or_dual_infeasible_also_ok) {
115 return absl::InvalidArgumentError(absl::StrCat(
116 "expected either problem_status.dual_status = ",
117 ProtoEnumToString(required_status), " (and was ",
118 ProtoEnumToString(actual_status),
119 ") or problem_status.primal_or_dual_infeasible = true (and "
120 "was false)"));
121 }
122 return absl::InvalidArgumentError(
123 absl::StrCat("expected problem_status.dual_status = ",
124 ProtoEnumToString(required_status), ", but was ",
125 ProtoEnumToString(actual_status)));
126}
127
128absl::Status ValidateObjectiveBounds(const ObjectiveBoundsProto& bounds) {
129 const DoubleOptions nonan;
130 RETURN_IF_ERROR(CheckScalar(bounds.primal_bound(), nonan))
131 << "in primal_bound";
132 RETURN_IF_ERROR(CheckScalar(bounds.dual_bound(), nonan)) << "in dual_bound";
133 return absl::OkStatus();
134}
135
136absl::Status CheckFinitePrimalBound(const ObjectiveBoundsProto& bounds) {
137 if (!std::isfinite(bounds.primal_bound())) {
139 << "expected finite primal bound, but found "
140 << bounds.primal_bound();
141 }
142 return absl::OkStatus();
143}
144
145namespace {
146
147absl::Status ValidateFiniteBoundImpliesFeasibleStatus(
148 const double first_bound, const FeasibilityStatusProto first_status,
149 absl::string_view first_name) {
150 if (!std::isfinite(first_bound)) {
151 return absl::OkStatus();
152 }
153 if (first_status == FEASIBILITY_STATUS_FEASIBLE) {
154 return absl::OkStatus();
155 }
157 << "expected " << first_name
158 << " status = FEASIBILITY_STATUS_FEASIBLE for finite " << first_name
159 << " bound = " << first_bound << ", but found " << first_name
160 << " status = " << ProtoEnumToString(first_status);
161}
162
163absl::Status ValidateNotUnboundedBoundImpliesNotUnboundedStatus(
164 const double first_bound, const FeasibilityStatusProto first_status,
165 const FeasibilityStatusProto second_status, absl::string_view first_name,
166 absl::string_view second_name, const double unbounded_bound) {
167 if (first_bound == unbounded_bound) {
168 return absl::OkStatus();
169 }
170 if (first_status != FEASIBILITY_STATUS_FEASIBLE ||
171 second_status != FEASIBILITY_STATUS_INFEASIBLE) {
172 return absl::OkStatus();
173 }
175 << "unexpected (" << first_name << " status, " << second_name
176 << " status) = (FEASIBILITY_STATUS_FEASIBLE, "
177 "FEASIBILITY_STATUS_INFEASIBLE) for not-unbounded "
178 << first_name << " bound = " << first_bound;
179}
180
181absl::Status ValidateUnboundedBoundImpliesUnboundedStatus(
182 const double first_bound, const double second_bound,
183 const FeasibilityStatusProto first_status,
184 const FeasibilityStatusProto second_status, absl::string_view first_name,
185 absl::string_view second_name, const double unbounded_bound) {
186 if (first_bound != unbounded_bound) {
187 return absl::OkStatus();
188 }
189 if (first_status != FEASIBILITY_STATUS_FEASIBLE) {
191 << "expected " << first_name
192 << " status = FEASIBILITY_STATUS_FEASIBLE for unbounded "
193 << first_name << " bound = " << first_bound << ", but found "
194 << first_name << " status = " << ProtoEnumToString(first_status);
195 }
196 if (second_status != FEASIBILITY_STATUS_INFEASIBLE) {
198 << "expected " << second_name
199 << " status = FEASIBILITY_STATUS_INFEASIBLE for unbounded "
200 << first_name << " bound = " << first_bound << ", but found "
201 << second_name << " status = " << ProtoEnumToString(second_status);
202 }
203 if (second_bound != first_bound) {
205 << "expected " << second_name << " bound = " << first_name
206 << " bound for unbounded " << first_name
207 << " bound = " << first_bound << ", but found " << second_name
208 << " bound = " << second_bound;
209 }
210 return absl::OkStatus();
211}
212
213} // namespace
214
216 const ObjectiveBoundsProto& objective_bounds,
217 const ProblemStatusProto& status, bool is_maximize) {
218 RETURN_IF_ERROR(ValidateUnboundedBoundImpliesUnboundedStatus(
219 /*first_bound=*/objective_bounds.primal_bound(),
220 /*second_bound=*/objective_bounds.dual_bound(),
221 /*first_status=*/status.primal_status(),
222 /*second_status=*/status.dual_status(), "primal", "dual",
223 /*unbounded_bound=*/is_maximize ? kInf : -kInf))
224 << "for is_maximize = " << std::boolalpha << is_maximize;
225 RETURN_IF_ERROR(ValidateUnboundedBoundImpliesUnboundedStatus(
226 /*first_bound=*/objective_bounds.dual_bound(),
227 /*second_bound=*/objective_bounds.primal_bound(),
228 /*first_status=*/status.dual_status(),
229 /*second_status=*/status.primal_status(), "dual", "primal",
230 /*unbounded_bound=*/is_maximize ? -kInf : kInf))
231 << "for is_maximize = " << std::boolalpha << is_maximize;
232
233 RETURN_IF_ERROR(ValidateFiniteBoundImpliesFeasibleStatus(
234 /*first_bound=*/objective_bounds.primal_bound(),
235 /*first_status=*/status.primal_status(), /*first_name=*/"primal"));
236 RETURN_IF_ERROR(ValidateFiniteBoundImpliesFeasibleStatus(
237 /*first_bound=*/objective_bounds.dual_bound(),
238 /*first_status=*/status.dual_status(), /*first_name=*/"dual"));
239
240 RETURN_IF_ERROR(ValidateNotUnboundedBoundImpliesNotUnboundedStatus(
241 /*first_bound=*/objective_bounds.primal_bound(),
242 /*first_status=*/status.primal_status(),
243 /*second_status=*/status.dual_status(), "primal", "dual",
244 /*unbounded_bound=*/is_maximize ? kInf : -kInf))
245 << "for is_maximize = " << std::boolalpha << is_maximize;
246 RETURN_IF_ERROR(ValidateNotUnboundedBoundImpliesNotUnboundedStatus(
247 /*first_bound=*/objective_bounds.dual_bound(),
248 /*first_status=*/status.dual_status(),
249 /*second_status=*/status.primal_status(), "dual", "primal",
250 /*unbounded_bound=*/is_maximize ? -kInf : kInf))
251 << "for is_maximize = " << std::boolalpha << is_maximize;
252 return absl::OkStatus();
253}
254
255} // namespace operations_research::math_opt
#define RETURN_IF_ERROR(expr)
absl::Status status
Definition g_gurobi.cc:44
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
absl::Status ValidateFeasibilityStatus(const FeasibilityStatusProto &status)
absl::Status CheckDualStatusIsNot(const ProblemStatusProto &status, const FeasibilityStatusProto forbidden_status)
Assumes ValidateProblemStatus(status) is ok.
absl::Status CheckPrimalStatusIs(const ProblemStatusProto &status, const FeasibilityStatusProto required_status)
Assumes ValidateProblemStatus(status) is ok.
absl::Status ValidateProblemStatus(const ProblemStatusProto &status)
absl::Status CheckDualStatusIs(const ProblemStatusProto &status, const FeasibilityStatusProto required_status, const bool primal_or_dual_infeasible_also_ok)
Assumes ValidateProblemStatus(status) is ok.
absl::Status CheckScalar(const double value, const DoubleOptions &options)
Checks value is not NaN and satisfies the additional conditions in options.
absl::Status CheckFinitePrimalBound(const ObjectiveBoundsProto &bounds)
absl::Status ValidateObjectiveBounds(const ObjectiveBoundsProto &bounds)
absl::Status ValidateBoundStatusConsistency(const ObjectiveBoundsProto &objective_bounds, const ProblemStatusProto &status, bool is_maximize)
absl::Status CheckPrimalStatusIsNot(const ProblemStatusProto &status, const FeasibilityStatusProto forbidden_status)
Assumes ValidateProblemStatus(status) is ok.
std::string ProtoEnumToString(ProtoEnumType enum_value)
Definition proto_utils.h:50
StatusBuilder InvalidArgumentErrorBuilder()