OR-Tools  9.1
proto_converter.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 <algorithm>
17 #include <cstdint>
18 #include <string>
19 #include <utility>
20 #include <vector>
21 
23 #include "absl/container/flat_hash_map.h"
24 #include "absl/status/status.h"
25 #include "absl/status/statusor.h"
30 #include "ortools/math_opt/model.pb.h"
31 #include "ortools/math_opt/sparse_containers.pb.h"
34 
35 namespace operations_research {
36 namespace math_opt {
37 namespace {
38 
39 absl::Status IsSupported(const MPModelProto& model) {
40  std::string validity_string = FindErrorInMPModelProto(model);
41  if (validity_string.length() > 0) {
42  return absl::InvalidArgumentError(validity_string);
43  }
44  if (model.general_constraint_size() > 0) {
45  return absl::InvalidArgumentError("General constraints are not supported");
46  }
47  if (model.quadratic_objective().coefficient_size() > 0) {
48  return absl::InvalidArgumentError("Quadratic objectives not supported");
49  }
50  if (model.solution_hint().var_index_size() > 0) {
51  return absl::InvalidArgumentError("Solution Hint not supported");
52  }
53  return absl::OkStatus();
54 }
55 
56 absl::Status IsSupported(const math_opt::ModelProto& model) {
57  return ValidateModel(model);
58 }
59 
60 bool AnyVarNamed(const MPModelProto& model) {
61  for (const MPVariableProto& var : model.variable()) {
62  if (var.name().length() > 0) {
63  return true;
64  }
65  }
66  return false;
67 }
68 
69 bool AnyConstraintNamed(const MPModelProto& model) {
70  for (const MPConstraintProto& constraint : model.constraint()) {
71  if (constraint.name().length() > 0) {
72  return true;
73  }
74  }
75  return false;
76 }
77 
78 } // namespace
79 
80 absl::StatusOr<::operations_research::math_opt::ModelProto>
81 MPModelProtoToMathOptModel(const ::operations_research::MPModelProto& model) {
82  RETURN_IF_ERROR(IsSupported(model));
83 
84  ModelProto output;
85  output.set_name(model.name());
86 
87  math_opt::VariablesProto* const vars = output.mutable_variables();
88  int objective_non_zeros = 0;
89  const int num_vars = model.variable_size();
90  const bool vars_have_name = AnyVarNamed(model);
91  vars->mutable_lower_bounds()->Reserve(num_vars);
92  vars->mutable_upper_bounds()->Reserve(num_vars);
93  vars->mutable_integers()->Reserve(num_vars);
94  if (vars_have_name) {
95  vars->mutable_names()->Reserve(num_vars);
96  }
97  for (int i = 0; i < model.variable_size(); ++i) {
98  const MPVariableProto& var = model.variable(i);
99  if (var.objective_coefficient() != 0.0) {
100  ++objective_non_zeros;
101  }
102  vars->add_ids(i);
103  vars->add_lower_bounds(var.lower_bound());
104  vars->add_upper_bounds(var.upper_bound());
105  vars->add_integers(var.is_integer());
106  if (vars_have_name) {
107  vars->add_names(var.name());
108  }
109  }
110 
111  math_opt::ObjectiveProto* const objective = output.mutable_objective();
112  if (objective_non_zeros > 0) {
113  objective->mutable_linear_coefficients()->mutable_ids()->Reserve(
114  objective_non_zeros);
115  objective->mutable_linear_coefficients()->mutable_values()->Reserve(
116  objective_non_zeros);
117  for (int j = 0; j < num_vars; ++j) {
118  const double value = model.variable(j).objective_coefficient();
119  if (value == 0.0) continue;
120  objective->mutable_linear_coefficients()->add_ids(j);
121  objective->mutable_linear_coefficients()->add_values(value);
122  }
123  }
124  objective->set_maximize(model.maximize());
125  objective->set_offset(model.objective_offset());
126 
127  math_opt::LinearConstraintsProto* const constraints =
128  output.mutable_linear_constraints();
129  const int num_constraints = model.constraint_size();
130  const bool constraints_have_name = AnyConstraintNamed(model);
131  int num_non_zeros = 0;
132  constraints->mutable_lower_bounds()->Reserve(num_constraints);
133  constraints->mutable_upper_bounds()->Reserve(num_constraints);
134  if (constraints_have_name) {
135  constraints->mutable_names()->Reserve(num_constraints);
136  }
137  for (int i = 0; i < num_constraints; ++i) {
138  const MPConstraintProto& constraint = model.constraint(i);
139  constraints->add_ids(i);
140  constraints->add_lower_bounds(constraint.lower_bound());
141  constraints->add_upper_bounds(constraint.upper_bound());
142  if (constraints_have_name) {
143  constraints->add_names(constraint.name());
144  }
145  num_non_zeros += constraint.var_index_size();
146  }
147 
148  SparseDoubleMatrixProto* const matrix =
149  output.mutable_linear_constraint_matrix();
150  matrix->mutable_column_ids()->Reserve(num_non_zeros);
151  matrix->mutable_row_ids()->Reserve(num_non_zeros);
152  matrix->mutable_coefficients()->Reserve(num_non_zeros);
153  // This allocation is reused across loop iterations, use caution!
154  std::vector<std::pair<int, double>> terms_in_order;
155  for (int i = 0; i < num_constraints; ++i) {
156  const MPConstraintProto& constraint = model.constraint(i);
157  const int constraint_non_zeros = constraint.var_index_size();
158  for (int k = 0; k < constraint_non_zeros; ++k) {
159  const double coefficient = constraint.coefficient(k);
160  if (coefficient == 0.0) {
161  continue;
162  }
163  terms_in_order.emplace_back(constraint.var_index(k), coefficient);
164  }
165  std::sort(terms_in_order.begin(), terms_in_order.end());
166  for (const auto& term : terms_in_order) {
167  matrix->add_column_ids(i);
168  matrix->add_row_ids(term.first);
169  matrix->add_coefficients(term.second);
170  }
171  terms_in_order.clear();
172  }
173  return output;
174 }
175 
176 absl::StatusOr<::operations_research::MPModelProto> MathOptModelToMPModelProto(
177  const ::operations_research::math_opt::ModelProto& model) {
178  RETURN_IF_ERROR(IsSupported(model));
179 
180  const bool vars_have_name = model.variables().names_size() > 0;
181  const bool constraints_have_name =
182  model.linear_constraints().names_size() > 0;
183  absl::flat_hash_map<int64_t, int> variable_id_to_mp_position;
184  absl::flat_hash_map<int64_t, MPConstraintProto*>
185  constraint_id_to_mp_constraint;
186 
187  MPModelProto output;
188  output.set_name(model.name());
189 
190  const int num_vars = NumVariables(model.variables());
191  output.mutable_variable()->Reserve(num_vars);
192  for (int j = 0; j < num_vars; ++j) {
193  MPVariableProto* const variable = output.add_variable();
194  variable_id_to_mp_position.emplace(model.variables().ids(j), j);
195  variable->set_lower_bound(model.variables().lower_bounds(j));
196  variable->set_upper_bound(model.variables().upper_bounds(j));
197  variable->set_is_integer(model.variables().integers(j));
198  if (vars_have_name) {
199  variable->set_name(model.variables().names(j));
200  }
201  }
202 
203  const int num_constraints = NumConstraints(model.linear_constraints());
204  output.mutable_constraint()->Reserve(num_constraints);
205  for (int i = 0; i < num_constraints; ++i) {
206  MPConstraintProto* const constraint = output.add_constraint();
207  constraint_id_to_mp_constraint.emplace(model.linear_constraints().ids(i),
208  constraint);
209  constraint->set_lower_bound(model.linear_constraints().lower_bounds(i));
210  constraint->set_upper_bound(model.linear_constraints().upper_bounds(i));
211  if (constraints_have_name) {
212  constraint->set_name(model.linear_constraints().names(i));
213  }
214  }
215 
216  output.set_maximize(model.objective().maximize());
217  output.set_objective_offset(model.objective().offset());
218  for (const auto& [var, coef] :
219  MakeView(model.objective().linear_coefficients())) {
220  const int var_position = variable_id_to_mp_position[var];
221  MPVariableProto* const variable = output.mutable_variable(var_position);
222  variable->set_objective_coefficient(coef);
223  }
224 
225  // TODO(user): use the constraint iterator from scip_solver.cc here.
226  const int constraint_non_zeros =
227  model.linear_constraint_matrix().coefficients_size();
228  for (int k = 0; k < constraint_non_zeros; ++k) {
229  const int64_t constraint_id = model.linear_constraint_matrix().row_ids(k);
230  MPConstraintProto* const constraint =
231  constraint_id_to_mp_constraint[constraint_id];
232  const int64_t variable_id = model.linear_constraint_matrix().column_ids(k);
233  const int variable_position = variable_id_to_mp_position[variable_id];
234  constraint->add_var_index(variable_position);
235  const double value = model.linear_constraint_matrix().coefficients(k);
236  constraint->add_coefficient(value);
237  }
238 
239  return output;
240 }
241 
242 } // namespace math_opt
243 } // namespace operations_research
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
absl::StatusOr<::operations_research::math_opt::ModelProto > MPModelProtoToMathOptModel(const ::operations_research::MPModelProto &model)
::PROTOBUF_NAMESPACE_ID::int32 var_index(int index) const
void set_name(ArgT0 &&arg0, ArgT... args)
GRBmodel * model
int64_t coefficient
int64_t coef
Definition: expr_array.cc:1875
int NumVariables(const VariablesProto &variables)
std::string FindErrorInMPModelProto(const MPModelProto &model, double abs_value_threshold, const bool accept_trivially_infeasible_bounds)
Returns an empty string iff the model is valid and not trivially infeasible.
::operations_research::MPConstraintProto * mutable_constraint(int index)
::operations_research::MPConstraintProto * add_constraint()
int NumConstraints(const LinearConstraintsProto &linear_constraints)
::operations_research::MPVariableProto * add_variable()
::operations_research::MPVariableProto * mutable_variable(int index)
void set_name(ArgT0 &&arg0, ArgT... args)
absl::Status ValidateModel(const ModelProto &model, const bool check_names)
Collection of objects used to extend the Constraint Solver library.
#define RETURN_IF_ERROR(expr)
Definition: status_macros.h:29
IntVar * var
Definition: expr_array.cc:1874
void set_name(ArgT0 &&arg0, ArgT... args)
int64_t value
void add_var_index(::PROTOBUF_NAMESPACE_ID::int32 value)
absl::StatusOr<::operations_research::MPModelProto > MathOptModelToMPModelProto(const ::operations_research::math_opt::ModelProto &model)