OR-Tools  9.1
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"
26#include "ortools/math_opt/model_parameters.pb.h"
27#include "ortools/math_opt/result.pb.h"
28#include "ortools/math_opt/solution.pb.h"
29#include "ortools/math_opt/sparse_containers.pb.h"
34
35namespace operations_research {
36namespace math_opt {
37namespace {
38
39constexpr double kInf = std::numeric_limits<double>::infinity();
40
41} // namespace
42
43absl::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
82namespace {
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.
89template <typename T>
90absl::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.
132absl::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
145absl::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
156absl::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
165absl::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
181absl::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
215absl::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");
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)
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 ValidateBasis(const BasisProto &basis, const ModelSummary &model_summary)
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