schubmult 2.0.3__py3-none-any.whl → 3.0.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 (59) hide show
  1. schubmult/__init__.py +94 -1
  2. schubmult/perm_lib.py +233 -880
  3. schubmult/poly_lib/__init__.py +31 -0
  4. schubmult/poly_lib/poly_lib.py +244 -0
  5. schubmult/poly_lib/schub_poly.py +148 -0
  6. schubmult/poly_lib/variables.py +204 -0
  7. schubmult/rings/__init__.py +17 -0
  8. schubmult/rings/_quantum_schubert_polynomial_ring.py +788 -0
  9. schubmult/rings/_schubert_polynomial_ring.py +1006 -0
  10. schubmult/rings/_tensor_schub_ring.py +128 -0
  11. schubmult/rings/_utils.py +55 -0
  12. schubmult/{sage_integration → sage}/__init__.py +17 -15
  13. schubmult/{sage_integration → sage}/_fast_double_schubert_polynomial_ring.py +142 -220
  14. schubmult/{sage_integration → sage}/_fast_schubert_polynomial_ring.py +78 -72
  15. schubmult/sage/_indexing.py +51 -0
  16. schubmult/schub_lib/__init__.py +51 -0
  17. schubmult/{schubmult_double/_funcs.py → schub_lib/double.py} +618 -798
  18. schubmult/{schubmult_q/_funcs.py → schub_lib/quantum.py} +70 -72
  19. schubmult/schub_lib/quantum_double.py +954 -0
  20. schubmult/schub_lib/schub_lib.py +659 -0
  21. schubmult/{schubmult_py/_funcs.py → schub_lib/single.py} +58 -48
  22. schubmult/schub_lib/tests/__init__.py +0 -0
  23. schubmult/schub_lib/tests/legacy_perm_lib.py +946 -0
  24. schubmult/schub_lib/tests/test_vs_old.py +109 -0
  25. schubmult/scripts/__init__.py +0 -0
  26. schubmult/scripts/schubmult_double.py +378 -0
  27. schubmult/scripts/schubmult_py.py +84 -0
  28. schubmult/scripts/schubmult_q.py +109 -0
  29. schubmult/scripts/schubmult_q_double.py +207 -0
  30. schubmult/utils/__init__.py +0 -0
  31. schubmult/{_base_argparse.py → utils/argparse.py} +40 -11
  32. schubmult/utils/logging.py +16 -0
  33. schubmult/utils/parsing.py +20 -0
  34. schubmult/utils/perm_utils.py +135 -0
  35. schubmult/utils/test_utils.py +65 -0
  36. schubmult-3.0.0.dist-info/METADATA +1234 -0
  37. schubmult-3.0.0.dist-info/RECORD +41 -0
  38. {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/WHEEL +1 -1
  39. schubmult-3.0.0.dist-info/entry_points.txt +5 -0
  40. schubmult/_tests.py +0 -9
  41. schubmult/sage_integration/_indexing.py +0 -51
  42. schubmult/schubmult_double/__init__.py +0 -22
  43. schubmult/schubmult_double/__main__.py +0 -5
  44. schubmult/schubmult_double/_script.py +0 -474
  45. schubmult/schubmult_py/__init__.py +0 -13
  46. schubmult/schubmult_py/__main__.py +0 -5
  47. schubmult/schubmult_py/_script.py +0 -96
  48. schubmult/schubmult_q/__init__.py +0 -13
  49. schubmult/schubmult_q/__main__.py +0 -5
  50. schubmult/schubmult_q/_script.py +0 -160
  51. schubmult/schubmult_q_double/__init__.py +0 -17
  52. schubmult/schubmult_q_double/__main__.py +0 -5
  53. schubmult/schubmult_q_double/_funcs.py +0 -540
  54. schubmult/schubmult_q_double/_script.py +0 -398
  55. schubmult-2.0.3.dist-info/METADATA +0 -455
  56. schubmult-2.0.3.dist-info/RECORD +0 -30
  57. schubmult-2.0.3.dist-info/entry_points.txt +0 -5
  58. {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/licenses/LICENSE +0 -0
  59. {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/top_level.txt +0 -0
schubmult/perm_lib.py CHANGED
@@ -1,56 +1,198 @@
1
- from symengine import sympify, Mul, Pow, symarray
2
- from functools import cache
3
- from itertools import chain
4
- from bisect import bisect_left
5
- import numpy as np
1
+ import math
2
+ from functools import cache, cached_property
3
+
4
+ import sympy.combinatorics.permutations as spp
5
+ from symengine import sympify
6
+ from sympy import Basic, Tuple
7
+
8
+ import schubmult.utils.logging as lg
9
+ from schubmult.utils.perm_utils import cyclic_sort, permtrim_list, sg
10
+
11
+ # schubmult.poly_lib.variables import GeneratingSet
12
+
13
+ logger = lg.get_logger(__name__)
6
14
 
7
15
  zero = sympify(0)
8
16
  n = 100
9
17
 
10
- q_var = symarray("q", n)
18
+ # TODO: permutations act
19
+
20
+
21
+ class Permutation(Basic):
22
+ def __new__(cls, perm):
23
+ return Permutation.__xnew_cached__(cls, tuple(perm))
24
+
25
+ print_as_code = False
26
+
27
+ @staticmethod
28
+ @cache
29
+ def __xnew_cached__(_class, perm):
30
+ return Permutation.__xnew__(_class, perm)
31
+
32
+ @staticmethod
33
+ def __xnew__(_class, perm):
34
+ p = tuple(permtrim_list([*perm]))
35
+ s_perm = spp.Permutation._af_new([i - 1 for i in p])
36
+ obj = Basic.__new__(_class, Tuple(*perm))
37
+ obj._s_perm = s_perm
38
+ obj._perm = p
39
+ obj._hash_code = hash(p)
40
+ cd = s_perm.inversion_vector()
41
+ obj._unique_key = (len(p), sum([cd[i] * math.factorial(len(p) - 1 - i) for i in range(len(cd))]))
42
+ return obj
43
+
44
+ @classmethod
45
+ def sorting_perm(cls, itera):
46
+ L = [i + 1 for i in range(len(itera))]
47
+ L.sort(key=lambda i: itera[i - 1])
48
+ return Permutation(L)
49
+
50
+ def _sympystr(self, printer):
51
+ if Permutation.print_as_code:
52
+ return printer.doprint(trimcode(self))
53
+ return printer.doprint(self._perm)
54
+
55
+ def __call__(self, i):
56
+ """1-indexed"""
57
+ return self[i - 1]
58
+
59
+ def descents(self, zero_indexed=True):
60
+ if zero_indexed:
61
+ return self._s_perm.descents()
62
+ return {i + 1 for i in self._s_perm.descents()}
63
+
64
+ def get_cycles(self):
65
+ return self.get_cycles_cached()
66
+
67
+ @cache
68
+ def get_cycles_cached(self):
69
+ return [tuple(cyclic_sort([i + 1 for i in c])) for c in self._s_perm.cyclic_form]
70
+
71
+ @property
72
+ def code(self):
73
+ return list(self.cached_code())
74
+
75
+ @cache
76
+ def cached_code(self):
77
+ return self._s_perm.inversion_vector()
78
+
79
+ @cached_property
80
+ def inv(self):
81
+ return self._s_perm.inversions()
82
+
83
+ def swap(self, i, j):
84
+ new_perm = [*self._perm]
85
+ # print(f"SWAP {new_perm=}")
86
+ if i > j:
87
+ i, j = j, i
88
+ if j >= len(new_perm):
89
+ # print(f"SWAP {j}>={new_perm=}")
90
+ new_perm += list(range(len(new_perm) + 1, j + 2))
91
+ # print(f"SWAP extended {new_perm=}")
92
+ new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
93
+ # print(f"SWAP iddle {new_perm=}")
94
+ return Permutation(new_perm)
95
+
96
+ def __getitem__(self, i):
97
+ if isinstance(i, slice):
98
+ return [self[ii] for ii in range(i.start if i.start is not None else 0, i.stop if i.stop is not None else len(self))]
99
+ if i >= len(self._perm):
100
+ return i + 1
101
+ return self._perm[i]
102
+
103
+ def __setitem__(self, i, v):
104
+ raise NotImplementedError
105
+
106
+ def __hash__(self):
107
+ return self._hash_code
108
+
109
+ def __mul__(self, other):
110
+ new_sperm = other._s_perm * self._s_perm
111
+ new_perm = permtrim_list([new_sperm.array_form[i] + 1 for i in range(new_sperm.size)])
112
+ return Permutation(new_perm)
113
+
114
+ def __iter__(self):
115
+ yield from self._perm.__iter__()
116
+
117
+ def __getslice__(self, i, j):
118
+ return self._perm[i:j]
119
+
120
+ def __str__(self):
121
+ return str(self._perm)
122
+
123
+ def __add__(self, other):
124
+ if not isinstance(other, list):
125
+ raise NotImplementedError
126
+ permlist = [*self._perm, *other]
127
+ try:
128
+ return Permutation(permlist)
129
+ except Exception:
130
+ return permlist
131
+
132
+ def __radd__(self, other):
133
+ if not isinstance(other, list):
134
+ raise NotImplementedError
135
+ permlist = [*other, *self._perm]
136
+ try:
137
+ return Permutation(permlist)
138
+ except Exception:
139
+ return permlist
140
+
141
+ def __eq__(self, other):
142
+ if isinstance(other, Permutation):
143
+ # print(f"{other._perm= } {self._perm=} {type(self._perm)=}")
144
+ # return other._perm == self._perm
145
+ return other._unique_key == self._unique_key
146
+ if isinstance(other, list):
147
+ # print(f"{[*self._perm]= } {other=}")
148
+ return [*self._perm] == other
149
+ if isinstance(other, tuple):
150
+ # print(f"{self._perm=} {other=}")
151
+ return self._perm == other
152
+ return False
153
+
154
+ def __len__(self):
155
+ # print("REMOVE THIS")
156
+ return max(len(self._perm), 2)
157
+
158
+ def __invert__(self):
159
+ new_sperm = ~(self._s_perm)
160
+ new_perm = [new_sperm.array_form[i] + 1 for i in range(new_sperm.size)]
161
+ return Permutation(new_perm)
11
162
 
163
+ def __repr__(self):
164
+ return self.__str__()
12
165
 
13
- def getpermval(perm, index):
14
- if index < len(perm):
15
- return perm[index]
16
- return index + 1
166
+ def __lt__(self, other):
167
+ return tuple(self) < tuple(other)
17
168
 
18
169
 
170
+ def ensure_perms(func):
171
+ def wrapper(*args):
172
+ return func(*[Permutation(arg) if (isinstance(arg, list) or isinstance(arg, tuple)) else arg for arg in args])
173
+
174
+ return wrapper
175
+
176
+
177
+ @ensure_perms
19
178
  def inv(perm):
20
- L = len(perm)
21
- v = [i for i in range(1, L + 1)]
22
- ans = 0
23
- for i in range(L):
24
- itr = bisect_left(v, perm[i])
25
- ans += itr
26
- v = v[:itr] + v[itr + 1 :]
27
- return ans
179
+ return perm.inv
28
180
 
29
181
 
182
+ @ensure_perms
30
183
  def code(perm):
31
- L = len(perm)
32
- ret = []
33
- v = [i for i in range(1, L + 1)]
34
- for i in range(L - 1):
35
- itr = bisect_left(v, perm[i])
36
- ret += [itr]
37
- v = v[:itr] + v[itr + 1 :]
38
- return ret
184
+ return perm.code
39
185
 
40
186
 
187
+ @ensure_perms
41
188
  def mulperm(perm1, perm2):
42
- if len(perm1) < len(perm2):
43
- return [
44
- perm1[perm2[i] - 1] if perm2[i] <= len(perm1) else perm2[i] for i in range(len(perm2))
45
- ]
46
- else:
47
- return [perm1[perm2[i] - 1] for i in range(len(perm2))] + perm1[len(perm2) :]
189
+ return perm1 * perm2
48
190
 
49
191
 
50
192
  def uncode(cd):
51
193
  cd2 = [*cd]
52
194
  if cd2 == []:
53
- return [1, 2]
195
+ return Permutation([])
54
196
  max_required = max([cd2[i] + i for i in range(len(cd2))])
55
197
  cd2 += [0 for i in range(len(cd2), max_required)]
56
198
  fullperm = [i + 1 for i in range(len(cd2) + 1)]
@@ -58,129 +200,19 @@ def uncode(cd):
58
200
  for i in range(len(cd2)):
59
201
  perm += [fullperm.pop(cd2[i])]
60
202
  perm += [fullperm[0]]
61
- return perm
62
-
63
-
64
- def reversecode(perm):
65
- ret = []
66
- for i in range(len(perm) - 1, 0, -1):
67
- ret = [0] + ret
68
- for j in range(i, -1, -1):
69
- if perm[i] > perm[j]:
70
- ret[-1] += 1
71
- return ret
72
-
73
-
74
- def reverseuncode(cd):
75
- cd2 = list(cd)
76
- if cd2 == []:
77
- return [1, 2]
78
- # max_required = max([cd2[i]+i for i in range(len(cd2))])
79
- # cd2 += [0 for i in range(len(cd2),max_required)]
80
- fullperm = [i + 1 for i in range(len(cd2) + 1)]
81
- perm = []
82
- for i in range(len(cd2) - 1, 0, -1):
83
- perm = [fullperm[cd2[i]]] + perm
84
- fullperm.pop(cd2[i])
85
- perm += [fullperm[0]]
86
- return perm
203
+ return Permutation(perm)
87
204
 
88
205
 
206
+ @ensure_perms
89
207
  def inverse(perm):
90
- retperm = [0 for i in range(len(perm))]
91
- for i in range(len(perm)):
92
- retperm[perm[i] - 1] = i + 1
93
- return retperm
208
+ return ~perm
94
209
 
95
210
 
96
211
  def permtrim(perm):
97
- L = len(perm)
98
- while L > 2 and perm[-1] == L:
99
- L = perm.pop() - 1
100
- return perm
101
-
102
-
103
- def has_bruhat_descent(perm, i, j):
104
- if perm[i] < perm[j]:
105
- return False
106
- for p in range(i + 1, j):
107
- if perm[i] > perm[p] and perm[p] > perm[j]:
108
- return False
109
- return True
110
-
111
-
112
- def count_bruhat(perm, i, j):
113
- up_amount = 0
114
- if perm[i] < perm[j]:
115
- up_amount = 1
116
- else:
117
- up_amount = -1
118
- for k in range(i + 1, j):
119
- if perm[i] < perm[k] and perm[k] < perm[j]:
120
- up_amount += 2
121
- elif perm[i] > perm[k] and perm[k] > perm[j]:
122
- up_amount -= 2
123
- return up_amount
124
-
125
-
126
- def has_bruhat_ascent(perm, i, j):
127
- if perm[i] > perm[j]:
128
- return False
129
- for p in range(i + 1, j):
130
- if perm[i] < perm[p] and perm[p] < perm[j]:
131
- return False
132
- return True
133
-
134
-
135
- def elem_sym_perms(orig_perm, p, k):
136
- total_list = [(orig_perm, 0)]
137
- up_perm_list = [(orig_perm, 1000000000)]
138
- for pp in range(p):
139
- perm_list = []
140
- for up_perm, last in up_perm_list:
141
- up_perm2 = [*up_perm, len(up_perm) + 1]
142
- if len(up_perm2) < k + 1:
143
- up_perm2 += [i + 1 for i in range(len(up_perm2), k + 2)]
144
- pos_list = [i for i in range(k) if up_perm2[i] < last]
145
- for j in range(k, len(up_perm2)):
146
- if up_perm2[j] >= last:
147
- continue
148
- for i in pos_list:
149
- if has_bruhat_ascent(up_perm2, i, j):
150
- new_perm = [*up_perm2]
151
- new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
152
- if new_perm[-1] == len(new_perm):
153
- new_perm_add = tuple(new_perm[:-1])
154
- else:
155
- new_perm_add = tuple(new_perm)
156
- perm_list += [(new_perm_add, up_perm2[j])]
157
- total_list += [(new_perm_add, pp + 1)]
158
- up_perm_list = perm_list
159
- return total_list
160
-
161
-
162
- def elem_sym_perms_op(orig_perm, p, k):
163
- total_list = [(orig_perm, 0)]
164
- up_perm_list = [(orig_perm, k)]
165
- for pp in range(p):
166
- perm_list = []
167
- for up_perm, last in up_perm_list:
168
- up_perm2 = [*up_perm]
169
- if len(up_perm2) < k + 1:
170
- up_perm2 += [i + 1 for i in range(len(up_perm2), k + 2)]
171
- pos_list = [i for i in range(k) if getpermval(up_perm2, i) == getpermval(orig_perm, i)]
172
- for j in range(last, len(up_perm2)):
173
- for i in pos_list:
174
- if has_bruhat_descent(up_perm2, i, j):
175
- new_perm = [*up_perm2]
176
- new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
177
- new_perm_add = tuple(permtrim(new_perm))
178
- perm_list += [(new_perm_add, j)]
179
- total_list += [(new_perm_add, pp + 1)]
180
- up_perm_list = perm_list
181
- return total_list
212
+ return Permutation(perm)
182
213
 
183
214
 
215
+ @ensure_perms
184
216
  def strict_theta(u):
185
217
  ret = [*trimcode(u)]
186
218
  did_one = True
@@ -196,301 +228,20 @@ def strict_theta(u):
196
228
  return ret
197
229
 
198
230
 
199
- def elem_sym_perms_q(orig_perm, p, k, q_var=q_var):
200
- total_list = [(orig_perm, 0, 1)]
201
- up_perm_list = [(orig_perm, 1, 1000)]
202
- for pp in range(p):
203
- perm_list = []
204
- for up_perm, val, last_j in up_perm_list:
205
- up_perm2 = [*up_perm, len(up_perm) + 1]
206
- if len(up_perm2) < k + 1:
207
- up_perm2 += [i + 1 for i in range(len(up_perm2), k + 2)]
208
- pos_list = [
209
- i
210
- for i in range(k)
211
- if (i >= len(orig_perm) and up_perm2[i] == i + 1)
212
- or (i < len(orig_perm) and up_perm2[i] == orig_perm[i])
213
- ]
214
- for j in range(min(len(up_perm2) - 1, last_j), k - 1, -1):
215
- for i in pos_list:
216
- ct = count_bruhat(up_perm2, i, j)
217
- # print(f"{up_perm2=} {ct=} {i=} {j=} {k=} {pp=}")
218
- if ct == 1 or ct == 2 * (i - j) + 1:
219
- new_perm = [*up_perm2]
220
- new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
221
- new_perm_add = tuple(permtrim(new_perm))
222
- new_val = val
223
- if ct < 0:
224
- new_val *= np.prod([q_var[index] for index in range(i + 1, j + 1)])
225
- perm_list += [(new_perm_add, new_val, j)]
226
- total_list += [(new_perm_add, pp + 1, new_val)]
227
- up_perm_list = perm_list
228
- return total_list
229
-
230
-
231
- def elem_sym_perms_q_op(orig_perm, p, k, n, q_var=q_var):
232
- total_list = [(orig_perm, 0, 1)]
233
- up_perm_list = [(orig_perm, 1, k)]
234
- for pp in range(p):
235
- perm_list = []
236
- for up_perm, val, last_j in up_perm_list:
237
- up_perm2 = [*up_perm]
238
- if len(up_perm) < n:
239
- up_perm2 += [i + 1 for i in range(len(up_perm2), n)]
240
- pos_list = [
241
- i
242
- for i in range(k)
243
- if (i >= len(orig_perm) and up_perm2[i] == i + 1)
244
- or (i < len(orig_perm) and up_perm2[i] == orig_perm[i])
245
- ]
246
- for j in range(last_j, n):
247
- for i in pos_list:
248
- ct = count_bruhat(up_perm2, i, j)
249
- # print(f"{up_perm2=} {ct=} {i=} {j=} {k=} {pp=}")
250
- if ct == -1 or ct == 2 * (j - i) - 1:
251
- new_perm = [*up_perm2]
252
- new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
253
- new_perm_add = tuple(permtrim(new_perm))
254
- new_val = val
255
- if ct > 0:
256
- new_val *= np.prod([q_var[index] for index in range(i + 1, j + 1)])
257
- perm_list += [(new_perm_add, new_val, j)]
258
- total_list += [(new_perm_add, pp + 1, new_val)]
259
- up_perm_list = perm_list
260
- return total_list
261
-
262
-
263
- def q_vector(q_exp, q_var=q_var):
264
- qvar_list = q_var.tolist()
265
- ret = []
266
-
267
- if q_exp == 1:
268
- return ret
269
- if q_exp in q_var:
270
- i = qvar_list.index(q_exp)
271
- ret = [0 for j in range(i - 1)] + [1]
272
- return ret
273
- if isinstance(q_exp, Pow):
274
- qv = q_exp.args[0]
275
- expon = int(q_exp.args[1])
276
- i = qvar_list.index(qv)
277
- ret = [0 for j in range(i - 1)] + [expon]
278
- return ret
279
- if isinstance(q_exp, Mul):
280
- for a in q_exp.args:
281
- v1 = q_vector(a)
282
- v1 += [0 for i in range(len(v1), len(ret))]
283
- ret += [0 for i in range(len(ret), len(v1))]
284
- ret = [ret[i] + v1[i] for i in range(len(ret))]
285
- return ret
286
-
287
- return None
288
-
289
-
290
- def omega(i, qv):
291
- i = i - 1
292
- if len(qv) == 0 or i > len(qv):
293
- return 0
294
- if i == 0:
295
- if len(qv) == 1:
296
- return 2 * qv[0]
297
- return 2 * qv[0] - qv[1]
298
- if i == len(qv):
299
- return -qv[-1]
300
- if i == len(qv) - 1:
301
- return 2 * qv[-1] - qv[-2]
302
- return 2 * qv[i] - qv[i - 1] - qv[i + 1]
303
-
304
-
305
- def sg(i, w):
306
- if i >= len(w) - 1 or w[i] < w[i + 1]:
307
- return 0
308
- return 1
309
-
310
-
311
- def reduce_q_coeff(u, v, w, qv):
312
- for i in range(len(qv)):
313
- if sg(i, v) == 1 and sg(i, u) == 0 and sg(i, w) + omega(i + 1, qv) == 1:
314
- ret_v = [*v]
315
- ret_v[i], ret_v[i + 1] = ret_v[i + 1], ret_v[i]
316
- ret_w = [*w] + [j + 1 for j in range(len(w), i + 2)]
317
- ret_w[i], ret_w[i + 1] = ret_w[i + 1], ret_w[i]
318
- qv_ret = [*qv]
319
- if sg(i, w) == 0:
320
- qv_ret[i] -= 1
321
- return u, tuple(permtrim(ret_v)), tuple(permtrim(ret_w)), qv_ret, True
322
- elif sg(i, u) == 1 and sg(i, v) == 0 and sg(i, w) + omega(i + 1, qv) == 1:
323
- ret_u = [*u]
324
- ret_u[i], ret_u[i + 1] = ret_u[i + 1], ret_u[i]
325
- ret_w = [*w] + [j + 1 for j in range(len(w), i + 2)]
326
- ret_w[i], ret_w[i + 1] = ret_w[i + 1], ret_w[i]
327
- qv_ret = [*qv]
328
- if sg(i, w) == 0:
329
- qv_ret[i] -= 1
330
- return tuple(permtrim(ret_u)), v, tuple(permtrim(ret_w)), qv_ret, True
331
- elif sg(i, u) == 1 and sg(i, v) == 1 and sg(i, w) + omega(i + 1, qv) == 2:
332
- ret_u = [*u]
333
- ret_u[i], ret_u[i + 1] = ret_u[i + 1], ret_u[i]
334
- ret_w = [*w] + [j + 1 for j in range(len(w), i + 2)]
335
- ret_w[i], ret_w[i + 1] = ret_w[i + 1], ret_w[i]
336
- qv_ret = [*qv]
337
- if sg(i, w) == 0:
338
- qv_ret[i] -= 1
339
- return tuple(permtrim(ret_u)), v, tuple(permtrim(ret_w)), qv_ret, True
340
- return u, v, w, qv, False
341
-
342
-
343
- def reduce_q_coeff_u_only(u, v, w, qv):
344
- for i in range(len(qv)):
345
- if sg(i, u) == 1 and sg(i, v) == 0 and sg(i, w) + omega(i + 1, qv) == 1:
346
- ret_u = [*u]
347
- ret_u[i], ret_u[i + 1] = ret_u[i + 1], ret_u[i]
348
- ret_w = [*w] + [j + 1 for j in range(len(w), i + 2)]
349
- ret_w[i], ret_w[i + 1] = ret_w[i + 1], ret_w[i]
350
- qv_ret = [*qv]
351
- if sg(i, w) == 0:
352
- qv_ret[i] -= 1
353
- return tuple(permtrim(ret_u)), v, tuple(permtrim(ret_w)), qv_ret, True
354
- elif sg(i, u) == 1 and sg(i, v) == 1 and sg(i, w) + omega(i + 1, qv) == 2:
355
- ret_u = [*u]
356
- ret_u[i], ret_u[i + 1] = ret_u[i + 1], ret_u[i]
357
- ret_w = [*w] + [j + 1 for j in range(len(w), i + 2)]
358
- ret_w[i], ret_w[i + 1] = ret_w[i + 1], ret_w[i]
359
- qv_ret = [*qv]
360
- if sg(i, w) == 0:
361
- qv_ret[i] -= 1
362
- return tuple(permtrim(ret_u)), v, tuple(permtrim(ret_w)), qv_ret, True
363
- return u, v, w, qv, False
364
-
365
-
366
231
  def longest_element(indices):
367
- perm = [1, 2]
232
+ perm = Permutation([1, 2])
368
233
  did_one = True
369
234
  while did_one:
370
235
  did_one = False
371
236
  for i in range(len(indices)):
372
237
  j = indices[i] - 1
373
238
  if sg(j, perm) == 0:
374
- if len(perm) < j + 2:
375
- perm = perm + [index for index in range(len(perm) + 1, j + 3)]
376
- perm[j], perm[j + 1] = perm[j + 1], perm[j]
239
+ perm = perm.swap(j, j + 1)
377
240
  did_one = True
378
241
  return permtrim(perm)
379
242
 
380
243
 
381
- def count_less_than(arr, val):
382
- ct = 0
383
- i = 0
384
- while i < len(arr) and arr[i] < val:
385
- i += 1
386
- ct += 1
387
- return ct
388
-
389
-
390
- def is_parabolic(w, parabolic_index):
391
- for i in parabolic_index:
392
- if sg(i - 1, w) == 1:
393
- return False
394
- return True
395
-
396
-
397
- def check_blocks(qv, parabolic_index):
398
- blocks = []
399
- cur_block = []
400
- last_val = -1
401
- for i in range(len(parabolic_index)):
402
- if last_val == -1 or last_val + 1 == parabolic_index[i]:
403
- last_val = parabolic_index[i]
404
- cur_block += [last_val]
405
- else:
406
- blocks += [cur_block]
407
- cur_block = []
408
- for block in blocks:
409
- for i in range(len(block)):
410
- for j in range(i, len(block)):
411
- val = 0
412
- for k in range(i, j + 1):
413
- val += omega(block[k], qv)
414
- if val != 0 and val != -1:
415
- return False
416
- return True
417
-
418
-
419
- # perms and inversion diff
420
- def kdown_perms(perm, monoperm, p, k):
421
- inv_m = inv(monoperm)
422
- inv_p = inv(perm)
423
- full_perm_list = []
424
-
425
- if inv(mulperm(list(perm), monoperm)) == inv_m - inv_p:
426
- full_perm_list += [(tuple(perm), 0, 1)]
427
-
428
- down_perm_list = [(perm, 1)]
429
- if len(perm) < k:
430
- return full_perm_list
431
- a2 = k - 1
432
- for pp in range(1, p + 1):
433
- down_perm_list2 = []
434
- for perm2, s in down_perm_list:
435
- L = len(perm2)
436
- if L < k:
437
- continue
438
- s2 = -s
439
- for b in chain(range(k - 1), range(k, L)):
440
- if perm2[b] != perm[b]:
441
- continue
442
- if b < a2:
443
- i, j = b, a2
444
- else:
445
- i, j, s2 = a2, b, s
446
- if has_bruhat_descent(perm2, i, j):
447
- new_perm = [*perm2]
448
- new_perm[a2], new_perm[b] = new_perm[b], new_perm[a2]
449
- permtrim(new_perm)
450
- down_perm_list2 += [(new_perm, s2)]
451
- if inv(mulperm(new_perm, monoperm)) == inv_m - inv_p + pp:
452
- full_perm_list += [(tuple(new_perm), pp, s2)]
453
- down_perm_list = down_perm_list2
454
- return full_perm_list
455
-
456
-
457
- def compute_vpathdicts(th, vmu, smpify=False):
458
- vpathdicts = [{} for index in range(len(th))]
459
- vpathdicts[-1][tuple(vmu)] = None
460
- thL = len(th)
461
-
462
- top = code(inverse(uncode(th)))
463
- for i in range(thL - 1, -1, -1):
464
- top2 = code(inverse(uncode(top)))
465
- while top2[-1] == 0:
466
- top2.pop()
467
- top2.pop()
468
- top = code(inverse(uncode(top2)))
469
- monoperm = uncode(top)
470
- if len(monoperm) < 2:
471
- monoperm = [1, 2]
472
- k = i + 1
473
- for last_perm in vpathdicts[i]:
474
- newperms = kdown_perms(last_perm, monoperm, th[i], k)
475
- vpathdicts[i][last_perm] = newperms
476
- if i > 0:
477
- for trip in newperms:
478
- vpathdicts[i - 1][trip[0]] = None
479
- vpathdicts2 = [{} for i in range(len(th))]
480
- for i in range(len(th)):
481
- for key, valueset in vpathdicts[i].items():
482
- for value in valueset:
483
- key2 = value[0]
484
- if key2 not in vpathdicts2[i]:
485
- vpathdicts2[i][key2] = set()
486
- v2 = value[2]
487
- if smpify:
488
- v2 = sympify(v2)
489
- vpathdicts2[i][key2].add((key, value[1], v2))
490
- # print(vpathdicts2)
491
- return vpathdicts2
492
-
493
-
244
+ @ensure_perms
494
245
  def theta(perm):
495
246
  cd = code(perm)
496
247
  for i in range(len(cd) - 1, 0, -1):
@@ -501,147 +252,30 @@ def theta(perm):
501
252
  return cd
502
253
 
503
254
 
504
- def add_perm_dict(d1, d2):
505
- for k, v in d2.items():
506
- d1[k] = d1.get(k, 0) + v
507
- return d1
508
-
509
-
510
- one = sympify(1)
511
-
512
-
513
- def elem_sym_poly_q(p, k, varl1, varl2, q_var=q_var):
514
- if p == 0 and k >= 0:
515
- return one
516
- if p < 0 or p > k:
517
- return zero
518
- return (
519
- (varl1[k - 1] - varl2[k - p]) * elem_sym_poly_q(p - 1, k - 1, varl1, varl2, q_var)
520
- + elem_sym_poly_q(p, k - 1, varl1, varl2, q_var)
521
- + q_var[k - 1] * elem_sym_poly_q(p - 2, k - 2, varl1, varl2, q_var)
522
- )
523
-
524
-
525
- def elem_sym_poly(p, k, varl1, varl2, xstart=0, ystart=0):
526
- global zero, one
527
- if p > k:
528
- return zero
529
- if p == 0:
530
- return one
531
- if p == 1:
532
- res = varl1[xstart] - varl2[ystart]
533
- for i in range(1, k):
534
- res += varl1[xstart + i] - varl2[ystart + i]
535
- return res
536
- if p == k:
537
- res = (varl1[xstart] - varl2[ystart]) * (varl1[xstart + 1] - varl2[ystart])
538
- for i in range(2, k):
539
- res *= varl1[i + xstart] - varl2[ystart]
540
- return res
541
- mid = k // 2
542
- xsm = xstart + mid
543
- ysm = ystart + mid
544
- kmm = k - mid
545
- res = elem_sym_poly(p, mid, varl1, varl2, xstart, ystart) + elem_sym_poly(
546
- p, kmm, varl1, varl2, xsm, ysm
547
- )
548
- for p2 in range(max(1, p - kmm), min(p, mid + 1)):
549
- res += elem_sym_poly(p2, mid, varl1, varl2, xstart, ystart) * elem_sym_poly(
550
- p - p2, kmm, varl1, varl2, xsm, ysm - p2
551
- )
552
- return res
553
-
554
-
555
- @cache
556
- def call_zvars(v1, v2, k, i):
557
- v3 = [*v2] + [j for j in range(len(v2) + 1, i + 1)]
558
- zvars = (
559
- [v3[i - 1]]
560
- + [v3[j] for j in range(len(v1), len(v3)) if v3[j] != j + 1 and j != i - 1]
561
- + [v3[j] for j in range(len(v1)) if v1[j] != v3[j] and j != i - 1]
562
- )
563
- return zvars
564
-
565
-
566
- def elem_sym_func(k, i, u1, u2, v1, v2, udiff, vdiff, varl1, varl2):
567
- global zero, one
568
- newk = k - udiff
569
- if newk < vdiff:
570
- return zero
571
- if newk == vdiff:
572
- return one
573
- yvars = []
574
- for j in range(min(len(u1), k)):
575
- if u1[j] == u2[j]:
576
- yvars += [varl1[u2[j]]]
577
- for j in range(len(u1), min(k, len(u2))):
578
- if u2[j] == j + 1:
579
- yvars += [varl1[u2[j]]]
580
- for j in range(len(u2), k):
581
- yvars += [varl1[j + 1]]
582
- zvars = [varl2[i] for i in call_zvars(v1, v2, k, i)]
583
- return elem_sym_poly(newk - vdiff, newk, yvars, zvars)
584
-
585
-
586
- def elem_sym_func_q(k, i, u1, u2, v1, v2, udiff, vdiff, varl1, varl2):
587
- global zero, one
588
- newk = k - udiff
589
- if newk < vdiff:
590
- return zero
591
- if newk == vdiff:
592
- return one
593
- yvars = []
594
- mlen = max(len(u1), len(u2))
595
- u1 = [*u1] + [a + 1 for a in range(len(u1), mlen)]
596
- u2 = [*u2] + [a + 1 for a in range(len(u2), mlen)]
597
- for j in range(min(len(u1), k)):
598
- if u1[j] == u2[j]:
599
- yvars += [varl1[u2[j]]]
600
- for j in range(len(u1), min(k, len(u2))):
601
- if u2[j] == j + 1:
602
- yvars += [varl1[u2[j]]]
603
- for j in range(len(u2), k):
604
- yvars += [varl1[j + 1]]
605
- zvars = [varl2[a] for a in call_zvars(v1, v2, k, i)]
606
- return elem_sym_poly(newk - vdiff, newk, yvars, zvars)
607
-
608
-
255
+ @ensure_perms
609
256
  def trimcode(perm):
610
- cd = code(perm)
257
+ cd = perm.code
611
258
  while len(cd) > 0 and cd[-1] == 0:
612
259
  cd.pop()
613
260
  return cd
614
261
 
615
262
 
616
- def p_trans(part):
617
- newpart = []
618
- if len(part) == 0 or part[0] == 0:
619
- return [0]
620
- for i in range(1, part[0] + 1):
621
- cnt = 0
622
- for j in range(len(part)):
623
- if part[j] >= i:
624
- cnt += 1
625
- if cnt == 0:
626
- break
627
- newpart += [cnt]
628
- return newpart
629
-
630
-
631
263
  def cycle(p, q):
632
- return [i for i in range(1, p)] + [i + 1 for i in range(p, p + q)] + [p]
264
+ return Permutation(list(range(1, p)) + [i + 1 for i in range(p, p + q)] + [p])
633
265
 
634
266
 
267
+ @ensure_perms
635
268
  def phi1(u):
636
- c_star = code(inverse(u))
269
+ c_star = (~u).code
637
270
  c_star.pop(0)
638
- phi_u = inverse(uncode(c_star))
639
- return phi_u
271
+ # print(f"{uncode(c_star)=}")
272
+ return ~(uncode(c_star))
640
273
 
641
274
 
275
+ @ensure_perms
642
276
  def one_dominates(u, w):
643
- c_star_u = code(inverse(u))
644
- c_star_w = code(inverse(w))
277
+ c_star_u = (~u).code
278
+ c_star_w = (~w).code
645
279
 
646
280
  a = c_star_u[0]
647
281
  b = c_star_w[0]
@@ -655,332 +289,16 @@ def one_dominates(u, w):
655
289
 
656
290
 
657
291
  def dominates(u, w):
658
- u2 = [*u]
659
- w2 = [*w]
660
- while u2 != [1, 2] and one_dominates(u2, w2):
292
+ u2 = u
293
+ w2 = w
294
+ while inv(u2) > 0 and one_dominates(u2, w2):
661
295
  u2 = phi1(u2)
662
296
  w2 = phi1(w2)
663
- if u2 == [1, 2]:
297
+ if inv(u2) == 0:
664
298
  return True
665
299
  return False
666
300
 
667
301
 
668
- def reduce_coeff(u, v, w):
669
- t_mu_u_t = theta(inverse(u))
670
- t_mu_v_t = theta(inverse(v))
671
-
672
- mu_u_inv = uncode(t_mu_u_t)
673
- mu_v_inv = uncode(t_mu_v_t)
674
-
675
- t_mu_u = p_trans(t_mu_u_t)
676
- t_mu_v = p_trans(t_mu_v_t)
677
-
678
- t_mu_u += [0 for i in range(len(t_mu_u), max(len(t_mu_u), len(t_mu_v)))]
679
- t_mu_v += [0 for i in range(len(t_mu_v), max(len(t_mu_u), len(t_mu_v)))]
680
-
681
- t_mu_uv = [t_mu_u[i] + t_mu_v[i] for i in range(len(t_mu_u))]
682
- t_mu_uv_t = p_trans(t_mu_uv)
683
-
684
- mu_uv_inv = uncode(t_mu_uv_t)
685
-
686
- if inv(mulperm(list(w), mu_uv_inv)) != inv(mu_uv_inv) - inv(w):
687
- return u, v, w
688
-
689
- umu = mulperm(list(u), mu_u_inv)
690
- vmu = mulperm(list(v), mu_v_inv)
691
- wmu = mulperm(list(w), mu_uv_inv)
692
-
693
- t_mu_w = theta(inverse(wmu))
694
-
695
- mu_w = uncode(t_mu_w)
696
-
697
- w_prime = mulperm(wmu, mu_w)
698
-
699
- if permtrim(list(w)) == permtrim(w_prime):
700
- return (permtrim(list(u)), permtrim(list(v)), permtrim(list(w)))
701
-
702
- A = []
703
- B = []
704
- indexA = 0
705
-
706
- while len(t_mu_u_t) > 0 and t_mu_u_t[-1] == 0:
707
- t_mu_u_t.pop()
708
-
709
- while len(t_mu_v_t) > 0 and t_mu_v_t[-1] == 0:
710
- t_mu_v_t.pop()
711
-
712
- while len(t_mu_uv_t) > 0 and t_mu_uv_t[-1] == 0:
713
- t_mu_uv_t.pop()
714
-
715
- for index in range(len(t_mu_uv_t)):
716
- if indexA < len(t_mu_u_t) and t_mu_uv_t[index] == t_mu_u_t[indexA]:
717
- A += [index]
718
- indexA += 1
719
- else:
720
- B += [index]
721
-
722
- mu_w_A = uncode(mu_A(code(mu_w), A))
723
- mu_w_B = uncode(mu_A(code(mu_w), B))
724
-
725
- return (
726
- permtrim(mulperm(umu, mu_w_A)),
727
- permtrim(mulperm(vmu, mu_w_B)),
728
- permtrim(w_prime),
729
- )
730
-
731
-
732
- def mu_A(mu, A):
733
- mu_t = p_trans(mu)
734
- mu_A_t = []
735
- for i in range(len(A)):
736
- if A[i] < len(mu_t):
737
- mu_A_t += [mu_t[A[i]]]
738
- return p_trans(mu_A_t)
739
-
740
-
741
- def reduce_descents(u, v, w):
742
- u2 = [*u]
743
- v2 = [*v]
744
- w2 = [*w]
745
- found_one = True
746
- while found_one:
747
- found_one = False
748
- if (
749
- will_formula_work(u2, v2)
750
- or will_formula_work(v2, u2)
751
- or one_dominates(u2, w2)
752
- or is_reducible(v2)
753
- or inv(w2) - inv(u2) == 1
754
- ):
755
- break
756
- for i in range(len(w2) - 2, -1, -1):
757
- if (
758
- w2[i] > w2[i + 1]
759
- and i < len(v2) - 1
760
- and v2[i] > v2[i + 1]
761
- and (i >= len(u2) - 1 or u2[i] < u2[i + 1])
762
- ):
763
- w2[i], w2[i + 1] = w2[i + 1], w2[i]
764
- v2[i], v2[i + 1] = v2[i + 1], v2[i]
765
- found_one = True
766
- elif (
767
- w2[i] > w2[i + 1]
768
- and i < len(u2) - 1
769
- and u2[i] > u2[i + 1]
770
- and (i >= len(v2) - 1 or v2[i] < v2[i + 1])
771
- ):
772
- w2[i], w2[i + 1] = w2[i + 1], w2[i]
773
- u2[i], u2[i + 1] = u2[i + 1], u2[i]
774
- found_one = True
775
- if found_one:
776
- break
777
- return permtrim(u2), permtrim(v2), permtrim(w2)
778
-
779
-
780
- def is_reducible(v):
781
- c03 = code(v)
782
- found0 = False
783
- good = True
784
- for i in range(len(c03)):
785
- if c03[i] == 0:
786
- found0 = True
787
- elif c03[i] != 0 and found0:
788
- good = False
789
- break
790
- return good
791
-
792
-
793
- def try_reduce_v(u, v, w):
794
- if is_reducible(v):
795
- return tuple(permtrim([*u])), tuple(permtrim([*v])), tuple(permtrim([*w]))
796
- u2 = [*u]
797
- v2 = [*v]
798
- w2 = [*w]
799
- cv = code(v2)
800
- for i in range(len(v2) - 2, -1, -1):
801
- if cv[i] == 0 and i < len(cv) - 1 and cv[i + 1] != 0:
802
- if i >= len(u2) - 1 or u2[i] < u2[i + 1]:
803
- v2[i], v2[i + 1] = v2[i + 1], v2[i]
804
- if i >= len(w2) - 1:
805
- w2 += [j for j in range(len(w2) + 1, i + 3)]
806
- w2[i + 1], w2[i] = w2[i], w2[i + 1]
807
- if is_reducible(v2):
808
- return tuple(permtrim(u2)), tuple(permtrim(v2)), tuple(permtrim(w2))
809
- else:
810
- return try_reduce_v(u2, v2, w2)
811
- elif i < len(w2) - 1 and w2[i] > w2[i + 1]:
812
- u2[i], u2[i + 1] = u2[i + 1], u2[i]
813
- v2[i], v2[i + 1] = v2[i + 1], v2[i]
814
- return try_reduce_v(u2, v2, w2)
815
- else:
816
- return tuple(permtrim(u2)), tuple(permtrim(v2)), tuple(permtrim(w2))
817
- return tuple(permtrim(u2)), tuple(permtrim(v2)), tuple(permtrim(w2))
818
-
819
-
820
- def try_reduce_u(u, v, w):
821
- if one_dominates(u, w):
822
- return u, v, w
823
- u2 = [*u]
824
- v2 = [*v]
825
- w2 = [*w]
826
- cu = code(u)
827
- for i in range(len(u2) - 2, -1, -1):
828
- if cu[i] == 0 and i < len(cu) - 1 and cu[i + 1] != 0:
829
- if i >= len(v2) - 1 or v2[i] < v2[i + 1]:
830
- u2[i], u2[i + 1] = u2[i + 1], u2[i]
831
- if i > len(w2) - 1:
832
- w2 += [j for j in range(len(w2) + 1, i + 3)]
833
- w2[i + 1], w2[i] = w2[i], w2[i + 1]
834
- if one_dominates(u, w):
835
- return tuple(permtrim(u2)), tuple(permtrim(v2)), tuple(permtrim(w2))
836
- else:
837
- return try_reduce_u(u2, v2, w2)
838
- elif i < len(w2) - 1 and w2[i] > w2[i + 1]:
839
- u2[i], u2[i + 1] = u2[i + 1], u2[i]
840
- v2[i], v2[i + 1] = v2[i + 1], v2[i]
841
- return try_reduce_u(u2, v2, w2)
842
- else:
843
- return tuple(permtrim(u2)), tuple(permtrim(v2)), tuple(permtrim(w2))
844
- return tuple(permtrim(u2)), tuple(permtrim(v2)), tuple(permtrim(w2))
845
-
846
-
847
- def divdiffable(v, u):
848
- inv_v = inv(v)
849
- inv_u = inv(u)
850
- perm2 = permtrim(mulperm(v, inverse(u)))
851
- if inv(perm2) != inv_v - inv_u:
852
- return []
853
- return perm2
854
-
855
-
856
- def will_formula_work(u, v):
857
- muv = uncode(theta(v))
858
- vn1muv = mulperm(inverse(v), muv)
859
- while True:
860
- found_one = False
861
- for i in range(len(vn1muv) - 1):
862
- if vn1muv[i] > vn1muv[i + 1]:
863
- found_one = True
864
- if i < len(u) - 1 and u[i] > u[i + 1]:
865
- return False
866
- else:
867
- vn1muv[i], vn1muv[i + 1] = vn1muv[i + 1], vn1muv[i]
868
- break
869
- if not found_one:
870
- return True
871
-
872
-
873
- def pull_out_var(vnum, v):
874
- vup = v + [len(v) + 1]
875
- if vnum >= len(v):
876
- return [[[], v]]
877
- vpm_list = [(vup, 0)]
878
- ret_list = []
879
- for p in range(len(v) + 1 - vnum):
880
- vpm_list2 = []
881
- for vpm, b in vpm_list:
882
- if vpm[vnum - 1] == len(v) + 1:
883
- vpm2 = [*vpm]
884
- vpm2.pop(vnum - 1)
885
- vp = permtrim(vpm2)
886
- ret_list += [
887
- [
888
- [
889
- v[i]
890
- for i in range(vnum, len(v))
891
- if ((i > len(vp) and v[i] == i) or (i <= len(vp) and v[i] == vp[i - 1]))
892
- ],
893
- vp,
894
- ]
895
- ]
896
- for j in range(vnum, len(vup)):
897
- if vpm[j] <= b:
898
- continue
899
- for i in range(vnum):
900
- if has_bruhat_ascent(vpm, i, j):
901
- vpm[i], vpm[j] = vpm[j], vpm[i]
902
- vpm_list2 += [([*vpm], vpm[i])]
903
- vpm[i], vpm[j] = vpm[j], vpm[i]
904
- vpm_list = vpm_list2
905
- for vpm, b in vpm_list:
906
- if vpm[vnum - 1] == len(v) + 1:
907
- vpm2 = [*vpm]
908
- vpm2.pop(vnum - 1)
909
- vp = permtrim(vpm2)
910
- ret_list += [
911
- [
912
- [
913
- v[i]
914
- for i in range(vnum, len(v))
915
- if ((i > len(vp) and v[i] == i) or (i <= len(vp) and v[i] == vp[i - 1]))
916
- ],
917
- vp,
918
- ]
919
- ]
920
- return ret_list
921
-
922
-
923
- def get_cycles(perm):
924
- cycle_set = []
925
- done_vals = set()
926
- for i in range(len(perm)):
927
- p = i + 1
928
- if perm[i] == p:
929
- continue
930
- if p in done_vals:
931
- continue
932
- cycle = []
933
- m = -1
934
- max_index = -1
935
- while p not in done_vals:
936
- cycle += [p]
937
- done_vals.add(p)
938
- if p > m:
939
- m = p
940
- max_index = len(cycle) - 1
941
- p = perm[p - 1]
942
- cycle = tuple(cycle[max_index + 1 :] + cycle[: max_index + 1])
943
- cycle_set += [cycle]
944
- return cycle_set
945
-
946
-
947
- def double_elem_sym_q(u, p1, p2, k, q_var=q_var):
948
- ret_list = {}
949
- perms1 = elem_sym_perms_q(u, p1, k, q_var)
950
- iu = inverse(u)
951
- for perm1, udiff1, mul_val1 in perms1:
952
- perms2 = elem_sym_perms_q(perm1, p2, k, q_var)
953
- cycles1 = get_cycles(tuple(permtrim(mulperm(iu, [*perm1]))))
954
- cycles1_dict = {}
955
- for c in cycles1:
956
- if c[-1] not in cycles1_dict:
957
- cycles1_dict[c[-1]] = []
958
- cycles1_dict[c[-1]] += [set(c)]
959
- ip1 = inverse(perm1)
960
- for perm2, udiff2, mul_val2 in perms2:
961
- cycles2 = get_cycles(tuple(permtrim(mulperm(ip1, [*perm2]))))
962
- good = True
963
- for i in range(len(cycles2)):
964
- c2 = cycles2[i]
965
- if c2[-1] not in cycles1_dict:
966
- continue
967
- for c1_s in cycles1_dict[c2[-1]]:
968
- for a in range(len(c2) - 2, -1, -1):
969
- if c2[a] in c1_s:
970
- good = False
971
- break
972
- if not good:
973
- break
974
- if not good:
975
- break
976
-
977
- if good:
978
- if (perm1, udiff1, mul_val1) not in ret_list:
979
- ret_list[(perm1, udiff1, mul_val1)] = []
980
- ret_list[(perm1, udiff1, mul_val1)] += [(perm2, udiff2, mul_val2)]
981
- return ret_list
982
-
983
-
984
302
  def medium_theta(perm):
985
303
  cd = code(perm)
986
304
  found_one = True
@@ -992,8 +310,43 @@ def medium_theta(perm):
992
310
  cd[i], cd[i + 1] = cd[i + 1] + 1, cd[i]
993
311
  break
994
312
  if cd[i] == cd[i + 1] and cd[i] != 0 and i > 0 and cd[i - 1] <= cd[i] + 1:
995
- # if cd[i]==cd[i+1] and i>0 and cd[i-1]<=cd[i]+1:
996
313
  cd[i] += 1
997
314
  found_one = True
998
315
  break
999
316
  return cd
317
+
318
+
319
+ def split_perms(perms):
320
+ perms2 = [perms[0]]
321
+ for perm in perms[1:]:
322
+ cd = code(perm)
323
+ index = -1
324
+ not_zero = False
325
+ did = False
326
+ for i in range(len(cd)):
327
+ if cd[i] != 0:
328
+ not_zero = True
329
+ elif not_zero and cd[i] == 0:
330
+ not_zero = False
331
+ index = i
332
+ num_zeros_to_miss = 0
333
+ for j in range(index):
334
+ if cd[j] != 0:
335
+ num_zeros_to_miss = max(num_zeros_to_miss, cd[j] - (index - 1 - j))
336
+ num_zeros = 0
337
+ for j in range(index, len(cd)):
338
+ if cd[j] != 0:
339
+ break
340
+ num_zeros += 1
341
+ if num_zeros >= num_zeros_to_miss:
342
+ cd1 = cd[:index]
343
+ cd2 = [0 for i in range(index)] + cd[index:]
344
+ perms2 += [
345
+ uncode(cd1),
346
+ uncode(cd2),
347
+ ]
348
+ did = True
349
+ break
350
+ if not did:
351
+ perms2 += [perm]
352
+ return perms2