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,278 @@
1
+ """Counted polynomial operations for flopscope."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import inspect as _inspect
6
+ from typing import Any
7
+
8
+ import numpy as _np
9
+ from numpy.typing import ArrayLike
10
+
11
+ from flopscope._budget import _call_numpy, _counted_wrapper
12
+ from flopscope._docstrings import attach_docstring
13
+ from flopscope._ndarray import FlopscopeArray, _to_base_ndarray
14
+ from flopscope._validation import require_budget
15
+
16
+ # ---------------------------------------------------------------------------
17
+ # Cost functions
18
+ # ---------------------------------------------------------------------------
19
+
20
+
21
+ def polyval_cost(deg: int, m: int) -> int:
22
+ """Cost for polyval: Horner's method under FMA=2 textbook convention.
23
+
24
+ Per coefficient: 1 multiply + 1 add (FMA=2). m output cells, deg coefficients.
25
+ Returns 2 * m * deg FLOPs.
26
+ """
27
+ return max(2 * m * deg, 1)
28
+
29
+
30
+ def polyadd_cost(n1: int, n2: int) -> int:
31
+ """Cost for polyadd: max(n1, n2) FLOPs."""
32
+ return max(n1, n2, 1)
33
+
34
+
35
+ def polysub_cost(n1: int, n2: int) -> int:
36
+ """Cost for polysub: max(n1, n2) FLOPs."""
37
+ return max(n1, n2, 1)
38
+
39
+
40
+ def polyder_cost(n: int) -> int:
41
+ """Cost for polyder: n FLOPs (n = len of coeffs)."""
42
+ return max(n, 1)
43
+
44
+
45
+ def polyint_cost(n: int) -> int:
46
+ """Cost for polyint: n FLOPs (n = len of coeffs)."""
47
+ return max(n, 1)
48
+
49
+
50
+ def polymul_cost(n1: int, n2: int) -> int:
51
+ """Cost for polymul: n1 * n2 FLOPs."""
52
+ return max(n1 * n2, 1)
53
+
54
+
55
+ def polydiv_cost(n1: int, n2: int) -> int:
56
+ """Cost for polydiv: n1 * n2 FLOPs."""
57
+ return max(n1 * n2, 1)
58
+
59
+
60
+ def polyfit_cost(m: int, deg: int) -> int:
61
+ """Cost for polyfit: 2 * m * (deg+1)^2 FLOPs."""
62
+ return max(2 * m * (deg + 1) ** 2, 1)
63
+
64
+
65
+ def poly_cost(n: int) -> int:
66
+ """Cost for poly: $n^2$ FLOPs."""
67
+ return max(n * n, 1)
68
+
69
+
70
+ def roots_cost(n: int) -> int:
71
+ """Cost for roots: $n^3$ FLOPs (companion matrix eigendecomposition, simplified)."""
72
+ return max(n**3, 1)
73
+
74
+
75
+ # ---------------------------------------------------------------------------
76
+ # Wrapped operations
77
+ # ---------------------------------------------------------------------------
78
+
79
+
80
+ @_counted_wrapper
81
+ def polyval(p: ArrayLike, x: ArrayLike) -> FlopscopeArray:
82
+ """Evaluate a polynomial at given points. Wraps ``numpy.polyval``.
83
+
84
+ Both ``p`` and ``x`` are converted to plain ``np.ndarray`` (via
85
+ ``_to_base_ndarray`` after ``np.asarray``) before being passed to
86
+ ``_np.polyval``, because numpy's polyval implementation internally calls
87
+ ``np.zeros_like(x)`` and other ops that do not handle
88
+ ``FlopscopeArray`` subclasses (they are not in the
89
+ ``__array_function__`` allowlist).
90
+ """
91
+ budget = require_budget()
92
+ p_arr = _to_base_ndarray(_np.asarray(p))
93
+ x_arr = _to_base_ndarray(_np.asarray(x))
94
+ deg = len(p_arr) - 1
95
+ m = x_arr.size
96
+ cost = polyval_cost(deg, m)
97
+ with budget.deduct(
98
+ "polyval", flop_cost=cost, subscripts=None, shapes=(p_arr.shape, x_arr.shape)
99
+ ):
100
+ result = _call_numpy(_np.polyval, p_arr, x_arr)
101
+ return result # type: ignore[return-value]
102
+
103
+
104
+ attach_docstring(
105
+ polyval, _np.polyval, "counted_custom", "m * deg FLOPs (Horner's method, FMA=1)"
106
+ )
107
+
108
+
109
+ @_counted_wrapper
110
+ def polyadd(a1: ArrayLike, a2: ArrayLike) -> FlopscopeArray:
111
+ """Add two polynomials. Wraps ``numpy.polyadd``."""
112
+ budget = require_budget()
113
+ a1 = _np.asarray(a1)
114
+ a2 = _np.asarray(a2)
115
+ n1 = len(a1)
116
+ n2 = len(a2)
117
+ cost = polyadd_cost(n1, n2)
118
+ with budget.deduct(
119
+ "polyadd", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
120
+ ):
121
+ result = _call_numpy(_np.polyadd, a1, a2)
122
+ return result # type: ignore[return-value]
123
+
124
+
125
+ attach_docstring(polyadd, _np.polyadd, "counted_custom", "max(n1, n2) FLOPs")
126
+
127
+
128
+ @_counted_wrapper
129
+ def polysub(a1: ArrayLike, a2: ArrayLike) -> FlopscopeArray:
130
+ """Subtract two polynomials. Wraps ``numpy.polysub``."""
131
+ budget = require_budget()
132
+ a1 = _np.asarray(a1)
133
+ a2 = _np.asarray(a2)
134
+ n1 = len(a1)
135
+ n2 = len(a2)
136
+ cost = polysub_cost(n1, n2)
137
+ with budget.deduct(
138
+ "polysub", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
139
+ ):
140
+ result = _call_numpy(_np.polysub, a1, a2)
141
+ return result # type: ignore[return-value]
142
+
143
+
144
+ attach_docstring(polysub, _np.polysub, "counted_custom", "max(n1, n2) FLOPs")
145
+
146
+
147
+ @_counted_wrapper
148
+ def polyder(p: ArrayLike, m: int = 1) -> FlopscopeArray:
149
+ """Differentiate a polynomial. Wraps ``numpy.polyder``."""
150
+ budget = require_budget()
151
+ p = _np.asarray(p)
152
+ n = len(p)
153
+ cost = polyder_cost(n)
154
+ with budget.deduct("polyder", flop_cost=cost, subscripts=None, shapes=(p.shape,)):
155
+ result = _call_numpy(_np.polyder, p, m=m)
156
+ return result # type: ignore[return-value]
157
+
158
+
159
+ attach_docstring(polyder, _np.polyder, "counted_custom", "n FLOPs (n = len(coeffs))")
160
+
161
+
162
+ @_counted_wrapper
163
+ def polyint(p: ArrayLike, m: int = 1, k: ArrayLike | None = None) -> FlopscopeArray:
164
+ """Integrate a polynomial. Wraps ``numpy.polyint``."""
165
+ budget = require_budget()
166
+ p = _np.asarray(p)
167
+ n = len(p)
168
+ cost = polyint_cost(n)
169
+ with budget.deduct("polyint", flop_cost=cost, subscripts=None, shapes=(p.shape,)):
170
+ if k is None:
171
+ result = _call_numpy(_np.polyint, p, m=m)
172
+ else:
173
+ result = _call_numpy(_np.polyint, p, m=m, k=k) # type: ignore[arg-type]
174
+ return result # type: ignore[return-value]
175
+
176
+
177
+ attach_docstring(polyint, _np.polyint, "counted_custom", "n FLOPs (n = len(coeffs))")
178
+
179
+
180
+ @_counted_wrapper
181
+ def polymul(a1: ArrayLike, a2: ArrayLike) -> FlopscopeArray:
182
+ """Multiply polynomials. Wraps ``numpy.polymul``."""
183
+ budget = require_budget()
184
+ a1 = _np.asarray(a1)
185
+ a2 = _np.asarray(a2)
186
+ n1 = len(a1)
187
+ n2 = len(a2)
188
+ cost = polymul_cost(n1, n2)
189
+ with budget.deduct(
190
+ "polymul", flop_cost=cost, subscripts=None, shapes=(a1.shape, a2.shape)
191
+ ):
192
+ result = _call_numpy(_np.polymul, a1, a2)
193
+ return result # type: ignore[return-value]
194
+
195
+
196
+ attach_docstring(polymul, _np.polymul, "counted_custom", "n1 * n2 FLOPs")
197
+
198
+
199
+ @_counted_wrapper
200
+ def polydiv(u: ArrayLike, v: ArrayLike) -> tuple[FlopscopeArray, FlopscopeArray]:
201
+ """Divide one polynomial by another. Wraps ``numpy.polydiv``."""
202
+ budget = require_budget()
203
+ u = _np.atleast_1d(_np.asarray(u))
204
+ v = _np.atleast_1d(_np.asarray(v))
205
+ n1 = len(u)
206
+ n2 = len(v)
207
+ cost = polydiv_cost(n1, n2)
208
+ with budget.deduct(
209
+ "polydiv", flop_cost=cost, subscripts=None, shapes=(u.shape, v.shape)
210
+ ):
211
+ result = _call_numpy(_np.polydiv, u, v)
212
+ return result # type: ignore[return-value]
213
+
214
+
215
+ attach_docstring(polydiv, _np.polydiv, "counted_custom", "n1 * n2 FLOPs")
216
+
217
+
218
+ @_counted_wrapper
219
+ def polyfit(
220
+ x: ArrayLike,
221
+ y: ArrayLike,
222
+ deg: int,
223
+ **kwargs: Any,
224
+ ) -> FlopscopeArray:
225
+ """Least-squares polynomial fit. Wraps ``numpy.polyfit``."""
226
+ budget = require_budget()
227
+ x = _np.asarray(x)
228
+ m = len(x)
229
+ cost = polyfit_cost(m, deg)
230
+ with budget.deduct("polyfit", flop_cost=cost, subscripts=None, shapes=(x.shape,)):
231
+ result = _call_numpy(_np.polyfit, x, y, deg, **kwargs) # type: ignore[arg-type]
232
+ return result # type: ignore[return-value]
233
+
234
+
235
+ attach_docstring(polyfit, _np.polyfit, "counted_custom", "2 * m * (deg+1)^2 FLOPs")
236
+ polyfit.__signature__ = _inspect.signature(_np.polyfit) # type: ignore[attr-defined]
237
+
238
+
239
+ @_counted_wrapper
240
+ def poly(seq_of_zeros: ArrayLike) -> FlopscopeArray:
241
+ """Return polynomial coefficients from roots. Wraps ``numpy.poly``."""
242
+ budget = require_budget()
243
+ seq = _np.asarray(seq_of_zeros)
244
+ # If 2D (square matrix), n = shape[0]; if 1D, n = len(seq)
245
+ if seq.ndim == 2:
246
+ n = seq.shape[0]
247
+ else:
248
+ n = len(seq)
249
+ cost = poly_cost(n)
250
+ with budget.deduct("poly", flop_cost=cost, subscripts=None, shapes=(seq.shape,)):
251
+ result = _call_numpy(_np.poly, seq_of_zeros)
252
+ return result # type: ignore[return-value]
253
+
254
+
255
+ attach_docstring(poly, _np.poly, "counted_custom", "n^2 FLOPs")
256
+
257
+
258
+ @_counted_wrapper
259
+ def roots(p: ArrayLike) -> FlopscopeArray:
260
+ """Return the roots of a polynomial with given coefficients. Wraps ``numpy.roots``."""
261
+ budget = require_budget()
262
+ p = _np.asarray(p)
263
+ n = len(p) - 1 # degree = number of roots
264
+ cost = roots_cost(n)
265
+ with budget.deduct("roots", flop_cost=cost, subscripts=None, shapes=(p.shape,)):
266
+ result = _call_numpy(_np.roots, p)
267
+ return result # type: ignore[return-value]
268
+
269
+
270
+ attach_docstring(
271
+ roots, _np.roots, "counted_custom", "n^3 FLOPs (companion matrix eig, simplified)"
272
+ )
273
+
274
+ import sys as _sys # noqa: E402
275
+
276
+ from flopscope._ndarray import wrap_module_returns as _wrap_module_returns # noqa: E402
277
+
278
+ _wrap_module_returns(_sys.modules[__name__])