main.js (10475B)
1 /** 2 * @license Apache-2.0 3 * 4 * Copyright (c) 2021 The Stdlib Authors. 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * See the License for the specific language governing permissions and 16 * limitations under the License. 17 */ 18 19 'use strict'; 20 21 // MODULES // 22 23 var iterationOrder = require( './../../../base/iteration-order' ); 24 var minmaxViewBufferIndex = require( './../../../base/minmax-view-buffer-index' ); 25 var copy = require( './copy_ndarray.js' ); 26 var blockedaccessorunary2d = require( './2d_blocked_accessors.js' ); 27 var blockedaccessorunary3d = require( './3d_blocked_accessors.js' ); 28 var blockedaccessorunary4d = require( './4d_blocked_accessors.js' ); 29 var blockedaccessorunary5d = require( './5d_blocked_accessors.js' ); 30 var blockedaccessorunary6d = require( './6d_blocked_accessors.js' ); 31 var blockedaccessorunary7d = require( './7d_blocked_accessors.js' ); 32 var blockedaccessorunary8d = require( './8d_blocked_accessors.js' ); 33 var blockedaccessorunary9d = require( './9d_blocked_accessors.js' ); 34 var blockedaccessorunary10d = require( './10d_blocked_accessors.js' ); 35 var blockedunary2d = require( './2d_blocked.js' ); 36 var blockedunary3d = require( './3d_blocked.js' ); 37 var blockedunary4d = require( './4d_blocked.js' ); 38 var blockedunary5d = require( './5d_blocked.js' ); 39 var blockedunary6d = require( './6d_blocked.js' ); 40 var blockedunary7d = require( './7d_blocked.js' ); 41 var blockedunary8d = require( './8d_blocked.js' ); 42 var blockedunary9d = require( './9d_blocked.js' ); 43 var blockedunary10d = require( './10d_blocked.js' ); 44 var accessorunary0d = require( './0d_accessors.js' ); 45 var accessorunary1d = require( './1d_accessors.js' ); 46 var accessorunary2d = require( './2d_accessors.js' ); 47 var accessorunary3d = require( './3d_accessors.js' ); 48 var accessorunary4d = require( './4d_accessors.js' ); 49 var accessorunary5d = require( './5d_accessors.js' ); 50 var accessorunary6d = require( './6d_accessors.js' ); 51 var accessorunary7d = require( './7d_accessors.js' ); 52 var accessorunary8d = require( './8d_accessors.js' ); 53 var accessorunary9d = require( './9d_accessors.js' ); 54 var accessorunary10d = require( './10d_accessors.js' ); 55 var accessorunarynd = require( './nd_accessors.js' ); 56 var unary0d = require( './0d.js' ); 57 var unary1d = require( './1d.js' ); 58 var unary2d = require( './2d.js' ); 59 var unary3d = require( './3d.js' ); 60 var unary4d = require( './4d.js' ); 61 var unary5d = require( './5d.js' ); 62 var unary6d = require( './6d.js' ); 63 var unary7d = require( './7d.js' ); 64 var unary8d = require( './8d.js' ); 65 var unary9d = require( './9d.js' ); 66 var unary10d = require( './10d.js' ); 67 var unarynd = require( './nd.js' ); 68 69 70 // VARIABLES // 71 72 var UNARY = [ 73 unary0d, 74 unary1d, 75 unary2d, 76 unary3d, 77 unary4d, 78 unary5d, 79 unary6d, 80 unary7d, 81 unary8d, 82 unary9d, 83 unary10d 84 ]; 85 var ACCESSOR_UNARY = [ 86 accessorunary0d, 87 accessorunary1d, 88 accessorunary2d, 89 accessorunary3d, 90 accessorunary4d, 91 accessorunary5d, 92 accessorunary6d, 93 accessorunary7d, 94 accessorunary8d, 95 accessorunary9d, 96 accessorunary10d 97 ]; 98 var BLOCKED_UNARY = [ 99 blockedunary2d, // 0 100 blockedunary3d, 101 blockedunary4d, 102 blockedunary5d, 103 blockedunary6d, 104 blockedunary7d, 105 blockedunary8d, 106 blockedunary9d, 107 blockedunary10d // 8 108 ]; 109 var BLOCKED_ACCESSOR_UNARY = [ 110 blockedaccessorunary2d, // 0 111 blockedaccessorunary3d, 112 blockedaccessorunary4d, 113 blockedaccessorunary5d, 114 blockedaccessorunary6d, 115 blockedaccessorunary7d, 116 blockedaccessorunary8d, 117 blockedaccessorunary9d, 118 blockedaccessorunary10d // 8 119 ]; 120 var MAX_DIMS = UNARY.length - 1; 121 122 123 // MAIN // 124 125 /** 126 * Applies a unary callback to elements in an input ndarray and assigns results to elements in an output ndarray. 127 * 128 * ## Notes 129 * 130 * - Each provided ndarray should be an `object` with the following properties: 131 * 132 * - **dtype**: data type. 133 * - **data**: data buffer. 134 * - **shape**: dimensions. 135 * - **strides**: stride lengths. 136 * - **offset**: index offset. 137 * - **order**: specifies whether an ndarray is row-major (C-style) or column major (Fortran-style). 138 * 139 * @param {ArrayLikeObject<Object>} arrays - array-like object containing one input array and one output array 140 * @param {Callback} fcn - unary callback 141 * @throws {Error} arrays must have the same number of dimensions 142 * @throws {Error} arrays must have the same shape 143 * @returns {void} 144 * 145 * @example 146 * var Float64Array = require( '@stdlib/array/float64' ); 147 * 148 * function scale( x ) { 149 * return x * 10.0; 150 * } 151 * 152 * // Create data buffers: 153 * 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 ] ); 154 * var ybuf = new Float64Array( 6 ); 155 * 156 * // Define the shape of the input and output arrays: 157 * var shape = [ 3, 1, 2 ]; 158 * 159 * // Define the array strides: 160 * var sx = [ 4, 4, 1 ]; 161 * var sy = [ 2, 2, 1 ]; 162 * 163 * // Define the index offsets: 164 * var ox = 1; 165 * var oy = 0; 166 * 167 * // Create the input and output ndarray-like objects: 168 * var x = { 169 * 'dtype': 'float64', 170 * 'data': xbuf, 171 * 'shape': shape, 172 * 'strides': sx, 173 * 'offset': ox, 174 * 'order': 'row-major' 175 * }; 176 * var y = { 177 * 'dtype': 'float64', 178 * 'data': ybuf, 179 * 'shape': shape, 180 * 'strides': sy, 181 * 'offset': oy, 182 * 'order': 'row-major' 183 * }; 184 * 185 * // Apply the unary function: 186 * unary( [ x, y ], scale ); 187 * 188 * console.log( y.data ); 189 * // => <Float64Array>[ 20.0, 30.0, 60.0, 70.0, 100.0, 110.0 ] 190 */ 191 function unary( arrays, fcn ) { 192 var ndims; 193 var xmmv; 194 var ymmv; 195 var shx; 196 var shy; 197 var iox; 198 var ioy; 199 var len; 200 var sx; 201 var sy; 202 var ox; 203 var oy; 204 var ns; 205 var x; 206 var y; 207 var d; 208 var i; 209 210 // Unpack the ndarrays and standardize ndarray meta data: 211 x = copy( arrays[ 0 ] ); 212 y = copy( arrays[ 1 ] ); 213 214 // Verify that the input and output arrays have the same number of dimensions... 215 shx = x.shape; 216 shy = y.shape; 217 ndims = shx.length; 218 if ( ndims !== shy.length ) { 219 throw new Error( 'invalid arguments. Arrays must have the same number of dimensions (i.e., same rank). ndims(x) == '+ndims+'. ndims(y) == '+shy.length+'.' ); 220 } 221 // Determine whether we can avoid iteration altogether... 222 if ( ndims === 0 ) { 223 if ( x.accessors || y.accessors ) { 224 return ACCESSOR_UNARY[ ndims ]( x, y, fcn ); 225 } 226 return UNARY[ ndims ]( x, y, fcn ); 227 } 228 // Verify that the input and output arrays have the same dimensions... 229 len = 1; // number of elements 230 ns = 0; // number of singleton dimensions 231 for ( i = 0; i < ndims; i++ ) { 232 d = shx[ i ]; 233 if ( d !== shy[ i ] ) { 234 throw new Error( 'invalid arguments. Arrays must have the same shape.' ); 235 } 236 // Note that, if one of the dimensions is `0`, the length will be `0`... 237 len *= d; 238 239 // Check whether the current dimension is a singleton dimension... 240 if ( d === 1 ) { 241 ns += 1; 242 } 243 } 244 // Check whether we were provided empty ndarrays... 245 if ( len === 0 ) { 246 return; 247 } 248 // Determine whether the ndarrays are one-dimensional and thus readily translate to one-dimensional strided arrays... 249 if ( ndims === 1 ) { 250 if ( x.accessors || y.accessors ) { 251 return ACCESSOR_UNARY[ ndims ]( x, y, fcn ); 252 } 253 return UNARY[ ndims ]( x, y, fcn ); 254 } 255 sx = x.strides; 256 sy = y.strides; 257 258 // 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... 259 if ( ns === ndims-1 ) { 260 // Get the index of the non-singleton dimension... 261 for ( i = 0; i < ndims; i++ ) { 262 if ( shx[ i ] !== 1 ) { 263 break; 264 } 265 } 266 x.shape = [ shx[i] ]; 267 y.shape = x.shape; 268 x.strides = [ sx[i] ]; 269 y.strides = [ sy[i] ]; 270 if ( x.accessors || y.accessors ) { 271 return ACCESSOR_UNARY[ 1 ]( x, y, fcn ); 272 } 273 return UNARY[ 1 ]( x, y, fcn ); 274 } 275 iox = iterationOrder( sx ); // +/-1 276 ioy = iterationOrder( sy ); // +/-1 277 278 // Determine whether we can avoid blocked iteration... 279 if ( iox !== 0 && ioy !== 0 && x.order === y.order ) { 280 // Determine the minimum and maximum linear indices which are accessible by the array views: 281 xmmv = minmaxViewBufferIndex( shx, sx, x.offset ); 282 ymmv = minmaxViewBufferIndex( shy, sy, y.offset ); 283 284 // Determine whether we can ignore shape (and strides) and treat the ndarrays as linear one-dimensional strided arrays... 285 if ( len === ( xmmv[1]-xmmv[0]+1 ) && len === ( ymmv[1]-ymmv[0]+1 ) ) { 286 // Note: the above is equivalent to @stdlib/ndarray/base/assert/is-contiguous, but in-lined so we can retain computed values... 287 if ( iox === 1 ) { 288 ox = xmmv[ 0 ]; 289 } else { 290 ox = xmmv[ 1 ]; 291 } 292 if ( ioy === 1 ) { 293 oy = ymmv[ 0 ]; 294 } else { 295 oy = ymmv[ 1 ]; 296 } 297 x.shape = [ len ]; 298 y.shape = x.shape; 299 x.strides = [ iox ]; 300 y.strides = [ ioy ]; 301 x.offset = ox; 302 y.offset = oy; 303 if ( x.accessors || y.accessors ) { 304 return ACCESSOR_UNARY[ 1 ]( x, y, fcn ); 305 } 306 return UNARY[ 1 ]( x, y, fcn ); 307 } 308 // At least one ndarray is non-contiguous, so we cannot directly use one-dimensional array functionality... 309 310 // Determine whether we can use simple nested loops... 311 if ( ndims <= MAX_DIMS ) { 312 // 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... 313 if ( x.accessors || y.accessors ) { 314 return ACCESSOR_UNARY[ ndims ]( x, y, fcn ); 315 } 316 return UNARY[ ndims ]( x, y, fcn ); 317 } 318 // Fall-through to blocked iteration... 319 } 320 // 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... 321 322 // Determine whether we can perform blocked iteration... 323 if ( ndims <= MAX_DIMS ) { 324 if ( x.accessors || y.accessors ) { 325 return BLOCKED_ACCESSOR_UNARY[ ndims-2 ]( x, y, fcn ); 326 } 327 return BLOCKED_UNARY[ ndims-2 ]( x, y, fcn ); 328 } 329 // Fall-through to linear view iteration without regard for how data is stored in memory (i.e., take the slow path)... 330 if ( x.accessors || y.accessors ) { 331 return accessorunarynd( x, y, fcn ); 332 } 333 unarynd( x, y, fcn ); 334 } 335 336 337 // EXPORTS // 338 339 module.exports = unary;