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.
- benchmarks/__init__.py +1 -0
- benchmarks/__main__.py +6 -0
- benchmarks/_baseline.py +171 -0
- benchmarks/_bitwise.py +231 -0
- benchmarks/_complex.py +176 -0
- benchmarks/_contractions.py +291 -0
- benchmarks/_fft.py +198 -0
- benchmarks/_impl_urls.py +139 -0
- benchmarks/_linalg.py +197 -0
- benchmarks/_linalg_delegates.py +407 -0
- benchmarks/_metadata.py +141 -0
- benchmarks/_misc.py +653 -0
- benchmarks/_perf.py +321 -0
- benchmarks/_perm_group_calibration.py +175 -0
- benchmarks/_pointwise.py +372 -0
- benchmarks/_polynomial.py +193 -0
- benchmarks/_random.py +209 -0
- benchmarks/_reductions.py +136 -0
- benchmarks/_sorting.py +289 -0
- benchmarks/_stats.py +137 -0
- benchmarks/_window.py +92 -0
- benchmarks/accumulation/__init__.py +0 -0
- benchmarks/accumulation/bench_cost_compute.py +138 -0
- benchmarks/dashboard.py +312 -0
- benchmarks/runner.py +636 -0
- flopscope/__init__.py +273 -0
- flopscope/_accumulation/__init__.py +13 -0
- flopscope/_accumulation/_bipartite.py +121 -0
- flopscope/_accumulation/_burnside.py +51 -0
- flopscope/_accumulation/_cache.py +146 -0
- flopscope/_accumulation/_components.py +153 -0
- flopscope/_accumulation/_cost.py +1414 -0
- flopscope/_accumulation/_cost_descriptions.py +63 -0
- flopscope/_accumulation/_detection.py +318 -0
- flopscope/_accumulation/_ladder.py +191 -0
- flopscope/_accumulation/_output_orbit.py +104 -0
- flopscope/_accumulation/_partition.py +290 -0
- flopscope/_accumulation/_path_info.py +211 -0
- flopscope/_accumulation/_public.py +169 -0
- flopscope/_accumulation/_reduction.py +310 -0
- flopscope/_accumulation/_regimes.py +303 -0
- flopscope/_accumulation/_shape.py +33 -0
- flopscope/_accumulation/_wreath.py +209 -0
- flopscope/_budget.py +1027 -0
- flopscope/_config.py +118 -0
- flopscope/_counting_ops.py +451 -0
- flopscope/_display.py +478 -0
- flopscope/_docstrings.py +59 -0
- flopscope/_dtypes.py +20 -0
- flopscope/_einsum.py +717 -0
- flopscope/_errstate.py +25 -0
- flopscope/_flops.py +282 -0
- flopscope/_free_ops.py +2654 -0
- flopscope/_ndarray.py +1126 -0
- flopscope/_opt_einsum/LICENSE +21 -0
- flopscope/_opt_einsum/NOTICE +59 -0
- flopscope/_opt_einsum/__init__.py +209 -0
- flopscope/_opt_einsum/_contract.py +1478 -0
- flopscope/_opt_einsum/_helpers.py +164 -0
- flopscope/_opt_einsum/_hsluv.py +273 -0
- flopscope/_opt_einsum/_path_random.py +462 -0
- flopscope/_opt_einsum/_paths.py +1653 -0
- flopscope/_opt_einsum/_subgraph_symmetry.py +544 -0
- flopscope/_opt_einsum/_symmetry.py +140 -0
- flopscope/_opt_einsum/_typing.py +37 -0
- flopscope/_perm_group.py +717 -0
- flopscope/_pointwise.py +2522 -0
- flopscope/_polynomial.py +278 -0
- flopscope/_registry.py +3216 -0
- flopscope/_sorting_ops.py +571 -0
- flopscope/_symmetric.py +812 -0
- flopscope/_symmetry_transport.py +510 -0
- flopscope/_symmetry_utils.py +669 -0
- flopscope/_type_info.py +12 -0
- flopscope/_unwrap.py +70 -0
- flopscope/_validation.py +83 -0
- flopscope/_version_check.py +46 -0
- flopscope/_weights.py +195 -0
- flopscope/_window.py +177 -0
- flopscope/accounting.py +565 -0
- flopscope/data/default_weights.json +462 -0
- flopscope/data/weights.csv +509 -0
- flopscope/errors.py +197 -0
- flopscope/numpy/__init__.py +878 -0
- flopscope/numpy/fft/__init__.py +55 -0
- flopscope/numpy/fft/_free.py +51 -0
- flopscope/numpy/fft/_transforms.py +695 -0
- flopscope/numpy/linalg/__init__.py +105 -0
- flopscope/numpy/linalg/_aliases.py +126 -0
- flopscope/numpy/linalg/_compound.py +161 -0
- flopscope/numpy/linalg/_decompositions.py +353 -0
- flopscope/numpy/linalg/_properties.py +533 -0
- flopscope/numpy/linalg/_solvers.py +444 -0
- flopscope/numpy/linalg/_svd.py +122 -0
- flopscope/numpy/random/__init__.py +684 -0
- flopscope/numpy/random/_cost_formulas.py +115 -0
- flopscope/numpy/random/_counted_classes.py +241 -0
- flopscope/numpy/testing/__init__.py +13 -0
- flopscope/numpy/typing/__init__.py +30 -0
- flopscope/py.typed +0 -0
- flopscope/stats/__init__.py +84 -0
- flopscope/stats/_base.py +77 -0
- flopscope/stats/_cauchy.py +146 -0
- flopscope/stats/_erf.py +190 -0
- flopscope/stats/_expon.py +146 -0
- flopscope/stats/_laplace.py +150 -0
- flopscope/stats/_logistic.py +148 -0
- flopscope/stats/_lognorm.py +160 -0
- flopscope/stats/_ndtri.py +133 -0
- flopscope/stats/_norm.py +149 -0
- flopscope/stats/_truncnorm.py +186 -0
- flopscope/stats/_uniform.py +141 -0
- flopscope-0.2.0.dist-info/METADATA +23 -0
- flopscope-0.2.0.dist-info/RECORD +115 -0
- flopscope-0.2.0.dist-info/WHEEL +4 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2014 Daniel Smith
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
This package is a slim adapter layer over opt_einsum by Daniel G. A. Smith et al.
|
|
2
|
+
|
|
3
|
+
Original repository : https://github.com/dgasmith/opt_einsum
|
|
4
|
+
Original license : MIT (see LICENSE in this directory)
|
|
5
|
+
Original authors : Daniel G. A. Smith and Johnnie Gray
|
|
6
|
+
Adapter date : 2026-05-07
|
|
7
|
+
|
|
8
|
+
This adapter is no longer a fork. opt_einsum is a runtime dependency
|
|
9
|
+
(declared in pyproject.toml). The local files here adapt upstream's PathInfo
|
|
10
|
+
to flopscope's expected dataclass shape, recompute per-step FLOP counts using
|
|
11
|
+
flopscope's FMA convention (default 1, configurable), and preserve the colored
|
|
12
|
+
rich-render display.
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
================================================================================
|
|
16
|
+
LOCAL FILES
|
|
17
|
+
================================================================================
|
|
18
|
+
|
|
19
|
+
__init__.py
|
|
20
|
+
──────────────────────────────────────────────────
|
|
21
|
+
Re-exports `contract_path` (wrapped to return flopscope's PathInfo),
|
|
22
|
+
`parse_einsum_input` (from upstream), `PathInfo`, `StepInfo`, `flop_count`,
|
|
23
|
+
`build_path_info`.
|
|
24
|
+
|
|
25
|
+
_contract.py
|
|
26
|
+
──────────────────────────────────────────────────
|
|
27
|
+
Contains:
|
|
28
|
+
- StepInfo, PathInfo dataclasses (flopscope's expected shape)
|
|
29
|
+
- build_path_info() — adapter from upstream's PathInfo to flopscope's
|
|
30
|
+
- Rich-render display methods for terminal output
|
|
31
|
+
|
|
32
|
+
_helpers.py
|
|
33
|
+
──────────────────────────────────────────────────
|
|
34
|
+
flop_count() — computes per-step FLOP cost using flopscope's FMA convention.
|
|
35
|
+
Default FMA = 1; configurable via flopscope.configure(fma_cost=2).
|
|
36
|
+
|
|
37
|
+
_hsluv.py
|
|
38
|
+
──────────────────────────────────────────────────
|
|
39
|
+
HSLuv color palette helpers used by the rich-render display.
|
|
40
|
+
Adapted from the MIT-licensed hsluv-python reference implementation:
|
|
41
|
+
https://github.com/hsluv/hsluv-python
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
================================================================================
|
|
45
|
+
HISTORY
|
|
46
|
+
================================================================================
|
|
47
|
+
|
|
48
|
+
Originally vendored as a full fork (2026-04-03) to support symmetry-aware
|
|
49
|
+
path search via SubgraphSymmetryOracle. After the symmetry-aware einsum cost
|
|
50
|
+
rewrite (2026-05-07) made path search symmetry-agnostic, the fork's
|
|
51
|
+
customizations collapsed to:
|
|
52
|
+
- FMA=1 cost convention
|
|
53
|
+
- Custom PathInfo/StepInfo shape with rich-render display
|
|
54
|
+
|
|
55
|
+
This adapter package was created (2026-05-07) by deleting the bulk of the
|
|
56
|
+
fork (~3000 lines: _paths.py, _path_random.py, _blas.py, _parser.py,
|
|
57
|
+
_testing.py, _typing.py) and adding opt_einsum as a runtime dependency.
|
|
58
|
+
|
|
59
|
+
LICENSE (MIT) is preserved here per opt_einsum's licensing terms.
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
"""flopscope's slim adapter over opt_einsum.
|
|
2
|
+
|
|
3
|
+
Re-exports contract_path from upstream and the flopscope PathInfo/StepInfo
|
|
4
|
+
adapters. Path search algorithms come from upstream; per-step FLOP costs are
|
|
5
|
+
recomputed using flopscope's FMA convention (default 1, configurable via the
|
|
6
|
+
``fma_cost`` setting).
|
|
7
|
+
|
|
8
|
+
See LICENSE and NOTICE in this directory for attribution to opt_einsum.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import opt_einsum.path_random as _path_random_upstream
|
|
12
|
+
from opt_einsum import contract_path as _upstream_contract_path
|
|
13
|
+
from opt_einsum.parser import get_shape as _get_shape
|
|
14
|
+
from opt_einsum.parser import parse_einsum_input
|
|
15
|
+
from opt_einsum.paths import (
|
|
16
|
+
_AUTO_CHOICES,
|
|
17
|
+
_AUTO_HQ_CHOICES,
|
|
18
|
+
BranchBound,
|
|
19
|
+
DynamicProgramming,
|
|
20
|
+
PathOptimizer,
|
|
21
|
+
get_path_fn,
|
|
22
|
+
greedy,
|
|
23
|
+
register_path_fn,
|
|
24
|
+
)
|
|
25
|
+
from opt_einsum.paths import (
|
|
26
|
+
_PATH_OPTIONS as _upstream_path_options,
|
|
27
|
+
)
|
|
28
|
+
|
|
29
|
+
from ._contract import PathInfo, StepInfo, build_path_info
|
|
30
|
+
from ._helpers import flop_count
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def _resolve_optimizer_name(optimize, num_ops: int) -> str:
|
|
34
|
+
"""Resolve the effective optimizer name for display in PathInfo.
|
|
35
|
+
|
|
36
|
+
Mirrors the resolution logic from the old local contract_path:
|
|
37
|
+
- 'auto' and 'auto-hq' resolve to the inner function name based on num_ops
|
|
38
|
+
- Explicit string names are returned as-is
|
|
39
|
+
- num_ops <= 2 returns 'trivial'
|
|
40
|
+
- PathOptimizer instances return their class name
|
|
41
|
+
"""
|
|
42
|
+
if num_ops <= 2:
|
|
43
|
+
return "trivial"
|
|
44
|
+
if isinstance(optimize, PathOptimizer):
|
|
45
|
+
return type(optimize).__name__
|
|
46
|
+
if not isinstance(optimize, str):
|
|
47
|
+
return ""
|
|
48
|
+
if optimize in (True, "auto", None):
|
|
49
|
+
inner_fn = _AUTO_CHOICES.get(num_ops, greedy)
|
|
50
|
+
return getattr(inner_fn, "__name__", None) or getattr(
|
|
51
|
+
getattr(inner_fn, "func", None), "__name__", str(inner_fn)
|
|
52
|
+
)
|
|
53
|
+
if optimize == "auto-hq":
|
|
54
|
+
inner_fn = _AUTO_HQ_CHOICES.get(
|
|
55
|
+
num_ops, _path_random_upstream.random_greedy_128
|
|
56
|
+
)
|
|
57
|
+
return getattr(inner_fn, "__name__", None) or getattr(
|
|
58
|
+
getattr(inner_fn, "func", None), "__name__", str(inner_fn)
|
|
59
|
+
)
|
|
60
|
+
return optimize
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _resolve_local_path(optimize, args, kwargs):
|
|
64
|
+
"""Resolve a path locally for optimizers not known to upstream opt_einsum.
|
|
65
|
+
|
|
66
|
+
Used when ``optimize`` is a local PathOptimizer instance or a string key
|
|
67
|
+
registered only in flopscope's local registry (e.g. a custom key
|
|
68
|
+
added via ``register_path_fn``).
|
|
69
|
+
|
|
70
|
+
Returns ``(resolved_path_list, optimizer_name_str)``.
|
|
71
|
+
"""
|
|
72
|
+
from . import _helpers as helpers
|
|
73
|
+
|
|
74
|
+
operands_ = [args[0]] + list(args[1:])
|
|
75
|
+
shapes = kwargs.get("shapes", False)
|
|
76
|
+
input_subscripts, output_subscript, operands_prepped = parse_einsum_input(
|
|
77
|
+
operands_, shapes=shapes
|
|
78
|
+
)
|
|
79
|
+
input_list = input_subscripts.split(",")
|
|
80
|
+
input_sets = [frozenset(x) for x in input_list]
|
|
81
|
+
if shapes:
|
|
82
|
+
input_shapes = list(operands_prepped)
|
|
83
|
+
else:
|
|
84
|
+
input_shapes = [_get_shape(x) for x in operands_prepped]
|
|
85
|
+
output_set = frozenset(output_subscript)
|
|
86
|
+
size_dict: dict[str, int] = {}
|
|
87
|
+
for tnum, term in enumerate(input_list):
|
|
88
|
+
sh = input_shapes[tnum]
|
|
89
|
+
for cnum, char in enumerate(term):
|
|
90
|
+
dim = int(sh[cnum])
|
|
91
|
+
if char not in size_dict:
|
|
92
|
+
size_dict[char] = dim
|
|
93
|
+
elif size_dict[char] == 1:
|
|
94
|
+
size_dict[char] = dim
|
|
95
|
+
|
|
96
|
+
memory_limit = kwargs.get("memory_limit", None)
|
|
97
|
+
size_list_mem = [
|
|
98
|
+
helpers.compute_size_by_dict(t, size_dict)
|
|
99
|
+
for t in input_list + [output_subscript]
|
|
100
|
+
]
|
|
101
|
+
memory_arg = None
|
|
102
|
+
if memory_limit == "max_input":
|
|
103
|
+
memory_arg = max(size_list_mem)
|
|
104
|
+
elif isinstance(memory_limit, int) and memory_limit > 0:
|
|
105
|
+
memory_arg = memory_limit
|
|
106
|
+
|
|
107
|
+
num_ops = len(input_list)
|
|
108
|
+
if isinstance(optimize, PathOptimizer):
|
|
109
|
+
resolved_path = optimize(input_sets, output_set, size_dict, memory_arg)
|
|
110
|
+
optimizer_name = _resolve_optimizer_name(optimize, num_ops)
|
|
111
|
+
else:
|
|
112
|
+
# String key in registry
|
|
113
|
+
path_fn = get_path_fn(optimize)
|
|
114
|
+
resolved_path = path_fn(input_sets, output_set, size_dict, memory_arg)
|
|
115
|
+
optimizer_name = _resolve_optimizer_name(optimize, num_ops)
|
|
116
|
+
|
|
117
|
+
return list(resolved_path), optimizer_name
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
def _count_num_ops(args, kwargs):
|
|
121
|
+
"""Count the number of operands from args/kwargs without full parse."""
|
|
122
|
+
operands_ = [args[0]] + list(args[1:])
|
|
123
|
+
shapes = kwargs.get("shapes", False)
|
|
124
|
+
input_subscripts, _, _ = parse_einsum_input(operands_, shapes=shapes)
|
|
125
|
+
return len(input_subscripts.split(","))
|
|
126
|
+
|
|
127
|
+
|
|
128
|
+
def contract_path(*args, **kwargs):
|
|
129
|
+
"""Run upstream opt_einsum.contract_path, then adapt the result to
|
|
130
|
+
flopscope's PathInfo (with FMA-aware per-step costs).
|
|
131
|
+
|
|
132
|
+
All arguments are forwarded to upstream. The return value's PathInfo is
|
|
133
|
+
flopscope's dataclass form; the path itself is unchanged.
|
|
134
|
+
|
|
135
|
+
If ``optimize`` is a PathOptimizer instance, or a string key registered
|
|
136
|
+
only in flopscope's local registry, the path is resolved locally
|
|
137
|
+
first and then forwarded to upstream as an explicit path list so that
|
|
138
|
+
upstream produces the contraction_list we need for build_path_info.
|
|
139
|
+
"""
|
|
140
|
+
optimize = kwargs.get("optimize", True)
|
|
141
|
+
|
|
142
|
+
needs_local_resolve = False
|
|
143
|
+
if isinstance(optimize, PathOptimizer):
|
|
144
|
+
needs_local_resolve = True
|
|
145
|
+
elif (
|
|
146
|
+
isinstance(optimize, str)
|
|
147
|
+
and optimize in _upstream_path_options
|
|
148
|
+
and optimize not in _upstream_path_options
|
|
149
|
+
):
|
|
150
|
+
# This branch is structurally unreachable but kept for symmetry.
|
|
151
|
+
needs_local_resolve = True
|
|
152
|
+
|
|
153
|
+
if needs_local_resolve:
|
|
154
|
+
resolved_path, optimizer_name = _resolve_local_path(optimize, args, kwargs)
|
|
155
|
+
# Re-issue upstream call with the resolved path so contraction_list is built.
|
|
156
|
+
new_kwargs = {k: v for k, v in kwargs.items() if k != "optimize"}
|
|
157
|
+
new_kwargs["optimize"] = resolved_path
|
|
158
|
+
upstream_path, upstream_info = _upstream_contract_path(*args, **new_kwargs)
|
|
159
|
+
else:
|
|
160
|
+
upstream_path, upstream_info = _upstream_contract_path(*args, **kwargs)
|
|
161
|
+
num_ops = (
|
|
162
|
+
len(upstream_info.input_subscripts.split(","))
|
|
163
|
+
if upstream_info.input_subscripts
|
|
164
|
+
else 2
|
|
165
|
+
)
|
|
166
|
+
if optimize is True or optimize is None:
|
|
167
|
+
optimize = "auto"
|
|
168
|
+
optimizer_name = _resolve_optimizer_name(optimize, num_ops)
|
|
169
|
+
|
|
170
|
+
return list(upstream_path), build_path_info(
|
|
171
|
+
upstream_path,
|
|
172
|
+
upstream_info,
|
|
173
|
+
size_dict=upstream_info.size_dict,
|
|
174
|
+
optimizer_used=optimizer_name,
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
__all__ = [
|
|
179
|
+
"PathInfo",
|
|
180
|
+
"StepInfo",
|
|
181
|
+
"contract_path",
|
|
182
|
+
"flop_count",
|
|
183
|
+
"build_path_info",
|
|
184
|
+
"parse_einsum_input",
|
|
185
|
+
"BranchBound",
|
|
186
|
+
"DynamicProgramming",
|
|
187
|
+
"register_path_fn",
|
|
188
|
+
]
|
|
189
|
+
|
|
190
|
+
|
|
191
|
+
def __getattr__(name: str) -> object:
|
|
192
|
+
"""Lazy package-level attribute hook (PEP 562).
|
|
193
|
+
|
|
194
|
+
Exposes upstream opt_einsum sub-modules under the private names used by the
|
|
195
|
+
vendored test suite (``oe._helpers``, ``oe._paths``, ``oe._path_random``)
|
|
196
|
+
WITHOUT registering them in the package ``__dict__``, so they never shadow
|
|
197
|
+
the real ``flopscope._opt_einsum._helpers`` submodule that lives on disk.
|
|
198
|
+
"""
|
|
199
|
+
if name == "_helpers":
|
|
200
|
+
import opt_einsum.helpers as _m
|
|
201
|
+
|
|
202
|
+
return _m
|
|
203
|
+
if name == "_paths":
|
|
204
|
+
import opt_einsum.paths as _m # type: ignore[no-redef]
|
|
205
|
+
|
|
206
|
+
return _m
|
|
207
|
+
if name == "_path_random":
|
|
208
|
+
return _path_random_upstream
|
|
209
|
+
raise AttributeError(f"module {__name__!r} has no attribute {name!r}")
|