| 1 | """ |
|---|
| 2 | Dense matrices over the integer ring. |
|---|
| 3 | |
|---|
| 4 | AUTHORS: |
|---|
| 5 | -- William Stein |
|---|
| 6 | -- Robert Bradshaw |
|---|
| 7 | |
|---|
| 8 | EXAMPLES: |
|---|
| 9 | sage: a = matrix(ZZ, 3,3, range(9)); a |
|---|
| 10 | [0 1 2] |
|---|
| 11 | [3 4 5] |
|---|
| 12 | [6 7 8] |
|---|
| 13 | sage: a.det() |
|---|
| 14 | 0 |
|---|
| 15 | sage: a[0,0] = 10; a.det() |
|---|
| 16 | -30 |
|---|
| 17 | sage: a.charpoly() |
|---|
| 18 | x^3 - 22*x^2 + 102*x + 30 |
|---|
| 19 | sage: b = -3*a |
|---|
| 20 | sage: a == b |
|---|
| 21 | False |
|---|
| 22 | sage: b < a |
|---|
| 23 | True |
|---|
| 24 | """ |
|---|
| 25 | |
|---|
| 26 | ###################################################################### |
|---|
| 27 | # Copyright (C) 2006,2007 William Stein |
|---|
| 28 | # |
|---|
| 29 | # Distributed under the terms of the GNU General Public License (GPL) |
|---|
| 30 | # |
|---|
| 31 | # The full text of the GPL is available at: |
|---|
| 32 | # http://www.gnu.org/licenses/ |
|---|
| 33 | ###################################################################### |
|---|
| 34 | |
|---|
| 35 | from sage.modules.vector_integer_dense cimport Vector_integer_dense |
|---|
| 36 | |
|---|
| 37 | from sage.misc.misc import verbose, get_verbose, cputime |
|---|
| 38 | |
|---|
| 39 | include "../ext/interrupt.pxi" |
|---|
| 40 | include "../ext/stdsage.pxi" |
|---|
| 41 | include "../ext/gmp.pxi" |
|---|
| 42 | |
|---|
| 43 | ctypedef unsigned int uint |
|---|
| 44 | |
|---|
| 45 | from sage.ext.multi_modular import MultiModularBasis |
|---|
| 46 | from sage.ext.multi_modular cimport MultiModularBasis |
|---|
| 47 | |
|---|
| 48 | from sage.rings.integer cimport Integer |
|---|
| 49 | from sage.rings.rational_field import QQ |
|---|
| 50 | from sage.rings.integer_ring import ZZ |
|---|
| 51 | from sage.rings.integer_mod_ring import IntegerModRing |
|---|
| 52 | from sage.rings.polynomial_ring import PolynomialRing |
|---|
| 53 | from sage.structure.element cimport ModuleElement, RingElement, Element, Vector |
|---|
| 54 | |
|---|
| 55 | from matrix_modn_dense import Matrix_modn_dense |
|---|
| 56 | from matrix_modn_dense cimport Matrix_modn_dense |
|---|
| 57 | |
|---|
| 58 | import sage.modules.free_module |
|---|
| 59 | import sage.modules.free_module_element |
|---|
| 60 | |
|---|
| 61 | from matrix cimport Matrix |
|---|
| 62 | |
|---|
| 63 | cimport sage.structure.element |
|---|
| 64 | |
|---|
| 65 | import matrix_space |
|---|
| 66 | |
|---|
| 67 | from sage.libs.linbox.linbox cimport Linbox_integer_dense |
|---|
| 68 | cdef Linbox_integer_dense linbox |
|---|
| 69 | linbox = Linbox_integer_dense() |
|---|
| 70 | |
|---|
| 71 | #import sage.misc.misc |
|---|
| 72 | #USE_LINBOX_POLY = not sage.misc.misc.is_64bit() |
|---|
| 73 | |
|---|
| 74 | USE_LINBOX_POLY = True |
|---|
| 75 | |
|---|
| 76 | cdef class Matrix_integer_dense(matrix_dense.Matrix_dense): # dense or sparse |
|---|
| 77 | r""" |
|---|
| 78 | Matrix over the integers. |
|---|
| 79 | |
|---|
| 80 | On a 32-bit machine, they can have at most $2^{32}-1$ rows or |
|---|
| 81 | columns. On a 64-bit machine, matrices can have at most |
|---|
| 82 | $2^{64}-1$ rows or columns. |
|---|
| 83 | |
|---|
| 84 | EXAMPLES: |
|---|
| 85 | sage: a = MatrixSpace(ZZ,3)(2); a |
|---|
| 86 | [2 0 0] |
|---|
| 87 | [0 2 0] |
|---|
| 88 | [0 0 2] |
|---|
| 89 | sage: a = matrix(ZZ,1,3, [1,2,-3]); a |
|---|
| 90 | [ 1 2 -3] |
|---|
| 91 | sage: a = MatrixSpace(ZZ,2,4)(2); a |
|---|
| 92 | Traceback (most recent call last): |
|---|
| 93 | ... |
|---|
| 94 | TypeError: nonzero scalar matrix must be square |
|---|
| 95 | """ |
|---|
| 96 | ######################################################################## |
|---|
| 97 | # LEVEL 1 functionality |
|---|
| 98 | # x * __new__ |
|---|
| 99 | # x * __dealloc__ |
|---|
| 100 | # x * __init__ |
|---|
| 101 | # x * set_unsafe |
|---|
| 102 | # x * get_unsafe |
|---|
| 103 | # x * def _pickle |
|---|
| 104 | # x * def _unpickle |
|---|
| 105 | ######################################################################## |
|---|
| 106 | |
|---|
| 107 | def __new__(self, parent, entries, coerce, copy): |
|---|
| 108 | """ |
|---|
| 109 | Create and allocate memory for the matrix. Does not actually initialize |
|---|
| 110 | any of the memory. |
|---|
| 111 | |
|---|
| 112 | INPUT: |
|---|
| 113 | parent, entries, coerce, copy -- as for __init__. |
|---|
| 114 | |
|---|
| 115 | EXAMPLES: |
|---|
| 116 | sage: from sage.matrix.matrix_integer_dense import Matrix_integer_dense |
|---|
| 117 | sage: a = Matrix_integer_dense.__new__(Matrix_integer_dense, Mat(ZZ,3), 0,0,0) |
|---|
| 118 | sage: type(a) |
|---|
| 119 | <type 'sage.matrix.matrix_integer_dense.Matrix_integer_dense'> |
|---|
| 120 | |
|---|
| 121 | WARNING: This is for internal use only, or if you really know what you're doing. |
|---|
| 122 | """ |
|---|
| 123 | matrix_dense.Matrix_dense.__init__(self, parent) |
|---|
| 124 | self._nrows = parent.nrows() |
|---|
| 125 | self._ncols = parent.ncols() |
|---|
| 126 | self._pivots = None |
|---|
| 127 | |
|---|
| 128 | # Allocate an array where all the entries of the matrix are stored. |
|---|
| 129 | _sig_on |
|---|
| 130 | self._entries = <mpz_t *>sage_malloc(sizeof(mpz_t) * (self._nrows * self._ncols)) |
|---|
| 131 | _sig_off |
|---|
| 132 | if self._entries == NULL: |
|---|
| 133 | raise MemoryError, "out of memory allocating a matrix" |
|---|
| 134 | |
|---|
| 135 | # Allocate an array of pointers to the rows, which is useful for |
|---|
| 136 | # certain algorithms. |
|---|
| 137 | self._matrix = <mpz_t **> sage_malloc(sizeof(mpz_t*)*self._nrows) |
|---|
| 138 | if self._matrix == NULL: |
|---|
| 139 | sage_free(self._entries) |
|---|
| 140 | self._entries = NULL |
|---|
| 141 | raise MemoryError, "out of memory allocating a matrix" |
|---|
| 142 | |
|---|
| 143 | # Set each of the pointers in the array self._matrix to point |
|---|
| 144 | # at the memory for the corresponding row. |
|---|
| 145 | cdef Py_ssize_t i, k |
|---|
| 146 | k = 0 |
|---|
| 147 | for i from 0 <= i < self._nrows: |
|---|
| 148 | self._matrix[i] = self._entries + k |
|---|
| 149 | k = k + self._ncols |
|---|
| 150 | |
|---|
| 151 | cdef _init_linbox(self): |
|---|
| 152 | _sig_on |
|---|
| 153 | linbox.set(self._matrix, self._nrows, self._ncols) |
|---|
| 154 | _sig_off |
|---|
| 155 | |
|---|
| 156 | def __copy__(self): |
|---|
| 157 | cdef Matrix_integer_dense A |
|---|
| 158 | A = Matrix_integer_dense.__new__(Matrix_integer_dense, self._parent, |
|---|
| 159 | 0, 0, 0) |
|---|
| 160 | cdef Py_ssize_t i |
|---|
| 161 | _sig_on |
|---|
| 162 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 163 | mpz_init_set(A._entries[i], self._entries[i]) |
|---|
| 164 | _sig_off |
|---|
| 165 | A._initialized = True |
|---|
| 166 | return A |
|---|
| 167 | |
|---|
| 168 | def __dealloc__(self): |
|---|
| 169 | """ |
|---|
| 170 | Frees all the memory allocated for this matrix. |
|---|
| 171 | EXAMPLE: |
|---|
| 172 | sage: a = Matrix(ZZ,2,[1,2,3,4]) |
|---|
| 173 | sage: del a |
|---|
| 174 | """ |
|---|
| 175 | cdef Py_ssize_t i |
|---|
| 176 | if self._initialized: |
|---|
| 177 | for i from 0 <= i < (self._nrows * self._ncols): |
|---|
| 178 | mpz_clear(self._entries[i]) |
|---|
| 179 | sage_free(self._entries) |
|---|
| 180 | sage_free(self._matrix) |
|---|
| 181 | |
|---|
| 182 | def __init__(self, parent, entries, copy, coerce): |
|---|
| 183 | r""" |
|---|
| 184 | Initialize a dense matrix over the integers. |
|---|
| 185 | |
|---|
| 186 | INPUT: |
|---|
| 187 | parent -- a matrix space |
|---|
| 188 | entries -- list - create the matrix with those entries along the rows. |
|---|
| 189 | other -- a scalar; entries is coerced to an integer and the diagonal |
|---|
| 190 | entries of this matrix are set to that integer. |
|---|
| 191 | coerce -- whether need to coerce entries to the integers (program may crash |
|---|
| 192 | if you get this wrong) |
|---|
| 193 | copy -- ignored (since integers are immutable) |
|---|
| 194 | |
|---|
| 195 | EXAMPLES: |
|---|
| 196 | |
|---|
| 197 | The __init__ function is called implicitly in each of the |
|---|
| 198 | examples below to actually fill in the values of the matrix. |
|---|
| 199 | |
|---|
| 200 | We create a $2 \times 2$ and a $1\times 4$ matrix: |
|---|
| 201 | sage: matrix(ZZ,2,2,range(4)) |
|---|
| 202 | [0 1] |
|---|
| 203 | [2 3] |
|---|
| 204 | sage: Matrix(ZZ,1,4,range(4)) |
|---|
| 205 | [0 1 2 3] |
|---|
| 206 | |
|---|
| 207 | If the number of columns isn't given, it is determined from the number of |
|---|
| 208 | elements in the list. |
|---|
| 209 | sage: matrix(ZZ,2,range(4)) |
|---|
| 210 | [0 1] |
|---|
| 211 | [2 3] |
|---|
| 212 | sage: matrix(ZZ,2,range(6)) |
|---|
| 213 | [0 1 2] |
|---|
| 214 | [3 4 5] |
|---|
| 215 | |
|---|
| 216 | Another way to make a matrix is to create the space of |
|---|
| 217 | matrices and coerce lists into it. |
|---|
| 218 | sage: A = Mat(ZZ,2); A |
|---|
| 219 | Full MatrixSpace of 2 by 2 dense matrices over Integer Ring |
|---|
| 220 | sage: A(range(4)) |
|---|
| 221 | [0 1] |
|---|
| 222 | [2 3] |
|---|
| 223 | |
|---|
| 224 | Actually it is only necessary that the input can be coerced to a list, so |
|---|
| 225 | the following also works: |
|---|
| 226 | sage: v = reversed(range(4)); type(v) |
|---|
| 227 | <type 'listreverseiterator'> |
|---|
| 228 | sage: A(v) |
|---|
| 229 | [3 2] |
|---|
| 230 | [1 0] |
|---|
| 231 | |
|---|
| 232 | Matrices can have many rows or columns (in fact, on a 64-bit machine they could |
|---|
| 233 | have up to $2^64-1$ rows or columns): |
|---|
| 234 | sage: v = matrix(ZZ,1,10^5, range(10^5)) |
|---|
| 235 | sage: v.parent() |
|---|
| 236 | Full MatrixSpace of 1 by 100000 dense matrices over Integer Ring |
|---|
| 237 | """ |
|---|
| 238 | cdef Py_ssize_t i, j |
|---|
| 239 | cdef int is_list |
|---|
| 240 | cdef Integer x |
|---|
| 241 | |
|---|
| 242 | if not isinstance(entries, list): |
|---|
| 243 | try: |
|---|
| 244 | entries = list(entries) |
|---|
| 245 | is_list = 1 |
|---|
| 246 | except TypeError: |
|---|
| 247 | try: |
|---|
| 248 | # Try to coerce entries to a scalar (an integer) |
|---|
| 249 | x = ZZ(entries) |
|---|
| 250 | is_list = 0 |
|---|
| 251 | except TypeError: |
|---|
| 252 | raise TypeError, "entries must be coercible to a list or integer" |
|---|
| 253 | else: |
|---|
| 254 | is_list = 1 |
|---|
| 255 | |
|---|
| 256 | if is_list: |
|---|
| 257 | |
|---|
| 258 | # Create the matrix whose entries are in the given entry list. |
|---|
| 259 | if len(entries) != self._nrows * self._ncols: |
|---|
| 260 | sage_free(self._entries) |
|---|
| 261 | sage_free(self._matrix) |
|---|
| 262 | self._entries = NULL |
|---|
| 263 | raise TypeError, "entries has the wrong length" |
|---|
| 264 | if coerce: |
|---|
| 265 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 266 | # TODO: Should use an unsafe un-bounds-checked array access here. |
|---|
| 267 | x = ZZ(entries[i]) |
|---|
| 268 | # todo -- see integer.pyx and the TODO there; perhaps this could be |
|---|
| 269 | # sped up by creating a mpz_init_set_sage function. |
|---|
| 270 | mpz_init_set(self._entries[i], x.value) |
|---|
| 271 | self._initialized = True |
|---|
| 272 | else: |
|---|
| 273 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 274 | # TODO: Should use an unsafe un-bounds-checked array access here. |
|---|
| 275 | mpz_init_set(self._entries[i], (<Integer> entries[i]).value) |
|---|
| 276 | self._initialized = True |
|---|
| 277 | else: |
|---|
| 278 | |
|---|
| 279 | # If x is zero, make the zero matrix and be done. |
|---|
| 280 | if mpz_cmp_si(x.value, 0) == 0: |
|---|
| 281 | self._zero_out_matrix() |
|---|
| 282 | return |
|---|
| 283 | |
|---|
| 284 | # the matrix must be square: |
|---|
| 285 | if self._nrows != self._ncols: |
|---|
| 286 | sage_free(self._entries) |
|---|
| 287 | sage_free(self._matrix) |
|---|
| 288 | self._entries = NULL |
|---|
| 289 | raise TypeError, "nonzero scalar matrix must be square" |
|---|
| 290 | |
|---|
| 291 | # Now we set all the diagonal entries to x and all other entries to 0. |
|---|
| 292 | self._zero_out_matrix() |
|---|
| 293 | j = 0 |
|---|
| 294 | for i from 0 <= i < self._nrows: |
|---|
| 295 | mpz_init_set(self._entries[j], x.value) |
|---|
| 296 | j = j + self._nrows + 1 |
|---|
| 297 | self._initialized = True |
|---|
| 298 | |
|---|
| 299 | cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value): |
|---|
| 300 | """ |
|---|
| 301 | Set position i,j of this matrix to x. |
|---|
| 302 | |
|---|
| 303 | (VERY UNSAFE -- value *must* be of type Integer). |
|---|
| 304 | |
|---|
| 305 | INPUT: |
|---|
| 306 | ij -- tuple (i,j), where i is the row and j the column |
|---|
| 307 | Alternatively, ij can be an integer, and the ij-th row is set. |
|---|
| 308 | |
|---|
| 309 | EXAMPLES: |
|---|
| 310 | sage: a = matrix(ZZ,2,3, range(6)); a |
|---|
| 311 | [0 1 2] |
|---|
| 312 | [3 4 5] |
|---|
| 313 | sage: a[0,0] = 10 |
|---|
| 314 | sage: a |
|---|
| 315 | [10 1 2] |
|---|
| 316 | [ 3 4 5] |
|---|
| 317 | """ |
|---|
| 318 | #cdef Integer Z |
|---|
| 319 | #Z = value |
|---|
| 320 | #mpz_set(self._matrix[i][j], Z.value) |
|---|
| 321 | mpz_set(self._matrix[i][j], (<Integer>value).value) |
|---|
| 322 | |
|---|
| 323 | cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): |
|---|
| 324 | """ |
|---|
| 325 | EXAMPLES: |
|---|
| 326 | sage: a = MatrixSpace(ZZ,3)(range(9)); a |
|---|
| 327 | [0 1 2] |
|---|
| 328 | [3 4 5] |
|---|
| 329 | [6 7 8] |
|---|
| 330 | sage: a[1,2] |
|---|
| 331 | 5 |
|---|
| 332 | sage: a[0] |
|---|
| 333 | (0, 1, 2) |
|---|
| 334 | sage: a[4,7] |
|---|
| 335 | Traceback (most recent call last): |
|---|
| 336 | ... |
|---|
| 337 | IndexError: matrix index out of range |
|---|
| 338 | sage: a[-1,0] |
|---|
| 339 | Traceback (most recent call last): |
|---|
| 340 | ... |
|---|
| 341 | IndexError: matrix index out of range |
|---|
| 342 | """ |
|---|
| 343 | cdef Integer z |
|---|
| 344 | z = PY_NEW(Integer) |
|---|
| 345 | mpz_set(z.value, self._matrix[i][j]) |
|---|
| 346 | return z |
|---|
| 347 | |
|---|
| 348 | def _pickle(self): |
|---|
| 349 | return self._pickle_version0(), 0 |
|---|
| 350 | |
|---|
| 351 | cdef _pickle_version0(self): |
|---|
| 352 | # TODO: *maybe* redo this to use mpz_import and mpz_export |
|---|
| 353 | # from sec 5.14 of the GMP manual. ?? |
|---|
| 354 | cdef int i, j, len_so_far, m, n |
|---|
| 355 | cdef char *a |
|---|
| 356 | cdef char *s, *t, *tmp |
|---|
| 357 | |
|---|
| 358 | if self._nrows == 0 or self._ncols == 0: |
|---|
| 359 | data = '' |
|---|
| 360 | else: |
|---|
| 361 | n = self._nrows*self._ncols*10 |
|---|
| 362 | s = <char*> sage_malloc(n * sizeof(char)) |
|---|
| 363 | t = s |
|---|
| 364 | len_so_far = 0 |
|---|
| 365 | |
|---|
| 366 | _sig_on |
|---|
| 367 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 368 | m = mpz_sizeinbase (self._entries[i], 32) |
|---|
| 369 | if len_so_far + m + 1 >= n: |
|---|
| 370 | # copy to new string with double the size |
|---|
| 371 | n = 2*n + m + 1 |
|---|
| 372 | tmp = <char*> sage_malloc(n * sizeof(char)) |
|---|
| 373 | strcpy(tmp, s) |
|---|
| 374 | sage_free(s) |
|---|
| 375 | s = tmp |
|---|
| 376 | t = s + len_so_far |
|---|
| 377 | #endif |
|---|
| 378 | mpz_get_str(t, 32, self._entries[i]) |
|---|
| 379 | m = strlen(t) |
|---|
| 380 | len_so_far = len_so_far + m + 1 |
|---|
| 381 | t = t + m |
|---|
| 382 | t[0] = <char>32 |
|---|
| 383 | t[1] = <char>0 |
|---|
| 384 | t = t + 1 |
|---|
| 385 | _sig_off |
|---|
| 386 | data = str(s)[:-1] |
|---|
| 387 | free(s) |
|---|
| 388 | return data |
|---|
| 389 | |
|---|
| 390 | def _unpickle(self, data, int version): |
|---|
| 391 | if version == 0: |
|---|
| 392 | self._unpickle_version0(data) |
|---|
| 393 | else: |
|---|
| 394 | raise RuntimeError, "unknown matrix version (=%s)"%version |
|---|
| 395 | |
|---|
| 396 | cdef _unpickle_version0(self, data): |
|---|
| 397 | cdef Py_ssize_t i, n |
|---|
| 398 | data = data.split() |
|---|
| 399 | n = self._nrows * self._ncols |
|---|
| 400 | if len(data) != n: |
|---|
| 401 | raise RuntimeError, "invalid pickle data." |
|---|
| 402 | for i from 0 <= i < n: |
|---|
| 403 | s = data[i] |
|---|
| 404 | if mpz_init_set_str(self._entries[i], s, 32): |
|---|
| 405 | raise RuntimeError, "invalid pickle data" |
|---|
| 406 | self._initialized = True |
|---|
| 407 | |
|---|
| 408 | |
|---|
| 409 | def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons. |
|---|
| 410 | return self._richcmp(right, op) |
|---|
| 411 | |
|---|
| 412 | def __hash__(self): |
|---|
| 413 | return self._hash() |
|---|
| 414 | |
|---|
| 415 | ######################################################################## |
|---|
| 416 | # LEVEL 1 helpers: |
|---|
| 417 | # These function support the implementation of the level 1 functionality. |
|---|
| 418 | ######################################################################## |
|---|
| 419 | cdef _zero_out_matrix(self): |
|---|
| 420 | """ |
|---|
| 421 | Set this matrix to be the zero matrix. |
|---|
| 422 | This is only for internal use. |
|---|
| 423 | """ |
|---|
| 424 | # TODO: This is about 6-10 slower than MAGMA doing what seems to be the same thing. |
|---|
| 425 | # Moreover, NTL can also do this quickly. Why? I think both have specialized |
|---|
| 426 | # small integer classes. |
|---|
| 427 | _sig_on |
|---|
| 428 | cdef Py_ssize_t i |
|---|
| 429 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 430 | mpz_init(self._entries[i]) |
|---|
| 431 | _sig_off |
|---|
| 432 | self._initialized = True |
|---|
| 433 | |
|---|
| 434 | cdef _new_unitialized_matrix(self, Py_ssize_t nrows, Py_ssize_t ncols): |
|---|
| 435 | """ |
|---|
| 436 | Return a new matrix over the integers with the given number of rows and columns. |
|---|
| 437 | All memory is allocated for this matrix, but its entries have not yet been |
|---|
| 438 | filled in. |
|---|
| 439 | """ |
|---|
| 440 | P = self._parent.matrix_space(nrows, ncols) |
|---|
| 441 | return Matrix_integer_dense.__new__(Matrix_integer_dense, P, None, None, None) |
|---|
| 442 | |
|---|
| 443 | |
|---|
| 444 | ######################################################################## |
|---|
| 445 | # LEVEL 2 functionality |
|---|
| 446 | # x * cdef _add_c_impl |
|---|
| 447 | # x * cdef _sub_c_impl |
|---|
| 448 | # x * cdef _mul_c_impl |
|---|
| 449 | # x * cdef _cmp_c_impl |
|---|
| 450 | # * __neg__ |
|---|
| 451 | # * __invert__ |
|---|
| 452 | # * __copy__ |
|---|
| 453 | # x * _multiply_classical |
|---|
| 454 | # * _list -- list of underlying elements (need not be a copy) |
|---|
| 455 | # * _dict -- sparse dictionary of underlying elements (need not be a copy) |
|---|
| 456 | ######################################################################## |
|---|
| 457 | |
|---|
| 458 | # cdef _mul_c_impl(self, Matrix right): |
|---|
| 459 | # def __neg__(self): |
|---|
| 460 | # def __invert__(self): |
|---|
| 461 | # def __copy__(self): |
|---|
| 462 | # def _multiply_classical(left, matrix.Matrix _right): |
|---|
| 463 | # def _list(self): |
|---|
| 464 | # def _dict(self): |
|---|
| 465 | |
|---|
| 466 | def is_zero(self): |
|---|
| 467 | cdef mpz_t *a, *b |
|---|
| 468 | cdef Py_ssize_t i, j |
|---|
| 469 | cdef int k |
|---|
| 470 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 471 | if mpz_cmp_si(self._entries[i], 0): |
|---|
| 472 | return False |
|---|
| 473 | return True |
|---|
| 474 | |
|---|
| 475 | def _multiply_linbox(self, Matrix right): |
|---|
| 476 | """ |
|---|
| 477 | Multiply matrices over ZZ using linbox. |
|---|
| 478 | |
|---|
| 479 | WARNING: This is very slow right now, i.e., linbox is very slow. |
|---|
| 480 | |
|---|
| 481 | EXAMPLES: |
|---|
| 482 | sage: A = matrix(ZZ,2,3,range(6)) |
|---|
| 483 | sage: A*A.transpose() |
|---|
| 484 | [ 5 14] |
|---|
| 485 | [14 50] |
|---|
| 486 | sage: A._multiply_linbox(A.transpose()) |
|---|
| 487 | [ 5 14] |
|---|
| 488 | [14 50] |
|---|
| 489 | """ |
|---|
| 490 | cdef int e |
|---|
| 491 | cdef Matrix_integer_dense ans, B |
|---|
| 492 | B = right |
|---|
| 493 | ans = self.new_matrix(nrows = self.nrows(), ncols = right.ncols()) |
|---|
| 494 | self._init_linbox() |
|---|
| 495 | _sig_on |
|---|
| 496 | linbox.matrix_matrix_multiply(ans._matrix, B._matrix, B._nrows, B._ncols) |
|---|
| 497 | _sig_off |
|---|
| 498 | return ans |
|---|
| 499 | |
|---|
| 500 | def _multiply_classical(self, Matrix right): |
|---|
| 501 | """ |
|---|
| 502 | EXAMPLE: |
|---|
| 503 | sage: n = 3 |
|---|
| 504 | sage: a = MatrixSpace(ZZ,n,n)(range(n^2)) |
|---|
| 505 | sage: a*a |
|---|
| 506 | [ 15 18 21] |
|---|
| 507 | [ 42 54 66] |
|---|
| 508 | [ 69 90 111] |
|---|
| 509 | """ |
|---|
| 510 | if self._ncols != right._nrows: |
|---|
| 511 | raise IndexError, "Number of columns of self must equal number of rows of right." |
|---|
| 512 | |
|---|
| 513 | cdef Py_ssize_t i, j, k, l, nr, nc, snc |
|---|
| 514 | cdef mpz_t *v |
|---|
| 515 | |
|---|
| 516 | nr = self._nrows |
|---|
| 517 | nc = right._ncols |
|---|
| 518 | snc = self._ncols |
|---|
| 519 | |
|---|
| 520 | parent = self.matrix_space(nr, nc) |
|---|
| 521 | |
|---|
| 522 | cdef Matrix_integer_dense M, _right |
|---|
| 523 | _right = right |
|---|
| 524 | |
|---|
| 525 | M = Matrix_integer_dense.__new__(Matrix_integer_dense, parent, None, None, None) |
|---|
| 526 | Matrix.__init__(M, parent) |
|---|
| 527 | |
|---|
| 528 | # M has memory allocated but entries are not initialized |
|---|
| 529 | cdef mpz_t *entries |
|---|
| 530 | entries = M._entries |
|---|
| 531 | |
|---|
| 532 | cdef mpz_t s, z |
|---|
| 533 | mpz_init(s) |
|---|
| 534 | mpz_init(z) |
|---|
| 535 | |
|---|
| 536 | _sig_on |
|---|
| 537 | l = 0 |
|---|
| 538 | for i from 0 <= i < nr: |
|---|
| 539 | for j from 0 <= j < nc: |
|---|
| 540 | mpz_set_si(s,0) # set s = 0 |
|---|
| 541 | v = self._matrix[i] |
|---|
| 542 | for k from 0 <= k < snc: |
|---|
| 543 | mpz_mul(z, v[k], _right._matrix[k][j]) |
|---|
| 544 | mpz_add(s, s, z) |
|---|
| 545 | mpz_init_set(entries[l], s) |
|---|
| 546 | l += 1 |
|---|
| 547 | _sig_off |
|---|
| 548 | mpz_clear(s) |
|---|
| 549 | mpz_clear(z) |
|---|
| 550 | M._initialized = True |
|---|
| 551 | return M |
|---|
| 552 | |
|---|
| 553 | cdef sage.structure.element.Matrix _matrix_times_matrix_c_impl(self, sage.structure.element.Matrix right): |
|---|
| 554 | |
|---|
| 555 | return self._multiply_classical(right) |
|---|
| 556 | |
|---|
| 557 | # NOTE -- the multimodular matrix multiply implementation |
|---|
| 558 | # breaks on 64-bit machines; e..g, the following doctests |
|---|
| 559 | # *all* fail if multimodular matrix multiply is enabled |
|---|
| 560 | # on sage.math.washington.edu: |
|---|
| 561 | |
|---|
| 562 | #sage -t devel/sage-main/sage/modular/modsym/modsym.py |
|---|
| 563 | #sage -t devel/sage-main/sage/modular/modsym/space.py |
|---|
| 564 | #sage -t devel/sage-main/sage/modular/modsym/subspace.py |
|---|
| 565 | #sage -t devel/sage-main/sage/modular/hecke/hecke_operator.py |
|---|
| 566 | #sage -t devel/sage-main/sage/modular/hecke/module.py |
|---|
| 567 | |
|---|
| 568 | ############# |
|---|
| 569 | # see the tune_multiplication function below. |
|---|
| 570 | n = max(self._nrows, self._ncols, right._nrows, right._ncols) |
|---|
| 571 | if n <= 20: |
|---|
| 572 | return self._multiply_classical(right) |
|---|
| 573 | return self._multiply_multi_modular(right) |
|---|
| 574 | ## a = self.height(); b = right.height() |
|---|
| 575 | ## # waiting for multiply_multi_modular to get fixed, and not assume all matrix entries |
|---|
| 576 | ## # are between 0 and prod - 1. |
|---|
| 577 | ## if float(max(a,b)) / float(n) >= 0.70: |
|---|
| 578 | ## return self._multiply_classical(right) |
|---|
| 579 | ## else: |
|---|
| 580 | ## return self._multiply_multi_modular(right) |
|---|
| 581 | |
|---|
| 582 | cdef ModuleElement _lmul_c_impl(self, RingElement right): |
|---|
| 583 | """ |
|---|
| 584 | EXAMPLES: |
|---|
| 585 | sage: a = matrix(QQ,2,range(6)) |
|---|
| 586 | sage: (3/4) * a |
|---|
| 587 | [ 0 3/4 3/2] |
|---|
| 588 | [ 9/4 3 15/4] |
|---|
| 589 | """ |
|---|
| 590 | cdef Py_ssize_t i |
|---|
| 591 | cdef Integer _x |
|---|
| 592 | _x = Integer(right) |
|---|
| 593 | cdef Matrix_integer_dense M |
|---|
| 594 | M = Matrix_integer_dense.__new__(Matrix_integer_dense, self._parent, None, None, None) |
|---|
| 595 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 596 | mpz_init(M._entries[i]) |
|---|
| 597 | mpz_mul(M._entries[i], self._entries[i], _x.value) |
|---|
| 598 | M._initialized = True |
|---|
| 599 | return M |
|---|
| 600 | |
|---|
| 601 | cdef ModuleElement _add_c_impl(self, ModuleElement right): |
|---|
| 602 | """ |
|---|
| 603 | Add two dense matrices over ZZ. |
|---|
| 604 | |
|---|
| 605 | EXAMPLES: |
|---|
| 606 | sage: a = MatrixSpace(ZZ,3)(range(9)) |
|---|
| 607 | sage: a+a |
|---|
| 608 | [ 0 2 4] |
|---|
| 609 | [ 6 8 10] |
|---|
| 610 | [12 14 16] |
|---|
| 611 | sage: b = MatrixSpace(ZZ,3)(range(9)) |
|---|
| 612 | sage: b.swap_rows(1,2) |
|---|
| 613 | sage: a+b |
|---|
| 614 | [ 0 2 4] |
|---|
| 615 | [ 9 11 13] |
|---|
| 616 | [ 9 11 13] |
|---|
| 617 | """ |
|---|
| 618 | cdef Py_ssize_t i, j |
|---|
| 619 | |
|---|
| 620 | cdef Matrix_integer_dense M |
|---|
| 621 | M = Matrix_integer_dense.__new__(Matrix_integer_dense, self._parent, None, None, None) |
|---|
| 622 | |
|---|
| 623 | _sig_on |
|---|
| 624 | cdef mpz_t *row_self, *row_right, *row_ans |
|---|
| 625 | for i from 0 <= i < self._nrows: |
|---|
| 626 | row_self = self._matrix[i] |
|---|
| 627 | row_right = (<Matrix_integer_dense> right)._matrix[i] |
|---|
| 628 | row_ans = M._matrix[i] |
|---|
| 629 | for j from 0 <= j < self._ncols: |
|---|
| 630 | mpz_init(row_ans[j]) |
|---|
| 631 | mpz_add(row_ans[j], row_self[j], row_right[j]) |
|---|
| 632 | _sig_off |
|---|
| 633 | M._initialized = True |
|---|
| 634 | return M |
|---|
| 635 | |
|---|
| 636 | cdef ModuleElement _sub_c_impl(self, ModuleElement right): |
|---|
| 637 | """ |
|---|
| 638 | Subtract two dense matrices over ZZ. |
|---|
| 639 | |
|---|
| 640 | EXAMPLES: |
|---|
| 641 | sage: M = Mat(ZZ,3) |
|---|
| 642 | sage: a = M(range(9)); b = M(reversed(range(9))) |
|---|
| 643 | sage: a - b |
|---|
| 644 | [-8 -6 -4] |
|---|
| 645 | [-2 0 2] |
|---|
| 646 | [ 4 6 8] |
|---|
| 647 | """ |
|---|
| 648 | cdef Py_ssize_t i, j |
|---|
| 649 | |
|---|
| 650 | cdef Matrix_integer_dense M |
|---|
| 651 | M = Matrix_integer_dense.__new__(Matrix_integer_dense, self._parent, None, None, None) |
|---|
| 652 | |
|---|
| 653 | _sig_on |
|---|
| 654 | cdef mpz_t *row_self, *row_right, *row_ans |
|---|
| 655 | for i from 0 <= i < self._nrows: |
|---|
| 656 | row_self = self._matrix[i] |
|---|
| 657 | row_right = (<Matrix_integer_dense> right)._matrix[i] |
|---|
| 658 | row_ans = M._matrix[i] |
|---|
| 659 | for j from 0 <= j < self._ncols: |
|---|
| 660 | mpz_init(row_ans[j]) |
|---|
| 661 | mpz_sub(row_ans[j], row_self[j], row_right[j]) |
|---|
| 662 | _sig_off |
|---|
| 663 | M._initialized = True |
|---|
| 664 | return M |
|---|
| 665 | |
|---|
| 666 | |
|---|
| 667 | cdef int _cmp_c_impl(self, Element right) except -2: |
|---|
| 668 | cdef mpz_t *a, *b |
|---|
| 669 | cdef Py_ssize_t i, j |
|---|
| 670 | cdef int k |
|---|
| 671 | for i from 0 <= i < self._nrows: |
|---|
| 672 | a = self._matrix[i] |
|---|
| 673 | b = (<Matrix_integer_dense>right)._matrix[i] |
|---|
| 674 | for j from 0 <= j < self._ncols: |
|---|
| 675 | k = mpz_cmp(a[j], b[j]) |
|---|
| 676 | if k: |
|---|
| 677 | if k < 0: |
|---|
| 678 | return -1 |
|---|
| 679 | else: |
|---|
| 680 | return 1 |
|---|
| 681 | return 0 |
|---|
| 682 | |
|---|
| 683 | |
|---|
| 684 | cdef Vector _vector_times_matrix_c_impl(self, Vector v): |
|---|
| 685 | """ |
|---|
| 686 | Returns the vector times matrix product. |
|---|
| 687 | |
|---|
| 688 | INPUT: |
|---|
| 689 | v -- a free module element. |
|---|
| 690 | |
|---|
| 691 | OUTPUT: |
|---|
| 692 | The the vector times matrix product v*A. |
|---|
| 693 | |
|---|
| 694 | EXAMPLES: |
|---|
| 695 | sage: B = matrix(ZZ,2, [1,2,3,4]) |
|---|
| 696 | sage: V = ZZ^2 |
|---|
| 697 | sage: w = V([-1,5]) |
|---|
| 698 | sage: w*B |
|---|
| 699 | (14, 18) |
|---|
| 700 | """ |
|---|
| 701 | cdef Vector_integer_dense w, ans |
|---|
| 702 | cdef Py_ssize_t i, j |
|---|
| 703 | cdef mpz_t x |
|---|
| 704 | |
|---|
| 705 | M = self._row_ambient_module() |
|---|
| 706 | w = <Vector_integer_dense> v |
|---|
| 707 | ans = M.zero_vector() |
|---|
| 708 | |
|---|
| 709 | mpz_init(x) |
|---|
| 710 | for i from 0 <= i < self._ncols: |
|---|
| 711 | mpz_set_si(x, 0) |
|---|
| 712 | for j from 0 <= j < self._nrows: |
|---|
| 713 | mpz_addmul(x, w._entries[j], self._matrix[j][i]) |
|---|
| 714 | mpz_set(ans._entries[i], x) |
|---|
| 715 | mpz_clear(x) |
|---|
| 716 | return ans |
|---|
| 717 | |
|---|
| 718 | |
|---|
| 719 | ######################################################################## |
|---|
| 720 | # LEVEL 3 functionality (Optional) |
|---|
| 721 | # * cdef _sub_c_impl |
|---|
| 722 | # * __deepcopy__ |
|---|
| 723 | # * __invert__ |
|---|
| 724 | # * Matrix windows -- only if you need strassen for that base |
|---|
| 725 | # * Other functions (list them here): |
|---|
| 726 | # * Specialized echelon form |
|---|
| 727 | ######################################################################## |
|---|
| 728 | |
|---|
| 729 | def charpoly(self, var='x', algorithm='linbox'): |
|---|
| 730 | """ |
|---|
| 731 | INPUT: |
|---|
| 732 | var -- a variable name |
|---|
| 733 | algorithm -- 'linbox' (default) |
|---|
| 734 | 'generic' |
|---|
| 735 | |
|---|
| 736 | NOTE: Linbox charpoly disabled on 64-bit machines, since it |
|---|
| 737 | hangs in many cases. |
|---|
| 738 | |
|---|
| 739 | EXAMPLES: |
|---|
| 740 | sage: A = matrix(ZZ,6, range(36)) |
|---|
| 741 | sage: f = A.charpoly(); f |
|---|
| 742 | x^6 - 105*x^5 - 630*x^4 |
|---|
| 743 | sage: f(A) == 0 |
|---|
| 744 | True |
|---|
| 745 | sage: n=20; A = Mat(ZZ,n)(range(n^2)) |
|---|
| 746 | sage: A.charpoly() |
|---|
| 747 | x^20 - 3990*x^19 - 266000*x^18 |
|---|
| 748 | sage: A.minpoly() # optional -- os x only right now |
|---|
| 749 | x^3 - 3990*x^2 - 266000*x |
|---|
| 750 | """ |
|---|
| 751 | key = 'charpoly_%s_%s'%(algorithm, var) |
|---|
| 752 | x = self.fetch(key) |
|---|
| 753 | if x: return x |
|---|
| 754 | |
|---|
| 755 | if algorithm == 'linbox' and not USE_LINBOX_POLY: |
|---|
| 756 | algorithm = 'generic' |
|---|
| 757 | |
|---|
| 758 | if algorithm == 'linbox': |
|---|
| 759 | g = self._charpoly_linbox(var) |
|---|
| 760 | elif algorithm == 'generic': |
|---|
| 761 | g = matrix_dense.Matrix_dense.charpoly(self, var) |
|---|
| 762 | else: |
|---|
| 763 | raise ValueError, "no algorithm '%s'"%algorithm |
|---|
| 764 | |
|---|
| 765 | self.cache(key, g) |
|---|
| 766 | return g |
|---|
| 767 | |
|---|
| 768 | def minpoly(self, var='x', algorithm='linbox'): |
|---|
| 769 | """ |
|---|
| 770 | INPUT: |
|---|
| 771 | var -- a variable name |
|---|
| 772 | algorithm -- 'linbox' (default) |
|---|
| 773 | 'generic' |
|---|
| 774 | |
|---|
| 775 | NOTE: Linbox charpoly disabled on 64-bit machines, since it |
|---|
| 776 | hangs in many cases. |
|---|
| 777 | |
|---|
| 778 | EXAMPLES: |
|---|
| 779 | sage: A = matrix(ZZ,6, range(36)) |
|---|
| 780 | sage: A.minpoly() # optional -- os x only right now |
|---|
| 781 | x^3 - 105*x^2 - 630*x |
|---|
| 782 | sage: n=6; A = Mat(ZZ,n)([k^2 for k in range(n^2)]) |
|---|
| 783 | sage: A.minpoly() # optional -- os x only right now |
|---|
| 784 | x^4 - 2695*x^3 - 257964*x^2 + 1693440*x |
|---|
| 785 | """ |
|---|
| 786 | key = 'minpoly_%s_%s'%(algorithm, var) |
|---|
| 787 | x = self.fetch(key) |
|---|
| 788 | if x: return x |
|---|
| 789 | |
|---|
| 790 | if algorithm == 'linbox' and not USE_LINBOX_POLY: |
|---|
| 791 | algorithm = 'generic' |
|---|
| 792 | |
|---|
| 793 | if algorithm == 'linbox': |
|---|
| 794 | g = self._minpoly_linbox(var) |
|---|
| 795 | elif algorithm == 'generic': |
|---|
| 796 | g = matrix_dense.Matrix_dense.minpoly(self, var) |
|---|
| 797 | else: |
|---|
| 798 | raise ValueError, "no algorithm '%s'"%algorithm |
|---|
| 799 | |
|---|
| 800 | self.cache(key, g) |
|---|
| 801 | return g |
|---|
| 802 | |
|---|
| 803 | def _minpoly_linbox(self, var='x'): |
|---|
| 804 | return self._poly_linbox(var=var, typ='minpoly') |
|---|
| 805 | |
|---|
| 806 | def _charpoly_linbox(self, var='x'): |
|---|
| 807 | if self.is_zero(): # program around a bug in linbox on 32-bit linux |
|---|
| 808 | x = self.base_ring()[var].gen() |
|---|
| 809 | return x ** self._nrows |
|---|
| 810 | return self._poly_linbox(var=var, typ='charpoly') |
|---|
| 811 | |
|---|
| 812 | def _poly_linbox(self, var='x', typ='minpoly'): |
|---|
| 813 | """ |
|---|
| 814 | INPUT: |
|---|
| 815 | var -- 'x' |
|---|
| 816 | typ -- 'minpoly' or 'charpoly' |
|---|
| 817 | """ |
|---|
| 818 | time = verbose('computing %s of %s x %s matrix using linbox'%(typ, self._nrows, self._ncols)) |
|---|
| 819 | if self._nrows != self._ncols: |
|---|
| 820 | raise ValueError, "matrix must be square" |
|---|
| 821 | if self._nrows <= 1: |
|---|
| 822 | return matrix_dense.Matrix_dense.charpoly(self, var) |
|---|
| 823 | self._init_linbox() |
|---|
| 824 | if typ == 'minpoly': |
|---|
| 825 | _sig_on |
|---|
| 826 | v = linbox.minpoly() |
|---|
| 827 | _sig_off |
|---|
| 828 | else: |
|---|
| 829 | _sig_on |
|---|
| 830 | v = linbox.charpoly() |
|---|
| 831 | _sig_off |
|---|
| 832 | R = self._base_ring[var] |
|---|
| 833 | verbose('finished computing %s'%typ, time) |
|---|
| 834 | return R(v) |
|---|
| 835 | |
|---|
| 836 | |
|---|
| 837 | def height(self): |
|---|
| 838 | """ |
|---|
| 839 | Return the height of this matrix, i.e., the max absolute value |
|---|
| 840 | of the entries of the matrix. |
|---|
| 841 | |
|---|
| 842 | OUTPUT: |
|---|
| 843 | A nonnegative integer. |
|---|
| 844 | |
|---|
| 845 | EXAMPLE: |
|---|
| 846 | sage: a = Mat(ZZ,3)(range(9)) |
|---|
| 847 | sage: a.height() |
|---|
| 848 | 8 |
|---|
| 849 | sage: a = Mat(ZZ,2,3)([-17,3,-389,15,-1,0]); a |
|---|
| 850 | [ -17 3 -389] |
|---|
| 851 | [ 15 -1 0] |
|---|
| 852 | sage: a.height() |
|---|
| 853 | 389 |
|---|
| 854 | """ |
|---|
| 855 | cdef mpz_t h |
|---|
| 856 | cdef Integer x |
|---|
| 857 | |
|---|
| 858 | self.mpz_height(h) |
|---|
| 859 | x = Integer() |
|---|
| 860 | x.set_from_mpz(h) |
|---|
| 861 | mpz_clear(h) |
|---|
| 862 | |
|---|
| 863 | return x |
|---|
| 864 | |
|---|
| 865 | cdef int mpz_height(self, mpz_t height) except -1: |
|---|
| 866 | """ |
|---|
| 867 | Used to compute the height of this matrix. |
|---|
| 868 | |
|---|
| 869 | INPUT: |
|---|
| 870 | height -- a GMP mpz_t (that has not been initialized!) |
|---|
| 871 | OUTPUT: |
|---|
| 872 | sets the value of height to the height of this matrix, i.e., the max absolute |
|---|
| 873 | value of the entries of the matrix. |
|---|
| 874 | """ |
|---|
| 875 | cdef mpz_t x, h |
|---|
| 876 | cdef Py_ssize_t i |
|---|
| 877 | |
|---|
| 878 | mpz_init_set_si(h, 0) |
|---|
| 879 | mpz_init(x) |
|---|
| 880 | |
|---|
| 881 | _sig_on |
|---|
| 882 | |
|---|
| 883 | for i from 0 <= i < self._nrows * self._ncols: |
|---|
| 884 | mpz_abs(x, self._entries[i]) |
|---|
| 885 | if mpz_cmp(h, x) < 0: |
|---|
| 886 | mpz_set(h, x) |
|---|
| 887 | |
|---|
| 888 | _sig_off |
|---|
| 889 | |
|---|
| 890 | mpz_init_set(height, h) |
|---|
| 891 | mpz_clear(h) |
|---|
| 892 | mpz_clear(x) |
|---|
| 893 | |
|---|
| 894 | return 0 # no error occured. |
|---|
| 895 | |
|---|
| 896 | def _multiply_multi_modular(left, Matrix_integer_dense right): |
|---|
| 897 | |
|---|
| 898 | cdef Integer h |
|---|
| 899 | cdef mod_int *moduli |
|---|
| 900 | cdef int i, n, k |
|---|
| 901 | |
|---|
| 902 | h = left.height() * right.height() * left.ncols() |
|---|
| 903 | verbose('multiplying matrices of height %s and %s'%(left.height(),right.height())) |
|---|
| 904 | mm = MultiModularBasis(h) |
|---|
| 905 | res = left._reduce(mm) |
|---|
| 906 | res_right = right._reduce(mm) |
|---|
| 907 | k = len(mm) |
|---|
| 908 | for i in range(k): # yes, I could do this with zip, but to conserve memory... |
|---|
| 909 | t = cputime() |
|---|
| 910 | res[i] *= res_right[i] |
|---|
| 911 | verbose('multiplied matrices modulo a prime (%s/%s)'%(i+1,k), t) |
|---|
| 912 | result = left.new_matrix(left.nrows(), right.ncols()) |
|---|
| 913 | _lift_crt(result, res, mm) # changes result |
|---|
| 914 | return result |
|---|
| 915 | |
|---|
| 916 | def _mod_int(self, modulus): |
|---|
| 917 | return self._mod_int_c(modulus) |
|---|
| 918 | |
|---|
| 919 | cdef _mod_int_c(self, mod_int p): |
|---|
| 920 | cdef Py_ssize_t i, j |
|---|
| 921 | cdef Matrix_modn_dense res |
|---|
| 922 | cdef mpz_t* self_row |
|---|
| 923 | cdef mod_int* res_row |
|---|
| 924 | res = Matrix_modn_dense.__new__(Matrix_modn_dense, matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False), None, None, None) |
|---|
| 925 | for i from 0 <= i < self._nrows: |
|---|
| 926 | self_row = self._matrix[i] |
|---|
| 927 | res_row = res._matrix[i] |
|---|
| 928 | for j from 0 <= j < self._ncols: |
|---|
| 929 | res_row[j] = mpz_fdiv_ui(self_row[j], p) |
|---|
| 930 | return res |
|---|
| 931 | |
|---|
| 932 | def _reduce(self, moduli): |
|---|
| 933 | |
|---|
| 934 | if isinstance(moduli, (int, long, Integer)): |
|---|
| 935 | return self._mod_int(moduli) |
|---|
| 936 | elif isinstance(moduli, list): |
|---|
| 937 | moduli = MultiModularBasis(moduli) |
|---|
| 938 | |
|---|
| 939 | cdef MultiModularBasis mm |
|---|
| 940 | mm = moduli |
|---|
| 941 | |
|---|
| 942 | res = [Matrix_modn_dense.__new__(Matrix_modn_dense, |
|---|
| 943 | matrix_space.MatrixSpace(IntegerModRing(p), self._nrows, self._ncols, sparse=False), |
|---|
| 944 | None, None, None) for p in mm] |
|---|
| 945 | |
|---|
| 946 | cdef size_t i, k, n |
|---|
| 947 | cdef Py_ssize_t nr, nc |
|---|
| 948 | |
|---|
| 949 | n = len(mm) |
|---|
| 950 | nr = self._nrows |
|---|
| 951 | nc = self._ncols |
|---|
| 952 | |
|---|
| 953 | cdef mod_int **row_list |
|---|
| 954 | row_list = <mod_int**>sage_malloc(sizeof(mod_int*) * n) |
|---|
| 955 | if row_list == NULL: |
|---|
| 956 | raise MemoryError, "out of memory allocating multi-modular coefficent list" |
|---|
| 957 | |
|---|
| 958 | cdef PyObject** res_seq |
|---|
| 959 | res_seq = FAST_SEQ_UNSAFE(res) |
|---|
| 960 | |
|---|
| 961 | _sig_on |
|---|
| 962 | for i from 0 <= i < nr: |
|---|
| 963 | for k from 0 <= k < n: |
|---|
| 964 | row_list[k] = (<Matrix_modn_dense>res_seq[k])._matrix[i] |
|---|
| 965 | mm.mpz_reduce_vec(self._matrix[i], row_list, nc) |
|---|
| 966 | _sig_off |
|---|
| 967 | |
|---|
| 968 | sage_free(row_list) |
|---|
| 969 | return res |
|---|
| 970 | |
|---|
| 971 | def _echelon_in_place_classical(self): |
|---|
| 972 | cdef Matrix_integer_dense E |
|---|
| 973 | E = self.echelon_form() |
|---|
| 974 | |
|---|
| 975 | cdef int i |
|---|
| 976 | for i from 0 <= i < self._ncols * self._nrows: |
|---|
| 977 | mpz_set(self._entries[i], E._entries[i]) |
|---|
| 978 | |
|---|
| 979 | self.clear_cache() |
|---|
| 980 | |
|---|
| 981 | def _echelon_strassen(self): |
|---|
| 982 | raise NotImplementedError |
|---|
| 983 | |
|---|
| 984 | def echelon_form(self, algorithm="default", cutoff=0, include_zero_rows=True): |
|---|
| 985 | r""" |
|---|
| 986 | Return the echelon form of this matrix over the integers. |
|---|
| 987 | |
|---|
| 988 | INPUT: |
|---|
| 989 | algorithm, cutoff -- ignored currently |
|---|
| 990 | include_zero_rows -- (default: True) if False, don't include zero rows. |
|---|
| 991 | |
|---|
| 992 | EXAMPLES: |
|---|
| 993 | sage: A = MatrixSpace(ZZ,2)([1,2,3,4]) |
|---|
| 994 | sage: A.echelon_form() |
|---|
| 995 | [1 0] |
|---|
| 996 | [0 2] |
|---|
| 997 | |
|---|
| 998 | sage: A = MatrixSpace(ZZ,5)(range(25)) |
|---|
| 999 | sage: A.echelon_form() |
|---|
| 1000 | [ 5 0 -5 -10 -15] |
|---|
| 1001 | [ 0 1 2 3 4] |
|---|
| 1002 | [ 0 0 0 0 0] |
|---|
| 1003 | [ 0 0 0 0 0] |
|---|
| 1004 | [ 0 0 0 0 0] |
|---|
| 1005 | """ |
|---|
| 1006 | x = self.fetch('echelon_form') |
|---|
| 1007 | if not x is None: |
|---|
| 1008 | return x |
|---|
| 1009 | |
|---|
| 1010 | if self._nrows == 0 or self._ncols == 0: |
|---|
| 1011 | self.cache('echelon_form', self) |
|---|
| 1012 | self.cache('pivots', []) |
|---|
| 1013 | self.cache('rank', 0) |
|---|
| 1014 | return self |
|---|
| 1015 | |
|---|
| 1016 | cdef Py_ssize_t nr, nc, n, i |
|---|
| 1017 | nr = self._nrows |
|---|
| 1018 | nc = self._ncols |
|---|
| 1019 | |
|---|
| 1020 | # The following complicated sequence of column reversals |
|---|
| 1021 | # and transposes is needed since PARI's Hermite Normal Form |
|---|
| 1022 | # does column operations instead of row operations. |
|---|
| 1023 | n = nc |
|---|
| 1024 | r = [] |
|---|
| 1025 | for i from 0 <= i < n: |
|---|
| 1026 | r.append(n-i) |
|---|
| 1027 | v = self._pari_() |
|---|
| 1028 | v = v.vecextract(r) # this reverses the order of columns |
|---|
| 1029 | v = v.mattranspose() |
|---|
| 1030 | w = v.mathnf(1) |
|---|
| 1031 | |
|---|
| 1032 | cdef Matrix_integer_dense H_m |
|---|
| 1033 | H = convert_parimatrix(w[0]) |
|---|
| 1034 | if nc == 1: |
|---|
| 1035 | H = [H] |
|---|
| 1036 | |
|---|
| 1037 | # We do a 'fast' change of the above into a list of ints, |
|---|
| 1038 | # since we know all entries are ints: |
|---|
| 1039 | num_missing_rows = (nr*nc - len(H)) / nc |
|---|
| 1040 | rank = nr - num_missing_rows |
|---|
| 1041 | |
|---|
| 1042 | if include_zero_rows: |
|---|
| 1043 | H = H + ['0']*(num_missing_rows*nc) |
|---|
| 1044 | H_m = self.new_matrix(nrows=nr, ncols=nc, entries=H, coerce=True) |
|---|
| 1045 | else: |
|---|
| 1046 | H_m = self.new_matrix(nrows=rank, ncols=nc, entries=H, coerce=True) |
|---|
| 1047 | |
|---|
| 1048 | H_m.set_immutable() |
|---|
| 1049 | H_m.cache('rank', rank) |
|---|
| 1050 | self.cache('rank',rank) |
|---|
| 1051 | H_m.cache('echelon_form',H_m) |
|---|
| 1052 | self.cache('echelon_form',H_m) |
|---|
| 1053 | return H_m |
|---|
| 1054 | |
|---|
| 1055 | def pivots(self): |
|---|
| 1056 | """ |
|---|
| 1057 | Return the pivot column positions of this matrix as a list of Python integers. |
|---|
| 1058 | |
|---|
| 1059 | This returns a list, of the position of the first nonzero entry in each row |
|---|
| 1060 | of the echelon form. |
|---|
| 1061 | |
|---|
| 1062 | OUTPUT: |
|---|
| 1063 | list -- a list of Python ints |
|---|
| 1064 | |
|---|
| 1065 | EXAMPLES: |
|---|
| 1066 | sage: n = 3; A = matrix(ZZ,n,range(n^2)); A |
|---|
| 1067 | [0 1 2] |
|---|
| 1068 | [3 4 5] |
|---|
| 1069 | [6 7 8] |
|---|
| 1070 | sage: A.pivots() |
|---|
| 1071 | [0, 1] |
|---|
| 1072 | sage: A.echelon_form() |
|---|
| 1073 | [ 3 0 -3] |
|---|
| 1074 | [ 0 1 2] |
|---|
| 1075 | [ 0 0 0] |
|---|
| 1076 | """ |
|---|
| 1077 | p = self.fetch('pivots') |
|---|
| 1078 | if not p is None: return p |
|---|
| 1079 | |
|---|
| 1080 | cdef Matrix_integer_dense E |
|---|
| 1081 | E = self.echelon_form() |
|---|
| 1082 | |
|---|
| 1083 | # Now we determine the pivots from the matrix E as quickly as we can. |
|---|
| 1084 | # For each row, we find the first nonzero position in that row -- it is the pivot. |
|---|
| 1085 | cdef Py_ssize_t i, j, k |
|---|
| 1086 | cdef mpz_t *row |
|---|
| 1087 | p = [] |
|---|
| 1088 | k = 0 |
|---|
| 1089 | for i from 0 <= i < E._nrows: |
|---|
| 1090 | row = E._matrix[i] # pointer to ith row |
|---|
| 1091 | for j from k <= j < E._ncols: |
|---|
| 1092 | if mpz_cmp_si(row[j], 0) != 0: # nonzero position |
|---|
| 1093 | p.append(j) |
|---|
| 1094 | k = j+1 # so start at next position next time |
|---|
| 1095 | break |
|---|
| 1096 | self.cache('pivots', p) |
|---|
| 1097 | return p |
|---|
| 1098 | |
|---|
| 1099 | #### Elementary divisors |
|---|
| 1100 | |
|---|
| 1101 | def elementary_divisors(self, algorithm='linbox'): |
|---|
| 1102 | """ |
|---|
| 1103 | Return the elementary divisors of self, in order. |
|---|
| 1104 | |
|---|
| 1105 | IMPLEMENTATION: Uses linbox. |
|---|
| 1106 | |
|---|
| 1107 | WARNING: This is MUCH faster than the smith_form function. |
|---|
| 1108 | |
|---|
| 1109 | The elementary divisors are the invariants of the finite |
|---|
| 1110 | abelian group that is the cokernel of this matrix. They are |
|---|
| 1111 | ordered in reverse by divisibility. |
|---|
| 1112 | |
|---|
| 1113 | INPUT: |
|---|
| 1114 | self -- matrix |
|---|
| 1115 | algorithm -- 'linbox' or 'pari' |
|---|
| 1116 | |
|---|
| 1117 | OUTPUT: |
|---|
| 1118 | list of int's |
|---|
| 1119 | |
|---|
| 1120 | EXAMPLES: |
|---|
| 1121 | sage: matrix(3, range(9)).elementary_divisors() |
|---|
| 1122 | [1, 3, 0] |
|---|
| 1123 | sage: matrix(3, range(9)).elementary_divisors(algorithm='pari') |
|---|
| 1124 | [1, 3, 0] |
|---|
| 1125 | sage: C = MatrixSpace(ZZ,4)([3,4,5,6,7,3,8,10,14,5,6,7,2,2,10,9]) |
|---|
| 1126 | sage: C.elementary_divisors() |
|---|
| 1127 | [1, 1, 1, 687] |
|---|
| 1128 | |
|---|
| 1129 | SEE ALSO: smith_form |
|---|
| 1130 | """ |
|---|
| 1131 | d = self.fetch('elementary_divisors') |
|---|
| 1132 | if not d is None: |
|---|
| 1133 | return d |
|---|
| 1134 | if self._nrows == 0 or self._ncols == 0: |
|---|
| 1135 | d = [] |
|---|
| 1136 | else: |
|---|
| 1137 | if algorithm == 'linbox': |
|---|
| 1138 | d = self._elementary_divisors_linbox() |
|---|
| 1139 | elif algorithm == 'pari': |
|---|
| 1140 | d = self._pari_().matsnf(0).python() |
|---|
| 1141 | i = d.count(0) |
|---|
| 1142 | if i > 0: |
|---|
| 1143 | d = list(reversed(d[i:])) + [d[0]]*i |
|---|
| 1144 | else: |
|---|
| 1145 | raise ValueError, "algorithm (='%s') unknown"%algorithm |
|---|
| 1146 | self.cache('elementary_divisors', d) |
|---|
| 1147 | return d |
|---|
| 1148 | |
|---|
| 1149 | def _elementary_divisors_linbox(self): |
|---|
| 1150 | self._init_linbox() |
|---|
| 1151 | _sig_on |
|---|
| 1152 | d = linbox.smithform() |
|---|
| 1153 | _sig_off |
|---|
| 1154 | return d |
|---|
| 1155 | |
|---|
| 1156 | def smith_form(self): |
|---|
| 1157 | """ |
|---|
| 1158 | Returns matrices S, U, and V such that S = U*self*V, and S |
|---|
| 1159 | is in Smith normal form. Thus S is diagonal with diagonal |
|---|
| 1160 | entries the ordered elementary divisors of S. |
|---|
| 1161 | |
|---|
| 1162 | WARNING: The elementary_divisors function, which returns |
|---|
| 1163 | the diagonal entries of S, is VASTLY faster than this |
|---|
| 1164 | function. |
|---|
| 1165 | |
|---|
| 1166 | The elementary divisors are the invariants of the finite |
|---|
| 1167 | abelian group that is the cokernel of this matrix. They are |
|---|
| 1168 | ordered in reverse by divisibility. |
|---|
| 1169 | |
|---|
| 1170 | EXAMPLES: |
|---|
| 1171 | sage: A = MatrixSpace(IntegerRing(), 3)(range(9)) |
|---|
| 1172 | sage: D, U, V = A.smith_form() |
|---|
| 1173 | sage: D |
|---|
| 1174 | [0 0 0] |
|---|
| 1175 | [0 3 0] |
|---|
| 1176 | [0 0 1] |
|---|
| 1177 | sage: U |
|---|
| 1178 | [-1 2 -1] |
|---|
| 1179 | [ 0 -1 1] |
|---|
| 1180 | [ 0 1 0] |
|---|
| 1181 | sage: V |
|---|
| 1182 | [ 1 4 -1] |
|---|
| 1183 | [-2 -3 1] |
|---|
| 1184 | [ 1 0 0] |
|---|
| 1185 | sage: U*A*V |
|---|
| 1186 | [0 0 0] |
|---|
| 1187 | [0 3 0] |
|---|
| 1188 | [0 0 1] |
|---|
| 1189 | |
|---|
| 1190 | It also makes sense for nonsquare matrices: |
|---|
| 1191 | |
|---|
| 1192 | sage: A = Matrix(ZZ,3,2,range(6)) |
|---|
| 1193 | sage: D, U, V = A.smith_form() |
|---|
| 1194 | sage: D |
|---|
| 1195 | [0 0] |
|---|
| 1196 | [2 0] |
|---|
| 1197 | [0 1] |
|---|
| 1198 | sage: U |
|---|
| 1199 | [-1 2 -1] |
|---|
| 1200 | [ 0 -1 1] |
|---|
| 1201 | [ 0 1 0] |
|---|
| 1202 | sage: V |
|---|
| 1203 | [ 3 -1] |
|---|
| 1204 | [-2 1] |
|---|
| 1205 | sage: U * A * V |
|---|
| 1206 | [0 0] |
|---|
| 1207 | [2 0] |
|---|
| 1208 | [0 1] |
|---|
| 1209 | |
|---|
| 1210 | SEE ALSO: elementary_divisors |
|---|
| 1211 | """ |
|---|
| 1212 | v = self._pari_().matsnf(1).python() |
|---|
| 1213 | D = self.matrix_space()(v[2]) |
|---|
| 1214 | U = self.matrix_space(ncols = self._nrows)(v[0]) |
|---|
| 1215 | V = self.matrix_space(nrows = self._ncols)(v[1]) |
|---|
| 1216 | return D, U, V |
|---|
| 1217 | |
|---|
| 1218 | def frobenius(self,flag=0, var='x'): |
|---|
| 1219 | """ |
|---|
| 1220 | Return the Frobenius form (rational canonical form) of this matrix. |
|---|
| 1221 | |
|---|
| 1222 | INPUT: |
|---|
| 1223 | flag --an integer: |
|---|
| 1224 | 0 -- (default) return the Frobenius form of this matrix. |
|---|
| 1225 | 1 -- return only the elementary divisor polynomials, as |
|---|
| 1226 | polynomials in var. |
|---|
| 1227 | 2 -- return a two-components vector [F,B] where F is the |
|---|
| 1228 | Frobenius form and B is the basis change so that $M=B^{-1}FB$. |
|---|
| 1229 | var -- a string (default: 'x') |
|---|
| 1230 | |
|---|
| 1231 | INPUT: |
|---|
| 1232 | flag -- 0 (default), 1 or 2 as described above |
|---|
| 1233 | |
|---|
| 1234 | ALGORITHM: uses pari's matfrobenius() |
|---|
| 1235 | |
|---|
| 1236 | EXAMPLE: |
|---|
| 1237 | sage: A = MatrixSpace(ZZ, 3)(range(9)) |
|---|
| 1238 | sage: A.frobenius(0) |
|---|
| 1239 | [ 0 0 0] |
|---|
| 1240 | [ 1 0 18] |
|---|
| 1241 | [ 0 1 12] |
|---|
| 1242 | sage: A.frobenius(1) |
|---|
| 1243 | [x^3 - 12*x^2 - 18*x] |
|---|
| 1244 | sage: A.frobenius(1, var='y') |
|---|
| 1245 | [y^3 - 12*y^2 - 18*y] |
|---|
| 1246 | sage: A.frobenius(2) |
|---|
| 1247 | ([ 0 0 0] |
|---|
| 1248 | [ 1 0 18] |
|---|
| 1249 | [ 0 1 12], |
|---|
| 1250 | [ -1 2 -1] |
|---|
| 1251 | [ 0 23/15 -14/15] |
|---|
| 1252 | [ 0 -2/15 1/15]) |
|---|
| 1253 | |
|---|
| 1254 | AUTHOR: |
|---|
| 1255 | -- 2006-04-02: Martin Albrecht |
|---|
| 1256 | |
|---|
| 1257 | TODO: |
|---|
| 1258 | -- move this to work for more general matrices than just over Z. |
|---|
| 1259 | This will require fixing how PARI polynomials are coerced |
|---|
| 1260 | to SAGE polynomials. |
|---|
| 1261 | """ |
|---|
| 1262 | if not self.is_square(): |
|---|
| 1263 | raise ArithmeticError, "frobenius matrix of non-square matrix not defined." |
|---|
| 1264 | |
|---|
| 1265 | v = self._pari_().matfrobenius(flag) |
|---|
| 1266 | if flag==0: |
|---|
| 1267 | return self.matrix_space()(v.python()) |
|---|
| 1268 | elif flag==1: |
|---|
| 1269 | r = PolynomialRing(self.base_ring(), names=var) |
|---|
| 1270 | retr = [] |
|---|
| 1271 | for f in v: |
|---|
| 1272 | retr.append(eval(str(f).replace("^","**"), {'x':r.gen()}, r.gens_dict())) |
|---|
| 1273 | return retr |
|---|
| 1274 | elif flag==2: |
|---|
| 1275 | F = matrix_space.MatrixSpace(QQ, self.nrows())(v[0].python()) |
|---|
| 1276 | B = matrix_space.MatrixSpace(QQ, self.nrows())(v[1].python()) |
|---|
| 1277 | return F, B |
|---|
| 1278 | |
|---|
| 1279 | def kernel(self, LLL=False): |
|---|
| 1280 | r""" |
|---|
| 1281 | Return the kernel of this matrix, as a module over the integers. |
|---|
| 1282 | |
|---|
| 1283 | INPUT: |
|---|
| 1284 | LLL -- bool (default: False); if True the basis is an LLL |
|---|
| 1285 | reduced basis; otherwise, it is an echelon basis. |
|---|
| 1286 | |
|---|
| 1287 | EXAMPLES: |
|---|
| 1288 | sage: M = MatrixSpace(ZZ,4,2)(range(8)) |
|---|
| 1289 | sage: M.kernel() |
|---|
| 1290 | Free module of degree 4 and rank 2 over Integer Ring |
|---|
| 1291 | Echelon basis matrix: |
|---|
| 1292 | [ 1 0 -3 2] |
|---|
| 1293 | [ 0 1 -2 1] |
|---|
| 1294 | """ |
|---|
| 1295 | if self._nrows == 0: # from a 0 space |
|---|
| 1296 | M = sage.modules.free_module.FreeModule(ZZ, self._nrows) |
|---|
| 1297 | return M.zero_subspace() |
|---|
| 1298 | |
|---|
| 1299 | elif self._ncols == 0: # to a 0 space |
|---|
| 1300 | return sage.modules.free_module.FreeModule(ZZ, self._nrows) |
|---|
| 1301 | |
|---|
| 1302 | A = self._pari_().mattranspose() |
|---|
| 1303 | B = A.matkerint() |
|---|
| 1304 | n = self._nrows |
|---|
| 1305 | M = sage.modules.free_module.FreeModule(ZZ, n) |
|---|
| 1306 | |
|---|
| 1307 | if B.ncols() == 0: |
|---|
| 1308 | return M.zero_submodule() |
|---|
| 1309 | |
|---|
| 1310 | # Now B is a basis for the LLL-reduced integer kernel as a |
|---|
| 1311 | # PARI object. The basis vectors or B[0], ..., B[n-1], |
|---|
| 1312 | # where n is the dimension of the kernel. |
|---|
| 1313 | X = [] |
|---|
| 1314 | for b in B: |
|---|
| 1315 | tmp = [] |
|---|
| 1316 | for x in b: |
|---|
| 1317 | tmp.append(ZZ(x)) |
|---|
| 1318 | X.append(M(tmp)) |
|---|
| 1319 | |
|---|
| 1320 | if LLL: |
|---|
| 1321 | return M.span_of_basis(X) |
|---|
| 1322 | else: |
|---|
| 1323 | return M.span(X) |
|---|
| 1324 | |
|---|
| 1325 | def _adjoint(self): |
|---|
| 1326 | """ |
|---|
| 1327 | Return the adjoint of this matrix. |
|---|
| 1328 | |
|---|
| 1329 | Assumes self is a square matrix (checked in adjoint). |
|---|
| 1330 | """ |
|---|
| 1331 | return self.parent()(self._pari_().matadjoint().python()) |
|---|
| 1332 | |
|---|
| 1333 | def _ntl_(self): |
|---|
| 1334 | r""" |
|---|
| 1335 | ntl.mat_ZZ representation of self. |
|---|
| 1336 | |
|---|
| 1337 | \note{NTL only knows dense matrices, so if you provide a |
|---|
| 1338 | sparse matrix NTL will allocate memory for every zero entry.} |
|---|
| 1339 | """ |
|---|
| 1340 | return mat_ZZ(self._nrows,self._ncols, self.list()) |
|---|
| 1341 | |
|---|
| 1342 | |
|---|
| 1343 | #################################################################################### |
|---|
| 1344 | # LLL |
|---|
| 1345 | #################################################################################### |
|---|
| 1346 | def lllgram(self): |
|---|
| 1347 | """ |
|---|
| 1348 | LLL reduction of the lattice whose gram matrix is self. |
|---|
| 1349 | |
|---|
| 1350 | INPUT: |
|---|
| 1351 | M -- gram matrix of a definite quadratic form |
|---|
| 1352 | |
|---|
| 1353 | OUTPUT: |
|---|
| 1354 | U -- unimodular transformation matrix such that |
|---|
| 1355 | |
|---|
| 1356 | U.transpose() * M * U |
|---|
| 1357 | |
|---|
| 1358 | is LLL-reduced |
|---|
| 1359 | |
|---|
| 1360 | ALGORITHM: |
|---|
| 1361 | Use PARI |
|---|
| 1362 | |
|---|
| 1363 | EXAMPLES: |
|---|
| 1364 | sage: M = Matrix(ZZ, 2, 2, [5,3,3,2]) ; M |
|---|
| 1365 | [5 3] |
|---|
| 1366 | [3 2] |
|---|
| 1367 | sage: U = M.lllgram(); U |
|---|
| 1368 | [-1 1] |
|---|
| 1369 | [ 1 -2] |
|---|
| 1370 | sage: U.transpose() * M * U |
|---|
| 1371 | [1 0] |
|---|
| 1372 | [0 1] |
|---|
| 1373 | |
|---|
| 1374 | Semidefinite and indefinite forms raise a ValueError: |
|---|
| 1375 | |
|---|
| 1376 | sage: Matrix(ZZ,2,2,[2,6,6,3]).lllgram() |
|---|
| 1377 | Traceback (most recent call last): |
|---|
| 1378 | ... |
|---|
| 1379 | ValueError: not a definite matrix |
|---|
| 1380 | sage: Matrix(ZZ,2,2,[1,0,0,-1]).lllgram() |
|---|
| 1381 | Traceback (most recent call last): |
|---|
| 1382 | ... |
|---|
| 1383 | ValueError: not a definite matrix |
|---|
| 1384 | |
|---|
| 1385 | BUGS: |
|---|
| 1386 | should work for semidefinite forms (PARI is ok) |
|---|
| 1387 | """ |
|---|
| 1388 | if self._nrows != self._ncols: |
|---|
| 1389 | raise ArithmeticError, "matrix must be square" |
|---|
| 1390 | |
|---|
| 1391 | n = self.nrows() |
|---|
| 1392 | # maybe should be /unimodular/ matrices ? |
|---|
| 1393 | P = self._pari_() |
|---|
| 1394 | try: |
|---|
| 1395 | U = P.lllgramint() |
|---|
| 1396 | except (RuntimeError, ArithmeticError), msg: |
|---|
| 1397 | raise ValueError, "not a definite matrix" |
|---|
| 1398 | MS = matrix_space.MatrixSpace(ZZ,n) |
|---|
| 1399 | U = MS(U.python()) |
|---|
| 1400 | # Fix last column so that det = +1 |
|---|
| 1401 | if U.det() == -1: |
|---|
| 1402 | for i in range(n): |
|---|
| 1403 | U[i,n-1] = - U[i,n-1] |
|---|
| 1404 | return U |
|---|
| 1405 | |
|---|
| 1406 | def prod_of_row_sums(self, cols): |
|---|
| 1407 | cdef Py_ssize_t c, row |
|---|
| 1408 | cdef mpz_t s, pr |
|---|
| 1409 | mpz_init(s) |
|---|
| 1410 | mpz_init(pr) |
|---|
| 1411 | |
|---|
| 1412 | mpz_set_si(pr, 1) |
|---|
| 1413 | for row from 0 <= row < self._nrows: |
|---|
| 1414 | tmp = [] |
|---|
| 1415 | mpz_set_si(s, 0) |
|---|
| 1416 | for c in cols: |
|---|
| 1417 | if c<0 or c >= self._ncols: |
|---|
| 1418 | raise IndexError, "matrix column index out of range" |
|---|
| 1419 | mpz_add(s, s, self._matrix[row][c]) |
|---|
| 1420 | mpz_mul(pr, pr, s) |
|---|
| 1421 | cdef Integer z |
|---|
| 1422 | z = PY_NEW(Integer) |
|---|
| 1423 | mpz_set(z.value, pr) |
|---|
| 1424 | mpz_clear(s) |
|---|
| 1425 | mpz_clear(pr) |
|---|
| 1426 | return z |
|---|
| 1427 | |
|---|
| 1428 | def _linbox_sparse(self): |
|---|
| 1429 | cdef Py_ssize_t i, j |
|---|
| 1430 | v = ['%s %s +'%(self._nrows, self._ncols)] |
|---|
| 1431 | for i from 0 <= i < self._nrows: |
|---|
| 1432 | for j from 0 <= j < self._ncols: |
|---|
| 1433 | if mpz_cmp_si(self._matrix[i][j], 0): |
|---|
| 1434 | v.append('%s %s %s'%(i+1,j+1,self.get_unsafe(i,j))) |
|---|
| 1435 | v.append('0 0 0\n') |
|---|
| 1436 | return '\n'.join(v) |
|---|
| 1437 | |
|---|
| 1438 | def _linbox_dense(self): |
|---|
| 1439 | cdef Py_ssize_t i, j |
|---|
| 1440 | v = ['%s %s x'%(self._nrows, self._ncols)] |
|---|
| 1441 | for i from 0 <= i < self._nrows: |
|---|
| 1442 | for j from 0 <= j < self._ncols: |
|---|
| 1443 | v.append(str(self.get_unsafe(i,j))) |
|---|
| 1444 | return ' '.join(v) |
|---|
| 1445 | |
|---|
| 1446 | def rational_reconstruction(self, N): |
|---|
| 1447 | """ |
|---|
| 1448 | Use rational reconstruction to lift self to a matrix over the |
|---|
| 1449 | rational numbers (if possible), where we view self as a matrix |
|---|
| 1450 | modulo N. |
|---|
| 1451 | """ |
|---|
| 1452 | import misc |
|---|
| 1453 | return misc.matrix_integer_dense_rational_reconstruction(self, N) |
|---|
| 1454 | |
|---|
| 1455 | def randomize(self, density=1, x=None, y=None): |
|---|
| 1456 | """ |
|---|
| 1457 | Randomize density proportion of the entries of this matrix, |
|---|
| 1458 | leaving the rest unchanged. |
|---|
| 1459 | |
|---|
| 1460 | The randomized entries of this matrix to be between x and y |
|---|
| 1461 | and have density 1. |
|---|
| 1462 | """ |
|---|
| 1463 | self.check_mutability() |
|---|
| 1464 | self.clear_cache() |
|---|
| 1465 | |
|---|
| 1466 | cdef int _min, _max |
|---|
| 1467 | if y is None: |
|---|
| 1468 | if x is None: |
|---|
| 1469 | min = -2 |
|---|
| 1470 | max = 3 |
|---|
| 1471 | else: |
|---|
| 1472 | min = 0 |
|---|
| 1473 | max = x |
|---|
| 1474 | else: |
|---|
| 1475 | min = x |
|---|
| 1476 | max = y |
|---|
| 1477 | |
|---|
| 1478 | density = float(density) |
|---|
| 1479 | |
|---|
| 1480 | cdef int min_is_zero |
|---|
| 1481 | min_is_nonzero = (min != 0) |
|---|
| 1482 | |
|---|
| 1483 | cdef Integer n_max, n_min, n_width |
|---|
| 1484 | n_max = Integer(max) |
|---|
| 1485 | n_min = Integer(min) |
|---|
| 1486 | n_width = n_max - n_min |
|---|
| 1487 | |
|---|
| 1488 | cdef Py_ssize_t i, j, k, nc, num_per_row |
|---|
| 1489 | global state |
|---|
| 1490 | |
|---|
| 1491 | _sig_on |
|---|
| 1492 | if density == 1: |
|---|
| 1493 | for i from 0 <= i < self._nrows*self._ncols: |
|---|
| 1494 | mpz_urandomm(self._entries[i], state, n_width.value) |
|---|
| 1495 | if min_is_nonzero: |
|---|
| 1496 | mpz_add(self._entries[i], self._entries[i], n_min.value) |
|---|
| 1497 | else: |
|---|
| 1498 | nc = self._ncols |
|---|
| 1499 | num_per_row = int(density * nc) |
|---|
| 1500 | for i from 0 <= i < self._nrows: |
|---|
| 1501 | for j from 0 <= j < num_per_row: |
|---|
| 1502 | k = random()%nc |
|---|
| 1503 | mpz_urandomm(self._matrix[i][k], state, n_width.value) |
|---|
| 1504 | if min_is_nonzero: |
|---|
| 1505 | mpz_add(self._matrix[i][k], self._matrix[i][j], n_min.value) |
|---|
| 1506 | _sig_off |
|---|
| 1507 | |
|---|
| 1508 | #### Rank |
|---|
| 1509 | |
|---|
| 1510 | def rank(self): |
|---|
| 1511 | # Can very quickly detect full rank by working modulo p. |
|---|
| 1512 | r = self._rank_modp() |
|---|
| 1513 | if r == self._nrows or r == self._ncols: |
|---|
| 1514 | self.cache('rank', r) |
|---|
| 1515 | return r |
|---|
| 1516 | # Detecting full rank didn't work -- use Linbox's general algorithm. |
|---|
| 1517 | r = self._rank_linbox() |
|---|
| 1518 | self.cache('rank', r) |
|---|
| 1519 | return r |
|---|
| 1520 | |
|---|
| 1521 | def _rank_linbox(self): |
|---|
| 1522 | """ |
|---|
| 1523 | Compute the rank of this matrix using Linbox. |
|---|
| 1524 | """ |
|---|
| 1525 | self._init_linbox() |
|---|
| 1526 | cdef unsigned long r |
|---|
| 1527 | _sig_on |
|---|
| 1528 | r = linbox.rank() |
|---|
| 1529 | _sig_off |
|---|
| 1530 | return Integer(r) |
|---|
| 1531 | |
|---|
| 1532 | def _rank_modp(self, p=46337): |
|---|
| 1533 | A = self._mod_int_c(p) |
|---|
| 1534 | return A.rank() |
|---|
| 1535 | |
|---|
| 1536 | #### Determinante |
|---|
| 1537 | |
|---|
| 1538 | def determinant(self): |
|---|
| 1539 | """ |
|---|
| 1540 | Return the determinant of this matrix. |
|---|
| 1541 | |
|---|
| 1542 | ALGORITHM: Uses linbox. |
|---|
| 1543 | |
|---|
| 1544 | EXAMPLES: |
|---|
| 1545 | |
|---|
| 1546 | """ |
|---|
| 1547 | d = self.fetch('det') |
|---|
| 1548 | if not d is None: |
|---|
| 1549 | return d |
|---|
| 1550 | d = self._det_linbox() |
|---|
| 1551 | self.cache('det', d) |
|---|
| 1552 | return d |
|---|
| 1553 | |
|---|
| 1554 | def _det_linbox(self): |
|---|
| 1555 | """ |
|---|
| 1556 | Compute the determinant of this matrix using Linbox. |
|---|
| 1557 | """ |
|---|
| 1558 | self._init_linbox() |
|---|
| 1559 | _sig_on |
|---|
| 1560 | d = linbox.det() |
|---|
| 1561 | _sig_off |
|---|
| 1562 | return Integer(d) |
|---|
| 1563 | |
|---|
| 1564 | |
|---|
| 1565 | ############################################################### |
|---|
| 1566 | |
|---|
| 1567 | ########################################### |
|---|
| 1568 | # Helper code for Echelon form algorithm. |
|---|
| 1569 | ########################################### |
|---|
| 1570 | def _parimatrix_to_strlist(A): |
|---|
| 1571 | s = str(A) |
|---|
| 1572 | s = s.replace('Mat(','').replace(')','') |
|---|
| 1573 | s = s.replace(';',',').replace(' ','') |
|---|
| 1574 | s = s.replace(",", "','") |
|---|
| 1575 | s = s.replace("[", "['") |
|---|
| 1576 | s = s.replace("]", "']") |
|---|
| 1577 | return eval(s) |
|---|
| 1578 | |
|---|
| 1579 | def _parimatrix_to_reversed_strlist(A): |
|---|
| 1580 | s = str(A) |
|---|
| 1581 | if s.find('Mat') != -1: |
|---|
| 1582 | return _parimatrix_to_strlist(A) |
|---|
| 1583 | s = s.replace('[','').replace(']','').replace(' ','') |
|---|
| 1584 | v = s.split(';') |
|---|
| 1585 | v.reverse() |
|---|
| 1586 | s = "['" + (','.join(v)) + "']" |
|---|
| 1587 | s = s.replace(",", "','") |
|---|
| 1588 | return eval(s) |
|---|
| 1589 | |
|---|
| 1590 | def convert_parimatrix(z): |
|---|
| 1591 | n = z.ncols(); |
|---|
| 1592 | r = [] |
|---|
| 1593 | for i from 0 <= i < n: |
|---|
| 1594 | r.append(n-i) |
|---|
| 1595 | z = z.vecextract(r) |
|---|
| 1596 | z = z.mattranspose() |
|---|
| 1597 | n = z.ncols(); |
|---|
| 1598 | r = [] |
|---|
| 1599 | for i from 0 <= i < n: |
|---|
| 1600 | r.append(n-i) |
|---|
| 1601 | z = z.vecextract(r) |
|---|
| 1602 | return _parimatrix_to_strlist(z) |
|---|
| 1603 | |
|---|
| 1604 | |
|---|
| 1605 | |
|---|
| 1606 | def _lift_crt(Matrix_integer_dense M, residues, moduli=None): |
|---|
| 1607 | |
|---|
| 1608 | cdef size_t n, i, j, k |
|---|
| 1609 | cdef Py_ssize_t nr, nc |
|---|
| 1610 | |
|---|
| 1611 | n = len(residues) |
|---|
| 1612 | nr = residues[0].nrows() |
|---|
| 1613 | nc = residues[0].ncols() |
|---|
| 1614 | |
|---|
| 1615 | if moduli is None: |
|---|
| 1616 | moduli = MultiModularBasis([m.base_ring().order() for m in residues]) |
|---|
| 1617 | else: |
|---|
| 1618 | if len(residues) != len(moduli): |
|---|
| 1619 | raise IndexError, "Number of residues (%s) does not match number of moduli (%s)"%(len(residues), len(moduli)) |
|---|
| 1620 | |
|---|
| 1621 | cdef MultiModularBasis mm |
|---|
| 1622 | mm = moduli |
|---|
| 1623 | |
|---|
| 1624 | for b in residues: |
|---|
| 1625 | if not PY_TYPE_CHECK(b, Matrix_modn_dense): |
|---|
| 1626 | raise TypeError, "Can only perform CRT on list of type Matrix_modn_dense." |
|---|
| 1627 | cdef PyObject** res |
|---|
| 1628 | res = FAST_SEQ_UNSAFE(residues) |
|---|
| 1629 | |
|---|
| 1630 | cdef mod_int **row_list |
|---|
| 1631 | row_list = <mod_int**>sage_malloc(sizeof(mod_int*) * n) |
|---|
| 1632 | if row_list == NULL: |
|---|
| 1633 | raise MemoryError, "out of memory allocating multi-modular coefficent list" |
|---|
| 1634 | |
|---|
| 1635 | _sig_on |
|---|
| 1636 | for i from 0 <= i < nr: |
|---|
| 1637 | for k from 0 <= k < n: |
|---|
| 1638 | row_list[k] = (<Matrix_modn_dense>res[k])._matrix[i] |
|---|
| 1639 | mm.mpz_crt_vec(M._matrix[i], row_list, nc) |
|---|
| 1640 | _sig_off |
|---|
| 1641 | |
|---|
| 1642 | sage_free(row_list) |
|---|
| 1643 | return M |
|---|
| 1644 | |
|---|
| 1645 | ########################################################## |
|---|
| 1646 | # Setup the c-library and GMP random number generators. |
|---|
| 1647 | # seed it when module is loaded. |
|---|
| 1648 | from random import randrange |
|---|
| 1649 | cdef extern from "stdlib.h": |
|---|
| 1650 | long random() |
|---|
| 1651 | void srandom(unsigned int seed) |
|---|
| 1652 | k = randrange(0,Integer(2)**(32)) |
|---|
| 1653 | srandom(k) |
|---|
| 1654 | |
|---|
| 1655 | cdef gmp_randstate_t state |
|---|
| 1656 | gmp_randinit_mt(state) |
|---|
| 1657 | gmp_randseed_ui(state,k) |
|---|
| 1658 | |
|---|
| 1659 | ####################################################### |
|---|
| 1660 | |
|---|
| 1661 | # Conclusions: |
|---|
| 1662 | # On OS X Intel, at least: |
|---|
| 1663 | # - if log_2(height) >= 0.70 * nrows, use classical |
|---|
| 1664 | |
|---|
| 1665 | def tune_multiplication(k, nmin=10, nmax=200, bitmin=2,bitmax=64): |
|---|
| 1666 | from constructor import random_matrix |
|---|
| 1667 | from sage.rings.integer_ring import ZZ |
|---|
| 1668 | for n in range(nmin,nmax,10): |
|---|
| 1669 | for i in range(bitmin, bitmax, 4): |
|---|
| 1670 | A = random_matrix(ZZ, n, n, x = 2**i) |
|---|
| 1671 | B = random_matrix(ZZ, n, n, x = 2**i) |
|---|
| 1672 | t0 = cputime() |
|---|
| 1673 | for j in range(k//n + 1): |
|---|
| 1674 | C = A._multiply_classical(B) |
|---|
| 1675 | t0 = cputime(t0) |
|---|
| 1676 | t1 = cputime() |
|---|
| 1677 | for j in range(k//n+1): |
|---|
| 1678 | C = A._multiply_multi_modular(B) |
|---|
| 1679 | t1 = cputime(t1) |
|---|
| 1680 | print n, i, float(i)/float(n) |
|---|
| 1681 | if t0 < t1: |
|---|
| 1682 | print 'classical' |
|---|
| 1683 | else: |
|---|
| 1684 | print 'multimod' |
|---|
| 1685 | |
|---|
| 1686 | |
|---|