Skip to content

Commit

Permalink
gh-38729: provide monomial_coefficients for polynomials and allow sin…
Browse files Browse the repository at this point in the history
…gle argument for MPolynomialRing_base.monomial

    
The goal of this pull request is to allow generic programming with
instances of `CombinatorialFreeModule` and polynomial rings.

In particular, we provide an alias `monomial_coefficients` for the
existing method `dict` of polynomial rings, and we allow to specify the
exponents in `monomial` of multivariate polynomial rings as a single
tuple.
    
URL: #38729
Reported by: Martin Rubey
Reviewer(s): Frédéric Chapoton, Martin Rubey, Travis Scrimshaw
  • Loading branch information
Release Manager committed Oct 11, 2024
2 parents 6f7bfad + 38904b6 commit 73874f0
Show file tree
Hide file tree
Showing 57 changed files with 417 additions and 265 deletions.
7 changes: 4 additions & 3 deletions src/sage/algebras/cluster_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -462,7 +462,7 @@ def d_vector(self) -> tuple:
sage: x.d_vector()
(1, 1, 2, -2)
"""
monomials = self.lift().dict()
monomials = self.lift().monomial_coefficients()
minimal = map(min, zip(*monomials))
return tuple(-vector(minimal))[:self.parent().rank()]

Expand Down Expand Up @@ -615,8 +615,9 @@ def theta_basis_decomposition(self):
f_poly = components[g_vect].F_polynomial()
g_vect = vector(g_vect)
while f_poly != zero_U:
y_exp = min(f_poly.dict())
coeff = f_poly.dict()[y_exp]
coeffs = f_poly.monomial_coefficients()
y_exp = min(coeffs)
coeff = coeffs[y_exp]
g_theta = tuple(g_vect + B * vector(y_exp))
out[g_theta] = out.get(g_theta, zero_A) + A({zero_t + tuple(y_exp): coeff})
f_poly -= U({y_exp: coeff}) * A.theta_basis_F_polynomial(g_theta)
Expand Down
41 changes: 24 additions & 17 deletions src/sage/algebras/commutative_dga.py
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ def image_monomial(exponent):
return A.zero()

for g in I.gens():
d = g.dict()
d = g.monomial_coefficients()
res = A.sum(d[ex] * image_monomial(ex) for ex in d)
if not res.is_zero():
raise ValueError("the differential does not preserve the ideal")
Expand Down Expand Up @@ -303,7 +303,7 @@ def _call_(self, x):
if x.is_zero():
return self.codomain().zero()
res = self.codomain().zero()
dic = x.dict()
dic = x.monomial_coefficients()
for key in dic:
keyl = list(key)
coef = dic[key]
Expand Down Expand Up @@ -392,11 +392,11 @@ def differential_matrix(self, n):
A = self.domain()
dom = A.basis(n)
cod = A.basis(n + 1)
cokeys = [next(iter(a.lift().dict().keys())) for a in cod]
cokeys = [next(iter(a.lift().monomial_coefficients().keys())) for a in cod]
m = matrix(A.base_ring(), len(dom), len(cod))
for i, domi in enumerate(dom):
im = self(domi)
dic = im.lift().dict()
dic = im.lift().monomial_coefficients()
for j in dic.keys():
k = cokeys.index(j)
m[i, k] = dic[j]
Expand Down Expand Up @@ -670,11 +670,11 @@ def differential_matrix_multigraded(self, n, total=False):
n = G(vector(n))
dom = A.basis(n)
cod = A.basis(n + self._degree_of_differential)
cokeys = [next(iter(a.lift().dict().keys())) for a in cod]
cokeys = [next(iter(a.lift().monomial_coefficients().keys())) for a in cod]
m = matrix(self.base_ring(), len(dom), len(cod))
for i, domi in enumerate(dom):
im = self(domi)
dic = im.lift().dict()
dic = im.lift().monomial_coefficients()
for j in dic.keys():
k = cokeys.index(j)
m[i, k] = dic[j]
Expand Down Expand Up @@ -1182,7 +1182,7 @@ def basis(self, n):
basis = []
for v in free_basis:
el = prod([self.gen(i)**v[i] for i in range(len(v))])
di = el.dict()
di = el.monomial_coefficients()
if len(di) == 1:
k, = di.keys()
if tuple(k) == v:
Expand Down Expand Up @@ -1477,7 +1477,7 @@ def degree(self, total=False):
"""
if self.is_zero():
raise ValueError("the zero element does not have a well-defined degree")
exps = self.lift().dict().keys()
exps = self.monomial_coefficients().keys()
degrees = self.parent()._degrees
n = self.parent().ngens()
l = [sum(e[i] * degrees[i] for i in range(n)) for e in exps]
Expand Down Expand Up @@ -1548,7 +1548,7 @@ def homogeneous_parts(self):
sage: a.homogeneous_parts()
{1: -2*e3 + e5, 2: e1*e2, 3: e1*e3*e5 - 3*e2*e3*e5}
"""
dic = self.dict()
dic = self.monomial_coefficients()
terms = [self.parent()({t: dic[t]}) for t in dic.keys()]
res = {}
for term in terms:
Expand All @@ -1559,7 +1559,7 @@ def homogeneous_parts(self):
res[deg] = term
return {i: res[i] for i in sorted(res.keys())}

def dict(self):
def monomial_coefficients(self):
r"""
A dictionary that determines the element.
Expand All @@ -1569,11 +1569,18 @@ def dict(self):
EXAMPLES::
sage: A.<x,y,z,t> = GradedCommutativeAlgebra(QQ, degrees=(1, 2, 2, 3))
sage: dic = (x*y - 5*y*z + 7*x*y^2*z^3*t).dict()
sage: sorted(dic.items())
sage: elt = x*y - 5*y*z + 7*x*y^2*z^3*t
sage: sorted(elt.monomial_coefficients().items())
[((0, 1, 1, 0), -5), ((1, 1, 0, 0), 1), ((1, 2, 3, 1), 7)]
``dict`` is an alias::
sage: sorted(elt.dict().items())
[((0, 1, 1, 0), -5), ((1, 1, 0, 0), 1), ((1, 2, 3, 1), 7)]
"""
return self.lift().dict()
return self.lift().monomial_coefficients()

dict = monomial_coefficients

def __call__(self, *values, **kwargs):
r"""
Expand Down Expand Up @@ -1645,8 +1652,8 @@ def __call__(self, *values, **kwargs):
if gstr in kwargs:
images[i] = kwargs[gstr]
res = 0
for (m, c) in self.dict().items():
term = prod((gen**y for (y, gen) in zip(m, images)), c)
for m, c in self.monomial_coefficients().items():
term = prod((gen ** y for y, gen in zip(m, images)), c)
res += term
return res

