25 #include "Eigen/SparseCore"
26 #include "absl/log/check.h"
27 #include "absl/status/status.h"
28 #include "absl/status/statusor.h"
29 #include "absl/strings/str_cat.h"
31 #include "ortools/linear_solver/linear_solver.pb.h"
35 using ::Eigen::VectorXd;
42 return absl::InvalidArgumentError(absl::StrCat(
43 "Inconsistent dimensions: variable lower bound vector has size ",
44 var_lb_size,
" while variable upper bound vector has size ",
48 return absl::InvalidArgumentError(absl::StrCat(
49 "Inconsistent dimensions: variable lower bound vector has size ",
50 var_lb_size,
" while objective vector has size ",
54 return absl::InvalidArgumentError(absl::StrCat(
55 "Inconsistent dimensions: variable lower bound vector has size ",
56 var_lb_size,
" while constraint matrix has ",
61 return absl::InvalidArgumentError(absl::StrCat(
62 "Inconsistent dimensions: variable lower bound vector has size ",
63 var_lb_size,
" while objective matrix has ",
67 return absl::InvalidArgumentError(absl::StrCat(
68 "Inconsistent dimensions: constraint lower bound vector has size ",
69 con_lb_size,
" while constraint upper bound vector has size ",
73 return absl::InvalidArgumentError(absl::StrCat(
74 "Inconsistent dimensions: constraint lower bound vector has size ",
75 con_lb_size,
" while constraint matrix has ",
79 return absl::OkStatus();
83 const bool constraint_bounds_valid =
86 const bool variable_bounds_valid =
89 return constraint_bounds_valid && variable_bounds_valid;
93 const MPModelProto&
proto,
bool relax_integer_variables,
95 if (!
proto.general_constraint().empty()) {
96 return absl::InvalidArgumentError(
"General constraints are not supported.");
98 const int primal_size =
proto.variable_size();
99 const int dual_size =
proto.constraint_size();
106 for (
int i = 0; i < primal_size; ++i) {
107 const auto&
var =
proto.variable(i);
111 if (
var.is_integer() && !relax_integer_variables) {
112 return absl::InvalidArgumentError(
113 "Integer variable encountered with relax_integer_variables == false");
119 std::vector<int> nonzeros_by_column(primal_size);
120 for (
int i = 0; i < dual_size; ++i) {
121 const auto& con =
proto.constraint(i);
122 for (
int j = 0; j < con.var_index_size(); ++j) {
123 if (con.var_index(j) < 0 || con.var_index(j) >= primal_size) {
124 return absl::InvalidArgumentError(absl::StrCat(
125 "Variable index of ", i,
"th constraint's ", j,
"th nonzero is ",
126 con.var_index(j),
" which is not in the allowed range [0, ",
129 nonzeros_by_column[con.var_index(j)]++;
145 for (
int i = 0; i < dual_size; ++i) {
146 const auto& con =
proto.constraint(i);
147 CHECK_EQ(con.var_index_size(), con.coefficient_size())
148 <<
" in " << i <<
"th constraint";
149 if (con.var_index_size() != con.coefficient_size()) {
150 return absl::InvalidArgumentError(
151 absl::StrCat(i,
"th constraint has ", con.coefficient_size(),
152 " coefficients, expected ", con.var_index_size()));
155 for (
int j = 0; j < con.var_index_size(); ++j) {
164 std::vector<Eigen::Triplet<double, int64_t>> triplets;
165 const auto& quadratic =
proto.quadratic_objective();
166 if (quadratic.qvar1_index_size() != quadratic.qvar2_index_size() ||
167 quadratic.qvar1_index_size() != quadratic.coefficient_size()) {
168 return absl::InvalidArgumentError(absl::StrCat(
169 "The quadratic objective has ", quadratic.qvar1_index_size(),
170 " qvar1_indices, ", quadratic.qvar2_index_size(),
171 " qvar2_indices, and ", quadratic.coefficient_size(),
172 " coefficients, expected equal numbers."));
174 if (quadratic.qvar1_index_size() > 0) {
179 for (
int i = 0; i < quadratic.qvar1_index_size(); ++i) {
180 const int index1 = quadratic.qvar1_index(i);
181 const int index2 = quadratic.qvar2_index(i);
182 if (index1 < 0 || index2 < 0 || index1 >= primal_size ||
183 index2 >= primal_size) {
184 return absl::InvalidArgumentError(absl::StrCat(
185 "The quadratic objective's ", i,
"th nonzero has indices ", index1,
186 " and ", index2,
", which are not both in the expected range [0, ",
189 if (index1 != index2) {
190 return absl::InvalidArgumentError(absl::StrCat(
191 "The quadratic objective's ", i,
192 "th nonzero has off-diagonal element at (", index1,
", ", index2,
193 "). Only diagonal objective matrices are supported."));
200 if (
proto.maximize()) {
208 return std::move(qp);
218 const int64_t largest_ok_size) {
221 bool primal_too_big = primal_size > largest_ok_size;
222 if (primal_too_big) {
223 return absl::InvalidArgumentError(absl::StrCat(
224 "Too many variables (", primal_size,
") to index with an int32_t."));
226 bool dual_too_big = dual_size > largest_ok_size;
228 return absl::InvalidArgumentError(absl::StrCat(
229 "Too many constraints (", dual_size,
") to index with an int32_t."));
231 return absl::OkStatus();
238 return absl::InvalidArgumentError(
239 "objective_scaling_factor cannot be zero.");
249 proto.set_maximize(
true);
251 proto.set_maximize(
false);
254 proto.mutable_variable()->Reserve(primal_size);
255 for (int64_t i = 0; i < primal_size; ++i) {
261 if (qp.
variable_names.has_value() && i < qp.variable_names->size()) {
269 proto.mutable_constraint()->Reserve(dual_size);
270 for (int64_t i = 0; i < dual_size; ++i) {
271 auto* con =
proto.add_constraint();
282 using InnerIterator =
283 ::Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>::InnerIterator;
286 auto* con =
proto.mutable_constraint(iter.row());
289 con->add_var_index(iter.col());
290 con->add_coefficient(iter.value());
299 auto* quadratic_objective =
proto.mutable_quadratic_objective();
301 for (int64_t i = 0; i < diagonal.size(); ++i) {
302 if (diagonal[i] != 0.0) {
303 quadratic_objective->add_qvar1_index(i);
304 quadratic_objective->add_qvar2_index(i);
316 std::vector<Eigen::Triplet<double, int64_t>> triplets,
317 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix) {
318 using Triplet = Eigen::Triplet<double, int64_t>;
319 std::sort(triplets.begin(), triplets.end(),
320 [](
const Triplet& lhs,
const Triplet& rhs) {
321 return std::tie(lhs.col(), lhs.row()) <
322 std::tie(rhs.col(), rhs.row());
330 std::vector<int64_t> num_column_entries(matrix.cols());
331 for (
const Triplet& triplet : triplets) {
332 ++num_column_entries[triplet.col()];
336 matrix.reserve(num_column_entries);
337 for (
const Triplet& triplet : triplets) {
338 matrix.insert(triplet.row(), triplet.col()) = triplet.value();
340 if (matrix.outerSize() > 0) {
341 matrix.makeCompressed();
347 std::vector<Eigen::Triplet<double, int64_t>>& triplets) {
348 if (triplets.empty())
return;
349 auto output_iter = triplets.begin();
350 for (
auto p = output_iter + 1; p != triplets.end(); ++p) {
351 if (output_iter->row() == p->row() && output_iter->col() == p->col()) {
352 *output_iter = {output_iter->row(), output_iter->col(),
353 output_iter->value() + p->value()};
356 if (output_iter != p) {
362 triplets.erase(output_iter + 1, triplets.end());
#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)
void SetEigenMatrixFromTriplets(std::vector< Eigen::Triplet< double, int64_t >> triplets, Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix)
absl::Status CanFitInMpModelProto(const QuadraticProgram &qp)
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
std::optional< std::vector< std::string > > constraint_names
Eigen::VectorXd constraint_lower_bounds
std::optional< std::string > problem_name
std::optional< std::vector< std::string > > variable_names
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