43 return absl::InvalidArgumentError(absl::StrCat(
44 "Inconsistent dimensions: variable lower bound vector has size ",
45 var_lb_size,
" while variable upper bound vector has size ",
49 return absl::InvalidArgumentError(absl::StrCat(
50 "Inconsistent dimensions: variable lower bound vector has size ",
51 var_lb_size,
" while objective vector has size ",
55 return absl::InvalidArgumentError(absl::StrCat(
56 "Inconsistent dimensions: variable lower bound vector has size ",
57 var_lb_size,
" while constraint matrix has ",
62 return absl::InvalidArgumentError(absl::StrCat(
63 "Inconsistent dimensions: variable lower bound vector has size ",
64 var_lb_size,
" while objective matrix has ",
68 return absl::InvalidArgumentError(absl::StrCat(
69 "Inconsistent dimensions: constraint lower bound vector has size ",
70 con_lb_size,
" while constraint upper bound vector has size ",
74 return absl::InvalidArgumentError(absl::StrCat(
75 "Inconsistent dimensions: constraint lower bound vector has size ",
76 con_lb_size,
" while constraint matrix has ",
82 return absl::InvalidArgumentError(absl::StrCat(
83 "Inconsistent dimensions: variable lower bound vector has size ",
84 var_lb_size,
" while variable names has size ",
90 return absl::InvalidArgumentError(absl::StrCat(
91 "Inconsistent dimensions: constraint lower bound vector has size ",
92 con_lb_size,
" while constraint names has size ",
96 return absl::OkStatus();
100 const MPModelProto&
proto,
bool relax_integer_variables,
101 bool include_names) {
102 if (!
proto.general_constraint().empty()) {
103 return absl::InvalidArgumentError(
"General constraints are not supported.");
105 const int primal_size =
proto.variable_size();
106 const int dual_size =
proto.constraint_size();
113 for (
int i = 0; i < primal_size; ++i) {
114 const auto&
var =
proto.variable(i);
118 if (
var.is_integer() && !relax_integer_variables) {
119 return absl::InvalidArgumentError(
120 "Integer variable encountered with relax_integer_variables == false");
126 std::vector<int> nonzeros_by_column(primal_size);
127 for (
int i = 0; i < dual_size; ++i) {
128 const auto& con =
proto.constraint(i);
129 for (
int j = 0; j < con.var_index_size(); ++j) {
130 if (con.var_index(j) < 0 || con.var_index(j) >= primal_size) {
131 return absl::InvalidArgumentError(absl::StrCat(
132 "Variable index of ", i,
"th constraint's ", j,
"th nonzero is ",
133 con.var_index(j),
" which is not in the allowed range [0, ",
136 nonzeros_by_column[con.var_index(j)]++;
152 for (
int i = 0; i < dual_size; ++i) {
153 const auto& con =
proto.constraint(i);
154 CHECK_EQ(con.var_index_size(), con.coefficient_size())
155 <<
" in " << i <<
"th constraint";
156 if (con.var_index_size() != con.coefficient_size()) {
157 return absl::InvalidArgumentError(
158 absl::StrCat(i,
"th constraint has ", con.coefficient_size(),
159 " coefficients, expected ", con.var_index_size()));
162 for (
int j = 0; j < con.var_index_size(); ++j) {
171 std::vector<Eigen::Triplet<double, int64_t>> triplets;
172 const auto& quadratic =
proto.quadratic_objective();
173 if (quadratic.qvar1_index_size() != quadratic.qvar2_index_size() ||
174 quadratic.qvar1_index_size() != quadratic.coefficient_size()) {
175 return absl::InvalidArgumentError(absl::StrCat(
176 "The quadratic objective has ", quadratic.qvar1_index_size(),
177 " qvar1_indices, ", quadratic.qvar2_index_size(),
178 " qvar2_indices, and ", quadratic.coefficient_size(),
179 " coefficients, expected equal numbers."));
181 if (quadratic.qvar1_index_size() > 0) {
186 for (
int i = 0; i < quadratic.qvar1_index_size(); ++i) {
187 const int index1 = quadratic.qvar1_index(i);
188 const int index2 = quadratic.qvar2_index(i);
189 if (index1 < 0 || index2 < 0 || index1 >= primal_size ||
190 index2 >= primal_size) {
191 return absl::InvalidArgumentError(absl::StrCat(
192 "The quadratic objective's ", i,
"th nonzero has indices ", index1,
193 " and ", index2,
", which are not both in the expected range [0, ",
196 if (index1 != index2) {
197 return absl::InvalidArgumentError(absl::StrCat(
198 "The quadratic objective's ", i,
199 "th nonzero has off-diagonal element at (", index1,
", ", index2,
200 "). Only diagonal objective matrices are supported."));
207 if (
proto.maximize()) {
225 const int64_t largest_ok_size) {
228 bool primal_too_big = primal_size > largest_ok_size;
229 if (primal_too_big) {
230 return absl::InvalidArgumentError(absl::StrCat(
231 "Too many variables (", primal_size,
") to index with an int32_t."));
233 bool dual_too_big = dual_size > largest_ok_size;
235 return absl::InvalidArgumentError(absl::StrCat(
236 "Too many constraints (", dual_size,
") to index with an int32_t."));
238 return absl::OkStatus();
245 return absl::InvalidArgumentError(
246 "objective_scaling_factor cannot be zero.");
256 proto.set_maximize(
true);
258 proto.set_maximize(
false);
261 proto.mutable_variable()->Reserve(primal_size);
262 for (int64_t i = 0; i < primal_size; ++i) {
276 proto.mutable_constraint()->Reserve(dual_size);
277 for (int64_t i = 0; i < dual_size; ++i) {
278 auto* con =
proto.add_constraint();
289 using InnerIterator =
290 ::Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>::InnerIterator;
293 auto* con =
proto.mutable_constraint(iter.row());
296 con->add_var_index(iter.col());
297 con->add_coefficient(iter.value());
306 auto* quadratic_objective =
proto.mutable_quadratic_objective();
308 for (int64_t i = 0; i < diagonal.size(); ++i) {
309 if (diagonal[i] != 0.0) {
310 quadratic_objective->add_qvar1_index(i);
311 quadratic_objective->add_qvar2_index(i);
323 auto object_name = [](int64_t
index,
324 const std::optional<std::vector<std::string>>& names,
325 absl::string_view prefix) {
326 if (names.has_value()) {
327 CHECK_LT(
index, names->size());
328 return (*names)[
index];
330 return absl::StrCat(prefix,
index);
332 auto variable_name = [&qp, &object_name](int64_t
var_index) {
335 auto constraint_name = [&qp, &object_name](int64_t con_index) {
340 return absl::StrCat(
"Quadratic program with inconsistent dimensions: ",
355 if (result.size() >= max_size)
break;
359 result.append(
" + 1/2 * (");
361 for (int64_t i = 0; i < obj_diagonal.size(); ++i) {
362 if (obj_diagonal[i] != 0.0) {
363 absl::StrAppend(&result,
" + ", obj_diagonal[i],
" ", variable_name(i),
365 if (result.size() >= max_size)
break;
372 result.append(
")\n");
374 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>
376 for (int64_t constraint_idx = 0;
377 constraint_idx < constraint_matrix_transpose.outerSize();
379 absl::StrAppend(&result, constraint_name(constraint_idx),
":");
381 -std::numeric_limits<double>::infinity()) {
385 for (
decltype(constraint_matrix_transpose)::InnerIterator it(
386 constraint_matrix_transpose, constraint_idx);
388 absl::StrAppend(&result,
" + ", it.value(),
" ",
389 variable_name(it.index()));
390 if (result.size() >= max_size)
break;
393 std::numeric_limits<double>::infinity()) {
394 absl::StrAppend(&result,
399 result.append(
"Bounds\n");
402 -std::numeric_limits<double>::infinity()) {
404 std::numeric_limits<double>::infinity()) {
405 absl::StrAppend(&result, variable_name(i),
" free\n");
407 absl::StrAppend(&result, variable_name(i),
412 std::numeric_limits<double>::infinity()) {
413 absl::StrAppend(&result, variable_name(i),
418 " <= ", variable_name(i),
422 if (result.size() >= max_size)
break;
424 if (result.size() > max_size) {
425 result.resize(max_size - 4);
426 result.append(
"...\n");
432 std::vector<Eigen::Triplet<double, int64_t>> triplets,
433 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix) {
434 using Triplet = Eigen::Triplet<double, int64_t>;
435 std::sort(triplets.begin(), triplets.end(),
436 [](
const Triplet& lhs,
const Triplet& rhs) {
437 return std::tie(lhs.col(), lhs.row()) <
438 std::tie(rhs.col(), rhs.row());
446 std::vector<int64_t> num_column_entries(matrix.cols());
447 for (
const Triplet& triplet : triplets) {
448 ++num_column_entries[triplet.col()];
452 matrix.reserve(num_column_entries);
453 for (
const Triplet& triplet : triplets) {
454 matrix.insert(triplet.row(), triplet.col()) = triplet.value();
456 if (matrix.outerSize() > 0) {
457 matrix.makeCompressed();
463 std::vector<Eigen::Triplet<double, int64_t>>& triplets) {
464 if (triplets.empty())
return;
465 auto output_iter = triplets.begin();
466 for (
auto p = output_iter + 1; p != triplets.end(); ++p) {
467 if (output_iter->row() == p->row() && output_iter->col() == p->col()) {
468 *output_iter = {output_iter->row(), output_iter->col(),
469 output_iter->value() + p->value()};
472 if (output_iter != p) {
478 triplets.erase(output_iter + 1, triplets.end());