1 // Copyright (c) 2018-2019 Cem Bassoy
2 //
3 // Distributed under the Boost Software License, Version 1.0. (See
4 // accompanying file LICENSE_1_0.txt or copy at
5 // http://www.boost.org/LICENSE_1_0.txt)
6 //
7 // The authors gratefully acknowledge the support of
8 // Fraunhofer and Google in producing this work
9 // which started as a Google Summer of Code project.
10 //
11
12
13
14 #include <random>
15 #include <boost/numeric/ublas/tensor/tensor.hpp>
16
17 #define BOOST_TEST_DYN_LINK
18 #define BOOST_TEST_MODULE TestTensor
19
20 #include <boost/test/unit_test.hpp>
21 #include "utility.hpp"
22
23 //BOOST_AUTO_TEST_SUITE ( test_tensor, * boost::unit_test::depends_on("test_extents") ) ;
24 BOOST_AUTO_TEST_SUITE ( test_tensor )
25
26 using test_types = zip<int,long,float,double,std::complex<float>>::with_t<boost::numeric::ublas::first_order, boost::numeric::ublas::last_order>;
27
28
BOOST_AUTO_TEST_CASE_TEMPLATE(test_tensor_ctor,value,test_types)29 BOOST_AUTO_TEST_CASE_TEMPLATE( test_tensor_ctor, value, test_types)
30 {
31 using namespace boost::numeric;
32 using value_type = typename value::first_type;
33 using layout_type = typename value::second_type;
34 using tensor_type = ublas::tensor<value_type, layout_type>;
35
36 auto a1 = tensor_type{};
37 BOOST_CHECK_EQUAL( a1.size() , 0ul );
38 BOOST_CHECK( a1.empty() );
39 BOOST_CHECK_EQUAL( a1.data() , nullptr);
40
41 auto a2 = tensor_type{1,1};
42 BOOST_CHECK_EQUAL( a2.size() , 1 );
43 BOOST_CHECK( !a2.empty() );
44 BOOST_CHECK_NE( a2.data() , nullptr);
45
46 auto a3 = tensor_type{2,1};
47 BOOST_CHECK_EQUAL( a3.size() , 2 );
48 BOOST_CHECK( !a3.empty() );
49 BOOST_CHECK_NE( a3.data() , nullptr);
50
51 auto a4 = tensor_type{1,2};
52 BOOST_CHECK_EQUAL( a4.size() , 2 );
53 BOOST_CHECK( !a4.empty() );
54 BOOST_CHECK_NE( a4.data() , nullptr);
55
56 auto a5 = tensor_type{2,1};
57 BOOST_CHECK_EQUAL( a5.size() , 2 );
58 BOOST_CHECK( !a5.empty() );
59 BOOST_CHECK_NE( a5.data() , nullptr);
60
61 auto a6 = tensor_type{4,3,2};
62 BOOST_CHECK_EQUAL( a6.size() , 4*3*2 );
63 BOOST_CHECK( !a6.empty() );
64 BOOST_CHECK_NE( a6.data() , nullptr);
65
66 auto a7 = tensor_type{4,1,2};
67 BOOST_CHECK_EQUAL( a7.size() , 4*1*2 );
68 BOOST_CHECK( !a7.empty() );
69 BOOST_CHECK_NE( a7.data() , nullptr);
70 }
71
72
73 struct fixture
74 {
75 using extents_type = boost::numeric::ublas::basic_extents<std::size_t>;
fixturefixture76 fixture()
77 : extents {
78 extents_type{}, // 0
79 extents_type{1,1}, // 1
80 extents_type{1,2}, // 2
81 extents_type{2,1}, // 3
82 extents_type{2,3}, // 4
83 extents_type{2,3,1}, // 5
84 extents_type{4,1,3}, // 6
85 extents_type{1,2,3}, // 7
86 extents_type{4,2,3}, // 8
87 extents_type{4,2,3,5}} // 9
88 {
89 }
90 std::vector<extents_type> extents;
91 };
92
93
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_ctor_extents,value,test_types,fixture)94 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ctor_extents, value, test_types, fixture )
95 {
96 using namespace boost::numeric;
97 using value_type = typename value::first_type;
98 using layout_type = typename value::second_type;
99 using tensor_type = ublas::tensor<value_type, layout_type>;
100
101 auto check = [](auto const& e) {
102 auto t = tensor_type{e};
103 BOOST_CHECK_EQUAL ( t.size() , e.product() );
104 BOOST_CHECK_EQUAL ( t.rank() , e.size() );
105 if(e.empty()) {
106 BOOST_CHECK ( t.empty() );
107 BOOST_CHECK_EQUAL ( t.data() , nullptr);
108 }
109 else{
110 BOOST_CHECK ( !t.empty() );
111 BOOST_CHECK_NE ( t.data() , nullptr);
112 }
113 };
114
115 for(auto const& e : extents)
116 check(e);
117 }
118
119
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_copy_ctor,value,test_types,fixture)120 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_copy_ctor, value, test_types, fixture )
121 {
122 using namespace boost::numeric;
123 using value_type = typename value::first_type;
124 using layout_type = typename value::second_type;
125 using tensor_type = ublas::tensor<value_type, layout_type>;
126
127 auto check = [](auto const& e)
128 {
129 auto r = tensor_type{e};
130 auto t = r;
131 BOOST_CHECK_EQUAL ( t.size() , r.size() );
132 BOOST_CHECK_EQUAL ( t.rank() , r.rank() );
133 BOOST_CHECK ( t.strides() == r.strides() );
134 BOOST_CHECK ( t.extents() == r.extents() );
135
136 if(e.empty()) {
137 BOOST_CHECK ( t.empty() );
138 BOOST_CHECK_EQUAL ( t.data() , nullptr);
139 }
140 else{
141 BOOST_CHECK ( !t.empty() );
142 BOOST_CHECK_NE ( t.data() , nullptr);
143 }
144
145 for(auto i = 0ul; i < t.size(); ++i)
146 BOOST_CHECK_EQUAL( t[i], r[i] );
147 };
148
149 for(auto const& e : extents)
150 check(e);
151 }
152
153
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_copy_ctor_layout,value,test_types,fixture)154 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_copy_ctor_layout, value, test_types, fixture )
155 {
156 using namespace boost::numeric;
157 using value_type = typename value::first_type;
158 using layout_type = typename value::second_type;
159 using tensor_type = ublas::tensor<value_type, layout_type>;
160 using other_layout_type = std::conditional_t<std::is_same<ublas::first_order,layout_type>::value, ublas::last_order, ublas::first_order>;
161 using other_tensor_type = ublas::tensor<value_type, other_layout_type>;
162
163
164 for(auto const& e : extents)
165 {
166 auto r = tensor_type{e};
167 other_tensor_type t = r;
168 tensor_type q = t;
169
170 BOOST_CHECK_EQUAL ( t.size() , r.size() );
171 BOOST_CHECK_EQUAL ( t.rank() , r.rank() );
172 BOOST_CHECK ( t.extents() == r.extents() );
173
174 BOOST_CHECK_EQUAL ( q.size() , r.size() );
175 BOOST_CHECK_EQUAL ( q.rank() , r.rank() );
176 BOOST_CHECK ( q.strides() == r.strides() );
177 BOOST_CHECK ( q.extents() == r.extents() );
178
179 for(auto i = 0ul; i < t.size(); ++i)
180 BOOST_CHECK_EQUAL( q[i], r[i] );
181 }
182 }
183
184
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_copy_move_ctor,value,test_types,fixture)185 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_copy_move_ctor, value, test_types, fixture )
186 {
187 using namespace boost::numeric;
188 using value_type = typename value::first_type;
189 using layout_type = typename value::second_type;
190 using tensor_type = ublas::tensor<value_type, layout_type>;
191
192 auto check = [](auto const& e)
193 {
194 auto r = tensor_type{e};
195 auto t = std::move(r);
196 BOOST_CHECK_EQUAL ( t.size() , e.product() );
197 BOOST_CHECK_EQUAL ( t.rank() , e.size() );
198
199 if(e.empty()) {
200 BOOST_CHECK ( t.empty() );
201 BOOST_CHECK_EQUAL ( t.data() , nullptr);
202 }
203 else{
204 BOOST_CHECK ( !t.empty() );
205 BOOST_CHECK_NE ( t.data() , nullptr);
206 }
207
208 };
209
210 for(auto const& e : extents)
211 check(e);
212 }
213
214
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_ctor_extents_init,value,test_types,fixture)215 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ctor_extents_init, value, test_types, fixture )
216 {
217 using namespace boost::numeric;
218 using value_type = typename value::first_type;
219 using layout_type = typename value::second_type;
220 using tensor_type = ublas::tensor<value_type, layout_type>;
221
222 std::random_device device{};
223 std::minstd_rand0 generator(device());
224
225 using distribution_type = std::conditional_t<std::is_integral_v<value_type>, std::uniform_int_distribution<>, std::uniform_real_distribution<> >;
226 auto distribution = distribution_type(1,6);
227
228 for(auto const& e : extents){
229 auto r = static_cast<value_type>(distribution(generator));
230 auto t = tensor_type{e,r};
231 for(auto i = 0ul; i < t.size(); ++i)
232 BOOST_CHECK_EQUAL( t[i], r );
233 }
234 }
235
236
237
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_ctor_extents_array,value,test_types,fixture)238 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_ctor_extents_array, value, test_types, fixture)
239 {
240 using namespace boost::numeric;
241 using value_type = typename value::first_type;
242 using layout_type = typename value::second_type;
243 using tensor_type = ublas::tensor<value_type, layout_type>;
244 using array_type = typename tensor_type::array_type;
245
246 for(auto const& e : extents) {
247 auto a = array_type(e.product());
248 auto v = value_type {};
249
250 for(auto& aa : a){
251 aa = v;
252 v += value_type{1};
253 }
254 auto t = tensor_type{e, a};
255 v = value_type{};
256
257 for(auto i = 0ul; i < t.size(); ++i, v+=value_type{1})
258 BOOST_CHECK_EQUAL( t[i], v);
259 }
260 }
261
262
263
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_read_write_single_index_access,value,test_types,fixture)264 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_read_write_single_index_access, value, test_types, fixture)
265 {
266 using namespace boost::numeric;
267 using value_type = typename value::first_type;
268 using layout_type = typename value::second_type;
269 using tensor_type = ublas::tensor<value_type, layout_type>;
270
271 for(auto const& e : extents) {
272 auto t = tensor_type{e};
273 auto v = value_type {};
274 for(auto i = 0ul; i < t.size(); ++i, v+=value_type{1}){
275 t[i] = v;
276 BOOST_CHECK_EQUAL( t[i], v );
277
278 t(i) = v;
279 BOOST_CHECK_EQUAL( t(i), v );
280 }
281 }
282 }
283
284
285
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_read_write_multi_index_access_at,value,test_types,fixture)286 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_read_write_multi_index_access_at, value, test_types, fixture)
287 {
288 using namespace boost::numeric;
289 using value_type = typename value::first_type;
290 using layout_type = typename value::second_type;
291 using tensor_type = ublas::tensor<value_type, layout_type>;
292
293 auto check1 = [](const tensor_type& t)
294 {
295 auto v = value_type{};
296 for(auto k = 0ul; k < t.size(); ++k){
297 BOOST_CHECK_EQUAL(t[k], v);
298 v+=value_type{1};
299 }
300 };
301
302 auto check2 = [](const tensor_type& t)
303 {
304 std::array<unsigned,2> k;
305 auto r = std::is_same_v<layout_type,ublas::first_order> ? 1 : 0;
306 auto q = std::is_same_v<layout_type,ublas::last_order > ? 1 : 0;
307 auto v = value_type{};
308 for(k[r] = 0ul; k[r] < t.size(r); ++k[r]){
309 for(k[q] = 0ul; k[q] < t.size(q); ++k[q]){
310 BOOST_CHECK_EQUAL(t.at(k[0],k[1]), v);
311 v+=value_type{1};
312 }
313 }
314 };
315
316 auto check3 = [](const tensor_type& t)
317 {
318 std::array<unsigned,3> k;
319 using op_type = std::conditional_t<std::is_same_v<layout_type,ublas::first_order>, std::minus<>, std::plus<>>;
320 auto r = std::is_same_v<layout_type,ublas::first_order> ? 2 : 0;
321 auto o = op_type{};
322 auto v = value_type{};
323 for(k[r] = 0ul; k[r] < t.size(r); ++k[r]){
324 for(k[o(r,1)] = 0ul; k[o(r,1)] < t.size(o(r,1)); ++k[o(r,1)]){
325 for(k[o(r,2)] = 0ul; k[o(r,2)] < t.size(o(r,2)); ++k[o(r,2)]){
326 BOOST_CHECK_EQUAL(t.at(k[0],k[1],k[2]), v);
327 v+=value_type{1};
328 }
329 }
330 }
331 };
332
333 auto check4 = [](const tensor_type& t)
334 {
335 std::array<unsigned,4> k;
336 using op_type = std::conditional_t<std::is_same_v<layout_type,ublas::first_order>, std::minus<>, std::plus<>>;
337 auto r = std::is_same_v<layout_type,ublas::first_order> ? 3 : 0;
338 auto o = op_type{};
339 auto v = value_type{};
340 for(k[r] = 0ul; k[r] < t.size(r); ++k[r]){
341 for(k[o(r,1)] = 0ul; k[o(r,1)] < t.size(o(r,1)); ++k[o(r,1)]){
342 for(k[o(r,2)] = 0ul; k[o(r,2)] < t.size(o(r,2)); ++k[o(r,2)]){
343 for(k[o(r,3)] = 0ul; k[o(r,3)] < t.size(o(r,3)); ++k[o(r,3)]){
344 BOOST_CHECK_EQUAL(t.at(k[0],k[1],k[2],k[3]), v);
345 v+=value_type{1};
346 }
347 }
348 }
349 }
350 };
351
352 auto check = [check1,check2,check3,check4](auto const& e) {
353 auto t = tensor_type{e};
354 auto v = value_type {};
355 for(auto i = 0ul; i < t.size(); ++i){
356 t[i] = v;
357 v+=value_type{1};
358 }
359
360 if(t.rank() == 1) check1(t);
361 else if(t.rank() == 2) check2(t);
362 else if(t.rank() == 3) check3(t);
363 else if(t.rank() == 4) check4(t);
364
365 };
366
367 for(auto const& e : extents)
368 check(e);
369 }
370
371
372
373
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_reshape,value,test_types,fixture)374 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_reshape, value, test_types, fixture)
375 {
376 using namespace boost::numeric;
377 using value_type = typename value::first_type;
378 using layout_type = typename value::second_type;
379 using tensor_type = ublas::tensor<value_type, layout_type>;
380
381
382 for(auto const& efrom : extents){
383 for(auto const& eto : extents){
384
385 auto v = value_type {};
386 v+=value_type{1};
387 auto t = tensor_type{efrom, v};
388 for(auto i = 0ul; i < t.size(); ++i)
389 BOOST_CHECK_EQUAL( t[i], v );
390
391 t.reshape(eto);
392 for(auto i = 0ul; i < std::min(efrom.product(),eto.product()); ++i)
393 BOOST_CHECK_EQUAL( t[i], v );
394
395 BOOST_CHECK_EQUAL ( t.size() , eto.product() );
396 BOOST_CHECK_EQUAL ( t.rank() , eto.size() );
397 BOOST_CHECK ( t.extents() == eto );
398
399 if(efrom != eto){
400 for(auto i = efrom.product(); i < t.size(); ++i)
401 BOOST_CHECK_EQUAL( t[i], value_type{} );
402 }
403 }
404 }
405 }
406
407
408
409
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_swap,value,test_types,fixture)410 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_swap, value, test_types, fixture)
411 {
412 using namespace boost::numeric;
413 using value_type = typename value::first_type;
414 using layout_type = typename value::second_type;
415 using tensor_type = ublas::tensor<value_type, layout_type>;
416
417 for(auto const& e_t : extents){
418 for(auto const& e_r : extents) {
419
420 auto v = value_type {} + value_type{1};
421 auto w = value_type {} + value_type{2};
422 auto t = tensor_type{e_t, v};
423 auto r = tensor_type{e_r, w};
424
425 std::swap( r, t );
426
427 for(auto i = 0ul; i < t.size(); ++i)
428 BOOST_CHECK_EQUAL( t[i], w );
429
430 BOOST_CHECK_EQUAL ( t.size() , e_r.product() );
431 BOOST_CHECK_EQUAL ( t.rank() , e_r.size() );
432 BOOST_CHECK ( t.extents() == e_r );
433
434 for(auto i = 0ul; i < r.size(); ++i)
435 BOOST_CHECK_EQUAL( r[i], v );
436
437 BOOST_CHECK_EQUAL ( r.size() , e_t.product() );
438 BOOST_CHECK_EQUAL ( r.rank() , e_t.size() );
439 BOOST_CHECK ( r.extents() == e_t );
440
441
442 }
443 }
444 }
445
446
447
BOOST_FIXTURE_TEST_CASE_TEMPLATE(test_tensor_standard_iterator,value,test_types,fixture)448 BOOST_FIXTURE_TEST_CASE_TEMPLATE( test_tensor_standard_iterator, value, test_types, fixture)
449 {
450 using namespace boost::numeric;
451 using value_type = typename value::first_type;
452 using layout_type = typename value::second_type;
453 using tensor_type = ublas::tensor<value_type, layout_type>;
454
455 for(auto const& e : extents)
456 {
457 auto v = value_type {} + value_type{1};
458 auto t = tensor_type{e, v};
459
460 BOOST_CHECK_EQUAL( std::distance(t.begin(), t.end ()), t.size() );
461 BOOST_CHECK_EQUAL( std::distance(t.rbegin(), t.rend()), t.size() );
462
463 BOOST_CHECK_EQUAL( std::distance(t.cbegin(), t.cend ()), t.size() );
464 BOOST_CHECK_EQUAL( std::distance(t.crbegin(), t.crend()), t.size() );
465
466 if(t.size() > 0) {
467 BOOST_CHECK( t.data() == std::addressof( *t.begin () ) ) ;
468 BOOST_CHECK( t.data() == std::addressof( *t.cbegin() ) ) ;
469 }
470 }
471 }
472
473 BOOST_AUTO_TEST_SUITE_END()
474