From 98e29e75749c4b2c81745f62bca0b79a34347dbf Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 1 May 2020 15:34:32 +0200 Subject: [PATCH 01/16] Working implementation of Ore polynomials --- src/module_list.py | 3 + src/sage/categories/rings.py | 7 +- src/sage/rings/derivation.py | 2 +- src/sage/rings/polynomial/all.py | 3 +- .../polynomial/ore_polynomial_element.pxd | 46 + .../polynomial/ore_polynomial_element.pyx | 2969 ++++++++++++++ .../rings/polynomial/ore_polynomial_ring.py | 724 ++++ .../polynomial/skew_polynomial_element.pxd | 49 +- .../polynomial/skew_polynomial_element.pyx | 3439 ++--------------- .../skew_polynomial_finite_field.pyx | 2 +- .../skew_polynomial_finite_order.pyx | 6 +- .../rings/polynomial/skew_polynomial_ring.py | 833 +--- 12 files changed, 4100 insertions(+), 3983 deletions(-) create mode 100644 src/sage/rings/polynomial/ore_polynomial_element.pxd create mode 100644 src/sage/rings/polynomial/ore_polynomial_element.pyx create mode 100644 src/sage/rings/polynomial/ore_polynomial_ring.py diff --git a/src/module_list.py b/src/module_list.py index 331d8c13586..b90f6f1b286 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1533,6 +1533,9 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.symmetric_reduction', sources = ['sage/rings/polynomial/symmetric_reduction.pyx']), + Extension('sage.rings.polynomial.ore_polynomial_element', + sources = ['sage/rings/polynomial/ore_polynomial_element.pyx']), + Extension('sage.rings.polynomial.skew_polynomial_element', sources = ['sage/rings/polynomial/skew_polynomial_element.pyx']), diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index ff0f66b3aeb..391db25f052 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1069,9 +1069,10 @@ def normalize_arg(arg): if isinstance(arg, tuple): from sage.categories.morphism import Morphism - if len(arg) == 2 and isinstance(arg[1], Morphism): - from sage.rings.polynomial.skew_polynomial_ring import SkewPolynomialRing - return SkewPolynomialRing(self, arg[1], names=arg[0]) + from sage.rings.derivation import RingDerivation + if len(arg) == 2 and isinstance(arg[1], (Morphism, RingDerivation)): + from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing + return OrePolynomialRing(self, arg[1], names=arg[0]) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py index cba1e1fabca..af51e8b85e6 100644 --- a/src/sage/rings/derivation.py +++ b/src/sage/rings/derivation.py @@ -203,7 +203,7 @@ from sage.categories.lie_algebras import LieAlgebras from sage.categories.map import Map -from sage.categories.all import Rings +from sage.categories.rings import Rings class RingDerivationModule(Module, UniqueRepresentation): diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index 2605180acb2..eb1a17b7c97 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -45,7 +45,8 @@ # Infinite Polynomial Rings from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing -# Skew Polynomial Rings +# Ore Polynomial Rings +from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing from sage.rings.polynomial.skew_polynomial_ring import SkewPolynomialRing # Evaluation of cyclotomic polynomials diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pxd b/src/sage/rings/polynomial/ore_polynomial_element.pxd new file mode 100644 index 00000000000..4a90682f01b --- /dev/null +++ b/src/sage/rings/polynomial/ore_polynomial_element.pxd @@ -0,0 +1,46 @@ +from sage.structure.element cimport AlgebraElement +from sage.structure.parent cimport Parent +from sage.rings.morphism cimport Morphism +from sage.structure.element cimport RingElement +from sage.rings.polynomial.polynomial_element cimport Polynomial_generic_dense + +cdef class OrePolynomial(AlgebraElement): + cdef _is_gen + + cdef long _hash_c(self) + cdef OrePolynomial _new_c(self, list coeffs, Parent P, char check=*) + cpdef OrePolynomial _new_constant_poly(self, RingElement a, Parent P, char check=*) + cpdef _neg_(self) + cpdef _floordiv_(self, right) + cpdef _mod_(self, right) + + cpdef bint is_zero(self) + cpdef bint is_one(self) + + cdef _left_quo_rem(self, OrePolynomial other) + cdef _right_quo_rem(self, OrePolynomial other) + cdef OrePolynomial _left_lcm_cofactor(self, OrePolynomial other) + cdef OrePolynomial _right_lcm_cofactor(self, OrePolynomial other) + + # Abstract methods + cpdef int degree(self) + cpdef list coefficients(self, sparse=*) + + +cdef void lmul_gen(list A, Morphism m, d) + +cdef class OrePolynomial_generic_dense(OrePolynomial): + cdef list _coeffs + + cdef void __normalize(self) + cpdef _add_(self, other) + cdef list _mul_list(self, list A) + cpdef _mul_(self, other) + + cpdef dict dict(self) + cpdef list list(self, bint copy=*) + + +cdef class OrePolynomialBaseringInjection(Morphism): + cdef RingElement _an_element + cdef object _new_constant_poly_ diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx new file mode 100644 index 00000000000..d3c9e0cd7d1 --- /dev/null +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -0,0 +1,2969 @@ +r""" +Univariate Skew Polynomials + +This module provides the +:class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial`, +which constructs a single univariate skew polynomial over commutative +base rings and an automorphism over the base ring. Skew polynomials are +non-commutative and so principal methods such as gcd, lcm, monic, +multiplication, and division are given in left and right forms. + +The generic implementation of dense skew polynomials is +:class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial_generic_dense`. +The classes +:class:`~sage.rings.polynomial.skew_polynomial_element.ConstantOrePolynomialSection` +and :class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomialBaseringInjection` +handle conversion from a skew polynomial ring to its base ring and vice versa respectively. + +.. WARNING:: + + The current semantics of + :meth:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial.__call__` + are experimental, so a warning is thrown when a skew polynomial is evaluated + for the first time in a session. See the method documentation for details. + + TESTS:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 2*(t + x) + 1 + sage: a(t^2) + doctest:...: FutureWarning: This class/method/function is marked as + experimental. It, its functionality or its interface might change + without a formal deprecation. + See http://trac.sagemath.org/13215 for details. + 2*t^3 + 3*t^2 + 4*t + 2 + sage: a(t) + 2*t^2 + 3*t + 2 + +AUTHORS: + +- Xavier Caruso (2012-06-29): initial version + +- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests and + refactored classes and methods + +- Johan Rosenkilde (2016-08-03): changes for bug fixes, docstring and + doctest errors + +""" + +############################################################################# +# Copyright (C) 2012 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# http://www.gnu.org/licenses/ +#**************************************************************************** + +import re +from cysignals.signals cimport sig_check + +from sage.rings.infinity import infinity +from sage.structure.factorization import Factorization +from sage.structure.element cimport Element, RingElement, AlgebraElement, ModuleElement +from sage.structure.parent cimport Parent +from sage.structure.parent_gens cimport ParentWithGens +from sage.misc.abstract_method import abstract_method +from sage.categories.homset import Hom +from sage.categories.fields import Fields +from sage.rings.integer cimport Integer +from cpython.object cimport PyObject_RichCompare +from sage.categories.map cimport Map +from sage.rings.morphism cimport Morphism, RingHomomorphism +from sage.rings.polynomial.polynomial_element cimport _dict_to_list +from sage.structure.element import coerce_binop +from sage.misc.superseded import experimental + + +cdef class OrePolynomial(AlgebraElement): + r""" + Abstract base class for skew polynomials. + + This class must be inherited from and have key methods overridden. + + .. RUBRIC:: Definition + + Let `R` be a commutative ring equipped with an automorphism `\sigma`. + + Then, a skew polynomial is given by the equation: + + .. MATH:: + + F(X) = a_{n} X^{n} + \cdots + a_0, + + where the coefficients `a_i \in R` and `X` is a formal variable. + + Addition between two skew polynomials is defined by the usual addition + operation and the modified multiplication is defined by the rule + `X a = \sigma(a) X` for all `a` in `R`. Skew polynomials are thus + non-commutative and the degree of a product is equal to the sum of the + degrees of the factors. + + Let `a` and `b` be two skew polynomials in the same ring `S`. + The *left (resp. right) euclidean division* of `a` by `b` is a couple + `(q,r)` of elements in `S` such that + + - `a = q b + r` (resp. `a = b q + r`) + + - the degree of `r` is less than the degree of `b` + + `q` (resp. `r`) is called the *quotient* (resp. the remainder) + of this euclidean division. + + .. RUBRIC:: Properties + + Keeping the previous notation, if the leading coefficient of `b` + is a unit (e.g. if `b` is monic) then the quotient and the remainder + in the *right* euclidean division exist and are unique. + + The same result holds for the *left* euclidean division if in addition + the twist map defining the skew polynomial ring is invertible. + + .. RUBRIC:: Evaluation + + The value of a given a skew polynomial `p(x) = \sum_{i=0}^d a_i x^i` + at `r` is calculated using the formula: + + .. MATH:: + + p(r) = \sum_{i=0}^d a_i \sigma^i(r) + + where `\sigma` is the base ring automorphism. This is called + the *operator evaluation* method. + + EXAMPLES: + + We illustrate some functionalities implemented in this class. + + We create the skew polynomial ring:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma]; S + Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring + twisted by t |--> t + 1 + + and some elements in it:: + + sage: a = t + x + 1; a + x + t + 1 + sage: b = S([t^2,t+1,1]); b + x^2 + (t + 1)*x + t^2 + sage: c = S.random_element(degree=3,monic=True); c + x^3 + (2*t - 1)*x + + Ring operations are supported:: + + sage: a + b + x^2 + (t + 2)*x + t^2 + t + 1 + sage: a - b + -x^2 - t*x - t^2 + t + 1 + + sage: a * b + x^3 + (2*t + 3)*x^2 + (2*t^2 + 4*t + 2)*x + t^3 + t^2 + sage: b * a + x^3 + (2*t + 4)*x^2 + (2*t^2 + 3*t + 2)*x + t^3 + t^2 + sage: a * b == b * a + False + + sage: b^2 + x^4 + (2*t + 4)*x^3 + (3*t^2 + 7*t + 6)*x^2 + + (2*t^3 + 4*t^2 + 3*t + 1)*x + t^4 + sage: b^2 == b*b + True + + Sage also implements arithmetic over skew polynomial rings. You will find + below a short panorama:: + + sage: q,r = c.right_quo_rem(b) + sage: q + x - t - 2 + sage: r + 3*t*x + t^3 + 2*t^2 + sage: c == q*b + r + True + + The operators ``//`` and ``%`` give respectively the quotient + and the remainder of the *right* euclidean division:: + + sage: q == c // b + True + sage: r == c % b + True + + Left euclidean division won't work over our current `S` because Sage can't + invert the twist map:: + + sage: q,r = c.left_quo_rem(b) + Traceback (most recent call last): + ... + NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring + Defn: t |--> t + 1 + + Here we can see the effect of the operator evaluation compared to the usual + polynomial evaluation:: + + sage: a = x^2 + sage: a(t) + t + 2 + + Here is a working example over a finite field:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^4 + (4*t + 1)*x^3 + (t^2 + 3*t + 3)*x^2 + (3*t^2 + 2*t + 2)*x + (3*t^2 + 3*t + 1) + sage: b = (2*t^2 + 3)*x^2 + (3*t^2 + 1)*x + 4*t + 2 + sage: q,r = a.left_quo_rem(b) + sage: q + (4*t^2 + t + 1)*x^2 + (2*t^2 + 2*t + 2)*x + 2*t^2 + 4*t + 3 + sage: r + (t + 2)*x + 3*t^2 + 2*t + 4 + sage: a == b*q + r + True + + Once we have euclidean divisions, we have for free gcd and lcm + (at least if the base ring is a field):: + + sage: a = (x + t) * (x + t^2)^2 + sage: b = (x + t) * (t*x + t + 1) * (x + t^2) + sage: a.right_gcd(b) + x + t^2 + sage: a.left_gcd(b) + x + t + + The left lcm has the following meaning: given skew polynomials `a` and `b`, + their left lcm is the least degree polynomial `c = ua = vb` for some skew + polynomials `u, v`. Such a `c` always exist if the base ring is a field:: + + sage: c = a.left_lcm(b); c + x^5 + (4*t^2 + t + 3)*x^4 + (3*t^2 + 4*t)*x^3 + 2*t^2*x^2 + (2*t^2 + t)*x + 4*t^2 + 4 + sage: c.is_right_divisible_by(a) + True + sage: c.is_right_divisible_by(b) + True + + The right lcm is defined similarly as the least degree polynomial `c = au = + bv` for some `u,v`:: + + sage: d = a.right_lcm(b); d + x^5 + (t^2 + 1)*x^4 + (3*t^2 + 3*t + 3)*x^3 + (3*t^2 + t + 2)*x^2 + (4*t^2 + 3*t)*x + 4*t + 4 + sage: d.is_left_divisible_by(a) + True + sage: d.is_left_divisible_by(b) + True + + .. SEEALSO:: + + - :mod:`sage.rings.polynomial.skew_polynomial_ring` + - :mod:`sage.rings.polynomial.skew_polynomial_ring_constructor` + """ + def __init__(self, parent, construct=False): + r""" + Initialize ``self``. + + INPUT: + + - ``parent`` -- parent of ``self`` + + - ``construct`` -- boolean (default: ``False``) + + TESTS:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: P = x + t + sage: TestSuite(P).run() + sage: Q = S([1, t, t+2]) + sage: TestSuite(Q).run() + """ + AlgebraElement.__init__(self, parent) + + cdef long _hash_c(self): + raise NotImplementedError + + def __hash__(self): + r""" + Return hash of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: hash(a) == hash(a) + True + """ + return self._hash_c() + + cpdef int degree(self): + r""" + Return the degree of ``self``. + + By convention, the zero skew polynomial has degree `-1`. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^2 + t*x^3 + t^2*x + 1 + sage: a.degree() + 3 + sage: S.zero().degree() + -1 + sage: S(5).degree() + 0 + """ + + cdef OrePolynomial _new_c(self, list coeffs, Parent P, char check=0): + r""" + Fast creation of a new skew polynomial + + .. NOTE:: + + Override this function in classes which inherit + from OrePolynomial. + """ + return P(coeffs) + + cpdef OrePolynomial _new_constant_poly(self, RingElement a, Parent P, char check=0): + r""" + Fast creation of a new constant skew polynomial + + EXAMPLES:: + + sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: OrePolynomialBaseringInjection(k, k['x', Frob]) #indirect doctest + Skew Polynomial base injection morphism: + From: Finite Field in t of size 5^3 + To: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + """ + if a: + n = self._new_c([a],P,check) + else: + n = self._new_c([],P) + return n + + def __setitem__(self, n, value): + r""" + Set the ``n``-th coefficient of ``self``. + + This always raises an ``IndexError``, since polynomials are immutable in + Sage. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x + t + sage: a[1] = t + 1 + Traceback (most recent call last): + ... + IndexError: skew polynomials are immutable + """ + raise IndexError("skew polynomials are immutable") + + def square(self): + r""" + Return the square of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + t; a + x + t + sage: a.square() + x^2 + (2*t + 1)*x + t^2 + sage: a.square() == a*a + True + """ + return self * self + + def constant_coefficient(self): + r""" + Return the constant coefficient (i.e. the coefficient of term + of degree `0`) of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + t^2 + 2 + sage: a.constant_coefficient() + t^2 + 2 + """ + if self: + return self[0] + else: + return self.base_ring().zero() + + def leading_coefficient(self): + r""" + Return the coefficient of the highest-degree monomial of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (t+1)*x^5 + t^2*x^3 + x + sage: a.leading_coefficient() + t + 1 + """ + cdef int d = self.degree() + if d == -1: + raise ValueError("the skew polynomial must not be 0") + return self[d] + + def is_unit(self): + r""" + Return ``True`` if this skew polynomial is a unit. + + When the base ring `R` is an integral domain, then a skew polynomial `f` + is a unit if and only if degree of `f` is `0` and `f` is then a unit in + `R`. + + .. NOTE:: + + The case when `R` is not an integral domain is not yet implemented. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + (t+1)*x^5 + t^2*x^3 - x^5 + sage: a.is_unit() + False + """ + # TODO: Sage does not yet have support for finding order of + # automorphisms. Once that is available, general case can + # be implemented. Reference: http://bit.ly/29Vidu7 + if self._parent.base_ring().is_integral_domain(): + if self.degree() == 0 and self[0].is_unit(): + return True + else: + return False + else: + raise NotImplementedError("is_unit is not implemented for skew polynomial rings " + "over base rings which are not integral domains.") + + def is_nilpotent(self): + r""" + Check if ``self`` is nilpotent. + + Given a commutative ring `R` and a base ring automorphism `\sigma` + of order `n`, an element `f` of `R[X, \sigma]` is nilpotent if + and only if all coefficients of `f^n` are nilpotent in `R`. + + .. NOTE:: + + The paper "Nilpotents and units in skew polynomial rings + over commutative rings" by M. Rimmer and K.R. Pearson describes + the method to check whether a given skew polynomial is nilpotent. + That method however, requires one to know the order of the + automorphism which is not available in Sage. This method is thus + not yet implemented. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: x.is_nilpotent() + Traceback (most recent call last): + ... + NotImplementedError + """ + raise NotImplementedError + + def is_monic(self): + r""" + Return ``True`` if this skew polynomial is monic. + + The zero polynomial is by definition not monic. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + t + sage: a.is_monic() + True + sage: a = 0*x + sage: a.is_monic() + False + sage: a = t*x^3 + x^4 + (t+1)*x^2 + sage: a.is_monic() + True + sage: a = (t^2 + 2*t)*x^2 + x^3 + t^10*x^5 + sage: a.is_monic() + False + """ + return not self.is_zero() and self[self.degree()] == 1 + + def left_monic(self): + r""" + Return the unique monic skew polynomial `m` which divides ``self`` on + the left and has the same degree. + + Given a skew polynomial `p` of degree `n`, its left monic is given by + `m = p \sigma^{-n}(1/k)`, where `k` is the leading coefficient of + `p`, i.e. by the appropriate scalar multiplication on the right. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (3*t^2 + 3*t + 2)*x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 + sage: b = a.left_monic(); b + x^3 + (4*t^2 + 3*t)*x^2 + (4*t + 2)*x + 2*t^2 + 4*t + 3 + + Check list:: + + sage: b.degree() == a.degree() + True + sage: b.is_left_divisible_by(a) + True + sage: twist = S.twist_map(-a.degree()) + sage: a == b * twist(a.leading_coefficient()) + True + + Note that `b` does not divide `a` on the right:: + + sage: a.is_right_divisible_by(b) + False + + This function does not work if the leading coefficient is not a + unit:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t*x + sage: a.left_monic() + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient is not a unit + """ + try: + a = self.base_ring()(~self.leading_coefficient()) + except (ZeroDivisionError, TypeError): + raise NotImplementedError("the leading coefficient is not a unit") + cdef Morphism m = self._parent.twisting_morphism(-self.degree()) + if m is None: + return self * a + else: + return self * m(a) + return r + + def right_monic(self): + r""" + Return the unique monic skew polynomial `m` which divides ``self`` on + the right and has the same degree. + + Given a skew polynomial `p` of degree `n`, its left monic is given by + `m = (1/k) * p`, where `k` is the leading coefficient of `p`, i.e. by + the appropriate scalar multiplication on the left. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (3*t^2 + 3*t + 2)*x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 + sage: b = a.right_monic(); b + x^3 + (2*t^2 + 3*t + 4)*x^2 + (3*t^2 + 4*t + 1)*x + 2*t^2 + 4*t + 3 + + Check list:: + + sage: b.degree() == a.degree() + True + sage: b.is_right_divisible_by(a) + True + sage: a == a.leading_coefficient() * b + True + + Note that `b` does not divide `a` on the right:: + + sage: a.is_left_divisible_by(b) + False + + This function does not work if the leading coefficient is not a + unit:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t*x + sage: a.right_monic() + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient is not a unit + """ + try: + a = self.base_ring()(~self.leading_coefficient()) + except (ZeroDivisionError, TypeError): + raise NotImplementedError("the leading coefficient is not a unit") + r = a * self + return r + + cpdef _mod_(self, other): + r""" + Return the remainder in the *right* euclidean division of + ``self`` by ``other```. + + TESTS:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: b = x^2 + 2*t*x + 2 + sage: a = (x+t)*b + t*x + 1 + sage: a % b + t*x + 1 + + sage: (a*t).right_quo_rem(b*t) + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient of the divisor is not invertible + """ + _,r = self.right_quo_rem(other) + return r + + cpdef _floordiv_(self, right): + r""" + Return the quotient of the *right* euclidean division of + ``self`` by ``right``. + + The algorithm fails if the leading coefficient of the divisor + (``right``) is not invertible. + + TESTS:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: b = x^2 + t + sage: a = (x^2 + t*x + 1)*b + t^3*x + sage: a // b + x^2 + t*x + 1 + + sage: (t*a) // (t*b) + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient of the divisor is not invertible + + """ + q,_ = self.right_quo_rem(right) + return q + + cpdef _div_(self, right): + r""" + Not Implemented. + + To implement this, localization of Ore rings is needed, see + :trac:`13215`. + + Use the operator `//` even for exact division. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^5 + (t + 2)*x^2 + t^2 + sage: b = x^3 + 4*t + sage: c = a*b + + sage: c / b + Traceback (most recent call last): + ... + NotImplementedError: localization of Ore rings not yet implemented + + sage: c // b == a + True + """ + # Should this actually return something in the fraction field like + # we do elsewhere in Sage? - TCS + raise NotImplementedError("localization of Ore rings not yet implemented") + + def is_left_divisible_by(self, other): + r""" + Check if ``self`` is divisible by ``other`` on the left. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + OUTPUT: + + Return ``True`` or ``False``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^2 + t*x + t^2 + 3 + sage: b = x^3 + (t + 1)*x^2 + 1 + sage: c = a*b + sage: c.is_left_divisible_by(a) + True + sage: c.is_left_divisible_by(b) + False + + Divisibility by `0` does not make sense:: + + sage: c.is_left_divisible_by(S(0)) + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero is not valid + """ + _, r = self.left_quo_rem(other) + return r.is_zero() + + def is_right_divisible_by(self, other): + r""" + Check if ``self`` is divisible by ``other`` on the right. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + OUTPUT: + + Return ``True`` or ``False``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^2 + t*x + t^2 + 3 + sage: b = x^3 + (t + 1)*x^2 + 1 + sage: c = a*b + sage: c.is_right_divisible_by(a) + False + sage: c.is_right_divisible_by(b) + True + + Divisibility by `0` does not make sense:: + + sage: c.is_right_divisible_by(S(0)) + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero is not valid + + This function does not work if the leading coefficient of the divisor + is not a unit:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^2 + 2*x + t + sage: b = (t+1)*x + t^2 + sage: c = a*b + sage: c.is_right_divisible_by(b) + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient of the divisor is not invertible + """ + _, r = self.right_quo_rem(other) + return r.is_zero() + + def left_divides(self, other): + r""" + Check if ``self`` divides ``other`` on the left. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + OUTPUT: + + Return ``True`` or ``False``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^2 + t*x + t^2 + 3 + sage: b = x^3 + (t + 1)*x^2 + 1 + sage: c = a*b + sage: a.left_divides(c) + True + sage: b.left_divides(c) + False + + Divisibility by `0` does not make sense:: + + sage: S(0).left_divides(c) + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero is not valid + """ + _, r = other.left_quo_rem(self) + return r.is_zero() + + def right_divides(self, other): + r""" + Check if ``self`` divides ``other`` on the right. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + OUTPUT: + + Return ``True`` or ``False``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x^2 + t*x + t^2 + 3 + sage: b = x^3 + (t + 1)*x^2 + 1 + sage: c = a*b + sage: a.right_divides(c) + False + sage: b.right_divides(c) + True + + Divisibility by `0` does not make sense:: + + sage: S(0).right_divides(c) + Traceback (most recent call last): + ... + ZeroDivisionError: division by zero is not valid + + This function does not work if the leading coefficient of the divisor + is not a unit:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^2 + 2*x + t + sage: b = (t+1)*x + t^2 + sage: c = a*b + sage: b.right_divides(c) + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient of the divisor is not invertible + """ + _, r = other.right_quo_rem(self) + return r.is_zero() + + @coerce_binop + def left_xgcd(self, other, monic=True): + r""" + Return the left gcd of ``self`` and ``other`` along with the + coefficients for the linear combination. + + If `a` is ``self`` and `b` is ``other``, then there are skew polynomials + `u` and `v` such that `g = a u + b v`, where `g` is the left gcd of `a` + and `b`. This method returns `(g, u, v)`. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- boolean (default: ``True``). Return whether the left gcd + should be normalized to be monic. + + OUTPUT: + + - The left gcd of ``self`` and ``other``, that is a skew polynomial + `g` with the following property: any skew polynomial is + divisible on the left by `g` iff it is divisible on the left + by both ``self`` and ``other``. + If monic is ``True``, `g` is in addition monic. (With this + extra condition, it is uniquely determined.) + + - Two skew polynomials `u` and `v` such that: + + .. MATH:: + + g = a * u + b * v, + + where `s` is ``self`` and `b` is ``other``. + + .. NOTE:: + + Works only if following two conditions are fulfilled + (otherwise left gcd do not exist in general): + 1) the base ring is a field and 2) the twist map on + this field is bijective. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (x + t) * (x^2 + t*x + 1) + sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) + sage: g,u,v = a.left_xgcd(b); g + x + t + sage: a*u + b*v == g + True + + Specifying ``monic=False``, we *can* get a nonmonic gcd:: + + sage: g,u,v = a.left_xgcd(b, monic=False); g + 2*t*x + 4*t + 2 + sage: a*u + b*v == g + True + + The base ring must be a field:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (x + t) * (x^2 + t*x + 1) + sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) + sage: a.left_xgcd(b) + Traceback (most recent call last): + ... + TypeError: the base ring must be a field + + And the twist map must be bijective:: + + sage: FR = R.fraction_field() + sage: f = FR.hom([FR(t)^2]) + sage: S. = FR['x',f] + sage: a = (x + t) * (x^2 + t*x + 1) + sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) + sage: a.left_xgcd(b) + Traceback (most recent call last): + ... + NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t^2 + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + cdef OrePolynomial G = self + cdef OrePolynomial U = self._parent.one() + cdef OrePolynomial V, V1, V2, Q, R, T + cdef Morphism m + if not other: + V = self._parent.zero() + else: + V1 = self._parent.zero() + V3 = other + while V3: + Q,R = G._left_quo_rem(V3) + T = U - V1*Q + U = V1 + G = V3 + V1 = T + V3 = R + V = G - self*U + V, _ = V._left_quo_rem(other) + if monic: + lc = ~G.leading_coefficient() + m = self._parent.twisting_morphism(-G.degree()) + if m is not None: + lc = m(lc) + G = G * lc + U = U * lc + V = V * lc + return G,U,V + + cdef _left_quo_rem(self, OrePolynomial other): + r""" + Return the quotient and remainder of the left euclidean + division of ``self`` by ``other`` (C implementation). + + Must be implemented in subclasses. + """ + raise NotImplementedError("left Euclidean division is not implemented") + + @coerce_binop + def left_quo_rem(self, other): + r""" + Return the quotient and remainder of the left euclidean + division of ``self`` by ``other``. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + OUTPUT: + + - the quotient and the remainder of the left euclidean + division of this skew polynomial by ``other`` + + .. NOTE:: + + This will fail if the leading coefficient of ``other`` is not a unit + or if Sage can't invert the twist map. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (3*t^2 + 3*t + 2)*x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 + sage: b = (3*t^2 + 4*t + 2)*x^2 + (2*t^2 + 4*t + 3)*x + 2*t^2 + t + 1 + sage: q,r = a.left_quo_rem(b) + sage: a == b*q + r + True + + In the following example, Sage does not know the inverse + of the twist map:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (-2*t^2 - t + 1)*x^3 + (-t^2 + t)*x^2 + (-12*t - 2)*x - t^2 - 95*t + 1 + sage: b = x^2 + (5*t - 6)*x - 4*t^2 + 4*t - 1 + sage: a.left_quo_rem(b) + Traceback (most recent call last): + ... + NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring + Defn: t |--> t + 1 + """ + if not other: + raise ZeroDivisionError("division by zero is not valid") + return self._left_quo_rem(other) + + cdef _right_quo_rem(self, OrePolynomial other): + r""" + Return the quotient and remainder of the right euclidean + division of ``self`` by ``other`` (C implementation). + + Must be implemented in subclasses. + """ + raise NotImplementedError("right Euclidean division is not implemented") + + @coerce_binop + def right_quo_rem(self, other): + r""" + Return the quotient and remainder of the right euclidean + division of ``self`` by ``other``. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + OUTPUT: + + - the quotient and the remainder of the left euclidean + division of this skew polynomial by ``other`` + + .. NOTE:: + + This will fail if the leading coefficient of the divisor + is not a unit. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = S.random_element(degree=4); a + (-t - 95)*x^4 + x^3 + (2*t - 1)*x + sage: b = S.random_element(monic=True); b + x^2 + (-12*t - 2)*x + sage: q,r = a.right_quo_rem(b) + sage: a == q*b + r + True + + The leading coefficient of the divisor need to be invertible:: + + sage: c = S.random_element(); c + (t - 1)*x^2 + t^2*x + sage: a.right_quo_rem(c) + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient of the divisor is not invertible + """ + if not other: + raise ZeroDivisionError("division by zero is not valid") + return self._right_quo_rem(other) + + @coerce_binop + def right_xgcd(self, other, monic=True): + r""" + Return the right gcd of ``self`` and ``other`` along with the + coefficients for the linear combination. + + If `a` is ``self`` and `b` is ``other``, then there are skew polynomials + `u` and `v` such that `g = u a + v b`, where `g` is the right gcd of `a` + and `b`. This method returns `(g, u, v)`. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- boolean (default: ``True``). Return whether the right gcd + should be normalized to be monic. + + OUTPUT: + + - The right gcd of ``self`` and ``other``, that is a skew polynomial + `g` with the following property: any skew polynomial is + divisible on the right by `g` iff it is divisible on the right + by both ``self`` and ``other``. + If monic is ``True``, `g` is in addition monic. (With this + extra condition, it is uniquely determined.) + + - Two skew polynomials `u` and `v` such that: + + .. MATH:: + + g = u * a + v * b + + where `a` is ``self`` and `b` is ``other``. + + .. NOTE:: + + Works only if the base ring is a field (otherwise right + gcd do not exist in general). + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (x^2 + t*x + 1) * (x + t) + sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) + sage: g,u,v = a.right_xgcd(b); g + x + t + sage: u*a + v*b == g + True + + Specifying ``monic=False``, we *can* get a nonmonic gcd:: + + sage: g,u,v = a.right_xgcd(b,monic=False); g + (4*t^2 + 4*t + 1)*x + 4*t^2 + 4*t + 3 + sage: u*a + v*b == g + True + + The base ring must be a field:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (x^2 + t*x + 1) * (x + t) + sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) + sage: a.right_xgcd(b) + Traceback (most recent call last): + ... + TypeError: the base ring must be a field + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + cdef OrePolynomial G = self + cdef OrePolynomial U = self._parent.one() + cdef OrePolynomial V, V1, V3, Q, R, T + if other.is_zero(): + V = self._parent.zero() + else: + V1 = self._parent.zero() + V3 = other + while not V3.is_zero(): + Q, R = G._right_quo_rem(V3) + T = U - Q*V1 + U = V1 + G = V3 + V1 = T + V3 = R + V = G - U*self + V,_ = V._right_quo_rem(other) + if monic: + lc = ~G.leading_coefficient() + G = lc * G + U = lc * U + V = lc * V + return G,U,V + + @coerce_binop + def right_gcd(self, other, monic=True): + r""" + Return the right gcd of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- boolean (default: ``True``). Return whether the right gcd + should be normalized to be monic. + + OUTPUT: + + The right gcd of ``self`` and ``other``, that is a skew polynomial + `g` with the following property: any skew polynomial is + divisible on the right by `g` iff it is divisible on the right + by both ``self`` and ``other``. + If monic is ``True``, `g` is in addition monic. (With this + extra condition, it is uniquely determined.) + + .. NOTE:: + + Works only if the base ring is a field (otherwise right + gcd do not exist in general). + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (x^2 + t*x + 1) * (x + t) + sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) + sage: a.right_gcd(b) + x + t + + Specifying ``monic=False``, we *can* get a nonmonic gcd:: + + sage: a.right_gcd(b,monic=False) + (4*t^2 + 4*t + 1)*x + 4*t^2 + 4*t + 3 + + The base ring need to be a field:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (x^2 + t*x + 1) * (x + t) + sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) + sage: a.right_gcd(b) + Traceback (most recent call last): + ... + TypeError: the base ring must be a field + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if other.is_zero(): + return self + cdef OrePolynomial A = self + cdef OrePolynomial B = other + while not B.is_zero(): + A, B = B, A % B + if monic: + A = A.right_monic() + return A + + @coerce_binop + def left_gcd(self, other, monic=True): + r""" + Return the left gcd of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- boolean (default: ``True``). Return whether the left gcd + should be normalized to be monic. + + OUTPUT: + + The left gcd of ``self`` and ``other``, that is a skew polynomial + `g` with the following property: any skew polynomial is + divisible on the left by `g` iff it is divisible on the left + by both ``self`` and ``other``. + If monic is ``True``, `g` is in addition monic. (With this + extra condition, it is uniquely determined.) + + .. NOTE:: + + Works only if two following conditions are fulfilled + (otherwise left gcd do not exist in general): + 1) the base ring is a field and 2) the twist map on + this field is bijective. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (x + t) * (x^2 + t*x + 1) + sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) + sage: a.left_gcd(b) + x + t + + Specifying ``monic=False``, we *can* get a nonmonic gcd:: + + sage: a.left_gcd(b,monic=False) + 2*t*x + 4*t + 2 + + The base ring needs to be a field:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (x + t) * (x^2 + t*x + 1) + sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) + sage: a.left_gcd(b) + Traceback (most recent call last): + ... + TypeError: the base ring must be a field + + And the twist map needs to be bijective:: + + sage: FR = R.fraction_field() + sage: f = FR.hom([FR(t)^2]) + sage: S. = FR['x',f] + sage: a = (x + t) * (x^2 + t*x + 1) + sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) + sage: a.left_gcd(b) + Traceback (most recent call last): + ... + NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t^2 + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if other.is_zero(): + return self + cdef OrePolynomial A_, A = self + cdef OrePolynomial B = other + while not B.is_zero(): + A_ = A + A = B + _, B = A_._left_quo_rem(B) + if monic: + A = A.left_monic() + return A + + cdef OrePolynomial _left_lcm_cofactor(self, OrePolynomial other): + r""" + Return a skew polynomial `U` such that `U P = c L` + where `P` is this skew polynomial (``self``), `L` + is the left lcm of `P` and ``other`` and `c` is a + constant + + TESTS:: + + sage: cython(''' + ....: from sage.rings.polynomial.skew_polynomial_element cimport OrePolynomial + ....: def left_lcm_cofactor(OrePolynomial P, OrePolynomial Q): + ....: return P._left_lcm_cofactor(Q) + ....: ''') + + sage: k. = GF(7^5) + sage: Frob = k.frobenius_endomorphism(3) + sage: S. = k['x', Frob] + + sage: D = S.random_element() + sage: P = S.random_element() * D + sage: Q = S.random_element() * D + sage: L = P.left_lcm(Q) + sage: U = left_lcm_cofactor(P, Q) + sage: (U*P).right_monic() == L + True + """ + cdef OrePolynomial Q, R, T + cdef OrePolynomial U = self._parent.one() + cdef OrePolynomial V = self._parent.zero() + while other: + Q, R = self._right_quo_rem(other) + T = U - Q*V + U = V + V = T + self = other + other = R + return V + + @coerce_binop + def left_xlcm(self, other, monic=True): + r""" + Return the left lcm `L` of ``self`` and ``other`` together + with two skew polynomials `U` and `V` such that + + .. MATH:: + + `U \cdot \text{self} = V \cdot \text{other} = L` + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: P = (x + t^2) * (x + t) + sage: Q = 2 * (x^2 + t + 1) * (x * t) + sage: L, U, V = P.left_xlcm(Q) + sage: L + x^5 + (2*t^2 + t + 4)*x^4 + (3*t^2 + 4)*x^3 + (3*t^2 + 3*t + 2)*x^2 + (t^2 + t + 2)*x + + sage: U*P == L + True + sage: V*Q == L + True + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if self.is_zero() or other.is_zero(): + raise ZeroDivisionError("division by zero is not valid") + cdef OrePolynomial V1 = self._left_lcm_cofactor(other) + cdef OrePolynomial L = V1 * self + if monic: + s = ~(L.leading_coefficient()) + L = s * L + V1 = s * V1 + return L, V1, L // other + + cdef OrePolynomial _right_lcm_cofactor(self, OrePolynomial other): + r""" + Return a skew polynomial `U` such that `P U = L c` + where `P` is this skew polynomial (``self``), `L` + is the right lcm of `P` and ``other`` and `c` is a + constant + + TESTS:: + + sage: cython(''' + ....: from sage.rings.polynomial.skew_polynomial_element cimport OrePolynomial + ....: def right_lcm_cofactor(OrePolynomial P, OrePolynomial Q): + ....: return P._right_lcm_cofactor(Q) + ....: ''') + + sage: k. = GF(7^5) + sage: Frob = k.frobenius_endomorphism(3) + sage: S. = k['x', Frob] + + sage: D = S.random_element() + sage: P = D * S.random_element() + sage: Q = D * S.random_element() + sage: L = P.right_lcm(Q) + sage: U = right_lcm_cofactor(P, Q) + sage: (P*U).left_monic() == L + True + """ + cdef OrePolynomial Q, R, T + cdef OrePolynomial U = self._parent.one() + cdef OrePolynomial V = self._parent.zero() + while other: + Q, R = self._left_quo_rem(other) + T = U - V*Q + U = V + V = T + self = other + other = R + return V + + @coerce_binop + def right_xlcm(self, other, monic=True): + r""" + Return the right lcm `L` of ``self`` and ``other`` together + with two skew polynomials `U` and `V` such that + + .. MATH:: + + `\text{self} \cdot U = \text{other} \cdot V = L` + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- a boolean (default: ``True``); whether the right lcm + should be normalized to be monic + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: P = (x + t) * (x + t^2) + sage: Q = 2 * (x + t) * (x^2 + t + 1) + sage: L, U, V = P.right_xlcm(Q) + sage: L + x^4 + (2*t^2 + t + 2)*x^3 + (3*t^2 + 4*t + 1)*x^2 + (3*t^2 + 4*t + 1)*x + t^2 + 4 + sage: P*U == L + True + sage: Q*V == L + True + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if self.is_zero() or other.is_zero(): + raise ZeroDivisionError("division by zero is not valid") + cdef OrePolynomial V1 = self._right_lcm_cofactor(other) + cdef OrePolynomial L = self * V1 + if monic: + s = self.base_ring()(~L.leading_coefficient()) + s = self._parent.twist_map(-L.degree())(s) + L = L * s + V1 = V1 * s + W1, _ = L._left_quo_rem(other) + return L, V1, W1 + + + @coerce_binop + def left_lcm(self, other, monic=True): + r""" + Return the left lcm of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- boolean (default: ``True``). Return whether the left lcm + should be normalized to be monic. + + OUTPUT: + + The left lcm of ``self`` and ``other``, that is a skew polynomial + `g` with the following property: any skew polynomial divides + `g` on the *right* iff it divides both ``self`` and ``other`` + on the *right*. + If monic is ``True``, `g` is in addition monic. (With this + extra condition, it is uniquely determined.) + + .. NOTE:: + + Works only if the base ring is a field (otherwise left + lcm do not exist in general). + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (x + t^2) * (x + t) + sage: b = 2 * (x^2 + t + 1) * (x * t) + sage: c = a.left_lcm(b); c + x^5 + (2*t^2 + t + 4)*x^4 + (3*t^2 + 4)*x^3 + (3*t^2 + 3*t + 2)*x^2 + (t^2 + t + 2)*x + sage: c.is_right_divisible_by(a) + True + sage: c.is_right_divisible_by(b) + True + sage: a.degree() + b.degree() == c.degree() + a.right_gcd(b).degree() + True + + Specifying ``monic=False``, we *can* get a nonmonic gcd:: + + sage: a.left_lcm(b,monic=False) + (t^2 + t)*x^5 + (4*t^2 + 4*t + 1)*x^4 + (t + 1)*x^3 + (t^2 + 2)*x^2 + (3*t + 4)*x + + The base ring needs to be a field:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (x + t^2) * (x + t) + sage: b = 2 * (x^2 + t + 1) * (x * t) + sage: a.left_lcm(b) + Traceback (most recent call last): + ... + TypeError: the base ring must be a field + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if self.is_zero() or other.is_zero(): + raise ZeroDivisionError("division by zero is not valid") + L = self._left_lcm_cofactor(other) * self + if monic: + L = L.right_monic() + return L + + @coerce_binop + def right_lcm(self, other, monic=True): + r""" + Return the right lcm of ``self`` and ``other``. + + INPUT: + + - ``other`` -- a skew polynomial in the same ring as ``self`` + + - ``monic`` -- boolean (default: ``True``). Return whether the right lcm + should be normalized to be monic. + + OUTPUT: + + The right lcm of ``self`` and ``other``, that is a skew polynomial + `g` with the following property: any skew polynomial divides + `g` on the *left* iff it divides both ``self`` and ``other`` + on the *left*. + If monic is ``True``, `g` is in addition monic. (With this + extra condition, it is uniquely determined.) + + .. NOTE:: + + Works only if two following conditions are fulfilled + (otherwise right lcm do not exist in general): + 1) the base ring is a field and 2) the twist map on + this field is bijective. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = (x + t) * (x + t^2) + sage: b = 2 * (x + t) * (x^2 + t + 1) + sage: c = a.right_lcm(b); c + x^4 + (2*t^2 + t + 2)*x^3 + (3*t^2 + 4*t + 1)*x^2 + (3*t^2 + 4*t + 1)*x + t^2 + 4 + sage: c.is_left_divisible_by(a) + True + sage: c.is_left_divisible_by(b) + True + sage: a.degree() + b.degree() == c.degree() + a.left_gcd(b).degree() + True + + Specifying ``monic=False``, we *can* get a nonmonic gcd:: + + sage: a.right_lcm(b,monic=False) + 2*t*x^4 + (3*t + 1)*x^3 + (4*t^2 + 4*t + 3)*x^2 + + (3*t^2 + 4*t + 2)*x + 3*t^2 + 2*t + 3 + + The base ring needs to be a field:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = (x + t) * (x + t^2) + sage: b = 2 * (x + t) * (x^2 + t + 1) + sage: a.right_lcm(b) + Traceback (most recent call last): + ... + TypeError: the base ring must be a field + + And the twist map needs to be bijective:: + + sage: FR = R.fraction_field() + sage: f = FR.hom([FR(t)^2]) + sage: S. = FR['x',f] + sage: a = (x + t) * (x + t^2) + sage: b = 2 * (x + t) * (x^2 + t + 1) + sage: a.right_lcm(b) + Traceback (most recent call last): + ... + NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t^2 + """ + if self.base_ring() not in Fields: + raise TypeError("the base ring must be a field") + if self.is_zero() or other.is_zero(): + raise ZeroDivisionError("division by zero is not valid") + L = self * self._right_lcm_cofactor(other) + if monic: + L = L.left_monic() + return L + + def _repr_(self, name=None): + r""" + Return string representation of this skew polynomial. + + INPUT: + + - ``name`` -- the name of the variable (default: the + name given when the skew polynomial ring was created) + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t^2 + 1/2*x*t + sage: a._repr_() + '(1/2*t + 1/2)*x + t^2' + sage: a._repr_(name='y') + '(1/2*t + 1/2)*y + t^2' + """ + s = " " + m = self.degree() + 1 + if name is None: + name = self.parent().variable_name() + atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic') + coeffs = self.list() + for n in reversed(range(m)): + x = coeffs[n] + if x: + if n != m-1: + s += " + " + x = y = repr(x) + if y.find("-") == 0: + y = y[1:] + if not atomic_repr and n > 0 and (y.find("+") != -1 or y.find("-") != -1): + x = "(%s)"%x + if n > 1: + var = "*%s^%s"%(name,n) + elif n==1: + var = "*%s"%name + else: + var = "" + s += "%s%s"%(x,var) + s = s.replace(" + -", " - ") + s = re.sub(r' 1(\.0+)?\*',' ', s) + s = re.sub(r' -1(\.0+)?\*',' -', s) + if s == " ": + return "0" + return s[1:] + + def _latex_(self, name=None): + r""" + Return a latex representation of this skew polynomial. + + INPUT: + + - ``name`` -- the name of the variable (default: the + name given when the skew polynomial ring was created) + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t^2 + 1/2*x*t + sage: a._latex_() + '\\left(\\frac{1}{2} t + \\frac{1}{2}\\right) x + t^{2}' + sage: a._latex_(name='y') + '\\left(\\frac{1}{2} t + \\frac{1}{2}\\right) y + t^{2}' + """ + s = " " + coeffs = self.list() + m = len(coeffs) + if name is None: + name = self.parent().latex_variable_names()[0] + atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic') + for n in reversed(range(m)): + x = self[n] + x = y = x._latex_() + if x != '0': + if n != m-1: + s += " + " + if y.find("-") == 0: + y = y[1:] + if not atomic_repr and n > 0 and (y.find("+") != -1 or y.find("-") != -1): + x = "\\left(%s\\right)"%x + if n > 1: + var = "|%s^{%s}"%(name,n) + elif n==1: + var = "|%s"%name + else: + var = "" + s += "%s %s"%(x,var) + s = s.replace(" + -", " - ") + s = re.sub(" 1(\.0+)? \|"," ", s) + s = re.sub(" -1(\.0+)? \|", " -", s) + s = s.replace("|","") + if s == " ": + return "0" + return s[1:].lstrip().rstrip() + + def _is_atomic(self): + r""" + Check ``self`` is a single monomial whose leading coefficient + is atomic in the base ring. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: S([t+1])._is_atomic() + False + sage: S([1])._is_atomic() + True + """ + return (self.degree() == self.valuation() and + self.leading_coefficient()._is_atomic()) + + def __nonzero__(self): + r""" + Test whether ``self`` is nonzero. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + 1 + sage: bool(a) + True + sage: b = S.zero() + sage: bool(b) + False + """ + return not self.is_zero() + + def base_ring(self): + r""" + Return the base ring of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = S.random_element() + sage: a.base_ring() + Univariate Polynomial Ring in t over Integer Ring + sage: a.base_ring() is R + True + """ + return self.parent().base_ring() + + def shift(self, n): + r""" + Return ``self`` multiplied on the right by the power `x^n`. + + If `n` is negative, terms below `x^n` will be discarded. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^5 + t^4*x^4 + t^2*x^2 + t^10 + sage: a.shift(0) + x^5 + t^4*x^4 + t^2*x^2 + t^10 + sage: a.shift(-1) + x^4 + t^4*x^3 + t^2*x + sage: a.shift(-5) + 1 + sage: a.shift(2) + x^7 + t^4*x^6 + t^2*x^4 + t^10*x^2 + + One can also use the infix shift operator:: + + sage: a >> 2 + x^3 + t^4*x^2 + t^2 + sage: a << 2 + x^7 + t^4*x^6 + t^2*x^4 + t^10*x^2 + """ + if n == 0 or self.degree() < 0: + return self + if n > 0: + return self._parent(n*[self.base_ring().zero()] + self.list(), check=False) + if n < 0: + if n > self.degree(): + return self._parent([]) + else: + return self._parent(self.list()[-n:], check=False) + + def __lshift__(self, k): + r""" + Return ``self`` multiplied on the right by the power `x^k`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^5 + t^4*x^4 + t^2*x^2 + t^10 + sage: a << 2 + x^7 + t^4*x^6 + t^2*x^4 + t^10*x^2 + """ + return self.shift(k) + + def __rshift__(self, k): + r""" + Return ``self`` multiplied on the right by the power `x^(-k)`. + + If `n` is negative, terms below `x^n` will be discarded. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^5 + t^4*x^4 + t^2*x^2 + t^10 + sage: a >> 2 + x^3 + t^4*x^2 + t^2 + """ + return self.shift(-k) + + def change_variable_name(self, var): + r""" + Change the name of the variable of ``self``. + + This will create the skew polynomial ring with the new name but same + base ring and twist map. The returned skew polynomial will be an element + of that skew polynomial ring. + + INPUT: + + - ``var`` -- the name of the new variable + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x', sigma] + sage: a = x^3 + (2*t + 1)*x + t^2 + 3*t + 5 + sage: b = a.change_variable_name('y'); b + y^3 + (2*t + 1)*y + t^2 + 3*t + 5 + + Note that a new parent is created at the same time:: + + sage: b.parent() + Skew Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring + twisted by t |--> t + 1 + """ + R = self._parent.change_var(var) + return R(self.list()) + + def is_term(self): + r""" + Return ``True`` if ``self`` is an element of the base ring times a + power of the generator. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: x.is_term() + True + sage: R(1).is_term() + True + sage: (3*x^5).is_term() + True + sage: (1+3*x^5).is_term() + False + + If you want to test that ``self`` also has leading coefficient 1, use + :meth:`is_monomial()` instead:: + + sage: (3*x^5).is_monomial() + False + """ + return len(self.exponents()) == 1 + + def is_monomial(self): + r""" + Return ``True`` if ``self`` is a monomial, i.e., a power of + the generator. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: x.is_monomial() + True + sage: (x+1).is_monomial() + False + sage: (x^2).is_monomial() + True + sage: S(1).is_monomial() + True + + The coefficient must be 1:: + + sage: (2*x^5).is_monomial() + False + sage: S(t).is_monomial() + False + + To allow a non-1 leading coefficient, use is_term():: + + sage: (2*x^5).is_term() + True + sage: S(t).is_term() + True + """ + return self.is_term() and self.leading_coefficient() == 1 + + cpdef list coefficients(self, sparse=True): + r""" + Return the coefficients of the monomials appearing in ``self``. + + If ``sparse=True`` (the default), return only the non-zero coefficients. + Otherwise, return the same value as ``self.list()``. + + .. NOTE:: + + This should be overridden in subclasses. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: a.coefficients() + [t^2 + 1, t + 1, 1] + sage: a.coefficients(sparse=False) + [t^2 + 1, 0, t + 1, 0, 1] + """ + raise NotImplementedError + + def number_of_terms(self): + r""" + Return the number of non-zero coefficients of ``self``. + + This is also known as the weight, hamming weight or sparsity. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: a.number_of_terms() + 3 + + This is also an alias for ``hamming_weight``:: + + sage: a.hamming_weight() + 3 + """ + return len(self.coefficients()) + + # alias hamming_weight for number_of_terms: + hamming_weight = number_of_terms + + def __copy__(self): + r""" + Return a "copy" of ``self``. + + In Sage, since skew polynomials are immutable, this just returns + ``self`` again. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: b = copy(a) + sage: b is a + True + """ + return self + + cpdef bint is_zero(self): + r""" + Return ``True`` if ``self`` is the zero polynomial. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + 1 + sage: a.is_zero() + False + sage: b = S.zero() + sage: b.is_zero() + True + """ + return self.degree() == -1 + + cpdef bint is_one(self): + r""" + Test whether this polynomial is `1`. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: R(1).is_one() + True + sage: (x + 3).is_one() + False + """ + return self.degree() == 0 and self[0].is_one() + + @coerce_binop + def right_mod(self, other): + r""" + Return the remainder of right division of ``self`` by ``other``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + t*x^2 + sage: b = x + 1 + sage: a % b + t + 1 + sage: (x^3 + x - 1).right_mod(x^2 - 1) + 2*x - 1 + """ + return self % other + + @coerce_binop + def left_mod(self, other): + r""" + Return the remainder of left division of ``self`` by ``other``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = 1 + t*x^2 + sage: b = x + 1 + sage: a.left_mod(b) + 2*t^2 + 4*t + """ + _, r = self.left_quo_rem(other) + return r + + def is_constant(self): + r""" + Return whether ``self`` is a constant polynomial. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: R(2).is_constant() + True + sage: (x + 1).is_constant() + False + """ + return self.degree() <= 0 + + def exponents(self): + r""" + Return the exponents of the monomials appearing in ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: a.exponents() + [0, 2, 4] + """ + return [i for i in range(self.degree()+1) if bool(self[i])] + + def prec(self): + r""" + Return the precision of ``self``. + + This is always infinity, since polynomials are of infinite precision by + definition (there is no big-oh). + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: x.prec() + +Infinity + """ + return infinity + + def padded_list(self, n=None): + r""" + Return list of coefficients of ``self`` up to (but not including) + degree `n`. + + Includes `0`s in the list on the right so that the list always has length + exactly `n`. + + INPUT: + + - ``n`` -- (default: ``None``); if given, an integer that + is at least `0` + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + t*x^3 + t^2*x^5 + sage: a.padded_list() + [1, 0, 0, t, 0, t^2] + sage: a.padded_list(10) + [1, 0, 0, t, 0, t^2, 0, 0, 0, 0] + sage: len(a.padded_list(10)) + 10 + sage: a.padded_list(3) + [1, 0, 0] + sage: a.padded_list(0) + [] + sage: a.padded_list(-1) + Traceback (most recent call last): + ... + ValueError: n must be at least 0 + """ + v = self.list() + if n is None: + return v + if n < 0: + raise ValueError("n must be at least 0") + if len(v) < n: + z = self._parent.base_ring().zero() + return v + [z]*(n - len(v)) + else: + return v[:int(n)] + + def variable_name(self): + r""" + Return the string name of the variable used in ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x + t + sage: a.variable_name() + 'x' + """ + return self.parent().variable_name() + + +cdef void lmul_gen(list A, Morphism m, d): + r""" + Helper function + """ + if m is None: + A.append(A[-1]) + for j in range(len(A)-2, 0, -1): + A[j] = d(A[j]) + A[j-1] + else: + A.append(m(A[-1])) + for j in range(len(A)-2, 0, -1): + A[j] = d(A[j]) + m(A[j-1]) + A[0] = d(A[0]) + +cdef class OrePolynomial_generic_dense(OrePolynomial): + r""" + Generic implementation of dense skew polynomial supporting any valid base + ring and twist map. + """ + def __init__(self, parent, x=None, int check=1, int construct=0, **kwds): + r""" + Construct a skew polynomial over the given parent with the given + coefficients. + + INPUT: + + - ``parent`` -- parent of ``self`` + + - ``x`` -- list of coefficients from which ``self`` can be constructed + + - ``check`` -- flag variable to normalize the polynomial + + - ``construct`` -- boolean (default: ``False``) + + TESTS:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + + We create a skew polynomial from a list:: + + sage: S([t,1]) + x + t + + from another skew polynomial:: + + sage: S(x^2 + t) + x^2 + t + + from a constant:: + + sage: x = S(t^2 + 1); x + t^2 + 1 + sage: x.parent() is S + True + """ + OrePolynomial.__init__(self, parent) + if x is None: + self._coeffs = [] + return + + R = parent.base_ring() + if isinstance(x, list): + if check: + self._coeffs = [R(t) for t in x] + self.__normalize() + else: + self._coeffs = x + return + + if isinstance(x, OrePolynomial): + if (x)._parent is self._parent: + x = list(x.list()) + elif R.has_coerce_map_from((x)._parent): + try: + if x.is_zero(): + self._coeffs = [] + return + except (AttributeError, TypeError): + pass + x = [x] + else: + self._coeffs = [R(a, **kwds) for a in x.list()] + if check: + self.__normalize() + return + + elif isinstance(x, int) and x == 0: + self._coeffs = [] + return + + elif isinstance(x, dict): + x = _dict_to_list(x, R.zero()) + + elif not isinstance(x, list): + x = [x] + if check: + self._coeffs = [R(z, **kwds) for z in x] + self.__normalize() + else: + self._coeffs = x + + def __reduce__(self): + r""" + Return the generic dense skew polynomial corresponding to the + current parameters provided ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: loads(dumps(x)) == x + True + sage: loads(dumps(x)) + x + """ + return (self._parent, (self._coeffs,)) + + cdef long _hash_c(self): + r""" + This hash incorporates the name of the variable. + + .. NOTE:: + + This is an internal method. Use :meth:`__hash__` instead. + """ + #todo - come up with a way to create hashes of zero that + # that do not incorrectly indicate that the element is 0. + cdef long result = 0 + cdef long result_mon + cdef long c_hash + cdef long var_name_hash = 0 + cdef int i + for i from 0 <= i < len(self._coeffs): + if i == 1: + var_name_hash = hash((self._parent)._names[0]) + c_hash = hash(self._coeffs[i]) + if c_hash != 0: + if i == 0: + result += c_hash + else: + result_mon = c_hash + result_mon = (1000003 * result_mon) ^ var_name_hash + result_mon = (1000003 * result_mon) ^ i + result += result_mon + if result == -1: + return -2 + return result + + cdef OrePolynomial _new_c(self, list coeffs, Parent P, char check=0): + r""" + Fast creation of a new skew polynomial given a list of coefficients. + + .. WARNING:: + + The list ``coeffs`` is stored internally in the newly created skew + polynomial, so this must not be modified after calling this method. + + TESTS:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t*x^3 + x^4 + (t+1)*x^2 + sage: a.truncate(4) #indirect doctest + t*x^3 + (t + 1)*x^2 + """ + cdef type t = type(self) + cdef OrePolynomial_generic_dense f = t.__new__(t) + f._parent = P + f._coeffs = coeffs + if check: + f.__normalize() + return f + + cdef void __normalize(self): + r""" + Remove higher order `0`-coefficients from the representation of ``self``. + + TESTS:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma]; S #indirect doctest + Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + """ + cdef list x = self._coeffs + cdef Py_ssize_t n = len(x) - 1 + while n >= 0 and not x[n]: + del x[n] + n -= 1 + + cpdef _richcmp_(left, right, int op): + r""" + Compare the two skew polynomials ``self`` and ``other``. + + We order polynomials first by degree, then in dictionary order + starting with the coefficient of largest degree. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: b = (2*t^2)*x + t + 1 + sage: a > b + True + sage: a < b + False + """ + cdef x = (left)._coeffs + cdef y = (right)._coeffs + return PyObject_RichCompare(x, y, op) + + def __iter__(self): + r""" + Iterate over the list of coefficients of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: P = S([1, 2, 3]) + sage: [y for y in iter(P)] + [1, 2, 3] + """ + return iter((self)._coeffs) + + def __getitem__(self, n): + r""" + Return the `n`-th coefficient of ``self``. + + INPUT: + + - ``n`` -- an integer + + OUTPUT: + + - the ``n``-th coefficient of ``self`` + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t*x^2 + (t + 3/7)*x + t^2 + sage: a[1] + t + 3/7 + sage: a[3] + 0 + """ + try: + return self._coeffs[n] + except IndexError: + return self.base_ring().zero() + + cpdef list list(self, bint copy=True): + r""" + Return a list of the coefficients of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: l = a.list(); l + [t^2 + 1, 0, t + 1, 0, 1] + + Note that `l` is a list, it is mutable, and each call to the list + method returns a new list:: + + sage: type(l) + <... 'list'> + sage: l[0] = 5 + sage: a.list() + [t^2 + 1, 0, t + 1, 0, 1] + """ + if copy: + # This creates a shallow copy + return list((self)._coeffs) + else: + return (self)._coeffs + + cpdef dict dict(self): + r""" + Return a dictionary representation of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^2012 + t*x^1006 + t^3 + 2*t + sage: a.dict() + {0: t^3 + 2*t, 1006: t, 2012: 1} + """ + cdef dict X = {} + cdef list Y = (self)._coeffs + cdef int i + for i in range(len(Y)): + c = Y[i] + if c: + X[i] = c + return X + + cpdef int degree(self): + r""" + Return the degree of ``self``. + + By convention, the zero skew polynomial has degree `-1`. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^2 + t*x^3 + t^2*x + 1 + sage: a.degree() + 3 + + By convention, the degree of `0` is `-1`:: + + sage: S(0).degree() + -1 + """ + return len(self._coeffs) - 1 + + cpdef _add_(self, right): + r""" + Add two polynomials. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = S.random_element(monic=True); a + x^2 + (-1/95*t - 1/2)*x + sage: b = -S.random_element(monic=True); b + -x^2 - 1/2*x + sage: c = a+b; c + (-1/95*t - 1)*x + sage: c.degree() + 1 + """ + cdef Py_ssize_t i, min + cdef list x = (self)._coeffs + cdef list y = (right)._coeffs + cdef Py_ssize_t dx = len(x), dy = len(y) + if dx > dy: + r = self._new_c([x[i] + y[i] for i from 0 <= i < dy] + x[dy:], self._parent, 0) + elif dx < dy: + r = self._new_c([x[i] + y[i] for i from 0 <= i < dx] + y[dx:], self._parent, 0) + else: + r = self._new_c([x[i] + y[i] for i in range(dx)], self._parent, 1) + return r + + cpdef _sub_(self, right): + r""" + Subtract polynomial ``right`` from ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = S.random_element(monic=True); a + x^2 + (-1/95*t - 1/2)*x + sage: b = S.random_element(monic=True); b + x^2 + 1/2*x + sage: c = a-b; c + (-1/95*t - 1)*x + sage: c.degree() + 1 + """ + cdef Py_ssize_t i, min + cdef list x = (self)._coeffs + cdef list y = (right)._coeffs + cdef Py_ssize_t dx = len(x), dy = len(y) + cdef RingElement c + if dx > dy: + r = self._new_c([x[i] - y[i] for i from 0 <= i < dy] + x[dy:], self._parent, 0) + elif dx < dy: + r = self._new_c([x[i] - y[i] for i from 0 <= i < dx] + [ -c for c in y[dx:] ], self._parent, 0) + else: + r = self._new_c([x[i] - y[i] for i from 0 <= i < dx], self._parent, 1) + return r + + cpdef _neg_(self): + r""" + Return the negative of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t*x^2 + x - 3 + sage: -a + -t*x^2 - x + 3 + """ + c = self._new_c([-x for x in (self)._coeffs], + self._parent, 0) + return c + + def valuation(self): + r""" + Return the minimal degree of a non-zero monomial of ``self``. + + By convention, the zero skew polynomial has valuation `+\infty`. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = x^2 + t*x^3 + t^2*x + sage: a.valuation() + 1 + + By convention, the valuation of `0` is `+\infty`:: + + sage: S(0).valuation() + +Infinity + """ + cdef list x = self._coeffs + if not x: + return infinity + cdef Py_ssize_t v = 0 + while x[v].is_zero() and v < len(x): + v += 1 + return v + + def truncate(self, n): + r""" + Return the polynomial resulting from discarding all monomials of degree + at least `n`. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = t*x^3 + x^4 + (t+1)*x^2 + sage: a.truncate(4) + t*x^3 + (t + 1)*x^2 + sage: a.truncate(3) + (t + 1)*x^2 + """ + return self._new_c(self._coeffs[:n], self._parent, 1) + + cdef list _mul_list(self, list A): + r""" + Return the list of coefficients of the product of this + Ore polynomial by that whose coefficients are given by ``A``. + + This is an helper function. + """ + cdef list BA = [ self.base_ring().zero() ] * (len(self._coeffs) + len(A) - 1) + cdef Morphism m = self._parent._morphism + cdef d = self._parent._derivation + cdef coeff + cdef Py_ssize_t i, j + for i in range(len(self._coeffs)): + if i: + lmul_gen(A, m, d) + coeff = self._coeffs[i] + if coeff: + for j in range(len(A)): + BA[j] += coeff * A[j] + return BA + + cpdef _lmul_(self, Element s): + cdef coeffs = self._mul_list([s]) + return self._new_c(coeffs, self._parent, 1) + + cpdef _rmul_(self, Element s): + return self._new_c([ s*c for c in self._coeffs ], self._parent) + + cpdef _mul_(self, other): + cdef coeffs = list((other)._coeffs) + if coeffs: + coeffs = self._mul_list(coeffs) + return self._new_c(coeffs, self._parent, 1) + + cdef _left_quo_rem(self, OrePolynomial other): + cdef list A = list(self._coeffs) + cdef Py_ssize_t degB = other.degree() + cdef Morphism m = self._parent.twisting_morphism(-degB) + cdef RingElement s, inv = other.leading_coefficient() + cdef Py_ssize_t nb = len(A) - degB + cdef list L + cdef list quo = [ ] + if not inv.is_unit(): + raise ValueError("the leading coefficient of the divisor in not a unit") + inv = inv.inverse_of_unit() + for i in range(nb-1, -1, -1): + s = A[i+degB] + if s: + s = s * inv + if m is not None: + s = m(s) + L = (other)._mul_list([s]) + for j in range(len(L)-1): + A[i+j] -= L[j] + quo.append(s) + quo.reverse() + return self._new_c(quo, self._parent), self._new_c(A[:degB], self._parent, 1) + + cdef _right_quo_rem(self, OrePolynomial other): + cdef list A = list(self._coeffs) + cdef Py_ssize_t i, j + cdef Py_ssize_t degB = other.degree() + cdef Py_ssize_t nb = len(A) - degB + cdef Morphism m = self._parent._morphism + cdef d = self._parent._derivation + cdef RingElement s, inv = other.leading_coefficient() + cdef list XnB = (other)._coeffs + cdef list XnBs = [ ] + cdef list quo = [ ] + if not inv.is_unit(): + raise ValueError("the leading coefficient of the divisor in not a unit") + inv = inv.inverse_of_unit() + for i in range(nb): + if i: + XnB = list(XnB) + lmul_gen(XnB, m, d) + XnBs.append(XnB) + for i in range(nb-1, -1, -1): + s = A[i+degB] + if s: + if m: + s *= self._parent.twisting_morphism(i)(inv) + else: + s *= inv + XnB = XnBs[i] + for j in range(len(XnB)-1): + A[j] -= s * XnB[j] + quo.append(s) + quo.reverse() + return self._new_c(quo, self._parent), self._new_c(A[:degB], self._parent, 1) + + + cpdef list coefficients(self, sparse=True): + r""" + Return the coefficients of the monomials appearing in ``self``. + + If ``sparse=True`` (the default), return only the non-zero coefficients. + Otherwise, return the same value as ``self.list()``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: a = 1 + x^4 + (t+1)*x^2 + t^2 + sage: a.coefficients() + [t^2 + 1, t + 1, 1] + sage: a.coefficients(sparse=False) + [t^2 + 1, 0, t + 1, 0, 1] + """ + zero = self.parent().base_ring().zero() + if sparse: + return [c for c in self._coeffs if not c.is_zero()] + else: + return self._coeffs + +cdef class ConstantOrePolynomialSection(Map): + r""" + Representation of the canonical homomorphism from the constants of a skew + polynomial ring to the base ring. + + This class is necessary for automatic coercion from zero-degree skew + polynomial ring into the base ring. + + EXAMPLES:: + + sage: from sage.rings.polynomial.skew_polynomial_element import ConstantOrePolynomialSection + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: m = ConstantOrePolynomialSection(S, R); m + Generic map: + From: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + To: Univariate Polynomial Ring in t over Rational Field + """ + cpdef Element _call_(self, x): + r""" + Return the corresponding element of the base ring if ``self`` is a + constant skew polynomial. Otherwise, it fails. + + TESTS:: + + sage: from sage.rings.polynomial.skew_polynomial_element import ConstantOrePolynomialSection + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: m = ConstantOrePolynomialSection(S, R); m + Generic map: + From: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + To: Univariate Polynomial Ring in t over Rational Field + sage: m(S([0,1])-S([0,1])) + 0 + sage: m(S([3,1])-S([0,1])) + 3 + sage: m(S([0,1])-S([0,t])) + Traceback (most recent call last): + ... + TypeError: not a constant polynomial + """ + if x.degree() <= 0: + try: + return (x.constant_coefficient()) + except AttributeError: + return ((x).constant_coefficient()) + else: + raise TypeError("not a constant polynomial") + + +cdef class OrePolynomialBaseringInjection(Morphism): + r""" + Representation of the canonical homomorphism from a ring `R` into a skew + polynomial ring over `R`. + + This class is necessary for automatic coercion from the base ring to the Ore + polynomial ring. + + .. SEEALSO:: + + :class:`~sage.rings.polynomial.polynomial_element.PolynomialBaseringInjection` + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: S.coerce_map_from(S.base_ring()) #indirect doctest + Skew Polynomial base injection morphism: + From: Univariate Polynomial Ring in t over Rational Field + To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + """ + + def __init__(self, domain, codomain): + r""" + Construct a Skew Polynomial Basering Injection. + + INPUT: + + - ``domain`` -- a ring `R`. This will be the domain of the injection. + + - ``codomain`` -- a skew polynomial ring over ``domain``. This will be + the codomain. + + TESTS:: + + sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: OrePolynomialBaseringInjection(k, k['x', Frob]) + Skew Polynomial base injection morphism: + From: Finite Field in t of size 5^3 + To: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: R. = QQ[] + sage: OrePolynomialBaseringInjection(QQ, k['x', Frob]) + Traceback (most recent call last): + ... + AssertionError: the domain of the injection must be the base ring of the skew polynomial ring + """ + assert codomain.base_ring() is domain, \ + "the domain of the injection must be the base ring of the skew polynomial ring" + Morphism.__init__(self, Hom(domain,codomain)) + self._an_element = codomain.gen() + self._repr_type_str = "Ore Polynomial base injection" + self._new_constant_poly_ = self._an_element._new_constant_poly + + def an_element(self): + r""" + Return an element of the codomain of the ring homomorphism. + + EXAMPLES:: + + sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: m = OrePolynomialBaseringInjection(k, k['x', Frob]) + sage: m.an_element() + x + """ + return self._an_element + + cpdef Element _call_(self, e): + r""" + Return the corresponding skew polynomial to the element from the + base ring according to ``self``. + + INPUT: + + - ``e`` -- element belonging to the base ring according to ``self`` + + OUTPUT: + + The skew polynomial corresponding to `e` according to ``self``. + + TESTS:: + + sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: m = OrePolynomialBaseringInjection(k, k['x', Frob]) + sage: m(4) + 4 + sage: parent(m(4)) + Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + """ + try: + return self._codomain._element_constructor_(e) + except AttributeError: + return self._codomain(e) + + def section(self): + r""" + Return the canonical homomorphism from the constants of a skew + polynomial ring to the base ring according to ``self``. + + TESTS:: + + sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: m = OrePolynomialBaseringInjection(k, k['x', Frob]) + sage: m.section() + Generic map: + From: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + To: Finite Field in t of size 5^3 + """ + return ConstantOrePolynomialSection(self._codomain, self.domain()) diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py new file mode 100644 index 00000000000..00bff43a8fd --- /dev/null +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -0,0 +1,724 @@ +r""" +Univariate Ore Polynomial Rings + +AUTHOR: + +- Xavier Caruso (2020-04) + +""" +# *************************************************************************** +# Copyright (C) 2020 Xavier Caruso +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License as published by +# the Free Software Foundation, either version 2 of the License, or +# (at your option) any later version. +# https://www.gnu.org/licenses/ +# *************************************************************************** + +import sage + +from sage.structure.richcmp import op_EQ +from sage.misc.prandom import randint +from sage.misc.cachefunc import cached_method +from sage.rings.infinity import Infinity +from sage.structure.category_object import normalize_names + +from sage.structure.unique_representation import UniqueRepresentation +from sage.rings.ring import Algebra, Field +from sage.rings.integer import Integer + +from sage.categories.commutative_rings import CommutativeRings +from sage.categories.algebras import Algebras +from sage.categories.fields import Fields + +from sage.categories.morphism import Morphism, IdentityMorphism +from sage.rings.morphism import RingHomomorphism +from sage.rings.derivation import RingDerivation +from sage.categories.homset import Hom +from sage.categories.map import Section + +from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing +from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection + +WORKING_CENTER_MAX_TRIES = 1000 + + +# Generic implementation of skew polynomial rings +################################################# + +class OrePolynomialRing(Algebra, UniqueRepresentation): + Element = None + + @staticmethod + def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): + if base_ring not in CommutativeRings(): + raise TypeError('base_ring must be a commutative ring') + if isinstance(twist, Morphism): + if (twist.domain() is not base_ring + or twist.codomain() is not base_ring): + raise TypeError("the twisting morphism must be an endomorphism of base_ring (=%s)" % base_ring) + if twist.is_identity(): + morphism = None + else: + morphism = twist + derivation = None + elif isinstance(twist, RingDerivation): + if (twist.domain() is not base_ring + or twist.codomain() is not base_ring): + raise TypeError("the twisting derivation must be an endomorphism of base_ring (=%s)" % base_ring) + morphism = twist.parent().twisting_morphism() + if twist: + derivation = twist + else: + derivation = None + else: + raise TypeError("the twist map must be a ring morphism or a derivation") + if sparse: + raise NotImplementedError("sparse skew polynomial rings are not implemented") + if names is None: + raise TypeError("you must specify the name of the variable") + try: + names = normalize_names(1, names)[0] + except IndexError: + raise NotImplementedError("multivariate skew polynomials rings not supported") + + # We find the best constructor + if derivation is None and morphism is None: + return PolynomialRing(base_ring, names, sparse) + + constructors = [ ] + if derivation is None: + if base_ring in Fields(): + try: + order = morphism.order() + if order is not Infinity: + if base_ring.is_finite(): + constructors.append(sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field) + else: + constructors.append(sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order) + except (AttributeError, NotImplementedError): + pass + constructors.append(sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing) + + for constructor in constructors: + try: + return constructor(base_ring, morphism, derivation, names, sparse) + except (AttributeError, NotImplementedError): + pass + + # We fallback to generic implementation + return cls.__classcall__(cls, base_ring, morphism, derivation, names, sparse) + + def __init__(self, base_ring, morphism, derivation, name, sparse, category=None): + r""" + Initialize ``self``. + + INPUT: + + - ``base_ring`` -- a commutative ring + + - ``twisting_morphism`` -- an automorphism of the base ring + + - ``name`` -- string or list of strings representing the name of + the variables of ring + + - ``sparse`` -- boolean (default: ``False``) + + - ``category`` -- a category + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R,sigma) + sage: S.category() + Category of algebras over Univariate Polynomial Ring in t over Integer Ring + sage: S([1]) + S([-1]) + 0 + sage: TestSuite(S).run() + """ + if self.Element is None: + self.Element = sage.rings.polynomial.ore_polynomial_element.OrePolynomial_generic_dense + self.__is_sparse = sparse + self._morphism = morphism + self._derivation = derivation + category = Algebras(base_ring).or_subcategory(category) + Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) + + def _element_constructor_(self, a=None, check=True, construct=False, **kwds): + r""" + Convert a base ring element ``a`` into a constant of this univariate + skew polynomial ring, possibly non-canonically. + + INPUT: + + - ``a`` -- (default: ``None``) an element of the base ring + of ``self`` or a ring that has a coerce map from ``self`` + + - ``check`` -- boolean (default: ``True``) + + - ``construct`` -- boolean (default: ``False``) + + OUTPUT: + + An zero-degree skew polynomial in ``self``, equal to ``a``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R,sigma) + sage: S(1 + x + x^2 + x^3) + x^3 + x^2 + x + 1 + sage: S(1 + t) + t + 1 + sage: S(1 + t).degree() + 0 + sage: S(0).list() + [] + + TESTS:: + + sage: S(x, check=True) + x + """ + C = self.Element + if isinstance(a, list): + return C(self, a, check=check, construct=construct) + if isinstance(a, sage.structure.element.Element): + P = a.parent() + def build(check): + if a.is_zero(): + return P.zero() + else: + return C(self, [a], check=check, construct=construct) + if P is self: + return a + elif P is self.base_ring(): + build(False) + elif P == self.base_ring() or self.base_ring().has_coerce_map_from(P): + build(True) + try: + return a._polynomial_(self) + except AttributeError: + pass + if isinstance(a, str): + try: + from sage.misc.parser import Parser, LookupNameMaker + R = self.base_ring() + p = Parser(Integer, R, LookupNameMaker({self.variable_name(): self.gen()}, R)) + return self(p.parse(a)) + except NameError: + raise TypeError("unable to coerce string") + return C(self, a, check, construct=construct, **kwds) + + def _coerce_map_from_base_ring(self): + """ + Return a coercion map from the base ring of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: S. = SkewPolynomialRing(R, R.hom([t + 1])) + sage: S.coerce_map_from(R) + Skew Polynomial base injection morphism: + From: Univariate Polynomial Ring in t over Integer Ring + To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + sage: x.parent() + Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + sage: t.parent() + Univariate Polynomial Ring in t over Integer Ring + sage: y = x + t # indirect doctest + sage: y + x + t + sage: y.parent() is S + True + """ + return OrePolynomialBaseringInjection(self.base_ring(), self) + + def _coerce_map_from_(self, P): + r""" + Check whether ``self`` has a coerce map from ``P``. + + The rings that canonically coerce into this ring are: + + - this ring itself + + - any ring that canonically coerces to the base ring of this ring + + - skew polynomial rings in the same variable and automorphism over + any base ring that canonically coerces to the base ring of this ring + + INPUT: + + - ``P`` -- a ring + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R,sigma) + sage: S.has_coerce_map_from(S) + True + sage: S.has_coerce_map_from(R) + True + sage: S.has_coerce_map_from(ZZ) + True + sage: S.has_coerce_map_from(GF(5^3)) + False + + sage: S.coerce_map_from(ZZ) + Composite map: + From: Integer Ring + To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + Defn: Polynomial base injection morphism: + From: Integer Ring + To: Univariate Polynomial Ring in t over Integer Ring + then + Skew Polynomial base injection morphism: + From: Univariate Polynomial Ring in t over Integer Ring + To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + sage: S.coerce_map_from(S) + Identity endomorphism of Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + """ + base_ring = self.base_ring() + try: + connecting = base_ring.coerce_map_from(P) + if connecting is not None: + return self.coerce_map_from(base_ring) * connecting + except TypeError: + pass + if isinstance(P, OrePolynomialRing): + if self.__is_sparse and not P.is_sparse(): + return False + if P.variable_name() == self.variable_name(): + return base_ring.has_coerce_map_from(P.base_ring()) + + def _repr_(self): + r""" + Return a string representation of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R,sigma) + sage: S + Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + """ + if self._derivation is None: + s = "Skew Polynomial Ring in %s over %s twisted by %s" % (self.variable_name(), + self.base_ring(), + self._morphism._repr_short()) + else: + s = "Ore Polynomial Ring in %s over %s twisted by " % (self.variable_name(), self.base_ring()) + if self._morphism is not None: + s += "%s and " % self._morphism._repr_short() + s += self._derivation._repr_() + if self.is_sparse(): + s = "Sparse " + s + return s + + def _latex_(self): + r""" + Return a latex representation of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R,sigma) + sage: latex(S) + \Bold{Z}[t][x,\begin{array}{l} + \text{\texttt{Ring{ }endomorphism...}} + \end{array}] + """ + from sage.misc.latex import latex + s = "%s\\left[%s" % latex(self.base_ring(), self._latex_variable_names()[0]) + sep = ";" + if self._morphism is not None: + s += sep + latex(self._morphism) + sep = "," + if self._derivation is not None: + s += sep + latex(self._derivation) + return s + "\\right]" + + def change_var(self, var): + r""" + Return the skew polynomial ring in variable ``var`` with the same base + ring and twist map as ``self``. + + INPUT: + + - ``var`` -- a string representing the name of the new variable. + + OUTPUT: + + ``self`` with variable name changed to ``var``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: R. = SkewPolynomialRing(k,Frob); R + Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: Ry = R.change_var('y'); Ry + Skew Polynomial Ring in y over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: Ry is R.change_var('y') + True + """ + if self._derivation is None: + twist = self._morphism + else: + twist = self._derivation + return OrePolynomialRing(self.base_ring(), twist, names=var, + sparse=self.__is_sparse) + + def characteristic(self): + r""" + Return the characteristic of the base ring of ``self``. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: R['x',sigma].characteristic() + 0 + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: k['y',Frob].characteristic() + 5 + """ + return self.base_ring().characteristic() + + + @cached_method + def twisting_morphism(self, n=1): + r""" + Return the twisting endomorphism of the base ring iterated ``n`` times. + + INPUT: + + - ``n`` - an integer (default: 1) + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: S.twisting_morphism() + Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t + 1 + sage: S.twisting_morphism() == sigma + True + sage: S.twisting_morphism(10) + Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t + 10 + + If ``n`` in negative, Sage tries to compute the inverse of the + twist map:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: T. = k['y',Frob] + sage: T.twisting_morphism(-1) + Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 + + Sometimes it fails, even if the twist map is actually invertible:: + + sage: S.twisting_morphism(-1) + Traceback (most recent call last): + ... + NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t + 1 + """ + if self._morphism is not None: + try: + return self._morphism ** n + except TypeError as e: + if n < 0: + raise NotImplementedError("inversion of the twisting morphism %s" % self._map) + else: + raise ValueError("Unexpected error in iterating the twisting morphism: %s", e) + + def twist_map(self, n): + # Deprecation + return self.twisting_morphism(n) + + def twisting_derivation(self): + return self._derivation + + + @cached_method + def gen(self, n=0): + r""" + Return the indeterminate generator of this skew polynomial ring. + + INPUT: + + - ``n`` -- index of generator to return (default: 0). Exists for + compatibility with other polynomial rings. + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma]; S + Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + sage: y = S.gen(); y + x + sage: y == x + True + sage: y is x + True + sage: S.gen(0) + x + + This is also known as the parameter:: + + sage: S.parameter() is S.gen() + True + """ + if n != 0: + raise IndexError("generator %s not defined" % n) + return self.Element(self, [0,1]) + + parameter = gen + + def gens_dict(self): + r""" + Return a {name: variable} dictionary of the generators of ``self``. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R,sigma) + sage: S.gens_dict() + {'x': x} + """ + return dict(zip(self.variable_names(), self.gens())) + + def is_finite(self): + r""" + Return ``False`` since skew polynomial rings are not finite + (unless the base ring is `0`.) + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: k.is_finite() + True + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: S.is_finite() + False + """ + R = self.base_ring() + return R.is_finite() and R.order() == 1 + + def is_exact(self): + r""" + Return ``True`` if elements of this skew polynomial ring are exact. + This happens if and only if elements of the base ring are exact. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: S.is_exact() + True + sage: S.base_ring().is_exact() + True + + sage: R. = k[[]] + sage: sigma = R.hom([u+u^2]) + sage: T. = R['y',sigma] + sage: T.is_exact() + False + sage: T.base_ring().is_exact() + False + """ + return self.base_ring().is_exact() + + def is_sparse(self): + r""" + Return ``True`` if the elements of this polynomial ring are sparsely + represented. + + .. WARNING:: + + Since sparse skew polynomials are not yet implemented, this + function always returns ``False``. + + EXAMPLES:: + + sage: R. = RR[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: S.is_sparse() + False + """ + return self.__is_sparse + + def ngens(self): + r""" + Return the number of generators of this skew polynomial ring, + which is 1. + + EXAMPLES:: + + sage: R. = RR[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x',sigma] + sage: S.ngens() + 1 + """ + return 1 + + def random_element(self, degree=2, monic=False, *args, **kwds): + r""" + Return a random skew polynomial in ``self``. + + INPUT: + + - ``degree`` -- (default: 2) integer with degree + or a tuple of integers with minimum and maximum degrees + + - ``monic`` -- (default: ``False``) if ``True``, return a monic + skew polynomial + + - ``*args, **kwds`` -- passed on to the ``random_element`` method + for the base ring + + OUTPUT: + + Skew polynomial such that the coefficients of `x^i`, for `i` up + to ``degree``, are random elements from the base ring, randomized + subject to the arguments ``*args`` and ``**kwds``. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x', Frob] + sage: S.random_element() # random + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 + sage: S.random_element(monic=True) # random + x^2 + (2*t^2 + t + 1)*x + 3*t^2 + 3*t + 2 + + Use ``degree`` to obtain polynomials of higher degree + + sage: p = S.random_element(degree=5) # random + (t^2 + 3*t)*x^4 + (4*t + 4)*x^3 + (4*t^2 + 4*t)*x^2 + (2*t^2 + 1)*x + 3 + + When ``monic`` is ``False``, the returned skew polynomial may have + a degree less than ``degree`` (it happens when the random leading + coefficient is zero). However, if ``monic`` is ``True``, this can't + happen:: + + sage: p = S.random_element(degree=4, monic=True) + sage: p.leading_coefficient() == S.base_ring().one() + True + sage: p.degree() == 4 + True + + If a tuple of two integers is given for the degree argument, a random + integer will be chosen between the first and second element of the + tuple as the degree, both inclusive:: + + sage: S.random_element(degree=(2,7)) # random + (3*t^2 + 1)*x^4 + (4*t + 2)*x^3 + (4*t + 1)*x^2 + + (t^2 + 3*t + 3)*x + 3*t^2 + 2*t + 2 + + If the first tuple element is greater than the second, a a + ``ValueError`` is raised:: + + sage: S.random_element(degree=(5,4)) + Traceback (most recent call last): + ... + ValueError: first degree argument must be less or equal to the second + """ + R = self.base_ring() + if isinstance(degree, (list, tuple)): + if len(degree) != 2: + raise ValueError("degree argument must be an integer or a tuple of 2 integers (min_degree, max_degree)") + if degree[0] > degree[1]: + raise ValueError("first degree argument must be less or equal to the second") + degree = randint(*degree) + if monic: + return self([R.random_element(*args, **kwds) for _ in range(degree)] + [R.one()]) + else: + return self([R.random_element(*args, **kwds) for _ in range(degree+1)]) + + def random_irreducible(self, degree=2, monic=True, *args, **kwds): + r""" + Return a random irreducible skew polynomial. + + .. WARNING:: + + Elements of this skew polynomial ring need to have a method + is_irreducible(). Currently, this method is implemented only + when the base ring is a finite field. + + INPUT: + + - ``degree`` - Integer with degree (default: 2) + or a tuple of integers with minimum and maximum degrees + + - ``monic`` - if True, returns a monic skew polynomial + (default: True) + + - ``*args, **kwds`` - Passed on to the ``random_element`` method for + the base ring + + OUTPUT: + + - A random skew polynomial + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: A = S.random_irreducible(); A + x^2 + (4*t^2 + 3*t + 4)*x + 4*t^2 + t + sage: A.is_irreducible() + True + sage: B = S.random_irreducible(degree=3,monic=False); B # random + (4*t + 1)*x^3 + (t^2 + 3*t + 3)*x^2 + (3*t^2 + 2*t + 2)*x + 3*t^2 + 3*t + 1 + sage: B.is_irreducible() + True + """ + if isinstance(degree, (list, tuple)): + if len(degree) != 2: + raise ValueError("degree argument must be an integer or a tuple of 2 integers (min_degree, max_degree)") + if degree[0] > degree[1]: + raise ValueError("minimum degree must be less or equal than maximum degree") + degree = randint(*degree) + while True: + irred = self.random_element((degree,degree), monic=monic) + if irred.is_irreducible(): + return irred + + def is_commutative(self): + r""" + Return ``True`` if this skew polynomial ring is commutative, i.e. if the + twist map is the identity. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: S.is_commutative() + False + + sage: T. = k['y',Frob^3] + sage: T.is_commutative() + True + """ + return self.twisting_morphism().is_identity() diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pxd b/src/sage/rings/polynomial/skew_polynomial_element.pxd index 1591df63702..b7222e38117 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_element.pxd @@ -1,51 +1,8 @@ -from sage.structure.element cimport AlgebraElement -from sage.structure.parent cimport Parent -from sage.rings.morphism cimport Morphism -from sage.structure.element cimport RingElement -from sage.rings.polynomial.polynomial_element cimport Polynomial_generic_dense +from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial_generic_dense -cdef class SkewPolynomial(AlgebraElement): - cdef _is_gen - - cdef long _hash_c(self) - cdef SkewPolynomial _new_c(self, list coeffs, Parent P, char check=*) - cpdef SkewPolynomial _new_constant_poly(self, RingElement a, Parent P, char check=*) - cpdef _neg_(self) - cpdef _floordiv_(self, right) - cpdef _mod_(self, right) - - cpdef bint is_zero(self) - cpdef bint is_one(self) - - cpdef operator_eval(self, eval_pt) - - cdef _left_quo_rem(self, SkewPolynomial other) - cdef _right_quo_rem(self, SkewPolynomial other) - - cdef SkewPolynomial _left_lcm_cofactor(self, SkewPolynomial other) - cdef SkewPolynomial _right_lcm_cofactor(self, SkewPolynomial other) - - # Abstract methods +cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): cdef void _inplace_rmul(self, SkewPolynomial_generic_dense right) cdef void _inplace_pow(self, Py_ssize_t n) - cpdef int degree(self) - cpdef list coefficients(self, sparse=*) - - -cdef class SkewPolynomial_generic_dense(SkewPolynomial): - cdef list _coeffs - - cdef void __normalize(self) - cpdef _add_(self, other) - cpdef _mul_(self, other) - - cpdef dict dict(self) - cpdef list list(self, bint copy=*) - cpdef right_power_mod(self, exp, modulus) cpdef left_power_mod(self, exp, modulus) - - -cdef class SkewPolynomialBaseringInjection(Morphism): - cdef RingElement _an_element - cdef object _new_constant_poly_ + cpdef operator_eval(self, eval_pt) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index db64f3ce84f..3b80e8e6aff 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -78,287 +78,193 @@ from sage.rings.polynomial.polynomial_element cimport _dict_to_list from sage.structure.element import coerce_binop from sage.misc.superseded import experimental +from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial +from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial_generic_dense -cdef class SkewPolynomial(AlgebraElement): - r""" - Abstract base class for skew polynomials. - - This class must be inherited from and have key methods overridden. - .. RUBRIC:: Definition - - Let `R` be a commutative ring equipped with an automorphism `\sigma`. +cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): + r""" + Generic implementation of dense skew polynomial supporting any valid base + ring and twist map. + """ + cpdef left_power_mod(self, exp, modulus): + r""" + Return the remainder of ``self**exp`` in the left euclidean division + by ``modulus``. - Then, a skew polynomial is given by the equation: + INPUT: - .. MATH:: + - ``exp`` -- an Integer - F(X) = a_{n} X^{n} + \cdots + a_0, + - ``modulus`` -- a skew polynomial in the same ring as ``self`` - where the coefficients `a_i \in R` and `X` is a formal variable. + OUTPUT: - Addition between two skew polynomials is defined by the usual addition - operation and the modified multiplication is defined by the rule - `X a = \sigma(a) X` for all `a` in `R`. Skew polynomials are thus - non-commutative and the degree of a product is equal to the sum of the - degrees of the factors. + Remainder of ``self**exp`` in the left euclidean division + by ``modulus``. - Let `a` and `b` be two skew polynomials in the same ring `S`. - The *left (resp. right) euclidean division* of `a` by `b` is a couple - `(q,r)` of elements in `S` such that + REMARK: - - `a = q b + r` (resp. `a = b q + r`) + The quotient of the underlying skew polynomial ring by the + principal ideal generated by ``modulus`` is in general *not* + a ring. - - the degree of `r` is less than the degree of `b` + As a consequence, Sage first computes exactly ``self**exp`` + and then reduce it modulo ``modulus``. - `q` (resp. `r`) is called the *quotient* (resp. the remainder) - of this euclidean division. + EXAMPLES:: - .. RUBRIC:: Properties + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x + t + sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 + sage: a.left_power_mod(100,modulus) + (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t + """ + cdef OrePolynomial_generic_dense r + if not isinstance(exp, Integer): + try: + exp = Integer(exp) + except TypeError: + raise TypeError("non-integral exponents not supported") - Keeping the previous notation, if the leading coefficient of `b` - is a unit (e.g. if `b` is monic) then the quotient and the remainder - in the *right* euclidean division exist and are unique. + if len(self._coeffs) <= 1: + return self.parent()(self._coeffs[0]**exp) + if exp == 0: + return self.parent().one() + if exp < 0: + return (~self).left_power_mod(-exp, modulus) - The same result holds for the *left* euclidean division if in addition - the twist map defining the skew polynomial ring is invertible. + if self == self.parent().gen(): + P = self.parent() + R = P.base_ring() + v = [R.zero()]*exp + [R.one()] + r = self._parent(v) + else: + r = self._new_c(list(self._coeffs), self._parent) + r._inplace_pow(exp) - .. RUBRIC:: Evaluation + if modulus: + _, r = r._left_quo_rem(modulus) + return r - The value of a given a skew polynomial `p(x) = \sum_{i=0}^d a_i x^i` - at `r` is calculated using the formula: + cpdef right_power_mod(self, exp, modulus): + r""" + Return the remainder of ``self**exp`` in the right euclidean division + by ``modulus``. - .. MATH:: + INPUT: - p(r) = \sum_{i=0}^d a_i \sigma^i(r) + - ``exp`` -- an Integer - where `\sigma` is the base ring automorphism. This is called - the *operator evaluation* method. + - ``modulus`` -- a skew polynomial in the same ring as ``self`` - EXAMPLES: + OUTPUT: - We illustrate some functionalities implemented in this class. + Remainder of ``self**exp`` in the right euclidean division + by ``modulus``. - We create the skew polynomial ring:: + REMARK: - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma]; S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - - and some elements in it:: - - sage: a = t + x + 1; a - x + t + 1 - sage: b = S([t^2,t+1,1]); b - x^2 + (t + 1)*x + t^2 - sage: c = S.random_element(degree=3,monic=True); c - x^3 + (2*t - 1)*x - - Ring operations are supported:: - - sage: a + b - x^2 + (t + 2)*x + t^2 + t + 1 - sage: a - b - -x^2 - t*x - t^2 + t + 1 - - sage: a * b - x^3 + (2*t + 3)*x^2 + (2*t^2 + 4*t + 2)*x + t^3 + t^2 - sage: b * a - x^3 + (2*t + 4)*x^2 + (2*t^2 + 3*t + 2)*x + t^3 + t^2 - sage: a * b == b * a - False - - sage: b^2 - x^4 + (2*t + 4)*x^3 + (3*t^2 + 7*t + 6)*x^2 - + (2*t^3 + 4*t^2 + 3*t + 1)*x + t^4 - sage: b^2 == b*b - True - - Sage also implements arithmetic over skew polynomial rings. You will find - below a short panorama:: - - sage: q,r = c.right_quo_rem(b) - sage: q - x - t - 2 - sage: r - 3*t*x + t^3 + 2*t^2 - sage: c == q*b + r - True - - The operators ``//`` and ``%`` give respectively the quotient - and the remainder of the *right* euclidean division:: - - sage: q == c // b - True - sage: r == c % b - True - - Left euclidean division won't work over our current `S` because Sage can't - invert the twist map:: - - sage: q,r = c.left_quo_rem(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 - - Here we can see the effect of the operator evaluation compared to the usual - polynomial evaluation:: - - sage: a = x^2 - sage: a(t) - t + 2 - - Here is a working example over a finite field:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^4 + (4*t + 1)*x^3 + (t^2 + 3*t + 3)*x^2 + (3*t^2 + 2*t + 2)*x + (3*t^2 + 3*t + 1) - sage: b = (2*t^2 + 3)*x^2 + (3*t^2 + 1)*x + 4*t + 2 - sage: q,r = a.left_quo_rem(b) - sage: q - (4*t^2 + t + 1)*x^2 + (2*t^2 + 2*t + 2)*x + 2*t^2 + 4*t + 3 - sage: r - (t + 2)*x + 3*t^2 + 2*t + 4 - sage: a == b*q + r - True - - Once we have euclidean divisions, we have for free gcd and lcm - (at least if the base ring is a field):: - - sage: a = (x + t) * (x + t^2)^2 - sage: b = (x + t) * (t*x + t + 1) * (x + t^2) - sage: a.right_gcd(b) - x + t^2 - sage: a.left_gcd(b) - x + t - - The left lcm has the following meaning: given skew polynomials `a` and `b`, - their left lcm is the least degree polynomial `c = ua = vb` for some skew - polynomials `u, v`. Such a `c` always exist if the base ring is a field:: - - sage: c = a.left_lcm(b); c - x^5 + (4*t^2 + t + 3)*x^4 + (3*t^2 + 4*t)*x^3 + 2*t^2*x^2 + (2*t^2 + t)*x + 4*t^2 + 4 - sage: c.is_right_divisible_by(a) - True - sage: c.is_right_divisible_by(b) - True - - The right lcm is defined similarly as the least degree polynomial `c = au = - bv` for some `u,v`:: - - sage: d = a.right_lcm(b); d - x^5 + (t^2 + 1)*x^4 + (3*t^2 + 3*t + 3)*x^3 + (3*t^2 + t + 2)*x^2 + (4*t^2 + 3*t)*x + 4*t + 4 - sage: d.is_left_divisible_by(a) - True - sage: d.is_left_divisible_by(b) - True - - .. SEEALSO:: - - - :mod:`sage.rings.polynomial.skew_polynomial_ring` - - :mod:`sage.rings.polynomial.skew_polynomial_ring_constructor` - """ - def __init__(self, parent, construct=False): - r""" - Initialize ``self``. + The quotient of the underlying skew polynomial ring by the + principal ideal generated by ``modulus`` is in general *not* + a ring. - INPUT: + As a consequence, Sage first computes exactly ``self**exp`` + and then reduce it modulo ``modulus``. - - ``parent`` -- parent of ``self`` + EXAMPLES:: - - ``construct`` -- boolean (default: ``False``) + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x + t + sage: b = a^10 # short form for ``a._pow_(10)`` + sage: b == a*a*a*a*a*a*a*a*a*a + True + sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 + sage: br = a.right_power_mod(10,modulus); br + (t^2 + t)*x^2 + (3*t^2 + 1)*x + t^2 + t + sage: rq, rr = b.right_quo_rem(modulus) + sage: br == rr + True + sage: a.right_power_mod(100,modulus) + (2*t^2 + 3)*x^2 + (t^2 + 4*t + 2)*x + t^2 + 2*t + 1 + """ + cdef OrePolynomial_generic_dense r + if not isinstance(exp, Integer): + try: + exp = Integer(exp) + except TypeError: + raise TypeError("non-integral exponents not supported") - TESTS:: + if len(self._coeffs) <= 1: + return self.parent()(self._coeffs[0]**exp) + if exp == 0: + return self.parent().one() + if exp < 0: + return (~self).right_power_mod(-exp, modulus) - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: P = x + t - sage: TestSuite(P).run() - sage: Q = S([1, t, t+2]) - sage: TestSuite(Q).run() - """ - AlgebraElement.__init__(self, parent) + if self == self.parent().gen(): + P = self.parent() + R = P.base_ring() + v = [R.zero()]*exp + [R.one()] + r = self._parent(v) + else: + r = self._new_c(list(self._coeffs), self._parent) + r._inplace_pow(exp) - cdef long _hash_c(self): - raise NotImplementedError + if modulus: + _, r = r._right_quo_rem(modulus) + return r - def __hash__(self): + def __pow__(self, exp, modulus): r""" - Return hash of ``self``. - - EXAMPLES:: + Return the remainder of ``self**exp`` in the left euclidean + division by ``modulus``. - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: hash(a) == hash(a) - True - """ - return self._hash_c() + INPUT: - cdef void _inplace_rmul(self, SkewPolynomial_generic_dense right): - raise NotImplementedError - cdef void _inplace_pow(self, Py_ssize_t n): - raise NotImplementedError + - ``exp`` -- an Integer - cpdef int degree(self): - r""" - Return the degree of ``self``. + - ``modulus`` -- a skew polynomial in the same ring as ``self`` - By convention, the zero skew polynomial has degree `-1`. + OUTPUT: - EXAMPLES:: + Remainder of ``self**exp`` in the right euclidean division + by ``modulus``. - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2 + t*x^3 + t^2*x + 1 - sage: a.degree() - 3 - sage: S.zero().degree() - -1 - sage: S(5).degree() - 0 - """ + REMARK: - cdef SkewPolynomial _new_c(self, list coeffs, Parent P, char check=0): - r""" - Fast creation of a new skew polynomial + The quotient of the underlying skew polynomial ring by the + principal ideal generated by ``modulus`` is in general *not* + a ring. - .. NOTE:: + As a consequence, Sage first computes exactly ``self**exp`` + and then reduce it modulo ``modulus``. - Override this function in classes which inherit - from SkewPolynomial. - """ - l = P(coeffs) - return l + .. SEEALSO:: - cpdef SkewPolynomial _new_constant_poly(self, RingElement a, Parent P, char check=0): - r""" - Fast creation of a new constant skew polynomial + :meth:`~sage.rings.polynomial.skew_polynomial_element._pow_` EXAMPLES:: - sage: from sage.rings.polynomial.skew_polynomial_element import SkewPolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] - sage: SkewPolynomialBaseringInjection(k, k['x', Frob]) #indirect doctest - Skew Polynomial base injection morphism: - From: Finite Field in t of size 5^3 - To: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: a = x + t + sage: b = a^10 + sage: b == a*a*a*a*a*a*a*a*a*a + True + sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 + sage: bmod = a.right_power_mod(10,modulus); bmod + (t^2 + t)*x^2 + (3*t^2 + 1)*x + t^2 + t + sage: rq, rr = b.right_quo_rem(modulus) + sage: bmod == rr + True """ - if a: - n = self._new_c([a],P,check) - else: - n = self._new_c([],P) - return n + return self.right_power_mod(exp, modulus) def __call__(self, eval_pt): r""" @@ -460,7 +366,7 @@ cdef class SkewPolynomial(AlgebraElement): ... TypeError: 1/t fails to convert into the map's domain Univariate Polynomial Ring in t over Rational Field, but a `pushforward` method is not properly implemented """ - cdef RingHomomorphism sigma = self._parent.twist_map() + cdef RingHomomorphism sigma = self._parent.twist_morphism() cdef list coefficients = self.list() cdef RingElement ret = self.base_ring().zero() cdef RingElement a = eval_pt @@ -469,44 +375,6 @@ cdef class SkewPolynomial(AlgebraElement): a = sigma(a) return ret - def __setitem__(self, n, value): - r""" - Set the ``n``-th coefficient of ``self``. - - This always raises an ``IndexError``, since polynomials are immutable in - Sage. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: a[1] = t + 1 - Traceback (most recent call last): - ... - IndexError: skew polynomials are immutable - """ - raise IndexError("skew polynomials are immutable") - - def square(self): - r""" - Return the square of ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + t; a - x + t - sage: a.square() - x^2 + (2*t + 1)*x + t^2 - sage: a.square() == a*a - True - """ - return self * self - def conjugate(self, n): r""" Return ``self`` conjugated by `x^n`, where `x` is the @@ -551,2912 +419,265 @@ cdef class SkewPolynomial(AlgebraElement): sage: u*y == y*v True """ - r = self._new_c([self._parent.twist_map(n)(x) for x in self.list()], + r = self._new_c([self._parent.twist_morphism(n)(x) for x in self.list()], self._parent, 0) return r - def constant_coefficient(self): - r""" - Return the constant coefficient (i.e. the coefficient of term - of degree `0`) of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + t^2 + 2 - sage: a.constant_coefficient() - t^2 + 2 + def multi_point_evaluation(self, eval_pts): """ - if not self: - return self.base_ring().zero() - else: - return self[0] - - def leading_coefficient(self): - r""" - Return the coefficient of the highest-degree monomial of ``self``. + Evaluate ``self`` at list of evaluation points. - EXAMPLES:: + INPUT: - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (t+1)*x^5 + t^2*x^3 + x - sage: a.leading_coefficient() - t + 1 - """ - cdef int d = self.degree() - if d == -1: - raise ValueError("the skew polynomial must not be 0") - return self[d] + - ``eval_pts`` -- list of points at which ``self`` is to be evaluated - def is_unit(self): - r""" - Return ``True`` if this skew polynomial is a unit. + OUTPUT: - When the base ring `R` is an integral domain, then a skew polynomial `f` - is a unit if and only if degree of `f` is `0` and `f` is then a unit in - `R`. + List of values of ``self`` at the ``eval_pts``. - .. NOTE:: + .. TODO:: - The case when `R` is not an integral domain is not yet implemented. + This method currently trivially calls the evaluation function + repeatedly. If fast skew polynomial multiplication is available, an + asymptotically faster method is possible using standard divide and + conquer techniques and + :meth:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general.minimal_vanishing_polynomial`. EXAMPLES:: - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + (t+1)*x^5 + t^2*x^3 - x^5 - sage: a.is_unit() - False + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x + t + sage: eval_pts = [1, t, t^2] + sage: c = a.multi_point_evaluation(eval_pts); c + [t + 1, 3*t^2 + 4*t + 4, 4*t] + sage: c == [ a(e) for e in eval_pts ] + True """ - # TODO: Sage does not yet have support for finding order of - # automorphisms. Once that is available, general case can - # be implemented. Reference: http://bit.ly/29Vidu7 - if self._parent.base_ring().is_integral_domain(): - if self.degree() == 0 and self[0].is_unit(): - return True - else: - return False - else: - raise NotImplementedError("is_unit is not implemented for skew polynomial rings " - "over base rings which are not integral domains.") + return [ self(e) for e in eval_pts ] - def is_nilpotent(self): + cpdef ModuleElement _lmul_(self, Element right): r""" - Check if ``self`` is nilpotent. - - Given a commutative ring `R` and a base ring automorphism `\sigma` - of order `n`, an element `f` of `R[X, \sigma]` is nilpotent if - and only if all coefficients of `f^n` are nilpotent in `R`. + Multiply ``self`` on the right by scalar. - .. NOTE:: + INPUT: - The paper "Nilpotents and units in skew polynomial rings - over commutative rings" by M. Rimmer and K.R. Pearson describes - the method to check whether a given skew polynomial is nilpotent. - That method however, requires one to know the order of the - automorphism which is not available in Sage. This method is thus - not yet implemented. + - ``right`` -- an element of the base ring EXAMPLES:: - sage: R. = ZZ[] + sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] - sage: x.is_nilpotent() - Traceback (most recent call last): - ... - NotImplementedError + sage: a = x + t + sage: b = t + sage: a * b + (t + 1)*x + t^2 + sage: a * b == b * a + False """ - raise NotImplementedError + if right == 0: + return self._parent.zero() + cdef list x = (self)._coeffs + cdef Py_ssize_t i + twist_morphism = self._parent._map + r = self._new_c([ (twist_morphism**i)(right)*x[i] for i from 0 <= i < len(x) ], + self._parent, 0) + return r - def is_monic(self): + cpdef ModuleElement _rmul_(self, Element left): r""" - Return ``True`` if this skew polynomial is monic. + Multiply ``self`` on the left by scalar. + + INPUT: - The zero polynomial is by definition not monic. + - ``left`` -- an element of the base ring EXAMPLES:: - sage: R. = ZZ[] + sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] - sage: a = x + t - sage: a.is_monic() - True - sage: a = 0*x - sage: a.is_monic() + sage: a = t + sage: b = x + t + sage: a * b + t*x + t^2 + sage: a * b == b * a False - sage: a = t*x^3 + x^4 + (t+1)*x^2 - sage: a.is_monic() - True - sage: a = (t^2 + 2*t)*x^2 + x^3 + t^10*x^5 - sage: a.is_monic() - False - """ - return not self.is_zero() and self[self.degree()] == 1 - - def left_monic(self): - r""" - Return the unique monic skew polynomial `m` which divides ``self`` on - the left and has the same degree. - - Given a skew polynomial `p` of degree `n`, its left monic is given by - `m = p \sigma^{-n}(1/k)`, where `k` is the leading coefficient of - `p`, i.e. by the appropriate scalar multiplication on the right. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (3*t^2 + 3*t + 2)*x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 - sage: b = a.left_monic(); b - x^3 + (4*t^2 + 3*t)*x^2 + (4*t + 2)*x + 2*t^2 + 4*t + 3 - - Check list:: - - sage: b.degree() == a.degree() - True - sage: b.is_left_divisible_by(a) - True - sage: twist = S.twist_map(-a.degree()) - sage: a == b * twist(a.leading_coefficient()) - True - - Note that `b` does not divide `a` on the right:: - - sage: a.is_right_divisible_by(b) - False - - This function does not work if the leading coefficient is not a - unit:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t*x - sage: a.left_monic() - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient is not a unit - """ - try: - a = self.base_ring()(~self.leading_coefficient()) - except (ZeroDivisionError, TypeError): - raise NotImplementedError("the leading coefficient is not a unit") - r = self * self._parent.twist_map(-self.degree())(a) - return r - - def right_monic(self): - r""" - Return the unique monic skew polynomial `m` which divides ``self`` on - the right and has the same degree. - - Given a skew polynomial `p` of degree `n`, its left monic is given by - `m = (1/k) * p`, where `k` is the leading coefficient of `p`, i.e. by - the appropriate scalar multiplication on the left. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (3*t^2 + 3*t + 2)*x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 - sage: b = a.right_monic(); b - x^3 + (2*t^2 + 3*t + 4)*x^2 + (3*t^2 + 4*t + 1)*x + 2*t^2 + 4*t + 3 - - Check list:: - - sage: b.degree() == a.degree() - True - sage: b.is_right_divisible_by(a) - True - sage: a == a.leading_coefficient() * b - True - - Note that `b` does not divide `a` on the right:: - - sage: a.is_left_divisible_by(b) - False - - This function does not work if the leading coefficient is not a - unit:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t*x - sage: a.right_monic() - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient is not a unit - """ - try: - a = self.base_ring()(~self.leading_coefficient()) - except (ZeroDivisionError, TypeError): - raise NotImplementedError("the leading coefficient is not a unit") - r = a * self - return r - - cpdef _mod_(self, other): - r""" - Return the remainder in the *right* euclidean division of - ``self`` by ``other```. - - TESTS:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: b = x^2 + 2*t*x + 2 - sage: a = (x+t)*b + t*x + 1 - sage: a % b - t*x + 1 - - sage: (a*t).right_quo_rem(b*t) - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient of the divisor is not invertible - """ - _,r = self.right_quo_rem(other) - return r - - cpdef _floordiv_(self, right): - r""" - Return the quotient of the *right* euclidean division of - ``self`` by ``right``. - - The algorithm fails if the leading coefficient of the divisor - (``right``) is not invertible. - - TESTS:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: b = x^2 + t - sage: a = (x^2 + t*x + 1)*b + t^3*x - sage: a // b - x^2 + t*x + 1 - - sage: (t*a) // (t*b) - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient of the divisor is not invertible - - """ - q,_ = self.right_quo_rem(right) - return q - - cpdef _div_(self, right): - r""" - Not Implemented. - - To implement this, localization of Ore rings is needed, see - :trac:`13215`. - - Use the operator `//` even for exact division. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^5 + (t + 2)*x^2 + t^2 - sage: b = x^3 + 4*t - sage: c = a*b - - sage: c / b - Traceback (most recent call last): - ... - NotImplementedError: localization of Ore rings not yet implemented - - sage: c // b == a - True - """ - # Should this actually return something in the fraction field like - # we do elsewhere in Sage? - TCS - raise NotImplementedError("localization of Ore rings not yet implemented") - - def is_left_divisible_by(self, other): - r""" - Check if ``self`` is divisible by ``other`` on the left. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Return ``True`` or ``False``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^2 + t*x + t^2 + 3 - sage: b = x^3 + (t + 1)*x^2 + 1 - sage: c = a*b - sage: c.is_left_divisible_by(a) - True - sage: c.is_left_divisible_by(b) - False - - Divisibility by `0` does not make sense:: - - sage: c.is_left_divisible_by(S(0)) - Traceback (most recent call last): - ... - ZeroDivisionError: division by zero is not valid - """ - _, r = self.left_quo_rem(other) - return r.is_zero() - - def is_right_divisible_by(self, other): - r""" - Check if ``self`` is divisible by ``other`` on the right. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Return ``True`` or ``False``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^2 + t*x + t^2 + 3 - sage: b = x^3 + (t + 1)*x^2 + 1 - sage: c = a*b - sage: c.is_right_divisible_by(a) - False - sage: c.is_right_divisible_by(b) - True - - Divisibility by `0` does not make sense:: - - sage: c.is_right_divisible_by(S(0)) - Traceback (most recent call last): - ... - ZeroDivisionError: division by zero is not valid - - This function does not work if the leading coefficient of the divisor - is not a unit:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2 + 2*x + t - sage: b = (t+1)*x + t^2 - sage: c = a*b - sage: c.is_right_divisible_by(b) - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient of the divisor is not invertible - """ - _, r = self.right_quo_rem(other) - return r.is_zero() - - def left_divides(self, other): - r""" - Check if ``self`` divides ``other`` on the left. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Return ``True`` or ``False``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^2 + t*x + t^2 + 3 - sage: b = x^3 + (t + 1)*x^2 + 1 - sage: c = a*b - sage: a.left_divides(c) - True - sage: b.left_divides(c) - False - - Divisibility by `0` does not make sense:: - - sage: S(0).left_divides(c) - Traceback (most recent call last): - ... - ZeroDivisionError: division by zero is not valid - """ - _, r = other.left_quo_rem(self) - return r.is_zero() - - def right_divides(self, other): - r""" - Check if ``self`` divides ``other`` on the right. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Return ``True`` or ``False``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x^2 + t*x + t^2 + 3 - sage: b = x^3 + (t + 1)*x^2 + 1 - sage: c = a*b - sage: a.right_divides(c) - False - sage: b.right_divides(c) - True - - Divisibility by `0` does not make sense:: - - sage: S(0).right_divides(c) - Traceback (most recent call last): - ... - ZeroDivisionError: division by zero is not valid - - This function does not work if the leading coefficient of the divisor - is not a unit:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2 + 2*x + t - sage: b = (t+1)*x + t^2 - sage: c = a*b - sage: b.right_divides(c) - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient of the divisor is not invertible - """ - _, r = other.right_quo_rem(self) - return r.is_zero() - - @coerce_binop - def left_xgcd(self, other, monic=True): - r""" - Return the left gcd of ``self`` and ``other`` along with the - coefficients for the linear combination. - - If `a` is ``self`` and `b` is ``other``, then there are skew polynomials - `u` and `v` such that `g = a u + b v`, where `g` is the left gcd of `a` - and `b`. This method returns `(g, u, v)`. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- boolean (default: ``True``). Return whether the left gcd - should be normalized to be monic. - - OUTPUT: - - - The left gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is - divisible on the left by `g` iff it is divisible on the left - by both ``self`` and ``other``. - If monic is ``True``, `g` is in addition monic. (With this - extra condition, it is uniquely determined.) - - - Two skew polynomials `u` and `v` such that: - - .. MATH:: - - g = a * u + b * v, - - where `s` is ``self`` and `b` is ``other``. - - .. NOTE:: - - Works only if following two conditions are fulfilled - (otherwise left gcd do not exist in general): - 1) the base ring is a field and 2) the twist map on - this field is bijective. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (x + t) * (x^2 + t*x + 1) - sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) - sage: g,u,v = a.left_xgcd(b); g - x + t - sage: a*u + b*v == g - True - - Specifying ``monic=False``, we *can* get a nonmonic gcd:: - - sage: g,u,v = a.left_xgcd(b, monic=False); g - 2*t*x + 4*t + 2 - sage: a*u + b*v == g - True - - The base ring must be a field:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (x + t) * (x^2 + t*x + 1) - sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) - sage: a.left_xgcd(b) - Traceback (most recent call last): - ... - TypeError: the base ring must be a field - - And the twist map must be bijective:: - - sage: FR = R.fraction_field() - sage: f = FR.hom([FR(t)^2]) - sage: S. = FR['x',f] - sage: a = (x + t) * (x^2 + t*x + 1) - sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) - sage: a.left_xgcd(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t^2 - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - cdef SkewPolynomial_generic_dense G = self - cdef SkewPolynomial_generic_dense U = self._parent.one() - cdef SkewPolynomial_generic_dense V, V1, V2, Q, R, T - if not other: - V = self._parent.zero() - else: - V1 = self._parent.zero() - V3 = other - while V3: - Q,R = G._left_quo_rem(V3) - T = U - V1*Q - U = V1 - G = V3 - V1 = T - V3 = R - V = G - self*U - V, _ = V._left_quo_rem(other) - if monic: - lc = ~G.leading_coefficient() - lc = self._parent.twist_map(-G.degree())(lc) - G = G * lc - U = U * lc - V = V * lc - return G,U,V - - cdef _left_quo_rem(self, SkewPolynomial other): - r""" - Return the quotient and remainder of the left euclidean - division of ``self`` by ``other`` (C implementation). - - Must be implemented in subclasses. - """ - raise NotImplementedError("left Euclidean division is not implemented") - - @coerce_binop - def left_quo_rem(self, other): - r""" - Return the quotient and remainder of the left euclidean - division of ``self`` by ``other``. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - - the quotient and the remainder of the left euclidean - division of this skew polynomial by ``other`` - - .. NOTE:: - - This will fail if the leading coefficient of ``other`` is not a unit - or if Sage can't invert the twist map. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (3*t^2 + 3*t + 2)*x^3 + (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 - sage: b = (3*t^2 + 4*t + 2)*x^2 + (2*t^2 + 4*t + 3)*x + 2*t^2 + t + 1 - sage: q,r = a.left_quo_rem(b) - sage: a == b*q + r - True - - In the following example, Sage does not know the inverse - of the twist map:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (-2*t^2 - t + 1)*x^3 + (-t^2 + t)*x^2 + (-12*t - 2)*x - t^2 - 95*t + 1 - sage: b = x^2 + (5*t - 6)*x - 4*t^2 + 4*t - 1 - sage: a.left_quo_rem(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 - """ - if not other: - raise ZeroDivisionError("division by zero is not valid") - return self._left_quo_rem(other) - - cdef _right_quo_rem(self, SkewPolynomial other): - r""" - Return the quotient and remainder of the right euclidean - division of ``self`` by ``other`` (C implementation). - - Must be implemented in subclasses. - """ - raise NotImplementedError("right Euclidean division is not implemented") - - @coerce_binop - def right_quo_rem(self, other): - r""" - Return the quotient and remainder of the right euclidean - division of ``self`` by ``other``. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - - the quotient and the remainder of the left euclidean - division of this skew polynomial by ``other`` - - .. NOTE:: - - This will fail if the leading coefficient of the divisor - is not a unit. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = S.random_element(degree=4); a - (-t - 95)*x^4 + x^3 + (2*t - 1)*x - sage: b = S.random_element(monic=True); b - x^2 + (-12*t - 2)*x - sage: q,r = a.right_quo_rem(b) - sage: a == q*b + r - True - - The leading coefficient of the divisor need to be invertible:: - - sage: c = S.random_element(); c - (t - 1)*x^2 + t^2*x - sage: a.right_quo_rem(c) - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient of the divisor is not invertible - """ - if not other: - raise ZeroDivisionError("division by zero is not valid") - return self._right_quo_rem(other) - - @coerce_binop - def right_xgcd(self, other, monic=True): - r""" - Return the right gcd of ``self`` and ``other`` along with the - coefficients for the linear combination. - - If `a` is ``self`` and `b` is ``other``, then there are skew polynomials - `u` and `v` such that `g = u a + v b`, where `g` is the right gcd of `a` - and `b`. This method returns `(g, u, v)`. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- boolean (default: ``True``). Return whether the right gcd - should be normalized to be monic. - - OUTPUT: - - - The right gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is - divisible on the right by `g` iff it is divisible on the right - by both ``self`` and ``other``. - If monic is ``True``, `g` is in addition monic. (With this - extra condition, it is uniquely determined.) - - - Two skew polynomials `u` and `v` such that: - - .. MATH:: - - g = u * a + v * b - - where `a` is ``self`` and `b` is ``other``. - - .. NOTE:: - - Works only if the base ring is a field (otherwise right - gcd do not exist in general). - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (x^2 + t*x + 1) * (x + t) - sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) - sage: g,u,v = a.right_xgcd(b); g - x + t - sage: u*a + v*b == g - True - - Specifying ``monic=False``, we *can* get a nonmonic gcd:: - - sage: g,u,v = a.right_xgcd(b,monic=False); g - (4*t^2 + 4*t + 1)*x + 4*t^2 + 4*t + 3 - sage: u*a + v*b == g - True - - The base ring must be a field:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (x^2 + t*x + 1) * (x + t) - sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) - sage: a.right_xgcd(b) - Traceback (most recent call last): - ... - TypeError: the base ring must be a field - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - cdef SkewPolynomial_generic_dense G = self - cdef SkewPolynomial_generic_dense U = self._parent.one() - cdef SkewPolynomial_generic_dense V, V1, V3, Q, R, T - if other.is_zero(): - V = self._parent.zero() - else: - V1 = self._parent.zero() - V3 = other - while not V3.is_zero(): - Q, R = G._right_quo_rem(V3) - T = U - Q*V1 - U = V1 - G = V3 - V1 = T - V3 = R - V = G - U*self - V,_ = V._right_quo_rem(other) - if monic: - lc = ~G.leading_coefficient() - G = lc * G - U = lc * U - V = lc * V - return G,U,V - - @coerce_binop - def right_gcd(self, other, monic=True): - r""" - Return the right gcd of ``self`` and ``other``. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- boolean (default: ``True``). Return whether the right gcd - should be normalized to be monic. - - OUTPUT: - - The right gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is - divisible on the right by `g` iff it is divisible on the right - by both ``self`` and ``other``. - If monic is ``True``, `g` is in addition monic. (With this - extra condition, it is uniquely determined.) - - .. NOTE:: - - Works only if the base ring is a field (otherwise right - gcd do not exist in general). - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (x^2 + t*x + 1) * (x + t) - sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) - sage: a.right_gcd(b) - x + t - - Specifying ``monic=False``, we *can* get a nonmonic gcd:: - - sage: a.right_gcd(b,monic=False) - (4*t^2 + 4*t + 1)*x + 4*t^2 + 4*t + 3 - - The base ring need to be a field:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (x^2 + t*x + 1) * (x + t) - sage: b = 2 * (x^3 + (t+1)*x^2 + t^2) * (x + t) - sage: a.right_gcd(b) - Traceback (most recent call last): - ... - TypeError: the base ring must be a field - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - if other.is_zero(): - return self - cdef SkewPolynomial_generic_dense A = self - cdef SkewPolynomial_generic_dense B = other - while not B.is_zero(): - A, B = B, A % B - if monic: - A = A.right_monic() - return A - - @coerce_binop - def left_gcd(self, other, monic=True): - r""" - Return the left gcd of ``self`` and ``other``. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- boolean (default: ``True``). Return whether the left gcd - should be normalized to be monic. - - OUTPUT: - - The left gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is - divisible on the left by `g` iff it is divisible on the left - by both ``self`` and ``other``. - If monic is ``True``, `g` is in addition monic. (With this - extra condition, it is uniquely determined.) - - .. NOTE:: - - Works only if two following conditions are fulfilled - (otherwise left gcd do not exist in general): - 1) the base ring is a field and 2) the twist map on - this field is bijective. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (x + t) * (x^2 + t*x + 1) - sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) - sage: a.left_gcd(b) - x + t - - Specifying ``monic=False``, we *can* get a nonmonic gcd:: - - sage: a.left_gcd(b,monic=False) - 2*t*x + 4*t + 2 - - The base ring needs to be a field:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (x + t) * (x^2 + t*x + 1) - sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) - sage: a.left_gcd(b) - Traceback (most recent call last): - ... - TypeError: the base ring must be a field - - And the twist map needs to be bijective:: - - sage: FR = R.fraction_field() - sage: f = FR.hom([FR(t)^2]) - sage: S. = FR['x',f] - sage: a = (x + t) * (x^2 + t*x + 1) - sage: b = 2 * (x + t) * (x^3 + (t+1)*x^2 + t^2) - sage: a.left_gcd(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t^2 - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - if other.is_zero(): - return self - cdef SkewPolynomial_generic_dense A_, A = self - cdef SkewPolynomial_generic_dense B = other - while not B.is_zero(): - A_ = A - A = B - _, B = A_._left_quo_rem(B) - if monic: - A = A.left_monic() - return A - - cdef SkewPolynomial _left_lcm_cofactor(self, SkewPolynomial other): - r""" - Return a skew polynomial `U` such that `U P = c L` - where `P` is this skew polynomial (``self``), `L` - is the left lcm of `P` and ``other`` and `c` is a - constant - - TESTS:: - - sage: cython(''' - ....: from sage.rings.polynomial.skew_polynomial_element cimport SkewPolynomial - ....: def left_lcm_cofactor(SkewPolynomial P, SkewPolynomial Q): - ....: return P._left_lcm_cofactor(Q) - ....: ''') - - sage: k. = GF(7^5) - sage: Frob = k.frobenius_endomorphism(3) - sage: S. = k['x', Frob] - - sage: D = S.random_element() - sage: P = S.random_element() * D - sage: Q = S.random_element() * D - sage: L = P.left_lcm(Q) - sage: U = left_lcm_cofactor(P, Q) - sage: (U*P).right_monic() == L - True - """ - cdef SkewPolynomial Q, R, T - cdef SkewPolynomial U = self._parent.one() - cdef SkewPolynomial V = self._parent.zero() - while other: - Q, R = self._right_quo_rem(other) - T = U - Q*V - U = V - V = T - self = other - other = R - return V - - @coerce_binop - def left_xlcm(self, other, monic=True): - r""" - Return the left lcm `L` of ``self`` and ``other`` together - with two skew polynomials `U` and `V` such that - - .. MATH:: - - `U \cdot \text{self} = V \cdot \text{other} = L` - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: P = (x + t^2) * (x + t) - sage: Q = 2 * (x^2 + t + 1) * (x * t) - sage: L, U, V = P.left_xlcm(Q) - sage: L - x^5 + (2*t^2 + t + 4)*x^4 + (3*t^2 + 4)*x^3 + (3*t^2 + 3*t + 2)*x^2 + (t^2 + t + 2)*x - - sage: U*P == L - True - sage: V*Q == L - True - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - if self.is_zero() or other.is_zero(): - raise ZeroDivisionError("division by zero is not valid") - cdef SkewPolynomial_generic_dense V1 = self._left_lcm_cofactor(other) - cdef SkewPolynomial_generic_dense L = V1 * self - if monic: - s = ~(L.leading_coefficient()) - L = s * L - V1 = s * V1 - return L, V1, L // other - - cdef SkewPolynomial _right_lcm_cofactor(self, SkewPolynomial other): - r""" - Return a skew polynomial `U` such that `P U = L c` - where `P` is this skew polynomial (``self``), `L` - is the right lcm of `P` and ``other`` and `c` is a - constant - - TESTS:: - - sage: cython(''' - ....: from sage.rings.polynomial.skew_polynomial_element cimport SkewPolynomial - ....: def right_lcm_cofactor(SkewPolynomial P, SkewPolynomial Q): - ....: return P._right_lcm_cofactor(Q) - ....: ''') - - sage: k. = GF(7^5) - sage: Frob = k.frobenius_endomorphism(3) - sage: S. = k['x', Frob] - - sage: D = S.random_element() - sage: P = D * S.random_element() - sage: Q = D * S.random_element() - sage: L = P.right_lcm(Q) - sage: U = right_lcm_cofactor(P, Q) - sage: (P*U).left_monic() == L - True - """ - cdef SkewPolynomial Q, R, T - cdef SkewPolynomial U = self._parent.one() - cdef SkewPolynomial V = self._parent.zero() - while other: - Q, R = self._left_quo_rem(other) - T = U - V*Q - U = V - V = T - self = other - other = R - return V - - @coerce_binop - def right_xlcm(self, other, monic=True): - r""" - Return the right lcm `L` of ``self`` and ``other`` together - with two skew polynomials `U` and `V` such that - - .. MATH:: - - `\text{self} \cdot U = \text{other} \cdot V = L` - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- a boolean (default: ``True``); whether the right lcm - should be normalized to be monic - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: P = (x + t) * (x + t^2) - sage: Q = 2 * (x + t) * (x^2 + t + 1) - sage: L, U, V = P.right_xlcm(Q) - sage: L - x^4 + (2*t^2 + t + 2)*x^3 + (3*t^2 + 4*t + 1)*x^2 + (3*t^2 + 4*t + 1)*x + t^2 + 4 - sage: P*U == L - True - sage: Q*V == L - True - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - if self.is_zero() or other.is_zero(): - raise ZeroDivisionError("division by zero is not valid") - cdef SkewPolynomial_generic_dense V1 = self._right_lcm_cofactor(other) - cdef SkewPolynomial_generic_dense L = self * V1 - if monic: - s = self.base_ring()(~L.leading_coefficient()) - s = self._parent.twist_map(-L.degree())(s) - L = L * s - V1 = V1 * s - W1, _ = L._left_quo_rem(other) - return L, V1, W1 - - - @coerce_binop - def left_lcm(self, other, monic=True): - r""" - Return the left lcm of ``self`` and ``other``. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- boolean (default: ``True``). Return whether the left lcm - should be normalized to be monic. - - OUTPUT: - - The left lcm of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial divides - `g` on the *right* iff it divides both ``self`` and ``other`` - on the *right*. - If monic is ``True``, `g` is in addition monic. (With this - extra condition, it is uniquely determined.) - - .. NOTE:: - - Works only if the base ring is a field (otherwise left - lcm do not exist in general). - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (x + t^2) * (x + t) - sage: b = 2 * (x^2 + t + 1) * (x * t) - sage: c = a.left_lcm(b); c - x^5 + (2*t^2 + t + 4)*x^4 + (3*t^2 + 4)*x^3 + (3*t^2 + 3*t + 2)*x^2 + (t^2 + t + 2)*x - sage: c.is_right_divisible_by(a) - True - sage: c.is_right_divisible_by(b) - True - sage: a.degree() + b.degree() == c.degree() + a.right_gcd(b).degree() - True - - Specifying ``monic=False``, we *can* get a nonmonic gcd:: - - sage: a.left_lcm(b,monic=False) - (t^2 + t)*x^5 + (4*t^2 + 4*t + 1)*x^4 + (t + 1)*x^3 + (t^2 + 2)*x^2 + (3*t + 4)*x - - The base ring needs to be a field:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (x + t^2) * (x + t) - sage: b = 2 * (x^2 + t + 1) * (x * t) - sage: a.left_lcm(b) - Traceback (most recent call last): - ... - TypeError: the base ring must be a field - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - if self.is_zero() or other.is_zero(): - raise ZeroDivisionError("division by zero is not valid") - L = self._left_lcm_cofactor(other) * self - if monic: - L = L.right_monic() - return L - - @coerce_binop - def right_lcm(self, other, monic=True): - r""" - Return the right lcm of ``self`` and ``other``. - - INPUT: - - - ``other`` -- a skew polynomial in the same ring as ``self`` - - - ``monic`` -- boolean (default: ``True``). Return whether the right lcm - should be normalized to be monic. - - OUTPUT: - - The right lcm of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial divides - `g` on the *left* iff it divides both ``self`` and ``other`` - on the *left*. - If monic is ``True``, `g` is in addition monic. (With this - extra condition, it is uniquely determined.) - - .. NOTE:: - - Works only if two following conditions are fulfilled - (otherwise right lcm do not exist in general): - 1) the base ring is a field and 2) the twist map on - this field is bijective. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = (x + t) * (x + t^2) - sage: b = 2 * (x + t) * (x^2 + t + 1) - sage: c = a.right_lcm(b); c - x^4 + (2*t^2 + t + 2)*x^3 + (3*t^2 + 4*t + 1)*x^2 + (3*t^2 + 4*t + 1)*x + t^2 + 4 - sage: c.is_left_divisible_by(a) - True - sage: c.is_left_divisible_by(b) - True - sage: a.degree() + b.degree() == c.degree() + a.left_gcd(b).degree() - True - - Specifying ``monic=False``, we *can* get a nonmonic gcd:: - - sage: a.right_lcm(b,monic=False) - 2*t*x^4 + (3*t + 1)*x^3 + (4*t^2 + 4*t + 3)*x^2 - + (3*t^2 + 4*t + 2)*x + 3*t^2 + 2*t + 3 - - The base ring needs to be a field:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = (x + t) * (x + t^2) - sage: b = 2 * (x + t) * (x^2 + t + 1) - sage: a.right_lcm(b) - Traceback (most recent call last): - ... - TypeError: the base ring must be a field - - And the twist map needs to be bijective:: - - sage: FR = R.fraction_field() - sage: f = FR.hom([FR(t)^2]) - sage: S. = FR['x',f] - sage: a = (x + t) * (x + t^2) - sage: b = 2 * (x + t) * (x^2 + t + 1) - sage: a.right_lcm(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t^2 - """ - if self.base_ring() not in Fields: - raise TypeError("the base ring must be a field") - if self.is_zero() or other.is_zero(): - raise ZeroDivisionError("division by zero is not valid") - L = self * self._right_lcm_cofactor(other) - if monic: - L = L.left_monic() - return L - - def _repr_(self, name=None): - r""" - Return string representation of this skew polynomial. - - INPUT: - - - ``name`` -- the name of the variable (default: the - name given when the skew polynomial ring was created) - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t^2 + 1/2*x*t - sage: a._repr_() - '(1/2*t + 1/2)*x + t^2' - sage: a._repr_(name='y') - '(1/2*t + 1/2)*y + t^2' - """ - s = " " - m = self.degree() + 1 - if name is None: - name = self.parent().variable_name() - atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic') - coeffs = self.list() - for n in reversed(range(m)): - x = coeffs[n] - if x: - if n != m-1: - s += " + " - x = y = repr(x) - if y.find("-") == 0: - y = y[1:] - if not atomic_repr and n > 0 and (y.find("+") != -1 or y.find("-") != -1): - x = "(%s)"%x - if n > 1: - var = "*%s^%s"%(name,n) - elif n==1: - var = "*%s"%name - else: - var = "" - s += "%s%s"%(x,var) - s = s.replace(" + -", " - ") - s = re.sub(r' 1(\.0+)?\*',' ', s) - s = re.sub(r' -1(\.0+)?\*',' -', s) - if s == " ": - return "0" - return s[1:] - - def _latex_(self, name=None): - r""" - Return a latex representation of this skew polynomial. - - INPUT: - - - ``name`` -- the name of the variable (default: the - name given when the skew polynomial ring was created) - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t^2 + 1/2*x*t - sage: a._latex_() - '\\left(\\frac{1}{2} t + \\frac{1}{2}\\right) x + t^{2}' - sage: a._latex_(name='y') - '\\left(\\frac{1}{2} t + \\frac{1}{2}\\right) y + t^{2}' - """ - s = " " - coeffs = self.list() - m = len(coeffs) - if name is None: - name = self.parent().latex_variable_names()[0] - atomic_repr = self.parent().base_ring()._repr_option('element_is_atomic') - for n in reversed(range(m)): - x = self[n] - x = y = x._latex_() - if x != '0': - if n != m-1: - s += " + " - if y.find("-") == 0: - y = y[1:] - if not atomic_repr and n > 0 and (y.find("+") != -1 or y.find("-") != -1): - x = "\\left(%s\\right)"%x - if n > 1: - var = "|%s^{%s}"%(name,n) - elif n==1: - var = "|%s"%name - else: - var = "" - s += "%s %s"%(x,var) - s = s.replace(" + -", " - ") - s = re.sub(" 1(\.0+)? \|"," ", s) - s = re.sub(" -1(\.0+)? \|", " -", s) - s = s.replace("|","") - if s == " ": - return "0" - return s[1:].lstrip().rstrip() - - def _is_atomic(self): - r""" - Check ``self`` is a single monomial whose leading coefficient - is atomic in the base ring. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: S([t+1])._is_atomic() - False - sage: S([1])._is_atomic() - True - """ - return (self.degree() == self.valuation() and - self.leading_coefficient()._is_atomic()) - - def __nonzero__(self): - r""" - Test whether ``self`` is nonzero. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + 1 - sage: bool(a) - True - sage: b = S.zero() - sage: bool(b) - False - """ - return not self.is_zero() - - def base_ring(self): - r""" - Return the base ring of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = S.random_element() - sage: a.base_ring() - Univariate Polynomial Ring in t over Integer Ring - sage: a.base_ring() is R - True - """ - return self.parent().base_ring() - - def shift(self, n): - r""" - Return ``self`` multiplied on the right by the power `x^n`. - - If `n` is negative, terms below `x^n` will be discarded. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^5 + t^4*x^4 + t^2*x^2 + t^10 - sage: a.shift(0) - x^5 + t^4*x^4 + t^2*x^2 + t^10 - sage: a.shift(-1) - x^4 + t^4*x^3 + t^2*x - sage: a.shift(-5) - 1 - sage: a.shift(2) - x^7 + t^4*x^6 + t^2*x^4 + t^10*x^2 - - One can also use the infix shift operator:: - - sage: a >> 2 - x^3 + t^4*x^2 + t^2 - sage: a << 2 - x^7 + t^4*x^6 + t^2*x^4 + t^10*x^2 - """ - if n == 0 or self.degree() < 0: - return self - if n > 0: - return self._parent(n*[self.base_ring().zero()] + self.list(), check=False) - if n < 0: - if n > self.degree(): - return self._parent([]) - else: - return self._parent(self.list()[-n:], check=False) - - def __lshift__(self, k): - r""" - Return ``self`` multiplied on the right by the power `x^k`. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^5 + t^4*x^4 + t^2*x^2 + t^10 - sage: a << 2 - x^7 + t^4*x^6 + t^2*x^4 + t^10*x^2 - """ - return self.shift(k) - - def __rshift__(self, k): - r""" - Return ``self`` multiplied on the right by the power `x^(-k)`. - - If `n` is negative, terms below `x^n` will be discarded. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^5 + t^4*x^4 + t^2*x^2 + t^10 - sage: a >> 2 - x^3 + t^4*x^2 + t^2 - """ - return self.shift(-k) - - def change_variable_name(self, var): - r""" - Change the name of the variable of ``self``. - - This will create the skew polynomial ring with the new name but same - base ring and twist map. The returned skew polynomial will be an element - of that skew polynomial ring. - - INPUT: - - - ``var`` -- the name of the new variable - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x', sigma] - sage: a = x^3 + (2*t + 1)*x + t^2 + 3*t + 5 - sage: b = a.change_variable_name('y'); b - y^3 + (2*t + 1)*y + t^2 + 3*t + 5 - - Note that a new parent is created at the same time:: - - sage: b.parent() - Skew Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - """ - parent = self._parent - R = parent.base_ring()[var,parent.twist_map()] - return R(self.list()) - - def is_term(self): - r""" - Return ``True`` if ``self`` is an element of the base ring times a - power of the generator. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: x.is_term() - True - sage: R(1).is_term() - True - sage: (3*x^5).is_term() - True - sage: (1+3*x^5).is_term() - False - - If you want to test that ``self`` also has leading coefficient 1, use - :meth:`is_monomial()` instead:: - - sage: (3*x^5).is_monomial() - False - """ - return len(self.exponents()) == 1 - - def is_monomial(self): - r""" - Return ``True`` if ``self`` is a monomial, i.e., a power of - the generator. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: x.is_monomial() - True - sage: (x+1).is_monomial() - False - sage: (x^2).is_monomial() - True - sage: S(1).is_monomial() - True - - The coefficient must be 1:: - - sage: (2*x^5).is_monomial() - False - sage: S(t).is_monomial() - False - - To allow a non-1 leading coefficient, use is_term():: - - sage: (2*x^5).is_term() - True - sage: S(t).is_term() - True - """ - return self.is_term() and self.leading_coefficient() == 1 - - cpdef list coefficients(self, sparse=True): - r""" - Return the coefficients of the monomials appearing in ``self``. - - If ``sparse=True`` (the default), return only the non-zero coefficients. - Otherwise, return the same value as ``self.list()``. - - .. NOTE:: - - This should be overridden in subclasses. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: a.coefficients() - [t^2 + 1, t + 1, 1] - sage: a.coefficients(sparse=False) - [t^2 + 1, 0, t + 1, 0, 1] - """ - raise NotImplementedError - - def number_of_terms(self): - r""" - Return the number of non-zero coefficients of ``self``. - - This is also known as the weight, hamming weight or sparsity. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: a.number_of_terms() - 3 - - This is also an alias for ``hamming_weight``:: - - sage: a.hamming_weight() - 3 - """ - return len(self.coefficients()) - - # alias hamming_weight for number_of_terms: - hamming_weight = number_of_terms - - def __copy__(self): - r""" - Return a "copy" of ``self``. - - In Sage, since skew polynomials are immutable, this just returns - ``self`` again. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: b = copy(a) - sage: b is a - True - """ - return self - - cpdef bint is_zero(self): - r""" - Return ``True`` if ``self`` is the zero polynomial. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + 1 - sage: a.is_zero() - False - sage: b = S.zero() - sage: b.is_zero() - True - """ - return self.degree() == -1 - - cpdef bint is_one(self): - r""" - Test whether this polynomial is `1`. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: R(1).is_one() - True - sage: (x + 3).is_one() - False - """ - return self.degree() == 0 and self[0].is_one() - - @coerce_binop - def right_mod(self, other): - r""" - Return the remainder of right division of ``self`` by ``other``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + t*x^2 - sage: b = x + 1 - sage: a % b - t + 1 - sage: (x^3 + x - 1).right_mod(x^2 - 1) - 2*x - 1 - """ - return self % other - - @coerce_binop - def left_mod(self, other): - r""" - Return the remainder of left division of ``self`` by ``other``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = 1 + t*x^2 - sage: b = x + 1 - sage: a.left_mod(b) - 2*t^2 + 4*t - """ - (_,r) = self.left_quo_rem(other) - return r - - def is_constant(self): - r""" - Return whether ``self`` is a constant polynomial. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: R(2).is_constant() - True - sage: (x + 1).is_constant() - False - """ - return self.degree() <= 0 - - def exponents(self): - r""" - Return the exponents of the monomials appearing in ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: a.exponents() - [0, 2, 4] - """ - return [i for i in range(self.degree()+1) if bool(self[i])] - - def prec(self): - r""" - Return the precision of ``self``. - - This is always infinity, since polynomials are of infinite precision by - definition (there is no big-oh). - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: x.prec() - +Infinity - """ - return infinity - - def padded_list(self, n=None): - r""" - Return list of coefficients of ``self`` up to (but not including) - degree `n`. - - Includes `0`s in the list on the right so that the list always has length - exactly `n`. - - INPUT: - - - ``n`` -- (default: ``None``); if given, an integer that - is at least `0` - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + t*x^3 + t^2*x^5 - sage: a.padded_list() - [1, 0, 0, t, 0, t^2] - sage: a.padded_list(10) - [1, 0, 0, t, 0, t^2, 0, 0, 0, 0] - sage: len(a.padded_list(10)) - 10 - sage: a.padded_list(3) - [1, 0, 0] - sage: a.padded_list(0) - [] - sage: a.padded_list(-1) - Traceback (most recent call last): - ... - ValueError: n must be at least 0 - """ - v = self.list() - if n is None: - return v - if n < 0: - raise ValueError("n must be at least 0") - if len(v) < n: - z = self._parent.base_ring().zero() - return v + [z]*(n - len(v)) - else: - return v[:int(n)] - - def variable_name(self): - r""" - Return the string name of the variable used in ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + t - sage: a.variable_name() - 'x' - """ - return self.parent().variable_name() - - def multi_point_evaluation(self, eval_pts): - """ - Evaluate ``self`` at list of evaluation points. - - INPUT: - - - ``eval_pts`` -- list of points at which ``self`` is to be evaluated - - OUTPUT: - - List of values of ``self`` at the ``eval_pts``. - - .. TODO:: - - This method currently trivially calls the evaluation function - repeatedly. If fast skew polynomial multiplication is available, an - asymptotically faster method is possible using standard divide and - conquer techniques and - :meth:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general.minimal_vanishing_polynomial`. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: eval_pts = [1, t, t^2] - sage: c = a.multi_point_evaluation(eval_pts); c - [t + 1, 3*t^2 + 4*t + 4, 4*t] - sage: c == [ a(e) for e in eval_pts ] - True - """ - return [ self(e) for e in eval_pts ] - - -cdef class SkewPolynomial_generic_dense(SkewPolynomial): - r""" - Generic implementation of dense skew polynomial supporting any valid base - ring and twist map. - """ - - def __init__(self, parent, x=None, int check=1, int construct=0, **kwds): - r""" - Construct a skew polynomial over the given parent with the given - coefficients. - - INPUT: - - - ``parent`` -- parent of ``self`` - - - ``x`` -- list of coefficients from which ``self`` can be constructed - - - ``check`` -- flag variable to normalize the polynomial - - - ``construct`` -- boolean (default: ``False``) - - TESTS:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - - We create a skew polynomial from a list:: - - sage: S([t,1]) - x + t - - from another skew polynomial:: - - sage: S(x^2 + t) - x^2 + t - - from a constant:: - - sage: x = S(t^2 + 1); x - t^2 + 1 - sage: x.parent() is S - True - """ - SkewPolynomial.__init__(self, parent) - if x is None: - self._coeffs = [] - return - - R = parent.base_ring() - if isinstance(x, list): - if check: - self._coeffs = [R(t) for t in x] - self.__normalize() - else: - self._coeffs = x - return - - if isinstance(x, SkewPolynomial): - if (x)._parent is self._parent: - x = list(x.list()) - elif R.has_coerce_map_from((x)._parent): - try: - if x.is_zero(): - self._coeffs = [] - return - except (AttributeError, TypeError): - pass - x = [x] - else: - self._coeffs = [R(a, **kwds) for a in x.list()] - if check: - self.__normalize() - return - - elif isinstance(x, int) and x == 0: - self._coeffs = [] - return - - elif isinstance(x, dict): - x = _dict_to_list(x, R.zero()) - - elif not isinstance(x, list): - x = [x] - if check: - self._coeffs = [R(z, **kwds) for z in x] - self.__normalize() - else: - self._coeffs = x - - def __reduce__(self): - r""" - Return the generic dense skew polynomial corresponding to the - current parameters provided ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: loads(dumps(x)) == x - True - sage: loads(dumps(x)) - x - """ - return (self._parent, (self._coeffs,)) - - cdef long _hash_c(self): - r""" - This hash incorporates the name of the variable. - - .. NOTE:: - - This is an internal method. Use :meth:`__hash__` instead. - """ - #todo - come up with a way to create hashes of zero that - # that do not incorrectly indicate that the element is 0. - cdef long result = 0 - cdef long result_mon - cdef long c_hash - cdef long var_name_hash = 0 - cdef int i - for i from 0 <= i < len(self._coeffs): - if i == 1: - var_name_hash = hash((self._parent)._names[0]) - c_hash = hash(self._coeffs[i]) - if c_hash != 0: - if i == 0: - result += c_hash - else: - result_mon = c_hash - result_mon = (1000003 * result_mon) ^ var_name_hash - result_mon = (1000003 * result_mon) ^ i - result += result_mon - if result == -1: - return -2 - return result - - cpdef _richcmp_(left, right, int op): - r""" - Compare the two skew polynomials ``self`` and ``other``. - - We order polynomials first by degree, then in dictionary order - starting with the coefficient of largest degree. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: b = (2*t^2)*x + t + 1 - sage: a > b - True - sage: a < b - False - """ - cdef x = (left)._coeffs - cdef y = (right)._coeffs - return PyObject_RichCompare(x, y, op) - - def __iter__(self): - r""" - Iterate over the list of coefficients of ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: P = S([1, 2, 3]) - sage: [y for y in iter(P)] - [1, 2, 3] - """ - return iter((self)._coeffs) - - def __getitem__(self, n): - r""" - Return the `n`-th coefficient of ``self``. - - INPUT: - - - ``n`` -- an integer - - OUTPUT: - - - the ``n``-th coefficient of ``self`` - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t*x^2 + (t + 3/7)*x + t^2 - sage: a[1] - t + 3/7 - sage: a[3] - 0 - """ - try: - l = (self)._coeffs[n] - return l - except IndexError: - return self.base_ring().zero() - - cpdef list list(self, bint copy=True): - r""" - Return a list of the coefficients of ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: l = a.list(); l - [t^2 + 1, 0, t + 1, 0, 1] - - Note that `l` is a list, it is mutable, and each call to the list - method returns a new list:: - - sage: type(l) - <... 'list'> - sage: l[0] = 5 - sage: a.list() - [t^2 + 1, 0, t + 1, 0, 1] - """ - if copy: - # This creates a shallow copy - return list((self)._coeffs) - else: - return (self)._coeffs - - cpdef dict dict(self): - r""" - Return a dictionary representation of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2012 + t*x^1006 + t^3 + 2*t - sage: a.dict() - {0: t^3 + 2*t, 1006: t, 2012: 1} - """ - cdef dict X = {} - cdef list Y = (self)._coeffs - cdef int i - for i in range(len(Y)): - c = Y[i] - if c: - X[i] = c - return X - - cpdef int degree(self): - r""" - Return the degree of ``self``. - - By convention, the zero skew polynomial has degree `-1`. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2 + t*x^3 + t^2*x + 1 - sage: a.degree() - 3 - - By convention, the degree of `0` is `-1`:: - - sage: S(0).degree() - -1 - """ - return len(self._coeffs) - 1 - - cpdef _add_(self, right): - r""" - Add two polynomials. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = S.random_element(monic=True); a - x^2 + (-1/95*t - 1/2)*x - sage: b = -S.random_element(monic=True); b - -x^2 - 1/2*x - sage: c = a+b; c - (-1/95*t - 1)*x - sage: c.degree() - 1 - """ - cdef Py_ssize_t i, min - cdef list x = (self)._coeffs - cdef list y = (right)._coeffs - cdef Py_ssize_t dx = len(x), dy = len(y) - - if dx > dy: - r = self._new_c([x[i] + y[i] for i from 0 <= i < dy] + x[dy:], self._parent, 0) - elif dx < dy: - r = self._new_c([x[i] + y[i] for i from 0 <= i < dx] + y[dx:], self._parent, 0) - else: - r = self._new_c([x[i] + y[i] for i in range(dx)], self._parent, 1) - return r - - cpdef _sub_(self, right): - r""" - Subtract polynomial ``right`` from ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = S.random_element(monic=True); a - x^2 + (-1/95*t - 1/2)*x - sage: b = S.random_element(monic=True); b - x^2 + 1/2*x - sage: c = a-b; c - (-1/95*t - 1)*x - sage: c.degree() - 1 - """ - cdef Py_ssize_t i, min - cdef list x = (self)._coeffs - cdef list y = (right)._coeffs - cdef Py_ssize_t dx = len(x), dy = len(y) - cdef RingElement c - - if dx > dy: - r = self._new_c([x[i] - y[i] for i from 0 <= i < dy] + x[dy:], self._parent, 0) - elif dx < dy: - r = self._new_c([x[i] - y[i] for i from 0 <= i < dx] + [ -c for c in y[dx:] ], self._parent, 0) - else: - r = self._new_c([x[i] - y[i] for i from 0 <= i < dx], self._parent, 1) - return r - - cpdef _neg_(self): - r""" - Return the negative of ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t*x^2 + x - 3 - sage: -a - -t*x^2 - x + 3 - """ - c = self._new_c([-x for x in (self)._coeffs], - self._parent, 0) - return c - - cpdef ModuleElement _lmul_(self, Element right): - r""" - Multiply ``self`` on the right by scalar. - - INPUT: - - - ``right`` -- an element of the base ring - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x + t - sage: b = t - sage: a * b - (t + 1)*x + t^2 - sage: a * b == b * a - False - """ - if right == 0: - return self._parent.zero() - cdef list x = (self)._coeffs - cdef Py_ssize_t i - twist_map = self._parent._map - r = self._new_c([ (twist_map**i)(right)*x[i] for i from 0 <= i < len(x) ], - self._parent, 0) - return r - - cpdef ModuleElement _rmul_(self, Element left): - r""" - Multiply ``self`` on the left by scalar. - - INPUT: - - - ``left`` -- an element of the base ring - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t - sage: b = x + t - sage: a * b - t*x + t^2 - sage: a * b == b * a - False - """ - if left == 0: - return self.parent().zero() - cdef list x = (self)._coeffs - cdef Py_ssize_t i - r = self._new_c([ left*x[i] for i from 0 <= i < len(x) ], self._parent, 0) - return r - - cpdef _mul_(self, right): - r""" - Multiply ``self`` on the right by a skew polynomial. - - INPUT: - - - ``right`` -- a skew polynomial in the same ring as ``self`` - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2 + t; a - x^2 + t - sage: b = x^2 + (t + 1)*x; b - x^2 + (t + 1)*x - sage: a * b - x^4 + (t + 3)*x^3 + t*x^2 + (t^2 + t)*x - sage: a * b == b * a - False - - TESTS:: - - sage: S(0)*a, (S(0)*a).list() - (0, []) - """ - cdef list x = (self)._coeffs - cdef list y = (right)._coeffs - cdef Py_ssize_t i, k, start, end - cdef Py_ssize_t dx = len(x)-1, dy = len(y)-1 - parent = self._parent - if dx == -1: - return self # = zero - elif dy == -1: - return right # = zero - elif dx == 0: - c = x[0] - r = self._new_c([c*a for a in y], parent, 0) - return r - cdef list coeffs = [] - for k from 0 <= k <= dx+dy: - start = 0 if k <= dy else k-dy - end = k if k <= dx else dx - sum = x[start] * parent.twist_map(start)(y[k-start]) - for i from start < i <= end: - sum += x[i] * parent.twist_map(i)(y[k-i]) - coeffs.append(sum) - r = self._new_c(coeffs, parent, 0) - return r - - cdef SkewPolynomial _new_c(self, list coeffs, Parent P, char check=0): - r""" - Fast creation of a new skew polynomial given a list of coefficients. - - .. WARNING:: - - The list ``coeffs`` is stored internally in the newly created skew - polynomial, so this must not be modified after calling this method. - - TESTS:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t*x^3 + x^4 + (t+1)*x^2 - sage: a.truncate(4) #indirect doctest - t*x^3 + (t + 1)*x^2 - """ - cdef type t = type(self) - cdef SkewPolynomial_generic_dense f = t.__new__(t) - f._parent = P - f._coeffs = coeffs - if check: - f.__normalize() - return f - - cdef void __normalize(self): - r""" - Remove higher order `0`-coefficients from the representation of ``self``. - - TESTS:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma]; S #indirect doctest - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 - """ - cdef list x = self._coeffs - cdef Py_ssize_t n = len(x) - 1 - while n >= 0 and not x[n]: - del x[n] - n -= 1 - - def valuation(self): - r""" - Return the minimal degree of a non-zero monomial of ``self``. - - By convention, the zero skew polynomial has valuation `+\infty`. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = x^2 + t*x^3 + t^2*x - sage: a.valuation() - 1 - - By convention, the valuation of `0` is `+\infty`:: - - sage: S(0).valuation() - +Infinity - """ - cdef list x = self._coeffs - if not x: - return infinity - cdef Py_ssize_t v = 0 - while x[v].is_zero() and v < len(x): - v += 1 - return v - - cdef void _inplace_rmul(self, SkewPolynomial_generic_dense right): - r""" - Replace ``self`` by ``self*right`` (only for internal use). - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 - sage: a.left_power_mod(100,modulus) # indirect doctest - (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t - """ - cdef list x = self._coeffs - cdef list y = right._coeffs - cdef Py_ssize_t i, k, start, end - cdef Py_ssize_t d1 = len(x)-1, d2 = len(y)-1 - parent = self._parent - if d2 == -1: - self._coeffs = [ ] - elif d1 >= 0: - for k from d1 < k <= d1+d2: - start = 0 if k <= d2 else k-d2 - sum = x[start] * parent.twist_map(start)(y[k-start]) - for i from start < i <= d1: - sum += x[i] * parent.twist_map(i)(y[k-i]) - x.append(sum) - for k from d1 >= k >= 0: - start = 0 if k <= d2 else k-d2 - end = k if k <= d1 else d1 - sum = x[start] * parent.twist_map(start)(y[k-start]) - for i from start < i <= end: - sum += x[i] * parent.twist_map(i)(y[k-i]) - x[k] = sum - - cdef void _inplace_pow(self, Py_ssize_t n): - r""" - Replace ``self`` by ``self**n`` (only for internal use). - - TESTS:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 - sage: a.left_power_mod(100,modulus) # indirect doctest - (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t - """ - while n & 1 == 0: - self._inplace_rmul(self) - n = n >> 1 - cdef SkewPolynomial_generic_dense selfpow = self._new_c(list(self._coeffs), self._parent) - n = n >> 1 - while n != 0: - selfpow._inplace_rmul(selfpow) - if n&1 == 1: - self._inplace_rmul(selfpow) - n = n >> 1 - - def truncate(self, n): - r""" - Return the polynomial resulting from discarding all monomials of degree - at least `n`. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = t*x^3 + x^4 + (t+1)*x^2 - sage: a.truncate(4) - t*x^3 + (t + 1)*x^2 - sage: a.truncate(3) - (t + 1)*x^2 - """ - return self._new_c(self._coeffs[:n], self._parent, 1) - - cdef _left_quo_rem(self, SkewPolynomial other): - r""" - Return the quotient and remainder of the left euclidean - division of ``self`` by ``other`` (C implementation). - """ - sig_check() - cdef list a = list(self._coeffs) - cdef list b = (other)._coeffs - cdef Py_ssize_t i, j - cdef Py_ssize_t da = self.degree(), db = other.degree() - if da < db: - return (self._new_c([], self._parent), self) - try: - inv = self.base_ring()(~b[db]) - except (ZeroDivisionError, TypeError): - raise NotImplementedError("the leading coefficient of the divisor is not invertible") - cdef list q = [ ] - parent = self._parent - for i from da-db >= i >= 0: - try: - c = parent.twist_map(-db)(inv*a[i+db]) - for j from 0 <= j < db: - a[i+j] -= b[j] * parent.twist_map(j)(c) - except Exception: - raise NotImplementedError("inversion of the twist map %s" % parent.twist_map()) - q.append(c) - q.reverse() - return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) - - cdef _right_quo_rem(self, SkewPolynomial other): - r""" - Return the quotient and remainder of the right euclidean - division of ``self`` by ``other`` (C implementation). - """ - sig_check() - cdef list a = list(self._coeffs) - cdef list b = (other)._coeffs - cdef Py_ssize_t i, j - cdef Py_ssize_t da = self.degree(), db = other.degree() - parent = self._parent - if da < db: - return (self._new_c([],parent), self) - try: - inv = self.base_ring()(~b[db]) - except (ZeroDivisionError, TypeError): - raise NotImplementedError("the leading coefficient of the divisor" - " is not invertible") - cdef list q = [ ] - parent = self._parent - for i from da-db >= i >= 0: - c = parent.twist_map(i)(inv) * a[i+db] - for j from 0 <= j < db: - a[i+j] -= c * parent.twist_map(i)(b[j]) - q.append(c) - q.reverse() - return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) - - cpdef left_power_mod(self, exp, modulus): - r""" - Return the remainder of ``self**exp`` in the left euclidean division - by ``modulus``. - - INPUT: - - - ``exp`` -- an Integer - - - ``modulus`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Remainder of ``self**exp`` in the left euclidean division - by ``modulus``. - - REMARK: - - The quotient of the underlying skew polynomial ring by the - principal ideal generated by ``modulus`` is in general *not* - a ring. - - As a consequence, Sage first computes exactly ``self**exp`` - and then reduce it modulo ``modulus``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 - sage: a.left_power_mod(100,modulus) - (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t - """ - cdef SkewPolynomial_generic_dense r - if not isinstance(exp, Integer): - try: - exp = Integer(exp) - except TypeError: - raise TypeError("non-integral exponents not supported") - - if len(self._coeffs) <= 1: - return self.parent()(self._coeffs[0]**exp) - if exp == 0: - return self.parent().one() - if exp < 0: - return (~self).left_power_mod(-exp, modulus) - - if self == self.parent().gen(): - P = self.parent() - R = P.base_ring() - v = [R.zero()]*exp + [R.one()] - r = self._parent(v) - else: - r = self._new_c(list(self._coeffs), self._parent) - r._inplace_pow(exp) - - if modulus: - _, r = r._left_quo_rem(modulus) - return r - - cpdef right_power_mod(self, exp, modulus): - r""" - Return the remainder of ``self**exp`` in the right euclidean division - by ``modulus``. - - INPUT: - - - ``exp`` -- an Integer - - - ``modulus`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Remainder of ``self**exp`` in the right euclidean division - by ``modulus``. - - REMARK: - - The quotient of the underlying skew polynomial ring by the - principal ideal generated by ``modulus`` is in general *not* - a ring. - - As a consequence, Sage first computes exactly ``self**exp`` - and then reduce it modulo ``modulus``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: b = a^10 # short form for ``a._pow_(10)`` - sage: b == a*a*a*a*a*a*a*a*a*a - True - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 - sage: br = a.right_power_mod(10,modulus); br - (t^2 + t)*x^2 + (3*t^2 + 1)*x + t^2 + t - sage: rq, rr = b.right_quo_rem(modulus) - sage: br == rr - True - sage: a.right_power_mod(100,modulus) - (2*t^2 + 3)*x^2 + (t^2 + 4*t + 2)*x + t^2 + 2*t + 1 """ - cdef SkewPolynomial_generic_dense r - if not isinstance(exp, Integer): - try: - exp = Integer(exp) - except TypeError: - raise TypeError("non-integral exponents not supported") - - if len(self._coeffs) <= 1: - return self.parent()(self._coeffs[0]**exp) - if exp == 0: - return self.parent().one() - if exp < 0: - return (~self).right_power_mod(-exp, modulus) - - if self == self.parent().gen(): - P = self.parent() - R = P.base_ring() - v = [R.zero()]*exp + [R.one()] - r = self._parent(v) - else: - r = self._new_c(list(self._coeffs), self._parent) - r._inplace_pow(exp) - - if modulus: - _, r = r._right_quo_rem(modulus) + if left == 0: + return self.parent().zero() + cdef list x = (self)._coeffs + cdef Py_ssize_t i + r = self._new_c([ left*x[i] for i from 0 <= i < len(x) ], self._parent, 0) return r - def __pow__(self, exp, modulus): + cpdef _mul_(self, right): r""" - Return the remainder of ``self**exp`` in the left euclidean - division by ``modulus``. + Multiply ``self`` on the right by a skew polynomial. INPUT: - - ``exp`` -- an Integer - - - ``modulus`` -- a skew polynomial in the same ring as ``self`` - - OUTPUT: - - Remainder of ``self**exp`` in the right euclidean division - by ``modulus``. - - REMARK: - - The quotient of the underlying skew polynomial ring by the - principal ideal generated by ``modulus`` is in general *not* - a ring. - - As a consequence, Sage first computes exactly ``self**exp`` - and then reduce it modulo ``modulus``. - - .. SEEALSO:: - - :meth:`~sage.rings.polynomial.skew_polynomial_element._pow_` - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: b = a^10 - sage: b == a*a*a*a*a*a*a*a*a*a - True - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 - sage: bmod = a.right_power_mod(10,modulus); bmod - (t^2 + t)*x^2 + (3*t^2 + 1)*x + t^2 + t - sage: rq, rr = b.right_quo_rem(modulus) - sage: bmod == rr - True - """ - return self.right_power_mod(exp, modulus) - - cpdef list coefficients(self, sparse=True): - r""" - Return the coefficients of the monomials appearing in ``self``. - - If ``sparse=True`` (the default), return only the non-zero coefficients. - Otherwise, return the same value as ``self.list()``. + - ``right`` -- a skew polynomial in the same ring as ``self`` EXAMPLES:: sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] - sage: a = 1 + x^4 + (t+1)*x^2 + t^2 - sage: a.coefficients() - [t^2 + 1, t + 1, 1] - sage: a.coefficients(sparse=False) - [t^2 + 1, 0, t + 1, 0, 1] - """ - zero = self.parent().base_ring().zero() - if sparse: - return [c for c in self._coeffs if not c.is_zero()] - else: - return self._coeffs - -cdef class ConstantSkewPolynomialSection(Map): - r""" - Representation of the canonical homomorphism from the constants of a skew - polynomial ring to the base ring. - - This class is necessary for automatic coercion from zero-degree skew - polynomial ring into the base ring. - - EXAMPLES:: + sage: a = x^2 + t; a + x^2 + t + sage: b = x^2 + (t + 1)*x; b + x^2 + (t + 1)*x + sage: a * b + x^4 + (t + 3)*x^3 + t*x^2 + (t^2 + t)*x + sage: a * b == b * a + False - sage: from sage.rings.polynomial.skew_polynomial_element import ConstantSkewPolynomialSection - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: m = ConstantSkewPolynomialSection(S, R); m - Generic map: - From: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 - To: Univariate Polynomial Ring in t over Rational Field - """ - cpdef Element _call_(self, x): - r""" - Return the corresponding element of the base ring if ``self`` is a - constant skew polynomial. Otherwise, it fails. - TESTS:: - - sage: from sage.rings.polynomial.skew_polynomial_element import ConstantSkewPolynomialSection - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: m = ConstantSkewPolynomialSection(S, R); m - Generic map: - From: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 - To: Univariate Polynomial Ring in t over Rational Field - sage: m(S([0,1])-S([0,1])) - 0 - sage: m(S([3,1])-S([0,1])) - 3 - sage: m(S([0,1])-S([0,t])) - Traceback (most recent call last): - ... - TypeError: not a constant polynomial - """ - if x.degree() <= 0: - try: - return (x.constant_coefficient()) - except AttributeError: - return ((x).constant_coefficient()) - else: - raise TypeError("not a constant polynomial") - -cdef class SkewPolynomialBaseringInjection(Morphism): - r""" - Representation of the canonical homomorphism from a ring `R` into a skew - polynomial ring over `R`. - - This class is necessary for automatic coercion from the base ring to the skew - polynomial ring. - - .. SEEALSO:: - - :class:`~sage.rings.polynomial.polynomial_element.PolynomialBaseringInjection` - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: S.coerce_map_from(S.base_ring()) #indirect doctest - Skew Polynomial base injection morphism: - From: Univariate Polynomial Ring in t over Rational Field - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 - """ + sage: S(0)*a, (S(0)*a).list() + (0, []) + """ + cdef list x = (self)._coeffs + cdef list y = (right)._coeffs + cdef Py_ssize_t i, k, start, end + cdef Py_ssize_t dx = len(x)-1, dy = len(y)-1 + parent = self._parent + if dx == -1: + return self # = zero + elif dy == -1: + return right # = zero + elif dx == 0: + c = x[0] + r = self._new_c([c*a for a in y], parent, 0) + return r + cdef list coeffs = [] + for k from 0 <= k <= dx+dy: + start = 0 if k <= dy else k-dy + end = k if k <= dx else dx + sum = x[start] * parent.twist_morphism(start)(y[k-start]) + for i from start < i <= end: + sum += x[i] * parent.twist_morphism(i)(y[k-i]) + coeffs.append(sum) + r = self._new_c(coeffs, parent, 0) + return r - def __init__(self, domain, codomain): + cdef void _inplace_rmul(self, SkewPolynomial_generic_dense right): r""" - Construct a Skew Polynomial Basering Injection. - - INPUT: - - - ``domain`` -- a ring `R`. This will be the domain of the injection. - - - ``codomain`` -- a skew polynomial ring over ``domain``. This will be - the codomain. + Replace ``self`` by ``self*right`` (only for internal use). TESTS:: - sage: from sage.rings.polynomial.skew_polynomial_element import SkewPolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] - sage: SkewPolynomialBaseringInjection(k, k['x', Frob]) - Skew Polynomial base injection morphism: - From: Finite Field in t of size 5^3 - To: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - sage: R. = QQ[] - sage: SkewPolynomialBaseringInjection(QQ, k['x', Frob]) - Traceback (most recent call last): - ... - AssertionError: the domain of the injection must be the base ring of the skew polynomial ring + sage: a = x + t + sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 + sage: a.left_power_mod(100,modulus) # indirect doctest + (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t """ - assert codomain.base_ring() is domain, \ - "the domain of the injection must be the base ring of the skew polynomial ring" - Morphism.__init__(self, Hom(domain,codomain)) - self._an_element = codomain.gen() - self._repr_type_str = "Skew Polynomial base injection" - self._new_constant_poly_ = self._an_element._new_constant_poly - - def an_element(self): + cdef list x = self._coeffs + cdef list y = right._coeffs + cdef Py_ssize_t i, k, start, end + cdef Py_ssize_t d1 = len(x)-1, d2 = len(y)-1 + parent = self._parent + if d2 == -1: + self._coeffs = [ ] + elif d1 >= 0: + for k from d1 < k <= d1+d2: + start = 0 if k <= d2 else k-d2 + sum = x[start] * parent.twist_morphism(start)(y[k-start]) + for i from start < i <= d1: + sum += x[i] * parent.twist_morphism(i)(y[k-i]) + x.append(sum) + for k from d1 >= k >= 0: + start = 0 if k <= d2 else k-d2 + end = k if k <= d1 else d1 + sum = x[start] * parent.twist_morphism(start)(y[k-start]) + for i from start < i <= end: + sum += x[i] * parent.twist_morphism(i)(y[k-i]) + x[k] = sum + + cdef void _inplace_pow(self, Py_ssize_t n): r""" - Return an element of the codomain of the ring homomorphism. + Replace ``self`` by ``self**n`` (only for internal use). - EXAMPLES:: + TESTS:: - sage: from sage.rings.polynomial.skew_polynomial_element import SkewPolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] - sage: m = SkewPolynomialBaseringInjection(k, k['x', Frob]) - sage: m.an_element() - x + sage: a = x + t + sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 + sage: a.left_power_mod(100,modulus) # indirect doctest + (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t """ - return self._an_element + while n & 1 == 0: + self._inplace_rmul(self) + n = n >> 1 + cdef SkewPolynomial_generic_dense selfpow = self._new_c(list(self._coeffs), self._parent) + n = n >> 1 + while n != 0: + selfpow._inplace_rmul(selfpow) + if n&1 == 1: + self._inplace_rmul(selfpow) + n = n >> 1 - cpdef Element _call_(self, e): + cdef _left_quo_rem(self, OrePolynomial other): r""" - Return the corresponding skew polynomial to the element from the - base ring according to ``self``. - - INPUT: - - - ``e`` -- element belonging to the base ring according to ``self`` - - OUTPUT: - - The skew polynomial corresponding to `e` according to ``self``. - - TESTS:: - - sage: from sage.rings.polynomial.skew_polynomial_element import SkewPolynomialBaseringInjection - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: m = SkewPolynomialBaseringInjection(k, k['x', Frob]) - sage: m(4) - 4 - sage: parent(m(4)) - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Return the quotient and remainder of the left euclidean + division of ``self`` by ``other`` (C implementation). """ + sig_check() + cdef list a = list(self._coeffs) + cdef list b = (other)._coeffs + cdef Py_ssize_t i, j + cdef Py_ssize_t da = self.degree(), db = other.degree() + if da < db: + return (self._new_c([], self._parent), self) try: - return self._codomain._element_constructor_(e) - except AttributeError: - return self._codomain(e) + inv = self.base_ring()(~b[db]) + except (ZeroDivisionError, TypeError): + raise NotImplementedError("the leading coefficient of the divisor is not invertible") + cdef list q = [ ] + parent = self._parent + for i from da-db >= i >= 0: + try: + c = parent.twist_morphism(-db)(inv*a[i+db]) + for j from 0 <= j < db: + a[i+j] -= b[j] * parent.twist_morphism(j)(c) + except Exception: + raise NotImplementedError("inversion of the twist map %s" % parent.twist_morphism()) + q.append(c) + q.reverse() + return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) - def section(self): + cdef _right_quo_rem(self, OrePolynomial other): r""" - Return the canonical homomorphism from the constants of a skew - polynomial ring to the base ring according to ``self``. - - TESTS:: - - sage: from sage.rings.polynomial.skew_polynomial_element import SkewPolynomialBaseringInjection - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: m = SkewPolynomialBaseringInjection(k, k['x', Frob]) - sage: m.section() - Generic map: - From: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - To: Finite Field in t of size 5^3 + Return the quotient and remainder of the right euclidean + division of ``self`` by ``other`` (C implementation). """ - return ConstantSkewPolynomialSection(self._codomain, self.domain()) + sig_check() + cdef list a = list(self._coeffs) + cdef list b = (other)._coeffs + cdef Py_ssize_t i, j + cdef Py_ssize_t da = self.degree(), db = other.degree() + parent = self._parent + if da < db: + return (self._new_c([],parent), self) + try: + inv = self.base_ring()(~b[db]) + except (ZeroDivisionError, TypeError): + raise NotImplementedError("the leading coefficient of the divisor" + " is not invertible") + cdef list q = [ ] + parent = self._parent + for i from da-db >= i >= 0: + c = parent.twist_morphism(i)(inv) * a[i+db] + for j from 0 <= j < db: + a[i+j] -= c * parent.twist_morphism(i)(b[j]) + q.append(c) + q.reverse() + return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 6f4f090cab9..b854ceeb2c9 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -32,7 +32,7 @@ from sage.matrix.matrix2 import NotFullRankError from sage.rings.polynomial.polynomial_element cimport Polynomial from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.polynomial.skew_polynomial_element cimport SkewPolynomial +from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial as SkewPolynomial from sage.rings.polynomial.skew_polynomial_finite_order cimport SkewPolynomial_finite_order_dense from sage.combinat.permutation import Permutation, Permutations diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index d1f50a04775..eed23b1640a 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -157,7 +157,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): pol.append(l[k]) M.append(Polk(pol)) for i from 0 <= i <= d: - l[i] = self._parent.twist_map()(l[i]) + l[i] = self._parent.twist_morphism()(l[i]) return matrix(Polk, r, r, M) @@ -216,12 +216,12 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): True """ order = self.parent()._order - twist_map = self.parent().twist_map() + twist_morphism = self.parent().twist_morphism() coeffs = [ ] for i in range(0, self.degree()+1, order): tr = c = self._coeffs[i] for _ in range(order-1): - tr = c + twist_map(tr) + tr = c + twist_morphism(tr) coeffs.append(tr) if var is False: return tuple(coeffs) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index a6c7e1203e1..8ee501e21cf 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -38,26 +38,20 @@ import sage from sage.structure.richcmp import op_EQ -from sage.misc.prandom import randint -from sage.misc.cachefunc import cached_method -from sage.rings.infinity import Infinity from sage.structure.category_object import normalize_names -from sage.structure.unique_representation import UniqueRepresentation from sage.rings.ring import Algebra, Field from sage.rings.integer import Integer -from sage.categories.commutative_rings import CommutativeRings -from sage.categories.algebras import Algebras -from sage.categories.fields import Fields +from sage.matrix.matrix_space import MatrixSpace -from sage.categories.morphism import Morphism, IdentityMorphism +from sage.categories.morphism import Morphism from sage.rings.morphism import RingHomomorphism from sage.categories.homset import Hom from sage.categories.map import Section from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing -from sage.rings.polynomial.skew_polynomial_element import SkewPolynomialBaseringInjection +from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing WORKING_CENTER_MAX_TRIES = 1000 @@ -219,245 +213,8 @@ def _lagrange_polynomial(R, eval_pts, values): # Generic implementation of skew polynomial rings ################################################# -class SkewPolynomialRing(Algebra, UniqueRepresentation): - r""" - Construct and return the globally unique skew polynomial ring with the - given properties and variable names. - - Given a ring `R` and a ring automorphism `\sigma` of `R`, the ring of - skew polynomials `R[X, \sigma]` is the usual abelian group polynomial - `R[X]` equipped with the modification multiplication deduced from the - rule `X a = \sigma(a) X`. - We refer to [Ore1933]_ for more material on skew polynomials. - - .. SEEALSO:: - - - :class:`sage.rings.polynomial.skew_polynomial_element.SkewPolynomial` - - INPUT: - - - ``base_ring`` -- a commutative ring - - - ``twist_map`` -- an automorphism of the base ring - - - ``names`` -- a string or a list of strings - - - ``sparse`` -- a boolean (default: ``False``). Currently not supported. - - .. NOTE:: - - The current implementation of skew polynomial rings does not - support derivations. Sparse skew polynomials and multivariate skew - polynomials are also not implemented. - - OUTPUT: - - A univariate skew polynomial ring over ``base_ring`` twisted by - ``twist_map`` when ``names`` is a string with no - commas (``,``) or a list of length 1. Otherwise we raise a - ``NotImplementedError`` as multivariate skew polynomial rings are - not yet implemented. - - UNIQUENESS and IMMUTABILITY: - - In Sage, there is exactly one skew polynomial ring for each - triple (base ring, twisting map, name of the variable). - - EXAMPLES of VARIABLE NAME CONTEXT:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R, sigma); S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - - The names of the variables defined above cannot be arbitrarily - modified because each skew polynomial ring is unique in Sage and other - objects in Sage could have pointers to that skew polynomial ring. - - However, the variable can be changed within the scope of a ``with`` - block using the localvars context:: - - sage: with localvars(S, ['y']): - ....: print(S) - Skew Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - - SQUARE BRACKETS NOTATION: - - You can alternatively create a skew polynomial ring over `R` - twisted by ``twist_map`` by writing - ``R['varname', twist_map]``. - - EXAMPLES: - - We first define the base ring:: - - sage: R. = ZZ[]; R - Univariate Polynomial Ring in t over Integer Ring - - and the twisting map:: - - sage: twist_map = R.hom([t+1]); twist_map - Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 - - Now, we are ready to define the skew polynomial ring:: - - sage: S = SkewPolynomialRing(R, twist_map, names='x'); S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - - Use the diamond brackets notation to make the variable ready - for use after you define the ring:: - - sage: S. = SkewPolynomialRing(R, twist_map) - sage: (x + t)^2 - x^2 + (2*t + 1)*x + t^2 - - Here is an example with the square bracket notations:: - - sage: S. = R['x', twist_map]; S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - - Rings with different variables names are different:: - - sage: R['x', twist_map] == R['y', twist_map] - False - - Of course, skew polynomial rings with different twist maps are not - equal either:: - - sage: R['x',sigma] == R['x',sigma^2] - False - - TESTS: - - You must specify a variable name:: - - sage: SkewPolynomialRing(R, twist_map) - Traceback (most recent call last): - ... - TypeError: you must specify the name of the variable - - With this syntax, it is not possible to omit the name of the - variable neither in LHS nor in RHS. If we omit it in LHS, the - variable is not created:: - - sage: Sy = R['y', twist_map]; Sy - Skew Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring - twisted by t |--> t + 1 - sage: y.parent() - Traceback (most recent call last): - ... - NameError: name 'y' is not defined - - If we omit it in RHS, sage tries to create a polynomial ring and fails:: - - sage: Sz. = R[twist_map] - Traceback (most recent call last): - ... - ValueError: variable name 'Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring\n Defn: t |--> t + 1' is not alphanumeric - - Multivariate skew polynomial rings are not supported:: - - sage: S = SkewPolynomialRing(R, twist_map,names=['x','y']) - Traceback (most recent call last): - ... - NotImplementedError: multivariate skew polynomials rings not supported - - Sparse skew polynomial rings are not implemented:: - - sage: S = SkewPolynomialRing(R, twist_map, names='x', sparse=True) - Traceback (most recent call last): - ... - NotImplementedError: sparse skew polynomial rings are not implemented - - Saving and loading of polynomial rings works:: - - sage: loads(dumps(R['x',sigma])) == R['x',sigma] - True - - .. TODO:: - - - Sparse Skew Polynomial Ring - - Multivariate Skew Polynomial Ring - - Add derivations. - """ - Element = None - - @staticmethod - def __classcall_private__(cls, base_ring, twist_map=None, names=None, sparse=False): - r""" - Construct the skew polynomial ring associated to the given parameters - - TESTS:: - - sage: k. = ZZ[] - sage: theta = k.hom([t+1]) - sage: S. = SkewPolynomialRing(k, theta) - sage: S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - sage: type(S) - - - We check the uniqueness property of parents:: - - sage: sigma = k.hom([t+1]) - sage: T. = SkewPolynomialRing(k, sigma) - sage: S is T - True - - When the twisting morphism is a Frobenius over a finite field, a special class - is used:: - - sage: k. = GF(7^5) - sage: Frob = k.frobenius_endomorphism(2) - sage: S. = SkewPolynomialRing(k, Frob) - sage: type(S) - - """ - if base_ring not in CommutativeRings(): - raise TypeError('base_ring must be a commutative ring') - if twist_map is None: - twist_map = IdentityMorphism(base_ring) - else: - if (not isinstance(twist_map, Morphism) - or twist_map.domain() is not base_ring - or twist_map.codomain() is not base_ring): - raise TypeError("the twist map must be a ring automorphism of base_ring (=%s)" % base_ring) - if sparse: - raise NotImplementedError("sparse skew polynomial rings are not implemented") - if names is None: - raise TypeError("you must specify the name of the variable") - try: - names = normalize_names(1, names)[0] - except IndexError: - raise NotImplementedError("multivariate skew polynomials rings not supported") - - # We find the best constructor - constructor = None - if base_ring in Fields(): - try: - order = twist_map.order() - if order is not Infinity: - if base_ring.is_finite(): - constructor = SkewPolynomialRing_finite_field - else: - constructor = SkewPolynomialRing_finite_order - except (AttributeError, NotImplementedError): - pass - if constructor is not None: - try: - return constructor(base_ring, twist_map, names, sparse) - except (AttributeError, NotImplementedError): - pass - - # We fallback to generic implementation - return cls.__classcall__(cls, base_ring, twist_map, names, sparse) - - def __init__(self, base_ring, twist_map, name, sparse, category=None): +class SkewPolynomialRing(OrePolynomialRing): + def __init__(self, base_ring, morphism, derivation, name, sparse, category=None): r""" Initialize ``self``. @@ -485,572 +242,11 @@ def __init__(self, base_ring, twist_map, name, sparse, category=None): 0 sage: TestSuite(S).run() """ + if derivation is not None: + raise NotImplementedError if self.Element is None: self.Element = sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense - self.__is_sparse = sparse - self._map = twist_map - self._maps = {0: IdentityMorphism(base_ring), 1: self._map} - category = Algebras(base_ring).or_subcategory(category) - Algebra.__init__(self, base_ring, names=name, normalize=True, category=category) - - def _element_constructor_(self, a=None, check=True, construct=False, **kwds): - r""" - Convert a base ring element ``a`` into a constant of this univariate - skew polynomial ring, possibly non-canonically. - - INPUT: - - - ``a`` -- (default: ``None``) an element of the base ring - of ``self`` or a ring that has a coerce map from ``self`` - - - ``check`` -- boolean (default: ``True``) - - - ``construct`` -- boolean (default: ``False``) - - OUTPUT: - - An zero-degree skew polynomial in ``self``, equal to ``a``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) - sage: S(1 + x + x^2 + x^3) - x^3 + x^2 + x + 1 - sage: S(1 + t) - t + 1 - sage: S(1 + t).degree() - 0 - sage: S(0).list() - [] - - TESTS:: - - sage: S(x, check=True) - x - """ - C = self.Element - if isinstance(a, list): - return C(self, a, check=check, construct=construct) - if isinstance(a, sage.structure.element.Element): - P = a.parent() - - def build(check): - if a.is_zero(): - return P.zero() - else: - return C(self, [a], check=check, construct=construct) - if P is self: - return a - elif P is self.base_ring(): - build(False) - elif P == self.base_ring() or self.base_ring().has_coerce_map_from(P): - build(True) - try: - return a._polynomial_(self) - except AttributeError: - pass - if isinstance(a, str): - try: - from sage.misc.parser import Parser, LookupNameMaker - R = self.base_ring() - p = Parser(Integer, R, LookupNameMaker({self.variable_name(): self.gen()}, R)) - return self(p.parse(a)) - except NameError: - raise TypeError("unable to coerce string") - return C(self, a, check, construct=construct, **kwds) - - def _coerce_map_from_base_ring(self): - """ - Return a coercion map from the base ring of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: S. = SkewPolynomialRing(R, R.hom([t + 1])) - sage: S.coerce_map_from(R) - Skew Polynomial base injection morphism: - From: Univariate Polynomial Ring in t over Integer Ring - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - sage: x.parent() - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - sage: t.parent() - Univariate Polynomial Ring in t over Integer Ring - sage: y = x + t # indirect doctest - sage: y - x + t - sage: y.parent() is S - True - """ - return SkewPolynomialBaseringInjection(self.base_ring(), self) - - def _coerce_map_from_(self, P): - r""" - Check whether ``self`` has a coerce map from ``P``. - - The rings that canonically coerce into this ring are: - - - this ring itself - - - any ring that canonically coerces to the base ring of this ring - - - skew polynomial rings in the same variable and automorphism over - any base ring that canonically coerces to the base ring of this ring - - INPUT: - - - ``P`` -- a ring - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) - sage: S.has_coerce_map_from(S) - True - sage: S.has_coerce_map_from(R) - True - sage: S.has_coerce_map_from(ZZ) - True - sage: S.has_coerce_map_from(GF(5^3)) - False - - sage: S.coerce_map_from(ZZ) - Composite map: - From: Integer Ring - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - Defn: Polynomial base injection morphism: - From: Integer Ring - To: Univariate Polynomial Ring in t over Integer Ring - then - Skew Polynomial base injection morphism: - From: Univariate Polynomial Ring in t over Integer Ring - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - sage: S.coerce_map_from(S) - Identity endomorphism of Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - """ - base_ring = self.base_ring() - try: - connecting = base_ring.coerce_map_from(P) - if connecting is not None: - return self.coerce_map_from(base_ring) * connecting - except TypeError: - pass - if isinstance(P, SkewPolynomialRing): - if self.__is_sparse and not P.is_sparse(): - return False - if P.variable_name() == self.variable_name(): - return base_ring.has_coerce_map_from(P.base_ring()) - - def _repr_(self): - r""" - Return a string representation of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) - sage: S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - """ - s = "Skew Polynomial Ring in %s over %s twisted by %s" % (self.variable_name(), - self.base_ring(), - self._map._repr_short()) - if self.is_sparse(): - s = "Sparse " + s - return s - - def _latex_(self): - r""" - Return a latex representation of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) - sage: latex(S) - \Bold{Z}[t][x,\begin{array}{l} - \text{\texttt{Ring{ }endomorphism...}} - \end{array}] - """ - from sage.misc.latex import latex - return "%s[%s,%s]" % (latex(self.base_ring()), self.latex_variable_names()[0], - latex(self._map)) - - def change_var(self, var): - r""" - Return the skew polynomial ring in variable ``var`` with the same base - ring and twist map as ``self``. - - INPUT: - - - ``var`` -- a string representing the name of the new variable. - - OUTPUT: - - ``self`` with variable name changed to ``var``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: R. = SkewPolynomialRing(k,Frob); R - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 - sage: Ry = R.change_var('y'); Ry - Skew Polynomial Ring in y over Finite Field in t of size 5^3 twisted by t |--> t^5 - sage: Ry is R.change_var('y') - True - """ - return SkewPolynomialRing(self.base_ring(), self._map, names=var, - sparse=self.__is_sparse) - - def characteristic(self): - r""" - Return the characteristic of the base ring of ``self``. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: R['x',sigma].characteristic() - 0 - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: k['y',Frob].characteristic() - 5 - """ - return self.base_ring().characteristic() - - @cached_method - def twist_map(self, n=1): - r""" - Return the twist map, the automorphism of the base ring of - ``self``, iterated ``n`` times. - - INPUT: - - - ``n`` - an integer (default: 1) - - OUTPUT: - - ``n``-th iterative of the twist map of this skew polynomial ring. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: S.twist_map() - Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 1 - sage: S.twist_map() == sigma - True - sage: S.twist_map(10) - Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 10 - - If ``n`` in negative, Sage tries to compute the inverse of the - twist map:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: T. = k['y',Frob] - sage: T.twist_map(-1) - Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 - - Sometimes it fails, even if the twist map is actually invertible:: - - sage: S.twist_map(-1) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 1 - """ - try: - return self._map ** n - except TypeError as e: - if n < 0: - raise NotImplementedError("inversion of the twist map %s" % self._map) - else: - raise ValueError("Unexpected error in iterating the twist map: %s", e) - - @cached_method - def gen(self, n=0): - r""" - Return the indeterminate generator of this skew polynomial ring. - - INPUT: - - - ``n`` -- index of generator to return (default: 0). Exists for - compatibility with other polynomial rings. - - EXAMPLES:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma]; S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 - sage: y = S.gen(); y - x - sage: y == x - True - sage: y is x - True - sage: S.gen(0) - x - - This is also known as the parameter:: - - sage: S.parameter() is S.gen() - True - """ - if n != 0: - raise IndexError("generator %s not defined" % n) - return self.Element(self, [0, 1]) - - parameter = gen - - def gens_dict(self): - r""" - Return a {name: variable} dictionary of the generators of ``self``. - - EXAMPLES:: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) - sage: S.gens_dict() - {'x': x} - """ - return dict(zip(self.variable_names(), self.gens())) - - def is_finite(self): - r""" - Return ``False`` since skew polynomial rings are not finite - (unless the base ring is `0`.) - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: k.is_finite() - True - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: S.is_finite() - False - """ - R = self.base_ring() - return R.is_finite() and R.order() == 1 - - def is_exact(self): - r""" - Return ``True`` if elements of this skew polynomial ring are exact. - This happens if and only if elements of the base ring are exact. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: S.is_exact() - True - sage: S.base_ring().is_exact() - True - - sage: R. = k[[]] - sage: sigma = R.hom([u+u^2]) - sage: T. = R['y',sigma] - sage: T.is_exact() - False - sage: T.base_ring().is_exact() - False - """ - return self.base_ring().is_exact() - - def is_sparse(self): - r""" - Return ``True`` if the elements of this polynomial ring are sparsely - represented. - - .. WARNING:: - - Since sparse skew polynomials are not yet implemented, this - function always returns ``False``. - - EXAMPLES:: - - sage: R. = RR[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: S.is_sparse() - False - """ - return self.__is_sparse - - def ngens(self): - r""" - Return the number of generators of this skew polynomial ring, - which is 1. - - EXAMPLES:: - - sage: R. = RR[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: S.ngens() - 1 - """ - return 1 - - def random_element(self, degree=2, monic=False, *args, **kwds): - r""" - Return a random skew polynomial in ``self``. - - INPUT: - - - ``degree`` -- (default: 2) integer with degree - or a tuple of integers with minimum and maximum degrees - - - ``monic`` -- (default: ``False``) if ``True``, return a monic - skew polynomial - - - ``*args, **kwds`` -- passed on to the ``random_element`` method - for the base ring - - OUTPUT: - - Skew polynomial such that the coefficients of `x^i`, for `i` up - to ``degree``, are random elements from the base ring, randomized - subject to the arguments ``*args`` and ``**kwds``. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x', Frob] - sage: S.random_element() # random - (2*t^2 + 3)*x^2 + (4*t^2 + t + 4)*x + 2*t^2 + 2 - sage: S.random_element(monic=True) # random - x^2 + (2*t^2 + t + 1)*x + 3*t^2 + 3*t + 2 - - Use ``degree`` to obtain polynomials of higher degree - - sage: p = S.random_element(degree=5) # random - (t^2 + 3*t)*x^4 + (4*t + 4)*x^3 + (4*t^2 + 4*t)*x^2 + (2*t^2 + 1)*x + 3 - - When ``monic`` is ``False``, the returned skew polynomial may have - a degree less than ``degree`` (it happens when the random leading - coefficient is zero). However, if ``monic`` is ``True``, this can't - happen:: - - sage: p = S.random_element(degree=4, monic=True) - sage: p.leading_coefficient() == S.base_ring().one() - True - sage: p.degree() == 4 - True - - If a tuple of two integers is given for the degree argument, a random - integer will be chosen between the first and second element of the - tuple as the degree, both inclusive:: - - sage: S.random_element(degree=(2,7)) # random - (3*t^2 + 1)*x^4 + (4*t + 2)*x^3 + (4*t + 1)*x^2 - + (t^2 + 3*t + 3)*x + 3*t^2 + 2*t + 2 - - If the first tuple element is greater than the second, a a - ``ValueError`` is raised:: - - sage: S.random_element(degree=(5,4)) - Traceback (most recent call last): - ... - ValueError: first degree argument must be less or equal to the second - """ - R = self.base_ring() - if isinstance(degree, (list, tuple)): - if len(degree) != 2: - raise ValueError("degree argument must be an integer or a tuple of 2 integers (min_degree, max_degree)") - if degree[0] > degree[1]: - raise ValueError("first degree argument must be less or equal to the second") - degree = randint(*degree) - if monic: - return self([R.random_element(*args, **kwds) - for _ in range(degree)] + [R.one()]) - else: - return self([R.random_element(*args, **kwds) - for _ in range(degree + 1)]) - - def random_irreducible(self, degree=2, monic=True, *args, **kwds): - r""" - Return a random irreducible skew polynomial. - - .. WARNING:: - - Elements of this skew polynomial ring need to have a method - is_irreducible(). Currently, this method is implemented only - when the base ring is a finite field. - - INPUT: - - - ``degree`` - Integer with degree (default: 2) - or a tuple of integers with minimum and maximum degrees - - - ``monic`` - if True, returns a monic skew polynomial - (default: True) - - - ``*args, **kwds`` - Passed on to the ``random_element`` method for - the base ring - - OUTPUT: - - - A random skew polynomial - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: A = S.random_irreducible(); A - x^2 + (4*t^2 + 3*t + 4)*x + 4*t^2 + t - sage: A.is_irreducible() - True - sage: B = S.random_irreducible(degree=3,monic=False); B # random - (4*t + 1)*x^3 + (t^2 + 3*t + 3)*x^2 + (3*t^2 + 2*t + 2)*x + 3*t^2 + 3*t + 1 - sage: B.is_irreducible() - True - """ - if isinstance(degree, (list, tuple)): - if len(degree) != 2: - raise ValueError("degree argument must be an integer or a tuple of 2 integers (min_degree, max_degree)") - if degree[0] > degree[1]: - raise ValueError("minimum degree must be less or equal than maximum degree") - degree = randint(*degree) - while True: - irred = self.random_element((degree,degree), monic=monic) - if irred.is_irreducible(): - return irred - - def is_commutative(self): - r""" - Return ``True`` if this skew polynomial ring is commutative, i.e. if the - twist map is the identity. - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: S.is_commutative() - False - - sage: T. = k['y',Frob^3] - sage: T.is_commutative() - True - """ - return self.twist_map().is_identity() + OrePolynomialRing.__init__(self, base_ring, morphism, None, name, sparse, category) def minimal_vanishing_polynomial(self, eval_pts): """ @@ -1353,7 +549,7 @@ class SkewPolynomialRing_finite_order(SkewPolynomialRing): :class:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` :mod:`sage.rings.polynomial.skew_polynomial_finite_order` """ - def __init__(self, base_ring, twist_map, name, sparse, category=None): + def __init__(self, base_ring, morphism, derivation, name, sparse, category=None): r""" Initialize this skew polynomial @@ -1384,9 +580,9 @@ def __init__(self, base_ring, twist_map, name, sparse, category=None): if self.Element is None: import sage.rings.polynomial.skew_polynomial_finite_order self.Element = sage.rings.polynomial.skew_polynomial_finite_order.SkewPolynomial_finite_order_dense - SkewPolynomialRing.__init__(self, base_ring, twist_map, name, sparse, category) - self._order = twist_map.order() - (self._constants, self._embed_constants) = twist_map.fixed_field() + SkewPolynomialRing.__init__(self, base_ring, morphism, derivation, name, sparse, category) + self._order = morphism.order() + (self._constants, self._embed_constants) = morphism.fixed_field() # Configure and create center self._center = {} @@ -1554,7 +750,7 @@ class SkewPolynomialRing_finite_field(SkewPolynomialRing_finite_order): Add methods related to center of skew polynomial ring, irreducibility, karatsuba multiplication and factorization. """ - def __init__(self, base_ring, twist_map, names, sparse, category=None): + def __init__(self, base_ring, morphism, derivation, names, sparse, category=None): """ This method is a constructor for a general, dense univariate skew polynomial ring over a finite field. @@ -1585,7 +781,7 @@ def __init__(self, base_ring, twist_map, names, sparse, category=None): if self.Element is None: import sage.rings.polynomial.skew_polynomial_finite_field self.Element = sage.rings.polynomial.skew_polynomial_finite_field.SkewPolynomial_finite_field_dense - SkewPolynomialRing_finite_order.__init__(self, base_ring, twist_map, names, sparse, category) + SkewPolynomialRing_finite_order.__init__(self, base_ring, morphism, derivation, names, sparse, category) self._matrix_retraction = None def _new_retraction_map(self, seed=None): @@ -1640,7 +836,6 @@ def _new_retraction_map(self, seed=None): tr += x elt *= k.gen() trace.append(section(tr)) - from sage.matrix.matrix_space import MatrixSpace self._matrix_retraction = MatrixSpace(kfixed, 1, k.degree())(trace) def _retraction(self, x, newmap=False, seed=None): From 52932e045db7fb9141a37469002ee8c710d80cb4 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 9 May 2020 01:02:04 +0200 Subject: [PATCH 02/16] fix typos --- src/sage/rings/polynomial/all.py | 2 +- .../rings/polynomial/ore_polynomial_ring.py | 11 +++--- .../polynomial/skew_polynomial_element.pyx | 36 +++++++++---------- .../skew_polynomial_finite_order.pyx | 6 ++-- .../rings/polynomial/skew_polynomial_ring.py | 26 +++++++------- 5 files changed, 40 insertions(+), 41 deletions(-) diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index eb1a17b7c97..4ea0d9761c0 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -47,7 +47,7 @@ # Ore Polynomial Rings from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing -from sage.rings.polynomial.skew_polynomial_ring import SkewPolynomialRing +SkewPolynomialRing = OrePolynomialRing # Evaluation of cyclotomic polynomials from sage.rings.polynomial.cyclotomic import cyclotomic_value diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 00bff43a8fd..5c968cf08da 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -87,6 +87,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): if derivation is None and morphism is None: return PolynomialRing(base_ring, names, sparse) + from sage.rings.polynomial import skew_polynomial_ring constructors = [ ] if derivation is None: if base_ring in Fields(): @@ -94,12 +95,12 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): order = morphism.order() if order is not Infinity: if base_ring.is_finite(): - constructors.append(sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field) + constructors.append(skew_polynomial_ring.SkewPolynomialRing_finite_field) else: - constructors.append(sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order) + constructors.append(skew_polynomial_ring.SkewPolynomialRing_finite_order) except (AttributeError, NotImplementedError): pass - constructors.append(sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing) + constructors.append(skew_polynomial_ring.SkewPolynomialRing) for constructor in constructors: try: @@ -431,7 +432,7 @@ def twisting_morphism(self, n=1): sage: S.twisting_morphism(-1) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t + 1 """ if self._morphism is not None: @@ -439,7 +440,7 @@ def twisting_morphism(self, n=1): return self._morphism ** n except TypeError as e: if n < 0: - raise NotImplementedError("inversion of the twisting morphism %s" % self._map) + raise NotImplementedError("inversion of the twisting morphism %s" % self._morphism) else: raise ValueError("Unexpected error in iterating the twisting morphism: %s", e) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 3b80e8e6aff..cee93bf4a1f 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -122,7 +122,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: a.left_power_mod(100,modulus) (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t """ - cdef OrePolynomial_generic_dense r + cdef SkewPolynomial_generic_dense r if not isinstance(exp, Integer): try: exp = Integer(exp) @@ -192,7 +192,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: a.right_power_mod(100,modulus) (2*t^2 + 3)*x^2 + (t^2 + 4*t + 2)*x + t^2 + 2*t + 1 """ - cdef OrePolynomial_generic_dense r + cdef SkewPolynomial_generic_dense r if not isinstance(exp, Integer): try: exp = Integer(exp) @@ -366,7 +366,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): ... TypeError: 1/t fails to convert into the map's domain Univariate Polynomial Ring in t over Rational Field, but a `pushforward` method is not properly implemented """ - cdef RingHomomorphism sigma = self._parent.twist_morphism() + cdef RingHomomorphism sigma = self._parent.twisting_morphism() cdef list coefficients = self.list() cdef RingElement ret = self.base_ring().zero() cdef RingElement a = eval_pt @@ -404,7 +404,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: b = a.conjugate(-1) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t + 1 Here is a working example:: @@ -419,7 +419,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: u*y == y*v True """ - r = self._new_c([self._parent.twist_morphism(n)(x) for x in self.list()], + r = self._new_c([self._parent.twisting_morphism(n)(x) for x in self.list()], self._parent, 0) return r @@ -481,8 +481,8 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): return self._parent.zero() cdef list x = (self)._coeffs cdef Py_ssize_t i - twist_morphism = self._parent._map - r = self._new_c([ (twist_morphism**i)(right)*x[i] for i from 0 <= i < len(x) ], + twisting_morphism = self._parent._morphism + r = self._new_c([ (twisting_morphism**i)(right)*x[i] for i from 0 <= i < len(x) ], self._parent, 0) return r @@ -557,9 +557,9 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): for k from 0 <= k <= dx+dy: start = 0 if k <= dy else k-dy end = k if k <= dx else dx - sum = x[start] * parent.twist_morphism(start)(y[k-start]) + sum = x[start] * parent.twisting_morphism(start)(y[k-start]) for i from start < i <= end: - sum += x[i] * parent.twist_morphism(i)(y[k-i]) + sum += x[i] * parent.twisting_morphism(i)(y[k-i]) coeffs.append(sum) r = self._new_c(coeffs, parent, 0) return r @@ -588,16 +588,16 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): elif d1 >= 0: for k from d1 < k <= d1+d2: start = 0 if k <= d2 else k-d2 - sum = x[start] * parent.twist_morphism(start)(y[k-start]) + sum = x[start] * parent.twisting_morphism(start)(y[k-start]) for i from start < i <= d1: - sum += x[i] * parent.twist_morphism(i)(y[k-i]) + sum += x[i] * parent.twisting_morphism(i)(y[k-i]) x.append(sum) for k from d1 >= k >= 0: start = 0 if k <= d2 else k-d2 end = k if k <= d1 else d1 - sum = x[start] * parent.twist_morphism(start)(y[k-start]) + sum = x[start] * parent.twisting_morphism(start)(y[k-start]) for i from start < i <= end: - sum += x[i] * parent.twist_morphism(i)(y[k-i]) + sum += x[i] * parent.twisting_morphism(i)(y[k-i]) x[k] = sum cdef void _inplace_pow(self, Py_ssize_t n): @@ -645,11 +645,11 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): parent = self._parent for i from da-db >= i >= 0: try: - c = parent.twist_morphism(-db)(inv*a[i+db]) + c = parent.twisting_morphism(-db)(inv*a[i+db]) for j from 0 <= j < db: - a[i+j] -= b[j] * parent.twist_morphism(j)(c) + a[i+j] -= b[j] * parent.twisting_morphism(j)(c) except Exception: - raise NotImplementedError("inversion of the twist map %s" % parent.twist_morphism()) + raise NotImplementedError("inversion of the twist map %s" % parent.twisting_morphism()) q.append(c) q.reverse() return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) @@ -675,9 +675,9 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): cdef list q = [ ] parent = self._parent for i from da-db >= i >= 0: - c = parent.twist_morphism(i)(inv) * a[i+db] + c = parent.twisting_morphism(i)(inv) * a[i+db] for j from 0 <= j < db: - a[i+j] -= c * parent.twist_morphism(i)(b[j]) + a[i+j] -= c * parent.twisting_morphism(i)(b[j]) q.append(c) q.reverse() return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index eed23b1640a..1e70849dfbd 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -157,7 +157,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): pol.append(l[k]) M.append(Polk(pol)) for i from 0 <= i <= d: - l[i] = self._parent.twist_morphism()(l[i]) + l[i] = self._parent.twisting_morphism()(l[i]) return matrix(Polk, r, r, M) @@ -216,12 +216,12 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): True """ order = self.parent()._order - twist_morphism = self.parent().twist_morphism() + twisting_morphism = self.parent().twisting_morphism() coeffs = [ ] for i in range(0, self.degree()+1, order): tr = c = self._coeffs[i] for _ in range(order-1): - tr = c + twist_morphism(tr) + tr = c + twisting_morphism(tr) coeffs.append(tr) if var is False: return tuple(coeffs) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 8ee501e21cf..3faff223f62 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -87,7 +87,7 @@ def _base_ring_to_fraction_field(S): else: Q = R.fraction_field() gens = R.gens() - sigmaS = S.twist_map() + sigmaS = S.twisting_morphism() # try: sigmaQ = Q.hom([Q(sigmaS(g)) for g in gens]) return Q[S.variable_name(), sigmaQ] @@ -132,7 +132,7 @@ def _minimal_vanishing_polynomial(R, eval_pts): if e.is_zero(): return R.one() else: - return R.gen() - R.twist_map()(e) / e + return R.gen() - R.twisting_morphism()(e) / e else: t = l // 2 A = eval_pts[:t] @@ -222,7 +222,7 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) - ``base_ring`` -- a commutative ring - - ``twist_map`` -- an automorphism of the base ring + - ``twisting_morphism`` -- an automorphism of the base ring - ``name`` -- string or list of strings representing the name of the variables of ring @@ -245,6 +245,7 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) if derivation is not None: raise NotImplementedError if self.Element is None: + import sage.rings.polynomial.skew_polynomial_element self.Element = sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense OrePolynomialRing.__init__(self, base_ring, morphism, None, name, sparse, category) @@ -255,7 +256,7 @@ def minimal_vanishing_polynomial(self, eval_pts): The degree of the vanishing polynomial is at most the length of ``eval_pts``. Equality holds if and only if the elements of ``eval_pts`` - are linearly independent over the fixed field of ``self.twist_map()``. + are linearly independent over the fixed field of ``self.twisting_morphism()``. - ``eval_pts`` -- list of evaluation points which are linearly independent over the fixed field of the twist map of the associated @@ -298,10 +299,10 @@ def lagrange_polynomial(self, points): More precisely, given `n` pairs `(x_1, y_1), ..., (x_n, y_n) \in R^2`, where `R` is ``self.base_ring()``, compute a skew polynomial `p(x)` such that `p(x_i) = y_i` for each `i`, under the condition that the `x_i` are - linearly independent over the fixed field of ``self.twist_map()``. + linearly independent over the fixed field of ``self.twisting_morphism()``. If the `x_i` are linearly independent over the fixed field of - ``self.twist_map()`` then such a polynomial is guaranteed to exist. + ``self.twisting_morphism()`` then such a polynomial is guaranteed to exist. Otherwise, it might exist depending on the `y_i`, but the algorithm used in this implementation does not support that, and so an error is always raised. @@ -310,7 +311,7 @@ def lagrange_polynomial(self, points): - ``points`` -- a list of pairs ``(x_1, y_1),..., (x_n, y_n)`` of elements of the base ring of ``self``. The `x_i` should be linearly - independent over the fixed field of ``self.twist_map()``. + independent over the fixed field of ``self.twisting_morphism()``. OUTPUT: @@ -335,7 +336,7 @@ def lagrange_polynomial(self, points): True If the `x_i` are linearly dependent over the fixed field of - ``self.twist_map()``, then an error is raised:: + ``self.twisting_morphism()``, then an error is raised:: sage: T.lagrange_polynomial([ (t, 1), (2*t, 3) ]) Traceback (most recent call last): @@ -819,10 +820,7 @@ def _new_retraction_map(self, seed=None): """ k = self.base_ring() base = k.base_ring() - (kfixed, embed) = self._maps[1].fixed_field() - section = embed.section() - if not kfixed.has_coerce_map_from(base): - raise NotImplementedError("No coercion map from %s to %s" % (base, kfixed)) + section = self._embed_constants.section() if seed is None: seed = k.random_element() self._seed_retraction = seed @@ -832,11 +830,11 @@ def _new_retraction_map(self, seed=None): x = elt tr = elt for _ in range(1, self._order): - x = self._map(x) + x = self._morphism(x) tr += x elt *= k.gen() trace.append(section(tr)) - self._matrix_retraction = MatrixSpace(kfixed, 1, k.degree())(trace) + self._matrix_retraction = MatrixSpace(self._constants, 1, k.degree())(trace) def _retraction(self, x, newmap=False, seed=None): """ From 31a3c074798ca0f70af9bd1c398e6ddff150e855 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 May 2020 00:55:28 +0200 Subject: [PATCH 03/16] add documentation --- .../polynomial/ore_polynomial_element.pyx | 460 +++++++++-------- .../rings/polynomial/ore_polynomial_ring.py | 487 +++++++++++++++--- .../polynomial/skew_polynomial_element.pyx | 40 +- .../skew_polynomial_finite_field.pyx | 2 +- .../skew_polynomial_finite_order.pyx | 2 +- .../rings/polynomial/skew_polynomial_ring.py | 59 ++- 6 files changed, 712 insertions(+), 338 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index d3c9e0cd7d1..df6f849ce68 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -1,56 +1,27 @@ r""" -Univariate Skew Polynomials +Univariate Ore Polynomials This module provides the :class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial`, -which constructs a single univariate skew polynomial over commutative -base rings and an automorphism over the base ring. Skew polynomials are -non-commutative and so principal methods such as gcd, lcm, monic, -multiplication, and division are given in left and right forms. +which constructs a single univariate Ore polynomial over a commutative +base equipped with an endomorphism and/or a derivation. +It provides generic implementation of standard arithmetical operations +on Ore polynomials as addition, multiplication, gcd, lcm, etc. -The generic implementation of dense skew polynomials is -:class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial_generic_dense`. +The generic implementation of dense Ore polynomials is +:class:`~sage.rings.polynomial.ore_polynomial_element.OrePolynomial_generic_dense`. The classes -:class:`~sage.rings.polynomial.skew_polynomial_element.ConstantOrePolynomialSection` -and :class:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomialBaseringInjection` -handle conversion from a skew polynomial ring to its base ring and vice versa respectively. - -.. WARNING:: - - The current semantics of - :meth:`~sage.rings.polynomial.skew_polynomial_element.OrePolynomial.__call__` - are experimental, so a warning is thrown when a skew polynomial is evaluated - for the first time in a session. See the method documentation for details. - - TESTS:: - - sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = 2*(t + x) + 1 - sage: a(t^2) - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. - See http://trac.sagemath.org/13215 for details. - 2*t^3 + 3*t^2 + 4*t + 2 - sage: a(t) - 2*t^2 + 3*t + 2 +:class:`~sage.rings.polynomial.ore_polynomial_element.ConstantOrePolynomialSection` +and :class:`~sage.rings.polynomial.ore_polynomial_element.OrePolynomialBaseringInjection` +handle conversion from a Ore polynomial ring to its base ring and vice versa. AUTHORS: -- Xavier Caruso (2012-06-29): initial version - -- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests and - refactored classes and methods - -- Johan Rosenkilde (2016-08-03): changes for bug fixes, docstring and - doctest errors - +- Xavier Caruso (2020-05) """ ############################################################################# -# Copyright (C) 2012 Xavier Caruso +# Copyright (C) 2020 Xavier Caruso # # This program is free software: you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -81,15 +52,16 @@ from sage.misc.superseded import experimental cdef class OrePolynomial(AlgebraElement): r""" - Abstract base class for skew polynomials. + Abstract base class for Ore polynomials. This class must be inherited from and have key methods overridden. .. RUBRIC:: Definition - Let `R` be a commutative ring equipped with an automorphism `\sigma`. + Let `R` be a commutative ring equipped with an automorphism `\sigma` + and a `\sigma`-derivation `\partial`. - Then, a skew polynomial is given by the equation: + A Ore polynomial is given by the equation: .. MATH:: @@ -97,13 +69,13 @@ cdef class OrePolynomial(AlgebraElement): where the coefficients `a_i \in R` and `X` is a formal variable. - Addition between two skew polynomials is defined by the usual addition + Addition between two Ore polynomials is defined by the usual addition operation and the modified multiplication is defined by the rule - `X a = \sigma(a) X` for all `a` in `R`. Skew polynomials are thus - non-commutative and the degree of a product is equal to the sum of the - degrees of the factors. + `X a = \sigma(a) X + \partial(a)` for all `a` in `R`. + Ore polynomials are thus non-commutative and the degree of a product + is equal to the sum of the degrees of the factors. - Let `a` and `b` be two skew polynomials in the same ring `S`. + Let `a` and `b` be two Ore polynomials in the same ring `S`. The *left (resp. right) euclidean division* of `a` by `b` is a couple `(q,r)` of elements in `S` such that @@ -121,30 +93,18 @@ cdef class OrePolynomial(AlgebraElement): in the *right* euclidean division exist and are unique. The same result holds for the *left* euclidean division if in addition - the twist map defining the skew polynomial ring is invertible. - - .. RUBRIC:: Evaluation - - The value of a given a skew polynomial `p(x) = \sum_{i=0}^d a_i x^i` - at `r` is calculated using the formula: - - .. MATH:: - - p(r) = \sum_{i=0}^d a_i \sigma^i(r) - - where `\sigma` is the base ring automorphism. This is called - the *operator evaluation* method. + the twisting morphism defining the Ore polynomial ring is invertible. EXAMPLES: We illustrate some functionalities implemented in this class. - We create the skew polynomial ring:: + We create the Ore polynomial ring (here the derivation is zero):: sage: R. = ZZ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma]; S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 and some elements in it:: @@ -176,7 +136,7 @@ cdef class OrePolynomial(AlgebraElement): sage: b^2 == b*b True - Sage also implements arithmetic over skew polynomial rings. You will find + Sage also implements arithmetic over Ore polynomial rings. You will find below a short panorama:: sage: q,r = c.right_quo_rem(b) @@ -196,12 +156,12 @@ cdef class OrePolynomial(AlgebraElement): True Left euclidean division won't work over our current `S` because Sage can't - invert the twist map:: + invert the twisting morphism:: sage: q,r = c.left_quo_rem(b) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring Defn: t |--> t + 1 Here we can see the effect of the operator evaluation compared to the usual @@ -209,6 +169,9 @@ cdef class OrePolynomial(AlgebraElement): sage: a = x^2 sage: a(t) + doctest:...: FutureWarning: This class/method/function is marked as experimental. + It, its functionality or its interface might change without a formal deprecation. + See http://trac.sagemath.org/13215 for details. t + 2 Here is a working example over a finite field:: @@ -236,8 +199,8 @@ cdef class OrePolynomial(AlgebraElement): sage: a.left_gcd(b) x + t - The left lcm has the following meaning: given skew polynomials `a` and `b`, - their left lcm is the least degree polynomial `c = ua = vb` for some skew + The left lcm has the following meaning: given Ore polynomials `a` and `b`, + their left lcm is the least degree polynomial `c = ua = vb` for some Ore polynomials `u, v`. Such a `c` always exist if the base ring is a field:: sage: c = a.left_lcm(b); c @@ -259,8 +222,7 @@ cdef class OrePolynomial(AlgebraElement): .. SEEALSO:: - - :mod:`sage.rings.polynomial.skew_polynomial_ring` - - :mod:`sage.rings.polynomial.skew_polynomial_ring_constructor` + - :mod:`sage.rings.polynomial.ore_polynomial_ring` """ def __init__(self, parent, construct=False): r""" @@ -306,7 +268,7 @@ cdef class OrePolynomial(AlgebraElement): r""" Return the degree of ``self``. - By convention, the zero skew polynomial has degree `-1`. + By convention, the zero Ore polynomial has degree `-1`. EXAMPLES:: @@ -324,7 +286,7 @@ cdef class OrePolynomial(AlgebraElement): cdef OrePolynomial _new_c(self, list coeffs, Parent P, char check=0): r""" - Fast creation of a new skew polynomial + Fast creation of a new Ore polynomial .. NOTE:: @@ -335,18 +297,18 @@ cdef class OrePolynomial(AlgebraElement): cpdef OrePolynomial _new_constant_poly(self, RingElement a, Parent P, char check=0): r""" - Fast creation of a new constant skew polynomial + Fast creation of a new constant Ore polynomial EXAMPLES:: - sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] sage: OrePolynomialBaseringInjection(k, k['x', Frob]) #indirect doctest - Skew Polynomial base injection morphism: + Ore Polynomial base injection morphism: From: Finite Field in t of size 5^3 - To: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + To: Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ if a: n = self._new_c([a],P,check) @@ -370,9 +332,9 @@ cdef class OrePolynomial(AlgebraElement): sage: a[1] = t + 1 Traceback (most recent call last): ... - IndexError: skew polynomials are immutable + IndexError: Ore polynomials are immutable """ - raise IndexError("skew polynomials are immutable") + raise IndexError("Ore polynomials are immutable") def square(self): r""" @@ -382,13 +344,18 @@ cdef class OrePolynomial(AlgebraElement): sage: R. = QQ[] sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] + sage: S. = R['x', sigma] sage: a = x + t; a x + t sage: a.square() x^2 + (2*t + 1)*x + t^2 sage: a.square() == a*a True + + sage: der = R.derivation() + sage: A. = R['d', der] + sage: (d + t).square() + d^2 + 2*t*d + t^2 + 1 """ return self * self @@ -426,14 +393,14 @@ cdef class OrePolynomial(AlgebraElement): """ cdef int d = self.degree() if d == -1: - raise ValueError("the skew polynomial must not be 0") + raise ValueError("the Ore polynomial must not be 0") return self[d] def is_unit(self): r""" - Return ``True`` if this skew polynomial is a unit. + Return ``True`` if this Ore polynomial is a unit. - When the base ring `R` is an integral domain, then a skew polynomial `f` + When the base ring `R` is an integral domain, then a Ore polynomial `f` is a unit if and only if degree of `f` is `0` and `f` is then a unit in `R`. @@ -459,22 +426,18 @@ cdef class OrePolynomial(AlgebraElement): else: return False else: - raise NotImplementedError("is_unit is not implemented for skew polynomial rings " + raise NotImplementedError("is_unit is not implemented for Ore polynomial rings " "over base rings which are not integral domains.") def is_nilpotent(self): r""" Check if ``self`` is nilpotent. - Given a commutative ring `R` and a base ring automorphism `\sigma` - of order `n`, an element `f` of `R[X, \sigma]` is nilpotent if - and only if all coefficients of `f^n` are nilpotent in `R`. - .. NOTE:: The paper "Nilpotents and units in skew polynomial rings over commutative rings" by M. Rimmer and K.R. Pearson describes - the method to check whether a given skew polynomial is nilpotent. + a method to check whether a given skew polynomial is nilpotent. That method however, requires one to know the order of the automorphism which is not available in Sage. This method is thus not yet implemented. @@ -493,7 +456,7 @@ cdef class OrePolynomial(AlgebraElement): def is_monic(self): r""" - Return ``True`` if this skew polynomial is monic. + Return ``True`` if this Ore polynomial is monic. The zero polynomial is by definition not monic. @@ -519,12 +482,12 @@ cdef class OrePolynomial(AlgebraElement): def left_monic(self): r""" - Return the unique monic skew polynomial `m` which divides ``self`` on - the left and has the same degree. + Return the unique monic Ore polynomial `m` which divides this + polynomial on the left and has the same degree. - Given a skew polynomial `p` of degree `n`, its left monic is given by - `m = p \sigma^{-n}(1/k)`, where `k` is the leading coefficient of - `p`, i.e. by the appropriate scalar multiplication on the right. + Given a Ore polynomial `P` of degree `n`, its left monic is given by + `P \cdot \sigma^{-n}(1/k)`, where `k` is the leading coefficient of + `P` and `\sigma` is the twisting morphism. EXAMPLES:: @@ -541,7 +504,7 @@ cdef class OrePolynomial(AlgebraElement): True sage: b.is_left_divisible_by(a) True - sage: twist = S.twist_map(-a.degree()) + sage: twist = S.twisting_morphism(-a.degree()) sage: a == b * twist(a.leading_coefficient()) True @@ -554,8 +517,8 @@ cdef class OrePolynomial(AlgebraElement): unit:: sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] + sage: der = R.derivation() + sage: S. = R['x', der] sage: a = t*x sage: a.left_monic() Traceback (most recent call last): @@ -575,12 +538,11 @@ cdef class OrePolynomial(AlgebraElement): def right_monic(self): r""" - Return the unique monic skew polynomial `m` which divides ``self`` on - the right and has the same degree. + Return the unique monic Ore polynomial which divides this polynomial + on the right and has the same degree. - Given a skew polynomial `p` of degree `n`, its left monic is given by - `m = (1/k) * p`, where `k` is the leading coefficient of `p`, i.e. by - the appropriate scalar multiplication on the left. + Given a Ore polynomial `P` of degree `n`, its left monic is given by + `(1/k) \cdot P`, where `k` is the leading coefficient of `p`. EXAMPLES:: @@ -609,8 +571,8 @@ cdef class OrePolynomial(AlgebraElement): unit:: sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] + sage: der = R.derivation() + sage: S. = R['x', der] sage: a = t*x sage: a.right_monic() Traceback (most recent call last): @@ -710,7 +672,7 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` OUTPUT: @@ -745,7 +707,7 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` OUTPUT: @@ -794,7 +756,7 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` OUTPUT: @@ -829,7 +791,7 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` OUTPUT: @@ -878,27 +840,27 @@ cdef class OrePolynomial(AlgebraElement): Return the left gcd of ``self`` and ``other`` along with the coefficients for the linear combination. - If `a` is ``self`` and `b` is ``other``, then there are skew polynomials + If `a` is ``self`` and `b` is ``other``, then there are Ore polynomials `u` and `v` such that `g = a u + b v`, where `g` is the left gcd of `a` and `b`. This method returns `(g, u, v)`. INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- boolean (default: ``True``). Return whether the left gcd should be normalized to be monic. OUTPUT: - - The left gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is + - The left gcd of ``self`` and ``other``, that is a Ore polynomial + `g` with the following property: any Ore polynomial is divisible on the left by `g` iff it is divisible on the left by both ``self`` and ``other``. If monic is ``True``, `g` is in addition monic. (With this extra condition, it is uniquely determined.) - - Two skew polynomials `u` and `v` such that: + - Two Ore polynomials `u` and `v` such that: .. MATH:: @@ -910,8 +872,8 @@ cdef class OrePolynomial(AlgebraElement): Works only if following two conditions are fulfilled (otherwise left gcd do not exist in general): - 1) the base ring is a field and 2) the twist map on - this field is bijective. + 1) the base ring is a field and + 2) the twisting morphism is bijective. EXAMPLES:: @@ -944,7 +906,7 @@ cdef class OrePolynomial(AlgebraElement): ... TypeError: the base ring must be a field - And the twist map must be bijective:: + And the twisting morphism must be bijective:: sage: FR = R.fraction_field() sage: f = FR.hom([FR(t)^2]) @@ -954,7 +916,7 @@ cdef class OrePolynomial(AlgebraElement): sage: a.left_xgcd(b) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 """ if self.base_ring() not in Fields: @@ -1004,17 +966,17 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` OUTPUT: - the quotient and the remainder of the left euclidean - division of this skew polynomial by ``other`` + division of this Ore polynomial by ``other`` .. NOTE:: This will fail if the leading coefficient of ``other`` is not a unit - or if Sage can't invert the twist map. + or if Sage can't invert the twisting morphism. EXAMPLES:: @@ -1028,7 +990,7 @@ cdef class OrePolynomial(AlgebraElement): True In the following example, Sage does not know the inverse - of the twist map:: + of the twisting morphism:: sage: R. = ZZ[] sage: sigma = R.hom([t+1]) @@ -1038,7 +1000,7 @@ cdef class OrePolynomial(AlgebraElement): sage: a.left_quo_rem(b) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring Defn: t |--> t + 1 """ if not other: @@ -1062,12 +1024,12 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` OUTPUT: - the quotient and the remainder of the left euclidean - division of this skew polynomial by ``other`` + division of this Ore polynomial by ``other`` .. NOTE:: @@ -1106,27 +1068,27 @@ cdef class OrePolynomial(AlgebraElement): Return the right gcd of ``self`` and ``other`` along with the coefficients for the linear combination. - If `a` is ``self`` and `b` is ``other``, then there are skew polynomials + If `a` is ``self`` and `b` is ``other``, then there are Ore polynomials `u` and `v` such that `g = u a + v b`, where `g` is the right gcd of `a` and `b`. This method returns `(g, u, v)`. INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- boolean (default: ``True``). Return whether the right gcd should be normalized to be monic. OUTPUT: - - The right gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is + - The right gcd of ``self`` and ``other``, that is a Ore polynomial + `g` with the following property: any Ore polynomial is divisible on the right by `g` iff it is divisible on the right by both ``self`` and ``other``. If monic is ``True``, `g` is in addition monic. (With this extra condition, it is uniquely determined.) - - Two skew polynomials `u` and `v` such that: + - Two Ore polynomials `u` and `v` such that: .. MATH:: @@ -1203,15 +1165,15 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- boolean (default: ``True``). Return whether the right gcd should be normalized to be monic. OUTPUT: - The right gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is + The right gcd of ``self`` and ``other``, that is a Ore polynomial + `g` with the following property: any Ore polynomial is divisible on the right by `g` iff it is divisible on the right by both ``self`` and ``other``. If monic is ``True``, `g` is in addition monic. (With this @@ -1268,15 +1230,15 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- boolean (default: ``True``). Return whether the left gcd should be normalized to be monic. OUTPUT: - The left gcd of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial is + The left gcd of ``self`` and ``other``, that is a Ore polynomial + `g` with the following property: any Ore polynomial is divisible on the left by `g` iff it is divisible on the left by both ``self`` and ``other``. If monic is ``True``, `g` is in addition monic. (With this @@ -1284,10 +1246,10 @@ cdef class OrePolynomial(AlgebraElement): .. NOTE:: - Works only if two following conditions are fulfilled + Works only if following two conditions are fulfilled (otherwise left gcd do not exist in general): - 1) the base ring is a field and 2) the twist map on - this field is bijective. + 1) the base ring is a field and + 2) the twisting morphism is bijective. EXAMPLES:: @@ -1316,7 +1278,7 @@ cdef class OrePolynomial(AlgebraElement): ... TypeError: the base ring must be a field - And the twist map needs to be bijective:: + And the twisting morphism needs to be bijective:: sage: FR = R.fraction_field() sage: f = FR.hom([FR(t)^2]) @@ -1326,7 +1288,7 @@ cdef class OrePolynomial(AlgebraElement): sage: a.left_gcd(b) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 """ if self.base_ring() not in Fields: @@ -1345,15 +1307,15 @@ cdef class OrePolynomial(AlgebraElement): cdef OrePolynomial _left_lcm_cofactor(self, OrePolynomial other): r""" - Return a skew polynomial `U` such that `U P = c L` - where `P` is this skew polynomial (``self``), `L` + Return a Ore polynomial `U` such that `U P = c L` + where `P` is this Ore polynomial (``self``), `L` is the left lcm of `P` and ``other`` and `c` is a constant TESTS:: sage: cython(''' - ....: from sage.rings.polynomial.skew_polynomial_element cimport OrePolynomial + ....: from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial ....: def left_lcm_cofactor(OrePolynomial P, OrePolynomial Q): ....: return P._left_lcm_cofactor(Q) ....: ''') @@ -1386,7 +1348,7 @@ cdef class OrePolynomial(AlgebraElement): def left_xlcm(self, other, monic=True): r""" Return the left lcm `L` of ``self`` and ``other`` together - with two skew polynomials `U` and `V` such that + with two Ore polynomials `U` and `V` such that .. MATH:: @@ -1422,15 +1384,15 @@ cdef class OrePolynomial(AlgebraElement): cdef OrePolynomial _right_lcm_cofactor(self, OrePolynomial other): r""" - Return a skew polynomial `U` such that `P U = L c` - where `P` is this skew polynomial (``self``), `L` + Return a Ore polynomial `U` such that `P U = L c` + where `P` is this Ore polynomial (``self``), `L` is the right lcm of `P` and ``other`` and `c` is a constant TESTS:: sage: cython(''' - ....: from sage.rings.polynomial.skew_polynomial_element cimport OrePolynomial + ....: from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial ....: def right_lcm_cofactor(OrePolynomial P, OrePolynomial Q): ....: return P._right_lcm_cofactor(Q) ....: ''') @@ -1463,7 +1425,7 @@ cdef class OrePolynomial(AlgebraElement): def right_xlcm(self, other, monic=True): r""" Return the right lcm `L` of ``self`` and ``other`` together - with two skew polynomials `U` and `V` such that + with two Ore polynomials `U` and `V` such that .. MATH:: @@ -1471,7 +1433,7 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- a boolean (default: ``True``); whether the right lcm should be normalized to be monic @@ -1499,7 +1461,7 @@ cdef class OrePolynomial(AlgebraElement): cdef OrePolynomial L = self * V1 if monic: s = self.base_ring()(~L.leading_coefficient()) - s = self._parent.twist_map(-L.degree())(s) + s = self._parent.twisting_morphism(-L.degree())(s) L = L * s V1 = V1 * s W1, _ = L._left_quo_rem(other) @@ -1513,15 +1475,15 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- boolean (default: ``True``). Return whether the left lcm should be normalized to be monic. OUTPUT: - The left lcm of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial divides + The left lcm of ``self`` and ``other``, that is a Ore polynomial + `g` with the following property: any Ore polynomial divides `g` on the *right* iff it divides both ``self`` and ``other`` on the *right*. If monic is ``True``, `g` is in addition monic. (With this @@ -1581,15 +1543,15 @@ cdef class OrePolynomial(AlgebraElement): INPUT: - - ``other`` -- a skew polynomial in the same ring as ``self`` + - ``other`` -- a Ore polynomial in the same ring as ``self`` - ``monic`` -- boolean (default: ``True``). Return whether the right lcm should be normalized to be monic. OUTPUT: - The right lcm of ``self`` and ``other``, that is a skew polynomial - `g` with the following property: any skew polynomial divides + The right lcm of ``self`` and ``other``, that is a Ore polynomial + `g` with the following property: any Ore polynomial divides `g` on the *left* iff it divides both ``self`` and ``other`` on the *left*. If monic is ``True``, `g` is in addition monic. (With this @@ -1599,7 +1561,7 @@ cdef class OrePolynomial(AlgebraElement): Works only if two following conditions are fulfilled (otherwise right lcm do not exist in general): - 1) the base ring is a field and 2) the twist map on + 1) the base ring is a field and 2) the twisting morphism on this field is bijective. EXAMPLES:: @@ -1636,7 +1598,7 @@ cdef class OrePolynomial(AlgebraElement): ... TypeError: the base ring must be a field - And the twist map needs to be bijective:: + And the twisting morphism needs to be bijective:: sage: FR = R.fraction_field() sage: f = FR.hom([FR(t)^2]) @@ -1646,7 +1608,7 @@ cdef class OrePolynomial(AlgebraElement): sage: a.right_lcm(b) Traceback (most recent call last): ... - NotImplementedError: inversion of the twist map Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 """ if self.base_ring() not in Fields: @@ -1660,12 +1622,12 @@ cdef class OrePolynomial(AlgebraElement): def _repr_(self, name=None): r""" - Return string representation of this skew polynomial. + Return string representation of this Ore polynomial. INPUT: - ``name`` -- the name of the variable (default: the - name given when the skew polynomial ring was created) + name given when the Ore polynomial ring was created) EXAMPLES:: @@ -1710,12 +1672,12 @@ cdef class OrePolynomial(AlgebraElement): def _latex_(self, name=None): r""" - Return a latex representation of this skew polynomial. + Return a latex representation of this Ore polynomial. INPUT: - ``name`` -- the name of the variable (default: the - name given when the skew polynomial ring was created) + name given when the Ore polynomial ring was created) EXAMPLES:: @@ -1886,9 +1848,9 @@ cdef class OrePolynomial(AlgebraElement): r""" Change the name of the variable of ``self``. - This will create the skew polynomial ring with the new name but same - base ring and twist map. The returned skew polynomial will be an element - of that skew polynomial ring. + This will create the Ore polynomial ring with the new name but same + base ring, twisting morphism and twisting derivation. The returned + Ore polynomial will be an element of that Ore polynomial ring. INPUT: @@ -1906,7 +1868,7 @@ cdef class OrePolynomial(AlgebraElement): Note that a new parent is created at the same time:: sage: b.parent() - Skew Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring + Ore Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 """ R = self._parent.change_var(var) @@ -2027,7 +1989,7 @@ cdef class OrePolynomial(AlgebraElement): r""" Return a "copy" of ``self``. - In Sage, since skew polynomials are immutable, this just returns + In Sage, since Ore polynomials are immutable, this just returns ``self`` again. EXAMPLES:: @@ -2224,7 +2186,19 @@ cdef class OrePolynomial(AlgebraElement): cdef void lmul_gen(list A, Morphism m, d): r""" - Helper function + If ``A`` is the list of coefficients of a Ore polynomial ``P``, + replace it by the list of coefficients of ``X*P`` (where ``X`` + is the variable in the Ore polynomial ring). + + This is an helper function. + + INPUT: + + - ``A`` -- a list of coefficients + + - ``m`` -- the twisting morphism of the Ore polynomial ring + + - ``d`` -- the twisting derivation of the Ore polynomial ring """ if m is None: A.append(A[-1]) @@ -2238,12 +2212,12 @@ cdef void lmul_gen(list A, Morphism m, d): cdef class OrePolynomial_generic_dense(OrePolynomial): r""" - Generic implementation of dense skew polynomial supporting any valid base - ring and twist map. + Generic implementation of dense Ore polynomial supporting any valid base + ring, twisting morphism and twisting derivation. """ def __init__(self, parent, x=None, int check=1, int construct=0, **kwds): r""" - Construct a skew polynomial over the given parent with the given + Construct a Ore polynomial over the given parent with the given coefficients. INPUT: @@ -2262,12 +2236,12 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] - We create a skew polynomial from a list:: + We create a Ore polynomial from a list:: sage: S([t,1]) x + t - from another skew polynomial:: + from another Ore polynomial:: sage: S(x^2 + t) x^2 + t @@ -2327,7 +2301,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): def __reduce__(self): r""" - Return the generic dense skew polynomial corresponding to the + Return the generic dense Ore polynomial corresponding to the current parameters provided ``self``. EXAMPLES:: @@ -2375,11 +2349,11 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef OrePolynomial _new_c(self, list coeffs, Parent P, char check=0): r""" - Fast creation of a new skew polynomial given a list of coefficients. + Fast creation of a new Ore polynomial given a list of coefficients. .. WARNING:: - The list ``coeffs`` is stored internally in the newly created skew + The list ``coeffs`` is stored internally in the newly created Ore polynomial, so this must not be modified after calling this method. TESTS:: @@ -2408,7 +2382,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma]; S #indirect doctest - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 """ cdef list x = self._coeffs cdef Py_ssize_t n = len(x) - 1 @@ -2418,7 +2392,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cpdef _richcmp_(left, right, int op): r""" - Compare the two skew polynomials ``self`` and ``other``. + Compare the two Ore polynomials ``self`` and ``other``. We order polynomials first by degree, then in dictionary order starting with the coefficient of largest degree. @@ -2536,7 +2510,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): r""" Return the degree of ``self``. - By convention, the zero skew polynomial has degree `-1`. + By convention, the zero Ore polynomial has degree `-1`. EXAMPLES:: @@ -2636,7 +2610,7 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): r""" Return the minimal degree of a non-zero monomial of ``self``. - By convention, the zero skew polynomial has valuation `+\infty`. + By convention, the zero Ore polynomial has valuation `+\infty`. EXAMPLES:: @@ -2700,19 +2674,87 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): return BA cpdef _lmul_(self, Element s): + r""" + Return the product ``self * right``. + + INPUT: + + - ``right`` -- an element of the base ring + + EXAMPLES:: + + sage: R. = QQ[] + sage: der = R.derivation() + sage: A. = R['d', der] + sage: d*t # indirect doctest + t*d + 1 + """ cdef coeffs = self._mul_list([s]) return self._new_c(coeffs, self._parent, 1) cpdef _rmul_(self, Element s): + r""" + Return the product ``left * self``. + + INPUT: + + - ``left`` -- an element of the base ring + + EXAMPLES:: + + sage: R. = QQ[] + sage: der = R.derivation() + sage: A. = R['d', der] + sage: t*(d + 1) # indirect doctest + t*d + t + """ return self._new_c([ s*c for c in self._coeffs ], self._parent) cpdef _mul_(self, other): + r""" + Return the product ``self * right``. + + INPUT: + + - ``right`` -- a Ore polynomial in the same ring as ``self`` + + EXAMPLES:: + + sage: R. = QQ[] + sage: der = R.derivation() + sage: A. = R['d', der] + sage: P = d^2 + t + sage: Q = d^2 + (t + 1)*d + sage: P * Q + d^4 + (t + 1)*d^3 + (t + 2)*d^2 + (t^2 + t)*d + sage: P * Q == Q * P + False + + TESTS:: + + We check associativity and distributivity:: + + sage: U = A.random_element(degree=10) + sage: V = A.random_element(degree=10) + sage: W = A.random_element(degree=10) + sage: U * (V * W) == (U * V) * W + True + sage: U * (V + W) == U*V + U*W + True + sage: (U + V) * W == U*W + V*W + True + """ cdef coeffs = list((other)._coeffs) if coeffs: coeffs = self._mul_list(coeffs) return self._new_c(coeffs, self._parent, 1) cdef _left_quo_rem(self, OrePolynomial other): + r""" + Return the quotient and remainder of the left euclidean + division of ``self`` by ``other`` (C implementation). + """ + sig_check() cdef list A = list(self._coeffs) cdef Py_ssize_t degB = other.degree() cdef Morphism m = self._parent.twisting_morphism(-degB) @@ -2737,6 +2779,11 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): return self._new_c(quo, self._parent), self._new_c(A[:degB], self._parent, 1) cdef _right_quo_rem(self, OrePolynomial other): + r""" + Return the quotient and remainder of the right euclidean + division of ``self`` by ``other`` (C implementation). + """ + sig_check() cdef list A = list(self._coeffs) cdef Py_ssize_t i, j cdef Py_ssize_t degB = other.degree() @@ -2796,37 +2843,37 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): cdef class ConstantOrePolynomialSection(Map): r""" - Representation of the canonical homomorphism from the constants of a skew + Representation of the canonical homomorphism from the constants of a Ore polynomial ring to the base ring. - This class is necessary for automatic coercion from zero-degree skew + This class is necessary for automatic coercion from zero-degree Ore polynomial ring into the base ring. EXAMPLES:: - sage: from sage.rings.polynomial.skew_polynomial_element import ConstantOrePolynomialSection + sage: from sage.rings.polynomial.ore_polynomial_element import ConstantOrePolynomialSection sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] sage: m = ConstantOrePolynomialSection(S, R); m Generic map: - From: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + From: Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 To: Univariate Polynomial Ring in t over Rational Field """ cpdef Element _call_(self, x): r""" Return the corresponding element of the base ring if ``self`` is a - constant skew polynomial. Otherwise, it fails. + constant Ore polynomial. Otherwise, it fails. TESTS:: - sage: from sage.rings.polynomial.skew_polynomial_element import ConstantOrePolynomialSection + sage: from sage.rings.polynomial.ore_polynomial_element import ConstantOrePolynomialSection sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] sage: m = ConstantOrePolynomialSection(S, R); m Generic map: - From: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + From: Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 To: Univariate Polynomial Ring in t over Rational Field sage: m(S([0,1])-S([0,1])) 0 @@ -2848,7 +2895,7 @@ cdef class ConstantOrePolynomialSection(Map): cdef class OrePolynomialBaseringInjection(Morphism): r""" - Representation of the canonical homomorphism from a ring `R` into a skew + Representation of the canonical homomorphism from a ring `R` into a Ore polynomial ring over `R`. This class is necessary for automatic coercion from the base ring to the Ore @@ -2864,11 +2911,10 @@ cdef class OrePolynomialBaseringInjection(Morphism): sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma] sage: S.coerce_map_from(S.base_ring()) #indirect doctest - Skew Polynomial base injection morphism: + Ore Polynomial base injection morphism: From: Univariate Polynomial Ring in t over Rational Field - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + To: Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 """ - def __init__(self, domain, codomain): r""" Construct a Skew Polynomial Basering Injection. @@ -2877,27 +2923,27 @@ cdef class OrePolynomialBaseringInjection(Morphism): - ``domain`` -- a ring `R`. This will be the domain of the injection. - - ``codomain`` -- a skew polynomial ring over ``domain``. This will be + - ``codomain`` -- a Ore polynomial ring over ``domain``. This will be the codomain. TESTS:: - sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] sage: OrePolynomialBaseringInjection(k, k['x', Frob]) - Skew Polynomial base injection morphism: + Ore Polynomial base injection morphism: From: Finite Field in t of size 5^3 - To: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + To: Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: R. = QQ[] sage: OrePolynomialBaseringInjection(QQ, k['x', Frob]) Traceback (most recent call last): ... - AssertionError: the domain of the injection must be the base ring of the skew polynomial ring + AssertionError: the domain of the injection must be the base ring of the Ore polynomial ring """ assert codomain.base_ring() is domain, \ - "the domain of the injection must be the base ring of the skew polynomial ring" + "the domain of the injection must be the base ring of the Ore polynomial ring" Morphism.__init__(self, Hom(domain,codomain)) self._an_element = codomain.gen() self._repr_type_str = "Ore Polynomial base injection" @@ -2909,7 +2955,7 @@ cdef class OrePolynomialBaseringInjection(Morphism): EXAMPLES:: - sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -2921,7 +2967,7 @@ cdef class OrePolynomialBaseringInjection(Morphism): cpdef Element _call_(self, e): r""" - Return the corresponding skew polynomial to the element from the + Return the corresponding Ore polynomial to the element from the base ring according to ``self``. INPUT: @@ -2930,11 +2976,11 @@ cdef class OrePolynomialBaseringInjection(Morphism): OUTPUT: - The skew polynomial corresponding to `e` according to ``self``. + The Ore polynomial corresponding to `e` according to ``self``. TESTS:: - sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] @@ -2942,7 +2988,7 @@ cdef class OrePolynomialBaseringInjection(Morphism): sage: m(4) 4 sage: parent(m(4)) - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ try: return self._codomain._element_constructor_(e) @@ -2951,19 +2997,19 @@ cdef class OrePolynomialBaseringInjection(Morphism): def section(self): r""" - Return the canonical homomorphism from the constants of a skew + Return the canonical homomorphism from the constants of a Ore polynomial ring to the base ring according to ``self``. TESTS:: - sage: from sage.rings.polynomial.skew_polynomial_element import OrePolynomialBaseringInjection + sage: from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] sage: m = OrePolynomialBaseringInjection(k, k['x', Frob]) sage: m.section() Generic map: - From: Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + From: Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 To: Finite Field in t of size 5^3 """ return ConstantOrePolynomialSection(self._codomain, self.domain()) diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 5c968cf08da..9d97dfb5220 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -1,6 +1,10 @@ r""" Univariate Ore Polynomial Rings +This module provides the :class:`~sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing`. +which constructs a general dense univariate Ore polynomial ring over a commutative base with +equipped with an endomorphism and/or a derivation. + AUTHOR: - Xavier Caruso (2020-04) @@ -44,14 +48,249 @@ WORKING_CENTER_MAX_TRIES = 1000 -# Generic implementation of skew polynomial rings +# Generic implementation of Ore polynomial rings ################################################# class OrePolynomialRing(Algebra, UniqueRepresentation): + r""" + Construct and return the globally unique Ore polynomial ring with the + given properties and variable names. + + Given a ring `R` and a ring automorphism `\sigma` of `R` and a + `\sigma`-derivation `\partial`, the ring of Ore polynomials + `R[X; \sigma, \partial]` is the usual abelian group polynomial + `R[X]` equipped with the modification multiplication deduced from the + rule `X a = \sigma(a) X + \partial(a)`. + We refer to [Ore1933]_ for more material on Ore polynomials. + + INPUT: + + - ``base_ring`` -- a commutative ring + + - ``twisting_map`` -- either an endomorphism of the base ring, or + a (twisted) derivation of it + + - ``names`` -- a string or a list of strings + + - ``sparse`` -- a boolean (default: ``False``). Currently not supported. + + EXAMPLES: + + THE CASE OF A TWISTING ENDOMORPHISM: + + We create the Ore ring `\FF_{5^3}[x, \text{Frob}]` where Frob is the + Frobenius endomorphism. + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S = OrePolynomialRing(k, Frob, 'x') + sage: S + Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 + + In particular, observe that it is not needed to create and pass in + the twisting derivation (which is `0` in our example). + + As a shortcut, we can use the square brackets notation as follow:: + + sage: T. = k['x', Frob] + sage: T + Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 + sage: T is S + True + + We emphasize that it is necessary to repeat the name of the variable + in the right hand side. Indeed, the following fails (it is interpreted + by Sage as a classical polynomial ring with variable name ``Frob``). + + sage: T. = k[Frob] + Traceback (most recent call last): + ... + ValueError: variable name 'Frobenius endomorphism a |--> a^5 on Finite Field in a of size 5^3' is not alphanumeric + + Note moreover that, similarly to the classical case, using the brackets + notation also sets the variable:: + + sage: x.parent() is S + True + + We are now ready to carry on computations in the Ore ring:: + + sage: x*a + (2*a^2 + 4*a + 4)*x + sage: Frob(a)*x + (2*a^2 + 4*a + 4)*x + + THE CASE OF A TWISTING DERIVATION: + + We can similarly create the Ore ring of differential operators over + `\QQ[t]`, namely `\QQ[t][d, \frac{d}{dt}]`:: + + sage: R. = QQ[] + sage: der = R.derivation(); der + d/dt + sage: A = OrePolynomialRing(R, der, 'd') + sage: A + Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt + + Again, the brackets notation is available:: + + sage: B. = R['d', der] + sage: A is B + True + + and computations can be carried out:: + + sage: d*t + t*d + 1 + + THE COMBINED CASE: + + Ore polynomial rings involving at the same time a twisting morphism + `sigma` and a twisting `sigma`-derivation can be created as well as + follows:: + + sage: F. = Qq(3^2) + sage: sigma = F.frobenius_endomorphism(); sigma + Frobenius endomorphism on 3-adic Unramified Extension Field in u defined by x^2 + 2*x + 2 lifting u |--> u^3 on the residue field + sage: der = F.derivation(3, twist=sigma); der + (3 + O(3^21))*([Frob] - id) + + sage: M. = F['X', der] + sage: M + Ore Polynomial Ring in X over 3-adic Unramified Extension Field in u defined by x^2 + 2*x + 2 twisted by Frob and (3 + O(3^21))*([Frob] - id) + + We emphasize that we only need to pass in the twisted derivation as + it already contains in it the datum of the twisting endomorphism. + Actually, passing in both twisting maps results in an error:: + + sage: F['X', sigma, der] + Traceback (most recent call last): + ... + ValueError: variable name 'Frobenius endomorphism ...' is not alphanumeric + + EXAMPLES of VARIABLE NAME CONTEXT:: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: S. = SkewPolynomialRing(R, sigma); S + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring + twisted by t |--> t + 1 + + The names of the variables defined above cannot be arbitrarily + modified because each Ore polynomial ring is unique in Sage and other + objects in Sage could have pointers to that Ore polynomial ring. + + However, the variable can be changed within the scope of a ``with`` + block using the localvars context:: + + sage: with localvars(S, ['y']): + ....: print(S) + Ore Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring + twisted by t |--> t + 1 + + UNIQUENESS and IMMUTABILITY: + + In Sage, there is exactly one Ore polynomial ring for each quadruple + (base ring, twisting morphism, twisting derivation, name of the variable). + + sage: k. = GF(7^3) + sage: Frob = k.frobenius_endomorphism() + sage: S = k['x', Frob] + sage: T = k['x', Frob] + sage: S is T + True + + Rings with different variables names are different:: + + sage: S is k['y', Frob] + False + + Similarly, varying the twisting morphisms yields to different Ore rings + (expect when the morphism coincide):: + + sage: S is k['x', Frob^2] + False + sage: S is k['x', Frob^3] + False + sage: S is k['x', Frob^4] + True + + TESTS: + + You must specify a variable name:: + + sage: SkewPolynomialRing(k, Frob) + Traceback (most recent call last): + ... + TypeError: you must specify the name of the variable + + Multivariate Ore polynomial rings are not supported:: + + sage: S = OrePolynomialRing(k, Frob,names=['x','y']) + Traceback (most recent call last): + ... + NotImplementedError: multivariate Ore polynomials rings not supported + + Sparse Ore polynomial rings are not implemented:: + + sage: S = SkewPolynomialRing(k, Frob, names='x', sparse=True) + Traceback (most recent call last): + ... + NotImplementedError: sparse Ore polynomial rings are not implemented + + Saving and loading of polynomial rings works:: + + sage: loads(dumps(S)) is S + True + + .. TODO:: + + - Sparse Ore Polynomial Ring + - Multivariate Ore Polynomial Ring + """ Element = None @staticmethod def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): + r""" + Construct the Ore polynomial ring associated to the given parameters. + + TESTS:: + + sage: R. = QQ[] + sage: der = R.derivation() + sage: A. = OrePolynomialRing(R, der) + sage: A + Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt + sage: type(A) + + + We check the uniqueness property of parents:: + + sage: der2 = R.derivation() + sage: B. = SkewPolynomialRing(R, der2) + sage: A is B + True + + When there is no twisting derivation, a special class is used:: + + sage: k. = ZZ[] + sage: theta = k.hom([t+1]) + sage: S. = SkewPolynomialRing(k, theta) + sage: S + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + sage: type(S) + + + In certain situations (e.g. when the twisting morphism is the Frobenius + over a finite field), even more specialized classes are used:: + + sage: k. = GF(7^5) + sage: Frob = k.frobenius_endomorphism(2) + sage: S. = SkewPolynomialRing(k, Frob) + sage: type(S) + + """ if base_ring not in CommutativeRings(): raise TypeError('base_ring must be a commutative ring') if isinstance(twist, Morphism): @@ -73,19 +312,22 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): else: derivation = None else: - raise TypeError("the twist map must be a ring morphism or a derivation") - if sparse: - raise NotImplementedError("sparse skew polynomial rings are not implemented") + raise TypeError("the twisting map must be a ring morphism or a derivation") if names is None: raise TypeError("you must specify the name of the variable") try: names = normalize_names(1, names)[0] except IndexError: - raise NotImplementedError("multivariate skew polynomials rings not supported") + raise NotImplementedError("multivariate Ore polynomials rings not supported") - # We find the best constructor + # If there is no twisting morphism and no twisting derivation + # we return a classical polynomial ring if derivation is None and morphism is None: - return PolynomialRing(base_ring, names, sparse) + return PolynomialRing(base_ring, names, sparse=sparse) + + # We find the best constructor + if sparse: + raise NotImplementedError("sparse Ore polynomial rings are not implemented") from sage.rings.polynomial import skew_polynomial_ring constructors = [ ] @@ -119,7 +361,9 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) - ``base_ring`` -- a commutative ring - - ``twisting_morphism`` -- an automorphism of the base ring + - ``morphism`` -- an automorphism of the base ring + + - ``derivation`` -- a derivation or a twisted derivation of the base ring - ``name`` -- string or list of strings representing the name of the variables of ring @@ -150,7 +394,7 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) def _element_constructor_(self, a=None, check=True, construct=False, **kwds): r""" Convert a base ring element ``a`` into a constant of this univariate - skew polynomial ring, possibly non-canonically. + Ore polynomial ring, possibly non-canonically. INPUT: @@ -163,7 +407,7 @@ def _element_constructor_(self, a=None, check=True, construct=False, **kwds): OUTPUT: - An zero-degree skew polynomial in ``self``, equal to ``a``. + An zero-degree Ore polynomial in ``self``, equal to ``a``. EXAMPLES:: @@ -221,13 +465,13 @@ def _coerce_map_from_base_ring(self): EXAMPLES:: sage: R. = ZZ[] - sage: S. = SkewPolynomialRing(R, R.hom([t + 1])) + sage: S. = OrePolynomialRing(R, R.hom([t + 1])) sage: S.coerce_map_from(R) - Skew Polynomial base injection morphism: + Ore Polynomial base injection morphism: From: Univariate Polynomial Ring in t over Integer Ring - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + To: Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 sage: x.parent() - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 sage: t.parent() Univariate Polynomial Ring in t over Integer Ring sage: y = x + t # indirect doctest @@ -248,7 +492,7 @@ def _coerce_map_from_(self, P): - any ring that canonically coerces to the base ring of this ring - - skew polynomial rings in the same variable and automorphism over + - Ore polynomial rings in the same variable and automorphism over any base ring that canonically coerces to the base ring of this ring INPUT: @@ -259,7 +503,7 @@ def _coerce_map_from_(self, P): sage: R. = ZZ[] sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) + sage: S. = OrePolynomialRing(R,sigma) sage: S.has_coerce_map_from(S) True sage: S.has_coerce_map_from(R) @@ -272,16 +516,16 @@ def _coerce_map_from_(self, P): sage: S.coerce_map_from(ZZ) Composite map: From: Integer Ring - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + To: Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 Defn: Polynomial base injection morphism: From: Integer Ring To: Univariate Polynomial Ring in t over Integer Ring then - Skew Polynomial base injection morphism: + Ore Polynomial base injection morphism: From: Univariate Polynomial Ring in t over Integer Ring - To: Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + To: Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 sage: S.coerce_map_from(S) - Identity endomorphism of Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + Identity endomorphism of Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 """ base_ring = self.base_ring() try: @@ -300,20 +544,27 @@ def _repr_(self): r""" Return a string representation of ``self``. + The locution *Ore Polynomial Ring* is used when the twisting + derivation is zero. + Otherwise the locution *Ore Polynomial Ring* is used. + EXAMPLES:: - sage: R. = ZZ[] + sage: R. = QQ[] sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) + sage: S. = OrePolynomialRing(R, sigma) sage: S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + + sage: der = R.derivation() + sage: T. = OrePolynomialRing(R, der) + sage: T + Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt """ + s = "Ore Polynomial Ring in %s over %s twisted by " % (self.variable_name(), self.base_ring()) if self._derivation is None: - s = "Skew Polynomial Ring in %s over %s twisted by %s" % (self.variable_name(), - self.base_ring(), - self._morphism._repr_short()) + s += self._morphism._repr_short() else: - s = "Ore Polynomial Ring in %s over %s twisted by " % (self.variable_name(), self.base_ring()) if self._morphism is not None: s += "%s and " % self._morphism._repr_short() s += self._derivation._repr_() @@ -327,16 +578,20 @@ def _latex_(self): EXAMPLES:: - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = SkewPolynomialRing(R,sigma) - sage: latex(S) - \Bold{Z}[t][x,\begin{array}{l} - \text{\texttt{Ring{ }endomorphism...}} - \end{array}] + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x', Frob] + sage: latex(S) # indirect doctest + \Bold{F}_{5^{3}}\left[x ; a \mapsto a^{5} \right] + + sage: R. = QQ[] + sage: der = R.derivation() + sage: T. = R['d', der] + sage: latex(T) # indirect doctest + \Bold{Q}[t]\left[d ; \text{\texttt{d/dt}} \right] """ from sage.misc.latex import latex - s = "%s\\left[%s" % latex(self.base_ring(), self._latex_variable_names()[0]) + s = "%s\\left[%s" % (latex(self.base_ring()), self.latex_variable_names()[0]) sep = ";" if self._morphism is not None: s += sep + latex(self._morphism) @@ -347,8 +602,8 @@ def _latex_(self): def change_var(self, var): r""" - Return the skew polynomial ring in variable ``var`` with the same base - ring and twist map as ``self``. + Return the Ore polynomial ring in variable ``var`` with the same base + ring, twisting morphism and twisting derivation as ``self``. INPUT: @@ -362,10 +617,10 @@ def change_var(self, var): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: R. = SkewPolynomialRing(k,Frob); R - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: R. = OrePolynomialRing(k,Frob); R + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: Ry = R.change_var('y'); Ry - Skew Polynomial Ring in y over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in y over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: Ry is R.change_var('y') True """ @@ -398,7 +653,8 @@ def characteristic(self): @cached_method def twisting_morphism(self, n=1): r""" - Return the twisting endomorphism of the base ring iterated ``n`` times. + Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times + or ``None`` if this Ore polynomial ring is not twisted by an endomorphism. INPUT: @@ -408,7 +664,7 @@ def twisting_morphism(self, n=1): sage: R. = QQ[] sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] + sage: S. = R['x', sigma] sage: S.twisting_morphism() Ring endomorphism of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t + 1 @@ -419,7 +675,7 @@ def twisting_morphism(self, n=1): Defn: t |--> t + 10 If ``n`` in negative, Sage tries to compute the inverse of the - twist map:: + twisting morphism:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -427,13 +683,38 @@ def twisting_morphism(self, n=1): sage: T.twisting_morphism(-1) Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 - Sometimes it fails, even if the twist map is actually invertible:: + Sometimes it fails, even if the twisting morphism is actually invertible:: sage: S.twisting_morphism(-1) Traceback (most recent call last): ... NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t + 1 + + When the Ore polynomial ring is only twisted by a derivation, this + method returns nothing:: + + sage: der = R.derivation() + sage: A. = R['x', der] + sage: A + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by d/dt + sage: A.twisting_morphism() + + Here is an example where the twisting morphism is automatically + infered from the derivation:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: der = k.derivation(1, twist=Frob) + sage: der + [a |--> a^5] - id + sage: S. = k['x', der] + sage: S.twisting_morphism() + Frobenius endomorphism a |--> a^5 on Finite Field in a of size 5^3 + + .. SEEALSO:: + + :meth:`twisting_derivation` """ if self._morphism is not None: try: @@ -444,18 +725,63 @@ def twisting_morphism(self, n=1): else: raise ValueError("Unexpected error in iterating the twisting morphism: %s", e) - def twist_map(self, n): - # Deprecation + def twist_map(self, n=1): + r""" + Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times + or ``None`` if this Ore polynomial ring is not twisted by an endomorphism. + + This method is deprecated. You should use :meth:`twisting_morphism` instead. + + INPUT: + + - ``n`` - an integer (default: 1) + + EXAMPLES:: + + sage: R. = QQ[] + sage: sigma = R.hom([t+1]) + sage: S. = R['x', sigma] + sage: S.twist_map() + ... + DeprecationWarning: The method twist_map is deprecated; use twisting_morphism (same semantic) instead + See https://trac.sagemath.org/29629 for details. + Ring endomorphism of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> t + 1 + """ + from sage.misc.superseded import deprecation + deprecation(29629, "The method twist_map is deprecated; use twisting_morphism (same semantic) instead") return self.twisting_morphism(n) def twisting_derivation(self): + r""" + Return the twisting derivation defining this Ore polynomial ring + or ``None`` if this Ore polynomial ring is not twisted by a derivation. + + EXAMPLES:: + + sage: R. = QQ[] + sage: der = R.derivation(); der + d/dt + sage: A. = R['d', der] + sage: A.twisting_derivation() + d/dt + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x', Frob] + sage: S.twisting_derivation() + + .. SEEALSO:: + + :meth:`twisting_morphism` + """ return self._derivation @cached_method def gen(self, n=0): r""" - Return the indeterminate generator of this skew polynomial ring. + Return the indeterminate generator of this Ore polynomial ring. INPUT: @@ -467,7 +793,7 @@ def gen(self, n=0): sage: R. = QQ[] sage: sigma = R.hom([t+1]) sage: S. = R['x',sigma]; S - Skew Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 + Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by t |--> t + 1 sage: y = S.gen(); y x sage: y == x @@ -490,7 +816,8 @@ def gen(self, n=0): def gens_dict(self): r""" - Return a {name: variable} dictionary of the generators of ``self``. + Return a {name: variable} dictionary of the generators of + this Ore polynomial ring. EXAMPLES:: @@ -504,7 +831,7 @@ def gens_dict(self): def is_finite(self): r""" - Return ``False`` since skew polynomial rings are not finite + Return ``False`` since Ore polynomial rings are not finite (unless the base ring is `0`.) EXAMPLES:: @@ -522,14 +849,14 @@ def is_finite(self): def is_exact(self): r""" - Return ``True`` if elements of this skew polynomial ring are exact. + Return ``True`` if elements of this Ore polynomial ring are exact. This happens if and only if elements of the base ring are exact. EXAMPLES:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] + sage: S. = k['x', Frob] sage: S.is_exact() True sage: S.base_ring().is_exact() @@ -537,7 +864,7 @@ def is_exact(self): sage: R. = k[[]] sage: sigma = R.hom([u+u^2]) - sage: T. = R['y',sigma] + sage: T. = R['y', sigma] sage: T.is_exact() False sage: T.base_ring().is_exact() @@ -547,12 +874,12 @@ def is_exact(self): def is_sparse(self): r""" - Return ``True`` if the elements of this polynomial ring are sparsely + Return ``True`` if the elements of this Ore polynomial ring are sparsely represented. .. WARNING:: - Since sparse skew polynomials are not yet implemented, this + Since sparse Ore polynomials are not yet implemented, this function always returns ``False``. EXAMPLES:: @@ -567,8 +894,8 @@ def is_sparse(self): def ngens(self): r""" - Return the number of generators of this skew polynomial ring, - which is 1. + Return the number of generators of this Ore polynomial ring, + which is `1`. EXAMPLES:: @@ -582,7 +909,7 @@ def ngens(self): def random_element(self, degree=2, monic=False, *args, **kwds): r""" - Return a random skew polynomial in ``self``. + Return a random Ore polynomial in this ring. INPUT: @@ -590,14 +917,14 @@ def random_element(self, degree=2, monic=False, *args, **kwds): or a tuple of integers with minimum and maximum degrees - ``monic`` -- (default: ``False``) if ``True``, return a monic - skew polynomial + Ore polynomial - ``*args, **kwds`` -- passed on to the ``random_element`` method for the base ring OUTPUT: - Skew polynomial such that the coefficients of `x^i`, for `i` up + Ore polynomial such that the coefficients of `x^i`, for `i` up to ``degree``, are random elements from the base ring, randomized subject to the arguments ``*args`` and ``**kwds``. @@ -616,7 +943,7 @@ def random_element(self, degree=2, monic=False, *args, **kwds): sage: p = S.random_element(degree=5) # random (t^2 + 3*t)*x^4 + (4*t + 4)*x^3 + (4*t^2 + 4*t)*x^2 + (2*t^2 + 1)*x + 3 - When ``monic`` is ``False``, the returned skew polynomial may have + When ``monic`` is ``False``, the returned Ore polynomial may have a degree less than ``degree`` (it happens when the random leading coefficient is zero). However, if ``monic`` is ``True``, this can't happen:: @@ -635,7 +962,7 @@ def random_element(self, degree=2, monic=False, *args, **kwds): (3*t^2 + 1)*x^4 + (4*t + 2)*x^3 + (4*t + 1)*x^2 + (t^2 + 3*t + 3)*x + 3*t^2 + 2*t + 2 - If the first tuple element is greater than the second, a a + If the first tuple element is greater than the second, a ``ValueError`` is raised:: sage: S.random_element(degree=(5,4)) @@ -657,11 +984,11 @@ def random_element(self, degree=2, monic=False, *args, **kwds): def random_irreducible(self, degree=2, monic=True, *args, **kwds): r""" - Return a random irreducible skew polynomial. + Return a random irreducible Ore polynomial. .. WARNING:: - Elements of this skew polynomial ring need to have a method + Elements of this Ore polynomial ring need to have a method is_irreducible(). Currently, this method is implemented only when the base ring is a finite field. @@ -670,16 +997,12 @@ def random_irreducible(self, degree=2, monic=True, *args, **kwds): - ``degree`` - Integer with degree (default: 2) or a tuple of integers with minimum and maximum degrees - - ``monic`` - if True, returns a monic skew polynomial - (default: True) + - ``monic`` - if ``True``, returns a monic Ore polynomial + (default: ``True``) - - ``*args, **kwds`` - Passed on to the ``random_element`` method for + - ``*args, **kwds`` - passed on to the ``random_element`` method for the base ring - OUTPUT: - - - A random skew polynomial - EXAMPLES:: sage: k. = GF(5^3) @@ -707,19 +1030,29 @@ def random_irreducible(self, degree=2, monic=True, *args, **kwds): def is_commutative(self): r""" - Return ``True`` if this skew polynomial ring is commutative, i.e. if the - twist map is the identity. + Return ``True`` if this Ore polynomial ring is commutative, i.e. if the + twisting morphism is the identity and the twisting derivation vanishes. EXAMPLES:: - sage: k. = GF(5^3) + sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] + sage: S. = k['x', Frob] sage: S.is_commutative() False - sage: T. = k['y',Frob^3] + sage: T. = k['y', Frob^3] sage: T.is_commutative() True + + sage: R. = GF(5)[] + sage: der = R.derivation() + sage: A. = R['d', der] + sage: A.is_commutative() + False + + sage: B. = R['b', 5*der] + sage: B.is_commutative() + True """ - return self.twisting_morphism().is_identity() + return self._morphism is None and self._derivation is None diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index cee93bf4a1f..31454f5f916 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -2,18 +2,9 @@ r""" Univariate Skew Polynomials This module provides the -:class:`~sage.rings.polynomial.skew_polynomial_element.SkewPolynomial`, -which constructs a single univariate skew polynomial over commutative -base rings and an automorphism over the base ring. Skew polynomials are -non-commutative and so principal methods such as gcd, lcm, monic, -multiplication, and division are given in left and right forms. - -The generic implementation of dense skew polynomials is -:class:`~sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense`. -The classes -:class:`~sage.rings.polynomial.skew_polynomial_element.ConstantSkewPolynomialSection` -and :class:`~sage.rings.polynomial.skew_polynomial_element.SkewPolynomialBaseringInjection` -handle conversion from a skew polynomial ring to its base ring and vice versa respectively. +:class:`~sage.rings.polynomial.skew_polynomial_element.SkewPolynomial`. +In the class hierarchy in Sage, the locution *Skew Polynomial* is used +for a Ore polynomial without twisting derivation. .. WARNING:: @@ -29,9 +20,8 @@ handle conversion from a skew polynomial ring to its base ring and vice versa re sage: S. = R['x',sigma] sage: a = 2*(t + x) + 1 sage: a(t^2) - doctest:...: FutureWarning: This class/method/function is marked as - experimental. It, its functionality or its interface might change - without a formal deprecation. + doctest:...: FutureWarning: This class/method/function is marked as experimental. + It, its functionality or its interface might change without a formal deprecation. See http://trac.sagemath.org/13215 for details. 2*t^3 + 3*t^2 + 4*t + 2 sage: a(t) @@ -85,7 +75,7 @@ from sage.rings.polynomial.ore_polynomial_element cimport OrePolynomial_generic_ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): r""" Generic implementation of dense skew polynomial supporting any valid base - ring and twist map. + ring and twisting morphism. """ cpdef left_power_mod(self, exp, modulus): r""" @@ -272,7 +262,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): Given a skew polynomial `p(x) = \sum_{i=0}^d a_i * x^i`, we define the evaluation `p(r)` to be `\sum_{i=0}^d a_i * \sigma^i(r)`, where - `\sigma` is the twist map of the skew polynomial ring. + `\sigma` is the twisting morphism of the skew polynomial ring. INPUT: @@ -355,7 +345,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: a.operator_eval(t) 2*t^2 + 2*t + 3 - Evaluation points outside the base ring is usually not possible due to the twist map:: + Evaluation points outside the base ring is usually not possible due to the twisting morphism:: sage: R. = QQ[] sage: sigma = R.hom([t+1]) @@ -381,7 +371,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): variable of ``self``. The conjugate is obtained from ``self`` by applying the `n`-th iterate - of the twist map to each of its coefficients. + of the twisting morphism to each of its coefficients. INPUT: @@ -399,7 +389,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): True In principle, negative values for `n` are allowed, but Sage needs to be - able to invert the twist map:: + able to invert the twisting morphism:: sage: b = a.conjugate(-1) Traceback (most recent call last): @@ -459,7 +449,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): cpdef ModuleElement _lmul_(self, Element right): r""" - Multiply ``self`` on the right by scalar. + Return the product ``self * right``. INPUT: @@ -488,7 +478,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): cpdef ModuleElement _rmul_(self, Element left): r""" - Multiply ``self`` on the left by scalar. + Return the product ``left * self``. INPUT: @@ -515,11 +505,11 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): cpdef _mul_(self, right): r""" - Multiply ``self`` on the right by a skew polynomial. + Return the product ``self * right``. INPUT: - - ``right`` -- a skew polynomial in the same ring as ``self`` + - ``right`` -- a Ore polynomial in the same ring as ``self`` EXAMPLES:: @@ -649,7 +639,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): for j from 0 <= j < db: a[i+j] -= b[j] * parent.twisting_morphism(j)(c) except Exception: - raise NotImplementedError("inversion of the twist map %s" % parent.twisting_morphism()) + raise NotImplementedError("inversion of the twisting morphism %s" % parent.twisting_morphism()) q.append(c) q.reverse() return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index b854ceeb2c9..ac6288866f0 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -159,7 +159,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): [] If `a = N`, the type is just `[r]` where `r` is the order - of the twist map ``Frob``:: + of the twisting morphism ``Frob``:: sage: N = x3^2 + x3 + 1 sage: S(N).type(N) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index 1e70849dfbd..afe08a36caa 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -50,7 +50,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): sage: R. = GF(5^3) sage: Frob = R.frobenius_endomorphism() sage: S. = R['x', Frob]; S - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 We create a skew polynomial from a list:: diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 3faff223f62..a78cf2f3502 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1,18 +1,23 @@ r""" Skew Univariate Polynomial Rings -This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` -which constructs a general dense skew univariate polynomials over commutative base rings with -automorphisms over the base rings. This is the set of formal polynomials where the coefficients -are written on the left of the variable of the skew polynomial ring. The modified multiplication -operation over elements of the base ring is extended to all elements of the skew poynomial ring -by associativity and distributivity. - -This module also provides :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order` -which is a specialized class for skew polynomial rings over fields equipped with an automorphism of -finite order. It inherits from -:class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` but contains more -methods and provides better algorithms. +This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing`. +In the class hierarchy in Sage, the locution *Skew Polynomial* is used for a Ore polynomial +without twisting derivation. + +This module also provides: + +- the class :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order` + which is a specialized class for skew polynomial rings over fields equipped with an automorphism of + finite order. It inherits from :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` + but contains more methods and provides better algorithms. + +- the class :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field` + which is a specialized class for skew polynomial rings over finite fields. + +.. SEEALSO:: + + :class:`~sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing` AUTHOR: @@ -79,7 +84,7 @@ def _base_ring_to_fraction_field(S): sage: sigma = R.hom([t+1]) sage: S. = R['x', sigma] sage: _base_ring_to_fraction_field(S) - Skew Polynomial Ring in x over Fraction Field of Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 + Ore Polynomial Ring in x over Fraction Field of Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 """ R = S.base_ring() if isinstance(R, Field): @@ -92,7 +97,7 @@ def _base_ring_to_fraction_field(S): sigmaQ = Q.hom([Q(sigmaS(g)) for g in gens]) return Q[S.variable_name(), sigmaQ] # except Exception, e: - # raise ValueError("unable to lift the twist map to a twist map over %s (error was: %s)" % (Q, e)) + # raise ValueError("unable to lift the twisting morphism to a twisting morphism over %s (error was: %s)" % (Q, e)) def _minimal_vanishing_polynomial(R, eval_pts): @@ -180,7 +185,7 @@ def _lagrange_polynomial(R, eval_pts, values): True The following restrictions are impossible to satisfy because the evaluation - points are linearly dependent over the fixed field of the twist map, and the + points are linearly dependent over the fixed field of the twisting morphism, and the corresponding values do not match:: sage: eval_pts = [ t, 2*t ] @@ -188,14 +193,14 @@ def _lagrange_polynomial(R, eval_pts, values): sage: _lagrange_polynomial(S, eval_pts, values) Traceback (most recent call last): ... - ValueError: the given evaluation points are linearly dependent over the fixed field of the twist map, + ValueError: the given evaluation points are linearly dependent over the fixed field of the twisting morphism, so a Lagrange polynomial could not be determined (and might not exist). """ l = len(eval_pts) if l == 1: if eval_pts[0].is_zero(): # This is due to linear dependence among the eval_pts. - raise ValueError("the given evaluation points are linearly dependent over the fixed field of the twist map, so a Lagrange polynomial could not be determined (and might not exist).") + raise ValueError("the given evaluation points are linearly dependent over the fixed field of the twisting morphism, so a Lagrange polynomial could not be determined (and might not exist).") return (values[0] / eval_pts[0]) * R.one() else: t = l // 2 @@ -259,7 +264,7 @@ def minimal_vanishing_polynomial(self, eval_pts): are linearly independent over the fixed field of ``self.twisting_morphism()``. - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of the associated + independent over the fixed field of the twisting morphism of the associated skew polynomial ring OUTPUT: @@ -281,7 +286,7 @@ def minimal_vanishing_polynomial(self, eval_pts): [0, 0, 0] If the evaluation points are linearly dependent over the fixed field of - the twist map, then the returned polynomial has lower degree than the + the twisting morphism, then the returned polynomial has lower degree than the number of evaluation points:: sage: S.minimal_vanishing_polynomial([t]) @@ -341,7 +346,7 @@ def lagrange_polynomial(self, points): sage: T.lagrange_polynomial([ (t, 1), (2*t, 3) ]) Traceback (most recent call last): ... - ValueError: the given evaluation points are linearly dependent over the fixed field of the twist map, + ValueError: the given evaluation points are linearly dependent over the fixed field of the twisting morphism, so a Lagrange polynomial could not be determined (and might not exist). """ l = len(points) @@ -456,7 +461,7 @@ def __init__(self, domain, codomain, embed, order): sage: S. = SkewPolynomialRing(k, k.frobenius_endomorphism()) sage: Z = S.center() sage: S.convert_map_from(Z) # indirect doctest - Embedding of the center of Skew Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring + Embedding of the center of Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring """ RingHomomorphism.__init__(self, Hom(domain, codomain)) self._embed = embed @@ -475,9 +480,9 @@ def _repr_(self): sage: Z = S.center() sage: iota = S.convert_map_from(Z) sage: iota - Embedding of the center of Skew Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring + Embedding of the center of Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring sage: iota._repr_() - 'Embedding of the center of Skew Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring' + 'Embedding of the center of Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 into this ring' """ return "Embedding of the center of %s into this ring" % self._codomain @@ -559,7 +564,7 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x', Frob]; S - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: S.category() Category of algebras over Finite Field in t of size 5^3 @@ -621,7 +626,7 @@ def center(self, name=None, names=None, default=False): sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob]; S - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: Z = S.center(); Z Univariate Polynomial Ring in z over Finite Field of size 5 @@ -648,7 +653,7 @@ def center(self, name=None, names=None, default=False): sage: P = y + x; P x^3 + x sage: P.parent() - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 sage: P.parent() is S True @@ -777,7 +782,7 @@ def __init__(self, base_ring, morphism, derivation, names, sparse, category=None sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: T. = k['x', Frob]; T - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 """ if self.Element is None: import sage.rings.polynomial.skew_polynomial_finite_field From 0a232cd37b8c3f2346e317e24c2c3a8e3d0ef9d0 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 May 2020 01:15:57 +0200 Subject: [PATCH 04/16] LaTeX representation of derivations --- src/sage/rings/derivation.py | 108 +++++++++++++++++- .../rings/polynomial/ore_polynomial_ring.py | 4 +- 2 files changed, 106 insertions(+), 6 deletions(-) diff --git a/src/sage/rings/derivation.py b/src/sage/rings/derivation.py index af51e8b85e6..9e19a228482 100644 --- a/src/sage/rings/derivation.py +++ b/src/sage/rings/derivation.py @@ -205,6 +205,8 @@ from sage.categories.map import Map from sage.categories.rings import Rings +from sage.misc.latex import latex + class RingDerivationModule(Module, UniqueRepresentation): """ @@ -473,7 +475,6 @@ def _repr_(self): sage: R.derivation_module(twist=theta) Module of twisted derivations over Multivariate Polynomial Ring in x, y over Integer Ring (twisting morphism: x |--> y, y |--> x) - """ t = "" if self._twist is None: @@ -850,7 +851,6 @@ def _repr_(self): d/dx sage: R.derivation(y) d/dy - """ parent = self.parent() try: @@ -884,6 +884,56 @@ def _repr_(self): else: return s + def _latex_(self): + r""" + Return a LaTeX representation of this derivation. + + EXAMPLES:: + + sage: R. = ZZ[] + sage: ddx = R.derivation(x) + sage: ddy = R.derivation(y) + + sage: latex(ddx) + \frac{d}{dx} + sage: latex(ddy) + \frac{d}{dy} + sage: latex(ddx + ddy) + \frac{d}{dx} + \frac{d}{dy} + """ + parent = self.parent() + try: + dual_basis = parent.dual_basis() + except NotImplementedError: + return "\\text{A derivation on } %s" % latex(parent.domain()) + coeffs = self.list() + s = "" + for i in range(len(dual_basis)): + c = coeffs[i] + sc = str(c) + if sc == "0": + continue + ddx = "\\frac{d}{d%s}" % latex(dual_basis[i]) + if sc == "1": + s += " + " + ddx + elif sc == "-1": + s += " - " + ddx + elif c._is_atomic() and sc[0] != "-": + s += " + %s %s" % (sc, ddx) + elif (-c)._is_atomic(): + s += " - %s %s" % (-c, ddx) + else: + s += " + \\left(%s\\right) %s" % (sc, ddx) + if s[:3] == " + ": + return s[3:] + elif s[:3] == " - ": + return "-" + s[3:] + elif s == "": + return "0" + else: + return s + + def list(self): """ Return the list of coefficient of this derivation @@ -1268,7 +1318,18 @@ def _repr_(self): sage: M = ZZ.derivation_module() sage: M() 0 + """ + return "0" + + def _latex_(self): + """ + Return a string representation of this derivation. + EXAMPLES:: + + sage: M = ZZ.derivation_module() + sage: latex(M()) + 0 """ return "0" @@ -1911,7 +1972,6 @@ def _repr_(self): sage: theta = R.hom([y,x]) sage: R.derivation(1, twist=theta) [x |--> y, y |--> x] - id - """ scalar = self._scalar sc = str(scalar) @@ -1942,6 +2002,47 @@ def _repr_(self): s = "(%s)*" % sc return "%s(%s - %s)" % (s, stwi, sdef) + def _latex_(self): + r""" + Return a LaTeX representation of this derivation. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: der = k.derivation(a+1, twist=Frob) + sage: latex(der) + \left(a + 1\right) \left(\left[a \mapsto a^{5}\right] - \text{id}\right) + """ + scalar = self._scalar + sc = str(scalar) + if sc == "0": + return "0" + defining_morphism = self.parent().defining_morphism() + twisting_morphism = self.parent().twisting_morphism() + try: + if defining_morphism.is_identity(): + sdef = "\\text{id}" + else: + sdef = "\\left[%s\\right]" % latex(defining_morphism) + except (AttributeError, NotImplementedError): + sdef = "\\text{defining morphism}" + try: + stwi = "\\left[%s\\right]" % latex(twisting_morphism) + except AttributeError: + stwi = "\\text{twisting morphism}" + if sc == "1": + return "%s - %s" % (stwi, sdef) + elif sc == "-1": + s = "-" + elif scalar._is_atomic(): + s = "%s " % sc + elif (-scalar)._is_atomic(): + s = "-%s " % (-scalar) + else: + s = "\\left(%s\\right) " % sc + return "%s \\left(%s - %s\\right)" % (s, stwi, sdef) + def _add_(self, other): """ Return the sum of this derivation and ``other``. @@ -2173,4 +2274,3 @@ def _richcmp_(self, other, op): else: return True return NotImplemented - diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 9d97dfb5220..8cd06578d53 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -586,9 +586,9 @@ def _latex_(self): sage: R. = QQ[] sage: der = R.derivation() - sage: T. = R['d', der] + sage: T. = R['delta', der] sage: latex(T) # indirect doctest - \Bold{Q}[t]\left[d ; \text{\texttt{d/dt}} \right] + \Bold{Q}[t]\left[\delta ; \frac{d}{dt} \right] """ from sage.misc.latex import latex s = "%s\\left[%s" % (latex(self.base_ring()), self.latex_variable_names()[0]) From d4565513e8f19e8d1e8ea85ca83d088632578b6d Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 May 2020 02:26:23 +0200 Subject: [PATCH 05/16] implement Hilbert shift --- .../polynomial/ore_polynomial_element.pyx | 94 ++++++++++++++++++- .../rings/polynomial/ore_polynomial_ring.py | 4 +- 2 files changed, 94 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index df6f849ce68..9a02aa431f7 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -32,6 +32,8 @@ AUTHORS: import re from cysignals.signals cimport sig_check +from sage.structure.element import coerce_binop +from sage.misc.superseded import experimental from sage.rings.infinity import infinity from sage.structure.factorization import Factorization @@ -46,8 +48,6 @@ from cpython.object cimport PyObject_RichCompare from sage.categories.map cimport Map from sage.rings.morphism cimport Morphism, RingHomomorphism from sage.rings.polynomial.polynomial_element cimport _dict_to_list -from sage.structure.element import coerce_binop -from sage.misc.superseded import experimental cdef class OrePolynomial(AlgebraElement): @@ -2841,6 +2841,96 @@ cdef class OrePolynomial_generic_dense(OrePolynomial): else: return self._coeffs + def hilbert_shift(self, s, var=None): + r""" + Return this Ore polynomial with variable shifted by `s`, + i.e. if this Ore polynomial is `P(x)`, return `P(x+s)`. + + INPUT: + + - ``s`` -- an element in the base ring + + - ``var`` -- a string; the variable name + + EXAMPLES:: + + sage: R. = GF(7)[] + sage: der = R.derivation() + sage: A. = R['d', der] + + sage: L = d^3 + t*d^2 + sage: L.hilbert_shift(t) + d^3 + 4*t*d^2 + (5*t^2 + 3)*d + 2*t^3 + 4*t + sage: (d+t)^3 + t*(d+t)^2 + d^3 + 4*t*d^2 + (5*t^2 + 3)*d + 2*t^3 + 4*t + + One can specify another variable name:: + + sage: L.hilbert_shift(t, var='x') + x^3 + 4*t*x^2 + (5*t^2 + 3)*x + 2*t^3 + 4*t + + When the twisting morphism is not trivial, the output lies + in a different Ore polynomial ring:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x', Frob] + + sage: P = x^2 + a*x + a^2 + sage: Q = P.hilbert_shift(a); Q + x^2 + (2*a^2 + a + 4)*x + a^2 + 3*a + 4 + + sage: Q.parent() + Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 and a*([a |--> a^5] - id) + sage: Q.parent() is S + False + + This behavior ensures that the Hilbert shift by a fixed element + defines an homomorphism of rings:: + + sage: U = S.random_element(degree=5) + sage: V = S.random_element(degree=5) + sage: s = k.random_element() + sage: (U+V).hilbert_shift(s) == U.hilbert_shift(s) + V.hilbert_shift(s) + True + sage: (U*V).hilbert_shift(s) == U.hilbert_shift(s) * V.hilbert_shift(s) + True + + We check that shifting by an element and then by its opposite + gives back the initial Ore polynomial:: + + sage: P = S.random_element(degree=10) + sage: s = k.random_element() + sage: P.hilbert_shift(s).hilbert_shift(-s) == P + True + """ + from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing + parent = self._parent + k = parent.base_ring() + morphism = parent._morphism + derivation = parent._derivation + if morphism is not None: + if derivation is None: + derivation = k.derivation(s, twist=morphism) + else: + derivation += k.derivation(s, twist=morphism) + if var is None: + var = parent.variable_name() + if derivation is None: + S = OrePolynomialRing(k, morphism, var) + else: + S = OrePolynomialRing(k, derivation, var) + if not self: + return S.zero() + X = S.gen() + s + Xi = S.one() + ans = S(self[0]) + for i in range(1, self.degree()+1): + Xi = X * Xi + ans += self[i] * Xi + return ans + + cdef class ConstantOrePolynomialSection(Map): r""" Representation of the canonical homomorphism from the constants of a Ore diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 8cd06578d53..91fb3b6b1b8 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -268,7 +268,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): We check the uniqueness property of parents:: sage: der2 = R.derivation() - sage: B. = SkewPolynomialRing(R, der2) + sage: B. = OrePolynomialRing(R, der2) sage: A is B True @@ -276,7 +276,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): sage: k. = ZZ[] sage: theta = k.hom([t+1]) - sage: S. = SkewPolynomialRing(k, theta) + sage: S. = OrePolynomialRing(k, theta) sage: S Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 sage: type(S) From f225cbc1a8533f908eb9827b0d2564eab1df2bbc Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Mon, 11 May 2020 08:52:05 +0200 Subject: [PATCH 06/16] fix pyflakes --- src/sage/rings/polynomial/ore_polynomial_ring.py | 9 +++------ src/sage/rings/polynomial/skew_polynomial_ring.py | 8 +------- 2 files changed, 4 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 91fb3b6b1b8..fd7b6c6203d 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -20,27 +20,24 @@ # https://www.gnu.org/licenses/ # *************************************************************************** + import sage -from sage.structure.richcmp import op_EQ from sage.misc.prandom import randint from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity from sage.structure.category_object import normalize_names from sage.structure.unique_representation import UniqueRepresentation -from sage.rings.ring import Algebra, Field +from sage.rings.ring import Algebra from sage.rings.integer import Integer from sage.categories.commutative_rings import CommutativeRings from sage.categories.algebras import Algebras from sage.categories.fields import Fields -from sage.categories.morphism import Morphism, IdentityMorphism -from sage.rings.morphism import RingHomomorphism +from sage.categories.morphism import Morphism from sage.rings.derivation import RingDerivation -from sage.categories.homset import Hom -from sage.categories.map import Section from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.polynomial.ore_polynomial_element import OrePolynomialBaseringInjection diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index a78cf2f3502..f7b0e02fa89 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -40,17 +40,12 @@ # https://www.gnu.org/licenses/ # *************************************************************************** -import sage - from sage.structure.richcmp import op_EQ from sage.structure.category_object import normalize_names -from sage.rings.ring import Algebra, Field -from sage.rings.integer import Integer - +from sage.rings.ring import Field from sage.matrix.matrix_space import MatrixSpace -from sage.categories.morphism import Morphism from sage.rings.morphism import RingHomomorphism from sage.categories.homset import Hom from sage.categories.map import Section @@ -824,7 +819,6 @@ def _new_retraction_map(self, seed=None): [ 0 6 3 10] """ k = self.base_ring() - base = k.base_ring() section = self._embed_constants.section() if seed is None: seed = k.random_element() From b35e1d0ab287298943e3b4f67ec906351cfa10c7 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Wed, 13 May 2020 08:39:49 +0200 Subject: [PATCH 07/16] fix one doctest --- src/sage/categories/rings.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 391db25f052..dcd926410cb 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -915,12 +915,17 @@ def __getitem__(self, arg): sage: GF(17)['a']['b'] Univariate Polynomial Ring in b over Univariate Polynomial Ring in a over Finite Field of size 17 - We can create skew polynomial rings:: + We can create Ore polynomial rings:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: k['x',Frob] - Skew Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + sage: k['x', Frob] + Ore Polynomial Ring in x over Finite Field in t of size 5^3 twisted by t |--> t^5 + + sage: R. = QQ[] + sage: der = R.derivation() + sage: R['d', der] + Ore Polynomial Ring in d over Univariate Polynomial Ring in t over Rational Field twisted by d/dt We can also create power series rings by using double brackets:: From ea3f0b11545d13198007e3f1effdc95f1d523f15 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jul 2020 09:27:54 +0200 Subject: [PATCH 08/16] address Travis' comments --- .../polynomial/ore_polynomial_element.pyx | 59 ++++++++++-------- .../rings/polynomial/ore_polynomial_ring.py | 62 +++++++++---------- 2 files changed, 61 insertions(+), 60 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 9a02aa431f7..828b9f808c3 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -42,7 +42,7 @@ from sage.structure.parent cimport Parent from sage.structure.parent_gens cimport ParentWithGens from sage.misc.abstract_method import abstract_method from sage.categories.homset import Hom -from sage.categories.fields import Fields +from sage.rings.ring import _Fields from sage.rings.integer cimport Integer from cpython.object cimport PyObject_RichCompare from sage.categories.map cimport Map @@ -390,10 +390,16 @@ cdef class OrePolynomial(AlgebraElement): sage: a = (t+1)*x^5 + t^2*x^3 + x sage: a.leading_coefficient() t + 1 + + By convention, the leading coefficient to the zero polynomial is + zero:: + + sage: S(0).leading_coefficient() + 0 """ cdef int d = self.degree() if d == -1: - raise ValueError("the Ore polynomial must not be 0") + return self.base_ring()(0) return self[d] def is_unit(self): @@ -427,7 +433,7 @@ cdef class OrePolynomial(AlgebraElement): return False else: raise NotImplementedError("is_unit is not implemented for Ore polynomial rings " - "over base rings which are not integral domains.") + "over base rings which are not integral domains") def is_nilpotent(self): r""" @@ -478,7 +484,7 @@ cdef class OrePolynomial(AlgebraElement): sage: a.is_monic() False """ - return not self.is_zero() and self[self.degree()] == 1 + return self.leading_coefficient() == 1 def left_monic(self): r""" @@ -606,8 +612,7 @@ cdef class OrePolynomial(AlgebraElement): ... NotImplementedError: the leading coefficient of the divisor is not invertible """ - _,r = self.right_quo_rem(other) - return r + return self.right_quo_rem(other)[1] cpdef _floordiv_(self, right): r""" @@ -848,8 +853,8 @@ cdef class OrePolynomial(AlgebraElement): - ``other`` -- a Ore polynomial in the same ring as ``self`` - - ``monic`` -- boolean (default: ``True``). Return whether the left gcd - should be normalized to be monic. + - ``monic`` -- boolean (default: ``True``); return whether the left gcd + should be normalized to be monic OUTPUT: @@ -919,7 +924,7 @@ cdef class OrePolynomial(AlgebraElement): NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") cdef OrePolynomial G = self cdef OrePolynomial U = self._parent.one() @@ -1076,8 +1081,8 @@ cdef class OrePolynomial(AlgebraElement): - ``other`` -- a Ore polynomial in the same ring as ``self`` - - ``monic`` -- boolean (default: ``True``). Return whether the right gcd - should be normalized to be monic. + - ``monic`` -- boolean (default: ``True``); return whether the right gcd + should be normalized to be monic OUTPUT: @@ -1132,7 +1137,7 @@ cdef class OrePolynomial(AlgebraElement): ... TypeError: the base ring must be a field """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") cdef OrePolynomial G = self cdef OrePolynomial U = self._parent.one() @@ -1167,8 +1172,8 @@ cdef class OrePolynomial(AlgebraElement): - ``other`` -- a Ore polynomial in the same ring as ``self`` - - ``monic`` -- boolean (default: ``True``). Return whether the right gcd - should be normalized to be monic. + - ``monic`` -- boolean (default: ``True``); return whether the right gcd + should be normalized to be monic OUTPUT: @@ -1211,7 +1216,7 @@ cdef class OrePolynomial(AlgebraElement): ... TypeError: the base ring must be a field """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") if other.is_zero(): return self @@ -1232,8 +1237,8 @@ cdef class OrePolynomial(AlgebraElement): - ``other`` -- a Ore polynomial in the same ring as ``self`` - - ``monic`` -- boolean (default: ``True``). Return whether the left gcd - should be normalized to be monic. + - ``monic`` -- boolean (default: ``True``); return whether the left gcd + should be normalized to be monic OUTPUT: @@ -1291,7 +1296,7 @@ cdef class OrePolynomial(AlgebraElement): NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") if other.is_zero(): return self @@ -1370,7 +1375,7 @@ cdef class OrePolynomial(AlgebraElement): sage: V*Q == L True """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") if self.is_zero() or other.is_zero(): raise ZeroDivisionError("division by zero is not valid") @@ -1453,7 +1458,7 @@ cdef class OrePolynomial(AlgebraElement): sage: Q*V == L True """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") if self.is_zero() or other.is_zero(): raise ZeroDivisionError("division by zero is not valid") @@ -1477,8 +1482,8 @@ cdef class OrePolynomial(AlgebraElement): - ``other`` -- a Ore polynomial in the same ring as ``self`` - - ``monic`` -- boolean (default: ``True``). Return whether the left lcm - should be normalized to be monic. + - ``monic`` -- boolean (default: ``True``); return whether the left lcm + should be normalized to be monic OUTPUT: @@ -1527,7 +1532,7 @@ cdef class OrePolynomial(AlgebraElement): ... TypeError: the base ring must be a field """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") if self.is_zero() or other.is_zero(): raise ZeroDivisionError("division by zero is not valid") @@ -1545,8 +1550,8 @@ cdef class OrePolynomial(AlgebraElement): - ``other`` -- a Ore polynomial in the same ring as ``self`` - - ``monic`` -- boolean (default: ``True``). Return whether the right lcm - should be normalized to be monic. + - ``monic`` -- boolean (default: ``True``); return whether the right lcm + should be normalized to be monic OUTPUT: @@ -1611,7 +1616,7 @@ cdef class OrePolynomial(AlgebraElement): NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field Defn: t |--> t^2 """ - if self.base_ring() not in Fields: + if self.base_ring() not in _Fields: raise TypeError("the base ring must be a field") if self.is_zero() or other.is_zero(): raise ZeroDivisionError("division by zero is not valid") @@ -1755,7 +1760,7 @@ cdef class OrePolynomial(AlgebraElement): sage: bool(b) False """ - return not self.is_zero() + return self.degree() > -1 def base_ring(self): r""" diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index fd7b6c6203d..545c710efe0 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -34,7 +34,7 @@ from sage.categories.commutative_rings import CommutativeRings from sage.categories.algebras import Algebras -from sage.categories.fields import Fields +from sage.rings.ring import _Fields from sage.categories.morphism import Morphism from sage.rings.derivation import RingDerivation @@ -48,13 +48,13 @@ # Generic implementation of Ore polynomial rings ################################################# -class OrePolynomialRing(Algebra, UniqueRepresentation): +class OrePolynomialRing(UniqueRepresentation, Algebra): r""" Construct and return the globally unique Ore polynomial ring with the given properties and variable names. Given a ring `R` and a ring automorphism `\sigma` of `R` and a - `\sigma`-derivation `\partial`, the ring of Ore polynomials + `\sigma`-derivation `\partial`, the ring of Ore polynomials `R[X; \sigma, \partial]` is the usual abelian group polynomial `R[X]` equipped with the modification multiplication deduced from the rule `X a = \sigma(a) X + \partial(a)`. @@ -73,18 +73,18 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): EXAMPLES: - THE CASE OF A TWISTING ENDOMORPHISM: + .. RUBRIC:: The case of a twisting endomorphism - We create the Ore ring `\FF_{5^3}[x, \text{Frob}]` where Frob is the + We create the Ore ring `\GF{5^3}[x, \text{Frob}]` where Frob is the Frobenius endomorphism. - + sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S = OrePolynomialRing(k, Frob, 'x') sage: S Ore Polynomial Ring in x over Finite Field in a of size 5^3 twisted by a |--> a^5 - In particular, observe that it is not needed to create and pass in + In particular, observe that it is not needed to create and pass in the twisting derivation (which is `0` in our example). As a shortcut, we can use the square brackets notation as follow:: @@ -96,7 +96,7 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): True We emphasize that it is necessary to repeat the name of the variable - in the right hand side. Indeed, the following fails (it is interpreted + in the right hand side. Indeed, the following fails (it is interpreted by Sage as a classical polynomial ring with variable name ``Frob``). sage: T. = k[Frob] @@ -104,7 +104,7 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): ... ValueError: variable name 'Frobenius endomorphism a |--> a^5 on Finite Field in a of size 5^3' is not alphanumeric - Note moreover that, similarly to the classical case, using the brackets + Note moreover that, similarly to the classical case, using the brackets notation also sets the variable:: sage: x.parent() is S @@ -117,7 +117,7 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): sage: Frob(a)*x (2*a^2 + 4*a + 4)*x - THE CASE OF A TWISTING DERIVATION: + .. RUBRIC:: The case of a twisting derivation We can similarly create the Ore ring of differential operators over `\QQ[t]`, namely `\QQ[t][d, \frac{d}{dt}]`:: @@ -140,10 +140,10 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): sage: d*t t*d + 1 - THE COMBINED CASE: + .. RUBRIC:: The combined case Ore polynomial rings involving at the same time a twisting morphism - `sigma` and a twisting `sigma`-derivation can be created as well as + `\sigma` and a twisting `\sigma`-derivation can be created as well as follows:: sage: F. = Qq(3^2) @@ -165,7 +165,7 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): ... ValueError: variable name 'Frobenius endomorphism ...' is not alphanumeric - EXAMPLES of VARIABLE NAME CONTEXT:: + .. RUBRIC:: Examples of variable name context sage: R. = ZZ[] sage: sigma = R.hom([t+1]) @@ -185,7 +185,7 @@ class OrePolynomialRing(Algebra, UniqueRepresentation): Ore Polynomial Ring in y over Univariate Polynomial Ring in t over Integer Ring twisted by t |--> t + 1 - UNIQUENESS and IMMUTABILITY: + .. RUBRIC:: Uniqueness and immutability In Sage, there is exactly one Ore polynomial ring for each quadruple (base ring, twisting morphism, twisting derivation, name of the variable). @@ -279,7 +279,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): sage: type(S) - In certain situations (e.g. when the twisting morphism is the Frobenius + In certain situations (e.g. when the twisting morphism is the Frobenius over a finite field), even more specialized classes are used:: sage: k. = GF(7^5) @@ -329,7 +329,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): from sage.rings.polynomial import skew_polynomial_ring constructors = [ ] if derivation is None: - if base_ring in Fields(): + if base_ring in _Fields: try: order = morphism.order() if order is not Infinity: @@ -340,7 +340,7 @@ def __classcall_private__(cls, base_ring, twist=None, names=None, sparse=False): except (AttributeError, NotImplementedError): pass constructors.append(skew_polynomial_ring.SkewPolynomialRing) - + for constructor in constructors: try: return constructor(base_ring, morphism, derivation, names, sparse) @@ -541,7 +541,7 @@ def _repr_(self): r""" Return a string representation of ``self``. - The locution *Ore Polynomial Ring* is used when the twisting + The locution *Ore Polynomial Ring* is used when the twisting derivation is zero. Otherwise the locution *Ore Polynomial Ring* is used. @@ -604,11 +604,7 @@ def change_var(self, var): INPUT: - - ``var`` -- a string representing the name of the new variable. - - OUTPUT: - - ``self`` with variable name changed to ``var``. + - ``var`` -- a string representing the name of the new variable EXAMPLES:: @@ -650,7 +646,7 @@ def characteristic(self): @cached_method def twisting_morphism(self, n=1): r""" - Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times + Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times or ``None`` if this Ore polynomial ring is not twisted by an endomorphism. INPUT: @@ -696,8 +692,8 @@ def twisting_morphism(self, n=1): sage: A Ore Polynomial Ring in x over Univariate Polynomial Ring in t over Rational Field twisted by d/dt sage: A.twisting_morphism() - - Here is an example where the twisting morphism is automatically + + Here is an example where the twisting morphism is automatically infered from the derivation:: sage: k. = GF(5^3) @@ -710,7 +706,7 @@ def twisting_morphism(self, n=1): Frobenius endomorphism a |--> a^5 on Finite Field in a of size 5^3 .. SEEALSO:: - + :meth:`twisting_derivation` """ if self._morphism is not None: @@ -724,7 +720,7 @@ def twisting_morphism(self, n=1): def twist_map(self, n=1): r""" - Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times + Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times or ``None`` if this Ore polynomial ring is not twisted by an endomorphism. This method is deprecated. You should use :meth:`twisting_morphism` instead. @@ -769,7 +765,7 @@ def twisting_derivation(self): sage: S.twisting_derivation() .. SEEALSO:: - + :meth:`twisting_morphism` """ return self._derivation @@ -782,8 +778,8 @@ def gen(self, n=0): INPUT: - - ``n`` -- index of generator to return (default: 0). Exists for - compatibility with other polynomial rings. + - ``n`` -- index of generator to return (default: 0); exists for + compatibility with other polynomial rings EXAMPLES:: @@ -829,7 +825,7 @@ def gens_dict(self): def is_finite(self): r""" Return ``False`` since Ore polynomial rings are not finite - (unless the base ring is `0`.) + (unless the base ring is `0`). EXAMPLES:: @@ -997,7 +993,7 @@ def random_irreducible(self, degree=2, monic=True, *args, **kwds): - ``monic`` - if ``True``, returns a monic Ore polynomial (default: ``True``) - - ``*args, **kwds`` - passed on to the ``random_element`` method for + - ``*args, **kwds`` - passed in to the ``random_element`` method for the base ring EXAMPLES:: From 445913a25a44a209f6b82c47b6a5ce7e8557b3e5 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jul 2020 09:43:36 +0200 Subject: [PATCH 09/16] abstract method degree raises NotImplementedError --- src/sage/rings/polynomial/ore_polynomial_element.pyx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 828b9f808c3..0bdfabc6aa8 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -283,6 +283,7 @@ cdef class OrePolynomial(AlgebraElement): sage: S(5).degree() 0 """ + raise NotImplementedError cdef OrePolynomial _new_c(self, list coeffs, Parent P, char check=0): r""" From fdbded75f7b0cbacabfa218162cb96386d4de3de Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jul 2020 09:46:38 +0200 Subject: [PATCH 10/16] add full-stop after math formulas --- src/sage/rings/polynomial/ore_polynomial_element.pyx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index 0bdfabc6aa8..f019cf01728 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -1358,7 +1358,7 @@ cdef class OrePolynomial(AlgebraElement): .. MATH:: - `U \cdot \text{self} = V \cdot \text{other} = L` + U \cdot \text{self} = V \cdot \text{other} = L. EXAMPLES:: @@ -1435,7 +1435,7 @@ cdef class OrePolynomial(AlgebraElement): .. MATH:: - `\text{self} \cdot U = \text{other} \cdot V = L` + \text{self} \cdot U = \text{other} \cdot V = L. INPUT: From c77835679d5a6ce00b33a9f303a815b2245d3078 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 3 Jul 2020 10:47:44 +0200 Subject: [PATCH 11/16] remove import sage --- src/sage/rings/polynomial/ore_polynomial_ring.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 545c710efe0..8aca9d788dd 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -21,8 +21,6 @@ # *************************************************************************** -import sage - from sage.misc.prandom import randint from sage.misc.cachefunc import cached_method from sage.rings.infinity import Infinity @@ -31,6 +29,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.rings.ring import Algebra from sage.rings.integer import Integer +from sage.structure.element import Element from sage.categories.commutative_rings import CommutativeRings from sage.categories.algebras import Algebras @@ -381,6 +380,7 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) sage: TestSuite(S).run() """ if self.Element is None: + import sage.rings.polynomial.ore_polynomial_element self.Element = sage.rings.polynomial.ore_polynomial_element.OrePolynomial_generic_dense self.__is_sparse = sparse self._morphism = morphism @@ -428,7 +428,7 @@ def _element_constructor_(self, a=None, check=True, construct=False, **kwds): C = self.Element if isinstance(a, list): return C(self, a, check=check, construct=construct) - if isinstance(a, sage.structure.element.Element): + if isinstance(a, Element): P = a.parent() def build(check): if a.is_zero(): From 94a1a5c0439ef5f5cef470e0b563fa81cfe88cf5 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 4 Jul 2020 08:19:48 +0200 Subject: [PATCH 12/16] lazy import --- src/sage/rings/polynomial/all.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/all.py b/src/sage/rings/polynomial/all.py index 4ea0d9761c0..621fb9fe764 100644 --- a/src/sage/rings/polynomial/all.py +++ b/src/sage/rings/polynomial/all.py @@ -46,7 +46,7 @@ from sage.rings.polynomial.infinite_polynomial_ring import InfinitePolynomialRing # Ore Polynomial Rings -from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing +lazy_import('sage.rings.polynomial.ore_polynomial_ring', 'OrePolynomialRing') SkewPolynomialRing = OrePolynomialRing # Evaluation of cyclotomic polynomials From 15f6dfad8a18ed26fbab018718e0df60769163aa Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sat, 4 Jul 2020 08:29:49 +0200 Subject: [PATCH 13/16] fix indentation --- src/sage/categories/rings.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sage/categories/rings.py b/src/sage/categories/rings.py index 6cec5d17e62..d34fc3464d4 100644 --- a/src/sage/categories/rings.py +++ b/src/sage/categories/rings.py @@ -1084,8 +1084,8 @@ def normalize_arg(arg): from sage.categories.morphism import Morphism from sage.rings.derivation import RingDerivation if len(arg) == 2 and isinstance(arg[1], (Morphism, RingDerivation)): - from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing - return OrePolynomialRing(self, arg[1], names=arg[0]) + from sage.rings.polynomial.ore_polynomial_ring import OrePolynomialRing + return OrePolynomialRing(self, arg[1], names=arg[0]) # 2. Otherwise, if all specified elements are algebraic, try to # return an algebraic extension From a9bae6909bcc44b89e7e5cc9c44ba8a753a60a05 Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Sun, 5 Jul 2020 11:07:18 +0200 Subject: [PATCH 14/16] update documentation --- src/doc/en/reference/polynomial_rings/index.rst | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index 934132e374c..9ef1e3f9daa 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -29,15 +29,18 @@ Multivariate Polynomials invariant_theory polynomial_rings_toy_implementations -Skew Polynomials ----------------- +Ore Polynomials +--------------- .. toctree:: :maxdepth: 2 - sage/rings/polynomial/skew_polynomial_element + sage/rings/polynomial/ore_polynomial_ring + sage/rings/polynomial/ore_polynomial_element sage/rings/polynomial/skew_polynomial_ring + sage/rings/polynomial/skew_polynomial_element sage/rings/polynomial/skew_polynomial_finite_order + sage/rings/polynomial/skew_polynomial_finite_field Rational Functions ------------------ From 5d822f323238c9dd73224a41d12efd2b79b52ec4 Mon Sep 17 00:00:00 2001 From: Travis Scrimshaw Date: Mon, 6 Jul 2020 10:56:59 +1000 Subject: [PATCH 15/16] Fixing the doc and != for polynomial injection maps. --- .../polynomial/ore_polynomial_element.pyx | 4 +- .../rings/polynomial/ore_polynomial_ring.py | 53 ++++--- .../polynomial/skew_polynomial_element.pyx | 15 +- .../skew_polynomial_finite_field.pyx | 77 +++++----- .../skew_polynomial_finite_order.pyx | 32 ++-- .../rings/polynomial/skew_polynomial_ring.py | 144 ++++++++++-------- 6 files changed, 177 insertions(+), 148 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index f019cf01728..f1b5bd20252 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -20,7 +20,7 @@ AUTHORS: - Xavier Caruso (2020-05) """ -############################################################################# +# *************************************************************************** # Copyright (C) 2020 Xavier Caruso # # This program is free software: you can redistribute it and/or modify @@ -28,7 +28,7 @@ AUTHORS: # the Free Software Foundation, either version 2 of the License, or # (at your option) any later version. # http://www.gnu.org/licenses/ -#**************************************************************************** +# *************************************************************************** import re from cysignals.signals cimport sig_check diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index 8aca9d788dd..bed9ad0f626 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -1,9 +1,10 @@ r""" Univariate Ore Polynomial Rings -This module provides the :class:`~sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing`. -which constructs a general dense univariate Ore polynomial ring over a commutative base with -equipped with an endomorphism and/or a derivation. +This module provides the +:class:`~sage.rings.polynomial.ore_polynomial_ring.OrePolynomialRing`, +which constructs a general dense univariate Ore polynomial ring over a +commutative base with equipped with an endomorphism and/or a derivation. AUTHOR: @@ -68,14 +69,14 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): - ``names`` -- a string or a list of strings - - ``sparse`` -- a boolean (default: ``False``). Currently not supported. + - ``sparse`` -- a boolean (default: ``False``); currently not supported EXAMPLES: .. RUBRIC:: The case of a twisting endomorphism We create the Ore ring `\GF{5^3}[x, \text{Frob}]` where Frob is the - Frobenius endomorphism. + Frobenius endomorphism:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -96,7 +97,7 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): We emphasize that it is necessary to repeat the name of the variable in the right hand side. Indeed, the following fails (it is interpreted - by Sage as a classical polynomial ring with variable name ``Frob``). + by Sage as a classical polynomial ring with variable name ``Frob``):: sage: T. = k[Frob] Traceback (most recent call last): @@ -147,13 +148,15 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): sage: F. = Qq(3^2) sage: sigma = F.frobenius_endomorphism(); sigma - Frobenius endomorphism on 3-adic Unramified Extension Field in u defined by x^2 + 2*x + 2 lifting u |--> u^3 on the residue field + Frobenius endomorphism on 3-adic Unramified Extension Field in u + defined by x^2 + 2*x + 2 lifting u |--> u^3 on the residue field sage: der = F.derivation(3, twist=sigma); der (3 + O(3^21))*([Frob] - id) sage: M. = F['X', der] sage: M - Ore Polynomial Ring in X over 3-adic Unramified Extension Field in u defined by x^2 + 2*x + 2 twisted by Frob and (3 + O(3^21))*([Frob] - id) + Ore Polynomial Ring in X over 3-adic Unramified Extension Field in u + defined by x^2 + 2*x + 2 twisted by Frob and (3 + O(3^21))*([Frob] - id) We emphasize that we only need to pass in the twisted derivation as it already contains in it the datum of the twisting endomorphism. @@ -166,6 +169,8 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): .. RUBRIC:: Examples of variable name context + Consider the following:: + sage: R. = ZZ[] sage: sigma = R.hom([t+1]) sage: S. = SkewPolynomialRing(R, sigma); S @@ -187,7 +192,7 @@ class OrePolynomialRing(UniqueRepresentation, Algebra): .. RUBRIC:: Uniqueness and immutability In Sage, there is exactly one Ore polynomial ring for each quadruple - (base ring, twisting morphism, twisting derivation, name of the variable). + (base ring, twisting morphism, twisting derivation, name of the variable):: sage: k. = GF(7^3) sage: Frob = k.frobenius_endomorphism() @@ -642,12 +647,12 @@ def characteristic(self): """ return self.base_ring().characteristic() - @cached_method def twisting_morphism(self, n=1): r""" - Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times - or ``None`` if this Ore polynomial ring is not twisted by an endomorphism. + Return the twisting endomorphism defining this Ore polynomial ring + iterated ``n`` times or ``None`` if this Ore polynomial ring is not + twisted by an endomorphism. INPUT: @@ -676,7 +681,8 @@ def twisting_morphism(self, n=1): sage: T.twisting_morphism(-1) Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 - Sometimes it fails, even if the twisting morphism is actually invertible:: + Sometimes it fails, even if the twisting morphism is + actually invertible:: sage: S.twisting_morphism(-1) Traceback (most recent call last): @@ -720,10 +726,11 @@ def twisting_morphism(self, n=1): def twist_map(self, n=1): r""" - Return the twisting endomorphism defining this Ore polynomial ring iterated ``n`` times - or ``None`` if this Ore polynomial ring is not twisted by an endomorphism. + Return the twisting endomorphism defining this Ore polynomial ring + iterated ``n`` times or ``None`` if this Ore polynomial ring is not + twisted by an endomorphism. - This method is deprecated. You should use :meth:`twisting_morphism` instead. + This method is deprecated. Use :meth:`twisting_morphism` instead. INPUT: @@ -770,7 +777,6 @@ def twisting_derivation(self): """ return self._derivation - @cached_method def gen(self, n=0): r""" @@ -867,8 +873,8 @@ def is_exact(self): def is_sparse(self): r""" - Return ``True`` if the elements of this Ore polynomial ring are sparsely - represented. + Return ``True`` if the elements of this Ore polynomial ring are + sparsely represented. .. WARNING:: @@ -931,15 +937,17 @@ def random_element(self, degree=2, monic=False, *args, **kwds): sage: S.random_element(monic=True) # random x^2 + (2*t^2 + t + 1)*x + 3*t^2 + 3*t + 2 - Use ``degree`` to obtain polynomials of higher degree + Use ``degree`` to obtain polynomials of higher degree:: sage: p = S.random_element(degree=5) # random (t^2 + 3*t)*x^4 + (4*t + 4)*x^3 + (4*t^2 + 4*t)*x^2 + (2*t^2 + 1)*x + 3 + sage: p.degree() <= 5 + True When ``monic`` is ``False``, the returned Ore polynomial may have a degree less than ``degree`` (it happens when the random leading - coefficient is zero). However, if ``monic`` is ``True``, this can't - happen:: + coefficient is zero). However, if ``monic`` is ``True``, this + cannot happen:: sage: p = S.random_element(degree=4, monic=True) sage: p.leading_coefficient() == S.base_ring().one() @@ -1049,3 +1057,4 @@ def is_commutative(self): True """ return self._morphism is None and self._derivation is None + diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 31454f5f916..1cc8bc3a43c 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -36,10 +36,9 @@ AUTHORS: - Johan Rosenkilde (2016-08-03): changes for bug fixes, docstring and doctest errors - """ -############################################################################# +# *************************************************************************** # Copyright (C) 2012 Xavier Caruso # # This program is free software: you can redistribute it and/or modify @@ -345,7 +344,8 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: a.operator_eval(t) 2*t^2 + 2*t + 3 - Evaluation points outside the base ring is usually not possible due to the twisting morphism:: + Evaluation points outside the base ring is usually not possible + due to the twisting morphism:: sage: R. = QQ[] sage: sigma = R.hom([t+1]) @@ -354,7 +354,9 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: a.operator_eval(1/t) Traceback (most recent call last): ... - TypeError: 1/t fails to convert into the map's domain Univariate Polynomial Ring in t over Rational Field, but a `pushforward` method is not properly implemented + TypeError: 1/t fails to convert into the map's domain + Univariate Polynomial Ring in t over Rational Field, + but a `pushforward` method is not properly implemented """ cdef RingHomomorphism sigma = self._parent.twisting_morphism() cdef list coefficients = self.list() @@ -414,7 +416,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): return r def multi_point_evaluation(self, eval_pts): - """ + r""" Evaluate ``self`` at list of evaluation points. INPUT: @@ -431,7 +433,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): repeatedly. If fast skew polynomial multiplication is available, an asymptotically faster method is possible using standard divide and conquer techniques and - :meth:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general.minimal_vanishing_polynomial`. + :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing.minimal_vanishing_polynomial`. EXAMPLES:: @@ -671,3 +673,4 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): q.append(c) q.reverse() return (self._new_c(q, parent), self._new_c(a[:db], parent, 1)) + diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index ac6288866f0..3a5334610b6 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -1,19 +1,21 @@ r""" Univariate Dense Skew Polynomials over Finite Fields -This module provides the :class:`~sage.rings.polynomial.skew_polynomial_finite_field.SkewPolynomial_finite_field_dense` -which constructs a single univariate skew polynomial over a finite field equipped with the Frobenius -endomorphism. Among other things, it implements the fast factorization algorithm designed in [CL2017]_. +This module provides the +class:`~sage.rings.polynomial.skew_polynomial_finite_field.SkewPolynomial_finite_field_dense`, +which constructs a single univariate skew polynomial over a finite field +equipped with the Frobenius endomorphism. Among other things, it implements +the fast factorization algorithm designed in [CL2017]_. AUTHOR:: - Xavier Caruso (2012-06-29): initial version -- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests and refactored classes and methods - +- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests and + refactored classes and methods """ -############################################################################# +# *************************************************************************** # Copyright (C) 2012 Xavier Caruso # # This program is free software: you can redistribute it and/or modify @@ -55,8 +57,8 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): return self._norm_factor def is_irreducible(self): - """ - Return True if this skew polynomial is irreducible. + r""" + Return ``True`` if this skew polynomial is irreducible. EXAMPLES:: @@ -103,13 +105,13 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def type(self, N): - """ + r""" Return the `N`-type of this skew polynomial (see definition below). INPUT: - - ``N`` -- an irreducible polynomial in the - center of the underlying skew polynomial ring + - ``N`` -- an irreducible polynomial in the + center of the underlying skew polynomial ring .. NOTE:: @@ -118,11 +120,11 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): DEFINITION: The `N`-type of a skew polynomial `a` is the Partition - `(t_0, t_1, t_2, ...)` defined by + `(t_0, t_1, t_2, \ldots)` defined by .. MATH:: - t_0 + \cdots + t_i = \frac{\deg gcd(a,N^i)}{\deg N} + t_0 + \cdots + t_i = \frac{\deg gcd(a,N^i)}{\deg N}, where `\deg N` is the degree of `N` considered as an element in the center. @@ -215,7 +217,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): # ---------------- cdef SkewPolynomial_finite_field_dense _rdivisor_c(self, N): - """ + r""" Return a right divisor of this skew polynomial whose reduced norm is `N`. @@ -322,11 +324,11 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: Q._reduced_norm_factor_uniform() z + 1 - Now, we consider the product `R = P*Q`; it admits 32 irreducible - divisors but among them, only one has norm `z + 2`, the others - having norm `z + 1`. - Therefore this method outputs `z + 2` with probability 1/32 - and `z + 1` with probability 31/32. + Now, we consider the product `R = P \cdot Q`; it admits `32` + irreducible divisors but among them, only one has norm `z + 2`, + the others having norm `z + 1`. + Therefore this method outputs `z + 2` with probability `1 / 32` + and `z + 1` with probability `31 / 32`:: sage: R = P*Q sage: counts = { z+1: 0, z+2: 0 } @@ -360,7 +362,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def _irreducible_divisors(self, bint right): - """ + r""" Return an iterator over all irreducible monic divisors of this skew polynomial. @@ -472,7 +474,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def right_irreducible_divisor(self, uniform=False): - """ + r""" Return a right irreducible divisor of this skew polynomial. INPUT: @@ -517,7 +519,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): x^3 + x^2 + (4*t^2 + 2*t + 4)*x + t^2 + 3 By convention, the zero skew polynomial has no irreducible - divisor: + divisor:: sage: S(0).right_irreducible_divisor() Traceback (most recent call last): @@ -547,7 +549,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): return D def left_irreducible_divisor(self, uniform=False): - """ + r""" Return a left irreducible divisor of this skew polynomial. INPUT: @@ -585,7 +587,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): x^3 + (t^2 + t + 2)*x^2 + (3*t^2 + t)*x + 2*t + 1 By convention, the zero skew polynomial has no irreducible - divisor: + divisor:: sage: S(0).left_irreducible_divisor() Traceback (most recent call last): @@ -620,11 +622,11 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def right_irreducible_divisors(self): - """ + r""" Return an iterator over all irreducible monic right divisors of this skew polynomial. - EXAMPLES: + EXAMPLES:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -655,11 +657,11 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): return self._irreducible_divisors(True) def left_irreducible_divisors(self): - """ + r""" Return an iterator over all irreducible monic left divisors of this skew polynomial. - EXAMPLES: + EXAMPLES:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -691,7 +693,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def count_irreducible_divisors(self): - """ + r""" Return the number of irreducible monic divisors of this skew polynomial. @@ -708,7 +710,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: S. = k['x',Frob] We illustrate that a skew polynomial may have a number of irreducible - divisors greater than its degree. + divisors greater than its degree:: sage: a = x^4 + (4*t + 3)*x^3 + t^2*x^2 + (4*t^2 + 3*t)*x + 3*t sage: a.count_irreducible_divisors() @@ -752,7 +754,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): # ---------------------- cdef _factor_c(self): - """ + r""" Compute a factorization of ``self``. This is the low level implementation of :meth:`factor`. @@ -824,7 +826,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): cdef _factor_uniform_c(self): - """ + r""" Compute a uniformly distrbuted factorization of ``self``. This is the low level implementation of :meth:`factor`. @@ -932,7 +934,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def factor(self, uniform=False): - """ + r""" Return a factorization of this skew polynomial. INPUT: @@ -970,7 +972,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): There is a priori no guarantee on the distribution of the factorizations we get. Passing in the keyword ``uniform=True`` ensures the output is uniformly distributed among all - factorizations. + factorizations:: sage: a.factor(uniform=True) # random (x + t^2 + 4) * (x + t) * (x + t + 3) @@ -979,7 +981,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): sage: a.factor(uniform=True) # random (x + 2*t^2 + 3*t) * (x + 4*t + 2) * (x + 2*t + 2) - By convention, the zero skew polynomial has no factorization: + By convention, the zero skew polynomial has no factorization:: sage: S(0).factor() Traceback (most recent call last): @@ -1000,7 +1002,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): def count_factorizations(self): - """ + r""" Return the number of factorizations (as a product of a unit and a product of irreducible monic factors) of this skew polynomial. @@ -1045,7 +1047,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): # Not optimized: # many calls to reduced_norm, reduced_norm_factor, _rdivisor_c, which are slow def factorizations(self): - """ + r""" Return an iterator over all factorizations (as a product of a unit and a product of irreducible monic factors) of this skew polynomial. @@ -1104,3 +1106,4 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_finite_order_dense): unit = self.leading_coefficient() for factors in factorizations_rec(~unit*self): yield Factorization(factors, sort=False, unit=unit) + diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx index afe08a36caa..1dadb5c0e65 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_order.pyx @@ -5,11 +5,11 @@ AUTHOR:: - Xavier Caruso (2012-06-29): initial version -- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests and refactored classes and methods - +- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests + and refactored classes and methods """ -############################################################################# +# *************************************************************************** # Copyright (C) 2012 Xavier Caruso # # This program is free software: you can redistribute it and/or modify @@ -68,7 +68,6 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): t^2 + 1 sage: x.parent() is S True - """ SkewPolynomial_generic_dense.__init__ (self, parent, x, check, construct, **kwds) self._norm = None @@ -180,7 +179,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): 3*z + 4 The reduced trace lies in the center of `S`, which is a univariate - polynomial ring in the variable `z = x^3` over `GF(5)`. + polynomial ring in the variable `z = x^3` over `\GF{5}`:: sage: tr.parent() Univariate Polynomial Ring in z over Finite Field of size 5 @@ -193,7 +192,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): 3*x^3 + 4 By default, the name of the central variable is usually ``z`` (see - :meth:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomiaRing_finite_order.center` + :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` for more details about this). However, the user can specify a different variable name if desired:: @@ -251,7 +250,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): z^3 + 4*z^2 + 4 The reduced norm lies in the center of `S`, which is a univariate - polynomial ring in the variable `z = x^3` over `GF(5)`. + polynomial ring in the variable `z = x^3` over `\GF{5}`:: sage: N.parent() Univariate Polynomial Ring in z over Finite Field of size 5 @@ -264,7 +263,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): x^9 + 4*x^6 + 4 By default, the name of the central variable is usually ``z`` (see - :meth:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomiaRing_finite_order.center` + :meth:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order.center` for more details about this). However, the user can speciify a different variable name if desired:: @@ -338,8 +337,8 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): def is_central(self): - """ - Return True if this skew polynomial lies in the center. + r""" + Return ``True`` if this skew polynomial lies in the center. EXAMPLES:: @@ -363,7 +362,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): def bound(self): - """ + r""" Return a bound of this skew polynomial (i.e. a multiple of this skew polynomial lying in the center). @@ -398,8 +397,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): z^2 + z + 4 We observe that the bound is explicity given as an element of the - center (which is a univariate polynomial ring in the variable - `z`). + center (which is a univariate polynomial ring in the variable `z`). We can use conversion to send it in the skew polynomial ring:: sage: S(b) @@ -446,7 +444,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): def optimal_bound(self): - """ + r""" Return the optimal bound of this skew polynomial (i.e. the monic multiple of this skew polynomial of minimal degree lying in the center). @@ -468,8 +466,7 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): z + 3 We observe that the bound is explicity given as an element of the - center (which is a univariate polynomial ring in the variable - `z`). + center (which is a univariate polynomial ring in the variable `z`). We can use conversion to send it in the skew polynomial ring:: sage: S(b) @@ -495,4 +492,5 @@ cdef class SkewPolynomial_finite_order_dense(SkewPolynomial_generic_dense): # TODO: # fast multiplication - # reduced characteristic polynomial + # reduced characteristic polynomial + diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 8b91140afa9..57e69ebd22e 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1,18 +1,20 @@ r""" Skew Univariate Polynomial Rings -This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing`. -In the class hierarchy in Sage, the locution *Skew Polynomial* is used for a Ore polynomial -without twisting derivation. +This module provides the +:class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing`. +In the class hierarchy in Sage, the locution *Skew Polynomial* is used +for a Ore polynomial without twisting derivation. This module also provides: -- the class :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order` - which is a specialized class for skew polynomial rings over fields equipped with an automorphism of - finite order. It inherits from :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` +- the class :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_order`, + which is a specialized class for skew polynomial rings over fields + equipped with an automorphism of finite order. It inherits from + :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` but contains more methods and provides better algorithms. -- the class :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field` +- the class :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field`, which is a specialized class for skew polynomial rings over finite fields. .. SEEALSO:: @@ -28,8 +30,8 @@ - Johan Rosenkilde (2016-08-03): changes for bug fixes, docstring and doctest errors - """ + # *************************************************************************** # Copyright (C) 2012 Xavier Caruso # @@ -40,7 +42,7 @@ # https://www.gnu.org/licenses/ # *************************************************************************** -from sage.structure.richcmp import op_EQ +from sage.structure.richcmp import op_EQ, op_NE from sage.structure.category_object import normalize_names from sage.rings.ring import Field @@ -59,18 +61,18 @@ # Helper functions def _base_ring_to_fraction_field(S): - """ + r""" Return the unique skew polynomial ring over the fraction field of ``S.base_ring()`` which has ``S`` a sub-ring (internal method). INPUT: - - ``S`` -- a skew polynomial ring. + - ``S`` -- a skew polynomial ring OUTPUT: - - ``Q`` -- the skew polynomial ring over the fraction field of - ``S.base_ring``. + - ``Q`` -- the skew polynomial ring over the fraction field + of ``S.base_ring`` EXAMPLES:: @@ -96,7 +98,7 @@ def _base_ring_to_fraction_field(S): def _minimal_vanishing_polynomial(R, eval_pts): - """ + r""" Return the minimal vanishing polynomial (internal function). See the documentation for @@ -104,7 +106,7 @@ def _minimal_vanishing_polynomial(R, eval_pts): INPUT: - - ``R`` -- A skew polynomial ring over a field. + - ``R`` -- a skew polynomial ring over a field - ``eval_pts`` -- a list of evaluation points @@ -120,7 +122,8 @@ def _minimal_vanishing_polynomial(R, eval_pts): sage: S. = k['x',Frob] sage: eval_pts = [1, t, t^2] sage: b = _minimal_vanishing_polynomial(S, eval_pts); b - doctest:...: FutureWarning: This class/method/function is marked as experimental. It, its functionality or its interface might change without a formal deprecation. + doctest:...: FutureWarning: This class/method/function is marked as experimental. + It, its functionality or its interface might change without a formal deprecation. See http://trac.sagemath.org/13215 for details. x^3 + 4 """ @@ -144,7 +147,7 @@ def _minimal_vanishing_polynomial(R, eval_pts): def _lagrange_polynomial(R, eval_pts, values): - """ + r""" Return the Lagrange polynomial of the given points if it exists. Otherwise return an unspecified polynomial (internal method). @@ -164,7 +167,7 @@ def _lagrange_polynomial(R, eval_pts, values): OUTPUT: - - the Lagrange polynomial. + - the Lagrange polynomial EXAMPLES:: @@ -189,13 +192,16 @@ def _lagrange_polynomial(R, eval_pts, values): Traceback (most recent call last): ... ValueError: the given evaluation points are linearly dependent over the fixed field of the twisting morphism, - so a Lagrange polynomial could not be determined (and might not exist). + so a Lagrange polynomial could not be determined (and might not exist) """ l = len(eval_pts) if l == 1: if eval_pts[0].is_zero(): # This is due to linear dependence among the eval_pts. - raise ValueError("the given evaluation points are linearly dependent over the fixed field of the twisting morphism, so a Lagrange polynomial could not be determined (and might not exist).") + raise ValueError("the given evaluation points are linearly dependent" + " over the fixed field of the twisting morphism," + " so a Lagrange polynomial could not be determined" + " (and might not exist)") return (values[0] / eval_pts[0]) * R.one() else: t = l // 2 @@ -250,17 +256,18 @@ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None) OrePolynomialRing.__init__(self, base_ring, morphism, None, name, sparse, category) def minimal_vanishing_polynomial(self, eval_pts): - """ + r""" Return the minimal-degree, monic skew polynomial which vanishes at all the given evaluation points. The degree of the vanishing polynomial is at most the length of - ``eval_pts``. Equality holds if and only if the elements of ``eval_pts`` - are linearly independent over the fixed field of ``self.twisting_morphism()``. + ``eval_pts``. Equality holds if and only if the elements of + ``eval_pts`` are linearly independent over the fixed field of + ``self.twisting_morphism()``. - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twisting morphism of the associated - skew polynomial ring + independent over the fixed field of the twisting morphism of + the associated skew polynomial ring OUTPUT: @@ -275,14 +282,15 @@ def minimal_vanishing_polynomial(self, eval_pts): sage: b = S.minimal_vanishing_polynomial(eval_pts); b x^3 + 4 - The minimal vanishing polynomial evaluates to 0 at each of the evaluation points:: + The minimal vanishing polynomial evaluates to 0 at each of + the evaluation points:: sage: eval = b.multi_point_evaluation(eval_pts); eval [0, 0, 0] - If the evaluation points are linearly dependent over the fixed field of - the twisting morphism, then the returned polynomial has lower degree than the - number of evaluation points:: + If the evaluation points are linearly dependent over the fixed + field of the twisting morphism, then the returned polynomial has + lower degree than the number of evaluation points:: sage: S.minimal_vanishing_polynomial([t]) x + 3*t^2 + 3*t @@ -296,22 +304,23 @@ def lagrange_polynomial(self, points): Return the minimal-degree polynomial which interpolates the given points. - More precisely, given `n` pairs `(x_1, y_1), ..., (x_n, y_n) \in R^2`, - where `R` is ``self.base_ring()``, compute a skew polynomial `p(x)` such - that `p(x_i) = y_i` for each `i`, under the condition that the `x_i` are - linearly independent over the fixed field of ``self.twisting_morphism()``. + More precisely, given `n` pairs `(x_1, y_1), \ldots, (x_n, y_n) \in R^2`, + where `R` is ``self.base_ring()``, compute a skew polynomial `p(x)` + such that `p(x_i) = y_i` for each `i`, under the condition that + the `x_i` are linearly independent over the fixed field of + ``self.twisting_morphism()``. If the `x_i` are linearly independent over the fixed field of - ``self.twisting_morphism()`` then such a polynomial is guaranteed to exist. - Otherwise, it might exist depending on the `y_i`, but the algorithm used - in this implementation does not support that, and so an error is always - raised. + ``self.twisting_morphism()`` then such a polynomial is guaranteed + to exist. Otherwise, it might exist depending on the `y_i`, but + the algorithm used in this implementation does not support that, + and so an error is always raised. INPUT: - - ``points`` -- a list of pairs ``(x_1, y_1),..., (x_n, y_n)`` of - elements of the base ring of ``self``. The `x_i` should be linearly - independent over the fixed field of ``self.twisting_morphism()``. + - ``points`` -- a list of pairs `(x_1, y_1), \ldots, (x_n, y_n)` of + elements of the base ring of ``self``; the `x_i` should be linearly + independent over the fixed field of ``self.twisting_morphism()`` OUTPUT: @@ -342,7 +351,7 @@ def lagrange_polynomial(self, points): Traceback (most recent call last): ... ValueError: the given evaluation points are linearly dependent over the fixed field of the twisting morphism, - so a Lagrange polynomial could not be determined (and might not exist). + so a Lagrange polynomial could not be determined (and might not exist) """ l = len(points) if not all(len(pair) == 2 for pair in points): @@ -354,7 +363,7 @@ def lagrange_polynomial(self, points): raise TypeError("the evaluation points must be distinct") zero_i = [i for i in range(l) if eval_pts[i].is_zero()] if zero_i and not values[zero_i[0]].is_zero(): - raise TypeError("a skew polynomial always evaluates to 0 at 0, but a non-zero value was requested.") + raise TypeError("a skew polynomial always evaluates to 0 at 0, but a non-zero value was requested") return _lagrange_polynomial(_base_ring_to_fraction_field(self), eval_pts, values) @@ -365,7 +374,7 @@ def lagrange_polynomial(self, points): class SectionSkewPolynomialCenterInjection(Section): r""" Section of the canonical injection of the center of a skew - polynomial ring into this ring + polynomial ring into this ring. TESTS:: @@ -378,7 +387,7 @@ class SectionSkewPolynomialCenterInjection(Section): """ def _call_(self, x): r""" - Return `x` viewed as an element of the center + Return `x` viewed as an element of the center. EXAMPLES:: @@ -412,7 +421,7 @@ def _call_(self, x): def _richcmp_(self, right, op): r""" - Compare this morphism with ``right`` + Compare this morphism with ``right``. TESTS:: @@ -425,18 +434,22 @@ def _richcmp_(self, right, op): sage: s = loads(dumps(sigma)) sage: s == sigma True + sage: s != sigma + False sage: s is sigma False """ if op == op_EQ: return (self.domain() is right.domain()) and (self.codomain() is right.codomain()) + if op == op_NE: + return (self.domain() is not right.domain()) or (self.codomain() is not right.codomain()) return NotImplemented class SkewPolynomialCenterInjection(RingHomomorphism): r""" Canonical injection of the center of a skew polynomial ring - into this ring + into this ring. TESTS:: @@ -448,7 +461,7 @@ class SkewPolynomialCenterInjection(RingHomomorphism): """ def __init__(self, domain, codomain, embed, order): r""" - Initialize this morphism + Initialize this morphism. EXAMPLES:: @@ -466,7 +479,7 @@ def __init__(self, domain, codomain, embed, order): def _repr_(self): r""" - Return a string representation of this morphism + Return a string representation of this morphism. EXAMPLES:: @@ -483,7 +496,7 @@ def _repr_(self): def _call_(self, x): r""" - Return the image of `x` by this morphism + Return the image of `x` by this morphism. TESTS:: @@ -504,7 +517,7 @@ def _call_(self, x): def _richcmp_(self, right, op): r""" - Compare this morphism with ``right`` + Compare this morphism with ``right``. TESTS:: @@ -516,16 +529,20 @@ def _richcmp_(self, right, op): sage: i = loads(dumps(iota)) sage: i == iota True + sage: i != iota + False sage: i is iota False """ if op == op_EQ: return (self.domain() is right.domain()) and (self.codomain() is right.codomain()) + if op == op_NE: + return (self.domain() is not right.domain()) or (self.codomain() is not right.codomain()) return NotImplemented def section(self): r""" - Return a section of this morphism + Return a section of this morphism. EXAMPLES:: @@ -546,13 +563,12 @@ class SkewPolynomialRing_finite_order(SkewPolynomialRing): .. SEEALSO:: - :meth:`sage.rings.polynomial.skew_polynomial_ring_constructor.SkewPolynomialRing` - :class:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` - :mod:`sage.rings.polynomial.skew_polynomial_finite_order` + - :class:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` + - :mod:`sage.rings.polynomial.skew_polynomial_finite_order` """ def __init__(self, base_ring, morphism, derivation, name, sparse, category=None): r""" - Initialize this skew polynomial + Initialize this skew polynomial. TESTS:: @@ -604,7 +620,7 @@ def center(self, name=None, names=None, default=False): .. NOTE:: - If F denotes the subring of R fixed by `\sigma` and `\sigma` + If `F` denotes the subring of `R` fixed by `\sigma` and `\sigma` has order `r`, the center of `K[x,\sigma]` is `F[x^r]`, that is a univariate polynomial ring over `F`. @@ -669,7 +685,7 @@ def center(self, name=None, names=None, default=False): sage: S1.center() is S2.center() True - ABOUT THE DEFAULT NAME OF THE CENTRAL VARIABLE: + .. RUBRIC:: About the default name of the central variable A priori, the default is ``z``. @@ -737,14 +753,13 @@ def center(self, name=None, names=None, default=False): ###################################################### class SkewPolynomialRing_finite_field(SkewPolynomialRing_finite_order): - """ + r""" A specialized class for skew polynomial rings over finite fields. .. SEEALSO:: - :meth:`sage.rings.polynomial.skew_polynomial_ring_constructor.SkewPolynomialRing` - :class:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general` - :mod:`sage.rings.polynomial.skew_polynomial_finite_field` + - :class:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing` + - :mod:`sage.rings.polynomial.skew_polynomial_finite_field` .. TODO:: @@ -752,7 +767,7 @@ class SkewPolynomialRing_finite_field(SkewPolynomialRing_finite_order): multiplication and factorization. """ def __init__(self, base_ring, morphism, derivation, names, sparse, category=None): - """ + r""" This method is a constructor for a general, dense univariate skew polynomial ring over a finite field. @@ -786,7 +801,7 @@ def __init__(self, base_ring, morphism, derivation, names, sparse, category=None self._matrix_retraction = None def _new_retraction_map(self, seed=None): - """ + r""" Create a retraction map from the ring of coefficient of this skew polynomial ring to its fixed subfield under the twisting morphism @@ -836,7 +851,7 @@ def _new_retraction_map(self, seed=None): self._matrix_retraction = MatrixSpace(self._constants, 1, k.degree())(trace) def _retraction(self, x, newmap=False, seed=None): - """ + r""" Return the image of `x` under the retraction map (see also :meth:`_new_retraction_map`) @@ -880,3 +895,4 @@ def _retraction(self, x, newmap=False, seed=None): if newmap or seed is not None or self._matrix_retraction is None: self._new_retraction_map() return (self._matrix_retraction*self.base_ring()(x)._vector_())[0] + From 200d7bd99aaea8377e5148d8e744b9cb5d56cf3a Mon Sep 17 00:00:00 2001 From: Xavier Caruso Date: Fri, 10 Jul 2020 01:13:19 +0200 Subject: [PATCH 16/16] update doctests --- .../polynomial/ore_polynomial_element.pyx | 22 ++++++------------- .../rings/polynomial/ore_polynomial_ring.py | 12 +++++----- .../polynomial/skew_polynomial_element.pyx | 10 ++++----- 3 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/sage/rings/polynomial/ore_polynomial_element.pyx b/src/sage/rings/polynomial/ore_polynomial_element.pyx index f019cf01728..f6081b1cc1a 100644 --- a/src/sage/rings/polynomial/ore_polynomial_element.pyx +++ b/src/sage/rings/polynomial/ore_polynomial_element.pyx @@ -155,15 +155,6 @@ cdef class OrePolynomial(AlgebraElement): sage: r == c % b True - Left euclidean division won't work over our current `S` because Sage can't - invert the twisting morphism:: - - sage: q,r = c.left_quo_rem(b) - Traceback (most recent call last): - ... - NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 - Here we can see the effect of the operator evaluation compared to the usual polynomial evaluation:: @@ -174,7 +165,7 @@ cdef class OrePolynomial(AlgebraElement): See http://trac.sagemath.org/13215 for details. t + 2 - Here is a working example over a finite field:: + Here is another example over a finite field:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -998,16 +989,17 @@ cdef class OrePolynomial(AlgebraElement): In the following example, Sage does not know the inverse of the twisting morphism:: - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] + sage: R. = QQ[] + sage: K = R.fraction_field() + sage: sigma = K.hom([(t+1)/(t-1)]) + sage: S. = K['x',sigma] sage: a = (-2*t^2 - t + 1)*x^3 + (-t^2 + t)*x^2 + (-12*t - 2)*x - t^2 - 95*t + 1 sage: b = x^2 + (5*t - 6)*x - 4*t^2 + 4*t - 1 sage: a.left_quo_rem(b) Traceback (most recent call last): ... - NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring - Defn: t |--> t + 1 + NotImplementedError: inversion of the twisting morphism Ring endomorphism of Fraction Field of Univariate Polynomial Ring in t over Rational Field + Defn: t |--> (t + 1)/(t - 1) """ if not other: raise ZeroDivisionError("division by zero is not valid") diff --git a/src/sage/rings/polynomial/ore_polynomial_ring.py b/src/sage/rings/polynomial/ore_polynomial_ring.py index b0a9828971c..16294384bcf 100644 --- a/src/sage/rings/polynomial/ore_polynomial_ring.py +++ b/src/sage/rings/polynomial/ore_polynomial_ring.py @@ -670,19 +670,21 @@ def twisting_morphism(self, n=1): If ``n`` in negative, Sage tries to compute the inverse of the twisting morphism:: - sage: k. = GF(5^3) + sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: T. = k['y',Frob] sage: T.twisting_morphism(-1) - Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 + Frobenius endomorphism a |--> a^(5^2) on Finite Field in a of size 5^3 Sometimes it fails, even if the twisting morphism is actually invertible:: - sage: S.twisting_morphism(-1) + sage: K = R.fraction_field() + sage: phi = K.hom([(t+1)/(t-1)]) + sage: T. = K['y', phi] + sage: T.twisting_morphism(-1) Traceback (most recent call last): ... - NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 1 + NotImplementedError: inverse not implemented for morphisms of Fraction Field of Univariate Polynomial Ring in t over Rational Field When the Ore polynomial ring is only twisted by a derivation, this method returns nothing:: diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 31454f5f916..0fd90c93d22 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -380,11 +380,12 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): EXAMPLES:: sage: R. = QQ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] + sage: K = R.fraction_field() + sage: sigma = K.hom([1 + 1/t]) + sage: S. = K['x',sigma] sage: a = t*x^3 + (t^2 + 1)*x^2 + 2*t sage: b = a.conjugate(2); b - (t + 2)*x^3 + (t^2 + 4*t + 5)*x^2 + 2*t + 4 + ((2*t + 1)/(t + 1))*x^3 + ((5*t^2 + 6*t + 2)/(t^2 + 2*t + 1))*x^2 + (4*t + 2)/(t + 1) sage: x^2*a == b*x^2 True @@ -394,8 +395,7 @@ cdef class SkewPolynomial_generic_dense(OrePolynomial_generic_dense): sage: b = a.conjugate(-1) Traceback (most recent call last): ... - NotImplementedError: inversion of the twisting morphism Ring endomorphism of Univariate Polynomial Ring in t over Rational Field - Defn: t |--> t + 1 + NotImplementedError: inverse not implemented for morphisms of Fraction Field of Univariate Polynomial Ring in t over Rational Field Here is a working example::