| 1 | """ |
| 2 | Sparse matrices over Real Double and Complex Double fields |
| 3 | |
| 4 | EXAMPLES:: |
| 5 | |
| 6 | sage: matrix(RDF, {(0, 0): 1, (1, 1): 2}) |
| 7 | [1.0 0.0] |
| 8 | [0.0 2.0] |
| 9 | sage: matrix(RDF, 3, 3, 1, sparse=True) |
| 10 | [1.0 0.0 0.0] |
| 11 | [0.0 1.0 0.0] |
| 12 | [0.0 0.0 1.0] |
| 13 | |
| 14 | :: |
| 15 | |
| 16 | sage: b = MatrixSpace(RDF,2,3,sparse=True).basis() |
| 17 | sage: b[0] |
| 18 | [1.0 0.0 0.0] |
| 19 | [0.0 0.0 0.0] |
| 20 | |
| 21 | |
| 22 | We deal with the case of zero rows or zero columns:: |
| 23 | |
| 24 | sage: m = MatrixSpace(RDF,0,3,sparse=True) |
| 25 | sage: m.zero_matrix() |
| 26 | [] |
| 27 | |
| 28 | AUTHORS: |
| 29 | - Dag Sverre Seljebotn |
| 30 | """ |
| 31 | |
| 32 | ############################################################################## |
| 33 | # Copyright (C) 2009 Dag Sverre Seljebotn <dagss@student.matnat.uio.no> |
| 34 | # Distributed under the terms of the GNU General Public License (GPL) |
| 35 | # The full text of the GPL is available at: |
| 36 | # http://www.gnu.org/licenses/ |
| 37 | ############################################################################## |
| 38 | |
| 39 | # TODO list: |
| 40 | # - Efficient cmp? |
| 41 | # - Efficient get/set_unsafe when not changing sparsity |
| 42 | # - Cholesky |
| 43 | # - Matrix inversion |
| 44 | |
| 45 | import math |
| 46 | |
| 47 | import sage.rings.real_double |
| 48 | from sage.rings.real_double import RDF |
| 49 | import sage.rings.complex_double |
| 50 | from sage.rings.complex_double import CDF |
| 51 | from matrix cimport Matrix |
| 52 | from sage.structure.element cimport ModuleElement, Vector, RingElement, Element |
| 53 | from constructor import matrix |
| 54 | from sage.modules.free_module_element import vector |
| 55 | cimport sage.structure.element |
| 56 | from matrix_space import MatrixSpace |
| 57 | cimport matrix_sparse |
| 58 | import warnings |
| 59 | from sage.structure.parent cimport Parent |
| 60 | from matrix_double_dense cimport Matrix_double_dense, create_uninitialized_Matrix_double_dense |
| 61 | from matrix_generic_sparse cimport _convert_sparse_entries_to_dict |
| 62 | from sage.modules.vector_double_dense cimport Vector_double_dense |
| 63 | |
| 64 | numpy = None |
| 65 | scipy_sparse = None |
| 66 | |
| 67 | cdef bint VERBOSE = False |
| 68 | |
| 69 | times = 0 |
| 70 | |
| 71 | cdef class _verbose: |
| 72 | """ |
| 73 | The _verbose context manager allows unit testing of different |
| 74 | code paths being taken. Used by in-module doctests *only*. |
| 75 | """ |
| 76 | def __enter__(self): |
| 77 | global VERBOSE |
| 78 | VERBOSE = True |
| 79 | return None |
| 80 | def __exit__(self, a, b, c): |
| 81 | global VERBOSE |
| 82 | VERBOSE = False |
| 83 | return False # do not suppress exception |
| 84 | |
| 85 | cdef Matrix_double_sparse create_uninitialized_Matrix_double_sparse(parent): |
| 86 | """ |
| 87 | Creates an uninitialized Matrix_double_dense with the given parent. |
| 88 | The _entries_csc field of the result must be filled in by the |
| 89 | caller. |
| 90 | """ |
| 91 | return Matrix_double_sparse.__new__(Matrix_double_sparse, parent, |
| 92 | None, None, None) |
| 93 | |
| 94 | cdef class Matrix_double_sparse(matrix_sparse.Matrix_sparse): |
| 95 | """ |
| 96 | Sparse, compressed matrix over double floating point fields (RDF |
| 97 | or CDF). |
| 98 | |
| 99 | The compressed format supports fast matrix operations and |
| 100 | algorithms. However, changing the sparsity structure (assigning to |
| 101 | an element which was previously zero, or setting a non-zero |
| 102 | element to zero) is slow. Changes which do not affect sparsity are |
| 103 | faster but still slower than with a hash-based or dense matrix. |
| 104 | It is recommended to construct the matrix with all non-zero |
| 105 | positions filled in and keep the sparsity pattern. Remember to |
| 106 | call :meth:`set_immutable` after construction if possible. |
| 107 | |
| 108 | Operations that are not directly supported will still work, but |
| 109 | may use algorithms which are slow, use a lot of memory and are |
| 110 | numerically unstable. Currently supported efficient operations: |
| 111 | |
| 112 | - Addition, subtraction, negation, transpose, pickling |
| 113 | |
| 114 | - Multiplications (scalar, vector, dense and sparse |
| 115 | matrices). Multiplying with a dense matrix or vector does not |
| 116 | require more memory than the size of the (dense) result. |
| 117 | |
| 118 | The storage requirement is the same as the in-memory requirement, |
| 119 | which is about ``4*nrows + 4*nnz + element_size*nnz`` bytes where |
| 120 | ``nnz`` is the number of non-zero elements, and ``element_size`` |
| 121 | is 8 for RDF and 16 for CDF. In addition comes any cached |
| 122 | information (which can be cleared with a call to |
| 123 | meth:`_clear_cache`). |
| 124 | |
| 125 | The intention is to eventually support more efficient sparse |
| 126 | matrix algorithms, such as LU factorization and Cholesky |
| 127 | decomposition. The exact format is currently CSC, this might |
| 128 | change in the future (and may dynamically change in respose to |
| 129 | requested algorithms), but it will remain a compressed format with |
| 130 | inefficient single-element access. |
| 131 | |
| 132 | EXAMPLES:: |
| 133 | |
| 134 | sage: m = matrix(RDF, [[1,2],[3,4]], sparse=True) |
| 135 | sage: m**2 |
| 136 | [ 7.0 10.0] |
| 137 | [15.0 22.0] |
| 138 | sage: n= m^(-1); n |
| 139 | [-2.0 1.0] |
| 140 | [ 1.5 -0.5] |
| 141 | |
| 142 | sage: m = matrix(CDF, [[1,2*I],[3+I,4]], sparse=True) |
| 143 | sage: m**2 |
| 144 | [-1.0 + 6.0*I 10.0*I] |
| 145 | [15.0 + 5.0*I 14.0 + 6.0*I] |
| 146 | sage: n= m^(-1); n |
| 147 | [ 0.333333333333 + 0.333333333333*I 0.166666666667 - 0.166666666667*I] |
| 148 | [ -0.166666666667 - 0.333333333333*I 0.0833333333333 + 0.0833333333333*I] |
| 149 | """ |
| 150 | |
| 151 | def __cinit__(self, Parent parent, entries, copy, coerce): |
| 152 | """ |
| 153 | Set up a new matrix |
| 154 | |
| 155 | TESTS: |
| 156 | |
| 157 | sage: matrix(RDF, 2**31, 2, sparse=True) |
| 158 | Traceback (most recent call last): |
| 159 | ... |
| 160 | NotImplementedError: Number of rows and columns must currently be smaller than 2^31, even on 64-bit systems. This only concerns RDF/CDF sparse matrices. |
| 161 | |
| 162 | sage: matrix(RDF, 2**31-1, 2, sparse=True) |
| 163 | 2147483647 x 2 sparse matrix over Real Double Field |
| 164 | """ |
| 165 | |
| 166 | global scipy_sparse, numpy |
| 167 | if scipy_sparse is None: |
| 168 | from scipy import sparse as scipy_sparse |
| 169 | if numpy is None: |
| 170 | import numpy |
| 171 | matrix_sparse.Matrix_sparse.__init__(self, parent) |
| 172 | if self._nrows >= 2**31 or self._ncols >= 2**31: |
| 173 | raise NotImplementedError( |
| 174 | 'Number of rows and columns must currently be smaller than 2^31, even on 64-bit systems. This only concerns RDF/CDF sparse matrices.') |
| 175 | # This is a limitation in csc_matrix, even on 64-bit systems |
| 176 | self._entries_csc = None # this will be set either by __init__ or _unpickle |
| 177 | |
| 178 | self._sage_dtype = parent._base |
| 179 | if parent._base == RDF: |
| 180 | self._numpy_dtype = numpy.dtype('float64') |
| 181 | self._python_dtype = float |
| 182 | elif parent._base == CDF: |
| 183 | self._numpy_dtype = numpy.dtype('complex128') |
| 184 | self._python_dtype = complex |
| 185 | else: |
| 186 | raise ValueError("Parent has invalid base ring") |
| 187 | |
| 188 | def __dealloc__(self): |
| 189 | """ Deallocate any memory that was initialized.""" |
| 190 | return |
| 191 | |
| 192 | def __richcmp__(Matrix self, right, int op): # always need for mysterious reasons. |
| 193 | return self._richcmp(right, op) |
| 194 | |
| 195 | def __init__(self, parent, entries, copy, coerce): |
| 196 | """ |
| 197 | Fill the matrix with entries. |
| 198 | |
| 199 | EXAMPLES:: |
| 200 | |
| 201 | sage: matrix(RDF,3,sparse=True) |
| 202 | [0.0 0.0 0.0] |
| 203 | [0.0 0.0 0.0] |
| 204 | [0.0 0.0 0.0] |
| 205 | sage: matrix(RDF,3,range(9),sparse=True) |
| 206 | [0.0 1.0 2.0] |
| 207 | [3.0 4.0 5.0] |
| 208 | [6.0 7.0 8.0] |
| 209 | sage: matrix(CDF,3,3,2,sparse=True) |
| 210 | [2.0 0 0] |
| 211 | [ 0 2.0 0] |
| 212 | [ 0 0 2.0] |
| 213 | |
| 214 | TESTS:: |
| 215 | |
| 216 | sage: matrix(RDF,3,0,sparse=True) |
| 217 | [] |
| 218 | sage: matrix(RDF,3,3,0,sparse=True) |
| 219 | [0.0 0.0 0.0] |
| 220 | [0.0 0.0 0.0] |
| 221 | [0.0 0.0 0.0] |
| 222 | sage: matrix(RDF,3,3,1,sparse=True) |
| 223 | [1.0 0.0 0.0] |
| 224 | [0.0 1.0 0.0] |
| 225 | [0.0 0.0 1.0] |
| 226 | sage: matrix(RDF,3,3,2,sparse=True) |
| 227 | [2.0 0.0 0.0] |
| 228 | [0.0 2.0 0.0] |
| 229 | [0.0 0.0 2.0] |
| 230 | sage: matrix(CDF,3,0,sparse=True) |
| 231 | [] |
| 232 | sage: matrix(CDF,3,3,0,sparse=True) |
| 233 | [0 0 0] |
| 234 | [0 0 0] |
| 235 | [0 0 0] |
| 236 | sage: matrix(CDF,3,3,1,sparse=True) |
| 237 | [1.0 0 0] |
| 238 | [ 0 1.0 0] |
| 239 | [ 0 0 1.0] |
| 240 | sage: matrix(CDF,3,3,range(9),sparse=True) |
| 241 | [ 0 1.0 2.0] |
| 242 | [3.0 4.0 5.0] |
| 243 | [6.0 7.0 8.0] |
| 244 | sage: matrix(CDF,2,2,[CDF(1+I)*j for j in range(4)],sparse=True) |
| 245 | [ 0 1.0 + 1.0*I] |
| 246 | [2.0 + 2.0*I 3.0 + 3.0*I] |
| 247 | |
| 248 | The following invokes the constructor with list arguments:: |
| 249 | |
| 250 | sage: M=MatrixSpace(RDF, 2, 2, sparse=True) |
| 251 | sage: M(matrix(RDF, 2, 2, 1, sparse=False)) |
| 252 | [1.0 0.0] |
| 253 | [0.0 1.0] |
| 254 | |
| 255 | """ |
| 256 | cdef Py_ssize_t i, j |
| 257 | global scipy_sparse |
| 258 | |
| 259 | if self._nrows == 0 or self._ncols == 0: |
| 260 | return # leave _entries_csc as None |
| 261 | shape = (self._nrows, self._ncols) |
| 262 | |
| 263 | # Below is taken verbatim from matrix_generic_sparse.pyx. |
| 264 | # Something should be done to avoid copying code like this |
| 265 | # across all the matrix classes and a couple of places in |
| 266 | # matrix_space.py, but I'm too lazy and make another copy... |
| 267 | if isinstance(entries, list) and len(entries) > 0 and \ |
| 268 | hasattr(entries[0], "is_vector"): |
| 269 | entries = _convert_sparse_entries_to_dict(entries) |
| 270 | |
| 271 | if isinstance(entries, list): |
| 272 | if len(entries) != self.nrows() * self.ncols(): |
| 273 | raise TypeError, "entries has the wrong length" |
| 274 | x = entries |
| 275 | entries = {} |
| 276 | k = 0 |
| 277 | for i from 0 <= i < self._nrows: |
| 278 | for j from 0 <= j < self._ncols: |
| 279 | if x[k] != 0: |
| 280 | entries[(int(i),int(j))] = x[k] |
| 281 | k = k + 1 |
| 282 | copy = False |
| 283 | # End copy |
| 284 | |
| 285 | if isinstance(entries, dict): |
| 286 | M = scipy_sparse.dok_matrix(shape, dtype=self._numpy_dtype) |
| 287 | if coerce: |
| 288 | for key, value in entries.iteritems(): |
| 289 | M[key] = self._python_dtype(value) |
| 290 | z = self._python_dtype(value) |
| 291 | if z != 0: |
| 292 | M[key] = z |
| 293 | else: |
| 294 | # TODO: Is the coerce flag present for speed or to catch nonsense? |
| 295 | # Could drop check below if the purpose is speed |
| 296 | for key, value in entries.iteritems(): |
| 297 | if value.parent() != self._sage_dtype: |
| 298 | raise TypeError, ("all entries must be in %s" % self._sage_dtype) |
| 299 | z = self._python_dtype(value) |
| 300 | if z != 0: |
| 301 | M[key] = z |
| 302 | self._entries_csc = M.tocsc() |
| 303 | else: |
| 304 | if entries is None: |
| 305 | z = 0 |
| 306 | else: |
| 307 | try: |
| 308 | z = self._python_dtype(entries) |
| 309 | except TypeError: |
| 310 | raise TypeError, ("entries must be coercible to dict or %s" % |
| 311 | self._python_dtype.__name__) |
| 312 | if z == 0: |
| 313 | self._entries_csc = scipy_sparse.csc_matrix(shape, dtype=self._numpy_dtype) |
| 314 | else: |
| 315 | # Diagonal matrix of constant. |
| 316 | # TODO: Faster direct construction. |
| 317 | M = scipy_sparse.eye(self._nrows, self._ncols, dtype=self._numpy_dtype) |
| 318 | M *= z |
| 319 | self._entries_csc = M.tocsc() |
| 320 | |
| 321 | cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, object value): |
| 322 | """ |
| 323 | Set the (i,j) entry to value without any bounds checking, |
| 324 | mutability checking, etc. |
| 325 | """ |
| 326 | # TODO: More efficient setter if not changing sparsity pattern |
| 327 | |
| 328 | # We call the self._python_dtype function on the value since |
| 329 | # numpy does not know how to deal with complex numbers other |
| 330 | # than the built-in complex number type. |
| 331 | with warnings.catch_warnings(): |
| 332 | warnings.simplefilter('ignore', scipy_sparse.SparseEfficiencyWarning) |
| 333 | self._entries_csc[i, j] = self._python_dtype(value) |
| 334 | |
| 335 | cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j): |
| 336 | """ |
| 337 | Get the (i,j) entry without any bounds checking, etc. |
| 338 | """ |
| 339 | # TODO: More efficient getter |
| 340 | |
| 341 | return self._sage_dtype(self._entries_csc[i, j]) |
| 342 | |
| 343 | def _pickle(self): |
| 344 | """ |
| 345 | See _unpickle. |
| 346 | """ |
| 347 | version = 0 |
| 348 | data = self._entries_csc.data |
| 349 | indices = self._entries_csc.indices |
| 350 | indptr = self._entries_csc.indptr |
| 351 | return (data, indices, indptr), version |
| 352 | |
| 353 | def _unpickle(self, data, int version): |
| 354 | """ |
| 355 | Format version 0: Returns a tuple (data, indices, indptr), |
| 356 | three NumPy arrays containing the matrix in CSC format. |
| 357 | |
| 358 | TESTS:: |
| 359 | |
| 360 | sage: a = matrix(RDF,2,range(4), sparse=True) |
| 361 | sage: loads(dumps(a)) == a |
| 362 | True |
| 363 | sage: a = matrix(CDF,2,range(4), sparse=True) |
| 364 | sage: loads(dumps(a)) == a |
| 365 | True |
| 366 | """ |
| 367 | if version == 0: |
| 368 | self._entries_csc = scipy_sparse.csc_matrix(data, (self._nrows, self._ncols)) |
| 369 | else: |
| 370 | raise RuntimeError, "unknown matrix version (=%s)"%version |
| 371 | |
| 372 | def _dict(self): |
| 373 | """ |
| 374 | Unsafe version of the dict method, mainly for internal use. |
| 375 | See ancestor docstring. |
| 376 | |
| 377 | TESTS:: |
| 378 | |
| 379 | sage: matrix(RDF, 3, 0, sparse=True)._dict() |
| 380 | {} |
| 381 | |
| 382 | sage: x=matrix(RDF, 3, 3, 2, sparse=True)._dict().items(); x.sort(); x |
| 383 | [((0, 0), 2.0), ((1, 1), 2.0), ((2, 2), 2.0)] |
| 384 | """ |
| 385 | # TODO: Lots of potential for optimization |
| 386 | if self._ncols == 0 or self._nrows == 0: |
| 387 | return {} |
| 388 | d = self.fetch('dict') |
| 389 | if d is not None: |
| 390 | return d |
| 391 | d = {} |
| 392 | num_d = self._entries_csc.todok() |
| 393 | for key, value in num_d.iteritems(): |
| 394 | d[key] = self._sage_dtype(value) |
| 395 | self.cache('dict', d) |
| 396 | return d |
| 397 | |
| 398 | def _nonzero_positions_by_row(self, copy=True): |
| 399 | """ |
| 400 | See ancestor docstring. |
| 401 | |
| 402 | TESTS:: |
| 403 | |
| 404 | sage: x=matrix(RDF, 3, 3, {(1, 2) : 1, (2, 1) : 1, (0, 1): 1}, sparse=True) |
| 405 | sage: x._nonzero_positions_by_row() |
| 406 | [(0, 1), (1, 2), (2, 1)] |
| 407 | """ |
| 408 | # TODO: Lots of potential for optimization |
| 409 | x = self.fetch('nonzero_positions') |
| 410 | if not x is None: |
| 411 | if copy: |
| 412 | return list(x) |
| 413 | return x |
| 414 | x = self._entries_csc.todok().keys() |
| 415 | x.sort() |
| 416 | self.cache('nonzero_positions', x) |
| 417 | return x |
| 418 | |
| 419 | def _nonzero_positions_by_column(self, copy=True): |
| 420 | """ |
| 421 | See ancestor docstring. |
| 422 | |
| 423 | TESTS:: |
| 424 | |
| 425 | sage: x=matrix(RDF, 3, 3, {(1, 2) : 1, (2, 1) : 1, (0, 1): 1}, sparse=True) |
| 426 | sage: x._nonzero_positions_by_column() |
| 427 | [(0, 1), (2, 1), (1, 2)] |
| 428 | """ |
| 429 | # TODO: Lots of potential for optimization |
| 430 | x = self.fetch('nonzero_positions_by_column') |
| 431 | if not x is None: |
| 432 | if copy: |
| 433 | return list(x) |
| 434 | return x |
| 435 | x = self._dict().keys() |
| 436 | x.sort(key=_transpose_index) |
| 437 | self.cache('nonzero_positions_by_column', x) |
| 438 | return x |
| 439 | |
| 440 | cdef Matrix_double_sparse _new(self, int nrows=-1, int ncols=-1): |
| 441 | """ |
| 442 | Return a new uninitialized matrix with same parent as self. |
| 443 | Note that _entries_csc will be None in the returned matrix |
| 444 | and *must* be set by the caller (unless nrows or ncols == 0). |
| 445 | |
| 446 | INPUT |
| 447 | nrows -- (default self._nrows) number of rows in returned matrix |
| 448 | ncols -- (default self._ncols) number of columns in returned matrix |
| 449 | """ |
| 450 | cdef Matrix_double_sparse m |
| 451 | if nrows == -1: |
| 452 | nrows = self._nrows |
| 453 | if ncols == -1: |
| 454 | ncols = self._ncols |
| 455 | parent = self.matrix_space(nrows, ncols) |
| 456 | m = self.__class__.__new__(self.__class__,parent,None,None,None) |
| 457 | return m |
| 458 | |
| 459 | def __copy__(self): |
| 460 | r""" |
| 461 | Returns a new copy of this matrix. |
| 462 | |
| 463 | EXAMPLES:: |
| 464 | |
| 465 | sage: a = matrix(RDF,1,3, [1,2,-3],sparse=True) |
| 466 | sage: a |
| 467 | [ 1.0 2.0 -3.0] |
| 468 | sage: b = a.__copy__() |
| 469 | sage: b |
| 470 | [ 1.0 2.0 -3.0] |
| 471 | sage: b is a |
| 472 | False |
| 473 | sage: b == a |
| 474 | True |
| 475 | sage: b[0,0] = 3 |
| 476 | sage: a[0,0] # note that a hasn't changed |
| 477 | 1.0 |
| 478 | """ |
| 479 | if self._nrows == 0 or self._ncols == 0: |
| 480 | return self.new_matrix(self._nrows, self._ncols) |
| 481 | |
| 482 | cdef Matrix_double_sparse A |
| 483 | A = self._new(self._nrows, self._ncols) |
| 484 | A._entries_csc = self._entries_csc.copy() |
| 485 | if self.subdivisions is not None: |
| 486 | A.subdivide(*self.get_subdivisions()) |
| 487 | return A |
| 488 | |
| 489 | def transpose(self): |
| 490 | """ |
| 491 | Returns the transpose of self, without changing self. |
| 492 | |
| 493 | EXAMPLES: |
| 494 | |
| 495 | We create a matrix and compute its transpose. Subdivisions |
| 496 | are preserved:: |
| 497 | |
| 498 | sage: A = matrix(RDF, 2, 3, range(6), sparse=True) |
| 499 | sage: A.subdivide(None, 1) |
| 500 | sage: A |
| 501 | [0.0|1.0 2.0] |
| 502 | [3.0|4.0 5.0] |
| 503 | sage: B = A.transpose(); B |
| 504 | [0.0 3.0] |
| 505 | [-------] |
| 506 | [1.0 4.0] |
| 507 | [2.0 5.0] |
| 508 | |
| 509 | Note that changing the transpose does not affect the |
| 510 | original matrix:: |
| 511 | |
| 512 | sage: B[0,0] = 10 |
| 513 | sage: A |
| 514 | [0.0|1.0 2.0] |
| 515 | [3.0|4.0 5.0] |
| 516 | |
| 517 | """ |
| 518 | cdef Matrix_double_sparse trans = self._new(self._ncols, self._nrows) |
| 519 | trans._entries_csc = self._entries_csc.transpose(copy=True).tocsc() |
| 520 | if self.subdivisions is not None: |
| 521 | # TODO: Consistently move this up in the hierarchy everywhere |
| 522 | # and introduce _transpose_, so that e.g. other sparse matrices |
| 523 | # still have subdivisions applied |
| 524 | row_divs, col_divs = self.get_subdivisions() |
| 525 | trans.subdivide(col_divs, row_divs) |
| 526 | return trans |
| 527 | |
| 528 | |
| 529 | |
| 530 | cdef int _cmp_c_impl(self, Element right) except -2: |
| 531 | """ |
| 532 | Compare two matrices. See parent docstring for details. |
| 533 | |
| 534 | TESTS:: |
| 535 | |
| 536 | sage: matrix(RDF,2,[1,2,3,4],sparse=True) < matrix(RDF,2,[3,2,3,4],sparse=True) |
| 537 | True |
| 538 | sage: matrix(RDF,2,[1,2,3,4],sparse=True) > matrix(RDF,2,[3,2,3,4],sparse=True) |
| 539 | False |
| 540 | sage: matrix(RDF,2,[0,2,3,4],sparse=True) < matrix(RDF,2,[0,3,3,4],sparse=True) |
| 541 | True |
| 542 | sage: matrix(RDF,2,{(0,0): 1, (1,1): 2},sparse=True) == matrix(RDF,2,[1,0,0,2]) |
| 543 | True |
| 544 | """ |
| 545 | # TODO: I intended to do this, realized that it requires more than 30 minutes, |
| 546 | # but have already written the tests...so cop out for now. |
| 547 | # Also the tests uncovered a serious bug in __init__ and should be left in. |
| 548 | return cmp(self._dict(), right._dict()) |
| 549 | |
| 550 | ######################################################################## |
| 551 | # Arithmetic operations. Just use scipy.sparse. The result is converted |
| 552 | # using tocsc() in all cases, but that just returns "self" in the |
| 553 | # current implementation (but the API technically appears free to |
| 554 | # to return other forms) |
| 555 | # |
| 556 | # - _lmul_ |
| 557 | # - _add_ |
| 558 | # - __neg__ |
| 559 | # - _generic_matrix_times_matrix_ |
| 560 | ######################################################################## |
| 561 | |
| 562 | cpdef ModuleElement _lmul_(self, RingElement right): |
| 563 | """ |
| 564 | EXAMPLES:: |
| 565 | |
| 566 | sage: A = matrix(RDF, 2, 3, range(6), sparse=True) |
| 567 | sage: A * RDF(0.5) |
| 568 | [0.0 0.5 1.0] |
| 569 | [1.5 2.0 2.5] |
| 570 | sage: 1/2 * A |
| 571 | [0.0 0.5 1.0] |
| 572 | [1.5 2.0 2.5] |
| 573 | """ |
| 574 | cdef Matrix_double_sparse M = self._new() |
| 575 | M._entries_csc = self._entries_csc * self._python_dtype(right) |
| 576 | return M |
| 577 | |
| 578 | cpdef ModuleElement _add_(self, ModuleElement right): |
| 579 | """ |
| 580 | Add two matrices together. |
| 581 | |
| 582 | EXAMPLES: |
| 583 | sage: A = matrix(RDF,3,range(1,10),sparse=True) |
| 584 | sage: A+A |
| 585 | [ 2.0 4.0 6.0] |
| 586 | [ 8.0 10.0 12.0] |
| 587 | [14.0 16.0 18.0] |
| 588 | """ |
| 589 | if self._nrows == 0 or self._ncols == 0: |
| 590 | return self.__copy__() |
| 591 | |
| 592 | cdef Matrix_double_sparse M, _left, _right |
| 593 | _left = self |
| 594 | _right = right |
| 595 | |
| 596 | M = self._new() |
| 597 | M._entries_csc = (_left._entries_csc + _right._entries_csc).tocsc() |
| 598 | return M |
| 599 | |
| 600 | cpdef ModuleElement _sub_(self, ModuleElement right): |
| 601 | """ |
| 602 | Return self - right |
| 603 | |
| 604 | EXAMPLES: |
| 605 | sage: A = matrix(RDF,3,range(1,10),sparse=True) |
| 606 | sage: (A-A).is_zero() |
| 607 | True |
| 608 | """ |
| 609 | if self._nrows == 0 or self._ncols == 0: |
| 610 | return self.__copy__() |
| 611 | |
| 612 | cdef Matrix_double_sparse M,_right,_left |
| 613 | _right = right |
| 614 | _left = self |
| 615 | |
| 616 | M = self._new() |
| 617 | M._entries_csc = (_left._entries_csc - _right._entries_csc).tocsc() |
| 618 | return M |
| 619 | |
| 620 | def __neg__(self): |
| 621 | """ |
| 622 | Negate this matrix |
| 623 | |
| 624 | EXAMPLES: |
| 625 | sage: A = matrix(RDF,3,range(1,10),sparse=True) |
| 626 | sage: -A |
| 627 | [-1.0 -2.0 -3.0] |
| 628 | [-4.0 -5.0 -6.0] |
| 629 | [-7.0 -8.0 -9.0] |
| 630 | sage: B = -A ; (A+B).is_zero() |
| 631 | True |
| 632 | """ |
| 633 | if self._nrows == 0 or self._ncols == 0: |
| 634 | return self.__copy__() |
| 635 | |
| 636 | cdef Matrix_double_sparse M |
| 637 | M = self._new() |
| 638 | M._entries_csc = (-self._entries_csc).tocsc() |
| 639 | return M |
| 640 | |
| 641 | cdef sage.structure.element.Matrix _generic_matrix_times_matrix_(self, |
| 642 | sage.structure.element.Matrix right, Parent codomain): |
| 643 | """ |
| 644 | Multiply ``self`` with ``right``. |
| 645 | |
| 646 | Overrides generic version to provide: |
| 647 | |
| 648 | - faster code paths for sparse times dense |
| 649 | - complex times real uses less memory (indices not copied) |
| 650 | |
| 651 | TESTS: |
| 652 | |
| 653 | sage: from sage.matrix.matrix_double_sparse import _verbose |
| 654 | sage: A = matrix(RDF,3,range(1,10),sparse=True) |
| 655 | sage: B = matrix(RDF,3,range(1,13),sparse=True) |
| 656 | sage: AC = A.change_ring(CDF); BC = B.change_ring(CDF) |
| 657 | |
| 658 | sage: with _verbose(): A*B |
| 659 | Sparse times sparse |
| 660 | [ 38.0 44.0 50.0 56.0] |
| 661 | [ 83.0 98.0 113.0 128.0] |
| 662 | [128.0 152.0 176.0 200.0] |
| 663 | |
| 664 | Different sparseness takes different code path:: |
| 665 | |
| 666 | sage: with _verbose(): A*B.dense_matrix() |
| 667 | Sparse times dense |
| 668 | [ 38.0 44.0 50.0 56.0] |
| 669 | [ 83.0 98.0 113.0 128.0] |
| 670 | [128.0 152.0 176.0 200.0] |
| 671 | |
| 672 | Multiplying with matrix with non-double base ring works:: |
| 673 | |
| 674 | sage: A*B.change_ring(ZZ) |
| 675 | [ 38.0 44.0 50.0 56.0] |
| 676 | [ 83.0 98.0 113.0 128.0] |
| 677 | [128.0 152.0 176.0 200.0] |
| 678 | |
| 679 | Different base rings takes different code path:: |
| 680 | |
| 681 | sage: with _verbose(): A.change_ring(CDF) * B |
| 682 | Sparse times sparse |
| 683 | [ 38.0 44.0 50.0 56.0] |
| 684 | [ 83.0 98.0 113.0 128.0] |
| 685 | [128.0 152.0 176.0 200.0] |
| 686 | sage: with _verbose(): A.change_ring(CDF) * B.dense_matrix() |
| 687 | Sparse times dense |
| 688 | [ 38.0 44.0 50.0 56.0] |
| 689 | [ 83.0 98.0 113.0 128.0] |
| 690 | [128.0 152.0 176.0 200.0] |
| 691 | |
| 692 | A.dense_matrix()*B is handled in ``Matrix_double_dense``. |
| 693 | """ |
| 694 | cdef sage.structure.element.Matrix left |
| 695 | cdef Matrix_double_sparse result_sparse, right_sparse |
| 696 | cdef Matrix_double_dense result_dense, right_dense |
| 697 | |
| 698 | if self._nrows == 0 or self._ncols == 0 or right._nrows == 0 or right._ncols == 0: |
| 699 | return codomain.zero_matrix() |
| 700 | |
| 701 | base = codomain._base |
| 702 | if base is not RDF and base is not CDF: |
| 703 | # Don't know how to handle base ring. Convert left matrix and |
| 704 | # forward call. |
| 705 | left = self.change_ring(codomain._base) |
| 706 | return left._generic_matrix_times_matrix(right, codomain) |
| 707 | |
| 708 | # Note: Codomain base ring may be either of RDF or CDF. |
| 709 | # Codomain is dense if and only if right.is_dense(). |
| 710 | if right.is_dense_c(): |
| 711 | if VERBOSE: print 'Sparse times dense' |
| 712 | result_dense = create_uninitialized_Matrix_double_dense(codomain) |
| 713 | result_dense._matrix_numpy = (self._entries_csc * |
| 714 | (<Matrix_double_dense?>right)._matrix_numpy) |
| 715 | assert result_dense._matrix_numpy.dtype == result_dense._numpy_dtype |
| 716 | return result_dense |
| 717 | else: |
| 718 | if VERBOSE: print 'Sparse times sparse' |
| 719 | if right._parent._base is not base: |
| 720 | right = right.change_ring(codomain._base) |
| 721 | result_sparse = create_uninitialized_Matrix_double_sparse(codomain) |
| 722 | result_sparse._entries_csc = (self._entries_csc * |
| 723 | (<Matrix_double_sparse?>right)._entries_csc).tocsc() |
| 724 | assert result_sparse._entries_csc.dtype == result_sparse._numpy_dtype |
| 725 | return result_sparse |
| 726 | |
| 727 | cdef Vector _matrix_times_vector_(self, Vector v): |
| 728 | """ |
| 729 | TESTS:: |
| 730 | |
| 731 | sage: A = matrix(RDF, [[1,0,1],[0,1,1]], sparse=True) |
| 732 | sage: v = vector(RDF, [1, 2, 3], sparse=True) |
| 733 | sage: z = A * v; z; parent(z) |
| 734 | (4.0, 5.0) |
| 735 | Sparse vector space of dimension 2 over Real Double Field |
| 736 | |
| 737 | sage: z = A * v.dense_vector(); z; parent(z) |
| 738 | (4.0, 5.0) |
| 739 | Sparse vector space of dimension 2 over Real Double Field |
| 740 | |
| 741 | sage: z = vector(CDF, [1+1j, 1-1j], sparse=True) * A; z; parent(z) |
| 742 | (1.0 + 1.0*I, 1.0 - 1.0*I, 2.0) |
| 743 | Sparse vector space of dimension 3 over Complex Double Field |
| 744 | |
| 745 | """ |
| 746 | # This keeps existing Sage semantics, but is wrong from a |
| 747 | # numerical POV -- should discuss whether it is OK to return a |
| 748 | # dense vector when multiplying with a dense vector (that must |
| 749 | # be fixed in the coercions though, not just here.) |
| 750 | from sage.modules.free_module_element import vector |
| 751 | cdef Vector_double_dense v_dense = v.dense_vector() |
| 752 | result = self._entries_csc * v_dense._vector_numpy |
| 753 | result = vector(self._sage_dtype, result) |
| 754 | return result.sparse_vector() |
| 755 | |
| 756 | |
| 757 | ######################################################################## |
| 758 | # Solvers and factorizations. |
| 759 | # |
| 760 | # Re-implement right_solve using LU factorizations -- the default |
| 761 | # implementation is useless in a numerical setting (begins by finding |
| 762 | # matrix rank...), and since solving is so different for numerical |
| 763 | # matrices, we redefine docstring rather than introduce a |
| 764 | # _right_solve_ |
| 765 | ######################################################################## |
| 766 | |
| 767 | # SuperLU is a great library with lots of cool options (like doing |
| 768 | # iterative refinement to get a solution within a specified error tolerance, |
| 769 | # and caching many parts of the computation). I need to think more about how |
| 770 | # I want to expose it properly to user-space in a friendly manner which doesn't |
| 771 | # make it awkward to maintain backwards compatability later. |
| 772 | # The below docstring is a start though, so I keep it in the source. |
| 773 | # -- dagss |
| 774 | |
| 775 | |
| 776 | ## def solve_right(self, B, check=True, algorithm="lu", **kwds): |
| 777 | ## r""" |
| 778 | ## If self is a matrix `A`, then this function returns a |
| 779 | ## vector or matrix `X` such that `A X = B`. If |
| 780 | ## `B` is a vector then `X` is a vector and if |
| 781 | ## `B` is a matrix, then `X` is a matrix. |
| 782 | |
| 783 | ## Multiple algorithms are available and work well for different |
| 784 | ## matrices. Temporary results such as computed decompositions |
| 785 | ## are cached and available to subsequent calls to |
| 786 | ## :meth:`solve_right` or :meth:`solve_left`. Consider making the |
| 787 | ## matrix immutable (:meth:`set_immutable`) prior to solving. |
| 788 | |
| 789 | ## Currently supported algorithms: |
| 790 | |
| 791 | ## - "lu" - (the default) (P)LU factorization. Usually needs no tuning |
| 792 | ## arguments, see :meth:`LU` for the arguments which are |
| 793 | ## accepted. |
| 794 | |
| 795 | ## - "naive" - Row reduction to echelon form. This is almost always a bad |
| 796 | ## idea. Takes no tuning arguments. |
| 797 | |
| 798 | ## .. note:: |
| 799 | |
| 800 | ## In Sage one can also write ``A \backslash B`` for |
| 801 | ## ``A.solve_right(B)``, i.e., Sage implements the "the |
| 802 | ## MATLAB/Octave backslash operator". |
| 803 | |
| 804 | ## INPUT: |
| 805 | |
| 806 | ## - ``B`` - a matrix or vector |
| 807 | ## - ``check`` - bool (default: True) - if False and self |
| 808 | ## is nonsquare, may not raise an error message even if there is no |
| 809 | ## solution. This is faster but more dangerous. |
| 810 | ## - ``algorithm`` - string (default: "lu"). Which algorithm to use. |
| 811 | ## See above list. |
| 812 | ## - ``**kwds`` - Tuning arguments can optionally supplied. The arguments |
| 813 | ## varies depending on selected algorithm; see above. |
| 814 | |
| 815 | ## OUTPUT: a matrix or vector |
| 816 | |
| 817 | ## .. seealso:: |
| 818 | |
| 819 | ## :meth:`solve_left` |
| 820 | ## :meth:`solve_left` |
| 821 | |
| 822 | |
| 823 | ## """ |
| 824 | ## # Ideas: |
| 825 | ## # - Cholesky solver (CHOLMOD/SuiteSparse) |
| 826 | ## # - QR (through SuiteSparse) |
| 827 | ## # - For LU: UMFPACK in addition to SuperLU |
| 828 | ## # - Perhaps make available particularily versatile algorithms from |
| 829 | ## # the endless list of iterative methods...and perhaps not |
| 830 | |
| 831 | |
| 832 | |
| 833 | |
| 834 | def _transpose_index(idx): |
| 835 | return (idx[1], idx[0]) |