247 lines
6.3 KiB
TypeScript
247 lines
6.3 KiB
TypeScript
|
|
export type RawMatrix = number[][];
|
|
|
|
export enum TriangularForm {UPPER, LOWER};
|
|
|
|
export class Matrix {
|
|
|
|
rows!: number;
|
|
cols!: number;
|
|
matrix!: RawMatrix;
|
|
|
|
static zero(rows: number, cols: number) {
|
|
let matrix: RawMatrix = [];
|
|
for(let i=0; i<rows; i++) {
|
|
matrix.push([]);
|
|
for(let j=0; j<cols; j++) {
|
|
matrix[i].push(0);
|
|
}
|
|
}
|
|
return new Matrix(...matrix);
|
|
}
|
|
|
|
constructor(...matrix: RawMatrix) {
|
|
this.set(...matrix);
|
|
}
|
|
|
|
set(...matrix: RawMatrix) {
|
|
this.matrix = matrix;
|
|
this.rows = matrix.length;
|
|
if(matrix.length == 0) throw Error("Empty matrix");
|
|
this.cols = matrix[0].length;
|
|
for(let i=0; i<matrix.length; i++) {
|
|
if(matrix[i].length !== this.cols) throw Error("Invalid matrix row " + (i+1));
|
|
}
|
|
}
|
|
|
|
add(other_matrix: Matrix) {
|
|
if(!(other_matrix.cols == this.cols && other_matrix.rows == this.rows)) throw Error("Other matrix has more or few rows or columns");
|
|
for(let i=0; i<this.matrix.length; i++) {
|
|
for(let j=0; j<this.matrix[i].length; j++) {
|
|
this.matrix[i][j] += other_matrix.matrix[i][j];
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
sub(other_matrix: Matrix) {
|
|
if(!(other_matrix.cols == this.cols && other_matrix.rows == this.cols)) throw Error("Other matrix has more or few rows or columns");
|
|
for(let i=0; i<this.matrix.length; i++) {
|
|
for(let j=0; j<this.matrix[i].length; j++) {
|
|
this.matrix[i][j] -= other_matrix.matrix[i][j];
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
|
|
mul(other: Matrix | number) {
|
|
if(other instanceof Matrix) {
|
|
if(!(this.cols == other.rows)) throw Error("These matrices cannot be multiplied");
|
|
let final_matrix: RawMatrix = [];
|
|
for(let a_row_i=0; a_row_i<this.matrix.length; a_row_i++) {
|
|
final_matrix.push([]);
|
|
for(let b_col_i=0; b_col_i<other.matrix[0].length; b_col_i++) {
|
|
let sum = 0;
|
|
for(let a_col_b_row_i=0; a_col_b_row_i<this.matrix[0].length; a_col_b_row_i++) {
|
|
sum += this.matrix[a_row_i][a_col_b_row_i] * other.matrix[a_col_b_row_i][b_col_i];
|
|
}
|
|
final_matrix[a_row_i].push(sum);
|
|
}
|
|
}
|
|
return new Matrix(...final_matrix);
|
|
} else {
|
|
for(let i=0; i<this.matrix.length; i++) {
|
|
for(let j=0; j<this.matrix[i].length; j++) {
|
|
this.matrix[i][j] = this.matrix[i][j] * other;
|
|
}
|
|
}
|
|
return this;
|
|
}
|
|
}
|
|
|
|
pow(power: number) {
|
|
if(power > 0)
|
|
for(let i=0; i<(power-1); i++)
|
|
this.set(...this.mul(this).matrix);
|
|
return this;
|
|
}
|
|
|
|
invert() {
|
|
this.matrix = this.mul(-1).matrix;
|
|
return this;
|
|
}
|
|
|
|
transpose() {
|
|
let new_matrix = Matrix.zero(this.cols, this.rows);
|
|
for(let i=0; i<this.rows; i++) {
|
|
for(let j=0; j<this.cols; j++) {
|
|
new_matrix.matrix[j][i] = this.matrix[i][j];
|
|
}
|
|
}
|
|
return new_matrix;
|
|
}
|
|
|
|
diagonal() {
|
|
let diagonal: number[] = [];
|
|
for(let i=0; i<Math.min(this.rows, this.cols); i++) {
|
|
diagonal.push(this.matrix[i][i]);
|
|
}
|
|
return diagonal;
|
|
}
|
|
|
|
eq(other_matrix: Matrix) {
|
|
if(this.rows != other_matrix.rows || this.cols != other_matrix.cols) return false;
|
|
for(let i=0; i<this.rows; i++) {
|
|
for(let j=0; j<this.cols; j++) {
|
|
if(this.matrix[i][j] != other_matrix.matrix[i][j]) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
is_in_elchelon_form() {
|
|
let pivot_index = -1;
|
|
for(let i=0; i<this.rows; i++) {
|
|
for(let j=0; j<this.cols; j++) {
|
|
if(this.matrix[i][j] == 0) {
|
|
if(j > pivot_index) pivot_index = j;
|
|
} else if(pivot_index >= j) return false;
|
|
}
|
|
pivot_index++;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
is_triangular(form: TriangularForm = TriangularForm.UPPER) {
|
|
for(let i=0; i<Math.min(this.rows, this.cols); i++) {
|
|
for(let j=0; j<i; j++) {
|
|
if(this.matrix[i][j] !== 0 && form == TriangularForm.UPPER) return false;
|
|
if(this.matrix[j][i] !== 0 && form == TriangularForm.LOWER) return false;
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
pivots() {
|
|
let pivots: number[] = [];
|
|
for(let i=0; i<this.rows; i++) {
|
|
let col = 0;
|
|
for(let j=0; j<this.cols; j++) {
|
|
if(this.matrix[i][j] != 0) break;
|
|
col++;
|
|
}
|
|
pivots.push(col);
|
|
}
|
|
return pivots;
|
|
}
|
|
|
|
remove_row(row: number) {
|
|
let m = [...this.matrix];
|
|
m.splice(row, 1); this.set(...m);
|
|
}
|
|
|
|
invertible() {
|
|
let matrix = Matrix.zero(this.rows, this.cols*2);
|
|
this.matrix.forEach((row, i) => {
|
|
matrix.matrix[i][this.cols+i] = 1;
|
|
row.forEach((col, j) => {
|
|
matrix.matrix[i][j] = col;
|
|
});
|
|
});
|
|
let gje = GaussianElimination.gje(matrix).matrix.map(GaussianElimination.make_pivot_1);
|
|
let result = Matrix.zero(this.rows, this.cols);
|
|
result.set(...result.matrix.map((row, i) => {
|
|
return row.map((col, j) => gje[i][this.cols+j]);
|
|
}));
|
|
return result;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
export abstract class GaussianElimination {
|
|
|
|
static make_pivot_1(row: number[]) {
|
|
let d = 0;
|
|
return row.map((n, i) => {
|
|
if(n != 0 && !d) d = n;
|
|
return d && n ? n / d : 0;
|
|
});
|
|
}
|
|
|
|
static sort(matrix: Matrix) {
|
|
let m = matrix.matrix;
|
|
let new_matrix = [m[0]];
|
|
let pivots = matrix.pivots();
|
|
for(let i=1; i<pivots.length; i++) {
|
|
if(pivots[i] < pivots[i-1]) new_matrix.unshift(m[i]);
|
|
else new_matrix.push(m[i]);
|
|
}
|
|
return new Matrix(...new_matrix);
|
|
}
|
|
|
|
static ge(matrix: Matrix): Matrix {
|
|
for(let v_offset=0; v_offset<matrix.rows; v_offset++) {
|
|
matrix = this.sort(matrix);
|
|
let m = matrix.matrix;
|
|
let h_offset = matrix.pivots()[v_offset];
|
|
let first_number = m[v_offset][h_offset];
|
|
for(let i=1+v_offset; i<matrix.rows; i++) {
|
|
let n = -m[i][h_offset]/first_number;
|
|
m[i] = m[i].map((c, j) => c + n*m[v_offset][j]);
|
|
}
|
|
}
|
|
return matrix;
|
|
}
|
|
|
|
static gje(matrix: Matrix): Matrix {
|
|
matrix = this.ge(matrix);
|
|
let pivots = matrix.pivots();
|
|
for(let v_offset=matrix.rows-1; v_offset>0; v_offset--) {
|
|
let m = matrix.matrix;
|
|
let h_offset = pivots[v_offset];
|
|
if(h_offset >= matrix.cols) continue;
|
|
let first_number = m[v_offset][h_offset];
|
|
for(let i=v_offset-1; i>=0; i--) {
|
|
let n = -m[i][h_offset]/first_number;
|
|
m[i] = m[i].map((c, j) => c + n*m[v_offset][j]);
|
|
}
|
|
}
|
|
return matrix;
|
|
}
|
|
|
|
static solve(matrix: Matrix): number[] | Matrix {
|
|
let solved_matrix = this.gje(matrix);
|
|
let pivots = solved_matrix.pivots();
|
|
let results: number[] = [];
|
|
solved_matrix.matrix = solved_matrix.matrix.map(GaussianElimination.make_pivot_1);
|
|
for(let row=0; row<solved_matrix.rows; row++) {
|
|
let col = pivots[row];
|
|
let pivot = solved_matrix.matrix[row][col];
|
|
results.push(solved_matrix.matrix[row][solved_matrix.cols-1]/pivot);
|
|
}
|
|
if(results.filter(n => isNaN(n)).length == 0) return results;
|
|
else return solved_matrix;
|
|
}
|
|
|
|
} |