From 7b10e011e63c18ff57dc939290fb623d259358fb Mon Sep 17 00:00:00 2001 From: David Contreras Date: Mon, 16 Sep 2024 10:50:47 -0600 Subject: [PATCH 01/11] Faster deepMap recurse --- src/function/matrix/forEach.js | 4 +-- src/function/matrix/map.js | 4 +-- src/type/matrix/DenseMatrix.js | 6 ++-- src/utils/array.js | 50 ++++++++++++++++++++++++++++------ src/utils/collection.js | 42 ++++++++++++++++++---------- src/utils/optimizeCallback.js | 10 +++---- 6 files changed, 81 insertions(+), 35 deletions(-) diff --git a/src/function/matrix/forEach.js b/src/function/matrix/forEach.js index 3dfa266278..5ceb158815 100644 --- a/src/function/matrix/forEach.js +++ b/src/function/matrix/forEach.js @@ -1,6 +1,6 @@ import { optimizeCallback } from '../../utils/optimizeCallback.js' import { factory } from '../../utils/factory.js' -import { recurse } from '../../utils/array.js' +import { deepForEach } from '../../utils/array.js' const name = 'forEach' const dependencies = ['typed'] @@ -45,5 +45,5 @@ export const createForEach = /* #__PURE__ */ factory(name, dependencies, ({ type * @private */ function _forEach (array, callback) { - recurse(array, [], array, optimizeCallback(callback, array, name)) + deepForEach(array, array, optimizeCallback(callback, array, name)) } diff --git a/src/function/matrix/map.js b/src/function/matrix/map.js index d8ff940431..fb4c94ab82 100644 --- a/src/function/matrix/map.js +++ b/src/function/matrix/map.js @@ -1,5 +1,5 @@ import { optimizeCallback } from '../../utils/optimizeCallback.js' -import { arraySize, broadcastSizes, broadcastTo, get, recurse } from '../../utils/array.js' +import { arraySize, broadcastSizes, broadcastTo, get, deepMap } from '../../utils/array.js' import { factory } from '../../utils/factory.js' const name = 'map' @@ -151,6 +151,6 @@ export const createMap = /* #__PURE__ */ factory(name, dependencies, ({ typed }) * @private */ function _mapArray (array, callback) { - return recurse(array, [], array, optimizeCallback(callback, array, name)) + return deepMap(array, array, optimizeCallback(callback, array, name)) } }) diff --git a/src/type/matrix/DenseMatrix.js b/src/type/matrix/DenseMatrix.js index a05a593042..35663be902 100644 --- a/src/type/matrix/DenseMatrix.js +++ b/src/type/matrix/DenseMatrix.js @@ -1,5 +1,5 @@ import { isArray, isBigNumber, isCollection, isIndex, isMatrix, isNumber, isString, typeOf } from '../../utils/is.js' -import { arraySize, getArrayDataType, processSizesWildcard, reshape, resize, unsqueeze, validate, validateIndex, broadcastTo, get, recurse } from '../../utils/array.js' +import { arraySize, getArrayDataType, processSizesWildcard, reshape, resize, unsqueeze, validate, validateIndex, broadcastTo, get, deepMap, deepForEach } from '../../utils/array.js' import { format } from '../../utils/string.js' import { isInteger } from '../../utils/number.js' import { clone, deepStrictEqual } from '../../utils/object.js' @@ -541,7 +541,7 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies // determine the new datatype when the original matrix has datatype defined // TODO: should be done in matrix constructor instead - const data = recurse(me._data, [], me, fastCallback) + const data = deepMap(me._data, me, fastCallback) const datatype = me._datatype !== undefined ? getArrayDataType(data, typeOf) : undefined @@ -559,7 +559,7 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies // matrix instance const me = this const fastCallback = optimizeCallback(callback, me._data, 'forEach') - recurse(this._data, [], me, fastCallback) + deepForEach(this._data, me, fastCallback) } /** diff --git a/src/utils/array.js b/src/utils/array.js index 4017983361..3894e336f0 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -834,15 +834,47 @@ export function get (array, index) { * @param {Function} callback - Function that produces the element of the new Array, taking three arguments: the value of the element, the index of the element, and the Array being processed. * @returns {*} The new array with each element being the result of the callback function. */ -export function recurse (value, index, array, callback) { - if (Array.isArray(value)) { - return value.map(function (child, i) { - // we create a copy of the index array and append the new index value - return recurse(child, index.concat(i), array, callback) - }) - } else { - // invoke the callback function with the right number of arguments - return callback(value, index, array) +export function deepMap (value, array, callback) { + return recurse(value, [], array, callback) + function recurse (value, index, array, callback) { + if (Array.isArray(value)) { + return value.map(function (child, i) { + // we create a copy of the index array and append the new index value + index.push(i) + const results = recurse(child, index, array, callback) + index.pop() + return results + }) + } else { + // invoke the callback function with the right number of arguments + return callback(value, [...index], array) + } + } +} + +/** + * Recursive function to map a multi-dimensional array. + * + * @param {*} value - The current value being processed in the array. + * @param {Array} index - The index of the current value being processed in the array. + * @param {Array} array - The array being processed. + * @param {Function} callback - Function that produces the element of the new Array, taking three arguments: the value of the element, the index of the element, and the Array being processed. + * @returns {*} The new array with each element being the result of the callback function. + */ +export function deepForEach (value, array, callback) { + recurse(value, [], array, callback) + function recurse (value, index, array, callback) { + if (Array.isArray(value)) { + return value.forEach(function (child, i) { + // we create a copy of the index array and append the new index value + index.push(i) + recurse(child, index, array, callback) + index.pop() + }) + } else { + // invoke the callback function with the right number of arguments + callback(value, [...index], array) + } } } diff --git a/src/utils/collection.js b/src/utils/collection.js index 021d980cba..8c740fc98c 100644 --- a/src/utils/collection.js +++ b/src/utils/collection.js @@ -27,16 +27,15 @@ export function containsCollections (array) { */ export function deepForEach (array, callback) { if (isMatrix(array)) { - array = array.valueOf() + recurse(array.valueOf()) + } else { + recurse(array) } - - for (let i = 0, ii = array.length; i < ii; i++) { - const value = array[i] - - if (Array.isArray(value)) { - deepForEach(value, callback) + function recurse (array) { + if (Array.isArray(array)) { + array.forEach(value => recurse(value)) } else { - callback(value) + callback(array) } } } @@ -54,13 +53,28 @@ export function deepForEach (array, callback) { * @return {Array | Matrix} res */ export function deepMap (array, callback, skipZeros) { - if (array && (typeof array.map === 'function')) { - // TODO: replace array.map with a for loop to improve performance - return array.map(function (x) { - return deepMap(x, callback, skipZeros) - }) + if (skipZeros) { + const callbackSkip = (x) => x === 0 ? 0 : callback(x) + if (isMatrix(array)) { + return array.create(recurse(array.valueOf(), callbackSkip), array.datatype()) + } else { + return recurse(array, callbackSkip) + } } else { - return callback(array) + if (isMatrix(array)) { + return array.create(recurse(array.valueOf(), callback), array.datatype()) + } else { + return recurse(array, callback) + } + } + function recurse (array) { + if (Array.isArray(array)) { + return array.map(function (x) { + return recurse(x) + }) + } else { + return callback(array) + } } } diff --git a/src/utils/optimizeCallback.js b/src/utils/optimizeCallback.js index 4f0166ee30..68657c3ad6 100644 --- a/src/utils/optimizeCallback.js +++ b/src/utils/optimizeCallback.js @@ -15,17 +15,17 @@ export function optimizeCallback (callback, array, name) { const firstIndex = (array.isMatrix ? array.size() : arraySize(array)).map(() => 0) const firstValue = array.isMatrix ? array.get(firstIndex) : get(array, firstIndex) const hasSingleSignature = Object.keys(callback.signatures).length === 1 - const numberOfArguments = _findNumberOfArguments(callback, firstValue, firstIndex, array) + const numberOfArguments = findNumberOfArguments(callback, firstValue, firstIndex, array) const fastCallback = hasSingleSignature ? Object.values(callback.signatures)[0] : callback if (numberOfArguments >= 1 && numberOfArguments <= 3) { - return (...args) => _tryFunctionWithArgs(fastCallback, args.slice(0, numberOfArguments), name, callback.name) + return (...args) => tryFunctionWithArgs(fastCallback, args.slice(0, numberOfArguments), name, callback.name) } - return (...args) => _tryFunctionWithArgs(fastCallback, args, name, callback.name) + return (...args) => tryFunctionWithArgs(fastCallback, args, name, callback.name) } return callback } -function _findNumberOfArguments (callback, value, index, array) { +export function findNumberOfArguments (callback, value, index, array) { const testArgs = [value, index, array] for (let i = 3; i > 0; i--) { const args = testArgs.slice(0, i) @@ -43,7 +43,7 @@ function _findNumberOfArguments (callback, value, index, array) { * @returns {*} Returns the return value of the invoked signature * @throws {TypeError} Throws an error when no matching signature was found */ -function _tryFunctionWithArgs (func, args, mappingFnName, callbackName) { +export function tryFunctionWithArgs (func, args, mappingFnName, callbackName) { try { return func(...args) } catch (err) { From c7d98dff010227cabc7537b2571caf4f1e5dec7c Mon Sep 17 00:00:00 2001 From: David Contreras Date: Mon, 16 Sep 2024 11:18:31 -0600 Subject: [PATCH 02/11] Callback with right number of arguments. --- src/utils/array.js | 92 ++++++++++++++++++++++++++++++++--- src/utils/optimizeCallback.js | 14 +++++- 2 files changed, 97 insertions(+), 9 deletions(-) diff --git a/src/utils/array.js b/src/utils/array.js index 3894e336f0..487e41a085 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -4,6 +4,7 @@ import { format } from './string.js' import { DimensionError } from '../error/DimensionError.js' import { IndexError } from '../error/IndexError.js' import { deepStrictEqual } from './object.js' +import { findNumberOfArguments } from './optimizeCallback.js' /** * Calculate the size of a multi dimensional array. @@ -835,13 +836,52 @@ export function get (array, index) { * @returns {*} The new array with each element being the result of the callback function. */ export function deepMap (value, array, callback) { - return recurse(value, [], array, callback) - function recurse (value, index, array, callback) { + const numberOfArguments = findNumberOfArguments(callback, array) + switch (numberOfArguments) { + case 1: + return recurse1(value) + case 2: + return recurse2(value, []) + case 3: + return recurse3(value, []) + default: + return recurse3(value, []) + } + + function recurse1 (value) { + if (Array.isArray(value)) { + return value.map(function (child) { + // we create a copy of the index array and append the new index value + const results = recurse1(child) + return results + }) + } else { + // invoke the callback function with the right number of arguments + return callback(value) + } + } + + function recurse2 (value, index) { if (Array.isArray(value)) { return value.map(function (child, i) { // we create a copy of the index array and append the new index value index.push(i) - const results = recurse(child, index, array, callback) + const results = recurse2(child, index) + index.pop() + return results + }) + } else { + // invoke the callback function with the right number of arguments + return callback(value, [...index]) + } + } + + function recurse3 (value, index) { + if (Array.isArray(value)) { + return value.map(function (child, i) { + // we create a copy of the index array and append the new index value + index.push(i) + const results = recurse3(child, index) index.pop() return results }) @@ -862,13 +902,51 @@ export function deepMap (value, array, callback) { * @returns {*} The new array with each element being the result of the callback function. */ export function deepForEach (value, array, callback) { - recurse(value, [], array, callback) - function recurse (value, index, array, callback) { + const numberOfArguments = findNumberOfArguments(callback, array) + switch (numberOfArguments) { + case 1: + recurse1(value) + break + case 2: + recurse2(value, []) + break + case 3: + recurse3(value, []) + break + default: + recurse3(value, []) + break + } + + function recurse1 (value) { + if (Array.isArray(value)) { + return value.forEach(function (child) { + recurse1(child) + }) + } else { + // invoke the callback function with the right number of arguments + callback(value) + } + } + + function recurse2 (value, index) { + if (Array.isArray(value)) { + return value.forEach(function (child, i) { + index.push(i) + recurse2(child, index) + index.pop() + }) + } else { + // invoke the callback function with the right number of arguments + callback(value, [...index]) + } + } + + function recurse3 (value, index) { if (Array.isArray(value)) { return value.forEach(function (child, i) { - // we create a copy of the index array and append the new index value index.push(i) - recurse(child, index, array, callback) + recurse3(child, index) index.pop() }) } else { diff --git a/src/utils/optimizeCallback.js b/src/utils/optimizeCallback.js index 68657c3ad6..0d2f4e1b74 100644 --- a/src/utils/optimizeCallback.js +++ b/src/utils/optimizeCallback.js @@ -15,7 +15,7 @@ export function optimizeCallback (callback, array, name) { const firstIndex = (array.isMatrix ? array.size() : arraySize(array)).map(() => 0) const firstValue = array.isMatrix ? array.get(firstIndex) : get(array, firstIndex) const hasSingleSignature = Object.keys(callback.signatures).length === 1 - const numberOfArguments = findNumberOfArguments(callback, firstValue, firstIndex, array) + const numberOfArguments = _typedFindNumberOfArguments(callback, firstValue, firstIndex, array) const fastCallback = hasSingleSignature ? Object.values(callback.signatures)[0] : callback if (numberOfArguments >= 1 && numberOfArguments <= 3) { return (...args) => tryFunctionWithArgs(fastCallback, args.slice(0, numberOfArguments), name, callback.name) @@ -25,7 +25,17 @@ export function optimizeCallback (callback, array, name) { return callback } -export function findNumberOfArguments (callback, value, index, array) { +export function findNumberOfArguments (callback, array) { + if (typed.isTypedFunction(callback)) { + const firstIndex = (array.isMatrix ? array.size() : arraySize(array)).map(() => 0) + const firstValue = array.isMatrix ? array.get(firstIndex) : get(array, firstIndex) + return _typedFindNumberOfArguments(callback, firstValue, firstIndex, array) + } else { + return callback.length + } +} + +function _typedFindNumberOfArguments (callback, value, index, array) { const testArgs = [value, index, array] for (let i = 3; i > 0; i--) { const args = testArgs.slice(0, i) From 8324f63f828d0528ce8ed6d9f7c132b9ca4070b4 Mon Sep 17 00:00:00 2001 From: David Contreras Date: Mon, 16 Sep 2024 16:41:25 -0600 Subject: [PATCH 03/11] Removed return in forEach --- src/utils/array.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/utils/array.js b/src/utils/array.js index 487e41a085..5e68133545 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -920,7 +920,7 @@ export function deepForEach (value, array, callback) { function recurse1 (value) { if (Array.isArray(value)) { - return value.forEach(function (child) { + value.forEach(function (child) { recurse1(child) }) } else { @@ -931,7 +931,7 @@ export function deepForEach (value, array, callback) { function recurse2 (value, index) { if (Array.isArray(value)) { - return value.forEach(function (child, i) { + value.forEach(function (child, i) { index.push(i) recurse2(child, index) index.pop() @@ -944,7 +944,7 @@ export function deepForEach (value, array, callback) { function recurse3 (value, index) { if (Array.isArray(value)) { - return value.forEach(function (child, i) { + value.forEach(function (child, i) { index.push(i) recurse3(child, index) index.pop() From 9c427208f051aabb4589ed3908937aba03ebce72 Mon Sep 17 00:00:00 2001 From: David Contreras Date: Wed, 18 Sep 2024 20:38:28 -0600 Subject: [PATCH 04/11] Clone with slice --- src/utils/array.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/utils/array.js b/src/utils/array.js index 5e68133545..83733a0178 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -872,7 +872,7 @@ export function deepMap (value, array, callback) { }) } else { // invoke the callback function with the right number of arguments - return callback(value, [...index]) + return callback(value, index.slice()) } } @@ -887,7 +887,7 @@ export function deepMap (value, array, callback) { }) } else { // invoke the callback function with the right number of arguments - return callback(value, [...index], array) + return callback(value, index.slice(), array) } } } @@ -938,7 +938,7 @@ export function deepForEach (value, array, callback) { }) } else { // invoke the callback function with the right number of arguments - callback(value, [...index]) + callback(value, index.slice()) } } @@ -951,7 +951,7 @@ export function deepForEach (value, array, callback) { }) } else { // invoke the callback function with the right number of arguments - callback(value, [...index], array) + callback(value, index.slice(), array) } } } From 2ec54adbf4db4abd2ffcbde9b66ddbaf998f8247 Mon Sep 17 00:00:00 2001 From: David Contreras Date: Wed, 18 Sep 2024 20:57:49 -0600 Subject: [PATCH 05/11] Added index benchmarks --- test/benchmark/forEach.js | 10 ++++++++++ test/benchmark/map.js | 9 +++++++++ 2 files changed, 19 insertions(+) diff --git a/test/benchmark/forEach.js b/test/benchmark/forEach.js index 7f3f3da510..9030c2027a 100644 --- a/test/benchmark/forEach.js +++ b/test/benchmark/forEach.js @@ -43,6 +43,16 @@ new Benchmark.Suite() .add(pad('numberMatrix.forEach(abs.signatures.number)'), () => { numberMatrix.forEach(abs.signatures.number) }) + .add(pad('genericMatrix.forEach(abs+idx)'), () => { + genericMatrix.forEach((x, idx) => abs(x)+idx[0]-idx[1]) + }) + .add(pad('numberMatrix.forEach(abs+idx)'), () => { + numberMatrix.forEach((x, idx) => abs(x)+idx[0]-idx[1]) + }) + .add(pad('forEach(genericMatrix, abs+idx)'), () => { + forEach(genericMatrix, ((x, idx) => abs(x)+idx[0]-idx[1])) + }) + .add() .on('cycle', function (event) { console.log(String(event.target)) }) diff --git a/test/benchmark/map.js b/test/benchmark/map.js index 05b0ea3986..016d258dcd 100644 --- a/test/benchmark/map.js +++ b/test/benchmark/map.js @@ -43,6 +43,15 @@ new Benchmark.Suite() .add(pad('numberMatrix.map(abs.signatures.number)'), () => { numberMatrix.map(abs.signatures.number) }) + .add(pad('map(array, abs + idx)'), () => { + map(array, (x, idx) => abs(x)+idx[0]-idx[1]) + }) + .add(pad('genericMatrix.map(abs + idx)'), () => { + genericMatrix.map((x, idx) => abs(x)+idx[0]-idx[1]) + }) + .add(pad('numberMatrix.map(abs + idx)'), () => { + numberMatrix.map((x, idx) => abs(x)+idx[0]-idx[1]) + }) .on('cycle', function (event) { console.log(String(event.target)) }) From 81f6d7697bcbf62db3e648054ba975c2c8f012ce Mon Sep 17 00:00:00 2001 From: David Contreras Date: Thu, 19 Sep 2024 19:41:04 -0600 Subject: [PATCH 06/11] Format --- test/benchmark/forEach.js | 6 +++--- test/benchmark/map.js | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/test/benchmark/forEach.js b/test/benchmark/forEach.js index 9030c2027a..06ad200731 100644 --- a/test/benchmark/forEach.js +++ b/test/benchmark/forEach.js @@ -44,13 +44,13 @@ new Benchmark.Suite() numberMatrix.forEach(abs.signatures.number) }) .add(pad('genericMatrix.forEach(abs+idx)'), () => { - genericMatrix.forEach((x, idx) => abs(x)+idx[0]-idx[1]) + genericMatrix.forEach((x, idx) => abs(x) + idx[0] - idx[1]) }) .add(pad('numberMatrix.forEach(abs+idx)'), () => { - numberMatrix.forEach((x, idx) => abs(x)+idx[0]-idx[1]) + numberMatrix.forEach((x, idx) => abs(x) + idx[0] - idx[1]) }) .add(pad('forEach(genericMatrix, abs+idx)'), () => { - forEach(genericMatrix, ((x, idx) => abs(x)+idx[0]-idx[1])) + forEach(genericMatrix, (x, idx) => abs(x) + idx[0] - idx[1]) }) .add() .on('cycle', function (event) { diff --git a/test/benchmark/map.js b/test/benchmark/map.js index 016d258dcd..6754592971 100644 --- a/test/benchmark/map.js +++ b/test/benchmark/map.js @@ -44,13 +44,13 @@ new Benchmark.Suite() numberMatrix.map(abs.signatures.number) }) .add(pad('map(array, abs + idx)'), () => { - map(array, (x, idx) => abs(x)+idx[0]-idx[1]) + map(array, (x, idx) => abs(x) + idx[0] - idx[1]) }) .add(pad('genericMatrix.map(abs + idx)'), () => { - genericMatrix.map((x, idx) => abs(x)+idx[0]-idx[1]) + genericMatrix.map((x, idx) => abs(x) + idx[0] - idx[1]) }) .add(pad('numberMatrix.map(abs + idx)'), () => { - numberMatrix.map((x, idx) => abs(x)+idx[0]-idx[1]) + numberMatrix.map((x, idx) => abs(x) + idx[0] - idx[1]) }) .on('cycle', function (event) { console.log(String(event.target)) From edc61eb069925079dc84c6827c49c753cc3ec88c Mon Sep 17 00:00:00 2001 From: David Contreras Date: Thu, 19 Sep 2024 21:12:35 -0600 Subject: [PATCH 07/11] Optimize callback returns the number of arguments --- src/function/matrix/filter.js | 2 +- src/function/matrix/forEach.js | 3 ++- src/function/matrix/map.js | 3 ++- src/type/matrix/DenseMatrix.js | 8 ++++---- src/type/matrix/SparseMatrix.js | 4 ++-- src/utils/array.js | 10 ++++------ src/utils/optimizeCallback.js | 25 ++++++++++++++++++------- 7 files changed, 33 insertions(+), 22 deletions(-) diff --git a/src/function/matrix/filter.js b/src/function/matrix/filter.js index 1b8c832647..e56e745853 100644 --- a/src/function/matrix/filter.js +++ b/src/function/matrix/filter.js @@ -58,7 +58,7 @@ export const createFilter = /* #__PURE__ */ factory(name, dependencies, ({ typed * @private */ function _filterCallback (x, callback) { - const fastCallback = optimizeCallback(callback, x, 'filter') + const fastCallback = optimizeCallback(callback, x, 'filter')[0] return filter(x, function (value, index, array) { // invoke the callback function with the right number of arguments return fastCallback(value, [index], array) diff --git a/src/function/matrix/forEach.js b/src/function/matrix/forEach.js index 5ceb158815..a8b7c3fa9f 100644 --- a/src/function/matrix/forEach.js +++ b/src/function/matrix/forEach.js @@ -45,5 +45,6 @@ export const createForEach = /* #__PURE__ */ factory(name, dependencies, ({ type * @private */ function _forEach (array, callback) { - deepForEach(array, array, optimizeCallback(callback, array, name)) + const [fastCallback, numberOfArguments] = optimizeCallback(callback, array, name, { detailedError: true }) + deepForEach(array, array, fastCallback, numberOfArguments) } diff --git a/src/function/matrix/map.js b/src/function/matrix/map.js index fb4c94ab82..b295e600f6 100644 --- a/src/function/matrix/map.js +++ b/src/function/matrix/map.js @@ -151,6 +151,7 @@ export const createMap = /* #__PURE__ */ factory(name, dependencies, ({ typed }) * @private */ function _mapArray (array, callback) { - return deepMap(array, array, optimizeCallback(callback, array, name)) + const [fastCallback, numberOfArguments] = optimizeCallback(callback, array, name, { detailedError: true }) + return deepMap(array, array, fastCallback, numberOfArguments) } }) diff --git a/src/type/matrix/DenseMatrix.js b/src/type/matrix/DenseMatrix.js index 35663be902..d2e75b802e 100644 --- a/src/type/matrix/DenseMatrix.js +++ b/src/type/matrix/DenseMatrix.js @@ -537,11 +537,11 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies DenseMatrix.prototype.map = function (callback) { // matrix instance const me = this - const fastCallback = optimizeCallback(callback, me._data, 'map') + const [fastCallback, numberOfArguments] = optimizeCallback(callback, me._data, 'map') // determine the new datatype when the original matrix has datatype defined // TODO: should be done in matrix constructor instead - const data = deepMap(me._data, me, fastCallback) + const data = deepMap(me._data, me, fastCallback, numberOfArguments) const datatype = me._datatype !== undefined ? getArrayDataType(data, typeOf) : undefined @@ -558,8 +558,8 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies DenseMatrix.prototype.forEach = function (callback) { // matrix instance const me = this - const fastCallback = optimizeCallback(callback, me._data, 'forEach') - deepForEach(this._data, me, fastCallback) + const [fastCallback, numberOfArguments] = optimizeCallback(callback, me._data, 'forEach') + deepForEach(this._data, me, fastCallback, numberOfArguments) } /** diff --git a/src/type/matrix/SparseMatrix.js b/src/type/matrix/SparseMatrix.js index 355cfb297d..deed71442e 100644 --- a/src/type/matrix/SparseMatrix.js +++ b/src/type/matrix/SparseMatrix.js @@ -853,7 +853,7 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie // rows and columns const rows = this._size[0] const columns = this._size[1] - const fastCallback = optimizeCallback(callback, me, 'map') + const fastCallback = optimizeCallback(callback, me, 'map')[0] // invoke callback const invoke = function (v, i, j) { // invoke callback @@ -962,7 +962,7 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie // rows and columns const rows = this._size[0] const columns = this._size[1] - const fastCallback = optimizeCallback(callback, me, 'forEach') + const fastCallback = optimizeCallback(callback, me, 'forEach')[0] // loop columns for (let j = 0; j < columns; j++) { // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] diff --git a/src/utils/array.js b/src/utils/array.js index 83733a0178..3cbd3506d0 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -835,9 +835,8 @@ export function get (array, index) { * @param {Function} callback - Function that produces the element of the new Array, taking three arguments: the value of the element, the index of the element, and the Array being processed. * @returns {*} The new array with each element being the result of the callback function. */ -export function deepMap (value, array, callback) { - const numberOfArguments = findNumberOfArguments(callback, array) - switch (numberOfArguments) { +export function deepMap (value, array, callback, numberOfArguments) { + switch (numberOfArguments || findNumberOfArguments(callback, array)) { case 1: return recurse1(value) case 2: @@ -901,9 +900,8 @@ export function deepMap (value, array, callback) { * @param {Function} callback - Function that produces the element of the new Array, taking three arguments: the value of the element, the index of the element, and the Array being processed. * @returns {*} The new array with each element being the result of the callback function. */ -export function deepForEach (value, array, callback) { - const numberOfArguments = findNumberOfArguments(callback, array) - switch (numberOfArguments) { +export function deepForEach (value, array, callback, numberOfArguments) { + switch (numberOfArguments || findNumberOfArguments(callback, array)) { case 1: recurse1(value) break diff --git a/src/utils/optimizeCallback.js b/src/utils/optimizeCallback.js index 0d2f4e1b74..f3f9ab90cc 100644 --- a/src/utils/optimizeCallback.js +++ b/src/utils/optimizeCallback.js @@ -8,21 +8,32 @@ import { typeOf as _typeOf } from './is.js' * @param {Function} callback The original callback function to simplify. * @param {Array|Matrix} array The array that will be used with the callback function. * @param {string} name The name of the function that is using the callback. - * @returns {Function} Returns a simplified version of the callback function. + * @returns {Array} Returns an array with the simplified version of the callback function and it's number of arguments */ -export function optimizeCallback (callback, array, name) { +export function optimizeCallback (callback, array, name, options) { if (typed.isTypedFunction(callback)) { const firstIndex = (array.isMatrix ? array.size() : arraySize(array)).map(() => 0) const firstValue = array.isMatrix ? array.get(firstIndex) : get(array, firstIndex) const hasSingleSignature = Object.keys(callback.signatures).length === 1 const numberOfArguments = _typedFindNumberOfArguments(callback, firstValue, firstIndex, array) - const fastCallback = hasSingleSignature ? Object.values(callback.signatures)[0] : callback - if (numberOfArguments >= 1 && numberOfArguments <= 3) { - return (...args) => tryFunctionWithArgs(fastCallback, args.slice(0, numberOfArguments), name, callback.name) + const fastCallback = hasSingleSignature ? Object.values(callback.signatures)[0] : (...args) => callback(...args) + if (options && options.detailedError) { + if (numberOfArguments >= 1 && numberOfArguments <= 3) { + const limitedCallback = (...args) => tryFunctionWithArgs(fastCallback, args.slice(0, numberOfArguments), name, callback.name) + return [limitedCallback, numberOfArguments] + } else { + const enhancedCallback = (...args) => tryFunctionWithArgs(fastCallback, args, name, callback.name) + return [enhancedCallback, numberOfArguments] + } + } else { + if (numberOfArguments >= 1 && numberOfArguments <= 3) { + return [(...args) => fastCallback(...args.slice(0, numberOfArguments)), numberOfArguments] + } else { + return [(...args) => fastCallback(...args), numberOfArguments] + } } - return (...args) => tryFunctionWithArgs(fastCallback, args, name, callback.name) } - return callback + return [callback, callback.length] } export function findNumberOfArguments (callback, array) { From 9f80b901e6cb12e49279748d356ea65d77b3586b Mon Sep 17 00:00:00 2001 From: David Contreras Date: Thu, 19 Sep 2024 21:12:59 -0600 Subject: [PATCH 08/11] Added tests for callbacks with three arguments. --- test/benchmark/forEach.js | 12 ++++++++++++ test/benchmark/map.js | 9 +++++++++ 2 files changed, 21 insertions(+) diff --git a/test/benchmark/forEach.js b/test/benchmark/forEach.js index 06ad200731..6b720df30f 100644 --- a/test/benchmark/forEach.js +++ b/test/benchmark/forEach.js @@ -52,6 +52,18 @@ new Benchmark.Suite() .add(pad('forEach(genericMatrix, abs+idx)'), () => { forEach(genericMatrix, (x, idx) => abs(x) + idx[0] - idx[1]) }) + .add(pad('genericMatrix.forEach(abs+idx+arr)'), () => { + genericMatrix.forEach((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) + }) + .add(pad('numberMatrix.forEach(abs+idx+arr)'), () => { + numberMatrix.forEach((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) + }) + .add(pad('forEach(genericMatrix, abs+idx+arr)'), () => { + forEach(genericMatrix, (x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) + }) + .add(pad('forEach(array, abs+idx+arr)'), () => { + forEach(array, (x, idx, X) => abs(x) + idx[0] - idx[1] + X[0][0]) + }) .add() .on('cycle', function (event) { console.log(String(event.target)) diff --git a/test/benchmark/map.js b/test/benchmark/map.js index 6754592971..7581b3c5ed 100644 --- a/test/benchmark/map.js +++ b/test/benchmark/map.js @@ -52,6 +52,15 @@ new Benchmark.Suite() .add(pad('numberMatrix.map(abs + idx)'), () => { numberMatrix.map((x, idx) => abs(x) + idx[0] - idx[1]) }) + .add(pad('map(array, abs + idx + arr)'), () => { + map(array, (x, idx, X) => abs(x) + idx[0] - idx[1] + X[0][0]) + }) + .add(pad('genericMatrix.map(abs + idx + matrix)'), () => { + genericMatrix.map((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) + }) + .add(pad('numberMatrix.map(abs + idx + matrix)'), () => { + numberMatrix.map((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) + }) .on('cycle', function (event) { console.log(String(event.target)) }) From a5618b5a25d9bad0b193108c23c688508bb2e6cd Mon Sep 17 00:00:00 2001 From: David Contreras Date: Fri, 20 Sep 2024 21:12:17 -0600 Subject: [PATCH 09/11] optimizeCallback returns a non typed function. --- src/function/matrix/filter.js | 2 +- src/function/matrix/forEach.js | 4 ++-- src/function/matrix/map.js | 4 ++-- src/type/matrix/DenseMatrix.js | 8 ++++---- src/type/matrix/SparseMatrix.js | 4 ++-- src/utils/optimizeCallback.js | 34 +++++++++++++++++++++------------ 6 files changed, 33 insertions(+), 23 deletions(-) diff --git a/src/function/matrix/filter.js b/src/function/matrix/filter.js index e56e745853..1b8c832647 100644 --- a/src/function/matrix/filter.js +++ b/src/function/matrix/filter.js @@ -58,7 +58,7 @@ export const createFilter = /* #__PURE__ */ factory(name, dependencies, ({ typed * @private */ function _filterCallback (x, callback) { - const fastCallback = optimizeCallback(callback, x, 'filter')[0] + const fastCallback = optimizeCallback(callback, x, 'filter') return filter(x, function (value, index, array) { // invoke the callback function with the right number of arguments return fastCallback(value, [index], array) diff --git a/src/function/matrix/forEach.js b/src/function/matrix/forEach.js index a8b7c3fa9f..21fb8baf00 100644 --- a/src/function/matrix/forEach.js +++ b/src/function/matrix/forEach.js @@ -45,6 +45,6 @@ export const createForEach = /* #__PURE__ */ factory(name, dependencies, ({ type * @private */ function _forEach (array, callback) { - const [fastCallback, numberOfArguments] = optimizeCallback(callback, array, name, { detailedError: true }) - deepForEach(array, array, fastCallback, numberOfArguments) + const fastCallback = optimizeCallback(callback, array, name, { detailedError: true }) + deepForEach(array, array, fastCallback, fastCallback.length) } diff --git a/src/function/matrix/map.js b/src/function/matrix/map.js index b295e600f6..cd09833dfc 100644 --- a/src/function/matrix/map.js +++ b/src/function/matrix/map.js @@ -151,7 +151,7 @@ export const createMap = /* #__PURE__ */ factory(name, dependencies, ({ typed }) * @private */ function _mapArray (array, callback) { - const [fastCallback, numberOfArguments] = optimizeCallback(callback, array, name, { detailedError: true }) - return deepMap(array, array, fastCallback, numberOfArguments) + const fastCallback = optimizeCallback(callback, array, name, { detailedError: true }) + return deepMap(array, array, fastCallback, fastCallback.length) } }) diff --git a/src/type/matrix/DenseMatrix.js b/src/type/matrix/DenseMatrix.js index d2e75b802e..bbbd3eea4a 100644 --- a/src/type/matrix/DenseMatrix.js +++ b/src/type/matrix/DenseMatrix.js @@ -537,11 +537,11 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies DenseMatrix.prototype.map = function (callback) { // matrix instance const me = this - const [fastCallback, numberOfArguments] = optimizeCallback(callback, me._data, 'map') + const fastCallback = optimizeCallback(callback, me._data, 'map') // determine the new datatype when the original matrix has datatype defined // TODO: should be done in matrix constructor instead - const data = deepMap(me._data, me, fastCallback, numberOfArguments) + const data = deepMap(me._data, me, fastCallback, fastCallback.length) const datatype = me._datatype !== undefined ? getArrayDataType(data, typeOf) : undefined @@ -558,8 +558,8 @@ export const createDenseMatrixClass = /* #__PURE__ */ factory(name, dependencies DenseMatrix.prototype.forEach = function (callback) { // matrix instance const me = this - const [fastCallback, numberOfArguments] = optimizeCallback(callback, me._data, 'forEach') - deepForEach(this._data, me, fastCallback, numberOfArguments) + const fastCallback = optimizeCallback(callback, me._data, 'forEach') + deepForEach(this._data, me, fastCallback, fastCallback.length) } /** diff --git a/src/type/matrix/SparseMatrix.js b/src/type/matrix/SparseMatrix.js index deed71442e..355cfb297d 100644 --- a/src/type/matrix/SparseMatrix.js +++ b/src/type/matrix/SparseMatrix.js @@ -853,7 +853,7 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie // rows and columns const rows = this._size[0] const columns = this._size[1] - const fastCallback = optimizeCallback(callback, me, 'map')[0] + const fastCallback = optimizeCallback(callback, me, 'map') // invoke callback const invoke = function (v, i, j) { // invoke callback @@ -962,7 +962,7 @@ export const createSparseMatrixClass = /* #__PURE__ */ factory(name, dependencie // rows and columns const rows = this._size[0] const columns = this._size[1] - const fastCallback = optimizeCallback(callback, me, 'forEach')[0] + const fastCallback = optimizeCallback(callback, me, 'forEach') // loop columns for (let j = 0; j < columns; j++) { // k0 <= k < k1 where k0 = _ptr[j] && k1 = _ptr[j+1] diff --git a/src/utils/optimizeCallback.js b/src/utils/optimizeCallback.js index f3f9ab90cc..049aad5eed 100644 --- a/src/utils/optimizeCallback.js +++ b/src/utils/optimizeCallback.js @@ -16,24 +16,34 @@ export function optimizeCallback (callback, array, name, options) { const firstValue = array.isMatrix ? array.get(firstIndex) : get(array, firstIndex) const hasSingleSignature = Object.keys(callback.signatures).length === 1 const numberOfArguments = _typedFindNumberOfArguments(callback, firstValue, firstIndex, array) - const fastCallback = hasSingleSignature ? Object.values(callback.signatures)[0] : (...args) => callback(...args) if (options && options.detailedError) { - if (numberOfArguments >= 1 && numberOfArguments <= 3) { - const limitedCallback = (...args) => tryFunctionWithArgs(fastCallback, args.slice(0, numberOfArguments), name, callback.name) - return [limitedCallback, numberOfArguments] - } else { - const enhancedCallback = (...args) => tryFunctionWithArgs(fastCallback, args, name, callback.name) - return [enhancedCallback, numberOfArguments] + const fastCallback = hasSingleSignature ? Object.values(callback.signatures)[0] : callback + switch (numberOfArguments) { + case 1: + return (val) => tryFunctionWithArgs(fastCallback, [val], name, callback.name) + case 2: + return (val, idx) => tryFunctionWithArgs(fastCallback, [val, idx], name, callback.name) + case 3: + return (val, idx, array) => tryFunctionWithArgs(fastCallback, [val, idx, array], name, callback.name) + default: + return (...args) => tryFunctionWithArgs(fastCallback, args, name, callback.name) } + } else if (hasSingleSignature) { + return Object.values(callback.signatures)[0] } else { - if (numberOfArguments >= 1 && numberOfArguments <= 3) { - return [(...args) => fastCallback(...args.slice(0, numberOfArguments)), numberOfArguments] - } else { - return [(...args) => fastCallback(...args), numberOfArguments] + switch (numberOfArguments) { + case 1: + return val => callback(val) + case 2: + return (val, idx) => callback(val, idx) + case 3: + return (val, idx, array) => callback(val, idx, array) + default: + return callback } } } - return [callback, callback.length] + return callback } export function findNumberOfArguments (callback, array) { From c7a4210197fc4dc4fa53a10d9bc4898938e1349e Mon Sep 17 00:00:00 2001 From: David Contreras Date: Mon, 30 Sep 2024 22:56:11 -0600 Subject: [PATCH 10/11] Optimize recursre functions by mapping the last dimension. --- src/utils/array.js | 96 +++++++++++++++++++++++++++------------------- 1 file changed, 56 insertions(+), 40 deletions(-) diff --git a/src/utils/array.js b/src/utils/array.js index 3cbd3506d0..76b8bc627d 100644 --- a/src/utils/array.js +++ b/src/utils/array.js @@ -836,57 +836,65 @@ export function get (array, index) { * @returns {*} The new array with each element being the result of the callback function. */ export function deepMap (value, array, callback, numberOfArguments) { + const size = arraySize(value) + const N = size.length - 1 switch (numberOfArguments || findNumberOfArguments(callback, array)) { case 1: - return recurse1(value) + return recurse1(value, 0) case 2: - return recurse2(value, []) + return recurse2(value, size.map(() => null), 0) case 3: - return recurse3(value, []) + return recurse3(value, size.map(() => null), 0) default: - return recurse3(value, []) + return recurse3(value, size.map(() => null), 0) } - function recurse1 (value) { - if (Array.isArray(value)) { + function recurse1 (value, depth) { + if (depth < N) { return value.map(function (child) { // we create a copy of the index array and append the new index value - const results = recurse1(child) + const results = recurse1(child, depth + 1) return results }) } else { // invoke the callback function with the right number of arguments - return callback(value) + return value.map(v => callback(v)) } } - function recurse2 (value, index) { - if (Array.isArray(value)) { + function recurse2 (value, index, depth) { + if (depth < N) { return value.map(function (child, i) { // we create a copy of the index array and append the new index value - index.push(i) - const results = recurse2(child, index) - index.pop() + index[depth] = i + const results = recurse2(child, index, depth + 1) + index[depth] = null return results }) } else { // invoke the callback function with the right number of arguments - return callback(value, index.slice()) + return value.map((v, i) => { + index[depth] = i + return callback(v, index.slice()) + }) } } - function recurse3 (value, index) { - if (Array.isArray(value)) { + function recurse3 (value, index, depth) { + if (depth < N) { return value.map(function (child, i) { // we create a copy of the index array and append the new index value - index.push(i) - const results = recurse3(child, index) - index.pop() + index[depth] = i + const results = recurse3(child, index, depth + 1) + index[depth] = null return results }) } else { // invoke the callback function with the right number of arguments - return callback(value, index.slice(), array) + return value.map((v, i) => { + index[depth] = i + return callback(v, index.slice(), array) + }) } } } @@ -901,55 +909,63 @@ export function deepMap (value, array, callback, numberOfArguments) { * @returns {*} The new array with each element being the result of the callback function. */ export function deepForEach (value, array, callback, numberOfArguments) { + const size = arraySize(value) + const N = size.length - 1 switch (numberOfArguments || findNumberOfArguments(callback, array)) { case 1: - recurse1(value) + recurse1(value, 0) break case 2: - recurse2(value, []) + recurse2(value, size.map(() => null), 0) break case 3: - recurse3(value, []) + recurse3(value, size.map(() => null), 0) break default: - recurse3(value, []) + recurse3(value, size.map(() => null), 0) break } - function recurse1 (value) { - if (Array.isArray(value)) { + function recurse1 (value, depth) { + if (depth < N) { value.forEach(function (child) { - recurse1(child) + recurse1(child, depth + 1) }) } else { // invoke the callback function with the right number of arguments - callback(value) + value.forEach(v => callback(v)) } } - function recurse2 (value, index) { - if (Array.isArray(value)) { + function recurse2 (value, index, depth) { + if (depth < N) { value.forEach(function (child, i) { - index.push(i) - recurse2(child, index) - index.pop() + index[depth] = i + recurse2(child, index, depth + 1) + index[depth] = null }) } else { // invoke the callback function with the right number of arguments - callback(value, index.slice()) + value.forEach((v, i) => { + index[depth] = i + callback(v, index.slice()) + }) } } - function recurse3 (value, index) { - if (Array.isArray(value)) { + function recurse3 (value, index, depth) { + if (depth < N) { value.forEach(function (child, i) { - index.push(i) - recurse3(child, index) - index.pop() + index[depth] = i + recurse3(child, index, depth + 1) + index[depth] = null }) } else { // invoke the callback function with the right number of arguments - callback(value, index.slice(), array) + value.forEach((v, i) => { + index[depth] = i + callback(v, index.slice(), array) + }) } } } From f98a164b0a1f3aa65f6c570fc622fffe93e17a1d Mon Sep 17 00:00:00 2001 From: David Contreras Date: Mon, 16 Dec 2024 11:01:19 -0600 Subject: [PATCH 11/11] Updated tests --- test/benchmark/forEach.js | 17 +++++++---------- test/benchmark/map.js | 15 ++++++--------- 2 files changed, 13 insertions(+), 19 deletions(-) diff --git a/test/benchmark/forEach.js b/test/benchmark/forEach.js index b9341854b1..534ebb0caf 100644 --- a/test/benchmark/forEach.js +++ b/test/benchmark/forEach.js @@ -6,9 +6,6 @@ const genericMatrix = map(ones(10, 10, 'dense'), _ => round(random(-5, 5), 2)) const numberMatrix = new DenseMatrix(genericMatrix, 'number') const array = genericMatrix.toArray() -// console.log('data', array) -// console.log('abs(data)', abs(array))npm run - const bench = new Bench({ time: 100, iterations: 100 }) .add('abs(genericMatrix)', () => { abs(genericMatrix) @@ -43,25 +40,25 @@ const bench = new Bench({ time: 100, iterations: 100 }) .add('numberMatrix.forEach(abs.signatures.number)', () => { numberMatrix.forEach(abs.signatures.number) }) - .add(pad('genericMatrix.forEach(abs+idx)'), () => { + .add('genericMatrix.forEach(abs+idx)', () => { genericMatrix.forEach((x, idx) => abs(x) + idx[0] - idx[1]) }) - .add(pad('numberMatrix.forEach(abs+idx)'), () => { + .add('numberMatrix.forEach(abs+idx)', () => { numberMatrix.forEach((x, idx) => abs(x) + idx[0] - idx[1]) }) - .add(pad('forEach(genericMatrix, abs+idx)'), () => { + .add('forEach(genericMatrix, abs+idx)', () => { forEach(genericMatrix, (x, idx) => abs(x) + idx[0] - idx[1]) }) - .add(pad('genericMatrix.forEach(abs+idx+arr)'), () => { + .add('genericMatrix.forEach(abs+idx+arr)', () => { genericMatrix.forEach((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) }) - .add(pad('numberMatrix.forEach(abs+idx+arr)'), () => { + .add('numberMatrix.forEach(abs+idx+arr)', () => { numberMatrix.forEach((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) }) - .add(pad('forEach(genericMatrix, abs+idx+arr)'), () => { + .add('forEach(genericMatrix, abs+idx+arr)', () => { forEach(genericMatrix, (x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) }) - .add(pad('forEach(array, abs+idx+arr)'), () => { + .add('forEach(array, abs+idx+arr)', () => { forEach(array, (x, idx, X) => abs(x) + idx[0] - idx[1] + X[0][0]) }) .add() diff --git a/test/benchmark/map.js b/test/benchmark/map.js index 338a46e682..8c4b68b61e 100644 --- a/test/benchmark/map.js +++ b/test/benchmark/map.js @@ -6,9 +6,6 @@ const genericMatrix = map(ones(10, 10, 'dense'), _ => round(random(-5, 5), 2)) const numberMatrix = new DenseMatrix(genericMatrix, 'number') const array = genericMatrix.toArray() -// console.log('data', array) -// console.log('abs(data)', abs(array))npm run - const bench = new Bench({ time: 100, iterations: 100 }) .add('abs(genericMatrix)', () => { abs(genericMatrix) @@ -43,22 +40,22 @@ const bench = new Bench({ time: 100, iterations: 100 }) .add('numberMatrix.map(abs.signatures.number)', () => { numberMatrix.map(abs.signatures.number) }) - .add(pad('map(array, abs + idx)'), () => { + .add('map(array, abs + idx)', () => { map(array, (x, idx) => abs(x) + idx[0] - idx[1]) }) - .add(pad('genericMatrix.map(abs + idx)'), () => { + .add('genericMatrix.map(abs + idx)', () => { genericMatrix.map((x, idx) => abs(x) + idx[0] - idx[1]) }) - .add(pad('numberMatrix.map(abs + idx)'), () => { + .add('numberMatrix.map(abs + idx)', () => { numberMatrix.map((x, idx) => abs(x) + idx[0] - idx[1]) }) - .add(pad('map(array, abs + idx + arr)'), () => { + .add('map(array, abs + idx + arr)', () => { map(array, (x, idx, X) => abs(x) + idx[0] - idx[1] + X[0][0]) }) - .add(pad('genericMatrix.map(abs + idx + matrix)'), () => { + .add('genericMatrix.map(abs + idx + matrix)', () => { genericMatrix.map((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) }) - .add(pad('numberMatrix.map(abs + idx + matrix)'), () => { + .add('numberMatrix.map(abs + idx + matrix)', () => { numberMatrix.map((x, idx, X) => abs(x) + idx[0] - idx[1] + X.get([0, 0])) }) .on('cycle', function (event) {