1
1
# An OOP approach to representing and manipulating matrices
2
2
3
+ from __future__ import annotations
4
+
3
5
4
6
class Matrix :
5
7
"""
@@ -54,7 +56,9 @@ class Matrix:
54
56
[6. -12. 6.]
55
57
[-3. 6. -3.]]
56
58
>>> print(matrix.inverse())
57
- None
59
+ Traceback (most recent call last):
60
+ ...
61
+ TypeError: Only matrices with a non-zero determinant have an inverse
58
62
59
63
Determinant is an int, float, or Nonetype
60
64
>>> matrix.determinant()
@@ -101,10 +105,9 @@ class Matrix:
101
105
[198. 243. 288. 304.]
102
106
[306. 378. 450. 472.]
103
107
[414. 513. 612. 640.]]
104
-
105
108
"""
106
109
107
- def __init__ (self , rows ):
110
+ def __init__ (self , rows : list [ list [ int ]] ):
108
111
error = TypeError (
109
112
"Matrices must be formed from a list of zero or more lists containing at "
110
113
"least one and the same number of values, each of which must be of type "
@@ -125,53 +128,54 @@ def __init__(self, rows):
125
128
self .rows = []
126
129
127
130
# MATRIX INFORMATION
128
- def columns (self ):
131
+ def columns (self ) -> list [ list [ int ]] :
129
132
return [[row [i ] for row in self .rows ] for i in range (len (self .rows [0 ]))]
130
133
131
134
@property
132
- def num_rows (self ):
135
+ def num_rows (self ) -> int :
133
136
return len (self .rows )
134
137
135
138
@property
136
- def num_columns (self ):
139
+ def num_columns (self ) -> int :
137
140
return len (self .rows [0 ])
138
141
139
142
@property
140
- def order (self ):
143
+ def order (self ) -> tuple [ int , int ] :
141
144
return (self .num_rows , self .num_columns )
142
145
143
146
@property
144
- def is_square (self ):
147
+ def is_square (self ) -> bool :
145
148
return self .order [0 ] == self .order [1 ]
146
149
147
- def identity (self ):
150
+ def identity (self ) -> Matrix :
148
151
values = [
149
152
[0 if column_num != row_num else 1 for column_num in range (self .num_rows )]
150
153
for row_num in range (self .num_rows )
151
154
]
152
155
return Matrix (values )
153
156
154
- def determinant (self ):
157
+ def determinant (self ) -> int :
155
158
if not self .is_square :
156
- return None
159
+ return 0
157
160
if self .order == (0 , 0 ):
158
161
return 1
159
162
if self .order == (1 , 1 ):
160
- return self .rows [0 ][0 ]
163
+ return int ( self .rows [0 ][0 ])
161
164
if self .order == (2 , 2 ):
162
- return (self .rows [0 ][0 ] * self .rows [1 ][1 ]) - (
163
- self .rows [0 ][1 ] * self .rows [1 ][0 ]
165
+ return int (
166
+ (self .rows [0 ][0 ] * self .rows [1 ][1 ])
167
+ - (self .rows [0 ][1 ] * self .rows [1 ][0 ])
164
168
)
165
169
else :
166
170
return sum (
167
171
self .rows [0 ][column ] * self .cofactors ().rows [0 ][column ]
168
172
for column in range (self .num_columns )
169
173
)
170
174
171
- def is_invertable (self ):
175
+ def is_invertable (self ) -> bool :
172
176
return bool (self .determinant ())
173
177
174
- def get_minor (self , row , column ) :
178
+ def get_minor (self , row : int , column : int ) -> int :
175
179
values = [
176
180
[
177
181
self .rows [other_row ][other_column ]
@@ -183,20 +187,20 @@ def get_minor(self, row, column):
183
187
]
184
188
return Matrix (values ).determinant ()
185
189
186
- def get_cofactor (self , row , column ) :
190
+ def get_cofactor (self , row : int , column : int ) -> int :
187
191
if (row + column ) % 2 == 0 :
188
192
return self .get_minor (row , column )
189
193
return - 1 * self .get_minor (row , column )
190
194
191
- def minors (self ):
195
+ def minors (self ) -> Matrix :
192
196
return Matrix (
193
197
[
194
198
[self .get_minor (row , column ) for column in range (self .num_columns )]
195
199
for row in range (self .num_rows )
196
200
]
197
201
)
198
202
199
- def cofactors (self ):
203
+ def cofactors (self ) -> Matrix :
200
204
return Matrix (
201
205
[
202
206
[
@@ -209,25 +213,27 @@ def cofactors(self):
209
213
]
210
214
)
211
215
212
- def adjugate (self ):
216
+ def adjugate (self ) -> Matrix :
213
217
values = [
214
218
[self .cofactors ().rows [column ][row ] for column in range (self .num_columns )]
215
219
for row in range (self .num_rows )
216
220
]
217
221
return Matrix (values )
218
222
219
- def inverse (self ):
223
+ def inverse (self ) -> Matrix :
220
224
determinant = self .determinant ()
221
- return None if not determinant else self .adjugate () * (1 / determinant )
225
+ if not determinant :
226
+ raise TypeError ("Only matrices with a non-zero determinant have an inverse" )
227
+ return self .adjugate () * (1 / determinant )
222
228
223
- def __repr__ (self ):
229
+ def __repr__ (self ) -> str :
224
230
return str (self .rows )
225
231
226
- def __str__ (self ):
232
+ def __str__ (self ) -> str :
227
233
if self .num_rows == 0 :
228
234
return "[]"
229
235
if self .num_rows == 1 :
230
- return "[[" + ". " .join (self .rows [0 ]) + "]]"
236
+ return "[[" + ". " .join (str ( self .rows [0 ]) ) + "]]"
231
237
return (
232
238
"["
233
239
+ "\n " .join (
@@ -240,7 +246,7 @@ def __str__(self):
240
246
)
241
247
242
248
# MATRIX MANIPULATION
243
- def add_row (self , row , position = None ):
249
+ def add_row (self , row : list [ int ] , position : int | None = None ) -> None :
244
250
type_error = TypeError ("Row must be a list containing all ints and/or floats" )
245
251
if not isinstance (row , list ):
246
252
raise type_error
@@ -256,7 +262,7 @@ def add_row(self, row, position=None):
256
262
else :
257
263
self .rows = self .rows [0 :position ] + [row ] + self .rows [position :]
258
264
259
- def add_column (self , column , position = None ):
265
+ def add_column (self , column : list [ int ] , position : int | None = None ) -> None :
260
266
type_error = TypeError (
261
267
"Column must be a list containing all ints and/or floats"
262
268
)
@@ -278,18 +284,18 @@ def add_column(self, column, position=None):
278
284
]
279
285
280
286
# MATRIX OPERATIONS
281
- def __eq__ (self , other ) :
287
+ def __eq__ (self , other : object ) -> bool :
282
288
if not isinstance (other , Matrix ):
283
289
raise TypeError ("A Matrix can only be compared with another Matrix" )
284
290
return self .rows == other .rows
285
291
286
- def __ne__ (self , other ) :
292
+ def __ne__ (self , other : object ) -> bool :
287
293
return not self == other
288
294
289
- def __neg__ (self ):
295
+ def __neg__ (self ) -> Matrix :
290
296
return self * - 1
291
297
292
- def __add__ (self , other ) :
298
+ def __add__ (self , other : Matrix ) -> Matrix :
293
299
if self .order != other .order :
294
300
raise ValueError ("Addition requires matrices of the same order" )
295
301
return Matrix (
@@ -299,7 +305,7 @@ def __add__(self, other):
299
305
]
300
306
)
301
307
302
- def __sub__ (self , other ) :
308
+ def __sub__ (self , other : Matrix ) -> Matrix :
303
309
if self .order != other .order :
304
310
raise ValueError ("Subtraction requires matrices of the same order" )
305
311
return Matrix (
@@ -309,9 +315,11 @@ def __sub__(self, other):
309
315
]
310
316
)
311
317
312
- def __mul__ (self , other ) :
318
+ def __mul__ (self , other : Matrix | int | float ) -> Matrix :
313
319
if isinstance (other , (int , float )):
314
- return Matrix ([[element * other for element in row ] for row in self .rows ])
320
+ return Matrix (
321
+ [[int (element * other ) for element in row ] for row in self .rows ]
322
+ )
315
323
elif isinstance (other , Matrix ):
316
324
if self .num_columns != other .num_rows :
317
325
raise ValueError (
@@ -329,7 +337,7 @@ def __mul__(self, other):
329
337
"A Matrix can only be multiplied by an int, float, or another matrix"
330
338
)
331
339
332
- def __pow__ (self , other ) :
340
+ def __pow__ (self , other : int ) -> Matrix :
333
341
if not isinstance (other , int ):
334
342
raise TypeError ("A Matrix can only be raised to the power of an int" )
335
343
if not self .is_square :
@@ -348,7 +356,7 @@ def __pow__(self, other):
348
356
return result
349
357
350
358
@classmethod
351
- def dot_product (cls , row , column ) :
359
+ def dot_product (cls , row : list [ int ] , column : list [ int ]) -> int :
352
360
return sum (row [i ] * column [i ] for i in range (len (row )))
353
361
354
362
0 commit comments