OR-Tools  9.6
math_opt_proto_utils.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <algorithm>
17 #include <cstdint>
18 #include <functional>
19 #include <string>
20 
21 #include "absl/container/flat_hash_set.h"
22 #include "absl/status/status.h"
23 #include "absl/strings/string_view.h"
24 #include "absl/log/check.h"
25 #include "ortools/base/logging.h"
27 #include "ortools/math_opt/callback.pb.h"
29 #include "ortools/math_opt/model.pb.h"
30 #include "ortools/math_opt/model_update.pb.h"
31 #include "ortools/math_opt/result.pb.h"
32 #include "ortools/math_opt/sparse_containers.pb.h"
33 
34 namespace operations_research {
35 namespace math_opt {
36 
37 void RemoveSparseDoubleVectorZeros(SparseDoubleVectorProto& sparse_vector) {
38  CHECK_EQ(sparse_vector.ids_size(), sparse_vector.values_size());
39  // Keep track of the next index that has not yet been used for a non zero
40  // value.
41  int next = 0;
42  for (const auto [id, value] : MakeView(sparse_vector)) {
43  // Se use `!(== 0.0)` here so that we keep NaN values for which both `v ==
44  // 0` and `v != 0` returns false.
45  if (!(value == 0.0)) {
46  sparse_vector.set_ids(next, id);
47  sparse_vector.set_values(next, value);
48  ++next;
49  }
50  }
51  // At the end of the iteration, `next` contains the index of the first unused
52  // index. This means it contains the number of used elements.
53  sparse_vector.mutable_ids()->Truncate(next);
54  sparse_vector.mutable_values()->Truncate(next);
55 }
56 
58  const SparseVectorFilterProto& filter)
59  : filter_(filter) {
60  // We only do this test in non-optimized builds.
61  if (DEBUG_MODE && filter_.filter_by_ids()) {
62  // Checks that input filtered_ids are strictly increasing.
63  // That is: for all i, ids(i) < ids(i+1).
64  // Hence here we fail if there exists i such that ids(i) >= ids(i+1).
65  const auto& ids = filter_.filtered_ids();
66  CHECK(std::adjacent_find(ids.begin(), ids.end(),
67  std::greater_equal<int64_t>()) == ids.end())
68  << "The input filter.filtered_ids must be strictly increasing.";
69  }
70 }
71 
72 absl::flat_hash_set<CallbackEventProto> EventSet(
73  const CallbackRegistrationProto& callback_registration) {
74  // Here we don't use for-range loop since for repeated enum fields, the type
75  // used in C++ is RepeatedField<int>. Using the generated getter instead
76  // guarantees type safety.
77  absl::flat_hash_set<CallbackEventProto> events;
78  for (int i = 0; i < callback_registration.request_registration_size(); ++i) {
79  events.emplace(callback_registration.request_registration(i));
80  }
81  return events;
82 }
83 
84 TerminationProto TerminateForLimit(const LimitProto limit, const bool feasible,
85  const absl::string_view detail) {
86  TerminationProto result;
87  if (feasible) {
88  result.set_reason(TERMINATION_REASON_FEASIBLE);
89  } else {
90  result.set_reason(TERMINATION_REASON_NO_SOLUTION_FOUND);
91  }
92  result.set_limit(limit);
93  if (!detail.empty()) {
94  result.set_detail(std::string(detail));
95  }
96  return result;
97 }
98 
99 TerminationProto FeasibleTermination(const LimitProto limit,
100  const absl::string_view detail) {
101  return TerminateForLimit(limit, /*feasible=*/true, detail);
102 }
103 
104 TerminationProto NoSolutionFoundTermination(const LimitProto limit,
105  const absl::string_view detail) {
106  return TerminateForLimit(limit, /*feasible=*/false, detail);
107 }
108 
109 TerminationProto TerminateForReason(const TerminationReasonProto reason,
110  const absl::string_view detail) {
111  TerminationProto result;
112  result.set_reason(reason);
113  if (!detail.empty()) {
114  result.set_detail(std::string(detail));
115  }
116  return result;
117 }
118 
119 absl::Status ModelIsSupported(const ModelProto& model,
120  const SupportedProblemStructures& support_menu,
121  const absl::string_view solver_name) {
122  const auto error_status = [solver_name](
123  const absl::string_view structure,
124  const SupportType support) -> absl::Status {
125  switch (support) {
128  << solver_name << " does not support " << structure;
131  << "MathOpt does not currently support " << solver_name
132  << " models with " << structure;
134  LOG(FATAL) << "Unexpected call with `kSupported`";
135  }
136  };
137  if (const SupportType support = support_menu.integer_variables;
138  support != SupportType::kSupported) {
139  for (const bool is_integer : model.variables().integers()) {
140  if (is_integer) {
141  return error_status("integer variables", support);
142  }
143  }
144  }
145  if (const SupportType support = support_menu.multi_objectives;
146  support != SupportType::kSupported) {
147  if (!model.auxiliary_objectives().empty()) {
148  return error_status("multi objectives", support);
149  }
150  }
151  if (const SupportType support = support_menu.quadratic_objectives;
152  support != SupportType::kSupported) {
153  if (!model.objective().quadratic_coefficients().row_ids().empty()) {
154  return error_status("quadratic objectives", support);
155  }
156  for (const auto& [_, objective] : model.auxiliary_objectives()) {
157  if (!objective.quadratic_coefficients().row_ids().empty()) {
158  return error_status("quadratic objectives", support);
159  }
160  }
161  }
162  if (const SupportType support = support_menu.quadratic_constraints;
163  support != SupportType::kSupported) {
164  if (!model.quadratic_constraints().empty()) {
165  return error_status("quadratic constraints", support);
166  }
167  }
168  if (const SupportType support = support_menu.sos1_constraints;
169  support != SupportType::kSupported) {
170  if (!model.sos1_constraints().empty()) {
171  return error_status("sos1 constraints", support);
172  }
173  }
174  if (const SupportType support = support_menu.sos2_constraints;
175  support != SupportType::kSupported) {
176  if (!model.sos2_constraints().empty()) {
177  return error_status("sos2 constraints", support);
178  }
179  }
180  if (const SupportType support = support_menu.indicator_constraints;
181  support != SupportType::kSupported) {
182  if (!model.indicator_constraints().empty()) {
183  return error_status("indicator constraints", support);
184  }
185  }
186  return absl::OkStatus();
187 }
188 
189 bool UpdateIsSupported(const ModelUpdateProto& update,
190  const SupportedProblemStructures& support_menu) {
191  if (support_menu.integer_variables != SupportType::kSupported) {
192  for (const bool is_integer :
193  update.variable_updates().integers().values()) {
194  if (is_integer) {
195  return false;
196  }
197  }
198  for (const bool is_integer : update.new_variables().integers()) {
199  if (is_integer) {
200  return false;
201  }
202  }
203  }
204  if (support_menu.multi_objectives != SupportType::kSupported) {
205  if (!update.auxiliary_objectives_updates()
206  .deleted_objective_ids()
207  .empty() ||
208  !update.auxiliary_objectives_updates().new_objectives().empty() ||
209  !update.auxiliary_objectives_updates().objective_updates().empty()) {
210  return false;
211  }
212  }
213  if (support_menu.quadratic_objectives != SupportType::kSupported) {
214  if (!update.objective_updates()
215  .quadratic_coefficients()
216  .row_ids()
217  .empty()) {
218  return false;
219  }
220  for (const auto& [_, new_objective] :
221  update.auxiliary_objectives_updates().new_objectives()) {
222  if (!new_objective.quadratic_coefficients().row_ids().empty()) {
223  return false;
224  }
225  }
226  for (const auto& [_, objective_update] :
227  update.auxiliary_objectives_updates().objective_updates()) {
228  if (!objective_update.quadratic_coefficients().row_ids().empty()) {
229  return false;
230  }
231  }
232  }
233  // Duck-types that the proto parameter contains fields named `new_constraints`
234  // and `deleted_constraint_ids`. This is standard for "mapped" constraints.
235  const auto contains_new_or_deleted_constraints =
236  [](const auto& constraint_update) {
237  return !constraint_update.new_constraints().empty() ||
238  !constraint_update.deleted_constraint_ids().empty();
239  };
240  if (support_menu.quadratic_constraints != SupportType::kSupported) {
241  if (contains_new_or_deleted_constraints(
242  update.quadratic_constraint_updates())) {
243  return false;
244  }
245  }
246  if (support_menu.sos1_constraints != SupportType::kSupported) {
247  if (contains_new_or_deleted_constraints(update.sos1_constraint_updates())) {
248  return false;
249  }
250  }
251  if (support_menu.sos2_constraints != SupportType::kSupported) {
252  if (contains_new_or_deleted_constraints(update.sos2_constraint_updates())) {
253  return false;
254  }
255  }
256  if (support_menu.indicator_constraints != SupportType::kSupported) {
257  if (contains_new_or_deleted_constraints(
258  update.indicator_constraint_updates())) {
259  return false;
260  }
261  }
262  return true;
263 }
264 
265 } // namespace math_opt
266 } // namespace operations_research
SparseVectorFilterPredicate(const SparseVectorFilterProto &filter)
Block * next
int64_t value
GRBmodel * model
Filter filter_
const bool DEBUG_MODE
Definition: macros.h:24
TerminationProto FeasibleTermination(const LimitProto limit, const absl::string_view detail)
absl::Status ModelIsSupported(const ModelProto &model, const SupportedProblemStructures &support_menu, const absl::string_view solver_name)
bool UpdateIsSupported(const ModelUpdateProto &update, const SupportedProblemStructures &support_menu)
TerminationProto TerminateForLimit(const LimitProto limit, const bool feasible, const absl::string_view detail)
TerminationProto NoSolutionFoundTermination(const LimitProto limit, const absl::string_view detail)
TerminationProto TerminateForReason(const TerminationReasonProto reason, const absl::string_view detail)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::flat_hash_set< CallbackEventProto > EventSet(const CallbackRegistrationProto &callback_registration)
void RemoveSparseDoubleVectorZeros(SparseDoubleVectorProto &sparse_vector)
Collection of objects used to extend the Constraint Solver library.
StatusBuilder UnimplementedErrorBuilder()
StatusBuilder InvalidArgumentErrorBuilder()