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,353 @@
1
+ # src/flopscope/linalg/_decompositions.py
2
+ """Matrix decomposition wrappers with FLOP counting.
3
+
4
+ Each operation has a co-located cost function documenting the formula,
5
+ source, and assumptions. Cost functions are pure (shape params) -> int.
6
+ """
7
+
8
+ from __future__ import annotations
9
+
10
+ import numpy as _np
11
+ from numpy.linalg._linalg import EighResult, EigResult, QRResult
12
+ from numpy.typing import ArrayLike
13
+
14
+ from flopscope._budget import _call_numpy, _counted_wrapper
15
+ from flopscope._docstrings import attach_docstring
16
+ from flopscope._ndarray import FlopscopeArray, _asflopscope, _to_base_ndarray
17
+ from flopscope._validation import require_budget
18
+ from flopscope.numpy.linalg._solvers import _batch_size, _has_zero_dim
19
+
20
+
21
+ def cholesky_cost(n: int) -> int:
22
+ """FLOP cost of Cholesky decomposition.
23
+
24
+ Parameters
25
+ ----------
26
+ n : int
27
+ Matrix dimension.
28
+
29
+ Returns
30
+ -------
31
+ int
32
+ Estimated FLOP count: $n^3$.
33
+
34
+ Notes
35
+ -----
36
+ Simplified cubic cost model for Cholesky decomposition.
37
+ """
38
+ return max(n**3, 1)
39
+
40
+
41
+ @_counted_wrapper
42
+ def cholesky(a: ArrayLike, /, *, upper: bool = False) -> FlopscopeArray:
43
+ """Cholesky decomposition with FLOP counting."""
44
+ budget = require_budget()
45
+ inputs_were_whest = isinstance(a, FlopscopeArray)
46
+ if not isinstance(a, _np.ndarray):
47
+ a = _np.asarray(a)
48
+ n = a.shape[-1]
49
+ batch = _batch_size(a.shape)
50
+ cost = cholesky_cost(n) * batch if not _has_zero_dim(a.shape) else 0
51
+ with budget.deduct(
52
+ "linalg.cholesky", flop_cost=cost, subscripts=None, shapes=(a.shape,)
53
+ ):
54
+ result = _call_numpy(_np.linalg.cholesky, _to_base_ndarray(a), upper=upper)
55
+ if isinstance(result, _np.ndarray) and inputs_were_whest:
56
+ return _asflopscope(result) # type: ignore[reportReturnType]
57
+ return result # type: ignore[reportReturnType]
58
+
59
+
60
+ attach_docstring(cholesky, _np.linalg.cholesky, "linalg", r"$n^3$ FLOPs")
61
+
62
+
63
+ def qr_cost(m: int, n: int) -> int:
64
+ r"""FLOP cost of QR decomposition.
65
+
66
+ Parameters
67
+ ----------
68
+ m : int
69
+ Number of rows.
70
+ n : int
71
+ Number of columns.
72
+
73
+ Returns
74
+ -------
75
+ int
76
+ Estimated FLOP count: $m \cdot n \cdot \min(m, n)$.
77
+
78
+ Notes
79
+ -----
80
+ Simplified cubic cost model for QR decomposition.
81
+ """
82
+ return max(m * n * min(m, n), 1)
83
+
84
+
85
+ @_counted_wrapper
86
+ def qr(
87
+ a: ArrayLike, mode: str = "reduced"
88
+ ) -> tuple[FlopscopeArray, FlopscopeArray] | FlopscopeArray:
89
+ """QR decomposition with FLOP counting.
90
+
91
+ Returns vary by ``mode``:
92
+
93
+ - ``"reduced"`` (default) / ``"complete"`` → ``QRResult(q, r)``
94
+ - ``"r"`` → ``r`` only
95
+ - ``"raw"`` → 2-tuple ``(h, tau)`` of two ndarrays with mismatched
96
+ shapes (preserved as a tuple rather than collapsed via
97
+ ``_asflopscope``, which would otherwise fail with
98
+ ``ValueError: ... inhomogeneous shape``)
99
+ """
100
+ budget = require_budget()
101
+ inputs_were_whest = isinstance(a, FlopscopeArray)
102
+ if not isinstance(a, _np.ndarray):
103
+ a = _np.asarray(a)
104
+ m, n = a.shape[-2], a.shape[-1]
105
+ batch = _batch_size(a.shape)
106
+ cost = qr_cost(m, n) * batch if not _has_zero_dim(a.shape) else 0
107
+ with budget.deduct("linalg.qr", flop_cost=cost, subscripts=None, shapes=(a.shape,)):
108
+ result = _call_numpy(_np.linalg.qr, _to_base_ndarray(a), mode=mode) # type: ignore[reportCallIssue]
109
+ if mode in ("reduced", "complete"):
110
+ q, r = result # type: ignore[reportGeneralTypeIssues]
111
+ if inputs_were_whest:
112
+ return QRResult(_asflopscope(q), _asflopscope(r)) # type: ignore[reportReturnType]
113
+ return QRResult(q, r) # type: ignore[reportReturnType]
114
+ if mode == "raw":
115
+ # ``raw`` returns a (h, tau) tuple of two ndarrays with different
116
+ # shapes — preserve the tuple structure rather than collapsing to
117
+ # a single array via ``_asflopscope``.
118
+ h, tau = result # type: ignore[reportGeneralTypeIssues]
119
+ if inputs_were_whest:
120
+ return _asflopscope(h), _asflopscope(tau) # type: ignore[reportReturnType]
121
+ return h, tau # type: ignore[reportReturnType]
122
+ if inputs_were_whest:
123
+ return _asflopscope(result) # type: ignore[reportArgumentType]
124
+ return result # type: ignore[reportReturnType]
125
+
126
+
127
+ attach_docstring(qr, _np.linalg.qr, "linalg", r"$m \cdot n \cdot \min(m,n)$ FLOPs")
128
+
129
+
130
+ def eig_cost(n: int) -> int:
131
+ """FLOP cost of eigendecomposition.
132
+
133
+ Parameters
134
+ ----------
135
+ n : int
136
+ Matrix dimension.
137
+
138
+ Returns
139
+ -------
140
+ int
141
+ Estimated FLOP count: $n^3$.
142
+
143
+ Notes
144
+ -----
145
+ Simplified cubic cost model for eigendecomposition.
146
+ """
147
+ return max(n**3, 1)
148
+
149
+
150
+ @_counted_wrapper
151
+ def eig(a: ArrayLike) -> tuple[FlopscopeArray, FlopscopeArray]:
152
+ """Eigendecomposition with FLOP counting."""
153
+ budget = require_budget()
154
+ inputs_were_whest = isinstance(a, FlopscopeArray)
155
+ if not isinstance(a, _np.ndarray):
156
+ a = _np.asarray(a)
157
+ n = a.shape[-1]
158
+ batch = _batch_size(a.shape)
159
+ cost = eig_cost(n) * batch if not _has_zero_dim(a.shape) else 0
160
+ with budget.deduct(
161
+ "linalg.eig", flop_cost=cost, subscripts=None, shapes=(a.shape,)
162
+ ):
163
+ result = _call_numpy(_np.linalg.eig, _to_base_ndarray(a))
164
+ if inputs_were_whest:
165
+ return EigResult( # type: ignore[reportReturnType]
166
+ _asflopscope(result.eigenvalues), _asflopscope(result.eigenvectors)
167
+ )
168
+ return result # type: ignore[reportReturnType]
169
+
170
+
171
+ attach_docstring(eig, _np.linalg.eig, "linalg", r"$n^3$ FLOPs")
172
+
173
+
174
+ def eigh_cost(n: int) -> int:
175
+ """FLOP cost of symmetric eigendecomposition.
176
+
177
+ Parameters
178
+ ----------
179
+ n : int
180
+ Matrix dimension.
181
+
182
+ Returns
183
+ -------
184
+ int
185
+ Estimated FLOP count: $n^3$.
186
+
187
+ Notes
188
+ -----
189
+ Simplified cubic cost model for symmetric eigendecomposition.
190
+ """
191
+ return max(n**3, 1)
192
+
193
+
194
+ @_counted_wrapper
195
+ def eigh(a: ArrayLike, UPLO: str = "L") -> tuple[FlopscopeArray, FlopscopeArray]:
196
+ """Symmetric eigendecomposition with FLOP counting."""
197
+ budget = require_budget()
198
+ inputs_were_whest = isinstance(a, FlopscopeArray)
199
+ if not isinstance(a, _np.ndarray):
200
+ a = _np.asarray(a)
201
+ n = a.shape[-1]
202
+ batch = _batch_size(a.shape)
203
+ cost = eigh_cost(n) * batch if not _has_zero_dim(a.shape) else 0
204
+ with budget.deduct(
205
+ "linalg.eigh", flop_cost=cost, subscripts=None, shapes=(a.shape,)
206
+ ):
207
+ result = _call_numpy(_np.linalg.eigh, _to_base_ndarray(a), UPLO=UPLO) # type: ignore[reportCallIssue]
208
+ if inputs_were_whest:
209
+ return EighResult( # type: ignore[reportReturnType]
210
+ _asflopscope(result.eigenvalues), _asflopscope(result.eigenvectors)
211
+ )
212
+ return result # type: ignore[reportReturnType]
213
+
214
+
215
+ attach_docstring(eigh, _np.linalg.eigh, "linalg", r"$n^3$ FLOPs")
216
+
217
+
218
+ def eigvals_cost(n: int) -> int:
219
+ """FLOP cost of computing eigenvalues.
220
+
221
+ Parameters
222
+ ----------
223
+ n : int
224
+ Matrix dimension.
225
+
226
+ Returns
227
+ -------
228
+ int
229
+ Estimated FLOP count: $n^3$.
230
+
231
+ Notes
232
+ -----
233
+ Simplified cubic cost model for eigenvalue computation.
234
+ """
235
+ return max(n**3, 1)
236
+
237
+
238
+ @_counted_wrapper
239
+ def eigvals(a: ArrayLike) -> FlopscopeArray:
240
+ """Eigenvalues (nonsymmetric) with FLOP counting."""
241
+ budget = require_budget()
242
+ inputs_were_whest = isinstance(a, FlopscopeArray)
243
+ if not isinstance(a, _np.ndarray):
244
+ a = _np.asarray(a)
245
+ n = a.shape[-1]
246
+ batch = _batch_size(a.shape)
247
+ cost = eigvals_cost(n) * batch if not _has_zero_dim(a.shape) else 0
248
+ with budget.deduct(
249
+ "linalg.eigvals", flop_cost=cost, subscripts=None, shapes=(a.shape,)
250
+ ):
251
+ result = _call_numpy(_np.linalg.eigvals, _to_base_ndarray(a))
252
+ if inputs_were_whest:
253
+ return _asflopscope(result) # type: ignore[reportReturnType]
254
+ return result # type: ignore[reportReturnType]
255
+
256
+
257
+ attach_docstring(eigvals, _np.linalg.eigvals, "linalg", r"$n^3$ FLOPs")
258
+
259
+
260
+ def eigvalsh_cost(n: int) -> int:
261
+ """FLOP cost of computing eigenvalues of a symmetric matrix.
262
+
263
+ Parameters
264
+ ----------
265
+ n : int
266
+ Matrix dimension.
267
+
268
+ Returns
269
+ -------
270
+ int
271
+ Estimated FLOP count: $n^3$.
272
+
273
+ Notes
274
+ -----
275
+ Simplified cubic cost model for symmetric eigenvalue computation.
276
+ """
277
+ return max(n**3, 1)
278
+
279
+
280
+ @_counted_wrapper
281
+ def eigvalsh(a: ArrayLike, UPLO: str = "L") -> FlopscopeArray:
282
+ """Eigenvalues (symmetric) with FLOP counting."""
283
+ budget = require_budget()
284
+ inputs_were_whest = isinstance(a, FlopscopeArray)
285
+ if not isinstance(a, _np.ndarray):
286
+ a = _np.asarray(a)
287
+ n = a.shape[-1]
288
+ batch = _batch_size(a.shape)
289
+ cost = eigvalsh_cost(n) * batch if not _has_zero_dim(a.shape) else 0
290
+ with budget.deduct(
291
+ "linalg.eigvalsh", flop_cost=cost, subscripts=None, shapes=(a.shape,)
292
+ ):
293
+ result = _call_numpy(_np.linalg.eigvalsh, _to_base_ndarray(a), UPLO=UPLO) # type: ignore[reportCallIssue]
294
+ if inputs_were_whest:
295
+ return _asflopscope(result) # type: ignore[reportReturnType]
296
+ return result # type: ignore[reportReturnType]
297
+
298
+
299
+ attach_docstring(eigvalsh, _np.linalg.eigvalsh, "linalg", r"$n^3$ FLOPs")
300
+
301
+
302
+ def svdvals_cost(m: int, n: int, k: int | None = None) -> int:
303
+ """FLOP cost of computing singular values.
304
+
305
+ Parameters
306
+ ----------
307
+ m : int
308
+ Number of rows.
309
+ n : int
310
+ Number of columns.
311
+ k : int or None, optional
312
+ Number of singular values to compute. Defaults to min(m, n).
313
+
314
+ Returns
315
+ -------
316
+ int
317
+ Estimated FLOP count: m * n * k.
318
+
319
+ Notes
320
+ -----
321
+ Source: Golub-Reinsch bidiagonalization. Same cost model as SVD.
322
+ """
323
+ if k is None:
324
+ k = min(m, n)
325
+ return max(m * n * k, 1)
326
+
327
+
328
+ @_counted_wrapper
329
+ def svdvals(x: ArrayLike, /, *, k: int | None = None) -> FlopscopeArray:
330
+ """Singular values with FLOP counting."""
331
+ budget = require_budget()
332
+ inputs_were_whest = isinstance(x, FlopscopeArray)
333
+ if not isinstance(x, _np.ndarray):
334
+ x = _np.asarray(x)
335
+ m, n = x.shape[-2], x.shape[-1]
336
+ batch = _batch_size(x.shape)
337
+ if k is None:
338
+ k = min(m, n)
339
+ if min(m, n) > 0 and not (1 <= k <= min(m, n)):
340
+ raise ValueError(f"k must satisfy 1 <= k <= min(m, n) = {min(m, n)}, got k={k}")
341
+ cost = svdvals_cost(m, n, k) * batch if not _has_zero_dim(x.shape) else 0
342
+ with budget.deduct(
343
+ "linalg.svdvals", flop_cost=cost, subscripts=None, shapes=(x.shape,)
344
+ ):
345
+ result = _call_numpy(_np.linalg.svdvals, _to_base_ndarray(x))
346
+ if k < min(m, n):
347
+ result = result[..., :k]
348
+ if inputs_were_whest:
349
+ return _asflopscope(result) # type: ignore[reportReturnType]
350
+ return result # type: ignore[reportReturnType]
351
+
352
+
353
+ attach_docstring(svdvals, _np.linalg.svdvals, "linalg", r"$m \cdot n \cdot k$ FLOPs")