24 #include "Eigen/SparseCore"
25 #include "absl/random/distributions.h"
26 #include "gmock/gmock.h"
27 #include "gtest/gtest.h"
35 using ::Eigen::DiagonalMatrix;
36 using ::Eigen::VectorXd;
37 using ::testing::DoubleNear;
38 using ::testing::ElementsAre;
39 using ::testing::Test;
40 using Shard = Sharder::Shard;
46 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> TestSparseMatrix() {
47 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat(3, 4);
48 mat.coeffRef(0, 0) = 7;
49 mat.coeffRef(0, 1) = -0.5;
50 mat.coeffRef(1, 0) = 1;
51 mat.coeffRef(1, 2) = 3;
52 mat.coeffRef(1, 3) = 2;
53 mat.coeffRef(2, 0) = -1;
54 mat.coeffRef(2, 3) = 5;
61 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> LargeSparseMatrix(
64 std::mt19937 rand(48709241);
65 std::vector<Eigen::Triplet<double, int64_t>> triplets;
66 for (int64_t
col = 0;
col < size; ++
col) {
69 row += absl::Uniform(rand, 1,
col + 2);
71 double value = absl::Uniform(rand, 1, 10);
76 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat(size, size);
77 mat.setFromTriplets(triplets.begin(), triplets.end());
83 void VerifySharder(
const Sharder& sharder,
const int target_num_shards,
84 const std::vector<int64_t>& element_masses) {
85 int64_t num_elements = element_masses.size();
86 int num_shards = sharder.NumShards();
87 ASSERT_EQ(sharder.NumElements(), num_elements);
88 ASSERT_GE(num_elements, 1);
89 ASSERT_GE(num_shards, 1);
90 int64_t elements_so_far = 0;
91 for (
int shard = 0; shard < num_shards; ++shard) {
92 int64_t shard_start = sharder.ShardStart(shard);
93 EXPECT_EQ(shard_start, elements_so_far) <<
" in shard: " << shard;
94 int64_t shard_mass = 0;
95 EXPECT_GE(sharder.ShardSize(shard), 1) <<
" in shard: " << shard;
96 EXPECT_GE(sharder.ShardMass(shard), 1) <<
" in shard: " << shard;
97 for (int64_t i = 0; i < sharder.ShardSize(shard); ++i) {
98 shard_mass += element_masses[shard_start + i];
100 EXPECT_EQ(shard_mass, sharder.ShardMass(shard)) <<
" in shard: " << shard;
101 elements_so_far += sharder.ShardSize(shard);
103 EXPECT_EQ(elements_so_far, num_elements);
105 EXPECT_LE(num_shards, 2 * target_num_shards);
106 ASSERT_GE(target_num_shards, 1);
107 const int64_t overall_mass =
108 std::accumulate(element_masses.begin(), element_masses.end(), int64_t{0});
109 const int64_t max_element_mass =
110 *std::max_element(element_masses.begin(), element_masses.end());
111 const int64_t upper_mass_limit =
std::max(
115 const int64_t lower_mass_limit =
116 overall_mass / target_num_shards -
118 for (
int shard = 0; shard < sharder.NumShards(); ++shard) {
119 EXPECT_LE(sharder.ShardMass(shard), upper_mass_limit)
120 <<
" in shard: " << shard;
121 if (shard + 1 < sharder.NumShards()) {
122 EXPECT_GE(sharder.ShardMass(shard), lower_mass_limit)
123 <<
" in shard: " << shard;
128 TEST(SharderTest, SharderFromMatrix) {
129 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat =
131 Sharder sharder(mat, 2,
nullptr);
132 VerifySharder(sharder, 2, {4, 2, 2, 3});
135 TEST(SharderTest, UniformSharder) {
136 Sharder sharder(10, 3,
nullptr);
137 VerifySharder(sharder, 3, {1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
140 TEST(SharderTest, UniformSharderFromOtherSharder) {
141 Sharder other_sharder(5, 3,
nullptr);
142 Sharder sharder(other_sharder, 10);
143 VerifySharder(sharder, other_sharder.NumShards(),
144 {1, 1, 1, 1, 1, 1, 1, 1, 1, 1});
147 TEST(SharderTest, UniformSharderExcessiveShards) {
148 Sharder sharder(5, 7,
nullptr);
149 EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0, 1, 2, 3, 4, 5));
150 VerifySharder(sharder, 7, {1, 1, 1, 1, 1});
153 TEST(SharderTest, UniformSharderHugeNumShards) {
154 Sharder sharder(5, 1'000'000'000,
nullptr);
155 EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0, 1, 2, 3, 4, 5));
156 VerifySharder(sharder, 7, {1, 1, 1, 1, 1});
159 TEST(SharderTest, UniformSharderOneShard) {
160 Sharder sharder(5, 1,
nullptr);
161 EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0, 5));
162 VerifySharder(sharder, 1, {1, 1, 1, 1, 1});
165 TEST(SharderTest, UniformSharderOneElementVector) {
166 Sharder sharder(1, 5,
nullptr);
167 EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0, 1));
168 VerifySharder(sharder, 5, {1});
171 TEST(SharderTest, UniformSharderZeroElementVector) {
172 Sharder sharder(0, 3,
nullptr);
173 EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0));
174 EXPECT_EQ(sharder.NumShards(), 0);
175 EXPECT_EQ(sharder.NumElements(), 0);
176 sharder.ParallelForEachShard([](
const Shard& ) {
177 LOG(FATAL) <<
"There are no shards so this shouldn't be called.";
181 TEST(SharderTest, UniformSharderFromOtherZeroElementSharder) {
182 Sharder empty_sharder(0, 3,
nullptr);
183 EXPECT_THAT(empty_sharder.ShardStartsForTesting(), ElementsAre(0));
184 EXPECT_EQ(empty_sharder.NumShards(), 0);
185 EXPECT_EQ(empty_sharder.NumElements(), 0);
186 Sharder sharder(empty_sharder, 5);
187 EXPECT_THAT(sharder.ShardStartsForTesting(), ElementsAre(0, 5));
188 VerifySharder(sharder, 1, {1, 1, 1, 1, 1});
191 TEST(ParallelSumOverShards, SmallExample) {
194 Sharder sharder(vec.size(), 2,
nullptr);
195 const double sum = sharder.ParallelSumOverShards(
196 [&vec](
const Shard& shard) {
return shard(vec).sum(); });
200 TEST(ParallelSumOverShards, SmallExampleUsingVectorBlock) {
203 auto vec_block = vec.segment(1, 2);
204 Sharder sharder(vec_block.size(), 2,
nullptr);
205 const double sum = sharder.ParallelSumOverShards(
206 [&vec_block](
const Shard& shard) {
return shard(vec_block).sum(); });
210 TEST(ParallelSumOverShards, SmallExampleUsingConstVectorBlock) {
213 const VectorXd& const_vec = vec;
214 auto vec_block = const_vec.segment(1, 2);
215 Sharder sharder(vec_block.size(), 2,
nullptr);
216 const double sum = sharder.ParallelSumOverShards(
217 [&vec_block](
const Shard& shard) {
return shard(vec_block).sum(); });
221 TEST(ParallelSumOverShards, SmallExampleUsingDiagonalMatrix) {
222 DiagonalMatrix<double, Eigen::Dynamic> diag{{1, 2, 3}};
223 Sharder sharder(diag.cols(), 2,
nullptr);
224 const double sum = sharder.ParallelSumOverShards(
225 [&diag](
const Shard& shard) {
return shard(diag).diagonal().sum(); });
229 TEST(ParallelSumOverShards, SmallExampleUsingDiagonalMatrixMultiplication) {
230 DiagonalMatrix<double, Eigen::Dynamic> diag{{1, 2, 3}};
231 VectorXd vec{{1, 1, 1}};
233 Sharder sharder(diag.cols(), 2,
nullptr);
234 sharder.ParallelForEachShard(
235 [&](
const Shard& shard) { shard(answer) = shard(diag) * shard(vec); });
236 EXPECT_THAT(answer, ElementsAre(1.0, 2.0, 3.0));
239 TEST(ParallelTrueForAllShards, SmallTrueExample) {
242 Sharder sharder(vec.size(), 2,
nullptr);
243 const bool result = sharder.ParallelTrueForAllShards(
244 [&vec](
const Shard& shard) {
return (shard(vec).array() > 0.0).all(); });
248 TEST(ParallelTrueForAllShards, SmallFalseExample) {
251 Sharder sharder(vec.size(), 2,
nullptr);
252 const bool result = sharder.ParallelTrueForAllShards(
253 [&vec](
const Shard& shard) {
return (shard(vec).array() < 2.5).all(); });
254 EXPECT_FALSE(result);
257 TEST(MatrixVectorProductTest, SmallExample) {
258 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat =
260 Sharder sharder(mat, 3,
nullptr);
264 EXPECT_THAT(ans, ElementsAre(6.0, -0.5, 6.0, 19));
267 TEST(SetZeroTest, SmallExample) {
268 Sharder sharder(3, 2,
nullptr);
272 EXPECT_THAT(vec, ElementsAre(0.0, 0.0, 0.0));
275 TEST(ZeroVectorTest, SmallExample) {
276 Sharder sharder(3, 2,
nullptr);
277 EXPECT_THAT(
ZeroVector(sharder), ElementsAre(0.0, 0.0, 0.0));
280 TEST(OnesVectorTest, SmallExample) {
281 Sharder sharder(3, 2,
nullptr);
282 EXPECT_THAT(
OnesVector(sharder), ElementsAre(1.0, 1.0, 1.0));
285 TEST(AddScaledVectorTest, SmallExample) {
286 Sharder sharder(3, 2,
nullptr);
287 VectorXd vec1(3), vec2(3);
291 EXPECT_THAT(vec1, ElementsAre(6, 19, 26));
294 TEST(AssignVectorTest, SmallExample) {
295 Sharder sharder(3, 2,
nullptr);
296 VectorXd vec1, vec2(3);
299 EXPECT_THAT(vec1, ElementsAre(1, 7, 3));
302 TEST(CloneVectorTest, SmallExample) {
303 Sharder sharder(3, 2,
nullptr);
306 EXPECT_THAT(
CloneVector(vec, sharder), ElementsAre(1, 7, 3));
309 TEST(CoefficientWiseProductInPlaceTest, SmallExample) {
310 Sharder sharder(3, 2,
nullptr);
311 VectorXd vec1(3), vec2(3);
316 EXPECT_THAT(vec1, ElementsAre(4, 10, 60));
319 TEST(CoefficientWiseQuotientInPlaceTest, SmallExample) {
320 Sharder sharder(3, 2,
nullptr);
321 VectorXd vec1(3), vec2(3);
326 EXPECT_THAT(vec1, ElementsAre(4, 3, 4));
329 TEST(DotTest, SmallExample) {
330 Sharder sharder(3, 2,
nullptr);
331 VectorXd vec1(3), vec2(3);
334 double ans =
Dot(vec1, vec2, sharder);
335 EXPECT_THAT(ans, DoubleNear(4 + 10 + 18, 1.0e-13));
338 TEST(LInfNormTest, SmallExample) {
339 Sharder sharder(3, 2,
nullptr);
342 double ans =
LInfNorm(vec, sharder);
346 TEST(LInfNormTest, EmptyExample) {
347 Sharder sharder(0, 2,
nullptr);
349 double ans =
LInfNorm(vec, sharder);
353 TEST(L1NormTest, SmallExample) {
354 Sharder sharder(3, 2,
nullptr);
357 double ans =
L1Norm(vec, sharder);
361 TEST(L1NormTest, EmptyExample) {
362 Sharder sharder(0, 2,
nullptr);
364 double ans =
L1Norm(vec, sharder);
368 TEST(SquaredNormTest, SmallExample) {
369 Sharder sharder(3, 2,
nullptr);
373 EXPECT_THAT(ans, DoubleNear(1 + 4 + 9, 1.0e-13));
376 TEST(NormTest, SmallExample) {
377 Sharder sharder(3, 2,
nullptr);
380 double ans =
Norm(vec, sharder);
381 EXPECT_THAT(ans, DoubleNear(std::sqrt(1 + 4 + 9), 1.0e-13));
384 TEST(SquaredDistanceTest, SmallExample) {
385 Sharder sharder(3, 2,
nullptr);
391 EXPECT_THAT(ans, DoubleNear(5, 1.0e-13));
394 TEST(DistanceTest, SmallExample) {
395 Sharder sharder(3, 2,
nullptr);
400 double ans =
Distance(vec1, vec2, sharder);
401 EXPECT_THAT(ans, DoubleNear(std::sqrt(5), 1.0e-13));
404 TEST(ScaledLInfNormTest, SmallExample) {
405 Sharder sharder(3, 2,
nullptr);
414 TEST(ScaledSquaredNormTest, SmallExample) {
415 Sharder sharder(3, 2,
nullptr);
424 TEST(ScaledNormTest, SmallExample) {
425 Sharder sharder(3, 2,
nullptr);
431 EXPECT_EQ(ans, std::sqrt(169));
435 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat =
437 Sharder sharder(mat, 3,
nullptr);
438 VectorXd row_scaling_vec(3);
439 VectorXd col_scaling_vec(4);
440 row_scaling_vec << 1, -2, 1;
441 col_scaling_vec << 1, 2, -1, -1;
444 EXPECT_THAT(answer, ElementsAre(7, 1, 6, 5));
448 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat =
450 Sharder sharder(mat, 3,
nullptr);
451 VectorXd row_scaling_vec(3);
452 VectorXd col_scaling_vec(4);
453 row_scaling_vec << 1, -2, 1;
454 col_scaling_vec << 1, 2, -1, -1;
457 EXPECT_THAT(answer, ElementsAre(std::sqrt(54), 1.0, 6.0, std::sqrt(41)));
460 class VariousSizesTest :
public testing::TestWithParam<int64_t> {};
462 TEST_P(VariousSizesTest, LargeMatVec) {
463 const int64_t size = GetParam();
464 Eigen::SparseMatrix<double, Eigen::ColMajor, int64_t> mat =
465 LargeSparseMatrix(size);
466 const int num_threads = 5;
467 const int shards_per_thread = 3;
468 ThreadPool pool(
"MatrixVectorProductTest", num_threads);
470 Sharder sharder(mat, shards_per_thread * num_threads, &pool);
471 VectorXd rhs = VectorXd::Random(size);
472 VectorXd direct = mat.transpose() * rhs;
474 EXPECT_LE((direct - threaded).norm(), 1.0e-8);
477 TEST_P(VariousSizesTest, LargeVectors) {
478 const int64_t size = GetParam();
479 const int num_threads = 5;
480 ThreadPool pool(
"SquaredNormTest", num_threads);
482 Sharder sharder(size, num_threads, &pool);
483 VectorXd vec = VectorXd::Random(size);
484 const double direct = vec.squaredNorm();
486 EXPECT_THAT(threaded, DoubleNear(direct, size * 1.0e-14));
489 INSTANTIATE_TEST_SUITE_P(VariousSizesTestInstantiation, VariousSizesTest,
490 testing::Values(10, 1000, 100 * 1000));
static IntegralType CeilOfRatio(IntegralType numerator, IntegralType denominator)
double SquaredNorm(const VectorXd &vector, const Sharder &sharder)
void SetZero(const Sharder &sharder, VectorXd &dest)
double ScaledNorm(const VectorXd &vector, const VectorXd &scale, const Sharder &sharder)
double Dot(const VectorXd &v1, const VectorXd &v2, const Sharder &sharder)
double SquaredDistance(const VectorXd &vector1, const VectorXd &vector2, const Sharder &sharder)
double LInfNorm(const VectorXd &vector, const Sharder &sharder)
double Distance(const VectorXd &vector1, const VectorXd &vector2, const Sharder &sharder)
VectorXd TransposedMatrixVectorProduct(const Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix, const VectorXd &vector, const Sharder &sharder)
double ScaledLInfNorm(const VectorXd &vector, const VectorXd &scale, const Sharder &sharder)
double ScaledSquaredNorm(const VectorXd &vector, const VectorXd &scale, const Sharder &sharder)
VectorXd ScaledColLInfNorm(const Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix, const VectorXd &row_scaling_vec, const VectorXd &col_scaling_vec, const Sharder &sharder)
void AddScaledVector(const double scale, const VectorXd &increment, const Sharder &sharder, VectorXd &dest)
void CoefficientWiseProductInPlace(const VectorXd &scale, const Sharder &sharder, VectorXd &dest)
void CoefficientWiseQuotientInPlace(const VectorXd &scale, const Sharder &sharder, VectorXd &dest)
VectorXd ScaledColL2Norm(const Eigen::SparseMatrix< double, Eigen::ColMajor, int64_t > &matrix, const VectorXd &row_scaling_vec, const VectorXd &col_scaling_vec, const Sharder &sharder)
double L1Norm(const VectorXd &vector, const Sharder &sharder)
VectorXd CloneVector(const VectorXd &vec, const Sharder &sharder)
double Norm(const VectorXd &vector, const Sharder &sharder)
VectorXd ZeroVector(const Sharder &sharder)
void AssignVector(const VectorXd &vec, const Sharder &sharder, VectorXd &dest)
VectorXd OnesVector(const Sharder &sharder)
TEST(LinearAssignmentTest, NullMatrix)