/**
* Matrix operations for advanced mathematical computations
* Handles Gaussian elimination, matrix manipulations, and linear algebra
*/
export class MatrixOperations {
/**
* Performs Gaussian elimination on a matrix
* @param coefficientMatrix Matrix of coefficients
* @param constantVector Vector of constants
* @returns Reduced matrix, rank, and whether a solution exists
*/
gaussianElimination(coefficientMatrix: number[][], constantVector: number[]): {
reducedMatrix: number[][],
rank: number,
hasSolution: boolean
} {
const numRows = coefficientMatrix.length;
const numCols = coefficientMatrix[0].length;
// Create augmented matrix
const augmentedMatrix: number[][] = [];
for (let i = 0; i < numRows; i++) {
augmentedMatrix.push([...coefficientMatrix[i], constantVector[i]]);
}
// Forward elimination
let rank = 0;
for (let col = 0; col < numCols; col++) {
// Find pivot row
let pivotRow = this.findPivotRow(augmentedMatrix, rank, col);
if (pivotRow === -1) continue; // No pivot in this column
// Swap rows if needed
if (pivotRow !== rank) {
this.swapRows(augmentedMatrix, rank, pivotRow);
}
// Normalize pivot row
this.normalizePivotRow(augmentedMatrix, rank, col);
// Eliminate below
this.eliminateBelow(augmentedMatrix, rank, col);
rank++;
}
// Back substitution
this.backSubstitution(augmentedMatrix, rank, numCols);
// Check for inconsistent system
const hasSolution = this.checkConsistency(augmentedMatrix, rank, numRows, numCols);
return {
reducedMatrix: augmentedMatrix,
rank,
hasSolution
};
}
/**
* Finds the pivot row for a given column
* @param matrix The augmented matrix
* @param startRow Starting row to search from
* @param col Column to find pivot for
* @returns Row index of pivot or -1 if not found
*/
private findPivotRow(matrix: number[][], startRow: number, col: number): number {
let maxRow = -1;
let maxValue = 0;
for (let row = startRow; row < matrix.length; row++) {
const absValue = Math.abs(matrix[row][col]);
if (absValue > 1e-10 && absValue > maxValue) {
maxValue = absValue;
maxRow = row;
}
}
return maxRow;
}
/**
* Swaps two rows in a matrix
* @param matrix The matrix
* @param row1 First row index
* @param row2 Second row index
*/
private swapRows(matrix: number[][], row1: number, row2: number): void {
[matrix[row1], matrix[row2]] = [matrix[row2], matrix[row1]];
}
/**
* Normalizes the pivot row by dividing by the pivot element
* @param matrix The matrix
* @param row Row to normalize
* @param col Pivot column
*/
private normalizePivotRow(matrix: number[][], row: number, col: number): void {
const pivot = matrix[row][col];
if (Math.abs(pivot) < 1e-10) return;
for (let j = col; j < matrix[row].length; j++) {
matrix[row][j] /= pivot;
}
}
/**
* Eliminates elements below the pivot
* @param matrix The matrix
* @param pivotRow Row containing the pivot
* @param pivotCol Column containing the pivot
*/
private eliminateBelow(matrix: number[][], pivotRow: number, pivotCol: number): void {
for (let row = pivotRow + 1; row < matrix.length; row++) {
const factor = matrix[row][pivotCol];
if (Math.abs(factor) < 1e-10) continue;
for (let col = pivotCol; col < matrix[row].length; col++) {
matrix[row][col] -= factor * matrix[pivotRow][col];
}
}
}
/**
* Performs back substitution to get reduced row echelon form
* @param matrix The matrix
* @param rank Rank of the matrix
* @param numCols Number of columns in original matrix
*/
private backSubstitution(matrix: number[][], rank: number, numCols: number): void {
for (let row = rank - 1; row > 0; row--) {
// Find leading coefficient
let leadingCol = -1;
for (let col = 0; col < numCols; col++) {
if (Math.abs(matrix[row][col]) > 1e-10) {
leadingCol = col;
break;
}
}
if (leadingCol === -1) continue;
// Eliminate above
for (let i = 0; i < row; i++) {
const factor = matrix[i][leadingCol];
if (Math.abs(factor) < 1e-10) continue;
for (let j = leadingCol; j < matrix[i].length; j++) {
matrix[i][j] -= factor * matrix[row][j];
}
}
}
}
/**
* Checks if the system is consistent
* @param matrix The reduced matrix
* @param rank Rank of the matrix
* @param numRows Number of rows
* @param numCols Number of columns in original matrix
* @returns True if consistent
*/
private checkConsistency(matrix: number[][], rank: number, numRows: number, numCols: number): boolean {
for (let row = rank; row < numRows; row++) {
// Check if all coefficients are zero
let allZero = true;
for (let col = 0; col < numCols; col++) {
if (Math.abs(matrix[row][col]) > 1e-10) {
allZero = false;
break;
}
}
// If all coefficients are zero but constant is not, system is inconsistent
if (allZero && Math.abs(matrix[row][numCols]) > 1e-10) {
return false;
}
}
return true;
}
/**
* Multiplies two matrices
* @param a First matrix
* @param b Second matrix
* @returns Product matrix
*/
multiplyMatrices(a: number[][], b: number[][]): number[][] {
if (a[0].length !== b.length) {
throw new Error('Invalid matrix dimensions for multiplication');
}
const result: number[][] = [];
for (let i = 0; i < a.length; i++) {
result[i] = [];
for (let j = 0; j < b[0].length; j++) {
let sum = 0;
for (let k = 0; k < a[0].length; k++) {
sum += a[i][k] * b[k][j];
}
result[i][j] = sum;
}
}
return result;
}
/**
* Calculates the determinant of a matrix
* @param matrix Square matrix
* @returns Determinant value
*/
determinant(matrix: number[][]): number {
const n = matrix.length;
if (n !== matrix[0].length) {
throw new Error('Determinant requires a square matrix');
}
if (n === 1) {
return matrix[0][0];
}
if (n === 2) {
return matrix[0][0] * matrix[1][1] - matrix[0][1] * matrix[1][0];
}
// Use cofactor expansion for larger matrices
let det = 0;
for (let j = 0; j < n; j++) {
det += (j % 2 === 0 ? 1 : -1) * matrix[0][j] * this.determinant(this.getMinor(matrix, 0, j));
}
return det;
}
/**
* Gets the minor of a matrix (matrix with one row and column removed)
* @param matrix Original matrix
* @param row Row to remove
* @param col Column to remove
* @returns Minor matrix
*/
private getMinor(matrix: number[][], row: number, col: number): number[][] {
const minor: number[][] = [];
for (let i = 0; i < matrix.length; i++) {
if (i === row) continue;
const minorRow: number[] = [];
for (let j = 0; j < matrix[i].length; j++) {
if (j === col) continue;
minorRow.push(matrix[i][j]);
}
minor.push(minorRow);
}
return minor;
}
/**
* Inverts a matrix
* @param matrix Square matrix to invert
* @returns Inverted matrix
*/
invertMatrix(matrix: number[][]): number[][] {
const n = matrix.length;
if (n !== matrix[0].length) {
throw new Error('Inversion requires a square matrix');
}
const det = this.determinant(matrix);
if (Math.abs(det) < 1e-10) {
throw new Error('Matrix is singular and cannot be inverted');
}
// Create augmented matrix [A|I]
const augmented: number[][] = [];
for (let i = 0; i < n; i++) {
augmented[i] = [...matrix[i]];
for (let j = 0; j < n; j++) {
augmented[i].push(i === j ? 1 : 0);
}
}
// Apply Gaussian elimination
for (let i = 0; i < n; i++) {
// Find pivot
let pivot = augmented[i][i];
if (Math.abs(pivot) < 1e-10) {
// Swap rows
for (let k = i + 1; k < n; k++) {
if (Math.abs(augmented[k][i]) > 1e-10) {
this.swapRows(augmented, i, k);
pivot = augmented[i][i];
break;
}
}
}
// Normalize row
for (let j = 0; j < 2 * n; j++) {
augmented[i][j] /= pivot;
}
// Eliminate column
for (let k = 0; k < n; k++) {
if (k === i) continue;
const factor = augmented[k][i];
for (let j = 0; j < 2 * n; j++) {
augmented[k][j] -= factor * augmented[i][j];
}
}
}
// Extract inverse from right side
const inverse: number[][] = [];
for (let i = 0; i < n; i++) {
inverse[i] = augmented[i].slice(n);
}
return inverse;
}
}