[Scipy-svn] r3908 - in trunk/scipy/sparse: . tests

scipy-svn at scipy.org scipy-svn at scipy.org
Sat Feb 9 02:25:30 EST 2008


Author: wnbell
Date: 2008-02-09 01:25:21 -0600 (Sat, 09 Feb 2008)
New Revision: 3908

Modified:
   trunk/scipy/sparse/construct.py
   trunk/scipy/sparse/tests/test_construct.py
Log:
added sparse.bmat
partially addresses ticket #602


Modified: trunk/scipy/sparse/construct.py
===================================================================
--- trunk/scipy/sparse/construct.py	2008-02-09 05:27:55 UTC (rev 3907)
+++ trunk/scipy/sparse/construct.py	2008-02-09 07:25:21 UTC (rev 3908)
@@ -2,14 +2,17 @@
 """
 
 
-__all__ = [ 'spdiags','speye','spidentity','spkron', 'lil_eye', 'lil_diags' ]
+__all__ = [ 'spdiags','speye','spidentity','spkron', 'bmat', 'lil_eye', 'lil_diags' ]
 
 from itertools import izip
 from warnings import warn
 
 import numpy
-from numpy import ones, clip, array, arange, intc
+from numpy import ones, clip, array, arange, intc, asarray, rank, zeros, \
+        cumsum, concatenate, empty
 
+from sputils import upcast
+
 from csr import csr_matrix, isspmatrix_csr
 from csc import csc_matrix, isspmatrix_csc
 from bsr import bsr_matrix
@@ -228,3 +231,104 @@
     return out
 
 
+def bmat( blocks, format=None, dtype=None ):
+    """
+    Build a sparse matrix from sparse sub-blocks
+
+    Parameters
+    ==========
+
+    blocks -- grid of sparse matrices with compatible shapes
+            - an entry of None implies an all-zero matrix
+    format -- sparse format of the result (e.g. "csr")
+            -  by default an appropriate sparse matrix format is returned.
+               This choice is subject to change.
+
+   
+    Example
+    =======
+
+    >>> from scipy.sparse import coo_matrix, bmat
+    >>> A = coo_matrix([[1,2],[3,4]])
+    >>> B = coo_matrix([[5],[6]])
+    >>> C = coo_matrix([[7]])
+    >>> bmat( [[A,B],[None,C]] ).todense()
+    matrix([[1, 2, 5],
+            [3, 4, 6],
+            [0, 0, 7]])
+ 
+    >>> bmat( [[A,None],[None,C]] ).todense()
+    matrix([[1, 2, 0],
+            [3, 4, 0],
+            [0, 0, 7]])
+
+
+    """
+    
+    blocks = asarray(blocks, dtype='object')
+
+    if rank(blocks) != 2:
+        raise ValueError('blocks must have rank 2')
+
+    M,N = blocks.shape
+
+    block_mask   = zeros( blocks.shape, dtype='bool' )
+    brow_lengths = zeros( blocks.shape[0], dtype=int )
+    bcol_lengths = zeros( blocks.shape[1], dtype=int )
+
+    # convert everything to COO format
+    for i in range(M):
+        for j in range(N):
+            if blocks[i,j] is not None:
+                A = coo_matrix(blocks[i,j])
+                blocks[i,j] = A
+                block_mask[i,j] = True
+
+                if brow_lengths[i] == 0:
+                    brow_lengths[i] = A.shape[0]
+                else:
+                    if brow_lengths[i] != A.shape[0]:
+                        raise ValueError('blocks[%d,:] has incompatible row dimensions' % i)
+
+                if bcol_lengths[j] == 0:
+                    bcol_lengths[j] = A.shape[1]
+                else:
+                    if bcol_lengths[j] != A.shape[0]:
+                        raise ValueError('blocks[:,%d] has incompatible column dimensions' % j)
+
+
+    # ensure that at least one value in each row and col is not None
+    if brow_lengths.min() == 0:
+        raise ValueError('blocks[%d,:] is all None' % brow_lengths.argmin() )
+    if bcol_lengths.min() == 0:
+        raise ValueError('blocks[:,%d] is all None' % bcol_lengths.argmin() )
+    
+    nnz = sum([ A.nnz for A in blocks[block_mask] ]) 
+    if dtype is None:
+        dtype = upcast( *tuple([A.dtype for A in blocks[block_mask]]) )
+
+    row_offsets = concatenate(([0],cumsum(brow_lengths)))
+    col_offsets = concatenate(([0],cumsum(bcol_lengths)))
+
+    data = empty(nnz, dtype=dtype)
+    row  = empty(nnz, dtype=intc)
+    col  = empty(nnz, dtype=intc)
+
+    nnz = 0
+    for i in range(M):
+        for j in range(N):
+            if blocks[i,j] is not None:
+                A = blocks[i,j]
+                data[nnz:nnz + A.nnz] = A.data
+                row[nnz:nnz + A.nnz]  = A.row
+                col[nnz:nnz + A.nnz]  = A.col
+
+                row[nnz:nnz + A.nnz] += row_offsets[i]
+                col[nnz:nnz + A.nnz] += col_offsets[j]
+                
+                nnz += A.nnz
+
+    shape = (sum(brow_lengths),sum(bcol_lengths)) 
+    return coo_matrix( (data, (row, col)), shape=shape )
+
+

Modified: trunk/scipy/sparse/tests/test_construct.py
===================================================================
--- trunk/scipy/sparse/tests/test_construct.py	2008-02-09 05:27:55 UTC (rev 3907)
+++ trunk/scipy/sparse/tests/test_construct.py	2008-02-09 07:25:21 UTC (rev 3908)
@@ -1,13 +1,13 @@
 """test sparse matrix construction functions"""
 
-from numpy import array, kron
+from numpy import array, matrix, kron
 from scipy.testing import *
 
 
-from scipy.sparse import csr_matrix, \
-     spidentity, speye, spkron, spdiags, \
-     lil_eye, lil_diags
+from scipy.sparse import csr_matrix, coo_matrix
 
+from scipy.sparse.construct import *
+
 #TODO check whether format=XXX is respected
 
 class TestConstructUtils(TestCase):
@@ -101,6 +101,30 @@
 
                 assert_array_equal(result,expected)
 
+    def test_bmat(self):
+
+        A = coo_matrix([[1,2],[3,4]])
+        B = coo_matrix([[5],[6]])
+        C = coo_matrix([[7]])
+
+        expected = matrix([[1, 2, 5],
+                           [3, 4, 6],
+                           [0, 0, 7]])
+        assert_equal( bmat( [[A,B],[None,C]] ).todense(), expected )
+
+ 
+        expected = matrix([[1, 2, 0],
+                           [3, 4, 0],
+                           [0, 0, 7]])
+        assert_equal( bmat( [[A,None],[None,C]] ).todense(), expected )
+    
+        expected = matrix([[0, 5],
+                           [0, 6],
+                           [7, 0]])
+        assert_equal( bmat( [[None,B],[C,None]] ).todense(), expected )
+    
+        #TODO test failure cases
+
     def test_lil_diags(self):
         assert_array_equal(lil_diags([[1,2,3],[4,5],[6]],
                                      [0,1,2],(3,3)).todense(),




More information about the Scipy-svn mailing list