Skip to content

Instantly share code, notes, and snippets.

@evtn
Last active March 10, 2022 09:51
Show Gist options
  • Select an option

  • Save evtn/8683e58770f2901527275d46465e4cbe to your computer and use it in GitHub Desktop.

Select an option

Save evtn/8683e58770f2901527275d46465e4cbe to your computer and use it in GitHub Desktop.
Simple matrix calculator

Basic usage

from matrix import Matrix
# 1 0
# 2 0
# 3 0
A = Matrix.from_list([[1, 0], [2, 0], [3, 0]])

# 0 0 0
# 0 0 0
# 0 0 0
B = Matrix(3, 3)
# 10 0  0
# 0  89 0
# 0  0  0
B[1, 1] = 10
B[2, 2] = 89

# 10  0
# 178 0
# 0   0
C = B * A
for i, j in C:
    print(i, j, C[i, j]) # prints every pair of indices and element on that position
from typing import Tuple, Callable, List, Dict, Union
Number = Union[float, int]
class MatrixIter:
def __init__(self, matrix):
self.matrix = matrix
self.index = 0
def __iter__(self):
return self
def __next__(self) -> Tuple[int]:
prev_index = self.index
self.index += 1
if prev_index >= (self.matrix.rows * self.matrix.columns):
raise StopIteration
return (
prev_index % self.matrix.rows + 1,
prev_index // self.matrix.rows + 1
)
class Matrix:
def __init__(self, rows: int, columns: int):
self.data: Dict[Tuple[int], Number] = {}
self.rows: int = rows
self.columns: int = columns
@property
def size(self) -> Tuple[int]:
return (self.rows, self.columns)
def cut(self, ij: Tuple[int]) -> "Matrix":
self.bound_check(ij)
new = Matrix(self.rows - 1, self.columns - 1)
ci, cj = ij
for i, j in self:
ri = i - (i > ci)
rj = j - (j > cj)
if not (ci == i or cj == j):
new[ri, rj] = self[i, j]
return new
def __eq__(self, other: "Matrix") -> bool:
return (self.rows == other.rows) and (self.columns == other.columns) and (self.data == other.data)
def bound_check(self, ij: Tuple[int]) -> None:
if (ij[0] - 1) not in range(self.rows):
raise IndexError("invalid row index: {} (Must be in 1...{} range)".format(ij[0], self.rows))
if (ij[1] - 1) not in range(self.columns):
raise IndexError("invalid column index: {} (Must be in 1...{} range)".format(ij[1], self.columns))
def __getitem__(self, ij: Tuple[int]) -> Number:
self.bound_check(ij)
return self.data.get(ij, 0)
def __setitem__(self, ij: Tuple[int], value: Number) -> None:
self.bound_check(ij)
if not value:
self.data.pop(ij, 0)
self.data[ij] = value
def __iter__(self):
return MatrixIter(self)
def __str__(self) -> str:
return "Matrix object ({}x{} matrix)".format(*self.size)
def __matmul__(self, other: "Matrix") -> "Matrix":
return self * other
def __mul__(self, other: Union["Matrix", Number]) -> "Matrix":
if type(other) in [int, float]:
new = self.new()
for i, j in self:
new[i, j] = self[i, j] * other
return new
if self.columns != other.rows:
raise ValueError("Can't multiply ({self.rows} x {self.columns}) matrix and ({other.rows} x {other.columns}) matrix".format(self=self, other=other))
new = Matrix(self.rows, other.columns)
for i, j in new:
new[i, j] = sum([self[i, x] * other[x, j] for x in range(1, self.columns + 1)])
return new
def __add__(self, other: "Matrix") -> "Matrix":
if self.size != other.size:
raise ValueError("Can't add matrices with different sizes")
new = self.new()
for i, j in self:
new[i, j] = self[i, j] + other[i, j]
return new
def __sub__(self, other: "Matrix") -> "Matrix":
return self + (-other)
def __neg__(self) -> "Matrix":
return self * -1
def apply(self, func: Callable) -> "Matrix":
new = self.new()
for i, j in self:
new[i, j] = func(self[i, j])
return new
def new(self) -> "Matrix":
return Matrix(self.rows, self.columns)
def copy(self) -> "Matrix":
data = self.data.copy()
new = self.new()
new.data = data
return new
def transpose(self) -> "Matrix":
new = Matrix(self.columns, self.rows)
for i, j in new:
new[i, j] = self[j, i]
return new
def to_list(self) -> List[List[Number]]:
result = []
for i in range(self.rows):
result.append([])
for j in range(self.columns):
result[i].append(self[i + 1, j + 1])
return result
def to_flat_list(self) -> List[Number]:
if 1 not in self.size:
raise ValueError("Can flatten only vector-like matrices")
l = self.to_list()
if self.columns == 1:
return [c[0] for c in l]
return l[0]
@property
def determinant(self) -> Number:
if self.m != self.n:
raise ValueError("Can't calculate determinant for non-square matrix")
if not any(sum(x[1:]) for x in self.data[1:]):
return 0
if self.m == 2:
return self[1][1] * self[2][2] - self[1][2] * self[2][1]
return sum([((-1) ** (s + 1)) * self[1][s] * self.minor_matrix(cj=s).determinant for s in self.columns])
@staticmethod
def from_list(data: List[List[Number]]) -> "Matrix":
rows = len(data)
columns_lengths = set([len(x) for x in data])
if not columns_lengths:
raise ValueError("Your matrix is empty")
if len(columns_lengths) > 1:
raise ValueError("Columns of your list have different sizes")
columns = len(data[0])
new = Matrix(rows, columns)
for i, row in enumerate(data):
for j, value in enumerate(row):
new[i + 1, j + 1] = value
return new
@staticmethod
def scalar(size, num=1) -> "Matrix":
new = Matrix(size, size)
for i in range(1, size + 1):
new[i, i] = num
return new
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment