tvm
Loading...
Searching...
No Matches
pooling.h
Go to the documentation of this file.
1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. 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,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
24#ifndef TVM_TOPI_NN_POOLING_H_
25#define TVM_TOPI_NN_POOLING_H_
26
27#include <tvm/s_tir/stmt.h>
28#include <tvm/sym/analyzer.h>
30#include <tvm/topi/nn.h>
31#include <tvm/topi/reduction.h>
32#include <tvm/topi/tags.h>
33
34#include <algorithm>
35#include <string>
36#include <vector>
37
38namespace tvm {
39namespace topi {
40namespace nn {
41
42using namespace tvm::te;
43
45enum PoolType : int {
48};
49
50inline Tensor pool_grad_impl(const Tensor& out_grad, const Tensor& x,
51 const ffi::Array<PrimExpr>& kernel_size,
52 const ffi::Array<PrimExpr>& stride_size,
53 const ffi::Array<PrimExpr>& padding_size, PoolType pool_type,
54 bool ceil_mode, const size_t height_axis, const size_t width_axis,
55 bool count_include_pad) {
56 using namespace tvm::prim;
57 TVM_FFI_ICHECK(out_grad->shape.size() >= 2) << "Pooling grad output must >= 2-D (H, W)";
58 TVM_FFI_ICHECK(x->shape.size() >= 2) << "Pooling input must >= 2-D (H, W)";
59 TVM_FFI_ICHECK_EQ(kernel_size.size(), 2) << "Pooling kernel_size must have 2 elements";
60 TVM_FFI_ICHECK_EQ(stride_size.size(), 2) << "Pooling stride_size must have 2 elements";
61 TVM_FFI_ICHECK_EQ(padding_size.size(), 4) << "Pooling padding_size must have 4 elements";
62
63 auto kernel_height = kernel_size[0];
64 auto kernel_width = kernel_size[1];
65 auto stride_height = stride_size[0];
66 auto stride_width = stride_size[1];
67
68 auto height = x->shape[height_axis];
69 auto width = x->shape[width_axis];
70
71 auto pad_top = padding_size[0];
72 auto pad_left = padding_size[1];
73 auto pad_bottom = padding_size[2];
74 auto pad_right = padding_size[3];
75
76 if (ceil_mode) {
77 // Additional padding to ensure we do ceil instead of floor when
78 // dividing by stride.
79 pad_bottom += stride_height - 1;
80 pad_right += stride_width - 1;
81 }
82
83 ffi::Array<PrimExpr> pad_before(std::vector<PrimExpr>(x->shape.size(), 0));
84 pad_before.Set(height_axis, pad_top);
85 pad_before.Set(width_axis, pad_left);
86
87 ffi::Array<PrimExpr> pad_after(std::vector<PrimExpr>(x->shape.size(), 0));
88 pad_after.Set(height_axis, pad_bottom);
89 pad_after.Set(width_axis, pad_right);
90 sym::Analyzer analyzer;
91 auto out_height =
92 analyzer->Simplify((height - kernel_height + pad_top + pad_bottom) / stride_height + 1);
93 auto out_width =
94 analyzer->Simplify((width - kernel_width + pad_left + pad_right) / stride_width + 1);
95
96 auto dheight = tvm::te::reduce_axis(Range(0, kernel_height), "dh");
97 auto dwidth = tvm::te::reduce_axis(Range(0, kernel_width), "dw");
98
99 ffi::Array<PrimExpr> data_shape = x->shape;
100 ffi::Array<PrimExpr> out_shape = data_shape;
101 out_shape.Set(height_axis, out_height);
102 out_shape.Set(width_axis, out_width);
103
104 const auto* padding_h0 = pad_top.as<IntImmNode>();
105 const auto* padding_w0 = pad_left.as<IntImmNode>();
106 const auto* padding_h1 = pad_bottom.as<IntImmNode>();
107 const auto* padding_w1 = pad_right.as<IntImmNode>();
108 const bool do_pad =
109 ((padding_h0 && padding_h0->value != 0) || (padding_w0 && padding_w0->value != 0)) ||
110 ((padding_h1 && padding_h1->value != 0) || (padding_w1 && padding_w1->value != 0));
111
112 if (pool_type == kMaxPool) {
113 ffi::Array<PrimExpr> ravel_shape{data_shape.begin(), data_shape.end()};
114 ravel_shape.Set(height_axis, ravel_shape[height_axis] + pad_top + pad_bottom);
115 ravel_shape.Set(width_axis, ravel_shape[width_axis] + pad_left + pad_right);
116
117 auto windowh =
118 tvm::te::reduce_axis(Range(0, (kernel_height + stride_height - 1) / stride_height), "wh");
119 auto windoww =
120 tvm::te::reduce_axis(Range(0, (kernel_width + stride_width - 1) / stride_width), "ww");
121
122 auto argmax = MakeArgmaxReducer();
123 auto pad_x =
124 do_pad ? pad(x, pad_before, pad_after, tvm::prim::min_value(PrimType(x->dtype)), "pad_temp")
125 : x;
126
127 auto mp_argmax = tvm::te::compute(
128 out_shape,
129 [&](const ffi::Array<PrimVar>& inds) {
130 ffi::Array<PrimExpr> window_inds =
131 inds.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
132 window_inds.Set(height_axis, inds[height_axis] * stride_height + dheight);
133 window_inds.Set(width_axis, inds[width_axis] * stride_width + dwidth);
134 auto idx = detail::RavelIndex(window_inds, ravel_shape);
135 return argmax({idx, pad_x(window_inds)}, {dheight, dwidth}, nullptr);
136 },
137 "maxpool_grad_argmax", kCommReduceIdx);
138
139 auto mp_inds = mp_argmax[0];
140
141 return tvm::te::compute(
142 data_shape,
143 [&](const ffi::Array<PrimVar>& inds) {
144 ffi::Array<PrimExpr> pad_inds =
145 inds.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
146 pad_inds.Set(height_axis, pad_inds[height_axis] + pad_top);
147 pad_inds.Set(width_axis, pad_inds[width_axis] + pad_left);
148 auto idx = detail::RavelIndex(pad_inds, ravel_shape);
149
150 ffi::Array<PrimExpr> out_idx =
151 inds.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
152 out_idx.Set(height_axis, (inds[height_axis] + pad_top) / stride_height - windowh);
153 out_idx.Set(width_axis, (inds[width_axis] + pad_left) / stride_width - windoww);
154
155 PrimExpr out_idx_lower_h = prim::Select(
156 pad_inds[height_axis] < kernel_height, IntImm(pad_inds[height_axis].ty(), 0),
157 (pad_inds[height_axis] - kernel_height) / stride_height + 1);
158 PrimExpr out_idx_lower_w = prim::Select(
159 pad_inds[width_axis] < kernel_width, IntImm(pad_inds[width_axis].ty(), 0),
160 (pad_inds[width_axis] - kernel_width) / stride_width + 1);
161
162 return tvm::prim::sum(
163 tvm::if_then_else(prim::And(prim::And(out_idx[height_axis] >= out_idx_lower_h,
164 out_idx[width_axis] >= out_idx_lower_w),
165 mp_inds(out_idx) == idx),
166 out_grad(out_idx), MakeConst(PrimType(x->dtype), 0)),
167 {windowh, windoww});
168 },
169 "T_pool_grad", "pool_grad_max");
170 } else if (pool_type == kAvgPool) {
171 auto windowh =
172 tvm::te::reduce_axis(Range(0, (kernel_height + stride_height - 1) / stride_height), "wh");
173 auto windoww =
174 tvm::te::reduce_axis(Range(0, (kernel_width + stride_width - 1) / stride_width), "ww");
175 return tvm::te::compute(
176 data_shape,
177 [&](const ffi::Array<PrimVar>& inds) {
178 PrimExpr pad_h_idx = inds[height_axis] + pad_top;
179 PrimExpr pad_w_idx = inds[width_axis] + pad_left;
180
181 // output indices whose pooling windows cover current input element (can be out-of-bound)
182 ffi::Array<PrimExpr> out_idx =
183 inds.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
184 out_idx.Set(height_axis, (pad_h_idx / stride_height - windowh));
185 out_idx.Set(width_axis, (pad_w_idx / stride_width - windoww));
186
187 PrimExpr out_idx_lower_h =
188 prim::Select(pad_h_idx < kernel_height, IntImm(pad_h_idx.ty(), 0),
189 (pad_h_idx - kernel_height) / stride_height + 1);
190 PrimExpr out_idx_lower_w =
191 prim::Select(pad_w_idx < kernel_width, IntImm(pad_w_idx.ty(), 0),
192 (pad_w_idx - kernel_width) / stride_width + 1);
193
194 PrimExpr divide_factor = kernel_height * kernel_width; // number of pooled elements
195 if (!count_include_pad) {
196 PrimExpr h_start = out_idx[height_axis] * stride_height - pad_top;
197 PrimExpr w_start = out_idx[width_axis] * stride_width - pad_left;
198
199 PrimExpr h_end = min(h_start + kernel_height, height);
200 PrimExpr w_end = min(w_start + kernel_width, width);
201 h_start = max(h_start, IntImm(h_start.ty(), 0));
202 w_start = max(w_start, IntImm(w_start.ty(), 0));
203 divide_factor = max((h_end - h_start) * (w_end - w_start), IntImm(h_end.ty(), 1));
204 }
205 return tvm::prim::sum(
206 tvm::if_then_else(prim::And(prim::And(out_idx[height_axis] >= out_idx_lower_h,
207 out_idx[height_axis] < out_height),
208 prim::And(out_idx[width_axis] >= out_idx_lower_w,
209 out_idx[width_axis] < out_width)),
210 out_grad(out_idx) / divide_factor,
211 MakeConst(PrimType(out_grad->dtype), 0)),
212 {windowh, windoww});
213 },
214 "T_pool_grad", "pool_grad_avg");
215 } else {
216 TVM_FFI_THROW(ValueError) << "Unrecognized pool_type: " << pool_type;
217 }
218}
219
231inline bool find_depth_height_width(const std::string& layout, int* depth_axis, int* height_axis,
232 int* width_axis) {
233 if (depth_axis) *depth_axis = -1;
234 if (height_axis) *height_axis = -1;
235 if (width_axis) *width_axis = -1;
236 int curr_idx = 0;
237 for (size_t i = 0; i < layout.size(); ++i) {
238 if ((layout[i] >= 'A' && layout[i] <= 'Z') || (layout[i] >= 'a' && layout[i] <= 'z')) {
239 if (layout[i] == 'D' && depth_axis) {
240 if (*depth_axis != -1) return false;
241 *depth_axis = curr_idx;
242 } else if (layout[i] == 'H' && height_axis) {
243 if (*height_axis != -1) return false;
244 *height_axis = curr_idx;
245 } else if (layout[i] == 'W' && width_axis) {
246 if (*width_axis != -1) return false;
247 *width_axis = curr_idx;
248 } else if (layout[i] == 'd' || layout[i] == 'h' || layout[i] == 'w') {
249 // do not support split on height, width or depth, e.g., NCHW16w
250 return false;
251 }
252 ++curr_idx;
253 }
254 }
255 if ((depth_axis && *depth_axis == -1) || (height_axis && *height_axis == -1) ||
256 (width_axis && *width_axis == -1))
257 return false;
258 return true;
259}
260
261inline bool find_height_width(const std::string& layout, int* height_axis, int* width_axis) {
262 return find_depth_height_width(layout, /*depth_axis=*/nullptr, height_axis, width_axis);
263}
264
265inline bool find_width(const std::string& layout, int* width_axis) {
266 return find_depth_height_width(layout, /*depth_axis=*/nullptr, /*height_axis=*/nullptr,
267 width_axis);
268}
269
300inline Tensor pool_grad(const Tensor& out_grad, const Tensor& x,
301 const ffi::Array<PrimExpr>& kernel_size,
302 const ffi::Array<PrimExpr>& stride_size,
303 const ffi::Array<PrimExpr>& padding_size, PoolType pool_type,
304 bool ceil_mode, const std::string& layout = "NCHW",
305 bool count_include_pad = true) {
306 int height_axis = -1, width_axis = -1;
307 TVM_FFI_ICHECK(find_height_width(layout, &height_axis, &width_axis))
308 << "Unsupported layout " << layout;
309 return pool_grad_impl(out_grad, x, kernel_size, stride_size, padding_size, pool_type, ceil_mode,
310 height_axis, width_axis, count_include_pad);
311}
312
313inline PrimExpr start_index(const PrimVar& out_index, const PrimExpr& odim, const PrimExpr& idim) {
314 return indexdiv(out_index * idim, odim);
315}
316
317inline PrimExpr end_index(const PrimVar& out_index, const PrimExpr& odim, const PrimExpr& idim) {
318 PrimExpr tmp = indexdiv((out_index + 1) * idim, odim);
319 return tvm::prim::Select(indexmod((out_index + 1) * idim, odim) == 0, tmp, tmp + 1);
320}
321
332inline Tensor adaptive_pool_impl(const Tensor& x, const ffi::Array<PrimExpr>& output_size,
333 PoolType pool_type, const std::vector<int>& axes) {
334 const auto n_dim = output_size.size();
335 TVM_FFI_ICHECK_EQ(axes.size(), n_dim) << "The number of axes not equal to the in/out dimension";
336
337 ffi::Array<PrimExpr> data_shape = x->shape;
338 ffi::Array<PrimExpr> out_shape = data_shape;
339 ffi::Array<PrimExpr> in_size, out_size;
340 for (size_t i = 0; i < n_dim; ++i) {
341 in_size.push_back(data_shape[axes[i]]);
342 out_size.push_back(output_size[i]);
343 out_shape.Set(axes[i], out_size[i]);
344 }
345
346 auto get_iter_vars = [=](const ffi::Array<PrimVar>& output, bool reduce_indices) {
347 ffi::Array<PrimExpr> indices;
348 for (size_t i = 0; i < output.size(); ++i) indices.push_back(output[i]);
349 ffi::Array<s_tir::IterVar> reduce_axes;
350 for (size_t i = 0; i < n_dim; ++i) {
351 auto i_start = start_index(output[axes[i]], out_size[i], in_size[i]);
352 auto i_end = end_index(output[axes[i]], out_size[i], in_size[i]);
353 auto rv_name = "rv" + std::to_string(i);
354 auto rv_axis = tvm::te::reduce_axis(Range(0, i_end - i_start), rv_name);
355 reduce_axes.push_back(rv_axis);
356 if (reduce_indices) {
357 indices.Set(axes[i], i_start + rv_axis);
358 }
359 }
360 return std::make_tuple(indices, reduce_axes);
361 };
362
363 ffi::Map<ffi::String, ffi::Any> attrs;
364 if (pool_type == kMaxPool) {
365 attrs.Set(tvm::s_tir::attr::kScheduleRule, tvm::ffi::String("meta_schedule.adaptive_pool_max"));
366 return tvm::te::compute(
367 out_shape,
368 [&](const ffi::Array<PrimVar>& output) {
369 ffi::Array<PrimExpr> indices;
370 ffi::Array<s_tir::IterVar> reduce_axes;
371 std::tie(indices, reduce_axes) = get_iter_vars(output, true);
372 return tvm::max(x(indices), reduce_axes); // NOLINT(*)
373 },
374 "adaptive_pool_max", "adaptive_pool_max", attrs);
375 } else if (pool_type == kAvgPool) {
376 attrs.Set(tvm::s_tir::attr::kScheduleRule, tvm::ffi::String("meta_schedule.adaptive_pool_avg"));
377 auto pool_sum = tvm::te::compute(
378 out_shape,
379 [&](const ffi::Array<PrimVar>& output) {
380 ffi::Array<PrimExpr> indices;
381 ffi::Array<s_tir::IterVar> reduce_axes;
382 std::tie(indices, reduce_axes) = get_iter_vars(output, true);
383 return tvm::prim::sum(x(indices), reduce_axes);
384 },
385 "adaptive_pool_sum", "adaptive_pool_sum");
386
387 return tvm::te::compute(
388 out_shape,
389 [&](const ffi::Array<PrimVar>& output) {
390 ffi::Array<PrimExpr> indices;
391 ffi::Array<s_tir::IterVar> reduce_axes;
392 std::tie(indices, reduce_axes) = get_iter_vars(output, false);
393
394 PrimExpr divide_factor = tvm::prim::cast(PrimType(x->dtype), 1);
395 for (size_t i = 0; i < n_dim; ++i) {
396 divide_factor *=
397 tvm::prim::cast(PrimType::Int(32), reduce_axes[i]->dom.value()->extent);
398 }
399
400 return div(pool_sum(indices), divide_factor);
401 },
402 "adaptive_pool_avg", kElementWise, attrs);
403 } else {
404 LOG(ERROR) << "Unrecognized pool_type: " << pool_type;
405 return x;
406 }
407}
408
435inline Tensor adaptive_pool(const Tensor& x, const ffi::Array<PrimExpr>& output_size,
436 PoolType pool_type, const std::string& layout = "NCHW") {
437 int height_axis = -1, width_axis = -1;
438 TVM_FFI_ICHECK(find_height_width(layout, &height_axis, &width_axis))
439 << "Unsupported layout " << layout;
440 return adaptive_pool_impl(x, output_size, pool_type, {height_axis, width_axis});
441}
442
451inline Tensor adaptive_pool3d(const Tensor& x, const ffi::Array<PrimExpr>& output_size,
452 PoolType pool_type, const std::string& layout = "NCDHW") {
453 int depth_axis = -1, height_axis = -1, width_axis = -1;
454 TVM_FFI_ICHECK(find_depth_height_width(layout, &depth_axis, &height_axis, &width_axis))
455 << "Unsupported layout " << layout;
456 return adaptive_pool_impl(x, output_size, pool_type, {depth_axis, height_axis, width_axis});
457}
458
467inline Tensor adaptive_pool1d(const Tensor& x, const ffi::Array<PrimExpr>& output_size,
468 PoolType pool_type, const std::string& layout = "NCW") {
469 int width_axis = -1;
470 TVM_FFI_ICHECK(find_width(layout, &width_axis)) << "Unsupported layout " << layout;
471 return adaptive_pool_impl(x, output_size, pool_type, {width_axis});
472}
473
499inline Tensor global_pool(const Tensor& x, PoolType pool_type, const std::string& layout = "NCHW") {
500 return adaptive_pool(x, ffi::Array<PrimExpr>{1, 1}, pool_type, layout);
501}
502
519inline Tensor pool_impl_nd(const Tensor& x, const ffi::Array<PrimExpr>& kernel_size,
520 const ffi::Array<PrimExpr>& stride_size,
521 const ffi::Array<PrimExpr>& dilation_size,
522 const ffi::Array<PrimExpr>& padding_size, PoolType pool_type,
523 bool ceil_mode, const std::vector<int>& axis, bool count_include_pad) {
524 using namespace tvm::prim;
525 int k_size = kernel_size.size();
526 int x_size = x->shape.size();
527 TVM_FFI_ICHECK_EQ(stride_size.size(), k_size)
528 << "Pooling stride_size must have same elements as kernel";
529 TVM_FFI_ICHECK_EQ(padding_size.size(), k_size * 2)
530 << "Pooling padding_size must has double elements of"
531 " kernel";
532 TVM_FFI_ICHECK_EQ(axis.size(), k_size) << "axis must have same elements as kernel";
533
534 ffi::Array<IterVar> daxis;
535 std::vector<PrimExpr> kernel(kernel_size.begin(), kernel_size.end());
536 std::vector<PrimExpr> stride(stride_size.begin(), stride_size.end());
537 std::vector<PrimExpr> dilation(dilation_size.begin(), dilation_size.end());
538 std::vector<PrimExpr> pad_head(padding_size.begin(), padding_size.begin() + k_size);
539 std::vector<PrimExpr> pad_tail(padding_size.begin() + k_size, padding_size.end());
540 std::vector<PrimExpr> offset(k_size, 0);
541 ffi::Array<PrimExpr> pad_before(std::vector<PrimExpr>(x_size, 0));
542 ffi::Array<PrimExpr> pad_after(std::vector<PrimExpr>(x_size, 0));
543 ffi::Array<PrimExpr> data_shape = x->shape;
544 ffi::Array<PrimExpr> out_shape = data_shape;
545
546 bool do_pad = false;
547 for (int i = 0; i < k_size; i++) {
548 int ii = axis[i];
549
550 if (ceil_mode) {
551 // The offset[i] is an additional padding to ensure we do ceil instead of floor when
552 // dividing by stride.
553 // In the case of ceil_mode=True and count_include_pad=True,
554 // in order to obtain the correct boundary,
555 // we also need to use the offset[i] to eliminate this extra padding.
556 offset[i] = stride[i] - 1;
557 pad_tail[i] += offset[i];
558 }
559
560 const auto* padding0 = pad_head[i].as<IntImmNode>();
561 const auto* padding1 = pad_tail[i].as<IntImmNode>();
562 do_pad = do_pad || (padding0 && padding0->value != 0) || (padding1 && padding1->value != 0);
563
564 daxis.push_back(tvm::te::reduce_axis(Range(0, kernel[i]), "rv" + std::to_string(i)));
565
566 pad_before.Set(ii, pad_head[i]);
567 pad_after.Set(ii, pad_tail[i]);
568
569 sym::Analyzer analyzer;
570
571 PrimExpr numerator =
572 data_shape[ii] - (kernel[i] - 1) * dilation[i] - 1 + pad_head[i] + pad_tail[i];
573 auto raw_out = indexdiv(numerator, stride[i]) + 1;
574 if (ceil_mode) {
575 // In the case of ceil_mode=True, we need to check if the last pooling window is valid.
576 // If not, we skip the last window as it would start in the bottom padded region,
577 // we need to minus 1 to get the correct output shape.
578 auto invalid_last = (raw_out - 1) * stride[i] >= data_shape[ii] + pad_head[i];
579 auto out_dim = analyzer->Simplify(if_then_else(invalid_last, raw_out - 1, raw_out));
580 out_shape.Set(ii, out_dim);
581 } else {
582 auto out_dim = analyzer->Simplify(raw_out);
583 out_shape.Set(ii, out_dim);
584 }
585 }
586
587 ffi::Map<ffi::String, ffi::Any> attrs;
588 if (pool_type == kMaxPool) {
589 auto temp =
590 do_pad ? pad(x, pad_before, pad_after, tvm::prim::min_value(PrimType(x->dtype)), "pad_temp")
591 : x;
592 attrs.Set(tvm::s_tir::attr::kScheduleRule, tvm::ffi::String("meta_schedule.pool_max"));
593 return tvm::te::compute(
594 out_shape,
595 [&](const ffi::Array<PrimVar>& output) {
596 ffi::Array<PrimExpr> indices;
597 for (const PrimVar& var : output) indices.push_back(var);
598
599 for (int i = 0; i < k_size; i++) {
600 int ii = axis[i];
601 indices.Set(ii, output[ii] * stride[i] + daxis[i] * dilation[i]);
602 }
603 return tvm::max(temp(indices), daxis);
604 },
605 "pool_max", "pool_max", attrs);
606 } else if (pool_type == kAvgPool) {
607 attrs.Set(tvm::s_tir::attr::kScheduleRule, tvm::ffi::String("meta_schedule.pool_avg"));
608 // Pad the inputs
609 auto temp = do_pad ? pad(x, pad_before, pad_after, 0, "pad_temp") : x;
610
611 // TVM compute for summing the pooling window.
612 auto pool_sum = tvm::te::compute(
613 out_shape,
614 [&](const ffi::Array<PrimVar>& output) {
615 ffi::Array<PrimExpr> indices;
616 for (const PrimVar& var : output) indices.push_back(var);
617
618 for (int i = 0; i < k_size; i++) {
619 int ii = axis[i];
620 indices.Set(ii, output[ii] * stride[i] + daxis[i] * dilation[i]);
621 }
622 return tvm::prim::sum(temp(indices), daxis);
623 },
624 "pool_sum", "pool_sum");
625
626 // TVM compute for dividing the reduced window sum by kernel size.
627 return tvm::te::compute(
628 out_shape,
629 [&](const ffi::Array<PrimVar>& output) {
630 ffi::Array<PrimExpr> indices;
631 for (const PrimVar& var : output) indices.push_back(var);
632 if (count_include_pad) {
633 auto num_el = IntImm::Int32(1);
634 for (int i = 0; i < k_size; i++) {
635 int ii = axis[i];
636 PrimExpr start = output[ii] * stride[i] - pad_head[i];
637 // When computing the output shape in ceil_mode,
638 // we have added the extra padding of offset[i],
639 // so now in order to calculate the correct boundary ,
640 // we need to substract the offset[i].
641 PrimExpr end = start + (kernel[i] - 1) * dilation[i];
642 end = min(end, data_shape[ii] + pad_tail[i] - 1 - offset[i]);
643 num_el *= (end - start) / dilation[i] + 1;
644 }
645 return div(pool_sum(indices), num_el);
646 } else {
647 auto num_el = IntImm::Int32(1);
648 for (int i = 0; i < k_size; i++) {
649 int ii = axis[i];
650
651 // Let start and end contain the first and last index of our Tensor
652 // along the relevant dimension we use in our calculation.
653 // Assume indices -1, -2 represent the padding before (tail) and
654 // len(arr), len(arr) + 1 represent the padding after (head).
655 PrimExpr start = output[ii] * stride[i] - pad_head[i];
656 PrimExpr end = start + (kernel[i] - 1) * dilation[i];
657
658 // if start < 0, e.g. we start on a tail padded number this will be a positive
659 // number that represents the number of steps along the dilated kernel to reach a
660 // non-padded value. Otherwise this should be 0.
661 PrimExpr jumps_to_non_pad = (dilation[i] - 1 - start) / dilation[i];
662 jumps_to_non_pad = max(jumps_to_non_pad, IntImm(jumps_to_non_pad.ty(), 0));
663
664 end = min(end, data_shape[ii] - 1);
665 num_el *= (end - (start + dilation[i] * jumps_to_non_pad)) / dilation[i] + 1;
666 }
667
668 PrimExpr divide_factor = max(num_el, IntImm::Int32(1));
669 return div(pool_sum(indices), divide_factor);
670 }
671 },
672 "pool_avg", kElementWise, attrs);
673 } else {
674 LOG(ERROR) << "Unrecognized pool_type: " << pool_type;
675 return x;
676 }
677}
678
709inline Tensor pool1d(const Tensor& x, const ffi::Array<PrimExpr>& kernel_size,
710 const ffi::Array<PrimExpr>& stride_size,
711 const ffi::Array<PrimExpr>& dilation_size,
712 const ffi::Array<PrimExpr>& padding_size, PoolType pool_type, bool ceil_mode,
713 const std::string& layout = "NCW", bool count_include_pad = true) {
714 int width_axis = -1;
715 TVM_FFI_ICHECK(find_width(layout, &width_axis)) << "Unsupported layout " << layout;
716 std::vector<int> axis = {width_axis};
717 return pool_impl_nd(x, kernel_size, stride_size, dilation_size, padding_size, pool_type,
718 ceil_mode, axis, count_include_pad);
719}
720
751inline Tensor pool2d(const Tensor& x, const ffi::Array<PrimExpr>& kernel_size,
752 const ffi::Array<PrimExpr>& stride_size,
753 const ffi::Array<PrimExpr>& dilation_size,
754 const ffi::Array<PrimExpr>& padding_size, PoolType pool_type, bool ceil_mode,
755 const std::string& layout = "NCHW", bool count_include_pad = true) {
756 int height_axis = -1, width_axis = -1;
757 TVM_FFI_ICHECK(find_height_width(layout, &height_axis, &width_axis))
758 << "Unsupported layout " << layout;
759 std::vector<int> axis = {height_axis, width_axis};
760 return pool_impl_nd(x, kernel_size, stride_size, dilation_size, padding_size, pool_type,
761 ceil_mode, axis, count_include_pad);
762}
763
795inline Tensor pool3d(const Tensor& x, const ffi::Array<PrimExpr>& kernel_size,
796 const ffi::Array<PrimExpr>& stride_size,
797 const ffi::Array<PrimExpr>& dilation_size,
798 const ffi::Array<PrimExpr>& padding_size, PoolType pool_type, bool ceil_mode,
799 const std::string& layout = "NCDHW", bool count_include_pad = true) {
800 int depth_axis = -1, height_axis = -1, width_axis = -1;
801 TVM_FFI_ICHECK(find_depth_height_width(layout, &depth_axis, &height_axis, &width_axis))
802 << "Unsupported layout " << layout;
803 std::vector<int> axis = {depth_axis, height_axis, width_axis};
804 return pool_impl_nd(x, kernel_size, stride_size, dilation_size, padding_size, pool_type,
805 ceil_mode, axis, count_include_pad);
806}
807
808} // namespace nn
809} // namespace topi
810} // namespace tvm
811#endif // TVM_TOPI_NN_POOLING_H_
Algebra expression simplifications.
Constant integer literals in the program.
Definition expr.h:135
ffi::BigInt value
the Internal value.
Definition expr.h:138
Managed reference class to IntImmNode.
Definition expr.h:152
static IntImm Int32(ffi::BigInt value, Location loc=UnknownLoc())
Construct a scalar int32 constant.
Definition expr.h:181
Typed reference/view over any Expr whose ExprNode::ty is PrimType.
Definition base_expr.h:447
Definition base_expr.h:161
static TVM_TIRX_DLL PrimType Int(int bits, int lanes=1)
Construct a signed integer type with fixed lanes.
Checked scalar view over a VarNode.
Definition expr.h:742
Range container
Definition expr.h:1025
ExpectedType ty() const
Definition base_expr.h:424
Managed reference to AndNode.
Definition expr.h:578
Managed reference to SelectNode.
Definition expr.h:712
Managed reference to AnalyzerObj.
Definition analyzer.h:916
Tensor structure representing a possible input, or intermediate computation result.
Definition tensor.h:111
Definition expr.h:47
TVM_TIRX_DLL PrimExpr min_value(PrimType dtype, Location loc=UnknownLoc())
PrimExpr sum(PrimExpr source, ffi::Array< s_tir::IterVar > axis, ffi::Array< PrimExpr > init={}, Location loc=UnknownLoc())
sum of source expression over axis
TVM_TIRX_DLL PrimExpr cast(PrimType t, PrimExpr value, Location loc=UnknownLoc())
cast value to type.
constexpr const char * kScheduleRule
Definition stmt.h:388
Tensor expression language DSL.
Definition extracted_task.h:33
PrimVar var(std::string name_hint, PrimType t=PrimType::Int(32))
Construct a new Var expression.
IterVar reduce_axis(Range dom, std::string name="rv")
Create a new IterVar for reduction operations.
Tensor compute(ffi::Array< PrimExpr > shape, FCompute fcompute, std::string name="tensor", std::string tag="", ffi::Map< ffi::String, ffi::Any > attrs={})
Construct a new tensor by computing over shape, using the computation rule: result_tensor[axis] = fco...
Tensor adaptive_pool3d(const Tensor &x, const ffi::Array< PrimExpr > &output_size, PoolType pool_type, const std::string &layout="NCDHW")
Adaptively perform pooling on three dimensional data. See the two dimensional version above for detai...
Definition pooling.h:451
Tensor adaptive_pool(const Tensor &x, const ffi::Array< PrimExpr > &output_size, PoolType pool_type, const std::string &layout="NCHW")
Adaptively perform pooling on height and width dimension of data. The pooling kernel and stride sizes...
Definition pooling.h:435
PoolType
Pooling type.
Definition pooling.h:45
@ kAvgPool
Definition pooling.h:46
@ kMaxPool
Definition pooling.h:47
PrimExpr start_index(const PrimVar &out_index, const PrimExpr &odim, const PrimExpr &idim)
Definition pooling.h:313
Tensor adaptive_pool1d(const Tensor &x, const ffi::Array< PrimExpr > &output_size, PoolType pool_type, const std::string &layout="NCW")
Adaptively perform pooling on one dimensional data. See the two dimensional version above for details...
Definition pooling.h:467
Tensor pool3d(const Tensor &x, const ffi::Array< PrimExpr > &kernel_size, const ffi::Array< PrimExpr > &stride_size, const ffi::Array< PrimExpr > &dilation_size, const ffi::Array< PrimExpr > &padding_size, PoolType pool_type, bool ceil_mode, const std::string &layout="NCDHW", bool count_include_pad=true)
Perform pooling on depth, height and width dimension of data. It decides the depth,...
Definition pooling.h:795
Tensor pool2d(const Tensor &x, const ffi::Array< PrimExpr > &kernel_size, const ffi::Array< PrimExpr > &stride_size, const ffi::Array< PrimExpr > &dilation_size, const ffi::Array< PrimExpr > &padding_size, PoolType pool_type, bool ceil_mode, const std::string &layout="NCHW", bool count_include_pad=true)
Perform pooling on height and width dimension of data. It decides the height and width dimension acco...
Definition pooling.h:751
Tensor pool_grad_impl(const Tensor &out_grad, const Tensor &x, const ffi::Array< PrimExpr > &kernel_size, const ffi::Array< PrimExpr > &stride_size, const ffi::Array< PrimExpr > &padding_size, PoolType pool_type, bool ceil_mode, const size_t height_axis, const size_t width_axis, bool count_include_pad)
Definition pooling.h:50
Tensor pool_grad(const Tensor &out_grad, const Tensor &x, const ffi::Array< PrimExpr > &kernel_size, const ffi::Array< PrimExpr > &stride_size, const ffi::Array< PrimExpr > &padding_size, PoolType pool_type, bool ceil_mode, const std::string &layout="NCHW", bool count_include_pad=true)
Calculate gradient of pooling on height and width dimension of data. It decides the height and width ...
Definition pooling.h:300
bool find_depth_height_width(const std::string &layout, int *depth_axis, int *height_axis, int *width_axis)
Find index of Depth, Height or Width dimension in a layout string.
Definition pooling.h:231
bool find_width(const std::string &layout, int *width_axis)
Definition pooling.h:265
Tensor pool_impl_nd(const Tensor &x, const ffi::Array< PrimExpr > &kernel_size, const ffi::Array< PrimExpr > &stride_size, const ffi::Array< PrimExpr > &dilation_size, const ffi::Array< PrimExpr > &padding_size, PoolType pool_type, bool ceil_mode, const std::vector< int > &axis, bool count_include_pad)
Perform pooling on N-dimension of data.
Definition pooling.h:519
PrimExpr end_index(const PrimVar &out_index, const PrimExpr &odim, const PrimExpr &idim)
Definition pooling.h:317
bool find_height_width(const std::string &layout, int *height_axis, int *width_axis)
Definition pooling.h:261
Tensor global_pool(const Tensor &x, PoolType pool_type, const std::string &layout="NCHW")
Perform global pooling on height and width dimension of data. It decides the height and width dimensi...
Definition pooling.h:499
Tensor adaptive_pool_impl(const Tensor &x, const ffi::Array< PrimExpr > &output_size, PoolType pool_type, const std::vector< int > &axes)
Perform adaptive pooling on N dimensional data.
Definition pooling.h:332
Tensor pool1d(const Tensor &x, const ffi::Array< PrimExpr > &kernel_size, const ffi::Array< PrimExpr > &stride_size, const ffi::Array< PrimExpr > &dilation_size, const ffi::Array< PrimExpr > &padding_size, PoolType pool_type, bool ceil_mode, const std::string &layout="NCW", bool count_include_pad=true)
Perform pooling on the width dimension of data. Width axis is determined by the layout string in whic...
Definition pooling.h:709
constexpr auto kElementWise
Definition tags.h:32
tvm::te::Tensor pad(const tvm::te::Tensor &t, const tvm::ffi::Array< tvm::PrimExpr > &pad_before, tvm::ffi::Array< tvm::PrimExpr > pad_after=tvm::ffi::Array< tvm::PrimExpr >(), ffi::Optional< PrimExpr > pad_value=std::nullopt, std::string name="T_pad", std::string tag=kElementWise, std::string pad_mode="constant", const ffi::Array< PrimExpr > *dyn_output_shape=nullptr)
Creates an operation that performs padding.
Definition nn.h:155
Tensor min(const Tensor &data, const ffi::Optional< ffi::Array< int64_t > > &axis, bool keepdims=false, bool atleast1d=false)
Creates an operation that finds the minimum of elements over a given axis.
Definition reduction.h:432
FCommReduce MakeArgmaxReducer(bool select_last_index=false)
Definition reduction.h:513
Tensor max(const Tensor &data, const ffi::Optional< ffi::Array< int64_t > > &axis, bool keepdims=false, bool atleast1d=false)
Creates an operation that finds the maximum of elements over a given axis.
Definition reduction.h:451
constexpr auto kCommReduceIdx
Definition tags.h:35
Tensor argmax(const Tensor &data, const ffi::Optional< ffi::Array< int64_t > > &axis, bool keepdims=false, bool atleast1d=false, bool select_last_index=false)
Creates an operation that finds the indices of the maximum values over a given axis.
Definition reduction.h:562
An object that builds and maintains block scope and StmtSref mapping for Dependence analysis.
Definition attr.h:23
TVM_TIRX_DLL PrimExpr max(PrimExpr a, PrimExpr b, Location loc=UnknownLoc())
take maximum of two values
TVM_TIRX_DLL PrimExpr if_then_else(PrimExpr cond, PrimExpr true_value, PrimExpr false_value, Location loc=UnknownLoc())
Conditional expression.
TVM_TIRX_DLL PrimExpr indexdiv(PrimExpr a, PrimExpr b, Location loc=UnknownLoc())
compute floor(a / b) where a and b are non-negative.
TVM_TIRX_DLL PrimExpr div(PrimExpr a, PrimExpr b, Location loc=UnknownLoc())
compute division in C semantics.
TVM_TIRX_DLL PrimExpr indexmod(PrimExpr a, PrimExpr b, Location loc=UnknownLoc())
compute the remainder floor(a / b) where a and b are non-negative.
Padding helpers.
S-TIR (Schedulable TIR) statements and attributes.
Tag definitions.
NN op constructions.
Reduction op constructors.