|
1 | 1 | from collections.abc import Sequence |
2 | | -from builtins import all as py_all |
3 | | -from builtins import any as py_any |
4 | | -from builtins import bool as py_bool |
5 | | -from builtins import int as py_int |
6 | | -from builtins import slice as py_slice |
7 | | -from builtins import tuple as py_tuple |
8 | 2 | from typing import Final |
9 | 3 |
|
10 | 4 | import tensorflow as _tf |
|
13 | 7 |
|
14 | 8 | __all__ = clone_module("tensorflow", globals()) |
15 | 9 |
|
| 10 | +# TensorShape is not the array object; this lets shape helpers consume x.shape |
| 11 | +# without mutating TensorFlow tensors. |
16 | 12 | Sequence.register(type(_tf.TensorShape(()))) |
17 | 13 |
|
18 | | - |
19 | | -class _ArrayAPIShape(py_tuple): |
20 | | - def __new__(cls, tensor_shape): |
21 | | - obj = super().__new__(cls, py_tuple(tensor_shape.as_list())) |
22 | | - obj._tensor_shape = tensor_shape |
23 | | - return obj |
24 | | - |
25 | | - @property |
26 | | - def rank(self): |
27 | | - return self._tensor_shape.rank |
28 | | - |
29 | | - @property |
30 | | - def ndims(self): |
31 | | - return self._tensor_shape.ndims |
32 | | - |
33 | | - def as_list(self): |
34 | | - return self._tensor_shape.as_list() |
35 | | - |
36 | | - def __getitem__(self, key): |
37 | | - out = self._tensor_shape[key] |
38 | | - if isinstance(key, py_slice): |
39 | | - return type(self)(out) |
40 | | - return out |
41 | | - |
42 | | - def __getattr__(self, name): |
43 | | - return getattr(self._tensor_shape, name) |
44 | | - |
45 | | - |
46 | | -def _patch_eager_tensor_shape() -> None: |
47 | | - eager_tensor = type(_tf.constant(0)) |
48 | | - if getattr(eager_tensor, "_array_api_compat_shape_patched", False): |
49 | | - return |
50 | | - old_shape = eager_tensor.shape |
51 | | - |
52 | | - def shape(self): |
53 | | - return _ArrayAPIShape(old_shape.__get__(self, eager_tensor)) |
54 | | - |
55 | | - eager_tensor.shape = property(shape) |
56 | | - eager_tensor._array_api_compat_shape_patched = True |
57 | | - |
58 | | - |
59 | | -_patch_eager_tensor_shape() |
60 | | - |
61 | | - |
62 | | -def _patch_eager_tensor_getitem() -> None: |
63 | | - eager_tensor = type(_tf.constant(0)) |
64 | | - if getattr(eager_tensor, "_array_api_compat_getitem_patched", False): |
65 | | - return |
66 | | - old_getitem = eager_tensor.__getitem__ |
67 | | - |
68 | | - def tensor_index(key): |
69 | | - if isinstance(key, _tf.Tensor): |
70 | | - return int(key) if key.shape == () else key |
71 | | - if isinstance(key, py_slice): |
72 | | - return py_slice(tensor_index(key.start), tensor_index(key.stop), tensor_index(key.step)) |
73 | | - if isinstance(key, py_tuple): |
74 | | - return py_tuple(tensor_index(k) for k in key) |
75 | | - return key |
76 | | - |
77 | | - def bool_getitem(self, key): |
78 | | - x_shape = py_tuple(self.shape) |
79 | | - key_shape = py_tuple(key.shape) |
80 | | - if key.ndim > self.ndim or py_any( |
81 | | - ks not in (xs, 0) for xs, ks in zip(x_shape, key_shape, strict=False) |
82 | | - ): |
83 | | - raise IndexError("boolean index did not match indexed array") |
84 | | - if key.ndim == 0: |
85 | | - x = _tf.reshape(self, (1,) + x_shape) |
86 | | - return _tf.boolean_mask(x, _tf.reshape(key, (1,))) |
87 | | - if py_any(ks == 0 for ks in key_shape): |
88 | | - return _tf.zeros((0,) + x_shape[key.ndim :], dtype=self.dtype) |
89 | | - return _tf.boolean_mask(self, key) |
90 | | - |
91 | | - def high_dim_int_getitem(self, key): |
92 | | - shape = py_tuple(self.shape) |
93 | | - if len(shape) <= 7: |
94 | | - return None |
95 | | - key = tensor_index(key) |
96 | | - if isinstance(key, py_int): |
97 | | - key = (key,) |
98 | | - if not isinstance(key, py_tuple) or len(key) != len(shape): |
99 | | - return None |
100 | | - if not py_all(isinstance(k, py_int) and not isinstance(k, py_bool) for k in key): |
101 | | - return None |
102 | | - normalized = [] |
103 | | - for k, side in zip(key, shape, strict=True): |
104 | | - k = k + side if k < 0 else k |
105 | | - if k < 0 or k >= side: |
106 | | - raise IndexError("index out of bounds") |
107 | | - normalized.append(k) |
108 | | - flat_index = 0 |
109 | | - for k, stride in zip(normalized, _strides(shape), strict=True): |
110 | | - flat_index += k * stride |
111 | | - return _tf.gather(_tf.reshape(self, (-1,)), flat_index) |
112 | | - |
113 | | - def _strides(shape): |
114 | | - strides = [] |
115 | | - stride = 1 |
116 | | - for side in reversed(shape): |
117 | | - strides.append(stride) |
118 | | - stride *= side |
119 | | - return py_tuple(reversed(strides)) |
120 | | - |
121 | | - def getitem(self, key): |
122 | | - if isinstance(key, _tf.Tensor) and key.dtype == _tf.bool: |
123 | | - return bool_getitem(self, key) |
124 | | - out = high_dim_int_getitem(self, key) |
125 | | - if out is not None: |
126 | | - return out |
127 | | - return old_getitem(self, tensor_index(key)) |
128 | | - |
129 | | - eager_tensor.__getitem__ = getitem |
130 | | - eager_tensor._array_api_compat_getitem_patched = True |
131 | | - |
132 | | - |
133 | | -_patch_eager_tensor_getitem() |
134 | | - |
135 | | - |
136 | | -def _patch_eager_tensor_binary_ops() -> None: |
137 | | - eager_tensor = type(_tf.constant(0)) |
138 | | - if getattr(eager_tensor, "_array_api_compat_binary_ops_patched", False): |
139 | | - return |
140 | | - |
141 | | - def wrap(name, func_name, *, reverse=False): |
142 | | - func = globals()[func_name] |
143 | | - |
144 | | - def op(self, other): |
145 | | - if reverse: |
146 | | - return func(other, self) |
147 | | - return func(self, other) |
148 | | - |
149 | | - setattr(eager_tensor, name, op) |
150 | | - |
151 | | - def wrap_unary(name, func_name): |
152 | | - func = globals()[func_name] |
153 | | - |
154 | | - def op(self): |
155 | | - return func(self) |
156 | | - |
157 | | - setattr(eager_tensor, name, op) |
158 | | - |
159 | | - for name, func_name, reverse in [ |
160 | | - ("__add__", "add", False), |
161 | | - ("__radd__", "add", True), |
162 | | - ("__sub__", "subtract", False), |
163 | | - ("__rsub__", "subtract", True), |
164 | | - ("__mul__", "multiply", False), |
165 | | - ("__rmul__", "multiply", True), |
166 | | - ("__floordiv__", "floor_divide", False), |
167 | | - ("__rfloordiv__", "floor_divide", True), |
168 | | - ("__mod__", "remainder", False), |
169 | | - ("__rmod__", "remainder", True), |
170 | | - ("__pow__", "pow", False), |
171 | | - ("__rpow__", "pow", True), |
172 | | - ("__lt__", "less", False), |
173 | | - ("__le__", "less_equal", False), |
174 | | - ("__gt__", "greater", False), |
175 | | - ("__ge__", "greater_equal", False), |
176 | | - ("__eq__", "equal", False), |
177 | | - ("__ne__", "not_equal", False), |
178 | | - ("__and__", "bitwise_and", False), |
179 | | - ("__rand__", "bitwise_and", True), |
180 | | - ("__or__", "bitwise_or", False), |
181 | | - ("__ror__", "bitwise_or", True), |
182 | | - ("__xor__", "bitwise_xor", False), |
183 | | - ("__rxor__", "bitwise_xor", True), |
184 | | - ("__lshift__", "bitwise_left_shift", False), |
185 | | - ("__rlshift__", "bitwise_left_shift", True), |
186 | | - ("__rshift__", "bitwise_right_shift", False), |
187 | | - ("__rrshift__", "bitwise_right_shift", True), |
188 | | - ("__matmul__", "matmul", False), |
189 | | - ("__rmatmul__", "matmul", True), |
190 | | - ]: |
191 | | - wrap(name, func_name, reverse=reverse) |
192 | | - |
193 | | - for name, func_name in [ |
194 | | - ("__abs__", "abs"), |
195 | | - ("__invert__", "bitwise_invert"), |
196 | | - ("__neg__", "negative"), |
197 | | - ("__pos__", "positive"), |
198 | | - ]: |
199 | | - wrap_unary(name, func_name) |
200 | | - |
201 | | - eager_tensor._array_api_compat_binary_ops_patched = True |
202 | | - |
203 | | - |
204 | | -def _patch_eager_tensor_dlpack() -> None: |
205 | | - eager_tensor = type(_tf.constant(0)) |
206 | | - if getattr(eager_tensor, "_array_api_compat_dlpack_patched", False): |
207 | | - return |
208 | | - old_dlpack = eager_tensor.__dlpack__ |
209 | | - |
210 | | - def dlpack(self, *, stream=None, max_version=None, dl_device=None, copy=None): |
211 | | - tensor = _tf.identity(self) if copy is True else self |
212 | | - del dl_device |
213 | | - return old_dlpack(tensor, stream=stream, max_version=max_version) |
214 | | - |
215 | | - eager_tensor.__dlpack__ = dlpack |
216 | | - eager_tensor._array_api_compat_dlpack_patched = True |
217 | | - |
218 | | - |
219 | 14 | # These imports may overwrite names from the import * above. |
220 | 15 | from . import _aliases |
221 | 16 | from ._aliases import * # noqa: F403 |
222 | 17 | from ._info import __array_namespace_info__ # noqa: F401 |
223 | 18 |
|
224 | | -_patch_eager_tensor_binary_ops() |
225 | | -_patch_eager_tensor_dlpack() |
226 | | - |
227 | 19 | __import__(__spec__.parent + ".linalg") |
228 | 20 | __import__(__spec__.parent + ".fft") |
229 | 21 |
|
|
0 commit comments