OR-Tools  9.0
solution_validator.cc
Go to the documentation of this file.
1 // Copyright 2010-2021 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 <cstdint>
17 #include <limits>
18 #include <string>
19 
21 #include "absl/status/status.h"
22 #include "absl/strings/str_cat.h"
24 #include "ortools/math_opt/model_parameters.pb.h"
26 #include "ortools/math_opt/result.pb.h"
27 #include "ortools/math_opt/solution.pb.h"
28 #include "ortools/math_opt/sparse_containers.pb.h"
34 
35 namespace operations_research {
36 namespace math_opt {
37 namespace {
38 
39 constexpr double kInf = std::numeric_limits<double>::infinity();
40 
41 } // namespace
42 
43 absl::Status ValidateResult(const SolveResultProto& result,
44  const ModelSolveParametersProto& parameters,
45  const ModelSummary& model_summary) {
46  for (int i = 0; i < result.primal_solutions_size(); ++i) {
47  RETURN_IF_ERROR(ValidatePrimalSolution(result.primal_solutions(i),
48  parameters.primal_variables_filter(),
49  model_summary))
50  << "Invalid primal_solutions[" << i << "]";
51  }
52  for (int i = 0; i < result.primal_rays_size(); ++i) {
53  RETURN_IF_ERROR(ValidatePrimalRay(result.primal_rays(i),
54  parameters.primal_variables_filter(),
55  model_summary))
56  << "Invalid primal_rays[" << i << "]";
57  }
58  for (int i = 0; i < result.dual_solutions_size(); ++i) {
59  RETURN_IF_ERROR(ValidateDualSolution(result.dual_solutions(i), parameters,
60  model_summary))
61  << "Invalid dual_solutions[" << i << "]";
62  }
63  for (int i = 0; i < result.dual_rays_size(); ++i) {
65  ValidateDualRay(result.dual_rays(i), parameters, model_summary))
66  << "Invalid dual_rays[" << i << "]";
67  }
68  for (int i = 0; i < result.basis_size(); ++i) {
69  RETURN_IF_ERROR(ValidateBasis(result.basis(i), model_summary));
70  }
71 
72  // TODO(b/174345677): validates all other contracts of the result (we have one
73  // solution when the termination_reason says so, ...)
74 
75  return absl::OkStatus();
76 }
77 
79 // Solutions & Rays
81 
82 namespace {
83 
84 // Validates that all pairs in the input view match the provided filter and that
85 // all expected values are there when skip_zero_values is not used.
86 //
87 // This function assumes caller have checked already that the input
88 // vector_view.ids() and the input filter are valid.
89 template <typename T>
90 absl::Status IsFiltered(const SparseVectorView<T>& vector_view,
91  const SparseVectorFilterProto& filter,
92  const IdNameBiMap& all_items) {
94  SparseVectorFilterPredicate predicate(filter);
95  RETURN_IF_ERROR(CheckIdsSubset(vector_view.ids(), all_items, "sparse vector",
96  "model IDs"));
97  for (int i = 0; i < vector_view.ids_size(); ++i) {
98  const int64_t id = vector_view.ids(i);
99  if (!predicate.AcceptsAndUpdate(id, vector_view.values(i))) {
100  return absl::InvalidArgumentError(
101  absl::StrCat("sparse vector should not contain the pair (id: ", id,
102  ", value: ", vector_view.values(i), ") (at index: ", i,
103  ") that should have been filtered"));
104  }
105  }
106 
107  // We don't test the length on the input if we skipped the zeros since missing
108  // values are expected.
109  if (filter.skip_zero_values()) {
110  return absl::OkStatus();
111  }
112 
113  const int expected_size =
114  filter.filter_by_ids() ? filter.filtered_ids_size() : all_items.Size();
115  if (vector_view.ids_size() != expected_size) {
116  return absl::InvalidArgumentError(absl::StrCat(
117  "sparse vector should contain ", expected_size, " values but contains ",
118  vector_view.ids_size(), " instead"));
119  }
120 
121  return absl::OkStatus();
122 }
123 
124 // A solution vector is valid if:
125 // * it is a valid SparseDoubleVectorProto.
126 // * its values are finite.
127 // * it contains only elements that pass the filter
128 // * it contains all elements that pass the filter when skip_zero_values is not
129 // used.
130 //
131 // TODO(b/174345677): check that the ids are valid.
132 absl::Status IsValidSolutionVector(const SparseDoubleVectorProto& vector,
133  const SparseVectorFilterProto& filter,
134  const IdNameBiMap& all_items) {
135  const auto vector_view = MakeView(vector);
137  vector_view,
138  {.allow_positive_infinity = false, .allow_negative_infinity = false}));
139  RETURN_IF_ERROR(IsFiltered(vector_view, filter, all_items));
140  return absl::OkStatus();
141 }
142 
143 } // namespace
144 
145 absl::Status ValidatePrimalSolution(const PrimalSolutionProto& primal_solution,
146  const SparseVectorFilterProto& filter,
147  const ModelSummary& model_summary) {
148  RETURN_IF_ERROR(IsValidSolutionVector(primal_solution.variable_values(),
149  filter, model_summary.variables))
150  << "Invalid PrimalSolutionProto.variable_values";
151  RETURN_IF_ERROR(CheckScalarNoNanNoInf(primal_solution.objective_value()))
152  << "Invalid PrimalSolutionProto.objective_value";
153  return absl::OkStatus();
154 }
155 
156 absl::Status ValidatePrimalRay(const PrimalRayProto& primal_ray,
157  const SparseVectorFilterProto& filter,
158  const ModelSummary& model_summary) {
159  RETURN_IF_ERROR(IsValidSolutionVector(primal_ray.variable_values(), filter,
160  model_summary.variables))
161  << "Invalid PrimalRayProto.variable_values";
162  return absl::OkStatus();
163 }
164 
165 absl::Status ValidateDualSolution(const DualSolutionProto& dual_solution,
166  const ModelSolveParametersProto& parameters,
167  const ModelSummary& model_summary) {
168  RETURN_IF_ERROR(IsValidSolutionVector(
169  dual_solution.dual_values(), parameters.dual_linear_constraints_filter(),
170  model_summary.linear_constraints))
171  << "Invalid DualSolutionProto.dual_values";
172  RETURN_IF_ERROR(IsValidSolutionVector(dual_solution.reduced_costs(),
173  parameters.dual_variables_filter(),
174  model_summary.variables))
175  << "Invalid DualSolutionProto.reduced_costs";
176  RETURN_IF_ERROR(CheckScalarNoNanNoInf(dual_solution.objective_value()))
177  << "Invalid DualSolutionProto.objective_value";
178  return absl::OkStatus();
179 }
180 
181 absl::Status ValidateDualRay(const DualRayProto& dual_ray,
182  const ModelSolveParametersProto& parameters,
183  const ModelSummary& model_summary) {
184  RETURN_IF_ERROR(IsValidSolutionVector(
185  dual_ray.dual_values(), parameters.dual_linear_constraints_filter(),
186  model_summary.linear_constraints))
187  << "Invalid DualRayProto.dual_values";
188  RETURN_IF_ERROR(IsValidSolutionVector(dual_ray.reduced_costs(),
189  parameters.dual_variables_filter(),
190  model_summary.variables))
191  << "Invalid DualRayProto.reduced_costs";
192  return absl::OkStatus();
193 }
194 
196 // Basis
198 
200  const SparseVectorView<int>& status_vector_view) {
201  RETURN_IF_ERROR(CheckIdsAndValues(status_vector_view));
202  for (auto [id, value] : status_vector_view) {
203  if (!BasisStatus_IsValid(value)) {
204  return absl::InvalidArgumentError(
205  absl::StrCat("Invalid status: ", value, " for id ", id));
206  }
207  if (value == BasisStatus::INVALID) {
208  return absl::InvalidArgumentError(
209  absl::StrCat("Found BasisStatus::INVALID for id ", id));
210  }
211  }
212  return absl::OkStatus();
213 }
214 
215 absl::Status ValidateBasis(const BasisProto& basis,
216  const ModelSummary& model_summary) {
217  const auto constraint_status_view = MakeView(basis.constraint_status());
218  const auto variable_status_view = MakeView(basis.variable_status());
219  RETURN_IF_ERROR(SparseBasisStatusVectorIsValid(constraint_status_view))
220  << absl::StrCat("BasisProto.constraint_status invalid");
221  RETURN_IF_ERROR(SparseBasisStatusVectorIsValid(variable_status_view))
222  << absl::StrCat("BasisProto.variable_status invalid");
223 
225  basis.constraint_status().ids(), model_summary.linear_constraints,
226  "BasisProto.constraint_status.ids", "model_summary.linear_constraints"));
228  basis.variable_status().ids(), model_summary.variables,
229  "BasisProto.variable_status.ids", "model_summary.variables"));
230 
231  int non_basic_variables = 0;
232  for (const auto [id, value] : constraint_status_view) {
233  if (value != BasisStatus::BASIC) {
234  non_basic_variables++;
235  }
236  }
237  for (auto [id, value] : variable_status_view) {
238  if (value != BasisStatus::BASIC) {
239  non_basic_variables++;
240  }
241  }
242  if (non_basic_variables != model_summary.variables.Size()) {
243  return absl::InvalidArgumentError(absl::StrCat(
244  "Inconsistent number of non-basic variable+constraints: ",
245  non_basic_variables, ", variables: ", model_summary.variables.Size()));
246  }
247  return absl::OkStatus();
248 }
249 
250 } // namespace math_opt
251 } // namespace operations_research
SatParameters parameters
int64_t value
absl::Status ValidateResult(const SolveResultProto &result, const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::Status CheckIdsAndValuesSize(const SparseVectorView< T > &vector_view, absl::string_view value_name="values")
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 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 ValidateBasis(const BasisProto &basis, const ModelSummary &model_summary)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::Status ValidateDualRay(const DualRayProto &dual_ray, const ModelSolveParametersProto &parameters, const ModelSummary &model_summary)
absl::Status CheckScalarNoNanNoInf(const double d)
absl::Status ValidateDualSolution(const DualSolutionProto &dual_solution, const ModelSolveParametersProto &parameters, 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)
Definition: status_macros.h:29