Expand Down Expand Up @@ -2000,7 +2007,7 @@ def degree(self, total=False):
raise ValueError("the zero element does not have a well-defined degree")
degrees = self.parent()._degrees_multi
n = self.parent().ngens()
exps = self.lift().dict().keys()
exps = self.monomial_coefficients().keys()
l = [sum(exp[i] * degrees[i] for i in range(n)) for exp in exps]
if len(set(l)) == 1:
return l[0]
Expand Down Expand Up @@ -3861,7 +3868,7 @@ def _call_(self, x):
"""
codomain = self.codomain()
result = codomain.zero()
for mono, coeff in x.dict().items():
for mono, coeff in x.monomial_coefficients().items():
term = prod([gen**y for (y, gen) in zip(mono, self.im_gens())],
codomain.one())
result += coeff * term
Expand Down
2 changes: 1 addition & 1 deletion src/sage/algebras/free_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -651,7 +651,7 @@ def exp_to_monomial(T):
return M([(i % ngens, Ti) for i, Ti in enumerate(T) if Ti])

return self.element_class(self, {exp_to_monomial(T): c
for T, c in x.letterplace_polynomial().dict().items()})
for T, c in x.letterplace_polynomial().monomial_coefficients().items()})
# ok, not a free algebra element (or should not be viewed as one).
if isinstance(x, str):
from sage.misc.sage_eval import sage_eval
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -273,7 +273,7 @@ cdef get_reduced_hexagons(factory, tuple mp_params):
if i % n_proc == child_id:
he = req_cy(basis, r_matrix, fvars, _Nk_ij, id_anyon, sextuple)
if he:
red = reduce_poly_dict(he.dict(), _nnz, _ks, one)
red = reduce_poly_dict(he.monomial_coefficients(), _nnz, _ks, one)

# Avoid pickling cyclotomic coefficients
red = _flatten_coeffs(red)
Expand Down Expand Up @@ -341,7 +341,7 @@ cdef get_reduced_pentagons(factory, tuple mp_params):
if i % n_proc == child_id:
pe = feq_cy(basis, fvars, _Nk_ij, id_anyon, zero, nonuple, prune=True)
if pe:
red = reduce_poly_dict(pe.dict(), _nnz, _ks, one)
red = reduce_poly_dict(pe.monomial_coefficients(), _nnz, _ks, one)

# Avoid pickling cyclotomic coefficients
red = _flatten_coeffs(red)
Expand Down
2 changes: 1 addition & 1 deletion src/sage/algebras/fusion_rings/poly_tup_engine.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ cpdef inline tuple poly_to_tup(MPolynomial_libsingular poly):
sage: poly_to_tup(x**2*y**4 - 4/5*x*y**2 + 1/3 * y)
(((2, 4), 1), ((1, 2), -4/5), ((0, 1), 1/3))
"""
return tuple(poly.dict().items())
return tuple(poly.monomial_coefficients().items())

