| // Licensed to the Apache Software Foundation (ASF) under one |
| // or more contributor license agreements. See the NOTICE file |
| // distributed with this work for additional information |
| // regarding copyright ownership. The ASF licenses this file |
| // to you under the Apache License, Version 2.0 (the |
| // "License"); you may not use this file except in compliance |
| // with the License. You may obtain a copy of the License at |
| // |
| // http://www.apache.org/licenses/LICENSE-2.0 |
| // |
| // Unless required by applicable law or agreed to in writing, |
| // software distributed under the License is distributed on an |
| // "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| // KIND, either express or implied. See the License for the |
| // specific language governing permissions and limitations |
| // under the License. |
| |
| // Unit tests for DataType (and subclasses), Field, and Schema |
| |
| #include <cmath> |
| #include <cstdint> |
| #include <memory> |
| #include <numeric> |
| #include <string> |
| #include <vector> |
| |
| #include <iostream> |
| |
| #include <gtest/gtest.h> |
| |
| #include "arrow/sparse_tensor.h" |
| #include "arrow/testing/gtest_util.h" |
| #include "arrow/testing/util.h" |
| #include "arrow/type.h" |
| #include "arrow/util/logging_internal.h" |
| #include "arrow/util/sort_internal.h" |
| |
| namespace arrow { |
| |
| static inline void CheckSparseIndexFormatType(SparseTensorFormat::type expected, |
| const SparseTensor& sparse_tensor) { |
| ASSERT_EQ(expected, sparse_tensor.format_id()); |
| ASSERT_EQ(expected, sparse_tensor.sparse_index()->format_id()); |
| } |
| |
| static inline void AssertCOOIndex(const std::shared_ptr<Tensor>& sidx, const int64_t nth, |
| const std::vector<int64_t>& expected_values) { |
| int64_t n = static_cast<int64_t>(expected_values.size()); |
| for (int64_t i = 0; i < n; ++i) { |
| ASSERT_EQ(expected_values[i], sidx->Value<Int64Type>({nth, i})); |
| } |
| } |
| |
| //----------------------------------------------------------------------------- |
| // SparseCOOIndex |
| |
| TEST(TestSparseCOOIndex, MakeRowMajorCanonical) { |
| std::vector<int32_t> values = {0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 1, 3, 0, 2, 0, 0, 2, 2, |
| 1, 0, 1, 1, 0, 3, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3}; |
| auto data = Buffer::Wrap(values); |
| std::vector<int64_t> shape = {12, 3}; |
| std::vector<int64_t> strides = {3 * sizeof(int32_t), sizeof(int32_t)}; // Row-major |
| |
| // OK |
| std::shared_ptr<SparseCOOIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data)); |
| ASSERT_EQ(shape, si->indices()->shape()); |
| ASSERT_EQ(strides, si->indices()->strides()); |
| ASSERT_EQ(data->data(), si->indices()->raw_data()); |
| ASSERT_TRUE(si->is_canonical()); |
| |
| // Non-integer type |
| auto res = SparseCOOIndex::Make(float32(), shape, strides, data); |
| ASSERT_RAISES(TypeError, res); |
| |
| // Non-matrix indices |
| res = SparseCOOIndex::Make(int32(), {4, 3, 4}, strides, data); |
| ASSERT_RAISES(Invalid, res); |
| |
| // Non-contiguous indices |
| res = SparseCOOIndex::Make(int32(), {6, 3}, {6 * sizeof(int32_t), 2 * sizeof(int32_t)}, |
| data); |
| ASSERT_RAISES(Invalid, res); |
| |
| // Make from sparse tensor properties |
| // (shape is arbitrary 3-dim, non-zero length = 12) |
| ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), {99, 99, 99}, 12, data)); |
| ASSERT_EQ(shape, si->indices()->shape()); |
| ASSERT_EQ(strides, si->indices()->strides()); |
| ASSERT_EQ(data->data(), si->indices()->raw_data()); |
| } |
| |
| TEST(TestSparseCOOIndex, MakeRowMajorNonCanonical) { |
| std::vector<int32_t> values = {0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 1, 3, 0, 2, 0, 1, 0, 1, |
| 0, 2, 2, 1, 0, 3, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3}; |
| auto data = Buffer::Wrap(values); |
| std::vector<int64_t> shape = {12, 3}; |
| std::vector<int64_t> strides = {3 * sizeof(int32_t), sizeof(int32_t)}; // Row-major |
| |
| // OK |
| std::shared_ptr<SparseCOOIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data)); |
| ASSERT_EQ(shape, si->indices()->shape()); |
| ASSERT_EQ(strides, si->indices()->strides()); |
| ASSERT_EQ(data->data(), si->indices()->raw_data()); |
| ASSERT_FALSE(si->is_canonical()); |
| } |
| |
| TEST(TestSparseCOOIndex, MakeColumnMajorCanonical) { |
| std::vector<int32_t> values = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 2, 2, |
| 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}; |
| auto data = Buffer::Wrap(values); |
| std::vector<int64_t> shape = {12, 3}; |
| std::vector<int64_t> strides = {sizeof(int32_t), 12 * sizeof(int32_t)}; // Column-major |
| |
| // OK |
| std::shared_ptr<SparseCOOIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data)); |
| ASSERT_EQ(shape, si->indices()->shape()); |
| ASSERT_EQ(strides, si->indices()->strides()); |
| ASSERT_EQ(data->data(), si->indices()->raw_data()); |
| ASSERT_TRUE(si->is_canonical()); |
| } |
| |
| TEST(TestSparseCOOIndex, MakeColumnMajorNonCanonical) { |
| std::vector<int32_t> values = {0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 2, 0, |
| 2, 0, 1, 1, 2, 2, 0, 2, 1, 3, 0, 1, 2, 3, 0, 2, 1, 3}; |
| auto data = Buffer::Wrap(values); |
| std::vector<int64_t> shape = {12, 3}; |
| std::vector<int64_t> strides = {sizeof(int32_t), 12 * sizeof(int32_t)}; // Column-major |
| |
| // OK |
| std::shared_ptr<SparseCOOIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data)); |
| ASSERT_EQ(shape, si->indices()->shape()); |
| ASSERT_EQ(strides, si->indices()->strides()); |
| ASSERT_EQ(data->data(), si->indices()->raw_data()); |
| ASSERT_FALSE(si->is_canonical()); |
| } |
| |
| TEST(TestSparseCOOIndex, MakeEmptyIndex) { |
| std::vector<int32_t> values = {}; |
| auto data = Buffer::Wrap(values); |
| std::vector<int64_t> shape = {0, 3}; |
| std::vector<int64_t> strides = {sizeof(int32_t), sizeof(int32_t)}; // Empty strides |
| |
| // OK |
| std::shared_ptr<SparseCOOIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCOOIndex::Make(int32(), shape, strides, data)); |
| ASSERT_EQ(shape, si->indices()->shape()); |
| ASSERT_EQ(strides, si->indices()->strides()); |
| ASSERT_EQ(data->data(), si->indices()->raw_data()); |
| ASSERT_TRUE(si->is_canonical()); |
| } |
| |
| TEST(TestSparseCSRIndex, Make) { |
| std::vector<int32_t> indptr_values = {0, 2, 4, 6, 8, 10, 12}; |
| std::vector<int32_t> indices_values = {0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}; |
| auto indptr_data = Buffer::Wrap(indptr_values); |
| auto indices_data = Buffer::Wrap(indices_values); |
| std::vector<int64_t> indptr_shape = {7}; |
| std::vector<int64_t> indices_shape = {12}; |
| |
| // OK |
| std::shared_ptr<SparseCSRIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCSRIndex::Make(int32(), indptr_shape, indices_shape, |
| indptr_data, indices_data)); |
| ASSERT_EQ(indptr_shape, si->indptr()->shape()); |
| ASSERT_EQ(indptr_data->data(), si->indptr()->raw_data()); |
| ASSERT_EQ(indices_shape, si->indices()->shape()); |
| ASSERT_EQ(indices_data->data(), si->indices()->raw_data()); |
| ASSERT_EQ(std::string("SparseCSRIndex"), si->ToString()); |
| |
| // Non-integer type |
| auto res = SparseCSRIndex::Make(float32(), indptr_shape, indices_shape, indptr_data, |
| indices_data); |
| ASSERT_RAISES(TypeError, res); |
| |
| // Non-vector indptr shape |
| ASSERT_RAISES(Invalid, SparseCSRIndex::Make(int32(), {1, 2}, indices_shape, indptr_data, |
| indices_data)); |
| |
| // Non-vector indices shape |
| ASSERT_RAISES(Invalid, SparseCSRIndex::Make(int32(), indptr_shape, {1, 2}, indptr_data, |
| indices_data)); |
| } |
| |
| TEST(TestSparseCSCIndex, Make) { |
| std::vector<int32_t> indptr_values = {0, 2, 4, 6, 8, 10, 12}; |
| std::vector<int32_t> indices_values = {0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}; |
| auto indptr_data = Buffer::Wrap(indptr_values); |
| auto indices_data = Buffer::Wrap(indices_values); |
| std::vector<int64_t> indptr_shape = {7}; |
| std::vector<int64_t> indices_shape = {12}; |
| |
| // OK |
| std::shared_ptr<SparseCSCIndex> si; |
| ASSERT_OK_AND_ASSIGN(si, SparseCSCIndex::Make(int32(), indptr_shape, indices_shape, |
| indptr_data, indices_data)); |
| ASSERT_EQ(indptr_shape, si->indptr()->shape()); |
| ASSERT_EQ(indptr_data->data(), si->indptr()->raw_data()); |
| ASSERT_EQ(indices_shape, si->indices()->shape()); |
| ASSERT_EQ(indices_data->data(), si->indices()->raw_data()); |
| ASSERT_EQ(std::string("SparseCSCIndex"), si->ToString()); |
| |
| // Non-integer type |
| ASSERT_RAISES(TypeError, SparseCSCIndex::Make(float32(), indptr_shape, indices_shape, |
| indptr_data, indices_data)); |
| |
| // Non-vector indptr shape |
| ASSERT_RAISES(Invalid, SparseCSCIndex::Make(int32(), {1, 2}, indices_shape, indptr_data, |
| indices_data)); |
| |
| // Non-vector indices shape |
| ASSERT_RAISES(Invalid, SparseCSCIndex::Make(int32(), indptr_shape, {1, 2}, indptr_data, |
| indices_data)); |
| } |
| |
| template <typename ValueType> |
| class TestSparseTensorBase : public ::testing::Test { |
| protected: |
| std::vector<int64_t> shape_; |
| std::vector<std::string> dim_names_; |
| }; |
| |
| //----------------------------------------------------------------------------- |
| // SparseCOOTensor |
| |
| template <typename IndexValueType, typename ValueType = Int64Type> |
| class TestSparseCOOTensorBase : public TestSparseTensorBase<ValueType> { |
| public: |
| using c_value_type = typename ValueType::c_type; |
| |
| void SetUp() { |
| shape_ = {2, 3, 4}; |
| dim_names_ = {"foo", "bar", "baz"}; |
| |
| // Dense representation: |
| // [ |
| // [ |
| // 1 0 2 0 |
| // 0 3 0 4 |
| // 5 0 6 0 |
| // ], |
| // [ |
| // 0 11 0 12 |
| // 13 0 14 0 |
| // 0 15 0 16 |
| // ] |
| // ] |
| dense_values_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| auto dense_data = Buffer::Wrap(dense_values_); |
| NumericTensor<ValueType> dense_tensor(dense_data, shape_, {}, dim_names_); |
| ASSERT_OK_AND_ASSIGN(sparse_tensor_from_dense_, |
| SparseCOOTensor::Make( |
| dense_tensor, TypeTraits<IndexValueType>::type_singleton())); |
| } |
| |
| protected: |
| using TestSparseTensorBase<ValueType>::shape_; |
| using TestSparseTensorBase<ValueType>::dim_names_; |
| std::vector<c_value_type> dense_values_; |
| std::shared_ptr<SparseCOOTensor> sparse_tensor_from_dense_; |
| }; |
| |
| class TestSparseCOOTensor : public TestSparseCOOTensorBase<Int64Type> {}; |
| |
| TEST_F(TestSparseCOOTensor, CreationEmptyTensor) { |
| SparseCOOTensor st1(int64(), this->shape_); |
| SparseCOOTensor st2(int64(), this->shape_, this->dim_names_); |
| |
| ASSERT_EQ(0, st1.non_zero_length()); |
| ASSERT_EQ(0, st2.non_zero_length()); |
| |
| ASSERT_EQ(24, st1.size()); |
| ASSERT_EQ(24, st2.size()); |
| |
| ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st2.dim_names()); |
| ASSERT_EQ("foo", st2.dim_name(0)); |
| ASSERT_EQ("bar", st2.dim_name(1)); |
| ASSERT_EQ("baz", st2.dim_name(2)); |
| |
| ASSERT_EQ(std::vector<std::string>({}), st1.dim_names()); |
| ASSERT_EQ("", st1.dim_name(0)); |
| ASSERT_EQ("", st1.dim_name(1)); |
| ASSERT_EQ("", st1.dim_name(2)); |
| } |
| |
| TEST_F(TestSparseCOOTensor, CreationFromZeroTensor) { |
| const auto dense_size = |
| std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1), |
| [](int64_t a, int64_t x) { return a * x; }); |
| std::vector<int64_t> dense_values(dense_size, 0); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero, |
| Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_)); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st_zero, |
| SparseCOOTensor::Make(*t_zero, int64())); |
| |
| ASSERT_EQ(0, st_zero->non_zero_length()); |
| ASSERT_EQ(dense_size, st_zero->size()); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor()); |
| ASSERT_TRUE(t->Equals(*t_zero)); |
| } |
| |
| TEST_F(TestSparseCOOTensor, CreationFromNumericTensor) { |
| auto st = this->sparse_tensor_from_dense_; |
| CheckSparseIndexFormatType(SparseTensorFormat::COO, *st); |
| |
| ASSERT_EQ(12, st->non_zero_length()); |
| ASSERT_TRUE(st->is_mutable()); |
| |
| auto* raw_data = reinterpret_cast<const int64_t*>(st->raw_data()); |
| AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}); |
| |
| auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index()); |
| ASSERT_EQ(std::string("SparseCOOIndex"), si->ToString()); |
| ASSERT_TRUE(si->is_canonical()); |
| |
| std::shared_ptr<Tensor> sidx = si->indices(); |
| ASSERT_EQ(std::vector<int64_t>({12, 3}), sidx->shape()); |
| ASSERT_TRUE(sidx->is_row_major()); |
| |
| AssertCOOIndex(sidx, 0, {0, 0, 0}); |
| AssertCOOIndex(sidx, 1, {0, 0, 2}); |
| AssertCOOIndex(sidx, 2, {0, 1, 1}); |
| AssertCOOIndex(sidx, 10, {1, 2, 1}); |
| AssertCOOIndex(sidx, 11, {1, 2, 3}); |
| } |
| |
| TEST_F(TestSparseCOOTensor, CreationFromNumericTensor1D) { |
| auto dense_data = Buffer::Wrap(this->dense_values_); |
| std::vector<int64_t> dense_shape({static_cast<int64_t>(this->dense_values_.size())}); |
| NumericTensor<Int64Type> dense_vector(dense_data, dense_shape); |
| |
| std::shared_ptr<SparseCOOTensor> st; |
| ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(dense_vector)); |
| |
| ASSERT_EQ(12, st->non_zero_length()); |
| ASSERT_TRUE(st->is_mutable()); |
| |
| auto* raw_data = reinterpret_cast<const int64_t*>(st->raw_data()); |
| AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}); |
| |
| auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index()); |
| ASSERT_TRUE(si->is_canonical()); |
| |
| auto sidx = si->indices(); |
| ASSERT_EQ(std::vector<int64_t>({12, 1}), sidx->shape()); |
| |
| AssertCOOIndex(sidx, 0, {0}); |
| AssertCOOIndex(sidx, 1, {2}); |
| AssertCOOIndex(sidx, 2, {5}); |
| AssertCOOIndex(sidx, 10, {21}); |
| AssertCOOIndex(sidx, 11, {23}); |
| } |
| |
| TEST_F(TestSparseCOOTensor, CreationFromTensor) { |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(this->dense_values_); |
| Tensor tensor(int64(), buffer, this->shape_, {}, this->dim_names_); |
| |
| std::shared_ptr<SparseCOOTensor> st; |
| ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(tensor)); |
| |
| ASSERT_EQ(12, st->non_zero_length()); |
| ASSERT_TRUE(st->is_mutable()); |
| |
| ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st->dim_names()); |
| ASSERT_EQ("foo", st->dim_name(0)); |
| ASSERT_EQ("bar", st->dim_name(1)); |
| ASSERT_EQ("baz", st->dim_name(2)); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| |
| auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index()); |
| ASSERT_TRUE(si->is_canonical()); |
| } |
| |
| TEST_F(TestSparseCOOTensor, CreationFromNonContiguousTensor) { |
| std::vector<int64_t> values = {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, |
| 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0, |
| 13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0}; |
| std::vector<int64_t> strides = {192, 64, 16}; |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(values); |
| Tensor tensor(int64(), buffer, this->shape_, strides); |
| |
| std::shared_ptr<SparseCOOTensor> st; |
| ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(tensor)); |
| |
| ASSERT_EQ(12, st->non_zero_length()); |
| ASSERT_TRUE(st->is_mutable()); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| |
| auto si = internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index()); |
| ASSERT_TRUE(si->is_canonical()); |
| } |
| |
| TEST_F(TestSparseCOOTensor, TestToTensor) { |
| std::vector<int64_t> values = {1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, |
| 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4}; |
| std::vector<int64_t> shape({4, 3, 2, 1}); |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(values); |
| Tensor tensor(int64(), buffer, shape, {}, this->dim_names_); |
| |
| std::shared_ptr<SparseCOOTensor> sparse_tensor; |
| ASSERT_OK_AND_ASSIGN(sparse_tensor, SparseCOOTensor::Make(tensor)); |
| |
| ASSERT_EQ(5, sparse_tensor->non_zero_length()); |
| ASSERT_TRUE(sparse_tensor->is_mutable()); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dense_tensor, sparse_tensor->ToTensor()); |
| ASSERT_TRUE(tensor.Equals(*dense_tensor)); |
| } |
| |
| template <typename ValueType> |
| class TestSparseCOOTensorEquality : public TestSparseTensorBase<ValueType> { |
| public: |
| void SetUp() { |
| shape_ = {2, 3, 4}; |
| values1_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| values2_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 0, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| auto buffer1 = Buffer::Wrap(values1_); |
| auto buffer2 = Buffer::Wrap(values2_); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, this->shape_).Value(&tensor1_)); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, this->shape_).Value(&tensor2_)); |
| } |
| |
| protected: |
| using TestSparseTensorBase<ValueType>::shape_; |
| std::vector<typename ValueType::c_type> values1_; |
| std::vector<typename ValueType::c_type> values2_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor1_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor2_; |
| }; |
| |
| template <typename ValueType> |
| class TestIntegerSparseCOOTensorEquality : public TestSparseCOOTensorEquality<ValueType> { |
| }; |
| |
| TYPED_TEST_SUITE_P(TestIntegerSparseCOOTensorEquality); |
| |
| TYPED_TEST_P(TestIntegerSparseCOOTensorEquality, TestEquality) { |
| using ValueType = TypeParam; |
| static_assert(is_integer_type<ValueType>::value, "Integer type is required"); |
| |
| std::shared_ptr<SparseCOOTensor> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCOOTensor::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCOOTensor::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCOOTensor::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCOOTensorEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCOOTensorEquality, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCOOTensorEquality, UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCOOTensorEquality, Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCOOTensorEquality, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCOOTensorEquality, Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCOOTensorEquality, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCOOTensorEquality, Int64Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCOOTensorEquality, |
| UInt64Type); |
| |
| template <typename ValueType> |
| class TestFloatingSparseCOOTensorEquality |
| : public TestSparseCOOTensorEquality<ValueType> {}; |
| |
| TYPED_TEST_SUITE_P(TestFloatingSparseCOOTensorEquality); |
| |
| TYPED_TEST_P(TestFloatingSparseCOOTensorEquality, TestEquality) { |
| using ValueType = TypeParam; |
| using c_value_type = typename ValueType::c_type; |
| static_assert(is_floating_type<ValueType>::value, "Float type is required"); |
| |
| std::shared_ptr<SparseCOOTensor> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCOOTensor::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCOOTensor::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCOOTensor::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| |
| // sparse tensors with NaNs |
| const c_value_type nan_value = static_cast<c_value_type>(NAN); |
| this->values2_[13] = nan_value; |
| EXPECT_TRUE(std::isnan(this->tensor2_->Value({1, 0, 1}))); |
| |
| std::shared_ptr<SparseCOOTensor> st4; |
| ASSERT_OK_AND_ASSIGN(st4, SparseCOOTensor::Make(*this->tensor2_)); |
| EXPECT_FALSE(st4->Equals(*st4)); // same object |
| EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object |
| |
| std::vector<c_value_type> values5 = this->values2_; |
| std::shared_ptr<SparseCOOTensor> st5; |
| std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5); |
| NumericTensor<ValueType> tensor5(buffer5, this->shape_); |
| ASSERT_OK_AND_ASSIGN(st5, SparseCOOTensor::Make(tensor5)); |
| EXPECT_FALSE(st4->Equals(*st5)); // different memory |
| EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCOOTensorEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCOOTensorEquality, FloatType); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCOOTensorEquality, |
| DoubleType); |
| |
| template <typename IndexValueType> |
| class TestSparseCOOTensorForIndexValueType |
| : public TestSparseCOOTensorBase<IndexValueType> { |
| public: |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| void SetUp() override { |
| TestSparseCOOTensorBase<IndexValueType>::SetUp(); |
| |
| // Sparse representation: |
| // idx[0] = [0 0 0 0 0 0 1 1 1 1 1 1] |
| // idx[1] = [0 0 1 1 2 2 0 0 1 1 2 2] |
| // idx[2] = [0 2 1 3 0 2 1 3 0 2 1 3] |
| // data = [1 2 3 4 5 6 11 12 13 14 15 16] |
| |
| coords_values_row_major_ = {0, 0, 0, 0, 0, 2, 0, 1, 1, 0, 1, 3, 0, 2, 0, 0, 2, 2, |
| 1, 0, 1, 1, 0, 3, 1, 1, 0, 1, 1, 2, 1, 2, 1, 1, 2, 3}; |
| |
| coords_values_col_major_ = {0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 2, 2, |
| 0, 0, 1, 1, 2, 2, 0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}; |
| } |
| |
| std::shared_ptr<DataType> index_data_type() const { |
| return TypeTraits<IndexValueType>::type_singleton(); |
| } |
| |
| protected: |
| std::vector<c_index_value_type> coords_values_row_major_; |
| std::vector<c_index_value_type> coords_values_col_major_; |
| |
| Result<std::shared_ptr<SparseCOOIndex>> MakeSparseCOOIndex( |
| const std::vector<int64_t>& shape, const std::vector<int64_t>& strides, |
| const std::vector<c_index_value_type>& values) const { |
| return SparseCOOIndex::Make(index_data_type(), shape, strides, Buffer::Wrap(values)); |
| } |
| |
| template <typename CValueType> |
| Result<std::shared_ptr<SparseCOOTensor>> MakeSparseTensor( |
| const std::shared_ptr<SparseCOOIndex>& si, |
| std::vector<CValueType>& sparse_values) const { |
| auto data = Buffer::Wrap(sparse_values); |
| return SparseCOOTensor::Make(si, CTypeTraits<CValueType>::type_singleton(), data, |
| this->shape_, this->dim_names_); |
| } |
| }; |
| |
| TYPED_TEST_SUITE_P(TestSparseCOOTensorForIndexValueType); |
| |
| TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, Make) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| constexpr int sizeof_index_value = sizeof(c_index_value_type); |
| ASSERT_OK_AND_ASSIGN( |
| std::shared_ptr<SparseCOOIndex> si, |
| this->MakeSparseCOOIndex({12, 3}, {sizeof_index_value * 3, sizeof_index_value}, |
| this->coords_values_row_major_)); |
| |
| std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}; |
| auto sparse_data = Buffer::Wrap(sparse_values); |
| |
| std::shared_ptr<SparseCOOTensor> st; |
| |
| // OK |
| ASSERT_OK_AND_ASSIGN(st, SparseCOOTensor::Make(si, int64(), sparse_data, this->shape_, |
| this->dim_names_)); |
| ASSERT_EQ(int64(), st->type()); |
| ASSERT_EQ(this->shape_, st->shape()); |
| ASSERT_EQ(this->dim_names_, st->dim_names()); |
| ASSERT_EQ(sparse_data->data(), st->raw_data()); |
| ASSERT_TRUE( |
| internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index())->Equals(*si)); |
| |
| // OK with an empty dim_names |
| ASSERT_OK_AND_ASSIGN(st, |
| SparseCOOTensor::Make(si, int64(), sparse_data, this->shape_, {})); |
| ASSERT_EQ(int64(), st->type()); |
| ASSERT_EQ(this->shape_, st->shape()); |
| ASSERT_EQ(std::vector<std::string>{}, st->dim_names()); |
| ASSERT_EQ(sparse_data->data(), st->raw_data()); |
| ASSERT_TRUE( |
| internal::checked_pointer_cast<SparseCOOIndex>(st->sparse_index())->Equals(*si)); |
| |
| // invalid data type |
| auto res = SparseCOOTensor::Make(si, binary(), sparse_data, this->shape_, {}); |
| ASSERT_RAISES(Invalid, res); |
| |
| // negative items in shape |
| res = SparseCOOTensor::Make(si, int64(), sparse_data, {2, -3, 4}, {}); |
| ASSERT_RAISES(Invalid, res); |
| |
| // sparse index and ndim (shape length) are inconsistent |
| res = SparseCOOTensor::Make(si, int64(), sparse_data, {6, 4}, {}); |
| ASSERT_RAISES(Invalid, res); |
| |
| // shape and dim_names are inconsistent |
| res = SparseCOOTensor::Make(si, int64(), sparse_data, this->shape_, |
| std::vector<std::string>{"foo"}); |
| ASSERT_RAISES(Invalid, res); |
| } |
| |
| TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, CreationWithRowMajorIndex) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| constexpr int sizeof_index_value = sizeof(c_index_value_type); |
| ASSERT_OK_AND_ASSIGN( |
| std::shared_ptr<SparseCOOIndex> si, |
| this->MakeSparseCOOIndex({12, 3}, {sizeof_index_value * 3, sizeof_index_value}, |
| this->coords_values_row_major_)); |
| |
| std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}; |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st, |
| this->MakeSparseTensor(si, sparse_values)); |
| |
| ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st->dim_names()); |
| ASSERT_EQ("foo", st->dim_name(0)); |
| ASSERT_EQ("bar", st->dim_name(1)); |
| ASSERT_EQ("baz", st->dim_name(2)); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| } |
| |
| TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, CreationWithColumnMajorIndex) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| constexpr int sizeof_index_value = sizeof(c_index_value_type); |
| ASSERT_OK_AND_ASSIGN( |
| std::shared_ptr<SparseCOOIndex> si, |
| this->MakeSparseCOOIndex({12, 3}, {sizeof_index_value, sizeof_index_value * 12}, |
| this->coords_values_col_major_)); |
| |
| std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}; |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st, |
| this->MakeSparseTensor(si, sparse_values)); |
| |
| ASSERT_EQ(std::vector<std::string>({"foo", "bar", "baz"}), st->dim_names()); |
| ASSERT_EQ("foo", st->dim_name(0)); |
| ASSERT_EQ("bar", st->dim_name(1)); |
| ASSERT_EQ("baz", st->dim_name(2)); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| } |
| |
| TYPED_TEST_P(TestSparseCOOTensorForIndexValueType, |
| EqualityBetweenRowAndColumnMajorIndices) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| // Row-major COO index |
| const std::vector<int64_t> coords_shape = {12, 3}; |
| constexpr int sizeof_index_value = sizeof(c_index_value_type); |
| ASSERT_OK_AND_ASSIGN( |
| std::shared_ptr<SparseCOOIndex> si_row_major, |
| this->MakeSparseCOOIndex(coords_shape, {sizeof_index_value * 3, sizeof_index_value}, |
| this->coords_values_row_major_)); |
| |
| // Column-major COO index |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOIndex> si_col_major, |
| this->MakeSparseCOOIndex( |
| coords_shape, {sizeof_index_value, sizeof_index_value * 12}, |
| this->coords_values_col_major_)); |
| |
| std::vector<int64_t> sparse_values_1 = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}; |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st1, |
| this->MakeSparseTensor(si_row_major, sparse_values_1)); |
| |
| std::vector<int64_t> sparse_values_2 = sparse_values_1; |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCOOTensor> st2, |
| this->MakeSparseTensor(si_row_major, sparse_values_2)); |
| |
| ASSERT_TRUE(st2->Equals(*st1)); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestSparseCOOTensorForIndexValueType, Make, |
| CreationWithRowMajorIndex, CreationWithColumnMajorIndex, |
| EqualityBetweenRowAndColumnMajorIndices); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestSparseCOOTensorForIndexValueType, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestSparseCOOTensorForIndexValueType, |
| UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestSparseCOOTensorForIndexValueType, |
| Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestSparseCOOTensorForIndexValueType, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestSparseCOOTensorForIndexValueType, |
| Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestSparseCOOTensorForIndexValueType, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestSparseCOOTensorForIndexValueType, |
| Int64Type); |
| |
| TEST(TestSparseCOOTensorForUInt64Index, Make) { |
| std::vector<int64_t> dense_values = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| Tensor dense_tensor(uint64(), Buffer::Wrap(dense_values), {2, 3, 4}); |
| ASSERT_RAISES(Invalid, SparseCOOTensor::Make(dense_tensor, uint64())); |
| } |
| |
| template <typename IndexValueType> |
| class TestSparseCSRMatrixBase : public TestSparseTensorBase<Int64Type> { |
| public: |
| void SetUp() { |
| shape_ = {6, 4}; |
| dim_names_ = {"foo", "bar"}; |
| |
| // Dense representation: |
| // [ |
| // 1 0 2 0 |
| // 0 3 0 4 |
| // 5 0 6 0 |
| // 0 11 0 12 |
| // 13 0 14 0 |
| // 0 15 0 16 |
| // ] |
| dense_values_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| auto dense_data = Buffer::Wrap(dense_values_); |
| NumericTensor<Int64Type> dense_tensor(dense_data, shape_, {}, dim_names_); |
| ASSERT_OK_AND_ASSIGN(sparse_tensor_from_dense_, |
| SparseCSRMatrix::Make( |
| dense_tensor, TypeTraits<IndexValueType>::type_singleton())); |
| } |
| |
| protected: |
| std::vector<int64_t> dense_values_; |
| std::shared_ptr<SparseCSRMatrix> sparse_tensor_from_dense_; |
| }; |
| |
| class TestSparseCSRMatrix : public TestSparseCSRMatrixBase<Int64Type> {}; |
| |
| TEST_F(TestSparseCSRMatrix, CreationFromZeroTensor) { |
| const auto dense_size = |
| std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1), |
| [](int64_t a, int64_t x) { return a * x; }); |
| std::vector<int64_t> dense_values(dense_size, 0); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero, |
| Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_)); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCSRMatrix> st_zero, |
| SparseCSRMatrix::Make(*t_zero, int64())); |
| |
| ASSERT_EQ(0, st_zero->non_zero_length()); |
| ASSERT_EQ(dense_size, st_zero->size()); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor()); |
| ASSERT_TRUE(t->Equals(*t_zero)); |
| } |
| |
| TEST_F(TestSparseCSRMatrix, CreationFromNumericTensor2D) { |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(this->dense_values_); |
| NumericTensor<Int64Type> tensor(buffer, this->shape_); |
| |
| std::shared_ptr<SparseCSRMatrix> st1; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSRMatrix::Make(tensor)); |
| |
| auto st2 = this->sparse_tensor_from_dense_; |
| |
| CheckSparseIndexFormatType(SparseTensorFormat::CSR, *st1); |
| |
| ASSERT_EQ(12, st1->non_zero_length()); |
| ASSERT_TRUE(st1->is_mutable()); |
| |
| ASSERT_EQ(std::vector<std::string>({"foo", "bar"}), st2->dim_names()); |
| ASSERT_EQ("foo", st2->dim_name(0)); |
| ASSERT_EQ("bar", st2->dim_name(1)); |
| |
| ASSERT_EQ(std::vector<std::string>({}), st1->dim_names()); |
| ASSERT_EQ("", st1->dim_name(0)); |
| ASSERT_EQ("", st1->dim_name(1)); |
| ASSERT_EQ("", st1->dim_name(2)); |
| |
| const int64_t* raw_data = reinterpret_cast<const int64_t*>(st1->raw_data()); |
| AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}); |
| |
| auto si = internal::checked_pointer_cast<SparseCSRIndex>(st1->sparse_index()); |
| ASSERT_EQ(std::string("SparseCSRIndex"), si->ToString()); |
| ASSERT_EQ(1, si->indptr()->ndim()); |
| ASSERT_EQ(1, si->indices()->ndim()); |
| |
| const int64_t* indptr_begin = |
| reinterpret_cast<const int64_t*>(si->indptr()->raw_data()); |
| std::vector<int64_t> indptr_values(indptr_begin, |
| indptr_begin + si->indptr()->shape()[0]); |
| |
| ASSERT_EQ(7, indptr_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 6, 8, 10, 12}), indptr_values); |
| |
| const int64_t* indices_begin = |
| reinterpret_cast<const int64_t*>(si->indices()->raw_data()); |
| std::vector<int64_t> indices_values(indices_begin, |
| indices_begin + si->indices()->shape()[0]); |
| |
| ASSERT_EQ(12, indices_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}), indices_values); |
| } |
| |
| TEST_F(TestSparseCSRMatrix, CreationFromNonContiguousTensor) { |
| std::vector<int64_t> values = {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, |
| 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0, |
| 13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0}; |
| std::vector<int64_t> strides = {64, 16}; |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(values); |
| Tensor tensor(int64(), buffer, this->shape_, strides); |
| |
| std::shared_ptr<SparseCSRMatrix> st; |
| ASSERT_OK_AND_ASSIGN(st, SparseCSRMatrix::Make(tensor)); |
| |
| ASSERT_EQ(12, st->non_zero_length()); |
| ASSERT_TRUE(st->is_mutable()); |
| |
| const int64_t* raw_data = reinterpret_cast<const int64_t*>(st->raw_data()); |
| AssertNumericDataEqual(raw_data, {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}); |
| |
| auto si = internal::checked_pointer_cast<SparseCSRIndex>(st->sparse_index()); |
| ASSERT_EQ(1, si->indptr()->ndim()); |
| ASSERT_EQ(1, si->indices()->ndim()); |
| |
| const int64_t* indptr_begin = |
| reinterpret_cast<const int64_t*>(si->indptr()->raw_data()); |
| std::vector<int64_t> indptr_values(indptr_begin, |
| indptr_begin + si->indptr()->shape()[0]); |
| |
| ASSERT_EQ(7, indptr_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 6, 8, 10, 12}), indptr_values); |
| |
| const int64_t* indices_begin = |
| reinterpret_cast<const int64_t*>(si->indices()->raw_data()); |
| std::vector<int64_t> indices_values(indices_begin, |
| indices_begin + si->indices()->shape()[0]); |
| |
| ASSERT_EQ(12, indices_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}), indices_values); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| } |
| |
| TEST_F(TestSparseCSRMatrix, TestToTensor) { |
| std::vector<int64_t> values = {1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 1, |
| 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1}; |
| std::vector<int64_t> shape({6, 4}); |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(values); |
| Tensor tensor(int64(), buffer, shape, {}, this->dim_names_); |
| |
| std::shared_ptr<SparseCSRMatrix> sparse_tensor; |
| ASSERT_OK_AND_ASSIGN(sparse_tensor, SparseCSRMatrix::Make(tensor)); |
| |
| ASSERT_EQ(7, sparse_tensor->non_zero_length()); |
| ASSERT_TRUE(sparse_tensor->is_mutable()); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dense_tensor, sparse_tensor->ToTensor()); |
| ASSERT_TRUE(tensor.Equals(*dense_tensor)); |
| } |
| |
| template <typename ValueType> |
| class TestSparseCSRMatrixEquality : public TestSparseTensorBase<ValueType> { |
| public: |
| void SetUp() { |
| shape_ = {6, 4}; |
| values1_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| values2_ = {9, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| auto buffer1 = Buffer::Wrap(values1_); |
| auto buffer2 = Buffer::Wrap(values2_); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, this->shape_).Value(&tensor1_)); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, this->shape_).Value(&tensor2_)); |
| } |
| |
| protected: |
| using TestSparseTensorBase<ValueType>::shape_; |
| std::vector<typename ValueType::c_type> values1_; |
| std::vector<typename ValueType::c_type> values2_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor1_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor2_; |
| }; |
| |
| template <typename ValueType> |
| class TestIntegerSparseCSRMatrixEquality : public TestSparseCSRMatrixEquality<ValueType> { |
| }; |
| |
| TYPED_TEST_SUITE_P(TestIntegerSparseCSRMatrixEquality); |
| |
| TYPED_TEST_P(TestIntegerSparseCSRMatrixEquality, TestEquality) { |
| using ValueType = TypeParam; |
| static_assert(is_integer_type<ValueType>::value, "Integer type is required"); |
| |
| std::shared_ptr<SparseCSRMatrix> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSRMatrix::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCSRMatrix::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCSRMatrix::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCSRMatrixEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCSRMatrixEquality, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCSRMatrixEquality, UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCSRMatrixEquality, Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCSRMatrixEquality, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCSRMatrixEquality, Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCSRMatrixEquality, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCSRMatrixEquality, Int64Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCSRMatrixEquality, |
| UInt64Type); |
| |
| template <typename ValueType> |
| class TestFloatingSparseCSRMatrixEquality |
| : public TestSparseCSRMatrixEquality<ValueType> {}; |
| |
| TYPED_TEST_SUITE_P(TestFloatingSparseCSRMatrixEquality); |
| |
| TYPED_TEST_P(TestFloatingSparseCSRMatrixEquality, TestEquality) { |
| using ValueType = TypeParam; |
| using c_value_type = typename ValueType::c_type; |
| static_assert(is_floating_type<ValueType>::value, "Float type is required"); |
| |
| std::shared_ptr<SparseCSRMatrix> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSRMatrix::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCSRMatrix::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCSRMatrix::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| |
| // sparse tensors with NaNs |
| const c_value_type nan_value = static_cast<c_value_type>(NAN); |
| this->values2_[13] = nan_value; |
| EXPECT_TRUE(std::isnan(this->tensor2_->Value({3, 1}))); |
| |
| std::shared_ptr<SparseCSRMatrix> st4; |
| ASSERT_OK_AND_ASSIGN(st4, SparseCSRMatrix::Make(*this->tensor2_)); |
| EXPECT_FALSE(st4->Equals(*st4)); // same object |
| EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object |
| |
| std::vector<c_value_type> values5 = this->values2_; |
| std::shared_ptr<SparseCSRMatrix> st5; |
| std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5); |
| NumericTensor<ValueType> tensor5(buffer5, this->shape_); |
| ASSERT_OK_AND_ASSIGN(st5, SparseCSRMatrix::Make(tensor5)); |
| EXPECT_FALSE(st4->Equals(*st5)); // different memory |
| EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCSRMatrixEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCSRMatrixEquality, FloatType); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCSRMatrixEquality, |
| DoubleType); |
| |
| template <typename IndexValueType> |
| class TestSparseCSRMatrixForIndexValueType |
| : public TestSparseCSRMatrixBase<IndexValueType> {}; |
| |
| TYPED_TEST_SUITE_P(TestSparseCSRMatrixForIndexValueType); |
| |
| TYPED_TEST_P(TestSparseCSRMatrixForIndexValueType, Make) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| // Sparse representation: |
| std::vector<c_index_value_type> indptr_values = {0, 2, 4, 6, 8, 10, 12}; |
| std::vector<c_index_value_type> indices_values = {0, 2, 1, 3, 0, 2, 1, 3, 0, 2, 1, 3}; |
| |
| std::vector<int64_t> indptr_shape = {7}; |
| std::vector<int64_t> indices_shape = {12}; |
| |
| std::shared_ptr<SparseCSRIndex> si; |
| ASSERT_OK_AND_ASSIGN( |
| si, SparseCSRIndex::Make(TypeTraits<IndexValueType>::type_singleton(), indptr_shape, |
| indices_shape, Buffer::Wrap(indptr_values), |
| Buffer::Wrap(indices_values))); |
| |
| std::vector<int64_t> sparse_values = {1, 2, 3, 4, 5, 6, 11, 12, 13, 14, 15, 16}; |
| auto sparse_data = Buffer::Wrap(sparse_values); |
| |
| std::shared_ptr<SparseCSRMatrix> sm; |
| |
| // OK |
| ASSERT_OK( |
| SparseCSRMatrix::Make(si, int64(), sparse_data, this->shape_, this->dim_names_)); |
| |
| // OK with an empty dim_names |
| ASSERT_OK(SparseCSRMatrix::Make(si, int64(), sparse_data, this->shape_, {})); |
| |
| // invalid data type |
| ASSERT_RAISES(Invalid, |
| SparseCSRMatrix::Make(si, binary(), sparse_data, this->shape_, {})); |
| |
| // empty shape |
| ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {}, {})); |
| |
| // 1D shape |
| ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {24}, {})); |
| |
| // negative items in shape |
| ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {6, -4}, {})); |
| |
| // sparse index and ndim (shape length) are inconsistent |
| ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, {4, 6}, {})); |
| |
| // shape and dim_names are inconsistent |
| ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(si, int64(), sparse_data, this->shape_, |
| std::vector<std::string>{"foo"})); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestSparseCSRMatrixForIndexValueType, Make); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestSparseCSRMatrixForIndexValueType, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestSparseCSRMatrixForIndexValueType, |
| UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestSparseCSRMatrixForIndexValueType, |
| Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestSparseCSRMatrixForIndexValueType, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestSparseCSRMatrixForIndexValueType, |
| Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestSparseCSRMatrixForIndexValueType, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestSparseCSRMatrixForIndexValueType, |
| Int64Type); |
| |
| TEST(TestSparseCSRMatrixForUInt64Index, Make) { |
| std::vector<int64_t> dense_values = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| Tensor dense_tensor(uint64(), Buffer::Wrap(dense_values), {6, 4}); |
| ASSERT_RAISES(Invalid, SparseCSRMatrix::Make(dense_tensor, uint64())); |
| } |
| |
| template <typename IndexValueType> |
| class TestSparseCSCMatrixBase : public TestSparseTensorBase<Int64Type> { |
| public: |
| void SetUp() { |
| shape_ = {6, 4}; |
| dim_names_ = {"foo", "bar"}; |
| |
| // Dense representation: |
| // [ |
| // 1 0 2 0 |
| // 0 3 0 4 |
| // 5 0 6 0 |
| // 0 11 0 12 |
| // 13 0 14 0 |
| // 0 15 0 16 |
| // ] |
| dense_values_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| auto dense_data = Buffer::Wrap(dense_values_); |
| NumericTensor<Int64Type> dense_tensor(dense_data, shape_, {}, dim_names_); |
| ASSERT_OK_AND_ASSIGN(sparse_tensor_from_dense_, |
| SparseCSCMatrix::Make( |
| dense_tensor, TypeTraits<IndexValueType>::type_singleton())); |
| } |
| |
| protected: |
| std::vector<int64_t> dense_values_; |
| std::shared_ptr<SparseCSCMatrix> sparse_tensor_from_dense_; |
| }; |
| |
| class TestSparseCSCMatrix : public TestSparseCSCMatrixBase<Int64Type> {}; |
| |
| TEST_F(TestSparseCSCMatrix, CreationFromZeroTensor) { |
| const auto dense_size = |
| std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1), |
| [](int64_t a, int64_t x) { return a * x; }); |
| std::vector<int64_t> dense_values(dense_size, 0); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero, |
| Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_)); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCSCMatrix> st_zero, |
| SparseCSCMatrix::Make(*t_zero, int64())); |
| |
| ASSERT_EQ(0, st_zero->non_zero_length()); |
| ASSERT_EQ(dense_size, st_zero->size()); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor()); |
| ASSERT_TRUE(t->Equals(*t_zero)); |
| } |
| |
| TEST_F(TestSparseCSCMatrix, CreationFromNumericTensor2D) { |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(this->dense_values_); |
| NumericTensor<Int64Type> tensor(buffer, this->shape_); |
| |
| std::shared_ptr<SparseCSCMatrix> st1; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSCMatrix::Make(tensor)); |
| |
| auto st2 = this->sparse_tensor_from_dense_; |
| |
| CheckSparseIndexFormatType(SparseTensorFormat::CSC, *st1); |
| |
| ASSERT_EQ(12, st1->non_zero_length()); |
| ASSERT_TRUE(st1->is_mutable()); |
| |
| ASSERT_EQ(std::vector<std::string>({"foo", "bar"}), st2->dim_names()); |
| ASSERT_EQ("foo", st2->dim_name(0)); |
| ASSERT_EQ("bar", st2->dim_name(1)); |
| |
| ASSERT_EQ(std::vector<std::string>({}), st1->dim_names()); |
| ASSERT_EQ("", st1->dim_name(0)); |
| ASSERT_EQ("", st1->dim_name(1)); |
| ASSERT_EQ("", st1->dim_name(2)); |
| |
| const int64_t* raw_data = reinterpret_cast<const int64_t*>(st1->raw_data()); |
| AssertNumericDataEqual(raw_data, {1, 5, 13, 3, 11, 15, 2, 6, 14, 4, 12, 16}); |
| |
| auto si = internal::checked_pointer_cast<SparseCSCIndex>(st1->sparse_index()); |
| ASSERT_EQ(std::string("SparseCSCIndex"), si->ToString()); |
| ASSERT_EQ(1, si->indptr()->ndim()); |
| ASSERT_EQ(1, si->indices()->ndim()); |
| |
| const int64_t* indptr_begin = |
| reinterpret_cast<const int64_t*>(si->indptr()->raw_data()); |
| std::vector<int64_t> indptr_values(indptr_begin, |
| indptr_begin + si->indptr()->shape()[0]); |
| |
| ASSERT_EQ(5, indptr_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 3, 6, 9, 12}), indptr_values); |
| |
| const int64_t* indices_begin = |
| reinterpret_cast<const int64_t*>(si->indices()->raw_data()); |
| std::vector<int64_t> indices_values(indices_begin, |
| indices_begin + si->indices()->shape()[0]); |
| |
| ASSERT_EQ(12, indices_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 1, 3, 5, 0, 2, 4, 1, 3, 5}), indices_values); |
| } |
| |
| TEST_F(TestSparseCSCMatrix, CreationFromNonContiguousTensor) { |
| std::vector<int64_t> values = {1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 4, 0, |
| 5, 0, 0, 0, 6, 0, 0, 0, 0, 0, 11, 0, 0, 0, 12, 0, |
| 13, 0, 0, 0, 14, 0, 0, 0, 0, 0, 15, 0, 0, 0, 16, 0}; |
| std::vector<int64_t> strides = {64, 16}; |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(values); |
| Tensor tensor(int64(), buffer, this->shape_, strides); |
| |
| std::shared_ptr<SparseCSCMatrix> st; |
| ASSERT_OK_AND_ASSIGN(st, SparseCSCMatrix::Make(tensor)); |
| |
| ASSERT_EQ(12, st->non_zero_length()); |
| ASSERT_TRUE(st->is_mutable()); |
| |
| const int64_t* raw_data = reinterpret_cast<const int64_t*>(st->raw_data()); |
| AssertNumericDataEqual(raw_data, {1, 5, 13, 3, 11, 15, 2, 6, 14, 4, 12, 16}); |
| |
| auto si = internal::checked_pointer_cast<SparseCSCIndex>(st->sparse_index()); |
| ASSERT_EQ(1, si->indptr()->ndim()); |
| ASSERT_EQ(1, si->indices()->ndim()); |
| |
| const int64_t* indptr_begin = |
| reinterpret_cast<const int64_t*>(si->indptr()->raw_data()); |
| std::vector<int64_t> indptr_values(indptr_begin, |
| indptr_begin + si->indptr()->shape()[0]); |
| |
| ASSERT_EQ(5, indptr_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 3, 6, 9, 12}), indptr_values); |
| |
| const int64_t* indices_begin = |
| reinterpret_cast<const int64_t*>(si->indices()->raw_data()); |
| std::vector<int64_t> indices_values(indices_begin, |
| indices_begin + si->indices()->shape()[0]); |
| |
| ASSERT_EQ(12, indices_values.size()); |
| ASSERT_EQ(std::vector<int64_t>({0, 2, 4, 1, 3, 5, 0, 2, 4, 1, 3, 5}), indices_values); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| } |
| |
| TEST_F(TestSparseCSCMatrix, TestToTensor) { |
| std::vector<int64_t> values = {1, 0, 0, 0, 0, 0, 2, 1, 0, 0, 0, 1, |
| 0, 2, 0, 0, 0, 0, 0, 3, 0, 0, 0, 1}; |
| std::vector<int64_t> shape({6, 4}); |
| std::shared_ptr<Buffer> buffer = Buffer::Wrap(values); |
| Tensor tensor(int64(), buffer, shape, {}, this->dim_names_); |
| |
| std::shared_ptr<SparseCSCMatrix> sparse_tensor; |
| ASSERT_OK_AND_ASSIGN(sparse_tensor, SparseCSCMatrix::Make(tensor)); |
| |
| ASSERT_EQ(7, sparse_tensor->non_zero_length()); |
| ASSERT_TRUE(sparse_tensor->is_mutable()); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dense_tensor, sparse_tensor->ToTensor()); |
| ASSERT_TRUE(tensor.Equals(*dense_tensor)); |
| } |
| |
| template <typename ValueType> |
| class TestSparseCSCMatrixEquality : public TestSparseTensorBase<ValueType> { |
| public: |
| void SetUp() { |
| shape_ = {6, 4}; |
| values1_ = {1, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| values2_ = {9, 0, 2, 0, 0, 3, 0, 4, 5, 0, 6, 0, |
| 0, 11, 0, 12, 13, 0, 14, 0, 0, 15, 0, 16}; |
| auto buffer1 = Buffer::Wrap(values1_); |
| auto buffer2 = Buffer::Wrap(values2_); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, shape_).Value(&tensor1_)); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, shape_).Value(&tensor2_)); |
| } |
| |
| protected: |
| using TestSparseTensorBase<ValueType>::shape_; |
| std::vector<typename ValueType::c_type> values1_; |
| std::vector<typename ValueType::c_type> values2_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor1_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor2_; |
| }; |
| |
| template <typename ValueType> |
| class TestIntegerSparseCSCMatrixEquality : public TestSparseCSCMatrixEquality<ValueType> { |
| }; |
| |
| TYPED_TEST_SUITE_P(TestIntegerSparseCSCMatrixEquality); |
| |
| TYPED_TEST_P(TestIntegerSparseCSCMatrixEquality, TestEquality) { |
| using ValueType = TypeParam; |
| static_assert(is_integer_type<ValueType>::value, "Integer type is required"); |
| |
| std::shared_ptr<SparseCSCMatrix> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSCMatrix::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCSCMatrix::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCSCMatrix::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCSCMatrixEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCSCMatrixEquality, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCSCMatrixEquality, UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCSCMatrixEquality, Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCSCMatrixEquality, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCSCMatrixEquality, Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCSCMatrixEquality, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCSCMatrixEquality, Int64Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCSCMatrixEquality, |
| UInt64Type); |
| |
| template <typename ValueType> |
| class TestFloatingSparseCSCMatrixEquality |
| : public TestSparseCSCMatrixEquality<ValueType> {}; |
| |
| TYPED_TEST_SUITE_P(TestFloatingSparseCSCMatrixEquality); |
| |
| TYPED_TEST_P(TestFloatingSparseCSCMatrixEquality, TestEquality) { |
| using ValueType = TypeParam; |
| using c_value_type = typename ValueType::c_type; |
| static_assert(is_floating_type<ValueType>::value, "Float type is required"); |
| |
| std::shared_ptr<SparseCSCMatrix> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSCMatrix::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCSCMatrix::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCSCMatrix::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| |
| // sparse tensors with NaNs |
| const c_value_type nan_value = static_cast<c_value_type>(NAN); |
| this->values2_[13] = nan_value; |
| EXPECT_TRUE(std::isnan(this->tensor2_->Value({3, 1}))); |
| |
| std::shared_ptr<SparseCSCMatrix> st4; |
| ASSERT_OK_AND_ASSIGN(st4, SparseCSCMatrix::Make(*this->tensor2_)); |
| EXPECT_FALSE(st4->Equals(*st4)); // same object |
| EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object |
| |
| std::vector<c_value_type> values5 = this->values2_; |
| std::shared_ptr<SparseCSCMatrix> st5; |
| std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5); |
| NumericTensor<ValueType> tensor5(buffer5, this->shape_); |
| ASSERT_OK_AND_ASSIGN(st5, SparseCSCMatrix::Make(tensor5)); |
| EXPECT_FALSE(st4->Equals(*st5)); // different memory |
| EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCSCMatrixEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCSCMatrixEquality, FloatType); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCSCMatrixEquality, |
| DoubleType); |
| |
| template <typename ValueType> |
| class TestSparseCSFTensorEquality : public TestSparseTensorBase<ValueType> { |
| public: |
| void SetUp() { |
| shape_ = {2, 3, 4, 5}; |
| |
| values1_[0][0][0][1] = 1; |
| values1_[0][0][0][2] = 2; |
| values1_[0][1][0][0] = 3; |
| values1_[0][1][0][2] = 4; |
| values1_[0][1][1][0] = 5; |
| values1_[1][1][1][0] = 6; |
| values1_[1][1][1][1] = 7; |
| values1_[1][1][1][2] = 8; |
| |
| length_ = sizeof(values1_); |
| |
| values2_[0][0][0][1] = 1; |
| values2_[0][0][0][2] = 2; |
| values2_[0][1][0][0] = 3; |
| values2_[0][1][0][2] = 9; |
| values2_[0][1][1][0] = 5; |
| values2_[1][1][1][0] = 6; |
| values2_[1][1][1][1] = 7; |
| values2_[1][1][1][2] = 8; |
| |
| auto buffer1 = Buffer::Wrap(values1_, length_); |
| auto buffer2 = Buffer::Wrap(values2_, length_); |
| |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer1, shape_).Value(&tensor1_)); |
| DCHECK_OK(NumericTensor<ValueType>::Make(buffer2, shape_).Value(&tensor2_)); |
| } |
| |
| protected: |
| using TestSparseTensorBase<ValueType>::shape_; |
| typename ValueType::c_type values1_[2][3][4][5] = {}; |
| typename ValueType::c_type values2_[2][3][4][5] = {}; |
| int64_t length_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor1_; |
| std::shared_ptr<NumericTensor<ValueType>> tensor2_; |
| }; |
| |
| template <typename ValueType> |
| class TestIntegerSparseCSFTensorEquality : public TestSparseCSFTensorEquality<ValueType> { |
| }; |
| |
| TYPED_TEST_SUITE_P(TestIntegerSparseCSFTensorEquality); |
| |
| TYPED_TEST_P(TestIntegerSparseCSFTensorEquality, TestEquality) { |
| using ValueType = TypeParam; |
| static_assert(is_integer_type<ValueType>::value, "Integer type is required"); |
| |
| std::shared_ptr<SparseCSFTensor> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSFTensor::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCSFTensor::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCSFTensor::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestIntegerSparseCSFTensorEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestIntegerSparseCSFTensorEquality, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestIntegerSparseCSFTensorEquality, UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestIntegerSparseCSFTensorEquality, Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestIntegerSparseCSFTensorEquality, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestIntegerSparseCSFTensorEquality, Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestIntegerSparseCSFTensorEquality, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestIntegerSparseCSFTensorEquality, Int64Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt64, TestIntegerSparseCSFTensorEquality, |
| UInt64Type); |
| |
| template <typename ValueType> |
| class TestFloatingSparseCSFTensorEquality |
| : public TestSparseCSFTensorEquality<ValueType> {}; |
| |
| TYPED_TEST_SUITE_P(TestFloatingSparseCSFTensorEquality); |
| |
| TYPED_TEST_P(TestFloatingSparseCSFTensorEquality, TestEquality) { |
| using ValueType = TypeParam; |
| using c_value_type = typename ValueType::c_type; |
| static_assert(is_floating_type<ValueType>::value, "Floating type is required"); |
| |
| std::shared_ptr<SparseCSFTensor> st1, st2, st3; |
| ASSERT_OK_AND_ASSIGN(st1, SparseCSFTensor::Make(*this->tensor1_)); |
| ASSERT_OK_AND_ASSIGN(st2, SparseCSFTensor::Make(*this->tensor2_)); |
| ASSERT_OK_AND_ASSIGN(st3, SparseCSFTensor::Make(*this->tensor1_)); |
| |
| ASSERT_TRUE(st1->Equals(*st1)); |
| ASSERT_FALSE(st1->Equals(*st2)); |
| ASSERT_TRUE(st1->Equals(*st3)); |
| |
| // sparse tensors with NaNs |
| const c_value_type nan_value = static_cast<c_value_type>(NAN); |
| this->values2_[1][1][1][1] = nan_value; |
| EXPECT_TRUE(std::isnan(this->tensor2_->Value({1, 1, 1, 1}))); |
| |
| std::shared_ptr<SparseCSFTensor> st4; |
| ASSERT_OK_AND_ASSIGN(st4, SparseCSFTensor::Make(*this->tensor2_)); |
| EXPECT_FALSE(st4->Equals(*st4)); // same object |
| EXPECT_TRUE(st4->Equals(*st4, EqualOptions().nans_equal(true))); // same object |
| |
| c_value_type values5[2][3][4][5] = {}; |
| std::copy_n(&this->values2_[0][0][0][0], this->length_ / sizeof(c_value_type), |
| &values5[0][0][0][0]); |
| std::shared_ptr<SparseCSFTensor> st5; |
| std::shared_ptr<Buffer> buffer5 = Buffer::Wrap(values5, sizeof(values5)); |
| NumericTensor<ValueType> tensor5(buffer5, this->shape_); |
| ASSERT_OK_AND_ASSIGN(st5, SparseCSFTensor::Make(tensor5)); |
| EXPECT_FALSE(st4->Equals(*st5)); // different memory |
| EXPECT_TRUE(st4->Equals(*st5, EqualOptions().nans_equal(true))); // different memory |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestFloatingSparseCSFTensorEquality, TestEquality); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestFloat, TestFloatingSparseCSFTensorEquality, FloatType); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestDouble, TestFloatingSparseCSFTensorEquality, |
| DoubleType); |
| |
| template <typename IndexValueType> |
| class TestSparseCSFTensorBase : public TestSparseTensorBase<Int16Type> { |
| public: |
| void SetUp() { |
| dim_names_ = {"a", "b", "c", "d"}; |
| shape_ = {2, 3, 4, 5}; |
| |
| dense_values_[0][0][0][1] = 1; |
| dense_values_[0][0][0][2] = 2; |
| dense_values_[0][1][0][0] = 3; |
| dense_values_[0][1][0][2] = 4; |
| dense_values_[0][1][1][0] = 5; |
| dense_values_[1][1][1][0] = 6; |
| dense_values_[1][1][1][1] = 7; |
| dense_values_[1][1][1][2] = 8; |
| |
| auto dense_buffer = Buffer::Wrap(dense_values_, sizeof(dense_values_)); |
| Tensor dense_tensor_(int16(), dense_buffer, shape_, {}, dim_names_); |
| ASSERT_OK_AND_ASSIGN( |
| sparse_tensor_from_dense_, |
| SparseCSFTensor::Make(dense_tensor_, |
| TypeTraits<IndexValueType>::type_singleton())); |
| } |
| |
| protected: |
| std::vector<int64_t> shape_; |
| std::vector<std::string> dim_names_; |
| int16_t dense_values_[2][3][4][5] = {}; |
| std::shared_ptr<SparseCSFTensor> sparse_tensor_from_dense_; |
| }; |
| |
| class TestSparseCSFTensor : public TestSparseCSFTensorBase<Int64Type> {}; |
| |
| TEST_F(TestSparseCSFTensor, CreationFromZeroTensor) { |
| const auto dense_size = |
| std::accumulate(this->shape_.begin(), this->shape_.end(), int64_t(1), |
| [](int64_t a, int64_t x) { return a * x; }); |
| std::vector<int64_t> dense_values(dense_size, 0); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t_zero, |
| Tensor::Make(int64(), Buffer::Wrap(dense_values), this->shape_)); |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<SparseCSFTensor> st_zero, |
| SparseCSFTensor::Make(*t_zero, int64())); |
| |
| ASSERT_EQ(0, st_zero->non_zero_length()); |
| ASSERT_EQ(dense_size, st_zero->size()); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> t, st_zero->ToTensor()); |
| ASSERT_TRUE(t->Equals(*t_zero)); |
| } |
| |
| template <typename IndexValueType> |
| class TestSparseCSFTensorForIndexValueType |
| : public TestSparseCSFTensorBase<IndexValueType> { |
| protected: |
| std::shared_ptr<SparseCSFIndex> MakeSparseCSFIndex( |
| const std::vector<int64_t>& axis_order, |
| const std::vector<std::vector<typename IndexValueType::c_type>>& indptr_values, |
| const std::vector<std::vector<typename IndexValueType::c_type>>& indices_values) |
| const { |
| int64_t ndim = axis_order.size(); |
| std::vector<std::shared_ptr<Tensor>> indptr(ndim - 1); |
| std::vector<std::shared_ptr<Tensor>> indices(ndim); |
| |
| for (int64_t i = 0; i < ndim - 1; ++i) { |
| indptr[i] = std::make_shared<Tensor>( |
| TypeTraits<IndexValueType>::type_singleton(), Buffer::Wrap(indptr_values[i]), |
| std::vector<int64_t>({static_cast<int64_t>(indptr_values[i].size())})); |
| } |
| for (int64_t i = 0; i < ndim; ++i) { |
| indices[i] = std::make_shared<Tensor>( |
| TypeTraits<IndexValueType>::type_singleton(), Buffer::Wrap(indices_values[i]), |
| std::vector<int64_t>({static_cast<int64_t>(indices_values[i].size())})); |
| } |
| return std::make_shared<SparseCSFIndex>(indptr, indices, axis_order); |
| } |
| |
| template <typename CValueType> |
| std::shared_ptr<SparseCSFTensor> MakeSparseTensor( |
| const std::shared_ptr<SparseCSFIndex>& si, std::vector<CValueType>& sparse_values, |
| const std::vector<int64_t>& shape, |
| const std::vector<std::string>& dim_names) const { |
| auto data_buffer = Buffer::Wrap(sparse_values); |
| return std::make_shared<SparseCSFTensor>( |
| si, CTypeTraits<CValueType>::type_singleton(), data_buffer, shape, dim_names); |
| } |
| }; |
| |
| TYPED_TEST_SUITE_P(TestSparseCSFTensorForIndexValueType); |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestCreateSparseTensor) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| std::vector<int64_t> shape = {2, 3, 4, 5}; |
| std::vector<std::string> dim_names = {"a", "b", "c", "d"}; |
| std::vector<int64_t> axis_order = {0, 1, 2, 3}; |
| std::vector<int16_t> sparse_values = {1, 2, 3, 4, 5, 6, 7, 8}; |
| std::vector<std::vector<c_index_value_type>> indptr_values = { |
| {0, 2, 3}, {0, 1, 3, 4}, {0, 2, 4, 5, 8}}; |
| std::vector<std::vector<c_index_value_type>> indices_values = { |
| {0, 1}, {0, 1, 1}, {0, 0, 1, 1}, {1, 2, 0, 2, 0, 0, 1, 2}}; |
| |
| auto si = this->MakeSparseCSFIndex(axis_order, indptr_values, indices_values); |
| auto st = this->MakeSparseTensor(si, sparse_values, shape, dim_names); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| } |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestTensorToSparseTensor) { |
| std::vector<std::string> dim_names = {"a", "b", "c", "d"}; |
| ASSERT_EQ(8, this->sparse_tensor_from_dense_->non_zero_length()); |
| ASSERT_TRUE(this->sparse_tensor_from_dense_->is_mutable()); |
| ASSERT_EQ(dim_names, this->sparse_tensor_from_dense_->dim_names()); |
| } |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestSparseTensorToTensor) { |
| std::vector<int64_t> shape = {2, 3, 4, 5}; |
| auto dense_buffer = Buffer::Wrap(this->dense_values_, sizeof(this->dense_values_)); |
| Tensor dense_tensor(int16(), dense_buffer, shape, {}, this->dim_names_); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dt, |
| this->sparse_tensor_from_dense_->ToTensor()); |
| ASSERT_TRUE(dense_tensor.Equals(*dt)); |
| ASSERT_EQ(dense_tensor.dim_names(), dt->dim_names()); |
| } |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestRoundTrip) { |
| using IndexValueType = TypeParam; |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dt, |
| this->sparse_tensor_from_dense_->ToTensor()); |
| std::shared_ptr<SparseCSFTensor> st; |
| ASSERT_OK_AND_ASSIGN( |
| st, SparseCSFTensor::Make(*dt, TypeTraits<IndexValueType>::type_singleton())); |
| |
| ASSERT_TRUE(st->Equals(*this->sparse_tensor_from_dense_)); |
| } |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestAlternativeAxisOrder) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| std::vector<int16_t> dense_values = {1, 0, 0, 3, 0, 0, 0, 2, 0, 0, 0, 0, |
| 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 0, 5}; |
| std::vector<int64_t> shape = {4, 6}; |
| std::vector<std::string> dim_names = {"a", "b"}; |
| std::shared_ptr<Buffer> dense_buffer = Buffer::Wrap(dense_values); |
| Tensor tensor(int16(), dense_buffer, shape, {}, dim_names); |
| |
| // Axis order 1 |
| std::vector<int64_t> axis_order_1 = {0, 1}; |
| std::vector<int16_t> sparse_values_1 = {1, 3, 2, 4, 5}; |
| std::vector<std::vector<c_index_value_type>> indptr_values_1 = {{0, 2, 3, 5}}; |
| std::vector<std::vector<c_index_value_type>> indices_values_1 = {{0, 1, 3}, |
| {0, 3, 1, 3, 5}}; |
| auto si_1 = this->MakeSparseCSFIndex(axis_order_1, indptr_values_1, indices_values_1); |
| auto st_1 = this->MakeSparseTensor(si_1, sparse_values_1, shape, dim_names); |
| |
| // Axis order 2 |
| std::vector<int64_t> axis_order_2 = {1, 0}; |
| std::vector<int16_t> sparse_values_2 = {1, 2, 3, 4, 5}; |
| std::vector<std::vector<c_index_value_type>> indptr_values_2 = {{0, 1, 2, 4, 5}}; |
| std::vector<std::vector<c_index_value_type>> indices_values_2 = {{0, 1, 3, 5}, |
| {0, 1, 0, 3, 3}}; |
| auto si_2 = this->MakeSparseCSFIndex(axis_order_2, indptr_values_2, indices_values_2); |
| auto st_2 = this->MakeSparseTensor(si_2, sparse_values_2, shape, dim_names); |
| |
| std::shared_ptr<Tensor> dt_1, dt_2; |
| ASSERT_OK_AND_ASSIGN(dt_1, st_1->ToTensor()); |
| ASSERT_OK_AND_ASSIGN(dt_2, st_2->ToTensor()); |
| |
| ASSERT_FALSE(st_1->Equals(*st_2)); |
| ASSERT_TRUE(dt_1->Equals(*dt_2)); |
| ASSERT_TRUE(dt_1->Equals(tensor)); |
| } |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestNonAscendingShape) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| std::vector<int64_t> shape = {5, 2, 3, 4}; |
| int16_t dense_values[5][2][3][4] = {}; // zero-initialized |
| dense_values[0][0][0][1] = 1; |
| dense_values[0][0][0][2] = 2; |
| dense_values[0][1][0][0] = 3; |
| dense_values[0][1][0][2] = 4; |
| dense_values[0][1][1][0] = 5; |
| dense_values[1][1][1][0] = 6; |
| dense_values[1][1][1][1] = 7; |
| dense_values[1][1][1][2] = 8; |
| auto dense_buffer = Buffer::Wrap(dense_values, sizeof(dense_values)); |
| Tensor dense_tensor(int16(), dense_buffer, shape, {}, this->dim_names_); |
| |
| std::shared_ptr<SparseCSFTensor> sparse_tensor; |
| ASSERT_OK_AND_ASSIGN( |
| sparse_tensor, |
| SparseCSFTensor::Make(dense_tensor, TypeTraits<IndexValueType>::type_singleton())); |
| |
| std::vector<std::vector<c_index_value_type>> indptr_values = { |
| {0, 1, 3}, {0, 2, 4, 7}, {0, 1, 2, 3, 4, 6, 7, 8}}; |
| std::vector<std::vector<c_index_value_type>> indices_values = { |
| {0, 1}, {0, 0, 1}, {1, 2, 0, 2, 0, 1, 2}, {0, 0, 0, 0, 0, 1, 1, 1}}; |
| std::vector<int64_t> axis_order = {1, 2, 3, 0}; |
| std::vector<int16_t> sparse_values = {1, 2, 3, 4, 5, 6, 7, 8}; |
| auto si = this->MakeSparseCSFIndex(axis_order, indptr_values, indices_values); |
| auto st = this->MakeSparseTensor(si, sparse_values, shape, this->dim_names_); |
| |
| ASSERT_OK_AND_ASSIGN(std::shared_ptr<Tensor> dt, st->ToTensor()); |
| ASSERT_TRUE(dt->Equals(dense_tensor)); |
| ASSERT_TRUE(st->Equals(*sparse_tensor)); |
| } |
| |
| TYPED_TEST_P(TestSparseCSFTensorForIndexValueType, TestEqualityMismatchedDimensions) { |
| using IndexValueType = TypeParam; |
| using c_index_value_type = typename IndexValueType::c_type; |
| |
| // 2D vs 3D - comparing indices with different dimensionality |
| // 2D CSF: ndim=2, so indptr.size()=1, indices.size()=2 |
| std::vector<int64_t> axis_order_2D = {0, 1}; |
| std::vector<std::vector<c_index_value_type>> indptr_2D = {{0, 1}}; |
| std::vector<std::vector<c_index_value_type>> indices_2D = {{0}, {0}}; |
| auto si_2D = this->MakeSparseCSFIndex(axis_order_2D, indptr_2D, indices_2D); |
| |
| // 3D CSF: ndim=3, so indptr.size()=2, indices.size()=3 |
| std::vector<int64_t> axis_order_3D = {0, 1, 2}; |
| std::vector<std::vector<c_index_value_type>> indptr_3D = {{0, 1}, {0, 1}}; |
| std::vector<std::vector<c_index_value_type>> indices_3D = {{0}, {0}, {0}}; |
| auto si_3D = this->MakeSparseCSFIndex(axis_order_3D, indptr_3D, indices_3D); |
| |
| ASSERT_FALSE(si_2D->Equals(*si_3D)); |
| ASSERT_FALSE(si_3D->Equals(*si_2D)); |
| ASSERT_TRUE(si_2D->Equals(*si_2D)); |
| } |
| |
| REGISTER_TYPED_TEST_SUITE_P(TestSparseCSFTensorForIndexValueType, TestCreateSparseTensor, |
| TestTensorToSparseTensor, TestSparseTensorToTensor, |
| TestAlternativeAxisOrder, TestNonAscendingShape, |
| TestRoundTrip, TestEqualityMismatchedDimensions); |
| |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt8, TestSparseCSFTensorForIndexValueType, Int8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt8, TestSparseCSFTensorForIndexValueType, |
| UInt8Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt16, TestSparseCSFTensorForIndexValueType, |
| Int16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt16, TestSparseCSFTensorForIndexValueType, |
| UInt16Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt32, TestSparseCSFTensorForIndexValueType, |
| Int32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestUInt32, TestSparseCSFTensorForIndexValueType, |
| UInt32Type); |
| INSTANTIATE_TYPED_TEST_SUITE_P(TestInt64, TestSparseCSFTensorForIndexValueType, |
| Int64Type); |
| |
| TEST(TestSparseCSFMatrixForUInt64Index, Make) { |
| int16_t dense_values[2][3][4][5] = {}; |
| dense_values[0][0][0][1] = 1; |
| dense_values[0][0][0][2] = 2; |
| dense_values[0][1][0][0] = 3; |
| dense_values[0][1][0][2] = 4; |
| dense_values[0][1][1][0] = 5; |
| dense_values[1][1][1][0] = 6; |
| dense_values[1][1][1][1] = 7; |
| dense_values[1][1][1][2] = 8; |
| |
| Tensor dense_tensor(uint64(), Buffer::Wrap(dense_values, sizeof(dense_values)), |
| {2, 3, 4, 5}); |
| ASSERT_RAISES(Invalid, SparseCSFTensor::Make(dense_tensor, uint64())); |
| } |
| |
| } // namespace arrow |