Skip to content

BUG: do not change the dtype in asarray(array_from_another_library)#223

Open
ev-br wants to merge 1 commit into
data-apis:mainfrom
ev-br:asarray_device_dtyped_obj
Open

BUG: do not change the dtype in asarray(array_from_another_library)#223
ev-br wants to merge 1 commit into
data-apis:mainfrom
ev-br:asarray_device_dtyped_obj

Conversation

@ev-br

@ev-br ev-br commented Jul 10, 2026

Copy link
Copy Markdown
Member

closes gh-222

Consider

>>> x = np.asarray(3, dtype=np.float32)
>>> y =  xp.asarray(x, device=xp.Device("device1"))
>>> y.dtype == xp.float32

Since x is an array, the dtype of y must match the dtype of x, not be the default dtype of the device of y!

In fact, this is the behavior in 2.5, so restore it. The fix follows from the difference between 2.5 and 2.6:

  • in 2.5, we called res = np.array(...) and wrapped res into an xp-array
  • in 2.6, we additionally select adjust the dtype to match the default for the device.

The last part should be gated:

  • if obj is an array_like python object, it does not have a .dtype attribute, thus we adjust res.dtype (this is new in 2.6)
  • if obj is a array from numpy or pytorch CPU (i.e. numpy conversion succeeds), res.dtype is a numpy analog of obj.dtype, so we honor that (this is what 2.6 broke, and we fix here)
  • if obj has a dtype attribute but numpy fails to convert it (so obj is a GPU array or a pydata sparse array etc), calling np.array(obj) fails with an exception, and we never reach the dtype adjustment stage anyway (and this is unchanged from 2.5).
$ git diff 2.5..2.6 -- array_api_strict/_creation_functions.py 

... snip ...

@@ -108,6 +106,27 @@ def asarray(
         raise OverflowError("Integer out of bounds for array dtypes")

     res = np.array(obj, dtype=_np_dtype, copy=copy)
+
+    # numpy default dtype may differ; if so, adjust the dtype
+    if dtype is None and device is not None:
+        res_dtype = DType(res.dtype)
+        # The dtype selected by Numpy might not be the default dtype
+        # on this device. We thus find the default dtype for the dtype "kind", and
+        # cast to the device-appropriate default.
+        from ._data_type_functions import isdtype
+        if isdtype(res_dtype, "bool"):
+            target_dtype = DType("bool")
+        elif isdtype(res_dtype, "integral"):
+            target_dtype = get_default_dtypes(device)["integral"]
+        elif isdtype(res_dtype, "real floating"):
+            target_dtype = get_default_dtypes(device)["real floating"]
+        elif isdtype(res_dtype, "complex floating"):
+            target_dtype = get_default_dtypes(device)["complex floating"]
+        else:
+            raise ValueError(f"{res_dtype = } not understood.")
+
+        res = res.astype(target_dtype._np_dtype)
+
     return Array._new(res, device=device)

Consider

>>> x = np.asarray(3, dtype=np.float32)
>>> y =  xp.asarray(x, device=xp.Device("device1"))
>>> y.dtype == xp.float32

Since `x` is an array, the dtype of `y` must match the dtype of `x`,
not be the default dtype of the device of `y`!

In fact, this is the behavior in 2.5, so restore it.
The fix follows from the difference between 2.5 and 2.6:

- in 2.5, we called `res = np.array(...)` and wrapped `res` into an xp-array
- in 2.6, we additionally select adjust the dtype to match the default for the device.

The last part should be gated:

- if `obj` is an array_like python object, it does not have a .dtype attribute,
  thus we adjust res.dtype (this is new in 2.6)
- if `obj` is a array from numpy or pytorch CPU (i.e. numpy conversion succeeds),
  `res.dtype` is a numpy analog of `obj.dtype`, so we honor that (this is
  what 2.6 broke, and we fix here)
- if `obj` has a dtype attribute but numpy fails to convert it (so `obj`
  is a GPU array or a pydata sparse array etc), calling `np.array(obj)` fails with
  an exception, and we never reach the dtype adjustment stage anyway
  (and this is unchanged from 2.5).

$ git diff 2.5..2.6 -- array_api_strict/_creation_functions.py
... snip ...

@@ -108,6 +106,27 @@ def asarray(
         raise OverflowError("Integer out of bounds for array dtypes")

     res = np.array(obj, dtype=_np_dtype, copy=copy)
+
+    # numpy default dtype may differ; if so, adjust the dtype
+    if dtype is None and device is not None:
+        res_dtype = DType(res.dtype)
+        # The dtype selected by Numpy might not be the default dtype
+        # on this device. We thus find the default dtype for the dtype "kind", and
+        # cast to the device-appropriate default.
+        from ._data_type_functions import isdtype
+        if isdtype(res_dtype, "bool"):
+            target_dtype = DType("bool")
+        elif isdtype(res_dtype, "integral"):
+            target_dtype = get_default_dtypes(device)["integral"]
+        elif isdtype(res_dtype, "real floating"):
+            target_dtype = get_default_dtypes(device)["real floating"]
+        elif isdtype(res_dtype, "complex floating"):
+            target_dtype = get_default_dtypes(device)["complex floating"]
+        else:
+            raise ValueError(f"{res_dtype = } not understood.")
+
+        res = res.astype(target_dtype._np_dtype)
+
     return Array._new(res, device=device)
@ev-br ev-br added the bug Something isn't working label Jul 10, 2026
@ev-br ev-br added this to the 2.6.1 milestone Jul 10, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Array's dtype asarray(np_float32_array, device=Device("device1"))

1 participant