array-api-strict 2.5__tar.gz → 2.6__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. {array_api_strict-2.5 → array_api_strict-2.6}/.github/dependabot.yml +2 -0
  2. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/array-api-tests.yml +8 -6
  3. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/dependabot-auto-merge.yml +1 -1
  4. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/docs-build.yml +3 -3
  5. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/docs-deploy.yml +2 -2
  6. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/publish-package.yml +7 -7
  7. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/ruff.yml +2 -2
  8. {array_api_strict-2.5 → array_api_strict-2.6}/.github/workflows/tests.yml +7 -5
  9. {array_api_strict-2.5 → array_api_strict-2.6}/CHANGELOG.md +38 -0
  10. {array_api_strict-2.5/array_api_strict.egg-info → array_api_strict-2.6}/PKG-INFO +1 -1
  11. {array_api_strict-2.5 → array_api_strict-2.6}/array-api-tests-xfails.txt +7 -0
  12. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_array_object.py +46 -40
  13. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_creation_functions.py +94 -34
  14. array_api_strict-2.6/array_api_strict/_devices.py +136 -0
  15. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_dtypes.py +3 -1
  16. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_fft.py +21 -1
  17. array_api_strict-2.6/array_api_strict/_info.py +75 -0
  18. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_statistical_functions.py +8 -2
  19. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_version.py +3 -3
  20. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_array_object.py +44 -1
  21. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_creation_functions.py +159 -2
  22. array_api_strict-2.6/array_api_strict/tests/test_device_support.py +80 -0
  23. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_elementwise_functions.py +6 -1
  24. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_searching_functions.py +1 -1
  25. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_statistical_functions.py +16 -0
  26. {array_api_strict-2.5 → array_api_strict-2.6/array_api_strict.egg-info}/PKG-INFO +1 -1
  27. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict.egg-info/SOURCES.txt +1 -0
  28. {array_api_strict-2.5 → array_api_strict-2.6}/docs/changelog.md +38 -0
  29. {array_api_strict-2.5 → array_api_strict-2.6}/pyproject.toml +1 -1
  30. array_api_strict-2.5/array_api_strict/_info.py +0 -137
  31. array_api_strict-2.5/array_api_strict/tests/test_device_support.py +0 -38
  32. {array_api_strict-2.5 → array_api_strict-2.6}/.gitattributes +0 -0
  33. {array_api_strict-2.5 → array_api_strict-2.6}/.gitignore +0 -0
  34. {array_api_strict-2.5 → array_api_strict-2.6}/LICENSE +0 -0
  35. {array_api_strict-2.5 → array_api_strict-2.6}/README.md +0 -0
  36. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/__init__.py +0 -0
  37. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_constants.py +0 -0
  38. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_data_type_functions.py +0 -0
  39. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_elementwise_functions.py +0 -0
  40. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_flags.py +0 -0
  41. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_helpers.py +0 -0
  42. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_indexing_functions.py +0 -0
  43. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_linalg.py +0 -0
  44. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_linear_algebra_functions.py +0 -0
  45. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_manipulation_functions.py +0 -0
  46. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_searching_functions.py +0 -0
  47. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_set_functions.py +0 -0
  48. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_sorting_functions.py +0 -0
  49. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_typing.py +0 -0
  50. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/_utility_functions.py +0 -0
  51. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/py.typed +0 -0
  52. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/__init__.py +0 -0
  53. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/conftest.py +0 -0
  54. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_data_type_functions.py +0 -0
  55. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_flags.py +0 -0
  56. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_indexing_functions.py +0 -0
  57. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_linalg.py +0 -0
  58. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_manipulation_functions.py +0 -0
  59. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_set_functions.py +0 -0
  60. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_sorting_functions.py +0 -0
  61. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict/tests/test_validation.py +0 -0
  62. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict.egg-info/dependency_links.txt +0 -0
  63. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict.egg-info/requires.txt +0 -0
  64. {array_api_strict-2.5 → array_api_strict-2.6}/array_api_strict.egg-info/top_level.txt +0 -0
  65. {array_api_strict-2.5 → array_api_strict-2.6}/docs/Makefile +0 -0
  66. {array_api_strict-2.5 → array_api_strict-2.6}/docs/_static/custom.css +0 -0
  67. {array_api_strict-2.5 → array_api_strict-2.6}/docs/_static/favicon.png +0 -0
  68. {array_api_strict-2.5 → array_api_strict-2.6}/docs/api.rst +0 -0
  69. {array_api_strict-2.5 → array_api_strict-2.6}/docs/conf.py +0 -0
  70. {array_api_strict-2.5 → array_api_strict-2.6}/docs/index.md +0 -0
  71. {array_api_strict-2.5 → array_api_strict-2.6}/docs/make.bat +0 -0
  72. {array_api_strict-2.5 → array_api_strict-2.6}/docs/releasing.md +0 -0
  73. {array_api_strict-2.5 → array_api_strict-2.6}/docs/requirements.txt +0 -0
  74. {array_api_strict-2.5 → array_api_strict-2.6}/pytest.ini +0 -0
  75. {array_api_strict-2.5 → array_api_strict-2.6}/requirements-dev.txt +0 -0
  76. {array_api_strict-2.5 → array_api_strict-2.6}/requirements.txt +0 -0
  77. {array_api_strict-2.5 → array_api_strict-2.6}/ruff.toml +0 -0
  78. {array_api_strict-2.5 → array_api_strict-2.6}/setup.cfg +0 -0
