Line data Source code
1 : /**
2 : * @file test_DataHandlers.cpp
3 : *
4 : * @brief Common tests for DataHandlers class
5 : *
6 : * @author David Frank - initial code
7 : * @author Tobias Lasser - rewrite and code coverage
8 : * @author Jens Petit - refactoring to general DataHandler test
9 : */
10 :
11 : #include "doctest/doctest.h"
12 : #include "DataHandlerCPU.h"
13 : #include "DataHandlerMapCPU.h"
14 : #include "testHelpers.h"
15 :
16 : #ifdef ELSA_CUDA_VECTOR
17 : #include "DataHandlerGPU.h"
18 : #include "DataHandlerMapGPU.h"
19 : #endif
20 :
21 : template <typename data_t>
22 : long elsa::useCount(const DataHandlerCPU<data_t>& dh)
23 306 : {
24 306 : return dh._data.use_count();
25 306 : }
26 :
27 : #ifdef ELSA_CUDA_VECTOR
28 : template <typename data_t>
29 : long elsa::useCount(const DataHandlerGPU<data_t>& dh)
30 : {
31 : return dh._data.use_count();
32 : }
33 : #endif
34 :
35 : using namespace elsa;
36 : using namespace doctest;
37 :
38 : using CPUTypeTuple =
39 : std::tuple<DataHandlerCPU<float>, DataHandlerCPU<double>, DataHandlerCPU<complex<float>>,
40 : DataHandlerCPU<complex<double>>, DataHandlerCPU<index_t>>;
41 :
42 : TYPE_TO_STRING(DataHandlerCPU<float>);
43 : TYPE_TO_STRING(DataHandlerCPU<double>);
44 : TYPE_TO_STRING(DataHandlerCPU<index_t>);
45 : TYPE_TO_STRING(DataHandlerCPU<complex<float>>);
46 : TYPE_TO_STRING(DataHandlerCPU<complex<double>>);
47 :
48 : #ifdef ELSA_CUDA_VECTOR
49 : using GPUTypeTuple =
50 : std::tuple<DataHandlerGPU<float>, DataHandlerGPU<double>, DataHandlerGPU<complex<float>>,
51 : DataHandlerGPU<complex<double>>, DataHandlerGPU<index_t>>;
52 :
53 : TYPE_TO_STRING(DataHandlerGPU<float>);
54 : TYPE_TO_STRING(DataHandlerGPU<double>);
55 : TYPE_TO_STRING(DataHandlerGPU<index_t>);
56 : TYPE_TO_STRING(DataHandlerGPU<complex<float>>);
57 : TYPE_TO_STRING(DataHandlerGPU<complex<double>>);
58 : #endif
59 :
60 : TEST_SUITE_BEGIN("core");
61 :
62 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing Construction", TestType, datahandler_construction)
63 20 : {
64 20 : using data_t = typename TestType::value_type;
65 :
66 20 : GIVEN("a certain size")
67 20 : {
68 20 : index_t size = 314;
69 :
70 20 : WHEN("constructing")
71 20 : {
72 5 : const TestType dh{size};
73 :
74 5 : THEN("it has the correct size") { REQUIRE_EQ(size, dh.getSize()); }
75 5 : }
76 :
77 20 : WHEN("constructing with a given vector")
78 20 : {
79 5 : auto randVec = generateRandomMatrix<data_t>(size);
80 5 : const TestType dh{randVec};
81 :
82 1575 : for (index_t i = 0; i < size; ++i)
83 5 : REQUIRE_UNARY(checkApproxEq(dh[i], randVec(i)));
84 5 : }
85 :
86 20 : WHEN("copy constructing")
87 20 : {
88 5 : auto randVec = generateRandomMatrix<data_t>(size);
89 5 : const TestType dh{randVec};
90 5 : const auto dhView = dh.getBlock(0, size);
91 :
92 5 : TestType dh2 = dh;
93 :
94 5 : THEN("a shallow copy is created")
95 5 : {
96 5 : REQUIRE_EQ(dh2, dh);
97 5 : REQUIRE_EQ(useCount(dh), 2);
98 :
99 5 : const auto dh2View = dh2.getBlock(0, size);
100 5 : AND_THEN("associated maps are not transferred")
101 5 : {
102 5 : dh2[0] = data_t(1);
103 5 : REQUIRE_UNARY(checkApproxEq(dh2[0], 1));
104 5 : REQUIRE_UNARY(checkApproxEq((*dhView)[0], randVec[0]));
105 5 : REQUIRE_UNARY(checkApproxEq((*dh2View)[0], 1));
106 5 : }
107 5 : }
108 5 : }
109 :
110 20 : WHEN("move constructing")
111 20 : {
112 5 : auto randVec = generateRandomMatrix<data_t>(size);
113 5 : TestType dh{randVec};
114 5 : const auto dhView = dh.getBlock(0, size);
115 5 : TestType testDh{randVec};
116 :
117 5 : const TestType dh2 = std::move(dh);
118 :
119 5 : THEN("data and associated maps are moved to the new handler")
120 5 : {
121 5 : REQUIRE_EQ(useCount(dh2), 1);
122 5 : REQUIRE_EQ(dh2, testDh);
123 5 : REQUIRE_EQ(&(*dhView)[0], &dh2[0]);
124 5 : }
125 5 : }
126 20 : }
127 20 : }
128 :
129 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing equality operator", TestType, datahandler_equality)
130 20 : {
131 20 : using data_t = typename TestType::value_type;
132 :
133 20 : GIVEN("some DataHandler")
134 20 : {
135 20 : const index_t size = 314;
136 20 : auto randVec = generateRandomMatrix<data_t>(size);
137 20 : const TestType dh{randVec};
138 :
139 20 : WHEN("comparing to a handler with a different size")
140 20 : {
141 5 : const TestType dh2{size + 1};
142 5 : THEN("the result is false")
143 5 : {
144 5 : REQUIRE_NE(dh, dh2);
145 5 : REQUIRE_NE(dh, *dh2.getBlock(0, size + 1));
146 5 : }
147 5 : }
148 :
149 20 : WHEN("comparing to a shallow copy or view of the handler")
150 20 : {
151 5 : const auto dh2 = dh;
152 5 : THEN("the result is true")
153 5 : {
154 5 : REQUIRE_EQ(dh, dh2);
155 5 : REQUIRE_EQ(dh, *dh.getBlock(0, size));
156 5 : }
157 5 : }
158 :
159 20 : WHEN("comparing to a deep copy or a view of the deep copy")
160 20 : {
161 5 : const TestType dh2{randVec};
162 5 : THEN("the result is true")
163 5 : {
164 5 : REQUIRE_EQ(dh, dh2);
165 5 : REQUIRE_EQ(dh, *dh2.getBlock(0, size));
166 5 : }
167 5 : }
168 :
169 20 : WHEN("comparing to a handler or map with different data")
170 20 : {
171 5 : randVec[0] += 1;
172 5 : const TestType dh2{randVec};
173 5 : THEN("the result is false")
174 5 : {
175 5 : REQUIRE_NE(dh, dh2);
176 5 : REQUIRE_NE(dh, *dh2.getBlock(0, size));
177 5 : }
178 5 : }
179 20 : }
180 20 : }
181 :
182 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Assigning to DataHandlerCPU", TestType,
183 : datahandler_assigncpu)
184 80 : {
185 80 : using data_t = typename TestType::value_type;
186 :
187 80 : GIVEN("a DataHandlerCPU with an associated map")
188 80 : {
189 80 : const index_t size = 314;
190 80 : DataHandlerCPU<data_t> dh{size};
191 80 : auto dhMap = dh.getBlock(size / 2, size / 3);
192 :
193 80 : WHEN("copy assigning")
194 80 : {
195 :
196 10 : auto randVec = generateRandomMatrix<data_t>(size);
197 10 : const DataHandlerCPU dh2{randVec};
198 10 : const auto dh2Map = dh2.getBlock(size / 2, size / 3);
199 :
200 10 : THEN("sizes must match")
201 10 : {
202 5 : const DataHandlerCPU<data_t> bigDh{2 * size};
203 5 : REQUIRE_THROWS(dh = bigDh);
204 5 : }
205 :
206 10 : dh = dh2;
207 10 : THEN("a shallow copy is performed and associated Maps are updated")
208 10 : {
209 5 : REQUIRE_EQ(useCount(dh), 2);
210 5 : REQUIRE_EQ(dh, dh2);
211 5 : REQUIRE_EQ(*dhMap, *dh2Map);
212 5 : }
213 10 : }
214 :
215 80 : WHEN("move assigning")
216 80 : {
217 10 : auto randVec = generateRandomMatrix<data_t>(size);
218 10 : DataHandlerCPU dh2{randVec};
219 10 : const auto dh2View = dh2.getBlock(0, size);
220 10 : DataHandlerCPU testDh{randVec};
221 :
222 10 : THEN("sizes must match")
223 10 : {
224 5 : DataHandlerCPU<data_t> bigDh{2 * size};
225 5 : REQUIRE_THROWS(dh = std::move(bigDh));
226 5 : }
227 :
228 10 : dh = std::move(dh2);
229 10 : THEN("data is moved, associated maps are merged")
230 10 : {
231 5 : REQUIRE_EQ(useCount(dh), 1);
232 5 : REQUIRE_EQ(dh, testDh);
233 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
234 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
235 5 : REQUIRE_EQ(&(*dh2View)[0], &dh[0]);
236 5 : REQUIRE_EQ(dh2View->getSize(), size);
237 5 : }
238 10 : }
239 :
240 80 : WHEN("copy assigning a DataHandlerCPU through base pointers")
241 80 : {
242 10 : DataHandler<data_t>* dhPtr = &dh;
243 :
244 10 : auto randVec = generateRandomMatrix<data_t>(size);
245 10 : const auto dh2Ptr = std::make_unique<const DataHandlerCPU<data_t>>(randVec);
246 10 : const auto dh2Map = dh2Ptr->getBlock(size / 2, size / 3);
247 :
248 10 : THEN("sizes must match")
249 10 : {
250 5 : std::unique_ptr<DataHandler<data_t>> bigDh =
251 5 : std::make_unique<DataHandlerCPU<data_t>>(2 * size);
252 5 : REQUIRE_THROWS(*dhPtr = *bigDh);
253 5 : }
254 :
255 10 : *dhPtr = *dh2Ptr;
256 10 : THEN("a shallow copy is performed and associated Maps are updated")
257 10 : {
258 5 : REQUIRE_EQ(useCount(dh), 2);
259 5 : REQUIRE_EQ(dh, *dh2Ptr);
260 5 : REQUIRE_EQ(*dhMap, *dh2Map);
261 5 : dh[0] = 1;
262 5 : REQUIRE_NE(&dh[0], &(*dh2Ptr)[0]);
263 5 : REQUIRE_EQ(*dhMap, *dh2Map);
264 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
265 5 : }
266 10 : }
267 :
268 80 : WHEN("copy assigning a partial DataHandlerMapCPU through base pointers")
269 80 : {
270 10 : DataHandler<data_t>* dhPtr = &dh;
271 10 : const auto dhCopy = dh;
272 :
273 10 : auto randVec = generateRandomMatrix<data_t>(2 * size);
274 10 : const DataHandlerCPU<data_t> dh2{randVec};
275 10 : const auto dh2Map = dh2.getBlock(0, size);
276 :
277 10 : THEN("sizes must match")
278 10 : {
279 5 : const auto bigDh = dh2.getBlock(0, size + 1);
280 5 : REQUIRE_THROWS(*dhPtr = *bigDh);
281 5 : }
282 :
283 10 : *dhPtr = *dh2Map;
284 10 : THEN("a deep copy is performed")
285 10 : {
286 5 : REQUIRE_EQ(useCount(dh), 1);
287 5 : REQUIRE_EQ(useCount(dhCopy), 1);
288 5 : REQUIRE_EQ(dh, *dh2Map);
289 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
290 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
291 5 : }
292 10 : }
293 :
294 80 : WHEN("copy assigning a full DataHandlerMapCPU (aka a view) through base pointers")
295 80 : {
296 10 : DataHandler<data_t>* dhPtr = &dh;
297 :
298 10 : auto randVec = generateRandomMatrix<data_t>(size);
299 10 : const DataHandlerCPU<data_t> dh2{randVec};
300 10 : const auto dh2View = dh2.getBlock(0, size);
301 10 : const auto dh2Map = dh2.getBlock(size / 2, size / 3);
302 :
303 10 : THEN("sizes must match")
304 10 : {
305 5 : std::unique_ptr<DataHandler<data_t>> bigDh =
306 5 : std::make_unique<DataHandlerCPU<data_t>>(2 * size);
307 5 : auto bigDhView = bigDh->getBlock(0, 2 * size);
308 5 : REQUIRE_THROWS(*dhPtr = *bigDhView);
309 5 : }
310 :
311 10 : *dhPtr = *dh2View;
312 10 : THEN("a shallow copy is performed and associated maps are updated")
313 10 : {
314 5 : REQUIRE_EQ(useCount(dh), 2);
315 5 : REQUIRE_EQ(dh, *dh2View);
316 5 : REQUIRE_EQ(*dhMap, *dh2Map);
317 5 : dh[0] = 1;
318 5 : REQUIRE_NE(&dh[0], &(*dh2View)[0]);
319 5 : REQUIRE_EQ(*dhMap, *dh2Map);
320 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
321 5 : }
322 10 : }
323 :
324 80 : WHEN("move assigning a DataHandlerCPU through base pointers")
325 80 : {
326 10 : DataHandler<data_t>* dhPtr = &dh;
327 :
328 10 : auto randVec = generateRandomMatrix<data_t>(size);
329 10 : std::unique_ptr<DataHandler<data_t>> dh2Ptr =
330 10 : std::make_unique<DataHandlerCPU<data_t>>(randVec);
331 10 : const auto dh2View = dh2Ptr->getBlock(0, size);
332 10 : DataHandlerCPU<data_t> testDh{randVec};
333 :
334 10 : THEN("sizes must match")
335 10 : {
336 5 : std::unique_ptr<DataHandler<data_t>> bigDh =
337 5 : std::make_unique<DataHandlerCPU<data_t>>(2 * size);
338 5 : REQUIRE_THROWS(*dhPtr = std::move(*bigDh));
339 5 : }
340 :
341 10 : *dhPtr = std::move(*dh2Ptr);
342 10 : THEN("data is moved and associated Maps are updated")
343 10 : {
344 5 : REQUIRE_EQ(useCount(dh), 1);
345 5 : REQUIRE_EQ(dh, testDh);
346 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
347 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
348 5 : REQUIRE_EQ(&(*dh2View)[0], &dh[0]);
349 5 : REQUIRE_EQ(dh2View->getSize(), size);
350 5 : }
351 10 : }
352 :
353 80 : WHEN("\"move\" assigning a partial DataHandlerMapCPU through base pointers")
354 80 : {
355 10 : DataHandler<data_t>* dhPtr = &dh;
356 10 : const auto dhCopy = dh;
357 :
358 10 : auto randVec = generateRandomMatrix<data_t>(2 * size);
359 10 : DataHandlerCPU<data_t> dh2{randVec};
360 10 : const auto dh2Map = dh2.getBlock(0, size);
361 :
362 10 : THEN("sizes must match")
363 10 : {
364 5 : REQUIRE_THROWS(*dhPtr = std::move(*dh2.getBlock(0, 2 * size)));
365 5 : }
366 :
367 10 : *dhPtr = std::move(*dh2Map);
368 10 : THEN("a deep copy is performed")
369 10 : {
370 5 : REQUIRE_EQ(useCount(dh), 1);
371 5 : REQUIRE_EQ(useCount(dhCopy), 1);
372 5 : REQUIRE_EQ(dh, *dh2.getBlock(0, size));
373 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
374 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
375 5 : }
376 10 : }
377 :
378 80 : WHEN("\"move\" assigning a full DataHandlerMapCPU (aka a view) through base pointers")
379 80 : {
380 10 : DataHandler<data_t>* dhPtr = &dh;
381 :
382 10 : auto randVec = generateRandomMatrix<data_t>(size);
383 10 : DataHandlerCPU<data_t> dh2{randVec};
384 10 : const auto dh2View = dh2.getBlock(0, size);
385 10 : const auto dh2Map = dh2.getBlock(size / 2, size / 3);
386 :
387 10 : THEN("sizes must match")
388 10 : {
389 5 : const std::unique_ptr<const DataHandler<data_t>> bigDh =
390 5 : std::make_unique<const DataHandlerCPU<data_t>>(2 * size);
391 5 : REQUIRE_THROWS(*dhPtr = std::move(*bigDh->getBlock(0, 2 * size)));
392 5 : }
393 :
394 10 : *dhPtr = std::move(*dh2View);
395 10 : THEN("a shallow copy is performed and associated maps are updated")
396 10 : {
397 5 : REQUIRE_EQ(useCount(dh), 2);
398 5 : REQUIRE_EQ(dh, *dh2View);
399 5 : REQUIRE_EQ(*dhMap, *dh2Map);
400 5 : dh[0] = 1;
401 5 : REQUIRE_NE(&dh[0], &dh2[0]);
402 5 : REQUIRE_EQ(*dhMap, *dh2Map);
403 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[size / 2]);
404 5 : }
405 10 : }
406 80 : }
407 80 : }
408 :
409 : #ifdef ELSA_CUDA_VECTOR
410 : TEST_CASE_TEMPLATE("DataHandlers: Testing clone()", TestType, DataHandlerCPU<float>,
411 : DataHandlerGPU<float>)
412 : #else
413 : TEST_CASE_TEMPLATE("DataHandlers: Testing clone()", TestType, DataHandlerCPU<float>)
414 : #endif
415 1 : {
416 1 : GIVEN("some DataHandler")
417 1 : {
418 1 : index_t size = 728;
419 1 : TestType dh(size);
420 1 : dh = 1.0f;
421 :
422 1 : WHEN("cloning")
423 1 : {
424 1 : auto dhClone = dh.clone();
425 :
426 1 : THEN("a shallow copy is produced")
427 1 : {
428 1 : REQUIRE_NE(dhClone.get(), &dh);
429 :
430 1 : REQUIRE_EQ(useCount(dh), 2);
431 1 : REQUIRE_EQ(*dhClone, dh);
432 :
433 1 : REQUIRE_EQ(dhClone->getSize(), dh.getSize());
434 :
435 1 : dh[0] = 2.f;
436 1 : REQUIRE_NE(dh, *dhClone);
437 1 : }
438 1 : }
439 1 : }
440 1 : }
441 :
442 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the reduction operations", TestType,
443 : datahandler_reduction)
444 10 : {
445 10 : using data_t = typename TestType::value_type;
446 :
447 10 : GIVEN("some DataHandler")
448 10 : {
449 10 : index_t size = 16;
450 :
451 10 : WHEN("putting in some random data")
452 10 : {
453 10 : auto randVec = generateRandomMatrix<data_t>(size);
454 10 : TestType dh(randVec);
455 :
456 10 : THEN("the reductions work as expected")
457 10 : {
458 5 : auto eps = std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon();
459 5 : REQUIRE_UNARY(checkApproxEq(dh.sum(), randVec.sum()));
460 5 : REQUIRE_UNARY(
461 5 : checkApproxEq(dh.l0PseudoNorm(), (randVec.array().cwiseAbs() >= eps).count()));
462 5 : REQUIRE_UNARY(checkApproxEq(dh.l1Norm(), randVec.array().abs().sum()));
463 5 : REQUIRE_UNARY(checkApproxEq(dh.lInfNorm(), randVec.array().abs().maxCoeff()));
464 5 : REQUIRE_UNARY(checkApproxEq(dh.squaredL2Norm(), randVec.squaredNorm()));
465 5 : REQUIRE_UNARY(checkApproxEq(dh.l2Norm(), randVec.norm()));
466 :
467 5 : auto randVec2 = generateRandomMatrix<data_t>(size);
468 5 : TestType dh2(randVec2);
469 :
470 5 : REQUIRE_UNARY(checkApproxEq(dh.dot(dh2), randVec.dot(randVec2)));
471 :
472 5 : auto dhMap = dh2.getBlock(0, dh2.getSize());
473 :
474 5 : REQUIRE_UNARY(checkApproxEq(dh.dot(*dhMap), randVec.dot(randVec2)));
475 5 : }
476 :
477 10 : THEN("the dot product expects correctly sized arguments")
478 10 : {
479 5 : index_t wrongSize = size - 1;
480 :
481 5 : auto randVec2 = generateRandomMatrix<data_t>(wrongSize);
482 5 : TestType dh2(randVec2);
483 :
484 5 : REQUIRE_THROWS_AS(dh.dot(dh2), InvalidArgumentError);
485 5 : }
486 10 : }
487 10 : }
488 10 : }
489 :
490 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the element-wise operations", TestType,
491 : datahandler_elementwise)
492 15 : {
493 15 : using data_t = typename TestType::value_type;
494 :
495 15 : GIVEN("some DataHandler")
496 15 : {
497 15 : index_t size = 567;
498 :
499 15 : WHEN("putting in some random data")
500 15 : {
501 15 : auto randVec = generateRandomMatrix<data_t>(size);
502 15 : TestType dh(randVec);
503 :
504 15 : THEN("the element-wise binary vector operations work as expected")
505 15 : {
506 5 : TestType oldDh = dh;
507 :
508 5 : auto randVec2 = generateRandomMatrix<data_t>(size);
509 5 : TestType dh2(randVec2);
510 :
511 5 : auto dhMap = dh2.getBlock(0, dh2.getSize());
512 :
513 5 : TestType bigDh{size + 1};
514 5 : REQUIRE_THROWS(dh += bigDh);
515 5 : REQUIRE_THROWS(dh -= bigDh);
516 5 : REQUIRE_THROWS(dh *= bigDh);
517 5 : REQUIRE_THROWS(dh /= bigDh);
518 :
519 5 : dh += dh2;
520 2840 : for (index_t i = 0; i < size; ++i)
521 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + dh2[i]));
522 :
523 5 : dh = oldDh;
524 5 : dh += *dhMap;
525 2840 : for (index_t i = 0; i < size; ++i)
526 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + dh2[i]));
527 :
528 5 : dh = oldDh;
529 5 : dh -= dh2;
530 2840 : for (index_t i = 0; i < size; ++i)
531 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - dh2[i]));
532 :
533 5 : dh = oldDh;
534 5 : dh -= *dhMap;
535 2840 : for (index_t i = 0; i < size; ++i)
536 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - dh2[i]));
537 :
538 5 : dh = oldDh;
539 5 : dh *= dh2;
540 2840 : for (index_t i = 0; i < size; ++i)
541 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * dh2[i]));
542 :
543 5 : dh = oldDh;
544 5 : dh *= *dhMap;
545 2840 : for (index_t i = 0; i < size; ++i)
546 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * dh2[i]));
547 :
548 5 : dh = oldDh;
549 5 : dh /= dh2;
550 2840 : for (index_t i = 0; i < size; ++i)
551 2835 : if (dh2[i] != data_t(0))
552 : // due to floating point arithmetic less precision
553 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / dh2[i]));
554 :
555 5 : dh = oldDh;
556 5 : dh /= *dhMap;
557 2840 : for (index_t i = 0; i < size; ++i)
558 2835 : if (dh2[i] != data_t(0))
559 : // due to floating point arithmetic less precision
560 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / dh2[i]));
561 5 : }
562 :
563 15 : THEN("the element-wise binary scalar operations work as expected")
564 15 : {
565 5 : TestType oldDh = dh;
566 5 : data_t scalar = std::is_integral_v<data_t> ? 3 : 3.5;
567 :
568 5 : dh += scalar;
569 2840 : for (index_t i = 0; i < size; ++i)
570 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + scalar));
571 :
572 5 : dh = oldDh;
573 5 : dh -= scalar;
574 2840 : for (index_t i = 0; i < size; ++i)
575 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - scalar));
576 :
577 5 : dh = oldDh;
578 5 : dh *= scalar;
579 2840 : for (index_t i = 0; i < size; ++i)
580 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * scalar));
581 :
582 5 : dh = oldDh;
583 5 : dh /= scalar;
584 2840 : for (index_t i = 0; i < size; ++i)
585 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / scalar));
586 5 : }
587 :
588 15 : THEN("the element-wise assignment of a scalar works as expected")
589 15 : {
590 5 : auto scalar = std::is_integral_v<data_t> ? data_t(47) : data_t(47.11f);
591 :
592 5 : dh = scalar;
593 2840 : for (index_t i = 0; i < size; ++i)
594 5 : REQUIRE_UNARY(checkApproxEq(dh[i], scalar));
595 5 : }
596 15 : }
597 15 : }
598 15 : }
599 :
600 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing referencing blocks", TestType,
601 : datahandler_blockreferencing)
602 10 : {
603 10 : using data_t = typename TestType::value_type;
604 :
605 10 : GIVEN("some DataHandler")
606 10 : {
607 10 : index_t size = 728;
608 10 : Eigen::Matrix<data_t, Eigen::Dynamic, 1> dataVec(size);
609 10 : TestType dh(dataVec);
610 :
611 10 : WHEN("getting the reference to a block")
612 10 : {
613 5 : REQUIRE_THROWS(dh.getBlock(size, 1));
614 5 : REQUIRE_THROWS(dh.getBlock(0, size + 1));
615 :
616 5 : auto dhBlock = dh.getBlock(size / 3, size / 2);
617 :
618 5 : THEN("returned data handler references the correct elements")
619 5 : {
620 5 : REQUIRE_EQ(dhBlock->getSize(), size / 2);
621 :
622 1825 : for (index_t i = 0; i < size / 2; i++)
623 5 : REQUIRE_UNARY(checkApproxEq(&(*dhBlock)[i], &dh[i + size / 3]));
624 5 : }
625 5 : }
626 :
627 10 : WHEN("the whole volume is referenced")
628 10 : {
629 5 : auto dhBlock = dh.getBlock(0, size);
630 :
631 5 : THEN("the referenced volume and the actual volume are equal")
632 5 : {
633 :
634 5 : REQUIRE_EQ(dh, *dhBlock);
635 5 : }
636 5 : }
637 10 : }
638 10 : }
639 :
640 : TEST_CASE_TEMPLATE_DEFINE("DataHandlers: Testing the copy-on-write mechanism", TestType,
641 : datahandler_copyonwrite)
642 55 : {
643 55 : using data_t = typename TestType::value_type;
644 :
645 55 : const index_t size = 42;
646 :
647 55 : GIVEN("A random DataContainer")
648 55 : {
649 55 : auto randVec = generateRandomMatrix<data_t>(size);
650 55 : TestType dh{randVec};
651 :
652 55 : WHEN("const manipulating a copy constructed shallow copy")
653 55 : {
654 5 : TestType dh2 = dh;
655 :
656 5 : THEN("the data is the same")
657 5 : {
658 5 : REQUIRE_EQ(dh, dh2);
659 5 : REQUIRE_EQ(useCount(dh), 2);
660 5 : }
661 5 : }
662 :
663 55 : WHEN("non-const manipulating a copy constructed shallow copy")
664 55 : {
665 45 : TestType dh2 = dh;
666 45 : REQUIRE_EQ(useCount(dh), 2);
667 45 : REQUIRE_EQ(useCount(dh2), 2);
668 :
669 45 : THEN("copy-on-write is invoked")
670 45 : {
671 5 : dh2 += 2;
672 5 : REQUIRE_NE(dh2, dh);
673 5 : REQUIRE_EQ(useCount(dh2), 1);
674 5 : REQUIRE_EQ(useCount(dh), 1);
675 5 : }
676 :
677 45 : THEN("copy-on-write is invoked")
678 45 : {
679 5 : dh2 += dh;
680 5 : REQUIRE_NE(dh2, dh);
681 5 : REQUIRE_EQ(useCount(dh2), 1);
682 5 : REQUIRE_EQ(useCount(dh), 1);
683 5 : }
684 :
685 45 : THEN("copy-on-write is invoked")
686 45 : {
687 5 : dh2 -= 2;
688 5 : REQUIRE_NE(dh2, dh);
689 5 : }
690 :
691 45 : THEN("copy-on-write is invoked")
692 45 : {
693 5 : dh2 -= dh;
694 5 : REQUIRE_NE(dh2, dh);
695 5 : }
696 :
697 45 : THEN("copy-on-write is invoked")
698 45 : {
699 5 : dh2 /= 2;
700 5 : REQUIRE_NE(dh2, dh);
701 5 : }
702 :
703 45 : THEN("copy-on-write is invoked")
704 45 : {
705 5 : dh2 /= dh;
706 5 : REQUIRE_NE(dh2, dh);
707 5 : }
708 :
709 45 : THEN("copy-on-write is invoked")
710 45 : {
711 5 : dh2 *= 2;
712 5 : REQUIRE_NE(dh2, dh);
713 5 : }
714 :
715 45 : THEN("copy-on-write is invoked")
716 45 : {
717 5 : dh2 *= dh;
718 5 : REQUIRE_NE(dh2, dh);
719 5 : }
720 :
721 45 : THEN("copy-on-write is invoked")
722 45 : {
723 5 : dh[0] += 2;
724 5 : REQUIRE_NE(dh2, dh);
725 5 : }
726 45 : }
727 :
728 55 : WHEN("manipulating a non-shallow-copied container")
729 55 : {
730 215 : for (index_t i = 0; i < dh.getSize(); ++i) {
731 210 : dh[i] += 2;
732 210 : }
733 :
734 5 : THEN("copy-on-write should not be invoked") { REQUIRE_EQ(useCount(dh), 1); }
735 5 : }
736 55 : }
737 55 : }
738 :
739 : // "instantiate" the test templates for cpu types
740 : TEST_CASE_TEMPLATE_APPLY(datahandler_construction, CPUTypeTuple);
741 : TEST_CASE_TEMPLATE_APPLY(datahandler_equality, CPUTypeTuple);
742 : TEST_CASE_TEMPLATE_APPLY(datahandler_assigncpu, CPUTypeTuple);
743 : TEST_CASE_TEMPLATE_APPLY(datahandler_reduction, CPUTypeTuple);
744 : TEST_CASE_TEMPLATE_APPLY(datahandler_elementwise, CPUTypeTuple);
745 : TEST_CASE_TEMPLATE_APPLY(datahandler_blockreferencing, CPUTypeTuple);
746 : TEST_CASE_TEMPLATE_APPLY(datahandler_copyonwrite, CPUTypeTuple);
747 :
748 : #ifdef ELSA_CUDA_VECTOR
749 : // "instantiate" the test templates for GPU types
750 : TEST_CASE_TEMPLATE_APPLY(datahandler_construction, CPUTypeTuple);
751 : TEST_CASE_TEMPLATE_APPLY(datahandler_equality, CPUTypeTuple);
752 : TEST_CASE_TEMPLATE_APPLY(datahandler_assigncpu, CPUTypeTuple);
753 : TEST_CASE_TEMPLATE_APPLY(datahandler_reduction, CPUTypeTuple);
754 : TEST_CASE_TEMPLATE_APPLY(datahandler_elementwise, CPUTypeTuple);
755 : TEST_CASE_TEMPLATE_APPLY(datahandler_blockreferencing, CPUTypeTuple);
756 : TEST_CASE_TEMPLATE_APPLY(datahandler_copyonwrite, CPUTypeTuple);
757 : #endif
758 :
759 : TEST_SUITE_END();
|