main.js (4754B)
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 // MAIN // 22 23 /** 24 * Broadcasts array shapes to a single shape. 25 * 26 * ## Notes 27 * 28 * - Two respective dimensions in two shape arrays are compatible if 29 * 30 * 1. the dimensions are equal. 31 * 2. one dimension is `1`. 32 * 33 * - The function returns `null` if provided incompatible shapes (i.e., shapes which cannot be broadcast with one another). 34 * 35 * @param {Array<NonNegativeIntegerArray>} shapes - array of shape arrays 36 * @returns {(NonNegativeIntegerArray|null)} broadcast shape (or `null`) 37 * 38 * @example 39 * var shapes = [ 40 * [ 8, 1, 6, 1 ], 41 * [ 7, 1, 5 ] 42 * ]; 43 * 44 * var out = broadcastShapes( shapes ); 45 * // returns [ 8, 7, 6, 5 ] 46 * 47 * @example 48 * var shapes = [ 49 * [ 5, 4 ], 50 * [ 1 ] 51 * ]; 52 * 53 * var out = broadcastShapes( shapes ); 54 * // returns [ 5, 4 ] 55 * 56 * @example 57 * var shapes = [ 58 * [ 5, 4 ], 59 * [ 4 ] 60 * ]; 61 * 62 * var out = broadcastShapes( shapes ); 63 * // returns [ 5, 4 ] 64 * 65 * @example 66 * var shapes = [ 67 * [ 15, 3, 5 ], 68 * [ 15, 1, 5 ] 69 * ]; 70 * 71 * var out = broadcastShapes( shapes ); 72 * // returns [ 15, 3, 5 ] 73 * 74 * @example 75 * var shapes = [ 76 * [ 15, 3, 5 ], 77 * [ 3, 5 ] 78 * ]; 79 * 80 * var out = broadcastShapes( shapes ); 81 * // returns [ 15, 3, 5 ] 82 * 83 * @example 84 * var shapes = [ 85 * [ 15, 3, 5 ], 86 * [ 3, 1 ] 87 * ]; 88 * 89 * var out = broadcastShapes( shapes ); 90 * // returns [ 15, 3, 5 ] 91 * 92 * @example 93 * var shapes = [ 94 * [ 8, 1, 1, 6, 1 ], 95 * [ 1, 7, 1, 5 ], 96 * [ 8, 4, 1, 6, 5 ] 97 * ]; 98 * 99 * var out = broadcastShapes( shapes ); 100 * // returns [ 8, 4, 7, 6, 5 ] 101 * 102 * @example 103 * var shapes = [ 104 * [ 8, 1, 1, 6, 1 ], 105 * [ 0 ] 106 * ]; 107 * 108 * var out = broadcastShapes( shapes ); 109 * // returns [ 8, 1, 1, 6, 0 ] 110 * 111 * @example 112 * var shapes = [ 113 * [ 8, 1, 1, 6, 1 ], 114 * [ 8, 0, 1, 6, 1 ] 115 * ]; 116 * 117 * var out = broadcastShapes( shapes ); 118 * // returns [ 8, 0, 1, 6, 1 ] 119 * 120 * @example 121 * var shapes = [ 122 * [ 8, 8, 1, 6, 1 ], 123 * [ 8, 0, 1, 6, 1 ] 124 * ]; 125 * 126 * var out = broadcastShapes( shapes ); 127 * // returns null 128 * 129 * @example 130 * var shapes = [ 131 * [ 8, 0, 1, 6, 1 ], 132 * [ 8, 8, 1, 6, 1 ] 133 * ]; 134 * 135 * var out = broadcastShapes( shapes ); 136 * // returns null 137 * 138 * @example 139 * var shapes = [ 140 * [] 141 * ]; 142 * 143 * var out = broadcastShapes( shapes ); 144 * // returns [] 145 * 146 * @example 147 * var shapes = [ 148 * [], 149 * [] 150 * ]; 151 * 152 * var out = broadcastShapes( shapes ); 153 * // returns [] 154 * 155 * @example 156 * var shapes = []; 157 * 158 * var out = broadcastShapes( shapes ); 159 * // returns [] 160 * 161 * @example 162 * var shapes = [ 163 * [ 3, 2, 1 ], 164 * [] 165 * ]; 166 * 167 * var out = broadcastShapes( shapes ); 168 * // returns [ 3, 2, 1 ] 169 * 170 * @example 171 * var shapes = [ 172 * [], 173 * [ 3, 2, 1 ] 174 * ]; 175 * 176 * var out = broadcastShapes( shapes ); 177 * // returns [ 3, 2, 1 ] 178 */ 179 function broadcastShapes( shapes ) { 180 var ndims; 181 var out; 182 var dim; 183 var sh; 184 var n1; 185 var n2; 186 var d; 187 var M; 188 var N; 189 var i; 190 var j; 191 192 M = shapes.length; 193 out = []; 194 if ( M === 0 ) { 195 return out; 196 } 197 sh = shapes[ 0 ]; 198 N = sh.length; 199 200 // If provided a single input shape array, then the broadcast shape is input shape... 201 if ( M === 1 ) { 202 // Need to manually copy to output shape, as shapes could be array-like objects... 203 for ( i = 0; i < N; i++ ) { 204 out.push( sh[ i ] ); 205 } 206 return out; 207 } 208 // Determine the maximum dimensionality... 209 ndims = [ N ]; 210 for ( i = 1; i < M; i++ ) { 211 ndims.push( shapes[ i ].length ); 212 if ( ndims[ i ] > N ) { 213 N = ndims[ i ]; 214 } 215 } 216 // Initialize the output array... 217 for ( i = 0; i < N; i++ ) { 218 out.push( 0 ); 219 } 220 // Compute the broadcast shape... 221 i = N - 1; 222 while ( i >= 0 ) { 223 n1 = ndims[ 0 ] - N + i; 224 if ( n1 >= 0 ) { 225 dim = sh[ n1 ]; 226 } else { 227 dim = 1; 228 } 229 for ( j = 1; j < M; j++ ) { 230 n2 = ndims[ j ] - N + i; 231 if ( n2 >= 0 ) { 232 d = shapes[ j ][ n2 ]; 233 } else { 234 d = 1; 235 } 236 if ( dim === 1 ) { 237 dim = d; 238 continue; 239 } 240 if ( d === 1 || dim === d ) { 241 // When either `d` is `1` or `d` equals the current output shape dimension, the current output shape dimension remains the same... 242 continue; 243 } 244 // The current shape cannot be broadcast against one of the other shapes... 245 return null; 246 } 247 out[ i ] = dim; 248 i -= 1; 249 } 250 return out; 251 } 252 253 254 // EXPORTS // 255 256 module.exports = broadcastShapes;