schubmult 2.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 (36) hide show
  1. schubmult/__init__.py +1 -0
  2. schubmult/_base_argparse.py +174 -0
  3. schubmult/perm_lib.py +999 -0
  4. schubmult/sage_integration/__init__.py +25 -0
  5. schubmult/sage_integration/_fast_double_schubert_polynomial_ring.py +528 -0
  6. schubmult/sage_integration/_fast_schubert_polynomial_ring.py +356 -0
  7. schubmult/sage_integration/_indexing.py +44 -0
  8. schubmult/schubmult_double/__init__.py +18 -0
  9. schubmult/schubmult_double/__main__.py +5 -0
  10. schubmult/schubmult_double/_funcs.py +1590 -0
  11. schubmult/schubmult_double/_script.py +407 -0
  12. schubmult/schubmult_double/_vars.py +16 -0
  13. schubmult/schubmult_py/__init__.py +10 -0
  14. schubmult/schubmult_py/__main__.py +5 -0
  15. schubmult/schubmult_py/_funcs.py +111 -0
  16. schubmult/schubmult_py/_script.py +115 -0
  17. schubmult/schubmult_py/_vars.py +3 -0
  18. schubmult/schubmult_q/__init__.py +12 -0
  19. schubmult/schubmult_q/__main__.py +5 -0
  20. schubmult/schubmult_q/_funcs.py +304 -0
  21. schubmult/schubmult_q/_script.py +157 -0
  22. schubmult/schubmult_q/_vars.py +18 -0
  23. schubmult/schubmult_q_double/__init__.py +14 -0
  24. schubmult/schubmult_q_double/__main__.py +5 -0
  25. schubmult/schubmult_q_double/_funcs.py +507 -0
  26. schubmult/schubmult_q_double/_script.py +337 -0
  27. schubmult/schubmult_q_double/_vars.py +21 -0
  28. schubmult-2.0.0.dist-info/METADATA +455 -0
  29. schubmult-2.0.0.dist-info/RECORD +36 -0
  30. schubmult-2.0.0.dist-info/WHEEL +5 -0
  31. schubmult-2.0.0.dist-info/entry_points.txt +5 -0
  32. schubmult-2.0.0.dist-info/licenses/LICENSE +674 -0
  33. schubmult-2.0.0.dist-info/top_level.txt +2 -0
  34. tests/__init__.py +0 -0
  35. tests/test_fast_double_schubert.py +145 -0
  36. tests/test_fast_schubert.py +38 -0
