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,209 @@
|
|
|
1
|
+
"""Direct enumerator for the wreath product ∏_i (H_i ≀ S_{m_i}).
|
|
2
|
+
|
|
3
|
+
`i` indexes identical-operand groups (operands sharing the same name).
|
|
4
|
+
`H_i` is each operand's declared axis symmetry on its own axes.
|
|
5
|
+
`m_i` is the number of copies of operand i.
|
|
6
|
+
|
|
7
|
+
Each wreath element is a row permutation σ on the U-vertices, paired with
|
|
8
|
+
factorization metadata that names the (outer-S_{m_i}, base-H_i) decomposition
|
|
9
|
+
for diagnostic display.
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
from __future__ import annotations
|
|
13
|
+
|
|
14
|
+
import itertools
|
|
15
|
+
from collections.abc import Iterator, Sequence
|
|
16
|
+
from dataclasses import dataclass
|
|
17
|
+
from typing import Any
|
|
18
|
+
|
|
19
|
+
from flopscope._perm_group import SymmetryGroup, _dimino
|
|
20
|
+
from flopscope._perm_group import _Permutation as Permutation
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
@dataclass(frozen=True)
|
|
24
|
+
class WreathElement:
|
|
25
|
+
"""One element of ∏_i (H_i ≀ S_{m_i}). Carries the row permutation and provenance."""
|
|
26
|
+
|
|
27
|
+
row_perm: Permutation
|
|
28
|
+
factorization: dict[str, Any]
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def enumerate_h(sym: Any, rank: int) -> Iterator[Permutation]:
|
|
32
|
+
"""Enumerate every element of the rank-`rank` group described by `sym`.
|
|
33
|
+
|
|
34
|
+
`sym` may be:
|
|
35
|
+
- None or 'none' → identity only
|
|
36
|
+
- 'symmetric' → S_rank
|
|
37
|
+
- 'cyclic' → C_rank
|
|
38
|
+
- 'dihedral' → D_rank
|
|
39
|
+
- a SymmetryGroup object → use its elements directly
|
|
40
|
+
- a dict with {'type': 'custom', 'generators': [...]} → custom group
|
|
41
|
+
|
|
42
|
+
Returns rank-`rank` permutations (operate on positions 0..rank-1).
|
|
43
|
+
"""
|
|
44
|
+
identity = Permutation.identity(rank)
|
|
45
|
+
if sym is None or sym == "none":
|
|
46
|
+
yield identity
|
|
47
|
+
return
|
|
48
|
+
|
|
49
|
+
if isinstance(sym, SymmetryGroup):
|
|
50
|
+
axes = getattr(sym, "axes", None)
|
|
51
|
+
for el in sym.elements():
|
|
52
|
+
arr = list(range(rank))
|
|
53
|
+
if axes is not None:
|
|
54
|
+
# elements() yields permutations on positions 0..len(axes)-1;
|
|
55
|
+
# map them to the actual tensor axis positions via sym.axes.
|
|
56
|
+
for local_i, local_j in enumerate(el.array_form):
|
|
57
|
+
if local_i < len(axes) and local_j < len(axes):
|
|
58
|
+
from_axis = axes[local_i]
|
|
59
|
+
to_axis = axes[local_j]
|
|
60
|
+
if from_axis < rank and to_axis < rank:
|
|
61
|
+
arr[from_axis] = to_axis
|
|
62
|
+
else:
|
|
63
|
+
# No axes annotation; embed at zero offset (legacy path).
|
|
64
|
+
for i, j in enumerate(el.array_form):
|
|
65
|
+
if i < rank:
|
|
66
|
+
arr[i] = j if j < rank else i
|
|
67
|
+
yield Permutation(arr)
|
|
68
|
+
return
|
|
69
|
+
|
|
70
|
+
if sym == "symmetric" or (isinstance(sym, dict) and sym.get("type") == "symmetric"):
|
|
71
|
+
gens = []
|
|
72
|
+
for k in range(rank - 1):
|
|
73
|
+
arr = list(range(rank))
|
|
74
|
+
arr[k], arr[k + 1] = arr[k + 1], arr[k]
|
|
75
|
+
gens.append(Permutation(arr))
|
|
76
|
+
if not gens:
|
|
77
|
+
yield identity
|
|
78
|
+
return
|
|
79
|
+
for el in _dimino(tuple(gens)):
|
|
80
|
+
yield el
|
|
81
|
+
return
|
|
82
|
+
|
|
83
|
+
if sym == "cyclic" or (isinstance(sym, dict) and sym.get("type") == "cyclic"):
|
|
84
|
+
if rank <= 1:
|
|
85
|
+
yield identity
|
|
86
|
+
return
|
|
87
|
+
rotation = list(range(1, rank)) + [0]
|
|
88
|
+
for el in _dimino((Permutation(rotation),)):
|
|
89
|
+
yield el
|
|
90
|
+
return
|
|
91
|
+
|
|
92
|
+
if sym == "dihedral" or (isinstance(sym, dict) and sym.get("type") == "dihedral"):
|
|
93
|
+
if rank <= 2:
|
|
94
|
+
for el in enumerate_h("symmetric", rank):
|
|
95
|
+
yield el
|
|
96
|
+
return
|
|
97
|
+
rot = list(range(1, rank)) + [0]
|
|
98
|
+
ref = list(range(rank))
|
|
99
|
+
for k in range(rank // 2):
|
|
100
|
+
ref[k], ref[rank - 1 - k] = ref[rank - 1 - k], ref[k]
|
|
101
|
+
for el in _dimino((Permutation(rot), Permutation(ref))):
|
|
102
|
+
yield el
|
|
103
|
+
return
|
|
104
|
+
|
|
105
|
+
raise ValueError(f"unsupported symmetry declaration: {sym!r}")
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _outer_permutations(m: int) -> Iterator[list[int]]:
|
|
109
|
+
"""Yield every permutation array of length m (S_m). Used for the outer factor."""
|
|
110
|
+
for perm_tuple in itertools.permutations(range(m)):
|
|
111
|
+
yield list(perm_tuple)
|
|
112
|
+
|
|
113
|
+
|
|
114
|
+
def _flatten_factor_to_row_perm(
|
|
115
|
+
group: Sequence[int],
|
|
116
|
+
base_tuple: Sequence[Permutation],
|
|
117
|
+
top_perm: Sequence[int],
|
|
118
|
+
u_offsets: Sequence[int],
|
|
119
|
+
axis_ranks: Sequence[int],
|
|
120
|
+
n_u: int,
|
|
121
|
+
) -> list[int]:
|
|
122
|
+
"""Build row-perm contribution for one identical-group factor.
|
|
123
|
+
|
|
124
|
+
Mirrors JS flattenFactorToRowPerm: arr[to] = from (inverse representation
|
|
125
|
+
consistent with the JS engine).
|
|
126
|
+
top_perm[j] = new position of copy j within the group.
|
|
127
|
+
base_tuple[j] = axis permutation applied to copy j's axes before relocation.
|
|
128
|
+
"""
|
|
129
|
+
arr = list(range(n_u))
|
|
130
|
+
for j in range(len(group)):
|
|
131
|
+
p = group[j]
|
|
132
|
+
rank = axis_ranks[p]
|
|
133
|
+
new_j = top_perm[j]
|
|
134
|
+
new_p = group[new_j]
|
|
135
|
+
h = base_tuple[j]
|
|
136
|
+
for a in range(rank):
|
|
137
|
+
from_idx = u_offsets[p] + a
|
|
138
|
+
to_idx = u_offsets[new_p] + h.array_form[a]
|
|
139
|
+
arr[to_idx] = from_idx
|
|
140
|
+
return arr
|
|
141
|
+
|
|
142
|
+
|
|
143
|
+
def enumerate_wreath(
|
|
144
|
+
*,
|
|
145
|
+
identical_groups: Sequence[Sequence[int]],
|
|
146
|
+
per_op_symmetry: Sequence[Any],
|
|
147
|
+
axis_ranks: Sequence[int],
|
|
148
|
+
u_offsets: Sequence[int],
|
|
149
|
+
) -> Iterator[WreathElement]:
|
|
150
|
+
"""Iterate ∏_i (H_i ≀ S_{m_i}) and yield row permutations on the U-vertices.
|
|
151
|
+
|
|
152
|
+
`identical_groups`: tuple of operand-index tuples, each grouping copies of
|
|
153
|
+
the same operand.
|
|
154
|
+
`per_op_symmetry`: parallel to operand index — declared H_i for each operand.
|
|
155
|
+
`axis_ranks`: parallel — number of axes per operand.
|
|
156
|
+
`u_offsets`: parallel — starting U-vertex index for each operand.
|
|
157
|
+
"""
|
|
158
|
+
total_u = sum(axis_ranks)
|
|
159
|
+
|
|
160
|
+
# For each identical-group, build a list of (arr, factor_meta) pairs.
|
|
161
|
+
per_group_options: list[list[tuple[list[int], dict]]] = []
|
|
162
|
+
for grp in identical_groups:
|
|
163
|
+
m = len(grp)
|
|
164
|
+
# Base H_i — all copies in the group share the same declared symmetry.
|
|
165
|
+
base_sym = per_op_symmetry[grp[0]]
|
|
166
|
+
base_rank = axis_ranks[grp[0]]
|
|
167
|
+
h_elements = list(enumerate_h(base_sym, base_rank))
|
|
168
|
+
|
|
169
|
+
group_options: list[tuple[list[int], dict]] = []
|
|
170
|
+
for top_perm in _outer_permutations(m):
|
|
171
|
+
for base_tuple in itertools.product(h_elements, repeat=m):
|
|
172
|
+
arr = _flatten_factor_to_row_perm(
|
|
173
|
+
grp, base_tuple, top_perm, u_offsets, axis_ranks, total_u
|
|
174
|
+
)
|
|
175
|
+
group_options.append(
|
|
176
|
+
(
|
|
177
|
+
arr,
|
|
178
|
+
{
|
|
179
|
+
"group": tuple(grp),
|
|
180
|
+
"outer": tuple(top_perm),
|
|
181
|
+
"base_arrs": tuple(tuple(h.array_form) for h in base_tuple),
|
|
182
|
+
},
|
|
183
|
+
)
|
|
184
|
+
)
|
|
185
|
+
per_group_options.append(group_options)
|
|
186
|
+
|
|
187
|
+
if not per_group_options:
|
|
188
|
+
# No operands: just identity.
|
|
189
|
+
yield WreathElement(
|
|
190
|
+
row_perm=Permutation.identity(total_u),
|
|
191
|
+
factorization={"groups": ()},
|
|
192
|
+
)
|
|
193
|
+
return
|
|
194
|
+
|
|
195
|
+
# Cartesian product across groups: merge contributions into a single row perm.
|
|
196
|
+
# Different groups touch disjoint U-vertex ranges, so merge = overwrite non-identity.
|
|
197
|
+
for combo in itertools.product(*per_group_options):
|
|
198
|
+
row_perm_arr = list(range(total_u))
|
|
199
|
+
for arr, _factor in combo:
|
|
200
|
+
for i in range(total_u):
|
|
201
|
+
if arr[i] != i:
|
|
202
|
+
row_perm_arr[i] = arr[i]
|
|
203
|
+
factorization = {
|
|
204
|
+
"groups": tuple(factor for _, factor in combo),
|
|
205
|
+
}
|
|
206
|
+
yield WreathElement(
|
|
207
|
+
row_perm=Permutation(row_perm_arr),
|
|
208
|
+
factorization=factorization,
|
|
209
|
+
)
|