2 // Copyright (c) 2016-2019 Intel Corporation
4 // Licensed under the Apache License, Version 2.0 (the "License");
5 // you may not use this file except in compliance with the License.
6 // You may obtain a copy of the License at
8 // http://www.apache.org/licenses/LICENSE-2.0
10 // Unless required by applicable law or agreed to in writing, software
11 // distributed under the License is distributed on an "AS IS" BASIS,
12 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 // See the License for the specific language governing permissions and
14 // limitations under the License.
17 ///////////////////////////////////////////////////////////////////////////////////////////////////
19 #include "cldnn_defs.h"
20 #include "compounds.h"
21 #include "meta_utils.hpp"
36 /// @addtogroup cpp_api C++ API
39 /// @addtogroup cpp_memory Memory description and management
42 /// @brief Format information helper class.
43 struct format_traits {
44 /// @brief Number of batch dimensions in a format.
46 /// @brief Number of feature map/channel dimensions in a format.
48 /// @brief Number of spatial (x,y) dimensions in a format.
50 /// @brief Number of local (x,y) dimensions in a format.
52 /// @brief Dimensions changing order from rare to often.
54 /// @brief Dimensions order for internal storage.
55 std::string internal_order;
56 /// @brief Block sizes as a vector of pairs of dimension number and block size ordered from rare to often.
57 std::vector<std::pair<size_t, int>> block_sizes;
58 /// @brief Characters representing batch dimensions in an order.
59 static const char* batch_chars() { return "bn"; }
60 /// @brief Characters representing feature map/channel dimensions in an order.
61 static const char* feature_chars() { return "fioc"; }
62 /// @brief Characters representing spatial dimensions in an order.
63 static const char* spatial_chars() { return "xyzhsw"; }
64 /// @brief Characters representing local dimensions in an order.
65 static const char* local_chars() { return "kl"; }
66 /// @brief Checks if @p c represents batch dimension.
67 static bool is_batch_char(char c) { return std::string(batch_chars()).find_first_of(c) != std::string::npos; }
68 /// @brief Checks if @p c represents feature map/channel dimension.
69 static bool is_feature_char(char c) { return std::string(feature_chars()).find_first_of(c) != std::string::npos; }
70 /// @brief Checks if @p c represents spatial dimension.
71 static bool is_spatial_char(char c) { return std::string(spatial_chars()).find_first_of(c) != std::string::npos; }
72 /// @brief Checks if @p c represents local dimensions.
73 static bool is_local_char(char c) { return std::string(local_chars()).find_first_of(c) != std::string::npos; }
76 /// @brief Represents memory formats (orders).
77 /// @n In CNN most of data is described as 4 dimensional blocks. In Intel(R) clDNN library we describe memory with 4 letters
78 /// - b - number of blocks in batch. For weights formats: output features - conv, neurons - inner product
79 /// - f - number of feature maps, features or channels. For weights formats: input features - conv, inputs, inner product
80 /// - x - spatial, width
81 /// - y - spatial, height
83 /// For explanation how each format type is implemented in memory we will use naming shown bellow (b=2,f=3,y=3,x=3):
84 /// \image html layout_memory_representation.jpg
88 yxfb = cldnn_format_yxfb, ///< batch first, feature and than spatials \n \image html yxfb.jpg
89 byxf = cldnn_format_byxf, ///< used in bitmaps, input from user i.e b images of RGB format \n \image html byxf.jpg
90 bfyx = cldnn_format_bfyx, ///< the most common format for activations in clDNN. \n \image html bfyx.jpg
91 fyxb = cldnn_format_fyxb, ///< format not used inside clDNN, but supported in reorder as extension
92 ///< for user provided formats.
93 bfyx_f16 = cldnn_format_bfyx_f16, ///< format used for blocked convolution
94 bs_xs_xsv8_bsv8 = cldnn_format_bs_xs_xsv8_bsv8, ///< format used only for fully connected weights: bs - batch slice,
95 ///< xs - x slice, bsv8 - 8 values of single slice.
96 bs_xs_xsv8_bsv16 = cldnn_format_bs_xs_xsv8_bsv16, ///< format used only for fully connected weights: bs - batch slice,
97 ///< xs - x slice, bsv16 - 16 values of single slice.
98 bs_x_bsv16 = cldnn_format_bs_x_bsv16, ///< format used only for fully connected weights fp16 batch=1 : bs - batch slice
99 ///< (responses slice), bsv16 - 16 values of single batch slice, x - flattened plane of (fyx)
100 ///< \n \image html bs_x_bsv16.jpg
101 bf8_xy16 = cldnn_format_bf8_xy16, ///< format used only for convolution 1x1 input, xy aligned to 16, f aligned to 8
102 ///< \n \image html bf8_xy16.jpg
103 b_fs_yx_32fp = cldnn_format_b_fs_yx_32fp, ///< format for data for binary convolutions
104 ///< \n \image html image_2d_weights_c1_b_fyx.jpg
105 winograd_2x3_s1_data = cldnn_format_winograd_2x3_s1_data, ///< format used for input for winograd convolution, F(2,3) -- filter 3x3 with stride 1
106 byxf_af32 = cldnn_format_byxf_af32, ///< format for input for primitives using MMAD
107 byx8_f4 = cldnn_format_byx8_f4, ///< format for input for MMAD convolutions
108 fs_bs_yx_bsv4_fsv32 = cldnn_format_fs_bs_yx_bs4_fs32, ///< format for batched input for primitives using MMAD
109 b_fs_yx_fsv4 = cldnn_format_b_fs_yx_fsv4, ///< format for input for IMAD convolutions
110 bfzyx = cldnn_format_bfzyx, ///< format for 5d data tensors
111 bfwzyx = cldnn_format_bfwzyx, /// batch, feature, 4D spatial
112 fs_b_yx_fsv32 = cldnn_format_fs_b_yx_fsv32, ///< format for input for fp16 primitives
115 o_i_yx_i16_o16 = cldnn_format_o_i_yx_i16_o16, ///< format used for blocked convolution
116 oiyx_o16 = cldnn_format_oiyx_o16, ///< format used only for convolution weights:
117 ///< os - output feature maps slice, i - input feature maps,
118 ///< yx - spatials, sv16 - 16 values of single slice.
119 os_iyx_osv16 = cldnn_format_os_iyx_osv16, ///< format used only for convolution weights:
120 ///< os - output feature maps slice, i - input feature maps,
121 ///< yx - spatials, sv16 - 16 values of single slice.
122 os_iyx_osv32 = cldnn_format_os_iyx_osv32, ///< format used only for convolution weights:
123 ///< os - output feature maps slice, i - input feature maps,
124 ///< yx - spatials, sv32 - 32 values of single slice.
125 os_iyx_osv64 = cldnn_format_os_iyx_osv64, ///< format used only for convolution weights:
126 ///< os - output feature maps slice, i - input feature maps,
127 ///< yx - spatials, sv64 - 64 values of single slice.
128 image_2d_weights_c4_fyx_b = cldnn_format_image_2d_weights_c4_fyx_b, ///< image format for weights, width size is f*y*x/4
129 ///< (4-channels filled with fyx data), height is b
130 ///< \n \image html image_2d_weights_c4_fyx_b.jpg
131 image_2d_weights_c1_b_fyx = cldnn_format_image_2d_weights_c1_b_fyx, ///< image format for weights, width size is b,
132 ///< height is f*y*x, single channel
133 winograd_2x3_s1_weights = cldnn_format_winograd_2x3_s1_weights, ///< format used for weights for winograd non-fused
134 ///< convolution, F(2,3) -- filter 3x3 with stride 1
135 winograd_2x3_s1_fused_weights = cldnn_format_winograd_2x3_s1_fused_weights, ///< format used for weights for winograd fused
136 ///< convolution, F(2,3) -- filter 3x3 with stride 1
137 winograd_6x3_s1_fused_weights = cldnn_format_winograd_6x3_s1_fused_weights, ///< format used for weights for winograd fused
138 ///< convolution, F(6,3) -- filter 3x3 with stride 1
139 image_2d_weights_winograd_6x3_s1_fbxyb = cldnn_format_image_2d_weights_winograd_6x3_s1_fbxyb, ///< image format used for weights for winograd fused
140 ///< convolution, F(6,3) -- filter 3x3 with stride 1
141 image_2d_weights_winograd_6x3_s1_xfbyb = cldnn_format_image_2d_weights_winograd_6x3_s1_xfbyb, ///< image format used for weights for winograd fused
142 ///< convolution, F(6,3) -- filter 3x3 with stride 1
143 os_is_yx_isa8_osv8_isv4 = cldnn_format_os_is_yx_isa8_osv8_isv4, ///< format for weights for MMAD convolution
144 os_is_yx_isa8_osv8_isv4_swizzled_by_4 = cldnn_format_os_is_yx_isa8_osv8_isv4_swizzled_by_4, ///< format for weights for MMAD convolution
145 is_o_yx_isv32 = cldnn_format_is_o_yx_isv32, ///< format for weights for 1x1 MMAD convolutions
146 is_o32_yx_isv32_swizzled_by_4 = cldnn_format_is_o32_yx_isv32_swizzled_by_4, ///< format for weights for 1x1 MMAD convolutions
147 os_is_y_x8_osv8_isv4 = cldnn_format_os_is_y_x8_osv8_isv4, ///< format for weights for 1x1 MMAD convolutions
148 os_is_y_x8_osv8_isv4_swizzled_by_4 = cldnn_format_os_is_y_x8_osv8_isv4_swizzled_by_4, ///< format for weights for 1x1 MMAD convolutions
149 os_is_yx_osv16_isv4 = cldnn_format_os_is_yx_osv16_isv4, ///< format for weights for IMAD convolutions
150 bf_lyx_yx = cldnn_bf_lyx_yx, ///< format for local convolution weights
151 os_is_yx_osv32_isv32p = cldnn_format_os_is_yx_osv32_isv32p, ///< format for weights for binary convolutions
153 format_num = cldnn_format_format_num, ///< number of format types
154 any = cldnn_format_any
157 /// @brief Get format traits for particular @p format::type
158 static const format_traits& traits(type fmt) {
159 static const std::map<type, format_traits> traits {
160 { yxfb, { 1, 1, 2, 0, "yxfb", "bfxy?", {}}},
161 { byxf, { 1, 1, 2, 0, "byxf", "bfxy?", {}}},
162 { bfyx, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
163 { fyxb, { 1, 1, 2, 0, "fyxb", "bfxy?", {}}},
164 { bfyx_f16, { 1, 1, 2, 0, "bfyx", "bfxy", {{1, 16}}}},
165 { bs_xs_xsv8_bsv8, { 1, 1, 1, 0, "bx", "b?x??", {{2, 8}, {0, 8}}}},
166 { bs_xs_xsv8_bsv16, { 1, 1, 1, 0, "bx", "b?x??", {{2, 8}, {0, 16}}}},
167 { bs_x_bsv16, { 1, 1, 1, 0, "bx", "b?x??", {{0, 16}}}},
168 { bf8_xy16, { 1, 1, 2, 0, "bfyx", "bfxy?", {{1, 8}}}},
169 { winograd_2x3_s1_data, { 1, 1, 2, 0, "bxyf", "bfxy?", {}}},
170 { byxf_af32, { 1, 1, 2, 0, "byxf", "bfxy?", {}}},
171 { byx8_f4 , { 1, 1, 2, 0, "byxf", "bfxy?", {}}},
172 { fs_bs_yx_bsv4_fsv32, { 1, 1, 2, 0, "fbyx", "bfxy?", {{0, 4}, {1, 32}}}},
173 { b_fs_yx_fsv4, { 1, 1, 1, 0, "bfyx", "bfxy?", {{1, 4}}}},
174 { bfzyx, { 1, 1, 3, 0, "bfzyx", "bfxyz", {}}},
175 { bfwzyx, { 1, 1, 4, 0, "bfwzyx", "bfxyzw", {}}},
176 { fs_b_yx_fsv32, { 1, 1, 2, 0, "fbyx", "bfxy?", {{1, 32}}}},
177 { b_fs_yx_32fp, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
179 { o_i_yx_i16_o16, { 1, 1, 2, 0, "bfyx", "bfxy", {{1, 16}, {0, 16}}}},
180 { oiyx_o16, { 1, 1, 2, 0, "bfyx", "bfxy", {{0, 16}}}},
181 { os_iyx_osv16, { 1, 1, 2, 0, "bfyx", "bfxy?", {{0, 16}}}},
182 { os_iyx_osv32, { 1, 1, 2, 0, "bfyx", "bfxy?", {{0, 32}}}},
183 { os_iyx_osv64, { 1, 1, 2, 0, "bfyx", "bfxy?", {{0, 64}}}},
184 { winograd_2x3_s1_weights, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
185 { winograd_2x3_s1_fused_weights, { 1, 1, 2, 0, "xyfb", "bfxy?", {}}},
186 { winograd_6x3_s1_fused_weights, { 1, 1, 2, 0, "xyfb", "bfxy?", {}}},
187 { image_2d_weights_winograd_6x3_s1_fbxyb, { 1, 1, 2, 0, "xyfb", "bfxy?", {}}},
188 { image_2d_weights_winograd_6x3_s1_xfbyb, { 1, 1, 2, 0, "xyfb", "bfxy?", {}}},
189 { image_2d_weights_c4_fyx_b, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
190 { image_2d_weights_c1_b_fyx, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
191 { os_is_yx_isa8_osv8_isv4, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
192 { os_is_yx_isa8_osv8_isv4_swizzled_by_4, { 1, 1, 2, 0, "bfyx", "bfxy?", {}}},
193 { is_o_yx_isv32, { 1, 1, 2, 0, "byxf", "bfxy?", {{1, 32}}}},
194 { is_o32_yx_isv32_swizzled_by_4, { 1, 1, 2, 0, "byxf", "bfxy?", {}}},
195 { os_is_y_x8_osv8_isv4, { 1, 1, 2, 0, "byxf", "bfxy?", {}}},
196 { os_is_y_x8_osv8_isv4_swizzled_by_4, { 1, 1, 2, 0, "byxf", "bfxy?", {}}},
197 { bf_lyx_yx, { 1, 1, 2, 2, "bfklyx", "bfxy??lk", {}}},
198 { os_is_yx_osv16_isv4, { 1, 1, 1, 0, "bfxy", "bfxy?", {{0, 16}, {1, 4}}}},
199 { os_is_yx_osv32_isv32p, { 1, 1, 1, 0, "bfxy", "bfxy?", {}}},
201 return traits.at(fmt);
204 /// @brief Returns number of batch dimensions for a @p format.
205 static size_t batch_num(type fmt) { return traits(fmt).batch_num; }
206 /// @brief Returns number of feature dimensions for a @p format.
207 static size_t feature_num(type fmt) { return traits(fmt).feature_num; }
208 /// @brief Returns number of spatial dimensions for a @p format.
209 static size_t spatial_num(type fmt) { return traits(fmt).spatial_num; }
210 /// @brief Returns number of local dimensions for a @p format.
211 static size_t local_num(type fmt) { return traits(fmt).local_num; }
212 /// @brief Returns an order of dimensions for a @ format.
213 static const std::string& order(type fmt) { return traits(fmt).order; }
214 /// @brief Returns an internal orders of dimensions for a @p format.
215 static const std::string& internal_order(type fmt) { return traits(fmt).internal_order; }
216 /// @brief Returns block sizes for @p format.
217 static const std::vector<std::pair<size_t, int>>& block_sizes(type fmt) { return traits(fmt).block_sizes; }
218 /// @brief Returns number of dimensions contained within a @p format
219 static size_t dimension(type fmt) { return order(fmt).size(); }
220 /// @brief Checks if @p format is a winograd format
221 static bool is_winograd(type fmt) {
222 return (fmt == winograd_2x3_s1_data ||
223 fmt == winograd_2x3_s1_weights ||
224 fmt == winograd_2x3_s1_fused_weights ||
225 fmt == winograd_6x3_s1_fused_weights ||
226 fmt == image_2d_weights_winograd_6x3_s1_fbxyb ||
227 fmt == image_2d_weights_winograd_6x3_s1_xfbyb); }
228 /// @brief Checks if @p format is of image2d type
229 static bool is_image_2d(type fmt) {
230 return (fmt == image_2d_weights_c4_fyx_b ||
231 fmt == image_2d_weights_c1_b_fyx ||
232 fmt == image_2d_weights_winograd_6x3_s1_fbxyb ||
233 fmt == image_2d_weights_winograd_6x3_s1_xfbyb); }
234 /// @brief Checks if @p format is of image type
235 static bool is_image(type fmt) { return (is_image_2d(fmt)); }
236 /// @brief Checks if @p format is blocked format
237 static bool is_blocked(type fmt) { return !(block_sizes(fmt).empty()); }
239 /// @brief Returns number of batch dimensions.
240 size_t batch_num() const { return traits(value).batch_num; }
241 /// @brief Returns number of feature dimensions.
242 size_t feature_num() const { return traits(value).feature_num; }
243 /// @brief Returns number of spatial dimensions.
244 size_t spatial_num() const { return traits(value).spatial_num; }
245 /// @brief Returns number of local dimensions.
246 size_t local_num() const { return traits(value).local_num; }
247 /// @brief Returns an order of dimensions in form of string.
248 const std::string& order() const { return traits(value).order; }
249 /// @brief Returns an internal orders of dimensions form of string.
250 const std::string& internal_order() const { return traits(value).internal_order; }
251 /// @brief Returns block sizes as vector of pairs of dimension and block size for that dimension.
252 const std::vector<std::pair<size_t, int>>& block_sizes() const { return traits(value).block_sizes; }
253 /// @brief Returns number of dimensions contained within this format
254 size_t dimension() const { return order(value).size(); }
255 /// @brief Checks if @p format is a winograd format
256 bool is_winograd() const { return is_winograd(value); }
257 /// @brief Checks if @p format is of image 2d type
258 bool is_image_2d() const { return is_image_2d(value); }
259 /// @brief Checks if @p format is of image type
260 bool is_image() const { return is_image(value); }
261 /// @brief Checks if @p format is blocked format
262 bool is_blocked() { return is_blocked(value); }
264 /// @brief Transforms dimension from internal order to external order
265 size_t internal_to_external(size_t idx) const {
266 auto index = order().find_first_of(internal_order()[idx]);
267 if (index == std::string::npos)
268 throw std::invalid_argument("Internal dimension index does not map to external index.");
273 /// @brief Implicit conversion from format::type.
274 constexpr format(type t) : value(t) {}
275 /// @brief Implicit conversion to format::type.
276 constexpr operator type() const { return value; }
277 /// @brief Conversion from C API @ref ::cldnn_format_type.
278 constexpr explicit format(cldnn_format_type t) : value(static_cast<type>(t)) {}
279 /// @brief Conversion to C API @ref ::cldnn_format_type.
280 constexpr explicit operator cldnn_format_type() const { return static_cast<cldnn_format_type>(value); }
285 /// @brief Helper structs used in tensor constructor with dim_vec_kinds
287 /// @brief enum class that represent dimension kinds
288 enum class dim_vec_kind {
295 /// @brief template class with max_dimensionalities and dimension offset for dimension kinds
296 template <dim_vec_kind Kind>
297 struct dim_vec_limits {
298 static_assert(meta::always_false_ty_val<dim_vec_kind, Kind>::value, "Limits are undefined for selected value of dim_vec_kind.");
302 struct dim_vec_limits<dim_vec_kind::batch> {
303 static constexpr int32_t max_dimentionality = CLDNN_TENSOR_BATCH_DIM_MAX;
304 static constexpr int32_t dim_offset = 0;
308 struct dim_vec_limits<dim_vec_kind::feature> {
309 static constexpr int32_t max_dimentionality = CLDNN_TENSOR_FEATURE_DIM_MAX;
310 static constexpr int32_t dim_offset = CLDNN_TENSOR_BATCH_DIM_MAX;
314 struct dim_vec_limits<dim_vec_kind::spatial> {
315 static constexpr int32_t max_dimentionality = CLDNN_TENSOR_SPATIAL_DIM_MAX;
316 static constexpr int32_t dim_offset = CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX;
320 struct dim_vec_limits<dim_vec_kind::local> {
321 static constexpr int32_t max_dimentionality = CLDNN_TENSOR_LOCAL_DIM_MAX;
322 static constexpr int32_t dim_offset = CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + CLDNN_TENSOR_SPATIAL_DIM_MAX;
325 /// @brief Template class used in tensor constructor using dim_vec_kinds
326 template <dim_vec_kind Kind>
327 class dim_vec_kind_init {
329 static constexpr auto _max_dimensionality = dim_vec_limits<Kind>::max_dimentionality;
330 static constexpr auto _dimOffset = dim_vec_limits<Kind>::dim_offset;
332 template <typename... DimTys>
333 explicit dim_vec_kind_init(DimTys&&... values)
334 : _sizes{int32_t(std::forward<DimTys>(values))...}, _dimSize(sizeof...(DimTys)) {
337 void init_tensor_values(cldnn::tensor& t);
339 int32_t _sizes[_max_dimensionality];
342 } // namespace details
344 template <typename... InitTys>
345 details::dim_vec_kind_init<details::dim_vec_kind::batch> batch(InitTys&&... inits) {
346 return details::dim_vec_kind_init<details::dim_vec_kind::batch>(std::forward<InitTys>(inits)...);
349 template <typename... InitTys>
350 details::dim_vec_kind_init<details::dim_vec_kind::feature> feature(InitTys&&... inits) {
351 return details::dim_vec_kind_init<details::dim_vec_kind::feature>(std::forward<InitTys>(inits)...);
354 template <typename... InitTys>
355 details::dim_vec_kind_init<details::dim_vec_kind::spatial> spatial(InitTys&&... inits) {
356 return details::dim_vec_kind_init<details::dim_vec_kind::spatial>(std::forward<InitTys>(inits)...);
359 template <typename... InitTys>
360 details::dim_vec_kind_init<details::dim_vec_kind::local> local(InitTys&&... inits) {
361 return details::dim_vec_kind_init<details::dim_vec_kind::local>(std::forward<InitTys>(inits)...);
364 /// @brief N-dimensional vector. Mostly used to represent memory size.
366 friend class details::dim_vec_kind_init<details::dim_vec_kind::batch>;
367 friend class details::dim_vec_kind_init<details::dim_vec_kind::feature>;
368 friend class details::dim_vec_kind_init<details::dim_vec_kind::spatial>;
369 friend class details::dim_vec_kind_init<details::dim_vec_kind::local>;
371 typedef int32_t value_type; ///< Values type stored in tensor.
372 // TODO find the way to prevent direct change of following fields.
373 mutable_array_ref<value_type> raw; ///< Raw representation of all dimensions.
374 mutable_array_ref<value_type> batch; ///< Batch dimensions.
375 mutable_array_ref<value_type> feature; ///< Feature maps.
376 mutable_array_ref<value_type> spatial; ///< Spatial dimensions.
377 mutable_array_ref<value_type> local; ///< Local dimensions.
380 value_type _sizes[CLDNN_TENSOR_DIM_MAX];
381 value_type _dimOffset;
385 explicit tensor(value_type default_size = 0) :
386 raw(_sizes, CLDNN_TENSOR_DIM_MAX),
387 batch(_sizes, CLDNN_TENSOR_BATCH_DIM_MAX),
388 feature(_sizes + CLDNN_TENSOR_BATCH_DIM_MAX, CLDNN_TENSOR_FEATURE_DIM_MAX),
389 spatial(_sizes + CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX, CLDNN_TENSOR_SPATIAL_DIM_MAX),
390 local(_sizes + CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + CLDNN_TENSOR_SPATIAL_DIM_MAX, CLDNN_TENSOR_LOCAL_DIM_MAX) {
391 std::fill_n(_sizes, CLDNN_TENSOR_DIM_MAX, default_size);
394 /// @brief Constructs tensor.
395 /// @param[in] kind_inits Dimensions defined using dim_vec_kind. If dimension is not provided it is set to 1.
396 /// @details Example:
399 tensor my_tensor(batch(2), spatial(5, 6)); // y=6, x=5, b=2, f - not set
400 cout << my_tensor.batch[0] << endl; // 2
401 cout << my_tensor.feature[0] << endl; // 1 - default_size
402 cout << "x=" << my_tensor.spatial[0] << endl; // x=5
403 cout << "y=" << my_tensor.spatial[1] << endl; // y=6
407 template <typename... KindInitTys,
408 typename = typename std::enable_if<
410 meta::is_any_of<KindInitTys,
411 cldnn::details::dim_vec_kind_init<cldnn::details::dim_vec_kind::batch>,
412 cldnn::details::dim_vec_kind_init<cldnn::details::dim_vec_kind::feature>,
413 cldnn::details::dim_vec_kind_init<details::dim_vec_kind::spatial>>::value...>::value,
415 explicit tensor(KindInitTys&&... kind_inits)
417 assign_inits(std::forward<KindInitTys>(kind_inits)...);
420 /// @brief Constructs @p tensor.
421 /// @details Example:
424 tensor my_tensor( 2, 3, 4, 5 ); // b=2, f=3, x=4, y=5
425 cout << my_tensor.batch[0] << endl; // 2
426 cout << my_tensor.feature[0] << endl; // 3
427 cout << "x=" << my_tensor.spatial[0] << endl; // x=4
428 cout << "y=" << my_tensor.spatial[1] << endl; // y=5
432 tensor(value_type batch_num, value_type feature_num, value_type width, value_type height)
434 _sizes[0] = batch_num;
435 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX] = feature_num;
436 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX] = width;
437 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + 1] = height;
438 if (batch_num == 0 && feature_num == 0 && width == 0 && height == 0)
439 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + 2] = 0;
442 /// @brief Constructs @p tensor.
443 /// @details Example:
446 tensor my_tensor( 2, 3, 4, 5, 6 ); // b=2, f=3, x=4, y=5, z =6
447 cout << my_tensor.batch[0] << endl; // 2
448 cout << my_tensor.feature[0] << endl; // 3
449 cout << "x=" << my_tensor.spatial[0] << endl; // x=4
450 cout << "y=" << my_tensor.spatial[1] << endl; // y=5
451 cout << "z=" << my_tensor.spatial[2] << endl; // z=6
455 tensor(value_type batch_num, value_type feature_num, value_type width, value_type height, value_type depth)
457 _sizes[0] = batch_num;
458 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX] = feature_num;
459 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX] = width;
460 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + 1] = height;
461 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + 2] = depth;
464 /// @brief Constructs @p tensor.
465 /// @details Example:
468 tensor my_tensor( 2, 3, 4, 5, 6, 7 ); // b=2, f=3, x=4, y=5, lx= 6, ly =7
469 cout << my_tensor.batch[0] << endl; // 2
470 cout << my_tensor.feature[0] << endl; // 3
471 cout << "x=" << my_tensor.spatial[0] << endl; // x=4
472 cout << "y=" << my_tensor.spatial[1] << endl; // y=5
473 cout << "local x=" << my_tensor.local[0] << endl; // local x=6
474 cout << "loxal y=" << my_tensor.local[1] << endl; // local y=7
478 tensor(value_type batch_num, value_type feature_num, value_type width,
479 value_type height, value_type local_x, value_type local_y)
481 _sizes[0] = batch_num;
482 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX] = feature_num;
483 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX] = width;
484 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + 1] = height;
485 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + CLDNN_TENSOR_SPATIAL_DIM_MAX] = local_x;
486 _sizes[CLDNN_TENSOR_BATCH_DIM_MAX + CLDNN_TENSOR_FEATURE_DIM_MAX + CLDNN_TENSOR_SPATIAL_DIM_MAX + 1] = local_y;
489 /// @brief Constructs @p tensor using vector of sizes.
490 /// @param[in] sizes dimensions need to be provided in the following order {batch, feature, spatial_x, spatial_y [, spatial_z] }.
491 /// @param[in] default_size default_size for tensor dimensions.
492 /// @details Example:
495 tensor my_tensor = { 2, 3, 4, 5 }; // b=2, f=3, x=4, y=5
496 cout << my_tensor.batch[0] << endl; // 2
497 cout << my_tensor.feature[0] << endl; // 3
498 cout << "x=" << my_tensor.spatial[0] << endl; // x=4
499 cout << "y=" << my_tensor.spatial[1] << endl; // y=5
503 explicit tensor(const std::vector<value_type>& sizes, value_type default_size = 1)
504 : tensor(default_size) {
505 int max_size = std::min(static_cast<int>(sizes.size()), CLDNN_TENSOR_DIM_MAX);
506 for (int i = 0; i < max_size; i++)
507 _sizes[i] = sizes[i];
510 tensor(format fmt, const std::vector<value_type>& sizes, value_type default_size = 1)
511 : tensor(default_size) {
512 auto in_order = fmt.order();
513 auto out_order = fmt.internal_order();
514 if (in_order.size() != sizes.size())
515 throw std::invalid_argument("The count of values passed to initialize tensor does not match passed format.");
517 for (size_t out_idx = 0; out_idx < out_order.size(); ++out_idx) {
518 auto channel = out_order[out_idx];
522 auto in_idx = in_order.find(channel);
523 if (in_idx == in_order.npos)
524 throw std::runtime_error("Internal order of a format contains channel which does not appear in external order.");
526 _sizes[out_idx] = sizes[in_idx];
530 /// @brief Implicit conversion form C API :: cldnn_tensor.
531 explicit tensor(const cldnn_tensor& other)
533 std::copy_n(other.sizes, CLDNN_TENSOR_DIM_MAX, _sizes);
536 /// @brief Implicit conversion to C API ::cldnn_tensor.
537 operator cldnn_tensor() const {
539 result.batch_num = batch.size();
540 result.feature_num = feature.size();
541 result.spatial_num = spatial.size();
542 result.local_num = local.size();
543 std::copy_n(_sizes, CLDNN_TENSOR_DIM_MAX, result.sizes);
547 /// @brief Copy construction.
548 tensor(const tensor& other)
550 std::copy_n(other._sizes, CLDNN_TENSOR_DIM_MAX, _sizes);
553 /// @brief Copy assignment.
554 tensor& operator=(const tensor& other) {
557 std::copy_n(other._sizes, CLDNN_TENSOR_DIM_MAX, _sizes);
561 friend bool operator==(const tensor& lhs, const tensor& rhs) {
562 return lhs.raw.size() == rhs.raw.size() && std::equal(lhs.raw.begin(), lhs.raw.end(), rhs.raw.begin());
565 friend bool operator!=(const tensor& lhs, const tensor& rhs) {
566 return !(lhs == rhs);
569 friend bool operator<(const tensor& lhs, const tensor& rhs) {
570 if (lhs.raw.size() != rhs.raw.size())
571 return lhs.raw.size() < rhs.raw.size();
572 for (size_t i = 0; i < lhs.raw.size(); ++i) {
573 if (lhs.raw[i] < rhs.raw[i])
575 if (rhs.raw[i] < lhs.raw[i])
582 friend std::ostream& operator<<(std::ostream& os, const tensor& tensor) {
583 os << tensor.to_string();
587 std::string to_string() const {
588 std::stringstream out;
589 const char* delim = "";
592 for (size_t i = 0; i < batch.size(); ++i) {
593 out << delim << batch[i];
599 for (size_t i = 0; i < feature.size(); ++i) {
600 out << delim << feature[i];
604 std::vector<std::string> spatial_dim_names = {", x", ", y", ", z", ", w"};
605 for (size_t i = 0; i < spatial.size(); ++i) {
606 out << spatial_dim_names[i] << ":" << spatial[i];
613 /// @brief Returns a tensor with all negated elements.
614 tensor negate() const {
616 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; i++) {
617 result._sizes[i] = -_sizes[i];
622 /// @brief Returns a tensor with all elements multilied to @p multiplier.
623 tensor mul(value_type multiplier) const {
625 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; i++) {
626 result._sizes[i] *= multiplier;
631 /// @brief Returns a tensor with all elements divided by @p divider.
632 tensor div(value_type divider) const {
634 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; i++) {
635 result._sizes[i] /= divider;
640 /// @brief Returns a tensor with all elements added by appropriate elements of @p rhs
641 tensor add(const tensor& rhs) const {
643 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; i++) {
644 result._sizes[i] += rhs._sizes[i];
649 /// @brief Returns a tensor with all elements subtracted by appropriate elements of @p rhs
650 tensor sub(const tensor& rhs) const {
651 return add(rhs.negate());
654 /// @brief Assign and add
655 tensor& operator+=(const tensor& rhs) {
656 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; i++)
657 _sizes[i] += rhs._sizes[i];
661 /// @brief Assign and subtract
662 tensor& operator-=(const tensor& rhs) {
663 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; i++)
664 _sizes[i] -= rhs._sizes[i];
668 /// @brief Returns a vector of tensors values, ordered regarding to @p format.
669 std::vector<value_type> sizes(cldnn::format fmt) const {
670 auto output_order = fmt.order();
671 auto internal_order = fmt.internal_order();
672 std::vector<value_type> sizes(output_order.size(), 0);
674 for (size_t i = 0; i < sizes.size(); ++i) {
675 auto c = output_order[i];
676 auto pos = internal_order.find(c);
677 if (pos == internal_order.npos)
678 throw std::domain_error(std::string("Unknown coord type: ") + c);
680 sizes[i] = _sizes[pos];
686 /// @brief Returns a vector of tensors values, ordered batch, feature, spatial_x, spatial_y.
687 std::vector<value_type> sizes() const {
688 std::vector<value_type> sizes(sizeof(_sizes) / sizeof(_sizes[0]), 0);
689 for (size_t i = 0; i < sizes.size(); ++i)
690 sizes[i] = _sizes[i];
694 /// @brief Returns tensor elements count calculated as multiplication of all elements.
695 size_t count() const {
696 return std::accumulate(
699 static_cast<size_t>(1),
700 std::multiplies<size_t>());
703 /// @brief Returns new tensor based on current but transformed to new @p format.
704 /// @param[in] new_fmt Format of new tensor.
705 /// @param[in] default_size Default element values for positions not defined by current format.
706 /// @details Example:
709 tensor my_tensor({ 2, 3, 4, 5 });
710 auto my_sizes = my_tensor.sizes();
711 cout << "dims_num=" << my_sizes.size() << endl; // dims_num=2
712 cout << "b=" << my_sizes[0] << endl; // b=2
713 cout << "f=" << my_sizes[1] << endl; // f=3
714 cout << "x=" << my_sizes[2] << endl; // x=5
715 cout << "y=" << my_sizes[3] << endl; // y=4
716 auto new_tensor = my_tensor.transform(format::yxfb, 10);
717 auto new_sizes = new_tensor.sizes();
718 cout << "new_num=" << new_sizes.size() << endl; // new_num=4
719 for(auto dim : new_sizes) cout << " " << dim; // 5 4 3 2
723 tensor transform(cldnn::format new_fmt, value_type default_size) const {
724 cldnn::format format = cldnn::format::bfwzyx;
725 auto val_order = format.internal_order();
726 auto new_order = new_fmt.internal_order();
727 std::vector<value_type> old_sizes = sizes();
728 std::vector<value_type> new_sizes(old_sizes.size(), default_size);
732 for (size_t i = 0; i < format.order().size(); i++) {
733 auto c = val_order[i];
734 // skip f and y, z for the formats that do not have it
735 if (((new_fmt == format::bs_xs_xsv8_bsv8) ||
736 (new_fmt == format::bs_xs_xsv8_bsv16) ||
737 (new_fmt == format::bs_x_bsv16)) &&
742 if (new_order[i] == '?')
743 new_sizes[i] = default_size;
749 // skip z for the formats that do not have it
750 if (((new_fmt != format::bfzyx && new_fmt != format::bfwzyx)) && (c == 'z')) {
751 if (new_order[i] == '?')
752 new_sizes[i] = default_size;
754 tmp_z *= old_sizes[i];
758 if (new_fmt != format::bfwzyx && c == 'w') {
759 if (new_order[i] == '?')
760 new_sizes[i] = default_size;
762 if (new_fmt == format::bfzyx)
763 tmp_w *= old_sizes[i];
765 tmp_z *= old_sizes[i];
769 auto new_pos = new_order.find(c);
770 if (new_pos == std::string::npos)
771 throw std::invalid_argument("cannot convert to new format");
772 new_sizes[new_pos] = old_sizes[i];
775 // in case of formats with smaller number of dimensions than input, flatten is performed below
776 if (tmp != 1 || tmp_z != 1 || tmp_w != 1) {
777 for (size_t i = 0; i < format.order().size(); i++) {
778 auto c = val_order[i];
780 auto new_pos = new_order.find(c);
781 new_sizes[new_pos] *= tmp;
784 auto new_pos = new_order.find(c);
785 if (new_pos != std::string::npos)
786 new_sizes[new_pos] *= tmp_z;
789 auto new_pos = new_order.find(c);
790 if (new_pos != std::string::npos)
791 new_sizes[new_pos] *= tmp_w;
796 tensor sizes { new_sizes };
800 /// @brief Calculates linear offset for given @p coord within current tensor.
801 /// @param coord The coordinate within current tensor.
802 size_t get_linear_offset(const tensor& coord, cldnn::format fmt) const {
803 auto my_sizes = this->sizes(fmt);
804 auto adjusted_coords = coord.sizes(fmt);
806 // Extend N-dimensional format with B blocked dimensions to (N+B) sizes
807 for (const auto& block : fmt.block_sizes()) {
808 auto block_axis = block.first;
809 auto block_size = block.second;
810 auto external_axis = fmt.internal_to_external(block_axis);
812 my_sizes.push_back(block_size);
813 my_sizes[external_axis] = ceil_div(my_sizes[external_axis], block_size);
815 adjusted_coords.push_back(adjusted_coords[external_axis] % block_size);
816 adjusted_coords[external_axis] /= block_size;
819 if (fmt == cldnn::format::byxf_af32 && !(is_aligned_to(my_sizes[3], 32))) {
820 my_sizes[3] = align_to(my_sizes[3], 32);
821 } else if (fmt == cldnn::format::byx8_f4 && (!(is_aligned_to(my_sizes[3], 4)) || !(is_aligned_to(my_sizes[2], 8)))) {
822 my_sizes[3] = align_to(my_sizes[3], 4);
823 my_sizes[2] = align_to(my_sizes[2], 8);
824 } else if (fmt == cldnn::format::bf8_xy16) {
825 // Special case of blocked format, where xy is treated as one flattened dimension
826 auto flat_xy = adjusted_coords[3] + adjusted_coords[2] * my_sizes[3];
828 my_sizes.push_back(16);
829 my_sizes[3] = ceil_div(my_sizes[2] * my_sizes[3], 16);
832 adjusted_coords.push_back(flat_xy % 16);
833 adjusted_coords[3] = flat_xy / 16;
834 adjusted_coords[2] = 0;
835 } else if (fmt == cldnn::format::os_is_yx_isa8_osv8_isv4 && // TODO Fix offsets calculation for formats below
836 !(is_aligned_to(my_sizes[0], 8)) &&
837 !(is_aligned_to(my_sizes[1], 32))) {
838 my_sizes[0] = align_to(my_sizes[0], 8);
839 my_sizes[1] = align_to(my_sizes[1], 32);
840 adjusted_coords[0] = align_to(adjusted_coords[0], 8);
841 adjusted_coords[1] = align_to(adjusted_coords[1], 32);
842 } else if (fmt == cldnn::format::os_is_yx_isa8_osv8_isv4_swizzled_by_4 && !(is_aligned_to(my_sizes[0], 32)) && !(is_aligned_to(my_sizes[1], 32))) {
843 my_sizes[0] = align_to(my_sizes[0], 32);
844 my_sizes[1] = align_to(my_sizes[1], 32);
845 adjusted_coords[0] = align_to(adjusted_coords[0], 32);
846 adjusted_coords[1] = align_to(adjusted_coords[1], 32);
847 } else if (fmt == cldnn::format::is_o32_yx_isv32_swizzled_by_4 && (!is_aligned_to(my_sizes[1], 32) || !is_aligned_to(my_sizes[0], 32))) {
848 my_sizes[0] = align_to(my_sizes[0], 32);
849 my_sizes[3] = align_to(my_sizes[3], 32);
850 adjusted_coords[0] = align_to(adjusted_coords[0], 32);
851 adjusted_coords[3] = align_to(adjusted_coords[3], 32);
852 } else if (fmt == cldnn::format::os_is_y_x8_osv8_isv4 || fmt == cldnn::format::os_is_yx_isa8_osv8_isv4_swizzled_by_4) {
853 my_sizes[1] = align_to(my_sizes[1], 4);
854 my_sizes[0] = align_to(my_sizes[0], 8);
855 my_sizes[2] = align_to(my_sizes[2], 8);
858 assert(my_sizes.size() == adjusted_coords.size());
859 assert(adjusted_coords.size() > 0);
861 size_t offset = adjusted_coords[0];
862 for (size_t i = 1; i < adjusted_coords.size(); i++) {
863 offset = offset * my_sizes[i] + adjusted_coords[i];
868 /// @brief Returns a tensor containing values maximum from @p lhs and @p rhs.
869 static tensor max(tensor const& lhs, tensor const& rhs) {
871 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; ++i)
872 ret._sizes[i] = std::max(ret._sizes[i], rhs._sizes[i]);
877 /// @brief Returns a tensor containing values minimum from @p lhs and @p rhs.
878 static tensor min(tensor const& lhs, tensor const& rhs) {
880 for (size_t i = 0; i < CLDNN_TENSOR_DIM_MAX; ++i)
881 ret._sizes[i] = std::min(ret._sizes[i], rhs._sizes[i]);
887 /// @brief Helper functions for tensor constructor using dim_vec_kinds
888 template <typename KindInitT>
889 void assign_inits(KindInitT&& init) {
890 init.init_tensor_values(*this);
893 template <typename KindInitT, typename... KindInitTys>
894 void assign_inits(KindInitT&& init, KindInitTys&&... kind_inits) {
895 init.init_tensor_values(*this);
896 assign_inits(std::forward<KindInitTys>(kind_inits)...);
900 #define TensorValue(val) static_cast<cldnn::tensor::value_type>(val)
902 template <details::dim_vec_kind Kind>
903 inline void details::dim_vec_kind_init<Kind>::init_tensor_values(cldnn::tensor& t) {
904 for (size_t i = _dimOffset; i < (size_t)(_dimOffset + _dimSize); i++)
905 t._sizes[i] = _sizes[i - _dimOffset];
908 /// @brief Adds two @p tensors
909 inline tensor operator+(const tensor& lhs, const tensor& rhs) { return lhs.add(rhs); }
910 /// @brief Subtracts two @p tensors
911 inline tensor operator-(const tensor& lhs, const tensor& rhs) { return lhs.sub(rhs); }
912 /// @brief Multiplies a @p tensor to a @p scalar
913 inline tensor operator*(const tensor& lhs, tensor::value_type rhs) { return lhs.mul(rhs); }
914 /// @brief Divides a @p tensor by a @p scalar
915 inline tensor operator/(const tensor& lhs, tensor::value_type rhs) { return lhs.div(rhs); }
918 /// \brief Converts C API tensor_array to std::vector<tensor>
920 inline std::vector<tensor> tensor_arr_to_vector(const cldnn_tensor_arr& arr) {
921 std::vector<tensor> result(arr.size);
922 for (size_t i = 0; i < arr.size; i++)
923 result[i] = (tensor) arr.data[i];
929 /// \brief Converts std::vector<tensor> to std::vector of C API tensor
931 inline std::vector<cldnn_tensor> tensor_vector_to_cldnn_vector(const std::vector<tensor>& stor) {
932 return std::vector<cldnn_tensor>(stor.begin(), stor.end());