source: inundation/ga/storm_surge/pyvolution/sparse.py @ 530

Last change on this file since 530 was 485, checked in by ole, 20 years ago

Cleaned up is sparse and least squares.
Verified that quad trees work.
Implemented sparse matrix x matrix mult and simplified
interpolate in least_squares.
Added more unit testing.

File size: 5.9 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.A = {}
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.A[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.A`
48
49    def __len__(self):
50        """Return number of nonzeros of A
51        """
52        return len(self.A)
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.A[key] = float(x)
67        else:
68            if self.A.has_key( key ):           
69                del self.A[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.A.has_key( key ):
78            return self.A[ 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.A.keys():
87            i, j = key
88
89            new[i,j] = self.A[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.A.has_key( (i,j) ):               
102                    D[i, j] = self.A[ (i,j) ]
103        return D
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            print 'FIXME: Only Numeric types implemented so far'
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            assert B.shape[0] == self.N, 'Mismatching dimensions'
127
128            R = zeros(self.M, Float) #Result
129           
130            #Multiply nonzero elements
131            for key in self.A.keys():
132                i, j = key
133
134                R[i] += self.A[key]*B[j]
135        elif len(B.shape) == 2:
136       
137           
138            R = zeros((self.M, B.shape[1]), Float) #Result matrix
139
140            #Multiply nonzero elements
141            for col in range(R.shape[1]):
142                #For each column
143               
144                for key in self.A.keys():
145                    i, j = key
146
147                    R[i, col] += self.A[key]*B[j, col]
148           
149           
150        else:
151            raise ValueError, 'Dimension too high: d=%d' %len(B.shape)
152
153        return R
154   
155
156    def __add__(self, other):
157        """Add this matrix onto 'other'
158        """
159
160        from Numeric import array, zeros, Float
161       
162        new = other.copy()
163        for key in self.A.keys():
164            i, j = key
165
166            new[i,j] += self.A[key]
167
168        return new
169
170
171    def __rmul__(self, other):
172        """Right multiply this matrix with scalar
173        """
174
175        from Numeric import array, zeros, Float
176       
177        try:
178            other = float(other)
179        except:
180            msg = 'Sparse matrix can only "right-multiply" onto a scalar'
181            raise TypeError, msg
182        else:
183            new = self.copy()
184            #Multiply nonzero elements
185            for key in new.A.keys():
186                i, j = key
187
188                new.A[key] = other*new.A[key]
189
190        return new
191
192
193    def trans_mult(self, other):
194        """Multiply the transpose of matrix with 'other' which can be
195        a Numeric vector.
196        """
197
198        from Numeric import array, zeros, Float
199       
200        try:
201            B = array(other)
202        except:
203            print 'FIXME: Only Numeric types implemented so far'
204
205
206        #Assume numeric types from now on
207        if len(B.shape) == 1:
208            #Vector
209
210            assert B.shape[0] == self.M, 'Mismatching dimensions'
211
212            R = zeros((self.N,), Float) #Result
213
214            #Multiply nonzero elements
215            for key in self.A.keys():
216                i, j = key
217
218                R[j] += self.A[key]*B[i]
219
220        else:
221            raise 'Can only multiply with 1d array'
222
223        return R
224
225
226   
227   
228if __name__ == '__main__':
229
230    from Numeric import allclose, array, Float
231   
232    A = Sparse(3,3)
233
234    A[1,1] = 4
235
236
237    print A
238    print A.todense()
239
240    A[1,1] = 0
241
242    print A
243    print A.todense()   
244
245    A[1,2] = 0
246
247
248    A[0,0] = 3
249    A[1,1] = 2
250    A[1,2] = 2
251    A[2,2] = 1
252
253    print A
254    print A.todense()
255
256
257    #Right hand side vector
258    v = [2,3,4]
259
260    u = A*v
261    print u
262    assert allclose(u, [6,14,4])
263
264    u = A.trans_mult(v)
265    print u
266    assert allclose(u, [6,6,10])
267
268    #Right hand side column
269    v = array([[2,4],[3,4],[4,4]])
270
271    u = A*v[:,0]
272    assert allclose(u, [6,14,4])
273
274    #u = A*v[:,1]
275    #print u
276    print A.shape
277
278    B = 3*A
279    print B.todense()
280
281    B[1,0] = 2
282
283    C = A+B
284
285    print C.todense()
Note: See TracBrowser for help on using the repository browser.