25#include "Eigen/SparseCore"
26#include "absl/base/casts.h"
27#include "absl/container/flat_hash_map.h"
28#include "absl/log/check.h"
29#include "absl/status/status.h"
30#include "absl/status/statusor.h"
31#include "absl/strings/match.h"
32#include "absl/strings/str_format.h"
33#include "absl/strings/string_view.h"
41#include "ortools/linear_solver/linear_solver.pb.h"
54 if (absl::EndsWith(filename,
".mps") || absl::EndsWith(filename,
".mps.gz") ||
55 absl::EndsWith(filename,
".mps.bz2")) {
58 if (absl::EndsWith(filename,
".pb") ||
59 absl::EndsWith(filename,
".textproto") ||
60 absl::EndsWith(filename,
".json") ||
61 absl::EndsWith(filename,
".json.gz")) {
64 LOG(QFATAL) <<
"Invalid filename suffix in " << filename
65 <<
". Valid suffixes are .mps, .mps.gz, .pb, .textproto,"
66 <<
".json, and .json.gz";
70 const std::string& mpmodel_proto_file,
bool include_names) {
71 MPModelProto lp_proto;
73 << mpmodel_proto_file;
77 return *std::move(result);
81 const std::string& mps_file) {
83 return absl::InvalidArgumentError(
84 "'linear_program' has a quadratic objective");
97 const std::string& mpmodel_proto_file) {
108class MpsReaderDimensionAndNames {
110 using IndexType = int64_t;
112 read_or_parse_failed_ =
false;
113 col_name_to_index_.clear();
114 row_name_to_index_.clear();
115 added_non_zeros_ = 0;
118 double ConstraintLowerBound(IndexType
index) {
return 0; }
119 double ConstraintUpperBound(IndexType
index) {
return 0; }
120 IndexType FindOrCreateConstraint(absl::string_view row_name) {
121 const auto [it, success_unused] = row_name_to_index_.try_emplace(
122 row_name,
static_cast<IndexType
>(row_name_to_index_.size()));
125 IndexType FindOrCreateVariable(absl::string_view col_name) {
126 const auto [it, success_unused] = col_name_to_index_.try_emplace(
127 col_name,
static_cast<IndexType
>(col_name_to_index_.size()));
130 void SetConstraintBounds(IndexType row_index,
double lower_bound,
132 void SetConstraintCoefficient(IndexType row_index, IndexType col_index,
136 void SetIsLazy(IndexType row_index) {}
137 void SetName(absl::string_view problem_name) {}
139 void SetObjectiveDirection(
bool maximize) {}
140 void SetObjectiveOffset(
double offset) {}
141 void SetVariableTypeToInteger(IndexType
index) {}
142 void SetVariableTypeToSemiContinuous(IndexType
index) {
143 LOG_FIRST_N(WARNING, 1)
144 <<
"Semi-continuous variables not supported, failed to parse file";
145 read_or_parse_failed_ =
true;
149 double VariableLowerBound(IndexType
index) {
return 0; }
150 double VariableUpperBound(IndexType
index) {
return 0; }
151 absl::Status CreateIndicatorConstraint(absl::string_view row_name,
152 IndexType col_index,
bool var_value) {
154 "Indicator constraints not supported, failed to parse file";
155 LOG_FIRST_N(WARNING, 1) <<
message;
156 return absl::InvalidArgumentError(
message);
162 IndexType FindVariableOrDie(absl::string_view col_name)
const {
163 const auto it = col_name_to_index_.find(col_name);
164 if (it == col_name_to_index_.end()) {
165 LOG(QFATAL) << absl::StrFormat(
166 "column `%s` not previously found in file, internal error?",
171 IndexType FindConstraintOrDie(absl::string_view row_name)
const {
172 const auto it = row_name_to_index_.find(row_name);
173 if (it == row_name_to_index_.end()) {
174 LOG(QFATAL) << absl::StrFormat(
175 "row `%s` not previously found in file, internal error?", row_name);
181 int64_t AddedNonZeros()
const {
return added_non_zeros_; }
184 int64_t
NumVariables()
const {
return col_name_to_index_.size(); }
187 int64_t
NumConstraints()
const {
return row_name_to_index_.size(); }
190 bool FailedToParse()
const {
return read_or_parse_failed_; }
193 const absl::flat_hash_map<std::string, IndexType>& ColNameIndexMap()
const {
194 return col_name_to_index_;
198 const absl::flat_hash_map<std::string, IndexType>& RowNameIndexMap()
const {
199 return row_name_to_index_;
203 bool read_or_parse_failed_ =
false;
204 absl::flat_hash_map<std::string, IndexType> col_name_to_index_;
205 absl::flat_hash_map<std::string, IndexType> row_name_to_index_;
206 int64_t added_non_zeros_ = 0;
224class MpsReaderQpDataWrapper {
226 using IndexType = int64_t;
233 MpsReaderQpDataWrapper(
const MpsReaderDimensionAndNames* dimension_and_names,
235 : include_names_{include_names},
236 dimension_and_names_{*dimension_and_names} {}
240 const int64_t num_variables = dimension_and_names_.NumVariables();
241 const int64_t num_constraints = dimension_and_names_.NumConstraints();
242 triplets_.reserve(dimension_and_names_.AddedNonZeros());
243 quadratic_program_ = QuadraticProgram(num_variables,
248 quadratic_program_.constraint_lower_bounds =
249 Eigen::VectorXd::Zero(num_constraints);
250 quadratic_program_.constraint_upper_bounds =
251 Eigen::VectorXd::Zero(num_constraints);
252 quadratic_program_.variable_lower_bounds =
253 Eigen::VectorXd::Zero(num_variables);
257 quadratic_program_.constraint_matrix);
259 if (quadratic_program_.objective_scaling_factor == -1) {
260 quadratic_program_.objective_offset *= -1;
261 for (
double& objective_coefficient :
262 quadratic_program_.objective_vector) {
263 objective_coefficient *= -1;
266 if (include_names_) {
267 quadratic_program_.variable_names =
268 std::vector<std::string>(dimension_and_names_.NumVariables());
269 quadratic_program_.constraint_names =
270 std::vector<std::string>(dimension_and_names_.NumConstraints());
271 for (
const auto& [
name,
index] : dimension_and_names_.ColNameIndexMap()) {
272 (*quadratic_program_.variable_names)[
index] =
name;
274 for (
const auto& [
name,
index] : dimension_and_names_.RowNameIndexMap()) {
275 (*quadratic_program_.constraint_names)[
index] =
name;
279 double ConstraintLowerBound(IndexType
index) {
280 return quadratic_program_.constraint_lower_bounds[
index];
282 double ConstraintUpperBound(IndexType
index) {
283 return quadratic_program_.constraint_upper_bounds[
index];
285 IndexType FindOrCreateConstraint(absl::string_view row_name) {
286 return dimension_and_names_.FindConstraintOrDie(row_name);
288 IndexType FindOrCreateVariable(absl::string_view col_name) {
289 return dimension_and_names_.FindVariableOrDie(col_name);
296 void SetConstraintCoefficient(IndexType row_index, IndexType col_index,
298 DCHECK_LT(triplets_.size(), triplets_.capacity());
299 triplets_.emplace_back(row_index, col_index,
coefficient);
301 void SetIsLazy(IndexType row_index) {
302 LOG_FIRST_N(WARNING, 1) <<
"Lazy constraint information lost, treated as "
303 "regular constraint instead";
305 void SetName(absl::string_view problem_name) {
306 if (include_names_) {
307 quadratic_program_.problem_name = std::string(problem_name);
313 void SetObjectiveDirection(
bool maximize) {
314 quadratic_program_.objective_scaling_factor = (maximize ? -1 : 1);
316 void SetObjectiveOffset(
double offset) {
317 quadratic_program_.objective_offset = offset;
319 void SetVariableTypeToInteger(IndexType
index) {
320 LOG_FIRST_N(WARNING, 1) <<
"Dropping integrality requirements, all "
321 "variables treated as continuous";
323 void SetVariableTypeToSemiContinuous(IndexType
index) {
326 LOG(QFATAL) <<
"Semi-continuous variables are unsupported, and should have "
327 "been detected before (in MpsReaderDimensionAndNames)";
334 double VariableLowerBound(IndexType
index) {
335 return quadratic_program_.variable_lower_bounds[
index];
337 double VariableUpperBound(IndexType
index) {
338 return quadratic_program_.variable_upper_bounds[
index];
340 absl::Status CreateIndicatorConstraint(absl::string_view row_name,
341 IndexType col_index,
bool var_value) {
344 LOG(QFATAL) <<
"Indicator constraints are unsupported, and should have "
345 "been detected before (in MpsReaderDimensionAndNames)";
351 QuadraticProgram&& GetAndClearQuadraticProgram() {
352 return std::move(quadratic_program_);
357 QuadraticProgram quadratic_program_;
358 const MpsReaderDimensionAndNames& dimension_and_names_;
359 std::vector<Eigen::Triplet<double, int64_t>> triplets_;
365 const std::string& lp_file,
bool include_names) {
366 MpsReaderDimensionAndNames dimension_and_names;
369 const absl::StatusOr<MPSReaderFormat> pass_one_format =
370 pass_one_reader.
ParseFile(lp_file, &dimension_and_names,
371 MPSReaderFormat::kAutoDetect);
372 if (!pass_one_format.ok()) {
374 "Could not read or parse file `%s` as an MPS file", lp_file);
376 if (dimension_and_names.FailedToParse()) {
377 return absl::InvalidArgumentError(absl::StrFormat(
378 "Could not read or parse file `%s` as an MPS file, or unsupported "
379 "features/sections found",
382 DCHECK(*pass_one_format == MPSReaderFormat::kFixed ||
383 *pass_one_format == MPSReaderFormat::kFree);
385 MpsReaderQpDataWrapper qp_data_wrapper(&dimension_and_names, include_names);
387 const absl::StatusOr<MPSReaderFormat> pass_two_format =
388 pass_two_reader.
ParseFile(lp_file, &qp_data_wrapper, *pass_one_format);
389 if (!pass_two_format.ok()) {
391 "Could not read or parse file `%s` as an MPS file "
392 "(maybe file changed between reads?)",
395 DCHECK(*pass_one_format == *pass_two_format);
396 return qp_data_wrapper.GetAndClearQuadraticProgram();
400 bool include_names) {
401 const absl::StatusOr<QuadraticProgram> result =
404 LOG(QFATAL) <<
"Error reading MPS Linear Program from " << lp_file <<
": "
405 << result.status().message();
#define ASSIGN_OR_RETURN(lhs, rexpr)
#define RETURN_IF_ERROR(expr)
absl::StatusOr< MPSReaderFormat > ParseFile(absl::string_view file_name, DataWrapper *data, MPSReaderFormat form=MPSReaderFormat::kAutoDetect)
CpModelProto proto
The output proto.
const std::string name
A name for logging purposes.
absl::Status Open(absl::string_view filename, absl::string_view mode, File **f, Options options)
As of 2016-01, these methods can only be used with flags = file::Defaults().
absl::Status SetBinaryProto(absl::string_view filename, const google::protobuf::Message &proto, Options options)
absl::Status WriteString(File *file, absl::string_view contents, Options options)
int NumVariables(const VariablesProto &variables)
int NumConstraints(const LinearConstraintsProto &linear_constraints)
Validation utilities for solvers.proto.
absl::StatusOr< MPModelProto > QpToMpModelProto(const QuadraticProgram &qp)
QuadraticProgram ReadMPModelProtoFileOrDie(const std::string &mpmodel_proto_file, bool include_names)
absl::StatusOr< QuadraticProgram > QpFromMpModelProto(const MPModelProto &proto, bool relax_integer_variables, bool include_names)
QuadraticProgram ReadQuadraticProgramOrDie(const std::string &filename, bool include_names)
bool IsLinearProgram(const QuadraticProgram &qp)
absl::Status WriteLinearProgramToMps(const QuadraticProgram &linear_program, const std::string &mps_file)
QuadraticProgram ReadMpsLinearProgramOrDie(const std::string &lp_file, bool include_names)
void SetEigenMatrixFromTriplets(std::vector< Eigen::Triplet< double, int64_t > > triplets, Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix)
absl::Status WriteQuadraticProgramToMPModelProto(const QuadraticProgram &quadratic_program, const std::string &mpmodel_proto_file)
absl::StatusOr< QuadraticProgram > ReadMpsLinearProgram(const std::string &lp_file, bool include_names)
absl::StatusOr< std::string > ExportModelAsMpsFormat(const MPModelProto &model, const MPModelExportOptions &options)
absl::Status ReadFileToProto(absl::string_view filename, google::protobuf::Message *proto, bool allow_partial)