UNPKG

mathjslab

Version:

MathJSLab - An interpreter with language syntax like MATLAB®/Octave. ISBN 978-65-00-82338-7

336 lines (317 loc) 14.9 kB
import { ComplexDecimal, TBinaryOperationName } from './complex-decimal'; import { MultiArray } from './multi-array'; import { NodeReturnList } from './evaluator'; export abstract class CoreFunctions { public static functions: { [name: string]: Function } = { ind2sub: CoreFunctions.ind2sub, sub2ind: CoreFunctions.sub2ind, size: CoreFunctions.size, zeros: CoreFunctions.zeros, ones: CoreFunctions.ones, rand: CoreFunctions.rand, randi: CoreFunctions.randi, cat: CoreFunctions.cat, horzcat: CoreFunctions.horzcat, vertcat: CoreFunctions.vertcat, sum: CoreFunctions.sum, sumsq: CoreFunctions.sumsq, prod: CoreFunctions.prod, mean: CoreFunctions.mean, min: CoreFunctions.min, max: CoreFunctions.max, }; /** * Convert linear indices to subscripts. * @param DIMS * @param IND * @returns */ public static ind2sub(DIMS: any, IND: any): any { if (arguments.length === 2) { return global.EvaluatorPointer.nodeReturnList((length: number, index: number): any => { if (length === 1) { return IND; } else { let dims = MultiArray.linearize(DIMS).map((value) => value.re.toNumber()); let lenghtGreater = false; if (length > dims.length) { MultiArray.appendSingletonTail(dims, length); lenghtGreater = true; } else { dims = dims.slice(0, length - 1); } const ind = MultiArray.scalarToMultiArray(IND); const result = new MultiArray(ind.dimension); const subscript = ind.array.map((row) => row.map((value) => MultiArray.ind2subNumber(dims, value.re.toNumber()))); if (index === length - 1 && lenghtGreater) { result.array = subscript.map((row) => row.map((value) => new ComplexDecimal(value[length]))); } else { result.array = subscript.map((row) => row.map((value) => new ComplexDecimal(value[index]))); } result.type = ComplexDecimal.numberClass.real; return MultiArray.MultiArrayToScalar(result); } }); } else { throw new Error(`Invalid call to ind2sub. Type 'help ind2sub' to see correct usage.`); } } /** * Convert subscripts to linear indices. * @param DIMS * @param S * @returns */ public static sub2ind(DIMS: any, ...S: any) { if (arguments.length > 1) { const dims = MultiArray.linearize(DIMS).map((value) => value.re.toNumber()); const subscript: MultiArray[] = S.map((s: any) => MultiArray.scalarToMultiArray(s)); for (let s = 1; s < subscript.length; s++) { if (!MultiArray.arrayEquals(subscript[0].dimension, subscript[s].dimension)) { throw new Error('sub2ind: all subscripts must be of the same size.'); } } MultiArray.appendSingletonTail(dims, subscript.length); const result = new MultiArray(subscript[0].dimension); for (let n = 0; n < MultiArray.linearLength(subscript[0]); n++) { const subscriptN = subscript.map((s) => { const [i, j] = MultiArray.linearIndexToMultiArrayRowColumn(s.dimension[0], s.dimension[1], n); return s.array[i][j]; }); const index = MultiArray.parseSubscript(dims, subscriptN, 'index '); const [p, q] = MultiArray.linearIndexToMultiArrayRowColumn(result.dimension[0], result.dimension[1], n); result.array[p][q] = new ComplexDecimal(index + 1); } return MultiArray.MultiArrayToScalar(result); } else { throw new Error(`Invalid call to sub2ind. Type 'help su2ind' to see correct usage.`); } } /** * Returns array dimensions. * @param M MultiArray * @param DIM Dimensions * @returns Dimensions of `M` parameter. */ public static size(M: MultiArray | ComplexDecimal, ...DIM: any): MultiArray | ComplexDecimal { const parseDimension = (dimension: ComplexDecimal): number => { const dim = dimension.re.toNumber(); if (dim < 1 || !dimension.re.trunc().eq(dimension.re)) { throw new Error(`size: requested dimension DIM (= ${dim}) out of range. DIM must be a positive integer.`); } return dim; }; const sizeDim = 'array' in M ? M.dimension.slice() : [1, 1]; if (DIM.length === 0) { const result = new MultiArray([1, sizeDim.length]); result.array[0] = sizeDim.map((d) => new ComplexDecimal(d)); result.type = ComplexDecimal.numberClass.real; return result; } else { const dims = DIM.length === 1 && 'array' in DIM[0] ? MultiArray.linearize(DIM[0]).map((dim) => parseDimension(dim)) : DIM.map((dim: any) => parseDimension(MultiArray.firstElement(dim))); MultiArray.appendSingletonTail(sizeDim, Math.max(...dims)); const result = new MultiArray([1, dims.length]); result.array[0] = dims.map((dim: number) => new ComplexDecimal(sizeDim[dim - 1])); result.type = ComplexDecimal.numberClass.real; return MultiArray.MultiArrayToScalar(result); } } /** * Create array of all zeros. * @param dimension * @returns */ public static zeros(...dimension: any): MultiArray | ComplexDecimal { return MultiArray.newFilled(ComplexDecimal.zero(), ...dimension); } /** * Create array of all ones. * @param dimension * @returns */ public static ones(...dimension: any): MultiArray | ComplexDecimal { return MultiArray.newFilled(ComplexDecimal.one(), ...dimension); } /** * Uniformly distributed pseudorandom numbers distributed on the * interval (0, 1). * @param dimension * @returns */ public static rand(...dimension: any): MultiArray | ComplexDecimal { return MultiArray.newFilledEach((n: number) => new ComplexDecimal(Math.random()), ...dimension); } /** * Uniformly distributed pseudorandom integers. * @param imax * @param args * @returns */ public static randi(range: MultiArray | ComplexDecimal, ...dimension: any): MultiArray | ComplexDecimal { let imin = 0; let imax = 0; if ('array' in range) { const rangeLinearized = MultiArray.linearize(range); if (rangeLinearized.length > 1) { imin = rangeLinearized[0].re.toNumber(); imax = rangeLinearized[1].re.toNumber(); } else if (rangeLinearized.length > 0) { imax = rangeLinearized[0].re.toNumber(); } else { throw new Error('bounds(1): out of bound 0 (dimensions are 0x0)'); } } else { imax = range.re.toNumber(); } if (Math.trunc(imin) !== imin || Math.trunc(imax) !== imax) { throw new Error(`randi: must be integer bounds.`); } if (imax > imin) { return MultiArray.newFilledEach( imin === 0 ? (n: number) => new ComplexDecimal(Math.round(imax * Math.random())) : (n: number) => new ComplexDecimal(Math.round((imax - imin) * Math.random() + imin)), ...dimension, ); } else { if ((imin = 0)) { throw new Error(`randi: require imax >= 1.`); } else { throw new Error(`randi: require imax > imin.`); } } } /** * Return the concatenation of N-D array objects, ARRAY1, ARRAY2, ..., * ARRAYN along dimension `DIM`. * @param DIM Dimension of concatenation. * @param ARRAY Arrays to concatenate. * @returns Concatenated arrays along dimension `DIM`. */ public static cat(DIM: MultiArray | any, ...ARRAY: (MultiArray | any)[]): MultiArray { const dimension = MultiArray.firstElement(DIM); const array = ARRAY.map((m) => MultiArray.scalarToMultiArray(m)); return MultiArray.concatenate(dimension, 'cat', ...array); } /** * Concatenate arrays horizontally. * @param ARRAY Arrays to concatenate horizontally. * @returns Concatenated arrays horizontally. */ public static horzcat(...ARRAY: (MultiArray | any)[]): MultiArray { const array = ARRAY.map((m) => MultiArray.scalarToMultiArray(m)); return MultiArray.concatenate(1, 'horzcat', ...array); } /** * Concatenate arrays vertically. * @param ARRAY Arrays to concatenate vertically. * @returns Concatenated arrays vertically. */ public static vertcat(...ARRAY: (MultiArray | any)[]): MultiArray { const array = ARRAY.map((m) => MultiArray.scalarToMultiArray(m)); return MultiArray.concatenate(0, 'vertcat', ...array); } /** * Calculate sum of elements along dimension DIM. * @param M Array * @param DIM Dimension * @returns Array with sum of elements along dimension DIM. */ public static sum(M: MultiArray, DIM?: ComplexDecimal): MultiArray | ComplexDecimal { const dim = DIM ? MultiArray.firstElement(DIM).re.toNumber() - 1 : MultiArray.firstNonSingleDimension(M); return MultiArray.reduce(dim, M, (p, c) => ComplexDecimal.add(p, c)); } /** * Calculate sum of squares of elements along dimension DIM. * @param M Matrix * @param DIM Dimension * @returns One dimensional matrix with sum of squares of elements along dimension DIM. */ public static sumsq(M: MultiArray, DIM?: ComplexDecimal): MultiArray | ComplexDecimal { const dim = DIM ? MultiArray.firstElement(DIM).re.toNumber() - 1 : MultiArray.firstNonSingleDimension(M); return MultiArray.reduce(dim, M, (p, c) => ComplexDecimal.add(ComplexDecimal.mul(p, ComplexDecimal.conj(p)), ComplexDecimal.mul(c, ComplexDecimal.conj(c)))); } /** * Calculate product of elements along dimension DIM. * @param M Matrix * @param DIM Dimension * @returns One dimensional matrix with product of elements along dimension DIM. */ public static prod(M: MultiArray, DIM?: ComplexDecimal): MultiArray | ComplexDecimal { const dim = DIM ? MultiArray.firstElement(DIM).re.toNumber() - 1 : MultiArray.firstNonSingleDimension(M); return MultiArray.reduce(dim, M, (p, c) => ComplexDecimal.mul(p, c)); } /** * Calculate average or mean of elements along dimension DIM. * @param M Matrix * @param DIM Dimension * @returns One dimensional matrix with product of elements along dimension DIM. */ public static mean(M: MultiArray, DIM?: ComplexDecimal): MultiArray | ComplexDecimal { const dim = DIM ? MultiArray.firstElement(DIM).re.toNumber() - 1 : MultiArray.firstNonSingleDimension(M); const sum = MultiArray.reduce(dim, M, (p, c) => ComplexDecimal.add(p, c)); return MultiArray.MultiArrayOpScalar('rdiv', MultiArray.scalarToMultiArray(sum), new ComplexDecimal(M.dimension[dim])); } /** * Base method of min and max user functions. * @param op 'min' or 'max' * @param args One to three arguments like user function. * @returns Return like user function. */ private static minMax(op: 'min' | 'max', ...args: any[]): MultiArray | NodeReturnList { const minMaxAlogDimension = (M: MultiArray, dimension: number) => { const reduced = MultiArray.reduceToArray(dimension, M); const resultM = new MultiArray(reduced.dimension); const indexM = new MultiArray(reduced.dimension); const cmp = op === 'min' ? 'lt' : 'gt'; for (let i = 0; i < indexM.array.length; i++) { for (let j = 0; j < indexM.array[0].length; j++) { const [m, n] = ComplexDecimal[`minMaxArray${args[0].type < 2 ? 'Real' : 'Complex'}WithIndex`](cmp, ...(reduced.array[i][j] as unknown as ComplexDecimal[])); resultM.array[i][j] = m; indexM.array[i][j] = new ComplexDecimal(n + 1); } } return global.EvaluatorPointer.nodeReturnList((length: number, index: number): any => { global.EvaluatorPointer.throwErrorIfGreaterThanReturnList(2, length); return MultiArray.MultiArrayToScalar(index === 0 ? resultM : indexM); }); }; switch (args.length) { case 1: // Along first non singleton dimension. const dimension = MultiArray.firstNonSingleDimension(MultiArray.scalarToMultiArray(args[0])); return minMaxAlogDimension(MultiArray.scalarToMultiArray(args[0]), dimension); case 2: // Broadcast return MultiArray.elementWiseOperation((op + 'Wise') as TBinaryOperationName, MultiArray.scalarToMultiArray(args[0]), args[1]); case 3: // Along selected dimension. if (!('array' in args[1] && args[1].dimension.length === 2 && args[1].dimension[0] === 0 && args[1].dimension[1] === 0)) { // Error if second argument is different from []. throw new Error(`${op}: second argument is ignored`); } return minMaxAlogDimension(MultiArray.scalarToMultiArray(args[0]), MultiArray.firstElement(args[2]).re.toNumber() - 1); default: throw new Error(`Invalid call to ${op}. Type 'help ${op}' to see correct usage.`); } } /** * Minimum elements of array. * @param M * @returns */ public static min(...args: any[]): MultiArray | NodeReturnList { return CoreFunctions.minMax('min', ...args); } /** * Maximum elements of array. * @param M * @returns */ public static max(...args: any[]): MultiArray | NodeReturnList { return CoreFunctions.minMax('max', ...args); } }