Path: blob/master/src/sage/matrix/matrix_mod2_dense.pyx
8815 views
"""1Dense matrices over GF(2) using the M4RI library.23AUTHOR: Martin Albrecht <[email protected]>45EXAMPLES::67sage: a = matrix(GF(2),3,range(9),sparse=False); a8[0 1 0]9[1 0 1]10[0 1 0]11sage: a.rank()12213sage: type(a)14<type 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>15sage: a[0,0] = 116sage: a.rank()17318sage: parent(a)19Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 22021sage: a^222[0 1 1]23[1 0 0]24[1 0 1]25sage: a+a26[0 0 0]27[0 0 0]28[0 0 0]2930sage: b = a.new_matrix(2,3,range(6)); b31[0 1 0]32[1 0 1]3334sage: a*b35Traceback (most recent call last):36...37TypeError: unsupported operand parent(s) for '*': 'Full MatrixSpace of 3 by 3 dense matrices over Finite Field of size 2' and 'Full MatrixSpace of 2 by 3 dense matrices over Finite Field of size 2'38sage: b*a39[1 0 1]40[1 0 0]4142sage: TestSuite(a).run()43sage: TestSuite(b).run()4445sage: a.echelonize(); a46[1 0 0]47[0 1 0]48[0 0 1]49sage: b.echelonize(); b50[1 0 1]51[0 1 0]5253TESTS:54sage: FF = FiniteField(2)55sage: V = VectorSpace(FF,2)56sage: v = V([0,1]); v57(0, 1)58sage: W = V.subspace([v])59sage: W60Vector space of degree 2 and dimension 1 over Finite Field of size 261Basis matrix:62[0 1]63sage: v in W64True6566sage: M = Matrix(GF(2), [[1,1,0],[0,1,0]])67sage: M.row_space()68Vector space of degree 3 and dimension 2 over Finite Field of size 269Basis matrix:70[1 0 0]71[0 1 0]7273sage: M = Matrix(GF(2), [[1,1,0],[0,0,1]])74sage: M.row_space()75Vector space of degree 3 and dimension 2 over Finite Field of size 276Basis matrix:77[1 1 0]78[0 0 1]7980TODO:81- make LinBox frontend and use it82- charpoly ?83- minpoly ?84- make Matrix_modn_frontend and use it (?)85"""8687##############################################################################88# Copyright (C) 2004,2005,2006 William Stein <[email protected]>89# Copyright (C) 2007,2008,2009 Martin Albrecht <[email protected]>90# Distributed under the terms of the GNU General Public License (GPL)91# The full text of the GPL is available at:92# http://www.gnu.org/licenses/93##############################################################################9495include "sage/ext/interrupt.pxi"96include "sage/ext/cdefs.pxi"97include 'sage/ext/stdsage.pxi'98include 'sage/ext/random.pxi'99100cimport matrix_dense101from sage.structure.element cimport Matrix, Vector102from sage.structure.element cimport ModuleElement, Element103104from sage.misc.functional import log105106from sage.misc.misc import verbose, get_verbose, cputime107108from sage.modules.free_module import VectorSpace109from sage.modules.vector_mod2_dense cimport Vector_mod2_dense110111cdef extern from "gd.h":112ctypedef struct gdImagePtr "gdImagePtr":113pass114115gdImagePtr gdImageCreateFromPng(FILE *f)116gdImagePtr gdImageCreateFromPngPtr(int size, void *s)117gdImagePtr gdImageCreate(int x, int y)118void gdImagePng(gdImagePtr im, FILE *out)119void *gdImagePngPtr(gdImagePtr im, int *size)120void gdImageDestroy(gdImagePtr im)121int gdImageSX(gdImagePtr im)122int gdImageSY(gdImagePtr im)123int gdImageGetPixel(gdImagePtr im, int x, int y)124void gdImageSetPixel(gdImagePtr im, int x, int y, int value)125int gdImageColorAllocate(gdImagePtr im, int r, int g, int b)126void gdImageFilledRectangle(gdImagePtr im, int x1, int y1, int x2, int y2, int color)127void gdFree(void *m)128129## from sage.libs.linbox.linbox cimport Linbox_mod2_dense130## cdef Linbox_mod2_dense linbox131## linbox = Linbox_mod2_dense()132133cdef object called134135cdef void init_m4ri():136global called137if called is None:138m4ri_build_all_codes()139called = True140141init_m4ri()142143def free_m4ri():144"""145Free global Gray code tables.146"""147m4ri_destroy_all_codes()148149150151cdef class Matrix_mod2_dense(matrix_dense.Matrix_dense): # dense or sparse152"""153Dense matrix over GF(2).154"""155########################################################################156# LEVEL 1 functionality157########################################################################158def __cinit__(self, parent, entries, copy, coerce, alloc=True):159"""160Dense matrix over GF(2) constructor.161162INPUT:163164- ``parent`` - MatrixSpace.165- ``entries`` - may be list or 0 or 1166- ``copy`` - ignored, elements are always copied167- ``coerce`` - ignored, elements are always coerced to ints % 2168169EXAMPLES::170171sage: type(random_matrix(GF(2),2,2))172<type 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>173174sage: Matrix(GF(2),3,3,1) # indirect doctest175[1 0 0]176[0 1 0]177[0 0 1]178179See trac #10858:180181sage: matrix(GF(2),0,[]) * vector(GF(2),0,[])182()183"""184matrix_dense.Matrix_dense.__init__(self, parent)185186if alloc:187self._entries = mzd_init(self._nrows, self._ncols)188189# cache elements190self._zero = self._base_ring(0)191self._one = self._base_ring(1)192193def __dealloc__(self):194if self._entries:195mzd_free(self._entries)196self._entries = NULL197198def __init__(self, parent, entries, copy, coerce):199"""200Dense matrix over GF(2) constructor.201202INPUT:203204- ``parent`` - MatrixSpace.205- ``entries`` - may be list or 0 or 1206- ``copy`` - ignored, elements are always copied207- ``coerce`` - ignored, elements are always coerced to ints % 2208209EXAMPLES::210211sage: type(random_matrix(GF(2),2,2))212<type 'sage.matrix.matrix_mod2_dense.Matrix_mod2_dense'>213214sage: Matrix(GF(2),3,3,1)215[1 0 0]216[0 1 0]217[0 0 1]218219sage: Matrix(GF(2),2,2,[1,1,1,0])220[1 1]221[1 0]222223sage: Matrix(GF(2),2,2,4)224[0 0]225[0 0]226227sage: Matrix(GF(2),1,1, 1/3)228[1]229sage: Matrix(GF(2),1,1, [1/3])230[1]231232TESTS::233234sage: Matrix(GF(2),0,0)235[]236sage: Matrix(GF(2),2,0)237[]238sage: Matrix(GF(2),0,2)239[]240"""241cdef int i,j,e242243if entries is None:244return245246R = self.base_ring()247248# scalar ?249if not isinstance(entries, list):250if self._nrows and self._ncols and R(entries) == 1:251mzd_set_ui(self._entries, 1)252return253254# all entries are given as a long list255if len(entries) != self._nrows * self._ncols:256raise IndexError("The vector of entries has the wrong length.")257258k = 0259260for i from 0 <= i < self._nrows:261sig_check()262for j from 0 <= j < self._ncols:263mzd_write_bit(self._entries,i,j, R(entries[k]))264k = k + 1265266def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons.267"""268Compares ``self`` with ``right``. While equality and269inequality are clearly defined, ``<`` and ``>`` are not. For270those first the matrix dimensions of ``self`` and ``right``271are compared. If these match then ``<`` means that there is a272position smallest (i,j) in ``self`` where ``self[i,j]`` is273zero but ``right[i,j]`` is one. This (i,j) is smaller than the274(i,j) if ``self`` and ``right`` are exchanged for the275comparison.276277INPUT:278279- ``right`` - a matrix280- ``op`` - comparison operation281282EXAMPLE::283284sage: A = random_matrix(GF(2),2,2)285sage: B = random_matrix(GF(2),3,3)286sage: A < B287True288sage: A = MatrixSpace(GF(2),3,3).one()289sage: B = copy(MatrixSpace(GF(2),3,3).one())290sage: B[0,1] = 1291sage: A < B292True293294TESTS::295296sage: A = matrix(GF(2),2,0)297sage: B = matrix(GF(2),2,0)298sage: A < B299False300"""301return self._richcmp(right, op)302303def __hash__(self):304r"""305The has of a matrix is computed as `\oplus i*a_i` where the306`a_i` are the flattened entries in a matrix (by row, then by307column).308309EXAMPLE::310311sage: B = random_matrix(GF(2),3,3)312sage: B.set_immutable()313sage: {B:0} # indirect doctest314{[0 1 0]315[0 1 1]316[0 0 0]: 0}317sage: M = random_matrix(GF(2), 123, 321)318sage: M.set_immutable()319sage: MZ = M.change_ring(ZZ)320sage: MZ.set_immutable()321sage: hash(M) == hash(MZ)322True323sage: MS = M.sparse_matrix()324sage: MS.set_immutable()325sage: hash(M) == hash(MS)326True327328TEST:329sage: A = matrix(GF(2),2,0)330sage: hash(A)331Traceback (most recent call last):332...333TypeError: mutable matrices are unhashable334sage: A.set_immutable()335sage: hash(A)3360337338"""339if not self._is_immutable:340raise TypeError("mutable matrices are unhashable")341342x = self.fetch('hash')343if not x is None:344return x345346if self._nrows == 0 or self._ncols == 0:347return 0348349cdef unsigned long i, j, truerow350cdef unsigned long start, shift351cdef m4ri_word row_xor352cdef m4ri_word end_mask = __M4RI_LEFT_BITMASK(self._ncols%m4ri_radix)353cdef m4ri_word top_mask, bot_mask354cdef m4ri_word cur355cdef m4ri_word* row356357# running_xor is the xor of all words in the matrix, as if the rows358# in the matrix were written out consecutively, without regard to359# word boundaries.360cdef m4ri_word running_xor = 0361# running_parity is the number of extra words that must be xor'd.362cdef unsigned long running_parity = 0363364365for i from 0 <= i < self._entries.nrows:366367# All this shifting and masking is because the368# rows are word-aligned.369row = self._entries.rows[i]370start = (i*self._entries.ncols) >> 6371shift = (i*self._entries.ncols) & 0x3F372bot_mask = __M4RI_LEFT_BITMASK(m4ri_radix - shift)373top_mask = ~bot_mask374375if self._entries.width > 1:376row_xor = row[0]377running_parity ^= start & parity_mask(row[0] & bot_mask)378379for j from 1 <= j < self._entries.width - 1:380row_xor ^= row[j]381cur = ((row[j-1] >> (63-shift)) >> 1) ^ (row[j] << shift)382running_parity ^= (start+j) & parity_mask(cur)383384running_parity ^= (start+j) & parity_mask(row[j-1] & top_mask)385386else:387j = 0388row_xor = 0389390cur = row[j] & end_mask391row_xor ^= cur392running_parity ^= (start+j) & parity_mask(cur & bot_mask)393running_parity ^= (start+j+1) & parity_mask(cur & top_mask)394395running_xor ^= (row_xor << shift) ^ ((row_xor >> (63-shift)) >> 1)396397cdef unsigned long bit_is_set398cdef unsigned long h399400# Now we assemble the running_parity and running_xor to get the hash.401# Viewing the flattened matrix as a list of a_i, the hash is the xor402# of the i for which a_i is non-zero. We split i into the lower m4ri_radix403# bits and the rest, so i = i1 << m4ri_radix + i0. Now two matching i0404# would cancel, so we only need the parity of how many of each405# possible i0 occur. This is stored in the bits of running_xor.406# Similarly, running_parity is the xor of the i1 needed. It's called407# parity because i1 is constant across a word, and for each word408# the number of i1 to add is equal to the number of set bits in that409# word (but because two cancel, we only need keep track of the410# parity.411412h = m4ri_radix * running_parity413for i from 0 <= i < m4ri_radix:414bit_is_set = (running_xor >> i) & 1415h ^= (m4ri_radix-1) & ~(bit_is_set-1) & i416417if h == -1:418h = -2419420self.cache('hash', h)421return h422423# this exists for compatibility with matrix_modn_dense424cdef set_unsafe_int(self, Py_ssize_t i, Py_ssize_t j, int value):425"""426Set the (i,j) entry of self to the int value.427"""428mzd_write_bit(self._entries, i, j, int(value))429430cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):431mzd_write_bit(self._entries, i, j, int(value))432433cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):434if mzd_read_bit(self._entries, i, j):435return self._one436else:437return self._zero438439440def str(self, rep_mapping=None, zero=None, plus_one=None, minus_one=None):441"""442Return a nice string representation of the matrix.443444INPUT:445446- ``rep_mapping`` - a dictionary or callable used to override447the usual representation of elements. For a dictionary,448keys should be elements of the base ring and values the449desired string representation.450451- ``zero`` - string (default: ``None``); if not ``None`` use452the value of ``zero`` as the representation of the zero453element.454455- ``plus_one`` - string (default: ``None``); if not ``None``456use the value of ``plus_one`` as the representation of the457one element.458459- ``minus_one`` - Ignored. Only for compatibility with460generic matrices.461462EXAMPLE::463464sage: B = random_matrix(GF(2),3,3)465sage: B # indirect doctest466[0 1 0]467[0 1 1]468[0 0 0]469sage: block_matrix([[B, 1], [0, B]])470[0 1 0|1 0 0]471[0 1 1|0 1 0]472[0 0 0|0 0 1]473[-----+-----]474[0 0 0|0 1 0]475[0 0 0|0 1 1]476[0 0 0|0 0 0]477sage: B.str(zero='.')478'[. 1 .]\n[. 1 1]\n[. . .]'479"""480if self._nrows ==0 or self._ncols == 0:481return "[]"482483cdef int i,j, last_i484cdef list s = []485empty_row = " "*(self._ncols*2-1)486cdef char *row_s487cdef char *div_s488489# Set the mapping based on keyword arguments490# We ignore minus_one (it's only there for compatibility with Matrix)491if rep_mapping is not None or zero is not None or plus_one is not None:492# Shunt mappings off to the generic code since they might not be single characters493return matrix_dense.Matrix_dense.str(self, rep_mapping=rep_mapping, zero=zero, plus_one=plus_one)494495cdef list row_div, col_div496if self._subdivisions is not None:497row_s = empty_row498div_s = row_divider = b"[%s]" % ("-" * (self._ncols*2-1))499row_div, col_div = self.subdivisions()500last_i = 0501for i in col_div:502if i == last_i or i == self._ncols:503# Adjacent column divisions messy, use generic code504return matrix_dense.Matrix_dense.str(self, rep_mapping)505row_s[2*i-1] = '|'506div_s[2*i] = '+'507last_i = i508509for i from 0 <= i < self._nrows:510row_s = row = b"[%s]" % empty_row511for j from 0 <= j < self._ncols:512row_s[1+2*j] = c'0' + mzd_read_bit(self._entries,i,j)513s.append(row)514515if self._subdivisions is not None:516for i in reversed(row_div):517s.insert(i, row_divider)518519return "\n".join(s)520521def row(self, Py_ssize_t i, from_list=False):522"""523Return the ``i``'th row of this matrix as a vector.524525This row is a dense vector if and only if the matrix is a dense526matrix.527528INPUT:529530- ``i`` - integer531532- ``from_list`` - bool (default: ``False``); if ``True``,533returns the ``i``'th element of ``self.rows()`` (see534:func:`rows`), which may be faster, but requires building a535list of all rows the first time it is called after an entry536of the matrix is changed.537538EXAMPLES::539540sage: A = random_matrix(GF(2),10,10); A541[0 1 0 1 1 0 0 0 1 1]542[0 1 1 1 0 1 1 0 0 1]543[0 0 0 1 0 1 0 0 1 0]544[0 1 1 0 0 1 0 1 1 0]545[0 0 0 1 1 1 1 0 1 1]546[0 0 1 1 1 1 0 0 0 0]547[1 1 1 1 0 1 0 1 1 0]548[0 0 0 1 1 0 0 0 1 1]549[1 0 0 0 1 1 1 0 1 1]550[1 0 0 1 1 0 1 0 0 0]551552sage: A.row(0)553(0, 1, 0, 1, 1, 0, 0, 0, 1, 1)554555sage: A.row(-1)556(1, 0, 0, 1, 1, 0, 1, 0, 0, 0)557558sage: A.row(2,from_list=True)559(0, 0, 0, 1, 0, 1, 0, 0, 1, 0)560561sage: A = Matrix(GF(2),1,0)562sage: A.row(0)563()564"""565if self._nrows == 0:566raise IndexError("matrix has no rows")567if i >= self._nrows or i < -self._nrows:568raise IndexError("row index out of range")569if i < 0:570i = i + self._nrows571if from_list:572return self.rows(copy=False)[i]573cdef Py_ssize_t j574cdef Vector_mod2_dense z = PY_NEW(Vector_mod2_dense)575z._init(self._ncols, VectorSpace(self.base_ring(),self._ncols))576if self._ncols:577mzd_submatrix(z._entries, self._entries, i, 0, i+1, self._ncols)578return z579580########################################################################581# LEVEL 2 functionality582# * def _pickle583# * def _unpickle584# * cdef _mul_585# * cdef _cmp_c_impl586# * _list -- list of underlying elements (need not be a copy)587# * _dict -- sparse dictionary of underlying elements (need not be a copy)588########################################################################589# def _pickle(self):590# def _unpickle(self, data, int version): # use version >= 0591592cpdef ModuleElement _add_(self, ModuleElement right):593"""594Matrix addition.595596INPUT:597right -- matrix of dimension self.nrows() x self.ncols()598599EXAMPLES::600601sage: A = random_matrix(GF(2),10,10)602sage: A + A == Matrix(GF(2),10,10,0)603True604605sage: A = random_matrix(GF(2),257,253)606sage: A + A == Matrix(GF(2),257,253,0) # indirect doctest607True608609TESTS::610611sage: A = matrix(GF(2),2,0)612sage: A+A613[]614sage: A = matrix(GF(2),0,2)615sage: A+A616[]617sage: A = matrix(GF(2),0,0)618sage: A+A619[]620"""621cdef Matrix_mod2_dense A622A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc=False)623if self._nrows == 0 or self._ncols == 0:624return A625A._entries = mzd_add(NULL, self._entries,(<Matrix_mod2_dense>right)._entries)626627return A628629cpdef ModuleElement _sub_(self, ModuleElement right):630"""631Matrix addition.632633INPUT:634right -- matrix of dimension self.nrows() x self.ncols()635636EXAMPLES::637638sage: A = random_matrix(GF(2),10,10)639sage: A - A == Matrix(GF(2),10,10,0) # indirect doctest640True641"""642return self._add_(right)643644cdef Vector _matrix_times_vector_(self, Vector v):645"""646EXAMPLES::647648sage: A = random_matrix(GF(2),10^4,10^4)649sage: v0 = random_matrix(GF(2),10^4,1)650sage: v1 = v0.column(0)651sage: r0 = A*v0652sage: r1 = A*v1653sage: r0.column(0) == r1654True655"""656cdef mzd_t *tmp657if not PY_TYPE_CHECK(v, Vector_mod2_dense):658M = VectorSpace(self._base_ring, self._nrows)659v = M(v)660if self.ncols() != v.degree():661raise ArithmeticError("number of columns of matrix must equal degree of vector")662663VS = VectorSpace(self._base_ring, self._nrows)664cdef Vector_mod2_dense c = PY_NEW(Vector_mod2_dense)665c._init(self._nrows, VS)666c._entries = mzd_init(1, self._nrows)667if c._entries.nrows and c._entries.ncols:668tmp = mzd_init(self._nrows, 1)669_mzd_mul_naive(tmp, self._entries, (<Vector_mod2_dense>v)._entries, 0)670mzd_transpose(c._entries, tmp)671mzd_free(tmp)672return c673674cdef Matrix _matrix_times_matrix_(self, Matrix right):675"""676Matrix multiplication.677678ALGORITHM: Uses the 'Method of the Four Russians679Multiplication', see :func:`_multiply_m4rm`.680"""681if get_verbose() >= 2:682verbose('matrix multiply of %s x %s matrix by %s x %s matrix'%(683self._nrows, self._ncols, right._nrows, right._ncols))684685return self._multiply_strassen(right, 0)686687cpdef Matrix_mod2_dense _multiply_m4rm(Matrix_mod2_dense self, Matrix_mod2_dense right, int k):688"""689Multiply matrices using the 'Method of the Four Russians690Multiplication' (M4RM) or Konrod's method.691692The algorithm is based on an algorithm by Arlazarov, Dinic,693Kronrod, and Faradzev [ADKF70] and appeared in [AHU]. This694implementation is based on a description given in Gregory695Bard's 'Method of the Four Russians Inversion' paper [B06].696697INPUT:698right -- Matrix699k -- parameter $k$ for the Gray Code table size. If $k=0$ a700suitable value is chosen by the function.701($0<= k <= 16$, default: 0)702703EXAMPLE::704705sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )706sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )707sage: A708[0 0 0]709[0 1 0]710[0 1 1]711[0 0 1]712sage: B713[0 0 1 0]714[1 0 0 1]715[1 1 0 0]716sage: A._multiply_m4rm(B, 0)717[0 0 0 0]718[1 0 0 1]719[0 1 0 1]720[1 1 0 0]721722TESTS::723724sage: A = random_matrix(GF(2),0,0)725sage: B = random_matrix(GF(2),0,0)726sage: A._multiply_m4rm(B, 0)727[]728sage: A = random_matrix(GF(2),3,0)729sage: B = random_matrix(GF(2),0,3)730sage: A._multiply_m4rm(B, 0)731[0 0 0]732[0 0 0]733[0 0 0]734sage: A = random_matrix(GF(2),0,3)735sage: B = random_matrix(GF(2),3,0)736sage: A._multiply_m4rm(B, 0)737[]738739ALGORITHM: Uses the 'Method of the Four Russians'740multiplication as implemented in the M4RI library.741742REFERENCES:743[AHU] A. Aho, J. Hopcroft, and J. Ullman. 'Chapter 6:744Matrix Multiplication and Related Operations.'745The Design and Analysis of Computer746Algorithms. Addison-Wesley, 1974.747748[ADKF70] V. Arlazarov, E. Dinic, M. Kronrod, and749I. Faradzev. 'On Economical Construction of the750Transitive Closure of a Directed Graph.'751Dokl. Akad. Nauk. SSSR No. 194 (in Russian),752English Translation in Soviet Math Dokl. No. 11,7531970.754755[Bard06] G. Bard. 'Accelerating Cryptanalysis with the756Method of Four Russians'. Cryptography E-Print757Archive (http://eprint.iacr.org/2006/251.pdf),7582006.759"""760if self._ncols != right._nrows:761raise ArithmeticError("left ncols must match right nrows")762763if get_verbose() >= 2:764verbose('m4rm multiply of %s x %s matrix by %s x %s matrix'%(765self._nrows, self._ncols, right._nrows, right._ncols))766767cdef Matrix_mod2_dense ans768769ans = self.new_matrix(nrows = self._nrows, ncols = right._ncols)770if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:771return ans772sig_on()773ans._entries = mzd_mul_m4rm(ans._entries, self._entries, right._entries, k)774sig_off()775return ans776777def _multiply_classical(Matrix_mod2_dense self, Matrix_mod2_dense right):778"""779Classical `O(n^3)` multiplication.780781This can be quite fast for matrix vector multiplication but782the other routines fall back to this implementation in that783case anyway.784785EXAMPLE::786787sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )788sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )789sage: A790[0 0 0]791[0 1 0]792[0 1 1]793[0 0 1]794sage: B795[0 0 1 0]796[1 0 0 1]797[1 1 0 0]798sage: A._multiply_classical(B)799[0 0 0 0]800[1 0 0 1]801[0 1 0 1]802[1 1 0 0]803804TESTS:805sage: A = random_matrix(GF(2),0,0)806sage: B = random_matrix(GF(2),0,0)807sage: A._multiply_classical(B)808[]809sage: A = random_matrix(GF(2),3,0)810sage: B = random_matrix(GF(2),0,3)811sage: A._multiply_classical(B)812[0 0 0]813[0 0 0]814[0 0 0]815sage: A = random_matrix(GF(2),0,3)816sage: B = random_matrix(GF(2),3,0)817sage: A._multiply_classical(B)818[]819"""820cdef Matrix_mod2_dense A821A = self.new_matrix(nrows = self._nrows, ncols = right._ncols)822if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:823return A824A._entries = mzd_mul_naive(A._entries, self._entries,(<Matrix_mod2_dense>right)._entries)825return A826827cpdef Matrix_mod2_dense _multiply_strassen(Matrix_mod2_dense self, Matrix_mod2_dense right, int cutoff):828r"""829Strassen-Winograd `O(n^{2.807})` multiplication [Str69].830831This implementation in M4RI is inspired by Sage's generic832Strassen implementation [BHS08] but uses a more memory833efficient operation schedule [DP08].834835The performance of this routine depends on the parameter836cutoff. On many modern machines 2048 should give acceptable837performance, a good rule of thumb for calculating the optimal838cutoff would that two matrices of the cutoff size should fit839in L2 cache, so: `cutoff = \sqrt{L2 * 8 * 1024^2 / 2}` where840`L2` is the size of the L2 cache in MB.841842INPUT:843844- ``right`` - a matrix of matching dimensions.845- ``cutoff`` - matrix dimension where M4RM should be used846instead of Strassen (default: let M4RI decide)847848EXAMPLE::849850sage: A = Matrix(GF(2), 4, 3, [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 0, 1] )851sage: B = Matrix(GF(2), 3, 4, [0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0] )852sage: A853[0 0 0]854[0 1 0]855[0 1 1]856[0 0 1]857sage: B858[0 0 1 0]859[1 0 0 1]860[1 1 0 0]861sage: A._multiply_strassen(B, 0)862[0 0 0 0]863[1 0 0 1]864[0 1 0 1]865[1 1 0 0]866sage: A = random_matrix(GF(2),2701,3000)867sage: B = random_matrix(GF(2),3000,3172)868sage: A._multiply_strassen(B, 256) == A._multiply_m4rm(B, 0)869True870871TESTS:872sage: A = random_matrix(GF(2),0,0)873sage: B = random_matrix(GF(2),0,0)874sage: A._multiply_strassen(B, 0)875[]876sage: A = random_matrix(GF(2),3,0)877sage: B = random_matrix(GF(2),0,3)878sage: A._multiply_strassen(B, 0)879[0 0 0]880[0 0 0]881[0 0 0]882sage: A = random_matrix(GF(2),0,3)883sage: B = random_matrix(GF(2),3,0)884sage: A._multiply_strassen(B, 0)885[]886887ALGORITHM: Uses Strassen-Winograd matrix multiplication with888M4RM as base case as implemented in the M4RI library.889890REFERENCES:891[Str69] Volker Strassen. Gaussian elimination is not892optimal. Numerische Mathematik, 13:354-356, 1969.893894[BHS08] Robert Bradshaw, David Harvey and William895Stein. strassen_window_multiply_c. strassen.pyx,896Sage 3.0, 2008. http://www.sagemath.org897898[DP08] Jean-Guillaume Dumas and Clement Pernet. Memory899efficient scheduling of Strassen-Winograd's matrix900multiplication algorithm. arXiv:0707.2347v1, 2008.901"""902if self._ncols != right._nrows:903raise ArithmeticError("left ncols must match right nrows")904905cdef Matrix_mod2_dense ans906#ans = self.new_matrix(nrows = self._nrows, ncols = right._ncols)907# The following is a little faster:908ans = self.matrix_space(self._nrows, right._ncols, sparse=False).zero_matrix().__copy__()909if self._nrows == 0 or self._ncols == 0 or right._ncols == 0:910return ans911912sig_on()913ans._entries = mzd_mul(ans._entries, self._entries, right._entries, cutoff)914sig_off()915return ans916917def __neg__(self):918"""919EXAMPLES::920921sage: A = random_matrix(GF(2),100,100)922sage: A - A == A - -A923True924"""925return self.__copy__()926927def __invert__(self):928"""929Inverts self using the 'Method of the Four Russians'930inversion.931932If ``self`` is not invertible a ``ZeroDivisionError`` is933raised.934935EXAMPLE::936937sage: A = Matrix(GF(2),3,3, [0, 0, 1, 0, 1, 1, 1, 0, 1])938sage: MS = A.parent()939sage: A940[0 0 1]941[0 1 1]942[1 0 1]943sage: ~A944[1 0 1]945[1 1 0]946[1 0 0]947sage: A * ~A == ~A * A == MS(1)948True949950TESTS:951sage: A = matrix(GF(2),0,0)952sage: A^(-1)953[]954"""955cdef int k = 0956cdef mzd_t *I957cdef Matrix_mod2_dense A958959if self._nrows != self._ncols:960raise ArithmeticError("self must be a square matrix")961962if self._ncols == 0:963return self.__copy__()964965if self.rank() != self._nrows:966raise ZeroDivisionError("Matrix does not have full rank.")967968A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0, alloc = False)969sig_on()970A._entries = mzd_inv_m4ri(NULL, self._entries, 0)971sig_off()972973if A._entries==NULL:974raise ZeroDivisionError("input matrix must be nonsingular")975else:976return A977978def __copy__(self):979"""980Returns a copy of ``self``.981982EXAMPLES::983984sage: MS = MatrixSpace(GF(2),3,3)985sage: A = MS(1)986sage: A.__copy__() == A987True988sage: A.__copy__() is A989False990991sage: A = random_matrix(GF(2),100,100)992sage: A.__copy__() == A993True994sage: A.__copy__() is A995False996997sage: A.echelonize()998sage: A.__copy__() == A999True10001001"""1002cdef Matrix_mod2_dense A1003A = Matrix_mod2_dense.__new__(Matrix_mod2_dense, self._parent, 0, 0, 0)10041005if self._nrows and self._ncols:1006mzd_copy(A._entries, self._entries)10071008if self._subdivisions is not None:1009A.subdivide(*self.subdivisions())10101011return A10121013def _list(self):1014"""1015Returns list of the elements of ``self`` in row major1016ordering.10171018EXAMPLE::10191020sage: A = Matrix(GF(2),2,2,[1,0,1,1])1021sage: A1022[1 0]1023[1 1]1024sage: A.list() #indirect doctest1025[1, 0, 1, 1]10261027TESTS:1028sage: A = Matrix(GF(2),3,0)1029sage: A.list()1030[]1031"""1032cdef int i,j1033l = []1034for i from 0 <= i < self._nrows:1035for j from 0 <= j < self._ncols:1036if mzd_read_bit(self._entries,i,j):1037l.append(self._one)1038else:1039l.append(self._zero)1040return l10411042# def _dict(self):10431044########################################################################1045# LEVEL 3 functionality (Optional)1046# * __deepcopy__1047# * Matrix windows -- only if you need strassen for that base1048########################################################################10491050def echelonize(self, algorithm='heuristic', cutoff=0, reduced=True, **kwds):1051"""1052Puts self in (reduced) row echelon form.10531054INPUT:1055self -- a mutable matrix1056algorithm -- 'heuristic' -- uses M4RI and PLUQ (default)1057'm4ri' -- uses M4RI1058'pluq' -- uses PLUQ factorization1059'classical' -- uses classical Gaussian elimination1060k -- the parameter 'k' of the M4RI algorithm. It MUST be between10611 and 16 (inclusive). If it is not specified it will be calculated as10623/4 * log_2( min(nrows, ncols) ) as suggested in the M4RI paper.1063reduced -- return reduced row echelon form (default:True)10641065EXAMPLE::10661067sage: A = random_matrix(GF(2), 10, 10)1068sage: B = A.__copy__(); B.echelonize() # fastest1069sage: C = A.__copy__(); C.echelonize(k=2) # force k1070sage: E = A.__copy__(); E.echelonize(algorithm='classical') # force Gaussian elimination1071sage: B == C == E1072True10731074TESTS::10751076sage: VF2 = VectorSpace(GF(2),2)1077sage: WF2 = VF2.submodule([VF2([1,1])])1078sage: WF21079Vector space of degree 2 and dimension 1 over Finite Field of size 21080Basis matrix:1081[1 1]10821083sage: A2 = matrix(GF(2),2,[1,0,0,1])1084sage: A2.kernel()1085Vector space of degree 2 and dimension 0 over Finite Field of size 21086Basis matrix:1087[]10881089ALGORITHM: Uses M4RI library10901091REFERENCES:1092[Bard06] G. Bard. 'Accelerating Cryptanalysis with the Method of Four Russians'. Cryptography1093E-Print Archive (http://eprint.iacr.org/2006/251.pdf), 2006.1094"""1095if self._nrows == 0 or self._ncols == 0:1096self.cache('in_echelon_form',True)1097self.cache('rank', 0)1098self.cache('pivots', ())1099return self1100cdef int k, n, full11011102full = int(reduced)11031104x = self.fetch('in_echelon_form')1105if not x is None: return # already known to be in echelon form11061107if algorithm == 'heuristic':11081109self.check_mutability()1110self.clear_cache()11111112sig_on()1113r = mzd_echelonize(self._entries, full)1114sig_off()11151116self.cache('in_echelon_form',True)1117self.cache('rank', r)1118self.cache('pivots', tuple(self._pivots()))11191120elif algorithm == 'm4ri':11211122self.check_mutability()1123self.clear_cache()11241125if 'k' in kwds:1126k = int(kwds['k'])11271128if k<1 or k>16:1129raise RuntimeError("k must be between 1 and 16")1130k = round(k)1131else:1132k = 011331134sig_on()1135r = mzd_echelonize_m4ri(self._entries, full, k)1136sig_off()11371138self.cache('in_echelon_form',True)1139self.cache('rank', r)1140self.cache('pivots', tuple(self._pivots()))114111421143elif algorithm == 'pluq':11441145self.check_mutability()1146self.clear_cache()11471148sig_on()1149r = mzd_echelonize_pluq(self._entries, full)1150sig_off()11511152self.cache('in_echelon_form',True)1153self.cache('rank', r)1154self.cache('pivots', tuple(self._pivots()))11551156elif algorithm == 'linbox':11571158#self._echelonize_linbox()1159raise NotImplementedError11601161elif algorithm == 'classical':11621163# for debugging purposes only, it is slow1164self._echelon_in_place_classical()1165else:1166raise ValueError("no algorithm '%s'"%algorithm)11671168def _pivots(self):1169"""1170Returns the pivot columns of ``self`` if ``self`` is in1171row echelon form.11721173EXAMPLE::11741175sage: A = matrix(GF(2),5,5,[0,1,0,1,0,0,1,0,1,1,0,1,0,1,0,0,0,0,1,0,0,0,1,0,1])1176sage: E = A.echelon_form()1177sage: E1178[0 1 0 0 0]1179[0 0 1 0 0]1180[0 0 0 1 0]1181[0 0 0 0 1]1182[0 0 0 0 0]1183sage: E._pivots()1184[1, 2, 3, 4]1185"""1186if not self.fetch('in_echelon_form'):1187raise RuntimeError("self must be in reduced row echelon form first.")1188pivots = []1189cdef Py_ssize_t i, j, nc1190nc = self._ncols1191i = 01192while i < self._nrows:1193for j from i <= j < nc:1194if mzd_read_bit(self._entries, i, j):1195pivots.append(j)1196i += 11197break1198if j == nc:1199break1200return pivots12011202def randomize(self, density=1, nonzero=False):1203"""1204Randomize ``density`` proportion of the entries of this matrix,1205leaving the rest unchanged.12061207INPUT:12081209- ``density`` - float; proportion (roughly) to be considered for1210changes1211- ``nonzero`` - Bool (default: ``False``); whether the new entries1212are forced to be non-zero12131214OUTPUT:12151216- None, the matrix is modified in-space12171218EXAMPLES::12191220sage: A = matrix(GF(2), 5, 5, 0)1221sage: A.randomize(0.5); A1222[0 0 0 1 1]1223[0 1 0 0 1]1224[1 0 0 0 0]1225[0 1 0 0 0]1226[0 0 0 1 0]1227sage: A.randomize(); A1228[0 0 1 1 0]1229[1 1 0 0 1]1230[1 1 1 1 0]1231[1 1 1 1 1]1232[0 0 1 1 0]12331234TESTS:12351236With the libc random number generator random(), we had problems1237where the ranks of all of these matrices would be the same1238(and they would all be far too low). This verifies that the1239problem is gone, with Mersenne Twister::12401241sage: MS2 = MatrixSpace(GF(2), 1000)1242sage: [MS2.random_element().rank() for i in range(5)]1243[999, 998, 1000, 999, 999]12441245Testing corner case::12461247sage: A = random_matrix(GF(2),3,0)1248sage: A1249[]1250"""1251if self._ncols == 0 or self._nrows == 0:1252return12531254density = float(density)1255if density <= 0:1256return1257if density > 1:1258density = float(1)12591260self.check_mutability()1261self.clear_cache()12621263cdef randstate rstate = current_randstate()12641265cdef int i, j, k1266cdef int nc1267cdef unsigned int low, high1268cdef m4ri_word mask = 012691270# Original code, before adding the ``nonzero`` option.1271if not nonzero:1272if density == 1:1273assert(sizeof(m4ri_word) == 8)1274mask = __M4RI_LEFT_BITMASK(self._entries.ncols % m4ri_radix)1275for i from 0 <= i < self._nrows:1276for j from 0 <= j < self._entries.width:1277# for portability we get 32-bit twice rather than 64-bit once1278low = gmp_urandomb_ui(rstate.gmp_state, 32)1279high = gmp_urandomb_ui(rstate.gmp_state, 32)1280self._entries.rows[i][j] = m4ri_swap_bits( ((<unsigned long long>high)<<32) | (<unsigned long long>low) )1281self._entries.rows[i][self._entries.width - 1] &= mask1282else:1283nc = self._ncols1284num_per_row = int(density * nc)1285sig_on()1286for i from 0 <= i < self._nrows:1287for j from 0 <= j < num_per_row:1288k = rstate.c_random()%nc1289mzd_write_bit(self._entries, i, k, rstate.c_random() % 2)1290sig_off()12911292# New code for the case when ``nonzero`` is ``True``.1293else:1294sig_on()1295for i from 0 <= i < self._nrows:1296for j from 0 <= j < self._ncols:1297if rstate.c_rand_double() <= density:1298mzd_write_bit(self._entries, i, j, 1)1299sig_off()13001301cdef rescale_row_c(self, Py_ssize_t row, multiple, Py_ssize_t start_col):1302"""1303EXAMPLE::13041305sage: A = random_matrix(GF(2),3,3); A1306[0 1 0]1307[0 1 1]1308[0 0 0]13091310sage: A.rescale_row(0,0,0); A1311[0 0 0]1312[0 1 1]1313[0 0 0]1314"""1315if (int(multiple)%2) == 0:1316mzd_row_clear_offset(self._entries, row, start_col);13171318cdef add_multiple_of_row_c(self, Py_ssize_t row_to, Py_ssize_t row_from, multiple,1319Py_ssize_t start_col):1320"""1321EXAMPLE::13221323sage: A = random_matrix(GF(2),3,3); A1324[0 1 0]1325[0 1 1]1326[0 0 0]1327sage: A.add_multiple_of_row(0,1,1,0); A1328[0 0 1]1329[0 1 1]1330[0 0 0]1331"""1332if (int(multiple)%2) != 0:1333mzd_row_add_offset(self._entries, row_to, row_from, start_col)13341335cdef swap_rows_c(self, Py_ssize_t row1, Py_ssize_t row2):1336"""1337EXAMPLE::13381339sage: A = random_matrix(GF(2),3,3); A1340[0 1 0]1341[0 1 1]1342[0 0 0]1343sage: A.swap_rows(0,1); A1344[0 1 1]1345[0 1 0]1346[0 0 0]1347"""1348mzd_row_swap(self._entries, row1, row2)13491350cdef swap_columns_c(self, Py_ssize_t col1, Py_ssize_t col2):1351"""1352EXAMPLE::13531354sage: A = random_matrix(GF(2),3,3); A1355[0 1 0]1356[0 1 1]1357[0 0 0]13581359sage: A.swap_columns(0,1); A1360[1 0 0]1361[1 0 1]1362[0 0 0]13631364sage: A = random_matrix(GF(2),3,65)13651366sage: B = A.__copy__()1367sage: B.swap_columns(0,1)1368sage: B.swap_columns(0,1)1369sage: A == B1370True13711372sage: A.swap_columns(0,64)1373sage: A.column(0) == B.column(64)1374True1375sage: A.swap_columns(63,64)1376sage: A.column(63) == B.column(0)1377True1378"""1379mzd_col_swap(self._entries, col1, col2)1380138113821383def _magma_init_(self, magma):1384"""1385Returns a string of self in ``Magma`` form. Does not return1386``Magma`` object but string.13871388EXAMPLE::13891390sage: A = random_matrix(GF(2),3,3)1391sage: A._magma_init_(magma) # optional - magma1392'Matrix(GF(2),3,3,StringToIntegerSequence("0 1 0 0 1 1 0 0 0"))'1393sage: A = random_matrix(GF(2),100,100)1394sage: B = random_matrix(GF(2),100,100)1395sage: magma(A*B) == magma(A) * magma(B) # optional - magma1396True13971398TESTS::13991400sage: A = random_matrix(GF(2),0,3)1401sage: magma(A) # optional - magma1402Matrix with 0 rows and 3 columns1403sage: A = matrix(GF(2),2,3,[0,1,1,1,0,0])1404sage: A._magma_init_(magma) # optional - magma1405'Matrix(GF(2),2,3,StringToIntegerSequence("0 1 1 1 0 0"))'1406sage: magma(A) # optional - magma1407[0 1 1]1408[1 0 0]1409"""1410s = self.base_ring()._magma_init_(magma)1411return 'Matrix(%s,%s,%s,StringToIntegerSequence("%s"))'%(1412s, self._nrows, self._ncols, self._export_as_string())14131414def determinant(self):1415"""1416Return the determinant of this matrix over GF(2).14171418EXAMPLES::14191420sage: matrix(GF(2),2,[1,1,0,1]).determinant()142111422sage: matrix(GF(2),2,[1,1,1,1]).determinant()142301424"""1425if not self.is_square():1426raise ValueError("self must be a square matrix")1427return self.base_ring()(1 if self.rank() == self.nrows() else 0)14281429def transpose(self):1430"""1431Returns transpose of self and leaves self untouched.14321433EXAMPLE::14341435sage: A = Matrix(GF(2),3,5,[1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 1, 1, 0, 1, 0])1436sage: A1437[1 0 1 0 0]1438[0 1 1 0 0]1439[1 1 0 1 0]1440sage: B = A.transpose(); B1441[1 0 1]1442[0 1 1]1443[1 1 0]1444[0 0 1]1445[0 0 0]1446sage: B.transpose() == A1447True14481449``.T`` is a convenient shortcut for the transpose::14501451sage: A.T1452[1 0 1]1453[0 1 1]1454[1 1 0]1455[0 0 1]1456[0 0 0]14571458TESTS:1459sage: A = random_matrix(GF(2),0,40)1460sage: A.transpose()146140 x 0 dense matrix over Finite Field of size 214621463sage: A = Matrix(GF(2), [1,0])1464sage: B = A.transpose()1465sage: A[0,0] = 01466sage: B[0,0]146711468"""1469cdef Matrix_mod2_dense A = self.new_matrix(ncols = self._nrows, nrows = self._ncols)1470if self._nrows == 0 or self._ncols == 0:1471return A14721473A._entries = mzd_transpose(A._entries, self._entries)1474if self._subdivisions is not None:1475A.subdivide(*self.subdivisions())1476return A14771478cdef int _cmp_c_impl(self, Element right) except -2:1479if self._nrows == 0 or self._ncols == 0:1480return 01481return mzd_cmp(self._entries, (<Matrix_mod2_dense>right)._entries)148214831484def augment(self, right, subdivide=False):1485r"""1486Augments ``self`` with ``right``.14871488EXAMPLE::14891490sage: MS = MatrixSpace(GF(2),3,3)1491sage: A = MS([0, 1, 0, 1, 1, 0, 1, 1, 1]); A1492[0 1 0]1493[1 1 0]1494[1 1 1]1495sage: B = A.augment(MS(1)); B1496[0 1 0 1 0 0]1497[1 1 0 0 1 0]1498[1 1 1 0 0 1]1499sage: B.echelonize(); B1500[1 0 0 1 1 0]1501[0 1 0 1 0 0]1502[0 0 1 0 1 1]1503sage: C = B.matrix_from_columns([3,4,5]); C1504[1 1 0]1505[1 0 0]1506[0 1 1]1507sage: C == ~A1508True1509sage: C*A == MS(1)1510True15111512A vector may be augmented to a matrix. ::15131514sage: A = matrix(GF(2), 3, 4, range(12))1515sage: v = vector(GF(2), 3, range(3))1516sage: A.augment(v)1517[0 1 0 1 0]1518[0 1 0 1 1]1519[0 1 0 1 0]15201521The ``subdivide`` option will add a natural subdivision between1522``self`` and ``right``. For more details about how subdivisions1523are managed when augmenting, see1524:meth:`sage.matrix.matrix1.Matrix.augment`. ::15251526sage: A = matrix(GF(2), 3, 5, range(15))1527sage: B = matrix(GF(2), 3, 3, range(9))1528sage: A.augment(B, subdivide=True)1529[0 1 0 1 0|0 1 0]1530[1 0 1 0 1|1 0 1]1531[0 1 0 1 0|0 1 0]15321533TESTS::15341535sage: A = random_matrix(GF(2),2,3)1536sage: B = random_matrix(GF(2),2,0)1537sage: A.augment(B)1538[0 1 0]1539[0 1 1]15401541sage: B.augment(A)1542[0 1 0]1543[0 1 1]15441545sage: M = Matrix(GF(2), 0, 0, 0)1546sage: N = Matrix(GF(2), 0, 19, 0)1547sage: W = M.augment(N)1548sage: W.ncols()1549191550sage: M = Matrix(GF(2), 0, 1, 0)1551sage: N = Matrix(GF(2), 0, 1, 0)1552sage: M.augment(N)1553[]1554"""1555if hasattr(right, '_vector_'):1556right = right.column()15571558cdef Matrix_mod2_dense other = right15591560if self._nrows != other._nrows:1561raise TypeError("Both numbers of rows must match.")15621563if self._ncols == 0:1564return other.__copy__()1565if other._ncols == 0:1566return self.__copy__()15671568cdef Matrix_mod2_dense Z1569Z = self.new_matrix(ncols = self._ncols + other._ncols)1570if self._nrows == 0:1571return Z1572Z._entries = mzd_concat(Z._entries, self._entries, other._entries)1573if subdivide:1574Z._subdivide_on_augment(self, other)1575return Z15761577def stack(self, bottom, subdivide=False):1578r"""1579Stack ``self`` on top of ``bottom``.15801581EXAMPLE::15821583sage: A = matrix(GF(2),2,2,[1,0,0,1])1584sage: B = matrix(GF(2),2,2,[0,1,1,0])1585sage: A.stack(B)1586[1 0]1587[0 1]1588[0 1]1589[1 0]1590sage: B.stack(A)1591[0 1]1592[1 0]1593[1 0]1594[0 1]15951596A vector may be stacked below a matrix. ::15971598sage: A = matrix(GF(2), 2, 5, range(10))1599sage: v = vector(GF(2), 5, range(5))1600sage: A.stack(v)1601[0 1 0 1 0]1602[1 0 1 0 1]1603[0 1 0 1 0]16041605The ``subdivide`` option will add a natural subdivision between1606``self`` and ``bottom``. For more details about how subdivisions1607are managed when stacking, see1608:meth:`sage.matrix.matrix1.Matrix.stack`. ::16091610sage: A = matrix(GF(2), 3, 5, range(15))1611sage: B = matrix(GF(2), 1, 5, range(5))1612sage: A.stack(B, subdivide=True)1613[0 1 0 1 0]1614[1 0 1 0 1]1615[0 1 0 1 0]1616[---------]1617[0 1 0 1 0]16181619TESTS::16201621sage: A = random_matrix(GF(2),0,3)1622sage: B = random_matrix(GF(2),3,3)1623sage: A.stack(B)1624[0 1 0]1625[0 1 1]1626[0 0 0]16271628sage: B.stack(A)1629[0 1 0]1630[0 1 1]1631[0 0 0]16321633sage: M = Matrix(GF(2), 0, 0, 0)1634sage: N = Matrix(GF(2), 19, 0, 0)1635sage: W = M.stack(N)1636sage: W.nrows()1637191638sage: M = Matrix(GF(2), 1, 0, 0)1639sage: N = Matrix(GF(2), 1, 0, 0)1640sage: M.stack(N)1641[]1642"""1643if hasattr(bottom, '_vector_'):1644bottom = bottom.row()1645cdef Matrix_mod2_dense other = bottom16461647if self._ncols != other._ncols:1648raise TypeError("Both numbers of columns must match.")16491650if self._nrows == 0:1651return other.__copy__()1652if other._nrows == 0:1653return self.__copy__()16541655cdef Matrix_mod2_dense Z1656Z = self.new_matrix(nrows = self._nrows + other._nrows)1657if self._ncols == 0:1658return Z1659Z._entries = mzd_stack(Z._entries, self._entries, other._entries)1660if subdivide:1661Z._subdivide_on_stack(self, other)1662return Z16631664def submatrix(self, lowr, lowc, nrows , ncols):1665"""1666Return submatrix from the index lowr,lowc (inclusive) with1667dimension nrows x ncols.16681669INPUT:1670lowr -- index of start row1671lowc -- index of start column1672nrows -- number of rows of submatrix1673ncols -- number of columns of submatrix16741675EXAMPLES::16761677sage: A = random_matrix(GF(2),200,200)1678sage: A[0:2,0:2] == A.submatrix(0,0,2,2)1679True1680sage: A[0:100,0:100] == A.submatrix(0,0,100,100)1681True1682sage: A == A.submatrix(0,0,200,200)1683True16841685sage: A[1:3,1:3] == A.submatrix(1,1,2,2)1686True1687sage: A[1:100,1:100] == A.submatrix(1,1,99,99)1688True1689sage: A[1:200,1:200] == A.submatrix(1,1,199,199)1690True1691"""1692cdef Matrix_mod2_dense A16931694cdef int highr, highc16951696highr = lowr + nrows1697highc = lowc + ncols16981699if nrows <= 0 or ncols <= 0:1700raise TypeError("Expected nrows, ncols to be > 0, but got %d,%d instead."%(nrows, ncols))17011702if highc > self._entries.ncols:1703raise TypeError("Expected highc <= self.ncols(), but got %d > %d instead."%(highc, self._entries.ncols))17041705if highr > self._entries.nrows:1706raise TypeError("Expected highr <= self.nrows(), but got %d > %d instead."%(highr, self._entries.nrows))17071708if lowr < 0:1709raise TypeError("Expected lowr >= 0, but got %d instead."%lowr)17101711if lowc < 0:1712raise TypeError("Expected lowc >= 0, but got %d instead."%lowc)17131714A = self.new_matrix(nrows = nrows, ncols = ncols)1715if self._ncols == 0 or self._nrows == 0:1716return A1717A._entries = mzd_submatrix(A._entries, self._entries, lowr, lowc, highr, highc)1718return A17191720def __reduce__(self):1721"""1722Serialize ``self``.17231724EXAMPLE::17251726sage: A = random_matrix(GF(2),10,10)1727sage: f,s = A.__reduce__()1728sage: f(*s) == A1729True1730"""1731cdef int i,j, r,c, size17321733r, c = self.nrows(), self.ncols()1734if r == 0 or c == 0:1735return unpickle_matrix_mod2_dense_v1, (r, c, None, 0)17361737sig_on()1738cdef gdImagePtr im = gdImageCreate(c, r)1739sig_off()1740cdef int black = gdImageColorAllocate(im, 0, 0, 0)1741cdef int white = gdImageColorAllocate(im, 255, 255, 255)1742gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)1743for i from 0 <= i < r:1744for j from 0 <= j < c:1745if mzd_read_bit(self._entries, i, j):1746gdImageSetPixel(im, j, i, black )17471748cdef signed char *buf = <signed char*>gdImagePngPtr(im, &size)17491750data = [buf[i] for i in range(size)]1751gdFree(buf)1752gdImageDestroy(im)1753return unpickle_matrix_mod2_dense_v1, (r,c, data, size)17541755cpdef _export_as_string(self):1756"""1757Return space separated string of the entries in this matrix.17581759EXAMPLES:1760sage: w = matrix(GF(2),2,3,[1,0,1,1,1,0])1761sage: w._export_as_string()1762'1 0 1 1 1 0'1763"""1764cdef Py_ssize_t i, j, k, n1765cdef char *s, *t17661767if self._nrows == 0 or self._ncols == 0:1768data = ''1769else:1770n = self._nrows*self._ncols*2 + 21771s = <char*> sage_malloc(n * sizeof(char))1772k = 01773sig_on()1774for i in range(self._nrows):1775for j in range(self._ncols):1776s[k] = <char>(48 + (1 if mzd_read_bit(self._entries,i,j) else 0)) # "0" or "1"1777k += 11778s[k] = <char>32 # space1779k += 11780sig_off()1781s[k-1] = <char>01782data = str(s)1783sage_free(s)1784return data17851786def density(self, approx=False):1787"""1788Return the density of this matrix.17891790By density we understand the ration of the number of nonzero1791positions and the self.nrows() * self.ncols(), i.e. the number1792of possible nonzero positions.17931794INPUT:1795approx -- return floating point approximation (default: False)17961797EXAMPLE::17981799sage: A = random_matrix(GF(2),1000,1000)1800sage: d = A.density(); d180162483/12500018021803sage: float(d)18040.49986418051806sage: A.density(approx=True)18070.499864000...18081809sage: float(len(A.nonzero_positions())/1000^2)18100.4998641811"""1812if approx:1813from sage.rings.real_mpfr import create_RealNumber1814return create_RealNumber(mzd_density(self._entries, 1))1815else:1816return matrix_dense.Matrix_dense.density(self)18171818def rank(self, algorithm='ple'):1819"""1820Return the rank of this matrix.18211822On average 'ple' should be faster than 'm4ri' and hence it is1823the default choice. However, for small - i.e. quite few1824thousand rows & columns - and sparse matrices 'm4ri' might be1825a better choice.18261827INPUT:18281829- ``algorithm`` - either "ple" or "m4ri"18301831EXAMPLE::18321833sage: A = random_matrix(GF(2), 1000, 1000)1834sage: A.rank()183599918361837sage: A = matrix(GF(2),10, 0)1838sage: A.rank()183901840"""1841x = self.fetch('rank')1842if not x is None:1843return x1844if self._nrows == 0 or self._ncols == 0:1845return 01846cdef mzd_t *A = mzd_copy(NULL, self._entries)1847cdef mzp_t *P, *Q18481849if algorithm == 'pls':1850from sage.misc.superseded import deprecation1851deprecation(12840, "Parameter 'pls' is deprecated, use 'ple' instead.")1852P = mzp_init(self._entries.nrows)1853Q = mzp_init(self._entries.ncols)1854r = mzd_ple(A, P, Q, 0)1855mzp_free(P)1856mzp_free(Q)1857elif algorithm == 'ple':1858P = mzp_init(self._entries.nrows)1859Q = mzp_init(self._entries.ncols)1860r = mzd_ple(A, P, Q, 0)1861mzp_free(P)1862mzp_free(Q)1863elif algorithm == 'm4ri':1864r = mzd_echelonize_m4ri(A, 0, 0)1865else:1866raise ValueError("Algorithm '%s' unknown."%algorithm)1867mzd_free(A)1868self.cache('rank', r)1869return r18701871def _right_kernel_matrix(self, **kwds):1872r"""1873Returns a pair that includes a matrix of basis vectors1874for the right kernel of ``self``.18751876INPUT:18771878- ``kwds`` - these are provided for consistency with other versions1879of this method. Here they are ignored as there is no optional1880behavior available.18811882OUTPUT:18831884Returns a pair. First item is the string 'computed-pluq'1885that identifies the nature of the basis vectors.18861887Second item is a matrix whose rows are a basis for the right kernel,1888over the integers mod 2, as computed by the M4RI library1889using PLUQ matrix decomposition.18901891EXAMPLES::18921893sage: A = matrix(GF(2), [1894... [0, 1, 0, 0, 1, 0, 1, 1],1895... [1, 0, 1, 0, 0, 1, 1, 0],1896... [0, 0, 1, 0, 0, 1, 0, 1],1897... [1, 0, 1, 1, 0, 1, 1, 0],1898... [0, 0, 1, 0, 0, 1, 0, 1],1899... [1, 1, 0, 1, 1, 0, 0, 0]])1900sage: A1901[0 1 0 0 1 0 1 1]1902[1 0 1 0 0 1 1 0]1903[0 0 1 0 0 1 0 1]1904[1 0 1 1 0 1 1 0]1905[0 0 1 0 0 1 0 1]1906[1 1 0 1 1 0 0 0]1907sage: result = A._right_kernel_matrix()1908sage: result[0]1909'computed-pluq'1910sage: result[1]1911[0 1 0 0 1 0 0 0]1912[0 0 1 0 0 1 0 0]1913[1 1 0 0 0 0 1 0]1914[1 1 1 0 0 0 0 1]1915sage: X = result[1].transpose()1916sage: A*X == zero_matrix(GF(2), 6, 4)1917True19181919TESTS:19201921We test the three trivial cases. Matrices with no rows or columns will1922cause segfaults in the M4RI code, so we protect against that instance.1923Computing a kernel or a right kernel matrix should never pass these1924problem matrices here. ::19251926sage: A = matrix(GF(2), 0, 2)1927sage: A._right_kernel_matrix()1928Traceback (most recent call last):1929...1930ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed1931sage: A = matrix(GF(2), 2, 0)1932sage: A._right_kernel_matrix()1933Traceback (most recent call last):1934...1935ValueError: kernels of matrices mod 2 with zero rows or zero columns cannot be computed1936sage: A = zero_matrix(GF(2), 4, 3)1937sage: A._right_kernel_matrix()[1]1938[1 0 0]1939[0 1 0]1940[0 0 1]1941"""1942tm = verbose("computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1)1943if self.nrows()==0 or self.ncols()==0:1944raise ValueError("kernels of matrices mod 2 with zero rows or zero columns cannot be computed")1945cdef Matrix_mod2_dense M1946cdef mzd_t *A = mzd_copy(NULL, self._entries)1947# Despite the name, this next call returns X such that M*X = 01948cdef mzd_t *k = mzd_kernel_left_pluq(A, 0)1949mzd_free(A)1950if k != NULL:1951M = self.new_matrix(nrows = k.ncols, ncols = k.nrows)1952mzd_transpose(M._entries, k)1953mzd_free(k)1954else:1955M = self.new_matrix(nrows = 0, ncols = self._ncols)1956verbose("done computing right kernel matrix over integers mod 2 for %sx%s matrix" % (self.nrows(), self.ncols()),level=1, t=tm)1957return 'computed-pluq', M19581959# Used for hashing1960cdef int i, k1961cdef unsigned long parity_table[256]1962for i from 0 <= i < 256:1963parity_table[i] = 1 & ((i) ^ (i>>1) ^ (i>>2) ^ (i>>3) ^1964(i>>4) ^ (i>>5) ^ (i>>6) ^ (i>>7))19651966# gmp's ULONG_PARITY may use special1967# assembly instructions, could be faster1968cpdef inline unsigned long parity(m4ri_word a):1969"""1970Returns the parity of the number of bits in a.19711972EXAMPLES::19731974sage: from sage.matrix.matrix_mod2_dense import parity1975sage: parity(1)19761L1977sage: parity(3)19780L1979sage: parity(0x10000101011)19801L1981"""1982if sizeof(m4ri_word) == 8:1983a ^= a >> 321984a ^= a >> 161985a ^= a >> 81986return parity_table[a & 0xFF]19871988cdef inline unsigned long parity_mask(m4ri_word a):1989return -parity(a)199019911992def unpickle_matrix_mod2_dense_v1(r, c, data, size):1993"""1994Deserialize a matrix encoded in the string ``s``.19951996INPUT:1997r -- number of rows of matrix1998c -- number of columns of matrix1999s -- a string2000size -- length of the string s20012002EXAMPLE::20032004sage: A = random_matrix(GF(2),100,101)2005sage: _,(r,c,s,s2) = A.__reduce__()2006sage: from sage.matrix.matrix_mod2_dense import unpickle_matrix_mod2_dense_v12007sage: unpickle_matrix_mod2_dense_v1(r,c,s,s2) == A2008True2009sage: loads(dumps(A)) == A2010True2011"""2012from sage.matrix.constructor import Matrix2013from sage.rings.finite_rings.constructor import FiniteField as GF20142015cdef int i, j2016cdef Matrix_mod2_dense A20172018A = <Matrix_mod2_dense>Matrix(GF(2),r,c)2019if r == 0 or c == 0:2020return A20212022cdef signed char *buf = <signed char*>sage_malloc(size)2023for i from 0 <= i < size:2024buf[i] = data[i]20252026sig_on()2027cdef gdImagePtr im = gdImageCreateFromPngPtr(size, buf)2028sig_off()20292030sage_free(buf)20312032if gdImageSX(im) != c or gdImageSY(im) != r:2033raise TypeError("Pickled data dimension doesn't match.")203420352036for i from 0 <= i < r:2037for j from 0 <= j < c:2038mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))2039gdImageDestroy(im)2040return A20412042def from_png(filename):2043"""2044Returns a dense matrix over GF(2) from a 1-bit PNG image read from2045``filename``. No attempt is made to verify that the filename string2046actually points to a PNG image.20472048INPUT:2049filename -- a string20502051EXAMPLE::20522053sage: from sage.matrix.matrix_mod2_dense import from_png, to_png2054sage: A = random_matrix(GF(2),10,10)2055sage: fn = tmp_filename()2056sage: to_png(A, fn)2057sage: B = from_png(fn)2058sage: A == B2059True2060"""2061from sage.matrix.constructor import Matrix2062from sage.rings.finite_rings.constructor import FiniteField as GF20632064cdef int i,j,r,c2065cdef Matrix_mod2_dense A20662067fn = open(filename,"r") # check filename2068fn.close()20692070cdef FILE *f = fopen(filename, "rb")2071sig_on()2072cdef gdImagePtr im = gdImageCreateFromPng(f)2073sig_off()20742075c, r = gdImageSX(im), gdImageSY(im)20762077A = <Matrix_mod2_dense>Matrix(GF(2),r,c)20782079for i from 0 <= i < r:2080for j from 0 <= j < c:2081mzd_write_bit(A._entries, i, j, 1-gdImageGetPixel(im, j, i))2082fclose(f)2083gdImageDestroy(im)2084return A20852086def to_png(Matrix_mod2_dense A, filename):2087"""2088Saves the matrix ``A`` to filename as a 1-bit PNG image.20892090INPUT:20912092- ``A`` - a matrix over GF(2)2093- ``filename`` - a string for a file in a writable position20942095EXAMPLE::20962097sage: from sage.matrix.matrix_mod2_dense import from_png, to_png2098sage: A = random_matrix(GF(2),10,10)2099sage: fn = tmp_filename()2100sage: to_png(A, fn)2101sage: B = from_png(fn)2102sage: A == B2103True2104"""2105cdef int i,j, r,c2106r, c = A.nrows(), A.ncols()2107if r == 0 or c == 0:2108raise TypeError("Cannot write image with dimensions %d x %d"%(c,r))2109fn = open(filename,"w") # check filename2110fn.close()2111cdef gdImagePtr im = gdImageCreate(c, r)2112cdef FILE * out = fopen(filename, "wb")2113cdef int black = gdImageColorAllocate(im, 0, 0, 0)2114cdef int white = gdImageColorAllocate(im, 255, 255, 255)2115gdImageFilledRectangle(im, 0, 0, c-1, r-1, white)2116for i from 0 <= i < r:2117for j from 0 <= j < c:2118if mzd_read_bit(A._entries, i, j):2119gdImageSetPixel(im, j, i, black )21202121gdImagePng(im, out)2122gdImageDestroy(im)2123fclose(out)21242125def pluq(Matrix_mod2_dense A, algorithm="standard", int param=0):2126"""2127Return PLUQ factorization of A.21282129INPUT:2130A -- matrix2131algorithm -- 'standard' asymptotically fast (default)2132'mmpf' M4RI inspired2133'naive' naive cubic2134param -- either k for 'mmpf' is chosen or matrix multiplication2135cutoff for 'standard' (default: 0)21362137EXAMPLE::21382139sage: from sage.matrix.matrix_mod2_dense import pluq2140sage: A = random_matrix(GF(2),4,4); A2141[0 1 0 1]2142[0 1 1 1]2143[0 0 0 1]2144[0 1 1 0]21452146sage: LU, P, Q = pluq(A)2147sage: LU2148[1 0 1 0]2149[1 1 0 0]2150[0 0 1 0]2151[1 1 1 0]21522153sage: P2154[0, 1, 2, 3]21552156sage: Q2157[1, 2, 3, 3]2158"""2159cdef Matrix_mod2_dense B = A.__copy__()2160cdef mzp_t *p = mzp_init(A._entries.nrows)2161cdef mzp_t *q = mzp_init(A._entries.ncols)21622163if algorithm == "standard":2164sig_on()2165mzd_pluq(B._entries, p, q, param)2166sig_off()2167elif algorithm == "mmpf":2168sig_on()2169_mzd_pluq_russian(B._entries, p, q, param)2170sig_off()2171elif algorithm == "naive":2172sig_on()2173_mzd_pluq_naive(B._entries, p, q)2174sig_off()2175else:2176raise ValueError("Algorithm '%s' unknown."%algorithm)21772178P = [p.values[i] for i in range(A.nrows())]2179Q = [q.values[i] for i in range(A.ncols())]2180mzp_free(p)2181mzp_free(q)2182return B,P,Q21832184def ple(Matrix_mod2_dense A, algorithm="standard", int param=0):2185"""2186Return PLE factorization of A.21872188INPUT:2189A -- matrix2190algorithm -- 'standard' asymptotically fast (default)2191'russian' M4RI inspired2192'naive' naive cubic2193param -- either k for 'mmpf' is chosen or matrix multiplication2194cutoff for 'standard' (default: 0)21952196EXAMPLE::21972198sage: from sage.matrix.matrix_mod2_dense import ple2199sage: A = random_matrix(GF(2),4,4); A2200[0 1 0 1]2201[0 1 1 1]2202[0 0 0 1]2203[0 1 1 0]22042205sage: LU, P, Q = ple(A)2206sage: LU2207[1 0 0 1]2208[1 1 0 0]2209[0 0 1 0]2210[1 1 1 0]22112212sage: P2213[0, 1, 2, 3]22142215sage: Q2216[1, 2, 3, 3]22172218sage: A = random_matrix(GF(2),1000,1000)2219sage: ple(A) == ple(A,'russian') == ple(A,'naive')2220True2221"""2222cdef Matrix_mod2_dense B = A.__copy__()2223cdef mzp_t *p = mzp_init(A._entries.nrows)2224cdef mzp_t *q = mzp_init(A._entries.ncols)22252226if algorithm == 'standard':2227sig_on()2228mzd_ple(B._entries, p, q, param)2229sig_off()22302231elif algorithm == "mmpf":2232from sage.misc.superseded import deprecation2233deprecation(12840, "Parameter 'mmpf' is deprecated, use 'russian' instead.")2234sig_on()2235_mzd_ple_russian(B._entries, p, q, param)2236sig_off()2237elif algorithm == "russian":2238sig_on()2239_mzd_ple_russian(B._entries, p, q, param)2240sig_off()2241elif algorithm == "naive":2242sig_on()2243_mzd_ple_naive(B._entries, p, q)2244sig_off()2245else:2246raise ValueError("Algorithm '%s' unknown."%algorithm)22472248P = [p.values[i] for i in range(A.nrows())]2249Q = [q.values[i] for i in range(A.ncols())]2250mzp_free(p)2251mzp_free(q)2252return B,P,Q225322542255