diff options
| -rw-r--r-- | doc/release/upcoming_changes/21995.compatibility.rst | 46 | ||||
| -rw-r--r-- | numpy/array_api/tests/test_asarray.py | 43 |
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 |
