Caffe2 - C++ API
A deep learning, cross platform ML framework
tensor_apply.cpp
1 #include <torch/csrc/utils/tensor_apply.h>
2 
3 #include <ATen/TensorUtils.h>
4 #include <ATen/ExpandUtils.h>
5 
6 #include <torch/csrc/Exceptions.h>
7 #include <torch/csrc/utils/python_numbers.h>
8 #include <torch/csrc/utils/python_scalars.h>
9 
10 using namespace at;
11 
12 namespace torch { namespace utils {
13 
14 struct StridedData {
15  StridedData(const Tensor & tensor)
16  : data(tensor.data_ptr())
17  , strides(tensor.strides())
18  , elementSize(tensor.element_size()) {}
19 
20  void* data;
21  IntArrayRef strides;
22  int64_t elementSize;
23 
24  void step(int dim) {
25  data = (char*)data + (strides[dim] * elementSize);
26  }
27 };
28 
29 template<size_t N>
30 static void recursive_apply(IntArrayRef sizes, ScalarType scalarType, int64_t dim,
31  PyObject* fn, std::array<StridedData, N> strided_data) {
32  int64_t ndim = sizes.size();
33  if (dim == ndim) {
34  auto args = THPObjectPtr(PyTuple_New(N));
35  if (!args) throw python_error();
36  for (size_t i = 0; i < N; i++) {
37  PyObject* arg = load_scalar(strided_data[i].data, scalarType);
38  if (!arg) throw python_error();
39  PyTuple_SET_ITEM(args.get(), i, arg);
40  }
41  auto ret = THPObjectPtr(PyObject_CallObject(fn, args.get()));
42  if (!ret) throw python_error();
43  store_scalar(strided_data[0].data, scalarType, ret.get());
44  return;
45  }
46 
47  auto n = sizes[dim];
48  for (int64_t i = 0; i < n; i++) {
49  recursive_apply(sizes, scalarType, dim + 1, fn, strided_data);
50  for (auto& td : strided_data) {
51  td.step(dim);
52  }
53  }
54 }
55 
56 Tensor & apply_(Tensor & self, PyObject* fn) {
57  if (self.type().backend() != Backend::CPU) {
58  throw TypeError("apply_ is only implemented on CPU tensors");
59  }
60  auto scalarType = self.scalar_type();
61  recursive_apply<1>(self.sizes(), scalarType, 0, fn, {{ self }});
62  return self;
63 }
64 
65 Tensor & map_(Tensor & self, const Tensor & other_, PyObject* fn) {
66  if (self.type().backend() != Backend::CPU) {
67  throw TypeError("map_ is only implemented on CPU tensors");
68  }
69  if (other_.type() != self.type()) {
70  throw TypeError("map_: expected %s for 'other' (got %s)",
71  self.type().toString(), other_.type().toString());
72  }
73  Tensor other;
74  std::tie(other) = expand_inplace(self, other_, "map_");
75  auto scalarType = self.scalar_type();
76  recursive_apply<2>(self.sizes(), scalarType, 0, fn, {{ self, other }});
77  return self;
78 }
79 
80 Tensor & map2_(Tensor & self, const Tensor & x_, const Tensor & y_, PyObject* fn) {
81  if (self.type().backend() != Backend::CPU || x_.type().backend() != Backend::CPU || y_.type().backend() != Backend::CPU) {
82  throw TypeError("map2_ is only implemented on CPU tensors");
83  }
84  if (x_.type() != self.type()) {
85  throw TypeError("map2_: expected %s for argument 'x' (got %s)",
86  self.type().toString(), x_.type().toString());
87  }
88  if (y_.type() != self.type()) {
89  throw TypeError("map2_: expected %s for argument 'y' (got %s)",
90  self.type().toString(), y_.type().toString());
91  }
92  Tensor other1, other2;
93  std::tie(other1, other2) = expand_inplace(self, x_, y_, "map2_");
94  auto scalarType = self.scalar_type();
95  recursive_apply<3>(self.sizes(), scalarType, 0, fn, {{ self, other1, other2 }});
96  return self;
97 }
98 
99 }} // namespace torch::utils
constexpr size_t size() const
size - Get the array size.
Definition: ArrayRef.h:138
Definition: jit_type.h:17
Flush-To-Zero and Denormals-Are-Zero mode.