diff --git a/src/sage/groups/finitely_presented.py b/src/sage/groups/finitely_presented.py index 96d03e90c83..a351e544ce7 100644 --- a/src/sage/groups/finitely_presented.py +++ b/src/sage/groups/finitely_presented.py @@ -145,6 +145,7 @@ from sage.sets.set import Set from sage.structure.unique_representation import UniqueRepresentation + class GroupMorphismWithGensImages(SetMorphism): r""" Class used for morphisms from finitely presented groups to @@ -1702,12 +1703,18 @@ def abelian_alexander_matrix(self, ring=QQ, simplified=True): sage: g = FreeGroup(1) / [] sage: g.abelian_alexander_matrix() ([], []) + sage: g.abelian_alexander_matrix()[0].base_ring() + Univariate Laurent Polynomial Ring in f1 over Rational Field sage: g = FreeGroup(0) / [] - sage: g.abelian_alexander_matrix() - ([], []) + sage: A, ideal = g.abelian_alexander_matrix(); A + [] + sage: A.base_ring() + Rational Field """ ab, R, ideal, images = self.abelianization_to_algebra(ring=ring) A = self.alexander_matrix(im_gens=images) + if A.base_ring() != R: + A = A.change_ring(R) if simplified: n, m = A.dimensions() if n == 0 or m == 0: @@ -1761,66 +1768,111 @@ def characteristic_varieties(self, ring=QQ, matrix_ideal=None, groebner=False): OUTPUT: - If ``groebner`` is ``False`` a list of ideals defining the characteristic varieties. - If it is ``True``, a list of lists for Gröbner bases for each ideal. + A dictionary with keys the indices of the varieties. If ``groebner`` is ``False`` + the values are the ideals defining the characteristic varieties. + If it is ``True``, lists for Gröbner bases for the ideal of each irreducible + component, stopping when the first time a characteristic variety is empty. EXAMPLES:: sage: L = [2*(i, j) + 2* (-i, -j) for i, j in ((1, 2), (2, 3), (3, 1))] sage: G = FreeGroup(3) / L sage: G.characteristic_varieties(groebner=True) - [[(f1 - 1, f2 - 1, f3 - 1), - (f1 + 1, f2 - 1, f3 - 1), - (f1 - 1, f2 - 1, f3 + 1), - (f3^2 + 1, f1 - f3, f2 - f3), - (f1 - 1, f2 + 1, f3 - 1)], - [(f1 - 1, f2 - 1, f3 - 1), - (f1*f3 + 1, f2 - 1), - (f1*f2 + 1, f3 - 1), - (f2*f3 + 1, f1 - 1), - (f2*f3 + 1, f1 - f2), - (f2*f3 + 1, f1 - f3), - (f1*f3 + 1, f2 - f3)]] + {0: [(0,)], + 1: [(f1 - 1, f2 - 1, f3 - 1), (f1*f3 + 1, f2 - 1), (f1*f2 + 1, f3 - 1), (f2*f3 + 1, f1 - 1), + (f2*f3 + 1, f1 - f2), (f2*f3 + 1, f1 - f3), (f1*f3 + 1, f2 - f3)], + 2: [(f1 - 1, f2 - 1, f3 - 1), (f1 + 1, f2 - 1, f3 - 1), (f1 - 1, f2 - 1, f3 + 1), + (f3^2 + 1, f1 - f3, f2 - f3), (f1 - 1, f2 + 1, f3 - 1)], + 3: [(f1 - 1, f2 - 1, f3 - 1)], + 4: []} sage: G = FreeGroup(2)/[2*(1,2,-1,-2)] sage: G.characteristic_varieties() - [Ideal (-2*f2 + 2, 2*f1 - 2) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field] + {0: Ideal (0) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field, + 1: Ideal (f2 - 1, f1 - 1) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field, + 2: Ideal (f2 - 1, f1 - 1) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field, + 3: Ideal (1) of Multivariate Laurent Polynomial Ring in f1, f2 over Rational Field} sage: G.characteristic_varieties(ring=ZZ) - [Ideal (-2*f2 + 2, 2*f1 - 2) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring] + {0: Ideal (0) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring, + 1: Ideal (2*f2 - 2, 2*f1 - 2) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring, + 2: Ideal (f2 - 1, f1 - 1) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring, + 3: Ideal (1) of Multivariate Laurent Polynomial Ring in f1, f2 over Integer Ring} sage: G = FreeGroup(2)/[(1,2,1,-2,-1,-2)] sage: G.characteristic_varieties() - [Ideal (1 - f2 + f2^2, -1 + f2 - f2^2) of Univariate Laurent Polynomial Ring in f2 over Rational Field] + {0: Ideal (0) of Univariate Laurent Polynomial Ring in f2 over Rational Field, + 1: Ideal (-1 + 2*f2 - 2*f2^2 + f2^3) of Univariate Laurent Polynomial Ring in f2 over Rational Field, + 2: Ideal (1) of Univariate Laurent Polynomial Ring in f2 over Rational Field} + sage: G.characteristic_varieties(groebner=True) + {0: [0], 1: [-1 + f2, 1 - f2 + f2^2], 2: []} + sage: G = FreeGroup(2)/[3 * (1, ), 2 * (2, )] sage: G.characteristic_varieties(groebner=True) - [[1 - f2 + f2^2]] + {0: [-1 + F1, 1 + F1, 1 - F1 + F1^2, 1 + F1 + F1^2], 1: [1 - F1 + F1^2], 2: []} + sage: G = FreeGroup(2)/[2 * (2, )] + sage: G.characteristic_varieties(groebner=True) + {0: [(f1 + 1,), (f1 - 1,)], 1: [(f1 + 1,), (f1 - 1, f2 - 1)], 2: []} + sage: G = (FreeGroup(0) / []) + sage: G.characteristic_varieties() + {0: Principal ideal (0) of Rational Field, + 1: Principal ideal (1) of Rational Field} + sage: G.characteristic_varieties(groebner=True) + {0: [(0,)], 1: [(1,)]} """ - A, ideal = self.abelian_alexander_matrix(ring=ring, simplified=True) + if self.ngens() == 0: + if groebner: + return {j: [(ring(j),)] for j in (0, 1)} + return {j: ring.ideal(j) for j in (0, 1)} + A, rels = self.abelian_alexander_matrix(ring=ring, simplified=True) R = A.base_ring() - res = [] + eval_1 = {x: ring(1) for x in R.gens()} + A_scalar = A.apply_map(lambda p: p.subs(eval_1)) + n = A.ncols() + n1 = n - A_scalar.rank() + ideal_1 = R.ideal([x - 1 for x in R.gens()]) S = R.polynomial_ring() - ideal = [S(elt) for elt in ideal] - for j in range(1, A.ncols()): - L = [p.monomial_reduction()[0] for p in A.minors(j)] - J = R.ideal(L + ideal) - res.append(J) - if not groebner or not R.base_ring().is_field(): + K = R.base_ring() + id_rels = R.ideal(rels) + res = dict() + bound = n + 1 + for j in range(bound + 1): + J = id_rels + A.fitting_ideal(j) + # J = R.ideal(id_rels.gens() + A.fitting_ideal(j).gens()) + if j <= n1: + J1 = K.ideal([K(p.subs(eval_1)) for p in J.gens()]) + if J1: + J *= ideal_1 + res[j] = R.ideal(J.gens_reduced()) + if R(1) in res[j].gens(): + bound = j + break + if not groebner or not ring.is_field(): return res if R.ngens() == 1: - res0 = [gcd(S(p) for p in J.gens()) for J in res] - res1 = [] - for p in res0: - if p == 0: - res1.append([R(0)]) + res = {j: gcd(S(p) for p in res[j].gens()) for j in range(bound + 1)} + char_var = dict() + strict = True + j = 0 + while strict and j <= bound: + if res[j] == 0: + char_var[j] = [R(0)] else: - fct = [q[0] for q in R(p).factor()] + fct = [q[0] for q in R(res[j]).factor()] if fct: - res1.append(fct) - return res1 - res1 = [] - for J in res: - LJ = J.minimal_associated_primes() + char_var[j] = fct + else: + char_var[j] = [] + strict = False + j += 1 + return char_var + char_var = dict() + strict = True + j = 0 + while strict and j <= bound: + LJ = res[j].minimal_associated_primes() fct = [id.groebner_basis() for id in LJ] - if fct != [(S.one(),)]: - res1.append(fct) - return res1 + char_var[j] = fct + if not fct: + strict = False + j += 1 + return char_var def rewriting_system(self): """ diff --git a/src/sage/matrix/matrix_laurent_mpolynomial_dense.pxd b/src/sage/matrix/matrix_laurent_mpolynomial_dense.pxd new file mode 100644 index 00000000000..2a1170d0bc5 --- /dev/null +++ b/src/sage/matrix/matrix_laurent_mpolynomial_dense.pxd @@ -0,0 +1,4 @@ +from sage.matrix.matrix_generic_dense cimport Matrix_generic_dense + +cdef class Matrix_Laurent_mpolynomial_dense(Matrix_generic_dense): + pass diff --git a/src/sage/matrix/matrix_laurent_mpolynomial_dense.pyx b/src/sage/matrix/matrix_laurent_mpolynomial_dense.pyx new file mode 100644 index 00000000000..1f345a68ba6 --- /dev/null +++ b/src/sage/matrix/matrix_laurent_mpolynomial_dense.pyx @@ -0,0 +1,114 @@ +""" +Dense matrices over multivariate polynomials over fields. + +AUTHOR: + +- Enrique Artal (2023-??): initial version +""" + +# ***************************************************************************** +# Copyright (C) 2023 Enrique Artal +# +# Distributed under the terms of the GNU General Public License (GPL) +# as published by the Free Software Foundation; either version 2 of +# the License, or (at your option) any later version. +# https://www.gnu.org/licenses/ +# ***************************************************************************** +from sage.matrix.constructor import identity_matrix +from sage.rings.polynomial.laurent_polynomial_ring_base import LaurentPolynomialRing_generic + +cdef class Matrix_laurent_mpolynomial_dense(Matrix_generic_dense): + """ + Dense matrix over a Laurent multivariate polynomial ring over a field. + """ + def laurent_matrix_reduction(self): + """ + From a matrix `self` of Laurent polynomials, apply elementary operations + to obtain a matrix `P` of polynomials such that the variables do not divide + no column and no row. + + OUTPUT: + + Three matrices `L`, `P`, `R` such that ``self` equals `L P R`, where `L` and + `R` are diagonal with monomial entries. + + EXAMPLES: + + sage: R. = LaurentPolynomialRing(QQ) + sage: L = [1/3*x^-1*y - 6*x^-2*y^2 - 1/2*x^-2*y, 1/5*x + 1/2*y + 1/6] + sage: L += [1/2 - 5*x^-1*y - 2*x^-1, -1/3*y^-2 - 4*x^-1*y^-1 + 11*x^-1*y^-2] + sage: A = matrix(R, 2, L) + sage: lf, P, rg = A.laurent_matrix_reduction() + sage: lf + [ x^-2 0] + [ 0 x^-1*y^-2] + sage: P + [ 1/3*x - 6*y - 1/2 1/5*x^3 + 1/2*x^2*y + 1/6*x^2] + [ 1/2*x*y - 5*y^2 - 2*y -1/3*x - 4*y + 11] + sage: rg + [y 0] + [0 1] + """ + R = self.base_ring() + n_rows, n_cols = self.dimensions() + mat_l = identity_matrix(R, n_rows) + mat_r = identity_matrix(R, n_cols) + res = self.__copy__() + for j, rw in enumerate(res.rows()): + for t in R.gens(): + n = min(mon.degree(t) for a in rw for cf, mon in a) + res.rescale_row(j, t ** -n) + mat_l.rescale_col(j, t ** n) + for j, cl in enumerate(res.columns()): + for t in R.gens(): + n = min(mon.degree(t) for a in cl for cf, mon in a) + res.rescale_col(j, t ** -n) + mat_r.rescale_row(j, t ** n) + res = res.change_ring(R.polynomial_ring()) + return mat_l, res, mat_r + + def _fitting_ideal(self, i): + r""" + Return the `i`-th Fitting ideal of the matrix. This is the ideal generated + by the `n - i` minors, where `n` is the number of columns. + + INPUT: + + ``i`` -- an integer + + OUTPUT: + + An ideal on the base ring. + + EXAMPLES:: + + sage: R. = LaurentPolynomialRing(QQ) + sage: M = matrix(R, [[2*x^-1-z, 0, y-z^-2, 0], [0, z - y^-1, z - x, 0],[z - y, x^-2 - y, 0, z]]) + sage: M + [-z + 2*x^-1 0 y - z^-2 0] + [ 0 z - y^-1 -x + z 0] + [ -y + z -y + x^-2 0 z] + sage: M.fitting_ideal(0) + Ideal (0) of Multivariate Laurent Polynomial Ring in x, y, z over Rational Field + sage: M.fitting_ideal(1) == M._fitting_ideal(1) + True + sage: M.fitting_ideal(1).groebner_basis() + (x^4 - 2*x^3*y - x*z^3 - 4*x^2*y + 8*x*y^2 + 4*x*y*z + 2*z^2 - 8*y, + x*y*z^2 - x*z - 2*y*z + 2, + x^2*z - x*z^2 - 2*x + 2*z, + y^2*z + 1/4*x^2 - 1/2*x*y - 1/4*x*z - y + 1/2) + sage: M.fitting_ideal(2).groebner_basis() + (1,) + sage: M.fitting_ideal(3).groebner_basis() + (1,) + sage: M.fitting_ideal(4).groebner_basis() + (1,) + sage: [R.ideal(M.minors(i)) == M._fitting_ideal(4 - i) for i in range(5)] + [True, True, True, True, True] + + """ + R = self.base_ring() + S = R.polynomial_ring() + A = self.laurent_matrix_reduction()[1].change_ring(S) + J = A._fitting_ideal(i) + return J.change_ring(R) diff --git a/src/sage/matrix/matrix_space.py b/src/sage/matrix/matrix_space.py index 3795ff4b0fa..940358b2c81 100644 --- a/src/sage/matrix/matrix_space.py +++ b/src/sage/matrix/matrix_space.py @@ -306,6 +306,13 @@ def get_matrix_class(R, nrows, ncols, sparse, implementation): pass else: return matrix_mpolynomial_dense.Matrix_mpolynomial_dense + elif isinstance(R, sage.rings.polynomial.laurent_polynomial_ring.LaurentPolynomialRing_mpair) and R.base_ring() in _Fields: + try: + from . import matrix_laurent_mpolynomial_dense + except ImportError: + pass + else: + return matrix_laurent_mpolynomial_dense.Matrix_laurent_mpolynomial_dense # The fallback from sage.matrix.matrix_generic_dense import Matrix_generic_dense diff --git a/src/sage/rings/polynomial/ideal.py b/src/sage/rings/polynomial/ideal.py index 1ab86736d5b..9719c81ba41 100644 --- a/src/sage/rings/polynomial/ideal.py +++ b/src/sage/rings/polynomial/ideal.py @@ -85,3 +85,16 @@ def groebner_basis(self, algorithm=None): gb = self.gens_reduced() from sage.rings.polynomial.multi_polynomial_sequence import PolynomialSequence_generic return PolynomialSequence_generic([gb], self.ring(), immutable=True) + + def change_ring(self, R): + """ + Coerce an ideal into a new ring. + + EXAMPLES:: + + sage: R. = QQ[] + sage: I = R.ideal([q^2+q-1]) + sage: I.change_ring(RR['q']) + Principal ideal (q^2 + q - 1.00000000000000) of Univariate Polynomial Ring in q over Real Field with 53 bits of precision + """ + return R.ideal(self.gens()) diff --git a/src/sage/rings/polynomial/laurent_polynomial.pyx b/src/sage/rings/polynomial/laurent_polynomial.pyx index 32dfa655ea7..8e5bd127993 100644 --- a/src/sage/rings/polynomial/laurent_polynomial.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial.pyx @@ -1317,6 +1317,63 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): return ~self raise ArithmeticError("element is not a unit") + def xgcd(self, other): + """ + Extended `gcd` for univariate Laurent polynomial rings over a field. + + EXAMPLES:: + + sage: S. = LaurentPolynomialRing(QQ) + sage: (t^-2 + 1).xgcd(t^-3 + 1) + (1, 1/2*t^2 - 1/2*t^3 - 1/2*t^4, 1/2*t^3 + 1/2*t^4) + """ + R = self.parent() + S = R.polynomial_ring() + f, df = self.monomial_reduction() + g, dg = other.monomial_reduction() + h, p, q = f.xgcd(g) + return R(h), p / df, q / dg + + def inverse_mod(a, m): + """ + Invert the polynomial ``a`` with respect to ``m``, or raise a :class:`ValueError` + if no such inverse exists. + + The parameter ``m`` may be either a single polynomial or an ideal + (for consistency with :meth:`inverse_mod` in other rings). + + ALGORITHM: Solve the system `as + mt = 1`, returning `s` as the inverse + of `a` mod `m`. + + EXAMPLES:: + + sage: S. = LaurentPolynomialRing(QQ) + sage: f = inverse_mod(t^-2 + 1, t^-3 + 1); f + 1/2*t^2 - 1/2*t^3 - 1/2*t^4 + sage: f * (t^-2 + 1) + (1/2*t^4 + 1/2*t^3) * (t^-3 + 1) + 1 + """ + from sage.rings.ideal import is_Ideal + if is_Ideal(m): + v = m.gens_reduced() + if len(v) > 1: + raise NotImplementedError("only inversion modulo principal ideals implemented") + m = v[0] + if m.degree() == 1 and m[1].is_unit(): + # a(x) mod (x-r) = a(r) + r = -m[0] + if not m[1].is_one(): + r *= m.base_ring()(~m[1]) + u = a(r) + if u.is_unit(): + return a.parent()(~u) + g, s, _ = a.xgcd(m) + if g == 1: + return s + elif g.is_unit(): + return g.inverse_of_unit() * s + raise ValueError("impossible inverse modulo") + def _fraction_pair(self): """ Return one representation of ``self`` as a pair @@ -1989,3 +2046,38 @@ cdef class LaurentPolynomial_univariate(LaurentPolynomial): 0 """ return self.__u[-self.__n] + + @coerce_binop + def divides(self, other): + r""" + Return ``True`` if ``self`` divides ``other``. + + EXAMPLES:: + + sage: R. = LaurentPolynomialRing(ZZ) + sage: (2*x**-1 + 1).divides(4*x**-2 - 1) + True + sage: (2*x + 1).divides(4*x**2 + 1) + False + sage: (2*x + x**-1).divides(R(0)) + True + sage: R(0).divides(2*x ** -1 + 1) + False + sage: R(0).divides(R(0)) + True + sage: R. = LaurentPolynomialRing(Zmod(6)) + sage: p = 4*x + 3*x^-1 + sage: q = 5*x^2 + x + 2*x^-2 + sage: p.divides(q) + False + + sage: R. = GF(2)[] + sage: S. = LaurentPolynomialRing(R) + sage: p = (x+y+1) * z**-1 + x*y + sage: q = (y^2-x^2) * z**-2 + z + x-y + sage: p.divides(q), p.divides(p*q) # needs sage.libs.singular + (False, True) + """ + p = self.polynomial_construction()[0] + q = other.polynomial_construction()[0] + return p.divides(q) diff --git a/src/sage/rings/polynomial/laurent_polynomial_ideal.py b/src/sage/rings/polynomial/laurent_polynomial_ideal.py index aa18314e523..d569dc06101 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ideal.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ideal.py @@ -23,7 +23,9 @@ # **************************************************************************** from sage.rings.ideal import Ideal_generic +from sage.rings.polynomial.laurent_polynomial_ring import LaurentPolynomialRing_univariate from sage.structure.richcmp import op_EQ, op_NE, op_LT, op_LE, op_GT, op_GE +from sage.arith.misc import GCD class LaurentPolynomialIdeal( Ideal_generic ): def __init__(self, ring, gens, coerce=True, hint=None): @@ -91,7 +93,7 @@ def __init__(self, ring, gens, coerce=True, hint=None): """ Ideal_generic.__init__(self, ring, gens, coerce=coerce) self._poly_ring = ring.polynomial_ring() - self._poly_ideal = None # Create only as needed + self._poly_ideal = None # Create only as needed self._saturated = False if hint is None: self._hint = self._poly_ring.zero_ideal() @@ -192,13 +194,38 @@ def __contains__(self, f): sage: I = P.ideal([x^2*y + 3*x*y^2]) sage: x + 3*y in I True + + This also works in the univariate case:: + + sage: P. = LaurentPolynomialRing(QQ) + sage: I = P.ideal([x^2 + 3*x]) + sage: 1 + 3*x^-1 in I + True """ if not f or f in self.gens(): return True f = self.ring()(f) - g = f.__reduce__()[1][0] + if isinstance(self.ring(), LaurentPolynomialRing_univariate): + g = f.__reduce__()[1][1] + else: + g = f.__reduce__()[1][0] return (g in self.polynomial_ideal()) + def gens_reduced(self): + """ + Return a reduced system of generators. + + EXAMPLES:: + + sage: P. = LaurentPolynomialRing(QQ) + sage: J = P.ideal([x^2 - y^-2, x * y^3 + 2 * y^2+ y]) + sage: J.gens_reduced() + (x + 6*y + 5, 3*y^2 + 4*y + 1) + """ + R = self.ring() + J = self.polynomial_ideal() + return tuple([R(p) for p in J.gens()]) + # Operations on ideals def change_ring(self, R, hint=None): @@ -323,7 +350,7 @@ def toric_coordinate_change(self, M, forward_hint=True): """ if forward_hint: R = self.ring() - apply_to_hint = lambda x, M=M, R=R: R(x).toric_coordinate_change(M).__reduce__()[1][0] + apply_to_hint = lambda x, M=M, R=R: R(x).toric_coordinate_change(M).monomial_reduction()[0] else: apply_to_hint = None return self.apply_map(lambda x, M=M: x.toric_coordinate_change(M), @@ -396,20 +423,34 @@ def polynomial_ideal(self, saturate=True): sage: P. = LaurentPolynomialRing(QQ, 2) sage: I = P.ideal([x^2*y + 3*x*y^2]) sage: I.polynomial_ideal() - Ideal (x + 3*y) of Multivariate Polynomial Ring in x, y - over Rational Field + Ideal (x + 3*y) of Multivariate Polynomial Ring in x, y over Rational Field + sage: P. = LaurentPolynomialRing(QQ) + sage: J = P.ideal(t^2 - t^-1) + sage: J.polynomial_ideal() + Principal ideal (t^3 - 1) of Univariate Polynomial Ring in t over Rational Field + sage: J = P.ideal([t^2 - t^-1, t + t^-1]) + sage: J.polynomial_ideal() + Principal ideal (1) of Univariate Polynomial Ring in t over Rational Field + sage: J = P.ideal([t^2 - t^-1, t - t^-1]) + sage: J.polynomial_ideal() + Principal ideal (t - 1) of Univariate Polynomial Ring in t over Rational Field """ - if self._poly_ideal is not None and (self._saturated or not saturate): - return self._poly_ideal P = self.ring() Q = self._poly_ring + if isinstance(self.ring(), LaurentPolynomialRing_univariate): + a = [Q(p.polynomial_construction()[0]) for p in self.gens()] + if P.base_ring().is_field(): + a = GCD(a) + return Q.ideal(a) + if self._poly_ideal is not None and (self._saturated or not saturate): + return self._poly_ideal gens = self.gens() if len(gens) == 0: - I = Q.ideal([]) - self._poly_ideal = I - self._hint = I + id = Q.ideal([]) + self._poly_ideal = id + self._hint = id self._saturated = True - return I + return id l2 = [f.__reduce__()[1][0] for f in gens] hint = self._hint l2 += list(hint.groebner_basis()) diff --git a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx index 9c118a97a0f..c0fd659591f 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx +++ b/src/sage/rings/polynomial/laurent_polynomial_mpair.pyx @@ -1534,17 +1534,17 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): sage: R. = LaurentPolynomialRing(QQ) sage: f = y / x + x^2 / y + 3 * x^4 * y^-2 sage: f.monomial_reduction() - (3*x^5 + x^3*y + y^3, 1/(x*y^2)) + (3*x^5 + x^3*y + y^3, x^-1*y^-2) sage: f = y * x + x^2 / y + 3 * x^4 * y^-2 sage: f.monomial_reduction() - (3*x^3 + y^3 + x*y, x/y^2) + (3*x^3 + y^3 + x*y, x*y^-2) sage: x.monomial_reduction() (1, x) sage: (y^-1).monomial_reduction() - (1, 1/y) + (1, y^-1) """ self._normalize() - ring = self._parent._R + ring = self._parent g = ring.gens() mon = ring.prod(g[i] ** j for i, j in enumerate(self._mon)) return (self._poly, mon) @@ -1854,3 +1854,26 @@ cdef class LaurentPolynomial_mpair(LaurentPolynomial): if new_ring is not None: return new_ring(ans) return ans + + @coerce_binop + def divides(self, other): + """ + Check if ``self`` divides ``other``. + + EXAMPLES:: + + sage: R. = LaurentPolynomialRing(QQ) + sage: f1 = x^-2*y^3 - 9 - 1/14*x^-1*y - 1/3*x^-1 + sage: h = 3*x^-1 - 3*x^-2*y - 1/2*x^-3*y^2 - x^-3*y + x^-3 + sage: f2 = f1 * h + sage: f3 = f2 + x * y + sage: f1.divides(f2) + True + sage: f1.divides(f3) + False + sage: f1.divides(3) + False + """ + p = self.monomial_reduction()[0] + q = other.monomial_reduction()[0] + return p.divides(q) diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring.py b/src/sage/rings/polynomial/laurent_polynomial_ring.py index ac40e815724..812a4b3a351 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring.py @@ -442,8 +442,10 @@ def __init__(self, R): TESTS:: + sage: TestSuite(LaurentPolynomialRing(Zmod(2), 'y')).run() sage: TestSuite(LaurentPolynomialRing(Zmod(4), 'y')).run() sage: TestSuite(LaurentPolynomialRing(ZZ, 'u')).run() + sage: TestSuite(LaurentPolynomialRing(Zmod(2)['T'], 'u')).run() sage: TestSuite(LaurentPolynomialRing(Zmod(4)['T'], 'u')).run() """ if R.ngens() != 1: diff --git a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py index 0c9022c492c..c10cbb219b0 100644 --- a/src/sage/rings/polynomial/laurent_polynomial_ring_base.py +++ b/src/sage/rings/polynomial/laurent_polynomial_ring_base.py @@ -162,11 +162,9 @@ def is_noetherian(self): EXAMPLES:: sage: LaurentPolynomialRing(QQ, 2, 'x').is_noetherian() - Traceback (most recent call last): - ... - NotImplementedError + True """ - raise NotImplementedError + return self.base_ring().is_noetherian() def construction(self): """ diff --git a/src/sage/rings/polynomial/polynomial_element.pyx b/src/sage/rings/polynomial/polynomial_element.pyx index 8e9c4822a7b..d6fa1b16e05 100644 --- a/src/sage/rings/polynomial/polynomial_element.pyx +++ b/src/sage/rings/polynomial/polynomial_element.pyx @@ -11055,10 +11055,10 @@ cdef class Polynomial(CommutativePolynomial): sage: R. = Zmod(6)[] sage: p = 4*x + 3 sage: q = 5*x**2 + x + 2 + sage: q.divides(p) + False sage: p.divides(q) - Traceback (most recent call last): - ... - NotImplementedError: divisibility test only implemented for polynomials over an integral domain + False TESTS:: @@ -11081,16 +11081,24 @@ cdef class Polynomial(CommutativePolynomial): sage: q = (y^2-x^2) * z^2 + z + x-y sage: p.divides(q), p.divides(p*q) # needs sage.libs.singular (False, True) + sage: R. = Zmod(6)[] + sage: p = 4*x + 3 + sage: q = 2*x**2 + x + 2 + sage: p.divides(q) + Traceback (most recent call last): + ... + NotImplementedError: divisibility test only implemented for polynomials over an integral domain unless obvious non divisibility of leading terms """ - if not self.base_ring().is_integral_domain(): - raise NotImplementedError("divisibility test only implemented for polynomials over an integral domain") - - if p.is_zero(): return True # everything divides 0 - if self.is_zero(): return False # 0 only divides 0 + if p.is_zero(): + return True # everything divides 0 + if self.is_zero(): + return False # 0 only divides 0 try: - if self.is_unit(): return True # units divide everything + if self.is_unit(): + return True # units divide everything except NotImplementedError: - if self.is_one(): return True # if is_unit is not implemented + if self.is_one(): + return True # if is_unit is not implemented if self.degree() > p.degree(): return False @@ -11098,10 +11106,13 @@ cdef class Polynomial(CommutativePolynomial): if not self.leading_coefficient().divides(p.leading_coefficient()): return False + if not self.base_ring().is_integral_domain(): + raise NotImplementedError("divisibility test only implemented for polynomials over an integral domain unless obvious non divisibility of leading terms") + try: return (p % self).is_zero() # if quo_rem is defined except ArithmeticError: - return False # if division is not exact + return False def specialization(self, D=None, phi=None): r"""