Skip to content

Commit 47cc473

Browse files
committed
Added several basic matrix properties and methods, with doctests
- is_square - is_identy - is_binary - is_zero_matrix - is_ones_matrix - is_diagonal - is_exchange - is_elemantary - diagonal() - anti_diagonal()
1 parent 25c712f commit 47cc473

1 file changed

Lines changed: 284 additions & 2 deletions

File tree

‎matrix/matrix_class.py‎

Lines changed: 284 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ class Matrix:
88
Matrix object generated from a 2D array where each element is an array representing
99
a row.
1010
Rows can contain type int or float.
11-
Common operations and information available.
11+
Common operations and properties are available.
1212
>>> rows = [
1313
... [1, 2, 3],
1414
... [4, 5, 6],
@@ -145,15 +145,297 @@ def order(self) -> tuple[int, int]:
145145

146146
@property
147147
def is_square(self) -> bool:
148-
return self.order[0] == self.order[1]
148+
"""
149+
Returns True if the matrix is square, False otherwise.
150+
Wikipedia reference: https://en.wikipedia.org/wiki/Square_matrix
151+
>>> print(Matrix([[1, 2], [3, 4]]).is_square)
152+
True
153+
>>> print(Matrix([[1, 2, 3], [3, 4, 5]]).is_square)
154+
False
155+
>>> print(Matrix([[1]]).is_square)
156+
True
157+
>>> print(Matrix([[1, 2], [3,4], [5,6]]).is_square)
158+
False
159+
"""
160+
return self.num_rows == self.num_columns
149161

150162
def identity(self) -> Matrix:
163+
"""
164+
Returns the identity matrix of the same order as the matrix.
165+
Wikipedia reference: https://en.wikipedia.org/wiki/Identity_matrix
166+
>>> print(Matrix([[1, 2], [3, 4]]).identity())
167+
[[1. 0.]
168+
[0. 1.]]
169+
>>> print(Matrix([[1, 2, 3], [3, 4, 5]]).identity())
170+
Traceback (most recent call last):
171+
...
172+
ValueError: Only square matrices have an identity matrix
173+
"""
174+
if not self.is_square:
175+
raise ValueError("Only square matrices have an identity matrix")
151176
values = [
152177
[0 if column_num != row_num else 1 for column_num in range(self.num_rows)]
153178
for row_num in range(self.num_rows)
154179
]
155180
return Matrix(values)
156181

182+
@property
183+
def is_identy(self) -> bool:
184+
"""
185+
Returns True if the matrix is an identity matrix, False otherwise.
186+
Wikipedia reference: https://en.wikipedia.org/wiki/Identity_matrix
187+
>>> print(Matrix([[1, 0], [0, 1]]).is_identy)
188+
True
189+
>>> print(Matrix([[1, 2], [1, 4]]).is_identy)
190+
False
191+
>>> print(Matrix([[1, 0, 0], [0, 0, 1]]).is_identy)
192+
Traceback (most recent call last):
193+
...
194+
ValueError: Only square matrices can be identity matrices
195+
"""
196+
if not self.is_square:
197+
raise ValueError("Only square matrices can be identity matrices")
198+
for row in range(self.num_rows):
199+
for col in range(self.num_columns):
200+
if self.rows[row][col] != (0 if row != col else 1):
201+
return False
202+
return True
203+
204+
@property
205+
def is_zero_matrix(self) -> bool:
206+
"""
207+
Returns True if all elements of the matrix are zero, False otherwise.
208+
Wikipedia reference: https://en.wikipedia.org/wiki/Zero_matrix
209+
>>> print(Matrix([[1, 2, 3], [3, 4, 5]]).is_zero_matrix)
210+
False
211+
>>> print(Matrix([[0,0,0],[0,0,0],]).is_zero_matrix)
212+
True
213+
>>> print(Matrix([[0]]).is_zero_matrix)
214+
True
215+
>>> print(Matrix([[1]]).is_zero_matrix)
216+
False
217+
"""
218+
for row in range(self.num_rows):
219+
for col in range(self.num_columns):
220+
if self.rows[row][col] != 0:
221+
return False
222+
return True
223+
224+
@property
225+
def is_ones_matrix(self) -> bool:
226+
"""
227+
Returns True if all elements of the matrix are 1, False otherwise.
228+
Wikipedia: https://en.wikipedia.org/wiki/Matrix_of_ones
229+
>>> print(Matrix([[1, 2, 3], [3, 4, 5]]).is_ones_matrix)
230+
False
231+
>>> print(Matrix([[1,1,1],[1,1,1]]).is_ones_matrix)
232+
True
233+
>>> print(Matrix([[0]]).is_ones_matrix)
234+
False
235+
>>> print(Matrix([[1]]).is_ones_matrix)
236+
True
237+
"""
238+
239+
for row in range(self.num_rows):
240+
for col in range(self.num_columns):
241+
if self.rows[row][col] != 1:
242+
return False
243+
return True
244+
245+
@property
246+
def is_binary(self) -> bool:
247+
"""
248+
Returns True if all elements of the matrix are either 0 or 1, False otherwise.
249+
Wikipedia reference: https://en.wikipedia.org/wiki/Logical_matrix
250+
>>> print(Matrix([[1, 0], [0, 1]]).is_binary)
251+
True
252+
>>> print(Matrix([[1, 2], [3, 4]]).is_binary)
253+
False
254+
"""
255+
return all(element in (0, 1) for row in self.rows for element in row)
256+
257+
def diagonal(self) -> list[int]:
258+
"""
259+
Returns a list containing the values on the diagonal of the matrix.
260+
Wikipedia reference: https://en.wikipedia.org/wiki/Main_diagonal
261+
>>> print(Matrix([[1,2,3], [4,5,6], [7,8,9]]).diagonal())
262+
[1, 5, 9]
263+
>>> print(Matrix([[2]]).diagonal())
264+
[2]
265+
>>> print(Matrix([[1,2,3,4], [5,6,7,8], [9,10,11,12]]).diagonal())
266+
[1, 6, 11]
267+
>>> print(Matrix([[1,2,3], [4,5,6], [7,8,9] ,[10,11,12]]).diagonal())
268+
[1, 5, 9]
269+
"""
270+
return [self.rows[i][i] for i in range(min(self.num_rows, self.num_columns))]
271+
272+
def anti_diagonal(self) -> list[int]:
273+
"""
274+
Returns a list containing the anti-diagonal elements of the matrix.
275+
Wikipedia reference: https://en.wikipedia.org/wiki/Anti-diagonal_matrix
276+
>>> print(Matrix([[1,2,3], [4,5,6], [7,8,9]]).anti_diagonal())
277+
[3, 5, 7]
278+
>>> print(Matrix([[1,2,3,4], [5,6, 7, 8]]).anti_diagonal())
279+
[4, 7]
280+
>>> print(Matrix([[1,2], [3,4], [5,6]]).anti_diagonal())
281+
[2, 3]
282+
>>> print(Matrix([[2]]).anti_diagonal())
283+
[2]
284+
"""
285+
return [
286+
self.rows[i][-1 - i] for i in range(min(self.num_rows, self.num_columns))
287+
]
288+
289+
@property
290+
def is_diagonal(self) -> bool:
291+
"""
292+
Returns True if the matrix is diagonal, False otherwise.
293+
Wikipedia reference: https://en.wikipedia.org/wiki/Main_diagonal
294+
295+
>>> print(Matrix([[2, 0], [0, 1]]).is_diagonal)
296+
True
297+
>>> print(Matrix([[2, 0, 0], [0, 1, 0]]).is_diagonal)
298+
True
299+
>>> print(Matrix([[2, 0, 0], [0, 1, 0], [0, 0, 3], [0,0,0]]).is_diagonal)
300+
True
301+
>>> print(Matrix([[1, 2], [3, 4]]).is_diagonal)
302+
False
303+
>>> print(Matrix([[1,2,3,4], [5,6,7,8], [9,10,11,12]]).is_diagonal)
304+
False
305+
"""
306+
size = min(self.num_rows, self.num_columns)
307+
return (
308+
sum(
309+
self.rows[r][c] if r != c else 0
310+
for r in range(size)
311+
for c in range(size)
312+
)
313+
== 0
314+
)
315+
316+
@property
317+
def is_exchange(self) -> bool:
318+
"""
319+
Returns True if the matrix is an exchange matrix, False otherwise.
320+
Wikipedia: https://en.wikipedia.org/wiki/Exchange_matrix
321+
322+
>>> print(Matrix([[1, 0], [0, 1]]).is_exchange)
323+
False
324+
>>> print(Matrix([[0, 1], [1, 0]]).is_exchange)
325+
True
326+
>>> print(Matrix([[0, 2], [3, 0]]).is_exchange)
327+
True
328+
>>> print(Matrix([[1, 2], [3, 4]]).is_exchange)
329+
False
330+
>>> print(Matrix([[1,2,3,4], [5,6,7,8], [9,10,11,12]]).is_exchange)
331+
Traceback (most recent call last):
332+
...
333+
ValueError: Only square matrices can be exchange matrices
334+
"""
335+
if not self.is_square:
336+
raise ValueError("Only square matrices can be exchange matrices")
337+
338+
size = self.num_rows
339+
return (
340+
sum(
341+
self.rows[r][c] if r != size - c - 1 else 0
342+
for r in range(size)
343+
for c in range(size)
344+
)
345+
== 0
346+
)
347+
348+
@property
349+
def is_elementary(self) -> bool:
350+
"""
351+
An elementary matrix is a matrix that can be obtained from the identity matrix
352+
by exactly one elementary row operation.
353+
Wikipedia reference: https://en.wikipedia.org/wiki/Elementary_matrix
354+
355+
For a square matrix, there are only three possibilities:
356+
Row swapping: A row can be swapped with another row.
357+
Row multiplication: A row can be multiplied by a value other than 0 or 1.
358+
Row addition: A row can be replaced by the sum of that row and
359+
a non-zero multiple of another row.
360+
361+
>>> print(Matrix([[0]]).is_elementary)
362+
False
363+
>>> print(Matrix([[1]]).is_elementary)
364+
False
365+
>>> print(Matrix([[2]]).is_elementary)
366+
True
367+
>>> print(Matrix([[1,0,0], [0,1,0], [0,0,1]]).is_elementary)
368+
False
369+
>>> print(Matrix([[1,0,0], [0,0,1], [0,1,0]]).is_elementary)
370+
True
371+
>>> print(Matrix([[0,0,1], [1,0,0], [1,0,0]]).is_elementary)
372+
False
373+
>>> print(Matrix([[2,0,0], [0,1,0], [0,0,1]]).is_elementary)
374+
True
375+
>>> print(Matrix([[1,0,0], [0,2,0], [0,0,1]]).is_elementary)
376+
True
377+
>>> print(Matrix([[2,0,0], [0,2,0], [0,0,1]]).is_elementary)
378+
False
379+
>>> print(Matrix([[1,2,0], [0,1,0], [0,0,1]]).is_elementary)
380+
True
381+
>>> print(Matrix([[1,2,0], [0,1,0], [0,0,1]]).is_elementary)
382+
True
383+
>>> print(Matrix([[1,2,0], [0,2,0], [0,0,1]]).is_elementary)
384+
False
385+
>>> print(Matrix([[1,0,0], [0,1,0], [0,3,1]]).is_elementary)
386+
True
387+
>>> print(Matrix([[1,0,3], [0,1,0], [0,3,1]]).is_elementary)
388+
False
389+
>>> print(Matrix([[1,0], [0,1], [0,3]]).is_elementary)
390+
Traceback (most recent call last):
391+
...
392+
ValueError: Only square matrices can be elementary
393+
"""
394+
if not self.is_square:
395+
raise ValueError("Only square matrices can be elementary")
396+
size = self.order[0]
397+
if size == 1:
398+
# Special case:
399+
# A 1 x 1 matrix is elementary if its value is different from 0 or 1
400+
return self.rows[0][0] not in [0, 1]
401+
402+
# Check row swapping
403+
# True when:
404+
# matrix is binary
405+
# matrix is not the identity matrix
406+
# exactly 2 values are not on the diagonal
407+
if (
408+
self.is_binary
409+
and self != self.identity()
410+
and sum(
411+
1 if r == c and self.rows[r][c] == 0 else 0
412+
for r in range(size)
413+
for c in range(size)
414+
)
415+
== 2
416+
):
417+
return True
418+
# Type 2: row multiplication
419+
# True when:
420+
# matrix is diagonal
421+
# only 1 value differs from 1
422+
if self.is_diagonal and sum(x != 1 for x in self.diagonal()) == 1:
423+
return True
424+
425+
# Type 3: row addition
426+
# True when:
427+
# values on diagonal are all 1
428+
# only 1 value not on the diagonal differs from 0
429+
return (
430+
all(v == 1 for v in self.diagonal())
431+
and sum(
432+
0 if r == c or self.rows[r][c] == 0 else 1
433+
for r in range(size)
434+
for c in range(size)
435+
)
436+
== 1
437+
)
438+
157439
def determinant(self) -> int:
158440
if not self.is_square:
159441
return 0

0 commit comments

Comments
 (0)