From e46f7785524ade7db547b89dc27ab20f6459d368 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 25 Jul 2016 02:38:39 +0530 Subject: [PATCH 01/37] added the finite field files without karatsuba, center, irreducibility and factor stuff. cleaned up code. fixed doctest errors. --- .../skew_polynomial_finite_field.pxd | 20 + .../skew_polynomial_finite_field.pyx | 581 ++++++++++++++++++ 2 files changed, 601 insertions(+) create mode 100644 src/sage/rings/polynomial/skew_polynomial_finite_field.pxd create mode 100644 src/sage/rings/polynomial/skew_polynomial_finite_field.pyx diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd new file mode 100644 index 00000000000..d927743be70 --- /dev/null +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -0,0 +1,20 @@ +from sage.rings.integer cimport Integer +from skew_polynomial_element cimport SkewPolynomial_generic_dense +from sage.matrix.matrix_dense cimport Matrix_dense +from polynomial_element cimport Polynomial +from sage.structure.element cimport RingElement + +cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): + + cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other) + cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other) + cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other) + cdef void _inplace_lfloordiv(self, SkewPolynomial_finite_field_dense other) + cdef void _inplace_rfloordiv(self, SkewPolynomial_finite_field_dense other) + cdef void _inplace_lmonic(self) + cdef void _inplace_rmonic(self) + cdef void _inplace_rgcd(self,SkewPolynomial_finite_field_dense other) + cdef Py_ssize_t _val_inplace_unit(self) + cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other) + + cdef Matrix_dense _matmul_c(self) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx new file mode 100644 index 00000000000..ac734eccbf3 --- /dev/null +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -0,0 +1,581 @@ +r""" +This module implements skew polynomials over finite fields. + +Let `k` be a finite field and `\sigma` be a ring automorphism +of `k` (i.e. a power of the Frobenius endomorphism). Let +Put `S = k[X,\sigma]`: as an addtive group, it is the usual ring +of polynomials with coefficients in `k` and the multiplication +on `S` is defined by the rule `X * a = \sigma(a) * X`. + +We recall that: + +#. `S` is a left (resp. right) euclidean noncommutative ring + +#. in particular, every left (resp. right) ideal is principal + +.. TODO:: + + Try to replace as possible ``finite field`` by ``field + endowed with a finite order twist morphism``. It may cause + new phenomena due to the non trivality of the Brauer group. + +EXAMPLES:: + +We illustrate some properties listed above:: + + 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 + +AUTHOR:: + +- Xavier Caruso (2012-06-29) +""" + +############################################################################# +# Copyright (C) 2012 Xavier Caruso +# +# Distributed under the terms of the GNU General Public License (GPL) +# +# http://www.gnu.org/licenses/ +#**************************************************************************** + +cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): + + def __init__(self, parent, x=None, int check=1, is_gen=False, int construct=0, **kwds): + SkewPolynomial_generic_dense.__init__ (self, parent, x, check, is_gen, construct, **kwds) + + cdef SkewPolynomial _new_c(self, list coeffs, Parent P, char check=0): + """ + Fast creation of a new skew polynomial + """ + cdef type t = type(self) + cdef SkewPolynomial_finite_field_dense f = t.__new__(t) + f._parent = P + f.__coeffs = coeffs + if check: + f.__normalize() + return f + + def rquo_rem(self, other): + """ + DEFINITION: + + Let `a` and `b` be two skew polynomials over the same + ring. The *right euclidean division* of `a` by `b` is + a couple `(q,r)` such that + + - `a = q*b + 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. + + If the leading coefficient of `b` is a unit (e.g. if + `b` is monic) then `q` and `r` exist and are unique. + + INPUT: + + - ``other`` -- a skew polynomial ring over the same + base ring + + OUTPUT: + + - the quotient and the remainder of the left euclidean + division of this skew polynomial by ``other`` + + .. NOTE:: + + Doesn't work 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^2*x^4 + (-12*t^2 - 2*t - 1)*x^3 + (-95*t^2 + t + 2)*x^2 + (-t^2 + t)*x + 2*t - 8 + sage: b = S.random_element(monic=True); b + x^2 + (4*t^2 - t - 2)*x - t^2 + t - 1 + 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 + (-4*t^2 + t)*x^2 - 2*t^2*x + 5*t^2 - 6*t - 4 + sage: a.right_quo_rem(c) + Traceback (most recent call last): + ... + NotImplementedError: the leading coefficient of the divisor is not invertible + """ + cdef list a = self.list() + cdef list b = other.list() + cdef Py_ssize_t i, j + cdef Py_ssize_t da = len(a)-1, db = len(b)-1 + parent = self._parent + if db < 0: + raise ZeroDivisionError + if da < db: + res = parent(0), self + return res + cdef RingElement inv = ~b[db] + cdef list q = [ ] + cdef Py_ssize_t order = parent._order + cdef list twinv = [ inv ], twb = [ b ] + cdef RingElement c, x + for i from 0 <= i < min(da-db,order-1): + twinv.append(parent.twist_map()(twinv[i])) + twb.append([ parent.twist_map()(x) for x in twb[i] ]) + for i from da-db >= i >= 0: + c = twinv[i%order] * a[i+db] + for j from 0 <= j < db: + a[i+j] -= c * twb[i%order][j] + q.append(c) + q.reverse() + res = parent(q), parent(a[:db]) + return res + + cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other): + """ + Fast right gcd. + """ + cdef SkewPolynomial_finite_field_dense A = self + cdef SkewPolynomial_finite_field_dense B = other + cdef SkewPolynomial_finite_field_dense swap + if len(B.__coeffs): + A = self._new_c(A.__coeffs[:],A._parent) + B = B._new_c(B.__coeffs[:],B._parent) + while len(B.__coeffs): + A._inplace_rrem(B) + swap = A; A = B; B = swap + return A + else: + return self + + cpdef _leftpow_(self, exp, modulus=None): + """ + INPUT: + + - ``exp`` -- an Integer + + - ``modulus`` -- a skew polynomial over the same ring (default: None) + + OUTPUT: + + If ``modulus`` is None, return ``self**exp``. + + Otherwise, return the 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``. + + However, if the base ring is a finite field, Sage uses the + following optimized algorithm: + + #. One first compute a central skew polynomial `N` which is + divisible by ``modulus``. (Since `N` lies in center, the + quotient `K[X,\sigma]/N` inherits a ring structure.) + + #. One compute ``self**exp`` in the quotient ring `K[X,\sigma]/N` + + #. One reduce modulo ``modulus`` the result computed in the + previous step + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: a = x + t + sage: b = a._leftpow_(10) + + sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 + sage: br = a._leftpow_(10, modulus); br + (4*t^2 + 2*t + 3)*x^2 + (3*t^2 + 1)*x + 2*t + 3 + sage: lq, lr = b.left_quo_rem(modulus) + sage: br == lr + True + + sage: a._leftpow_(100, modulus) # rather fast + (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t + """ + cdef SkewPolynomial_finite_field_dense r + + if not isinstance(exp, Integer) or isinstance(exp, int): + try: + exp = Integer(exp) + except TypeError: + raise TypeError("non-integral exponents not supported") + + if self.degree() <= 0: + r = self.parent()(self[0]**exp) + return r + if exp == 0: + r = self.parent()(1) + return r + if exp < 0: + r = (~self).leftpow(-exp,modulus) + return r + + if self == self.parent().gen(): # special case x**n should be faster! + P = self.parent() + R = P.base_ring() + v = [R.zero()]*exp + [R.one()] + r = self._new_c(v,self._parent) + if modulus: + _, r = r.left_quo_rem(modulus) + return r + + mod = modulus + if not modulus is None: + try: + mod = self.parent()(mod.bound()) + except NotImplementedError: + mod = None + r = self._new_c(copy.copy(self.__coeffs),self._parent) + if mod: + r._inplace_pow_mod(exp,mod) + else: + r._inplace_pow(exp) + if (not modulus is None) and modulus != mod: + _, r = r.left_quo_rem(modulus) + return r + + cpdef _rightpow_(self, exp, modulus=None): + """ + INPUT: + + - ``exp`` -- an Integer + + - ``modulus`` -- a skew polynomial over the same ring (default: None) + + OUTPUT: + + If ``modulus`` is None, return ``self**exp``. + + Otherwise, return the 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``. + + However, if the base ring is a finite field, Sage uses the + following optimized algorithm: + + #. One first compute a central skew polynomial `N` which is + divisible by ``modulus``. (Since `N` lies in center, the + quotient `K[X,\sigma]/N` inherits a ring structure.) + + #. One compute ``self**exp`` in the quotient ring `K[X,\sigma]/N` + + #. One reduce modulo ``modulus`` the result computed in the + previous step + + 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._rightpow_(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._rightpow_(100, modulus) # rather fast + (2*t^2 + 3)*x^2 + (t^2 + 4*t + 2)*x + t^2 + 2*t + 1 + """ + cdef SkewPolynomial_finite_field_dense r + + if not isinstance(exp, Integer) or isinstance(exp, int): + try: + exp = Integer(exp) + except TypeError: + raise TypeError("non-integral exponents not supported") + + if self.degree() <= 0: + r = self.parent()(self[0]**exp) + return r + if exp == 0: + r = self.parent()(1) + return r + if exp < 0: + r = (~self).rightpow(-exp,modulus) + return r + + if self == self.parent().gen(): # special case x**n should be faster! + P = self.parent() + R = P.base_ring() + v = [R.zero()]*exp + [R.one()] + r = self._new_c(v,self._parent) + if modulus: + _, r = r.right_quo_rem(modulus) + return r + + mod = modulus + if not modulus is None: + try: + mod = self.parent()(mod.bound()) + except NotImplementedError: + mod = None + r = self._new_c(copy.copy(self.__coeffs),self._parent) + if mod: + r._inplace_pow_mod(exp,mod) + else: + r._inplace_pow(exp) + if (not modulus is None) and modulus != mod: + _, r = r.right_quo_rem(modulus) + return r + + cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other): + """ + Replace self by the remainder in the left euclidean division + of self by other (only for internal use). + """ + cdef list a = (self).__coeffs + cdef list b = (other).__coeffs + cdef Py_ssize_t da = len(a)-1, db = len(b)-1 + cdef Py_ssize_t i, j + cdef RingElement c, inv + parent = self._parent + if db < 0: + raise ZeroDivisionError + if da >= db: + inv = ~b[db] + for i from da-db >= i >= 0: + 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) + del a[db:] + self.__normalize() + + cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other): + """ + Replace self by the remainder in the right euclidean division + of self by other (only for internal use). + """ + cdef list a = (self).__coeffs + cdef list b = (other).__coeffs + cdef Py_ssize_t da = len(a)-1, db = len(b)-1 + cdef Py_ssize_t i, j, order + cdef RingElement c, x, inv + cdef list twinv, twb + parent = self._parent + if db < 0: + raise ZeroDivisionError + if da >= db: + order = parent._order + inv = ~b[db] + twinv = [ inv ] + for i from 0 <= i < min(da-db,order-1): + twinv.append(parent.twist_map()(twinv[i])) + twb = (other)._conjugates + for i from len(twb)-1 <= i < min(da-db,order-1): + twb.append([ parent.twist_map()(x) for x in twb[i] ]) + for i from da-db >= i >= 0: + c = twinv[i%order] * a[i+db] + for j from 0 <= j < db: + a[i+j] -= c * twb[i%order][j] + del a[db:] + self.__normalize() + + cdef void _inplace_lfloordiv(self, SkewPolynomial_finite_field_dense other): + """ + Replace self by the quotient in the left euclidean division + of self by other (only for internal use). + """ + cdef list a = (self).__coeffs + cdef list b = (other).__coeffs + cdef Py_ssize_t da = len(a)-1, db = len(b)-1 + cdef Py_ssize_t i, j, deb + cdef RingElement c, inv + parent = self._parent + if db < 0: + sig_off() + raise ZeroDivisionError + if da < db: + (self).__coeffs = [ ] + else: + inv = ~b[db] + for i from da-db >= i >= 0: + c = a[i+db] = parent.twist_map(-db)(inv*a[i+db]) + if i < db: deb = db + else: deb = i + for j from deb <= j < db+i: + a[j] -= b[j-i] * parent.twist_map(j-i)(c) + del a[:db] + self.__normalize() + + cdef void _inplace_rfloordiv(self, SkewPolynomial_finite_field_dense other): + """ + Replace self by the quotient in the right euclidean division + of self by other (only for internal use). + """ + cdef list a = (self).__coeffs + cdef list b = (other).__coeffs + cdef Py_ssize_t da = len(a)-1, db = len(b)-1 + cdef Py_ssize_t i, j, deb, order + cdef RingElement c, x, inv + parent = self._parent + if db < 0: + raise ZeroDivisionError + if da < db: + (self).__coeffs = [ ] + else: + order = parent._order + inv = ~b[db] + twinv = [ inv ] + for i from 0 <= i < min(da-db,order-1): + twinv.append(parent.twist_map()(twinv[i])) + twb = (other)._conjugates + for i from len(twb)-1 <= i < min(da-db,order-1): + twb.append([ parent.twist_map()(x) for x in twb[i] ]) + for i from da-db >= i >= 0: + c = a[i+db] = twinv[i%order] * a[i+db] + if i < db: deb = db + else: deb = i + for j from deb <= j < db+i: + a[j] -= c * twb[i%order][j-i] + del a[:db] + self.__normalize() + + cdef void _inplace_lmonic(self): + """ + Replace self by ``self.lmonic()`` (only for internal use). + """ + cdef list a = (self).__coeffs + cdef Py_ssize_t da = len(a)-1, i + cdef RingElement inv = ~a[da] + parent = self._parent + a[da] = parent.base_ring()(1) + for i from 0 <= i < da: + a[i] *= parent.twist_map(i-da)(inv) + + cdef void _inplace_rmonic(self): + """ + Replace self by ``self.rmonic()`` (only for internal use). + """ + cdef list a = (self).__coeffs + cdef Py_ssize_t da = len(a)-1, i + cdef RingElement inv = ~a[da] + a[da] = self._parent.base_ring()(1) + for i from 0 <= i < da: + a[i] *= inv + + cdef void _inplace_rgcd(self,SkewPolynomial_finite_field_dense other): + """ + Replace self by its right gcd with other (only for internal use). + """ + cdef SkewPolynomial_finite_field_dense B + cdef list swap + if len(other.__coeffs): + B = self._new_c(other.__coeffs[:],other._parent) + while len(B.__coeffs): + B._conjugates = [ B.__coeffs ] + self._inplace_rrem(B) + swap = self.__coeffs + self.__coeffs = B.__coeffs + B.__coeffs = swap + + + cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other): + """ + Replace self by the remainder in the right euclidean division + of self by other and return the quotient (only for internal use). + """ + cdef list a = (self).__coeffs + cdef list b = (other).__coeffs + cdef Py_ssize_t da = len(a)-1, db = len(b)-1 + cdef Py_ssize_t i, j + cdef RingElement c, inv + cdef list q + parent = self._parent + if db < 0: + raise ZeroDivisionError + if da < db: + r = self._new_c([],self._parent) + return r + inv = ~b[db] + q = [ ] + for i from da-db >= i >= 0: + c = parent.twist_map(i)(inv) * a[i+db] + q.append(c) + for j from 0 <= j < db: + a[i+j] -= c * parent.twist_map(i)(b[j]) + del a[db:] + self.__normalize() + q.reverse() + r = self._new_c(q,self._parent) + return r + + cdef Py_ssize_t _val_inplace_unit(self): + """ + Return `v` the valuation of self and replace self by + self >> v (only for internal use). + """ + cdef list a = (self).__coeffs + cdef Py_ssize_t val = 0 + if len(a) < 0: + sig_off() + return -1 + while a[0].is_zero(): + del a[0] + val += 1 + return val + + cdef Matrix_dense _matmul_c(self): + r""" + Return the matrix of the multiplication by self on + `K[X,\sigma]` considered as a free module over `K[X^r]` + (here `r` is the order of `\sigma`). + + .. WARNING:: + + Does not work if self is not monic. + """ + cdef Py_ssize_t i, j, deb, k, r = self.parent()._order + cdef Py_ssize_t d = self.degree () + cdef Ring base_ring = self.parent().base_ring() + cdef RingElement minusone = base_ring(-1) + cdef RingElement zero = base_ring(0) + cdef Polk = PolynomialRing (base_ring, 'xr') + cdef Matrix_dense M = zero_matrix(Polk,r,r) + cdef list l = self.list() + for j from 0 <= j < r: + for i from 0 <= i < r: + if i < j: + pol = [ zero ] + deb = i-j+r + else: + pol = [ ] + deb = i-j + for k from deb <= k <= d by r: + pol.append(l[k]) + M.set_unsafe(i,j,Polk(pol)) + for i from 0 <= i <= d: + l[i] = self._parent.twist_map()(l[i]) + return M From f4b841336eb6c557013e3684668dbd5db851c343 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 25 Jul 2016 05:26:05 +0530 Subject: [PATCH 02/37] added missing documentation and some tests. added class SkewPolynomialRing_finite_field. --- .../skew_polynomial_finite_field.pyx | 117 +++++------------- .../rings/polynomial/skew_polynomial_ring.py | 96 ++++++++++++++ 2 files changed, 130 insertions(+), 83 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index ac734eccbf3..3662d6059cd 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -7,6 +7,14 @@ Put `S = k[X,\sigma]`: as an addtive group, it is the usual ring of polynomials with coefficients in `k` and the multiplication on `S` is defined by the rule `X * a = \sigma(a) * X`. +.. SEE ALSO:: + + - ``Class SkewPolynomial_generic_dense`` and ``Class SkewPolynomial`` + in sage.rings.polynomial.skew_polynomial_element + + - ``Class SkewPolynomialRing`` and ``Class SkewPolynomialRing_finite_field`` + in sage.rings.polynomial.skew_polynomial_ring + We recall that: #. `S` is a left (resp. right) euclidean noncommutative ring @@ -21,8 +29,6 @@ We recall that: EXAMPLES:: -We illustrate some properties listed above:: - sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob]; S @@ -44,101 +50,46 @@ AUTHOR:: cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): def __init__(self, parent, x=None, int check=1, is_gen=False, int construct=0, **kwds): - SkewPolynomial_generic_dense.__init__ (self, parent, x, check, is_gen, construct, **kwds) - - cdef SkewPolynomial _new_c(self, list coeffs, Parent P, char check=0): - """ - Fast creation of a new skew polynomial """ - cdef type t = type(self) - cdef SkewPolynomial_finite_field_dense f = t.__new__(t) - f._parent = P - f.__coeffs = coeffs - if check: - f.__normalize() - return f - - def rquo_rem(self, other): - """ - DEFINITION: - - Let `a` and `b` be two skew polynomials over the same - ring. The *right euclidean division* of `a` by `b` is - a couple `(q,r)` such that - - - `a = q*b + r` + This method constructs a generic dense skew polynomial over finite field. + + INPUT:: + + - ``parent`` -- parent of `self` - - the degree of `r` is less than the degree of `b` + - ``x`` -- list of coefficients from which `self` can be constructed - `q` (resp. `r`) is called the *quotient* (resp. the - remainder) of this euclidean division. + - ``check`` -- flag variable to normalize the polynomial - If the leading coefficient of `b` is a unit (e.g. if - `b` is monic) then `q` and `r` exist and are unique. + - ``is_gen`` -- boolean (default: False) - INPUT: + - ``construct`` -- boolean (default: False) - - ``other`` -- a skew polynomial ring over the same - base ring + TESTS:: - OUTPUT: + 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 - - the quotient and the remainder of the left euclidean - division of this skew polynomial by ``other`` + We create a skew polynomial from a list:: - .. NOTE:: + sage: S([t,1]) + x + t - Doesn't work if the leading coefficient of the divisor - is not a unit. + from another skew polynomial:: - EXAMPLES:: + sage: S(x^2 + t) + x^2 + t - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: S. = R['x',sigma] - sage: a = S.random_element(degree=4); a - t^2*x^4 + (-12*t^2 - 2*t - 1)*x^3 + (-95*t^2 + t + 2)*x^2 + (-t^2 + t)*x + 2*t - 8 - sage: b = S.random_element(monic=True); b - x^2 + (4*t^2 - t - 2)*x - t^2 + t - 1 - sage: q,r = a.right_quo_rem(b) - sage: a == q*b + r + from a constant:: + + sage: x = S(t^2 + 1); x + t^2 + 1 + sage: x.parent() is S True - - The leading coefficient of the divisor need to be invertible:: - - sage: c = S.random_element(); c - (-4*t^2 + t)*x^2 - 2*t^2*x + 5*t^2 - 6*t - 4 - sage: a.right_quo_rem(c) - Traceback (most recent call last): - ... - NotImplementedError: the leading coefficient of the divisor is not invertible """ - cdef list a = self.list() - cdef list b = other.list() - cdef Py_ssize_t i, j - cdef Py_ssize_t da = len(a)-1, db = len(b)-1 - parent = self._parent - if db < 0: - raise ZeroDivisionError - if da < db: - res = parent(0), self - return res - cdef RingElement inv = ~b[db] - cdef list q = [ ] - cdef Py_ssize_t order = parent._order - cdef list twinv = [ inv ], twb = [ b ] - cdef RingElement c, x - for i from 0 <= i < min(da-db,order-1): - twinv.append(parent.twist_map()(twinv[i])) - twb.append([ parent.twist_map()(x) for x in twb[i] ]) - for i from da-db >= i >= 0: - c = twinv[i%order] * a[i+db] - for j from 0 <= j < db: - a[i+j] -= c * twb[i%order][j] - q.append(c) - q.reverse() - res = parent(q), parent(a[:db]) - return res + SkewPolynomial_generic_dense.__init__ (self, parent, x, check, is_gen, construct, **kwds) cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other): """ diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index a77bb097faf..63a6be9710c 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -657,3 +657,99 @@ def is_commutative(self): True """ return self.twist_map().is_identity() + +class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): + """ + A specific class for skew polynomial rings over finite field. + """ + @staticmethod + def __classcall__(cls, base_ring, map, name=None, sparse=False, element_class=None): + if not element_class: + if sparse: + raise NotImplementedError("sparse skew polynomials are not implemented") + else: + from sage.rings.polynomial import skew_polynomial_finite_field + element_class = skew_polynomial_finite_field.SkewPolynomial_finite_field_dense + return super(SkewPolynomialRing_general,cls).__classcall__(cls,base_ring,map,name,sparse,element_class) + + def __init__(self, base_ring, map, name, sparse, element_class): + """ + This method is a constructor for a general, dense univariate skew polynomial ring + over a finite field. + + INPUT:: + + - ``base_ring`` -- a commutative ring + + - ``map`` -- an automorphism of the base ring + + - ``name`` -- string or list of strings representing the name of the variables of ring + + - ``sparse`` -- boolean (default: False) + + - ``element_class`` -- class representing the type of element to be used in ring + + ..NOTE:: + + Multivariate and Sparse rings are not implemented. + + EXAMPLES:: + + 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 + sage: S([1]) + S([-1]) + 0 + 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 + """ + self._order = -1 + try: + self._order = map.order() + except (AttributeError,NotImplementedError): + pass + if self._order < 0: + try: + if map.is_identity(): + self._order = 1 + except (AttributeError,NotImplementedError): + pass + if self._order < 0: + raise NotImplementedError("Unable to determine the order of %s" % map) + SkewPolynomialRing_general.__init__ (self, base_ring, map, name, sparse, element_class) + self._maps = [ map**i for i in range(self._order) ] + + def twist_map(self,n=1): + """ + Return the twist map (eventually iterated several times) used to define + this skew polynomial ring. + + INPUT: + + - ``n`` - a relative integer (default: 1) + + OUTPUT: + + - The `n`-th iterative of the twist map of this skew polynomial ring. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: S.twist_map() + Frobenius endomorphism t |--> t^5 on Finite Field in t of size 5^3 + sage: S.twist_map(11) + Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 + sage: S.twist_map(3) + Identity endomorphism of Finite Field in t of size 5^3 + + It also works if `n` is negative:: + + sage: S.twist_map(-1) + Frobenius endomorphism t |--> t^(5^2) on Finite Field in t of size 5^3 + """ + return self._maps[n%self._order] From d49392ef39ca1d25cf48be649f0c2540b6e6cc48 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 25 Jul 2016 17:11:56 +0530 Subject: [PATCH 03/37] resolved single and double back tick formatting inconsistencies. --- .../polynomial/skew_polynomial_element.pyx | 152 +++++++++--------- 1 file changed, 80 insertions(+), 72 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 70ad51f03d6..abd1dccfd81 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -205,11 +205,11 @@ cdef class SkewPolynomial(AlgebraElement): INPUT:: - - ``parent`` -- parent of `self` + - ``parent`` -- parent of ``self`` - - ``is_gen`` -- boolean (default: False) + - ``is_gen`` -- boolean (default: ``False``) - - ``construct`` -- boolean (default: False) + - ``construct`` -- boolean (default: ``False``) The following examples illustrate the creation of elements of skew polynomial rings. @@ -259,7 +259,7 @@ cdef class SkewPolynomial(AlgebraElement): def __hash__(self): """ - Return hash of `self`. + Return hash of ``self``. EXAMPLES:: @@ -289,7 +289,7 @@ cdef class SkewPolynomial(AlgebraElement): cpdef int _cmp_(self, other) except -2: """ - Compare the two skew polynomials self and other. + 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. @@ -344,7 +344,7 @@ cdef class SkewPolynomial(AlgebraElement): def list(self): """ - Return a new copy of the list of the underlying elements of self. + Return a new copy of the list of the underlying elements of ``self``. EXAMPLES:: @@ -369,7 +369,15 @@ cdef class SkewPolynomial(AlgebraElement): def __call__(self, eval_pt): """ - Evaluate this polynomial. + Evaluate this polynomial. The value of a given a skew polynomial + `p` of degree `d` at ring element `elem`, is calculated using the + formula: + + p(elem) = \sum_{i=0}^{d} (coefficients[i])*sigma^{i}(elem) + + where `coefficients` is the list of coefficients of `p` and `sigma` + is the base ring automorphism. This is called the operator evaluation + method. INPUT: @@ -424,7 +432,7 @@ cdef class SkewPolynomial(AlgebraElement): def __iter__(self): """ - Return list iterator object of the list of coefficients of self. + Return list iterator object of the list of coefficients of ``self``. EXAMPLE:: @@ -439,7 +447,7 @@ cdef class SkewPolynomial(AlgebraElement): def __getitem__(self, n): """ - Return the 'n'-th coefficient of self. + Return the `n`-th coefficient of ``self``. INPUT: @@ -447,7 +455,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - - the `n`-th coefficient of self + - the ``n``-th coefficient of ``self`` .. NOTE:: @@ -473,7 +481,7 @@ cdef class SkewPolynomial(AlgebraElement): def __getslice__(self, Py_ssize_t i, Py_ssize_t j): """ - Return a specific portion of self. + Return a specific portion of ``self``. .. NOTE:: @@ -611,7 +619,7 @@ cdef class SkewPolynomial(AlgebraElement): cpdef _sub_(self, right): """ - Subtract polynomial `right` from self. + Subtract polynomial ``right`` from ``self``. EXAMPLES:: @@ -643,7 +651,7 @@ cdef class SkewPolynomial(AlgebraElement): cpdef _neg_(self): """ - Return the negative of self. + Return the negative of ``self``. EXAMPLES:: @@ -659,11 +667,11 @@ cdef class SkewPolynomial(AlgebraElement): cpdef ModuleElement _lmul_(self, RingElement right): """ - Multiply self on the right by scalar. + Multiply ``self`` on the right by scalar. INPUT: - - right -- an element of the base ring + - ``right`` -- an element of the base ring EXAMPLES:: @@ -687,11 +695,11 @@ cdef class SkewPolynomial(AlgebraElement): cpdef ModuleElement _rmul_(self, RingElement left): """ - Multiply self on the left by scalar. + Multiply ``self`` on the left by scalar. INPUT: - - left -- an element of the base ring + - ``left`` -- an element of the base ring EXAMPLES:: @@ -715,11 +723,11 @@ cdef class SkewPolynomial(AlgebraElement): cpdef _mul_(self, right): """ - Multiply self on the right by a skew polynomial. + Multiply ``self`` on the right by a skew polynomial. INPUT: - - right -- a skew polynomial in the same ring + - ``right`` -- a skew polynomial in the same ring EXAMPLES:: @@ -761,7 +769,7 @@ cdef class SkewPolynomial(AlgebraElement): def square(self): """ - Return the square of self. + Return the square of ``self``. EXAMPLES:: @@ -785,7 +793,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - - this skew polynomial conjugated by x^n (where x is + - this skew polynomial conjugated by `x^n` (where x is the variable) .. NOTE:: @@ -913,7 +921,7 @@ cdef class SkewPolynomial(AlgebraElement): def truncate(self, n): """ Returns the polynomial of degree ` < n` which is equivalent - to self modulo `x^n`. + to ``self`` modulo `x^n`. EXAMPLES:: @@ -1218,9 +1226,9 @@ cdef class SkewPolynomial(AlgebraElement): def __floordiv__(self, right): """ - Return the quotient of the right euclidean division of self by right. + Return the quotient of the right euclidean division of ``self`` by right. - The algorithm fails if the leading coefficient of the divisor (right) + The algorithm fails if the leading coefficient of the divisor (``right``) is not invertible. .. SEEALSO:: @@ -1279,7 +1287,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - Return True iff self is divisible by other on the left + Return True iff ``self`` is divisible by ``other`` on the left EXAMPLES:: @@ -1312,7 +1320,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - Return True iff self is divisible by other on the right + Return True iff ``self`` is divisible by ``other`` on the right EXAMPLES:: @@ -1359,7 +1367,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - Return True iff self divides other on the left + Return True iff ``self`` divides ``other`` on the left EXAMPLES:: @@ -1392,7 +1400,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - Return True iff self divides other on the right + Return True iff ``self`` divides ``other`` on the right EXAMPLES:: @@ -1442,7 +1450,7 @@ cdef class SkewPolynomial(AlgebraElement): OUTPUT: - - The left gcd of self and other, that is a skew polynomial + - 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``. @@ -1535,14 +1543,14 @@ cdef class SkewPolynomial(AlgebraElement): """ INPUT: - - ``other`` -- an other skew polynomial over the same + - ``other`` -- another skew polynomial over the same base - ``monic`` -- boolean (default: True) OUTPUT: - - The right gcd of self and other, that is a skew polynomial + - 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``. @@ -1619,14 +1627,14 @@ cdef class SkewPolynomial(AlgebraElement): """ INPUT: - - ``other`` -- an other skew polynomial over the same + - ``other`` -- another skew polynomial over the same base - ``monic`` -- boolean (default: True) OUTPUT: - - The right gcd of self and other, that is a skew polynomial + - 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``. @@ -1681,14 +1689,14 @@ cdef class SkewPolynomial(AlgebraElement): """ INPUT: - - ``other`` -- an other skew polynomial over the same + - ``other`` -- another skew polynomial over the same base - ``monic`` -- boolean (default: True) OUTPUT: - - The left gcd of self and other, that is a skew polynomial + - 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``. @@ -1760,14 +1768,14 @@ cdef class SkewPolynomial(AlgebraElement): """ INPUT: - - ``other`` -- an other skew polynomial over the same + - ``other`` -- another skew polynomial over the same base - ``monic`` -- boolean (default: True) OUTPUT: - - The left lcm of self and other, that is a skew polynomial + - 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*. @@ -1836,14 +1844,14 @@ cdef class SkewPolynomial(AlgebraElement): """ INPUT: - - ``other`` -- an other skew polynomial over the same + - ``other`` -- another skew polynomial over the same base - ``monic`` -- boolean (default: True) OUTPUT: - - The right lcm of self and other, that is a skew polynomial + - 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*. @@ -2198,7 +2206,7 @@ cdef class SkewPolynomial(AlgebraElement): def is_term(self): """ - Return True if self is an element of the base ring times a + Return True if ``self`` is an element of the base ring times a power of the generator. EXAMPLES:: @@ -2224,7 +2232,7 @@ cdef class SkewPolynomial(AlgebraElement): def is_monomial(self): """ - Returns True if self is a monomial, i.e., a power of the generator. + Returns True if ``self`` is a monomial, i.e., a power of the generator. EXAMPLES:: @@ -2258,7 +2266,7 @@ cdef class SkewPolynomial(AlgebraElement): def is_gen(self): """ - Return True if `self` is the distinguished generator + Return True if ``self`` is the distinguished generator of the parent skew polynomial ring. EXAMPLES:: @@ -2274,8 +2282,8 @@ cdef class SkewPolynomial(AlgebraElement): .. NOTE:: - This function does not return True if `self` equals - the generator; it returns True only *if* `self` is + This function does not return True if ``self`` equals + the generator; it returns True only *if* ``self`` is the generator. sage: b = S([0,1]) @@ -2290,7 +2298,7 @@ cdef class SkewPolynomial(AlgebraElement): def coefficients(self, sparse=True): """ - Return the coefficients of the monomials appearing in self. + Return the coefficients of the monomials appearing in ``self``. If ``sparse=True`` (the default), it returns only the non-zero coefficients. Otherwise, it returns the same value as ``self.list()``. (In this case, it may be slightly faster to invoke ``self.list()`` directly.) @@ -2314,7 +2322,7 @@ cdef class SkewPolynomial(AlgebraElement): def number_of_terms(self): """ - Returns the number of non-zero coefficients of self. Also called weight, + Returns the number of non-zero coefficients of ``self``. Also called weight, hamming weight or sparsity. EXAMPLES:: @@ -2383,8 +2391,8 @@ cdef class SkewPolynomial(AlgebraElement): def __copy__(self): """ - Return a "copy" of self. In Sage, since skew polynomials - are immutable, this just returns self again. + Return a "copy" of ``self``. In Sage, since skew polynomials + are immutable, this just returns ``self`` again. EXAMPLES:: @@ -2416,7 +2424,7 @@ cdef class SkewPolynomial(AlgebraElement): def mod(self, other): """ - Remainder of division of self by other. + Remainder of division of ``self`` by ``other``. EXAMPLES:: @@ -2434,7 +2442,7 @@ cdef class SkewPolynomial(AlgebraElement): def is_constant(self): """ - Return True if this is a constant polynomial. + Return True if ``self`` is a constant polynomial. OUTPUT: @@ -2454,7 +2462,7 @@ cdef class SkewPolynomial(AlgebraElement): def exponents(self): """ - Return the exponents of the monomials appearing in self. + Return the exponents of the monomials appearing in ``self``. EXAMPLES:: @@ -2470,7 +2478,7 @@ cdef class SkewPolynomial(AlgebraElement): def prec(self): """ - Return the precision of this polynomial. This is always infinity, + Return the precision of ``self``. This is always infinity, since polynomials are of infinite precision by definition (there is no big-oh). @@ -2486,7 +2494,7 @@ cdef class SkewPolynomial(AlgebraElement): def padded_list(self, n=None): """ - Return list of coefficients of self up to (but not including) + Return list of coefficients of ``self`` up to (but not including) `q^n`. Includes 0's in the list on the right so that the list has length @@ -2495,7 +2503,7 @@ cdef class SkewPolynomial(AlgebraElement): INPUT: - - ``n`` - (default: None); if given, an integer that + - ``n`` - (default: ``None``); if given, an integer that is at least 0 @@ -2533,7 +2541,7 @@ cdef class SkewPolynomial(AlgebraElement): def variable_name(self): """ - Return name of variable used in this polynomial as a string. + Return name of variable used in ``self`` as a string. EXAMPLES:: @@ -2558,15 +2566,15 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): INPUT:: - - ``parent`` -- parent of `self` + - ``parent`` -- parent of ``self`` - - ``x`` -- list of coefficients from which `self` can be constructed + - ``x`` -- list of coefficients from which ``self`` can be constructed - ``check`` -- flag variable to normalize the polynomial - - ``is_gen`` -- boolean (default: False) + - ``is_gen`` -- boolean (default: ``False``) - - ``construct`` -- boolean (default: False) + - ``construct`` -- boolean (default: ``False``) TESTS:: @@ -2640,7 +2648,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef list _list_c(self): """ - Return the list of the underlying elements of self. + Return the list of the underlying elements of ``self``. .. WARNING:: @@ -2662,7 +2670,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef void __normalize(self): """ - Remove higher order 0-coefficients from the representation of self. + Remove higher order 0-coefficients from the representation of ``self``. """ x = self.__coeffs cdef Py_ssize_t n = len(x) - 1 @@ -2672,7 +2680,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef void _inplace_add(self, SkewPolynomial_generic_dense right): """ - Replace self by self+right (only for internal use). + Replace ``self`` by `self+right` (only for internal use). """ cdef Py_ssize_t i, min x = (self).__coeffs @@ -2690,7 +2698,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef void _inplace_sub(self, SkewPolynomial_generic_dense right): """ - Replace self by self-right (only for internal use). + Replace ``self`` by `self-right` (only for internal use). """ cdef Py_ssize_t i, min cdef list x = (self).__coeffs @@ -2707,7 +2715,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef void _inplace_rmul(self, SkewPolynomial_generic_dense right): """ - Replace self by self*right (only for internal use). + Replace ``self`` by `self*right` (only for internal use). """ cdef list x = (self).__coeffs cdef list y = (right).__coeffs @@ -2733,7 +2741,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef void _inplace_lmul(self, SkewPolynomial_generic_dense left): """ - Replace self by left*self (only for internal use). + Replace ``self`` by `left*self` (only for internal use). """ cdef list x = (self).__coeffs cdef list y = (left).__coeffs @@ -2759,7 +2767,7 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): cdef void _inplace_pow(self, Py_ssize_t n): """ - Replace self by self**n. + Replace ``self`` by `self**n`. """ while n & 1 == 0: self._inplace_rmul(self) @@ -2778,13 +2786,13 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): - ``exp`` -- an Integer - - ``modulus`` -- a skew polynomial over the same ring (default: None) + - ``modulus`` -- a skew polynomial over the same ring (default: ``None``) OUTPUT: If ``modulus`` is None, return ``self**exp``. - Otherwise, return the remainder of self**exp in the left + Otherwise, return the remainder of ``self**exp`` in the left euclidean division by ``modulus``. REMARK: @@ -2860,11 +2868,11 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): - ``exp`` -- an Integer - - ``modulus`` -- a skew polynomial over the same ring (default: None) + - ``modulus`` -- a skew polynomial over the same ring (default: ``None``) OUTPUT: - If ``modulus`` is None, return ``self**exp``. + If ``modulus`` is ``None``, return ``self**exp``. Otherwise, return the remainder of self**exp in the right euclidean division by ``modulus``. @@ -2945,11 +2953,11 @@ cdef class SkewPolynomial_generic_dense(SkewPolynomial): - ``exp`` -- an Integer - ``modulus`` -- a skew polynomial over the same ring - (default: None) + (default: ``None``) OUTPUT: - If ``modulus`` is None, return ``self**exp``. + If ``modulus`` is ``None``, return ``self**exp``. Otherwise, return the remainder of self**exp in the right euclidean division by ``modulus``. From 1a06b095a569eba51bf2efdc2f86712cfa7b49aa Mon Sep 17 00:00:00 2001 From: arpitdm Date: Tue, 26 Jul 2016 00:02:47 +0530 Subject: [PATCH 04/37] added methods for multi-point evaluation, minimum subspace polynomial and interpolation --- .../rings/polynomial/skew_polynomial_ring.py | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 63a6be9710c..07a3a99d127 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -658,6 +658,56 @@ def is_commutative(self): """ return self.twist_map().is_identity() + def minimum_subspace_polynomial(self, eval_pts): + x = self([0, 1]) + q = self.base_ring().characteristic() + if len(eval_pts) == 1: + if eval_pts[0] == self.zero(): + return pow(x, pow(q, 0)) + else: + return pow(x, pow(q, 1)) - pow(eval_pts[0], q-1) * pow(x, pow(q, 0)) + else: + A = eval_pts[:len(eval_pts)/2] + B = eval_pts[(len(eval_pts)/2):] + M_A = self.minimum_subspace_polynomial(A) + M_A_B = self.multi_point_evaluation(M_A, B) + M_M_A_B = self.minimum_subspace_polynomial(M_A_B) + return M_M_A_B * M_A + + def multi_point_evaluation(self, p, eval_pts): + coefficients = p.list() + q = self.base_ring().characteristic() + if len(eval_pts) == 1: + y = [] + y.append(coefficients[1]*pow(eval_pts[0], pow(q, 1)) + coefficients[0]*pow(eval_pts[0], pow(q, 0))) + return y + else: + A = eval_pts[:len(eval_pts)/2] + B = eval_pts[(len(eval_pts)/2):] + M_A = self.minimum_subspace_polynomial(A) + M_B = self.minimum_subspace_polynomial(B) + Q_A, R_A = p.right_quo_rem(M_A) + Q_B, R_B = p.right_quo_rem(M_B) + r = list(set(self.multi_point_evaluation(R_A, A)).union(set(self.multi_point_evaluation(R_B, B)))) + return list(set(self.multi_point_evaluation(R_A, A)).union(set(self.multi_point_evaluation(R_B, B)))) + + def interpolation_polynomial(self, eval_pts, values): + x = self([0, 1]) + q = self.base_ring().characteristic() + if len(values) == 1: + c, _ = values[0].quo_rem(eval_pts[0]) + return c*pow(x, pow(q, 0)) + else: + A = eval_pts[:len(eval_pts)/2] + B = eval_pts[(len(eval_pts)/2):] + M_A = self.minimum_subspace_polynomial(A) + M_B = self.minimum_subspace_polynomial(B) + A_ = self.multi_point_evaluation(M_B, A) + B_ = self.multi_point_evaluation(M_A, B) + I_1 = self.interpolation_polynomial(A_, values[:len(eval_pts)/2]) + I_2 = self.interpolation_polynomial(B_, values[(len(eval_pts)/2):]) + return I_1 * M_B + I_2 * M_A + class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ A specific class for skew polynomial rings over finite field. From eaca2535762165b7ace9896a3993d8ae7fb03eb8 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Thu, 28 Jul 2016 21:11:03 +0530 Subject: [PATCH 05/37] integrated skew polynomial finite field into sage. removed some compile and doctest errors. --- src/module_list.py | 3 + .../skew_polynomial_finite_field.pxd | 3 + .../skew_polynomial_finite_field.pyx | 65 +++++++++++-------- .../rings/polynomial/skew_polynomial_ring.py | 50 -------------- .../skew_polynomial_ring_constructor.py | 9 ++- 5 files changed, 49 insertions(+), 81 deletions(-) diff --git a/src/module_list.py b/src/module_list.py index c75f26bae9b..02c4ff6eb43 100644 --- a/src/module_list.py +++ b/src/module_list.py @@ -1796,6 +1796,9 @@ def uname_specific(name, value, alternative): Extension('sage.rings.polynomial.skew_polynomial_element', sources = ['sage/rings/polynomial/skew_polynomial_element.pyx']), + Extension('sage.rings.polynomial.skew_polynomial_finite_field', + sources = ['sage/rings/polynomial/skew_polynomial_finite_field.pyx']), + ################################ ## ## sage.tests diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index d927743be70..63d5760543b 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -18,3 +18,6 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other) cdef Matrix_dense _matmul_c(self) + + cpdef _leftpow_(self, exp, modulus=*) + cpdef _rightpow_(self, exp, modulus=*) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 3662d6059cd..2b7bf0d0edd 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -47,6 +47,15 @@ AUTHOR:: # http://www.gnu.org/licenses/ #**************************************************************************** +include "../../ext/stdsage.pxi" + +import copy +import cysignals +from sage.matrix.constructor import zero_matrix +from sage.rings.ring cimport Ring +from polynomial_ring_constructor import PolynomialRing +from skew_polynomial_element cimport SkewPolynomial_generic_dense + cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): def __init__(self, parent, x=None, int check=1, is_gen=False, int construct=0, **kwds): @@ -98,10 +107,10 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef SkewPolynomial_finite_field_dense A = self cdef SkewPolynomial_finite_field_dense B = other cdef SkewPolynomial_finite_field_dense swap - if len(B.__coeffs): - A = self._new_c(A.__coeffs[:],A._parent) - B = B._new_c(B.__coeffs[:],B._parent) - while len(B.__coeffs): + if len(B._coeffs): + A = self._new_c(A._coeffs[:],A._parent) + B = B._new_c(B._coeffs[:],B._parent) + while len(B._coeffs): A._inplace_rrem(B) swap = A; A = B; B = swap return A @@ -195,7 +204,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): mod = self.parent()(mod.bound()) except NotImplementedError: mod = None - r = self._new_c(copy.copy(self.__coeffs),self._parent) + r = self._new_c(copy.copy(self._coeffs),self._parent) if mod: r._inplace_pow_mod(exp,mod) else: @@ -293,7 +302,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): mod = self.parent()(mod.bound()) except NotImplementedError: mod = None - r = self._new_c(copy.copy(self.__coeffs),self._parent) + r = self._new_c(copy.copy(self._coeffs),self._parent) if mod: r._inplace_pow_mod(exp,mod) else: @@ -307,8 +316,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): Replace self by the remainder in the left euclidean division of self by other (only for internal use). """ - cdef list a = (self).__coeffs - cdef list b = (other).__coeffs + cdef list a = (self)._coeffs + cdef list b = (other)._coeffs cdef Py_ssize_t da = len(a)-1, db = len(b)-1 cdef Py_ssize_t i, j cdef RingElement c, inv @@ -329,8 +338,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): Replace self by the remainder in the right euclidean division of self by other (only for internal use). """ - cdef list a = (self).__coeffs - cdef list b = (other).__coeffs + cdef list a = (self)._coeffs + cdef list b = (other)._coeffs cdef Py_ssize_t da = len(a)-1, db = len(b)-1 cdef Py_ssize_t i, j, order cdef RingElement c, x, inv @@ -359,8 +368,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): Replace self by the quotient in the left euclidean division of self by other (only for internal use). """ - cdef list a = (self).__coeffs - cdef list b = (other).__coeffs + cdef list a = (self)._coeffs + cdef list b = (other)._coeffs cdef Py_ssize_t da = len(a)-1, db = len(b)-1 cdef Py_ssize_t i, j, deb cdef RingElement c, inv @@ -369,7 +378,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): sig_off() raise ZeroDivisionError if da < db: - (self).__coeffs = [ ] + (self)._coeffs = [ ] else: inv = ~b[db] for i from da-db >= i >= 0: @@ -386,8 +395,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): Replace self by the quotient in the right euclidean division of self by other (only for internal use). """ - cdef list a = (self).__coeffs - cdef list b = (other).__coeffs + cdef list a = (self)._coeffs + cdef list b = (other)._coeffs cdef Py_ssize_t da = len(a)-1, db = len(b)-1 cdef Py_ssize_t i, j, deb, order cdef RingElement c, x, inv @@ -395,7 +404,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): if db < 0: raise ZeroDivisionError if da < db: - (self).__coeffs = [ ] + (self)._coeffs = [ ] else: order = parent._order inv = ~b[db] @@ -418,7 +427,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): """ Replace self by ``self.lmonic()`` (only for internal use). """ - cdef list a = (self).__coeffs + cdef list a = (self)._coeffs cdef Py_ssize_t da = len(a)-1, i cdef RingElement inv = ~a[da] parent = self._parent @@ -430,7 +439,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): """ Replace self by ``self.rmonic()`` (only for internal use). """ - cdef list a = (self).__coeffs + cdef list a = (self)._coeffs cdef Py_ssize_t da = len(a)-1, i cdef RingElement inv = ~a[da] a[da] = self._parent.base_ring()(1) @@ -443,14 +452,14 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): """ cdef SkewPolynomial_finite_field_dense B cdef list swap - if len(other.__coeffs): - B = self._new_c(other.__coeffs[:],other._parent) - while len(B.__coeffs): - B._conjugates = [ B.__coeffs ] + if len(other._coeffs): + B = self._new_c(other._coeffs[:],other._parent) + while len(B._coeffs): + B._conjugates = [ B._coeffs ] self._inplace_rrem(B) - swap = self.__coeffs - self.__coeffs = B.__coeffs - B.__coeffs = swap + swap = self._coeffs + self._coeffs = B._coeffs + B._coeffs = swap cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other): @@ -458,8 +467,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): Replace self by the remainder in the right euclidean division of self by other and return the quotient (only for internal use). """ - cdef list a = (self).__coeffs - cdef list b = (other).__coeffs + cdef list a = (self)._coeffs + cdef list b = (other)._coeffs cdef Py_ssize_t da = len(a)-1, db = len(b)-1 cdef Py_ssize_t i, j cdef RingElement c, inv @@ -488,7 +497,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): Return `v` the valuation of self and replace self by self >> v (only for internal use). """ - cdef list a = (self).__coeffs + cdef list a = (self)._coeffs cdef Py_ssize_t val = 0 if len(a) < 0: sig_off() diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 08e35c9c9e0..ac766642ffc 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -677,56 +677,6 @@ def is_commutative(self): """ return self.twist_map().is_identity() - def minimum_subspace_polynomial(self, eval_pts): - x = self([0, 1]) - q = self.base_ring().characteristic() - if len(eval_pts) == 1: - if eval_pts[0] == self.zero(): - return pow(x, pow(q, 0)) - else: - return pow(x, pow(q, 1)) - pow(eval_pts[0], q-1) * pow(x, pow(q, 0)) - else: - A = eval_pts[:len(eval_pts)/2] - B = eval_pts[(len(eval_pts)/2):] - M_A = self.minimum_subspace_polynomial(A) - M_A_B = self.multi_point_evaluation(M_A, B) - M_M_A_B = self.minimum_subspace_polynomial(M_A_B) - return M_M_A_B * M_A - - def multi_point_evaluation(self, p, eval_pts): - coefficients = p.list() - q = self.base_ring().characteristic() - if len(eval_pts) == 1: - y = [] - y.append(coefficients[1]*pow(eval_pts[0], pow(q, 1)) + coefficients[0]*pow(eval_pts[0], pow(q, 0))) - return y - else: - A = eval_pts[:len(eval_pts)/2] - B = eval_pts[(len(eval_pts)/2):] - M_A = self.minimum_subspace_polynomial(A) - M_B = self.minimum_subspace_polynomial(B) - Q_A, R_A = p.right_quo_rem(M_A) - Q_B, R_B = p.right_quo_rem(M_B) - r = list(set(self.multi_point_evaluation(R_A, A)).union(set(self.multi_point_evaluation(R_B, B)))) - return list(set(self.multi_point_evaluation(R_A, A)).union(set(self.multi_point_evaluation(R_B, B)))) - - def interpolation_polynomial(self, eval_pts, values): - x = self([0, 1]) - q = self.base_ring().characteristic() - if len(values) == 1: - c, _ = values[0].quo_rem(eval_pts[0]) - return c*pow(x, pow(q, 0)) - else: - A = eval_pts[:len(eval_pts)/2] - B = eval_pts[(len(eval_pts)/2):] - M_A = self.minimum_subspace_polynomial(A) - M_B = self.minimum_subspace_polynomial(B) - A_ = self.multi_point_evaluation(M_B, A) - B_ = self.multi_point_evaluation(M_A, B) - I_1 = self.interpolation_polynomial(A_, values[:len(eval_pts)/2]) - I_2 = self.interpolation_polynomial(B_, values[(len(eval_pts)/2):]) - return I_1 * M_B + I_2 * M_A - class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ A specific class for skew polynomial rings over finite field. diff --git a/src/sage/rings/polynomial/skew_polynomial_ring_constructor.py b/src/sage/rings/polynomial/skew_polynomial_ring_constructor.py index 189ccb69604..c226e720a61 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring_constructor.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring_constructor.py @@ -20,8 +20,9 @@ #**************************************************************************** import cysignals -from sage.structure.category_object import normalize_names import sage.rings.ring as ring +from sage.structure.category_object import normalize_names +from sage.rings.finite_rings.finite_field_base import is_FiniteField from sage.categories.morphism import Morphism, IdentityMorphism def SkewPolynomialRing(base_ring, base_ring_automorphism=None, names=None, sparse=False): @@ -195,6 +196,8 @@ def SkewPolynomialRing(base_ring, base_ring_automorphism=None, names=None, spars raise NotImplementedError("multivariate skew polynomials rings not supported.") import sage.rings.polynomial.skew_polynomial_ring - R = sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general(base_ring, base_ring_automorphism, names, sparse) - + if is_FiniteField(base_ring): + R = sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field(base_ring, base_ring_automorphism, names, sparse) + else: + R = sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general(base_ring, base_ring_automorphism, names, sparse) return R From 76640607bcbecccf6b969483cc112db06c6dd0d5 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Thu, 28 Jul 2016 21:14:49 +0530 Subject: [PATCH 06/37] removed leftpow and rightpow methods from SkewPolynomial_finite_field_dense class because they require the 'bound' method which in turn requires 'center'. this will be added in another separate ticket with the rest of the center stuff. --- .../skew_polynomial_finite_field.pxd | 3 - .../skew_polynomial_finite_field.pyx | 194 ------------------ 2 files changed, 197 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index 63d5760543b..d927743be70 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -18,6 +18,3 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other) cdef Matrix_dense _matmul_c(self) - - cpdef _leftpow_(self, exp, modulus=*) - cpdef _rightpow_(self, exp, modulus=*) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 2b7bf0d0edd..e2a5053a24b 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -117,200 +117,6 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): else: return self - cpdef _leftpow_(self, exp, modulus=None): - """ - INPUT: - - - ``exp`` -- an Integer - - - ``modulus`` -- a skew polynomial over the same ring (default: None) - - OUTPUT: - - If ``modulus`` is None, return ``self**exp``. - - Otherwise, return the 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``. - - However, if the base ring is a finite field, Sage uses the - following optimized algorithm: - - #. One first compute a central skew polynomial `N` which is - divisible by ``modulus``. (Since `N` lies in center, the - quotient `K[X,\sigma]/N` inherits a ring structure.) - - #. One compute ``self**exp`` in the quotient ring `K[X,\sigma]/N` - - #. One reduce modulo ``modulus`` the result computed in the - previous step - - EXAMPLES:: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: a = x + t - sage: b = a._leftpow_(10) - - sage: modulus = x^3 + t*x^2 + (t+3)*x - 2 - sage: br = a._leftpow_(10, modulus); br - (4*t^2 + 2*t + 3)*x^2 + (3*t^2 + 1)*x + 2*t + 3 - sage: lq, lr = b.left_quo_rem(modulus) - sage: br == lr - True - - sage: a._leftpow_(100, modulus) # rather fast - (4*t^2 + t + 1)*x^2 + (t^2 + 4*t + 1)*x + 3*t^2 + 3*t - """ - cdef SkewPolynomial_finite_field_dense r - - if not isinstance(exp, Integer) or isinstance(exp, int): - try: - exp = Integer(exp) - except TypeError: - raise TypeError("non-integral exponents not supported") - - if self.degree() <= 0: - r = self.parent()(self[0]**exp) - return r - if exp == 0: - r = self.parent()(1) - return r - if exp < 0: - r = (~self).leftpow(-exp,modulus) - return r - - if self == self.parent().gen(): # special case x**n should be faster! - P = self.parent() - R = P.base_ring() - v = [R.zero()]*exp + [R.one()] - r = self._new_c(v,self._parent) - if modulus: - _, r = r.left_quo_rem(modulus) - return r - - mod = modulus - if not modulus is None: - try: - mod = self.parent()(mod.bound()) - except NotImplementedError: - mod = None - r = self._new_c(copy.copy(self._coeffs),self._parent) - if mod: - r._inplace_pow_mod(exp,mod) - else: - r._inplace_pow(exp) - if (not modulus is None) and modulus != mod: - _, r = r.left_quo_rem(modulus) - return r - - cpdef _rightpow_(self, exp, modulus=None): - """ - INPUT: - - - ``exp`` -- an Integer - - - ``modulus`` -- a skew polynomial over the same ring (default: None) - - OUTPUT: - - If ``modulus`` is None, return ``self**exp``. - - Otherwise, return the 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``. - - However, if the base ring is a finite field, Sage uses the - following optimized algorithm: - - #. One first compute a central skew polynomial `N` which is - divisible by ``modulus``. (Since `N` lies in center, the - quotient `K[X,\sigma]/N` inherits a ring structure.) - - #. One compute ``self**exp`` in the quotient ring `K[X,\sigma]/N` - - #. One reduce modulo ``modulus`` the result computed in the - previous step - - 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._rightpow_(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._rightpow_(100, modulus) # rather fast - (2*t^2 + 3)*x^2 + (t^2 + 4*t + 2)*x + t^2 + 2*t + 1 - """ - cdef SkewPolynomial_finite_field_dense r - - if not isinstance(exp, Integer) or isinstance(exp, int): - try: - exp = Integer(exp) - except TypeError: - raise TypeError("non-integral exponents not supported") - - if self.degree() <= 0: - r = self.parent()(self[0]**exp) - return r - if exp == 0: - r = self.parent()(1) - return r - if exp < 0: - r = (~self).rightpow(-exp,modulus) - return r - - if self == self.parent().gen(): # special case x**n should be faster! - P = self.parent() - R = P.base_ring() - v = [R.zero()]*exp + [R.one()] - r = self._new_c(v,self._parent) - if modulus: - _, r = r.right_quo_rem(modulus) - return r - - mod = modulus - if not modulus is None: - try: - mod = self.parent()(mod.bound()) - except NotImplementedError: - mod = None - r = self._new_c(copy.copy(self._coeffs),self._parent) - if mod: - r._inplace_pow_mod(exp,mod) - else: - r._inplace_pow(exp) - if (not modulus is None) and modulus != mod: - _, r = r.right_quo_rem(modulus) - return r - cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other): """ Replace self by the remainder in the left euclidean division From a6e93e12c1fefffde3615c8eab75a45b3c277ab2 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Thu, 28 Jul 2016 21:52:37 +0530 Subject: [PATCH 07/37] added SEEALSO and TODO blocks and made small polishes to the documentation. --- .../skew_polynomial_finite_field.pyx | 6 +-- .../rings/polynomial/skew_polynomial_ring.py | 42 ++++++++++--------- 2 files changed, 26 insertions(+), 22 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index e2a5053a24b..a12ff4a07bf 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -61,9 +61,9 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): def __init__(self, parent, x=None, int check=1, is_gen=False, int construct=0, **kwds): """ This method constructs a generic dense skew polynomial over finite field. - + INPUT:: - + - ``parent`` -- parent of `self` - ``x`` -- list of coefficients from which `self` can be constructed @@ -92,7 +92,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): x^2 + t from a constant:: - + sage: x = S(t^2 + 1); x t^2 + 1 sage: x.parent() is S diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index ac766642ffc..b4e22d580e1 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -37,7 +37,7 @@ class SkewPolynomialRing_general(sage.algebras.algebra.Algebra,UniqueRepresentation): """ - Skew Univariate polynomial ring over a ring. + General Skew Univariate polynomial ring over a ring. DEFINITION: @@ -411,7 +411,7 @@ def characteristic(self): def twist_map(self, n=1): """ Return the twist map, otherwise known as the automorphism over the base ring of - ``self``, iterated multiple times. + ``self``, iterated `n` times. INPUT: @@ -679,7 +679,17 @@ def is_commutative(self): class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ - A specific class for skew polynomial rings over finite field. + 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` + + .. TODO:: + + Add center, irreducibility, karatsuba multiplication methods and factorization. """ @staticmethod def __classcall__(cls, base_ring, map, name=None, sparse=False, element_class=None): @@ -690,7 +700,7 @@ def __classcall__(cls, base_ring, map, name=None, sparse=False, element_class=No from sage.rings.polynomial import skew_polynomial_finite_field element_class = skew_polynomial_finite_field.SkewPolynomial_finite_field_dense return super(SkewPolynomialRing_general,cls).__classcall__(cls,base_ring,map,name,sparse,element_class) - + def __init__(self, base_ring, map, name, sparse, element_class): """ This method is a constructor for a general, dense univariate skew polynomial ring @@ -704,7 +714,7 @@ def __init__(self, base_ring, map, name, sparse, element_class): - ``name`` -- string or list of strings representing the name of the variables of ring - - ``sparse`` -- boolean (default: False) + - ``sparse`` -- boolean (default: ``False``) - ``element_class`` -- class representing the type of element to be used in ring @@ -714,15 +724,9 @@ def __init__(self, base_ring, map, name, sparse, element_class): EXAMPLES:: - 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 - sage: S([1]) + S([-1]) - 0 sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() - sage: T. = k['x', Frob]; T + 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 """ self._order = -1 @@ -737,21 +741,21 @@ def __init__(self, base_ring, map, name, sparse, element_class): except (AttributeError,NotImplementedError): pass if self._order < 0: - raise NotImplementedError("Unable to determine the order of %s" % map) + raise NotImplementedError("unable to determine the order of %s" % map) SkewPolynomialRing_general.__init__ (self, base_ring, map, name, sparse, element_class) self._maps = [ map**i for i in range(self._order) ] - def twist_map(self,n=1): + def twist_map(self, n=1): """ - Return the twist map (eventually iterated several times) used to define - this skew polynomial ring. - + Return the twist map, otherwise known as the automorphism over the base ring of + ``self``, iterated `n` times. + INPUT: - ``n`` - a relative integer (default: 1) - + OUTPUT: - + - The `n`-th iterative of the twist map of this skew polynomial ring. EXAMPLES:: From 130b1737fa64fe1613c53257ef0a74d24f8eadad Mon Sep 17 00:00:00 2001 From: arpitdm Date: Fri, 29 Jul 2016 03:10:38 +0530 Subject: [PATCH 08/37] improved documentation for skew_polynomials_finite_field.pyx file --- .../skew_polynomial_finite_field.pyx | 133 ++++++++++-------- 1 file changed, 76 insertions(+), 57 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index a12ff4a07bf..6e610279afe 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -1,38 +1,9 @@ r""" -This module implements skew polynomials over finite fields. +Univariate Dense Skew Polynomials over Finite Fields -Let `k` be a finite field and `\sigma` be a ring automorphism -of `k` (i.e. a power of the Frobenius endomorphism). Let -Put `S = k[X,\sigma]`: as an addtive group, it is the usual ring -of polynomials with coefficients in `k` and the multiplication -on `S` is defined by the rule `X * a = \sigma(a) * X`. - -.. SEE ALSO:: - - - ``Class SkewPolynomial_generic_dense`` and ``Class SkewPolynomial`` - in sage.rings.polynomial.skew_polynomial_element - - - ``Class SkewPolynomialRing`` and ``Class SkewPolynomialRing_finite_field`` - in sage.rings.polynomial.skew_polynomial_ring - -We recall that: - -#. `S` is a left (resp. right) euclidean noncommutative ring - -#. in particular, every left (resp. right) ideal is principal - -.. TODO:: - - Try to replace as possible ``finite field`` by ``field - endowed with a finite order twist morphism``. It may cause - new phenomena due to the non trivality of the Brauer group. - -EXAMPLES:: - - 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 +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. AUTHOR:: @@ -57,22 +28,69 @@ from polynomial_ring_constructor import PolynomialRing from skew_polynomial_element cimport SkewPolynomial_generic_dense cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): + """ + A generic, dense, univariate skew polynomial over a finite field. + + DEFINITION: + + Let `R` be a commutative ring and let `\sigma` be the Frobenius + Endomorphism over `R` defined by: + `\sigma(r) = r^p` + where `r` is an element of `R` and `p` is the prime characteristic of `R`. + + Then, a formal skew polynomial is given by the equation: + `F(X) = a_{n}X^{n} + ... + a_0` + where the coefficients `a_{i}` belong to `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. + + The ring of such skew polynomials over `R` equipped with `\sigma` is + denoted by `S = k[X,\sigma]`and it is an addtive group. + + .. NOTE:: + + #. `S` is a left (resp. right) euclidean noncommutative ring + + #. in particular, every left (resp. right) ideal is principal + + EXAMPLES:: + + 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 + + .. SEE ALSO:: + + :mod:`sage.rings.polynomial.skew_polynomial_element.SkewPolynomial_generic_dense` + :mod:`sage.rings.polynomial.skew_polynomial_element.SkewPolynomial` + :mod:`sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_finite_field` + + .. TODO:: + + Try to replace as possible ``finite field`` by ``field + endowed with a finite order twist morphism``. It may cause + new phenomena due to the non trivality of the Brauer group. + """ def __init__(self, parent, x=None, int check=1, is_gen=False, int construct=0, **kwds): """ - This method constructs a generic dense skew polynomial over finite field. + This method constructs a generic dense skew polynomial over a finite field. INPUT:: - - ``parent`` -- parent of `self` + - ``parent`` -- parent of ``self`` - - ``x`` -- list of coefficients from which `self` can be constructed + - ``x`` -- list of coefficients from which ``self`` can be constructed - ``check`` -- flag variable to normalize the polynomial - - ``is_gen`` -- boolean (default: False) + - ``is_gen`` -- boolean (default: ``False``) - - ``construct`` -- boolean (default: False) + - ``construct`` -- boolean (default: ``False``) TESTS:: @@ -97,12 +115,13 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): t^2 + 1 sage: x.parent() is S True - """ + + """ SkewPolynomial_generic_dense.__init__ (self, parent, x, check, is_gen, construct, **kwds) - cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other): + cdef SkewPolynomial_finite_field_dense _rgcd(self, SkewPolynomial_finite_field_dense other): """ - Fast right gcd. + Fast creation of the right gcd of ``self`` and ``other``. """ cdef SkewPolynomial_finite_field_dense A = self cdef SkewPolynomial_finite_field_dense B = other @@ -119,8 +138,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other): """ - Replace self by the remainder in the left euclidean division - of self by other (only for internal use). + Replace ``self`` by the remainder in the left euclidean division + of ``self`` by ``other`` (only for internal use). """ cdef list a = (self)._coeffs cdef list b = (other)._coeffs @@ -141,8 +160,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other): """ - Replace self by the remainder in the right euclidean division - of self by other (only for internal use). + Replace ``self`` by the remainder in the right euclidean division + of ``self`` by ``other`` (only for internal use). """ cdef list a = (self)._coeffs cdef list b = (other)._coeffs @@ -171,8 +190,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_lfloordiv(self, SkewPolynomial_finite_field_dense other): """ - Replace self by the quotient in the left euclidean division - of self by other (only for internal use). + Replace ``self`` by the quotient in the left euclidean division + of ``self`` by ``other`` (only for internal use). """ cdef list a = (self)._coeffs cdef list b = (other)._coeffs @@ -198,8 +217,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_rfloordiv(self, SkewPolynomial_finite_field_dense other): """ - Replace self by the quotient in the right euclidean division - of self by other (only for internal use). + Replace ``self`` by the quotient in the right euclidean division + of ``self`` by ``other`` (only for internal use). """ cdef list a = (self)._coeffs cdef list b = (other)._coeffs @@ -231,7 +250,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_lmonic(self): """ - Replace self by ``self.lmonic()`` (only for internal use). + Replace ``self`` by ``self.lmonic()`` (only for internal use). """ cdef list a = (self)._coeffs cdef Py_ssize_t da = len(a)-1, i @@ -243,7 +262,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_rmonic(self): """ - Replace self by ``self.rmonic()`` (only for internal use). + Replace ``self`` by ``self.rmonic()`` (only for internal use). """ cdef list a = (self)._coeffs cdef Py_ssize_t da = len(a)-1, i @@ -254,7 +273,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef void _inplace_rgcd(self,SkewPolynomial_finite_field_dense other): """ - Replace self by its right gcd with other (only for internal use). + Replace ``self`` by its right gcd with ``other`` (only for internal use). """ cdef SkewPolynomial_finite_field_dense B cdef list swap @@ -270,8 +289,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef SkewPolynomial_finite_field_dense _rquo_inplace_rem(self, SkewPolynomial_finite_field_dense other): """ - Replace self by the remainder in the right euclidean division - of self by other and return the quotient (only for internal use). + Replace ``self`` by the remainder in the right euclidean division + of ``self`` by ``other`` and return the quotient (only for internal use). """ cdef list a = (self)._coeffs cdef list b = (other)._coeffs @@ -300,8 +319,8 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef Py_ssize_t _val_inplace_unit(self): """ - Return `v` the valuation of self and replace self by - self >> v (only for internal use). + Return `v` the valuation of ``self`` and replace ``self`` by + `self >> v` (only for internal use). """ cdef list a = (self)._coeffs cdef Py_ssize_t val = 0 @@ -315,7 +334,7 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): cdef Matrix_dense _matmul_c(self): r""" - Return the matrix of the multiplication by self on + Return the matrix of the multiplication by ``self`` on `K[X,\sigma]` considered as a free module over `K[X^r]` (here `r` is the order of `\sigma`). From 15861b92f34893114ce4f12cf9c83ecda98223ce Mon Sep 17 00:00:00 2001 From: arpitdm Date: Fri, 29 Jul 2016 04:40:30 +0530 Subject: [PATCH 09/37] documentation builds successfully. --- .../en/reference/polynomial_rings/index.rst | 1 + .../skew_polynomial_finite_field.pyx | 21 +++++++++---------- .../rings/polynomial/skew_polynomial_ring.py | 9 ++++---- 3 files changed, 16 insertions(+), 15 deletions(-) diff --git a/src/doc/en/reference/polynomial_rings/index.rst b/src/doc/en/reference/polynomial_rings/index.rst index 4e40b497ed5..4fe3a2d6176 100644 --- a/src/doc/en/reference/polynomial_rings/index.rst +++ b/src/doc/en/reference/polynomial_rings/index.rst @@ -38,6 +38,7 @@ Skew Polynomials sage/rings/polynomial/skew_polynomial_element sage/rings/polynomial/skew_polynomial_ring_constructor sage/rings/polynomial/skew_polynomial_ring + sage/rings/polynomial/skew_polynomial_finite_field Rational Functions ------------------ diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index 6e610279afe..f8307613a57 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -27,16 +27,15 @@ from sage.rings.ring cimport Ring from polynomial_ring_constructor import PolynomialRing from skew_polynomial_element cimport SkewPolynomial_generic_dense -cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): +cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): """ A generic, dense, univariate skew polynomial over a finite field. DEFINITION: Let `R` be a commutative ring and let `\sigma` be the Frobenius - Endomorphism over `R` defined by: - `\sigma(r) = r^p` - where `r` is an element of `R` and `p` is the prime characteristic of `R`. + Endomorphism over `R` defined by `\sigma(r) = r^p` where `r` is + an element of `R` and `p` is the prime characteristic of `R`. Then, a formal skew polynomial is given by the equation: `F(X) = a_{n}X^{n} + ... + a_0` @@ -44,17 +43,17 @@ cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): 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. + `X a = \sigma(a) X` for all `a` in `R`. - The ring of such skew polynomials over `R` equipped with `\sigma` is - denoted by `S = k[X,\sigma]`and it is an addtive group. + Skew polynomials are thus non-commutative and the degree of a product + is equal to the sum of the degree of its factors. The ring of such skew + polynomials over `R` equipped with `\sigma` is denoted by `S = R[X, \sigma]` + and it is an additive group. .. NOTE:: - + #. `S` is a left (resp. right) euclidean noncommutative ring - + #. in particular, every left (resp. right) ideal is principal EXAMPLES:: diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index b4e22d580e1..8d4563a9374 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -88,16 +88,17 @@ class SkewPolynomialRing_general(sage.algebras.algebra.Algebra,UniqueRepresentat sage: Sz. = R[sigma] 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 - + ValueError: variable name 'Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring + Defn: t |--> t + 1' is not alphanumeric + As for polynomials, skew polynomial rings with different variable names are not equal:: - + sage: R['x',sigma] == R['y',sigma] False Of course, skew polynomial rings with different twist maps are not - equal as well + equal as well:: sage: R['x',sigma] == R['x',sigma^2] False From 866c699e68972f45d79bf996a80415fe3eeef3b3 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Sat, 30 Jul 2016 04:25:57 +0530 Subject: [PATCH 10/37] added minimum vanishing polynomial and multi-point evaluation methods --- .../rings/polynomial/skew_polynomial_ring.py | 82 ++++++++++++++++++- 1 file changed, 81 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 8d4563a9374..80e73f270e7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -88,7 +88,7 @@ class SkewPolynomialRing_general(sage.algebras.algebra.Algebra,UniqueRepresentat sage: Sz. = R[sigma] Traceback (most recent call last): ... - ValueError: variable name 'Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring + ValueError: variable name 'Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring\n Defn: t |--> t + 1' is not alphanumeric As for polynomials, skew polynomial rings with different variable names @@ -678,6 +678,86 @@ def is_commutative(self): """ return self.twist_map().is_identity() + def minimal_vanishing_polynomial(self, eval_pts): + """ + Return the minimal vanishing polynomial. Given the elements + `a_1, ..., a_s`, it is defined as the unique minimal degree polynomial + `p` such that `p` is monic and `p(a_i) = 0`, for `i = 1, ..., s`. + + INPUT: + + - ``eval_pts`` -- list of evaluation points + + OUTPUT: + + The 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: b = S.minimal_vanishing_polynomial(eval_pts); b + x^3 + 4 + """ + x = self.gen() + sigma = self.twist_map() + if len(eval_pts) == 0: + return self.one() + elif len(eval_pts) == 1: + if eval_pts[0] == 0: + return self.one() + else: + return x - (sigma(eval_pts[0]) / eval_pts[0]) + else: + A = eval_pts[:len(eval_pts)/2] + B = eval_pts[(len(eval_pts)/2):] + M_A = self.minimal_vanishing_polynomial(A) + M_A_B = self.multi_point_evaluation(M_A, B) + M_M_A_B = self.minimal_vanishing_polynomial(M_A_B) + return M_M_A_B * M_A + + def multi_point_evaluation(self, p, eval_pts): + """ + Evaluate skew polynomial at multiple evaluation points. + + INPUT: + + - ``p`` -- skew polynomial belonging to ``self`` + + - ``eval_pts`` -- list of points at which `p` is to be evaluated + + OUTPUT: + + List of values of `p` at the `eval_pts`. + + 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 = S.multi_point_evaluation(a, eval_pts); c + [t + 1, 3*t^2 + 4*t + 4, 4*t] + """ + coefficients = p.list() + sigma = self.twist_map() + if len(eval_pts) == 1: + if len(coefficients) == 1: + coefficients.append(0) + return [coefficients[1]*sigma(eval_pts[0]) + coefficients[0]*eval_pts[0]] + else: + A = eval_pts[:len(eval_pts)/2] + B = eval_pts[(len(eval_pts)/2):] + M_A = self.minimal_vanishing_polynomial(A) + M_B = self.minimal_vanishing_polynomial(B) + Q_A, R_A = p.right_quo_rem(M_A) + Q_B, R_B = p.right_quo_rem(M_B) + return self.multi_point_evaluation(R_A, A) + self.multi_point_evaluation(R_B, B) + class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ A specialized class for skew polynomial rings over finite fields. From c568c420198a1013d05eddd134f2fabb3bd8cdac Mon Sep 17 00:00:00 2001 From: arpitdm Date: Sat, 30 Jul 2016 05:22:04 +0530 Subject: [PATCH 11/37] added interpolation polynomial method --- .../rings/polynomial/skew_polynomial_ring.py | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 80e73f270e7..86a993d4f8a 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -23,6 +23,8 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import Element +from sage.functions.all import log +from sage.misc.cachefunc import cached_method import sage.algebras.algebra import sage.categories.basic as categories from sage.rings.integer import Integer @@ -758,6 +760,65 @@ def multi_point_evaluation(self, p, eval_pts): Q_B, R_B = p.right_quo_rem(M_B) return self.multi_point_evaluation(R_A, A) + self.multi_point_evaluation(R_B, B) + def interpolation_polynomial(self, eval_pts, values, check=True): + """ + Return the interpolation polynomial. Given `s` pairs of evaluation points + and values `{(x_1, y_1), ..., (x_s, y_s)}`, where each `x_i` is distinct + and non-zero, there exists a unique interpolation polynomial `I` such that + `I(x_i) = y_i`, for all `i = 1,...,s`. + + INPUT: + + - ``eval_pts`` -- list of evaluation points + + - ``values`` -- list of values that the interpolation polynomial `I` takes + at the respective `eval_pts` + + - ``check`` -- boolean (default: ``True``) that verifies whether the + `eval_pts` are linearly independent in the base ring of ``self``. + + OUTPUT: + + The interpolation polynomial. + + EXAMPLES:: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: eval_pts = [t, t^2] + sage: values = [3*t^2 + 4*t + 4, 4*t] + sage: d = S.interpolation_polynomial(eval_pts, values); d + x + t + """ + l = len(eval_pts) + if l > log(self.base_ring().order(), self.base_ring().characteristic()): + raise TypeError("number of evaluation points cannot be more than dimension of base field") + if l != len(values): + raise TypeError("number of evaluation points and values must be equal") + if l > len(set(eval_pts)): + raise TypeError("the evaluation points must be distinct") + if 0 in eval_pts: + raise TypeError("evaluation points must be non-zero") + if l == 1: + c, _ = values[0].quo_rem(eval_pts[0]) + return c*self.one() + else: + A = eval_pts[:l/2] + B = eval_pts[(l/2):] + M_A = self.minimal_vanishing_polynomial(A) + M_B = self.minimal_vanishing_polynomial(B) + A_ = self.multi_point_evaluation(M_B, A) + B_ = self.multi_point_evaluation(M_A, B) + I_1 = self.interpolation_polynomial(A_, values[:l/2]) + I_2 = self.interpolation_polynomial(B_, values[(l/2):]) + interpolation_polynomial = I_1 * M_B + I_2 * M_A + if check: + for i in range(l): + if interpolation_polynomial(eval_pts[i]) != values[i]: + return ValueError("the evaluation points are not linearly independent") + return interpolation_polynomial + class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ A specialized class for skew polynomial rings over finite fields. From 282cfb1bf5a92744918b56d0190f6789295ca532 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Sat, 30 Jul 2016 06:04:49 +0530 Subject: [PATCH 12/37] added check for linear independence of evalpts in MVP --- src/sage/rings/polynomial/skew_polynomial_ring.py | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 86a993d4f8a..94c8f48afa4 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -680,7 +680,7 @@ def is_commutative(self): """ return self.twist_map().is_identity() - def minimal_vanishing_polynomial(self, eval_pts): + def minimal_vanishing_polynomial(self, eval_pts, check=True): """ Return the minimal vanishing polynomial. Given the elements `a_1, ..., a_s`, it is defined as the unique minimal degree polynomial @@ -690,6 +690,9 @@ def minimal_vanishing_polynomial(self, eval_pts): - ``eval_pts`` -- list of evaluation points + - ``check`` -- boolean (default: ``True``) that verifies whether the + `eval_pts` are linearly independent in the base ring of ``self``. + OUTPUT: The minimal vanishing polynomial. @@ -718,6 +721,9 @@ def minimal_vanishing_polynomial(self, eval_pts): B = eval_pts[(len(eval_pts)/2):] M_A = self.minimal_vanishing_polynomial(A) M_A_B = self.multi_point_evaluation(M_A, B) + if check: + if 0 in M_A_B: + raise ValueError("evaluation points are not linearly independent") M_M_A_B = self.minimal_vanishing_polynomial(M_A_B) return M_M_A_B * M_A From 7806d862bb8af2574a4c77ffb65b55cecc941054 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Sat, 30 Jul 2016 17:55:59 +0200 Subject: [PATCH 13/37] Fixed bug in interpolation --- src/sage/rings/polynomial/skew_polynomial_ring.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 94c8f48afa4..d049f8c6276 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -754,9 +754,7 @@ def multi_point_evaluation(self, p, eval_pts): coefficients = p.list() sigma = self.twist_map() if len(eval_pts) == 1: - if len(coefficients) == 1: - coefficients.append(0) - return [coefficients[1]*sigma(eval_pts[0]) + coefficients[0]*eval_pts[0]] + return [ p(eval_pts[0]) ] else: A = eval_pts[:len(eval_pts)/2] B = eval_pts[(len(eval_pts)/2):] From e1cce49aade25c4e7e5523212c8e00ed1c2b9442 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Sat, 30 Jul 2016 17:56:06 +0200 Subject: [PATCH 14/37] Improved doc test for multi-point evaluation --- src/sage/rings/polynomial/skew_polynomial_ring.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index d049f8c6276..bf965f95b87 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -750,6 +750,8 @@ def multi_point_evaluation(self, p, eval_pts): sage: eval_pts = [1, t, t^2] sage: c = S.multi_point_evaluation(a, eval_pts); c [t + 1, 3*t^2 + 4*t + 4, 4*t] + sage: [ a(e) for e in eval_pts ] + [t + 1, 3*t^2 + 4*t + 4, 4*t] """ coefficients = p.list() sigma = self.twist_map() From b683f55821991763310a398797e03d935aa9e5fb Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Sat, 30 Jul 2016 18:01:14 +0200 Subject: [PATCH 15/37] divide-by-two potential error. Improved an error message --- src/sage/rings/polynomial/skew_polynomial_ring.py | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index bf965f95b87..bf5146d5c2b 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -717,13 +717,14 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): else: return x - (sigma(eval_pts[0]) / eval_pts[0]) else: - A = eval_pts[:len(eval_pts)/2] - B = eval_pts[(len(eval_pts)/2):] + t = len(eval_pts)//2 + A = eval_pts[:t] + B = eval_pts[t:] M_A = self.minimal_vanishing_polynomial(A) M_A_B = self.multi_point_evaluation(M_A, B) if check: if 0 in M_A_B: - raise ValueError("evaluation points are not linearly independent") + raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") M_M_A_B = self.minimal_vanishing_polynomial(M_A_B) return M_M_A_B * M_A @@ -758,8 +759,9 @@ def multi_point_evaluation(self, p, eval_pts): if len(eval_pts) == 1: return [ p(eval_pts[0]) ] else: - A = eval_pts[:len(eval_pts)/2] - B = eval_pts[(len(eval_pts)/2):] + t = len(eval_pts)//2 + A = eval_pts[:t] + B = eval_pts[t:] M_A = self.minimal_vanishing_polynomial(A) M_B = self.minimal_vanishing_polynomial(B) Q_A, R_A = p.right_quo_rem(M_A) From 012c9d315c8dd88459c55d8874fe3f5b9d1d8313 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Sat, 30 Jul 2016 19:06:23 +0200 Subject: [PATCH 16/37] rm non-general sanity check --- src/sage/rings/polynomial/skew_polynomial_ring.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index bf5146d5c2b..6c9f7dbc70d 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -800,8 +800,6 @@ def interpolation_polynomial(self, eval_pts, values, check=True): x + t """ l = len(eval_pts) - if l > log(self.base_ring().order(), self.base_ring().characteristic()): - raise TypeError("number of evaluation points cannot be more than dimension of base field") if l != len(values): raise TypeError("number of evaluation points and values must be equal") if l > len(set(eval_pts)): From 9064e0e99ab9d14f20ea20d65a803c1fe89375a8 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Sat, 30 Jul 2016 19:06:38 +0200 Subject: [PATCH 17/37] Fix division bug in interpolation --- src/sage/rings/polynomial/skew_polynomial_ring.py | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 6c9f7dbc70d..f20f19c8039 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -807,8 +807,7 @@ def interpolation_polynomial(self, eval_pts, values, check=True): if 0 in eval_pts: raise TypeError("evaluation points must be non-zero") if l == 1: - c, _ = values[0].quo_rem(eval_pts[0]) - return c*self.one() + return (values[0]/eval_pts[0])*self.one() else: A = eval_pts[:l/2] B = eval_pts[(l/2):] From 6618340ece629db87b92d77b0c7ae0e482480791 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Sat, 30 Jul 2016 19:06:55 +0200 Subject: [PATCH 18/37] Fix divide-by-two potential problem in interpolation --- src/sage/rings/polynomial/skew_polynomial_ring.py | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index f20f19c8039..b73d0930265 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -809,14 +809,15 @@ def interpolation_polynomial(self, eval_pts, values, check=True): if l == 1: return (values[0]/eval_pts[0])*self.one() else: - A = eval_pts[:l/2] - B = eval_pts[(l/2):] + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] M_A = self.minimal_vanishing_polynomial(A) M_B = self.minimal_vanishing_polynomial(B) A_ = self.multi_point_evaluation(M_B, A) B_ = self.multi_point_evaluation(M_A, B) - I_1 = self.interpolation_polynomial(A_, values[:l/2]) - I_2 = self.interpolation_polynomial(B_, values[(l/2):]) + I_1 = self.interpolation_polynomial(A_, values[:t]) + I_2 = self.interpolation_polynomial(B_, values[t:]) interpolation_polynomial = I_1 * M_B + I_2 * M_A if check: for i in range(l): From 4f067de25489338b1400609f9190e2f4d8cd8134 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Wed, 3 Aug 2016 02:06:20 +0530 Subject: [PATCH 19/37] Fixed documentation --- .../rings/polynomial/skew_polynomial_ring.py | 29 +++++++++++++++++-- 1 file changed, 26 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 0d1483fa786..0db26c4613f 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -30,6 +30,7 @@ from sage.rings.integer import Integer from sage.structure.category_object import normalize_names from sage.misc.prandom import randint +from sage.rings.ring import Field from sage.categories.morphism import Morphism from sage.categories.morphism import IdentityMorphism import sage.misc.latex as latex @@ -690,7 +691,8 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): INPUT: - - ``eval_pts`` -- list of evaluation points + - ``eval_pts`` -- list of evaluation points which are linearly + independent over the fixed field of the twist map of ``self``. - ``check`` -- boolean (default: ``True``) that verifies whether the `eval_pts` are linearly independent in the base ring of ``self``. @@ -717,6 +719,20 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): if eval_pts[0] == 0: return self.one() else: + R = self.base_ring() + print R + print isinstance(R, Field) + if not isinstance(R, Field): + print "Hi" + Q = R.fraction_field() + print Q + t = Q(R.variable_name()) +# sigma = Q.self.twist_map() + print sigma + print self.twist_map() + print Q.variable_name() + S = Q[self.variable_name(), sigma] + x = S.gen() return x - (sigma(eval_pts[0]) / eval_pts[0]) else: t = len(eval_pts)//2 @@ -779,7 +795,8 @@ def interpolation_polynomial(self, eval_pts, values, check=True): INPUT: - - ``eval_pts`` -- list of evaluation points + - ``eval_pts`` -- list of evaluation points which are linearly + independent over the fixed field of the twist map of ``self``. - ``values`` -- list of values that the interpolation polynomial `I` takes at the respective `eval_pts` @@ -809,7 +826,13 @@ def interpolation_polynomial(self, eval_pts, values, check=True): if 0 in eval_pts: raise TypeError("evaluation points must be non-zero") if l == 1: - return (values[0]/eval_pts[0])*self.one() + one = self.one() + R = self.base_ring() + if not isinstance(R, Field): + Q = FractionField(R) + S = Q[self.variable_name(), self.twist_map()] + one = S.one() + return (values[0]/eval_pts[0])*one else: t = l//2 A = eval_pts[:t] From 0c890341ef4c8d4d23cf63ac2dd020edec804171 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 8 Aug 2016 02:00:53 +0530 Subject: [PATCH 20/37] creating twist map over fraction field --- .../rings/polynomial/skew_polynomial_ring.py | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 0db26c4613f..9253f5f4a3a 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -720,19 +720,17 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): return self.one() else: R = self.base_ring() - print R - print isinstance(R, Field) + sigma = self.twist_map() +# print R +## print isinstance(R, Field) if not isinstance(R, Field): - print "Hi" +## print "Hi" Q = R.fraction_field() - print Q - t = Q(R.variable_name()) -# sigma = Q.self.twist_map() - print sigma - print self.twist_map() - print Q.variable_name() - S = Q[self.variable_name(), sigma] - x = S.gen() + gens = R.gens() + try: + sigma = Q.hom([ Q(sigma(g)) for g in gens ]) + except: + raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) return x - (sigma(eval_pts[0]) / eval_pts[0]) else: t = len(eval_pts)//2 From 50f9bdd6558ebf4365a9f9adf4744dcb64673f8e Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 8 Aug 2016 05:21:52 +0530 Subject: [PATCH 21/37] refactored mvp and interpolation to have inner functions so that checks on the arguments are made only at the top level. --- .../rings/polynomial/skew_polynomial_ring.py | 133 ++++++++++-------- 1 file changed, 73 insertions(+), 60 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 62867981e77..be38797425a 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -702,7 +702,7 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): independent over the fixed field of the twist map of ``self``. - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the base ring of ``self``. + `eval_pts` are linearly independent in the fixed field of twist map of ``self``. OUTPUT: @@ -731,38 +731,44 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): ... ValueError: evaluation points must be linearly independent over the fixed field of the twist map """ - x = self.gen() + R = self.base_ring() sigma = self.twist_map() - if len(eval_pts) == 0: - return self.one() - elif len(eval_pts) == 1: - if eval_pts[0] == 0: - return self.one() - else: - R = self.base_ring() - sigma = self.twist_map() - if not isinstance(R, Field): - Q = R.fraction_field() - gens = R.gens() - try: - sigma = Q.hom([ Q(sigma(g)) for g in gens ]) - S = Q[self.variable_name(), sigma] - x = S.gen() - except: - raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) - return x - (sigma(eval_pts[0]) / eval_pts[0]) + if not isinstance(R, Field): + Q = R.fraction_field() + gens = R.gens() + try: + sigma = Q.hom([ Q(sigma(g)) for g in gens ]) + S = Q[self.variable_name(), sigma] + one = S.one() + x = S.gen() + except: + raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) else: - t = len(eval_pts)//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = self.minimal_vanishing_polynomial(A) - M_A_B = self.multi_point_evaluation(M_A, B) - if check: - if 0 in M_A_B: - raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = self.minimal_vanishing_polynomial(M_A_B) - return M_M_A_B * M_A - + one = self.one() + x = self.gen() + + def create_mvp(eval_pts): + l = len(eval_pts) + if l == 0: + return one + elif l == 1: + if eval_pts[0] == 0: + return one + else: + return x - (sigma(eval_pts[0]) / eval_pts[0]) + else: + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] + M_A = create_mvp(A) + M_A_B = self.multi_point_evaluation(M_A, B) + if check: + if 0 in M_A_B: + raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") + M_M_A_B = create_mvp(M_A_B) + return M_M_A_B * M_A + return create_mvp(eval_pts) + def multi_point_evaluation(self, p, eval_pts): """ Evaluate skew polynomial at multiple evaluation points. @@ -819,7 +825,7 @@ def interpolation_polynomial(self, eval_pts, values, check=True): at the respective `eval_pts` - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the base ring of ``self``. + `eval_pts` are linearly independent in the fixed field of twist map of ``self``. OUTPUT: @@ -850,36 +856,43 @@ def interpolation_polynomial(self, eval_pts, values, check=True): raise TypeError("the evaluation points must be distinct") if 0 in eval_pts: raise TypeError("evaluation points must be non-zero") - if l == 1: - one = self.one() - R = self.base_ring() + + R = self.base_ring() + if not isinstance(R, Field): + Q = R.fraction_field() + gens = R.gens() sigma = self.twist_map() - if not isinstance(R, Field): - Q = R.fraction_field() - gens = R.gens() - try: - sigma = Q.hom([ Q(sigma(g)) for g in gens ]) - S = Q[self.variable_name(), sigma] - one = S.one() - except: - raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) - return (values[0]/eval_pts[0])*one + try: + sigma_frac = Q.hom([ Q(sigma(g)) for g in gens ]) + S = Q[self.variable_name(), sigma_frac] + one = S.one() + except: + raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) else: - t = l//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = self.minimal_vanishing_polynomial(A) - M_B = self.minimal_vanishing_polynomial(B) - A_ = self.multi_point_evaluation(M_B, A) - B_ = self.multi_point_evaluation(M_A, B) - I_1 = self.interpolation_polynomial(A_, values[:t]) - I_2 = self.interpolation_polynomial(B_, values[t:]) - interpolation_polynomial = I_1 * M_B + I_2 * M_A - if check: - for i in range(l): - if interpolation_polynomial(eval_pts[i]) != values[i]: - return ValueError("the evaluation points are not linearly independent") - return interpolation_polynomial + one = self.one() + + def interpolate(eval_pts, values): + l = len(eval_pts) + if l == 1: + return (values[0]/eval_pts[0])*one + else: + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] + M_A = self.minimal_vanishing_polynomial(A) + M_B = self.minimal_vanishing_polynomial(B) + A_ = self.multi_point_evaluation(M_B, A) + B_ = self.multi_point_evaluation(M_A, B) + I_1 = interpolate(A_, values[:t]) + I_2 = interpolate(B_, values[t:]) + return I_1 * M_B + I_2 * M_A + + interpolation_polynomial = interpolate(eval_pts, values) + if check: + for i in range(l): + if interpolation_polynomial(eval_pts[i]) != values[i]: + return ValueError("the evaluation points are not linearly independent") + return interpolation_polynomial class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ From dd667ed9b6cabc1a97f155c76990fee67544bbdb Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 8 Aug 2016 05:31:42 +0530 Subject: [PATCH 22/37] changed MPE to the trivial repeated calling of the evaluation function. added a todo block to the documentation. --- .../rings/polynomial/skew_polynomial_ring.py | 23 +++++++------------ 1 file changed, 8 insertions(+), 15 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index be38797425a..69f460f01e7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -783,6 +783,13 @@ def multi_point_evaluation(self, p, eval_pts): List of values of `p` at the `eval_pts`. + .. TODO:: + + This method currently trivially calls the evaluation function + repeatedly and should be updated to the recursive algorithm + from the paper "Fast Operations on Linearized Polynomials + and their Applications in Coding Theory" by Puchinger, et al. + EXAMPLES: sage: k. = GF(5^3) @@ -792,22 +799,8 @@ def multi_point_evaluation(self, p, eval_pts): sage: eval_pts = [1, t, t^2] sage: c = S.multi_point_evaluation(a, eval_pts); c [t + 1, 3*t^2 + 4*t + 4, 4*t] - sage: [ a(e) for e in eval_pts ] - [t + 1, 3*t^2 + 4*t + 4, 4*t] """ - coefficients = p.list() - sigma = self.twist_map() - if len(eval_pts) == 1: - return [ p(eval_pts[0]) ] - else: - t = len(eval_pts)//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = self.minimal_vanishing_polynomial(A) - M_B = self.minimal_vanishing_polynomial(B) - Q_A, R_A = p.right_quo_rem(M_A) - Q_B, R_B = p.right_quo_rem(M_B) - return self.multi_point_evaluation(R_A, A) + self.multi_point_evaluation(R_B, B) + return [ p(e) for e in eval_pts ] def interpolation_polynomial(self, eval_pts, values, check=True): """ From 8f42a338565e35ce2210216c9ec9e7a240f97fe9 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Mon, 8 Aug 2016 05:42:22 +0530 Subject: [PATCH 23/37] made MPE a method on skew polynomials. --- .../polynomial/skew_polynomial_element.pyx | 31 +++++++++++++ .../rings/polynomial/skew_polynomial_ring.py | 45 +++---------------- 2 files changed, 37 insertions(+), 39 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 07f6a867db8..e0e2a73c2ed 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -2537,6 +2537,37 @@ cdef class SkewPolynomial(AlgebraElement): """ return self.parent().variable_name() + def multi_point_evaluation(self, eval_pts): + """ + Evaluate skew polynomial at multiple 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 and should be updated to the recursive algorithm + from the paper "Fast Operations on Linearized Polynomials + and their Applications in Coding Theory" by Puchinger, et al. + + 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] + """ + return [ self(e) for e in eval_pts ] + cdef class SkewPolynomial_generic_dense(SkewPolynomial): """ diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 69f460f01e7..0f01bcf7f1e 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -100,10 +100,10 @@ class SkewPolynomialRing_general(sage.algebras.algebra.Algebra,UniqueRepresentat ... ValueError: variable name 'Ring endomorphism of Univariate Polynomial Ring in t over Integer Ring\n Defn: t |--> t + 1' is not alphanumeric - + As for polynomials, skew polynomial rings with different variable names are not equal:: - + sage: R['x',sigma] == R['y',sigma] False @@ -761,46 +761,13 @@ def create_mvp(eval_pts): A = eval_pts[:t] B = eval_pts[t:] M_A = create_mvp(A) - M_A_B = self.multi_point_evaluation(M_A, B) + M_A_B = M_A.multi_point_evaluation(B) if check: if 0 in M_A_B: raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") M_M_A_B = create_mvp(M_A_B) return M_M_A_B * M_A return create_mvp(eval_pts) - - def multi_point_evaluation(self, p, eval_pts): - """ - Evaluate skew polynomial at multiple evaluation points. - - INPUT: - - - ``p`` -- skew polynomial belonging to ``self`` - - - ``eval_pts`` -- list of points at which `p` is to be evaluated - - OUTPUT: - - List of values of `p` at the `eval_pts`. - - .. TODO:: - - This method currently trivially calls the evaluation function - repeatedly and should be updated to the recursive algorithm - from the paper "Fast Operations on Linearized Polynomials - and their Applications in Coding Theory" by Puchinger, et al. - - 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 = S.multi_point_evaluation(a, eval_pts); c - [t + 1, 3*t^2 + 4*t + 4, 4*t] - """ - return [ p(e) for e in eval_pts ] def interpolation_polynomial(self, eval_pts, values, check=True): """ @@ -874,8 +841,8 @@ def interpolate(eval_pts, values): B = eval_pts[t:] M_A = self.minimal_vanishing_polynomial(A) M_B = self.minimal_vanishing_polynomial(B) - A_ = self.multi_point_evaluation(M_B, A) - B_ = self.multi_point_evaluation(M_A, B) + A_ = M_B.multi_point_evaluation(A) + B_ = M_A.multi_point_evaluation(B) I_1 = interpolate(A_, values[:t]) I_2 = interpolate(B_, values[t:]) return I_1 * M_B + I_2 * M_A @@ -884,7 +851,7 @@ def interpolate(eval_pts, values): if check: for i in range(l): if interpolation_polynomial(eval_pts[i]) != values[i]: - return ValueError("the evaluation points are not linearly independent") + return ValueError("evaluation points must be linearly independent over the fixed field of the twist map") return interpolation_polynomial class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): From f25f886d0570b7304041b65a660f6bc79e32edd1 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Wed, 10 Aug 2016 16:35:23 +0530 Subject: [PATCH 24/37] created top level private function for MVP --- .../rings/polynomial/skew_polynomial_ring.py | 48 +++++++++++-------- 1 file changed, 27 insertions(+), 21 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 0f01bcf7f1e..dc36baca345 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -690,6 +690,31 @@ def is_commutative(self): """ return self.twist_map().is_identity() + def _create_mvp(self, x, one, sigma, eval_pts, check=True): + """ + """ +# one = S.one() +# sigma = S.twist_map() + l = len(eval_pts) + if l == 0: + return one + elif l == 1: + if eval_pts[0] == 0: + return one + else: + return x - (sigma(eval_pts[0]) / eval_pts[0]) + else: + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] + M_A = self._create_mvp(x, one, sigma, A) + M_A_B = M_A.multi_point_evaluation(B) + if check: + if 0 in M_A_B: + raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") + M_M_A_B = self._create_mvp(x, one, sigma, M_A_B) + return M_M_A_B * M_A + def minimal_vanishing_polynomial(self, eval_pts, check=True): """ Return the minimal vanishing polynomial. Given the elements @@ -747,27 +772,8 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): one = self.one() x = self.gen() - def create_mvp(eval_pts): - l = len(eval_pts) - if l == 0: - return one - elif l == 1: - if eval_pts[0] == 0: - return one - else: - return x - (sigma(eval_pts[0]) / eval_pts[0]) - else: - t = l//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = create_mvp(A) - M_A_B = M_A.multi_point_evaluation(B) - if check: - if 0 in M_A_B: - raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = create_mvp(M_A_B) - return M_M_A_B * M_A - return create_mvp(eval_pts) +# def create_mvp(eval_pts): + return self._create_mvp(x, one, sigma, eval_pts) def interpolation_polynomial(self, eval_pts, values, check=True): """ From 34bebbc6eb951d7e459ee2ee9988461d89af27d3 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Wed, 10 Aug 2016 18:53:50 +0530 Subject: [PATCH 25/37] created top level functions for interpolation and fraction field skew polynomial ring. added documentation and indirect doctests. --- .../rings/polynomial/skew_polynomial_ring.py | 183 ++++++++++++------ 1 file changed, 129 insertions(+), 54 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 5ba34d2d350..951343acd04 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -698,11 +698,81 @@ def is_commutative(self): """ return self.twist_map().is_identity() - def _create_mvp(self, x, one, sigma, eval_pts, check=True): + def _gen_one_sigma(self): """ + Return the indeterminate generator, one and twist map of + ``self``, or of the skew polynomial ring `S` associated + with the fraction field of the base ring of ``self`` if + the base ring of ``self`` is not a field (internal method). + + OUTPUT: + + - ``x`` -- indeterminate generator of ``self`` or `S` + + - ``one`` -- one of ``self`` or `S` + + - ``sigma`` -- twist map of ``self`` or `S` + + EXAMPLES: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: T. = R['x', sigma] + sage: eval_pts = [1, t, t^2] + sage: values = [t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4] + sage: p = T.interpolation_polynomial(eval_pts, values); p #indirect doctest + ((-t^4 - 2*t - 3)/-2)*x^2 + (-t^4 - t^3 - t^2 - 3*t - 2)*x + (-t^4 - 2*t^3 - 4*t^2 - 10*t - 9)/-2 + """ + R = self.base_ring() + sigma = self.twist_map() + if not isinstance(R, Field): + Q = R.fraction_field() + gens = R.gens() + try: + sigma = Q.hom([ Q(sigma(g)) for g in gens ]) + S = Q[self.variable_name(), sigma] + one = S.one() + x = S.gen() + except: + raise ValueError("unable to lift the twist map to a twist map over %s" % Q) + else: + one = self.one() + x = self.gen() + return x, one, sigma + + def _create_mvp(self, x, one, sigma, eval_pts, check=True): """ -# one = S.one() -# sigma = S.twist_map() + Return the minimum vanishing polynomial (internal method). + + INPUT: + + - ``x`` -- the generator of the associated skew polynomial ring + + - ``one`` -- the one of the associated skew polynomial ring + + - ``sigma`` -- the twist map of the associated skew polynomial ring + + - ``eval_pts`` -- list of evaluation points which are linearly + independent over the fixed field of the twist map of the associated + skew polynomial ring + + - ``check`` -- boolean (default: ``True``) that verifies whether the + `eval_pts` are linearly independent in the fixed field of twist map + of the associated skew polynomial ring + + OUTPUT: + + The minimum vanishing polynomial. + + EXAMPLES: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: eval_pts = [1, t, t^2] + sage: b = S.minimal_vanishing_polynomial(eval_pts); b #indirect doctest + x^3 + 4 + """ l = len(eval_pts) if l == 0: return one @@ -715,12 +785,12 @@ def _create_mvp(self, x, one, sigma, eval_pts, check=True): t = l//2 A = eval_pts[:t] B = eval_pts[t:] - M_A = self._create_mvp(x, one, sigma, A) + M_A = self._create_mvp(x, one, sigma, A, check) M_A_B = M_A.multi_point_evaluation(B) if check: if 0 in M_A_B: raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = self._create_mvp(x, one, sigma, M_A_B) + M_M_A_B = self._create_mvp(x, one, sigma, M_A_B, check) return M_M_A_B * M_A def minimal_vanishing_polynomial(self, eval_pts, check=True): @@ -732,7 +802,7 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): INPUT: - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of ``self``. + independent over the fixed field of the twist map of ``self``. - ``check`` -- boolean (default: ``True``) that verifies whether the `eval_pts` are linearly independent in the fixed field of twist map of ``self``. @@ -764,24 +834,56 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): ... ValueError: evaluation points must be linearly independent over the fixed field of the twist map """ - R = self.base_ring() - sigma = self.twist_map() - if not isinstance(R, Field): - Q = R.fraction_field() - gens = R.gens() - try: - sigma = Q.hom([ Q(sigma(g)) for g in gens ]) - S = Q[self.variable_name(), sigma] - one = S.one() - x = S.gen() - except: - raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) - else: - one = self.one() - x = self.gen() + x, one, sigma = self._gen_one_sigma() + return self._create_mvp(x, one, sigma, eval_pts, check) + + def _interpolate(self, x, one, sigma, eval_pts, values): + """ + Return the interpolation polynomial (internal method). + + INPUT: + + - ``x`` -- the generator of the associated skew polynomial ring + + - ``one`` -- the one of the associated skew polynomial ring + + - ``sigma`` -- the twist map of the associated skew polynomial ring + + - ``eval_pts`` -- list of evaluation points which are linearly + independent over the fixed field of the twist map of the associated + skew polynomial ring + + - ``values`` -- list of values that the interpolation polynomial takes + at the respective `eval_pts` + + OUTPUT: + + The interpolation polynomial. -# def create_mvp(eval_pts): - return self._create_mvp(x, one, sigma, eval_pts) + EXAMPLES: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: eval_pts = [t, t^2] + sage: values = [3*t^2 + 4*t + 4, 4*t] + sage: d = S.interpolation_polynomial(eval_pts, values); d #indirect doctest + x + t + """ + l = len(eval_pts) + if l == 1: + return (values[0]/eval_pts[0])*one + else: + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] + M_A = self._create_mvp(x, one, sigma, A) + M_B = self._create_mvp(x, one, sigma, B) + A_ = M_B.multi_point_evaluation(A) + B_ = M_A.multi_point_evaluation(B) + I_1 = self._interpolate(x, one, sigma, A_, values[:t]) + I_2 = self._interpolate(x, one, sigma, B_, values[t:]) + return I_1 * M_B + I_2 * M_A def interpolation_polynomial(self, eval_pts, values, check=True): """ @@ -793,7 +895,7 @@ def interpolation_polynomial(self, eval_pts, values, check=True): INPUT: - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of ``self``. + independent over the fixed field of the twist map of ``self``. - ``values`` -- list of values that the interpolation polynomial `I` takes at the respective `eval_pts` @@ -831,41 +933,14 @@ def interpolation_polynomial(self, eval_pts, values, check=True): if 0 in eval_pts: raise TypeError("evaluation points must be non-zero") - R = self.base_ring() - if not isinstance(R, Field): - Q = R.fraction_field() - gens = R.gens() - sigma = self.twist_map() - try: - sigma_frac = Q.hom([ Q(sigma(g)) for g in gens ]) - S = Q[self.variable_name(), sigma_frac] - one = S.one() - except: - raise ValueError("Unable to lift the twist map to a twist map over %s" % Q) - else: - one = self.one() + x, one, sigma = self._gen_one_sigma() + interpolation_polynomial = self._interpolate(x, one, sigma, eval_pts, values) - def interpolate(eval_pts, values): - l = len(eval_pts) - if l == 1: - return (values[0]/eval_pts[0])*one - else: - t = l//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = self.minimal_vanishing_polynomial(A) - M_B = self.minimal_vanishing_polynomial(B) - A_ = M_B.multi_point_evaluation(A) - B_ = M_A.multi_point_evaluation(B) - I_1 = interpolate(A_, values[:t]) - I_2 = interpolate(B_, values[t:]) - return I_1 * M_B + I_2 * M_A - - interpolation_polynomial = interpolate(eval_pts, values) if check: for i in range(l): if interpolation_polynomial(eval_pts[i]) != values[i]: return ValueError("evaluation points must be linearly independent over the fixed field of the twist map") + return interpolation_polynomial class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): From b523806a461e61036de748c7d7598044acc85892 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Thu, 11 Aug 2016 22:22:42 +0530 Subject: [PATCH 26/37] changed names of private methods --- .../rings/polynomial/skew_polynomial_ring.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 951343acd04..d1390b08d94 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -698,7 +698,7 @@ def is_commutative(self): """ return self.twist_map().is_identity() - def _gen_one_sigma(self): + def _base_ring_to_fraction_field(self): """ Return the indeterminate generator, one and twist map of ``self``, or of the skew polynomial ring `S` associated @@ -740,7 +740,7 @@ def _gen_one_sigma(self): x = self.gen() return x, one, sigma - def _create_mvp(self, x, one, sigma, eval_pts, check=True): + def _minimum_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): """ Return the minimum vanishing polynomial (internal method). @@ -785,12 +785,12 @@ def _create_mvp(self, x, one, sigma, eval_pts, check=True): t = l//2 A = eval_pts[:t] B = eval_pts[t:] - M_A = self._create_mvp(x, one, sigma, A, check) + M_A = self._minimum_vanishing_polynomial(x, one, sigma, A, check) M_A_B = M_A.multi_point_evaluation(B) if check: if 0 in M_A_B: raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = self._create_mvp(x, one, sigma, M_A_B, check) + M_M_A_B = self._minimum_vanishing_polynomial(x, one, sigma, M_A_B, check) return M_M_A_B * M_A def minimal_vanishing_polynomial(self, eval_pts, check=True): @@ -834,8 +834,8 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): ... ValueError: evaluation points must be linearly independent over the fixed field of the twist map """ - x, one, sigma = self._gen_one_sigma() - return self._create_mvp(x, one, sigma, eval_pts, check) + x, one, sigma = self._base_ring_to_fraction_field() + return self._minimum_vanishing_polynomial(x, one, sigma, eval_pts, check) def _interpolate(self, x, one, sigma, eval_pts, values): """ @@ -877,8 +877,8 @@ def _interpolate(self, x, one, sigma, eval_pts, values): t = l//2 A = eval_pts[:t] B = eval_pts[t:] - M_A = self._create_mvp(x, one, sigma, A) - M_B = self._create_mvp(x, one, sigma, B) + M_A = self._minimum_vanishing_polynomial(x, one, sigma, A) + M_B = self._minimum_vanishing_polynomial(x, one, sigma, B) A_ = M_B.multi_point_evaluation(A) B_ = M_A.multi_point_evaluation(B) I_1 = self._interpolate(x, one, sigma, A_, values[:t]) @@ -933,7 +933,7 @@ def interpolation_polynomial(self, eval_pts, values, check=True): if 0 in eval_pts: raise TypeError("evaluation points must be non-zero") - x, one, sigma = self._gen_one_sigma() + x, one, sigma = self._base_ring_to_fraction_field() interpolation_polynomial = self._interpolate(x, one, sigma, eval_pts, values) if check: From a2c4f06f4ac65342098630ed345dad2f53659306 Mon Sep 17 00:00:00 2001 From: arpitdm Date: Fri, 12 Aug 2016 18:25:35 +0530 Subject: [PATCH 27/37] removed unused imports, signal statements. small fixes to documentation. --- .../skew_polynomial_finite_field.pxd | 5 +--- .../skew_polynomial_finite_field.pyx | 24 ++++++++++++------- .../rings/polynomial/skew_polynomial_ring.py | 12 +++++++--- 3 files changed, 25 insertions(+), 16 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd index d927743be70..82936bd7d3d 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pxd @@ -1,11 +1,8 @@ -from sage.rings.integer cimport Integer from skew_polynomial_element cimport SkewPolynomial_generic_dense from sage.matrix.matrix_dense cimport Matrix_dense -from polynomial_element cimport Polynomial -from sage.structure.element cimport RingElement cdef class SkewPolynomial_finite_field_dense (SkewPolynomial_generic_dense): - + cdef SkewPolynomial_finite_field_dense _rgcd(self,SkewPolynomial_finite_field_dense other) cdef void _inplace_lrem(self, SkewPolynomial_finite_field_dense other) cdef void _inplace_rrem(self, SkewPolynomial_finite_field_dense other) diff --git a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx index f8307613a57..180acd9ee3f 100644 --- a/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_finite_field.pyx @@ -7,23 +7,30 @@ Endomorphism. AUTHOR:: -- Xavier Caruso (2012-06-29) +- Xavier Caruso (2012-06-29): initial version + +- Arpit Merchant (2016-08-04): improved docstrings, fixed doctests and refactored classes and methods + """ ############################################################################# # Copyright (C) 2012 Xavier Caruso # -# Distributed under the terms of the GNU General Public License (GPL) -# +# 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/ #**************************************************************************** -include "../../ext/stdsage.pxi" - import copy import cysignals from sage.matrix.constructor import zero_matrix from sage.rings.ring cimport Ring +from sage.matrix.matrix_dense cimport Matrix_dense +from polynomial_element cimport Polynomial +from sage.rings.integer cimport Integer +from sage.structure.element cimport RingElement from polynomial_ring_constructor import PolynomialRing from skew_polynomial_element cimport SkewPolynomial_generic_dense @@ -51,9 +58,9 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): and it is an additive group. .. NOTE:: - + #. `S` is a left (resp. right) euclidean noncommutative ring - + #. in particular, every left (resp. right) ideal is principal EXAMPLES:: @@ -199,7 +206,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): cdef RingElement c, inv parent = self._parent if db < 0: - sig_off() raise ZeroDivisionError if da < db: (self)._coeffs = [ ] @@ -324,7 +330,6 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): cdef list a = (self)._coeffs cdef Py_ssize_t val = 0 if len(a) < 0: - sig_off() return -1 while a[0].is_zero(): del a[0] @@ -360,6 +365,7 @@ cdef class SkewPolynomial_finite_field_dense(SkewPolynomial_generic_dense): for k from deb <= k <= d by r: pol.append(l[k]) M.set_unsafe(i,j,Polk(pol)) + for i from 0 <= i <= d: l[i] = self._parent.twist_map()(l[i]) return M diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index dafd2b4f3db..c479cfa9fc2 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -1,13 +1,18 @@ r""" Skew Univariate Polynomial Rings -This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general`` +This module provides the :class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general` 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_field` +which is a specialized class for skew polynomial rings over finite fields. It inherits from +:class:`~sage.rings.polynomial.skew_polynomial_ring.SkewPolynomialRing_general` but allows for +the more efficient computations in the case of finite fields. + AUTHOR: - Xavier Caruso (2012-06-29): initial version @@ -100,7 +105,7 @@ class SkewPolynomialRing_general(sage.algebras.algebra.Algebra,UniqueRepresentat As for polynomials, skew polynomial rings with different variable names are not equal:: - + sage: R['x',sigma] == R['y',sigma] False @@ -743,7 +748,8 @@ class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): .. TODO:: - Add center, irreducibility, karatsuba multiplication methods and factorization. + Add methods related to center of skew polynomial ring, irreducibility, karatsuba + multiplication and factorization. """ @staticmethod def __classcall__(cls, base_ring, map, name=None, sparse=False, element_class=None): From d403fae361316c8da23e388381e69148863ae48b Mon Sep 17 00:00:00 2001 From: arpitdm Date: Fri, 12 Aug 2016 19:01:30 +0530 Subject: [PATCH 28/37] fixes to documentation --- .../rings/polynomial/skew_polynomial_ring.py | 29 ++++++++++++------- 1 file changed, 18 insertions(+), 11 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index c38f19c6970..003b1fbf4c7 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -800,17 +800,21 @@ def _minimum_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): def minimal_vanishing_polynomial(self, eval_pts, check=True): """ - Return the minimal vanishing polynomial. Given the elements - `a_1, ..., a_s`, it is defined as the unique minimal degree polynomial - `p` such that `p` is monic and `p(a_i) = 0`, for `i = 1, ..., s`. + Return the minimal vanishing polynomial. + + Given the elements `a_1, ..., a_s`, it is defined as the + unique minimal degree polynomial `p` such that `p` is monic + and `p(a_i) = 0`, for `i = 1, ..., s`. INPUT: - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of ``self``. + independent over the fixed field of the twist map of the associated + skew polynomial ring - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the fixed field of twist map of ``self``. + `eval_pts` are linearly independent in the fixed field of twist map of + the associated skew polynomial ring OUTPUT: @@ -892,21 +896,24 @@ def _interpolate(self, x, one, sigma, eval_pts, values): def interpolation_polynomial(self, eval_pts, values, check=True): """ - Return the interpolation polynomial. Given `s` pairs of evaluation points - and values `{(x_1, y_1), ..., (x_s, y_s)}`, where each `x_i` is distinct - and non-zero, there exists a unique interpolation polynomial `I` such that - `I(x_i) = y_i`, for all `i = 1,...,s`. + Return the interpolation polynomial. + + Given `s` pairs of evaluation points and values `{(x_1, y_1), ..., (x_s, y_s)}`, + where each `x_i` is distinct and non-zero, there exists a unique interpolation + polynomial `I` such that `I(x_i) = y_i`, for all `i = 1,...,s`. INPUT: - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of ``self``. + independent over the fixed field of the twist map of the associated + skew polynomial ring - ``values`` -- list of values that the interpolation polynomial `I` takes at the respective `eval_pts` - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the fixed field of twist map of ``self``. + `eval_pts` are linearly independent in the fixed field of twist map of + the associated skew polynomial ring OUTPUT: From ce8b6b4976fd114c9af00630c21e7e567b2b4d3c Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 13:36:48 +0200 Subject: [PATCH 29/37] mpe: modified TODO and improved doc test --- src/sage/rings/polynomial/skew_polynomial_element.pyx | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 3e4fabf1a1e..15f3f9bd85d 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -2555,9 +2555,10 @@ cdef class SkewPolynomial(AlgebraElement): .. TODO:: This method currently trivially calls the evaluation function - repeatedly and should be updated to the recursive algorithm - from the paper "Fast Operations on Linearized Polynomials - and their Applications in Coding Theory" by Puchinger, et al. + repeatedly. If fast skew polynomial multiplication is available, an + asymptotically faster method is possible using standard divide and + conquer techniques and + :meth:`SkewPolynomialRing.minimal_vanishing_polynomial`. EXAMPLES: @@ -2568,6 +2569,8 @@ cdef class SkewPolynomial(AlgebraElement): 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 ] From 256052a6783b24e17f562fd9242722f3c633053e Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 13:46:33 +0200 Subject: [PATCH 30/37] minimum -> minimal --- .../rings/polynomial/skew_polynomial_ring.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 003b1fbf4c7..201dcf38838 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -745,9 +745,9 @@ def _base_ring_to_fraction_field(self): x = self.gen() return x, one, sigma - def _minimum_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): + def _minimal_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): """ - Return the minimum vanishing polynomial (internal method). + Return the minimal vanishing polynomial (internal method). INPUT: @@ -767,7 +767,7 @@ def _minimum_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): OUTPUT: - The minimum vanishing polynomial. + The minimal vanishing polynomial. EXAMPLES: @@ -790,12 +790,12 @@ def _minimum_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): t = l//2 A = eval_pts[:t] B = eval_pts[t:] - M_A = self._minimum_vanishing_polynomial(x, one, sigma, A, check) + M_A = self._minimal_vanishing_polynomial(x, one, sigma, A, check) M_A_B = M_A.multi_point_evaluation(B) if check: if 0 in M_A_B: raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = self._minimum_vanishing_polynomial(x, one, sigma, M_A_B, check) + M_M_A_B = self._minimal_vanishing_polynomial(x, one, sigma, M_A_B, check) return M_M_A_B * M_A def minimal_vanishing_polynomial(self, eval_pts, check=True): @@ -829,7 +829,7 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): sage: b = S.minimal_vanishing_polynomial(eval_pts); b x^3 + 4 - The minimum 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(eval_pts[y]) for y in range(len(eval_pts))]; eval [0, 0, 0] @@ -844,7 +844,7 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): ValueError: evaluation points must be linearly independent over the fixed field of the twist map """ x, one, sigma = self._base_ring_to_fraction_field() - return self._minimum_vanishing_polynomial(x, one, sigma, eval_pts, check) + return self._minimal_vanishing_polynomial(x, one, sigma, eval_pts, check) def _interpolate(self, x, one, sigma, eval_pts, values): """ @@ -886,8 +886,8 @@ def _interpolate(self, x, one, sigma, eval_pts, values): t = l//2 A = eval_pts[:t] B = eval_pts[t:] - M_A = self._minimum_vanishing_polynomial(x, one, sigma, A) - M_B = self._minimum_vanishing_polynomial(x, one, sigma, B) + M_A = self._minimal_vanishing_polynomial(x, one, sigma, A) + M_B = self._minimal_vanishing_polynomial(x, one, sigma, B) A_ = M_B.multi_point_evaluation(A) B_ = M_A.multi_point_evaluation(B) I_1 = self._interpolate(x, one, sigma, A_, values[:t]) From 27077683faa875f8fc26710034f71f3ec7594dca Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 13:46:40 +0200 Subject: [PATCH 31/37] clarify a doctest --- src/sage/rings/polynomial/skew_polynomial_ring.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 201dcf38838..c0eb79d1f72 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -831,7 +831,7 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): The minimal vanishing polynomial evaluates to 0 at each of the evaluation points:: - sage: eval = [b(eval_pts[y]) for y in range(len(eval_pts))]; eval + sage: eval = b.multi_point_evaluation(eval_pts); eval [0, 0, 0] If the evaluation points are not linearly independent over the given fixed From 3a1b992d21cc5e4e1194d1a8c593aba166461163 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 15:41:54 +0200 Subject: [PATCH 32/37] Moved _-functions outside class. Simplified call conventions --- .../rings/polynomial/skew_polynomial_ring.py | 308 +++++++++--------- 1 file changed, 160 insertions(+), 148 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index c0eb79d1f72..733d678aa65 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -36,7 +36,7 @@ from sage.structure.unique_representation import UniqueRepresentation from sage.structure.element import Element from sage.functions.all import log -from sage.misc.cachefunc import cached_method +from sage.misc.cachefunc import cached_method, cached_function import sage.algebras.algebra import sage.categories.basic as categories from sage.rings.integer import Integer @@ -50,6 +50,159 @@ ######################################################################################### + +def _base_ring_to_fraction_field(S): + """ + TODO + Return the indeterminate generator, one and twist map of + ``self``, or of the skew polynomial ring `S` associated + with the fraction field of the base ring of ``self`` if + the base ring of ``self`` is not a field (internal method). + + OUTPUT: + + - ``x`` -- indeterminate generator of ``self`` or `S` + + - ``one`` -- one of ``self`` or `S` + + - ``sigma`` -- twist map of ``self`` or `S` + + EXAMPLES: + + sage: R. = ZZ[] + sage: sigma = R.hom([t+1]) + sage: T. = R['x', sigma] + sage: eval_pts = [1, t, t^2] + sage: values = [t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4] + sage: p = T.interpolation_polynomial(eval_pts, values); p #indirect doctest + ((-t^4 - 2*t - 3)/-2)*x^2 + (-t^4 - t^3 - t^2 - 3*t - 2)*x + (-t^4 - 2*t^3 - 4*t^2 - 10*t - 9)/-2 + """ + R = S.base_ring() + if isinstance(R, Field): + return S + else: + Q = R.fraction_field() + gens = R.gens() + sigmaS = S.twist_map() + # try: + 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)) + + +def _minimal_vanishing_polynomial(R, eval_pts, check=True): + """ + Return the minimal vanishing polynomial (internal method). + + TODO + + INPUT: + + - ``x`` -- the generator of the associated skew polynomial ring + + - ``one`` -- the one of the associated skew polynomial ring + + - ``sigma`` -- the twist map of the associated skew polynomial ring + + - ``eval_pts`` -- a frozen set of evaluation points which are linearly + independent over the fixed field of the twist map of the associated + skew polynomial ring + + - ``check`` -- boolean (default: ``True``) that verifies whether the + `eval_pts` are linearly independent in the fixed field of twist map + of the associated skew polynomial ring + + OUTPUT: + + The minimal vanishing polynomial. + + EXAMPLES: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: eval_pts = [1, t, t^2] + sage: b = S.minimal_vanishing_polynomial(eval_pts); b #indirect doctest + x^3 + 4 + """ + l = len(eval_pts) + if l == 0: + return R.one() + elif l == 1: + if eval_pts[0] == 0: + return R.one() + else: + return R.gen() - (R.twist_map()(eval_pts[0]) / eval_pts[0]) + else: + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] + M_A = _minimal_vanishing_polynomial(R, A, check) + M_A_B = M_A.multi_point_evaluation(B) + if check: + if 0 in M_A_B: + raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") + M_M_A_B = _minimal_vanishing_polynomial(R, M_A_B, check) + return M_M_A_B * M_A + + +def _interpolate(R, eval_pts, values): + """ + Return the interpolation polynomial (internal method). + + TODO + + TODO: args should be like lagrange_polynomial. Name as well? + + INPUT: + + - ``x`` -- the generator of the associated skew polynomial ring + + - ``one`` -- the one of the associated skew polynomial ring + + - ``sigma`` -- the twist map of the associated skew polynomial ring + + - ``eval_pts`` -- vector of evaluation points which are linearly + independent over the fixed field of the twist map of the associated + skew polynomial ring + + - ``values`` -- list of values that the interpolation polynomial takes + at the respective `eval_pts` + + OUTPUT: + + The interpolation polynomial. + + EXAMPLES: + + sage: k. = GF(5^3) + sage: Frob = k.frobenius_endomorphism() + sage: S. = k['x',Frob] + sage: eval_pts = [t, t^2] + sage: values = [3*t^2 + 4*t + 4, 4*t] + sage: d = S.interpolation_polynomial(eval_pts, values); d #indirect doctest + x + t + """ + l = len(eval_pts) + if l == 1: + return (values[0]/eval_pts[0])*R.one() + else: + t = l//2 + A = eval_pts[:t] + B = eval_pts[t:] + M_A = _minimal_vanishing_polynomial(R, A) + M_B = _minimal_vanishing_polynomial(R, B) + A_ = M_B.multi_point_evaluation(A) + B_ = M_A.multi_point_evaluation(B) + I_1 = _interpolate(R, A_, values[:t]) + I_2 = _interpolate(R, B_, values[t:]) + return I_1 * M_B + I_2 * M_A + + +######################################################################################### + + class SkewPolynomialRing_general(sage.algebras.algebra.Algebra,UniqueRepresentation): """ General Skew Univariate polynomial ring over a ring. @@ -703,101 +856,6 @@ def is_commutative(self): """ return self.twist_map().is_identity() - def _base_ring_to_fraction_field(self): - """ - Return the indeterminate generator, one and twist map of - ``self``, or of the skew polynomial ring `S` associated - with the fraction field of the base ring of ``self`` if - the base ring of ``self`` is not a field (internal method). - - OUTPUT: - - - ``x`` -- indeterminate generator of ``self`` or `S` - - - ``one`` -- one of ``self`` or `S` - - - ``sigma`` -- twist map of ``self`` or `S` - - EXAMPLES: - - sage: R. = ZZ[] - sage: sigma = R.hom([t+1]) - sage: T. = R['x', sigma] - sage: eval_pts = [1, t, t^2] - sage: values = [t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4] - sage: p = T.interpolation_polynomial(eval_pts, values); p #indirect doctest - ((-t^4 - 2*t - 3)/-2)*x^2 + (-t^4 - t^3 - t^2 - 3*t - 2)*x + (-t^4 - 2*t^3 - 4*t^2 - 10*t - 9)/-2 - """ - R = self.base_ring() - sigma = self.twist_map() - if not isinstance(R, Field): - Q = R.fraction_field() - gens = R.gens() - try: - sigma = Q.hom([ Q(sigma(g)) for g in gens ]) - S = Q[self.variable_name(), sigma] - one = S.one() - x = S.gen() - except: - raise ValueError("unable to lift the twist map to a twist map over %s" % Q) - else: - one = self.one() - x = self.gen() - return x, one, sigma - - def _minimal_vanishing_polynomial(self, x, one, sigma, eval_pts, check=True): - """ - Return the minimal vanishing polynomial (internal method). - - INPUT: - - - ``x`` -- the generator of the associated skew polynomial ring - - - ``one`` -- the one of the associated skew polynomial ring - - - ``sigma`` -- the twist map of the associated skew polynomial ring - - - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of the associated - skew polynomial ring - - - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the fixed field of twist map - of the associated skew polynomial ring - - OUTPUT: - - The minimal vanishing polynomial. - - EXAMPLES: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: eval_pts = [1, t, t^2] - sage: b = S.minimal_vanishing_polynomial(eval_pts); b #indirect doctest - x^3 + 4 - """ - l = len(eval_pts) - if l == 0: - return one - elif l == 1: - if eval_pts[0] == 0: - return one - else: - return x - (sigma(eval_pts[0]) / eval_pts[0]) - else: - t = l//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = self._minimal_vanishing_polynomial(x, one, sigma, A, check) - M_A_B = M_A.multi_point_evaluation(B) - if check: - if 0 in M_A_B: - raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = self._minimal_vanishing_polynomial(x, one, sigma, M_A_B, check) - return M_M_A_B * M_A - def minimal_vanishing_polynomial(self, eval_pts, check=True): """ Return the minimal vanishing polynomial. @@ -816,6 +874,8 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): `eval_pts` are linearly independent in the fixed field of twist map of the associated skew polynomial ring + TODO Don't check? + OUTPUT: The minimal vanishing polynomial. @@ -843,56 +903,7 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): ... ValueError: evaluation points must be linearly independent over the fixed field of the twist map """ - x, one, sigma = self._base_ring_to_fraction_field() - return self._minimal_vanishing_polynomial(x, one, sigma, eval_pts, check) - - def _interpolate(self, x, one, sigma, eval_pts, values): - """ - Return the interpolation polynomial (internal method). - - INPUT: - - - ``x`` -- the generator of the associated skew polynomial ring - - - ``one`` -- the one of the associated skew polynomial ring - - - ``sigma`` -- the twist map of the associated skew polynomial ring - - - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of the associated - skew polynomial ring - - - ``values`` -- list of values that the interpolation polynomial takes - at the respective `eval_pts` - - OUTPUT: - - The interpolation polynomial. - - EXAMPLES: - - sage: k. = GF(5^3) - sage: Frob = k.frobenius_endomorphism() - sage: S. = k['x',Frob] - sage: eval_pts = [t, t^2] - sage: values = [3*t^2 + 4*t + 4, 4*t] - sage: d = S.interpolation_polynomial(eval_pts, values); d #indirect doctest - x + t - """ - l = len(eval_pts) - if l == 1: - return (values[0]/eval_pts[0])*one - else: - t = l//2 - A = eval_pts[:t] - B = eval_pts[t:] - M_A = self._minimal_vanishing_polynomial(x, one, sigma, A) - M_B = self._minimal_vanishing_polynomial(x, one, sigma, B) - A_ = M_B.multi_point_evaluation(A) - B_ = M_A.multi_point_evaluation(B) - I_1 = self._interpolate(x, one, sigma, A_, values[:t]) - I_2 = self._interpolate(x, one, sigma, B_, values[t:]) - return I_1 * M_B + I_2 * M_A + return _minimal_vanishing_polynomial(_base_ring_to_fraction_field(self), eval_pts, check) def interpolation_polynomial(self, eval_pts, values, check=True): """ @@ -915,6 +926,8 @@ def interpolation_polynomial(self, eval_pts, values, check=True): `eval_pts` are linearly independent in the fixed field of twist map of the associated skew polynomial ring + TODO Better doc of ``check`` etc. + OUTPUT: The interpolation polynomial. @@ -945,8 +958,7 @@ def interpolation_polynomial(self, eval_pts, values, check=True): if 0 in eval_pts: raise TypeError("evaluation points must be non-zero") - x, one, sigma = self._base_ring_to_fraction_field() - interpolation_polynomial = self._interpolate(x, one, sigma, eval_pts, values) + interpolation_polynomial = _interpolate(_base_ring_to_fraction_field(self), eval_pts, values) if check: for i in range(l): From 0ed0179cf46bd7f4ae2bee72f551bd7359fd0659 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 16:01:53 +0200 Subject: [PATCH 33/37] Allow minimal_vanishing_polynomial on input which is linearly dependent --- .../rings/polynomial/skew_polynomial_ring.py | 59 +++++++------------ 1 file changed, 22 insertions(+), 37 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 733d678aa65..f14304a875e 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -91,28 +91,21 @@ def _base_ring_to_fraction_field(S): # raise ValueError("unable to lift the twist map to a twist map over %s (error was: %s)" % (Q, e)) -def _minimal_vanishing_polynomial(R, eval_pts, check=True): +def _minimal_vanishing_polynomial(R, eval_pts): """ Return the minimal vanishing polynomial (internal method). - TODO + See the documentation for + :meth:`SkewPolynomialRing.minimal_vanishing_polynomial` for a description. INPUT: - - ``x`` -- the generator of the associated skew polynomial ring - - - ``one`` -- the one of the associated skew polynomial ring - - - ``sigma`` -- the twist map of the associated skew polynomial ring + - ``R`` -- A skew polynomial ring over a field. - - ``eval_pts`` -- a frozen set of evaluation points which are linearly + - ``eval_pts`` -- a frozen set TODO of evaluation points which are linearly independent over the fixed field of the twist map of the associated skew polynomial ring - - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the fixed field of twist map - of the associated skew polynomial ring - OUTPUT: The minimal vanishing polynomial. @@ -130,7 +123,7 @@ def _minimal_vanishing_polynomial(R, eval_pts, check=True): if l == 0: return R.one() elif l == 1: - if eval_pts[0] == 0: + if eval_pts[0].is_zero(): return R.one() else: return R.gen() - (R.twist_map()(eval_pts[0]) / eval_pts[0]) @@ -138,12 +131,9 @@ def _minimal_vanishing_polynomial(R, eval_pts, check=True): t = l//2 A = eval_pts[:t] B = eval_pts[t:] - M_A = _minimal_vanishing_polynomial(R, A, check) + M_A = _minimal_vanishing_polynomial(R, A) M_A_B = M_A.multi_point_evaluation(B) - if check: - if 0 in M_A_B: - raise ValueError("evaluation points must be linearly independent over the fixed field of the twist map") - M_M_A_B = _minimal_vanishing_polynomial(R, M_A_B, check) + M_M_A_B = _minimal_vanishing_polynomial(R, M_A_B) return M_M_A_B * M_A @@ -856,13 +846,14 @@ def is_commutative(self): """ return self.twist_map().is_identity() - def minimal_vanishing_polynomial(self, eval_pts, check=True): + def minimal_vanishing_polynomial(self, eval_pts): """ - Return the minimal vanishing polynomial. + Return the minimal-degree, monic skew polynomial which vanishes at all + the given evaluation points. - Given the elements `a_1, ..., a_s`, it is defined as the - unique minimal degree polynomial `p` such that `p` is monic - and `p(a_i) = 0`, for `i = 1, ..., s`. + 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()``. INPUT: @@ -870,12 +861,6 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): independent over the fixed field of the twist map of the associated skew polynomial ring - - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the fixed field of twist map of - the associated skew polynomial ring - - TODO Don't check? - OUTPUT: The minimal vanishing polynomial. @@ -894,16 +879,16 @@ def minimal_vanishing_polynomial(self, eval_pts, check=True): sage: eval = b.multi_point_evaluation(eval_pts); eval [0, 0, 0] - If the evaluation points are not linearly independent over the given fixed - field of the twist map, an error is raised:: + If the evaluation points are linearly dependent over the fixed field of + the twist map, then the returned polynomial has lower degree than the + number of evaluation points: - sage: eval_pts_ld = [1, t, 2*t] - sage: c = S.minimal_vanishing_polynomial(eval_pts_ld) - Traceback (most recent call last): - ... - ValueError: evaluation points must be linearly independent over the fixed field of the twist map + sage: S.minimal_vanishing_polynomial([t]) + x + 3*t^2 + 3*t + sage: S.minimal_vanishing_polynomial([t, 3*t]) + x + 3*t^2 + 3*t """ - return _minimal_vanishing_polynomial(_base_ring_to_fraction_field(self), eval_pts, check) + return _minimal_vanishing_polynomial(_base_ring_to_fraction_field(self), eval_pts) def interpolation_polynomial(self, eval_pts, values, check=True): """ From db72757357670b35fec328e70d58861d8bd29d39 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 16:38:21 +0200 Subject: [PATCH 34/37] interpolation -> lagrange_polynomial and make call convention as PolynomialRing --- .../rings/polynomial/skew_polynomial_ring.py | 95 ++++++++++--------- 1 file changed, 50 insertions(+), 45 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index f14304a875e..5dc69188526 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -72,10 +72,9 @@ def _base_ring_to_fraction_field(S): sage: R. = ZZ[] sage: sigma = R.hom([t+1]) sage: T. = R['x', sigma] - sage: eval_pts = [1, t, t^2] - sage: values = [t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4] - sage: p = T.interpolation_polynomial(eval_pts, values); p #indirect doctest - ((-t^4 - 2*t - 3)/-2)*x^2 + (-t^4 - t^3 - t^2 - 3*t - 2)*x + (-t^4 - 2*t^3 - 4*t^2 - 10*t - 9)/-2 + sage: points = [ (t, 3*t^2 + 4*t + 4) , (t^2, 4*t) ] + sage: p = T.lagrange_polynomial(points); p #indirect doctest + ((3*t^3 + 4*t^2)/(-t - 1))*x + (-3*t^3 - 7*t^2 - 4*t - 4)/-t """ R = S.base_ring() if isinstance(R, Field): @@ -137,9 +136,9 @@ def _minimal_vanishing_polynomial(R, eval_pts): return M_M_A_B * M_A -def _interpolate(R, eval_pts, values): +def _lagrange_interpolation(R, eval_pts, values): """ - Return the interpolation polynomial (internal method). + Return the lagrange polynomial (internal method). TODO @@ -157,21 +156,20 @@ def _interpolate(R, eval_pts, values): independent over the fixed field of the twist map of the associated skew polynomial ring - - ``values`` -- list of values that the interpolation polynomial takes + - ``values`` -- list of values that the lagrange polynomial takes at the respective `eval_pts` OUTPUT: - The interpolation polynomial. + The lagrange polynomial. EXAMPLES: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] - sage: eval_pts = [t, t^2] - sage: values = [3*t^2 + 4*t + 4, 4*t] - sage: d = S.interpolation_polynomial(eval_pts, values); d #indirect doctest + sage: points = [ (t, 3*t^2 + 4*t + 4) , (t^2, 4*t) ] + sage: d = S.lagrange_polynomial(points); d #indirect doctest x + t """ l = len(eval_pts) @@ -185,8 +183,8 @@ def _interpolate(R, eval_pts, values): M_B = _minimal_vanishing_polynomial(R, B) A_ = M_B.multi_point_evaluation(A) B_ = M_A.multi_point_evaluation(B) - I_1 = _interpolate(R, A_, values[:t]) - I_2 = _interpolate(R, B_, values[t:]) + I_1 = _lagrange_interpolation(R, A_, values[:t]) + I_2 = _lagrange_interpolation(R, B_, values[t:]) return I_1 * M_B + I_2 * M_A @@ -890,67 +888,74 @@ def minimal_vanishing_polynomial(self, eval_pts): """ return _minimal_vanishing_polynomial(_base_ring_to_fraction_field(self), eval_pts) - def interpolation_polynomial(self, eval_pts, values, check=True): + def lagrange_polynomial(self, points, check=True): """ - Return the interpolation polynomial. + Return the minimal-degree polynomial which interpolates the given + points, if one exists. - Given `s` pairs of evaluation points and values `{(x_1, y_1), ..., (x_s, y_s)}`, - where each `x_i` is distinct and non-zero, there exists a unique interpolation - polynomial `I` such that `I(x_i) = y_i`, for all `i = 1,...,s`. + More precisely, given `n` pairs `(x_1, y_1), ..., (x_n, y_n) \in R^2`, + where `R` is ``self.base_ring()``, determine whether a skew polynomial + `p(x)` such that `p(x_i) = y_i` for each `i` exists, and return it if + it does. - INPUT: + If the `x_i` are linearly independent over the fixed field of + ``self.twist_map()`` then such a polynomial is guaranteed to exist. - - ``eval_pts`` -- list of evaluation points which are linearly - independent over the fixed field of the twist map of the associated - skew polynomial ring + INPUT: - - ``values`` -- list of values that the interpolation polynomial `I` takes - at the respective `eval_pts` + - ``points`` -- a list of pairs ``(x_1, y_1),..., (x_n, y_n)`` of + elements of the base ring of ``self``. - - ``check`` -- boolean (default: ``True``) that verifies whether the - `eval_pts` are linearly independent in the fixed field of twist map of - the associated skew polynomial ring + # - ``check`` -- boolean (default: ``True``): if ``True`` then the computed polynomial is + # that verifies whether the + # `eval_pts` are linearly independent in the fixed field of twist map of + # the associated skew polynomial ring TODO Better doc of ``check`` etc. + TODO Example showing linear dependence + OUTPUT: - The interpolation polynomial. + The lagrange polynomial. EXAMPLES:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] - sage: eval_pts = [t, t^2] - sage: values = [3*t^2 + 4*t + 4, 4*t] - sage: d = S.interpolation_polynomial(eval_pts, values); d + sage: points = [(t, 3*t^2 + 4*t + 4), (t^2, 4*t)] + sage: d = S.lagrange_polynomial(points); d x + t sage: R. = ZZ[] sage: sigma = R.hom([t+1]) sage: T. = R['x', sigma] - sage: eval_pts = [1, t, t^2] - sage: values = [t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4] - sage: p = T.interpolation_polynomial(eval_pts, values); p + sage: points = [ (1, t^2 + 3*t + 4), (t, 2*t^2 + 3*t + 1), (t^2, t^2 + 3*t + 4) ] + sage: p = T.lagrange_polynomial(points); p ((-t^4 - 2*t - 3)/-2)*x^2 + (-t^4 - t^3 - t^2 - 3*t - 2)*x + (-t^4 - 2*t^3 - 4*t^2 - 10*t - 9)/-2 + sage: p.multi_point_evaluation([1, t, t^2]) == [ t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4 ] + True """ - l = len(eval_pts) - if l != len(values): - raise TypeError("number of evaluation points and values must be equal") + l = len(points) + if not all( len(pair) == 2 for pair in points ): + raise TypeError("supplied points must be pairs of elements of base ring") + eval_pts, values = zip(*points) #unzip + eval_pts = list(eval_pts) + values = list(values) + if l > len(set(eval_pts)): raise TypeError("the evaluation points must be distinct") - if 0 in eval_pts: - raise TypeError("evaluation points must be non-zero") + 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.") - interpolation_polynomial = _interpolate(_base_ring_to_fraction_field(self), eval_pts, values) + p = _lagrange_interpolation(_base_ring_to_fraction_field(self), eval_pts, values) - if check: - for i in range(l): - if interpolation_polynomial(eval_pts[i]) != values[i]: - return ValueError("evaluation points must be linearly independent over the fixed field of the twist map") + if check and p.multi_point_evaluation(eval_pts) != values: + raise ValueError("the requested Lagrange polynomial does not exist (the evaluation points are linearly dependent over the fixed field of the twist map and the sought values do not match.)") - return interpolation_polynomial + return p class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ From 115cf04ca51ba1ce82aa6e8c530a758dbb3b627b Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 16:43:38 +0200 Subject: [PATCH 35/37] Improved doc and example of _base_ring_to_fraction_field --- .../rings/polynomial/skew_polynomial_ring.py | 24 +++++++++---------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 5dc69188526..0c5135d6147 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -53,28 +53,26 @@ def _base_ring_to_fraction_field(S): """ - TODO - Return the indeterminate generator, one and twist map of - ``self``, or of the skew polynomial ring `S` associated - with the fraction field of the base ring of ``self`` if - the base ring of ``self`` is not a field (internal method). + Return the unique skew polynomial ring over the fraction field of + ``S.base_ring()`` which has ``S`` a sub-ring (internal method). - OUTPUT: + INPUT: - - ``x`` -- indeterminate generator of ``self`` or `S` + - ``S`` -- a skew polynomial ring. - - ``one`` -- one of ``self`` or `S` + OUTPUT: - - ``sigma`` -- twist map of ``self`` or `S` + - ``Q`` -- the skew polynomial ring over the fraction field of + ``S.base_ring``. EXAMPLES: + sage: from sage.rings.polynomial.skew_polynomial_ring import _base_ring_to_fraction_field sage: R. = ZZ[] sage: sigma = R.hom([t+1]) - sage: T. = R['x', sigma] - sage: points = [ (t, 3*t^2 + 4*t + 4) , (t^2, 4*t) ] - sage: p = T.lagrange_polynomial(points); p #indirect doctest - ((3*t^3 + 4*t^2)/(-t - 1))*x + (-3*t^3 - 7*t^2 - 4*t - 4)/-t + 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 """ R = S.base_ring() if isinstance(R, Field): From ff371ab8e1191e86673ac0ddd8a782ddb8751a71 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 17:55:12 +0200 Subject: [PATCH 36/37] lagrange: improve doc. Rm check param and instead discover linear dependence --- .../rings/polynomial/skew_polynomial_ring.py | 96 ++++++++++--------- 1 file changed, 53 insertions(+), 43 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 0c5135d6147..654d475386e 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -134,44 +134,56 @@ def _minimal_vanishing_polynomial(R, eval_pts): return M_M_A_B * M_A -def _lagrange_interpolation(R, eval_pts, values): +def _lagrange_polynomial(R, eval_pts, values): """ - Return the lagrange polynomial (internal method). + Return the Lagrange polynomial of the given points if it exists. Otherwise + return an unspecified polynomial (internal method). - TODO - - TODO: args should be like lagrange_polynomial. Name as well? - - INPUT: - - - ``x`` -- the generator of the associated skew polynomial ring + See the documentation for + :meth:`SkewPolynomialRing.lagrange_polynomial` for a description of Lagrange polynomial. + + INPUT:: - - ``one`` -- the one of the associated skew polynomial ring + - ``R`` -- a skew polynomial ring over a field - - ``sigma`` -- the twist map of the associated skew polynomial ring - - - ``eval_pts`` -- vector of evaluation points which are linearly - independent over the fixed field of the twist map of the associated - skew polynomial ring + - ``eval_pts`` -- list of evaluation points - ``values`` -- list of values that the lagrange polynomial takes at the respective `eval_pts` - OUTPUT: + OUTPUT:: - The lagrange polynomial. + - the lagrange polynomial. - EXAMPLES: + EXAMPLES:: + sage: from sage.rings.polynomial.skew_polynomial_ring import _lagrange_polynomial sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] - sage: points = [ (t, 3*t^2 + 4*t + 4) , (t^2, 4*t) ] - sage: d = S.lagrange_polynomial(points); d #indirect doctest + sage: eval_pts = [ t , t^2 ] + sage: values = [ 3*t^2 + 4*t + 4 , 4*t ] + sage: d = _lagrange_polynomial(S, eval_pts, values); d x + t + sage: d.multi_point_evaluation(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 + corresponding values do not match:: + + sage: eval_pts = [ t, 2*t ] + sage: values = [ 1, 3 ] + 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, 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).") return (values[0]/eval_pts[0])*R.one() else: t = l//2 @@ -181,8 +193,8 @@ def _lagrange_interpolation(R, eval_pts, values): M_B = _minimal_vanishing_polynomial(R, B) A_ = M_B.multi_point_evaluation(A) B_ = M_A.multi_point_evaluation(B) - I_1 = _lagrange_interpolation(R, A_, values[:t]) - I_2 = _lagrange_interpolation(R, B_, values[t:]) + I_1 = _lagrange_polynomial(R, A_, values[:t]) + I_2 = _lagrange_polynomial(R, B_, values[t:]) return I_1 * M_B + I_2 * M_A @@ -886,32 +898,27 @@ def minimal_vanishing_polynomial(self, eval_pts): """ return _minimal_vanishing_polynomial(_base_ring_to_fraction_field(self), eval_pts) - def lagrange_polynomial(self, points, check=True): + def lagrange_polynomial(self, points): """ Return the minimal-degree polynomial which interpolates the given - points, if one exists. + points. More precisely, given `n` pairs `(x_1, y_1), ..., (x_n, y_n) \in R^2`, - where `R` is ``self.base_ring()``, determine whether a skew polynomial - `p(x)` such that `p(x_i) = y_i` for each `i` exists, and return it if - it does. + where `R` is ``self.base_ring()``, compute a skew polymial `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()``. If the `x_i` are linearly independent over the fixed field of ``self.twist_map()`` 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``. - - # - ``check`` -- boolean (default: ``True``): if ``True`` then the computed polynomial is - # that verifies whether the - # `eval_pts` are linearly independent in the fixed field of twist map of - # the associated skew polynomial ring - - TODO Better doc of ``check`` etc. - - TODO Example showing linear dependence + elements of the base ring of ``self``. The `x_i` should be linearly + independent over the fixed field of ``self.twist_map()``. OUTPUT: @@ -934,6 +941,14 @@ def lagrange_polynomial(self, points, check=True): ((-t^4 - 2*t - 3)/-2)*x^2 + (-t^4 - t^3 - t^2 - 3*t - 2)*x + (-t^4 - 2*t^3 - 4*t^2 - 10*t - 9)/-2 sage: p.multi_point_evaluation([1, t, t^2]) == [ t^2 + 3*t + 4, 2*t^2 + 3*t + 1, t^2 + 3*t + 4 ] True + + If the `x_i` are linearly dependent over the fixed field of + ``self.twist_map()``, then an error is raised:: + + 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, 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 ): @@ -948,12 +963,7 @@ def lagrange_polynomial(self, points, check=True): 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.") - p = _lagrange_interpolation(_base_ring_to_fraction_field(self), eval_pts, values) - - if check and p.multi_point_evaluation(eval_pts) != values: - raise ValueError("the requested Lagrange polynomial does not exist (the evaluation points are linearly dependent over the fixed field of the twist map and the sought values do not match.)") - - return p + return _lagrange_polynomial(_base_ring_to_fraction_field(self), eval_pts, values) class SkewPolynomialRing_finite_field(SkewPolynomialRing_general): """ From 4bb82b8c5ffff8e442ef5aad6a224b590c3f9331 Mon Sep 17 00:00:00 2001 From: "Johan S. R. Nielsen" Date: Tue, 16 Aug 2016 18:25:52 +0200 Subject: [PATCH 37/37] Improve a bit the code and doc --- .../polynomial/skew_polynomial_element.pyx | 4 +-- .../rings/polynomial/skew_polynomial_ring.py | 29 +++++++++---------- 2 files changed, 16 insertions(+), 17 deletions(-) diff --git a/src/sage/rings/polynomial/skew_polynomial_element.pyx b/src/sage/rings/polynomial/skew_polynomial_element.pyx index 15f3f9bd85d..06035e007fa 100644 --- a/src/sage/rings/polynomial/skew_polynomial_element.pyx +++ b/src/sage/rings/polynomial/skew_polynomial_element.pyx @@ -2542,7 +2542,7 @@ cdef class SkewPolynomial(AlgebraElement): def multi_point_evaluation(self, eval_pts): """ - Evaluate skew polynomial at multiple evaluation points. + Evaluate ``self`` at list of evaluation points. INPUT: @@ -2560,7 +2560,7 @@ cdef class SkewPolynomial(AlgebraElement): conquer techniques and :meth:`SkewPolynomialRing.minimal_vanishing_polynomial`. - EXAMPLES: + EXAMPLES:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() diff --git a/src/sage/rings/polynomial/skew_polynomial_ring.py b/src/sage/rings/polynomial/skew_polynomial_ring.py index 654d475386e..8b9bdc7358d 100644 --- a/src/sage/rings/polynomial/skew_polynomial_ring.py +++ b/src/sage/rings/polynomial/skew_polynomial_ring.py @@ -90,7 +90,7 @@ def _base_ring_to_fraction_field(S): def _minimal_vanishing_polynomial(R, eval_pts): """ - Return the minimal vanishing polynomial (internal method). + Return the minimal vanishing polynomial (internal function). See the documentation for :meth:`SkewPolynomialRing.minimal_vanishing_polynomial` for a description. @@ -99,9 +99,7 @@ def _minimal_vanishing_polynomial(R, eval_pts): - ``R`` -- A skew polynomial ring over a field. - - ``eval_pts`` -- a frozen set TODO of evaluation points which are linearly - independent over the fixed field of the twist map of the associated - skew polynomial ring + - ``eval_pts`` -- a list of evaluation points OUTPUT: @@ -109,29 +107,31 @@ def _minimal_vanishing_polynomial(R, eval_pts): EXAMPLES: + sage: from sage.rings.polynomial.skew_polynomial_ring import _minimal_vanishing_polynomial sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() sage: S. = k['x',Frob] sage: eval_pts = [1, t, t^2] - sage: b = S.minimal_vanishing_polynomial(eval_pts); b #indirect doctest + sage: b = _minimal_vanishing_polynomial(S, eval_pts); b x^3 + 4 """ l = len(eval_pts) if l == 0: return R.one() elif l == 1: - if eval_pts[0].is_zero(): + e = eval_pts[0] + if e.is_zero(): return R.one() else: - return R.gen() - (R.twist_map()(eval_pts[0]) / eval_pts[0]) + return R.gen() - R.twist_map()(e)/e else: t = l//2 A = eval_pts[:t] B = eval_pts[t:] M_A = _minimal_vanishing_polynomial(R, A) - M_A_B = M_A.multi_point_evaluation(B) - M_M_A_B = _minimal_vanishing_polynomial(R, M_A_B) - return M_M_A_B * M_A + B_moved = M_A.multi_point_evaluation(B) + M_at_B_moved = _minimal_vanishing_polynomial(R, B_moved) + return M_at_B_moved * M_A def _lagrange_polynomial(R, eval_pts, values): @@ -873,7 +873,7 @@ def minimal_vanishing_polynomial(self, eval_pts): The minimal vanishing polynomial. - EXAMPLES: + EXAMPLES:: sage: k. = GF(5^3) sage: Frob = k.frobenius_endomorphism() @@ -889,7 +889,7 @@ def minimal_vanishing_polynomial(self, eval_pts): If the evaluation points are linearly dependent over the fixed field of the twist map, then the returned polynomial has lower degree than the - number of evaluation points: + number of evaluation points:: sage: S.minimal_vanishing_polynomial([t]) x + 3*t^2 + 3*t @@ -953,9 +953,8 @@ def lagrange_polynomial(self, points): l = len(points) if not all( len(pair) == 2 for pair in points ): raise TypeError("supplied points must be pairs of elements of base ring") - eval_pts, values = zip(*points) #unzip - eval_pts = list(eval_pts) - values = list(values) + eval_pts = [ x for (x,_) in points ] + values = [ y for (_,y) in points ] if l > len(set(eval_pts)): raise TypeError("the evaluation points must be distinct")