@@ -5,6 +5,8 @@ updates:
5
5
  directory: "/"
6
6
  schedule:
7
7
  interval: "weekly"
8
+ cooldown:
9
+ default-days: 7 # optional
8
10
  groups:
9
11
  actions:
10
12
  patterns:
@@ -11,11 +11,11 @@ jobs:
11
11
  runs-on: ubuntu-latest
12
12
  strategy:
13
13
  matrix:
14
- python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
15
- numpy-version: ['1.26', '2.3.5', 'dev']
14
+ python-version: ['3.10', '3.13', '3.14']
15
+ numpy-version: ['1.26', 'latest', 'dev']
16
16
  exclude:
17
17
  - python-version: '3.10'
18
- numpy-version: '2.3.5'
18
+ numpy-version: 'latest'
19
19
  - python-version: '3.10'
20
20
  numpy-version: 'dev'
21
21
  - python-version: '3.13'
@@ -25,17 +25,17 @@ jobs:
25
25
  fail-fast: false
26
26
  steps:
27
27
  - name: Checkout array-api-strict
28
- uses: actions/checkout@v6
28
+ uses: actions/checkout@v7.0.0
29
29
  with:
30
30
  path: array-api-strict
31
31
  - name: Checkout array-api-tests
32
- uses: actions/checkout@v6
32
+ uses: actions/checkout@v7.0.0
33
33
  with:
34
34
  repository: data-apis/array-api-tests
35
35
  submodules: 'true'
36
36
  path: array-api-tests
37
37
  - name: Set up Python ${{ matrix.python-version }}
38
- uses: actions/setup-python@v6
38
+ uses: actions/setup-python@v6.3.0
39
39
  with:
40
40
  python-version: ${{ matrix.python-version }}
41
41
  - name: Install Dependencies
@@ -43,6 +43,8 @@ jobs:
43
43
  python -m pip install --upgrade pip
44
44
  if [[ "${{ matrix.numpy-version }}" == "dev" ]]; then
45
45
  python -m pip install --pre --extra-index https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy;
46
+ elif [[ "${{ matrix.numpy-version }}" == "latest" ]]; then
47
+ python -m pip install numpy
46
48
  else
47
49
  python -m pip install 'numpy=='${{ matrix.numpy-version }};
48
50
  fi
@@ -13,7 +13,7 @@ jobs:
13
13
  steps:
14
14
  - name: Dependabot metadata
15
15
  id: metadata
16
- uses: dependabot/fetch-metadata@v2
16
+ uses: dependabot/fetch-metadata@v3
17
17
  with:
18
18
  github-token: "${{ secrets.GITHUB_TOKEN }}"
19
19
  - name: Enable auto-merge for Dependabot PRs
@@ -6,8 +6,8 @@ jobs:
6
6
  docs-build:
7
7
  runs-on: ubuntu-latest
8
8
  steps:
9
- - uses: actions/checkout@v6
10
- - uses: actions/setup-python@v6
9
+ - uses: actions/checkout@v7.0.0
10
+ - uses: actions/setup-python@v6.3.0
11
11
  - name: Install Dependencies
12
12
  run: |
13
13
  python -m pip install -r docs/requirements.txt
@@ -16,7 +16,7 @@ jobs:
16
16
  cd docs
17
17
  make html
18
18
  - name: Upload Artifact
