2
# objdoc: Block Linear Algebra module
4
# modified by Kent-Andre Mardal
7
Python block matrices and block vectors. This module provides three classes for block linear algebra systems in
8
Python. The main class, C{ BlockMatrix} implements a dense block matrix,
9
C{DiagBlockMatrix} implements a block diagonal matrix (used e.g. for
10
preconditioning, and C{BlockVector} is a simple block vector consiting of
14
import operator, numpy, math
16
class ZeroBlock(object):
18
This class is a zeros block matrix used to fill upp empty blocks
22
def __init__(self, *args):
23
"""Input arguments are two integer values
24
specifying the size of the block"""
25
if isinstance(args[0], int) and isinstance(args[1], int):
31
print "Wrong input value in ZeroBlock"
35
"""Return the number of block rows and columns."""
36
return (self.n, self.m)
38
shape = property(shape)
40
def __mul__(self, other):
41
"""Matrix vector product."""
45
class BlockMatrix(object):
46
"""A simple block matrix implementation in Python. The blocks are typically
47
matrices implemented in compile languages and later exposed to Python
48
either manually or using some sort of wrapper generator software."""
50
def __init__(self, *args):
51
"""Input arguments are either two integer values
52
specifying the number of block rows and columns, or a nested Python
53
list containing the block elements directly."""
54
if isinstance(args[0], int):
59
self.rdim = self.cdim = None
63
self.data[i].append(0)
64
elif isinstance(args[0], (list, tuple)):
67
self.data = list(args)
74
rdims = numpy.zeros((m,n), dtype='l')
75
cdims = numpy.zeros((m,n), dtype='l')
79
if not isinstance(mat, int):
80
_m, _n = mat.size(0), mat.size(1)
84
rdim = numpy.zeros(m, dtype="l")
85
cdim = numpy.zeros(n, dtype="l")
88
row = numpy.where(row==0, max(row), row)
89
if min(row) == max(row):
92
raise RuntimeError, "Wrong row dimensions in block matrix"
95
col = numpy.where(col==0, max(col), col)
96
if min(col) == max(col):
99
raise RuntimeError, "Wrong col dimensions in block matrix"
104
if isinstance(self.data[i][j], int):
106
self.data[i][j] = Misc.genZeroBlock(self.rdim[i])
107
self.data[i][j].n = self.cdim[j]
110
def row_dim(self, i):
113
def col_dim(self, j):
117
"""Return the number of block rows and columns."""
118
return (self.n, self.m)
120
shape = property(shape)
123
"""Pretty print (ugly)."""
124
return str(self.data)
127
"""Return string capable of copying the object when calling
128
C{eval(repr(self))}"""
129
return "BlockMatrix(%d, %d)" % (self.n, self.m)
131
def __setitem__(self, idx, entry):
132
"""Set a block matrix entry."""
133
if (self._check_tuple(idx)):
134
i = idx[0]; j = idx[1]
135
self.data[i][j] = entry
137
def __getitem__(self, idx):
138
"""Get a block matrix entry."""
139
if (self._check_tuple(idx)):
140
return self.data[idx[0]][idx[1]]
142
def __add__(self, other):
143
"""Add two block matrices."""
144
if (not isinstance(other, BlockMatrix)):
145
raise TypeError, "Can not add a BlockMatrix and a %s" %(str(other))
146
if (not self.shape == other.shape):
147
raise ValueError, "The BlockMatrices have different shapes: %s, %s" % (str(self.shape), str(other.shape))
148
b = Blockmatrix(self.n, self.m)
149
for i in range(self.n):
150
for j in range(self.m):
151
b[i,j] = self[i]+other[j]
154
def __mul__(self, other):
155
"""Matrix vector product."""
156
if (not isinstance(other, BlockVector)):
157
raise TypeError, "Can not multiply BlockMatrix with %s" % (str(type(other)))
158
if (not other.n == self.m):
159
raise ValueError, "The length of the BlockVector (%d) does not match the dimention of the BlockMatrix (%d, %d)" % (other.n, self.n, self.m)
162
if self.rdim == None:
164
for i in range(self.n):
166
rhs_i = other.data[i].copy()
168
res.data.append(rhs_i)
172
for j in range(self.m):
173
self.data[i][j].mult(other.data[j], tmp)
177
def prod(self, x, b,transposed=False): #Compute b = A*x
179
if (not isinstance(x, BlockVector)):
180
raise TypeError, "Can not multiply BlockMatrix with %s" % (str(type(other)))
181
if (not x.n == self.m):
182
raise ValueError, "The length of the BlockVector (%d) does not match the dimention of the BlockMatrix (%d, %d)" % (x.n, self.n, self.m)
184
for i in range(self.n):
187
for j in range(self.m):
189
self.data[i][j].mult(x.data[j], tmp, transposed)
191
self.data[j][i].mult(x.data[j], tmp, transposed)
196
def _check_tuple(self, idx):
197
"""Assure that idx is a tuple containing two-elements inside the
198
range (self.n-1, self.m-1)"""
199
if (not isinstance(idx, tuple)):
200
raise TypeError, "wrong type: %s as index" % (str(type(idx)))
201
if (not len(idx) == 2):
202
raise ValueError, "length of index tuple must be 2, got %d" %(len(idx))
203
if (idx[0] > self.n-1):
204
raise ValueError, "index out of range, %d" %(idx[0])
205
if (idx[1] > self.m-1):
206
raise ValueError, "index out of range, %d" %(idx[1])
209
class DiagBlockMatrix(object):
210
"""A diagonal block matrix implementation in Python. This class is
211
typically used to store block diagonal preconditioners"""
213
def __init__(self, input):
216
Input arguments are either an integer value specifying the number of
217
block rows, or a Python list containing the diagonal block elements
219
if isinstance(input, int):
225
elif isinstance(input, (list, tuple)):
231
"""Return the number of block rows."""
234
shape = property(shape)
237
"""Pretty print (ugly)."""
238
return str(self.data)
241
"""Return string capable of copying the object when calling
243
return "BlockMatrix(%d)" % (self.n)
245
def __setitem__(self, idx, entry):
246
"""Set a diagonal block matrix entry."""
247
if (self._check_tuple(idx)):
248
if not idx[0] == idx[1] :
249
raise ValueError, "diagonal block matrix can not assign to indices (%d, %d)" % (idx[0], idx[1])
250
self.data[idx[0]] = entry
252
def __getitem__(self, idx):
253
"""Get a diagonal block matrix entry."""
254
if (self._check_tuple(idx)):
255
if not idx[0] == idx[1] :
256
raise ValueError, "diagonal block matrix dows not provide indices (%d, %d)" % (idx[0], idx[1])
257
return self.data[idx[0]]
259
def __add__(self, other):
260
"""Add two diagonal block matrices."""
261
if (not isinstance(other, DiagBlockMatrix)):
262
raise TypeError, "Can not add a DiagBlockMatrix and a %s" %(str(other))
263
if (not self.shape == other.shape):
264
raise ValueError, "The BlockMatrices have different shapes: %s, %s" % (str(self.shape), str(other.shape))
265
b = DiagBlockmatrix(self.n)
266
for i in range(self.n):
267
b.data[i] = self[i]+other[i]
270
def __mul__(self, other):
271
"""Matrix vector product."""
272
if (not isinstance(other, BlockVector)):
273
raise TypeError, "Can not multiply DiagBlockMatrix with %s" % (str(type(other)))
274
if (not other.n == self.n):
275
raise ValueError, "The length of the BlockVector (%d) does not match the dimention of the DiagBlockMatrix (%d)" % (other.n, self.n)
278
for i in range(self.n):
279
res.data.append(self.data[i]*other.data[i])
283
def prod(self, x, b,transposed=False): #Compute b = A*x
285
if (not isinstance(x, BlockVector)):
286
raise TypeError, "Can not multiply DiagBlockMatrix with %s" % (str(type(other)))
287
if (not x.n == self.n):
288
raise ValueError, "The length of the BlockVector (%d) does not match the dimention of the DiagBlockMatrix (%d, %d)" % (x.n, self.n, self.n)
290
for i in range(self.n):
291
b.data[i]=self.data[i]*x.data[i]
293
for i in range(self.n):
294
self.data[i].transposed = True
295
b.data[i]=self.data[i]*x.data[i]
296
self.data[i].transposed = False
301
def _check_tuple(self, idx):
302
"""Assure that idx is a tuple containing two-elements inside the
303
range (self.n-1, self.m-1)"""
304
if (not isinstance(idx, tuple)):
305
raise TypeError, "wrong type: %s as index" % (str(type(idx)))
306
if (not len(idx) == 2):
307
raise ValueError, "length of index tuple must be 2, got %d" %(len(idx))
308
if (idx[0] > self.n-1):
309
raise ValueError, "index out of range, %d" %(idx[0])
310
if (idx[1] > self.n-1):
311
raise ValueError, "index out of range, %d" %(idx[1])
316
class BlockVector(object):
317
"""A block vector implementation in Python.
319
The blocks in \c BlockVector are numpy arrays. Instances of this class
320
can be matrix vector multiplied with both BlockVectors and
323
def __init__(self, *x):
332
"""Return the number of blocks."""
335
shape = property(shape)
337
def num_entries(self):
339
for i in range(self.n):
340
len += self.data[i].size()
345
return reduce(operator.add, map(lambda x: numpy.dot(x,x), self.data))
347
def inner(self, other):
348
return reduce(operator.add, map(lambda x,y: x.inner(y), self.data, other.data))
350
def __add__(self, other):
353
for i in range(self.n):
354
# tmp = self.data[i].copy()
355
# tmp.axpy(1.0, other.data[i])
356
# res.data.append(tmp)
357
a = self.data[i].copy()
362
def __sub__(self, other):
365
for i in range(self.n):
366
# tmp = self.data[i].copy()
367
# tmp.axpy(-1.0, other.data[i])
368
# res.data.append(tmp)
369
a = self.data[i].copy()
375
def __mul__(self, other):
378
if isinstance(other,BlockVector):
379
for i in range(self.n):
380
res.data.append(self.data[i]*other[i])
382
for i in range(self.n):
383
tmp = self.data[i].copy()
388
def __rmul__(self, other):
389
return self.__mul__(other)
392
def __imul__(self, other):
393
for i in range(self.n):
394
self.data[i] *= other
397
def __iadd__(self, other):
398
for i in range(self.n):
399
self.data[i] += other.data[i]
402
def __isub__(self, other):
403
for i in range(self.n):
404
self.data[i] -= other.data[i]
407
def __getitem__(self, i):
410
def __setitem__(self, i, x):
417
raise "BlockVector error","Unable to attach vector in block %d" % (i)
422
for i in range(self.n):
423
res.data.append(self.data[i].copy())
426
if __name__ == '__main__':
430
u = numpy.arange(10,dtype='d')
431
v = numpy.arange(10,dtype='d')
441
B = DiagBlockMatrix(2)