18#include "absl/status/status.h"
19#include "google/protobuf/repeated_ptr_field.h"
24#include "ortools/math_opt/model_parameters.pb.h"
25#include "ortools/math_opt/result.pb.h"
26#include "ortools/math_opt/solution.pb.h"
35constexpr double kInf = std::numeric_limits<double>::infinity();
37bool HasPrimalFeasibleSolution(
const SolutionProto&
solution) {
38 return solution.has_primal_solution() &&
39 solution.primal_solution().feasibility_status() ==
40 SOLUTION_STATUS_FEASIBLE;
43bool HasPrimalFeasibleSolution(
44 const google::protobuf::RepeatedPtrField<SolutionProto>& solutions) {
46 return !solutions.empty() && HasPrimalFeasibleSolution(solutions.at(0));
49bool HasDualFeasibleSolution(
const SolutionProto&
solution) {
50 return solution.has_dual_solution() &&
51 solution.dual_solution().feasibility_status() ==
52 SOLUTION_STATUS_FEASIBLE;
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])) {
159 const DualSolutionProto& dual_solution = solutions[
i].dual_solution();
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();
187 if (
termination.problem_status().primal_status() !=
188 FEASIBILITY_STATUS_FEASIBLE) {
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);
194 const double primal_bound =
termination.objective_bounds().primal_bound();
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) {
209 FEASIBILITY_STATUS_FEASIBLE &&
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);
216 const double dual_bound =
termination.objective_bounds().dual_bound();
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();
288 TerminationProto
termination = result.termination();
297 if (
termination.reason() == TERMINATION_REASON_OPTIMAL ||
298 termination.reason() == TERMINATION_REASON_FEASIBLE) {
300 <<
"inconsistent termination reason "
303 if (
termination.reason() == TERMINATION_REASON_NO_SOLUTION_FOUND) {
305 <<
"inconsistent termination reason "
310 result.termination(), result.solutions(), model_summary.
maximize));
312 result.termination(), result.solutions(), model_summary.
maximize));
314 if (result.primal_rays_size() > 0 &&
316 FEASIBILITY_STATUS_FEASIBLE) {
317 return absl::InvalidArgumentError(
318 "termination.problem_status.dual_status = "
319 "FEASIBILITY_STATUS_FEASIBLE, "
320 "but a primal ray is returned");
322 for (
int i = 0; i < result.primal_rays_size(); ++i) {
326 <<
"Invalid primal_rays[" << i <<
"]";
328 if (result.dual_rays_size() > 0 &&
330 FEASIBILITY_STATUS_FEASIBLE) {
331 return absl::InvalidArgumentError(
332 "termination.problem_status.primal_status = "
333 "FEASIBILITY_STATUS_FEASIBLE, but a dual ray is returned");
335 for (
int i = 0; i < result.dual_rays_size(); ++i) {
338 <<
"Invalid dual_rays[" << i <<
"]";
341 return absl::OkStatus();
#define RETURN_IF_ERROR(expr)
TerminationReason termination
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)
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.
std::string ProtoEnumToString(ProtoEnumType enum_value)
StatusBuilder InvalidArgumentErrorBuilder()