Coverage for local/lib/python2.7/site-packages/sage/matrix/matrix_cyclo_dense.pyx : 82%

Hot-keys on this page
r m x p toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
""" Matrices over Cyclotomic Fields
The underlying matrix for a Matrix_cyclo_dense object is stored as follows: given an n x m matrix over a cyclotomic field of degree d, we store a d x (nm) matrix over QQ, each column of which corresponds to an element of the original matrix. This can be retrieved via the _rational_matrix method. Here is an example illustrating this:
EXAMPLES::
sage: F.<zeta> = CyclotomicField(5) sage: M = Matrix(F, 2, 3, [zeta, 3, zeta**4+5, (zeta+1)**4, 0, 1]) sage: M [ zeta 3 -zeta^3 - zeta^2 - zeta + 4] [3*zeta^3 + 5*zeta^2 + 3*zeta 0 1]
sage: M._rational_matrix() [ 0 3 4 0 0 1] [ 1 0 -1 3 0 0] [ 0 0 -1 5 0 0] [ 0 0 -1 3 0 0]
AUTHORS: * William Stein * Craig Citro """
#***************************************************************************** # Copyright (C) 2008 William Stein <wstein@gmail.com> # # 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/ #***************************************************************************** from __future__ import absolute_import
from cysignals.signals cimport sig_on, sig_off
include "sage/libs/ntl/decl.pxi"
from sage.structure.element cimport ModuleElement, RingElement, Element, Vector from sage.misc.randstate cimport randstate, current_randstate from sage.libs.gmp.randomize cimport *
from sage.libs.flint.types cimport fmpz_t, fmpq from sage.libs.flint.fmpz cimport fmpz_init, fmpz_clear, fmpz_set, fmpz_set_mpz, fmpz_one, fmpz_get_mpz, fmpz_add, fmpz_mul, fmpz_sub, fmpz_mul_si, fmpz_mul_si, fmpz_mul_si, fmpz_divexact, fmpz_lcm from sage.libs.flint.fmpq cimport fmpq_is_zero, fmpq_get_mpq, fmpq_set_mpq, fmpq_canonicalise from sage.libs.flint.fmpq_mat cimport fmpq_mat_entry_num, fmpq_mat_entry_den, fmpq_mat_entry
from .constructor import matrix from .matrix_space import MatrixSpace from .matrix cimport Matrix from . import matrix_dense from .matrix_integer_dense cimport _lift_crt from sage.structure.element cimport Matrix as baseMatrix from .misc import matrix_integer_dense_rational_reconstruction
from sage.rings.rational_field import QQ from sage.rings.integer_ring import ZZ from sage.arith.all import previous_prime, binomial from sage.rings.all import RealNumber from sage.rings.integer cimport Integer from sage.rings.rational cimport Rational from sage.rings.polynomial.polynomial_ring_constructor import PolynomialRing from sage.rings.number_field.number_field import NumberField_quadratic from sage.rings.number_field.number_field_element cimport NumberFieldElement from sage.rings.number_field.number_field_element_quadratic cimport NumberFieldElement_quadratic
from sage.structure.proof.proof import get_flag as get_proof_flag from sage.misc.misc import verbose import math
from sage.matrix.matrix_modn_dense_double import MAX_MODULUS as MAX_MODULUS_modn_dense_double from sage.arith.multi_modular import MAX_MODULUS as MAX_MODULUS_multi_modular MAX_MODULUS = min(MAX_MODULUS_modn_dense_double, MAX_MODULUS_multi_modular)
# parameters for tuning echelon_primes_increment = 15 echelon_verbose_level = 1
cdef class Matrix_cyclo_dense(Matrix_dense): def __cinit__(self, parent, entries, coerce, copy): """ Create a new dense cyclotomic matrix.
INPUT: parent -- a matrix space over a cyclotomic field entries -- a list of entries or scalar coerce -- bool; if true entries are coerced to base ring copy -- bool; ignored due to underlying data structure
EXAMPLES::
sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True) sage: type(A) <type 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense'>
Note that the entries of A haven't even been set yet above; that doesn't happen until init is called::
sage: A[0,0] Traceback (most recent call last): ... ValueError: matrix entries not yet initialized """
# This is not necessary, since we do not (yet) explicitly allocate # any memory. #def __dealloc__(self): # pass
def __init__(self, parent, entries=None, copy=True, coerce=True): """ Initialize a newly created cyclotomic matrix.
INPUT:
- ``parent`` -- a matrix space over a cyclotomic field
- ``entries`` -- a list of entries or scalar
- ``coerce`` -- boolean; if true entries are coerced to base ring
- ``copy`` -- boolean; ignored due to underlying data structure
EXAMPLES:
This function is called implicitly when you create new cyclotomic dense matrices::
sage: W.<a> = CyclotomicField(100) sage: A = matrix(2, 3, [1, 1/a, 1-a,a, -2/3*a, a^19]) sage: A [ 1 -a^39 + a^29 - a^19 + a^9 -a + 1] [ a -2/3*a a^19] sage: TestSuite(A).run()
TESTS::
sage: matrix(W, 2, 1, a) Traceback (most recent call last): ... TypeError: nonzero scalar matrix must be square
We call __init__ explicitly below::
sage: from sage.matrix.matrix_cyclo_dense import Matrix_cyclo_dense sage: A = Matrix_cyclo_dense.__new__(Matrix_cyclo_dense, MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True) sage: A.__init__(MatrixSpace(CyclotomicField(3),2), [0,1,2,3], True, True) sage: A [0 1] [2 3]
""" cdef int i pass # This code could be made much faster using Cython, etc. else:
# This could also be made much faster.
cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): """ Set the ij-th entry of self.
WARNING: This function does no bounds checking whatsoever, as the name suggests. It also assumes certain facts about the internal representation of cyclotomic fields. This is intended for internal use only.
EXAMPLES::
sage: K.<z> = CyclotomicField(11) ; M = Matrix(K,2,range(4)) sage: M[0,1] = z ; M [0 z] [2 3]
sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4)) sage: M[1,1] = z+1 ; M [ 0 1] [ 2 z + 1]
TESTS:
Since separate code exists for each quadratic field, we need doctests for each.::
sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4)) sage: M[1,1] = z+1 ; M [ 0 1] [ 2 z + 1] sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4)) sage: M[1,1] = z+1 ; M [ 0 1] [ 2 z + 1] """ # NEW FAST VERSION -- makes assumptions about how the # cyclotomic field is implemented. cdef Py_ssize_t k, c cdef NumberFieldElement v cdef mpz_t numer, denom cdef fmpz_t ftmp
# The i,j entry is the (i * self._ncols + j)'th column.
# Must be coded differently, since elements of # quadratic number fields are stored differently. (<NumberFieldElement_quadratic>value).a) (<NumberFieldElement_quadratic>value).denom)
(<NumberFieldElement_quadratic>value).b) (<NumberFieldElement_quadratic>value).denom) elif self._n == 3: # mat[0,c] = (x.a + x.b) / x.denom (<NumberFieldElement_quadratic>value).a)
# NOTE: it would be convenient here to have fmpz_add_mpz fmpq_mat_entry_num(self._matrix._matrix, 0, c), ftmp)
(<NumberFieldElement_quadratic>value).denom)
# mat[1,c] = (2 x.b) / x.denom (<NumberFieldElement_quadratic>value).b) fmpq_mat_entry_num(self._matrix._matrix, 1, c), 2) (<NumberFieldElement_quadratic>value).denom) else: # self._n is 6 (<NumberFieldElement_quadratic>value).a)
# NOTE: it would be convenient here to have fmpz_add_mpz fmpq_mat_entry_num(self._matrix._matrix, 0, c), ftmp)
(<NumberFieldElement_quadratic>value).denom)
(<NumberFieldElement_quadratic>value).b) fmpq_mat_entry_num(self._matrix._matrix, 1, c), 2) (<NumberFieldElement_quadratic>value).denom)
cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): """ Get the ij-th of self.
WARNING: As the name suggests, expect segfaults if i,j are out of bounds!! This is for internal use only.
EXAMPLES::
sage: W.<a> = CyclotomicField(5) sage: A = matrix(2, 3, [9939208341, 1/a, 1-a,a, -2/3*a, a^19])
This implicitly calls get_unsafe::
sage: A[0,0] 9939208341
TESTS:
Since separate code exists for each quadratic field, we need doctests for each.::
sage: K.<z> = CyclotomicField(3) ; M = Matrix(K,2,range(4)) sage: M[1,1] = z+1 ; M[1,1] z + 1 sage: (M[1,1] - M[0,1])**3 1 sage: K.<z> = CyclotomicField(4) ; M = Matrix(K,2,range(4)) sage: M[1,1] = z+1 ; M[1,1] z + 1 sage: (M[1,1] - M[0,1])**4 1 sage: K.<z> = CyclotomicField(6) ; M = Matrix(K,2,range(4)) sage: M[1,1] = z+1 ; M[1,1] z + 1 sage: (M[1,1] - M[0,1])**6 1 """ cdef Py_ssize_t k, c cdef NumberFieldElement x cdef NumberFieldElement_quadratic xq cdef mpz_t quo, tmp cdef fmpz_t denom, ftmp cdef ZZ_c coeff
fmpq_mat_entry_den(self._matrix._matrix, 1, c)) fmpq_mat_entry_den(self._matrix._matrix, 0, c)) fmpq_mat_entry_den(self._matrix._matrix, 1, c))
else: # n is 3 or 6 fmpq_mat_entry_num(self._matrix._matrix, 1, c)) else: # n == 6
fmpq_mat_entry_num(self._matrix._matrix, 1, c))
fmpq_mat_entry_den(self._matrix._matrix, 1, c))
# Get the least common multiple of the denominators in # this column.
# set each entry of x to a*denom/b where a/b is the # k,c entry of _matrix. # Now set k-th entry of x's numerator to tmp
# Set the denominator of x to denom.
def _pickle(self): """ Used for pickling matrices. This function returns the underlying data and pickle version.
OUTPUT: data -- output of pickle version -- int
EXAMPLES::
sage: K.<z> = CyclotomicField(3) sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z]) sage: w._pickle() (('0 0 -2 2 0 0 0 1 2 0 -1 0 -2 2 1 1 -1 3', 0), 0) """
def _unpickle(self, data, int version): """ Called when unpickling matrices.
INPUT: data -- a string version -- int
This modifies self.
EXAMPLES::
sage: K.<z> = CyclotomicField(3) sage: w = matrix(K, 3, 3, [0, -z, -2, -2*z + 2, 2*z, z, z, 1-z, 2+3*z]) sage: data, version = w._pickle() sage: k = w.parent()(0) sage: k._unpickle(data, version) sage: k == w True """ #self.check_mutability() else: raise RuntimeError("unknown matrix version (=%s)" % version)
######################################################################## # LEVEL 2 functionality # x * cdef _add_ # x * cdef _sub_ # * cdef _mul_ # x * cdef _lmul_ -- scalar multiplication # x * cpdef _cmp_ # x * __neg__ # * __invert__ # x * __copy__ # * _multiply_classical # * _list -- list of underlying elements (need not be a copy) # * _dict -- sparse dictionary of underlying elements (need not be a copy) ########################################################################
cpdef _add_(self, right): """ Return the sum of two dense cyclotomic matrices.
INPUT: self, right -- dense cyclotomic matrices with the same parents OUTPUT: a dense cyclotomic matrix
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: 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]) sage: A + B [ 0 3*z 4*z^2] [ z^3 + 5*z + 1 -2*z^3 - 2*z^2 - 2*z - 2 z]
Verify directly that the above output is correct::
sage: (A+B).list() == [A.list()[i]+B.list()[i] for i in range(6)] True """ # Fix the redundancy here.
cpdef _sub_(self, right): """ Return the difference of two dense cyclotomic matrices.
INPUT: self, right -- dense cyclotomic matrices with the same parent OUTPUT: a dense cyclotomic matrix
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: 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]) sage: A - B [ 2 -z -2*z^2] [z^3 - 5*z - 1 0 1/3*z]
Verify directly that the above output is correct::
sage: (A-B).list() == [A.list()[i]-B.list()[i] for i in range(6)] True """
cpdef _lmul_(self, Element right): """ Multiply a dense cyclotomic matrix by a scalar.
INPUT:
- ``right`` -- scalar in the base cyclotomic field
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(2, 3, [1,z,z^2,z^3,z^4,2/3*z]) sage: (1 + z/3)*A [ 1/3*z + 1 1/3*z^2 + z 1/3*z^3 + z^2] [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]
Verify directly that the above output is correct::
sage: ((1+z/3)*A).list() == [(1+z/3)*x for x in A.list()] True """
# Create a new matrix object but with the _matrix attribute not initialized:
else: # Multiply by nontrivial element of the cyclotomic number field # We do this by finding the matrix of this element, then left # multiplying by it. We have to tweak the matrix some to # get the right basis, etc.
cdef _matrix_times_matrix_(self, baseMatrix right): """ Return the product of two cyclotomic dense matrices.
INPUT: self, right -- cyclotomic dense matrices with compatible parents (same base ring, and compatible dimensions for matrix multiplication).
OUTPUT: cyclotomic dense matrix
ALGORITHM: Use a multimodular algorithm that involves multiplying the two matrices modulo split primes.
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: 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]) sage: A*B [ -z^3 + 7*z^2 + z - 1 -z^3 + 3*z^2 + 2*z + 1 -z^3 + 25/3*z^2] [-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] [ 4*z^2 + 4*z + 4 -7*z^2 + z + 7 -9*z^3 - 2/3*z^2 + 3*z + 10]
Verify that the answer above is consistent with what the generic sparse matrix multiply gives (which is a different implementation).::
sage: A*B == A.sparse_matrix()*B.sparse_matrix() True
sage: N1 = Matrix(CyclotomicField(6), 1, [1]) sage: cf6 = CyclotomicField(6) ; z6 = cf6.0 sage: N2 = Matrix(CyclotomicField(6), 1, 5, [0,1,z6,-z6,-z6+1]) sage: N1*N2 [ 0 1 zeta6 -zeta6 -zeta6 + 1] sage: N1 = Matrix(CyclotomicField(6), 1, [-1]) sage: N1*N2 [ 0 -1 -zeta6 zeta6 zeta6 - 1]
Verify that a degenerate case bug reported at :trac:`5974` is fixed.
sage: K.<zeta6>=CyclotomicField(6); matrix(K,1,2) * matrix(K,2,[0, 1, 0, -2*zeta6, 0, 0, 1, -2*zeta6 + 1]) [0 0 0 0]
TESTS:
This is from :trac:`8666`::
sage: K.<zeta4> = CyclotomicField(4) sage: m = matrix(K, [125]) sage: n = matrix(K, [186]) sage: m*n [23250] sage: (-m)*n [-23250] """
# conservative but correct estimate: 2 is there to account for the # sign of the entries
raise RuntimeError("we ran out of primes in matrix multiplication.") else: None, None, None)
cdef long _hash_(self) except -1: """ Return hash of an immutable matrix. Raise a TypeError if input matrix is mutable.
EXAMPLES:
This is called implicitly by the hash function.::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])
The matrix must be immutable.::
sage: hash(A) Traceback (most recent call last): ... TypeError: mutable matrices are unhashable sage: A.set_immutable()
Yes, this works::
sage: hash(A) # random 3107179158321342168
::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2]) sage: hash(A) Traceback (most recent call last): ... TypeError: mutable matrices are unhashable sage: A.set_immutable() sage: A.__hash__() # random 2347601038649299176
"""
cpdef _richcmp_(self, right, int op): """ Implement comparison of two cyclotomic matrices with identical parents.
INPUT:
- ``self``, ``right`` -- matrices with same parent
OUTPUT: boolean
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,z,-z,1+z/2])
These implicitly call richcmp::
sage: A == 5 False sage: A < 100 True
This function is called implicitly when comparisons with matrices are done::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2]) sage: A == A True sage: A < 2*A True sage: A >= 2*A False """
def __copy__(self): """ Make a copy of this matrix.
EXAMPLES:
We create a cyclotomic matrix.::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2])
We make a copy of A.:: sage: C = A.__copy__()
We make another reference to A.::
sage: B = A
Changing this reference changes A itself::
sage: B[0,0] = 10 sage: A[0,0] 10
Changing the copy does not change A.::
sage: C[0,0] = 20 sage: C[0,0] 20 sage: A[0,0] 10 """
def __neg__(self): """ Return the negative of this matrix.
OUTPUT: matrix
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2]) sage: -A [ -1 -z^2 - 2/3*z] [ z -1/2*z - 1] sage: A.__neg__() [ -1 -z^2 - 2/3*z] [ z -1/2*z - 1] """
######################################################################## # LEVEL 3 functionality (Optional) # * __deepcopy__ # * __invert__ # * Matrix windows -- only if you need strassen for that base # * Other functions (list them here): # * Specialized echelon form # * tensor product ######################################################################## def set_immutable(self): """ Change this matrix so that it is immutable.
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1,2/3*z+z^2,-z,1+z/2]) sage: A[0,0] = 10 sage: A.set_immutable() sage: A[0,0] = 20 Traceback (most recent call last): ... ValueError: matrix is immutable; please change a copy instead (i.e., use copy(M) to change a copy of M).
Note that there is no function to set a matrix to be mutable again, since such a function would violate the whole point. Instead make a copy, which is always mutable by default.::
sage: A.set_mutable() Traceback (most recent call last): ... AttributeError: 'sage.matrix.matrix_cyclo_dense.Matrix_cyclo_dense' object has no attribute 'set_mutable' sage: B = A.__copy__() sage: B[0,0] = 20 sage: B[0,0] 20 """
def _rational_matrix(self): """ Return the underlying rational matrix corresponding to self.
EXAMPLES::
sage: Matrix(CyclotomicField(7),4,4,range(16))._rational_matrix() [ 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0] sage: Matrix(CyclotomicField(7),4,4,[CyclotomicField(7).gen(0)**i for i in range(16)])._rational_matrix() [ 1 0 0 0 0 0 -1 1 0 0 0 0 0 -1 1 0] [ 0 1 0 0 0 0 -1 0 1 0 0 0 0 -1 0 1] [ 0 0 1 0 0 0 -1 0 0 1 0 0 0 -1 0 0] [ 0 0 0 1 0 0 -1 0 0 0 1 0 0 -1 0 0] [ 0 0 0 0 1 0 -1 0 0 0 0 1 0 -1 0 0] [ 0 0 0 0 0 1 -1 0 0 0 0 0 1 -1 0 0] """
def denominator(self): """ Return the denominator of the entries of this matrix.
OUTPUT: integer -- the smallest integer d so that d * self has entries in the ring of integers
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [-2/7,2/3*z+z^2,-z,1+z/19]); A [ -2/7 z^2 + 2/3*z] [ -z 1/19*z + 1] sage: d = A.denominator(); d 399 """
def coefficient_bound(self): r""" Return an upper bound for the (complex) absolute values of all entries of self with respect to all embeddings.
Use ``self.height()`` for a sharper bound.
This is computed using just the Cauchy-Schwarz inequality, i.e., we use the fact that ::
\left| \sum_i a_i\zeta^i \right| \leq \sum_i |a_i|,
as `|\zeta| = 1`.
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A [ z + 1 0] [9*z + 7 4*z - 3] sage: A.coefficient_bound() 16
The above bound is just $9 + 7$, coming from the lower left entry. A better bound would be the following::
sage: (A[1,0]).abs() 12.997543663... """ cdef Py_ssize_t i, j
def height(self): r""" Return the height of self.
If we let `a_{ij}` be the `i,j` entry of self, then we define the height of self to be
`\max_v \max_{i,j} |a_{ij}|_v`,
where `v` runs over all complex embeddings of ``self.base_ring()``.
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A [ z + 1 0] [9*z + 7 4*z - 3] sage: A.height() 12.997543663... sage: (A[1,0]).abs() 12.997543663... """ cdef Py_ssize_t i, j
cdef _randomize_rational_column_unsafe(Matrix_cyclo_dense self, Py_ssize_t col, mpz_t nump1, mpz_t denp1, distribution=None): """ Randomizes all entries in column ``col``. This is a helper method used in the implementation of dense matrices over cyclotomic fields.
INPUT:
- ``col`` - Integer, indicating the column; must be coercable to ``int``, and this must lie between 0 (inclusive) and ``self._ncols`` (exclusive), since no bounds-checking is performed - ``nump1`` - Integer, numerator bound plus one - ``denp1`` - Integer, denominator bound plus one - ``distribution`` - ``None`` or '1/n' (default: ``None``); if '1/n' then ``num_bound``, ``den_bound`` are ignored and numbers are chosen using the GMP function ``mpq_randomize_entry_recip_uniform`` - ``nonzero`` - Bool (default: ``False``); whether the new entries are forced to be non-zero
OUTPUT:
- None, the matrix is modified in-space
WARNING:
This method is quite unsafe. It's called from the method ``randomize``, but probably shouldn't be called from another method without first carefully reading the source code!
TESTS:
The following doctests are all indirect::
sage: MS = MatrixSpace(CyclotomicField(10), 4, 4) sage: A = MS.random_element(); A [ -2*zeta10^3 + 2*zeta10^2 - zeta10 zeta10^3 + 2*zeta10^2 - zeta10 + 1 0 -2*zeta10^3 + zeta10^2 - 2*zeta10 + 2] [ 0 -zeta10^3 + 2*zeta10^2 - zeta10 -zeta10^3 + 1 zeta10^3 + zeta10] [ 1/2*zeta10^2 -2*zeta10^2 + 2 -1/2*zeta10^3 + 1/2*zeta10^2 + 2 2*zeta10^3 - zeta10^2 - 2] [ 1 zeta10^2 + 2 2*zeta10^2 2*zeta10 - 2] sage: B = MS.random_element(density=0.5) sage: B._rational_matrix() [ 0 0 0 0 1 0 0 2 0 2 0 0 0 0 0 0] [ 0 0 0 0 0 0 0 0 -1 -2 0 0 0 0 0 2] [ 0 -1 0 0 -1 0 0 0 0 0 0 0 0 0 -2 -1] [ 0 0 0 0 0 0 0 2 -1/2 1/2 0 0 0 0 -1 0] sage: C = MS.random_element(density=0.5, num_bound=20, den_bound=20) sage: C._rational_matrix() [ 0 0 8/11 -10/3 -11/7 8 1 -3 0 0 1 0 0 0 0 0] [ 0 0 -11/17 -3/13 -5/6 17/3 -19/17 -4/5 0 0 9 0 0 0 0 0] [ 0 0 -11 -3/2 -5/12 8/11 0 -3/19 0 0 -5/6 0 0 0 0 0] [ 0 0 0 5/8 -5/11 -5/4 6/11 2/3 0 0 -16/11 0 0 0 0 0] """ cdef Py_ssize_t i cdef fmpq * entry cdef mpq_t tmp
for i in range(mat._nrows): mpq_randomize_entry_recip_uniform(tmp) fmpq_set_mpq(fmpq_mat_entry(mat._matrix, i, col), tmp) else: for i in range(mat._nrows): mpq_randomize_entry_as_int(tmp, nump1) fmpq_set_mpq(fmpq_mat_entry(mat._matrix, i, col), tmp)
def randomize(self, density=1, num_bound=2, den_bound=2, \ distribution=None, nonzero=False, *args, **kwds): r""" Randomize the entries of ``self``.
Choose rational numbers according to ``distribution``, whose numerators are bounded by ``num_bound`` and whose denominators are bounded by ``den_bound``.
EXAMPLES::
sage: A = Matrix(CyclotomicField(5),2,2,range(4)) ; A [0 1] [2 3] sage: A.randomize() sage: A # random output [ 1/2*zeta5^2 + zeta5 1/2] [ -zeta5^2 + 2*zeta5 -2*zeta5^3 + 2*zeta5^2 + 2] """ # Problem 1: # We cannot simply call the ``randomize`` code in ``matrix2.pyx`` on # the underlying matrix, since this is a d x (mn) matrix, where d is # the degree of the field extension, which leads to an overly dense # matrix. # # Problem 2: # We cannot simply copy the code from ``matrix2.pyx``, since the # ``random_element`` method for cyclotomic fields does not support # the arguments ``num_bound`` and ``den_bound``, which are support by # the rational field. # # Proposed solution: # Randomly select a proportion of ``density`` of the elements in the # matrix over the cyclotomic field, that is, this many columns in the # underlying rational matrix. Then, for each element in that column, # randomize it to a rational number, applying the arguments # ``num_bound`` and ``den_bound``.
return density = 1
cdef Py_ssize_t col, i, k, num cdef Integer B, C cdef bint col_is_zero
for col in range(self._matrix._ncols): col_is_zero = True while col_is_zero: self._randomize_rational_column_unsafe(col, B.value, \ C.value, distribution) # Check whether the new column is non-zero for i in range(self._degree): if not fmpq_is_zero(fmpq_mat_entry(self._matrix._matrix, i, col)): col_is_zero = False break else: C.value, distribution) # Check whether the new column is non-zero else: C.value, distribution) else: num = int(self._nrows * self._ncols * density) for k in range(num): col = rstate.c_random() % self._matrix._ncols self._randomize_rational_column_unsafe(col, B.value, \ C.value, distribution)
def _charpoly_bound(self): """ Determine a bound for the coefficients of the characteristic polynomial of self. We use the bound in Lemma 2.2 of:
Dumas, J-G. "Bounds on the coefficients of characteristic and minimal polynomials." J. Inequal. Pure Appl. Math. 8 (2007), no. 2.
This bound only applies for `self._nrows >= 4`, so in all smaller cases, we just use a naive bound.
EXAMPLES::
sage: A = Matrix(CyclotomicField(7),3,3,range(9)) sage: A._charpoly_bound() 2048 sage: A.charpoly() x^3 - 12*x^2 - 18*x
An example from the above paper, where the bound is sharp::
sage: 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]) sage: B._charpoly_bound() 80 sage: B.charpoly() x^5 - 5*x^4 + 40*x^2 - 80*x + 48 """ cdef Py_ssize_t i
# should we even bother with this check, or just say in # the docstring that we assume it's square? raise ArithmeticError("self must be a square matrix")
return 1
# TODO: should charpoly just hardcode the return value for # self.nrows() < 4?
# this bound is only valid for n >= 4, use naive bounds # in other cases.
# This is an approximation to 2^(5/6*log_2(5) - 2/3*log_2(6)) # This is 2*e^(1-(2(7\gamma-4))/(13(3-2\gamma))), where \gamma # is Euler's constant. # This is an approximation to 1/2. :)
# TODO: we don't check anything about overflows anywhere here; # should we?
# i = 0 case
def charpoly(self, var='x', algorithm="multimodular", proof=None): r""" Return the characteristic polynomial of self, as a polynomial over the base ring.
INPUT:
- algorithm
- 'multimodular' (default): reduce modulo primes, compute charpoly mod p, and lift (very fast) - 'pari': use pari (quite slow; comparable to Magma v2.14 though) - 'hessenberg': put matrix in Hessenberg form (double dog slow)
- proof -- bool (default: None) proof flag determined by global linalg proof.
OUTPUT:
polynomial
EXAMPLES::
sage: K.<z> = CyclotomicField(5) sage: a = matrix(K, 3, [1,z,1+z^2, z/3,1,2,3,z^2,1-z]) sage: f = a.charpoly(); f x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3 sage: f(a) [0 0 0] [0 0 0] [0 0 0] sage: a.charpoly(algorithm='pari') x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3 sage: a.charpoly(algorithm='hessenberg') x^3 + (z - 3)*x^2 + (-16/3*z^2 - 2*z)*x - 2/3*z^3 + 16/3*z^2 - 5*z + 5/3
sage: Matrix(K, 1, [0]).charpoly() x sage: Matrix(K, 1, [5]).charpoly(var='y') y - 5
sage: Matrix(CyclotomicField(13),3).charpoly() x^3 sage: Matrix(CyclotomicField(13),3).charpoly()[2].parent() Cyclotomic Field of order 13 and degree 12
TESTS::
sage: Matrix(CyclotomicField(10),0).charpoly() 1 """
raise TypeError("self must be square")
else: raise ValueError("unknown algorithm '%s'" % algorithm)
def _charpoly_mod(self, p): """ Return the characteristic polynomial of self*denom modulo all primes over $p$.
This is used internally by the multimodular charpoly algorithm.
INPUT: p -- a prime that splits completely
OUTPUT: matrix over GF(p) whose columns correspond to the entries of all the characteristic polynomials of the reduction of self modulo all the primes over p.
EXAMPLES::
sage: W.<z> = CyclotomicField(5) sage: A = matrix(W, 2, 2, [1+z, 0, 9*z+7, -3 + 4*z]); A [ z + 1 0] [9*z + 7 4*z - 3] sage: A._charpoly_mod(11) [8 2 1] [1 6 0] [4 0 0] [0 0 0] """ # Reduce self modulo all primes over p # Compute the characteristic polynomial of each reduced matrix # Put the characteristic polynomials together as the rows of a mod-p matrix # multiply by inverse of reduction matrix to lift # Now the columns of the matrix X define the entries of the # charpoly modulo p.
def _charpoly_multimodular(self, var='x', proof=None): """ Compute the characteristic polynomial of self using a multimodular algorithm.
INPUT: proof -- bool (default: global flag); if False, compute using primes $p_i$ until the lift modulo all primes up to $p_i$ is the same as the lift modulo all primes up to $p_{i+3}$ or the bound is reached.
EXAMPLES::
sage: K.<z> = CyclotomicField(3) sage: 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]) sage: A._charpoly_multimodular() x^3 + (-z + 3/2)*x^2 + (17/2*z + 9/2)*x - 9/2*z - 23/2 sage: A._charpoly_multimodular('T') T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/2 sage: A._charpoly_multimodular('T', proof=False) T^3 + (-z + 3/2)*T^2 + (17/2*z + 9/2)*T - 9/2*z - 23/2
TESTS:
We test a degenerate case::
sage: A = matrix(CyclotomicField(1),2,[1,2,3,4]); A.charpoly() x^2 - 5*x - 2 """ cdef Matrix_cyclo_dense A
#A, denom = self._matrix._clear_denom() # TODO: this might be stupidly slow raise RuntimeError("we ran out of primes in multimodular charpoly algorithm.")
# if we've used enough primes as determined by bound, or # if we've used 3 primes, we check to see if the result is # the same. break
# Now each column of L encodes a coefficient of the output polynomial, # with column 0 being the constant coefficient.
# Rescale to account for denominator, if necessary
def _reductions(self, p): """ Compute the reductions modulo all primes over p of denom*self, where denom is the denominator of self.
INPUT: p -- a prime that splits completely in the base cyclotomic field.
OUTPUT: list -- of r distinct matrices modulo p, where r is the degree of the cyclotomic base field. denom -- an integer
EXAMPLES::
sage: K.<z> = CyclotomicField(3) sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z]) sage: R, d = w._reductions(7) sage: R[0] [0 2 4] [1 1 4] sage: R[1] [0 1 4] [5 4 2] sage: d 15 """ # Get matrix that defines the linear reduction maps modulo # each prime of the base ring over p. # Clear denominator and get matrix over the integers suitable # for reduction. # Actually reduce the matrix over the integers modulo the # prime p. # Now multiply, which computes from B all the reductions of # self*denom modulo each of the primes over p. # Finally compute the actual reductions by extracting them # from R (note that the rows of R define the reductions).
def _reduction_matrix(self, p): """ INPUT: p -- a prime that splits completely in the base field.
OUTPUT: -- Matrix over GF(p) whose action from the left gives the map from O_K to GF(p) x ... x GF(p) given by reducing modulo all the primes over p. -- inverse of this matrix
EXAMPLES::
sage: K.<z> = CyclotomicField(3) sage: w = matrix(K, 2, 3, [0, -z/5, -2/3, -2*z + 2, 2*z, z]) sage: A, B = w._reduction_matrix(7) sage: A [1 4] [1 2] sage: B [6 2] [4 3]
The reduction matrix is used to calculate the reductions mod primes above p. ::
sage: K.<z> = CyclotomicField(5) sage: A = matrix(K, 2, 2, [1, z, z^2+1, 5*z^3]); A [ 1 z] [z^2 + 1 5*z^3] sage: T, S = A._reduction_matrix(11) sage: T * A._rational_matrix().change_ring(GF(11)) [ 1 9 5 4] [ 1 5 4 9] [ 1 4 6 1] [ 1 3 10 3]
The rows of this product are the (flattened) matrices mod each prime above p::
sage: roots = [r for r, e in K.defining_polynomial().change_ring(GF(11)).roots()]; roots [9, 5, 4, 3] sage: [r^2+1 for r in roots] [5, 4, 6, 10] sage: [5*r^3 for r in roots] [4, 9, 1, 3]
The reduction matrix is cached:: sage: w._reduction_matrix(7) is w._reduction_matrix(7) True """ pass raise ValueError("the prime p (=%s) must split completely but doesn't" % p)
def echelon_form(self, algorithm='multimodular', height_guess=None): """ Find the echelon form of self, using the specified algorithm.
The result is cached for each algorithm separately.
EXAMPLES::
sage: W.<z> = CyclotomicField(3) sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A [ z + 1 2/3 9*z + 7] [4*z - 3 z -7*z] sage: A.echelon_form() [ 1 0 -192/97*z - 361/97] [ 0 1 1851/97*z + 1272/97] sage: A.echelon_form(algorithm='classical') [ 1 0 -192/97*z - 361/97] [ 0 1 1851/97*z + 1272/97]
We verify that the result is cached and that the caches are separate::
sage: A.echelon_form() is A.echelon_form() True sage: A.echelon_form() is A.echelon_form(algorithm='classical') False
TESTS::
sage: W.<z> = CyclotomicField(13) sage: A = Matrix(W, 2,3, [10^30*(1-z)^13, 1, 2, 3, 4, z]) sage: B = Matrix(W, 2,3, [(1-z)^13, 1, 2, 3, 4, z]) sage: A.echelon_form() == A.echelon_form('classical') # long time (4s on sage.math, 2011) True sage: B.echelon_form() == B.echelon_form('classical') True
A degenerate case with the degree 1 cyclotomic field::
sage: A = matrix(CyclotomicField(1),2,3,[1,2,3,4,5,6]); sage: A.echelon_form() [ 1 0 -1] [ 0 1 2]
A case that checks the bug in :trac:`3500`::
sage: cf4 = CyclotomicField(4) ; z4 = cf4.0 sage: A = Matrix(cf4, 1, 2, [-z4, 1]) sage: A.echelon_form() [ 1 zeta4]
Verify that the matrix on :trac:`10281` works::
sage: K.<rho> = CyclotomicField(106) sage: 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)] sage: m = matrix(2, coeffs) sage: a = m.echelon_form(algorithm='classical') sage: b = m.echelon_form(algorithm='multimodular') # long time (5s on sage.math, 2012) sage: a == b # long time (depends on previous) True """
else: raise ValueError("unknown algorithm '%s'" % algorithm)
def _echelon_form_multimodular(self, num_primes=10, height_guess=None): """ Use a multimodular algorithm to find the echelon form of self.
INPUT: num_primes -- number of primes to work modulo height_guess -- guess for the height of the echelon form of self
OUTPUT: matrix in reduced row echelon form
EXAMPLES::
sage: W.<z> = CyclotomicField(3) sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A [ z + 1 2/3 9*z + 7] [4*z - 3 z -7*z] sage: A._echelon_form_multimodular(10) [ 1 0 -192/97*z - 361/97] [ 0 1 1851/97*z + 1272/97]
TESTS:
We test a degenerate case::
sage: A = matrix(CyclotomicField(5),0); A [] sage: A._echelon_form_multimodular(10) [] sage: A.pivots() ()
sage: A = matrix(CyclotomicField(13), 2, 3, [5, 1, 2, 46307, 46307*4, 46307]) sage: A._echelon_form_multimodular() [ 1 0 7/19] [ 0 1 3/19] """ cdef int i cdef Matrix_cyclo_dense res
# This bound is chosen somewhat arbitrarily. Changing it affects the # runtime, not the correctness of the result.
# This is all setup to keep track of various data # in the loop below.
# Generate primes to use, and find echelon form # modulo those primes. except ValueError: # This means that we chose a prime which divides # the denominator of the echelon form of self, so # just skip it and continue p = previous_prime(p) continue # if we have the identity, just return it, and # we're done. # add this to the list of primes # add this to the list of primes else: # this means that the rank profile mod this # prime is worse than those that came before, # so we just loop p = previous_prime(p) continue
num_primes = found
# Use CRT to lift back to ZZ # note: saving the CRT intermediate MultiModularBasis does # not seem to affect the runtime at all
# Attempt to use rational reconstruction to find # our echelon form
except ValueError: # If a ValueError is raised here, it means that the # rational reconstruction failed. In this case, add # on a few more primes, and try again.
num_primes += echelon_primes_increment verbose("rational reconstruction failed, trying with %s primes"%num_primes, level=echelon_verbose_level) continue
# In this case, we don't know the result to sufficient # "precision" (here precision is just the modulus, # prod) to guarantee its correctness, so loop.
num_primes += echelon_primes_increment verbose("height not sufficient to determine echelon form", level=echelon_verbose_level) continue
def _echelon_form_one_prime(self, p): """ Find the echelon form of self mod the primes dividing p. Return the rational matrix representing this lift. If the pivots of the reductions mod the primes over p are different, then no such lift exists, and we raise a ValueError. If this happens, then the denominator of the echelon form of self is divisible by p. (Note that the converse need not be true.)
INPUT: p -- a prime that splits completely in the cyclotomic base field.
OUTPUT: matrix -- Lift via CRT of the echelon forms of self modulo each of the primes over p. tuple -- the tuple of pivots for the echelon form of self mod the primes dividing p
EXAMPLES::
sage: W.<z> = CyclotomicField(3) sage: A = matrix(W, 2, 3, [1+z, 2/3, 9*z+7, -3 + 4*z, z, -7*z]); A [ z + 1 2/3 9*z + 7] [4*z - 3 z -7*z] sage: A._echelon_form_one_prime(7) ( [1 0 4 0 1 2] [0 0 3 0 0 4], (0, 1) ) sage: Matrix(W,2,3,[2*z+3,0,1,0,1,0])._echelon_form_one_prime(7) Traceback (most recent call last): ... ValueError: echelon form mod 7 not defined
""" cdef Matrix_cyclo_dense res cdef int i
# Initialize variables
# Find our first echelon form, and the associated list # of pivots # If we've found the identity matrix, we're all done.
# For each reduction of self (i.e. for each prime of # self.base_ring() over p), compute the echelon form, and # keep track of all reductions which have the largest # number of pivots seen so far.
# This should only occur when p divides the denominator # of the echelon form of self.
# Now, just lift back to ZZ and return it.
# TODO: coercion going on here
# TODO: more coercion happening here
def tensor_product(self, A, subdivide=True): r""" Return the tensor product of two matrices.
INPUT:
- ``A`` -- a matrix - ``subdivide`` -- (default: ``True``) whether or not to return natural subdivisions with the matrix
OUTPUT:
Replace each element of ``self`` by a copy of ``A``, but first create a scalar multiple of ``A`` by the element it replaces. So if ``self`` is an `m\times n` matrix and ``A`` is a `p\times q` matrix, then the tensor product is an `mp\times nq` matrix. By default, the matrix will be subdivided into submatrices of size `p\times q`.
EXAMPLES::
sage: C = CyclotomicField(12) sage: M = matrix.random(C, 3, 3) sage: N = matrix.random(C, 50, 50) sage: M.tensor_product(M) == super(type(M), M).tensor_product(M) True sage: N = matrix.random(C, 15, 20) sage: M.tensor_product(N) == super(type(M), M).tensor_product(N) True
TESTS::
sage: Mp = matrix.random(C, 2,3) sage: Np = matrix.random(C, 4,5) sage: subdiv = super(type(Mp),Mp).tensor_product(Np).subdivisions() sage: Mp.tensor_product(Np).subdivisions() == subdiv True
Check that `m \times 0` and `0 \times m` matrices work (:trac:`22769`)::
sage: m1 = matrix(C, 1, 0, []) sage: m2 = matrix(C, 2, 2, [1, 2, 3, 4]) sage: m1.tensor_product(m2).dimensions() (2, 0) sage: m2.tensor_product(m1).dimensions() (2, 0) sage: m3 = matrix(C, 0, 3, []) sage: m3.tensor_product(m2).dimensions() (0, 6) sage: m2.tensor_product(m3).dimensions() (0, 6) """ raise TypeError('tensor product requires a second matrix, not {0}'.format(A))
return super(Matrix_cyclo_dense, self).tensor_product(A, subdivide)
cdef Matrix_cyclo_dense M
None, None, None)
|