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,153 @@
1
+ """Connected-component decomposition of G_pt's label-interaction graph.
2
+
3
+ Each component gets its own restricted group (generators + dimino closure), V/W split,
4
+ sizes, and visible_positions. The cost orchestrator invokes the regime ladder per component.
5
+ """
6
+
7
+ from __future__ import annotations
8
+
9
+ from collections.abc import Sequence
10
+ from dataclasses import dataclass
11
+
12
+ from flopscope._perm_group import _dimino
13
+ from flopscope._perm_group import _Permutation as Permutation
14
+
15
+ from ._detection import DetectedGroup, _classify_group_name
16
+
17
+
18
+ @dataclass(frozen=True)
19
+ class Component:
20
+ """One independent block of G_pt's action on labels."""
21
+
22
+ indices: tuple[int, ...] # positions in all_labels
23
+ labels: tuple[str, ...]
24
+ va: tuple[str, ...]
25
+ wa: tuple[str, ...]
26
+ sizes: tuple[int, ...]
27
+ visible_positions: tuple[int, ...]
28
+ generators: tuple[Permutation, ...]
29
+ elements: tuple[Permutation, ...]
30
+ order: int
31
+ group_name: str
32
+
33
+
34
+ class _UnionFind:
35
+ def __init__(self, n: int) -> None:
36
+ self._parent = list(range(n))
37
+ self._rank = [0] * n
38
+
39
+ def find(self, x: int) -> int:
40
+ while self._parent[x] != x:
41
+ self._parent[x] = self._parent[self._parent[x]]
42
+ x = self._parent[x]
43
+ return x
44
+
45
+ def union(self, a: int, b: int) -> None:
46
+ ra, rb = self.find(a), self.find(b)
47
+ if ra == rb:
48
+ return
49
+ if self._rank[ra] < self._rank[rb]:
50
+ ra, rb = rb, ra
51
+ self._parent[rb] = ra
52
+ if self._rank[ra] == self._rank[rb]:
53
+ self._rank[ra] += 1
54
+
55
+
56
+ def _build_label_interaction_components(
57
+ all_labels: Sequence[str],
58
+ generators: Sequence[Permutation],
59
+ ) -> list[list[int]]:
60
+ """Connected components of the label-interaction graph: labels are connected
61
+ if any single generator moves them together."""
62
+ n = len(all_labels)
63
+ uf = _UnionFind(n)
64
+ for gen in generators:
65
+ moved = [i for i in range(n) if gen.array_form[i] != i]
66
+ for j in range(1, len(moved)):
67
+ uf.union(moved[0], moved[j])
68
+ for i in range(n):
69
+ target = gen.array_form[i]
70
+ if target != i:
71
+ uf.union(i, target)
72
+
73
+ component_map: dict[int, list[int]] = {}
74
+ for i in range(n):
75
+ root = uf.find(i)
76
+ component_map.setdefault(root, []).append(i)
77
+ return [sorted(indices) for indices in component_map.values()]
78
+
79
+
80
+ def _restrict_permutation(
81
+ perm: Permutation, indices: Sequence[int]
82
+ ) -> Permutation | None:
83
+ """Restrict perm to the given global indices. Returns None if perm doesn't preserve
84
+ the index set."""
85
+ local_idx = {global_idx: local_pos for local_pos, global_idx in enumerate(indices)}
86
+ arr: list[int] = []
87
+ for global_idx in indices:
88
+ target = perm.array_form[global_idx]
89
+ if target not in local_idx:
90
+ return None
91
+ arr.append(local_idx[target])
92
+ return Permutation(arr)
93
+
94
+
95
+ def decompose_into_components(
96
+ *,
97
+ detected_group: DetectedGroup,
98
+ v_labels: frozenset[str],
99
+ w_labels: frozenset[str],
100
+ sizes: Sequence[int],
101
+ ) -> tuple[Component, ...]:
102
+ """Pure: G_pt + V/W → independent components. Used by einsum and (future) reduction code paths."""
103
+ all_labels = detected_group.all_labels
104
+ raw_components = _build_label_interaction_components(
105
+ all_labels, detected_group.generators
106
+ )
107
+
108
+ components: list[Component] = []
109
+ for indices in raw_components:
110
+ labels = tuple(all_labels[i] for i in indices)
111
+ va = tuple(lbl for lbl in labels if lbl in v_labels)
112
+ wa = tuple(lbl for lbl in labels if lbl in w_labels)
113
+ comp_sizes = tuple(sizes[i] for i in indices)
114
+ visible_positions = tuple(labels.index(lbl) for lbl in va)
115
+
116
+ restricted_gens: list[Permutation] = []
117
+ seen_keys: set[tuple[int, ...]] = set()
118
+ for gen in detected_group.generators:
119
+ r = _restrict_permutation(gen, indices)
120
+ if r is None:
121
+ continue
122
+ if r.is_identity:
123
+ continue
124
+ key = tuple(r.array_form)
125
+ if key in seen_keys:
126
+ continue
127
+ seen_keys.add(key)
128
+ restricted_gens.append(r)
129
+
130
+ if restricted_gens:
131
+ elements = _dimino(tuple(restricted_gens))
132
+ else:
133
+ elements = (Permutation.identity(len(indices)),)
134
+
135
+ order = len(elements)
136
+ group_name = _classify_group_name(labels, restricted_gens, elements)
137
+
138
+ components.append(
139
+ Component(
140
+ indices=tuple(indices),
141
+ labels=labels,
142
+ va=va,
143
+ wa=wa,
144
+ sizes=comp_sizes,
145
+ visible_positions=visible_positions,
146
+ generators=tuple(restricted_gens),
147
+ elements=tuple(elements),
148
+ order=order,
149
+ group_name=group_name,
150
+ )
151
+ )
152
+
153
+ return tuple(components)