/** * @license Apache-2.0 * * Copyright (c) 2021 The Stdlib Authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ 'use strict'; // MODULES // var iterationOrder = require( './../../../base/iteration-order' ); var minmaxViewBufferIndex = require( './../../../base/minmax-view-buffer-index' ); var copy = require( './copy_ndarray.js' ); var blockedaccessorunary2d = require( './2d_blocked_accessors.js' ); var blockedaccessorunary3d = require( './3d_blocked_accessors.js' ); var blockedaccessorunary4d = require( './4d_blocked_accessors.js' ); var blockedaccessorunary5d = require( './5d_blocked_accessors.js' ); var blockedaccessorunary6d = require( './6d_blocked_accessors.js' ); var blockedaccessorunary7d = require( './7d_blocked_accessors.js' ); var blockedaccessorunary8d = require( './8d_blocked_accessors.js' ); var blockedaccessorunary9d = require( './9d_blocked_accessors.js' ); var blockedaccessorunary10d = require( './10d_blocked_accessors.js' ); var blockedunary2d = require( './2d_blocked.js' ); var blockedunary3d = require( './3d_blocked.js' ); var blockedunary4d = require( './4d_blocked.js' ); var blockedunary5d = require( './5d_blocked.js' ); var blockedunary6d = require( './6d_blocked.js' ); var blockedunary7d = require( './7d_blocked.js' ); var blockedunary8d = require( './8d_blocked.js' ); var blockedunary9d = require( './9d_blocked.js' ); var blockedunary10d = require( './10d_blocked.js' ); var accessorunary0d = require( './0d_accessors.js' ); var accessorunary1d = require( './1d_accessors.js' ); var accessorunary2d = require( './2d_accessors.js' ); var accessorunary3d = require( './3d_accessors.js' ); var accessorunary4d = require( './4d_accessors.js' ); var accessorunary5d = require( './5d_accessors.js' ); var accessorunary6d = require( './6d_accessors.js' ); var accessorunary7d = require( './7d_accessors.js' ); var accessorunary8d = require( './8d_accessors.js' ); var accessorunary9d = require( './9d_accessors.js' ); var accessorunary10d = require( './10d_accessors.js' ); var accessorunarynd = require( './nd_accessors.js' ); var unary0d = require( './0d.js' ); var unary1d = require( './1d.js' ); var unary2d = require( './2d.js' ); var unary3d = require( './3d.js' ); var unary4d = require( './4d.js' ); var unary5d = require( './5d.js' ); var unary6d = require( './6d.js' ); var unary7d = require( './7d.js' ); var unary8d = require( './8d.js' ); var unary9d = require( './9d.js' ); var unary10d = require( './10d.js' ); var unarynd = require( './nd.js' ); // VARIABLES // var UNARY = [ unary0d, unary1d, unary2d, unary3d, unary4d, unary5d, unary6d, unary7d, unary8d, unary9d, unary10d ]; var ACCESSOR_UNARY = [ accessorunary0d, accessorunary1d, accessorunary2d, accessorunary3d, accessorunary4d, accessorunary5d, accessorunary6d, accessorunary7d, accessorunary8d, accessorunary9d, accessorunary10d ]; var BLOCKED_UNARY = [ blockedunary2d, // 0 blockedunary3d, blockedunary4d, blockedunary5d, blockedunary6d, blockedunary7d, blockedunary8d, blockedunary9d, blockedunary10d // 8 ]; var BLOCKED_ACCESSOR_UNARY = [ blockedaccessorunary2d, // 0 blockedaccessorunary3d, blockedaccessorunary4d, blockedaccessorunary5d, blockedaccessorunary6d, blockedaccessorunary7d, blockedaccessorunary8d, blockedaccessorunary9d, blockedaccessorunary10d // 8 ]; var MAX_DIMS = UNARY.length - 1; // MAIN // /** * Applies a unary callback to elements in an input ndarray and assigns results to elements in an output ndarray. * * ## Notes * * - Each provided ndarray should be an `object` with the following properties: * * - **dtype**: data type. * - **data**: data buffer. * - **shape**: dimensions. * - **strides**: stride lengths. * - **offset**: index offset. * - **order**: specifies whether an ndarray is row-major (C-style) or column major (Fortran-style). * * @param {ArrayLikeObject} arrays - array-like object containing one input array and one output array * @param {Callback} fcn - unary callback * @throws {Error} arrays must have the same number of dimensions * @throws {Error} arrays must have the same shape * @returns {void} * * @example * var Float64Array = require( '@stdlib/array/float64' ); * * function scale( x ) { * return x * 10.0; * } * * // Create data buffers: * var xbuf = new Float64Array( [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0, 10.0, 11.0, 12.0 ] ); * var ybuf = new Float64Array( 6 ); * * // Define the shape of the input and output arrays: * var shape = [ 3, 1, 2 ]; * * // Define the array strides: * var sx = [ 4, 4, 1 ]; * var sy = [ 2, 2, 1 ]; * * // Define the index offsets: * var ox = 1; * var oy = 0; * * // Create the input and output ndarray-like objects: * var x = { * 'dtype': 'float64', * 'data': xbuf, * 'shape': shape, * 'strides': sx, * 'offset': ox, * 'order': 'row-major' * }; * var y = { * 'dtype': 'float64', * 'data': ybuf, * 'shape': shape, * 'strides': sy, * 'offset': oy, * 'order': 'row-major' * }; * * // Apply the unary function: * unary( [ x, y ], scale ); * * console.log( y.data ); * // => [ 20.0, 30.0, 60.0, 70.0, 100.0, 110.0 ] */ function unary( arrays, fcn ) { var ndims; var xmmv; var ymmv; var shx; var shy; var iox; var ioy; var len; var sx; var sy; var ox; var oy; var ns; var x; var y; var d; var i; // Unpack the ndarrays and standardize ndarray meta data: x = copy( arrays[ 0 ] ); y = copy( arrays[ 1 ] ); // Verify that the input and output arrays have the same number of dimensions... shx = x.shape; shy = y.shape; ndims = shx.length; if ( ndims !== shy.length ) { throw new Error( 'invalid arguments. Arrays must have the same number of dimensions (i.e., same rank). ndims(x) == '+ndims+'. ndims(y) == '+shy.length+'.' ); } // Determine whether we can avoid iteration altogether... if ( ndims === 0 ) { if ( x.accessors || y.accessors ) { return ACCESSOR_UNARY[ ndims ]( x, y, fcn ); } return UNARY[ ndims ]( x, y, fcn ); } // Verify that the input and output arrays have the same dimensions... len = 1; // number of elements ns = 0; // number of singleton dimensions for ( i = 0; i < ndims; i++ ) { d = shx[ i ]; if ( d !== shy[ i ] ) { throw new Error( 'invalid arguments. Arrays must have the same shape.' ); } // Note that, if one of the dimensions is `0`, the length will be `0`... len *= d; // Check whether the current dimension is a singleton dimension... if ( d === 1 ) { ns += 1; } } // Check whether we were provided empty ndarrays... if ( len === 0 ) { return; } // Determine whether the ndarrays are one-dimensional and thus readily translate to one-dimensional strided arrays... if ( ndims === 1 ) { if ( x.accessors || y.accessors ) { return ACCESSOR_UNARY[ ndims ]( x, y, fcn ); } return UNARY[ ndims ]( x, y, fcn ); } sx = x.strides; sy = y.strides; // Determine whether the ndarray has only **one** non-singleton dimension (e.g., ndims=4, shape=[10,1,1,1]) so that we can treat the ndarrays as being equivalent to one-dimensional strided arrays... if ( ns === ndims-1 ) { // Get the index of the non-singleton dimension... for ( i = 0; i < ndims; i++ ) { if ( shx[ i ] !== 1 ) { break; } } x.shape = [ shx[i] ]; y.shape = x.shape; x.strides = [ sx[i] ]; y.strides = [ sy[i] ]; if ( x.accessors || y.accessors ) { return ACCESSOR_UNARY[ 1 ]( x, y, fcn ); } return UNARY[ 1 ]( x, y, fcn ); } iox = iterationOrder( sx ); // +/-1 ioy = iterationOrder( sy ); // +/-1 // Determine whether we can avoid blocked iteration... if ( iox !== 0 && ioy !== 0 && x.order === y.order ) { // Determine the minimum and maximum linear indices which are accessible by the array views: xmmv = minmaxViewBufferIndex( shx, sx, x.offset ); ymmv = minmaxViewBufferIndex( shy, sy, y.offset ); // Determine whether we can ignore shape (and strides) and treat the ndarrays as linear one-dimensional strided arrays... if ( len === ( xmmv[1]-xmmv[0]+1 ) && len === ( ymmv[1]-ymmv[0]+1 ) ) { // Note: the above is equivalent to @stdlib/ndarray/base/assert/is-contiguous, but in-lined so we can retain computed values... if ( iox === 1 ) { ox = xmmv[ 0 ]; } else { ox = xmmv[ 1 ]; } if ( ioy === 1 ) { oy = ymmv[ 0 ]; } else { oy = ymmv[ 1 ]; } x.shape = [ len ]; y.shape = x.shape; x.strides = [ iox ]; y.strides = [ ioy ]; x.offset = ox; y.offset = oy; if ( x.accessors || y.accessors ) { return ACCESSOR_UNARY[ 1 ]( x, y, fcn ); } return UNARY[ 1 ]( x, y, fcn ); } // At least one ndarray is non-contiguous, so we cannot directly use one-dimensional array functionality... // Determine whether we can use simple nested loops... if ( ndims <= MAX_DIMS ) { // So long as iteration for each respective array always moves in the same direction (i.e., no mixed sign strides), we can leverage cache-optimal (i.e., normal) nested loops without resorting to blocked iteration... if ( x.accessors || y.accessors ) { return ACCESSOR_UNARY[ ndims ]( x, y, fcn ); } return UNARY[ ndims ]( x, y, fcn ); } // Fall-through to blocked iteration... } // At this point, we're either dealing with non-contiguous n-dimensional arrays, high dimensional n-dimensional arrays, and/or arrays having differing memory layouts, so our only hope is that we can still perform blocked iteration... // Determine whether we can perform blocked iteration... if ( ndims <= MAX_DIMS ) { if ( x.accessors || y.accessors ) { return BLOCKED_ACCESSOR_UNARY[ ndims-2 ]( x, y, fcn ); } return BLOCKED_UNARY[ ndims-2 ]( x, y, fcn ); } // Fall-through to linear view iteration without regard for how data is stored in memory (i.e., take the slow path)... if ( x.accessors || y.accessors ) { return accessorunarynd( x, y, fcn ); } unarynd( x, y, fcn ); } // EXPORTS // module.exports = unary;