tvm
transform.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_TRANSFORM_H_
25 #define TVM_TOPI_TRANSFORM_H_
26 
27 #include <tvm/arith/analyzer.h>
28 #include <tvm/s_tir/data_layout.h>
29 #include <tvm/te/operation.h>
30 #include <tvm/tirx/index_map.h>
31 #include <tvm/topi/broadcast.h>
37 #include <tvm/topi/tags.h>
38 
39 #include <algorithm>
40 #include <iterator>
41 #include <limits>
42 #include <string>
43 #include <unordered_set>
44 #include <utility>
45 #include <vector>
46 
47 #include "tvm/ffi/dtype.h"
48 #include "tvm/ir/expr.h"
49 #include "tvm/tirx/expr.h"
50 #include "tvm/tirx/op.h"
51 #include "tvm/tirx/var.h"
52 
53 namespace tvm {
54 namespace topi {
55 
56 using namespace tvm::te;
57 using namespace topi::detail;
58 
76 inline Tensor sliding_window(const Tensor& x, int axis, ffi::Array<int64_t> window_shape,
77  ffi::Array<int64_t> strides, std::string name = "T_sliding_window",
78  std::string tag = "") {
79  TVM_FFI_ICHECK_GE(axis, 0);
80  auto _axis = size_t(axis);
81  TVM_FFI_ICHECK_LT(_axis, x->shape.size()) << "axis must be a valid dimension index of x.";
82  TVM_FFI_ICHECK_EQ(x->shape.size() - _axis, window_shape.size())
83  << "There must be a window shape for every dimension of x "
84  << "over which we are sliding the window.";
85  TVM_FFI_ICHECK_EQ(strides.size(), window_shape.size())
86  << "Windows and strides should be the same length.";
87 
88  // Compute the new shape.
89  ffi::Array<PrimExpr> new_shape;
90  // Dimensions up until `axis` remain the same.
91  for (size_t i = 0; i < _axis; ++i) {
92  new_shape.push_back(x->shape[i]);
93  }
94 
95  // New dimensions which result from sliding the window in each dimension. One new dimension per
96  // window dimension.
97  for (size_t i = 0; i < window_shape.size(); ++i) {
98  // Length of the shape along this dimension.
99  auto dim_len = x->shape[_axis + i];
100  // Length of the window along this dimension.
101  PrimExpr window_len = IntImm::Int64(window_shape[i]);
102  // Strides along this dimension.
103  PrimExpr stride = IntImm::Int64(strides[i]);
104 
105  new_shape.push_back(floordiv(dim_len - (window_len - 1) + stride - 1, stride));
106  }
107 
108  // Dimensions comprising the window.
109  for (size_t i = 0; i < window_shape.size(); ++i) {
110  new_shape.push_back(IntImm::Int64(window_shape[i]));
111  }
112 
113  TVM_FFI_ICHECK(new_shape.size() == _axis + 2 * window_shape.size());
114 
115  return compute(
116  new_shape,
117  [&](const ffi::Array<PrimVar>& indices) {
118  // The index at which to index the old tensor x.
119  ffi::Array<PrimExpr> idx;
120 
121  // Dimensions up until `axis` remain the same.
122  for (size_t i = 0; i < _axis; ++i) {
123  idx.push_back(indices[i]);
124  }
125 
126  for (size_t i = 0; i < window_shape.size(); ++i) {
127  // Which window in this dimension we are indexing.
128  auto window_idx = indices[_axis + i];
129  // Which index within the window we are indexing.
130  auto idx_within_window = indices[_axis + window_shape.size() + i];
131  // Stride value for this dimension.
132  PrimExpr stride = IntImm::Int64(strides[i]);
133 
134  idx.push_back(window_idx * stride + idx_within_window);
135  }
136 
137  TVM_FFI_ICHECK(idx.size() == x->shape.size());
138 
139  return x(idx);
140  },
141  name, tag);
142 }
143 
156 inline Tensor expand_dims(const Tensor& x, int axis, int num_newaxis = 1,
157  std::string name = "T_expand_dims", std::string tag = kBroadcast) {
158  int ndim = static_cast<int>(x->shape.size());
159  TVM_FFI_ICHECK(-ndim - 1 <= axis && axis <= ndim)
160  << "expand_dims only accepts `axis` in [-data.ndim - 1, data.ndim]"
161  << ", but got axis = " << axis << ", and data.ndim = " << ndim;
162  TVM_FFI_ICHECK(num_newaxis >= 0) << "expand_dims only accepts `num_newaxis >= 0`"
163  << ", but got num_newaxis = " << num_newaxis;
164  if (axis < 0) {
165  // Calculate offset from last dimension
166  axis = ndim + axis + 1;
167  }
168  ffi::Array<PrimExpr> new_shape;
169  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
170  new_shape.push_back(x->shape[i]);
171  }
172  for (size_t i = 0; i < static_cast<size_t>(num_newaxis); ++i) {
173  new_shape.push_back(1);
174  }
175  for (size_t i = axis; i < x->shape.size(); ++i) {
176  new_shape.push_back(x->shape[i]);
177  }
178 
179  return compute(
180  new_shape,
181  [&](const ffi::Array<PrimVar>& indices) {
182  ffi::Array<PrimExpr> idx;
183  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
184  idx.push_back(indices[i]);
185  }
186  for (size_t i = axis + num_newaxis; i < indices.size(); ++i) {
187  idx.push_back(indices[i]);
188  }
189  return x(idx);
190  },
191  name, tag);
192 }
193 
205 inline Tensor transpose(const Tensor& x, ffi::Optional<ffi::Array<int64_t>> opt_axes,
206  std::string name = "T_transpose", std::string tag = kInjective) {
207  ffi::Array<int64_t> axes = opt_axes.value_or({});
208  if (axes.size() == 0) {
209  for (int i = static_cast<int>(x->shape.size()) - 1; i >= 0; --i) {
210  axes.push_back(i);
211  }
212  }
213 
214  ffi::Array<PrimExpr> new_shape;
215  for (size_t i = 0; i < axes.size(); ++i) {
216  int axis = static_cast<int>(axes[i]);
217  int new_axis = axis;
218  if (axis < 0) {
219  new_axis = static_cast<int>(x->shape.size()) + axis;
220  axes.Set(i, new_axis);
221  }
222  TVM_FFI_ICHECK((new_axis >= 0) && (new_axis < static_cast<int>(x->shape.size())))
223  << "axis=" << axis << " is invalid for the " << static_cast<int>(x->shape.size())
224  << "-dimensional input tensor";
225 
226  for (size_t j = 0; j < axes.size(); ++j) {
227  if (i != j) {
228  TVM_FFI_ICHECK(new_axis != static_cast<int>(axes[j])) << "repeated axis in transpose";
229  }
230  }
231  new_shape.push_back(x->shape[new_axis]);
232  }
233 
234  return compute(
235  new_shape,
236  [&](const ffi::Array<PrimVar>& indices) {
237  std::vector<PrimExpr> idx;
238  for (size_t i = 0; i < axes.size(); ++i) {
239  idx.push_back(1);
240  }
241  for (size_t i = 0; i < axes.size(); ++i) {
242  int axis = static_cast<int>(axes[i]);
243  idx[axis] = indices[i];
244  }
245  return x(idx);
246  },
247  name, tag);
248 }
249 
264 inline Tensor reverse_sequence(const Tensor& x, const Tensor& seq_lengths, int seq_axis = 1,
265  int batch_axis = 0, std::string name = "T_reverse_sequence",
266  std::string tag = kInjective) {
267  size_t src_tensor_dim = x->shape.size();
268  int seq_axis_inp = seq_axis;
269 
270  if (seq_lengths.defined()) {
271  size_t seq_lengths_dim = seq_lengths->shape.size();
272  int batch_axis_inp = batch_axis;
273  if (batch_axis < 0) {
274  batch_axis = static_cast<int>(x->shape.size()) + batch_axis;
275  }
276 
277  TVM_FFI_ICHECK(seq_lengths_dim == 1) << "seq_lengths should be 1D vector";
278 
279  TVM_FFI_ICHECK(GetConstInt(seq_lengths->shape[0]) == GetConstInt(x->shape[batch_axis]))
280  << "For reverse_sequnece seq_lengths size should match with dimension of batch axis"
281  << ", but got dimension of batch_axis = " << GetConstInt(x->shape[batch_axis])
282  << ", and seq_length size = " << GetConstInt(seq_lengths->shape[0]);
283 
284  TVM_FFI_ICHECK((0 <= batch_axis) && (batch_axis < static_cast<int>(x->shape.size())))
285  << "batch_axis=" << batch_axis_inp << " is invalid for the "
286  << static_cast<int>(x->shape.size()) << "-dimensional input tensor";
287  }
288 
289  if (seq_axis < 0) {
290  seq_axis = static_cast<int>(x->shape.size()) + seq_axis;
291  }
292  TVM_FFI_ICHECK((0 <= seq_axis) && (seq_axis < static_cast<int>(x->shape.size())))
293  << "seq_axis=" << seq_axis_inp << " is invalid for the " << static_cast<int>(x->shape.size())
294  << "-dimensional input tensor";
295 
296  auto func = [&](const ffi::Array<PrimVar>& indices) {
297  ffi::Array<PrimExpr> real_indices;
298  for (size_t i = 0; i < src_tensor_dim; ++i) {
299  if (i == static_cast<size_t>(seq_axis)) {
300  if (seq_lengths.defined()) {
301  auto len = seq_lengths(indices[batch_axis]);
302  auto idx = if_then_else(
303  len <= 1 || len <= indices[i], indices[i],
304  if_then_else(len > x->shape[i], x->shape[i] - 1 - indices[i], len - 1 - indices[i]));
305  real_indices.push_back(idx);
306  } else {
307  real_indices.push_back(x->shape[i] - 1 - indices[i]);
308  }
309  } else {
310  real_indices.push_back(indices[i]);
311  }
312  }
313  return x(real_indices);
314  };
315 
316  return compute(x->shape, func, name, tag);
317 }
318 
329 inline Tensor reshape(const Tensor& x, ffi::Array<PrimExpr> newshape,
330  std::string name = "T_reshape", std::string tag = kInjective) {
331  auto x_shape = x->shape;
332  ffi::Array<PrimExpr> target_shape;
333 
334  for (const auto& ele : newshape) {
335  target_shape.push_back(ele);
336  }
337 
338  // If either the input shape or the target shape contains a zero, return an empty tensor.
339  if (is_empty_shape(target_shape) || is_empty_shape(x->shape)) {
340  return compute(
341  target_shape,
342  [&](const ffi::Array<PrimVar>& indices) { return tvm::cast(PrimType(x->dtype), 0); }, name,
343  tag);
344  } else {
345  return compute(
346  target_shape,
347  [&](const ffi::Array<PrimVar>& indices) {
348  ffi::Array<PrimExpr> prim_indices =
349  indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
350  return x(UnravelIndex(RavelIndex(prim_indices, target_shape), x_shape));
351  },
352  name, tag);
353  }
354 }
355 
367 inline Tensor unravel_index(const Tensor& x, const Tensor& shape, std::string name = "T_unravel",
368  std::string tag = kInjective) {
369  auto x_shape = x->shape;
370  auto shape_shape = shape->shape;
371 
372  ffi::Array<PrimExpr> oshape;
373  oshape.push_back(shape_shape[0]);
374  if (x_shape.size() != 0) {
375  oshape.push_back(x_shape[0]);
376  }
377 
378  auto func = [&](const ffi::Array<PrimVar>& indices) {
379  auto i = indices[0];
380  std::vector<PrimExpr> indices_divs;
381  PrimExpr ret = 0;
382  PrimExpr cur_val = 0;
383  PrimExpr index_val = 0;
384 
385  if (x_shape.size() != 0) {
386  index_val = x[indices[1]];
387  } else {
388  index_val = x();
389  }
390  indices_divs.push_back(index_val);
391  for (int v = GetConstInt(shape_shape[0]) - 1; v >= 0; --v) {
392  ret = tvm::if_then_else(i == v, indexmod(indices_divs.back(), shape[v]), ret);
393  cur_val = indexdiv(indices_divs.back(), shape[v]);
394  indices_divs.push_back(cur_val);
395  }
396  return ret;
397  };
398 
399  return compute(oshape, func, name, tag);
400 }
401 
415 inline Tensor squeeze(const Tensor& x, ffi::Optional<ffi::Array<int64_t>> opt_axes,
416  bool atleast1d = false, std::string name = "T_squeeze",
417  std::string tag = kInjective) {
418  auto ndim = x->shape.size();
419  std::vector<int> axis_val;
420  if (!opt_axes.has_value()) {
421  for (size_t i = 0; i < ndim; ++i) {
422  if (IsConstInt(x->shape[i]) && GetConstInt(x->shape[i]) == 1) {
423  axis_val.push_back(static_cast<int>(i));
424  }
425  }
426  } else {
427  ffi::Array<int64_t> axis = *std::move(opt_axes);
428  for (size_t i = 0; i < axis.size(); ++i) {
429  int64_t val = axis[i];
430  if (val < 0) {
431  val += static_cast<int>(x->shape.size());
432  }
433  // If a dimension is not 1, silently skip it (no-op).
434  bool is_const = IsConstInt(x->shape[val]);
435  if ((is_const && GetConstInt(x->shape[val]) == 1) || !is_const) {
436  axis_val.push_back(val);
437  }
438  }
439  }
440 
441  std::unordered_set<int> axis_set(axis_val.begin(), axis_val.end());
442 
443  ffi::Array<PrimExpr> out_shape;
444  for (size_t i = 0; i < ndim; ++i) {
445  if (axis_set.count(static_cast<int>(i)) == 0) {
446  out_shape.push_back(x->shape[i]);
447  }
448  }
449  if (out_shape.size() == 0 && atleast1d) {
450  out_shape.push_back(1);
451  }
452 
453  return compute(
454  out_shape,
455  [&](const ffi::Array<PrimVar>& indices) {
456  ffi::Array<PrimExpr> real_indices;
457  int flag = 0;
458  for (size_t i = 0; i < ndim; ++i) {
459  if (axis_set.count(static_cast<int>(i)) == 0) {
460  real_indices.push_back(indices[i - flag]);
461  } else {
462  real_indices.push_back(0);
463  flag += 1;
464  }
465  }
466  return x(real_indices);
467  },
468  name, tag);
469 }
470 
481 inline Tensor concatenate(const ffi::Array<Tensor>& inputs, int axis = 0,
482  std::string name = "T_concat", std::string tag = kInjective) {
483  int ndim = static_cast<int>(inputs[0]->shape.size());
484  TVM_FFI_ICHECK(-ndim <= axis && axis < ndim)
485  << "concatenate only accepts `axis` in [-ndim, ndim)"
486  << ", but got axis = " << axis << ", and ndim = " << ndim;
487  if (axis < 0) {
488  axis += ndim;
489  }
490  TVM_FFI_ICHECK_LT(axis, inputs[0]->shape.size()) << "axis out of bounds";
491 
492  ffi::Array<PrimExpr> axis_sizes;
493  for (auto t : inputs) {
494  axis_sizes.push_back(t->shape[axis]);
495  }
496  arith::Analyzer analyzer;
497  PrimExpr join_size = axis_sizes[0];
498  for (size_t i = 1; i < axis_sizes.size(); ++i) {
499  join_size += axis_sizes[i];
500  }
501  join_size = analyzer->Simplify(join_size);
502  ffi::Array<PrimExpr> out_shape;
503  for (size_t i = 0; i < inputs[0]->shape.size(); ++i) {
504  out_shape.push_back(i == static_cast<size_t>(axis) ? join_size : inputs[0]->shape[i]);
505  }
506 
507  return compute(
508  out_shape,
509  [&](const ffi::Array<PrimVar>& indices) {
510  auto ret = inputs[0](indices);
511  PrimExpr ind = indices[axis].as_or_throw<PrimExpr>();
512  for (size_t i = 0; i < inputs.size() - 1; ++i) {
513  ind -= axis_sizes[i];
514 
515  ffi::Array<PrimExpr> idx;
516  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
517  idx.push_back(indices[i]);
518  }
519  idx.push_back(ind);
520  for (size_t i = axis + 1; i < indices.size(); ++i) {
521  idx.push_back(indices[i]);
522  }
523 
524  ret = tvm::if_then_else(ind >= 0, inputs[i + 1](idx), ret);
525  }
526  return ret;
527  },
528  name, tag);
529 }
530 
541 inline Tensor stack(const ffi::Array<Tensor>& inputs, int axis = 0, std::string name = "T_stack",
542  std::string tag = kInjective) {
543  int ndim = static_cast<int>(inputs[0]->shape.size());
544  TVM_FFI_ICHECK(-ndim - 1 <= axis && axis <= ndim)
545  << "stack only accepts `axis` in [-ndim, ndim)"
546  << ", but got axis = " << axis << ", and ndim = " << ndim;
547  if (axis < 0) {
548  axis += ndim + 1;
549  }
550  TVM_FFI_ICHECK_LT(axis, inputs[0]->shape.size() + 1) << "axis out of bounds";
551 
552  const int stack_size = static_cast<int>(inputs.size());
553  ffi::Array<PrimExpr> out_shape;
554  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) out_shape.push_back(inputs[0]->shape[i]);
555  out_shape.push_back(stack_size);
556  for (size_t i = static_cast<size_t>(axis); i < static_cast<size_t>(ndim); ++i)
557  out_shape.push_back(inputs[0]->shape[i]);
558 
559  return compute(
560  out_shape,
561  [&](const ffi::Array<PrimVar>& indices) {
562  ffi::Array<PrimExpr> idx;
563  for (size_t i = 0; i < indices.size(); ++i)
564  if (i != static_cast<size_t>(axis)) idx.push_back(indices[i]);
565  auto ind = indices[axis];
566  auto ret = inputs[0](idx);
567  for (int i = 0; i < static_cast<int>(inputs.size() - 1); ++i) {
568  ret = tvm::if_then_else(ind == i + 1, inputs[i + 1](idx), ret);
569  }
570  return ret;
571  },
572  name, tag);
573 }
574 
587 inline ffi::Array<Tensor> split_indices_array(const Tensor& x, ffi::Array<PrimExpr> split_indices,
588  int axis, std::string name = "T_split",
589  std::string tag = kInjective) {
590  if (axis < 0) {
591  axis += static_cast<int>(x->shape.size());
592  }
593  TVM_FFI_ICHECK_LT(axis, x->shape.size()) << "axis out of bounds";
594 
595  auto src_axis_size = x->shape[axis];
596  std::vector<PrimExpr> begin_ids;
597  begin_ids.push_back(0);
598 
599  for (auto idx : split_indices) {
600  auto idx_node = idx.as<IntImmNode>();
601  auto back_node = begin_ids.back().as<IntImmNode>();
602  if (idx_node && back_node) {
603  TVM_FFI_ICHECK_GT(idx_node->value, back_node->value) << "split_indices must be sorted";
604  }
605  begin_ids.push_back(idx);
606  }
607 
608  ffi::Array<ffi::Array<PrimExpr>> out_shapes;
609  for (size_t i = 0; i < begin_ids.size(); ++i) {
610  PrimExpr out_axis_size;
611  if (i == begin_ids.size() - 1) {
612  out_axis_size = src_axis_size - begin_ids[i];
613  } else {
614  out_axis_size = begin_ids[i + 1] - begin_ids[i];
615  }
616 
617  ffi::Array<PrimExpr> shape;
618  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
619  shape.push_back(x->shape[i]);
620  }
621  shape.push_back(out_axis_size);
622  for (size_t i = axis + 1; i < x->shape.size(); ++i) {
623  shape.push_back(x->shape[i]);
624  }
625 
626  out_shapes.push_back(shape);
627  }
628 
629  ffi::Array<Tensor> result;
630  for (size_t i = 0; i < begin_ids.size(); ++i) {
631  result.push_back(compute(
632  out_shapes[i],
633  [&](const ffi::Array<PrimVar>& indices) {
634  auto begin = begin_ids[i];
635  ffi::Array<PrimExpr> real_indices;
636  for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
637  real_indices.push_back(indices[j]);
638  }
639  real_indices.push_back(indices[axis] + begin);
640  for (size_t j = axis + 1; j < indices.size(); ++j) {
641  real_indices.push_back(indices[j]);
642  }
643 
644  return x(real_indices);
645  },
646  name, tag));
647  }
648 
649  return result;
650 }
651 
653  auto idx_var = index.as<tvm::tirx::VarNode>();
654  auto extent_var = extent.as<tvm::tirx::VarNode>();
655 
656  if (idx_var && extent_var && idx_var->name_hint == extent_var->name_hint) {
657  return index;
658  }
659 
660  PrimExpr begin_range = tvm::if_then_else(stride < 0, -1, 0);
661  PrimExpr end_range = tvm::if_then_else(stride < 0, extent - 1, extent);
662 
663  if (!(index->IsInstance<tvm::IntImmNode>() && GetConstInt(index) >= 0)) {
664  index = tvm::if_then_else(index < 0, index + extent, index);
665  }
666 
667  return tvm::min(tvm::max(index, begin_range), end_range);
668 }
669 
670 inline int64_t StaticCanonicalizeIndex(int64_t index, int64_t extent, int64_t stride) {
671  int64_t begin_range = stride < 0 ? -1 : 0;
672  int64_t end_range = stride < 0 ? extent - 1 : extent;
673  if (index < 0) {
674  index += extent;
675  }
676  return std::min(std::max(index, begin_range), end_range);
677 }
678 
679 inline PrimExpr CanonicalizeIndex(PrimExpr index, PrimExpr extent, PrimExpr stride) {
680  if (index->IsInstance<tvm::IntImmNode>() && extent->IsInstance<tvm::IntImmNode>() &&
681  stride->IsInstance<tvm::IntImmNode>()) {
682  return tvm::IntImm(
683  tvm::PrimType::Int(64),
684  StaticCanonicalizeIndex(GetConstInt(index), GetConstInt(extent), GetConstInt(stride)));
685  }
686  return DynamicCanonicalizeIndex(index, extent, stride);
687 }
688 
689 inline PrimExpr GetLength(PrimExpr begin, PrimExpr end, PrimExpr stride, PrimExpr extent,
690  bool assume_inbound = true) {
691  if (assume_inbound) {
692  return ceildiv(end - begin, stride);
693  } else {
694  begin = CanonicalizeIndex(begin, extent, stride);
695  end = CanonicalizeIndex(end, extent, stride);
696  return tvm::if_then_else(stride < 0, ceildiv(begin - end, -stride),
697  ceildiv(end - begin, stride));
698  }
699 }
700 
717  const te::Tensor& x, const ffi::Array<PrimExpr>& begin, const ffi::Array<PrimExpr>& end,
718  const ffi::Array<PrimExpr>& strides, const ffi::Array<int64_t>& axes,
719  bool assume_inbound = true, std::string name = "T_dynamic_strided_slice_with_axes",
720  std::string tag = kInjective) {
721  const size_t src_tensor_dim = x->shape.size();
722  TVM_FFI_ICHECK_EQ(begin.size(), end.size());
723  TVM_FFI_ICHECK_EQ(begin.size(), strides.size());
724  TVM_FFI_ICHECK_EQ(begin.size(), axes.size());
725  TVM_FFI_ICHECK_LE(begin.size(), src_tensor_dim);
726 
727  for (const auto& axis_imm : axes) {
728  int axis = static_cast<int>(axis_imm);
729  TVM_FFI_ICHECK_LT(axis, src_tensor_dim);
730  }
731 
732  arith::Analyzer analyzer;
733 
734  ffi::Array<PrimExpr> out_shape = x->shape;
735  for (size_t i = 0; i < begin.size(); i++) {
736  int axis = static_cast<int>(axes[i]);
737  PrimExpr new_shape = analyzer->Simplify(
738  GetLength(begin[i], end[i], strides[i], out_shape[axis], assume_inbound));
739  out_shape.Set(axis, new_shape);
740  }
741 
742  return te::compute(
743  out_shape,
744  [&](const ffi::Array<tvm::tirx::PrimVar>& indices) {
745  ffi::Array<PrimExpr> real_indices =
746  indices.Map([](const auto& var) -> PrimExpr { return var; });
747 
748  for (size_t i = 0; i < begin.size(); i++) {
749  int axis = static_cast<int>(axes[i]);
750  PrimExpr new_index = indices[axis] * strides[i] + begin[i];
751  real_indices.Set(axis, new_index);
752  }
753 
754  return x(real_indices);
755  },
756  name, tag);
757 }
758 
773 inline Tensor dynamic_strided_slice(const Tensor& x, const ffi::Array<PrimExpr>& begin,
774  const ffi::Array<PrimExpr>& end,
775  const ffi::Array<PrimExpr>& strides, bool assume_inbound = true,
776  std::string name = "T_dynamic_strided_slice",
777  std::string tag = kInjective) {
778  const size_t src_tensor_dim = x->shape.size();
779  TVM_FFI_ICHECK_LE(begin.size(), src_tensor_dim);
780  TVM_FFI_ICHECK_LE(end.size(), src_tensor_dim);
781  TVM_FFI_ICHECK_LE(strides.size(), src_tensor_dim);
782  TVM_FFI_ICHECK_EQ(begin.size(), end.size());
783  TVM_FFI_ICHECK_EQ(begin.size(), strides.size());
784 
785  const size_t num_slice_axes = begin.size();
786  ffi::Array<PrimExpr> out_shape;
787 
788  arith::Analyzer analyzer;
789  for (size_t i = 0; i < num_slice_axes; ++i) {
790  // Check ProducerLoad to keep backward compatibility for Relax.
791  if (!begin[i]->IsInstance<ProducerLoadNode>() && !end[i]->IsInstance<ProducerLoadNode>() &&
792  !strides[i]->IsInstance<ProducerLoadNode>()) {
793  out_shape.push_back(
794  analyzer->Simplify(GetLength(begin[i], end[i], strides[i], x->shape[i], assume_inbound)));
795  } else {
796  out_shape.push_back(tvm::tirx::PrimVar("dim"));
797  }
798  }
799 
800  for (size_t i = num_slice_axes; i < src_tensor_dim; ++i) {
801  out_shape.push_back(x->shape[i]);
802  }
803 
804  return te::compute(
805  out_shape,
806  [&](const ffi::Array<tvm::tirx::PrimVar>& indices) {
807  ffi::Array<PrimExpr> real_indices;
808  for (size_t i = 0; i < num_slice_axes; ++i) {
809  real_indices.push_back(indices[i] * strides[i] + tvm::min(begin[i], x->shape[i] - 1));
810  }
811  // keep input dim
812  for (size_t i = num_slice_axes; i < src_tensor_dim; ++i) {
813  real_indices.push_back(indices[i]);
814  }
815  return x(real_indices);
816  },
817  name, tag);
818 }
819 
835  const te::Tensor& end, const te::Tensor& strides,
836  bool assume_inbound = true,
837  std::string name = "T_strided_slice_dynamic",
838  std::string tag = topi::kInjective) {
839  PrimType index_ty = begin->shape[0].ty();
840  const int64_t num_dynamic_axes = begin->shape[0].as<IntImmNode>()->value;
841  TVM_FFI_ICHECK_EQ(end->shape[0].as<IntImmNode>()->value, num_dynamic_axes);
842  TVM_FFI_ICHECK_EQ(strides->shape[0].as<IntImmNode>()->value, num_dynamic_axes);
843 
844  ffi::Array<PrimExpr> begin_expr, end_expr, strides_expr;
845  for (int64_t i = 0; i < num_dynamic_axes; ++i) {
846  auto ind = IntImm(index_ty, i);
847  begin_expr.push_back(begin(ind));
848  end_expr.push_back(end(ind));
849  strides_expr.push_back(strides(ind));
850  }
851  return dynamic_strided_slice(x, begin_expr, end_expr, strides_expr, assume_inbound, name, tag);
852 }
853 
868 inline ffi::Array<PrimExpr> StridedSliceOutputShape(const ffi::Array<PrimExpr>& ishape,
869  const ffi::Array<ffi::Optional<IntImm>>& begin,
870  const ffi::Array<ffi::Optional<IntImm>>& end,
871  const ffi::Array<IntImm>& strides,
872  const ffi::Array<int64_t>& axes,
873  const std::string& slice_mode) {
874  TVM_FFI_ICHECK(axes.size() == begin.size() && axes.size() == end.size() &&
875  axes.size() == strides.size());
876  std::vector<int64_t> begin_vec, end_vec, strides_vec;
877  std::tie(begin_vec, end_vec, strides_vec) = ConvertToVec(begin, end, strides, slice_mode);
878  PrimType index_ty =
879  (begin.size() > 0 && begin[0].has_value()) ? begin[0].value().ty() : PrimType::Int(64);
880  auto begin_canonicalized =
881  StridedSliceCanonicalizeBegin(ishape, begin_vec, strides_vec, axes, index_ty, slice_mode);
882  return StridedSliceOutputShape(ishape, begin_vec, end_vec, strides_vec, axes, slice_mode,
883  begin_canonicalized, true);
884 }
885 
903  const Tensor& x, const ffi::Array<ffi::Optional<IntImm>>& begin,
904  const ffi::Array<ffi::Optional<IntImm>>& end, const ffi::Array<IntImm>& strides,
905  const ffi::Array<int64_t>& axes, std::string slice_mode = "end",
906  std::string name = "T_strided_slice_with_axes", std::string tag = kInjective) {
907  const int64_t src_tensor_dim = static_cast<int64_t>(x->shape.size());
908  TVM_FFI_ICHECK(static_cast<int64_t>(axes.size()) <= src_tensor_dim);
909  TVM_FFI_ICHECK(axes.size() == begin.size() && axes.size() == end.size() &&
910  axes.size() == strides.size());
911 
912  // Normalize negative axes
913  ffi::Array<int64_t> normalized_axes;
914  for (size_t i = 0; i < axes.size(); ++i) {
915  int64_t axis = axes[i];
916  if (axis < 0) {
917  axis += src_tensor_dim;
918  }
919  TVM_FFI_ICHECK(axis >= 0 && axis < src_tensor_dim)
920  << "Axis " << axes[i] << " is out of bounds for tensor with " << src_tensor_dim
921  << " dimensions";
922  normalized_axes.push_back(axis);
923  }
924 
925  std::vector<int64_t> begin_vec, end_vec, strides_vec;
926  std::tie(begin_vec, end_vec, strides_vec) = ConvertToVec(begin, end, strides, slice_mode);
927 
928  PrimType index_ty =
929  (begin.size() > 0 && begin[0].has_value()) ? begin[0].value().ty() : PrimType::Int(64);
930  auto begin_expr = StridedSliceCanonicalizeBegin(x->shape, begin_vec, strides_vec, normalized_axes,
931  index_ty, slice_mode);
932  auto out_shape = StridedSliceOutputShape(x->shape, begin_vec, end_vec, strides_vec,
933  normalized_axes, slice_mode, begin_expr);
934 
935  return te::compute(
936  out_shape,
937  [&](const ffi::Array<tirx::PrimVar>& indices) {
938  ffi::Array<PrimExpr> real_indices;
939  for (size_t i = 0; i < out_shape.size(); ++i) real_indices.push_back(indices[i]);
940  for (size_t i = 0; i < normalized_axes.size(); ++i) {
941  int64_t ax = normalized_axes[i];
942  auto stride = IntImm(strides[i]->ty.as_or_throw<PrimType>(), strides_vec[i]);
943  PrimExpr ind = indices[ax] * stride + begin_expr[i];
944  real_indices.Set(ax, ind);
945  }
946  return x(real_indices);
947  },
948  name, tag);
949 }
950 
965 inline Tensor strided_slice(const Tensor& x, const ffi::Array<ffi::Optional<IntImm>>& begin,
966  const ffi::Array<ffi::Optional<IntImm>>& end,
967  const ffi::Array<IntImm>& strides, std::string slice_mode = "end",
968  std::string name = "T_strided_slice", std::string tag = kInjective) {
969  size_t src_tensor_dim = static_cast<size_t>(x->shape.size());
970  ffi::Array<int64_t> axes;
971  for (size_t i = 0; i < src_tensor_dim; ++i) axes.push_back(i);
972  ffi::Array<ffi::Optional<IntImm>> begin_full(begin);
973  ffi::Array<ffi::Optional<IntImm>> end_full(end);
974  ffi::Array<IntImm> strides_full(strides);
975 
976  PrimType index_ty =
977  (begin.size() > 0 && begin[0].has_value()) ? begin[0].value().ty() : PrimType::Int(64);
978  const IntImm one = IntImm(index_ty, 1);
979  const IntImm zero = IntImm(index_ty, 0);
980  const IntImm max_range = max_value(index_ty).as_or_throw<IntImm>();
981 
982  for (size_t i = strides.size(); i < src_tensor_dim; ++i) {
983  strides_full.push_back(one);
984  }
985  for (size_t i = begin.size(); i < src_tensor_dim; ++i) {
986  begin_full.push_back(strides_full[i]->value > 0 ? zero : max_range);
987  }
988  for (size_t i = end.size(); i < src_tensor_dim; ++i) {
989  end_full.push_back(strides_full[i]->value < 0 ? zero : max_range);
990  }
991 
992  return strided_slice_with_axes(x, begin_full, end_full, strides_full, axes, slice_mode, name,
993  tag);
994 }
995 
1008 inline ffi::Array<Tensor> split_n_sections(const Tensor& x, int num_sections, int axis,
1009  std::string name = "T_split_sections",
1010  std::string tag = kInjective) {
1011  if (axis < 0) {
1012  axis += static_cast<int>(x->shape.size());
1013  }
1014  TVM_FFI_ICHECK_LT(axis, x->shape.size()) << "axis out of bounds";
1015 
1016  auto src_axis_size = x->shape[axis];
1017 
1018  TVM_FFI_ICHECK_GT(num_sections, 0) << "Slice count must be > 0";
1019 
1020  ffi::Array<PrimExpr> split_indices;
1021  auto seg_size = indexdiv(src_axis_size + num_sections - 1, num_sections);
1022  for (int i = 0; i < num_sections; ++i) {
1023  // region at index 0 is added by split()
1024  if (i != 0) {
1025  split_indices.push_back(seg_size * i);
1026  }
1027  }
1028 
1029  return split_indices_array(x, split_indices, axis, name, tag);
1030 }
1031 
1044 inline Tensor take(const Tensor& a, const Tensor& indices, int batch_dims,
1045  std::string mode = "fast", std::string name = "T_take",
1046  std::string tag = kInjective) {
1047  ffi::Array<PrimExpr> a_shape = a->shape;
1048  ffi::Array<PrimExpr> out_shape = indices->shape;
1049  PrimExpr a_size = 1;
1050  for (size_t i = 0; i < a_shape.size(); ++i) {
1051  a_size = a_size * a_shape[i];
1052  }
1053 
1054  if (mode == "clip") {
1055  return compute(
1056  out_shape,
1057  [&](const ffi::Array<PrimVar>& out_index) {
1058  auto idx = tvm::min(tvm::max(0, indices(out_index)), a_size - 1);
1059  return a(UnravelIndex(idx, a_shape));
1060  },
1061  name, tag);
1062  } else if (mode == "fast") {
1063  LOG(WARNING) << "Fast mode segfaults when there are out-of-bounds indices. "
1064  "Make sure input indices are in bound";
1065  return compute(
1066  out_shape,
1067  [&](const ffi::Array<PrimVar>& out_index) {
1068  return a(UnravelIndex(indices(out_index), a_shape));
1069  },
1070  name, tag);
1071  } else if (mode == "nan") {
1072  return compute(
1073  out_shape,
1074  [&](const ffi::Array<PrimVar>& out_index) {
1075  auto idx = tvm::if_then_else(
1076  indices(out_index) < 0 || indices(out_index) >= a_size,
1077  tvm::FloatImm(tvm::PrimType(a->dtype), std::numeric_limits<float>::quiet_NaN()),
1078  indices(out_index));
1079  return a(UnravelIndex(idx, a_shape));
1080  },
1081  name, tag);
1082  } else { // mode == "wrap"
1083  return compute(
1084  out_shape,
1085  [&](const ffi::Array<PrimVar>& out_index) {
1086  auto idx = truncmod(truncmod(indices(out_index), a_size) + a_size, a_size);
1087  return a(UnravelIndex(idx, a_shape));
1088  },
1089  name, tag);
1090  }
1091 }
1092 
1105 inline Tensor sequence_mask(const Tensor& data, const Tensor& valid_length, double mask_value,
1106  int axis, std::string name = "T_sequence_mask",
1107  std::string tag = kInjective) {
1108  TVM_FFI_ICHECK(axis == 0 || axis == 1) << "axis must be either 0 or 1";
1109  TVM_FFI_ICHECK_EQ(valid_length->shape.size(), 1)
1110  << "valid_length must have ndim=1, i.e., (batch_size,).";
1111  auto length_dim = data->shape[axis];
1112  auto batch_dim = data->shape[1 - axis];
1113  ffi::Array<PrimExpr> out_shape = data->shape;
1114  Tensor out = compute(
1115  out_shape,
1116  [&](const ffi::Array<PrimVar>& out_index) {
1117  ffi::Array<PrimExpr> len_index;
1118  auto tid = out_index[axis];
1119  auto bid = out_index[1 - axis];
1120  len_index.push_back(bid);
1122  tvm::cast(PrimType(valid_length->dtype), tid) >= valid_length(len_index),
1123  tvm::tirx::MakeConst(PrimType(data->dtype), mask_value), data(out_index));
1124  return ret;
1125  },
1126  name, tag);
1127  return out;
1128 }
1129 
1144 inline Tensor take(const Tensor& a, ffi::Variant<Tensor, PrimExpr> indices, int batch_dims,
1145  int axis, std::string mode = "fast", std::string name = "T_take",
1146  std::string tag = kInjective) {
1147  if (axis < 0) {
1148  axis += static_cast<int>(a->shape.size());
1149  }
1150  TVM_FFI_ICHECK_GE(axis, 0) << "axis out of bounds";
1151  TVM_FFI_ICHECK_LT(axis, a->shape.size()) << "axis out of bounds";
1152  auto axis_dim = a->shape[axis];
1153  auto indices_shape = [&]() -> ffi::Array<PrimExpr> {
1154  if (auto tensor = indices.as<TensorNode>()) {
1155  return tensor->shape;
1156  } else {
1157  return {};
1158  }
1159  }();
1160 
1161  int indices_len = static_cast<int>(indices_shape.size());
1162 
1163  int batch_dims_ = batch_dims;
1164  if (batch_dims_ != 0) {
1165  TVM_FFI_ICHECK_GE(batch_dims_, -indices_len) << "batch_dims out of bounds";
1166  TVM_FFI_ICHECK_LE(batch_dims_, indices_len) << "batch_dims out of bounds";
1167 
1168  if (batch_dims_ < 0) {
1169  batch_dims_ = indices_len + batch_dims_;
1170  }
1171 
1172  TVM_FFI_ICHECK_LT(batch_dims_, a->shape.size()) << "batch_dims out of bounds";
1173  TVM_FFI_ICHECK_LE(batch_dims_, axis) << "batch_dims must be less than or equal to axis";
1174  for (int i = 0; i < batch_dims_; ++i) {
1175  auto addr1 = a->shape[i];
1176  auto addr2 = indices_shape[i];
1177  auto v1 = static_cast<IntImm*>(&addr1)->get()->value;
1178  auto v2 = static_cast<IntImm*>(&addr2)->get()->value;
1179  TVM_FFI_ICHECK_EQ(v1, v2) << "a.shape[" << i << "] should be equal to indices.shape[" << i
1180  << "]";
1181  }
1182  }
1183 
1184  // The result shape is a.shape[:axis] + indices.shape[batch_dims:] +
1185  // a.shape[axis + 1:].
1186 
1187  ffi::Array<PrimExpr> out_shape;
1188  for (int i = 0; i < batch_dims_; ++i) {
1189  out_shape.push_back(a->shape[i]);
1190  }
1191  for (int i = batch_dims_; i < axis; ++i) {
1192  out_shape.push_back(a->shape[i]);
1193  }
1194  for (int i = batch_dims_; i < indices_len; ++i) {
1195  out_shape.push_back(indices_shape[i]);
1196  }
1197  for (size_t i = axis + 1; i < a->shape.size(); ++i) {
1198  out_shape.push_back(a->shape[i]);
1199  }
1200 
1201  auto get_index = [&](const ffi::Array<PrimExpr>& indices_position) -> PrimExpr {
1202  if (auto tensor = indices.as<Tensor>()) {
1203  return tensor.value()(indices_position);
1204  } else if (auto prim = indices.as<PrimExpr>()) {
1205  TVM_FFI_ICHECK_EQ(indices_position.size(), 0);
1206  return prim.value();
1207  } else {
1208  TVM_FFI_THROW(InternalError) << "Variant did not contain either allowed type";
1209  }
1210  };
1211 
1212  if (mode == "clip") {
1213  if (batch_dims_ == 0) {
1214  return compute(
1215  out_shape,
1216  [&](const ffi::Array<PrimVar>& out_index) {
1217  ffi::Array<PrimExpr> indices_position;
1218  for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1219  indices_position.push_back(out_index[j]);
1220  }
1221  ffi::Array<PrimExpr> real_indices;
1222  for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1223  real_indices.push_back(out_index[j]);
1224  }
1225  auto idx = tvm::min(tvm::max(0, get_index(indices_position)), axis_dim - 1);
1226  real_indices.push_back(idx);
1227  for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1228  real_indices.push_back(out_index[j]);
1229  }
1230  return a(real_indices);
1231  },
1232  name, tag);
1233  } else {
1234  return compute(
1235  out_shape,
1236  [&](const ffi::Array<PrimVar>& out_index) {
1237  ffi::Array<PrimExpr> indices_position;
1238  for (size_t j = 0; j < static_cast<size_t>(batch_dims_); ++j) {
1239  indices_position.push_back(out_index[j]);
1240  }
1241  for (size_t j = axis; j < static_cast<size_t>(axis + indices_len - batch_dims_); ++j) {
1242  indices_position.push_back(out_index[j]);
1243  }
1244  ffi::Array<PrimExpr> real_indices;
1245  for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1246  real_indices.push_back(out_index[j]);
1247  }
1248  auto idx = tvm::min(tvm::max(0, get_index(indices_position)), axis_dim - 1);
1249  real_indices.push_back(idx);
1250  for (size_t j = axis + indices_len - batch_dims_; j < out_index.size(); ++j) {
1251  real_indices.push_back(out_index[j]);
1252  }
1253  return a(real_indices);
1254  },
1255  name, tag);
1256  }
1257  } else if (mode == "fast") {
1258  LOG(WARNING) << "Fast mode segfaults when there are out-of-bounds indices. "
1259  "Make sure input indices are in bound";
1260  return compute(
1261  out_shape,
1262  [&](const ffi::Array<PrimVar>& out_index) {
1263  ffi::Array<PrimExpr> indices_position;
1264  for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1265  indices_position.push_back(out_index[j]);
1266  }
1267  ffi::Array<PrimExpr> real_indices;
1268  for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1269  real_indices.push_back(out_index[j]);
1270  }
1271  real_indices.push_back(get_index(indices_position));
1272  for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1273  real_indices.push_back(out_index[j]);
1274  }
1275  return a(real_indices);
1276  },
1277  name, tag);
1278  } else if (mode == "nan") {
1279  return compute(
1280  out_shape,
1281  [&](const ffi::Array<PrimVar>& out_index) {
1282  ffi::Array<PrimExpr> indices_position;
1283  for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1284  indices_position.push_back(out_index[j]);
1285  }
1286  ffi::Array<PrimExpr> real_indices;
1287  for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1288  real_indices.push_back(out_index[j]);
1289  }
1290  PrimExpr idx = get_index(indices_position);
1291  real_indices.push_back(idx);
1292  for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1293  real_indices.push_back(out_index[j]);
1294  }
1295  PrimExpr in_bounds = idx >= 0 && idx < axis_dim;
1296  return tvm::if_then_else(
1297  in_bounds, a(real_indices),
1298  tvm::tirx::MakeConst(PrimType(a->dtype), std::numeric_limits<float>::quiet_NaN()));
1299  },
1300  name, tag);
1301  } else { // mode == "wrap"
1302  return compute(
1303  out_shape,
1304  [&](const ffi::Array<PrimVar>& out_index) {
1305  ffi::Array<PrimExpr> indices_position;
1306  for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1307  indices_position.push_back(out_index[j]);
1308  }
1309  ffi::Array<PrimExpr> real_indices;
1310  for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1311  real_indices.push_back(out_index[j]);
1312  }
1313  auto idx = truncmod(truncmod(get_index(indices_position), axis_dim) + axis_dim, axis_dim);
1314  real_indices.push_back(idx);
1315  for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1316  real_indices.push_back(out_index[j]);
1317  }
1318  return a(real_indices);
1319  },
1320  name, tag);
1321  }
1322 }
1323 
1335 inline Tensor where(const Tensor& condition, const Tensor& x, const Tensor& y,
1336  std::string name = "T_where", std::string tag = kBroadcast) {
1337  TVM_FFI_ICHECK_EQ(x->dtype, y->dtype)
1338  << "x and y must have the same dtype: " << x->dtype << " vs " << y->dtype;
1339  auto get_out_shape = [&]() {
1340  auto bh1 = detail::BroadcastShape(x->shape, y->shape);
1341  ffi::Array<PrimExpr> common_shape1(bh1.common_shape.begin(), bh1.common_shape.end());
1342  auto bh2 = detail::BroadcastShape(condition->shape, common_shape1);
1343  ffi::Array<PrimExpr> common_shape2(bh2.common_shape.begin(), bh2.common_shape.end());
1344  return common_shape2;
1345  };
1346 
1347  auto oshape = get_out_shape();
1348 
1349  auto c_bh = detail::BroadcastShape(condition->shape, oshape);
1350  auto x_bh = detail::BroadcastShape(x->shape, oshape);
1351  auto y_bh = detail::BroadcastShape(y->shape, oshape);
1352 
1353  auto select = [&](tvm::ffi::Array<tvm::tirx::PrimVar> ovars) {
1354  auto c = condition(InputIndexFromBroadcast(ovars, condition, c_bh.vars1, c_bh.all_vars));
1355  auto true_val = x(InputIndexFromBroadcast(ovars, x, x_bh.vars1, x_bh.all_vars));
1356  auto false_val = y(InputIndexFromBroadcast(ovars, y, y_bh.vars1, y_bh.all_vars));
1357  return tvm::tirx::Select(c != 0, true_val, false_val);
1358  };
1359 
1360  return compute(oshape, select, name, tag);
1361 }
1362 
1375 inline Tensor repeat(const Tensor& x, int repeats, int axis, std::string name = "T_repeat",
1376  std::string tag = kBroadcast) {
1377  int ndim = static_cast<int>(x->shape.size());
1378  TVM_FFI_ICHECK(-ndim - 1 <= axis && axis <= ndim)
1379  << "repeat only accepts `axis` in [-data.ndim - 1, data.ndim]"
1380  << ", but got axis = " << axis << ", and data.ndim = " << ndim;
1381  TVM_FFI_ICHECK(repeats >= 1) << "repeat only accepts `repeats >= 1`"
1382  << ", but got repeats = " << repeats;
1383  if (axis < 0) {
1384  // Calculate offset from last dimension
1385  axis += ndim;
1386  }
1387  ffi::Array<PrimExpr> new_shape;
1388  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
1389  new_shape.push_back(x->shape[i]);
1390  }
1391  new_shape.push_back(repeats * x->shape[axis]);
1392  for (size_t i = axis + 1; i < x->shape.size(); ++i) {
1393  new_shape.push_back(x->shape[i]);
1394  }
1395 
1396  return compute(
1397  new_shape,
1398  [&](const ffi::Array<PrimVar>& indices) {
1399  ffi::Array<PrimExpr> idx;
1400  for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
1401  idx.push_back(indices[i]);
1402  }
1403  idx.push_back(indexdiv(indices[axis], repeats));
1404  for (size_t i = axis + 1; i < indices.size(); ++i) {
1405  idx.push_back(indices[i]);
1406  }
1407  return x(idx);
1408  },
1409  name, tag);
1410 }
1411 
1422 inline Tensor tile(const Tensor& x, ffi::Array<int64_t> reps, std::string name = "T_tile",
1423  std::string tag = kBroadcast) {
1424  size_t ndim = x->shape.size();
1425  size_t rdim = reps.size();
1426  size_t tdim = (ndim > rdim) ? ndim : rdim;
1427  ffi::Array<PrimExpr> data_shape;
1428  ffi::Array<PrimExpr> reps_shape;
1429  ffi::Array<PrimExpr> new_shape;
1430  if (ndim == rdim) {
1431  for (size_t i = 0; i < ndim; ++i) {
1432  data_shape.push_back(x->shape[i]);
1433  reps_shape.push_back(IntImm::Int64(reps[i]));
1434  }
1435  } else if (ndim > rdim) {
1436  for (size_t i = 0; i < ndim; ++i) data_shape.push_back(x->shape[i]);
1437  for (size_t i = 0; i < (ndim - rdim); ++i) reps_shape.push_back(1);
1438  for (size_t i = 0; i < rdim; ++i) reps_shape.push_back(IntImm::Int64(reps[i]));
1439  } else {
1440  for (size_t i = 0; i < (rdim - ndim); ++i) data_shape.push_back(1);
1441  for (size_t i = 0; i < ndim; ++i) data_shape.push_back(x->shape[i]);
1442  for (size_t i = 0; i < rdim; ++i) reps_shape.push_back(IntImm::Int64(reps[i]));
1443  }
1444  for (size_t i = 0; i < tdim; ++i) new_shape.push_back(data_shape[i] * reps_shape[i]);
1445 
1446  if (is_empty_shape(new_shape)) {
1447  return compute(
1448  new_shape,
1449  [&](const ffi::Array<PrimVar>& indices) { return tvm::cast(PrimType(x->dtype), 0); }, name,
1450  tag);
1451  } else {
1452  return compute(
1453  new_shape,
1454  [&](const ffi::Array<PrimVar>& indices) {
1455  ffi::Array<PrimExpr> idx;
1456  if (ndim >= rdim) {
1457  for (size_t i = 0; i < ndim; ++i) idx.push_back(indexmod(indices[i], x->shape[i]));
1458  } else {
1459  for (size_t i = 0; i < ndim; ++i)
1460  idx.push_back(indexmod(indices[rdim - ndim + i], x->shape[i]));
1461  }
1462  return x(idx);
1463  },
1464  name, tag);
1465  }
1466 }
1467 
1479 inline Tensor dyn_tile(const Tensor& x, ffi::Array<PrimExpr> new_shape, size_t rdim,
1480  std::string name = "T_tile", std::string tag = kBroadcast) {
1481  size_t ndim = x->shape.size();
1482  if (is_empty_shape(new_shape)) {
1483  return compute(
1484  new_shape,
1485  [&](const ffi::Array<PrimVar>& indices) { return tvm::cast(PrimType(x->dtype), 0); }, name,
1486  tag);
1487  } else {
1488  return compute(
1489  new_shape,
1490  [&](const ffi::Array<PrimVar>& indices) {
1491  ffi::Array<PrimExpr> idx;
1492  if (ndim >= rdim) {
1493  for (size_t i = 0; i < ndim; ++i) {
1494  idx.push_back(indexmod(indices[i], x->shape[i]));
1495  }
1496  } else {
1497  for (size_t i = 0; i < ndim; ++i) {
1498  idx.push_back(indexmod(indices[rdim - ndim + i], x->shape[i]));
1499  }
1500  }
1501  return x(idx);
1502  },
1503  name, tag);
1504  }
1505 }
1506 
1518 inline Tensor gather(const Tensor& data, int axis, const Tensor& indices,
1519  std::string name = "T_gather", std::string tag = kInjective) {
1520  size_t ndim_d = data->shape.size();
1521  size_t ndim_i = indices->shape.size();
1522  TVM_FFI_ICHECK_GE(ndim_d, 1) << "Cannot gather from a scalar.";
1523  TVM_FFI_ICHECK_EQ(ndim_d, ndim_i);
1524  if (axis < 0) {
1525  axis += ndim_d;
1526  }
1527  TVM_FFI_ICHECK_GE(axis, 0);
1528  TVM_FFI_ICHECK_LT(axis, ndim_d);
1529  if (indices->shape[axis].as<IntImmNode>()) {
1530  size_t indices_dim_i = static_cast<size_t>(GetConstInt(indices->shape[axis]));
1531  TVM_FFI_ICHECK_GE(indices_dim_i, 1);
1532  }
1533  // Index tensors are validated by integer element kind; vector lane encoding is irrelevant here.
1534  PrimType indices_ty = indices->dtype;
1535  TVM_FFI_ICHECK(indices_ty.MatchesCode(DLDataTypeCode::kDLInt, DLDataTypeCode::kDLUInt));
1536 
1537  ffi::Array<PrimExpr> out_shape;
1538  for (size_t i = 0; i < ndim_i; ++i) {
1539  out_shape.push_back(indices->shape[i]);
1540  }
1541 
1542  return compute(
1543  out_shape,
1544  [&](const ffi::Array<PrimVar>& out_index) {
1545  ffi::Array<PrimExpr> indices_position;
1546  for (size_t i = 0; i < ndim_i; ++i) {
1547  indices_position.push_back(out_index[i]);
1548  }
1549  ffi::Array<PrimExpr> real_indices;
1550  for (size_t i = 0; i < ndim_i; ++i) {
1551  if (i == static_cast<size_t>(axis)) {
1552  real_indices.push_back(indices(indices_position));
1553  } else {
1554  real_indices.push_back(indices_position[i]);
1555  }
1556  }
1557  return data(real_indices);
1558  },
1559  name, tag);
1560 }
1561 
1573 inline Tensor gather_nd(const Tensor& data, const Tensor& indices, int batch_dims = 0,
1574  std::string name = "T_gather_nd", std::string tag = kInjective) {
1575  size_t ndim_d = data->shape.size();
1576  size_t ndim_i = indices->shape.size();
1577  TVM_FFI_ICHECK_GE(ndim_i, 1) << "indices tensor must have at least 1 dimensions";
1578  size_t indices_dim0 = static_cast<size_t>(GetConstInt(indices->shape[0]));
1579  TVM_FFI_ICHECK_LE(indices_dim0, ndim_d) << "dim 0 of indices tensor must be no more "
1580  << "than dimensions of data tensor";
1581  ffi::Array<PrimExpr> out_shape;
1582  for (size_t i = 1; i < ndim_i; ++i) {
1583  out_shape.push_back(indices->shape[i]);
1584  }
1585  for (size_t i = indices_dim0 + batch_dims; i < ndim_d; ++i) {
1586  out_shape.push_back(data->shape[i]);
1587  }
1588  return compute(
1589  out_shape,
1590  [&](const ffi::Array<PrimVar>& out_index) {
1591  ffi::Array<PrimExpr> indices_position;
1592  indices_position.push_back(0);
1593  for (size_t i = 0; i < ndim_i - 1; ++i) {
1594  indices_position.push_back(out_index[i]);
1595  }
1596  ffi::Array<PrimExpr> real_indices;
1597  for (size_t i = 0; i < static_cast<size_t>(batch_dims); ++i) {
1598  real_indices.push_back(out_index[i]);
1599  }
1600  for (size_t i = 0; i < indices_dim0; ++i) {
1601  indices_position.Set(0, IntImm::Int32(i));
1602  // Index tensors are validated by integer element kind; vector lane encoding is
1603  // irrelevant for choosing whether an index cast is needed.
1604  PrimType indices_ty = indices->dtype;
1605  if (indices_ty.MatchesCode(DLDataTypeCode::kDLInt, DLDataTypeCode::kDLUInt)) {
1606  real_indices.push_back(indices(indices_position));
1607  } else {
1608  real_indices.push_back(tvm::cast(tvm::PrimType::Int(32), indices(indices_position)));
1609  }
1610  }
1611  if (real_indices.size() == ndim_d) {
1612  return data(real_indices);
1613  }
1614  for (size_t i = ndim_i - 1; i < out_index.size(); ++i) {
1615  real_indices.push_back(out_index[i]);
1616  }
1617  return data(real_indices);
1618  },
1619  name, tag);
1620 }
1621 
1638  bool trans_a = false, bool trans_b = false,
1639  std::string name = "T_matmul", std::string tag = kMatMul) {
1640  tvm::ffi::Array<tvm::PrimExpr> output_shape{A->shape[trans_a ? 1 : 0], B->shape[trans_b ? 0 : 1]};
1641  auto k = tvm::te::reduce_axis(tvm::Range{0, A->shape[trans_a ? 0 : 1]}, "k");
1642  auto l = [&](tvm::tirx::PrimVar i, tvm::tirx::PrimVar j) {
1643  return tvm::sum((trans_a ? A[k][i] : A[i][k]) * (trans_b ? B[j][k] : B[k][j]), {k});
1644  };
1645  return tvm::te::compute(output_shape, l, name, tag);
1646 }
1647 
1659 inline Tensor tensordot(const Tensor& A, const tvm::te::Tensor& B, int axes = 2,
1660  std::string name = "T_tensordot", std::string tag = kMatMul) {
1661  TVM_FFI_ICHECK_GE(A->shape.size(), axes);
1662  TVM_FFI_ICHECK_GE(B->shape.size(), axes);
1663 
1664  ffi::Array<PrimExpr> output_shape(A->shape.begin(), A->shape.end() + (-axes));
1665  for (auto it = B->shape.begin() + axes; it != B->shape.end(); ++it) output_shape.push_back(*it);
1666 
1667  ffi::Array<IterVar> iter_vars;
1668  for (int i = 0; i < axes; ++i)
1669  iter_vars.push_back(reduce_axis(Range(0, B->shape[i]), "k" + std::to_string(i)));
1670 
1671  auto func = [&A, &B, &iter_vars, axes](const ffi::Array<PrimVar>& input_indices) {
1672  ffi::Array<PrimExpr> A_indices;
1673  for (auto it = input_indices.begin(); it != input_indices.begin() + (A->shape.size() - axes);
1674  ++it) {
1675  A_indices.push_back((*it).as_or_throw<PrimExpr>());
1676  }
1677  for (auto& v : iter_vars) A_indices.push_back(v);
1678 
1679  ffi::Array<PrimExpr> B_indices;
1680  for (auto& v : iter_vars) B_indices.push_back(v);
1681 
1682  auto it = input_indices.begin() + (A->shape.size() - axes);
1683  for (; it != input_indices.end(); ++it) {
1684  B_indices.push_back((*it).as_or_throw<PrimExpr>());
1685  }
1686 
1687  // Some passes don't like reductions with empty axis, so avoid it here
1688  if (iter_vars.empty()) {
1689  return A(A_indices) * B(B_indices);
1690  } else {
1691  return sum(A(A_indices) * B(B_indices), iter_vars);
1692  }
1693  };
1694 
1695  return compute(output_shape, func, name, tag);
1696 }
1697 
1710 inline Tensor tensordot(const Tensor& A, const tvm::te::Tensor& B, ffi::Array<PrimExpr> A_axes,
1711  ffi::Array<PrimExpr> B_axes, std::string name = "T_tensordot",
1712  std::string tag = kMatMul) {
1713  TVM_FFI_ICHECK_EQ(A_axes.size(), B_axes.size());
1714 
1715  auto A_axes_val = GetConstIntValues(A_axes, "A_axes");
1716  auto B_axes_val = GetConstIntValues(B_axes, "B_axes");
1717 
1718  ffi::Array<PrimExpr> output_shape;
1719  for (unsigned i = 0; i < A->shape.size(); ++i)
1720  if (std::find(A_axes_val.begin(), A_axes_val.end(), i) == A_axes_val.end())
1721  output_shape.push_back(A->shape[i]);
1722  for (unsigned i = 0; i < B->shape.size(); ++i)
1723  if (std::find(B_axes_val.begin(), B_axes_val.end(), i) == B_axes_val.end())
1724  output_shape.push_back(B->shape[i]);
1725 
1726  ffi::Array<IterVar> iter_vars;
1727  for (unsigned i = 0; i < B_axes_val.size(); ++i)
1728  iter_vars.push_back(reduce_axis(Range(0, B->shape[B_axes_val[i]]), "k" + std::to_string(i)));
1729 
1730  auto func = [&A, &B, &iter_vars, A_axes_val,
1731  B_axes_val](const ffi::Array<PrimVar>& input_indices) {
1732  int idx_input = 0;
1733  ffi::Array<PrimExpr> A_indices;
1734  for (unsigned i = 0; i < A->shape.size(); ++i) {
1735  auto axes_pos = std::find(A_axes_val.begin(), A_axes_val.end(), i);
1736  if (axes_pos == A_axes_val.end()) {
1737  A_indices.push_back(input_indices[idx_input++]);
1738  } else {
1739  A_indices.push_back(iter_vars[axes_pos - A_axes_val.begin()]);
1740  }
1741  }
1742 
1743  ffi::Array<PrimExpr> B_indices;
1744  for (unsigned i = 0; i < B->shape.size(); ++i) {
1745  auto axes_pos = std::find(B_axes_val.begin(), B_axes_val.end(), i);
1746  if (axes_pos == B_axes_val.end()) {
1747  B_indices.push_back(input_indices[idx_input++]);
1748  } else {
1749  B_indices.push_back(iter_vars[axes_pos - B_axes_val.begin()]);
1750  }
1751  }
1752  return sum(A(A_indices) * B(B_indices), iter_vars);
1753  };
1754  return compute(output_shape, func, name, tag);
1755 }
1756 
1757 inline Tensor arange(const PrimExpr& start, const PrimExpr& stop, const PrimExpr& step,
1758  PrimType dtype, std::string name = "T_arange", std::string tag = kInjective) {
1759  arith::Analyzer analyzer;
1760  PrimExpr num_elem;
1761  PrimType start_ty = start.ty();
1762  PrimType stop_ty = stop.ty();
1763  PrimType step_ty = step.ty();
1764  bool is_all_int = start_ty.code() == DLDataTypeCode::kDLInt &&
1765  stop_ty.code() == DLDataTypeCode::kDLInt &&
1766  step_ty.code() == DLDataTypeCode::kDLInt;
1767  if (is_all_int && analyzer->CanProveGreaterEqual(step, 1)) {
1768  // fast path for integer arange when step is positive
1769  num_elem = tvm::floordiv((stop - start + step - 1), step);
1770  } else if (is_all_int && analyzer->CanProveLess(step, 0)) {
1771  // fast path for integer arange when step is negative
1772  num_elem = tvm::floordiv((start - stop - step - 1), -step);
1773  } else {
1774  // fallback path for non-integer or step of unknown sign
1775  num_elem = tvm::cast(PrimType(DefaultIndexType()),
1776  tvm::ceil(tvm::cast(tvm::PrimType::Float(32), stop - start) / step));
1777  }
1778  num_elem = analyzer->Simplify(num_elem);
1779 
1780  return compute(
1781  {num_elem},
1782  [&](const ffi::Array<PrimVar>& indices) {
1783  return tvm::cast(dtype, start + step * indices[0]);
1784  },
1785  name, tag);
1786 }
1787 
1798 inline ffi::Array<Tensor> meshgrid(const ffi::Array<Tensor>& inputs, const std::string& indexing,
1799  std::string name = "T_meshgrid", std::string tag = kInjective) {
1800  const bool cartesian_indexing = indexing == "xy" && inputs.size() >= 2;
1801  ffi::Array<PrimExpr> out_shape;
1802  for (size_t i = 0; i < inputs.size(); ++i) {
1803  const int src_index = (cartesian_indexing && i < 2) ? 1 - i : i;
1804  out_shape.push_back(inputs[src_index]->shape.size() == 0 ? 1 : inputs[src_index]->shape[0]);
1805  }
1806  ffi::Array<Tensor> result;
1807  for (size_t i = 0; i < inputs.size(); ++i) {
1808  result.push_back(compute(
1809  out_shape,
1810  [&](const ffi::Array<PrimVar>& indices) {
1811  const int src_index = (cartesian_indexing && i < 2) ? 1 - i : i;
1812  auto ndim = inputs[i]->GetShape().size();
1813  ffi::Array<PrimExpr> real_indices = {};
1814  if (ndim > 0) {
1815  real_indices = {indices[src_index]};
1816  }
1817  return inputs[i](real_indices);
1818  },
1819  name, tag));
1820  }
1821  return result;
1822 }
1823 
1834 inline Tensor layout_transform(const Tensor& src, const std::string& src_layout,
1835  const std::string& dst_layout,
1836  const std::string schedule_rule = "None",
1837  const std::string name = "T_layout_trans",
1838  const std::string tag = kInjective) {
1839  SLayout src_layout_struct(src_layout);
1840  SLayout dst_layout_struct(dst_layout);
1841 
1842  if (src_layout_struct.Equals(dst_layout_struct)) {
1843  return src;
1844  }
1845 
1846  TVM_FFI_ICHECK(src_layout_struct.defined() && dst_layout_struct.defined())
1847  << "cannot convert from/to undefined layout";
1848 
1849  auto layout_converter = tirx::SBijectiveLayout(src_layout_struct, dst_layout_struct);
1850  TVM_FFI_ICHECK(layout_converter.defined())
1851  << "cannot convert from " << src_layout << " to " << dst_layout;
1852 
1853  ffi::Array<PrimExpr> dst_shape = layout_converter.ForwardShape(src->shape);
1854 
1855  ffi::Map<ffi::String, ffi::Any> attrs = {{"schedule_rule", ffi::String(schedule_rule)},
1856  // Information about layouts needed for the schedule rule
1857  {"src_layout", ffi::String(src_layout)},
1858  {"dst_layout", ffi::String(dst_layout)},
1859  {"input_shape", src->shape}};
1860 
1861  return compute(
1862  dst_shape,
1863  [&](const ffi::Array<PrimVar>& dst_indices) {
1864  ffi::Array<PrimExpr> dst_indices_expr =
1865  dst_indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
1866  ffi::Array<PrimExpr> src_indices = layout_converter.BackwardIndex(dst_indices_expr);
1867  PrimExpr in_range = PrimExpr(1) > PrimExpr(0); // init with dtype=bool and value=true
1868  for (size_t i = 0; i < src.ndim(); ++i) {
1869  in_range = in_range && (src_indices[i] < src->shape[i]);
1870  }
1871  return if_then_else(in_range, src(src_indices),
1872  tvm::cast(PrimType(src->dtype), PrimExpr(0)));
1873  },
1874  name, tag, attrs);
1875 }
1876 
1878 inline void parse_auto_scheduler_layout(const ffi::String& layout, ffi::Array<PrimExpr>* shape,
1879  std::vector<std::string>* axes) {
1880  int32_t factor = 0;
1881  std::string axis = "";
1882  for (char c : std::string(layout)) {
1883  if (c >= 'A' && c <= 'z') {
1884  axis += c;
1885  if (factor != 0) {
1886  shape->push_back(factor);
1887  factor = 0;
1888  }
1889  } else if (c >= '0' && c <= '9') {
1890  factor = factor * 10 + c - '0';
1891  if (!axis.empty()) {
1892  axes->push_back(axis);
1893  axis = "";
1894  }
1895  } else {
1896  TVM_FFI_THROW(InternalError) << "Invalid layout " << layout;
1897  }
1898  }
1899  if (!axis.empty()) {
1900  axes->push_back(axis);
1901  }
1902 }
1903 
1915  const Tensor& src, const ffi::String& src_layout, const ffi::String& dst_layout,
1916  const ffi::String name = "T_auto_scheduler_layout_trans", const ffi::String tag = kInjective) {
1917  ffi::Array<PrimExpr> src_shape;
1918  std::vector<std::string> src_axes;
1919  ffi::Array<PrimExpr> dst_shape;
1920  std::vector<std::string> dst_axes;
1921 
1922  parse_auto_scheduler_layout(src_layout, &src_shape, &src_axes);
1923  parse_auto_scheduler_layout(dst_layout, &dst_shape, &dst_axes);
1924  return compute(
1925  dst_shape,
1926  [&](const ffi::Array<PrimVar>& dst_indices) {
1927  ffi::Array<PrimExpr> dst_indices_expr =
1928  dst_indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
1929  ffi::Array<PrimExpr> src_indices;
1930  for (const std::string& src_axis : src_axes) {
1931  PrimExpr src_index = 0;
1932  TVM_FFI_ICHECK_EQ(dst_indices_expr.size(), dst_axes.size());
1933  for (size_t i = 0; i < dst_axes.size(); ++i) {
1934  if (dst_axes[i] == src_axis) {
1935  src_index = src_index * dst_shape[i] + dst_indices_expr[i];
1936  }
1937  }
1938  src_indices.push_back(src_index);
1939  }
1940  return src(src_indices);
1941  },
1942  name, tag);
1943 }
1944 
1982  const Tensor& src, const tirx::IndexMap& index_map,
1983  const ffi::String name = "T_meta_schedule_layout_trans", const ffi::String tag = kInjective) {
1984  arith::Analyzer analyzer;
1985  ffi::Array<Range> iter_domain;
1986  iter_domain.reserve(src->shape.size());
1987  for (const PrimExpr& e : src->shape) {
1988  iter_domain.push_back(Range::FromMinExtent(IntImm(e.ty(), 0), e));
1989  }
1990  ffi::Array<PrimExpr> post_transform_shape = index_map->MapShape(src->shape, analyzer);
1991  return compute(
1992  post_transform_shape,
1993  [src, inv = index_map.Inverse(iter_domain, analyzer),
1994  &analyzer](const ffi::Array<PrimVar>& indices) -> PrimExpr {
1995  ffi::Array<PrimExpr> prim_indices =
1996  indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
1997  return src(inv->MapIndices(prim_indices, analyzer));
1998  },
1999  name, tag);
2000 }
2001 
2010 inline Tensor shape(const Tensor& src, PrimType dtype, const std::string name = "T_shape",
2011  const std::string tag = kInjective) {
2012  int ndim = static_cast<int>(src->shape.size());
2013  ffi::Array<PrimExpr> out_shape{ndim};
2014  return compute(
2015  out_shape,
2016  [&](const ffi::Array<PrimVar>& indices) {
2017  auto idx = indices[0];
2018  PrimExpr ret = 0;
2019  for (int i = 0; i < ndim; ++i) {
2020  ret = tvm::if_then_else(idx == i, src->shape[i], ret);
2021  }
2022  return tvm::cast(dtype, ret);
2023  },
2024  name, tag);
2025 }
2026 
2027 inline Tensor shape(const Tensor& src, DLDataType dtype, const std::string name = "T_shape",
2028  const std::string tag = kInjective) {
2029  return shape(src, PrimType(dtype), name, tag);
2030 }
2031 
2040 inline te::Tensor tensor_size(const te::Tensor& src, PrimType dtype,
2041  const std::string& name = "tensor_size",
2042  const std::string& tag = kInjective) {
2043  int ndim = static_cast<int>(src->shape.size());
2044  ffi::Array<PrimExpr> out_tensor_size = {};
2045  return compute(
2046  out_tensor_size,
2047  [&](const ffi::Array<PrimVar>& indices) {
2048  PrimExpr ret = 1;
2049  for (int i = 0; i < ndim; ++i) {
2050  ret *= src->shape[i];
2051  }
2052  return tvm::cast(dtype, ret);
2053  },
2054  name, tag);
2055 }
2056 
2057 inline te::Tensor tensor_size(const te::Tensor& src, DLDataType dtype,
2058  const std::string& name = "tensor_size",
2059  const std::string& tag = kInjective) {
2060  return tensor_size(src, PrimType(dtype), name, tag);
2061 }
2062 
2077 inline Tensor one_hot(const Tensor& indices, const PrimExpr on_value, const PrimExpr off_value,
2078  int depth, int axis, PrimType dtype,
2079  ffi::Array<PrimExpr> oshape = ffi::Array<PrimExpr>(),
2080  const std::string name = "T_one_hot", const std::string tag = kInjective) {
2081  int true_axis = (axis == -1) ? indices->shape.size() : axis;
2082  if (oshape.size() == 0) {
2083  int ndim = indices->shape.size() + 1;
2084  int indices_index = 0;
2085  for (int i = 0; i < ndim; i++) {
2086  if (i == true_axis) {
2087  oshape.push_back(IntImm::Int32(depth));
2088  } else {
2089  oshape.push_back(indices->shape[indices_index++]);
2090  }
2091  }
2092  }
2093 
2094  PrimExpr on_value_cast = cast(dtype, on_value);
2095  PrimExpr off_value_cast = cast(dtype, off_value);
2096  return compute(
2097  oshape,
2098  [&](const ffi::Array<PrimVar>& iter_vars) {
2099  ffi::Array<PrimVar> indices_indices;
2100  for (size_t i = 0; i < iter_vars.size(); i++) {
2101  if (static_cast<int>(i) == true_axis) {
2102  continue;
2103  }
2104 
2105  indices_indices.push_back(iter_vars[i]);
2106  }
2107 
2108  auto idx = iter_vars[true_axis];
2109  return tirx::Select(indices(indices_indices) == idx.as_or_throw<PrimExpr>(), on_value_cast,
2110  off_value_cast);
2111  },
2112  name, tag);
2113 }
2114 
2115 inline Tensor one_hot(const Tensor& indices, const PrimExpr on_value, const PrimExpr off_value,
2116  int depth, int axis, DLDataType dtype,
2117  ffi::Array<PrimExpr> oshape = ffi::Array<PrimExpr>(),
2118  const std::string name = "T_one_hot", const std::string tag = kInjective) {
2119  return one_hot(indices, on_value, off_value, depth, axis, PrimType(dtype), std::move(oshape),
2120  name, tag);
2121 }
2122 
2133 inline Tensor sparse_to_dense(const Tensor& sparse_indices,
2134  const ffi::Array<PrimExpr>& output_shape, const Tensor& sparse_values,
2135  const PrimExpr& default_value,
2136  const std::string name = "T_sparse_to_dense",
2137  const std::string tag = kInjective) {
2138  // Sparse indices are validated by signed integer element kind; lane encoding is irrelevant here.
2139  TVM_FFI_ICHECK_EQ(sparse_indices->dtype.code(), DLDataTypeCode::kDLInt)
2140  << "sparse_indices only accepts integer values";
2141  TVM_FFI_ICHECK_LE(sparse_indices->shape.size(), 3)
2142  << "sparse_indices tensor should be 0D, 1D, or 2D only";
2143  TVM_FFI_ICHECK_LE(sparse_values->shape.size(), 2)
2144  << "sparse_values tensor should be 0D or 1D only";
2145 
2146  const auto rank_sparse_indices = static_cast<int>(sparse_indices->shape.size());
2147  ffi::Array<PrimExpr> oshape;
2148  for (auto l : output_shape) {
2149  oshape.push_back(l);
2150  }
2151  return compute(
2152  oshape,
2153  [&](const ffi::Array<PrimVar>& indices) {
2154  PrimExpr ret = default_value;
2155  if (0 == rank_sparse_indices) {
2156  ret = if_then_else(indices[0].as_or_throw<PrimExpr>() == sparse_indices(),
2157  sparse_values(), ret);
2158  } else if (1 == rank_sparse_indices) {
2159  for (int j = 0; j < GetConstInt(sparse_indices->shape[0]); j++) {
2160  ret = if_then_else(indices[0].as_or_throw<PrimExpr>() == sparse_indices[j],
2161  sparse_values[j], ret);
2162  }
2163  } else {
2164  for (int j = 0; j < GetConstInt(sparse_indices->shape[0]); j++) {
2165  PrimExpr aggregate_condition;
2166  for (int k = 0; k < GetConstInt(sparse_indices->shape[1]); k++) {
2167  PrimExpr comparision = indices[k].as_or_throw<PrimExpr>() == sparse_indices[j][k];
2168  aggregate_condition = 0 == k ? comparision : aggregate_condition && comparision;
2169  }
2170  ret = if_then_else(aggregate_condition, sparse_values[j], ret);
2171  }
2172  }
2173  return ret;
2174  },
2175  name, tag);
2176 }
2177 
2190 inline Tensor matrix_set_diag(const Tensor& input, const Tensor& diagonal, int k1, int k2,
2191  bool super_diag_right_align, bool sub_diag_right_align,
2192  const std::string name = "T_matrix_set_diag",
2193  const std::string tag = kInjective) {
2194  size_t ndim = input->shape.size() - 1;
2195 
2196  bool only_one_diagonal = k1 == k2;
2197 
2198  return compute(
2199  input->shape,
2200  [&](const ffi::Array<PrimVar>& iter_vars) {
2201  auto get_diag = [&]() {
2202  ffi::Array<PrimExpr> diagonal_indices;
2203  PrimExpr k, offset = 0;
2204  for (size_t i = 0; i < ndim - 1; i++) {
2205  diagonal_indices.push_back(iter_vars[i]);
2206  }
2207  if (only_one_diagonal) {
2208  k = k1;
2209  } else {
2210  // Determining which diagonal/sub-diagonal/super-diagonal it is
2211  k = iter_vars[ndim] - iter_vars[ndim - 1];
2212  diagonal_indices.push_back(k2 - k);
2213 
2214  // Calculating the offset in diagonal tensor for this diagonal
2215  auto get_offset = [&](PrimExpr M, PrimExpr N) {
2216  // offset = max_diagonal_length - diagonal_length
2217  return diagonal->shape[diagonal->shape.size() - 1] - if_then_else(M < N, M, N);
2218  };
2219  offset = if_then_else(
2220  k >= 0,
2221  super_diag_right_align ? get_offset(input->shape[ndim] - k, input->shape[ndim - 1])
2222  : 0,
2223  sub_diag_right_align ? get_offset(input->shape[ndim], input->shape[ndim - 1] + k)
2224  : 0);
2225  }
2226  diagonal_indices.push_back(if_then_else(k >= 0, iter_vars[ndim - 1], iter_vars[ndim]) +
2227  offset);
2228  return diagonal(diagonal_indices);
2229  };
2230  return if_then_else((PrimExpr)iter_vars[ndim] - iter_vars[ndim - 1] >= k1,
2231  if_then_else((PrimExpr)iter_vars[ndim] - iter_vars[ndim - 1] <= k2,
2232  get_diag(), input(iter_vars)),
2233  input(iter_vars));
2234  },
2235  name, tag);
2236 }
2237 
2246 inline Tensor adv_index(const Tensor& data, const ffi::Array<Tensor>& indices,
2247  const std::string name = "advanced_index",
2248  const std::string tag = kInjective) {
2249  TVM_FFI_ICHECK_LE(indices.size(), data->shape.size()) << "too many indices for data!";
2250  ffi::Array<PrimExpr> oshape;
2251  ffi::Array<PrimExpr> broadcast_shape;
2252  ffi::Array<Tensor> bindices;
2253 
2254  broadcast_shape = indices[0]->shape;
2255  for (size_t i = 1; i < indices.size(); ++i) {
2256  auto bh = detail::BroadcastShape(broadcast_shape, indices[i]->shape);
2257  broadcast_shape = ffi::Array<PrimExpr>(bh.common_shape.begin(), bh.common_shape.end());
2258  }
2259  if (indices.size() == 1) {
2260  // quick path
2261  bindices = indices;
2262  } else {
2263  // Do broadcast for indices
2264  for (size_t i = 0; i < indices.size(); ++i) {
2265  bindices.push_back(broadcast_to(indices[i], broadcast_shape));
2266  }
2267  }
2268 
2269  for (const auto& dim : broadcast_shape) {
2270  oshape.push_back(dim);
2271  }
2272  for (size_t i = indices.size(); i < data->shape.size(); ++i) {
2273  oshape.push_back(data->shape[i]);
2274  }
2275 
2276  return compute(
2277  oshape,
2278  [&](const ffi::Array<PrimVar>& iter_var) {
2279  ffi::Array<PrimExpr> tensor_indices;
2280  for (size_t i = 0; i < broadcast_shape.size(); ++i) {
2281  tensor_indices.push_back(iter_var[i]);
2282  }
2283  ffi::Array<PrimExpr> real_indices;
2284  for (size_t i = 0; i < bindices.size(); ++i) {
2285  real_indices.push_back(bindices[i](tensor_indices));
2286  }
2287  for (size_t i = broadcast_shape.size(); i < iter_var.size(); ++i) {
2288  real_indices.push_back(iter_var[i]);
2289  }
2290 
2291  return data(real_indices);
2292  },
2293  name, tag);
2294 }
2295 
2296 namespace relax {
2297 // relax dynamic slice
2299  const te::Tensor& end, const te::Tensor& strides,
2300  ffi::Array<PrimExpr> output_shape,
2301  std::string name = "T_strided_slice_dynamic",
2302  std::string tag = kInjective) {
2303  const size_t num_dynamic_axes = x.ndim();
2304  TVM_FFI_ICHECK_EQ(begin.ndim(), 1);
2305  TVM_FFI_ICHECK_EQ(end.ndim(), 1);
2306  TVM_FFI_ICHECK_EQ(strides.ndim(), 1);
2307  const auto* len_begin = begin->shape[0].as<IntImmNode>();
2308  const auto* len_end = end->shape[0].as<IntImmNode>();
2309  const auto* len_strides = strides->shape[0].as<IntImmNode>();
2310  TVM_FFI_ICHECK(len_begin);
2311  TVM_FFI_ICHECK(len_end);
2312  TVM_FFI_ICHECK(len_strides);
2313  TVM_FFI_ICHECK_EQ(len_begin->value, num_dynamic_axes);
2314  TVM_FFI_ICHECK_EQ(len_end->value, num_dynamic_axes);
2315  TVM_FFI_ICHECK_EQ(len_strides->value, num_dynamic_axes);
2316 
2317  return te::compute(
2318  output_shape,
2319  [&](const ffi::Array<tvm::tirx::PrimVar>& indices) {
2320  ffi::Array<PrimExpr> real_indices;
2321  for (size_t i = 0; i < num_dynamic_axes; ++i) {
2322  auto ind = IntImm::Int64(i);
2323  real_indices.push_back(indices[i] * strides(ind) + tvm::min(begin(ind), x->shape[i] - 1));
2324  }
2325  return x(real_indices);
2326  },
2327  name, tag);
2328 }
2329 
2330 } // namespace relax
2331 
2332 } // namespace topi
2333 } // namespace tvm
2334 #endif // TVM_TOPI_TRANSFORM_H_
Algebra expression simplifications.
Broadcast op constructions.
Managed reference class to FloatImmNode.
Definition: expr.h:441
Constant integer literals in the program.
Definition: expr.h:361
int64_t value
the Internal value.
Definition: expr.h:364
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
static IntImm Int64(int64_t value, Span span=Span())
Construct a scalar int64 constant.
Definition: expr.h:411
Typed reference/view over any Expr whose ExprNode::ty is PrimType.
Definition: base_expr.h:354
Definition: base_expr.h:113
TVM_FFI_INLINE DLDataTypeCode code() const
Definition: base_expr.h:150
static PrimType Float(int bits, int lanes=1)
Construct a floating-point type with fixed lanes.
static PrimType Int(int bits, int lanes=1)
Construct a signed integer type with fixed lanes.
TVM_FFI_INLINE bool MatchesCode(Codes... codes) const
Check whether the dtype code matches any of the provided DLPack codes.
Definition: base_expr.h:184
Range container
Definition: expr.h:484
static Range FromMinExtent(PrimExpr min, PrimExpr extent, Span span=Span())
construct a new range with min and extent The corresponding constructor is removed,...
ExpectedType ty() const
Definition: base_expr.h:333
Managed reference to AnalyzerObj.
Definition: analyzer.h:913
Managed Tensor. The array is backed by reference counted blocks.
Definition: tensor.h:49
Node to represent a tensor.
Definition: tensor.h:70
Tensor structure representing a possible input, or intermediate computation result.
Definition: tensor.h:100
size_t ndim() const
Definition: tensor.h:212
Definition: index_map.h:192
IndexMap Inverse(ffi::Array< Range > initial_ranges) const
Generate the inverse mapping using a fresh analyzer.
Checked scalar view over a VarNode.
Definition: var.h:127
Bijective function mapping for data layout transformation. Given two SLayout, SBijectiveLayout build ...
Definition: data_layout.h:386
Managed reference to SLayoutNode.
Definition: data_layout.h:126
bool Equals(const SLayout &rhs) const
Whether the two layouts are equal.
Definition: data_layout.h:332
Managed reference to SelectNode.
Definition: expr.h:526
A variable node in the IR.
Definition: var.h:49
ffi::String name_hint
The hint to the variable name.
Definition: var.h:55
Utility functions for handling constants in TVM expressions.
SLayout expression to describe the data organization of a tensor. And SBijectiveLayout to mapping two...
Detail broadcast.
Defines a remapping of buffer indices.
Base expr nodes in TVM.
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...
const Op & select()
DLDataType DefaultIndexType()
Definition: buffer.h:52
const Op & zero()
PrimExpr MakeConst(PrimType dtype, ValueType value, Span span=Span())
Make a const value with certain data type.
Definition: op.h:1012
te::Tensor dynamic_strided_slice(const te::Tensor &x, const te::Tensor &begin, const te::Tensor &end, const te::Tensor &strides, ffi::Array< PrimExpr > output_shape, std::string name="T_strided_slice_dynamic", std::string tag=kInjective)
Definition: transform.h:2298
PrimExpr GetLength(PrimExpr begin, PrimExpr end, PrimExpr stride, PrimExpr extent, bool assume_inbound=true)
Definition: transform.h:689
Tensor sum(const Tensor &data, const ffi::Optional< ffi::Array< int64_t >> &axis, bool keepdims=false, bool atleast1d=false)
Creates an operation that sums array elements over a given axis.
Definition: reduction.h:337
Tensor sequence_mask(const Tensor &data, const Tensor &valid_length, double mask_value, int axis, std::string name="T_sequence_mask", std::string tag=kInjective)
Mask the out-of-boundary elements of each sequence.
Definition: transform.h:1105
Tensor transpose(const Tensor &x, ffi::Optional< ffi::Array< int64_t >> opt_axes, std::string name="T_transpose", std::string tag=kInjective)
Permute the dimensions of an array.
Definition: transform.h:205
Tensor gather_nd(const Tensor &data, const Tensor &indices, int batch_dims=0, std::string name="T_gather_nd", std::string tag=kInjective)
Gather elements from a n-dimension array.
Definition: transform.h:1573
int64_t StaticCanonicalizeIndex(int64_t index, int64_t extent, int64_t stride)
Definition: transform.h:670
Tensor strided_slice_with_axes(const Tensor &x, const ffi::Array< ffi::Optional< IntImm >> &begin, const ffi::Array< ffi::Optional< IntImm >> &end, const ffi::Array< IntImm > &strides, const ffi::Array< int64_t > &axes, std::string slice_mode="end", std::string name="T_strided_slice_with_axes", std::string tag=kInjective)
strided_slice of a tensor
Definition: transform.h:902
Tensor reshape(const Tensor &x, ffi::Array< PrimExpr > newshape, std::string name="T_reshape", std::string tag=kInjective)
Reshape a tensor.
Definition: transform.h:329
Tensor shape(const Tensor &src, PrimType dtype, const std::string name="T_shape", const std::string tag=kInjective)
Get the shape of input tensor.
Definition: transform.h:2010
Tensor squeeze(const Tensor &x, ffi::Optional< ffi::Array< int64_t >> opt_axes, bool atleast1d=false, std::string name="T_squeeze", std::string tag=kInjective)
Remove size 1 dimensions from the shape of a tensor. The removed dimensions must have a constant size...
Definition: transform.h:415
tvm::te::Tensor broadcast_to(const tvm::te::Tensor &t, const tvm::ffi::Array< tvm::PrimExpr > &output_shape, std::string name="T_broadcast_to", std::string tag=kBroadcast)
Creates an operation that broadcasts a tensor into a compatible shape according to numpy's rules.
Definition: broadcast.h:48
constexpr auto kBroadcast
Definition: tags.h:36
constexpr auto kInjective
Definition: tags.h:33
Tensor stack(const ffi::Array< Tensor > &inputs, int axis=0, std::string name="T_stack", std::string tag=kInjective)
Join a sequence of tensors along a new axis.
Definition: transform.h:541
Tensor arange(const PrimExpr &start, const PrimExpr &stop, const PrimExpr &step, PrimType dtype, std::string name="T_arange", std::string tag=kInjective)
Definition: transform.h:1757
Tensor auto_scheduler_layout_transform(const Tensor &src, const ffi::String &src_layout, const ffi::String &dst_layout, const ffi::String name="T_auto_scheduler_layout_trans", const ffi::String tag=kInjective)
Transform the auto-scheduler generated layout according to src_layout and dst_layout.
Definition: transform.h:1914
PrimExpr CanonicalizeIndex(PrimExpr index, PrimExpr extent, PrimExpr stride)
Definition: transform.h:679
void parse_auto_scheduler_layout(const ffi::String &layout, ffi::Array< PrimExpr > *shape, std::vector< std::string > *axes)
Utility function for auto_scheduler_layout_transform.
Definition: transform.h:1878
ffi::Array< Tensor > split_n_sections(const Tensor &x, int num_sections, int axis, std::string name="T_split_sections", std::string tag=kInjective)
Split a tensor into a number of sub-tensors.
Definition: transform.h:1008
Tensor expand_dims(const Tensor &x, int axis, int num_newaxis=1, std::string name="T_expand_dims", std::string tag=kBroadcast)
Creates an operation to insert new dimensions of length 1.
Definition: transform.h:156
Tensor sparse_to_dense(const Tensor &sparse_indices, const ffi::Array< PrimExpr > &output_shape, const Tensor &sparse_values, const PrimExpr &default_value, const std::string name="T_sparse_to_dense", const std::string tag=kInjective)
Get a dense tensor.
Definition: transform.h:2133
Tensor sliding_window(const Tensor &x, int axis, ffi::Array< int64_t > window_shape, ffi::Array< int64_t > strides, std::string name="T_sliding_window", std::string tag="")
Creates an operation to slide a window over the input x.
Definition: transform.h:76
Tensor unravel_index(const Tensor &x, const Tensor &shape, std::string name="T_unravel", std::string tag=kInjective)
Converts a flat index or array of flat indices into a tuple of coordinate arrays.
Definition: transform.h:367
Tensor layout_transform(const Tensor &src, const std::string &src_layout, const std::string &dst_layout, const std::string schedule_rule="None", const std::string name="T_layout_trans", const std::string tag=kInjective)
Transform the layout according to src_layout and dst_layout.
Definition: transform.h:1834
Tensor adv_index(const Tensor &data, const ffi::Array< Tensor > &indices, const std::string name="advanced_index", const std::string tag=kInjective)
Numpy style advanced indexing with tensor.
Definition: transform.h:2246
ffi::Array< PrimExpr > StridedSliceOutputShape(const ffi::Array< PrimExpr > &ishape, const ffi::Array< ffi::Optional< IntImm >> &begin, const ffi::Array< ffi::Optional< IntImm >> &end, const ffi::Array< IntImm > &strides, const ffi::Array< int64_t > &axes, const std::string &slice_mode)
Calculate the output shape of strided_slice, the entry point for Relax type relation.
Definition: transform.h:868
Tensor concatenate(const ffi::Array< Tensor > &inputs, int axis=0, std::string name="T_concat", std::string tag=kInjective)
Join a sequence of tensors along an existing axis.
Definition: transform.h:481
ffi::Array< Tensor > meshgrid(const ffi::Array< Tensor > &inputs, const std::string &indexing, std::string name="T_meshgrid", std::string tag=kInjective)
Produce grids by expanding input over dimensions defined by other inputs.
Definition: transform.h:1798
constexpr auto kMatMul
Definition: tags.h:37
Tensor dyn_tile(const Tensor &x, ffi::Array< PrimExpr > new_shape, size_t rdim, std::string name="T_tile", std::string tag=kBroadcast)
Creates an operation to tile elements of an array.
Definition: transform.h:1479
te::Tensor tensor_size(const te::Tensor &src, PrimType dtype, const std::string &name="tensor_size", const std::string &tag=kInjective)
Get the size of input tensor.
Definition: transform.h:2040
Tensor reverse_sequence(const Tensor &x, const Tensor &seq_lengths, int seq_axis=1, int batch_axis=0, std::string name="T_reverse_sequence", std::string tag=kInjective)
Reverse the tensor for variable length slices. Input is first sliced along batch axis and then elemen...
Definition: transform.h:264
Tensor one_hot(const Tensor &indices, const PrimExpr on_value, const PrimExpr off_value, int depth, int axis, PrimType dtype, ffi::Array< PrimExpr > oshape=ffi::Array< PrimExpr >(), const std::string name="T_one_hot", const std::string tag=kInjective)
Returns a one-hot tensor where the locations repsented by indices take value on_value,...
Definition: transform.h:2077
ffi::Array< Tensor > split_indices_array(const Tensor &x, ffi::Array< PrimExpr > split_indices, int axis, std::string name="T_split", std::string tag=kInjective)
Split a tensor into multiple sub-tensors.
Definition: transform.h:587
Tensor tensordot(const Tensor &A, const tvm::te::Tensor &B, int axes=2, std::string name="T_tensordot", std::string tag=kMatMul)
A generalization of matrix multiplication to tensors.
Definition: transform.h:1659
Tensor meta_schedule_layout_transform(const Tensor &src, const tirx::IndexMap &index_map, const ffi::String name="T_meta_schedule_layout_trans", const ffi::String tag=kInjective)
Transform the meta-schedule generated layout according to TIR's IndexMap.
Definition: transform.h:1981
te::Tensor dynamic_strided_slice_with_axes(const te::Tensor &x, const ffi::Array< PrimExpr > &begin, const ffi::Array< PrimExpr > &end, const ffi::Array< PrimExpr > &strides, const ffi::Array< int64_t > &axes, bool assume_inbound=true, std::string name="T_dynamic_strided_slice_with_axes", std::string tag=kInjective)
strided_slice of a tensor where begin/end/stride can be mixed static and dynamic
Definition: transform.h:716
Tensor take(const Tensor &a, const Tensor &indices, int batch_dims, std::string mode="fast", std::string name="T_take", std::string tag=kInjective)
Take elements from an flattened input array when axis is None.
Definition: transform.h:1044
PrimExpr DynamicCanonicalizeIndex(PrimExpr index, PrimExpr extent, PrimExpr stride)
Definition: transform.h:652
tvm::te::Tensor matmul(const tvm::te::Tensor &A, const tvm::te::Tensor &B, bool trans_a=false, bool trans_b=false, std::string name="T_matmul", std::string tag=kMatMul)
Creates an operation that calculates a matrix multiplication (row-major notation): A(i,...
Definition: transform.h:1637
Tensor dynamic_strided_slice(const Tensor &x, const ffi::Array< PrimExpr > &begin, const ffi::Array< PrimExpr > &end, const ffi::Array< PrimExpr > &strides, bool assume_inbound=true, std::string name="T_dynamic_strided_slice", std::string tag=kInjective)
strided_slice of a tensor where begin/end/stride can be mixed static and dynamic
Definition: transform.h:773
Tensor strided_slice(const Tensor &x, const ffi::Array< ffi::Optional< IntImm >> &begin, const ffi::Array< ffi::Optional< IntImm >> &end, const ffi::Array< IntImm > &strides, std::string slice_mode="end", std::string name="T_strided_slice", std::string tag=kInjective)
strided_slice of a tensor
Definition: transform.h:965
Tensor matrix_set_diag(const Tensor &input, const Tensor &diagonal, int k1, int k2, bool super_diag_right_align, bool sub_diag_right_align, const std::string name="T_matrix_set_diag", const std::string tag=kInjective)
Returns a tensor with the diagonal of input tensor replaced with the provided diagonals.
Definition: transform.h:2190
Tensor where(const Tensor &condition, const Tensor &x, const Tensor &y, std::string name="T_where", std::string tag=kBroadcast)
Return the elements, either from x or y, depending on the condition.
Definition: transform.h:1335
Tensor cast(const Tensor &x, PrimType type, std::string name, std::string tag)
Cast each element of x to the given type. If expr is scalar and type is a corresponding vector type,...
Definition: elemwise.h:287
Tensor gather(const Tensor &data, int axis, const Tensor &indices, std::string name="T_gather", std::string tag=kInjective)
Gather values along given axis from given indices.
Definition: transform.h:1518
Tensor tile(const Tensor &x, ffi::Array< int64_t > reps, std::string name="T_tile", std::string tag=kBroadcast)
Creates an operation to tile elements of an array.
Definition: transform.h:1422
Tensor repeat(const Tensor &x, int repeats, int axis, std::string name="T_repeat", std::string tag=kBroadcast)
Creates an operation to repeat elements of an array.
Definition: transform.h:1375
An object that builds and maintains block scope and StmtSref mapping for Dependence analysis.
Definition: analyzer.h:40
PrimExpr ceildiv(PrimExpr a, PrimExpr b, Span span=Span())
compute ceil(a / b)
PrimExpr ret(PrimExpr value, Span span=Span())
Return the value.
PrimExpr max(PrimExpr a, PrimExpr b, Span span=Span())
take maximum of two values
PrimExpr max_value(PrimType dtype, Span span=Span())
PrimExpr truncmod(PrimExpr a, PrimExpr b, Span span=Span())
compute the remainder of truncdiv
PrimExpr if_then_else(PrimExpr cond, PrimExpr true_value, PrimExpr false_value, Span span=Span())
Conditional expression.
PrimExpr ceil(PrimExpr x, Span span=Span())
Calculate ceil(x)
PrimExpr cast(PrimType t, PrimExpr value, Span span=Span())
cast value to type.
PrimExpr indexdiv(PrimExpr a, PrimExpr b, Span span=Span())
compute floor(a / b) where a and b are non-negative.
PrimExpr min(PrimExpr a, PrimExpr b, Span span=Span())
take minimum of two values
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.
PrimExpr floordiv(PrimExpr a, PrimExpr b, Span span=Span())
compute floor(a / b)
Operation node can generate one or multiple Tensors.
Index ravel and unraval operations.
Utility functions for strided_slice op.
Tag definitions.
Utility functions for handling tensor.
TIR expressions.
Common operators defined for Expr.
Variables in the TIR.