26#include "Eigen/SparseCore"
27#include "absl/status/status.h"
28#include "absl/status/statusor.h"
29#include "absl/strings/str_cat.h"
30#include "absl/types/optional.h"
33#include "ortools/linear_solver/linear_solver.pb.h"
37using ::Eigen::VectorXd;
44 return absl::InvalidArgumentError(absl::StrCat(
45 "Inconsistent dimensions: variable lower bound vector has size ",
46 var_lb_size,
" while variable upper bound vector has size ",
50 return absl::InvalidArgumentError(absl::StrCat(
51 "Inconsistent dimensions: variable lower bound vector has size ",
52 var_lb_size,
" while objective vector has size ",
56 return absl::InvalidArgumentError(absl::StrCat(
57 "Inconsistent dimensions: variable lower bound vector has size ",
58 var_lb_size,
" while constraint matrix has ",
63 return absl::InvalidArgumentError(absl::StrCat(
64 "Inconsistent dimensions: variable lower bound vector has size ",
65 var_lb_size,
" while objective matrix has ",
69 return absl::InvalidArgumentError(absl::StrCat(
70 "Inconsistent dimensions: constraint lower bound vector has size ",
71 con_lb_size,
" while constraint upper bound vector has size ",
75 return absl::InvalidArgumentError(absl::StrCat(
76 "Inconsistent dimensions: constraint lower bound vector has size ",
77 con_lb_size,
" while constraint matrix has ",
81 return absl::OkStatus();
85 const bool constraint_bounds_valid =
88 const bool variable_bounds_valid =
91 return constraint_bounds_valid && variable_bounds_valid;
95 const MPModelProto&
proto,
bool relax_integer_variables,
97 if (!
proto.general_constraint().empty()) {
98 return absl::InvalidArgumentError(
"General constraints are not supported.");
100 const int primal_size =
proto.variable_size();
101 const int dual_size =
proto.constraint_size();
108 for (
int i = 0; i < primal_size; ++i) {
109 const auto&
var =
proto.variable(i);
113 if (
var.is_integer() && !relax_integer_variables) {
114 return absl::InvalidArgumentError(
115 "Integer variable encountered with relax_integer_variables == false");
121 std::vector<int> nonzeros_by_column(primal_size);
122 for (
int i = 0; i < dual_size; ++i) {
123 const auto& con =
proto.constraint(i);
124 for (
int j = 0; j < con.var_index_size(); ++j) {
125 if (con.var_index(j) < 0 || con.var_index(j) >= primal_size) {
126 return absl::InvalidArgumentError(absl::StrCat(
127 "Variable index of ", i,
"th constraint's ", j,
"th nonzero is ",
128 con.var_index(j),
" which is not in the allowed range [0, ",
131 nonzeros_by_column[con.var_index(j)]++;
147 for (
int i = 0; i < dual_size; ++i) {
148 const auto& con =
proto.constraint(i);
149 CHECK_EQ(con.var_index_size(), con.coefficient_size())
150 <<
" in " << i <<
"th constraint";
151 if (con.var_index_size() != con.coefficient_size()) {
152 return absl::InvalidArgumentError(
153 absl::StrCat(i,
"th constraint has ", con.coefficient_size(),
154 " coefficients, expected ", con.var_index_size()));
157 for (
int j = 0; j < con.var_index_size(); ++j) {
166 std::vector<Eigen::Triplet<double, int64_t>> triplets;
167 const auto& quadratic =
proto.quadratic_objective();
168 if (quadratic.qvar1_index_size() != quadratic.qvar2_index_size() ||
169 quadratic.qvar1_index_size() != quadratic.coefficient_size()) {
170 return absl::InvalidArgumentError(absl::StrCat(
171 "The quadratic objective has ", quadratic.qvar1_index_size(),
172 " qvar1_indices, ", quadratic.qvar2_index_size(),
173 " qvar2_indices, and ", quadratic.coefficient_size(),
174 " coefficients, expected equal numbers."));
176 if (quadratic.qvar1_index_size() > 0) {
181 for (
int i = 0; i < quadratic.qvar1_index_size(); ++i) {
182 const int index1 = quadratic.qvar1_index(i);
183 const int index2 = quadratic.qvar2_index(i);
184 if (index1 < 0 || index2 < 0 || index1 >= primal_size ||
185 index2 >= primal_size) {
186 return absl::InvalidArgumentError(absl::StrCat(
187 "The quadratic objective's ", i,
"th nonzero has indices ", index1,
188 " and ", index2,
", which are not both in the expected range [0, ",
191 if (index1 != index2) {
192 return absl::InvalidArgumentError(absl::StrCat(
193 "The quadratic objective's ", i,
194 "th nonzero has off-diagonal element at (", index1,
", ", index2,
195 "). Only diagonal objective matrices are supported."));
202 if (
proto.maximize()) {
210 return std::move(qp);
220 const int64_t largest_ok_size) {
223 bool primal_too_big = primal_size > largest_ok_size;
224 if (primal_too_big) {
225 return absl::InvalidArgumentError(absl::StrCat(
226 "Too many variables (", primal_size,
") to index with an int32_t."));
228 bool dual_too_big = dual_size > largest_ok_size;
230 return absl::InvalidArgumentError(absl::StrCat(
231 "Too many constraints (", dual_size,
") to index with an int32_t."));
233 return absl::OkStatus();
240 return absl::InvalidArgumentError(
241 "objective_scaling_factor cannot be zero.");
251 proto.set_maximize(
true);
253 proto.set_maximize(
false);
256 proto.mutable_variable()->Reserve(primal_size);
257 for (int64_t i = 0; i < primal_size; ++i) {
263 if (qp.
variable_names.has_value() && i < qp.variable_names->size()) {
271 proto.mutable_constraint()->Reserve(dual_size);
272 for (int64_t i = 0; i < dual_size; ++i) {
273 auto* con =
proto.add_constraint();
284 using InnerIterator =
285 ::Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>::InnerIterator;
288 auto* con =
proto.mutable_constraint(iter.row());
291 con->add_var_index(iter.col());
292 con->add_coefficient(iter.value());
300 auto* quadratic_objective =
proto.mutable_quadratic_objective();
302 for (int64_t i = 0; i < diagonal.size(); ++i) {
303 if (diagonal[i] != 0.0) {
304 quadratic_objective->add_qvar1_index(i);
305 quadratic_objective->add_qvar2_index(i);
317 std::vector<Eigen::Triplet<double, int64_t>> triplets,
318 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix) {
319 using Triplet = Eigen::Triplet<double, int64_t>;
320 std::sort(triplets.begin(), triplets.end(),
321 [](
const Triplet& lhs,
const Triplet& rhs) {
322 return std::tie(lhs.col(), lhs.row()) <
323 std::tie(rhs.col(), rhs.row());
331 std::vector<int64_t> num_column_entries(matrix.cols());
332 for (
const Triplet& triplet : triplets) {
333 ++num_column_entries[triplet.col()];
337 matrix.reserve(num_column_entries);
338 for (
const Triplet& triplet : triplets) {
339 matrix.insert(triplet.row(), triplet.col()) = triplet.value();
341 if (matrix.outerSize() > 0) {
342 matrix.makeCompressed();
348 std::vector<Eigen::Triplet<double, int64_t>>& triplets) {
349 if (triplets.empty())
return;
350 auto output_iter = triplets.begin();
351 for (
auto p = output_iter + 1; p != triplets.end(); ++p) {
352 if (output_iter->row() == p->row() && output_iter->col() == p->col()) {
353 *output_iter = {output_iter->row(), output_iter->col(),
354 output_iter->value() + p->value()};
357 if (output_iter != p) {
363 triplets.erase(output_iter + 1, triplets.end());
#define CHECK_EQ(val1, val2)
#define RETURN_IF_ERROR(expr)
void CombineRepeatedTripletsInPlace(std::vector< Eigen::Triplet< double, int64_t > > &triplets)
absl::Status TestableCanFitInMpModelProto(const QuadraticProgram &qp, const int64_t largest_ok_size)
absl::StatusOr< QuadraticProgram > QpFromMpModelProto(const MPModelProto &proto, bool relax_integer_variables, bool include_names)
absl::Status ValidateQuadraticProgramDimensions(const QuadraticProgram &qp)
absl::Status CanFitInMpModelProto(const QuadraticProgram &qp)
void SetEigenMatrixFromTriplets(std::vector< Eigen::Triplet< double, int64_t > > triplets, Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix)
bool HasValidBounds(const QuadraticProgram &qp)
absl::StatusOr< MPModelProto > QpToMpModelProto(const QuadraticProgram &qp)
bool IsLinearProgram(const QuadraticProgram &qp)
Eigen::VectorXd variable_upper_bounds
Eigen::VectorXd variable_lower_bounds
double objective_scaling_factor
absl::optional< std::vector< std::string > > constraint_names
absl::optional< std::vector< std::string > > variable_names
Eigen::VectorXd constraint_lower_bounds
absl::optional< std::string > problem_name
Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > constraint_matrix
std::optional< Eigen::DiagonalMatrix< double, Eigen::Dynamic > > objective_matrix
Eigen::VectorXd constraint_upper_bounds
Eigen::VectorXd objective_vector