Line data Source code
1 : /**
2 : * @file test_DataContainer.cpp
3 : *
4 : * @brief Tests for DataContainer class
5 : *
6 : * @author Matthias Wieczorek - initial code
7 : * @author David Frank - rewrite to use doctest and BDD
8 : * @author Tobias Lasser - rewrite and added code coverage
9 : */
10 :
11 : #include "doctest/doctest.h"
12 : #include "DataContainer.h"
13 : #include "IdenticalBlocksDescriptor.h"
14 : #include "testHelpers.h"
15 : #include "VolumeDescriptor.h"
16 :
17 : #include <type_traits>
18 :
19 : using namespace elsa;
20 : using namespace doctest;
21 :
22 : // Provides object to be used with TEMPLATE_PRODUCT_TEST_CASE, necessary because enum cannot be
23 : // passed directly
24 : template <typename T>
25 : struct TestHelperCPU {
26 : using data_t = T;
27 : };
28 :
29 : using CPUTypeTuple =
30 : std::tuple<TestHelperCPU<float>, TestHelperCPU<double>, TestHelperCPU<complex<float>>,
31 : TestHelperCPU<complex<double>>, TestHelperCPU<index_t>>;
32 :
33 : TYPE_TO_STRING(TestHelperCPU<float>);
34 : TYPE_TO_STRING(TestHelperCPU<double>);
35 : TYPE_TO_STRING(TestHelperCPU<index_t>);
36 : TYPE_TO_STRING(TestHelperCPU<complex<float>>);
37 : TYPE_TO_STRING(TestHelperCPU<complex<double>>);
38 :
39 : TYPE_TO_STRING(DataContainer<float>);
40 : TYPE_TO_STRING(DataContainer<double>);
41 : TYPE_TO_STRING(DataContainer<index_t>);
42 : TYPE_TO_STRING(DataContainer<complex<float>>);
43 : TYPE_TO_STRING(DataContainer<complex<double>>);
44 :
45 : TYPE_TO_STRING(complex<float>);
46 : TYPE_TO_STRING(complex<double>);
47 :
48 : TEST_SUITE_BEGIN("core");
49 :
50 : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing construction", TestType,
51 : datacontainer_construction)
52 50 : {
53 50 : using data_t = typename TestType::data_t;
54 :
55 50 : INFO("Testing type: " << TypeName_v<const data_t>);
56 :
57 50 : GIVEN("a DataDescriptor")
58 50 : {
59 20 : IndexVector_t numCoeff(3);
60 20 : numCoeff << 17, 47, 91;
61 20 : VolumeDescriptor desc(numCoeff);
62 :
63 20 : WHEN("constructing an empty DataContainer")
64 20 : {
65 10 : DataContainer<data_t> dc(desc);
66 :
67 10 : THEN("it has the correct DataDescriptor")
68 10 : {
69 5 : REQUIRE_EQ(dc.getDataDescriptor(), desc);
70 5 : }
71 :
72 10 : THEN("it has a data vector of correct size")
73 10 : {
74 5 : REQUIRE_EQ(dc.getSize(), desc.getNumberOfCoefficients());
75 5 : }
76 10 : }
77 :
78 20 : WHEN("constructing an initialized DataContainer")
79 20 : {
80 10 : auto data = generateRandomMatrix<data_t>(desc.getNumberOfCoefficients());
81 :
82 10 : DataContainer<data_t> dc(desc, data);
83 :
84 10 : THEN("it has the correct DataDescriptor")
85 10 : {
86 5 : REQUIRE_EQ(dc.getDataDescriptor(), desc);
87 5 : }
88 :
89 10 : THEN("it has correctly initialized data")
90 10 : {
91 5 : REQUIRE_EQ(dc.getSize(), desc.getNumberOfCoefficients());
92 :
93 363550 : for (index_t i = 0; i < dc.getSize(); ++i)
94 5 : REQUIRE_UNARY(checkApproxEq(dc[i], data[i]));
95 5 : }
96 10 : }
97 20 : }
98 :
99 50 : GIVEN("another DataContainer")
100 50 : {
101 30 : IndexVector_t numCoeff(2);
102 30 : numCoeff << 32, 57;
103 30 : VolumeDescriptor desc(numCoeff);
104 :
105 30 : DataContainer<data_t> otherDc(desc);
106 :
107 30 : auto randVec = generateRandomMatrix<data_t>(otherDc.getSize());
108 54750 : for (index_t i = 0; i < otherDc.getSize(); ++i)
109 54720 : otherDc[i] = randVec(i);
110 :
111 30 : WHEN("copy constructing")
112 30 : {
113 5 : DataContainer dc(otherDc);
114 :
115 5 : THEN("it copied correctly")
116 5 : {
117 5 : REQUIRE_EQ(dc.getDataDescriptor(), otherDc.getDataDescriptor());
118 5 : REQUIRE_NE(&dc.getDataDescriptor(), &otherDc.getDataDescriptor());
119 :
120 5 : REQUIRE_EQ(dc, otherDc);
121 5 : }
122 5 : }
123 :
124 30 : WHEN("copy assigning")
125 30 : {
126 5 : DataContainer<data_t> dc(desc);
127 5 : dc = otherDc;
128 :
129 5 : THEN("it copied correctly")
130 5 : {
131 5 : REQUIRE_EQ(dc.getDataDescriptor(), otherDc.getDataDescriptor());
132 5 : REQUIRE_NE(&dc.getDataDescriptor(), &otherDc.getDataDescriptor());
133 :
134 5 : REQUIRE_EQ(dc, otherDc);
135 5 : }
136 5 : }
137 :
138 30 : WHEN("move constructing")
139 30 : {
140 10 : DataContainer oldOtherDc(otherDc);
141 :
142 10 : DataContainer dc(std::move(otherDc));
143 :
144 10 : THEN("it moved correctly")
145 10 : {
146 5 : REQUIRE_EQ(dc.getDataDescriptor(), oldOtherDc.getDataDescriptor());
147 :
148 5 : REQUIRE_EQ(dc, oldOtherDc);
149 5 : }
150 :
151 10 : THEN("the moved from object is still valid (but empty)")
152 10 : {
153 5 : otherDc = dc;
154 5 : }
155 10 : }
156 :
157 30 : WHEN("move assigning")
158 30 : {
159 10 : DataContainer oldOtherDc(otherDc);
160 :
161 10 : DataContainer<data_t> dc(desc);
162 10 : dc = std::move(otherDc);
163 :
164 10 : THEN("it moved correctly")
165 10 : {
166 5 : REQUIRE_EQ(dc.getDataDescriptor(), oldOtherDc.getDataDescriptor());
167 :
168 5 : REQUIRE_EQ(dc, oldOtherDc);
169 5 : }
170 :
171 10 : THEN("the moved from object is still valid (but empty)")
172 10 : {
173 5 : otherDc = dc;
174 5 : }
175 10 : }
176 30 : }
177 50 : }
178 :
179 : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing the reduction operations", TestType,
180 : datacontainer_reduction)
181 5 : {
182 5 : using data_t = typename TestType::data_t;
183 :
184 5 : INFO("Testing type: " << TypeName_v<const data_t>);
185 :
186 5 : GIVEN("a DataContainer")
187 5 : {
188 5 : IndexVector_t numCoeff(3);
189 5 : numCoeff << 11, 73, 45;
190 5 : VolumeDescriptor desc(numCoeff);
191 :
192 5 : WHEN("putting in some random data")
193 5 : {
194 5 : auto [dc, randVec] = generateRandomContainer<data_t>(desc);
195 :
196 5 : THEN("the reductions work a expected")
197 5 : {
198 5 : REQUIRE_UNARY(checkApproxEq(dc.sum(), randVec.sum()));
199 5 : REQUIRE_UNARY(checkApproxEq(
200 5 : dc.l0PseudoNorm(),
201 5 : (randVec.array().cwiseAbs()
202 5 : >= std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon())
203 5 : .count()));
204 5 : REQUIRE_UNARY(checkApproxEq(dc.l1Norm(), randVec.array().abs().sum()));
205 5 : REQUIRE_UNARY(checkApproxEq(dc.lInfNorm(), randVec.array().abs().maxCoeff()));
206 5 : REQUIRE_UNARY(checkApproxEq(dc.squaredL2Norm(), randVec.squaredNorm()));
207 :
208 5 : auto [dc2, randVec2] = generateRandomContainer<data_t>(desc);
209 :
210 5 : REQUIRE_UNARY(checkApproxEq(dc.dot(dc2), randVec.dot(randVec2)));
211 :
212 5 : if constexpr (!isComplex<data_t>) {
213 3 : REQUIRE_UNARY(checkApproxEq(dc.minElement(), randVec.array().minCoeff()));
214 3 : REQUIRE_UNARY(checkApproxEq(dc.maxElement(), randVec.array().maxCoeff()));
215 3 : }
216 5 : }
217 5 : }
218 5 : }
219 5 : }
220 :
221 : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing element-wise access", TestType,
222 : datacontainer_elemwise)
223 75 : {
224 75 : using data_t = typename TestType::data_t;
225 :
226 75 : INFO("Testing type: " << TypeName_v<const data_t>);
227 :
228 75 : GIVEN("a DataContainer")
229 75 : {
230 5 : IndexVector_t numCoeff(2);
231 5 : numCoeff << 47, 11;
232 5 : VolumeDescriptor desc(numCoeff);
233 5 : DataContainer<data_t> dc(desc);
234 :
235 5 : WHEN("accessing the elements")
236 5 : {
237 5 : IndexVector_t coord(2);
238 5 : coord << 17, 4;
239 5 : index_t index = desc.getIndexFromCoordinate(coord);
240 :
241 5 : THEN("it works as expected when using indices/coordinates")
242 5 : {
243 : // For integral typeps don't have a floating point value
244 5 : if constexpr (std::is_integral_v<data_t>) {
245 4 : dc[index] = data_t(2);
246 4 : REQUIRE_UNARY(checkApproxEq(dc[index], 2));
247 4 : REQUIRE_UNARY(checkApproxEq(dc(coord), 2));
248 4 : REQUIRE_UNARY(checkApproxEq(dc(17, 4), 2));
249 :
250 4 : dc(coord) = data_t(3);
251 4 : REQUIRE_UNARY(checkApproxEq(dc[index], 3));
252 4 : REQUIRE_UNARY(checkApproxEq(dc(coord), 3));
253 4 : REQUIRE_UNARY(checkApproxEq(dc(17, 4), 3));
254 4 : } else {
255 4 : dc[index] = data_t(2.2f);
256 4 : REQUIRE_UNARY(checkApproxEq(dc[index], 2.2f));
257 4 : REQUIRE_UNARY(checkApproxEq(dc(coord), 2.2f));
258 4 : REQUIRE_UNARY(checkApproxEq(dc(17, 4), 2.2f));
259 :
260 4 : dc(coord) = data_t(3.3f);
261 4 : REQUIRE_UNARY(checkApproxEq(dc[index], 3.3f));
262 4 : REQUIRE_UNARY(checkApproxEq(dc(coord), 3.3f));
263 4 : REQUIRE_UNARY(checkApproxEq(dc(17, 4), 3.3f));
264 4 : }
265 5 : }
266 5 : }
267 5 : }
268 :
269 75 : GIVEN("a DataContainer")
270 75 : {
271 70 : IndexVector_t numCoeff(2);
272 70 : numCoeff << 47, 11;
273 70 : VolumeDescriptor desc(numCoeff);
274 :
275 70 : WHEN("putting in some random data")
276 70 : {
277 30 : auto [dc, randVec] = generateRandomContainer<data_t>(desc);
278 :
279 30 : THEN("the element-wise unary operations work as expected")
280 30 : {
281 5 : DataContainer dcAbs = cwiseAbs(dc);
282 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
283 5 : REQUIRE_UNARY(checkApproxEq(dcAbs[i], randVec.array().abs()[i]));
284 :
285 5 : DataContainer dcSquare = square(dc);
286 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
287 5 : REQUIRE_UNARY(checkApproxEq(dcSquare[i], randVec.array().square()[i]));
288 5 : DataContainer dcSqrt = sqrt(dcSquare);
289 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
290 5 : REQUIRE_UNARY(checkApproxEq(dcSqrt[i], randVec.array().square().sqrt()[i]));
291 :
292 : // do exponent check only for floating point types as for integer will likely
293 : // lead to overflow due to random init over full value range
294 5 : if constexpr (!std::is_integral_v<data_t>) {
295 4 : DataContainer dcExp = exp(dc);
296 2072 : for (index_t i = 0; i < dc.getSize(); ++i)
297 4 : REQUIRE_UNARY(checkApproxEq(dcExp[i], randVec.array().exp()[i]));
298 4 : }
299 :
300 5 : DataContainer dcLog = log(dcSquare);
301 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
302 5 : REQUIRE_UNARY(checkApproxEq(dcLog[i], randVec.array().square().log()[i]));
303 :
304 5 : DataContainer dcReal = real(dc);
305 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
306 5 : REQUIRE_UNARY(checkApproxEq(dcReal[i], randVec.array().real()[i]));
307 :
308 5 : DataContainer dcImag = imag(dc);
309 :
310 5 : if constexpr (isComplex<data_t>) {
311 1036 : for (index_t i = 0; i < dc.getSize(); ++i)
312 3 : REQUIRE_UNARY(checkApproxEq(dcImag[i], randVec.array().imag()[i]));
313 3 : } else {
314 1554 : for (index_t i = 0; i < dc.getSize(); ++i)
315 3 : REQUIRE_UNARY(checkApproxEq(dcImag[i], 0));
316 3 : }
317 5 : }
318 :
319 30 : auto scalar = static_cast<data_t>(923.41f);
320 :
321 30 : THEN("the binary in-place addition of a scalar work as expected")
322 30 : {
323 5 : dc += scalar;
324 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
325 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) + scalar));
326 5 : }
327 :
328 30 : THEN("the binary in-place subtraction of a scalar work as expected")
329 30 : {
330 5 : dc -= scalar;
331 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
332 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) - scalar));
333 5 : }
334 :
335 30 : THEN("the binary in-place multiplication with a scalar work as expected")
336 30 : {
337 5 : dc *= scalar;
338 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
339 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) * scalar));
340 5 : }
341 :
342 30 : THEN("the binary in-place division by a scalar work as expected")
343 30 : {
344 5 : dc /= scalar;
345 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
346 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) / scalar));
347 5 : }
348 :
349 30 : THEN("the element-wise assignment of a scalar works as expected")
350 30 : {
351 5 : dc = scalar;
352 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
353 5 : REQUIRE_UNARY(checkApproxEq(dc[i], scalar));
354 5 : }
355 30 : }
356 :
357 70 : WHEN("having two containers with random data")
358 70 : {
359 20 : auto [dc, randVec] = generateRandomContainer<data_t>(desc);
360 20 : auto [dc2, randVec2] = generateRandomContainer<data_t>(desc);
361 :
362 20 : THEN("the element-wise in-place addition works as expected")
363 20 : {
364 5 : dc += dc2;
365 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
366 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) + randVec2(i)));
367 5 : }
368 :
369 20 : THEN("the element-wise in-place subtraction works as expected")
370 20 : {
371 5 : dc -= dc2;
372 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
373 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) - randVec2(i)));
374 5 : }
375 :
376 20 : THEN("the element-wise in-place multiplication works as expected")
377 20 : {
378 5 : dc *= dc2;
379 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
380 5 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) * randVec2(i)));
381 5 : }
382 :
383 20 : THEN("the element-wise in-place division works as expected")
384 20 : {
385 5 : dc /= dc2;
386 2590 : for (index_t i = 0; i < dc.getSize(); ++i)
387 2585 : if (dc2[i] != data_t(0))
388 2585 : REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) / randVec2(i)));
389 5 : }
390 20 : }
391 :
392 70 : WHEN("having two containers with real and complex data each")
393 70 : {
394 20 : auto [dcReals1, realsVec1] = generateRandomContainer<real_t>(desc);
395 20 : auto [dcComps1, compsVec1] = generateRandomContainer<complex<real_t>>(desc);
396 :
397 20 : THEN("the element-wise maximum operation works as expected for two real "
398 20 : "DataContainers")
399 20 : {
400 5 : auto [dcReals2, realsVec2] = generateRandomContainer<real_t>(desc);
401 :
402 5 : DataContainer dcCWiseMax = cwiseMax(dcReals1, dcReals2);
403 2590 : for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
404 5 : REQUIRE_UNARY(
405 5 : checkApproxEq(dcCWiseMax[i], realsVec1.array().max(realsVec2.array())[i]));
406 5 : }
407 :
408 20 : THEN("the element-wise maximum operation works as expected for a real and a complex "
409 20 : "DataContainer")
410 20 : {
411 5 : auto [dcComps2, compsVec2] = generateRandomContainer<complex<real_t>>(desc);
412 :
413 5 : DataContainer dcCWiseMax = cwiseMax(dcReals1, dcComps2);
414 2590 : for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
415 5 : REQUIRE_UNARY(checkApproxEq(dcCWiseMax[i],
416 5 : realsVec1.array().max(compsVec2.array().abs())[i]));
417 5 : }
418 :
419 20 : THEN("the element-wise maximum operation works as expected for a complex and a real "
420 20 : "DataContainer")
421 20 : {
422 5 : auto [dcComps2, compsVec2] = generateRandomContainer<complex<real_t>>(desc);
423 :
424 5 : DataContainer dcCWiseMax = cwiseMax(dcComps2, dcReals1);
425 2590 : for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
426 5 : REQUIRE_UNARY(checkApproxEq(dcCWiseMax[i],
427 5 : compsVec2.array().abs().max(realsVec1.array())[i]));
428 5 : }
429 :
430 20 : THEN("the element-wise maximum operation works as expected for two DataContainers")
431 20 : {
432 5 : auto [dcComps2, compsVec2] = generateRandomContainer<complex<real_t>>(desc);
433 :
434 5 : DataContainer dcCWiseMax = cwiseMax(dcComps1, dcComps2);
435 2590 : for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
436 5 : REQUIRE_UNARY(checkApproxEq(
437 5 : dcCWiseMax[i], compsVec1.array().abs().max(compsVec2.array().abs())[i]));
438 5 : }
439 20 : }
440 70 : }
441 75 : }
442 :
443 : TEST_CASE_TEMPLATE_DEFINE(
444 : "DataContainer: Testing the arithmetic operations with DataContainers arguments", TestType,
445 : datacontainer_arithmetic)
446 10 : {
447 10 : using data_t = typename TestType::data_t;
448 :
449 10 : INFO("Testing type: " << TypeName_v<const data_t>);
450 :
451 10 : GIVEN("some DataContainers")
452 10 : {
453 10 : IndexVector_t numCoeff(3);
454 10 : numCoeff << 52, 7, 29;
455 10 : VolumeDescriptor desc(numCoeff);
456 :
457 10 : auto [dc, randVec] = generateRandomContainer<data_t>(desc);
458 10 : auto [dc2, randVec2] = generateRandomContainer<data_t>(desc);
459 :
460 10 : THEN("the binary element-wise operations work as expected")
461 10 : {
462 5 : DataContainer resultPlus = dc + dc2;
463 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
464 5 : REQUIRE_UNARY(checkApproxEq(resultPlus[i], dc[i] + dc2[i]));
465 :
466 5 : DataContainer resultMinus = dc - dc2;
467 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
468 5 : REQUIRE_UNARY(checkApproxEq(resultMinus[i], dc[i] - dc2[i]));
469 :
470 5 : DataContainer resultMult = dc * dc2;
471 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
472 5 : REQUIRE_UNARY(checkApproxEq(resultMult[i], dc[i] * dc2[i]));
473 :
474 5 : DataContainer resultDiv = dc / dc2;
475 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
476 52780 : if (dc2[i] != data_t(0))
477 52780 : REQUIRE_UNARY(checkApproxEq(resultDiv[i], dc[i] / dc2[i]));
478 5 : }
479 :
480 10 : THEN("the operations with a scalar work as expected")
481 10 : {
482 5 : data_t scalar = static_cast<data_t>(4.92f);
483 :
484 5 : DataContainer resultScalarPlus = scalar + dc;
485 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
486 5 : REQUIRE_UNARY(checkApproxEq(resultScalarPlus[i], scalar + dc[i]));
487 :
488 5 : DataContainer resultPlusScalar = dc + scalar;
489 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
490 5 : REQUIRE_UNARY(checkApproxEq(resultPlusScalar[i], dc[i] + scalar));
491 :
492 5 : DataContainer resultScalarMinus = scalar - dc;
493 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
494 5 : REQUIRE_UNARY(checkApproxEq(resultScalarMinus[i], scalar - dc[i]));
495 :
496 5 : DataContainer resultMinusScalar = dc - scalar;
497 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
498 5 : REQUIRE_UNARY(checkApproxEq(resultMinusScalar[i], dc[i] - scalar));
499 :
500 5 : DataContainer resultScalarMult = scalar * dc;
501 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
502 5 : REQUIRE_UNARY(checkApproxEq(resultScalarMult[i], scalar * dc[i]));
503 :
504 5 : DataContainer resultMultScalar = dc * scalar;
505 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
506 5 : REQUIRE_UNARY(checkApproxEq(resultMultScalar[i], dc[i] * scalar));
507 :
508 5 : DataContainer resultScalarDiv = scalar / dc;
509 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
510 52780 : if (dc[i] != data_t(0))
511 52780 : REQUIRE_UNARY(checkApproxEq(resultScalarDiv[i], scalar / dc[i]));
512 :
513 5 : DataContainer resultDivScalar = dc / scalar;
514 52785 : for (index_t i = 0; i < dc.getSize(); ++i)
515 5 : REQUIRE_UNARY(checkApproxEq(resultDivScalar[i], dc[i] / scalar));
516 5 : }
517 10 : }
518 10 : }
519 :
520 : TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing creation of Maps through DataContainer", TestType,
521 : datacontainer_maps)
522 20 : {
523 20 : using data_t = typename TestType::data_t;
524 :
525 20 : INFO("Testing type: " << TypeName_v<const data_t>);
526 :
527 20 : GIVEN("a non-blocked container")
528 20 : {
529 10 : IndexVector_t numCoeff(3);
530 10 : numCoeff << 52, 7, 29;
531 10 : VolumeDescriptor desc(numCoeff);
532 :
533 10 : DataContainer<data_t> dc(desc);
534 10 : const DataContainer<data_t> constDc(desc);
535 :
536 10 : WHEN("trying to reference a block")
537 10 : {
538 5 : THEN("an exception occurs")
539 5 : {
540 5 : REQUIRE_THROWS(dc.getBlock(0));
541 5 : REQUIRE_THROWS(constDc.getBlock(0));
542 5 : }
543 5 : }
544 :
545 10 : WHEN("creating a view")
546 10 : {
547 5 : IndexVector_t numCoeff(1);
548 5 : numCoeff << desc.getNumberOfCoefficients();
549 5 : VolumeDescriptor linearDesc(numCoeff);
550 5 : auto linearDc = dc.viewAs(linearDesc);
551 5 : auto linearConstDc = constDc.viewAs(linearDesc);
552 :
553 5 : THEN("view has the correct descriptor and data")
554 5 : {
555 5 : REQUIRE_EQ(linearDesc, linearDc.getDataDescriptor());
556 5 : REQUIRE_EQ(&linearDc[0], &dc[0]);
557 :
558 5 : REQUIRE_EQ(linearDesc, linearConstDc.getDataDescriptor());
559 5 : REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
560 :
561 5 : AND_THEN("view is not a shallow copy")
562 5 : {
563 5 : const auto dcCopy = dc;
564 5 : const auto constDcCopy = constDc;
565 :
566 5 : linearDc[0] = 1;
567 5 : REQUIRE_EQ(&linearDc[0], &dc[0]);
568 5 : REQUIRE_NE(&linearDc[0], &dcCopy[0]);
569 :
570 5 : linearConstDc[0] = 1;
571 5 : REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
572 5 : REQUIRE_NE(&linearConstDc[0], &constDcCopy[0]);
573 5 : }
574 5 : }
575 5 : }
576 10 : }
577 :
578 20 : GIVEN("a blocked container")
579 20 : {
580 10 : IndexVector_t numCoeff(2);
581 10 : numCoeff << 52, 29;
582 10 : VolumeDescriptor desc(numCoeff);
583 10 : index_t numBlocks = 7;
584 10 : IdenticalBlocksDescriptor blockDesc(numBlocks, desc);
585 :
586 10 : DataContainer<data_t> dc(blockDesc);
587 10 : const DataContainer<data_t> constDc(blockDesc);
588 :
589 10 : WHEN("referencing a block")
590 10 : {
591 5 : THEN("block has the correct descriptor and data")
592 5 : {
593 40 : for (index_t i = 0; i < numBlocks; i++) {
594 35 : auto dcBlock = dc.getBlock(i);
595 35 : const auto constDcBlock = constDc.getBlock(i);
596 :
597 35 : REQUIRE_EQ(dcBlock.getDataDescriptor(), blockDesc.getDescriptorOfBlock(i));
598 35 : REQUIRE_EQ(&dcBlock[0], &dc[0] + blockDesc.getOffsetOfBlock(i));
599 :
600 35 : REQUIRE_EQ(constDcBlock.getDataDescriptor(), blockDesc.getDescriptorOfBlock(i));
601 35 : REQUIRE_EQ(&constDcBlock[0], &constDc[0] + blockDesc.getOffsetOfBlock(i));
602 35 : }
603 5 : }
604 5 : }
605 :
606 10 : WHEN("creating a view")
607 10 : {
608 5 : IndexVector_t numCoeff(1);
609 5 : numCoeff << blockDesc.getNumberOfCoefficients();
610 5 : VolumeDescriptor linearDesc(numCoeff);
611 5 : auto linearDc = dc.viewAs(linearDesc);
612 5 : auto linearConstDc = constDc.viewAs(linearDesc);
613 :
614 5 : THEN("view has the correct descriptor and data")
615 5 : {
616 5 : REQUIRE_EQ(linearDesc, linearDc.getDataDescriptor());
617 5 : REQUIRE_EQ(&linearDc[0], &dc[0]);
618 :
619 5 : REQUIRE_EQ(linearDesc, linearConstDc.getDataDescriptor());
620 5 : REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
621 :
622 5 : AND_THEN("view is not a shallow copy")
623 5 : {
624 5 : const auto dcCopy = dc;
625 5 : const auto constDcCopy = constDc;
626 :
627 5 : linearDc[0] = 1;
628 5 : REQUIRE_EQ(&linearDc[0], &dc[0]);
629 5 : REQUIRE_NE(&linearDc[0], &dcCopy[0]);
630 :
631 5 : linearConstDc[0] = 1;
632 5 : REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
633 5 : REQUIRE_NE(&linearConstDc[0], &constDcCopy[0]);
634 5 : }
635 5 : }
636 5 : }
637 10 : }
638 20 : }
639 :
640 : TEST_CASE("DataContainer: Testing iterators for DataContainer")
641 9 : {
642 9 : GIVEN("A 1D container")
643 9 : {
644 3 : constexpr index_t size = 20;
645 3 : IndexVector_t numCoeff(1);
646 3 : numCoeff << size;
647 3 : VolumeDescriptor desc(numCoeff);
648 :
649 3 : DataContainer dc1(desc);
650 3 : DataContainer dc2(desc);
651 :
652 3 : Eigen::VectorXf randVec1 = Eigen::VectorXf::Ones(size);
653 3 : Eigen::VectorXf randVec2 = Eigen::VectorXf::Random(size);
654 :
655 63 : for (index_t i = 0; i < size; ++i) {
656 60 : dc1[i] = randVec1(i);
657 60 : dc2[i] = randVec2(i);
658 60 : }
659 :
660 3 : THEN("We can iterate forward")
661 3 : {
662 1 : int i = 0;
663 1 : auto first = dc1.begin();
664 1 : auto last = dc1.end();
665 :
666 21 : while (first != last) {
667 20 : INFO("Error at position: ", i);
668 20 : CHECK_UNARY(checkApproxEq(*first, randVec1[i]));
669 20 : ++i;
670 20 : ++first;
671 20 : }
672 1 : REQUIRE_EQ(i, size);
673 1 : }
674 :
675 : // THEN("We can iterate backward")
676 : // {
677 : // int i = size;
678 : // for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
679 : // REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
680 : // }
681 : // REQUIRE_EQ(i, 0);
682 : // }
683 :
684 3 : THEN("We can iterate and mutate")
685 3 : {
686 1 : int i = 0;
687 20 : for (auto& v : dc1) {
688 20 : v = v * 2;
689 20 : REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
690 20 : }
691 1 : REQUIRE_EQ(i, size);
692 :
693 1 : i = 0;
694 20 : for (auto v : dc1) {
695 20 : REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
696 20 : }
697 1 : REQUIRE_EQ(i, size);
698 1 : }
699 :
700 3 : THEN("We can use STL algorithms")
701 3 : {
702 1 : REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
703 1 : REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
704 1 : }
705 3 : }
706 9 : GIVEN("A 2D container")
707 9 : {
708 3 : constexpr index_t size = 20;
709 3 : IndexVector_t numCoeff(2);
710 3 : numCoeff << size, size;
711 3 : VolumeDescriptor desc(numCoeff);
712 :
713 3 : DataContainer dc1(desc);
714 :
715 3 : Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size);
716 :
717 1203 : for (index_t i = 0; i < dc1.getSize(); ++i) {
718 1200 : dc1[i] = randVec1[i];
719 1200 : }
720 :
721 3 : THEN("We can iterate forward")
722 3 : {
723 1 : int i = 0;
724 400 : for (auto v : dc1) {
725 400 : REQUIRE_UNARY(checkApproxEq(v, randVec1[i++]));
726 400 : }
727 1 : REQUIRE_EQ(i, size * size);
728 1 : }
729 :
730 : // THEN("We can iterate backward")
731 : // {
732 : // int i = size * size;
733 : // for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
734 : // REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
735 : // }
736 : // REQUIRE_EQ(i, 0);
737 : // }
738 :
739 3 : THEN("We can iterate and mutate")
740 3 : {
741 1 : int i = 0;
742 400 : for (auto& v : dc1) {
743 400 : v = v * 2;
744 400 : REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
745 400 : }
746 1 : REQUIRE_EQ(i, size * size);
747 :
748 1 : i = 0;
749 400 : for (auto v : dc1) {
750 400 : REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
751 400 : }
752 1 : REQUIRE_EQ(i, size * size);
753 1 : }
754 :
755 3 : THEN("We can use STL algorithms")
756 3 : {
757 1 : REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
758 1 : REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
759 1 : }
760 3 : }
761 9 : GIVEN("A 3D container")
762 9 : {
763 3 : constexpr index_t size = 20;
764 3 : IndexVector_t numCoeff(3);
765 3 : numCoeff << size, size, size;
766 3 : VolumeDescriptor desc(numCoeff);
767 :
768 3 : DataContainer dc1(desc);
769 :
770 3 : Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size * size);
771 :
772 24003 : for (index_t i = 0; i < dc1.getSize(); ++i) {
773 24000 : dc1[i] = randVec1[i];
774 24000 : }
775 :
776 3 : THEN("We can iterate forward")
777 3 : {
778 1 : int i = 0;
779 8000 : for (auto v : dc1) {
780 8000 : REQUIRE_UNARY(checkApproxEq(v, randVec1[i++]));
781 8000 : }
782 1 : REQUIRE_EQ(i, size * size * size);
783 1 : }
784 :
785 : // THEN("We can iterate backward")
786 : // {
787 : // int i = size * size * size;
788 : // for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
789 : // REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
790 : // }
791 : // REQUIRE_EQ(i, 0);
792 : // }
793 :
794 3 : THEN("We can iterate and mutate")
795 3 : {
796 1 : int i = 0;
797 8000 : for (auto& v : dc1) {
798 8000 : v = v * 2;
799 8000 : REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
800 8000 : }
801 1 : REQUIRE_EQ(i, size * size * size);
802 :
803 1 : i = 0;
804 8000 : for (auto v : dc1) {
805 8000 : REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
806 8000 : }
807 1 : REQUIRE_EQ(i, size * size * size);
808 1 : }
809 :
810 3 : THEN("We can use STL algorithms")
811 3 : {
812 1 : REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
813 1 : REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
814 1 : }
815 3 : }
816 9 : }
817 :
818 : TEST_CASE_TEMPLATE("DataContainer: Clip a DataContainer", data_t, float, double)
819 20 : {
820 20 : GIVEN("some 1D vectors")
821 20 : {
822 16 : index_t size = 7;
823 16 : IndexVector_t numCoeff(1);
824 16 : numCoeff << size;
825 16 : VolumeDescriptor desc(numCoeff);
826 :
827 16 : data_t min = 6;
828 16 : data_t max = 19;
829 :
830 16 : Vector_t<data_t> dataVec1(desc.getNumberOfCoefficients());
831 16 : dataVec1 << 6, 10, 7, 18, 10, 11, 9;
832 16 : Vector_t<data_t> expectedDataVec1(desc.getNumberOfCoefficients());
833 16 : expectedDataVec1 << 6, 10, 7, 18, 10, 11, 9;
834 :
835 16 : Vector_t<data_t> dataVec2(desc.getNumberOfCoefficients());
836 16 : dataVec2 << 4, -23, 7, 18, 18, 10, 10;
837 16 : Vector_t<data_t> expectedDataVec2(desc.getNumberOfCoefficients());
838 16 : expectedDataVec2 << min, min, 7, 18, 18, 10, 10;
839 :
840 16 : Vector_t<data_t> dataVec3(desc.getNumberOfCoefficients());
841 16 : dataVec3 << 14, 23, 7, 18, 20, 10, 10;
842 16 : Vector_t<data_t> expectedDataVec3(desc.getNumberOfCoefficients());
843 16 : expectedDataVec3 << 14, max, 7, 18, max, 10, 10;
844 :
845 16 : Vector_t<data_t> dataVec4(desc.getNumberOfCoefficients());
846 16 : dataVec4 << 1, 23, 5, 28, 20, 30, 0;
847 16 : Vector_t<data_t> expectedDataVec4(desc.getNumberOfCoefficients());
848 16 : expectedDataVec4 << min, max, min, max, max, max, min;
849 :
850 16 : WHEN("creating a data container out of a vector within bounds")
851 16 : {
852 4 : DataContainer dc(desc, dataVec1);
853 4 : auto clipped = clip(dc, min, max);
854 :
855 4 : THEN("the size of the clipped DataContainer is equal to that of the original container")
856 4 : {
857 2 : REQUIRE_EQ(clipped.getSize(), size);
858 2 : }
859 :
860 4 : THEN("the values correspond to the original DataContainers")
861 4 : {
862 16 : for (int i = 0; i < size; ++i) {
863 14 : INFO("Error at position: ", i);
864 14 : REQUIRE_EQ(clipped[i], expectedDataVec1[i]);
865 14 : }
866 2 : }
867 4 : }
868 :
869 16 : WHEN("creating a data container out of a vector within or lower than the bounds")
870 16 : {
871 4 : DataContainer dc(desc, dataVec2);
872 4 : auto clipped = clip(dc, min, max);
873 :
874 4 : THEN("the size of the clipped DataContainer is equal to that of the original container")
875 4 : {
876 2 : REQUIRE_EQ(clipped.getSize(), size);
877 2 : }
878 :
879 4 : THEN("the values correspond to the original DataContainers")
880 4 : {
881 16 : for (int i = 0; i < size; ++i) {
882 14 : INFO("Error at position: ", i);
883 14 : REQUIRE_EQ(clipped[i], expectedDataVec2[i]);
884 14 : }
885 2 : }
886 4 : }
887 :
888 16 : WHEN("creating a data container out of a vector within or higher than the bounds")
889 16 : {
890 4 : DataContainer dc(desc, dataVec3);
891 4 : auto clipped = clip(dc, min, max);
892 :
893 4 : THEN("the size of the clipped DataContainer is equal to that of the original container")
894 4 : {
895 2 : REQUIRE_EQ(clipped.getSize(), size);
896 2 : }
897 :
898 4 : THEN("the values correspond to the original DataContainers")
899 4 : {
900 16 : for (int i = 0; i < size; ++i) {
901 14 : INFO("Error at position: ", i);
902 14 : REQUIRE_EQ(clipped[i], expectedDataVec3[i]);
903 14 : }
904 2 : }
905 4 : }
906 :
907 16 : WHEN("creating a data container out of a vector outside the bounds")
908 16 : {
909 4 : DataContainer dc(desc, dataVec4);
910 4 : auto clipped = clip(dc, min, max);
911 :
912 4 : THEN("the size of the clipped DataContainer is equal to that of the original container")
913 4 : {
914 2 : REQUIRE_EQ(clipped.getSize(), size);
915 2 : }
916 :
917 4 : THEN("the values correspond to the original DataContainers")
918 4 : {
919 16 : for (int i = 0; i < size; ++i) {
920 14 : INFO("Error at position: ", i);
921 14 : REQUIRE_EQ(clipped[i], expectedDataVec4[i]);
922 14 : }
923 2 : }
924 4 : }
925 16 : }
926 :
927 20 : GIVEN("a 2D data container")
928 20 : {
929 4 : IndexVector_t numCoeff(2);
930 4 : numCoeff << 3, 2;
931 4 : VolumeDescriptor desc(numCoeff);
932 :
933 4 : data_t min = 0;
934 4 : data_t max = 8;
935 :
936 4 : Vector_t<data_t> dataVec(desc.getNumberOfCoefficients());
937 4 : dataVec << -19, -23, 7, 8, 20, 1;
938 4 : Vector_t<data_t> expectedDataVec(desc.getNumberOfCoefficients());
939 4 : expectedDataVec << min, min, 7, 8, max, 1;
940 :
941 4 : WHEN("creating a data container out of a vector within and outside of both bounds")
942 4 : {
943 4 : DataContainer dc(desc, dataVec);
944 4 : auto clipped = clip(dc, min, max);
945 :
946 4 : THEN("the size of the clipped DataContainer is equal to that of the original container")
947 4 : {
948 2 : REQUIRE_EQ(clipped.getSize(), desc.getNumberOfCoefficients());
949 2 : }
950 :
951 4 : THEN("the values correspond to the original DataContainers")
952 4 : {
953 14 : for (int i = 0; i < desc.getNumberOfCoefficients(); ++i) {
954 12 : INFO("Error at position: ", i);
955 12 : REQUIRE_EQ(clipped[i], expectedDataVec[i]);
956 12 : }
957 2 : }
958 4 : }
959 4 : }
960 20 : }
961 :
962 : TEST_CASE_TEMPLATE("DataContainer: Concatenate two DataContainers", data_t, float, double,
963 : complex<float>, complex<double>)
964 28 : {
965 28 : GIVEN("Two equally sized 1D data containers")
966 28 : {
967 8 : constexpr index_t size = 20;
968 8 : IndexVector_t numCoeff(1);
969 8 : numCoeff << size;
970 8 : VolumeDescriptor desc(numCoeff);
971 :
972 8 : Vector_t<data_t> randVec1 = Vector_t<data_t>::Random(size);
973 8 : Vector_t<data_t> randVec2 = Vector_t<data_t>::Random(size);
974 :
975 8 : DataContainer dc1(desc, randVec1);
976 8 : DataContainer dc2(desc, randVec2);
977 :
978 8 : auto concated = concatenate(dc1, dc2);
979 8 : THEN("The size of the concatenated DataContainer is twice the original one")
980 8 : {
981 4 : REQUIRE_EQ(concated.getSize(), 2 * size);
982 4 : }
983 :
984 8 : THEN("The values correspond to the original DataContainers")
985 8 : {
986 84 : for (int i = 0; i < size; ++i) {
987 80 : INFO("Error at position: ", i);
988 80 : REQUIRE_EQ(concated[i], randVec1[i]);
989 80 : }
990 :
991 84 : for (int i = 0; i < size; ++i) {
992 80 : INFO("Error at position: ", i + size);
993 80 : REQUIRE_EQ(concated[i + size], randVec2[i]);
994 80 : }
995 4 : }
996 8 : }
997 :
998 28 : GIVEN("Two differently sized 1D data containers")
999 28 : {
1000 8 : IndexVector_t numCoeff(1);
1001 :
1002 8 : constexpr index_t size1 = 20;
1003 8 : numCoeff[0] = size1;
1004 8 : VolumeDescriptor desc1(numCoeff);
1005 :
1006 8 : constexpr index_t size2 = 10;
1007 8 : numCoeff[0] = size2;
1008 8 : VolumeDescriptor desc2(numCoeff);
1009 :
1010 8 : Vector_t<data_t> randVec1 = Vector_t<data_t>::Random(size1);
1011 8 : Vector_t<data_t> randVec2 = Vector_t<data_t>::Random(size2);
1012 :
1013 8 : DataContainer dc1(desc1, randVec1);
1014 8 : DataContainer dc2(desc2, randVec2);
1015 :
1016 8 : auto concated = concatenate(dc1, dc2);
1017 :
1018 8 : THEN("The size of the concatenated DataContainer is twice the original one")
1019 8 : {
1020 4 : REQUIRE_EQ(concated.getSize(), size1 + size2);
1021 4 : }
1022 :
1023 8 : THEN("The values correspond to the original DataContainers")
1024 8 : {
1025 84 : for (int i = 0; i < size1; ++i) {
1026 80 : INFO("Error at position: ", i);
1027 80 : REQUIRE_EQ(concated[i], randVec1[i]);
1028 80 : }
1029 :
1030 44 : for (int i = 0; i < size2; ++i) {
1031 40 : INFO("Error at position: ", i + size1);
1032 40 : REQUIRE_EQ(concated[i + size1], randVec2[i]);
1033 40 : }
1034 4 : }
1035 8 : }
1036 :
1037 28 : GIVEN("Two equally sized 2D data containers")
1038 28 : {
1039 8 : constexpr index_t size = 20;
1040 8 : IndexVector_t numCoeff(2);
1041 8 : numCoeff << size, size;
1042 8 : VolumeDescriptor desc(numCoeff);
1043 :
1044 8 : Vector_t<data_t> randVec1 = Vector_t<data_t>::Random(size * size);
1045 8 : Vector_t<data_t> randVec2 = Vector_t<data_t>::Random(size * size);
1046 :
1047 8 : DataContainer dc1(desc, randVec1);
1048 8 : DataContainer dc2(desc, randVec2);
1049 :
1050 8 : auto concated = concatenate(dc1, dc2);
1051 8 : THEN("The size of the concatenated DataContainer is twice the original one")
1052 8 : {
1053 4 : REQUIRE_EQ(concated.getSize(), 2 * (size * size));
1054 4 : }
1055 :
1056 8 : THEN("The values correspond to the original DataContainers")
1057 8 : {
1058 1604 : for (int i = 0; i < size * size; ++i) {
1059 1600 : INFO("Error at position: ", i);
1060 1600 : REQUIRE_EQ(concated[i], randVec1[i]);
1061 1600 : }
1062 :
1063 1604 : for (int i = 0; i < size * size; ++i) {
1064 1600 : INFO("Error at position: ", i + size);
1065 1600 : REQUIRE_EQ(concated[i + size * size], randVec2[i]);
1066 1600 : }
1067 4 : }
1068 8 : }
1069 :
1070 28 : GIVEN("DataContainers of different dimension")
1071 28 : {
1072 4 : IndexVector_t numCoeff1D(1);
1073 4 : numCoeff1D << 20;
1074 4 : VolumeDescriptor desc1D(numCoeff1D);
1075 :
1076 4 : IndexVector_t numCoeff2D(2);
1077 4 : numCoeff2D << 20, 20;
1078 4 : VolumeDescriptor desc2D(numCoeff2D);
1079 :
1080 4 : DataContainer dc1(desc1D);
1081 4 : DataContainer dc2(desc2D);
1082 :
1083 4 : THEN("The concatenation throws")
1084 4 : {
1085 4 : REQUIRE_THROWS_AS(concatenate(dc1, dc2), LogicError);
1086 4 : }
1087 4 : }
1088 28 : }
1089 :
1090 : TEST_CASE_TEMPLATE("DataContainer: Assign to a slice of a DataContainer", data_t, float, double,
1091 : complex<float>, complex<double>)
1092 4 : {
1093 : // Set seed for Eigen Matrices!
1094 4 : srand((unsigned int) 666);
1095 :
1096 4 : constexpr index_t size = 20;
1097 4 : IndexVector_t numCoeff2D(2);
1098 4 : numCoeff2D << size, size;
1099 :
1100 4 : const VolumeDescriptor desc(numCoeff2D);
1101 4 : const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
1102 4 : const DataContainer<data_t> dc(desc, randVec);
1103 :
1104 4 : IndexVector_t sliceCoeffs({{4, 5}});
1105 4 : const VolumeDescriptor sliceDesc(sliceCoeffs);
1106 :
1107 84 : for (int i = 0; i < size; ++i) {
1108 80 : auto slice = dc.slice(i);
1109 80 : auto ones = DataContainer<data_t>(sliceDesc, Vector_t<data_t>::Ones(size));
1110 80 : auto tmp = slice.viewAs(sliceDesc);
1111 :
1112 80 : slice.viewAs(sliceDesc) = ones;
1113 :
1114 1680 : for (int j = 0; j < size; ++j) {
1115 1600 : CHECK_UNARY(checkApproxEq(slice[j], ones[j]));
1116 1600 : CHECK_UNARY(checkApproxEq(dc(j, i), ones[j]));
1117 1600 : }
1118 80 : }
1119 4 : }
1120 :
1121 : TEST_CASE_TEMPLATE("DataContainer: Slice a DataContainer", data_t, float, double, complex<float>,
1122 : complex<double>)
1123 72 : {
1124 : // Set seed for Eigen Matrices!
1125 72 : srand((unsigned int) 666);
1126 :
1127 72 : GIVEN("A non 3D DataContainer")
1128 72 : {
1129 16 : constexpr index_t size = 20;
1130 16 : IndexVector_t numCoeff2D(2);
1131 16 : numCoeff2D << size, size;
1132 :
1133 16 : const VolumeDescriptor desc(numCoeff2D);
1134 16 : const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
1135 16 : const DataContainer<data_t> dc(desc, randVec);
1136 :
1137 16 : IndexVector_t sliceCoeffs({{4, 5}});
1138 16 : const VolumeDescriptor sliceDesc(sliceCoeffs);
1139 :
1140 16 : THEN("Accessing an out of bounds slice throws")
1141 16 : {
1142 4 : REQUIRE_THROWS_AS(dc.slice(20), LogicError);
1143 4 : }
1144 :
1145 16 : WHEN("Accessing a slice")
1146 16 : {
1147 12 : const auto i = 5;
1148 12 : auto slice = dc.slice(5);
1149 :
1150 12 : THEN("The the slice is a 2D slice of \"thickness\" 1")
1151 12 : {
1152 4 : REQUIRE_EQ(slice.getDataDescriptor().getNumberOfDimensions(), 2);
1153 :
1154 4 : auto coeffs = slice.getDataDescriptor().getNumberOfCoefficientsPerDimension();
1155 4 : auto expectedCoeffs = IndexVector_t(2);
1156 4 : expectedCoeffs << size, 1;
1157 4 : REQUIRE_EQ(coeffs, expectedCoeffs);
1158 4 : }
1159 :
1160 12 : THEN("All values are the same as of the original DataContainer")
1161 12 : {
1162 : // Check that it's read correctly
1163 4 : auto vecSlice = randVec.segment(i * size, size);
1164 84 : for (int j = 0; j < size; ++j) {
1165 80 : REQUIRE_UNARY(checkApproxEq(slice(j, 0), vecSlice[j]));
1166 80 : }
1167 4 : }
1168 :
1169 12 : THEN("Assigning to slice changes original container")
1170 12 : {
1171 4 : auto ones = DataContainer<data_t>(sliceDesc, Vector_t<data_t>::Ones(size));
1172 4 : auto tmp = slice.viewAs(sliceDesc);
1173 :
1174 4 : slice = ones;
1175 :
1176 84 : for (int j = 0; j < size; ++j) {
1177 80 : CHECK_UNARY(checkApproxEq(slice(j, 0), ones[j]));
1178 80 : CHECK_UNARY(checkApproxEq(dc(j, i), ones[j]));
1179 80 : }
1180 4 : }
1181 12 : }
1182 16 : }
1183 :
1184 72 : GIVEN("A const 3D DataContainer")
1185 72 : {
1186 16 : constexpr index_t size = 20;
1187 :
1188 16 : const VolumeDescriptor desc({size, size, size});
1189 16 : const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * size);
1190 16 : const DataContainer<data_t> dc(desc, randVec);
1191 :
1192 16 : THEN("Accessing an out of bounds slice throws")
1193 16 : {
1194 4 : REQUIRE_THROWS_AS(dc.slice(20), LogicError);
1195 4 : }
1196 :
1197 16 : WHEN("Accessing all the slices")
1198 16 : {
1199 252 : for (int i = 0; i < size; ++i) {
1200 240 : auto slice = dc.slice(i);
1201 :
1202 240 : THEN("The the slice is a 3D slice of \"thickness\" 1")
1203 240 : {
1204 4 : REQUIRE_EQ(slice.getDataDescriptor().getNumberOfDimensions(), 3);
1205 :
1206 4 : auto coeffs = slice.getDataDescriptor().getNumberOfCoefficientsPerDimension();
1207 4 : auto expectedCoeffs = IndexVector_t(3);
1208 4 : expectedCoeffs << size, size, 1;
1209 4 : REQUIRE_EQ(coeffs, expectedCoeffs);
1210 4 : }
1211 :
1212 240 : THEN("All values are the same as of the original DataContainer")
1213 240 : {
1214 : // Check that it's read correctly
1215 4 : auto vecSlice = randVec.segment(i * size * size, size * size);
1216 84 : for (int j = 0; j < size; ++j) {
1217 1680 : for (int k = 0; k < size; ++k) {
1218 1600 : REQUIRE_UNARY(checkApproxEq(slice(k, j, 0), vecSlice[k + j * size]));
1219 1600 : }
1220 80 : }
1221 4 : }
1222 240 : }
1223 12 : }
1224 16 : }
1225 :
1226 72 : GIVEN("A non-const 3D DataContainer")
1227 72 : {
1228 32 : constexpr index_t size = 20;
1229 32 : IndexVector_t numCoeff(3);
1230 32 : numCoeff << size, size, size;
1231 :
1232 32 : const VolumeDescriptor desc(numCoeff);
1233 32 : DataContainer<data_t> dc(desc);
1234 32 : dc = 0;
1235 :
1236 32 : THEN("Accessing an out of bounds slice throws")
1237 32 : {
1238 4 : REQUIRE_THROWS_AS(dc.slice(20), LogicError);
1239 4 : }
1240 :
1241 32 : WHEN("Setting the first slice to 1")
1242 32 : {
1243 8 : dc.slice(0) = 1;
1244 :
1245 8 : THEN("Only the first slice is set to 1")
1246 8 : {
1247 84 : for (int j = 0; j < size; ++j) {
1248 1680 : for (int i = 0; i < size; ++i) {
1249 1600 : data_t val = dc(i, j, 0);
1250 1600 : INFO("Expected slice 0 to be ", data_t{1}, " but it's ", val, " (at (", i,
1251 1600 : ", ", j, ", 0))");
1252 1600 : REQUIRE_UNARY(checkApproxEq(val, 1));
1253 1600 : }
1254 80 : }
1255 4 : }
1256 :
1257 8 : THEN("The other slices are still set to 0")
1258 8 : {
1259 80 : for (int k = 1; k < size; ++k) {
1260 1596 : for (int j = 0; j < size; ++j) {
1261 31920 : for (int i = 0; i < size; ++i) {
1262 30400 : data_t val = dc(i, j, k);
1263 30400 : INFO("Expected all slices but the first to be ", data_t{0},
1264 30400 : " but it's ", val, " (at (", i, ", ", j, ", 0))");
1265 30400 : REQUIRE_UNARY(checkApproxEq(val, 0));
1266 30400 : }
1267 1520 : }
1268 76 : }
1269 4 : }
1270 8 : }
1271 :
1272 32 : WHEN("Setting the fifth slice to some random data using a 3D DataContainer")
1273 32 : {
1274 12 : Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * 1);
1275 12 : const DataContainer slice(VolumeDescriptor({size, size, 1}), randVec);
1276 :
1277 12 : dc.slice(5) = slice;
1278 12 : THEN("The first 4 slices are still zero")
1279 12 : {
1280 24 : for (int k = 0; k < 5; ++k) {
1281 420 : for (int j = 0; j < size; ++j) {
1282 8400 : for (int i = 0; i < size; ++i) {
1283 8000 : data_t val = dc(i, j, k);
1284 :
1285 8000 : INFO("Expected all slices but the first to be ", data_t{0},
1286 8000 : " but it's ", val, " (at (", i, ", ", j, ", 0))");
1287 8000 : REQUIRE_UNARY(checkApproxEq(val, 0));
1288 8000 : }
1289 400 : }
1290 20 : }
1291 4 : }
1292 :
1293 12 : THEN("The fifth slices set correctly")
1294 12 : {
1295 84 : for (int j = 0; j < size; ++j) {
1296 1680 : for (int i = 0; i < size; ++i) {
1297 1600 : data_t val = dc(i, j, 5);
1298 1600 : auto expected = randVec[i + j * size];
1299 1600 : INFO("Expected slice 0 to be ", expected, " but it's ", val, " (at (", i,
1300 1600 : ", ", j, ", 0))");
1301 1600 : REQUIRE_UNARY(checkApproxEq(val, expected));
1302 1600 : }
1303 80 : }
1304 4 : }
1305 :
1306 12 : THEN("The last 14 slices are still zero")
1307 12 : {
1308 : // Check last slices
1309 60 : for (int k = 6; k < size; ++k) {
1310 1176 : for (int j = 0; j < size; ++j) {
1311 23520 : for (int i = 0; i < size; ++i) {
1312 22400 : data_t val = dc(i, j, k);
1313 :
1314 22400 : INFO("Expected all slices but the first to be ", data_t{0},
1315 22400 : " but it's ", val, " (at (", i, ", ", j, ", 0))");
1316 22400 : REQUIRE_UNARY(checkApproxEq(val, 0));
1317 22400 : }
1318 1120 : }
1319 56 : }
1320 4 : }
1321 12 : }
1322 :
1323 32 : WHEN("Setting the first slice to some random data using a 2D DataContainer")
1324 32 : {
1325 8 : Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size);
1326 8 : const DataContainer slice(VolumeDescriptor({size, size}), randVec);
1327 :
1328 8 : dc.slice(0) = slice;
1329 8 : THEN("The fifth slices set correctly")
1330 8 : {
1331 84 : for (int j = 0; j < size; ++j) {
1332 1680 : for (int i = 0; i < size; ++i) {
1333 1600 : data_t val = dc(i, j, 0);
1334 1600 : auto expected = randVec[i + j * size];
1335 1600 : INFO("Expected slice 0 to be ", expected, " but it's ", val, " (at (", i,
1336 1600 : ", ", j, ", 0))");
1337 1600 : REQUIRE_UNARY(checkApproxEq(val, expected));
1338 1600 : }
1339 80 : }
1340 4 : }
1341 8 : THEN("The other slices are still zero")
1342 8 : {
1343 80 : for (int k = 1; k < size; ++k) {
1344 1596 : for (int j = 0; j < size; ++j) {
1345 31920 : for (int i = 0; i < size; ++i) {
1346 30400 : data_t val = dc(i, j, k);
1347 30400 : INFO("Expected all slices but the first to be ", data_t{0},
1348 30400 : " but it's ", val, " (at (", i, ", ", j, ", 0))");
1349 30400 : REQUIRE_UNARY(checkApproxEq(val, 0));
1350 30400 : }
1351 1520 : }
1352 76 : }
1353 4 : }
1354 8 : }
1355 32 : }
1356 :
1357 72 : GIVEN("a 3D DataDescriptor and a 3D random Vector")
1358 72 : {
1359 8 : constexpr index_t size = 28;
1360 8 : constexpr index_t one = 1;
1361 8 : IndexVector_t numCoeff3D(3);
1362 8 : numCoeff3D << size, size, one;
1363 :
1364 8 : const VolumeDescriptor desc(numCoeff3D);
1365 8 : const Vector_t<data_t> randVec = Vector_t<data_t>::Random(size * size * one);
1366 :
1367 8 : WHEN("slicing a non-const DataContainer with the size of the last dimension of 1")
1368 8 : {
1369 4 : DataContainer<data_t> dc(desc, randVec);
1370 :
1371 4 : DataContainer<data_t> res = dc.slice(0);
1372 :
1373 4 : THEN("the DataContainers match")
1374 4 : {
1375 4 : REQUIRE_EQ(dc, res);
1376 4 : }
1377 4 : }
1378 :
1379 8 : WHEN("slicing a const DataContainer with the size of the last dimension of 1")
1380 8 : {
1381 4 : const DataContainer<data_t> dc(desc, randVec);
1382 :
1383 4 : const DataContainer<data_t> res = dc.slice(0);
1384 :
1385 4 : THEN("the DataContainers match")
1386 4 : {
1387 4 : REQUIRE_EQ(dc, res);
1388 4 : }
1389 4 : }
1390 8 : }
1391 72 : }
1392 :
1393 : TEST_CASE_TEMPLATE("DataContainer: FFT shift and IFFT shift a DataContainer", data_t, float, double,
1394 : complex<float>, complex<double>)
1395 48 : {
1396 48 : GIVEN("a one-element 2D data container")
1397 48 : {
1398 16 : DataContainer<data_t> dc(VolumeDescriptor{{1, 1}});
1399 16 : dc[0] = 8;
1400 16 : WHEN("running the FFT shift operation to the container")
1401 16 : {
1402 8 : DataContainer<data_t> fftShiftedDC = fftShift2D(dc);
1403 8 : THEN("the data descriptors match")
1404 8 : {
1405 4 : REQUIRE_EQ(dc.getDataDescriptor(), fftShiftedDC.getDataDescriptor());
1406 4 : }
1407 8 : THEN("the data containers match")
1408 8 : {
1409 4 : REQUIRE_UNARY(fftShiftedDC == dc);
1410 4 : }
1411 8 : }
1412 :
1413 16 : WHEN("running the IFFT shift operation to the container")
1414 16 : {
1415 8 : DataContainer<data_t> ifftShiftedDC = ifftShift2D(dc);
1416 8 : THEN("the data descriptors match")
1417 8 : {
1418 4 : REQUIRE_EQ(dc.getDataDescriptor(), ifftShiftedDC.getDataDescriptor());
1419 4 : }
1420 8 : THEN("the data containers match")
1421 8 : {
1422 4 : REQUIRE_UNARY(ifftShiftedDC == dc);
1423 4 : }
1424 8 : }
1425 16 : }
1426 :
1427 48 : GIVEN("a 3x3 2D data container")
1428 48 : {
1429 16 : DataContainer<data_t> dc(VolumeDescriptor{{3, 3}});
1430 16 : dc(0, 0) = 0;
1431 16 : dc(0, 1) = 1;
1432 16 : dc(0, 2) = 2;
1433 16 : dc(1, 0) = 3;
1434 16 : dc(1, 1) = 4;
1435 16 : dc(1, 2) = -4;
1436 16 : dc(2, 0) = -3;
1437 16 : dc(2, 1) = -2;
1438 16 : dc(2, 2) = -1;
1439 :
1440 16 : DataContainer<data_t> expectedFFTShiftDC(VolumeDescriptor{{3, 3}});
1441 16 : expectedFFTShiftDC(0, 0) = -1;
1442 16 : expectedFFTShiftDC(0, 1) = -3;
1443 16 : expectedFFTShiftDC(0, 2) = -2;
1444 16 : expectedFFTShiftDC(1, 0) = 2;
1445 16 : expectedFFTShiftDC(1, 1) = 0;
1446 16 : expectedFFTShiftDC(1, 2) = 1;
1447 16 : expectedFFTShiftDC(2, 0) = -4;
1448 16 : expectedFFTShiftDC(2, 1) = 3;
1449 16 : expectedFFTShiftDC(2, 2) = 4;
1450 :
1451 16 : WHEN("running the FFT shift operation to the container")
1452 16 : {
1453 8 : DataContainer<data_t> fftShiftedDC = fftShift2D(dc);
1454 8 : THEN("the data descriptors match")
1455 8 : {
1456 4 : REQUIRE_EQ(fftShiftedDC.getDataDescriptor(),
1457 4 : expectedFFTShiftDC.getDataDescriptor());
1458 4 : }
1459 8 : THEN("the data containers match")
1460 8 : {
1461 4 : REQUIRE_UNARY(fftShiftedDC == expectedFFTShiftDC);
1462 4 : }
1463 8 : }
1464 :
1465 16 : DataContainer<data_t> expectedIFFTShiftDC(VolumeDescriptor{{3, 3}});
1466 16 : expectedIFFTShiftDC(0, 0) = 4;
1467 16 : expectedIFFTShiftDC(0, 1) = -4;
1468 16 : expectedIFFTShiftDC(0, 2) = 3;
1469 16 : expectedIFFTShiftDC(1, 0) = -2;
1470 16 : expectedIFFTShiftDC(1, 1) = -1;
1471 16 : expectedIFFTShiftDC(1, 2) = -3;
1472 16 : expectedIFFTShiftDC(2, 0) = 1;
1473 16 : expectedIFFTShiftDC(2, 1) = 2;
1474 16 : expectedIFFTShiftDC(2, 2) = 0;
1475 :
1476 16 : WHEN("running the IFFT shift operation to the container")
1477 16 : {
1478 8 : DataContainer<data_t> ifftShiftedDC = ifftShift2D(dc);
1479 8 : THEN("the data descriptors match")
1480 8 : {
1481 4 : REQUIRE_EQ(ifftShiftedDC.getDataDescriptor(),
1482 4 : expectedIFFTShiftDC.getDataDescriptor());
1483 4 : }
1484 8 : THEN("the data containers match")
1485 8 : {
1486 4 : REQUIRE_UNARY(ifftShiftedDC == expectedIFFTShiftDC);
1487 4 : }
1488 8 : }
1489 16 : }
1490 :
1491 48 : GIVEN("a 5x5 2D data container")
1492 48 : {
1493 16 : DataContainer<data_t> dc(VolumeDescriptor{{5, 5}});
1494 16 : dc(0, 0) = 28;
1495 16 : dc(0, 1) = 1;
1496 16 : dc(0, 2) = 5;
1497 16 : dc(0, 3) = -18;
1498 16 : dc(0, 4) = 8;
1499 16 : dc(1, 0) = 5;
1500 16 : dc(1, 1) = 6;
1501 16 : dc(1, 2) = 50;
1502 16 : dc(1, 3) = -8;
1503 16 : dc(1, 4) = 9;
1504 16 : dc(2, 0) = 8;
1505 16 : dc(2, 1) = 9;
1506 16 : dc(2, 2) = 10;
1507 16 : dc(2, 3) = 11;
1508 16 : dc(2, 4) = 12;
1509 16 : dc(3, 0) = -12;
1510 16 : dc(3, 1) = -41;
1511 16 : dc(3, 2) = -10;
1512 16 : dc(3, 3) = -9;
1513 16 : dc(3, 4) = -8;
1514 16 : dc(4, 0) = -70;
1515 16 : dc(4, 1) = -6;
1516 16 : dc(4, 2) = 22;
1517 16 : dc(4, 3) = -10;
1518 16 : dc(4, 4) = -3;
1519 :
1520 16 : DataContainer<data_t> expectedFFTShiftDC(VolumeDescriptor{{5, 5}});
1521 16 : expectedFFTShiftDC(0, 0) = -9;
1522 16 : expectedFFTShiftDC(0, 1) = -8;
1523 16 : expectedFFTShiftDC(0, 2) = -12;
1524 16 : expectedFFTShiftDC(0, 3) = -41;
1525 16 : expectedFFTShiftDC(0, 4) = -10;
1526 16 : expectedFFTShiftDC(1, 0) = -10;
1527 16 : expectedFFTShiftDC(1, 1) = -3;
1528 16 : expectedFFTShiftDC(1, 2) = -70;
1529 16 : expectedFFTShiftDC(1, 3) = -6;
1530 16 : expectedFFTShiftDC(1, 4) = 22;
1531 16 : expectedFFTShiftDC(2, 0) = -18;
1532 16 : expectedFFTShiftDC(2, 1) = 8;
1533 16 : expectedFFTShiftDC(2, 2) = 28;
1534 16 : expectedFFTShiftDC(2, 3) = 1;
1535 16 : expectedFFTShiftDC(2, 4) = 5;
1536 16 : expectedFFTShiftDC(3, 0) = -8;
1537 16 : expectedFFTShiftDC(3, 1) = 9;
1538 16 : expectedFFTShiftDC(3, 2) = 5;
1539 16 : expectedFFTShiftDC(3, 3) = 6;
1540 16 : expectedFFTShiftDC(3, 4) = 50;
1541 16 : expectedFFTShiftDC(4, 0) = 11;
1542 16 : expectedFFTShiftDC(4, 1) = 12;
1543 16 : expectedFFTShiftDC(4, 2) = 8;
1544 16 : expectedFFTShiftDC(4, 3) = 9;
1545 16 : expectedFFTShiftDC(4, 4) = 10;
1546 :
1547 16 : WHEN("running the FFT shift operation to the container")
1548 16 : {
1549 8 : DataContainer<data_t> fftShiftedDC = fftShift2D(dc);
1550 8 : THEN("the data descriptors match")
1551 8 : {
1552 4 : REQUIRE_EQ(fftShiftedDC.getDataDescriptor(),
1553 4 : expectedFFTShiftDC.getDataDescriptor());
1554 4 : }
1555 8 : THEN("the data containers match")
1556 8 : {
1557 4 : REQUIRE_UNARY(fftShiftedDC == expectedFFTShiftDC);
1558 4 : }
1559 8 : }
1560 :
1561 16 : DataContainer<data_t> expectedIFFTShiftDC(VolumeDescriptor{{5, 5}});
1562 16 : expectedIFFTShiftDC(0, 0) = 10;
1563 16 : expectedIFFTShiftDC(0, 1) = 11;
1564 16 : expectedIFFTShiftDC(0, 2) = 12;
1565 16 : expectedIFFTShiftDC(0, 3) = 8;
1566 16 : expectedIFFTShiftDC(0, 4) = 9;
1567 16 : expectedIFFTShiftDC(1, 0) = -10;
1568 16 : expectedIFFTShiftDC(1, 1) = -9;
1569 16 : expectedIFFTShiftDC(1, 2) = -8;
1570 16 : expectedIFFTShiftDC(1, 3) = -12;
1571 16 : expectedIFFTShiftDC(1, 4) = -41;
1572 16 : expectedIFFTShiftDC(2, 0) = 22;
1573 16 : expectedIFFTShiftDC(2, 1) = -10;
1574 16 : expectedIFFTShiftDC(2, 2) = -3;
1575 16 : expectedIFFTShiftDC(2, 3) = -70;
1576 16 : expectedIFFTShiftDC(2, 4) = -6;
1577 16 : expectedIFFTShiftDC(3, 0) = 5;
1578 16 : expectedIFFTShiftDC(3, 1) = -18;
1579 16 : expectedIFFTShiftDC(3, 2) = 8;
1580 16 : expectedIFFTShiftDC(3, 3) = 28;
1581 16 : expectedIFFTShiftDC(3, 4) = 1;
1582 16 : expectedIFFTShiftDC(4, 0) = 50;
1583 16 : expectedIFFTShiftDC(4, 1) = -8;
1584 16 : expectedIFFTShiftDC(4, 2) = 9;
1585 16 : expectedIFFTShiftDC(4, 3) = 5;
1586 16 : expectedIFFTShiftDC(4, 4) = 6;
1587 :
1588 16 : WHEN("running the IFFT shift operation to the container")
1589 16 : {
1590 8 : DataContainer<data_t> ifftShiftedDC = ifftShift2D(dc);
1591 8 : THEN("the data descriptors match")
1592 8 : {
1593 4 : REQUIRE_EQ(ifftShiftedDC.getDataDescriptor(),
1594 4 : expectedIFFTShiftDC.getDataDescriptor());
1595 4 : }
1596 8 : THEN("the data containers match")
1597 8 : {
1598 4 : REQUIRE_UNARY(ifftShiftedDC == expectedIFFTShiftDC);
1599 4 : }
1600 8 : }
1601 16 : }
1602 48 : }
1603 :
1604 : // "instantiate" the test templates for CPU types
1605 : TEST_CASE_TEMPLATE_APPLY(datacontainer_construction, CPUTypeTuple);
1606 : TEST_CASE_TEMPLATE_APPLY(datacontainer_reduction, CPUTypeTuple);
1607 : TEST_CASE_TEMPLATE_APPLY(datacontainer_elemwise, CPUTypeTuple);
1608 : TEST_CASE_TEMPLATE_APPLY(datacontainer_arithmetic, CPUTypeTuple);
1609 : TEST_CASE_TEMPLATE_APPLY(datacontainer_maps, CPUTypeTuple);
1610 :
1611 : #ifdef ELSA_CUDA_VECTOR
1612 : // "instantiate" the test templates for GPU types
1613 : TEST_CASE_TEMPLATE_APPLY(datacontainer_construction, GPUTypeTuple);
1614 : TEST_CASE_TEMPLATE_APPLY(datacontainer_reduction, GPUTypeTuple);
1615 : TEST_CASE_TEMPLATE_APPLY(datacontainer_elemwise, GPUTypeTuple);
1616 : TEST_CASE_TEMPLATE_APPLY(datacontainer_arithmetic, GPUTypeTuple);
1617 : TEST_CASE_TEMPLATE_APPLY(datacontainer_maps, GPUTypeTuple);
1618 : #endif
1619 :
1620 : TEST_SUITE_END();
|