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.
Files changed (115) hide show
  1. benchmarks/__init__.py +1 -0
  2. benchmarks/__main__.py +6 -0
  3. benchmarks/_baseline.py +171 -0
  4. benchmarks/_bitwise.py +231 -0
  5. benchmarks/_complex.py +176 -0
  6. benchmarks/_contractions.py +291 -0
  7. benchmarks/_fft.py +198 -0
  8. benchmarks/_impl_urls.py +139 -0
  9. benchmarks/_linalg.py +197 -0
  10. benchmarks/_linalg_delegates.py +407 -0
  11. benchmarks/_metadata.py +141 -0
  12. benchmarks/_misc.py +653 -0
  13. benchmarks/_perf.py +321 -0
  14. benchmarks/_perm_group_calibration.py +175 -0
  15. benchmarks/_pointwise.py +372 -0
  16. benchmarks/_polynomial.py +193 -0
  17. benchmarks/_random.py +209 -0
  18. benchmarks/_reductions.py +136 -0
  19. benchmarks/_sorting.py +289 -0
  20. benchmarks/_stats.py +137 -0
  21. benchmarks/_window.py +92 -0
  22. benchmarks/accumulation/__init__.py +0 -0
  23. benchmarks/accumulation/bench_cost_compute.py +138 -0
  24. benchmarks/dashboard.py +312 -0
  25. benchmarks/runner.py +636 -0
  26. flopscope/__init__.py +273 -0
  27. flopscope/_accumulation/__init__.py +13 -0
  28. flopscope/_accumulation/_bipartite.py +121 -0
  29. flopscope/_accumulation/_burnside.py +51 -0
  30. flopscope/_accumulation/_cache.py +146 -0
  31. flopscope/_accumulation/_components.py +153 -0
  32. flopscope/_accumulation/_cost.py +1414 -0
  33. flopscope/_accumulation/_cost_descriptions.py +63 -0
  34. flopscope/_accumulation/_detection.py +318 -0
  35. flopscope/_accumulation/_ladder.py +191 -0
  36. flopscope/_accumulation/_output_orbit.py +104 -0
  37. flopscope/_accumulation/_partition.py +290 -0
  38. flopscope/_accumulation/_path_info.py +211 -0
  39. flopscope/_accumulation/_public.py +169 -0
  40. flopscope/_accumulation/_reduction.py +310 -0
  41. flopscope/_accumulation/_regimes.py +303 -0
  42. flopscope/_accumulation/_shape.py +33 -0
  43. flopscope/_accumulation/_wreath.py +209 -0
  44. flopscope/_budget.py +1027 -0
  45. flopscope/_config.py +118 -0
  46. flopscope/_counting_ops.py +451 -0
  47. flopscope/_display.py +478 -0
  48. flopscope/_docstrings.py +59 -0
  49. flopscope/_dtypes.py +20 -0
  50. flopscope/_einsum.py +717 -0
  51. flopscope/_errstate.py +25 -0
  52. flopscope/_flops.py +282 -0
  53. flopscope/_free_ops.py +2654 -0
  54. flopscope/_ndarray.py +1126 -0
  55. flopscope/_opt_einsum/LICENSE +21 -0
  56. flopscope/_opt_einsum/NOTICE +59 -0
  57. flopscope/_opt_einsum/__init__.py +209 -0
  58. flopscope/_opt_einsum/_contract.py +1478 -0
  59. flopscope/_opt_einsum/_helpers.py +164 -0
  60. flopscope/_opt_einsum/_hsluv.py +273 -0
  61. flopscope/_opt_einsum/_path_random.py +462 -0
  62. flopscope/_opt_einsum/_paths.py +1653 -0
  63. flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
  64. flopscope/_opt_einsum/_symmetry.py +140 -0
  65. flopscope/_opt_einsum/_typing.py +37 -0
  66. flopscope/_perm_group.py +717 -0
  67. flopscope/_pointwise.py +2522 -0
  68. flopscope/_polynomial.py +278 -0
  69. flopscope/_registry.py +3216 -0
  70. flopscope/_sorting_ops.py +571 -0
  71. flopscope/_symmetric.py +812 -0
  72. flopscope/_symmetry_transport.py +510 -0
  73. flopscope/_symmetry_utils.py +669 -0
  74. flopscope/_type_info.py +12 -0
  75. flopscope/_unwrap.py +70 -0
  76. flopscope/_validation.py +83 -0
  77. flopscope/_version_check.py +46 -0
  78. flopscope/_weights.py +195 -0
  79. flopscope/_window.py +177 -0
  80. flopscope/accounting.py +565 -0
  81. flopscope/data/default_weights.json +462 -0
  82. flopscope/data/weights.csv +509 -0
  83. flopscope/errors.py +197 -0
  84. flopscope/numpy/__init__.py +878 -0
  85. flopscope/numpy/fft/__init__.py +55 -0
  86. flopscope/numpy/fft/_free.py +51 -0
  87. flopscope/numpy/fft/_transforms.py +695 -0
  88. flopscope/numpy/linalg/__init__.py +105 -0
  89. flopscope/numpy/linalg/_aliases.py +126 -0
  90. flopscope/numpy/linalg/_compound.py +161 -0
  91. flopscope/numpy/linalg/_decompositions.py +353 -0
  92. flopscope/numpy/linalg/_properties.py +533 -0
  93. flopscope/numpy/linalg/_solvers.py +444 -0
  94. flopscope/numpy/linalg/_svd.py +122 -0
  95. flopscope/numpy/random/__init__.py +684 -0
  96. flopscope/numpy/random/_cost_formulas.py +115 -0
  97. flopscope/numpy/random/_counted_classes.py +241 -0
  98. flopscope/numpy/testing/__init__.py +13 -0
  99. flopscope/numpy/typing/__init__.py +30 -0
  100. flopscope/py.typed +0 -0
  101. flopscope/stats/__init__.py +84 -0
  102. flopscope/stats/_base.py +77 -0
  103. flopscope/stats/_cauchy.py +146 -0
  104. flopscope/stats/_erf.py +190 -0
  105. flopscope/stats/_expon.py +146 -0
  106. flopscope/stats/_laplace.py +150 -0
  107. flopscope/stats/_logistic.py +148 -0
  108. flopscope/stats/_lognorm.py +160 -0
  109. flopscope/stats/_ndtri.py +133 -0
  110. flopscope/stats/_norm.py +149 -0
  111. flopscope/stats/_truncnorm.py +186 -0
  112. flopscope/stats/_uniform.py +141 -0
  113. flopscope-0.2.0.dist-info/METADATA +23 -0
  114. flopscope-0.2.0.dist-info/RECORD +115 -0
  115. 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)