Skip to content

Commit 4901cde

Browse files
authored
Fix dpnp.unique with axis=0 and 1d input (#2530)
The PR fixes `dpnp.unique` with 1d input array and `axis=0`, `equal_nan=True` keywords passed where the produced result is wrong and doesn't collapse the NaNs.
1 parent fed6925 commit 4901cde

File tree

4 files changed

+39
-26
lines changed

4 files changed

+39
-26
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
4242
* Updated `pre-commit` GitHub workflow to pass `no-commit-to-branch` check [#2501](https://github.com/IntelPython/dpnp/pull/2501)
4343
* Updated the math formulas in summary of `dpnp.matvec` and `dpnp.vecmat` to correct a typo [#2503](https://github.com/IntelPython/dpnp/pull/2503)
4444
* Avoided negating unsigned integers in ceil division used in `dpnp.resize` implementation [#2508](https://github.com/IntelPython/dpnp/pull/2508)
45+
* Fixed `dpnp.unique` with 1d input array and `axis=0`, `equal_nan=True` keywords passed where the produced result doesn't collapse the NaNs [#2530](https://github.com/IntelPython/dpnp/pull/2530)
4546

4647
### Security
4748

dpnp/dpnp_iface_manipulation.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4245,7 +4245,7 @@ def unique(
42454245
42464246
"""
42474247

4248-
if axis is None:
4248+
if axis is None or (axis == 0 and ar.ndim == 1):
42494249
return _unique_1d(
42504250
ar, return_index, return_inverse, return_counts, equal_nan
42514251
)

dpnp/tests/helper.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -343,7 +343,7 @@ def get_integer_dtypes(all_int_types=False, no_unsigned=False):
343343
if config.all_int_types or all_int_types:
344344
dtypes += [dpnp.int8, dpnp.int16]
345345
if not no_unsigned:
346-
dtypes += [dpnp.uint8, dpnp.uint16, dpnp.uint32, dpnp.uint64]
346+
dtypes += get_unsigned_dtypes()
347347

348348
return dtypes
349349

@@ -378,6 +378,14 @@ def not_excluded(dtype):
378378
return dtypes
379379

380380

381+
def get_unsigned_dtypes():
382+
"""
383+
Build a list of unsigned integer types supported by DPNP.
384+
"""
385+
386+
return [dpnp.uint8, dpnp.uint16, dpnp.uint32, dpnp.uint64]
387+
388+
381389
def has_support_aspect16(device=None):
382390
"""
383391
Return True if the device supports 16-bit precision floating point operations,

dpnp/tests/test_manipulation.py

Lines changed: 28 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
get_float_dtypes,
2222
get_integer_dtypes,
2323
get_integer_float_dtypes,
24+
get_unsigned_dtypes,
2425
has_support_aspect64,
2526
numpy_version,
2627
)
@@ -1685,6 +1686,7 @@ def test_axis_list(self, axis):
16851686
expected = numpy.unique(a, axis=axis)
16861687
assert_array_equal(result, expected)
16871688

1689+
@testing.with_requires("numpy>=2.0.1")
16881690
@pytest.mark.parametrize("dt", get_all_dtypes(no_none=True))
16891691
@pytest.mark.parametrize(
16901692
"axis_kwd",
@@ -1716,17 +1718,6 @@ def test_2d_axis(self, dt, axis_kwd, return_kwds):
17161718
if len(return_kwds) == 0:
17171719
assert_array_equal(result, expected)
17181720
else:
1719-
if (
1720-
len(axis_kwd) == 0
1721-
and numpy.lib.NumpyVersion(numpy.__version__) < "2.0.1"
1722-
):
1723-
# gh-26961: numpy.unique(..., return_inverse=True, axis=None)
1724-
# returned flatten unique_inverse till 2.0.1 version
1725-
expected = (
1726-
expected[:2]
1727-
+ (expected[2].reshape(a.shape),)
1728-
+ expected[3:]
1729-
)
17301721
for iv, v in zip(result, expected):
17311722
assert_array_equal(iv, v)
17321723

@@ -1756,17 +1747,14 @@ def test_1d_axis(self, axis):
17561747
expected = numpy.unique(a, axis=axis)
17571748
assert_array_equal(result, expected)
17581749

1750+
@testing.with_requires("numpy>=2.0.1")
17591751
@pytest.mark.parametrize("axis", [None, 0, -1])
17601752
def test_2d_axis_inverse(self, axis):
17611753
a = numpy.array([[4, 4, 3], [2, 2, 1], [2, 2, 1], [4, 4, 3]])
17621754
ia = dpnp.array(a)
17631755

17641756
result = dpnp.unique(ia, return_inverse=True, axis=axis)
17651757
expected = numpy.unique(a, return_inverse=True, axis=axis)
1766-
if axis is None and numpy.lib.NumpyVersion(numpy.__version__) < "2.0.1":
1767-
# gh-26961: numpy.unique(..., return_inverse=True, axis=None)
1768-
# returned flatten unique_inverse till 2.0.1 version
1769-
expected = expected[:1] + (expected[1].reshape(a.shape),)
17701758

17711759
for iv, v in zip(result, expected):
17721760
assert_array_equal(iv, v)
@@ -1812,8 +1800,18 @@ def test_2d_axis_signed_inetger(self, dt):
18121800
expected = numpy.unique(a, axis=0)
18131801
assert_array_equal(result, expected)
18141802

1803+
@pytest.mark.parametrize("axis", [None, 0, 1])
1804+
@pytest.mark.parametrize("dt", get_unsigned_dtypes())
1805+
def test_2d_axis_unsigned_inetger(self, axis, dt):
1806+
a = numpy.array([[7, 1, 2, 1], [5, 7, 5, 7]], dtype=dt)
1807+
ia = dpnp.array(a)
1808+
1809+
result = dpnp.unique(ia, axis=axis)
1810+
expected = numpy.unique(a, axis=axis)
1811+
assert_array_equal(result, expected)
1812+
18151813
@pytest.mark.parametrize("axis", [None, 0])
1816-
@pytest.mark.parametrize("dt", "bBhHiIlLqQ")
1814+
@pytest.mark.parametrize("dt", get_integer_dtypes(all_int_types=True))
18171815
def test_1d_axis_all_inetger(self, axis, dt):
18181816
a = numpy.array([5, 7, 1, 2, 1, 5, 7], dtype=dt)
18191817
ia = dpnp.array(a)
@@ -1838,6 +1836,20 @@ def test_equal_nan(self, eq_nan_kwd):
18381836
expected = numpy.unique(a, **eq_nan_kwd)
18391837
assert_array_equal(result, expected)
18401838

1839+
# TODO: uncomment once numpy 2.3.2 release is published
1840+
# @testing.with_requires("numpy>=2.3.2")
1841+
def test_1d_equal_nan_axis0(self):
1842+
a = numpy.array([numpy.nan, 0, 0, numpy.nan])
1843+
ia = dpnp.array(a)
1844+
1845+
result = dpnp.unique(ia, axis=0, equal_nan=True)
1846+
expected = numpy.unique(a, axis=0, equal_nan=True)
1847+
# TODO: remove when numpy#29372 is released
1848+
if numpy_version() < "2.3.2":
1849+
expected = numpy.array([0.0, numpy.nan])
1850+
assert_array_equal(result, expected)
1851+
1852+
@testing.with_requires("numpy>=2.0.1")
18411853
@pytest.mark.parametrize("dt", get_float_complex_dtypes())
18421854
@pytest.mark.parametrize(
18431855
"axis_kwd",
@@ -1879,14 +1891,6 @@ def test_2d_axis_nans(self, dt, axis_kwd, return_kwds, row):
18791891
if len(return_kwds) == 0:
18801892
assert_array_equal(result, expected)
18811893
else:
1882-
if len(axis_kwd) == 0 and numpy_version() < "2.0.1":
1883-
# gh-26961: numpy.unique(..., return_inverse=True, axis=None)
1884-
# returned flatten unique_inverse till 2.0.1 version
1885-
expected = (
1886-
expected[:2]
1887-
+ (expected[2].reshape(a.shape),)
1888-
+ expected[3:]
1889-
)
18901894
for iv, v in zip(result, expected):
18911895
assert_array_equal(iv, v)
18921896

0 commit comments

Comments
 (0)