Skip to content

Commit 10f8b32

Browse files
authored
Add tests and fixes to sp sparse matrix (#819)
1 parent e60d694 commit 10f8b32

File tree

9 files changed

+262
-17
lines changed

9 files changed

+262
-17
lines changed

include/dr/detail/index.hpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ template <std::integral T = std::size_t> class index {
7272
}
7373

7474
constexpr bool operator==(const index &) const noexcept = default;
75+
constexpr bool operator<(const index &other) const noexcept
76+
requires(std::totally_ordered<T>)
77+
{
78+
if (first < other.first) {
79+
return true;
80+
}
81+
if (first == other.first && second < other.second) {
82+
return true;
83+
}
84+
return false;
85+
}
7586

7687
template <std::size_t Index>
7788
constexpr T get() const noexcept

include/dr/sp/algorithms/matrix/gemv.hpp

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ namespace dr::sp {
1919
template <dr::distributed_range C, typename T, typename I,
2020
dr::distributed_range B>
2121
void flat_gemv(C &&c, dr::sp::sparse_matrix<T, I> &a, B &&b) {
22-
assert(c.size() == b.size());
22+
assert(a.shape()[0] == c.size());
2323
assert(a.shape()[1] == b.size());
2424
assert(a.grid_shape()[0] == c.segments().size());
2525
assert(a.grid_shape()[1] == 1);
@@ -79,7 +79,7 @@ template <dr::distributed_range C, typename T, typename I,
7979
dr::distributed_range B>
8080
void gemv(C &&c, dr::sp::sparse_matrix<T, I> &a, B &&b,
8181
sp::duplicated_vector<rng::range_value_t<B>> &scratch) {
82-
assert(c.size() == b.size());
82+
assert(a.shape()[0] == c.size());
8383
assert(a.shape()[1] == b.size());
8484
assert(a.grid_shape()[0] == c.segments().size());
8585
assert(a.grid_shape()[1] == 1);
@@ -104,6 +104,7 @@ void gemv(C &&c, dr::sp::sparse_matrix<T, I> &a, B &&b,
104104
dr::ranges::local(b_duplicated.local_vector(a_tile.rank()).begin());
105105
auto c_iter = dr::ranges::local(c.segments()[i].begin());
106106

107+
assert(c.segments()[i].size() == a_tile.shape()[0]);
107108
auto &&q = __detail::queue(a_tile.rank());
108109

109110
auto event = __detail::local_gemv(q, a_tile, b_iter, c_iter,

include/dr/sp/containers/matrix_partition.hpp

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -86,7 +86,12 @@ class block_cyclic final : public matrix_partition {
8686

8787
dr::index<> tile_shape_;
8888
dr::index<> grid_shape_;
89-
}; // namespace dr::sp
89+
};
90+
91+
inline auto row_cyclic() {
92+
return block_cyclic({dr::sp::tile::div, dr::sp::tile::div},
93+
{dr::sp::nprocs(), 1});
94+
}
9095

9196
inline std::vector<block_cyclic> partition_matmul(std::size_t m, std::size_t n,
9297
std::size_t k) {

include/dr/sp/containers/sparse_matrix.hpp

Lines changed: 19 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,13 @@ class distributed_range_accessor {
4646
constexpr distributed_range_accessor(Segments segments, size_type segment_id,
4747
size_type idx) noexcept
4848
: segments_(rng::views::all(std::forward<Segments>(segments))),
49-
segment_id_(segment_id), idx_(idx) {}
49+
segment_id_(segment_id), idx_(idx) {
50+
while (idx_ >= rng::size((*(segments_.begin() + segment_id_))) &&
51+
segment_id_ < rng::size(segments_)) {
52+
segment_id_++;
53+
idx_ = 0;
54+
}
55+
}
5056

5157
constexpr distributed_range_accessor &
5258
operator+=(difference_type offset) noexcept {
@@ -59,7 +65,8 @@ class distributed_range_accessor {
5965
idx_ += current_offset;
6066
offset -= current_offset;
6167

62-
if (idx_ >= rng::size((*(segments_.begin() + segment_id_)))) {
68+
while (idx_ >= rng::size((*(segments_.begin() + segment_id_))) &&
69+
segment_id_ < rng::size(segments_)) {
6370
segment_id_++;
6471
idx_ = 0;
6572
}
@@ -71,14 +78,16 @@ class distributed_range_accessor {
7178

7279
difference_type new_idx = difference_type(idx_) - current_offset;
7380

74-
if (new_idx < 0) {
81+
while (new_idx < 0 && segment_id_ > 0) {
7582
segment_id_--;
7683
new_idx = rng::size(*(segments_.begin() + segment_id_)) - 1;
7784
}
7885

7986
idx_ = new_idx;
87+
offset += current_offset;
8088
}
8189

90+
assert(offset == 0);
8291
return *this;
8392
}
8493

@@ -151,12 +160,12 @@ template <typename T, std::integral I = std::int64_t> class sparse_matrix {
151160
distributed_sparse_matrix_iterator<std::span<segment_type> &&>;
152161

153162
sparse_matrix(key_type shape)
154-
: shape_(shape), partition_(new dr::sp::block_cyclic()) {
163+
: shape_(shape), partition_(default_partition_()) {
155164
init_();
156165
}
157166

158167
sparse_matrix(key_type shape, double density)
159-
: shape_(shape), partition_(new dr::sp::block_cyclic()) {
168+
: shape_(shape), partition_(default_partition_()) {
160169
init_random_(density);
161170
}
162171

@@ -393,6 +402,11 @@ template <typename T, std::integral I = std::int64_t> class sparse_matrix {
393402
segments_ = generate_segments_();
394403
}
395404

405+
std::unique_ptr<dr::sp::matrix_partition> default_partition_() {
406+
auto ptr = dr::sp::row_cyclic();
407+
return std::make_unique<dr::sp::block_cyclic>(ptr);
408+
}
409+
396410
private:
397411
key_type shape_;
398412
key_type grid_shape_;

include/dr/sp/util/matrix_io.hpp

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -279,11 +279,7 @@ auto mmread(std::string file_path, const matrix_partition &partition,
279279

280280
template <typename T, typename I = std::size_t>
281281
auto mmread(std::string file_path, bool one_indexed = true) {
282-
return mmread<T, I>(
283-
file_path,
284-
dr::sp::block_cyclic({dr::sp::tile::div, dr::sp::tile::div},
285-
{dr::sp::nprocs(), 1}),
286-
one_indexed);
282+
return mmread<T, I>(file_path, dr::sp::row_cyclic(), one_indexed);
287283
}
288284

289285
} // namespace dr::sp

include/dr/sp/views/csr_matrix_view.hpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,11 @@ class csr_matrix_view
156156
std::size_t rank() const { return rank_; }
157157

158158
iterator begin() const {
159-
return iterator(values_, rowptr_, colind_, 0, 0, shape()[1], idx_offset_);
159+
return iterator(values_, rowptr_, colind_, 0, 0, shape()[0], idx_offset_);
160160
}
161161

162162
iterator end() const {
163-
return iterator(values_, rowptr_, colind_, nnz_, shape()[1], shape()[1],
163+
return iterator(values_, rowptr_, colind_, nnz_, shape()[0], shape()[0],
164164
idx_offset_);
165165
}
166166

test/gtest/sp/CMakeLists.txt

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,12 +14,12 @@ add_executable(
1414
../common/reduce.cpp ../common/sort.cpp ../common/subrange.cpp
1515
../common/take.cpp ../common/transform.cpp ../common/transform_view.cpp
1616
../common/zip.cpp ../common/zip_local.cpp containers.cpp algorithms.cpp
17-
copy.cpp detail.cpp fill.cpp gemv.cpp transform.cpp)
17+
copy.cpp detail.cpp fill.cpp gemv.cpp sparse.cpp transform.cpp)
1818

1919
add_executable(sp-tests-3 sp-tests.cpp containers-3.cpp copy-3.cpp)
2020

2121
# skeleton for rapid builds of individual tests, feel free to change this
22-
add_executable(sp-quick-test sp-tests.cpp ../common/equal.cpp)
22+
add_executable(sp-quick-test sp-tests.cpp sparse.cpp)
2323
target_compile_definitions(sp-quick-test PRIVATE QUICK_TEST)
2424

2525
foreach(test-exec IN ITEMS sp-tests sp-tests-3 sp-quick-test)

test/gtest/sp/gemv.cpp

Lines changed: 149 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,3 +33,152 @@ TEST(SparseMatrix, Gemv) {
3333
EXPECT_TRUE(fp_equal(c_ref, c_local))
3434
<< fmt::format("Reference:\n {}\nActual:\n {}\n", c_ref, c_local);
3535
}
36+
37+
TEST(SparseMatrix, EmptyGemv) {
38+
std::size_t m = 100;
39+
std::size_t k = 100;
40+
using T = float;
41+
using I = int;
42+
43+
dr::sp::__detail::coo_matrix<T, I> base;
44+
auto csr = dr::sp::__detail::convert_to_csr(base, {m, k}, base.size(),
45+
std::allocator<T>{});
46+
dr::sp::sparse_matrix<T, I> a =
47+
dr::sp::create_distributed(csr, dr::sp::row_cyclic());
48+
49+
dr::sp::distributed_vector<T> b(k, 1.f);
50+
dr::sp::distributed_vector<T> c(m, 0.f);
51+
52+
dr::sp::gemv(c, a, b);
53+
54+
std::vector<T> c_local(m);
55+
56+
dr::sp::copy(c.begin(), c.end(), c_local.begin());
57+
58+
std::vector<T> c_ref(m, 0.f);
59+
60+
EXPECT_TRUE(fp_equal(c_ref, c_local))
61+
<< fmt::format("Reference:\n {}\nActual:\n {}\n", c_ref, c_local);
62+
}
63+
64+
TEST(SparseMatrix, ZeroVector) {
65+
std::size_t m = 100;
66+
std::size_t k = 100;
67+
using T = float;
68+
using I = int;
69+
std::vector<std::pair<std::pair<I, I>, T>> base;
70+
for (int i = 0; i < m; i++) {
71+
for (int j = 0; j < k; j++) {
72+
base.push_back({{i, j}, i + j});
73+
}
74+
}
75+
76+
auto csr = dr::sp::__detail::convert_to_csr(base, {m, k}, base.size(),
77+
std::allocator<T>{});
78+
dr::sp::sparse_matrix<T, I> a =
79+
dr::sp::create_distributed(csr, dr::sp::row_cyclic());
80+
81+
dr::sp::distributed_vector<T> b(k, 0.f);
82+
dr::sp::distributed_vector<T> c(m, 0.f);
83+
84+
dr::sp::gemv(c, a, b);
85+
86+
std::vector<T> c_local(m);
87+
88+
dr::sp::copy(c.begin(), c.end(), c_local.begin());
89+
90+
std::vector<T> c_ref(m, 0.f);
91+
92+
EXPECT_TRUE(fp_equal(c_ref, c_local))
93+
<< fmt::format("Reference:\n {}\nActual:\n {}\n", c_ref, c_local);
94+
}
95+
96+
TEST(SparseMatrix, NotSquareMatrix) {
97+
std::size_t m = 10;
98+
std::size_t k = 1000;
99+
100+
dr::sp::sparse_matrix<float> a(
101+
{m, k}, 0.1f,
102+
dr::sp::block_cyclic({dr::sp::tile::div, dr::sp::tile::div},
103+
{dr::sp::nprocs(), 1}));
104+
105+
dr::sp::distributed_vector<float> b(k, 1.f);
106+
dr::sp::distributed_vector<float> c(m, 0.f);
107+
108+
dr::sp::gemv(c, a, b);
109+
110+
std::vector<float> c_local(m);
111+
112+
dr::sp::copy(c.begin(), c.end(), c_local.begin());
113+
114+
std::vector<float> c_ref(m, 0.f);
115+
116+
for (auto &&[index, v] : a) {
117+
auto &&[i, k] = index;
118+
119+
c_ref[i] += v;
120+
}
121+
122+
EXPECT_TRUE(fp_equal(c_ref, c_local))
123+
<< fmt::format("Reference:\n {}\nActual:\n {}\n", c_ref, c_local);
124+
}
125+
126+
TEST(SparseMatrix, NotSquareMatrixOtherAxis) {
127+
std::size_t m = 1000;
128+
std::size_t k = 10;
129+
130+
dr::sp::sparse_matrix<float> a(
131+
{m, k}, 0.1f,
132+
dr::sp::block_cyclic({dr::sp::tile::div, dr::sp::tile::div},
133+
{dr::sp::nprocs(), 1}));
134+
135+
dr::sp::distributed_vector<float> b(k, 1.f);
136+
dr::sp::distributed_vector<float> c(m, 0.f);
137+
138+
dr::sp::gemv(c, a, b);
139+
140+
std::vector<float> c_local(m);
141+
142+
dr::sp::copy(c.begin(), c.end(), c_local.begin());
143+
144+
std::vector<float> c_ref(m, 0.f);
145+
146+
for (auto &&[index, v] : a) {
147+
auto &&[i, k] = index;
148+
149+
c_ref[i] += v;
150+
}
151+
152+
EXPECT_TRUE(fp_equal(c_ref, c_local))
153+
<< fmt::format("Reference:\n {}\nActual:\n {}\n", c_ref, c_local);
154+
}
155+
156+
TEST(SparseMatrix, VerySparseMatrix) {
157+
std::size_t m = 100;
158+
std::size_t k = 100;
159+
160+
dr::sp::sparse_matrix<float> a(
161+
{m, k}, 0.001f,
162+
dr::sp::block_cyclic({dr::sp::tile::div, dr::sp::tile::div},
163+
{dr::sp::nprocs(), 1}));
164+
165+
dr::sp::distributed_vector<float> b(k, 1.f);
166+
dr::sp::distributed_vector<float> c(m, 0.f);
167+
168+
dr::sp::gemv(c, a, b);
169+
170+
std::vector<float> c_local(m);
171+
172+
dr::sp::copy(c.begin(), c.end(), c_local.begin());
173+
174+
std::vector<float> c_ref(m, 0.f);
175+
176+
for (auto &&[index, v] : a) {
177+
auto &&[i, k] = index;
178+
179+
c_ref[i] += v;
180+
}
181+
182+
EXPECT_TRUE(fp_equal(c_ref, c_local))
183+
<< fmt::format("Reference:\n {}\nActual:\n {}\n", c_ref, c_local);
184+
}

test/gtest/sp/sparse.cpp

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
// SPDX-FileCopyrightText: Intel Corporation
2+
//
3+
// SPDX-License-Identifier: BSD-3-Clause
4+
#include "xp-tests.hpp"
5+
6+
TEST(SparseMatrix, IterationForward) {
7+
std::size_t m = 10;
8+
std::size_t k = 10;
9+
using T = float;
10+
using I = int;
11+
std::vector<std::pair<std::pair<I, I>, T>> base;
12+
for (int i = 0; i < m; i++) {
13+
for (int j = 0; j < k; j++) {
14+
base.push_back({{i, j}, i + j});
15+
}
16+
}
17+
std::vector<std::pair<std::pair<I, I>, T>> reference(base.size());
18+
std::copy(base.begin(), base.end(), reference.begin());
19+
auto csr = dr::sp::__detail::convert_to_csr(base, {m, k}, base.size(),
20+
std::allocator<T>{});
21+
dr::sp::sparse_matrix<T, I> a =
22+
dr::sp::create_distributed(csr, dr::sp::row_cyclic());
23+
int i = 0;
24+
for (auto elem : a) {
25+
auto [index, value] = elem;
26+
auto [real_index, real_value] = reference[i];
27+
auto [m, n] = index;
28+
auto [r_m, r_n] = real_index;
29+
30+
EXPECT_TRUE(m == r_m && n == r_n) << fmt::format(
31+
"Reference m, n:\n {}, {}\nActual:\n {}, {}\n", r_m, r_n, m, n);
32+
EXPECT_TRUE(value == real_value) << fmt::format(
33+
"Reference value:\n {}\nActual:\n {}\n", real_value, value);
34+
i++;
35+
}
36+
}
37+
38+
TEST(SparseMatrix, IterationReverse) {
39+
std::size_t m = 10;
40+
std::size_t k = 10;
41+
using T = float;
42+
using I = int;
43+
std::vector<std::pair<std::pair<I, I>, T>> base;
44+
for (int i = 0; i < m; i++) {
45+
for (int j = 0; j < k; j++) {
46+
base.push_back({{i, j}, i + j});
47+
}
48+
}
49+
std::vector<std::pair<std::pair<I, I>, T>> reference(base.size());
50+
std::copy(base.begin(), base.end(), reference.begin());
51+
auto csr = dr::sp::__detail::convert_to_csr(base, {m, k}, base.size(),
52+
std::allocator<T>{});
53+
dr::sp::sparse_matrix<T, I> a =
54+
dr::sp::create_distributed(csr, dr::sp::row_cyclic());
55+
int i = base.size();
56+
auto iterator = a.end();
57+
while (iterator > a.begin()) {
58+
iterator--;
59+
i--;
60+
auto [index, value] = *iterator;
61+
auto [real_index, real_value] = reference[i];
62+
auto [m, n] = index;
63+
auto [r_m, r_n] = real_index;
64+
EXPECT_TRUE(m == r_m && n == r_n) << fmt::format(
65+
"Reference m, n:\n {}, {}\nActual:\n {}, {}\n", r_m, r_n, m, n);
66+
EXPECT_TRUE(value == real_value) << fmt::format(
67+
"Reference value:\n {}\nActual:\n {}\n", real_value, value);
68+
}
69+
}

0 commit comments

Comments
 (0)