OR-Tools  9.6
sharded_quadratic_program.cc
Go to the documentation of this file.
1 // Copyright 2010-2022 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 <cstdint>
17 #include <memory>
18 #include <utility>
19 
20 #include "Eigen/Core"
21 #include "Eigen/SparseCore"
22 #include "absl/log/check.h"
23 #include "absl/memory/memory.h"
24 #include "absl/strings/string_view.h"
25 #include "ortools/base/logging.h"
28 #include "ortools/pdlp/sharder.h"
29 
30 namespace operations_research::pdlp {
31 
32 namespace {
33 
34 // Logs a warning if `matrix` has more than `density_limit` non-zeros in
35 // a single column.
36 void WarnIfMatrixUnbalanced(
37  const Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix,
38  absl::string_view matrix_name, int64_t density_limit) {
39  if (matrix.cols() == 0) return;
40  int64_t worst_column = 0;
41  for (int64_t col = 0; col < matrix.cols(); ++col) {
42  if (matrix.col(col).nonZeros() > matrix.col(worst_column).nonZeros()) {
43  worst_column = col;
44  }
45  }
46  if (matrix.col(worst_column).nonZeros() > density_limit) {
47  // TODO(user): fix this automatically in presolve instead of asking the
48  // user to do it.
49  LOG(WARNING)
50  << "The " << matrix_name << " has "
51  << matrix.col(worst_column).nonZeros() << " non-zeros in its "
52  << worst_column
53  << "th column. For best parallelization all rows and columns should "
54  "have at most order "
55  << density_limit
56  << " non-zeros. Consider rewriting the QP to split the corresponding "
57  "variable or constraint.";
58  }
59 }
60 
61 } // namespace
62 
64  const int num_threads,
65  const int num_shards)
66  : qp_(std::move(qp)),
67  transposed_constraint_matrix_(qp_.constraint_matrix.transpose()),
68  thread_pool_(num_threads == 1
69  ? nullptr
70  : std::make_unique<ThreadPool>("PDLP", num_threads)),
71  constraint_matrix_sharder_(qp_.constraint_matrix, num_shards,
72  thread_pool_.get()),
73  transposed_constraint_matrix_sharder_(transposed_constraint_matrix_,
74  num_shards, thread_pool_.get()),
75  primal_sharder_(qp_.variable_lower_bounds.size(), num_shards,
76  thread_pool_.get()),
77  dual_sharder_(qp_.constraint_lower_bounds.size(), num_shards,
78  thread_pool_.get()) {
79  CHECK_GE(num_threads, 1);
80  CHECK_GE(num_shards, num_threads);
81  if (num_threads > 1) {
82  thread_pool_->StartWorkers();
83  const int64_t work_per_iteration = qp_.constraint_matrix.nonZeros() +
84  qp_.variable_lower_bounds.size() +
85  qp_.constraint_lower_bounds.size();
86  const int64_t column_density_limit = work_per_iteration / num_threads;
87  WarnIfMatrixUnbalanced(qp_.constraint_matrix, "constraint matrix",
88  column_density_limit);
89  WarnIfMatrixUnbalanced(transposed_constraint_matrix_,
90  "transposed constraint matrix",
91  column_density_limit);
92  }
93 }
94 
95 namespace {
96 
97 // Multiply each entry of `matrix` by the corresponding element of
98 // `row_scaling_vec` and `col_scaling_vec`, i.e.,
99 // `matrix[i,j] *= row_scaling_vec[i] * col_scaling_vec[j]`.
100 void ScaleMatrix(
101  const Eigen::VectorXd& col_scaling_vec,
102  const Eigen::VectorXd& row_scaling_vec, const Sharder& sharder,
103  Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix) {
104  CHECK_EQ(matrix.cols(), col_scaling_vec.size());
105  CHECK_EQ(matrix.rows(), row_scaling_vec.size());
106  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
107  auto matrix_shard = shard(matrix);
108  auto col_scaling_vec_shard = shard(col_scaling_vec);
109  for (int64_t col_num = 0; col_num < shard(matrix).outerSize(); ++col_num) {
110  for (decltype(matrix_shard)::InnerIterator it(matrix_shard, col_num); it;
111  ++it) {
112  it.valueRef() *=
113  row_scaling_vec[it.row()] * col_scaling_vec_shard[it.col()];
114  }
115  }
116  });
117 }
118 
119 } // namespace
120 
122  const Eigen::VectorXd& col_scaling_vec,
123  const Eigen::VectorXd& row_scaling_vec) {
124  CHECK_EQ(PrimalSize(), col_scaling_vec.size());
125  CHECK_EQ(DualSize(), row_scaling_vec.size());
126  primal_sharder_.ParallelForEachShard([&](const Sharder::Shard& shard) {
127  CHECK((shard(col_scaling_vec).array() > 0.0).all());
128  shard(qp_.objective_vector) =
129  shard(qp_.objective_vector).cwiseProduct(shard(col_scaling_vec));
130  shard(qp_.variable_lower_bounds) =
131  shard(qp_.variable_lower_bounds).cwiseQuotient(shard(col_scaling_vec));
132  shard(qp_.variable_upper_bounds) =
133  shard(qp_.variable_upper_bounds).cwiseQuotient(shard(col_scaling_vec));
134  if (!IsLinearProgram(qp_)) {
135  shard(qp_.objective_matrix->diagonal()) =
136  shard(qp_.objective_matrix->diagonal())
137  .cwiseProduct(
138  shard(col_scaling_vec).cwiseProduct(shard(col_scaling_vec)));
139  }
140  });
141  dual_sharder_.ParallelForEachShard([&](const Sharder::Shard& shard) {
142  CHECK((shard(row_scaling_vec).array() > 0.0).all());
143  shard(qp_.constraint_lower_bounds) =
144  shard(qp_.constraint_lower_bounds).cwiseProduct(shard(row_scaling_vec));
145  shard(qp_.constraint_upper_bounds) =
146  shard(qp_.constraint_upper_bounds).cwiseProduct(shard(row_scaling_vec));
147  });
148 
149  ScaleMatrix(col_scaling_vec, row_scaling_vec, constraint_matrix_sharder_,
150  qp_.constraint_matrix);
151  ScaleMatrix(row_scaling_vec, col_scaling_vec,
152  transposed_constraint_matrix_sharder_,
153  transposed_constraint_matrix_);
154 }
155 
156 } // namespace operations_research::pdlp
void RescaleQuadraticProgram(const Eigen::VectorXd &col_scaling_vec, const Eigen::VectorXd &row_scaling_vec)
ShardedQuadraticProgram(QuadraticProgram qp, int num_threads, int num_shards)
void ParallelForEachShard(const std::function< void(const Shard &)> &func) const
Definition: sharder.cc:104
ColIndex col
Definition: markowitz.cc:186
bool IsLinearProgram(const QuadraticProgram &qp)
Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > constraint_matrix
std::optional< Eigen::DiagonalMatrix< double, Eigen::Dynamic > > objective_matrix
VectorXd variable_lower_bounds