time-to-botec/js/node_modules/@stdlib/ndarray/base/unary/lib/main.js

340 lines
10 KiB
JavaScript
Raw Normal View History

/**
* @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<Object>} 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 );
* // => <Float64Array>[ 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;