flopscope 0.2.0__py3-none-any.whl
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.
- benchmarks/__init__.py +1 -0
- benchmarks/__main__.py +6 -0
- benchmarks/_baseline.py +171 -0
- benchmarks/_bitwise.py +231 -0
- benchmarks/_complex.py +176 -0
- benchmarks/_contractions.py +291 -0
- benchmarks/_fft.py +198 -0
- benchmarks/_impl_urls.py +139 -0
- benchmarks/_linalg.py +197 -0
- benchmarks/_linalg_delegates.py +407 -0
- benchmarks/_metadata.py +141 -0
- benchmarks/_misc.py +653 -0
- benchmarks/_perf.py +321 -0
- benchmarks/_perm_group_calibration.py +175 -0
- benchmarks/_pointwise.py +372 -0
- benchmarks/_polynomial.py +193 -0
- benchmarks/_random.py +209 -0
- benchmarks/_reductions.py +136 -0
- benchmarks/_sorting.py +289 -0
- benchmarks/_stats.py +137 -0
- benchmarks/_window.py +92 -0
- benchmarks/accumulation/__init__.py +0 -0
- benchmarks/accumulation/bench_cost_compute.py +138 -0
- benchmarks/dashboard.py +312 -0
- benchmarks/runner.py +636 -0
- flopscope/__init__.py +273 -0
- flopscope/_accumulation/__init__.py +13 -0
- flopscope/_accumulation/_bipartite.py +121 -0
- flopscope/_accumulation/_burnside.py +51 -0
- flopscope/_accumulation/_cache.py +146 -0
- flopscope/_accumulation/_components.py +153 -0
- flopscope/_accumulation/_cost.py +1414 -0
- flopscope/_accumulation/_cost_descriptions.py +63 -0
- flopscope/_accumulation/_detection.py +318 -0
- flopscope/_accumulation/_ladder.py +191 -0
- flopscope/_accumulation/_output_orbit.py +104 -0
- flopscope/_accumulation/_partition.py +290 -0
- flopscope/_accumulation/_path_info.py +211 -0
- flopscope/_accumulation/_public.py +169 -0
- flopscope/_accumulation/_reduction.py +310 -0
- flopscope/_accumulation/_regimes.py +303 -0
- flopscope/_accumulation/_shape.py +33 -0
- flopscope/_accumulation/_wreath.py +209 -0
- flopscope/_budget.py +1027 -0
- flopscope/_config.py +118 -0
- flopscope/_counting_ops.py +451 -0
- flopscope/_display.py +478 -0
- flopscope/_docstrings.py +59 -0
- flopscope/_dtypes.py +20 -0
- flopscope/_einsum.py +717 -0
- flopscope/_errstate.py +25 -0
- flopscope/_flops.py +282 -0
- flopscope/_free_ops.py +2654 -0
- flopscope/_ndarray.py +1126 -0
- flopscope/_opt_einsum/LICENSE +21 -0
- flopscope/_opt_einsum/NOTICE +59 -0
- flopscope/_opt_einsum/__init__.py +209 -0
- flopscope/_opt_einsum/_contract.py +1478 -0
- flopscope/_opt_einsum/_helpers.py +164 -0
- flopscope/_opt_einsum/_hsluv.py +273 -0
- flopscope/_opt_einsum/_path_random.py +462 -0
- flopscope/_opt_einsum/_paths.py +1653 -0
- flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
- flopscope/_opt_einsum/_symmetry.py +140 -0
- flopscope/_opt_einsum/_typing.py +37 -0
- flopscope/_perm_group.py +717 -0
- flopscope/_pointwise.py +2522 -0
- flopscope/_polynomial.py +278 -0
- flopscope/_registry.py +3216 -0
- flopscope/_sorting_ops.py +571 -0
- flopscope/_symmetric.py +812 -0
- flopscope/_symmetry_transport.py +510 -0
- flopscope/_symmetry_utils.py +669 -0
- flopscope/_type_info.py +12 -0
- flopscope/_unwrap.py +70 -0
- flopscope/_validation.py +83 -0
- flopscope/_version_check.py +46 -0
- flopscope/_weights.py +195 -0
- flopscope/_window.py +177 -0
- flopscope/accounting.py +565 -0
- flopscope/data/default_weights.json +462 -0
- flopscope/data/weights.csv +509 -0
- flopscope/errors.py +197 -0
- flopscope/numpy/__init__.py +878 -0
- flopscope/numpy/fft/__init__.py +55 -0
- flopscope/numpy/fft/_free.py +51 -0
- flopscope/numpy/fft/_transforms.py +695 -0
- flopscope/numpy/linalg/__init__.py +105 -0
- flopscope/numpy/linalg/_aliases.py +126 -0
- flopscope/numpy/linalg/_compound.py +161 -0
- flopscope/numpy/linalg/_decompositions.py +353 -0
- flopscope/numpy/linalg/_properties.py +533 -0
- flopscope/numpy/linalg/_solvers.py +444 -0
- flopscope/numpy/linalg/_svd.py +122 -0
- flopscope/numpy/random/__init__.py +684 -0
- flopscope/numpy/random/_cost_formulas.py +115 -0
- flopscope/numpy/random/_counted_classes.py +241 -0
- flopscope/numpy/testing/__init__.py +13 -0
- flopscope/numpy/typing/__init__.py +30 -0
- flopscope/py.typed +0 -0
- flopscope/stats/__init__.py +84 -0
- flopscope/stats/_base.py +77 -0
- flopscope/stats/_cauchy.py +146 -0
- flopscope/stats/_erf.py +190 -0
- flopscope/stats/_expon.py +146 -0
- flopscope/stats/_laplace.py +150 -0
- flopscope/stats/_logistic.py +148 -0
- flopscope/stats/_lognorm.py +160 -0
- flopscope/stats/_ndtri.py +133 -0
- flopscope/stats/_norm.py +149 -0
- flopscope/stats/_truncnorm.py +186 -0
- flopscope/stats/_uniform.py +141 -0
- flopscope-0.2.0.dist-info/METADATA +23 -0
- flopscope-0.2.0.dist-info/RECORD +115 -0
- flopscope-0.2.0.dist-info/WHEEL +4 -0
flopscope/_free_ops.py
ADDED
|
@@ -0,0 +1,2654 @@
|
|
|
1
|
+
"""Zero-FLOP wrappers around NumPy tensor creation and manipulation.
|
|
2
|
+
|
|
3
|
+
Every function in this module delegates directly to the corresponding
|
|
4
|
+
NumPy function and costs **0 FLOPs**, so they work both inside and
|
|
5
|
+
outside a :class:`~flopscope._budget.BudgetContext`.
|
|
6
|
+
"""
|
|
7
|
+
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
import inspect as _inspect
|
|
11
|
+
from collections.abc import Sequence
|
|
12
|
+
from functools import lru_cache
|
|
13
|
+
from typing import Any
|
|
14
|
+
|
|
15
|
+
import numpy as _np
|
|
16
|
+
from numpy.typing import ArrayLike, DTypeLike
|
|
17
|
+
|
|
18
|
+
from flopscope import _symmetry_transport as _st
|
|
19
|
+
from flopscope._budget import _call_numpy, _counted_wrapper
|
|
20
|
+
from flopscope._docstrings import attach_docstring
|
|
21
|
+
from flopscope._ndarray import (
|
|
22
|
+
FlopscopeArray,
|
|
23
|
+
_asplainflopscope,
|
|
24
|
+
_to_base_ndarray,
|
|
25
|
+
_to_base_ndarray_tree,
|
|
26
|
+
)
|
|
27
|
+
from flopscope._perm_group import SymmetryGroup
|
|
28
|
+
from flopscope._symmetric import SymmetricTensor
|
|
29
|
+
from flopscope._symmetry_utils import (
|
|
30
|
+
broadcast_group,
|
|
31
|
+
validate_symmetry_group,
|
|
32
|
+
wrap_with_inferred_symmetry,
|
|
33
|
+
wrap_with_symmetry,
|
|
34
|
+
wrap_with_trusted_symmetry,
|
|
35
|
+
)
|
|
36
|
+
from flopscope._validation import require_budget
|
|
37
|
+
from flopscope.errors import (
|
|
38
|
+
SymmetryError,
|
|
39
|
+
UnsupportedFunctionError,
|
|
40
|
+
_warn_symmetry_loss,
|
|
41
|
+
)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
def _warn_if_symmetric(arr, op_name: str) -> None:
|
|
45
|
+
"""Emit SymmetryLossWarning if `arr` is a SymmetricTensor with a group."""
|
|
46
|
+
if isinstance(arr, SymmetricTensor) and arr.symmetry is not None:
|
|
47
|
+
_warn_symmetry_loss(
|
|
48
|
+
lost_dims=[arr.symmetry.axes or tuple(range(arr.symmetry.degree))],
|
|
49
|
+
reason=f"op '{op_name}' is not symmetry-aware",
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
@lru_cache(maxsize=1024)
|
|
54
|
+
def _infer_constant_shape_symmetry(shape):
|
|
55
|
+
if len(shape) < 2:
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
blocks_by_extent: dict[int, list[int]] = {}
|
|
59
|
+
for axis, extent in enumerate(shape):
|
|
60
|
+
blocks_by_extent.setdefault(int(extent), []).append(axis)
|
|
61
|
+
|
|
62
|
+
blocks = tuple(tuple(axes) for axes in blocks_by_extent.values() if len(axes) >= 2)
|
|
63
|
+
if not blocks:
|
|
64
|
+
return None
|
|
65
|
+
if len(blocks) == 1:
|
|
66
|
+
return SymmetryGroup.symmetric(axes=blocks[0])
|
|
67
|
+
return SymmetryGroup.young(blocks=blocks)
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def _wrap_constant_fill(result: _np.ndarray) -> FlopscopeArray:
|
|
71
|
+
symmetry = _infer_constant_shape_symmetry(result.shape)
|
|
72
|
+
if symmetry is None:
|
|
73
|
+
return result # type: ignore[return-value]
|
|
74
|
+
return wrap_with_inferred_symmetry(result, symmetry) # type: ignore[return-value]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _compatible_symmetry_for_shape(symmetry, shape):
|
|
78
|
+
"""Return ``symmetry`` only when ``shape`` still supports it exactly."""
|
|
79
|
+
if symmetry is None:
|
|
80
|
+
return None
|
|
81
|
+
try:
|
|
82
|
+
validate_symmetry_group(symmetry, ndim=len(shape), shape=shape)
|
|
83
|
+
except (SymmetryError, ValueError):
|
|
84
|
+
return None
|
|
85
|
+
return symmetry
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
def _infer_structural_constructor_symmetry(*, kind, N=None, M=None, k=0, v_ndim=None):
|
|
89
|
+
if kind == "eye":
|
|
90
|
+
if k == 0 and (M is None or M == N):
|
|
91
|
+
return SymmetryGroup.symmetric(axes=(0, 1))
|
|
92
|
+
return None
|
|
93
|
+
if kind == "identity":
|
|
94
|
+
return SymmetryGroup.symmetric(axes=(0, 1))
|
|
95
|
+
if kind == "diag":
|
|
96
|
+
if v_ndim == 1 and k == 0:
|
|
97
|
+
return SymmetryGroup.symmetric(axes=(0, 1))
|
|
98
|
+
return None
|
|
99
|
+
if kind == "diagflat":
|
|
100
|
+
if k == 0:
|
|
101
|
+
return SymmetryGroup.symmetric(axes=(0, 1))
|
|
102
|
+
return None
|
|
103
|
+
return None
|
|
104
|
+
|
|
105
|
+
|
|
106
|
+
# ---------------------------------------------------------------------------
|
|
107
|
+
# Tensor creation
|
|
108
|
+
# ---------------------------------------------------------------------------
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
@_counted_wrapper
|
|
112
|
+
def array(
|
|
113
|
+
object: ArrayLike,
|
|
114
|
+
dtype: DTypeLike | None = None,
|
|
115
|
+
**kwargs: Any,
|
|
116
|
+
) -> FlopscopeArray:
|
|
117
|
+
"""Create an array. Cost: numel(output)."""
|
|
118
|
+
budget = require_budget()
|
|
119
|
+
# Pre-compute cost from input to keep numpy call inside the timer
|
|
120
|
+
_probe = _np.asarray(object)
|
|
121
|
+
cost = max(_probe.size, 1)
|
|
122
|
+
with budget.deduct(
|
|
123
|
+
"array", flop_cost=cost, subscripts=None, shapes=(_probe.shape,)
|
|
124
|
+
):
|
|
125
|
+
result = _call_numpy(_np.array, object, dtype=dtype, **kwargs)
|
|
126
|
+
return result # type: ignore[return-value]
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
attach_docstring(array, _np.array, "counted_custom", "numel(input) FLOPs")
|
|
130
|
+
|
|
131
|
+
|
|
132
|
+
def zeros(
|
|
133
|
+
shape: int | Sequence[int],
|
|
134
|
+
dtype: DTypeLike = float,
|
|
135
|
+
**kwargs: Any,
|
|
136
|
+
) -> FlopscopeArray:
|
|
137
|
+
"""Return array of zeros. Wraps ``numpy.zeros``. Cost: 0 FLOPs."""
|
|
138
|
+
return _wrap_constant_fill(_np.zeros(shape, dtype=dtype, **kwargs))
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
attach_docstring(zeros, _np.zeros, "free", "0 FLOPs")
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def ones(
|
|
145
|
+
shape: int | Sequence[int],
|
|
146
|
+
dtype: DTypeLike = float,
|
|
147
|
+
**kwargs: Any,
|
|
148
|
+
) -> FlopscopeArray:
|
|
149
|
+
"""Return array of ones. Wraps ``numpy.ones``. Cost: 0 FLOPs."""
|
|
150
|
+
return _wrap_constant_fill(_np.ones(shape, dtype=dtype, **kwargs))
|
|
151
|
+
|
|
152
|
+
|
|
153
|
+
attach_docstring(ones, _np.ones, "free", "0 FLOPs")
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
@_counted_wrapper
|
|
157
|
+
def full(
|
|
158
|
+
shape: int | Sequence[int],
|
|
159
|
+
fill_value: ArrayLike,
|
|
160
|
+
dtype: DTypeLike | None = None,
|
|
161
|
+
**kwargs: Any,
|
|
162
|
+
) -> FlopscopeArray:
|
|
163
|
+
"""Return array filled with *fill_value*. Cost: numel(output)."""
|
|
164
|
+
budget = require_budget()
|
|
165
|
+
result = _np.full(shape, fill_value, dtype=dtype, **kwargs)
|
|
166
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
167
|
+
with budget.deduct("full", flop_cost=cost, subscripts=None, shapes=()):
|
|
168
|
+
result = _wrap_constant_fill(result)
|
|
169
|
+
return result
|
|
170
|
+
|
|
171
|
+
|
|
172
|
+
attach_docstring(full, _np.full, "free", "0 FLOPs")
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def eye(
|
|
176
|
+
N: int,
|
|
177
|
+
M: int | None = None,
|
|
178
|
+
k: int = 0,
|
|
179
|
+
dtype: DTypeLike = float,
|
|
180
|
+
**kwargs: Any,
|
|
181
|
+
) -> FlopscopeArray:
|
|
182
|
+
"""Return identity matrix. Wraps ``numpy.eye``. Cost: 0 FLOPs."""
|
|
183
|
+
result = _np.eye(N, M=M, k=k, dtype=dtype, **kwargs)
|
|
184
|
+
symmetry = _infer_structural_constructor_symmetry(kind="eye", N=N, M=M, k=k)
|
|
185
|
+
if symmetry is not None:
|
|
186
|
+
return wrap_with_trusted_symmetry(result, symmetry) # type: ignore[return-value]
|
|
187
|
+
return result # type: ignore[return-value]
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
attach_docstring(eye, _np.eye, "free", "0 FLOPs")
|
|
191
|
+
|
|
192
|
+
|
|
193
|
+
@_counted_wrapper
|
|
194
|
+
def diag(v: ArrayLike, k: int = 0) -> FlopscopeArray:
|
|
195
|
+
"""Extract diagonal or construct diagonal array.
|
|
196
|
+
|
|
197
|
+
Cost: numel(output) when constructing (1D→2D), min(m,n) when extracting (2D→1D).
|
|
198
|
+
"""
|
|
199
|
+
budget = require_budget()
|
|
200
|
+
v = _np.asarray(v)
|
|
201
|
+
if v.ndim == 1:
|
|
202
|
+
# Constructing diagonal matrix: output is (n+|k|) x (n+|k|)
|
|
203
|
+
n = v.shape[0] + abs(k)
|
|
204
|
+
cost = n * n
|
|
205
|
+
else:
|
|
206
|
+
# Extracting diagonal: reads min(m,n) elements
|
|
207
|
+
m, n = v.shape[0], v.shape[1] if v.ndim > 1 else v.shape[0]
|
|
208
|
+
cost = min(m, n)
|
|
209
|
+
with budget.deduct("diag", flop_cost=cost, subscripts=None, shapes=(v.shape,)):
|
|
210
|
+
result = _call_numpy(_np.diag, v, k=k)
|
|
211
|
+
symmetry = _infer_structural_constructor_symmetry(kind="diag", k=k, v_ndim=v.ndim)
|
|
212
|
+
if symmetry is not None:
|
|
213
|
+
return wrap_with_trusted_symmetry(result, symmetry) # type: ignore[return-value]
|
|
214
|
+
return result # type: ignore[return-value]
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
attach_docstring(diag, _np.diag, "free", "0 FLOPs")
|
|
218
|
+
|
|
219
|
+
|
|
220
|
+
@_counted_wrapper
|
|
221
|
+
def arange(*args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
222
|
+
"""Return evenly spaced values. Cost: numel(output)."""
|
|
223
|
+
budget = require_budget()
|
|
224
|
+
# cost depends on result; duration is post-hoc
|
|
225
|
+
# arange output size depends on start/stop/step parsing which is non-trivial
|
|
226
|
+
result = _np.arange(*args, **kwargs)
|
|
227
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
228
|
+
with budget.deduct("arange", flop_cost=cost, subscripts=None, shapes=()):
|
|
229
|
+
pass
|
|
230
|
+
return result
|
|
231
|
+
|
|
232
|
+
|
|
233
|
+
attach_docstring(arange, _np.arange, "counted_custom", "numel(output) FLOPs")
|
|
234
|
+
|
|
235
|
+
|
|
236
|
+
@_counted_wrapper
|
|
237
|
+
def linspace(
|
|
238
|
+
start: ArrayLike,
|
|
239
|
+
stop: ArrayLike,
|
|
240
|
+
num: int = 50,
|
|
241
|
+
**kwargs: Any,
|
|
242
|
+
) -> FlopscopeArray:
|
|
243
|
+
"""Return evenly spaced numbers. Cost: numel(output)."""
|
|
244
|
+
budget = require_budget()
|
|
245
|
+
cost = max(int(num), 1)
|
|
246
|
+
with budget.deduct("linspace", flop_cost=cost, subscripts=None, shapes=()):
|
|
247
|
+
result = _call_numpy(_np.linspace, start, stop, num=num, **kwargs) # type: ignore[arg-type, call-overload]
|
|
248
|
+
return result
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
attach_docstring(linspace, _np.linspace, "counted_custom", "numel(output) FLOPs")
|
|
252
|
+
|
|
253
|
+
|
|
254
|
+
def zeros_like(
|
|
255
|
+
a: ArrayLike,
|
|
256
|
+
dtype: DTypeLike | None = None,
|
|
257
|
+
**kwargs: Any,
|
|
258
|
+
) -> FlopscopeArray:
|
|
259
|
+
"""Return array of zeros with same shape. Wraps ``numpy.zeros_like``. Cost: 0 FLOPs."""
|
|
260
|
+
result = _np.zeros_like(_to_base_ndarray(a), dtype=dtype, **kwargs)
|
|
261
|
+
propagated_symmetry = None
|
|
262
|
+
if isinstance(a, SymmetricTensor):
|
|
263
|
+
propagated_symmetry = _compatible_symmetry_for_shape(a.symmetry, result.shape)
|
|
264
|
+
if propagated_symmetry is not None:
|
|
265
|
+
return wrap_with_trusted_symmetry(result, propagated_symmetry) # type: ignore[return-value]
|
|
266
|
+
inferred_symmetry = _infer_constant_shape_symmetry(result.shape)
|
|
267
|
+
if inferred_symmetry is None:
|
|
268
|
+
if isinstance(a, SymmetricTensor):
|
|
269
|
+
return _np.array(result, copy=False, subok=False) # type: ignore[return-value]
|
|
270
|
+
return result # type: ignore[return-value]
|
|
271
|
+
return wrap_with_inferred_symmetry(result, inferred_symmetry) # type: ignore[return-value]
|
|
272
|
+
|
|
273
|
+
|
|
274
|
+
attach_docstring(zeros_like, _np.zeros_like, "free", "0 FLOPs")
|
|
275
|
+
|
|
276
|
+
|
|
277
|
+
def ones_like(
|
|
278
|
+
a: ArrayLike,
|
|
279
|
+
dtype: DTypeLike | None = None,
|
|
280
|
+
**kwargs: Any,
|
|
281
|
+
) -> FlopscopeArray:
|
|
282
|
+
"""Return array of ones with same shape. Wraps ``numpy.ones_like``. Cost: 0 FLOPs."""
|
|
283
|
+
result = _np.ones_like(_to_base_ndarray(a), dtype=dtype, **kwargs)
|
|
284
|
+
propagated_symmetry = None
|
|
285
|
+
if isinstance(a, SymmetricTensor):
|
|
286
|
+
propagated_symmetry = _compatible_symmetry_for_shape(a.symmetry, result.shape)
|
|
287
|
+
if propagated_symmetry is not None:
|
|
288
|
+
return wrap_with_trusted_symmetry(result, propagated_symmetry) # type: ignore[return-value]
|
|
289
|
+
inferred_symmetry = _infer_constant_shape_symmetry(result.shape)
|
|
290
|
+
if inferred_symmetry is None:
|
|
291
|
+
if isinstance(a, SymmetricTensor):
|
|
292
|
+
return _np.array(result, copy=False, subok=False) # type: ignore[return-value]
|
|
293
|
+
return result # type: ignore[return-value]
|
|
294
|
+
return wrap_with_inferred_symmetry(result, inferred_symmetry) # type: ignore[return-value]
|
|
295
|
+
|
|
296
|
+
|
|
297
|
+
attach_docstring(ones_like, _np.ones_like, "free", "0 FLOPs")
|
|
298
|
+
|
|
299
|
+
|
|
300
|
+
@_counted_wrapper
|
|
301
|
+
def full_like(
|
|
302
|
+
a: ArrayLike,
|
|
303
|
+
fill_value: ArrayLike,
|
|
304
|
+
dtype: DTypeLike | None = None,
|
|
305
|
+
**kwargs: Any,
|
|
306
|
+
) -> FlopscopeArray:
|
|
307
|
+
"""Return full array with same shape. Cost: numel(output)."""
|
|
308
|
+
budget = require_budget()
|
|
309
|
+
a_arr = _np.asarray(a)
|
|
310
|
+
cost = max(a_arr.size, 1)
|
|
311
|
+
with budget.deduct("full_like", flop_cost=cost, subscripts=None, shapes=()):
|
|
312
|
+
result = _call_numpy(
|
|
313
|
+
_np.full_like, _to_base_ndarray(a), fill_value, dtype=dtype, **kwargs
|
|
314
|
+
)
|
|
315
|
+
propagated_symmetry = None
|
|
316
|
+
if isinstance(a, SymmetricTensor):
|
|
317
|
+
propagated_symmetry = _compatible_symmetry_for_shape(a.symmetry, result.shape)
|
|
318
|
+
if propagated_symmetry is not None:
|
|
319
|
+
return wrap_with_trusted_symmetry(result, propagated_symmetry) # type: ignore[return-value]
|
|
320
|
+
inferred_symmetry = _infer_constant_shape_symmetry(result.shape)
|
|
321
|
+
if inferred_symmetry is None:
|
|
322
|
+
if isinstance(a, SymmetricTensor):
|
|
323
|
+
return _np.array(result, copy=False, subok=False) # type: ignore[return-value]
|
|
324
|
+
return result # type: ignore[return-value]
|
|
325
|
+
return wrap_with_inferred_symmetry(result, inferred_symmetry) # type: ignore[return-value]
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
attach_docstring(full_like, _np.full_like, "free", "0 FLOPs")
|
|
329
|
+
|
|
330
|
+
|
|
331
|
+
def empty(
|
|
332
|
+
shape: int | Sequence[int],
|
|
333
|
+
dtype: DTypeLike = float,
|
|
334
|
+
**kwargs: Any,
|
|
335
|
+
) -> FlopscopeArray:
|
|
336
|
+
"""Return uninitialized array. Wraps ``numpy.empty``. Cost: 0 FLOPs."""
|
|
337
|
+
return _np.empty(shape, dtype=dtype, **kwargs) # type: ignore[return-value]
|
|
338
|
+
|
|
339
|
+
|
|
340
|
+
attach_docstring(empty, _np.empty, "free", "0 FLOPs")
|
|
341
|
+
|
|
342
|
+
|
|
343
|
+
def empty_like(
|
|
344
|
+
a: ArrayLike,
|
|
345
|
+
dtype: DTypeLike | None = None,
|
|
346
|
+
**kwargs: Any,
|
|
347
|
+
) -> FlopscopeArray:
|
|
348
|
+
"""Return uninitialized array with same shape. Wraps ``numpy.empty_like``. Cost: 0 FLOPs."""
|
|
349
|
+
return _np.empty_like(_to_base_ndarray(a), dtype=dtype, **kwargs) # type: ignore[return-value]
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
attach_docstring(empty_like, _np.empty_like, "free", "0 FLOPs")
|
|
353
|
+
|
|
354
|
+
|
|
355
|
+
def identity(n: int, dtype: DTypeLike = float) -> FlopscopeArray:
|
|
356
|
+
"""Return identity matrix. Wraps ``numpy.identity``. Cost: 0 FLOPs."""
|
|
357
|
+
result = _np.identity(n, dtype=dtype)
|
|
358
|
+
symmetry = _infer_structural_constructor_symmetry(kind="identity")
|
|
359
|
+
if symmetry is not None:
|
|
360
|
+
return wrap_with_trusted_symmetry(result, symmetry) # type: ignore[return-value]
|
|
361
|
+
return result # type: ignore[return-value]
|
|
362
|
+
|
|
363
|
+
|
|
364
|
+
attach_docstring(identity, _np.identity, "free", "0 FLOPs")
|
|
365
|
+
|
|
366
|
+
# ---------------------------------------------------------------------------
|
|
367
|
+
# Tensor manipulation
|
|
368
|
+
# ---------------------------------------------------------------------------
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
def reshape(a: ArrayLike, /, *args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
372
|
+
"""Reshape an array. Wraps ``numpy.reshape``. Cost: 0 FLOPs."""
|
|
373
|
+
a_arr = _np.asarray(a)
|
|
374
|
+
result = _np.reshape(a_arr, *args, **kwargs)
|
|
375
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
376
|
+
out_group = _st.transport_reshape(
|
|
377
|
+
in_group,
|
|
378
|
+
input_shape=a_arr.shape,
|
|
379
|
+
output_shape=result.shape,
|
|
380
|
+
)
|
|
381
|
+
if in_group is not None and out_group is None:
|
|
382
|
+
_warn_symmetry_loss(
|
|
383
|
+
lost_dims=[
|
|
384
|
+
in_group.axes
|
|
385
|
+
if in_group.axes is not None
|
|
386
|
+
else tuple(range(in_group.degree))
|
|
387
|
+
],
|
|
388
|
+
reason="reshape merges or splits axes inside the symmetric block",
|
|
389
|
+
)
|
|
390
|
+
if out_group is not None:
|
|
391
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
392
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
393
|
+
|
|
394
|
+
|
|
395
|
+
attach_docstring(reshape, _np.reshape, "free", "0 FLOPs")
|
|
396
|
+
|
|
397
|
+
|
|
398
|
+
def transpose(
|
|
399
|
+
a: ArrayLike,
|
|
400
|
+
axes: Sequence[int] | None = None,
|
|
401
|
+
) -> FlopscopeArray:
|
|
402
|
+
"""Permute array dimensions. Wraps ``numpy.transpose``. Cost: 0 FLOPs."""
|
|
403
|
+
a_arr = _np.asarray(a)
|
|
404
|
+
result = _np.transpose(a_arr, axes=axes)
|
|
405
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
406
|
+
out_group = _st.transport_transpose(in_group, ndim=a_arr.ndim, axes=axes)
|
|
407
|
+
# transpose never genuinely drops (axis perm always preserves S_n etc.).
|
|
408
|
+
if out_group is not None:
|
|
409
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
410
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
411
|
+
|
|
412
|
+
|
|
413
|
+
attach_docstring(transpose, _np.transpose, "free", "0 FLOPs")
|
|
414
|
+
|
|
415
|
+
|
|
416
|
+
def swapaxes(a: ArrayLike, axis1: int, axis2: int) -> FlopscopeArray:
|
|
417
|
+
"""Swap two axes. Wraps ``numpy.swapaxes``. Cost: 0 FLOPs."""
|
|
418
|
+
a_arr = _np.asarray(a)
|
|
419
|
+
result = _np.swapaxes(a_arr, axis1, axis2)
|
|
420
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
421
|
+
out_group = _st.transport_swapaxes(
|
|
422
|
+
in_group,
|
|
423
|
+
ndim=a_arr.ndim,
|
|
424
|
+
axis1=axis1,
|
|
425
|
+
axis2=axis2,
|
|
426
|
+
)
|
|
427
|
+
if out_group is not None:
|
|
428
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
429
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
430
|
+
|
|
431
|
+
|
|
432
|
+
attach_docstring(swapaxes, _np.swapaxes, "free", "0 FLOPs")
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
def moveaxis(
|
|
436
|
+
a: ArrayLike,
|
|
437
|
+
source,
|
|
438
|
+
destination,
|
|
439
|
+
) -> FlopscopeArray:
|
|
440
|
+
"""Move axes to new positions. Wraps ``numpy.moveaxis``. Cost: 0 FLOPs."""
|
|
441
|
+
a_arr = _np.asarray(a)
|
|
442
|
+
result = _np.moveaxis(a_arr, source, destination)
|
|
443
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
444
|
+
out_group = _st.transport_moveaxis(
|
|
445
|
+
in_group,
|
|
446
|
+
ndim=a_arr.ndim,
|
|
447
|
+
source=source,
|
|
448
|
+
destination=destination,
|
|
449
|
+
)
|
|
450
|
+
if out_group is not None:
|
|
451
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
452
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
453
|
+
|
|
454
|
+
|
|
455
|
+
attach_docstring(moveaxis, _np.moveaxis, "free", "0 FLOPs")
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
@_counted_wrapper
|
|
459
|
+
def concatenate(
|
|
460
|
+
arrays: Sequence[ArrayLike],
|
|
461
|
+
axis: int | None = 0,
|
|
462
|
+
**kwargs: Any,
|
|
463
|
+
) -> FlopscopeArray:
|
|
464
|
+
"""Join arrays along an axis. Cost: numel(output)."""
|
|
465
|
+
budget = require_budget()
|
|
466
|
+
arr_list = [_np.asarray(a) for a in arrays]
|
|
467
|
+
cost = max(sum(a.size for a in arr_list), 1)
|
|
468
|
+
groups = [(a.symmetry if isinstance(a, SymmetricTensor) else None) for a in arrays]
|
|
469
|
+
raw_arrs = [_to_base_ndarray(a) for a in arrays]
|
|
470
|
+
with budget.deduct("concatenate", flop_cost=cost, subscripts=None, shapes=()):
|
|
471
|
+
result = _call_numpy(_np.concatenate, raw_arrs, axis=axis, **kwargs)
|
|
472
|
+
out_group = _st.transport_concatenate(
|
|
473
|
+
groups,
|
|
474
|
+
output_ndim=result.ndim,
|
|
475
|
+
axis=axis,
|
|
476
|
+
)
|
|
477
|
+
if any(g is not None for g in groups) and out_group is None:
|
|
478
|
+
_warn_symmetry_loss(
|
|
479
|
+
lost_dims=[
|
|
480
|
+
g.axes if g.axes is not None else tuple(range(g.degree))
|
|
481
|
+
for g in groups
|
|
482
|
+
if g is not None
|
|
483
|
+
],
|
|
484
|
+
reason="concatenate breaks block symmetry or mixes with plain inputs",
|
|
485
|
+
)
|
|
486
|
+
if out_group is not None:
|
|
487
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
488
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
489
|
+
|
|
490
|
+
|
|
491
|
+
attach_docstring(concatenate, _np.concatenate, "counted_custom", "numel(output) FLOPs")
|
|
492
|
+
|
|
493
|
+
|
|
494
|
+
@_counted_wrapper
|
|
495
|
+
def stack(
|
|
496
|
+
arrays: Sequence[ArrayLike],
|
|
497
|
+
axis: int = 0,
|
|
498
|
+
**kwargs: Any,
|
|
499
|
+
) -> FlopscopeArray:
|
|
500
|
+
"""Stack arrays along a new axis. Cost: numel(output)."""
|
|
501
|
+
budget = require_budget()
|
|
502
|
+
arr_list = [_np.asarray(a) for a in arrays]
|
|
503
|
+
cost = max(sum(a.size for a in arr_list), 1)
|
|
504
|
+
groups = [a.symmetry if isinstance(a, SymmetricTensor) else None for a in arrays]
|
|
505
|
+
with budget.deduct("stack", flop_cost=cost, subscripts=None, shapes=()):
|
|
506
|
+
result = _call_numpy(
|
|
507
|
+
_np.stack, _to_base_ndarray_tree(arrays), axis=axis, **kwargs
|
|
508
|
+
)
|
|
509
|
+
out_group = _st.transport_stack(groups, output_ndim=result.ndim, axis=axis)
|
|
510
|
+
if any(g is not None for g in groups) and out_group is None:
|
|
511
|
+
_warn_symmetry_loss(
|
|
512
|
+
lost_dims=[
|
|
513
|
+
g.axes if g.axes is not None else tuple(range(g.degree))
|
|
514
|
+
for g in groups
|
|
515
|
+
if g is not None
|
|
516
|
+
],
|
|
517
|
+
reason="stack inputs disagree or include plain arrays",
|
|
518
|
+
)
|
|
519
|
+
if out_group is not None:
|
|
520
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
521
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
522
|
+
|
|
523
|
+
|
|
524
|
+
attach_docstring(stack, _np.stack, "free", "0 FLOPs")
|
|
525
|
+
|
|
526
|
+
|
|
527
|
+
@_counted_wrapper
|
|
528
|
+
def vstack(tup: Sequence[ArrayLike]) -> FlopscopeArray:
|
|
529
|
+
"""Stack arrays vertically. Cost: numel(output)."""
|
|
530
|
+
budget = require_budget()
|
|
531
|
+
arr_list = [_np.asarray(a) for a in tup]
|
|
532
|
+
cost = max(sum(a.size for a in arr_list), 1)
|
|
533
|
+
groups = [a.symmetry if isinstance(a, SymmetricTensor) else None for a in tup]
|
|
534
|
+
input_ndims = [a.ndim for a in arr_list]
|
|
535
|
+
with budget.deduct("vstack", flop_cost=cost, subscripts=None, shapes=()):
|
|
536
|
+
result = _call_numpy(_np.vstack, _to_base_ndarray_tree(tup)) # type: ignore[arg-type, call-overload]
|
|
537
|
+
out_group = _st.transport_vstack(
|
|
538
|
+
groups,
|
|
539
|
+
output_ndim=result.ndim,
|
|
540
|
+
input_ndims=input_ndims,
|
|
541
|
+
)
|
|
542
|
+
if any(g is not None for g in groups) and out_group is None:
|
|
543
|
+
_warn_symmetry_loss(
|
|
544
|
+
lost_dims=[
|
|
545
|
+
g.axes if g.axes is not None else tuple(range(g.degree))
|
|
546
|
+
for g in groups
|
|
547
|
+
if g is not None
|
|
548
|
+
],
|
|
549
|
+
reason="vstack breaks block symmetry",
|
|
550
|
+
)
|
|
551
|
+
if out_group is not None:
|
|
552
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
553
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
554
|
+
|
|
555
|
+
|
|
556
|
+
attach_docstring(vstack, _np.vstack, "free", "0 FLOPs")
|
|
557
|
+
|
|
558
|
+
|
|
559
|
+
def hstack(tup: Sequence[ArrayLike]) -> FlopscopeArray:
|
|
560
|
+
"""Stack arrays horizontally. Wraps ``numpy.hstack``. Cost: 0 FLOPs."""
|
|
561
|
+
arr_list = [_np.asarray(a) for a in tup]
|
|
562
|
+
groups = [a.symmetry if isinstance(a, SymmetricTensor) else None for a in tup]
|
|
563
|
+
input_ndims = [a.ndim for a in arr_list]
|
|
564
|
+
result = _np.hstack(_to_base_ndarray_tree(tup)) # type: ignore[arg-type, call-overload]
|
|
565
|
+
out_group = _st.transport_hstack(
|
|
566
|
+
groups,
|
|
567
|
+
output_ndim=result.ndim,
|
|
568
|
+
input_ndims=input_ndims,
|
|
569
|
+
)
|
|
570
|
+
if any(g is not None for g in groups) and out_group is None:
|
|
571
|
+
_warn_symmetry_loss(
|
|
572
|
+
lost_dims=[
|
|
573
|
+
g.axes if g.axes is not None else tuple(range(g.degree))
|
|
574
|
+
for g in groups
|
|
575
|
+
if g is not None
|
|
576
|
+
],
|
|
577
|
+
reason="hstack breaks block symmetry",
|
|
578
|
+
)
|
|
579
|
+
if out_group is not None:
|
|
580
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
581
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
582
|
+
|
|
583
|
+
|
|
584
|
+
attach_docstring(hstack, _np.hstack, "free", "0 FLOPs")
|
|
585
|
+
|
|
586
|
+
|
|
587
|
+
@_counted_wrapper
|
|
588
|
+
def split(
|
|
589
|
+
ary: ArrayLike,
|
|
590
|
+
indices_or_sections: int | Sequence[int],
|
|
591
|
+
axis: int = 0,
|
|
592
|
+
) -> list[FlopscopeArray]:
|
|
593
|
+
"""Split array. Cost: numel(input)."""
|
|
594
|
+
budget = require_budget()
|
|
595
|
+
ary_arr = _np.asarray(ary)
|
|
596
|
+
cost = ary_arr.size
|
|
597
|
+
in_group = ary.symmetry if isinstance(ary, SymmetricTensor) else None
|
|
598
|
+
out_group = _st.transport_split(in_group, input_shape=ary_arr.shape, axis=axis)
|
|
599
|
+
if in_group is not None and out_group is None:
|
|
600
|
+
_warn_symmetry_loss(
|
|
601
|
+
lost_dims=[
|
|
602
|
+
in_group.axes
|
|
603
|
+
if in_group.axes is not None
|
|
604
|
+
else tuple(range(in_group.degree))
|
|
605
|
+
],
|
|
606
|
+
reason=f"split along axis {axis} breaks block symmetry",
|
|
607
|
+
)
|
|
608
|
+
with budget.deduct(
|
|
609
|
+
"split", flop_cost=cost, subscripts=None, shapes=(ary_arr.shape,)
|
|
610
|
+
):
|
|
611
|
+
raw_pieces = _call_numpy(
|
|
612
|
+
_np.split,
|
|
613
|
+
ary_arr,
|
|
614
|
+
indices_or_sections,
|
|
615
|
+
axis=axis,
|
|
616
|
+
)
|
|
617
|
+
if out_group is not None:
|
|
618
|
+
return [wrap_with_symmetry(p, out_group) for p in raw_pieces] # type: ignore[return-value]
|
|
619
|
+
return [_asplainflopscope(p) for p in raw_pieces] # type: ignore[return-value]
|
|
620
|
+
|
|
621
|
+
|
|
622
|
+
attach_docstring(split, _np.split, "free", "0 FLOPs")
|
|
623
|
+
|
|
624
|
+
|
|
625
|
+
def hsplit(
|
|
626
|
+
ary: ArrayLike,
|
|
627
|
+
indices_or_sections: int | Sequence[int],
|
|
628
|
+
) -> list[FlopscopeArray]:
|
|
629
|
+
"""Split array horizontally. Wraps ``numpy.hsplit``. Cost: 0 FLOPs."""
|
|
630
|
+
ary_arr = _np.asarray(ary)
|
|
631
|
+
in_group = ary.symmetry if isinstance(ary, SymmetricTensor) else None
|
|
632
|
+
out_group = _st.transport_hsplit(in_group, input_shape=ary_arr.shape)
|
|
633
|
+
raw_pieces = _np.hsplit(ary_arr, indices_or_sections)
|
|
634
|
+
if in_group is not None and out_group is None:
|
|
635
|
+
_warn_symmetry_loss(
|
|
636
|
+
lost_dims=[
|
|
637
|
+
in_group.axes
|
|
638
|
+
if in_group.axes is not None
|
|
639
|
+
else tuple(range(in_group.degree))
|
|
640
|
+
],
|
|
641
|
+
reason="hsplit breaks block symmetry",
|
|
642
|
+
)
|
|
643
|
+
if out_group is not None:
|
|
644
|
+
return [wrap_with_symmetry(p, out_group) for p in raw_pieces] # type: ignore[return-value]
|
|
645
|
+
return [_asplainflopscope(p) for p in raw_pieces] # type: ignore[return-value]
|
|
646
|
+
|
|
647
|
+
|
|
648
|
+
attach_docstring(hsplit, _np.hsplit, "free", "0 FLOPs")
|
|
649
|
+
|
|
650
|
+
|
|
651
|
+
@_counted_wrapper
|
|
652
|
+
def vsplit(
|
|
653
|
+
ary: ArrayLike,
|
|
654
|
+
indices_or_sections: int | Sequence[int],
|
|
655
|
+
) -> list[FlopscopeArray]:
|
|
656
|
+
"""Split array vertically. Cost: numel(input)."""
|
|
657
|
+
budget = require_budget()
|
|
658
|
+
ary_arr = _np.asarray(ary)
|
|
659
|
+
cost = ary_arr.size
|
|
660
|
+
in_group = ary.symmetry if isinstance(ary, SymmetricTensor) else None
|
|
661
|
+
out_group = _st.transport_vsplit(in_group, input_shape=ary_arr.shape)
|
|
662
|
+
if in_group is not None and out_group is None:
|
|
663
|
+
_warn_symmetry_loss(
|
|
664
|
+
lost_dims=[
|
|
665
|
+
in_group.axes
|
|
666
|
+
if in_group.axes is not None
|
|
667
|
+
else tuple(range(in_group.degree))
|
|
668
|
+
],
|
|
669
|
+
reason="vsplit breaks block symmetry",
|
|
670
|
+
)
|
|
671
|
+
with budget.deduct(
|
|
672
|
+
"vsplit", flop_cost=cost, subscripts=None, shapes=(ary_arr.shape,)
|
|
673
|
+
):
|
|
674
|
+
raw_pieces = _call_numpy(_np.vsplit, ary_arr, indices_or_sections)
|
|
675
|
+
if out_group is not None:
|
|
676
|
+
return [wrap_with_symmetry(p, out_group) for p in raw_pieces] # type: ignore[return-value]
|
|
677
|
+
return [_asplainflopscope(p) for p in raw_pieces] # type: ignore[return-value]
|
|
678
|
+
|
|
679
|
+
|
|
680
|
+
attach_docstring(vsplit, _np.vsplit, "free", "0 FLOPs")
|
|
681
|
+
|
|
682
|
+
|
|
683
|
+
def squeeze(
|
|
684
|
+
a: ArrayLike,
|
|
685
|
+
axis: int | tuple[int, ...] | None = None,
|
|
686
|
+
) -> FlopscopeArray:
|
|
687
|
+
"""Remove length-1 axes. Wraps ``numpy.squeeze``. Cost: 0 FLOPs."""
|
|
688
|
+
a_arr = _np.asarray(a)
|
|
689
|
+
result = _np.squeeze(a_arr, axis=axis)
|
|
690
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
691
|
+
out_group = _st.transport_squeeze(in_group, input_shape=a_arr.shape, axis=axis)
|
|
692
|
+
if in_group is not None and out_group is None:
|
|
693
|
+
_warn_symmetry_loss(
|
|
694
|
+
lost_dims=[
|
|
695
|
+
in_group.axes
|
|
696
|
+
if in_group.axes is not None
|
|
697
|
+
else tuple(range(in_group.degree))
|
|
698
|
+
],
|
|
699
|
+
reason="squeeze removes an axis inside the symmetric block",
|
|
700
|
+
)
|
|
701
|
+
if out_group is not None:
|
|
702
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
703
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
704
|
+
|
|
705
|
+
|
|
706
|
+
attach_docstring(squeeze, _np.squeeze, "free", "0 FLOPs")
|
|
707
|
+
|
|
708
|
+
|
|
709
|
+
def expand_dims(a: ArrayLike, axis) -> FlopscopeArray:
|
|
710
|
+
"""Insert a new axis. Wraps ``numpy.expand_dims``. Cost: 0 FLOPs."""
|
|
711
|
+
a_arr = _np.asarray(a)
|
|
712
|
+
result = _np.expand_dims(a_arr, axis=axis)
|
|
713
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
714
|
+
out_group = _st.transport_expand_dims(
|
|
715
|
+
in_group,
|
|
716
|
+
input_ndim=a_arr.ndim,
|
|
717
|
+
axis=axis,
|
|
718
|
+
)
|
|
719
|
+
if out_group is not None:
|
|
720
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
721
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
722
|
+
|
|
723
|
+
|
|
724
|
+
attach_docstring(expand_dims, _np.expand_dims, "free", "0 FLOPs")
|
|
725
|
+
|
|
726
|
+
|
|
727
|
+
@_counted_wrapper
|
|
728
|
+
def ravel(a: ArrayLike, **kwargs: Any) -> FlopscopeArray:
|
|
729
|
+
"""Flatten array. Cost: numel(output)."""
|
|
730
|
+
budget = require_budget()
|
|
731
|
+
a_arr = _np.asarray(a)
|
|
732
|
+
cost = max(a_arr.size, 1)
|
|
733
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
734
|
+
out_group = _st.transport_ravel(in_group, input_shape=a_arr.shape)
|
|
735
|
+
with budget.deduct("ravel", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)):
|
|
736
|
+
result = _call_numpy(_np.ravel, a_arr, **kwargs)
|
|
737
|
+
if in_group is not None and out_group is None:
|
|
738
|
+
_warn_symmetry_loss(
|
|
739
|
+
lost_dims=[
|
|
740
|
+
in_group.axes
|
|
741
|
+
if in_group.axes is not None
|
|
742
|
+
else tuple(range(in_group.degree))
|
|
743
|
+
],
|
|
744
|
+
reason="ravel collapses to a single axis; block cannot fit",
|
|
745
|
+
)
|
|
746
|
+
if out_group is not None:
|
|
747
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
748
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
749
|
+
|
|
750
|
+
|
|
751
|
+
attach_docstring(ravel, _np.ravel, "free", "0 FLOPs")
|
|
752
|
+
|
|
753
|
+
|
|
754
|
+
def copy(a: ArrayLike, **kwargs: Any) -> FlopscopeArray:
|
|
755
|
+
"""Return copy of array. Wraps ``numpy.copy``. Cost: 0 FLOPs."""
|
|
756
|
+
result = _np.copy(_np.asarray(a), **kwargs)
|
|
757
|
+
if isinstance(a, SymmetricTensor):
|
|
758
|
+
return wrap_with_symmetry(result, a.symmetry) # type: ignore[return-value]
|
|
759
|
+
return result # type: ignore[return-value]
|
|
760
|
+
|
|
761
|
+
|
|
762
|
+
attach_docstring(copy, _np.copy, "free", "0 FLOPs")
|
|
763
|
+
|
|
764
|
+
|
|
765
|
+
@_counted_wrapper
|
|
766
|
+
def where(
|
|
767
|
+
condition: ArrayLike,
|
|
768
|
+
x: ArrayLike | None = None,
|
|
769
|
+
y: ArrayLike | None = None,
|
|
770
|
+
) -> FlopscopeArray | tuple[FlopscopeArray, ...]:
|
|
771
|
+
"""Return elements chosen from *x* or *y*. Cost: numel(input)."""
|
|
772
|
+
budget = require_budget()
|
|
773
|
+
cond_arr = _np.asarray(condition)
|
|
774
|
+
cost = cond_arr.size
|
|
775
|
+
with budget.deduct(
|
|
776
|
+
"where", flop_cost=cost, subscripts=None, shapes=(cond_arr.shape,)
|
|
777
|
+
):
|
|
778
|
+
if x is None and y is None:
|
|
779
|
+
result = _call_numpy(_np.where, _to_base_ndarray(condition))
|
|
780
|
+
else:
|
|
781
|
+
result = _call_numpy(
|
|
782
|
+
_np.where,
|
|
783
|
+
_to_base_ndarray(condition),
|
|
784
|
+
_to_base_ndarray(x), # type: ignore[arg-type, call-overload]
|
|
785
|
+
_to_base_ndarray(y), # type: ignore[arg-type, call-overload]
|
|
786
|
+
)
|
|
787
|
+
return result # type: ignore[return-value]
|
|
788
|
+
|
|
789
|
+
|
|
790
|
+
attach_docstring(where, _np.where, "free", "0 FLOPs")
|
|
791
|
+
|
|
792
|
+
|
|
793
|
+
@_counted_wrapper
|
|
794
|
+
def tile(A: ArrayLike, reps: int | Sequence[int]) -> FlopscopeArray:
|
|
795
|
+
"""Construct array by repeating. Cost: numel(output)."""
|
|
796
|
+
budget = require_budget()
|
|
797
|
+
a_arr = _np.asarray(A)
|
|
798
|
+
result = _np.tile(a_arr, reps)
|
|
799
|
+
cost = max(result.size, 1)
|
|
800
|
+
in_group = A.symmetry if isinstance(A, SymmetricTensor) else None
|
|
801
|
+
out_group = _st.transport_tile(
|
|
802
|
+
in_group,
|
|
803
|
+
input_shape=a_arr.shape,
|
|
804
|
+
output_shape=result.shape,
|
|
805
|
+
reps=reps,
|
|
806
|
+
)
|
|
807
|
+
with budget.deduct("tile", flop_cost=cost, subscripts=None, shapes=()):
|
|
808
|
+
pass # cost deducted; result already computed
|
|
809
|
+
if in_group is not None and out_group is None:
|
|
810
|
+
_warn_symmetry_loss(
|
|
811
|
+
lost_dims=[
|
|
812
|
+
in_group.axes
|
|
813
|
+
if in_group.axes is not None
|
|
814
|
+
else tuple(range(in_group.degree))
|
|
815
|
+
],
|
|
816
|
+
reason="tile reps not constant on block orbit",
|
|
817
|
+
)
|
|
818
|
+
if out_group is not None:
|
|
819
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
820
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
821
|
+
|
|
822
|
+
|
|
823
|
+
attach_docstring(tile, _np.tile, "free", "0 FLOPs")
|
|
824
|
+
|
|
825
|
+
|
|
826
|
+
@_counted_wrapper
|
|
827
|
+
def repeat(
|
|
828
|
+
a: ArrayLike,
|
|
829
|
+
repeats: int | ArrayLike,
|
|
830
|
+
axis: int | None = None,
|
|
831
|
+
) -> FlopscopeArray:
|
|
832
|
+
"""Repeat elements. Cost: numel(output)."""
|
|
833
|
+
budget = require_budget()
|
|
834
|
+
a_arr = _np.asarray(a)
|
|
835
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
836
|
+
out_group = _st.transport_repeat(in_group, input_shape=a_arr.shape, axis=axis)
|
|
837
|
+
result = _np.repeat(a_arr, repeats, axis=axis) # type: ignore[arg-type]
|
|
838
|
+
cost = max(result.size, 1)
|
|
839
|
+
with budget.deduct(
|
|
840
|
+
"repeat", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
841
|
+
):
|
|
842
|
+
pass # cost deducted; result already computed
|
|
843
|
+
if in_group is not None and out_group is None:
|
|
844
|
+
_warn_symmetry_loss(
|
|
845
|
+
lost_dims=[
|
|
846
|
+
in_group.axes
|
|
847
|
+
if in_group.axes is not None
|
|
848
|
+
else tuple(range(in_group.degree))
|
|
849
|
+
],
|
|
850
|
+
reason="repeat along a block axis breaks block symmetry",
|
|
851
|
+
)
|
|
852
|
+
if out_group is not None:
|
|
853
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
854
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
855
|
+
|
|
856
|
+
|
|
857
|
+
attach_docstring(repeat, _np.repeat, "free", "0 FLOPs")
|
|
858
|
+
|
|
859
|
+
|
|
860
|
+
def flip(
|
|
861
|
+
m: ArrayLike,
|
|
862
|
+
axis: int | tuple[int, ...] | None = None,
|
|
863
|
+
) -> FlopscopeArray:
|
|
864
|
+
"""Reverse order of elements. Wraps ``numpy.flip``. Cost: 0 FLOPs."""
|
|
865
|
+
a_arr = _np.asarray(m)
|
|
866
|
+
result = _np.flip(a_arr, axis=axis)
|
|
867
|
+
in_group = m.symmetry if isinstance(m, SymmetricTensor) else None
|
|
868
|
+
out_group = _st.transport_flip(
|
|
869
|
+
in_group,
|
|
870
|
+
ndim=a_arr.ndim,
|
|
871
|
+
axes_flipped=axis,
|
|
872
|
+
)
|
|
873
|
+
if in_group is not None and out_group is None:
|
|
874
|
+
_warn_symmetry_loss(
|
|
875
|
+
lost_dims=[
|
|
876
|
+
in_group.axes
|
|
877
|
+
if in_group.axes is not None
|
|
878
|
+
else tuple(range(in_group.degree))
|
|
879
|
+
],
|
|
880
|
+
reason="flip on a proper subset of block axes breaks group action",
|
|
881
|
+
)
|
|
882
|
+
if out_group is not None:
|
|
883
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
884
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
885
|
+
|
|
886
|
+
|
|
887
|
+
attach_docstring(flip, _np.flip, "free", "0 FLOPs")
|
|
888
|
+
|
|
889
|
+
|
|
890
|
+
def roll(
|
|
891
|
+
a: ArrayLike,
|
|
892
|
+
shift: int | Sequence[int],
|
|
893
|
+
axis: int | Sequence[int] | None = None,
|
|
894
|
+
) -> FlopscopeArray:
|
|
895
|
+
"""Roll array elements along an axis. Wraps ``numpy.roll``. Cost: 0 FLOPs."""
|
|
896
|
+
a_arr = _np.asarray(a)
|
|
897
|
+
result = _np.roll(a_arr, shift, axis=axis)
|
|
898
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
899
|
+
out_group = _st.transport_roll(in_group, input_shape=a_arr.shape, axis=axis)
|
|
900
|
+
if in_group is not None and out_group is None:
|
|
901
|
+
_warn_symmetry_loss(
|
|
902
|
+
lost_dims=[
|
|
903
|
+
in_group.axes
|
|
904
|
+
if in_group.axes is not None
|
|
905
|
+
else tuple(range(in_group.degree))
|
|
906
|
+
],
|
|
907
|
+
reason="roll along a block axis breaks block symmetry",
|
|
908
|
+
)
|
|
909
|
+
if out_group is not None:
|
|
910
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
911
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
912
|
+
|
|
913
|
+
|
|
914
|
+
attach_docstring(roll, _np.roll, "free", "0 FLOPs")
|
|
915
|
+
|
|
916
|
+
|
|
917
|
+
@_counted_wrapper
|
|
918
|
+
def pad(array: ArrayLike, pad_width: Any, **kwargs: Any) -> FlopscopeArray:
|
|
919
|
+
"""Pad an array. Cost: numel(output)."""
|
|
920
|
+
budget = require_budget()
|
|
921
|
+
_warn_if_symmetric(array, "pad")
|
|
922
|
+
# cost depends on result; duration is post-hoc
|
|
923
|
+
# pad_width parsing is complex (scalar, per-axis, per-side) — not worth replicating
|
|
924
|
+
result = _np.pad(_to_base_ndarray(array), pad_width, **kwargs)
|
|
925
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
926
|
+
with budget.deduct("pad", flop_cost=cost, subscripts=None, shapes=()):
|
|
927
|
+
pass
|
|
928
|
+
return result # type: ignore[return-value]
|
|
929
|
+
|
|
930
|
+
|
|
931
|
+
attach_docstring(pad, _np.pad, "free", "0 FLOPs")
|
|
932
|
+
|
|
933
|
+
|
|
934
|
+
def triu(m: ArrayLike, k: int = 0) -> FlopscopeArray:
|
|
935
|
+
"""Upper triangle. Wraps ``numpy.triu``. Cost: 0 FLOPs."""
|
|
936
|
+
_warn_if_symmetric(m, "triu")
|
|
937
|
+
return _np.triu(_to_base_ndarray(m), k=k) # type: ignore[return-value]
|
|
938
|
+
|
|
939
|
+
|
|
940
|
+
attach_docstring(triu, _np.triu, "free", "0 FLOPs")
|
|
941
|
+
|
|
942
|
+
|
|
943
|
+
def tril(m: ArrayLike, k: int = 0) -> FlopscopeArray:
|
|
944
|
+
"""Lower triangle. Wraps ``numpy.tril``. Cost: 0 FLOPs."""
|
|
945
|
+
_warn_if_symmetric(m, "tril")
|
|
946
|
+
return _np.tril(_to_base_ndarray(m), k=k) # type: ignore[return-value]
|
|
947
|
+
|
|
948
|
+
|
|
949
|
+
attach_docstring(tril, _np.tril, "free", "0 FLOPs")
|
|
950
|
+
|
|
951
|
+
|
|
952
|
+
@_counted_wrapper
|
|
953
|
+
def diagonal(
|
|
954
|
+
a: ArrayLike,
|
|
955
|
+
offset: int = 0,
|
|
956
|
+
axis1: int = 0,
|
|
957
|
+
axis2: int = 1,
|
|
958
|
+
) -> FlopscopeArray:
|
|
959
|
+
"""Return diagonal. Cost: numel(output)."""
|
|
960
|
+
budget = require_budget()
|
|
961
|
+
_warn_if_symmetric(a, "diagonal")
|
|
962
|
+
a_arr = _np.asarray(a)
|
|
963
|
+
# Diagonal length along axis1/axis2
|
|
964
|
+
m, n = a_arr.shape[axis1], a_arr.shape[axis2]
|
|
965
|
+
if offset >= 0:
|
|
966
|
+
diag_len = max(min(m, n - offset), 0)
|
|
967
|
+
else:
|
|
968
|
+
diag_len = max(min(m + offset, n), 0)
|
|
969
|
+
cost = max(diag_len, 1)
|
|
970
|
+
with budget.deduct(
|
|
971
|
+
"diagonal", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
972
|
+
):
|
|
973
|
+
result = _call_numpy(
|
|
974
|
+
_np.diagonal, _to_base_ndarray(a), offset=offset, axis1=axis1, axis2=axis2
|
|
975
|
+
)
|
|
976
|
+
return result # type: ignore[return-value]
|
|
977
|
+
|
|
978
|
+
|
|
979
|
+
attach_docstring(diagonal, _np.diagonal, "free", "0 FLOPs")
|
|
980
|
+
|
|
981
|
+
|
|
982
|
+
@_counted_wrapper
|
|
983
|
+
def broadcast_to(
|
|
984
|
+
array: ArrayLike,
|
|
985
|
+
shape: int | Sequence[int],
|
|
986
|
+
) -> FlopscopeArray:
|
|
987
|
+
"""Broadcast array to shape. Cost: numel(output)."""
|
|
988
|
+
output_shape = (shape,) if isinstance(shape, int) else tuple(shape)
|
|
989
|
+
arr = _np.asarray(array)
|
|
990
|
+
budget = require_budget()
|
|
991
|
+
cost = max(int(_np.prod(output_shape)), 1)
|
|
992
|
+
with budget.deduct("broadcast_to", flop_cost=cost, subscripts=None, shapes=()):
|
|
993
|
+
result = _call_numpy(_np.broadcast_to, arr, output_shape)
|
|
994
|
+
in_group = array.symmetry if isinstance(array, SymmetricTensor) else None
|
|
995
|
+
out_group = _st.transport_broadcast_to(
|
|
996
|
+
in_group,
|
|
997
|
+
input_shape=arr.shape,
|
|
998
|
+
output_shape=output_shape,
|
|
999
|
+
)
|
|
1000
|
+
if in_group is not None and out_group is None:
|
|
1001
|
+
_warn_symmetry_loss(
|
|
1002
|
+
lost_dims=[
|
|
1003
|
+
in_group.axes
|
|
1004
|
+
if in_group.axes is not None
|
|
1005
|
+
else tuple(range(in_group.degree))
|
|
1006
|
+
],
|
|
1007
|
+
reason="broadcast_to expands length-1 block axes",
|
|
1008
|
+
)
|
|
1009
|
+
if out_group is not None:
|
|
1010
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
1011
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
1012
|
+
|
|
1013
|
+
|
|
1014
|
+
attach_docstring(broadcast_to, _np.broadcast_to, "free", "0 FLOPs")
|
|
1015
|
+
|
|
1016
|
+
|
|
1017
|
+
@_counted_wrapper
|
|
1018
|
+
def meshgrid(*xi: ArrayLike, **kwargs: Any) -> tuple[FlopscopeArray, ...]:
|
|
1019
|
+
"""Return coordinate matrices. Cost: numel(output)."""
|
|
1020
|
+
budget = require_budget()
|
|
1021
|
+
# Each output grid has shape = product of all input lengths; there are len(xi) grids
|
|
1022
|
+
sizes = [_np.asarray(x).size for x in xi]
|
|
1023
|
+
grid_size = int(_np.prod(sizes)) if sizes else 0
|
|
1024
|
+
cost = max(grid_size * len(sizes), 1)
|
|
1025
|
+
with budget.deduct("meshgrid", flop_cost=cost, subscripts=None, shapes=()):
|
|
1026
|
+
result = _call_numpy(_np.meshgrid, *[_to_base_ndarray(x) for x in xi], **kwargs)
|
|
1027
|
+
return result # type: ignore[return-value]
|
|
1028
|
+
|
|
1029
|
+
|
|
1030
|
+
attach_docstring(meshgrid, _np.meshgrid, "free", "0 FLOPs")
|
|
1031
|
+
|
|
1032
|
+
# ---------------------------------------------------------------------------
|
|
1033
|
+
# Type / info helpers
|
|
1034
|
+
# ---------------------------------------------------------------------------
|
|
1035
|
+
|
|
1036
|
+
|
|
1037
|
+
def astype(
|
|
1038
|
+
x: ArrayLike,
|
|
1039
|
+
dtype: DTypeLike,
|
|
1040
|
+
/,
|
|
1041
|
+
*,
|
|
1042
|
+
copy: bool = True,
|
|
1043
|
+
device: Any = None,
|
|
1044
|
+
) -> FlopscopeArray:
|
|
1045
|
+
"""Cast array to *dtype*. Wraps ``np.astype(x, dtype)``. Cost: 0 FLOPs."""
|
|
1046
|
+
return _np.astype(_to_base_ndarray(x), dtype, copy=copy, device=device) # type: ignore[arg-type, call-overload]
|
|
1047
|
+
|
|
1048
|
+
|
|
1049
|
+
@_counted_wrapper
|
|
1050
|
+
def asarray(
|
|
1051
|
+
a: ArrayLike,
|
|
1052
|
+
dtype: DTypeLike | None = None,
|
|
1053
|
+
**kwargs: Any,
|
|
1054
|
+
) -> FlopscopeArray:
|
|
1055
|
+
"""Convert to array. Cost: numel(output)."""
|
|
1056
|
+
budget = require_budget()
|
|
1057
|
+
# Pre-compute cost; asarray on an already-array is a no-op
|
|
1058
|
+
_probe = _np.asarray(a)
|
|
1059
|
+
cost = max(_probe.size, 1)
|
|
1060
|
+
with budget.deduct(
|
|
1061
|
+
"asarray", flop_cost=cost, subscripts=None, shapes=(_probe.shape,)
|
|
1062
|
+
):
|
|
1063
|
+
result = _call_numpy(_np.asarray, a, dtype=dtype, **kwargs)
|
|
1064
|
+
return result # type: ignore[return-value]
|
|
1065
|
+
|
|
1066
|
+
|
|
1067
|
+
attach_docstring(asarray, _np.asarray, "free", "0 FLOPs")
|
|
1068
|
+
|
|
1069
|
+
|
|
1070
|
+
@_counted_wrapper
|
|
1071
|
+
def isnan(x: ArrayLike, **kwargs: Any) -> FlopscopeArray:
|
|
1072
|
+
"""Test element-wise for NaN. Cost: numel(input)."""
|
|
1073
|
+
budget = require_budget()
|
|
1074
|
+
x_arr = _np.asarray(x)
|
|
1075
|
+
cost = x_arr.size
|
|
1076
|
+
with budget.deduct("isnan", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)):
|
|
1077
|
+
# Strip flopscope subclasses so the raw NumPy ufunc does not
|
|
1078
|
+
# re-dispatch through __array_ufunc__ and recurse.
|
|
1079
|
+
result = _call_numpy(_np.isnan, _to_base_ndarray(x), **kwargs)
|
|
1080
|
+
return result # type: ignore[return-value]
|
|
1081
|
+
|
|
1082
|
+
|
|
1083
|
+
attach_docstring(isnan, _np.isnan, "free", "0 FLOPs")
|
|
1084
|
+
|
|
1085
|
+
|
|
1086
|
+
@_counted_wrapper
|
|
1087
|
+
def isfinite(x: ArrayLike, **kwargs: Any) -> FlopscopeArray:
|
|
1088
|
+
"""Test element-wise for finiteness. Cost: numel(input)."""
|
|
1089
|
+
budget = require_budget()
|
|
1090
|
+
x_arr = _np.asarray(x)
|
|
1091
|
+
cost = x_arr.size
|
|
1092
|
+
with budget.deduct(
|
|
1093
|
+
"isfinite", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)
|
|
1094
|
+
):
|
|
1095
|
+
result = _call_numpy(_np.isfinite, _to_base_ndarray(x), **kwargs)
|
|
1096
|
+
return result # type: ignore[return-value]
|
|
1097
|
+
|
|
1098
|
+
|
|
1099
|
+
attach_docstring(isfinite, _np.isfinite, "free", "0 FLOPs")
|
|
1100
|
+
|
|
1101
|
+
|
|
1102
|
+
@_counted_wrapper
|
|
1103
|
+
def isinf(x: ArrayLike, **kwargs: Any) -> FlopscopeArray:
|
|
1104
|
+
"""Test element-wise for Inf. Cost: numel(input)."""
|
|
1105
|
+
budget = require_budget()
|
|
1106
|
+
x_arr = _np.asarray(x)
|
|
1107
|
+
cost = x_arr.size
|
|
1108
|
+
with budget.deduct("isinf", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)):
|
|
1109
|
+
result = _call_numpy(_np.isinf, _to_base_ndarray(x), **kwargs)
|
|
1110
|
+
return result # type: ignore[return-value]
|
|
1111
|
+
|
|
1112
|
+
|
|
1113
|
+
attach_docstring(isinf, _np.isinf, "free", "0 FLOPs")
|
|
1114
|
+
|
|
1115
|
+
# ---------------------------------------------------------------------------
|
|
1116
|
+
# New free ops
|
|
1117
|
+
# ---------------------------------------------------------------------------
|
|
1118
|
+
|
|
1119
|
+
|
|
1120
|
+
@_counted_wrapper
|
|
1121
|
+
def append(
|
|
1122
|
+
arr: ArrayLike,
|
|
1123
|
+
values: ArrayLike,
|
|
1124
|
+
axis: int | None = None,
|
|
1125
|
+
**kwargs: Any,
|
|
1126
|
+
) -> FlopscopeArray:
|
|
1127
|
+
"""Append values. Cost: numel(appended values)."""
|
|
1128
|
+
budget = require_budget()
|
|
1129
|
+
_warn_if_symmetric(arr, "append")
|
|
1130
|
+
values_arr = _np.asarray(values)
|
|
1131
|
+
cost = values_arr.size # num appended
|
|
1132
|
+
with budget.deduct("append", flop_cost=cost, subscripts=None, shapes=()):
|
|
1133
|
+
result = _call_numpy(
|
|
1134
|
+
_np.append,
|
|
1135
|
+
_to_base_ndarray(arr),
|
|
1136
|
+
_to_base_ndarray(values),
|
|
1137
|
+
axis=axis,
|
|
1138
|
+
**kwargs,
|
|
1139
|
+
)
|
|
1140
|
+
return result # type: ignore[return-value]
|
|
1141
|
+
|
|
1142
|
+
|
|
1143
|
+
attach_docstring(append, _np.append, "free", "0 FLOPs")
|
|
1144
|
+
|
|
1145
|
+
|
|
1146
|
+
@_counted_wrapper
|
|
1147
|
+
def argwhere(a: ArrayLike, *args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
1148
|
+
"""Find indices of non-zero elements. Cost: numel(input)."""
|
|
1149
|
+
budget = require_budget()
|
|
1150
|
+
a_arr = _np.asarray(a)
|
|
1151
|
+
cost = a_arr.size
|
|
1152
|
+
with budget.deduct(
|
|
1153
|
+
"argwhere", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
1154
|
+
):
|
|
1155
|
+
result = _call_numpy(_np.argwhere, _to_base_ndarray(a), *args, **kwargs)
|
|
1156
|
+
return result # type: ignore[return-value]
|
|
1157
|
+
|
|
1158
|
+
|
|
1159
|
+
attach_docstring(argwhere, _np.argwhere, "free", "0 FLOPs")
|
|
1160
|
+
|
|
1161
|
+
|
|
1162
|
+
@_counted_wrapper
|
|
1163
|
+
def array_split(ary: ArrayLike, *args: Any, **kwargs: Any) -> list[FlopscopeArray]:
|
|
1164
|
+
"""Split array into sub-arrays. Cost: numel(input)."""
|
|
1165
|
+
budget = require_budget()
|
|
1166
|
+
_warn_if_symmetric(ary, "array_split")
|
|
1167
|
+
ary_arr = _np.asarray(ary)
|
|
1168
|
+
cost = ary_arr.size
|
|
1169
|
+
with budget.deduct(
|
|
1170
|
+
"array_split", flop_cost=cost, subscripts=None, shapes=(ary_arr.shape,)
|
|
1171
|
+
):
|
|
1172
|
+
result = _call_numpy(_np.array_split, _to_base_ndarray(ary), *args, **kwargs)
|
|
1173
|
+
return result # type: ignore[return-value]
|
|
1174
|
+
|
|
1175
|
+
|
|
1176
|
+
attach_docstring(array_split, _np.array_split, "free", "0 FLOPs")
|
|
1177
|
+
|
|
1178
|
+
|
|
1179
|
+
@_counted_wrapper
|
|
1180
|
+
def asarray_chkfinite(a: ArrayLike, *args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
1181
|
+
"""Convert to array checking for NaN/Inf. Cost: numel(output)."""
|
|
1182
|
+
budget = require_budget()
|
|
1183
|
+
result = _np.asarray_chkfinite(_to_base_ndarray(a), *args, **kwargs)
|
|
1184
|
+
cost = (
|
|
1185
|
+
result.size
|
|
1186
|
+
if hasattr(result, "size")
|
|
1187
|
+
else len(result)
|
|
1188
|
+
if hasattr(result, "__len__")
|
|
1189
|
+
else 1
|
|
1190
|
+
)
|
|
1191
|
+
with budget.deduct(
|
|
1192
|
+
"asarray_chkfinite", flop_cost=cost, subscripts=None, shapes=(result.shape,)
|
|
1193
|
+
):
|
|
1194
|
+
pass # numpy call already executed above
|
|
1195
|
+
return result # type: ignore[return-value]
|
|
1196
|
+
|
|
1197
|
+
|
|
1198
|
+
attach_docstring(asarray_chkfinite, _np.asarray_chkfinite, "free", "0 FLOPs")
|
|
1199
|
+
|
|
1200
|
+
|
|
1201
|
+
def atleast_1d(
|
|
1202
|
+
*arys: ArrayLike,
|
|
1203
|
+
) -> FlopscopeArray | tuple[FlopscopeArray, ...]:
|
|
1204
|
+
"""Convert to 1-D or higher. Wraps ``numpy.atleast_1d``. Cost: 0 FLOPs."""
|
|
1205
|
+
|
|
1206
|
+
def _one(a):
|
|
1207
|
+
a_arr = _np.asarray(a)
|
|
1208
|
+
result = _np.atleast_1d(a_arr)
|
|
1209
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
1210
|
+
out_group = _st.transport_atleast_1d(in_group, input_shape=a_arr.shape)
|
|
1211
|
+
if in_group is not None and out_group is None:
|
|
1212
|
+
_warn_symmetry_loss(
|
|
1213
|
+
lost_dims=[
|
|
1214
|
+
in_group.axes
|
|
1215
|
+
if in_group.axes is not None
|
|
1216
|
+
else tuple(range(in_group.degree))
|
|
1217
|
+
],
|
|
1218
|
+
reason="atleast_1d incompatible with block structure",
|
|
1219
|
+
)
|
|
1220
|
+
if out_group is not None:
|
|
1221
|
+
return wrap_with_symmetry(result, out_group)
|
|
1222
|
+
return _asplainflopscope(result)
|
|
1223
|
+
|
|
1224
|
+
if len(arys) == 1:
|
|
1225
|
+
return _one(arys[0]) # type: ignore[return-value]
|
|
1226
|
+
return tuple(_one(a) for a in arys) # type: ignore[return-value]
|
|
1227
|
+
|
|
1228
|
+
|
|
1229
|
+
attach_docstring(atleast_1d, _np.atleast_1d, "free", "0 FLOPs")
|
|
1230
|
+
|
|
1231
|
+
|
|
1232
|
+
def atleast_2d(
|
|
1233
|
+
*arys: ArrayLike,
|
|
1234
|
+
) -> FlopscopeArray | tuple[FlopscopeArray, ...]:
|
|
1235
|
+
"""Convert to 2-D or higher. Wraps ``numpy.atleast_2d``. Cost: 0 FLOPs."""
|
|
1236
|
+
|
|
1237
|
+
def _one(a):
|
|
1238
|
+
a_arr = _np.asarray(a)
|
|
1239
|
+
result = _np.atleast_2d(a_arr)
|
|
1240
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
1241
|
+
out_group = _st.transport_atleast_2d(in_group, input_shape=a_arr.shape)
|
|
1242
|
+
if in_group is not None and out_group is None:
|
|
1243
|
+
_warn_symmetry_loss(
|
|
1244
|
+
lost_dims=[
|
|
1245
|
+
in_group.axes
|
|
1246
|
+
if in_group.axes is not None
|
|
1247
|
+
else tuple(range(in_group.degree))
|
|
1248
|
+
],
|
|
1249
|
+
reason="atleast_2d incompatible with block structure",
|
|
1250
|
+
)
|
|
1251
|
+
if out_group is not None:
|
|
1252
|
+
return wrap_with_symmetry(result, out_group)
|
|
1253
|
+
return _asplainflopscope(result)
|
|
1254
|
+
|
|
1255
|
+
if len(arys) == 1:
|
|
1256
|
+
return _one(arys[0]) # type: ignore[return-value]
|
|
1257
|
+
return tuple(_one(a) for a in arys) # type: ignore[return-value]
|
|
1258
|
+
|
|
1259
|
+
|
|
1260
|
+
attach_docstring(atleast_2d, _np.atleast_2d, "free", "0 FLOPs")
|
|
1261
|
+
|
|
1262
|
+
|
|
1263
|
+
def atleast_3d(
|
|
1264
|
+
*arys: ArrayLike,
|
|
1265
|
+
) -> FlopscopeArray | tuple[FlopscopeArray, ...]:
|
|
1266
|
+
"""Convert to 3-D or higher. Wraps ``numpy.atleast_3d``. Cost: 0 FLOPs."""
|
|
1267
|
+
|
|
1268
|
+
def _one(a):
|
|
1269
|
+
a_arr = _np.asarray(a)
|
|
1270
|
+
result = _np.atleast_3d(a_arr)
|
|
1271
|
+
in_group = a.symmetry if isinstance(a, SymmetricTensor) else None
|
|
1272
|
+
out_group = _st.transport_atleast_3d(in_group, input_shape=a_arr.shape)
|
|
1273
|
+
if in_group is not None and out_group is None:
|
|
1274
|
+
_warn_symmetry_loss(
|
|
1275
|
+
lost_dims=[
|
|
1276
|
+
in_group.axes
|
|
1277
|
+
if in_group.axes is not None
|
|
1278
|
+
else tuple(range(in_group.degree))
|
|
1279
|
+
],
|
|
1280
|
+
reason="atleast_3d incompatible with block structure",
|
|
1281
|
+
)
|
|
1282
|
+
if out_group is not None:
|
|
1283
|
+
return wrap_with_symmetry(result, out_group)
|
|
1284
|
+
return _asplainflopscope(result)
|
|
1285
|
+
|
|
1286
|
+
if len(arys) == 1:
|
|
1287
|
+
return _one(arys[0]) # type: ignore[return-value]
|
|
1288
|
+
return tuple(_one(a) for a in arys) # type: ignore[return-value]
|
|
1289
|
+
|
|
1290
|
+
|
|
1291
|
+
attach_docstring(atleast_3d, _np.atleast_3d, "free", "0 FLOPs")
|
|
1292
|
+
|
|
1293
|
+
|
|
1294
|
+
@_counted_wrapper
|
|
1295
|
+
def base_repr(*args, **kwargs):
|
|
1296
|
+
"""Return string representation of number. Cost: numel(output)."""
|
|
1297
|
+
budget = require_budget()
|
|
1298
|
+
result = _np.base_repr(*args, **kwargs)
|
|
1299
|
+
cost = len(result)
|
|
1300
|
+
with budget.deduct("base_repr", flop_cost=cost, subscripts=None, shapes=()):
|
|
1301
|
+
pass # numpy call already executed above
|
|
1302
|
+
return result
|
|
1303
|
+
|
|
1304
|
+
|
|
1305
|
+
attach_docstring(base_repr, _np.base_repr, "free", "0 FLOPs")
|
|
1306
|
+
|
|
1307
|
+
|
|
1308
|
+
@_counted_wrapper
|
|
1309
|
+
def binary_repr(*args, **kwargs):
|
|
1310
|
+
"""Return binary representation of integer. Cost: numel(output)."""
|
|
1311
|
+
budget = require_budget()
|
|
1312
|
+
result = _np.binary_repr(*args, **kwargs)
|
|
1313
|
+
cost = len(result)
|
|
1314
|
+
with budget.deduct("binary_repr", flop_cost=cost, subscripts=None, shapes=()):
|
|
1315
|
+
pass # numpy call already executed above
|
|
1316
|
+
return result
|
|
1317
|
+
|
|
1318
|
+
|
|
1319
|
+
attach_docstring(binary_repr, _np.binary_repr, "free", "0 FLOPs")
|
|
1320
|
+
|
|
1321
|
+
|
|
1322
|
+
@_counted_wrapper
|
|
1323
|
+
def block(*args, **kwargs):
|
|
1324
|
+
"""Assemble array from nested lists. Cost: numel(output)."""
|
|
1325
|
+
budget = require_budget()
|
|
1326
|
+
|
|
1327
|
+
# Warn for any SymmetricTensor found in the nested structure.
|
|
1328
|
+
def _walk_warn(obj):
|
|
1329
|
+
if isinstance(obj, (list, tuple)):
|
|
1330
|
+
for item in obj:
|
|
1331
|
+
_walk_warn(item)
|
|
1332
|
+
else:
|
|
1333
|
+
_warn_if_symmetric(obj, "block")
|
|
1334
|
+
|
|
1335
|
+
for a in args:
|
|
1336
|
+
_walk_warn(a)
|
|
1337
|
+
result = _np.block(*[_to_base_ndarray_tree(a) for a in args], **kwargs)
|
|
1338
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1339
|
+
with budget.deduct("block", flop_cost=cost, subscripts=None, shapes=()):
|
|
1340
|
+
pass # numpy call already executed above
|
|
1341
|
+
return result
|
|
1342
|
+
|
|
1343
|
+
|
|
1344
|
+
attach_docstring(block, _np.block, "free", "0 FLOPs")
|
|
1345
|
+
|
|
1346
|
+
|
|
1347
|
+
@_counted_wrapper
|
|
1348
|
+
def bmat(*args, **kwargs):
|
|
1349
|
+
"""Build matrix from string/nested sequence. Cost: numel(output)."""
|
|
1350
|
+
budget = require_budget()
|
|
1351
|
+
# First arg may be a string OR a nested sequence of arrays
|
|
1352
|
+
stripped_args = []
|
|
1353
|
+
for arg in args:
|
|
1354
|
+
if isinstance(arg, (tuple, list)):
|
|
1355
|
+
stripped_args.append(_to_base_ndarray_tree(arg))
|
|
1356
|
+
else:
|
|
1357
|
+
stripped_args.append(arg)
|
|
1358
|
+
result = _np.bmat(*stripped_args, **kwargs)
|
|
1359
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1360
|
+
with budget.deduct("bmat", flop_cost=cost, subscripts=None, shapes=()):
|
|
1361
|
+
pass # numpy call already executed above
|
|
1362
|
+
return result
|
|
1363
|
+
|
|
1364
|
+
|
|
1365
|
+
attach_docstring(bmat, _np.bmat, "free", "0 FLOPs")
|
|
1366
|
+
|
|
1367
|
+
|
|
1368
|
+
@_counted_wrapper
|
|
1369
|
+
def broadcast_arrays(*args: ArrayLike, **kwargs: Any) -> tuple[FlopscopeArray, ...]:
|
|
1370
|
+
"""Broadcast any number of arrays. Cost: numel(output)."""
|
|
1371
|
+
arrays = tuple(_np.asarray(arg) for arg in args)
|
|
1372
|
+
budget = require_budget()
|
|
1373
|
+
result = _np.broadcast_arrays(*arrays, **kwargs)
|
|
1374
|
+
cost = sum(a.size for a in result)
|
|
1375
|
+
with budget.deduct("broadcast_arrays", flop_cost=cost, subscripts=None, shapes=()):
|
|
1376
|
+
pass # numpy call already executed above
|
|
1377
|
+
if not result:
|
|
1378
|
+
return result # type: ignore[return-value]
|
|
1379
|
+
output_shape = result[0].shape
|
|
1380
|
+
wrapped = []
|
|
1381
|
+
for original, array, broadcasted in zip(args, arrays, result, strict=True):
|
|
1382
|
+
symmetry = broadcast_group(
|
|
1383
|
+
original.symmetry if isinstance(original, SymmetricTensor) else None,
|
|
1384
|
+
input_shape=array.shape,
|
|
1385
|
+
output_shape=output_shape,
|
|
1386
|
+
)
|
|
1387
|
+
wrapped.append(wrap_with_symmetry(broadcasted, symmetry))
|
|
1388
|
+
return tuple(wrapped)
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
attach_docstring(broadcast_arrays, _np.broadcast_arrays, "free", "0 FLOPs")
|
|
1392
|
+
|
|
1393
|
+
|
|
1394
|
+
def broadcast_shapes(*args, **kwargs):
|
|
1395
|
+
"""Broadcast shapes to a common shape. Wraps ``numpy.broadcast_shapes``. Cost: 0 FLOPs."""
|
|
1396
|
+
return _np.broadcast_shapes(*args, **kwargs)
|
|
1397
|
+
|
|
1398
|
+
|
|
1399
|
+
attach_docstring(broadcast_shapes, _np.broadcast_shapes, "free", "0 FLOPs")
|
|
1400
|
+
|
|
1401
|
+
|
|
1402
|
+
def can_cast(*args, **kwargs):
|
|
1403
|
+
"""Returns True if cast between data types can occur. Wraps ``numpy.can_cast``. Cost: 0 FLOPs."""
|
|
1404
|
+
return _np.can_cast(*args, **kwargs)
|
|
1405
|
+
|
|
1406
|
+
|
|
1407
|
+
attach_docstring(can_cast, _np.can_cast, "free", "0 FLOPs")
|
|
1408
|
+
|
|
1409
|
+
|
|
1410
|
+
@_counted_wrapper
|
|
1411
|
+
def choose(*args, **kwargs):
|
|
1412
|
+
"""Construct array from index array. Cost: numel(output)."""
|
|
1413
|
+
budget = require_budget()
|
|
1414
|
+
# Warn if the first arg (index array) carries symmetry.
|
|
1415
|
+
if args:
|
|
1416
|
+
_warn_if_symmetric(args[0], "choose")
|
|
1417
|
+
# Args: (a, choices, ...) or just (a, choices) — strip arrays.
|
|
1418
|
+
stripped_args = []
|
|
1419
|
+
for arg in args:
|
|
1420
|
+
if isinstance(arg, _np.ndarray):
|
|
1421
|
+
stripped_args.append(_to_base_ndarray(arg))
|
|
1422
|
+
elif isinstance(arg, (tuple, list)):
|
|
1423
|
+
stripped_args.append(_to_base_ndarray_tree(arg))
|
|
1424
|
+
else:
|
|
1425
|
+
stripped_args.append(arg)
|
|
1426
|
+
result = _np.choose(*stripped_args, **kwargs)
|
|
1427
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1428
|
+
with budget.deduct("choose", flop_cost=cost, subscripts=None, shapes=()):
|
|
1429
|
+
pass # numpy call already executed above
|
|
1430
|
+
return result
|
|
1431
|
+
|
|
1432
|
+
|
|
1433
|
+
attach_docstring(choose, _np.choose, "free", "0 FLOPs")
|
|
1434
|
+
|
|
1435
|
+
|
|
1436
|
+
def column_stack(tup: Sequence[ArrayLike]) -> FlopscopeArray:
|
|
1437
|
+
"""Stack 1-D arrays as columns. Wraps ``numpy.column_stack``. Cost: 0 FLOPs."""
|
|
1438
|
+
arr_list = [_np.asarray(a) for a in tup]
|
|
1439
|
+
groups = [a.symmetry if isinstance(a, SymmetricTensor) else None for a in tup]
|
|
1440
|
+
input_ndims = [a.ndim for a in arr_list]
|
|
1441
|
+
result = _np.column_stack(_to_base_ndarray_tree(tup)) # type: ignore[arg-type, call-overload]
|
|
1442
|
+
out_group = _st.transport_column_stack(
|
|
1443
|
+
groups,
|
|
1444
|
+
output_ndim=result.ndim,
|
|
1445
|
+
input_ndims=input_ndims,
|
|
1446
|
+
)
|
|
1447
|
+
if any(g is not None for g in groups) and out_group is None:
|
|
1448
|
+
_warn_symmetry_loss(
|
|
1449
|
+
lost_dims=[
|
|
1450
|
+
g.axes if g.axes is not None else tuple(range(g.degree))
|
|
1451
|
+
for g in groups
|
|
1452
|
+
if g is not None
|
|
1453
|
+
],
|
|
1454
|
+
reason="column_stack breaks block symmetry",
|
|
1455
|
+
)
|
|
1456
|
+
if out_group is not None:
|
|
1457
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
1458
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
1459
|
+
|
|
1460
|
+
|
|
1461
|
+
attach_docstring(column_stack, _np.column_stack, "free", "0 FLOPs")
|
|
1462
|
+
|
|
1463
|
+
|
|
1464
|
+
def common_type(*args, **kwargs):
|
|
1465
|
+
"""Return scalar type common to input arrays. Wraps ``numpy.common_type``. Cost: 0 FLOPs."""
|
|
1466
|
+
return _np.common_type(*[_to_base_ndarray(a) for a in args], **kwargs)
|
|
1467
|
+
|
|
1468
|
+
|
|
1469
|
+
attach_docstring(common_type, _np.common_type, "free", "0 FLOPs")
|
|
1470
|
+
|
|
1471
|
+
|
|
1472
|
+
@_counted_wrapper
|
|
1473
|
+
def compress(
|
|
1474
|
+
condition: ArrayLike,
|
|
1475
|
+
a: ArrayLike,
|
|
1476
|
+
*args: Any,
|
|
1477
|
+
**kwargs: Any,
|
|
1478
|
+
) -> FlopscopeArray:
|
|
1479
|
+
"""Return selected slices along an axis. Cost: numel(output)."""
|
|
1480
|
+
budget = require_budget()
|
|
1481
|
+
_warn_if_symmetric(a, "compress")
|
|
1482
|
+
result = _np.compress(
|
|
1483
|
+
_to_base_ndarray(condition), # type: ignore[arg-type]
|
|
1484
|
+
_to_base_ndarray(a),
|
|
1485
|
+
*args,
|
|
1486
|
+
**kwargs,
|
|
1487
|
+
)
|
|
1488
|
+
cost = (
|
|
1489
|
+
result.size
|
|
1490
|
+
if hasattr(result, "size")
|
|
1491
|
+
else len(result)
|
|
1492
|
+
if hasattr(result, "__len__")
|
|
1493
|
+
else 1
|
|
1494
|
+
)
|
|
1495
|
+
with budget.deduct(
|
|
1496
|
+
"compress", flop_cost=cost, subscripts=None, shapes=(result.shape,)
|
|
1497
|
+
):
|
|
1498
|
+
pass # numpy call already executed above
|
|
1499
|
+
return result
|
|
1500
|
+
|
|
1501
|
+
|
|
1502
|
+
attach_docstring(compress, _np.compress, "free", "0 FLOPs")
|
|
1503
|
+
|
|
1504
|
+
|
|
1505
|
+
@_counted_wrapper
|
|
1506
|
+
def concat(
|
|
1507
|
+
arrays: Sequence[ArrayLike],
|
|
1508
|
+
axis: int | None = 0,
|
|
1509
|
+
**kwargs: Any,
|
|
1510
|
+
) -> FlopscopeArray:
|
|
1511
|
+
"""Join arrays along an axis. Cost: numel(output)."""
|
|
1512
|
+
budget = require_budget()
|
|
1513
|
+
result = _np.concat(_to_base_ndarray_tree(arrays), axis=axis, **kwargs) # type: ignore[arg-type, call-overload]
|
|
1514
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1515
|
+
with budget.deduct("concat", flop_cost=cost, subscripts=None, shapes=()):
|
|
1516
|
+
pass # numpy call already executed above
|
|
1517
|
+
return result # type: ignore[return-value]
|
|
1518
|
+
|
|
1519
|
+
|
|
1520
|
+
attach_docstring(concat, _np.concat, "free", "0 FLOPs")
|
|
1521
|
+
|
|
1522
|
+
|
|
1523
|
+
@_counted_wrapper
|
|
1524
|
+
def copyto(dst, src, casting="same_kind", where=True):
|
|
1525
|
+
"""Copies values from one array to another. Cost: num elements copied."""
|
|
1526
|
+
budget = require_budget()
|
|
1527
|
+
src_arr = _np.asarray(src)
|
|
1528
|
+
if where is not True:
|
|
1529
|
+
where_arr = _np.asarray(where)
|
|
1530
|
+
cost = int(_np.count_nonzero(where_arr))
|
|
1531
|
+
else:
|
|
1532
|
+
cost = src_arr.size
|
|
1533
|
+
with budget.deduct("copyto", flop_cost=cost, subscripts=None, shapes=()):
|
|
1534
|
+
result = _call_numpy(
|
|
1535
|
+
_np.copyto,
|
|
1536
|
+
_to_base_ndarray(dst),
|
|
1537
|
+
_to_base_ndarray(src),
|
|
1538
|
+
casting=casting, # type: ignore[arg-type, call-overload]
|
|
1539
|
+
where=_to_base_ndarray(where) if where is not True else where,
|
|
1540
|
+
)
|
|
1541
|
+
return result
|
|
1542
|
+
|
|
1543
|
+
|
|
1544
|
+
attach_docstring(copyto, _np.copyto, "free", "0 FLOPs")
|
|
1545
|
+
|
|
1546
|
+
|
|
1547
|
+
@_counted_wrapper
|
|
1548
|
+
def delete(
|
|
1549
|
+
arr: ArrayLike,
|
|
1550
|
+
obj: Any,
|
|
1551
|
+
axis: int | None = None,
|
|
1552
|
+
**kwargs: Any,
|
|
1553
|
+
) -> FlopscopeArray:
|
|
1554
|
+
"""Return new array with sub-arrays deleted. Cost: num elements removed."""
|
|
1555
|
+
budget = require_budget()
|
|
1556
|
+
_warn_if_symmetric(arr, "delete")
|
|
1557
|
+
arr_np = _np.asarray(arr)
|
|
1558
|
+
result = _np.delete(_to_base_ndarray(arr), obj, axis=axis, **kwargs)
|
|
1559
|
+
cost = max(arr_np.size - result.size, 0) # num deleted
|
|
1560
|
+
with budget.deduct("delete", flop_cost=cost, subscripts=None, shapes=()):
|
|
1561
|
+
pass # numpy call already executed above
|
|
1562
|
+
return result # type: ignore[return-value]
|
|
1563
|
+
|
|
1564
|
+
|
|
1565
|
+
attach_docstring(delete, _np.delete, "free", "0 FLOPs")
|
|
1566
|
+
|
|
1567
|
+
|
|
1568
|
+
def diag_indices(*args, **kwargs):
|
|
1569
|
+
"""Return indices to access main diagonal. Wraps ``numpy.diag_indices``. Cost: 0 FLOPs."""
|
|
1570
|
+
return _np.diag_indices(*args, **kwargs)
|
|
1571
|
+
|
|
1572
|
+
|
|
1573
|
+
attach_docstring(diag_indices, _np.diag_indices, "free", "0 FLOPs")
|
|
1574
|
+
|
|
1575
|
+
|
|
1576
|
+
def diag_indices_from(*args, **kwargs):
|
|
1577
|
+
"""Return indices to access main diagonal of array. Wraps ``numpy.diag_indices_from``. Cost: 0 FLOPs."""
|
|
1578
|
+
return _np.diag_indices_from(*args, **kwargs)
|
|
1579
|
+
|
|
1580
|
+
|
|
1581
|
+
attach_docstring(diag_indices_from, _np.diag_indices_from, "free", "0 FLOPs")
|
|
1582
|
+
|
|
1583
|
+
|
|
1584
|
+
@_counted_wrapper
|
|
1585
|
+
def diagflat(v: ArrayLike, k: int = 0) -> FlopscopeArray:
|
|
1586
|
+
"""Create diagonal array from flattened input. Cost: numel(output)."""
|
|
1587
|
+
budget = require_budget()
|
|
1588
|
+
v_arr = _np.asarray(v)
|
|
1589
|
+
result = _np.diagflat(_to_base_ndarray(v), k=k)
|
|
1590
|
+
cost = result.size # output is (n+|k|)×(n+|k|) matrix
|
|
1591
|
+
with budget.deduct(
|
|
1592
|
+
"diagflat", flop_cost=cost, subscripts=None, shapes=(v_arr.shape,)
|
|
1593
|
+
):
|
|
1594
|
+
pass # numpy call already executed above
|
|
1595
|
+
symmetry = _infer_structural_constructor_symmetry(
|
|
1596
|
+
kind="diagflat", k=k, v_ndim=v_arr.ndim
|
|
1597
|
+
)
|
|
1598
|
+
if symmetry is not None:
|
|
1599
|
+
return wrap_with_trusted_symmetry(result, symmetry) # type: ignore[return-value]
|
|
1600
|
+
return result # type: ignore[return-value]
|
|
1601
|
+
|
|
1602
|
+
|
|
1603
|
+
attach_docstring(diagflat, _np.diagflat, "free", "0 FLOPs")
|
|
1604
|
+
|
|
1605
|
+
|
|
1606
|
+
@_counted_wrapper
|
|
1607
|
+
def dsplit(ary: ArrayLike, *args: Any, **kwargs: Any) -> list[FlopscopeArray]:
|
|
1608
|
+
"""Split array along third axis. Cost: numel(input)."""
|
|
1609
|
+
budget = require_budget()
|
|
1610
|
+
ary_arr = _np.asarray(ary)
|
|
1611
|
+
cost = ary_arr.size
|
|
1612
|
+
in_group = ary.symmetry if isinstance(ary, SymmetricTensor) else None
|
|
1613
|
+
out_group = _st.transport_dsplit(in_group, input_shape=ary_arr.shape)
|
|
1614
|
+
if in_group is not None and out_group is None:
|
|
1615
|
+
_warn_symmetry_loss(
|
|
1616
|
+
lost_dims=[
|
|
1617
|
+
in_group.axes
|
|
1618
|
+
if in_group.axes is not None
|
|
1619
|
+
else tuple(range(in_group.degree))
|
|
1620
|
+
],
|
|
1621
|
+
reason="dsplit breaks block symmetry",
|
|
1622
|
+
)
|
|
1623
|
+
with budget.deduct(
|
|
1624
|
+
"dsplit", flop_cost=cost, subscripts=None, shapes=(ary_arr.shape,)
|
|
1625
|
+
):
|
|
1626
|
+
raw_pieces = _call_numpy(_np.dsplit, ary_arr, *args, **kwargs)
|
|
1627
|
+
if out_group is not None:
|
|
1628
|
+
return [wrap_with_symmetry(p, out_group) for p in raw_pieces] # type: ignore[return-value]
|
|
1629
|
+
return [_asplainflopscope(p) for p in raw_pieces] # type: ignore[return-value]
|
|
1630
|
+
|
|
1631
|
+
|
|
1632
|
+
attach_docstring(dsplit, _np.dsplit, "free", "0 FLOPs")
|
|
1633
|
+
|
|
1634
|
+
|
|
1635
|
+
@_counted_wrapper
|
|
1636
|
+
def dstack(tup: Sequence[ArrayLike]) -> FlopscopeArray:
|
|
1637
|
+
"""Stack arrays along third axis. Cost: numel(output)."""
|
|
1638
|
+
budget = require_budget()
|
|
1639
|
+
for a in tup:
|
|
1640
|
+
_warn_if_symmetric(a, "dstack")
|
|
1641
|
+
result = _np.dstack(_to_base_ndarray_tree(tup)) # type: ignore[arg-type]
|
|
1642
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1643
|
+
with budget.deduct("dstack", flop_cost=cost, subscripts=None, shapes=()):
|
|
1644
|
+
pass # numpy call already executed above
|
|
1645
|
+
return result # type: ignore[return-value]
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
attach_docstring(dstack, _np.dstack, "free", "0 FLOPs")
|
|
1649
|
+
|
|
1650
|
+
|
|
1651
|
+
@_counted_wrapper
|
|
1652
|
+
def extract(
|
|
1653
|
+
condition: ArrayLike,
|
|
1654
|
+
arr: ArrayLike,
|
|
1655
|
+
*args: Any,
|
|
1656
|
+
**kwargs: Any,
|
|
1657
|
+
) -> FlopscopeArray:
|
|
1658
|
+
"""Return elements satisfying condition. Cost: numel(input)."""
|
|
1659
|
+
budget = require_budget()
|
|
1660
|
+
arr_np = _np.asarray(arr)
|
|
1661
|
+
cost = arr_np.size
|
|
1662
|
+
with budget.deduct(
|
|
1663
|
+
"extract", flop_cost=cost, subscripts=None, shapes=(arr_np.shape,)
|
|
1664
|
+
):
|
|
1665
|
+
result = _call_numpy(
|
|
1666
|
+
_np.extract,
|
|
1667
|
+
_to_base_ndarray(condition),
|
|
1668
|
+
_to_base_ndarray(arr),
|
|
1669
|
+
*args,
|
|
1670
|
+
**kwargs,
|
|
1671
|
+
)
|
|
1672
|
+
return result # type: ignore[return-value]
|
|
1673
|
+
|
|
1674
|
+
|
|
1675
|
+
attach_docstring(extract, _np.extract, "free", "0 FLOPs")
|
|
1676
|
+
|
|
1677
|
+
|
|
1678
|
+
@_counted_wrapper
|
|
1679
|
+
def fill_diagonal(
|
|
1680
|
+
a: ArrayLike,
|
|
1681
|
+
val: Any,
|
|
1682
|
+
wrap: bool = False,
|
|
1683
|
+
**kwargs: Any,
|
|
1684
|
+
) -> None:
|
|
1685
|
+
"""Fill main diagonal of array in-place. Cost: min(m,n)."""
|
|
1686
|
+
budget = require_budget()
|
|
1687
|
+
a_arr = _np.asarray(a)
|
|
1688
|
+
cost = min(a_arr.shape[0], a_arr.shape[1]) if a_arr.ndim >= 2 else a_arr.size
|
|
1689
|
+
with budget.deduct(
|
|
1690
|
+
"fill_diagonal", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
1691
|
+
):
|
|
1692
|
+
# ``np.fill_diagonal`` mutates ``a`` in-place; ``_to_base_ndarray``
|
|
1693
|
+
# is zero-copy so the mutation propagates to the user's array.
|
|
1694
|
+
result = _call_numpy(
|
|
1695
|
+
_np.fill_diagonal, _to_base_ndarray(a), val, wrap=wrap, **kwargs
|
|
1696
|
+
) # type: ignore[arg-type, call-overload]
|
|
1697
|
+
return result
|
|
1698
|
+
|
|
1699
|
+
|
|
1700
|
+
attach_docstring(fill_diagonal, _np.fill_diagonal, "free", "0 FLOPs")
|
|
1701
|
+
|
|
1702
|
+
|
|
1703
|
+
@_counted_wrapper
|
|
1704
|
+
def flatnonzero(a: ArrayLike, *args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
1705
|
+
"""Return indices of non-zero elements in flattened array. Cost: numel(input)."""
|
|
1706
|
+
budget = require_budget()
|
|
1707
|
+
a_arr = _np.asarray(a)
|
|
1708
|
+
cost = a_arr.size
|
|
1709
|
+
with budget.deduct(
|
|
1710
|
+
"flatnonzero", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
1711
|
+
):
|
|
1712
|
+
result = _call_numpy(_np.flatnonzero, _to_base_ndarray(a), *args, **kwargs)
|
|
1713
|
+
return result # type: ignore[return-value]
|
|
1714
|
+
|
|
1715
|
+
|
|
1716
|
+
attach_docstring(flatnonzero, _np.flatnonzero, "free", "0 FLOPs")
|
|
1717
|
+
|
|
1718
|
+
|
|
1719
|
+
def fliplr(*args, **kwargs):
|
|
1720
|
+
"""Reverse elements along axis 1. Wraps ``numpy.fliplr``. Cost: 0 FLOPs."""
|
|
1721
|
+
_warn_if_symmetric(args[0], "fliplr")
|
|
1722
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
1723
|
+
return _np.fliplr(*stripped_args, **kwargs)
|
|
1724
|
+
|
|
1725
|
+
|
|
1726
|
+
attach_docstring(fliplr, _np.fliplr, "free", "0 FLOPs")
|
|
1727
|
+
|
|
1728
|
+
|
|
1729
|
+
def flipud(*args, **kwargs):
|
|
1730
|
+
"""Reverse elements along axis 0. Wraps ``numpy.flipud``. Cost: 0 FLOPs."""
|
|
1731
|
+
_warn_if_symmetric(args[0], "flipud")
|
|
1732
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
1733
|
+
return _np.flipud(*stripped_args, **kwargs)
|
|
1734
|
+
|
|
1735
|
+
|
|
1736
|
+
attach_docstring(flipud, _np.flipud, "free", "0 FLOPs")
|
|
1737
|
+
|
|
1738
|
+
|
|
1739
|
+
@_counted_wrapper
|
|
1740
|
+
def from_dlpack(*args, **kwargs):
|
|
1741
|
+
"""Create array from DLPack capsule. Cost: numel(output)."""
|
|
1742
|
+
budget = require_budget()
|
|
1743
|
+
result = _np.from_dlpack(*args, **kwargs)
|
|
1744
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1745
|
+
with budget.deduct("from_dlpack", flop_cost=cost, subscripts=None, shapes=()):
|
|
1746
|
+
pass # numpy call already executed above
|
|
1747
|
+
return result
|
|
1748
|
+
|
|
1749
|
+
|
|
1750
|
+
attach_docstring(from_dlpack, _np.from_dlpack, "free", "0 FLOPs")
|
|
1751
|
+
|
|
1752
|
+
|
|
1753
|
+
@_counted_wrapper
|
|
1754
|
+
def frombuffer(
|
|
1755
|
+
buffer: Any,
|
|
1756
|
+
dtype: DTypeLike = float,
|
|
1757
|
+
count: int = -1,
|
|
1758
|
+
offset: int = 0,
|
|
1759
|
+
) -> FlopscopeArray:
|
|
1760
|
+
"""Interpret buffer as 1-D array. Cost: numel(output)."""
|
|
1761
|
+
budget = require_budget()
|
|
1762
|
+
result = _np.frombuffer(buffer, dtype=dtype, count=count, offset=offset)
|
|
1763
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1764
|
+
with budget.deduct("frombuffer", flop_cost=cost, subscripts=None, shapes=()):
|
|
1765
|
+
pass # numpy call already executed above
|
|
1766
|
+
return result # type: ignore[return-value]
|
|
1767
|
+
|
|
1768
|
+
|
|
1769
|
+
attach_docstring(frombuffer, _np.frombuffer, "free", "0 FLOPs")
|
|
1770
|
+
|
|
1771
|
+
|
|
1772
|
+
@_counted_wrapper
|
|
1773
|
+
def fromfile(*args, **kwargs):
|
|
1774
|
+
"""Construct array from data in text or binary file. Cost: numel(output)."""
|
|
1775
|
+
budget = require_budget()
|
|
1776
|
+
result = _np.fromfile(*args, **kwargs)
|
|
1777
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1778
|
+
with budget.deduct("fromfile", flop_cost=cost, subscripts=None, shapes=()):
|
|
1779
|
+
pass # numpy call already executed above
|
|
1780
|
+
return result
|
|
1781
|
+
|
|
1782
|
+
|
|
1783
|
+
attach_docstring(fromfile, _np.fromfile, "free", "0 FLOPs")
|
|
1784
|
+
|
|
1785
|
+
|
|
1786
|
+
@_counted_wrapper
|
|
1787
|
+
def fromfunction(*args, **kwargs):
|
|
1788
|
+
"""Construct array by executing function over each coordinate. Cost: numel(output)."""
|
|
1789
|
+
budget = require_budget()
|
|
1790
|
+
result = _np.fromfunction(*args, **kwargs)
|
|
1791
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1792
|
+
with budget.deduct("fromfunction", flop_cost=cost, subscripts=None, shapes=()):
|
|
1793
|
+
pass # numpy call already executed above
|
|
1794
|
+
return result
|
|
1795
|
+
|
|
1796
|
+
|
|
1797
|
+
attach_docstring(fromfunction, _np.fromfunction, "free", "0 FLOPs")
|
|
1798
|
+
|
|
1799
|
+
|
|
1800
|
+
@_counted_wrapper
|
|
1801
|
+
def fromiter(*args, **kwargs):
|
|
1802
|
+
"""Create array from iterable object. Cost: numel(output)."""
|
|
1803
|
+
budget = require_budget()
|
|
1804
|
+
result = _np.fromiter(*args, **kwargs)
|
|
1805
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1806
|
+
with budget.deduct("fromiter", flop_cost=cost, subscripts=None, shapes=()):
|
|
1807
|
+
pass # numpy call already executed above
|
|
1808
|
+
return result
|
|
1809
|
+
|
|
1810
|
+
|
|
1811
|
+
attach_docstring(fromiter, _np.fromiter, "free", "0 FLOPs")
|
|
1812
|
+
|
|
1813
|
+
|
|
1814
|
+
@_counted_wrapper
|
|
1815
|
+
def fromregex(*args, **kwargs):
|
|
1816
|
+
"""Construct array from text file using regex. Cost: numel(output)."""
|
|
1817
|
+
budget = require_budget()
|
|
1818
|
+
result = _np.fromregex(*args, **kwargs)
|
|
1819
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1820
|
+
with budget.deduct("fromregex", flop_cost=cost, subscripts=None, shapes=()):
|
|
1821
|
+
pass # numpy call already executed above
|
|
1822
|
+
return result
|
|
1823
|
+
|
|
1824
|
+
|
|
1825
|
+
attach_docstring(fromregex, _np.fromregex, "free", "0 FLOPs")
|
|
1826
|
+
|
|
1827
|
+
|
|
1828
|
+
@_counted_wrapper
|
|
1829
|
+
def fromstring(*args, **kwargs):
|
|
1830
|
+
"""Construct array from string. Cost: numel(output)."""
|
|
1831
|
+
budget = require_budget()
|
|
1832
|
+
result = _np.fromstring(*args, **kwargs)
|
|
1833
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1834
|
+
with budget.deduct("fromstring", flop_cost=cost, subscripts=None, shapes=()):
|
|
1835
|
+
pass # numpy call already executed above
|
|
1836
|
+
return result
|
|
1837
|
+
|
|
1838
|
+
|
|
1839
|
+
attach_docstring(fromstring, _np.fromstring, "free", "0 FLOPs")
|
|
1840
|
+
|
|
1841
|
+
|
|
1842
|
+
@_counted_wrapper
|
|
1843
|
+
def indices(*args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
1844
|
+
"""Return array representing indices of a grid. Cost: numel(output)."""
|
|
1845
|
+
budget = require_budget()
|
|
1846
|
+
result = _np.indices(*args, **kwargs)
|
|
1847
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
1848
|
+
with budget.deduct("indices", flop_cost=cost, subscripts=None, shapes=()):
|
|
1849
|
+
pass # numpy call already executed above
|
|
1850
|
+
return result
|
|
1851
|
+
|
|
1852
|
+
|
|
1853
|
+
attach_docstring(indices, _np.indices, "free", "0 FLOPs")
|
|
1854
|
+
|
|
1855
|
+
|
|
1856
|
+
@_counted_wrapper
|
|
1857
|
+
def insert(
|
|
1858
|
+
arr: ArrayLike,
|
|
1859
|
+
obj: Any,
|
|
1860
|
+
values: ArrayLike,
|
|
1861
|
+
axis: int | None = None,
|
|
1862
|
+
**kwargs: Any,
|
|
1863
|
+
) -> FlopscopeArray:
|
|
1864
|
+
"""Insert values along axis before given indices. Cost: numel(inserted values)."""
|
|
1865
|
+
budget = require_budget()
|
|
1866
|
+
values_arr = _np.asarray(values)
|
|
1867
|
+
cost = values_arr.size # num inserted
|
|
1868
|
+
with budget.deduct("insert", flop_cost=cost, subscripts=None, shapes=()):
|
|
1869
|
+
result = _call_numpy(
|
|
1870
|
+
_np.insert,
|
|
1871
|
+
_to_base_ndarray(arr),
|
|
1872
|
+
obj,
|
|
1873
|
+
_to_base_ndarray(values),
|
|
1874
|
+
axis=axis,
|
|
1875
|
+
**kwargs,
|
|
1876
|
+
)
|
|
1877
|
+
return result # type: ignore[return-value]
|
|
1878
|
+
|
|
1879
|
+
|
|
1880
|
+
attach_docstring(insert, _np.insert, "free", "0 FLOPs")
|
|
1881
|
+
|
|
1882
|
+
|
|
1883
|
+
def isdtype(*args, **kwargs):
|
|
1884
|
+
"""Returns boolean indicating whether a provided dtype is of a specified kind. Wraps ``numpy.isdtype``. Cost: 0 FLOPs."""
|
|
1885
|
+
return _np.isdtype(*args, **kwargs)
|
|
1886
|
+
|
|
1887
|
+
|
|
1888
|
+
attach_docstring(isdtype, _np.isdtype, "free", "0 FLOPs")
|
|
1889
|
+
|
|
1890
|
+
|
|
1891
|
+
def isfortran(*args, **kwargs):
|
|
1892
|
+
"""Returns True if array is Fortran contiguous. Wraps ``numpy.isfortran``. Cost: 0 FLOPs."""
|
|
1893
|
+
return _np.isfortran(*args, **kwargs)
|
|
1894
|
+
|
|
1895
|
+
|
|
1896
|
+
attach_docstring(isfortran, _np.isfortran, "free", "0 FLOPs")
|
|
1897
|
+
|
|
1898
|
+
|
|
1899
|
+
def isin(
|
|
1900
|
+
element: ArrayLike,
|
|
1901
|
+
test_elements: ArrayLike,
|
|
1902
|
+
assume_unique: bool = False,
|
|
1903
|
+
invert: bool = False,
|
|
1904
|
+
) -> FlopscopeArray:
|
|
1905
|
+
"""Test element-wise membership in a set. Wraps ``numpy.isin``. Cost: 0 FLOPs."""
|
|
1906
|
+
return _np.isin( # type: ignore[return-value]
|
|
1907
|
+
_to_base_ndarray(element),
|
|
1908
|
+
_to_base_ndarray(test_elements),
|
|
1909
|
+
assume_unique=assume_unique,
|
|
1910
|
+
invert=invert,
|
|
1911
|
+
)
|
|
1912
|
+
|
|
1913
|
+
|
|
1914
|
+
attach_docstring(isin, _np.isin, "free", "0 FLOPs")
|
|
1915
|
+
|
|
1916
|
+
|
|
1917
|
+
def isscalar(*args, **kwargs):
|
|
1918
|
+
"""Returns True if element is scalar type. Wraps ``numpy.isscalar``. Cost: 0 FLOPs."""
|
|
1919
|
+
return _np.isscalar(*args, **kwargs)
|
|
1920
|
+
|
|
1921
|
+
|
|
1922
|
+
attach_docstring(isscalar, _np.isscalar, "free", "0 FLOPs")
|
|
1923
|
+
|
|
1924
|
+
|
|
1925
|
+
def issubdtype(*args, **kwargs):
|
|
1926
|
+
"""Returns True if first argument is a typecode lower/equal in type hierarchy. Wraps ``numpy.issubdtype``. Cost: 0 FLOPs."""
|
|
1927
|
+
return _np.issubdtype(*args, **kwargs)
|
|
1928
|
+
|
|
1929
|
+
|
|
1930
|
+
attach_docstring(issubdtype, _np.issubdtype, "free", "0 FLOPs")
|
|
1931
|
+
|
|
1932
|
+
|
|
1933
|
+
def iterable(*args, **kwargs):
|
|
1934
|
+
"""Check whether or not object is iterable. Wraps ``numpy.iterable``. Cost: 0 FLOPs."""
|
|
1935
|
+
return _np.iterable(*args, **kwargs)
|
|
1936
|
+
|
|
1937
|
+
|
|
1938
|
+
attach_docstring(iterable, _np.iterable, "free", "0 FLOPs")
|
|
1939
|
+
|
|
1940
|
+
|
|
1941
|
+
@_counted_wrapper
|
|
1942
|
+
def ix_(*args: ArrayLike, **kwargs: Any) -> tuple[FlopscopeArray, ...]:
|
|
1943
|
+
"""Construct open mesh from multiple sequences. Cost: numel(output)."""
|
|
1944
|
+
budget = require_budget()
|
|
1945
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
1946
|
+
result = _np.ix_(*stripped_args, **kwargs) # type: ignore[arg-type, call-overload]
|
|
1947
|
+
cost = sum(a.size for a in result)
|
|
1948
|
+
with budget.deduct("ix_", flop_cost=cost, subscripts=None, shapes=()):
|
|
1949
|
+
pass # numpy call already executed above
|
|
1950
|
+
return result
|
|
1951
|
+
|
|
1952
|
+
|
|
1953
|
+
attach_docstring(ix_, _np.ix_, "free", "0 FLOPs")
|
|
1954
|
+
|
|
1955
|
+
|
|
1956
|
+
@_counted_wrapper
|
|
1957
|
+
def mask_indices(*args, **kwargs):
|
|
1958
|
+
"""Return indices to access main or off-diagonal of array. Cost: numel(output)."""
|
|
1959
|
+
budget = require_budget()
|
|
1960
|
+
result = _np.mask_indices(*args, **kwargs)
|
|
1961
|
+
cost = sum(a.size for a in result) if isinstance(result, tuple) else 1
|
|
1962
|
+
with budget.deduct("mask_indices", flop_cost=cost, subscripts=None, shapes=()):
|
|
1963
|
+
pass # numpy call already executed above
|
|
1964
|
+
return result
|
|
1965
|
+
|
|
1966
|
+
|
|
1967
|
+
attach_docstring(mask_indices, _np.mask_indices, "free", "0 FLOPs")
|
|
1968
|
+
|
|
1969
|
+
|
|
1970
|
+
def matrix_transpose(x: ArrayLike) -> FlopscopeArray:
|
|
1971
|
+
"""Swap last two axes. Wraps ``numpy.matrix_transpose``. Cost: 0 FLOPs."""
|
|
1972
|
+
x_arr = _np.asarray(x)
|
|
1973
|
+
result = _np.matrix_transpose(x_arr)
|
|
1974
|
+
in_group = x.symmetry if isinstance(x, SymmetricTensor) else None
|
|
1975
|
+
out_group = _st.transport_matrix_transpose(in_group, ndim=x_arr.ndim)
|
|
1976
|
+
if in_group is not None and out_group is None:
|
|
1977
|
+
_warn_symmetry_loss(
|
|
1978
|
+
lost_dims=[
|
|
1979
|
+
in_group.axes
|
|
1980
|
+
if in_group.axes is not None
|
|
1981
|
+
else tuple(range(in_group.degree))
|
|
1982
|
+
],
|
|
1983
|
+
reason="matrix_transpose: rank too low for sym",
|
|
1984
|
+
)
|
|
1985
|
+
if out_group is not None:
|
|
1986
|
+
return wrap_with_symmetry(result, out_group) # type: ignore[return-value]
|
|
1987
|
+
return _asplainflopscope(result) # type: ignore[return-value]
|
|
1988
|
+
|
|
1989
|
+
|
|
1990
|
+
attach_docstring(matrix_transpose, _np.matrix_transpose, "free", "0 FLOPs")
|
|
1991
|
+
|
|
1992
|
+
|
|
1993
|
+
def may_share_memory(*args, **kwargs):
|
|
1994
|
+
"""Determine if two arrays might share memory. Wraps ``numpy.may_share_memory``. Cost: 0 FLOPs."""
|
|
1995
|
+
stripped_args = tuple(_to_base_ndarray(a) for a in args)
|
|
1996
|
+
return _np.may_share_memory(*stripped_args, **kwargs) # type: ignore[arg-type, call-overload]
|
|
1997
|
+
|
|
1998
|
+
|
|
1999
|
+
attach_docstring(may_share_memory, _np.may_share_memory, "free", "0 FLOPs")
|
|
2000
|
+
|
|
2001
|
+
|
|
2002
|
+
def min_scalar_type(*args, **kwargs):
|
|
2003
|
+
"""Return smallest scalar type. Wraps ``numpy.min_scalar_type``. Cost: 0 FLOPs."""
|
|
2004
|
+
return _np.min_scalar_type(*args, **kwargs)
|
|
2005
|
+
|
|
2006
|
+
|
|
2007
|
+
attach_docstring(min_scalar_type, _np.min_scalar_type, "free", "0 FLOPs")
|
|
2008
|
+
|
|
2009
|
+
|
|
2010
|
+
def mintypecode(*args, **kwargs):
|
|
2011
|
+
"""Return minimum data type character. Wraps ``numpy.mintypecode``. Cost: 0 FLOPs."""
|
|
2012
|
+
return _np.mintypecode(*args, **kwargs)
|
|
2013
|
+
|
|
2014
|
+
|
|
2015
|
+
attach_docstring(mintypecode, _np.mintypecode, "free", "0 FLOPs")
|
|
2016
|
+
|
|
2017
|
+
|
|
2018
|
+
def ndim(*args, **kwargs):
|
|
2019
|
+
"""Return number of dimensions. Wraps ``numpy.ndim``. Cost: 0 FLOPs."""
|
|
2020
|
+
return _np.ndim(*args, **kwargs)
|
|
2021
|
+
|
|
2022
|
+
|
|
2023
|
+
attach_docstring(ndim, _np.ndim, "free", "0 FLOPs")
|
|
2024
|
+
|
|
2025
|
+
|
|
2026
|
+
@_counted_wrapper
|
|
2027
|
+
def nonzero(a: ArrayLike, *args: Any, **kwargs: Any) -> tuple[FlopscopeArray, ...]:
|
|
2028
|
+
"""Return indices of non-zero elements. Cost: numel(input)."""
|
|
2029
|
+
budget = require_budget()
|
|
2030
|
+
a_arr = _np.asarray(a)
|
|
2031
|
+
cost = a_arr.size
|
|
2032
|
+
with budget.deduct(
|
|
2033
|
+
"nonzero", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
2034
|
+
):
|
|
2035
|
+
result = _call_numpy(_np.nonzero, _to_base_ndarray(a), *args, **kwargs) # type: ignore[arg-type, call-overload]
|
|
2036
|
+
return result # type: ignore[return-value]
|
|
2037
|
+
|
|
2038
|
+
|
|
2039
|
+
attach_docstring(nonzero, _np.nonzero, "free", "0 FLOPs")
|
|
2040
|
+
|
|
2041
|
+
|
|
2042
|
+
@_counted_wrapper
|
|
2043
|
+
def packbits(a: ArrayLike, *args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
2044
|
+
"""Pack binary-valued array into bits. Cost: numel(output)."""
|
|
2045
|
+
budget = require_budget()
|
|
2046
|
+
result = _np.packbits(_to_base_ndarray(a), *args, **kwargs) # type: ignore[arg-type, call-overload]
|
|
2047
|
+
cost = (
|
|
2048
|
+
result.size
|
|
2049
|
+
if hasattr(result, "size")
|
|
2050
|
+
else len(result)
|
|
2051
|
+
if hasattr(result, "__len__")
|
|
2052
|
+
else 1
|
|
2053
|
+
)
|
|
2054
|
+
with budget.deduct(
|
|
2055
|
+
"packbits", flop_cost=cost, subscripts=None, shapes=(result.shape,)
|
|
2056
|
+
):
|
|
2057
|
+
pass # numpy call already executed above
|
|
2058
|
+
return result # type: ignore[return-value]
|
|
2059
|
+
|
|
2060
|
+
|
|
2061
|
+
attach_docstring(packbits, _np.packbits, "free", "0 FLOPs")
|
|
2062
|
+
|
|
2063
|
+
|
|
2064
|
+
def permute_dims(*args, **kwargs):
|
|
2065
|
+
"""Permute dimensions of array. Wraps ``numpy.permute_dims``. Cost: 0 FLOPs."""
|
|
2066
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
2067
|
+
return _np.permute_dims(*stripped_args, **kwargs)
|
|
2068
|
+
|
|
2069
|
+
|
|
2070
|
+
attach_docstring(permute_dims, _np.permute_dims, "free", "0 FLOPs")
|
|
2071
|
+
|
|
2072
|
+
|
|
2073
|
+
@_counted_wrapper
|
|
2074
|
+
def place(
|
|
2075
|
+
arr: ArrayLike,
|
|
2076
|
+
mask: ArrayLike,
|
|
2077
|
+
vals: ArrayLike,
|
|
2078
|
+
*args: Any,
|
|
2079
|
+
**kwargs: Any,
|
|
2080
|
+
) -> None:
|
|
2081
|
+
"""Change elements of array based on conditional. Cost: numel(input)."""
|
|
2082
|
+
budget = require_budget()
|
|
2083
|
+
arr_np = _np.asarray(arr)
|
|
2084
|
+
cost = arr_np.size
|
|
2085
|
+
with budget.deduct(
|
|
2086
|
+
"place", flop_cost=cost, subscripts=None, shapes=(arr_np.shape,)
|
|
2087
|
+
):
|
|
2088
|
+
# ``np.place`` mutates ``arr`` in-place; ``_to_base_ndarray`` is
|
|
2089
|
+
# zero-copy so the mutation propagates to the user's array.
|
|
2090
|
+
result = _call_numpy(
|
|
2091
|
+
_np.place,
|
|
2092
|
+
_to_base_ndarray(arr), # type: ignore[arg-type, call-overload]
|
|
2093
|
+
_to_base_ndarray(mask),
|
|
2094
|
+
_to_base_ndarray(vals),
|
|
2095
|
+
*args,
|
|
2096
|
+
**kwargs,
|
|
2097
|
+
)
|
|
2098
|
+
return result
|
|
2099
|
+
|
|
2100
|
+
|
|
2101
|
+
attach_docstring(place, _np.place, "free", "0 FLOPs")
|
|
2102
|
+
|
|
2103
|
+
|
|
2104
|
+
def promote_types(*args, **kwargs):
|
|
2105
|
+
"""Return smallest size and least significant type. Wraps ``numpy.promote_types``. Cost: 0 FLOPs."""
|
|
2106
|
+
return _np.promote_types(*args, **kwargs)
|
|
2107
|
+
|
|
2108
|
+
|
|
2109
|
+
attach_docstring(promote_types, _np.promote_types, "free", "0 FLOPs")
|
|
2110
|
+
|
|
2111
|
+
|
|
2112
|
+
@_counted_wrapper
|
|
2113
|
+
def put(
|
|
2114
|
+
a: ArrayLike,
|
|
2115
|
+
ind: ArrayLike,
|
|
2116
|
+
v: ArrayLike,
|
|
2117
|
+
*args: Any,
|
|
2118
|
+
**kwargs: Any,
|
|
2119
|
+
) -> None:
|
|
2120
|
+
"""Replace elements at given flat indices. Cost: numel(input)."""
|
|
2121
|
+
budget = require_budget()
|
|
2122
|
+
a_arr = _np.asarray(a)
|
|
2123
|
+
cost = a_arr.size
|
|
2124
|
+
with budget.deduct("put", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)):
|
|
2125
|
+
# ``np.put`` mutates ``a`` in-place. ``_to_base_ndarray`` is a
|
|
2126
|
+
# zero-copy view, so the mutation propagates to the user's
|
|
2127
|
+
# original FlopscopeArray buffer.
|
|
2128
|
+
result = _call_numpy(
|
|
2129
|
+
_np.put,
|
|
2130
|
+
_to_base_ndarray(a), # type: ignore[arg-type, call-overload]
|
|
2131
|
+
_to_base_ndarray(ind), # type: ignore[arg-type, call-overload]
|
|
2132
|
+
_to_base_ndarray(v),
|
|
2133
|
+
*args,
|
|
2134
|
+
**kwargs,
|
|
2135
|
+
)
|
|
2136
|
+
return result
|
|
2137
|
+
|
|
2138
|
+
|
|
2139
|
+
attach_docstring(put, _np.put, "free", "0 FLOPs")
|
|
2140
|
+
|
|
2141
|
+
|
|
2142
|
+
@_counted_wrapper
|
|
2143
|
+
def put_along_axis(
|
|
2144
|
+
arr: ArrayLike,
|
|
2145
|
+
indices: ArrayLike,
|
|
2146
|
+
values: ArrayLike,
|
|
2147
|
+
axis: int | None,
|
|
2148
|
+
*args: Any,
|
|
2149
|
+
**kwargs: Any,
|
|
2150
|
+
) -> None:
|
|
2151
|
+
"""Put values into destination array along axis. Cost: numel(input)."""
|
|
2152
|
+
budget = require_budget()
|
|
2153
|
+
arr_np = _np.asarray(arr)
|
|
2154
|
+
cost = arr_np.size
|
|
2155
|
+
with budget.deduct(
|
|
2156
|
+
"put_along_axis", flop_cost=cost, subscripts=None, shapes=(arr_np.shape,)
|
|
2157
|
+
):
|
|
2158
|
+
# ``np.put_along_axis`` mutates ``arr`` in-place; ``_to_base_ndarray``
|
|
2159
|
+
# is zero-copy so the mutation propagates to the user's array.
|
|
2160
|
+
result = _call_numpy(
|
|
2161
|
+
_np.put_along_axis,
|
|
2162
|
+
_to_base_ndarray(arr), # type: ignore[arg-type, call-overload]
|
|
2163
|
+
_to_base_ndarray(indices), # type: ignore[arg-type, call-overload]
|
|
2164
|
+
_to_base_ndarray(values),
|
|
2165
|
+
axis,
|
|
2166
|
+
*args,
|
|
2167
|
+
**kwargs,
|
|
2168
|
+
)
|
|
2169
|
+
return result
|
|
2170
|
+
|
|
2171
|
+
|
|
2172
|
+
attach_docstring(put_along_axis, _np.put_along_axis, "free", "0 FLOPs")
|
|
2173
|
+
|
|
2174
|
+
|
|
2175
|
+
@_counted_wrapper
|
|
2176
|
+
def putmask(
|
|
2177
|
+
a: ArrayLike,
|
|
2178
|
+
mask: ArrayLike,
|
|
2179
|
+
values: ArrayLike,
|
|
2180
|
+
*args: Any,
|
|
2181
|
+
**kwargs: Any,
|
|
2182
|
+
) -> None:
|
|
2183
|
+
"""Change elements of array based on condition. Cost: numel(input)."""
|
|
2184
|
+
budget = require_budget()
|
|
2185
|
+
a_arr = _np.asarray(a)
|
|
2186
|
+
cost = a_arr.size
|
|
2187
|
+
with budget.deduct(
|
|
2188
|
+
"putmask", flop_cost=cost, subscripts=None, shapes=(a_arr.shape,)
|
|
2189
|
+
):
|
|
2190
|
+
result = _call_numpy(
|
|
2191
|
+
_np.putmask,
|
|
2192
|
+
_to_base_ndarray(a), # type: ignore[arg-type, call-overload]
|
|
2193
|
+
_to_base_ndarray(mask), # type: ignore[arg-type, call-overload]
|
|
2194
|
+
_to_base_ndarray(values),
|
|
2195
|
+
*args,
|
|
2196
|
+
**kwargs,
|
|
2197
|
+
)
|
|
2198
|
+
return result
|
|
2199
|
+
|
|
2200
|
+
|
|
2201
|
+
attach_docstring(putmask, _np.putmask, "free", "0 FLOPs")
|
|
2202
|
+
|
|
2203
|
+
|
|
2204
|
+
def ravel_multi_index(*args, **kwargs):
|
|
2205
|
+
"""Convert multi-index to flat index. Wraps ``numpy.ravel_multi_index``. Cost: 0 FLOPs."""
|
|
2206
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
2207
|
+
return _np.ravel_multi_index(*stripped_args, **kwargs)
|
|
2208
|
+
|
|
2209
|
+
|
|
2210
|
+
attach_docstring(ravel_multi_index, _np.ravel_multi_index, "free", "0 FLOPs")
|
|
2211
|
+
|
|
2212
|
+
|
|
2213
|
+
def require(*args, **kwargs):
|
|
2214
|
+
"""Return array satisfying requirements. Wraps ``numpy.require``. Cost: 0 FLOPs."""
|
|
2215
|
+
# Pass args through unstripped: ``_np.require`` is a thin Python
|
|
2216
|
+
# helper around ``np.asanyarray`` and does not enter the
|
|
2217
|
+
# ``__array_function__`` dispatch path, so passing a FlopscopeArray
|
|
2218
|
+
# cannot recurse. Stripping would break ``np.require(x).is(x)``
|
|
2219
|
+
# identity for already-conforming inputs.
|
|
2220
|
+
return _np.require(*args, **kwargs)
|
|
2221
|
+
|
|
2222
|
+
|
|
2223
|
+
attach_docstring(require, _np.require, "free", "0 FLOPs")
|
|
2224
|
+
|
|
2225
|
+
|
|
2226
|
+
@_counted_wrapper
|
|
2227
|
+
def resize(*args, **kwargs):
|
|
2228
|
+
"""Return new array with given shape. Cost: numel(output)."""
|
|
2229
|
+
budget = require_budget()
|
|
2230
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
2231
|
+
result = _np.resize(*stripped_args, **kwargs)
|
|
2232
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
2233
|
+
with budget.deduct("resize", flop_cost=cost, subscripts=None, shapes=()):
|
|
2234
|
+
pass # numpy call already executed above
|
|
2235
|
+
return result
|
|
2236
|
+
|
|
2237
|
+
|
|
2238
|
+
attach_docstring(resize, _np.resize, "free", "0 FLOPs")
|
|
2239
|
+
|
|
2240
|
+
|
|
2241
|
+
def result_type(*args, **kwargs):
|
|
2242
|
+
"""Returns type that results from applying type promotion. Wraps ``numpy.result_type``. Cost: 0 FLOPs."""
|
|
2243
|
+
return _np.result_type(*args, **kwargs)
|
|
2244
|
+
|
|
2245
|
+
|
|
2246
|
+
attach_docstring(result_type, _np.result_type, "free", "0 FLOPs")
|
|
2247
|
+
|
|
2248
|
+
|
|
2249
|
+
@_counted_wrapper
|
|
2250
|
+
def rollaxis(*args, **kwargs):
|
|
2251
|
+
"""Roll specified axis backwards. Cost: numel(output)."""
|
|
2252
|
+
budget = require_budget()
|
|
2253
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
2254
|
+
result = _np.rollaxis(*stripped_args, **kwargs)
|
|
2255
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
2256
|
+
with budget.deduct("rollaxis", flop_cost=cost, subscripts=None, shapes=()):
|
|
2257
|
+
pass # numpy call already executed above
|
|
2258
|
+
return result
|
|
2259
|
+
|
|
2260
|
+
|
|
2261
|
+
attach_docstring(rollaxis, _np.rollaxis, "free", "0 FLOPs")
|
|
2262
|
+
|
|
2263
|
+
|
|
2264
|
+
def rot90(*args, **kwargs):
|
|
2265
|
+
"""Rotate array 90 degrees. Wraps ``numpy.rot90``. Cost: 0 FLOPs."""
|
|
2266
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
2267
|
+
return _np.rot90(*stripped_args, **kwargs)
|
|
2268
|
+
|
|
2269
|
+
|
|
2270
|
+
attach_docstring(rot90, _np.rot90, "free", "0 FLOPs")
|
|
2271
|
+
|
|
2272
|
+
|
|
2273
|
+
def row_stack(*args, **kwargs):
|
|
2274
|
+
"""Stack arrays vertically (alias for vstack). Wraps ``numpy.row_stack``. Cost: 0 FLOPs."""
|
|
2275
|
+
stripped_args = _to_base_ndarray_tree(args)
|
|
2276
|
+
return _np.row_stack(*stripped_args, **kwargs)
|
|
2277
|
+
|
|
2278
|
+
|
|
2279
|
+
attach_docstring(row_stack, _np.row_stack, "free", "0 FLOPs")
|
|
2280
|
+
|
|
2281
|
+
|
|
2282
|
+
@_counted_wrapper
|
|
2283
|
+
def select(
|
|
2284
|
+
condlist: Sequence[ArrayLike],
|
|
2285
|
+
choicelist: Sequence[ArrayLike],
|
|
2286
|
+
default: Any = 0,
|
|
2287
|
+
) -> FlopscopeArray:
|
|
2288
|
+
"""Return array drawn from elements depending on conditions. Cost: numel(input)."""
|
|
2289
|
+
budget = require_budget()
|
|
2290
|
+
# Cost based on the size of the choice arrays
|
|
2291
|
+
cost = max((_np.asarray(c).size for c in choicelist), default=1)
|
|
2292
|
+
with budget.deduct("select", flop_cost=cost, subscripts=None, shapes=()):
|
|
2293
|
+
result = _call_numpy(
|
|
2294
|
+
_np.select,
|
|
2295
|
+
_to_base_ndarray_tree(condlist), # type: ignore[arg-type]
|
|
2296
|
+
_to_base_ndarray_tree(choicelist), # type: ignore[arg-type]
|
|
2297
|
+
default=default,
|
|
2298
|
+
)
|
|
2299
|
+
return result # type: ignore[return-value]
|
|
2300
|
+
|
|
2301
|
+
|
|
2302
|
+
attach_docstring(select, _np.select, "free", "0 FLOPs")
|
|
2303
|
+
|
|
2304
|
+
|
|
2305
|
+
def shape(*args, **kwargs):
|
|
2306
|
+
"""Return shape of array. Wraps ``numpy.shape``. Cost: 0 FLOPs."""
|
|
2307
|
+
return _np.shape(*args, **kwargs)
|
|
2308
|
+
|
|
2309
|
+
|
|
2310
|
+
attach_docstring(shape, _np.shape, "free", "0 FLOPs")
|
|
2311
|
+
|
|
2312
|
+
|
|
2313
|
+
def shares_memory(*args, **kwargs):
|
|
2314
|
+
"""Determine if two arrays share memory. Wraps ``numpy.shares_memory``. Cost: 0 FLOPs."""
|
|
2315
|
+
return _np.shares_memory(*[_to_base_ndarray(a) for a in args], **kwargs) # type: ignore[arg-type]
|
|
2316
|
+
|
|
2317
|
+
|
|
2318
|
+
attach_docstring(shares_memory, _np.shares_memory, "free", "0 FLOPs")
|
|
2319
|
+
|
|
2320
|
+
|
|
2321
|
+
def size(*args, **kwargs):
|
|
2322
|
+
"""Return number of elements along a given axis. Wraps ``numpy.size``. Cost: 0 FLOPs."""
|
|
2323
|
+
return _np.size(*args, **kwargs)
|
|
2324
|
+
|
|
2325
|
+
|
|
2326
|
+
attach_docstring(size, _np.size, "free", "0 FLOPs")
|
|
2327
|
+
|
|
2328
|
+
|
|
2329
|
+
@_counted_wrapper
|
|
2330
|
+
def take(
|
|
2331
|
+
a: ArrayLike,
|
|
2332
|
+
indices: ArrayLike,
|
|
2333
|
+
axis: int | None = None,
|
|
2334
|
+
out: FlopscopeArray | None = None,
|
|
2335
|
+
mode: str = "raise",
|
|
2336
|
+
) -> FlopscopeArray:
|
|
2337
|
+
"""Take elements from array along axis. Cost: numel(output)."""
|
|
2338
|
+
budget = require_budget()
|
|
2339
|
+
result = _np.take(
|
|
2340
|
+
_to_base_ndarray(a),
|
|
2341
|
+
_to_base_ndarray(indices), # type: ignore[arg-type]
|
|
2342
|
+
axis=axis,
|
|
2343
|
+
out=_to_base_ndarray(out) if out is not None else None, # type: ignore[arg-type]
|
|
2344
|
+
mode=mode, # type: ignore[arg-type]
|
|
2345
|
+
)
|
|
2346
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
2347
|
+
with budget.deduct("take", flop_cost=cost, subscripts=None, shapes=()):
|
|
2348
|
+
pass # numpy call already executed above
|
|
2349
|
+
return result # type: ignore[return-value]
|
|
2350
|
+
|
|
2351
|
+
|
|
2352
|
+
attach_docstring(take, _np.take, "free", "0 FLOPs")
|
|
2353
|
+
|
|
2354
|
+
|
|
2355
|
+
@_counted_wrapper
|
|
2356
|
+
def take_along_axis(
|
|
2357
|
+
arr: ArrayLike,
|
|
2358
|
+
indices: ArrayLike,
|
|
2359
|
+
axis: int | None,
|
|
2360
|
+
) -> FlopscopeArray:
|
|
2361
|
+
"""Take values from input array along axis using indices. Cost: numel(output)."""
|
|
2362
|
+
budget = require_budget()
|
|
2363
|
+
result = _np.take_along_axis(
|
|
2364
|
+
_to_base_ndarray(arr), # type: ignore[arg-type]
|
|
2365
|
+
_to_base_ndarray(indices), # type: ignore[arg-type]
|
|
2366
|
+
axis=axis,
|
|
2367
|
+
)
|
|
2368
|
+
cost = result.size if hasattr(result, "size") else 1
|
|
2369
|
+
with budget.deduct("take_along_axis", flop_cost=cost, subscripts=None, shapes=()):
|
|
2370
|
+
pass # numpy call already executed above
|
|
2371
|
+
return result # type: ignore[return-value]
|
|
2372
|
+
|
|
2373
|
+
|
|
2374
|
+
attach_docstring(take_along_axis, _np.take_along_axis, "free", "0 FLOPs")
|
|
2375
|
+
|
|
2376
|
+
|
|
2377
|
+
def tri(*args, **kwargs):
|
|
2378
|
+
"""Array with ones at and below the given diagonal. Wraps ``numpy.tri``. Cost: 0 FLOPs."""
|
|
2379
|
+
return _np.tri(*args, **kwargs)
|
|
2380
|
+
|
|
2381
|
+
|
|
2382
|
+
attach_docstring(tri, _np.tri, "free", "0 FLOPs")
|
|
2383
|
+
|
|
2384
|
+
|
|
2385
|
+
def tril_indices(*args, **kwargs):
|
|
2386
|
+
"""Return indices for lower-triangle of array. Wraps ``numpy.tril_indices``. Cost: 0 FLOPs."""
|
|
2387
|
+
return _np.tril_indices(*args, **kwargs)
|
|
2388
|
+
|
|
2389
|
+
|
|
2390
|
+
attach_docstring(tril_indices, _np.tril_indices, "free", "0 FLOPs")
|
|
2391
|
+
|
|
2392
|
+
|
|
2393
|
+
def tril_indices_from(*args, **kwargs):
|
|
2394
|
+
"""Return indices for lower-triangle of given array. Wraps ``numpy.tril_indices_from``. Cost: 0 FLOPs."""
|
|
2395
|
+
return _np.tril_indices_from(*args, **kwargs)
|
|
2396
|
+
|
|
2397
|
+
|
|
2398
|
+
attach_docstring(tril_indices_from, _np.tril_indices_from, "free", "0 FLOPs")
|
|
2399
|
+
|
|
2400
|
+
|
|
2401
|
+
@_counted_wrapper
|
|
2402
|
+
def trim_zeros(filt: ArrayLike, trim: str = "fb", **kwargs: Any) -> FlopscopeArray:
|
|
2403
|
+
"""Trim leading and/or trailing zeros from 1-D array. Cost: num elements trimmed."""
|
|
2404
|
+
budget = require_budget()
|
|
2405
|
+
filt_arr = _np.asarray(filt)
|
|
2406
|
+
result = _np.trim_zeros(_to_base_ndarray(filt), trim=trim, **kwargs) # type: ignore[arg-type]
|
|
2407
|
+
result_arr = _np.asarray(result)
|
|
2408
|
+
cost = max(filt_arr.size - result_arr.size, 0) # num trimmed
|
|
2409
|
+
with budget.deduct("trim_zeros", flop_cost=cost, subscripts=None, shapes=()):
|
|
2410
|
+
pass # numpy call already executed above
|
|
2411
|
+
return result
|
|
2412
|
+
|
|
2413
|
+
|
|
2414
|
+
attach_docstring(trim_zeros, _np.trim_zeros, "free", "0 FLOPs")
|
|
2415
|
+
|
|
2416
|
+
|
|
2417
|
+
def triu_indices(*args, **kwargs):
|
|
2418
|
+
"""Return indices for upper-triangle of array. Wraps ``numpy.triu_indices``. Cost: 0 FLOPs."""
|
|
2419
|
+
return _np.triu_indices(*args, **kwargs)
|
|
2420
|
+
|
|
2421
|
+
|
|
2422
|
+
attach_docstring(triu_indices, _np.triu_indices, "free", "0 FLOPs")
|
|
2423
|
+
|
|
2424
|
+
|
|
2425
|
+
def triu_indices_from(*args, **kwargs):
|
|
2426
|
+
"""Return indices for upper-triangle of given array. Wraps ``numpy.triu_indices_from``. Cost: 0 FLOPs."""
|
|
2427
|
+
return _np.triu_indices_from(*args, **kwargs)
|
|
2428
|
+
|
|
2429
|
+
|
|
2430
|
+
attach_docstring(triu_indices_from, _np.triu_indices_from, "free", "0 FLOPs")
|
|
2431
|
+
|
|
2432
|
+
|
|
2433
|
+
def typename(*args, **kwargs):
|
|
2434
|
+
"""Return description for given data type code. Wraps ``numpy.typename``. Cost: 0 FLOPs."""
|
|
2435
|
+
return _np.typename(*args, **kwargs)
|
|
2436
|
+
|
|
2437
|
+
|
|
2438
|
+
attach_docstring(typename, _np.typename, "free", "0 FLOPs")
|
|
2439
|
+
|
|
2440
|
+
|
|
2441
|
+
@_counted_wrapper
|
|
2442
|
+
def unpackbits(a: ArrayLike, *args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
2443
|
+
"""Unpack elements of uint8 array into binary-valued bit array. Cost: numel(output)."""
|
|
2444
|
+
budget = require_budget()
|
|
2445
|
+
result = _np.unpackbits(_to_base_ndarray(a), *args, **kwargs) # type: ignore[arg-type]
|
|
2446
|
+
cost = (
|
|
2447
|
+
result.size
|
|
2448
|
+
if hasattr(result, "size")
|
|
2449
|
+
else len(result)
|
|
2450
|
+
if hasattr(result, "__len__")
|
|
2451
|
+
else 1
|
|
2452
|
+
)
|
|
2453
|
+
with budget.deduct(
|
|
2454
|
+
"unpackbits", flop_cost=cost, subscripts=None, shapes=(result.shape,)
|
|
2455
|
+
):
|
|
2456
|
+
pass # numpy call already executed above
|
|
2457
|
+
return result # type: ignore[return-value]
|
|
2458
|
+
|
|
2459
|
+
|
|
2460
|
+
attach_docstring(unpackbits, _np.unpackbits, "free", "0 FLOPs")
|
|
2461
|
+
|
|
2462
|
+
|
|
2463
|
+
def unravel_index(*args, **kwargs):
|
|
2464
|
+
"""Convert flat indices to multi-dimensional index. Wraps ``numpy.unravel_index``. Cost: 0 FLOPs."""
|
|
2465
|
+
return _np.unravel_index(*args, **kwargs)
|
|
2466
|
+
|
|
2467
|
+
|
|
2468
|
+
attach_docstring(unravel_index, _np.unravel_index, "free", "0 FLOPs")
|
|
2469
|
+
|
|
2470
|
+
|
|
2471
|
+
if hasattr(_np, "unstack"):
|
|
2472
|
+
|
|
2473
|
+
@_counted_wrapper
|
|
2474
|
+
def unstack(x: ArrayLike, *args: Any, **kwargs: Any) -> tuple[FlopscopeArray, ...]: # pyright: ignore[reportRedeclaration]
|
|
2475
|
+
"""Split array into sequence of arrays along an axis. Cost: numel(input)."""
|
|
2476
|
+
budget = require_budget()
|
|
2477
|
+
x_arr = _np.asarray(x)
|
|
2478
|
+
cost = x_arr.size
|
|
2479
|
+
with budget.deduct(
|
|
2480
|
+
"unstack", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)
|
|
2481
|
+
):
|
|
2482
|
+
result = _call_numpy(_np.unstack, _to_base_ndarray(x), *args, **kwargs)
|
|
2483
|
+
return result # type: ignore[return-value]
|
|
2484
|
+
|
|
2485
|
+
attach_docstring(unstack, _np.unstack, "free", "0 FLOPs")
|
|
2486
|
+
|
|
2487
|
+
else:
|
|
2488
|
+
|
|
2489
|
+
def unstack(*args: Any, **kwargs: Any) -> tuple[FlopscopeArray, ...]: # pyright: ignore[reportRedeclaration]
|
|
2490
|
+
raise UnsupportedFunctionError("unstack", min_version="2.1")
|
|
2491
|
+
|
|
2492
|
+
|
|
2493
|
+
# ---------------------------------------------------------------------------
|
|
2494
|
+
# Wrap all free op return values as FlopscopeArray
|
|
2495
|
+
# ---------------------------------------------------------------------------
|
|
2496
|
+
|
|
2497
|
+
from flopscope._ndarray import wrap_module_returns as _wrap_module_returns # noqa: E402
|
|
2498
|
+
|
|
2499
|
+
_FREE_OPS_SKIP = {
|
|
2500
|
+
"shape",
|
|
2501
|
+
"size",
|
|
2502
|
+
"ndim",
|
|
2503
|
+
"isscalar",
|
|
2504
|
+
"isfortran",
|
|
2505
|
+
"isfinite",
|
|
2506
|
+
"isinf",
|
|
2507
|
+
"isnan",
|
|
2508
|
+
"isdtype",
|
|
2509
|
+
"issubdtype",
|
|
2510
|
+
"iscomplex",
|
|
2511
|
+
"iscomplexobj",
|
|
2512
|
+
"isnat",
|
|
2513
|
+
"isneginf",
|
|
2514
|
+
"isposinf",
|
|
2515
|
+
"isreal",
|
|
2516
|
+
"isrealobj",
|
|
2517
|
+
"iterable",
|
|
2518
|
+
"may_share_memory",
|
|
2519
|
+
"shares_memory",
|
|
2520
|
+
"can_cast",
|
|
2521
|
+
"common_type",
|
|
2522
|
+
"min_scalar_type",
|
|
2523
|
+
"promote_types",
|
|
2524
|
+
"result_type",
|
|
2525
|
+
"typename",
|
|
2526
|
+
"base_repr",
|
|
2527
|
+
"binary_repr",
|
|
2528
|
+
"broadcast_shapes",
|
|
2529
|
+
"fill_diagonal",
|
|
2530
|
+
}
|
|
2531
|
+
|
|
2532
|
+
import sys as _sys # noqa: E402
|
|
2533
|
+
|
|
2534
|
+
# ---------------------------------------------------------------------------
|
|
2535
|
+
# Signature conformance: set __signature__ to match numpy exactly
|
|
2536
|
+
# ---------------------------------------------------------------------------
|
|
2537
|
+
_this_module = _sys.modules[__name__]
|
|
2538
|
+
|
|
2539
|
+
|
|
2540
|
+
def _set_sig(func_name, np_func):
|
|
2541
|
+
"""Set __signature__ of a module-level function to match numpy."""
|
|
2542
|
+
fn = globals().get(func_name)
|
|
2543
|
+
if fn is not None and callable(np_func):
|
|
2544
|
+
try:
|
|
2545
|
+
fn.__signature__ = _inspect.signature(np_func) # pyright: ignore[reportFunctionMemberAccess]
|
|
2546
|
+
except (ValueError, TypeError):
|
|
2547
|
+
pass
|
|
2548
|
+
|
|
2549
|
+
|
|
2550
|
+
# Functions with *args/**kwargs that need numpy's signature
|
|
2551
|
+
_set_sig("arange", _np.arange)
|
|
2552
|
+
_set_sig("array", _np.array)
|
|
2553
|
+
_set_sig("zeros", _np.zeros)
|
|
2554
|
+
_set_sig("ones", _np.ones)
|
|
2555
|
+
_set_sig("full", _np.full)
|
|
2556
|
+
_set_sig("eye", _np.eye)
|
|
2557
|
+
_set_sig("linspace", _np.linspace)
|
|
2558
|
+
_set_sig("zeros_like", _np.zeros_like)
|
|
2559
|
+
_set_sig("ones_like", _np.ones_like)
|
|
2560
|
+
_set_sig("full_like", _np.full_like)
|
|
2561
|
+
_set_sig("empty", _np.empty)
|
|
2562
|
+
_set_sig("empty_like", _np.empty_like)
|
|
2563
|
+
_set_sig("identity", _np.identity)
|
|
2564
|
+
_set_sig("reshape", _np.reshape)
|
|
2565
|
+
_set_sig("concatenate", _np.concatenate)
|
|
2566
|
+
_set_sig("stack", _np.stack)
|
|
2567
|
+
_set_sig("vstack", _np.vstack)
|
|
2568
|
+
_set_sig("hstack", _np.hstack)
|
|
2569
|
+
_set_sig("ravel", _np.ravel)
|
|
2570
|
+
_set_sig("copy", _np.copy)
|
|
2571
|
+
_set_sig("pad", _np.pad)
|
|
2572
|
+
_set_sig("broadcast_to", _np.broadcast_to)
|
|
2573
|
+
_set_sig("meshgrid", _np.meshgrid)
|
|
2574
|
+
_set_sig("asarray", _np.asarray)
|
|
2575
|
+
_set_sig("astype", _np.astype)
|
|
2576
|
+
_set_sig("append", _np.append)
|
|
2577
|
+
_set_sig("argwhere", _np.argwhere)
|
|
2578
|
+
_set_sig("array_split", _np.array_split)
|
|
2579
|
+
_set_sig("asarray_chkfinite", _np.asarray_chkfinite)
|
|
2580
|
+
_set_sig("atleast_1d", _np.atleast_1d)
|
|
2581
|
+
_set_sig("atleast_2d", _np.atleast_2d)
|
|
2582
|
+
_set_sig("atleast_3d", _np.atleast_3d)
|
|
2583
|
+
_set_sig("base_repr", _np.base_repr)
|
|
2584
|
+
_set_sig("binary_repr", _np.binary_repr)
|
|
2585
|
+
_set_sig("block", _np.block)
|
|
2586
|
+
_set_sig("bmat", _np.bmat)
|
|
2587
|
+
_set_sig("broadcast_arrays", _np.broadcast_arrays)
|
|
2588
|
+
_set_sig("broadcast_shapes", _np.broadcast_shapes)
|
|
2589
|
+
_set_sig("can_cast", _np.can_cast)
|
|
2590
|
+
_set_sig("choose", _np.choose)
|
|
2591
|
+
_set_sig("column_stack", _np.column_stack)
|
|
2592
|
+
_set_sig("common_type", _np.common_type)
|
|
2593
|
+
_set_sig("compress", _np.compress)
|
|
2594
|
+
_set_sig("concat", _np.concat)
|
|
2595
|
+
_set_sig("delete", _np.delete)
|
|
2596
|
+
_set_sig("diag_indices", _np.diag_indices)
|
|
2597
|
+
_set_sig("diag_indices_from", _np.diag_indices_from)
|
|
2598
|
+
_set_sig("dsplit", _np.dsplit)
|
|
2599
|
+
_set_sig("dstack", _np.dstack)
|
|
2600
|
+
_set_sig("extract", _np.extract)
|
|
2601
|
+
_set_sig("flatnonzero", _np.flatnonzero)
|
|
2602
|
+
_set_sig("fliplr", _np.fliplr)
|
|
2603
|
+
_set_sig("flipud", _np.flipud)
|
|
2604
|
+
_set_sig("from_dlpack", _np.from_dlpack)
|
|
2605
|
+
_set_sig("frombuffer", _np.frombuffer)
|
|
2606
|
+
_set_sig("fromfile", _np.fromfile)
|
|
2607
|
+
_set_sig("fromfunction", _np.fromfunction)
|
|
2608
|
+
_set_sig("fromiter", _np.fromiter)
|
|
2609
|
+
_set_sig("fromregex", _np.fromregex)
|
|
2610
|
+
_set_sig("fromstring", _np.fromstring)
|
|
2611
|
+
_set_sig("indices", _np.indices)
|
|
2612
|
+
_set_sig("insert", _np.insert)
|
|
2613
|
+
_set_sig("isdtype", _np.isdtype)
|
|
2614
|
+
_set_sig("isfortran", _np.isfortran)
|
|
2615
|
+
_set_sig("isin", _np.isin)
|
|
2616
|
+
_set_sig("isnan", _np.isnan)
|
|
2617
|
+
_set_sig("isfinite", _np.isfinite)
|
|
2618
|
+
_set_sig("isinf", _np.isinf)
|
|
2619
|
+
_set_sig("isscalar", _np.isscalar)
|
|
2620
|
+
_set_sig("issubdtype", _np.issubdtype)
|
|
2621
|
+
_set_sig("iterable", _np.iterable)
|
|
2622
|
+
_set_sig("ix_", _np.ix_)
|
|
2623
|
+
_set_sig("mask_indices", _np.mask_indices)
|
|
2624
|
+
_set_sig("matrix_transpose", _np.matrix_transpose)
|
|
2625
|
+
_set_sig("may_share_memory", _np.may_share_memory)
|
|
2626
|
+
_set_sig("min_scalar_type", _np.min_scalar_type)
|
|
2627
|
+
_set_sig("mintypecode", _np.mintypecode)
|
|
2628
|
+
_set_sig("ndim", _np.ndim)
|
|
2629
|
+
_set_sig("nonzero", _np.nonzero)
|
|
2630
|
+
_set_sig("permute_dims", _np.permute_dims)
|
|
2631
|
+
_set_sig("put", _np.put)
|
|
2632
|
+
_set_sig("require", _np.require)
|
|
2633
|
+
_set_sig("resize", _np.resize)
|
|
2634
|
+
_set_sig("rollaxis", _np.rollaxis)
|
|
2635
|
+
_set_sig("rot90", _np.rot90)
|
|
2636
|
+
_set_sig("row_stack", _np.row_stack)
|
|
2637
|
+
_set_sig("shape", _np.shape)
|
|
2638
|
+
_set_sig("size", _np.size)
|
|
2639
|
+
_set_sig("take", _np.take)
|
|
2640
|
+
_set_sig("take_along_axis", _np.take_along_axis)
|
|
2641
|
+
_set_sig("tri", _np.tri)
|
|
2642
|
+
_set_sig("tril_indices", _np.tril_indices)
|
|
2643
|
+
_set_sig("tril_indices_from", _np.tril_indices_from)
|
|
2644
|
+
_set_sig("trim_zeros", _np.trim_zeros)
|
|
2645
|
+
_set_sig("triu_indices", _np.triu_indices)
|
|
2646
|
+
_set_sig("triu_indices_from", _np.triu_indices_from)
|
|
2647
|
+
_set_sig("typename", _np.typename)
|
|
2648
|
+
_set_sig("unravel_index", _np.unravel_index)
|
|
2649
|
+
if hasattr(_np, "unstack"):
|
|
2650
|
+
_set_sig("unstack", _np.unstack)
|
|
2651
|
+
|
|
2652
|
+
del _set_sig, _this_module
|
|
2653
|
+
|
|
2654
|
+
_wrap_module_returns(_sys.modules[__name__], skip_names=_FREE_OPS_SKIP)
|