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,356 @@
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
+
6
+ from sage.combinat.permutation import Permutations, Permutation, from_lehmer_code
7
+ from sage.combinat.composition import (
8
+ Compositions,
9
+ Composition,
10
+ )
11
+ from sage.misc.cachefunc import cached_method
12
+ from sage.rings.polynomial.multi_polynomial_ring import MPolynomialRing_base
13
+ from sage.rings.polynomial.multi_polynomial import MPolynomial
14
+ from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing
15
+ from ._indexing import _coerce_index
16
+
17
+
18
+ import schubmult.schubmult_q as sq
19
+ import schubmult.schubmult_q_double as qyz
20
+ import schubmult.schubmult_py as py
21
+ import schubmult.schubmult_double as yz
22
+
23
+
24
+ from sympy import sympify
25
+ import symengine as syme
26
+
27
+
28
+ def FastSchubertPolynomialRing(
29
+ R: Parent, # noqa: F405
30
+ num_vars: int,
31
+ base_variable_name: str,
32
+ *,
33
+ code_display: bool = False,
34
+ q_varname: str = "q",
35
+ is_quantum: bool = False,
36
+ indices: tuple[int] = tuple([1]),
37
+ ):
38
+ """Wrapper function to return a double Schubert polynomial Ring
39
+
40
+ Calls the _xbasis class to return a (quantum) Schubert
41
+ polynomial ring with the indicated base ring, number of variables,
42
+ variable name, coproduct indices, code_display representation option,
43
+ q-ring variable name, and whether the ring is quantum.
44
+
45
+ Example call:
46
+
47
+ ```python
48
+ X = FastSchubertPolynomialRing(ZZ, 100, "x")
49
+ X([2, 4, 3, 1]) + X([2, 1, 4, 3])
50
+ ```
51
+ This produces a sum of Schubert polynomials in the "x" variables. These will coerce
52
+ to any polynomial ring with variables with the same names as Schubert polynomials.
53
+
54
+ Args:
55
+ R (Parent): The base ring
56
+ num_vars (int): Cardinality of the sets of variables
57
+ base_variable_name (str): Base variable name
58
+ code_display (bool, optional): Whether to display the indices as the Lehmer code. Defaults to False.
59
+ q_varname (str, optional): Variable name of the q-ring. Defaults to "q".
60
+ is_quantum (bool, optional): Whether or not the ring is quantum. Defaults to False.
61
+ indices (tuple[int], optional): Indicies of the variables to split on for the coproduct.
62
+
63
+ Returns:
64
+ FastSchubertPolynomialRing_xbasis: Element constructor of the ring
65
+ """
66
+ if is_quantum:
67
+ QR = PolynomialRing(R, num_vars, q_varname)
68
+ else:
69
+ QR = R
70
+ return FastSchubertPolynomialRing_xbasis(
71
+ R,
72
+ num_vars,
73
+ base_variable_name,
74
+ q_varname,
75
+ code_display,
76
+ indices,
77
+ is_quantum,
78
+ QR,
79
+ )
80
+
81
+
82
+ def FastQuantumSchubertPolynomialRing(
83
+ R: Parent, # noqa: F405
84
+ num_vars: int,
85
+ base_variable_name: str,
86
+ q_varname: str = "q",
87
+ code_display: bool = False,
88
+ ):
89
+ """Quantum Schubert ring generator
90
+
91
+ Wraps FastSchubertPolynomialRing(), omitting indices and setting
92
+ is_quantum to True.
93
+
94
+ Args:
95
+ R (Parent): The base ring
96
+ num_vars (int): Cardinality of the sets of variables
97
+ base_variable_name (str): Base variable name
98
+ q_varname (str, optional): Variable name of the q-ring. Defaults to "q".
99
+ code_display (bool, optional): Whether to display the indices as the Lehmer code. Defaults to False.
100
+
101
+
102
+ Returns:
103
+ FastSchubertPolynomialRing_xbasis: Element constructor of the ring
104
+ """
105
+ return FastSchubertPolynomialRing(
106
+ R,
107
+ num_vars,
108
+ base_variable_name,
109
+ q_varname=q_varname,
110
+ code_display=code_display,
111
+ is_quantum=True,
112
+ )
113
+
114
+
115
+ class FastSchubertPolynomial_class(CombinatorialFreeModule.Element):
116
+ @property
117
+ def base_varname(self):
118
+ return self.parent()._base_varname
119
+
120
+ @property
121
+ def q_varname(self):
122
+ return self.parent()._q_varname
123
+
124
+ @property
125
+ def is_quantum(self):
126
+ return self.parent()._quantum
127
+
128
+ @property
129
+ def polynomial_ring(self):
130
+ return self.parent()._polynomial_ring
131
+
132
+ def expand(self):
133
+ if self.is_quantum:
134
+ return sum(
135
+ [
136
+ self.parent()._polynomial_ring(
137
+ qyz.schubpoly_quantum(
138
+ tuple(_coerce_index(k, self.parent()._ascode, False)),
139
+ self.parent()._polynomial_ring.gens(),
140
+ [0 for i in range(100)],
141
+ self.parent()._q_ring.gens(),
142
+ v,
143
+ )
144
+ )
145
+ for k, v in self.monomial_coefficients().items()
146
+ ]
147
+ )
148
+ else:
149
+ return sum(
150
+ [
151
+ self.parent()._polynomial_ring(
152
+ yz.schubmult(
153
+ {(1, 2): v},
154
+ tuple(_coerce_index(k, self.parent()._ascode, False)),
155
+ self.parent()._polynomial_ring.gens(),
156
+ [0 for i in range(100)],
157
+ ).get((1, 2), 0)
158
+ )
159
+ for k, v in self.monomial_coefficients().items()
160
+ ]
161
+ )
162
+
163
+
164
+ class FastSchubertPolynomialRing_xbasis(CombinatorialFreeModule):
165
+ Element = FastSchubertPolynomial_class
166
+
167
+ def __init__(
168
+ self,
169
+ R,
170
+ num_vars,
171
+ base_variable_name,
172
+ q_varname,
173
+ code_display,
174
+ indices,
175
+ quantum,
176
+ QR,
177
+ ):
178
+ self._name = f"{'Quantum ' if quantum else ''}Schubert polynomial ring with X basis"
179
+ self._splitter = indices
180
+ self._repr_option_bracket = False
181
+ self._quantum = quantum
182
+
183
+ cat = (
184
+ GradedAlgebrasWithBasis(QR).Commutative()
185
+ if quantum
186
+ else GradedBialgebrasWithBasis(R).Commutative()
187
+ )
188
+
189
+ index_set = Permutations()
190
+ self._ascode = False
191
+
192
+ if code_display:
193
+ index_set = Compositions()
194
+ self._ascode = True
195
+
196
+ CombinatorialFreeModule.__init__(
197
+ self,
198
+ R if not quantum else QR,
199
+ index_set,
200
+ category=cat,
201
+ prefix=f"QS{base_variable_name}",
202
+ )
203
+ self._q_ring = QR
204
+ self._base_varname = base_variable_name
205
+ self._q_varname = q_varname
206
+ self._polynomial_ring = PolynomialRing(R, num_vars, base_variable_name)
207
+ self._populate_coercion_lists_()
208
+
209
+ def _coerce_map_from_(self, S):
210
+ if isinstance(S, MPolynomialRing_base):
211
+ return True
212
+ if isinstance(S, FastSchubertPolynomialRing_base):
213
+ return True
214
+ if isinstance(S, FastSchubertPolynomialRing_base):
215
+ return True
216
+ return super()._coerce_map_from_(S)
217
+
218
+ @cached_method
219
+ def one_basis(self):
220
+ return _coerce_index([1], False, self._ascode)
221
+
222
+ def set_coproduct_indices(self, indices):
223
+ self._splitter = indices
224
+
225
+ def _element_constructor_(self, x):
226
+ if (
227
+ isinstance(x, list)
228
+ or isinstance(x, tuple)
229
+ or isinstance(x, Composition)
230
+ or isinstance(x, Permutation)
231
+ ):
232
+ # checking the input to avoid symmetrica crashing Sage, see trac 12924
233
+ elem = self._from_dict(
234
+ {_coerce_index(x, self._ascode, self._ascode): self.base_ring().one()}
235
+ )
236
+ elif isinstance(x, FastSchubertPolynomial):
237
+ if (
238
+ x.base_varname == self._base_varname
239
+ and (self._quantum == x.parent()._quantum)
240
+ and (not self._quantum or x.q_varname == self._q_varname)
241
+ ):
242
+ elem = self._from_dict(
243
+ {
244
+ _coerce_index(k, x.parent()._ascode, self._ascode): v
245
+ for k, v in x.monomial_coefficients().items()
246
+ }
247
+ )
248
+ else:
249
+ return self(x.expand())
250
+ elif isinstance(x, MPolynomial):
251
+ from sage.interfaces.sympy import sympy_init
252
+
253
+ sympy_init()
254
+ sympy_floff = sympify(str(x))
255
+ val = syme.sympify(sympy_floff)
256
+ if self._quantum:
257
+ result = sq.mult_poly(
258
+ {(1, 2): 1},
259
+ val,
260
+ [syme.Symbol(str(g)) for g in self._polynomial_ring.gens()],
261
+ [syme.Symbol(str(g)) for g in self._q_ring.gens()],
262
+ )
263
+ else:
264
+ result = py.mult_poly(
265
+ {(1, 2): 1},
266
+ val,
267
+ [syme.Symbol(str(g)) for g in self._polynomial_ring.gens()],
268
+ )
269
+ elem = self._from_dict(
270
+ {
271
+ _coerce_index(k, False, self._ascode): self._q_ring(str(v))
272
+ if self._quantum
273
+ else self.base_ring()(str(v))
274
+ for k, v in result.items()
275
+ }
276
+ )
277
+ else:
278
+ raise TypeError(f"Could not convert {x=} to {self}")
279
+ return elem
280
+
281
+ def some_elements(self):
282
+ return [
283
+ self.one(),
284
+ self(_coerce_index([1], False, self._ascode))
285
+ + 2 * self(_coerce_index([2, 1], False, self._ascode)),
286
+ self(_coerce_index([4, 2, 1, 3], False, self._ascode))
287
+ - self(_coerce_index([3, 2, 1], False, self._ascode)),
288
+ ]
289
+
290
+ def product_on_basis(self, left, right):
291
+ if self._quantum:
292
+ return sum(
293
+ [
294
+ self.base_ring()(str(v)) * self(_coerce_index(k, False, self._ascode))
295
+ for k, v in sq.schubmult_db(
296
+ {tuple(_coerce_index(left, self._ascode, False)): self.base_ring()(1)},
297
+ tuple(_coerce_index(right, self._ascode, False)),
298
+ list(self._q_ring.gens()),
299
+ ).items()
300
+ ]
301
+ )
302
+ else:
303
+ return sum(
304
+ [
305
+ self.base_ring()(v) * self(_coerce_index(k, False, self._ascode))
306
+ for k, v in py.schubmult(
307
+ {tuple(_coerce_index(left, self._ascode, False)): 1},
308
+ tuple(_coerce_index(right, self._ascode, False)),
309
+ ).items()
310
+ ]
311
+ )
312
+
313
+ def coproduct_on_basis(self, mperm):
314
+ if self._quantum:
315
+ raise NotImplementedError("Quantum Schubert polynomials do not have a coproduct")
316
+ mperm = _coerce_index(mperm, self._ascode, False)
317
+ indices = self._splitter
318
+ indices = sorted(indices)
319
+ k = len(indices)
320
+ n = len(mperm)
321
+ kcd = [indices[i] - i - 1 for i in range(len(indices))] + [n + 1 - k for i in range(k, n)]
322
+ max_required = max([kcd[i] + i for i in range(len(kcd))])
323
+ kcd2 = kcd + [0 for i in range(len(kcd), max_required)] + [0]
324
+ N = len(kcd)
325
+ kperm = from_lehmer_code(kcd2).inverse()
326
+ coeff_dict = {tuple(kperm): 1}
327
+ coeff_dict = py.schubmult(coeff_dict, tuple(mperm))
328
+
329
+ inv_kperm = kperm.number_of_inversions()
330
+ inverse_kperm = kperm.inverse()
331
+ total_sum = 0
332
+ for perm, val in coeff_dict.items():
333
+ pperm = Permutation(list(perm))
334
+ downperm = pperm.left_action_product(inverse_kperm)
335
+ if downperm.number_of_inversions() == pperm.number_of_inversions() - inv_kperm:
336
+ flag = True
337
+ for i in range(N):
338
+ if downperm[i] > N:
339
+ flag = False
340
+ break
341
+ if not flag:
342
+ continue
343
+ firstperm = Permutation(list(downperm[0:N]))
344
+ secondperm = Permutation([downperm[i] - N for i in range(N, len(downperm))])
345
+ total_sum += self.base_ring()(val) * self(
346
+ _coerce_index(firstperm, False, self._ascode)
347
+ ).tensor(self(_coerce_index(secondperm, False, self._ascode)))
348
+ return total_sum
349
+
350
+
351
+ def _repr_(self):
352
+ return f"Ring of Schubert polynomials in {self._base_varname} with {len(self._polynomial_ring.gens())} variables over {self._q_ring.base_ring()}"
353
+
354
+
355
+ FastSchubertPolynomial = FastSchubertPolynomial_class
356
+ FastSchubertPolynomialRing_base = FastSchubertPolynomialRing_xbasis
@@ -0,0 +1,44 @@
1
+ from sage.combinat.permutation import Permutation
2
+
3
+ from sage.combinat.composition import Composition
4
+
5
+ from schubmult.perm_lib import uncode, trimcode, permtrim
6
+
7
+
8
+ def _coerce_index(indexed_obj, is_comp, should_be_comp):
9
+ if is_comp == should_be_comp:
10
+ if isinstance(indexed_obj, list) or isinstance(indexed_obj, tuple):
11
+ if is_comp:
12
+ return Composition(trimcode(permtrim(uncode(list(indexed_obj)))))
13
+ else:
14
+ return Permutation(permtrim(list(indexed_obj)))
15
+ else:
16
+ return indexed_obj
17
+ else:
18
+ if is_comp:
19
+ if (
20
+ isinstance(indexed_obj, list)
21
+ or isinstance(indexed_obj, tuple)
22
+ or isinstance(indexed_obj, Composition)
23
+ ):
24
+ return Permutation(permtrim(uncode(list(indexed_obj))))
25
+
26
+ if isinstance(indexed_obj, dict): # keys are comps
27
+ return {
28
+ Permutation(permtrim(uncode(list(k)))): v
29
+ for k, v in indexed_obj.items()
30
+ }
31
+ else:
32
+ if (
33
+ isinstance(indexed_obj, list)
34
+ or isinstance(indexed_obj, tuple)
35
+ or isinstance(indexed_obj, Permutation)
36
+ ):
37
+ return Composition(trimcode(list(indexed_obj)))
38
+
39
+ if isinstance(indexed_obj, dict): # keys are comps
40
+ return {
41
+ Composition(trimcode(permtrim(list(k)))): v
42
+ for k, v in indexed_obj.items()
43
+ }
44
+ raise TypeError
@@ -0,0 +1,18 @@
1
+ from ._funcs import (
2
+ compute_positive_rep,
3
+ schubmult,
4
+ single_variable,
5
+ mult_poly,
6
+ posify,
7
+ div_diff
8
+ )
9
+
10
+
11
+ __all__ = [
12
+ "compute_positive_rep",
13
+ "schubmult",
14
+ "single_variable",
15
+ "mult_poly",
16
+ "posify",
17
+ "div_diff"
18
+ ]
@@ -0,0 +1,5 @@
1
+ import sys
2
+ from ._script import main
3
+
4
+ if __name__ == "__main__":
5
+ sys.exit(main())