]> git.proxmox.com Git - ceph.git/blame - ceph/src/arrow/cpp/src/arrow/sparse_tensor_test.cc
import quincy 17.2.0
[ceph.git] / ceph / src / arrow / cpp / src / arrow / sparse_tensor_test.cc
CommitLineData
1d09f67e
TL
1// Licensed to the Apache Software Foundation (ASF) under one
2// or more contributor license agreements. See the NOTICE file
3// distributed with this work for additional information
4// regarding copyright ownership. The ASF licenses this file
5// to you under the Apache License, Version 2.0 (the
6// "License"); you may not use this file except in compliance
7// with the License. You may obtain a copy of the License at
8//
9// http://www.apache.org/licenses/LICENSE-2.0
10//
11// Unless required by applicable law or agreed to in writing,
12// software distributed under the License is distributed on an
13// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14// KIND, either express or implied. See the License for the
15// specific language governing permissions and limitations
16// under the License.
17
18// Unit tests for DataType (and subclasses), Field, and Schema
19
20#include <cmath>
21#include <cstdint>
22#include <memory>
23#include <string>
24#include <vector>
25
26#include <iostream>
27
28#include <gtest/gtest.h>
29
30#include "arrow/sparse_tensor.h"
31#include "arrow/testing/gtest_util.h"
32#include "arrow/testing/util.h"
33#include "arrow/type.h"
34#include "arrow/util/logging.h"
35#include "arrow/util/sort.h"
36
37namespace arrow {
38
39static inline void CheckSparseIndexFormatType(SparseTensorFormat::type expected,
40 const SparseTensor& sparse_tensor) {
41 ASSERT_EQ(expected, sparse_tensor.format_id());
42 ASSERT_EQ(expected, sparse_tensor.sparse_index()->format_id());
43}
44
45static inline void AssertCOOIndex(const std::shared_ptr<Tensor>& sidx, const int64_t nth,
46 const std::vector<int64_t>& expected_values) {
47 int64_t n = static_cast<int64_t>(expected_values.size());
48 for (int64_t i = 0; i < n; ++i) {
49 ASSERT_EQ(expected_values[i], sidx->Value<Int64Type>({nth, i}));
50 }
51}
52
53//-----------------------------------------------------------------------------
54// SparseCOOIndex
55
56TEST(TestSparseCOOIndex, MakeRowMajorCanonical) {
57 std::vector<int32_t> values = {0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 1, 3, 0, 2, 0, 0, 2, 2,
58 1, 0, 1, 1, 0, 3, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3};
59 auto data = Buffer::Wrap(values);
60 std::vector<int64_t> shape = {12, 3};
61 std::vector<int64_t> strides = {3 * sizeof(int32_t), sizeof(int32_t)}; // Row-major
62
63 // OK
64 std::shared_ptr<SparseCOOIndex> si;
65 ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data));
66 ASSERT_EQ(shape, si->indices()->shape());
67 ASSERT_EQ(strides, si->indices()->strides());
68 ASSERT_EQ(data->data(), si->indices()->raw_data());
69 ASSERT_TRUE(si->is_canonical());
70
71 // Non-integer type
72 auto res = SparseCOOIndex::Make(float32(), shape, strides, data);
73 ASSERT_RAISES(TypeError, res);
74
75 // Non-matrix indices
76 res = SparseCOOIndex::Make(int32(), {4, 3, 4}, strides, data);
77 ASSERT_RAISES(Invalid, res);
78
79 // Non-contiguous indices
80 res = SparseCOOIndex::Make(int32(), {6, 3}, {6 * sizeof(int32_t), 2 * sizeof(int32_t)},
81 data);
82 ASSERT_RAISES(Invalid, res);
83
84 // Make from sparse tensor properties
85 // (shape is arbitrary 3-dim, non-zero length = 12)
86 ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), {99, 99, 99}, 12, data));
87 ASSERT_EQ(shape, si->indices()->shape());
88 ASSERT_EQ(strides, si->indices()->strides());
89 ASSERT_EQ(data->data(), si->indices()->raw_data());
90}
91
92TEST(TestSparseCOOIndex, MakeRowMajorNonCanonical) {
93 std::vector<int32_t> values = {0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 1, 3, 0, 2, 0, 1, 0, 1,
94 0, 2, 2, 1, 0, 3, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3};
95 auto data = Buffer::Wrap(values);
96 std::vector<int64_t> shape = {12, 3};
97 std::vector<int64_t> strides = {3 * sizeof(int32_t), sizeof(int32_t)}; // Row-major
98
99 // OK
100 std::shared_ptr<SparseCOOIndex> si;
101 ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data));
102 ASSERT_EQ(shape, si->indices()->shape());
103 ASSERT_EQ(strides, si->indices()->strides());
104 ASSERT_EQ(data->data(), si->indices()->raw_data());
105 ASSERT_FALSE(si->is_canonical());
106}
107
108TEST(TestSparseCOOIndex, MakeColumnMajorCanonical) {
109 std::vector<int32_t> values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 2, 2,
110 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3};
111 auto data = Buffer::Wrap(values);
112 std::vector<int64_t> shape = {12, 3};
113 std::vector<int64_t> strides = {sizeof(int32_t), 12 * sizeof(int32_t)}; // Column-major
114
115 // OK
116 std::shared_ptr<SparseCOOIndex> si;
117 ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data));
118 ASSERT_EQ(shape, si->indices()->shape());
119 ASSERT_EQ(strides, si->indices()->strides());
120 ASSERT_EQ(data->data(), si->indices()->raw_data());
121 ASSERT_TRUE(si->is_canonical());
122}
123
124TEST(TestSparseCOOIndex, MakeColumnMajorNonCanonical) {
125 std::vector<int32_t> values = {0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 2, 0,
126 2, 0, 1, 1, 2, 2, 0, 2, 1, 3, 0, 1, 2, 3, 0, 2, 1, 3};
127 auto data = Buffer::Wrap(values);
128 std::vector<int64_t> shape = {12, 3};
129 std::vector<int64_t> strides = {sizeof(int32_t), 12 * sizeof(int32_t)}; // Column-major
130
131 // OK
132 std::shared_ptr<SparseCOOIndex> si;
133 ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data));
134 ASSERT_EQ(shape, si->indices()->shape());
135 ASSERT_EQ(strides, si->indices()->strides());
136 ASSERT_EQ(data->data(), si->indices()->raw_data());
137 ASSERT_FALSE(si->is_canonical());
138}
139
140TEST(TestSparseCOOIndex, MakeEmptyIndex) {
141 std::vector<int32_t> values = {};
142 auto data = Buffer::Wrap(values);
143 std::vector<int64_t> shape = {0, 3};
144 std::vector<int64_t> strides = {sizeof(int32_t), sizeof(int32_t)}; // Empty strides
145
146 // OK
147 std::shared_ptr<SparseCOOIndex> si;
148 ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data));
149 ASSERT_EQ(shape, si->indices()->shape());
150 ASSERT_EQ(strides, si->indices()->strides());
151 ASSERT_EQ(data->data(), si->indices()->raw_data());
152 ASSERT_TRUE(si->is_canonical());
153}
154
155TEST(TestSparseCSRIndex, Make) {
156 std::vector<int32_t> indptr_values = {0, 2, 4, 6, 8, 10, 12};
157 std::vector<int32_t> indices_values = {0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3};
158 auto indptr_data = Buffer::Wrap(indptr_values);
159 auto indices_data = Buffer::Wrap(indices_values);
160 std::vector<int64_t> indptr_shape = {7};
161 std::vector<int64_t> indices_shape = {12};
162
163 // OK
164 std::shared_ptr<SparseCSRIndex> si;
165 ASSERT_OK_AND_ASSIGN(si, SparseCSRIndex::Make(int32(), indptr_shape, indices_shape,
166 indptr_data, indices_data));
167 ASSERT_EQ(indptr_shape, si->indptr()->shape());
168 ASSERT_EQ(indptr_data->data(), si->indptr()->raw_data());
169 ASSERT_EQ(indices_shape, si->indices()->shape());
170 ASSERT_EQ(indices_data->data(), si->indices()->raw_data());
171 ASSERT_EQ(std::string("SparseCSRIndex"), si->ToString());
172
173 // Non-integer type
174 auto res = SparseCSRIndex::Make(float32(), indptr_shape, indices_shape, indptr_data,
175 indices_data);
176 ASSERT_RAISES(TypeError, res);
177
178 // Non-vector indptr shape
179 ASSERT_RAISES(Invalid, SparseCSRIndex::Make(int32(), {1, 2}, indices_shape, indptr_data,
180 indices_data));
181
182 // Non-vector indices shape
183 ASSERT_RAISES(Invalid, SparseCSRIndex::Make(int32(), indptr_shape, {1, 2}, indptr_data,
184 indices_data));
185}
186
187TEST(TestSparseCSCIndex, Make) {
188 std::vector<int32_t> indptr_values = {0, 2, 4, 6, 8, 10, 12};
189 std::vector<int32_t> indices_values = {0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3};
190 auto indptr_data = Buffer::Wrap(indptr_values);
191 auto indices_data = Buffer::Wrap(indices_values);
192 std::vector<int64_t> indptr_shape = {7};
193 std::vector<int64_t> indices_shape = {12};
194
195 // OK
196 std::shared_ptr<SparseCSCIndex> si;
197 ASSERT_OK_AND_ASSIGN(si, SparseCSCIndex::Make(int32(), indptr_shape, indices_shape,
198 indptr_data, indices_data));
199 ASSERT_EQ(indptr_shape, si->indptr()->shape());
200 ASSERT_EQ(indptr_data->data(), si->indptr()->raw_data());
201 ASSERT_EQ(indices_shape, si->indices()->shape());
202 ASSERT_EQ(indices_data->data(), si->indices()->raw_data());
203 ASSERT_EQ(std::string("SparseCSCIndex"), si->ToString());
204
205 // Non-integer type
206 ASSERT_RAISES(TypeError, SparseCSCIndex::Make(float32(), indptr_shape, indices_shape,
207 indptr_data, indices_data));
208
209 // Non-vector indptr shape
210 ASSERT_RAISES(Invalid, SparseCSCIndex::Make(int32(), {1, 2}, indices_shape, indptr_data,
211 indices_data));
212
213 // Non-vector indices shape
214 ASSERT_RAISES(Invalid, SparseCSCIndex::Make(int32(), indptr_shape, {1, 2}, indptr_data,
215 indices_data));
216}
217
218template <typename ValueType>
219class TestSparseTensorBase : public ::testing::Test {
220 protected:
221 std::vector<int64_t> shape_;
222 std::vector<std::string> dim_names_;
223};
224
225//-----------------------------------------------------------------------------
226// SparseCOOTensor
227
228template <typename IndexValueType, typename ValueType = Int64Type>
229class TestSparseCOOTensorBase : public TestSparseTensorBase<ValueType> {
230 public:
231 using c_value_type = typename ValueType::c_type;
232
233 void SetUp() {
234 shape_ = {2, 3, 4};
235 dim_names_ = {"foo", "bar", "baz"};
236
237 // Dense representation:
238 // [
239 // [
240 // 1 0 2 0
241 // 0 3 0 4
242 // 5 0 6 0
243 // ],
244 // [
245 // 0 11 0 12
246 // 13 0 14 0
247 // 0 15 0 16
248 // ]
249 // ]
250 dense_values_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
251 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
252 auto dense_data = Buffer::Wrap(dense_values_);
253 NumericTensor<ValueType> dense_tensor(dense_data, shape_, {}, dim_names_);
254 ASSERT_OK_AND_ASSIGN(sparse_tensor_from_dense_,
255 SparseCOOTensor::Make(
256 dense_tensor, TypeTraits<IndexValueType>::type_singleton()));
257 }
258
259 protected:
260 using TestSparseTensorBase<ValueType>::shape_;
261 using TestSparseTensorBase<ValueType>::dim_names_;
262 std::vector<c_value_type> dense_values_;
263 std::shared_ptr<SparseCOOTensor> sparse_tensor_from_dense_;
264};
265
266class TestSparseCOOTensor : public TestSparseCOOTensorBase<Int64Type> {};
267
268TEST_F(TestSparseCOOTensor, CreationEmptyTensor) {
269 SparseCOOTensor st1(int64(), this->shape_);
270 SparseCOOTensor st2(int64(), this->shape_, this->dim_names_);
271
272 ASSERT_EQ(0, st1.non_zero_length());
273 ASSERT_EQ(0, st2.non_zero_length());
274
275 ASSERT_EQ(24, st1.size());
276 ASSERT_EQ(24, st2.size());
277
278 ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st2.dim_names());
279 ASSERT_EQ("foo", st2.dim_name(0));
280 ASSERT_EQ("bar", st2.dim_name(1));
281 ASSERT_EQ("baz", st2.dim_name(2));
282
283 ASSERT_EQ(std::vector<std::string>({}), st1.dim_names());
284 ASSERT_EQ("", st1.dim_name(0));
285 ASSERT_EQ("", st1.dim_name(1));
286 ASSERT_EQ("", st1.dim_name(2));
287}
288
289TEST_F(TestSparseCOOTensor, CreationFromZeroTensor) {
290 const auto dense_size =
291 std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1),
292 [](int64_t a, int64_t x) { return a * x; });
293 std::vector<int64_t> dense_values(dense_size, 0);
294 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero,
295 Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_));
296 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st_zero,
297 SparseCOOTensor::Make(*t_zero, int64()));
298
299 ASSERT_EQ(0, st_zero->non_zero_length());
300 ASSERT_EQ(dense_size, st_zero->size());
301
302 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor());
303 ASSERT_TRUE(t->Equals(*t_zero));
304}
305
306TEST_F(TestSparseCOOTensor, CreationFromNumericTensor) {
307 auto st = this->sparse_tensor_from_dense_;
308 CheckSparseIndexFormatType(SparseTensorFormat::COO, *st);
309
310 ASSERT_EQ(12, st->non_zero_length());
311 ASSERT_TRUE(st->is_mutable());
312
313 auto* raw_data = reinterpret_cast<const int64_t*>(st->raw_data());
314 AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16});
315
316 auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index());
317 ASSERT_EQ(std::string("SparseCOOIndex"), si->ToString());
318 ASSERT_TRUE(si->is_canonical());
319
320 std::shared_ptr<Tensor> sidx = si->indices();
321 ASSERT_EQ(std::vector<int64_t>({12, 3}), sidx->shape());
322 ASSERT_TRUE(sidx->is_row_major());
323
324 AssertCOOIndex(sidx, 0, {0, 0, 0});
325 AssertCOOIndex(sidx, 1, {0, 0, 2});
326 AssertCOOIndex(sidx, 2, {0, 1, 1});
327 AssertCOOIndex(sidx, 10, {1, 2, 1});
328 AssertCOOIndex(sidx, 11, {1, 2, 3});
329}
330
331TEST_F(TestSparseCOOTensor, CreationFromNumericTensor1D) {
332 auto dense_data = Buffer::Wrap(this->dense_values_);
333 std::vector<int64_t> dense_shape({static_cast<int64_t>(this->dense_values_.size())});
334 NumericTensor<Int64Type> dense_vector(dense_data, dense_shape);
335
336 std::shared_ptr<SparseCOOTensor> st;
337 ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(dense_vector));
338
339 ASSERT_EQ(12, st->non_zero_length());
340 ASSERT_TRUE(st->is_mutable());
341
342 auto* raw_data = reinterpret_cast<const int64_t*>(st->raw_data());
343 AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16});
344
345 auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index());
346 ASSERT_TRUE(si->is_canonical());
347
348 auto sidx = si->indices();
349 ASSERT_EQ(std::vector<int64_t>({12, 1}), sidx->shape());
350
351 AssertCOOIndex(sidx, 0, {0});
352 AssertCOOIndex(sidx, 1, {2});
353 AssertCOOIndex(sidx, 2, {5});
354 AssertCOOIndex(sidx, 10, {21});
355 AssertCOOIndex(sidx, 11, {23});
356}
357
358TEST_F(TestSparseCOOTensor, CreationFromTensor) {
359 std::shared_ptr<Buffer> buffer = Buffer::Wrap(this->dense_values_);
360 Tensor tensor(int64(), buffer, this->shape_, {}, this->dim_names_);
361
362 std::shared_ptr<SparseCOOTensor> st;
363 ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(tensor));
364
365 ASSERT_EQ(12, st->non_zero_length());
366 ASSERT_TRUE(st->is_mutable());
367
368 ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st->dim_names());
369 ASSERT_EQ("foo", st->dim_name(0));
370 ASSERT_EQ("bar", st->dim_name(1));
371 ASSERT_EQ("baz", st->dim_name(2));
372
373 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
374
375 auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index());
376 ASSERT_TRUE(si->is_canonical());
377}
378
379TEST_F(TestSparseCOOTensor, CreationFromNonContiguousTensor) {
380 std::vector<int64_t> values = {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0,
381 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0,
382 13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0};
383 std::vector<int64_t> strides = {192, 64, 16};
384 std::shared_ptr<Buffer> buffer = Buffer::Wrap(values);
385 Tensor tensor(int64(), buffer, this->shape_, strides);
386
387 std::shared_ptr<SparseCOOTensor> st;
388 ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(tensor));
389
390 ASSERT_EQ(12, st->non_zero_length());
391 ASSERT_TRUE(st->is_mutable());
392
393 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
394
395 auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index());
396 ASSERT_TRUE(si->is_canonical());
397}
398
399TEST_F(TestSparseCOOTensor, TestToTensor) {
400 std::vector<int64_t> values = {1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1,
401 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4};
402 std::vector<int64_t> shape({4, 3, 2, 1});
403 std::shared_ptr<Buffer> buffer = Buffer::Wrap(values);
404 Tensor tensor(int64(), buffer, shape, {}, this->dim_names_);
405
406 std::shared_ptr<SparseCOOTensor> sparse_tensor;
407 ASSERT_OK_AND_ASSIGN(sparse_tensor, SparseCOOTensor::Make(tensor));
408
409 ASSERT_EQ(5, sparse_tensor->non_zero_length());
410 ASSERT_TRUE(sparse_tensor->is_mutable());
411 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dense_tensor, sparse_tensor->ToTensor());
412 ASSERT_TRUE(tensor.Equals(*dense_tensor));
413}
414
415template <typename ValueType>
416class TestSparseCOOTensorEquality : public TestSparseTensorBase<ValueType> {
417 public:
418 void SetUp() {
419 shape_ = {2, 3, 4};
420 values1_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
421 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
422 values2_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
423 0, 0, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
424 auto buffer1 = Buffer::Wrap(values1_);
425 auto buffer2 = Buffer::Wrap(values2_);
426 DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, this->shape_).Value(&tensor1_));
427 DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, this->shape_).Value(&tensor2_));
428 }
429
430 protected:
431 using TestSparseTensorBase<ValueType>::shape_;
432 std::vector<typename ValueType::c_type> values1_;
433 std::vector<typename ValueType::c_type> values2_;
434 std::shared_ptr<NumericTensor<ValueType>> tensor1_;
435 std::shared_ptr<NumericTensor<ValueType>> tensor2_;
436};
437
438template <typename ValueType>
439class TestIntegerSparseCOOTensorEquality : public TestSparseCOOTensorEquality<ValueType> {
440};
441
442TYPED_TEST_SUITE_P(TestIntegerSparseCOOTensorEquality);
443
444TYPED_TEST_P(TestIntegerSparseCOOTensorEquality, TestEquality) {
445 using ValueType = TypeParam;
446 static_assert(is_integer_type<ValueType>::value, "Integer type is required");
447
448 std::shared_ptr<SparseCOOTensor> st1, st2, st3;
449 ASSERT_OK_AND_ASSIGN(st1, SparseCOOTensor::Make(*this->tensor1_));
450 ASSERT_OK_AND_ASSIGN(st2, SparseCOOTensor::Make(*this->tensor2_));
451 ASSERT_OK_AND_ASSIGN(st3, SparseCOOTensor::Make(*this->tensor1_));
452
453 ASSERT_TRUE(st1->Equals(*st1));
454 ASSERT_FALSE(st1->Equals(*st2));
455 ASSERT_TRUE(st1->Equals(*st3));
456}
457
458REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCOOTensorEquality, TestEquality);
459
460INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCOOTensorEquality, Int8Type);
461INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCOOTensorEquality, UInt8Type);
462INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCOOTensorEquality, Int16Type);
463INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCOOTensorEquality,
464 UInt16Type);
465INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCOOTensorEquality, Int32Type);
466INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCOOTensorEquality,
467 UInt32Type);
468INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCOOTensorEquality, Int64Type);
469INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCOOTensorEquality,
470 UInt64Type);
471
472template <typename ValueType>
473class TestFloatingSparseCOOTensorEquality
474 : public TestSparseCOOTensorEquality<ValueType> {};
475
476TYPED_TEST_SUITE_P(TestFloatingSparseCOOTensorEquality);
477
478TYPED_TEST_P(TestFloatingSparseCOOTensorEquality, TestEquality) {
479 using ValueType = TypeParam;
480 using c_value_type = typename ValueType::c_type;
481 static_assert(is_floating_type<ValueType>::value, "Float type is required");
482
483 std::shared_ptr<SparseCOOTensor> st1, st2, st3;
484 ASSERT_OK_AND_ASSIGN(st1, SparseCOOTensor::Make(*this->tensor1_));
485 ASSERT_OK_AND_ASSIGN(st2, SparseCOOTensor::Make(*this->tensor2_));
486 ASSERT_OK_AND_ASSIGN(st3, SparseCOOTensor::Make(*this->tensor1_));
487
488 ASSERT_TRUE(st1->Equals(*st1));
489 ASSERT_FALSE(st1->Equals(*st2));
490 ASSERT_TRUE(st1->Equals(*st3));
491
492 // sparse tensors with NaNs
493 const c_value_type nan_value = static_cast<c_value_type>(NAN);
494 this->values2_[13] = nan_value;
495 EXPECT_TRUE(std::isnan(this->tensor2_->Value({1, 0, 1})));
496
497 std::shared_ptr<SparseCOOTensor> st4;
498 ASSERT_OK_AND_ASSIGN(st4, SparseCOOTensor::Make(*this->tensor2_));
499 EXPECT_FALSE(st4->Equals(*st4)); // same object
500 EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object
501
502 std::vector<c_value_type> values5 = this->values2_;
503 std::shared_ptr<SparseCOOTensor> st5;
504 std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5);
505 NumericTensor<ValueType> tensor5(buffer5, this->shape_);
506 ASSERT_OK_AND_ASSIGN(st5, SparseCOOTensor::Make(tensor5));
507 EXPECT_FALSE(st4->Equals(*st5)); // different memory
508 EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory
509}
510
511REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCOOTensorEquality, TestEquality);
512
513INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCOOTensorEquality, FloatType);
514INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCOOTensorEquality,
515 DoubleType);
516
517template <typename IndexValueType>
518class TestSparseCOOTensorForIndexValueType
519 : public TestSparseCOOTensorBase<IndexValueType> {
520 public:
521 using c_index_value_type = typename IndexValueType::c_type;
522
523 void SetUp() override {
524 TestSparseCOOTensorBase<IndexValueType>::SetUp();
525
526 // Sparse representation:
527 // idx[0] = [0 0 0 0 0 0 1 1 1 1 1 1]
528 // idx[1] = [0 0 1 1 2 2 0 0 1 1 2 2]
529 // idx[2] = [0 2 1 3 0 2 1 3 0 2 1 3]
530 // data = [1 2 3 4 5 6 11 12 13 14 15 16]
531
532 coords_values_row_major_ = {0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 1, 3, 0, 2, 0, 0, 2, 2,
533 1, 0, 1, 1, 0, 3, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3};
534
535 coords_values_col_major_ = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 2, 2,
536 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3};
537 }
538
539 std::shared_ptr<DataType> index_data_type() const {
540 return TypeTraits<IndexValueType>::type_singleton();
541 }
542
543 protected:
544 std::vector<c_index_value_type> coords_values_row_major_;
545 std::vector<c_index_value_type> coords_values_col_major_;
546
547 Result<std::shared_ptr<SparseCOOIndex>> MakeSparseCOOIndex(
548 const std::vector<int64_t>& shape, const std::vector<int64_t>& strides,
549 const std::vector<c_index_value_type>& values) const {
550 return SparseCOOIndex::Make(index_data_type(), shape, strides, Buffer::Wrap(values));
551 }
552
553 template <typename CValueType>
554 Result<std::shared_ptr<SparseCOOTensor>> MakeSparseTensor(
555 const std::shared_ptr<SparseCOOIndex>& si,
556 std::vector<CValueType>& sparse_values) const {
557 auto data = Buffer::Wrap(sparse_values);
558 return SparseCOOTensor::Make(si, CTypeTraits<CValueType>::type_singleton(), data,
559 this->shape_, this->dim_names_);
560 }
561};
562
563TYPED_TEST_SUITE_P(TestSparseCOOTensorForIndexValueType);
564
565TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, Make) {
566 using IndexValueType = TypeParam;
567 using c_index_value_type = typename IndexValueType::c_type;
568
569 constexpr int sizeof_index_value = sizeof(c_index_value_type);
570 ASSERT_OK_AND_ASSIGN(
571 std::shared_ptr<SparseCOOIndex> si,
572 this->MakeSparseCOOIndex({12, 3}, {sizeof_index_value * 3, sizeof_index_value},
573 this->coords_values_row_major_));
574
575 std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16};
576 auto sparse_data = Buffer::Wrap(sparse_values);
577
578 std::shared_ptr<SparseCOOTensor> st;
579
580 // OK
581 ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(si, int64(), sparse_data, this->shape_,
582 this->dim_names_));
583 ASSERT_EQ(int64(), st->type());
584 ASSERT_EQ(this->shape_, st->shape());
585 ASSERT_EQ(this->dim_names_, st->dim_names());
586 ASSERT_EQ(sparse_data->data(), st->raw_data());
587 ASSERT_TRUE(
588 internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index())->Equals(*si));
589
590 // OK with an empty dim_names
591 ASSERT_OK_AND_ASSIGN(st,
592 SparseCOOTensor::Make(si, int64(), sparse_data, this->shape_, {}));
593 ASSERT_EQ(int64(), st->type());
594 ASSERT_EQ(this->shape_, st->shape());
595 ASSERT_EQ(std::vector<std::string>{}, st->dim_names());
596 ASSERT_EQ(sparse_data->data(), st->raw_data());
597 ASSERT_TRUE(
598 internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index())->Equals(*si));
599
600 // invalid data type
601 auto res = SparseCOOTensor::Make(si, binary(), sparse_data, this->shape_, {});
602 ASSERT_RAISES(Invalid, res);
603
604 // negative items in shape
605 res = SparseCOOTensor::Make(si, int64(), sparse_data, {2, -3, 4}, {});
606 ASSERT_RAISES(Invalid, res);
607
608 // sparse index and ndim (shape length) are inconsistent
609 res = SparseCOOTensor::Make(si, int64(), sparse_data, {6, 4}, {});
610 ASSERT_RAISES(Invalid, res);
611
612 // shape and dim_names are inconsistent
613 res = SparseCOOTensor::Make(si, int64(), sparse_data, this->shape_,
614 std::vector<std::string>{"foo"});
615 ASSERT_RAISES(Invalid, res);
616}
617
618TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, CreationWithRowMajorIndex) {
619 using IndexValueType = TypeParam;
620 using c_index_value_type = typename IndexValueType::c_type;
621
622 constexpr int sizeof_index_value = sizeof(c_index_value_type);
623 ASSERT_OK_AND_ASSIGN(
624 std::shared_ptr<SparseCOOIndex> si,
625 this->MakeSparseCOOIndex({12, 3}, {sizeof_index_value * 3, sizeof_index_value},
626 this->coords_values_row_major_));
627
628 std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16};
629 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st,
630 this->MakeSparseTensor(si, sparse_values));
631
632 ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st->dim_names());
633 ASSERT_EQ("foo", st->dim_name(0));
634 ASSERT_EQ("bar", st->dim_name(1));
635 ASSERT_EQ("baz", st->dim_name(2));
636
637 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
638}
639
640TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, CreationWithColumnMajorIndex) {
641 using IndexValueType = TypeParam;
642 using c_index_value_type = typename IndexValueType::c_type;
643
644 constexpr int sizeof_index_value = sizeof(c_index_value_type);
645 ASSERT_OK_AND_ASSIGN(
646 std::shared_ptr<SparseCOOIndex> si,
647 this->MakeSparseCOOIndex({12, 3}, {sizeof_index_value, sizeof_index_value * 12},
648 this->coords_values_col_major_));
649
650 std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16};
651 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st,
652 this->MakeSparseTensor(si, sparse_values));
653
654 ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st->dim_names());
655 ASSERT_EQ("foo", st->dim_name(0));
656 ASSERT_EQ("bar", st->dim_name(1));
657 ASSERT_EQ("baz", st->dim_name(2));
658
659 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
660}
661
662TYPED_TEST_P(TestSparseCOOTensorForIndexValueType,
663 EqualityBetweenRowAndColumnMajorIndices) {
664 using IndexValueType = TypeParam;
665 using c_index_value_type = typename IndexValueType::c_type;
666
667 // Row-major COO index
668 const std::vector<int64_t> coords_shape = {12, 3};
669 constexpr int sizeof_index_value = sizeof(c_index_value_type);
670 ASSERT_OK_AND_ASSIGN(
671 std::shared_ptr<SparseCOOIndex> si_row_major,
672 this->MakeSparseCOOIndex(coords_shape, {sizeof_index_value * 3, sizeof_index_value},
673 this->coords_values_row_major_));
674
675 // Column-major COO index
676 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOIndex> si_col_major,
677 this->MakeSparseCOOIndex(
678 coords_shape, {sizeof_index_value, sizeof_index_value * 12},
679 this->coords_values_col_major_));
680
681 std::vector<int64_t> sparse_values_1 = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16};
682 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st1,
683 this->MakeSparseTensor(si_row_major, sparse_values_1));
684
685 std::vector<int64_t> sparse_values_2 = sparse_values_1;
686 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st2,
687 this->MakeSparseTensor(si_row_major, sparse_values_2));
688
689 ASSERT_TRUE(st2->Equals(*st1));
690}
691
692REGISTER_TYPED_TEST_SUITE_P(TestSparseCOOTensorForIndexValueType, Make,
693 CreationWithRowMajorIndex, CreationWithColumnMajorIndex,
694 EqualityBetweenRowAndColumnMajorIndices);
695
696INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestSparseCOOTensorForIndexValueType, Int8Type);
697INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestSparseCOOTensorForIndexValueType,
698 UInt8Type);
699INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestSparseCOOTensorForIndexValueType,
700 Int16Type);
701INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestSparseCOOTensorForIndexValueType,
702 UInt16Type);
703INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestSparseCOOTensorForIndexValueType,
704 Int32Type);
705INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestSparseCOOTensorForIndexValueType,
706 UInt32Type);
707INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestSparseCOOTensorForIndexValueType,
708 Int64Type);
709
710TEST(TestSparseCOOTensorForUInt64Index, Make) {
711 std::vector<int64_t> dense_values = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
712 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
713 Tensor dense_tensor(uint64(), Buffer::Wrap(dense_values), {2, 3, 4});
714 ASSERT_RAISES(Invalid, SparseCOOTensor::Make(dense_tensor, uint64()));
715}
716
717template <typename IndexValueType>
718class TestSparseCSRMatrixBase : public TestSparseTensorBase<Int64Type> {
719 public:
720 void SetUp() {
721 shape_ = {6, 4};
722 dim_names_ = {"foo", "bar"};
723
724 // Dense representation:
725 // [
726 // 1 0 2 0
727 // 0 3 0 4
728 // 5 0 6 0
729 // 0 11 0 12
730 // 13 0 14 0
731 // 0 15 0 16
732 // ]
733 dense_values_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
734 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
735 auto dense_data = Buffer::Wrap(dense_values_);
736 NumericTensor<Int64Type> dense_tensor(dense_data, shape_, {}, dim_names_);
737 ASSERT_OK_AND_ASSIGN(sparse_tensor_from_dense_,
738 SparseCSRMatrix::Make(
739 dense_tensor, TypeTraits<IndexValueType>::type_singleton()));
740 }
741
742 protected:
743 std::vector<int64_t> dense_values_;
744 std::shared_ptr<SparseCSRMatrix> sparse_tensor_from_dense_;
745};
746
747class TestSparseCSRMatrix : public TestSparseCSRMatrixBase<Int64Type> {};
748
749TEST_F(TestSparseCSRMatrix, CreationFromZeroTensor) {
750 const auto dense_size =
751 std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1),
752 [](int64_t a, int64_t x) { return a * x; });
753 std::vector<int64_t> dense_values(dense_size, 0);
754 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero,
755 Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_));
756 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCSRMatrix> st_zero,
757 SparseCSRMatrix::Make(*t_zero, int64()));
758
759 ASSERT_EQ(0, st_zero->non_zero_length());
760 ASSERT_EQ(dense_size, st_zero->size());
761
762 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor());
763 ASSERT_TRUE(t->Equals(*t_zero));
764}
765
766TEST_F(TestSparseCSRMatrix, CreationFromNumericTensor2D) {
767 std::shared_ptr<Buffer> buffer = Buffer::Wrap(this->dense_values_);
768 NumericTensor<Int64Type> tensor(buffer, this->shape_);
769
770 std::shared_ptr<SparseCSRMatrix> st1;
771 ASSERT_OK_AND_ASSIGN(st1, SparseCSRMatrix::Make(tensor));
772
773 auto st2 = this->sparse_tensor_from_dense_;
774
775 CheckSparseIndexFormatType(SparseTensorFormat::CSR, *st1);
776
777 ASSERT_EQ(12, st1->non_zero_length());
778 ASSERT_TRUE(st1->is_mutable());
779
780 ASSERT_EQ(std::vector<std::string>({"foo", "bar"}), st2->dim_names());
781 ASSERT_EQ("foo", st2->dim_name(0));
782 ASSERT_EQ("bar", st2->dim_name(1));
783
784 ASSERT_EQ(std::vector<std::string>({}), st1->dim_names());
785 ASSERT_EQ("", st1->dim_name(0));
786 ASSERT_EQ("", st1->dim_name(1));
787 ASSERT_EQ("", st1->dim_name(2));
788
789 const int64_t* raw_data = reinterpret_cast<const int64_t*>(st1->raw_data());
790 AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16});
791
792 auto si = internal::checked_pointer_cast<SparseCSRIndex>(st1->sparse_index());
793 ASSERT_EQ(std::string("SparseCSRIndex"), si->ToString());
794 ASSERT_EQ(1, si->indptr()->ndim());
795 ASSERT_EQ(1, si->indices()->ndim());
796
797 const int64_t* indptr_begin =
798 reinterpret_cast<const int64_t*>(si->indptr()->raw_data());
799 std::vector<int64_t> indptr_values(indptr_begin,
800 indptr_begin + si->indptr()->shape()[0]);
801
802 ASSERT_EQ(7, indptr_values.size());
803 ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 6, 8, 10, 12}), indptr_values);
804
805 const int64_t* indices_begin =
806 reinterpret_cast<const int64_t*>(si->indices()->raw_data());
807 std::vector<int64_t> indices_values(indices_begin,
808 indices_begin + si->indices()->shape()[0]);
809
810 ASSERT_EQ(12, indices_values.size());
811 ASSERT_EQ(std::vector<int64_t>({0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}), indices_values);
812}
813
814TEST_F(TestSparseCSRMatrix, CreationFromNonContiguousTensor) {
815 std::vector<int64_t> values = {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0,
816 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0,
817 13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0};
818 std::vector<int64_t> strides = {64, 16};
819 std::shared_ptr<Buffer> buffer = Buffer::Wrap(values);
820 Tensor tensor(int64(), buffer, this->shape_, strides);
821
822 std::shared_ptr<SparseCSRMatrix> st;
823 ASSERT_OK_AND_ASSIGN(st, SparseCSRMatrix::Make(tensor));
824
825 ASSERT_EQ(12, st->non_zero_length());
826 ASSERT_TRUE(st->is_mutable());
827
828 const int64_t* raw_data = reinterpret_cast<const int64_t*>(st->raw_data());
829 AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16});
830
831 auto si = internal::checked_pointer_cast<SparseCSRIndex>(st->sparse_index());
832 ASSERT_EQ(1, si->indptr()->ndim());
833 ASSERT_EQ(1, si->indices()->ndim());
834
835 const int64_t* indptr_begin =
836 reinterpret_cast<const int64_t*>(si->indptr()->raw_data());
837 std::vector<int64_t> indptr_values(indptr_begin,
838 indptr_begin + si->indptr()->shape()[0]);
839
840 ASSERT_EQ(7, indptr_values.size());
841 ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 6, 8, 10, 12}), indptr_values);
842
843 const int64_t* indices_begin =
844 reinterpret_cast<const int64_t*>(si->indices()->raw_data());
845 std::vector<int64_t> indices_values(indices_begin,
846 indices_begin + si->indices()->shape()[0]);
847
848 ASSERT_EQ(12, indices_values.size());
849 ASSERT_EQ(std::vector<int64_t>({0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}), indices_values);
850
851 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
852}
853
854TEST_F(TestSparseCSRMatrix, TestToTensor) {
855 std::vector<int64_t> values = {1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 1,
856 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1};
857 std::vector<int64_t> shape({6, 4});
858 std::shared_ptr<Buffer> buffer = Buffer::Wrap(values);
859 Tensor tensor(int64(), buffer, shape, {}, this->dim_names_);
860
861 std::shared_ptr<SparseCSRMatrix> sparse_tensor;
862 ASSERT_OK_AND_ASSIGN(sparse_tensor, SparseCSRMatrix::Make(tensor));
863
864 ASSERT_EQ(7, sparse_tensor->non_zero_length());
865 ASSERT_TRUE(sparse_tensor->is_mutable());
866
867 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dense_tensor, sparse_tensor->ToTensor());
868 ASSERT_TRUE(tensor.Equals(*dense_tensor));
869}
870
871template <typename ValueType>
872class TestSparseCSRMatrixEquality : public TestSparseTensorBase<ValueType> {
873 public:
874 void SetUp() {
875 shape_ = {6, 4};
876 values1_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
877 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
878 values2_ = {9, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
879 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
880 auto buffer1 = Buffer::Wrap(values1_);
881 auto buffer2 = Buffer::Wrap(values2_);
882 DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, this->shape_).Value(&tensor1_));
883 DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, this->shape_).Value(&tensor2_));
884 }
885
886 protected:
887 using TestSparseTensorBase<ValueType>::shape_;
888 std::vector<typename ValueType::c_type> values1_;
889 std::vector<typename ValueType::c_type> values2_;
890 std::shared_ptr<NumericTensor<ValueType>> tensor1_;
891 std::shared_ptr<NumericTensor<ValueType>> tensor2_;
892};
893
894template <typename ValueType>
895class TestIntegerSparseCSRMatrixEquality : public TestSparseCSRMatrixEquality<ValueType> {
896};
897
898TYPED_TEST_SUITE_P(TestIntegerSparseCSRMatrixEquality);
899
900TYPED_TEST_P(TestIntegerSparseCSRMatrixEquality, TestEquality) {
901 using ValueType = TypeParam;
902 static_assert(is_integer_type<ValueType>::value, "Integer type is required");
903
904 std::shared_ptr<SparseCSRMatrix> st1, st2, st3;
905 ASSERT_OK_AND_ASSIGN(st1, SparseCSRMatrix::Make(*this->tensor1_));
906 ASSERT_OK_AND_ASSIGN(st2, SparseCSRMatrix::Make(*this->tensor2_));
907 ASSERT_OK_AND_ASSIGN(st3, SparseCSRMatrix::Make(*this->tensor1_));
908
909 ASSERT_TRUE(st1->Equals(*st1));
910 ASSERT_FALSE(st1->Equals(*st2));
911 ASSERT_TRUE(st1->Equals(*st3));
912}
913
914REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCSRMatrixEquality, TestEquality);
915
916INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCSRMatrixEquality, Int8Type);
917INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCSRMatrixEquality, UInt8Type);
918INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCSRMatrixEquality, Int16Type);
919INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCSRMatrixEquality,
920 UInt16Type);
921INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCSRMatrixEquality, Int32Type);
922INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCSRMatrixEquality,
923 UInt32Type);
924INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCSRMatrixEquality, Int64Type);
925INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCSRMatrixEquality,
926 UInt64Type);
927
928template <typename ValueType>
929class TestFloatingSparseCSRMatrixEquality
930 : public TestSparseCSRMatrixEquality<ValueType> {};
931
932TYPED_TEST_SUITE_P(TestFloatingSparseCSRMatrixEquality);
933
934TYPED_TEST_P(TestFloatingSparseCSRMatrixEquality, TestEquality) {
935 using ValueType = TypeParam;
936 using c_value_type = typename ValueType::c_type;
937 static_assert(is_floating_type<ValueType>::value, "Float type is required");
938
939 std::shared_ptr<SparseCSRMatrix> st1, st2, st3;
940 ASSERT_OK_AND_ASSIGN(st1, SparseCSRMatrix::Make(*this->tensor1_));
941 ASSERT_OK_AND_ASSIGN(st2, SparseCSRMatrix::Make(*this->tensor2_));
942 ASSERT_OK_AND_ASSIGN(st3, SparseCSRMatrix::Make(*this->tensor1_));
943
944 ASSERT_TRUE(st1->Equals(*st1));
945 ASSERT_FALSE(st1->Equals(*st2));
946 ASSERT_TRUE(st1->Equals(*st3));
947
948 // sparse tensors with NaNs
949 const c_value_type nan_value = static_cast<c_value_type>(NAN);
950 this->values2_[13] = nan_value;
951 EXPECT_TRUE(std::isnan(this->tensor2_->Value({3, 1})));
952
953 std::shared_ptr<SparseCSRMatrix> st4;
954 ASSERT_OK_AND_ASSIGN(st4, SparseCSRMatrix::Make(*this->tensor2_));
955 EXPECT_FALSE(st4->Equals(*st4)); // same object
956 EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object
957
958 std::vector<c_value_type> values5 = this->values2_;
959 std::shared_ptr<SparseCSRMatrix> st5;
960 std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5);
961 NumericTensor<ValueType> tensor5(buffer5, this->shape_);
962 ASSERT_OK_AND_ASSIGN(st5, SparseCSRMatrix::Make(tensor5));
963 EXPECT_FALSE(st4->Equals(*st5)); // different memory
964 EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory
965}
966
967REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCSRMatrixEquality, TestEquality);
968
969INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCSRMatrixEquality, FloatType);
970INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCSRMatrixEquality,
971 DoubleType);
972
973template <typename IndexValueType>
974class TestSparseCSRMatrixForIndexValueType
975 : public TestSparseCSRMatrixBase<IndexValueType> {};
976
977TYPED_TEST_SUITE_P(TestSparseCSRMatrixForIndexValueType);
978
979TYPED_TEST_P(TestSparseCSRMatrixForIndexValueType, Make) {
980 using IndexValueType = TypeParam;
981 using c_index_value_type = typename IndexValueType::c_type;
982
983 // Sparse representation:
984 std::vector<c_index_value_type> indptr_values = {0, 2, 4, 6, 8, 10, 12};
985 std::vector<c_index_value_type> indices_values = {0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3};
986
987 std::vector<int64_t> indptr_shape = {7};
988 std::vector<int64_t> indices_shape = {12};
989
990 std::shared_ptr<SparseCSRIndex> si;
991 ASSERT_OK_AND_ASSIGN(
992 si, SparseCSRIndex::Make(TypeTraits<IndexValueType>::type_singleton(), indptr_shape,
993 indices_shape, Buffer::Wrap(indptr_values),
994 Buffer::Wrap(indices_values)));
995
996 std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16};
997 auto sparse_data = Buffer::Wrap(sparse_values);
998
999 std::shared_ptr<SparseCSRMatrix> sm;
1000
1001 // OK
1002 ASSERT_OK(
1003 SparseCSRMatrix::Make(si, int64(), sparse_data, this->shape_, this->dim_names_));
1004
1005 // OK with an empty dim_names
1006 ASSERT_OK(SparseCSRMatrix::Make(si, int64(), sparse_data, this->shape_, {}));
1007
1008 // invalid data type
1009 ASSERT_RAISES(Invalid,
1010 SparseCSRMatrix::Make(si, binary(), sparse_data, this->shape_, {}));
1011
1012 // empty shape
1013 ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {}, {}));
1014
1015 // 1D shape
1016 ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {24}, {}));
1017
1018 // negative items in shape
1019 ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {6, -4}, {}));
1020
1021 // sparse index and ndim (shape length) are inconsistent
1022 ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {4, 6}, {}));
1023
1024 // shape and dim_names are inconsistent
1025 ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, this->shape_,
1026 std::vector<std::string>{"foo"}));
1027}
1028
1029REGISTER_TYPED_TEST_SUITE_P(TestSparseCSRMatrixForIndexValueType, Make);
1030
1031INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestSparseCSRMatrixForIndexValueType, Int8Type);
1032INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestSparseCSRMatrixForIndexValueType,
1033 UInt8Type);
1034INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestSparseCSRMatrixForIndexValueType,
1035 Int16Type);
1036INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestSparseCSRMatrixForIndexValueType,
1037 UInt16Type);
1038INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestSparseCSRMatrixForIndexValueType,
1039 Int32Type);
1040INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestSparseCSRMatrixForIndexValueType,
1041 UInt32Type);
1042INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestSparseCSRMatrixForIndexValueType,
1043 Int64Type);
1044
1045TEST(TestSparseCSRMatrixForUInt64Index, Make) {
1046 std::vector<int64_t> dense_values = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
1047 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
1048 Tensor dense_tensor(uint64(), Buffer::Wrap(dense_values), {6, 4});
1049 ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(dense_tensor, uint64()));
1050}
1051
1052template <typename IndexValueType>
1053class TestSparseCSCMatrixBase : public TestSparseTensorBase<Int64Type> {
1054 public:
1055 void SetUp() {
1056 shape_ = {6, 4};
1057 dim_names_ = {"foo", "bar"};
1058
1059 // Dense representation:
1060 // [
1061 // 1 0 2 0
1062 // 0 3 0 4
1063 // 5 0 6 0
1064 // 0 11 0 12
1065 // 13 0 14 0
1066 // 0 15 0 16
1067 // ]
1068 dense_values_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
1069 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
1070 auto dense_data = Buffer::Wrap(dense_values_);
1071 NumericTensor<Int64Type> dense_tensor(dense_data, shape_, {}, dim_names_);
1072 ASSERT_OK_AND_ASSIGN(sparse_tensor_from_dense_,
1073 SparseCSCMatrix::Make(
1074 dense_tensor, TypeTraits<IndexValueType>::type_singleton()));
1075 }
1076
1077 protected:
1078 std::vector<int64_t> dense_values_;
1079 std::shared_ptr<SparseCSCMatrix> sparse_tensor_from_dense_;
1080};
1081
1082class TestSparseCSCMatrix : public TestSparseCSCMatrixBase<Int64Type> {};
1083
1084TEST_F(TestSparseCSCMatrix, CreationFromZeroTensor) {
1085 const auto dense_size =
1086 std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1),
1087 [](int64_t a, int64_t x) { return a * x; });
1088 std::vector<int64_t> dense_values(dense_size, 0);
1089 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero,
1090 Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_));
1091 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCSCMatrix> st_zero,
1092 SparseCSCMatrix::Make(*t_zero, int64()));
1093
1094 ASSERT_EQ(0, st_zero->non_zero_length());
1095 ASSERT_EQ(dense_size, st_zero->size());
1096
1097 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor());
1098 ASSERT_TRUE(t->Equals(*t_zero));
1099}
1100
1101TEST_F(TestSparseCSCMatrix, CreationFromNumericTensor2D) {
1102 std::shared_ptr<Buffer> buffer = Buffer::Wrap(this->dense_values_);
1103 NumericTensor<Int64Type> tensor(buffer, this->shape_);
1104
1105 std::shared_ptr<SparseCSCMatrix> st1;
1106 ASSERT_OK_AND_ASSIGN(st1, SparseCSCMatrix::Make(tensor));
1107
1108 auto st2 = this->sparse_tensor_from_dense_;
1109
1110 CheckSparseIndexFormatType(SparseTensorFormat::CSC, *st1);
1111
1112 ASSERT_EQ(12, st1->non_zero_length());
1113 ASSERT_TRUE(st1->is_mutable());
1114
1115 ASSERT_EQ(std::vector<std::string>({"foo", "bar"}), st2->dim_names());
1116 ASSERT_EQ("foo", st2->dim_name(0));
1117 ASSERT_EQ("bar", st2->dim_name(1));
1118
1119 ASSERT_EQ(std::vector<std::string>({}), st1->dim_names());
1120 ASSERT_EQ("", st1->dim_name(0));
1121 ASSERT_EQ("", st1->dim_name(1));
1122 ASSERT_EQ("", st1->dim_name(2));
1123
1124 const int64_t* raw_data = reinterpret_cast<const int64_t*>(st1->raw_data());
1125 AssertNumericDataEqual(raw_data, {1, 5, 13, 3, 11, 15, 2, 6, 14, 4, 12, 16});
1126
1127 auto si = internal::checked_pointer_cast<SparseCSCIndex>(st1->sparse_index());
1128 ASSERT_EQ(std::string("SparseCSCIndex"), si->ToString());
1129 ASSERT_EQ(1, si->indptr()->ndim());
1130 ASSERT_EQ(1, si->indices()->ndim());
1131
1132 const int64_t* indptr_begin =
1133 reinterpret_cast<const int64_t*>(si->indptr()->raw_data());
1134 std::vector<int64_t> indptr_values(indptr_begin,
1135 indptr_begin + si->indptr()->shape()[0]);
1136
1137 ASSERT_EQ(5, indptr_values.size());
1138 ASSERT_EQ(std::vector<int64_t>({0, 3, 6, 9, 12}), indptr_values);
1139
1140 const int64_t* indices_begin =
1141 reinterpret_cast<const int64_t*>(si->indices()->raw_data());
1142 std::vector<int64_t> indices_values(indices_begin,
1143 indices_begin + si->indices()->shape()[0]);
1144
1145 ASSERT_EQ(12, indices_values.size());
1146 ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 1, 3, 5, 0, 2, 4, 1, 3, 5}), indices_values);
1147}
1148
1149TEST_F(TestSparseCSCMatrix, CreationFromNonContiguousTensor) {
1150 std::vector<int64_t> values = {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0,
1151 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0,
1152 13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0};
1153 std::vector<int64_t> strides = {64, 16};
1154 std::shared_ptr<Buffer> buffer = Buffer::Wrap(values);
1155 Tensor tensor(int64(), buffer, this->shape_, strides);
1156
1157 std::shared_ptr<SparseCSCMatrix> st;
1158 ASSERT_OK_AND_ASSIGN(st, SparseCSCMatrix::Make(tensor));
1159
1160 ASSERT_EQ(12, st->non_zero_length());
1161 ASSERT_TRUE(st->is_mutable());
1162
1163 const int64_t* raw_data = reinterpret_cast<const int64_t*>(st->raw_data());
1164 AssertNumericDataEqual(raw_data, {1, 5, 13, 3, 11, 15, 2, 6, 14, 4, 12, 16});
1165
1166 auto si = internal::checked_pointer_cast<SparseCSCIndex>(st->sparse_index());
1167 ASSERT_EQ(1, si->indptr()->ndim());
1168 ASSERT_EQ(1, si->indices()->ndim());
1169
1170 const int64_t* indptr_begin =
1171 reinterpret_cast<const int64_t*>(si->indptr()->raw_data());
1172 std::vector<int64_t> indptr_values(indptr_begin,
1173 indptr_begin + si->indptr()->shape()[0]);
1174
1175 ASSERT_EQ(5, indptr_values.size());
1176 ASSERT_EQ(std::vector<int64_t>({0, 3, 6, 9, 12}), indptr_values);
1177
1178 const int64_t* indices_begin =
1179 reinterpret_cast<const int64_t*>(si->indices()->raw_data());
1180 std::vector<int64_t> indices_values(indices_begin,
1181 indices_begin + si->indices()->shape()[0]);
1182
1183 ASSERT_EQ(12, indices_values.size());
1184 ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 1, 3, 5, 0, 2, 4, 1, 3, 5}), indices_values);
1185
1186 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
1187}
1188
1189TEST_F(TestSparseCSCMatrix, TestToTensor) {
1190 std::vector<int64_t> values = {1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 1,
1191 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1};
1192 std::vector<int64_t> shape({6, 4});
1193 std::shared_ptr<Buffer> buffer = Buffer::Wrap(values);
1194 Tensor tensor(int64(), buffer, shape, {}, this->dim_names_);
1195
1196 std::shared_ptr<SparseCSCMatrix> sparse_tensor;
1197 ASSERT_OK_AND_ASSIGN(sparse_tensor, SparseCSCMatrix::Make(tensor));
1198
1199 ASSERT_EQ(7, sparse_tensor->non_zero_length());
1200 ASSERT_TRUE(sparse_tensor->is_mutable());
1201
1202 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dense_tensor, sparse_tensor->ToTensor());
1203 ASSERT_TRUE(tensor.Equals(*dense_tensor));
1204}
1205
1206template <typename ValueType>
1207class TestSparseCSCMatrixEquality : public TestSparseTensorBase<ValueType> {
1208 public:
1209 void SetUp() {
1210 shape_ = {6, 4};
1211 values1_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
1212 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
1213 values2_ = {9, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0,
1214 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16};
1215 auto buffer1 = Buffer::Wrap(values1_);
1216 auto buffer2 = Buffer::Wrap(values2_);
1217 DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, shape_).Value(&tensor1_));
1218 DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, shape_).Value(&tensor2_));
1219 }
1220
1221 protected:
1222 using TestSparseTensorBase<ValueType>::shape_;
1223 std::vector<typename ValueType::c_type> values1_;
1224 std::vector<typename ValueType::c_type> values2_;
1225 std::shared_ptr<NumericTensor<ValueType>> tensor1_;
1226 std::shared_ptr<NumericTensor<ValueType>> tensor2_;
1227};
1228
1229template <typename ValueType>
1230class TestIntegerSparseCSCMatrixEquality : public TestSparseCSCMatrixEquality<ValueType> {
1231};
1232
1233TYPED_TEST_SUITE_P(TestIntegerSparseCSCMatrixEquality);
1234
1235TYPED_TEST_P(TestIntegerSparseCSCMatrixEquality, TestEquality) {
1236 using ValueType = TypeParam;
1237 static_assert(is_integer_type<ValueType>::value, "Integer type is required");
1238
1239 std::shared_ptr<SparseCSCMatrix> st1, st2, st3;
1240 ASSERT_OK_AND_ASSIGN(st1, SparseCSCMatrix::Make(*this->tensor1_));
1241 ASSERT_OK_AND_ASSIGN(st2, SparseCSCMatrix::Make(*this->tensor2_));
1242 ASSERT_OK_AND_ASSIGN(st3, SparseCSCMatrix::Make(*this->tensor1_));
1243
1244 ASSERT_TRUE(st1->Equals(*st1));
1245 ASSERT_FALSE(st1->Equals(*st2));
1246 ASSERT_TRUE(st1->Equals(*st3));
1247}
1248
1249REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCSCMatrixEquality, TestEquality);
1250
1251INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCSCMatrixEquality, Int8Type);
1252INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCSCMatrixEquality, UInt8Type);
1253INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCSCMatrixEquality, Int16Type);
1254INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCSCMatrixEquality,
1255 UInt16Type);
1256INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCSCMatrixEquality, Int32Type);
1257INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCSCMatrixEquality,
1258 UInt32Type);
1259INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCSCMatrixEquality, Int64Type);
1260INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCSCMatrixEquality,
1261 UInt64Type);
1262
1263template <typename ValueType>
1264class TestFloatingSparseCSCMatrixEquality
1265 : public TestSparseCSCMatrixEquality<ValueType> {};
1266
1267TYPED_TEST_SUITE_P(TestFloatingSparseCSCMatrixEquality);
1268
1269TYPED_TEST_P(TestFloatingSparseCSCMatrixEquality, TestEquality) {
1270 using ValueType = TypeParam;
1271 using c_value_type = typename ValueType::c_type;
1272 static_assert(is_floating_type<ValueType>::value, "Float type is required");
1273
1274 std::shared_ptr<SparseCSCMatrix> st1, st2, st3;
1275 ASSERT_OK_AND_ASSIGN(st1, SparseCSCMatrix::Make(*this->tensor1_));
1276 ASSERT_OK_AND_ASSIGN(st2, SparseCSCMatrix::Make(*this->tensor2_));
1277 ASSERT_OK_AND_ASSIGN(st3, SparseCSCMatrix::Make(*this->tensor1_));
1278
1279 ASSERT_TRUE(st1->Equals(*st1));
1280 ASSERT_FALSE(st1->Equals(*st2));
1281 ASSERT_TRUE(st1->Equals(*st3));
1282
1283 // sparse tensors with NaNs
1284 const c_value_type nan_value = static_cast<c_value_type>(NAN);
1285 this->values2_[13] = nan_value;
1286 EXPECT_TRUE(std::isnan(this->tensor2_->Value({3, 1})));
1287
1288 std::shared_ptr<SparseCSCMatrix> st4;
1289 ASSERT_OK_AND_ASSIGN(st4, SparseCSCMatrix::Make(*this->tensor2_));
1290 EXPECT_FALSE(st4->Equals(*st4)); // same object
1291 EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object
1292
1293 std::vector<c_value_type> values5 = this->values2_;
1294 std::shared_ptr<SparseCSCMatrix> st5;
1295 std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5);
1296 NumericTensor<ValueType> tensor5(buffer5, this->shape_);
1297 ASSERT_OK_AND_ASSIGN(st5, SparseCSCMatrix::Make(tensor5));
1298 EXPECT_FALSE(st4->Equals(*st5)); // different memory
1299 EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory
1300}
1301
1302REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCSCMatrixEquality, TestEquality);
1303
1304INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCSCMatrixEquality, FloatType);
1305INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCSCMatrixEquality,
1306 DoubleType);
1307
1308template <typename ValueType>
1309class TestSparseCSFTensorEquality : public TestSparseTensorBase<ValueType> {
1310 public:
1311 void SetUp() {
1312 shape_ = {2, 3, 4, 5};
1313
1314 values1_[0][0][0][1] = 1;
1315 values1_[0][0][0][2] = 2;
1316 values1_[0][1][0][0] = 3;
1317 values1_[0][1][0][2] = 4;
1318 values1_[0][1][1][0] = 5;
1319 values1_[1][1][1][0] = 6;
1320 values1_[1][1][1][1] = 7;
1321 values1_[1][1][1][2] = 8;
1322
1323 length_ = sizeof(values1_);
1324
1325 values2_[0][0][0][1] = 1;
1326 values2_[0][0][0][2] = 2;
1327 values2_[0][1][0][0] = 3;
1328 values2_[0][1][0][2] = 9;
1329 values2_[0][1][1][0] = 5;
1330 values2_[1][1][1][0] = 6;
1331 values2_[1][1][1][1] = 7;
1332 values2_[1][1][1][2] = 8;
1333
1334 auto buffer1 = Buffer::Wrap(values1_, length_);
1335 auto buffer2 = Buffer::Wrap(values2_, length_);
1336
1337 DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, shape_).Value(&tensor1_));
1338 DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, shape_).Value(&tensor2_));
1339 }
1340
1341 protected:
1342 using TestSparseTensorBase<ValueType>::shape_;
1343 typename ValueType::c_type values1_[2][3][4][5] = {};
1344 typename ValueType::c_type values2_[2][3][4][5] = {};
1345 int64_t length_;
1346 std::shared_ptr<NumericTensor<ValueType>> tensor1_;
1347 std::shared_ptr<NumericTensor<ValueType>> tensor2_;
1348};
1349
1350template <typename ValueType>
1351class TestIntegerSparseCSFTensorEquality : public TestSparseCSFTensorEquality<ValueType> {
1352};
1353
1354TYPED_TEST_SUITE_P(TestIntegerSparseCSFTensorEquality);
1355
1356TYPED_TEST_P(TestIntegerSparseCSFTensorEquality, TestEquality) {
1357 using ValueType = TypeParam;
1358 static_assert(is_integer_type<ValueType>::value, "Integer type is required");
1359
1360 std::shared_ptr<SparseCSFTensor> st1, st2, st3;
1361 ASSERT_OK_AND_ASSIGN(st1, SparseCSFTensor::Make(*this->tensor1_));
1362 ASSERT_OK_AND_ASSIGN(st2, SparseCSFTensor::Make(*this->tensor2_));
1363 ASSERT_OK_AND_ASSIGN(st3, SparseCSFTensor::Make(*this->tensor1_));
1364
1365 ASSERT_TRUE(st1->Equals(*st1));
1366 ASSERT_FALSE(st1->Equals(*st2));
1367 ASSERT_TRUE(st1->Equals(*st3));
1368}
1369
1370REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCSFTensorEquality, TestEquality);
1371
1372INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCSFTensorEquality, Int8Type);
1373INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCSFTensorEquality, UInt8Type);
1374INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCSFTensorEquality, Int16Type);
1375INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCSFTensorEquality,
1376 UInt16Type);
1377INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCSFTensorEquality, Int32Type);
1378INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCSFTensorEquality,
1379 UInt32Type);
1380INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCSFTensorEquality, Int64Type);
1381INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCSFTensorEquality,
1382 UInt64Type);
1383
1384template <typename ValueType>
1385class TestFloatingSparseCSFTensorEquality
1386 : public TestSparseCSFTensorEquality<ValueType> {};
1387
1388TYPED_TEST_SUITE_P(TestFloatingSparseCSFTensorEquality);
1389
1390TYPED_TEST_P(TestFloatingSparseCSFTensorEquality, TestEquality) {
1391 using ValueType = TypeParam;
1392 using c_value_type = typename ValueType::c_type;
1393 static_assert(is_floating_type<ValueType>::value, "Floating type is required");
1394
1395 std::shared_ptr<SparseCSFTensor> st1, st2, st3;
1396 ASSERT_OK_AND_ASSIGN(st1, SparseCSFTensor::Make(*this->tensor1_));
1397 ASSERT_OK_AND_ASSIGN(st2, SparseCSFTensor::Make(*this->tensor2_));
1398 ASSERT_OK_AND_ASSIGN(st3, SparseCSFTensor::Make(*this->tensor1_));
1399
1400 ASSERT_TRUE(st1->Equals(*st1));
1401 ASSERT_FALSE(st1->Equals(*st2));
1402 ASSERT_TRUE(st1->Equals(*st3));
1403
1404 // sparse tensors with NaNs
1405 const c_value_type nan_value = static_cast<c_value_type>(NAN);
1406 this->values2_[1][1][1][1] = nan_value;
1407 EXPECT_TRUE(std::isnan(this->tensor2_->Value({1, 1, 1, 1})));
1408
1409 std::shared_ptr<SparseCSFTensor> st4;
1410 ASSERT_OK_AND_ASSIGN(st4, SparseCSFTensor::Make(*this->tensor2_));
1411 EXPECT_FALSE(st4->Equals(*st4)); // same object
1412 EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object
1413
1414 c_value_type values5[2][3][4][5] = {};
1415 std::copy_n(&this->values2_[0][0][0][0], this->length_ / sizeof(c_value_type),
1416 &values5[0][0][0][0]);
1417 std::shared_ptr<SparseCSFTensor> st5;
1418 std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5, sizeof(values5));
1419 NumericTensor<ValueType> tensor5(buffer5, this->shape_);
1420 ASSERT_OK_AND_ASSIGN(st5, SparseCSFTensor::Make(tensor5));
1421 EXPECT_FALSE(st4->Equals(*st5)); // different memory
1422 EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory
1423}
1424
1425REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCSFTensorEquality, TestEquality);
1426
1427INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCSFTensorEquality, FloatType);
1428INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCSFTensorEquality,
1429 DoubleType);
1430
1431template <typename IndexValueType>
1432class TestSparseCSFTensorBase : public TestSparseTensorBase<Int16Type> {
1433 public:
1434 void SetUp() {
1435 dim_names_ = {"a", "b", "c", "d"};
1436 shape_ = {2, 3, 4, 5};
1437
1438 dense_values_[0][0][0][1] = 1;
1439 dense_values_[0][0][0][2] = 2;
1440 dense_values_[0][1][0][0] = 3;
1441 dense_values_[0][1][0][2] = 4;
1442 dense_values_[0][1][1][0] = 5;
1443 dense_values_[1][1][1][0] = 6;
1444 dense_values_[1][1][1][1] = 7;
1445 dense_values_[1][1][1][2] = 8;
1446
1447 auto dense_buffer = Buffer::Wrap(dense_values_, sizeof(dense_values_));
1448 Tensor dense_tensor_(int16(), dense_buffer, shape_, {}, dim_names_);
1449 ASSERT_OK_AND_ASSIGN(
1450 sparse_tensor_from_dense_,
1451 SparseCSFTensor::Make(dense_tensor_,
1452 TypeTraits<IndexValueType>::type_singleton()));
1453 }
1454
1455 protected:
1456 std::vector<int64_t> shape_;
1457 std::vector<std::string> dim_names_;
1458 int16_t dense_values_[2][3][4][5] = {};
1459 std::shared_ptr<SparseCSFTensor> sparse_tensor_from_dense_;
1460};
1461
1462class TestSparseCSFTensor : public TestSparseCSFTensorBase<Int64Type> {};
1463
1464TEST_F(TestSparseCSFTensor, CreationFromZeroTensor) {
1465 const auto dense_size =
1466 std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1),
1467 [](int64_t a, int64_t x) { return a * x; });
1468 std::vector<int64_t> dense_values(dense_size, 0);
1469 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero,
1470 Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_));
1471 ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCSFTensor> st_zero,
1472 SparseCSFTensor::Make(*t_zero, int64()));
1473
1474 ASSERT_EQ(0, st_zero->non_zero_length());
1475 ASSERT_EQ(dense_size, st_zero->size());
1476
1477 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor());
1478 ASSERT_TRUE(t->Equals(*t_zero));
1479}
1480
1481template <typename IndexValueType>
1482class TestSparseCSFTensorForIndexValueType
1483 : public TestSparseCSFTensorBase<IndexValueType> {
1484 protected:
1485 std::shared_ptr<SparseCSFIndex> MakeSparseCSFIndex(
1486 const std::vector<int64_t>& axis_order,
1487 const std::vector<std::vector<typename IndexValueType::c_type>>& indptr_values,
1488 const std::vector<std::vector<typename IndexValueType::c_type>>& indices_values)
1489 const {
1490 int64_t ndim = axis_order.size();
1491 std::vector<std::shared_ptr<Tensor>> indptr(ndim - 1);
1492 std::vector<std::shared_ptr<Tensor>> indices(ndim);
1493
1494 for (int64_t i = 0; i < ndim - 1; ++i) {
1495 indptr[i] = std::make_shared<Tensor>(
1496 TypeTraits<IndexValueType>::type_singleton(), Buffer::Wrap(indptr_values[i]),
1497 std::vector<int64_t>({static_cast<int64_t>(indptr_values[i].size())}));
1498 }
1499 for (int64_t i = 0; i < ndim; ++i) {
1500 indices[i] = std::make_shared<Tensor>(
1501 TypeTraits<IndexValueType>::type_singleton(), Buffer::Wrap(indices_values[i]),
1502 std::vector<int64_t>({static_cast<int64_t>(indices_values[i].size())}));
1503 }
1504 return std::make_shared<SparseCSFIndex>(indptr, indices, axis_order);
1505 }
1506
1507 template <typename CValueType>
1508 std::shared_ptr<SparseCSFTensor> MakeSparseTensor(
1509 const std::shared_ptr<SparseCSFIndex>& si, std::vector<CValueType>& sparse_values,
1510 const std::vector<int64_t>& shape,
1511 const std::vector<std::string>& dim_names) const {
1512 auto data_buffer = Buffer::Wrap(sparse_values);
1513 return std::make_shared<SparseCSFTensor>(
1514 si, CTypeTraits<CValueType>::type_singleton(), data_buffer, shape, dim_names);
1515 }
1516};
1517
1518TYPED_TEST_SUITE_P(TestSparseCSFTensorForIndexValueType);
1519
1520TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestCreateSparseTensor) {
1521 using IndexValueType = TypeParam;
1522 using c_index_value_type = typename IndexValueType::c_type;
1523
1524 std::vector<int64_t> shape = {2, 3, 4, 5};
1525 std::vector<std::string> dim_names = {"a", "b", "c", "d"};
1526 std::vector<int64_t> axis_order = {0, 1, 2, 3};
1527 std::vector<int16_t> sparse_values = {1, 2, 3, 4, 5, 6, 7, 8};
1528 std::vector<std::vector<c_index_value_type>> indptr_values = {
1529 {0, 2, 3}, {0, 1, 3, 4}, {0, 2, 4, 5, 8}};
1530 std::vector<std::vector<c_index_value_type>> indices_values = {
1531 {0, 1}, {0, 1, 1}, {0, 0, 1, 1}, {1, 2, 0, 2, 0, 0, 1, 2}};
1532
1533 auto si = this->MakeSparseCSFIndex(axis_order, indptr_values, indices_values);
1534 auto st = this->MakeSparseTensor(si, sparse_values, shape, dim_names);
1535
1536 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
1537}
1538
1539TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestTensorToSparseTensor) {
1540 std::vector<std::string> dim_names = {"a", "b", "c", "d"};
1541 ASSERT_EQ(8, this->sparse_tensor_from_dense_->non_zero_length());
1542 ASSERT_TRUE(this->sparse_tensor_from_dense_->is_mutable());
1543 ASSERT_EQ(dim_names, this->sparse_tensor_from_dense_->dim_names());
1544}
1545
1546TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestSparseTensorToTensor) {
1547 std::vector<int64_t> shape = {2, 3, 4, 5};
1548 auto dense_buffer = Buffer::Wrap(this->dense_values_, sizeof(this->dense_values_));
1549 Tensor dense_tensor(int16(), dense_buffer, shape, {}, this->dim_names_);
1550
1551 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dt,
1552 this->sparse_tensor_from_dense_->ToTensor());
1553 ASSERT_TRUE(dense_tensor.Equals(*dt));
1554 ASSERT_EQ(dense_tensor.dim_names(), dt->dim_names());
1555}
1556
1557TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestRoundTrip) {
1558 using IndexValueType = TypeParam;
1559
1560 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dt,
1561 this->sparse_tensor_from_dense_->ToTensor());
1562 std::shared_ptr<SparseCSFTensor> st;
1563 ASSERT_OK_AND_ASSIGN(
1564 st, SparseCSFTensor::Make(*dt, TypeTraits<IndexValueType>::type_singleton()));
1565
1566 ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_));
1567}
1568
1569TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestAlternativeAxisOrder) {
1570 using IndexValueType = TypeParam;
1571 using c_index_value_type = typename IndexValueType::c_type;
1572
1573 std::vector<int16_t> dense_values = {1, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0,
1574 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5};
1575 std::vector<int64_t> shape = {4, 6};
1576 std::vector<std::string> dim_names = {"a", "b"};
1577 std::shared_ptr<Buffer> dense_buffer = Buffer::Wrap(dense_values);
1578 Tensor tensor(int16(), dense_buffer, shape, {}, dim_names);
1579
1580 // Axis order 1
1581 std::vector<int64_t> axis_order_1 = {0, 1};
1582 std::vector<int16_t> sparse_values_1 = {1, 3, 2, 4, 5};
1583 std::vector<std::vector<c_index_value_type>> indptr_values_1 = {{0, 2, 3, 5}};
1584 std::vector<std::vector<c_index_value_type>> indices_values_1 = {{0, 1, 3},
1585 {0, 3, 1, 3, 5}};
1586 auto si_1 = this->MakeSparseCSFIndex(axis_order_1, indptr_values_1, indices_values_1);
1587 auto st_1 = this->MakeSparseTensor(si_1, sparse_values_1, shape, dim_names);
1588
1589 // Axis order 2
1590 std::vector<int64_t> axis_order_2 = {1, 0};
1591 std::vector<int16_t> sparse_values_2 = {1, 2, 3, 4, 5};
1592 std::vector<std::vector<c_index_value_type>> indptr_values_2 = {{0, 1, 2, 4, 5}};
1593 std::vector<std::vector<c_index_value_type>> indices_values_2 = {{0, 1, 3, 5},
1594 {0, 1, 0, 3, 3}};
1595 auto si_2 = this->MakeSparseCSFIndex(axis_order_2, indptr_values_2, indices_values_2);
1596 auto st_2 = this->MakeSparseTensor(si_2, sparse_values_2, shape, dim_names);
1597
1598 std::shared_ptr<Tensor> dt_1, dt_2;
1599 ASSERT_OK_AND_ASSIGN(dt_1, st_1->ToTensor());
1600 ASSERT_OK_AND_ASSIGN(dt_2, st_2->ToTensor());
1601
1602 ASSERT_FALSE(st_1->Equals(*st_2));
1603 ASSERT_TRUE(dt_1->Equals(*dt_2));
1604 ASSERT_TRUE(dt_1->Equals(tensor));
1605}
1606
1607TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestNonAscendingShape) {
1608 using IndexValueType = TypeParam;
1609 using c_index_value_type = typename IndexValueType::c_type;
1610
1611 std::vector<int64_t> shape = {5, 2, 3, 4};
1612 int16_t dense_values[5][2][3][4] = {}; // zero-initialized
1613 dense_values[0][0][0][1] = 1;
1614 dense_values[0][0][0][2] = 2;
1615 dense_values[0][1][0][0] = 3;
1616 dense_values[0][1][0][2] = 4;
1617 dense_values[0][1][1][0] = 5;
1618 dense_values[1][1][1][0] = 6;
1619 dense_values[1][1][1][1] = 7;
1620 dense_values[1][1][1][2] = 8;
1621 auto dense_buffer = Buffer::Wrap(dense_values, sizeof(dense_values));
1622 Tensor dense_tensor(int16(), dense_buffer, shape, {}, this->dim_names_);
1623
1624 std::shared_ptr<SparseCSFTensor> sparse_tensor;
1625 ASSERT_OK_AND_ASSIGN(
1626 sparse_tensor,
1627 SparseCSFTensor::Make(dense_tensor, TypeTraits<IndexValueType>::type_singleton()));
1628
1629 std::vector<std::vector<c_index_value_type>> indptr_values = {
1630 {0, 1, 3}, {0, 2, 4, 7}, {0, 1, 2, 3, 4, 6, 7, 8}};
1631 std::vector<std::vector<c_index_value_type>> indices_values = {
1632 {0, 1}, {0, 0, 1}, {1, 2, 0, 2, 0, 1, 2}, {0, 0, 0, 0, 0, 1, 1, 1}};
1633 std::vector<int64_t> axis_order = {1, 2, 3, 0};
1634 std::vector<int16_t> sparse_values = {1, 2, 3, 4, 5, 6, 7, 8};
1635 auto si = this->MakeSparseCSFIndex(axis_order, indptr_values, indices_values);
1636 auto st = this->MakeSparseTensor(si, sparse_values, shape, this->dim_names_);
1637
1638 ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dt, st->ToTensor());
1639 ASSERT_TRUE(dt->Equals(dense_tensor));
1640 ASSERT_TRUE(st->Equals(*sparse_tensor));
1641}
1642
1643REGISTER_TYPED_TEST_SUITE_P(TestSparseCSFTensorForIndexValueType, TestCreateSparseTensor,
1644 TestTensorToSparseTensor, TestSparseTensorToTensor,
1645 TestAlternativeAxisOrder, TestNonAscendingShape,
1646 TestRoundTrip);
1647
1648INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestSparseCSFTensorForIndexValueType, Int8Type);
1649INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestSparseCSFTensorForIndexValueType,
1650 UInt8Type);
1651INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestSparseCSFTensorForIndexValueType,
1652 Int16Type);
1653INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestSparseCSFTensorForIndexValueType,
1654 UInt16Type);
1655INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestSparseCSFTensorForIndexValueType,
1656 Int32Type);
1657INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestSparseCSFTensorForIndexValueType,
1658 UInt32Type);
1659INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestSparseCSFTensorForIndexValueType,
1660 Int64Type);
1661
1662TEST(TestSparseCSFMatrixForUInt64Index, Make) {
1663 int16_t dense_values[2][3][4][5] = {};
1664 dense_values[0][0][0][1] = 1;
1665 dense_values[0][0][0][2] = 2;
1666 dense_values[0][1][0][0] = 3;
1667 dense_values[0][1][0][2] = 4;
1668 dense_values[0][1][1][0] = 5;
1669 dense_values[1][1][1][0] = 6;
1670 dense_values[1][1][1][1] = 7;
1671 dense_values[1][1][1][2] = 8;
1672
1673 Tensor dense_tensor(uint64(), Buffer::Wrap(dense_values, sizeof(dense_values)),
1674 {2, 3, 4, 5});
1675 ASSERT_RAISES(Invalid, SparseCSFTensor::Make(dense_tensor, uint64()));
1676}
1677
1678} // namespace arrow