Skip to content
This repository was archived by the owner on Jan 30, 2023. It is now read-only.

Commit 5899abf

Browse files
committed
Fix divided difference
1 parent 9c2a107 commit 5899abf

File tree

1 file changed

+147
-99
lines changed

1 file changed

+147
-99
lines changed

src/sage/combinat/key_polynomial.py

+147-99
Original file line numberDiff line numberDiff line change
@@ -26,110 +26,23 @@
2626
# https://www.gnu.org/licenses/
2727
# ****************************************************************************
2828

29+
from sage.categories.graded_algebras_with_basis import GradedAlgebrasWithBasis
2930
from sage.combinat.free_module import CombinatorialFreeModule
30-
from sage.combinat.compositions import Compositions
31+
from sage.combinat.composition import Composition, Compositions
3132
from sage.misc.inherit_comparison import InheritComparisonClasscallMetaclass
32-
33-
from sage.rings.rational_field import QQ
3433
from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing
34+
from sage.rings.rational_field import QQ
3535

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):
7237

73-
@cachedmethod
74-
def from_polynomial(self):
38+
def expand(self):
7539
r"""
76-
40+
Return ``self`` written in the monomial basis.
7741
"""
42+
out = self.parent().zero()
7843

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)
13346

13447

13548
class KeyPolynomialRing(CombinatorialFreeModule):
@@ -149,14 +62,149 @@ def __init__(self, R):
14962
self._name = "Ring of key polynomials"
15063
self._repr_option_bracket = False
15164
CombinatorialFreeModule.__init__(self, R, Compositions(),
152-
#category=GradedAlgebrasWithBasis(R),
65+
category=GradedAlgebrasWithBasis(R),
15366
prefix='k')
15467

155-
def _element_constructor_(self, parent, alpha):
68+
def _element_constructor_(self, alpha):
69+
#todo: add a case where alpha is an InfinitePolynomialRing element
15670
return self.element_class(self, alpha)
15771

15872
def polynomial_ring(self):
15973
r"""
16074
Return the polynomial ring associated to ``self``.
16175
"""
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

Comments
 (0)