summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/release/upcoming_changes/21995.compatibility.rst46
-rw-r--r--numpy/array_api/tests/test_asarray.py43
2 files changed, 77 insertions, 12 deletions
diff --git a/doc/release/upcoming_changes/21995.compatibility.rst b/doc/release/upcoming_changes/21995.compatibility.rst
new file mode 100644
index 000000000..cc00b50b3
--- /dev/null
+++ b/doc/release/upcoming_changes/21995.compatibility.rst
@@ -0,0 +1,46 @@
+Returned arrays respect uniqueness of dtype kwarg objects
+---------------------------------------------------------
+When ``dtype`` keyword argument is used with :py:func:`np.array()`
+or :py:func:`asarray()`, the dtype of the returned array has
+the same dtype *instance* as provided by the caller.
+
+If the provided dtype is compatible, but not identically the same
+:py:class:`dtype` object, a new array handle is always created with
+a reference to the user-provided dtype instance.
+If the data type is compatible, and copying is not required, the new
+`ndarray` uses the original array as its
+`base <https://numpy.org/doc/stable/reference/generated/numpy.ndarray.base.html>`__.
+
+Before this change, for two equivalent but non-identical dtypes,
+
+ assert isinstance(typeA, np.dtype) and isinstance(typeB, np.dtype)
+ assert typeA == typeB
+ assert typeA is not typeB
+ if my_array.dtype is typeA:
+ assert my_array is np.asarray(my_array, dtype=typeB)
+ assert np.asarray(my_array, dtype=typeB).dtype is not typeB
+
+This change allows programs to be able to reliably get the exact dtype
+representation they request, regardless of possibly aliased types on the
+calling platform.
+
+However, identity semantics for array results and their
+dtype members may require minor updates to calling code.
+
+After this change, on a system where C ``int`` and C ``long`` are the same
+precision, ``np.dtype('i') == np.dtype('l')``,
+but ``np.dtype('i') is not np.dtype('l')``.
+
+ assert int_array.dtype is np.dtype('i')
+ long_int_array = np.asarray(int_array, dtype='l')
+ assert long_int_array is not int_array
+ if np.dtype('i') == np.dtype('l'):
+ assert int_array is long_int_array.base
+
+New array views are created with each call to `asarray` with non-identical
+dtype kwarg, but the underlying data is the same.
+
+ assert int_array.dtype is np.dtype('i')
+ long_int_array = np.asarray(int_array, dtype='l')
+ assert long_int_array is not np.asarray(int_array, dtype='l')
+ assert long_int_array.base is np.asarray(int_array, dtype='l').base
diff --git a/numpy/array_api/tests/test_asarray.py b/numpy/array_api/tests/test_asarray.py
index 4a9dd77a0..5c269823f 100644
--- a/numpy/array_api/tests/test_asarray.py
+++ b/numpy/array_api/tests/test_asarray.py
@@ -1,3 +1,5 @@
+import itertools
+
import numpy as np
@@ -24,22 +26,39 @@ def test_dtype_identity():
annotated_int_array = np.asarray(int_array, dtype=unequal_type)
assert annotated_int_array is not int_array
assert annotated_int_array.base is int_array
-
- # These ``asarray()`` calls may produce a new view or a copy,
- # but never the same object.
- long_int_array = np.asarray(int_array, dtype='l')
- assert long_int_array is not int_array
- assert np.asarray(int_array, dtype='q') is not int_array
- assert np.asarray(long_int_array, dtype='q') is not long_int_array
- assert long_int_array is not np.asarray(int_array, dtype='l')
- assert long_int_array.base is np.asarray(int_array, dtype='l').base
-
+ # Create an equivalent descriptor with a new and distinct dtype instance.
equivalent_requirement = np.dtype('i', metadata={'spam': True})
annotated_int_array_alt = np.asarray(annotated_int_array,
dtype=equivalent_requirement)
- # The descriptors are equivalent, but we have created
- # distinct dtype instances.
assert unequal_type == equivalent_requirement
assert unequal_type is not equivalent_requirement
assert annotated_int_array_alt is not annotated_int_array
assert annotated_int_array_alt.dtype is equivalent_requirement
+
+ # Check the same logic for a pair of C types whose equivalence may vary
+ # between computing environments.
+ # Find an equivalent pair.
+ integer_type_codes = ('i', 'l', 'q')
+ integer_dtypes = [np.dtype(code) for code in integer_type_codes]
+ typeA = None
+ typeB = None
+ for typeA, typeB in itertools.permutations(integer_dtypes, r=2):
+ if typeA == typeB:
+ assert typeA is not typeB
+ break
+ assert isinstance(typeA, np.dtype) and isinstance(typeB, np.dtype)
+
+ # These ``asarray()`` calls may produce a new view or a copy,
+ # but never the same object.
+ long_int_array = np.asarray(int_array, dtype='l')
+ long_long_int_array = np.asarray(int_array, dtype='q')
+ assert long_int_array is not int_array
+ assert long_long_int_array is not int_array
+ assert np.asarray(long_int_array, dtype='q') is not long_int_array
+ array_a = np.asarray(int_array, dtype=typeA)
+ assert typeA == typeB
+ assert typeA is not typeB
+ assert array_a.dtype is typeA
+ assert array_a is not np.asarray(array_a, dtype=typeB)
+ assert np.asarray(array_a, dtype=typeB).dtype is typeB
+ assert array_a is np.asarray(array_a, dtype=typeB).base