20#include "absl/status/status.h"
21#include "absl/strings/str_cat.h"
27#include "ortools/math_opt/model_parameters.pb.h"
28#include "ortools/math_opt/solution.pb.h"
29#include "ortools/math_opt/sparse_containers.pb.h"
38absl::Status ValidateSolutionStatus(
const SolutionStatusProto&
status) {
39 if (!SolutionStatusProto_IsValid(
status)) {
40 return absl::InvalidArgumentError(absl::StrCat(
"status = ",
status));
42 if (
status == SOLUTION_STATUS_UNSPECIFIED) {
43 return absl::InvalidArgumentError(
"status = SOLUTION_STATUS_UNSPECIFIED");
45 return absl::OkStatus();
62absl::Status IsFiltered(
const SparseVectorView<T>& vector_view,
63 const SparseVectorFilterProto& filter,
64 const IdNameBiMap& all_items) {
66 SparseVectorFilterPredicate predicate(filter);
69 for (
int i = 0;
i < vector_view.ids_size(); ++
i) {
70 const int64_t
id = vector_view.ids(i);
71 if (!predicate.AcceptsAndUpdate(
id, vector_view.values(i))) {
72 return absl::InvalidArgumentError(
73 absl::StrCat(
"sparse vector should not contain the pair (id: ",
id,
74 ", value: ", vector_view.values(i),
") (at index: ", i,
75 ") that should have been filtered"));
81 if (filter.skip_zero_values()) {
82 return absl::OkStatus();
85 const int expected_size =
86 filter.filter_by_ids() ? filter.filtered_ids_size() : all_items.Size();
87 if (vector_view.ids_size() != expected_size) {
88 return absl::InvalidArgumentError(absl::StrCat(
89 "sparse vector should contain ", expected_size,
" values but contains ",
90 vector_view.ids_size(),
" instead"));
93 return absl::OkStatus();
104absl::Status IsValidSolutionVector(
const SparseDoubleVectorProto& vector,
105 const SparseVectorFilterProto& filter,
106 const IdNameBiMap& all_items) {
107 const auto vector_view =
MakeView(vector);
110 {.allow_positive_infinity =
false, .allow_negative_infinity =
false}));
112 return absl::OkStatus();
122 return absl::InvalidArgumentError(
"empty solution");
124 if (
solution.has_primal_solution()) {
128 <<
"invalid primal_solution";
133 <<
"invalid dual_solution";
142 if (
solution.basis().basic_dual_feasibility() == SOLUTION_STATUS_FEASIBLE &&
143 solution.dual_solution().feasibility_status() !=
144 SOLUTION_STATUS_FEASIBLE) {
145 return absl::InvalidArgumentError(
146 "incompatible basis and dual solution: basis is dual feasible, but "
147 "dual solution is not feasible");
149 if (
solution.dual_solution().feasibility_status() ==
150 SOLUTION_STATUS_INFEASIBLE &&
151 solution.basis().basic_dual_feasibility() !=
152 SOLUTION_STATUS_INFEASIBLE) {
153 return absl::InvalidArgumentError(
154 "incompatible basis and dual solution: dual solution is infeasible, "
155 "but basis is not dual infeasible");
158 return absl::OkStatus();
162 const SparseVectorFilterProto& filter,
165 IsValidSolutionVector(vector, filter, model_summary.
variables));
166 return absl::OkStatus();
170 const SparseVectorFilterProto& filter,
172 RETURN_IF_ERROR(ValidateSolutionStatus(primal_solution.feasibility_status()))
173 <<
"invalid PrimalSolutionProto.feasibility_status";
175 primal_solution.variable_values(), filter, model_summary))
176 <<
"invalid PrimalSolutionProto.variable_values";
178 <<
"invalid PrimalSolutionProto.objective_value";
179 for (
const auto [
id, obj_value] :
180 primal_solution.auxiliary_objective_values()) {
183 <<
"unrecognized auxiliary objective ID: " <<
id
184 <<
"; invalid PrimalSolutionProto.auxiliary_objective_values";
187 <<
"invalid PrimalSolutionProto.auxiliary_objective_values";
189 return absl::OkStatus();
193 const SparseVectorFilterProto& filter,
195 RETURN_IF_ERROR(IsValidSolutionVector(primal_ray.variable_values(), filter,
197 <<
"invalid PrimalRayProto.variable_values";
198 return absl::OkStatus();
204 RETURN_IF_ERROR(ValidateSolutionStatus(dual_solution.feasibility_status()))
205 <<
"invalid DualSolutionProto.feasibility_status";
209 <<
"invalid DualSolutionProto.dual_values";
213 <<
"invalid DualSolutionProto.reduced_costs";
215 <<
"invalid DualSolutionProto.objective_value";
216 return absl::OkStatus();
225 <<
"invalid DualRayProto.dual_values";
229 <<
"invalid DualRayProto.reduced_costs";
230 return absl::OkStatus();
238 const SparseVectorView<int>& status_vector_view) {
240 for (
auto [
id,
value] : status_vector_view) {
241 if (!BasisStatusProto_IsValid(
value)) {
242 return absl::InvalidArgumentError(
243 absl::StrCat(
"invalid status: ",
value,
" for id ",
id));
245 if (
value == BASIS_STATUS_UNSPECIFIED) {
246 return absl::InvalidArgumentError(
247 absl::StrCat(
"found BASIS_STATUS_UNSPECIFIED for id ",
id));
250 return absl::OkStatus();
254 const ModelSummary& model_summary,
255 const bool check_dual_feasibility) {
256 if (check_dual_feasibility) {
258 <<
"invalid BasisProto.basic_dual_feasibility";
260 const auto constraint_status_view =
MakeView(basis.constraint_status());
261 const auto variable_status_view =
MakeView(basis.variable_status());
263 << absl::StrCat(
"BasisProto.constraint_status invalid");
265 << absl::StrCat(
"BasisProto.variable_status invalid");
268 basis.constraint_status().ids(), model_summary.linear_constraints,
269 "BasisProto.constraint_status.ids",
"model_summary.linear_constraints"));
271 basis.variable_status().ids(), model_summary.variables,
272 "BasisProto.variable_status.ids",
"model_summary.variables"));
274 int non_basic_variables = 0;
275 for (
const auto [
id,
value] : constraint_status_view) {
276 if (
value != BASIS_STATUS_BASIC) {
277 non_basic_variables++;
280 for (
auto [
id,
value] : variable_status_view) {
281 if (
value != BASIS_STATUS_BASIC) {
282 non_basic_variables++;
285 if (non_basic_variables != model_summary.variables.Size()) {
286 return absl::InvalidArgumentError(absl::StrCat(
287 "inconsistent number of non-basic variable+constraints: ",
288 non_basic_variables,
", variables: ", model_summary.variables.Size()));
290 return absl::OkStatus();
#define RETURN_IF_ERROR(expr)
bool HasId(int64_t id) const
absl::Status CheckIdsAndValuesSize(const SparseVectorView< T > &vector_view, absl::string_view value_name="values")
absl::Status ValidateDualRay(const DualRayProto &dual_ray, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status SparseBasisStatusVectorIsValid(const SparseVectorView< int > &status_vector_view)
absl::Status ValidateDualSolution(const DualSolutionProto &dual_solution, const ModelSolveParametersProto ¶meters, 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 ValidateSolution(const SolutionProto &solution, const ModelSolveParametersProto ¶meters, const ModelSummary &model_summary)
absl::Status ValidatePrimalSolutionVector(const SparseDoubleVectorProto &vector, const SparseVectorFilterProto &filter, const ModelSummary &model_summary)
absl::Status CheckIdsSubset(absl::Span< const int64_t > ids, const IdNameBiMap &universe, std::optional< int64_t > upper_bound)
absl::Status CheckIdsIdentical(absl::Span< const int64_t > first_ids, const IdNameBiMap &second_ids, absl::string_view first_description, absl::string_view second_description)
first_ids and second_ids must include distinct ids.
absl::Status CheckScalarNoNanNoInf(const double d)
absl::Status ValidateBasis(const BasisProto &basis, const ModelSummary &model_summary, const bool check_dual_feasibility)
absl::Status ValidatePrimalSolution(const PrimalSolutionProto &primal_solution, const SparseVectorFilterProto &filter, 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.
StatusBuilder InvalidArgumentErrorBuilder()
IdNameBiMap linear_constraints
IdNameBiMap auxiliary_objectives