18#include "absl/status/status.h"
19#include "google/protobuf/repeated_ptr_field.h"
35constexpr double kInf = std::numeric_limits<double>::infinity();
38 return solution.has_primal_solution() &&
39 solution.primal_solution().feasibility_status() ==
43bool HasPrimalFeasibleSolution(
44 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions) {
46 return !solutions.empty() && HasPrimalFeasibleSolution(solutions.at(0));
50 return solution.has_dual_solution() &&
51 solution.dual_solution().feasibility_status() ==
55bool HasDualFeasibleSolution(
56 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions) {
57 for (
const auto&
solution : solutions) {
58 if (HasDualFeasibleSolution(
solution)) {
67 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
71 for (
int i = 0; i < solutions.size(); ++i) {
73 <<
"invalid solutions[" << i <<
"]";
76 if (solutions.empty())
return absl::OkStatus();
80 bool previous_primal_feasible = HasPrimalFeasibleSolution(solutions[0]);
81 bool previous_dual_feasible = HasDualFeasibleSolution(solutions[0]);
82 for (
int i = 1; i < solutions.size(); ++i) {
83 const bool current_primal_feasible =
84 HasPrimalFeasibleSolution(solutions[i]);
85 const bool current_dual_feasible = HasDualFeasibleSolution(solutions[i]);
87 if (current_primal_feasible && !previous_primal_feasible) {
88 return absl::InvalidArgumentError(
89 "primal solution ordering not satisfied");
96 if (current_dual_feasible && !previous_dual_feasible) {
97 if (!(previous_primal_feasible && !current_primal_feasible)) {
98 return absl::InvalidArgumentError(
99 "dual solution ordering not satisfied");
102 previous_primal_feasible = current_primal_feasible;
103 previous_dual_feasible = current_dual_feasible;
105 return absl::OkStatus();
109absl::Status RequireNoPrimalFeasibleSolution(
const SolveResultProto& result) {
110 if (HasPrimalFeasibleSolution(result.solutions())) {
111 return absl::InvalidArgumentError(
112 "expected no primal feasible solution, but one was returned");
115 return absl::OkStatus();
118bool FirstPrimalObjectiveIsStrictlyBetter(
const double first_objective,
119 const double second_objective,
120 const bool maximize) {
122 return first_objective > second_objective;
124 return first_objective < second_objective;
127bool FirstDualObjectiveIsStrictlyBetter(
const double first_objective,
128 const double second_objective,
129 const bool maximize) {
131 return second_objective > first_objective;
133 return second_objective < first_objective;
136double GetBestPrimalObjective(
137 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
138 const bool maximize) {
139 double best_objective = maximize ? -
kInf :
kInf;
140 for (
int i = 0;
i < solutions.size(); ++
i) {
141 if (HasPrimalFeasibleSolution(solutions[i])) {
142 const double primal_objective =
143 solutions[
i].primal_solution().objective_value();
144 if (FirstPrimalObjectiveIsStrictlyBetter(primal_objective, best_objective,
146 best_objective = primal_objective;
150 return best_objective;
153double GetBestDualObjective(
154 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
155 const bool maximize) {
156 double best_objective = maximize ?
kInf : -
kInf;
157 for (
int i = 0;
i < solutions.size(); ++
i) {
158 if (HasDualFeasibleSolution(solutions[i])) {
160 if (dual_solution.has_objective_value() &&
161 FirstDualObjectiveIsStrictlyBetter(dual_solution.objective_value(),
162 best_objective, maximize)) {
163 best_objective = dual_solution.objective_value();
167 return best_objective;
172 if (!HasPrimalFeasibleSolution(result.
solutions())) {
173 return absl::InvalidArgumentError(
174 "primal feasible solution expected, but not found");
177 return absl::OkStatus();
182 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
183 const bool maximize) {
184 if (!HasPrimalFeasibleSolution(solutions)) {
185 return absl::OkStatus();
189 return absl::InvalidArgumentError(
190 "primal feasibility status is not FEASIBILITY_STATUS_FEASIBLE, but "
191 "primal feasible solution is returned.");
193 const double best_objective = GetBestPrimalObjective(solutions, maximize);
195 if (FirstPrimalObjectiveIsStrictlyBetter(best_objective, primal_bound,
198 <<
"best primal feasible solution objective = " << best_objective
199 <<
" is better than primal_bound = " << primal_bound;
201 return absl::OkStatus();
206 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions,
207 const bool maximize) {
210 HasDualFeasibleSolution(solutions)) {
211 return absl::InvalidArgumentError(
212 "dual feasibility status is not FEASIBILITY_STATUS_FEASIBLE, but "
213 "dual feasible solution is returned.");
215 const double best_objective = GetBestDualObjective(solutions, maximize);
217 if (FirstDualObjectiveIsStrictlyBetter(best_objective, dual_bound,
220 <<
"best dual feasible solution objective = " << best_objective
221 <<
" is better than dual_bound = " << dual_bound;
223 return absl::OkStatus();
229absl::Status ValidateSolveStatsTerminationEqualities(
230 const SolveResultProto& solve_result) {
231 const ObjectiveBoundsProto objective_bounds =
233 const SolveStatsProto& solve_stats = solve_result.solve_stats();
235 if (problem_status.primal_status() !=
236 solve_stats.problem_status().primal_status()) {
238 << problem_status.primal_status()
239 <<
" = termination.problem_status.primal_status != "
240 "solve_stats.problem_status.primal_status = "
241 << solve_stats.problem_status().primal_status();
243 if (problem_status.dual_status() !=
244 solve_stats.problem_status().dual_status()) {
246 << problem_status.dual_status()
247 <<
" = termination.problem_status.dual_status != "
248 "solve_stats.problem_status.dual_status = "
249 << solve_stats.problem_status().dual_status();
251 if (problem_status.primal_or_dual_infeasible() !=
252 solve_stats.problem_status().primal_or_dual_infeasible()) {
254 << problem_status.primal_or_dual_infeasible()
255 <<
" = termination.problem_status.primal_or_dual_infeasible != "
256 "solve_stats.problem_status.primal_or_dual_infeasible = "
257 << solve_stats.problem_status().primal_or_dual_infeasible();
259 if (objective_bounds.primal_bound() != solve_stats.best_primal_bound()) {
261 << objective_bounds.primal_bound()
262 <<
" = termination.objective_bounds.primal_bound != "
263 "solve_stats.best_primal_bound = "
264 << solve_stats.best_primal_bound();
266 if (objective_bounds.dual_bound() != solve_stats.best_dual_bound()) {
268 << objective_bounds.dual_bound()
269 <<
" = termination.objective_bounds.dual_bound != "
270 "solve_stats.best_dual_bound = "
271 << solve_stats.best_dual_bound();
273 return absl::OkStatus();
300 <<
"inconsistent termination reason "
305 <<
"inconsistent termination reason "
317 return absl::InvalidArgumentError(
318 "termination.problem_status.dual_status = "
319 "FEASIBILITY_STATUS_FEASIBLE, "
320 "but a primal ray is returned");
326 <<
"Invalid primal_rays[" << i <<
"]";
331 return absl::InvalidArgumentError(
332 "termination.problem_status.primal_status = "
333 "FEASIBILITY_STATUS_FEASIBLE, but a dual ray is returned");
338 <<
"Invalid dual_rays[" << i <<
"]";
341 return absl::OkStatus();
#define RETURN_IF_ERROR(expr)
const ::operations_research::math_opt::SparseVectorFilterProto & variable_values_filter() const
double dual_bound() const
double primal_bound() const
::operations_research::math_opt::FeasibilityStatusProto dual_status() const
::operations_research::math_opt::FeasibilityStatusProto primal_status() const
const ::operations_research::math_opt::PrimalRayProto & primal_rays(int index) const
const ::operations_research::math_opt::SolutionProto & solutions(int index) const
const ::operations_research::math_opt::DualRayProto & dual_rays(int index) const
int dual_rays_size() const
repeated .operations_research.math_opt.DualRayProto dual_rays = 5;
const ::operations_research::math_opt::SolveStatsProto & solve_stats() const
int primal_rays_size() const
repeated .operations_research.math_opt.PrimalRayProto primal_rays = 4;
const ::operations_research::math_opt::TerminationProto & termination() const
const ::operations_research::math_opt::ObjectiveBoundsProto & objective_bounds() const
::operations_research::math_opt::ObjectiveBoundsProto *PROTOBUF_NONNULL mutable_objective_bounds()
::operations_research::math_opt::ProblemStatusProto *PROTOBUF_NONNULL mutable_problem_status()
const ::operations_research::math_opt::ProblemStatusProto & problem_status() const
::operations_research::math_opt::TerminationReasonProto reason() const
An object oriented wrapper for quadratic constraints in ModelStorage.
@ TERMINATION_REASON_OPTIMAL
@ TERMINATION_REASON_NO_SOLUTION_FOUND
@ TERMINATION_REASON_FEASIBLE
@ SOLUTION_STATUS_FEASIBLE
absl::Status ValidateDualRay(const DualRayProto &dual_ray, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status CheckDualSolutionAndStatusConsistency(const TerminationProto &termination, const google::protobuf::RepeatedPtrField< SolutionProto > &solutions, const bool maximize)
@ FEASIBILITY_STATUS_FEASIBLE
ProblemStatusProto GetProblemStatus(const SolveResultProto &solve_result)
ObjectiveBoundsProto GetObjectiveBounds(const SolveResultProto &solve_result)
absl::Status ValidateSolveStats(const SolveStatsProto &solve_stats)
absl::Status CheckHasPrimalSolution(const SolveResultProto &result)
Returns absl::Ok only if a primal feasible solution is available.
absl::Status ValidateSolution(const SolutionProto &solution, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status CheckPrimalSolutionAndTerminationConsistency(const TerminationProto &termination, const google::protobuf::RepeatedPtrField< SolutionProto > &solutions, const bool maximize)
absl::Status ValidateResult(const SolveResultProto &result, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
Validates the input result.
absl::Status ValidateTermination(const TerminationProto &termination, const bool is_maximize)
Checks all messages are valid and compatible.
absl::Status ValidateSolutions(const google::protobuf::RepeatedPtrField< SolutionProto > &solutions, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status ValidatePrimalRay(const PrimalRayProto &primal_ray, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
In SWIG mode, we don't want anything besides these top-level includes.
Select next search node to expand Select next item_i to add this new search node to the search Generate a new search node where item_i is not in the knapsack Check validity of this new partial solution(using propagators) - If valid
std::string ProtoEnumToString(ProtoEnumType enum_value)
StatusBuilder InvalidArgumentErrorBuilder()