Skip to content

Address more comments #22

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Jan 26, 2025
Merged
Show file tree
Hide file tree
Changes from all 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
9 changes: 4 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 Down Expand Up @@ -2632,7 +2631,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