diff --git a/pgvector/sparsevec.py b/pgvector/sparsevec.py index 3631f78..be431dd 100644 --- a/pgvector/sparsevec.py +++ b/pgvector/sparsevec.py @@ -115,10 +115,15 @@ def _from_sparse(self, arr: sparray | spmatrix, /) -> None: if hasattr(value, 'coords'): # scipy 1.13+ - self._indices = value.coords[-1].tolist() + indices = value.coords[-1].tolist() else: - self._indices = value.col.tolist() - self._values = [float(v) for v in value.data] + indices = value.col.tolist() + + # coordinates are not necessarily ordered, and sparsevec requires ascending indices + elements = sorted(zip(indices, value.data)) + + self._indices = [int(v[0]) for v in elements] + self._values = [float(v[1]) for v in elements] def _from_dense(self, value: list[float] | ndarray, /) -> None: self._dim = len(value) diff --git a/tests/test_sparse_vector.py b/tests/test_sparse_vector.py index 1924f98..59a943a 100644 --- a/tests/test_sparse_vector.py +++ b/tests/test_sparse_vector.py @@ -60,6 +60,17 @@ def test_coo_array(self) -> None: assert vec.indices() == [0, 2, 4] assert isinstance(vec.values()[0], float) + def test_coo_array_unordered(self) -> None: + if np is None or sparse is None: + pytest.skip('NumPy and SciPy required') + + arr = sparse.coo_array(([3, 1, 2], ([4, 0, 2],)), shape=(6,)) + vec = SparseVector(arr) + assert vec.indices() == [0, 2, 4] + assert vec.values() == [1, 2, 3] + assert vec.to_text() == '{1:1.0,3:2.0,5:3.0}/6' + assert vec.to_binary() == pack('>iii3i3f', 6, 3, 0, 0, 2, 4, 1, 2, 3) + def test_coo_array_dimensions(self) -> None: if np is None or sparse is None: pytest.skip('NumPy and SciPy required')