OR-Tools  9.6
sharder.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 
14 #include "ortools/pdlp/sharder.h"
15 
16 #include <algorithm>
17 #include <cmath>
18 #include <cstdint>
19 #include <functional>
20 #include <vector>
21 
22 #include "Eigen/Core"
23 #include "Eigen/SparseCore"
24 #include "absl/log/check.h"
25 #include "absl/synchronization/blocking_counter.h"
26 #include "absl/time/time.h"
27 #include "ortools/base/logging.h"
28 #include "ortools/base/mathutil.h"
30 #include "ortools/base/timer.h"
31 
32 namespace operations_research::pdlp {
33 
34 using ::Eigen::VectorXd;
35 
36 Sharder::Sharder(const int64_t num_elements, const int num_shards,
37  ThreadPool* const thread_pool,
38  const std::function<int64_t(int64_t)>& element_mass)
39  : thread_pool_(thread_pool) {
40  CHECK_GE(num_elements, 0);
41  if (num_elements == 0) {
42  shard_starts_.push_back(0);
43  return;
44  }
45  CHECK_GE(num_shards, 1);
46  shard_starts_.reserve(
47  std::min(static_cast<int64_t>(num_shards), num_elements) + 1);
48  shard_masses_.reserve(
49  std::min(static_cast<int64_t>(num_shards), num_elements));
50  int64_t overall_mass = 0;
51  for (int64_t elem = 0; elem < num_elements; ++elem) {
52  overall_mass += element_mass(elem);
53  }
54  shard_starts_.push_back(0);
55  int64_t this_shard_mass = element_mass(0);
56  for (int64_t elem = 1; elem < num_elements; ++elem) {
57  int64_t this_elem_mass = element_mass(elem);
58  if (this_shard_mass + (this_elem_mass / 2) >= overall_mass / num_shards) {
59  // `elem` starts a new shard.
60  shard_masses_.push_back(this_shard_mass);
61  shard_starts_.push_back(elem);
62  this_shard_mass = this_elem_mass;
63  } else {
64  this_shard_mass += this_elem_mass;
65  }
66  }
67  shard_starts_.push_back(num_elements);
68  shard_masses_.push_back(this_shard_mass);
69  CHECK_EQ(NumShards(), shard_masses_.size());
70 }
71 
72 Sharder::Sharder(const int64_t num_elements, const int num_shards,
73  ThreadPool* const thread_pool)
74  : thread_pool_(thread_pool) {
75  CHECK_GE(num_elements, 0);
76  if (num_elements == 0) {
77  shard_starts_.push_back(0);
78  return;
79  }
80  CHECK_GE(num_shards, 1);
81  shard_starts_.reserve(
82  std::min(static_cast<int64_t>(num_shards), num_elements) + 1);
83  shard_masses_.reserve(
84  std::min(static_cast<int64_t>(num_shards), num_elements));
85  for (int shard = 0; shard < num_shards; ++shard) {
86  const int64_t this_shard_start = ((num_elements * shard) / num_shards);
87  const int64_t next_shard_start =
88  ((num_elements * (shard + 1)) / num_shards);
89  if (next_shard_start - this_shard_start > 0) {
90  shard_starts_.push_back(this_shard_start);
91  shard_masses_.push_back(next_shard_start - this_shard_start);
92  }
93  }
94  shard_starts_.push_back(num_elements);
95  CHECK_EQ(NumShards(), shard_masses_.size());
96 }
97 
98 Sharder::Sharder(const Sharder& other_sharder, const int64_t num_elements)
99  // The `std::max()` protects against `other_sharder.NumShards() == 0`, which
100  // will happen if `other_sharder` had `num_elements == 0`.
101  : Sharder(num_elements, std::max(1, other_sharder.NumShards()),
102  other_sharder.thread_pool_) {}
103 
105  const std::function<void(const Shard&)>& func) const {
106  if (thread_pool_) {
107  absl::BlockingCounter counter(NumShards());
108  VLOG(2) << "Starting ParallelForEachShard()";
109  for (int shard_num = 0; shard_num < NumShards(); ++shard_num) {
110  thread_pool_->Schedule([&, shard_num]() {
111  WallTimer timer;
112  if (VLOG_IS_ON(2)) {
113  timer.Start();
114  }
115  func(Shard(shard_num, this));
116  if (VLOG_IS_ON(2)) {
117  timer.Stop();
118  VLOG(2) << "Shard " << shard_num << " with " << ShardSize(shard_num)
119  << " elements and " << ShardMass(shard_num)
120  << " mass finished with "
121  << ShardMass(shard_num) /
122  std::max(int64_t{1}, absl::ToInt64Microseconds(
123  timer.GetDuration()))
124  << " mass/usec.";
125  }
126  counter.DecrementCount();
127  });
128  }
129  counter.Wait();
130  VLOG(2) << "Done ParallelForEachShard()";
131  } else {
132  for (int shard_num = 0; shard_num < NumShards(); ++shard_num) {
133  func(Shard(shard_num, this));
134  }
135  }
136 }
137 
139  const std::function<double(const Shard&)>& func) const {
140  VectorXd local_sums(NumShards());
141  ParallelForEachShard([&](const Sharder::Shard& shard) {
142  local_sums[shard.Index()] = func(shard);
143  });
144  return local_sums.sum();
145 }
146 
148  const std::function<bool(const Shard&)>& func) const {
149  // Recall `std::vector<bool>` is not thread-safe.
150  std::vector<int> local_result(NumShards());
151  ParallelForEachShard([&](const Sharder::Shard& shard) {
152  local_result[shard.Index()] = static_cast<int>(func(shard));
153  });
154  return std::all_of(local_result.begin(), local_result.end(),
155  [](const int v) { return static_cast<bool>(v); });
156 }
157 
159  const Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix,
160  const VectorXd& vector, const Sharder& sharder) {
161  CHECK_EQ(vector.size(), matrix.rows());
162  VectorXd answer(matrix.cols());
163  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
164  // NOTE: For very sparse columns, assignment to `shard(answer)` incurs a
165  // measurable overhead compared to using a constructor
166  // (i.e. `VectorXd temp = ...`). It is not clear why this is the case, nor
167  // how to avoid it.
168  shard(answer) = shard(matrix).transpose() * vector;
169  });
170  return answer;
171 }
172 
173 void SetZero(const Sharder& sharder, VectorXd& dest) {
174  dest.resize(sharder.NumElements());
175  sharder.ParallelForEachShard(
176  [&](const Sharder::Shard& shard) { shard(dest).setZero(); });
177 }
178 
179 VectorXd ZeroVector(const Sharder& sharder) {
180  VectorXd result(sharder.NumElements());
181  SetZero(sharder, result);
182  return result;
183 }
184 
185 VectorXd OnesVector(const Sharder& sharder) {
186  VectorXd result(sharder.NumElements());
187  sharder.ParallelForEachShard(
188  [&](const Sharder::Shard& shard) { shard(result).setOnes(); });
189  return result;
190 }
191 
192 void AddScaledVector(const double scale, const VectorXd& increment,
193  const Sharder& sharder, VectorXd& dest) {
194  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
195  shard(dest) += scale * shard(increment);
196  });
197 }
198 
199 void AssignVector(const VectorXd& vec, const Sharder& sharder, VectorXd& dest) {
200  dest.resize(vec.size());
201  sharder.ParallelForEachShard(
202  [&](const Sharder::Shard& shard) { shard(dest) = shard(vec); });
203 }
204 
205 VectorXd CloneVector(const VectorXd& vec, const Sharder& sharder) {
206  VectorXd dest;
207  AssignVector(vec, sharder, dest);
208  return dest;
209 }
210 
211 void CoefficientWiseProductInPlace(const VectorXd& scale,
212  const Sharder& sharder, VectorXd& dest) {
213  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
214  shard(dest) = shard(dest).cwiseProduct(shard(scale));
215  });
216 }
217 
218 void CoefficientWiseQuotientInPlace(const VectorXd& scale,
219  const Sharder& sharder, VectorXd& dest) {
220  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
221  shard(dest) = shard(dest).cwiseQuotient(shard(scale));
222  });
223 }
224 
225 double Dot(const VectorXd& v1, const VectorXd& v2, const Sharder& sharder) {
226  return sharder.ParallelSumOverShards(
227  [&](const Sharder::Shard& shard) { return shard(v1).dot(shard(v2)); });
228 }
229 
230 double LInfNorm(const VectorXd& vector, const Sharder& sharder) {
231  VectorXd local_max(sharder.NumShards());
232  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
233  local_max[shard.Index()] = shard(vector).lpNorm<Eigen::Infinity>();
234  });
235  return local_max.lpNorm<Eigen::Infinity>();
236 }
237 
238 double L1Norm(const VectorXd& vector, const Sharder& sharder) {
239  return sharder.ParallelSumOverShards(
240  [&](const Sharder::Shard& shard) { return shard(vector).lpNorm<1>(); });
241 }
242 
243 double SquaredNorm(const VectorXd& vector, const Sharder& sharder) {
244  return sharder.ParallelSumOverShards(
245  [&](const Sharder::Shard& shard) { return shard(vector).squaredNorm(); });
246 }
247 
248 double Norm(const VectorXd& vector, const Sharder& sharder) {
249  return std::sqrt(SquaredNorm(vector, sharder));
250 }
251 
252 double SquaredDistance(const VectorXd& vector1, const VectorXd& vector2,
253  const Sharder& sharder) {
254  return sharder.ParallelSumOverShards([&](const Sharder::Shard& shard) {
255  return (shard(vector1) - shard(vector2)).squaredNorm();
256  });
257 }
258 
259 double Distance(const VectorXd& vector1, const VectorXd& vector2,
260  const Sharder& sharder) {
261  return std::sqrt(SquaredDistance(vector1, vector2, sharder));
262 }
263 
264 double ScaledLInfNorm(const VectorXd& vector, const VectorXd& scale,
265  const Sharder& sharder) {
266  VectorXd local_max(sharder.NumShards());
267  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
268  local_max[shard.Index()] =
269  shard(vector).cwiseProduct(shard(scale)).lpNorm<Eigen::Infinity>();
270  });
271  return local_max.lpNorm<Eigen::Infinity>();
272 }
273 
274 double ScaledSquaredNorm(const VectorXd& vector, const VectorXd& scale,
275  const Sharder& sharder) {
276  return sharder.ParallelSumOverShards([&](const Sharder::Shard& shard) {
277  return shard(vector).cwiseProduct(shard(scale)).squaredNorm();
278  });
279 }
280 
281 double ScaledNorm(const VectorXd& vector, const VectorXd& scale,
282  const Sharder& sharder) {
283  return std::sqrt(ScaledSquaredNorm(vector, scale, sharder));
284 }
285 
287  const Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix,
288  const VectorXd& row_scaling_vec, const VectorXd& col_scaling_vec,
289  const Sharder& sharder) {
290  CHECK_EQ(matrix.cols(), col_scaling_vec.size());
291  CHECK_EQ(matrix.rows(), row_scaling_vec.size());
292  VectorXd answer(matrix.cols());
293  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
294  auto matrix_shard = shard(matrix);
295  auto col_scaling_shard = shard(col_scaling_vec);
296  for (int64_t col_num = 0; col_num < shard(matrix).outerSize(); ++col_num) {
297  double max = 0.0;
298  for (decltype(matrix_shard)::InnerIterator it(matrix_shard, col_num); it;
299  ++it) {
300  max = std::max(max, std::abs(it.value() * row_scaling_vec[it.row()]));
301  }
302  shard(answer)[col_num] = max * std::abs(col_scaling_shard[col_num]);
303  }
304  });
305  return answer;
306 }
307 
309  const Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t>& matrix,
310  const VectorXd& row_scaling_vec, const VectorXd& col_scaling_vec,
311  const Sharder& sharder) {
312  CHECK_EQ(matrix.cols(), col_scaling_vec.size());
313  CHECK_EQ(matrix.rows(), row_scaling_vec.size());
314  VectorXd answer(matrix.cols());
315  sharder.ParallelForEachShard([&](const Sharder::Shard& shard) {
316  auto matrix_shard = shard(matrix);
317  auto col_scaling_shard = shard(col_scaling_vec);
318  for (int64_t col_num = 0; col_num < shard(matrix).outerSize(); ++col_num) {
319  double sum_of_squares = 0.0;
320  for (decltype(matrix_shard)::InnerIterator it(matrix_shard, col_num); it;
321  ++it) {
322  sum_of_squares +=
323  MathUtil::Square(it.value() * row_scaling_vec[it.row()]);
324  }
325  shard(answer)[col_num] =
326  std::sqrt(sum_of_squares) * std::abs(col_scaling_shard[col_num]);
327  }
328  });
329  return answer;
330 }
331 
332 } // namespace operations_research::pdlp
int64_t max
Definition: alldiff_cst.cc:140
int64_t min
Definition: alldiff_cst.cc:139
void Start()
Definition: timer.h:31
void Stop()
Definition: timer.h:39
absl::Duration GetDuration() const
Definition: timer.h:48
static T Square(const T x)
Definition: mathutil.h:101
void Schedule(std::function< void()> closure)
Definition: threadpool.cc:77
Sharder(int64_t num_elements, int num_shards, ThreadPool *thread_pool, const std::function< int64_t(int64_t)> &element_mass)
Definition: sharder.cc:36
double ParallelSumOverShards(const std::function< double(const Shard &)> &func) const
Definition: sharder.cc:138
void ParallelForEachShard(const std::function< void(const Shard &)> &func) const
Definition: sharder.cc:104
bool ParallelTrueForAllShards(const std::function< bool(const Shard &)> &func) const
Definition: sharder.cc:147
int64_t ShardSize(int shard) const
Definition: sharder.h:186
int64_t ShardMass(int shard) const
Definition: sharder.h:198
double SquaredNorm(const VectorXd &vector, const Sharder &sharder)
Definition: sharder.cc:243
void SetZero(const Sharder &sharder, VectorXd &dest)
Definition: sharder.cc:173
double ScaledNorm(const VectorXd &vector, const VectorXd &scale, const Sharder &sharder)
Definition: sharder.cc:281
double Dot(const VectorXd &v1, const VectorXd &v2, const Sharder &sharder)
Definition: sharder.cc:225
double SquaredDistance(const VectorXd &vector1, const VectorXd &vector2, const Sharder &sharder)
Definition: sharder.cc:252
double LInfNorm(const VectorXd &vector, const Sharder &sharder)
Definition: sharder.cc:230
double Distance(const VectorXd &vector1, const VectorXd &vector2, const Sharder &sharder)
Definition: sharder.cc:259
VectorXd TransposedMatrixVectorProduct(const Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix, const VectorXd &vector, const Sharder &sharder)
Definition: sharder.cc:158
double ScaledLInfNorm(const VectorXd &vector, const VectorXd &scale, const Sharder &sharder)
Definition: sharder.cc:264
double ScaledSquaredNorm(const VectorXd &vector, const VectorXd &scale, const Sharder &sharder)
Definition: sharder.cc:274
VectorXd ScaledColLInfNorm(const Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix, const VectorXd &row_scaling_vec, const VectorXd &col_scaling_vec, const Sharder &sharder)
Definition: sharder.cc:286
void AddScaledVector(const double scale, const VectorXd &increment, const Sharder &sharder, VectorXd &dest)
Definition: sharder.cc:192
void CoefficientWiseProductInPlace(const VectorXd &scale, const Sharder &sharder, VectorXd &dest)
Definition: sharder.cc:211
void CoefficientWiseQuotientInPlace(const VectorXd &scale, const Sharder &sharder, VectorXd &dest)
Definition: sharder.cc:218
VectorXd ScaledColL2Norm(const Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix, const VectorXd &row_scaling_vec, const VectorXd &col_scaling_vec, const Sharder &sharder)
Definition: sharder.cc:308
double L1Norm(const VectorXd &vector, const Sharder &sharder)
Definition: sharder.cc:238
VectorXd CloneVector(const VectorXd &vec, const Sharder &sharder)
Definition: sharder.cc:205
double Norm(const VectorXd &vector, const Sharder &sharder)
Definition: sharder.cc:248
VectorXd ZeroVector(const Sharder &sharder)
Definition: sharder.cc:179
void AssignVector(const VectorXd &vec, const Sharder &sharder, VectorXd &dest)
Definition: sharder.cc:199
VectorXd OnesVector(const Sharder &sharder)
Definition: sharder.cc:185
#define VLOG(verboselevel)
Definition: vlog.h:39
#define VLOG_IS_ON(verboselevel)
Definition: vlog_is_on.h:47