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