forked from BigMacchia/flame_math
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmatrix_iterators.py
344 lines (280 loc) · 11.1 KB
/
matrix_iterators.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
import sys
print sys.path
#from flame_math import matrix
from flame_math.vector import Vector
matrix = None
class ColumnIterator(object):
"""
This class implements a column iterator for the given matrix,
it will slice out one at the time the column and give it to the user
for manipulation
"""
def __init__(self, data):
"""
This is the constructor
Args:
:data: Matrix, the matrix to iterate over
"""
self.data = data
self.counter = 0
self.columns = data.columns()
self.rows = data.rows()
def __iter__(self):
return self
def next(self):
"""
Function to setp forward the iterator
"""
#first we check if finised the iteration
if self.counter < self.columns:
#we slice the data and upgrade the counter
to_return = self.data[self.counter]
self.counter +=1
return to_return
else:
#if we are done we rise a stop iteration exception
raise StopIteration()
def merge(self, data):
"""
This method lets you merge the result back in the original
data structure, even tho if the user is careful for a column
iterator the reference to the original data is preserved and
the data doesnt need merge, we keep that function and suggest
to use it for consitency
"""
self.data[self.counter -1] = data
def reset(self):
"""
Resetting the iterator so can be used again
"""
self.counter = 0
class RowIterator(object):
"""
This class implements a row iterator for the given matrix,
it will slice out one at the time the column and give it to the user
for manipulation
"""
def __init__(self, data):
"""
This is the constructor
Args:
:data: Matrix, the matrix to iterate over
"""
self.data = data
self.counter = 0
self.columns = data.columns()
self.rows = data.rows()
def __iter__(self):
return self
def next(self):
"""
Function to setp forward the iterator
"""
#first we check if finised the iteration
if self.counter < self.rows:
#we slice the data and upgrade the counter
to_return = Vector([self.data[c][self.counter] for c in xrange(self.columns)])
self.counter +=1
return to_return
else:
#if we are done we rise a stop iteration exception
raise StopIteration()
def merge(self, data):
"""
This method lets you merge the result back in the original
data structure, even tho if the user is careful for a column
iterator the reference to the original data is preserved and
the data doesnt need merge, we keep that function and suggest
to use it for consitency
"""
for c in range(self.columns):
self.data[c][self.counter -1] = data[c]
def reset(self):
"""
Resetting the iterator so can be used again
"""
self.counter = 0
class DiagonalIterator(object):
"""
This is the interator used to iterate the matrix in a diagonal
fashion
"""
def __init__(self, data):
"""
This is the constructor
Args:
:data: Matrix, the matrix to iterate
"""
self.data = data
self.counter = 0
self.columns = data.columns()
self.rows = data.rows()
def __iter__(self):
return self
def next(self):
"""
This is the function used to step the iterator forward
:returns: Vector, float, Vector, which are in order the column vector above
the diagonal, the diagonal value, and the vector below the diagonal
"""
if self.counter < self.columns:
#if we did not extinguish the iterator we check if the counter
#is 0, meaning the first iteration.
#if this is the case the TL (top left part) of the matrix
#will be an empty vector
#todo: probably the check is useless the slice operation would retunr an empty
#list anyway
if self.counter == 0:
TL = Vector([])
else:
TL = Vector(self.data[self.counter][:self.counter])
#we extrapolate out the diagonal value , here is where we lose the reference
#to the original value
value = self.data[self.counter][self.counter]
#the BL (bottom left part) is nothing more then the remaining column vector
#below the the diagonal
BL = Vector(self.data[self.counter][self.counter+1:])
self.counter +=1
return [TL, value, BL]
else:
raise StopIteration()
def merge(self, TL, value, BL):
self.data[self.counter -1] = Vector(TL.values + [value] + BL.values)
class FullIterator(object):
def __init__(self, data, reverse=False):
"""
This is the interator used to iterate the matrix in a diagonal
fashion
"""
self.data = data
self.columns = data.columns()
self.rows = data.rows()
self.reverse = reverse
self.counter = 0
self.end_condition = self.__has_next_regular
self.counter_speed = 1
if self.reverse:
self.counter = min(self.columns, self.rows)-1
#here we use a "function pointer" to point to the correct
#function to call for the check of end iteration
self.end_condition = self.__has_next_reverse
self.counter_speed = -1
'''
#needed matrix parts
self.A00 = Matrix(0,0)
self.a01 =Vector([])
self.a11 = None
self.at10 = Vector([])
self.A20 = Matrix(0,0)
self.A02 = Matrix(0,0)
self.a21 = Vector([])
self.at12 =Vector([])
self.A22 = Matrix.from_vectors(self.data.data)
'''
def __has_next_regular(self):
"""
This style is borrowed from java iterators, returns wheter or
not the regular direction iterator has a next iteration or not
"""
return (self.counter< self.columns and self.counter < self.rows)
def __has_next_reverse(self):
return self.counter >= 0
def __iter__(self):
return self
def next(self):
if (self.end_condition()):
#top matrix
#column above diagonal
self.a01 = Vector(self.data[self.counter][:self.counter])
#diagonal value
self.a11= self.data[self.counter][self.counter]
#columb below diagonal
self.a21= Vector(self.data[self.counter][self.counter+1:])
#row before diagonal
self.at10= Vector(Vector([self.data[x][self.counter] for x in range(self.counter)]))
#row after diagonal
self.at12 = Vector([self.data[x][self.counter] for x in range(self.counter+1,self.columns)])
##matirces
if self.counter == 0:
self.A00 = matrix.Matrix(0,0)
self.A20 = matrix.Matrix(0,0)
else:
self.A00 = matrix.Matrix.from_vectors([Vector(self.data[c][:self.counter] ) for c in range(self.counter)])
#here we check first if there is no zero column or row in the matrix we would create, if so we create an empty
#matrix
if (self.counter+1 < (self.rows) and self.counter <(self.columns)):
self.A20 = matrix.Matrix.from_vectors([Vector(self.data[c][self.counter+1:] ) for c in range(self.counter)])
else:
self.A20 = matrix.Matrix(0,0)
#here we need to check if we are not going out of bound
if (self.counter+1 <self.columns) and self.counter > 0 :
self.A02 = matrix.Matrix.from_vectors([Vector(self.data[c][:self.counter] ) for c in range(self.counter+1,self.columns)])
else:
self.A02= matrix.Matrix(0,0)
#here we check first if there is no zero column or row in the matrix we would create, if so we create an empty
#matrix
if (self.counter+1 < (self.rows) and self.counter+1 <(self.columns)):
self.A22 = matrix.Matrix.from_vectors([Vector(self.data[c][self.counter+1:] ) for c in range(self.counter+1,self.columns)])
else:
self.A22 = matrix.Matrix(0,0)
self.counter += self.counter_speed
to_return = {"A00": self.A00,
"a01": self.a01,
"A02": self.A02,
"at10":self.at10,
"a11": self.a11,
"at12":self.at12,
"A20": self.A20,
"a21": self.a21,
"A22": self.A22}
return to_return
else:
raise StopIteration()
def merge(self, m_data):
"""
Used to merge the result back in the original data structure
In the merge process no assumption whatsoever about which parte has
been manipulated is made, this means all data will be merged no matter
what
Right now I am merging matrix column by coulmn probaby best way is trying to see how is possible
to do a 2D slicing of a matrix and use directly __setitem__ from the matrix
:m_data: dict, the matrix data to merge
"""
#let s merge everything in
#let start with A00
#extracting up left matrix columns and rows size
Ar= m_data["A00"].rows()
Ac= m_data["A00"].columns()
#extracting columns and rows from main matrix
Dr = self.data.rows()
Dc = self.data.columns()
for i in range(Ac):
self.data[i][:Ar] = m_data["A00"][i].values
#a01
self.data[Ac][:Ar]= m_data["a01"].values
#A02
A02c = m_data["A02"].columns()
A02r = m_data["A02"].rows()
for i in range(A02c):
self.data[i+Ac+1][:A02r] = m_data["A02"][i].values
#at10
for i in range(m_data["at10"].size()):
#self.data[i][Ar] = 8
self.data[i][Ar] = m_data["at10"][i]
#a11
self.data[Ac][Ar] = m_data["a11"]
#at12
for i in range(m_data["at12"].size()):
self.data[i+Ac+1][Ar] = m_data["at12"][i]
#A20
A20c = m_data["A20"].columns()
A20r = m_data["A20"].rows()
for i in range(A20c):
self.data[i][Ar+1:] = m_data["A20"][i].values
#a21
self.data[Ac][Ar+1:]= m_data["a21"].values
#A22
A22c = m_data["A22"].columns()
A22r = m_data["A22"].rows()
for i in range(A22c):
self.data[i+Ac+1][Ar+1:] = m_data["A22"][i].values