@@ -0,0 +1,25 @@
1
+ from sage.all import * # noqa: F403
2
+
3
+ from ._fast_schubert_polynomial_ring import (
4
+ FastSchubertPolynomialRing,
5
+ FastSchubertPolynomial,
6
+ FastSchubertPolynomialRing_base,
7
+ FastQuantumSchubertPolynomialRing,
8
+ )
9
+ from ._fast_double_schubert_polynomial_ring import (
10
+ FastDoubleSchubertPolynomialRing,
11
+ FastDoubleSchubertPolynomial,
12
+ FastDoubleSchubertPolynomialRing_base,
13
+ FastQuantumDoubleSchubertPolynomialRing,
14
+ )
15
+
16
+ __all__ = [
17
+ "FastSchubertPolynomialRing",
18
+ "FastSchubertPolynomial",
19
+ "FastSchubertPolynomialRing_base",
20
+ "FastDoubleSchubertPolynomialRing",
21
+ "FastDoubleSchubertPolynomial",
22
+ "FastDoubleSchubertPolynomialRing_base",
23
+ "FastQuantumSchubertPolynomialRing",
24
+ "FastQuantumDoubleSchubertPolynomialRing",
25
+ ]
@@ -0,0 +1,528 @@
1
+ from sage.all import * # noqa: F403
2
+ from sage.categories.graded_bialgebras_with_basis import GradedBialgebrasWithBasis
3
+ from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis
4
+ from sage.combinat.free_module import CombinatorialFreeModule
5
+ from sage.categories.cartesian_product import cartesian_product
6
+ from sage.combinat.permutation import Permutations, Permutation, from_lehmer_code
7
+ from sage.misc.cachefunc import cached_method
8
+ from sage.rings.polynomial.multi_polynomial import MPolynomial
9
+ from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_base
10
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
11
+ from sage.rings.polynomial.flatten import FlatteningMorphism
12
+ from sage.combinat.composition import (
13
+ Compositions,
14
+ Composition,
15
+ )
16
+ from . import (
17
+ FastSchubertPolynomialRing_base,
18
+ FastSchubertPolynomial,
19
+ )
20
+ from ._indexing import _coerce_index
21
+
22
+
23
+ import schubmult.schubmult_q_double as qyz
24
+ import schubmult.schubmult_double as yz
25
+ from sympy import sympify
26
+ import symengine as syme
27
+
28
+
29
+ def FastDoubleSchubertPolynomialRing(
30
+ R: Parent, # noqa: F405
31
+ num_vars: int,
32
+ base_variable_name: str,
33
+ coeff_variable_names: str | tuple[str],
34
+ *,
35
+ indices: tuple[int] = tuple([1]),
36
+ code_display: bool = False,
37
+ q_varname: str = "q",
38
+ is_quantum: bool = False,
39
+ ):
40
+ """Wrapper function to return a double Schubert polynomial Ring
41
+
42
+ Calls the _xbasis class to return a double or quantum double Schubert
43
+ polynomial ring with the indicated base ring, number of variables,
44
+ variable names (base variable, and then one or more sets of coefficient)
45
+ variables, coproduct indices, code_display representation option, q-ring
46
+ variable name, and whether the ring is quantum.
47
+
48
+ Example call:
49
+
50
+ ```python
51
+ X = FastDoubleSchubertPolynomialRing(ZZ, 100, "x", ("y", "z"))
52
+ X([2, 4, 3, 1]) + X([2, 1, 4, 3], "z")
53
+ ```
54
+
55
+ Args:
56
+ R (sage ring): The base ring
57
+ num_vars (int): Cardinality of the sets of variables
58
+ base_variable_name (str): Base variable name
59
+ coeff_variable_names (str | tuple[str]): Coefficient variable name(s)
60
+ indices (tuple[int], optional): Indicies of the variables to split on for the coproduct.
61
+ code_display (bool, optional): Whether to display the indices as the Lehmer code. Defaults to False.
62
+ q_varname (str, optional): Variable name of the q-ring. Defaults to "q".
63
+ is_quantum (bool, optional): Whether or not the ring is quantum. Defaults to False.
64
+
65
+ Returns:
66
+ FastDoubleSchubertPolynomialRing_xbasis: Basis element generator of the ring
67
+ """
68
+ QR = None
69
+ if is_quantum:
70
+ QR = PolynomialRing(R, num_vars, q_varname)
71
+ return FastDoubleSchubertPolynomialRing_xbasis(
72
+ R,
73
+ num_vars,
74
+ base_variable_name,
75
+ coeff_variable_names,
76
+ q_varname,
77
+ code_display,
78
+ indices,
79
+ is_quantum,
80
+ QR,
81
+ )
82
+
83
+
84
+ def FastQuantumDoubleSchubertPolynomialRing(
85
+ R: Parent, # noqa: F405
86
+ num_vars: int,
87
+ base_variable_name: str,
88
+ coeff_variable_names: str | tuple[str],
89
+ *,
90
+ code_display=False,
91
+ q_varname="q",
92
+ ):
93
+ """Quantum double Schubert ring generator
94
+
95
+ Wraps FastDoubleSchubertPolynomialRing(), omitting indices and setting
96
+ is_quantum to True.
97
+
98
+ Args:
99
+ R (sage ring): The base ring
100
+ num_vars (int): Cardinality of the sets of variables
101
+ base_variable_name (str): Base variable name
102
+ coeff_variable_names (str | tuple[str]): Coefficient variable name(s)
103
+ code_display (bool, optional): Whether to display the indices as the Lehmer code. Defaults to False.
104
+ q_varname (str, optional): Variable name of the q-ring. Defaults to "q".
105
+
106
+ Returns:
107
+ FastDoubleSchubertPolynomialRing_xbasis: Basis element generator of the quantum ring
108
+ """
109
+ return FastDoubleSchubertPolynomialRing(
110
+ R,
111
+ num_vars,
112
+ base_variable_name,
113
+ coeff_variable_names,
114
+ code_display=code_display,
115
+ indices=tuple([1]),
116
+ is_quantum=True,
117
+ q_varname=q_varname,
118
+ )
119
+
120
+
121
+ class FastDoubleSchubertPolynomial_class(CombinatorialFreeModule.Element):
122
+ @property
123
+ def base_varname(self):
124
+ return self.parent()._base_varname
125
+
126
+ @property
127
+ def q_varname(self):
128
+ if not self.parent().is_quantum:
129
+ return None
130
+ else:
131
+ return self.parent()._q_varname
132
+
133
+ @property
134
+ def base_polynomial_ring(self):
135
+ return self.parent()._base_polynomial_ring
136
+
137
+ @property
138
+ def coeff_polynomial_ring(self):
139
+ return self.parent()._coeff_polynomial_ring
140
+
141
+ @property
142
+ def q_ring(self):
143
+ if not self.parent()._quantum:
144
+ return None
145
+ else:
146
+ return self.parent()._q_ring
147
+
148
+ def expand(self):
149
+ if self.parent()._quantum:
150
+ return sum(
151
+ [
152
+ qyz.schubpoly_quantum(
153
+ tuple(_coerce_index(k[0], self.parent()._ascode, False)),
154
+ self.parent()._base_polynomial_ring.gens(),
155
+ self.parent()._coeff_polynomial_rings[k[1]].gens(),
156
+ self.parent()._q_ring.gens(),
157
+ v,
158
+ )
159
+ for k, v in self.monomial_coefficients().items()
160
+ ]
161
+ )
162
+ else:
163
+ return sum(
164
+ [
165
+ yz.schubmult(
166
+ {(1, 2): v},
167
+ tuple(_coerce_index(k[0], self.parent()._ascode, False)),
168
+ self.parent()._base_polynomial_ring.gens(),
169
+ self.parent()._coeff_polynomial_rings[k[1]].gens(),
170
+ ).get((1, 2), 0)
171
+ for k, v in self.monomial_coefficients().items()
172
+ ]
173
+ )
174
+
175
+ def __eq__(self, other):
176
+ ss = self.parent().one() * self
177
+ oo = self.parent().one() * other
178
+ return ss.monomial_coefficients() == oo.monomial_coefficients()
179
+
180
+ def __ne__(self, other):
181
+ ss = self.parent().one() * self
182
+ oo = self.parent().one() * other
183
+ return ss.monomial_coefficients() != oo.monomial_coefficients()
184
+
185
+ def root_coefficients(self, root_var_name):
186
+ num_vars = len(self.parent()._coeff_polynomial_ring.gens())
187
+ RR = PolynomialRing(self.parent().base_ring().base_ring(), num_vars, root_var_name)
188
+ r = [sum(RR._first_ngens(j)) for j in range(num_vars)]
189
+ subs_dict = {self.parent()._coeff_polynomial_ring.gens()[i]: r[i] for i in range(num_vars)}
190
+ # res = self
191
+ return self.map_coefficients(lambda foi: RR(foi.subs(subs_dict)))
192
+
193
+
194
+ class FastDoubleSchubertPolynomialRing_xbasis(CombinatorialFreeModule):
195
+ Element = FastDoubleSchubertPolynomial_class
196
+
197
+ def __init__(
198
+ self,
199
+ R,
200
+ num_vars,
201
+ base_variable_name,
202
+ coeff_variable_names,
203
+ q_varname,
204
+ code_display,
205
+ indices,
206
+ quantum,
207
+ QR,
208
+ ):
209
+ self._name = f"{'Quantum double' if quantum else 'Double'} Schubert polynomial ring"
210
+ self._splitter = indices
211
+ self._repr_option_bracket = False
212
+ self._mixed = False
213
+ self._q_ring = QR
214
+ self._quantum = quantum
215
+ self._base_varname = base_variable_name
216
+ self._q_varname = q_varname
217
+
218
+ if isinstance(coeff_variable_names, tuple):
219
+ self._mixed = True
220
+ self._varlist = [*coeff_variable_names]
221
+ self._coeff_polynomial_rings = {
222
+ name: PolynomialRing(R if not quantum else QR, num_vars, name)
223
+ for name in self._varlist
224
+ }
225
+
226
+ self._coeff_polynomial_ring = R if not quantum else QR
227
+ for name, CR in self._coeff_polynomial_rings.items():
228
+ self._coeff_polynomial_ring = PolynomialRing(
229
+ self._coeff_polynomial_ring, num_vars, name
230
+ )
231
+ self._coeff_polynomial_ring = FlatteningMorphism(self._coeff_polynomial_ring).codomain()
232
+ else:
233
+ self._varlist = [coeff_variable_names]
234
+ self._coeff_polynomial_ring = PolynomialRing(
235
+ R if not quantum else QR, num_vars, coeff_variable_names
236
+ )
237
+ self._coeff_polynomial_rings = {}
238
+ self._coeff_polynomial_rings[coeff_variable_names] = self._coeff_polynomial_ring
239
+
240
+ index_set = Permutations()
241
+ self._ascode = False
242
+
243
+ if code_display:
244
+ index_set = Compositions()
245
+ self._ascode = True
246
+
247
+ self._base_polynomial_ring = PolynomialRing(
248
+ self._coeff_polynomial_ring, num_vars, base_variable_name
249
+ )
250
+
251
+ self._index_wrapper = cartesian_product([index_set, self._varlist])
252
+ cat = (
253
+ GradedAlgebrasWithBasis(self._coeff_polynomial_ring).Commutative()
254
+ if quantum
255
+ else GradedBialgebrasWithBasis(self._coeff_polynomial_ring).Commutative()
256
+ )
257
+
258
+ CombinatorialFreeModule.__init__(
259
+ self,
260
+ self._coeff_polynomial_ring,
261
+ self._index_wrapper,
262
+ category=cat,
263
+ prefix=f"{'Q' if quantum else ''}S{base_variable_name}",
264
+ )
265
+ self._populate_coercion_lists_()
266
+
267
+ @cached_method
268
+ def one_basis(self):
269
+ return (_coerce_index([1], False, self._ascode), self._varlist[0])
270
+
271
+ @property
272
+ def is_quantum(self):
273
+ return self._quantum
274
+
275
+ def set_coproduct_indices(self, indices):
276
+ self._splitter = indices
277
+
278
+ def _element_constructor_(self, *x):
279
+ if len(x) == 1:
280
+ x = x[0]
281
+ elif len(x) > 2:
282
+ raise ValueError("Bad index for element")
283
+
284
+ if (
285
+ isinstance(x, list)
286
+ or isinstance(x, tuple)
287
+ or isinstance(x, Permutation)
288
+ or isinstance(x, Composition)
289
+ ):
290
+ # checking the input to avoid symmetrica crashing Sage, see trac 12924
291
+ if x in self._index_wrapper:
292
+ elem = self._from_dict({self._index_wrapper((x[0], x[1])): self.base_ring().one()})
293
+ else:
294
+ elem = self._from_dict(
295
+ {
296
+ self._index_wrapper(
297
+ (
298
+ _coerce_index(x, self._ascode, self._ascode),
299
+ self._varlist[0],
300
+ )
301
+ ): self.base_ring().one()
302
+ }
303
+ )
304
+ elif isinstance(x, FastDoubleSchubertPolynomial):
305
+ if (
306
+ x.base_varname == self._base_varname
307
+ and (self._quantum == x.parent()._quantum)
308
+ and (not self._quantum or x.q_varname == self._q_varname)
309
+ ):
310
+ elem = self._from_dict(
311
+ {
312
+ (_coerce_index(k[0], x.parent()._ascode, self._ascode), k[1]): v
313
+ for k, v in x.monomial_coefficients().items()
314
+ }
315
+ )
316
+ else:
317
+ return self(x.expand())
318
+ elif isinstance(x, FastSchubertPolynomial):
319
+ if (
320
+ x.base_varname == self._base_varname
321
+ and x.q_varname == self._q_varname
322
+ and (self._quantum == x.parent()._quantum)
323
+ and (not self._quantum or x.q_varname == self._q_varname)
324
+ ):
325
+ elem_dict = {}
326
+
327
+ for k, v in x.monomial_coefficients().items():
328
+ if self._quantum:
329
+ res = qyz.schubmult_db(
330
+ {(1, 2): self._coeff_polynomial_ring(v)},
331
+ tuple(_coerce_index(k, x.parent()._ascode, False)),
332
+ self._coeff_polynomial_rings[self._varlist[0]].gens(),
333
+ [
334
+ 0
335
+ for i in range(
336
+ len(self._coeff_polynomial_rings[self._varlist[0]].gens())
337
+ )
338
+ ],
339
+ self._q_ring.gens(),
340
+ )
341
+ else:
342
+ res = yz.schubmult(
343
+ {(1, 2): self._coeff_polynomial_ring(v)},
344
+ tuple(_coerce_index(k, x.parent()._ascode, False)),
345
+ self._coeff_polynomial_rings[self._varlist[0]].gens(),
346
+ [
347
+ 0
348
+ for i in range(
349
+ len(self._coeff_polynomial_rings[self._varlist[0]].gens())
350
+ )
351
+ ],
352
+ )
353
+ for k0, c0 in res.items():
354
+ elem_dict[(_coerce_index(k0, False, self._ascode), self._varlist[0])] = (
355
+ elem_dict.get(
356
+ (_coerce_index(k0, False, self._ascode), self._varlist[0]),
357
+ self._coeff_polynomial_ring.zero(),
358
+ )
359
+ + self._coeff_polynomial_ring(c0)
360
+ )
361
+ elem = self._from_dict(elem_dict)
362
+ else:
363
+ elem = self(x.expand())
364
+ elif isinstance(x, MPolynomial):
365
+ from sage.interfaces.sympy import sympy_init
366
+
367
+ sympy_init()
368
+ sympy_floff = sympify(str(x))
369
+ val = syme.sympify(sympy_floff)
370
+ if self._quantum:
371
+ result = qyz.mult_poly(
372
+ {(1, 2): 1},
373
+ val,
374
+ [syme.Symbol(str(g)) for g in self._base_polynomial_ring.gens()],
375
+ [
376
+ syme.Symbol(str(g))
377
+ for g in self._coeff_polynomial_rings[self._varlist[0]].gens()
378
+ ],
379
+ [syme.Symbol(str(g)) for g in self._q_ring.gens()],
380
+ )
381
+ else:
382
+ result = yz.mult_poly(
383
+ {(1, 2): 1},
384
+ val,
385
+ [syme.Symbol(str(g)) for g in self._base_polynomial_ring.gens()],
386
+ [
387
+ syme.Symbol(str(g))
388
+ for g in self._coeff_polynomial_rings[self._varlist[0]].gens()
389
+ ],
390
+ )
391
+ elem = self._from_dict(
392
+ {
393
+ (
394
+ _coerce_index(k, False, self._ascode),
395
+ self._varlist[0],
396
+ ): self._coeff_polynomial_ring(str(v))
397
+ for k, v in result.items()
398
+ }
399
+ )
400
+ else:
401
+ raise TypeError
402
+
403
+ return elem
404
+
405
+ def some_elements(self):
406
+ return [
407
+ self.one(),
408
+ self(_coerce_index([1], False, self._ascode))
409
+ + 2 * self(_coerce_index([2, 1], False, self._ascode)),
410
+ self(_coerce_index([4, 2, 1, 3], False, self._ascode))
411
+ - self(_coerce_index([3, 2, 1], False, self._ascode)),
412
+ ]
413
+
414
+ def product_on_basis(self, left, right):
415
+ le = tuple(left[0])
416
+ ri = tuple(right[0])
417
+ var_y = [
418
+ self._coeff_polynomial_ring(g) for g in self._coeff_polynomial_rings[left[1]].gens()
419
+ ]
420
+ var_z = [
421
+ self._coeff_polynomial_ring(g) for g in self._coeff_polynomial_rings[right[1]].gens()
422
+ ]
423
+ if self._quantum:
424
+ result = qyz.schubmult_db(
425
+ {tuple(_coerce_index(le, self._ascode, False)): 1},
426
+ tuple(_coerce_index(ri, self._ascode, False)),
427
+ var_y,
428
+ var_z,
429
+ self._q_ring.gens(),
430
+ )
431
+ else:
432
+ result = yz.schubmult(
433
+ {tuple(_coerce_index(le, self._ascode, False)): 1},
434
+ tuple(_coerce_index(ri, self._ascode, False)),
435
+ var_y,
436
+ var_z,
437
+ )
438
+ result = {k: v for k, v in result.items() if v != 0}
439
+ return sum(
440
+ [
441
+ self._coeff_polynomial_ring(v)
442
+ * self((_coerce_index(k, False, self._ascode), left[1]))
443
+ for k, v in result.items()
444
+ ]
445
+ )
446
+
447
+ def _coerce_map_from_(self, S):
448
+ if isinstance(S, MPolynomialRing_base):
449
+ return True
450
+ if isinstance(S, FastSchubertPolynomialRing_base):
451
+ return True
452
+ if isinstance(S, FastDoubleSchubertPolynomialRing_base):
453
+ return True
454
+ return super().has_coerce_map_from(S)
455
+
456
+ def coproduct_on_basis(self, indm):
457
+ if self._quantum:
458
+ raise NotImplementedError("Quantum double Schubert polynomials have no coproduct")
459
+ indices = self._splitter
460
+ indices = sorted(indices)
461
+ subs_dict_coprod = {}
462
+ mperm = indm[0]
463
+ mperm = _coerce_index(mperm, self._ascode, False)
464
+ RR = self._coeff_polynomial_rings[indm[1]]
465
+ RBase = self._coeff_polynomial_rings[self._varlist[0]]
466
+ k = len(indices)
467
+ n = len(mperm)
468
+ kcd = [indices[i] - i - 1 for i in range(len(indices))] + [n + 1 - k for i in range(k, n)]
469
+ max_required = max([kcd[i] + i for i in range(len(kcd))])
470
+ kcd2 = kcd + [0 for i in range(len(kcd), max_required)] + [0]
471
+ N = len(kcd)
472
+ kperm = from_lehmer_code(kcd2).inverse()
473
+ # r = [sum(self.base_ring()._first_ngens(j)) for j in range(100)]
474
+ vn = [f"soible_{i}" for i in range(N * 2 + 1)]
475
+ TR = PolynomialRing(self.base_ring(), N * 2 + 1, vn)
476
+
477
+ for i in range(N * 2 + 1):
478
+ if i <= N:
479
+ subs_dict_coprod[TR.gens()[i]] = self._coeff_polynomial_ring(RR.gens()[i])
480
+ else:
481
+ subs_dict_coprod[TR.gens()[i]] = self._coeff_polynomial_ring(RBase.gens()[i - N])
482
+
483
+ coeff_dict = {tuple(kperm): 1}
484
+ coeff_dict = yz.schubmult(
485
+ coeff_dict,
486
+ tuple(mperm),
487
+ list(TR.gens()),
488
+ RR.gens(),
489
+ )
490
+
491
+ inv_kperm = kperm.number_of_inversions()
492
+ inverse_kperm = kperm.inverse()
493
+ total_sum = 0
494
+ for perm, val in coeff_dict.items():
495
+ pperm = Permutation(list(perm))
496
+ downperm = pperm.left_action_product(inverse_kperm)
497
+ if downperm.number_of_inversions() == pperm.number_of_inversions() - inv_kperm:
498
+ flag = True
499
+ for i in range(N):
500
+ if downperm[i] > N:
501
+ flag = False
502
+ break
503
+ if not flag:
504
+ continue
505
+ firstperm = Permutation(list(downperm[0:N]))
506
+ secondperm = Permutation([downperm[i] - N for i in range(N, len(downperm))])
507
+ val = TR(val).subs(subs_dict_coprod)
508
+ total_sum += self._coeff_polynomial_ring(val) * self(
509
+ (_coerce_index(firstperm, False, self._ascode), indm[1])
510
+ ).tensor(
511
+ self(
512
+ (
513
+ _coerce_index(secondperm, False, self._ascode),
514
+ self._varlist[0],
515
+ )
516
+ )
517
+ )
518
+ return total_sum
519
+
520
+ def _repr_(self):
521
+ return (
522
+ f"Ring of {'quantum' if self._quantum else ''} double Schubert polynomials in {self._base_varname}{',' + self._q_varname if self._quantum else ''} with {len(self._base_polynomial_ring.gens())} variables with"
523
+ f" coefficient variables {','.join(self._varlist)} over the ring {self._coeff_polynomial_ring.base_ring()} indexed by {'the Lehmer code' if self._ascode else 'permutations'}"
524
+ )
525
+
526
+
527
+ FastDoubleSchubertPolynomial = FastDoubleSchubertPolynomial_class
528
+ FastDoubleSchubertPolynomialRing_base = FastDoubleSchubertPolynomialRing_xbasis