1 /* 2 * Copyright (c) 2016-2020 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 51 /** Default constructor: create a window containing a single element. */ Window()52 constexpr Window() 53 : _dims(), _is_broadcasted(utility::generate_array<bool, Coordinates::num_max_dimensions, false>::value) 54 { 55 } 56 /** Copy constructor 57 * 58 * @param[in] src Copy the values from src to a new object 59 */ 60 Window(const Window &src); 61 /** Copy assignment operator 62 * 63 * @param[in] rhs Copy the values from rhs to the current object 64 * 65 * @return Reference to the updated object 66 */ 67 Window &operator=(const Window &rhs); 68 69 /** Describe one of the image's dimensions with a start, end and step. 70 * 71 * Iteration through the elements of the dimension is done like this: 72 * for(int v = start(); v < end(); v += step()) 73 * { 74 * ... 75 * } 76 */ 77 class Dimension 78 { 79 public: 80 /** Constructor, by default creates a dimension of 1. 81 * 82 * @param[in] start Start of the dimension 83 * @param[in] end End of the dimension 84 * @param[in] step Step between two elements of the dimension when iterating. 85 * 86 */ 87 constexpr Dimension(int start = 0, int end = 1, int step = 1) _start(start)88 : _start(start), _end(end), _step(step) 89 { 90 } 91 /** Default assignment operator to allow dimensions to be copied */ 92 Dimension &operator=(const Dimension &d) = default; 93 /** Return the start of the dimension */ start()94 constexpr int start() const 95 { 96 return _start; 97 } 98 /** Return the end of the dimension */ end()99 constexpr int end() const 100 { 101 return _end; 102 } 103 /** Return the step of the dimension */ step()104 constexpr int step() const 105 { 106 return _step; 107 } 108 /** Set the dimension's step 109 * 110 * @param[in] step The new step 111 */ set_step(int step)112 void set_step(int step) 113 { 114 _step = step; 115 } 116 /** Set the dimension's end 117 * 118 * @param[in] end The new end 119 */ set_end(int end)120 void set_end(int end) 121 { 122 _end = end; 123 } 124 125 private: 126 int _start; /**< Start of the dimension */ 127 int _end; /**< End of the dimension */ 128 int _step; 129 }; 130 131 /** Read only access to a given dimension of the window 132 * 133 * @note Precondition: dimension < Coordinates::num_max_dimensions 134 * 135 * @param[in] dimension The dimension to access 136 * 137 * @return The requested dimension 138 */ 139 constexpr const Dimension &operator[](size_t dimension) const; 140 141 /** Alias to access the first dimension of the window 142 * 143 * @return First dimension of the window 144 */ x()145 constexpr const Dimension &x() const 146 { 147 return _dims.at(Window::DimX); 148 } 149 150 /** Alias to access the second dimension of the window 151 * 152 * @return Second dimension of the window 153 */ y()154 constexpr const Dimension &y() const 155 { 156 return _dims.at(Window::DimY); 157 } 158 159 /** Alias to access the third dimension of the window 160 * 161 * @return Third dimension of the window 162 */ z()163 constexpr const Dimension &z() const 164 { 165 return _dims.at(Window::DimZ); 166 } 167 168 /** Set the values of a given dimension 169 * 170 * @param[in] dimension The dimension to set 171 * @param[in] dim The values to set the dimension to 172 */ 173 void set(size_t dimension, const Dimension &dim); 174 175 /** Set the dimension as broadcasted dimension 176 * 177 * @param[in] dimension The dimension to set 178 */ 179 void set_broadcasted(size_t dimension); 180 181 /** Return whether a dimension has been broadcasted 182 * 183 * @param[in] dimension The requested dimension 184 * 185 * @return true if the dimension has been broadcasted 186 */ 187 bool is_broadcasted(size_t dimension) const; 188 189 /** Use the tensor's dimensions to fill the window dimensions. 190 * 191 * @param[in] shape @ref TensorShape to copy the dimensions from. 192 * @param[in] first_dimension Only copy dimensions which are greater or equal to this value. 193 */ 194 void use_tensor_dimensions(const TensorShape &shape, size_t first_dimension = Window::DimX); 195 196 /** Shift the values of a given dimension by the given shift_value 197 * 198 * @param[in] dimension The dimension to shift 199 * @param[in] shift_value Value to shift the start and end values of. 200 */ 201 void shift(size_t dimension, int shift_value); 202 203 /** Shift down all the dimensions of a window 204 * 205 * i.e new_dims[n] = old_dims[n+shift_value]. 206 * 207 * @param[in] shift_value Number of dimensions to shift the window by. 208 * 209 * @return The window with the shifted dimensions. 210 */ 211 Window shift_dimensions(unsigned int shift_value) const; 212 213 /** Adjust the start or end of a given dimension by the given value 214 * 215 * @param[in] dimension The dimension to adjust 216 * @param[in] adjust_value The adjusted value. 217 * @param[in] is_at_start The flag to indicate whether adjust the start or end of the dimension. 218 */ 219 void adjust(size_t dimension, int adjust_value, bool is_at_start); 220 221 /** Scale the values of a given dimension by the given scale_value 222 * 223 * @note The end of the window is rounded up to be a multiple of step after the scaling. 224 * 225 * @param[in] dimension The dimension to scale 226 * @param[in] scale_value Value to scale the start, end and step values of. 227 */ 228 void scale(size_t dimension, float scale_value); 229 230 /** Set the step of a given dimension. 231 * 232 * @param[in] dimension Dimension to update 233 * @param[in] step The new dimension's step value 234 */ 235 void set_dimension_step(size_t dimension, int step); 236 237 /** Will validate all the window's dimensions' values when asserts are enabled 238 * 239 * No-op when asserts are disabled 240 */ 241 void validate() const; 242 243 /** Return the number of iterations needed to iterate through a given dimension 244 * 245 * @param[in] dimension The requested dimension 246 * 247 * @return The number of iterations 248 */ 249 constexpr size_t num_iterations(size_t dimension) const; 250 /** Return the total number of iterations needed to iterate through the entire window 251 * 252 * @return Number of total iterations 253 */ 254 size_t num_iterations_total() const; 255 /** Return the shape of the window in number of steps */ 256 TensorShape shape() const; 257 /** Split a window into a set of sub windows along a given dimension 258 * 259 * For example to split a window into 3 sub-windows along the Y axis, you would have to do:<br/> 260 * Window sub0 = window.split_window( 1, 0, 3);<br/> 261 * Window sub1 = window.split_window( 1, 1, 3);<br/> 262 * Window sub2 = window.split_window( 1, 2, 3);<br/> 263 * 264 * @param[in] dimension Dimension along which the split will be performed 265 * @param[in] id Id of the sub-window to return. Must be in the range (0, total-1) 266 * @param[in] total Total number of sub-windows the window will be split into. 267 * 268 * @return The subwindow "id" out of "total" 269 */ 270 Window split_window(size_t dimension, size_t id, size_t total) const; 271 /** First 1D slice of the window 272 * 273 * @return The first slice of the window. 274 */ first_slice_window_1D()275 Window first_slice_window_1D() const 276 { 277 return first_slice_window<1>(); 278 }; 279 /** First 2D slice of the window 280 * 281 * @return The first slice of the window. 282 */ first_slice_window_2D()283 Window first_slice_window_2D() const 284 { 285 return first_slice_window<2>(); 286 }; 287 /** First 3D slice of the window 288 * 289 * @return The first slice of the window. 290 */ first_slice_window_3D()291 Window first_slice_window_3D() const 292 { 293 return first_slice_window<3>(); 294 }; 295 /** First 4D slice of the window 296 * 297 * @return The first slice of the window. 298 */ first_slice_window_4D()299 Window first_slice_window_4D() const 300 { 301 return first_slice_window<4>(); 302 }; 303 /** Slide the passed 1D window slice. 304 * 305 * If slice contains the last slice then it will remain unchanged and false will be returned. 306 * 307 * @param[in,out] slice Current slice, to be updated to the next slice. 308 * 309 * @return true if slice contains a new slice, false if slice already contained the last slice 310 */ slide_window_slice_1D(Window & slice)311 bool slide_window_slice_1D(Window &slice) const 312 { 313 return slide_window_slice<1>(slice); 314 } 315 /** Slide the passed 2D window slice. 316 * 317 * If slice contains the last slice then it will remain unchanged and false will be returned. 318 * 319 * @param[in,out] slice Current slice, to be updated to the next slice. 320 * 321 * @return true if slice contains a new slice, false if slice already contained the last slice 322 */ slide_window_slice_2D(Window & slice)323 bool slide_window_slice_2D(Window &slice) const 324 { 325 return slide_window_slice<2>(slice); 326 } 327 /** Slide the passed 3D window slice. 328 * 329 * If slice contains the last slice then it will remain unchanged and false will be returned. 330 * 331 * @param[in,out] slice Current slice, to be updated to the next slice. 332 * 333 * @return true if slice contains a new slice, false if slice already contained the last slice 334 */ slide_window_slice_3D(Window & slice)335 bool slide_window_slice_3D(Window &slice) const 336 { 337 return slide_window_slice<3>(slice); 338 } 339 /** Slide the passed 4D window slice. 340 * 341 * If slice contains the last slice then it will remain unchanged and false will be returned. 342 * 343 * @param[in,out] slice Current slice, to be updated to the next slice. 344 * 345 * @return true if slice contains a new slice, false if slice already contained the last slice 346 */ slide_window_slice_4D(Window & slice)347 bool slide_window_slice_4D(Window &slice) const 348 { 349 return slide_window_slice<4>(slice); 350 } 351 352 /** Collapse the dimensions between @p first and @p last if possible. 353 * 354 * A dimension is collapsable if it starts from 0 and matches the corresponding dimension in the full_window 355 * 356 * @param[in] full_window Full window @p window has been created from. 357 * @param[in] first Start dimension into which the following are collapsed. 358 * @param[in] last End (exclusive) dimension to collapse. 359 * @param[out] has_collapsed (Optional) Whether the window was collapsed. 360 * 361 * @return Collapsed window. 362 */ 363 Window collapse_if_possible(const Window &full_window, size_t first, size_t last, bool *has_collapsed = nullptr) const; 364 365 /** Collapse the dimensions higher than @p first 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[out] has_collapsed (Optional) Whether the window was collapsed. 372 * 373 * @return Collapsed window. 374 */ 375 Window collapse_if_possible(const Window &full_window, size_t first, bool *has_collapsed = nullptr) const 376 { 377 return collapse_if_possible(full_window, first, Coordinates::num_max_dimensions, has_collapsed); 378 } 379 380 /** Collapse the dimensions between @p first and @p last. 381 * 382 * A dimension is collapsable if it starts from 0 and matches the corresponding dimension in the full_window 383 * 384 * @param[in] full_window Full window @p window has been created from. 385 * @param[in] first Start dimension into which the following are collapsed. 386 * @param[in] last End (exclusive) dimension to collapse. 387 * 388 * @return Collapsed window if successful. 389 */ 390 Window collapse(const Window &full_window, size_t first, size_t last = Coordinates::num_max_dimensions) const; 391 392 /** Don't advance in the dimension where @p shape is less equal to 1. 393 * 394 * @param[in] shape A TensorShape. 395 * 396 * @return Broadcast window. 397 */ 398 Window broadcast_if_dimension_le_one(const TensorShape &shape) const; 399 400 /** Don't advance in the dimension where shape of @p info is less equal to 1. 401 * 402 * @param[in] info An ITensorInfo. 403 * 404 * @return Broadcast window. 405 */ broadcast_if_dimension_le_one(const ITensorInfo & info)406 Window broadcast_if_dimension_le_one(const ITensorInfo &info) const 407 { 408 return broadcast_if_dimension_le_one(info.tensor_shape()); 409 } 410 /** Friend function that swaps the contents of two windows 411 * 412 * @param[in] lhs First window to swap. 413 * @param[in] rhs Second window to swap. 414 */ 415 friend void swap(Window &lhs, Window &rhs); 416 417 private: 418 /** First slice of the window 419 * 420 * @return The first slice of the window. 421 */ 422 template <unsigned int window_dimension> 423 Window first_slice_window() const; 424 425 /** Slide the passed window slice. 426 * 427 * If slice contains the last slice then it will remain unchanged and false will be returned. 428 * 429 * @param[in,out] slice Current slice, to be updated to the next slice. 430 * 431 * @return true if slice contains a new slice, false if slice already contained the last slice 432 */ 433 template <unsigned int window_dimension> 434 bool slide_window_slice(Window &slice) const; 435 436 private: 437 std::array<Dimension, Coordinates::num_max_dimensions> _dims; 438 std::array<bool, Coordinates::num_max_dimensions> _is_broadcasted; 439 }; 440 } // namespace arm_compute 441 #include "Window.inl" 442 #endif /*ARM_COMPUTE_WINDOW_H */ 443