flopscope 0.2.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- benchmarks/__init__.py +1 -0
- benchmarks/__main__.py +6 -0
- benchmarks/_baseline.py +171 -0
- benchmarks/_bitwise.py +231 -0
- benchmarks/_complex.py +176 -0
- benchmarks/_contractions.py +291 -0
- benchmarks/_fft.py +198 -0
- benchmarks/_impl_urls.py +139 -0
- benchmarks/_linalg.py +197 -0
- benchmarks/_linalg_delegates.py +407 -0
- benchmarks/_metadata.py +141 -0
- benchmarks/_misc.py +653 -0
- benchmarks/_perf.py +321 -0
- benchmarks/_perm_group_calibration.py +175 -0
- benchmarks/_pointwise.py +372 -0
- benchmarks/_polynomial.py +193 -0
- benchmarks/_random.py +209 -0
- benchmarks/_reductions.py +136 -0
- benchmarks/_sorting.py +289 -0
- benchmarks/_stats.py +137 -0
- benchmarks/_window.py +92 -0
- benchmarks/accumulation/__init__.py +0 -0
- benchmarks/accumulation/bench_cost_compute.py +138 -0
- benchmarks/dashboard.py +312 -0
- benchmarks/runner.py +636 -0
- flopscope/__init__.py +273 -0
- flopscope/_accumulation/__init__.py +13 -0
- flopscope/_accumulation/_bipartite.py +121 -0
- flopscope/_accumulation/_burnside.py +51 -0
- flopscope/_accumulation/_cache.py +146 -0
- flopscope/_accumulation/_components.py +153 -0
- flopscope/_accumulation/_cost.py +1414 -0
- flopscope/_accumulation/_cost_descriptions.py +63 -0
- flopscope/_accumulation/_detection.py +318 -0
- flopscope/_accumulation/_ladder.py +191 -0
- flopscope/_accumulation/_output_orbit.py +104 -0
- flopscope/_accumulation/_partition.py +290 -0
- flopscope/_accumulation/_path_info.py +211 -0
- flopscope/_accumulation/_public.py +169 -0
- flopscope/_accumulation/_reduction.py +310 -0
- flopscope/_accumulation/_regimes.py +303 -0
- flopscope/_accumulation/_shape.py +33 -0
- flopscope/_accumulation/_wreath.py +209 -0
- flopscope/_budget.py +1027 -0
- flopscope/_config.py +118 -0
- flopscope/_counting_ops.py +451 -0
- flopscope/_display.py +478 -0
- flopscope/_docstrings.py +59 -0
- flopscope/_dtypes.py +20 -0
- flopscope/_einsum.py +717 -0
- flopscope/_errstate.py +25 -0
- flopscope/_flops.py +282 -0
- flopscope/_free_ops.py +2654 -0
- flopscope/_ndarray.py +1126 -0
- flopscope/_opt_einsum/LICENSE +21 -0
- flopscope/_opt_einsum/NOTICE +59 -0
- flopscope/_opt_einsum/__init__.py +209 -0
- flopscope/_opt_einsum/_contract.py +1478 -0
- flopscope/_opt_einsum/_helpers.py +164 -0
- flopscope/_opt_einsum/_hsluv.py +273 -0
- flopscope/_opt_einsum/_path_random.py +462 -0
- flopscope/_opt_einsum/_paths.py +1653 -0
- flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
- flopscope/_opt_einsum/_symmetry.py +140 -0
- flopscope/_opt_einsum/_typing.py +37 -0
- flopscope/_perm_group.py +717 -0
- flopscope/_pointwise.py +2522 -0
- flopscope/_polynomial.py +278 -0
- flopscope/_registry.py +3216 -0
- flopscope/_sorting_ops.py +571 -0
- flopscope/_symmetric.py +812 -0
- flopscope/_symmetry_transport.py +510 -0
- flopscope/_symmetry_utils.py +669 -0
- flopscope/_type_info.py +12 -0
- flopscope/_unwrap.py +70 -0
- flopscope/_validation.py +83 -0
- flopscope/_version_check.py +46 -0
- flopscope/_weights.py +195 -0
- flopscope/_window.py +177 -0
- flopscope/accounting.py +565 -0
- flopscope/data/default_weights.json +462 -0
- flopscope/data/weights.csv +509 -0
- flopscope/errors.py +197 -0
- flopscope/numpy/__init__.py +878 -0
- flopscope/numpy/fft/__init__.py +55 -0
- flopscope/numpy/fft/_free.py +51 -0
- flopscope/numpy/fft/_transforms.py +695 -0
- flopscope/numpy/linalg/__init__.py +105 -0
- flopscope/numpy/linalg/_aliases.py +126 -0
- flopscope/numpy/linalg/_compound.py +161 -0
- flopscope/numpy/linalg/_decompositions.py +353 -0
- flopscope/numpy/linalg/_properties.py +533 -0
- flopscope/numpy/linalg/_solvers.py +444 -0
- flopscope/numpy/linalg/_svd.py +122 -0
- flopscope/numpy/random/__init__.py +684 -0
- flopscope/numpy/random/_cost_formulas.py +115 -0
- flopscope/numpy/random/_counted_classes.py +241 -0
- flopscope/numpy/testing/__init__.py +13 -0
- flopscope/numpy/typing/__init__.py +30 -0
- flopscope/py.typed +0 -0
- flopscope/stats/__init__.py +84 -0
- flopscope/stats/_base.py +77 -0
- flopscope/stats/_cauchy.py +146 -0
- flopscope/stats/_erf.py +190 -0
- flopscope/stats/_expon.py +146 -0
- flopscope/stats/_laplace.py +150 -0
- flopscope/stats/_logistic.py +148 -0
- flopscope/stats/_lognorm.py +160 -0
- flopscope/stats/_ndtri.py +133 -0
- flopscope/stats/_norm.py +149 -0
- flopscope/stats/_truncnorm.py +186 -0
- flopscope/stats/_uniform.py +141 -0
- flopscope-0.2.0.dist-info/METADATA +23 -0
- flopscope-0.2.0.dist-info/RECORD +115 -0
- flopscope-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,571 @@
|
|
|
1
|
+
"""Counted wrappers for sorting, search, and set operations."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import inspect as _inspect
|
|
6
|
+
from collections.abc import Sequence
|
|
7
|
+
from typing import Any
|
|
8
|
+
|
|
9
|
+
import numpy as _np
|
|
10
|
+
from numpy.typing import ArrayLike
|
|
11
|
+
|
|
12
|
+
from flopscope._budget import _call_numpy, _counted_wrapper
|
|
13
|
+
from flopscope._docstrings import attach_docstring
|
|
14
|
+
from flopscope._flops import search_cost, sort_cost
|
|
15
|
+
from flopscope._ndarray import FlopscopeArray, _to_base_ndarray, _to_base_ndarray_tree
|
|
16
|
+
from flopscope._validation import require_budget
|
|
17
|
+
from flopscope.errors import UnsupportedFunctionError
|
|
18
|
+
|
|
19
|
+
# Numpy 2.3+ relaxed the sort guarantee for string / complex unique;
|
|
20
|
+
# this module shims the guarantee back for flopscope callers.
|
|
21
|
+
_NUMPY_GE_2_3 = tuple(int(x) for x in _np.__version__.split(".")[:2]) >= (2, 3)
|
|
22
|
+
|
|
23
|
+
# dtype kinds where numpy 2.3+ may drop the sort guarantee.
|
|
24
|
+
# Values are numpy dtype.kind codes:
|
|
25
|
+
# U = unicode string, S = bytes string, O = object,
|
|
26
|
+
# c = complex float (both complex64 and complex128).
|
|
27
|
+
_UNSORTED_IN_NP_2_3 = frozenset("USOc")
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _sort_cost_nd(a: _np.ndarray, axis: int) -> int:
|
|
31
|
+
"""Total sort cost for an n-d array sorted along *axis*.
|
|
32
|
+
|
|
33
|
+
Total cost = num_slices * sort_cost(n) where
|
|
34
|
+
num_slices = numel(a) / a.shape[axis].
|
|
35
|
+
"""
|
|
36
|
+
n = a.shape[axis]
|
|
37
|
+
numel = 1
|
|
38
|
+
for d in a.shape:
|
|
39
|
+
numel *= d
|
|
40
|
+
num_slices = numel // n if n > 0 else 1
|
|
41
|
+
return max(num_slices * sort_cost(n), 1)
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
# ---------------------------------------------------------------------------
|
|
45
|
+
# Sorting
|
|
46
|
+
# ---------------------------------------------------------------------------
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
@_counted_wrapper
|
|
50
|
+
def sort(
|
|
51
|
+
a: ArrayLike,
|
|
52
|
+
axis: int | None = -1,
|
|
53
|
+
kind: str | None = None,
|
|
54
|
+
order: str | Sequence[str] | None = None,
|
|
55
|
+
**kwargs: Any,
|
|
56
|
+
) -> FlopscopeArray:
|
|
57
|
+
"""Counted version of ``numpy.sort``. Cost: n*ceil(log2(n)) FLOPs per slice."""
|
|
58
|
+
budget = require_budget()
|
|
59
|
+
if not isinstance(a, _np.ndarray):
|
|
60
|
+
a = _np.asarray(a)
|
|
61
|
+
if a.ndim == 0:
|
|
62
|
+
cost = 1
|
|
63
|
+
elif axis is None:
|
|
64
|
+
cost = sort_cost(a.size)
|
|
65
|
+
else:
|
|
66
|
+
ax = axis % a.ndim
|
|
67
|
+
cost = _sort_cost_nd(a, ax)
|
|
68
|
+
with budget.deduct("sort", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
69
|
+
result = _call_numpy(_np.sort, _to_base_ndarray(a), axis=axis, **kwargs)
|
|
70
|
+
return result # type: ignore[return-value]
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
attach_docstring(sort, _np.sort, "counted_custom", "n*ceil(log2(n)) FLOPs per slice")
|
|
74
|
+
sort.__signature__ = _inspect.signature(_np.sort) # type: ignore[attr-defined]
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
@_counted_wrapper
|
|
78
|
+
def argsort(
|
|
79
|
+
a: ArrayLike,
|
|
80
|
+
axis: int | None = -1,
|
|
81
|
+
**kwargs: Any,
|
|
82
|
+
) -> FlopscopeArray:
|
|
83
|
+
"""Counted version of ``numpy.argsort``. Cost: n*ceil(log2(n)) FLOPs per slice."""
|
|
84
|
+
budget = require_budget()
|
|
85
|
+
if not isinstance(a, _np.ndarray):
|
|
86
|
+
a = _np.asarray(a)
|
|
87
|
+
if a.ndim == 0:
|
|
88
|
+
cost = 1
|
|
89
|
+
elif axis is None:
|
|
90
|
+
cost = sort_cost(a.size)
|
|
91
|
+
else:
|
|
92
|
+
ax = axis % a.ndim
|
|
93
|
+
cost = _sort_cost_nd(a, ax)
|
|
94
|
+
with budget.deduct("argsort", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
95
|
+
result = _call_numpy(_np.argsort, _to_base_ndarray(a), axis=axis, **kwargs)
|
|
96
|
+
return result # type: ignore[return-value]
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
attach_docstring(
|
|
100
|
+
argsort, _np.argsort, "counted_custom", "n*ceil(log2(n)) FLOPs per slice"
|
|
101
|
+
)
|
|
102
|
+
argsort.__signature__ = _inspect.signature(_np.argsort) # type: ignore[attr-defined]
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@_counted_wrapper
|
|
106
|
+
def lexsort(keys: Sequence[ArrayLike], axis: int = -1) -> FlopscopeArray:
|
|
107
|
+
"""Counted version of ``numpy.lexsort``.
|
|
108
|
+
|
|
109
|
+
Cost: k * sort_cost(n) where k = len(keys) and n = len(keys[0]).
|
|
110
|
+
"""
|
|
111
|
+
budget = require_budget()
|
|
112
|
+
# keys is a sequence of arrays; convert to list for inspection
|
|
113
|
+
keys_list = list(keys)
|
|
114
|
+
k = len(keys_list)
|
|
115
|
+
if k == 0:
|
|
116
|
+
cost = 1
|
|
117
|
+
else:
|
|
118
|
+
# numpy.lexsort uses the last key as primary; length is shape along axis
|
|
119
|
+
first = _np.asarray(keys_list[0])
|
|
120
|
+
n = first.shape[axis] if first.ndim > 0 else 1
|
|
121
|
+
cost = max(k * sort_cost(n), 1)
|
|
122
|
+
shapes = tuple(_np.asarray(key).shape for key in keys_list)
|
|
123
|
+
with budget.deduct("lexsort", flop_cost=cost, subscripts=None, shapes=shapes):
|
|
124
|
+
result = _call_numpy(_np.lexsort, _to_base_ndarray_tree(keys_list), axis=axis)
|
|
125
|
+
return result
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
attach_docstring(lexsort, _np.lexsort, "counted_custom", "k*n*ceil(log2(n)) FLOPs")
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
@_counted_wrapper
|
|
132
|
+
def partition(
|
|
133
|
+
a: ArrayLike,
|
|
134
|
+
kth: int | Sequence[int],
|
|
135
|
+
axis: int | None = -1,
|
|
136
|
+
**kwargs: Any,
|
|
137
|
+
) -> FlopscopeArray:
|
|
138
|
+
"""Counted version of ``numpy.partition``. Cost: n * len(kth) per slice."""
|
|
139
|
+
budget = require_budget()
|
|
140
|
+
if not isinstance(a, _np.ndarray):
|
|
141
|
+
a = _np.asarray(a)
|
|
142
|
+
# kth can be int or sequence of ints
|
|
143
|
+
kth_count = len(kth) if hasattr(kth, "__len__") else 1 # type: ignore[arg-type]
|
|
144
|
+
if a.ndim == 0:
|
|
145
|
+
cost = 1
|
|
146
|
+
else:
|
|
147
|
+
ax = axis if axis is not None else -1
|
|
148
|
+
ax = ax % a.ndim
|
|
149
|
+
n = a.shape[ax]
|
|
150
|
+
numel = 1
|
|
151
|
+
for d in a.shape:
|
|
152
|
+
numel *= d
|
|
153
|
+
num_slices = numel // n if n > 0 else 1
|
|
154
|
+
cost = max(num_slices * n * kth_count, 1)
|
|
155
|
+
with budget.deduct("partition", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
|
|
156
|
+
result = _call_numpy(
|
|
157
|
+
_np.partition, _to_base_ndarray(a), kth, axis=axis, **kwargs
|
|
158
|
+
)
|
|
159
|
+
return result # type: ignore[return-value]
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
attach_docstring(
|
|
163
|
+
partition, _np.partition, "counted_custom", "n FLOPs per slice (linear quickselect)"
|
|
164
|
+
)
|
|
165
|
+
partition.__signature__ = _inspect.signature(_np.partition) # type: ignore[attr-defined]
|
|
166
|
+
|
|
167
|
+
|
|
168
|
+
@_counted_wrapper
|
|
169
|
+
def argpartition(
|
|
170
|
+
a: ArrayLike,
|
|
171
|
+
kth: int | Sequence[int],
|
|
172
|
+
axis: int | None = -1,
|
|
173
|
+
**kwargs: Any,
|
|
174
|
+
) -> FlopscopeArray:
|
|
175
|
+
"""Counted version of ``numpy.argpartition``. Cost: n * len(kth) per slice."""
|
|
176
|
+
budget = require_budget()
|
|
177
|
+
if not isinstance(a, _np.ndarray):
|
|
178
|
+
a = _np.asarray(a)
|
|
179
|
+
# kth can be int or sequence of ints
|
|
180
|
+
kth_count = len(kth) if hasattr(kth, "__len__") else 1 # type: ignore[arg-type]
|
|
181
|
+
if a.ndim == 0:
|
|
182
|
+
cost = 1
|
|
183
|
+
else:
|
|
184
|
+
ax = axis if axis is not None else -1
|
|
185
|
+
ax = ax % a.ndim
|
|
186
|
+
n = a.shape[ax]
|
|
187
|
+
numel = 1
|
|
188
|
+
for d in a.shape:
|
|
189
|
+
numel *= d
|
|
190
|
+
num_slices = numel // n if n > 0 else 1
|
|
191
|
+
cost = max(num_slices * n * kth_count, 1)
|
|
192
|
+
with budget.deduct(
|
|
193
|
+
"argpartition", flop_cost=cost, subscripts=None, shapes=(a.shape,)
|
|
194
|
+
):
|
|
195
|
+
result = _call_numpy(
|
|
196
|
+
_np.argpartition, _to_base_ndarray(a), kth, axis=axis, **kwargs
|
|
197
|
+
)
|
|
198
|
+
return result # type: ignore[return-value]
|
|
199
|
+
|
|
200
|
+
|
|
201
|
+
attach_docstring(
|
|
202
|
+
argpartition,
|
|
203
|
+
_np.argpartition,
|
|
204
|
+
"counted_custom",
|
|
205
|
+
"n FLOPs per slice (linear quickselect)",
|
|
206
|
+
)
|
|
207
|
+
argpartition.__signature__ = _inspect.signature(_np.argpartition) # type: ignore[attr-defined]
|
|
208
|
+
|
|
209
|
+
|
|
210
|
+
# ---------------------------------------------------------------------------
|
|
211
|
+
# Search
|
|
212
|
+
# ---------------------------------------------------------------------------
|
|
213
|
+
|
|
214
|
+
|
|
215
|
+
@_counted_wrapper
|
|
216
|
+
def searchsorted(
|
|
217
|
+
a: ArrayLike,
|
|
218
|
+
v: ArrayLike,
|
|
219
|
+
**kwargs: Any,
|
|
220
|
+
) -> FlopscopeArray:
|
|
221
|
+
"""Counted version of ``numpy.searchsorted``.
|
|
222
|
+
|
|
223
|
+
Cost: m * ceil(log2(n)) where m = numel(v) and n = len(a).
|
|
224
|
+
"""
|
|
225
|
+
budget = require_budget()
|
|
226
|
+
if not isinstance(a, _np.ndarray):
|
|
227
|
+
a = _np.asarray(a)
|
|
228
|
+
v_arr = _np.asarray(v)
|
|
229
|
+
n = a.shape[0] if a.ndim > 0 else 1
|
|
230
|
+
m = max(v_arr.size, 1)
|
|
231
|
+
cost = search_cost(m, n)
|
|
232
|
+
with budget.deduct(
|
|
233
|
+
"searchsorted",
|
|
234
|
+
flop_cost=cost,
|
|
235
|
+
subscripts=None,
|
|
236
|
+
shapes=(a.shape, v_arr.shape),
|
|
237
|
+
):
|
|
238
|
+
result = _call_numpy(
|
|
239
|
+
_np.searchsorted, _to_base_ndarray(a), _to_base_ndarray(v), **kwargs
|
|
240
|
+
)
|
|
241
|
+
return result # type: ignore[return-value]
|
|
242
|
+
|
|
243
|
+
|
|
244
|
+
attach_docstring(
|
|
245
|
+
searchsorted,
|
|
246
|
+
_np.searchsorted,
|
|
247
|
+
"counted_custom",
|
|
248
|
+
"m*ceil(log2(n)) FLOPs (m queries into sorted array of size n)",
|
|
249
|
+
)
|
|
250
|
+
searchsorted.__signature__ = _inspect.signature(_np.searchsorted) # type: ignore[attr-defined]
|
|
251
|
+
|
|
252
|
+
|
|
253
|
+
@_counted_wrapper
|
|
254
|
+
def digitize(
|
|
255
|
+
x: ArrayLike,
|
|
256
|
+
bins: ArrayLike,
|
|
257
|
+
**kwargs: Any,
|
|
258
|
+
) -> FlopscopeArray:
|
|
259
|
+
"""Counted version of ``numpy.digitize``.
|
|
260
|
+
|
|
261
|
+
Cost: n * ceil(log2(len(bins))) where n = numel(x).
|
|
262
|
+
"""
|
|
263
|
+
budget = require_budget()
|
|
264
|
+
x_arr = _np.asarray(x)
|
|
265
|
+
bins_arr = _np.asarray(bins)
|
|
266
|
+
n = max(x_arr.size, 1)
|
|
267
|
+
cost = search_cost(n, max(len(bins_arr), 1))
|
|
268
|
+
with budget.deduct(
|
|
269
|
+
"digitize",
|
|
270
|
+
flop_cost=cost,
|
|
271
|
+
subscripts=None,
|
|
272
|
+
shapes=(x_arr.shape, bins_arr.shape),
|
|
273
|
+
):
|
|
274
|
+
result = _call_numpy(
|
|
275
|
+
_np.digitize, _to_base_ndarray(x), _to_base_ndarray(bins), **kwargs
|
|
276
|
+
) # type: ignore[arg-type]
|
|
277
|
+
return result # type: ignore[return-value]
|
|
278
|
+
|
|
279
|
+
|
|
280
|
+
attach_docstring(
|
|
281
|
+
digitize,
|
|
282
|
+
_np.digitize,
|
|
283
|
+
"counted_custom",
|
|
284
|
+
"n*ceil(log2(len(bins))) FLOPs",
|
|
285
|
+
)
|
|
286
|
+
digitize.__signature__ = _inspect.signature(_np.digitize) # type: ignore[attr-defined]
|
|
287
|
+
|
|
288
|
+
|
|
289
|
+
# ---------------------------------------------------------------------------
|
|
290
|
+
# Uniqueness
|
|
291
|
+
# ---------------------------------------------------------------------------
|
|
292
|
+
|
|
293
|
+
|
|
294
|
+
def _unique_cost(ar):
|
|
295
|
+
"""Compute sort-based cost for uniqueness: n * ceil(log2(n))."""
|
|
296
|
+
if not isinstance(ar, _np.ndarray):
|
|
297
|
+
ar = _np.asarray(ar)
|
|
298
|
+
n = max(ar.size, 1)
|
|
299
|
+
return sort_cost(n)
|
|
300
|
+
|
|
301
|
+
|
|
302
|
+
@_counted_wrapper
|
|
303
|
+
def unique(ar: ArrayLike, **kwargs: Any) -> FlopscopeArray | tuple[FlopscopeArray, ...]:
|
|
304
|
+
"""Counted version of ``numpy.unique``. Cost: n*ceil(log2(n)) FLOPs.
|
|
305
|
+
|
|
306
|
+
On numpy 2.3+ the sort guarantee is relaxed for string and complex dtypes.
|
|
307
|
+
This wrapper re-sorts the values (only when no auxiliary-return kwargs are
|
|
308
|
+
requested) to preserve pre-2.3 semantics.
|
|
309
|
+
"""
|
|
310
|
+
budget = require_budget()
|
|
311
|
+
ar_arr = _np.asarray(ar)
|
|
312
|
+
cost = _unique_cost(ar_arr)
|
|
313
|
+
with budget.deduct(
|
|
314
|
+
"unique", flop_cost=cost, subscripts=None, shapes=(ar_arr.shape,)
|
|
315
|
+
):
|
|
316
|
+
result = _call_numpy(_np.unique, ar_arr, **kwargs)
|
|
317
|
+
|
|
318
|
+
# Shim: restore sort guarantee for string / complex dtypes on numpy 2.3+.
|
|
319
|
+
# Only active for the default signature (no auxiliary arrays requested).
|
|
320
|
+
_returns_tuple = any(
|
|
321
|
+
kwargs.get(k, False)
|
|
322
|
+
for k in ("return_index", "return_inverse", "return_counts")
|
|
323
|
+
)
|
|
324
|
+
if (
|
|
325
|
+
_NUMPY_GE_2_3
|
|
326
|
+
and not _returns_tuple
|
|
327
|
+
and ar_arr.dtype.kind in _UNSORTED_IN_NP_2_3
|
|
328
|
+
):
|
|
329
|
+
result = _np.sort(_to_base_ndarray(result))
|
|
330
|
+
return result # type: ignore[return-value]
|
|
331
|
+
|
|
332
|
+
|
|
333
|
+
attach_docstring(unique, _np.unique, "counted_custom", "n*ceil(log2(n)) FLOPs")
|
|
334
|
+
unique.__signature__ = _inspect.signature(_np.unique) # type: ignore[attr-defined]
|
|
335
|
+
|
|
336
|
+
|
|
337
|
+
@_counted_wrapper
|
|
338
|
+
def unique_all(x: ArrayLike, /) -> Any:
|
|
339
|
+
"""Counted version of ``numpy.unique_all``. Cost: n*ceil(log2(n)) FLOPs."""
|
|
340
|
+
budget = require_budget()
|
|
341
|
+
x_arr = _np.asarray(x)
|
|
342
|
+
cost = _unique_cost(x_arr)
|
|
343
|
+
with budget.deduct(
|
|
344
|
+
"unique_all", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)
|
|
345
|
+
):
|
|
346
|
+
result = _call_numpy(_np.unique_all, x_arr)
|
|
347
|
+
return result
|
|
348
|
+
|
|
349
|
+
|
|
350
|
+
attach_docstring(unique_all, _np.unique_all, "counted_custom", "n*ceil(log2(n)) FLOPs")
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
@_counted_wrapper
|
|
354
|
+
def unique_counts(x: ArrayLike, /) -> Any:
|
|
355
|
+
"""Counted version of ``numpy.unique_counts``. Cost: n*ceil(log2(n)) FLOPs."""
|
|
356
|
+
budget = require_budget()
|
|
357
|
+
x_arr = _np.asarray(x)
|
|
358
|
+
cost = _unique_cost(x_arr)
|
|
359
|
+
with budget.deduct(
|
|
360
|
+
"unique_counts", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)
|
|
361
|
+
):
|
|
362
|
+
result = _call_numpy(_np.unique_counts, x_arr)
|
|
363
|
+
return result
|
|
364
|
+
|
|
365
|
+
|
|
366
|
+
attach_docstring(
|
|
367
|
+
unique_counts, _np.unique_counts, "counted_custom", "n*ceil(log2(n)) FLOPs"
|
|
368
|
+
)
|
|
369
|
+
|
|
370
|
+
|
|
371
|
+
@_counted_wrapper
|
|
372
|
+
def unique_inverse(x: ArrayLike, /) -> Any:
|
|
373
|
+
"""Counted version of ``numpy.unique_inverse``. Cost: n*ceil(log2(n)) FLOPs."""
|
|
374
|
+
budget = require_budget()
|
|
375
|
+
x_arr = _np.asarray(x)
|
|
376
|
+
cost = _unique_cost(x_arr)
|
|
377
|
+
with budget.deduct(
|
|
378
|
+
"unique_inverse", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)
|
|
379
|
+
):
|
|
380
|
+
result = _call_numpy(_np.unique_inverse, x_arr)
|
|
381
|
+
return result
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
attach_docstring(
|
|
385
|
+
unique_inverse, _np.unique_inverse, "counted_custom", "n*ceil(log2(n)) FLOPs"
|
|
386
|
+
)
|
|
387
|
+
|
|
388
|
+
|
|
389
|
+
@_counted_wrapper
|
|
390
|
+
def unique_values(x: ArrayLike, /) -> FlopscopeArray:
|
|
391
|
+
"""Counted version of ``numpy.unique_values``. Cost: n*ceil(log2(n)) FLOPs."""
|
|
392
|
+
budget = require_budget()
|
|
393
|
+
x_arr = _np.asarray(x)
|
|
394
|
+
cost = _unique_cost(x_arr)
|
|
395
|
+
with budget.deduct(
|
|
396
|
+
"unique_values", flop_cost=cost, subscripts=None, shapes=(x_arr.shape,)
|
|
397
|
+
):
|
|
398
|
+
result = _call_numpy(_np.unique_values, x_arr)
|
|
399
|
+
return result # type: ignore[return-value]
|
|
400
|
+
|
|
401
|
+
|
|
402
|
+
attach_docstring(
|
|
403
|
+
unique_values, _np.unique_values, "counted_custom", "n*ceil(log2(n)) FLOPs"
|
|
404
|
+
)
|
|
405
|
+
|
|
406
|
+
|
|
407
|
+
# ---------------------------------------------------------------------------
|
|
408
|
+
# Set operations (cost = (n+m) * ceil(log2(n+m)))
|
|
409
|
+
# ---------------------------------------------------------------------------
|
|
410
|
+
|
|
411
|
+
|
|
412
|
+
def _set_cost(ar1, ar2):
|
|
413
|
+
"""Compute cost for set operations on two arrays."""
|
|
414
|
+
n = _np.asarray(ar1).size
|
|
415
|
+
m = _np.asarray(ar2).size
|
|
416
|
+
total = max(n + m, 1)
|
|
417
|
+
return sort_cost(total)
|
|
418
|
+
|
|
419
|
+
|
|
420
|
+
if hasattr(_np, "in1d"):
|
|
421
|
+
|
|
422
|
+
@_counted_wrapper
|
|
423
|
+
def in1d(ar1: ArrayLike, ar2: ArrayLike, **kwargs: Any) -> FlopscopeArray: # pyright: ignore[reportRedeclaration]
|
|
424
|
+
"""Counted version of ``numpy.in1d``. Cost: (n+m)*ceil(log2(n+m)) FLOPs."""
|
|
425
|
+
budget = require_budget()
|
|
426
|
+
a1 = _np.asarray(ar1)
|
|
427
|
+
a2 = _np.asarray(ar2)
|
|
428
|
+
cost = _set_cost(a1, a2)
|
|
429
|
+
with budget.deduct(
|
|
430
|
+
"in1d", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
|
|
431
|
+
):
|
|
432
|
+
result = _call_numpy(
|
|
433
|
+
_np.in1d, _to_base_ndarray(ar1), _to_base_ndarray(ar2), **kwargs
|
|
434
|
+
)
|
|
435
|
+
return result # type: ignore[return-value]
|
|
436
|
+
|
|
437
|
+
attach_docstring(in1d, _np.in1d, "counted_custom", "(n+m)*ceil(log2(n+m)) FLOPs")
|
|
438
|
+
in1d.__signature__ = _inspect.signature(_np.in1d) # type: ignore[attr-defined]
|
|
439
|
+
|
|
440
|
+
else:
|
|
441
|
+
|
|
442
|
+
def in1d(*args: Any, **kwargs: Any) -> FlopscopeArray:
|
|
443
|
+
raise UnsupportedFunctionError("in1d", max_version="2.4", replacement="isin")
|
|
444
|
+
|
|
445
|
+
|
|
446
|
+
@_counted_wrapper
|
|
447
|
+
def isin(
|
|
448
|
+
element: ArrayLike,
|
|
449
|
+
test_elements: ArrayLike,
|
|
450
|
+
**kwargs: Any,
|
|
451
|
+
) -> FlopscopeArray:
|
|
452
|
+
"""Counted version of ``numpy.isin``. Cost: (n+m)*ceil(log2(n+m)) FLOPs."""
|
|
453
|
+
budget = require_budget()
|
|
454
|
+
el = _np.asarray(element)
|
|
455
|
+
te = _np.asarray(test_elements)
|
|
456
|
+
cost = _set_cost(el, te)
|
|
457
|
+
with budget.deduct(
|
|
458
|
+
"isin", flop_cost=cost, subscripts=None, shapes=(el.shape, te.shape)
|
|
459
|
+
):
|
|
460
|
+
result = _call_numpy(
|
|
461
|
+
_np.isin,
|
|
462
|
+
_to_base_ndarray(element),
|
|
463
|
+
_to_base_ndarray(test_elements),
|
|
464
|
+
**kwargs,
|
|
465
|
+
)
|
|
466
|
+
return result # type: ignore[return-value]
|
|
467
|
+
|
|
468
|
+
|
|
469
|
+
attach_docstring(isin, _np.isin, "counted_custom", "(n+m)*ceil(log2(n+m)) FLOPs")
|
|
470
|
+
isin.__signature__ = _inspect.signature(_np.isin) # type: ignore[attr-defined]
|
|
471
|
+
|
|
472
|
+
|
|
473
|
+
@_counted_wrapper
|
|
474
|
+
def intersect1d(
|
|
475
|
+
ar1: ArrayLike,
|
|
476
|
+
ar2: ArrayLike,
|
|
477
|
+
**kwargs: Any,
|
|
478
|
+
) -> FlopscopeArray | tuple[FlopscopeArray, ...]:
|
|
479
|
+
"""Counted version of ``numpy.intersect1d``. Cost: (n+m)*ceil(log2(n+m)) FLOPs."""
|
|
480
|
+
budget = require_budget()
|
|
481
|
+
a1 = _np.asarray(ar1)
|
|
482
|
+
a2 = _np.asarray(ar2)
|
|
483
|
+
cost = _set_cost(a1, a2)
|
|
484
|
+
with budget.deduct(
|
|
485
|
+
"intersect1d", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
|
|
486
|
+
):
|
|
487
|
+
result = _call_numpy(
|
|
488
|
+
_np.intersect1d, _to_base_ndarray(ar1), _to_base_ndarray(ar2), **kwargs
|
|
489
|
+
)
|
|
490
|
+
return result # type: ignore[return-value]
|
|
491
|
+
|
|
492
|
+
|
|
493
|
+
attach_docstring(
|
|
494
|
+
intersect1d, _np.intersect1d, "counted_custom", "(n+m)*ceil(log2(n+m)) FLOPs"
|
|
495
|
+
)
|
|
496
|
+
intersect1d.__signature__ = _inspect.signature(_np.intersect1d) # type: ignore[attr-defined]
|
|
497
|
+
|
|
498
|
+
|
|
499
|
+
@_counted_wrapper
|
|
500
|
+
def union1d(ar1: ArrayLike, ar2: ArrayLike) -> FlopscopeArray:
|
|
501
|
+
"""Counted version of ``numpy.union1d``. Cost: (n+m)*ceil(log2(n+m)) FLOPs."""
|
|
502
|
+
budget = require_budget()
|
|
503
|
+
a1 = _np.asarray(ar1)
|
|
504
|
+
a2 = _np.asarray(ar2)
|
|
505
|
+
cost = _set_cost(a1, a2)
|
|
506
|
+
with budget.deduct(
|
|
507
|
+
"union1d", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
|
|
508
|
+
):
|
|
509
|
+
result = _call_numpy(_np.union1d, _to_base_ndarray(ar1), _to_base_ndarray(ar2))
|
|
510
|
+
return result # type: ignore[return-value]
|
|
511
|
+
|
|
512
|
+
|
|
513
|
+
attach_docstring(union1d, _np.union1d, "counted_custom", "(n+m)*ceil(log2(n+m)) FLOPs")
|
|
514
|
+
|
|
515
|
+
|
|
516
|
+
@_counted_wrapper
|
|
517
|
+
def setdiff1d(
|
|
518
|
+
ar1: ArrayLike,
|
|
519
|
+
ar2: ArrayLike,
|
|
520
|
+
**kwargs: Any,
|
|
521
|
+
) -> FlopscopeArray:
|
|
522
|
+
"""Counted version of ``numpy.setdiff1d``. Cost: (n+m)*ceil(log2(n+m)) FLOPs."""
|
|
523
|
+
budget = require_budget()
|
|
524
|
+
a1 = _np.asarray(ar1)
|
|
525
|
+
a2 = _np.asarray(ar2)
|
|
526
|
+
cost = _set_cost(a1, a2)
|
|
527
|
+
with budget.deduct(
|
|
528
|
+
"setdiff1d", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
|
|
529
|
+
):
|
|
530
|
+
result = _call_numpy(
|
|
531
|
+
_np.setdiff1d, _to_base_ndarray(ar1), _to_base_ndarray(ar2), **kwargs
|
|
532
|
+
)
|
|
533
|
+
return result # type: ignore[return-value]
|
|
534
|
+
|
|
535
|
+
|
|
536
|
+
attach_docstring(
|
|
537
|
+
setdiff1d, _np.setdiff1d, "counted_custom", "(n+m)*ceil(log2(n+m)) FLOPs"
|
|
538
|
+
)
|
|
539
|
+
setdiff1d.__signature__ = _inspect.signature(_np.setdiff1d) # type: ignore[attr-defined]
|
|
540
|
+
|
|
541
|
+
|
|
542
|
+
@_counted_wrapper
|
|
543
|
+
def setxor1d(
|
|
544
|
+
ar1: ArrayLike,
|
|
545
|
+
ar2: ArrayLike,
|
|
546
|
+
**kwargs: Any,
|
|
547
|
+
) -> FlopscopeArray:
|
|
548
|
+
"""Counted version of ``numpy.setxor1d``. Cost: (n+m)*ceil(log2(n+m)) FLOPs."""
|
|
549
|
+
budget = require_budget()
|
|
550
|
+
a1 = _np.asarray(ar1)
|
|
551
|
+
a2 = _np.asarray(ar2)
|
|
552
|
+
cost = _set_cost(a1, a2)
|
|
553
|
+
with budget.deduct(
|
|
554
|
+
"setxor1d", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
|
|
555
|
+
):
|
|
556
|
+
result = _call_numpy(
|
|
557
|
+
_np.setxor1d, _to_base_ndarray(ar1), _to_base_ndarray(ar2), **kwargs
|
|
558
|
+
)
|
|
559
|
+
return result # type: ignore[return-value]
|
|
560
|
+
|
|
561
|
+
|
|
562
|
+
attach_docstring(
|
|
563
|
+
setxor1d, _np.setxor1d, "counted_custom", "(n+m)*ceil(log2(n+m)) FLOPs"
|
|
564
|
+
)
|
|
565
|
+
setxor1d.__signature__ = _inspect.signature(_np.setxor1d) # type: ignore[attr-defined]
|
|
566
|
+
|
|
567
|
+
import sys as _sys # noqa: E402
|
|
568
|
+
|
|
569
|
+
from flopscope._ndarray import wrap_module_returns as _wrap_module_returns # noqa: E402
|
|
570
|
+
|
|
571
|
+
_wrap_module_returns(_sys.modules[__name__])
|