Skip to content

Commit f9dc481

Browse files
authored
Eigen: Demonstrate and fix issue with passing scipy.sparse matrices with unsorted indices (#981)
1 parent 9f0a580 commit f9dc481

File tree

2 files changed

+30
-0
lines changed

2 files changed

+30
-0
lines changed

include/nanobind/eigen/sparse.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,10 @@ template <typename T> struct type_caster<T, enable_if_t<is_eigen_sparse_matrix_v
6262
return false;
6363
}
6464

65+
bool indices_sorted = cast<bool>(obj.attr("has_sorted_indices"));
66+
if (!indices_sorted)
67+
obj.attr("sort_indices")();
68+
6569
if (object data_o = obj.attr("data"); !data_caster.from_python(data_o, flags, cleanup))
6670
return false;
6771
ScalarNDArray& values = data_caster.value;

tests/test_eigen.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -231,6 +231,26 @@ def test07_mutate_arg():
231231
assert_array_equal(A, 2*A2)
232232

233233

234+
def create_spmat_unsorted():
235+
import scipy.sparse as sparse
236+
# Create a small matrix with explicit indices and indptr
237+
data = np.array([1.0, 2.0, 3.0, 4.0, 5.0])
238+
239+
# Deliberately unsorted indices within columns
240+
# For a properly sorted CSC matrix, indices should be sorted within each column
241+
indices = np.array([0, 2, 1, 4, 3]) # Unsorted (should be [0, 1, 2, 3, 4])
242+
243+
# indptr points to where each column starts in the indices/data arrays
244+
indptr = np.array([0, 2, 3, 5])
245+
246+
# Create a 5x3 matrix with unsorted indices
247+
unsorted_csc = sparse.csc_matrix((data, indices, indptr), shape=(5, 3))
248+
249+
# Verify that indices are unsorted
250+
assert not unsorted_csc.has_sorted_indices
251+
return unsorted_csc
252+
253+
234254
@needs_numpy_and_eigen
235255
def test08_sparse():
236256
pytest.importorskip("scipy")
@@ -244,6 +264,7 @@ def test08_sparse():
244264
assert type(t.sparse_copy_r(t.sparse_c())) is scipy.sparse.csr_matrix
245265
assert type(t.sparse_copy_c(t.sparse_r())) is scipy.sparse.csc_matrix
246266

267+
247268
def assert_sparse_equal_ref(sparse_mat):
248269
ref = np.array(
249270
[
@@ -263,6 +284,11 @@ def assert_sparse_equal_ref(sparse_mat):
263284
assert_sparse_equal_ref(t.sparse_copy_r(t.sparse_c()))
264285
assert_sparse_equal_ref(t.sparse_copy_c(t.sparse_r()))
265286

287+
# construct scipy matrix with unsorted indices
288+
assert type(t.sparse_copy_c(create_spmat_unsorted())) is scipy.sparse.csc_matrix
289+
mat_unsort = create_spmat_unsorted()
290+
assert_array_equal(t.sparse_copy_c(mat_unsort).toarray(), create_spmat_unsorted().toarray())
291+
266292

267293
@needs_numpy_and_eigen
268294
def test09_sparse_failures():

0 commit comments

Comments
 (0)