Untitled
unknown
python
2 years ago
9.4 kB
10
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