From 2a137f51f9081f40ca440d91f61d6206e492671e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 30 Nov 2022 16:18:54 +0100 Subject: [PATCH 01/13] adding the ring of integer-valued polynomials --- .../polynomial_rings_univar.rst | 2 + src/sage/rings/polynomial/all.py | 11 +- .../polynomial/integer_valued_polynomials.py | 1125 +++++++++++++++++ 3 files changed, 1134 insertions(+), 4 deletions(-) create mode 100644 src/sage/rings/polynomial/integer_valued_polynomials.py diff --git a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst index 414c04bb611..97cff1bb835 100644 --- a/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst +++ b/src/doc/en/reference/polynomial_rings/polynomial_rings_univar.rst @@ -43,3 +43,5 @@ whereas others have multiple bases. sage/rings/polynomial/polynomial_compiled sage/rings/polynomial/polynomial_fateman + + sage/rings/polynomial/integer_valued_polynomials diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index 816db5efe2a..853f422bdc7 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -1,8 +1,7 @@ """ Polynomials """ - -#***************************************************************************** +# **************************************************************************** # Copyright (C) 2005 William Stein # # Distributed under the terms of the GNU General Public License (GPL) @@ -14,8 +13,8 @@ # # The full text of the GPL is available at: # -# http://www.gnu.org/licenses/ -#***************************************************************************** +# https://www.gnu.org/licenses/ +# **************************************************************************** from sage.misc.lazy_import import lazy_import @@ -51,3 +50,7 @@ # Evaluation of cyclotomic polynomials from sage.rings.polynomial.cyclotomic import cyclotomic_value + +# Integer-valued Univariate Polynomial Ring +lazy_import('sage.rings.polynomial.integer_valued_polynomials', + 'IntegerValuedPolynomialRing') diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py new file mode 100644 index 00000000000..8bf54acb8d6 --- /dev/null +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -0,0 +1,1125 @@ +# -*- coding: utf-8 -*- +r""" +Integer-valued polynomial rings + +AUTHORS: + +- Frédéric Chapoton (2013-03): Initial version +""" +# *************************************************************************** +# Copyright (C) 2013 Frédéric Chapoton +# +# Distributed under the terms of the GNU General Public License (GPL) +# https://www.gnu.org/licenses/ +# *************************************************************************** +from sage.arith.misc import (binomial, factorial) +from sage.categories.rings import Rings +from sage.categories.all import Algebras +from sage.categories.realizations import Category_realization_of_parent +from sage.combinat.free_module import CombinatorialFreeModule +from sage.matrix.constructor import matrix +from sage.misc.cachefunc import cached_method +from sage.modules.free_module_element import vector +from sage.rings.integer import Integer +from sage.rings.integer_ring import ZZ +from sage.rings.polynomial.polynomial_ring import polygen +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.rational_field import QQ +from sage.sets.non_negative_integers import NonNegativeIntegers +from sage.sets.family import Family +from sage.misc.bindable_class import BindableClass +from sage.structure.unique_representation import UniqueRepresentation +from sage.structure.parent import Parent + + +class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): + r""" + The integer-valued polynomial ring over a base ring. + + Integer-valued polynomial rings are commutative and associative + algebras, with a basis indexed by non-negative integers. + + There are two natural bases, made of the sequence + `\binom{x}{n}` for `n \geq 0` (the *binomial basis*) and of + the other sequence `\binom{x+n}{n}` for `n \geq 0` (the *shifted basis*). + + TESTS:: + + sage: IntegerValuedPolynomialRing(24) + Traceback (most recent call last): + ... + TypeError: argument R must be a ring + """ + def __init__(self, R): + if R not in Rings(): + raise TypeError("argument R must be a ring") + self._base = R + cat = Algebras(R).Commutative().WithBasis() + Parent.__init__(self, base=R, category=cat.WithRealizations()) + + S = self.S() + B = self.B() + B.module_morphism(S._from_binomial_basis, codomain=S).register_as_coercion() + S.module_morphism(B._from_shifted_basis, codomain=B).register_as_coercion() + + _shorthands = ["B", "S"] + + def a_realization(self): + r""" + Return the default realization of ``self``. + + This is the shifted basis. + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ) + sage: A.a_realization() + Integer-Valued Polynomial Ring over Rational Field + in the shifted basis + """ + return self.S() + + def indices(self): + r""" + Return the objects that index the basis elements of this algebra. + + This is the set `\NN` of nonnegative integers. + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ) + sage: A.indices() + Non negative integers + """ + return NonNegativeIntegers() + + def _repr_(self) -> str: + r""" + Return the string representation. + + EXAMPLES:: + + sage: IntegerValuedPolynomialRing(QQ) + Integer-Valued Polynomial Ring over Rational Field + """ + br = self.base_ring() + return f"Integer-Valued Polynomial Ring over {br}" + + class Bases(Category_realization_of_parent): + def super_categories(self) -> list: + r""" + Return the super-categories of ``self``. + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ); A + Integer-Valued Polynomial Ring over Rational Field + sage: C = A.Bases(); C + Category of bases of Integer-Valued Polynomial Ring + over Rational Field + sage: C.super_categories() + [Category of realizations of Integer-Valued Polynomial Ring + over Rational Field, + Join of Category of algebras with basis over Rational Field and + Category of commutative algebras over Rational Field and + Category of realizations of unital magmas] + """ + A = self.base() + category = Algebras(A.base_ring()).Commutative() + return [A.Realizations(), + category.Realizations().WithBasis()] + + class ParentMethods: + def _repr_(self) -> str: + r""" + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(QQ).S() + sage: F # indirect doctest + Integer-Valued Polynomial Ring over Rational Field + in the shifted basis + sage: F = IntegerValuedPolynomialRing(QQ).B() + sage: F # indirect doctest + Integer-Valued Polynomial Ring over Rational Field + in the binomial basis + """ + real = self.realization_of() + return f"{real} in the {self._realization_name()} basis" + + @cached_method + def one_basis(self): + r""" + Return the number 0, which index the unit of this algebra. + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ).S() + sage: A.one_basis() + 0 + sage: A.one() + S[0] + """ + return self.basis().keys()(0) + + def gen(self): + r""" + Return the generator of this algebra. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: F.gen() + B[1] + """ + return self.algebra_generators()[0] + + @cached_method + def algebra_generators(self): + r""" + Return the generators of this algebra. + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).S(); A + Integer-Valued Polynomial Ring over Integer Ring + in the shifted basis + sage: A.algebra_generators() + Family (S[1],) + """ + NonNeg = self.basis().keys() + return Family([self.monomial(NonNeg(1))]) + + gens = algebra_generators + + class ElementMethods: + def __call__(self, v): + """ + Evaluation at some value ``v`` + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.gen() + sage: f = B**2+4*B+6 + sage: f(1/3) + 118/9 + + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: B = F.gen() + sage: f = B**2+4*B+6 + sage: f(1/3) + 67/9 + """ + return self.polynomial()(v) + + def shift(self, j=1): + """ + Shift all indices by `j`. + + INPUT: + + - `j` -- integer (default: 1) + + In the binomial basis, the shift by 1 corresponds to + a summation operator from `0` to `x`. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: B = F.gen() + sage: (B+1).shift() + B[1] + B[2] + sage: (B+1).shift(3) + B[3] + B[4] + """ + A = self.parent() + return A._from_dict({i + j: c for i, c in self}) + + class Shifted(CombinatorialFreeModule, BindableClass): + r""" + The integer-valued polynomial ring in the shifted basis. + + Integer-valued polynomial rings are commutative and associative + algebras, with a basis indexed by non-negative integers. + + The basis used here is given by `B[i] = \binom{i+n}{i}` for `i \in \NN`. + + Assuming `n_1 \leq n_2`, the product of two monomials `B[n_1] \cdot B[n_2]` + is given by the sum + + .. MATH:: + + \sum_{k=0}^{n_1} (-1)^k \binom{n_1}{k}\binom{n_1+n_2-k}{n_1} B[n_1 + n_2 - k]. + + There is a conversion formula between the two bases + + .. MATH:: + + \binom{x}{i} = \sum_{k=0}^{i} (-1)^{i-k} \binom{i}{k} \binom{x+k}{k}. + + + REFERENCES: + + - :wikipedia:`Integer-valued polynomial` + + INPUT: + + - ``A`` -- a parent with realization + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(QQ).S(); F + Integer-Valued Polynomial Ring over Rational Field + in the shifted basis + + sage: F.gen() + S[1] + + sage: S = IntegerValuedPolynomialRing(ZZ).S(); S + Integer-Valued Polynomial Ring over Integer Ring + in the shifted basis + sage: S.base_ring() + Integer Ring + + sage: G = IntegerValuedPolynomialRing(S).S(); G + Integer-Valued Polynomial Ring over Integer-Valued Polynomial + Ring over Integer Ring in the shifted basis in the shifted basis + sage: G.base_ring() + Integer-Valued Polynomial Ring over Integer Ring + in the shifted basis + + Integer-valued polynomial rings commute with their base ring:: + + sage: K = IntegerValuedPolynomialRing(QQ).S() + sage: a = K.gen() + sage: K.is_commutative() + True + sage: L = IntegerValuedPolynomialRing(K).S() + sage: c = L.gen() + sage: L.is_commutative() + True + sage: s = a * c^3; s + S[1]*S[1] + (-6*S[1])*S[2] + 6*S[1]*S[3] + sage: parent(s) + Integer-Valued Polynomial Ring over Integer-Valued Polynomial + Ring over Rational Field in the shifted basis in the shifted basis + + Integer-valued polynomial rings are commutative:: + + sage: c^3 * a == c * a * c * c + True + + We can also manipulate elements in the basis and + coerce elements from our base field:: + + sage: F = IntegerValuedPolynomialRing(QQ).S() + sage: S = F.basis() + sage: S[2] * S[3] + 3*S[3] - 12*S[4] + 10*S[5] + sage: 1 - S[2] * S[2] / 2 + S[0] - 1/2*S[2] + 3*S[3] - 3*S[4] + """ + def __init__(self, A): + r""" + Initialize ``self``. + + INPUT: + + - ``A`` -- a parent with realization + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(QQ).S(); F + Integer-Valued Polynomial Ring over Rational Field + in the shifted basis + sage: TestSuite(F).run() + """ + CombinatorialFreeModule.__init__(self, A.base_ring(), + A.indices(), + category=A.Bases(), + prefix="S", + latex_prefix=r"\mathbb{S}") + + def _realization_name(self) -> str: + r""" + TESTS:: + + sage: F = IntegerValuedPolynomialRing(QQ).S() + sage: F._realization_name() + 'shifted' + """ + return "shifted" + + # with @cached_method ? + def product_on_basis(self, n1, n2): + r""" + Return the product of basis elements ``n1`` and ``n2``. + + INPUT: + + - ``n1``, ``n2`` -- integers + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ).S() + sage: A.product_on_basis(0, 1) + S[1] + sage: A.product_on_basis(1, 2) + -2*S[2] + 3*S[3] + """ + i = ZZ(n1) + j = ZZ(n2) + if j < i: + j, i = i, j + + R = self.base_ring() + return self._from_dict({i + j - k: R((-1)**k * i.binomial(k) * (i + j - k).binomial(i)) + for k in range(i + 1)}) + + def _from_binomial_basis(self, i): + """ + Convert from the ``binomial(x,k)`` basis. + + INPUT: + + - ``i`` -- an integer + + EXAMPLES:: + + sage: S = IntegerValuedPolynomialRing(ZZ).S() + sage: B = IntegerValuedPolynomialRing(ZZ).B() + sage: b = B.basis() + sage: S(b[3]+1) # indirect doctest + 3*S[1] - 3*S[2] + S[3] + sage: B(_) + B[0] + B[3] + """ + i = ZZ(i) + R = self.base_ring() + return self._from_dict({k: R((-1)**(i - k) * i.binomial(k)) + for k in range(i + 1)}) + + def from_polynomial(self, p): + """ + Convert a polynomial into the ring of integer-valued polynomials. + + This raises a ``ValueError`` if this is not possible. + + INPUT: + + - `p` -- a polynomial in one variable + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: S = A.basis() + sage: S[5].polynomial() + 1/120*x^5 + 1/8*x^4 + 17/24*x^3 + 15/8*x^2 + 137/60*x + 1 + sage: A.from_polynomial(_) + S[5] + sage: x = polygen(QQ, 'x') + sage: A.from_polynomial(x) + -S[0] + S[1] + """ + B = self.basis() + x = p.parent().gen() + remain = p + result = self.zero() + while remain: + N = remain.degree() + top_coeff = remain.leading_coefficient() * factorial(N) + try: + top_coeff = self.base_ring()(top_coeff) + except TypeError as exc: + msg = 'not a polynomial with integer' + msg += f' values: {top_coeff}' + raise ValueError(msg) from exc + remain += -top_coeff * binomial(N + x, N) + result += top_coeff * B[N] + return result + + def from_h_vector(self, h): + """ + Convert from some `h`-vector. + + INPUT: + + - ``h`` -- a tuple or vector + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: S = A.basis() + sage: ex = S[2]+S[4] + sage: A.from_h_vector(ex.h_vector()) + S[2] + S[4] + """ + B = self.basis() + d = len(h) - 1 + m = matrix(QQ, d + 1, d + 1, + lambda j, i: (-1)**(d - j) * binomial(d - i, d - j)) + v = vector(QQ, [h[i] for i in range(d + 1)]) + return self.sum(Integer(c) * B[i] for i, c in enumerate(m * v)) + + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + INPUT: + + - ``x`` -- an integer or something convertible + + EXAMPLES:: + + sage: R = IntegerValuedPolynomialRing(QQ).S() + sage: x = R.gen() + sage: R(3) + 3*S[0] + sage: R(x) + S[1] + """ + if x in NonNegativeIntegers(): + W = self.basis().keys() + return self.monomial(W(x)) + + P = x.parent() + if isinstance(P, IntegerValuedPolynomialRing.Shifted): + if P is self: + return x + if P is not self.base_ring(): + return self.element_class(self, x.monomial_coefficients()) + + # ok, not a integer-valued polynomial ring element + R = self.base_ring() + # coercion via base ring + x = R(x) + if x == 0: + return self.element_class(self, {}) + return self.from_base_ring_from_one_basis(x) + + def _coerce_map_from_(self, R): + r""" + Return ``True`` if there is a coercion from ``R`` into ``self`` + and ``False`` otherwise. + + INPUT: + + - ``R`` -- a commutative ring + + The things that coerce into ``self`` are + + - Integer-Valued Polynomial Rings over a base + with a coercion map into ``self.base_ring()``. + + - Anything with a coercion into ``self.base_ring()``. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(GF(7)).S(); F + Integer-Valued Polynomial Ring over Finite Field of size 7 + in the shifted basis + + Elements of the integer-valued polynomial ring canonically + coerce in:: + + sage: x = F.gen() + sage: F.coerce(x*x) # indirect doctest + 6*S[1] + 2*S[2] + + Elements of the integers coerce in, since there is a coerce map + from `\ZZ` to GF(7):: + + sage: F.coerce(1) # indirect doctest + S[0] + + There is no coerce map from `\QQ` to `\GF{7}`:: + + sage: F.coerce(2/3) # indirect doctest + Traceback (most recent call last): + ... + TypeError: no canonical coercion from Rational Field to + Integer-Valued Polynomial Ring over Finite Field of size 7 + in the shifted basis + + Elements of the base ring coerce in:: + + sage: F.coerce(GF(7)(5)) + 5*S[0] + + The integer-valued polynomial ring over `\ZZ` on `x` coerces in, + since `\ZZ` coerces to `\GF{7}`:: + + sage: G = IntegerValuedPolynomialRing(ZZ).S() + sage: Gx = G.gen() + sage: z = F.coerce(Gx**2); z + -S[1] + 2*S[2] + sage: z.parent() is F + True + + However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle + algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: + + sage: G.coerce(x^3+x) + Traceback (most recent call last): + ... + TypeError: no canonical coercion from Integer-Valued Polynomial + Ring over Finite Field of size 7 in the shifted basis + to Integer-Valued Polynomial + Ring over Integer Ring in the shifted basis + + TESTS:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: G = IntegerValuedPolynomialRing(QQ).S() + sage: H = IntegerValuedPolynomialRing(ZZ).S() + sage: F._coerce_map_from_(G) + False + sage: G._coerce_map_from_(F) + True + sage: F._coerce_map_from_(H) + True + sage: F._coerce_map_from_(QQ) + False + sage: G._coerce_map_from_(QQ) + True + sage: F.has_coerce_map_from(PolynomialRing(ZZ,'x')) + False + """ + # integer-valued polynomial rings over any base + # that coerces in: + if isinstance(R, IntegerValuedPolynomialRing.Shifted): + return self.base_ring().has_coerce_map_from(R.base_ring()) + return self.base_ring().has_coerce_map_from(R) + + class Element(CombinatorialFreeModule.Element): + + def umbra(self): + """ + Return the Bernoulli umbra. + + This is the derivative at `-1` of the shift by one. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.gen() + sage: (B+1).umbra() + 3/2 + + TESTS:: + + sage: [(B**n).umbra() for n in range(1, 11)] + [1/2, 1/6, 0, -1/30, 0, 1/42, 0, -1/30, 0, 5/66] + """ + return self.shift().derivative_at_minus_one() + + def delta(self): + r""" + Return the image by the difference operator `\Delta`. + + The operator `\Delta` is defined on polynomials by + + .. MATH:: + + f \mapsto f(x+1)-f(x). + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: S = F.basis() + sage: S[5].delta() + S[0] + S[1] + S[2] + S[3] + S[4] + """ + return self.variable_shift() - self + + def variable_shift(self, k=1): + r""" + Return the image by the shift of variables. + + On polynomials, the action is the shift + on variables `x \mapsto x + 1`. + + INPUT: + + - `k` -- integer (default: 1) + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: S = A.basis() + sage: S[5].variable_shift() + S[0] + S[1] + S[2] + S[3] + S[4] + S[5] + """ + if k == 0: + return self + + A = self.parent() + resu = A.sum(c * A.monomial(j) for i, c in self + for j in range(i + 1)) + if k == 1: + return resu + return resu.variable_shift(k - 1) + + def variable_unshift(self, k=1): + r""" + Return the image by the unshift of variables. + + On polynomials, the action is the shift + on variables `x \mapsto x - k`. + + INPUT: + + - `k` -- integer (default: 1) + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: S = A.basis() + sage: S[5].variable_unshift() + -S[4] + S[5] + """ + if k == 0: + return self + + A = self.parent() + resu = (A.sum(c * A.monomial(i) for i, c in self) - + A.sum(c * A.monomial(i - 1) for i, c in self if i)) + if k == 1: + return resu + return resu.variable_unshift(k - 1) + + def derivative_at_minus_one(self): + """ + Return the derivative at `-1`. + + This is sometimes useful when `-1` is a root. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.gen() + sage: (B+1).derivative_at_minus_one() + 1 + """ + return QQ.sum(c / QQ(i) for i, c in self if i) + + special_value = derivative_at_minus_one + + def polynomial(self): + """ + Convert to a standard polynomial in `x`. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.gen() + sage: (B+1).polynomial() + x + 2 + """ + x = polygen(QQ, 'x') + return sum(c * binomial(x + i, i) for i, c in self) + + def h_vector(self): + """ + Return the numerator of the generating series of values. + + If ``self`` is an Ehrhart polynomial, this is the `h`-vector. + + .. SEEALSO:: :meth:`h_polynomial` + + EXAMPLES:: + + sage: x = polygen(QQ,'x') + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: ex = A.from_polynomial((1+x)**3) + sage: ex.h_vector() + (0, 1, 4, 1) + """ + d = max(self.support(), default=-1) + m = matrix(QQ, d + 1, d + 1, + lambda j, i: (-1)**(d - j) * (d - i).binomial(d - j)) + v = vector(QQ, [self.coefficient(i) for i in range(d + 1)]) + return m * v + + def h_polynomial(self): + """ + Return the `h`-vector as a polynomial. + + .. SEEALSO:: :meth:`h_vector` + + EXAMPLES:: + + sage: x = polygen(QQ,'x') + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: ex = A.from_polynomial((1+x)**3) + sage: ex.h_polynomial() + z^3 + 4*z^2 + z + """ + anneau = PolynomialRing(self.parent().base_ring(), 'z') + return anneau(list(self.h_vector())) + + def sum(self): + """ + Return the sum of coefficients. + + This is related to the evaluation at 0. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.basis() + sage: (B[2]*B[4]).sum() + 1 + """ + return sum(c for _, c in self) + + S = Shifted + + # ===== Another basis for the same algebra ===== + + class Binomial(CombinatorialFreeModule, BindableClass): + r""" + The integer-valued polynomial ring in the binomial basis. + + The basis used here is given by `B[i] = \binom{n}{i}` for `i \in \NN`. + + There is a conversion formula between the two bases + + .. MATH:: + + \binom{x+i}{i} = \sum_{k=0}^{i} \binom{i}{k} \binom{x}{k}. + + Assuming `n_1 \leq n_2`, the product of two monomials `B[n_1] \cdot B[n_2]` + is given by the sum + + .. MATH:: + + \sum_{k=0}^{n_1} \binom{n_1}{k}\binom{n_1+n_2-k}{n_1} B[n_1 + n_2 - k]. + + The product of two monomials is therefore a positive linear combination + of monomials. + + REFERENCES: + + - :wikipedia:`Integer-valued polynomial` + + INPUT: + + - ``R`` -- ring + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(QQ).B(); F + Integer-Valued Polynomial Ring over Rational Field + in the binomial basis + + sage: F.gen() + B[1] + + sage: S = IntegerValuedPolynomialRing(ZZ).B(); S + Integer-Valued Polynomial Ring over Integer Ring + in the binomial basis + sage: S.base_ring() + Integer Ring + + sage: G = IntegerValuedPolynomialRing(S).B(); G + Integer-Valued Polynomial Ring over Integer-Valued Polynomial Ring + over Integer Ring in the binomial basis in the binomial basis + sage: G.base_ring() + Integer-Valued Polynomial Ring over Integer Ring + in the binomial basis + + Integer-valued polynomial rings commute with their base ring:: + + sage: K = IntegerValuedPolynomialRing(QQ).B() + sage: a = K.gen() + sage: K.is_commutative() + True + sage: L = IntegerValuedPolynomialRing(K).B() + sage: c = L.gen() + sage: L.is_commutative() + True + sage: s = a * c^3; s + B[1]*B[1] + 6*B[1]*B[2] + 6*B[1]*B[3] + sage: parent(s) + Integer-Valued Polynomial Ring over Integer-Valued Polynomial + Ring over Rational Field in the binomial basis in the binomial basis + + Integer-valued polynomial rings are commutative:: + + sage: c^3 * a == c * a * c * c + True + + We can also manipulate elements in the basis:: + + sage: F = IntegerValuedPolynomialRing(QQ).B() + sage: B = F.basis() + sage: B[2] * B[3] + 3*B[3] + 12*B[4] + 10*B[5] + sage: 1 - B[2] * B[2] / 2 + B[0] - 1/2*B[2] - 3*B[3] - 3*B[4] + + and coerce elements from our base field:: + + sage: F(4/3) + 4/3*B[0] + """ + def __init__(self, A): + r""" + Initialize ``self``. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(QQ).B(); F + Integer-Valued Polynomial Ring over Rational Field + in the binomial basis + sage: TestSuite(F).run() + """ + CombinatorialFreeModule.__init__(self, A.base_ring(), A.indices(), + latex_prefix="", + category=A.Bases()) + + def _realization_name(self) -> str: + r""" + TESTS:: + + sage: F = IntegerValuedPolynomialRing(QQ).B() + sage: F._realization_name() + 'binomial' + """ + return "binomial" + + # with @cached_method ? + def product_on_basis(self, n1, n2): + r""" + Return the product of basis elements ``n1`` and ``n2``. + + INPUT: + + - ``n1``, ``n2`` -- integers + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ).B() + sage: A.product_on_basis(0, 1) + B[1] + sage: A.product_on_basis(1, 2) + 2*B[2] + 3*B[3] + """ + i = ZZ(n1) + j = ZZ(n2) + if j < i: + j, i = i, j + + R = self.base_ring() + return self._from_dict({i + j - k: + R(binomial(i, k) * binomial(i + j - k, i)) + for k in range(i + 1)}) + + def _from_shifted_basis(self, i): + """ + Convert from the shifted binomial(x+k,k) basis. + + INPUT: + + - ``i`` -- an integer + + EXAMPLES:: + + sage: S = IntegerValuedPolynomialRing(ZZ).S() + sage: B = IntegerValuedPolynomialRing(ZZ).B() + sage: s = S.basis() + sage: B(s[3]+1) # indirect doctest + 2*B[0] + 3*B[1] + 3*B[2] + B[3] + sage: S(_) + S[0] + S[3] + """ + i = ZZ(i) + R = self.base_ring() + return self._from_dict({k: R(i.binomial(k)) + for k in range(i + 1)}) + + def from_polynomial(self, p): + """ + Convert a polynomial into the ring of integer-valued polynomials. + + This raises a ``ValueError`` if this is not possible. + + INPUT: + + - ``p`` -- a polynomial in one variable + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).B() + sage: B = A.basis() + sage: B[5].polynomial() + 1/120*x^5 - 1/12*x^4 + 7/24*x^3 - 5/12*x^2 + 1/5*x + sage: A.from_polynomial(_) + B[5] + sage: x = polygen(QQ, 'x') + sage: A.from_polynomial(x) + B[1] + + TESTS:: + + sage: x = polygen(QQ,'x') + sage: A.from_polynomial(x+1/3) + Traceback (most recent call last): + ... + ValueError: not a polynomial with integer values + """ + B = self.basis() + x = p.parent().gen() + remain = p + result = self.zero() + while remain: + N = remain.degree() + top_coeff = remain.leading_coefficient() * factorial(N) + try: + top_coeff = self.base_ring()(top_coeff) + except TypeError as exc: + raise ValueError('not a polynomial with integer values') from exc + remain += -top_coeff * binomial(x, N) + result += top_coeff * B[N] + return result + + def _element_constructor_(self, x): + r""" + Convert ``x`` into ``self``. + + EXAMPLES:: + + sage: R = IntegerValuedPolynomialRing(QQ).B() + sage: x = R.gen() + sage: R(3) + 3*B[0] + sage: R(x) + B[1] + """ + if x in NonNegativeIntegers(): + W = self.basis().keys() + return self.monomial(W(x)) + + P = x.parent() + if isinstance(P, IntegerValuedPolynomialRing.Binomial): + if P is self: + return x + if P is not self.base_ring(): + return self.element_class(self, x.monomial_coefficients()) + + # ok, not a integer-valued polynomial ring element + R = self.base_ring() + # coercion via base ring + x = R(x) + if x == 0: + return self.element_class(self, {}) + return self.from_base_ring_from_one_basis(x) + + def _coerce_map_from_(self, R): + r""" + Return ``True`` if there is a coercion from ``R`` into ``self`` + and ``False`` otherwise. + + The things that coerce into ``self`` are + + - Integer-Valued Polynomial Rings over a base + with a coercion map into ``self.base_ring()``. + + - Anything with a coercion into ``self.base_ring()``. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(GF(7)).B(); F + Integer-Valued Polynomial Ring over Finite Field of size 7 + in the binomial basis + + Elements of the integer-valued polynomial ring canonically coerce + in:: + + sage: x = F.gen() + sage: F.coerce(x*x) # indirect doctest + B[1] + 2*B[2] + + Elements of the integers coerce in, since there is a coerce map + from `\ZZ` to GF(7):: + + sage: F.coerce(1) # indirect doctest + B[0] + + There is no coerce map from `\QQ` to `\GF{7}`:: + + sage: F.coerce(2/3) # indirect doctest + Traceback (most recent call last): + ... + TypeError: no canonical coercion from Rational Field to + Integer-Valued Polynomial Ring over Finite Field of size 7 + in the binomial basis + + Elements of the base ring coerce in:: + + sage: F.coerce(GF(7)(5)) + 5*B[0] + + The integer-valued polynomial ring over `\ZZ` on `x` coerces in, + since `\ZZ` coerces to `\GF{7}`:: + + sage: G = IntegerValuedPolynomialRing(ZZ).B() + sage: Gx = G.gen() + sage: z = F.coerce(Gx**2); z + B[1] + 2*B[2] + sage: z.parent() is F + True + + However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle + algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: + + sage: G.coerce(x^3+x) + Traceback (most recent call last): + ... + TypeError: no canonical coercion from Integer-Valued Polynomial + Ring over Finite Field of size 7 in the binomial basis to + Integer-Valued Polynomial Ring over Integer Ring + in the binomial basis + + TESTS:: + + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: G = IntegerValuedPolynomialRing(QQ).B() + sage: H = IntegerValuedPolynomialRing(ZZ).B() + sage: F._coerce_map_from_(G) + False + sage: G._coerce_map_from_(F) + True + sage: F._coerce_map_from_(H) + True + sage: F._coerce_map_from_(QQ) + False + sage: G._coerce_map_from_(QQ) + True + sage: F.has_coerce_map_from(PolynomialRing(ZZ,'x')) + False + """ + # integer-valued polynomial rings over any base + # that coerces in: + if isinstance(R, IntegerValuedPolynomialRing.Binomial): + return self.base_ring().has_coerce_map_from(R.base_ring()) + return self.base_ring().has_coerce_map_from(R) + + class Element(CombinatorialFreeModule.Element): + + def polynomial(self): + """ + Convert to a standard polynomial in `x`. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: B = F.gen() + sage: (B+1).polynomial() + x + 1 + """ + x = polygen(QQ, 'x') + return sum(c * binomial(x, i) for i, c in self) + + B = Binomial From 46a9c2404077a0923d5803582b2b1e7562a5186d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 26 Feb 2023 10:54:17 +0100 Subject: [PATCH 02/13] various details --- .../polynomial/integer_valued_polynomials.py | 61 ++++++++++++------- 1 file changed, 38 insertions(+), 23 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 8bf54acb8d6..7f730982c9a 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -48,11 +48,11 @@ class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): sage: IntegerValuedPolynomialRing(24) Traceback (most recent call last): ... - TypeError: argument R must be a ring + TypeError: argument R must be a commutative ring """ def __init__(self, R): - if R not in Rings(): - raise TypeError("argument R must be a ring") + if R not in Rings().Commutative(): + raise TypeError("argument R must be a commutative ring") self._base = R cat = Algebras(R).Commutative().WithBasis() Parent.__init__(self, base=R, category=cat.WithRealizations()) @@ -121,11 +121,12 @@ def super_categories(self) -> list: [Category of realizations of Integer-Valued Polynomial Ring over Rational Field, Join of Category of algebras with basis over Rational Field and + Category of filtered algebras over Rational Field and Category of commutative algebras over Rational Field and Category of realizations of unital magmas] """ A = self.base() - category = Algebras(A.base_ring()).Commutative() + category = Algebras(A.base_ring()).Commutative().Filtered() return [A.Realizations(), category.Realizations().WithBasis()] @@ -161,6 +162,18 @@ def one_basis(self): """ return self.basis().keys()(0) + def degree_on_basis(self, m): + r""" + Return the degree of the basis element indexed by ``m``. + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(QQ).S() + sage: A.degree_on_basis(4) # indirect doctest + 4 + """ + return ZZ(m) + def gen(self): r""" Return the generator of this algebra. @@ -262,10 +275,6 @@ class Shifted(CombinatorialFreeModule, BindableClass): - :wikipedia:`Integer-valued polynomial` - INPUT: - - - ``A`` -- a parent with realization - EXAMPLES:: sage: F = IntegerValuedPolynomialRing(QQ).S(); F @@ -323,10 +332,6 @@ def __init__(self, A): r""" Initialize ``self``. - INPUT: - - - ``A`` -- a parent with realization - EXAMPLES:: sage: F = IntegerValuedPolynomialRing(QQ).S(); F @@ -350,7 +355,6 @@ def _realization_name(self) -> str: """ return "shifted" - # with @cached_method ? def product_on_basis(self, n1, n2): r""" Return the product of basis elements ``n1`` and ``n2``. @@ -407,7 +411,7 @@ def from_polynomial(self, p): INPUT: - - `p` -- a polynomial in one variable + - ``p`` -- a polynomial in one variable EXAMPLES:: @@ -454,12 +458,12 @@ def from_h_vector(self, h): sage: A.from_h_vector(ex.h_vector()) S[2] + S[4] """ - B = self.basis() d = len(h) - 1 m = matrix(QQ, d + 1, d + 1, lambda j, i: (-1)**(d - j) * binomial(d - i, d - j)) v = vector(QQ, [h[i] for i in range(d + 1)]) - return self.sum(Integer(c) * B[i] for i, c in enumerate(m * v)) + return self._from_dict({i: Integer(c) + for i, c in enumerate(m * v)}) def _element_constructor_(self, x): r""" @@ -654,8 +658,9 @@ def variable_shift(self, k=1): return self A = self.parent() - resu = A.sum(c * A.monomial(j) for i, c in self - for j in range(i + 1)) + B = A.basis() + resu = A.linear_combination((B[j], c) for i, c in self + for j in range(i + 1)) if k == 1: return resu return resu.variable_shift(k - 1) @@ -682,8 +687,7 @@ def variable_unshift(self, k=1): return self A = self.parent() - resu = (A.sum(c * A.monomial(i) for i, c in self) - - A.sum(c * A.monomial(i - 1) for i, c in self if i)) + resu = self - A._from_dict({i - 1: c for i, c in self if i}) if k == 1: return resu return resu.variable_unshift(k - 1) @@ -715,9 +719,15 @@ def polynomial(self): sage: B = F.gen() sage: (B+1).polynomial() x + 2 + + TESTS:: + + sage: F.zero().polynomial().parent() + Univariate Polynomial Ring in x over Rational Field """ x = polygen(QQ, 'x') - return sum(c * binomial(x + i, i) for i, c in self) + R = x.parent() + return R.sum(c * binomial(x + i, i) for i, c in self) def h_vector(self): """ @@ -889,7 +899,6 @@ def _realization_name(self) -> str: """ return "binomial" - # with @cached_method ? def product_on_basis(self, n1, n2): r""" Return the product of basis elements ``n1`` and ``n2``. @@ -1118,8 +1127,14 @@ def polynomial(self): sage: B = F.gen() sage: (B+1).polynomial() x + 1 + + TESTS:: + + sage: F.zero().polynomial().parent() + Univariate Polynomial Ring in x over Rational Field """ x = polygen(QQ, 'x') - return sum(c * binomial(x, i) for i, c in self) + R = x.parent() + return R.sum(c * binomial(x, i) for i, c in self) B = Binomial From b044dd3a3f6f097fd1e00e88493ecb715c0ca565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Sun, 26 Feb 2023 21:14:59 +0100 Subject: [PATCH 03/13] more fixes --- .../polynomial/integer_valued_polynomials.py | 31 +++++-------------- 1 file changed, 8 insertions(+), 23 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 7f730982c9a..9afa41dd690 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -79,20 +79,6 @@ def a_realization(self): """ return self.S() - def indices(self): - r""" - Return the objects that index the basis elements of this algebra. - - This is the set `\NN` of nonnegative integers. - - EXAMPLES:: - - sage: A = IntegerValuedPolynomialRing(QQ) - sage: A.indices() - Non negative integers - """ - return NonNegativeIntegers() - def _repr_(self) -> str: r""" Return the string representation. @@ -255,16 +241,16 @@ class Shifted(CombinatorialFreeModule, BindableClass): Integer-valued polynomial rings are commutative and associative algebras, with a basis indexed by non-negative integers. - The basis used here is given by `B[i] = \binom{i+n}{i}` for `i \in \NN`. + The basis used here is given by `S[i] = \binom{i+n}{i}` for `i \in \NN`. - Assuming `n_1 \leq n_2`, the product of two monomials `B[n_1] \cdot B[n_2]` + Assuming `n_1 \leq n_2`, the product of two monomials `S[n_1] \cdot S[n_2]` is given by the sum .. MATH:: - \sum_{k=0}^{n_1} (-1)^k \binom{n_1}{k}\binom{n_1+n_2-k}{n_1} B[n_1 + n_2 - k]. + \sum_{k=0}^{n_1} (-1)^k \binom{n_1}{k}\binom{n_1+n_2-k}{n_1} S[n_1 + n_2 - k]. - There is a conversion formula between the two bases + There is a conversion formula between the two bases:: .. MATH:: @@ -340,7 +326,7 @@ def __init__(self, A): sage: TestSuite(F).run() """ CombinatorialFreeModule.__init__(self, A.base_ring(), - A.indices(), + NonNegativeIntegers(), category=A.Bases(), prefix="S", latex_prefix=r"\mathbb{S}") @@ -707,8 +693,6 @@ def derivative_at_minus_one(self): """ return QQ.sum(c / QQ(i) for i, c in self if i) - special_value = derivative_at_minus_one - def polynomial(self): """ Convert to a standard polynomial in `x`. @@ -793,7 +777,7 @@ class Binomial(CombinatorialFreeModule, BindableClass): The basis used here is given by `B[i] = \binom{n}{i}` for `i \in \NN`. - There is a conversion formula between the two bases + There is a conversion formula between the two bases:: .. MATH:: @@ -885,7 +869,8 @@ def __init__(self, A): in the binomial basis sage: TestSuite(F).run() """ - CombinatorialFreeModule.__init__(self, A.base_ring(), A.indices(), + CombinatorialFreeModule.__init__(self, A.base_ring(), + NonNegativeIntegers(), latex_prefix="", category=A.Bases()) From a29e33a88f31bfc630eee89aa348d878e749183a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Mon, 27 Feb 2023 16:22:16 +0100 Subject: [PATCH 04/13] little tweaks in doc, factoring some code --- .../polynomial/integer_valued_polynomials.py | 90 +++++++++---------- 1 file changed, 42 insertions(+), 48 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 9afa41dd690..cb9d7d90104 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -234,6 +234,21 @@ def shift(self, j=1): A = self.parent() return A._from_dict({i + j: c for i, c in self}) + def sum(self): + """ + Return the sum of coefficients. + + In the shifted basis, this is the evaluation at `x=0`. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.basis() + sage: (B[2]*B[4]).sum() + 1 + """ + return sum(c for _, c in self) + class Shifted(CombinatorialFreeModule, BindableClass): r""" The integer-valued polynomial ring in the shifted basis. @@ -250,7 +265,7 @@ class Shifted(CombinatorialFreeModule, BindableClass): \sum_{k=0}^{n_1} (-1)^k \binom{n_1}{k}\binom{n_1+n_2-k}{n_1} S[n_1 + n_2 - k]. - There is a conversion formula between the two bases:: + There is a conversion formula between the two bases: .. MATH:: @@ -589,6 +604,8 @@ def umbra(self): This is the derivative at `-1` of the shift by one. + .. SEEALSO:: :meth:`derivative_at_minus_one` + EXAMPLES:: sage: F = IntegerValuedPolynomialRing(ZZ).S() @@ -627,7 +644,7 @@ def variable_shift(self, k=1): Return the image by the shift of variables. On polynomials, the action is the shift - on variables `x \mapsto x + 1`. + on variables `x \mapsto x + k`. INPUT: @@ -639,44 +656,34 @@ def variable_shift(self, k=1): sage: S = A.basis() sage: S[5].variable_shift() S[0] + S[1] + S[2] + S[3] + S[4] + S[5] - """ - if k == 0: - return self - A = self.parent() - B = A.basis() - resu = A.linear_combination((B[j], c) for i, c in self - for j in range(i + 1)) - if k == 1: - return resu - return resu.variable_shift(k - 1) - - def variable_unshift(self, k=1): - r""" - Return the image by the unshift of variables. - - On polynomials, the action is the shift - on variables `x \mapsto x - k`. - - INPUT: - - - `k` -- integer (default: 1) + sage: S[5].variable_shift(-1) + -S[4] + S[5] - EXAMPLES:: + TESTS:: - sage: A = IntegerValuedPolynomialRing(ZZ).S() - sage: S = A.basis() - sage: S[5].variable_unshift() - -S[4] + S[5] + sage: S[5].variable_shift(0) + S[5] + sage: S[5].variable_shift().variable_shift(-1) + S[5] """ if k == 0: return self A = self.parent() + + if k > 0: + B = A.basis() + resu = A.linear_combination((B[j], c) for i, c in self + for j in range(i + 1)) + if k == 1: + return resu + return resu.variable_shift(k - 1) + resu = self - A._from_dict({i - 1: c for i, c in self if i}) - if k == 1: + if k == -1: return resu - return resu.variable_unshift(k - 1) + return resu.variable_shift(k + 1) def derivative_at_minus_one(self): """ @@ -684,6 +691,8 @@ def derivative_at_minus_one(self): This is sometimes useful when `-1` is a root. + .. SEEALSO:: :meth:`umbra` + EXAMPLES:: sage: F = IntegerValuedPolynomialRing(ZZ).S() @@ -695,7 +704,7 @@ def derivative_at_minus_one(self): def polynomial(self): """ - Convert to a standard polynomial in `x`. + Convert to a polynomial in `x`. EXAMPLES:: @@ -752,21 +761,6 @@ def h_polynomial(self): anneau = PolynomialRing(self.parent().base_ring(), 'z') return anneau(list(self.h_vector())) - def sum(self): - """ - Return the sum of coefficients. - - This is related to the evaluation at 0. - - EXAMPLES:: - - sage: F = IntegerValuedPolynomialRing(ZZ).S() - sage: B = F.basis() - sage: (B[2]*B[4]).sum() - 1 - """ - return sum(c for _, c in self) - S = Shifted # ===== Another basis for the same algebra ===== @@ -777,7 +771,7 @@ class Binomial(CombinatorialFreeModule, BindableClass): The basis used here is given by `B[i] = \binom{n}{i}` for `i \in \NN`. - There is a conversion formula between the two bases:: + There is a conversion formula between the two bases: .. MATH:: @@ -1104,7 +1098,7 @@ class Element(CombinatorialFreeModule.Element): def polynomial(self): """ - Convert to a standard polynomial in `x`. + Convert to a polynomial in `x`. EXAMPLES:: From d95286b96be1fc93e0707e2d94e34f9c0d6a0c83 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 28 Feb 2023 08:48:16 +0100 Subject: [PATCH 05/13] tweak in doc --- src/sage/rings/polynomial/integer_valued_polynomials.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index cb9d7d90104..543a46bc88e 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -155,7 +155,7 @@ def degree_on_basis(self, m): EXAMPLES:: sage: A = IntegerValuedPolynomialRing(QQ).S() - sage: A.degree_on_basis(4) # indirect doctest + sage: A.degree_on_basis(4) 4 """ return ZZ(m) @@ -451,6 +451,8 @@ def from_h_vector(self, h): - ``h`` -- a tuple or vector + .. SEEALSO:: :meth:`Element.h_vector` + EXAMPLES:: sage: A = IntegerValuedPolynomialRing(ZZ).S() From 1bc29df0b27c4b73b36a04502b7790d9c5ead379 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 28 Feb 2023 10:50:27 +0100 Subject: [PATCH 06/13] remove a_realization, adding content, other small tweaks to doc --- .../polynomial/integer_valued_polynomials.py | 45 ++++++++++++------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index cb9d7d90104..19940251011 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -43,6 +43,16 @@ class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): `\binom{x}{n}` for `n \geq 0` (the *binomial basis*) and of the other sequence `\binom{x+n}{n}` for `n \geq 0` (the *shifted basis*). + These two bases are available as follows:: + + sage: B = IntegerValuedPolynomialRing(QQ).Binomial() + sage: S = IntegerValuedPolynomialRing(QQ).Shifted() + + or by using the shortcuts:: + + sage: B = IntegerValuedPolynomialRing(QQ).B() + sage: S = IntegerValuedPolynomialRing(QQ).S() + TESTS:: sage: IntegerValuedPolynomialRing(24) @@ -64,21 +74,6 @@ def __init__(self, R): _shorthands = ["B", "S"] - def a_realization(self): - r""" - Return the default realization of ``self``. - - This is the shifted basis. - - EXAMPLES:: - - sage: A = IntegerValuedPolynomialRing(QQ) - sage: A.a_realization() - Integer-Valued Polynomial Ring over Rational Field - in the shifted basis - """ - return self.S() - def _repr_(self) -> str: r""" Return the string representation. @@ -234,7 +229,7 @@ def shift(self, j=1): A = self.parent() return A._from_dict({i + j: c for i, c in self}) - def sum(self): + def sum_of_coefficients(self): """ Return the sum of coefficients. @@ -244,11 +239,27 @@ def sum(self): sage: F = IntegerValuedPolynomialRing(ZZ).S() sage: B = F.basis() - sage: (B[2]*B[4]).sum() + sage: (B[2]*B[4]).sum_of_coefficients() 1 """ return sum(c for _, c in self) + def content(self): + """ + Return the content of ``self``. + + This is the gcd of the coefficients. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.basis() + sage: (3*B[4]+6*B[7]).content() + 3 + """ + from sage.arith.misc import gcd + return gcd(c for _, c in self) + class Shifted(CombinatorialFreeModule, BindableClass): r""" The integer-valued polynomial ring in the shifted basis. From 001fdfd7694b872b977dabd59dc8fb4a03ea4502 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Tue, 28 Feb 2023 11:28:51 +0100 Subject: [PATCH 07/13] sanity check for shift --- src/sage/rings/polynomial/integer_valued_polynomials.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index c8fc79c66df..8fdea454159 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -227,7 +227,7 @@ def shift(self, j=1): B[3] + B[4] """ A = self.parent() - return A._from_dict({i + j: c for i, c in self}) + return A._from_dict({A._indices(i + j): c for i, c in self}) def sum_of_coefficients(self): """ From a09d769b329c456e5c0796e36b8eca3301370804 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Wed, 1 Mar 2023 17:04:10 +0100 Subject: [PATCH 08/13] moving some documentation to the top --- .../polynomial/integer_valued_polynomials.py | 41 ++++++++----------- 1 file changed, 16 insertions(+), 25 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 8fdea454159..8a328465afd 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -53,6 +53,22 @@ class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): sage: B = IntegerValuedPolynomialRing(QQ).B() sage: S = IntegerValuedPolynomialRing(QQ).S() + There is a conversion formula between the two bases: + + .. MATH:: + + \binom{x}{i} = \sum_{k=0}^{i} (-1)^{i-k} \binom{i}{k} \binom{x+k}{k}. + + with inverse: + + .. MATH:: + + \binom{x+i}{i} = \sum_{k=0}^{i} \binom{i}{k} \binom{x}{k}. + + REFERENCES: + + - :wikipedia:`Integer-valued polynomial` + TESTS:: sage: IntegerValuedPolynomialRing(24) @@ -276,17 +292,6 @@ class Shifted(CombinatorialFreeModule, BindableClass): \sum_{k=0}^{n_1} (-1)^k \binom{n_1}{k}\binom{n_1+n_2-k}{n_1} S[n_1 + n_2 - k]. - There is a conversion formula between the two bases: - - .. MATH:: - - \binom{x}{i} = \sum_{k=0}^{i} (-1)^{i-k} \binom{i}{k} \binom{x+k}{k}. - - - REFERENCES: - - - :wikipedia:`Integer-valued polynomial` - EXAMPLES:: sage: F = IntegerValuedPolynomialRing(QQ).S(); F @@ -784,12 +789,6 @@ class Binomial(CombinatorialFreeModule, BindableClass): The basis used here is given by `B[i] = \binom{n}{i}` for `i \in \NN`. - There is a conversion formula between the two bases: - - .. MATH:: - - \binom{x+i}{i} = \sum_{k=0}^{i} \binom{i}{k} \binom{x}{k}. - Assuming `n_1 \leq n_2`, the product of two monomials `B[n_1] \cdot B[n_2]` is given by the sum @@ -800,14 +799,6 @@ class Binomial(CombinatorialFreeModule, BindableClass): The product of two monomials is therefore a positive linear combination of monomials. - REFERENCES: - - - :wikipedia:`Integer-valued polynomial` - - INPUT: - - - ``R`` -- ring - EXAMPLES:: sage: F = IntegerValuedPolynomialRing(QQ).B(); F From 95b8190ce38dca3eb2911bb2f5f9b86d8a59081a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 2 Mar 2023 08:22:16 +0100 Subject: [PATCH 09/13] refactoring polynomial and from_polynomial --- .../polynomial/integer_valued_polynomials.py | 229 ++++++++---------- 1 file changed, 107 insertions(+), 122 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 8a328465afd..75c6c6c580a 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -171,6 +171,63 @@ def degree_on_basis(self, m): """ return ZZ(m) + def from_polynomial(self, p): + """ + Convert a polynomial into the ring of integer-valued polynomials. + + This raises a ``ValueError`` if this is not possible. + + INPUT: + + - ``p`` -- a polynomial in one variable + + EXAMPLES:: + + sage: A = IntegerValuedPolynomialRing(ZZ).S() + sage: S = A.basis() + sage: S[5].polynomial() + 1/120*x^5 + 1/8*x^4 + 17/24*x^3 + 15/8*x^2 + 137/60*x + 1 + sage: A.from_polynomial(_) + S[5] + sage: x = polygen(QQ, 'x') + sage: A.from_polynomial(x) + -S[0] + S[1] + + sage: A = IntegerValuedPolynomialRing(ZZ).B() + sage: B = A.basis() + sage: B[5].polynomial() + 1/120*x^5 - 1/12*x^4 + 7/24*x^3 - 5/12*x^2 + 1/5*x + sage: A.from_polynomial(_) + B[5] + sage: x = polygen(QQ, 'x') + sage: A.from_polynomial(x) + B[1] + + TESTS:: + + sage: x = polygen(QQ,'x') + sage: A.from_polynomial(x+1/3) + Traceback (most recent call last): + ... + ValueError: not a polynomial with integer values: 1/3 + """ + B = self.basis() + poly = self._poly + remain = p + result = self.zero() + while remain: + N = remain.degree() + top_coeff = remain.leading_coefficient() * factorial(N) + try: + top_coeff = self.base_ring()(top_coeff) + except TypeError as exc: + msg = 'not a polynomial with integer' + msg += f' values: {top_coeff}' + raise ValueError(msg) from exc + remain += -top_coeff * poly(N) + result += top_coeff * B[N] + return result + def gen(self): r""" Return the generator of this algebra. @@ -222,6 +279,31 @@ def __call__(self, v): """ return self.polynomial()(v) + def polynomial(self): + """ + Convert to a polynomial in `x`. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: B = F.gen() + sage: (B+1).polynomial() + x + 2 + + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: B = F.gen() + sage: (B+1).polynomial() + x + 1 + + TESTS:: + + sage: F.zero().polynomial().parent() + Univariate Polynomial Ring in x over Rational Field + """ + R = PolynomialRing(QQ, 'x') + p = self.parent()._poly + return R.sum(c * p(i) for i, c in self) + def shift(self, j=1): """ Shift all indices by `j`. @@ -420,45 +502,6 @@ def _from_binomial_basis(self, i): return self._from_dict({k: R((-1)**(i - k) * i.binomial(k)) for k in range(i + 1)}) - def from_polynomial(self, p): - """ - Convert a polynomial into the ring of integer-valued polynomials. - - This raises a ``ValueError`` if this is not possible. - - INPUT: - - - ``p`` -- a polynomial in one variable - - EXAMPLES:: - - sage: A = IntegerValuedPolynomialRing(ZZ).S() - sage: S = A.basis() - sage: S[5].polynomial() - 1/120*x^5 + 1/8*x^4 + 17/24*x^3 + 15/8*x^2 + 137/60*x + 1 - sage: A.from_polynomial(_) - S[5] - sage: x = polygen(QQ, 'x') - sage: A.from_polynomial(x) - -S[0] + S[1] - """ - B = self.basis() - x = p.parent().gen() - remain = p - result = self.zero() - while remain: - N = remain.degree() - top_coeff = remain.leading_coefficient() * factorial(N) - try: - top_coeff = self.base_ring()(top_coeff) - except TypeError as exc: - msg = 'not a polynomial with integer' - msg += f' values: {top_coeff}' - raise ValueError(msg) from exc - remain += -top_coeff * binomial(N + x, N) - result += top_coeff * B[N] - return result - def from_h_vector(self, h): """ Convert from some `h`-vector. @@ -614,6 +657,19 @@ def _coerce_map_from_(self, R): return self.base_ring().has_coerce_map_from(R.base_ring()) return self.base_ring().has_coerce_map_from(R) + def _poly(self, i): + """ + Convert the basis element `S[i]` to a polynomial. + + EXAMPLES:: + + sage: F = IntegerValuedPolynomialRing(ZZ).S() + sage: F._poly(4) + 1/24*x^4 + 5/12*x^3 + 35/24*x^2 + 25/12*x + 1 + """ + x = polygen(QQ, 'x') + return binomial(x + i, i) + class Element(CombinatorialFreeModule.Element): def umbra(self): @@ -720,26 +776,6 @@ def derivative_at_minus_one(self): """ return QQ.sum(c / QQ(i) for i, c in self if i) - def polynomial(self): - """ - Convert to a polynomial in `x`. - - EXAMPLES:: - - sage: F = IntegerValuedPolynomialRing(ZZ).S() - sage: B = F.gen() - sage: (B+1).polynomial() - x + 2 - - TESTS:: - - sage: F.zero().polynomial().parent() - Univariate Polynomial Ring in x over Rational Field - """ - x = polygen(QQ, 'x') - R = x.parent() - return R.sum(c * binomial(x + i, i) for i, c in self) - def h_vector(self): """ Return the numerator of the generating series of values. @@ -931,51 +967,6 @@ def _from_shifted_basis(self, i): return self._from_dict({k: R(i.binomial(k)) for k in range(i + 1)}) - def from_polynomial(self, p): - """ - Convert a polynomial into the ring of integer-valued polynomials. - - This raises a ``ValueError`` if this is not possible. - - INPUT: - - - ``p`` -- a polynomial in one variable - - EXAMPLES:: - - sage: A = IntegerValuedPolynomialRing(ZZ).B() - sage: B = A.basis() - sage: B[5].polynomial() - 1/120*x^5 - 1/12*x^4 + 7/24*x^3 - 5/12*x^2 + 1/5*x - sage: A.from_polynomial(_) - B[5] - sage: x = polygen(QQ, 'x') - sage: A.from_polynomial(x) - B[1] - - TESTS:: - - sage: x = polygen(QQ,'x') - sage: A.from_polynomial(x+1/3) - Traceback (most recent call last): - ... - ValueError: not a polynomial with integer values - """ - B = self.basis() - x = p.parent().gen() - remain = p - result = self.zero() - while remain: - N = remain.degree() - top_coeff = remain.leading_coefficient() * factorial(N) - try: - top_coeff = self.base_ring()(top_coeff) - except TypeError as exc: - raise ValueError('not a polynomial with integer values') from exc - remain += -top_coeff * binomial(x, N) - result += top_coeff * B[N] - return result - def _element_constructor_(self, x): r""" Convert ``x`` into ``self``. @@ -1098,26 +1089,20 @@ def _coerce_map_from_(self, R): return self.base_ring().has_coerce_map_from(R.base_ring()) return self.base_ring().has_coerce_map_from(R) - class Element(CombinatorialFreeModule.Element): - - def polynomial(self): - """ - Convert to a polynomial in `x`. - - EXAMPLES:: + def _poly(self, i): + """ + Convert the basis element `B[i]` to a polynomial. - sage: F = IntegerValuedPolynomialRing(ZZ).B() - sage: B = F.gen() - sage: (B+1).polynomial() - x + 1 + EXAMPLES:: - TESTS:: + sage: F = IntegerValuedPolynomialRing(ZZ).B() + sage: F._poly(4) + 1/24*x^4 - 1/4*x^3 + 11/24*x^2 - 1/4*x + """ + x = polygen(QQ, 'x') + return binomial(x, i) - sage: F.zero().polynomial().parent() - Univariate Polynomial Ring in x over Rational Field - """ - x = polygen(QQ, 'x') - R = x.parent() - return R.sum(c * binomial(x, i) for i, c in self) + class Element(CombinatorialFreeModule.Element): + pass B = Binomial From a7f59136d5da6fffdaec1375c1111d4826e6aa5a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 2 Mar 2023 09:08:46 +0100 Subject: [PATCH 10/13] refactor coercion maps --- .../polynomial/integer_valued_polynomials.py | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 75c6c6c580a..d611a1b3623 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -83,11 +83,6 @@ def __init__(self, R): cat = Algebras(R).Commutative().WithBasis() Parent.__init__(self, base=R, category=cat.WithRealizations()) - S = self.S() - B = self.B() - B.module_morphism(S._from_binomial_basis, codomain=S).register_as_coercion() - S.module_morphism(B._from_shifted_basis, codomain=B).register_as_coercion() - _shorthands = ["B", "S"] def _repr_(self) -> str: @@ -555,6 +550,11 @@ def _element_constructor_(self, x): if P is not self.base_ring(): return self.element_class(self, x.monomial_coefficients()) + if isinstance(P, IntegerValuedPolynomialRing.Binomial): + phi = P.module_morphism(self._from_binomial_basis, + codomain=self) + return phi(x) + # ok, not a integer-valued polynomial ring element R = self.base_ring() # coercion via base ring @@ -655,6 +655,8 @@ def _coerce_map_from_(self, R): # that coerces in: if isinstance(R, IntegerValuedPolynomialRing.Shifted): return self.base_ring().has_coerce_map_from(R.base_ring()) + if isinstance(R, IntegerValuedPolynomialRing.Binomial): + return self.base_ring().has_coerce_map_from(R.base_ring()) return self.base_ring().has_coerce_map_from(R) def _poly(self, i): @@ -991,6 +993,11 @@ def _element_constructor_(self, x): if P is not self.base_ring(): return self.element_class(self, x.monomial_coefficients()) + if isinstance(P, IntegerValuedPolynomialRing.Shifted): + psi = P.module_morphism(self._from_shifted_basis, + codomain=self) + return psi(x) + # ok, not a integer-valued polynomial ring element R = self.base_ring() # coercion via base ring @@ -1087,6 +1094,8 @@ def _coerce_map_from_(self, R): # that coerces in: if isinstance(R, IntegerValuedPolynomialRing.Binomial): return self.base_ring().has_coerce_map_from(R.base_ring()) + if isinstance(R, IntegerValuedPolynomialRing.Shifted): + return self.base_ring().has_coerce_map_from(R.base_ring()) return self.base_ring().has_coerce_map_from(R) def _poly(self, i): From a8ae4024bd155106bb728b2e3178e17904546a84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 2 Mar 2023 11:59:54 +0100 Subject: [PATCH 11/13] minor tweaks in the doc --- .../rings/polynomial/integer_valued_polynomials.py | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index d611a1b3623..83cb9f13608 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -4,7 +4,7 @@ AUTHORS: -- Frédéric Chapoton (2013-03): Initial version +- Frédéric Chapoton (2023-03): Initial version """ # *************************************************************************** # Copyright (C) 2013 Frédéric Chapoton @@ -34,7 +34,7 @@ class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): r""" - The integer-valued polynomial ring over a base ring. + The integer-valued polynomial ring over a base ring `R`. Integer-valued polynomial rings are commutative and associative algebras, with a basis indexed by non-negative integers. @@ -57,7 +57,7 @@ class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): .. MATH:: - \binom{x}{i} = \sum_{k=0}^{i} (-1)^{i-k} \binom{i}{k} \binom{x+k}{k}. + \binom{x}{i} = \sum_{k=0}^{i} (-1)^{i-k} \binom{i}{k} \binom{x+k}{k} with inverse: @@ -357,10 +357,7 @@ class Shifted(CombinatorialFreeModule, BindableClass): r""" The integer-valued polynomial ring in the shifted basis. - Integer-valued polynomial rings are commutative and associative - algebras, with a basis indexed by non-negative integers. - - The basis used here is given by `S[i] = \binom{i+n}{i}` for `i \in \NN`. + The basis used here is given by `S[i] = \binom{i+x}{i}` for `i \in \NN`. Assuming `n_1 \leq n_2`, the product of two monomials `S[n_1] \cdot S[n_2]` is given by the sum @@ -825,7 +822,7 @@ class Binomial(CombinatorialFreeModule, BindableClass): r""" The integer-valued polynomial ring in the binomial basis. - The basis used here is given by `B[i] = \binom{n}{i}` for `i \in \NN`. + The basis used here is given by `B[i] = \binom{x}{i}` for `i \in \NN`. Assuming `n_1 \leq n_2`, the product of two monomials `B[n_1] \cdot B[n_2]` is given by the sum From e28bd1a8ef8fede32ff8215ade6f084393e2f38a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 2 Mar 2023 16:27:48 +0100 Subject: [PATCH 12/13] simplify coercion again --- .../polynomial/integer_valued_polynomials.py | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 83cb9f13608..1dcccd44c5f 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -547,11 +547,6 @@ def _element_constructor_(self, x): if P is not self.base_ring(): return self.element_class(self, x.monomial_coefficients()) - if isinstance(P, IntegerValuedPolynomialRing.Binomial): - phi = P.module_morphism(self._from_binomial_basis, - codomain=self) - return phi(x) - # ok, not a integer-valued polynomial ring element R = self.base_ring() # coercion via base ring @@ -653,7 +648,8 @@ def _coerce_map_from_(self, R): if isinstance(R, IntegerValuedPolynomialRing.Shifted): return self.base_ring().has_coerce_map_from(R.base_ring()) if isinstance(R, IntegerValuedPolynomialRing.Binomial): - return self.base_ring().has_coerce_map_from(R.base_ring()) + return R.module_morphism(self._from_binomial_basis, + codomain=self) return self.base_ring().has_coerce_map_from(R) def _poly(self, i): @@ -990,11 +986,6 @@ def _element_constructor_(self, x): if P is not self.base_ring(): return self.element_class(self, x.monomial_coefficients()) - if isinstance(P, IntegerValuedPolynomialRing.Shifted): - psi = P.module_morphism(self._from_shifted_basis, - codomain=self) - return psi(x) - # ok, not a integer-valued polynomial ring element R = self.base_ring() # coercion via base ring @@ -1092,7 +1083,8 @@ def _coerce_map_from_(self, R): if isinstance(R, IntegerValuedPolynomialRing.Binomial): return self.base_ring().has_coerce_map_from(R.base_ring()) if isinstance(R, IntegerValuedPolynomialRing.Shifted): - return self.base_ring().has_coerce_map_from(R.base_ring()) + return R.module_morphism(self._from_shifted_basis, + codomain=self) return self.base_ring().has_coerce_map_from(R) def _poly(self, i): From 8774a72d2fc90ad806408a140d781b61da2f8b5b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fr=C3=A9d=C3=A9ric=20Chapoton?= Date: Thu, 9 Mar 2023 09:03:36 +0100 Subject: [PATCH 13/13] reviewer's fixes --- .../polynomial/integer_valued_polynomials.py | 67 +++++++++++++------ 1 file changed, 47 insertions(+), 20 deletions(-) diff --git a/src/sage/rings/polynomial/integer_valued_polynomials.py b/src/sage/rings/polynomial/integer_valued_polynomials.py index 1dcccd44c5f..603cdbf2e38 100644 --- a/src/sage/rings/polynomial/integer_valued_polynomials.py +++ b/src/sage/rings/polynomial/integer_valued_polynomials.py @@ -20,7 +20,6 @@ from sage.matrix.constructor import matrix from sage.misc.cachefunc import cached_method from sage.modules.free_module_element import vector -from sage.rings.integer import Integer from sage.rings.integer_ring import ZZ from sage.rings.polynomial.polynomial_ring import polygen from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing @@ -77,6 +76,12 @@ class IntegerValuedPolynomialRing(UniqueRepresentation, Parent): TypeError: argument R must be a commutative ring """ def __init__(self, R): + """ + TESTS:: + + sage: IV = IntegerValuedPolynomialRing(ZZ) + sage: TestSuite(IV).run() + """ if R not in Rings().Commutative(): raise TypeError("argument R must be a commutative ring") self._base = R @@ -97,6 +102,20 @@ def _repr_(self) -> str: br = self.base_ring() return f"Integer-Valued Polynomial Ring over {br}" + def a_realization(self): + """ + Return a default realization. + + The Binomial realization is chosen. + + EXAMPLES:: + + sage: IntegerValuedPolynomialRing(QQ).a_realization() + Integer-Valued Polynomial Ring over Rational Field + in the binomial basis + """ + return self.Binomial() + class Bases(Category_realization_of_parent): def super_categories(self) -> list: r""" @@ -223,10 +242,12 @@ def from_polynomial(self, p): result += top_coeff * B[N] return result - def gen(self): + def gen(self, i=0): r""" Return the generator of this algebra. + The optional argument is ignored. + EXAMPLES:: sage: F = IntegerValuedPolynomialRing(ZZ).B() @@ -334,8 +355,14 @@ def sum_of_coefficients(self): sage: B = F.basis() sage: (B[2]*B[4]).sum_of_coefficients() 1 + + TESTS:: + + sage: (0*B[2]).sum_of_coefficients().parent() + Integer Ring """ - return sum(c for _, c in self) + R = self.parent().base_ring() + return R.sum(self._monomial_coefficients.values()) def content(self): """ @@ -349,9 +376,14 @@ def content(self): sage: B = F.basis() sage: (3*B[4]+6*B[7]).content() 3 + + TESTS:: + + sage: (0*B[2]).content() + 0 """ from sage.arith.misc import gcd - return gcd(c for _, c in self) + return gcd(self._monomial_coefficients.values()) class Shifted(CombinatorialFreeModule, BindableClass): r""" @@ -516,7 +548,8 @@ def from_h_vector(self, h): m = matrix(QQ, d + 1, d + 1, lambda j, i: (-1)**(d - j) * binomial(d - i, d - j)) v = vector(QQ, [h[i] for i in range(d + 1)]) - return self._from_dict({i: Integer(c) + R = self.base_ring() + return self._from_dict({i: R(c) for i, c in enumerate(m * v)}) def _element_constructor_(self, x): @@ -525,7 +558,7 @@ def _element_constructor_(self, x): INPUT: - - ``x`` -- an integer or something convertible + - ``x`` -- an element of the base ring or something convertible EXAMPLES:: @@ -536,10 +569,6 @@ def _element_constructor_(self, x): sage: R(x) S[1] """ - if x in NonNegativeIntegers(): - W = self.basis().keys() - return self.monomial(W(x)) - P = x.parent() if isinstance(P, IntegerValuedPolynomialRing.Shifted): if P is self: @@ -585,7 +614,7 @@ def _coerce_map_from_(self, R): 6*S[1] + 2*S[2] Elements of the integers coerce in, since there is a coerce map - from `\ZZ` to GF(7):: + from `\ZZ` to `\GF(7)`:: sage: F.coerce(1) # indirect doctest S[0] @@ -614,8 +643,9 @@ def _coerce_map_from_(self, R): sage: z.parent() is F True - However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle - algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: + However, `\GF{7}` does not coerce to `\ZZ`, so the + integer-valued polynomial algebra over `\GF{7}` does not + coerce to the one over `\ZZ`:: sage: G.coerce(x^3+x) Traceback (most recent call last): @@ -975,10 +1005,6 @@ def _element_constructor_(self, x): sage: R(x) B[1] """ - if x in NonNegativeIntegers(): - W = self.basis().keys() - return self.monomial(W(x)) - P = x.parent() if isinstance(P, IntegerValuedPolynomialRing.Binomial): if P is self: @@ -1020,7 +1046,7 @@ def _coerce_map_from_(self, R): B[1] + 2*B[2] Elements of the integers coerce in, since there is a coerce map - from `\ZZ` to GF(7):: + from `\ZZ` to `\GF(7)`:: sage: F.coerce(1) # indirect doctest B[0] @@ -1049,8 +1075,9 @@ def _coerce_map_from_(self, R): sage: z.parent() is F True - However, `\GF{7}` does not coerce to `\ZZ`, so the shuffle - algebra over `\GF{7}` does not coerce to the one over `\ZZ`:: + However, `\GF{7}` does not coerce to `\ZZ`, so the + integer-valued polynomial algebra over `\GF{7}` does not + coerce to the one over `\ZZ`:: sage: G.coerce(x^3+x) Traceback (most recent call last):