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,752 @@
1
+ # to encourage development
2
+
3
+ from bisect import bisect_left
4
+ from functools import cache
5
+
6
+ import sympy
7
+ from symengine import S, Symbol, sympify
8
+ from sympy import Basic
9
+
10
+ import schubmult.rings._schubert_polynomial_ring as spr
11
+ import schubmult.rings._utils as utils
12
+ import schubmult.schub_lib.quantum as py
13
+ import schubmult.schub_lib.quantum_double as yz
14
+ from schubmult.perm_lib import Permutation, longest_element
15
+ from schubmult.poly_lib.poly_lib import complete_sym_poly, elem_sym_poly, elem_sym_poly_q, xreplace_genvars
16
+ from schubmult.poly_lib.schub_poly import schubpoly_from_elems
17
+ from schubmult.poly_lib.variables import GeneratingSet, GeneratingSet_base
18
+ from schubmult.utils.logging import get_logger
19
+ from schubmult.utils.perm_utils import is_parabolic
20
+
21
+ ## EMULATE POLYTOOLS
22
+
23
+ q_var = GeneratingSet("q")
24
+ # _def_printer = StrPrinter({"order": "none"})
25
+
26
+ logger = get_logger(__name__)
27
+
28
+
29
+ class QuantumDoubleSchubertAlgebraElement(spr.BasisSchubertAlgebraElement):
30
+ def __new__(cls, _dict, basis):
31
+ return spr.BasisSchubertAlgebraElement.__new__(cls, _dict, basis)
32
+
33
+ def subs(self, old, new):
34
+ return self.as_classical().subs(old, new).as_quantum()
35
+
36
+
37
+ # # TODO: not a noncommutative symbol, something else
38
+ # # Atomic Schubert polynomial
39
+ _pretty_schub_char = "𝕼𝔖" # noqa: RUF001
40
+
41
+
42
+ class QDSchubPoly(QuantumDoubleSchubertAlgebraElement):
43
+ is_Atom = True
44
+
45
+ def __new__(cls, k, basis):
46
+ return QDSchubPoly.__xnew_cached__(cls, k, basis)
47
+
48
+ @staticmethod
49
+ def __xnew__(_class, k, basis):
50
+ obj = QuantumDoubleSchubertAlgebraElement.__new__(_class, sympy.Dict({(Permutation(k[0]), k[1]): 1}), basis)
51
+ obj._perm = k[0]
52
+ obj._key = k
53
+ obj._coeff_var = k[1]
54
+ # obj._base_var = base_var
55
+ return obj
56
+
57
+ @staticmethod
58
+ @cache
59
+ def __xnew_cached__(_class, k, genset):
60
+ return QDSchubPoly.__xnew__(_class, k, genset)
61
+
62
+ def _sympystr(self, printer):
63
+ if self._coeff_var == 0 or self._coeff_var == utils.NoneVar:
64
+ return printer.doprint(f"QS{self.genset.label}({printer.doprint(self._perm)})")
65
+ return printer.doprint(f"QDS{self.genset.label}({printer.doprint(self._perm)}, {spr._varstr(self._coeff_var)})")
66
+
67
+ def _pretty(self, printer):
68
+ if self._key[0] == Permutation([]):
69
+ return printer._print(1)
70
+ subscript = printer.doprint(int("".join([str(i) for i in self._key[0]])))
71
+ if self._key[1] == 0 or self._key[1] == utils.NoneVar:
72
+ return printer._print_Function(sympy.Function(f"{_pretty_schub_char}_{subscript}")(sympy.Symbol(self.genset.label)))
73
+ return printer._print_Function(sympy.Function(f"{_pretty_schub_char}_{subscript}")(sympy.Symbol(f"{self.genset.label}; {self._key[1]}")))
74
+
75
+ def _latex(self, printer):
76
+ if self._key[0] == Permutation([]):
77
+ return printer._print(1)
78
+ # subscript = printer._print(int("".join([str(i) for i in self._key[0]])))
79
+ subscript = sympy.sstr(self._key[0])
80
+ if self._key[1] == 0 or self._key[1] == utils.NoneVar:
81
+ return printer._print_Function(sympy.Function("\\widetilde{\\mathfrak{S}}" + f"_{'{' + subscript + '}'}")(sympy.Symbol(self.genset.label)))
82
+ return printer._print_Function(sympy.Function("\\widetilde{\\mathfrak{S}}" + f"_{'{' + subscript + '}'}")(sympy.Symbol(f"{self.genset.label}; {self._key[1]}")))
83
+
84
+
85
+ class ParabolicQuantumDoubleSchubertAlgebraElement(spr.BasisSchubertAlgebraElement):
86
+ def __new__(cls, _dict, basis):
87
+ return spr.BasisSchubertAlgebraElement.__new__(cls, _dict, basis)
88
+
89
+ @property
90
+ def index_comp(self):
91
+ return self.basis.index_comp
92
+
93
+ def kill_ideal(self):
94
+ length = sum(self.index_comp)
95
+ new_dict = {}
96
+ for k, v in self.coeff_dict.items():
97
+ if len(k[0]) <= length:
98
+ new_dict[k] = v
99
+ return self.basis._from_dict(new_dict)
100
+
101
+
102
+ class PQDSchubPoly(ParabolicQuantumDoubleSchubertAlgebraElement):
103
+ is_Atom = True
104
+
105
+ def __new__(cls, k, basis):
106
+ return PQDSchubPoly.__xnew_cached__(cls, k, basis)
107
+
108
+ @staticmethod
109
+ def __xnew__(_class, k, basis):
110
+ obj = ParabolicQuantumDoubleSchubertAlgebraElement.__new__(_class, sympy.Dict({(Permutation(k[0]), k[1]): 1}), basis)
111
+ obj._perm = k[0]
112
+ obj._key = k
113
+ # obj._perm._print_as_code = True
114
+ obj._coeff_var = k[1]
115
+ # obj._base_var = base_var
116
+ return obj
117
+
118
+ @staticmethod
119
+ @cache
120
+ def __xnew_cached__(_class, k, genset):
121
+ return PQDSchubPoly.__xnew__(_class, k, genset)
122
+
123
+ def _sympystr(self, printer):
124
+ if self._coeff_var == 0 or self._coeff_var == utils.NoneVar:
125
+ return printer.doprint(f"QPS{self.genset.label}{(tuple(self.index_comp))}({printer.doprint(self._perm)})")
126
+ return printer.doprint(f"QPDS{self.genset.label}{tuple(self.index_comp)}({printer.doprint(self._perm)}, {spr._varstr(self._coeff_var)})")
127
+
128
+ def _pretty(self, printer):
129
+ if self._key[0] == Permutation([]):
130
+ return printer._print(1)
131
+ subscript = printer._print(int("".join([str(i) for i in self._key[0]])))
132
+ # subscript = sympy.sstr(self._key[0])
133
+ if self._key[1] == 0 or self._key[1] == utils.NoneVar:
134
+ return printer._print_Function(sympy.Function(f"{_pretty_schub_char}_{subscript}")(sympy.Symbol(f"{self.genset.label} | {self.basis.index_comp}")))
135
+ return printer._print_Function(sympy.Function(f"{_pretty_schub_char}_{subscript}")(sympy.Symbol(f"{self.genset.label}; {self._key[1]} | {self.basis.index_comp}")))
136
+
137
+ def _latex(self, printer):
138
+ if self._key[0] == Permutation([]):
139
+ return printer._print(1)
140
+ # subscript = printer._print(int("".join([str(i) for i in self._key[0]])))
141
+ subscript = printer._print(self._key[0])
142
+ supscript = printer._print(sympy.Tuple(*self.index_comp))
143
+ if self._key[1] == 0 or self._key[1] == utils.NoneVar:
144
+ return printer._print_Function(sympy.Function("\\widetilde{\\mathfrak{S}}" + f"^{'{'}{supscript}{'}'}_{'{' + subscript + '}'}")(sympy.Symbol(self.genset.label)))
145
+ return printer._print_Function(sympy.Function("\\widetilde{\\mathfrak{S}}" + f"^{'{'}{supscript}{'}'}_{'{' + subscript + '}'}")(sympy.Symbol(f"{self.genset.label}; {self._key[1]}")))
146
+
147
+
148
+ class QuantumDoubleSchubertAlgebraElement_basis(Basic):
149
+ def __new__(cls, genset):
150
+ return Basic.__new__(cls, genset)
151
+
152
+ @property
153
+ def symbol_elem_func(self):
154
+ def elem_func(p, k, varl1, varl2): # noqa: ARG001
155
+ if p == 0 and k >= 0:
156
+ return 1
157
+ if p < 0 or p > k:
158
+ return 0
159
+ return sympy.Add(*[(Symbol(f"e_{p - i}_{k}") if p - i > 0 else 1) * complete_sym_poly(i, k + 1 - p, [-v for v in varl2]) for i in range(p + 1)])
160
+
161
+ return elem_func
162
+
163
+ def elem_sym_subs(self, kk):
164
+ elems = []
165
+ for k in range(1, kk + 1):
166
+ for p in range(1, k + 1):
167
+ elems += [(sympy.Symbol(f"e_{p}_{k}"), elem_sym_poly_q(p, k, self.genset[1:], utils.poly_ring(0)))]
168
+ return dict(elems)
169
+
170
+ def _from_dict(self, _dict):
171
+ return QuantumDoubleSchubertAlgebraElement(_dict, self)
172
+
173
+ @property
174
+ def genset(self):
175
+ return self.args[0]
176
+
177
+ @cache
178
+ def cached_product(self, u, v, va, vb):
179
+ return {(k, va): xreplace_genvars(x, utils.poly_ring(va), utils.poly_ring(vb)) for k, x in yz.schubmult_q_double_pair_generic(u, v).items()}
180
+
181
+ def in_quantum_basis(self, elem):
182
+ return elem
183
+
184
+ def in_classical_basis(self, elem):
185
+ result = S.Zero
186
+ for k, v in elem.coeff_dict.items():
187
+ result += v * self.quantum_as_classical_schubpoly(k[0], k[1])
188
+ return result
189
+
190
+ def classical_elem_func(self, coeff_var):
191
+ basis = spr.DoubleSchubertAlgebraElement_basis(self.genset)
192
+ q_var = yz._vars.q_var
193
+
194
+ def elem_func(p, k, varl1, varl2):
195
+ if p == 0 and k >= 0:
196
+ return basis([], coeff_var)
197
+ if p < 0 or p > k:
198
+ return basis(0, coeff_var)
199
+ return (varl1[k - 1] - varl2[k - p]) * elem_func(p - 1, k - 1, varl1, varl2) + elem_func(p, k - 1, varl1, varl2) + q_var[k - 1] * elem_func(p - 2, k - 2, varl1, varl2)
200
+
201
+ return elem_func
202
+
203
+ @property
204
+ def single_element_class(self):
205
+ return QDSchubPoly
206
+
207
+ @cache
208
+ def quantum_as_classical_schubpoly(self, perm, coeff_var="y"):
209
+ return schubpoly_from_elems(perm, self.genset, utils.poly_ring(coeff_var), self.classical_elem_func(coeff_var))
210
+
211
+ @cache
212
+ def cached_schubpoly(self, k):
213
+ return schubpoly_from_elems(k[0], self.genset, utils.poly_ring(k[1]), elem_func=elem_sym_poly_q) # yz.schubpoly_quantum(k[0], self.genset, utils.poly_ring(k[1]))
214
+
215
+ @cache
216
+ def cached_positive_product(self, u, v, va, vb):
217
+ return {(k, va): xreplace_genvars(x, utils.poly_ring(va), utils.poly_ring(vb)) for k, x in yz.schubmult_q_generic_partial_posify(u, v).items()}
218
+
219
+ @property
220
+ def double_mul(self):
221
+ return yz.schubmult_q_double_fast
222
+
223
+ @property
224
+ def single_mul(self):
225
+ return py.schubmult_q_fast
226
+
227
+ @property
228
+ def mult_poly_single(self):
229
+ return py.mult_poly_q
230
+
231
+ @property
232
+ def mult_poly_double(self):
233
+ return yz.mult_poly_q_double
234
+
235
+ def __call__(self, x, cv=None):
236
+ genset = self.genset
237
+ # logger.debug(f"{x=} {type(x)=}")
238
+ if not genset:
239
+ genset = self.genset
240
+ if not isinstance(genset, GeneratingSet_base):
241
+ raise TypeError
242
+ if isinstance(x, list) or isinstance(x, tuple):
243
+ if cv is None:
244
+ cv = "y"
245
+ elem = self._from_dict({(Permutation(x), cv): 1})
246
+ elif isinstance(x, Permutation):
247
+ if cv is None:
248
+ cv = "y"
249
+ elem = self._from_dict({(x, cv): 1})
250
+ # elif isinstance(x, spr.SchubertPolynomial):
251
+ # if x._parent._base_var == self._base_var:
252
+ # elem_dict = {(x, utils.NoneVar): v for k, v in x.coeff_dict.items()}
253
+ # elem = QuantumDoubleSchubertAlgebraElement(elem_dict, self)
254
+ # if cv is not None:
255
+ # elem = self([1, 2], cv) * elem
256
+ # else:
257
+ # return self(x.expand(), cv)
258
+ elif isinstance(x, QuantumDoubleSchubertAlgebraElement):
259
+ if x.is_Add or x.is_Mul:
260
+ return x
261
+ if x.genset == genset:
262
+ elem = QuantumDoubleSchubertAlgebraElement(x.coeff_dict, self) # , self)
263
+ else:
264
+ return self(x.expand(), cv, genset)
265
+ elif isinstance(x, spr.DoubleSchubertAlgebraElement):
266
+ if x.genset == self.genset:
267
+ return self(x.expand(), cv, genset)
268
+ else:
269
+ # logger.debug("bagelflap")
270
+ x = sympify(x)
271
+ if cv is None or cv == utils.NoneVar:
272
+ cv = utils.NoneVar
273
+ # logger.debug(f"{x=} {list(genset)=}")
274
+ result = py.mult_poly_q({Permutation([]): 1}, x, genset)
275
+ # logger.debug(f"{result=}")
276
+ else:
277
+ result = yz.mult_poly_q_double({Permutation([]): 1}, x, genset, utils.poly_ring(cv))
278
+ elem = QuantumDoubleSchubertAlgebraElement({(k, cv): v for k, v in result.items()}, self)
279
+ return elem
280
+
281
+
282
+ QDSx = QuantumDoubleSchubertAlgebraElement_basis(GeneratingSet("x"))
283
+
284
+ t = GeneratingSet("t")
285
+ a = GeneratingSet("a")
286
+
287
+ spunky_basis = spr.SchubertAlgebraElement_basis(t)
288
+
289
+
290
+ class QuantumSchubertAlgebraElement_basis(QuantumDoubleSchubertAlgebraElement_basis):
291
+ def __new__(cls, genset):
292
+ return QuantumDoubleSchubertAlgebraElement_basis.__new__(cls, genset)
293
+
294
+ def _from_single_dict(self, _dict):
295
+ return QuantumDoubleSchubertAlgebraElement({(k, utils.NoneVar): v for k, v in _dict.items()}, self)
296
+
297
+ def __call__(self, x):
298
+ genset = self.genset
299
+ # logger.debug(f"{x=} {type(x)=}")
300
+ if not genset:
301
+ genset = self.genset
302
+ if not isinstance(genset, GeneratingSet_base):
303
+ raise TypeError
304
+ if isinstance(x, list) or isinstance(x, tuple):
305
+ elem = self._from_single_dict({Permutation(x): 1})
306
+ elif isinstance(x, Permutation):
307
+ elem = self._from_single_dict({x: 1})
308
+ # elif isinstance(x, spr.SchubertPolynomial):
309
+ # if x._parent._base_var == self._base_var:
310
+ # elem_dict = {(x, utils.NoneVar): v for k, v in x.coeff_dict.items()}
311
+ # elem = QuantumDoubleSchubertAlgebraElement(elem_dict, self)
312
+ # if cv is not None:
313
+ # elem = self([1, 2], cv) * elem
314
+ # else:
315
+ # return self(x.expand(), cv)
316
+ elif isinstance(x, QuantumDoubleSchubertAlgebraElement):
317
+ if x.is_Add or x.is_Mul:
318
+ return x
319
+ if x.genset == genset:
320
+ elem = QuantumDoubleSchubertAlgebraElement(x.coeff_dict, self) # , self)
321
+ else:
322
+ return self(x.expand())
323
+ elif isinstance(x, spr.DoubleSchubertAlgebraElement):
324
+ if x.genset == self.genset:
325
+ return x.as_quantum()
326
+ elif isinstance(x, ParabolicQuantumDoubleSchubertAlgebraElement):
327
+ return x.as_quantum()
328
+ else:
329
+ x = sympify(x)
330
+ result = py.mult_poly_q({Permutation([]): 1}, x, genset)
331
+ elem = self._from_single_dict(result)
332
+ return elem
333
+
334
+ def in_SEM_basis(self):
335
+ result = S.Zero
336
+ for k, v in self.coeff_dict.items():
337
+ if len(k[0]) > len(self._longest):
338
+ parabolic_index = []
339
+ start = 0
340
+ # 1, 2 | 3
341
+ index_comp = [*self._n, len(k[0]) + 1 - self._N[-1]]
342
+ for i in range(len(index_comp)):
343
+ end = start + index_comp[i]
344
+ parabolic_index += list(range(start + 1, end))
345
+ # start += int(args.parabolic[i])
346
+ start = end
347
+ otherlong = Permutation(list(range(parabolic_index[-1] + 1, 0, -1)))
348
+ longpar = Permutation(longest_element(parabolic_index))
349
+ # print(f"{longpar=} {parabolic_index=}")
350
+ longest = otherlong * longpar
351
+ # print(f"new longest = {longest=}")
352
+ else:
353
+ longest = self._longest
354
+ result += v * schubpoly_from_elems(k[0], self.genset, utils.poly_ring(k[1]), elem_func=self.basis.symbol_elem_func, mumu=~longest)
355
+ return result
356
+
357
+
358
+ class ParabolicQuantumDoubleSchubertAlgebraElement_basis(Basic):
359
+ def __new__(cls, genset, index_comp):
360
+ obj = Basic.__new__(cls, genset, sympy.Tuple(*index_comp))
361
+ obj._quantum_basis = QuantumDoubleSchubertAlgebraElement_basis(genset)
362
+ obj._classical_basis = spr.DoubleSchubertAlgebraElement_basis(genset)
363
+ obj._n = list(index_comp)
364
+ obj._N = [sum(obj._n[:i]) for i in range(len(obj._n) + 1)]
365
+ parabolic_index = []
366
+ start = 0
367
+ for i in range(len(index_comp)):
368
+ end = start + index_comp[i]
369
+ parabolic_index += list(range(start + 1, end))
370
+ # start += int(args.parabolic[i])
371
+ start = end
372
+ obj._parabolic_index = parabolic_index
373
+ obj._otherlong = Permutation(list(range(obj._N[-1], 0, -1)))
374
+ obj._longest = obj._otherlong * longest_element(parabolic_index)
375
+ return obj
376
+
377
+ @property
378
+ def symbol_elem_func(self):
379
+ def elem_func(p, k, varl1, varl2): # noqa: ARG001
380
+ if p == 0 and k >= 0:
381
+ return 1
382
+ if p < 0 or p > k:
383
+ return 0
384
+ return sympy.Add(*[(Symbol(f"e_{p - i}_{k}") if p - i > 0 else 1) * complete_sym_poly(i, k + 1 - p, [-v for v in varl2]) for i in range(p + 1)])
385
+
386
+ return elem_func
387
+
388
+ def elem_sym_subs(self, kk):
389
+ elems = []
390
+ elem_func = self.elem_sym
391
+ for k in range(1, kk + 1):
392
+ for p in range(1, k + 1):
393
+ elems += [(sympy.Symbol(f"e_{p}_{k}"), elem_func(p, k, self.genset[1:], utils.poly_ring(0)))]
394
+ return dict(elems)
395
+
396
+ @property
397
+ def parabolic_index(self):
398
+ return self._parabolic_index
399
+
400
+ @property
401
+ def quantum_basis(self):
402
+ return self._quantum_basis
403
+
404
+ @property
405
+ def classical_basis(self):
406
+ return self._classical_basis
407
+
408
+ # def elem_sym_poly(self, p, k, varl1, varl2, xstart=0, ystart=0):
409
+ # # print(f"{p=} {k=} {xstart=} {ystart=} {len(varl1)=} {len(varl2)=}")
410
+ # if p > k:
411
+ # return zero
412
+ # if p == 0:
413
+ # return one
414
+ # if p == 1:
415
+ # res = varl1[xstart] - varl2[ystart]
416
+ # for i in range(1, k):
417
+ # res += varl1[xstart + i] - varl2[ystart + i]
418
+ # return res
419
+ # if p == k:
420
+ # res = (varl1[xstart] - varl2[ystart]) * (varl1[xstart + 1] - varl2[ystart])
421
+ # for i in range(2, k):
422
+ # res *= varl1[i + xstart] - varl2[ystart]
423
+ # return res
424
+ # mid = k // 2
425
+ # xsm = xstart + mid
426
+ # ysm = ystart + mid
427
+ # kmm = k - mid
428
+ # res = elem_sym_poly(p, mid, varl1, varl2, xstart, ystart) + elem_sym_poly(
429
+ # p,
430
+ # kmm,
431
+ # varl1,
432
+ # varl2,
433
+ # xsm,
434
+ # ysm,
435
+ # )
436
+ # for p2 in range(max(1, p - kmm), min(p, mid + 1)):
437
+ # res += elem_sym_poly(p2, mid, varl1, varl2, xstart, ystart) * elem_sym_poly(
438
+ # p - p2,
439
+ # kmm,
440
+ # varl1,
441
+ # varl2,
442
+ # xsm,
443
+ # ysm - p2,
444
+ # )
445
+ # return res
446
+
447
+ def elem_sym(self, p, k, varl1, varl2):
448
+ # print(f"{p=} {k=} {self._N=}")
449
+ if p < 0 or p > k:
450
+ return 0
451
+ if p == 0 and k >= 0:
452
+ return 1
453
+ if k <= self._N[1]:
454
+ return elem_sym_poly(p, k, varl1, varl2)
455
+ ret = 0
456
+ j = bisect_left(self._N, k)
457
+ if j < len(self._N) and k == self._N[j]:
458
+ ret = (-((-1) ** (self._n[j - 1]))) * q_var[j - 1] * self.elem_sym(p - self._N[j] + self._N[j - 2], self._N[j - 2], varl1, varl2)
459
+ ret += self.elem_sym(p, k - 1, varl1, varl2) + (varl1[k - 1] - varl2[k - p]) * self.elem_sym(p - 1, k - 1, varl1, varl2)
460
+ return ret
461
+
462
+ # def classical_elem(self, k, coeff_var):
463
+ # if k <= self._N[1]:
464
+ # return self(uncode([1 for i in range(k)]), coeff_var)
465
+
466
+ def _from_dict(self, _dict):
467
+ return ParabolicQuantumDoubleSchubertAlgebraElement(_dict, self)
468
+
469
+ @property
470
+ def genset(self):
471
+ return self.args[0]
472
+
473
+ @property
474
+ def index_comp(self):
475
+ return self.args[1]
476
+
477
+ def process_coeff_dict(self, coeff_dict):
478
+ max_len = max(len(w) for w in coeff_dict)
479
+ parabolic_index = [*self._parabolic_index]
480
+ # print(f"bagels = {parabolic_index=} {type(self)=}")
481
+ # parabolic_index += list(range(parabolic_index[-1] + 2, max_len + 1))
482
+ if max_len > len(self._longest):
483
+ parabolic_index = []
484
+ start = 0
485
+ # 1, 2 | 3
486
+ index_comp = [*self._n, max_len + 1 - self._N[-1]]
487
+ for i in range(len(index_comp)):
488
+ end = start + index_comp[i]
489
+ parabolic_index += list(range(start + 1, end))
490
+ # start += int(args.parabolic[i])
491
+ start = end
492
+ return yz.apply_peterson_woodward(coeff_dict, parabolic_index)
493
+
494
+ @cache
495
+ def cached_product(self, u, v, va, vb):
496
+ initial_dict = {k: xreplace_genvars(x, utils.poly_ring(va), utils.poly_ring(vb)) for k, x in yz.schubmult_q_double_pair_generic(u, v).items()}
497
+ return {(k, va): v for k, v in self.process_coeff_dict(initial_dict).items()}
498
+
499
+ def in_quantum_basis(self, elem):
500
+ result = S.Zero
501
+ for k, v in elem.coeff_dict.items():
502
+ result += v * schubpoly_from_elems(k[0], self.genset, utils.poly_ring(k[1]), self.quantum_elem_func(k[1]))
503
+ # print(f"{result=}")
504
+ return result
505
+
506
+ def in_classical_basis(self, elem):
507
+ result = S.Zero
508
+ for k, v in elem.coeff_dict.items():
509
+ result += v * self.quantum_as_classical_schubpoly(k[0], k[1])
510
+ # print(f"{result=}")
511
+ return result
512
+
513
+ @cache
514
+ def classical_in_basis(self, k):
515
+ from symengine import expand
516
+
517
+ a = self.classical_basis(*k)
518
+ b = self(*k)
519
+ if expand(a.as_polynomial() - b.as_polynomial()) == S.Zero:
520
+ return b
521
+ cd = dict(b.as_classical().coeff_dict)
522
+ for k2, v in cd.items():
523
+ if k != k2:
524
+ b -= v * self.classical_in_basis(k2)
525
+ return b
526
+
527
+ def classical_elem_func(self, coeff_var):
528
+ basis = spr.DoubleSchubertAlgebraElement_basis(self.genset)
529
+ q_var = yz._vars.q_var
530
+
531
+ def elem_func(p, k, varl1, varl2):
532
+ # print(f"{p=} {k=} {varl1=} {varl2=}")
533
+ if p == 0 and k >= 0:
534
+ return basis([], coeff_var)
535
+ if p < 0 or p > k:
536
+ return basis(0, coeff_var)
537
+ if k <= self._N[1]:
538
+ return basis(elem_sym_poly(p, k, varl1, varl2), coeff_var)
539
+ ret = basis(0, coeff_var)
540
+ j = bisect_left(self._N, k)
541
+ if j < len(self._N) and k == self._N[j]:
542
+ ret = (-((-1) ** (self._n[j - 1]))) * q_var[j - 1] * elem_func(p - self._N[j] + self._N[j - 2], self._N[j - 2], varl1, varl2)
543
+ ret += elem_func(p, k - 1, varl1, varl2) + (varl1[k - 1] - varl2[k - p]) * elem_func(p - 1, k - 1, varl1, varl2)
544
+ # print(f"{ret=}")
545
+ return ret
546
+
547
+ return elem_func
548
+
549
+ def quantum_elem_func(self, coeff_var):
550
+ basis = QuantumDoubleSchubertAlgebraElement_basis(self.genset)
551
+ q_var = yz._vars.q_var
552
+
553
+ def elem_func(p, k, varl1, varl2):
554
+ # print(f"{p=} {k=} {varl1=} {varl2=}")
555
+ if p == 0 and k >= 0:
556
+ return basis([], coeff_var)
557
+ if p < 0 or p > k:
558
+ return basis(0, coeff_var)
559
+ if k <= self._N[1]:
560
+ return basis(elem_sym_poly(p, k, varl1, varl2), coeff_var)
561
+ ret = basis(0, coeff_var)
562
+ j = bisect_left(self._N, k)
563
+ if j < len(self._N) and k == self._N[j]:
564
+ ret = (-((-1) ** (self._n[j - 1]))) * q_var[j - 1] * elem_func(p - self._N[j] + self._N[j - 2], self._N[j - 2], varl1, varl2)
565
+ ret += elem_func(p, k - 1, varl1, varl2) + (varl1[k - 1] - varl2[k - p]) * elem_func(p - 1, k - 1, varl1, varl2)
566
+ # print(f"{ret=}")
567
+ return ret
568
+
569
+ return elem_func
570
+
571
+ @property
572
+ def single_element_class(self):
573
+ return PQDSchubPoly
574
+
575
+ @cache
576
+ def quantum_as_classical_schubpoly(self, perm, coeff_var="y"):
577
+ k = (perm, coeff_var)
578
+ # print(f"{k=}")
579
+ if len(k[0]) > len(self._longest):
580
+ parabolic_index = []
581
+ start = 0
582
+ # 1, 2 | 3
583
+ index_comp = [*self._n, len(k[0]) + 1 - self._N[-1]]
584
+ for i in range(len(index_comp)):
585
+ end = start + index_comp[i]
586
+ parabolic_index += list(range(start + 1, end))
587
+ # start += int(args.parabolic[i])
588
+ start = end
589
+ otherlong = Permutation(list(range(parabolic_index[-1] + 1, 0, -1)))
590
+ longpar = Permutation(longest_element(parabolic_index))
591
+ # print(f"{longpar=} {parabolic_index=}")
592
+ longest = otherlong * longpar
593
+ # print(f"new longest = {longest=}")
594
+ else:
595
+ longest = self._longest
596
+ return schubpoly_from_elems(perm, self.genset, utils.poly_ring(coeff_var), elem_func=self.classical_elem_func(coeff_var), mumu=~longest)
597
+
598
+ @cache
599
+ def cached_schubpoly(self, k):
600
+ if len(k[0]) > len(self._longest):
601
+ parabolic_index = []
602
+ start = 0
603
+ # 1, 2 | 3
604
+ index_comp = [*self._n, len(k[0]) + 1 - self._N[-1]]
605
+ for i in range(len(index_comp)):
606
+ end = start + index_comp[i]
607
+ parabolic_index += list(range(start + 1, end))
608
+ # start += int(args.parabolic[i])
609
+ start = end
610
+ otherlong = Permutation(list(range(parabolic_index[-1] + 1, 0, -1)))
611
+ longpar = Permutation(longest_element(parabolic_index))
612
+ # print(f"{longpar=} {parabolic_index=}")
613
+ longest = otherlong * longpar
614
+ # print(f"new longest = {longest=}")
615
+ else:
616
+ longest = self._longest
617
+ return schubpoly_from_elems(k[0], self.genset, utils.poly_ring(k[1]), elem_func=self.elem_sym, mumu=~longest) # yz.schubpoly_quantum(k[0], self.genset, utils.poly_ring(k[1]))
618
+
619
+ @cache
620
+ def cached_positive_product(self, u, v, va, vb):
621
+ initial_dict = {k: xreplace_genvars(x, utils.poly_ring(va), utils.poly_ring(vb)) for k, x in yz.schubmult_q_generic_partial_posify(u, v).items()}
622
+ return {(k, va): v for k, v in self.process_coeff_dict(initial_dict).items()}
623
+
624
+ @property
625
+ def double_mul(self):
626
+ from schubmult.schub_lib.quantum_double import _vars
627
+
628
+ def do_double_mul(perm_dict, v, var2=_vars.var2, var3=_vars.var3, q_var=_vars.q_var):
629
+ coeff_dict = yz.schubmult_q_double_fast(perm_dict, v, var2, var3, q_var)
630
+ return self.process_coeff_dict(coeff_dict)
631
+
632
+ return do_double_mul
633
+
634
+ @property
635
+ def single_mul(self):
636
+ from schubmult.schub_lib.quantum_double import _vars
637
+
638
+ def do_single_mul(perm_dict, v, q_var=_vars.q_var):
639
+ coeff_dict = py.schubmult_q_fast(perm_dict, v, q_var)
640
+ return self.process_coeff_dict(coeff_dict)
641
+
642
+ return do_single_mul
643
+
644
+ @property
645
+ def mult_poly_single(self):
646
+ return py.mult_poly_q
647
+
648
+ @property
649
+ def mult_poly_double(self):
650
+ return yz.mult_poly_q_double
651
+
652
+ def __call__(self, x, cv=None):
653
+ genset = self.genset
654
+ # logger.debug(f"{x=} {type(x)=}")
655
+ if not genset:
656
+ genset = self.genset
657
+ if not isinstance(genset, GeneratingSet_base):
658
+ raise TypeError
659
+ if isinstance(x, list) or isinstance(x, tuple):
660
+ if cv is None:
661
+ cv = "y"
662
+ perm = Permutation(x)
663
+ if not is_parabolic(perm, self.parabolic_index):
664
+ raise ValueError(f"Permutation must be parabolic: {perm} is not")
665
+ elem = self._from_dict({(perm, cv): 1})
666
+ elif isinstance(x, Permutation):
667
+ if cv is None:
668
+ cv = "y"
669
+ if not is_parabolic(x, self.parabolic_index):
670
+ raise ValueError(f"Permutation must be parabolic: {x} is not")
671
+ elem = self._from_dict({(x, cv): 1})
672
+ elif isinstance(x, ParabolicQuantumDoubleSchubertAlgebraElement):
673
+ return x
674
+ else:
675
+ dct = self.classical_basis(x, cv)
676
+ elem = 0
677
+ if not isinstance(dct, spr.BasisSchubertAlgebraElement):
678
+ return dct
679
+ try:
680
+ for k, v in dct.coeff_dict.items():
681
+ if elem == 0:
682
+ elem = v * self.classical_in_basis(k)
683
+ else:
684
+ elem += v * self.classical_in_basis(k)
685
+ except ValueError:
686
+ raise ValueError(f"Could not convert {x=} to quantum parabolic")
687
+ return elem
688
+
689
+
690
+ QSx = QuantumSchubertAlgebraElement_basis(GeneratingSet("x"))
691
+
692
+ QuantumDoubleSchubertPolynomial = QuantumDoubleSchubertAlgebraElement
693
+
694
+
695
+ def make_parabolic_quantum_basis(index_comp):
696
+ return ParabolicQuantumDoubleSchubertAlgebraElement_basis(GeneratingSet("x"), index_comp)
697
+
698
+
699
+ @cache
700
+ def QPDSx(*args):
701
+ return make_parabolic_quantum_basis(args)
702
+
703
+
704
+ class ParabolicQuantumSchubertAlgebraElement_basis(ParabolicQuantumDoubleSchubertAlgebraElement_basis):
705
+ def __new__(cls, genset, index_comp):
706
+ return ParabolicQuantumDoubleSchubertAlgebraElement_basis.__new__(cls, genset, index_comp)
707
+
708
+ def __hash__(self):
709
+ return hash((*self.args, utils.NoneVar))
710
+
711
+ def _from_single_dict(self, _dict):
712
+ return ParabolicQuantumDoubleSchubertAlgebraElement({(k, utils.NoneVar): v for k, v in _dict.items()}, self)
713
+
714
+ def __call__(self, x):
715
+ genset = self.genset
716
+ # logger.debug(f"{x=} {type(x)=}")
717
+ if not genset:
718
+ genset = self.genset
719
+ if isinstance(x, list) or isinstance(x, tuple):
720
+ perm = Permutation(x)
721
+ if not is_parabolic(perm, self.parabolic_index):
722
+ raise ValueError(f"Permutation must be parabolic: {perm} is not")
723
+ elem = self._from_dict({(perm, utils.NoneVar): 1})
724
+ elif isinstance(x, Permutation):
725
+ if not is_parabolic(x, self.parabolic_index):
726
+ raise ValueError(f"Permutation must be parabolic: {x} is not")
727
+ elem = self._from_dict({(x, utils.NoneVar): 1})
728
+ elif isinstance(x, ParabolicQuantumDoubleSchubertAlgebraElement):
729
+ return x
730
+ else:
731
+ dct = self.classical_basis(x, utils.NoneVar)
732
+ elem = 0
733
+ if not isinstance(dct, spr.BasisSchubertAlgebraElement):
734
+ return dct
735
+ try:
736
+ for k, v in dct.coeff_dict.items():
737
+ if elem == 0:
738
+ elem = v * self.classical_in_basis(k)
739
+ else:
740
+ elem += v * self.classical_in_basis(k)
741
+ except ValueError:
742
+ raise ValueError(f"Could not convert {x=} to quantum parabolic")
743
+ return elem
744
+
745
+
746
+ def make_single_parabolic_quantum_basis(index_comp):
747
+ return ParabolicQuantumSchubertAlgebraElement_basis(GeneratingSet("x"), index_comp)
748
+
749
+
750
+ @cache
751
+ def QPSx(*args):
752
+ return make_single_parabolic_quantum_basis(args)