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.
- schubmult/__init__.py +96 -1
- schubmult/perm_lib.py +254 -819
- schubmult/poly_lib/__init__.py +31 -0
- schubmult/poly_lib/poly_lib.py +276 -0
- schubmult/poly_lib/schub_poly.py +148 -0
- schubmult/poly_lib/variables.py +204 -0
- schubmult/rings/__init__.py +18 -0
- schubmult/rings/_quantum_schubert_polynomial_ring.py +752 -0
- schubmult/rings/_schubert_polynomial_ring.py +1031 -0
- schubmult/rings/_tensor_schub_ring.py +128 -0
- schubmult/rings/_utils.py +55 -0
- schubmult/{sage_integration → sage}/__init__.py +4 -1
- schubmult/{sage_integration → sage}/_fast_double_schubert_polynomial_ring.py +67 -109
- schubmult/{sage_integration → sage}/_fast_schubert_polynomial_ring.py +33 -28
- schubmult/{sage_integration → sage}/_indexing.py +9 -5
- schubmult/schub_lib/__init__.py +51 -0
- schubmult/{schubmult_double/_funcs.py → schub_lib/double.py} +532 -596
- schubmult/{schubmult_q/_funcs.py → schub_lib/quantum.py} +54 -53
- schubmult/schub_lib/quantum_double.py +954 -0
- schubmult/schub_lib/schub_lib.py +659 -0
- schubmult/{schubmult_py/_funcs.py → schub_lib/single.py} +45 -35
- schubmult/schub_lib/tests/__init__.py +0 -0
- schubmult/schub_lib/tests/legacy_perm_lib.py +946 -0
- schubmult/schub_lib/tests/test_vs_old.py +109 -0
- schubmult/scripts/__init__.py +0 -0
- schubmult/scripts/schubmult_double.py +378 -0
- schubmult/scripts/schubmult_py.py +84 -0
- schubmult/scripts/schubmult_q.py +109 -0
- schubmult/scripts/schubmult_q_double.py +207 -0
- schubmult/utils/__init__.py +0 -0
- schubmult/{_base_argparse.py → utils/argparse.py} +29 -5
- schubmult/utils/logging.py +16 -0
- schubmult/utils/parsing.py +20 -0
- schubmult/utils/perm_utils.py +135 -0
- schubmult/utils/test_utils.py +65 -0
- schubmult-3.0.1.dist-info/METADATA +1234 -0
- schubmult-3.0.1.dist-info/RECORD +41 -0
- {schubmult-2.0.4.dist-info → schubmult-3.0.1.dist-info}/WHEEL +1 -1
- schubmult-3.0.1.dist-info/entry_points.txt +5 -0
- schubmult/_tests.py +0 -24
- schubmult/schubmult_double/__init__.py +0 -12
- schubmult/schubmult_double/__main__.py +0 -6
- schubmult/schubmult_double/_script.py +0 -474
- schubmult/schubmult_py/__init__.py +0 -12
- schubmult/schubmult_py/__main__.py +0 -6
- schubmult/schubmult_py/_script.py +0 -97
- schubmult/schubmult_q/__init__.py +0 -8
- schubmult/schubmult_q/__main__.py +0 -6
- schubmult/schubmult_q/_script.py +0 -166
- schubmult/schubmult_q_double/__init__.py +0 -10
- schubmult/schubmult_q_double/__main__.py +0 -6
- schubmult/schubmult_q_double/_funcs.py +0 -540
- schubmult/schubmult_q_double/_script.py +0 -396
- schubmult-2.0.4.dist-info/METADATA +0 -542
- schubmult-2.0.4.dist-info/RECORD +0 -30
- schubmult-2.0.4.dist-info/entry_points.txt +0 -5
- {schubmult-2.0.4.dist-info → schubmult-3.0.1.dist-info}/licenses/LICENSE +0 -0
- {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)
|