| 1 | """ |
|---|
| 2 | Sparse rational matrices. |
|---|
| 3 | |
|---|
| 4 | AUTHORS: |
|---|
| 5 | -- William Stein (2007-02-21) |
|---|
| 6 | -- Soroosh Yazdani (2007-02-21) |
|---|
| 7 | """ |
|---|
| 8 | |
|---|
| 9 | ############################################################################## |
|---|
| 10 | # Copyright (C) 2007 William Stein <wstein@gmail.com> |
|---|
| 11 | # Distributed under the terms of the GNU General Public License (GPL) |
|---|
| 12 | # The full text of the GPL is available at: |
|---|
| 13 | # http://www.gnu.org/licenses/ |
|---|
| 14 | ############################################################################## |
|---|
| 15 | |
|---|
| 16 | include '../modules/vector_rational_sparse_h.pxi' |
|---|
| 17 | include '../modules/vector_rational_sparse_c.pxi' |
|---|
| 18 | include '../ext/stdsage.pxi' |
|---|
| 19 | |
|---|
| 20 | from sage.rings.rational cimport Rational |
|---|
| 21 | from sage.rings.integer cimport Integer |
|---|
| 22 | from matrix cimport Matrix |
|---|
| 23 | |
|---|
| 24 | from sage.rings.integer_ring import ZZ |
|---|
| 25 | import sage.matrix.matrix_space |
|---|
| 26 | |
|---|
| 27 | cdef class Matrix_rational_sparse(matrix_sparse.Matrix_sparse): |
|---|
| 28 | |
|---|
| 29 | ######################################################################## |
|---|
| 30 | # LEVEL 1 functionality |
|---|
| 31 | # * __new__ |
|---|
| 32 | # * __dealloc__ |
|---|
| 33 | # * __init__ |
|---|
| 34 | # * set_unsafe |
|---|
| 35 | # * get_unsafe |
|---|
| 36 | # * __richcmp__ -- always the same |
|---|
| 37 | # * __hash__ -- alway simple |
|---|
| 38 | ######################################################################## |
|---|
| 39 | def __new__(self, parent, entries, copy, coerce): |
|---|
| 40 | # set the parent, nrows, ncols, etc. |
|---|
| 41 | matrix_sparse.Matrix_sparse.__init__(self, parent) |
|---|
| 42 | |
|---|
| 43 | self._matrix = <mpq_vector*> sage_malloc(parent.nrows()*sizeof(mpq_vector)) |
|---|
| 44 | if self._matrix == NULL: |
|---|
| 45 | raise MemoryError, "error allocating sparse matrix" |
|---|
| 46 | # initialize the rows |
|---|
| 47 | for i from 0 <= i < parent.nrows(): |
|---|
| 48 | init_mpq_vector(&self._matrix[i], self._ncols, 0) |
|---|
| 49 | |
|---|
| 50 | # record that rows have been initialized |
|---|
| 51 | self._initialized = True |
|---|
| 52 | |
|---|
| 53 | |
|---|
| 54 | def __dealloc__(self): |
|---|
| 55 | cdef Py_ssize_t i |
|---|
| 56 | if self._initialized: |
|---|
| 57 | for i from 0 <= i < self._nrows: |
|---|
| 58 | clear_mpq_vector(&self._matrix[i]) |
|---|
| 59 | sage_free(self._matrix) |
|---|
| 60 | |
|---|
| 61 | def __init__(self, parent, entries, copy, coerce): |
|---|
| 62 | """ |
|---|
| 63 | Create a sparse matrix over the rational numbers |
|---|
| 64 | |
|---|
| 65 | INPUT: |
|---|
| 66 | parent -- a matrix space |
|---|
| 67 | entries -- * a Python list of triples (i,j,x), where 0 <= i < nrows, |
|---|
| 68 | 0 <= j < ncols, and x is coercible to an int. The i,j |
|---|
| 69 | entry of self is set to x. The x's can be 0. |
|---|
| 70 | * Alternatively, entries can be a list of *all* the entries |
|---|
| 71 | of the sparse matrix (so they would be mostly 0). |
|---|
| 72 | copy -- ignored |
|---|
| 73 | coerce -- ignored |
|---|
| 74 | """ |
|---|
| 75 | cdef Py_ssize_t i, j, k |
|---|
| 76 | cdef Rational z |
|---|
| 77 | cdef void** X |
|---|
| 78 | |
|---|
| 79 | # fill in entries in the dict case |
|---|
| 80 | if isinstance(entries, dict): |
|---|
| 81 | R = self.base_ring() |
|---|
| 82 | for ij, x in entries.iteritems(): |
|---|
| 83 | z = R(x) |
|---|
| 84 | if z != 0: |
|---|
| 85 | i, j = ij # nothing better to do since this is user input, which may be bogus. |
|---|
| 86 | if i < 0 or j < 0 or i >= self._nrows or j >= self._ncols: |
|---|
| 87 | raise IndexError, "invalid entries list" |
|---|
| 88 | mpq_vector_set_entry(&self._matrix[i], j, z.value) |
|---|
| 89 | elif isinstance(entries, list): |
|---|
| 90 | # Dense input format -- fill in entries |
|---|
| 91 | if len(entries) != self._nrows * self._ncols: |
|---|
| 92 | raise TypeError, "list of entries must be a dictionary of (i,j):x or a dense list of n * m elements" |
|---|
| 93 | seq = PySequence_Fast(entries,"expected a list") |
|---|
| 94 | X = PySequence_Fast_ITEMS(seq) |
|---|
| 95 | k = 0 |
|---|
| 96 | R = self.base_ring() |
|---|
| 97 | # Get fast access to the entries list. |
|---|
| 98 | for i from 0 <= i < self._nrows: |
|---|
| 99 | for j from 0 <= j < self._ncols: |
|---|
| 100 | z = R(<object>X[k]) |
|---|
| 101 | if z != 0: |
|---|
| 102 | mpq_vector_set_entry(&self._matrix[i], j, z.value) |
|---|
| 103 | k = k + 1 |
|---|
| 104 | else: |
|---|
| 105 | # fill in entries in the scalar case |
|---|
| 106 | z = Rational(entries) |
|---|
| 107 | if z == 0: |
|---|
| 108 | return |
|---|
| 109 | if self._nrows != self._ncols: |
|---|
| 110 | raise TypeError, "matrix must be square to initialize with a scalar." |
|---|
| 111 | for i from 0 <= i < self._nrows: |
|---|
| 112 | mpq_vector_set_entry(&self._matrix[i], i, z.value) |
|---|
| 113 | |
|---|
| 114 | |
|---|
| 115 | cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, x): |
|---|
| 116 | mpq_vector_set_entry(&self._matrix[i], j, (<Rational> x).value) |
|---|
| 117 | |
|---|
| 118 | cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): |
|---|
| 119 | cdef Rational x |
|---|
| 120 | x = Rational() |
|---|
| 121 | mpq_vector_get_entry(&x.value, &self._matrix[i], j) |
|---|
| 122 | return x |
|---|
| 123 | |
|---|
| 124 | def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons. |
|---|
| 125 | return self._richcmp(right, op) |
|---|
| 126 | def __hash__(self): |
|---|
| 127 | return self._hash() |
|---|
| 128 | |
|---|
| 129 | |
|---|
| 130 | ######################################################################## |
|---|
| 131 | # LEVEL 2 functionality |
|---|
| 132 | # * def _pickle |
|---|
| 133 | # * def _unpickle |
|---|
| 134 | # * cdef _add_c_impl |
|---|
| 135 | # * cdef _sub_c_impl |
|---|
| 136 | # * cdef _mul_c_impl |
|---|
| 137 | # * cdef _cmp_c_impl |
|---|
| 138 | # * __neg__ |
|---|
| 139 | # * __invert__ |
|---|
| 140 | # * __copy__ |
|---|
| 141 | # * _multiply_classical |
|---|
| 142 | # * _matrix_times_matrix_c_impl |
|---|
| 143 | # * _list -- list of underlying elements (need not be a copy) |
|---|
| 144 | # * _dict -- sparse dictionary of underlying elements (need not be a copy) |
|---|
| 145 | ######################################################################## |
|---|
| 146 | # def _pickle(self): |
|---|
| 147 | # def _unpickle(self, data, int version): # use version >= 0 |
|---|
| 148 | # cdef ModuleElement _add_c_impl(self, ModuleElement right): |
|---|
| 149 | # cdef _mul_c_impl(self, Matrix right): |
|---|
| 150 | # cdef int _cmp_c_impl(self, Matrix right) except -2: |
|---|
| 151 | # def __neg__(self): |
|---|
| 152 | # def __invert__(self): |
|---|
| 153 | # def __copy__(self): |
|---|
| 154 | # def _multiply_classical(left, matrix.Matrix _right): |
|---|
| 155 | # def _list(self): |
|---|
| 156 | # def _dict(self): |
|---|
| 157 | |
|---|
| 158 | |
|---|
| 159 | ######################################################################## |
|---|
| 160 | # LEVEL 3 functionality (Optional) |
|---|
| 161 | # * cdef _sub_c_impl |
|---|
| 162 | # * __deepcopy__ |
|---|
| 163 | # * __invert__ |
|---|
| 164 | # * Matrix windows -- only if you need strassen for that base |
|---|
| 165 | # * Other functions (list them here): |
|---|
| 166 | ######################################################################## |
|---|
| 167 | |
|---|
| 168 | ## cdef int mpz_denom(self, mpz_t d) except -1: |
|---|
| 169 | ## mpz_set_si(d,1) |
|---|
| 170 | ## cdef Py_ssize_t i, j |
|---|
| 171 | ## cdef mpq_vector *v |
|---|
| 172 | |
|---|
| 173 | ## _sig_on |
|---|
| 174 | ## for i from 0 <= i < self._nrows: |
|---|
| 175 | ## v = self._matrix[i] |
|---|
| 176 | ## for j from 0 <= j < v.num_nonzero: |
|---|
| 177 | ## mpz_lcm(d, d, mpq_denref(v.entries[j])) |
|---|
| 178 | ## _sig_off |
|---|
| 179 | ## return 0 |
|---|
| 180 | |
|---|
| 181 | ## def _clear_denom(self): |
|---|
| 182 | ## """ |
|---|
| 183 | ## INPUT: |
|---|
| 184 | ## self -- a matrix |
|---|
| 185 | |
|---|
| 186 | ## OUTPUT: |
|---|
| 187 | ## D*self, D |
|---|
| 188 | |
|---|
| 189 | ## The product D*self is a matrix over ZZ |
|---|
| 190 | ## """ |
|---|
| 191 | ## cdef Integer D |
|---|
| 192 | ## cdef Py_ssize_t i, j |
|---|
| 193 | ## cdef Matrix_integer_sparse A |
|---|
| 194 | ## cdef mpz_t t |
|---|
| 195 | |
|---|
| 196 | ## D = Integer() |
|---|
| 197 | ## self.mpz_denom(D.value) |
|---|
| 198 | |
|---|
| 199 | ## MZ = sage.matrix.matrix_space.MatrixSpace(ZZ, self._nrows, self._ncols, sparse=True) |
|---|
| 200 | ## A = MZ.zero_matrix() |
|---|
| 201 | |
|---|
| 202 | ## mpz_init(t) |
|---|
| 203 | ## _sig_on |
|---|
| 204 | ## for i from 0 <= i < self._nrows: |
|---|
| 205 | ## v = self._matrix[i] |
|---|
| 206 | ## for j from 0 <= j < v.num_nonzero: |
|---|
| 207 | ## mpz_divexact(t, D.value, mpq_denref(v.entries[j])) |
|---|
| 208 | ## mpz_mul(t, t, mpq_numref(v.entries[j])) |
|---|
| 209 | ## mpz_vector_set_entry(&A._matrix[i], v.positions[j], t) |
|---|
| 210 | ## _sig_off |
|---|
| 211 | ## mpz_clear(t) |
|---|
| 212 | ## A._initialized = 1 |
|---|
| 213 | ## return A, D |
|---|
| 214 | |
|---|
| 215 | |
|---|
| 216 | def _echelon_form_multimodular(self, height_guess=None, proof=True): |
|---|
| 217 | """ |
|---|
| 218 | Returns reduced row-echelon form using a multi-modular |
|---|
| 219 | algorithm. Does not change self. |
|---|
| 220 | |
|---|
| 221 | REFERENCE: Chapter 7 of Stein's "Explicitly Computing Modular Forms". |
|---|
| 222 | |
|---|
| 223 | INPUT: |
|---|
| 224 | height_guess -- integer or None |
|---|
| 225 | proof -- boolean (default: True) |
|---|
| 226 | """ |
|---|
| 227 | import misc |
|---|
| 228 | return misc.matrix_rational_echelon_form_multimodular(self, |
|---|
| 229 | height_guess=height_guess, proof=proof) |
|---|