UNPKG

mathjslab

Version:

MathJSLab - Interpreter with language syntax like MATLAB/Octave

1,042 lines (973 loc) 41.2 kB
import { ComplexDecimal } from './complex-decimal'; declare let EvaluatorPointer: any; export type dimRange = { start: number; stride: number; stop: number; }; export type ComplexDecimalRange = { start: ComplexDecimal; stride: ComplexDecimal; stop: ComplexDecimal; }; export class MultiArray { dim: Array<number>; array: Array<Array<ComplexDecimal>>; static functions: { [name: string]: Function } = { size: MultiArray.size, sub2ind: MultiArray.sub2ind, ind2sub: MultiArray.ind2sub, zeros: MultiArray.zeros, ones: MultiArray.ones, eye: MultiArray.eye, inv: MultiArray.inv, det: MultiArray.det, trace: MultiArray.trace, rows: MultiArray.rows, cols: MultiArray.cols, minor: MultiArray.minor, cofactor: MultiArray.cofactor, adj: MultiArray.adj, pivot: MultiArray.pivot, lu: MultiArray.lu, min: MultiArray.min, max: MultiArray.max, mean: MultiArray.mean, horzcat: MultiArray.horzcat, vertcat: MultiArray.vertcat, gauss: MultiArray.gauss, }; constructor(shape?: number[], fill?: ComplexDecimal) { if (!shape) { this.dim = [0, 0]; this.array = []; } else if (!fill) { this.dim = shape.slice(); this.array = new Array(this.dim[0]); } else { this.dim = shape.slice(); this.array = new Array(this.dim[0]); for (let i = 0; i < this.dim[0]; i++) { this.array[i] = new Array(this.dim[1]).fill(fill); // for (let j = 0; j < this.dim[1]; j++) { // this.array[i][j] = new ComplexDecimal(fill.re, fill.im); // } } } } static firstRow(row: Array<any>): MultiArray { const result = new MultiArray([1, row.length]); result.array[0] = row; return result; } static appendRow(matrix: MultiArray, row: Array<any>): MultiArray { matrix.array.push(row); matrix.dim[0]++; return matrix; } static unparse(tree: MultiArray, that: any): string { let arraystr = ''; for (let i = 0; i < tree.dim[0]; i++) { for (let j = 0; j < tree.dim[1]; j++) { arraystr += that.Unparse(tree.array[i][j]) + ','; } arraystr = arraystr.substring(0, arraystr.length - 1); arraystr += ';'; } arraystr = arraystr.substring(0, arraystr.length - 1); return '[' + arraystr + ']'; } static unparseML(tree: MultiArray, that: any): string { let temp = ''; temp += '<mrow><mo>[</mo><mtable>'; for (let i = 0; i < tree.dim[0]; i++) { temp += '<mtr>'; for (let j = 0; j < tree.dim[1]; j++) { temp += '<mtd>' + that.unparserML(tree.array[i][j]) + '</mtd>'; } temp += '</mtr>'; } temp += tree.array.length > 0 ? '</mtable><mo>]</mo></mrow>' : "<mspace width='0.5em'/></mtable><mo>]</mo></mrow><mo>(</mo><mn>0</mn><mi>&times;</mi><mn>0</mn><mo>)</mo>"; return temp; } static evaluate(tree: MultiArray, that: any, fname: string): MultiArray { const result = new MultiArray(); for (let i = 0, k = 0; i < tree.array.length; i++, k++) { result.array.push([]); let h = 1; for (let j = 0; j < tree.array[i].length; j++) { const temp = that.Evaluator(tree.array[i][j], false, fname); if (MultiArray.isThis(temp)) { if (j == 0) { h = temp.array.length; result.array.splice(k, 1, temp.array[0]); for (let n = 1; n < h; n++) { result.array.splice(k + n, 0, temp.array[n]); } } else { for (let n = 0; n < temp.array.length; n++) { for (let m = 0; m < temp.array[0].length; m++) { result.array[k + n].push(temp.array[n][m]); } } } } else { result.array[k][j] = temp; } } k += h - 1; if (i != 0) { if (result.array[i].length != result.array[0].length) throw new Error('vertical dimensions mismatch (' + k + 'x' + result.array[0].length + ' vs 1x' + result.array[i].length + ')'); } } result.dim = [result.array.length, result.array.length ? result.array[0].length : 0]; return result; } static isThis(obj: any): boolean { return 'array' in obj; } static isRange(obj: any): boolean { return 'start' in obj; } static mat_0x0(): MultiArray { return new MultiArray([0, 0]); } static arrayEqual(left: Array<number>, right: Array<number>): boolean { return !(left < right || left > right); } static isSameDim(left: MultiArray, right: MultiArray) { return !(left.dim < right.dim || left.dim > right.dim); } static number2matrix1x1(value: ComplexDecimal | MultiArray) { if ('array' in value) { return value; } else if ('re' in value) { const result = new MultiArray([1, 1]); result.array[0] = [value]; return result; // return new MultiArray([1,1],value) } } static clone(M: MultiArray): MultiArray { const result = new MultiArray(M.dim); for (let i = 0; i < M.dim[0]; i++) { result.array[i] = new Array(M.dim[1]); for (let j = 0; j < M.dim[1]; j++) { result.array[i][j] = Object.assign({}, M.array[i][j]); } } return result; } static toLogical(M: MultiArray): ComplexDecimal { for (let i = 0; i < M.dim[0]; i++) { for (let j = 0; j < M.dim[1]; j++) { const value = ComplexDecimal.toMaxPrecision(M.array[i][j]); if (value.re.eq(0) && value.im.eq(0)) { return ComplexDecimal.false(); } } } return ComplexDecimal.true(); } static map(M: MultiArray, f: Function): MultiArray { const result = new MultiArray(M.dim.slice()); for (let i = 0; i < M.dim[0]; i++) { result.array[i] = M.array[i].map(f as any); } return result; } static expandRange(startNode: ComplexDecimal, stopNode: ComplexDecimal, strideNode?: ComplexDecimal | null): MultiArray { const temp = []; const s = strideNode ? strideNode.re.toNumber() : 1; for (let n = startNode.re.toNumber(), i = 0; n <= stopNode.re.toNumber(); n += s, i++) { temp[i] = new ComplexDecimal(n, 0); } const result = new MultiArray([1, temp.length]); result.array = [temp]; return result; } static testIndex(k: ComplexDecimal, bound: number, matrix: MultiArray, input: string): number { if (!k.re.isInteger() || !k.re.gte(1)) throw new Error(input + ': subscripts must be either integers greater than or equal 1 or logicals'); if (!k.im.eq(0)) throw new Error(input + ': subscripts must be real'); const result = k.re.toNumber() - 1; if (result >= bound) { throw new Error(input + ': out of bound ' + bound + ' (dimensions are ' + matrix.dim[0] + 'x' + matrix.dim[1] + ')'); } return result; } static oneRowToDim(M: Array<ComplexDecimal> | MultiArray): Array<number> { if (Array.isArray(M)) { const result = []; for (let i = 0; i < M.length; i++) { result[i] = M[i].re.toNumber(); } return result; } else { const result = []; for (let i = 0; i < M.array[0].length; i++) { result[i] = M.array[0][i].re.toNumber(); } return result; } } static subMatrix(temp: MultiArray, id: string, argumentsList: Array<any>): any { if (argumentsList.length == 1) { // single value indexing if ('array' in argumentsList[0]) { const result = new MultiArray(argumentsList[0].dim.slice(0, 2)); for (let i = 0; i < argumentsList[0].dim[0]; i++) { result.array[i] = new Array(argumentsList[0].dim[1]); for (let j = 0; j < argumentsList[0].dim[1]; j++) { const n = MultiArray.testIndex( argumentsList[0].array[i][j], temp.dim[0] * temp.dim[1], temp, id + '(' + argumentsList[0].array[i][j].re.toNumber() + ')', ); result.array[i][j] = temp.array[n % temp.dim[1]][Math.floor(n / temp.dim[1])]; } } return result; } else { const n = MultiArray.testIndex(argumentsList[0], temp.dim[0] * temp.dim[1], temp, id + '(' + argumentsList[0].re.toNumber() + ')'); return temp.array[n % temp.dim[1]][Math.floor(n / temp.dim[1])]; } } else if (argumentsList.length == 2) { // double value indexing argumentsList[0] = MultiArray.number2matrix1x1(argumentsList[0]); argumentsList[1] = MultiArray.number2matrix1x1(argumentsList[1]); const rows = argumentsList[0].dim[0] * argumentsList[0].dim[1]; const cols = argumentsList[1].dim[0] * argumentsList[1].dim[1]; const result = new MultiArray([rows, cols]); let s = 0; for (let j = 0; j < argumentsList[0].dim[1]; j++) { for (let i = 0; i < argumentsList[0].dim[0]; i++) { const p = MultiArray.testIndex( argumentsList[0].array[i][j], temp.dim[0], temp, id + '(' + argumentsList[0].array[i][j].re.toNumber() + ',_)', ); for (let n = 0; n < argumentsList[1].dim[1]; n++) { for (let m = 0; m < argumentsList[1].dim[0]; m++) { const q = MultiArray.testIndex( argumentsList[1].array[m][n], temp.dim[1], temp, id + '(_,' + argumentsList[1].array[m][n].re.toNumber() + ')', ); if (!(s % cols)) { result.array[Math.floor(s / cols)] = new Array(cols); } result.array[Math.floor(s / cols)][s % cols] = temp.array[p][q]; s++; } } } } if (result.dim[0] == 1 && result.dim[1] == 1) { return result.array[0][0]; } else { return result; } } else { throw new Error(id + '(_,_,...): out of bounds (dimensions are ' + temp.dim[0] + 'x' + temp.dim[1] + ')'); } } static mul(left: MultiArray, right: MultiArray): MultiArray { if (left.dim[1] == right.dim[0]) { //matrix multiplication const result = new MultiArray([left.dim[0], right.dim[1]]); for (let i = 0; i < left.dim[0]; i++) { result.array[i] = new Array(right.dim[1]).fill(ComplexDecimal.zero()); for (let j = 0; j < right.dim[1]; j++) { for (let n = 0; n < left.dim[1]; n++) { result.array[i][j] = ComplexDecimal.add(result.array[i][j], ComplexDecimal.mul(left.array[i][n], right.array[n][j])); } } } return result; } else { throw new Error( 'operator *: nonconformant arguments (op1 is ' + left.dim[0] + 'x' + left.dim[1] + ', op2 is ' + right.dim[0] + 'x' + right.dim[1] + ')', ); } } static scalarOpMultiArray( op: 'add' | 'sub' | 'mul' | 'rdiv' | 'ldiv' | 'pow' | 'lt' | 'lte' | 'eq' | 'gte' | 'gt' | 'ne' | 'and' | 'or', left: ComplexDecimal, right: MultiArray, ): MultiArray { const result = new MultiArray(right.dim); for (let i = 0; i < result.dim[0]; i++) { result.array[i] = new Array(result.dim[1]); for (let j = 0; j < result.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](left, right.array[i][j]); } } return result; } static MultiArrayOpScalar( op: 'add' | 'sub' | 'mul' | 'rdiv' | 'ldiv' | 'pow' | 'lt' | 'lte' | 'eq' | 'gte' | 'gt' | 'ne' | 'and' | 'or', left: MultiArray, right: ComplexDecimal, ): MultiArray { const result = new MultiArray(left.dim); for (let i = 0; i < result.dim[0]; i++) { result.array[i] = new Array(result.dim[1]); for (let j = 0; j < result.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](left.array[i][j], right); } } return result; } static leftOp(op: 'clone' | 'neg' | 'not', right: MultiArray): MultiArray { const result = new MultiArray(right.dim); for (let i = 0; i < result.dim[0]; i++) { result.array[i] = new Array(result.dim[1]); for (let j = 0; j < result.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](right.array[i][j]); } } return result; } static ewiseOp( op: 'add' | 'sub' | 'mul' | 'rdiv' | 'ldiv' | 'pow' | 'lt' | 'lte' | 'eq' | 'gte' | 'gt' | 'ne' | 'and' | 'or', left: MultiArray, right: MultiArray, ): MultiArray { if (MultiArray.arrayEqual(left.dim.slice(0, 2), right.dim.slice(0, 2))) { // left and right has same number of rows and columns const result = new MultiArray(left.dim); for (let i = 0; i < result.dim[0]; i++) { result.array[i] = new Array(result.dim[1]); for (let j = 0; j < result.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](left.array[i][j], right.array[i][j]); } } return result; } else if (left.dim[0] == right.dim[0]) { // left and right has same number of rows let col, matrix; if (left.dim[1] == 1) { // left has one column col = left; matrix = right; } else if (right.dim[1] == 1) { // right has one column col = right; matrix = left; } else { throw new Error( 'operator ' + op + ': nonconformant arguments (op1 is ' + left.dim[0] + 'x' + left.dim[1] + ', op2 is ' + right.dim[0] + 'x' + right.dim[1] + ')', ); } const result = new MultiArray([col.dim[0], matrix.dim[1]]); for (let i = 0; i < col.dim[0]; i++) { result.array[i] = new Array(matrix.dim[1]); for (let j = 0; j < matrix.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](col.array[i][0], matrix.array[i][j]); } } return result; } else if (left.dim[1] == right.dim[1]) { // left and right has same number of columns let row, matrix; if (left.dim[0] == 1) { // left has one row row = left; matrix = right; } else if (right.dim[0] == 1) { // right has one row row = right; matrix = left; } else { throw new Error( 'operator ' + op + ': nonconformant arguments (op1 is ' + left.dim[0] + 'x' + left.dim[1] + ', op2 is ' + right.dim[0] + 'x' + right.dim[1] + ')', ); } const result = new MultiArray([matrix.dim[0], row.dim[1]]); for (let i = 0; i < matrix.dim[0]; i++) { result.array[i] = new Array(row.dim[1]); for (let j = 0; j < row.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](row.array[0][j], matrix.array[i][j]); } } return result; } else if (left.dim[0] == 1 && right.dim[1] == 1) { // left has one row and right has one column const result = new MultiArray([right.dim[0], left.dim[1]]); for (let i = 0; i < right.dim[0]; i++) { result.array[i] = new Array(left.dim[1]); for (let j = 0; j < left.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](left.array[0][j], right.array[i][0]); } } return result; } else if (left.dim[1] == 1 && right.dim[0] == 1) { // left has one column and right has one row const result = new MultiArray([left.dim[0], right.dim[1]]); for (let i = 0; i < left.dim[0]; i++) { result.array[i] = new Array(right.dim[1]); for (let j = 0; j < right.dim[1]; j++) { result.array[i][j] = ComplexDecimal[op](left.array[i][0], right.array[0][j]); } } return result; } else { throw new Error( 'operator ' + op + ': nonconformant arguments (op1 is ' + left.dim[0] + 'x' + left.dim[1] + ', op2 is ' + right.dim[0] + 'x' + right.dim[1] + ')', ); } } static inv(M: MultiArray): MultiArray { // Returns the inverse of matrix `M`. // from http://blog.acipo.com/matrix-inversion-in-javascript/ // I use Guassian Elimination to calculate the inverse: // (1) 'augment' the matrix (left) by the identity (on the right) // (2) Turn the matrix on the left into the identity by elemetry row ops // (3) The matrix on the right is the inverse (was the identity matrix) // There are 3 elemtary row ops: (I combine b and c in my code) // (a) Swap 2 rows // (b) Multiply a row by a scalar // (c) Add 2 rows //if the matrix isn't square: exit (error) if (M.dim[0] == M.dim[1]) { //create the identity matrix (I), and a clone (C) of the original let i = 0, ii = 0, j = 0, e = ComplexDecimal.zero(); const dim = M.dim[0]; const I: Array<Array<ComplexDecimal>> = [], C: Array<Array<any>> = []; for (i = 0; i < dim; i += 1) { // Create the row I[I.length] = []; C[C.length] = []; for (j = 0; j < dim; j += 1) { //if we're on the diagonal, put a 1 (for identity) if (i == j) { I[i][j] = ComplexDecimal.one(); } else { I[i][j] = ComplexDecimal.zero(); } // Also, make the clone of the original C[i][j] = M.array[i][j]; } } // Perform elementary row operations for (i = 0; i < dim; i += 1) { // get the element e on the diagonal e = C[i][i]; // if we have a 0 on the diagonal (we'll need to swap with a lower row) if (e.re.eq(0) && e.im.eq(0)) { //look through every row below the i'th row for (ii = i + 1; ii < dim; ii += 1) { //if the ii'th row has a non-0 in the i'th col if (!C[ii][i].re.eq(0) && !C[ii][i].im.eq(0)) { //it would make the diagonal have a non-0 so swap it for (j = 0; j < dim; j++) { e = C[i][j]; //temp store i'th row C[i][j] = C[ii][j]; //replace i'th row by ii'th C[ii][j] = e; //repace ii'th by temp e = I[i][j]; //temp store i'th row I[i][j] = I[ii][j]; //replace i'th row by ii'th I[ii][j] = e; //repace ii'th by temp } //don't bother checking other rows since we've swapped break; } } //get the new diagonal e = C[i][i]; //if it's still 0, not invertable (error) if (e.re.eq(0) && e.im.eq(0)) { return new MultiArray([M.dim[0], M.dim[1]], ComplexDecimal.inf_0()); } } // Scale this row down by e (so we have a 1 on the diagonal) for (j = 0; j < dim; j++) { C[i][j] = ComplexDecimal.rdiv(C[i][j], e); //apply to original matrix I[i][j] = ComplexDecimal.rdiv(I[i][j], e); //apply to identity } // Subtract this row (scaled appropriately for each row) from ALL of // the other rows so that there will be 0's in this column in the // rows above and below this one for (ii = 0; ii < dim; ii++) { // Only apply to other rows (we want a 1 on the diagonal) if (ii == i) { continue; } // We want to change this element to 0 e = C[ii][i]; // Subtract (the row above(or below) scaled by e) from (the // current row) but start at the i'th column and assume all the // stuff left of diagonal is 0 (which it should be if we made this // algorithm correctly) for (j = 0; j < dim; j++) { C[ii][j] = ComplexDecimal.sub(C[ii][j], ComplexDecimal.mul(e, C[i][j])); //apply to original matrix I[ii][j] = ComplexDecimal.sub(I[ii][j], ComplexDecimal.mul(e, I[i][j])); //apply to identity } } } //we've done all operations, C should be the identity //matrix I should be the inverse: const result = new MultiArray(M.dim); result.array = I; return result; } else { throw new Error('inverse: A must be a square matrix'); } } static zeros(...args: any): MultiArray | ComplexDecimal { if (!args.length) { return ComplexDecimal.zero(); } else if (args.length == 1) { if ('re' in args[0]) { return new MultiArray([args[0].re.toNumber(), args[0].re.toNumber()], ComplexDecimal.zero()); } else { return new MultiArray(MultiArray.oneRowToDim(args[0]), ComplexDecimal.zero()); } } else { return new MultiArray(MultiArray.oneRowToDim(args), ComplexDecimal.zero()); } } static ones(...args: any): MultiArray | ComplexDecimal { if (!args.length) { return ComplexDecimal.one(); } else if (args.length == 1) { if ('re' in args[0]) { return new MultiArray([args[0].re.toNumber(), args[0].re.toNumber()], ComplexDecimal.one()); } else { return new MultiArray(MultiArray.oneRowToDim(args[0]), ComplexDecimal.one()); } } else { return new MultiArray(MultiArray.oneRowToDim(args), ComplexDecimal.one()); } } static eye(i: ComplexDecimal, j?: ComplexDecimal): MultiArray { if (!i.re.isInteger()) throw new Error('Invalid call to eye. Non integer number argument.'); const temp = new MultiArray([i.re.toNumber(), j ? j.re.toNumber() : i.re.toNumber()], ComplexDecimal.zero()); for (let n = 0; n < Math.min(i.re.toNumber(), j ? j.re.toNumber() : i.re.toNumber()); n++) { temp.array[n][n] = ComplexDecimal.one(); } return temp; } static pow(left: MultiArray, right: ComplexDecimal): MultiArray { let temp1; // matrix power (multiple multiplication) if (right.re.isInteger() && right.im.eq(0)) { if (right.re.eq(0)) { temp1 = MultiArray.eye(new ComplexDecimal(left.dim[0], 0)); } else if (right.re.gt(0)) { temp1 = MultiArray.clone(left); } else { temp1 = MultiArray.inv(left); } if (Math.abs(right.re.toNumber()) != 1) { let temp2 = MultiArray.clone(temp1); for (let i = 1; i < Math.abs(right.re.toNumber()); i++) { temp2 = MultiArray.mul(temp2, temp1); } temp1 = temp2; } return temp1; } else { throw new Error("exponent must be integer real in matrix '^'"); } } static transpose(left: MultiArray): MultiArray { const result = new MultiArray(left.dim.slice(0, 2).reverse()); for (let i = 0; i < left.dim[1]; i++) { result.array[i] = new Array(left.dim[0]); for (let j = 0; j < left.dim[0]; j++) { result.array[i][j] = Object.assign({}, left.array[j][i]); } } return result; } static ctranspose(left: MultiArray): MultiArray { const result = new MultiArray(left.dim.slice(0, 2).reverse()); for (let i = 0; i < left.dim[1]; i++) { result.array[i] = new Array(left.dim[0]); for (let j = 0; j < left.dim[0]; j++) { result.array[i][j] = ComplexDecimal.conj(left.array[j][i]); } } return result; } static horzcat(L: MultiArray, R: MultiArray): MultiArray { if (L.dim[0] == R.dim[0]) { const temp = new MultiArray([L.dim[0], L.dim[1] + R.dim[1]]); for (let i = 0; i < L.dim[0]; i++) { temp.array[i] = []; for (let j = 0; j < L.dim[1]; j++) { temp.array[i][j] = Object.assign({}, L.array[i][j]); } for (let j = 0; j < R.dim[1]; j++) { temp.array[i][j + L.dim[1]] = Object.assign({}, R.array[i][j]); } } return temp; } else { throw new Error('invalid dimensions in horzcat function'); } } static vertcat(U: MultiArray, D: MultiArray): MultiArray { if (U.dim[1] == D.dim[1]) { const temp = new MultiArray([U.dim[0] + D.dim[0], U.dim[1]]); for (let i = 0; i < U.dim[0]; i++) { temp.array[i] = []; for (let j = 0; j < U.dim[1]; j++) { temp.array[i][j] = Object.assign({}, U.array[i][j]); } } for (let i = 0; i < D.dim[0]; i++) { temp.array[i + U.dim[0]] = []; for (let j = 0; j < D.dim[1]; j++) { temp.array[i + U.dim[0]][j] = Object.assign({}, D.array[i][j]); } } return temp; } else { throw new Error('invalid dimensions in vertcat function'); } } static det(M: MultiArray): ComplexDecimal { if (M.dim[0] == M.dim[1]) { let det = ComplexDecimal.zero(); if (M.dim[0] == 1) det = M.array[0][0]; else if (M.dim[0] == 2) det = ComplexDecimal.sub(ComplexDecimal.mul(M.array[0][0], M.array[1][1]), ComplexDecimal.mul(M.array[0][1], M.array[1][0])); else { det = ComplexDecimal.zero(); for (let j1 = 0; j1 < M.dim[0]; j1++) { const m = new MultiArray([M.dim[0] - 1, M.dim[0] - 1], ComplexDecimal.zero()); for (let i = 1; i < M.dim[0]; i++) { let j2 = 0; for (let j = 0; j < M.dim[0]; j++) { if (j == j1) continue; m.array[i - 1][j2] = M.array[i][j]; j2++; } } det = ComplexDecimal.add( det, ComplexDecimal.mul(new ComplexDecimal(Math.pow(-1, 2.0 + j1), 0), ComplexDecimal.mul(M.array[0][j1], MultiArray.det(m))), ); } } return det; } else { throw new Error('det: A must be a square matrix'); } } static trace(M: MultiArray): ComplexDecimal { if (M.dim[0] == M.dim[1]) { let temp: ComplexDecimal = ComplexDecimal.zero(); for (let i = 0; i < M.dim[0]; i++) { temp = ComplexDecimal.add(temp, M.array[i][i]); } return temp; } else { throw new Error('trace: invalid dimensions'); } } static rows(M: MultiArray): ComplexDecimal { return new ComplexDecimal(M.dim[0], 0); } static cols(M: MultiArray): ComplexDecimal { return new ComplexDecimal(M.dim[1], 0); } static minor(M: MultiArray, p: ComplexDecimal, q: ComplexDecimal): MultiArray { // minor of matrix (remove line and column) const temp = MultiArray.clone(M); temp.array.splice(p.re.toNumber() - 1, 1); for (let i = 0; i < M.dim[0] - 1; i++) { temp.array[i].splice(q.re.toNumber() - 1, 1); } temp.dim[0]--; temp.dim[1]--; return temp; } static cofactor(M: MultiArray): MultiArray { const temp = new MultiArray([M.dim[0], M.dim[1]], ComplexDecimal.zero()); for (let i = 0; i < M.dim[0]; i++) { for (let j = 0; j < M.dim[1]; j++) { const minor = MultiArray.minor(M, new ComplexDecimal(i + 1, 0), new ComplexDecimal(j + 1, 0)); let sign: ComplexDecimal; if ((i + j) % 2 == 0) { sign = ComplexDecimal.one(); } else { sign = ComplexDecimal.minusone(); } temp.array[i][j] = ComplexDecimal.mul(sign, MultiArray.det(minor)); } } return temp; } static min(M: MultiArray): MultiArray | ComplexDecimal { let temp: MultiArray; if (M.dim[0] === 1) { temp = MultiArray.transpose(M); } else { temp = M; } const result = new MultiArray([1, temp.dim[1]]); // result.array = new Array(temp.dim[0]); // result.array[0] = new Array(); result.array = [new Array(temp.dim[1])]; for (let j = 0; j < temp.dim[1]; j++) { result.array[0][j] = temp.array[0][j]; for (let i = 1; i < temp.dim[0]; i++) { result.array[0][j] = ComplexDecimal.min(result.array[0][j], temp.array[i][j]); } } if (temp.dim[1] === 1) { return result.array[0][0]; } else { return result; } } static max(M: MultiArray): MultiArray | ComplexDecimal { let temp: MultiArray; if (M.dim[0] === 1) { temp = MultiArray.transpose(M); } else { temp = M; } const result = new MultiArray([1, temp.dim[1]]); // result.array = new Array(temp.dim[0]); result.array = [new Array(temp.dim[1])]; for (let j = 0; j < temp.dim[1]; j++) { result.array[0][j] = temp.array[0][j]; for (let i = 1; i < temp.dim[0]; i++) { result.array[0][j] = ComplexDecimal.max(result.array[0][j], temp.array[i][j]); } } if (temp.dim[1] === 1) { return result.array[0][0]; } else { return result; } } static mean(M: MultiArray): MultiArray | ComplexDecimal { let temp: MultiArray; if (M.dim[0] === 1) { temp = MultiArray.transpose(M); } else { temp = M; } const result = new MultiArray([1, temp.dim[1]]); // result.array = new Array(temp.dim[0]); result.array = [new Array(temp.dim[1])]; for (let j = 0; j < temp.dim[1]; j++) { result.array[0][j] = temp.array[0][j]; for (let i = 1; i < temp.dim[0]; i++) { result.array[0][j] = ComplexDecimal.add(result.array[0][j], temp.array[i][j]); } result.array[0][j] = ComplexDecimal.rdiv(result.array[0][j], new ComplexDecimal(temp.dim[0], 0)); } if (temp.dim[1] === 1) { return result.array[0][0]; } else { return result; } } static gauss(M: MultiArray, x: MultiArray): MultiArray | undefined { // Gaussian elimination algorithm for solving systems of linear equations. // Adapted from: https://github.com/itsravenous/gaussian-elimination if (M.dim[0] != M.dim[1]) throw new Error('invalid dimensions in function gauss'); const A: MultiArray = MultiArray.clone(M); let i: number, k: number, j: number; const DMin = Math.min(x.dim[0], x.dim[1]); if (DMin == x.dim[1]) { x = MultiArray.transpose(x); } // Just make a single matrix for (i = 0; i < A.dim[0]; i++) { A.array[i].push(x.array[0][i]); } const n = A.dim[0]; for (i = 0; i < n; i++) { // Search for maximum in this column let maxEl = ComplexDecimal.abs(A.array[i][i]), maxRow = i; for (k = i + 1; k < n; k++) { if (ComplexDecimal.abs(A.array[k][i]).re.gt(maxEl.re)) { maxEl = ComplexDecimal.abs(A.array[k][i]); maxRow = k; } } // Swap maximum row with current row (column by column) for (k = i; k < n + 1; k++) { const tmp = A.array[maxRow][k]; A.array[maxRow][k] = A.array[i][k]; A.array[i][k] = tmp; } // Make all rows below this one 0 in current column for (k = i + 1; k < n; k++) { const c = ComplexDecimal.rdiv(ComplexDecimal.neg(A.array[k][i]), A.array[i][i]); for (j = i; j < n + 1; j++) { if (i === j) { A.array[k][j] = ComplexDecimal.zero(); } else { A.array[k][j] = ComplexDecimal.add(A.array[k][j], ComplexDecimal.mul(c, A.array[i][j])); } } } } // Solve equation Ax=b for an upper triangular matrix A const X = new MultiArray([1, n], ComplexDecimal.zero()); for (i = n - 1; i > -1; i--) { X.array[0][i] = ComplexDecimal.rdiv(A.array[i][n], A.array[i][i]); for (k = i - 1; k > -1; k--) { A.array[k][n] = ComplexDecimal.sub(A.array[k][n], ComplexDecimal.mul(A.array[k][i], X.array[0][i])); } } return X; } static lu(M: MultiArray): any { const n = M.dim[0]; const lower = new MultiArray([M.dim[0], M.dim[1]], ComplexDecimal.zero()); const upper = new MultiArray([M.dim[0], M.dim[1]], ComplexDecimal.zero()); // Decomposing matrix into Upper and // Lower triangular matrix for (let i = 0; i < n; i++) { // Upper Triangular for (let k = i; k < n; k++) { // Summation of L(i, j) * U(j, k) let sum: ComplexDecimal = ComplexDecimal.zero(); for (let j = 0; j < i; j++) { sum = ComplexDecimal.add(sum, ComplexDecimal.mul(lower.array[i][j], upper.array[j][k])); } // Evaluating U(i, k) upper.array[i][k] = ComplexDecimal.sub(M.array[i][k], sum); } // Lower Triangular for (let k = i; k < n; k++) { if (i == k) { // Diagonal as 1 lower.array[i][i] = ComplexDecimal.one(); } else { // Summation of L(k, j) * U(j, i) let sum: ComplexDecimal = ComplexDecimal.zero(); for (let j = 0; j < i; j++) { sum = ComplexDecimal.add(sum, ComplexDecimal.mul(lower.array[k][j], upper.array[j][i])); } // Evaluating L(k, i) lower.array[k][i] = ComplexDecimal.rdiv(ComplexDecimal.sub(M.array[k][i], sum), upper.array[i][i]); } } } return EvaluatorPointer.nodeOp('*', lower, upper); } static pivot(M: MultiArray): MultiArray { const n = M.dim[0]; const id = MultiArray.eye(new ComplexDecimal(n, 0)); for (let i = 0; i < n; i++) { let maxm = ComplexDecimal.abs(M.array[i][i]); let row = i; for (let j = i; j < n; j++) if (ComplexDecimal.abs(M.array[j][i]).re.gt(maxm.re)) { maxm = ComplexDecimal.abs(M.array[j][i]); row = j; } if (i != row) { const tmp = id.array[i]; id.array[i] = id.array[row]; id.array[row] = tmp; } } return id; } static adj(M: MultiArray): MultiArray { return MultiArray.ctranspose(MultiArray.cofactor(M)); } static size(X: any, DIM: any) { if (arguments.length == 1) { if ('re' in X) { return { array: [[ComplexDecimal.one(), ComplexDecimal.one()]] }; } else if ('array' in X) { return { array: [[new ComplexDecimal(X.dim[0], 0), new ComplexDecimal(X.dim[1], 0)]] }; } } else if (arguments.length == 2) { if ('re' in DIM && DIM.im.eq(0)) { if ('re' in X) { return ComplexDecimal.one(); } else if ('array' in X) { if (Math.trunc(DIM.re.toNumber()) == 1) { return new ComplexDecimal(X.dim[0], 0); } else if (Math.trunc(DIM.re.toNumber()) == 2) { return new ComplexDecimal(X.dim[1], 0); } else if (Math.trunc(DIM.re.toNumber()) > 2) { return new ComplexDecimal(1, 0); } else { throw new Error('size: requested dimension DIM (= ' + Math.trunc(DIM.re.toNumber()) + ') out of range'); } } } else { throw new Error('size: DIM must be a positive integer'); } } else { throw new Error('Invalid call to size.'); } } static sub2ind(DIMS: any, ...S: any) { if (arguments.length > 1) { const n = DIMS; return new ComplexDecimal(1, 0); } else { throw new Error('Invalid call to sub2ind.'); } } static ind2sub(DIMS: any, IND: any) { if (arguments.length == 2) { return new ComplexDecimal(1, 0); } else { throw new Error('Invalid call to ind2sub.'); } } }