1 /* 2 * Copyright (c) 2016-2020, 2022 Arm Limited. 3 * 4 * SPDX-License-Identifier: MIT 5 * 6 * Permission is hereby granted, free of charge, to any person obtaining a copy 7 * of this software and associated documentation files (the "Software"), to 8 * deal in the Software without restriction, including without limitation the 9 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or 10 * sell copies of the Software, and to permit persons to whom the Software is 11 * furnished to do so, subject to the following conditions: 12 * 13 * The above copyright notice and this permission notice shall be included in all 14 * copies or substantial portions of the Software. 15 * 16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 * SOFTWARE. 23 */ 24 #ifndef ARM_COMPUTE_WINDOW_H 25 #define ARM_COMPUTE_WINDOW_H 26 27 #include <algorithm> 28 #include <array> 29 #include <cstddef> 30 31 #include "arm_compute/core/Coordinates.h" 32 #include "arm_compute/core/Error.h" 33 #include "arm_compute/core/ITensorInfo.h" 34 #include "arm_compute/core/Utils.h" 35 36 namespace arm_compute 37 { 38 /** Describe a multidimensional execution window. */ 39 class Window 40 { 41 public: 42 /** Alias for dimension 0 also known as X dimension */ 43 static constexpr size_t DimX = 0; 44 /** Alias for dimension 1 also known as Y dimension */ 45 static constexpr size_t DimY = 1; 46 /** Alias for dimension 2 also known as Z dimension */ 47 static constexpr size_t DimZ = 2; 48 /** Alias for dimension 3 also known as W dimension */ 49 static constexpr size_t DimW = 3; 50 /** Alias for dimension 4 also known as V dimension */ 51 static constexpr size_t DimV = 4; 52 53 /** Default constructor: create a window containing a single element. */ Window()54 constexpr Window() 55 : _dims(), _is_broadcasted(utility::generate_array<bool, Coordinates::num_max_dimensions, false>::value) 56 { 57 } 58 /** Copy constructor 59 * 60 * @param[in] src Copy the values from src to a new object 61 */ 62 Window(const Window &src); 63 /** Copy assignment operator 64 * 65 * @param[in] rhs Copy the values from rhs to the current object 66 * 67 * @return Reference to the updated object 68 */ 69 Window &operator=(const Window &rhs); 70 71 /** Describe one of the image's dimensions with a start, end and step. 72 * 73 * Iteration through the elements of the dimension is done like this: 74 * for(int v = start(); v < end(); v += step()) 75 * { 76 * ... 77 * } 78 */ 79 class Dimension 80 { 81 public: 82 /** Constructor, by default creates a dimension of 1. 83 * 84 * @param[in] start Start of the dimension 85 * @param[in] end End of the dimension 86 * @param[in] step Step between two elements of the dimension when iterating. 87 * 88 */ 89 constexpr Dimension(int start = 0, int end = 1, int step = 1) _start(start)90 : _start(start), _end(end), _step(step) 91 { 92 } 93 Dimension(const Dimension &d) = default; 94 /** Default assignment operator to allow dimensions to be copied */ 95 Dimension &operator=(const Dimension &d) = default; 96 /** Return the start of the dimension */ start()97 constexpr int start() const 98 { 99 return _start; 100 } 101 /** Return the end of the dimension */ end()102 constexpr int end() const 103 { 104 return _end; 105 } 106 /** Return the step of the dimension */ step()107 constexpr int step() const 108 { 109 return _step; 110 } 111 /** Set the dimension's step 112 * 113 * @param[in] step The new step 114 */ set_step(int step)115 void set_step(int step) 116 { 117 _step = step; 118 } 119 /** Set the dimension's end 120 * 121 * @param[in] end The new end 122 */ set_end(int end)123 void set_end(int end) 124 { 125 _end = end; 126 } 127 /** Check whether two Dimensions are equal. 128 * 129 * @param[in] lhs LHS Dimensions 130 * @param[in] rhs RHS Dimensions 131 * 132 * @return True if the Dimensions are the same. 133 */ 134 friend bool operator==(const Dimension &lhs, const Dimension &rhs) 135 { 136 return (lhs._start == rhs._start) && (lhs._end == rhs._end) && (lhs._step == rhs._step); 137 } 138 139 private: 140 int _start; /**< Start of the dimension */ 141 int _end; /**< End of the dimension */ 142 int _step; 143 }; 144 145 /** Read only access to a given dimension of the window 146 * 147 * @note Precondition: dimension < Coordinates::num_max_dimensions 148 * 149 * @param[in] dimension The dimension to access 150 * 151 * @return The requested dimension 152 */ 153 constexpr const Dimension &operator[](size_t dimension) const; 154 155 /** Alias to access the first dimension of the window 156 * 157 * @return First dimension of the window 158 */ x()159 constexpr const Dimension &x() const 160 { 161 return _dims.at(Window::DimX); 162 } 163 164 /** Alias to access the second dimension of the window 165 * 166 * @return Second dimension of the window 167 */ y()168 constexpr const Dimension &y() const 169 { 170 return _dims.at(Window::DimY); 171 } 172 173 /** Alias to access the third dimension of the window 174 * 175 * @return Third dimension of the window 176 */ z()177 constexpr const Dimension &z() const 178 { 179 return _dims.at(Window::DimZ); 180 } 181 182 /** Set the values of a given dimension 183 * 184 * @param[in] dimension The dimension to set 185 * @param[in] dim The values to set the dimension to 186 */ 187 void set(size_t dimension, const Dimension &dim); 188 189 /** Set the dimension as broadcasted dimension 190 * 191 * @param[in] dimension The dimension to set 192 */ 193 void set_broadcasted(size_t dimension); 194 195 /** Return whether a dimension has been broadcasted 196 * 197 * @param[in] dimension The requested dimension 198 * 199 * @return true if the dimension has been broadcasted 200 */ 201 bool is_broadcasted(size_t dimension) const; 202 203 /** Use the tensor's dimensions to fill the window dimensions. 204 * 205 * @param[in] shape @ref TensorShape to copy the dimensions from. 206 * @param[in] first_dimension Only copy dimensions which are greater or equal to this value. 207 */ 208 void use_tensor_dimensions(const TensorShape &shape, size_t first_dimension = Window::DimX); 209 210 /** Shift the values of a given dimension by the given shift_value 211 * 212 * @param[in] dimension The dimension to shift 213 * @param[in] shift_value Value to shift the start and end values of. 214 */ 215 void shift(size_t dimension, int shift_value); 216 217 /** Shift down all the dimensions of a window 218 * 219 * i.e new_dims[n] = old_dims[n+shift_value]. 220 * 221 * @param[in] shift_value Number of dimensions to shift the window by. 222 * 223 * @return The window with the shifted dimensions. 224 */ 225 Window shift_dimensions(unsigned int shift_value) const; 226 227 /** Adjust the start or end of a given dimension by the given value 228 * 229 * @param[in] dimension The dimension to adjust 230 * @param[in] adjust_value The adjusted value. 231 * @param[in] is_at_start The flag to indicate whether adjust the start or end of the dimension. 232 */ 233 void adjust(size_t dimension, int adjust_value, bool is_at_start); 234 235 /** Scale the values of a given dimension by the given scale_value 236 * 237 * @note The end of the window is rounded up to be a multiple of step after the scaling. 238 * 239 * @param[in] dimension The dimension to scale 240 * @param[in] scale_value Value to scale the start, end and step values of. 241 */ 242 void scale(size_t dimension, float scale_value); 243 244 /** Set the step of a given dimension. 245 * 246 * @param[in] dimension Dimension to update 247 * @param[in] step The new dimension's step value 248 */ 249 void set_dimension_step(size_t dimension, int step); 250 251 /** Will validate all the window's dimensions' values when asserts are enabled 252 * 253 * No-op when asserts are disabled 254 */ 255 void validate() const; 256 257 /** Return the number of iterations needed to iterate through a given dimension 258 * 259 * @param[in] dimension The requested dimension 260 * 261 * @return The number of iterations 262 */ 263 constexpr size_t num_iterations(size_t dimension) const; 264 /** Return the total number of iterations needed to iterate through the entire window 265 * 266 * @return Number of total iterations 267 */ 268 size_t num_iterations_total() const; 269 /** Return the shape of the window in number of steps */ 270 TensorShape shape() const; 271 /** Split a window into a set of sub windows along a given dimension 272 * 273 * For example to split a window into 3 sub-windows along the Y axis, you would have to do:<br/> 274 * Window sub0 = window.split_window( 1, 0, 3);<br/> 275 * Window sub1 = window.split_window( 1, 1, 3);<br/> 276 * Window sub2 = window.split_window( 1, 2, 3);<br/> 277 * 278 * @param[in] dimension Dimension along which the split will be performed 279 * @param[in] id Id of the sub-window to return. Must be in the range (0, total-1) 280 * @param[in] total Total number of sub-windows the window will be split into. 281 * 282 * @return The subwindow "id" out of "total" 283 */ 284 Window split_window(size_t dimension, size_t id, size_t total) const; 285 /** First 1D slice of the window 286 * 287 * @return The first slice of the window. 288 */ first_slice_window_1D()289 Window first_slice_window_1D() const 290 { 291 return first_slice_window<1>(); 292 }; 293 /** First 2D slice of the window 294 * 295 * @return The first slice of the window. 296 */ first_slice_window_2D()297 Window first_slice_window_2D() const 298 { 299 return first_slice_window<2>(); 300 }; 301 /** First 3D slice of the window 302 * 303 * @return The first slice of the window. 304 */ first_slice_window_3D()305 Window first_slice_window_3D() const 306 { 307 return first_slice_window<3>(); 308 }; 309 /** First 4D slice of the window 310 * 311 * @return The first slice of the window. 312 */ first_slice_window_4D()313 Window first_slice_window_4D() const 314 { 315 return first_slice_window<4>(); 316 }; 317 /** Slide the passed 1D window slice. 318 * 319 * If slice contains the last slice then it will remain unchanged and false will be returned. 320 * 321 * @param[in,out] slice Current slice, to be updated to the next slice. 322 * 323 * @return true if slice contains a new slice, false if slice already contained the last slice 324 */ slide_window_slice_1D(Window & slice)325 bool slide_window_slice_1D(Window &slice) const 326 { 327 return slide_window_slice<1>(slice); 328 } 329 /** Slide the passed 2D window slice. 330 * 331 * If slice contains the last slice then it will remain unchanged and false will be returned. 332 * 333 * @param[in,out] slice Current slice, to be updated to the next slice. 334 * 335 * @return true if slice contains a new slice, false if slice already contained the last slice 336 */ slide_window_slice_2D(Window & slice)337 bool slide_window_slice_2D(Window &slice) const 338 { 339 return slide_window_slice<2>(slice); 340 } 341 /** Slide the passed 3D window slice. 342 * 343 * If slice contains the last slice then it will remain unchanged and false will be returned. 344 * 345 * @param[in,out] slice Current slice, to be updated to the next slice. 346 * 347 * @return true if slice contains a new slice, false if slice already contained the last slice 348 */ slide_window_slice_3D(Window & slice)349 bool slide_window_slice_3D(Window &slice) const 350 { 351 return slide_window_slice<3>(slice); 352 } 353 /** Slide the passed 4D window slice. 354 * 355 * If slice contains the last slice then it will remain unchanged and false will be returned. 356 * 357 * @param[in,out] slice Current slice, to be updated to the next slice. 358 * 359 * @return true if slice contains a new slice, false if slice already contained the last slice 360 */ slide_window_slice_4D(Window & slice)361 bool slide_window_slice_4D(Window &slice) const 362 { 363 return slide_window_slice<4>(slice); 364 } 365 /** Collapse the dimensions between @p first and @p last if possible. 366 * 367 * A dimension is collapsable if it starts from 0 and matches the corresponding dimension in the full_window 368 * 369 * @param[in] full_window Full window @p window has been created from. 370 * @param[in] first Start dimension into which the following are collapsed. 371 * @param[in] last End (exclusive) dimension to collapse. 372 * @param[out] has_collapsed (Optional) Whether the window was collapsed. 373 * 374 * @return Collapsed window. 375 */ 376 Window collapse_if_possible(const Window &full_window, size_t first, size_t last, bool *has_collapsed = nullptr) const; 377 378 /** Collapse the dimensions higher than @p first if possible. 379 * 380 * A dimension is collapsable if it starts from 0 and matches the corresponding dimension in the full_window 381 * 382 * @param[in] full_window Full window @p window has been created from. 383 * @param[in] first Start dimension into which the following are collapsed. 384 * @param[out] has_collapsed (Optional) Whether the window was collapsed. 385 * 386 * @return Collapsed window. 387 */ 388 Window collapse_if_possible(const Window &full_window, size_t first, bool *has_collapsed = nullptr) const 389 { 390 return collapse_if_possible(full_window, first, Coordinates::num_max_dimensions, has_collapsed); 391 } 392 393 /** Collapse the dimensions between @p first and @p last. 394 * 395 * A dimension is collapsable if it starts from 0 and matches the corresponding dimension in the full_window 396 * 397 * @param[in] full_window Full window @p window has been created from. 398 * @param[in] first Start dimension into which the following are collapsed. 399 * @param[in] last End (exclusive) dimension to collapse. 400 * 401 * @return Collapsed window if successful. 402 */ 403 Window collapse(const Window &full_window, size_t first, size_t last = Coordinates::num_max_dimensions) const; 404 405 /** Don't advance in the dimension where @p shape is less equal to 1. 406 * 407 * @param[in] shape A TensorShape. 408 * 409 * @return Broadcast window. 410 */ 411 Window broadcast_if_dimension_le_one(const TensorShape &shape) const; 412 413 /** Don't advance in the dimension where shape of @p info is less equal to 1. 414 * 415 * @param[in] info An ITensorInfo. 416 * 417 * @return Broadcast window. 418 */ broadcast_if_dimension_le_one(const ITensorInfo & info)419 Window broadcast_if_dimension_le_one(const ITensorInfo &info) const 420 { 421 return broadcast_if_dimension_le_one(info.tensor_shape()); 422 } 423 /** Friend function that swaps the contents of two windows 424 * 425 * @param[in] lhs First window to swap. 426 * @param[in] rhs Second window to swap. 427 */ 428 friend void swap(Window &lhs, Window &rhs); 429 /** Check whether two Windows are equal. 430 * 431 * @param[in] lhs LHS window 432 * @param[in] rhs RHS window 433 * 434 * @return True if the given windows are the same. 435 */ 436 friend bool operator==(const Window &lhs, const Window &rhs); 437 438 private: 439 /** First slice of the window 440 * 441 * @return The first slice of the window. 442 */ 443 template <unsigned int window_dimension> 444 Window first_slice_window() const; 445 446 /** Slide the passed window slice. 447 * 448 * If slice contains the last slice then it will remain unchanged and false will be returned. 449 * 450 * @param[in,out] slice Current slice, to be updated to the next slice. 451 * 452 * @return true if slice contains a new slice, false if slice already contained the last slice 453 */ 454 template <unsigned int window_dimension> 455 bool slide_window_slice(Window &slice) const; 456 457 private: 458 std::array<Dimension, Coordinates::num_max_dimensions> _dims; 459 std::array<bool, Coordinates::num_max_dimensions> _is_broadcasted; 460 }; 461 } // namespace arm_compute 462 #include "Window.inl" 463 #endif /*ARM_COMPUTE_WINDOW_H */ 464