26
26
# https://www.gnu.org/licenses/
27
27
# ****************************************************************************
28
28
29
+ from sage .categories .graded_algebras_with_basis import GradedAlgebrasWithBasis
29
30
from sage .combinat .free_module import CombinatorialFreeModule
30
- from sage .combinat .compositions import Compositions
31
+ from sage .combinat .composition import Composition , Compositions
31
32
from sage .misc .inherit_comparison import InheritComparisonClasscallMetaclass
32
-
33
- from sage .rings .rational_field import QQ
34
33
from sage .rings .polynomial .infinite_polynomial_ring import InfinitePolynomialRing
34
+ from sage .rings .rational_field import QQ
35
35
36
- class KeyPolynomial (CombinatorialFreeModule .Element ,
37
- InheritComparisonClasscallMetaclass ):
38
- r"""
39
- Key polynomials indexed by (weak) compositions.
40
- """
41
-
42
- @staticmethod
43
- def __classcall_private__ (cls , alpha ):
44
- r"""
45
- Normalize the input
46
- """
47
- R = QQ
48
- return KeyPolynomialRing (R )(alpha )
49
-
50
- def pi_operator (self , w ):
51
- r"""
52
- Pi_i or produt
53
- """
54
- return
55
-
56
- def polynomial_ring (self ):
57
- return self .parent ().polynomial_ring ()
58
-
59
- def polynomial_expand (self ):
60
- R = self .polynomial ()
61
- out = R .zero ()
62
-
63
- for comp , coeff in self .monomial_coefficients ():
64
- p = [i for i ,j in sorted (list (enumerate (comp ._list , start = 1 )),
65
- key = lambda x : (x [1 ], x [0 ]), reverse = True )]
66
- P = Permutation (p ) # the minimum sorting permutation
67
- m = R .prod (x [i ]^ e for i ,e in enumerate (reversed (P .action (comp ))))
68
- out += self .pi_operator (w )()
69
-
70
- def atom_expand (self ):
71
- return
36
+ class KeyPolynomial (CombinatorialFreeModule .Element ):
72
37
73
- @cachedmethod
74
- def from_polynomial (self ):
38
+ def expand (self ):
75
39
r"""
76
-
40
+ Return ``self`` written in the monomial basis.
77
41
"""
42
+ out = self .parent ().zero ()
78
43
79
- def divided_difference (self , i ):
80
- r"""
81
- Return the ``i``-th divided difference operator, applied to ``self``.
82
- """
83
- if not self : # if self is 0
84
- return self
85
- Perms = Permutations ()
86
- if i in ZZ :
87
- if i <= 0 :
88
- raise ValueError (r"cannot apply \delta_{%s} to a (= %s)" % (i , self ))
89
- # The operator `\delta_i` sends the Schubert
90
- # polynomial `X_\pi` (where `\pi` is a finitely supported
91
- # permutation of `\{1, 2, 3, \ldots\}`) to:
92
- # - the Schubert polynomial X_\sigma`, where `\sigma` is
93
- # obtained from `\pi` by switching the values at `i` and `i+1`,
94
- # if `i` is a descent of `\pi` (that is, `\pi(i) > \pi(i+1)`);
95
- # - `0` otherwise.
96
- # Notice that distinct `\pi`s lead to distinct `\sigma`s,
97
- # so we can use `_from_dict` here.
98
- res_dict = {}
99
- for pi , coeff in self :
100
- pi = pi [:]
101
- n = len (pi )
102
- if n <= i :
103
- continue
104
- if pi [i - 1 ] < pi [i ]:
105
- continue
106
- pi [i - 1 ], pi [i ] = pi [i ], pi [i - 1 ]
107
- pi = Perms (pi ).remove_extra_fixed_points ()
108
- res_dict [pi ] = coeff
109
- return self .parent ()._from_dict (res_dict )
110
- elif i in Perms :
111
- i = Permutation (i )
112
- redw = i .reduced_word ()
113
- res_dict = {}
114
- for pi , coeff in self :
115
- next_pi = False
116
- pi = pi [:]
117
- n = len (pi )
118
- for j in redw :
119
- if n <= j :
120
- next_pi = True
121
- break
122
- if pi [j - 1 ] < pi [j ]:
123
- next_pi = True
124
- break
125
- pi [j - 1 ], pi [j ] = pi [j ], pi [j - 1 ]
126
- if next_pi :
127
- continue
128
- pi = Perms (pi ).remove_extra_fixed_points ()
129
- res_dict [pi ] = coeff
130
- return self .parent ()._from_dict (res_dict )
131
- else :
132
- raise TypeError ("i must either be an integer or permutation" )
44
+ for a , c in self .monomial_coefficients ():
45
+ out += c * self .parent ()._pi_i (a )
133
46
134
47
135
48
class KeyPolynomialRing (CombinatorialFreeModule ):
@@ -149,14 +62,149 @@ def __init__(self, R):
149
62
self ._name = "Ring of key polynomials"
150
63
self ._repr_option_bracket = False
151
64
CombinatorialFreeModule .__init__ (self , R , Compositions (),
152
- # category=GradedAlgebrasWithBasis(R),
65
+ category = GradedAlgebrasWithBasis (R ),
153
66
prefix = 'k' )
154
67
155
- def _element_constructor_ (self , parent , alpha ):
68
+ def _element_constructor_ (self , alpha ):
69
+ #todo: add a case where alpha is an InfinitePolynomialRing element
156
70
return self .element_class (self , alpha )
157
71
158
72
def polynomial_ring (self ):
159
73
r"""
160
74
Return the polynomial ring associated to ``self``.
161
75
"""
162
- return InfinitePolynomialRing (self .base_ring (), 'x' )
76
+ return InfinitePolynomialRing (self .base_ring (), 'z' )
77
+
78
+ def from_polynomial (self , f ):
79
+ r"""
80
+ Expand a polynomial in terms of the key basis.
81
+ """
82
+ if not f in self .polynomial_ring (): #todo: change this to accept coerce
83
+ raise ValueError (f"f must be an element of { self .polynomial_ring ()} " )
84
+
85
+ out = self .zero ()
86
+
87
+ while f .monomials ():
88
+ m = f .monomials ()[0 ] #i'm assuming these are sorted in lex order but that is an educated guess.
89
+ c = f .monomial_coefficient (m )
90
+ new_term = c * self (Composition (m ))
91
+ f -= new_term .expand ()
92
+ out += new_term
93
+
94
+ return out
95
+
96
+ def pi (self , w , f ):
97
+ r"""
98
+
99
+ """
100
+ if not hasattr (w , "__iter__" ):
101
+ w = [w ]
102
+ for i in reversed (w ):
103
+ f = self ._pi_i (i , f )
104
+ return f
105
+
106
+ def _pi_i (self , i , f ):
107
+ r"""
108
+
109
+ """
110
+ R = self .polynomial_ring ()
111
+ z , = R .gens ()
112
+ return self ._divided_difference (i , z [i ] * f )
113
+
114
+ def _divided_difference (self , i , f ):
115
+ r"""
116
+
117
+ EXAMPLES::
118
+
119
+ sage: from sage.combinat.key_polynomial import KeyPolynomialRing
120
+ sage: K = KeyPolynomialRing(QQ)
121
+ sage: R = K.polynomial_ring(); R
122
+ Infinite polynomial ring in x over Rational Field
123
+ sage: z, = R.gens()
124
+ sage: f = z[1]*z[2]^3 + z[1]*z[2]*z[3]
125
+ sage: K._divided_difference(2, f)
126
+ z[1]*z[2]^2 - z[1]*z[3]^2
127
+
128
+ """
129
+ out = self .polynomial_ring ().zero ()
130
+ # linearly extend the divided difference on monomials
131
+ for m in f .monomials ():
132
+ out += f .monomial_coefficient (m ) * self ._divided_difference_on_monomial (i , m )
133
+ return out
134
+
135
+ def _divided_difference_on_monomial (self , i , m ):
136
+ r"""
137
+ Apply the `i`th divided difference operator to `m`.
138
+
139
+ NOTE:: `i` can be 0.
140
+
141
+ EXAMPLES::
142
+
143
+ sage: from sage.combinat.key_polynomial import KeyPolynomialRing
144
+ sage: K = KeyPolynomialRing(QQ)
145
+ sage: R = K.polynomial_ring(); R
146
+ Infinite polynomial ring in x over Rational Field
147
+ sage: z, = R.gens()
148
+ sage: K._divided_difference_on_monomial(1, z[1]*z[2]^3)
149
+ z_2^2*z_1 + z_2*z_1^2
150
+ sage: K._divided_difference_on_monomial(2, z[1]*z[2]*z[3])
151
+ 0
152
+ sage: K._divided_difference_on_monomial(3, z[1]*z[2]*z[3])
153
+ z[2]*z[1]
154
+ sage: K._divided_difference_on_monomial(3, z[1]*z[2]*z[4])
155
+ -z[2]*z[1]
156
+
157
+ TODO::
158
+ add examples where there are multiple factors of xi-x(i+1)
159
+
160
+ """
161
+ # The polynomial ring and generators
162
+ R = self .polynomial_ring ()
163
+ x , = R .gens ()
164
+
165
+ # The exponent vector for the monomial
166
+ exp = list (reversed (m .exponents ()[0 ]))
167
+
168
+ if i >= len (exp ) - 1 :
169
+ if i == len (exp ) - 1 :
170
+ exp = exp + [0 ]
171
+ else :
172
+ # if the transposition acts on two varibles which aren't
173
+ # present, then the numerator is f - f == 0.
174
+ return R .zero ()
175
+
176
+ # Apply the transposition s_i to the exponent vector
177
+ si_exp = exp [:i ] + [exp [i + 1 ], exp [i ]] + exp [i + 2 :]
178
+
179
+ # Create the corresponding list of variables in the monomial
180
+ terms = list (x [i ]** j for i ,j in enumerate (si_exp ) if j )
181
+
182
+ # Create the monomial from the list of variables
183
+ si_m = R .prod (terms )
184
+
185
+ # Create the numerator of the divided difference operator
186
+ f = si_m - m
187
+
188
+ # Division using the / operator is not implemented for
189
+ # InfinitePolynomialRing, so we want to remove the factor of
190
+ # x[i+1] - x[i]. If x[i+1] - x[i] is not a factor, it is because
191
+ # the numerator is zero.
192
+ try :
193
+ factors = f .factor ()
194
+ factors_dict = dict (factors )
195
+ except ArithmeticError : # if f is zero already
196
+ return R .zero ()
197
+
198
+ factors_dict [x [i + 1 ] - x [i ]] = factors_dict [x [i + 1 ] - x [i ]] - 1
199
+ return R .prod (k ** v for k ,v in factors_dict .items ()) * factors .unit ()
200
+
201
+ def _sorting_permutation (alpha ):
202
+ r"""
203
+ Get the sorting permutation for a composition ``alpha``.
204
+
205
+ The sorting permutation is the permutation of minimal length
206
+ which turns ``alpha`` into a partition.
207
+ """
208
+ key = lambda x : (x [1 ], x [0 ])
209
+ p = [i for i ,j in sorted (list (enumerate (a ._list , start = 1 )), key = key , reverse = True )]
210
+ return Permutation (p )
0 commit comments