README.md (10317B)
1 <!-- 2 3 @license Apache-2.0 4 5 Copyright (c) 2021 The Stdlib Authors. 6 7 Licensed under the Apache License, Version 2.0 (the "License"); 8 you may not use this file except in compliance with the License. 9 You may obtain a copy of the License at 10 11 http://www.apache.org/licenses/LICENSE-2.0 12 13 Unless required by applicable law or agreed to in writing, software 14 distributed under the License is distributed on an "AS IS" BASIS, 15 WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 16 See the License for the specific language governing permissions and 17 limitations under the License. 18 19 --> 20 21 # broadcastShapes 22 23 > Broadcast array shapes to a single shape. 24 25 <!-- Section to include introductory text. Make sure to keep an empty line after the intro `section` element and another before the `/section` close. --> 26 27 <section class="intro"> 28 29 </section> 30 31 <!-- /.intro --> 32 33 <!-- Package usage documentation. --> 34 35 <section class="usage"> 36 37 ## Usage 38 39 ```javascript 40 var broadcastShapes = require( '@stdlib/ndarray/base/broadcast-shapes' ); 41 ``` 42 43 #### broadcastShapes( shapes ) 44 45 Broadcasts array shapes to a single shape. 46 47 ```javascript 48 var sh1 = [ 8, 1, 6, 1 ]; 49 var sh2 = [ 7, 1, 5 ]; 50 51 var sh = broadcastShapes( [ sh1, sh2 ] ); 52 // returns [ 8, 7, 6, 5 ] 53 ``` 54 55 </section> 56 57 <!-- /.usage --> 58 59 <!-- Package usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 60 61 <section class="notes"> 62 63 ## Notes 64 65 - When operating on two arrays, the function compares their shapes element-wise, beginning with the trailing (i.e., rightmost) dimension. The following are examples of compatible shapes and their corresponding broadcasted shape: 66 67 ```text 68 A (4d array): 8 x 1 x 6 x 1 69 B (3d array): 7 x 1 x 5 70 --------------------------------- 71 Result (4d array): 8 x 7 x 6 x 5 72 73 A (2d array): 5 x 4 74 B (1d array): 1 75 ------------------------- 76 Result (2d array): 5 x 4 77 78 A (2d array): 5 x 4 79 B (1d array): 4 80 ------------------------- 81 Result (2d array): 5 x 4 82 83 A (3d array): 15 x 3 x 5 84 B (3d array): 15 x 1 x 5 85 ------------------------------ 86 Result (3d array): 15 x 3 x 5 87 88 A (3d array): 15 x 3 x 5 89 B (2d array): 3 x 5 90 ------------------------------ 91 Result (3d array): 15 x 3 x 5 92 93 A (3d array): 15 x 3 x 5 94 B (2d array): 3 x 1 95 ------------------------------ 96 Result (3d array): 15 x 3 x 5 97 98 A (5d array): 8 x 1 x 1 x 6 x 1 99 B (4d array): 1 x 7 x 1 x 5 100 C (5d array): 8 x 4 x 1 x 6 x 5 101 ------------------------------------- 102 Result (5d array): 8 x 4 x 7 x 6 x 5 103 104 A (5d array): 8 x 1 x 1 x 6 x 1 105 B (1d array): 0 106 ------------------------------------- 107 Result (5d array): 8 x 1 x 1 x 6 x 0 108 109 A (5d array): 8 x 0 x 1 x 6 x 1 110 B (2d array): 6 x 5 111 ------------------------------------- 112 Result (5d array): 8 x 0 x 1 x 6 x 5 113 114 A (5d array): 8 x 1 x 1 x 6 x 1 115 B (5d array): 8 x 0 x 1 x 6 x 1 116 ------------------------------------- 117 Result (5d array): 8 x 0 x 1 x 6 x 1 118 119 A (3d array): 3 x 2 x 1 120 B (0d array): 121 ----------------------------- 122 Result (3d array): 3 x 2 x 1 123 124 A (0d array): 125 B (3d array): 3 x 2 x 1 126 ----------------------------- 127 Result (3d array): 3 x 2 x 1 128 ``` 129 130 As demonstrated above, arrays are not required to have the same number of dimensions in order to be broadcast compatible. Array shapes with fewer dimensions are implicitly prepended with singleton dimensions (i.e., dimensions equal to `1`). Accordingly, the following example 131 132 ```text 133 A (2d array): 5 x 4 134 B (1d array): 4 135 ------------------------- 136 Result (2d array): 5 x 4 137 ``` 138 139 is equivalent to 140 141 ```text 142 A (2d array): 5 x 4 143 B (2d array): 1 x 4 144 ------------------------- 145 Result (2d array): 5 x 4 146 ``` 147 148 Similarly, a zero-dimensional array is expanded (by prepending singleton dimensions) from 149 150 ```text 151 A (3d array): 3 x 2 x 1 152 B (0d array): 153 ----------------------------- 154 Result (3d array): 3 x 2 x 1 155 ``` 156 157 to 158 159 ```text 160 A (3d array): 3 x 2 x 1 161 B (3d array): 1 x 1 x 1 162 ----------------------------- 163 Result (3d array): 3 x 2 x 1 164 ``` 165 166 Stated otherwise, every array has implicit leading dimensions of size `1`. During broadcasting, a `3 x 4` matrix is the same as a `3 x 4 x 1 x 1 x 1` multidimensional array. 167 168 - Two respective dimensions in two shape arrays are compatible if 169 170 1. the dimensions are equal. 171 2. one dimension is `1`. 172 173 The two aforementioned rules apply to empty arrays or arrays that have a dimension of size `0`. For unequal dimensions, the size of the dimension which is not `1` determines the size of the output shape dimension. 174 175 Accordingly, dimensions of size `0` must be paired with a dimension of size `0` or `1`. In such cases, by the rules above, the size of the corresponding output shape dimension is `0`. 176 177 - The function returns `null` if provided incompatible shapes (i.e., shapes which cannot be broadcast with one another). 178 179 ```javascript 180 var sh1 = [ 3, 2 ]; 181 var sh2 = [ 2, 3 ]; 182 183 var sh = broadcastShapes( [ sh1, sh2 ] ); 184 // returns null 185 ``` 186 187 The following are examples of array shapes which are **not** compatible and do **not** broadcast: 188 189 ```text 190 A (1d array): 3 191 B (1d array): 4 # dimension does not match 192 193 A (2d array): 2 x 1 194 B (3d array): 8 x 4 x 3 # second dimension does not match 195 196 A (3d array): 15 x 3 x 5 197 B (2d array): 15 x 3 # singleton dimensions can only be prepended, not appended 198 199 A (5d array): 8 x 8 x 1 x 6 x 1 200 B (5d array): 8 x 0 x 1 x 6 x 1 # second dimension does not match 201 ``` 202 203 </section> 204 205 <!-- /.notes --> 206 207 <!-- Package usage examples. --> 208 209 <section class="examples"> 210 211 ## Examples 212 213 <!-- eslint no-undef: "error" --> 214 215 ```javascript 216 var lpad = require( '@stdlib/string/left-pad' ); 217 var broadcastShapes = require( '@stdlib/ndarray/base/broadcast-shapes' ); 218 219 var shapes; 220 var out; 221 var sh; 222 var i; 223 var j; 224 225 function shape2string( shape ) { 226 return lpad( shape.join( ' x ' ), 20, ' ' ); 227 } 228 229 shapes = [ 230 [ [ 1, 2 ], [ 2 ] ], 231 [ [ 1, 1 ], [ 3, 4 ] ], 232 [ [ 6, 7 ], [ 5, 6, 1 ], [ 7 ], [ 5, 1, 7 ] ], 233 [ [ 1, 3 ], [ 3, 1 ] ], 234 [ [ 1 ], [ 3 ] ], 235 [ [ 2 ], [ 3, 2 ] ], 236 [ [ 2, 3 ], [ 2, 3 ], [ 2, 3 ], [ 2, 3 ] ], 237 [ [ 1, 2 ], [ 1, 2 ] ] 238 ]; 239 240 for ( i = 0; i < shapes.length; i++ ) { 241 sh = shapes[ i ]; 242 for ( j = 0; j < sh.length; j++ ) { 243 console.log( shape2string( sh[ j ] ) ); 244 } 245 console.log( lpad( '', 20, '-' ) ); 246 247 out = broadcastShapes( sh ); 248 console.log( shape2string( out )+'\n' ); 249 } 250 ``` 251 252 </section> 253 254 <!-- /.examples --> 255 256 <!-- C interface documentation. --> 257 258 * * * 259 260 <section class="c"> 261 262 ## C APIs 263 264 <!-- Section to include introductory text. Make sure to keep an empty line after the intro `section` element and another before the `/section` close. --> 265 266 <section class="intro"> 267 268 </section> 269 270 <!-- /.intro --> 271 272 <!-- C usage documentation. --> 273 274 <section class="usage"> 275 276 ### Usage 277 278 ```c 279 #include "stdlib/ndarray/base/broadcast_shapes.h" 280 ``` 281 282 #### stdlib_ndarray_broadcast_shapes( M, \*\*shapes, \*ndims, \*out ) 283 284 Broadcasts array shapes to a single shape. 285 286 ```c 287 #include "stdlib/ndarray/base/broadcast_shapes.h" 288 #include <stdint.h> 289 290 int64_t N1 = 4; 291 int64_t sh1[] = { 8, 1, 6, 1 }; 292 293 int64_t N2 = 3; 294 int64_t sh2[] = { 7, 1, 5 }; 295 296 int64_t ndims[] = { N1, N2 }; 297 int64_t *shapes[] = { sh1, sh2 }; 298 299 int64_t out[] = { 0, 0, 0, 0 }; 300 int8_t status = stdlib_ndarray_broadcast_shapes( 2, shapes, ndims, out ); 301 if ( status != 0 ) { 302 // Handle error... 303 } 304 ``` 305 306 The function accepts the following arguments: 307 308 - **M**: `[in] int64_t` number of shape arrays. 309 - **shapes**: `[in] int64_t**` array of shape arrays (dimensions). 310 - **ndims**: `[in] int64_t*` number of dimensions for each respective shape array. 311 - **out**: `[out] int64_t*` output shape array. 312 313 ```c 314 int8_t stdlib_ndarray_broadcast_shapes( int64_t M, int64_t *shapes[], int64_t ndims[], int64_t *out ); 315 ``` 316 317 If successful, the function returns `0`; otherwise, the function returns `-1` (e.g., due to incompatible shapes). 318 319 </section> 320 321 <!-- /.usage --> 322 323 <!-- C API usage notes. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 324 325 <section class="notes"> 326 327 ### Notes 328 329 - Even if the function is unsuccessful, the function may still overwrite elements in the output array before returning. In other words, do not assume that providing incompatible shapes is a no-op with regard to the output array. 330 331 </section> 332 333 <!-- /.notes --> 334 335 <!-- C API usage examples. --> 336 337 <section class="examples"> 338 339 ### Examples 340 341 ```c 342 #include "stdlib/ndarray/base/broadcast_shapes.h" 343 #include <stdint.h> 344 #include <stdio.h> 345 #include <inttypes.h> 346 347 int main() { 348 int64_t N1 = 4; 349 int64_t sh1[] = { 8, 1, 6, 1 }; 350 351 int64_t N2 = 3; 352 int64_t sh2[] = { 7, 1, 5 }; 353 354 int64_t ndims[] = { N1, N2 }; 355 int64_t *shapes[] = { sh1, sh2 }; 356 357 int64_t out[] = { 0, 0, 0, 0 }; 358 int8_t status = stdlib_ndarray_broadcast_shapes( 2, shapes, ndims, out ); 359 if ( status != 0 ) { 360 printf( "incompatible shapes\n" ); 361 return 1; 362 } 363 int64_t i; 364 printf( "shape = ( " ); 365 for ( i = 0; i < N1; i++ ) { 366 printf( "%"PRId64"", out[ i ] ); 367 if ( i < N1-1 ) { 368 printf( ", " ); 369 } 370 } 371 printf( " )\n" ); 372 return 0; 373 } 374 ``` 375 376 </section> 377 378 <!-- /.examples --> 379 380 </section> 381 382 <!-- /.c --> 383 384 <!-- Section to include cited references. If references are included, add a horizontal rule *before* the section. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 385 386 <section class="references"> 387 388 </section> 389 390 <!-- /.references --> 391 392 <!-- Section for all links. Make sure to keep an empty line after the `section` element and another before the `/section` close. --> 393 394 <section class="links"> 395 396 </section> 397 398 <!-- /.links -->