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