cpdef inline MPolynomial_libsingular _tup_to_poly(tuple eq_tup, MPolynomialRing_libsingular parent):
r"""
Expand Down
2 changes: 1 addition & 1 deletion src/sage/algebras/hecke_algebras/cubic_hecke_base_ring.py
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ def _act_(self, perm, pol):
if not self.is_left():
perm, pol = pol, perm
pol_dict = {}
for key, value in pol.dict().items():
for key, value in pol.monomial_coefficients().items():
newkey = [0] * len(key)
for pos, k in enumerate(key):
newkey[perm(pos + 1) - 1] = k
Expand Down
2 changes: 1 addition & 1 deletion src/sage/algebras/iwahori_hecke_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -83,7 +83,7 @@ def normalized_laurent_polynomial(R, p):
u + v^-1 + u^-1
"""
try:
return R({k: R._base(c) for k, c in p.dict().items()})
return R({k: R._base(c) for k, c in p.monomial_coefficients().items()})
except (AttributeError, TypeError):
return R(p)

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -139,8 +139,8 @@ cdef class FreeAlgebraElement_letterplace(AlgebraElement):
sage: sorted(p) # indirect doctest
[((0, 0, 0, 1, 0, 0, 0, 1), 2), ((0, 1, 0, 0, 0, 0, 1, 0), 1)]
"""
cdef dict d = self._poly.dict()
yield from d.iteritems()
cdef dict d = self._poly.monomial_coefficients()
yield from d.items()

def _repr_(self):
"""
Expand Down
4 changes: 2 additions & 2 deletions src/sage/algebras/quantum_clifford.py
Original file line number Diff line number Diff line change
Expand Up @@ -533,7 +533,7 @@ def product_on_basis(self, m1, m2):
poly *= self._w_poly.monomial(*v)
poly = poly.reduce([vp[i]**(4*k) - (1 + q**(-2*k)) * vp[i]**(2*k) + q**(-2*k)
for i in range(self._n)])
pdict = poly.dict()
pdict = poly.monomial_coefficients()
ret = {(self._psi(p), tuple(e)): pdict[e] * q**q_power * sign
for e in pdict}

Expand Down Expand Up @@ -610,7 +610,7 @@ def inverse(self):
for wi in wp})
poly = poly.reduce([wi**(4*k) - (1 + q**(-2*k)) * wi**(2*k) + q**(-2*k)
for wi in wp])
pdict = poly.dict()
pdict = poly.monomial_coefficients()
coeff = coeff.inverse_of_unit()
ret = {(p, tuple(e)): coeff * c for e, c in pdict.items()}
return Cl.element_class(Cl, ret)
Expand Down
8 changes: 4 additions & 4 deletions src/sage/algebras/rational_cherednik_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,11 +328,11 @@ def product_on_basis(self, left, right):
def commute_w_hd(w, al): # al is given as a dictionary
ret = P.one()
for k in al:
x = sum(c * gens_dict[i] for i,c in alpha[k].weyl_action(w))
x = sum(c * gens_dict[i] for i, c in alpha[k].weyl_action(w))
ret *= x**al[k]
ret = ret.dict()
ret = ret.monomial_coefficients()
for k in ret:
yield (self._hd({I[i]: e for i,e in enumerate(k) if e != 0}), ret[k])
yield (self._hd({I[i]: e for i, e in enumerate(k) if e != 0}), ret[k])

