Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 0 additions & 4 deletions scipy/_lib/tests/test_array_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -66,10 +66,6 @@ def test_array_api_extra_hook(self):
with pytest.raises(TypeError, match=msg):
xpx.atleast_nd("abc", ndim=0)

@skip_xp_backends(
"dask.array",
reason="raw dask.array namespace doesn't ignores copy=True in asarray"
)
def test_copy(self, xp):
for _xp in [xp, None]:
x = xp.asarray([1, 2, 3])
Expand Down
7 changes: 4 additions & 3 deletions scipy/conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -399,9 +399,10 @@ def skip_or_xfail_xp_backends(request: pytest.FixtureRequest,
if 'cpu' not in d.device_kind:
skip_or_xfail(reason=reason)
elif xp.__name__ == 'dask.array' and 'dask.array' not in exceptions:
if xp_device(xp.empty(0)) != 'cpu':
skip_or_xfail(reason=reason)

# dask has no device. 'cpu' is a hack introduced by array-api-compat.
# Force to revisit this when in the future
# dask adds proper device support
assert xp_device(xp.empty(0)) == 'cpu'

# Following the approach of NumPy's conftest.py...
# Use a known and persistent tmpdir for hypothesis' caches, which
Expand Down
2 changes: 2 additions & 0 deletions scipy/fft/tests/test_helper.py
Original file line number Diff line number Diff line change
Expand Up @@ -518,6 +518,7 @@ def test_definition(self, xp):
x2 = xp.asarray([0, 1, 2, 3, 4, -5, -4, -3, -2, -1], dtype=xp.float64)

# default dtype varies across backends

y = 9 * fft.fftfreq(9, xp=xp)
xp_assert_close(y, x, check_dtype=False, check_namespace=True)

Expand Down Expand Up @@ -549,6 +550,7 @@ def test_definition(self, xp):
x2 = xp.asarray([0, 1, 2, 3, 4, 5], dtype=xp.float64)

# default dtype varies across backends

y = 9 * fft.rfftfreq(9, xp=xp)
xp_assert_close(y, x, check_dtype=False, check_namespace=True)

Expand Down
12 changes: 5 additions & 7 deletions scipy/fft/tests/test_real_transforms.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
from scipy.fft import dct, idct, dctn, idctn, dst, idst, dstn, idstn
import scipy.fft as fft
from scipy import fftpack
from scipy._lib._array_api import xp_assert_close
from scipy._lib._array_api import xp_copy, xp_assert_close

skip_xp_backends = pytest.mark.skip_xp_backends

Expand Down Expand Up @@ -195,10 +195,9 @@ def test_orthogonalize_noop(func, type, norm, xp):
@skip_xp_backends(cpu_only=True)
@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"])
def test_orthogonalize_dct1(norm, xp):
x_np = np.random.rand(100)
x = xp.asarray(x_np)
x = xp.asarray(np.random.rand(100))

x2 = xp.asarray(x_np.copy())
x2 = xp_copy(x, xp=xp)
x2[0] *= SQRT_2
x2[-1] *= SQRT_2

Expand Down Expand Up @@ -230,9 +229,8 @@ def test_orthogonalize_dcst2(func, norm, xp):
@pytest.mark.parametrize("norm", ["backward", "ortho", "forward"])
@pytest.mark.parametrize("func", [dct, dst])
def test_orthogonalize_dcst3(func, norm, xp):
x_np = np.random.rand(100)
x = xp.asarray(x_np)
x2 = xp.asarray(x_np.copy())
x = xp.asarray(np.random.rand(100))
x2 = xp_copy(x, xp=xp)
x2[0 if func == dct else -1] *= SQRT_2

y1 = func(x, type=3, norm=norm, orthogonalize=True)
Expand Down
6 changes: 3 additions & 3 deletions scipy/ndimage/_morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@
from . import _nd_image
from . import _filters

from scipy._lib._array_api import is_dask, array_namespace
from scipy._lib.array_api_compat import is_dask_array

__all__ = ['iterate_structure', 'generate_binary_structure', 'binary_erosion',
'binary_dilation', 'binary_opening', 'binary_closing',
Expand Down Expand Up @@ -222,7 +222,7 @@ def _binary_erosion(input, structure, iterations, mask, output,
except TypeError as e:
raise TypeError('iterations parameter should be an integer') from e

if is_dask(array_namespace(input)):
if is_dask_array(input):
# Note: If you create an dask array with ones
# it does a stride trick where it makes an array
# (with stride 0) using a scalar
Expand Down Expand Up @@ -1800,6 +1800,7 @@ def morphological_laplace(input, size=None, footprint=None, structure=None,
Output

"""
input = np.asarray(input)
tmp1 = grey_dilation(input, size, footprint, structure, None, mode,
cval, origin, axes=axes)
if isinstance(output, np.ndarray):
Expand All @@ -1812,7 +1813,6 @@ def morphological_laplace(input, size=None, footprint=None, structure=None,
tmp2 = grey_erosion(input, size, footprint, structure, None, mode,
cval, origin, axes=axes)
np.add(tmp1, tmp2, tmp2)
input = np.asarray(input)
np.subtract(tmp2, input, tmp2)
np.subtract(tmp2, input, tmp2)
return tmp2
Expand Down
30 changes: 14 additions & 16 deletions scipy/ndimage/tests/test_filters.py
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ def test_correlate01(self, xp):

@xfail_xp_backends('cupy', reason="Differs by a factor of two?")
@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="wrong answer")
@xfail_xp_backends("dask.array", reason="wrong answer")
def test_correlate01_overlap(self, xp):
array = xp.reshape(xp.arange(256), (16, 16))
weights = xp.asarray([2])
Expand Down Expand Up @@ -534,7 +534,7 @@ def test_correlate22(self, dtype_array, dtype_output, xp):
assert_array_almost_equal(output, expected)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype_array', types)
@pytest.mark.parametrize('dtype_output', types)
def test_correlate23(self, dtype_array, dtype_output, xp):
Expand All @@ -554,7 +554,7 @@ def test_correlate23(self, dtype_array, dtype_output, xp):
assert_array_almost_equal(output, expected)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype_array', types)
@pytest.mark.parametrize('dtype_output', types)
def test_correlate24(self, dtype_array, dtype_output, xp):
Expand All @@ -575,7 +575,7 @@ def test_correlate24(self, dtype_array, dtype_output, xp):
assert_array_almost_equal(output, tcov)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype_array', types)
@pytest.mark.parametrize('dtype_output', types)
def test_correlate25(self, dtype_array, dtype_output, xp):
Expand Down Expand Up @@ -881,7 +881,7 @@ def test_gauss06(self, xp):
assert_array_almost_equal(output1, output2)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="wrong result")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
def test_gauss_memory_overlap(self, xp):
input = xp.arange(100 * 100, dtype=xp.float32)
input = xp.reshape(input, (100, 100))
Expand Down Expand Up @@ -1228,7 +1228,7 @@ def test_prewitt01(self, dtype, xp):
assert_array_almost_equal(t, output)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype', types + complex_types)
def test_prewitt02(self, dtype, xp):
if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
Expand Down Expand Up @@ -1291,7 +1291,7 @@ def test_sobel01(self, dtype, xp):
assert_array_almost_equal(t, output)

@skip_xp_backends("jax.numpy", reason="output array is read-only.",)
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype', types + complex_types)
def test_sobel02(self, dtype, xp):
if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
Expand Down Expand Up @@ -1352,7 +1352,7 @@ def test_laplace01(self, dtype, xp):
assert_array_almost_equal(tmp1 + tmp2, output)

@skip_xp_backends("jax.numpy", reason="output array is read-only",)
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype',
["int32", "float32", "float64",
"complex64", "complex128"])
Expand Down Expand Up @@ -1383,7 +1383,7 @@ def test_gaussian_laplace01(self, dtype, xp):
assert_array_almost_equal(tmp1 + tmp2, output)

@skip_xp_backends("jax.numpy", reason="output array is read-only")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype',
["int32", "float32", "float64",
"complex64", "complex128"])
Expand All @@ -1400,7 +1400,7 @@ def test_gaussian_laplace02(self, dtype, xp):
assert_array_almost_equal(tmp1 + tmp2, output)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype', types + complex_types)
def test_generic_laplace01(self, dtype, xp):
if is_torch(xp) and dtype in ("uint16", "uint32", "uint64"):
Expand All @@ -1426,7 +1426,7 @@ def derivative2(input, axis, output, mode, cval, a, b):
assert_array_almost_equal(tmp, output)

@skip_xp_backends("jax.numpy", reason="output array is read-only")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype',
["int32", "float32", "float64",
"complex64", "complex128"])
Expand All @@ -1447,7 +1447,7 @@ def test_gaussian_gradient_magnitude01(self, dtype, xp):
xp_assert_close(output, expected, rtol=1e-6, atol=1e-6)

@skip_xp_backends("jax.numpy", reason="output array is read-only")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
@pytest.mark.parametrize('dtype',
["int32", "float32", "float64",
"complex64", "complex128"])
Expand Down Expand Up @@ -1838,9 +1838,7 @@ def test_rank06(self, xp):
@skip_xp_backends("jax.numpy",
reason="assignment destination is read-only",
)
@xfail_xp_backends("dask.array",
reason="wrong answer",
)
@xfail_xp_backends("dask.array", reason="wrong answer")
def test_rank06_overlap(self, xp):
array = xp.asarray([[3, 2, 5, 1, 4],
[5, 8, 3, 7, 1],
Expand Down Expand Up @@ -2647,7 +2645,7 @@ def test_gaussian_radius_invalid(xp):


@skip_xp_backends("jax.numpy", reason="output array is read-only")
@skip_xp_backends("dask.array", reason="wrong answer")
@xfail_xp_backends("dask.array", reason="wrong answer")
class TestThreading:
def check_func_thread(self, n, fun, args, out):
from threading import Thread
Expand Down
13 changes: 8 additions & 5 deletions scipy/ndimage/tests/test_measurements.py
Original file line number Diff line number Diff line change
Expand Up @@ -548,17 +548,16 @@ def test_value_indices02(xp):
ndimage.value_indices(data)


@skip_xp_backends("dask.array", reason="len on data-dependent output shapes")
def test_value_indices03(xp):
"Test different input array shapes, from 1-D to 4-D"
for shape in [(36,), (18, 2), (3, 3, 4), (3, 3, 2, 2)]:
a = xp.asarray((12*[1]+12*[2]+12*[3]), dtype=xp.int32)
a = xp.reshape(a, shape)

trueKeys = xp.unique_values(a)
# convert to numpy to prevent issues with data-dependent shapes
# from unique for dask
trueKeys = np.asarray(xp.unique_values(a))
vi = ndimage.value_indices(a)
# TODO: list(trueKeys) needs len of trueKeys
# (which is unknown for dask since it is the result of an unique call)
assert list(vi.keys()) == list(trueKeys)
for k in [int(x) for x in trueKeys]:
trueNdx = xp.nonzero(a == k)
Expand Down Expand Up @@ -688,6 +687,7 @@ def test_sum_labels(xp):
assert xp.all(output_sum == output_labels)
assert_array_almost_equal(output_labels, xp.asarray([4.0, 0.0, 5.0]))


def test_mean01(xp):
labels = np.asarray([1, 0], dtype=bool)
labels = xp.asarray(labels)
Expand Down Expand Up @@ -819,7 +819,6 @@ def test_maximum05(xp):
assert ndimage.maximum(x) == -1


@pytest.mark.filterwarnings("ignore::FutureWarning:dask")
def test_median01(xp):
a = xp.asarray([[1, 2, 0, 1],
[5, 3, 0, 4],
Expand Down Expand Up @@ -862,6 +861,7 @@ def test_median_gh12836_bool(xp):
output = ndimage.median(a, labels=xp.ones((2,)), index=xp.asarray([1]))
assert_array_almost_equal(output, xp.asarray([1.0]))


def test_median_no_int_overflow(xp):
# test integer overflow fix on example from gh-12836
a = xp.asarray([65, 70], dtype=xp.int8)
Expand Down Expand Up @@ -902,6 +902,7 @@ def test_variance04(xp):
output = ndimage.variance(input)
assert_almost_equal(output, xp.asarray(0.25), check_0d=False)


def test_variance05(xp):
labels = xp.asarray([2, 2, 3])
for type in types:
Expand All @@ -911,6 +912,7 @@ def test_variance05(xp):
output = ndimage.variance(input, labels, 2)
assert_almost_equal(output, xp.asarray(1.0), check_0d=False)


def test_variance06(xp):
labels = xp.asarray([2, 2, 3, 3, 4])
with np.errstate(all='ignore'):
Expand Down Expand Up @@ -1126,6 +1128,7 @@ def test_maximum_position06(xp):
assert output[0] == (0, 0)
assert output[1] == (1, 1)


@xfail_xp_backends("torch", reason="output[1] is wrong on pytorch")
def test_maximum_position07(xp):
# Test float labels
Expand Down
10 changes: 5 additions & 5 deletions scipy/ndimage/tests/test_morphology.py
Original file line number Diff line number Diff line change
Expand Up @@ -2315,7 +2315,6 @@ def test_grey_erosion01(self, xp):
[5, 5, 3, 3, 1]]))

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@xfail_xp_backends("cupy", reason="https://github.com/cupy/cupy/issues/8398")
def test_grey_erosion01_overlap(self, xp):

Expand Down Expand Up @@ -2511,7 +2510,7 @@ def test_morphological_laplace02(self, xp):
assert_array_almost_equal(output, expected)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
def test_white_tophat01(self, xp):
array = xp.asarray([[3, 2, 5, 1, 4],
[7, 6, 9, 3, 5],
Expand Down Expand Up @@ -2565,7 +2564,7 @@ def test_white_tophat03(self, xp):
xp_assert_equal(output, expected)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
def test_white_tophat04(self, xp):
array = np.eye(5, dtype=bool)
structure = np.ones((3, 3), dtype=bool)
Expand All @@ -2578,7 +2577,7 @@ def test_white_tophat04(self, xp):
ndimage.white_tophat(array, structure=structure, output=output)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
def test_black_tophat01(self, xp):
array = xp.asarray([[3, 2, 5, 1, 4],
[7, 6, 9, 3, 5],
Expand All @@ -2588,6 +2587,7 @@ def test_black_tophat01(self, xp):
tmp = ndimage.grey_closing(array, footprint=footprint,
structure=structure)
expected = tmp - array
# This output array is read-only for dask and jax
output = xp.zeros(array.shape, dtype=array.dtype)
ndimage.black_tophat(array, footprint=footprint,
structure=structure, output=output)
Expand Down Expand Up @@ -2632,7 +2632,7 @@ def test_black_tophat03(self, xp):
xp_assert_equal(output, expected)

@skip_xp_backends("jax.numpy", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="output array is read-only.")
@skip_xp_backends("dask.array", reason="converts dask output array to numpy")
def test_black_tophat04(self, xp):
array = xp.asarray(np.eye(5, dtype=bool))
structure = xp.asarray(np.ones((3, 3), dtype=bool))
Expand Down
3 changes: 3 additions & 0 deletions scipy/signal/_filter_design.py
Original file line number Diff line number Diff line change
Expand Up @@ -1779,6 +1779,9 @@ def normalize(b, a):
"""
num, den = b, a

# cast to numpy by hand to avoid libraries like dask
# trying to dispatch this function via NEP 18
den = np.asarray(den)
den = np.atleast_1d(den)
num = np.atleast_2d(_align_nums(num))

Expand Down
3 changes: 3 additions & 0 deletions scipy/signal/_signaltools.py
Original file line number Diff line number Diff line change
Expand Up @@ -4087,6 +4087,9 @@ def detrend(data: np.ndarray, axis: int = -1,
else:
dshape = data.shape
N = dshape[axis]
# Manually cast to numpy to prevent
# NEP18 dispatching for libraries like dask
bp = np.asarray(bp)
bp = np.sort(np.unique(np.concatenate(np.atleast_1d(0, bp, N))))
if np.any(bp > N):
raise ValueError("Breakpoints must be less than length "
Expand Down
Loading