Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
solve_result.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 <initializer_list>
17#include <limits>
18#include <optional>
19#include <ostream>
20#include <sstream>
21#include <string>
22#include <utility>
23#include <vector>
24
25#include "absl/log/check.h"
26#include "absl/status/status.h"
27#include "absl/status/statusor.h"
28#include "absl/strings/escaping.h"
29#include "absl/strings/str_cat.h"
30#include "absl/strings/str_join.h"
31#include "absl/strings/string_view.h"
32#include "absl/time/time.h"
33#include "absl/types/span.h"
39#include "ortools/math_opt/result.pb.h"
40#include "ortools/math_opt/solution.pb.h"
45
47namespace {
48constexpr double kInf = std::numeric_limits<double>::infinity();
49} // namespace
50
51ObjectiveBoundsProto ObjectiveBounds::Proto() const {
52 ObjectiveBoundsProto proto;
53 proto.set_primal_bound(primal_bound);
54 proto.set_dual_bound(dual_bound);
55 return proto;
56}
57
59 const ObjectiveBoundsProto& objective_bounds_proto) {
60 ObjectiveBounds result;
61 result.primal_bound = objective_bounds_proto.primal_bound();
62 result.dual_bound = objective_bounds_proto.dual_bound();
63 return result;
64}
65
66std::ostream& operator<<(std::ostream& ostr,
67 const ObjectiveBounds& objective_bounds) {
68 ostr << "{primal_bound: "
69 << RoundTripDoubleFormat(objective_bounds.primal_bound);
70 ostr << ", dual_bound: "
71 << RoundTripDoubleFormat(objective_bounds.dual_bound);
72 ostr << "}";
73 return ostr;
74}
75
76std::string ObjectiveBounds::ToString() const {
77 std::ostringstream stream;
78 stream << *this;
79 return stream.str();
80}
81
83 const double primal_bound = is_maximize ? -kInf : kInf;
84 const double dual_bound = -primal_bound;
85 return ObjectiveBounds{.primal_bound = primal_bound,
86 .dual_bound = dual_bound};
87}
94
96 const double primal_bound = is_maximize ? kInf : -kInf;
97 const double dual_bound = primal_bound;
98 return ObjectiveBounds{.primal_bound = primal_bound,
99 .dual_bound = dual_bound};
100}
101
105
109
114
115std::optional<absl::string_view> Enum<FeasibilityStatus>::ToOptString(
117 switch (value) {
119 return "undetermined";
121 return "feasible";
123 return "infeasible";
124 }
125 return std::nullopt;
126}
127
128absl::Span<const FeasibilityStatus> Enum<FeasibilityStatus>::AllValues() {
129 static constexpr FeasibilityStatus kFeasibilityStatus[] = {
133 };
134 return absl::MakeConstSpan(kFeasibilityStatus);
135}
136
137std::optional<absl::string_view> Enum<TerminationReason>::ToOptString(
139 switch (value) {
141 return "optimal";
143 return "infeasible";
145 return "unbounded";
147 return "infeasible_or_unbounded";
149 return "imprecise";
151 return "feasible";
153 return "no_solution_found";
155 return "numerical_error";
157 return "other_error";
158 }
159 return std::nullopt;
160}
161
162absl::Span<const TerminationReason> Enum<TerminationReason>::AllValues() {
163 static constexpr TerminationReason kTerminationReasonValues[] = {
173 };
174 return absl::MakeConstSpan(kTerminationReasonValues);
175}
176
177std::optional<absl::string_view> Enum<Limit>::ToOptString(Limit value) {
178 switch (value) {
180 return "undetermined";
182 return "iteration";
183 case Limit::kTime:
184 return "time";
185 case Limit::kNode:
186 return "node";
187 case Limit::kSolution:
188 return "solution";
189 case Limit::kMemory:
190 return "memory";
191 case Limit::kCutoff:
192 return "cutoff";
194 return "objective";
195 case Limit::kNorm:
196 return "norm";
198 return "interrupted";
200 return "slow_progress";
201 case Limit::kOther:
202 return "other";
203 }
204 return std::nullopt;
205}
206
207absl::Span<const Limit> Enum<Limit>::AllValues() {
208 static constexpr Limit kLimitValues[] = {
213 return absl::MakeConstSpan(kLimitValues);
214}
215
216Termination::Termination(const bool is_maximize, const TerminationReason reason,
217 std::string detail)
218 : reason(reason),
219 detail(std::move(detail)),
220 objective_bounds(ObjectiveBounds::MakeTrivial(is_maximize)) {}
221
222Termination Termination::Optimal(const double primal_objective_value,
223 const double dual_objective_value,
224 const std::string detail) {
226 detail);
227 termination.objective_bounds.primal_bound = primal_objective_value;
228 termination.objective_bounds.dual_bound = dual_objective_value;
229 termination.problem_status.primal_status = FeasibilityStatus::kFeasible;
230 termination.problem_status.dual_status = FeasibilityStatus::kFeasible;
231 return termination;
232}
233
234Termination Termination::Optimal(const double objective_value,
235 const std::string detail) {
236 return Optimal(objective_value, objective_value, detail);
237}
238
239Termination Termination::Infeasible(const bool is_maximize,
240 FeasibilityStatus dual_feasibility_status,
241 const std::string detail) {
243 if (dual_feasibility_status == FeasibilityStatus::kFeasible) {
244 termination.objective_bounds.dual_bound =
245 termination.objective_bounds.primal_bound;
246 }
247 termination.problem_status.primal_status = FeasibilityStatus::kInfeasible;
248 termination.problem_status.dual_status = dual_feasibility_status;
249 return termination;
250}
251
252Termination Termination::InfeasibleOrUnbounded(
253 const bool is_maximize, const FeasibilityStatus dual_feasibility_status,
254 const std::string detail) {
255 Termination termination(is_maximize,
257 termination.problem_status.primal_status = FeasibilityStatus::kUndetermined;
258 termination.problem_status.dual_status = dual_feasibility_status;
259 if (dual_feasibility_status == FeasibilityStatus::kUndetermined) {
260 termination.problem_status.primal_or_dual_infeasible = true;
261 }
262 return termination;
263}
264
265Termination Termination::Unbounded(const bool is_maximize,
266 const std::string detail) {
268 termination.objective_bounds = ObjectiveBounds::MakeUnbounded(is_maximize);
269 termination.problem_status.primal_status = FeasibilityStatus::kFeasible;
270 termination.problem_status.dual_status = FeasibilityStatus::kInfeasible;
271 return termination;
272}
273
274Termination Termination::NoSolutionFound(
275 const bool is_maximize, Limit limit,
276 const std::optional<double> optional_dual_objective,
277 const std::string detail) {
279 detail);
280 termination.problem_status.primal_status = FeasibilityStatus::kUndetermined;
281 termination.problem_status.dual_status = FeasibilityStatus::kUndetermined;
282 if (optional_dual_objective.has_value()) {
283 termination.objective_bounds.dual_bound = *optional_dual_objective;
284 termination.problem_status.dual_status = FeasibilityStatus::kFeasible;
285 }
286 termination.limit = limit;
287 return termination;
288}
289
290Termination Termination::Feasible(
291 const bool is_maximize, const Limit limit,
292 const double finite_primal_objective,
293 const std::optional<double> optional_dual_objective,
294 const std::string detail) {
296 termination.problem_status.primal_status = FeasibilityStatus::kFeasible;
297 termination.objective_bounds.primal_bound = finite_primal_objective;
298 termination.problem_status.dual_status = FeasibilityStatus::kUndetermined;
299 if (optional_dual_objective.has_value()) {
300 termination.objective_bounds.dual_bound = *optional_dual_objective;
301 termination.problem_status.dual_status = FeasibilityStatus::kFeasible;
302 }
303 termination.limit = limit;
304 return termination;
305}
306
307Termination Termination::Cutoff(const bool is_maximize,
308 const std::string detail) {
309 return NoSolutionFound(is_maximize, Limit::kCutoff,
310 /*optional_dual_objective=*/std::nullopt, detail);
311}
312
313TerminationProto Termination::Proto() const {
314 TerminationProto proto;
315 proto.set_reason(EnumToProto(reason));
316 proto.set_limit(EnumToProto(limit));
317 proto.set_detail(detail);
318 *proto.mutable_problem_status() = problem_status.Proto();
319 *proto.mutable_objective_bounds() = objective_bounds.Proto();
320 return proto;
321}
322
327
328absl::Status Termination::EnsureReasonIs(TerminationReason reason) const {
329 if (this->reason == reason) return absl::OkStatus();
331 << "expected termination reason '" << reason << "' but got " << *this;
332}
333
334absl::Status Termination::EnsureReasonIsAnyOf(
335 std::initializer_list<TerminationReason> reasons) const {
336 for (const TerminationReason reason : reasons) {
337 if (this->reason == reason) return absl::OkStatus();
338 }
340 << "expected termination reason in {"
341 << absl::StrJoin(reasons, ", ",
342 [](std::string* out, TerminationReason reason) {
343 absl::StrAppend(out, "'");
344 absl::StreamFormatter()(out, reason);
345 absl::StrAppend(out, "'");
346 })
347 << "} but got " << *this;
348}
349
350absl::Status Termination::EnsureIsOptimal() const {
351 return EnsureReasonIs(TerminationReason::kOptimal);
352}
353
354bool Termination::IsOptimalOrFeasible() const {
357}
358
360 return EnsureReasonIsAnyOf(
362}
363
364bool Termination::IsOptimal() const {
366}
367
368absl::StatusOr<Termination> Termination::FromProto(
369 const TerminationProto& termination_proto) {
370 const std::optional<TerminationReason> reason =
371 EnumFromProto(termination_proto.reason());
372 if (!reason.has_value()) {
373 return absl::InvalidArgumentError("reason must be specified");
374 }
375 Termination result(/*is_maximize=*/false, *reason,
376 termination_proto.detail());
377 result.limit = EnumFromProto(termination_proto.limit());
379 result.problem_status,
380 ProblemStatus::FromProto(termination_proto.problem_status()),
381 _ << "invalid problem_status");
382 result.objective_bounds =
383 ObjectiveBounds::FromProto(termination_proto.objective_bounds());
384 return result;
385}
386
387std::ostream& operator<<(std::ostream& ostr, const Termination& termination) {
388 ostr << "{reason: " << termination.reason;
389 if (termination.limit.has_value()) {
390 ostr << ", limit: " << *termination.limit;
391 }
392 if (!termination.detail.empty()) {
393 ostr << ", detail: " << '"' << absl::CEscape(termination.detail) << '"';
394 }
395 ostr << ", problem_status: " << termination.problem_status;
396 ostr << ", objective_bounds: " << termination.objective_bounds;
397 ostr << "}";
398 return ostr;
399}
400
401std::string Termination::ToString() const {
402 std::ostringstream stream;
403 stream << *this;
404 return stream.str();
405}
406
407ProblemStatusProto ProblemStatus::Proto() const {
408 ProblemStatusProto proto;
409 proto.set_primal_status(EnumToProto(primal_status));
410 proto.set_dual_status(EnumToProto(dual_status));
411 proto.set_primal_or_dual_infeasible(primal_or_dual_infeasible);
412 return proto;
413}
414
415absl::StatusOr<ProblemStatus> ProblemStatus::FromProto(
416 const ProblemStatusProto& problem_status_proto) {
417 const std::optional<FeasibilityStatus> primal_status =
418 EnumFromProto(problem_status_proto.primal_status());
419 if (!primal_status.has_value()) {
420 return absl::InvalidArgumentError("primal_status must be specified");
421 }
422 const std::optional<FeasibilityStatus> dual_status =
423 EnumFromProto(problem_status_proto.dual_status());
424 if (!dual_status.has_value()) {
425 return absl::InvalidArgumentError("dual_status must be specified");
426 }
427 return ProblemStatus{.primal_status = *primal_status,
428 .dual_status = *dual_status,
429 .primal_or_dual_infeasible =
430 problem_status_proto.primal_or_dual_infeasible()};
431}
432
433std::ostream& operator<<(std::ostream& ostr,
434 const ProblemStatus& problem_status) {
435 ostr << "{primal_status: " << problem_status.primal_status;
436 ostr << ", dual_status: " << problem_status.dual_status;
437 ostr << ", primal_or_dual_infeasible: "
438 << (problem_status.primal_or_dual_infeasible ? "true" : "false");
439 ostr << "}";
440 return ostr;
441}
442
443std::string ProblemStatus::ToString() const {
444 std::ostringstream stream;
445 stream << *this;
446 return stream.str();
447}
448
449absl::StatusOr<SolveStatsProto> SolveStats::Proto() const {
450 SolveStatsProto proto;
452 util_time::EncodeGoogleApiProto(solve_time, proto.mutable_solve_time()))
453 << "invalid solve_time (value must be finite)";
454 proto.set_simplex_iterations(simplex_iterations);
455 proto.set_barrier_iterations(barrier_iterations);
456 proto.set_first_order_iterations(first_order_iterations);
457 proto.set_node_count(node_count);
458 return proto;
459}
460
461absl::StatusOr<SolveStats> SolveStats::FromProto(
462 const SolveStatsProto& solve_stats_proto) {
463 SolveStats result;
465 result.solve_time,
466 util_time::DecodeGoogleApiProto(solve_stats_proto.solve_time()),
467 _ << "invalid solve_time");
468 result.simplex_iterations = solve_stats_proto.simplex_iterations();
469 result.barrier_iterations = solve_stats_proto.barrier_iterations();
470 result.first_order_iterations = solve_stats_proto.first_order_iterations();
471 result.node_count = solve_stats_proto.node_count();
472 return result;
473}
474
475std::ostream& operator<<(std::ostream& ostr, const SolveStats& solve_stats) {
476 ostr << "{solve_time: " << solve_stats.solve_time;
477 ostr << ", simplex_iterations: " << solve_stats.simplex_iterations;
478 ostr << ", barrier_iterations: " << solve_stats.barrier_iterations;
479 ostr << ", first_order_iterations: " << solve_stats.first_order_iterations;
480 ostr << ", node_count: " << solve_stats.node_count;
481 ostr << "}";
482 return ostr;
483}
484
485std::string SolveStats::ToString() const {
486 std::ostringstream stream;
487 stream << *this;
488 return stream.str();
489}
490
491absl::Status CheckSolverSpecificOutputEmpty(const SolveResultProto& result) {
492 if (result.solver_specific_output_case() ==
493 SolveResultProto::SOLVER_SPECIFIC_OUTPUT_NOT_SET) {
494 return absl::OkStatus();
495 }
497 << "cannot set solver specific output twice, was already "
498 << static_cast<int>(result.solver_specific_output_case());
499}
500
501absl::StatusOr<SolveResultProto> SolveResult::Proto() const {
502 SolveResultProto result;
503 *result.mutable_termination() = termination.Proto();
504 OR_ASSIGN_OR_RETURN3(*result.mutable_solve_stats(), solve_stats.Proto(),
505 _ << "invalid solve_stats");
506 for (const Solution& solution : solutions) {
507 *result.add_solutions() = solution.Proto();
508 }
509 for (const PrimalRay& primal_ray : primal_rays) {
510 *result.add_primal_rays() = primal_ray.Proto();
511 }
512 for (const DualRay& dual_ray : dual_rays) {
513 *result.add_dual_rays() = dual_ray.Proto();
514 }
515 // See yaqs/5107601535926272 on checking if a proto is empty.
516 if (gscip_solver_specific_output.ByteSizeLong() > 0) {
517 *result.mutable_gscip_output() = gscip_solver_specific_output;
518 }
519 if (pdlp_solver_specific_output.ByteSizeLong() > 0) {
520 *result.mutable_pdlp_output() = pdlp_solver_specific_output;
521 }
522 return result;
523}
524namespace {
525TerminationProto UpgradedTerminationProtoForStatsMigration(
526 const SolveResultProto& solve_result_proto) {
527 TerminationProto termination;
528 termination.set_reason(solve_result_proto.termination().reason());
529 termination.set_limit(solve_result_proto.termination().limit());
530 termination.set_detail(solve_result_proto.termination().detail());
531 *termination.mutable_problem_status() = GetProblemStatus(solve_result_proto);
532 *termination.mutable_objective_bounds() =
533 GetObjectiveBounds(solve_result_proto);
534 return termination;
535}
536
537} // namespace
538absl::StatusOr<SolveResult> SolveResult::FromProto(
539 const ModelStorage* model, const SolveResultProto& solve_result_proto) {
541 auto termination,
542 Termination::FromProto(
543 // TODO(b/290091715): Remove once solve_stats proto no longer has
544 // best_primal/dual_bound/problem_status and
545 // problem_status/objective_bounds are guaranteed to be present in
546 // termination proto.
547 UpgradedTerminationProtoForStatsMigration(solve_result_proto)),
548 _ << "invalid termination");
549 SolveResult result(std::move(termination));
550 OR_ASSIGN_OR_RETURN3(result.solve_stats,
551 SolveStats::FromProto(solve_result_proto.solve_stats()),
552 _ << "invalid solve_stats");
553
554 for (int i = 0; i < solve_result_proto.solutions_size(); ++i) {
556 auto solution,
557 Solution::FromProto(model, solve_result_proto.solutions(i)),
558 _ << "invalid solution at index " << i);
559 result.solutions.push_back(std::move(solution));
560 }
561 for (int i = 0; i < solve_result_proto.primal_rays_size(); ++i) {
563 auto primal_ray,
564 PrimalRay::FromProto(model, solve_result_proto.primal_rays(i)),
565 _ << "invalid primal ray at index " << i);
566 result.primal_rays.push_back(std::move(primal_ray));
567 }
568 for (int i = 0; i < solve_result_proto.dual_rays_size(); ++i) {
570 auto dual_ray,
571 DualRay::FromProto(model, solve_result_proto.dual_rays(i)),
572 _ << "invalid dual ray at index " << i);
573 result.dual_rays.push_back(std::move(dual_ray));
574 }
575 switch (solve_result_proto.solver_specific_output_case()) {
576 case SolveResultProto::kGscipOutput:
577 result.gscip_solver_specific_output = solve_result_proto.gscip_output();
578 return result;
579 case SolveResultProto::kPdlpOutput:
580 result.pdlp_solver_specific_output = solve_result_proto.pdlp_output();
581 return result;
582 case SolveResultProto::SOLVER_SPECIFIC_OUTPUT_NOT_SET:
583 return result;
584 }
586 << "unexpected value of solver_specific_output_case "
587 << solve_result_proto.solver_specific_output_case();
588}
589
590bool SolveResult::has_primal_feasible_solution() const {
591 return !solutions.empty() && solutions[0].primal_solution.has_value() &&
592 (solutions[0].primal_solution->feasibility_status ==
594}
595
596const PrimalSolution& SolveResult::best_primal_solution() const {
597 CHECK(has_primal_feasible_solution());
598 return *solutions.front().primal_solution;
599}
600
601double SolveResult::best_objective_bound() const {
602 return termination.objective_bounds.dual_bound;
603}
604
605double SolveResult::primal_bound() const {
606 return termination.objective_bounds.primal_bound;
607}
608
609double SolveResult::dual_bound() const {
610 return termination.objective_bounds.dual_bound;
611}
612
613double SolveResult::objective_value() const {
614 CHECK(has_primal_feasible_solution());
615 return solutions[0].primal_solution->objective_value;
616}
617
618double SolveResult::objective_value(const Objective objective) const {
619 CHECK(has_primal_feasible_solution());
620 return solutions[0].primal_solution->get_objective_value(objective);
621}
622
623bool SolveResult::bounded() const {
624 return termination.problem_status.primal_status ==
626 termination.problem_status.dual_status == FeasibilityStatus::kFeasible;
627}
628
629const VariableMap<double>& SolveResult::variable_values() const {
630 CHECK(has_primal_feasible_solution());
631 return solutions[0].primal_solution->variable_values;
632}
633
634const VariableMap<double>& SolveResult::ray_variable_values() const {
635 CHECK(has_ray());
636 return primal_rays[0].variable_values;
637}
638
639bool SolveResult::has_dual_feasible_solution() const {
640 return !solutions.empty() && solutions[0].dual_solution.has_value() &&
641 (solutions[0].dual_solution->feasibility_status ==
643}
644
645const LinearConstraintMap<double>& SolveResult::dual_values() const {
646 CHECK(has_dual_feasible_solution());
647 return solutions[0].dual_solution->dual_values;
648}
649
650const VariableMap<double>& SolveResult::reduced_costs() const {
651 CHECK(has_dual_feasible_solution());
652 return solutions[0].dual_solution->reduced_costs;
653}
654const LinearConstraintMap<double>& SolveResult::ray_dual_values() const {
655 CHECK(has_dual_ray());
656 return dual_rays[0].dual_values;
657}
658
659const VariableMap<double>& SolveResult::ray_reduced_costs() const {
660 CHECK(has_dual_ray());
661 return dual_rays[0].reduced_costs;
662}
663
664bool SolveResult::has_basis() const {
665 return !solutions.empty() && solutions[0].basis.has_value();
666}
667
668const LinearConstraintMap<BasisStatus>& SolveResult::constraint_status() const {
669 CHECK(has_basis());
670 return solutions[0].basis->constraint_status;
671}
672
673const VariableMap<BasisStatus>& SolveResult::variable_status() const {
674 CHECK(has_basis());
675 return solutions[0].basis->variable_status;
676}
677
678namespace {
679// Prints only the vector size, not its content.
680template <typename T>
681void PrintVectorSize(std::ostream& out, const std::vector<T>& v) {
682 out << '[';
683 if (!v.empty()) {
684 out << v.size() << " available";
685 }
686 out << ']';
687}
688} // namespace
689
690std::ostream& operator<<(std::ostream& out, const SolveResult& result) {
691 out << "{termination: " << result.termination
692 << ", solve_stats: " << result.solve_stats << ", solutions: ";
693 PrintVectorSize(out, result.solutions);
694 out << ", primal_rays: ";
695 PrintVectorSize(out, result.primal_rays);
696 out << ", dual_rays: ";
697 PrintVectorSize(out, result.dual_rays);
698 {
699 const std::string gscip_specific_output =
700 ProtobufShortDebugString(result.gscip_solver_specific_output);
701 if (!gscip_specific_output.empty()) {
702 out << ", gscip_solver_specific_output: " << gscip_specific_output;
703 }
704 }
705 out << '}';
706
707 return out;
708}
709
710} // namespace operations_research::math_opt
#define RETURN_IF_ERROR(expr)
CpModelProto proto
The output proto.
int64_t value
GRBmodel * model
TerminationReason termination
double solution
An object oriented wrapper for quadratic constraints in ModelStorage.
Definition gurobi_isv.cc:28
absl::Status CheckSolverSpecificOutputEmpty(const SolveResultProto &result)
absl::flat_hash_map< Variable, V > VariableMap
TerminationReason
The reason a call to Solve() terminates.
@ kOptimal
A provably optimal solution (up to numerical tolerances) has been found.
@ kInfeasible
The primal problem has no feasible solutions.
absl::flat_hash_map< LinearConstraint, V > LinearConstraintMap
std::optional< typename EnumProto< P >::Cpp > EnumFromProto(P proto_value)
Definition enums.h:281
std::ostream & operator<<(std::ostream &ostr, const IndicatorConstraint &constraint)
ProblemStatusProto GetProblemStatus(const SolveResultProto &solve_result)
ObjectiveBoundsProto GetObjectiveBounds(const SolveResultProto &solve_result)
@ kFeasible
Solver claims the solution is feasible.
Enum< E >::Proto EnumToProto(std::optional< E > value)
Definition enums.h:270
@ kMemory
The algorithm stopped because it ran out of memory.
@ kTime
The algorithm stopped after a user-specified computation time.
@ kNorm
The algorithm stopped because the norm of an iterate became too large.
@ kUndetermined
Solver does not claim a status.
@ kFeasible
Solver claims the problem is feasible.
@ kInfeasible
Solver claims the problem is infeasible.
std::string ProtobufShortDebugString(const P &message)
Definition proto_utils.h:41
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
StatusBuilder InternalErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()
static absl::StatusOr< DualRay > FromProto(const ModelStorage *model, const DualRayProto &dual_ray_proto)
Definition solution.cc:162
static absl::Span< const E > AllValues()
Returns all possible values of the enum.
static std::optional< absl::string_view > ToOptString(E value)
Bounds on the optimal objective value.
static ObjectiveBounds MakeTrivial(bool is_maximize)
static ObjectiveBounds MakeUnbounded(bool is_maximize)
static ObjectiveBounds MakeOptimal(double objective_value)
Sets both bounds to objective_value.
static ObjectiveBounds FromProto(const ObjectiveBoundsProto &objective_bounds_proto)
static absl::StatusOr< PrimalRay > FromProto(const ModelStorage *model, const PrimalRayProto &primal_ray_proto)
Definition solution.cc:106
FeasibilityStatus primal_status
Status for the primal problem.
static absl::StatusOr< ProblemStatus > FromProto(const ProblemStatusProto &problem_status_proto)
Returns an error if the primal_status or dual_status is unspecified.
FeasibilityStatus dual_status
Status for the dual problem (or for the dual of a continuous relaxation).
static absl::StatusOr< Solution > FromProto(const ModelStorage *model, const SolutionProto &solution_proto)
Definition solution.cc:225
absl::StatusOr< SolveStatsProto > Proto() const
Will return an error if solve_time is not finite.
static absl::StatusOr< SolveStats > FromProto(const SolveStatsProto &solve_stats_proto)
Returns an error if converting the problem_status or solve_time fails.
All information regarding why a call to Solve() terminated.
Termination(bool is_maximize, TerminationReason reason, std::string detail={})
ProblemStatus problem_status
Feasibility statuses for primal and dual problems.
ObjectiveBounds objective_bounds
Bounds on the optimal objective value.
double objective_value
The value objective_vector^T * (solution - center_point).
#define OR_ASSIGN_OR_RETURN3(lhs, rexpr, error_expression)