schubmult 2.0.3__py3-none-any.whl → 3.0.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- schubmult/__init__.py +94 -1
- schubmult/perm_lib.py +233 -880
- schubmult/poly_lib/__init__.py +31 -0
- schubmult/poly_lib/poly_lib.py +244 -0
- schubmult/poly_lib/schub_poly.py +148 -0
- schubmult/poly_lib/variables.py +204 -0
- schubmult/rings/__init__.py +17 -0
- schubmult/rings/_quantum_schubert_polynomial_ring.py +788 -0
- schubmult/rings/_schubert_polynomial_ring.py +1006 -0
- schubmult/rings/_tensor_schub_ring.py +128 -0
- schubmult/rings/_utils.py +55 -0
- schubmult/{sage_integration → sage}/__init__.py +17 -15
- schubmult/{sage_integration → sage}/_fast_double_schubert_polynomial_ring.py +142 -220
- schubmult/{sage_integration → sage}/_fast_schubert_polynomial_ring.py +78 -72
- schubmult/sage/_indexing.py +51 -0
- schubmult/schub_lib/__init__.py +51 -0
- schubmult/{schubmult_double/_funcs.py → schub_lib/double.py} +618 -798
- schubmult/{schubmult_q/_funcs.py → schub_lib/quantum.py} +70 -72
- 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} +58 -48
- 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} +40 -11
- 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.0.dist-info/METADATA +1234 -0
- schubmult-3.0.0.dist-info/RECORD +41 -0
- {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/WHEEL +1 -1
- schubmult-3.0.0.dist-info/entry_points.txt +5 -0
- schubmult/_tests.py +0 -9
- schubmult/sage_integration/_indexing.py +0 -51
- schubmult/schubmult_double/__init__.py +0 -22
- schubmult/schubmult_double/__main__.py +0 -5
- schubmult/schubmult_double/_script.py +0 -474
- schubmult/schubmult_py/__init__.py +0 -13
- schubmult/schubmult_py/__main__.py +0 -5
- schubmult/schubmult_py/_script.py +0 -96
- schubmult/schubmult_q/__init__.py +0 -13
- schubmult/schubmult_q/__main__.py +0 -5
- schubmult/schubmult_q/_script.py +0 -160
- schubmult/schubmult_q_double/__init__.py +0 -17
- schubmult/schubmult_q_double/__main__.py +0 -5
- schubmult/schubmult_q_double/_funcs.py +0 -540
- schubmult/schubmult_q_double/_script.py +0 -398
- schubmult-2.0.3.dist-info/METADATA +0 -455
- schubmult-2.0.3.dist-info/RECORD +0 -30
- schubmult-2.0.3.dist-info/entry_points.txt +0 -5
- {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/licenses/LICENSE +0 -0
- {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/top_level.txt +0 -0
@@ -0,0 +1,1006 @@
|
|
1
|
+
import multiprocessing
|
2
|
+
from functools import cache, cached_property
|
3
|
+
|
4
|
+
import sympy
|
5
|
+
from symengine import Add, S, Symbol, expand, sympify
|
6
|
+
from sympy import Basic
|
7
|
+
from sympy.core.expr import Expr
|
8
|
+
from sympy.core.kind import NumberKind
|
9
|
+
from sympy.printing.str import StrPrinter
|
10
|
+
|
11
|
+
import schubmult.rings._quantum_schubert_polynomial_ring as qsr
|
12
|
+
import schubmult.rings._tensor_schub_ring as tsr
|
13
|
+
import schubmult.rings._utils as utils
|
14
|
+
import schubmult.schub_lib.double as yz
|
15
|
+
|
16
|
+
# from schubmult.poly_lib.schub_poly import pull_out_var
|
17
|
+
import schubmult.schub_lib.schub_lib as schub_lib
|
18
|
+
import schubmult.schub_lib.single as py
|
19
|
+
from schubmult.perm_lib import Permutation, inv
|
20
|
+
from schubmult.poly_lib.poly_lib import elem_sym_poly, xreplace_genvars
|
21
|
+
from schubmult.poly_lib.schub_poly import schubpoly_classical_from_elems, schubpoly_from_elems
|
22
|
+
from schubmult.poly_lib.variables import CustomGeneratingSet, GeneratingSet, GeneratingSet_base, MaskedGeneratingSet
|
23
|
+
from schubmult.utils.logging import get_logger
|
24
|
+
from schubmult.utils.perm_utils import add_perm_dict
|
25
|
+
|
26
|
+
## EMULATE POLYTOOLS
|
27
|
+
|
28
|
+
_def_printer = StrPrinter({"order": "none"})
|
29
|
+
# _def_printer = StrPrinter()
|
30
|
+
|
31
|
+
logger = get_logger(__name__)
|
32
|
+
|
33
|
+
# numpy arrays
|
34
|
+
# sympy parsing
|
35
|
+
# quantum
|
36
|
+
|
37
|
+
# COPRODUCT
|
38
|
+
|
39
|
+
|
40
|
+
class NotEnoughGeneratorsError(ValueError):
|
41
|
+
pass
|
42
|
+
|
43
|
+
|
44
|
+
def _varstr(v):
|
45
|
+
if v == utils.NoneVar:
|
46
|
+
return "NoneVar"
|
47
|
+
if v == utils.ZeroVar:
|
48
|
+
return "0"
|
49
|
+
return f"'{v}'"
|
50
|
+
|
51
|
+
|
52
|
+
def domul(t1, dict2):
|
53
|
+
_vstr, kd, vd, basis, best_effort_positive = t1
|
54
|
+
this_dict = {}
|
55
|
+
for k, v in dict2:
|
56
|
+
did_positive = False
|
57
|
+
to_mul = v * vd
|
58
|
+
if best_effort_positive:
|
59
|
+
try:
|
60
|
+
# logger.critical(f"{to_mul=} {kd=} {k=}")
|
61
|
+
this_dict = add_perm_dict(this_dict, {k1: v1 * to_mul for k1, v1 in basis.cached_positive_product(kd, k[0], _vstr, k[1]).items()})
|
62
|
+
did_positive = True
|
63
|
+
except Exception:
|
64
|
+
# logger.debug("Failed to compute")
|
65
|
+
did_positive = False
|
66
|
+
if not did_positive:
|
67
|
+
this_dict = add_perm_dict(this_dict, {k1: v1 * to_mul for k1, v1 in basis.cached_product(kd, k[0], _vstr, k[1]).items()})
|
68
|
+
return this_dict
|
69
|
+
|
70
|
+
|
71
|
+
def _mul_schub_dicts(dict1, dict2, basis, best_effort_positive=True):
|
72
|
+
by_var = {}
|
73
|
+
|
74
|
+
none_dict = {}
|
75
|
+
for k, v in dict1.items():
|
76
|
+
if k[1] == utils.NoneVar: # or k[1] == utils.ZeroVar:
|
77
|
+
none_dict[k[0]] = v
|
78
|
+
else:
|
79
|
+
if k[1] not in by_var:
|
80
|
+
by_var[k[1]] = {}
|
81
|
+
by_var[k[1]][k[0]] = v
|
82
|
+
|
83
|
+
results = {}
|
84
|
+
# import sys
|
85
|
+
|
86
|
+
for _vstr, _dict in by_var.items():
|
87
|
+
if BasisSchubertAlgebraElement.do_parallel:
|
88
|
+
result_list = []
|
89
|
+
mul_funcs = [(_vstr, kd, vd, basis, best_effort_positive) for kd, vd in _dict.items()]
|
90
|
+
itemlist = list(dict2.items())
|
91
|
+
|
92
|
+
def add_result(result):
|
93
|
+
result_list.append(result)
|
94
|
+
|
95
|
+
with multiprocessing.Pool(processes=6) as pool:
|
96
|
+
for mulf in mul_funcs:
|
97
|
+
pool.apply_async(domul, args=(mulf, itemlist), callback=add_result)
|
98
|
+
pool.close()
|
99
|
+
pool.join()
|
100
|
+
for res in result_list:
|
101
|
+
results = add_perm_dict(results, res)
|
102
|
+
else:
|
103
|
+
this_dict = {}
|
104
|
+
for k, v in dict2.items():
|
105
|
+
for kd, vd in _dict.items():
|
106
|
+
did_positive = False
|
107
|
+
to_mul = v * vd
|
108
|
+
if best_effort_positive:
|
109
|
+
try:
|
110
|
+
# logger.critical(f"{to_mul=} {kd=} {k=}")
|
111
|
+
this_dict = add_perm_dict(this_dict, {k1: v1 * to_mul for k1, v1 in basis.cached_positive_product(kd, k[0], _vstr, k[1]).items()})
|
112
|
+
did_positive = True
|
113
|
+
except Exception:
|
114
|
+
# logger.debug("Failed to compute")
|
115
|
+
did_positive = False
|
116
|
+
if not did_positive:
|
117
|
+
this_dict = add_perm_dict(this_dict, {k1: v1 * to_mul for k1, v1 in basis.cached_product(kd, k[0], _vstr, k[1]).items()})
|
118
|
+
results = add_perm_dict(results, this_dict)
|
119
|
+
|
120
|
+
by_var2 = {}
|
121
|
+
none_dict2 = {}
|
122
|
+
for k, v in dict2.items():
|
123
|
+
if k[1] == utils.NoneVar: # or k[1] == utils.ZeroVar:
|
124
|
+
none_dict2[k[0]] = v
|
125
|
+
else:
|
126
|
+
if k[1] not in by_var2:
|
127
|
+
by_var2[k[1]] = {}
|
128
|
+
by_var2[k[1]][k[0]] = v
|
129
|
+
|
130
|
+
for _vstr, _dict in by_var2.items():
|
131
|
+
this_dict = {}
|
132
|
+
for k, v in none_dict.items():
|
133
|
+
if not best_effort_positive:
|
134
|
+
this_dict = add_perm_dict(this_dict, {(k1, _vstr): v1 * v for k1, v1 in basis.double_mul(_dict, k, utils.poly_ring(_vstr), utils.poly_ring(utils.NoneVar)).items()})
|
135
|
+
else:
|
136
|
+
this_dict = add_perm_dict(this_dict, {(k1, _vstr): expand(v1) * v for k1, v1 in basis.double_mul(_dict, k, utils.poly_ring(_vstr), utils.poly_ring(utils.NoneVar)).items()})
|
137
|
+
results = add_perm_dict(results, this_dict)
|
138
|
+
|
139
|
+
none_dict, none_dict2 = sorted([none_dict, none_dict2], key=lambda x: -len(x.keys()))
|
140
|
+
for k, v in none_dict2.items():
|
141
|
+
results = add_perm_dict(results, {(k1, utils.NoneVar): v1 * v for k1, v1 in basis.single_mul(none_dict, k).items()})
|
142
|
+
|
143
|
+
return results
|
144
|
+
|
145
|
+
|
146
|
+
class BasisSchubertAlgebraElement(Expr):
|
147
|
+
_op_priority = 1e200
|
148
|
+
_kind = NumberKind
|
149
|
+
is_commutative = False
|
150
|
+
# precedence = 40
|
151
|
+
do_parallel = False
|
152
|
+
|
153
|
+
def __new__(cls, _dict, basis):
|
154
|
+
obj = Expr.__new__(cls)
|
155
|
+
obj._dict = {k: sympify(v) for k, v in _dict.items() if expand(v) != S.Zero}
|
156
|
+
if len(obj._dict.keys()) == 1 and next(iter(obj._dict.values())) == S.One:
|
157
|
+
obj.precedence = 1000
|
158
|
+
else:
|
159
|
+
obj.precedence = 40
|
160
|
+
# obj.prune()
|
161
|
+
obj._basis = basis
|
162
|
+
return obj
|
163
|
+
|
164
|
+
# 217 per night
|
165
|
+
# 569 per night
|
166
|
+
@property
|
167
|
+
def args(self):
|
168
|
+
return (sympy.Dict(self._dict), self._basis)
|
169
|
+
|
170
|
+
@property
|
171
|
+
def coeff_dict(self):
|
172
|
+
return self._dict
|
173
|
+
|
174
|
+
@property
|
175
|
+
def genset(self):
|
176
|
+
return self.basis.genset
|
177
|
+
|
178
|
+
@property
|
179
|
+
def basis(self):
|
180
|
+
return self._basis
|
181
|
+
|
182
|
+
def _hashable_content(self):
|
183
|
+
return self.args
|
184
|
+
|
185
|
+
# def prune(self):
|
186
|
+
# keys = list(self._dict.keys())
|
187
|
+
# for k in keys:
|
188
|
+
# if expand(self._dict[k]) == S.Zero:
|
189
|
+
# del self._dict[k]
|
190
|
+
# return self
|
191
|
+
|
192
|
+
def mult_poly(self, poly):
|
193
|
+
res_dict2 = {}
|
194
|
+
# poly = self.genset[i + 1] - self.genset[i]
|
195
|
+
for k, v in self.coeff_dict.items():
|
196
|
+
if k[1] == utils.ZeroVar or k[1] == utils.NoneVar:
|
197
|
+
dict2 = self.basis.mult_poly_single({k[0]: v}, poly, self.genset)
|
198
|
+
else:
|
199
|
+
dict2 = self.basis.mult_poly_double({k[0]: v}, poly, self.genset, utils.poly_ring(k[1]))
|
200
|
+
res_dict2 = add_perm_dict(res_dict2, {(k2, k[1]): v for k2, v in dict2.items()})
|
201
|
+
logger.debug(f"{res_dict2=}")
|
202
|
+
return self.basis._from_dict(res_dict2)
|
203
|
+
|
204
|
+
# def _cached_sympystr(self, printer):
|
205
|
+
# return printer.doprint(
|
206
|
+
# sympy.Add(
|
207
|
+
# *[
|
208
|
+
# (self.coeff_dict[k] if k[0] == Permutation([]) else sympy.Mul(self.coeff_dict[k], self.basis.single_element_class(k, self.basis)))
|
209
|
+
# for k in sorted(self.coeff_dict.keys(), key=lambda bob: (inv(bob[0]), str(bob[1]), *bob[0]))
|
210
|
+
# ],
|
211
|
+
# ),
|
212
|
+
# )
|
213
|
+
|
214
|
+
def _sympystr(self, printer):
|
215
|
+
return printer._print_Add(self)
|
216
|
+
# return self._cached_sympystr(printer)
|
217
|
+
|
218
|
+
def as_terms(self):
|
219
|
+
if len(self.coeff_dict.keys()) == 0:
|
220
|
+
return [sympy.sympify(S.Zero)]
|
221
|
+
return [
|
222
|
+
(sympy.sympify(self.coeff_dict[k]) if k[0] == Permutation([]) else sympy.Mul(self.coeff_dict[k], self.basis.single_element_class(k, self.basis)))
|
223
|
+
for k in sorted(self.coeff_dict.keys(), key=lambda bob: (inv(bob[0]), str(bob[1]), *bob[0]))
|
224
|
+
]
|
225
|
+
|
226
|
+
def as_ordered_terms(self, *_, **__):
|
227
|
+
return self.as_terms()
|
228
|
+
|
229
|
+
# def _eval_simplify(self, *args, measure, **kwargs):
|
230
|
+
# return self.basis._from_dict({k: sympify(sympy.simplify(v, *args, measure=measure, **kwargs)) for k, v in self.coeff_dict.items()})
|
231
|
+
|
232
|
+
# def __iadd__(self, other):
|
233
|
+
# return self.__add__(other)
|
234
|
+
|
235
|
+
def __add__(self, other):
|
236
|
+
# if isinstance(self)
|
237
|
+
# # logger.debug(f"{type(other)=} {self.genset=}")
|
238
|
+
try:
|
239
|
+
other = self.basis(other)
|
240
|
+
except Exception:
|
241
|
+
return sympify(other) + self.as_polynomial()
|
242
|
+
return self.basis._from_dict(add_perm_dict(self.coeff_dict, other.coeff_dict))
|
243
|
+
|
244
|
+
def __radd__(self, other):
|
245
|
+
# logger.debug(f"{type(other)=}")
|
246
|
+
try:
|
247
|
+
other = self.basis(other)
|
248
|
+
except Exception:
|
249
|
+
# logger.debug(f"{other=} {list(self.genset)=}")
|
250
|
+
return sympify(other) + self.as_polynomial()
|
251
|
+
return self.basis._from_dict(add_perm_dict(other.coeff_dict, self.coeff_dict))
|
252
|
+
|
253
|
+
def __sub__(self, other):
|
254
|
+
# logger.debug(f"{type(other)=}")
|
255
|
+
try:
|
256
|
+
other = self.basis(other)
|
257
|
+
except Exception:
|
258
|
+
# logger.debug(f"{other=} {list(self.genset)=}")
|
259
|
+
return self.as_polynomial() - sympify(other)
|
260
|
+
double_dict = add_perm_dict(self.coeff_dict, {k: -v for k, v in other.coeff_dict.items()})
|
261
|
+
return self.basis._from_dict(double_dict)
|
262
|
+
|
263
|
+
def __rsub__(self, other):
|
264
|
+
# logger.debug(f"{type(other)=}")
|
265
|
+
try:
|
266
|
+
other = self.basis(other)
|
267
|
+
except Exception:
|
268
|
+
# logger.debug(f"{other=} {list(self.genset)=}")
|
269
|
+
return sympify(other) - self.as_polynomial()
|
270
|
+
double_dict = add_perm_dict(other.coeff_dict, {k: -v for k, v in self.coeff_dict.items()})
|
271
|
+
return self.basis._from_dict(double_dict)
|
272
|
+
|
273
|
+
def __neg__(self):
|
274
|
+
elem = self
|
275
|
+
if self.is_Add or self.is_Mul:
|
276
|
+
elem = self.doit()
|
277
|
+
double_dict = {k: -sympify(v) for k, v in elem.coeff_dict.items()}
|
278
|
+
return self.basis._from_dict(double_dict)
|
279
|
+
|
280
|
+
def __mul__(self, other):
|
281
|
+
try:
|
282
|
+
o = sympify(other)
|
283
|
+
return self.mult_poly(o)
|
284
|
+
except Exception:
|
285
|
+
try:
|
286
|
+
other = self.basis(other)
|
287
|
+
return self.basis._from_dict(_mul_schub_dicts(self.coeff_dict, other.coeff_dict, self.basis))
|
288
|
+
except Exception:
|
289
|
+
return self.as_polynomial() * sympify(other)
|
290
|
+
|
291
|
+
def __rmul__(self, other):
|
292
|
+
# logger.debug(f"{type(other)=}")
|
293
|
+
try:
|
294
|
+
o = sympify(other)
|
295
|
+
return self.mult_poly(o)
|
296
|
+
except Exception:
|
297
|
+
try:
|
298
|
+
other = self.basis(other)
|
299
|
+
return self.basis._from_dict(_mul_schub_dicts(other.coeff_dict, self.coeff_dict, self.basis))
|
300
|
+
except Exception:
|
301
|
+
return self.as_polynomial() * sympify(other)
|
302
|
+
|
303
|
+
# def equals(self, other):
|
304
|
+
# return self.__eq__(other)
|
305
|
+
|
306
|
+
# def test_equality(self, other, disp=False):
|
307
|
+
# elem1 = self
|
308
|
+
# elem2 = other
|
309
|
+
# done = set()
|
310
|
+
# import sys
|
311
|
+
|
312
|
+
# for k, v in elem1.coeff_dict.items():
|
313
|
+
# done.add(k)
|
314
|
+
# if expand(v - elem2.coeff_dict.get(k, 0)) != 0:
|
315
|
+
# if disp:
|
316
|
+
# # print(f"{k=} {v=} {elem2.coeff_dict.get(k, 0)=} {expand(v - elem2.coeff_dict.get(k, 0))=}", file=sys.stderr)
|
317
|
+
# return False
|
318
|
+
# for k, v in elem2.coeff_dict.items():
|
319
|
+
# if k in done:
|
320
|
+
# continue
|
321
|
+
# if expand(v - elem1.coeff_dict.get(k, 0)) != 0:
|
322
|
+
# if disp:
|
323
|
+
# # print(f"{k=} {v=} {expand(v - elem1.coeff_dict.get(k, 0))=}", file=sys.stderr)
|
324
|
+
# return False
|
325
|
+
# return True
|
326
|
+
|
327
|
+
# def __eq__(self, other):
|
328
|
+
# if self.is_Add or self.is_Mul:
|
329
|
+
# return self.doit().equals(other)
|
330
|
+
# cv = "y"
|
331
|
+
# elem1 = self
|
332
|
+
# elem2 = other
|
333
|
+
|
334
|
+
# if not elem1.test_equality(elem2):
|
335
|
+
# elem1_o = elem1.change_vars(cv)
|
336
|
+
# elem2_o = elem2.change_vars(cv)
|
337
|
+
# return elem1_o.test_equality(elem2_o)
|
338
|
+
# return True
|
339
|
+
# assert all([k[1] == cv for k in elem1.coeff_dict.keys()])
|
340
|
+
# assert all([k[1] == cv for k in elem2.coeff_dict.keys()])
|
341
|
+
|
342
|
+
# def __str__(self):
|
343
|
+
# pieces = []
|
344
|
+
# keys = list(self.coeff_dict.keys())
|
345
|
+
# for k in sorted(keys, key=lambda b: (inv(b[0]), b[1], *b[0])):
|
346
|
+
# v = self.coeff_dict[k]
|
347
|
+
# dvar = "D"
|
348
|
+
# if sympy.expand(v) != 0:
|
349
|
+
# pieces += [
|
350
|
+
# sympy.Mul(
|
351
|
+
# v,
|
352
|
+
# sympy.Symbol(
|
353
|
+
# f"{dvar}S{DSx._base_var}({list(k[0])}, {_varstr(k[1])})",
|
354
|
+
# commutative=False,
|
355
|
+
# )
|
356
|
+
# if k[0] != Permutation([])
|
357
|
+
# else 1,
|
358
|
+
# ),
|
359
|
+
# ]
|
360
|
+
# return sympy.sstr(sympy.Add(*pieces, evaluate=False), order="none")
|
361
|
+
|
362
|
+
# def __repr__(self):
|
363
|
+
# return str(self)
|
364
|
+
# def _as_ordered_terms(self, *args, **kwargs):
|
365
|
+
|
366
|
+
@cache
|
367
|
+
def change_vars(self, cv):
|
368
|
+
# result = {}
|
369
|
+
# fix
|
370
|
+
# for k, v in self.coeff_dict.items():
|
371
|
+
# result = add_perm_dict(result, {k1: v1 * v for k1, v1 in self.basis.cached_positive_product(Permutation([]), k[0], cv, k[1]).items()})
|
372
|
+
# # result = {(k, cv): v for k, v in self.basis.mul_double(Permutation([]),utils.poly_ring)}
|
373
|
+
# return self.basis._from_dict(result)
|
374
|
+
return self.basis([], cv) * self
|
375
|
+
|
376
|
+
def as_coefficients_dict(self):
|
377
|
+
return sympy.Dict({self.basis.single_element_class(k, self.basis): sympy.sympify(v) for k, v in self.coeff_dict.items()})
|
378
|
+
|
379
|
+
def expand(self, deep=True, *args, **kwargs): # noqa: ARG002
|
380
|
+
if not deep:
|
381
|
+
return self.basis._from_dict({k: expand(v) for k, v in self.coeff_dict.items()})
|
382
|
+
return sympy.sympify(expand(sympify(self.as_polynomial())))
|
383
|
+
|
384
|
+
def as_polynomial(self):
|
385
|
+
return sympy.sympify(Add(*[v * self.basis.cached_schubpoly(k) for k, v in self.coeff_dict.items()]))
|
386
|
+
|
387
|
+
def as_classical(self):
|
388
|
+
return self.basis.in_classical_basis(self)
|
389
|
+
|
390
|
+
def as_quantum(self):
|
391
|
+
return self.basis.in_quantum_basis(self)
|
392
|
+
|
393
|
+
|
394
|
+
class DoubleSchubertAlgebraElement(BasisSchubertAlgebraElement):
|
395
|
+
"""Algebra with sympy coefficients
|
396
|
+
and a dict basis
|
397
|
+
"""
|
398
|
+
|
399
|
+
# __slots__ = ("_dict", "_parent")
|
400
|
+
# is_polynomial = True
|
401
|
+
|
402
|
+
# default_coeff_var = "y"
|
403
|
+
|
404
|
+
def __new__(cls, _dict, basis):
|
405
|
+
return BasisSchubertAlgebraElement.__new__(cls, _dict, basis)
|
406
|
+
|
407
|
+
def divdiff(self, i):
|
408
|
+
return self.basis._from_dict({(k[0].swap(i - 1, i), k[1]): v for k, v in self.coeff_dict.items() if i - 1 in k[0].descents()})
|
409
|
+
|
410
|
+
def simpleref(self, i):
|
411
|
+
return self + self.divdiff(i).mult_poly(self.genset[i + 1] - self.genset[i])
|
412
|
+
|
413
|
+
def act(self, perm):
|
414
|
+
perm = Permutation(perm)
|
415
|
+
dset = perm.descents()
|
416
|
+
if len(dset) == 0:
|
417
|
+
return self
|
418
|
+
i = next(iter(dset))
|
419
|
+
return self.simpleref(i + 1).act(perm.swap(i, i + 1))
|
420
|
+
|
421
|
+
def max_index(self):
|
422
|
+
return max([max([0, *list(k[0].descents(zero_indexed=False))]) for k in self.coeff_dict.keys()])
|
423
|
+
|
424
|
+
def subs(self, old, new):
|
425
|
+
result = 0
|
426
|
+
if self.genset.index(old) != -1:
|
427
|
+
result = 0
|
428
|
+
index = self.genset.index(old)
|
429
|
+
mindex = self.max_index()
|
430
|
+
if mindex < index:
|
431
|
+
return self
|
432
|
+
# if already equal to the max index, we don't want to move it over
|
433
|
+
perm = Permutation([]).swap(index - 1, mindex) # index to max index + 1
|
434
|
+
# logger.debug(f"{mindex=}")
|
435
|
+
# logger.debug(f"{perm=}")
|
436
|
+
transf = self.act(perm)
|
437
|
+
# # logger.debug(f"{transf=}")
|
438
|
+
# # logger.debug(f"{self.expand()=}")
|
439
|
+
# # logger.debug(f"{transf.expand().expand()=}")
|
440
|
+
# transf2 = transf.coproduct([i for i in range(1,self.max_index()+1)],coeff_var=utils.NoneVar)
|
441
|
+
# # logger.debug(f"{transf2=}")
|
442
|
+
# for (k1, k2), v in transf2.coeff_dict.items():
|
443
|
+
# result += self.basis._from_dict({k1: v}) * (new**k2[0].inv)
|
444
|
+
# don't want to go nuts
|
445
|
+
# res_dict = {}
|
446
|
+
for k, v in transf.coeff_dict.items():
|
447
|
+
perm = k[0]
|
448
|
+
coeff_var = k[1]
|
449
|
+
coeff_gens = utils.poly_ring(coeff_var)
|
450
|
+
# cached mul_poly
|
451
|
+
L = schub_lib.pull_out_var(mindex + 1, perm)
|
452
|
+
# # logger.debug(f"{perm=} {L=}")
|
453
|
+
for index_list, new_perm in L:
|
454
|
+
result += self.basis._from_dict({(new_perm, k[1]): v}).mult_poly(sympy.prod([(new - coeff_gens[index2]) for index2 in index_list]))
|
455
|
+
return result
|
456
|
+
|
457
|
+
for k, v in self.coeff_dict.items():
|
458
|
+
if k[1] == utils.ZeroVar or k[1] == utils.NoneVar:
|
459
|
+
add_dict = {k: v.subs(old, new)}
|
460
|
+
else:
|
461
|
+
coeff_genset = utils.poly_ring(k[1])
|
462
|
+
if coeff_genset.index(old) != -1:
|
463
|
+
genset_list = [coeff_genset[i] for i in range(len(coeff_genset))]
|
464
|
+
genset_list[coeff_genset.index(old)] = 0
|
465
|
+
custom_genset = CustomGeneratingSet(genset_list)
|
466
|
+
new_add_dict = {k2: sympify(v2).subs(old, new) for k2, v2 in yz.schubmult_double({(): v}, k[0], custom_genset, coeff_genset).items()} # remove the variable
|
467
|
+
add_dict = {}
|
468
|
+
for k3, v3 in new_add_dict.items():
|
469
|
+
# convert back to coeff_genset
|
470
|
+
to_add_dict = {(k4, k[1]): v4 for k4, v4 in yz.schubmult_double({(): v3}, k3, coeff_genset, custom_genset).items()}
|
471
|
+
add_dict = add_perm_dict(add_dict, to_add_dict)
|
472
|
+
else:
|
473
|
+
add_dict = {k: sympify(v).subs(old, new)}
|
474
|
+
for k5, v5 in add_dict.items():
|
475
|
+
if any(self.genset.index(s) != -1 for s in sympify(v5).free_symbols):
|
476
|
+
result += self.basis._from_dict({k5: 1}).mult_poly(v5)
|
477
|
+
else:
|
478
|
+
result += self.basis._from_dict({k5: v5})
|
479
|
+
# check correct, change vars to zeroed coeff var for coeff
|
480
|
+
return result
|
481
|
+
|
482
|
+
# for k, v in self.coeff_dict.items():
|
483
|
+
# # can permute it to the end and substitute
|
484
|
+
# perm = k[0]
|
485
|
+
# coeff_var = k[1]
|
486
|
+
|
487
|
+
@property
|
488
|
+
def free_symbols(self):
|
489
|
+
ret = set()
|
490
|
+
for k, v in self.coeff_dict.items():
|
491
|
+
ret.update(v.free_symbols)
|
492
|
+
perm = k[0]
|
493
|
+
coeff_var = k[1]
|
494
|
+
if len(perm.descents()) > 0:
|
495
|
+
ret.update([self.genset[i] for i in range(1, max(perm.descents()) + 2)])
|
496
|
+
if coeff_var != utils.NoneVar and coeff_var != utils.ZeroVar:
|
497
|
+
genset2 = utils.poly_ring(coeff_var)
|
498
|
+
perm2 = ~perm
|
499
|
+
if len(perm2.descents()) > 0:
|
500
|
+
ret.update([genset2[i] for i in range(1, max(perm2.descents()) + 2)])
|
501
|
+
return ret
|
502
|
+
|
503
|
+
# def _eval_Eq(self, other):
|
504
|
+
# # this will prevent sympy from acting like an idiot
|
505
|
+
# return self.__eq__(other)
|
506
|
+
|
507
|
+
# def _eval_subs(self, old, new):
|
508
|
+
# b_old = sympify(old)
|
509
|
+
# b_new = sympify(new)
|
510
|
+
# result = {}
|
511
|
+
# stuff_to_do = False
|
512
|
+
# lots_of_stuff_to_do = False
|
513
|
+
# if b_new in utils.poly_ring(self._base_var):
|
514
|
+
# stuff_to_do = True
|
515
|
+
# if b_old in utils.poly_ring(self._base_var):
|
516
|
+
# lots_of_stuff_to_do = True
|
517
|
+
# for k, v in self.coeff_dict.items():
|
518
|
+
# if lots_of_stuff_to_do:
|
519
|
+
# poley = sympify(self.basis._from_dict({k: 1}).change_vars(0).expand() * v)
|
520
|
+
# if b_old in poley.free_symbols:
|
521
|
+
# poley = poley.subs(b_old, b_new)
|
522
|
+
# new_dict = yz.mult_poly_double({(1, 2): 1}, poley, utils.poly_ring(self._base_var), utils.poly_ring(k[1]))
|
523
|
+
# new_p = {(koifle, k[1]): voifle for koifle, voifle in new_dict.items()}
|
524
|
+
# result = add_perm_dict(result, new_p)
|
525
|
+
# elif stuff_to_do:
|
526
|
+
# this_p = self.basis._from_dict({k: v}).change_vars(0)
|
527
|
+
# for kkk, vvv in this_p.coeff_dict.items():
|
528
|
+
# vvvv = sympify(vvv).subs(b_old, b_new)
|
529
|
+
# if b_new in sympify(vvvv).free_symbols:
|
530
|
+
# s_dict = {kkk[0]: 1}
|
531
|
+
# r_dict = py.mult_poly_py(s_dict, vvvv, utils.poly_ring(self._base_var))
|
532
|
+
# else:
|
533
|
+
# r_dict = {kkk[0]: vvvv}
|
534
|
+
# r_dict = {(kk, 0): voif for kk, voif in r_dict.items()}
|
535
|
+
# new_p = self.basis._from_dict(r_dict).change_vars(k[1])
|
536
|
+
# result = add_perm_dict(result, new_p.coeff_dict)
|
537
|
+
# else:
|
538
|
+
# result[k] = result.get(k, 0) + sympify(v).subs(b_old, b_new)
|
539
|
+
# return self.basis._from_dict(result)
|
540
|
+
|
541
|
+
def coproduct(self, indices, coeff_var="y", gname1=None, gname2=None):
|
542
|
+
result_dict = {}
|
543
|
+
if gname1 is None:
|
544
|
+
gname1 = f"{self.genset.label}_A"
|
545
|
+
if gname2 is None:
|
546
|
+
gname2 = f"{self.genset.label}_B"
|
547
|
+
gens2 = MaskedGeneratingSet(self.genset, indices)
|
548
|
+
# logger.debug(f"{indices=}")
|
549
|
+
gens1 = gens2.complement()
|
550
|
+
# logger.debug(f"{gens1.index_mask=}")
|
551
|
+
# logger.debug(f"{list(gens1)=}")
|
552
|
+
# logger.debug(f"{gens2.index_mask=}")
|
553
|
+
# logger.debug(f"{list(gens2)=}")
|
554
|
+
gens1.set_label(gname1)
|
555
|
+
gens2.set_label(gname2)
|
556
|
+
for k, v in self.coeff_dict.items():
|
557
|
+
key = k[0]
|
558
|
+
var_str = k[1]
|
559
|
+
# print(f"{var_str=}")
|
560
|
+
# print(f"{coeff_var=}")
|
561
|
+
if var_str in (utils.NoneVar, utils.ZeroVar) and coeff_var in (utils.NoneVar, utils.ZeroVar):
|
562
|
+
coprod_dict = py.schub_coprod_py(key, indices)
|
563
|
+
else:
|
564
|
+
coprod_dict = yz.schub_coprod_double(key, indices, utils.poly_ring(var_str), utils.poly_ring(coeff_var))
|
565
|
+
# print(f"{coprod_dict=}")
|
566
|
+
result_dict = add_perm_dict(result_dict, {((k1, var_str), (k2, coeff_var)): v * v2 for (k1, k2), v2 in coprod_dict.items()})
|
567
|
+
basis = tsr.TensorAlgebraBasis(DoubleSchubertAlgebraElement_basis(gens1), DoubleSchubertAlgebraElement_basis(gens2))
|
568
|
+
return basis._from_dict(result_dict)
|
569
|
+
|
570
|
+
# def normalize_coefficients(self, coeff_var):
|
571
|
+
# return DSx([1, 2], coeff_var) * self
|
572
|
+
|
573
|
+
# def expand(self, *_a, **_):
|
574
|
+
# if isinstance(self, SchubAdd):
|
575
|
+
# return self.doit().expand()
|
576
|
+
# if isinstance(self, SchubMul):
|
577
|
+
# return self.doit().expand()
|
578
|
+
# return expand(Add(*[v * schubpoly(k[0], self.genset, utils.poly_ring(k[1])) for k, v in self.coeff_dict.items()]))
|
579
|
+
|
580
|
+
def in_SEM_basis(self):
|
581
|
+
result = S.Zero
|
582
|
+
for k, v in self.coeff_dict.items():
|
583
|
+
result += v * schubpoly_from_elems(k[0], self.genset, utils.poly_ring(k[1]), elem_func=self.basis.symbol_elem_func)
|
584
|
+
# print(f"{result=}")
|
585
|
+
# gens = []
|
586
|
+
# for k in range(1, 10):
|
587
|
+
# gens += [sympy.Symbol(f"e_{p}_{k}") for p in range(1,k+1)]
|
588
|
+
# #print(f"{gens=}")
|
589
|
+
# ply = sympy.poly(sympy.sympify(expand(result)), *gens)
|
590
|
+
# floss = 0
|
591
|
+
# for m, c in ply.as_dict().items():
|
592
|
+
# floss += c * sympy.prod([gens[i]**m[i] for i in range(len(m))])
|
593
|
+
# return floss
|
594
|
+
return result
|
595
|
+
|
596
|
+
@cached_property
|
597
|
+
def max_gens(self):
|
598
|
+
return max([max(k[0].descents()) for k in self.coeff_dict.keys()])
|
599
|
+
|
600
|
+
|
601
|
+
# Atomic Schubert polynomial
|
602
|
+
class DSchubPoly(DoubleSchubertAlgebraElement):
|
603
|
+
is_Atom = True
|
604
|
+
|
605
|
+
def __new__(cls, k, basis):
|
606
|
+
return DSchubPoly.__xnew_cached__(cls, k, basis)
|
607
|
+
|
608
|
+
@staticmethod
|
609
|
+
def __xnew__(_class, k, basis):
|
610
|
+
_coeff_dict = sympy.Dict({(Permutation(k[0]), k[1]): 1})
|
611
|
+
# if not isinstance(genset, GeneratingSet_base):
|
612
|
+
# raise TypeError
|
613
|
+
obj = DoubleSchubertAlgebraElement.__new__(_class, _coeff_dict, basis)
|
614
|
+
obj._key = k
|
615
|
+
obj._genset = basis.genset
|
616
|
+
obj._coeff_dict = _coeff_dict
|
617
|
+
obj._basis = basis
|
618
|
+
return obj
|
619
|
+
|
620
|
+
# @property
|
621
|
+
# def coeff_dict(self):
|
622
|
+
# return self._coeff_dict
|
623
|
+
|
624
|
+
@property
|
625
|
+
def perm(self):
|
626
|
+
return self._key[0]
|
627
|
+
|
628
|
+
@property
|
629
|
+
def args(self):
|
630
|
+
return (sympy.Tuple(*self._key), self._basis)
|
631
|
+
|
632
|
+
@staticmethod
|
633
|
+
@cache
|
634
|
+
def __xnew_cached__(_class, k, basis):
|
635
|
+
return DSchubPoly.__xnew__(_class, k, basis)
|
636
|
+
|
637
|
+
def _sympystr(self, printer):
|
638
|
+
if self._key[0] == Permutation([]):
|
639
|
+
return printer.doprint(1)
|
640
|
+
if self._key[1] == 0 or self._key[1] == utils.NoneVar:
|
641
|
+
return printer.doprint(f"S{self.genset.label}({printer.doprint(self._key[0])})")
|
642
|
+
return printer.doprint(f"DS{self.genset.label}({printer.doprint(self._key[0])}, {_varstr(self._key[1])})")
|
643
|
+
|
644
|
+
|
645
|
+
# def elem_func(p, k, vx, vy):
|
646
|
+
# return DSx(elem_func_q(p, k, vx, vy), "y")
|
647
|
+
|
648
|
+
# A = schubpoly_from_elems([4,1,3,2], DSx.genset, poly_ring("y"),elem_func)
|
649
|
+
|
650
|
+
|
651
|
+
# None is faster to store
|
652
|
+
class DoubleSchubertAlgebraElement_basis(Basic):
|
653
|
+
def __new__(cls, genset):
|
654
|
+
return Basic.__new__(cls, genset)
|
655
|
+
|
656
|
+
@property
|
657
|
+
def symbol_elem_func(self):
|
658
|
+
def elem_func(p, k, varl1, varl2):
|
659
|
+
if p == 0 and k >= 0:
|
660
|
+
return 1
|
661
|
+
if p < 0 or p > k:
|
662
|
+
return 0
|
663
|
+
return sympy.Add(*[(Symbol(f"e_{p - i}_{k}") if p - i > 0 else 1) * elem_sym_poly(i, k + 1 - p, [-v for v in varl2], [0 for a in varl1]) for i in range(p + 1)])
|
664
|
+
|
665
|
+
return elem_func
|
666
|
+
|
667
|
+
# def in_SEM_basis(self, elem):
|
668
|
+
# return
|
669
|
+
|
670
|
+
@property
|
671
|
+
def genset(self):
|
672
|
+
return self.args[0]
|
673
|
+
|
674
|
+
def _from_dict(self, _dict):
|
675
|
+
return DoubleSchubertAlgebraElement(_dict, self)
|
676
|
+
|
677
|
+
@property
|
678
|
+
def single_element_class(self):
|
679
|
+
return DSchubPoly
|
680
|
+
|
681
|
+
def in_quantum_basis(self, elem):
|
682
|
+
result = S.Zero
|
683
|
+
for k, v in elem.coeff_dict.items():
|
684
|
+
result += v * self.quantum_schubpoly(k[0], k[1])
|
685
|
+
return result
|
686
|
+
|
687
|
+
def in_classical_basis(self, elem):
|
688
|
+
return elem
|
689
|
+
|
690
|
+
@cache
|
691
|
+
def quantum_schubpoly(self, perm, coeff_var="y"):
|
692
|
+
return schubpoly_classical_from_elems(perm, self.genset, utils.poly_ring(coeff_var), self.quantum_elem_func(coeff_var))
|
693
|
+
|
694
|
+
@cache
|
695
|
+
def cached_product(self, u, v, va, vb):
|
696
|
+
return {(k, va): xreplace_genvars(x, utils.poly_ring(va), utils.poly_ring(vb)) for k, x in yz.schubmult_double_pair_generic(u, v).items()}
|
697
|
+
|
698
|
+
@cache
|
699
|
+
def cached_positive_product(self, u, v, va, vb):
|
700
|
+
return {(k, va): xreplace_genvars(x, utils.poly_ring(va), utils.poly_ring(vb)) for k, x in yz.schubmult_generic_partial_posify(u, v).items()}
|
701
|
+
|
702
|
+
@property
|
703
|
+
def double_mul(self):
|
704
|
+
return yz.schubmult_double
|
705
|
+
|
706
|
+
@property
|
707
|
+
def single_mul(self):
|
708
|
+
return py.schubmult_py
|
709
|
+
|
710
|
+
@property
|
711
|
+
def mult_poly_single(self):
|
712
|
+
return py.mult_poly_py
|
713
|
+
|
714
|
+
@property
|
715
|
+
def mult_poly_double(self):
|
716
|
+
return yz.mult_poly_double
|
717
|
+
|
718
|
+
def quantum_elem_func(self, coeff_var):
|
719
|
+
basis = qsr.QuantumDoubleSchubertAlgebraElement_basis(self.genset)
|
720
|
+
|
721
|
+
def elem_func(p, k, varl1, varl2, xstart=0, ystart=0):
|
722
|
+
if p > k:
|
723
|
+
return basis(0, coeff_var)
|
724
|
+
if p == 0:
|
725
|
+
return basis([], coeff_var)
|
726
|
+
if p == 1:
|
727
|
+
res = basis(varl1[xstart] - varl2[ystart], coeff_var)
|
728
|
+
for i in range(1, k):
|
729
|
+
res += basis(varl1[xstart + i] - varl2[ystart + i], coeff_var)
|
730
|
+
return res
|
731
|
+
if p == k:
|
732
|
+
res = basis((varl1[xstart] - varl2[ystart]) * (varl1[xstart + 1] - varl2[ystart]), coeff_var)
|
733
|
+
for i in range(2, k):
|
734
|
+
res *= basis(varl1[i + xstart] - varl2[ystart], coeff_var)
|
735
|
+
return res
|
736
|
+
mid = k // 2
|
737
|
+
xsm = xstart + mid
|
738
|
+
ysm = ystart + mid
|
739
|
+
kmm = k - mid
|
740
|
+
res = elem_func(p, mid, varl1, varl2, xstart, ystart) + elem_func(
|
741
|
+
p,
|
742
|
+
kmm,
|
743
|
+
varl1,
|
744
|
+
varl2,
|
745
|
+
xsm,
|
746
|
+
ysm,
|
747
|
+
)
|
748
|
+
for p2 in range(max(1, p - kmm), min(p, mid + 1)):
|
749
|
+
res += elem_func(p2, mid, varl1, varl2, xstart, ystart) * elem_func(
|
750
|
+
p - p2,
|
751
|
+
kmm,
|
752
|
+
varl1,
|
753
|
+
varl2,
|
754
|
+
xsm,
|
755
|
+
ysm - p2,
|
756
|
+
)
|
757
|
+
logger.debug(f"{res=}")
|
758
|
+
return res
|
759
|
+
|
760
|
+
return elem_func
|
761
|
+
|
762
|
+
# @cache
|
763
|
+
# def cached_schubpoly_oink(self, u):
|
764
|
+
# return yz.schubpoly(u)
|
765
|
+
|
766
|
+
@cache
|
767
|
+
def cached_schubpoly(self, k):
|
768
|
+
# return yz.schubpoly(u)
|
769
|
+
return schubpoly_classical_from_elems(k[0], self.genset, utils.poly_ring(k[1]), elem_func=elem_sym_poly)
|
770
|
+
|
771
|
+
def __call__(self, x, cv=None):
|
772
|
+
# print(f"frivol {x=} {cv=}")
|
773
|
+
genset = self.genset
|
774
|
+
if not isinstance(genset, GeneratingSet_base):
|
775
|
+
raise TypeError
|
776
|
+
# logger.debug(f"{type(x)=}")
|
777
|
+
# if isinstance(x, Mul) or isinstance(x, Add):
|
778
|
+
# raise TypeError
|
779
|
+
if isinstance(x, list) or isinstance(x, tuple):
|
780
|
+
if cv is None:
|
781
|
+
cv = "y"
|
782
|
+
p_x = Permutation(x)
|
783
|
+
if max([0, *list(p_x.descents())]) > len(self.genset):
|
784
|
+
raise NotEnoughGeneratorsError(f"Not enough generators {p_x=} {len(genset)=}")
|
785
|
+
elem = self._from_dict({(p_x, cv): 1})
|
786
|
+
elif isinstance(x, Permutation):
|
787
|
+
if cv is None:
|
788
|
+
cv = "y"
|
789
|
+
if max([0, *list(x.descents())]) > len(self.genset):
|
790
|
+
raise NotEnoughGeneratorsError(f"Not enough generators {p_x=} {len(genset)=}")
|
791
|
+
elem = self._from_dict({(x, cv): 1})
|
792
|
+
|
793
|
+
elif isinstance(x, DoubleSchubertAlgebraElement):
|
794
|
+
# logger.debug("Line record")
|
795
|
+
if x.is_Add or x.is_Mul:
|
796
|
+
return x.doit()
|
797
|
+
if x.genset == genset:
|
798
|
+
return x
|
799
|
+
raise ValueError("Different generating set")
|
800
|
+
# poly
|
801
|
+
# elif isinstance(x, sympy.Poly):
|
802
|
+
# # eject generators we don't want
|
803
|
+
# if not x.has_only_gens():
|
804
|
+
# x, _ = sympy.poly_from_expr(x.as_expr())
|
805
|
+
# new_gens = [g for g in x.gens if self.genset.index(g) != -1]
|
806
|
+
# # end_gens = [g for g in x.gens if self.genset.index(g) == -1]
|
807
|
+
# if len(new_gens) == 0:
|
808
|
+
# # logger.debug(f"Didn't find any gens in {x=}")
|
809
|
+
# return self(x.as_expr())
|
810
|
+
# new_gens.sort(key=lambda g: self.genset.index(g))
|
811
|
+
# # expand_gens = [self.genset[i] for i in range(self.genset.index(new_gens[-1])+1)] + end_gens
|
812
|
+
# x = sympy.poly(x, gens=tuple(new_gens))
|
813
|
+
# dct = x.as_dict()
|
814
|
+
# result = 0
|
815
|
+
# for monom, coeff in dct.items():
|
816
|
+
# srt_perm = Permutation.sorting_perm([-i for i in monom])
|
817
|
+
# # srt_perm.reverse()
|
818
|
+
# # srt_perm = Permutation(srt_perm)
|
819
|
+
# # print(sorted(monom,reverse=True))
|
820
|
+
# schub_perm = uncode(sorted(monom, reverse=True))
|
821
|
+
# result += self._from_dict({(schub_perm, utils.NoneVar): coeff}).act(srt_perm)
|
822
|
+
# return result
|
823
|
+
else:
|
824
|
+
# logger.debug(f"{x=}")
|
825
|
+
x = sympify(x)
|
826
|
+
if cv is None or cv == utils.NoneVar:
|
827
|
+
cv = utils.NoneVar
|
828
|
+
result = py.mult_poly_py({Permutation([]): 1}, x, genset)
|
829
|
+
else:
|
830
|
+
# print("splinterfish")
|
831
|
+
result = yz.mult_poly_double({Permutation([]): 1}, x, genset, utils.poly_ring(cv))
|
832
|
+
elem = self._from_dict({(k, cv): v for k, v in result.items()})
|
833
|
+
logger.debug(f"Returning {elem=}")
|
834
|
+
return elem
|
835
|
+
|
836
|
+
|
837
|
+
# def _do_schub_mul(a, b):
|
838
|
+
# A = DSx(a)
|
839
|
+
# B = DSx(b)
|
840
|
+
# return self.basis._from_dict(_mul_schub_dicts(A.coeff_dict, B.coeff_dict))
|
841
|
+
|
842
|
+
|
843
|
+
# def _do_schub_add(a, b):
|
844
|
+
# A = DSx(a)
|
845
|
+
# B = DSx(b)
|
846
|
+
# return self.basis._from_dict(add_perm_dict(A.coeff_dict, B.coeff_dict))
|
847
|
+
|
848
|
+
|
849
|
+
# def get_postprocessor(cls):
|
850
|
+
# if cls is Mul:
|
851
|
+
# return lambda expr: SchubMul(*expr.args) # .doit()
|
852
|
+
# if cls is Add:
|
853
|
+
# return lambda expr: SchubAdd(*expr.args) # .doit()
|
854
|
+
# return None
|
855
|
+
|
856
|
+
|
857
|
+
# Basic._constructor_postprocessor_mapping[BasisSchubertAlgebraElement] = {
|
858
|
+
# "Mul": [get_postprocessor(Mul)],
|
859
|
+
# "Add": [get_postprocessor(Add)],
|
860
|
+
# }
|
861
|
+
|
862
|
+
# add.register_handlerclass((Expr, SchubAdd), SchubAdd)
|
863
|
+
# mul.register_handlerclass((Expr, SchubMul), SchubMul)
|
864
|
+
|
865
|
+
|
866
|
+
DoubleSchubertPolynomial = DoubleSchubertAlgebraElement
|
867
|
+
|
868
|
+
|
869
|
+
# class SchubAdd(Add):
|
870
|
+
# is_Add = True
|
871
|
+
|
872
|
+
# def __new__(cls, *args, evaluate=False, _sympify=True, **_):
|
873
|
+
# obj = sympy.Add.__new__(cls, *args, evaluate=evaluate, _sympify=_sympify)
|
874
|
+
# obj._args = args
|
875
|
+
# if evaluate:
|
876
|
+
# return obj.doit()
|
877
|
+
# return obj
|
878
|
+
|
879
|
+
# @property
|
880
|
+
# def args(self):
|
881
|
+
# return self._args
|
882
|
+
|
883
|
+
# def doit(self):
|
884
|
+
# ret = self.args[0]
|
885
|
+
# # logger.debug(f"ADD {self.args=}")
|
886
|
+
# for arg in self.args[1:]:
|
887
|
+
# # logger.debug(f"{arg=} {type(arg)=}")
|
888
|
+
# # logger.debug(f"{ret=} {type(ret)=}")
|
889
|
+
# ret += sympy.expand(arg)
|
890
|
+
# return ret
|
891
|
+
|
892
|
+
# def _sympystr(self, printer):
|
893
|
+
# return printer._print_Add(self)
|
894
|
+
|
895
|
+
# def expand(self, deep=True, *_, **__):
|
896
|
+
# return SchubAdd(*[sympy.expand(arg) for arg in self.args]).doit()
|
897
|
+
|
898
|
+
|
899
|
+
# class SchubMul(sympy.Mul):
|
900
|
+
# is_Mul = True
|
901
|
+
|
902
|
+
# def __new__(cls, *args, evaluate=False, _sympify=True, **_):
|
903
|
+
# # args, a, b = Mul.flatten(list(args))
|
904
|
+
# # if len(args) == 0:
|
905
|
+
# # return 1
|
906
|
+
# obj = Mul.__new__(cls, *args, evaluate=evaluate, _sympify=_sympify)
|
907
|
+
# obj._args = args
|
908
|
+
# if evaluate:
|
909
|
+
# return obj.doit()
|
910
|
+
# return obj
|
911
|
+
|
912
|
+
# @property
|
913
|
+
# def args(self):
|
914
|
+
# return self._args
|
915
|
+
|
916
|
+
# def doit(self):
|
917
|
+
# ret = self.args[0]
|
918
|
+
# # logger.debug(f"MUL {self.args=}")
|
919
|
+
# for arg in self.args[1:]:
|
920
|
+
# # logger.debug(f"{arg=} {type(arg)=}")
|
921
|
+
# ret *= sympy.expand(arg)
|
922
|
+
# return ret
|
923
|
+
|
924
|
+
# def _sympystr(self, printer):
|
925
|
+
# return printer._print_Mul(self)
|
926
|
+
|
927
|
+
# def __neg__(self):
|
928
|
+
# return SchubMul(sympy.Integer(-1), self)
|
929
|
+
|
930
|
+
# def _eval_expand_mul(self, *_, **__):
|
931
|
+
# # logger.debug(f"Pringles {self.args=}")
|
932
|
+
# return SchubMul(*[sympy.expand(arg) for arg in self.args]).doit()
|
933
|
+
|
934
|
+
|
935
|
+
# Basic._constructor_postprocessor_mapping[DoubleSchubertAlgebraElement] = {
|
936
|
+
# "Mul": [get_postprocessor(Mul)],
|
937
|
+
# "Add": [get_postprocessor(Add)],
|
938
|
+
# }
|
939
|
+
|
940
|
+
DSx = DoubleSchubertAlgebraElement_basis(GeneratingSet("x"))
|
941
|
+
"""DSx: Double Schubert polynomial generator
|
942
|
+
DSx is an alias for a DoubleSchubertAlgebraElement_basis object with
|
943
|
+
GeneratingSet being variables with name x_i for i an integer up to 99.
|
944
|
+
It is a callable object, and the signature is
|
945
|
+
|
946
|
+
DSx(x, cv=None, genset=None)
|
947
|
+
|
948
|
+
x is either a tuple, a list, a schubmult.Permutation, or a sympy
|
949
|
+
or symengine object that you are trying to express in terms of
|
950
|
+
double Schubert polynomials. cv is a string that is the name of
|
951
|
+
the base GeneratingSet for the coefficient variable (defaults to
|
952
|
+
"y"), and genset is the "x" variable generating set by default,
|
953
|
+
but can be subsituted with a custom GeneratingSet_base object.
|
954
|
+
"""
|
955
|
+
|
956
|
+
|
957
|
+
# def Sx(x):
|
958
|
+
# return DSx(x, utils.NoneVar)
|
959
|
+
|
960
|
+
|
961
|
+
class SchubertAlgebraElement_basis(DoubleSchubertAlgebraElement_basis):
|
962
|
+
def __new__(cls, genset):
|
963
|
+
return DoubleSchubertAlgebraElement_basis.__new__(cls, genset)
|
964
|
+
|
965
|
+
def _from_single_dict(self, _dict):
|
966
|
+
return DoubleSchubertAlgebraElement({(k, utils.NoneVar): v for k, v in _dict.items()}, self)
|
967
|
+
|
968
|
+
def __call__(self, x):
|
969
|
+
genset = self.genset
|
970
|
+
# logger.debug(f"{x=} {type(x)=}")
|
971
|
+
if not genset:
|
972
|
+
genset = self.genset
|
973
|
+
if not isinstance(genset, GeneratingSet_base):
|
974
|
+
raise TypeError
|
975
|
+
if isinstance(x, list) or isinstance(x, tuple):
|
976
|
+
elem = self._from_single_dict({Permutation(x): 1})
|
977
|
+
elif isinstance(x, Permutation):
|
978
|
+
elem = self._from_single_dict({x: 1})
|
979
|
+
# elif isinstance(x, spr.SchubertPolynomial):
|
980
|
+
# if x._parent._base_var == self._base_var:
|
981
|
+
# elem_dict = {(x, utils.NoneVar): v for k, v in x.coeff_dict.items()}
|
982
|
+
# elem = QuantumDoubleSchubertAlgebraElement(elem_dict, self)
|
983
|
+
# if cv is not None:
|
984
|
+
# elem = self([1, 2], cv) * elem
|
985
|
+
# else:
|
986
|
+
# return self(x.expand(), cv)
|
987
|
+
elif isinstance(x, DoubleSchubertAlgebraElement):
|
988
|
+
if x.is_Add or x.is_Mul:
|
989
|
+
return x
|
990
|
+
if x.genset == genset:
|
991
|
+
elem = DoubleSchubertAlgebraElement(x.coeff_dict, self) # , self)
|
992
|
+
else:
|
993
|
+
return self(x.expand())
|
994
|
+
# elif isinstance(x, spr.DoubleSchubertAlgebraElement):
|
995
|
+
# if x.genset == self.genset:
|
996
|
+
# return x.as_quantum()
|
997
|
+
else:
|
998
|
+
x = sympify(x)
|
999
|
+
result = py.mult_poly_py({Permutation([]): 1}, x, genset)
|
1000
|
+
elem = self._from_single_dict(result)
|
1001
|
+
return elem
|
1002
|
+
|
1003
|
+
|
1004
|
+
Sx = SchubertAlgebraElement_basis(GeneratingSet("x"))
|
1005
|
+
|
1006
|
+
ybas = SchubertAlgebraElement_basis(GeneratingSet("y"))
|