Untitled
unknown
python
a year ago
9.4 kB
3
Indexable
from typing import Callable, TypeVar, Tuple, Optional, Generic, Union, List from common.utils import randomize_int, argmax from matrix.Vector import Vector import numbers T = TypeVar('T') class Matrix(Generic[T]): def __init__(self, content): assert len(content) > 0 assert all(len(row) > 0 for row in content) self.rows = len(content) self.cols = len(content[0]) self.content = tuple(tuple(row) for row in content) assert all(self.cols == len(row) for row in content) if self.rows == self.cols: from matrix.SqMatrix import SqMatrix self.__class__ = SqMatrix self.dim = self.rows def __repr__(self): return "Matrix(" + ",\n".join( ["(".ljust(6) + ", ".join(map(repr, row)).ljust(6) + ")" for row in self.content]) + ")" def format(self, file=True): strs = [[e.__repr__() for e in row] for row in self.content] max_len = max(len(s) for row in strs for s in row) eq_lens = [[" " * (max_len - len(s)) + s for s in row] for row in strs] str_repr = "[" + "\n ".join([', '.join(row) for row in eq_lens]) + " ]" if file is False: return str_repr elif file is True: print(str_repr) else: print(str_repr, file=file) @staticmethod def tabulate(rows: int, cols: int, f: Callable[[int, int], T]) -> 'Matrix': return Matrix([[f(i, j) for j in range(cols)] for i in range(rows)]) @staticmethod def random(rows: int, cols: int, randomizer: Callable[[], T] = randomize_int()) -> 'Matrix[T]': return Matrix([[randomizer() for _ in range(cols)] for _ in range(rows)]) @staticmethod def zero(rows: int, cols: int) -> 'Matrix[T]': return Matrix([[0] * cols for _ in range(rows)]) def __getitem__(self, items): return self.content[items] def __eq__(self, other) -> bool: if not isinstance(other, Matrix) or self.rows != other.rows or self.cols != other.cols: return False return all(self.content[i][j] == other[i][j] for i in range(self.rows) for j in range(self.cols)) def __add__(self, other: 'Matrix') -> 'Matrix': if self.rows != other.rows or self.cols != other.cols: raise ValueError("Matrix dimensions do not match for addition.") return Matrix([[self[i][j] + other[i][j] for j in range(self.cols)] for i in range(self.rows)]) def __sub__(self, other: 'Matrix') -> 'Matrix': if self.rows != other.rows or self.cols != other.cols: raise ValueError("Matrix dimensions do not match for subtraction.") return Matrix([[self[i][j] - other[i][j] for j in range(self.cols)] for i in range(self.rows)]) def suppress_rc(self, remove_row: int, remove_col: int) -> 'Matrix': return Matrix( [[self[i][j] for j in range(self.cols) if j != remove_col] for i in range(self.rows) if i != remove_row]) def replace_col(self, k: int, v: Vector) -> 'Matrix': if v.dim != self.rows: raise ValueError("Vector dimension must be equal to the number of rows in the matrix.") return Matrix([[v[i] if j == k else self[i][j] for j in range(self.cols)] for i in range(self.rows)]) def scale(self, num: T) -> 'Matrix': return Matrix([[num * self[i][j] for j in range(self.cols)] for i in range(self.rows)]) def __mul__(self, other: 'Matrix') -> Union['Matrix', Vector]: if isinstance(other, numbers.Number): return self.scale(other) elif isinstance(other, Matrix) and self.cols == other.rows: return Matrix([[sum(self[i][k] * other[k][j] for k in range(self.cols)) for j in range(other.cols)] for i in range(self.rows)]) elif isinstance(other, Vector) and self.cols == other.dim: return Vector([sum(self[i][j] * other[j] for j in range(self.cols)) for i in range(self.rows)]) else: raise RuntimeError(f"Cannot multiply {self} by {other}") def row_operation(self, s1: T, r1: int, s2: T, r2: int) -> 'Matrix': return Matrix( [self[i] if i != r2 else [s1 * self[r1][j] + s2 * self[r2][j] for j in range(self.cols)] for i in range(self.rows)]) def scale_row(self, s: T, r: int) -> 'Matrix': return Matrix([[s * self[r][j] if i == r else self[i][j] for j in range(self.cols)] for i in range(self.rows)]) def swap_cols(self, c1: int, c2: int) -> 'Matrix': return Matrix( [[self[i][c2] if j == c1 else (self[i][c1] if j == c2 else self[i][j]) for j in range(self.cols)] for i in range(self.rows)]) def swap_rows(self, r1: int, r2: int) -> 'Matrix': return Matrix( [[self[r2][j] if i == r1 else (self[r1][j] if i == r2 else self[i][j]) for j in range(self.cols)] for i in range(self.rows)]) def transpose(self) -> 'Matrix': return Matrix([[self[j][i] for j in range(self.rows)] for i in range(self.cols)]) def adjoin_col(self, v: Vector) -> 'Matrix': if v.dim != self.rows: raise ValueError("Vector dimension must be equal to the number of rows in the matrix.") return Matrix([[self[i][j] for j in range(self.cols)] + [v[i]] for i in range(self.rows)]) def adjoin_cols(self, m: 'Matrix') -> 'Matrix': if self.rows != m.rows: raise ValueError("Matrices must have the same number of rows to be adjoined.") return Matrix( [[self[i][j] for j in range(self.cols)] + [m[i][k] for k in range(m.cols)] for i in range(self.rows)]) def extract_rows(self, keep) -> 'Matrix': return Matrix([self[i] for i in keep]) def extract_cols(self, keep) -> 'Matrix': return Matrix([[self[i][j] for j in keep] for i in range(self.rows)]) def adjoin_rows(self, m: 'Matrix') -> 'Matrix': if self.cols != m.cols: raise ValueError("Matrices must have the same number of columns to be adjoined.") return Matrix(self.content + m.content) def adjoin_row(self, v: Vector) -> 'Matrix': if v.dim != self.cols: raise ValueError("Vector dimension must be equal to the number of columns in the matrix.") return Matrix(self.content + [v.content]) def row_vec(self, r: int) -> Vector: return Vector(self.content[r]) def col_vec(self, c: int) -> Vector: return Vector([self[i][c] for i in range(self.rows)]) @staticmethod def cols_to_matrix(vectors: List[Vector]) -> 'Matrix': if not vectors: raise ValueError("List of vectors cannot be empty.") if not all(len(vectors[0]) == len(v) for v in vectors): raise ValueError("All vectors must have the same dimension.") return Matrix([[v[i] for v in vectors] for i in range(len(vectors[0]))]) @staticmethod def rows_to_matrix(vectors: List[Vector]) -> 'Matrix': if not vectors: raise ValueError("List of vectors cannot be empty.") if not all(len(vectors[0]) == len(v) for v in vectors): raise ValueError("All vectors must have the same dimension.") return Matrix([v.content for v in vectors]) def back_substitution(self) -> Vector: if self.cols != self.rows + 1: raise ValueError("Matrix must have k rows and k+1 columns for back substitution.") dim = self.rows x = [0] * dim for k in range(dim - 1, -1, -1): x[k] = (self[k][-1] - sum(self[k][j] * x[j] for j in range(k + 1, dim))) / self[k][k] return Vector(x) def make_row_echelon(self) -> 'Matrix': m = self for k in range(0, self.rows - 1): pivot = m.find_pivot_row(k) if pivot is None: return None if pivot != k: m = m.swap_rows(k, pivot) for r in range(k + 1, self.rows): m = m.row_operation(1, k, -m[r][k] / m[k][k], r) return m def make_unit_diagonal(self) -> Tuple[Optional['Matrix'], T]: m = self det = 1 for k in range(0, self.rows): pivot = m.find_pivot_row(k) if pivot is None: return None, 0 if pivot != k: m = m.swap_rows(k, pivot) det *= -1 det *= m[k][k] m = m.scale_row(1 / m[k][k], k) for r in range(0, self.rows): if r != k: det *= -1 / m[k][k] m = m.row_operation(1, k, -m[r][k] / m[k][k], r) m = m.replace_col(k, Vector([1 if k == c else 0 for c in range(m.rows)])) return m, det def find_pivot_row(self, c: int, epsilon: float = 0.00001): p = argmax(lambda r: abs(self[r][c]), range(c, self.rows)) return p if abs(self[p][c]) > epsilon else None def norm(self) -> T: return max(abs(self[i][j]) for i in range(self.rows) for j in range(self.cols)) def distance(self, other) -> T: return (self - other).norm() if isinstance(other, Matrix) else NotImplemented
Editor is loading...
Leave a Comment