source: anuga_core/source/anuga/utilities/sparse.py @ 5053

Last change on this file since 5053 was 4859, checked in by duncan, 17 years ago

check the last triangle fit/interp speed up. Upgrading the code to time runs

File size: 9.4 KB
Line 
1"""Proof of concept sparse matrix code
2"""
3
4
5class Sparse:
6
7    def __init__(self, *args):
8        """Create sparse matrix.
9        There are two construction forms
10        Usage:
11
12        Sparse(A)     #Creates sparse matrix from dense matrix A
13        Sparse(M, N)  #Creates empty MxN sparse matrix
14        """
15
16        self.Data = {}
17           
18        if len(args) == 1:
19            from Numeric import array
20            try:
21                A = array(args[0])
22            except:
23                raise 'Input must be convertable to a Numeric array'
24
25            assert len(A.shape) == 2, 'Input must be a 2d matrix'
26           
27            self.M, self.N = A.shape
28            for i in range(self.M):
29                for j in range(self.N):
30                    if A[i, j] != 0.0:
31                        self.Data[i, j] = A[i, j]
32               
33           
34        elif len(args) == 2:
35            self.M = args[0]
36            self.N = args[1]
37        else:
38            raise 'Invalid construction'
39           
40        self.shape = (self.M, self.N) 
41
42
43    def __repr__(self):
44        return '%d X %d sparse matrix:\n' %(self.M, self.N) + `self.Data`
45
46    def __len__(self):
47        """Return number of nonzeros of A
48        """
49        return len(self.Data)
50
51    def nonzeros(self):
52        """Return number of nonzeros of A
53        """       
54        return len(self)
55   
56    def __setitem__(self, key, x):
57
58        i,j = key
59        # removing these asserts will not speed things up
60        assert 0 <= i < self.M
61        assert 0 <= j < self.N       
62
63        if x != 0:
64            self.Data[key] = float(x)
65        else:
66            if self.Data.has_key( key ):           
67                del self.Data[key]
68
69    def __getitem__(self, key):
70       
71        i,j = key
72        # removing these asserts will not speed things up
73        assert 0 <= i < self.M
74        assert 0 <= j < self.N               
75
76        if self.Data.has_key( key ):
77            return self.Data[ key ]
78        else:
79            return 0.0
80
81    def copy(self):
82        #FIXME: Use the copy module instead
83        new = Sparse(self.M,self.N)
84
85        for key in self.Data.keys():
86            i, j = key
87
88            new[i,j] = self.Data[i,j]
89
90        return new
91
92
93    def todense(self):
94        from Numeric import zeros, Float
95
96        D = zeros( (self.M, self.N), Float)
97       
98        for i in range(self.M):
99            for j in range(self.N):
100                if self.Data.has_key( (i,j) ):               
101                    D[i, j] = self.Data[ (i,j) ]
102        return D
103
104
105   
106    def __mul__(self, other):
107        """Multiply this matrix onto 'other' which can either be
108        a Numeric vector, a Numeric matrix or another sparse matrix.
109        """
110
111        from Numeric import array, zeros, Float
112       
113        try:
114            B = array(other)
115        except:
116            msg = 'FIXME: Only Numeric types implemented so far'
117            raise msg
118           
119
120        #Assume numeric types from now on
121       
122        if len(B.shape) == 0:
123            #Scalar - use __rmul__ method
124            R = B*self
125           
126        elif len(B.shape) == 1:
127            #Vector
128            assert B.shape[0] == self.N, 'Mismatching dimensions'
129
130            R = zeros(self.M, Float) #Result
131           
132            #Multiply nonzero elements
133            for key in self.Data.keys():
134                i, j = key
135
136                R[i] += self.Data[key]*B[j]
137        elif len(B.shape) == 2:
138       
139           
140            R = zeros((self.M, B.shape[1]), Float) #Result matrix
141
142            #Multiply nonzero elements
143            for col in range(R.shape[1]):
144                #For each column
145               
146                for key in self.Data.keys():
147                    i, j = key
148
149                    R[i, col] += self.Data[key]*B[j, col]
150           
151           
152        else:
153            raise ValueError, 'Dimension too high: d=%d' %len(B.shape)
154
155        return R
156   
157
158    def __add__(self, other):
159        """Add this matrix onto 'other'
160        """
161
162        from Numeric import array, zeros, Float
163       
164        new = other.copy()
165        for key in self.Data.keys():
166            i, j = key
167
168            new[i,j] += self.Data[key]
169
170        return new
171
172
173    def __rmul__(self, other):
174        """Right multiply this matrix with scalar
175        """
176
177        from Numeric import array, zeros, Float
178       
179        try:
180            other = float(other)
181        except:
182            msg = 'Sparse matrix can only "right-multiply" onto a scalar'
183            raise TypeError, msg
184        else:
185            new = self.copy()
186            #Multiply nonzero elements
187            for key in new.Data.keys():
188                i, j = key
189
190                new.Data[key] = other*new.Data[key]
191
192        return new
193
194
195    def trans_mult(self, other):
196        """Multiply the transpose of matrix with 'other' which can be
197        a Numeric vector.
198        """
199
200        from Numeric import array, zeros, Float
201       
202        try:
203            B = array(other)
204        except:
205            print 'FIXME: Only Numeric types implemented so far'
206
207
208        #Assume numeric types from now on
209        if len(B.shape) == 1:
210            #Vector
211
212            assert B.shape[0] == self.M, 'Mismatching dimensions'
213
214            R = zeros((self.N,), Float) #Result
215
216            #Multiply nonzero elements
217            for key in self.Data.keys():
218                i, j = key
219
220                R[j] += self.Data[key]*B[i]
221
222        else:
223            raise 'Can only multiply with 1d array'
224
225        return R
226
227class Sparse_CSR:
228
229    def __init__(self, A):
230        """Create sparse matrix in csr format.
231
232        Sparse_CSR(A) #creates csr sparse matrix from sparse matrix
233        Matrices are not built using this format, since it's painful to
234        add values to an existing sparse_CSR instance (hence there are no
235        objects to do this.)
236
237        Rather, build a matrix, and convert it to this format for a speed
238        increase.
239
240        data - a 1D array of the data
241        Colind - The ith item in this 1D array is the column index of the
242                 ith data in the data array
243        rowptr - 1D array, with the index representing the row of the matrix.
244                 The item in the row represents the index into colind of the
245                 first data value of this row.
246                 Regard it as a pointer into the colind array, for the ith row.
247
248                 
249        """
250
251        from Numeric import array, Float, Int
252
253        if isinstance(A,Sparse):
254
255            from Numeric import zeros
256            keys = A.Data.keys()
257            keys.sort()
258            nnz = len(keys)
259            data    = zeros ( (nnz,), Float)
260            colind  = zeros ( (nnz,), Int)
261            row_ptr = zeros ( (A.M+1,), Int)
262            current_row = -1
263            k = 0
264            for key in keys:
265                ikey0 = int(key[0])
266                ikey1 = int(key[1])
267                if ikey0 != current_row:
268                    current_row = ikey0
269                    row_ptr[ikey0] = k
270                data[k] = A.Data[key]
271                colind[k] = ikey1
272                k += 1
273            for row in range(current_row+1, A.M+1):
274                row_ptr[row] = nnz
275            #row_ptr[-1] = nnz
276       
277            self.data    = data
278            self.colind  = colind
279            self.row_ptr = row_ptr
280            self.M       = A.M
281            self.N       = A.N
282        else:
283            raise ValueError, "Sparse_CSR(A) expects A == Sparse Matrix"
284           
285    def __repr__(self):
286        return '%d X %d sparse matrix:\n' %(self.M, self.N) + `self.data`
287
288    def __len__(self):
289        """Return number of nonzeros of A
290        """
291        return self.row_ptr[-1]
292
293    def nonzeros(self):
294        """Return number of nonzeros of A
295        """       
296        return len(self)
297
298    def todense(self):
299        from Numeric import zeros, Float
300
301        D = zeros( (self.M, self.N), Float)
302       
303        for i in range(self.M):
304            for ckey in range(self.row_ptr[i],self.row_ptr[i+1]):
305                j = self.colind[ckey]
306                D[i, j] = self.data[ckey]
307        return D
308
309    def __mul__(self, other):
310        """Multiply this matrix onto 'other' which can either be
311        a Numeric vector, a Numeric matrix or another sparse matrix.
312        """
313
314        from Numeric import array, zeros, Float
315       
316        try:
317            B = array(other)
318        except:
319            print 'FIXME: Only Numeric types implemented so far'
320
321        return csr_mv(self,B) 
322
323
324# Setup for C extensions
325from anuga.utilities import compile
326if compile.can_use_C_extension('sparse_ext.c'):
327    # Access underlying c implementations
328    from sparse_ext import csr_mv
329
330
331if __name__ == '__main__':
332    # A little selftest
333   
334    from Numeric import allclose, array, Float
335   
336    A = Sparse(3,3)
337
338    A[1,1] = 4
339
340
341    print A
342    print A.todense()
343
344    A[1,1] = 0
345
346    print A
347    print A.todense()   
348
349    A[1,2] = 0
350
351
352    A[0,0] = 3
353    A[1,1] = 2
354    A[1,2] = 2
355    A[2,2] = 1
356
357    print A
358    print A.todense()
359
360
361    #Right hand side vector
362    v = [2,3,4]
363
364    u = A*v
365    print u
366    assert allclose(u, [6,14,4])
367
368    u = A.trans_mult(v)
369    print u
370    assert allclose(u, [6,6,10])
371
372    #Right hand side column
373    v = array([[2,4],[3,4],[4,4]])
374
375    u = A*v[:,0]
376    assert allclose(u, [6,14,4])
377
378    #u = A*v[:,1]
379    #print u
380    print A.shape
381
382    B = 3*A
383    print B.todense()
384
385    B[1,0] = 2
386
387    C = A+B
388
389    print C.todense()
390
391    C = Sparse_CSR(C)
392
393    y = C*[6,14,4]
394
395    print y
396
397    y2 = C*[[6,4],[4,28],[4,8]]
398
399    print y2
Note: See TracBrowser for help on using the repository browser.