# Do Lac Ra if they are both non-trivial
if dl and dr:
Expand Down Expand Up @@ -374,7 +374,7 @@ def commute_w_hd(w, al): # al is given as a dictionary
for i,c in alphacheck[k].weyl_action(right[1].reduced_word(),
inverse=True))
ret *= x**dl[k]
ret = ret.dict()
ret = ret.monomial_coefficients()
w = left[1]*right[1]
return self._from_dict({(left[0], w,
self._h({I[i]: e for i,e in enumerate(k)
Expand Down
15 changes: 11 additions & 4 deletions src/sage/algebras/splitting_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,26 @@ def is_unit(self):

return super().is_unit()

def dict(self):
def monomial_coefficients(self):
r"""
Return the dictionary of ``self`` according to its lift to the cover.
EXAMPLES::
sage: from sage.algebras.splitting_algebra import SplittingAlgebra
sage: CR3.<e3> = SplittingAlgebra(cyclotomic_polynomial(3))
sage: (e3 + 42).dict()
sage: f = e3 + 42
sage: f.monomial_coefficients()
{0: 42, 1: 1}
``dict`` is an alias::
sage: f.dict()
{0: 42, 1: 1}
"""
return self.lift().dict()
return self.lift().monomial_coefficients()

dict = monomial_coefficients

# ------------------------------------------------------------------------------------------------------------------
# Parent class of the splitting algebra
Expand Down Expand Up @@ -282,7 +289,7 @@ def __init__(self, monic_polynomial, names='X', iterate=True, warning=True):
root_names_reduces.remove(root_name)

P = base_ring_step[root_names_reduces[0]]
p = P(monic_polynomial.dict())
p = P(monic_polynomial.monomial_coefficients())
q, _ = p.quo_rem(P.gen() - first_root)

verbose("Invoking recursion with: %s" % (q,))
Expand Down
2 changes: 1 addition & 1 deletion src/sage/algebras/weyl_algebra.py
Original file line number Diff line number Diff line change
Expand Up @@ -786,7 +786,7 @@ def _element_constructor_(self, x):
return self.element_class(self, {i: R(c) for i, c in x if R(c) != zero})
x = self._poly_ring(x)
return self.element_class(self, {(tuple(m), t): c
for m, c in x.dict().items()})
for m, c in x.monomial_coefficients().items()})

def _coerce_map_from_(self, R):
"""
Expand Down
2 changes: 1 addition & 1 deletion src/sage/combinat/ncsf_qsym/qsym.py
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,7 @@ def from_polynomial(self, f, check=True):
-F[1, 1] + F[2]
"""
assert self.base_ring() == f.base_ring()
exponent_coefficient = f.dict()
exponent_coefficient = f.monomial_coefficients()
z = {}
for e, c in exponent_coefficient.items():
I = Compositions()([ei for ei in e if ei])
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ def from_polynomial(self, p):
"""
L = self.basis().keys()
return self.sum_of_terms((L.from_vector(vector(t)), c)
for (t,c) in p.dict().items())
for t, c in p.monomial_coefficients().items())

@cached_method
def divided_difference_on_basis(self, weight, i):
Expand Down
23 changes: 14 additions & 9 deletions src/sage/combinat/sf/monomial.py
Original file line number Diff line number Diff line change
Expand Up @@ -135,21 +135,25 @@ def product(self, left, right):
return self._from_dict(z_elt)

def from_polynomial(self, f, check=True):
"""
Return the symmetric function in the monomial basis corresponding to the polynomial ``f``.
r"""
Return the symmetric function in the monomial basis corresponding
to the polynomial ``f``.
INPUT:
- ``self`` -- a monomial symmetric function basis
- ``f`` -- a polynomial in finitely many variables over the same base ring as ``self``;
it is assumed that this polynomial is symmetric
- ``check`` -- boolean (default: ``True``); checks whether the polynomial is indeed symmetric
- ``f`` -- a polynomial in finitely many variables over the
same base ring as ``self``; it is assumed that this
polynomial is symmetric
- ``check`` -- boolean (default: ``True``); checks whether
the polynomial is indeed symmetric
OUTPUT:
- This function converts a symmetric polynomial `f` in a polynomial ring in finitely
many variables to a symmetric function in the monomial
basis of the ring of symmetric functions over the same base ring.
- This function converts a symmetric polynomial `f` in a
polynomial ring in finitely many variables to a symmetric
function in the monomial basis of the ring of symmetric
functions over the same base ring.
EXAMPLES::
Expand All @@ -173,12 +177,13 @@ def from_polynomial(self, f, check=True):
sage: f = (2*m[2,1]+m[1,1]+3*m[3]).expand(3)
sage: m.from_polynomial(f)
m[1, 1] + 2*m[2, 1] + 3*m[3]
"""
assert self.base_ring() == f.base_ring()
if check and not f.is_symmetric():
raise ValueError("%s is not a symmetric polynomial" % f)
out = self._from_dict({_Partitions.element_class(_Partitions, list(e)): c
for (e,c) in f.dict().items()
for e, c in f.monomial_coefficients().items()
if all(e[i+1] <= e[i] for i in range(len(e)-1))},
remove_zeros=False)
return out
Expand Down
2 changes: 1 addition & 1 deletion src/sage/combinat/sf/sfa.py
Original file line number Diff line number Diff line change
Expand Up @@ -6900,7 +6900,7 @@ def _from_polynomial(p, f):
n = p.parent().ngens()
if n == 1:
d = {_Partitions.from_exp([e]): c
for e, c in p.dict().items()}
for e, c in p.monomial_coefficients().items()}
else:
d = {_Partitions.from_exp(e): c
for e, c in p.iterator_exp_coeff(False)}
Expand Down
Loading

0 comments on commit 73874f0

Please sign in to comment.