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.
Files changed (59) hide show
  1. schubmult/__init__.py +94 -1
  2. schubmult/perm_lib.py +233 -880
  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 +17 -15
  13. schubmult/{sage_integration → sage}/_fast_double_schubert_polynomial_ring.py +142 -220
  14. schubmult/{sage_integration → sage}/_fast_schubert_polynomial_ring.py +78 -72
  15. schubmult/sage/_indexing.py +51 -0
  16. schubmult/schub_lib/__init__.py +51 -0
  17. schubmult/{schubmult_double/_funcs.py → schub_lib/double.py} +618 -798
  18. schubmult/{schubmult_q/_funcs.py → schub_lib/quantum.py} +70 -72
  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} +58 -48
  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} +40 -11
  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.3.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 -9
  41. schubmult/sage_integration/_indexing.py +0 -51
  42. schubmult/schubmult_double/__init__.py +0 -22
  43. schubmult/schubmult_double/__main__.py +0 -5
  44. schubmult/schubmult_double/_script.py +0 -474
  45. schubmult/schubmult_py/__init__.py +0 -13
  46. schubmult/schubmult_py/__main__.py +0 -5
  47. schubmult/schubmult_py/_script.py +0 -96
  48. schubmult/schubmult_q/__init__.py +0 -13
  49. schubmult/schubmult_q/__main__.py +0 -5
  50. schubmult/schubmult_q/_script.py +0 -160
  51. schubmult/schubmult_q_double/__init__.py +0 -17
  52. schubmult/schubmult_q_double/__main__.py +0 -5
  53. schubmult/schubmult_q_double/_funcs.py +0 -540
  54. schubmult/schubmult_q_double/_script.py +0 -398
  55. schubmult-2.0.3.dist-info/METADATA +0 -455
  56. schubmult-2.0.3.dist-info/RECORD +0 -30
  57. schubmult-2.0.3.dist-info/entry_points.txt +0 -5
  58. {schubmult-2.0.3.dist-info → schubmult-3.0.0.dist-info}/licenses/LICENSE +0 -0
  59. {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"))