source: inundation/pyvolution/sparse.py @ 1740

Last change on this file since 1740 was 1632, checked in by ole, 19 years ago

Interpolation work and some refactoring

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