Path: blob/master/src/sage/matrix/matrix_mod2e_dense.pyx
8815 views
"""1Dense matrices over `\GF{2^e}` for `2 <= e <= 10` using the M4RIE library.23The M4RIE library offers two matrix representations:451) ``mzed_t``67m x n matrices over `\GF{2^e}` are internally represented roughly as8m x (en) matrices over `\GF{2}`. Several elements are packed into9words such that each element is filled with zeroes until the next10power of two. Thus, for example, elements of `\GF{2^3}` are11represented as ``[0xxx|0xxx|0xxx|0xxx|...]``. This representation is12wrapped as :class:`Matrix_mod2e_dense` in Sage.1314Multiplication and elimination both use "Newton-John" tables. These15tables are simply all possible multiples of a given row in a matrix16such that a scale+add operation is reduced to a table lookup +17add. On top of Newton-John multiplication M4RIE implements18asymptotically fast Strassen-Winograd multiplication. Elimination19uses simple Gaussian elimination which requires `O(n^3)` additions20but only `O(n^2 * 2^e)` multiplications.21222) ``mzd_slice_t``2324m x n matrices over `\GF{2^e}` are internally represented as slices25of m x n matrices over `\GF{2}`. This representation allows for very26fast matrix times matrix products using Karatsuba's polynomial27multiplication for polynomials over matrices. However, it is not28feature complete yet and hence not wrapped in Sage for now.2930See http://m4ri.sagemath.org for more details on the M4RIE library.3132EXAMPLE::3334sage: K.<a> = GF(2^8)35sage: A = random_matrix(K, 3,4)36sage: A37[ a^6 + a^5 + a^4 + a^2 a^6 + a^3 + a + 1 a^5 + a^3 + a^2 + a + 1 a^7 + a^6 + a + 1]38[ a^7 + a^6 + a^3 a^7 + a^6 + a^5 + 1 a^5 + a^4 + a^3 + a + 1 a^6 + a^5 + a^4 + a^3 + a^2 + 1]39[ a^6 + a^5 + a + 1 a^7 + a^3 + 1 a^7 + a^3 + a + 1 a^7 + a^6 + a^3 + a^2 + a + 1]4041sage: A.echelon_form()42[ 1 0 0 a^6 + a^5 + a^4 + a^2]43[ 0 1 0 a^7 + a^5 + a^3 + a + 1]44[ 0 0 1 a^6 + a^4 + a^3 + a^2 + 1]4546AUTHOR:4748* Martin Albrecht <[email protected]>4950TESTS::5152sage: TestSuite(sage.matrix.matrix_mod2e_dense.Matrix_mod2e_dense).run(verbose=True)53running ._test_pickling() . . . pass5455TODO:5657- wrap ``mzd_slice_t``585960REFERENCES:6162.. [BB09] Tomas J. Boothby and Robert W. Bradshaw. *Bitslicing63and the Method of Four Russians Over Larger Finite Fields*. arXiv:0901.1413v1,642009. http://arxiv.org/abs/0901.141365"""6667include "sage/ext/interrupt.pxi"68include "sage/ext/cdefs.pxi"69include 'sage/ext/stdsage.pxi'70include 'sage/ext/random.pxi'7172cimport matrix_dense73from sage.structure.element cimport Matrix, Vector74from sage.structure.element cimport ModuleElement, Element, RingElement7576from sage.rings.all import FiniteField as GF77from sage.misc.randstate cimport randstate, current_randstate7879from sage.matrix.matrix_mod2_dense cimport Matrix_mod2_dense8081from sage.libs.m4ri cimport m4ri_word, mzd_copy, mzd_init82from sage.libs.m4rie cimport *83from sage.libs.m4rie cimport mzed_t848586# we must keep a copy of the internal finite field representation87# around to avoid re-creating it over and over again. Furthermore,88# M4RIE assumes pointer equivalence of identical fields.8990_m4rie_finite_field_cache = {}9192cdef class M4RIE_finite_field:93"""94A thin wrapper around the M4RIE finite field class such that we95can put it in a hash table. This class is not meant for public96consumption.97"""98cdef gf2e *ff99100def __cinit__(self):101"""102EXAMPLE::103104sage: from sage.matrix.matrix_mod2e_dense import M4RIE_finite_field105sage: K = M4RIE_finite_field(); K106<sage.matrix.matrix_mod2e_dense.M4RIE_finite_field object at 0x...>107"""108pass109110def __dealloc__(self):111"""112EXAMPLE::113114sage: from sage.matrix.matrix_mod2e_dense import M4RIE_finite_field115sage: K = M4RIE_finite_field()116sage: del K117"""118if self.ff:119gf2e_free(self.ff)120121cdef m4ri_word poly_to_word(f):122return f.integer_representation()123124cdef object word_to_poly(w, F):125return F.fetch_int(w)126127cdef class Matrix_mod2e_dense(matrix_dense.Matrix_dense):128########################################################################129# LEVEL 1 functionality130########################################################################131def __cinit__(self, parent, entries, copy, coerce, alloc=True):132"""133Create new matrix over `GF(2^e)` for 2<=e<=10.134135INPUT:136137- ``parent`` - a :class:`MatrixSpace`.138- ``entries`` - may be list or a finite field element.139- ``copy`` - ignored, elements are always copied140- ``coerce`` - ignored, elements are always coerced141- ``alloc`` - if ``True`` the matrix is allocated first (default: ``True``)142143EXAMPLES::144145sage: K.<a> = GF(2^4)146sage: A = Matrix(K, 3, 4); A147[0 0 0 0]148[0 0 0 0]149[0 0 0 0]150151sage: A.randomize(); A152[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]153[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]154[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]155156sage: K.<a> = GF(2^3)157sage: A = Matrix(K,3,4); A158[0 0 0 0]159[0 0 0 0]160[0 0 0 0]161162sage: A.randomize(); A163[ a^2 + a a^2 + 1 a^2 + a a^2 + a]164[ a^2 + 1 a^2 + a + 1 a^2 + 1 a^2]165[ a^2 + a a^2 + 1 a^2 + a + 1 a + 1]166"""167matrix_dense.Matrix_dense.__init__(self, parent)168169cdef M4RIE_finite_field FF170171R = parent.base_ring()172173f = R.polynomial()174cdef m4ri_word poly = sum(int(c)*2**i for i,c in enumerate(f))175176if alloc and self._nrows and self._ncols:177if poly in _m4rie_finite_field_cache:178self._entries = mzed_init((<M4RIE_finite_field>_m4rie_finite_field_cache[poly]).ff, self._nrows, self._ncols)179else:180FF = PY_NEW(M4RIE_finite_field)181FF.ff = gf2e_init(poly)182self._entries = mzed_init(FF.ff, self._nrows, self._ncols)183_m4rie_finite_field_cache[poly] = FF184185# cache elements186self._zero = self._base_ring(0)187self._one = self._base_ring(1)188189def __dealloc__(self):190"""191TESTS::192193sage: K.<a> = GF(2^4)194sage: A = Matrix(K, 1000, 1000)195sage: del A196sage: A = Matrix(K, 1000, 1000)197sage: del A198"""199if self._entries:200mzed_free(self._entries)201self._entries = NULL202203def __init__(self, parent, entries, copy, coerce):204"""205Create new matrix over `GF(2^e)` for 2<=e<=10.206207INPUT:208209- ``parent`` - a :class:`MatrixSpace`.210- ``entries`` - may be list or a finite field element.211- ``copy`` - ignored, elements are always copied212- ``coerce`` - ignored, elements are always coerced213214EXAMPLE::215216sage: K.<a> = GF(2^4)217sage: l = [K.random_element() for _ in range(3*4)]; l218[a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a]219220sage: A = Matrix(K, 3, 4, l); A221[ a^2 + 1 a^3 + 1 0 0]222[ a a^3 + a + 1 a + 1 a + 1]223[ a^2 a^3 + a + 1 a^3 + a a^3 + a]224225sage: A.list()226[a^2 + 1, a^3 + 1, 0, 0, a, a^3 + a + 1, a + 1, a + 1, a^2, a^3 + a + 1, a^3 + a, a^3 + a]227228sage: l[0], A[0,0]229(a^2 + 1, a^2 + 1)230231sage: A = Matrix(K, 3, 3, a); A232[a 0 0]233[0 a 0]234[0 0 a]235"""236cdef int i,j237238if entries is None:239return240241R = self.base_ring()242243# scalar ?244if not isinstance(entries, list):245if entries != 0:246if self.nrows() != self.ncols():247raise TypeError("self must be a square matrices for scalar assignment")248for i in range(self.nrows()):249self.set_unsafe(i,i, R(entries))250return251252# all entries are given as a long list253if len(entries) != self._nrows * self._ncols:254raise IndexError("The vector of entries has the wrong length.")255256k = 0257258for i from 0 <= i < self._nrows:259sig_check()260for j from 0 <= j < self._ncols:261e = R(entries[k])262mzed_write_elem(self._entries, i, j, poly_to_word(e))263k = k + 1264265cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):266"""267A[i,j] = value without bound checks268269INPUT:270- ``i`` - row index271- ``j`` - column index272- ``value`` - a finite field element (not checked but assumed)273274EXAMPLE::275276sage: K.<a> = GF(2^4)277sage: A = Matrix(K,3,4,[K.random_element() for _ in range(3*4)]); A278[ a^2 + 1 a^3 + 1 0 0]279[ a a^3 + a + 1 a + 1 a + 1]280[ a^2 a^3 + a + 1 a^3 + a a^3 + a]281282sage: A[0,0] = a # indirect doctest283sage: A284[ a a^3 + 1 0 0]285[ a a^3 + a + 1 a + 1 a + 1]286[ a^2 a^3 + a + 1 a^3 + a a^3 + a]287"""288mzed_write_elem(self._entries, i, j, poly_to_word(value))289290cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):291"""292Get A[i,j] without bound checks.293294INPUT:295- ``i`` - row index296- ``j`` - column index297298EXAMPLE::299300sage: K.<a> = GF(2^4)301sage: A = random_matrix(K,3,4)302sage: A[2,3] # indirect doctest303a^3 + a^2 + a + 1304sage: K.<a> = GF(2^3)305sage: m,n = 3, 4306sage: A = random_matrix(K,3,4); A307[ a^2 + a a^2 + 1 a^2 + a a^2 + a]308[ a^2 + 1 a^2 + a + 1 a^2 + 1 a^2]309[ a^2 + a a^2 + 1 a^2 + a + 1 a + 1]310"""311cdef int r = mzed_read_elem(self._entries, i, j)312return word_to_poly(r, self._base_ring)313314cpdef ModuleElement _add_(self, ModuleElement right):315"""316Return A+B317318INPUT:319320- ``right`` - a matrix321322EXAMPLE::323324sage: K.<a> = GF(2^4)325sage: A = random_matrix(K,3,4); A326[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]327[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]328[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]329330sage: B = random_matrix(K,3,4); B331[ a^2 + a a^2 + 1 a^2 + a a^3 + a^2 + a]332[ a^2 + 1 a^3 + a^2 + a + 1 a^2 + 1 a^2]333[ a^3 + a^2 + a a^2 + 1 a^2 + a + 1 a^3 + a + 1]334335sage: C = A + B; C # indirect doctest336[ a a^3 + a^2 + a a^3 + 1 a^3 + a^2 + 1]337[a^3 + a^2 + 1 a^3 + a^2 + a a^3 + a^2 + a a^3 + 1]338[a^3 + a^2 + 1 a^3 + a^2 a^3 + a^2 a^2]339"""340cdef Matrix_mod2e_dense A341A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0, alloc=False)342if self._nrows == 0 or self._ncols == 0:343return A344A._entries = mzed_add(NULL, self._entries, (<Matrix_mod2e_dense>right)._entries)345346return A347348cpdef ModuleElement _sub_(self, ModuleElement right):349"""350EXAMPLE::351352sage: from sage.matrix.matrix_mod2e_dense import Matrix_mod2e_dense353sage: K.<a> = GF(2^4)354sage: m,n = 3, 4355sage: MS = MatrixSpace(K,m,n)356sage: A = random_matrix(K,3,4); A357[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]358[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]359[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]360361sage: B = random_matrix(K,3,4); B362[ a^2 + a a^2 + 1 a^2 + a a^3 + a^2 + a]363[ a^2 + 1 a^3 + a^2 + a + 1 a^2 + 1 a^2]364[ a^3 + a^2 + a a^2 + 1 a^2 + a + 1 a^3 + a + 1]365366sage: C = A - B; C # indirect doctest367[ a a^3 + a^2 + a a^3 + 1 a^3 + a^2 + 1]368[a^3 + a^2 + 1 a^3 + a^2 + a a^3 + a^2 + a a^3 + 1]369[a^3 + a^2 + 1 a^3 + a^2 a^3 + a^2 a^2]370"""371return self._add_(right)372373def _multiply_classical(self, Matrix right):374"""375Classical cubic matrix multiplication.376377EXAMPLES::378379sage: K.<a> = GF(2^2)380sage: A = random_matrix(K, 50, 50)381sage: B = random_matrix(K, 50, 50)382sage: A*B == A._multiply_classical(B)383True384385sage: K.<a> = GF(2^4)386sage: A = random_matrix(K, 50, 50)387sage: B = random_matrix(K, 50, 50)388sage: A*B == A._multiply_classical(B)389True390391sage: K.<a> = GF(2^8)392sage: A = random_matrix(K, 50, 50)393sage: B = random_matrix(K, 50, 50)394sage: A*B == A._multiply_classical(B)395True396397sage: K.<a> = GF(2^10)398sage: A = random_matrix(K, 50, 50)399sage: B = random_matrix(K, 50, 50)400sage: A*B == A._multiply_classical(B)401True402403.. note::404405This function is very slow. Use ``*`` operator instead.406"""407if self._ncols != right._nrows:408raise ArithmeticError("left ncols must match right nrows")409410cdef Matrix_mod2e_dense ans411412ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())413if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:414return ans415sig_on()416ans._entries = mzed_mul_naive(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)417sig_off()418return ans419420cdef Matrix _matrix_times_matrix_(self, Matrix right):421"""422Return A*B423424Uses the M4RIE machinery to decide which function to call.425426INPUT:427428- ``right`` - a matrix429430EXAMPLES::431432sage: K.<a> = GF(2^3)433sage: A = random_matrix(K, 51, 50)434sage: B = random_matrix(K, 50, 52)435sage: A*B == A._multiply_newton_john(B) # indirect doctest436True437438sage: K.<a> = GF(2^5)439sage: A = random_matrix(K, 10, 50)440sage: B = random_matrix(K, 50, 12)441sage: A*B == A._multiply_newton_john(B)442True443444sage: K.<a> = GF(2^7)445sage: A = random_matrix(K,100, 50)446sage: B = random_matrix(K, 50, 17)447sage: A*B == A._multiply_classical(B)448True449"""450if self._ncols != right._nrows:451raise ArithmeticError("left ncols must match right nrows")452453cdef Matrix_mod2e_dense ans454455ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())456if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:457return ans458sig_on()459ans._entries = mzed_mul(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)460sig_off()461return ans462463cpdef Matrix_mod2e_dense _multiply_newton_john(Matrix_mod2e_dense self, Matrix_mod2e_dense right):464"""465Return A*B using Newton-John tables.466467We can write classical cubic multiplication (``C=A*B``) as::468469for i in range(A.ncols()):470for j in range(A.nrows()):471C[j] += A[j,i] * B[j]472473Hence, in the inner-most loop we compute multiples of ``B[j]``474by the values ``A[j,i]``. If the matrix ``A`` is big and the475finite field is small, there is a very high chance that476``e * B[j]`` is computed more than once for any ``e`` in the finite477field. Instead, we compute all possible478multiples of ``B[j]`` and re-use this data in the inner loop.479This is what is called a "Newton-John" table in M4RIE.480481INPUT:482483- ``right`` - a matrix484485EXAMPLES::486487sage: K.<a> = GF(2^2)488sage: A = random_matrix(K, 50, 50)489sage: B = random_matrix(K, 50, 50)490sage: A._multiply_newton_john(B) == A._multiply_classical(B) # indirect doctest491True492493sage: K.<a> = GF(2^4)494sage: A = random_matrix(K, 50, 50)495sage: B = random_matrix(K, 50, 50)496sage: A._multiply_newton_john(B) == A._multiply_classical(B)497True498499sage: K.<a> = GF(2^8)500sage: A = random_matrix(K, 50, 50)501sage: B = random_matrix(K, 50, 50)502sage: A._multiply_newton_john(B) == A._multiply_classical(B)503True504505sage: K.<a> = GF(2^10)506sage: A = random_matrix(K, 50, 50)507sage: B = random_matrix(K, 50, 50)508sage: A._multiply_newton_john(B) == A._multiply_classical(B)509True510"""511if self._ncols != right._nrows:512raise ArithmeticError("left ncols must match right nrows")513514cdef Matrix_mod2e_dense ans515516ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())517if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:518return ans519520sig_on()521ans._entries = mzed_mul_newton_john(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)522sig_off()523return ans524525cpdef Matrix_mod2e_dense _multiply_karatsuba(Matrix_mod2e_dense self, Matrix_mod2e_dense right):526"""527Matrix multiplication using Karatsuba over polynomials with528matrix coefficients over GF(2).529530The idea behind Karatsuba multiplication for matrices over531`\GF{p^n}` is to treat these matrices as polynomials with532coefficients of matrices over `\GF{p}`. Then, Karatsuba-style533formulas can be used to perform multiplication, cf. [BB09]_.534535INPUT:536537- ``right`` - a matrix538539EXAMPLES::540541sage: K.<a> = GF(2^2)542sage: A = random_matrix(K, 50, 50)543sage: B = random_matrix(K, 50, 50)544sage: A._multiply_karatsuba(B) == A._multiply_classical(B) # indirect doctest545True546547sage: K.<a> = GF(2^2)548sage: A = random_matrix(K, 137, 11)549sage: B = random_matrix(K, 11, 23)550sage: A._multiply_karatsuba(B) == A._multiply_classical(B)551True552553sage: K.<a> = GF(2^10)554sage: A = random_matrix(K, 50, 50)555sage: B = random_matrix(K, 50, 50)556sage: A._multiply_karatsuba(B) == A._multiply_classical(B)557True558"""559if self._ncols != right._nrows:560raise ArithmeticError("left ncols must match right nrows")561562cdef Matrix_mod2e_dense ans563564ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())565if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:566return ans567568sig_on()569ans._entries = mzed_mul_karatsuba(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)570sig_off()571return ans572573cpdef Matrix_mod2e_dense _multiply_strassen(Matrix_mod2e_dense self, Matrix_mod2e_dense right, cutoff=0):574"""575Winograd-Strassen matrix multiplication with Newton-John576multiplication as base case.577578INPUT:579580- ``right`` - a matrix581- ``cutoff`` - row or column dimension to switch over to582Newton-John multiplication (default: 64)583584EXAMPLES::585586sage: K.<a> = GF(2^2)587sage: A = random_matrix(K, 50, 50)588sage: B = random_matrix(K, 50, 50)589sage: A._multiply_strassen(B) == A._multiply_classical(B) # indirect doctest590True591592sage: K.<a> = GF(2^4)593sage: A = random_matrix(K, 50, 50)594sage: B = random_matrix(K, 50, 50)595sage: A._multiply_strassen(B) == A._multiply_classical(B)596True597598sage: K.<a> = GF(2^8)599sage: A = random_matrix(K, 50, 50)600sage: B = random_matrix(K, 50, 50)601sage: A._multiply_strassen(B) == A._multiply_classical(B)602True603604sage: K.<a> = GF(2^10)605sage: A = random_matrix(K, 50, 50)606sage: B = random_matrix(K, 50, 50)607sage: A._multiply_strassen(B) == A._multiply_classical(B)608True609"""610if self._ncols != right._nrows:611raise ArithmeticError("left ncols must match right nrows")612613cdef Matrix_mod2e_dense ans614615ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols())616if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:617return ans618619if cutoff == 0:620cutoff = _mzed_strassen_cutoff(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries)621622sig_on()623ans._entries = mzed_mul_strassen(ans._entries, self._entries, (<Matrix_mod2e_dense>right)._entries, cutoff)624sig_off()625return ans626627cpdef ModuleElement _lmul_(self, RingElement right):628"""629Return ``a*B`` for ``a`` an element of the base field.630631INPUT:632633- ``right`` - an element of the base field634635EXAMPLES::636637sage: K.<a> = GF(4)638sage: A = random_matrix(K,10,10)639sage: A640[ 0 a + 1 a + 1 a + 1 0 1 a + 1 1 a + 1 1]641[a + 1 a + 1 a 1 a a 1 a + 1 1 0]642[ a 1 a + 1 a + 1 0 a + 1 a 1 a a]643[a + 1 a 0 0 1 a + 1 a + 1 0 a + 1 1]644[ a 0 a + 1 a a 0 a + 1 a 1 a + 1]645[ a 0 a a + 1 a 1 a + 1 a a a]646[a + 1 a 0 1 0 a + 1 a + 1 a 0 a + 1]647[a + 1 a + 1 0 a + 1 a 1 a + 1 a + 1 a + 1 0]648[ 0 0 0 a + 1 1 a + 1 0 a + 1 1 0]649[ 1 a + 1 0 1 a 0 0 a a + 1 a]650651sage: a*A # indirect doctest652[ 0 1 1 1 0 a 1 a 1 a]653[ 1 1 a + 1 a a + 1 a + 1 a 1 a 0]654[a + 1 a 1 1 0 1 a + 1 a a + 1 a + 1]655[ 1 a + 1 0 0 a 1 1 0 1 a]656[a + 1 0 1 a + 1 a + 1 0 1 a + 1 a 1]657[a + 1 0 a + 1 1 a + 1 a 1 a + 1 a + 1 a + 1]658[ 1 a + 1 0 a 0 1 1 a + 1 0 1]659[ 1 1 0 1 a + 1 a 1 1 1 0]660[ 0 0 0 1 a 1 0 1 a 0]661[ a 1 0 a a + 1 0 0 a + 1 1 a + 1]662"""663cdef m4ri_word a = poly_to_word(right)664cdef Matrix_mod2e_dense C = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0)665mzed_mul_scalar(C._entries, a, self._entries)666return C667668def __neg__(self):669"""670EXAMPLE::671672sage: K.<a> = GF(2^4)673sage: A = random_matrix(K, 3, 4); A674[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]675[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]676[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]677678sage: -A679[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]680[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]681[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]682"""683return self.__copy__()684685def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.686"""687EXAMPLE::688689sage: K.<a> = GF(2^4)690sage: A = random_matrix(K,3,4)691sage: B = copy(A)692sage: A == B693True694sage: A[0,0] = a695sage: A == B696False697"""698return self._richcmp(right, op)699700cdef int _cmp_c_impl(self, Element right) except -2:701if self._nrows == 0 or self._ncols == 0:702return 0703return mzed_cmp(self._entries, (<Matrix_mod2e_dense>right)._entries)704705def __copy__(self):706"""707EXAMPLE::708709sage: K.<a> = GF(2^4)710sage: m,n = 3, 4711sage: A = random_matrix(K,3,4); A712[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]713[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]714[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]715716sage: A2 = copy(A); A2717[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]718[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]719[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]720721sage: A[0,0] = 1722sage: A2[0,0]723a^2724"""725cdef Matrix_mod2e_dense A726A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0)727728if self._nrows and self._ncols:729mzed_copy(A._entries, <const_mzed_t *>self._entries)730731return A732733def _list(self):734"""735EXAMPLE::736737sage: K.<a> = GF(2^4)738sage: m,n = 3, 4739sage: A = random_matrix(K,3,4); A740[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1]741[ a^3 1 a^3 + a + 1 a^3 + a^2 + 1]742[ a + 1 a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]743744sage: A.list() # indirect doctest745[a^2, a^3 + a + 1, a^3 + a^2 + a + 1, a + 1, a^3, 1, a^3 + a + 1, a^3 + a^2 + 1, a + 1, a^3 + 1, a^3 + a + 1, a^3 + a^2 + a + 1]746"""747cdef int i,j748l = []749for i from 0 <= i < self._nrows:750for j from 0 <= j < self._ncols:751l.append(self.get_unsafe(i,j))752return l753754def randomize(self, density=1, nonzero=False, *args, **kwds):755"""756Randomize ``density`` proportion of the entries of this matrix,757leaving the rest unchanged.758759INPUT:760761- ``density`` - float; proportion (roughly) to be considered for762changes763- ``nonzero`` - Bool (default: ``False``); whether the new entries764are forced to be non-zero765766OUTPUT:767768- None, the matrix is modified in-place769770EXAMPLE::771772sage: K.<a> = GF(2^4)773sage: A = Matrix(K,3,3)774775sage: A.randomize(); A776[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1]777[ a + 1 a^3 1]778[ a^3 + a + 1 a^3 + a^2 + 1 a + 1]779780sage: K.<a> = GF(2^4)781sage: A = random_matrix(K,1000,1000,density=0.1)782sage: float(A.density())7830.0999...784785sage: A = random_matrix(K,1000,1000,density=1.0)786sage: float(A.density())7871.0788789sage: A = random_matrix(K,1000,1000,density=0.5)790sage: float(A.density())7910.4996...792793Note, that the matrix is updated and not zero-ed out before794being randomized::795796sage: A = matrix(K,1000,1000)797sage: A.randomize(nonzero=False, density=0.1)798sage: float(A.density())7990.0936...800801sage: A.randomize(nonzero=False, density=0.05)802sage: float(A.density())8030.135854804"""805if self._ncols == 0 or self._nrows == 0:806return807808cdef Py_ssize_t i,j809self.check_mutability()810self.clear_cache()811812cdef m4ri_word mask = (1<<(self._parent.base_ring().degree())) - 1813814cdef randstate rstate = current_randstate()815K = self._parent.base_ring()816817if self._ncols == 0 or self._nrows == 0:818return819820cdef float _density = density821if _density <= 0:822return823if _density > 1:824_density = 1.0825826if _density == 1:827if nonzero == False:828sig_on()829for i in range(self._nrows):830for j in range(self._ncols):831tmp = rstate.c_random() & mask832mzed_write_elem(self._entries, i, j, tmp)833sig_off()834else:835sig_on()836for i in range(self._nrows):837for j in range(self._ncols):838tmp = rstate.c_random() & mask839while tmp == 0:840tmp = rstate.c_random() & mask841mzed_write_elem(self._entries, i, j, tmp)842sig_off()843else:844if nonzero == False:845sig_on()846for i in range(self._nrows):847for j in range(self._ncols):848if rstate.c_rand_double() <= _density:849tmp = rstate.c_random() & mask850mzed_write_elem(self._entries, i, j, tmp)851sig_off()852else:853sig_on()854for i in range(self._nrows):855for j in range(self._ncols):856if rstate.c_rand_double() <= _density:857tmp = rstate.c_random() & mask858while tmp == 0:859tmp = rstate.c_random() & mask860mzed_write_elem(self._entries, i, j, tmp)861sig_off()862863def echelonize(self, algorithm='heuristic', reduced=True, **kwds):864"""865Compute the row echelon form of ``self`` in place.866867INPUT:868869- ``algorithm`` - one of the following870- ``heuristic`` - let M4RIE decide (default)871- ``newton_john`` - use newton_john table based algorithm872- ``ple`` - use PLE decomposition873- ``naive`` - use naive cubic Gaussian elimination (M4RIE implementation)874- ``builtin`` - use naive cubic Gaussian elimination (Sage implementation)875- ``reduced`` - if ``True`` return reduced echelon form. No876guarantee is given that the matrix is *not* reduced if877``False`` (default: ``True``)878879EXAMPLE::880881sage: K.<a> = GF(2^4)882sage: m,n = 3, 5883sage: A = random_matrix(K, 3, 5); A884[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 a + 1 a^3]885[ 1 a^3 + a + 1 a^3 + a^2 + 1 a + 1 a^3 + 1]886[ a^3 + a + 1 a^3 + a^2 + a + 1 a^2 + a a^2 + 1 a^2 + a]887888sage: A.echelonize(); A889[ 1 0 0 a + 1 a^2 + 1]890[ 0 1 0 a^2 a + 1]891[ 0 0 1 a^3 + a^2 + a a^3]892893sage: K.<a> = GF(2^3)894sage: m,n = 3, 5895sage: MS = MatrixSpace(K,m,n)896sage: A = random_matrix(K, 3, 5)897898sage: copy(A).echelon_form('newton_john')899[ 1 0 a + 1 0 a^2 + 1]900[ 0 1 a^2 + a + 1 0 a]901[ 0 0 0 1 a^2 + a + 1]902903sage: copy(A).echelon_form('naive');904[ 1 0 a + 1 0 a^2 + 1]905[ 0 1 a^2 + a + 1 0 a]906[ 0 0 0 1 a^2 + a + 1]907908sage: copy(A).echelon_form('builtin');909[ 1 0 a + 1 0 a^2 + 1]910[ 0 1 a^2 + a + 1 0 a]911[ 0 0 0 1 a^2 + a + 1]912"""913if self._nrows == 0 or self._ncols == 0:914self.cache('in_echelon_form',True)915self.cache('rank', 0)916self.cache('pivots', [])917return self918919cdef int k, n, full920921full = int(reduced)922923x = self.fetch('in_echelon_form')924if not x is None: return # already known to be in echelon form925926self.check_mutability()927self.clear_cache()928929if algorithm == 'naive':930sig_on()931r = mzed_echelonize_naive(self._entries, full)932sig_off()933934elif algorithm == 'newton_john':935sig_on()936r = mzed_echelonize_newton_john(self._entries, full)937sig_off()938939elif algorithm == 'ple':940sig_on()941r = mzed_echelonize_ple(self._entries, full)942sig_off()943944elif algorithm == 'heuristic':945sig_on()946r = mzed_echelonize(self._entries, full)947sig_off()948949elif algorithm == 'builtin':950self._echelon_in_place_classical()951952else:953raise ValueError("No algorithm '%s'."%algorithm)954955self.cache('in_echelon_form',True)956self.cache('rank', r)957self.cache('pivots', self._pivots())958959def _pivots(self):960"""961EXAMPLE::962963sage: K.<a> = GF(2^8)964sage: A = random_matrix(K, 15, 15)965sage: A.pivots() # indirect doctest966(0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14)967"""968if not self.fetch('in_echelon_form'):969raise ValueError("self must be in reduced row echelon form first.")970pivots = []971cdef Py_ssize_t i, j, nc972nc = self._ncols973i = 0974while i < self._nrows:975for j from i <= j < nc:976if self.get_unsafe(i,j):977pivots.append(j)978i += 1979break980if j == nc:981break982return pivots983984def __invert__(self):985"""986EXAMPLE::987988sage: K.<a> = GF(2^3)989sage: A = random_matrix(K,3,3); A990[ a^2 a + 1 a^2 + a + 1]991[ a + 1 0 1]992[ a + 1 a^2 + 1 a + 1]993994sage: B = ~A; B995[a^2 + a + 1 a^2 a^2]996[ a + 1 a^2 + a + 1 a + 1]997[ a a^2 + a a^2 + a + 1]998999sage: A*B1000[1 0 0]1001[0 1 0]1002[0 0 1]1003"""1004cdef Matrix_mod2e_dense A1005A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, self._parent, 0, 0, 0)10061007if self._nrows and self._nrows == self._ncols:1008mzed_invert_newton_john(A._entries, self._entries)10091010return A10111012cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):1013"""1014Return ``multiple * self[row][start_col:]``10151016INPUT:10171018- ``row`` - row index for row to rescale1019- ``multiple`` - finite field element to scale by1020- ``start_col`` - only start at this column index.10211022EXAMPLE::10231024sage: K.<a> = GF(2^3)1025sage: A = random_matrix(K,3,3); A1026[ a^2 a + 1 a^2 + a + 1]1027[ a + 1 0 1]1028[ a + 1 a^2 + 1 a + 1]10291030sage: A.rescale_row(0, a , 0); A1031[ a + 1 a^2 + a a^2 + 1]1032[ a + 1 0 1]1033[ a + 1 a^2 + 1 a + 1]10341035sage: A.rescale_row(0,0,0); A1036[ 0 0 0]1037[ a + 1 0 1]1038[ a + 1 a^2 + 1 a + 1]1039"""1040cdef m4ri_word x = poly_to_word(multiple)1041mzed_rescale_row(self._entries, row, start_col, x)104210431044cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple, Py_ssize_t start_col):1045"""1046Compute ``self[row_to][start_col:] += multiple * self[row_from][start_col:]``.10471048INPUT:10491050- ``row_to`` - row index of source1051- ``row_from`` - row index of destination1052- ``multiple`` - finite field element1053- ``start_col`` - only start at this column index10541055EXAMPLE::10561057sage: K.<a> = GF(2^3)1058sage: A = random_matrix(K,3,3); A1059[ a^2 a + 1 a^2 + a + 1]1060[ a + 1 0 1]1061[ a + 1 a^2 + 1 a + 1]10621063sage: A.add_multiple_of_row(0,1,a,0); A1064[ a a + 1 a^2 + 1]1065[ a + 1 0 1]1066[ a + 1 a^2 + 1 a + 1]1067"""10681069cdef m4ri_word x = poly_to_word(multiple)1070mzed_add_multiple_of_row(self._entries, row_to, self._entries, row_from, x, start_col)107110721073cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):1074"""1075Swap rows ``row1`` and ``row2``.10761077INPUT:10781079- ``row1`` - row index1080- ``row2`` - row index10811082EXAMPLE::10831084sage: K.<a> = GF(2^3)1085sage: A = random_matrix(K,3,3)1086sage: A1087[ a^2 a + 1 a^2 + a + 1]1088[ a + 1 0 1]1089[ a + 1 a^2 + 1 a + 1]10901091sage: A.swap_rows(0,1); A1092[ a + 1 0 1]1093[ a^2 a + 1 a^2 + a + 1]1094[ a + 1 a^2 + 1 a + 1]10951096"""1097mzed_row_swap(self._entries, row1, row2)10981099cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):1100"""1101Swap columns ``col1`` and ``col2``.11021103INPUT:11041105- ``col1`` - column index1106- ``col2`` - column index11071108EXAMPLE::11091110sage: K.<a> = GF(2^3)1111sage: A = random_matrix(K,3,3)1112sage: A1113[ a^2 a + 1 a^2 + a + 1]1114[ a + 1 0 1]1115[ a + 1 a^2 + 1 a + 1]11161117sage: A.swap_columns(0,1); A1118[ a + 1 a^2 a^2 + a + 1]1119[ 0 a + 1 1]1120[ a^2 + 1 a + 1 a + 1]11211122sage: A = random_matrix(K,4,16)11231124sage: B = copy(A)1125sage: B.swap_columns(0,1)1126sage: B.swap_columns(0,1)1127sage: A == B1128True11291130sage: A.swap_columns(0,15)1131sage: A.column(0) == B.column(15)1132True1133sage: A.swap_columns(14,15)1134sage: A.column(14) == B.column(0)1135True1136"""1137mzed_col_swap(self._entries, col1, col2)11381139def augment(self, Matrix_mod2e_dense right):1140"""1141Augments ``self`` with ``right``.11421143INPUT:11441145- ``right`` - a matrix11461147EXAMPLE::11481149sage: K.<a> = GF(2^4)1150sage: MS = MatrixSpace(K,3,3)1151sage: A = random_matrix(K,3,3)1152sage: B = A.augment(MS(1)); B1153[ a^2 a^3 + a + 1 a^3 + a^2 + a + 1 1 0 0]1154[ a + 1 a^3 1 0 1 0]1155[ a^3 + a + 1 a^3 + a^2 + 1 a + 1 0 0 1]11561157sage: B.echelonize(); B1158[ 1 0 0 a^2 + a a^3 + 1 a^3 + a]1159[ 0 1 0 a^3 + a^2 + a a^3 + a^2 + a + 1 a^2 + a]1160[ 0 0 1 a + 1 a^3 a^3]11611162sage: C = B.matrix_from_columns([3,4,5]); C1163[ a^2 + a a^3 + 1 a^3 + a]1164[ a^3 + a^2 + a a^3 + a^2 + a + 1 a^2 + a]1165[ a + 1 a^3 a^3]11661167sage: C == ~A1168True11691170sage: C*A == MS(1)1171True11721173TESTS::11741175sage: K.<a> = GF(2^4)1176sage: A = random_matrix(K,2,3)1177sage: B = random_matrix(K,2,0)1178sage: A.augment(B)1179[ a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]1180[ a^2 + a a^2 + 1 a^2 + a]11811182sage: B.augment(A)1183[ a^3 + 1 a^3 + a + 1 a^3 + a^2 + a + 1]1184[ a^2 + a a^2 + 1 a^2 + a]11851186sage: M = Matrix(K, 0, 0, 0)1187sage: N = Matrix(K, 0, 19, 0)1188sage: W = M.augment(N)1189sage: W.ncols()11901911911192sage: M = Matrix(K, 0, 1, 0)1193sage: N = Matrix(K, 0, 1, 0)1194sage: M.augment(N)1195[]1196"""1197cdef Matrix_mod2e_dense A11981199if self._nrows != right._nrows:1200raise TypeError, "Both numbers of rows must match."12011202if self._ncols == 0:1203return right.__copy__()1204if right._ncols == 0:1205return self.__copy__()12061207A = self.new_matrix(ncols = self._ncols + right._ncols)1208if self._nrows == 0:1209return A1210A._entries = mzed_concat(A._entries, self._entries, right._entries)1211return A12121213def stack(self, Matrix_mod2e_dense other):1214"""1215Stack ``self`` on top of ``other``.12161217INPUT:12181219- ``other`` - a matrix12201221EXAMPLE::12221223sage: K.<a> = GF(2^4)1224sage: A = random_matrix(K,2,2); A1225[ a^2 a^3 + a + 1]1226[a^3 + a^2 + a + 1 a + 1]12271228sage: B = random_matrix(K,2,2); B1229[ a^3 1]1230[ a^3 + a + 1 a^3 + a^2 + 1]12311232sage: A.stack(B)1233[ a^2 a^3 + a + 1]1234[a^3 + a^2 + a + 1 a + 1]1235[ a^3 1]1236[ a^3 + a + 1 a^3 + a^2 + 1]12371238sage: B.stack(A)1239[ a^3 1]1240[ a^3 + a + 1 a^3 + a^2 + 1]1241[ a^2 a^3 + a + 1]1242[a^3 + a^2 + a + 1 a + 1]12431244TESTS::12451246sage: A = random_matrix(K,0,3)1247sage: B = random_matrix(K,3,3)1248sage: A.stack(B)1249[ a + 1 a^3 + 1 a^3 + a + 1]1250[a^3 + a^2 + a + 1 a^2 + a a^2 + 1]1251[ a^2 + a a^3 + a^2 + a a^2 + 1]12521253sage: B.stack(A)1254[ a + 1 a^3 + 1 a^3 + a + 1]1255[a^3 + a^2 + a + 1 a^2 + a a^2 + 1]1256[ a^2 + a a^3 + a^2 + a a^2 + 1]12571258sage: M = Matrix(K, 0, 0, 0)1259sage: N = Matrix(K, 19, 0, 0)1260sage: W = M.stack(N)1261sage: W.nrows()1262191263sage: M = Matrix(K, 1, 0, 0)1264sage: N = Matrix(K, 1, 0, 0)1265sage: M.stack(N)1266[]1267"""1268if self._ncols != other._ncols:1269raise TypeError, "Both numbers of columns must match."12701271if self._nrows == 0:1272return other.__copy__()1273if other._nrows == 0:1274return self.__copy__()12751276cdef Matrix_mod2e_dense A1277A = self.new_matrix(nrows = self._nrows + other._nrows)1278if self._ncols == 0:1279return A1280A._entries = mzed_stack(A._entries, self._entries, other._entries)1281return A12821283def submatrix(self, lowr, lowc, nrows , ncols):1284"""1285Return submatrix from the index ``lowr,lowc`` (inclusive) with1286dimension ``nrows x ncols``.12871288INPUT:12891290- ``lowr`` -- index of start row1291- ``lowc`` -- index of start column1292- ``nrows`` -- number of rows of submatrix1293- ``ncols`` -- number of columns of submatrix12941295EXAMPLES::12961297sage: K.<a> = GF(2^10)1298sage: A = random_matrix(K,200,200)1299sage: A[0:2,0:2] == A.submatrix(0,0,2,2)1300True1301sage: A[0:100,0:100] == A.submatrix(0,0,100,100)1302True1303sage: A == A.submatrix(0,0,200,200)1304True13051306sage: A[1:3,1:3] == A.submatrix(1,1,2,2)1307True1308sage: A[1:100,1:100] == A.submatrix(1,1,99,99)1309True1310sage: A[1:200,1:200] == A.submatrix(1,1,199,199)1311True1312"""1313cdef int highr = lowr + nrows1314cdef int highc = lowc + ncols13151316if nrows <= 0 or ncols <= 0:1317raise TypeError("Expected nrows, ncols to be > 0, but got %d,%d instead."%(nrows, ncols))13181319if highc > self._entries.ncols:1320raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead."%(highc, self._entries.ncols))13211322if highr > self._entries.nrows:1323raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead."%(highr, self._entries.nrows))13241325if lowr < 0:1326raise TypeError("Expected lowr >= 0, but got %d instead."%lowr)13271328if lowc < 0:1329raise TypeError("Expected lowc >= 0, but got %d instead."%lowc)13301331cdef Matrix_mod2e_dense A = self.new_matrix(nrows = nrows, ncols = ncols)1332if self._ncols == 0 or self._nrows == 0:1333return A1334A._entries = mzed_submatrix(A._entries, self._entries, lowr, lowc, highr, highc)1335return A13361337def rank(self):1338"""1339Return the rank of this matrix (cached).13401341EXAMPLE::13421343sage: K.<a> = GF(2^4)1344sage: A = random_matrix(K, 1000, 1000)1345sage: A.rank()1346100013471348sage: A = matrix(K, 10, 0)1349sage: A.rank()135001351"""1352x = self.fetch('rank')1353if not x is None:1354return x1355if self._nrows == 0 or self._ncols == 0:1356return 01357cdef mzed_t *A = mzed_copy(NULL, self._entries)13581359cdef size_t r = mzed_echelonize(A, 0)1360mzed_free(A)1361self.cache('rank', r)1362return r13631364def __hash__(self):1365"""1366EXAMPLE::13671368sage: K.<a> = GF(2^4)1369sage: A = random_matrix(K, 1000, 1000)1370sage: A.set_immutable()1371sage: {A:1} #indirect doctest1372{1000 x 1000 dense matrix over Finite Field in a of size 2^4 (type 'print A.str()' to see all of the entries): 1}13731374"""1375return self._hash()13761377def __reduce__(self):1378"""1379EXAMPLE::13801381sage: K.<a> = GF(2^8)1382sage: A = random_matrix(K,70,70)1383sage: f, s= A.__reduce__()1384sage: from sage.matrix.matrix_mod2e_dense import unpickle_matrix_mod2e_dense_v01385sage: f == unpickle_matrix_mod2e_dense_v01386True1387sage: f(*s) == A1388True1389"""1390from sage.matrix.matrix_space import MatrixSpace13911392cdef Matrix_mod2_dense A1393MS = MatrixSpace(GF(2), self._entries.x.nrows, self._entries.x.ncols)1394A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = False)1395A._entries = mzd_copy( NULL, self._entries.x)1396return unpickle_matrix_mod2e_dense_v0, (A, self.base_ring(), self.nrows(), self.ncols())13971398def slice(self):1399r"""1400Unpack this matrix into matrices over `\GF{2}`.14011402Elements in `\GF{2^e}` can be represented as `\sum c_i a^i`1403where `a` is a root the minimal polynomial. This function1404returns a tuple of matrices `C` whose entry `C_i[x,y]` is the1405coefficient of `c_i` in `A[x,y]` if this matrix is `A`.14061407EXAMPLE::14081409sage: K.<a> = GF(2^2)1410sage: A = random_matrix(K, 5, 5); A1411[ 0 a + 1 a + 1 a + 1 0]1412[ 1 a + 1 1 a + 1 1]1413[a + 1 a + 1 a 1 a]1414[ a 1 a + 1 1 0]1415[ a 1 a + 1 a + 1 0]14161417sage: A1,A0 = A.slice()1418sage: A01419[0 1 1 1 0]1420[0 1 0 1 0]1421[1 1 1 0 1]1422[1 0 1 0 0]1423[1 0 1 1 0]14241425sage: A11426[0 1 1 1 0]1427[1 1 1 1 1]1428[1 1 0 1 0]1429[0 1 1 1 0]1430[0 1 1 1 0]14311432sage: A0[2,4]*a + A1[2,4], A[2,4]1433(a, a)14341435sage: K.<a> = GF(2^3)1436sage: A = random_matrix(K, 5, 5); A1437[ a + 1 a^2 + a 1 a a^2 + a]1438[ a + 1 a^2 + a a^2 a^2 a^2 + 1]1439[a^2 + a + 1 a^2 + a + 1 0 a^2 + a + 1 a^2 + 1]1440[ a^2 + a 0 a^2 + a + 1 a a]1441[ a^2 a + 1 a a^2 + 1 a^2 + a + 1]14421443sage: A0,A1,A2 = A.slice()1444sage: A01445[1 0 1 0 0]1446[1 0 0 0 1]1447[1 1 0 1 1]1448[0 0 1 0 0]1449[0 1 0 1 1]14501451Slicing and clinging are inverse operations::14521453sage: B = matrix(K, 5, 5)1454sage: B.cling(A0,A1,A2)1455sage: B == A1456True1457"""1458if self._entries.finite_field.degree > 4:1459raise NotImplementedError("Slicing is only implemented for degree <= 4.")14601461from sage.matrix.matrix_space import MatrixSpace14621463MS = MatrixSpace(GF(2), self._nrows, self._ncols)1464cdef mzd_slice_t *a = mzed_slice(NULL, self._entries)14651466cdef Matrix_mod2_dense A0, A1, A2, A31467A0 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)1468A1 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)1469mzd_copy(A0._entries, a.x[0])1470mzd_copy(A1._entries, a.x[1])1471if self._entries.finite_field.degree > 2:1472A2 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)1473mzd_copy(A2._entries, a.x[2])1474if self._entries.finite_field.degree > 3:1475A3 = Matrix_mod2_dense.__new__(Matrix_mod2_dense, MS, 0, 0, 0, alloc = True)1476mzd_copy(A3._entries, a.x[3])14771478mzd_slice_free(a)1479if self._entries.finite_field.degree == 2:1480return A0,A11481elif self._entries.finite_field.degree == 3:1482return A0,A1,A21483elif self._entries.finite_field.degree == 4:1484return A0,A1,A2,A314851486def cling(self, *C):1487r"""1488Pack the matrices over `\GF{2}` into this matrix over `\GF{2^e}`.14891490Elements in `\GF{2^e}` can be represented as `\sum c_i a^i` where1491`a` is a root the minimal polynomial. If this matrix is `A`1492then this function writes `c_i a^i` to the entry `A[x,y]`1493where `c_i` is the entry `C_i[x,y]`.14941495INPUT:14961497- ``C`` - a list of matrices over GF(2)14981499EXAMPLE::15001501sage: K.<a> = GF(2^2)1502sage: A = matrix(K, 5, 5)1503sage: A0 = random_matrix(GF(2), 5, 5); A01504[0 1 0 1 1]1505[0 1 1 1 0]1506[0 0 0 1 0]1507[0 1 1 0 0]1508[0 0 0 1 1]15091510sage: A1 = random_matrix(GF(2), 5, 5); A11511[0 0 1 1 1]1512[1 1 1 1 0]1513[0 0 0 1 1]1514[1 0 0 0 1]1515[1 0 0 1 1]15161517sage: A.cling(A1, A0); A1518[ 0 a 1 a + 1 a + 1]1519[ 1 a + 1 a + 1 a + 1 0]1520[ 0 0 0 a + 1 1]1521[ 1 a a 0 1]1522[ 1 0 0 a + 1 a + 1]15231524sage: A0[0,3]*a + A1[0,3], A[0,3]1525(a + 1, a + 1)15261527Slicing and clinging are inverse operations::15281529sage: B1, B0 = A.slice()1530sage: B0 == A0 and B1 == A11531True15321533TESTS::15341535sage: K.<a> = GF(2^2)1536sage: A = matrix(K, 5, 5)1537sage: A0 = random_matrix(GF(2), 5, 5)1538sage: A1 = random_matrix(GF(2), 5, 5)1539sage: A.cling(A0, A1)1540sage: B = copy(A)1541sage: A.cling(A0, A1)1542sage: A == B1543True15441545sage: A.cling(A0)1546Traceback (most recent call last):1547...1548ValueError: The number of input matrices must be equal to the degree of the base field.15491550sage: K.<a> = GF(2^5)1551sage: A = matrix(K, 5, 5)1552sage: A0 = random_matrix(GF(2), 5, 5)1553sage: A1 = random_matrix(GF(2), 5, 5)1554sage: A2 = random_matrix(GF(2), 5, 5)1555sage: A3 = random_matrix(GF(2), 5, 5)1556sage: A4 = random_matrix(GF(2), 5, 5)1557sage: A.cling(A0, A1, A2, A3, A4)1558Traceback (most recent call last):1559...1560NotImplementedError: Cling is only implemented for degree <= 4.1561"""1562cdef Py_ssize_t i15631564if self._entries.finite_field.degree > 4:1565raise NotImplementedError("Cling is only implemented for degree <= 4.")15661567if self._is_immutable:1568raise TypeError("Immutable matrices cannot be modified.")15691570if len(C) != self._entries.finite_field.degree:1571raise ValueError("The number of input matrices must be equal to the degree of the base field.")15721573cdef mzd_slice_t *v = mzd_slice_init(self._entries.finite_field, self._nrows, self._ncols)1574for i in range(self._entries.finite_field.degree):1575if not PY_TYPE_CHECK(C[i], Matrix_mod2_dense):1576mzd_slice_free(v)1577raise TypeError("All input matrices must be over GF(2).")1578mzd_copy(v.x[i], (<Matrix_mod2_dense>C[i])._entries)1579mzed_set_ui(self._entries, 0)1580mzed_cling(self._entries, v)1581mzd_slice_free(v)15821583def unpickle_matrix_mod2e_dense_v0(Matrix_mod2_dense a, base_ring, nrows, ncols):1584"""1585EXAMPLE::15861587sage: K.<a> = GF(2^2)1588sage: A = random_matrix(K,10,10)1589sage: f, s= A.__reduce__()1590sage: from sage.matrix.matrix_mod2e_dense import unpickle_matrix_mod2e_dense_v01591sage: f == unpickle_matrix_mod2e_dense_v01592True1593sage: f(*s) == A1594True1595"""1596from sage.matrix.matrix_space import MatrixSpace15971598MS = MatrixSpace(base_ring, nrows, ncols)1599cdef Matrix_mod2e_dense A = Matrix_mod2e_dense.__new__(Matrix_mod2e_dense, MS, 0, 0, 0)1600mzd_copy(A._entries.x, a._entries)1601return A160216031604