diff --git a/CHANGELOG.md b/CHANGELOG.md index 2556938f779..93ff8ceb29a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -37,6 +37,7 @@ This release is compatible with NumPy 2.5. * Aligned the signature of `dpnp.tensor.expand_dims` with the Python array API by making `axis` a required argument [#2988](https://github.com/IntelPython/dpnp/pull/2988) * Removed dead code branches guarded by outdated oneMKL and DPC++ compiler version checks [#2999](https://github.com/IntelPython/dpnp/pull/2999) * Replaced the deprecated `nd_item::barrier()` member calls in the `accumulators` and `gemm` kernels with the SYCL 2020 `sycl::group_barrier()` free function [#3006](https://github.com/IntelPython/dpnp/pull/3006) +* Changed `dpnp.broadcast_arrays` and `dpnp.tensor.broadcast_arrays` to return a tuple instead of a list, aligning with the 2025.12 Python array API spec [#2944](https://github.com/IntelPython/dpnp/pull/2944) ### Deprecated diff --git a/dpnp/dpnp_iface_manipulation.py b/dpnp/dpnp_iface_manipulation.py index b96e5593c2a..b58c65ca818 100644 --- a/dpnp/dpnp_iface_manipulation.py +++ b/dpnp/dpnp_iface_manipulation.py @@ -1060,8 +1060,8 @@ def broadcast_arrays(*args, subok=False): Returns ------- - out : list of dpnp.ndarray - A list of arrays which are views on the original arrays from `args`. + out : tuple of dpnp.ndarray + A tuple of arrays which are views on the original arrays from `args`. Limitations ----------- @@ -1078,9 +1078,9 @@ def broadcast_arrays(*args, subok=False): >>> x = np.array([[1, 2, 3]]) >>> y = np.array([[4], [5]]) >>> np.broadcast_arrays(x, y) - [array([[1, 2, 3], + (array([[1, 2, 3], [1, 2, 3]]), array([[4, 4, 4], - [5, 5, 5]])] + [5, 5, 5]])) """ @@ -1088,10 +1088,10 @@ def broadcast_arrays(*args, subok=False): raise NotImplementedError(f"subok={subok} is currently not supported") if len(args) == 0: - return [] + return () usm_arrays = dpt.broadcast_arrays(*[dpnp.get_usm_ndarray(a) for a in args]) - return [dpnp_array._create_from_usm_ndarray(a) for a in usm_arrays] + return tuple(dpnp_array._create_from_usm_ndarray(a) for a in usm_arrays) def broadcast_shapes(*args): diff --git a/dpnp/tensor/_manipulation_functions.py b/dpnp/tensor/_manipulation_functions.py index d12422fdefa..9d5246ed6bf 100644 --- a/dpnp/tensor/_manipulation_functions.py +++ b/dpnp/tensor/_manipulation_functions.py @@ -228,8 +228,8 @@ def broadcast_arrays(*args): broadcasted. Returns: - List[usm_ndarray]: - A list of broadcasted arrays. Each array + tuple[usm_ndarray, ...]: + A tuple of broadcasted arrays. Each array must have the same shape. Each array must have the same `dtype`, `device` and `usm_type` attributes as its corresponding input array. @@ -245,7 +245,7 @@ def broadcast_arrays(*args): if all(X.shape == shape for X in args): return args - return [broadcast_to(X, shape) for X in args] + return tuple(broadcast_to(X, shape) for X in args) def broadcast_to(X, /, shape): diff --git a/dpnp/tests/tensor/test_usm_ndarray_manipulation.py b/dpnp/tests/tensor/test_usm_ndarray_manipulation.py index bb0a99a537f..3f45506c408 100644 --- a/dpnp/tests/tensor/test_usm_ndarray_manipulation.py +++ b/dpnp/tests/tensor/test_usm_ndarray_manipulation.py @@ -464,6 +464,14 @@ def test_incompatible_shapes_raise_valueerror(shapes): assert_broadcast_arrays_raise(input_shapes[::-1]) +def test_broadcast_arrays_tuple(): + q = get_queue_or_skip() + out = dpt.broadcast_arrays( + dpt.ones((1, 3), sycl_queue=q), dpt.zeros((3, 1), sycl_queue=q) + ) + assert isinstance(out, tuple) + + def test_broadcast_arrays_no_args(): with pytest.raises(ValueError): dpt.broadcast_arrays() diff --git a/dpnp/tests/test_arraymanipulation.py b/dpnp/tests/test_arraymanipulation.py index d1587b9a50c..f54740031d6 100644 --- a/dpnp/tests/test_arraymanipulation.py +++ b/dpnp/tests/test_arraymanipulation.py @@ -294,8 +294,12 @@ def test_incompatible_shapes_raise_valueerror(self, shapes): self.assert_broadcast_arrays_raise(input_shapes) self.assert_broadcast_arrays_raise(input_shapes[::-1]) + def test_broadcast_arrays_tuple(self): + out = dpnp.broadcast_arrays(dpnp.ones((1, 3)), dpnp.zeros((3, 1))) + assert isinstance(out, tuple) + def test_broadcast_arrays_empty_input(self): - assert dpnp.broadcast_arrays() == [] + assert dpnp.broadcast_arrays() == () def test_subok_error(self): x = dpnp.ones(4) diff --git a/dpnp/tests/third_party/cupy/manipulation_tests/test_dims.py b/dpnp/tests/third_party/cupy/manipulation_tests/test_dims.py index ae0f6ce18b4..8790d4cbcc6 100644 --- a/dpnp/tests/third_party/cupy/manipulation_tests/test_dims.py +++ b/dpnp/tests/third_party/cupy/manipulation_tests/test_dims.py @@ -315,6 +315,10 @@ def test_broadcast_arrays(self, xp, dtype): arrays = [testing.shaped_arange(s, xp, dtype) for s in self.shapes] return xp.broadcast_arrays(*arrays) + def test_broadcast_arrays_tuple(self): + out = cupy.broadcast_arrays(cupy.ones(3), cupy.zeros(3)) + assert isinstance(out, tuple) + @testing.parameterize( {"shapes": [(3,), (2,)]},