Path: blob/master/sage/matrix/matrix_generic_sparse.pyx
4048 views
r"""1Sparse Matrices over a general ring23EXAMPLES::45sage: R.<x> = PolynomialRing(QQ)6sage: M = MatrixSpace(QQ['x'],2,3,sparse=True); M7Full MatrixSpace of 2 by 3 sparse matrices over Univariate Polynomial Ring in x over Rational Field8sage: a = M(range(6)); a9[0 1 2]10[3 4 5]11sage: b = M([x^n for n in range(6)]); b12[ 1 x x^2]13[x^3 x^4 x^5]14sage: a * b.transpose()15[ 2*x^2 + x 2*x^5 + x^4]16[ 5*x^2 + 4*x + 3 5*x^5 + 4*x^4 + 3*x^3]17sage: pari(a)*pari(b.transpose())18[2*x^2 + x, 2*x^5 + x^4; 5*x^2 + 4*x + 3, 5*x^5 + 4*x^4 + 3*x^3]19sage: c = copy(b); c20[ 1 x x^2]21[x^3 x^4 x^5]22sage: c[0,0] = 5; c23[ 5 x x^2]24[x^3 x^4 x^5]25sage: b[0,0]26127sage: c.dict()28{(0, 1): x, (1, 2): x^5, (0, 0): 5, (1, 0): x^3, (0, 2): x^2, (1, 1): x^4}29sage: c.list()30[5, x, x^2, x^3, x^4, x^5]31sage: c.rows()32[(5, x, x^2), (x^3, x^4, x^5)]33sage: TestSuite(c).run()34sage: d = c.change_ring(CC['x']); d35[5.00000000000000 x x^2]36[ x^3 x^4 x^5]37sage: latex(c)38\left(\begin{array}{rrr}395 & x & x^{2} \\40x^{3} & x^{4} & x^{5}41\end{array}\right)42sage: c.sparse_rows()43[(5, x, x^2), (x^3, x^4, x^5)]44sage: d = c.dense_matrix(); d45[ 5 x x^2]46[x^3 x^4 x^5]47sage: parent(d)48Full MatrixSpace of 2 by 3 dense matrices over Univariate Polynomial Ring in x over Rational Field49sage: c.sparse_matrix() is c50True51sage: c.is_sparse()52True53"""5455cimport matrix56cimport matrix_sparse57cimport sage.structure.element58from sage.structure.element cimport ModuleElement5960import sage.misc.misc as misc6162cdef class Matrix_generic_sparse(matrix_sparse.Matrix_sparse):63r"""64The ``Matrix_generic_sparse`` class derives from65``Matrix``, and defines functionality for sparse66matrices over any base ring. A generic sparse matrix is represented67using a dictionary with keys pairs `(i,j)` and values in68the base ring.6970The values of the dictionary must never be zero.71"""72########################################################################73# LEVEL 1 functionality74# * __cinit__75# * __init__76# * __dealloc__77# * set_unsafe78# * get_unsafe79# * def _pickle80# * def _unpickle81########################################################################82def __cinit__(self, parent, entries=0, coerce=True, copy=True):83self._entries = {} # crucial so that pickling works8485def __init__(self, parent, entries=None, coerce=True, copy=True):86cdef Py_ssize_t i, j87matrix.Matrix.__init__(self, parent)8889R = self._base_ring90self._zero = R(0)91if not isinstance(entries, (list, dict)):92if entries is None:93x = R.zero_element()94else:95x = R(entries)96entries = {}97if x != self._zero:98if self._nrows != self._ncols:99raise TypeError, "scalar matrix must be square"100for i from 0 <= i < self._nrows:101entries[(int(i),int(i))] = x102103if isinstance(entries, list) and len(entries) > 0 and \104hasattr(entries[0], "is_vector"):105entries = _convert_sparse_entries_to_dict(entries)106107if isinstance(entries, list):108if len(entries) != self.nrows() * self.ncols():109raise TypeError, "entries has the wrong length"110x = entries111entries = {}112k = 0113for i from 0 <= i < self._nrows:114for j from 0 <= j < self._ncols:115if x[k] != 0:116entries[(int(i),int(j))] = x[k]117k = k + 1118copy = False119120if not isinstance(entries, dict):121raise TypeError, "entries must be a dict"122123if coerce:124try:125v = {}126for k, x in entries.iteritems():127w = R(x)128if w != 0:129v[k] = w130entries = v131except TypeError:132raise TypeError, "Unable to coerce entries to %s"%R133else:134if copy:135# Make a copy136entries = dict(entries)137for k in entries.keys():138if entries[k].is_zero():139del entries[k]140141self._entries = entries142143cdef set_unsafe(self, Py_ssize_t i, Py_ssize_t j, value):144# TODO: maybe make faster with Python/C API145k = (int(i),int(j))146if value.is_zero():147try:148del self._entries[k]149except KeyError:150pass151else:152self._entries[k] = value153154cdef get_unsafe(self, Py_ssize_t i, Py_ssize_t j):155# TODO: maybe make faster with Python/C API156try:157return self._entries[(int(i),int(j))]158except KeyError:159return self._zero160161def _pickle(self):162version = 0163return self._entries, version164165def _unpickle(self, data, int version):166"""167EXAMPLES::168169sage: a = matrix([[1,10],[3,4]],sparse=True); a170[ 1 10]171[ 3 4]172sage: loads(dumps(a)) == a173True174"""175cdef Py_ssize_t i, j, k176177if version == 0:178self._entries = data179self._zero = self._base_ring(0)180else:181raise RuntimeError, "unknown matrix version (=%s)"%version182183def __richcmp__(matrix.Matrix self, right, int op): # always need for mysterious reasons.184return self._richcmp(right, op)185def __hash__(self):186return self._hash()187188########################################################################189# LEVEL 2 functionality190# x * cdef _add_191# * cdef _mul_192# * cdef _cmp_c_impl193# * __neg__194# * __invert__195# x * __copy__196# * _multiply_classical197# x * _list -- copy of the list of underlying elements198# x * _dict -- copy of the sparse dictionary of underlying elements199########################################################################200201cpdef ModuleElement _add_(self, ModuleElement _other):202"""203EXAMPLES::204205sage: a = matrix([[1,10],[3,4]],sparse=True); a206[ 1 10]207[ 3 4]208sage: a+a209[ 2 20]210[ 6 8]211212::213214sage: a = matrix([[1,10,-5/3],[2/8,3,4]],sparse=True); a215[ 1 10 -5/3]216[ 1/4 3 4]217sage: a+a218[ 2 20 -10/3]219[ 1/2 6 8]220"""221# Compute the sum of two sparse matrices.222# This is complicated because of how we represent sparse matrices.223# Algorithm:224# 1. Sort both entry coefficient lists.225# 2. March through building a new list, adding when the two i,j are the same.226cdef Py_ssize_t i, j, len_v, len_w227cdef Matrix_generic_sparse other228other = <Matrix_generic_sparse> _other229v = self._entries.items()230v.sort()231w = other._entries.items()232w.sort()233s = {}234i = 0 # pointer into self235j = 0 # pointer into other236len_v = len(v) # could be sped up with Python/C API??237len_w = len(w)238while i < len_v and j < len_w:239vi = v[i][0]240wj = w[j][0]241if vi < wj:242s[vi] = v[i][1]243i = i + 1244elif vi > wj:245s[wj] = w[j][1]246j = j + 1247else: # equal248sm = v[i][1] + w[j][1]249if not sm.is_zero():250s[vi] = sm251i = i + 1252j = j + 1253while i < len(v):254s[v[i][0]] = v[i][1]255i = i + 1256while j < len(w):257s[w[j][0]] = w[j][1]258j = j + 1259260cdef Matrix_generic_sparse A261A = Matrix_generic_sparse.__new__(Matrix_generic_sparse, self._parent, 0,0,0)262matrix.Matrix.__init__(A, self._parent)263A._entries = s264A._zero = self._zero265A._base_ring = self._base_ring266return A267268def __copy__(self):269A = self.__class__(self._parent, self._entries, copy = True, coerce=False)270if self._subdivisions is not None:271A.subdivide(*self.subdivisions())272return A273274275def _list(self):276"""277Return all entries of self as a list of numbers of rows times278number of columns entries.279"""280x = self.fetch('list')281if not x is None:282return x283v = [self._base_ring(0)]*(self._nrows * self._ncols)284for ij, x in self._entries.iteritems():285i, j = ij # todo: optimize286v[i*self._ncols + j] = x287self.cache('list', v)288return v289290def _dict(self):291"""292Return the underlying dictionary of self.293"""294return self._entries295296########################################################################297# LEVEL 3 functionality -- matrix windows, etc.298########################################################################299300def _nonzero_positions_by_row(self, copy=True):301x = self.fetch('nonzero_positions')302if x is not None:303if copy:304return list(x)305return x306307v = self._entries.keys()308v.sort()309310self.cache('nonzero_positions', v)311if copy:312return list(v)313314return v315316def _nonzero_positions_by_column(self, copy=True):317x = self.fetch('nonzero_positions_by_column')318if x is not None:319if copy:320return list(x)321return x322323v = self._entries.keys()324v.sort(_cmp_backward)325326self.cache('nonzero_positions_by_column', v)327if copy:328return list(v)329return v330331######################332# Echelon support333######################334335336## def dense_matrix(self):337## import sage.matrix.matrix338## M = sage.matrix.matrix.MatrixSpace(self.base_ring(),339## self._nrows, self._ncols,340## sparse = False)(0)341## for i, j, x in self._entries342## M[i,j] = x343## return M344345## def nonpivots(self):346## # We convert the pivots to a set so we have fast347## # inclusion testing348## X = set(self.pivots())349## # [j for j in xrange(self.ncols()) if not (j in X)]350## np = []351## for j in xrange(self.ncols()):352## if not (j in X):353## np.append(j)354## return np355356## def matrix_from_nonpivot_columns(self):357## """358## The sparse matrix got by deleted all pivot columns.359## """360## return self.matrix_from_columns(self.nonpivots())361362## def matrix_from_columns(self, columns):363## """364## Returns the sparse submatrix of self composed of the given365## list of columns.366367## INPUT:368## columns -- list of int's369## OUTPUT:370## a sparse matrix.371## """372## # Let A be this matrix and let B denote this matrix with373## # the columns deleted.374## # ALGORITHM:375## # 1. Make a table that encodes the function376## # f : Z --> Z,377## # f(j) = column of B where the j-th column of A goes378## # 2. Build new list of entries and return resulting matrix.379## C = set(columns)380## X = []381## j = 0382## for i in xrange(self.ncols()):383## if i in C:384## X.append(j)385## j = j + 1386## else:387## X.append(-1) # column to be deleted.388## entries2 = []389## for i, j, x in self.entries():390## if j in C:391## entries2.append((i,X[j],x))392## return SparseMatrix(self.base_ring(), self.nrows(),393## len(C), entries2, coerce=False, sort=False)394395## def matrix_from_rows(self, rows):396## """397## Returns the sparse submatrix of self composed of the given398## list of rows.399400## INPUT:401## rows -- list of int's402## OUTPUT:403## a sparse matrix.404## """405## R = set(rows)406## if not R.issubset(set(xrange(self.nrows()))):407## raise ArithmeticError, "Invalid rows."408## X = []409## i = 0410## for j in xrange(self.nrows()):411## if j in R:412## X.append(i)413## i = i + 1414## else:415## X.append(-1) # row to be deleted.416## entries2 = []417## for i, j, x in self.entries():418## if i in R:419## entries2.append((X[i],j,x))420## return SparseMatrix(self.base_ring(), len(R),421## self.ncols(), entries2, coerce=False, sort=False)422423424## def transpose(self):425## """426## Returns the transpose of self.427## """428## entries2 = [] # [(j,i,x) for i,j,x in self.entries()]429## for i,j,x in self.entries():430## entries2.append((j,i,x))431## return SparseMatrix(self.base_ring(), self.ncols(),432## self.nrows(), entries2, coerce=False, sort=True)433434## def __rmul__(self, left):435## return self.scalar_multiple(left)436437## def scalar_multiple(self, left):438## R = self.base_ring()439## left = R(left)440## if left == R(1):441## return self442## if left == R(0):443## return SparseMatrix(R, self.nrows(), self.ncols(), coerce=False, sort=False)444445## X = []446## for i, j, x in self.list():447## X.append((i,j,left*x))448## return SparseMatrix(self.base_ring(), self.nrows(),449## self.ncols(), X, coerce=False, sort=False)450451452####################################################################################453# Various helper functions454####################################################################################455import matrix_space456457def Matrix_sparse_from_rows(X):458"""459INPUT:460461462- ``X`` - nonempty list of SparseVector rows463464465OUTPUT: Sparse_matrix with those rows.466467EXAMPLES::468469sage: V = VectorSpace(QQ,20,sparse=True)470sage: v = V(0)471sage: v[9] = 4472sage: from sage.matrix.matrix_generic_sparse import Matrix_sparse_from_rows473sage: Matrix_sparse_from_rows([v])474[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]475sage: Matrix_sparse_from_rows([v, v, v, V(0)])476[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]477[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]478[0 0 0 0 0 0 0 0 0 4 0 0 0 0 0 0 0 0 0 0]479[0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0]480"""481cdef Py_ssize_t i, j482483if not isinstance(X, (list, tuple)):484raise TypeError, "X (=%s) must be a list or tuple"%X485if len(X) == 0:486raise ArithmeticError, "X must be nonempty"487entries = {}488R = X[0].base_ring()489ncols = X[0].degree()490for i from 0 <= i < len(X):491for j, x in X[i].iteritems():492entries[(i,j)] = x493M = matrix_space.MatrixSpace(R, len(X), ncols, sparse=True)494return M(entries, coerce=False, copy=False)495496497## def _sparse_dot_product(v, w):498## """499## INPUT:500## v and w are dictionaries with integer keys.501## """502## x = set(v.keys()).intersection(set(w.keys()))503## a = 0504## for k in x:505## a = a + v[k]*w[k]506## return a507508cdef _convert_sparse_entries_to_dict(entries):509e = {}510for i in xrange(len(entries)):511for j, x in (entries[i].dict()).iteritems():512e[(i,j)] = x513return e514515516517def _cmp_backward(x,y): # todo: speed up via Python/C API518cdef int c519# compare two 2-tuples, but in reverse order, i.e., second entry than first520c = cmp(x[1],y[1])521if c: return c522c = cmp(x[0],y[0])523if c: return c524return 0525526527