Skip to content

Commit 73c955f

Browse files
ci: Add doctests to CI again. (#767)
Co-authored-by: Willow Ahrens <[email protected]>
1 parent b4ba7c6 commit 73c955f

20 files changed

+6309
-83
lines changed

.github/workflows/ci.yml

+9-1
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,12 @@ jobs:
5050
pip install -e '.[tests]'
5151
- name: Run tests
5252
run: |
53-
SPARSE_BACKEND=Numba pytest --pyargs sparse --cov-report=xml:coverage_Numba.xml -n auto -vvv
53+
if [ $(python -c 'import numpy as np; print(np.lib.NumpyVersion(np.__version__) >= "2.0.0a1")') = 'True' ]; then
54+
pytest --pyargs sparse --doctest-modules --cov-report=xml:coverage_Numba.xml -n auto -vvv
55+
else
56+
pytest --pyargs sparse --cov-report=xml:coverage_Numba.xml -n auto -vvv
57+
fi
58+
python -c 'import finch'
5459
SPARSE_BACKEND=Finch pytest --pyargs sparse/tests --cov-report=xml:coverage_Finch.xml -n auto -vvv
5560
SPARSE_BACKEND=MLIR pytest --pyargs sparse/mlir_backend --cov-report=xml:coverage_MLIR.xml -n auto -vvv
5661
- uses: codecov/codecov-action@v5
@@ -126,6 +131,9 @@ jobs:
126131
SPARSE_BACKEND: ${{ matrix.backend }}
127132
run: |
128133
cd ${GITHUB_WORKSPACE}/array-api-tests
134+
if [ "${SPARSE_BACKEND}" = "Finch" ]; then
135+
python -c 'import finch'
136+
fi
129137
pytest array_api_tests -v -c pytest.ini -n auto --max-examples=2 --derandomize --disable-deadline -o xfail_strict=True --xfails-file ${GITHUB_WORKSPACE}/ci/${{ matrix.backend }}-array-api-xfails.txt --skips-file ${GITHUB_WORKSPACE}/ci/${{ matrix.backend }}-array-api-skips.txt
130138
131139
on:

.gitignore

+1-1
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,7 @@ coverage.xml
4949
test_results/
5050
junit/
5151
.hypothesis/
52+
coverage_*.xml
5253

5354
# Translations
5455
*.mo
@@ -85,4 +86,3 @@ docs/examples_ipynb/
8586

8687
# Pixi envs
8788
.pixi/
88-
pixi.lock

ci/environment.yml

+7-7
Original file line numberDiff line numberDiff line change
@@ -5,14 +5,14 @@ channels:
55
dependencies:
66
- python
77
- pip
8-
- numpy
9-
- numba
10-
- scipy
11-
- dask
12-
- pytest
13-
- pytest-cov
14-
- pytest-xdist
158
- pip:
169
- finch-tensor>=0.2.2
1710
- finch-mlir>=0.0.2
1811
- pytest-codspeed
12+
- numpy
13+
- numba
14+
- scipy
15+
- dask
16+
- pytest
17+
- pytest-cov
18+
- pytest-xdist

conftest.py

+17-3
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
import pathlib
2+
3+
import sparse
4+
15
import pytest
26

37

@@ -7,8 +11,18 @@ def add_doctest_modules(doctest_namespace):
711

812
import numpy as np
913

10-
if sparse._BackendType.Numba != sparse._BACKEND:
11-
pass # TODO: pytest.skip() skips Finch and MLIR tests
12-
1314
doctest_namespace["np"] = np
1415
doctest_namespace["sparse"] = sparse
16+
17+
18+
def pytest_ignore_collect(collection_path: pathlib.Path, config: pytest.Config) -> bool | None:
19+
if "numba_backend" in collection_path.parts and sparse._BackendType.Numba != sparse._BACKEND:
20+
return True
21+
22+
if "mlir_backend" in collection_path.parts and sparse._BackendType.MLIR != sparse._BACKEND:
23+
return True
24+
25+
if "finch_backend" in collection_path.parts and sparse._BackendType.Finch != sparse._BACKEND:
26+
return True
27+
28+
return None

pixi.lock

+6,199
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pixi.toml

+16-12
Original file line numberDiff line numberDiff line change
@@ -12,29 +12,28 @@ numpy = ">=1.17"
1212
[dependencies]
1313
python = ">=3.10,<3.13"
1414

15-
[feature.extras.pypi-dependencies]
15+
[feature.extra.pypi-dependencies]
1616
dask = { version = ">=2024", extras = ["array"] }
1717
scipy = ">=0.19"
1818
scikit-learn = "*"
1919

20-
[feature.docs.pypi-dependencies]
20+
[feature.doc.pypi-dependencies]
2121
mkdocs-material = "*"
2222
mkdocstrings = { version = "*", extras = ["python"] }
2323
mkdocs-gen-files = "*"
2424
mkdocs-literate-nav = "*"
2525
mkdocs-section-index = "*"
2626
mkdocs-jupyter = "*"
2727

28-
[feature.tests.tasks]
29-
test = "pytest --pyargs sparse -n auto"
30-
test-mlir = { cmd = "pytest --pyargs sparse.mlir_backend -v" }
28+
[feature.test.tasks]
29+
test = "pytest -n auto --doctest-modules"
30+
test-mlir = "pytest --pyargs sparse.mlir_backend -v"
3131
test-finch = { cmd = "pytest --pyargs sparse/tests -n auto -v", depends-on = ["precompile"] }
3232

33-
[feature.tests.dependencies]
33+
[feature.test.pypi-dependencies]
3434
pytest = ">=3.5"
3535
pytest-cov = "*"
3636
pytest-xdist = "*"
37-
pre-commit = "*"
3837
pytest-codspeed = "*"
3938

4039
[feature.notebooks.pypi-dependencies]
@@ -51,6 +50,7 @@ matrepr = "*"
5150
precompile = "python -c 'import finch'"
5251

5352
[feature.finch.dependencies]
53+
python = ">=3.10"
5454
juliaup = ">=1.17.10"
5555

5656
[feature.finch.pypi-dependencies]
@@ -63,16 +63,20 @@ SPARSE_BACKEND = "Finch"
6363
[feature.finch.target.osx-arm64.activation.env]
6464
PYTHONFAULTHANDLER = "${HOME}/faulthandler.log"
6565

66+
[feature.mlir.dependencies]
67+
python = ">=3.10"
68+
6669
[feature.mlir.pypi-dependencies]
6770
scipy = ">=0.19"
6871
finch-mlir = ">=0.0.2"
72+
"PyYAML" = "*"
6973

7074
[feature.mlir.activation.env]
7175
SPARSE_BACKEND = "MLIR"
7276

7377
[environments]
74-
tests = ["tests", "extras"]
75-
docs = ["docs", "extras"]
76-
mlir-dev = {features = ["tests", "mlir"], no-default-feature = true}
77-
finch-dev = {features = ["tests", "finch"], no-default-feature = true}
78-
notebooks = ["extras", "mlir", "finch", "notebooks"]
78+
test = ["test", "extra"]
79+
doc = ["doc", "extra"]
80+
mlir-dev = {features = ["test", "mlir"], no-default-feature = true}
81+
finch-dev = {features = ["test", "finch"], no-default-feature = true}
82+
notebooks = ["extra", "mlir", "finch", "notebooks"]

pytest.ini

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
[pytest]
2-
addopts = --cov-report term-missing --cov-report html --cov-report=term:skip-covered --cov sparse --cov-config .coveragerc --junitxml=junit/test-results.xml
2+
addopts = --cov-report term-missing --cov-report html --cov-report=term:skip-covered --cov sparse --cov-config .coveragerc
33
filterwarnings =
44
ignore::PendingDeprecationWarning
55
testpaths =

sparse/mlir_backend/tests/__init__.py

Whitespace-only changes.

sparse/mlir_backend/tests/test_simple.py

-3
Original file line numberDiff line numberDiff line change
@@ -9,9 +9,6 @@
99
import numpy as np
1010
import scipy.sparse as sps
1111

12-
if sparse._BACKEND != sparse._BackendType.MLIR:
13-
pytest.skip("skipping MLIR tests", allow_module_level=True)
14-
1512
parametrize_dtypes = pytest.mark.parametrize(
1613
"dtype",
1714
[

sparse/numba_backend/_common.py

+8-8
Original file line numberDiff line numberDiff line change
@@ -1176,13 +1176,13 @@ def _parse_einsum_input(operands):
11761176
Examples
11771177
--------
11781178
The operand list is simplified to reduce printing:
1179-
>>> np.random.seed(123)
1180-
>>> a = np.random.rand(4, 4)
1181-
>>> b = np.random.rand(4, 4, 4)
1182-
>>> _parse_einsum_input(("...a,...a->...", a, b))
1183-
('za,xza', 'xz', [a, b]) # may vary
1184-
>>> _parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0]))
1185-
('za,xza', 'xz', [a, b]) # may vary
1179+
>>> rng = np.random.default_rng(42)
1180+
>>> a = rng.random((4, 4))
1181+
>>> b = rng.random((4, 4, 4))
1182+
>>> _parse_einsum_input(("...a,...a->...", a, b)) # doctest: +SKIP
1183+
('za,xza', 'xz', [a, b])
1184+
>>> _parse_einsum_input((a, [Ellipsis, 0], b, [Ellipsis, 0])) # doctest: +SKIP
1185+
('za,xza', 'xz', [a, b])
11861186
"""
11871187

11881188
if len(operands) == 0:
@@ -2061,7 +2061,7 @@ def asarray(obj, /, *, dtype=None, format="coo", copy=False, device=None):
20612061
Examples
20622062
--------
20632063
>>> x = np.eye(8, dtype="i8")
2064-
>>> sparse.asarray(x, format="COO")
2064+
>>> sparse.asarray(x, format="coo")
20652065
<COO: shape=(8, 8), dtype=int64, nnz=8, fill_value=0>
20662066
"""
20672067

