Caffe2 - C++ API
A deep learning, cross platform ML framework
Tensor.h
1 #pragma once
2 
3 #include <c10/core/Device.h>
4 #include <c10/core/Layout.h>
5 #include <c10/core/Scalar.h>
6 #include <c10/core/ScalarType.h>
7 #include <ATen/core/SparseTensorRef.h>
8 #include <c10/core/Storage.h>
9 #include <ATen/core/TensorAccessor.h>
10 #include <c10/core/TensorImpl.h>
11 #include <c10/core/UndefinedTensorImpl.h>
12 #include <c10/util/Exception.h>
13 #include <c10/util/Optional.h>
14 #include <c10/core/Tensor.h>
15 #include <ATen/core/LegacyTypeDispatch.h>
16 
17 namespace c10{
18 struct TensorOptions;
19 }
20 namespace at {
21 struct Generator;
22 struct Type;
23 class Tensor;
24 } // namespace at
25 
26 namespace at {
27 
28 class Tensor;
29 using TensorList = ArrayRef<Tensor>;
30 
31 // Tensor is a "generic" object holding a pointer to the underlying TensorImpl object, which
32 // has an embedded reference count. In this way, Tensor is similar to boost::intrusive_ptr.
33 //
34 // For example:
35 //
36 // void func(Tensor a) {
37 // Tensor b = a;
38 // ...
39 // }
40 //
41 // In this example, when we say Tensor b = a, we are creating a new object that points to the
42 // same underlying TensorImpl, and bumps its reference count. When b goes out of scope, the
43 // destructor decrements the reference count by calling release() on the TensorImpl it points to.
44 // The existing constructors, operator overloads, etc. take care to implement the correct semantics.
45 //
46 // Note that Tensor can also be NULL, i.e. it is not associated with any underlying TensorImpl, and
47 // special care must be taken to handle this.
48 class CAFFE2_API Tensor {
49  public:
50  Tensor(){};
51  // This constructor should not be used by end users and is an implementation
52  // detail invoked by autogenerated code.
53  explicit Tensor(
55  : impl_(std::move(tensor_impl)) {
56  if (impl_.get() == nullptr) {
57  throw std::runtime_error("TensorImpl with nullptr is not supported");
58  }
59  }
60  Tensor(const Tensor&) = default;
61  Tensor(Tensor&&) = default;
62 
63 
64  public:
65  // Creates a new wrapper from TensorImpl. Intentionally a free method because
66  // it should be used with care. Checks necessary invariants
67  static Tensor wrap_tensor_impl(
69  Tensor r(std::move(tensor_impl));
70  r.enforce_invariants();
71  return r;
72  }
73 
74  explicit Tensor(C10Tensor tensor) : impl_(std::move(tensor).impl()) {
75  enforce_invariants();
76  }
77 
78  explicit operator C10Tensor() const & {
79  return C10Tensor(impl_);
80  }
81 
82  explicit operator C10Tensor() && {
83  return C10Tensor(std::move(impl_));
84  }
85 
86  int64_t dim() const {
87  return impl_->dim();
88  }
89  int64_t storage_offset() const {
90  return impl_->storage_offset();
91  }
92 
93  TensorImpl * unsafeGetTensorImpl() const {
94  return impl_.get();
95  }
96  TensorImpl * unsafeReleaseTensorImpl() {
97  return impl_.release();
98  }
99  const c10::intrusive_ptr<TensorImpl, UndefinedTensorImpl>& getIntrusivePtr() const {
100  return impl_;
101  }
102 
103  bool defined() const {
104  return impl_;
105  }
106 
107  void reset() {
108  impl_.reset();
109  }
110 
111  // The following overloads are very intruiging. Consider the following
112  // program:
113  //
114  // x[1] = 3;
115  //
116  // We would expect that the first entry of x is written to 3. But how can we
117  // actually achieve this? x[1] evaluates to a tensor...
118  //
119  // The answer is, using a ref-qualifier. x[1] is an rvalue, which cannot be
120  // (profitably) assigned to in the traditional sense, so we overload
121  // assignment to mean, "Actually, copy 3 into the tensor data." This is done
122  // with an rvalue-reference ref-qualified overload (the methods with && at the
123  // end of their type.)
124  //
125  // There's one more fly in the ointment: We also want
126  //
127  // Tensor x = y;
128  //
129  // to work, and we want it NOT to copy. So we need a traditional operator=
130  // overload. But we MUST specify a mutable lvalue ref-qualifier, to
131  // disambiguate the traditional overload from the rvalue-reference
132  // ref-qualified overload. Otherwise, it will be ambiguous, because
133  // a non ref-qualified method is eligible for all situations.
134 
135  // Unfortunately, we have to write these constructors out manually
136  // to work around an MSVC bug:
137  // error C2580: 'at::Tensor &at::Tensor::operator =(const at::Tensor &) &':
138  // multiple versions of a defaulted special member functions are not allowed
139  // Tensor& operator=(const Tensor&) & = default;
140  // Tensor& operator=(Tensor&&) & = default;
141  Tensor& operator=(const Tensor& x) & {
142  impl_ = x.impl_;
143  return *this;
144  }
145  Tensor& operator=(Tensor&& x) & {
146  impl_ = std::move(x.impl_);
147  return *this;
148  }
149 
150  Tensor& operator=(Scalar v) &&;
151  Tensor& operator=(const Tensor&) &&;
152  Tensor& operator=(Tensor&&) &&;
153 
154  bool is_same(const Tensor& other) const noexcept {
155  return impl_ == other.impl_;
156  }
157  size_t use_count() const noexcept {
158  return impl_.use_count();
159  }
160  size_t weak_use_count() const noexcept {
161  return impl_.weak_use_count();
162  }
163 
164  const char * toString() const;
165 
166  IntArrayRef sizes() const {
167  return impl_->sizes();
168  }
169  IntArrayRef strides() const {
170  return impl_->strides();
171  }
172  int64_t ndimension() const {
173  return dim();
174  }
175  bool is_contiguous() const {
176  return impl_->is_contiguous();
177  }
178 
179  // Total bytes consumed by the "view" of elements of the array. Does not
180  // include size of metadata. The number reported here does not necessarily
181  // correspond to the true physical memory consumed by a tensor; instead,
182  // it reports the memory the tensor would take *if* it were contiguous.
183  // Defined to be numel() * itemsize()
184  size_t nbytes() const {
185  return impl_->numel() * impl_->itemsize();
186  }
187 
188  // Length of one array element in bytes. This is the traditional
189  // Numpy naming.
190  size_t itemsize() const {
191  return impl_->itemsize();
192  }
193 
194  // Same as itemsize(). This is the PyTorch naming.
195  size_t element_size() const {
196  return impl_->itemsize();
197  }
198 
199  Type & type() const {
200  return legacyTensorType(*impl_);
201  }
202  TensorTypeId type_id() const {
203  return impl_->type_id();
204  }
205  ScalarType scalar_type() const {
206  return typeMetaToScalarType(impl_->dtype());
207  }
208  bool has_storage() const {
209  return defined() && impl_->has_storage();
210  }
211  const Storage& storage() const {
212  return impl_->storage();
213  }
214  bool is_alias_of(const at::Tensor& other) const{
215  return impl_->storage().is_alias_of(other.storage());
216  }
217  Tensor toType(const Type & t, bool non_blocking=false) const;
218  Tensor & copy_(const Tensor & src, bool non_blocking=false);
219  Tensor toType(ScalarType t) const;
220  Tensor toBackend(Backend b) const;
221 
224  bool is_variable() const noexcept;
225 
227  Layout layout() const noexcept;
228 
230  caffe2::TypeMeta dtype() const noexcept;
231 
233  Device device() const;
234 
236  int64_t get_device() const;
237 
239  bool is_cuda() const;
240 
242  bool is_hip() const;
243 
245  bool is_sparse() const;
246 
249  TensorOptions options() const;
250 
251  template<typename T>
252  T * data() const;
253 
254  template <typename T>
255  T item() const;
256 
257  // Purposely not defined here to avoid inlining
258  void print() const;
259 
260  // Return a `TensorAccessor` for CPU `Tensor`s. You have to specify scalar type and
261  // dimension.
262  template<typename T, size_t N>
263  TensorAccessor<T,N> accessor() const& {
264  static_assert(N > 0, "accessor is used for indexing tensor, for scalars use *data<T>()");
265  AT_CHECK(dim() == N, "expected ", N, " dims but tensor has ", dim());
266  return TensorAccessor<T,N>(data<T>(),sizes().data(),strides().data());
267  }
268  template<typename T, size_t N>
269  TensorAccessor<T,N> accessor() && = delete;
270 
271  // Return a `PackedTensorAccessor` for CUDA `Tensor`s. You have to specify scalar type and
272  // dimension. You can optionally specify RestrictPtrTraits as a template parameter to
273  // cast the data pointer to a __restrict__ pointer.
274  // In order to use this, your CUDA kernel has to take a corresponding PackedTensorAccessor
275  // as an argument.
276  template<typename T, size_t N, template <typename U> class PtrTraits = DefaultPtrTraits, typename index_t = int64_t>
277  PackedTensorAccessor<T,N,PtrTraits,index_t> packed_accessor() const& {
278  static_assert(N > 0, "accessor is used for indexing tensor, for scalars use *data<T>()");
279  AT_CHECK(dim() == N, "expected ", N, " dims but tensor has ", dim());
280  return PackedTensorAccessor<T,N,PtrTraits,index_t>(static_cast<typename PtrTraits<T>::PtrType>(data<T>()),sizes().data(),strides().data());
281  }
282  template<typename T, size_t N, template <typename U> class PtrTraits = DefaultPtrTraits, typename index_t = int64_t>
283  PackedTensorAccessor<T,N> packed_accessor() && = delete;
284 
285  Tensor operator-() const;
286  Tensor& operator+=(const Tensor & other);
287  Tensor& operator+=(Scalar other);
288  Tensor& operator-=(const Tensor & other);
289  Tensor& operator-=(Scalar other);
290  Tensor& operator*=(const Tensor & other);
291  Tensor& operator*=(Scalar other);
292  Tensor& operator/=(const Tensor & other);
293  Tensor& operator/=(Scalar other);
294  Tensor operator[](Scalar index) const;
295  Tensor operator[](Tensor index) const;
296  Tensor operator[](int64_t index) const;
297 
298  Tensor cpu() const;
299  Tensor cuda() const;
300  Tensor hip() const;
301 
302  // ~~~~~ Autograd API ~~~~~
303 
304  Tensor& set_requires_grad(bool requires_grad) {
305  impl_->set_requires_grad(requires_grad);
306  return *this;
307  }
308  bool requires_grad() const {
309  return impl_->requires_grad();
310  }
311 
312  Tensor& grad() {
313  return impl_->grad();
314  }
315  const Tensor& grad() const {
316  return impl_->grad();
317  }
318 
319  void set_data(Tensor new_data);
320 
322  void backward(
323  c10::optional<Tensor> gradient = c10::nullopt,
324  bool keep_graph = false,
325  bool create_graph = false);
326 
327  // STOP. Thinking of adding a method here, which only makes use
328  // of other ATen methods? Define it in native_functions.yaml.
329 
330  //example
331  //Tensor * add(Tensor & b);
332  ${tensor_method_declarations}
333 
334  // We changed .dtype() to return a TypeMeta in #12766. Ideally, we want the
335  // at::kDouble and its friends to be TypeMeta's, but that hasn't happened yet.
336  // Before that change, we make this method to maintain BC for C++ usage like
337  // `x.to(y.dtype)`.
338  // TODO: remove following two after at::kDouble and its friends are TypeMeta's.
339  inline Tensor to(caffe2::TypeMeta type_meta, bool non_blocking=false, bool copy=false) const {
340  return this->to(/*scalar_type=*/typeMetaToScalarType(type_meta), non_blocking, copy);
341  }
342  inline Tensor to(Device device, caffe2::TypeMeta type_meta, bool non_blocking=false, bool copy=false) const {
343  return this->to(device, /*scalar_type=*/typeMetaToScalarType(type_meta), non_blocking, copy);
344  }
345 
346  template <typename F, typename... Args>
347  auto m(F func, Args&&... params) const -> decltype(func(*this, std::forward<Args>(params)...)) {
348  return func(*this, std::forward<Args>(params)...);
349  }
350 
351  friend struct WeakTensor;
352 
353 protected:
354  void enforce_invariants();
356 };
357 
358 struct CAFFE2_API WeakTensor {
359  WeakTensor(const Tensor& t) : weak_impl_(t.impl_) {}
360 
361  // XXX: this can return undefined tensors
362  // Ideally it would be c10::optional<Tensor>, but MSVC is too cool for that
363  Tensor lock() const {
364  return Tensor(weak_impl_.lock());
365  }
366 
367  bool is_same(const WeakTensor& other) const noexcept {
368  return weak_impl_ == other.weak_impl_;
369  }
370 
371  size_t use_count() const noexcept {
372  return weak_impl_.use_count();
373  }
374  size_t weak_use_count() const noexcept {
375  return weak_impl_.weak_use_count();
376  }
377 
378  TensorImpl* unsafeGetTensorImpl() const {
379  return weak_impl_._unsafe_get_target();
380  }
381 
382 private:
384 };
385 
386 namespace detail {
387 // Helper creator for Tensor clas which doesn't requires the users to pass
388 // in an intrusive_ptr instead it just converts the argument passed to
389 // requested intrusive_ptr type.
390 template <typename T, typename... Args>
391 Tensor make_tensor(Args&&... args) {
392  return Tensor(c10::make_intrusive<T>(std::forward<Args>(args)...));
393 }
394 } // namespace detail
395 
396 } // namespace at
397 
398 #include <ATen/core/TensorMethods.h>
TensorOptions device(Device device)
Convenience function that returns a TensorOptions object with the device set to the given one...
Backend
This legacy enum class defines the set of backends supported by old school, code generated Type-based...
Definition: Backend.h:23
TensorOptions(T &&device)
A class to encapsulate construction axes of an Tensor.
Definition: TensorOptions.h:80
A global dictionary that holds information about what Caffe2 modules have been loaded in the current ...
Definition: blob.h:13
TensorOptions layout(Layout layout)
Convenience function that returns a TensorOptions object with the layout set to the given one...
To register your own kernel for an operator, do in one (!) cpp file: C10_REGISTER_KERNEL(OperatorHand...
Definition: alias_info.h:7
Type & legacyTensorType(const TensorImpl &tensor)
Return the Type object corresponding to this Tensor, which we can use to do dynamic dispatch to opera...
TensorOptions requires_grad(bool requires_grad=true)
Convenience function that returns a TensorOptions object with the requires_grad set to the given one...
Flush-To-Zero and Denormals-Are-Zero mode.
TypeMeta is a thin class that allows us to store the type of a container such as a blob...
Definition: typeid.h:324
C10_NODISCARD TensorOptions requires_grad(c10::optional< bool > requires_grad) const noexcept
Sets the requires_grad property of the TensorOptions.
TensorOptions dtype(caffe2::TypeMeta dtype)
Convenience function that returns a TensorOptions object with the dtype set to the given one...