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
35namespace operations_research {
36namespace math_opt {
37namespace {
38
39absl::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
56absl::Status IsSupported(const math_opt::ModelProto& model) {
57 return ValidateModel(model);
58}
59
60bool 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
69bool 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
80absl::StatusOr<::operations_research::math_opt::ModelProto>
81MPModelProtoToMathOptModel(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
176absl::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);
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
::PROTOBUF_NAMESPACE_ID::int32 var_index(int index) const
void set_name(ArgT0 &&arg0, ArgT... args)
void add_var_index(::PROTOBUF_NAMESPACE_ID::int32 value)
::operations_research::MPConstraintProto * mutable_constraint(int index)
::operations_research::MPVariableProto * add_variable()
::operations_research::MPVariableProto * mutable_variable(int index)
void set_name(ArgT0 &&arg0, ArgT... args)
::operations_research::MPConstraintProto * add_constraint()
void set_name(ArgT0 &&arg0, ArgT... args)
int64_t value
IntVar * var
Definition: expr_array.cc:1874
int64_t coef
Definition: expr_array.cc:1875
GRBmodel * model
int NumVariables(const VariablesProto &variables)
absl::StatusOr<::operations_research::MPModelProto > MathOptModelToMPModelProto(const ::operations_research::math_opt::ModelProto &model)
SparseVectorView< T > MakeView(absl::Span< const int64_t > ids, const Collection &values)
int NumConstraints(const LinearConstraintsProto &linear_constraints)
absl::StatusOr<::operations_research::math_opt::ModelProto > MPModelProtoToMathOptModel(const ::operations_research::MPModelProto &model)
absl::Status ValidateModel(const ModelProto &model, const bool check_names)
Collection of objects used to extend the Constraint Solver library.
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.
int64_t coefficient
#define RETURN_IF_ERROR(expr)
Definition: status_macros.h:29