5

I am working on an algorithm that uses diagonal and first off-diagonal blocks of a large (will be e06 x e06) block diagonal sparse matrix.

Right now I create a dict that stores the blocks in such a way that I can access the blocks in a matrix like fashion. For example B[0,0](5x5) gives the first block of matrix A(20x20), assuming 5x5 blocks and that matrix A is of type sparse.lil.

This works fine but takes horribly long too run. It is inefficient because it copies the data, as this reference revealed to my astonishment: GetItem Method

Is there a way to only store a view on a sparse matrix in a dict? I would like to change the content and still be able to use the same identifiers. It is fine if it takes a little longer as it should only be done once. The blocks will have many different dimensions and shapes.

ire_and_curses
  • 68,372
  • 23
  • 116
  • 141
Jonas
  • 145
  • 1
  • 4

1 Answers1

4

As far as I know, all of the various sparse matricies in scipy.sparse return copies rather than a view of some sort. (Some of the others may be significantly faster at doing so than lil_matrix, though!)

One way of doing what you want is to just work with slice objects. For example:

import scipy.sparse

class SparseBlocks(object):
    def __init__(self, data, chunksize=5):
        self.data = data
        self.chunksize = chunksize
    def _convert_slices(self, slices):
        newslices = []
        for axslice in slices:
            if isinstance(axslice, slice):
                start, stop = axslice.start, axslice.stop
                if axslice.start is not None:
                    start *= self.chunksize
                if axslice.stop is not None:
                    stop *= self.chunksize
                axslice = slice(start, stop, None)
            elif axslice is not None:
                axslice = slice(axslice, axslice+self.chunksize)
            newslices.append(axslice)
        return tuple(newslices)

    def __getitem__(self, item):
        item = self._convert_slices(item)
        return self.data.__getitem__(item)
    def __setitem__(self, item, value):
        item = self._convert_slices(item)
        return self.data.__setitem__(item, value)

data = scipy.sparse.lil_matrix((20,20))
s = SparseBlocks(data)
s[0,0] = 1
print s.data

Now, whenever we modify s[whatever] it will modify s.data of the appropriate chunk. In other words, s[0,0] will return or set s.data[:5, :5], and so on.

Joe Kington
  • 275,208
  • 71
  • 604
  • 463
  • 1
    Nice, looks like just the thing i needed, sorry for the delay in answering i am trying to modify the code to accept multiple blocks of different size for one matrix. Don't help me just yet, i want to figure that out by myself :). If i run into further problems i will come back. Thanks – Jonas Jun 07 '11 at 15:50
  • Note that since version 0.11.0, SciPy has a (block diagonal function)[docs.scipy.org/doc/scipy/reference/generated/scipy.sparse.block_diag.html#scipy.sparse.block_diag] that looks to be helpful here. – drevicko Feb 25 '16 at 11:15