OR-Tools  9.2
solve_result.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 <optional>
17#include <string>
18#include <utility>
19#include <vector>
20
22#include "absl/container/flat_hash_map.h"
23#include "absl/strings/string_view.h"
24#include "absl/types/span.h"
26#include "ortools/math_opt/solution.pb.h"
28#include "absl/status/status.h"
29
30namespace operations_research {
31namespace math_opt {
32namespace {
33
34// Converts a map with BasisStatusProto values to a map with BasisStatus values
35// CHECKing that no values are BASIS_STATUS_UNSPECIFIED (the validation code
36// should have tested that).
37//
38// TODO(b/201344491): use FromProto() factory methods on solution members and
39// remove the need for this conversion from `IndexedSolutions`.
40template <typename TypedIndex>
41absl::flat_hash_map<TypedIndex, BasisStatus> BasisStatusMapFromProto(
42 const absl::flat_hash_map<TypedIndex, BasisStatusProto>& proto_map) {
43 absl::flat_hash_map<TypedIndex, BasisStatus> cpp_map;
44 for (const auto& [id, proto_value] : proto_map) {
45 const std::optional<BasisStatus> opt_status = EnumFromProto(proto_value);
46 CHECK(opt_status.has_value());
47 cpp_map.emplace(id, *opt_status);
48 }
49 return cpp_map;
50}
51
52} // namespace
53
54std::optional<absl::string_view> Enum<FeasibilityStatus>::ToOptString(
56 switch (value) {
58 return "undetermined";
60 return "feasible";
62 return "infeasible";
63 }
64 return std::nullopt;
65}
66
67absl::Span<const FeasibilityStatus> Enum<FeasibilityStatus>::AllValues() {
68 static constexpr FeasibilityStatus kFeasibilityStatus[] = {
72 };
73 return absl::MakeConstSpan(kFeasibilityStatus);
74}
75
76std::optional<absl::string_view> Enum<TerminationReason>::ToOptString(
78 switch (value) {
80 return "optimal";
82 return "infeasible";
84 return "unbounded";
86 return "infeasible_or_unbounded";
88 return "imprecise";
90 return "feasible";
92 return "no_solution_found";
94 return "numerical_error";
96 return "other_error";
97 }
98 return std::nullopt;
99}
100
101absl::Span<const TerminationReason> Enum<TerminationReason>::AllValues() {
102 static constexpr TerminationReason kTerminationReasonValues[] = {
112 };
113 return absl::MakeConstSpan(kTerminationReasonValues);
114}
115
116std::optional<absl::string_view> Enum<Limit>::ToOptString(Limit value) {
117 switch (value) {
119 return "undetermined";
121 return "iteration";
122 case Limit::kTime:
123 return "time";
124 case Limit::kNode:
125 return "node";
126 case Limit::kSolution:
127 return "solution";
128 case Limit::kMemory:
129 return "memory";
130 case Limit::kCutoff:
131 return "cutoff";
133 return "objective";
134 case Limit::kNorm:
135 return "norm";
137 return "interrupted";
139 return "slow_progress";
140 case Limit::kOther:
141 return "other";
142 }
143 return std::nullopt;
144}
145
146absl::Span<const Limit> Enum<Limit>::AllValues() {
147 static constexpr Limit kLimitValues[] = {
152 return absl::MakeConstSpan(kLimitValues);
153}
154
155Termination::Termination(const TerminationReason reason, std::string detail)
156 : reason(reason), detail(std::move(detail)) {}
157
158Termination Termination::Feasible(const Limit limit, const std::string detail) {
160 termination.limit = limit;
161 return termination;
162}
163
165 const std::string detail) {
167 termination.limit = limit;
168 return termination;
169}
170
171TerminationProto Termination::ToProto() const {
172 TerminationProto proto;
173 proto.set_reason(EnumToProto(reason));
174 if (limit.has_value()) {
175 proto.set_limit(EnumToProto(*limit));
176 }
177 proto.set_detail(detail);
178 return proto;
179}
180
184}
185
186Termination Termination::FromProto(const TerminationProto& termination_proto) {
187 const bool limit_reached =
188 termination_proto.reason() == TERMINATION_REASON_FEASIBLE ||
189 termination_proto.reason() == TERMINATION_REASON_NO_SOLUTION_FOUND;
190 const bool has_limit = termination_proto.limit() != LIMIT_UNSPECIFIED;
191 CHECK_EQ(limit_reached, has_limit)
192 << "Termination reason should be TERMINATION_REASON_FEASIBLE or "
193 "TERMINATION_REASON_NO_SOLUTION_FOUND if and only if limit is "
194 "specified, but found reason="
195 << ProtoEnumToString(termination_proto.reason())
196 << " and limit=" << ProtoEnumToString(termination_proto.limit());
197
198 if (has_limit) {
199 const std::optional<Limit> opt_limit =
200 EnumFromProto(termination_proto.limit());
201 CHECK(opt_limit.has_value());
202 if (termination_proto.reason() == TERMINATION_REASON_FEASIBLE) {
203 return Feasible(*opt_limit, termination_proto.detail());
204 }
205 return NoSolutionFound(*opt_limit, termination_proto.detail());
206 }
207
208 const std::optional<TerminationReason> opt_reason =
209 EnumFromProto(termination_proto.reason());
210 CHECK(opt_reason.has_value());
211 return Termination(*opt_reason, termination_proto.detail());
212}
213
214std::ostream& operator<<(std::ostream& ostr, const Termination& termination) {
215 ostr << "{reason: " << termination.reason;
216 if (termination.limit.has_value()) {
217 ostr << ", limit: " << *termination.limit;
218 }
219 if (!termination.detail.empty()) {
220 // TODO(b/200835670): quote detail and escape it properly.
221 ostr << ", detail: " << termination.detail;
222 }
223 ostr << "}";
224 return ostr;
225}
226
227std::string Termination::ToString() const {
228 std::ostringstream stream;
229 stream << *this;
230 return stream.str();
231}
232
233ProblemStatusProto ProblemStatus::ToProto() const {
234 ProblemStatusProto proto;
235 proto.set_primal_status(EnumToProto(primal_status));
236 proto.set_dual_status(EnumToProto(dual_status));
237 proto.set_primal_or_dual_infeasible(primal_or_dual_infeasible);
238 return proto;
239}
240
242 const ProblemStatusProto& problem_status_proto) {
243 ProblemStatus result;
244 // TODO(b/209014770): consider adding a function to simplify this pattern.
245 const std::optional<FeasibilityStatus> opt_primal_status =
246 EnumFromProto(problem_status_proto.primal_status());
247 const std::optional<FeasibilityStatus> opt_dual_status =
248 EnumFromProto(problem_status_proto.dual_status());
249 CHECK(opt_primal_status.has_value());
250 CHECK(opt_dual_status.has_value());
251 result.primal_status = *opt_primal_status;
252 result.dual_status = *opt_dual_status;
253 result.primal_or_dual_infeasible =
254 problem_status_proto.primal_or_dual_infeasible();
255 return result;
256}
257
258std::ostream& operator<<(std::ostream& ostr,
259 const ProblemStatus& problem_status) {
260 ostr << "{primal_status: " << problem_status.primal_status;
261 ostr << ", dual_status: " << problem_status.dual_status;
262 ostr << ", primal_or_dual_infeasible: "
263 << (problem_status.primal_or_dual_infeasible ? "true" : "false");
264 ostr << "}";
265 return ostr;
266}
267
268std::string ProblemStatus::ToString() const {
269 std::ostringstream stream;
270 stream << *this;
271 return stream.str();
272}
273
274SolveStatsProto SolveStats::ToProto() const {
275 SolveStatsProto proto;
276 CHECK_OK(
277 util_time::EncodeGoogleApiProto(solve_time, proto.mutable_solve_time()));
278 proto.set_best_primal_bound(best_primal_bound);
279 proto.set_best_dual_bound(best_dual_bound);
280 *proto.mutable_problem_status() = problem_status.ToProto();
281 proto.set_simplex_iterations(simplex_iterations);
282 proto.set_barrier_iterations(barrier_iterations);
283 proto.set_node_count(node_count);
284 return proto;
285}
286
287SolveStats SolveStats::FromProto(const SolveStatsProto& solve_stats_proto) {
288 SolveStats result;
289 result.solve_time =
290 util_time::DecodeGoogleApiProto(solve_stats_proto.solve_time()).value();
291 result.best_primal_bound = solve_stats_proto.best_primal_bound();
292 result.best_dual_bound = solve_stats_proto.best_dual_bound();
293 result.problem_status =
294 ProblemStatus::FromProto(solve_stats_proto.problem_status());
295 result.simplex_iterations = solve_stats_proto.simplex_iterations();
296 result.barrier_iterations = solve_stats_proto.barrier_iterations();
297 result.node_count = solve_stats_proto.node_count();
298 return result;
299}
300
301std::ostream& operator<<(std::ostream& ostr, const SolveStats& solve_stats) {
302 ostr << "{solve_time: " << solve_stats.solve_time;
303 ostr << ", best_primal_bound: " << solve_stats.best_primal_bound;
304 ostr << ", best_dual_bound: " << solve_stats.best_dual_bound;
305 ostr << ", problem_status: " << solve_stats.problem_status;
306 ostr << ", simplex_iterations: " << solve_stats.simplex_iterations;
307 ostr << ", barrier_iterations: " << solve_stats.barrier_iterations;
308 ostr << ", node_count: " << solve_stats.node_count;
309 ostr << "}";
310 return ostr;
311}
312
313std::string SolveStats::ToString() const {
314 std::ostringstream stream;
315 stream << *this;
316 return stream.str();
317}
318
320 const SolveResultProto& solve_result_proto) {
321 SolveResult result(Termination::FromProto(solve_result_proto.termination()));
322 result.warnings = {solve_result_proto.warnings().begin(),
323 solve_result_proto.warnings().end()};
324 result.solve_stats = SolveStats::FromProto(solve_result_proto.solve_stats());
325
326 for (const SolutionProto& solution : solve_result_proto.solutions()) {
327 result.solutions.push_back(Solution::FromProto(model, solution));
328 }
329 for (const PrimalRayProto& primal_ray : solve_result_proto.primal_rays()) {
330 result.primal_rays.push_back(PrimalRay::FromProto(model, primal_ray));
331 }
332 for (const DualRayProto& dual_ray : solve_result_proto.dual_rays()) {
333 result.dual_rays.push_back(DualRay::FromProto(model, dual_ray));
334 }
335 return result;
336}
337
339 return !solutions.empty() && solutions[0].primal_solution.has_value() &&
340 (solutions[0].primal_solution->feasibility_status ==
342}
343
346}
347
350 return solutions[0].primal_solution->objective_value;
351}
352
357}
358
361 return solutions[0].primal_solution->variable_values;
362}
363
365 CHECK(has_ray());
366 return primal_rays[0].variable_values;
367}
368
370 return !solutions.empty() && solutions[0].dual_solution.has_value() &&
371 (solutions[0].dual_solution->feasibility_status ==
373}
374
377 return solutions[0].dual_solution->dual_values;
378}
379
382 return solutions[0].dual_solution->reduced_costs;
383}
386 return dual_rays[0].dual_values;
387}
388
391 return dual_rays[0].reduced_costs;
392}
393
395 return !solutions.empty() && solutions[0].basis.has_value();
396}
397
399 CHECK(has_basis());
400 return solutions[0].basis->constraint_status;
401}
402
404 CHECK(has_basis());
405 return solutions[0].basis->variable_status;
406}
407
408} // namespace math_opt
409} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:702
#define CHECK_OK(x)
Definition: base/logging.h:44
CpModelProto proto
int64_t value
GRBmodel * model
std::ostream & operator<<(std::ostream &out, const E value)
Definition: enums.h:231
std::optional< typename EnumProto< P >::Cpp > EnumFromProto(const P proto_value)
Definition: enums.h:275
Enum< E >::Proto EnumToProto(const std::optional< E > value)
Definition: enums.h:264
Collection of objects used to extend the Constraint Solver library.
std::string ProtoEnumToString(ProtoEnumType enum_value)
STL namespace.
inline ::absl::StatusOr< absl::Duration > DecodeGoogleApiProto(const google::protobuf::Duration &proto)
Definition: protoutil.h:42
inline ::absl::StatusOr< google::protobuf::Duration > EncodeGoogleApiProto(absl::Duration d)
Definition: protoutil.h:27
static DualRay FromProto(const ModelStorage *model, const DualRayProto &dual_ray_proto)
Definition: solution.cc:148
static std::optional< absl::string_view > ToOptString(E value)
Definition: callback.cc:77
static absl::Span< const E > AllValues()
Definition: callback.cc:96
static PrimalRay FromProto(const ModelStorage *model, const PrimalRayProto &primal_ray_proto)
Definition: solution.cc:124
static ProblemStatus FromProto(const ProblemStatusProto &problem_status_proto)
static Solution FromProto(const ModelStorage *model, const SolutionProto &solution_proto)
Definition: solution.cc:171
const VariableMap< double > & ray_variable_values() const
const LinearConstraintMap< double > & dual_values() const
const VariableMap< BasisStatus > & variable_status() const
std::vector< std::string > warnings
Definition: solve_result.h:298
const LinearConstraintMap< double > & ray_dual_values() const
static SolveResult FromProto(const ModelStorage *model, const SolveResultProto &solve_result_proto)
const VariableMap< double > & ray_reduced_costs() const
const LinearConstraintMap< BasisStatus > & constraint_status() const
const VariableMap< double > & variable_values() const
const VariableMap< double > & reduced_costs() const
static SolveStats FromProto(const SolveStatsProto &solve_stats_proto)
Termination(TerminationReason reason, std::string detail={})
static Termination FromProto(const TerminationProto &termination_proto)
static Termination Feasible(Limit limit, std::string detail={})
static Termination NoSolutionFound(Limit limit, std::string detail={})