sparse/numba_backend/_compressed/compressed.py

+1-1
Original file line numberDiff line numberDiff line change
@@ -819,7 +819,7 @@ def _prune(self):
819819
--------
820820
>>> coords = np.array([[0, 1, 2, 3]])
821821
>>> data = np.array([1, 0, 1, 2])
822-
>>> s = COO(coords, data).asformat("gcxs")
822+
>>> s = COO(coords, data, shape=(4,)).asformat("gcxs")
823823
>>> s._prune()
824824
>>> s.nnz
825825
3

sparse/numba_backend/_coo/common.py

+6-6
Original file line numberDiff line numberDiff line change
@@ -901,7 +901,7 @@ def diagonalize(a, axis=0):
901901
>>> a = sparse.random((3, 3, 3, 3, 3), density=0.3)
902902
>>> a_diag = sparse.diagonalize(a, axis=2)
903903
>>> (sparse.diagonal(a_diag, axis1=2, axis2=5) == a.transpose([0, 1, 3, 4, 2])).all()
904-
True
904+
np.True_
905905
906906
Returns
907907
-------
@@ -944,7 +944,7 @@ def isposinf(x, out=None):
944944
--------
945945
[`numpy.isposinf`][] : The NumPy equivalent
946946
"""
947-
from .core import elemwise
947+
from sparse import elemwise
948948

949949
return elemwise(lambda x, out=None, dtype=None: np.isposinf(x, out=out), x, out=out)
950950

@@ -971,7 +971,7 @@ def isneginf(x, out=None):
971971
--------
972972
[`numpy.isneginf`][] : The NumPy equivalent
973973
"""
974-
from .core import elemwise
974+
from sparse import elemwise
975975

