1
+ class Matrix :
2
+ """
3
+ <class Matrix>
4
+ Matrix structure.
5
+ """
6
+
7
+ def __init__ (self , row : int , column : int , default_value : float = 0 ):
8
+ """
9
+ <method Matrix.__init__>
10
+ Initialize matrix with given size and default value.
11
+
12
+ Example:
13
+ >>> a = Matrix(2, 3, 1)
14
+ >>> a
15
+ Matrix consist of 2 rows and 3 columns
16
+ [1, 1, 1]
17
+ [1, 1, 1]
18
+ """
19
+
20
+ self .row , self .column = row , column
21
+ self .array = [[default_value for c in range (column )] for r in range (row )]
22
+
23
+ def __str__ (self ):
24
+ """
25
+ <method Matrix.__str__>
26
+ Return string representation of this matrix.
27
+ """
28
+
29
+ # Prefix
30
+ s = "Matrix consist of %d rows and %d columns\n " % (self .row , self .column )
31
+
32
+ # Make string identifier
33
+ max_element_length = 0
34
+ for row_vector in self .array :
35
+ for obj in row_vector :
36
+ max_element_length = max (max_element_length , len (str (obj )))
37
+ string_format_identifier = "%%%ds" % (max_element_length ,)
38
+
39
+ # Make string and return
40
+ def single_line (row_vector ):
41
+ nonlocal string_format_identifier
42
+ line = "["
43
+ line += ", " .join (string_format_identifier % (obj ,) for obj in row_vector )
44
+ line += "]"
45
+ return line
46
+ s += "\n " .join (single_line (row_vector ) for row_vector in self .array )
47
+ return s
48
+
49
+ def __repr__ (self ): return str (self )
50
+
51
+ def validateIndices (self , loc : tuple ):
52
+ """
53
+ <method Matrix.validateIndices>
54
+ Check if given indices are valid to pick element from matrix.
55
+
56
+ Example:
57
+ >>> a = Matrix(2, 6, 0)
58
+ >>> a.validateIndices((2, 7))
59
+ False
60
+ >>> a.validateIndices((0, 0))
61
+ True
62
+ """
63
+ if not (isinstance (loc , (list , tuple )) and len (loc ) == 2 ): return False
64
+ elif not (0 <= loc [0 ] < self .row and 0 <= loc [1 ] < self .column ): return False
65
+ else : return True
66
+
67
+ def __getitem__ (self , loc : tuple ):
68
+ """
69
+ <method Matrix.__getitem__>
70
+ Return array[row][column] where loc = (row, column).
71
+
72
+ Example:
73
+ >>> a = Matrix(3, 2, 7)
74
+ >>> a[1, 0]
75
+ 7
76
+ """
77
+ assert self .validateIndices (loc )
78
+ return self .array [loc [0 ]][loc [1 ]]
79
+
80
+ def __setitem__ (self , loc : tuple , value : float ):
81
+ """
82
+ <method Matrix.__setitem__>
83
+ Set array[row][column] = value where loc = (row, column).
84
+
85
+ Example:
86
+ >>> a = Matrix(2, 3, 1)
87
+ >>> a[1, 2] = 51
88
+ >>> a
89
+ Matrix consist of 2 rows and 3 columns
90
+ [ 1, 1, 1]
91
+ [ 1, 1, 51]
92
+ """
93
+ assert self .validateIndices (loc )
94
+ self .array [loc [0 ]][loc [1 ]] = value
95
+
96
+ def __add__ (self , another ):
97
+ """
98
+ <method Matrix.__add__>
99
+ Return self + another.
100
+
101
+ Example:
102
+ >>> a = Matrix(2, 1, -4)
103
+ >>> b = Matrix(2, 1, 3)
104
+ >>> a+b
105
+ Matrix consist of 2 rows and 1 columns
106
+ [-1]
107
+ [-1]
108
+ """
109
+
110
+ # Validation
111
+ assert isinstance (another , Matrix )
112
+ assert self .row == another .row and self .column == another .column
113
+
114
+ # Add
115
+ result = Matrix (self .row , self .column )
116
+ for r in range (self .row ):
117
+ for c in range (self .column ):
118
+ result [r ,c ] = self [r ,c ] + another [r ,c ]
119
+ return result
120
+
121
+ def __neg__ (self ):
122
+ """
123
+ <method Matrix.__neg__>
124
+ Return -self.
125
+
126
+ Example:
127
+ >>> a = Matrix(2, 2, 3)
128
+ >>> a[0, 1] = a[1, 0] = -2
129
+ >>> -a
130
+ Matrix consist of 2 rows and 2 columns
131
+ [-3, 2]
132
+ [ 2, -3]
133
+ """
134
+
135
+ result = Matrix (self .row , self .column )
136
+ for r in range (self .row ):
137
+ for c in range (self .column ):
138
+ result [r ,c ] = - self [r ,c ]
139
+ return result
140
+
141
+ def __sub__ (self , another ): return self + (- another )
142
+
143
+ def __mul__ (self , another ):
144
+ """
145
+ <method Matrix.__mul__>
146
+ Return self * another.
147
+
148
+ Example:
149
+ >>> a = Matrix(2, 3, 1)
150
+ >>> a[0,2] = a[1,2] = 3
151
+ >>> a * -2
152
+ Matrix consist of 2 rows and 3 columns
153
+ [-2, -2, -6]
154
+ [-2, -2, -6]
155
+ """
156
+
157
+ if isinstance (another , (int , float )): # Scalar multiplication
158
+ result = Matrix (self .row , self .column )
159
+ for r in range (self .row ):
160
+ for c in range (self .column ):
161
+ result [r ,c ] = self [r ,c ] * another
162
+ return result
163
+ elif isinstance (another , Matrix ): # Matrix multiplication
164
+ assert (self .column == another .row )
165
+ result = Matrix (self .row , another .column )
166
+ for r in range (self .row ):
167
+ for c in range (another .column ):
168
+ for i in range (self .column ):
169
+ result [r ,c ] += self [r ,i ] * another [i ,c ]
170
+ return result
171
+ else : raise TypeError ("Unsupported type given for another (%s)" % (type (another ),))
172
+
173
+ def transpose (self ):
174
+ """
175
+ <method Matrix.transpose>
176
+ Return self^T.
177
+
178
+ Example:
179
+ >>> a = Matrix(2, 3)
180
+ >>> for r in range(2):
181
+ ... for c in range(3):
182
+ ... a[r,c] = r*c
183
+ ...
184
+ >>> a.transpose()
185
+ Matrix consist of 3 rows and 2 columns
186
+ [0, 0]
187
+ [0, 1]
188
+ [0, 2]
189
+ """
190
+
191
+ result = Matrix (self .column , self .row )
192
+ for r in range (self .row ):
193
+ for c in range (self .column ):
194
+ result [c ,r ] = self [r ,c ]
195
+ return result
196
+
197
+ def ShermanMorrison (self , u , v ):
198
+ """
199
+ <method Matrix.ShermanMorrison>
200
+ Apply Sherman-Morrison formula in O(n^2).
201
+ To learn this formula, please look this: https://en.wikipedia.org/wiki/Sherman%E2%80%93Morrison_formula
202
+ This method returns (A + uv^T)^(-1) where A^(-1) is self. Returns None if it's impossible to calculate.
203
+ Warning: This method doesn't check if self is invertible.
204
+ Make sure self is invertible before execute this method.
205
+
206
+ Example:
207
+ >>> ainv = Matrix(3, 3, 0)
208
+ >>> for i in range(3): ainv[i,i] = 1
209
+ ...
210
+ >>> u = Matrix(3, 1, 0)
211
+ >>> u[0,0], u[1,0], u[2,0] = 1, 2, -3
212
+ >>> v = Matrix(3, 1, 0)
213
+ >>> v[0,0], v[1,0], v[2,0] = 4, -2, 5
214
+ >>> ainv.ShermanMorrison(u, v)
215
+ Matrix consist of 3 rows and 3 columns
216
+ [ 1.2857142857142856, -0.14285714285714285, 0.3571428571428571]
217
+ [ 0.5714285714285714, 0.7142857142857143, 0.7142857142857142]
218
+ [ -0.8571428571428571, 0.42857142857142855, -0.0714285714285714]
219
+ """
220
+
221
+ # Size validation
222
+ assert isinstance (u , Matrix ) and isinstance (v , Matrix )
223
+ assert self .row == self .column == u .row == v .row # u, v should be column vector
224
+ assert u .column == v .column == 1 # u, v should be column vector
225
+
226
+ # Calculate
227
+ vT = v .transpose ()
228
+ numerator_factor = (vT * self * u )[0 , 0 ] + 1
229
+ if numerator_factor == 0 : return None # It's not invertable
230
+ return self - ((self * u ) * (vT * self ) * (1.0 / numerator_factor ))
231
+
232
+ # Testing
233
+ if __name__ == "__main__" :
234
+
235
+ def test1 ():
236
+ # a^(-1)
237
+ ainv = Matrix (3 , 3 , 0 )
238
+ for i in range (3 ): ainv [i ,i ] = 1
239
+ print ("a^(-1) is %s" % (ainv ,))
240
+ # u, v
241
+ u = Matrix (3 , 1 , 0 )
242
+ u [0 ,0 ], u [1 ,0 ], u [2 ,0 ] = 1 , 2 , - 3
243
+ v = Matrix (3 , 1 , 0 )
244
+ v [0 ,0 ], v [1 ,0 ], v [2 ,0 ] = 4 , - 2 , 5
245
+ print ("u is %s" % (u ,))
246
+ print ("v is %s" % (v ,))
247
+ print ("uv^T is %s" % (u * v .transpose ()))
248
+ # Sherman Morrison
249
+ print ("(a + uv^T)^(-1) is %s" % (ainv .ShermanMorrison (u , v ),))
250
+
251
+ def test2 ():
252
+ import doctest
253
+ doctest .testmod ()
254
+
255
+ test2 ()
0 commit comments