Skip to content

GH-50906: [Python] Reshape 1D tensors to 2D in SparseCSR/CSC matrix conversion - #50907

Open
pratyushadk wants to merge 1 commit into
apache:mainfrom
pratyushadk:GH-50906-csx-1d-tensor-support
Open

GH-50906: [Python] Reshape 1D tensors to 2D in SparseCSR/CSC matrix conversion#50907
pratyushadk wants to merge 1 commit into
apache:mainfrom
pratyushadk:GH-50906-csx-1d-tensor-support

Conversation

@pratyushadk

@pratyushadk pratyushadk commented Aug 18, 2026

Copy link
Copy Markdown
Contributor

Rationale for this change

Calling SparseCSRMatrix.from_tensor() or SparseCSCMatrix.from_tensor() with a 1D Arrow tensor currently raises a NotImplemented error from the C++ layer. A 1D vector has a natural sparse representation as a single-row matrix — the same way scipy treats 1D input to csr_array. This change adds that support at the Python layer by reshaping the tensor to [1, n] before passing it to the C++ backend.

What changes are included in this PR?

python/pyarrow/tensor.pxi

SparseCSRMatrix.from_tensor and SparseCSCMatrix.from_tensor now check if the input tensor is 1D. If so, it is reshaped to [1, n] (a single-row matrix) before being passed to the C++ conversion layer.

python/pyarrow/tests/test_sparse_tensor.py

Two new tests: test_sparse_csr_matrix_from_1d and test_sparse_csc_matrix_from_1d. Each verifies creation from a 1D numpy array (from_dense_numpy), creation from a 1D Arrow tensor (from_tensor), and that the resulting shape is (1, n) with the correct non-zero count.

Are these changes tested?

Yes. The two new tests cover both from_dense_numpy and from_tensor paths for CSR and CSC. The existing sparse tensor test suite is unchanged.

Are there any user-facing changes?

SparseCSRMatrix.from_tensor, SparseCSRMatrix.from_dense_numpy, SparseCSCMatrix.from_tensor, and SparseCSCMatrix.from_dense_numpy now accept 1D tensors and numpy arrays. Previously these raised a NotImplemented error.

@pratyushadk
pratyushadk requested a review from pitrou as a code owner August 18, 2026 19:07
Copilot AI lite review requested due to automatic review settings August 18, 2026 19:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added the awaiting review Awaiting review label Aug 18, 2026
@github-actions

Copy link
Copy Markdown

⚠️ GitHub issue #50906 has been automatically assigned in GitHub to PR creator.

