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.
- schubmult/__init__.py +1 -0
- schubmult/_base_argparse.py +174 -0
- schubmult/perm_lib.py +999 -0
- schubmult/sage_integration/__init__.py +25 -0
- schubmult/sage_integration/_fast_double_schubert_polynomial_ring.py +528 -0
- schubmult/sage_integration/_fast_schubert_polynomial_ring.py +356 -0
- schubmult/sage_integration/_indexing.py +44 -0
- schubmult/schubmult_double/__init__.py +18 -0
- schubmult/schubmult_double/__main__.py +5 -0
- schubmult/schubmult_double/_funcs.py +1590 -0
- schubmult/schubmult_double/_script.py +407 -0
- schubmult/schubmult_double/_vars.py +16 -0
- schubmult/schubmult_py/__init__.py +10 -0
- schubmult/schubmult_py/__main__.py +5 -0
- schubmult/schubmult_py/_funcs.py +111 -0
- schubmult/schubmult_py/_script.py +115 -0
- schubmult/schubmult_py/_vars.py +3 -0
- schubmult/schubmult_q/__init__.py +12 -0
- schubmult/schubmult_q/__main__.py +5 -0
- schubmult/schubmult_q/_funcs.py +304 -0
- schubmult/schubmult_q/_script.py +157 -0
- schubmult/schubmult_q/_vars.py +18 -0
- schubmult/schubmult_q_double/__init__.py +14 -0
- schubmult/schubmult_q_double/__main__.py +5 -0
- schubmult/schubmult_q_double/_funcs.py +507 -0
- schubmult/schubmult_q_double/_script.py +337 -0
- schubmult/schubmult_q_double/_vars.py +21 -0
- schubmult-2.0.0.dist-info/METADATA +455 -0
- schubmult-2.0.0.dist-info/RECORD +36 -0
- schubmult-2.0.0.dist-info/WHEEL +5 -0
- schubmult-2.0.0.dist-info/entry_points.txt +5 -0
- schubmult-2.0.0.dist-info/licenses/LICENSE +674 -0
- schubmult-2.0.0.dist-info/top_level.txt +2 -0
- tests/__init__.py +0 -0
- tests/test_fast_double_schubert.py +145 -0
- 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
|
+
]
|