source: trunk/anuga_core/source/anuga/utilities/sparse.py @ 8710

Last change on this file since 8710 was 8154, checked in by steve, 14 years ago

Adding in kinematic viscosity. Added in some procedures to change boundary_values of
quantities

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