976976
return elemwise(lambda x, out=None, dtype=None: np.isneginf(x, out=out), x, out=out)
977977

@@ -1234,7 +1234,7 @@ def unique_values(x, /):
12341234
>>> import sparse
12351235
>>> x = sparse.COO.from_numpy([1, 0, 2, 1, 2, -3])
12361236
>>> sparse.unique_values(x)
1237-
array([-3, 0, 1, 2])
1237+
array([-3, 0, 1, 2])
12381238
"""
12391239

12401240
x = _validate_coo_input(x)
@@ -1279,9 +1279,9 @@ def sort(x, /, *, axis=-1, descending=False, stable=False):
12791279
>>> import sparse
12801280
>>> x = sparse.COO.from_numpy([1, 0, 2, 0, 2, -3])
12811281
>>> sparse.sort(x).todense()
1282-
array([-3, 0, 0, 1, 2, 2])
1282+
array([-3, 0, 0, 1, 2, 2])
12831283
>>> sparse.sort(x, descending=True).todense()
1284-
array([ 2, 2, 1, 0, 0, -3])
1284+
array([ 2, 2, 1, 0, 0, -3])
12851285
12861286
"""
12871287
from .._common import moveaxis

sparse/numba_backend/_coo/core.py

+13-12
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,7 @@ class COO(SparseArray, NDArrayOperatorsMixin): # lgtm [py/missing-equals]
156156
>>> rows = [0, 1, 2, 3, 4]
157157
>>> cols = [0, 0, 0, 1, 1]
158158
>>> data = [10, 20, 30, 40, 50]
159-
>>> z = COO((data, (rows, cols)))
159+
>>> z = COO((data, (rows, cols)), shape=(5, 2))
160160
>>> z.todense() # doctest: +NORMALIZE_WHITESPACE
161161
array([[10, 0],
162162
[20, 0],
@@ -168,10 +168,10 @@ class COO(SparseArray, NDArrayOperatorsMixin): # lgtm [py/missing-equals]
168168
indices imply summation:
169169
170170
>>> d = {(0, 0, 0): 1, (1, 2, 3): 2, (1, 1, 0): 3}
171-
>>> COO(d)
171+
>>> COO(d, shape=(2, 3, 4))
172172
<COO: shape=(2, 3, 4), dtype=int64, nnz=3, fill_value=0>
173173
>>> L = [((0, 0), 1), ((1, 1), 2), ((0, 0), 3)]
174-
>>> COO(L).todense() # doctest: +NORMALIZE_WHITESPACE
174+
>>> COO(L, shape=(2, 2)).todense() # doctest: +NORMALIZE_WHITESPACE
175175
array([[4, 0],
176176
[0, 2]])
177177
@@ -440,6 +440,7 @@ def from_scipy_sparse(cls, x, /, *, fill_value=None):
440440
441441
Examples
442442
--------
443+
>>> import scipy.sparse
443444
>>> x = scipy.sparse.rand(6, 3, density=0.2)
444445
>>> s = COO.from_scipy_sparse(x)
445446
>>> np.array_equal(x.todense(), s.todense())
@@ -459,7 +460,7 @@ def from_scipy_sparse(cls, x, /, *, fill_value=None):
459460
)
460461

461462
@classmethod
462-
def from_iter(cls, x, shape=None, fill_value=None, dtype=None):
463+
def from_iter(cls, x, shape, fill_value=None, dtype=None):
463464
"""
464465
Converts an iterable in certain formats to a [`sparse.COO`][] array. See examples
465466
for details.
@@ -468,7 +469,7 @@ def from_iter(cls, x, shape=None, fill_value=None, dtype=None):
468469
----------
469470
x : Iterable or Iterator
470471
The iterable to convert to [`sparse.COO`][].
471-
shape : tuple[int], optional
472+
shape : tuple[int]
472473
The shape of the array.
473474
fill_value : scalar
474475
The fill value for this array.
@@ -486,31 +487,31 @@ def from_iter(cls, x, shape=None, fill_value=None, dtype=None):
486487
Here, the first part represents the coordinate and the second part represents the value.
487488
488489
>>> x = [((0, 0), 1), ((1, 1), 1)]
489-
>>> s = COO.from_iter(x)
490+
>>> s = COO.from_iter(x, shape=(2, 2))
490491
>>> s.todense()
491492
array([[1, 0],
492493
[0, 1]])
493494
494495
You can also have a similar format with a dictionary.
495496
496497
>>> x = {(0, 0): 1, (1, 1): 1}
497-
>>> s = COO.from_iter(x)
498+
>>> s = COO.from_iter(x, shape=(2, 2))
498499
>>> s.todense()
499500
array([[1, 0],
500501
[0, 1]])
501502
502503
The third supported format is ``(data, (..., row, col))``.
503504
504505
>>> x = ([1, 1], ([0, 1], [0, 1]))
505-
>>> s = COO.from_iter(x)
506+
>>> s = COO.from_iter(x, shape=(2, 2))
506507
>>> s.todense()
507508
array([[1, 0],
508509
[0, 1]])
509510
510511
You can also pass in a [`collections.abc.Iterator`][] object.
511512
512513
>>> x = [((0, 0), 1), ((1, 1), 1)].__iter__()
513-
>>> s = COO.from_iter(x)
514+
>>> s = COO.from_iter(x, shape=(2, 2))
514515
>>> s.todense()
515516
array([[1, 0],
516517
[0, 1]])
@@ -1293,7 +1294,7 @@ def _sort_indices(self):
12931294
--------
12941295
>>> coords = np.array([[1, 2, 0]], dtype=np.uint8)
12951296
>>> data = np.array([4, 1, 3], dtype=np.uint8)
1296-
>>> s = COO(coords, data)
1297+
>>> s = COO(coords, data, shape=(3,))
12971298
>>> s._sort_indices()
12981299
>>> s.coords # doctest: +NORMALIZE_WHITESPACE
12991300
array([[0, 1, 2]], dtype=uint8)
@@ -1321,7 +1322,7 @@ def _sum_duplicates(self):
13211322
--------
13221323
>>> coords = np.array([[0, 1, 1, 2]], dtype=np.uint8)
13231324
>>> data = np.array([6, 5, 2, 2], dtype=np.uint8)
1324-
>>> s = COO(coords, data)
1325+
>>> s = COO(coords, data, shape=(3,))
13251326
>>> s._sum_duplicates()
13261327
>>> s.coords # doctest: +NORMALIZE_WHITESPACE
13271328
array([[0, 1, 2]], dtype=uint8)
@@ -1354,7 +1355,7 @@ def _prune(self):
13541355
--------
13551356
>>> coords = np.array([[0, 1, 2, 3]])
13561357
>>> data = np.array([1, 0, 1, 2])
1357-
>>> s = COO(coords, data)
1358+
>>> s = COO(coords, data, shape=(4,))
13581359
>>> s._prune()
13591360
>>> s.nnz
13601361
3

0 commit comments

Comments
 (0)