forked from BigMacchia/flame_math
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatrix.py
440 lines (340 loc) · 12.8 KB
/
matrix.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
from itertools import izip
matrix_iterators = None
from flame_math import vector
#TODO: put in place som way to convert vector to matrix and allow creation
# of row matrix or better allow to input vectors or list of row data that we will convert
# to column
class Matrix(object):
def __init__(self, rows, columns,data=None ):
"""
This is the constructor for the generic matrix
Args:
:rows: int, how many rows you want in the matrix
:columns: int, how many columns you want in the matrix
:data: Vector[], column vectors representing the data
"""
if not data:
self.data = [vector.Vector([0]*rows) for c in xrange(columns)]
else:
self.data = data
self.__columns = columns
self.__rows = rows
@classmethod
def from_vectors(cls, vectors):
"""
This is an alternative constructor that allows
to generate the matrix from a series of column vectors
"""
inst = cls(len(vectors[0]),len(vectors),vectors)
#inst.data = vectors
return inst
@classmethod
def from_list(cls, rows,columns, data):
"""
This alternative constructor lets you build the matrix
from a flat array of values, dimensions needs to be provided
to partition the data into columns
:rows: int, how many rows you want in the matrix
:columns: int, how many columns you want in the matrix
:data: float[], the data to partition
"""
vectors = []
for c in range(len(data)/rows):
vec = vector.Vector([ data[c*rows + r] for r in range(rows)])
vectors.append(vec)
inst = cls(rows,columns,vectors)
return inst
@classmethod
def copy(cls, mat):
"""
This is the copy constructor
Args:
:mat: Matrix, the matrix we wish to copy
:returns: Matrix instance
"""
return cls.from_vectors([vector.Vector.copy(v) for v in mat.column_iterator()])
def columns(self):
"""
Retunrs the orizontal size of the matrix
"""
return len(self.data)
def rows(self):
"""
Returns the vertical size of the matrix
"""
if (len(self.data) == 0):
return 0
return len(self.data[0])
def column_iterator(self):
"""
Return a column iterator for the matrix
:returns: iterator
"""
return matrix_iterators.ColumnIterator(self)
def row_iterator(self):
"""
Returns a row iterator of the matrix
:returns: iterator:
"""
return matrix_iterators.RowIterator(self)
def diagonal_iterator(self):
"""
Returns a diagonal iteerator for the matrix
:returns: iterator
"""
return matrix_iterators.DiagonalIterator(self)
def full_iterator(self, reverse=False):
return matrix_iterators.FullIterator(self, reverse)
def __getitem__(self, index):
"""
Subsctiption operator returns the colum vector
at the given index not tested with slice object
:index: int, the index we want to access
"""
return self.data[index]
def __setitem__(self, index ,data):
"""
Subscrition setter operator, not tested with slice object
:index: int, the index we want to set
:data: Vector, no checks has been done whatsoever on the dimension of the
vector compared to the matrix, it is up to the user to do not make
mistakes.
"""
self.data[index] = data
def set_to_value(self, value):
"""
This function sets the whole matrix to a specifc value
Args:
:value: float, the value to set the matrix to
"""
it = self.column_iterator()
for c in it:
c.set_to_value(value)
it.merge(c)
def set_zero(self):
"""
Zero out the matrix
"""
self.set_to_value(0)
def __str__(self):
"""
Print out the matrix
:todo: aligning caracters based maybe on float precision?
"""
to_return = ""
for r in xrange(self.__rows):
tmp = ""
for c in xrange(self.__columns):
tmp += (str(self.data[c][r]) + " ")
tmp += "\n"
to_return +=tmp
return to_return
def __eq__(self, mat):
"""
This function returns true wheter this matrix and the given one
are the same
:mat: Matrix, the matrix to check against
"""
#first we do a check on the dimension of the two matrix
if self.columns() != mat.columns() or self.rows() != mat.rows():
return False
#if we reach this point we do an equality on all the columns
for a,b in zip(self.column_iterator(),mat.column_iterator()):
if not a == b :
return False
return True
def __neg__(self):
mat = Matrix.copy(self)
it = mat.column_iterator()
for c in it:
it.merge(-c)
return mat
def set_scale(self,value):
"""
Scale the matrix by the given value
:value: float,int, the scalar value
"""
it = self.column_iterator()
for c in it:
c = value*c
it.merge(c)
def set_add(self,mat):
"""
Add the two matrices
:mat: Matrix, the matrix to add, no check is done on dimensions of matrix
"""
it = self.column_iterator()
mat_it = mat.column_iterator()
#here izip is needed because regular zip will consume the whole generators
#to generate the list of tuples, which is ok the generetors works, the problems
#comes at the time of the merge, to make life easier the merge is based on the
#internal generator index, means that it will point to last index always and thats
#wrong, instead izip works on the same idea of xrange, generating the tuple when needed
#meaning consuming step by step the iterators and not all at once
for c ,c2 in izip(it,mat_it):
c += c2
it.merge(c)
def transpose(self):
cit= self.column_iterator()
rit= self.row_iterator()
mat = self.__class__(self.columns(), self.rows(), list(rit))
return mat
###########################################
###### MATRIX VECTOR MULTIPLICATION
##########################################
#####################
##### DOT BASES
####################
def mult_vec_by_dot(self, vec):
"""
matrix vector mult using under dot product under the hood
:vec: Vector, the vector to multiply
return Vector
"""
it = self.row_iterator()
values = []
for row in it:
val = vector.dot_product(row, vec)
values.append(val)
return vector.Vector(values)
def mult_vec_by_transposed_matrix_dot(self,vec):
"""
This function multiply the given vector by the transpose of the current
matrix, the transposition is not performed, instead we loop the columns
instead of the rows
:vec: Vector, the vector to be multiplied
:returns: Vector
"""
it = self.column_iterator()
values = []
for c in it:
val = vector.dot_product(c, vec)
values.append(val)
return vector.Vector(values)
def mult_vec_by_dot_parted(self, vec):
"""
This is an alternative method to multiply a matrix by a vector
taking advantage of a dot product based operatin but with partitioned
matrix
:vec: Vector, the value to multiply
:returns: Vector
"""
result = vector.Vector([0]* self.rows())
tl, bl = vector.part(vec)
it = self.full_iterator()
for i,data in enumerate(it):
tl, value, bl = vector.slice_(tl,bl)
result[i] = (vector.dot_product(data["at10"],tl) +
data["a11"] * value +
vector.dot_product(data["at12"],bl))
vector.compose(tl,value,bl)
return result
####################
##### AXPY BASED
###################
def mult_vec_by_axpy(self, vec):
"""
This function implemetns a matrix vector multiplication but
this time based on the AXPY algorithm
:vec: Vector, the value to multiply
:returns: Vector
"""
result = vector.Vector([0]* self.rows())
for i,c in enumerate(self.data) :
vector.axpy(c,result, vec[i])
return result
def mult_vec_by_transposed_matrix_axpy(self,vec):
"""
This function implemetns a matrix vector multiplication using the
transposed version of the matrix and is based on a axpy algorithm
:vec: Vector, the value to multiply
:returns: Vector
"""
result = vector.Vector([0]* self.columns())
it = self.row_iterator()
for i,r in enumerate(it) :
vector.axpy(r,result, vec[i])
return result
def mult_vec_by_axpy_parted(self, vec):
"""
This function implements a matrix vector mult based on AXPY but with
a parted matrix
:vec: Vector, the vector to work on
:returns: Vector
"""
result = vector.Vector([0]* self.rows())
TL, BL = vector.part(result)
it = self.full_iterator()
for i,data in enumerate(it):
TL, value, BL = vector.slice_(TL,BL)
vector.axpy(data["a01"],TL,vec[i])
value = data["a11"] * vec[i] + value
vector.axpy(data["a21"], BL, vec[i])
vector.compose(TL,value,BL)
return TL
###############################################################
### MATRIX MATRIX MULT
##############################################################
def mult_matrix_update_by_column(self, mat):
"""
This function multiply the current matrix by the given matrix
In this function we update one collumn at the time of the resulting matrix
:mat: Matrix, that we compute for
:returns: Matrix
"""
vecs = []
it = mat.column_iterator()
for c in it:
v = self.mult_vec_by_dot(c)
vecs.append(v)
return Matrix.from_vectors(vecs)
def mult_matrix_update_by_row(self,mat):
"""
This function multiply the current matrix by the given matrix
In this function we update one row at the time of the resulting matrix
This version of the multiplication is particulary painful because it requires to convert
the rows coming out of the row iterator to a matrix, which also rigth now we dont
have yet a way to iunitialize a matrix by row vectors so we also have an extra transpose
to perform
after that we need to extraxt the vector out of the matrix and that forces us to another transpose
THIS MATRIX MULT GOES AGAINST ANY SENSE OF PERFORMANCE
:mat: Matrix, that we compute for
:returns: Matrix
"""
#let s make a copy of the matrix to have an iterator we can merge into
#without override the input matrix
cpy = Matrix.copy(mat)
#the resoult iterator
rit = cpy.row_iterator()
#the source iterator
sit = self.row_iterator()
for r ,t in izip(rit,sit):
#lets convert the vector to a matrix
matr = Matrix.from_vectors([t])
#we need to transpose since the constructor builds a column matrix
matr = matr.transpose()
#lets performs row * matrix computation
v = matr.mult_matrix_update_by_column(mat)
#transpose and extract the vector
v = v.transpose()
#merge the result back in
rit.merge(v.data[0])
#return the matrix
return cpy
def mult_matrix_update_by_rank1(self, mat):
"""
This function performs a rank-1 update of the given matrix
:mat: Matrix, that we compute for
:returns: Matrix
"""
rit = mat.row_iterator()
cit = self.column_iterator()
res = Matrix(self.rows(), mat.columns())
res.set_zero()
for c,r in izip(cit,rit):
#convert both vectors to matrix
cmat = Matrix.from_vectors([c])
rmat = Matrix.from_vectors([r])
rmat = rmat.transpose()
res.set_add( cmat.mult_matrix_update_by_column(rmat))
return res