@pitrou
pitrou requested a review from rok August 24, 2026 11:36
Copilot AI review requested due to automatic review settings August 24, 2026 16:05
@pratyushadk
pratyushadk force-pushed the GH-50906-csx-1d-tensor-support branch from fd4c81d to 0e17ebe Compare August 24, 2026 16:05

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@rok rok changed the title GH-50906: [C++] Support 1D tensors in SparseCSR/CSC matrix conversion GH-50906: [Python Support 1D tensors in SparseCSR/CSC matrix conversion Aug 24, 2026
@rok rok changed the title GH-50906: [Python Support 1D tensors in SparseCSR/CSC matrix conversion GH-50906: [Python] Support 1D tensors in SparseCSR/CSC matrix conversion Aug 24, 2026
@rok rok changed the title GH-50906: [Python] Support 1D tensors in SparseCSR/CSC matrix conversion GH-50906: [Python] Reshape 1D tensors to 2D in SparseCSR/CSC matrix conversion Aug 24, 2026
@rok

rok commented Aug 24, 2026

Copy link
Copy Markdown
Member

@pratyushadk I've renamed the issue and the PR to reflect that this should be implemented in Python. Let's move the discussion here.
Please update this PR with the Python solution.
Please do not copy paste agent output here. The goal is that you understand what you're doing and that you learn from it.

Copilot AI review requested due to automatic review settings August 24, 2026 17:21
@pratyushadk
pratyushadk force-pushed the GH-50906-csx-1d-tensor-support branch from 0e17ebe to cccd951 Compare August 24, 2026 17:21

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@pratyushadk

Copy link
Copy Markdown
Contributor Author

@rok Thanks for the direction. I'm new to open source and your feedback really helped me understand where the fix actually belongs. Updated the PR with the Python reshape approach. Looking forward to contributing more in the future!

Comment on lines +257 to +284
def test_sparse_csr_matrix_from_1d():
array = np.array([1, 0, 2, 0, 0, 3, 0, 4], dtype=np.int64)
tensor = pa.Tensor.from_numpy(array)

sparse = pa.SparseCSRMatrix.from_dense_numpy(array)
assert sparse.shape == (1, 8)
assert sparse.non_zero_length == 4

sparse = pa.SparseCSRMatrix.from_tensor(tensor)
assert sparse.shape == (1, 8)
assert sparse.non_zero_length == 4

dense = sparse.to_tensor()
assert dense.shape == (1, 8)
assert np.array_equal(np.array(dense).ravel(), array)


def test_sparse_csc_matrix_from_1d():
array = np.array([1, 0, 2, 0, 0, 3, 0, 4], dtype=np.int64)
tensor = pa.Tensor.from_numpy(array)

sparse = pa.SparseCSCMatrix.from_dense_numpy(array)
assert sparse.shape == (1, 8)
assert sparse.non_zero_length == 4

sparse = pa.SparseCSCMatrix.from_tensor(tensor)
assert sparse.shape == (1, 8)
assert sparse.non_zero_length == 4

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please make these parametric so they can be expressed as a single test and compare pyarrow's behavior to scipy's.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

@github-actions github-actions Bot added awaiting changes Awaiting changes and removed awaiting review Awaiting review labels Aug 24, 2026
Copilot AI review requested due to automatic review settings August 24, 2026 17:47
@pratyushadk
pratyushadk force-pushed the GH-50906-csx-1d-tensor-support branch from cccd951 to 6c7f030 Compare August 24, 2026 17:47

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Aug 24, 2026
Comment on lines +30 to +37
csr_array, csc_array, coo_array, csr_matrix, csc_matrix, coo_matrix
)
except ImportError:
coo_matrix = None
csr_matrix = None
csc_matrix = None
csr_array = None
csc_array = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't seem to be using csc_array. Please remove the import.

@github-actions github-actions Bot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Aug 24, 2026
Comment on lines +266 to +267
if sc_class is None:
pytest.skip('scipy not available')

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Elsewhere in this file we use:

@pytest.mark.skipif(not coo_matrix, reason="requires scipy")

as a test decorator. Let's do the same here.

Comment thread python/pyarrow/tests/test_sparse_tensor.py Outdated
Copilot AI review requested due to automatic review settings August 24, 2026 18:17
@pratyushadk
pratyushadk force-pushed the GH-50906-csx-1d-tensor-support branch from 6c7f030 to 9eec858 Compare August 24, 2026 18:17

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Aug 24, 2026
except ImportError:
coo_matrix = None
csr_matrix = None
csc_matrix = None

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use csc_array and avoid even this new import?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I removed that new import. Thanks for pointing it out!

@github-actions github-actions Bot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Aug 24, 2026
@pratyushadk
pratyushadk force-pushed the GH-50906-csx-1d-tensor-support branch from 9eec858 to b6e8abf Compare August 24, 2026 18:50
Copilot AI review requested due to automatic review settings August 24, 2026 18:50

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Aug 24, 2026
Comment on lines +272 to +274
sparse_tensor = pa_class.from_tensor(tensor)
assert np.array_equal(sparse_tensor.to_tensor().to_numpy(),
scipy_matrix.toarray())

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is redundant too.

@github-actions github-actions Bot added awaiting changes Awaiting changes and removed awaiting change review Awaiting change review labels Aug 24, 2026
Comment on lines +257 to +261
@pytest.mark.skipif(not csr_matrix, reason="requires scipy")
@pytest.mark.parametrize('pa_class', [
pa.SparseCSRMatrix,
pa.SparseCSCMatrix,
])

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We've lost csc and csr scipy objects. Try introducing csr_array and csc_array.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I added csr_array and csc_array coverage by checking the type returned from to_scipy() for both CSR and CSC.

For the 1D dense-input reference, I retained csr_matrix and csc_matrix: csc_array does not accept 1D input (ValueError: CSC arrays don't support 1D input. Use 2D), while csr_array preserves it as 1D (n,). The matrix classes normalize the input to (1, n), matching Arrow's result. I also removed the redundant from_dense_numpy check.

Copilot AI review requested due to automatic review settings August 24, 2026 19:26
@pratyushadk
pratyushadk force-pushed the GH-50906-csx-1d-tensor-support branch from b6e8abf to ab6a7e0 Compare August 24, 2026 19:26

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@github-actions github-actions Bot added awaiting change review Awaiting change review and removed awaiting changes Awaiting changes labels Aug 24, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants