All files augmented-matrix.mjs

98.07% Statements 51/52
93.75% Branches 15/16
100% Functions 9/9
97.95% Lines 48/49

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137                  445x     445x 445x                 297x 297x 297x               187x 187x               124x 124x                 3x 3x               124x                 110x         110x 228x 103x   125x   110x 110x 110x 6x   104x 124x 124x 124x 124x 124x                         24x 24x 24x 24x   24x 71x 71x 71x   8x   63x 3x 3x 3x   63x 63x 63x 63x     24x 47x   24x              
import { Matrix } from "./matrix.mjs";
 
export class AugmentedMatrix {
    /**
     * 
     * @param {Matrix} leftHandSide 
     * @param {Matrix=} rightHandSide 
     */
    constructor(leftHandSide, rightHandSide) {
        Iif (rightHandSide && leftHandSide.rows !== rightHandSide.rows) {
            throw Error('The left hand side must have the same number of rows as the right hand side');
        }
        this._leftHandSide = leftHandSide;
        this._rightHandSide = rightHandSide ?? new Matrix(leftHandSide.rows, 0);
    }
 
    /**
     * Returns a view of the augmented matrix that only contains the selected row
     * @param {number} row 
     * @returns {AugmentedMatrix}
     */
    getRow(row) {
        const leftHandSide = this._leftHandSide.slice(0, this._leftHandSide.cols, row, row + 1);
        const rightHandSide = this._rightHandSide.slice(0, this._rightHandSide.cols, row, row + 1);
        return new AugmentedMatrix(leftHandSide, rightHandSide);
    }
 
    /**
     * Multiplies both sides of equation
     * @param {number} factor
     */
    multiply(factor) {
        this._leftHandSide.multiply(factor, this._leftHandSide);
        this._rightHandSide.multiply(factor, this._rightHandSide);
    }
 
    /**
     * 
     * @param {AugmentedMatrix} other
     */
    add(other) {
        this._leftHandSide.add(other._leftHandSide, this._leftHandSide);
        this._rightHandSide.add(other._rightHandSide, this._rightHandSide);
    }
 
    /**
     * 
     * @param {number} rowOne 
     * @param {number} rowTwo 
     */
    rowSwap(rowOne, rowTwo) {
        this._leftHandSide.rowSwap(rowOne, rowTwo);
        this._rightHandSide.rowSwap(rowOne, rowTwo);
    }
 
    /**
     * Deep copy of system
     * @returns {AugmentedMatrix}
     */
    copy() {
        return new AugmentedMatrix(this._leftHandSide.copy(), this._rightHandSide.copy());
    }
 
    /**
     * Clears out rows below or above pivot row.
     * @param {number} pivot
     * @param {'up'|'down'} direction the direction to go
     */
    clearOutRows(pivot, direction) {
        const delta = direction === 'up' ? -1 : 1;
        /**
         * @param {number} row 
         * @returns {boolean}
         */
        const condition = (row) => {
            if (direction === 'up') {
                return row >= 0;
            }
            return row < this._leftHandSide.rows;
        };
        const pivotRowEquation = this.getRow(pivot);
        const pivotValue = this._leftHandSide.get(pivot, pivot);
        if (pivotValue < Number.EPSILON) {
            return;
        }
        for (let targetRow = pivot+delta; condition(targetRow); targetRow += delta) {
            const multiples = this._leftHandSide.get(pivot, targetRow) / pivotValue;
            const targetRowEquation = this.getRow(targetRow);
            const subtractOff = pivotRowEquation.copy();
            subtractOff.multiply(-multiples);
            targetRowEquation.add(subtractOff);
        }
    }
 
    /**
     * Performs Gauss Jordan Elimination on linear system
     * @returns {{
     *  leftHandSide: Matrix,
     *  rightHandSide: Matrix,
     *  scaledDown: number,
     * }}
     */
    gje() {
        const rightHandSide = this._rightHandSide;
        const leftHandSide = this._leftHandSide;
        let swaps = 0;
        let totalScale = 1;
        // put left matrix into row echelon
        for (let row = 0; row < Math.min(leftHandSide.rows, leftHandSide.cols); row++) {
            let scaleFactor = leftHandSide.get(row, row);
            const pivot = leftHandSide.findFirstRowWithPivot(row);
            if (pivot === undefined) {
                // There's not a pivot for this row so no need to clear out the rows below
                continue;
            }
            if (pivot !== row) {
                swaps++;
                this.rowSwap(row, pivot);
                scaleFactor = leftHandSide.get(row, row);
            }
            totalScale *= scaleFactor;
            const equation = this.getRow(row);
            equation.multiply(1 / scaleFactor);
            this.clearOutRows(row, 'down');
        }
        // convert left into reduced row echelon
        for (let row = leftHandSide.rows - 1; row > 0; row--) {
            this.clearOutRows(row, 'up');
        }
        return {
            leftHandSide,
            rightHandSide,
            scaledDown: (-1) ** swaps * totalScale,
        }
    }
}