OR-Tools  9.3
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
21#include "absl/container/flat_hash_map.h"
22#include "absl/status/status.h"
23#include "absl/status/statusor.h"
24#include "absl/strings/string_view.h"
25#include "absl/time/time.h"
26#include "absl/types/span.h"
32#include "ortools/math_opt/solution.pb.h"
34
35namespace operations_research {
36namespace math_opt {
37namespace {
38
39// Converts a map with BasisStatusProto values to a map with BasisStatus values
40// CHECKing that no values are BASIS_STATUS_UNSPECIFIED (the validation code
41// should have tested that).
42//
43// TODO(b/201344491): use FromProto() factory methods on solution members and
44// remove the need for this conversion from `IndexedSolutions`.
45template <typename TypedIndex>
46absl::flat_hash_map<TypedIndex, BasisStatus> BasisStatusMapFromProto(
47 const absl::flat_hash_map<TypedIndex, BasisStatusProto>& proto_map) {
48 absl::flat_hash_map<TypedIndex, BasisStatus> cpp_map;
49 for (const auto& [id, proto_value] : proto_map) {
50 const std::optional<BasisStatus> opt_status = EnumFromProto(proto_value);
51 CHECK(opt_status.has_value());
52 cpp_map.emplace(id, *opt_status);
53 }
54 return cpp_map;
55}
56
57} // namespace
58
59std::optional<absl::string_view> Enum<FeasibilityStatus>::ToOptString(
61 switch (value) {
63 return "undetermined";
65 return "feasible";
67 return "infeasible";
68 }
69 return std::nullopt;
70}
71
72absl::Span<const FeasibilityStatus> Enum<FeasibilityStatus>::AllValues() {
73 static constexpr FeasibilityStatus kFeasibilityStatus[] = {
77 };
78 return absl::MakeConstSpan(kFeasibilityStatus);
79}
80
81std::optional<absl::string_view> Enum<TerminationReason>::ToOptString(
83 switch (value) {
85 return "optimal";
87 return "infeasible";
89 return "unbounded";
91 return "infeasible_or_unbounded";
93 return "imprecise";
95 return "feasible";
97 return "no_solution_found";
99 return "numerical_error";
101 return "other_error";
102 }
103 return std::nullopt;
104}
105
106absl::Span<const TerminationReason> Enum<TerminationReason>::AllValues() {
107 static constexpr TerminationReason kTerminationReasonValues[] = {
117 };
118 return absl::MakeConstSpan(kTerminationReasonValues);
119}
120
121std::optional<absl::string_view> Enum<Limit>::ToOptString(Limit value) {
122 switch (value) {
124 return "undetermined";
126 return "iteration";
127 case Limit::kTime:
128 return "time";
129 case Limit::kNode:
130 return "node";
131 case Limit::kSolution:
132 return "solution";
133 case Limit::kMemory:
134 return "memory";
135 case Limit::kCutoff:
136 return "cutoff";
138 return "objective";
139 case Limit::kNorm:
140 return "norm";
142 return "interrupted";
144 return "slow_progress";
145 case Limit::kOther:
146 return "other";
147 }
148 return std::nullopt;
149}
150
151absl::Span<const Limit> Enum<Limit>::AllValues() {
152 static constexpr Limit kLimitValues[] = {
157 return absl::MakeConstSpan(kLimitValues);
158}
159
160Termination::Termination(const TerminationReason reason, std::string detail)
161 : reason(reason), detail(std::move(detail)) {}
162
163Termination Termination::Feasible(const Limit limit, const std::string detail) {
165 termination.limit = limit;
166 return termination;
167}
168
170 const std::string detail) {
172 termination.limit = limit;
173 return termination;
174}
175
176TerminationProto Termination::ToProto() const {
177 TerminationProto proto;
178 proto.set_reason(EnumToProto(reason));
179 if (limit.has_value()) {
180 proto.set_limit(EnumToProto(*limit));
181 }
182 proto.set_detail(detail);
183 return proto;
184}
185
189}
190
191Termination Termination::FromProto(const TerminationProto& termination_proto) {
192 const bool limit_reached =
193 termination_proto.reason() == TERMINATION_REASON_FEASIBLE ||
194 termination_proto.reason() == TERMINATION_REASON_NO_SOLUTION_FOUND;
195 const bool has_limit = termination_proto.limit() != LIMIT_UNSPECIFIED;
196 CHECK_EQ(limit_reached, has_limit)
197 << "Termination reason should be TERMINATION_REASON_FEASIBLE or "
198 "TERMINATION_REASON_NO_SOLUTION_FOUND if and only if limit is "
199 "specified, but found reason="
200 << ProtoEnumToString(termination_proto.reason())
201 << " and limit=" << ProtoEnumToString(termination_proto.limit());
202
203 if (has_limit) {
204 const std::optional<Limit> opt_limit =
205 EnumFromProto(termination_proto.limit());
206 CHECK(opt_limit.has_value());
207 if (termination_proto.reason() == TERMINATION_REASON_FEASIBLE) {
208 return Feasible(*opt_limit, termination_proto.detail());
209 }
210 return NoSolutionFound(*opt_limit, termination_proto.detail());
211 }
212
213 const std::optional<TerminationReason> opt_reason =
214 EnumFromProto(termination_proto.reason());
215 CHECK(opt_reason.has_value());
216 return Termination(*opt_reason, termination_proto.detail());
217}
218
219std::ostream& operator<<(std::ostream& ostr, const Termination& termination) {
220 ostr << "{reason: " << termination.reason;
221 if (termination.limit.has_value()) {
222 ostr << ", limit: " << *termination.limit;
223 }
224 if (!termination.detail.empty()) {
225 // TODO(b/200835670): quote detail and escape it properly.
226 ostr << ", detail: " << termination.detail;
227 }
228 ostr << "}";
229 return ostr;
230}
231
232std::string Termination::ToString() const {
233 std::ostringstream stream;
234 stream << *this;
235 return stream.str();
236}
237
238ProblemStatusProto ProblemStatus::ToProto() const {
239 ProblemStatusProto proto;
240 proto.set_primal_status(EnumToProto(primal_status));
241 proto.set_dual_status(EnumToProto(dual_status));
242 proto.set_primal_or_dual_infeasible(primal_or_dual_infeasible);
243 return proto;
244}
245
247 const ProblemStatusProto& problem_status_proto) {
248 ProblemStatus result;
249 // TODO(b/209014770): consider adding a function to simplify this pattern.
250 const std::optional<FeasibilityStatus> opt_primal_status =
251 EnumFromProto(problem_status_proto.primal_status());
252 const std::optional<FeasibilityStatus> opt_dual_status =
253 EnumFromProto(problem_status_proto.dual_status());
254 CHECK(opt_primal_status.has_value());
255 CHECK(opt_dual_status.has_value());
256 result.primal_status = *opt_primal_status;
257 result.dual_status = *opt_dual_status;
258 result.primal_or_dual_infeasible =
259 problem_status_proto.primal_or_dual_infeasible();
260 return result;
261}
262
263std::ostream& operator<<(std::ostream& ostr,
264 const ProblemStatus& problem_status) {
265 ostr << "{primal_status: " << problem_status.primal_status;
266 ostr << ", dual_status: " << problem_status.dual_status;
267 ostr << ", primal_or_dual_infeasible: "
268 << (problem_status.primal_or_dual_infeasible ? "true" : "false");
269 ostr << "}";
270 return ostr;
271}
272
273std::string ProblemStatus::ToString() const {
274 std::ostringstream stream;
275 stream << *this;
276 return stream.str();
277}
278
279SolveStatsProto SolveStats::ToProto() const {
280 SolveStatsProto proto;
281 CHECK_OK(
282 util_time::EncodeGoogleApiProto(solve_time, proto.mutable_solve_time()));
283 proto.set_best_primal_bound(best_primal_bound);
284 proto.set_best_dual_bound(best_dual_bound);
285 *proto.mutable_problem_status() = problem_status.ToProto();
286 proto.set_simplex_iterations(simplex_iterations);
287 proto.set_barrier_iterations(barrier_iterations);
288 proto.set_first_order_iterations(first_order_iterations);
289 proto.set_node_count(node_count);
290 return proto;
291}
292
293SolveStats SolveStats::FromProto(const SolveStatsProto& solve_stats_proto) {
294 SolveStats result;
295 result.solve_time =
296 util_time::DecodeGoogleApiProto(solve_stats_proto.solve_time()).value();
297 result.best_primal_bound = solve_stats_proto.best_primal_bound();
298 result.best_dual_bound = solve_stats_proto.best_dual_bound();
299 result.problem_status =
300 ProblemStatus::FromProto(solve_stats_proto.problem_status());
301 result.simplex_iterations = solve_stats_proto.simplex_iterations();
302 result.barrier_iterations = solve_stats_proto.barrier_iterations();
303 result.first_order_iterations = solve_stats_proto.first_order_iterations();
304 result.node_count = solve_stats_proto.node_count();
305 return result;
306}
307
308std::ostream& operator<<(std::ostream& ostr, const SolveStats& solve_stats) {
309 ostr << "{solve_time: " << solve_stats.solve_time;
310 ostr << ", best_primal_bound: " << solve_stats.best_primal_bound;
311 ostr << ", best_dual_bound: " << solve_stats.best_dual_bound;
312 ostr << ", problem_status: " << solve_stats.problem_status;
313 ostr << ", simplex_iterations: " << solve_stats.simplex_iterations;
314 ostr << ", barrier_iterations: " << solve_stats.barrier_iterations;
315 ostr << ", first_order_iterations: " << solve_stats.first_order_iterations;
316 ostr << ", node_count: " << solve_stats.node_count;
317 ostr << "}";
318 return ostr;
319}
320
321std::string SolveStats::ToString() const {
322 std::ostringstream stream;
323 stream << *this;
324 return stream.str();
325}
326
328 const SolveResultProto& solve_result_proto) {
329 SolveResult result(Termination::FromProto(solve_result_proto.termination()));
330 result.solve_stats = SolveStats::FromProto(solve_result_proto.solve_stats());
331
332 for (const SolutionProto& solution : solve_result_proto.solutions()) {
333 result.solutions.push_back(Solution::FromProto(model, solution));
334 }
335 for (const PrimalRayProto& primal_ray : solve_result_proto.primal_rays()) {
336 result.primal_rays.push_back(PrimalRay::FromProto(model, primal_ray));
337 }
338 for (const DualRayProto& dual_ray : solve_result_proto.dual_rays()) {
339 result.dual_rays.push_back(DualRay::FromProto(model, dual_ray));
340 }
341 if (solve_result_proto.has_gscip_output()) {
343 std::move(solve_result_proto.gscip_output());
344 }
345 return result;
346}
347
349 return !solutions.empty() && solutions[0].primal_solution.has_value() &&
350 (solutions[0].primal_solution->feasibility_status ==
352}
353
356}
357
360 return solutions[0].primal_solution->objective_value;
361}
362
367}
368
371 return solutions[0].primal_solution->variable_values;
372}
373
375 CHECK(has_ray());
376 return primal_rays[0].variable_values;
377}
378
380 return !solutions.empty() && solutions[0].dual_solution.has_value() &&
381 (solutions[0].dual_solution->feasibility_status ==
383}
384
387 return solutions[0].dual_solution->dual_values;
388}
389
392 return solutions[0].dual_solution->reduced_costs;
393}
396 return dual_rays[0].dual_values;
397}
398
401 return dual_rays[0].reduced_costs;
402}
403
405 return !solutions.empty() && solutions[0].basis.has_value();
406}
407
409 CHECK(has_basis());
410 return solutions[0].basis->constraint_status;
411}
412
414 CHECK(has_basis());
415 return solutions[0].basis->variable_status;
416}
417
418} // namespace math_opt
419} // namespace operations_research
#define CHECK(condition)
Definition: base/logging.h:495
#define CHECK_EQ(val1, val2)
Definition: base/logging.h:703
#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:151
static std::optional< absl::string_view > ToOptString(E value)
Definition: callback.cc:75
static absl::Span< const E > AllValues()
Definition: callback.cc:94
static PrimalRay FromProto(const ModelStorage *model, const PrimalRayProto &primal_ray_proto)
Definition: solution.cc:127
static ProblemStatus FromProto(const ProblemStatusProto &problem_status_proto)
static Solution FromProto(const ModelStorage *model, const SolutionProto &solution_proto)
Definition: solution.cc:174
const VariableMap< double > & ray_variable_values() const
const LinearConstraintMap< double > & dual_values() const
const VariableMap< BasisStatus > & variable_status() const
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={})