-
Notifications
You must be signed in to change notification settings - Fork 4.2k
ARROW-17933: [C++] SparseCOOTensor raises error when created with zero elements #14378
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1676,4 +1676,54 @@ TEST(TestSparseCSFMatrixForUInt64Index, Make) { | |
| ASSERT_RAISES(Invalid, SparseCSFTensor::Make(dense_tensor, uint64())); | ||
| } | ||
|
|
||
| //----------------------------------------------------------------------------- | ||
| // Create SparseTensors from a dense Tensor with only zeros | ||
|
|
||
| template <typename SparseTensorType> | ||
| class TestSparseTensorFromDenseBase : public ::testing::Test { | ||
| public: | ||
| void SetUp() { | ||
| shape_ = {0, 12}; | ||
| dim_names_ = {"foo", "bar"}; | ||
| dense_values_ = {}; | ||
| dense_data_ = Buffer::Wrap(dense_values_); | ||
| } | ||
|
|
||
| protected: | ||
| std::vector<int64_t> shape_; | ||
| std::vector<std::string> dim_names_; | ||
| std::vector<int64_t> dense_values_; | ||
| std::shared_ptr<Buffer> dense_data_; | ||
| }; | ||
|
|
||
| template <typename SparseTensorType> | ||
| class TestSparseTensorFromDense : public TestSparseTensorFromDenseBase<SparseTensorType> { | ||
| }; | ||
|
|
||
| TYPED_TEST_SUITE_P(TestSparseTensorFromDense); | ||
|
|
||
| TYPED_TEST_P(TestSparseTensorFromDense, TestNonZeroLength) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Ok, but is it the situation that the PR is meant to be fixing?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Indeed. I changed the c++ test so that one dimension is now 0. |
||
| using SparseTensorType = TypeParam; | ||
|
|
||
| NumericTensor<Int64Type> dense_tensor_ = | ||
| NumericTensor<Int64Type>(this->dense_data_, this->shape_, {}, this->dim_names_); | ||
| ASSERT_OK_AND_ASSIGN( | ||
| auto sparse_tensor_, | ||
| SparseTensorType::Make(dense_tensor_, TypeTraits<Int64Type>::type_singleton())); | ||
| ASSERT_EQ(sparse_tensor_->non_zero_length(), 0); | ||
| ASSERT_EQ(sparse_tensor_->shape(), this->shape_); | ||
| ASSERT_EQ(sparse_tensor_->dim_names(), this->dim_names_); | ||
| } | ||
|
|
||
| REGISTER_TYPED_TEST_SUITE_P(TestSparseTensorFromDense, TestNonZeroLength); | ||
|
|
||
| INSTANTIATE_TYPED_TEST_SUITE_P(TestSparseCOOTensor, TestSparseTensorFromDense, | ||
| SparseCOOTensor); | ||
| INSTANTIATE_TYPED_TEST_SUITE_P(TestSparseCSRMatrix, TestSparseTensorFromDense, | ||
| SparseCSRMatrix); | ||
| INSTANTIATE_TYPED_TEST_SUITE_P(TestSparseCSCMatrix, TestSparseTensorFromDense, | ||
| SparseCSCMatrix); | ||
| INSTANTIATE_TYPED_TEST_SUITE_P(TestSparseCSFTensor, TestSparseTensorFromDense, | ||
| SparseCSFTensor); | ||
|
|
||
| } // namespace arrow | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -33,7 +33,6 @@ | |
| except ImportError: | ||
| sparse = None | ||
|
|
||
|
|
||
| tensor_type_pairs = [ | ||
| ('i1', pa.int8()), | ||
| ('i2', pa.int16()), | ||
|
|
@@ -434,6 +433,23 @@ def test_sparse_coo_tensor_scipy_roundtrip(dtype_str, arrow_type): | |
| assert sparse_tensor.has_canonical_format | ||
| assert out_scipy_matrix.has_canonical_format | ||
|
|
||
| scipy_matrix = coo_matrix([[0, 0], [0, 0]]) | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Does this actually create a tensor with zero elements?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, coo_matrix automatically ignores zeros, I'll add an assertion checking for number of elements to make this explicit.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well actually it's:
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Right, but the shape is all non-zero.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Well those are actually different constructors (shape seems to be ignored in this case): https://docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.coo_matrix.html
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But this doesn't address the issue, does it?
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reported issue was due to
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I might be missing the point though :D
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @pitrou ping
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I believe @rok has the correct test here but I could be wrong. The original issue is about a sparse matrix with a valid non-zero shape but no elements: |
||
| sparse_tensor = pa.SparseCOOTensor.from_scipy(scipy_matrix, | ||
| dim_names=dim_names) | ||
| out_scipy_matrix = sparse_tensor.to_scipy() | ||
| dense_array = scipy_matrix.toarray() | ||
|
|
||
| assert scipy_matrix.has_canonical_format | ||
| assert sparse_tensor.has_canonical_format | ||
| assert out_scipy_matrix.has_canonical_format | ||
|
|
||
| assert scipy_matrix.nnz == 0 | ||
| assert scipy_matrix.nnz == sparse_tensor.non_zero_length | ||
| assert np.array_equal(scipy_matrix.data, out_scipy_matrix.data) | ||
| assert np.array_equal(scipy_matrix.row, out_scipy_matrix.row) | ||
| assert np.array_equal(scipy_matrix.col, out_scipy_matrix.col) | ||
| assert np.array_equal(dense_array, sparse_tensor.to_tensor().to_numpy()) | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not csr_matrix, reason="requires scipy") | ||
| @pytest.mark.parametrize('dtype_str,arrow_type', tensor_type_pairs) | ||
|
|
@@ -464,6 +480,19 @@ def test_sparse_csr_matrix_scipy_roundtrip(dtype_str, arrow_type): | |
| dense_array = sparse_array.toarray() | ||
| assert np.array_equal(dense_array, sparse_tensor.to_tensor().to_numpy()) | ||
|
|
||
| scipy_matrix = csr_matrix([[0, 0], [0, 0]]) | ||
| sparse_tensor = pa.SparseCSRMatrix.from_scipy(scipy_matrix, | ||
| dim_names=dim_names) | ||
| out_scipy_matrix = sparse_tensor.to_scipy() | ||
| dense_array = scipy_matrix.toarray() | ||
|
|
||
| assert scipy_matrix.nnz == 0 | ||
| assert scipy_matrix.nnz == sparse_tensor.non_zero_length | ||
| assert np.array_equal(scipy_matrix.data, out_scipy_matrix.data) | ||
| assert np.array_equal(scipy_matrix.indptr, out_scipy_matrix.indptr) | ||
| assert np.array_equal(scipy_matrix.indices, out_scipy_matrix.indices) | ||
| assert np.array_equal(dense_array, sparse_tensor.to_tensor().to_numpy()) | ||
|
|
||
|
|
||
| @pytest.mark.skipif(not sparse, reason="requires pydata/sparse") | ||
| @pytest.mark.parametrize('dtype_str,arrow_type', tensor_type_pairs) | ||
|
|
@@ -489,3 +518,16 @@ def test_pydata_sparse_sparse_coo_tensor_roundtrip(dtype_str, arrow_type): | |
| assert np.array_equal(sparse_array.coords, out_sparse_array.coords) | ||
| assert np.array_equal(sparse_array.todense(), | ||
| sparse_tensor.to_tensor().to_numpy()) | ||
|
|
||
| sparse_array = sparse.COO.from_numpy([[0, 0], [0, 0]]) | ||
| sparse_tensor = pa.SparseCOOTensor.from_pydata_sparse(sparse_array, | ||
| dim_names=dim_names) | ||
| out_sparse_array = sparse_tensor.to_pydata_sparse() | ||
| dense_array = sparse_array.todense() | ||
|
|
||
| assert sparse_array.nnz == 0 | ||
| assert sparse_array.nnz == sparse_tensor.non_zero_length | ||
| assert out_sparse_array.nnz == sparse_tensor.non_zero_length | ||
| assert np.array_equal(sparse_array.data, out_sparse_array.data) | ||
| assert np.array_equal(sparse_array.coords, out_sparse_array.coords) | ||
| assert np.array_equal(dense_array, sparse_tensor.to_tensor().to_numpy()) | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't understand what is happening here (I don't understand much about the tensors so not surprising). Why is it ok if one of the shape elements is zero? I would expect an empty sparse matrix to still have a shape: