tvm
Loading...
Searching...
No Matches
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/ir/prim/expr.h>
30#include <tvm/te/operation.h>
31#include <tvm/tirx/index_map.h>
32#include <tvm/topi/broadcast.h>
38#include <tvm/topi/tags.h>
39
40#include <algorithm>
41#include <iterator>
42#include <limits>
43#include <string>
44#include <unordered_set>
45#include <utility>
46#include <vector>
47
48#include "tvm/ffi/dtype.h"
49#include "tvm/ir/expr.h"
50#include "tvm/tirx/op.h"
51#include "tvm/tirx/var.h"
52
53namespace tvm {
54namespace topi {
55
56using namespace tvm::te;
57using namespace topi::detail;
58
76inline 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.
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) {
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
156inline 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
205inline 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
264inline 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
329inline 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(
342 [&](const ffi::Array<PrimVar>& indices) { return tvm::cast(PrimType(x->dtype), 0); }, name,
343 tag);
344 } else {
345 return compute(
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
367inline 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;
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
415inline 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
481inline 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 }
498 for (size_t i = 1; i < axis_sizes.size(); ++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
541inline 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
587inline 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) {
611 if (i == begin_ids.size() - 1) {
613 } else {
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::PrimVar>();
654 auto extent_var = extent.as<tvm::tirx::PrimVar>();
655
656 if (idx_var && extent_var && (*idx_var)->name == (*extent_var)->name) {
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
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
679inline 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(
684 StaticCanonicalizeIndex(GetConstInt(index), GetConstInt(extent), GetConstInt(stride)));
685 }
686 return DynamicCanonicalizeIndex(index, extent, stride);
687}
688
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());
726
727 for (const auto& axis_imm : axes) {
728 int axis = static_cast<int>(axis_imm);
730 }
731
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
773inline 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();
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
789 for (size_t i = 0; i < num_slice_axes; ++i) {
790 // Dynamic scalar tensor loads cannot be simplified while inferring shape.
791 if (!te::IsTensorLoad(begin[i]) && !te::IsTensorLoad(end[i]) && !te::IsTensorLoad(strides[i])) {
792 out_shape.push_back(
793 analyzer->Simplify(GetLength(begin[i], end[i], strides[i], x->shape[i], assume_inbound)));
794 } else {
795 out_shape.push_back(tvm::tirx::PrimVar("dim"));
796 }
797 }
798
799 for (size_t i = num_slice_axes; i < src_tensor_dim; ++i) {
800 out_shape.push_back(x->shape[i]);
801 }
802
803 return te::compute(
804 out_shape,
805 [&](const ffi::Array<tvm::tirx::PrimVar>& indices) {
806 ffi::Array<PrimExpr> real_indices;
807 for (size_t i = 0; i < num_slice_axes; ++i) {
808 real_indices.push_back(indices[i] * strides[i] + tvm::min(begin[i], x->shape[i] - 1));
809 }
810 // keep input dim
811 for (size_t i = num_slice_axes; i < src_tensor_dim; ++i) {
812 real_indices.push_back(indices[i]);
813 }
814 return x(real_indices);
815 },
816 name, tag);
817}
818
834 const te::Tensor& end, const te::Tensor& strides,
835 bool assume_inbound = true,
836 std::string name = "T_strided_slice_dynamic",
837 std::string tag = topi::kInjective) {
838 PrimType index_ty = begin->shape[0].ty();
839 const int64_t num_dynamic_axes = begin->shape[0].as<IntImmNode>()->value;
841 TVM_FFI_ICHECK_EQ(strides->shape[0].as<IntImmNode>()->value, num_dynamic_axes);
842
843 ffi::Array<PrimExpr> begin_expr, end_expr, strides_expr;
844 for (int64_t i = 0; i < num_dynamic_axes; ++i) {
845 auto ind = IntImm(index_ty, i);
846 begin_expr.push_back(begin(ind));
847 end_expr.push_back(end(ind));
848 strides_expr.push_back(strides(ind));
849 }
850 return dynamic_strided_slice(x, begin_expr, end_expr, strides_expr, assume_inbound, name, tag);
851}
852
867inline ffi::Array<PrimExpr> StridedSliceOutputShape(const ffi::Array<PrimExpr>& ishape,
868 const ffi::Array<ffi::Optional<IntImm>>& begin,
869 const ffi::Array<ffi::Optional<IntImm>>& end,
870 const ffi::Array<IntImm>& strides,
871 const ffi::Array<int64_t>& axes,
872 const std::string& slice_mode) {
873 TVM_FFI_ICHECK(axes.size() == begin.size() && axes.size() == end.size() &&
874 axes.size() == strides.size());
875 std::vector<int64_t> begin_vec, end_vec, strides_vec;
876 std::tie(begin_vec, end_vec, strides_vec) = ConvertToVec(begin, end, strides, slice_mode);
878 (begin.size() > 0 && begin[0].has_value()) ? begin[0].value().ty() : PrimType::Int(64);
880 StridedSliceCanonicalizeBegin(ishape, begin_vec, strides_vec, axes, index_ty, slice_mode);
881 return StridedSliceOutputShape(ishape, begin_vec, end_vec, strides_vec, axes, slice_mode,
882 begin_canonicalized, true);
883}
884
902 const Tensor& x, const ffi::Array<ffi::Optional<IntImm>>& begin,
903 const ffi::Array<ffi::Optional<IntImm>>& end, const ffi::Array<IntImm>& strides,
904 const ffi::Array<int64_t>& axes, std::string slice_mode = "end",
905 std::string name = "T_strided_slice_with_axes", std::string tag = kInjective) {
906 const int64_t src_tensor_dim = static_cast<int64_t>(x->shape.size());
907 TVM_FFI_ICHECK(static_cast<int64_t>(axes.size()) <= src_tensor_dim);
908 TVM_FFI_ICHECK(axes.size() == begin.size() && axes.size() == end.size() &&
909 axes.size() == strides.size());
910
911 // Normalize negative axes
912 ffi::Array<int64_t> normalized_axes;
913 for (size_t i = 0; i < axes.size(); ++i) {
914 int64_t axis = axes[i];
915 if (axis < 0) {
916 axis += src_tensor_dim;
917 }
918 TVM_FFI_ICHECK(axis >= 0 && axis < src_tensor_dim)
919 << "Axis " << axes[i] << " is out of bounds for tensor with " << src_tensor_dim
920 << " dimensions";
921 normalized_axes.push_back(axis);
922 }
923
924 std::vector<int64_t> begin_vec, end_vec, strides_vec;
925 std::tie(begin_vec, end_vec, strides_vec) = ConvertToVec(begin, end, strides, slice_mode);
926
928 (begin.size() > 0 && begin[0].has_value()) ? begin[0].value().ty() : PrimType::Int(64);
929 auto begin_expr = StridedSliceCanonicalizeBegin(x->shape, begin_vec, strides_vec, normalized_axes,
931 auto out_shape = StridedSliceOutputShape(x->shape, begin_vec, end_vec, strides_vec,
933
934 return te::compute(
935 out_shape,
936 [&](const ffi::Array<tirx::PrimVar>& indices) {
937 ffi::Array<PrimExpr> real_indices;
938 for (size_t i = 0; i < out_shape.size(); ++i) real_indices.push_back(indices[i]);
939 for (size_t i = 0; i < normalized_axes.size(); ++i) {
941 auto stride = IntImm(strides[i]->ty.as_or_throw<PrimType>(), strides_vec[i]);
942 PrimExpr ind = indices[ax] * stride + begin_expr[i];
943 real_indices.Set(ax, ind);
944 }
945 return x(real_indices);
946 },
947 name, tag);
948}
949
964inline Tensor strided_slice(const Tensor& x, const ffi::Array<ffi::Optional<IntImm>>& begin,
965 const ffi::Array<ffi::Optional<IntImm>>& end,
966 const ffi::Array<IntImm>& strides, std::string slice_mode = "end",
967 std::string name = "T_strided_slice", std::string tag = kInjective) {
968 size_t src_tensor_dim = static_cast<size_t>(x->shape.size());
969 ffi::Array<int64_t> axes;
970 for (size_t i = 0; i < src_tensor_dim; ++i) axes.push_back(i);
971 ffi::Array<ffi::Optional<IntImm>> begin_full(begin);
972 ffi::Array<ffi::Optional<IntImm>> end_full(end);
973 ffi::Array<IntImm> strides_full(strides);
974
976 (begin.size() > 0 && begin[0].has_value()) ? begin[0].value().ty() : PrimType::Int(64);
977 const IntImm one = IntImm(index_ty, 1);
978 const IntImm zero = IntImm(index_ty, 0);
979 const IntImm max_range = max_value(index_ty).as_or_throw<IntImm>();
980
981 for (size_t i = strides.size(); i < src_tensor_dim; ++i) {
982 strides_full.push_back(one);
983 }
984 for (size_t i = begin.size(); i < src_tensor_dim; ++i) {
985 begin_full.push_back(strides_full[i]->value > 0 ? zero : max_range);
986 }
987 for (size_t i = end.size(); i < src_tensor_dim; ++i) {
988 end_full.push_back(strides_full[i]->value < 0 ? zero : max_range);
989 }
990
992 tag);
993}
994
1007inline ffi::Array<Tensor> split_n_sections(const Tensor& x, int num_sections, int axis,
1008 std::string name = "T_split_sections",
1009 std::string tag = kInjective) {
1010 if (axis < 0) {
1011 axis += static_cast<int>(x->shape.size());
1012 }
1013 TVM_FFI_ICHECK_LT(axis, x->shape.size()) << "axis out of bounds";
1014
1015 auto src_axis_size = x->shape[axis];
1016
1017 TVM_FFI_ICHECK_GT(num_sections, 0) << "Slice count must be > 0";
1018
1019 ffi::Array<PrimExpr> split_indices;
1021 for (int i = 0; i < num_sections; ++i) {
1022 // region at index 0 is added by split()
1023 if (i != 0) {
1024 split_indices.push_back(seg_size * i);
1025 }
1026 }
1027
1028 return split_indices_array(x, split_indices, axis, name, tag);
1029}
1030
1043inline Tensor take(const Tensor& a, const Tensor& indices, int batch_dims,
1044 std::string mode = "fast", std::string name = "T_take",
1045 std::string tag = kInjective) {
1046 ffi::Array<PrimExpr> a_shape = a->shape;
1047 ffi::Array<PrimExpr> out_shape = indices->shape;
1048 PrimExpr a_size = 1;
1049 for (size_t i = 0; i < a_shape.size(); ++i) {
1050 a_size = a_size * a_shape[i];
1051 }
1052
1053 if (mode == "clip") {
1054 return compute(
1055 out_shape,
1056 [&](const ffi::Array<PrimVar>& out_index) {
1057 auto idx = tvm::min(tvm::max(0, indices(out_index)), a_size - 1);
1058 return a(UnravelIndex(idx, a_shape));
1059 },
1060 name, tag);
1061 } else if (mode == "fast") {
1062 LOG(WARNING) << "Fast mode segfaults when there are out-of-bounds indices. "
1063 "Make sure input indices are in bound";
1064 return compute(
1065 out_shape,
1066 [&](const ffi::Array<PrimVar>& out_index) {
1067 return a(UnravelIndex(indices(out_index), a_shape));
1068 },
1069 name, tag);
1070 } else if (mode == "nan") {
1071 return compute(
1072 out_shape,
1073 [&](const ffi::Array<PrimVar>& out_index) {
1074 auto idx = tvm::if_then_else(
1075 indices(out_index) < 0 || indices(out_index) >= a_size,
1076 tvm::FloatImm(tvm::PrimType(a->dtype), std::numeric_limits<float>::quiet_NaN()),
1077 indices(out_index));
1078 return a(UnravelIndex(idx, a_shape));
1079 },
1080 name, tag);
1081 } else { // mode == "wrap"
1082 return compute(
1083 out_shape,
1084 [&](const ffi::Array<PrimVar>& out_index) {
1085 auto idx = truncmod(truncmod(indices(out_index), a_size) + a_size, a_size);
1086 return a(UnravelIndex(idx, a_shape));
1087 },
1088 name, tag);
1089 }
1090}
1091
1104inline Tensor sequence_mask(const Tensor& data, const Tensor& valid_length, double mask_value,
1105 int axis, std::string name = "T_sequence_mask",
1106 std::string tag = kInjective) {
1107 TVM_FFI_ICHECK(axis == 0 || axis == 1) << "axis must be either 0 or 1";
1108 TVM_FFI_ICHECK_EQ(valid_length->shape.size(), 1)
1109 << "valid_length must have ndim=1, i.e., (batch_size,).";
1110 auto length_dim = data->shape[axis];
1111 auto batch_dim = data->shape[1 - axis];
1112 ffi::Array<PrimExpr> out_shape = data->shape;
1113 Tensor out = compute(
1114 out_shape,
1115 [&](const ffi::Array<PrimVar>& out_index) {
1116 ffi::Array<PrimExpr> len_index;
1117 auto tid = out_index[axis];
1118 auto bid = out_index[1 - axis];
1119 len_index.push_back(bid);
1122 tvm::tirx::MakeConst(PrimType(data->dtype), mask_value), data(out_index));
1123 return ret;
1124 },
1125 name, tag);
1126 return out;
1127}
1128
1143inline Tensor take(const Tensor& a, ffi::Variant<Tensor, PrimExpr> indices, int batch_dims,
1144 int axis, std::string mode = "fast", std::string name = "T_take",
1145 std::string tag = kInjective) {
1146 if (axis < 0) {
1147 axis += static_cast<int>(a->shape.size());
1148 }
1149 TVM_FFI_ICHECK_GE(axis, 0) << "axis out of bounds";
1150 TVM_FFI_ICHECK_LT(axis, a->shape.size()) << "axis out of bounds";
1151 auto axis_dim = a->shape[axis];
1152 auto indices_shape = [&]() -> ffi::Array<PrimExpr> {
1153 if (auto tensor = indices.as<TensorNode>()) {
1154 return tensor->shape;
1155 } else {
1156 return {};
1157 }
1158 }();
1159
1160 int indices_len = static_cast<int>(indices_shape.size());
1161
1162 int batch_dims_ = batch_dims;
1163 if (batch_dims_ != 0) {
1164 TVM_FFI_ICHECK_GE(batch_dims_, -indices_len) << "batch_dims out of bounds";
1165 TVM_FFI_ICHECK_LE(batch_dims_, indices_len) << "batch_dims out of bounds";
1166
1167 if (batch_dims_ < 0) {
1169 }
1170
1171 TVM_FFI_ICHECK_LT(batch_dims_, a->shape.size()) << "batch_dims out of bounds";
1172 TVM_FFI_ICHECK_LE(batch_dims_, axis) << "batch_dims must be less than or equal to axis";
1173 for (int i = 0; i < batch_dims_; ++i) {
1174 auto addr1 = a->shape[i];
1175 auto addr2 = indices_shape[i];
1176 auto v1 = static_cast<IntImm*>(&addr1)->get()->value;
1177 auto v2 = static_cast<IntImm*>(&addr2)->get()->value;
1178 TVM_FFI_ICHECK_EQ(v1, v2) << "a.shape[" << i << "] should be equal to indices.shape[" << i
1179 << "]";
1180 }
1181 }
1182
1183 // The result shape is a.shape[:axis] + indices.shape[batch_dims:] +
1184 // a.shape[axis + 1:].
1185
1186 ffi::Array<PrimExpr> out_shape;
1187 for (int i = 0; i < batch_dims_; ++i) {
1188 out_shape.push_back(a->shape[i]);
1189 }
1190 for (int i = batch_dims_; i < axis; ++i) {
1191 out_shape.push_back(a->shape[i]);
1192 }
1193 for (int i = batch_dims_; i < indices_len; ++i) {
1194 out_shape.push_back(indices_shape[i]);
1195 }
1196 for (size_t i = axis + 1; i < a->shape.size(); ++i) {
1197 out_shape.push_back(a->shape[i]);
1198 }
1199
1200 auto get_index = [&](const ffi::Array<PrimExpr>& indices_position) -> PrimExpr {
1201 if (auto tensor = indices.as<Tensor>()) {
1202 return tensor.value()(indices_position);
1203 } else if (auto prim = indices.as<PrimExpr>()) {
1205 return prim.value();
1206 } else {
1207 TVM_FFI_THROW(InternalError) << "Variant did not contain either allowed type";
1208 }
1209 };
1210
1211 if (mode == "clip") {
1212 if (batch_dims_ == 0) {
1213 return compute(
1214 out_shape,
1215 [&](const ffi::Array<PrimVar>& out_index) {
1216 ffi::Array<PrimExpr> indices_position;
1217 for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1218 indices_position.push_back(out_index[j]);
1219 }
1220 ffi::Array<PrimExpr> real_indices;
1221 for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1222 real_indices.push_back(out_index[j]);
1223 }
1225 real_indices.push_back(idx);
1226 for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1227 real_indices.push_back(out_index[j]);
1228 }
1229 return a(real_indices);
1230 },
1231 name, tag);
1232 } else {
1233 return compute(
1234 out_shape,
1235 [&](const ffi::Array<PrimVar>& out_index) {
1236 ffi::Array<PrimExpr> indices_position;
1237 for (size_t j = 0; j < static_cast<size_t>(batch_dims_); ++j) {
1238 indices_position.push_back(out_index[j]);
1239 }
1240 for (size_t j = axis; j < static_cast<size_t>(axis + indices_len - batch_dims_); ++j) {
1241 indices_position.push_back(out_index[j]);
1242 }
1243 ffi::Array<PrimExpr> real_indices;
1244 for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1245 real_indices.push_back(out_index[j]);
1246 }
1248 real_indices.push_back(idx);
1249 for (size_t j = axis + indices_len - batch_dims_; j < out_index.size(); ++j) {
1250 real_indices.push_back(out_index[j]);
1251 }
1252 return a(real_indices);
1253 },
1254 name, tag);
1255 }
1256 } else if (mode == "fast") {
1257 LOG(WARNING) << "Fast mode segfaults when there are out-of-bounds indices. "
1258 "Make sure input indices are in bound";
1259 return compute(
1260 out_shape,
1261 [&](const ffi::Array<PrimVar>& out_index) {
1262 ffi::Array<PrimExpr> indices_position;
1263 for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1264 indices_position.push_back(out_index[j]);
1265 }
1266 ffi::Array<PrimExpr> real_indices;
1267 for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1268 real_indices.push_back(out_index[j]);
1269 }
1271 for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1272 real_indices.push_back(out_index[j]);
1273 }
1274 return a(real_indices);
1275 },
1276 name, tag);
1277 } else if (mode == "nan") {
1278 return compute(
1279 out_shape,
1280 [&](const ffi::Array<PrimVar>& out_index) {
1281 ffi::Array<PrimExpr> indices_position;
1282 for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1283 indices_position.push_back(out_index[j]);
1284 }
1285 ffi::Array<PrimExpr> real_indices;
1286 for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1287 real_indices.push_back(out_index[j]);
1288 }
1290 real_indices.push_back(idx);
1291 for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1292 real_indices.push_back(out_index[j]);
1293 }
1294 PrimExpr in_bounds = idx >= 0 && idx < axis_dim;
1295 return tvm::if_then_else(
1297 tvm::tirx::MakeConst(PrimType(a->dtype), std::numeric_limits<float>::quiet_NaN()));
1298 },
1299 name, tag);
1300 } else { // mode == "wrap"
1301 return compute(
1302 out_shape,
1303 [&](const ffi::Array<PrimVar>& out_index) {
1304 ffi::Array<PrimExpr> indices_position;
1305 for (size_t j = axis; j < static_cast<size_t>(axis + indices_len); ++j) {
1306 indices_position.push_back(out_index[j]);
1307 }
1308 ffi::Array<PrimExpr> real_indices;
1309 for (size_t j = 0; j < static_cast<size_t>(axis); ++j) {
1310 real_indices.push_back(out_index[j]);
1311 }
1313 real_indices.push_back(idx);
1314 for (size_t j = axis + indices_len; j < out_index.size(); ++j) {
1315 real_indices.push_back(out_index[j]);
1316 }
1317 return a(real_indices);
1318 },
1319 name, tag);
1320 }
1321}
1322
1334inline Tensor where(const Tensor& condition, const Tensor& x, const Tensor& y,
1335 std::string name = "T_where", std::string tag = kBroadcast) {
1336 TVM_FFI_ICHECK_EQ(x->dtype, y->dtype)
1337 << "x and y must have the same dtype: " << x->dtype << " vs " << y->dtype;
1338 auto get_out_shape = [&]() {
1339 auto bh1 = detail::BroadcastShape(x->shape, y->shape);
1340 ffi::Array<PrimExpr> common_shape1(bh1.common_shape.begin(), bh1.common_shape.end());
1341 auto bh2 = detail::BroadcastShape(condition->shape, common_shape1);
1342 ffi::Array<PrimExpr> common_shape2(bh2.common_shape.begin(), bh2.common_shape.end());
1343 return common_shape2;
1344 };
1345
1346 auto oshape = get_out_shape();
1347
1348 auto c_bh = detail::BroadcastShape(condition->shape, oshape);
1349 auto x_bh = detail::BroadcastShape(x->shape, oshape);
1350 auto y_bh = detail::BroadcastShape(y->shape, oshape);
1351
1352 auto select = [&](tvm::ffi::Array<tvm::tirx::PrimVar> ovars) {
1353 auto c = condition(InputIndexFromBroadcast(ovars, condition, c_bh.vars1, c_bh.all_vars));
1354 auto true_val = x(InputIndexFromBroadcast(ovars, x, x_bh.vars1, x_bh.all_vars));
1355 auto false_val = y(InputIndexFromBroadcast(ovars, y, y_bh.vars1, y_bh.all_vars));
1356 return tvm::prim::Select(c != 0, true_val, false_val);
1357 };
1358
1359 return compute(oshape, select, name, tag);
1360}
1361
1374inline Tensor repeat(const Tensor& x, int repeats, int axis, std::string name = "T_repeat",
1375 std::string tag = kBroadcast) {
1376 int ndim = static_cast<int>(x->shape.size());
1377 TVM_FFI_ICHECK(-ndim - 1 <= axis && axis <= ndim)
1378 << "repeat only accepts `axis` in [-data.ndim - 1, data.ndim]"
1379 << ", but got axis = " << axis << ", and data.ndim = " << ndim;
1380 TVM_FFI_ICHECK(repeats >= 1) << "repeat only accepts `repeats >= 1`"
1381 << ", but got repeats = " << repeats;
1382 if (axis < 0) {
1383 // Calculate offset from last dimension
1384 axis += ndim;
1385 }
1386 ffi::Array<PrimExpr> new_shape;
1387 for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
1388 new_shape.push_back(x->shape[i]);
1389 }
1390 new_shape.push_back(repeats * x->shape[axis]);
1391 for (size_t i = axis + 1; i < x->shape.size(); ++i) {
1392 new_shape.push_back(x->shape[i]);
1393 }
1394
1395 return compute(
1396 new_shape,
1397 [&](const ffi::Array<PrimVar>& indices) {
1398 ffi::Array<PrimExpr> idx;
1399 for (size_t i = 0; i < static_cast<size_t>(axis); ++i) {
1400 idx.push_back(indices[i]);
1401 }
1402 idx.push_back(indexdiv(indices[axis], repeats));
1403 for (size_t i = axis + 1; i < indices.size(); ++i) {
1404 idx.push_back(indices[i]);
1405 }
1406 return x(idx);
1407 },
1408 name, tag);
1409}
1410
1421inline Tensor tile(const Tensor& x, ffi::Array<int64_t> reps, std::string name = "T_tile",
1422 std::string tag = kBroadcast) {
1423 size_t ndim = x->shape.size();
1424 size_t rdim = reps.size();
1425 size_t tdim = (ndim > rdim) ? ndim : rdim;
1426 ffi::Array<PrimExpr> data_shape;
1427 ffi::Array<PrimExpr> reps_shape;
1428 ffi::Array<PrimExpr> new_shape;
1429 if (ndim == rdim) {
1430 for (size_t i = 0; i < ndim; ++i) {
1431 data_shape.push_back(x->shape[i]);
1432 reps_shape.push_back(IntImm::Int64(reps[i]));
1433 }
1434 } else if (ndim > rdim) {
1435 for (size_t i = 0; i < ndim; ++i) data_shape.push_back(x->shape[i]);
1436 for (size_t i = 0; i < (ndim - rdim); ++i) reps_shape.push_back(1);
1437 for (size_t i = 0; i < rdim; ++i) reps_shape.push_back(IntImm::Int64(reps[i]));
1438 } else {
1439 for (size_t i = 0; i < (rdim - ndim); ++i) data_shape.push_back(1);
1440 for (size_t i = 0; i < ndim; ++i) data_shape.push_back(x->shape[i]);
1441 for (size_t i = 0; i < rdim; ++i) reps_shape.push_back(IntImm::Int64(reps[i]));
1442 }
1443 for (size_t i = 0; i < tdim; ++i) new_shape.push_back(data_shape[i] * reps_shape[i]);
1444
1445 if (is_empty_shape(new_shape)) {
1446 return compute(
1447 new_shape,
1448 [&](const ffi::Array<PrimVar>& indices) { return tvm::cast(PrimType(x->dtype), 0); }, name,
1449 tag);
1450 } else {
1451 return compute(
1452 new_shape,
1453 [&](const ffi::Array<PrimVar>& indices) {
1454 ffi::Array<PrimExpr> idx;
1455 if (ndim >= rdim) {
1456 for (size_t i = 0; i < ndim; ++i) idx.push_back(indexmod(indices[i], x->shape[i]));
1457 } else {
1458 for (size_t i = 0; i < ndim; ++i)
1459 idx.push_back(indexmod(indices[rdim - ndim + i], x->shape[i]));
1460 }
1461 return x(idx);
1462 },
1463 name, tag);
1464 }
1465}
1466
1478inline Tensor dyn_tile(const Tensor& x, ffi::Array<PrimExpr> new_shape, size_t rdim,
1479 std::string name = "T_tile", std::string tag = kBroadcast) {
1480 size_t ndim = x->shape.size();
1481 if (is_empty_shape(new_shape)) {
1482 return compute(
1483 new_shape,
1484 [&](const ffi::Array<PrimVar>& indices) { return tvm::cast(PrimType(x->dtype), 0); }, name,
1485 tag);
1486 } else {
1487 return compute(
1488 new_shape,
1489 [&](const ffi::Array<PrimVar>& indices) {
1490 ffi::Array<PrimExpr> idx;
1491 if (ndim >= rdim) {
1492 for (size_t i = 0; i < ndim; ++i) {
1493 idx.push_back(indexmod(indices[i], x->shape[i]));
1494 }
1495 } else {
1496 for (size_t i = 0; i < ndim; ++i) {
1497 idx.push_back(indexmod(indices[rdim - ndim + i], x->shape[i]));
1498 }
1499 }
1500 return x(idx);
1501 },
1502 name, tag);
1503 }
1504}
1505
1517inline Tensor gather(const Tensor& data, int axis, const Tensor& indices,
1518 std::string name = "T_gather", std::string tag = kInjective) {
1519 size_t ndim_d = data->shape.size();
1520 size_t ndim_i = indices->shape.size();
1521 TVM_FFI_ICHECK_GE(ndim_d, 1) << "Cannot gather from a scalar.";
1523 if (axis < 0) {
1524 axis += ndim_d;
1525 }
1526 TVM_FFI_ICHECK_GE(axis, 0);
1528 if (indices->shape[axis].as<IntImmNode>()) {
1529 size_t indices_dim_i = static_cast<size_t>(GetConstInt(indices->shape[axis]));
1531 }
1532 // Index tensors are validated by integer element kind; vector lane encoding is irrelevant here.
1533 PrimType indices_ty = indices->dtype;
1534 TVM_FFI_ICHECK(indices_ty.MatchesCode(DLDataTypeCode::kDLInt, DLDataTypeCode::kDLUInt));
1535
1536 ffi::Array<PrimExpr> out_shape;
1537 for (size_t i = 0; i < ndim_i; ++i) {
1538 out_shape.push_back(indices->shape[i]);
1539 }
1540
1541 return compute(
1542 out_shape,
1543 [&](const ffi::Array<PrimVar>& out_index) {
1544 ffi::Array<PrimExpr> indices_position;
1545 for (size_t i = 0; i < ndim_i; ++i) {
1546 indices_position.push_back(out_index[i]);
1547 }
1548 ffi::Array<PrimExpr> real_indices;
1549 for (size_t i = 0; i < ndim_i; ++i) {
1550 if (i == static_cast<size_t>(axis)) {
1551 real_indices.push_back(indices(indices_position));
1552 } else {
1553 real_indices.push_back(indices_position[i]);
1554 }
1555 }
1556 return data(real_indices);
1557 },
1558 name, tag);
1559}
1560
1572inline Tensor gather_nd(const Tensor& data, const Tensor& indices, int batch_dims = 0,
1573 std::string name = "T_gather_nd", std::string tag = kInjective) {
1574 size_t ndim_d = data->shape.size();
1575 size_t ndim_i = indices->shape.size();
1576 TVM_FFI_ICHECK_GE(ndim_i, 1) << "indices tensor must have at least 1 dimensions";
1577 size_t indices_dim0 = static_cast<size_t>(GetConstInt(indices->shape[0]));
1578 TVM_FFI_ICHECK_LE(indices_dim0, ndim_d) << "dim 0 of indices tensor must be no more "
1579 << "than dimensions of data tensor";
1580 ffi::Array<PrimExpr> out_shape;
1581 for (size_t i = 1; i < ndim_i; ++i) {
1582 out_shape.push_back(indices->shape[i]);
1583 }
1584 for (size_t i = indices_dim0 + batch_dims; i < ndim_d; ++i) {
1585 out_shape.push_back(data->shape[i]);
1586 }
1587 return compute(
1588 out_shape,
1589 [&](const ffi::Array<PrimVar>& out_index) {
1590 ffi::Array<PrimExpr> indices_position;
1591 indices_position.push_back(0);
1592 for (size_t i = 0; i < ndim_i - 1; ++i) {
1593 indices_position.push_back(out_index[i]);
1594 }
1595 ffi::Array<PrimExpr> real_indices;
1596 for (size_t i = 0; i < static_cast<size_t>(batch_dims); ++i) {
1597 real_indices.push_back(out_index[i]);
1598 }
1599 for (size_t i = 0; i < indices_dim0; ++i) {
1601 // Index tensors are validated by integer element kind; vector lane encoding is
1602 // irrelevant for choosing whether an index cast is needed.
1603 PrimType indices_ty = indices->dtype;
1604 if (indices_ty.MatchesCode(DLDataTypeCode::kDLInt, DLDataTypeCode::kDLUInt)) {
1605 real_indices.push_back(indices(indices_position));
1606 } else {
1608 }
1609 }
1610 if (real_indices.size() == ndim_d) {
1611 return data(real_indices);
1612 }
1613 for (size_t i = ndim_i - 1; i < out_index.size(); ++i) {
1614 real_indices.push_back(out_index[i]);
1615 }
1616 return data(real_indices);
1617 },
1618 name, tag);
1619}
1620
1637 bool trans_a = false, bool trans_b = false,
1638 std::string name = "T_matmul", std::string tag = kMatMul) {
1639 tvm::ffi::Array<tvm::PrimExpr> output_shape{A->shape[trans_a ? 1 : 0], B->shape[trans_b ? 0 : 1]};
1640 auto k = tvm::te::reduce_axis(tvm::Range{0, A->shape[trans_a ? 0 : 1]}, "k");
1642 return tvm::sum((trans_a ? A[k][i] : A[i][k]) * (trans_b ? B[j][k] : B[k][j]), {k});
1643 };
1644 return tvm::te::compute(output_shape, l, name, tag);
1645}
1646
1658inline Tensor tensordot(const Tensor& A, const tvm::te::Tensor& B, int axes = 2,
1659 std::string name = "T_tensordot", std::string tag = kMatMul) {
1660 TVM_FFI_ICHECK_GE(A->shape.size(), axes);
1661 TVM_FFI_ICHECK_GE(B->shape.size(), axes);
1662
1663 ffi::Array<PrimExpr> output_shape(A->shape.begin(), A->shape.end() + (-axes));
1664 for (auto it = B->shape.begin() + axes; it != B->shape.end(); ++it) output_shape.push_back(*it);
1665
1666 ffi::Array<IterVar> iter_vars;
1667 for (int i = 0; i < axes; ++i)
1668 iter_vars.push_back(reduce_axis(Range(0, B->shape[i]), "k" + std::to_string(i)));
1669
1670 auto func = [&A, &B, &iter_vars, axes](const ffi::Array<PrimVar>& input_indices) {
1671 ffi::Array<PrimExpr> A_indices;
1672 for (auto it = input_indices.begin(); it != input_indices.begin() + (A->shape.size() - axes);
1673 ++it) {
1674 A_indices.push_back((*it).as_or_throw<PrimExpr>());
1675 }
1676 for (auto& v : iter_vars) A_indices.push_back(v);
1677
1678 ffi::Array<PrimExpr> B_indices;
1679 for (auto& v : iter_vars) B_indices.push_back(v);
1680
1681 auto it = input_indices.begin() + (A->shape.size() - axes);
1682 for (; it != input_indices.end(); ++it) {
1683 B_indices.push_back((*it).as_or_throw<PrimExpr>());
1684 }
1685
1686 // Some passes don't like reductions with empty axis, so avoid it here
1687 if (iter_vars.empty()) {
1688 return A(A_indices) * B(B_indices);
1689 } else {
1690 return sum(A(A_indices) * B(B_indices), iter_vars);
1691 }
1692 };
1693
1694 return compute(output_shape, func, name, tag);
1695}
1696
1709inline Tensor tensordot(const Tensor& A, const tvm::te::Tensor& B, ffi::Array<PrimExpr> A_axes,
1710 ffi::Array<PrimExpr> B_axes, std::string name = "T_tensordot",
1711 std::string tag = kMatMul) {
1712 TVM_FFI_ICHECK_EQ(A_axes.size(), B_axes.size());
1713
1714 auto A_axes_val = GetConstIntValues(A_axes, "A_axes");
1715 auto B_axes_val = GetConstIntValues(B_axes, "B_axes");
1716
1717 ffi::Array<PrimExpr> output_shape;
1718 for (unsigned i = 0; i < A->shape.size(); ++i)
1719 if (std::find(A_axes_val.begin(), A_axes_val.end(), i) == A_axes_val.end())
1720 output_shape.push_back(A->shape[i]);
1721 for (unsigned i = 0; i < B->shape.size(); ++i)
1722 if (std::find(B_axes_val.begin(), B_axes_val.end(), i) == B_axes_val.end())
1723 output_shape.push_back(B->shape[i]);
1724
1725 ffi::Array<IterVar> iter_vars;
1726 for (unsigned i = 0; i < B_axes_val.size(); ++i)
1727 iter_vars.push_back(reduce_axis(Range(0, B->shape[B_axes_val[i]]), "k" + std::to_string(i)));
1728
1729 auto func = [&A, &B, &iter_vars, A_axes_val,
1730 B_axes_val](const ffi::Array<PrimVar>& input_indices) {
1731 int idx_input = 0;
1732 ffi::Array<PrimExpr> A_indices;
1733 for (unsigned i = 0; i < A->shape.size(); ++i) {
1734 auto axes_pos = std::find(A_axes_val.begin(), A_axes_val.end(), i);
1735 if (axes_pos == A_axes_val.end()) {
1736 A_indices.push_back(input_indices[idx_input++]);
1737 } else {
1738 A_indices.push_back(iter_vars[axes_pos - A_axes_val.begin()]);
1739 }
1740 }
1741
1742 ffi::Array<PrimExpr> B_indices;
1743 for (unsigned i = 0; i < B->shape.size(); ++i) {
1744 auto axes_pos = std::find(B_axes_val.begin(), B_axes_val.end(), i);
1745 if (axes_pos == B_axes_val.end()) {
1746 B_indices.push_back(input_indices[idx_input++]);
1747 } else {
1748 B_indices.push_back(iter_vars[axes_pos - B_axes_val.begin()]);
1749 }
1750 }
1751 return sum(A(A_indices) * B(B_indices), iter_vars);
1752 };
1753 return compute(output_shape, func, name, tag);
1754}
1755
1756inline Tensor arange(const PrimExpr& start, const PrimExpr& stop, const PrimExpr& step,
1757 PrimType dtype, std::string name = "T_arange", std::string tag = kInjective) {
1760 PrimType start_ty = start.ty();
1761 PrimType stop_ty = stop.ty();
1762 PrimType step_ty = step.ty();
1763 bool is_all_int = start_ty.code() == DLDataTypeCode::kDLInt &&
1764 stop_ty.code() == DLDataTypeCode::kDLInt &&
1765 step_ty.code() == DLDataTypeCode::kDLInt;
1766 if (is_all_int && analyzer->CanProveGreaterEqual(step, 1)) {
1767 // fast path for integer arange when step is positive
1768 num_elem = tvm::floordiv((stop - start + step - 1), step);
1769 } else if (is_all_int && analyzer->CanProveLess(step, 0)) {
1770 // fast path for integer arange when step is negative
1771 num_elem = tvm::floordiv((start - stop - step - 1), -step);
1772 } else {
1773 // fallback path for non-integer or step of unknown sign
1775 tvm::ceil(tvm::cast(tvm::PrimType::Float(32), stop - start) / step));
1776 }
1777 num_elem = analyzer->Simplify(num_elem);
1778
1779 return compute(
1780 {num_elem},
1781 [&](const ffi::Array<PrimVar>& indices) {
1782 return tvm::cast(dtype, start + step * indices[0]);
1783 },
1784 name, tag);
1785}
1786
1797inline ffi::Array<Tensor> meshgrid(const ffi::Array<Tensor>& inputs, const std::string& indexing,
1798 std::string name = "T_meshgrid", std::string tag = kInjective) {
1799 const bool cartesian_indexing = indexing == "xy" && inputs.size() >= 2;
1800 ffi::Array<PrimExpr> out_shape;
1801 for (size_t i = 0; i < inputs.size(); ++i) {
1802 const int src_index = (cartesian_indexing && i < 2) ? 1 - i : i;
1803 out_shape.push_back(inputs[src_index]->shape.size() == 0 ? 1 : inputs[src_index]->shape[0]);
1804 }
1805 ffi::Array<Tensor> result;
1806 for (size_t i = 0; i < inputs.size(); ++i) {
1807 result.push_back(compute(
1808 out_shape,
1809 [&](const ffi::Array<PrimVar>& indices) {
1810 const int src_index = (cartesian_indexing && i < 2) ? 1 - i : i;
1811 auto ndim = inputs[i]->GetShape().size();
1812 ffi::Array<PrimExpr> real_indices = {};
1813 if (ndim > 0) {
1814 real_indices = {indices[src_index]};
1815 }
1816 return inputs[i](real_indices);
1817 },
1818 name, tag));
1819 }
1820 return result;
1821}
1822
1833inline Tensor layout_transform(const Tensor& src, const std::string& src_layout,
1834 const std::string& dst_layout,
1835 const std::string schedule_rule = "None",
1836 const std::string name = "T_layout_trans",
1837 const std::string tag = kInjective) {
1838 SLayout src_layout_struct(src_layout);
1839 SLayout dst_layout_struct(dst_layout);
1840
1842 return src;
1843 }
1844
1845 TVM_FFI_ICHECK(src_layout_struct.defined() && dst_layout_struct.defined())
1846 << "cannot convert from/to undefined layout";
1847
1850 << "cannot convert from " << src_layout << " to " << dst_layout;
1851
1852 ffi::Array<PrimExpr> dst_shape = layout_converter.ForwardShape(src->shape);
1853
1854 ffi::Map<ffi::String, ffi::Any> attrs = {{"schedule_rule", ffi::String(schedule_rule)},
1855 // Information about layouts needed for the schedule rule
1856 {"src_layout", ffi::String(src_layout)},
1857 {"dst_layout", ffi::String(dst_layout)},
1858 {"input_shape", src->shape}};
1859
1860 return compute(
1861 dst_shape,
1862 [&](const ffi::Array<PrimVar>& dst_indices) {
1863 ffi::Array<PrimExpr> dst_indices_expr =
1864 dst_indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
1865 ffi::Array<PrimExpr> src_indices = layout_converter.BackwardIndex(dst_indices_expr);
1866 PrimExpr in_range = PrimExpr(1) > PrimExpr(0); // init with dtype=bool and value=true
1867 for (size_t i = 0; i < src.ndim(); ++i) {
1868 in_range = in_range && (src_indices[i] < src->shape[i]);
1869 }
1870 return if_then_else(in_range, src(src_indices),
1871 tvm::cast(PrimType(src->dtype), PrimExpr(0)));
1872 },
1873 name, tag, attrs);
1874}
1875
1877inline void parse_auto_scheduler_layout(const ffi::String& layout, ffi::Array<PrimExpr>* shape,
1878 std::vector<std::string>* axes) {
1879 int32_t factor = 0;
1880 std::string axis = "";
1881 for (char c : std::string(layout)) {
1882 if (c >= 'A' && c <= 'z') {
1883 axis += c;
1884 if (factor != 0) {
1885 shape->push_back(factor);
1886 factor = 0;
1887 }
1888 } else if (c >= '0' && c <= '9') {
1889 factor = factor * 10 + c - '0';
1890 if (!axis.empty()) {
1891 axes->push_back(axis);
1892 axis = "";
1893 }
1894 } else {
1895 TVM_FFI_THROW(InternalError) << "Invalid layout " << layout;
1896 }
1897 }
1898 if (!axis.empty()) {
1899 axes->push_back(axis);
1900 }
1901}
1902
1914 const Tensor& src, const ffi::String& src_layout, const ffi::String& dst_layout,
1915 const ffi::String name = "T_auto_scheduler_layout_trans", const ffi::String tag = kInjective) {
1916 ffi::Array<PrimExpr> src_shape;
1917 std::vector<std::string> src_axes;
1918 ffi::Array<PrimExpr> dst_shape;
1919 std::vector<std::string> dst_axes;
1920
1923 return compute(
1924 dst_shape,
1925 [&](const ffi::Array<PrimVar>& dst_indices) {
1926 ffi::Array<PrimExpr> dst_indices_expr =
1927 dst_indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
1928 ffi::Array<PrimExpr> src_indices;
1929 for (const std::string& src_axis : src_axes) {
1930 PrimExpr src_index = 0;
1932 for (size_t i = 0; i < dst_axes.size(); ++i) {
1933 if (dst_axes[i] == src_axis) {
1935 }
1936 }
1937 src_indices.push_back(src_index);
1938 }
1939 return src(src_indices);
1940 },
1941 name, tag);
1942}
1943
1981 const Tensor& src, const tirx::IndexMap& index_map,
1982 const ffi::String name = "T_meta_schedule_layout_trans", const ffi::String tag = kInjective) {
1984 ffi::Array<Range> iter_domain;
1985 iter_domain.reserve(src->shape.size());
1986 for (const PrimExpr& e : src->shape) {
1987 iter_domain.push_back(Range::FromMinExtent(IntImm(e.ty(), 0), e));
1988 }
1989 ffi::Array<PrimExpr> post_transform_shape = index_map->MapShape(src->shape, analyzer);
1990 return compute(
1992 [src, inv = index_map.Inverse(iter_domain, analyzer),
1993 &analyzer](const ffi::Array<PrimVar>& indices) -> PrimExpr {
1994 ffi::Array<PrimExpr> prim_indices =
1995 indices.Map([](const PrimVar& var) { return var.as_or_throw<PrimExpr>(); });
1996 return src(inv->MapIndices(prim_indices, analyzer));
1997 },
1998 name, tag);
1999}
2000
2009inline Tensor shape(const Tensor& src, PrimType dtype, const std::string name = "T_shape",
2010 const std::string tag = kInjective) {
2011 int ndim = static_cast<int>(src->shape.size());
2012 ffi::Array<PrimExpr> out_shape{ndim};
2013 return compute(
2014 out_shape,
2015 [&](const ffi::Array<PrimVar>& indices) {
2016 auto idx = indices[0];
2017 PrimExpr ret = 0;
2018 for (int i = 0; i < ndim; ++i) {
2019 ret = tvm::if_then_else(idx == i, src->shape[i], ret);
2020 }
2021 return tvm::cast(dtype, ret);
2022 },
2023 name, tag);
2024}
2025
2026inline Tensor shape(const Tensor& src, DLDataType dtype, const std::string name = "T_shape",
2027 const std::string tag = kInjective) {
2028 return shape(src, PrimType(dtype), name, tag);
2029}
2030
2040 const std::string& name = "tensor_size",
2041 const std::string& tag = kInjective) {
2042 int ndim = static_cast<int>(src->shape.size());
2043 ffi::Array<PrimExpr> out_tensor_size = {};
2044 return compute(
2046 [&](const ffi::Array<PrimVar>& indices) {
2047 PrimExpr ret = 1;
2048 for (int i = 0; i < ndim; ++i) {
2049 ret *= src->shape[i];
2050 }
2051 return tvm::cast(dtype, ret);
2052 },
2053 name, tag);
2054}
2055
2057 const std::string& name = "tensor_size",
2058 const std::string& tag = kInjective) {
2059 return tensor_size(src, PrimType(dtype), name, tag);
2060}
2061
2076inline Tensor one_hot(const Tensor& indices, const PrimExpr on_value, const PrimExpr off_value,
2077 int depth, int axis, PrimType dtype,
2078 ffi::Array<PrimExpr> oshape = ffi::Array<PrimExpr>(),
2079 const std::string name = "T_one_hot", const std::string tag = kInjective) {
2080 int true_axis = (axis == -1) ? indices->shape.size() : axis;
2081 if (oshape.size() == 0) {
2082 int ndim = indices->shape.size() + 1;
2083 int indices_index = 0;
2084 for (int i = 0; i < ndim; i++) {
2085 if (i == true_axis) {
2086 oshape.push_back(IntImm::Int32(depth));
2087 } else {
2088 oshape.push_back(indices->shape[indices_index++]);
2089 }
2090 }
2091 }
2092
2095 return compute(
2096 oshape,
2097 [&](const ffi::Array<PrimVar>& iter_vars) {
2098 ffi::Array<PrimVar> indices_indices;
2099 for (size_t i = 0; i < iter_vars.size(); i++) {
2100 if (static_cast<int>(i) == true_axis) {
2101 continue;
2102 }
2103
2104 indices_indices.push_back(iter_vars[i]);
2105 }
2106
2107 auto idx = iter_vars[true_axis];
2108 return prim::Select(indices(indices_indices) == idx.as_or_throw<PrimExpr>(), on_value_cast,
2110 },
2111 name, tag);
2112}
2113
2114inline Tensor one_hot(const Tensor& indices, const PrimExpr on_value, const PrimExpr off_value,
2115 int depth, int axis, DLDataType dtype,
2116 ffi::Array<PrimExpr> oshape = ffi::Array<PrimExpr>(),
2117 const std::string name = "T_one_hot", const std::string tag = kInjective) {
2118 return one_hot(indices, on_value, off_value, depth, axis, PrimType(dtype), std::move(oshape),
2119 name, tag);
2120}
2121
2133 const ffi::Array<PrimExpr>& output_shape, const Tensor& sparse_values,
2134 const PrimExpr& default_value,
2135 const std::string name = "T_sparse_to_dense",
2136 const std::string tag = kInjective) {
2137 // Sparse indices are validated by signed integer element kind; lane encoding is irrelevant here.
2138 TVM_FFI_ICHECK_EQ(sparse_indices->dtype.code(), DLDataTypeCode::kDLInt)
2139 << "sparse_indices only accepts integer values";
2140 TVM_FFI_ICHECK_LE(sparse_indices->shape.size(), 3)
2141 << "sparse_indices tensor should be 0D, 1D, or 2D only";
2142 TVM_FFI_ICHECK_LE(sparse_values->shape.size(), 2)
2143 << "sparse_values tensor should be 0D or 1D only";
2144
2145 const auto rank_sparse_indices = static_cast<int>(sparse_indices->shape.size());
2146 ffi::Array<PrimExpr> oshape;
2147 for (auto l : output_shape) {
2148 oshape.push_back(l);
2149 }
2150 return compute(
2151 oshape,
2152 [&](const ffi::Array<PrimVar>& indices) {
2153 PrimExpr ret = default_value;
2154 if (0 == rank_sparse_indices) {
2155 ret = if_then_else(indices[0].as_or_throw<PrimExpr>() == sparse_indices(),
2156 sparse_values(), ret);
2157 } else if (1 == rank_sparse_indices) {
2158 for (int j = 0; j < GetConstInt(sparse_indices->shape[0]); j++) {
2159 ret = if_then_else(indices[0].as_or_throw<PrimExpr>() == sparse_indices[j],
2160 sparse_values[j], ret);
2161 }
2162 } else {
2163 for (int j = 0; j < GetConstInt(sparse_indices->shape[0]); j++) {
2165 for (int k = 0; k < GetConstInt(sparse_indices->shape[1]); k++) {
2166 PrimExpr comparision = indices[k].as_or_throw<PrimExpr>() == sparse_indices[j][k];
2168 }
2170 }
2171 }
2172 return ret;
2173 },
2174 name, tag);
2175}
2176
2189inline Tensor matrix_set_diag(const Tensor& input, const Tensor& diagonal, int k1, int k2,
2191 const std::string name = "T_matrix_set_diag",
2192 const std::string tag = kInjective) {
2193 size_t ndim = input->shape.size() - 1;
2194
2195 bool only_one_diagonal = k1 == k2;
2196
2197 return compute(
2198 input->shape,
2199 [&](const ffi::Array<PrimVar>& iter_vars) {
2200 auto get_diag = [&]() {
2201 ffi::Array<PrimExpr> diagonal_indices;
2202 PrimExpr k, offset = 0;
2203 for (size_t i = 0; i < ndim - 1; i++) {
2204 diagonal_indices.push_back(iter_vars[i]);
2205 }
2206 if (only_one_diagonal) {
2207 k = k1;
2208 } else {
2209 // Determining which diagonal/sub-diagonal/super-diagonal it is
2210 k = iter_vars[ndim] - iter_vars[ndim - 1];
2211 diagonal_indices.push_back(k2 - k);
2212
2213 // Calculating the offset in diagonal tensor for this diagonal
2214 auto get_offset = [&](PrimExpr M, PrimExpr N) {
2215 // offset = max_diagonal_length - diagonal_length
2216 return diagonal->shape[diagonal->shape.size() - 1] - if_then_else(M < N, M, N);
2217 };
2218 offset = if_then_else(
2219 k >= 0,
2220 super_diag_right_align ? get_offset(input->shape[ndim] - k, input->shape[ndim - 1])
2221 : 0,
2222 sub_diag_right_align ? get_offset(input->shape[ndim], input->shape[ndim - 1] + k)
2223 : 0);
2224 }
2225 diagonal_indices.push_back(if_then_else(k >= 0, iter_vars[ndim - 1], iter_vars[ndim]) +
2226 offset);
2227 return diagonal(diagonal_indices);
2228 };
2229 return if_then_else((PrimExpr)iter_vars[ndim] - iter_vars[ndim - 1] >= k1,
2230 if_then_else((PrimExpr)iter_vars[ndim] - iter_vars[ndim - 1] <= k2,
2231 get_diag(), input(iter_vars)),
2232 input(iter_vars));
2233 },
2234 name, tag);
2235}
2236
2245inline Tensor adv_index(const Tensor& data, const ffi::Array<Tensor>& indices,
2246 const std::string name = "advanced_index",
2247 const std::string tag = kInjective) {
2248 TVM_FFI_ICHECK_LE(indices.size(), data->shape.size()) << "too many indices for data!";
2249 ffi::Array<PrimExpr> oshape;
2250 ffi::Array<PrimExpr> broadcast_shape;
2251 ffi::Array<Tensor> bindices;
2252
2253 broadcast_shape = indices[0]->shape;
2254 for (size_t i = 1; i < indices.size(); ++i) {
2255 auto bh = detail::BroadcastShape(broadcast_shape, indices[i]->shape);
2256 broadcast_shape = ffi::Array<PrimExpr>(bh.common_shape.begin(), bh.common_shape.end());
2257 }
2258 if (indices.size() == 1) {
2259 // quick path
2260 bindices = indices;
2261 } else {
2262 // Do broadcast for indices
2263 for (size_t i = 0; i < indices.size(); ++i) {
2264 bindices.push_back(broadcast_to(indices[i], broadcast_shape));
2265 }
2266 }
2267
2268 for (const auto& dim : broadcast_shape) {
2269 oshape.push_back(dim);
2270 }
2271 for (size_t i = indices.size(); i < data->shape.size(); ++i) {
2272 oshape.push_back(data->shape[i]);
2273 }
2274
2275 return compute(
2276 oshape,
2277 [&](const ffi::Array<PrimVar>& iter_var) {
2278 ffi::Array<PrimExpr> tensor_indices;
2279 for (size_t i = 0; i < broadcast_shape.size(); ++i) {
2280 tensor_indices.push_back(iter_var[i]);
2281 }
2282 ffi::Array<PrimExpr> real_indices;
2283 for (size_t i = 0; i < bindices.size(); ++i) {
2285 }
2286 for (size_t i = broadcast_shape.size(); i < iter_var.size(); ++i) {
2287 real_indices.push_back(iter_var[i]);
2288 }
2289
2290 return data(real_indices);
2291 },
2292 name, tag);
2293}
2294
2295namespace relax {
2296// relax dynamic slice
2298 const te::Tensor& end, const te::Tensor& strides,
2299 ffi::Array<PrimExpr> output_shape,
2300 std::string name = "T_strided_slice_dynamic",
2301 std::string tag = kInjective) {
2302 const size_t num_dynamic_axes = x.ndim();
2303 TVM_FFI_ICHECK_EQ(begin.ndim(), 1);
2304 TVM_FFI_ICHECK_EQ(end.ndim(), 1);
2305 TVM_FFI_ICHECK_EQ(strides.ndim(), 1);
2306 const auto* len_begin = begin->shape[0].as<IntImmNode>();
2307 const auto* len_end = end->shape[0].as<IntImmNode>();
2308 const auto* len_strides = strides->shape[0].as<IntImmNode>();
2315
2316 return te::compute(
2317 output_shape,
2318 [&](const ffi::Array<tvm::tirx::PrimVar>& indices) {
2319 ffi::Array<PrimExpr> real_indices;
2320 for (size_t i = 0; i < num_dynamic_axes; ++i) {
2321 auto ind = IntImm::Int64(i);
2322 real_indices.push_back(indices[i] * strides(ind) + tvm::min(begin(ind), x->shape[i] - 1));
2323 }
2324 return x(real_indices);
2325 },
2326 name, tag);
2327}
2328
2329} // namespace relax
2330
2331} // namespace topi
2332} // namespace tvm
2333#endif // TVM_TOPI_TRANSFORM_H_
Algebra expression simplifications.
Broadcast op constructions.
Managed reference class to FloatImmNode.
Definition expr.h:567
Constant integer literals in the program.
Definition expr.h:487
int64_t value
the Internal value.
Definition expr.h:490
Managed reference class to IntImmNode.
Definition expr.h:504
static IntImm Int32(int64_t value, Span span=Span())
Construct a scalar int32 constant.
Definition expr.h:528
static IntImm Int64(int64_t value, Span span=Span())
Construct a scalar int64 constant.
Definition expr.h:537
Typed reference/view over any Expr whose ExprNode::ty is PrimType.
Definition base_expr.h:401
Definition base_expr.h:137
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.
Range container
Definition expr.h:610
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:380
RAII wrapper function to enter and exit a context object similar to python's with syntax.
Definition with_context.h:59
Managed reference to AnalyzerObj.
Definition analyzer.h:931
Managed reference to SelectNode.
Definition expr.h:525
Managed Tensor. The array is backed by reference counted blocks.
Definition tensor.h:49
Opaque construction-time node that represents a tensor.
Definition tensor.h:70
Tensor structure representing a possible input, or intermediate computation result.
Definition tensor.h:98
size_t ndim() const
Definition tensor.h:220
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:46
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
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.
TIR expressions.
Tensor expression language DSL.
Definition extracted_task.h:33
PrimVar var(std::string name_hint, PrimType t=PrimType::Int(32))
Construct a new Var expression.
IterVar reduce_axis(Range dom, std::string name="rv")
Create a new IterVar for reduction operations.
bool IsTensorLoad(const Expr &expr)
Return whether an expression is a Call whose callee is a TE Tensor.
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()
const Op & zero()
DLDataType DefaultIndexType()
Definition buffer.h:52
PrimExpr MakeConst(PrimType dtype, ValueType value, Span span=Span())
Make a const value with certain data type.
Definition op.h:1002
const Op & sum()
PrimExpr GetLength(PrimExpr begin, PrimExpr end, PrimExpr stride, PrimExpr extent, bool assume_inbound=true)
Definition transform.h:689
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:1104
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:1572
int64_t StaticCanonicalizeIndex(int64_t index, int64_t extent, int64_t stride)
Definition transform.h:670
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:2009
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:1756
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:964
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:1913
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
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:1797
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:1877
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:901
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:2132
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:1833
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:2245
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
constexpr auto kMatMul
Definition tags.h:37
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:1007
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:1478
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:2039
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:2076
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:1658
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:1980
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:1043
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:1636
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
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 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 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:2189
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:1334
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:1517
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:1421
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:1374
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 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.
Common operators defined for Expr.
Variables in the TIR.