IndexingUtils.h 6.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189
  1. #if !defined(TORCH_STABLE_ONLY) && !defined(TORCH_TARGET_VERSION)
  2. #pragma once
  3. #include <ATen/ExpandUtils.h>
  4. #include <ATen/native/CanUse32BitIndexMath.h>
  5. #include <ATen/native/TensorIterator.h>
  6. #include <ATen/core/IListRef.h>
  7. #include <c10/util/irange.h>
  8. #ifndef AT_PER_OPERATOR_HEADERS
  9. #include <ATen/Functions.h>
  10. #else
  11. #include <ATen/ops/empty.h>
  12. #include <ATen/ops/nonzero.h>
  13. #endif
  14. namespace at::native {
  15. [[noreturn]]
  16. static void invalid_mask(const Tensor & self, int64_t idx, const Tensor & mask, int64_t maskIdx) {
  17. TORCH_CHECK_INDEX(false, "The shape of the mask ", mask.sizes(), " at index ", maskIdx,
  18. " does not match the shape of the indexed tensor ", self.sizes(), " at index ", idx);
  19. }
  20. [[maybe_unused]] static std::vector<Tensor> expandTensors(
  21. const Tensor& self,
  22. IOptTensorListRef indices,
  23. bool ensure_same_device = false) {
  24. // If indices come in as ByteTensor or BoolTensor (masks), expand them into
  25. // the equivalent indexing by LongTensors
  26. std::vector<Tensor> result;
  27. for (const auto& index_opt : indices) {
  28. if (!index_opt.has_value()) {
  29. result.emplace_back();
  30. } else {
  31. const auto& index = *index_opt;
  32. if (index.scalar_type() == kByte || index.scalar_type() == kBool) {
  33. if (index.scalar_type() == kByte) {
  34. TORCH_WARN("indexing with dtype torch.uint8 is now deprecated," \
  35. " please use a dtype torch.bool instead.");
  36. }
  37. // The sizes of the ByteTensor mask or bool tensor must match the sizes of the
  38. // corresponding dimensions in self
  39. for (const auto j : c10::irange(index.dim())) {
  40. int64_t srcIdx = static_cast<int64_t>(result.size() + j);
  41. if (index.size(j) != self.size(srcIdx)) {
  42. invalid_mask(self, srcIdx, index, j);
  43. }
  44. }
  45. // Replace with nonzeros
  46. at::Tensor nonzero;
  47. if (ensure_same_device && index.device() != self.device()) {
  48. bool non_blocking = index.is_cpu() && self.device().is_cuda();
  49. auto out = at::empty({0}, index.options().dtype(kLong).pinned_memory(non_blocking));
  50. nonzero = at::nonzero_out(out, index).to(self.device(), non_blocking);
  51. } else {
  52. nonzero = index.nonzero();
  53. }
  54. for (const auto j : c10::irange(index.dim())) {
  55. result.emplace_back(nonzero.select(1, j));
  56. }
  57. } else if (ensure_same_device && index.device() != self.device()) {
  58. result.emplace_back(index.to(self.device()));
  59. } else {
  60. result.emplace_back(index);
  61. }
  62. }
  63. }
  64. return result;
  65. }
  66. [[maybe_unused]] static void checkIndexTensorTypes(
  67. IOptTensorListRef indices,
  68. bool allow_int = false) {
  69. for (const auto& tensor : indices) {
  70. if (tensor.has_value() && tensor->defined()) {
  71. auto scalarType = tensor->scalar_type();
  72. if (allow_int) {
  73. if (scalarType != kLong && scalarType != kByte && scalarType != kBool && scalarType != kInt) {
  74. TORCH_CHECK_INDEX(false, "tensors used as indices must be long, int, byte or bool tensors");
  75. }
  76. } else {
  77. if (scalarType != kLong && scalarType != kByte && scalarType != kBool) {
  78. TORCH_CHECK_INDEX(false, "tensors used as indices must be long, byte or bool tensors");
  79. }
  80. }
  81. }
  82. }
  83. }
  84. inline torch::List<std::optional<Tensor>> toListOfOptionalTensors(ArrayRef<Tensor> list) {
  85. torch::List<std::optional<Tensor>> result;
  86. result.reserve(list.size());
  87. for (const Tensor& a : list) {
  88. result.push_back(a);
  89. }
  90. return result;
  91. }
  92. inline torch::List<std::optional<Tensor>> toListOfOptionalTensors(ArrayRef<IValue> list) {
  93. torch::List<std::optional<Tensor>> result;
  94. result.reserve(list.size());
  95. for (const IValue& a : list) {
  96. result.push_back(a.isTensor() ? std::optional<Tensor>(a.toTensor()) : std::optional<Tensor>());
  97. }
  98. return result;
  99. }
  100. [[maybe_unused]] static bool hasContiguousSubspace(TensorList tl) {
  101. // true if all the non-null tensors are adjacent
  102. auto isDefined = [](const Tensor & tensor){ return tensor.defined(); };
  103. auto isNull = [](const Tensor & tensor){ return !tensor.defined(); };
  104. auto start = std::find_if(tl.begin(), tl.end(), isDefined);
  105. if (start == tl.end()) {
  106. return true;
  107. }
  108. auto stop = std::find_if(tl.rbegin(), tl.rend(), isDefined);
  109. auto it = std::find_if(start, stop.base(), isNull);
  110. return it == stop.base();
  111. }
  112. // Transposes the tensor and indices together so that all the non-null indices
  113. // index the first k dimensions of the tensor. Returns the transposed tensor
  114. // and the reordered indices. For example:
  115. // transposeToFront(tensor, {nullptr, a, nullptr, b})
  116. // returns
  117. // tensor.permute([1, 3, 0, 2]), {a, b, nullptr, nullptr}
  118. [[maybe_unused]] static std::tuple<Tensor, std::vector<Tensor>> transposeToFront(
  119. const Tensor& self,
  120. TensorList indices) {
  121. std::vector<int64_t> dims;
  122. std::vector<Tensor> transposedIndices;
  123. dims.reserve(self.dim());
  124. for (const auto i : c10::irange(self.dim())) {
  125. if (indices[i].defined()) {
  126. dims.push_back(i);
  127. transposedIndices.emplace_back(indices[i]);
  128. }
  129. }
  130. for (const auto i : c10::irange(self.dim())) {
  131. if (!indices[i].defined()) {
  132. dims.push_back(i);
  133. transposedIndices.emplace_back();
  134. }
  135. }
  136. return std::make_tuple(self.permute(dims), std::move(transposedIndices));
  137. }
  138. inline std::tuple<Tensor, std::vector<Tensor>, std::vector<int64_t>>
  139. transposeToFrontAndInvPerm(const Tensor& self, TensorList indices) {
  140. std::vector<int64_t> dims;
  141. std::vector<int64_t> invPerm;
  142. std::vector<Tensor> transposedIndices;
  143. dims.reserve(self.dim());
  144. invPerm.resize(self.dim());
  145. for (const auto i : c10::irange(self.dim())) {
  146. if (indices[i].defined()) {
  147. dims.push_back(i);
  148. transposedIndices.emplace_back(indices[i]);
  149. }
  150. }
  151. for (const auto i : c10::irange(self.dim())) {
  152. if (!indices[i].defined()) {
  153. dims.push_back(i);
  154. transposedIndices.emplace_back();
  155. }
  156. }
  157. for (const auto i : c10::irange(self.dim())) {
  158. invPerm[dims[i]] = i;
  159. }
  160. return std::make_tuple(self.permute(dims), std::move(transposedIndices), std::move(invPerm));
  161. }
  162. struct AdvancedIndex {
  163. AdvancedIndex(const Tensor& src, TensorList indices);
  164. Tensor src;
  165. std::vector<Tensor> indices;
  166. DimVector indexed_sizes;
  167. DimVector indexed_strides;
  168. int64_t dims_before;
  169. int64_t dims_after;
  170. };
  171. } //namespace at::native
  172. #else
  173. #error "This file should not be included when either TORCH_STABLE_ONLY or TORCH_TARGET_VERSION is defined."
  174. #endif // !defined(TORCH_STABLE_ONLY) && !defined(TORCH_TARGET_VERSION)