Skip to content

Commit 63150a8

Browse files
authored
Add sparse conversion tests (dense2sparse, sparse2dense) (#873)
1 parent fcb6d3d commit 63150a8

File tree

3 files changed

+216
-13
lines changed

3 files changed

+216
-13
lines changed

test/00_sparse/Basic.cu

+15-13
Original file line numberDiff line numberDiff line change
@@ -38,17 +38,13 @@
3838

3939
using namespace matx;
4040

41-
template <typename T> class SparseTest : public ::testing::Test { };
41+
template <typename T> class BasicSparseTest : public ::testing::Test { };
4242

43-
template <typename TensorType> class SparseTestsAll : public SparseTest<TensorType> { };
43+
template <typename T> class BasicSparseTestsAll : public BasicSparseTest<T> { };
4444

45-
// For all operations that run on host and device.
46-
TYPED_TEST_SUITE(SparseTestsAll, MatXAllTypesAllExecs);
45+
TYPED_TEST_SUITE(BasicSparseTestsAll, MatXAllTypesAllExecs);
4746

48-
// For all operations that run on device only.
49-
TYPED_TEST_SUITE(SparseTestsAllCUDA, MatXAllTypesCUDAExec);
50-
51-
TYPED_TEST(SparseTestsAll, MakeZeroCOO) {
47+
TYPED_TEST(BasicSparseTestsAll, MakeZeroCOO) {
5248
MATX_ENTER_HANDLER();
5349
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
5450
auto A = experimental::make_zero_tensor_coo<TestType, index_t>({17, 33});
@@ -60,10 +56,12 @@ TYPED_TEST(SparseTestsAll, MakeZeroCOO) {
6056
ASSERT_EQ(A.posSize(1), 0);
6157
ASSERT_EQ(A.crdSize(0), 0);
6258
ASSERT_EQ(A.crdSize(1), 0);
59+
// Element getter.
60+
ASSERT_EQ(A(0, 0), static_cast<TestType>(0)); // not found
6361
MATX_EXIT_HANDLER();
6462
}
6563

66-
TYPED_TEST(SparseTestsAll, MakeCOO) {
64+
TYPED_TEST(BasicSparseTestsAll, MakeCOO) {
6765
MATX_ENTER_HANDLER();
6866
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
6967
auto vals = make_tensor<TestType>({3});
@@ -91,7 +89,7 @@ TYPED_TEST(SparseTestsAll, MakeCOO) {
9189
MATX_EXIT_HANDLER();
9290
}
9391

94-
TYPED_TEST(SparseTestsAll, MakeZeroCSR) {
92+
TYPED_TEST(BasicSparseTestsAll, MakeZeroCSR) {
9593
MATX_ENTER_HANDLER();
9694
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
9795
auto A = experimental::make_zero_tensor_csr<TestType, index_t, index_t>({17, 33});
@@ -103,10 +101,12 @@ TYPED_TEST(SparseTestsAll, MakeZeroCSR) {
103101
ASSERT_EQ(A.posSize(1), 18);
104102
ASSERT_EQ(A.crdSize(0), 0);
105103
ASSERT_EQ(A.crdSize(1), 0);
104+
// Element getter.
105+
ASSERT_EQ(A(0, 0), static_cast<TestType>(0)); // not found
106106
MATX_EXIT_HANDLER();
107107
}
108108

109-
TYPED_TEST(SparseTestsAll, MakeCSR) {
109+
TYPED_TEST(BasicSparseTestsAll, MakeCSR) {
110110
MATX_ENTER_HANDLER();
111111
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
112112
auto vals = make_tensor<TestType>({3});
@@ -134,7 +134,7 @@ TYPED_TEST(SparseTestsAll, MakeCSR) {
134134
MATX_EXIT_HANDLER();
135135
}
136136

137-
TYPED_TEST(SparseTestsAll, MakeZeroCSC) {
137+
TYPED_TEST(BasicSparseTestsAll, MakeZeroCSC) {
138138
MATX_ENTER_HANDLER();
139139
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
140140
auto A = experimental::make_zero_tensor_csc<TestType, index_t, index_t>({17, 33});
@@ -146,10 +146,12 @@ TYPED_TEST(SparseTestsAll, MakeZeroCSC) {
146146
ASSERT_EQ(A.posSize(1), 34);
147147
ASSERT_EQ(A.crdSize(0), 0);
148148
ASSERT_EQ(A.crdSize(1), 0);
149+
// Element getter.
150+
ASSERT_EQ(A(0, 0), static_cast<TestType>(0)); // not found
149151
MATX_EXIT_HANDLER();
150152
}
151153

152-
TYPED_TEST(SparseTestsAll, MakeCSC) {
154+
TYPED_TEST(BasicSparseTestsAll, MakeCSC) {
153155
MATX_ENTER_HANDLER();
154156
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
155157
auto vals = make_tensor<TestType>({3});

test/00_sparse/Convert.cu

+200
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,200 @@
1+
////////////////////////////////////////////////////////////////////////////////
2+
// BSD 3-Clause License
3+
//
4+
// Copyright (c) 2025, NVIDIA Corporation
5+
// All rights reserved.
6+
//
7+
// Redistribution and use in source and binary forms, with or without
8+
// modification, are permitted provided that the following conditions are met:
9+
//
10+
// 1. Redistributions of source code must retain the above copyright notice,
11+
// this list of conditions and the following disclaimer.
12+
//
13+
// 2. Redistributions in binary form must reproduce the above copyright notice,
14+
// this list of conditions and the following disclaimer in the documentation
15+
// and/or other materials provided with the distribution.
16+
//
17+
// 3. Neither the name of the copyright holder nor the names of its
18+
// contributors may be used to endorse or promote products derived from
19+
// this software without specific prior written permission.
20+
//
21+
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
22+
// AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
23+
// IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
24+
// DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
25+
// FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
26+
// DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
27+
// SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
28+
// CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
29+
// OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30+
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31+
/////////////////////////////////////////////////////////////////////////////////
32+
33+
#include "assert.h"
34+
#include "matx.h"
35+
#include "test_types.h"
36+
#include "utilities.h"
37+
#include "gtest/gtest.h"
38+
39+
using namespace matx;
40+
41+
// Helper method
42+
template <typename T> static auto makeD() {
43+
const index_t m = 10;
44+
const index_t n = 10;
45+
tensor_t<T, 2> D = make_tensor<T>({m, n});
46+
for (index_t i = 0; i < m; i++) {
47+
for (index_t j = 0; j < n; j++) {
48+
D(i, j) = static_cast<T>(0);
49+
}
50+
}
51+
D(0, 1) = static_cast<T>(1);
52+
D(4, 4) = static_cast<T>(2);
53+
D(9, 1) = static_cast<T>(3);
54+
D(9, 9) = static_cast<T>(4);
55+
return D;
56+
}
57+
58+
template <typename T> class ConvertSparseTest : public ::testing::Test { };
59+
60+
template <typename T> class ConvertSparseTestsAll : public ConvertSparseTest<T> { };
61+
62+
TYPED_TEST_SUITE(ConvertSparseTestsAll, MatXFloatNonComplexTypesCUDAExec);
63+
64+
TYPED_TEST(ConvertSparseTestsAll, ConvertCOO) {
65+
MATX_ENTER_HANDLER();
66+
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
67+
using ExecType = cuda::std::tuple_element_t<1, TypeParam>;
68+
69+
ExecType exec{};
70+
71+
auto D = makeD<TestType>();
72+
const auto m = D.Size(0);
73+
const auto n = D.Size(1);
74+
75+
// Convert dense D to sparse S.
76+
auto S = experimental::make_zero_tensor_coo<TestType, index_t>({m, n});
77+
(S = dense2sparse(D)).run(exec);
78+
ASSERT_EQ(S.Rank(), 2);
79+
ASSERT_EQ(S.Size(0), m);
80+
ASSERT_EQ(S.Size(1), n);
81+
ASSERT_EQ(S.Nse(), 4);
82+
ASSERT_EQ(S.posSize(0), 2);
83+
ASSERT_EQ(S.posSize(1), 0);
84+
ASSERT_EQ(S.crdSize(0), 4);
85+
ASSERT_EQ(S.crdSize(1), 4);
86+
87+
// Getters are expensive, but fully functional!
88+
exec.sync();
89+
for (index_t i = 0; i < m; i++) {
90+
for (index_t j = 0; j < n; j++) {
91+
ASSERT_EQ(S(i, j), D(i, j));
92+
}
93+
}
94+
95+
// Convert sparse S back to dense D.
96+
auto O = make_tensor<TestType>({m, n});
97+
(O = sparse2dense(S)).run(exec);
98+
99+
// Back to cheap random-access getters only.
100+
exec.sync();
101+
for (index_t i = 0; i < m; i++) {
102+
for (index_t j = 0; j < n; j++) {
103+
ASSERT_EQ(O(i, j), D(i, j));
104+
}
105+
}
106+
107+
MATX_EXIT_HANDLER();
108+
}
109+
110+
TYPED_TEST(ConvertSparseTestsAll, ConvertCSR) {
111+
MATX_ENTER_HANDLER();
112+
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
113+
using ExecType = cuda::std::tuple_element_t<1, TypeParam>;
114+
115+
ExecType exec{};
116+
117+
auto D = makeD<TestType>();
118+
const auto m = D.Size(0);
119+
const auto n = D.Size(1);
120+
121+
// Convert dense D to sparse S.
122+
auto S = experimental::make_zero_tensor_csr<TestType, index_t, index_t>({m, n});
123+
(S = dense2sparse(D)).run(exec);
124+
ASSERT_EQ(S.Rank(), 2);
125+
ASSERT_EQ(S.Size(0), m);
126+
ASSERT_EQ(S.Size(1), n);
127+
ASSERT_EQ(S.Nse(), 4);
128+
ASSERT_EQ(S.posSize(0), 0);
129+
ASSERT_EQ(S.posSize(1), m + 1);
130+
ASSERT_EQ(S.crdSize(0), 0);
131+
ASSERT_EQ(S.crdSize(1), 4);
132+
133+
// Getters are expensive, but fully functional!
134+
exec.sync();
135+
for (index_t i = 0; i < m; i++) {
136+
for (index_t j = 0; j < n; j++) {
137+
ASSERT_EQ(S(i, j), D(i, j));
138+
}
139+
}
140+
141+
// Convert sparse S back to dense D.
142+
auto O = make_tensor<TestType>({m, n});
143+
(O = sparse2dense(S)).run(exec);
144+
145+
// Back to cheap random-access getters only.
146+
exec.sync();
147+
for (index_t i = 0; i < m; i++) {
148+
for (index_t j = 0; j < n; j++) {
149+
ASSERT_EQ(O(i, j), D(i, j));
150+
}
151+
}
152+
153+
MATX_EXIT_HANDLER();
154+
}
155+
156+
TYPED_TEST(ConvertSparseTestsAll, ConvertCSC) {
157+
MATX_ENTER_HANDLER();
158+
using TestType = cuda::std::tuple_element_t<0, TypeParam>;
159+
using ExecType = cuda::std::tuple_element_t<1, TypeParam>;
160+
161+
ExecType exec{};
162+
163+
auto D = makeD<TestType>();
164+
const auto m = D.Size(0);
165+
const auto n = D.Size(1);
166+
167+
// Convert dense D to sparse S.
168+
auto S = experimental::make_zero_tensor_csc<TestType, index_t, index_t>({m, n});
169+
(S = dense2sparse(D)).run(exec);
170+
ASSERT_EQ(S.Rank(), 2);
171+
ASSERT_EQ(S.Size(0), m);
172+
ASSERT_EQ(S.Size(1), n);
173+
ASSERT_EQ(S.Nse(), 4);
174+
ASSERT_EQ(S.posSize(0), 0);
175+
ASSERT_EQ(S.posSize(1), n + 1);
176+
ASSERT_EQ(S.crdSize(0), 0);
177+
ASSERT_EQ(S.crdSize(1), 4);
178+
179+
// Getters are expensive, but fully functional!
180+
exec.sync();
181+
for (index_t i = 0; i < m; i++) {
182+
for (index_t j = 0; j < n; j++) {
183+
ASSERT_EQ(S(i, j), D(i, j));
184+
}
185+
}
186+
187+
// Convert sparse S back to dense D.
188+
auto O = make_tensor<TestType>({m, n});
189+
(O = sparse2dense(S)).run(exec);
190+
191+
// Back to cheap random-access getters only.
192+
exec.sync();
193+
for (index_t i = 0; i < m; i++) {
194+
for (index_t j = 0; j < n; j++) {
195+
ASSERT_EQ(O(i, j), D(i, j));
196+
}
197+
}
198+
199+
MATX_EXIT_HANDLER();
200+
}

test/CMakeLists.txt

+1
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ set (test_sources
3838
01_radar/ambgfun.cu
3939
01_radar/dct.cu
4040
00_sparse/Basic.cu
41+
00_sparse/Convert.cu
4142
main.cu
4243
)
4344

0 commit comments

Comments
 (0)