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
@@ -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__])