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/errors.py ADDED
@@ -0,0 +1,197 @@
1
+ """Exception and warning classes for flopscope."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ import sys as _sys
7
+ import warnings as _warnings
8
+
9
+ _DEFAULT_DOCS_ROOT = "https://aicrowd.github.io/flopscope/docs"
10
+ _BUDGET_DOCS_PATH = "/guides/budget-planning"
11
+ _COMPETITION_DOCS_PATH = "/getting-started/competition"
12
+ _SYMMETRY_DOCS_PATH = "/guides/symmetry"
13
+
14
+
15
+ def _docs_url(path: str) -> str:
16
+ root = os.environ.get("FLOPSCOPE_DOCS_ROOT", "").strip() or _DEFAULT_DOCS_ROOT
17
+ return f"{root.rstrip('/')}{path}"
18
+
19
+
20
+ class FlopscopeError(Exception):
21
+ """Base exception for all flopscope errors."""
22
+
23
+
24
+ class BudgetExhaustedError(FlopscopeError):
25
+ """Raised when an operation would exceed the FLOP budget."""
26
+
27
+ def __init__(self, op_name: str, *, flop_cost: int, flops_remaining: int):
28
+ self.op_name = op_name
29
+ self.flop_cost = flop_cost
30
+ self.flops_remaining = flops_remaining
31
+ super().__init__(
32
+ f"{op_name} would cost {flop_cost:,} FLOPs but only "
33
+ f"{flops_remaining:,} remain. "
34
+ f"See: {_docs_url(_BUDGET_DOCS_PATH)}"
35
+ )
36
+
37
+
38
+ class TimeExhaustedError(FlopscopeError):
39
+ """Raised when an operation exceeds the wall-clock time limit."""
40
+
41
+ def __init__(self, op_name: str, *, elapsed_s: float, limit_s: float):
42
+ self.op_name = op_name
43
+ self.elapsed_s = elapsed_s
44
+ self.limit_s = limit_s
45
+ super().__init__(
46
+ f"{op_name}: wall-clock time {elapsed_s:.3f}s exceeds "
47
+ f"limit {limit_s:.3f}s. "
48
+ f"See: {_docs_url(_BUDGET_DOCS_PATH)}"
49
+ )
50
+
51
+
52
+ class NoBudgetContextError(FlopscopeError):
53
+ """Raised when a counted operation is called outside a BudgetContext."""
54
+
55
+ def __init__(self):
56
+ super().__init__(
57
+ "No active BudgetContext. "
58
+ "Wrap your code in `with flopscope.BudgetContext(...):` "
59
+ f"See: {_docs_url(_COMPETITION_DOCS_PATH)}"
60
+ )
61
+
62
+
63
+ class SymmetryError(FlopscopeError):
64
+ """Raised when a claimed tensor symmetry does not hold."""
65
+
66
+ def __init__(
67
+ self,
68
+ axes: tuple[int, ...],
69
+ max_deviation: float,
70
+ atol: float = 1e-6,
71
+ rtol: float = 1e-5,
72
+ ):
73
+ self.axes = axes
74
+ self.max_deviation = max_deviation
75
+ self.atol = atol
76
+ self.rtol = rtol
77
+ super().__init__(
78
+ f"Tensor not symmetric along axes ({', '.join(str(d) for d in axes)}): "
79
+ f"max deviation = {max_deviation} "
80
+ f"(tolerance: atol={atol}, rtol={rtol}). "
81
+ f"See: {_docs_url(_SYMMETRY_DOCS_PATH)}"
82
+ )
83
+
84
+
85
+ class UnsupportedFunctionError(FlopscopeError):
86
+ """Raised when calling a function not available in the installed NumPy.
87
+
88
+ Use ``min_version`` for functions that require a newer numpy than installed
89
+ (e.g. ``bitwise_count`` requires numpy >= 2.1). Use ``max_version`` for
90
+ functions that have been removed in a newer numpy than installed
91
+ (e.g. ``in1d`` was removed in numpy 2.4). ``replacement`` optionally names
92
+ the function users should call instead.
93
+ """
94
+
95
+ def __init__(
96
+ self,
97
+ func_name: str,
98
+ *,
99
+ min_version: str | None = None,
100
+ max_version: str | None = None,
101
+ replacement: str | None = None,
102
+ ):
103
+ import numpy as _np
104
+
105
+ self.func_name = func_name
106
+ self.min_version = min_version
107
+ self.max_version = max_version
108
+ self.replacement = replacement
109
+
110
+ if max_version is not None:
111
+ if replacement is not None:
112
+ msg = (
113
+ f"numpy.{func_name} was removed in numpy {max_version}; "
114
+ f"use `{replacement}` instead "
115
+ f"(you have numpy {_np.__version__})."
116
+ )
117
+ else:
118
+ msg = (
119
+ f"numpy.{func_name} was removed in numpy {max_version} "
120
+ f"(you have numpy {_np.__version__})."
121
+ )
122
+ elif min_version is not None:
123
+ msg = (
124
+ f"numpy.{func_name} requires numpy >= {min_version} "
125
+ f"(you have numpy {_np.__version__}). "
126
+ f"To use it: uv pip install 'numpy>={min_version}'"
127
+ )
128
+ else:
129
+ msg = f"numpy.{func_name} is not supported by flopscope."
130
+
131
+ super().__init__(msg)
132
+
133
+
134
+ class FlopscopeWarning(UserWarning):
135
+ """Warning issued when flopscope detects potential numerical issues."""
136
+
137
+
138
+ class SymmetryLossWarning(FlopscopeWarning):
139
+ """Warning issued when an operation causes loss of symmetry metadata."""
140
+
141
+
142
+ class CostFallbackWarning(FlopscopeWarning):
143
+ """Warning issued when flopscope skips its symmetry-aware cost adjustment.
144
+
145
+ The output's symmetry group is too large for the placeholder cost
146
+ model's Burnside enumeration — its order ``|G|`` exceeds the configured
147
+ ``dimino_budget``. The op runs correctly with the dense cost charged
148
+ instead — only the FLOP accounting is conservative; the data is
149
+ unaffected. Common trigger: ``np.ones((1,)*n)`` for large ``n`` or
150
+ other auto-inferred ``S_n`` symmetries on degenerate shapes.
151
+
152
+ Tune the threshold with ``flops.configure(dimino_budget=...)``. Suppress
153
+ the warning with ``flops.configure(symmetry_warnings=False)`` (shares
154
+ the flag with :class:`SymmetryLossWarning` since both are
155
+ symmetry-related diagnostics).
156
+ """
157
+
158
+
159
+ # Used by _user_stacklevel() to skip frames inside the flopscope package.
160
+ _FLOPSCOPE_PKG_DIR = os.path.dirname(os.path.abspath(__file__))
161
+
162
+
163
+ def _user_stacklevel() -> int:
164
+ """Stacklevel for :func:`warnings.warn` that points to the first frame
165
+ outside the ``flopscope`` package — i.e. the user's call site.
166
+
167
+ Walks the active call stack starting from the caller of
168
+ :func:`_warn_symmetry_loss`. Robust to changes in the number of
169
+ decorator/wrapper layers between user code and the warn site.
170
+ """
171
+ frame = _sys._getframe(2)
172
+ level = 2
173
+ while frame is not None:
174
+ if not frame.f_code.co_filename.startswith(_FLOPSCOPE_PKG_DIR):
175
+ return level
176
+ frame = frame.f_back
177
+ level += 1
178
+ return 3
179
+
180
+
181
+ def _warn_symmetry_loss(
182
+ lost_dims: list[tuple[int, ...]],
183
+ reason: str,
184
+ ) -> None:
185
+ """Emit a :class:`SymmetryLossWarning` if warnings are enabled."""
186
+ from flopscope._config import get_setting
187
+
188
+ if not get_setting("symmetry_warnings"):
189
+ return
190
+ dim_str = ", ".join(str(g) for g in lost_dims)
191
+ _warnings.warn(
192
+ f"Symmetry lost along dims {dim_str}: {reason}. "
193
+ "Use as_symmetric() to re-tag if you know the result is symmetric. "
194
+ "Suppress with flops.configure(symmetry_warnings=False).",
195
+ SymmetryLossWarning,
196
+ stacklevel=_user_stacklevel(),
197
+ )