Path: blob/master/src/sage/matrix/matrix_cyclo_dense.pyx
8815 views
"""1Matrices over Cyclotomic Fields23The underlying matrix for a Matrix_cyclo_dense object is stored as4follows: given an n x m matrix over a cyclotomic field of degree d, we5store a d x (nm) matrix over QQ, each column of which corresponds to6an element of the original matrix. This can be retrieved via the7_rational_matrix method. Here is an example illustrating this:89EXAMPLES::1011sage: F.<zeta> = CyclotomicField(5)12sage: M = Matrix(F, 2, 3, [zeta, 3, zeta**4+5, (zeta+1)**4, 0, 1])13sage: M14[ zeta 3 -zeta^3 - zeta^2 - zeta + 4]15[3*zeta^3 + 5*zeta^2 + 3*zeta 0 1]1617sage: M._rational_matrix()18[ 0 3 4 0 0 1]19[ 1 0 -1 3 0 0]20[ 0 0 -1 5 0 0]21[ 0 0 -1 3 0 0]222324AUTHORS:25* William Stein26* Craig Citro27"""2829######################################################################30# Copyright (C) 2008 William Stein31# Distributed under the terms of the GNU General Public License (GPL)32# The full text of the GPL is available at:33# http://www.gnu.org/licenses/34######################################################################3536include "sage/ext/interrupt.pxi"37# include "sage/ext/stdsage.pxi"38include "sage/ext/cdefs.pxi"39include "sage/ext/gmp.pxi"40include "sage/ext/random.pxi"41include "sage/libs/ntl/decl.pxi"4243from sage.structure.element cimport ModuleElement, RingElement, Element, Vector44from sage.misc.randstate cimport randstate, current_randstate4546from constructor import matrix47from matrix_space import MatrixSpace48from matrix cimport Matrix49import matrix_dense50from matrix_integer_dense import _lift_crt51from sage.structure.element cimport Matrix as baseMatrix52from misc import matrix_integer_dense_rational_reconstruction5354from sage.rings.rational_field import QQ55from sage.rings.integer_ring import ZZ56from sage.rings.arith import previous_prime, binomial57from sage.rings.all import RealNumber58from sage.rings.integer cimport Integer59from sage.rings.rational cimport Rational60from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing61from sage.rings.number_field.number_field import NumberField_quadratic62from sage.rings.number_field.number_field_element cimport NumberFieldElement63from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic6465from sage.structure.proof.proof import get_flag as get_proof_flag66from sage.misc.misc import verbose67import math6869from sage.matrix.matrix_modn_dense_double import MAX_MODULUS as MAX_MODULUS_modn_dense_double70from sage.ext.multi_modular import MAX_MODULUS as MAX_MODULUS_multi_modular71MAX_MODULUS = min(MAX_MODULUS_modn_dense_double, MAX_MODULUS_multi_modular)7273# parameters for tuning74echelon_primes_increment = 1575echelon_verbose_level = 17677cdef class Matrix_cyclo_dense(matrix_dense.Matrix_dense):78########################################################################79# LEVEL 1 functionality80# x * __cinit__81# x * __dealloc__ (not needed)82# x * __init__83# x * set_unsafe84# x * get_unsafe85# x * _pickle86# x * _unpickle87########################################################################8889def __cinit__(self, parent, entries, coerce, copy):90"""91Create a new dense cyclotomic matrix.9293INPUT:94parent -- a matrix space over a cyclotomic field95entries -- a list of entries or scalar96coerce -- bool; if true entries are coerced to base ring97copy -- bool; ignored due to underlying data structure9899EXAMPLES::100101sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense102sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)103sage: type(A)104<type 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense'>105106Note that the entries of A haven't even been set yet above; that doesn't107happen until init is called::108109sage: A[0,0]110Traceback (most recent call last):111...112ValueError: matrix entries not yet initialized113"""114Matrix.__init__(self, parent)115self._degree = self._base_ring.degree()116self._n = int(self._base_ring._n())117118# This is not necessary, since we do not (yet) explicitly allocate119# any memory.120#def __dealloc__(self):121# pass122123def __init__(self, parent, entries=None, copy=True, coerce=True):124"""125Initialize a newly created cyclotomic matrix.126127INPUT:128129- ``parent`` -- a matrix space over a cyclotomic field130131- ``entries`` -- a list of entries or scalar132133- ``coerce`` -- boolean; if true entries are coerced to base134ring135136- ``copy`` -- boolean; ignored due to underlying data137structure138139EXAMPLES:140141This function is called implicitly when you create new142cyclotomic dense matrices::143144sage: W.<a> = CyclotomicField(100)145sage: A = matrix(2, 3, [1, 1/a, 1-a,a, -2/3*a, a^19])146sage: A147[ 1 -a^39 + a^29 - a^19 + a^9 -a + 1]148[ a -2/3*a a^19]149sage: TestSuite(A).run()150151TESTS::152153sage: matrix(W, 2, 1, a)154Traceback (most recent call last):155...156TypeError: nonzero scalar matrix must be square157158We call __init__ explicitly below::159160sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense161sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)162sage: A.__init__(MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True)163sage: A164[0 1]165[2 3]166167"""168cdef int i169z = None170if (entries is None) or (entries == 0):171pass172elif isinstance(entries, list):173# This code could be made much faster using Cython, etc.174if coerce:175K = parent.base_ring()176entries = [K(a) for a in entries]177entries = sum([a.list() for a in entries], [])178else:179K = self._base_ring180z = K(entries)181entries = 0182183self._n = int(self._base_ring._n())184self._matrix = Matrix_rational_dense(MatrixSpace(QQ, self._nrows*self._ncols, self._degree),185entries, copy=False, coerce=False).transpose()186# This could also be made much faster.187if z is not None:188if self._nrows != self._ncols:189raise TypeError, "nonzero scalar matrix must be square"190for i in range(self._nrows):191self.set_unsafe(i,i,z)192193cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):194"""195Set the ij-th entry of self.196197WARNING: This function does no bounds checking whatsoever, as198the name suggests. It also assumes certain facts about the199internal representation of cyclotomic fields. This is intended200for internal use only.201202EXAMPLES::203204sage: K.<z> = CyclotomicField(11) ; M = Matrix(K,2,range(4))205sage: M[0,1] = z ; M206[0 z]207[2 3]208209sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4))210sage: M[1,1] = z+1 ; M211[ 0 1]212[ 2 z + 1]213214TESTS:215216Since separate code exists for each quadratic field, we need217doctests for each.::218219sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4))220sage: M[1,1] = z+1 ; M221[ 0 1]222[ 2 z + 1]223sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4))224sage: M[1,1] = z+1 ; M225[ 0 1]226[ 2 z + 1]227"""228# NEW FAST VERSION -- makes assumptions about how the229# cyclotomic field is implemented.230cdef Py_ssize_t k, c231cdef NumberFieldElement v232cdef mpz_t numer, denom233234# The i,j entry is the (i * self._ncols + j)'th column.235c = i * self._ncols + j236237if PY_TYPE_CHECK_EXACT(value, NumberFieldElement_quadratic):238# Must be coded differently, since elements of239# quadratic number fields are stored differently.240if self._n == 4:241mpz_set(mpq_numref(self._matrix._matrix[0][c]),242(<NumberFieldElement_quadratic>value).a)243mpz_set(mpq_denref(self._matrix._matrix[0][c]),244(<NumberFieldElement_quadratic>value).denom)245mpq_canonicalize(self._matrix._matrix[0][c])246247mpz_set(mpq_numref(self._matrix._matrix[1][c]),248(<NumberFieldElement_quadratic>value).b)249mpz_set(mpq_denref(self._matrix._matrix[1][c]),250(<NumberFieldElement_quadratic>value).denom)251mpq_canonicalize(self._matrix._matrix[1][c])252elif self._n == 3:253mpz_set(mpq_numref(self._matrix._matrix[0][c]),254(<NumberFieldElement_quadratic>value).a)255mpz_add(mpq_numref(self._matrix._matrix[0][c]),256mpq_numref(self._matrix._matrix[0][c]),257(<NumberFieldElement_quadratic>value).b)258mpz_set(mpq_denref(self._matrix._matrix[0][c]),259(<NumberFieldElement_quadratic>value).denom)260mpq_canonicalize(self._matrix._matrix[0][c])261262mpz_set(mpq_numref(self._matrix._matrix[1][c]),263(<NumberFieldElement_quadratic>value).b)264mpz_mul_si(mpq_numref(self._matrix._matrix[1][c]),265mpq_numref(self._matrix._matrix[1][c]), 2)266mpz_set(mpq_denref(self._matrix._matrix[1][c]),267(<NumberFieldElement_quadratic>value).denom)268mpq_canonicalize(self._matrix._matrix[1][c])269else: # self._n is 6270mpz_set(mpq_numref(self._matrix._matrix[0][c]),271(<NumberFieldElement_quadratic>value).a)272mpz_sub(mpq_numref(self._matrix._matrix[0][c]),273mpq_numref(self._matrix._matrix[0][c]),274(<NumberFieldElement_quadratic>value).b)275mpz_set(mpq_denref(self._matrix._matrix[0][c]),276(<NumberFieldElement_quadratic>value).denom)277mpq_canonicalize(self._matrix._matrix[0][c])278279mpz_set(mpq_numref(self._matrix._matrix[1][c]),280(<NumberFieldElement_quadratic>value).b)281mpz_mul_si(mpq_numref(self._matrix._matrix[1][c]),282mpq_numref(self._matrix._matrix[1][c]), 2)283mpz_set(mpq_denref(self._matrix._matrix[1][c]),284(<NumberFieldElement_quadratic>value).denom)285mpq_canonicalize(self._matrix._matrix[1][c])286return287288v = value289290mpz_init(numer)291mpz_init(denom)292293v._ntl_denom_as_mpz(&denom)294for k from 0 <= k < self._degree:295v._ntl_coeff_as_mpz(&numer, k)296mpz_set(mpq_numref(self._matrix._matrix[k][c]), numer)297mpz_set(mpq_denref(self._matrix._matrix[k][c]), denom)298mpq_canonicalize(self._matrix._matrix[k][c])299300mpz_clear(numer)301mpz_clear(denom)302303cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):304"""305Get the ij-th of self.306307WARNING: As the name suggests, expect segfaults if i,j are out308of bounds!! This is for internal use only.309310EXAMPLES::311312sage: W.<a> = CyclotomicField(5)313sage: A = matrix(2, 3, [9939208341, 1/a, 1-a,a, -2/3*a, a^19])314315This implicitly calls get_unsafe::316317sage: A[0,0]3189939208341319320TESTS:321322Since separate code exists for each quadratic field, we need323doctests for each.::324325sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4))326sage: M[1,1] = z+1 ; M[1,1]327z + 1328sage: (M[1,1] - M[0,1])**33291330sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4))331sage: M[1,1] = z+1 ; M[1,1]332z + 1333sage: (M[1,1] - M[0,1])**43341335sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4))336sage: M[1,1] = z+1 ; M[1,1]337z + 1338sage: (M[1,1] - M[0,1])**63391340"""341cdef Py_ssize_t k, c342cdef NumberFieldElement x343cdef NumberFieldElement_quadratic xq344cdef mpz_t denom, quo, tmp345cdef ZZ_c coeff346347if self._matrix is None:348raise ValueError, "matrix entries not yet initialized"349350c = i * self._ncols + j351mpz_init(tmp)352353if self._degree == 2:354xq = self._base_ring(0)355if self._n == 4:356mpz_mul(xq.a, mpq_numref(self._matrix._matrix[0][c]),357mpq_denref(self._matrix._matrix[1][c]))358mpz_mul(xq.b, mpq_numref(self._matrix._matrix[1][c]),359mpq_denref(self._matrix._matrix[0][c]))360mpz_mul(xq.denom, mpq_denref(self._matrix._matrix[0][c]),361mpq_denref(self._matrix._matrix[1][c]))362else: # n is 3 or 6363mpz_mul(xq.a, mpq_numref(self._matrix._matrix[0][c]),364mpq_denref(self._matrix._matrix[1][c]))365mpz_mul_si(xq.a, xq.a, 2)366mpz_mul(tmp, mpq_denref(self._matrix._matrix[0][c]),367mpq_numref(self._matrix._matrix[1][c]))368if self._n == 3:369mpz_sub(xq.a, xq.a, tmp)370else: # n == 6371mpz_add(xq.a, xq.a, tmp)372373mpz_mul(xq.b, mpq_denref(self._matrix._matrix[0][c]),374mpq_numref(self._matrix._matrix[1][c]))375376mpz_mul(xq.denom, mpq_denref(self._matrix._matrix[0][c]),377mpq_denref(self._matrix._matrix[1][c]))378mpz_mul_si(xq.denom, xq.denom, 2)379380xq._reduce_c_()381mpz_clear(tmp)382return xq383384x = self._base_ring(0)385ZZ_construct(&coeff)386mpz_init_set_ui(denom, 1)387388# Get the least common multiple of the denominators in389# this column.390for k from 0 <= k < self._degree:391mpz_lcm(denom, denom, mpq_denref(self._matrix._matrix[k][c]))392393for k from 0 <= k < self._degree:394# set each entry of x to a*denom/b where a/b is the395# k,c entry of _matrix.396mpz_mul(tmp, mpq_numref(self._matrix._matrix[k][c]), denom)397mpz_divexact(tmp, tmp, mpq_denref(self._matrix._matrix[k][c]))398# Now set k-th entry of x's numerator to tmp399mpz_to_ZZ(&coeff, &tmp)400ZZX_SetCoeff(x.__numerator, k, coeff)401402# Set the denominator of x to denom.403mpz_to_ZZ(&x.__denominator, &denom)404mpz_clear(denom)405mpz_clear(tmp)406ZZ_destruct(&coeff)407408return x409410def _pickle(self):411"""412Used for pickling matrices. This function returns the413underlying data and pickle version.414415OUTPUT:416data -- output of pickle417version -- int418419EXAMPLES::420421sage: K.<z> = CyclotomicField(3)422sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z])423sage: w._pickle()424(('0 0 -2 2 0 0 0 1 2 0 -1 0 -2 2 1 1 -1 3', 0), 0)425"""426data = self._matrix._pickle()427return data, 0428429def _unpickle(self, data, int version):430"""431Called when unpickling matrices.432433INPUT:434data -- a string435version -- int436437This modifies self.438439EXAMPLES::440441sage: K.<z> = CyclotomicField(3)442sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z])443sage: data, version = w._pickle()444sage: k = w.parent()(0)445sage: k._unpickle(data, version)446sage: k == w447True448"""449#self.check_mutability()450if version == 0:451self._matrix = Matrix_rational_dense(MatrixSpace(QQ, self._degree, self._nrows*self._ncols), None, False, False)452self._matrix._unpickle(*data) # data is (data, matrix_QQ_version)453else:454raise RuntimeError, "unknown matrix version (=%s)"%version455456########################################################################457# LEVEL 2 functionality458# x * cdef _add_459# x * cdef _sub_460# * cdef _mul_461# x * cdef _lmul_ -- scalar multiplication462# x * cdef _cmp_c_impl463# x * __neg__464# * __invert__465# x * __copy__466# * _multiply_classical467# * _list -- list of underlying elements (need not be a copy)468# * _dict -- sparse dictionary of underlying elements (need not be a copy)469########################################################################470471cpdef ModuleElement _add_(self, ModuleElement right):472"""473Return the sum of two dense cyclotomic matrices.474475INPUT:476self, right -- dense cyclotomic matrices with the same477parents478OUTPUT:479a dense cyclotomic matrix480481EXAMPLES::482483sage: W.<z> = CyclotomicField(5)484sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]); B = matrix(2, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z])485sage: A + B486[ 0 3*z 4*z^2]487[ z^3 + 5*z + 1 -2*z^3 - 2*z^2 - 2*z - 2 z]488489Verify directly that the above output is correct::490491sage: (A+B).list() == [A.list()[i]+B.list()[i] for i in range(6)]492True493"""494cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)495# Fix the redundancy here.496A._matrix = self._matrix + (<Matrix_cyclo_dense>right)._matrix497return A498499cpdef ModuleElement _sub_(self, ModuleElement right):500"""501Return the difference of two dense cyclotomic matrices.502503INPUT:504self, right -- dense cyclotomic matrices with the same505parent506OUTPUT:507a dense cyclotomic matrix508509EXAMPLES::510511sage: W.<z> = CyclotomicField(5)512sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]); B = matrix(2, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z])513sage: A - B514[ 2 -z -2*z^2]515[z^3 - 5*z - 1 0 1/3*z]516517Verify directly that the above output is correct::518519sage: (A-B).list() == [A.list()[i]-B.list()[i] for i in range(6)]520True521"""522cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)523A._matrix = self._matrix - (<Matrix_cyclo_dense>right)._matrix524return A525526cpdef ModuleElement _lmul_(self, RingElement right):527"""528Multiply a dense cyclotomic matrix by a scalar.529530INPUT:531self -- dense cyclotomic matrix532right --- scalar in the base cyclotomic field533534EXAMPLES::535536sage: W.<z> = CyclotomicField(5)537sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z])538sage: (1 + z/3)*A539[ 1/3*z + 1 1/3*z^2 + z 1/3*z^3 + z^2]540[2/3*z^3 - 1/3*z^2 - 1/3*z - 1/3 -z^3 - z^2 - z - 2/3 2/9*z^2 + 2/3*z]541542Verify directly that the above output is correct::543544sage: ((1+z/3)*A).list() == [(1+z/3)*x for x in A.list()]545True546"""547if right == 1:548return self549elif right == 0:550return self.parent()(0)551552# Create a new matrix object but with the _matrix attribute not initialized:553cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense,554self.parent(), None, None, None)555556if right.polynomial().degree() == 0:557# multiplication by a rational number558A._matrix = self._matrix._lmul_(right)559else:560# Multiply by nontrivial element of the cyclotomic number field561# We do this by finding the matrix of this element, then left562# multiplying by it. We have to tweak the matrix some to563# get the right basis, etc.564T = right.matrix().transpose()565A._matrix = T * self._matrix566return A567568cdef baseMatrix _matrix_times_matrix_(self, baseMatrix right):569"""570Return the product of two cyclotomic dense matrices.571572INPUT:573self, right -- cyclotomic dense matrices with compatible574parents (same base ring, and compatible575dimensions for matrix multiplication).576577OUTPUT:578cyclotomic dense matrix579580ALGORITHM:581Use a multimodular algorithm that involves multiplying the582two matrices modulo split primes.583584EXAMPLES::585586sage: W.<z> = CyclotomicField(5)587sage: A = matrix(3, 3, [1,z,z^2,z^3,z^4,2/3*z,-3*z,z,2+z]); B = matrix(3, 3, [-1,2*z,3*z^2,5*z+1,z^4,1/3*z,2-z,3-z,5-z])588sage: A*B589[ -z^3 + 7*z^2 + z - 1 -z^3 + 3*z^2 + 2*z + 1 -z^3 + 25/3*z^2]590[-2*z^3 - 5/3*z^2 + 1/3*z + 4 -z^3 - 8/3*z^2 - 2 -2/3*z^2 + 10/3*z + 10/3]591[ 4*z^2 + 4*z + 4 -7*z^2 + z + 7 -9*z^3 - 2/3*z^2 + 3*z + 10]592593Verify that the answer above is consistent with what the594generic sparse matrix multiply gives (which is a different595implementation).::596597sage: A*B == A.sparse_matrix()*B.sparse_matrix()598True599600sage: N1 = Matrix(CyclotomicField(6), 1, [1])601sage: cf6 = CyclotomicField(6) ; z6 = cf6.0602sage: N2 = Matrix(CyclotomicField(6), 1, 5, [0,1,z6,-z6,-z6+1])603sage: N1*N2604[ 0 1 zeta6 -zeta6 -zeta6 + 1]605sage: N1 = Matrix(CyclotomicField(6), 1, [-1])606sage: N1*N2607[ 0 -1 -zeta6 zeta6 zeta6 - 1]608609Verify that a degenerate case bug reported at trac 5974 is fixed.610611sage: K.<zeta6>=CyclotomicField(6); matrix(K,1,2) * matrix(K,2,[0, 1, 0, -2*zeta6, 0, 0, 1, -2*zeta6 + 1])612[0 0 0 0]613614TESTS:615616This is from trac #8666::617618sage: K.<zeta4> = CyclotomicField(4)619sage: m = matrix(K, [125])620sage: n = matrix(K, [186])621sage: m*n622[23250]623sage: (-m)*n624[-23250]625"""626A, denom_self = self._matrix._clear_denom()627B, denom_right = (<Matrix_cyclo_dense>right)._matrix._clear_denom()628629# conservative but correct estimate: 2 is there to account for the630# sign of the entries631bound = 1 + 2 * A.height() * B.height() * self._ncols632633n = self._base_ring._n()634p = previous_prime(MAX_MODULUS)635prod = 1636v = []637while prod <= bound:638while (n >= 2 and p % n != 1) or denom_self % p == 0 or denom_right % p == 0:639if p == 2:640raise RuntimeError, "we ran out of primes in matrix multiplication."641p = previous_prime(p)642prod *= p643Amodp, _ = self._reductions(p)644Bmodp, _ = right._reductions(p)645_, S = self._reduction_matrix(p)646X = Amodp[0]._matrix_from_rows_of_matrices([Amodp[i] * Bmodp[i] for i in range(len(Amodp))])647v.append(S*X)648p = previous_prime(p)649M = matrix(ZZ, self._base_ring.degree(), self._nrows*right.ncols())650_lift_crt(M, v)651d = denom_self * denom_right652if d == 1:653M = M.change_ring(QQ)654else:655M = (1/d)*M656cdef Matrix_cyclo_dense C = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense,657MatrixSpace(self._base_ring, self._nrows, right.ncols()),658None, None, None)659C._matrix = M660return C661662def __richcmp__(Matrix self, right, int op):663"""664Compare a matrix with something else. This immediately calls665a base class _richcmp.666667EXAMPLES::668669sage: W.<z> = CyclotomicField(5)670sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])671672These implicitly call richcmp::673674sage: A == 5675False676sage: A < 100677True678"""679return self._richcmp(right, op)680681cdef long _hash(self) except -1:682"""683Return hash of this matrix.684685EXAMPLES:686687This is called implicitly by the hash function.::688689sage: W.<z> = CyclotomicField(5)690sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])691692The matrix must be immutable.::693694sage: hash(A)695Traceback (most recent call last):696...697TypeError: mutable matrices are unhashable698sage: A.set_immutable()699700Yes, this works::701702sage: hash(A)703-25704"""705return self._matrix._hash()706707def __hash__(self):708"""709Return hash of an immutable matrix. Raise a TypeError if input710matrix is mutable.711712EXAMPLES::713714sage: W.<z> = CyclotomicField(5)715sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])716sage: hash(A)717Traceback (most recent call last):718...719TypeError: mutable matrices are unhashable720sage: A.set_immutable()721sage: A.__hash__()722-18723"""724if self._is_immutable:725return self._hash()726else:727raise TypeError, "mutable matrices are unhashable"728729cdef int _cmp_c_impl(self, Element right) except -2:730"""731Implements comparison of two cyclotomic matrices with732identical parents.733734INPUT:735self, right -- matrices with same parent736OUTPUT:737int; either -1, 0, or 1738739EXAMPLES:740741This function is called implicitly when comparisons with matrices742are done or the cmp function is used.::743744sage: W.<z> = CyclotomicField(5)745sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])746sage: cmp(A,A)7470748sage: cmp(A,2*A)749-1750sage: cmp(2*A,A)7511752"""753return self._matrix._cmp_c_impl((<Matrix_cyclo_dense>right)._matrix)754755def __copy__(self):756"""757Make a copy of this matrix.758759EXAMPLES:760We create a cyclotomic matrix.::761762sage: W.<z> = CyclotomicField(5)763sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])764765We make a copy of A.::766sage: C = A.__copy__()767768We make another reference to A.::769770sage: B = A771772Changing this reference changes A itself::773774sage: B[0,0] = 10775sage: A[0,0]77610777778Changing the copy does not change A.::779780sage: C[0,0] = 20781sage: C[0,0]78220783sage: A[0,0]78410785"""786cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)787A._matrix = self._matrix.__copy__()788return A789790def __neg__(self):791"""792Return the negative of this matrix.793794OUTPUT:795matrix796797EXAMPLES::798799sage: W.<z> = CyclotomicField(5)800sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])801sage: -A802[ -1 -z^2 - 2/3*z]803[ z -1/2*z - 1]804sage: A.__neg__()805[ -1 -z^2 - 2/3*z]806[ z -1/2*z - 1]807"""808cdef Matrix_cyclo_dense A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(), None, None, None)809A._matrix = self._matrix.__neg__()810return A811812813########################################################################814# LEVEL 3 functionality (Optional)815# * __deepcopy__816# * __invert__817# * Matrix windows -- only if you need strassen for that base818# * Other functions (list them here):819# * Specialized echelon form820########################################################################821def set_immutable(self):822"""823Change this matrix so that it is immutable.824825EXAMPLES::826827sage: W.<z> = CyclotomicField(5)828sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])829sage: A[0,0] = 10830sage: A.set_immutable()831sage: A[0,0] = 20832Traceback (most recent call last):833...834ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).835836Note that there is no function to set a matrix to be mutable837again, since such a function would violate the whole point.838Instead make a copy, which is always mutable by default.::839840sage: A.set_mutable()841Traceback (most recent call last):842...843AttributeError: 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense' object has no attribute 'set_mutable'844sage: B = A.__copy__()845sage: B[0,0] = 20846sage: B[0,0]84720848"""849self._matrix.set_immutable()850matrix_dense.Matrix_dense.set_immutable(self)851852def _rational_matrix(self):853"""854Return the underlying rational matrix corresponding to self.855856EXAMPLES::857858sage: Matrix(CyclotomicField(7),4,4,range(16))._rational_matrix()859[ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15]860[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]861[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]862[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]863[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]864[ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]865sage: Matrix(CyclotomicField(7),4,4,[CyclotomicField(7).gen(0)**i for i in range(16)])._rational_matrix()866[ 1 0 0 0 0 0 -1 1 0 0 0 0 0 -1 1 0]867[ 0 1 0 0 0 0 -1 0 1 0 0 0 0 -1 0 1]868[ 0 0 1 0 0 0 -1 0 0 1 0 0 0 -1 0 0]869[ 0 0 0 1 0 0 -1 0 0 0 1 0 0 -1 0 0]870[ 0 0 0 0 1 0 -1 0 0 0 0 1 0 -1 0 0]871[ 0 0 0 0 0 1 -1 0 0 0 0 0 1 -1 0 0]872"""873return self._matrix874875def denominator(self):876"""877Return the denominator of the entries of this matrix.878879OUTPUT:880integer -- the smallest integer d so that d * self has881entries in the ring of integers882883EXAMPLES::884885sage: W.<z> = CyclotomicField(5)886sage: A = matrix(W, 2, 2, [-2/7,2/3*z+z^2,-z,1+z/19]); A887[ -2/7 z^2 + 2/3*z]888[ -z 1/19*z + 1]889sage: d = A.denominator(); d890399891"""892return self._matrix.denominator()893894def coefficient_bound(self):895r"""896Return an upper bound for the (complex) absolute values of all897entries of self with respect to all embeddings.898899Use \code{self.height()} for a sharper bound.900901This is computed using just the Cauchy-Schwarz inequality, i.e.,902we use the fact that903$$ \left| \sum_i a_i\zeta^i \right| \leq \sum_i |a_i|, $$904as $|\zeta| = 1$.905906EXAMPLES::907908sage: W.<z> = CyclotomicField(5)909sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A910[ z + 1 0]911[9*z + 7 4*z - 3]912sage: A.coefficient_bound()91316914915The above bound is just $9 + 7$, coming from the lower left entry.916A better bound would be the following::917918sage: (A[1,0]).abs()91912.997543663...920"""921cdef Py_ssize_t i, j922923bound = 0924for i from 0 <= i < self._matrix._ncols:925926n = 0927for j from 0 <= j < self._matrix._nrows:928n += self._matrix[j, i].abs()929if bound < n:930bound = n931932return bound933934935def height(self):936r"""937Return the height of self.938939If we let $a_{ij}$ be the $i,j$ entry of self, then we define940the height of self to be941$$942\max_v \max_{i,j} |a_{ij}|_v,943$$944where $v$ runs over all complex embeddings of \code{self.base_ring()}.945946EXAMPLES::947948sage: W.<z> = CyclotomicField(5)949sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A950[ z + 1 0]951[9*z + 7 4*z - 3]952sage: A.height()95312.997543663...954sage: (A[1,0]).abs()95512.997543663...956"""957cdef Py_ssize_t i, j958959emb = self._base_ring.complex_embeddings()960961ht = 0962for i from 0 <= i < self._nrows:963for j from 0 <= j < self._ncols:964t = max([ x.norm().sqrt() for x in [ f(self.get_unsafe(i,j)) for f in emb ] ])965if t > ht:966ht = t967968return ht969970cdef _randomize_rational_column_unsafe(Matrix_cyclo_dense self,971Py_ssize_t col, mpz_t nump1, mpz_t denp1, distribution=None):972"""973Randomizes all entries in column ``col``. This is a helper method974used in the implementation of dense matrices over cyclotomic fields.975976INPUT:977978- ``col`` - Integer, indicating the column; must be coercable to979``int``, and this must lie between 0 (inclusive) and980``self._ncols`` (exclusive), since no bounds-checking is performed981- ``nump1`` - Integer, numerator bound plus one982- ``denp1`` - Integer, denominator bound plus one983- ``distribution`` - ``None`` or '1/n' (default: ``None``); if '1/n'984then ``num_bound``, ``den_bound`` are ignored and numbers are chosen985using the GMP function ``mpq_randomize_entry_recip_uniform``986- ``nonzero`` - Bool (default: ``False``); whether the new entries987are forced to be non-zero988989OUTPUT:990991- None, the matrix is modified in-space992993WARNING:994995This method is quite unsafe. It's called from the method996``randomize``, but probably shouldn't be called from another method997without first carefully reading the source code!998999TESTS:10001001The following doctests are all indirect::10021003sage: MS = MatrixSpace(CyclotomicField(10), 4, 4)1004sage: A = MS.random_element(); A1005[ -2*zeta10^3 + 2*zeta10^2 - zeta10 zeta10^3 + 2*zeta10^2 - zeta10 + 1 0 -2*zeta10^3 + zeta10^2 - 2*zeta10 + 2]1006[ 0 -zeta10^3 + 2*zeta10^2 - zeta10 -zeta10^3 + 1 zeta10^3 + zeta10]1007[ 1/2*zeta10^2 -2*zeta10^2 + 2 -1/2*zeta10^3 + 1/2*zeta10^2 + 2 2*zeta10^3 - zeta10^2 - 2]1008[ 1 zeta10^2 + 2 2*zeta10^2 2*zeta10 - 2]1009sage: B = MS.random_element(density=0.5)1010sage: B._rational_matrix()1011[ 0 0 0 0 1 0 0 2 0 2 0 0 0 0 0 0]1012[ 0 0 0 0 0 0 0 0 -1 -2 0 0 0 0 0 2]1013[ 0 -1 0 0 -1 0 0 0 0 0 0 0 0 0 -2 -1]1014[ 0 0 0 0 0 0 0 2 -1/2 1/2 0 0 0 0 -1 0]1015sage: C = MS.random_element(density=0.5, num_bound=20, den_bound=20)1016sage: C._rational_matrix()1017[ 0 0 8/11 -10/3 -11/7 8 1 -3 0 0 1 0 0 0 0 0]1018[ 0 0 -11/17 -3/13 -5/6 17/3 -19/17 -4/5 0 0 9 0 0 0 0 0]1019[ 0 0 -11 -3/2 -5/12 8/11 0 -3/19 0 0 -5/6 0 0 0 0 0]1020[ 0 0 0 5/8 -5/11 -5/4 6/11 2/3 0 0 -16/11 0 0 0 0 0]1021"""1022cdef Py_ssize_t i1023cdef Matrix_rational_dense mat = self._matrix10241025sig_on()1026if distribution == "1/n":1027for i from 0 <= i < mat._nrows:1028mpq_randomize_entry_recip_uniform(mat._matrix[i][col])1029elif mpz_cmp_si(denp1, 2): # denom is > 11030for i from 0 <= i < mat._nrows:1031mpq_randomize_entry(mat._matrix[i][col], nump1, denp1)1032else:1033for i from 0 <= i < mat._nrows:1034mpq_randomize_entry_as_int(mat._matrix[i][col], nump1)1035sig_off()10361037def randomize(self, density=1, num_bound=2, den_bound=2, \1038distribution=None, nonzero=False, *args, **kwds):1039r"""1040Randomize the entries of ``self``.10411042Choose rational numbers according to ``distribution``, whose1043numerators are bounded by ``num_bound`` and whose denominators are1044bounded by ``den_bound``.10451046EXAMPLES::10471048sage: A = Matrix(CyclotomicField(5),2,2,range(4)) ; A1049[0 1]1050[2 3]1051sage: A.randomize()1052sage: A # random output1053[ 1/2*zeta5^2 + zeta5 1/2]1054[ -zeta5^2 + 2*zeta5 -2*zeta5^3 + 2*zeta5^2 + 2]1055"""1056# Problem 1:1057# We cannot simply call the ``randomize`` code in ``matrix2.pyx`` on1058# the underlying matrix, since this is a d x (mn) matrix, where d is1059# the degree of the field extension, which leads to an overly dense1060# matrix.1061#1062# Problem 2:1063# We cannot simply copy the code from ``matrix2.pyx``, since the1064# ``random_element`` method for cyclotomic fields does not support1065# the arguments ``num_bound`` and ``den_bound``, which are support by1066# the rational field.1067#1068# Proposed solution:1069# Randomly select a proportion of ``density`` of the elements in the1070# matrix over the cyclotomic field, that is, this many columns in the1071# underlying rational matrix. Then, for each element in that column,1072# randomize it to a rational number, applying the arguments1073# ``num_bound`` and ``den_bound``.10741075density = float(density)1076if density <= 0:1077return1078if density > 1:1079density = 110801081self.check_mutability()1082self.clear_cache()10831084cdef Py_ssize_t col, i, k, num1085cdef randstate rstate = current_randstate()1086cdef Integer B, C1087cdef bint col_is_zero10881089B = Integer(num_bound+1)1090C = Integer(den_bound+1)10911092if nonzero:1093if density >= 1:1094for col from 0 <= col < self._matrix._ncols:1095col_is_zero = True1096while col_is_zero:1097self._randomize_rational_column_unsafe(col, B.value, \1098C.value, distribution)1099# Check whether the new column is non-zero1100for i from 0 <= i < self._degree:1101if mpq_sgn(self._matrix._matrix[i][col]) != 0:1102col_is_zero = False1103break1104else:1105num = int(self._nrows * self._ncols * density)1106for k from 0 <= k < num:1107col = rstate.c_random() % self._matrix._ncols1108col_is_zero = True1109while col_is_zero:1110self._randomize_rational_column_unsafe(col, B.value, \1111C.value, distribution)1112# Check whether the new column is non-zero1113for i from 0 <= i < self._degree:1114if mpq_sgn(self._matrix._matrix[i][col]) != 0:1115col_is_zero = False1116break1117else:1118if density >= 1:1119for col from 0 <= col < self._matrix._ncols:1120self._randomize_rational_column_unsafe(col, B.value, \1121C.value, distribution)1122else:1123num = int(self._nrows * self._ncols * density)1124for k from 0 <= k < num:1125col = rstate.c_random() % self._matrix._ncols1126self._randomize_rational_column_unsafe(col, B.value, \1127C.value, distribution)11281129def _charpoly_bound(self):1130"""1131Determine a bound for the coefficients of the characteristic1132polynomial of self. We use the bound in Lemma 2.2 of:11331134Dumas, J-G. "Bounds on the coefficients of characteristic1135and minimal polynomials." J. Inequal. Pure Appl. Math. 81136(2007), no. 2.11371138This bound only applies for self._nrows >= 4, so in all1139smaller cases, we just use a naive bound.11401141EXAMPLES::11421143sage: A = Matrix(CyclotomicField(7),3,3,range(9))1144sage: A._charpoly_bound()114520481146sage: A.charpoly()1147x^3 - 12*x^2 - 18*x11481149An example from the above paper, where the bound is sharp::11501151sage: B = Matrix(CyclotomicField(7), 5,5, [1,1,1,1,1,1,1,-1,-1,-1,1,-1,1,-1,-1,1,-1,-1,1,-1,1,-1,-1,-1,1])1152sage: B._charpoly_bound()1153801154sage: B.charpoly()1155x^5 - 5*x^4 + 40*x^2 - 80*x + 481156"""1157cdef Py_ssize_t i11581159# should we even bother with this check, or just say in1160# the docstring that we assume it's square?1161if self._nrows != self._ncols:1162raise ArithmeticError, "self must be a square matrix"11631164if self.is_zero():1165return 111661167B = self.coefficient_bound()11681169# TODO: should charpoly just hardcode the return value for1170# self.nrows() < 4?11711172# this bound is only valid for n >= 4, use naive bounds1173# in other cases.1174if self._nrows <= 3:1175return max(1, 3*B, 6*B**2, 4*B**3)11761177# This is an approximation to 2^(5/6*log_2(5) - 2/3*log_2(6))1178alpha = RealNumber('1.15799718800731')1179# This is 2*e^(1-(2(7\gamma-4))/(13(3-2\gamma))), where \gamma1180# is Euler's constant.1181delta = RealNumber('5.418236')1182# This is an approximation to 1/2. :)1183half = RealNumber('0.5')11841185D = (((1+2*delta*self._nrows*(B**2)).sqrt()-1)/(delta*B**2)).ceil()11861187# TODO: we don't check anything about overflows anywhere here;1188# should we?11891190# i = 0 case1191M = ((self._nrows * B**2)**(self._nrows * half)).ceil()11921193for i from 1 <= i < D:1194val = binomial(self._nrows, i) * \1195(((self._nrows-i)*B**2)**((self._nrows-i)*half)).ceil()1196if val > M:1197M = val11981199return M120012011202def charpoly(self, var='x', algorithm="multimodular", proof=None):1203r"""1204Return the characteristic polynomial of self, as a polynomial1205over the base ring.12061207INPUT:1208algorithm -- 'multimodular' (default): reduce modulo1209primes, compute charpoly mod1210p, and lift (very fast)1211'pari': use pari (quite slow; comparable to1212Magma v2.14 though)1213'hessenberg': put matrix in Hessenberg form1214(double dog slow)1215proof -- bool (default: None) proof flag determined by1216global linalg proof.12171218OUTPUT:1219polynomial12201221EXAMPLES::12221223sage: K.<z> = CyclotomicField(5)1224sage: a = matrix(K, 3, [1,z,1+z^2, z/3,1,2,3,z^2,1-z])1225sage: f = a.charpoly(); f1226x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/31227sage: f(a)1228[0 0 0]1229[0 0 0]1230[0 0 0]1231sage: a.charpoly(algorithm='pari')1232x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/31233sage: a.charpoly(algorithm='hessenberg')1234x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/312351236sage: Matrix(K, 1, [0]).charpoly()1237x1238sage: Matrix(K, 1, [5]).charpoly(var='y')1239y - 512401241sage: Matrix(CyclotomicField(13),3).charpoly()1242x^31243sage: Matrix(CyclotomicField(13),3).charpoly()[2].parent()1244Cyclotomic Field of order 13 and degree 1212451246TESTS::12471248sage: Matrix(CyclotomicField(10),0).charpoly()124911250"""1251key = 'charpoly-%s-%s'%(algorithm,proof)1252f = self.fetch(key)1253if f is not None:1254return f.change_variable_name(var)12551256if self.nrows() != self.ncols():1257raise TypeError, "self must be square"12581259if self.is_zero():1260R = PolynomialRing(self.base_ring(), name=var)1261f = R.gen(0)**self.nrows()1262self.cache(key, f)1263return f12641265if self.nrows() == 1:1266R = PolynomialRing(self.base_ring(), name=var)1267f = R.gen(0) - self[0,0]1268self.cache(key, f)1269return f12701271if algorithm == 'multimodular':1272f = self._charpoly_multimodular(var, proof=proof)1273elif algorithm == 'pari':1274f = self._charpoly_over_number_field(var)1275elif algorithm == 'hessenberg':1276f = self._charpoly_hessenberg(var)1277else:1278raise ValueError, "unknown algorithm '%s'"%algorithm1279self.cache(key, f)1280return f12811282def _charpoly_mod(self, p):1283"""1284Return the characteristic polynomial of self*denom modulo all1285primes over $p$.12861287This is used internally by the multimodular charpoly algorithm.12881289INPUT:1290p -- a prime that splits completely12911292OUTPUT:1293matrix over GF(p) whose columns correspond to the entries1294of all the characteristic polynomials of the reduction of self modulo all1295the primes over p.12961297EXAMPLES::12981299sage: W.<z> = CyclotomicField(5)1300sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A1301[ z + 1 0]1302[9*z + 7 4*z - 3]1303sage: A._charpoly_mod(11)1304[8 2 1]1305[1 6 0]1306[4 0 0]1307[0 0 0]1308"""1309tm = verbose("Computing characteristic polynomial of cyclotomic matrix modulo %s."%p)1310# Reduce self modulo all primes over p1311R, denom = self._reductions(p)1312# Compute the characteristic polynomial of each reduced matrix1313F = [A.charpoly('x') for A in R]1314# Put the characteristic polynomials together as the rows of a mod-p matrix1315k = R[0].base_ring()1316S = matrix(k, len(F), self.nrows()+1, [f.list() for f in F])1317# multiply by inverse of reduction matrix to lift1318_, L = self._reduction_matrix(p)1319X = L * S1320# Now the columns of the matrix X define the entries of the1321# charpoly modulo p.1322verbose("Finished computing charpoly mod %s."%p, tm)1323return X13241325def _charpoly_multimodular(self, var='x', proof=None):1326"""1327Compute the characteristic polynomial of self using a1328multimodular algorithm.13291330INPUT:1331proof -- bool (default: global flag); if False, compute1332using primes $p_i$ until the lift modulo all1333primes up to $p_i$ is the same as the lift modulo1334all primes up to $p_{i+3}$ or the bound is1335reached.13361337EXAMPLES::13381339sage: K.<z> = CyclotomicField(3)1340sage: A = matrix(3, [-z, 2*z + 1, 1/2*z + 2, 1, -1/2, 2*z + 2, -2*z - 2, -2*z - 2, 2*z - 1])1341sage: A._charpoly_multimodular()1342x^3 + (-z + 3/2)*x^2 + (17/2*z + 9/2)*x - 9/2*z - 23/21343sage: A._charpoly_multimodular('T')1344T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/21345sage: A._charpoly_multimodular('T', proof=False)1346T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/213471348TESTS:13491350We test a degenerate case::13511352sage: A = matrix(CyclotomicField(1),2,[1,2,3,4]); A.charpoly()1353x^2 - 5*x - 21354"""1355cdef Matrix_cyclo_dense A1356A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(),1357None, None, None)13581359proof = get_proof_flag(proof, "linear_algebra")13601361n = self._base_ring._n()1362p = previous_prime(MAX_MODULUS)1363prod = 11364v = []1365#A, denom = self._matrix._clear_denom()1366# TODO: this might be stupidly slow1367denom = self._matrix.denominator()1368A._matrix = <Matrix_rational_dense>(denom*self._matrix)1369bound = A._charpoly_bound()1370L_last = 01371while prod <= bound:1372while (n >= 2 and p % n != 1) or denom % p == 0:1373if p == 2:1374raise RuntimeError, "we ran out of primes in multimodular charpoly algorithm."1375p = previous_prime(p)13761377X = A._charpoly_mod(p)1378v.append(X)1379prod *= p1380p = previous_prime(p)13811382# if we've used enough primes as determined by bound, or1383# if we've used 3 primes, we check to see if the result is1384# the same.1385if prod >= bound or (not proof and (len(v) % 3 == 0)):1386M = matrix(ZZ, self._base_ring.degree(), self._nrows+1)1387L = _lift_crt(M, v)1388if not proof and L == L_last:1389break1390L_last = L13911392# Now each column of L encodes a coefficient of the output polynomial,1393# with column 0 being the constant coefficient.1394K = self.base_ring()1395R = K[var]1396coeffs = [K(w.list()) for w in L.columns()]1397f = R(coeffs)13981399# Rescale to account for denominator, if necessary1400if denom != 1:1401x = R.gen()1402f = f(x * denom) * (1 / (denom**f.degree()))14031404return f140514061407def _reductions(self, p):1408"""1409Compute the reductions modulo all primes over p of denom*self,1410where denom is the denominator of self.14111412INPUT:1413p -- a prime that splits completely in the base cyclotomic field.14141415OUTPUT:1416list -- of r distinct matrices modulo p, where r is1417the degree of the cyclotomic base field.1418denom -- an integer14191420EXAMPLES::14211422sage: K.<z> = CyclotomicField(3)1423sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z])1424sage: R, d = w._reductions(7)1425sage: R[0]1426[0 2 4]1427[1 1 4]1428sage: R[1]1429[0 1 4]1430[5 4 2]1431sage: d1432151433"""1434# Get matrix that defines the linear reduction maps modulo1435# each prime of the base ring over p.1436T, _ = self._reduction_matrix(p)1437# Clear denominator and get matrix over the integers suitable1438# for reduction.1439A, denom = self._matrix._clear_denom()1440# Actually reduce the matrix over the integers modulo the1441# prime p.1442B = A._mod_int(p)1443# Now multiply, which computes from B all the reductions of1444# self*denom modulo each of the primes over p.1445R = T * B1446# Finally compute the actual reductions by extracting them1447# from R (note that the rows of R define the reductions).1448ans = R._matrices_from_rows(self._nrows, self._ncols)1449return ans, denom14501451def _reduction_matrix(self, p):1452"""1453INPUT:1454p -- a prime that splits completely in the base field.14551456OUTPUT:1457-- Matrix over GF(p) whose action from the left1458gives the map from O_K to GF(p) x ... x GF(p)1459given by reducing modulo all the primes over p.1460-- inverse of this matrix14611462EXAMPLES::14631464sage: K.<z> = CyclotomicField(3)1465sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z])1466sage: A, B = w._reduction_matrix(7)1467sage: A1468[1 4]1469[1 2]1470sage: B1471[6 2]1472[4 3]14731474The reduction matrix is used to calculate the reductions mod primes1475above p. ::14761477sage: K.<z> = CyclotomicField(5)1478sage: A = matrix(K, 2, 2, [1, z, z^2+1, 5*z^3]); A1479[ 1 z]1480[z^2 + 1 5*z^3]1481sage: T, S = A._reduction_matrix(11)1482sage: T * A._rational_matrix().change_ring(GF(11))1483[ 1 9 5 4]1484[ 1 5 4 9]1485[ 1 4 6 1]1486[ 1 3 10 3]14871488The rows of this product are the (flattened) matrices mod each prime above p::14891490sage: roots = [r for r, e in K.defining_polynomial().change_ring(GF(11)).roots()]; roots1491[9, 5, 4, 3]1492sage: [r^2+1 for r in roots]1493[5, 4, 6, 10]1494sage: [5*r^3 for r in roots]1495[4, 9, 1, 3]14961497The reduction matrix is cached::1498sage: w._reduction_matrix(7) is w._reduction_matrix(7)1499True1500"""1501cache = self.fetch('reduction_matrices')1502if cache is None:1503cache = {}1504self.cache('reduction_matrices', cache)1505try:1506return cache[p]1507except KeyError:1508pass1509K = self.base_ring()1510phi = K.defining_polynomial()1511from sage.rings.all import GF1512from constructor import matrix1513F = GF(p)1514aa = [a for a, _ in phi.change_ring(F).roots()]1515n = K.degree()1516if len(aa) != n:1517raise ValueError, "the prime p (=%s) must split completely but doesn't"%p1518T = matrix(F, n)1519for i in range(n):1520a = aa[i]1521b = 11522for j in range(n):1523T[i,j] = b1524b *= a1525T.set_immutable()1526ans = (T, T**(-1))1527cache[p] = ans1528return ans15291530def echelon_form(self, algorithm='multimodular', height_guess=None):1531"""1532Find the echelon form of self, using the specified algorithm.15331534The result is cached for each algorithm separately.15351536EXAMPLES::15371538sage: W.<z> = CyclotomicField(3)1539sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A1540[ z + 1 2/3 9*z + 7]1541[4*z - 3 z -7*z]1542sage: A.echelon_form()1543[ 1 0 -192/97*z - 361/97]1544[ 0 1 1851/97*z + 1272/97]1545sage: A.echelon_form(algorithm='classical')1546[ 1 0 -192/97*z - 361/97]1547[ 0 1 1851/97*z + 1272/97]15481549We verify that the result is cached and that the caches are separate::15501551sage: A.echelon_form() is A.echelon_form()1552True1553sage: A.echelon_form() is A.echelon_form(algorithm='classical')1554False15551556TESTS::15571558sage: W.<z> = CyclotomicField(13)1559sage: A = Matrix(W, 2,3, [10^30*(1-z)^13, 1, 2, 3, 4, z])1560sage: B = Matrix(W, 2,3, [(1-z)^13, 1, 2, 3, 4, z])1561sage: A.echelon_form() == A.echelon_form('classical') # long time (4s on sage.math, 2011)1562True1563sage: B.echelon_form() == B.echelon_form('classical')1564True15651566A degenerate case with the degree 1 cyclotomic field::15671568sage: A = matrix(CyclotomicField(1),2,3,[1,2,3,4,5,6]);1569sage: A.echelon_form()1570[ 1 0 -1]1571[ 0 1 2]15721573A case that checks the bug in :trac:`3500`::15741575sage: cf4 = CyclotomicField(4) ; z4 = cf4.01576sage: A = Matrix(cf4, 1, 2, [-z4, 1])1577sage: A.echelon_form()1578[ 1 zeta4]15791580Verify that the matrix on :trac:`10281` works::15811582sage: K.<rho> = CyclotomicField(106)1583sage: coeffs = [(18603/107*rho^51 - 11583/107*rho^50 - 19907/107*rho^49 - 13588/107*rho^48 - 8722/107*rho^47 + 2857/107*rho^46 - 19279/107*rho^45 - 16666/107*rho^44 - 11327/107*rho^43 + 3802/107*rho^42 + 18998/107*rho^41 - 10798/107*rho^40 + 16210/107*rho^39 - 13768/107*rho^38 + 15063/107*rho^37 - 14433/107*rho^36 - 19434/107*rho^35 - 12606/107*rho^34 + 3786/107*rho^33 - 17996/107*rho^32 + 12341/107*rho^31 - 15656/107*rho^30 - 19092/107*rho^29 + 8382/107*rho^28 - 18147/107*rho^27 + 14024/107*rho^26 + 18751/107*rho^25 - 8301/107*rho^24 - 20112/107*rho^23 - 14483/107*rho^22 + 4715/107*rho^21 + 20065/107*rho^20 + 15293/107*rho^19 + 10072/107*rho^18 + 4775/107*rho^17 - 953/107*rho^16 - 19782/107*rho^15 - 16020/107*rho^14 + 5633/107*rho^13 - 17618/107*rho^12 - 18187/107*rho^11 + 7492/107*rho^10 + 19165/107*rho^9 - 9988/107*rho^8 - 20042/107*rho^7 + 10109/107*rho^6 - 17677/107*rho^5 - 17723/107*rho^4 - 12489/107*rho^3 - 6321/107*rho^2 - 4082/107*rho - 1378/107, 1, 4*rho + 1), (0, 1, rho + 4)]1584sage: m = matrix(2, coeffs)1585sage: a = m.echelon_form(algorithm='classical')1586sage: b = m.echelon_form(algorithm='multimodular') # long time (5s on sage.math, 2012)1587sage: a == b # long time (depends on previous)1588True1589"""1590key = 'echelon_form-%s'%algorithm1591E = self.fetch(key)1592if E is not None:1593return E15941595if self._nrows == 0:1596E = self.__copy__()1597self.cache(key, E)1598self.cache('pivots', ())1599return E16001601if algorithm == 'multimodular':1602E = self._echelon_form_multimodular(height_guess=height_guess)1603elif algorithm == 'classical':1604E = (self*self.denominator())._echelon_classical()1605else:1606raise ValueError, "unknown algorithm '%s'"%algorithm16071608self.cache(key, E)1609return E16101611def _echelon_form_multimodular(self, num_primes=10, height_guess=None):1612"""1613Use a multimodular algorithm to find the echelon form of self.16141615INPUT:1616num_primes -- number of primes to work modulo1617height_guess -- guess for the height of the echelon form1618of self16191620OUTPUT:1621matrix in reduced row echelon form16221623EXAMPLES::16241625sage: W.<z> = CyclotomicField(3)1626sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A1627[ z + 1 2/3 9*z + 7]1628[4*z - 3 z -7*z]1629sage: A._echelon_form_multimodular(10)1630[ 1 0 -192/97*z - 361/97]1631[ 0 1 1851/97*z + 1272/97]16321633TESTS:16341635We test a degenerate case::16361637sage: A = matrix(CyclotomicField(5),0); A1638[]1639sage: A._echelon_form_multimodular(10)1640[]1641sage: A.pivots()1642()16431644sage: A = matrix(CyclotomicField(13), 2, 3, [5, 1, 2, 46307, 46307*4, 46307])1645sage: A._echelon_form_multimodular()1646[ 1 0 7/19]1647[ 0 1 3/19]1648"""1649cdef int i1650cdef Matrix_cyclo_dense res16511652verbose("entering _echelon_form_multimodular", level=echelon_verbose_level)16531654denom = self._matrix.denominator()1655A = denom * self16561657# This bound is chosen somewhat arbitrarily. Changing it affects the1658# runtime, not the correctness of the result.1659if height_guess is None:1660height_guess = (A.coefficient_bound()+100)*100000016611662# This is all setup to keep track of various data1663# in the loop below.1664p = previous_prime(MAX_MODULUS)1665found = 01666prod = 11667n = self._base_ring._n()1668height_bound = self._ncols * height_guess * A.coefficient_bound() + 11669mod_p_ech_ls = []1670max_pivots = []1671is_square = self._nrows == self._ncols16721673verbose("using height bound %s"%height_bound, level=echelon_verbose_level)16741675while True:1676# Generate primes to use, and find echelon form1677# modulo those primes.1678while found < num_primes or prod <= height_bound:1679if (n == 1) or p%n == 1:1680try:1681mod_p_ech, piv_ls = A._echelon_form_one_prime(p)1682except ValueError:1683# This means that we chose a prime which divides1684# the denominator of the echelon form of self, so1685# just skip it and continue1686p = previous_prime(p)1687continue1688# if we have the identity, just return it, and1689# we're done.1690if is_square and len(piv_ls) == self._nrows:1691self.cache('pivots', tuple(range(self._nrows)))1692return self.parent().identity_matrix()1693if piv_ls > max_pivots:1694mod_p_ech_ls = [mod_p_ech]1695max_pivots = piv_ls1696# add this to the list of primes1697prod = p1698found = 11699elif piv_ls == max_pivots:1700mod_p_ech_ls.append(mod_p_ech)1701# add this to the list of primes1702prod *= p1703found += 11704else:1705# this means that the rank profile mod this1706# prime is worse than those that came before,1707# so we just loop1708p = previous_prime(p)1709continue17101711p = previous_prime(p)17121713if found > num_primes:1714num_primes = found17151716verbose("computed echelon form mod %s primes"%num_primes,1717level=echelon_verbose_level)1718verbose("current product of primes used: %s"%prod,1719level=echelon_verbose_level)17201721# Use CRT to lift back to ZZ1722mat_over_ZZ = matrix(ZZ, self._base_ring.degree(), self._nrows * self._ncols)1723_lift_crt(mat_over_ZZ, mod_p_ech_ls)1724# note: saving the CRT intermediate MultiModularBasis does1725# not seem to affect the runtime at all17261727# Attempt to use rational reconstruction to find1728# our echelon form1729try:1730verbose("attempting rational reconstruction ...", level=echelon_verbose_level)1731res = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, self.parent(),1732None, None, None)1733res._matrix = <Matrix_rational_dense>matrix_integer_dense_rational_reconstruction(mat_over_ZZ, prod)17341735except ValueError:1736# If a ValueError is raised here, it means that the1737# rational reconstruction failed. In this case, add1738# on a few more primes, and try again.17391740num_primes += echelon_primes_increment1741verbose("rational reconstruction failed, trying with %s primes"%num_primes, level=echelon_verbose_level)1742continue17431744verbose("rational reconstruction succeeded with %s primes!"%num_primes, level=echelon_verbose_level)17451746if ((res * res.denominator()).coefficient_bound() *1747self.coefficient_bound() * self.ncols()) > prod:1748# In this case, we don't know the result to sufficient1749# "precision" (here precision is just the modulus,1750# prod) to guarantee its correctness, so loop.17511752num_primes += echelon_primes_increment1753verbose("height not sufficient to determine echelon form", level=echelon_verbose_level)1754continue17551756verbose("found echelon form with %s primes, whose product is %s"%(num_primes, prod), level=echelon_verbose_level)1757self.cache('pivots', tuple(max_pivots))1758return res17591760def _echelon_form_one_prime(self, p):1761"""1762Find the echelon form of self mod the primes dividing p. Return1763the rational matrix representing this lift. If the pivots of the1764reductions mod the primes over p are different, then no such lift1765exists, and we raise a ValueError. If this happens, then the1766denominator of the echelon form of self is divisible by p. (Note1767that the converse need not be true.)17681769INPUT:1770p -- a prime that splits completely in the cyclotomic base field.17711772OUTPUT:1773matrix -- Lift via CRT of the echelon forms of self modulo1774each of the primes over p.1775tuple -- the tuple of pivots for the echelon form of self mod the1776primes dividing p17771778EXAMPLES::17791780sage: W.<z> = CyclotomicField(3)1781sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A1782[ z + 1 2/3 9*z + 7]1783[4*z - 3 z -7*z]1784sage: A._echelon_form_one_prime(7)1785(1786[1 0 4 0 1 2]1787[0 0 3 0 0 4], (0, 1)1788)1789sage: Matrix(W,2,3,[2*z+3,0,1,0,1,0])._echelon_form_one_prime(7)1790Traceback (most recent call last):1791...1792ValueError: echelon form mod 7 not defined17931794"""1795cdef Matrix_cyclo_dense res1796cdef int i17971798# Initialize variables1799is_square = self._nrows == self._ncols1800ls, denom = self._reductions(p)18011802# Find our first echelon form, and the associated list1803# of pivots1804ech_ls = [ls[0].echelon_form()]1805pivot_ls = ech_ls[0].pivots()1806# If we've found the identity matrix, we're all done.1807if self._nrows == self._ncols == len(pivot_ls):1808return (self.parent().identity_matrix(), range(self._nrows))18091810# For each reduction of self (i.e. for each prime of1811# self.base_ring() over p), compute the echelon form, and1812# keep track of all reductions which have the largest1813# number of pivots seen so far.1814for i from 1 <= i < len(ls):1815ech = ls[i].echelon_form()18161817# This should only occur when p divides the denominator1818# of the echelon form of self.1819if ech.pivots() != pivot_ls:1820raise ValueError, "echelon form mod %s not defined"%p18211822ech_ls.append(ech)18231824# Now, just lift back to ZZ and return it.18251826# TODO: coercion going on here1827reduction = matrix(ZZ, len(ech_ls), self._nrows * self._ncols,1828[ [y.lift() for y in E.list()] for E in ech_ls])18291830# TODO: more coercion happening here1831_, Finv = self._reduction_matrix(p)18321833lifted_matrix = Finv * reduction18341835return (lifted_matrix, pivot_ls)1836183718381839