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