19
- uses: actions/upload-artifact@v6
19
+ uses: actions/upload-artifact@v7
20
20
  with:
21
21
  name: docs-build
22
22
  path: docs/_build/html
@@ -11,9 +11,9 @@ jobs:
11
11
  environment:
12
12
  name: docs-deploy
13
13
  steps:
14
- - uses: actions/checkout@v6
14
+ - uses: actions/checkout@v7.0.0
15
15
  - name: Download Artifact
16
- uses: dawidd6/action-download-artifact@v14
16
+ uses: dawidd6/action-download-artifact@v21
17
17
  with:
18
18
  workflow: docs-build.yml
19
19
  name: docs-build
@@ -30,12 +30,12 @@ jobs:
30
30
  runs-on: ubuntu-latest
31
31
 
32
32
  steps:
33
- - uses: actions/checkout@v6
33
+ - uses: actions/checkout@v7.0.0
34
34
  with:
35
35
  fetch-depth: 0
36
36
 
37
37
  - name: Set up Python
38
- uses: actions/setup-python@v6
38
+ uses: actions/setup-python@v6.3.0
39
39
  with:
40
40
  python-version: '3.x'
41
41
 
@@ -62,7 +62,7 @@ jobs:
62
62
  run: python -m zipfile --list dist/array_api_strict-*.whl
63
63
 
64
64
  - name: Upload distribution artifact
65
- uses: actions/upload-artifact@v6
65
+ uses: actions/upload-artifact@v7
66
66
  with:
67
67
  name: dist-artifact
68
68
  path: dist
@@ -83,7 +83,7 @@ jobs:
83
83
 
84
84
  steps:
85
85
  - name: Download distribution artifact
86
- uses: actions/download-artifact@v7
86
+ uses: actions/download-artifact@v8
87
87
  with:
88
88
  name: dist-artifact
89
89
  path: dist
@@ -97,19 +97,19 @@ jobs:
97
97
  # if: >-
98
98
  # (github.event_name == 'push' && startsWith(github.ref, 'refs/tags'))
99
99
  # || (github.event_name == 'workflow_dispatch' && github.event.inputs.publish == 'true')
100
- # uses: pypa/gh-action-pypi-publish@v1.13.0
100
+ # uses: pypa/gh-action-pypi-publish@v1.14.0
101
101
  # with:
102
102
  # repository-url: https://test.pypi.org/legacy/
103
103
  # print-hash: true
104
104
 
105
105
  - name: Publish distribution 📦 to PyPI
106
106
  if: github.event_name == 'push' && startsWith(github.ref, 'refs/tags')
107
- uses: pypa/gh-action-pypi-publish@v1.13.0
107
+ uses: pypa/gh-action-pypi-publish@v1.14.0
108
108
  with:
109
109
  print-hash: true
110
110
 
111
111
  - name: Create GitHub Release from a Tag
112
- uses: softprops/action-gh-release@v2
112
+ uses: softprops/action-gh-release@v3
113
113
  if: startsWith(github.ref, 'refs/tags/')
114
114
  with:
