Google OR-Tools v9.11
a fast and portable software suite for combinatorial optimization
Loading...
Searching...
No Matches
mps_reader.cc
Go to the documentation of this file.
1// Copyright 2010-2024 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 <limits>
17#include <string>
18#include <vector>
19
20#include "absl/container/btree_set.h"
21#include "absl/container/flat_hash_map.h"
22#include "absl/log/check.h"
23#include "absl/status/status.h"
24#include "absl/status/statusor.h"
25#include "absl/strings/str_cat.h"
26#include "absl/strings/string_view.h"
30#include "ortools/linear_solver/linear_solver.pb.h"
34
35namespace operations_research {
36namespace glop {
37
38// Data templates.
39
40template <class Data>
41class DataWrapper {};
42
43template <>
45 public:
46 using IndexType = int;
47 explicit DataWrapper(LinearProgram* data) { data_ = data; }
48
49 void SetUp() {
50 data_->SetDcheckBounds(false);
51 data_->Clear();
52 }
53
54 void SetName(absl::string_view name) { data_->SetName(name); }
55
56 void SetObjectiveDirection(bool maximize) {
57 data_->SetMaximizationProblem(maximize);
58 }
59
60 void SetObjectiveOffset(double objective_offset) {
61 data_->SetObjectiveOffset(objective_offset);
62 }
63
64 int FindOrCreateConstraint(absl::string_view name) {
65 return data_->FindOrCreateConstraint(name).value();
66 }
68 data_->SetConstraintBounds(RowIndex(index), lower_bound, upper_bound);
69 }
70 void SetConstraintCoefficient(int row_index, int col_index,
71 double coefficient) {
72 data_->SetCoefficient(RowIndex(row_index), ColIndex(col_index),
74 }
75 void SetIsLazy(int row_index) {
76 LOG_FIRST_N(WARNING, 1)
77 << "LAZYCONS section detected. It will be handled as an extension of "
78 "the ROWS section.";
79 }
80 double ConstraintLowerBound(int row_index) {
81 return data_->constraint_lower_bounds()[RowIndex(row_index)];
82 }
83 double ConstraintUpperBound(int row_index) {
84 return data_->constraint_upper_bounds()[RowIndex(row_index)];
85 }
86
87 int FindOrCreateVariable(absl::string_view name) {
88 return data_->FindOrCreateVariable(name).value();
89 }
91 data_->SetVariableType(ColIndex(index),
93 }
95 LOG(FATAL) << "Semi continuous variables are not supported";
96 }
97 void SetVariableBounds(int index, double lower_bound, double upper_bound) {
98 data_->SetVariableBounds(ColIndex(index), lower_bound, upper_bound);
99 }
101 data_->SetObjectiveCoefficient(ColIndex(index), coefficient);
102 }
104 return data_->variable_lower_bounds()[ColIndex(index)];
105 }
107 return data_->variable_upper_bounds()[ColIndex(index)];
108 }
109
110 absl::Status CreateIndicatorConstraint(absl::string_view row_name,
111 int col_index, bool col_value) {
112 return absl::UnimplementedError(
113 "LinearProgram does not support indicator constraints.");
114 }
115
116 void CleanUp() { data_->CleanUp(); }
117
118 private:
119 LinearProgram* data_;
120};
121
122template <>
123class DataWrapper<MPModelProto> {
124 public:
125 using IndexType = int;
126 explicit DataWrapper(MPModelProto* data) { data_ = data; }
127
128 void SetUp() {
129 data_->Clear();
130 variable_indices_by_name_.clear();
131 constraint_indices_by_name_.clear();
132 constraints_to_delete_.clear();
133 semi_continuous_variables_.clear();
134 }
135
136 void SetName(absl::string_view name) { data_->set_name(name); }
137
138 void SetObjectiveDirection(bool maximize) { data_->set_maximize(maximize); }
139
140 void SetObjectiveOffset(double objective_offset) {
141 data_->set_objective_offset(objective_offset);
142 }
143
144 int FindOrCreateConstraint(absl::string_view name) {
145 const auto it = constraint_indices_by_name_.find(name);
146 if (it != constraint_indices_by_name_.end()) return it->second;
147
148 const int index = data_->constraint_size();
149 MPConstraintProto* const constraint = data_->add_constraint();
150 constraint->set_lower_bound(0.0);
151 constraint->set_upper_bound(0.0);
152 constraint->set_name(name);
153 constraint_indices_by_name_[name] = index;
154 return index;
155 }
157 data_->mutable_constraint(index)->set_lower_bound(lower_bound);
158 data_->mutable_constraint(index)->set_upper_bound(upper_bound);
159 }
160 void SetConstraintCoefficient(int row_index, int col_index,
161 double coefficient) {
162 // Note that we assume that there is no duplicate in the mps file format. If
163 // there is, we will just add more than one entry from the same variable in
164 // a constraint, and we let any program that ingests an MPModelProto handle
165 // it.
166 MPConstraintProto* const constraint = data_->mutable_constraint(row_index);
167 constraint->add_var_index(col_index);
168 constraint->add_coefficient(coefficient);
169 }
170 void SetIsLazy(int row_index) {
171 data_->mutable_constraint(row_index)->set_is_lazy(true);
172 }
173 double ConstraintLowerBound(int row_index) {
174 return data_->constraint(row_index).lower_bound();
175 }
176 double ConstraintUpperBound(int row_index) {
177 return data_->constraint(row_index).upper_bound();
178 }
179
180 int FindOrCreateVariable(absl::string_view name) {
181 const auto it = variable_indices_by_name_.find(name);
182 if (it != variable_indices_by_name_.end()) return it->second;
183
184 const int index = data_->variable_size();
185 MPVariableProto* const variable = data_->add_variable();
186 variable->set_lower_bound(0.0);
187 variable->set_name(name);
188 variable_indices_by_name_[name] = index;
189 return index;
190 }
192 data_->mutable_variable(index)->set_is_integer(true);
193 }
195 semi_continuous_variables_.push_back(index);
196 }
197 void SetVariableBounds(int index, double lower_bound, double upper_bound) {
198 data_->mutable_variable(index)->set_lower_bound(lower_bound);
199 data_->mutable_variable(index)->set_upper_bound(upper_bound);
200 }
202 data_->mutable_variable(index)->set_objective_coefficient(coefficient);
203 }
205 return data_->variable(index).lower_bound();
206 }
208 return data_->variable(index).upper_bound();
209 }
210
211 absl::Status CreateIndicatorConstraint(absl::string_view cst_name,
212 int var_index, bool var_value) {
213 const auto it = constraint_indices_by_name_.find(cst_name);
214 if (it == constraint_indices_by_name_.end()) {
215 return absl::InvalidArgumentError(
216 absl::StrCat("Constraint \"", cst_name, "\" doesn't exist."));
217 }
218 const int cst_index = it->second;
219
220 MPGeneralConstraintProto* const constraint =
221 data_->add_general_constraint();
222 constraint->set_name(
223 absl::StrCat("ind_", data_->constraint(cst_index).name()));
224 MPIndicatorConstraint* const indicator =
225 constraint->mutable_indicator_constraint();
226 *indicator->mutable_constraint() = data_->constraint(cst_index);
227 indicator->set_var_index(var_index);
228 indicator->set_var_value(var_value);
229 constraints_to_delete_.insert(cst_index);
230
231 return absl::OkStatus();
232 }
233
234 void CleanUp() {
235 google::protobuf::util::RemoveAt(data_->mutable_constraint(),
236 constraints_to_delete_);
237
238 for (const int index : semi_continuous_variables_) {
239 MPVariableProto* mp_var = data_->mutable_variable(index);
240 // We detect that the lower bound was not set when it is left to its
241 // default value of zero.
242 const double lb =
243 mp_var->lower_bound() == 0 ? 1.0 : mp_var->lower_bound();
244 DCHECK_GT(lb, 0.0);
245 const double ub = mp_var->upper_bound();
246 mp_var->set_lower_bound(0.0);
247
248 // Create a new Boolean variable.
249 const int bool_var_index = data_->variable_size();
250 MPVariableProto* bool_var = data_->add_variable();
251 bool_var->set_lower_bound(0.0);
252 bool_var->set_upper_bound(1.0);
253 bool_var->set_is_integer(true);
254
255 // TODO(user): Experiment with the switch constant.
256 if (ub >= 1e8) { // Use indicator constraints
257 // bool_var == 0 implies var == 0.
258 MPGeneralConstraintProto* const zero_constraint =
259 data_->add_general_constraint();
260 MPIndicatorConstraint* const zero_indicator =
261 zero_constraint->mutable_indicator_constraint();
262 zero_indicator->set_var_index(bool_var_index);
263 zero_indicator->set_var_value(0);
264 zero_indicator->mutable_constraint()->set_lower_bound(0.0);
265 zero_indicator->mutable_constraint()->set_upper_bound(0.0);
266 zero_indicator->mutable_constraint()->add_var_index(index);
267 zero_indicator->mutable_constraint()->add_coefficient(1.0);
268
269 // bool_var == 1 implies lb <= var <= ub
270 MPGeneralConstraintProto* const one_constraint =
271 data_->add_general_constraint();
272 MPIndicatorConstraint* const one_indicator =
273 one_constraint->mutable_indicator_constraint();
274 one_indicator->set_var_index(bool_var_index);
275 one_indicator->set_var_value(1);
276 one_indicator->mutable_constraint()->set_lower_bound(lb);
277 one_indicator->mutable_constraint()->set_upper_bound(ub);
278 one_indicator->mutable_constraint()->add_var_index(index);
279 one_indicator->mutable_constraint()->add_coefficient(1.0);
280 } else { // Pure linear encoding.
281 // var >= bool_var * lb
282 MPConstraintProto* lower = data_->add_constraint();
283 lower->set_lower_bound(0.0);
284 lower->set_upper_bound(std::numeric_limits<double>::infinity());
285 lower->add_var_index(index);
286 lower->add_coefficient(1.0);
287 lower->add_var_index(bool_var_index);
288 lower->add_coefficient(-lb);
289
290 // var <= bool_var * ub
291 MPConstraintProto* upper = data_->add_constraint();
292 upper->set_lower_bound(-std::numeric_limits<double>::infinity());
293 upper->set_upper_bound(0.0);
294 upper->add_var_index(index);
295 upper->add_coefficient(1.0);
296 upper->add_var_index(bool_var_index);
297 upper->add_coefficient(-ub);
298 }
299 }
300 }
301
302 private:
303 MPModelProto* data_;
304
305 absl::flat_hash_map<std::string, int> variable_indices_by_name_;
306 absl::flat_hash_map<std::string, int> constraint_indices_by_name_;
307 absl::btree_set<int> constraints_to_delete_;
308 std::vector<int> semi_continuous_variables_;
309};
310
311namespace {
312
313// Translates MPSReader::Form into MPSReaderFormat, with `kAutoDetect` as
314// default value (even for invalid values of `form`).
315MPSReaderFormat TemplateFormat(MPSReader::Form form) {
316 return (form == MPSReader::FIXED) ? MPSReaderFormat::kFixed
317 : (form == MPSReader::FREE) ? MPSReaderFormat::kFree
318 : MPSReaderFormat::kAutoDetect;
319}
320
321} // namespace
322
323// Parses instance from a file.
324absl::Status MPSReader::ParseFile(absl::string_view file_name,
325 LinearProgram* data, Form form) {
326 DataWrapper<LinearProgram> data_wrapper(data);
328 .ParseFile(file_name, &data_wrapper, TemplateFormat(form))
329 .status();
330}
331
332absl::Status MPSReader::ParseFile(absl::string_view file_name,
333 MPModelProto* data, Form form) {
334 DataWrapper<MPModelProto> data_wrapper(data);
336 .ParseFile(file_name, &data_wrapper, TemplateFormat(form))
337 .status();
338}
339
340// Loads instance from string. Useful with MapReduce. Automatically detects
341// the file's format (free or fixed).
342absl::Status MPSReader::ParseProblemFromString(absl::string_view source,
343 LinearProgram* data,
344 MPSReader::Form form) {
345 DataWrapper<LinearProgram> data_wrapper(data);
347 .ParseString(source, &data_wrapper, TemplateFormat(form))
348 .status();
349}
350
351absl::Status MPSReader::ParseProblemFromString(absl::string_view source,
352 MPModelProto* data,
353 MPSReader::Form form) {
354 DataWrapper<MPModelProto> data_wrapper(data);
356 .ParseString(source, &data_wrapper, TemplateFormat(form))
357 .status();
358}
359
360absl::StatusOr<MPModelProto> MpsDataToMPModelProto(absl::string_view mps_data) {
361 MPModelProto model;
362 DataWrapper<MPModelProto> data_wrapper(&model);
365 .ParseString(mps_data, &data_wrapper, MPSReaderFormat::kAutoDetect)
366 .status()));
367 return model;
368}
369
370absl::StatusOr<MPModelProto> MpsFileToMPModelProto(absl::string_view mps_file) {
371 MPModelProto model;
372 DataWrapper<MPModelProto> data_wrapper(&model);
375 .ParseFile(mps_file, &data_wrapper, MPSReaderFormat::kAutoDetect)
376 .status()));
377 return model;
378}
379
380} // namespace glop
381} // namespace operations_research
#define RETURN_IF_ERROR(expr)
void SetObjectiveCoefficient(int index, double coefficient)
absl::Status CreateIndicatorConstraint(absl::string_view row_name, int col_index, bool col_value)
void SetConstraintBounds(int index, double lower_bound, double upper_bound)
Definition mps_reader.cc:67
void SetConstraintCoefficient(int row_index, int col_index, double coefficient)
Definition mps_reader.cc:70
void SetVariableBounds(int index, double lower_bound, double upper_bound)
Definition mps_reader.cc:97
void SetConstraintBounds(int index, double lower_bound, double upper_bound)
void SetObjectiveCoefficient(int index, double coefficient)
absl::Status CreateIndicatorConstraint(absl::string_view cst_name, int var_index, bool var_value)
void SetConstraintCoefficient(int row_index, int col_index, double coefficient)
void SetVariableBounds(int index, double lower_bound, double upper_bound)
@ INTEGER
The variable must only take integer values.
const std::string name
A name for logging purposes.
absl::Status status
Definition g_gurobi.cc:44
double lower
double upper
double upper_bound
double lower_bound
GRBmodel * model
int index
int RemoveAt(RepeatedType *array, const IndexContainer &indices)
void ParseFile(const std::string &filename, bool presolve)
absl::StatusOr< MPModelProto > MpsFileToMPModelProto(absl::string_view mps_file)
Parses an MPS model from a file.
absl::StatusOr< MPModelProto > MpsDataToMPModelProto(absl::string_view mps_data)
Parses an MPS model from a string.
In SWIG mode, we don't want anything besides these top-level includes.
int64_t coefficient
int var_index
Definition search.cc:3268