schubmult 2.0.4__py3-none-any.whl → 3.0.1__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 (58) hide show
  1. schubmult/__init__.py +96 -1
  2. schubmult/perm_lib.py +254 -819
  3. schubmult/poly_lib/__init__.py +31 -0
  4. schubmult/poly_lib/poly_lib.py +276 -0
  5. schubmult/poly_lib/schub_poly.py +148 -0
  6. schubmult/poly_lib/variables.py +204 -0
  7. schubmult/rings/__init__.py +18 -0
  8. schubmult/rings/_quantum_schubert_polynomial_ring.py +752 -0
  9. schubmult/rings/_schubert_polynomial_ring.py +1031 -0
  10. schubmult/rings/_tensor_schub_ring.py +128 -0
  11. schubmult/rings/_utils.py +55 -0
  12. schubmult/{sage_integration → sage}/__init__.py +4 -1
  13. schubmult/{sage_integration → sage}/_fast_double_schubert_polynomial_ring.py +67 -109
  14. schubmult/{sage_integration → sage}/_fast_schubert_polynomial_ring.py +33 -28
  15. schubmult/{sage_integration → sage}/_indexing.py +9 -5
  16. schubmult/schub_lib/__init__.py +51 -0
  17. schubmult/{schubmult_double/_funcs.py → schub_lib/double.py} +532 -596
  18. schubmult/{schubmult_q/_funcs.py → schub_lib/quantum.py} +54 -53
  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} +45 -35
  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} +29 -5
  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.1.dist-info/METADATA +1234 -0
  37. schubmult-3.0.1.dist-info/RECORD +41 -0
  38. {schubmult-2.0.4.dist-info → schubmult-3.0.1.dist-info}/WHEEL +1 -1
  39. schubmult-3.0.1.dist-info/entry_points.txt +5 -0
  40. schubmult/_tests.py +0 -24
  41. schubmult/schubmult_double/__init__.py +0 -12
  42. schubmult/schubmult_double/__main__.py +0 -6
  43. schubmult/schubmult_double/_script.py +0 -474
  44. schubmult/schubmult_py/__init__.py +0 -12
  45. schubmult/schubmult_py/__main__.py +0 -6
  46. schubmult/schubmult_py/_script.py +0 -97
  47. schubmult/schubmult_q/__init__.py +0 -8
  48. schubmult/schubmult_q/__main__.py +0 -6
  49. schubmult/schubmult_q/_script.py +0 -166
  50. schubmult/schubmult_q_double/__init__.py +0 -10
  51. schubmult/schubmult_q_double/__main__.py +0 -6
  52. schubmult/schubmult_q_double/_funcs.py +0 -540
  53. schubmult/schubmult_q_double/_script.py +0 -396
  54. schubmult-2.0.4.dist-info/METADATA +0 -542
  55. schubmult-2.0.4.dist-info/RECORD +0 -30
  56. schubmult-2.0.4.dist-info/entry_points.txt +0 -5
  57. {schubmult-2.0.4.dist-info → schubmult-3.0.1.dist-info}/licenses/LICENSE +0 -0
  58. {schubmult-2.0.4.dist-info → schubmult-3.0.1.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,659 @@
1
+ from functools import cache
2
+ from itertools import chain
3
+
4
+ import numpy as np
5
+ from symengine import sympify
6
+
7
+ import schubmult.poly_lib.variables as spl
8
+ from schubmult.perm_lib import (
9
+ Permutation,
10
+ code,
11
+ ensure_perms,
12
+ inv,
13
+ inverse,
14
+ one_dominates,
15
+ permtrim,
16
+ theta,
17
+ uncode,
18
+ )
19
+ from schubmult.utils.perm_utils import (
20
+ count_bruhat,
21
+ get_cycles,
22
+ getpermval,
23
+ has_bruhat_ascent,
24
+ has_bruhat_descent,
25
+ mu_A,
26
+ omega,
27
+ p_trans,
28
+ sg,
29
+ )
30
+
31
+ q_var = spl.GeneratingSet("q")
32
+
33
+
34
+ # def double_elem_sym_q(u, p1, p2, k, q_var=q_var):
35
+ # ret_list = {}
36
+ # perms1 = elem_sym_perms_q(u, p1, k, q_var)
37
+ # iu = inverse(u)
38
+ # for perm1, udiff1, mul_val1 in perms1:
39
+ # perms2 = elem_sym_perms_q(perm1, p2, k, q_var)
40
+ # cycles1 = get_cycles(tuple(permtrim(mulperm(iu, [*perm1]))))
41
+ # cycles1_dict = {}
42
+ # for c in cycles1:
43
+ # if c[-1] not in cycles1_dict:
44
+ # cycles1_dict[c[-1]] = []
45
+ # cycles1_dict[c[-1]] += [set(c)]
46
+ # ip1 = inverse(perm1)
47
+ # for perm2, udiff2, mul_val2 in perms2:
48
+ # cycles2 = get_cycles(tuple(permtrim(mulperm(ip1, [*perm2]))))
49
+ # good = True
50
+ # for i in range(len(cycles2)):
51
+ # c2 = cycles2[i]
52
+ # if c2[-1] not in cycles1_dict:
53
+ # continue
54
+ # for c1_s in cycles1_dict[c2[-1]]:
55
+ # for a in range(len(c2) - 2, -1, -1):
56
+ # if c2[a] in c1_s:
57
+ # good = False
58
+ # break
59
+ # if not good:
60
+ # break
61
+ # if not good:
62
+ # break
63
+
64
+ # if good:
65
+ # # print(f"{(perm1, udiff1, mul_val1)=}")
66
+ # if (perm1, udiff1, mul_val1) not in ret_list:
67
+ # ret_list[(perm1, udiff1, mul_val1)] = []
68
+ # ret_list[(perm1, udiff1, mul_val1)] += [(perm2, udiff2, mul_val2)]
69
+ # return ret_list
70
+
71
+
72
+ def double_elem_sym_q(u, p1, p2, k, q_var=q_var):
73
+ ret_list = {}
74
+ perms1 = elem_sym_perms_q(u, p1, k, q_var)
75
+ iu = ~u
76
+ for perm1, udiff1, mul_val1 in perms1:
77
+ perms2 = elem_sym_perms_q(perm1, p2, k, q_var)
78
+ cycles1 = get_cycles(iu*perm1)
79
+ cycles1_dict = {}
80
+ for c in cycles1:
81
+ if c[-1] not in cycles1_dict:
82
+ cycles1_dict[c[-1]] = []
83
+ cycles1_dict[c[-1]] += [set(c)]
84
+ ip1 = inverse(perm1)
85
+ for perm2, udiff2, mul_val2 in perms2:
86
+ cycles2 = get_cycles(ip1*perm2)
87
+ good = True
88
+ for i in range(len(cycles2)):
89
+ c2 = cycles2[i]
90
+ if c2[-1] not in cycles1_dict:
91
+ continue
92
+ for c1_s in cycles1_dict[c2[-1]]:
93
+ for a in range(len(c2) - 2, -1, -1):
94
+ if c2[a] in c1_s:
95
+ good = False
96
+ break
97
+ if not good:
98
+ break
99
+ if not good:
100
+ break
101
+
102
+ if good:
103
+ if (perm1, udiff1, mul_val1) not in ret_list:
104
+ ret_list[(perm1, udiff1, mul_val1)] = []
105
+ ret_list[(perm1, udiff1, mul_val1)] += [(perm2, udiff2, mul_val2)]
106
+ return ret_list
107
+
108
+
109
+ def will_formula_work(u, v):
110
+ u, v = Permutation(u), Permutation(v)
111
+ muv = uncode(theta(v))
112
+ vn1muv = (~v) * muv
113
+ while inv(vn1muv)>0:
114
+ found_one = False
115
+ for i in range(len(vn1muv) - 1):
116
+ if vn1muv[i] > vn1muv[i + 1]:
117
+ found_one = True
118
+ if i < len(u) - 1 and u[i] > u[i + 1]:
119
+ return False
120
+ # vn1muv[i], vn1muv[i + 1] = vn1muv[i + 1], vn1muv[i]
121
+ vn1muv = vn1muv.swap(i, i + 1)
122
+ break
123
+ if found_one:
124
+ return False
125
+ return True
126
+
127
+
128
+
129
+ def try_reduce_u(u, v, w):
130
+ if one_dominates(u, w):
131
+ return u, v, w
132
+ u2 = u
133
+ v2 = v
134
+ w2 = w
135
+ cu = code(u)
136
+ for i in range(len(u2) - 2, -1, -1):
137
+ if cu[i] == 0 and i < len(cu) - 1 and cu[i + 1] != 0:
138
+ if i >= len(v2) - 1 or v2[i] < v2[i + 1]:
139
+ # u2[i], u2[i + 1] = u2[i + 1], u2[i]
140
+ # if i > len(w2) - 1:
141
+ # w2 += list(range(len(w2) + 1, i + 3))
142
+ # w2[i + 1], w2[i] = w2[i], w2[i + 1]
143
+ u2 = u2.swap(i, i + 1)
144
+ w2 = w2.swap(i, i + 1)
145
+ if one_dominates(u2, w):
146
+ # return Permutation(u2), Permutation(v2), Permutation(w2)
147
+ return u2, v2, w2
148
+ return try_reduce_u(u2, v2, w2)
149
+ if i < len(w2) - 1 and w2[i] > w2[i + 1]:
150
+ # ERROR?
151
+ # u2[i], u2[i + 1] = u2[i + 1], u2[i]
152
+ # v2[i], v2[i + 1] = v2[i + 1], v2[i]
153
+ u2 = u2.swap(i, i + 1)
154
+ v2 = v2.swap(i, i + 1)
155
+ return try_reduce_u(u2, v2, w2)
156
+ # return Permutation(u2), Permutation(v2), Permutation(w2)
157
+ return u2, v2, w2
158
+ # return Permutation(u2), Permutation(v2), Permutation(w2)
159
+ return u2, v2, w2
160
+
161
+
162
+ def reduce_descents(u, v, w):
163
+ found_one = True
164
+ u2 = Permutation(u)
165
+ v2 = Permutation(v)
166
+ w2 = Permutation(w)
167
+ while found_one:
168
+ found_one = False
169
+ if will_formula_work(u2, v2) or will_formula_work(v2, u2) or one_dominates(u2, w2) or is_reducible(v2) or inv(w2) - inv(u2) == 1:
170
+ break
171
+ for i in range(len(w2) - 2, -1, -1):
172
+ if w2[i] > w2[i + 1] and i < len(v2) - 1 and v2[i] > v2[i + 1] and (i >= len(u2) - 1 or u2[i] < u2[i + 1]):
173
+ w2 = w2.swap(i, i + 1)
174
+ v2 = v2.swap(i, i + 1)
175
+ found_one = True
176
+ elif w2[i] > w2[i + 1] and i < len(u2) - 1 and u2[i] > u2[i + 1] and (i >= len(v2) - 1 or v2[i] < v2[i + 1]):
177
+ w2 = w2.swap(i, i + 1)
178
+ u2 = u2.swap(i, i + 1)
179
+ found_one = True
180
+ if found_one:
181
+ break
182
+ # return Permutation(u2), Permutation(v2), Permutation(w2)
183
+ return u2, v2, w2
184
+
185
+
186
+ def is_reducible(v):
187
+ c03 = code(v)
188
+ found0 = False
189
+ good = True
190
+ for i in range(len(c03)):
191
+ if c03[i] == 0:
192
+ found0 = True
193
+ elif c03[i] != 0 and found0:
194
+ good = False
195
+ break
196
+ return good
197
+
198
+
199
+ # @ensure_perms
200
+ def try_reduce_v(u, v, w):
201
+ if is_reducible(v):
202
+ return u, v, w
203
+ u2 = u
204
+ v2 = v
205
+ w2 = w
206
+ cv = code(v2)
207
+ for i in range(len(v2) - 2, -1, -1):
208
+ if cv[i] == 0 and i < len(cv) - 1 and cv[i + 1] != 0:
209
+ if i >= len(u2) - 1 or u2[i] < u2[i + 1]:
210
+ # v2[i], v2[i + 1] = v2[i + 1], v2[i]
211
+ v2 = v2.swap(i, i + 1)
212
+ # if i >= len(w2) - 1:
213
+ # w2 += list(range(len(w2) + 1, i + 3))
214
+ # w2[i + 1], w2[i] = w2[i], w2[i + 1]
215
+ w2 = w2.swap(i, i + 1)
216
+ if is_reducible(v2):
217
+ return Permutation(u2), Permutation(v2), Permutation(w2)
218
+ return try_reduce_v(u2, v2, w2)
219
+ if i < len(w2) - 1 and w2[i] > w2[i + 1]:
220
+ # u2[i], u2[i + 1] = u2[i + 1], u2[i]
221
+ # v2[i], v2[i + 1] = v2[i + 1], v2[i]
222
+ u2 = u2.swap(i, i + 1)
223
+ v2 = v2.swap(i, i + 1)
224
+ return try_reduce_v(u2, v2, w2)
225
+ # return Permutation(u2), Permutation(v2), Permutation(w2)
226
+ return u2, v2, w2
227
+ return u2, v2, w2
228
+
229
+
230
+ def reduce_coeff(u, v, w):
231
+ t_mu_u_t = theta(inverse(u))
232
+ t_mu_v_t = theta(inverse(v))
233
+
234
+ mu_u_inv = uncode(t_mu_u_t)
235
+ mu_v_inv = uncode(t_mu_v_t)
236
+
237
+ t_mu_u = p_trans(t_mu_u_t)
238
+ t_mu_v = p_trans(t_mu_v_t)
239
+
240
+ t_mu_u += [0 for i in range(len(t_mu_u), max(len(t_mu_u), len(t_mu_v)))]
241
+ t_mu_v += [0 for i in range(len(t_mu_v), max(len(t_mu_u), len(t_mu_v)))]
242
+
243
+ t_mu_uv = [t_mu_u[i] + t_mu_v[i] for i in range(len(t_mu_u))]
244
+ t_mu_uv_t = p_trans(t_mu_uv)
245
+
246
+ mu_uv_inv = uncode(t_mu_uv_t)
247
+
248
+ if inv(w * mu_uv_inv) != inv(mu_uv_inv) - inv(w):
249
+ return u, v, w
250
+
251
+ # umu = mulperm(list(u), mu_u_inv)
252
+ # vmu = mulperm(list(v), mu_v_inv)
253
+ # wmu = mulperm(list(w), mu_uv_inv)
254
+ umu = u * mu_u_inv
255
+ vmu = v * mu_v_inv
256
+ wmu = w * mu_uv_inv
257
+
258
+ t_mu_w = theta(inverse(wmu))
259
+
260
+ mu_w = uncode(t_mu_w)
261
+
262
+ w_prime = wmu * mu_w
263
+
264
+ if w == w_prime:
265
+ return u, v, w
266
+ # if permtrim(list(w)) == permtrim(w_prime):
267
+ # return (permtrim(list(u)), permtrim(list(v)), permtrim(list(w)))
268
+
269
+ A = []
270
+ B = []
271
+ indexA = 0
272
+
273
+ while len(t_mu_u_t) > 0 and t_mu_u_t[-1] == 0:
274
+ t_mu_u_t.pop()
275
+
276
+ while len(t_mu_v_t) > 0 and t_mu_v_t[-1] == 0:
277
+ t_mu_v_t.pop()
278
+
279
+ while len(t_mu_uv_t) > 0 and t_mu_uv_t[-1] == 0:
280
+ t_mu_uv_t.pop()
281
+
282
+ for index in range(len(t_mu_uv_t)):
283
+ if indexA < len(t_mu_u_t) and t_mu_uv_t[index] == t_mu_u_t[indexA]:
284
+ A += [index]
285
+ indexA += 1
286
+ else:
287
+ B += [index]
288
+
289
+ mu_w_A = uncode(mu_A(code(mu_w), A))
290
+ mu_w_B = uncode(mu_A(code(mu_w), B))
291
+
292
+ return (umu * mu_w_A, vmu * mu_w_B, w_prime)
293
+
294
+
295
+ def pull_out_var(vnum, v):
296
+ v = Permutation(v)
297
+ vup = v
298
+ if vnum >= len(v):
299
+ return [[[], v]]
300
+ vpm_list = [(vup, 0)]
301
+ ret_list = []
302
+ for p in range(len(v) + 1 - vnum):
303
+ vpm_list2 = []
304
+ for vpm, b in vpm_list:
305
+ if vpm[vnum - 1] == len(v) + 1:
306
+ vpm2 = [*vpm]
307
+ vpm2.pop(vnum - 1)
308
+ vp = permtrim(vpm2)
309
+ ret_list += [
310
+ [
311
+ [v[i] for i in range(vnum, len(v)) if ((i > len(vp) and v[i] == i) or (i <= len(vp) and v[i] == vp[i - 1]))],
312
+ vp,
313
+ ],
314
+ ]
315
+ for j in range(vnum, len(vup) + 2):
316
+ if vpm[j] <= b:
317
+ continue
318
+ for i in range(vnum):
319
+ if has_bruhat_ascent(vpm, i, j):
320
+ vpm_list2 += [(vpm.swap(i, j), vpm[j])]
321
+ vpm_list = vpm_list2
322
+ for vpm, b in vpm_list:
323
+ if vpm[vnum - 1] == len(v) + 1:
324
+ vpm2 = [*vpm]
325
+ vpm2.pop(vnum - 1)
326
+ vp = permtrim(vpm2)
327
+ ret_list += [
328
+ [
329
+ [v[i] for i in range(vnum, len(v)) if ((i > len(vp) and v[i] == i) or (i <= len(vp) and v[i] == vp[i - 1]))],
330
+ vp,
331
+ ],
332
+ ]
333
+ return ret_list
334
+
335
+
336
+ @ensure_perms
337
+ def divdiffable(v, u):
338
+ inv_v = inv(v)
339
+ inv_u = inv(u)
340
+ perm2 = v * (~u)
341
+ if inv(perm2) != inv_v - inv_u:
342
+ return []
343
+ return perm2
344
+
345
+
346
+ # perms and inversion diff
347
+ def kdown_perms(perm, monoperm, p, k):
348
+ perm = Permutation(perm)
349
+ monoperm = Permutation(monoperm)
350
+ inv_m = inv(monoperm)
351
+ inv_p = inv(perm)
352
+ full_perm_list = []
353
+ # perm = Permutation(perm)
354
+ # print(f"{perm=} {monoperm=} {inv_m=} {inv_p=} {perm*monoperm=}")
355
+ if inv(perm * monoperm) == inv_m - inv_p:
356
+ full_perm_list += [(perm, 0, 1)]
357
+
358
+ down_perm_list = [(perm, 1)]
359
+ if len(perm) < k:
360
+ return full_perm_list
361
+ a2 = k - 1
362
+ for pp in range(1, p + 1):
363
+ down_perm_list2 = []
364
+ for perm2, s in down_perm_list:
365
+ L = len(perm2)
366
+ # print(f"{perm2=} {L=}")
367
+ if k > L:
368
+ continue
369
+ s2 = -s
370
+ for b in chain(range(k - 1), range(k, L)):
371
+ if perm2[b] != perm[b]:
372
+ continue
373
+ if b < a2:
374
+ i, j = b, a2
375
+ else:
376
+ i, j, s2 = a2, b, s
377
+ # print(f"{perm2=} {i=} {j=}")
378
+ if has_bruhat_descent(perm2, i, j):
379
+ # print(f"YEAH BABY {perm2=} {i=} {j=}")
380
+ new_perm = perm2.swap(i, j)
381
+ # print(f"{new_perm=}")
382
+ down_perm_list2 += [(new_perm, s2)]
383
+ if inv(new_perm * monoperm) == inv_m - inv_p + pp:
384
+ full_perm_list += [(new_perm, pp, s2)]
385
+ # else:
386
+ # # print(f"NO BABY {perm2=} {i=} {j=}")
387
+ down_perm_list = down_perm_list2
388
+ return full_perm_list
389
+
390
+ @cache
391
+ def compute_vpathdicts_cached(th, vmu, smpify):
392
+ vpathdicts = [{} for index in range(len(th))]
393
+ vpathdicts[-1][vmu] = None
394
+ thL = len(th)
395
+
396
+ top = code(~Permutation(uncode(th)))
397
+ for i in range(thL - 1, -1, -1):
398
+ top2 = code(~Permutation(uncode(top)))
399
+ while top2[-1] == 0:
400
+ top2.pop()
401
+ top2.pop()
402
+ top = code(~Permutation(uncode(top2)))
403
+ # print(f"{top=}")
404
+ monoperm = Permutation(uncode(top))
405
+ # print(f"{monoperm=}")
406
+ k = i + 1
407
+ for last_perm in vpathdicts[i]:
408
+ newperms = kdown_perms(last_perm, monoperm, th[i], k)
409
+ vpathdicts[i][last_perm] = newperms
410
+ if i > 0:
411
+ for trip in newperms:
412
+ vpathdicts[i - 1][trip[0]] = None
413
+ vpathdicts2 = [{} for i in range(len(th))]
414
+ for i in range(len(th)):
415
+ for key, valueset in vpathdicts[i].items():
416
+ for value in valueset:
417
+ key2 = value[0]
418
+ if key2 not in vpathdicts2[i]:
419
+ vpathdicts2[i][key2] = set()
420
+ v2 = value[2]
421
+ if smpify:
422
+ v2 = sympify(v2)
423
+ vpathdicts2[i][key2].add((key, value[1], v2))
424
+ return vpathdicts2
425
+
426
+ def compute_vpathdicts(th, vmu, smpify=False):
427
+ return compute_vpathdicts_cached(tuple(th), vmu, smpify)
428
+
429
+
430
+ def check_blocks(qv, parabolic_index):
431
+ blocks = []
432
+ cur_block = []
433
+ last_val = -1
434
+ for i in range(len(parabolic_index)):
435
+ if last_val + 1 != parabolic_index[i] and i != 0:
436
+ blocks += [cur_block]
437
+ cur_block = []
438
+ last_val = parabolic_index[i]
439
+ cur_block += [last_val]
440
+ if len(cur_block) != 0:
441
+ blocks += [cur_block]
442
+ # print(f"{parabolic_index=}")
443
+ # print(f"{blocks=}")
444
+ for block in blocks:
445
+ for i in range(len(block)):
446
+ for j in range(i, len(block)):
447
+ val = 0
448
+ for k in range(i, j + 1):
449
+ val += omega(block[k], qv)
450
+ if val != 0 and val != -1:
451
+ return False
452
+ return True
453
+
454
+
455
+ def reduce_q_coeff(u, v, w, qv):
456
+ for i in range(len(qv)):
457
+ if sg(i, v) == 1 and sg(i, u) == 0 and sg(i, w) + omega(i + 1, qv) == 1:
458
+ ret_v = v.swap(i, i + 1)
459
+ ret_w = w.swap(i, i + 1)
460
+ qv_ret = [*qv]
461
+ if sg(i, w) == 0:
462
+ qv_ret[i] -= 1
463
+ return u, ret_v, ret_w, qv_ret, True
464
+ if (sg(i, u) == 1 and sg(i, v) == 0 and sg(i, w) + omega(i + 1, qv) == 1) or (sg(i, u) == 1 and sg(i, v) == 1 and sg(i, w) + omega(i + 1, qv) == 2):
465
+ ret_u = u.swap(i, i + 1)
466
+ ret_w = w.swap(i, i + 1)
467
+ qv_ret = [*qv]
468
+ if sg(i, w) == 0:
469
+ qv_ret[i] -= 1
470
+ return ret_u, v, ret_w, qv_ret, True
471
+ return u, v, w, qv, False
472
+
473
+
474
+ def reduce_q_coeff_u_only(u, v, w, qv):
475
+ for i in range(len(qv)):
476
+ if (sg(i, u) == 1 and sg(i, v) == 0 and sg(i, w) + omega(i + 1, qv) == 1) or (sg(i, u) == 1 and sg(i, v) == 1 and sg(i, w) + omega(i + 1, qv) == 2):
477
+ # ret_u = [*u]
478
+ # ret_u[i], ret_u[i + 1] = ret_u[i + 1], ret_u[i]
479
+ ret_u = u.swap(i, i + 1)
480
+ # ret_w = [*w] + [j + 1 for j in range(len(w), i + 2)]
481
+ # ret_w[i], ret_w[i + 1] = ret_w[i + 1], ret_w[i]
482
+ ret_w = w.swap(i, i + 1)
483
+ qv_ret = [*qv]
484
+ if sg(i, w) == 0:
485
+ qv_ret[i] -= 1
486
+ return ret_u, v, ret_w, qv_ret, True
487
+ return u, v, w, qv, False
488
+
489
+
490
+ # def elem_sym_perms_q(orig_perm, p, k, q_var=q_var):
491
+ # total_list = [(orig_perm, 0, 1)]
492
+ # up_perm_list = [(orig_perm, 1, 1000)]
493
+ # for pp in range(p):
494
+ # perm_list = []
495
+ # for up_perm, val, last_j in up_perm_list:
496
+ # up_perm2 = [*up_perm, len(up_perm) + 1]
497
+ # if len(up_perm2) < k + 1:
498
+ # up_perm2 += [i + 1 for i in range(len(up_perm2), k + 2)]
499
+ # pos_list = [i for i in range(k) if (i >= len(orig_perm) and up_perm2[i] == i + 1) or (i < len(orig_perm) and up_perm2[i] == orig_perm[i])]
500
+ # for j in range(min(len(up_perm2) - 1, last_j), k - 1, -1):
501
+ # for i in pos_list:
502
+ # ct = count_bruhat(up_perm2, i, j)
503
+ # # print(f"{up_perm2=} {ct=} {i=} {j=} {k=} {pp=}")
504
+ # if ct == 1 or ct == 2 * (i - j) + 1:
505
+ # new_perm = [*up_perm2]
506
+ # new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
507
+ # new_perm_add = tuple(permtrim(new_perm))
508
+ # new_val = val
509
+ # if ct < 0:
510
+ # new_val *= np.prod([q_var[index] for index in range(i + 1, j + 1)])
511
+ # perm_list += [(new_perm_add, new_val, j)]
512
+ # total_list += [(new_perm_add, pp + 1, new_val)]
513
+ # up_perm_list = perm_list
514
+ # return total_list
515
+
516
+
517
+ def elem_sym_perms_q(orig_perm, p, k, q_var=q_var):
518
+ total_list = [(orig_perm, 0, 1)]
519
+ up_perm_list = [(orig_perm, 1, 1000)]
520
+ for pp in range(p):
521
+ perm_list = []
522
+ for up_perm, val, last_j in up_perm_list:
523
+ pos_list = [i for i in range(k) if up_perm[i] == orig_perm[i]]
524
+ # print(f"{pos_list=} {up_perm=} {orig_perm=} {k=}", file=sys.stderr)
525
+ for j in range(min(max(k + 1, len(up_perm)), last_j), k - 1, -1):
526
+ for i in pos_list:
527
+ ct = count_bruhat(up_perm, i, j)
528
+ if ct == 1 or ct == 2 * (i - j) + 1:
529
+ new_perm_add = up_perm.swap(i, j)
530
+ # print(f"{new_perm_add=}")
531
+ new_val = val
532
+ if ct < 0:
533
+ new_val *= np.prod([q_var[index] for index in range(i + 1, j + 1)])
534
+ perm_list += [(new_perm_add, new_val, j)]
535
+ total_list += [(new_perm_add, pp + 1, new_val)]
536
+ up_perm_list = perm_list
537
+ return total_list
538
+
539
+
540
+ @ensure_perms
541
+ def elem_sym_perms_q_op(orig_perm, p, k, n, q_var=q_var):
542
+ total_list = [(orig_perm, 0, 1)]
543
+ up_perm_list = [(orig_perm, 1, k)]
544
+ for pp in range(p):
545
+ perm_list = []
546
+ for up_perm, val, last_j in up_perm_list:
547
+ up_perm2 = [*up_perm]
548
+ if len(up_perm) < n:
549
+ up_perm2 += [i + 1 for i in range(len(up_perm2), n)]
550
+ pos_list = [i for i in range(k) if (i >= len(orig_perm) and up_perm2[i] == i + 1) or (i < len(orig_perm) and up_perm2[i] == orig_perm[i])]
551
+ for j in range(last_j, n):
552
+ for i in pos_list:
553
+ ct = count_bruhat(up_perm2, i, j)
554
+ # print(f"{up_perm2=} {ct=} {i=} {j=} {k=} {pp=}")
555
+ if ct == -1 or ct == 2 * (j - i) - 1:
556
+ new_perm = [*up_perm2]
557
+ new_perm[i], new_perm[j] = new_perm[j], new_perm[i]
558
+ new_perm_add = Permutation(new_perm)
559
+ new_val = val
560
+ if ct > 0:
561
+ new_val *= np.prod([q_var[index] for index in range(i + 1, j + 1)])
562
+ perm_list += [(new_perm_add, new_val, j)]
563
+ total_list += [(new_perm_add, pp + 1, new_val)]
564
+ up_perm_list = perm_list
565
+ return total_list
566
+
567
+
568
+ def elem_sym_perms(orig_perm, p, k):
569
+ orig_perm = Permutation(orig_perm)
570
+ total_list = [(orig_perm, 0)]
571
+ up_perm_list = [(orig_perm, 1000000000)]
572
+ for pp in range(p):
573
+ perm_list = []
574
+ for up_perm, last in up_perm_list:
575
+ pos_list = [i for i in range(k) if up_perm[i] < last]
576
+ for j in range(k, max(k + 2, len(up_perm) + 1)):
577
+ if up_perm[j] >= last:
578
+ continue
579
+ for i in pos_list:
580
+ if has_bruhat_ascent(up_perm, i, j):
581
+ new_perm_add = up_perm.swap(i, j)
582
+ perm_list += [(new_perm_add, up_perm[j])]
583
+ total_list += [(new_perm_add, pp + 1)]
584
+ up_perm_list = perm_list
585
+ return total_list
586
+
587
+
588
+ def elem_sym_perms_op(orig_perm, p, k):
589
+ total_list = [(orig_perm, 0)]
590
+ up_perm_list = [(orig_perm, k)]
591
+ for pp in range(p):
592
+ perm_list = []
593
+ for up_perm, last in up_perm_list:
594
+ up_perm2 = [*up_perm]
595
+ if len(up_perm2) < k + 1:
596
+ up_perm2 += [i + 1 for i in range(len(up_perm2), k + 2)]
597
+ pos_list = [i for i in range(k) if getpermval(up_perm2, i) == getpermval(orig_perm, i)]
598
+ for j in range(last, len(up_perm2)):
599
+ for i in pos_list:
600
+ if has_bruhat_descent(up_perm2, i, j):
601
+ new_perm_add = up_perm.swap(i, j)
602
+ perm_list += [(new_perm_add, j)]
603
+ total_list += [(new_perm_add, pp + 1)]
604
+ up_perm_list = perm_list
605
+ return total_list
606
+
607
+
608
+ def is_split_two(u, v, w):
609
+ if inv(w) - inv(u) != 2:
610
+ return False, []
611
+ diff_perm = (~v) * w
612
+ identity = [i + 1 for i in range(len(diff_perm))]
613
+ cycles = []
614
+ for i in range(len(identity)):
615
+ if diff_perm[i] != identity[i]:
616
+ cycle0 = set()
617
+ cycle = {i + 1}
618
+ last = i
619
+ while len(cycle0) != len(cycle):
620
+ cycle0 = cycle
621
+ last = diff_perm[last] - 1
622
+ cycle.add(last + 1)
623
+ if len(cycle) > 1 and cycle not in cycles:
624
+ cycles += [cycle]
625
+ if len(cycles) > 2:
626
+ break
627
+ if len(cycles) == 2:
628
+ return True, cycles
629
+ return False, []
630
+
631
+
632
+ def is_coeff_irreducible(u, v, w):
633
+ return (
634
+ not will_formula_work(u, v)
635
+ and not will_formula_work(v, u)
636
+ and not one_dominates(u, w)
637
+ and not is_reducible(v)
638
+ and inv(w) - inv(u) > 1
639
+ and not is_split_two(u, v, w)[0]
640
+ and len([i for i in code(v) if i != 0]) > 1
641
+ )
642
+
643
+
644
+ def is_hook(cd):
645
+ started = False
646
+ done = False
647
+ found_zero_after = False
648
+ for i in range(len(cd)):
649
+ if (done or found_zero_after) and cd[i] != 0:
650
+ return False
651
+ if cd[i] == 1 and not started:
652
+ started = True
653
+ if cd[i] > 1:
654
+ done = True
655
+ if started and cd[i] == 0:
656
+ found_zero_after = True
657
+ if started or done:
658
+ return True
659
+ return False