115
115
  files: dist/*
@@ -5,9 +5,9 @@ jobs:
5
5
  runs-on: ubuntu-latest
6
6
  continue-on-error: true
7
7
  steps:
8
- - uses: actions/checkout@v6
8
+ - uses: actions/checkout@v7.0.0
9
9
  - name: Install Python
10
- uses: actions/setup-python@v6
10
+ uses: actions/setup-python@v6.3.0
11
11
  with:
12
12
  python-version: "3.11"
13
13
  - name: Install dependencies
@@ -5,11 +5,11 @@ jobs:
5
5
  runs-on: ubuntu-latest
6
6
  strategy:
7
7
  matrix:
8
- python-version: ['3.10', '3.11', '3.12', '3.13', '3.14']
9
- numpy-version: ['1.26', '2.3.5', 'dev']
8
+ python-version: ['3.10', '3.13', '3.14']
9
+ numpy-version: ['1.26', 'latest', 'dev']
10
10
  exclude:
11
11
  - python-version: '3.10'
12
- numpy-version: '2.3.5'
12
+ numpy-version: 'latest'
13
13
  - python-version: '3.10'
14
14
  numpy-version: 'dev'
15
15
  - python-version: '3.13'
@@ -18,8 +18,8 @@ jobs:
18
18
  numpy-version: '1.26'
19
19
  fail-fast: false
20
20
  steps:
21
- - uses: actions/checkout@v6
22
- - uses: actions/setup-python@v6
21
+ - uses: actions/checkout@v7.0.0
22
+ - uses: actions/setup-python@v6.3.0
23
23
  with:
24
24
  python-version: ${{ matrix.python-version }}
25
25
  - name: Install Dependencies
@@ -27,6 +27,8 @@ jobs:
27
27
  python -m pip install --upgrade pip
28
28
  if [[ "${{ matrix.numpy-version }}" == "dev" ]]; then
29
29
  python -m pip install --pre --extra-index https://pypi.anaconda.org/scientific-python-nightly-wheels/simple numpy;
30
+ elif [[ "${{ matrix.numpy-version }}" == "latest" ]]; then
31
+ python -m pip install numpy
30
32
  else
31
33
  python -m pip install 'numpy=='${{ matrix.numpy-version }}
32
34
  fi
@@ -1,5 +1,43 @@
1
1
  # Changelog
2
2
 
3
+ ## 2.6 (2026-07-09)
4
+
5
+ - `__setitem__` method of array objects got stricter:
6
+
7
+ - `__setitem__` method now disallows device transfers: if the right-hand side
8
+ argument is an array on a device which differs from the device of the left-hand
9
+ argument, an error is raised. Note that this change follows
10
+ the draft of a future Array API standard revision, see
11
+ [gh-1005](https://github.com/data-apis/array-api/pull/1005) for discussion.
12
+ - `None` is no longer allowed as an indexing key.
13
+
14
+ - Array objects expose a DLPack device number, via their `__dlpack_device__` method.
15
+ - `from_dlpack` function now supports `copy=` argument.
16
+
17
+ - `Device` objects are now allowed to only support a subset of data types, and can
18
+ have a device-dependent default data types. Note that this enhancement follows
19
+ the draft of a future Array API standard revision, see
20
+ [gh-1005](https://github.com/data-apis/array-api/pull/1005) for discussion.
21
+ - A new device object, `Device("no_float64")` was added which only supports
22
+ single-precision float and complex data types; this device mimics the behavior
23
+ of a "metal"/MPS device which does not support double precision floats.
24
+ Attempting to create an array with float64 or complex128 data type on this device
25
+ raises an error.
26
+ - The existing `Device("device2")` object has been modified: it supports float64 data
27
+ type but defaults to float32 and complex64:
28
+ `asarray([1.0], device=Device("device2")).dtype` is `float32`.
29
+
30
+
31
+ ### Contributors
32
+
33
+ The following users contributed to this release:
34
+
35
+ Evgeni Burovski,
36
+ Lucas Colley,
37
+ Ralf Gommers,
38
+ Tim Head.
39
+
40
+
3
41
  ## 2.5 (2026-02-23)
4
42
 
5
43
  This release targets the 2025.12 revision of the Array API standard, and supports
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: array_api_strict
3
- Version: 2.5
3
+ Version: 2.6
4
4
  Summary: A strict, minimal implementation of the Python array API standard.
5
5
  Author: Consortium for Python Data API Standards
6
6
  License: Copyright (c) 2021-2024, NumPy Developers, Consortium for Python Data API Standards
@@ -45,3 +45,10 @@ array_api_tests/test_special_cases.py::test_unary[tanh(real(x_i) is +infinity an
45
45
  array_api_tests/test_special_cases.py::test_unary[acosh(real(x_i) is +0 and imag(x_i) is NaN) -> NaN \xb1 \u03c0j/2]
46
46
 
47
47
  array_api_tests/test_special_cases.py::test_unary[sqrt(real(x_i) is +infinity and isfinite(imag(x_i)) and imag(x_i) > 0) -> +0 + infinity j]
48
+
49
+ # remove this when array-api-strict 2.6 is released: this is only for array-api-tests,
50
+ # which runs self-tests against a released array-api-strict
51
+ array_api_tests/test_dlpack.py::test_from_dlpack
52
+
53
+ # NumPy 1.26 : NotImplementedError: The copy argument to __dlpack__ is not yet implemented
54
+ array_api_tests/test_dlpack.py::test_dunder_dlpack
@@ -20,7 +20,7 @@ import sys
20
20
  from collections.abc import Iterator
21
21
  from enum import IntEnum
22
22
  from types import EllipsisType, ModuleType
23
- from typing import Any, Final, Literal, SupportsIndex, Callable
23
+ from typing import Any, Literal, SupportsIndex, Callable
24
24
 
25
25
  import numpy as np
26
26
  import numpy.typing as npt
@@ -40,35 +40,14 @@ from ._dtypes import (
40
40
  _real_to_complex_map,
41
41
  _result_type,
42
42
  )
43
+ from ._devices import (
44
+ CPU_DEVICE, Device, device_supports_dtype, _normalize_dl_device, _DLPACK_DEVICE_FOR,
45
+ DLDeviceType
46
+ )
43
47
  from ._flags import get_array_api_strict_flags, set_array_api_strict_flags
44
48
  from ._typing import PyCapsule
45
49
 
46
50
 
47
- class Device:
48
- _device: Final[str]
49
- __slots__ = ("_device", "__weakref__")
50
-
51
- def __init__(self, device: str = "CPU_DEVICE"):
52
- if device not in ("CPU_DEVICE", "device1", "device2"):
53
- raise ValueError(f"The device '{device}' is not a valid choice.")
54
- self._device = device
55
-
56
- def __repr__(self) -> str:
57
- return f"array_api_strict.Device('{self._device}')"
58
-
59
- def __eq__(self, other: object) -> bool:
60
- if not isinstance(other, Device):
61
- return False
62
- return self._device == other._device
63
-
64
- def __hash__(self) -> int:
65
- return hash(("Device", self._device))
66
-
67
-
68
- CPU_DEVICE = Device()
69
- ALL_DEVICES = (CPU_DEVICE, Device("device1"), Device("device2"))
70
-
71
-
72
51
  class Array:
73
52
  """
74
53
  n-d array object for the array API namespace.
@@ -113,10 +92,15 @@ class Array:
113
92
  raise TypeError(
114
93
  f"The array_api_strict namespace does not support the dtype '{x.dtype}'"
115
94
  )
116
- obj._array = x
117
- obj._dtype = _dtype
95
+
118
96
  if device is None:
119
97
  device = CPU_DEVICE
98
+ if not device_supports_dtype(device, _dtype):
99
+ raise ValueError(f"Device {device!r} does not support dtype={_dtype!r}.")
100
+
101
+ obj._array = x
102
+ obj._dtype = _dtype
103
+
120
104
  obj._device = device
121
105
  return obj
122
106
 
@@ -400,7 +384,7 @@ class Array:
400
384
  isinstance(i, SupportsIndex) # i.e. ints
401
385
  or isinstance(i, slice)
402
386
  or i == Ellipsis
403
- or i is None
387
+ or (op == "getitem" and i is None) # `None` disallowed in setitem
404
388
  or isinstance(i, Array)
405
389
  or isinstance(i, np.ndarray)
406
390
  ):
@@ -630,22 +614,40 @@ class Array:
630
614
  raise NotImplementedError("The copy argument to __dlpack__ is not yet implemented")
631
615
 
632
616
  return self._array.__dlpack__(stream=stream)
633
- else:
634
- kwargs = {'stream': stream}
635
- if max_version is not _undef:
636
- kwargs['max_version'] = max_version
637
- if dl_device is not _undef:
638
- kwargs['dl_device'] = dl_device
639
- if copy is not _undef:
640
- kwargs['copy'] = copy
641
- return self._array.__dlpack__(**kwargs)
617
+
618
+ kwargs: dict[str, Any] = {'stream': stream}
619
+ self_dl_device = _normalize_dl_device(*_DLPACK_DEVICE_FOR[self._device])
620
+ cpu_dl_device = _normalize_dl_device(DLDeviceType.kDLCPU, 0)
621
+ numpy_dl_device: tuple[IntEnum, int] | None = None
622
+
623
+ if dl_device not in [_undef, None]:
624
+ requested = _normalize_dl_device(dl_device[0], dl_device[1])
625
+ if requested == self_dl_device:
626
+ pass
627
+ elif requested == cpu_dl_device and self_dl_device != cpu_dl_device:
628
+ if copy is False:
629
+ raise BufferError(
630
+ "Cannot export array to CPU without copying when copy=False"
631
+ )
632
+ if copy is _undef:
633
+ copy = True
634
+ numpy_dl_device = (DLDeviceType.kDLCPU, 0)
635
+ else:
636
+ raise BufferError("unsupported device requested")
637
+
638
+ if max_version is not _undef:
639
+ kwargs['max_version'] = max_version
640
+ if numpy_dl_device is not None:
641
+ kwargs['dl_device'] = numpy_dl_device
642
+ if copy is not _undef:
643
+ kwargs['copy'] = copy
644
+ return self._array.__dlpack__(**kwargs)
642
645
 
643
646
  def __dlpack_device__(self) -> tuple[IntEnum, int]:
644
647
  """
645
648
  Performs the operation __dlpack_device__.
646
649
  """
647
- # Note: device support is required for this
648
- return self._array.__dlpack_device__()
650
+ return _DLPACK_DEVICE_FOR[self._device]
649
651
 
650
652
  def __eq__(self, other: Array | complex, /) -> Array: # type: ignore[override]
651
653
  """
@@ -960,6 +962,10 @@ class Array:
960
962
  other = value
961
963
  if isinstance(value, (bool, int, float, complex)):
962
964
  other = self._promote_scalar(value)
965
+ else:
966
+ if value.device != self.device:
967
+ raise ValueError(f"mismatched devices: {self.device = } != {value.device =}.")
968
+
963
969
  dt = _result_type(self.dtype, other.dtype)
964
970
  if dt != self.dtype:
965
971
  raise TypeError(f"mismatched dtypes: {self.dtype = } and {other.dtype = }")
@@ -5,7 +5,11 @@ from typing import TYPE_CHECKING, Literal
5
5
 
6
6
  import numpy as np
7
7
 
8
- from ._dtypes import DType, _all_dtypes, _np_dtype
8
+ from ._dtypes import DType, _all_dtypes, _np_dtype, bool as xp_bool
9
+ from ._devices import (
10
+ Device, device_supports_dtype, get_default_dtypes,
11
+ check_device as _check_device
12
+ )
9
13
  from ._flags import get_array_api_strict_flags
10
14
  from ._typing import NestedSequence, SupportsBufferProtocol, SupportsDLPack
11
15
 
@@ -14,7 +18,7 @@ if TYPE_CHECKING:
14
18
  from typing_extensions import TypeIs
15
19
 
16
20
  # Circular import
17
- from ._array_object import Array, Device
21
+ from ._array_object import Array
18
22
 
19
23
 
20
24
  class Undef(Enum):
@@ -24,10 +28,15 @@ class Undef(Enum):
24
28
  _undef = Undef.UNDEF
25
29
 
26
30
 
27
- def _check_valid_dtype(dtype: DType | None) -> None:
31
+ def _check_valid_dtype(dtype: DType | None, device: Device | None = None) -> None:
28
32
  # Note: Only spelling dtypes as the dtype objects is supported.
29
- if dtype not in (None,) + _all_dtypes:
30
- raise ValueError(f"dtype must be one of the supported dtypes, got {dtype!r}")
33
+ if dtype is not None:
34
+ if dtype not in _all_dtypes:
35
+ raise ValueError(f"dtype must be one of the supported dtypes, got {dtype!r}")
36
+
37
+ if device is not None:
38
+ if not device_supports_dtype(device, dtype):
39
+ raise ValueError(f"Device {device!r} does not support dtype={dtype!r}.")
31
40
 
32
41
 
33
42
  def _supports_buffer_protocol(obj: object) -> TypeIs[SupportsBufferProtocol]:
@@ -38,18 +47,6 @@ def _supports_buffer_protocol(obj: object) -> TypeIs[SupportsBufferProtocol]:
38
47
  return True
39
48
 
40
49
 
41
- def _check_device(device: Device | None) -> None:
42
- # _array_object imports in this file are inside the functions to avoid
43
- # circular imports
44
- from ._array_object import ALL_DEVICES, Device
45
-
46
- if device is not None and not isinstance(device, Device):
47
- raise ValueError(f"Unsupported device {device!r}")
48
-
49
- if device is not None and device not in ALL_DEVICES:
50
- raise ValueError(f"Unsupported device {device!r}")
51
-
52
-
53
50
  def asarray(
54
51
  obj: Array | complex | NestedSequence[complex] | SupportsBufferProtocol,
55
52
  /,
@@ -65,11 +62,12 @@ def asarray(
65
62
  """
66
63
  from ._array_object import Array
67
64
 
68
- _check_valid_dtype(dtype)
65
+ _check_device(device)
66
+ _check_valid_dtype(dtype, device)
69
67
  _np_dtype = None
70
68
  if dtype is not None:
71
69
  _np_dtype = dtype._np_dtype
72
- _check_device(device)
70
+
73
71
  if isinstance(obj, Array) and device is None:
74
72
  device = obj.device
75
73
 
@@ -108,6 +106,27 @@ def asarray(
108
106
  raise OverflowError("Integer out of bounds for array dtypes")
109
107
 
110
108
  res = np.array(obj, dtype=_np_dtype, copy=copy)
109
+
110
+ # numpy default dtype may differ; if so, adjust the dtype
111
+ if dtype is None and device is not None:
112
+ res_dtype = DType(res.dtype)
113
+ # The dtype selected by Numpy might not be the default dtype
114
+ # on this device. We thus find the default dtype for the dtype "kind", and
115
+ # cast to the device-appropriate default.
116
+ from ._data_type_functions import isdtype
117
+ if isdtype(res_dtype, "bool"):
118
+ target_dtype = DType("bool")
119
+ elif isdtype(res_dtype, "integral"):
120
+ target_dtype = get_default_dtypes(device)["integral"]
121
+ elif isdtype(res_dtype, "real floating"):
122
+ target_dtype = get_default_dtypes(device)["real floating"]
123
+ elif isdtype(res_dtype, "complex floating"):
124
+ target_dtype = get_default_dtypes(device)["complex floating"]
125
+ else:
126
+ raise ValueError(f"{res_dtype = } not understood.")
127
+
128
+ res = res.astype(target_dtype._np_dtype)
129
+
111
130
  return Array._new(res, device=device)
112
131
 
113
132
 
@@ -127,8 +146,13 @@ def arange(
127
146
  """
128
147
  from ._array_object import Array
129
148
 
130
- _check_valid_dtype(dtype)
131
149
  _check_device(device)
150
+ _check_valid_dtype(dtype, device)
151
+ if dtype is None:
152
+ if any(isinstance(x, float) for x in (start, stop, step)):
153
+ dtype = get_default_dtypes(device)["real floating"]
154
+ else:
155
+ dtype = get_default_dtypes(device)["integral"]
132
156
 
133
157
  return Array._new(
134
158
  np.arange(start, stop, step, dtype=_np_dtype(dtype)),
@@ -149,8 +173,10 @@ def empty(
149
173
  """
150
174
  from ._array_object import Array
151
175
 
152
- _check_valid_dtype(dtype)
153
176
  _check_device(device)
177
+ _check_valid_dtype(dtype, device)
178
+ if dtype is None:
179
+ dtype = get_default_dtypes(device)["real floating"]
154
180
 
155
181
  return Array._new(np.empty(shape, dtype=_np_dtype(dtype)), device=device)
156
182
 
@@ -165,10 +191,12 @@ def empty_like(
165
191
  """
166
192
  from ._array_object import Array
167
193
 
168
- _check_valid_dtype(dtype)
169
194
  _check_device(device)
170
195
  if device is None:
171
196
  device = x.device
197
+ if dtype is None:
198
+ dtype = x.dtype
199
+ _check_valid_dtype(dtype, device)
172
200
 
173
201
  return Array._new(np.empty_like(x._array, dtype=_np_dtype(dtype)), device=device)
174
202
 
@@ -189,8 +217,10 @@ def eye(
189
217
  """
190
218
  from ._array_object import Array
191
219
 
192
- _check_valid_dtype(dtype)
193
220
  _check_device(device)
221
+ _check_valid_dtype(dtype, device)
222
+ if dtype is None:
223
+ dtype = get_default_dtypes(device)["real floating"]
194
224
 
195
225
  return Array._new(
196
226
  np.eye(n_rows, M=n_cols, k=k, dtype=_np_dtype(dtype)), device=device
@@ -212,15 +242,20 @@ def from_dlpack(
212
242
  if copy is not _undef:
213
243
  raise ValueError("The copy argument to from_dlpack requires at least version 2023.12 of the array API")
214
244
 
215
- # Going to wait for upstream numpy support
216
245
  if device is not _undef:
217
246
  _check_device(device)
218
247
  else:
219
248
  device = None
220
- if copy not in [_undef, None]:
221
- raise NotImplementedError("The copy argument to from_dlpack is not yet implemented")
249
+ if hasattr(x, "__dlpack_device__"):
250
+ from ._devices import _device_from_dlpack_device
222
251
 
223
- return Array._new(np.from_dlpack(x), device=device)
252
+ dl_type, dl_id = x.__dlpack_device__()
253
+ device = _device_from_dlpack_device(dl_type, dl_id)
254
+ if copy in [_undef, None]:
255
+ # numpy 1.26 does not have the copy= arg
256
+ return Array._new(np.from_dlpack(x), device=device)
257
+
258
+ return Array._new(np.from_dlpack(x, copy=copy), device=device)
224
259
 
225
260
 
226
261
  def full(
@@ -237,12 +272,22 @@ def full(
237
272
  """
238
273
  from ._array_object import Array
239
274
 
240
- _check_valid_dtype(dtype)
241
275
  _check_device(device)
276
+ _check_valid_dtype(dtype, device)
242
277
 
243
278
  if not isinstance(fill_value, bool | int | float | complex):
244
279
  msg = f"Expected Python scalar fill_value, got type {type(fill_value)}"
245
280
  raise TypeError(msg)
281
+
282
+ if dtype is None:
283
+ if type(fill_value) == bool:
284
+ dtype = xp_bool
285
+ else:
286
+ kind = {
287
+ int: "integral", float: "real floating", complex: "complex floating"
288
+ }[type(fill_value)]
289
+ dtype = get_default_dtypes(device)[kind]
290
+
246
291
  res = np.full(shape, fill_value, dtype=_np_dtype(dtype))
247
292
  if DType(res.dtype) not in _all_dtypes:
248
293
  # This will happen if the fill value is not something that NumPy
@@ -266,10 +311,12 @@ def full_like(
266
311
  """
267
312
  from ._array_object import Array
268
313
 
269
- _check_valid_dtype(dtype)
270
314
  _check_device(device)
271
315
  if device is None:
272
316
  device = x.device
317
+ if dtype is None:
318
+ dtype = x.dtype
319
+ _check_valid_dtype(dtype, device)
273
320
 
274
321
  if not isinstance(fill_value, bool | int | float | complex):
275
322
  msg = f"Expected Python scalar fill_value, got type {type(fill_value)}"
@@ -300,8 +347,13 @@ def linspace(
300
347
  """
301
348
  from ._array_object import Array
302
349
 
303
- _check_valid_dtype(dtype)
304
350
  _check_device(device)
351
+ _check_valid_dtype(dtype, device)
352
+ if dtype is None:
353
+ if isinstance(start, complex) or isinstance(stop, complex):
354
+ dtype = get_default_dtypes(device)["complex floating"]
355
+ else:
356
+ dtype = get_default_dtypes(device)["real floating"]
305
357
 
306
358
  return Array._new(
307
359
  np.linspace(start, stop, num, dtype=_np_dtype(dtype), endpoint=endpoint),
@@ -353,8 +405,10 @@ def ones(
353
405
  """
354
406
  from ._array_object import Array
355
407
 
356
- _check_valid_dtype(dtype)
357
408
  _check_device(device)
409
+ _check_valid_dtype(dtype, device)
410
+ if dtype is None:
411
+ dtype = get_default_dtypes(device)["real floating"]
358
412
 
359
413
  return Array._new(np.ones(shape, dtype=_np_dtype(dtype)), device=device)
360
414
 
@@ -369,10 +423,12 @@ def ones_like(
369
423
  """
370
424
  from ._array_object import Array
371
425
 
372
- _check_valid_dtype(dtype)
373
426
  _check_device(device)
374
427
  if device is None:
375
428
  device = x.device
429
+ if dtype is None:
430
+ dtype = x.dtype
431
+ _check_valid_dtype(dtype, device)
376
432
 
377
433
  return Array._new(np.ones_like(x._array, dtype=_np_dtype(dtype)), device=device)
378
434
 
@@ -418,8 +474,10 @@ def zeros(
418
474
  """
419
475
  from ._array_object import Array
420
476
 
421
- _check_valid_dtype(dtype)
422
477
  _check_device(device)
478
+ _check_valid_dtype(dtype, device)
479
+ if dtype is None:
480
+ dtype = get_default_dtypes(device)["real floating"]
423
481
 
424
482
  return Array._new(np.zeros(shape, dtype=_np_dtype(dtype)), device=device)
425
483
 
@@ -434,9 +492,11 @@ def zeros_like(
434
492
  """
435
493
  from ._array_object import Array
436
494
 
437
- _check_valid_dtype(dtype)
438
495
  _check_device(device)
439
496
  if device is None:
440
497
  device = x.device
498
+ if dtype is None:
499
+ dtype = x.dtype
500
+ _check_valid_dtype(dtype, device)
441
501
 
442
502
  return Array._new(np.zeros_like(x._array, dtype=_np_dtype(dtype)), device=device)