Caffe2 - C++ API
A deep learning, cross platform ML framework
scalar_tensor_test.cpp
1 #include <gtest/gtest.h>
2 
3 #include <ATen/ATen.h>
4 #include <algorithm>
5 #include <iostream>
6 #include <numeric>
7 
8 using namespace at;
9 
10 #define TRY_CATCH_ELSE(fn, catc, els) \
11  { \
12  /* avoid mistakenly passing if els code throws exception*/ \
13  bool _passed = false; \
14  try { \
15  fn; \
16  _passed = true; \
17  els; \
18  } catch (std::exception & e) { \
19  ASSERT_FALSE(_passed); \
20  catc; \
21  } \
22  }
23 
24 void require_equal_size_dim(const Tensor &lhs, const Tensor &rhs) {
25  ASSERT_EQ(lhs.dim(), rhs.dim());
26  ASSERT_TRUE(lhs.sizes().equals(rhs.sizes()));
27 }
28 
29 bool should_expand(const IntArrayRef &from_size, const IntArrayRef &to_size) {
30  if (from_size.size() > to_size.size()) {
31  return false;
32  }
33  for (auto from_dim_it = from_size.rbegin(); from_dim_it != from_size.rend();
34  ++from_dim_it) {
35  for (auto to_dim_it = to_size.rbegin(); to_dim_it != to_size.rend();
36  ++to_dim_it) {
37  if (*from_dim_it != 1 && *from_dim_it != *to_dim_it) {
38  return false;
39  }
40  }
41  }
42  return true;
43 }
44 
45 void test(Type &T) {
46  std::vector<std::vector<int64_t>> sizes = {{}, {0}, {1}, {1, 1}, {2}};
47 
48  // single-tensor/size tests
49  for (auto s = sizes.begin(); s != sizes.end(); ++s) {
50  // verify that the dim, sizes, strides, etc match what was requested.
51  auto t = ones(*s, T);
52  ASSERT_EQ((size_t)t.dim(), s->size());
53  ASSERT_EQ((size_t)t.ndimension(), s->size());
54  ASSERT_TRUE(t.sizes().equals(*s));
55  ASSERT_EQ(t.strides().size(), s->size());
56  auto numel =
57  std::accumulate(s->begin(), s->end(), 1, std::multiplies<int64_t>());
58  ASSERT_EQ(t.numel(), numel);
59  // verify we can output
60  std::stringstream ss;
61  ASSERT_NO_THROW(ss << t << std::endl);
62 
63  // set_
64  auto t2 = ones(*s, T);
65  t2.set_();
66  require_equal_size_dim(t2, ones({0}, T));
67 
68  // unsqueeze
69  ASSERT_EQ(t.unsqueeze(0).dim(), t.dim() + 1);
70 
71  // unsqueeze_
72  {
73  auto t2 = ones(*s, T);
74  auto r = t2.unsqueeze_(0);
75  ASSERT_EQ(r.dim(), t.dim() + 1);
76  }
77 
78  // squeeze (with dimension argument)
79  if (t.dim() == 0 || t.sizes()[0] == 1) {
80  ASSERT_EQ(t.squeeze(0).dim(), std::max<int64_t>(t.dim() - 1, 0));
81  } else {
82  // In PyTorch, it is a no-op to try to squeeze a dimension that has size
83  // != 1; in NumPy this is an error.
84  ASSERT_EQ(t.squeeze(0).dim(), t.dim());
85  }
86 
87  // squeeze (with no dimension argument)
88  {
89  std::vector<int64_t> size_without_ones;
90  for (auto size : *s) {
91  if (size != 1) {
92  size_without_ones.push_back(size);
93  }
94  }
95  auto result = t.squeeze();
96  require_equal_size_dim(result, ones(size_without_ones, T));
97  }
98 
99  {
100  // squeeze_ (with dimension argument)
101  auto t2 = ones(*s, T);
102  if (t2.dim() == 0 || t2.sizes()[0] == 1) {
103  ASSERT_EQ(t2.squeeze_(0).dim(), std::max<int64_t>(t.dim() - 1, 0));
104  } else {
105  // In PyTorch, it is a no-op to try to squeeze a dimension that has size
106  // != 1; in NumPy this is an error.
107  ASSERT_EQ(t2.squeeze_(0).dim(), t.dim());
108  }
109  }
110 
111  // squeeze_ (with no dimension argument)
112  {
113  auto t2 = ones(*s, T);
114  std::vector<int64_t> size_without_ones;
115  for (auto size : *s) {
116  if (size != 1) {
117  size_without_ones.push_back(size);
118  }
119  }
120  auto r = t2.squeeze_();
121  require_equal_size_dim(t2, ones(size_without_ones, T));
122  }
123 
124  // reduce (with dimension argument and with 1 return argument)
125  if (t.numel() != 0) {
126  ASSERT_EQ(t.sum(0).dim(), std::max<int64_t>(t.dim() - 1, 0));
127  } else {
128  ASSERT_TRUE(t.sum(0).equal(at::zeros({}, T)));
129  }
130 
131  // reduce (with dimension argument and with 2 return arguments)
132  if (t.numel() != 0) {
133  auto ret = t.min(0);
134  ASSERT_EQ(std::get<0>(ret).dim(), std::max<int64_t>(t.dim() - 1, 0));
135  ASSERT_EQ(std::get<1>(ret).dim(), std::max<int64_t>(t.dim() - 1, 0));
136  } else {
137  ASSERT_ANY_THROW(t.min(0));
138  }
139 
140  // simple indexing
141  if (t.dim() > 0 && t.numel() != 0) {
142  ASSERT_EQ(t[0].dim(), std::max<int64_t>(t.dim() - 1, 0));
143  } else {
144  ASSERT_ANY_THROW(t[0]);
145  }
146 
147  // fill_ (argument to fill_ can only be a 0-dim tensor)
148  TRY_CATCH_ELSE(
149  t.fill_(t.sum(0)), ASSERT_GT(t.dim(), 1), ASSERT_LE(t.dim(), 1));
150  }
151 
152  for (auto lhs_it = sizes.begin(); lhs_it != sizes.end(); ++lhs_it) {
153  for (auto rhs_it = sizes.begin(); rhs_it != sizes.end(); ++rhs_it) {
154  // is_same_size should only match if they are the same shape
155  {
156  auto lhs = ones(*lhs_it, T);
157  auto rhs = ones(*rhs_it, T);
158  if (*lhs_it != *rhs_it) {
159  ASSERT_FALSE(lhs.is_same_size(rhs));
160  ASSERT_FALSE(rhs.is_same_size(lhs));
161  }
162  }
163  // forced size functions (resize_, resize_as, set_)
164  {// resize_
165  {auto lhs = ones(*lhs_it, T);
166  auto rhs = ones(*rhs_it, T);
167  lhs.resize_(*rhs_it);
168  require_equal_size_dim(lhs, rhs);
169  }
170  // resize_as_
171  {
172  auto lhs = ones(*lhs_it, T);
173  auto rhs = ones(*rhs_it, T);
174  lhs.resize_as_(rhs);
175  require_equal_size_dim(lhs, rhs);
176  }
177  // set_
178  {
179  {
180  // with tensor
181  auto lhs = ones(*lhs_it, T);
182  auto rhs = ones(*rhs_it, T);
183  lhs.set_(rhs);
184  require_equal_size_dim(lhs, rhs);
185  }
186  {
187  // with storage
188  auto lhs = ones(*lhs_it, T);
189  auto rhs = ones(*rhs_it, T);
190  lhs.set_(rhs.storage());
191  // should not be dim 0 because an empty storage is dim 1; all other
192  // storages aren't scalars
193  ASSERT_NE(lhs.dim(), 0);
194  }
195  {
196  // with storage, offset, sizes, strides
197  auto lhs = ones(*lhs_it, T);
198  auto rhs = ones(*rhs_it, T);
199  lhs.set_(rhs.storage(), rhs.storage_offset(), rhs.sizes(), rhs.strides());
200  require_equal_size_dim(lhs, rhs);
201  }
202  }
203  }
204 
205  // view
206  {
207  auto lhs = ones(*lhs_it, T);
208  auto rhs = ones(*rhs_it, T);
209  auto rhs_size = *rhs_it;
210  TRY_CATCH_ELSE(auto result = lhs.view(rhs_size),
211  ASSERT_NE(lhs.numel(), rhs.numel()),
212  ASSERT_EQ(lhs.numel(), rhs.numel());
213  require_equal_size_dim(result, rhs););
214  }
215 
216  // take
217  {
218  auto lhs = ones(*lhs_it, T);
219  auto rhs = zeros(*rhs_it, T).toType(ScalarType::Long);
220  TRY_CATCH_ELSE(auto result = lhs.take(rhs), ASSERT_EQ(lhs.numel(), 0);
221  ASSERT_NE(rhs.numel(), 0),
222  require_equal_size_dim(result, rhs));
223  }
224 
225  // ger
226  {
227  auto lhs = ones(*lhs_it, T);
228  auto rhs = ones(*rhs_it, T);
229  TRY_CATCH_ELSE(auto result = lhs.ger(rhs),
230  ASSERT_TRUE(
231  (lhs.numel() == 0 || rhs.numel() == 0 ||
232  lhs.dim() != 1 || rhs.dim() != 1)),
233  [&]() {
234  int64_t dim0 = lhs.dim() == 0 ? 1 : lhs.size(0);
235  int64_t dim1 = rhs.dim() == 0 ? 1 : rhs.size(0);
236  require_equal_size_dim(
237  result, at::empty({dim0, dim1}, result.options()));
238  }(););
239  }
240 
241  // expand
242  {
243  auto lhs = ones(*lhs_it, T);
244  auto lhs_size = *lhs_it;
245  auto rhs = ones(*rhs_it, T);
246  auto rhs_size = *rhs_it;
247  bool should_pass = should_expand(lhs_size, rhs_size);
248  TRY_CATCH_ELSE(auto result = lhs.expand(rhs_size),
249  ASSERT_FALSE(should_pass),
250  ASSERT_TRUE(should_pass);
251  require_equal_size_dim(result, rhs););
252 
253  // in-place functions (would be good if we can also do a non-broadcasting
254  // one, b/c broadcasting functions will always end up operating on tensors
255  // of same size; is there an example of this outside of assign_ ?)
256  {
257  bool should_pass_inplace = should_expand(rhs_size, lhs_size);
258  TRY_CATCH_ELSE(lhs.add_(rhs),
259  ASSERT_FALSE(should_pass_inplace),
260  ASSERT_TRUE(should_pass_inplace);
261  require_equal_size_dim(lhs, ones(*lhs_it, T)););
262  }
263  }
264 }
265 }
266 }
267 
268 TEST(TestScalarTensor, TestScalarTensorCPU) {
269  manual_seed(123);
270  test(CPU(kFloat));
271 }
272 
273 TEST(TestScalarTensor, TestScalarTensorCUDA) {
274  manual_seed(123);
275 
276  if (at::hasCUDA()) {
277  test(CUDA(kFloat));
278  }
279 }
Definition: module.cpp:17
constexpr bool equals(ArrayRef RHS) const
equals - Check for element-wise equality.
Definition: ArrayRef.h:155
constexpr size_t size() const
size - Get the array size.
Definition: ArrayRef.h:138
Flush-To-Zero and Denormals-Are-Zero mode.