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