schubmult 2.0.4__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 (58) hide show
  1. schubmult/__init__.py +94 -1
  2. schubmult/perm_lib.py +232 -819
  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 +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.0.dist-info/METADATA +1234 -0
  37. schubmult-3.0.0.dist-info/RECORD +41 -0
  38. {schubmult-2.0.4.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 -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.0.dist-info}/licenses/LICENSE +0 -0
  58. {schubmult-2.0.4.dist-info → schubmult-3.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,788 @@
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, 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 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
+ logger.debug("ferefef")
35
+ elb = self.as_classical().subs(old, new).as_quantum()
36
+ logger.debug(f"{elb=}")
37
+ return elb
38
+
39
+
40
+ # # TODO: not a noncommutative symbol, something else
41
+ # # Atomic Schubert polynomial
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._coeff_var = k[1]
53
+ # obj._base_var = base_var
54
+ return obj
55
+
56
+ @staticmethod
57
+ @cache
58
+ def __xnew_cached__(_class, k, genset):
59
+ return QDSchubPoly.__xnew__(_class, k, genset)
60
+
61
+ def _sympystr(self, printer):
62
+ if self._coeff_var == 0 or self._coeff_var == utils.NoneVar:
63
+ return printer.doprint(f"QS{self.genset.label}({printer.doprint(self._perm)})")
64
+ return printer.doprint(f"QDS{self.genset.label}({printer.doprint(self._perm)}, {spr._varstr(self._coeff_var)})")
65
+
66
+
67
+ class ParabolicQuantumDoubleSchubertAlgebraElement(spr.BasisSchubertAlgebraElement):
68
+ def __new__(cls, _dict, basis):
69
+ return spr.BasisSchubertAlgebraElement.__new__(cls, _dict, basis)
70
+
71
+ @property
72
+ def index_comp(self):
73
+ return self.basis.index_comp
74
+
75
+ def kill_ideal(self):
76
+ length = sum(self.index_comp)
77
+ new_dict = {}
78
+ for k, v in self.coeff_dict.items():
79
+ if len(k[0]) <= length:
80
+ new_dict[k] = v
81
+ return self.basis._from_dict(new_dict)
82
+
83
+
84
+ class PQDSchubPoly(ParabolicQuantumDoubleSchubertAlgebraElement):
85
+ is_Atom = True
86
+
87
+ def __new__(cls, k, basis):
88
+ return PQDSchubPoly.__xnew_cached__(cls, k, basis)
89
+
90
+ @staticmethod
91
+ def __xnew__(_class, k, basis):
92
+ obj = ParabolicQuantumDoubleSchubertAlgebraElement.__new__(_class, sympy.Dict({(Permutation(k[0]), k[1]): 1}), basis)
93
+ obj._perm = k[0]
94
+ # obj._perm._print_as_code = True
95
+ obj._coeff_var = k[1]
96
+ # obj._base_var = base_var
97
+ return obj
98
+
99
+ @staticmethod
100
+ @cache
101
+ def __xnew_cached__(_class, k, genset):
102
+ return PQDSchubPoly.__xnew__(_class, k, genset)
103
+
104
+ def _sympystr(self, printer):
105
+ if self._coeff_var == 0 or self._coeff_var == utils.NoneVar:
106
+ return printer.doprint(f"QPS{self.genset.label}{(tuple(self.index_comp))}({printer.doprint(self._perm)})")
107
+ return printer.doprint(f"QPDS{self.genset.label}{tuple(self.index_comp)}({printer.doprint(self._perm)}, {spr._varstr(self._coeff_var)})")
108
+
109
+
110
+ class QuantumDoubleSchubertAlgebraElement_basis(Basic):
111
+ def __new__(cls, genset):
112
+ return Basic.__new__(cls, genset)
113
+
114
+ def _from_dict(self, _dict):
115
+ return QuantumDoubleSchubertAlgebraElement(_dict, self)
116
+
117
+ @property
118
+ def genset(self):
119
+ return self.args[0]
120
+
121
+ @cache
122
+ def cached_product(self, u, v, va, vb):
123
+ 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()}
124
+
125
+ def in_quantum_basis(self, elem):
126
+ return elem
127
+
128
+ def in_classical_basis(self, elem):
129
+ result = S.Zero
130
+ for k, v in elem.coeff_dict.items():
131
+ result += v * self.quantum_as_classical_schubpoly(k[0], k[1])
132
+ return result
133
+
134
+ def classical_elem_func(self, coeff_var):
135
+ basis = spr.DoubleSchubertAlgebraElement_basis(self.genset)
136
+ q_var = yz._vars.q_var
137
+
138
+ def elem_func(p, k, varl1, varl2):
139
+ if p == 0 and k >= 0:
140
+ return basis([], coeff_var)
141
+ if p < 0 or p > k:
142
+ return basis(0, coeff_var)
143
+ 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)
144
+
145
+ return elem_func
146
+
147
+ @property
148
+ def single_element_class(self):
149
+ return QDSchubPoly
150
+
151
+ @cache
152
+ def quantum_as_classical_schubpoly(self, perm, coeff_var="y"):
153
+ return schubpoly_from_elems(perm, self.genset, utils.poly_ring(coeff_var), self.classical_elem_func(coeff_var))
154
+
155
+ @cache
156
+ def cached_schubpoly(self, k):
157
+ 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]))
158
+
159
+ @cache
160
+ def cached_positive_product(self, u, v, va, vb):
161
+ 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()}
162
+
163
+ @property
164
+ def double_mul(self):
165
+ return yz.schubmult_q_double_fast
166
+
167
+ @property
168
+ def single_mul(self):
169
+ return py.schubmult_q_fast
170
+
171
+ @property
172
+ def mult_poly_single(self):
173
+ return py.mult_poly_q
174
+
175
+ @property
176
+ def mult_poly_double(self):
177
+ return yz.mult_poly_q_double
178
+
179
+ def __call__(self, x, cv=None):
180
+ genset = self.genset
181
+ # logger.debug(f"{x=} {type(x)=}")
182
+ if not genset:
183
+ genset = self.genset
184
+ if not isinstance(genset, GeneratingSet_base):
185
+ raise TypeError
186
+ if isinstance(x, list) or isinstance(x, tuple):
187
+ if cv is None:
188
+ cv = "y"
189
+ elem = self._from_dict({(Permutation(x), cv): 1})
190
+ elif isinstance(x, Permutation):
191
+ if cv is None:
192
+ cv = "y"
193
+ elem = self._from_dict({(x, cv): 1})
194
+ # elif isinstance(x, spr.SchubertPolynomial):
195
+ # if x._parent._base_var == self._base_var:
196
+ # elem_dict = {(x, utils.NoneVar): v for k, v in x.coeff_dict.items()}
197
+ # elem = QuantumDoubleSchubertAlgebraElement(elem_dict, self)
198
+ # if cv is not None:
199
+ # elem = self([1, 2], cv) * elem
200
+ # else:
201
+ # return self(x.expand(), cv)
202
+ elif isinstance(x, QuantumDoubleSchubertAlgebraElement):
203
+ if x.is_Add or x.is_Mul:
204
+ return x
205
+ if x.genset == genset:
206
+ elem = QuantumDoubleSchubertAlgebraElement(x.coeff_dict, self) # , self)
207
+ else:
208
+ return self(x.expand(), cv, genset)
209
+ elif isinstance(x, spr.DoubleSchubertAlgebraElement):
210
+ if x.genset == self.genset:
211
+ return self(x.expand(), cv, genset)
212
+ else:
213
+ logger.debug("bagelflap")
214
+ x = sympify(x)
215
+ if cv is None or cv == utils.NoneVar:
216
+ cv = utils.NoneVar
217
+ logger.debug(f"{x=} {list(genset)=}")
218
+ result = py.mult_poly_q({Permutation([]): 1}, x, genset)
219
+ logger.debug(f"{result=}")
220
+ else:
221
+ result = yz.mult_poly_q_double({Permutation([]): 1}, x, genset, utils.poly_ring(cv))
222
+ elem = QuantumDoubleSchubertAlgebraElement({(k, cv): v for k, v in result.items()}, self)
223
+ return elem
224
+
225
+
226
+ QDSx = QuantumDoubleSchubertAlgebraElement_basis(GeneratingSet("x"))
227
+
228
+ t = GeneratingSet("t")
229
+ a = GeneratingSet("a")
230
+
231
+ spunky_basis = spr.SchubertAlgebraElement_basis(t)
232
+
233
+
234
+ class QuantumSchubertAlgebraElement_basis(QuantumDoubleSchubertAlgebraElement_basis):
235
+ def __new__(cls, genset):
236
+ return QuantumDoubleSchubertAlgebraElement_basis.__new__(cls, genset)
237
+
238
+ def _from_single_dict(self, _dict):
239
+ return QuantumDoubleSchubertAlgebraElement({(k, utils.NoneVar): v for k, v in _dict.items()}, self)
240
+
241
+ def __call__(self, x):
242
+ genset = self.genset
243
+ # logger.debug(f"{x=} {type(x)=}")
244
+ if not genset:
245
+ genset = self.genset
246
+ if not isinstance(genset, GeneratingSet_base):
247
+ raise TypeError
248
+ if isinstance(x, list) or isinstance(x, tuple):
249
+ elem = self._from_single_dict({Permutation(x): 1})
250
+ elif isinstance(x, Permutation):
251
+ elem = self._from_single_dict({x: 1})
252
+ # elif isinstance(x, spr.SchubertPolynomial):
253
+ # if x._parent._base_var == self._base_var:
254
+ # elem_dict = {(x, utils.NoneVar): v for k, v in x.coeff_dict.items()}
255
+ # elem = QuantumDoubleSchubertAlgebraElement(elem_dict, self)
256
+ # if cv is not None:
257
+ # elem = self([1, 2], cv) * elem
258
+ # else:
259
+ # return self(x.expand(), cv)
260
+ elif isinstance(x, QuantumDoubleSchubertAlgebraElement):
261
+ if x.is_Add or x.is_Mul:
262
+ return x
263
+ if x.genset == genset:
264
+ elem = QuantumDoubleSchubertAlgebraElement(x.coeff_dict, self) # , self)
265
+ else:
266
+ return self(x.expand())
267
+ elif isinstance(x, spr.DoubleSchubertAlgebraElement):
268
+ if x.genset == self.genset:
269
+ return x.as_quantum()
270
+ elif isinstance(x, ParabolicQuantumDoubleSchubertAlgebraElement):
271
+ return x.as_quantum()
272
+ else:
273
+ x = sympify(x)
274
+ result = py.mult_poly_q({Permutation([]): 1}, x, genset)
275
+ elem = self._from_single_dict(result)
276
+ return elem
277
+
278
+
279
+ class ParabolicQuantumDoubleSchubertAlgebraElement_basis(Basic):
280
+ def __new__(cls, genset, index_comp):
281
+ obj = Basic.__new__(cls, genset, tuple(index_comp))
282
+ obj._quantum_basis = QuantumDoubleSchubertAlgebraElement_basis(genset)
283
+ obj._classical_basis = spr.DoubleSchubertAlgebraElement_basis(genset)
284
+ obj._n = list(index_comp)
285
+ obj._N = [sum(obj._n[:i]) for i in range(len(obj._n) + 1)]
286
+ # print(f"{obj._N=}")
287
+ # obj._D = []
288
+ # obj._E = {}
289
+ # from symengine import Matrix
290
+ # for j in range(1, len(obj._N)):
291
+ # m_arr = [[0 for i in range(obj._N[j])] for p in range(obj._N[j])]
292
+ # for i in range(obj._N[j]):
293
+ # m_arr[i][i] = a[i+1] - t[1] #genset[i+1] - t[1]
294
+ # if i < obj._N[j] - 1:
295
+ # m_arr[i][i+1] = -1
296
+ # for b in range(1, j):
297
+ # njm1 = obj._N[b + 1] - 1
298
+ # njp1 = obj._N[b - 1]
299
+ # # print(f"{b=}")
300
+ # # print(f"{njm1=} {njp1=}")
301
+ # if njp1 < obj._N[j] and njm1 < obj._N[j]:
302
+ # # print(f"{b=} {obj._n[b]=}")
303
+ # m_arr[njm1][njp1] = -(-1)**(obj._n[b])*q_var[b]
304
+ # # print(Matrix(m_arr))
305
+ # poly = Matrix(m_arr).det().simplify()
306
+ # # print(f"{poly=}")
307
+ # # def dongle(v):
308
+ # # return poly.subs(t[1], v)
309
+ # obj._D += [spunky_basis(poly)]
310
+ # obj._E[obj._N[j]] = {obj._N[j]: obj._D[-1]}
311
+ # for i in range(1,obj._N[j]):
312
+ # obj._E[obj._N[j]][obj._N[j] - i] = -obj._E[obj._N[j]][obj._N[j] - i + 1].divdiff(i)
313
+ # # print(obj._E)
314
+ # add_am = 6
315
+ # index_comp += [add_am]
316
+ # obj._N += [obj._N[-1] + add_am]
317
+ parabolic_index = []
318
+ start = 0
319
+ # 1, 2 | 3
320
+ for i in range(len(index_comp)):
321
+ end = start + index_comp[i]
322
+ parabolic_index += list(range(start + 1, end))
323
+ # start += int(args.parabolic[i])
324
+ start = end
325
+ obj._parabolic_index = parabolic_index
326
+ obj._otherlong = Permutation(list(range(obj._N[-1], 0, -1)))
327
+ obj._longest = obj._otherlong * longest_element(parabolic_index)
328
+ # obj._E[0] = obj._from_dict({(Permutation([]),utils.NoneVar): S.One})
329
+ return obj
330
+
331
+ @property
332
+ def parabolic_index(self):
333
+ return self._parabolic_index
334
+
335
+ @property
336
+ def quantum_basis(self):
337
+ return self._quantum_basis
338
+
339
+ @property
340
+ def classical_basis(self):
341
+ return self._classical_basis
342
+
343
+ # def elem_sym_poly(self, p, k, varl1, varl2, xstart=0, ystart=0):
344
+ # # print(f"{p=} {k=} {xstart=} {ystart=} {len(varl1)=} {len(varl2)=}")
345
+ # if p > k:
346
+ # return zero
347
+ # if p == 0:
348
+ # return one
349
+ # if p == 1:
350
+ # res = varl1[xstart] - varl2[ystart]
351
+ # for i in range(1, k):
352
+ # res += varl1[xstart + i] - varl2[ystart + i]
353
+ # return res
354
+ # if p == k:
355
+ # res = (varl1[xstart] - varl2[ystart]) * (varl1[xstart + 1] - varl2[ystart])
356
+ # for i in range(2, k):
357
+ # res *= varl1[i + xstart] - varl2[ystart]
358
+ # return res
359
+ # mid = k // 2
360
+ # xsm = xstart + mid
361
+ # ysm = ystart + mid
362
+ # kmm = k - mid
363
+ # res = elem_sym_poly(p, mid, varl1, varl2, xstart, ystart) + elem_sym_poly(
364
+ # p,
365
+ # kmm,
366
+ # varl1,
367
+ # varl2,
368
+ # xsm,
369
+ # ysm,
370
+ # )
371
+ # for p2 in range(max(1, p - kmm), min(p, mid + 1)):
372
+ # res += elem_sym_poly(p2, mid, varl1, varl2, xstart, ystart) * elem_sym_poly(
373
+ # p - p2,
374
+ # kmm,
375
+ # varl1,
376
+ # varl2,
377
+ # xsm,
378
+ # ysm - p2,
379
+ # )
380
+ # return res
381
+
382
+ def elem_sym(self):
383
+ def elem_func(p, k, varl1, varl2):
384
+ # print(f"{p=} {k=} {self._N=}")
385
+ if p < 0 or p > k:
386
+ return 0
387
+ if p == 0 and k >= 0:
388
+ return 1
389
+ if k <= self._N[1]:
390
+ return elem_sym_poly(p, k, varl1, varl2)
391
+ ret = 0
392
+ j = bisect_left(self._N, k)
393
+ if j < len(self._N) and k == self._N[j]:
394
+ 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)
395
+ ret += elem_func(p, k - 1, varl1, varl2) + (varl1[k - 1] - varl2[k - p]) * elem_func(p - 1, k - 1, varl1, varl2)
396
+ return ret
397
+
398
+ return elem_func
399
+
400
+ def boingle_elem_sym(self):
401
+ def elem_func(p, k, varl1, varl2):
402
+ if p == 0 and k >= 0:
403
+ return S.One
404
+ if p < 0 or p > k:
405
+ return S.Zero
406
+ # print(f"{p=} {k=}")
407
+ spoink = self._E[k][p]
408
+ # print(f"{spoink=}")
409
+ # for i in range(1,k - p + 1):
410
+ # spoink = -spoink.divdiff(i)
411
+ return sympify(sympify(spoink.as_polynomial()).xreplace({t[i]: varl2[i - 1] for i in range(1, len(varl2) + 1)})).xreplace({a[i]: varl1[i - 1] for i in range(1, len(varl1) + 1)})
412
+ # TEMP
413
+ # varl2 = utils.poly_ring(0)
414
+ # if p < 0 or p > k:
415
+ # return 0
416
+ # if p == 0 and k >= 0:
417
+ # return 1
418
+ # if k == self._N[1]:
419
+ # return elem_sym_poly(p, k, varl1, varl2)
420
+ # print(f"{p=} {k=} {self._N=}")
421
+ # ret = 0
422
+ # j = bisect_left(self._N, k)
423
+ # if j < len(self._N) and k == self._N[j]:
424
+ # 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)
425
+ # #ret += elem_func(p, self._N[j-1], varl1, varl2)
426
+ # # print(f"{self._n[j-1]=}")
427
+ # for i in range(min(p+1,self._n[j-1]+1)):
428
+ # # print(f"{p=} {p - i=} {i=} {self._N[j-1]-1=} {self._N[j-1]-1-i=} {len(varl2)=}")
429
+ # ret += varl1[self._N[j] - 1 - i] * elem_func(p - i, self._N[j-1], varl1, varl2)
430
+ # else:
431
+ # # print("Bob jones")
432
+ # return ret
433
+
434
+ return elem_func
435
+
436
+ # def classical_elem(self, k, coeff_var):
437
+ # if k <= self._N[1]:
438
+ # return self(uncode([1 for i in range(k)]), coeff_var)
439
+
440
+ def _from_dict(self, _dict):
441
+ return ParabolicQuantumDoubleSchubertAlgebraElement(_dict, self)
442
+
443
+ @property
444
+ def genset(self):
445
+ return self.args[0]
446
+
447
+ @property
448
+ def index_comp(self):
449
+ return self.args[1]
450
+
451
+ def process_coeff_dict(self, coeff_dict):
452
+ max_len = max(len(w) for w in coeff_dict)
453
+ parabolic_index = [*self._parabolic_index]
454
+ # parabolic_index += list(range(parabolic_index[-1] + 2, max_len + 1))
455
+ if max_len > len(self._longest):
456
+ parabolic_index = []
457
+ start = 0
458
+ # 1, 2 | 3
459
+ index_comp = [*self._n, max_len + 1 - self._N[-1]]
460
+ for i in range(len(index_comp)):
461
+ end = start + index_comp[i]
462
+ parabolic_index += list(range(start + 1, end))
463
+ # start += int(args.parabolic[i])
464
+ start = end
465
+ return yz.apply_peterson_woodward(coeff_dict, parabolic_index)
466
+
467
+ @cache
468
+ def cached_product(self, u, v, va, vb):
469
+ 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()}
470
+ return {(k, va): v for k, v in self.process_coeff_dict(initial_dict).items()}
471
+
472
+ def in_quantum_basis(self, elem):
473
+ result = S.Zero
474
+ for k, v in elem.coeff_dict.items():
475
+ result += v * schubpoly_from_elems(k[0], self.genset, utils.poly_ring(k[1]), self.quantum_elem_func(k[1]))
476
+ # print(f"{result=}")
477
+ return result
478
+
479
+ def in_classical_basis(self, elem):
480
+ result = S.Zero
481
+ for k, v in elem.coeff_dict.items():
482
+ result += v * self.quantum_as_classical_schubpoly(k[0], k[1])
483
+ # print(f"{result=}")
484
+ return result
485
+
486
+ @cache
487
+ def classical_in_basis(self, k):
488
+ from symengine import expand
489
+
490
+ a = self.classical_basis(*k)
491
+ b = self(*k)
492
+ if expand(a.as_polynomial() - b.as_polynomial()) == S.Zero:
493
+ return b
494
+ cd = dict(b.as_classical().coeff_dict)
495
+ for k2, v in cd.items():
496
+ if k != k2:
497
+ b -= v * self.classical_in_basis(k2)
498
+ return b
499
+
500
+ def classical_elem_func(self, coeff_var):
501
+ basis = spr.DoubleSchubertAlgebraElement_basis(self.genset)
502
+ q_var = yz._vars.q_var
503
+
504
+ def elem_func(p, k, varl1, varl2):
505
+ # print(f"{p=} {k=} {varl1=} {varl2=}")
506
+ if p == 0 and k >= 0:
507
+ return basis([], coeff_var)
508
+ if p < 0 or p > k:
509
+ return basis(0, coeff_var)
510
+ if k <= self._N[1]:
511
+ return basis(elem_sym_poly(p, k, varl1, varl2), coeff_var)
512
+ ret = basis(0, coeff_var)
513
+ j = bisect_left(self._N, k)
514
+ if j < len(self._N) and k == self._N[j]:
515
+ 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)
516
+ ret += elem_func(p, k - 1, varl1, varl2) + (varl1[k - 1] - varl2[k - p]) * elem_func(p - 1, k - 1, varl1, varl2)
517
+ # print(f"{ret=}")
518
+ return ret
519
+
520
+ return elem_func
521
+
522
+ def quantum_elem_func(self, coeff_var):
523
+ basis = QuantumDoubleSchubertAlgebraElement_basis(self.genset)
524
+ q_var = yz._vars.q_var
525
+
526
+ def elem_func(p, k, varl1, varl2):
527
+ # print(f"{p=} {k=} {varl1=} {varl2=}")
528
+ if p == 0 and k >= 0:
529
+ return basis([], coeff_var)
530
+ if p < 0 or p > k:
531
+ return basis(0, coeff_var)
532
+ if k <= self._N[1]:
533
+ return basis(elem_sym_poly(p, k, varl1, varl2), coeff_var)
534
+ ret = basis(0, coeff_var)
535
+ j = bisect_left(self._N, k)
536
+ if j < len(self._N) and k == self._N[j]:
537
+ 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)
538
+ ret += elem_func(p, k - 1, varl1, varl2) + (varl1[k - 1] - varl2[k - p]) * elem_func(p - 1, k - 1, varl1, varl2)
539
+ # print(f"{ret=}")
540
+ return ret
541
+
542
+ return elem_func
543
+
544
+ @property
545
+ def single_element_class(self):
546
+ return PQDSchubPoly
547
+
548
+ @cache
549
+ def quantum_as_classical_schubpoly(self, perm, coeff_var="y"):
550
+ k = (perm, coeff_var)
551
+ # print(f"{k=}")
552
+ if len(k[0]) > len(self._longest):
553
+ parabolic_index = []
554
+ start = 0
555
+ # 1, 2 | 3
556
+ index_comp = [*self._n, len(k[0]) + 1 - self._N[-1]]
557
+ for i in range(len(index_comp)):
558
+ end = start + index_comp[i]
559
+ parabolic_index += list(range(start + 1, end))
560
+ # start += int(args.parabolic[i])
561
+ start = end
562
+ otherlong = Permutation(list(range(parabolic_index[-1] + 1, 0, -1)))
563
+ longpar = Permutation(longest_element(parabolic_index))
564
+ # print(f"{longpar=} {parabolic_index=}")
565
+ longest = otherlong * longpar
566
+ # print(f"new longest = {longest=}")
567
+ else:
568
+ longest = self._longest
569
+ return schubpoly_from_elems(perm, self.genset, utils.poly_ring(coeff_var), elem_func=self.classical_elem_func(coeff_var), mumu=~longest)
570
+
571
+ @cache
572
+ def cached_schubpoly(self, k):
573
+ if len(k[0]) > len(self._longest):
574
+ parabolic_index = []
575
+ start = 0
576
+ # 1, 2 | 3
577
+ index_comp = [*self._n, len(k[0]) + 1 - self._N[-1]]
578
+ for i in range(len(index_comp)):
579
+ end = start + index_comp[i]
580
+ parabolic_index += list(range(start + 1, end))
581
+ # start += int(args.parabolic[i])
582
+ start = end
583
+ otherlong = Permutation(list(range(parabolic_index[-1] + 1, 0, -1)))
584
+ longpar = Permutation(longest_element(parabolic_index))
585
+ # print(f"{longpar=} {parabolic_index=}")
586
+ longest = otherlong * longpar
587
+ # print(f"new longest = {longest=}")
588
+ else:
589
+ longest = self._longest
590
+ 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]))
591
+
592
+ @cache
593
+ def cached_positive_product(self, u, v, va, vb):
594
+ 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()}
595
+ return {(k, va): v for k, v in self.process_coeff_dict(initial_dict).items()}
596
+
597
+ @property
598
+ def double_mul(self):
599
+ return yz.schubmult_q_double_fast
600
+
601
+ @property
602
+ def single_mul(self):
603
+ return py.schubmult_q_fast
604
+
605
+ @property
606
+ def mult_poly_single(self):
607
+ return py.mult_poly_q
608
+
609
+ @property
610
+ def mult_poly_double(self):
611
+ return yz.mult_poly_q_double
612
+
613
+ def __call__(self, x, cv=None):
614
+ genset = self.genset
615
+ # logger.debug(f"{x=} {type(x)=}")
616
+ if not genset:
617
+ genset = self.genset
618
+ if not isinstance(genset, GeneratingSet_base):
619
+ raise TypeError
620
+ if isinstance(x, list) or isinstance(x, tuple):
621
+ if cv is None:
622
+ cv = "y"
623
+ perm = Permutation(x)
624
+ if not is_parabolic(perm, self.parabolic_index):
625
+ raise ValueError(f"Permutation must be parabolic: {perm} is not")
626
+ elem = self._from_dict({(perm, cv): 1})
627
+ elif isinstance(x, Permutation):
628
+ if cv is None:
629
+ cv = "y"
630
+ if not is_parabolic(x, self.parabolic_index):
631
+ raise ValueError(f"Permutation must be parabolic: {x} is not")
632
+ elem = self._from_dict({(x, cv): 1})
633
+ elif isinstance(x, ParabolicQuantumDoubleSchubertAlgebraElement):
634
+ return x
635
+ else:
636
+ dct = self.classical_basis(x, cv)
637
+ elem = 0
638
+ if not isinstance(dct, spr.BasisSchubertAlgebraElement):
639
+ return dct
640
+ try:
641
+ for k, v in dct.coeff_dict.items():
642
+ if elem == 0:
643
+ elem = v * self.classical_in_basis(k)
644
+ else:
645
+ elem += v * self.classical_in_basis(k)
646
+ except ValueError:
647
+ raise ValueError(f"Could not convert {x=} to quantum parabolic")
648
+ return elem
649
+
650
+
651
+ QSx = QuantumSchubertAlgebraElement_basis(GeneratingSet("x"))
652
+
653
+ QuantumDoubleSchubertPolynomial = QuantumDoubleSchubertAlgebraElement
654
+
655
+
656
+ def make_parabolic_quantum_basis(index_comp):
657
+ return ParabolicQuantumDoubleSchubertAlgebraElement_basis(GeneratingSet("x"), index_comp)
658
+
659
+
660
+ @cache
661
+ def QPDSx(*args):
662
+ return make_parabolic_quantum_basis(args)
663
+
664
+
665
+ # is_Add = True
666
+ # is_Mul = True
667
+ # is_Add
668
+ # is_AlgebraicNumber
669
+ # is_Atom
670
+ # is_Boolean
671
+ # is_Derivative
672
+ # is_Dummy
673
+ # is_Equality
674
+ # is_Float
675
+ # is_Function
676
+ # is_Indexed
677
+ # is_Integer
678
+ # is_MatAdd
679
+ # is_MatMul
680
+ # is_Matrix
681
+ # is_Mul
682
+ # is_Not
683
+ # is_Number
684
+ # is_NumberSymbol
685
+ # is_Order
686
+ # is_Piecewise
687
+ # is_Point
688
+ # is_Poly
689
+ # is_Pow
690
+ # is_Rational
691
+ # is_Relational
692
+ # is_Symbol
693
+ # is_Vector
694
+ # is_Wild
695
+ # is_algebraic
696
+ # is_algebraic_expr
697
+ # is_antihermitian
698
+ # is_commutative
699
+ # is_comparable
700
+ # is_complex
701
+ # is_composite
702
+ # is_constant
703
+ # is_even
704
+ # is_extended_negative
705
+ # is_extended_nonnegative
706
+ # is_extended_nonpositive
707
+ # is_extended_nonzero
708
+ # is_extended_positive
709
+ # is_extended_real
710
+ # is_finite
711
+ # is_hermitian
712
+ # is_hypergeometric
713
+ # is_imaginary
714
+ # is_infinite
715
+ # is_integer
716
+ # is_irrational
717
+ # is_meromorphic
718
+ # is_negative
719
+ # is_noninteger
720
+ # is_nonnegative
721
+ # is_nonpositive
722
+ # is_nonzero
723
+ # is_number
724
+ # is_odd
725
+ # is_polar
726
+ # is_polynomial
727
+ # is_positive
728
+ # is_prime
729
+ # is_rational
730
+ # is_rational_function
731
+ # is_real
732
+ # is_scalar
733
+ # is_symbol
734
+ # is_transcendental
735
+ # is_zero
736
+ # is_polynomial = True
737
+ # is_Symbol = True
738
+
739
+
740
+ # class SchubAdd(QuantumDoubleSchubertAlgebraElement, Add):
741
+ # is_Add = True
742
+
743
+ # def __new__(cls, *args, evaluate=True, _sympify=True):
744
+ # obj = Add.__new__(cls, *args, evaluate=evaluate, _sympify=_sympify)
745
+ # if evaluate:
746
+ # return obj.doit()
747
+ # return obj
748
+
749
+ # def doit(self):
750
+ # ret = self.args[0]
751
+ # for arg in self.args[1:]:
752
+ # if arg.is_Add or arg.is_Mul:
753
+ # arg = arg.doit()
754
+ # ret = _do_schub_add(ret, arg)
755
+ # return ret
756
+
757
+ # # def _sympystr(self, printer):
758
+ # # return _def_printer._print(f"SchubAdd({self.args}")
759
+
760
+
761
+ # class SchubMul(QuantumDoubleSchubertAlgebraElement, Mul):
762
+ # is_Mul = True
763
+
764
+ # def __new__(cls, *args, evaluate=True, _sympify=True):
765
+ # if len(args) == 0:
766
+ # return 1
767
+ # # args, a, b = Mul.flatten(list(args))
768
+ # # if len(args) == 0:
769
+ # # return 1
770
+ # obj = Mul.__new__(cls, *args, evaluate=evaluate, _sympify=_sympify)
771
+
772
+ # if evaluate:
773
+ # return obj.doit()
774
+ # return obj
775
+
776
+ # def doit(self):
777
+ # ret = self.args[0]
778
+ # for arg in self.args[1:]:
779
+ # if arg.is_Add or arg.is_Mul:
780
+ # arg = arg.doit()
781
+ # ret = _do_schub_mul(ret, arg)
782
+ # return ret
783
+
784
+
785
+ # Basic._constructor_postprocessor_mapping[DoubleSchubertAlgebraElement] = {
786
+ # "Mul": [get_postprocessor(Mul)],
787
+ # "Add": [get_postprocessor(Add)],
788
+ # }