ZhengyangZhang commited on
Commit
5b4e3c2
·
verified ·
1 Parent(s): b980082

Add files using upload-large-folder tool

Browse files
This view is limited to 50 files because it contains too many changes.   See raw diff
Files changed (50) hide show
  1. lib/python3.12/site-packages/torch/include/ATen/functorch/ADInterpreters.h +38 -0
  2. lib/python3.12/site-packages/torch/include/ATen/functorch/BatchRulesHelper.h +480 -0
  3. lib/python3.12/site-packages/torch/include/ATen/functorch/BatchingMetaprogramming.h +126 -0
  4. lib/python3.12/site-packages/torch/include/ATen/functorch/DynamicLayer.h +124 -0
  5. lib/python3.12/site-packages/torch/include/ATen/functorch/FunctionalizeInterpreter.h +22 -0
  6. lib/python3.12/site-packages/torch/include/ATen/functorch/LegacyVmapTransforms.h +187 -0
  7. lib/python3.12/site-packages/torch/include/ATen/functorch/Macros.h +3 -0
  8. lib/python3.12/site-packages/torch/include/ATen/functorch/VmapInterpreter.h +25 -0
  9. lib/python3.12/site-packages/torch/include/ATen/mps/EmptyTensor.h +28 -0
  10. lib/python3.12/site-packages/torch/include/ATen/mps/IndexKernels.h +220 -0
  11. lib/python3.12/site-packages/torch/include/ATen/mps/MPSAllocator.h +438 -0
  12. lib/python3.12/site-packages/torch/include/ATen/mps/MPSAllocatorInterface.h +68 -0
  13. lib/python3.12/site-packages/torch/include/ATen/mps/MPSDevice.h +72 -0
  14. lib/python3.12/site-packages/torch/include/ATen/mps/MPSEvent.h +105 -0
  15. lib/python3.12/site-packages/torch/include/ATen/mps/MPSGeneratorImpl.h +61 -0
  16. lib/python3.12/site-packages/torch/include/ATen/mps/MPSGuardImpl.h +179 -0
  17. lib/python3.12/site-packages/torch/include/ATen/mps/MPSHooks.h +63 -0
  18. lib/python3.12/site-packages/torch/include/ATen/mps/MPSProfiler.h +480 -0
  19. lib/python3.12/site-packages/torch/include/ATen/mps/MPSStream.h +153 -0
  20. lib/python3.12/site-packages/torch/include/ATen/native/Activation.h +73 -0
  21. lib/python3.12/site-packages/torch/include/ATen/native/AdaptivePooling.h +49 -0
  22. lib/python3.12/site-packages/torch/include/ATen/native/BatchLinearAlgebra.h +321 -0
  23. lib/python3.12/site-packages/torch/include/ATen/native/BinaryOps.h +119 -0
  24. lib/python3.12/site-packages/torch/include/ATen/native/BucketizationUtils.h +173 -0
  25. lib/python3.12/site-packages/torch/include/ATen/native/CPUFallback.h +46 -0
  26. lib/python3.12/site-packages/torch/include/ATen/native/ComplexHelper.h +97 -0
  27. lib/python3.12/site-packages/torch/include/ATen/native/CompositeRandomAccessor.h +34 -0
  28. lib/python3.12/site-packages/torch/include/ATen/native/CompositeRandomAccessorCommon.h +263 -0
  29. lib/python3.12/site-packages/torch/include/ATen/native/ConvUtils.h +449 -0
  30. lib/python3.12/site-packages/torch/include/ATen/native/ConvolutionMM3d.h +14 -0
  31. lib/python3.12/site-packages/torch/include/ATen/native/Copy.h +20 -0
  32. lib/python3.12/site-packages/torch/include/ATen/native/Cross.h +14 -0
  33. lib/python3.12/site-packages/torch/include/ATen/native/DilatedConvolutionUtils.h +229 -0
  34. lib/python3.12/site-packages/torch/include/ATen/native/DispatchStub.h +479 -0
  35. lib/python3.12/site-packages/torch/include/ATen/native/Distance.h +20 -0
  36. lib/python3.12/site-packages/torch/include/ATen/native/Distributions.h +518 -0
  37. lib/python3.12/site-packages/torch/include/ATen/native/EmbeddingBag.h +153 -0
  38. lib/python3.12/site-packages/torch/include/ATen/native/Fill.h +21 -0
  39. lib/python3.12/site-packages/torch/include/ATen/native/ForeachUtils.h +409 -0
  40. lib/python3.12/site-packages/torch/include/ATen/native/FractionalMaxPooling.h +80 -0
  41. lib/python3.12/site-packages/torch/include/ATen/native/FunctionOfAMatrixUtils.h +20 -0
  42. lib/python3.12/site-packages/torch/include/ATen/native/FusedAdagrad.h +20 -0
  43. lib/python3.12/site-packages/torch/include/ATen/native/FusedSGD.h +21 -0
  44. lib/python3.12/site-packages/torch/include/ATen/native/GridSampler.h +298 -0
  45. lib/python3.12/site-packages/torch/include/ATen/native/GridSamplerUtils.h +105 -0
  46. lib/python3.12/site-packages/torch/include/ATen/native/Histogram.h +16 -0
  47. lib/python3.12/site-packages/torch/include/ATen/native/IndexKernel.h +41 -0
  48. lib/python3.12/site-packages/torch/include/ATen/native/IndexingUtils.h +164 -0
  49. lib/python3.12/site-packages/torch/include/ATen/native/Lerp.h +46 -0
  50. lib/python3.12/site-packages/torch/include/ATen/native/LinearAlgebra.h +17 -0
lib/python3.12/site-packages/torch/include/ATen/functorch/ADInterpreters.h ADDED
@@ -0,0 +1,38 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/functorch/Interpreter.h>
3
+
4
+ namespace at::functorch {
5
+
6
+ // These are the interpreters for our AD transforms
7
+ // (grad, vjp and jvp).
8
+ // See NOTE: [functorch interpreter stack] for more details.
9
+
10
+ struct TORCH_API GradInterpreterPtr {
11
+ explicit GradInterpreterPtr(const Interpreter* base): base_(base) { TORCH_INTERNAL_ASSERT(base->key() == TransformType::Grad); }
12
+ TransformType key() const { return base_->key(); }
13
+ int64_t level() const { return base_->level(); }
14
+ void processImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack);
15
+ void sendToNextInterpreterImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack, bool grad_special_case);
16
+ bool prevGradMode() const {
17
+ return std::get<GradInterpreterMeta>(base_->meta()).prevGradMode_;
18
+ }
19
+ Tensor lift(const Tensor& tensor) const;
20
+ private:
21
+ const Interpreter* base_;
22
+ };
23
+
24
+ struct TORCH_API JvpInterpreterPtr {
25
+ explicit JvpInterpreterPtr(const Interpreter* base): base_(base) { TORCH_INTERNAL_ASSERT(base->key() == TransformType::Jvp); }
26
+ TransformType key() const { return base_->key(); }
27
+ int64_t level() const { return base_->level(); }
28
+ void processImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack);
29
+ void sendToNextInterpreterImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack, bool grad_special_case);
30
+ bool prevFwdGradMode() const {
31
+ return std::get<JvpInterpreterMeta>(base_->meta()).prevFwdGradMode_;
32
+ }
33
+ Tensor lift(const Tensor& tensor) const;
34
+ private:
35
+ const Interpreter* base_;
36
+ };
37
+
38
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/functorch/BatchRulesHelper.h ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Facebook, Inc. and its affiliates.
2
+ // All rights reserved.
3
+ //
4
+ // This source code is licensed under the BSD-style license found in the
5
+ // LICENSE file in the root directory of this source tree.
6
+ #pragma once
7
+
8
+ #include <c10/util/TypeList.h>
9
+
10
+ #include <ATen/ATen.h>
11
+ #include <ATen/Operators.h>
12
+
13
+ #include <ATen/functorch/DynamicLayer.h>
14
+ #include <ATen/functorch/TensorWrapper.h>
15
+ #include <ATen/functorch/BatchingMetaprogramming.h>
16
+ #include <ATen/functorch/LegacyVmapTransforms.h>
17
+ #include <ATen/functorch/BatchedFallback.h>
18
+ #include <ATen/functorch/PlumbingHelper.h>
19
+ #include <ATen/core/dispatch/Dispatcher.h>
20
+ #include <ATen/VmapGeneratedPlumbing.h>
21
+
22
+ #include <utility>
23
+
24
+ // This file contains helper functions for batching rules.
25
+
26
+ namespace at::functorch {
27
+
28
+ TORCH_API Tensor reshape_dim_into(int64_t src, int64_t dst, const Tensor& x);
29
+ TORCH_API Tensor reshape_dim_outof(int64_t src, int64_t size1, const Tensor& x);
30
+
31
+ TORCH_API Tensor reshape_dim_outof_symint(int64_t src, const c10::SymInt& size1, const Tensor& x);
32
+
33
+ Tensor moveBatchDimToFront(const Tensor& tensor, std::optional<int64_t> maybe_batch_dim);
34
+ int64_t rankWithoutBatchDim(const Tensor& tensor, std::optional<int64_t> maybe_batch_dim);
35
+ int64_t numelWithoutBatchDim(const Tensor& tensor, std::optional<int64_t> maybe_batch_dim);
36
+ std::optional<int64_t> valIfNonempty(std::optional<int64_t> maybe_empty, int64_t new_val);
37
+ int64_t getPhysicalDim(const Tensor& tensor, bool has_batch_dim, int64_t logical_dim);
38
+ VmapDimVector getPhysicalDims(const Tensor& tensor, bool has_batch_dim, IntArrayRef logical_dims);
39
+
40
+ void vmapIncompatibleInplaceError(const char* schema_name);
41
+
42
+ Tensor maybePadToLogicalRank(const Tensor& tensor, std::optional<int64_t> has_bdim, int64_t logical_rank);
43
+
44
+ void check_randomness(RandomnessType randomness);
45
+ void check_randomness(RandomnessType randomness, bool any_tensor_bdim);
46
+
47
+ inline Tensor ensure_has_bdim(const Tensor& tensor, bool has_bdim, c10::SymInt batch_size) {
48
+ if (has_bdim) {
49
+ return tensor;
50
+ }
51
+ const auto sizes = tensor.sym_sizes();
52
+ SymDimVector expanded_shape;
53
+ expanded_shape.reserve(sizes.size());
54
+ expanded_shape.emplace_back(std::move(batch_size));
55
+ expanded_shape.insert(expanded_shape.end(), sizes.begin(), sizes.end());
56
+ return tensor.expand_symint(expanded_shape);
57
+ }
58
+
59
+ #define VMAP_SUPPORT(op, batch_rule) \
60
+ m.impl(#op, op ## _generated_plumbing<decltype(&batch_rule), &batch_rule>);
61
+
62
+ #define VMAP_SUPPORT2(op, overload, batch_rule) \
63
+ m.impl(#op "." #overload, op ## _ ## overload ## _generated_plumbing<decltype(&batch_rule), &batch_rule>);
64
+
65
+ #define OP_DECOMPOSE(op) m.impl(#op, static_cast<decltype(&ATEN_FN(op))>(native::op));
66
+ #define OP_DECOMPOSE2(op, overload) m.impl(#op"."#overload, static_cast<decltype(&ATEN_FN2(op, overload))>(native::op));
67
+
68
+ // DO NOT USE ME DIRECTLY! Use BASIC_UNARY_BATCH_RULE to save yourself some pain
69
+ template <typename A, A a, typename C>
70
+ struct BasicUnaryBatchRuleHelper;
71
+
72
+ template <typename F, F Func, typename A, typename... T>
73
+ struct BasicUnaryBatchRuleHelper<F, Func, c10::guts::typelist::typelist<A, T...>> {
74
+ static std::tuple<Tensor, std::optional<int64_t>> apply(
75
+ const Tensor& tensor,
76
+ std::optional<int64_t> batch_dim,
77
+ T... extra_args) {
78
+ return std::make_tuple(Func(tensor, std::forward<T>(extra_args)...), batch_dim);
79
+ }
80
+ };
81
+
82
+ // USAGE: BASIC_UNARY_BATCH_RULE(at::sin)
83
+ // INCORRECT USAGE: BASIC_UNARY_BATCH_RULE(&at::sin)
84
+ // It is important that this macro is not passed a function pointer!!
85
+ #define BASIC_UNARY_BATCH_RULE(fn) SINGLE_ARG(\
86
+ BasicUnaryBatchRuleHelper<\
87
+ decltype(&fn),\
88
+ &fn,\
89
+ c10::guts::function_traits<decltype(fn)>::parameter_types>::apply)
90
+
91
+ #define UNARY_POINTWISE(op) \
92
+ VMAP_SUPPORT(op, BASIC_UNARY_BATCH_RULE(ATEN_FN(op)));
93
+
94
+ template <typename A, A a, typename C>
95
+ struct VariadicBdimsBatchRuleHelper;
96
+
97
+ template <typename F, F Func, typename A, typename... T>
98
+ struct VariadicBdimsBatchRuleHelper<F, Func, c10::guts::typelist::typelist<A, T...>> {
99
+ static std::tuple<Tensor, std::optional<int64_t>> apply(
100
+ const Tensor& tensor,
101
+ std::optional<int64_t> batch_dim,
102
+ T... extra_args) {
103
+ auto tensor_ = moveBatchDimToFront(tensor, batch_dim);
104
+ return std::make_tuple(Func(tensor_, std::forward<T>(extra_args)...), 0);
105
+ }
106
+ };
107
+
108
+ // USAGE: VARIADIC_BDIMS_BATCH_RULE(at::cholesky_inverse)
109
+ // INCORRECT USAGE: VARIADIC_BDIMS_BATCH_RULE(&at::cholesky_inverse)
110
+ // It is important that this macro is not passed a function pointer!!
111
+ #define VARIADIC_BDIMS_BATCH_RULE(fn) SINGLE_ARG(\
112
+ VariadicBdimsBatchRuleHelper<\
113
+ decltype(&fn),\
114
+ &fn,\
115
+ c10::guts::function_traits<decltype(fn)>::parameter_types>::apply)
116
+
117
+ #define VARIADIC_BDIMS(op) \
118
+ VMAP_SUPPORT(op, VARIADIC_BDIMS_BATCH_RULE(ATEN_FN(op)));
119
+
120
+ #define VARIADIC_BDIMS2(op, overload) \
121
+ VMAP_SUPPORT2(op, overload, VARIADIC_BDIMS_BATCH_RULE(ATEN_FN2(op, overload)));
122
+
123
+ template<class F, F Func>
124
+ void boxed_tensor_inputs_batch_rule(const c10::OperatorHandle& op, torch::jit::Stack* stack) {
125
+ const auto& schema = op.schema();
126
+ const auto num_returns = schema.returns().size();
127
+ const auto num_arguments = schema.arguments().size();
128
+
129
+ c10::impl::ExcludeDispatchKeyGuard guard(DispatchKey::FuncTorchBatched);
130
+ auto maybe_layer = maybeCurrentDynamicLayer();
131
+ vmap_check_escaped(maybe_layer, "boxed_tensor_inputs_batch_rule");
132
+
133
+ int64_t cur_level = maybe_layer->layerId();
134
+
135
+ auto orig_arguments = torch::jit::last(*stack, num_arguments);
136
+ if (std::none_of(orig_arguments.begin(), orig_arguments.end(), ivalueParticipatesInCurrentLevel)) {
137
+ op.callBoxed(stack);
138
+ return;
139
+ }
140
+
141
+ auto arguments = torch::jit::pop(*stack, num_arguments);
142
+ std::vector<std::pair<Tensor, std::optional<int64_t>>> tensor_inputs;
143
+ std::vector<int64_t> tensor_pos;
144
+ for (const auto idx : c10::irange(0, num_arguments)) {
145
+ const auto& ivalue = arguments[idx];
146
+ if (ivalue.isTensor()) {
147
+ auto [tensor_value, tensor_bdim] = unwrapTensorAtLevel(ivalue.toTensor(), cur_level);
148
+ tensor_inputs.emplace_back(std::move(tensor_value), tensor_bdim);
149
+ tensor_pos.push_back(static_cast<int64_t>(idx));
150
+ }
151
+ }
152
+ Func(tensor_inputs);
153
+
154
+ size_t tensor_idx = 0;
155
+ TORCH_INTERNAL_ASSERT(!tensor_pos.empty());
156
+ for (const auto arg_idx : c10::irange(0, num_arguments)) {
157
+ if (tensor_idx >= tensor_pos.size() || (int64_t)arg_idx != tensor_pos[tensor_idx]) {
158
+ torch::jit::push(stack, arguments[arg_idx]);
159
+ } else {
160
+ TORCH_INTERNAL_ASSERT(tensor_idx < tensor_inputs.size());
161
+ torch::jit::push(stack, tensor_inputs[tensor_idx].first);
162
+ tensor_idx++;
163
+ }
164
+ }
165
+
166
+ op.callBoxed(stack);
167
+ const auto returns = torch::jit::pop(*stack, num_returns);
168
+ for (const auto& ret : returns) {
169
+ if (ret.isTensor()) {
170
+ torch::jit::push(stack, makeBatched(ret.toTensor(), 0, cur_level));
171
+ } else {
172
+ TORCH_INTERNAL_ASSERT(false, "This boxed batching rule does not currently support ops that return non-tensor values");
173
+ }
174
+ }
175
+ }
176
+
177
+ inline void handle_pointwise_ops(std::vector<std::pair<Tensor, std::optional<int64_t>>> &tensor_inputs) {
178
+ int64_t out_logical_rank = 0;
179
+ for (auto& tensor_input : tensor_inputs) {
180
+ int64_t cur_logical_rank = rankWithoutBatchDim(tensor_input.first, tensor_input.second);
181
+ out_logical_rank = std::max(out_logical_rank, cur_logical_rank);
182
+ }
183
+ for (auto& tensor_input: tensor_inputs) {
184
+ tensor_input.first = moveBatchDimToFront(tensor_input.first, tensor_input.second);
185
+ tensor_input.first = maybePadToLogicalRank(tensor_input.first, tensor_input.second, out_logical_rank);
186
+ }
187
+ }
188
+
189
+ #define POINTWISE_BOXED(op) \
190
+ m.impl(#op, torch::CppFunction::makeFromBoxedFunction<boxed_tensor_inputs_batch_rule<decltype(&handle_pointwise_ops), &handle_pointwise_ops>>());
191
+
192
+ #define POINTWISE_BOXED2(op, overload) \
193
+ m.impl(#op "." #overload, torch::CppFunction::makeFromBoxedFunction<boxed_tensor_inputs_batch_rule<decltype(&handle_pointwise_ops), &handle_pointwise_ops>>());
194
+
195
+ inline void handle_variadic_bdims(std::vector<std::pair<Tensor, std::optional<int64_t>>> &tensor_inputs) {
196
+ for (auto & tensor_input : tensor_inputs) {
197
+ tensor_input.first = moveBatchDimToFront(tensor_input.first, tensor_input.second);
198
+ }
199
+ }
200
+
201
+ #define VARIADIC_BDIMS_BOXED(op) \
202
+ m.impl(#op, torch::CppFunction::makeFromBoxedFunction<boxed_tensor_inputs_batch_rule<decltype(&handle_variadic_bdims), &handle_variadic_bdims>>());
203
+
204
+ using UnpackedBatchedTensor = std::tuple<Tensor, std::optional<int64_t>>;
205
+
206
+ inline void find_and_unpack_tensors(
207
+ const torch::jit::Stack* stack,
208
+ int64_t num_args,
209
+ int64_t cur_level,
210
+ SmallVector<UnpackedBatchedTensor, 5>* tensors,
211
+ SmallVector<int64_t, 5>* tensors_pos,
212
+ int64_t* batch_size) {
213
+
214
+ int64_t computed_batch_size = -1;
215
+ int64_t args_begin = static_cast<int64_t>(stack->size()) - num_args;
216
+
217
+ for (const auto idx : c10::irange(0, num_args)) {
218
+ const auto& ivalue = (*stack)[args_begin + idx];
219
+ if (!ivalue.isTensor()) {
220
+ continue;
221
+ }
222
+ auto unpacked = unwrapTensorAtLevel(ivalue.toTensor(), cur_level);
223
+ const auto& [tensor_value, tensor_bdim] = unpacked;
224
+ if (tensor_bdim.has_value()) {
225
+ auto candidate_batch_size = tensor_value.size(*tensor_bdim);
226
+ if (computed_batch_size == -1) {
227
+ computed_batch_size = candidate_batch_size;
228
+ }
229
+ TORCH_INTERNAL_ASSERT(candidate_batch_size == computed_batch_size);
230
+ }
231
+
232
+ tensors->push_back(std::move(unpacked));
233
+ tensors_pos->push_back(idx);
234
+ }
235
+ TORCH_INTERNAL_ASSERT(computed_batch_size > -1);
236
+ *batch_size = computed_batch_size;
237
+ }
238
+
239
+ inline void boxed_existing_bdim_all_batch_rule(
240
+ const c10::OperatorHandle& op, torch::jit::Stack* stack) {
241
+ const auto& schema = op.schema();
242
+ const auto num_returns = schema.returns().size();
243
+ const auto num_arguments = static_cast<int64_t>(schema.arguments().size());
244
+
245
+ c10::impl::ExcludeDispatchKeyGuard guard(DispatchKey::FuncTorchBatched);
246
+ auto maybe_layer = maybeCurrentDynamicLayer();
247
+ vmap_check_escaped(maybe_layer, "boxed_existing_bdim_all_batch_rule");
248
+ int64_t cur_level = maybe_layer->layerId();
249
+
250
+ const auto arguments = torch::jit::last(stack, num_arguments);
251
+ if (std::none_of(arguments.begin(), arguments.end(), ivalueParticipatesInCurrentLevel)) {
252
+ op.callBoxed(stack);
253
+ return;
254
+ }
255
+
256
+ int64_t args_begin = static_cast<int64_t>(stack->size()) - num_arguments;
257
+ SmallVector<UnpackedBatchedTensor, 5> tensor_inputs;
258
+ SmallVector<int64_t, 5> tensor_pos;
259
+ int64_t batch_size = 0;
260
+
261
+ find_and_unpack_tensors(
262
+ stack, num_arguments, cur_level,
263
+ &tensor_inputs, &tensor_pos, &batch_size);
264
+
265
+ // for each tensor, ensure it has a bdim and reshape it.
266
+ for (const auto tensor_idx : c10::irange(0, tensor_inputs.size())) {
267
+ const auto& [value, bdim] = tensor_inputs[tensor_idx];
268
+ auto value_ = ensure_has_bdim(value, bdim.has_value(), batch_size);
269
+ (*stack)[args_begin + tensor_pos[tensor_idx]] = reshape_dim_into(bdim.value_or(0), 0, value_);
270
+ }
271
+
272
+ op.callBoxed(stack);
273
+
274
+ for (const auto idx : c10::irange(args_begin, args_begin + num_returns)) {
275
+ const auto& ret = (*stack)[idx];
276
+ TORCH_INTERNAL_ASSERT(ret.isTensor(),
277
+ "This boxed batching rule does not currently support ops that return non-tensor values");
278
+ (*stack)[idx] = makeBatched(reshape_dim_outof(0, batch_size, ret.toTensor()), 0, cur_level);
279
+ }
280
+ }
281
+
282
+ // Use when all tensors arguments accept one (normal) batch dim.
283
+ // This batching rule expands the batch dim on all Tensors, reshapes it into
284
+ // dim 0, calls the op, and then reshapes the batch dim out of dim 0.
285
+ // This is not the most efficient thing; if there are alternatives, plese try
286
+ // to use them. Use this only as a last resort.
287
+ #define EXISTING_BDIM_ALL_BOXED(op) \
288
+ m.impl(#op, torch::CppFunction::makeFromBoxedFunction<boxed_existing_bdim_all_batch_rule>());
289
+
290
+ template <int64_t feature_rank, int64_t contig_tensor_index=-1>
291
+ inline void boxed_all_tensors_have_optional_bdim(
292
+ const c10::OperatorHandle& op, torch::jit::Stack* stack) {
293
+ const auto& schema = op.schema();
294
+ const auto num_returns = schema.returns().size();
295
+ const auto num_arguments = schema.arguments().size();
296
+
297
+ c10::impl::ExcludeDispatchKeyGuard guard(DispatchKey::FuncTorchBatched);
298
+ auto maybe_layer = maybeCurrentDynamicLayer();
299
+ vmap_check_escaped(maybe_layer, "boxed_all_tensors_have_optional_bdim");
300
+ int64_t cur_level = maybe_layer->layerId();
301
+
302
+ const auto arguments = torch::jit::last(stack, num_arguments);
303
+ if (std::none_of(arguments.begin(), arguments.end(), ivalueParticipatesInCurrentLevel)) {
304
+ op.callBoxed(stack);
305
+ return;
306
+ }
307
+
308
+ int64_t args_begin = static_cast<int64_t>(stack->size() - num_arguments);
309
+ SmallVector<UnpackedBatchedTensor, 5> tensor_inputs;
310
+ SmallVector<int64_t, 5> tensor_pos;
311
+ int64_t batch_size = 0;
312
+
313
+ find_and_unpack_tensors(
314
+ stack, static_cast<int64_t>(num_arguments), cur_level,
315
+ &tensor_inputs, &tensor_pos, &batch_size);
316
+
317
+ std::optional<bool> is_no_batch_dim_case;
318
+
319
+ for (const auto tensor_idx : c10::irange(0, tensor_inputs.size())) {
320
+ const auto& value = std::get<0>(tensor_inputs[tensor_idx]);
321
+ auto bdim = std::get<1>(tensor_inputs[tensor_idx]);
322
+ const auto logical_rank = rankWithoutBatchDim(value, bdim);
323
+
324
+ if (!is_no_batch_dim_case.has_value()) {
325
+ is_no_batch_dim_case = (logical_rank == feature_rank);
326
+ }
327
+ auto value_ = ensure_has_bdim(value, bdim.has_value(), batch_size);
328
+ if (!bdim.has_value()) {
329
+ bdim = 0;
330
+ }
331
+ if (*is_no_batch_dim_case) {
332
+ TORCH_INTERNAL_ASSERT(logical_rank == feature_rank);
333
+ value_ = moveBatchDimToFront(value_, bdim);
334
+ if (tensor_idx == contig_tensor_index) {
335
+ value_ = value_.contiguous();
336
+ }
337
+ (*stack)[args_begin + tensor_pos[tensor_idx]] = std::move(value_);
338
+ continue;
339
+ }
340
+ TORCH_INTERNAL_ASSERT(logical_rank == feature_rank + 1);
341
+ value_ = reshape_dim_into(*bdim, 0, value_);
342
+ if (tensor_idx == contig_tensor_index) {
343
+ value_ = value_.contiguous();
344
+ }
345
+ (*stack)[args_begin + tensor_pos[tensor_idx]] = std::move(value_);
346
+ }
347
+
348
+ op.callBoxed(stack);
349
+
350
+ for (const auto idx : c10::irange(args_begin, args_begin + num_returns)) {
351
+ const auto& ret = (*stack)[idx];
352
+ TORCH_INTERNAL_ASSERT(ret.isTensor(),
353
+ "This boxed batching rule does not currently support ops that return non-tensor values");
354
+ if (*is_no_batch_dim_case) {
355
+ (*stack)[idx] = makeBatched(ret.toTensor(), 0, cur_level);
356
+ } else {
357
+ (*stack)[idx] = makeBatched(reshape_dim_outof(0, batch_size, ret.toTensor()), 0, cur_level);
358
+ }
359
+ }
360
+ }
361
+
362
+ // Useful for many NN operators.
363
+ // The operator must satisfy the following:
364
+ // - All arguments must accept an optional batch dim.
365
+ // - All arguments must be the same rank
366
+ #define ALL_TENSORS_HAVE_OPTIONAL_BDIM_BOXED(feature_rank, op) \
367
+ m.impl(#op, torch::CppFunction::makeFromBoxedFunction<boxed_all_tensors_have_optional_bdim<feature_rank>>());
368
+
369
+ #define ALL_TENSORS_HAVE_OPTIONAL_BDIM_BOXED_CONTIG1(feature_rank, op, contig_tensor_index) \
370
+ m.impl(#op, \
371
+ torch::CppFunction::makeFromBoxedFunction<\
372
+ boxed_all_tensors_have_optional_bdim<\
373
+ feature_rank, \
374
+ contig_tensor_index>\
375
+ >());
376
+
377
+ template <typename A, A a, typename C>
378
+ struct ExistingBdimBatchRuleHelper;
379
+
380
+ template <typename F, F Func, typename A, typename... T>
381
+ struct ExistingBdimBatchRuleHelper<F, Func, c10::guts::typelist::typelist<A, T...>> {
382
+ static std::tuple<Tensor, std::optional<int64_t>> apply(
383
+ const Tensor& self,
384
+ std::optional<int64_t> self_bdim,
385
+ T... extra_args) {
386
+ auto self_ = reshape_dim_into(*self_bdim, 0, self);
387
+ auto out = Func(self_, std::forward<T>(extra_args)...);
388
+ return std::make_tuple(reshape_dim_outof_symint(0, self.sym_sizes()[*self_bdim], out), 0);
389
+ }
390
+ };
391
+
392
+ // USAGE: EXISTING_BDIM_BATCH_RULE(at::cholesky_inverse)
393
+ // INCORRECT USAGE: EXISTING_BDIM_BATCH_RULE(&at::cholesky_inverse)
394
+ // It is important that this macro is not passed a function pointer!!
395
+ #define EXISTING_BDIM_BATCH_RULE(fn) SINGLE_ARG(\
396
+ ExistingBdimBatchRuleHelper<\
397
+ decltype(&fn),\
398
+ &fn,\
399
+ c10::guts::function_traits<decltype(fn)>::parameter_types>::apply)
400
+
401
+
402
+ #define EXISTING_BDIM(op) \
403
+ VMAP_SUPPORT(op, EXISTING_BDIM_BATCH_RULE(ATEN_FN(op)));
404
+
405
+ #define EXISTING_BDIM2(op, overload) \
406
+ VMAP_SUPPORT2(op, overload, EXISTING_BDIM_BATCH_RULE(ATEN_FN2(op, overload)));
407
+
408
+ #define INVOKE(object,ptrToMember) ((object).*(ptrToMember))
409
+
410
+
411
+ template <typename F, F Method, typename... ExtraArgs>
412
+ Tensor& unary_inplace_batch_rule(Tensor& self, std::optional<int64_t>, ExtraArgs... extra_args) {
413
+ INVOKE(self, Method)(std::forward<ExtraArgs>(extra_args)...);
414
+ return self;
415
+ }
416
+
417
+ inline int64_t get_bdim_size4(
418
+ const Tensor& a_value, std::optional<int64_t> a_bdim,
419
+ const Tensor& b_value, std::optional<int64_t> b_bdim,
420
+ const Tensor& c_value, std::optional<int64_t> c_bdim,
421
+ const Tensor& d_value, std::optional<int64_t> d_bdim) {
422
+ if (a_bdim)
423
+ return a_value.size(*a_bdim);
424
+ if (b_bdim)
425
+ return b_value.size(*b_bdim);
426
+ if (c_bdim)
427
+ return c_value.size(*c_bdim);
428
+ if (d_bdim)
429
+ return d_value.size(*d_bdim);
430
+ TORCH_INTERNAL_ASSERT(false);
431
+ }
432
+
433
+ inline int64_t get_bdim_size3(
434
+ const Tensor& a_value, std::optional<int64_t> a_bdim,
435
+ const Tensor& b_value, std::optional<int64_t> b_bdim,
436
+ const Tensor& c_value, std::optional<int64_t> c_bdim) {
437
+ if (a_bdim)
438
+ return a_value.size(*a_bdim);
439
+ if (b_bdim)
440
+ return b_value.size(*b_bdim);
441
+ if (c_bdim)
442
+ return c_value.size(*c_bdim);
443
+ TORCH_INTERNAL_ASSERT(false);
444
+ }
445
+
446
+ inline int64_t get_bdim_size2(
447
+ const Tensor& a_value, std::optional<int64_t> a_bdim,
448
+ const Tensor& b_value, std::optional<int64_t> b_bdim) {
449
+ if (a_bdim)
450
+ return a_value.size(*a_bdim);
451
+ if (b_bdim)
452
+ return b_value.size(*b_bdim);
453
+ TORCH_INTERNAL_ASSERT(false);
454
+ }
455
+
456
+ inline c10::SymInt get_bdim_size2_symint(
457
+ const Tensor& a_value, std::optional<int64_t> a_bdim,
458
+ const Tensor& b_value, std::optional<int64_t> b_bdim) {
459
+ if (a_bdim)
460
+ return a_value.sym_size(*a_bdim);
461
+ if (b_bdim)
462
+ return b_value.sym_size(*b_bdim);
463
+ TORCH_INTERNAL_ASSERT(false);
464
+ }
465
+
466
+ // [start, start + 1, ..., stop - 1]
467
+ inline VmapDimVector range(int64_t start, int64_t stop) {
468
+ TORCH_INTERNAL_ASSERT(stop >= start);
469
+ VmapDimVector dims;
470
+ dims.reserve(stop - start);
471
+ for (int64_t i = start; i < stop; i++) {
472
+ dims.emplace_back(i);
473
+ }
474
+ return dims;
475
+ }
476
+ std::tuple<Tensor, Tensor> _binary_pointwise_helper(
477
+ const Tensor& tensor, std::optional<int64_t> tensor_batch_dim, const Tensor& other, std::optional<int64_t> other_batch_dim,
478
+ bool do_type_promotion=true);
479
+
480
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/functorch/BatchingMetaprogramming.h ADDED
@@ -0,0 +1,126 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Facebook, Inc. and its affiliates.
2
+ // All rights reserved.
3
+ //
4
+ // This source code is licensed under the BSD-style license found in the
5
+ // LICENSE file in the root directory of this source tree.
6
+
7
+ #pragma once
8
+ #include <ATen/Tensor.h>
9
+ #include <ATen/VmapGeneratedPlumbing.h>
10
+
11
+ // This file contains template metaprogramming things that are used for our
12
+ // batching rules.
13
+ //
14
+ // See NOTE: [vmap plumbing] for more details on why this is necessary.
15
+ // The plumbing has a bunch of metaprogramming hacks for determining the signature
16
+ // of a batching rule from the signature of the operator, many of which use the
17
+ // helper functions in this file.
18
+
19
+ namespace at::functorch {
20
+
21
+ // Metaprogramming things
22
+ template <class... Items> using typelist = c10::guts::typelist::typelist<Items...>;
23
+ template <class TypeList> using head_t = c10::guts::typelist::head_t<TypeList>;
24
+ template <class TL1, class TL2> using concat_t = c10::guts::typelist::concat_t<TL1, TL2>;
25
+ template <typename T> class debug_t;
26
+
27
+ // tail operation
28
+ template<class TypeList>
29
+ struct tail final {
30
+ static_assert(c10::guts::false_t<TypeList>::value,
31
+ "In typelist::tail<T>, the T argument must be typelist<...>.");
32
+ };
33
+ template<class Head, class... Tail>
34
+ struct tail<typelist<Head, Tail...>> final {
35
+ using type = typelist<Tail...>;
36
+ };
37
+ template<class TypeList> using tail_t = typename tail<TypeList>::type;
38
+
39
+ template <class First, class Second, class Next, class Tail>
40
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext {
41
+ using type = Next;
42
+ };
43
+ template <class Next, class Tail>
44
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<Tensor, std::optional<int64_t>, Next, Tail> {
45
+ using type = Tail;
46
+ };
47
+ template <class Next, class Tail>
48
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<const Tensor&, std::optional<int64_t>, Next, Tail> {
49
+ using type = Tail;
50
+ };
51
+ template <class Next, class Tail>
52
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<Tensor&, std::optional<int64_t>, Next, Tail> {
53
+ using type = Tail;
54
+ };
55
+ template <class Next, class Tail>
56
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<std::optional<Tensor>, std::optional<int64_t>, Next, Tail> {
57
+ using type = Tail;
58
+ };
59
+ template <class Next, class Tail>
60
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<const std::optional<Tensor>&, std::optional<int64_t>, Next, Tail> {
61
+ using type = Tail;
62
+ };
63
+ template <class Next, class Tail>
64
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<std::optional<Tensor>&, std::optional<int64_t>, Next, Tail> {
65
+ using type = Tail;
66
+ };
67
+ template <class Next, class Tail>
68
+ struct IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<std::vector<Tensor>, std::optional<int64_t>, Next, Tail> {
69
+ using type = Tail;
70
+ };
71
+ template <class TypeList> struct RemoveBatchDimAfterTensor {
72
+ using first = head_t<TypeList>;
73
+ using next = tail_t<TypeList>;
74
+ using second = head_t<next>;
75
+ using tail = tail_t<next>;
76
+
77
+ using type = concat_t<
78
+ typelist<first>,
79
+ typename RemoveBatchDimAfterTensor<
80
+ typename IfFirstIsTensorAndSecondisBatchDimThenTailElseNext<first, second, next, tail>::type
81
+ >::type
82
+ >;
83
+ };
84
+ template <class Type> struct RemoveBatchDimAfterTensor<typelist<Type>> {
85
+ using type = typelist<Type>;
86
+ };
87
+ template <> struct RemoveBatchDimAfterTensor<typelist<>> {
88
+ using type = typelist<>;
89
+ };
90
+ template<class TypeList> using remove_batch_dim_after_tensor_t = typename RemoveBatchDimAfterTensor<TypeList>::type;
91
+
92
+ template <typename T> struct UnpackSingleItemTuple {
93
+ using type = T;
94
+ };
95
+ template <typename T> struct UnpackSingleItemTuple<std::tuple<T>> {
96
+ using type = T;
97
+ };
98
+ template <typename T> using unpack_single_item_tuple_t = typename UnpackSingleItemTuple<T>::type;
99
+
100
+ template <typename Return, typename TupleArgs> struct BuildFunctionHelper;
101
+ template <typename Return, typename... Args> struct BuildFunctionHelper<Return, std::tuple<Args...>> {
102
+ using type = Return(Args...);
103
+ };
104
+ template <typename Return, typename TL>
105
+ struct BuildFunction {
106
+ using type = typename BuildFunctionHelper<Return, c10::guts::typelist::to_tuple_t<TL>>::type;
107
+ };
108
+ template <typename Return, typename TL> using build_function_t = typename BuildFunction<Return, TL>::type;
109
+
110
+
111
+ template <typename batch_rule_t> struct ToOperatorType {
112
+ using batch_rule_return_type = typename c10::guts::function_traits<batch_rule_t>::return_type;
113
+ using batch_rule_parameter_types = typename c10::guts::function_traits<batch_rule_t>::parameter_types;
114
+
115
+ using operator_parameter_types = remove_batch_dim_after_tensor_t<batch_rule_parameter_types>;
116
+ using operator_return_type =
117
+ unpack_single_item_tuple_t<
118
+ c10::guts::typelist::to_tuple_t<
119
+ remove_batch_dim_after_tensor_t<
120
+ c10::guts::typelist::from_tuple_t<batch_rule_return_type>>>>;
121
+
122
+ using type = build_function_t<operator_return_type, operator_parameter_types>;
123
+ };
124
+ template <typename batch_rule_t> using to_operator_t = typename ToOperatorType<batch_rule_t>::type;
125
+
126
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/functorch/DynamicLayer.h ADDED
@@ -0,0 +1,124 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Facebook, Inc. and its affiliates.
2
+ // All rights reserved.
3
+ //
4
+ // This source code is licensed under the BSD-style license found in the
5
+ // LICENSE file in the root directory of this source tree.
6
+
7
+ #pragma once
8
+ #include <ATen/functorch/Macros.h>
9
+ #include <c10/core/DispatchKey.h>
10
+ #include <ATen/core/function_schema.h>
11
+ #include <optional>
12
+ #include <c10/core/impl/LocalDispatchKeySet.h>
13
+ #include <ATen/functorch/Interpreter.h>
14
+ #include <ATen/functorch/VmapInterpreter.h>
15
+ #include <ATen/functorch/ADInterpreters.h>
16
+ #include <ATen/functorch/FunctionalizeInterpreter.h>
17
+
18
+ // Forward declared
19
+ namespace c10 { struct AutogradMetaInterface; }
20
+
21
+ namespace at::functorch {
22
+
23
+ // This file contains the implementation of functorch's interpreter stack.
24
+ // See NOTE: [functorch interpreter stack] first before reading on.
25
+ //
26
+ // NB: the functorch interpreter stack is also referred to as:
27
+ // - the "dynamic layer stack" -- an older name for "interpreter" was
28
+ // "dynamic layer".
29
+ // - the "functorch mode stack". You can think of each functorch transform as a
30
+ // "mode" (in the same sense as torch_dispatch mode or torch_function mode),
31
+ // and functorch being an implementation of a "mode stack" where the modes
32
+ // may be arbitrary composed.
33
+
34
+ // DynamicLayer is basically the same thing as an Interpreter.
35
+ // It represents a functorch transform and it holds an Interpreter,
36
+ // which contains metadata related to the transform and instructions on
37
+ // how to perform the transform.
38
+ //
39
+ // TODO: we can excise DynamicLayer in favor of Interpreter,
40
+ // But I am going to leave it for now as a compatiblity shim to avoid
41
+ // needing to refactor a lot of callsites...
42
+ struct TORCH_API DynamicLayer {
43
+ explicit DynamicLayer(
44
+ TransformType transform_type,
45
+ int64_t layerId,
46
+ std::optional<c10::SymInt> batchSize = std::nullopt,
47
+ std::optional<RandomnessType> randomness = std::nullopt,
48
+ std::optional<bool> prev_grad_mode = std::nullopt,
49
+ std::optional<bool> pre_fwd_grad_mode = std::nullopt,
50
+ std::optional<bool> functionalize_add_back_views = std::nullopt);
51
+
52
+ TransformType key() const;
53
+ int64_t layerId() const;
54
+
55
+ const Interpreter& interpreter() const { return interpreter_; }
56
+ Interpreter& interpreter() { return interpreter_; }
57
+
58
+ // Only valid for vmap
59
+ c10::SymInt batchSize() const;
60
+ RandomnessType randomness() const;
61
+
62
+ private:
63
+ Interpreter interpreter_;
64
+ };
65
+
66
+ TORCH_API int64_t initAndPushDynamicLayer(
67
+ TransformType transform_type,
68
+ std::optional<c10::SymInt> batch_size = std::nullopt,
69
+ std::optional<RandomnessType> randomness = std::nullopt,
70
+ std::optional<bool> prev_grad_mode = std::nullopt,
71
+ std::optional<bool> prev_fwd_grad_mode = std::nullopt,
72
+ std::optional<bool> functionalize_add_back_views = std::nullopt);
73
+ TORCH_API DynamicLayer popDynamicLayerAndDeleteMetadata();
74
+ TORCH_API std::optional<DynamicLayer> maybeCurrentDynamicLayer();
75
+ TORCH_API const std::vector<DynamicLayer>& getDynamicLayerStack();
76
+ TORCH_API void setDynamicLayerStack(const std::vector<DynamicLayer>& stack);
77
+ TORCH_API void setDynamicLayerFrontBackKeysIncluded(bool included);
78
+
79
+ // NOTE: [Life handles and lexically scoped transforms]
80
+ // functorch transforms are lexically scoped.
81
+ // Given a level, we store a "life handle" that is a boolean that tells us if the
82
+ // transform with that level is active or not.
83
+ //
84
+ // functorch's TensorWrapper (for grad transforms) stores a life handle.
85
+ // If a TensorWrapper escapes from the scope of the transform, then somehow
86
+ // it must know it escaped; it can tell by querying the life handle.
87
+ TORCH_API const std::shared_ptr<bool>& getLifeHandleForLevel(int64_t level);
88
+
89
+ // Returns if an operator is in-place. An operator is inplace if:
90
+ // 1. The first argument is a Tensor and it is being written to
91
+ // 2. The first argument is being returned
92
+ // 3. No other arguments are aliased
93
+ // Here is an example of an in-place operator:
94
+ // add_(Tensor(a!) self, Tensor other, *, Scalar alpha=1) -> Tensor(a!)
95
+ TORCH_API bool isInplaceOp(const c10::FunctionSchema& schema);
96
+
97
+ // Given the indices of unwrapped inputs and the schema, this returns the indices of any outputs that should remain unwrapped
98
+ TORCH_API std::optional<size_t> findAliasedOutput(const FunctionSchema& schema, const int64_t immutable_input);
99
+
100
+ TORCH_API Tensor unwrapIfDead(const Tensor& tensor);
101
+ TORCH_API bool isDeadTensorWrapper(const Tensor& tensor);
102
+
103
+ // Pretty printers
104
+ TORCH_API std::ostream& operator<<(std::ostream& os, const DynamicLayer& layer);
105
+ TORCH_API std::ostream& operator<<(std::ostream& os, const std::vector<DynamicLayer>& dynamicLayerStack);
106
+
107
+ // While a functorch transform is active, torch.autograd.function._SingleLevelFunction
108
+ // is disabled by default. The following two APIs are APIs for enabling
109
+ // it. These are not user-facing APIs. We can delete this in the future, but
110
+ // it is useful for debugging when something goes wrong with the
111
+ // autograd.Function <> functorch interaction, which uses _SingleLevelFunction,
112
+ // because it leads to loud errors if something is incorrect.
113
+ TORCH_API void setSingleLevelAutogradFunctionAllowed(bool allowed);
114
+ TORCH_API bool getSingleLevelAutogradFunctionAllowed();
115
+
116
+ // While a functorch grad transform is active, Tensor.requires_grad_() gets
117
+ // disabled. These two functions are the mechanism to controlling that.
118
+ TORCH_API void setInplaceRequiresGradAllowed(bool allowed);
119
+ TORCH_API bool getInplaceRequiresGradAllowed();
120
+
121
+ TORCH_API DynamicLayer popDynamicLayer();
122
+ TORCH_API int64_t pushDynamicLayer(DynamicLayer&& layer);
123
+
124
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/functorch/FunctionalizeInterpreter.h ADDED
@@ -0,0 +1,22 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/functorch/Interpreter.h>
3
+
4
+ namespace at::functorch {
5
+
6
+ // This is the interpreter that handles the functionalize() transform.
7
+ // See NOTE: [functorch interpreter stack] for more details.
8
+
9
+ struct FunctionalizeInterpreterPtr {
10
+ explicit FunctionalizeInterpreterPtr(const Interpreter* base): base_(base) { TORCH_INTERNAL_ASSERT(base->key() == TransformType::Functionalize); }
11
+ TransformType key() const { return base_->key(); }
12
+ int64_t level() const { return base_->level(); }
13
+ void processImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack);
14
+ void sendToNextInterpreterImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack, bool grad_special_case);
15
+ bool functionalizeAddBackViews() const {
16
+ return std::get<FunctionalizeInterpreterMeta>(base_->meta()).functionalizeAddBackViews_;
17
+ }
18
+ private:
19
+ const Interpreter* base_;
20
+ };
21
+
22
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/functorch/LegacyVmapTransforms.h ADDED
@@ -0,0 +1,187 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright (c) Facebook, Inc. and its affiliates.
2
+ // All rights reserved.
3
+ //
4
+ // This source code is licensed under the BSD-style license found in the
5
+ // LICENSE file in the root directory of this source tree.
6
+
7
+ #pragma once
8
+
9
+ #include <ATen/functorch/Macros.h>
10
+ #include <ATen/functorch/BatchedTensorImpl.h>
11
+
12
+ namespace at::functorch {
13
+
14
+ // This files contains the legacy (now-deprecated) batching rule API.
15
+ // Please try to use the new-style batching rule API (see writing_batch_rules.md)
16
+
17
+ // This file contains abstractions used for transforming *logical* vmap arguments
18
+ // into *physical* arguments. (Keep reading for definitions of these terms).
19
+
20
+ // NOTE: [Logical vs physical args]
21
+ // Consider the following vmap.
22
+ // vmap(vmap(func, in_dims=(2,)), in_dims=(0,))(torch.ones(2, 3, 4))
23
+ // This would produce a BatchedTensor wrapping a Tensor of size [2, 3, 4],
24
+ // with batch dims 0 and 2:
25
+ // BatchedTensor(ones(2, 3, 4), bdims=[(lvl=1,dim=0),(lvl=2,dim=2)])
26
+ //
27
+ // We say the *logical* view of the tensor has size [3] -- tensors inside
28
+ // `func` appear to have size [3].
29
+ // However, the *physical* underlying tensor (the one passed to vmap) has size
30
+ // [2, 3, 4].
31
+ //
32
+ // This notion of logical vs physical also extends to non-tensor arguments.
33
+ // Consider the previous tensor; let's assume the user called
34
+ // `torch.sum(tensor, dim=0)` inside of `func`. Then the logical
35
+ // dimension they are reducing over is dim 0 but the physical dim is dim 1
36
+ // (the first non-batch dimension)
37
+
38
+ // Forward declared; see NOTE: [What is a VmapPhysicalView?]
39
+ struct VmapPhysicalView;
40
+
41
+ // Most PyTorch operators take 4 or fewer inputs.
42
+ constexpr int64_t kVmapTransformStaticInputSize = 4;
43
+ using VmapPhysicalViewVec = SmallVector<VmapPhysicalView, kVmapTransformStaticInputSize>;
44
+
45
+ // Pytorch generally advertises good performance for <= 5 dims.
46
+ // (see ATen/core/DimVector.h). We add a few extra dims (~3) for vmap
47
+ // dimensions to get 8. Adjust this number as necessary
48
+ constexpr int64_t kVmapStaticDimVecSize = 8;
49
+ using VmapDimVector = SmallVector<int64_t, kVmapStaticDimVecSize>;
50
+ using VmapSymDimVector = SmallVector<c10::SymInt, kVmapStaticDimVecSize>;
51
+
52
+ // NOTE: [What is an VmapTransform?]
53
+ // An *VmapTransform* converts logical views of tensors to physical views.
54
+ //
55
+ // Batching rules use VmapTransforms to convert logical arguments to
56
+ // physical arguments, then call one or more at:: operator that handles the
57
+ // physical arguments, and then converts the physical result back to a logical
58
+ // argument.
59
+
60
+ // VmapTransform for operators that take tensors with multiple batch dims.
61
+ // Given one or more logical views on Tensors, `logicalToPhysical`
62
+ // permutes all of the batch dims to the front of the tensor, aligns
63
+ // and expands the batch dims to match each other (according to their `level`),
64
+ // and returns a VmapPhysicalView on the tensor(s).
65
+ struct TORCH_API MultiBatchVmapTransform {
66
+ static VmapPhysicalView logicalToPhysical(const Tensor& logical_tensor);
67
+ static VmapPhysicalViewVec logicalToPhysical(ITensorListRef logical_tensors);
68
+ };
69
+
70
+ // VmapTransform for operators that broadcast all inputs.
71
+ // Given some logical views on Tensors, `logicalToPhysical`:
72
+ // - permutes all of the batch dims to the front of the tensors
73
+ // - aligns all the batch dims to the collective levels of all of the tensors.
74
+ // If a tensor does not have a batch dim for a vmap level, then it receives
75
+ // a size-one dimension for said level.
76
+ // - aligns the non-batch dims to have the same dimensionality, adding extra
77
+ // size-1 dimensions in between the batch dimensions and the non-batch dimensions
78
+ // so that the batch dimensions are lined up from the right.
79
+ //
80
+ // For example: given inputs of size (B, 2) and (B, 3, 2) where B is the batch
81
+ // dimension, BroadcastingVmapTransform returns VmapPhysicalViews that wrap tensors
82
+ // of size (B, 1, 2) and (B, 3, 2).
83
+ //
84
+ // Given inputs of size (B, 2) and (2,), BroadcastingVmapTransform returns
85
+ // VmapPhysicalViews wrapping tensors of size (B, 2) and (1, 2). We don't
86
+ // actually *need* to return a tensor of size (1, 2) for the second tensor
87
+ // because the broadcasting operation takes care of that for us, but we do
88
+ // it anyways to keep things simple.
89
+ struct TORCH_API BroadcastingVmapTransform {
90
+ static VmapPhysicalViewVec logicalToPhysical(TensorList logical_tensors);
91
+ };
92
+
93
+ // Forward declared, if you're reading this file head to toe, don't worry about
94
+ // it yet.
95
+ struct VmapPhysicalToLogicalMap;
96
+
97
+ // NOTE: [What is a VmapPhysicalView?]
98
+ // VmapPhysicalView represents a physical view on a Tensor.
99
+ //
100
+ // One can use it to further convert logical dimension indices, logical shapes,
101
+ // and more to their physical variants, or convert a new (physical) tensor into
102
+ // a logical BatchedTensor. (TODO(rzou): some of these are not yet implemented).
103
+ //
104
+ // VmapPhysicalView stores a physical tensor with all of its batch dimensions at
105
+ // the front and some levels that correspond to said batch dimensions.
106
+ //
107
+ // The levels bitset specifies which vmap levels correspond to the batch
108
+ // dimensions at the front of the tensor. In particular, the number of set bits
109
+ // corresponds to the number of batch dimensions on `tensor` and the rightmost
110
+ // bit of `levels` specifies the maximum number of nested vmaps we are in at
111
+ // this point in time.
112
+ // For example, given:
113
+ // physical_view = VmapPhysicalView(tensor=ones(2, 3, 4, 5, 6), levels={1, 3})
114
+ //
115
+ // Rightmost bit of `levels` is 3 indicating the number of nested vmaps less
116
+ // than or equal to 3.
117
+ // bitset: 010100
118
+ // ^
119
+ // |
120
+ // levels: 012345
121
+ struct TORCH_API VmapPhysicalView {
122
+ VmapPhysicalView(Tensor&& tensor, std::bitset<kVmapNumLevels> levels)
123
+ : levels_(levels), tensor_(std::move(tensor)) {
124
+ // TORCH_INTERNAL_ASSERT(!isBatchedTensor(tensor));
125
+ }
126
+
127
+ Tensor& tensor() { return tensor_; }
128
+ const Tensor& tensor() const { return tensor_; }
129
+
130
+ // Maps logical dim indices to physical dim indices. Also does dim wrapping.
131
+ //
132
+ // For example, given:
133
+ // physical_view = VmapPhysicalView(tensor=ones(2, 3, 4, 5), levels={1, 3})
134
+ //
135
+ // Then physical_view.getPhysicalDims({0, 1}) returns {2, 3}.
136
+ // This is because the size of levels tell us that the first two dimensions
137
+ // of `tensor_` are batch dimensions, so a logical dim of `n` is actually
138
+ // a physical dim of `n + 2`.
139
+ VmapDimVector getPhysicalDims(IntArrayRef logical_dims) const;
140
+ int64_t getPhysicalDim(int64_t logical_dim) const;
141
+
142
+ // Returns a VmapPhysicalToLogicalMap object. This can be used for
143
+ // mapping a physical tensor to a new logical tensor (BatchedTensor)
144
+ VmapPhysicalToLogicalMap getPhysicalToLogicalMap() const;
145
+
146
+ // Maps a logical shape to a physical shape by pre-pending the batch
147
+ // sizes to the logical shape.
148
+ VmapDimVector getPhysicalShape(IntArrayRef logical_shape) const;
149
+ SymDimVector getPhysicalShape(c10::SymIntArrayRef logical_shape) const;
150
+
151
+ int64_t numBatchDims() const;
152
+
153
+ private:
154
+ int64_t numLogicalDims() const;
155
+
156
+ std::bitset<kVmapNumLevels> levels_;
157
+ Tensor tensor_;
158
+ };
159
+
160
+ // Convenience struct used for mapping a physical tensor (a non-BatchedTensor)
161
+ // to a logical one (BatchedTensor). It holds some levels that are used to do the
162
+ // mapping and assumes that the batch dimensions in the physical tensor all
163
+ // occur at the front of the tensor.
164
+ struct TORCH_API VmapPhysicalToLogicalMap {
165
+ VmapPhysicalToLogicalMap(std::bitset<kVmapNumLevels> levels): levels_(levels) {}
166
+
167
+ // Maps a physical tensor to a new logical tensor (BatchedTensor).
168
+ // Assumes that all of the "batch dimensions" are at the front
169
+ // of the physical tensor. For example, given:
170
+ // - x = rank-4 Tensor with size 2, 3, 5, 7
171
+ // - levels = (2, 4)
172
+ // Returns:
173
+ // - BatchedTensor(x, bdims=[(dim=0,lvl=2), (dim=1, lvl=4)])
174
+ Tensor apply(const Tensor& physical_tensor) const;
175
+
176
+ // Given a vector of physical tensors,
177
+ // 1. maps each tensor to a new logical tensor. Assumes that all of the
178
+ // "batch dimensions" are at the front of the physical tensors.
179
+ // 2. stores the new logical tensors back into the passed-in vector. This is
180
+ // to avoid additional dynamic allocations.
181
+ void applyInplace(std::vector<Tensor>& physical_tensors) const;
182
+
183
+ std::bitset<kVmapNumLevels> levels_;
184
+ };
185
+
186
+
187
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/functorch/Macros.h ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ #pragma once
2
+
3
+ #define SINGLE_ARG(...) __VA_ARGS__
lib/python3.12/site-packages/torch/include/ATen/functorch/VmapInterpreter.h ADDED
@@ -0,0 +1,25 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/functorch/Interpreter.h>
3
+
4
+ namespace at::functorch {
5
+
6
+ // This is the interpreter that handles the functionalize() transform.
7
+ // See NOTE: [functorch interpreter stack] for more details.
8
+
9
+ struct VmapInterpreterPtr {
10
+ explicit VmapInterpreterPtr(const Interpreter* base): base_(base) { TORCH_INTERNAL_ASSERT(base->key() == TransformType::Vmap); }
11
+ TransformType key() const { return base_->key(); }
12
+ int64_t level() const { return base_->level(); }
13
+ void processImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack);
14
+ void sendToNextInterpreterImpl(const c10::OperatorHandle& op, torch::jit::Stack* stack, bool grad_special_case);
15
+ c10::SymInt batchSize() const {
16
+ return std::get<VmapInterpreterMeta>(base_->meta()).batchSize_;
17
+ }
18
+ RandomnessType randomness() const {
19
+ return std::get<VmapInterpreterMeta>(base_->meta()).randomness_;
20
+ }
21
+ private:
22
+ const Interpreter* base_;
23
+ };
24
+
25
+ } // namespace at::functorch
lib/python3.12/site-packages/torch/include/ATen/mps/EmptyTensor.h ADDED
@@ -0,0 +1,28 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+ #include <ATen/core/TensorBase.h>
5
+
6
+ namespace at::detail {
7
+
8
+ C10_EXPORT TensorBase empty_mps(
9
+ IntArrayRef size,
10
+ std::optional<ScalarType> dtype_opt,
11
+ std::optional<Layout> layout_opt,
12
+ std::optional<Device> device_opt,
13
+ std::optional<bool> pin_memory_opt,
14
+ std::optional<c10::MemoryFormat> memory_format_opt);
15
+ C10_EXPORT TensorBase empty_mps(IntArrayRef size, const TensorOptions& options);
16
+
17
+ C10_EXPORT TensorBase empty_strided_mps(
18
+ IntArrayRef size,
19
+ IntArrayRef stride,
20
+ ScalarType dtype,
21
+ std::optional<Device> device_opt);
22
+
23
+ C10_EXPORT TensorBase empty_strided_mps(
24
+ IntArrayRef size,
25
+ IntArrayRef stride,
26
+ const TensorOptions& options);
27
+
28
+ } // namespace at::detail
lib/python3.12/site-packages/torch/include/ATen/mps/IndexKernels.h ADDED
@@ -0,0 +1,220 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ namespace at::mps {
4
+
5
+ static const char* SCATTER_OPS_TEMPLATE = R"METAL_SCATTER(
6
+ template<typename Y, typename X>
7
+ Y cast(const X x);
8
+
9
+ template<>
10
+ {1} cast<{1}, {0}>(const {0} x) {{
11
+ return {2};
12
+ }}
13
+
14
+ kernel void scatter_kernel_n(uint linear_index [[thread_position_in_grid]],
15
+ constant void * src_ [[buffer(0)]],
16
+ device void * dst_ [[buffer(1)]],
17
+ constant uint32_t * size [[buffer(2)]],
18
+ constant uint32_t * stride [[buffer(3)]],
19
+ constant uint32_t & numel [[buffer(4)]],
20
+ constant int32_t & ndim [[buffer(5)]]) {{
21
+ if (linear_index >= numel) return;
22
+
23
+ constant {0} * src = (constant {0} *)src_;
24
+ device {1} * dst = (device {1} *)dst_;
25
+
26
+ uint64_t dst_offs = 0;
27
+ auto dst_idx = linear_index;
28
+ for(int dim = ndim - 1; dim >= 0; --dim) {{
29
+ dst_offs += stride[dim] * (dst_idx % size[dim]);
30
+ dst_idx /= size[dim];
31
+ }}
32
+
33
+ dst[dst_offs] = cast<{1}>(src[linear_index]);
34
+ }}
35
+
36
+ kernel void scatter_kernel_4(uint linear_index [[thread_position_in_grid]],
37
+ constant void * src_ [[buffer(0)]],
38
+ device void * dst_ [[buffer(1)]],
39
+ constant packed_uint4 & size [[buffer(2)]],
40
+ constant packed_uint4 & stride [[buffer(3)]],
41
+ constant uint32_t & numel [[buffer(4)]]) {{
42
+ if (linear_index >= numel) return;
43
+
44
+ constant {0} * src = (constant {0} *)src_;
45
+ device {1} * dst = (device {1} *)dst_;
46
+
47
+ packed_uint4 local_index;
48
+ local_index.x = linear_index / (size[3] * size[2] * size[1]) % size[0];
49
+ local_index.y = linear_index / (size[3] * size[2]) % size[1];
50
+ local_index.z = linear_index / size[3] % size[2];
51
+ local_index.w = linear_index % size[3];
52
+
53
+ const packed_uint4 strided_index = local_index * stride;
54
+ dst[strided_index.x + strided_index.y + strided_index.z + strided_index.w] = cast<{1}>(src[linear_index]);
55
+ }}
56
+
57
+ kernel void scatter_kernel_3(uint linear_index [[thread_position_in_grid]],
58
+ constant void * src_ [[buffer(0)]],
59
+ device void * dst_ [[buffer(1)]],
60
+ constant packed_uint3 & size [[buffer(2)]],
61
+ constant packed_uint3 & stride [[buffer(3)]],
62
+ constant uint32_t & numel [[buffer(4)]]) {{
63
+ if (linear_index >= numel) return;
64
+
65
+ constant {0} * src = (constant {0} *)src_;
66
+ device {1} * dst = (device {1} *)dst_;
67
+
68
+ packed_uint3 local_index;
69
+ local_index.x = linear_index / (size[2] * size[1]) % size[0];
70
+ local_index.y = linear_index / size[2] % size[1];
71
+ local_index.z = linear_index % size[2];
72
+
73
+ const packed_uint3 strided_index = local_index * stride;
74
+ dst[strided_index.x + strided_index.y + strided_index.z] = cast<{1}>(src[linear_index]);
75
+ }}
76
+
77
+ kernel void scatter_kernel_2(uint linear_index [[thread_position_in_grid]],
78
+ constant void * src_ [[buffer(0)]],
79
+ device void * dst_ [[buffer(1)]],
80
+ constant packed_uint2 & size [[buffer(2)]],
81
+ constant packed_uint2 & stride [[buffer(3)]],
82
+ constant uint32_t & numel [[buffer(4)]]) {{
83
+ if (linear_index >= numel) return;
84
+
85
+ constant {0} * src = (constant {0} *)src_;
86
+ device {1} * dst = (device {1} *)dst_;
87
+
88
+ packed_uint2 local_index;
89
+ local_index.x = linear_index / size[1] % size[0];
90
+ local_index.y = linear_index % size[1];
91
+
92
+ const packed_uint2 strided_index = local_index * stride;
93
+ dst[strided_index.x + strided_index.y] = cast<{1}>(src[linear_index]);
94
+ }}
95
+
96
+ kernel void scatter_kernel_1(uint linear_index [[thread_position_in_grid]],
97
+ constant void * src_ [[buffer(0)]],
98
+ device void * dst_ [[buffer(1)]],
99
+ constant int & size [[buffer(2)]],
100
+ constant int & stride [[buffer(3)]],
101
+ constant uint32_t & numel [[buffer(4)]]) {{
102
+ if (linear_index >= numel) return;
103
+
104
+ constant {0} * src = (constant {0} *)src_;
105
+ device {1} * dst = (device {1} *)dst_;
106
+
107
+ const int local_index = linear_index % size;
108
+ const int strided_index = local_index * stride;
109
+ dst[strided_index] = cast<{1}>(src[linear_index]);
110
+ }}
111
+ )METAL_SCATTER";
112
+
113
+ static const char* GATHER_OPS_TEMPLATE = R"METAL_GATHER(
114
+ template<typename Y, typename X>
115
+ Y cast(const X x);
116
+
117
+ template<>
118
+ {1} cast<{1}, {0}>(const {0} x) {{
119
+ return {2};
120
+ }}
121
+
122
+ kernel void gather_kernel_n(uint linear_index [[thread_position_in_grid]],
123
+ constant void * src_ [[buffer(0)]],
124
+ device void * dst_ [[buffer(1)]],
125
+ constant uint32_t * size [[buffer(2)]],
126
+ constant uint32_t * stride [[buffer(3)]],
127
+ constant uint32_t & numel [[buffer(4)]],
128
+ constant int32_t & ndim [[buffer(5)]]) {{
129
+ if (linear_index >= numel) return;
130
+
131
+ constant {0} * src = (constant {0} *)src_;
132
+ device {1} * dst = (device {1} *)dst_;
133
+
134
+ uint64_t src_offs = 0;
135
+ auto src_idx = linear_index;
136
+ for(int dim = ndim - 1; dim >= 0; --dim) {{
137
+ src_offs += stride[dim] * (src_idx % size[dim]);
138
+ src_idx /= size[dim];
139
+ }}
140
+
141
+ dst[linear_index] = cast<{1}>(src[src_offs]);
142
+ }}
143
+
144
+ kernel void gather_kernel_4(uint linear_index [[thread_position_in_grid]],
145
+ constant void * src_ [[buffer(0)]],
146
+ device void * dst_ [[buffer(1)]],
147
+ constant packed_uint4 & size [[buffer(2)]],
148
+ constant packed_uint4 & stride [[buffer(3)]],
149
+ constant uint32_t & numel [[buffer(4)]]) {{
150
+ if (linear_index >= numel) return;
151
+
152
+ constant {0} * src = (constant {0} *)src_;
153
+ device {1} * dst = (device {1} *)dst_;
154
+
155
+ packed_uint4 local_index;
156
+ local_index.x = linear_index / (size[3] * size[2] * size[1]) % size[0];
157
+ local_index.y = linear_index / (size[3] * size[2]) % size[1];
158
+ local_index.z = linear_index / size[3] % size[2];
159
+ local_index.w = linear_index % size[3];
160
+
161
+ const packed_uint4 strided_index = local_index * stride;
162
+ dst[linear_index] = cast<{1}>(src[strided_index.x + strided_index.y + strided_index.z + strided_index.w]);
163
+ }}
164
+
165
+ kernel void gather_kernel_3(uint linear_index [[thread_position_in_grid]],
166
+ constant void * src_ [[buffer(0)]],
167
+ device void * dst_ [[buffer(1)]],
168
+ constant packed_uint3 & size [[buffer(2)]],
169
+ constant packed_uint3 & stride [[buffer(3)]],
170
+ constant uint32_t & numel [[buffer(4)]]) {{
171
+ if (linear_index >= numel) return;
172
+
173
+ constant {0} * src = (constant {0} *)src_;
174
+ device {1} * dst = (device {1} *)dst_;
175
+
176
+ packed_uint3 local_index;
177
+ local_index.x = linear_index / (size[2] * size[1]) % size[0];
178
+ local_index.y = linear_index / size[2] % size[1];
179
+ local_index.z = linear_index % size[2];
180
+
181
+ const packed_uint3 strided_index = local_index * stride;
182
+ dst[linear_index] = cast<{1}>(src[strided_index.x + strided_index.y + strided_index.z]);
183
+ }}
184
+
185
+ kernel void gather_kernel_2(uint linear_index [[thread_position_in_grid]],
186
+ constant void * src_ [[buffer(0)]],
187
+ device void * dst_ [[buffer(1)]],
188
+ constant packed_uint2 & size [[buffer(2)]],
189
+ constant packed_uint2 & stride [[buffer(3)]],
190
+ constant uint32_t & numel [[buffer(4)]]) {{
191
+ if (linear_index >= numel) return;
192
+
193
+ constant {0} * src = (constant {0} *)src_;
194
+ device {1} * dst = (device {1} *)dst_;
195
+
196
+ packed_uint2 local_index;
197
+ local_index.x = linear_index / size[1] % size[0];
198
+ local_index.y = linear_index % size[1];
199
+
200
+ const packed_uint2 strided_index = local_index * stride;
201
+ dst[linear_index] = cast<{1}>(src[strided_index.x + strided_index.y]);
202
+ }}
203
+
204
+ kernel void gather_kernel_1(uint linear_index [[thread_position_in_grid]],
205
+ constant void * src_ [[buffer(0)]],
206
+ device void * dst_ [[buffer(1)]],
207
+ constant int & size [[buffer(2)]],
208
+ constant int & stride [[buffer(3)]],
209
+ constant uint32_t & numel [[buffer(4)]]) {{
210
+ if (linear_index >= numel) return;
211
+
212
+ constant {0} * src = (constant {0} *)src_;
213
+ device {1} * dst = (device {1} *)dst_;
214
+
215
+ const int local_index = linear_index % size;
216
+ const int strided_index = local_index * stride;
217
+ dst[linear_index] = cast<{1}>(src[strided_index]);
218
+ }}
219
+ )METAL_GATHER";
220
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSAllocator.h ADDED
@@ -0,0 +1,438 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/mps/MPSAllocatorInterface.h>
6
+ #include <ATen/mps/MPSEvent.h>
7
+ #include <ATen/mps/MPSStream.h>
8
+
9
+ #include <c10/util/flat_hash_map.h>
10
+ #include <mach/vm_page_size.h>
11
+ #include <cstdio>
12
+ #include <mutex>
13
+ #include <set>
14
+ #include <unordered_set>
15
+
16
+ // this implementation is based on CUDACachingAllocator.
17
+ // It utilizes Metal Heaps to improve the performance with buffer allocation.
18
+ // Do not include this header. Use MPSAllocatorInterface.h instead.
19
+ // TODO: Unify the logic with CUDACachingAllocator and remove redundant code.
20
+ namespace at::mps::HeapAllocator {
21
+
22
+ static const size_t kMaxSmallAlloc = MB(1); // largest "small" allocation is 1 MiB
23
+ static const size_t kMinLargeAlloc = MB(10); // allocations between 1 and 10 MiB may use kLargeHeap
24
+ static const size_t kRoundLarge = MB(2); // round up large allocations to 2 MiB
25
+ static const size_t kSmallHeap = MB(8); // "small" allocations are packed in 8 MiB heaps
26
+ static const size_t kLargeHeap = MB(32); // "large" allocations may be packed in 32 MiB heaps
27
+ static const size_t kXLargeHeapD =
28
+ MB(128); // "extra large" allocations on Discrete devices may be packed in 128 MiB heaps
29
+ static const size_t kXLargeHeapU =
30
+ MB(1024); // "extra large" allocations on Unified devices may be packed in 1 GiB heaps
31
+ static const size_t kMaxScalarAlloc = (sizeof(int64_t)); // largest "scalar" allocation
32
+
33
+ // buffer pools could be customized with a combination of usage flags
34
+ enum UsageFlags : uint32_t {
35
+ PRIVATE = 0,
36
+ SMALL = (1 << 0), // small heaps have sizes of kSmallHeap, and large ones kLargeHeap
37
+ SHARED = (1 << 1), // shared pools allocated on devices with unified memory; otherwise, private between host/device
38
+ MANAGED = (1 << 2), // managed storage mode
39
+ HAZARD = (1 << 3), // enables Automatic Hazard Tracking for the resources allocated on the pool
40
+ SCALAR = (1 << 4), // used to import CPU scalar values to GPU and use them in MPS Stream
41
+ };
42
+ // debug verbosity flags
43
+ enum DebugVerbosity : uint32_t {
44
+ SILENT = 0,
45
+ PROFILING = (1 << 0), // print generic profiling data for total system memory usage
46
+ ALLOCATIONS = (1 << 1), // print buffer allocations
47
+ RECYCLES = (1 << 2), // print buffer recycling
48
+ RELEASES = (1 << 3), // print buffer releases
49
+ LARGE_ONLY = (1 << 4), // only log large buffer pool transactions
50
+ };
51
+
52
+ struct HeapBlock;
53
+
54
+ struct BufferBlock {
55
+ id<MTLBuffer> buffer;
56
+ void* cpu_ptr = nullptr; // stores the pointer to CPU mapping of a Shared MTLBuffer
57
+ size_t size; // size after alignment
58
+ size_t requested_size; // requested size (before alignment)
59
+ // buffer shape is used for retrieving base of views in cached graphs
60
+ std::vector<int64_t> shape;
61
+ bool in_use = false;
62
+ HeapBlock* heap;
63
+ id_t buf_id;
64
+ // counter to candidate least recently used buffers for garbage collection
65
+ uint32_t gc_count = 0;
66
+ uint32_t use_count = 0;
67
+ // counter to assign unique ids to buffer blocks
68
+ static uint64_t buffer_counter;
69
+ // Metal events used to sync GPU/CPU operations on the shared-storage buffers
70
+ MPSEventPtr event;
71
+
72
+ BufferBlock(size_t Size, size_t RequestedSize = 0, const id<MTLBuffer> Buffer = nullptr, HeapBlock* Heap = nullptr)
73
+ : buffer(Buffer), size(Size), requested_size(RequestedSize), heap(Heap), buf_id(Buffer ? ++buffer_counter : 0) {}
74
+
75
+ static bool Comparator(const BufferBlock* a, const BufferBlock* b) {
76
+ return (a->size != b->size) ? a->size < b->size : (uintptr_t)a->buffer < (uintptr_t)b->buffer;
77
+ }
78
+ static size_t alignUp(size_t Size, size_t Alignment) {
79
+ assert(((Alignment - 1) & Alignment) == 0);
80
+ return ((Size + Alignment - 1) & ~(Alignment - 1));
81
+ }
82
+ uint32_t retainCount() const {
83
+ return [buffer retainCount];
84
+ }
85
+ };
86
+ typedef bool (*BufferComparison)(const BufferBlock*, const BufferBlock*);
87
+
88
+ struct BufferPool;
89
+ struct AllocParams {
90
+ AllocParams(size_t Alloc_Size, size_t Requested_Size, BufferPool* Pool)
91
+ : search_key(Alloc_Size), pool(Pool), requested_size(Requested_Size) {}
92
+ size_t size() const {
93
+ return search_key.size;
94
+ }
95
+
96
+ BufferBlock search_key;
97
+ BufferPool* pool;
98
+ BufferBlock* buffer_block = nullptr;
99
+ size_t requested_size;
100
+ // true if we exceed the low watermark limit. In this case
101
+ // we apply strategies to relieve the pressure before allocation.
102
+ bool has_memory_pressure = false;
103
+ // true if we're allocating on a unified memory device
104
+ bool has_unified_memory = true;
105
+ };
106
+
107
+ struct HeapBlock {
108
+ id<MTLHeap> heap;
109
+ struct {
110
+ size_t total, available;
111
+ } size;
112
+ BufferPool* pool;
113
+ unsigned int n_buffers = 0;
114
+ id_t heap_id;
115
+ // indicates if we split this heap to sub-allocate 'several' buffers (otherwise single buffer)
116
+ bool is_split;
117
+ // counter to assign unique ids to heap blocks
118
+ static uint64_t heap_counter;
119
+
120
+ HeapBlock(size_t Size, const id<MTLHeap> Heap = nullptr, BufferPool* Pool = nullptr)
121
+ : heap(Heap),
122
+ size({.total = Size, .available = Size}),
123
+ pool(Pool),
124
+ heap_id(Heap ? ++heap_counter : 0),
125
+ is_split(true) {}
126
+
127
+ static MTLResourceOptions getOptions(uint32_t usage) {
128
+ // TODO: check the caching performance of write-combined mode
129
+ MTLResourceOptions options = MTLResourceCPUCacheModeDefaultCache;
130
+
131
+ if (usage & UsageFlags::MANAGED)
132
+ options |= MTLResourceStorageModeManaged;
133
+ else if (usage & UsageFlags::SHARED)
134
+ options |= MTLResourceStorageModeShared;
135
+ else
136
+ options |= MTLResourceStorageModePrivate;
137
+
138
+ options |=
139
+ (usage & UsageFlags::HAZARD) ? MTLResourceHazardTrackingModeTracked : MTLResourceHazardTrackingModeUntracked;
140
+
141
+ return options;
142
+ }
143
+
144
+ static HeapBlock* createHeapBlock(AllocParams& params, id<MTLDevice> device, uint32_t usage) {
145
+ HeapBlock* heapBlock = nullptr;
146
+ bool is_split = true;
147
+ const size_t size = params.size();
148
+ MTLHeapDescriptor* d = [MTLHeapDescriptor new];
149
+ if (d) {
150
+ const size_t kXLargeHeap = params.has_unified_memory ? kXLargeHeapU : kXLargeHeapD;
151
+ if (size <= kMaxSmallAlloc) {
152
+ d.size = kSmallHeap;
153
+ } else if (size < kMinLargeAlloc) {
154
+ d.size = kLargeHeap;
155
+ } else if (size < kXLargeHeap / 2 && !params.has_memory_pressure) {
156
+ d.size = kXLargeHeap;
157
+ } else {
158
+ d.size = kRoundLarge * ((size + kRoundLarge - 1) / kRoundLarge);
159
+ is_split = false;
160
+ }
161
+ d.storageMode = (usage & UsageFlags::SHARED) ? MTLStorageModeShared : MTLStorageModePrivate;
162
+ d.cpuCacheMode = MTLCPUCacheModeDefaultCache;
163
+ // this automatically handles Metal buffer access synchronizations at the
164
+ // cost of slightly lower performance.
165
+ d.hazardTrackingMode =
166
+ (usage & UsageFlags::HAZARD) ? MTLHazardTrackingModeTracked : MTLHazardTrackingModeUntracked;
167
+ d.resourceOptions = getOptions(usage);
168
+ d.type = MTLHeapTypeAutomatic;
169
+ id<MTLHeap> heap = [device newHeapWithDescriptor:d];
170
+ if (heap) {
171
+ [heap setPurgeableState:MTLPurgeableStateNonVolatile];
172
+ const size_t heap_size = heapAvailableSize(heap);
173
+ heapBlock = new HeapBlock(heap_size, heap, params.pool);
174
+ if (heapBlock) {
175
+ heapBlock->is_split = is_split;
176
+ }
177
+ }
178
+ [d release];
179
+ }
180
+ return heapBlock;
181
+ }
182
+ static bool Comparator(const HeapBlock* a, const HeapBlock* b) {
183
+ return (a->size.available != b->size.available) ? a->size.available < b->size.available
184
+ : (uintptr_t)a->heap < (uintptr_t)b->heap;
185
+ }
186
+ static NSUInteger heapAvailableSize(id<MTLHeap> heap, size_t Alignment = vm_page_size) {
187
+ return [heap maxAvailableSizeWithAlignment:Alignment];
188
+ }
189
+ NSUInteger Size() {
190
+ return [heap size];
191
+ }
192
+ id<MTLBuffer> newMTLBuffer(size_t length, uint32_t usage) {
193
+ id<MTLBuffer> buf = [heap newBufferWithLength:length options:getOptions(usage)];
194
+ if (buf) {
195
+ updateAvailableSize();
196
+ n_buffers++;
197
+ }
198
+ return buf;
199
+ }
200
+ // returns the retainCount before releasing the buffer
201
+ uint32_t releaseMTLBuffer(id<MTLBuffer>& buffer) {
202
+ const uint32_t retainCount = [buffer retainCount];
203
+ [buffer release];
204
+ buffer = nil;
205
+ updateAvailableSize();
206
+ n_buffers--;
207
+ return retainCount;
208
+ }
209
+ // returns the retainCount before releasing the heap
210
+ uint32_t releaseMTLHeap() {
211
+ const uint32_t retainCount = [heap retainCount];
212
+ TORCH_INTERNAL_ASSERT(!n_buffers); // assert if heap isn't empty
213
+ [heap setPurgeableState:MTLPurgeableStateEmpty];
214
+ [heap release];
215
+ heap = nil;
216
+ size.available = 0;
217
+ return retainCount;
218
+ }
219
+ uint32_t retainCount() const {
220
+ return [heap retainCount];
221
+ }
222
+ void updateAvailableSize() {
223
+ size.available = heapAvailableSize(heap);
224
+ }
225
+ };
226
+ typedef bool (*HeapComparison)(const HeapBlock*, const HeapBlock*);
227
+
228
+ struct BufferPool {
229
+ enum class Kind {
230
+ PRIVATE_SMALL,
231
+ PRIVATE_LARGE,
232
+ SHARED_SMALL,
233
+ SHARED_LARGE,
234
+ SCALAR,
235
+ };
236
+
237
+ BufferPool(const id<MTLDevice> Device, uint32_t Usage)
238
+ : device(Device), usage(Usage), heaps(HeapBlock::Comparator), available_buffers(BufferBlock::Comparator) {}
239
+
240
+ const id<MTLDevice> device;
241
+ // usage flags to customize the pool for various purposes (see UsageFlags enum)
242
+ const uint32_t usage;
243
+ // total number of buffers in the pool
244
+ uint32_t n_buffers = 0;
245
+ // total allocations size on this pool
246
+ size_t allocated_size = 0;
247
+ // total memory available in the pool
248
+ size_t available_size = 0;
249
+ // list of heaps ordered by their "available" (not total) memory size
250
+ std::set<HeapBlock*, HeapComparison> heaps;
251
+ // list of only "available" buffers in the pool (i.e., buffers not in-use)
252
+ std::set<BufferBlock*, BufferComparison> available_buffers;
253
+ // list of buffers that are in a state of "limbo" where they've already been freed
254
+ // from PyTorch-side, but were not returned to pool due to still being
255
+ // in-use by command buffers with retainCount > 1. In this state, the buffer is
256
+ // neither ready to be recycled, nor could be returned to pool as available.
257
+ // These buffers will be returned to pool once the command buffer's
258
+ // completionHandler callbacks are called.
259
+ std::unordered_set<BufferBlock*> buffers_pending_free;
260
+ // list of heaps pending size update
261
+ std::unordered_set<HeapBlock*> heaps_pending_update;
262
+ };
263
+
264
+ class MPSHeapAllocatorImpl {
265
+ public:
266
+ explicit MPSHeapAllocatorImpl()
267
+ : m_device(at::mps::MPSDevice::getInstance()->device()),
268
+ m_max_buffer_size([m_device maxBufferLength]),
269
+ m_stream(getDefaultMPSStream()),
270
+ m_event_pool(getMPSEventPool()) {
271
+ init_allocator();
272
+ }
273
+ ~MPSHeapAllocatorImpl() {
274
+ emptyCache();
275
+ }
276
+ // interface exposed to at::Allocator
277
+ id<MTLBuffer> malloc(size_t size, uint32_t usage);
278
+ // frees a buffer and returns it into buffer pool
279
+ void free(void* ptr);
280
+ // releases all the cached buffers and their associated heaps
281
+ void emptyCache();
282
+ // free inactive buffers that are pending to be freed
283
+ void freeInactiveBuffers();
284
+ // returns true if buffer was allocated from the shared pool
285
+ bool isSharedBuffer(const void* ptr);
286
+ // get the requested unaligned size of an MTLBuffer
287
+ ssize_t getUnalignedBufferSize(const void* ptr);
288
+ // set the shape of a base tensor from a view tensor
289
+ void setBufferShape(const void* ptr, const IntArrayRef& shape);
290
+ // retrieve the shape of a base tensor from a view tensor
291
+ IntArrayRef getBufferShape(const void* ptr);
292
+ // get the unique ID of the buffer
293
+ id_t getBufferId(const void* ptr);
294
+ // allocate a buffer from a specialized pool to import CPU scalars into GPU
295
+ id<MTLBuffer> allocScalarBufferWithValue(void* value, size_t size);
296
+ // returns a CPU-mapping of the input buffer and its retainCount,
297
+ // if only it has Shared storage-mode and allocated on MPSAllocator
298
+ std::pair<const void*, uint32_t> getSharedBufferPtr(const void* buffer);
299
+ // records events for a list of MTLBuffers (list is used to lock the mutex once)
300
+ // returns true if records any event (given if passed buffers exist and are shared-storage)
301
+ bool recordEvents(c10::ArrayRef<const void*> buffers);
302
+ // waits for the event to signal the completion of GPU execution
303
+ // on the passed shared buffers (list is used to lock the mutex once)
304
+ // returns true if actually waited on any event
305
+ bool waitForEvents(c10::ArrayRef<const void*> buffers);
306
+ // this indicates how far (in Megabytes) the current total allocations are from the
307
+ // low watermark limit which is used to detect if we're under memory pressure
308
+ // This returns zero if we've reached the low watermark limit
309
+ ssize_t getLowWatermarkValue();
310
+ // (see m_low_watermark_ratio for description)
311
+ void setLowWatermarkRatio(double ratio);
312
+ // (see m_high_watermark_ratio for description)
313
+ void setHighWatermarkRatio(double ratio);
314
+ // (see m_low_watermark_limit for description)
315
+ size_t getLowWatermarkLimit() const {
316
+ return m_low_watermark_limit;
317
+ }
318
+ // (see m_max_total_allowed_size for description)
319
+ size_t getHighWatermarkLimit() const {
320
+ return m_max_total_allowed_size;
321
+ }
322
+ // (see m_total_allocated_memory for description)
323
+ size_t getTotalAllocatedMemory() const {
324
+ return m_total_allocated_memory;
325
+ }
326
+ // (see m_current_allocated_memory for description)
327
+ size_t getCurrentAllocatedMemory() const {
328
+ return m_current_allocated_memory;
329
+ }
330
+ // total GPU memory allocated in the process by Metal driver; including
331
+ // implicit allocations from MPS/MPSGraph frameworks and MPSHeapAllocatorImpl.
332
+ size_t getDriverAllocatedMemory() const {
333
+ return current_allocated_size();
334
+ }
335
+ // recommended Max memory for Metal
336
+ size_t getRecommendedMaxMemory() const {
337
+ return max_device_size();
338
+ }
339
+ // (see enum DebugVerbosity for description)
340
+ uint32_t getDebugVerbosity() const {
341
+ return m_debug_verbosity;
342
+ }
343
+ // returns the device that we allocate from
344
+ inline id<MTLDevice> Device() const {
345
+ return m_device;
346
+ }
347
+
348
+ // TODO: make a common function to do size unit conversions in PyTorch.
349
+ inline std::string format_size(uint64_t size) const;
350
+
351
+ private:
352
+ // (see m_high_watermark_ratio for description)
353
+ constexpr static double default_high_watermark_ratio = 1.7;
354
+ // we set the allowed upper bound to twice the size of recommendedMaxWorkingSetSize.
355
+ constexpr static double default_high_watermark_upper_bound = 2.0;
356
+ // (see m_low_watermark_ratio for description)
357
+ // on unified memory, we could allocate beyond the recommendedMaxWorkingSetSize
358
+ constexpr static double default_low_watermark_ratio_unified = 1.4;
359
+ constexpr static double default_low_watermark_ratio_discrete = 1.0;
360
+
361
+ const id<MTLDevice> m_device;
362
+ std::recursive_mutex m_mutex;
363
+ // allocated buffers by device pointer
364
+ ska::flat_hash_map<const void*, BufferBlock*> m_allocated_buffers;
365
+ // using a container for pools to simplify iterating them
366
+ ska::flat_hash_map<BufferPool::Kind, std::unique_ptr<BufferPool>> m_pools;
367
+ // total memory allocated by HeapAllocator (including blocks in pools)
368
+ size_t m_total_allocated_memory = 0;
369
+ // currently active memory allocations in use (i.e., blocks not in pools)
370
+ size_t m_current_allocated_memory = 0;
371
+ // max buffer size allowed by Metal
372
+ size_t m_max_buffer_size = 0;
373
+ // maximum total size allowed to be allocated
374
+ size_t m_max_total_allowed_size = 0;
375
+ // high watermark ratio is a hard limit for the total allowed allocations
376
+ // 0. : disables high watermark limit (may cause system failure if system-wide OOM occurs)
377
+ // 1. : recommended maximum allocation size (i.e., device.recommendedMaxWorkingSetSize)
378
+ // >1.: allows limits beyond the device.recommendedMaxWorkingSetSize
379
+ // e.g., value 0.95 means we allocate up to 95% of recommended maximum
380
+ // allocation size; beyond that, the allocations would fail with OOM error.
381
+ double m_high_watermark_ratio;
382
+ // low watermark ratio is a soft limit to attempt limiting memory allocations up to the lower watermark
383
+ // level by garbage collection or committing command buffers more frequently (a.k.a, adaptive commit).
384
+ // Value between 0 to m_high_watermark_ratio (setting 0.0 disables adaptive commit and garbage collection)
385
+ // e.g., value 0.9 means we 'attempt' to limit allocations up to 90% of recommended maximum
386
+ // allocation size.
387
+ double m_low_watermark_ratio;
388
+ // low watermark size limit (in Bytes) at the time we initialize the allocator
389
+ size_t m_low_watermark_limit;
390
+ // use "PYTORCH_DEBUG_MPS_ALLOCATOR" env-var to set debug verbosity
391
+ uint32_t m_debug_verbosity;
392
+ // default MPS stream
393
+ MPSStream* m_stream;
394
+ // we hold a reference to MPSEventPool so it could get destroyed after MPSAllocator
395
+ std::shared_ptr<MPSEventPool> m_event_pool;
396
+
397
+ void init_allocator();
398
+ void init_buffer_pools();
399
+ HeapBlock* get_free_heap(AllocParams& params);
400
+ bool get_free_buffer(AllocParams& params);
401
+ BufferBlock* get_allocated_buffer_block(const void* ptr);
402
+ BufferBlock* alloc_buffer_block(size_t size, uint32_t usage);
403
+ bool alloc_buffer(AllocParams& params);
404
+ void free_buffer(BufferBlock* buffer_block);
405
+ // returns true if the container heap is also released
406
+ bool release_buffer(BufferBlock* buffer_block, bool remove_empty_heap = true);
407
+ void release_buffers(BufferPool& pool);
408
+ bool release_available_cached_buffers(AllocParams& params);
409
+ bool release_cached_buffers();
410
+ // free unused cached blocks to reclaim GPU memory if memory pressure is high
411
+ void garbage_collect_cached_buffers(AllocParams& params);
412
+ // returns the suitable buffer pool type for the usage or
413
+ // requested/allocated sizes
414
+ BufferPool& get_pool(size_t requested_size, size_t aligned_size, uint32_t usage);
415
+ // returns the aligned allocation size that is optimized
416
+ // for the buffers to get reused frequently
417
+ size_t get_allocation_size(size_t size, uint32_t usage) const;
418
+ // maximum size of device memory available for allocation in current process
419
+ // Note: the recommendedMaxWorkingSetSize is typically 75% of the total system memory.
420
+ size_t max_device_size() const {
421
+ return [m_device recommendedMaxWorkingSetSize];
422
+ }
423
+ // there are implicit allocations from MPS backend, so we need to query the 'device' for
424
+ // total allocated size instead of manually tracking in MPSAllocator
425
+ size_t current_allocated_size() const {
426
+ return [m_device currentAllocatedSize];
427
+ }
428
+
429
+ bool trigger_memory_callbacks(BufferBlock* buffer_block, IMpsAllocatorCallback::EventType event) const {
430
+ for (const auto& name : MPSAllocatorCallbacksRegistry()->Keys()) {
431
+ MPSAllocatorCallbacksRegistry()->Create(name)->executeMPSAllocatorCallback(
432
+ buffer_block ? buffer_block->buffer : nullptr, event);
433
+ }
434
+ return true;
435
+ }
436
+ };
437
+
438
+ } // namespace at::mps::HeapAllocator
lib/python3.12/site-packages/torch/include/ATen/mps/MPSAllocatorInterface.h ADDED
@@ -0,0 +1,68 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2023 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/core/ATen_fwd.h>
6
+ #include <c10/core/Allocator.h>
7
+ #include <c10/util/Registry.h>
8
+
9
+ #define MB(x) (x * 1048576UL)
10
+
11
+ namespace at::mps {
12
+
13
+ // this is a public interface to access MPSAllocator.
14
+ // Do not declare methods that would depend on MPS or Metal frameworks.
15
+ class IMPSAllocator : public c10::Allocator {
16
+ public:
17
+ // see the comments in MPSAllocator.h for the description of these methods.
18
+ virtual void emptyCache() const = 0;
19
+ virtual void freeInactiveBuffers() const = 0;
20
+ virtual ssize_t getUnalignedBufferSize(const void* ptr) const = 0;
21
+ virtual IntArrayRef getBufferShape(const void* ptr) const = 0;
22
+ virtual id_t getBufferId(const void* ptr) const = 0;
23
+ virtual void setBufferShape(const void* ptr, const IntArrayRef& shape)
24
+ const = 0;
25
+ virtual bool isSharedBuffer(const void* ptr) const = 0;
26
+ virtual bool isSharedStorageSupported() const = 0;
27
+ virtual c10::DataPtr allocScalarBufferWithValue(void* value, size_t size)
28
+ const = 0;
29
+ virtual std::string formatSize(size_t size) const = 0;
30
+ virtual void setLowWatermarkRatio(double ratio) const = 0;
31
+ virtual void setHighWatermarkRatio(double ratio) const = 0;
32
+ virtual ssize_t getLowWatermarkValue() const = 0;
33
+ virtual size_t getLowWatermarkLimit() const = 0;
34
+ virtual size_t getHighWatermarkLimit() const = 0;
35
+ virtual size_t getTotalAllocatedMemory() const = 0;
36
+ virtual size_t getCurrentAllocatedMemory() const = 0;
37
+ virtual size_t getDriverAllocatedMemory() const = 0;
38
+ virtual size_t getRecommendedMaxMemory() const = 0;
39
+ virtual std::pair<const void*, uint32_t> getSharedBufferPtr(
40
+ const void* ptr) const = 0;
41
+ virtual bool recordEvents(c10::ArrayRef<const void*> buffers) const = 0;
42
+ virtual bool waitForEvents(c10::ArrayRef<const void*> buffers) const = 0;
43
+ };
44
+
45
+ class IMpsAllocatorCallback {
46
+ public:
47
+ enum class EventType {
48
+ ALLOCATED, // buffer got allocated to be used immediately
49
+ RECYCLED, // buffer pulled from free list to be reused
50
+ FREED, // buffer put to free list for future recycling
51
+ RELEASED, // buffer memory released
52
+ ALLOCATION_FAILED // buffer allocation failed
53
+ };
54
+ virtual ~IMpsAllocatorCallback() = default;
55
+ virtual void executeMPSAllocatorCallback(void* ptr, EventType event) = 0;
56
+ };
57
+
58
+ // MPS allocator will execute every registered callback when a block of memory
59
+ // is freed.
60
+ TORCH_DECLARE_REGISTRY(MPSAllocatorCallbacksRegistry, IMpsAllocatorCallback);
61
+ #define REGISTER_MPS_ALLOCATOR_CALLBACK(name, ...) \
62
+ C10_REGISTER_CLASS(MPSAllocatorCallbacksRegistry, name, __VA_ARGS__)
63
+
64
+ IMPSAllocator* getIMPSAllocator(bool sharedAllocator = false);
65
+
66
+ bool isMPSPinnedPtr(const void* data);
67
+
68
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSDevice.h ADDED
@@ -0,0 +1,72 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+ #include <c10/core/Allocator.h>
5
+ #include <c10/macros/Macros.h>
6
+ #include <c10/util/Exception.h>
7
+
8
+ #ifdef __OBJC__
9
+ #include <Foundation/Foundation.h>
10
+ #include <Metal/Metal.h>
11
+ typedef id<MTLDevice> MTLDevice_t;
12
+ #else
13
+ typedef void* MTLDevice_t;
14
+ #endif
15
+
16
+ namespace at::mps {
17
+
18
+ // Helper enum to check if a MPSGraph op is supported in a given macOS version
19
+ enum class MacOSVersion : uint32_t {
20
+ MACOS_VER_13_1_PLUS = 0,
21
+ MACOS_VER_13_2_PLUS,
22
+ MACOS_VER_13_3_PLUS,
23
+ MACOS_VER_14_0_PLUS,
24
+ MACOS_VER_14_4_PLUS,
25
+ MACOS_VER_15_0_PLUS,
26
+ MACOS_VER_15_1_PLUS,
27
+ };
28
+
29
+ //-----------------------------------------------------------------
30
+ // MPSDevice
31
+ //
32
+ // MPSDevice is a singleton class that returns the default device
33
+ //-----------------------------------------------------------------
34
+
35
+ class TORCH_API MPSDevice {
36
+ public:
37
+ /**
38
+ * MPSDevice should not be cloneable.
39
+ */
40
+ MPSDevice(MPSDevice& other) = delete;
41
+ /**
42
+ * MPSDevice should not be assignable.
43
+ */
44
+ void operator=(const MPSDevice&) = delete;
45
+ /**
46
+ * Gets single instance of the Device.
47
+ */
48
+ static MPSDevice* getInstance();
49
+ /**
50
+ * Returns the single device.
51
+ */
52
+ MTLDevice_t device() {
53
+ return _mtl_device;
54
+ }
55
+ /**
56
+ * Returns whether running on Ventura or newer
57
+ */
58
+ bool isMacOS13Plus(MacOSVersion version) const;
59
+
60
+ ~MPSDevice();
61
+
62
+ private:
63
+ static MPSDevice* _device;
64
+ MTLDevice_t _mtl_device;
65
+ MPSDevice();
66
+ };
67
+
68
+ TORCH_API bool is_available();
69
+ TORCH_API bool is_macos_13_or_newer(MacOSVersion version);
70
+ TORCH_API at::Allocator* GetMPSAllocator(bool useSharedAllocator = false);
71
+
72
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSEvent.h ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2023 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/mps/MPSStream.h>
6
+ #include <ctime>
7
+ #include <stack>
8
+
9
+ namespace at::mps {
10
+
11
+ // NOTE: don't create instances of this class directly.
12
+ // Use MPSEventPool to acquire instances of MPSEvent.
13
+ class MPSEvent {
14
+ public:
15
+ explicit MPSEvent(id_t ID, MPSStream* stream, bool enable_timing);
16
+ ~MPSEvent();
17
+
18
+ // records an event on the stream
19
+ void record(bool needsLock, bool syncEvent = false);
20
+ // makes all future work submitted to the stream wait for this event.
21
+ bool wait(bool needsLock, bool syncEvent = false);
22
+ // schedules a notifyListener callback for the event.
23
+ bool notify(bool needsLock, MTLSharedEventNotificationBlock block);
24
+ // checks if events are already signaled.
25
+ bool query() const;
26
+ // blocks the CPU thread until all the GPU work that were scheduled
27
+ // prior to recording this event are completed.
28
+ bool synchronize();
29
+ // resets this event with new parameters in case it gets reused from the event
30
+ // pool
31
+ void reset(MPSStream* stream, bool enable_timing);
32
+ // returns the unique ID of the event instance
33
+ id_t getID() const {
34
+ return m_id;
35
+ }
36
+ // returns the completion timestamp of the event
37
+ uint64_t getCompletionTime() const {
38
+ return m_completion_time;
39
+ }
40
+ // if already recorded, waits for cpu_sync_cv to be signaled
41
+ void waitForCpuSync();
42
+
43
+ private:
44
+ id_t m_id;
45
+ // enables measuring the completion time of the notifyListener of this event
46
+ bool m_enable_timing;
47
+ uint64_t m_signalCounter = 0;
48
+ MPSStream* m_stream = nullptr;
49
+ MTLSharedEvent_t m_event = nullptr;
50
+ MTLSharedEventListener* m_listener = nullptr;
51
+ // used to sync the events created on this Stream with CPU
52
+ std::mutex m_cpu_sync_mutex{};
53
+ std::condition_variable m_cpu_sync_cv{};
54
+ // CondVar predicate to sync the events created on this Stream with CPU
55
+ bool m_cpu_sync_completed = false;
56
+ // used to compute elapsed time
57
+ uint64_t m_completion_time = 0;
58
+
59
+ void recordLocked(bool syncEvent);
60
+ bool waitLocked(bool syncEvent);
61
+ bool notifyLocked(MTLSharedEventNotificationBlock block);
62
+ void notifyCpuSync();
63
+ static uint64_t getTime() {
64
+ return clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW);
65
+ }
66
+ };
67
+
68
+ typedef std::unique_ptr<MPSEvent, std::function<void(MPSEvent*)>> MPSEventPtr;
69
+
70
+ class MPSEventPool {
71
+ public:
72
+ explicit MPSEventPool(MPSStream* default_stream);
73
+ ~MPSEventPool();
74
+
75
+ MPSEventPtr acquireEvent(bool enable_timing, MPSStream* stream);
76
+ void emptyCache();
77
+
78
+ // these are mainly used for MPSHooks and torch.mps.Event() bindings
79
+ id_t acquireEvent(bool enable_timing);
80
+ void releaseEvent(id_t event_id);
81
+ void recordEvent(id_t event_id, bool syncEvent);
82
+ void waitForEvent(id_t event_id, bool syncEvent);
83
+ void synchronizeEvent(id_t event_id);
84
+ bool queryEvent(id_t event_id);
85
+ // returns elapsed time between two recorded events in milliseconds
86
+ double elapsedTime(id_t start_event_id, id_t end_event_id);
87
+
88
+ private:
89
+ MPSStream* m_default_stream = nullptr;
90
+ std::recursive_mutex m_mutex;
91
+ std::stack<std::unique_ptr<MPSEvent>> m_pool{};
92
+ // dictionary to associate event IDs with event objects
93
+ // used to retain in-use events out of the pool
94
+ // for torch.mps.Event() bindings.
95
+ std::unordered_map<id_t, MPSEventPtr> m_in_use_events{};
96
+ uint64_t m_event_counter = 0;
97
+ std::function<void(MPSEvent*)> m_default_deleter;
98
+
99
+ MPSEvent* getInUseEvent(id_t event_id, bool locked = true);
100
+ };
101
+
102
+ // shared_ptr is used to get MPSEventPool destroyed after dependent instances
103
+ std::shared_ptr<MPSEventPool> getMPSEventPool();
104
+
105
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSGeneratorImpl.h ADDED
@@ -0,0 +1,61 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/core/Generator.h>
6
+ #include <ATen/core/PhiloxRNGEngine.h>
7
+ #include <c10/core/GeneratorImpl.h>
8
+ #include <optional>
9
+
10
+ namespace at {
11
+ namespace mps::detail {
12
+
13
+ constexpr uint32_t PHILOX_STATE_N = 7;
14
+ struct rng_data_pod {
15
+ std::array<uint32_t, PHILOX_STATE_N> state{1};
16
+ uint64_t seed = default_rng_seed_val;
17
+ };
18
+
19
+ TORCH_API const Generator& getDefaultMPSGenerator();
20
+ TORCH_API Generator
21
+ createMPSGenerator(uint64_t seed_val = default_rng_seed_val);
22
+
23
+ } // namespace mps::detail
24
+
25
+ struct TORCH_API MPSGeneratorImpl : public c10::GeneratorImpl {
26
+ // Constructors
27
+ MPSGeneratorImpl(uint64_t seed_in = default_rng_seed_val);
28
+ ~MPSGeneratorImpl() override = default;
29
+
30
+ // MPSGeneratorImpl methods
31
+ std::shared_ptr<MPSGeneratorImpl> clone() const;
32
+ void set_current_seed(uint64_t seed) override;
33
+ void set_offset(uint64_t offset) override;
34
+ uint64_t get_offset() const override;
35
+ uint64_t current_seed() const override;
36
+ uint64_t seed() override;
37
+ void set_state(const c10::TensorImpl& new_state) override;
38
+ c10::intrusive_ptr<c10::TensorImpl> get_state() const override;
39
+ void update_philox_counters();
40
+
41
+ void set_engine(at::Philox4_32 engine) {
42
+ engine_ = engine;
43
+ }
44
+ at::Philox4_32 engine() {
45
+ return engine_;
46
+ }
47
+ uint32_t* state_data() {
48
+ return data_.state.data();
49
+ }
50
+ static DeviceType device_type() {
51
+ return DeviceType::MPS;
52
+ }
53
+
54
+ private:
55
+ mps::detail::rng_data_pod data_;
56
+ at::Philox4_32 engine_;
57
+
58
+ MPSGeneratorImpl* clone_impl() const override;
59
+ };
60
+
61
+ } // namespace at
lib/python3.12/site-packages/torch/include/ATen/mps/MPSGuardImpl.h ADDED
@@ -0,0 +1,179 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+ #include <ATen/Context.h>
5
+ #include <ATen/mps/MPSEvent.h>
6
+ #include <ATen/mps/MPSStream.h>
7
+ #include <c10/core/impl/DeviceGuardImplInterface.h>
8
+ #include <c10/macros/Macros.h>
9
+ #include <c10/util/Exception.h>
10
+
11
+ #ifdef __OBJC__
12
+ #include <Foundation/Foundation.h>
13
+ #include <Metal/Metal.h>
14
+ #include <MetalPerformanceShaders/MetalPerformanceShaders.h>
15
+ #endif
16
+
17
+ #include <ATen/Tensor.h>
18
+ #include <c10/core/MemoryFormat.h>
19
+ #include <c10/core/Storage.h>
20
+ #include <c10/core/TensorImpl.h>
21
+ #include <c10/core/UndefinedTensorImpl.h>
22
+ #include <c10/util/intrusive_ptr.h>
23
+ #include <sys/_types/_size_t.h>
24
+ #include <memory>
25
+
26
+ namespace at::mps {
27
+
28
+ typedef MPSEvent* mpsEvent_t;
29
+
30
+ // TODO: Move the MPSGuardImpl to inherit from NoOpDeviceGuardImpl
31
+ // https://github.com/pytorch/pytorch/issues/77170
32
+ struct TORCH_API MPSGuardImpl final
33
+ : public c10::impl::DeviceGuardImplInterface {
34
+ static constexpr c10::DeviceType static_type = c10::DeviceType::MPS;
35
+
36
+ // constructor
37
+ MPSGuardImpl() {}
38
+ explicit MPSGuardImpl(c10::DeviceType t) {
39
+ TORCH_INTERNAL_ASSERT(t == c10::DeviceType::MPS);
40
+ }
41
+
42
+ // returns the type
43
+ c10::DeviceType type() const override {
44
+ return c10::DeviceType::MPS;
45
+ }
46
+
47
+ Device exchangeDevice(Device d) const override {
48
+ return Device(c10::DeviceType::MPS, 0);
49
+ }
50
+
51
+ Device getDevice() const override {
52
+ return Device(c10::DeviceType::MPS, 0);
53
+ }
54
+
55
+ std::optional<Device> uncheckedGetDevice() const noexcept {
56
+ return Device(c10::DeviceType::MPS, 0);
57
+ }
58
+
59
+ void setDevice(Device d) const override {
60
+ TORCH_INTERNAL_ASSERT(d.is_mps());
61
+ }
62
+
63
+ void uncheckedSetDevice(Device d) const noexcept override {
64
+ // TODO: Currently setting only device 0
65
+ }
66
+
67
+ Stream getStream(Device d) const noexcept override {
68
+ return Stream(Stream::DEFAULT, Device(c10::DeviceType::MPS, 0));
69
+ }
70
+
71
+ Stream getNewStream(Device, int priority = 0) const override {
72
+ (void)priority;
73
+ return Stream(Stream::DEFAULT, Device(c10::DeviceType::MPS, 0));
74
+ }
75
+
76
+ Stream getDefaultStream(Device d) const override {
77
+ return Stream(Stream::DEFAULT, Device(c10::DeviceType::MPS, 0));
78
+ }
79
+
80
+ // NB: These do NOT set the current device
81
+ Stream exchangeStream(Stream s) const noexcept override {
82
+ return Stream(Stream::DEFAULT, Device(c10::DeviceType::MPS, 0));
83
+ }
84
+ DeviceIndex deviceCount() const noexcept override {
85
+ if (at::hasMPS()) {
86
+ // TODO: extend it for multi-device case
87
+ return 1;
88
+ } else {
89
+ return 0;
90
+ }
91
+ }
92
+
93
+ // Event-related functions
94
+ void createEvent(mpsEvent_t* event, const EventFlag flag) const;
95
+
96
+ void destroyEvent(void* event, const DeviceIndex device_index)
97
+ const noexcept override;
98
+
99
+ void record(
100
+ void** event,
101
+ const Stream& stream,
102
+ const DeviceIndex device_index,
103
+ const EventFlag flag) const override;
104
+
105
+ void block(void* event, const Stream& stream) const override;
106
+
107
+ bool queryEvent(void* event) const override;
108
+
109
+ void synchronizeEvent(void* event) const override;
110
+
111
+ double elapsedTime(void* event1, void* event2, const DeviceIndex device_index)
112
+ const override;
113
+
114
+ void synchronizeDevice(const DeviceIndex device_index) const override;
115
+ };
116
+
117
+ /// A variant of OptionalDeviceGuard that is specialized for MPS.
118
+ struct OptionalMPSGuard {
119
+ explicit OptionalMPSGuard() : guard_() {}
120
+
121
+ explicit OptionalMPSGuard(std::optional<Device> device_opt)
122
+ : guard_(device_opt) {}
123
+
124
+ /// Set the current MPS device to the passed device index, if it is not
125
+ /// nullopt
126
+ explicit OptionalMPSGuard(std::optional<DeviceIndex> device_index_opt)
127
+ : guard_(device_index_opt) {}
128
+
129
+ // Copy is not allowed
130
+ OptionalMPSGuard(const OptionalMPSGuard&) = delete;
131
+ OptionalMPSGuard& operator=(const OptionalMPSGuard&) = delete;
132
+ OptionalMPSGuard(OptionalMPSGuard&& other) = delete;
133
+ OptionalMPSGuard& operator=(OptionalMPSGuard&& other) = delete;
134
+
135
+ /// Sets the MPS device to the given device, initializing the guard if it
136
+ /// is not already initialized. Errors if the given device is not a MPS
137
+ /// device.
138
+ void set_device(Device device) {
139
+ guard_.set_device(device);
140
+ }
141
+
142
+ /// Sets the MPS device to the given device, initializing the guard if it is
143
+ /// not already initialized. Errors if the given device is not a MPS device.
144
+ void reset_device(Device device) {
145
+ guard_.reset_device(device);
146
+ }
147
+
148
+ /// Sets the MPS device to the given device index, initializing the guard if
149
+ /// it is not already initialized.
150
+ void set_index(DeviceIndex device_index) {
151
+ guard_.set_index(device_index);
152
+ }
153
+
154
+ /// Returns the device that was set immediately prior to initialization of the
155
+ /// guard, or nullopt if the guard is uninitialized.
156
+ std::optional<Device> original_device() const {
157
+ return guard_.original_device();
158
+ }
159
+
160
+ /// Returns the most recent device that was set using this device guard,
161
+ /// either from construction, or via set_device, if the guard is initialized,
162
+ /// or nullopt if the guard is uninitialized.
163
+ std::optional<Device> current_device() const {
164
+ return guard_.current_device();
165
+ }
166
+
167
+ /// Restore the original MPS device, resetting this guard to uninitialized
168
+ /// state.
169
+ void reset() {
170
+ guard_.reset();
171
+ }
172
+
173
+ private:
174
+ c10::impl::InlineOptionalDeviceGuard<MPSGuardImpl> guard_;
175
+ };
176
+
177
+ C10_REGISTER_GUARD_IMPL(MPS, MPSGuardImpl)
178
+
179
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSHooks.h ADDED
@@ -0,0 +1,63 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/Generator.h>
6
+ #include <ATen/detail/MPSHooksInterface.h>
7
+ #include <ATen/mps/MPSEvent.h>
8
+ #include <optional>
9
+
10
+ namespace at::mps {
11
+
12
+ // The real implementation of MPSHooksInterface
13
+ struct MPSHooks : public at::MPSHooksInterface {
14
+ MPSHooks(at::MPSHooksArgs) {}
15
+ void init() const override;
16
+
17
+ // MPSDevice interface
18
+ bool hasMPS() const override;
19
+ bool isOnMacOSorNewer(unsigned major, unsigned minor) const override;
20
+
21
+ // MPSGeneratorImpl interface
22
+ const Generator& getDefaultGenerator(
23
+ DeviceIndex device_index = -1) const override;
24
+
25
+ // MPSStream interface
26
+ void deviceSynchronize() const override;
27
+ void commitStream() const override;
28
+ void* getCommandBuffer() const override;
29
+ void* getDispatchQueue() const override;
30
+
31
+ // MPSAllocator interface
32
+ Allocator* getMPSDeviceAllocator() const override;
33
+ void emptyCache() const override;
34
+ size_t getCurrentAllocatedMemory() const override;
35
+ size_t getDriverAllocatedMemory() const override;
36
+ size_t getRecommendedMaxMemory() const override;
37
+ void setMemoryFraction(double ratio) const override;
38
+ bool isPinnedPtr(const void* data) const override;
39
+ Allocator* getPinnedMemoryAllocator() const override;
40
+
41
+ // MPSProfiler interface
42
+ void profilerStartTrace(const std::string& mode, bool waitUntilCompleted)
43
+ const override;
44
+ void profilerStopTrace() const override;
45
+
46
+ // MPSEvent interface
47
+ uint32_t acquireEvent(bool enable_timing) const override;
48
+ void releaseEvent(uint32_t event_id) const override;
49
+ void recordEvent(uint32_t event_id) const override;
50
+ void waitForEvent(uint32_t event_id) const override;
51
+ void synchronizeEvent(uint32_t event_id) const override;
52
+ bool queryEvent(uint32_t event_id) const override;
53
+ double elapsedTimeOfEvents(uint32_t start_event_id, uint32_t end_event_id)
54
+ const override;
55
+
56
+ // Compatibility with Accelerator API
57
+ bool hasPrimaryContext(DeviceIndex device_index) const override {
58
+ // When MPS is available, it is always in use for the one device.
59
+ return true;
60
+ }
61
+ };
62
+
63
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSProfiler.h ADDED
@@ -0,0 +1,480 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/Tensor.h>
6
+ #include <ATen/mps/MPSAllocatorInterface.h>
7
+ #include <ATen/mps/MPSStream.h>
8
+
9
+ #include <os/log.h>
10
+ #include <os/signpost.h>
11
+
12
+ #include <atomic>
13
+ #include <ctime>
14
+ #include <sstream>
15
+ #include <string>
16
+ #include <unordered_map>
17
+ #include <utility>
18
+
19
+ namespace at::mps {
20
+
21
+ namespace Profiler {
22
+
23
+ struct BaseInfo {
24
+ // profiling info types
25
+ enum class Type {
26
+ GRAPH,
27
+ KERNEL,
28
+ COPY,
29
+ CPU_FALLBACK,
30
+ };
31
+
32
+ BaseInfo(Type infoType, uint64_t Id, const uintptr_t Handle)
33
+ : type(infoType), profileId(Id), handle(Handle) {}
34
+ virtual ~BaseInfo() = default;
35
+
36
+ // type of profiling info
37
+ Type type;
38
+ // unique profile ID for execution instances of operations or copies
39
+ uint64_t profileId;
40
+ // ID generated by os_signpost
41
+ // since it's possible to use event and interval-based signposts at the
42
+ // same time, we need separate IDs for each.
43
+ os_signpost_id_t eventSignpostId = 0, intervalSignpostId = 0;
44
+ // accumulated GPU time in ms (obtained from CompletionHandler's "GPUEndTime -
45
+ // GPUStartTime")
46
+ std::atomic<double> totalGpuTime{0.0};
47
+ // accumulated Scheduling time in ms (obtained from CompletionHandler's
48
+ // "KernelEndTime - KernelStartTime")
49
+ std::atomic<double> totalSchedulingTime{0.0};
50
+ // indicates if the operation or copy execution has completed
51
+ std::atomic_bool completed{false};
52
+ // handle used to identify the profile info's instance (usually the pointer)
53
+ const uintptr_t handle;
54
+
55
+ virtual const std::string toString(
56
+ double gpuTime = 0,
57
+ double schedulingTime = 0) const;
58
+ // builds a string for a tensor (format: Device:ScalarType[tensor.sizes()])
59
+ static std::string buildTensorString(
60
+ const Tensor& tensor,
61
+ bool includeBufferId = false) {
62
+ if (tensor.defined()) {
63
+ std::stringstream tensorStr;
64
+ auto deviceType = tensor.device().type();
65
+ tensorStr << c10::DeviceTypeName(deviceType);
66
+ // see comments for INCLUDE_BUFFER_ID
67
+ if (includeBufferId && deviceType == at::kMPS) {
68
+ id<MTLBuffer> buffer =
69
+ __builtin_bit_cast(id<MTLBuffer>, tensor.storage().data());
70
+ tensorStr << "(buf#" << (getIMPSAllocator()->getBufferId(buffer)) << ":"
71
+ << buffer.retainCount << ")";
72
+ }
73
+ tensorStr << ":" << tensor.scalar_type() << tensor.sizes();
74
+ return tensorStr.str();
75
+ } else {
76
+ return "undefined";
77
+ }
78
+ }
79
+ static uint64_t getTime() {
80
+ return clock_gettime_nsec_np(CLOCK_MONOTONIC_RAW);
81
+ }
82
+ };
83
+
84
+ struct OperationInfo : BaseInfo {
85
+ OperationInfo(
86
+ const void* Handle,
87
+ bool IsGraph,
88
+ uint64_t Id,
89
+ const std::string& StrKey)
90
+ : BaseInfo(IsGraph ? Type::GRAPH : Type::KERNEL, Id, uintptr_t(Handle)),
91
+ strKey(StrKey) {}
92
+
93
+ uint64_t runCount = 0;
94
+ std::string strKey;
95
+
96
+ const std::string toString(double gpuTime = 0, double schedulingTime = 0)
97
+ const override;
98
+
99
+ // builds a string for a kernel
100
+ static std::string buildKernelString(
101
+ const std::string& kernelName,
102
+ const TensorList& tensors,
103
+ bool includeBufferId = false) {
104
+ std::stringstream kernelStr;
105
+ kernelStr << kernelName;
106
+ for (const Tensor& tensor : tensors) {
107
+ kernelStr << ":" << BaseInfo::buildTensorString(tensor, includeBufferId);
108
+ }
109
+ return kernelStr.str();
110
+ }
111
+ };
112
+
113
+ struct CpuFbInfo : BaseInfo {
114
+ CpuFbInfo(uint64_t Id, const std::string& OpName)
115
+ : BaseInfo(Type::CPU_FALLBACK, Id, 0), opName(OpName) {}
116
+
117
+ uint64_t runCount = 0;
118
+ // the current and total overhead of copies in bytes required to convert the
119
+ // Op's input tensors from MPS to CPU and then output from CPU back to MPS
120
+ size_t currentCopyOverhead = 0;
121
+ size_t totalCopyOverhead = 0;
122
+ std::string opName;
123
+ std::string strKey;
124
+ uint64_t startTime = 0;
125
+
126
+ const std::string toString(double gpuTime = 0, double schedulingTime = 0)
127
+ const override;
128
+
129
+ void updateCopyOverhead(const TensorList& tensors) {
130
+ currentCopyOverhead = 0;
131
+ for (const Tensor& tensor : tensors) {
132
+ if (tensor.defined()) {
133
+ currentCopyOverhead += tensor.nbytes();
134
+ }
135
+ }
136
+ totalCopyOverhead += currentCopyOverhead;
137
+ }
138
+ };
139
+
140
+ struct CopyInfo : BaseInfo {
141
+ enum class Kind {
142
+ MPS_TO_MPS,
143
+ MPS_TO_CPU,
144
+ CPU_TO_MPS,
145
+ };
146
+
147
+ CopyInfo(
148
+ const void* Handle,
149
+ size_t Length,
150
+ uint64_t Id,
151
+ bool IsNonBlocking,
152
+ bool UsesBlitter)
153
+ : BaseInfo(Type::COPY, Id, uintptr_t(Handle)),
154
+ kind(Kind::MPS_TO_MPS),
155
+ length(Length),
156
+ isNonBlocking(IsNonBlocking),
157
+ usesBlitter(UsesBlitter) {}
158
+
159
+ Kind kind;
160
+ size_t length;
161
+ bool isNonBlocking;
162
+ bool usesBlitter;
163
+ std::string srcStrKey;
164
+ std::string dstStrKey;
165
+ // for copies that don't use blitters, we measure CPU time
166
+ uint64_t startTime = 0;
167
+
168
+ const std::string toString(double gpuTime = 0, double schedulingTime = 0)
169
+ const override;
170
+
171
+ static std::string buildTensorString(
172
+ const void* buffer,
173
+ const OptionalTensorRef tensor,
174
+ bool includeBufferId = false);
175
+
176
+ static bool isStorageOnMPS(
177
+ const void* buffer,
178
+ const OptionalTensorRef tensor) {
179
+ if (tensor.has_value()) {
180
+ return tensor->device().type() == at::kMPS;
181
+ }
182
+ TORCH_INTERNAL_ASSERT_DEBUG_ONLY(buffer);
183
+ // getUnalignedBufferSize() returns -1 if input buffer is not on MPS device
184
+ return getIMPSAllocator()->getUnalignedBufferSize(buffer) >= 0;
185
+ }
186
+
187
+ static Kind getCopyKind(
188
+ const void* srcBuffer,
189
+ const void* dstBuffer,
190
+ const OptionalTensorRef srcTensor,
191
+ const OptionalTensorRef dstTensor) {
192
+ const bool isSrcOnMPS = isStorageOnMPS(srcBuffer, srcTensor);
193
+ const bool isDstOnMPS = isStorageOnMPS(dstBuffer, dstTensor);
194
+ TORCH_INTERNAL_ASSERT_DEBUG_ONLY(isSrcOnMPS || isDstOnMPS);
195
+ if (isSrcOnMPS && !isDstOnMPS) {
196
+ return Kind::MPS_TO_CPU;
197
+ } else if (!isSrcOnMPS && isDstOnMPS) {
198
+ return Kind::CPU_TO_MPS;
199
+ }
200
+ return Kind::MPS_TO_MPS;
201
+ }
202
+ };
203
+
204
+ struct CopyStat : CopyInfo {
205
+ explicit CopyStat(std::string CopyKindStr)
206
+ : CopyInfo(nullptr, 0, 0, false, false),
207
+ kindStr(std::move(CopyKindStr)) {}
208
+ // total number of copies
209
+ size_t totalCount = 0;
210
+ // number of Scalar copies (i.e., less than sizeof(int64))
211
+ size_t scalarsCount = 0;
212
+ // number of blocking copies (i.e., require syncing to GPU)
213
+ size_t blockingCount = 0;
214
+ // number of copies that used memcpy(), instead of Metal Blit Encoder
215
+ size_t memcpyCount = 0;
216
+ // accumulated GPU time in ms for the scalar copies
217
+ std::atomic<double> scalarsGpuTime{0.0};
218
+ // copy kind in string type
219
+ std::string kindStr;
220
+ };
221
+
222
+ class MPSProfiler {
223
+ public:
224
+ // lower 16 bits used for profiler options
225
+ enum ProfileOptions : uint32_t {
226
+ OPTIONS_NONE = 0,
227
+ // ALL_* means, all signpost types (RUN_OPERATION|BLIT_COPY|CPU_FALLBACK,
228
+ // etc.) (used for convenience to not compute bit flags by OR-ing manually)
229
+ // trace all signpost types using events
230
+ ALL_SIGNPOST_EVENTS = (1 << 0),
231
+ // trace all signpost types using intervals
232
+ ALL_SIGNPOST_INTERVALS = (1 << 1),
233
+ // always wait for command buffer to finish executing after each commit
234
+ WAIT_UNTIL_COMPLETED = (1 << 2),
235
+ // for interval-based signposts, include the scheduling portion of
236
+ // Graph/Kernel/Copy executions as well.
237
+ // if flag is disable, only "GPU run time" is included in interval,
238
+ // and not schedule time.
239
+ INCLUDE_SCHEDULE_INTERVAL = (1 << 3),
240
+
241
+ // use these if you need to trace signposts types individually (rarely
242
+ // required) trace signpost using intervals
243
+ USE_INTERVALS = (1 << 4),
244
+ // trace signpost by emitting events
245
+ USE_EVENTS = (1 << 5),
246
+ // used for sanity check (Change this when new option added)
247
+ OPTIONS_COUNT = (USE_EVENTS << 1) - 1,
248
+ };
249
+
250
+ // when adding new types, #define the type string in MPSProfiler.mm as well.
251
+ // upper 16 bits used for event types
252
+ enum SignpostTypes : uint32_t {
253
+ SIGNPOST_NONE = 0,
254
+ // trace signposts for PyTorch operation executions
255
+ RUN_OPERATION = (1 << 16),
256
+ // trace signposts for blitter copies
257
+ BLIT_COPY = (1 << 17),
258
+ // trace signposts for ops that fall back on CPU
259
+ CPU_FALLBACK = (1 << 18),
260
+ // used for sanity check (Change this when new type added)
261
+ SIGNPOST_COUNT = (CPU_FALLBACK << 1) - 1,
262
+ };
263
+
264
+ enum LogOptions : uint32_t {
265
+ LOG_NONE = 0,
266
+
267
+ // Info logging options during execution
268
+ // -------------------------------------
269
+ // prints operation info (id/key/run_count) during execution
270
+ OPERATION_INFO = (1 << 0),
271
+ // prints copy info (src/dst tensors/buffers, size, etc.) during execution
272
+ COPY_INFO = (1 << 1),
273
+ // prints CPU Fallback info (id/runCount/opName/copyOverhead) during
274
+ // execution
275
+ CPU_FALLBACK_INFO = (1 << 2),
276
+
277
+ // Profiling Statistics logging options when process terminates
278
+ // ------------------------------------------------------------
279
+ // prints all stats (OPERATION_STATS, COPY_STATS, CPU_FALLBACK_STATS) before
280
+ // process terminates this is convenient to not combine following stats bit
281
+ // flags manually
282
+ ALL_STATS = (1 << 3),
283
+ // prints operation stats (GPU times, run count, etc.) before process
284
+ // terminates
285
+ OPERATION_STATS = (1 << 4),
286
+ // prints copies stats (GPU times, copy kinds, sizes, etc.) before process
287
+ // terminates
288
+ COPY_STATS = (1 << 5),
289
+ // prints CPU Fallback stats (CPU times, run times, size of MPS<->CPU copies
290
+ // for tensors, etc.) before process terminates
291
+ CPU_FALLBACK_STATS = (1 << 6),
292
+
293
+ // Metadata format options when logging the info
294
+ // ---------------------------------------------
295
+ // if enabled, includes GPU run time in metadata (i.e.,
296
+ // GPUEndTime-GPUStartTime from Metal Command Buffers) (e.g., [GPU=0.324
297
+ // ms])
298
+ INCLUDE_GPU_TIME = (1 << 7),
299
+ // if enabled, includes GPU scheduling time in metadata separately
300
+ // (i.e., KernelEndTime-KernelStartTime from Metal Command Buffers)
301
+ // e.g., [GPU=0.324 ms, KRNL=0.036 ms]
302
+ INCLUDE_KERNEL_TIME = (1 << 8),
303
+ // if enabled, includes the unique buffer ID in metadata for the storage
304
+ // of a tensor that was allocated on MPSAllocator. This is useful (along
305
+ // with the EV "PYTORCH_DEBUG_MPS_ALLOCATOR") to identify buffers that are
306
+ // involved with various operations.
307
+ INCLUDE_BUFFER_ID = (1 << 9),
308
+
309
+ // used for sanity check (Change this when new option added)
310
+ LOG_COUNT = (INCLUDE_BUFFER_ID << 1) - 1,
311
+ };
312
+
313
+ explicit MPSProfiler();
314
+ ~MPSProfiler();
315
+
316
+ // the handle is either "MPSGraph*" or "id<MTLComputePipelineState>" for Metal
317
+ // Kernels the beginProfile*() functions return a profileId which is unique
318
+ // per graph/kernel/copy
319
+ uint64_t beginProfileKernel(
320
+ const void* handle,
321
+ const std::string& strKey,
322
+ bool isGraph);
323
+ uint64_t beginProfileKernel(
324
+ const void* handle,
325
+ const std::string& kernelName,
326
+ const TensorList& tensors);
327
+ uint64_t beginProfileCopy(
328
+ const void* srcBuffer,
329
+ const void* dstBuffer,
330
+ const OptionalTensorRef srcTensor,
331
+ const OptionalTensorRef dstTensor,
332
+ size_t length,
333
+ bool isNonBlocking,
334
+ bool usesBlitter = true);
335
+ uint64_t beginProfileCPUFallback(
336
+ const std::string& opName,
337
+ const TensorList& tensors);
338
+ void beginProfileGPUInterval(const void* handle);
339
+
340
+ void endProfileCopy(uint64_t profileId, SyncType syncType);
341
+ void endProfileKernel(const void* handle, SyncType syncType = SyncType::NONE);
342
+ void endProfileCPUFallback(const std::string& opName);
343
+
344
+ // these are used to hook into Python bindings for torch.mps.profiler module.
345
+ // this enables generating OS Signpost traces from MPSProfiler on-demand
346
+ // during runtime (instead of environment variables).
347
+ // The "mode" could be either "interval", "event", or both "interval,event"
348
+ // for interval-based and/or event-based signpost tracing.
349
+ void StartTrace(const std::string& mode, bool waitUntilCompleted);
350
+ void StopTrace();
351
+
352
+ // Abstractions for GPU trace capturing
353
+ bool isCaptureEnabled() const;
354
+ bool isCapturing() const;
355
+ void startCapture(const std::string& name, MPSStream* stream = nullptr);
356
+ void stopCapture(MPSStream* stream = nullptr);
357
+
358
+ // convenience functions to indicate whether signpost tracing or
359
+ // logging are enabled for the SignpostTypes
360
+ bool isOperationProfilingEnabled() const {
361
+ return (m_signpost_types & SignpostTypes::RUN_OPERATION) ||
362
+ (m_log_options &
363
+ (LogOptions::OPERATION_INFO | LogOptions::OPERATION_STATS));
364
+ }
365
+ bool isCopyProfilingEnabled() const {
366
+ return (m_signpost_types & SignpostTypes::BLIT_COPY) ||
367
+ (m_log_options & (LogOptions::COPY_INFO | LogOptions::COPY_STATS));
368
+ }
369
+ bool isCPUFallbackProfilingEnabled() const {
370
+ return (m_signpost_types & SignpostTypes::CPU_FALLBACK) ||
371
+ (m_log_options &
372
+ (LogOptions::CPU_FALLBACK_INFO | LogOptions::CPU_FALLBACK_STATS));
373
+ }
374
+ bool isSignpostTracingEnabled() const {
375
+ return (m_signpost_types != SignpostTypes::SIGNPOST_NONE);
376
+ }
377
+
378
+ private:
379
+ // indicates what type of signpost types are enabled and traced by MPS
380
+ // profiler.
381
+ uint32_t m_signpost_types = 0;
382
+ uint32_t m_profile_options = 0;
383
+ uint32_t m_log_options = 0;
384
+ uint64_t m_kernel_counter = 0;
385
+ uint64_t m_graph_counter = 0;
386
+ uint64_t m_cpu_fb_counter = 0;
387
+ uint64_t m_copy_counter = 0;
388
+ // technically, it's possible to trace both events and intervals at the same
389
+ // time so we use separate os_log categories for them
390
+ os_log_t m_os_log_events;
391
+ os_log_t m_os_log_intervals;
392
+ // stats logging could run either from destructor or signal handler
393
+ // so this is used to check if logging has already started.
394
+ std::atomic_bool hasLoggedStats{false};
395
+ // indicates there are pending completionHandler callbacks that haven't been
396
+ // called yet.
397
+ std::atomic_bool hasPendingCompletionHandlers{false};
398
+ // used to capture sigint signal to log profiling stats
399
+ static struct sigaction currentSigint, previousSigint;
400
+
401
+ // We use the following lists for two reasons:
402
+ // 1- for interval-based signposts the "begin" point won't be in same function
403
+ // as the "end" point where we need to be able to retrieve signpost's info
404
+ // 2- if Operations info need to be logged when process ends using
405
+ // LogOptions::OPERATION_INFO.
406
+
407
+ // the pointer key for this map is either "MPSGraph*" or
408
+ // "id<MTLComputePipelineState>" for Metal Kernels this list is retained and
409
+ // could be logged along with aggregate profiling numbers when the process
410
+ // ends.
411
+ std::unordered_map<uintptr_t, std::unique_ptr<OperationInfo>>
412
+ m_op_info_list{};
413
+ // the string key for this map is the op name that we fall back to execute on
414
+ // CPU this list is retained and could be logged along with aggregate
415
+ // profiling numbers when the process ends.
416
+ std::unordered_map<std::string, std::unique_ptr<CpuFbInfo>>
417
+ m_cpu_fb_info_list{};
418
+ // this list contains the info for copies, and its key is the unique profileId
419
+ // which is generated from m_copy_counter
420
+ // The copyInfo list is not retained.
421
+ std::unordered_map<uint64_t, std::unique_ptr<CopyInfo>> m_copy_info_list{};
422
+ // a short list that contains copy stats
423
+ std::unordered_map<CopyInfo::Kind, std::unique_ptr<CopyStat>>
424
+ m_copy_stat_list{};
425
+
426
+ mutable MTLCaptureManager* captureManager = nil;
427
+ unsigned captureCount = 0;
428
+
429
+ void initialize();
430
+ void beginProfileExecution(BaseInfo& info, bool cpuExecution = false);
431
+ void endProfileExecution(
432
+ BaseInfo& info,
433
+ os_signpost_id_t event_signpost_id,
434
+ os_signpost_id_t interval_signpost_id,
435
+ double gpuTime,
436
+ double schedulingTime);
437
+ void addProfilerScheduledHandler(BaseInfo& info);
438
+ void addProfilerCompletedHandler(BaseInfo& info, SyncType syncType);
439
+ void emitSignpostEvent(
440
+ SignpostTypes signpost_type,
441
+ os_signpost_id_t signpost_id,
442
+ const std::string& msg) const;
443
+ void beginSignpostInterval(
444
+ SignpostTypes signpost_type,
445
+ os_signpost_id_t signpost_id,
446
+ const std::string& msg) const;
447
+ void endSignpostInterval(
448
+ SignpostTypes signpost_type,
449
+ os_signpost_id_t signpost_id) const;
450
+
451
+ void updateCopyStats(
452
+ const CopyInfo& copyInfo,
453
+ double gpuTime,
454
+ double schedulingTime);
455
+ // returns true if logging the profiling info "during the execution" is
456
+ // enabled
457
+ bool isProfileInfoLoggingEnabled(
458
+ BaseInfo::Type infoType,
459
+ bool isExecutionEnded);
460
+ // logs all the profiling stats that are enabled
461
+ void logProfilingStats();
462
+ // logs kernel profiling stats when the process ends.
463
+ void logOperationsProfilingStats(std::FILE* f) const;
464
+ // logs CPU Fallback profiling stats when the process ends.
465
+ void logCPUFallbackProfilingStats(std::FILE* f) const;
466
+ // logs copy profiling stats when the process ends.
467
+ void logCopyProfilingStats(std::FILE* f) const;
468
+
469
+ os_signpost_id_t generateSignpostId(
470
+ os_signpost_type_t signpostType,
471
+ const void* ptr = nullptr);
472
+ static SignpostTypes getSignpostType(BaseInfo::Type infoType);
473
+ static void handleIntSignal(int signal);
474
+ };
475
+
476
+ } // namespace Profiler
477
+
478
+ Profiler::MPSProfiler& getMPSProfiler();
479
+
480
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/mps/MPSStream.h ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Copyright © 2022 Apple Inc.
2
+
3
+ #pragma once
4
+
5
+ #include <cstdint>
6
+ #include <utility>
7
+
8
+ #include <ATen/mps/MPSDevice.h>
9
+ #include <c10/core/DeviceGuard.h>
10
+ #include <c10/core/Stream.h>
11
+ #include <c10/util/Exception.h>
12
+
13
+ #ifdef __OBJC__
14
+ #include <Foundation/Foundation.h>
15
+ #include <Metal/Metal.h>
16
+ #include <MetalPerformanceShaders/MetalPerformanceShaders.h>
17
+ #include <MetalPerformanceShadersGraph/MetalPerformanceShadersGraph.h>
18
+ typedef id<MTLCommandQueue> MTLCommandQueue_t;
19
+ typedef id<MTLCommandBuffer> MTLCommandBuffer_t;
20
+ typedef id<MTLComputeCommandEncoder> MTLComputeCommandEncoder_t;
21
+ typedef id<MTLSharedEvent> MTLSharedEvent_t;
22
+ typedef id<MTLDevice> MTLDevice_t;
23
+ #else
24
+ typedef void* MTLCommandQueue_t;
25
+ typedef void* MTLCommandQueue;
26
+ typedef void* MTLCommandBuffer_t;
27
+ typedef void* MTLCommandBuffer;
28
+ typedef void* MTLComputeCommandEncoder_t;
29
+ typedef void* MTLSharedEvent_t;
30
+ typedef void* dispatch_queue_t;
31
+ typedef void* MTLDevice_t;
32
+ #define nil NULL;
33
+ #endif
34
+
35
+ namespace at::mps {
36
+
37
+ //-----------------------------------------------------------------
38
+ // MPSStream
39
+ //-----------------------------------------------------------------
40
+
41
+ enum class SyncType {
42
+ NONE, // no commit to command buffer
43
+ COMMIT, // commit and flush the command buffer
44
+ COMMIT_AND_WAIT, // flush and wait for command buffer execution to finish
45
+ COMMIT_AND_CONTINUE, // commit and continue with a new underlying command buffer
46
+ COMMIT_ADAPTIVE, // commit adaptively based on available memory
47
+ };
48
+
49
+ class TORCH_API MPSStream {
50
+ public:
51
+ enum Unchecked { UNCHECKED };
52
+
53
+ /// Construct a MPSStream from a Stream. This construction is checked,
54
+ /// and will raise an error if the Stream is not, in fact, a MPS stream.
55
+ explicit MPSStream(Stream stream);
56
+
57
+ ~MPSStream();
58
+ MTLCommandQueue_t commandQueue() const {
59
+ return _commandQueue;
60
+ };
61
+ dispatch_queue_t queue() const {
62
+ return _serialQueue;
63
+ }
64
+
65
+ MPSCommandBuffer* commandBuffer();
66
+ MTLComputeCommandEncoder_t commandEncoder();
67
+ void endKernelCoalescing();
68
+ void synchronize(SyncType syncType);
69
+ void fill(id<MTLBuffer> buffer, uint8_t value, size_t length, size_t offset, SyncType syncType = SyncType::NONE);
70
+ void copy(id<MTLBuffer> srcBuffer,
71
+ id<MTLBuffer> dstBuffer,
72
+ size_t length,
73
+ size_t srcOffset,
74
+ size_t dstOffset,
75
+ uint64_t profileId,
76
+ SyncType syncType = SyncType::NONE);
77
+ void copy_and_sync(id<MTLBuffer> srcBuffer,
78
+ id<MTLBuffer> dstBuffer,
79
+ size_t length,
80
+ size_t srcOffset,
81
+ size_t dstOffset,
82
+ bool non_blocking,
83
+ uint64_t profileId);
84
+ void executeMPSGraph(MPSGraph* mpsGraph,
85
+ NSDictionary* feeds,
86
+ NSDictionary* results,
87
+ SyncType syncType = SyncType::NONE);
88
+ void addCompletedHandler(MTLCommandBufferHandler block);
89
+
90
+ /// Get the MPS device index that this stream is associated with.
91
+ c10::DeviceIndex device_index() const {
92
+ return _stream.device_index();
93
+ }
94
+
95
+ MTLCommandQueue_t stream() const {
96
+ return _commandQueue;
97
+ };
98
+
99
+ MTLDevice_t device() const {
100
+ return [_commandQueue device];
101
+ }
102
+
103
+ /// Explicit conversion to Stream.
104
+ Stream unwrap() const {
105
+ return _stream;
106
+ }
107
+
108
+ private:
109
+ Stream _stream;
110
+ MTLCommandQueue_t _commandQueue = nil;
111
+ MPSCommandBuffer* _commandBuffer = nil;
112
+ MPSCommandBuffer* _prevCommandBuffer = nil;
113
+ MTLComputeCommandEncoder_t _commandEncoder = nil;
114
+ MPSGraphExecutionDescriptor* _executionDescriptor = nil;
115
+ MPSGraphCompilationDescriptor* _compilationDescriptor = nil;
116
+ dispatch_queue_t _serialQueue = nullptr;
117
+ // CommitAndContinue is enabled by default
118
+ bool _enableCommitAndContinue = true;
119
+
120
+ // use synchronize() to access any of these commit functions outside MPSStream
121
+ void commit();
122
+ void commitAndWait();
123
+ void commitAndContinue();
124
+ void flush();
125
+ };
126
+
127
+ /**
128
+ * Get the current MPS stream
129
+ */
130
+ TORCH_API MPSStream* getCurrentMPSStream();
131
+
132
+ /**
133
+ * Get the default MPS stream
134
+ */
135
+ TORCH_API MPSStream* getDefaultMPSStream();
136
+
137
+ //-----------------------------------------------------------------
138
+ // MPSStreamImpl
139
+ //-----------------------------------------------------------------
140
+
141
+ class TORCH_API MPSStreamImpl {
142
+ public:
143
+ /**
144
+ * Gets single instance of the MPSStream.
145
+ */
146
+ static MPSStream* getInstance();
147
+
148
+ private:
149
+ static MPSStream* _stream;
150
+ MPSStreamImpl();
151
+ };
152
+
153
+ } // namespace at::mps
lib/python3.12/site-packages/torch/include/ATen/native/Activation.h ADDED
@@ -0,0 +1,73 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+ #include <ATen/native/Gelu.h>
5
+ #include <c10/util/Exception.h>
6
+
7
+ namespace c10 {
8
+ class Scalar;
9
+ }
10
+
11
+ namespace at {
12
+ struct TensorIterator;
13
+ struct TensorIteratorBase;
14
+ class TensorBase;
15
+ }
16
+
17
+ namespace at::native {
18
+
19
+ using structured_activation_fn = void (*)(TensorIteratorBase&);
20
+ using structured_activation_backward_fn = void (*)(TensorIteratorBase&);
21
+
22
+ using activation_fn = void (*)(TensorIterator&);
23
+ using activation_backward_fn = void (*)(TensorIterator&);
24
+ using softplus_fn = void (*)(TensorIteratorBase&, const c10::Scalar&, const c10::Scalar&);
25
+ using softplus_backward_fn = void (*)(TensorIteratorBase&, const c10::Scalar&, const c10::Scalar&);
26
+ using threshold_fn = void (*)(TensorIteratorBase&, const c10::Scalar&, const c10::Scalar&);
27
+ using hardtanh_backward_fn = void (*)(TensorIterator&, const c10::Scalar&, const c10::Scalar&);
28
+ using hardsigmoid_fn = void(*)(TensorIteratorBase&);
29
+ using hardsigmoid_backward_fn = void(*)(TensorIteratorBase&);
30
+ using hardswish_fn = void(*)(TensorIterator&);
31
+ using hardswish_backward_fn = void(*)(TensorIterator&);
32
+ using shrink_fn = void (*)(TensorIteratorBase&, const c10::Scalar&);
33
+ using softshrink_fn = void (*)(TensorIteratorBase&, const c10::Scalar&);
34
+ using shrink_backward_fn = void (*)(TensorIteratorBase&, const c10::Scalar&);
35
+ using elu_fn = void (*)(TensorIteratorBase&, const c10::Scalar&, const c10::Scalar&, const c10::Scalar&);
36
+ using elu_backward_fn = void (*)(TensorIteratorBase&, const c10::Scalar&, const c10::Scalar&, const c10::Scalar&, bool);
37
+ using leaky_relu_fn = void (*)(TensorIteratorBase&, const c10::Scalar&);
38
+ using leaky_relu_backward_fn = void (*)(TensorIteratorBase&, const c10::Scalar&);
39
+ using log_sigmoid_cpu_fn = void (*)(TensorBase&, TensorBase&, const TensorBase&);
40
+ using gelu_fn = void (*)(TensorIteratorBase&, GeluType);
41
+ using gelu_backward_fn = void (*)(TensorIteratorBase&, GeluType);
42
+ using glu_jvp_fn = void (*)(TensorIteratorBase&);
43
+
44
+ DECLARE_DISPATCH(elu_fn, elu_stub)
45
+ DECLARE_DISPATCH(elu_backward_fn, elu_backward_stub)
46
+ DECLARE_DISPATCH(softplus_fn, softplus_stub)
47
+ DECLARE_DISPATCH(softplus_backward_fn, softplus_backward_stub)
48
+ DECLARE_DISPATCH(log_sigmoid_cpu_fn, log_sigmoid_cpu_stub)
49
+ DECLARE_DISPATCH(activation_backward_fn, log_sigmoid_backward_stub)
50
+ DECLARE_DISPATCH(threshold_fn, threshold_stub)
51
+ DECLARE_DISPATCH(gelu_fn, GeluKernel)
52
+ DECLARE_DISPATCH(gelu_backward_fn, GeluBackwardKernel)
53
+ DECLARE_DISPATCH(hardtanh_backward_fn, hardtanh_backward_stub)
54
+ DECLARE_DISPATCH(hardsigmoid_fn, hardsigmoid_stub)
55
+ DECLARE_DISPATCH(hardsigmoid_backward_fn, hardsigmoid_backward_stub)
56
+ DECLARE_DISPATCH(hardswish_fn, hardswish_stub)
57
+ DECLARE_DISPATCH(hardswish_backward_fn, hardswish_backward_stub)
58
+ DECLARE_DISPATCH(shrink_fn, hardshrink_stub)
59
+ DECLARE_DISPATCH(softshrink_fn, softshrink_stub)
60
+ DECLARE_DISPATCH(shrink_backward_fn, shrink_backward_stub)
61
+ DECLARE_DISPATCH(leaky_relu_fn, leaky_relu_stub)
62
+ DECLARE_DISPATCH(leaky_relu_backward_fn, leaky_relu_backward_stub)
63
+ DECLARE_DISPATCH(structured_activation_fn, glu_stub)
64
+ DECLARE_DISPATCH(activation_backward_fn, glu_backward_stub)
65
+ DECLARE_DISPATCH(glu_jvp_fn, glu_jvp_stub)
66
+ DECLARE_DISPATCH(structured_activation_fn, silu_stub)
67
+ DECLARE_DISPATCH(structured_activation_backward_fn, silu_backward_stub)
68
+ DECLARE_DISPATCH(structured_activation_fn, mish_stub)
69
+ DECLARE_DISPATCH(activation_backward_fn, mish_backward_stub)
70
+ DECLARE_DISPATCH(activation_fn, prelu_stub)
71
+ DECLARE_DISPATCH(activation_backward_fn, prelu_backward_stub)
72
+
73
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/AdaptivePooling.h ADDED
@@ -0,0 +1,49 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/core/Tensor.h>
4
+ #include <ATen/native/DispatchStub.h>
5
+ #include <c10/util/ArrayRef.h>
6
+ #include <c10/util/irange.h>
7
+ #include <cmath>
8
+
9
+ namespace at::native {
10
+
11
+ using adaptive_avg_pooling2d_fn = void(*)(Tensor& output, const Tensor& input, IntArrayRef output_size);
12
+ using adaptive_avg_pooling2d_backward_fn = void(*)(Tensor& grad_input, const Tensor& grad_output);
13
+ DECLARE_DISPATCH(adaptive_avg_pooling2d_fn, adaptive_avg_pool2d_kernel)
14
+ DECLARE_DISPATCH(adaptive_avg_pooling2d_backward_fn, adaptive_avg_pool2d_backward_kernel)
15
+
16
+ using adaptive_max_pooling2d_fn = void(*)(const Tensor& output, const Tensor& indices, const Tensor& input, IntArrayRef output_size);
17
+ using adaptive_max_pooling2d_backward_fn = void(*)(const Tensor& grad_input, const Tensor& grad_output, const Tensor& indices);
18
+ DECLARE_DISPATCH(adaptive_max_pooling2d_fn, adaptive_max_pool2d_kernel)
19
+ DECLARE_DISPATCH(adaptive_max_pooling2d_backward_fn, adaptive_max_pool2d_backward_kernel)
20
+
21
+ using adaptive_avg_pooling3d_fn = void(*)(Tensor& output, const Tensor& input, IntArrayRef output_size);
22
+ using adaptive_avg_pooling3d_backward_fn = void(*)(Tensor& grad_input, const Tensor& grad_output);
23
+ DECLARE_DISPATCH(adaptive_avg_pooling3d_fn, adaptive_avg_pool3d_kernel)
24
+ DECLARE_DISPATCH(adaptive_avg_pooling3d_backward_fn, adaptive_avg_pool3d_backward_kernel)
25
+
26
+ using adaptive_max_pooling3d_fn = void(*)(const Tensor& output, const Tensor& indices, const Tensor& input, IntArrayRef output_size);
27
+ using adaptive_max_pooling3d_backward_fn = void(*)(const Tensor& grad_input, const Tensor& grad_output, const Tensor& indices);
28
+ DECLARE_DISPATCH(adaptive_max_pooling3d_fn, adaptive_max_pool3d_kernel)
29
+ DECLARE_DISPATCH(adaptive_max_pooling3d_backward_fn, adaptive_max_pool3d_backward_kernel)
30
+
31
+ inline int64_t start_index(int64_t a, int64_t b, int64_t c) {
32
+ return (a / b) * c + ((a % b) * c) / b;
33
+ }
34
+
35
+ inline int64_t end_index(int64_t a, int64_t b, int64_t c) {
36
+ return 1 + ((a + 1) * c - 1) / b;
37
+ }
38
+
39
+ inline void adaptive_pool_empty_output_check(const Tensor& gradOutput_, const char* arg_name) {
40
+ int64_t ndim = gradOutput_.ndimension();
41
+ for (const auto i : c10::irange(1, ndim)) {
42
+ TORCH_CHECK(gradOutput_.size(i) > 0,
43
+ arg_name, "(): Expected grad_output to have non-zero size for non-batch dimensions, "
44
+ "but grad_output has sizes ", gradOutput_.sizes(), " with dimension ", i,
45
+ " being empty");
46
+ }
47
+ }
48
+
49
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/BatchLinearAlgebra.h ADDED
@@ -0,0 +1,321 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <optional>
4
+ #include <c10/util/string_view.h>
5
+ #include <ATen/Config.h>
6
+ #include <ATen/native/DispatchStub.h>
7
+
8
+ // Forward declare TI
9
+ namespace at {
10
+ class Tensor;
11
+ struct TensorIterator;
12
+
13
+ namespace native {
14
+ enum class TransposeType;
15
+ }
16
+
17
+ }
18
+
19
+ namespace at::native {
20
+
21
+ enum class LapackLstsqDriverType : int64_t { Gels, Gelsd, Gelsy, Gelss};
22
+
23
+ #if AT_BUILD_WITH_LAPACK()
24
+ // Define per-batch functions to be used in the implementation of batched
25
+ // linear algebra operations
26
+
27
+ template <class scalar_t>
28
+ void lapackCholesky(char uplo, int n, scalar_t *a, int lda, int *info);
29
+
30
+ template <class scalar_t>
31
+ void lapackCholeskyInverse(char uplo, int n, scalar_t *a, int lda, int *info);
32
+
33
+ template <class scalar_t, class value_t=scalar_t>
34
+ void lapackEig(char jobvl, char jobvr, int n, scalar_t *a, int lda, scalar_t *w, scalar_t* vl, int ldvl, scalar_t *vr, int ldvr, scalar_t *work, int lwork, value_t *rwork, int *info);
35
+
36
+ template <class scalar_t>
37
+ void lapackGeqrf(int m, int n, scalar_t *a, int lda, scalar_t *tau, scalar_t *work, int lwork, int *info);
38
+
39
+ template <class scalar_t>
40
+ void lapackOrgqr(int m, int n, int k, scalar_t *a, int lda, scalar_t *tau, scalar_t *work, int lwork, int *info);
41
+
42
+ template <class scalar_t>
43
+ void lapackOrmqr(char side, char trans, int m, int n, int k, scalar_t *a, int lda, scalar_t *tau, scalar_t *c, int ldc, scalar_t *work, int lwork, int *info);
44
+
45
+ template <class scalar_t, class value_t = scalar_t>
46
+ void lapackSyevd(char jobz, char uplo, int n, scalar_t* a, int lda, value_t* w, scalar_t* work, int lwork, value_t* rwork, int lrwork, int* iwork, int liwork, int* info);
47
+
48
+ template <class scalar_t>
49
+ void lapackGels(char trans, int m, int n, int nrhs,
50
+ scalar_t *a, int lda, scalar_t *b, int ldb,
51
+ scalar_t *work, int lwork, int *info);
52
+
53
+ template <class scalar_t, class value_t = scalar_t>
54
+ void lapackGelsd(int m, int n, int nrhs,
55
+ scalar_t *a, int lda, scalar_t *b, int ldb,
56
+ value_t *s, value_t rcond, int *rank,
57
+ scalar_t* work, int lwork,
58
+ value_t *rwork, int* iwork, int *info);
59
+
60
+ template <class scalar_t, class value_t = scalar_t>
61
+ void lapackGelsy(int m, int n, int nrhs,
62
+ scalar_t *a, int lda, scalar_t *b, int ldb,
63
+ int *jpvt, value_t rcond, int *rank,
64
+ scalar_t *work, int lwork, value_t* rwork, int *info);
65
+
66
+ template <class scalar_t, class value_t = scalar_t>
67
+ void lapackGelss(int m, int n, int nrhs,
68
+ scalar_t *a, int lda, scalar_t *b, int ldb,
69
+ value_t *s, value_t rcond, int *rank,
70
+ scalar_t *work, int lwork,
71
+ value_t *rwork, int *info);
72
+
73
+ template <LapackLstsqDriverType, class scalar_t, class value_t = scalar_t>
74
+ struct lapackLstsq_impl;
75
+
76
+ template <class scalar_t, class value_t>
77
+ struct lapackLstsq_impl<LapackLstsqDriverType::Gels, scalar_t, value_t> {
78
+ static void call(
79
+ char trans, int m, int n, int nrhs,
80
+ scalar_t *a, int lda, scalar_t *b, int ldb,
81
+ scalar_t *work, int lwork, int *info, // Gels flavor
82
+ int *jpvt, value_t rcond, int *rank, value_t* rwork, // Gelsy flavor
83
+ value_t *s, // Gelss flavor
84
+ int *iwork // Gelsd flavor
85
+ ) {
86
+ lapackGels<scalar_t>(
87
+ trans, m, n, nrhs,
88
+ a, lda, b, ldb,
89
+ work, lwork, info);
90
+ }
91
+ };
92
+
93
+ template <class scalar_t, class value_t>
94
+ struct lapackLstsq_impl<LapackLstsqDriverType::Gelsy, scalar_t, value_t> {
95
+ static void call(
96
+ char trans, int m, int n, int nrhs,
97
+ scalar_t *a, int lda, scalar_t *b, int ldb,
98
+ scalar_t *work, int lwork, int *info, // Gels flavor
99
+ int *jpvt, value_t rcond, int *rank, value_t* rwork, // Gelsy flavor
100
+ value_t *s, // Gelss flavor
101
+ int *iwork // Gelsd flavor
102
+ ) {
103
+ lapackGelsy<scalar_t, value_t>(
104
+ m, n, nrhs,
105
+ a, lda, b, ldb,
106
+ jpvt, rcond, rank,
107
+ work, lwork, rwork, info);
108
+ }
109
+ };
110
+
111
+ template <class scalar_t, class value_t>
112
+ struct lapackLstsq_impl<LapackLstsqDriverType::Gelsd, scalar_t, value_t> {
113
+ static void call(
114
+ char trans, int m, int n, int nrhs,
115
+ scalar_t *a, int lda, scalar_t *b, int ldb,
116
+ scalar_t *work, int lwork, int *info, // Gels flavor
117
+ int *jpvt, value_t rcond, int *rank, value_t* rwork, // Gelsy flavor
118
+ value_t *s, // Gelss flavor
119
+ int *iwork // Gelsd flavor
120
+ ) {
121
+ lapackGelsd<scalar_t, value_t>(
122
+ m, n, nrhs,
123
+ a, lda, b, ldb,
124
+ s, rcond, rank,
125
+ work, lwork,
126
+ rwork, iwork, info);
127
+ }
128
+ };
129
+
130
+ template <class scalar_t, class value_t>
131
+ struct lapackLstsq_impl<LapackLstsqDriverType::Gelss, scalar_t, value_t> {
132
+ static void call(
133
+ char trans, int m, int n, int nrhs,
134
+ scalar_t *a, int lda, scalar_t *b, int ldb,
135
+ scalar_t *work, int lwork, int *info, // Gels flavor
136
+ int *jpvt, value_t rcond, int *rank, value_t* rwork, // Gelsy flavor
137
+ value_t *s, // Gelss flavor
138
+ int *iwork // Gelsd flavor
139
+ ) {
140
+ lapackGelss<scalar_t, value_t>(
141
+ m, n, nrhs,
142
+ a, lda, b, ldb,
143
+ s, rcond, rank,
144
+ work, lwork,
145
+ rwork, info);
146
+ }
147
+ };
148
+
149
+ template <LapackLstsqDriverType driver_type, class scalar_t, class value_t = scalar_t>
150
+ void lapackLstsq(
151
+ char trans, int m, int n, int nrhs,
152
+ scalar_t *a, int lda, scalar_t *b, int ldb,
153
+ scalar_t *work, int lwork, int *info, // Gels flavor
154
+ int *jpvt, value_t rcond, int *rank, value_t* rwork, // Gelsy flavor
155
+ value_t *s, // Gelss flavor
156
+ int *iwork // Gelsd flavor
157
+ ) {
158
+ lapackLstsq_impl<driver_type, scalar_t, value_t>::call(
159
+ trans, m, n, nrhs,
160
+ a, lda, b, ldb,
161
+ work, lwork, info,
162
+ jpvt, rcond, rank, rwork,
163
+ s,
164
+ iwork);
165
+ }
166
+
167
+ template <class scalar_t>
168
+ void lapackLuSolve(char trans, int n, int nrhs, scalar_t *a, int lda, int *ipiv, scalar_t *b, int ldb, int *info);
169
+
170
+ template <class scalar_t>
171
+ void lapackLu(int m, int n, scalar_t *a, int lda, int *ipiv, int *info);
172
+
173
+ template <class scalar_t>
174
+ void lapackLdlHermitian(
175
+ char uplo,
176
+ int n,
177
+ scalar_t* a,
178
+ int lda,
179
+ int* ipiv,
180
+ scalar_t* work,
181
+ int lwork,
182
+ int* info);
183
+
184
+ template <class scalar_t>
185
+ void lapackLdlSymmetric(
186
+ char uplo,
187
+ int n,
188
+ scalar_t* a,
189
+ int lda,
190
+ int* ipiv,
191
+ scalar_t* work,
192
+ int lwork,
193
+ int* info);
194
+
195
+ template <class scalar_t>
196
+ void lapackLdlSolveHermitian(
197
+ char uplo,
198
+ int n,
199
+ int nrhs,
200
+ scalar_t* a,
201
+ int lda,
202
+ int* ipiv,
203
+ scalar_t* b,
204
+ int ldb,
205
+ int* info);
206
+
207
+ template <class scalar_t>
208
+ void lapackLdlSolveSymmetric(
209
+ char uplo,
210
+ int n,
211
+ int nrhs,
212
+ scalar_t* a,
213
+ int lda,
214
+ int* ipiv,
215
+ scalar_t* b,
216
+ int ldb,
217
+ int* info);
218
+
219
+ template<class scalar_t, class value_t=scalar_t>
220
+ void lapackSvd(char jobz, int m, int n, scalar_t *a, int lda, value_t *s, scalar_t *u, int ldu, scalar_t *vt, int ldvt, scalar_t *work, int lwork, value_t *rwork, int *iwork, int *info);
221
+ #endif
222
+
223
+ #if AT_BUILD_WITH_BLAS()
224
+ template <class scalar_t>
225
+ void blasTriangularSolve(char side, char uplo, char trans, char diag, int n, int nrhs, scalar_t* a, int lda, scalar_t* b, int ldb);
226
+ #endif
227
+
228
+ using cholesky_fn = void (*)(const Tensor& /*input*/, const Tensor& /*info*/, bool /*upper*/);
229
+ DECLARE_DISPATCH(cholesky_fn, cholesky_stub)
230
+
231
+ using cholesky_inverse_fn = Tensor& (*)(Tensor& /*result*/, Tensor& /*infos*/, bool /*upper*/);
232
+
233
+ DECLARE_DISPATCH(cholesky_inverse_fn, cholesky_inverse_stub)
234
+
235
+ using linalg_eig_fn = void (*)(Tensor& /*eigenvalues*/, Tensor& /*eigenvectors*/, Tensor& /*infos*/, const Tensor& /*input*/, bool /*compute_eigenvectors*/);
236
+
237
+ DECLARE_DISPATCH(linalg_eig_fn, linalg_eig_stub)
238
+
239
+ using geqrf_fn = void (*)(const Tensor& /*input*/, const Tensor& /*tau*/);
240
+ DECLARE_DISPATCH(geqrf_fn, geqrf_stub)
241
+
242
+ using orgqr_fn = Tensor& (*)(Tensor& /*result*/, const Tensor& /*tau*/);
243
+ DECLARE_DISPATCH(orgqr_fn, orgqr_stub)
244
+
245
+ using ormqr_fn = void (*)(const Tensor& /*input*/, const Tensor& /*tau*/, const Tensor& /*other*/, bool /*left*/, bool /*transpose*/);
246
+ DECLARE_DISPATCH(ormqr_fn, ormqr_stub)
247
+
248
+ using linalg_eigh_fn = void (*)(
249
+ const Tensor& /*eigenvalues*/,
250
+ const Tensor& /*eigenvectors*/,
251
+ const Tensor& /*infos*/,
252
+ bool /*upper*/,
253
+ bool /*compute_eigenvectors*/);
254
+ DECLARE_DISPATCH(linalg_eigh_fn, linalg_eigh_stub)
255
+
256
+ using lstsq_fn = void (*)(
257
+ const Tensor& /*a*/,
258
+ Tensor& /*b*/,
259
+ Tensor& /*rank*/,
260
+ Tensor& /*singular_values*/,
261
+ Tensor& /*infos*/,
262
+ double /*rcond*/,
263
+ std::string /*driver_name*/);
264
+ DECLARE_DISPATCH(lstsq_fn, lstsq_stub)
265
+
266
+ using triangular_solve_fn = void (*)(
267
+ const Tensor& /*A*/,
268
+ const Tensor& /*B*/,
269
+ bool /*left*/,
270
+ bool /*upper*/,
271
+ TransposeType /*transpose*/,
272
+ bool /*unitriangular*/);
273
+ DECLARE_DISPATCH(triangular_solve_fn, triangular_solve_stub)
274
+
275
+ using lu_factor_fn = void (*)(
276
+ const Tensor& /*input*/,
277
+ const Tensor& /*pivots*/,
278
+ const Tensor& /*infos*/,
279
+ bool /*compute_pivots*/);
280
+ DECLARE_DISPATCH(lu_factor_fn, lu_factor_stub)
281
+
282
+ using unpack_pivots_fn = void(*)(
283
+ TensorIterator& iter,
284
+ const int64_t dim_size,
285
+ const int64_t max_pivot);
286
+ DECLARE_DISPATCH(unpack_pivots_fn, unpack_pivots_stub)
287
+
288
+ using lu_solve_fn = void (*)(
289
+ const Tensor& /*LU*/,
290
+ const Tensor& /*pivots*/,
291
+ const Tensor& /*B*/,
292
+ TransposeType /*trans*/);
293
+ DECLARE_DISPATCH(lu_solve_fn, lu_solve_stub)
294
+
295
+ using ldl_factor_fn = void (*)(
296
+ const Tensor& /*LD*/,
297
+ const Tensor& /*pivots*/,
298
+ const Tensor& /*info*/,
299
+ bool /*upper*/,
300
+ bool /*hermitian*/);
301
+ DECLARE_DISPATCH(ldl_factor_fn, ldl_factor_stub)
302
+
303
+ using svd_fn = void (*)(
304
+ const Tensor& /*A*/,
305
+ const bool /*full_matrices*/,
306
+ const bool /*compute_uv*/,
307
+ const std::optional<std::string_view>& /*driver*/,
308
+ const Tensor& /*U*/,
309
+ const Tensor& /*S*/,
310
+ const Tensor& /*Vh*/,
311
+ const Tensor& /*info*/);
312
+ DECLARE_DISPATCH(svd_fn, svd_stub)
313
+
314
+ using ldl_solve_fn = void (*)(
315
+ const Tensor& /*LD*/,
316
+ const Tensor& /*pivots*/,
317
+ const Tensor& /*result*/,
318
+ bool /*upper*/,
319
+ bool /*hermitian*/);
320
+ DECLARE_DISPATCH(ldl_solve_fn, ldl_solve_stub)
321
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/BinaryOps.h ADDED
@@ -0,0 +1,119 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/core/TensorBase.h>
4
+ #include <ATen/native/DispatchStub.h>
5
+ #include <c10/core/Scalar.h>
6
+ #include <c10/util/TypeSafeSignMath.h>
7
+
8
+
9
+ namespace at {
10
+ struct TensorIterator;
11
+ struct TensorIteratorBase;
12
+ }
13
+
14
+ namespace at::native {
15
+
16
+ inline void alpha_check(const ScalarType dtype, const Scalar& alpha) {
17
+ TORCH_CHECK(! alpha.isBoolean() || dtype == ScalarType::Bool,
18
+ "Boolean alpha only supported for Boolean results.");
19
+ TORCH_CHECK(isFloatingType(dtype) || isComplexType(dtype)
20
+ || alpha.isIntegral(true),
21
+ "For integral input tensors, argument alpha must not be a floating point number.");
22
+ TORCH_CHECK(isComplexType(dtype) || !alpha.isComplex(),
23
+ "For non-complex input tensors, argument alpha must not be a complex number.")
24
+ }
25
+
26
+ // Basic checking for all sub functions.
27
+ inline void sub_check(const TensorBase& self, const TensorBase& other) {
28
+ TORCH_CHECK(self.scalar_type() != kBool || other.scalar_type() != kBool,
29
+ "Subtraction, the `-` operator, with two bool tensors is not supported. "
30
+ "Use the `^` or `logical_xor()` operator instead.")
31
+ TORCH_CHECK(self.scalar_type() != kBool && other.scalar_type() != kBool,
32
+ "Subtraction, the `-` operator, with a bool tensor is not supported. "
33
+ "If you are trying to invert a mask, use the `~` or `logical_not()` operator instead.");
34
+ }
35
+
36
+ inline void sub_check(const TensorBase& self, const Scalar& scalar) {
37
+ TORCH_CHECK(self.scalar_type() != kBool || !scalar.isBoolean(),
38
+ "Subtraction, the `-` operator, with two bool tensors is not supported. "
39
+ "Use the `^` or `logical_xor()` operator instead.")
40
+ TORCH_CHECK(self.scalar_type() != kBool && !scalar.isBoolean(),
41
+ "Subtraction, the `-` operator, with a bool tensor is not supported. "
42
+ "If you are trying to invert a mask, use the `~` or `logical_not()` operator instead.");
43
+ }
44
+
45
+ using structured_binary_fn_alpha = void(*)(TensorIteratorBase&, const Scalar& alpha);
46
+ using structured_binary_fn_double = void(*)(TensorIteratorBase&, double);
47
+ using structured_binary_fn = void(*)(TensorIteratorBase&);
48
+
49
+ using binary_fn_alpha = void(*)(TensorIteratorBase&, const Scalar& alpha);
50
+ using binary_fn_double = void(*)(TensorIterator&, double);
51
+ using binary_fn = void(*)(TensorIterator&);
52
+ using binary_clamp_fn_alpha =
53
+ void(*)(TensorIterator&, const Scalar& alpha, const Scalar& min_val, const Scalar& max_val);
54
+
55
+ // NB: codegenned
56
+ DECLARE_DISPATCH(structured_binary_fn_alpha, add_stub)
57
+
58
+ DECLARE_DISPATCH(binary_clamp_fn_alpha, add_clamp_stub)
59
+ DECLARE_DISPATCH(structured_binary_fn_alpha, sub_stub)
60
+ DECLARE_DISPATCH(structured_binary_fn, mul_stub)
61
+ DECLARE_DISPATCH(structured_binary_fn, div_true_stub)
62
+ DECLARE_DISPATCH(structured_binary_fn, div_floor_stub)
63
+ DECLARE_DISPATCH(structured_binary_fn, div_trunc_stub)
64
+ DECLARE_DISPATCH(structured_binary_fn, atan2_stub)
65
+ DECLARE_DISPATCH(structured_binary_fn, remainder_stub)
66
+ DECLARE_DISPATCH(structured_binary_fn, bitwise_and_stub)
67
+ DECLARE_DISPATCH(structured_binary_fn, bitwise_or_stub)
68
+ DECLARE_DISPATCH(structured_binary_fn, bitwise_xor_stub)
69
+ DECLARE_DISPATCH(structured_binary_fn, lshift_stub)
70
+ DECLARE_DISPATCH(structured_binary_fn, rshift_stub)
71
+ DECLARE_DISPATCH(binary_fn, logical_xor_stub)
72
+ DECLARE_DISPATCH(binary_fn, logical_and_stub)
73
+ DECLARE_DISPATCH(binary_fn, logical_or_stub)
74
+ DECLARE_DISPATCH(structured_binary_fn, lt_stub)
75
+ DECLARE_DISPATCH(structured_binary_fn, le_stub)
76
+ DECLARE_DISPATCH(structured_binary_fn, gt_stub)
77
+ DECLARE_DISPATCH(structured_binary_fn, ge_stub)
78
+ DECLARE_DISPATCH(structured_binary_fn, eq_stub)
79
+ DECLARE_DISPATCH(structured_binary_fn, ne_stub)
80
+ DECLARE_DISPATCH(binary_fn, max_elementwise_stub)
81
+ DECLARE_DISPATCH(binary_fn, min_elementwise_stub)
82
+ DECLARE_DISPATCH(structured_binary_fn, maximum_stub)
83
+ DECLARE_DISPATCH(structured_binary_fn, minimum_stub)
84
+ DECLARE_DISPATCH(structured_binary_fn, fmax_stub)
85
+ DECLARE_DISPATCH(structured_binary_fn, fmin_stub)
86
+ DECLARE_DISPATCH(structured_binary_fn_double, smooth_l1_stub)
87
+ DECLARE_DISPATCH(binary_fn_double, huber_stub)
88
+ DECLARE_DISPATCH(structured_binary_fn, sigmoid_backward_stub)
89
+ DECLARE_DISPATCH(binary_fn_alpha, logit_backward_stub)
90
+ DECLARE_DISPATCH(structured_binary_fn, tanh_backward_stub)
91
+ DECLARE_DISPATCH(structured_binary_fn, mse_stub)
92
+ DECLARE_DISPATCH(structured_binary_fn, fmod_stub)
93
+ DECLARE_DISPATCH(structured_binary_fn, logaddexp_stub)
94
+ DECLARE_DISPATCH(structured_binary_fn, logaddexp2_stub)
95
+ DECLARE_DISPATCH(structured_binary_fn, gcd_stub)
96
+ DECLARE_DISPATCH(structured_binary_fn, lcm_stub)
97
+ DECLARE_DISPATCH(structured_binary_fn, hypot_stub)
98
+ DECLARE_DISPATCH(structured_binary_fn, igamma_stub)
99
+ DECLARE_DISPATCH(structured_binary_fn, igammac_stub)
100
+ DECLARE_DISPATCH(structured_binary_fn, nextafter_stub)
101
+ DECLARE_DISPATCH(structured_binary_fn, heaviside_stub)
102
+ DECLARE_DISPATCH(structured_binary_fn, copysign_stub)
103
+ DECLARE_DISPATCH(structured_binary_fn, xlogy_stub)
104
+ DECLARE_DISPATCH(structured_binary_fn, xlog1py_stub)
105
+ DECLARE_DISPATCH(structured_binary_fn, zeta_stub)
106
+ DECLARE_DISPATCH(structured_binary_fn, chebyshev_polynomial_t_stub)
107
+ DECLARE_DISPATCH(structured_binary_fn, chebyshev_polynomial_u_stub)
108
+ DECLARE_DISPATCH(structured_binary_fn, chebyshev_polynomial_v_stub)
109
+ DECLARE_DISPATCH(structured_binary_fn, chebyshev_polynomial_w_stub)
110
+ DECLARE_DISPATCH(structured_binary_fn, hermite_polynomial_h_stub)
111
+ DECLARE_DISPATCH(structured_binary_fn, hermite_polynomial_he_stub)
112
+ DECLARE_DISPATCH(structured_binary_fn, laguerre_polynomial_l_stub)
113
+ DECLARE_DISPATCH(structured_binary_fn, legendre_polynomial_p_stub)
114
+ DECLARE_DISPATCH(structured_binary_fn, shifted_chebyshev_polynomial_t_stub)
115
+ DECLARE_DISPATCH(structured_binary_fn, shifted_chebyshev_polynomial_u_stub)
116
+ DECLARE_DISPATCH(structured_binary_fn, shifted_chebyshev_polynomial_v_stub)
117
+ DECLARE_DISPATCH(structured_binary_fn, shifted_chebyshev_polynomial_w_stub)
118
+
119
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/BucketizationUtils.h ADDED
@@ -0,0 +1,173 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/core/Tensor.h>
4
+ #include <ATen/native/TypeProperties.h>
5
+ #include <ATen/ScalarOps.h>
6
+
7
+ #ifndef AT_PER_OPERATOR_HEADERS
8
+ #include <ATen/NativeFunctions.h>
9
+ #else
10
+ #include <ATen/ops/result_type.h>
11
+ #endif
12
+
13
+ namespace at::native {
14
+
15
+ // original values given by raw_*. If an original value is not contiguous, will make a contiguous copy to
16
+ // the corresponding trimmed_* value. Additionally, if the dtypes of the boundary and input tensor do not
17
+ // match, will change them to be a common super type so comparisons are done between the same types.
18
+ // For any trimmed_* tensor, if its outgoing value matches what it was incoming (typically null), then the
19
+ // corresponding raw_* version should be used since it was already contiguous of the right type.
20
+ inline void searchsorted_maybe_trim_input_tensors(
21
+ Tensor& trimmed_input,
22
+ Tensor& trimmed_boundaries,
23
+ Tensor& trimmed_sorter,
24
+ const Tensor& raw_input,
25
+ const Tensor& raw_boundaries,
26
+ const Tensor& raw_sorter) {
27
+ bool in_is_contiguous = raw_input.is_contiguous();
28
+ bool bd_is_contiguous = raw_boundaries.is_contiguous();
29
+ bool sort_is_contiguous = raw_sorter.is_contiguous();
30
+
31
+ if (!in_is_contiguous) {
32
+ TORCH_WARN_ONCE("torch.searchsorted(): input value tensor is non-contiguous, this will lower the performance due "
33
+ "to extra data copy when converting non-contiguous tensor to contiguous, please use contiguous input value "
34
+ "tensor if possible. This message will only appear once per program.");
35
+ trimmed_input = raw_input.contiguous();
36
+ }
37
+ if (!bd_is_contiguous) {
38
+ TORCH_WARN_ONCE("torch.searchsorted(): boundary tensor is non-contiguous, this will lower the performance due "
39
+ "to extra data copy when converting non-contiguous tensor to contiguous, please use contiguous boundary "
40
+ "tensor if possible. This message will only appear once per program.");
41
+ trimmed_boundaries = raw_boundaries.contiguous();
42
+ }
43
+ if (!sort_is_contiguous) {
44
+ TORCH_WARN_ONCE("torch.searchsorted(): sorter tensor is non-contiguous, this will lower the performance due "
45
+ "to extra data copy when converting non-contiguous tensor to contiguous, please use contiguous sorter "
46
+ "tensor if possible. This message will only appear once per program.");
47
+ trimmed_sorter = raw_sorter.contiguous();
48
+ }
49
+ if (raw_input.dtype() != raw_boundaries.dtype()) {
50
+ at::native::ResultTypeState state = {};
51
+ state = at::native::update_result_type_state(raw_boundaries, state);
52
+ state = at::native::update_result_type_state(raw_input, state);
53
+ ScalarType common_stype = at::native::result_type(state);
54
+
55
+ TORCH_INTERNAL_ASSERT(common_stype != ScalarType::Undefined);
56
+ if (common_stype != raw_input.scalar_type()) {
57
+ trimmed_input = in_is_contiguous ? raw_input.to(common_stype) : trimmed_input.to(common_stype);
58
+ }
59
+ if (common_stype != raw_boundaries.scalar_type()) {
60
+ trimmed_boundaries = bd_is_contiguous ? raw_boundaries.to(common_stype) : trimmed_boundaries.to(common_stype);
61
+ }
62
+ }
63
+ }
64
+
65
+ /* unused but needed for internal jagged tensor class */
66
+ inline void searchsorted_maybe_trim_input_tensors(
67
+ Tensor& trimmed_input,
68
+ Tensor& trimmed_boundaries,
69
+ const Tensor& raw_input,
70
+ const Tensor& raw_boundaries) {
71
+ Tensor trimmed_sorter;
72
+ Tensor raw_sorter;
73
+ return searchsorted_maybe_trim_input_tensors(
74
+ trimmed_input,
75
+ trimmed_boundaries,
76
+ trimmed_sorter,
77
+ raw_input,
78
+ raw_boundaries,
79
+ raw_sorter);
80
+ }
81
+
82
+ inline bool searchsorted_dims_matched_before_last_dim(const Tensor& boundaries, const Tensor& input) {
83
+ if (boundaries.dim() != input.dim()) {
84
+ return false;
85
+ }
86
+ const auto& dims_bd = boundaries.sizes();
87
+ const auto& dims_in = input.sizes();
88
+ for (int64_t dim = 0; dim + 1 < boundaries.dim(); ++dim) {
89
+ if (dims_bd[dim] != dims_in[dim]) {
90
+ return false;
91
+ }
92
+ }
93
+ return true;
94
+ }
95
+
96
+ inline Tensor searchsorted_scalar_tensor(const Scalar& scalar, const c10::Device& device) {
97
+ auto tensor = c10::scalar_to_tensor(scalar, device);
98
+ // This is to adopt the scalar promotion rules defined in native/TypeProperties.h
99
+ // So we have the same type promotion rules as binary operations.
100
+ tensor.unsafeGetTensorImpl()->set_wrapped_number(true);
101
+ return tensor;
102
+ }
103
+
104
+ inline void searchsorted_pre_check(
105
+ const Tensor& boundaries,
106
+ const Tensor& input,
107
+ const Tensor& output,
108
+ const bool out_int32,
109
+ const bool right,
110
+ const std::optional<std::string_view> side_opt,
111
+ const Tensor& sorter) {
112
+ if (side_opt) {
113
+ const std::string_view side = *side_opt;
114
+ TORCH_CHECK(side == "left" || side == "right", "torch.searchsorted(): side can only be 'left' or 'right' but ",
115
+ "got ", side);
116
+
117
+ // assume the user has not explicitly set (right=False, side="right")
118
+ TORCH_CHECK(!right || side == "right", "torch.searchsorted(): side and right can't be set to opposites, got side "
119
+ "of ", side, " while right was True");
120
+ }
121
+
122
+ TORCH_CHECK(boundaries.device() == input.device(), "torch.searchsorted(): boundaries and input value tensors ",
123
+ "should have same device type, but got boundaries tensor device type ", boundaries.device(), " and input value ",
124
+ "tensor device type ", input.device());
125
+
126
+ if (sorter.defined()) {
127
+ TORCH_CHECK(sorter.device() == boundaries.device(), "torch.searchsorted(): sorter and boundary tensors should ",
128
+ "have same device type, but got sorter tensor device type ", sorter.device(), " and input value tensor ",
129
+ "device type ", boundaries.device());
130
+
131
+ TORCH_CHECK(sorter.sizes() == boundaries.sizes(), "torch.searchsorted(): boundary and sorter must have the same "
132
+ "size, but got boundary tensor ", boundaries.sizes(), "and got sorter tensor ", sorter.sizes());
133
+
134
+ TORCH_CHECK(sorter.scalar_type() == ScalarType::Long, "torch.searchsorted(): sorter must be a tensor of long ",
135
+ "dtype but got dtype ", sorter.scalar_type());
136
+
137
+ if (sorter.numel() > 0) {
138
+ auto minmax = sorter.aminmax();
139
+ int64_t vmin = std::get<0>(minmax).item().toLong();
140
+ int64_t vmax = std::get<1>(minmax).item().toLong();
141
+ TORCH_CHECK(vmin >= 0 && vmax < sorter.sizes().back(), "torch.searchsorted(): sorter index out of range");
142
+ }
143
+ }
144
+
145
+ TORCH_CHECK(input.dim() > 0 || (input.dim() == 0 && input.numel() == 1 && boundaries.dim() == 1),
146
+ "torch.searchsorted(): input value can be a scalar only when boundaries tensor dimension is 1, but we got ",
147
+ "boundaries tensor dim(", boundaries.dim(), ") and input value's dim(", input.dim(), ") numel(",
148
+ input.numel(), ")");
149
+
150
+ TORCH_CHECK(boundaries.dim() != 0, "torch.searchsorted(): boundaries tensor should have positive dimension, but ",
151
+ "got 0 dimension");
152
+
153
+ TORCH_CHECK(boundaries.dim() == 1 || searchsorted_dims_matched_before_last_dim(boundaries, input),
154
+ "torch.searchsorted(): boundaries tensor should be 1 dimension or the first N-1 dimensions of boundaries tensor ",
155
+ "and input value tensor must match, but we got boundaries tensor ", boundaries.sizes(), " and input value tensor ",
156
+ input.sizes());
157
+
158
+ ScalarType output_dtype = output.scalar_type();
159
+ TORCH_CHECK(
160
+ (output_dtype == ScalarType::Long && !out_int32) ||
161
+ (output_dtype == ScalarType::Int && out_int32),
162
+ "torch.searchsorted(): output tensor's dtype is wrong, it can only be Int(int32) or Long(int64) depending on ",
163
+ "whether out_int32 flag is True, but we got output tensor's dtype ", output_dtype,
164
+ " and out_int32 flag is ", (out_int32 ? "True" : "False"));
165
+
166
+ if (out_int32) {
167
+ TORCH_CHECK(boundaries.sizes().back() < INT_MAX,
168
+ "torch.searchsorted(): the size of boundaries' last dimension should be less than ", INT_MAX, ", but we got ",
169
+ boundaries.sizes().back());
170
+ }
171
+ }
172
+
173
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/CPUFallback.h ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/core/ivalue.h>
4
+ #include <ATen/core/stack.h>
5
+ #include <ATen/core/boxing/KernelFunction.h>
6
+ #include <ATen/core/dispatch/Dispatcher.h>
7
+ #include <c10/util/Metaprogramming.h>
8
+ #include <torch/library.h>
9
+
10
+ namespace at::native {
11
+
12
+ // This function implements a boxed fallback to CPU.
13
+ // External backends can add their own custom logging on top if it to customize their own CPU fallbacks.
14
+ TORCH_API void cpu_fallback(const c10::OperatorHandle& op, torch::jit::Stack* stack, bool error_on_views = false,
15
+ c10::DispatchKey cpu_dispatch_key = c10::DispatchKey::CPU);
16
+
17
+ // This is a helper function that backends can use to directly call their boxed CPU fallback
18
+ // TODO: update and add a usage example after https://github.com/pytorch/pytorch/pull/58092 lands.
19
+ template<c10::KernelFunction::BoxedKernelFunction* fallback_fn, class Op, bool symint, class ReturnType, class... ParameterTypes>
20
+ struct _call_fallback_fn final {};
21
+
22
+ template<c10::KernelFunction::BoxedKernelFunction* fallback_fn, class Op, bool symint, class ReturnType, class... ParameterTypes>
23
+ struct _call_fallback_fn<fallback_fn, Op, symint, ReturnType(ParameterTypes...)> final {
24
+ static ReturnType call(typename c10::maybe_keep_symint<symint, ParameterTypes>::type... args) {
25
+ auto op = c10::Dispatcher::singleton()
26
+ // TODO: figure out how to make compiler happy without dynamic casts
27
+ .findSchemaOrThrow((const char*) Op::name, (const char*) Op::overload_name)
28
+ //.findSchemaOrThrow("a", "b")
29
+ .typed<ReturnType (typename c10::maybe_keep_symint<symint, ParameterTypes>::type...)>();
30
+ return c10::impl::BoxedKernelWrapper<ReturnType (typename c10::maybe_keep_symint<symint, ParameterTypes>::type...)>::call(
31
+ c10::BoxedKernel::makeFromFunction<fallback_fn>(),
32
+ op,
33
+ c10::DispatchKeySet(), // we know that the cpu_fallback doesn't use the dispatch keyset.
34
+ // TODO: get std::forward<> to work
35
+ args...
36
+ );
37
+ }
38
+ };
39
+
40
+ template<c10::KernelFunction::BoxedKernelFunction* fallback_fn, class Op>
41
+ using call_fallback_fn_symint = _call_fallback_fn<fallback_fn, Op, true, typename Op::schema>;
42
+
43
+ template<c10::KernelFunction::BoxedKernelFunction* fallback_fn, class Op>
44
+ using call_fallback_fn = _call_fallback_fn<fallback_fn, Op, false, typename Op::schema>;
45
+
46
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/ComplexHelper.h ADDED
@@ -0,0 +1,97 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/core/Tensor.h>
4
+ #include <c10/util/irange.h>
5
+
6
+ #ifndef AT_PER_OPERATOR_HEADERS
7
+ #include <ATen/NativeFunctions.h>
8
+ #else
9
+ #include <ATen/ops/view_as_real_native.h>
10
+ #include <ATen/ops/view_as_complex_native.h>
11
+
12
+ #include <utility>
13
+ #endif
14
+
15
+ // WARNING: this header contains non-inline functions and should be only
16
+ // included from ONE cpp file
17
+
18
+ namespace at::native {
19
+
20
+ // View tensor with new dtype, storage offset, sizes and strides
21
+ inline Tensor view_tensor(
22
+ const Tensor &tensor, ScalarType dtype,
23
+ c10::SymInt offset, SymIntArrayRef sizes, SymIntArrayRef strides) {
24
+ Storage storage = tensor.storage();
25
+ auto key_set = tensor.key_set().remove(DispatchKey::Conjugate);
26
+ auto new_tensor = detail::make_tensor<TensorImpl>(
27
+ c10::TensorImpl::VIEW, std::move(storage), key_set, scalarTypeToTypeMeta(dtype));
28
+ auto * impl = new_tensor.unsafeGetTensorImpl();
29
+ impl->set_sizes_and_strides(sizes, strides, offset);
30
+ return new_tensor;
31
+ }
32
+
33
+ inline SymDimVector computeStrideForViewAsReal(SymIntArrayRef oldstride) {
34
+ SymDimVector res(oldstride.size() + 1);
35
+ for (const auto i : c10::irange(oldstride.size())) {
36
+ res[i] = oldstride[i] * 2;
37
+ }
38
+ res.back() = 1;
39
+ return res;
40
+ }
41
+
42
+ inline Tensor _view_as_real_physical(const Tensor& self) {
43
+ TORCH_CHECK(self.is_complex(), "view_as_real is only supported for complex tensors");
44
+ auto old_sizes = self.sym_sizes();
45
+ SymDimVector new_sizes(old_sizes.size() + 1);
46
+ std::copy(old_sizes.begin(), old_sizes.end(), new_sizes.begin());
47
+ // last dimension will always have two elements containing the real and imag vals
48
+ new_sizes.back() = 2;
49
+ auto new_strides = computeStrideForViewAsReal(self.sym_strides());
50
+ auto new_storage_offset = self.sym_storage_offset() * 2;
51
+ const auto float_type = c10::toRealValueType(self.scalar_type());
52
+ auto real_tensor = view_tensor(self, float_type, std::move(new_storage_offset), new_sizes, new_strides);
53
+ return real_tensor;
54
+ }
55
+
56
+ // expects as input a complex tensor and returns back a tensor
57
+ // with corresponding real dtype containing the complex values
58
+ // in the last two dimensions
59
+ Tensor view_as_real(const Tensor& self) {
60
+ TORCH_CHECK(!self.is_conj(), "view_as_real doesn't work on unresolved conjugated tensors. To resolve the conjugate tensor so you can view it as real, use self.resolve_conj(); however, be warned that the resulting tensor will NOT alias the original.");
61
+ return _view_as_real_physical(self);
62
+ }
63
+
64
+ inline SymDimVector computeStrideForViewAsComplex(SymIntArrayRef oldstride) {
65
+ const auto dim = oldstride.size();
66
+ TORCH_CHECK(dim > 0 && oldstride[dim - 1] == 1, "Tensor must have a last dimension with stride 1");
67
+
68
+ SymDimVector res(dim - 1);
69
+ for (const auto i : c10::irange(res.size())) {
70
+ TORCH_CHECK(oldstride[i] % 2 == 0, "Tensor must have a stride divisible by 2 for all but last dimension");
71
+ res[i] = oldstride[i] / 2;
72
+ }
73
+ return res;
74
+ }
75
+
76
+ // expects as input a float or double tensor with last dimension of size 2
77
+ // and returns back a tensor with corresponding complex dtype
78
+ Tensor view_as_complex(const Tensor& self) {
79
+ TORCH_CHECK(
80
+ self.scalar_type() == kFloat || self.scalar_type() == kDouble || self.scalar_type() == kHalf,
81
+ "view_as_complex is only supported for half, float and double tensors, but got a tensor of scalar type: ", self.scalar_type());
82
+
83
+ auto old_sizes = self.sym_sizes();
84
+ TORCH_CHECK(!old_sizes.empty(), "Input tensor must have one or more dimensions");
85
+ TORCH_CHECK(old_sizes[old_sizes.size()-1] == 2, "Tensor must have a last dimension of size 2");
86
+ SymDimVector new_sizes(old_sizes.begin(), old_sizes.end() - 1);
87
+
88
+ const auto new_strides = computeStrideForViewAsComplex(self.sym_strides());
89
+ const auto complex_type = c10::toComplexType(self.scalar_type());
90
+
91
+ TORCH_CHECK(self.sym_storage_offset() % 2 == 0, "Tensor must have a storage_offset divisible by 2");
92
+ const auto new_storage_offset = self.sym_storage_offset() / 2;
93
+
94
+ return view_tensor(self, complex_type, new_storage_offset, new_sizes, new_strides);
95
+ }
96
+
97
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/CompositeRandomAccessor.h ADDED
@@ -0,0 +1,34 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/CompositeRandomAccessorCommon.h>
4
+
5
+ namespace at::native {
6
+
7
+ struct TupleInfoCPU {
8
+ template <typename ...Types>
9
+ using tuple = std::tuple<Types...>;
10
+
11
+ template <typename ...Types>
12
+ static constexpr auto tie(Types&... args) noexcept {
13
+ return std::tie(args...);
14
+ }
15
+ };
16
+
17
+ template <typename KeyAccessor, typename ValueAccessor>
18
+ using CompositeRandomAccessorCPU =
19
+ CompositeRandomAccessor<KeyAccessor, ValueAccessor, TupleInfoCPU>;
20
+
21
+ template <typename Values, typename References>
22
+ void swap(
23
+ references_holder<Values, References> rh1,
24
+ references_holder<Values, References> rh2
25
+ ) {
26
+ return std::swap(rh1.data(), rh2.data());
27
+ }
28
+
29
+ template <int N, typename Values, typename References>
30
+ auto get(references_holder<Values, References> rh) -> decltype(std::get<N>(rh.data())) {
31
+ return std::get<N>(rh.data());
32
+ }
33
+
34
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/CompositeRandomAccessorCommon.h ADDED
@@ -0,0 +1,263 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <utility>
2
+
3
+ #pragma once
4
+
5
+ namespace at::native {
6
+
7
+ namespace {
8
+
9
+ // operator_brackets_proxy is used in
10
+ // CompositeRandomAccessor in place of operator[].
11
+ // For some iterators, references returned by operator[]
12
+ // could become invalid, operator_brackets_proxy tries to
13
+ // resolve that by making accessor[n] to be equivalent to
14
+ // *(accessor + n).
15
+ template <typename Accessor>
16
+ class operator_brackets_proxy {
17
+ using reference = typename std::iterator_traits<Accessor>::reference;
18
+ using value_type = typename std::iterator_traits<Accessor>::value_type;
19
+
20
+ public:
21
+ C10_HOST_DEVICE
22
+ operator_brackets_proxy(Accessor const& accessor)
23
+ : accessor(accessor)
24
+ {}
25
+
26
+ C10_HOST_DEVICE
27
+ operator reference() {
28
+ return *accessor;
29
+ }
30
+
31
+ C10_HOST_DEVICE
32
+ reference operator*() {
33
+ return *accessor;
34
+ }
35
+
36
+ C10_HOST_DEVICE
37
+ operator_brackets_proxy& operator=(value_type const& val) {
38
+ *accessor = val;
39
+ return *this;
40
+ }
41
+
42
+ private:
43
+ Accessor accessor;
44
+ };
45
+
46
+ }
47
+
48
+ // references_holder is used as a surrogate for the
49
+ // references type from std::iterator_traits in CompositeRandomAccessor.
50
+ // It is assumed in CompositeRandomAccessor that
51
+ // References = tuple<Types&...>,
52
+ // Values = tuple<Types...> by default,
53
+ // but they could be anything as long as References could be
54
+ // cast to Values.
55
+ // If you plan to use it with STL, for example, you will need to
56
+ // define 'swap` and `get`(aka std::get) methods.
57
+ template <typename Values, typename References>
58
+ class references_holder {
59
+ public:
60
+ using values = Values;
61
+ using references = References;
62
+
63
+ C10_HOST_DEVICE
64
+ references_holder(references refs)
65
+ : refs{std::move(refs)}
66
+ {}
67
+
68
+ C10_HOST_DEVICE
69
+ operator references() {
70
+ return refs;
71
+ }
72
+
73
+ C10_HOST_DEVICE
74
+ operator values() {
75
+ return refs;
76
+ }
77
+
78
+ C10_HOST_DEVICE
79
+ references_holder& operator=(values vals) {
80
+ refs = vals;
81
+ return *this;
82
+ }
83
+
84
+ C10_HOST_DEVICE
85
+ references& data() {
86
+ return refs;
87
+ }
88
+
89
+ protected:
90
+ references refs;
91
+ };
92
+
93
+ // CompositeRandomAccessor is essentially a simplified version of
94
+ // a random access iterator over two random access iterators.
95
+ // TupleInfo should contain a variadic type `tuple`, and a method `tie`,
96
+ // which constructs a tuple of references from a variadic list of arguments.
97
+ template <typename KeyAccessor, typename ValueAccessor, typename TupleInfo>
98
+ class CompositeRandomAccessor {
99
+ using self_type = CompositeRandomAccessor<KeyAccessor, ValueAccessor, TupleInfo>;
100
+
101
+ using key_accessor_value_type =
102
+ typename std::iterator_traits<KeyAccessor>::value_type;
103
+ using value_accessor_value_type =
104
+ typename std::iterator_traits<ValueAccessor>::value_type;
105
+ using key_accessor_reference_type =
106
+ typename std::iterator_traits<KeyAccessor>::reference;
107
+ using value_accessor_reference_type =
108
+ typename std::iterator_traits<ValueAccessor>::reference;
109
+
110
+ using composite_value_type = typename TupleInfo::template tuple<
111
+ key_accessor_value_type,
112
+ value_accessor_value_type>;
113
+ using composite_reference = typename TupleInfo::template tuple<
114
+ key_accessor_reference_type,
115
+ value_accessor_reference_type>;
116
+
117
+ public:
118
+ using value_type = composite_value_type;
119
+ using reference = references_holder<composite_value_type, composite_reference>;
120
+ // Note that CompositeRandomAccessor does not hold key and values
121
+ // in a specific datastructure, which means that a pointer to a (key, value)
122
+ // is not defined. Hence we just use a pointer type of the KeyAccessor.
123
+ using pointer = typename std::iterator_traits<KeyAccessor>::pointer;
124
+ using difference_type = typename std::iterator_traits<KeyAccessor>::difference_type;
125
+ using iterator_category = std::random_access_iterator_tag;
126
+
127
+ C10_HOST_DEVICE
128
+ CompositeRandomAccessor() = default;
129
+
130
+ C10_HOST_DEVICE
131
+ CompositeRandomAccessor(KeyAccessor keys, ValueAccessor values)
132
+ : keys(keys), values(values)
133
+ {}
134
+
135
+ // Pointer-like operations {
136
+ C10_HOST_DEVICE
137
+ reference operator*() const {
138
+ return TupleInfo::tie(*keys, *values);
139
+ }
140
+
141
+ // operator->() is supposed to return a pointer type.
142
+ // Since CompositeRandomAccessor does not hold pointers to pairs,
143
+ // we just return a pointer to a key.
144
+ C10_HOST_DEVICE
145
+ auto* operator->() const {
146
+ return keys.operator->();
147
+ }
148
+
149
+ C10_HOST_DEVICE
150
+ reference operator[](difference_type idx) {
151
+ return operator_brackets_proxy<self_type>(
152
+ CompositeRandomAccessor(keys + idx, values + idx)
153
+ );
154
+ }
155
+ // }
156
+
157
+ // Prefix/postfix increment/decrement {
158
+ C10_HOST_DEVICE
159
+ CompositeRandomAccessor& operator++() {
160
+ ++keys;
161
+ ++values;
162
+ return *this;
163
+ }
164
+
165
+ C10_HOST_DEVICE
166
+ CompositeRandomAccessor operator++(int) {
167
+ CompositeRandomAccessor copy(*this);
168
+ ++*this;
169
+ return copy;
170
+ }
171
+
172
+ C10_HOST_DEVICE
173
+ CompositeRandomAccessor& operator--() {
174
+ --keys;
175
+ --values;
176
+ return *this;
177
+ }
178
+
179
+ C10_HOST_DEVICE
180
+ CompositeRandomAccessor operator--(int) {
181
+ CompositeRandomAccessor copy(*this);
182
+ --*this;
183
+ return copy;
184
+ }
185
+ // }
186
+
187
+ // Arithmetic operations {
188
+ C10_HOST_DEVICE
189
+ CompositeRandomAccessor& operator+=(difference_type offset) {
190
+ keys += offset;
191
+ values += offset;
192
+ return *this;
193
+ }
194
+
195
+ C10_HOST_DEVICE
196
+ CompositeRandomAccessor operator+(difference_type offset) const {
197
+ return CompositeRandomAccessor(keys + offset, values + offset);
198
+ }
199
+
200
+ C10_HOST_DEVICE
201
+ friend CompositeRandomAccessor operator+(
202
+ difference_type offset,
203
+ const CompositeRandomAccessor& accessor
204
+ ) {
205
+ return accessor + offset;
206
+ }
207
+
208
+ C10_HOST_DEVICE
209
+ CompositeRandomAccessor& operator-=(difference_type offset) {
210
+ keys -= offset;
211
+ values -= offset;
212
+ return *this;
213
+ }
214
+
215
+ C10_HOST_DEVICE
216
+ CompositeRandomAccessor operator-(difference_type offset) const {
217
+ return CompositeRandomAccessor(keys - offset, values - offset);
218
+ }
219
+
220
+ C10_HOST_DEVICE
221
+ difference_type operator-(const CompositeRandomAccessor& other) const {
222
+ return keys - other.keys;
223
+ }
224
+ // }
225
+
226
+ // Comparison operators {
227
+ C10_HOST_DEVICE
228
+ bool operator==(const CompositeRandomAccessor& other) const {
229
+ return keys == other.keys;
230
+ }
231
+
232
+ C10_HOST_DEVICE
233
+ bool operator!=(const CompositeRandomAccessor& other) const {
234
+ return keys != other.keys;
235
+ }
236
+
237
+ C10_HOST_DEVICE
238
+ bool operator<(const CompositeRandomAccessor& other) const {
239
+ return keys < other.keys;
240
+ }
241
+
242
+ C10_HOST_DEVICE
243
+ bool operator<=(const CompositeRandomAccessor& other) const {
244
+ return keys <= other.keys;
245
+ }
246
+
247
+ C10_HOST_DEVICE
248
+ bool operator>(const CompositeRandomAccessor& other) const {
249
+ return keys > other.keys;
250
+ }
251
+
252
+ C10_HOST_DEVICE
253
+ bool operator>=(const CompositeRandomAccessor& other) const {
254
+ return keys >= other.keys;
255
+ }
256
+ // }
257
+
258
+ protected:
259
+ KeyAccessor keys;
260
+ ValueAccessor values;
261
+ };
262
+
263
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/ConvUtils.h ADDED
@@ -0,0 +1,449 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/core/Tensor.h>
3
+ #include <ATen/TensorUtils.h>
4
+ #include <ATen/detail/CUDAHooksInterface.h>
5
+ #include <ATen/native/DispatchStub.h>
6
+ #include <c10/util/env.h>
7
+ #include <c10/util/irange.h>
8
+
9
+ #include <utility>
10
+
11
+ namespace at::native {
12
+
13
+ using conv_depthwise2d_backward_fn = std::tuple<at::Tensor,at::Tensor>(*)(
14
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
15
+ at::IntArrayRef, at::IntArrayRef, std::array<bool, 2>);
16
+ DECLARE_DISPATCH(conv_depthwise2d_backward_fn, conv_depthwise2d_backward_stub)
17
+ using conv_depthwise3d_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
18
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
19
+ at::IntArrayRef, at::IntArrayRef, std::array<bool, 3>);
20
+ DECLARE_DISPATCH(conv_depthwise3d_backward_fn, conv_depthwise3d_backward_stub)
21
+ using cudnn_convolution_backward_fn = std::tuple<at::Tensor,at::Tensor>(*)(
22
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
23
+ at::IntArrayRef, int64_t, bool, bool, bool, std::array<bool,2>);
24
+ DECLARE_DISPATCH(cudnn_convolution_backward_fn, cudnn_convolution_backward_stub)
25
+ using mps_convolution_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
26
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
27
+ at::IntArrayRef, int64_t, std::array<bool,3>);
28
+ DECLARE_DISPATCH(mps_convolution_backward_fn, mps_convolution_backward_stub)
29
+ using cudnn_convolution_transpose_backward_fn = std::tuple<at::Tensor,at::Tensor>(*)(
30
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
31
+ at::IntArrayRef, at::IntArrayRef, int64_t, bool, bool, bool, std::array<bool,2>);
32
+ DECLARE_DISPATCH(cudnn_convolution_transpose_backward_fn, cudnn_convolution_transpose_backward_stub)
33
+ using miopen_convolution_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
34
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
35
+ at::IntArrayRef, int64_t, bool, bool, std::array<bool,3>);
36
+ DECLARE_DISPATCH(miopen_convolution_backward_fn, miopen_convolution_backward_stub)
37
+ using miopen_convolution_transpose_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
38
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
39
+ at::IntArrayRef, at::IntArrayRef, int64_t, bool, bool, std::array<bool,3>);
40
+ DECLARE_DISPATCH(miopen_convolution_transpose_backward_fn, miopen_convolution_transpose_backward_stub)
41
+ using miopen_depthwise_convolution_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
42
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
43
+ at::IntArrayRef, int64_t, bool, bool, std::array<bool,3>);
44
+ DECLARE_DISPATCH(miopen_depthwise_convolution_backward_fn, miopen_depthwise_convolution_backward_stub)
45
+ using mkldnn_convolution_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
46
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
47
+ at::IntArrayRef, int64_t, std::array<bool,3>);
48
+ DECLARE_DISPATCH(mkldnn_convolution_backward_fn, mkldnn_convolution_backward_stub)
49
+ using mkldnn_convolution_transpose_fn = Tensor(*)(const Tensor&, const Tensor&, const std::optional<Tensor>&,
50
+ IntArrayRef, IntArrayRef, IntArrayRef, IntArrayRef, int64_t);
51
+ DECLARE_DISPATCH(mkldnn_convolution_transpose_fn, mkldnn_convolution_transpose_stub)
52
+ using mkldnn_convolution_transpose_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
53
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
54
+ at::IntArrayRef, at::IntArrayRef, int64_t, std::array<bool,3>);
55
+ DECLARE_DISPATCH(mkldnn_convolution_transpose_backward_fn, mkldnn_convolution_transpose_backward_stub)
56
+ using slow_conv_dilated2d_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
57
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
58
+ at::IntArrayRef, at::IntArrayRef, std::array<bool, 3>);
59
+ DECLARE_DISPATCH(slow_conv_dilated2d_backward_fn, slow_conv_dilated2d_backward_stub)
60
+ using slow_conv_dilated3d_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
61
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
62
+ at::IntArrayRef, at::IntArrayRef, std::array<bool, 3>);
63
+ DECLARE_DISPATCH(slow_conv_dilated3d_backward_fn, slow_conv_dilated3d_backward_stub)
64
+ using slow_conv_transpose2d_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
65
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
66
+ at::IntArrayRef, at::IntArrayRef, at::IntArrayRef, std::array<bool,3>);
67
+ DECLARE_DISPATCH(slow_conv_transpose2d_backward_fn, slow_conv_transpose2d_backward_stub)
68
+ using slow_conv_transpose3d_backward_fn = std::tuple<at::Tensor,at::Tensor,at::Tensor>(*)(
69
+ const at::Tensor&, const at::Tensor&, const at::Tensor&, at::IntArrayRef, at::IntArrayRef,
70
+ at::IntArrayRef, at::IntArrayRef, at::IntArrayRef, std::array<bool,3>);
71
+ DECLARE_DISPATCH(slow_conv_transpose3d_backward_fn, slow_conv_transpose3d_backward_stub)
72
+
73
+ namespace {
74
+ bool is_cudnnv8_heuristic_mode_b() {
75
+ static const bool is_cudnnv8_heuristic_mode_b = c10::utils::check_env("TORCH_CUDNN_USE_HEURISTIC_MODE_B") == true;
76
+ return is_cudnnv8_heuristic_mode_b;
77
+ }
78
+ }
79
+
80
+ inline bool cudnnv8_enabled_check_debug() {
81
+ static bool cudnnv8_flag = c10::utils::check_env("TORCH_CUDNN_V8_API_DISABLED") != true;
82
+ static bool cudnnv8_debug = c10::utils::check_env("TORCH_CUDNN_V8_API_DEBUG") == true;
83
+ static uint8_t cudnnv8_debugcount = 0;
84
+ if (cudnnv8_debug == 1 && cudnnv8_debugcount < 10) {
85
+ TORCH_WARN("TORCH_CUDNN_V8_DEBUG ON, V8 ON: ", cudnnv8_flag, " TORCH_CUDNN_USE_HEURISTIC_MODE B: ", is_cudnnv8_heuristic_mode_b());
86
+ cudnnv8_debugcount++;
87
+ }
88
+ return cudnnv8_flag == 1;
89
+ }
90
+
91
+ inline bool cudnnv8_use_heur_mode_b() {
92
+ return is_cudnnv8_heuristic_mode_b();
93
+ }
94
+
95
+ // Keep in sync with py::enum_ in Module.cpp
96
+ enum class ConvBackend {
97
+ CudaDepthwise2d,
98
+ CudaDepthwise3d,
99
+ Cudnn,
100
+ CudnnTranspose,
101
+ Empty,
102
+ Miopen,
103
+ MiopenDepthwise,
104
+ MiopenTranspose,
105
+ Mkldnn,
106
+ MkldnnTranspose,
107
+ MkldnnEmpty,
108
+ NnpackSpatial,
109
+ Overrideable,
110
+ Slow2d,
111
+ Slow3d,
112
+ SlowDilated2d,
113
+ SlowDilated3d,
114
+ SlowTranspose2d,
115
+ SlowTranspose3d,
116
+ Winograd3x3Depthwise,
117
+ Xnnpack2d,
118
+ Mps,
119
+ MpsTranspose,
120
+ };
121
+
122
+ // Overload for selecting the convolution backend from the full set of convolution inputs.
123
+ // This overload is exposed to python for testing, etc.
124
+ TORCH_API ConvBackend select_conv_backend(
125
+ const Tensor& input, const Tensor& weight, const std::optional<Tensor>& bias_opt,
126
+ SymIntArrayRef stride, SymIntArrayRef padding, SymIntArrayRef dilation,
127
+ bool transposed, SymIntArrayRef output_padding, c10::SymInt groups, const at::OptionalSymIntArrayRef bias_sizes_opt);
128
+
129
+ TORCH_API at::MemoryFormat _determine_backend_memory_format(const Tensor& input,
130
+ const Tensor& weight,
131
+ const ConvBackend backend);
132
+
133
+ // ---------------------------------------------------------------------
134
+ //
135
+ // Math
136
+ //
137
+ // ---------------------------------------------------------------------
138
+
139
+ constexpr int input_batch_size_dim = 0; // also grad_input
140
+ constexpr int input_channels_dim = 1;
141
+ constexpr int output_batch_size_dim = 0; // also grad_output
142
+ constexpr int output_channels_dim = 1;
143
+ constexpr int weight_output_channels_dim = 0;
144
+ constexpr int weight_input_channels_dim = 1;
145
+
146
+ // Often written as 2 + max_dim (extra dims for batch size and channels)
147
+ constexpr int max_dim = 3;
148
+
149
+ // ---------------------------------------------------------------------
150
+ //
151
+ // Checking
152
+ //
153
+ // ---------------------------------------------------------------------
154
+
155
+ // Used on pad, stride and dilation
156
+ static void check_args(CheckedFrom c, IntArrayRef args, size_t expected_size, const char* arg_name)
157
+ {
158
+ TORCH_CHECK(args.size() <= expected_size,
159
+ "Too many ", arg_name, " values (", args.size(), ") supplied, expecting ",
160
+ expected_size, " (while checking arguments for ", c, ")");
161
+ TORCH_CHECK(args.size() >= expected_size,
162
+ "Not enough ", arg_name, " values (", args.size(), ") supplied, expecting ",
163
+ expected_size, " (while checking arguments for ", c, ")");
164
+
165
+ auto num_negative_values = std::count_if(args.begin(), args.end(), [](int x){return x < 0;});
166
+ if (num_negative_values > 0){
167
+ std::stringstream ss;
168
+ ss << arg_name << " should be greater than zero but got (";
169
+ std::copy(args.begin(), args.end() - 1, std::ostream_iterator<int>(ss,", "));
170
+ ss << args.back() << ")" << " (while checking arguments for " << c << ")";
171
+ TORCH_CHECK(false, ss.str());
172
+ }
173
+ }
174
+
175
+
176
+ // NOTE [ Convolution checks ]
177
+ //
178
+ // NB: For many call sites, it is not strictly necessary to check all of
179
+ // these relationships (for example, for forward convolution, we compute
180
+ // the size of output ourselves, so we don't actually need to check
181
+ // output. However, writing a single function that does everything
182
+ // means we get to reuse it for both forwards and all backwards
183
+ // variants, even when the set of "real" inputs varies. The magic of
184
+ // relational computing!
185
+ //
186
+ // (There is one downside, which is that it is slightly harder to write
187
+ // error messages which are able to distinguish between real inputs
188
+ // (which the user can change) and computed inputs (which the user can
189
+ // only indirectly affect). It would be an interesting exercise to
190
+ // come up with a general framework to handle such situations.)
191
+ inline void convolution_shape_check(
192
+ CheckedFrom c,
193
+ const TensorGeometryArg& input, const TensorGeometryArg& weight, const TensorGeometryArg& output,
194
+ IntArrayRef padding, IntArrayRef stride, IntArrayRef dilation, int64_t groups)
195
+ {
196
+ check_args(c, padding, input->dim() - 2, "padding");
197
+ check_args(c, stride, padding.size(), "stride");
198
+ check_args(c, dilation, padding.size(), "dilation");
199
+
200
+ // Input
201
+ checkDimRange(c, input, 3, 6 /* exclusive */);
202
+ checkSize_symint(c, input, input_channels_dim, weight->size(1) * groups);
203
+
204
+ // Weight
205
+ checkSameDim(c, input, weight);
206
+
207
+ // TODO: check that output->size() matches output_sizes
208
+ // TODO: check that weight matches output->sizes()
209
+ checkSameDim(c, input, output);
210
+ }
211
+
212
+ // NB: conv_output_size and conv_input_size are not bijections,
213
+ // as conv_output_size loses information; this is why conv_input_size
214
+ // takes an extra output_padding argument to resolve the ambiguity.
215
+
216
+ template <typename T>
217
+ inline std::vector<T> _conv_output_size(
218
+ ArrayRef<T> input_size, ArrayRef<T> weight_size,
219
+ ArrayRef<T> padding, ArrayRef<T> stride, ArrayRef<T> dilation = ArrayRef<T>()
220
+ ) {
221
+ // ASSERT(input_size.size() > 2)
222
+ // ASSERT(input_size.size() == weight_size.size())
223
+ bool has_dilation = !dilation.empty();
224
+ auto dim = input_size.size();
225
+ std::vector<T> output_size(dim);
226
+ output_size[0] = input_size[input_batch_size_dim];
227
+ output_size[1] = weight_size[weight_output_channels_dim];
228
+ for (const auto d : c10::irange(2, dim)) {
229
+ auto dilation_ = has_dilation ? dilation[d - 2] : 1;
230
+ auto kernel = dilation_ * (weight_size[d] - 1) + 1;
231
+ output_size[d] = (input_size[d] + (2 * padding[d - 2]) - kernel) / stride[d - 2] + 1;
232
+ }
233
+ return output_size;
234
+ }
235
+
236
+ inline std::vector<int64_t> conv_output_size(
237
+ IntArrayRef input_size, IntArrayRef weight_size,
238
+ IntArrayRef padding, IntArrayRef stride, IntArrayRef dilation = IntArrayRef()
239
+ ) {
240
+ return _conv_output_size(input_size, weight_size, padding, stride, dilation);
241
+ }
242
+
243
+ inline std::vector<c10::SymInt> conv_output_size(
244
+ SymIntArrayRef input_size, SymIntArrayRef weight_size,
245
+ SymIntArrayRef padding, SymIntArrayRef stride, SymIntArrayRef dilation = SymIntArrayRef()
246
+ ) {
247
+ return _conv_output_size(input_size, weight_size, padding, stride, dilation);
248
+ }
249
+
250
+ template <typename T>
251
+ std::vector<T> _conv_input_size(
252
+ ArrayRef<T> output_size, ArrayRef<T> weight_size,
253
+ ArrayRef<T> padding, ArrayRef<T> output_padding, ArrayRef<T> stride, ArrayRef<T> dilation, T groups
254
+ ) {
255
+ // ASSERT(output_size.size() > 2)
256
+ // ASSERT(output_size.size() == weight_size.size())
257
+ auto dim = output_size.size();
258
+ std::vector<T> input_size(dim);
259
+ input_size[0] = output_size[output_batch_size_dim];
260
+ input_size[1] = weight_size[weight_input_channels_dim] * groups;
261
+ for (const auto d : c10::irange(2, dim)) {
262
+ auto kernel = (weight_size[d] - 1) * dilation[d - 2] + 1;
263
+ input_size[d] = (output_size[d] - 1) * stride[d - 2] - (padding[d - 2] * 2) +
264
+ kernel + output_padding[d - 2];
265
+ }
266
+ return input_size;
267
+ }
268
+
269
+ inline std::vector<c10::SymInt> conv_input_size(
270
+ SymIntArrayRef output_size, SymIntArrayRef weight_size,
271
+ SymIntArrayRef padding, SymIntArrayRef output_padding, SymIntArrayRef stride, SymIntArrayRef dilation, c10::SymInt groups
272
+ ) {
273
+ return _conv_input_size(output_size, weight_size, padding, output_padding, stride, dilation, std::move(groups));
274
+ }
275
+
276
+ inline std::vector<int64_t> conv_input_size(
277
+ IntArrayRef output_size, IntArrayRef weight_size,
278
+ IntArrayRef padding, IntArrayRef output_padding, IntArrayRef stride, IntArrayRef dilation, int64_t groups
279
+ ) {
280
+ return _conv_input_size(output_size, weight_size, padding, output_padding, stride, dilation, groups);
281
+ }
282
+
283
+ template <typename T>
284
+ std::vector<T> _conv_weight_size(
285
+ ArrayRef<T> input_size, ArrayRef<T> output_size,
286
+ ArrayRef<T> padding, ArrayRef<T> output_padding, IntArrayRef stride, IntArrayRef dilation, int64_t groups
287
+ ) {
288
+ auto dim = input_size.size();
289
+ std::vector<T> weight_size(dim);
290
+ weight_size[0] = output_size[1];
291
+ weight_size[1] = input_size[1] / groups;
292
+ for (const auto d : c10::irange(2, dim)) {
293
+ auto kernel = input_size[d] - (output_size[d] - 1) * stride[d - 2]
294
+ + padding[d - 2] * 2 - output_padding[d - 2];
295
+ weight_size[d] = (kernel - 1) / dilation[d - 2] + 1;
296
+ }
297
+ return weight_size;
298
+ }
299
+
300
+ inline std::vector<c10::SymInt> conv_weight_size(
301
+ SymIntArrayRef input_size, SymIntArrayRef output_size,
302
+ SymIntArrayRef padding, SymIntArrayRef output_padding, IntArrayRef stride, IntArrayRef dilation, int64_t groups
303
+ ) {
304
+ return _conv_weight_size(input_size, output_size, padding, output_padding, stride, dilation, groups);
305
+ }
306
+
307
+ inline std::vector<int64_t> conv_weight_size(
308
+ IntArrayRef input_size, IntArrayRef output_size,
309
+ IntArrayRef padding, IntArrayRef output_padding, IntArrayRef stride, IntArrayRef dilation, int64_t groups
310
+ ) {
311
+ return _conv_weight_size(input_size, output_size, padding, output_padding, stride, dilation, groups);
312
+ }
313
+
314
+ inline Tensor reshape_bias(int64_t dim, const Tensor& bias) {
315
+ std::vector<int64_t> shape(dim, 1);
316
+ shape[1] = -1;
317
+ return bias.reshape(shape);
318
+ }
319
+
320
+ inline at::MemoryFormat cudnn_conv_suggest_memory_format(const at::Tensor& input, const at::Tensor& weight) {
321
+ // disable NHWC for float64 input.
322
+ if (!at::detail::getCUDAHooks().compiledWithCuDNN() ||
323
+ input.scalar_type() == at::kDouble ||
324
+ weight.scalar_type() == at::kDouble) {
325
+ return at::MemoryFormat::Contiguous;
326
+ }
327
+ long cudnn_version = at::detail::getCUDAHooks().versionCuDNN();
328
+ auto input_memory_format = input.suggest_memory_format();
329
+ auto weight_memory_format = weight.suggest_memory_format();
330
+ auto weight_ndim = weight.ndimension();
331
+
332
+ bool can_use_cudnn_channels_last_2d = (cudnn_version >= 7603) && (weight_ndim == 4) && (
333
+ (input_memory_format == at::MemoryFormat::ChannelsLast) ||
334
+ (weight_memory_format == at::MemoryFormat::ChannelsLast)
335
+ );
336
+ if (can_use_cudnn_channels_last_2d) {
337
+ return at::MemoryFormat::ChannelsLast;
338
+ }
339
+
340
+ bool can_use_cudnn_channels_last_3d = (cudnn_version >= 8005) && (weight_ndim == 5) && (
341
+ (input_memory_format == at::MemoryFormat::ChannelsLast3d) ||
342
+ (weight_memory_format == at::MemoryFormat::ChannelsLast3d)
343
+ );
344
+ if (can_use_cudnn_channels_last_3d) {
345
+ return at::MemoryFormat::ChannelsLast3d;
346
+ }
347
+
348
+ return at::MemoryFormat::Contiguous;
349
+ }
350
+
351
+ // controls whether emptyCache will be called following cudnn conv benchmarking
352
+ TORCH_API void _cudnn_set_conv_benchmark_empty_cache(bool enable);
353
+ TORCH_API bool _cudnn_get_conv_benchmark_empty_cache();
354
+
355
+
356
+ inline bool miopen_conv_use_channels_last(const at::Tensor& input, const at::Tensor& weight) {
357
+
358
+ // disable NHWC for float64 input.
359
+ if (!at::detail::getCUDAHooks().compiledWithMIOpen() ||
360
+ input.scalar_type() == at::kDouble ||
361
+ weight.scalar_type() == at::kDouble) {
362
+ return false;
363
+ }
364
+
365
+ bool can_use_miopen_channels_last_2d = false;
366
+ // TODO: Remove PYTORCH_MIOPEN_SUGGEST_NHWC once ROCm officially supports NHWC in MIOpen
367
+ // See #64427
368
+ static std::optional<bool> PYTORCH_MIOPEN_SUGGEST_NHWC = c10::utils::check_env("PYTORCH_MIOPEN_SUGGEST_NHWC");
369
+
370
+ auto input_memory_format = input.suggest_memory_format();
371
+ auto weight_memory_format = weight.suggest_memory_format();
372
+
373
+ can_use_miopen_channels_last_2d = PYTORCH_MIOPEN_SUGGEST_NHWC && *PYTORCH_MIOPEN_SUGGEST_NHWC && (
374
+ ( (input_memory_format == at::MemoryFormat::ChannelsLast) ||
375
+ (weight_memory_format == at::MemoryFormat::ChannelsLast) )
376
+ );
377
+
378
+ bool can_use_miopen_channels_last_3d = false;
379
+
380
+ return can_use_miopen_channels_last_2d || can_use_miopen_channels_last_3d;
381
+ }
382
+
383
+ inline bool mkldnn_conv_use_channels_last(const at::Tensor& input, const at::Tensor& weight) {
384
+
385
+ // disable NHWC for float64 input.
386
+ if (input.scalar_type() == at::kDouble ||
387
+ weight.scalar_type() == at::kDouble) {
388
+ return false;
389
+ }
390
+
391
+ // disable NHWC for MkldnnCPU tensor.
392
+ if (input.is_mkldnn() || weight.is_mkldnn()) {
393
+ return false;
394
+ }
395
+
396
+ auto input_memory_format = input.suggest_memory_format();
397
+ auto weight_memory_format = weight.suggest_memory_format();
398
+
399
+ bool can_use_mkldnn_channels_last_2d =
400
+ (input_memory_format == at::MemoryFormat::ChannelsLast) ||
401
+ (weight_memory_format == at::MemoryFormat::ChannelsLast);
402
+
403
+ bool can_use_mkldnn_channels_last_3d =
404
+ (input_memory_format == at::MemoryFormat::ChannelsLast3d) ||
405
+ (weight_memory_format == at::MemoryFormat::ChannelsLast3d);
406
+
407
+ return can_use_mkldnn_channels_last_2d || can_use_mkldnn_channels_last_3d;
408
+ }
409
+
410
+ inline bool thnn_conv_use_channels_last(const at::Tensor& input, const at::Tensor& weight) {
411
+
412
+ auto input_memory_format = input.suggest_memory_format();
413
+ auto weight_memory_format = weight.suggest_memory_format();
414
+
415
+ bool can_use_thnn_channels_last_2d = input.device().is_cpu() && (
416
+ (input_memory_format == at::MemoryFormat::ChannelsLast) || (
417
+ weight_memory_format == at::MemoryFormat::ChannelsLast));
418
+
419
+ return can_use_thnn_channels_last_2d;
420
+ }
421
+
422
+ inline bool xpu_conv_use_channels_last(const at::Tensor& input, const at::Tensor& weight) {
423
+
424
+ // check layout only for xpu tensor.
425
+ if (!input.is_xpu() || !weight.is_xpu()) {
426
+ return false;
427
+ }
428
+
429
+ // disable NHWC for float64 input.
430
+ if (input.scalar_type() == at::kDouble ||
431
+ weight.scalar_type() == at::kDouble) {
432
+ return false;
433
+ }
434
+
435
+ auto input_memory_format = input.suggest_memory_format();
436
+ auto weight_memory_format = weight.suggest_memory_format();
437
+
438
+ bool can_use_xpu_channels_last_2d =
439
+ (input_memory_format == at::MemoryFormat::ChannelsLast) ||
440
+ (weight_memory_format == at::MemoryFormat::ChannelsLast);
441
+
442
+ bool can_use_xpu_channels_last_3d =
443
+ (input_memory_format == at::MemoryFormat::ChannelsLast3d) ||
444
+ (weight_memory_format == at::MemoryFormat::ChannelsLast3d);
445
+
446
+ return can_use_xpu_channels_last_2d || can_use_xpu_channels_last_3d;
447
+ }
448
+
449
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/ConvolutionMM3d.h ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <ATen/core/Tensor.h>
2
+
3
+ namespace at::native {
4
+
5
+ std::tuple<Tensor, Tensor, Tensor> slow_conv3d_backward_cpu(
6
+ const Tensor& grad_output,
7
+ const Tensor& self,
8
+ const Tensor& weight,
9
+ IntArrayRef kernel_size,
10
+ IntArrayRef stride,
11
+ IntArrayRef padding,
12
+ std::array<bool, 3> output_mask);
13
+
14
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/Copy.h ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+
5
+ namespace at {
6
+
7
+ class Tensor;
8
+ struct TensorIterator;
9
+ class TensorBase;
10
+
11
+ namespace native {
12
+
13
+ using copy_fn = void (*)(TensorIterator&, bool non_blocking);
14
+
15
+ DECLARE_DISPATCH(copy_fn, copy_stub)
16
+
17
+ TORCH_API void copy_ignoring_overlaps(const TensorBase &dst, const TensorBase &src);
18
+
19
+ } // namespace native
20
+ } // namespace at
lib/python3.12/site-packages/torch/include/ATen/native/Cross.h ADDED
@@ -0,0 +1,14 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+
5
+ namespace at {
6
+ class Tensor;
7
+
8
+ namespace native {
9
+
10
+ using cross_fn = void(*)(const Tensor&, const Tensor&, const Tensor&, const int64_t d);
11
+
12
+ DECLARE_DISPATCH(cross_fn, cross_stub)
13
+
14
+ }} // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/DilatedConvolutionUtils.h ADDED
@@ -0,0 +1,229 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <algorithm>
4
+ #include <vector>
5
+
6
+ #include <ATen/div_rtn.h>
7
+ #include <ATen/core/Tensor.h>
8
+ #include <c10/util/irange.h>
9
+
10
+ #define TORCH_CHECK_DIM_SIZE(T, DIM, DIM_SIZE, SIZE) \
11
+ TORCH_CHECK( \
12
+ T.dim() == DIM && T.size(DIM_SIZE) == SIZE, \
13
+ "Need " #T " of dimension ", \
14
+ DIM, \
15
+ " and " #T ".size[", \
16
+ DIM_SIZE, \
17
+ "] == ", \
18
+ SIZE, \
19
+ " but got input to be of shape ", \
20
+ T.sizes())
21
+
22
+ namespace at::native::internal {
23
+ namespace {
24
+ inline bool all_positive(IntArrayRef& arr) {
25
+ return std::all_of(
26
+ arr.begin(), arr.end(), [](int64_t item) { return item > 0; });
27
+ }
28
+
29
+ inline bool all_nonnegative(std::vector<int64_t>& arr) {
30
+ return std::all_of(
31
+ arr.begin(), arr.end(), [](int64_t item) { return item >= 0; });
32
+ }
33
+
34
+ } // namespace
35
+
36
+ // calculate the rear part of output tensor sizes
37
+ template <int64_t dim>
38
+ std::vector<int64_t> get_output_size(
39
+ const Tensor& input,
40
+ IntArrayRef kernel_size,
41
+ IntArrayRef stride_size,
42
+ IntArrayRef pad_size,
43
+ IntArrayRef dilation_size) {
44
+ std::vector<int64_t> sizes;
45
+ for (const auto index : c10::irange(dim)) {
46
+ sizes.push_back(
47
+ div_rtn<int64_t>(
48
+ input.size(index + input.dim() - dim) + 2 * pad_size[index] -
49
+ (dilation_size[index] * (kernel_size[index] - 1) + 1),
50
+ stride_size[index]) +
51
+ 1);
52
+ }
53
+ return sizes;
54
+ }
55
+
56
+ // calculate the sizes of output tensor
57
+ template <int64_t dim>
58
+ std::vector<int64_t> get_output_size(
59
+ const Tensor& input,
60
+ const Tensor& weight,
61
+ IntArrayRef kernel_size,
62
+ IntArrayRef stride_size,
63
+ IntArrayRef pad_size,
64
+ IntArrayRef dilation_size) {
65
+ auto output_size = get_output_size<dim>(
66
+ input, kernel_size, stride_size, pad_size, dilation_size);
67
+ output_size.insert(output_size.begin(), weight.size(0));
68
+ if (input.dim() == dim + 2) {
69
+ output_size.insert(output_size.begin(), input.size(0));
70
+ }
71
+ return output_size;
72
+ }
73
+ /*
74
+ slow_conv_dilated_shape_check - check user-input to dilated convolution
75
+ forward and backward functions.
76
+ */
77
+ template <int64_t dim>
78
+ void slow_conv_dilated_shape_check(
79
+ const Tensor& input,
80
+ const Tensor& weight,
81
+ const Tensor& bias,
82
+ const Tensor& grad_output,
83
+ IntArrayRef kernel_size,
84
+ IntArrayRef stride_size,
85
+ IntArrayRef pad_size,
86
+ IntArrayRef dilation_size) {
87
+ /*
88
+ When the following tensors are defined:
89
+
90
+ bias, grad_weight, grad_output
91
+
92
+ then these are assumed to be contiguous without checking
93
+ because of these tensors are made contiguous by calling
94
+ .contiguous() method or by resizing of zero-sized tensors in
95
+ forward/backward functions.
96
+
97
+ When grad_weight is defined then it is assumed without
98
+ checking to have the same shape as weight, see backward
99
+ functions.
100
+ */
101
+ // Check size arguments
102
+ TORCH_CHECK(
103
+ kernel_size.size() == dim,
104
+ "kernel sizes length should be ",
105
+ dim,
106
+ ", but got ",
107
+ kernel_size.size());
108
+ TORCH_CHECK(
109
+ stride_size.size() == dim,
110
+ "strides length should be ",
111
+ dim,
112
+ ", but got ",
113
+ stride_size.size());
114
+ TORCH_CHECK(
115
+ dilation_size.size() == dim,
116
+ "dilations length should be ",
117
+ dim,
118
+ ", but got ",
119
+ dilation_size.size());
120
+ TORCH_CHECK(
121
+ pad_size.size() == dim,
122
+ "pads length should be ",
123
+ dim,
124
+ ", but got ",
125
+ pad_size.size());
126
+
127
+ TORCH_CHECK(
128
+ all_positive(kernel_size),
129
+ "kernel size should be greater than zero, but got ",
130
+ kernel_size);
131
+ TORCH_CHECK(
132
+ all_positive(stride_size),
133
+ "stride should be greater than zero, but got ",
134
+ stride_size);
135
+ TORCH_CHECK(
136
+ all_positive(dilation_size),
137
+ "dilation should be greater than zero, but got ",
138
+ dilation_size);
139
+
140
+ // check input
141
+ TORCH_CHECK(input.defined(), "input must be defined");
142
+ bool is_batch = input.dim() == dim + 2;
143
+ int64_t n = (is_batch ? 2 : 1);
144
+ int64_t ndim = n + dim;
145
+ if (!is_batch) {
146
+ // input dim has to be dim + 1 if not batched
147
+ TORCH_CHECK(
148
+ input.dim() == dim + 1,
149
+ "input must be 4D or 5D tensor but got ",
150
+ input.dim(),
151
+ "D tensor");
152
+ }
153
+
154
+ // check output sizes
155
+ auto output_size = get_output_size<dim>(
156
+ input, kernel_size, stride_size, pad_size, dilation_size);
157
+
158
+ TORCH_CHECK(
159
+ all_nonnegative(output_size),
160
+ "calculated output size ",
161
+ output_size,
162
+ " is too small (all sizes must be non-negative)");
163
+
164
+ // check weight
165
+ TORCH_CHECK(weight.defined(), "weight must be defined");
166
+ TORCH_CHECK(
167
+ weight.dim() == dim + 2,
168
+ "weight must be ",
169
+ dim + 2,
170
+ "D tensor but got ",
171
+ weight.dim(),
172
+ "D tensor dim=",
173
+ dim);
174
+ TORCH_CHECK(
175
+ weight.sizes().slice(2) == kernel_size,
176
+ "weight[2:] shape ",
177
+ weight.sizes().slice(2),
178
+ " must be equal to kernel_size ",
179
+ kernel_size);
180
+
181
+ TORCH_CHECK_DIM_SIZE(input, input.dim(), (is_batch ? 1 : 0), weight.size(1));
182
+
183
+ // check bias when present
184
+ if (bias.defined()) {
185
+ TORCH_CHECK(
186
+ bias.dim() == 1,
187
+ "bias must be 1D tensor but got ",
188
+ bias.dim(),
189
+ "D tensor");
190
+ TORCH_CHECK_DIM_SIZE(bias, 1, 0, weight.size(0));
191
+ }
192
+
193
+ // check grad_output when present
194
+ if (grad_output.defined()) {
195
+ TORCH_CHECK(
196
+ grad_output.dim() == ndim,
197
+ "grad_output must be ",
198
+ ndim,
199
+ "D tensor but got ",
200
+ grad_output.dim(),
201
+ "D tensor");
202
+ if (is_batch) {
203
+ TORCH_CHECK(
204
+ grad_output.size(0) == input.size(0),
205
+ "grad_output.size(0)=",
206
+ grad_output.size(0),
207
+ " must be input.size(0)=",
208
+ input.size(0));
209
+ }
210
+ TORCH_CHECK(
211
+ grad_output.size(n - 1) == weight.size(0),
212
+ "grad_output.size(",
213
+ n - 1,
214
+ ")=",
215
+ grad_output.size(n - 1),
216
+ " must be weight.size(0)=",
217
+ weight.size(0));
218
+ TORCH_CHECK(
219
+ grad_output.sizes().slice(n) == output_size,
220
+ "grad_output[",
221
+ n,
222
+ ":] shape",
223
+ grad_output.sizes().slice(n),
224
+ " must be equal to output size ",
225
+ output_size);
226
+ }
227
+ }
228
+
229
+ } // namespace at::native::internal
lib/python3.12/site-packages/torch/include/ATen/native/DispatchStub.h ADDED
@@ -0,0 +1,479 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <c10/core/DeviceType.h>
4
+ #include <c10/macros/Macros.h>
5
+ #include <c10/util/Array.h>
6
+
7
+ #include <atomic>
8
+ #include <utility>
9
+ #include <variant>
10
+
11
+ // Implements instruction set specific function dispatch.
12
+ //
13
+ // Kernels that may make use of specialized instruction sets (e.g. AVX2) are
14
+ // compiled multiple times with different compiler flags (e.g. -mavx2). A
15
+ // DispatchStub contains a table of function pointers for a kernel. At runtime,
16
+ // the fastest available kernel is chosen based on the features reported by
17
+ // cpuinfo.
18
+ //
19
+ // Example:
20
+ //
21
+ // In native/MyKernel.h:
22
+ // using fn_type = void(*)(const Tensor& x);
23
+ // DECLARE_DISPATCH(fn_type, stub)
24
+ //
25
+ // In native/MyKernel.cpp
26
+ // DEFINE_DISPATCH(stub);
27
+ //
28
+ // In native/cpu/MyKernel.cpp:
29
+ // namespace {
30
+ // // use anonymous namespace so that different cpu versions won't conflict
31
+ // void kernel(const Tensor& x) { ... }
32
+ // }
33
+ // REGISTER_DISPATCH(stub, &kernel);
34
+ //
35
+ // To call:
36
+ // stub(kCPU, tensor);
37
+ //
38
+ // TODO: CPU instruction set selection should be folded into whatever
39
+ // the main dispatch mechanism is.
40
+ //
41
+ // Supported device types for registration:
42
+ // - CPU: Central Processing Unit
43
+ // - CUDA: NVIDIA GPUs
44
+ // - HIP: AMD GPUs
45
+ // - MPS: Apple Silicon GPUs (Metal Performance Shaders)
46
+ // - MTIA: Meta Training and Inference Devices
47
+ // - XPU: Intel GPUs
48
+ // - PrivateUse1: Reserved for private/custom device types
49
+ //
50
+ // If you want to update the list of supported devices, add a new dispatch_ptr
51
+ // member in DispatchStubImpl.h and update the get_call_ptr switch.
52
+ // As well you will need to update the inlined list in 'is_device_supported`
53
+ //
54
+ //
55
+ // ignore warnings about DispatchStub::DEFAULT, AVX, AVX2 defined elsewhere
56
+ C10_CLANG_DIAGNOSTIC_PUSH()
57
+ C10_CLANG_DIAGNOSTIC_IGNORE("-Wundefined-var-template")
58
+
59
+ namespace at::native {
60
+
61
+ enum class CPUCapability {
62
+ DEFAULT = 0,
63
+ #if defined(HAVE_VSX_CPU_DEFINITION)
64
+ VSX = 1,
65
+ #elif defined(HAVE_ZVECTOR_CPU_DEFINITION)
66
+ ZVECTOR = 1,
67
+ #elif defined(HAVE_SVE_CPU_DEFINITION)
68
+ SVE256 = 1,
69
+ #else
70
+ AVX2 = 1,
71
+ AVX512 = 2,
72
+ #endif
73
+ NUM_OPTIONS
74
+ };
75
+
76
+ // Enum for error types
77
+ enum class ErrorType {
78
+ MissingDeviceKernel,
79
+ DeviceNotSupported
80
+ };
81
+
82
+ // Alias for the return type using std::variant
83
+ using DispatchResult = std::variant<void*, ErrorType>;
84
+
85
+ CPUCapability get_cpu_capability();
86
+
87
+ template <typename FnPtr, typename T>
88
+ struct DispatchStub;
89
+
90
+ /**
91
+ * The sole purpose of this class is to outline methods that don't need to be
92
+ * specialized or otherwise inlined and duplicated (by the compiler due to
93
+ * template expansion), since it causes size bloat if there are a significant
94
+ * number of specialization of the DispatchStub<> class.
95
+ */
96
+ struct TORCH_API DispatchStubImpl {
97
+
98
+ // The DispatchStubImpl::try_get_call_ptr() method is used to get the call
99
+ // pointer for a given device type. If the call pointer is not found,
100
+ // DispatchStubImpl::try_get_call_ptr() returns an ErrorType.
101
+ // The main difference between try_get_call_ptr() and get_call_ptr() is that
102
+ // try_get_call_ptr() will return the ErrorType and not raise an exception.
103
+ DispatchResult try_get_call_ptr(
104
+ c10::DeviceType device_type
105
+ , void *DEFAULT
106
+ #ifdef HAVE_AVX512_CPU_DEFINITION
107
+ , void *AVX512
108
+ #endif
109
+ #ifdef HAVE_AVX2_CPU_DEFINITION
110
+ , void *AVX2
111
+ #endif
112
+ #ifdef HAVE_VSX_CPU_DEFINITION
113
+ , void *VSX
114
+ #endif
115
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
116
+ , void *ZVECTOR
117
+ #endif
118
+ #ifdef HAVE_SVE256_CPU_DEFINITION
119
+ , void *SVE256
120
+ #endif
121
+ );
122
+
123
+ // Analogous to try_get_call_ptr(), but it will return the ErrorType and not
124
+ // raise an exception.
125
+ DispatchResult try_choose_cpu_impl(
126
+ void *DEFAULT
127
+ #ifdef HAVE_AVX512_CPU_DEFINITION
128
+ , void *AVX512
129
+ #endif
130
+ #ifdef HAVE_AVX2_CPU_DEFINITION
131
+ , void *AVX2
132
+ #endif
133
+ #ifdef HAVE_VSX_CPU_DEFINITION
134
+ , void *VSX
135
+ #endif
136
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
137
+ , void *ZVECTOR
138
+ #endif
139
+ #ifdef HAVE_SVE256_CPU_DEFINITION
140
+ , void *SVE256
141
+ #endif
142
+ );
143
+
144
+
145
+ void* get_call_ptr(
146
+ c10::DeviceType device_type
147
+ , void *DEFAULT
148
+ #ifdef HAVE_AVX512_CPU_DEFINITION
149
+ , void *AVX512
150
+ #endif
151
+ #ifdef HAVE_AVX2_CPU_DEFINITION
152
+ , void *AVX2
153
+ #endif
154
+ #ifdef HAVE_VSX_CPU_DEFINITION
155
+ , void *VSX
156
+ #endif
157
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
158
+ , void *ZVECTOR
159
+ #endif
160
+ #ifdef HAVE_SVE256_CPU_DEFINITION
161
+ , void *SVE256
162
+ #endif
163
+ );
164
+
165
+ /**
166
+ * The CPU Dispatch actual method is chosen in decreasing order of preference by
167
+ * DispatchStubImpl::choose_cpu_impl() in case none is found by
168
+ * DispatchStubImpl::get_call_ptr() in cpu_dispatch_ptr.
169
+ */
170
+ void* choose_cpu_impl(
171
+ void *DEFAULT
172
+ #ifdef HAVE_AVX512_CPU_DEFINITION
173
+ , void *AVX512
174
+ #endif
175
+ #ifdef HAVE_AVX2_CPU_DEFINITION
176
+ , void *AVX2
177
+ #endif
178
+ #ifdef HAVE_VSX_CPU_DEFINITION
179
+ , void *VSX
180
+ #endif
181
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
182
+ , void *ZVECTOR
183
+ #endif
184
+ #ifdef HAVE_SVE256_CPU_DEFINITION
185
+ , void *SVE256
186
+ #endif
187
+ );
188
+
189
+ // Fixing dispatch error in Windows debug builds.
190
+ // See https://github.com/pytorch/pytorch/issues/22681 for more details.
191
+ #if defined(_MSC_VER) && defined(_DEBUG)
192
+ std::atomic<void*> cpu_dispatch_ptr;
193
+ void* cuda_dispatch_ptr;
194
+ void* hip_dispatch_ptr;
195
+ void* mps_dispatch_ptr;
196
+ void* mtia_dispatch_ptr;
197
+ #if defined(USE_XPU)
198
+ void* xpu_dispatch_ptr;
199
+ #endif
200
+ void* privateuse1_dispatch_ptr;
201
+ #else
202
+ std::atomic<void*> cpu_dispatch_ptr{nullptr};
203
+ void* cuda_dispatch_ptr = nullptr;
204
+ void* hip_dispatch_ptr = nullptr;
205
+ void* mps_dispatch_ptr = nullptr;
206
+ void* mtia_dispatch_ptr = nullptr;
207
+ #if defined(USE_XPU)
208
+ void* xpu_dispatch_ptr = nullptr;
209
+ #endif
210
+ void* privateuse1_dispatch_ptr = nullptr;
211
+ #endif
212
+ };
213
+
214
+ template <typename rT, typename T, typename... Args>
215
+ struct DispatchStub<rT (*)(Args...), T> {
216
+ using FnPtr = rT (*) (Args...);
217
+
218
+ DispatchStub() = default;
219
+ DispatchStub(const DispatchStub&) = delete;
220
+ DispatchStub& operator=(const DispatchStub&) = delete;
221
+
222
+ private:
223
+ FnPtr get_call_ptr(const c10::DeviceType device_type) {
224
+ return reinterpret_cast<FnPtr>(
225
+ impl.get_call_ptr(device_type
226
+ , reinterpret_cast<void*>(DEFAULT)
227
+ #ifdef HAVE_AVX512_CPU_DEFINITION
228
+ , reinterpret_cast<void*>(AVX512)
229
+ #endif
230
+ #ifdef HAVE_AVX2_CPU_DEFINITION
231
+ , reinterpret_cast<void*>(AVX2)
232
+ #endif
233
+ #ifdef HAVE_VSX_CPU_DEFINITION
234
+ , reinterpret_cast<void*>(VSX)
235
+ #endif
236
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
237
+ , reinterpret_cast<void*>(ZVECTOR)
238
+ #endif
239
+ #ifdef HAVE_SVE256_CPU_DEFINITION
240
+ , reinterpret_cast<void*>(SVE256)
241
+ #endif
242
+ )
243
+ );
244
+ }
245
+
246
+ public:
247
+ template <typename... ArgTypes>
248
+ rT operator()(c10::DeviceType device_type, ArgTypes&&... args) {
249
+ FnPtr call_ptr = get_call_ptr(device_type);
250
+ return (*call_ptr)(std::forward<ArgTypes>(args)...);
251
+ }
252
+
253
+ void set_cuda_dispatch_ptr(FnPtr fn_ptr) {
254
+ impl.cuda_dispatch_ptr = reinterpret_cast<void*>(fn_ptr);
255
+ }
256
+
257
+ #if defined(USE_XPU)
258
+ void set_xpu_dispatch_ptr(FnPtr fn_ptr){
259
+ impl.xpu_dispatch_ptr = reinterpret_cast<void*>(fn_ptr);
260
+ }
261
+ #endif
262
+
263
+ void set_hip_dispatch_ptr(FnPtr fn_ptr) {
264
+ impl.hip_dispatch_ptr = reinterpret_cast<void*>(fn_ptr);
265
+ }
266
+
267
+ void set_mps_dispatch_ptr(FnPtr fn_ptr) {
268
+ impl.mps_dispatch_ptr = reinterpret_cast<void*>(fn_ptr);
269
+ }
270
+
271
+ void set_mtia_dispatch_ptr(FnPtr fn_ptr) {
272
+ impl.mtia_dispatch_ptr = reinterpret_cast<void*>(fn_ptr);
273
+ }
274
+
275
+ void set_privateuse1_dispatch_ptr(FnPtr fn_ptr) {
276
+ impl.privateuse1_dispatch_ptr = reinterpret_cast<void*>(fn_ptr);
277
+ }
278
+
279
+ // Returns true if the dispatcher has a kernel registered for this device
280
+ // type.
281
+ bool is_device_supported(const c10::DeviceType device_type) {
282
+ auto result = impl.try_get_call_ptr(device_type
283
+ , reinterpret_cast<void*>(DEFAULT)
284
+ #ifdef HAVE_AVX512_CPU_DEFINITION
285
+ , reinterpret_cast<void*>(AVX512)
286
+ #endif
287
+ #ifdef HAVE_AVX2_CPU_DEFINITION
288
+ , reinterpret_cast<void*>(AVX2)
289
+ #endif
290
+ #ifdef HAVE_VSX_CPU_DEFINITION
291
+ , reinterpret_cast<void*>(VSX)
292
+ #endif
293
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
294
+ , reinterpret_cast<void*>(ZVECTOR)
295
+ #endif
296
+ #ifdef HAVE_SVE256_CPU_DEFINITION
297
+ , reinterpret_cast<void*>(SVE256)
298
+ #endif
299
+ );
300
+ if (std::holds_alternative<ErrorType>(result)){
301
+ return false;
302
+ }
303
+ return true;
304
+ }
305
+
306
+ static TORCH_API FnPtr DEFAULT;
307
+ #ifdef HAVE_AVX512_CPU_DEFINITION
308
+ static TORCH_API FnPtr AVX512;
309
+ #endif
310
+ #ifdef HAVE_AVX2_CPU_DEFINITION
311
+ static TORCH_API FnPtr AVX2;
312
+ #endif
313
+ #ifdef HAVE_VSX_CPU_DEFINITION
314
+ static TORCH_API FnPtr VSX;
315
+ #endif
316
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
317
+ static TORCH_API FnPtr ZVECTOR;
318
+ #endif
319
+ #ifdef HAVE_SVE256_CPU_DEFINITION
320
+ static TORCH_API FnPtr SVE256;
321
+ #endif
322
+ private:
323
+ DispatchStubImpl impl;
324
+ };
325
+
326
+ namespace {
327
+ template <typename DispatchStub>
328
+ struct RegisterCUDADispatch {
329
+ RegisterCUDADispatch(DispatchStub &stub, typename DispatchStub::FnPtr value) {
330
+ stub.set_cuda_dispatch_ptr(value);
331
+ }
332
+ };
333
+
334
+ template <typename DispatchStub>
335
+ struct RegisterXPUDispatch {
336
+ RegisterXPUDispatch(DispatchStub &stub, typename DispatchStub::FnPtr value){
337
+ stub.set_xpu_dispatch_ptr(value);
338
+ }
339
+ };
340
+
341
+ template <typename DispatchStub>
342
+ struct RegisterMPSDispatch {
343
+ RegisterMPSDispatch(DispatchStub &stub, typename DispatchStub::FnPtr value) {
344
+ stub.set_mps_dispatch_ptr(value);
345
+ }
346
+ };
347
+
348
+ template <typename DispatchStub>
349
+ struct RegisterHIPDispatch {
350
+ RegisterHIPDispatch(DispatchStub &stub, typename DispatchStub::FnPtr value) {
351
+ // TODO: make this point at hip_dispatch_ptr
352
+ stub.set_cuda_dispatch_ptr(value);
353
+ }
354
+ };
355
+
356
+ template <typename DispatchStub>
357
+ struct RegisterMTIADispatch {
358
+ RegisterMTIADispatch(DispatchStub &stub, typename DispatchStub::FnPtr value) {
359
+ stub.set_mtia_dispatch_ptr(value);
360
+ }
361
+ };
362
+
363
+ template <typename DispatchStub>
364
+ struct RegisterPRIVATEUSE1Dispatch {
365
+ RegisterPRIVATEUSE1Dispatch(DispatchStub &stub, typename DispatchStub::FnPtr value) {
366
+ stub.set_privateuse1_dispatch_ptr(value);
367
+ }
368
+ };
369
+
370
+ } // anonymous namespace
371
+ // Compiler will complain if you put things like std::tuple<Tensor, Tensor> in
372
+ // the `fn` argument of DECLARE_DISPATCH. Some possible workarounds, e.g.,
373
+ // adding parentheses and using helper struct to get rid of the parentheses, do
374
+ // not work with MSVC. So do a `using`-declaration if you need to pass in such
375
+ // `fn`, e.g., grid_sampler_2d_backward_cpu_kernel in GridSampleKernel.h.
376
+ #define DECLARE_DISPATCH(fn, name) \
377
+ struct name##_DECLARE_DISPATCH_type : DispatchStub<fn, name##_DECLARE_DISPATCH_type> { \
378
+ name##_DECLARE_DISPATCH_type() = default; \
379
+ name##_DECLARE_DISPATCH_type(const name##_DECLARE_DISPATCH_type&) = delete; \
380
+ name##_DECLARE_DISPATCH_type& operator=(const name##_DECLARE_DISPATCH_type&) = delete; \
381
+ name##_DECLARE_DISPATCH_type(name##_DECLARE_DISPATCH_type&&) = delete; \
382
+ name##_DECLARE_DISPATCH_type& operator=(name##_DECLARE_DISPATCH_type&&) = delete; \
383
+ ~name##_DECLARE_DISPATCH_type() = default; \
384
+ }; \
385
+ extern TORCH_API struct name##_DECLARE_DISPATCH_type name;
386
+
387
+ #define DEFINE_DISPATCH(name) struct name##_DECLARE_DISPATCH_type name
388
+
389
+ #define REGISTER_ARCH_DISPATCH(name, arch, fn) \
390
+ template <> name##_DECLARE_DISPATCH_type::FnPtr TORCH_API DispatchStub<name##_DECLARE_DISPATCH_type::FnPtr, struct name##_DECLARE_DISPATCH_type>::arch = fn;
391
+
392
+ #ifdef HAVE_AVX512_CPU_DEFINITION
393
+ #define REGISTER_AVX512_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, AVX512, fn)
394
+ #else
395
+ #define REGISTER_AVX512_DISPATCH(name, fn)
396
+ #endif
397
+
398
+ #ifdef HAVE_AVX2_CPU_DEFINITION
399
+ #define REGISTER_AVX2_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, AVX2, fn)
400
+ #else
401
+ #define REGISTER_AVX2_DISPATCH(name, fn)
402
+ #endif
403
+
404
+ #ifdef HAVE_VSX_CPU_DEFINITION
405
+ #define REGISTER_VSX_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, VSX, fn)
406
+ #else
407
+ #define REGISTER_VSX_DISPATCH(name, fn)
408
+ #endif
409
+
410
+ #ifdef HAVE_ZVECTOR_CPU_DEFINITION
411
+ #define REGISTER_ZVECTOR_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, ZVECTOR, fn)
412
+ #else
413
+ #define REGISTER_ZVECTOR_DISPATCH(name, fn)
414
+ #endif
415
+
416
+ #ifdef HAVE_SVE256_CPU_DEFINITION
417
+ #define REGISTER_SVE256_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, SVE256, fn)
418
+ #else
419
+ #define REGISTER_SVE256_DISPATCH(name, fn)
420
+ #endif
421
+
422
+ // Macro to register the same kernel for all CPU arch types. This is useful
423
+ // if a kernel does not benefit from being recompiled across different arch types.
424
+ #define REGISTER_ALL_CPU_DISPATCH(name, fn) \
425
+ REGISTER_ARCH_DISPATCH(name, DEFAULT, fn) \
426
+ REGISTER_AVX512_DISPATCH(name, fn) \
427
+ REGISTER_AVX2_DISPATCH(name, fn) \
428
+ REGISTER_VSX_DISPATCH(name, fn) \
429
+ REGISTER_ZVECTOR_DISPATCH(name, fn) \
430
+ REGISTER_SVE256_DISPATCH(name, fn)
431
+
432
+ #define REGISTER_NO_CPU_DISPATCH(name) \
433
+ REGISTER_ALL_CPU_DISPATCH(name, nullptr)
434
+
435
+ #define REGISTER_CUDA_DISPATCH(name, fn) \
436
+ static RegisterCUDADispatch<struct name##_DECLARE_DISPATCH_type> name ## __register(name, fn);
437
+
438
+ #define REGISTER_XPU_DISPATCH(name, fn) \
439
+ static RegisterXPUDispatch<struct name##_DECLARE_DISPATCH_type> name ## __register(name, fn);
440
+
441
+ #define REGISTER_HIP_DISPATCH(name, fn) \
442
+ static RegisterHIPDispatch<struct name##_DECLARE_DISPATCH_type> name ## __register(name, fn);
443
+
444
+ #define REGISTER_MPS_DISPATCH(name, fn) \
445
+ static RegisterMPSDispatch<struct name##_DECLARE_DISPATCH_type> name ## __register(name, fn);
446
+
447
+ #define REGISTER_MTIA_DISPATCH(name, fn) \
448
+ static RegisterMTIADispatch<struct name##_DECLARE_DISPATCH_type> name ## __register(name, fn);
449
+
450
+ #define REGISTER_PRIVATEUSE1_DISPATCH(name, fn) \
451
+ static RegisterPRIVATEUSE1Dispatch<struct name##_DECLARE_DISPATCH_type> name ## __register(name, fn);
452
+
453
+ // NB: This macro must be used in an actual 'cu' file; if you try using
454
+ // it from a 'cpp' file it will not work!
455
+ #if defined(__CUDACC__)
456
+ #define REGISTER_DISPATCH(name, fn) REGISTER_CUDA_DISPATCH(name, fn)
457
+ #elif defined(__HIPCC__)
458
+ // TODO: cut this over to HIP dispatch once we stop pretending that CUDA
459
+ // is HIP in the PyTorch HIPify build.
460
+ #define REGISTER_DISPATCH(name, fn) REGISTER_CUDA_DISPATCH(name, fn)
461
+ // #define REGISTER_DISPATCH(name, fn) REGISTER_HIP_DISPATCH(name, fn)
462
+ #elif defined(__OBJC__) && defined(USE_MPS)
463
+ // NB: this macro must be used from a 'mm' file in order to dispatch a MPS kernel
464
+ #define REGISTER_DISPATCH(name, fn) REGISTER_MPS_DISPATCH(name, fn)
465
+ #elif defined(CPU_CAPABILITY)
466
+ // REGISTER_DISPATCH now dispatches an AVX512 kernel to nullptr but registers other dispatches.
467
+ // ALSO_REGISTER_AVX512_DISPATCH should be used for ensuring AVX512 dispatch, among others.
468
+ // ALSO_REGISTER_SVE256_DISPATCH should be used for ensuring SVE256 dispatch, among others.
469
+ #ifdef CPU_CAPABILITY_AVX512
470
+ #define REGISTER_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, CPU_CAPABILITY, ((void*)(fn) ? nullptr : nullptr))
471
+ #else
472
+ #define REGISTER_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, CPU_CAPABILITY, fn)
473
+ #endif
474
+ #define ALSO_REGISTER_AVX512_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, CPU_CAPABILITY, fn)
475
+ #define ALSO_REGISTER_SVE256_DISPATCH(name, fn) REGISTER_ARCH_DISPATCH(name, CPU_CAPABILITY, fn)
476
+ #endif
477
+ } // namespace at::native
478
+
479
+ C10_CLANG_DIAGNOSTIC_POP()
lib/python3.12/site-packages/torch/include/ATen/native/Distance.h ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+
5
+ namespace at {
6
+ class Tensor;
7
+
8
+ namespace native {
9
+
10
+ using pdist_forward_fn = void(*)(Tensor&, const Tensor&, const double p);
11
+ using pdist_backward_fn = void(*)(Tensor&, const Tensor&, const Tensor&, const double p, const Tensor&);
12
+ using cdist_fn = void(*)(Tensor&, const Tensor&, const Tensor&, const double p);
13
+ using cdist_backward_fn = void(*)(Tensor&, const Tensor&, const Tensor&, const Tensor&, const double p, const Tensor&);
14
+
15
+ DECLARE_DISPATCH(pdist_forward_fn, pdist_forward_stub)
16
+ DECLARE_DISPATCH(pdist_backward_fn, pdist_backward_stub)
17
+ DECLARE_DISPATCH(cdist_fn, cdist_stub)
18
+ DECLARE_DISPATCH(cdist_backward_fn, cdist_backward_stub)
19
+
20
+ }} // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/Distributions.h ADDED
@@ -0,0 +1,518 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/Math.h>
4
+ #include <c10/macros/Macros.h>
5
+ #include <c10/util/MathConstants.h>
6
+
7
+ // ROCM hcc doesn't work well with using std:: in kernel functions
8
+ #if defined(__CUDA_ARCH__)
9
+ #include <c10/cuda/CUDAMathCompat.h>
10
+ #define compat_exp c10::cuda::compat::exp
11
+ #define compat_ceil c10::cuda::compat::ceil
12
+ #define compat_floor c10::cuda::compat::floor
13
+ #define compat_log c10::cuda::compat::log
14
+ #define compat_pow c10::cuda::compat::pow
15
+ #define compat_sqrt c10::cuda::compat::sqrt
16
+ #define compat_tan c10::cuda::compat::tan
17
+ #define compat_abs c10::cuda::compat::abs
18
+ #define compat_log1p c10::cuda::compat::log1p
19
+ #elif defined(__HIPCC__)
20
+ #include <c10/hip/HIPMathCompat.h>
21
+ #define compat_exp c10::hip::compat::exp
22
+ #define compat_ceil c10::hip::compat::ceil
23
+ #define compat_floor c10::hip::compat::floor
24
+ #define compat_log c10::hip::compat::log
25
+ #define compat_pow c10::hip::compat::pow
26
+ #define compat_sqrt c10::hip::compat::sqrt
27
+ #define compat_tan c10::hip::compat::tan
28
+ #define compat_abs c10::hip::compat::abs
29
+ #define compat_log1p c10::hip::compat::log1p
30
+ #else
31
+ #define compat_exp std::exp
32
+ #define compat_ceil std::ceil
33
+ #define compat_floor std::floor
34
+ #define compat_log std::log
35
+ #define compat_pow std::pow
36
+ #define compat_sqrt std::sqrt
37
+ #define compat_tan std::tan
38
+ #define compat_abs std::abs
39
+ #define compat_log1p std::log1p
40
+ #endif
41
+
42
+ namespace {
43
+
44
+ #if !defined(__CUDA_ARCH__) && !defined(__HIPCC__)
45
+ // we cannot use std::isnan directly due to some incompatibility of
46
+ // gcc constexpr'ing and nvcc
47
+ using std::isnan;
48
+ #endif
49
+
50
+ // Here sampler_t should be function type scalar_t(void). For gpu
51
+ // "sampler" is a device function, but since ROCM doesn't have
52
+ // equivalent to nvstd::function, we use a template type parameter to
53
+ // capture it.
54
+ template<typename scalar_t, typename sampler_t>
55
+ struct BaseSampler {
56
+ sampler_t sampler;
57
+ C10_DEVICE BaseSampler(const sampler_t& sampler): sampler(sampler) {}
58
+ C10_DEVICE scalar_t sample() {
59
+ return sampler();
60
+ }
61
+ };
62
+
63
+ // The function `sample_gamma` is
64
+ // is adapted from Numpy's distributions.c implementation.
65
+ // It is MIT licensed, so here is the copyright:
66
+
67
+ /* Copyright 2005 Robert Kern (robert.kern@gmail.com)
68
+ *
69
+ * Permission is hereby granted, free of charge, to any person obtaining a
70
+ * copy of this software and associated documentation files (the
71
+ * "Software"), to deal in the Software without restriction, including
72
+ * without limitation the rights to use, copy, modify, merge, publish,
73
+ * distribute, sublicense, and/or sell copies of the Software, and to
74
+ * permit persons to whom the Software is furnished to do so, subject to
75
+ * the following conditions:
76
+ *
77
+ * The above copyright notice and this permission notice shall be included
78
+ * in all copies or substantial portions of the Software.
79
+ *
80
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
81
+ * OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
82
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
83
+ * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
84
+ * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
85
+ * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
86
+ * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
87
+ */
88
+
89
+ template<typename scalar_t, typename accscalar_t, typename uniform_sampler_t, typename normal_sampler_t>
90
+ C10_DEVICE scalar_t sample_gamma(scalar_t alpha, BaseSampler<accscalar_t, uniform_sampler_t>& standard_uniform, BaseSampler<accscalar_t, normal_sampler_t>& standard_normal) {
91
+ accscalar_t scale = 1.0f;
92
+
93
+ // Boost alpha for higher acceptance probability.
94
+ if (alpha < 1.0f) {
95
+ if (alpha == 0.f) return 0.f;
96
+ scale *= compat_pow(1 - standard_uniform.sample(), 1.0f / alpha);
97
+ alpha += 1.0f;
98
+ }
99
+
100
+ // This implements the acceptance-rejection method of Marsaglia and Tsang (2000)
101
+ // doi:10.1145/358407.358414
102
+ const accscalar_t d = alpha - 1.0f / 3.0f;
103
+ const accscalar_t c = 1.0f / compat_sqrt(9.0f * d);
104
+ for (;;) {
105
+ accscalar_t x, y;
106
+ do {
107
+ x = standard_normal.sample();
108
+ y = 1.0f + c * x;
109
+ } while (y <= 0);
110
+ const accscalar_t v = y * y * y;
111
+ const accscalar_t u = 1 - standard_uniform.sample();
112
+ const accscalar_t xx = x * x;
113
+ if (u < 1.0f - 0.0331f * xx * xx)
114
+ return static_cast<scalar_t>(scale * d * v);
115
+ if (compat_log(u) < 0.5f * xx + d * (1.0f - v + compat_log(v)))
116
+ return static_cast<scalar_t>(scale * d * v);
117
+ }
118
+ }
119
+
120
+ /* the functions stirling_approx_tail, binomial_inversion, and btrs are adapted
121
+ * from TensorFlow's random_binomial_op.cc implementation. That code is under
122
+ * copyright: 2019 The TensorFlow Authors.
123
+ *
124
+ * It was released under the Apache License, Version 2.0 (the "License"), available at:
125
+ * http://www.apache.org/licenses/LICENSE-2.0
126
+ */
127
+
128
+ template<typename scalar_t>
129
+ C10_DEVICE scalar_t stirling_approx_tail(scalar_t k) {
130
+ const static scalar_t kTailValues[] = {
131
+ 0.0810614667953272,
132
+ 0.0413406959554092,
133
+ 0.0276779256849983,
134
+ 0.02079067210376509,
135
+ 0.0166446911898211,
136
+ 0.0138761288230707,
137
+ 0.0118967099458917,
138
+ 0.0104112652619720,
139
+ 0.00925546218271273,
140
+ 0.00833056343336287
141
+ };
142
+ if (k <= 9) {
143
+ return kTailValues[static_cast<size_t>(k)];
144
+ }
145
+ scalar_t kp1sq = (k + 1) * (k + 1);
146
+ return (1.0 / 12 - (1.0 / 360 - 1.0 / 1260 / kp1sq) / kp1sq) / (k + 1);
147
+ }
148
+
149
+
150
+ template<typename scalar_t, typename accscalar_t, typename uniform_sampler_t>
151
+ C10_DEVICE scalar_t binomial_inversion(scalar_t count, scalar_t prob, BaseSampler<accscalar_t, uniform_sampler_t>& standard_uniform) {
152
+ accscalar_t U;
153
+ accscalar_t geom_sum = 0;
154
+ scalar_t num_geom = 0;
155
+
156
+ accscalar_t logprob = compat_log1p(-prob);
157
+
158
+ while (true) {
159
+ U = standard_uniform.sample();
160
+ accscalar_t geom = compat_ceil(compat_log(U) / logprob);
161
+ geom_sum += geom;
162
+ if (geom_sum > count) {
163
+ break;
164
+ }
165
+ num_geom = num_geom + 1;
166
+ }
167
+ return num_geom;
168
+ }
169
+
170
+ template<typename scalar_t, typename accscalar_t, typename uniform_sampler_t>
171
+ C10_DEVICE scalar_t btrs(scalar_t count, scalar_t prob, BaseSampler<accscalar_t, uniform_sampler_t>& standard_uniform) {
172
+ scalar_t k;
173
+ accscalar_t U, V, us;
174
+
175
+ // This is spq in the paper.
176
+ const accscalar_t stddev = compat_sqrt(count * prob * (1 - prob));
177
+
178
+ // Other coefficients for Transformed Rejection sampling.
179
+ const accscalar_t b = 1.15 + 2.53 * stddev;
180
+ const accscalar_t a = -0.0873 + 0.0248 * b + 0.01 * prob;
181
+ const accscalar_t c = count * prob + 0.5;
182
+ const accscalar_t v_r = 0.92 - 4.2 / b;
183
+ const accscalar_t r = prob / (1 - prob);
184
+
185
+ const accscalar_t alpha = (2.83 + 5.1 / b) * stddev;
186
+ const accscalar_t m = compat_floor((count + 1) * prob);
187
+
188
+ while (true) {
189
+ U = standard_uniform.sample() - 0.5;
190
+ V = standard_uniform.sample();
191
+
192
+ us = 0.5 - compat_abs(U);
193
+ k = static_cast<scalar_t>(compat_floor((2 * a / us + b) * U + c));
194
+
195
+ // Reject non-sensical answers.
196
+ if (k < 0 || k > count) {
197
+ continue;
198
+ }
199
+ // Region for which the box is tight, and we can return our calculated value.
200
+ // This should happen 0.86 * v_r times. In the limit as n * p is large,
201
+ // the acceptance rate converges to ~79% (and in the lower regime it is ~24%).
202
+ if (us >= 0.07 && V <= v_r) {
203
+ return k;
204
+ }
205
+
206
+ // This deviates from Hormann's BTRS algorithm, as there is a log missing.
207
+ // For all (u, v) pairs outside of the bounding box, this calculates the
208
+ // transformed-reject ratio.
209
+ V = compat_log(V * alpha / (a / (us * us) + b));
210
+ accscalar_t upperbound =
211
+ ((m + 0.5) * compat_log((m + 1) / (r * (count - m + 1))) +
212
+ (count + 1) * compat_log((count - m + 1) / (count - k + 1)) +
213
+ (k + 0.5) * compat_log(r * (count - k + 1) / (k + 1)) +
214
+ stirling_approx_tail<accscalar_t>(m) + stirling_approx_tail<accscalar_t>(count - m) -
215
+ stirling_approx_tail<accscalar_t>(k) - stirling_approx_tail<accscalar_t>(count - k));
216
+
217
+ if (V <= upperbound) {
218
+ return k;
219
+ }
220
+ }
221
+ }
222
+
223
+ template<typename scalar_t, typename accscalar_t, typename uniform_sampler_t>
224
+ C10_DEVICE scalar_t sample_binomial(scalar_t count, scalar_t prob, BaseSampler<accscalar_t, uniform_sampler_t>& standard_uniform) {
225
+ if (count <= 0.0 || prob <= 0.0) {
226
+ return 0;
227
+ } else if (prob >= 1.0) {
228
+ return count;
229
+ } else if (prob <= 0.5) {
230
+ if (count * prob >= 10.0) {
231
+ // btrs
232
+ return btrs<scalar_t, accscalar_t, uniform_sampler_t>(count, prob, standard_uniform);
233
+ } else {
234
+ // binomial inversion
235
+ return binomial_inversion<scalar_t, accscalar_t, uniform_sampler_t>(count, prob, standard_uniform);
236
+ }
237
+ } else if (prob > 0.5) {
238
+ scalar_t qprob = 1.0 - prob;
239
+ if (count * qprob >= 10.0) {
240
+ // btrs
241
+ return count - btrs<scalar_t, accscalar_t, uniform_sampler_t>(count, qprob, standard_uniform);
242
+ } else {
243
+ // count - binomial inversion
244
+ return count - binomial_inversion<scalar_t, accscalar_t, uniform_sampler_t>(count, qprob, standard_uniform);
245
+ }
246
+ } else {
247
+ // prob is nan?
248
+ return static_cast<scalar_t>(NAN);
249
+ }
250
+ }
251
+
252
+ /*
253
+ * This function is derived from the implementation of the digamma function in the Cephes Math Library.
254
+ * See note [3-Clause BSD License for the Cephes Math Library] in ATen/native/Math.h.
255
+ */
256
+ template<typename scalar_t, typename accscalar_t>
257
+ C10_DEVICE inline scalar_t digamma_one(scalar_t x) {
258
+ constexpr accscalar_t PSI_10 = 2.25175258906672110764;
259
+ if (x == 0) {
260
+ return INFINITY;
261
+ }
262
+ accscalar_t additional_summand = 0;
263
+ int x_is_integer = x == compat_floor(x);
264
+ if (x < 0) {
265
+ if (x_is_integer) {
266
+ return INFINITY;
267
+ }
268
+ // it is more standard to write this as recursion, but
269
+ // nvcc does not like that
270
+ additional_summand = -c10::pi<scalar_t> /
271
+ compat_tan(c10::pi<scalar_t> * x);
272
+ x = 1 - x;
273
+ }
274
+
275
+ // Push x to be >= 10
276
+ accscalar_t result = 0;
277
+ while (x < 10) {
278
+ result -= 1 / x;
279
+ x += 1;
280
+ }
281
+ if (x == 10) {
282
+ return result + PSI_10 + additional_summand;
283
+ }
284
+
285
+ // Compute asymptotic digamma
286
+ static const accscalar_t A[] = {
287
+ 8.33333333333333333333E-2,
288
+ -2.10927960927960927961E-2,
289
+ 7.57575757575757575758E-3,
290
+ -4.16666666666666666667E-3,
291
+ 3.96825396825396825397E-3,
292
+ -8.33333333333333333333E-3,
293
+ 8.33333333333333333333E-2,
294
+ };
295
+
296
+ accscalar_t y = 0;
297
+ if (x < 1.0e17f) {
298
+ accscalar_t z = 1.0 / (x * x);
299
+ y = z * polevl<accscalar_t>(z, A, 6);
300
+ }
301
+ return static_cast<scalar_t>(
302
+ result + compat_log(x) - (0.5f / x) - y + additional_summand);
303
+ }
304
+
305
+ // Computes the reparameterized gradient -(d/dalpha cdf(x;alpha)) / pdf(x;alpha)
306
+ // for random number x drawn from a standard Gamma distribution Gamma(alpha).
307
+ template <typename scalar_t, typename accscalar_t>
308
+ C10_HOST_DEVICE scalar_t standard_gamma_grad_one(scalar_t alpha_, scalar_t x_) {
309
+ // Use a Taylor series expansion for small x.
310
+ accscalar_t x = static_cast<accscalar_t>(x_);
311
+ accscalar_t alpha = static_cast<accscalar_t>(alpha_);
312
+ if (x < 0.8f) {
313
+ accscalar_t numer = 1;
314
+ accscalar_t denom = alpha;
315
+ auto series1 = numer / denom;
316
+ auto series2 = numer / (denom * denom);
317
+ for (int i = 1; i <= 5; ++i) {
318
+ numer *= -x / static_cast<accscalar_t>(i);
319
+ denom += 1;
320
+ series1 += numer / denom;
321
+ series2 += numer / (denom * denom);
322
+ }
323
+ const auto pow_x_alpha = compat_pow(x, alpha);
324
+ const auto gamma_pdf = compat_pow(x, alpha - 1) * compat_exp(-x);
325
+ const auto gamma_cdf = pow_x_alpha * series1;
326
+ const auto gamma_cdf_alpha =
327
+ (compat_log(x) - digamma_one<accscalar_t, accscalar_t>(alpha)) *
328
+ gamma_cdf -
329
+ pow_x_alpha * series2;
330
+ const auto result = -gamma_cdf_alpha / gamma_pdf;
331
+ return isnan(result) ? static_cast<scalar_t>( 0.f ) : static_cast<scalar_t>(result);
332
+ }
333
+
334
+ // Use a Rice saddle point expansion for large alpha.
335
+ if (alpha > 8.0f) {
336
+ if (0.9f * alpha <= x && x <= 1.1f * alpha) {
337
+ const auto numer_1 = 1 + 24 * alpha * (1 + 12 * alpha);
338
+ const auto numer_2 = 1440 * (alpha * alpha) + 6 * x * (53 - 120 * x)
339
+ - 65 * x * x / alpha + alpha * (107 + 3600 * x);
340
+ const auto denom = 1244160 * (alpha * alpha) * (alpha * alpha);
341
+ return static_cast<scalar_t>(numer_1 * numer_2 / denom);
342
+ }
343
+ const auto denom = compat_sqrt(8 * alpha);
344
+ const auto term2 = denom / (alpha - x);
345
+ const auto term3 = compat_pow(
346
+ x - alpha - alpha * compat_log(x / alpha),
347
+ static_cast<accscalar_t>(-1.5));
348
+ const auto term23 = (x < alpha) ? term2 - term3 : term2 + term3;
349
+ const auto term1 = compat_log(x / alpha) * term23 -
350
+ compat_sqrt(2 / alpha) * (alpha + x) / ((alpha - x) * (alpha - x));
351
+ const auto stirling = 1 + 1 / (12 * alpha) * (1 + 1 / (24 * alpha));
352
+ const auto numer = x * term1;
353
+ return static_cast<scalar_t>(-stirling * numer / denom);
354
+ }
355
+
356
+ // Use a bivariate rational approximation to the reparameterized gradient.
357
+ const auto u = compat_log(x / alpha);
358
+ const auto v = compat_log(alpha);
359
+ static const accscalar_t coef_uv[3][8] = {
360
+ {0.16009398, -0.094634809, 0.025146376, -0.0030648343,
361
+ 1, 0.32668115, 0.10406089, 0.0014179084},
362
+ {0.53487893, 0.1298071, 0.065735949, -0.0015649758,
363
+ 0.16639465, 0.020070113, -0.0035938915, -0.00058392623},
364
+ {0.040121004, -0.0065914022, -0.0026286047, -0.0013441777,
365
+ 0.017050642, -0.0021309326, 0.00085092367, -1.5247877e-07},
366
+ };
367
+ accscalar_t coef_v[8];
368
+ for (int i = 0; i < 8; ++ i) {
369
+ coef_v[i] = coef_uv[0][i] + u * (coef_uv[1][i] + u * coef_uv[2][i]);
370
+ }
371
+ const auto p = coef_v[0] + v * (coef_v[1] + v * (coef_v[2] + v * coef_v[3]));
372
+ const auto q = coef_v[4] + v * (coef_v[5] + v * (coef_v[6] + v * coef_v[7]));
373
+ return static_cast<scalar_t>(compat_exp(p / q));
374
+ }
375
+
376
+ // Approximate reparameterized gradient of Beta(x,alpha,beta) wrt alpha.
377
+ // Assumes x is close to zero and uses a Taylor expansion.
378
+ template <typename scalar_t, typename accscalar_t>
379
+ C10_DEVICE inline scalar_t _beta_grad_alpha_small(scalar_t x, scalar_t alpha, scalar_t beta) {
380
+ const scalar_t factor = digamma_one<scalar_t, accscalar_t>(alpha)
381
+ - digamma_one<scalar_t, accscalar_t>(alpha + beta) - compat_log(x);
382
+ scalar_t numer = 1;
383
+ scalar_t series = numer / alpha * (factor + 1 / alpha);
384
+ for (int i = 1; i <= 10; ++i) {
385
+ scalar_t casted_i = static_cast<scalar_t>(i);
386
+ numer *= (casted_i - beta) * x / casted_i;
387
+ const scalar_t denom = alpha + casted_i;
388
+ series += numer / denom * (factor + 1 / denom);
389
+ }
390
+ const scalar_t result = x * compat_pow(1 - x, -beta) * series;
391
+ return isnan(result) ? static_cast<scalar_t>( 0.f ) : result;
392
+ }
393
+
394
+ // Approximate reparameterized gradient of Beta(x,alpha,beta) wrt beta.
395
+ // Assumes x is close to zero and uses a Taylor expansion.
396
+ template <typename scalar_t, typename accscalar_t>
397
+ C10_DEVICE inline scalar_t _beta_grad_beta_small(scalar_t x, scalar_t alpha, scalar_t beta) {
398
+ const scalar_t factor = digamma_one<scalar_t, accscalar_t>(alpha + beta) - digamma_one<scalar_t, accscalar_t>(beta);
399
+ scalar_t numer = 1, betas = 1, dbetas = 0, series = factor / alpha;
400
+ for (int i = 1; i <= 8; ++i) {
401
+ scalar_t casted_i = static_cast<scalar_t>(i);
402
+ numer *= -x / casted_i;
403
+ dbetas = dbetas * (beta - casted_i) + betas;
404
+ betas = betas * (beta - casted_i);
405
+ series += numer / (alpha + casted_i) * (dbetas + factor * betas);
406
+ }
407
+ const scalar_t result = -compat_pow(1 - x, 1 - beta) * series;
408
+ return isnan(result) ? static_cast<scalar_t>( 0.f ) : result;
409
+ }
410
+
411
+ // Approximate reparameterized gradient of Beta(x,alpha,beta) wrt alpha.
412
+ // Assumes alpha and beta are both large and uses a Rice saddle point expansion.
413
+ // To ensure numerical stability, this computation is performed at higher precision.
414
+ template<typename scalar_t, typename accscalar_t>
415
+ C10_DEVICE inline scalar_t _beta_grad_alpha_mid(accscalar_t x, accscalar_t alpha, accscalar_t beta) {
416
+ const accscalar_t total = alpha + beta;
417
+ const accscalar_t mean = alpha / total;
418
+ const accscalar_t std = compat_sqrt(alpha * beta / (total + 1)) / total;
419
+ if (mean - 0.1 * std <= x && x <= mean + 0.1 * std) {
420
+ // Avoid the singularity at x = mean.
421
+ const accscalar_t poly = 47 * x * (beta * beta) * (beta * beta) + alpha * (
422
+ (43 + 20 * (16 + 27 * beta) * x) * (beta * beta) * beta + alpha * (
423
+ 3 * (59 + 180 * beta - 90 * x) * (beta * beta) + alpha * (
424
+ (453 + 1620 * beta * (1 - x) - 455 * x) * beta + alpha * (
425
+ 8 * (1 - x) * (135 * beta - 11)))));
426
+ const accscalar_t prefactor_num = (1 + 12 * alpha) * (1 + 12 * beta) / (total * total);
427
+ const accscalar_t prefactor_den = 12960 * alpha * alpha * alpha * beta * beta * (1 + 12 * total);
428
+ return prefactor_num / (1 - x) * poly / prefactor_den;
429
+ }
430
+ const accscalar_t prefactor = -x / compat_sqrt(2 * alpha * beta / total);
431
+ const accscalar_t stirling = (1 + 1 / (12 * alpha) + 1 / (288 * alpha * alpha))
432
+ * (1 + 1 / (12 * beta) + 1 / (288 * beta * beta))
433
+ / (1 + 1 / (12 * total) + 1 / (288 * total * total));
434
+ const accscalar_t term1_num = 2 * (alpha * alpha) * (x - 1) + alpha * beta * (x - 1) - x * (beta * beta);
435
+ const accscalar_t axbx = alpha * (x - 1) + beta * x;
436
+ const accscalar_t term1_den = compat_sqrt(2 * alpha / beta) * compat_pow(total, static_cast<accscalar_t>(1.5f)) * axbx * axbx;
437
+ const accscalar_t term1 = term1_num / term1_den;
438
+ const accscalar_t term2 = 0.5f * compat_log(alpha / (total * x));
439
+ const accscalar_t term3_num = compat_sqrt(8 * alpha * beta / total);
440
+ const accscalar_t term3_den = beta * x + alpha * (x - 1);
441
+ const accscalar_t term3 = term3_num / term3_den;
442
+ const accscalar_t term4_base = beta * compat_log(beta / (total * (1 - x))) +
443
+ alpha * compat_log(alpha / (total * x));
444
+ const accscalar_t term4 = compat_pow(term4_base, static_cast<accscalar_t>(-1.5f));
445
+ const accscalar_t term1234 = term1 + term2 * (term3 + (x < mean ? term4 : -term4));
446
+ return static_cast<scalar_t>(stirling * prefactor * term1234);
447
+ }
448
+
449
+ // Computes a scaled reparameterized gradient
450
+ // -(d/dalpha cdf(x;alpha,beta)) / pdf(x;alpha,beta) / (1-x)
451
+ // for random number x drawn from a Beta distribution Beta(alpha,beta).
452
+ // This function inputs total=alpha+beta to make it easy to implement
453
+ // Dirichlet reparameterized gradients in terms of Betas.
454
+ template<typename scalar_t, typename accscalar_t>
455
+ C10_HOST_DEVICE inline scalar_t dirichlet_grad_one(scalar_t x, scalar_t alpha, scalar_t total) {
456
+ accscalar_t x_ = static_cast<accscalar_t>(x);
457
+ accscalar_t alpha_ = static_cast<accscalar_t>(alpha);
458
+ accscalar_t total_ = static_cast<accscalar_t>(total);
459
+
460
+ const scalar_t beta = total - alpha;
461
+ const accscalar_t beta_ = total_ - alpha_;
462
+ const scalar_t boundary = total * x * (1 - x);
463
+
464
+ // Use an asymptotic approximation for x close to 0.
465
+ if (x <= 0.5f && boundary < 2.5f) {
466
+ return _beta_grad_alpha_small<scalar_t, accscalar_t>(x, alpha, beta);
467
+ }
468
+
469
+ // Use an asymptotic approximation for x close to 1.
470
+ if (x >= 0.5f && boundary < 0.75f) {
471
+ return -_beta_grad_beta_small<scalar_t, accscalar_t>(1 - x, beta, alpha);
472
+ }
473
+
474
+ // Use an asymptotic approximation when alpha and (total - alpha) are both large.
475
+ if (alpha > 6 && beta > 6) {
476
+ return _beta_grad_alpha_mid<scalar_t, accscalar_t>(x_, alpha_, beta_);
477
+ }
478
+
479
+ // Use a rational correction to an analytic approximation.
480
+ static const accscalar_t c[2][3][3][4] = {
481
+ {{{1.003668233, -0.01061107488, -0.0657888334, 0.01201642863},
482
+ {0.6336835991, -0.3557432599, 0.05486251648, -0.001465281033},
483
+ {-0.03276231906, 0.004474107445, 0.002429354597, -0.0001557569013}},
484
+ {{0.221950385, -0.3187676331, 0.01799915743, 0.01074823814},
485
+ {-0.2951249643, 0.06219954479, 0.01535556598, 0.001550077057},
486
+ {0.02155310298, 0.004170831599, 0.001292462449, 6.976601077e-05}},
487
+ {{-0.05980841433, 0.008441916499, 0.01085618172, 0.002319392565},
488
+ {0.02911413504, 0.01400243777, -0.002721828457, 0.000751041181},
489
+ {0.005900514878, -0.001936558688, -9.495446725e-06, 5.385558597e-05}}},
490
+ {{{1, -0.02924021934, -0.04438342661, 0.007285809825},
491
+ {0.6357567472, -0.3473456711, 0.05454656494, -0.002407477521},
492
+ {-0.03301322327, 0.004845219414, 0.00231480583, -0.0002307248149}},
493
+ {{0.5925320577, -0.1757678135, 0.01505928619, 0.000564515273},
494
+ {0.1014815858, -0.06589186703, 0.01272886114, -0.0007316646956},
495
+ {-0.007258481865, 0.001096195486, 0.0003934994223, -4.12701925e-05}},
496
+ {{0.06469649321, -0.0236701437, 0.002902096474, -5.896963079e-05},
497
+ {0.001925008108, -0.002869809258, 0.0008000589141, -6.063713228e-05},
498
+ {-0.0003477407336, 6.959756487e-05, 1.097287507e-05, -1.650964693e-06}}},
499
+ };
500
+ const accscalar_t u = compat_log(x_);
501
+ const accscalar_t a = compat_log(alpha_) - u;
502
+ const accscalar_t b = compat_log(total_) - a;
503
+ const accscalar_t pow_u[3] = {1, u, u * u};
504
+ const accscalar_t pow_a[3] = {1, a, a * a};
505
+ accscalar_t p = 0.0;
506
+ accscalar_t q = 0.0;
507
+ for (int i = 0; i < 3; ++i) {
508
+ for (int j = 0; j < 3; ++j) {
509
+ const accscalar_t ua = pow_u[i] * pow_a[j];
510
+ p += ua * (c[0][i][j][0] + b * (c[0][i][j][1] + b * (c[0][i][j][2] + b * c[0][i][j][3])));
511
+ q += ua * (c[1][i][j][0] + b * (c[1][i][j][1] + b * (c[1][i][j][2] + b * c[1][i][j][3])));
512
+ }
513
+ }
514
+ const accscalar_t approx = x_ * (digamma_one<scalar_t, accscalar_t>(total_) - digamma_one<scalar_t, accscalar_t>(alpha_)) / beta_;
515
+ return static_cast<scalar_t>(p / q * approx);
516
+ }
517
+
518
+ } // namespace
lib/python3.12/site-packages/torch/include/ATen/native/EmbeddingBag.h ADDED
@@ -0,0 +1,153 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <ATen/core/Tensor.h>
2
+ #include <ATen/Config.h>
3
+ #include <cstdint>
4
+
5
+ #ifdef USE_FBGEMM
6
+ #include <fbgemm/FbgemmEmbedding.h>
7
+ #endif
8
+
9
+ namespace at::native {
10
+
11
+ enum class EmbeddingBagMode {
12
+ SUM = 0,
13
+ MEAN = 1,
14
+ MAX = 2,
15
+ };
16
+
17
+ [[maybe_unused]] static bool operator==(int64_t op1, EmbeddingBagMode op2) {
18
+ return op1 == static_cast<int64_t>(op2);
19
+ }
20
+
21
+ [[maybe_unused]] static bool operator!=(int64_t op1, EmbeddingBagMode op2) {
22
+ return !(op1 == op2);
23
+ }
24
+
25
+ void check_arguments(
26
+ const Tensor& weight,
27
+ const Tensor& indices,
28
+ const Tensor& offsets,
29
+ const int64_t mode,
30
+ const std::optional<Tensor>& per_sample_weights,
31
+ bool include_last_offset);
32
+
33
+ void make_bag_size_out(
34
+ Tensor& bag_size_out,
35
+ const Tensor& offsets,
36
+ const Tensor& indices,
37
+ const int64_t mode,
38
+ const bool include_last_offset,
39
+ const bool requires_grad);
40
+
41
+ void make_max_indices_out(
42
+ Tensor& max_indices_out,
43
+ const Tensor& weight,
44
+ const Tensor& indices,
45
+ const Tensor& offsets,
46
+ const Tensor& bag_size,
47
+ const int64_t mode,
48
+ bool include_last_offset);
49
+
50
+ void make_offset2bag_out(
51
+ Tensor& offset2bag,
52
+ Tensor& output,
53
+ const Tensor& weight,
54
+ const Tensor& indices,
55
+ const Tensor& offsets,
56
+ const int64_t mode,
57
+ const std::optional<Tensor>& per_sample_weights,
58
+ const int64_t padding_idx = -1);
59
+
60
+ #ifdef USE_FBGEMM
61
+
62
+ template<bool has_weight, typename TIndex, typename TData>
63
+ struct _CallbackAndBlockSize {
64
+ using TCallback = typename fbgemm::EmbeddingSpMDMKernelSignature<TData, TIndex, TIndex, TData>::Type;
65
+
66
+ int64_t blockSize = -1;
67
+ TCallback callback = nullptr;
68
+
69
+ static TCallback generateCallback(int64_t block_size) {
70
+ return fbgemm::GenerateEmbeddingSpMDM<TData, TIndex, TIndex, TData>(
71
+ block_size,
72
+ has_weight,
73
+ /* normalize_by_lengths */false,
74
+ /* prefetch */16,
75
+ /* is_weight_positional */false,
76
+ /* use_offsets */true);
77
+ }
78
+
79
+ _CallbackAndBlockSize() = default;
80
+
81
+ explicit _CallbackAndBlockSize(std::optional<int64_t> maybe_block_size)
82
+ : blockSize(maybe_block_size.value_or(-1))
83
+ , callback(maybe_block_size.has_value() ? generateCallback(maybe_block_size.value()) : nullptr)
84
+ {}
85
+ };
86
+
87
+ template<typename... StorageMixins>
88
+ struct _EmbeddingBagKernelCacheImpl : private StorageMixins... {
89
+
90
+ _EmbeddingBagKernelCacheImpl() = default;
91
+ // use each of the mixins to store corresponding kernel and block size
92
+ explicit _EmbeddingBagKernelCacheImpl(std::optional<int64_t> maybe_block_size)
93
+ : StorageMixins(maybe_block_size)...
94
+ {}
95
+
96
+ // this method is thread safe (call sites may call from different threads)
97
+ template<bool has_weight, typename TIndex, typename TData>
98
+ typename _CallbackAndBlockSize<has_weight, TIndex, TData>::TCallback
99
+ getCallback(int64_t block_size) const {
100
+ // if the cache doesn't store the kernel for the incoming block size
101
+ // (so it is different from the one stored in corresponding mixin)
102
+ // regenerate the kernel (not writing it into the cache so we avoid locks)
103
+ if (block_size != _CallbackAndBlockSize<has_weight, TIndex, TData>::blockSize) {
104
+ return _CallbackAndBlockSize<has_weight, TIndex, TData>::generateCallback(block_size);
105
+ }
106
+ // else retrieve the cached kernel from the corresponding mixin
107
+ return _CallbackAndBlockSize<has_weight, TIndex, TData>::callback;
108
+ }
109
+ };
110
+
111
+ // instantiate the cache with the list of storage mixins
112
+ // for each of the 8 _EmbeddingBagKernelCache* usages in the EmbeddingBag.cpp impl file
113
+ using _EmbeddingBagKernelCache = _EmbeddingBagKernelCacheImpl<
114
+ _CallbackAndBlockSize<true, int32_t, float>,
115
+ _CallbackAndBlockSize<false, int32_t, float>,
116
+ _CallbackAndBlockSize<true, int64_t, float>,
117
+ _CallbackAndBlockSize<false, int64_t, float>,
118
+ _CallbackAndBlockSize<true, int32_t, unsigned short>,
119
+ _CallbackAndBlockSize<false, int32_t, unsigned short>,
120
+ _CallbackAndBlockSize<true, int64_t, unsigned short>,
121
+ _CallbackAndBlockSize<false, int64_t, unsigned short>>;
122
+ #else
123
+ struct _EmbeddingBagKernelCache {
124
+ explicit _EmbeddingBagKernelCache(std::optional<int64_t> /* maybe_block_size */) {}
125
+ };
126
+ #endif
127
+
128
+ void _embedding_bag_cpu_impl_out(Tensor& output, Tensor& offset2bag,
129
+ Tensor& bag_size, Tensor* max_indices,
130
+ const Tensor &weight, const Tensor &indices,
131
+ const Tensor &offsets, const int64_t mode = 0,
132
+ const std::optional<Tensor>& per_sample_weights = std::nullopt,
133
+ bool include_last_offset = false,
134
+ int64_t padding_idx = -1,
135
+ _EmbeddingBagKernelCache* fbgemm_kernel_cache = nullptr);
136
+
137
+ void _embedding_bag_cpu_out(
138
+ at::Tensor& output,
139
+ at::Tensor& offset2bag,
140
+ at::Tensor& bag_size,
141
+ at::Tensor* p_max_indices,
142
+ const at::Tensor& weight,
143
+ const at::Tensor& indices,
144
+ const at::Tensor& offsets,
145
+ const bool scale_grad_by_freq,
146
+ const int64_t mode,
147
+ const bool sparse,
148
+ const std::optional<at::Tensor>& per_sample_weights,
149
+ const bool include_last_offset,
150
+ const std::optional<int64_t>& padding_idx,
151
+ _EmbeddingBagKernelCache* fbgemm_kernel_cache = nullptr);
152
+
153
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/Fill.h ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Functions that fill Tensors with constants. Implementations are in Fill.cpp.
2
+
3
+ #pragma once
4
+
5
+ #include <ATen/native/DispatchStub.h>
6
+
7
+ namespace c10 {
8
+ class Scalar;
9
+ }
10
+
11
+ namespace at {
12
+ class Tensor;
13
+ struct TensorIterator;
14
+
15
+ namespace native {
16
+
17
+ DECLARE_DISPATCH(void(*)(TensorIterator&, const c10::Scalar&), fill_stub)
18
+
19
+ Tensor& fill_out(Tensor& self, const Scalar& value);
20
+
21
+ }} // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/ForeachUtils.h ADDED
@@ -0,0 +1,409 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/Device.h>
4
+ #include <ATen/Dispatch.h>
5
+ #include <ATen/ScalarType.h>
6
+ #include <ATen/core/Tensor.h>
7
+ #include <ATen/native/utils/ParamsHash.h>
8
+ #include <c10/util/Exception.h>
9
+ #include <c10/util/irange.h>
10
+
11
+ #ifndef AT_PER_OPERATOR_HEADERS
12
+ #include <ATen/NativeFunctions.h>
13
+ #else
14
+ #include <ATen/ops/result_type_native.h>
15
+ #endif
16
+
17
+ #include <unordered_map>
18
+ #include <vector>
19
+
20
+ namespace at::native {
21
+ namespace {
22
+ // Check if tensor list has either a boolean tensor or a integer tensor
23
+ inline bool has_integral_tensor(TensorList tensors, const bool includeBool) {
24
+ return std::any_of(
25
+ tensors.begin(), tensors.end(), [&includeBool](const auto& t) {
26
+ return at::isIntegralType(t.scalar_type(), includeBool);
27
+ });
28
+ }
29
+ // check if tensor list has bool tensors
30
+ inline bool has_bool_tensor(TensorList tensors) {
31
+ return std::any_of(tensors.begin(), tensors.end(), [](const auto& t) -> bool {
32
+ return t.scalar_type() == ScalarType::Bool;
33
+ });
34
+ }
35
+
36
+ // Check foreach API restrictions
37
+ // - Tensor lists must be non-empty.
38
+ // - All TensorLists and ScalarLists must have the same number of elements.
39
+ // - Corresponding tensors must have the same size.
40
+ inline void check_foreach_api_restrictions(TensorList tensors) {
41
+ TORCH_CHECK(!tensors.empty(), "Tensor list must have at least one tensor.");
42
+ }
43
+
44
+ inline void check_foreach_api_restrictions(
45
+ TensorList tensors,
46
+ ArrayRef<Scalar> scalars) {
47
+ check_foreach_api_restrictions(tensors);
48
+ TORCH_CHECK(
49
+ tensors.size() == scalars.size(),
50
+ "Tensor list must have same number of elements as scalar list.");
51
+ }
52
+
53
+ inline void check_foreach_api_restrictions(
54
+ TensorList tensors1,
55
+ TensorList tensors2) {
56
+ TORCH_CHECK(!tensors1.empty(), "Tensor list must have at least one tensor.");
57
+ TORCH_CHECK(!tensors2.empty(), "Tensor list must have at least one tensor.");
58
+ TORCH_CHECK(
59
+ tensors1.size() == tensors2.size(),
60
+ "Tensor lists must have the same number of tensors, got ",
61
+ tensors1.size(),
62
+ " and ",
63
+ tensors2.size());
64
+ }
65
+
66
+ inline void check_foreach_api_restrictions(
67
+ TensorList tensors1,
68
+ TensorList tensors2,
69
+ TensorList tensors3) {
70
+ TORCH_CHECK(!tensors1.empty(), "Tensor list must have at least one tensor.");
71
+ TORCH_CHECK(!tensors2.empty(), "Tensor list must have at least one tensor.");
72
+ TORCH_CHECK(!tensors3.empty(), "Tensor list must have at least one tensor.");
73
+ TORCH_CHECK(
74
+ tensors1.size() == tensors2.size(),
75
+ "Tensor lists must have the same number of tensors, got ",
76
+ tensors1.size(),
77
+ " and ",
78
+ tensors2.size());
79
+ TORCH_CHECK(
80
+ tensors1.size() == tensors3.size(),
81
+ "Tensor lists must have the same number of tensors, got ",
82
+ tensors1.size(),
83
+ " and ",
84
+ tensors3.size());
85
+ }
86
+
87
+ inline void check_foreach_api_restrictions(
88
+ TensorList tensors1,
89
+ TensorList tensors2,
90
+ TensorList tensors3,
91
+ ArrayRef<Scalar> scalars) {
92
+ check_foreach_api_restrictions(tensors1, tensors2, tensors3);
93
+ TORCH_CHECK(
94
+ tensors1.size() == scalars.size(),
95
+ "Tensor list must have same number of elements as scalar list, got ",
96
+ tensors1.size(),
97
+ " and ",
98
+ scalars.size());
99
+ }
100
+
101
+ inline void check_foreach_api_restrictions(
102
+ TensorList tensors1,
103
+ TensorList tensors2,
104
+ ArrayRef<Scalar> scalars) {
105
+ check_foreach_api_restrictions(tensors1, tensors2);
106
+ TORCH_CHECK(
107
+ tensors1.size() == scalars.size(),
108
+ "Tensor list must have same number of elements as scalar list, got ",
109
+ tensors1.size(),
110
+ " and ",
111
+ scalars.size());
112
+ }
113
+
114
+ // Helper function called in check_fast_path_restrictions to check whether all
115
+ // corresponding tensors (aligning in index across the tensorLists) share the
116
+ // same device and dtype.
117
+ inline bool _check_tensors_share_device_and_dtype(
118
+ ArrayRef<TensorList> tensorLists,
119
+ const bool skip_dtype_check = false) {
120
+ const auto expected_dtype = tensorLists[0][0].dtype();
121
+ const auto expected_device = tensorLists[0][0].device();
122
+
123
+ auto is_tensor_okay = [&](const Tensor& tensor) {
124
+ return (skip_dtype_check || tensor.dtype() == expected_dtype) &&
125
+ tensor.device() == expected_device && tensor.layout() == at::kStrided &&
126
+ tensor.is_non_overlapping_and_dense();
127
+ };
128
+
129
+ for (const auto& tensorList : tensorLists) {
130
+ for (const auto& tensor : tensorList) {
131
+ if (!is_tensor_okay(tensor)) {
132
+ return false;
133
+ }
134
+ }
135
+ }
136
+
137
+ return true;
138
+ }
139
+
140
+ // Helper function called in check_fast_path_restrictions to check if
141
+ // corresponding tensors in tensor lists have the same sizes and strides.
142
+ inline bool _check_tensors_share_sizes_and_strides(
143
+ ArrayRef<TensorList> tensorLists) {
144
+ auto is_diff_stride = [](const IntArrayRef& size,
145
+ const IntArrayRef& left_stride,
146
+ const IntArrayRef& right_stride) -> bool {
147
+ const size_t size_size = size.size();
148
+ for (const auto dim : c10::irange(size_size)) {
149
+ if (size[dim] == 1)
150
+ continue;
151
+ if (left_stride[dim] != right_stride[dim]) {
152
+ return true;
153
+ }
154
+ }
155
+ return false;
156
+ };
157
+ for (const auto i : c10::irange(1, tensorLists.size())) {
158
+ for (const auto j : c10::irange(tensorLists[0].size())) {
159
+ if (tensorLists[0][j].sizes() != tensorLists[i][j].sizes() ||
160
+ is_diff_stride(
161
+ tensorLists[0][j].sizes(),
162
+ tensorLists[0][j].strides(),
163
+ tensorLists[i][j].strides())) {
164
+ return false;
165
+ }
166
+ }
167
+ }
168
+
169
+ return true;
170
+ }
171
+
172
+ // Helper function called in check_fast_path_restrictions to check whether
173
+ // all tensors type promote properly with the scalars in scalarList. This
174
+ // function assumes that _check_tensors_share_device_and_dtype has already been
175
+ // called so that all corresponding tensors in tensorLists have the same dtype.
176
+ // Then, it is sufficient to check the type promotion with just one tensorList.
177
+ inline bool _check_tensors_do_type_promotion_with_scalars(
178
+ TensorList tensorList,
179
+ ArrayRef<Scalar> scalarList = {},
180
+ bool does_op_promote_integer_inputs_to_float = false) {
181
+ for (const auto i : c10::irange(tensorList.size())) {
182
+ // For division, integer inputs will result in float.
183
+ if (does_op_promote_integer_inputs_to_float) {
184
+ if (at::isIntegralType(
185
+ tensorList[i].scalar_type(), /*includeBool*/ true)) {
186
+ return false;
187
+ }
188
+ }
189
+ if (!scalarList.empty()) {
190
+ const auto& scalar =
191
+ scalarList.size() == 1 ? scalarList[0] : scalarList[i];
192
+ const auto& tensor = tensorList[i];
193
+ // note(mkozuki): This check might be responsible for
194
+ // `_foreach_add(bool_tensors, bool_tensors)` being pushed to slow path.
195
+ if (tensor.scalar_type() != at::native::result_type(scalar, tensor)) {
196
+ return false;
197
+ }
198
+ }
199
+ }
200
+
201
+ return true;
202
+ }
203
+
204
+ // To go via 'fast' path, several conditions must be satisfied
205
+ // - All tensors in all lists must have the same dtype.
206
+ // - All tensors must be on the same device
207
+ // - All tensors must have strided layout
208
+ // - All tensors must be non-overlapping and dense
209
+ // - Resulting tensor must have the same dtype as the input one
210
+
211
+ // [note: what's ``does_op_promote_integer_inputs_to_float=true``?]
212
+ // ``does_op_promote_integer_inputs_to_float=true`` means that the result of
213
+ // the op will be float even if inputs are integer or boolean, which
214
+ // currently fast path does not support. In short, this flag, when
215
+ // turned on, gatekeeps the op from going down the fastpath.
216
+
217
+ // Please, make sure to call check_foreach_api_restrictions before calling this
218
+ // method. There is a set of preconditions that have to be satisfied.
219
+ inline bool check_fast_path_restrictions(
220
+ ArrayRef<TensorList> tensorLists,
221
+ ArrayRef<Scalar> scalarList = {},
222
+ bool does_op_promote_integer_inputs_to_float = false) {
223
+ return _check_tensors_share_device_and_dtype(tensorLists) &&
224
+ _check_tensors_share_sizes_and_strides(tensorLists) &&
225
+ _check_tensors_do_type_promotion_with_scalars(
226
+ tensorLists[0],
227
+ scalarList,
228
+ does_op_promote_integer_inputs_to_float);
229
+ }
230
+
231
+ inline std::vector<c10::Scalar> convert_tensor_to_scalar_list(
232
+ const Tensor& scalarList_,
233
+ int64_t expect_length) {
234
+ std::vector<c10::Scalar> scalarList;
235
+ TORCH_CHECK(
236
+ scalarList_.device() == c10::kCPU,
237
+ "Expected scalars to be on CPU, got ",
238
+ scalarList_.device(),
239
+ " instead.");
240
+ TORCH_CHECK(
241
+ scalarList_.is_contiguous(), "Expected scalars to be contiguous.");
242
+ TORCH_CHECK(
243
+ scalarList_.dim() == 1,
244
+ "Expected packed scalar Tensor to be of dimension 1. Got ",
245
+ scalarList_.dim(),
246
+ " instead.");
247
+ AT_DISPATCH_ALL_TYPES_AND_COMPLEX_AND4(
248
+ kComplexHalf,
249
+ kHalf,
250
+ kBool,
251
+ kBFloat16,
252
+ scalarList_.scalar_type(),
253
+ "convert_tensor_to_scalar_list",
254
+ [&]() {
255
+ const scalar_t* scalar_data = scalarList_.const_data_ptr<scalar_t>();
256
+ TORCH_CHECK(
257
+ (expect_length == scalarList_.size(0)),
258
+ "Expected length of scalars to match input of length ",
259
+ expect_length,
260
+ " but got ",
261
+ scalarList_.size(0),
262
+ " instead.");
263
+ for (int64_t i = 0; i < scalarList_.size(0); i++) {
264
+ scalarList.emplace_back(scalar_data[i]);
265
+ }
266
+ });
267
+ return scalarList;
268
+ }
269
+
270
+ // see: [note: what's ``does_op_promote_integer_inputs_to_float=true``?]
271
+ inline bool can_use_fast_route(
272
+ ArrayRef<TensorList> tensorLists,
273
+ ArrayRef<Scalar> scalarList = {},
274
+ bool does_op_promote_integer_inputs_to_float = false) {
275
+ return check_fast_path_restrictions(
276
+ tensorLists, scalarList, does_op_promote_integer_inputs_to_float);
277
+ }
278
+
279
+ // see: [note: what's ``does_op_promote_integer_inputs_to_float=true``?]
280
+ inline bool can_use_fast_route(
281
+ TensorList tensors1,
282
+ TensorList tensors2,
283
+ bool does_op_promote_integer_inputs_to_float = false) {
284
+ return can_use_fast_route(
285
+ {tensors1, tensors2}, {}, does_op_promote_integer_inputs_to_float);
286
+ }
287
+
288
+ using DeviceDtypeKey = std::pair<at::Device, at::ScalarType>;
289
+ using IndicesT = std::vector<size_t>;
290
+ using nested_optional_tensorvec_t =
291
+ std::vector<std::vector<std::optional<at::Tensor>>>;
292
+ using TensorsAndIndicesT = std::pair<nested_optional_tensorvec_t, IndicesT>;
293
+ using FlatMap = std::unordered_map<
294
+ DeviceDtypeKey,
295
+ TensorsAndIndicesT,
296
+ ParamsHash<DeviceDtypeKey>>;
297
+
298
+ inline FlatMap _group_tensors_by_first_tensors_device_and_dtype(
299
+ const nested_optional_tensorvec_t& nested_tensorlist,
300
+ const bool with_indices) {
301
+ FlatMap grouped_tensors_with_indices;
302
+
303
+ TORCH_CHECK(!nested_tensorlist.empty());
304
+ TORCH_CHECK(!nested_tensorlist[0].empty());
305
+ const auto num_lists = nested_tensorlist.size();
306
+ const auto num_tensors = nested_tensorlist[0].size();
307
+
308
+ TORCH_CHECK(std::all_of(
309
+ nested_tensorlist.cbegin(),
310
+ nested_tensorlist.cend(),
311
+ [&](const auto& tensorlist) -> bool {
312
+ // note(crcrpar): Allow empty tensorlists following
313
+ // ref:
314
+ // https://github.com/pytorch/pytorch/blob/85885301fd3c6adb8b9dc3cf7afadf6945566684/torch/utils/_foreach_utils.py#L21-L24
315
+ return tensorlist.size() == num_tensors || tensorlist.size() == 0;
316
+ }));
317
+
318
+ for (const auto& tensor_index : c10::irange(num_tensors)) {
319
+ const auto key = [&]() -> DeviceDtypeKey {
320
+ const auto t = nested_tensorlist[0][tensor_index];
321
+ TORCH_CHECK(
322
+ t.has_value(),
323
+ "Tensors of the first list of nested Tensor lists are supposed to be defined but ",
324
+ "the ",
325
+ tensor_index,
326
+ "-th Tensor is not.");
327
+ return {t->device(), t->scalar_type()};
328
+ }();
329
+ TORCH_CHECK(
330
+ std::all_of(
331
+ nested_tensorlist.cbegin(),
332
+ nested_tensorlist.cend(),
333
+ [&](const auto& tensorlist) -> bool {
334
+ if (tensorlist.size() == 0) {
335
+ return true;
336
+ }
337
+ const auto& tensor = tensorlist[tensor_index];
338
+ // note(crcrpar): Currently the scope of this function is
339
+ // optimizers so there could be `state_steps` and other scalars
340
+ // whose elements are float tensors no matter what the parameter's
341
+ // dtype is.
342
+ if (!tensor.has_value()) {
343
+ return true;
344
+ } else {
345
+ const auto s = tensor->scalar_type();
346
+ const auto d = tensor->device();
347
+ // Note: `step` or `state_step` is float32 by default.
348
+ if (key.first == d) {
349
+ return key.second == s || s == at::ScalarType::Float ||
350
+ s == at::ScalarType::Double;
351
+ } else if (d.is_cpu()) {
352
+ // note(crcrpar): There are some test cases (e.g.
353
+ // TestOptim::test_adam) where state_steps are on CPU and the
354
+ // others are on CUDA. Currently a state_step Tensor has the
355
+ // dtype of float.
356
+ return s == at::ScalarType::Float ||
357
+ s == at::ScalarType::Double;
358
+ } else {
359
+ return false;
360
+ }
361
+ }
362
+ }),
363
+ "Tensors of the same index must be on the same device and the same dtype except `step` tensors that can be CPU and float32/64 notwithstanding");
364
+ if (!grouped_tensors_with_indices.count(key)) {
365
+ grouped_tensors_with_indices.insert(
366
+ {key,
367
+ TensorsAndIndicesT{
368
+ [&]() -> nested_optional_tensorvec_t {
369
+ nested_optional_tensorvec_t nested_tensorvec;
370
+ nested_tensorvec.reserve(num_lists);
371
+ for (const auto& i : c10::irange(num_lists)) {
372
+ std::vector<std::optional<at::Tensor>> tensors;
373
+ if (!nested_tensorlist[i].empty()) {
374
+ // NB: num_tensors is the max possible length for any of
375
+ // the inner lists of tensor references. Reserving the max
376
+ // trades memory for perf. This should not have significant
377
+ // impact.
378
+ tensors.reserve(num_tensors);
379
+ }
380
+ nested_tensorvec.emplace_back(tensors);
381
+ }
382
+ return nested_tensorvec;
383
+ }(),
384
+ [&]() -> IndicesT {
385
+ if (!with_indices) {
386
+ return {};
387
+ } else {
388
+ IndicesT indices;
389
+ indices.reserve(num_tensors);
390
+ return indices;
391
+ }
392
+ }()}});
393
+ }
394
+ for (const auto& list_index : c10::irange(num_lists)) {
395
+ if (!nested_tensorlist[list_index].empty()) {
396
+ grouped_tensors_with_indices[key].first[list_index].emplace_back(
397
+ nested_tensorlist[list_index][tensor_index]);
398
+ }
399
+ }
400
+ if (with_indices) {
401
+ grouped_tensors_with_indices[key].second.emplace_back(tensor_index);
402
+ }
403
+ }
404
+
405
+ return grouped_tensors_with_indices;
406
+ }
407
+
408
+ } // namespace
409
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/FractionalMaxPooling.h ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/core/Tensor.h>
3
+ #include <ATen/TensorUtils.h>
4
+ #include <c10/util/irange.h>
5
+
6
+ namespace at::native {
7
+
8
+ template<typename scalar_t>
9
+ inline std::vector<int64_t> generate_intervals(
10
+ scalar_t sample,
11
+ int64_t inputSize,
12
+ int64_t outputSize,
13
+ int64_t poolSize) {
14
+ std::vector<int64_t> sequence(outputSize);
15
+ if (outputSize > 1) {
16
+ scalar_t alpha = static_cast<scalar_t>(inputSize - poolSize) /
17
+ static_cast<scalar_t>(outputSize - 1);
18
+
19
+ for (const auto i : c10::irange(outputSize - 1)) {
20
+ sequence[i] =
21
+ static_cast<int>((i + sample) * alpha) - static_cast<int>(sample * alpha);
22
+ }
23
+ }
24
+ if (outputSize > 0) {
25
+ sequence[outputSize - 1] = inputSize - poolSize;
26
+ }
27
+ return sequence;
28
+ }
29
+
30
+ template <int64_t ndim>
31
+ inline void fractional_max_pool_check_shape(
32
+ const Tensor& input,
33
+ const Tensor& randomSamples) {
34
+
35
+ TORCH_CHECK(
36
+ input.scalar_type() == randomSamples.scalar_type(),
37
+ "Expect _random_samples to have the same dtype as input");
38
+
39
+ int64_t ndimension = randomSamples.ndimension();
40
+ TORCH_CHECK(
41
+ ndimension == 3,
42
+ "Expect _random_samples to have 3 dimensions, got ", ndimension);
43
+
44
+ int64_t N = randomSamples.size(0);
45
+ int64_t C = randomSamples.size(1);
46
+ int64_t D = randomSamples.size(2);
47
+
48
+ int64_t input_batch = 0, input_channel = 0;
49
+ if (ndim == 2) {
50
+ // fractional_max_pool2d
51
+ if (input.ndimension() == 3) {
52
+ input_batch = 1;
53
+ input_channel = input.size(0);
54
+ } else {
55
+ input_batch = input.size(0);
56
+ input_channel = input.size(1);
57
+ }
58
+ } else {
59
+ // factional_max_pool3d
60
+ if (input.ndimension() == 4) {
61
+ input_batch = 1;
62
+ input_channel = input.size(0);
63
+ } else {
64
+ input_batch = input.size(0);
65
+ input_channel = input.size(1);
66
+ }
67
+ }
68
+
69
+ TORCH_CHECK(
70
+ N >= input_batch,
71
+ "Expect _random_samples.size(0) no less then input batch size.");
72
+ TORCH_CHECK(
73
+ C == input_channel,
74
+ "Expect _random_samples.size(1) equals to input channel size.");
75
+ TORCH_CHECK(
76
+ D == ndim,
77
+ "Expect _random_samples.size(2) equals to ", ndim, "; got ", D, ".");
78
+ }
79
+
80
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/FunctionOfAMatrixUtils.h ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+ #include <cstdint>
5
+
6
+ namespace at {
7
+ struct TensorIterator;
8
+
9
+ namespace native {
10
+
11
+ using _compute_linear_combination_fn = void(*)(
12
+ TensorIterator& iter,
13
+ int64_t in_stride,
14
+ int64_t coeff_stride,
15
+ int64_t num_summations
16
+ );
17
+
18
+ DECLARE_DISPATCH(_compute_linear_combination_fn, _compute_linear_combination_stub)
19
+
20
+ }} // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/FusedAdagrad.h ADDED
@@ -0,0 +1,20 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <ATen/core/Tensor.h>
2
+ #include <ATen/native/DispatchStub.h>
3
+
4
+ namespace at::native {
5
+
6
+ using fused_adagrad_fn = void (*)(
7
+ const at::Tensor& param,
8
+ const at::Tensor& grad,
9
+ const at::Tensor& state_sum,
10
+ const at::Tensor& state_step,
11
+ const double lr,
12
+ const double lr_decay,
13
+ const double weight_decay,
14
+ const double eps,
15
+ const bool maximize,
16
+ const float* grad_scale_ptr);
17
+
18
+ DECLARE_DISPATCH(fused_adagrad_fn, fused_adagrad_stub)
19
+
20
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/FusedSGD.h ADDED
@@ -0,0 +1,21 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #include <ATen/core/Tensor.h>
2
+ #include <ATen/native/DispatchStub.h>
3
+
4
+ namespace at::native {
5
+
6
+ using fused_sgd_fn = void (*)(
7
+ const at::Tensor& param,
8
+ const at::Tensor& grad,
9
+ const at::Tensor& momentum_buffer,
10
+ const double weight_decay,
11
+ const double momentum,
12
+ const double lr,
13
+ const double dampening,
14
+ const bool nesterov,
15
+ const bool maximize,
16
+ const bool is_first_step,
17
+ const float* grad_scale_ptr);
18
+
19
+ DECLARE_DISPATCH(fused_sgd_fn, fused_sgd_stub)
20
+
21
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/GridSampler.h ADDED
@@ -0,0 +1,298 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <algorithm>
4
+ #include <cmath>
5
+ #include <cstdint>
6
+ #include <utility>
7
+
8
+ #include <ATen/native/GridSamplerUtils.h>
9
+
10
+ namespace at::native {
11
+
12
+ using detail::GridSamplerInterpolation;
13
+ using detail::GridSamplerPadding;
14
+
15
+ // Unnormalizes a coordinate from the -1 to +1 scale to its pixel index value,
16
+ // where we view each pixel as an area between (idx - 0.5) and (idx + 0.5).
17
+ // if align_corners: -1 and +1 get sent to the centers of the corner pixels
18
+ // -1 --> 0
19
+ // +1 --> (size - 1)
20
+ // scale_factor = (size - 1) / 2
21
+ // if not align_corners: -1 and +1 get sent to the image edges
22
+ // -1 --> -0.5
23
+ // +1 --> (size - 1) + 0.5 == size - 0.5
24
+ // scale_factor = size / 2
25
+ template <typename scalar_t>
26
+ static inline scalar_t grid_sampler_unnormalize(scalar_t coord, int64_t size,
27
+ bool align_corners) {
28
+ if (align_corners) {
29
+ // unnormalize coord from [-1, 1] to [0, size - 1]
30
+ return ((coord + 1) / 2) * (size - 1);
31
+ } else {
32
+ // unnormalize coord from [-1, 1] to [-0.5, size - 0.5]
33
+ return ((coord + 1) * size - 1) / 2;
34
+ }
35
+ }
36
+
37
+ // grid_sampler_unnormalize_set_grad works the same as grid_sampler_unnormalize
38
+ // except that it also returns the `d output / d input` via pointer argument
39
+ // `grad_in`.
40
+ // This is useful in the backward pass of grid_sampler.
41
+ template <typename scalar_t>
42
+ static inline scalar_t grid_sampler_unnormalize_set_grad(scalar_t coord, int64_t size,
43
+ bool align_corners, scalar_t *grad_in) {
44
+ if (align_corners) {
45
+ // unnormalize coord from [-1, 1] to [0, size - 1]
46
+ *grad_in = static_cast<scalar_t>(size - 1) / 2;
47
+ return ((coord + 1) / 2) * (size - 1);
48
+ } else {
49
+ // unnormalize coord from [-1, 1] to [-0.5, size - 0.5]
50
+ *grad_in = static_cast<scalar_t>(size) / 2;
51
+ return ((coord + 1) * size - 1) / 2;
52
+ }
53
+ }
54
+
55
+ // Clips coordinates to between 0 and clip_limit - 1
56
+ template<typename scalar_t>
57
+ static inline scalar_t clip_coordinates(scalar_t in, int64_t clip_limit) {
58
+ return std::min(static_cast<scalar_t>(clip_limit - 1), std::max(in, static_cast<scalar_t>(0)));
59
+ }
60
+
61
+ // clip_coordinates_set_grad works similarly to clip_coordinates except that
62
+ // it also returns the `d output / d input` via pointer argument `grad_in`.
63
+ // This is useful in the backward pass of grid_sampler.
64
+ template<typename scalar_t>
65
+ static inline scalar_t clip_coordinates_set_grad(scalar_t in, int64_t clip_limit,
66
+ scalar_t *grad_in) {
67
+ // Note that it is important for the gradient calculation that borders
68
+ // are considered out of bounds.
69
+ if (in <= static_cast<scalar_t>(0)) {
70
+ *grad_in = static_cast<scalar_t>(0);
71
+ return static_cast<scalar_t>(0);
72
+ } else {
73
+ scalar_t max = static_cast<scalar_t>(clip_limit - 1);
74
+ if (in >= max) {
75
+ *grad_in = static_cast<scalar_t>(0);
76
+ return max;
77
+ } else {
78
+ *grad_in = static_cast<scalar_t>(1);
79
+ return in;
80
+ }
81
+ }
82
+ }
83
+
84
+ // Reflects coordinates until they fall between low and high (inclusive).
85
+ // The bounds are passed as twice their value so that half-integer values
86
+ // can be represented as ints.
87
+ template<typename scalar_t>
88
+ static inline scalar_t reflect_coordinates(scalar_t in, int64_t twice_low,
89
+ int64_t twice_high) {
90
+ if (twice_low == twice_high) {
91
+ return static_cast<scalar_t>(0);
92
+ }
93
+ scalar_t min = static_cast<scalar_t>(twice_low) / 2;
94
+ scalar_t span = static_cast<scalar_t>(twice_high - twice_low) / 2;
95
+ in = std::fabs(in - min);
96
+ // `fmod` returns same sign as `in`, which is positive after the `fabs` above.
97
+ scalar_t extra = std::fmod(in, span);
98
+ int flips = static_cast<int>(std::floor(in / span));
99
+ if (flips % 2 == 0) {
100
+ return extra + min;
101
+ } else {
102
+ return span - extra + min;
103
+ }
104
+ }
105
+
106
+ // reflect_coordinates_set_grad works similarly to reflect_coordinates except
107
+ // that it also returns the `d output / d input` via pointer argument
108
+ // `grad_in`.
109
+ // This is useful in the backward pass of grid_sampler.
110
+ template<typename scalar_t>
111
+ static inline scalar_t reflect_coordinates_set_grad(scalar_t in, int64_t twice_low,
112
+ int64_t twice_high, scalar_t *grad_in) {
113
+ if (twice_low == twice_high) {
114
+ *grad_in = static_cast<scalar_t>(0);
115
+ return static_cast<scalar_t>(0);
116
+ }
117
+ int grad_in_mult_;
118
+ scalar_t min = static_cast<scalar_t>(twice_low) / 2;
119
+ scalar_t span = static_cast<scalar_t>(twice_high - twice_low) / 2;
120
+ in = in - min;
121
+ if (in < static_cast<scalar_t>(0)) {
122
+ grad_in_mult_ = -1;
123
+ in = -in;
124
+ } else {
125
+ grad_in_mult_ = 1;
126
+ }
127
+ // `fmod` returns same sign as `in`, which is positive after the `if` above.
128
+ scalar_t extra = std::fmod(in, span);
129
+ int flips = static_cast<int>(std::floor(in / span));
130
+ if (flips % 2 == 0) {
131
+ *grad_in = static_cast<scalar_t>(grad_in_mult_);
132
+ return extra + min;
133
+ } else {
134
+ *grad_in = static_cast<scalar_t>(-grad_in_mult_);
135
+ return span - extra + min;
136
+ }
137
+ }
138
+
139
+ // Mapping the out-of-boundary points back into boundary
140
+ // This would only affect padding_mode=border or reflection
141
+ template<typename scalar_t>
142
+ static inline scalar_t compute_coordinates(scalar_t coord, int64_t size,
143
+ GridSamplerPadding padding_mode,
144
+ bool align_corners) {
145
+ if (padding_mode == GridSamplerPadding::Border) {
146
+ // clip coordinates to image borders
147
+ coord = clip_coordinates(coord, size);
148
+ } else if (padding_mode == GridSamplerPadding::Reflection) {
149
+ // reflect coordinates by image borders
150
+ if (align_corners) {
151
+ coord = reflect_coordinates(coord, 0, 2*(size - 1));
152
+ } else {
153
+ coord = reflect_coordinates(coord, -1, 2*size - 1);
154
+ }
155
+ // clip coordinates to image borders
156
+ coord = clip_coordinates(coord, size);
157
+ }
158
+ return coord;
159
+ }
160
+
161
+ // Computes the pixel source index value for a grid coordinate
162
+ template <typename scalar_t>
163
+ static inline scalar_t grid_sampler_compute_source_index(
164
+ scalar_t coord,
165
+ int64_t size,
166
+ GridSamplerPadding padding_mode,
167
+ bool align_corners) {
168
+ coord = grid_sampler_unnormalize(coord, size, align_corners);
169
+ coord = compute_coordinates(coord, size, padding_mode, align_corners);
170
+ return coord;
171
+ }
172
+
173
+ // grid_sampler_compute_source_index_set_grad works similarly to
174
+ // grid_sampler_compute_source_index except that it also returns the
175
+ // `d output / d input` via pointer argument `grad_in`.
176
+ // This is useful in the backward pass of grid_sampler.
177
+ template <typename scalar_t>
178
+ static inline scalar_t grid_sampler_compute_source_index_set_grad(
179
+ scalar_t coord,
180
+ int64_t size,
181
+ GridSamplerPadding padding_mode,
182
+ bool align_corners,
183
+ scalar_t *grad_in) {
184
+ scalar_t grad_clip, grad_refl;
185
+ coord = grid_sampler_unnormalize_set_grad(coord, size, align_corners, grad_in);
186
+ if (padding_mode == GridSamplerPadding::Border) {
187
+ // clip coordinates to image borders
188
+ coord = clip_coordinates_set_grad(coord, size, &grad_clip);
189
+ *grad_in = (*grad_in) * grad_clip;
190
+ } else if (padding_mode == GridSamplerPadding::Reflection) {
191
+ // reflect coordinates by image borders
192
+ if (align_corners) {
193
+ coord = reflect_coordinates_set_grad(coord, 0, 2*(size - 1), &grad_refl);
194
+ } else {
195
+ coord = reflect_coordinates_set_grad(coord, -1, 2*size - 1, &grad_refl);
196
+ }
197
+ // clip coordinates to image borders
198
+ coord = clip_coordinates_set_grad(coord, size, &grad_clip);
199
+ *grad_in = (*grad_in) * grad_refl * grad_clip;
200
+ }
201
+ return coord;
202
+ }
203
+
204
+ static inline bool within_bounds_2d(int64_t h, int64_t w, int64_t H, int64_t W) {
205
+ return h >= 0 && h < H && w >= 0 && w < W;
206
+ }
207
+
208
+ static inline bool within_bounds_3d(int64_t d, int64_t h, int64_t w, int64_t D, int64_t H, int64_t W) {
209
+ return d >= 0 && d < D && h >= 0 && h < H && w >= 0 && w < W;
210
+ }
211
+
212
+ template<typename scalar_t>
213
+ static inline scalar_t get_value_bounded(
214
+ const scalar_t* data,
215
+ scalar_t x,
216
+ scalar_t y,
217
+ int64_t W,
218
+ int64_t H,
219
+ int64_t sW,
220
+ int64_t sH,
221
+ GridSamplerPadding padding_mode,
222
+ bool align_corners) {
223
+
224
+ x = compute_coordinates(x, W, padding_mode, align_corners);
225
+ y = compute_coordinates(y, H, padding_mode, align_corners);
226
+
227
+ int64_t ix = static_cast<int64_t>(x);
228
+ int64_t iy = static_cast<int64_t>(y);
229
+
230
+ if (within_bounds_2d(iy, ix, H, W)) {
231
+ return data[iy * sH + ix * sW];
232
+ }
233
+ return static_cast<scalar_t>(0);
234
+ }
235
+
236
+ template<typename scalar_t>
237
+ static inline void safe_add_2d(scalar_t *data, int64_t h, int64_t w,
238
+ int64_t sH, int64_t sW, int64_t H, int64_t W,
239
+ scalar_t delta) {
240
+ if (within_bounds_2d(h, w, H, W)) {
241
+ data[h * sH + w * sW] += delta;
242
+ }
243
+ }
244
+
245
+ template<typename scalar_t>
246
+ static inline void safe_add_3d(scalar_t *data, int64_t d, int64_t h, int64_t w,
247
+ int64_t sD, int64_t sH, int64_t sW,
248
+ int64_t D, int64_t H, int64_t W,
249
+ scalar_t delta) {
250
+ if (within_bounds_3d(d, h, w, D, H, W)) {
251
+ data[d * sD + h * sH + w * sW] += delta;
252
+ }
253
+ }
254
+
255
+ template<typename scalar_t>
256
+ static inline void add_value_bounded(
257
+ scalar_t* data,
258
+ scalar_t x,
259
+ scalar_t y,
260
+ int64_t W,
261
+ int64_t H,
262
+ int64_t sW,
263
+ int64_t sH,
264
+ scalar_t delta,
265
+ GridSamplerPadding padding_mode,
266
+ bool align_corners) {
267
+
268
+ x = compute_coordinates(x, W, padding_mode, align_corners);
269
+ y = compute_coordinates(y, H, padding_mode, align_corners);
270
+
271
+ int64_t ix = static_cast<int64_t>(x);
272
+ int64_t iy = static_cast<int64_t>(y);
273
+
274
+ safe_add_2d(data, iy, ix, sH, sW, H, W, delta);
275
+ }
276
+
277
+ // Calculate the differential of the cubic convolution, i.e. `d coeff / d x`
278
+ template<typename scalar_t>
279
+ static inline void get_cubic_coefficients_grad(
280
+ scalar_t coeffs[4],
281
+ scalar_t t) {
282
+
283
+ // Must be the same as forward calculation in
284
+ // aten/src/ATen/native/UpSample.h:get_cubic_upsample_coefficients
285
+ scalar_t A = -0.75;
286
+
287
+ scalar_t x;
288
+ x = -1 - t; // 1 < x = |-1 - tx| < 2
289
+ coeffs[0] = (-3 * A * x - 10 * A ) * x - 8 * A;
290
+ x = -t; // x = |0 - tx| <= 1
291
+ coeffs[1] = (-3 * (A + 2) * x - 2 * (A + 3)) * x;
292
+ x = 1 - t; // x = |1 - tx| <= 1
293
+ coeffs[2] = (3 * (A + 2) * x - 2 * (A + 3)) * x;
294
+ x = 2 - t; // 1 < x = |2 - tx| < 2
295
+ coeffs[3] = (3 * A * x - 10 * A) * x + 8 * A;
296
+ }
297
+
298
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/GridSamplerUtils.h ADDED
@@ -0,0 +1,105 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ // See NOTE: [Tensor vs. TensorBase]
4
+ // https://github.com/pytorch/pytorch/pull/66979
5
+ #include <ATen/core/TensorBase.h>
6
+ #include <ATen/native/TensorProperties.h>
7
+ #include <ATen/native/CanUse32BitIndexMath.h>
8
+
9
+ namespace at::native {
10
+
11
+ namespace detail {
12
+
13
+ enum class GridSamplerInterpolation {Bilinear, Nearest, Bicubic};
14
+ enum class GridSamplerPadding {Zeros, Border, Reflection};
15
+
16
+ } // namespace detail
17
+
18
+ using detail::GridSamplerInterpolation;
19
+ using detail::GridSamplerPadding;
20
+
21
+ // See NOTE [ grid_sampler Native Functions ].
22
+ inline void check_grid_sampler_common(
23
+ const TensorBase& input,
24
+ const TensorBase& grid
25
+ ) {
26
+ auto input_opt = input.options();
27
+ auto grid_opt = grid.options();
28
+
29
+ TORCH_CHECK(
30
+ input.defined(),
31
+ "grid_sampler(): expected input to not be undefined");
32
+ TORCH_CHECK(
33
+ grid.defined(),
34
+ "grid_sampler(): expected grid to not be undefined");
35
+ TORCH_CHECK(
36
+ input_opt.device() == grid_opt.device(),
37
+ "grid_sampler(): expected input and grid to be on same device, but input "
38
+ "is on ", input_opt.device(), " and grid is on ", grid_opt.device());
39
+ TORCH_CHECK(
40
+ input_opt.layout() == kStrided && grid_opt.layout() == kStrided,
41
+ "grid_sampler(): expected input and grid to have torch.strided layout, but "
42
+ "input has ", input_opt.layout(), " and grid has ", grid_opt.layout());
43
+ TORCH_CHECK(
44
+ input.size(0) == grid.size(0),
45
+ "grid_sampler(): expected grid and input to have same batch size, but got "
46
+ "input with sizes ", input.sizes(), " and grid with sizes ", grid.sizes());
47
+ TORCH_CHECK(
48
+ grid.size(-1) == input.dim() - 2,
49
+ "grid_sampler(): expected grid to have size ", input.dim() - 2, " in last "
50
+ "dimension, but got grid with sizes ", grid.sizes());
51
+
52
+ for (const auto i : c10::irange(2, input.dim())) {
53
+ TORCH_CHECK(input.size(i) > 0,
54
+ "grid_sampler(): expected input to have non-empty spatial dimensions, "
55
+ "but input has sizes ", input.sizes(), " with dimension ", i, " being "
56
+ "empty");
57
+ }
58
+ }
59
+
60
+ // See NOTE [ grid_sampler Native Functions ].
61
+ inline void check_grid_sampler_2d(
62
+ const TensorBase& input,
63
+ const TensorBase& grid
64
+ ) {
65
+ TORCH_CHECK(
66
+ input.dim() == 4 && input.dim() == grid.dim(),
67
+ "grid_sampler(): expected 4D input and grid with same number of "
68
+ "dimensions, but got input with sizes ", input.sizes(),
69
+ " and grid with sizes ", grid.sizes());
70
+ }
71
+
72
+ // See NOTE [ grid_sampler Native Functions ].
73
+ inline void check_grid_sampler_3d(
74
+ const TensorBase& input,
75
+ const TensorBase& grid,
76
+ int64_t interpolation_mode
77
+ ) {
78
+ TORCH_CHECK(
79
+ input.dim() == 5 && input.dim() == grid.dim(),
80
+ "grid_sampler(): expected 5D input and grid with same number of "
81
+ "dimensions, but got input with sizes ", input.sizes(),
82
+ " and grid with sizes ", grid.sizes());
83
+ TORCH_CHECK(
84
+ !(input.dim() == 5 &&
85
+ static_cast<GridSamplerInterpolation>(interpolation_mode) ==
86
+ GridSamplerInterpolation::Bicubic),
87
+ "grid_sampler(): bicubic interpolation only supports 4D input");
88
+ }
89
+
90
+ // See NOTE [ grid_sampler Native Functions ].
91
+ // cudnn does not support inputs larger than 1024.
92
+ inline bool cond_cudnn_grid_sampler(
93
+ const TensorBase& input,
94
+ const TensorBase& grid
95
+ ) {
96
+ return (
97
+ at::native::cudnn_is_acceptable(input) &&
98
+ at::native::cudnn_is_acceptable(grid) &&
99
+ at::native::canUse32BitIndexMath(input) &&
100
+ at::native::canUse32BitIndexMath(grid) &&
101
+ input.dim() == 4 &&
102
+ input.sym_size(1) <= 1024);
103
+ }
104
+
105
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/Histogram.h ADDED
@@ -0,0 +1,16 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/core/Tensor.h>
4
+ #include <ATen/native/DispatchStub.h>
5
+
6
+ namespace at::native {
7
+
8
+ using histogramdd_fn = void(*)(const Tensor&, const std::optional<Tensor>&, bool, Tensor&, const TensorList&);
9
+ using histogramdd_linear_fn = void(*)(const Tensor&, const std::optional<Tensor>&, bool, Tensor&, const TensorList&, bool);
10
+ using histogram_select_outer_bin_edges_fn = void(*)(const Tensor& input, const int64_t N, std::vector<double> &leftmost_edges, std::vector<double> &rightmost_edges);
11
+
12
+ DECLARE_DISPATCH(histogramdd_fn, histogramdd_stub)
13
+ DECLARE_DISPATCH(histogramdd_linear_fn, histogramdd_linear_stub)
14
+ DECLARE_DISPATCH(histogram_select_outer_bin_edges_fn, histogram_select_outer_bin_edges_stub)
15
+
16
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/IndexKernel.h ADDED
@@ -0,0 +1,41 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/native/DispatchStub.h>
3
+ #include <c10/util/ArrayRef.h>
4
+
5
+ namespace at {
6
+ class Tensor;
7
+ class TensorBase;
8
+ struct TensorIterator;
9
+ struct TensorIteratorBase;
10
+ }
11
+
12
+ namespace c10 {
13
+ class Scalar;
14
+ }
15
+
16
+ namespace at::native {
17
+
18
+ using index_fn = void(*)(TensorIteratorBase &, IntArrayRef indexed_sizes, IntArrayRef indexed_strides);
19
+ using index_fill_fn = void(*)(TensorIterator & iter, int64_t dim, int64_t self_dim_size, int64_t self_dim_stride, const Scalar& source);
20
+ using index_copy_fn = void(*)(TensorIterator & iter, int64_t dim, int64_t self_dim_size, int64_t self_dim_stride);
21
+ using index_put_fn = void(*)(TensorIterator &, IntArrayRef indexed_sizes, IntArrayRef indexed_strides, bool accumulate);
22
+ using put_fn = void(*)(TensorIterator & iter, const TensorBase& self, const bool accumulate);
23
+ using take_fn = void(*)(TensorIterator & iter, const TensorBase& input);
24
+ using flip_fn = void(*)(TensorIterator &, const bool);
25
+ using masked_fill_fn = void(*)(TensorIterator &, const Scalar& scalar);
26
+ using masked_select_fn = void(*)(TensorIterator &, int64_t orig_stride);
27
+ using masked_scatter_fn = void(*)(TensorIterator &, const TensorBase &);
28
+
29
+ DECLARE_DISPATCH(index_fn, index_stub)
30
+ DECLARE_DISPATCH(index_fill_fn, index_fill_stub)
31
+ DECLARE_DISPATCH(index_copy_fn, index_copy_stub)
32
+ DECLARE_DISPATCH(index_put_fn, index_put_stub)
33
+ DECLARE_DISPATCH(put_fn, put_stub)
34
+ DECLARE_DISPATCH(take_fn, take_stub)
35
+ DECLARE_DISPATCH(flip_fn, flip_stub)
36
+ DECLARE_DISPATCH(masked_fill_fn, masked_fill_stub)
37
+ DECLARE_DISPATCH(masked_select_fn, masked_select_serial_stub)
38
+ DECLARE_DISPATCH(masked_select_fn, masked_select_stub)
39
+ DECLARE_DISPATCH(masked_scatter_fn, masked_scatter_stub)
40
+
41
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/IndexingUtils.h ADDED
@@ -0,0 +1,164 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+ #include <ATen/ExpandUtils.h>
3
+ #include <ATen/native/CanUse32BitIndexMath.h>
4
+ #include <ATen/native/TensorIterator.h>
5
+ #include <ATen/core/IListRef.h>
6
+ #include <c10/util/irange.h>
7
+
8
+ namespace at::native {
9
+
10
+ [[noreturn]]
11
+ static void invalid_mask(const Tensor & self, int64_t idx, const Tensor & mask, int64_t maskIdx) {
12
+ TORCH_CHECK_INDEX(false, "The shape of the mask ", mask.sizes(), " at index ", maskIdx,
13
+ " does not match the shape of the indexed tensor ", self.sizes(), " at index ", idx);
14
+ }
15
+
16
+ [[maybe_unused]] static std::vector<Tensor> expandTensors(
17
+ const Tensor& self,
18
+ IOptTensorListRef indices) {
19
+ // If indices come in as ByteTensor or BoolTensor (masks), expand them into
20
+ // the equivalent indexing by LongTensors
21
+ std::vector<Tensor> result;
22
+ for (const auto& index_opt : indices) {
23
+ if (!index_opt.has_value()) {
24
+ result.emplace_back();
25
+ } else {
26
+ const auto& index = *index_opt;
27
+ if (index.scalar_type() == kByte || index.scalar_type() == kBool) {
28
+ if (index.scalar_type() == kByte) {
29
+ TORCH_WARN("indexing with dtype torch.uint8 is now deprecated," \
30
+ " please use a dtype torch.bool instead.");
31
+ }
32
+ // The sizes of the ByteTensor mask or bool tensor must match the sizes of the
33
+ // corresponding dimensions in self
34
+ for (const auto j : c10::irange(index.dim())) {
35
+ int64_t srcIdx = static_cast<int64_t>(result.size() + j);
36
+ if (index.size(j) != self.size(srcIdx)) {
37
+ invalid_mask(self, srcIdx, index, j);
38
+ }
39
+ }
40
+ // Replace with nonzeros
41
+ auto nonzero = index.nonzero();
42
+ for (const auto j : c10::irange(index.dim())) {
43
+ result.emplace_back(nonzero.select(1, j));
44
+ }
45
+ } else {
46
+ result.emplace_back(index);
47
+ }
48
+ }
49
+ }
50
+ return result;
51
+ }
52
+
53
+ [[maybe_unused]] static void checkIndexTensorTypes(
54
+ IOptTensorListRef indices,
55
+ bool allow_int = false) {
56
+ for (const auto& tensor : indices) {
57
+ if (tensor.has_value() && tensor->defined()) {
58
+ auto scalarType = tensor->scalar_type();
59
+ if (allow_int) {
60
+ if (scalarType != kLong && scalarType != kByte && scalarType != kBool && scalarType != kInt) {
61
+ TORCH_CHECK_INDEX(false, "tensors used as indices must be long, int, byte or bool tensors");
62
+ }
63
+ } else {
64
+ if (scalarType != kLong && scalarType != kByte && scalarType != kBool) {
65
+ TORCH_CHECK_INDEX(false, "tensors used as indices must be long, byte or bool tensors");
66
+ }
67
+ }
68
+ }
69
+ }
70
+ }
71
+
72
+ inline torch::List<std::optional<Tensor>> toListOfOptionalTensors(ArrayRef<Tensor> list) {
73
+ torch::List<std::optional<Tensor>> result;
74
+ result.reserve(list.size());
75
+ for (const Tensor& a : list) {
76
+ result.push_back(a);
77
+ }
78
+ return result;
79
+ }
80
+
81
+ inline torch::List<std::optional<Tensor>> toListOfOptionalTensors(ArrayRef<IValue> list) {
82
+ torch::List<std::optional<Tensor>> result;
83
+ result.reserve(list.size());
84
+ for (const IValue& a : list) {
85
+ result.push_back(a.isTensor() ? std::optional<Tensor>(a.toTensor()) : std::optional<Tensor>());
86
+ }
87
+ return result;
88
+ }
89
+
90
+ [[maybe_unused]] static bool hasContiguousSubspace(TensorList tl) {
91
+ // true if all the non-null tensors are adjacent
92
+ auto isDefined = [](const Tensor & tensor){ return tensor.defined(); };
93
+ auto isNull = [](const Tensor & tensor){ return !tensor.defined(); };
94
+ auto start = std::find_if(tl.begin(), tl.end(), isDefined);
95
+ auto stop = std::find_if(tl.rbegin(), tl.rend(), isDefined);
96
+ auto it = std::find_if(start, stop.base(), isNull);
97
+ return it == stop.base();
98
+ }
99
+
100
+ // Transposes the tensor and indices together so that all the non-null indices
101
+ // index the first k dimensions of the tensor. Returns the transposed tensor
102
+ // and the reordered indices. For example:
103
+ // transposeToFront(tensor, {nullptr, a, nullptr, b})
104
+ // returns
105
+ // tensor.permute([1, 3, 0, 2]), {a, b, nullptr, nullptr}
106
+ [[maybe_unused]] static std::tuple<Tensor, std::vector<Tensor>> transposeToFront(
107
+ const Tensor& self,
108
+ TensorList indices) {
109
+ std::vector<int64_t> dims;
110
+ std::vector<Tensor> transposedIndices;
111
+ dims.reserve(self.dim());
112
+ for (const auto i : c10::irange(self.dim())) {
113
+ if (indices[i].defined()) {
114
+ dims.push_back(i);
115
+ transposedIndices.emplace_back(indices[i]);
116
+ }
117
+ }
118
+ for (const auto i : c10::irange(self.dim())) {
119
+ if (!indices[i].defined()) {
120
+ dims.push_back(i);
121
+ transposedIndices.emplace_back();
122
+ }
123
+ }
124
+ return std::make_tuple(self.permute(dims), std::move(transposedIndices));
125
+ }
126
+
127
+ inline std::tuple<Tensor, std::vector<Tensor>, std::vector<int64_t>>
128
+ transposeToFrontAndInvPerm(const Tensor& self, TensorList indices) {
129
+ std::vector<int64_t> dims;
130
+ std::vector<int64_t> invPerm;
131
+ std::vector<Tensor> transposedIndices;
132
+ dims.reserve(self.dim());
133
+ invPerm.resize(self.dim());
134
+ for (const auto i : c10::irange(self.dim())) {
135
+ if (indices[i].defined()) {
136
+ dims.push_back(i);
137
+ transposedIndices.emplace_back(indices[i]);
138
+ }
139
+ }
140
+ for (const auto i : c10::irange(self.dim())) {
141
+ if (!indices[i].defined()) {
142
+ dims.push_back(i);
143
+ transposedIndices.emplace_back();
144
+ }
145
+ }
146
+ for (const auto i : c10::irange(self.dim())) {
147
+ invPerm[dims[i]] = i;
148
+ }
149
+ return std::make_tuple(self.permute(dims), std::move(transposedIndices), std::move(invPerm));
150
+ }
151
+
152
+ struct AdvancedIndex {
153
+ AdvancedIndex(const Tensor& src, TensorList indices);
154
+
155
+ Tensor src;
156
+ std::vector<Tensor> indices;
157
+ DimVector indexed_sizes;
158
+ DimVector indexed_strides;
159
+ int64_t dims_before;
160
+ int64_t dims_after;
161
+ };
162
+
163
+
164
+ } //namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/Lerp.h ADDED
@@ -0,0 +1,46 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+ #include <ATen/OpMathType.h>
5
+ #include <ATen/TensorIterator.h>
6
+ #include <c10/core/Scalar.h>
7
+
8
+ namespace at::native {
9
+
10
+ template <typename scalar_t>
11
+ C10_HOST_DEVICE C10_ALWAYS_INLINE bool is_lerp_weight_small(scalar_t weight) {
12
+ return std::abs(weight) < scalar_t(0.5);
13
+ }
14
+ template <typename scalar_t>
15
+ C10_HOST_DEVICE C10_ALWAYS_INLINE bool is_lerp_weight_small(c10::complex<scalar_t> weight) {
16
+ // Avoid the sqrt in abs(weight)
17
+ return (weight.real() * weight.real() + weight.imag() * weight.imag()) < scalar_t(0.25);
18
+ }
19
+
20
+ template <typename scalar_t, typename weight_t>
21
+ C10_HOST_DEVICE C10_ALWAYS_INLINE scalar_t lerp(scalar_t self_, scalar_t end_, weight_t weight_) {
22
+ using opmath_t = at::opmath_type<scalar_t>;
23
+ using opmath_weight_t = at::opmath_type<weight_t>;
24
+
25
+ opmath_t self = self_;
26
+ opmath_t end = end_;
27
+ opmath_weight_t weight = weight_;
28
+
29
+ // Conditional for better numeric. This has been discussed in
30
+ // https://github.com/pytorch/pytorch/pull/18871
31
+ return is_lerp_weight_small(weight)
32
+ ? self + weight * (end - self)
33
+ : end - (end - self) * (opmath_t(1) - weight);
34
+ }
35
+
36
+ using lerp_fn_scalar = void (*)(
37
+ at::TensorIteratorBase& iter,
38
+ const Scalar& weight);
39
+
40
+ using lerp_fn_tensor = void (*)(
41
+ at::TensorIteratorBase& iter);
42
+
43
+ DECLARE_DISPATCH(lerp_fn_scalar, lerp_kernel_scalar_weight)
44
+ DECLARE_DISPATCH(lerp_fn_tensor, lerp_kernel_tensor_weight)
45
+
46
+ } // namespace at::native
lib/python3.12/site-packages/torch/include/ATen/native/LinearAlgebra.h ADDED
@@ -0,0 +1,17 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ #pragma once
2
+
3
+ #include <ATen/native/DispatchStub.h>
4
+
5
+ namespace c10 {
6
+ class Scalar;
7
+ }
8
+
9
+ namespace at {
10
+ struct TensorIterator;
11
+ }
12
+
13
+ namespace at::native {
14
+
15
+ using addr_fn = void (*)(TensorIterator &, const Scalar& beta, const Scalar& alpha);
16
+ DECLARE_DISPATCH(addr_fn, addr_stub)
17
+ } // namespace at::native