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/_errstate.py ADDED
@@ -0,0 +1,25 @@
1
+ """NumPy runtime configuration and iteration utilities re-exported as free.
2
+
3
+ These are all pure numpy state management and iteration helpers.
4
+ None perform FLOP-costing operations.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ import numpy as _np
10
+
11
+ # Error state (context manager + getters/setters for numpy warnings)
12
+ errstate = _np.errstate
13
+ seterr = _np.seterr
14
+ geterr = _np.geterr
15
+
16
+ # Iteration utilities
17
+ ndindex = _np.ndindex
18
+ ndenumerate = _np.ndenumerate
19
+ broadcast = _np.broadcast
20
+ nditer = _np.nditer
21
+
22
+ # Print formatting
23
+ set_printoptions = _np.set_printoptions
24
+ get_printoptions = _np.get_printoptions
25
+ printoptions = _np.printoptions
flopscope/_flops.py ADDED
@@ -0,0 +1,282 @@
1
+ """FLOP cost calculators for flopscope operations."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import math
6
+ from collections import Counter
7
+
8
+ from flopscope._perm_group import SymmetryGroup
9
+ from flopscope._symmetry_utils import unique_elements_for_shape
10
+
11
+
12
+ def parse_einsum_subscripts(subscripts: str) -> tuple[list[list[str]], list[str]]:
13
+ """Parse an einsum subscript string into input and output index lists.
14
+
15
+ Parameters
16
+ ----------
17
+ subscripts : str
18
+ Einsum subscript string (e.g., ``'ij,jk->ik'``).
19
+
20
+ Returns
21
+ -------
22
+ inputs : list of list of str
23
+ Index labels for each input operand.
24
+ output : list of str
25
+ Index labels for the output.
26
+ """
27
+ subscripts = subscripts.replace(" ", "")
28
+ if "->" in subscripts:
29
+ input_part, output_part = subscripts.split("->")
30
+ output = list(output_part)
31
+ else:
32
+ input_part = subscripts
33
+ all_labels: list[str] = []
34
+ for part in input_part.split(","):
35
+ all_labels.extend(list(part))
36
+ counts = Counter(all_labels)
37
+ output = sorted(lbl for lbl, c in counts.items() if c == 1)
38
+ inputs = [list(part) for part in input_part.split(",")]
39
+ return inputs, output
40
+
41
+
42
+ def einsum_cost(
43
+ subscripts: str,
44
+ shapes: list[tuple[int, ...]],
45
+ operand_symmetries: list[SymmetryGroup | None] | None = None,
46
+ identity_pattern: tuple[tuple[int, ...], ...] | None = None,
47
+ ) -> int:
48
+ """FLOP cost of an einsum operation.
49
+
50
+ Uses the whole-expression direct-event accumulation model:
51
+ total = (k-1) * prod(M) + prod(alpha), where M is the number of
52
+ unique output elements and alpha is the number of unique output+
53
+ reduction-axis combinations. FMA = 2 (textbook): the α/M formula counts
54
+ multiplies and adds separately by construction.
55
+
56
+ Parameters
57
+ ----------
58
+ subscripts : str
59
+ Einsum subscript string.
60
+ shapes : list of tuple of int
61
+ Shapes of the input operands.
62
+ operand_symmetries : list of SymmetryGroup or None, optional
63
+ Exact symmetry group for each input operand.
64
+ identity_pattern : tuple of tuple of int, optional
65
+ Groups of operand positions that alias to the same underlying
66
+ array. Each inner tuple lists ≥2 positions sharing identity. When
67
+ provided, the cost path applies repeated-operand savings (e.g.,
68
+ the joint symmetry of ``A @ A``).
69
+
70
+ Returns
71
+ -------
72
+ int
73
+ Estimated FLOP count.
74
+ """
75
+ import numpy as _np
76
+
77
+ from flopscope._accumulation._cost import compute_accumulation_cost
78
+ from flopscope._opt_einsum import parse_einsum_input
79
+
80
+ dummy_operands = [_np.empty(s) for s in shapes]
81
+ input_subscripts, output_subscript, _ = parse_einsum_input(
82
+ (subscripts, *dummy_operands)
83
+ )
84
+ canonical_subscripts = f"{input_subscripts}->{output_subscript}"
85
+ input_parts = tuple(input_subscripts.split(","))
86
+
87
+ per_op_syms: tuple[SymmetryGroup | None, ...] = (
88
+ tuple(operand_symmetries)
89
+ if operand_symmetries is not None
90
+ else (None,) * len(shapes)
91
+ )
92
+
93
+ cost = compute_accumulation_cost(
94
+ canonical_subscripts=canonical_subscripts,
95
+ input_parts=input_parts,
96
+ output_subscript=output_subscript,
97
+ shapes=shapes,
98
+ per_op_symmetries=per_op_syms,
99
+ identity_pattern=identity_pattern,
100
+ )
101
+ return cost.total
102
+
103
+
104
+ def analytical_pointwise_cost(
105
+ shape: tuple[int, ...], symmetry: SymmetryGroup | None = None
106
+ ) -> int:
107
+ """FLOP cost of a pointwise (element-wise) operation.
108
+
109
+ Parameters
110
+ ----------
111
+ shape : tuple of int
112
+ Shape of the array.
113
+ symmetry : SymmetryGroup or None, optional
114
+ If provided, only unique elements are counted.
115
+
116
+ Returns
117
+ -------
118
+ int
119
+ Estimated FLOP count (one per element, or one per unique element).
120
+ """
121
+ if symmetry is not None:
122
+ return max(unique_elements_for_shape(symmetry, shape), 1)
123
+ result = 1
124
+ for dim in shape:
125
+ result *= dim
126
+ return max(result, 1)
127
+
128
+
129
+ def analytical_reduction_cost(
130
+ input_shape: tuple[int, ...],
131
+ axis: int | tuple[int, ...] | None = None,
132
+ symmetry: SymmetryGroup | None = None,
133
+ ) -> int:
134
+ """FLOP cost of a reduction operation (orbit-mapping model).
135
+
136
+ Delegates to compute_reduction_accumulation_cost from the
137
+ _accumulation subpackage. The legacy `unique_elements_for_shape`-based
138
+ formula is removed in favor of the path-independent orbit-mapping count
139
+ (with #56's off-by-one corrected).
140
+
141
+ Parameters
142
+ ----------
143
+ input_shape : tuple of int
144
+ Shape of the input array.
145
+ axis : int, tuple of int, or None, optional
146
+ Axis or axes to reduce. None means full reduction.
147
+ symmetry : SymmetryGroup, optional
148
+ Pointwise symmetry of the input.
149
+
150
+ Returns
151
+ -------
152
+ int
153
+ FLOP count via the Tier-1 orbit-mapping model.
154
+ """
155
+ from flopscope._accumulation._reduction import (
156
+ _normalize_axis,
157
+ compute_reduction_accumulation_cost,
158
+ )
159
+
160
+ ndim = len(input_shape)
161
+ axes_summed = _normalize_axis(axis, ndim)
162
+ cost = compute_reduction_accumulation_cost(
163
+ input_shape=tuple(input_shape),
164
+ axes_summed=axes_summed,
165
+ symmetry=symmetry,
166
+ op_factor=1,
167
+ extra_ops=0,
168
+ )
169
+ return max(cost.total, 1)
170
+
171
+
172
+ # Backward-compatible internal aliases. The public weighted API lives in
173
+ # ``flopscope.flops`` and wraps these analytical formulas.
174
+ pointwise_cost = analytical_pointwise_cost
175
+ reduction_cost = analytical_reduction_cost
176
+
177
+
178
+ def svd_cost(m: int, n: int, k: int | None = None) -> int:
179
+ """FLOP cost of a (truncated) SVD.
180
+
181
+ Parameters
182
+ ----------
183
+ m : int
184
+ Number of rows.
185
+ n : int
186
+ Number of columns.
187
+ k : int or None, optional
188
+ Number of singular values/vectors to compute. Defaults to min(m, n).
189
+
190
+ Returns
191
+ -------
192
+ int
193
+ Estimated FLOP count: m * n * k.
194
+
195
+ Notes
196
+ -----
197
+ Based on Golub-Reinsch bidiagonalization.
198
+ """
199
+ if k is None:
200
+ k = min(m, n)
201
+ return m * n * k
202
+
203
+
204
+ def matmul_cost(m: int, k: int, n: int) -> int:
205
+ """FLOP cost of a single 2D matmul ``(m, k) @ (k, n) -> (m, n)``.
206
+
207
+ Formula: ``2 * m * k * n - m * n`` (FMA=1 convention with accumulator
208
+ off-by-one), matching what ``fnp.matmul`` charges per 2D call via the
209
+ einsum machinery (``_resolve_cost_and_output_symmetry`` on the
210
+ canonical subscripts ``ij,jk->ik``).
211
+
212
+ Used by ``pinv_cost``, ``lstsq_cost``, and ``matrix_power_cost``
213
+ so those compound formulas track ``fnp.matmul`` automatically if the
214
+ matmul accounting convention ever changes again.
215
+
216
+ Parameters
217
+ ----------
218
+ m, k, n : int
219
+ Matmul dimensions: ``(m, k) @ (k, n)``.
220
+
221
+ Returns
222
+ -------
223
+ int
224
+ Estimated FLOP count, clamped to at least 1.
225
+ """
226
+ return max(2 * m * k * n - m * n, 1)
227
+
228
+
229
+ def _ceil_log2(n: int) -> int:
230
+ """Return ceil(log2(n)), minimum 1.
231
+
232
+ Parameters
233
+ ----------
234
+ n : int
235
+ Input value.
236
+
237
+ Returns
238
+ -------
239
+ int
240
+ ceil(log2(n)), with a floor of 1.
241
+ """
242
+ if n <= 1:
243
+ return 1
244
+ return max(math.ceil(math.log2(n)), 1)
245
+
246
+
247
+ def sort_cost(n: int) -> int:
248
+ """FLOP cost of comparison-based sort.
249
+
250
+ Parameters
251
+ ----------
252
+ n : int
253
+ Number of elements to sort.
254
+
255
+ Returns
256
+ -------
257
+ int
258
+ Estimated FLOP count: n * ceil(log2(n)).
259
+ """
260
+ if n <= 0:
261
+ return 1
262
+ return max(n * _ceil_log2(n), 1)
263
+
264
+
265
+ def search_cost(queries: int, sorted_size: int) -> int:
266
+ """FLOP cost of binary search.
267
+
268
+ Parameters
269
+ ----------
270
+ queries : int
271
+ Number of search queries.
272
+ sorted_size : int
273
+ Size of the sorted array being searched.
274
+
275
+ Returns
276
+ -------
277
+ int
278
+ Estimated FLOP count: queries * ceil(log2(sorted_size)).
279
+ """
280
+ if queries <= 0:
281
+ return 1
282
+ return max(queries * _ceil_log2(sorted_size), 1)