20#include "absl/status/status.h"
21#include "absl/strings/str_cat.h"
25#include "ortools/math_opt/model_parameters.pb.h"
26#include "ortools/math_opt/solution.pb.h"
27#include "ortools/math_opt/sparse_containers.pb.h"
37constexpr double kInf = std::numeric_limits<double>::infinity();
39absl::Status ValidateSolutionStatus(
const SolutionStatusProto&
status) {
40 if (!SolutionStatusProto_IsValid(
status)) {
41 return absl::InvalidArgumentError(absl::StrCat(
"status = ",
status));
43 if (
status == SOLUTION_STATUS_UNSPECIFIED) {
44 return absl::InvalidArgumentError(
"status = SOLUTION_STATUS_UNSPECIFIED");
46 return absl::OkStatus();
63absl::Status IsFiltered(
const SparseVectorView<T>& vector_view,
64 const SparseVectorFilterProto& filter,
65 const IdNameBiMap& all_items) {
67 SparseVectorFilterPredicate predicate(filter);
70 for (
int i = 0; i < vector_view.ids_size(); ++i) {
71 const int64_t
id = vector_view.ids(i);
72 if (!predicate.AcceptsAndUpdate(
id, vector_view.values(i))) {
73 return absl::InvalidArgumentError(
74 absl::StrCat(
"sparse vector should not contain the pair (id: ",
id,
75 ", value: ", vector_view.values(i),
") (at index: ", i,
76 ") that should have been filtered"));
82 if (filter.skip_zero_values()) {
83 return absl::OkStatus();
86 const int expected_size =
87 filter.filter_by_ids() ? filter.filtered_ids_size() : all_items.Size();
88 if (vector_view.ids_size() != expected_size) {
89 return absl::InvalidArgumentError(absl::StrCat(
90 "sparse vector should contain ", expected_size,
" values but contains ",
91 vector_view.ids_size(),
" instead"));
94 return absl::OkStatus();
105absl::Status IsValidSolutionVector(
const SparseDoubleVectorProto& vector,
106 const SparseVectorFilterProto& filter,
107 const IdNameBiMap& all_items) {
108 const auto vector_view =
MakeView(vector);
111 {.allow_positive_infinity =
false, .allow_negative_infinity =
false}));
113 return absl::OkStatus();
121 if (!solution.has_primal_solution() && !solution.has_dual_solution() &&
122 !solution.has_basis()) {
123 return absl::InvalidArgumentError(
"empty solution");
125 if (solution.has_primal_solution()) {
129 <<
"Invalid primal_solution";
131 if (solution.has_dual_solution()) {
134 <<
"Invalid dual_solution";
136 if (solution.has_basis()) {
142 if (solution.has_basis() && solution.has_dual_solution()) {
143 if (solution.basis().basic_dual_feasibility() == SOLUTION_STATUS_FEASIBLE &&
144 solution.dual_solution().feasibility_status() !=
145 SOLUTION_STATUS_FEASIBLE) {
146 return absl::InvalidArgumentError(
147 "Incompatible basis and dual solution: basis is dual feasible, but "
148 "dual solution is not feasible");
150 if (solution.dual_solution().feasibility_status() ==
151 SOLUTION_STATUS_INFEASIBLE &&
152 solution.basis().basic_dual_feasibility() !=
153 SOLUTION_STATUS_INFEASIBLE) {
154 return absl::InvalidArgumentError(
155 "Incompatible basis and dual solution: dual solution is infeasible, "
156 "but basis is not dual infeasible");
159 return absl::OkStatus();
163 const SparseVectorFilterProto& filter,
166 IsValidSolutionVector(vector, filter, model_summary.
variables));
167 return absl::OkStatus();
171 const SparseVectorFilterProto& filter,
173 RETURN_IF_ERROR(ValidateSolutionStatus(primal_solution.feasibility_status()))
174 <<
"Invalid PrimalSolutionProto.feasibility_status";
176 primal_solution.variable_values(), filter, model_summary))
177 <<
"Invalid PrimalSolutionProto.variable_values";
179 <<
"Invalid PrimalSolutionProto.objective_value";
180 return absl::OkStatus();
184 const SparseVectorFilterProto& filter,
186 RETURN_IF_ERROR(IsValidSolutionVector(primal_ray.variable_values(), filter,
188 <<
"Invalid PrimalRayProto.variable_values";
189 return absl::OkStatus();
195 RETURN_IF_ERROR(ValidateSolutionStatus(dual_solution.feasibility_status()))
196 <<
"Invalid DualSolutionProto.feasibility_status";
200 <<
"Invalid DualSolutionProto.dual_values";
204 <<
"Invalid DualSolutionProto.reduced_costs";
206 <<
"Invalid DualSolutionProto.objective_value";
207 return absl::OkStatus();
216 <<
"Invalid DualRayProto.dual_values";
220 <<
"Invalid DualRayProto.reduced_costs";
221 return absl::OkStatus();
231 for (
auto [
id,
value] : status_vector_view) {
232 if (!BasisStatusProto_IsValid(
value)) {
233 return absl::InvalidArgumentError(
234 absl::StrCat(
"Invalid status: ",
value,
" for id ",
id));
236 if (
value == BASIS_STATUS_UNSPECIFIED) {
237 return absl::InvalidArgumentError(
238 absl::StrCat(
"Found BASIS_STATUS_UNSPECIFIED for id ",
id));
241 return absl::OkStatus();
246 const bool check_dual_feasibility) {
247 if (check_dual_feasibility) {
248 RETURN_IF_ERROR(ValidateSolutionStatus(basis.basic_dual_feasibility()))
249 <<
"Invalid BasisProto.basic_dual_feasibility";
251 const auto constraint_status_view =
MakeView(basis.constraint_status());
252 const auto variable_status_view =
MakeView(basis.variable_status());
254 << absl::StrCat(
"BasisProto.constraint_status invalid");
256 << absl::StrCat(
"BasisProto.variable_status invalid");
260 "BasisProto.constraint_status.ids",
"model_summary.linear_constraints"));
262 basis.variable_status().ids(), model_summary.
variables,
263 "BasisProto.variable_status.ids",
"model_summary.variables"));
265 int non_basic_variables = 0;
266 for (
const auto [
id,
value] : constraint_status_view) {
267 if (
value != BASIS_STATUS_BASIC) {
268 non_basic_variables++;
271 for (
auto [
id,
value] : variable_status_view) {
272 if (
value != BASIS_STATUS_BASIC) {
273 non_basic_variables++;
277 return absl::InvalidArgumentError(absl::StrCat(
278 "Inconsistent number of non-basic variable+constraints: ",
279 non_basic_variables,
", variables: ", model_summary.
variables.
Size()));
281 return absl::OkStatus();
absl::Status CheckIdsAndValuesSize(const SparseVectorView< T > &vector_view, absl::string_view value_name="values")
absl::Status ValidateBasis(const BasisProto &basis, const ModelSummary &model_summary, const bool check_dual_feasibility)
absl::Status SparseBasisStatusVectorIsValid(const SparseVectorView< int > &status_vector_view)
absl::Status ValidatePrimalSolution(const PrimalSolutionProto &primal_solution, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
absl::Status ValidatePrimalSolutionVector(const SparseDoubleVectorProto &vector, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::Status CheckIdsAndValues(const SparseVectorView< T > &vector_view, absl::string_view value_name="values")
absl::Status ValidatePrimalRay(const PrimalRayProto &primal_ray, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
absl::Status CheckIdsSubset(absl::Span< const int64_t > ids, const IdNameBiMap &universe, absl::string_view ids_description, absl::string_view universe_description)
absl::Status ValidateDualRay(const DualRayProto &dual_ray, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status CheckScalarNoNanNoInf(const double d)
absl::Status ValidateSolution(const SolutionProto &solution, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status ValidateDualSolution(const DualSolutionProto &dual_solution, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status CheckIdsIdentical(absl::Span< const int64_t > first_ids, const IdNameBiMap &second_ids, absl::string_view first_description, absl::string_view second_description)
Collection of objects used to extend the Constraint Solver library.
#define RETURN_IF_ERROR(expr)
IdNameBiMap linear_constraints