Line data Source code
1 : /**
2 : * @file test_DataHandlerMap.cpp
3 : *
4 : * @brief Tests for DataHandlerMaps - DataHandlerMapCPU and DataHandlerMapGPU
5 : *
6 : * @author David Frank - initial code
7 : * @author Tobias Lasser - rewrite and code coverage
8 : * @author Jens Petit - refactoring into TEMPLATE_PRODUCT_TEST_CASE
9 : */
10 :
11 : #include "doctest/doctest.h"
12 : #include "DataHandlerMapCPU.h"
13 : #include "DataHandlerCPU.h"
14 : #include "testHelpers.h"
15 :
16 : #ifdef ELSA_CUDA_VECTOR
17 : #include "DataHandlerGPU.h"
18 : #include "DataHandlerMapGPU.h"
19 : #endif
20 :
21 : using namespace elsa;
22 : using namespace elsa;
23 :
24 : // for testing the copy-on-write mechanism
25 : template <typename data_t>
26 : long elsa::useCount(const DataHandlerCPU<data_t>& dh)
27 306 : {
28 306 : return dh._data.use_count();
29 306 : }
30 :
31 : #ifdef ELSA_CUDA_VECTOR
32 : // for testing the copy-on-write mechanism
33 : template <typename data_t>
34 : long elsa::useCount(const DataHandlerGPU<data_t>& dh)
35 : {
36 : return dh._data.use_count();
37 : }
38 : #endif
39 :
40 : // Helper to provide the correct map based on the handler type
41 : template <typename Handler>
42 : struct MapToHandler {
43 : using map =
44 : std::conditional_t<std::is_same_v<DataHandlerCPU<typename Handler::value_type>, Handler>,
45 : DataHandlerMapCPU<typename Handler::value_type>,
46 : #ifdef ELSA_CUDA_VECTOR
47 : DataHandlerMapGPU<typename Handler::value_type>>;
48 : #else
49 : DataHandlerMapCPU<typename Handler::value_type>>;
50 : #endif
51 : };
52 :
53 : using CPUTypeTuple =
54 : std::tuple<DataHandlerCPU<float>, DataHandlerCPU<double>, DataHandlerCPU<complex<float>>,
55 : DataHandlerCPU<complex<double>>, DataHandlerCPU<index_t>>;
56 :
57 : TYPE_TO_STRING(DataHandlerCPU<float>);
58 : TYPE_TO_STRING(DataHandlerCPU<double>);
59 : TYPE_TO_STRING(DataHandlerCPU<index_t>);
60 : TYPE_TO_STRING(DataHandlerCPU<complex<float>>);
61 : TYPE_TO_STRING(DataHandlerCPU<complex<double>>);
62 :
63 : #ifdef ELSA_CUDA_VECTOR
64 : using GPUTypeTuple =
65 : std::tuple<DataHandlerGPU<float>, DataHandlerGPU<double>, DataHandlerGPU<complex<float>>,
66 : DataHandlerGPU<complex<double>>, DataHandlerGPU<index_t>>;
67 :
68 : TYPE_TO_STRING(DataHandlerGPU<float>);
69 : TYPE_TO_STRING(DataHandlerGPU<double>);
70 : TYPE_TO_STRING(DataHandlerGPU<index_t>);
71 : TYPE_TO_STRING(DataHandlerGPU<complex<float>>);
72 : TYPE_TO_STRING(DataHandlerGPU<complex<double>>);
73 : #endif
74 :
75 : TEST_SUITE_BEGIN("core");
76 :
77 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing construction", TestType,
78 : datahandlermap_construction)
79 10 : {
80 10 : using data_t = typename TestType::value_type;
81 :
82 10 : GIVEN("a certain size")
83 10 : {
84 10 : index_t size = 314;
85 :
86 10 : WHEN("constructing with a given vector")
87 10 : {
88 5 : Vector_t<data_t> randVec{size * 2};
89 5 : randVec.setRandom();
90 5 : const TestType dh{randVec};
91 5 : const auto dhMap = dh.getBlock(size / 3, size / 3);
92 :
93 5 : THEN("the DataHandlerMap references the actual vector")
94 5 : {
95 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
96 :
97 525 : for (index_t i = 0; i < size / 3; ++i)
98 5 : REQUIRE_EQ(&(*dhMap)[i], &dh[i + size / 3]);
99 5 : }
100 5 : }
101 :
102 10 : WHEN("copy constructing")
103 10 : {
104 5 : Vector_t<data_t> randVec{size * 2};
105 5 : randVec.setRandom();
106 5 : const TestType dh{randVec};
107 5 : const auto dhMap = dh.getBlock(size / 3, size / 3);
108 :
109 5 : const auto& dhMapRef = static_cast<const typename MapToHandler<TestType>::map&>(*dhMap);
110 :
111 5 : const auto dhMapCopy = dhMapRef;
112 :
113 5 : THEN("the copy references the actual vector")
114 5 : {
115 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
116 :
117 525 : for (index_t i = 0; i < size / 3; ++i)
118 5 : REQUIRE_EQ(&dhMapCopy[i], &dh[i + size / 3]);
119 5 : }
120 5 : }
121 10 : }
122 10 : }
123 :
124 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing equality operator", TestType,
125 : datahandlermap_eqoperator)
126 20 : {
127 20 : using data_t = typename TestType::value_type;
128 :
129 20 : GIVEN("some DataHandlerMap")
130 20 : {
131 20 : index_t size = 314;
132 20 : Vector_t<data_t> randVec{size};
133 20 : randVec.setRandom();
134 :
135 20 : const TestType realDh{randVec};
136 20 : const auto dhPtr = realDh.getBlock(0, size);
137 20 : const auto& dh = *dhPtr;
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_FALSE(dh == dh2);
145 5 : REQUIRE_FALSE(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 = realDh;
152 5 : THEN("the result is true")
153 5 : {
154 5 : REQUIRE_EQ(dh, dh2);
155 5 : REQUIRE_EQ(dh, *realDh.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_FALSE(dh == dh2);
176 5 : REQUIRE_FALSE(dh == *dh2.getBlock(0, size));
177 5 : }
178 5 : }
179 20 : }
180 20 : }
181 :
182 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing assignment to DataHandlerMap", TestType,
183 : datahandlermap_assign)
184 140 : {
185 140 : using data_t = typename TestType::value_type;
186 140 : using MapType = typename MapToHandler<TestType>::map;
187 :
188 140 : const index_t size = 314;
189 :
190 : // Constructo DataHandler with 2x the size
191 140 : TestType dh{2 * size};
192 140 : dh = 0;
193 :
194 : // Create map to the first half of the DH
195 140 : const auto dhMap = dh.getBlock(0, size);
196 :
197 140 : REQUIRE_EQ(dhMap->getSize(), size);
198 140 : REQUIRE_EQ(dhMap->getSize(), dh.getSize() / 2);
199 :
200 140 : GIVEN("A reference to the concrete DataHandlerMap (TestType)")
201 140 : {
202 10 : auto& dhMapRef = static_cast<typename MapToHandler<TestType>::map&>(*dhMap);
203 :
204 10 : WHEN("Copy-assigning a DataHandler with the same size to the map")
205 10 : {
206 5 : Vector_t<data_t> randVec{size};
207 5 : randVec.setRandom();
208 5 : const TestType dh2{randVec};
209 5 : const auto dh2Map = dh2.getBlock(0, size);
210 5 : const auto& dh2MapRef = static_cast<const MapType&>(*dh2Map);
211 :
212 5 : dhMapRef = dh2MapRef;
213 :
214 5 : THEN("a deep copy is performed")
215 5 : {
216 5 : REQUIRE_EQ(useCount(dh), 1);
217 5 : REQUIRE_EQ(useCount(dh2), 1);
218 :
219 5 : REQUIRE_EQ(dhMapRef, dh2);
220 5 : REQUIRE_UNARY(isCwiseApprox(dhMapRef, dh2MapRef));
221 :
222 5 : REQUIRE_EQ(&dh[0], &dhMapRef[0]);
223 5 : REQUIRE_NE(&dh[0], &dh2MapRef[0]);
224 :
225 : // Changing the original DataHandler, doesn't change the new one
226 5 : dh[0] *= 4;
227 :
228 5 : THEN("Changing the original DataHandler doesn't affect the new one")
229 5 : {
230 5 : REQUIRE_UNARY(checkApproxEq(dh[0], dhMapRef[0]));
231 5 : REQUIRE_UNARY(checkApproxNe(dh[0], dh2[0]));
232 5 : REQUIRE_UNARY(checkApproxNe(dh[0], dh2MapRef[0]));
233 5 : }
234 5 : }
235 5 : }
236 :
237 10 : WHEN("Copy-assigning a DataHandler with a different size to the map")
238 10 : {
239 5 : const TestType dh2{3 * size};
240 5 : const auto dh2Map = dh2.getBlock(0, 3 * size);
241 5 : const auto& dh2MapRef =
242 5 : static_cast<const typename MapToHandler<TestType>::map&>(*dh2Map);
243 :
244 5 : THEN("the assignment throws") { REQUIRE_THROWS(dhMapRef = dh2MapRef); }
245 5 : }
246 10 : }
247 :
248 140 : GIVEN("Given the base pointer to the map")
249 140 : {
250 60 : Vector_t<data_t> randVec{size};
251 60 : randVec.setRandom();
252 60 : const std::unique_ptr<const DataHandler<data_t>> dh2Ptr =
253 60 : std::make_unique<const TestType>(randVec);
254 :
255 60 : WHEN("Copy-assigning a DataHandler base pointer of the same size")
256 60 : {
257 : // TODO: why do we need dhCopy?
258 5 : const auto dhCopy = dh;
259 5 : *dhMap = *dh2Ptr;
260 5 : THEN("a deep copy is performed")
261 5 : {
262 5 : REQUIRE_EQ(useCount(dh), 1);
263 5 : REQUIRE_EQ(useCount(dhCopy), 1);
264 :
265 1575 : for (index_t i = 0; i < size; i++)
266 5 : REQUIRE_EQ(dh[i], (*dh2Ptr)[i]);
267 :
268 5 : REQUIRE_EQ(*dhMap, *dh2Ptr);
269 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
270 :
271 : // Changing the original DataHandler, doesn't change the new one
272 5 : dh[0] *= 2;
273 :
274 5 : THEN("Changing the original DataHandler doesn't affect the new one")
275 5 : {
276 5 : REQUIRE_UNARY(checkApproxEq((*dhMap)[0], dh[0]));
277 5 : REQUIRE_UNARY(checkApproxNe((*dhMap)[0], (*dh2Ptr)[0]));
278 5 : }
279 5 : }
280 5 : }
281 :
282 60 : WHEN("Copy-assigning a DataHandler base pointer of a different size")
283 60 : {
284 5 : const std::unique_ptr<DataHandler<data_t>> bigDh = std::make_unique<TestType>(2 * size);
285 5 : THEN("The assigning throws") { REQUIRE_THROWS(*dhMap = *bigDh); }
286 5 : }
287 :
288 60 : WHEN("Copy-assigning a block of a DataHandlerMap though the base pointer")
289 60 : {
290 10 : const auto dhCopy = dh;
291 10 : Vector_t<data_t> randVec{2 * size};
292 10 : randVec.setRandom();
293 :
294 10 : const TestType dh2{randVec};
295 10 : const auto dh2Map = dh2.getBlock(0, size);
296 :
297 10 : WHEN("The sizes of the block and DataHandler are the same")
298 10 : {
299 5 : *dhMap = *dh2Map;
300 5 : THEN("a deep copy is performed")
301 5 : {
302 5 : REQUIRE_EQ(useCount(dh), 1);
303 5 : REQUIRE_EQ(useCount(dhCopy), 1);
304 :
305 1575 : for (index_t i = 0; i < size; i++)
306 5 : REQUIRE_EQ(dh[i], dh2[i]);
307 :
308 5 : REQUIRE_EQ(*dhMap, *dh2Map);
309 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
310 5 : }
311 5 : }
312 :
313 10 : WHEN("The sizes of the block and DataHandler are different")
314 10 : {
315 5 : const auto bigDh = dh2.getBlock(0, 2 * size);
316 5 : THEN("Assignment throws") { REQUIRE_THROWS(*dhMap = *bigDh); }
317 5 : }
318 10 : }
319 :
320 60 : WHEN("Copy-assigning a full DataHandlerMap (aka a view) through the base pointers")
321 60 : {
322 10 : const auto dhCopy = dh;
323 10 : Vector_t<data_t> randVec{size};
324 10 : randVec.setRandom();
325 :
326 10 : const TestType dh2{randVec};
327 10 : const auto dh2Map = dh2.getBlock(0, size);
328 :
329 10 : WHEN("The sizes are the same")
330 10 : {
331 5 : *dhMap = *dh2Map;
332 5 : THEN("a deep copy is performed")
333 5 : {
334 5 : REQUIRE_EQ(useCount(dh), 1);
335 5 : REQUIRE_EQ(useCount(dhCopy), 1);
336 :
337 1575 : for (index_t i = 0; i < size; i++)
338 5 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
339 :
340 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
341 5 : }
342 5 : }
343 :
344 10 : WHEN("The sizes are different")
345 10 : {
346 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
347 5 : std::make_unique<TestType>(2 * size);
348 5 : THEN("sizes must match") { REQUIRE_THROWS(*dhMap = *bigDh->getBlock(0, 2 * size)); }
349 5 : }
350 10 : }
351 :
352 60 : WHEN("\"move\" assigning a DataHandlerMap through the base pointer")
353 60 : {
354 10 : Vector_t<data_t> randVec{size};
355 10 : randVec.setRandom();
356 10 : const std::unique_ptr<DataHandler<data_t>> dh2Ptr = std::make_unique<TestType>(randVec);
357 :
358 10 : WHEN("The sizes are the same")
359 10 : {
360 5 : const auto dhCopy = dh;
361 :
362 5 : *dhMap = std::move(*dh2Ptr);
363 5 : THEN("a deep copy is performed")
364 5 : {
365 5 : REQUIRE_EQ(useCount(dh), 1);
366 5 : REQUIRE_EQ(useCount(dhCopy), 1);
367 :
368 1575 : for (index_t i = 0; i < size; i++)
369 5 : REQUIRE_UNARY(checkApproxEq(dh[i], (*dh2Ptr)[i]));
370 :
371 5 : REQUIRE_EQ(*dhMap, *dh2Ptr);
372 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
373 5 : }
374 5 : }
375 :
376 10 : WHEN("The sizes are different")
377 10 : {
378 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
379 5 : std::make_unique<TestType>(2 * size);
380 5 : THEN("the assignment throws") { REQUIRE_THROWS(*dhMap = std::move(*bigDh)); }
381 5 : }
382 10 : }
383 :
384 60 : WHEN("\"move\" assigning a block of a DataHandlerMap through the base pointer")
385 60 : {
386 10 : const auto dhCopy = dh;
387 10 : Vector_t<data_t> randVec{2 * size};
388 10 : randVec.setRandom();
389 10 : TestType dh2{randVec};
390 10 : const auto dh2Map = dh2.getBlock(0, size);
391 :
392 10 : WHEN("The sizes are the same")
393 10 : {
394 5 : *dhMap = std::move(*dh2Map);
395 5 : THEN("a deep copy is performed")
396 5 : {
397 5 : REQUIRE_EQ(useCount(dh), 1);
398 5 : REQUIRE_EQ(useCount(dhCopy), 1);
399 :
400 1575 : for (index_t i = 0; i < size; i++)
401 5 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
402 :
403 5 : REQUIRE_EQ(*dhMap, *dh2Map);
404 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
405 5 : }
406 5 : }
407 :
408 10 : WHEN("The sizes are different")
409 10 : {
410 5 : const auto bigDh = dh2.getBlock(0, 2 * size);
411 5 : THEN("the assignment throws") { REQUIRE_THROWS(*dhMap = std::move(*bigDh)); }
412 5 : }
413 10 : }
414 :
415 60 : WHEN("\"move\" assigning a full DataHandlerMap (aka a view) through the base pointer")
416 60 : {
417 10 : const auto dhCopy = dh;
418 10 : Vector_t<data_t> randVec{size};
419 10 : randVec.setRandom();
420 10 : TestType dh2{randVec};
421 10 : const auto dh2Map = dh2.getBlock(0, size);
422 :
423 10 : WHEN("The sizes are the same")
424 10 : {
425 5 : *dhMap = std::move(*dh2Map);
426 5 : THEN("a deep copy is performed")
427 5 : {
428 5 : REQUIRE_EQ(useCount(dh), 1);
429 5 : REQUIRE_EQ(useCount(dhCopy), 1);
430 :
431 1575 : for (index_t i = 0; i < size; i++)
432 5 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
433 :
434 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
435 5 : }
436 5 : }
437 :
438 10 : WHEN("The sizes are different")
439 10 : {
440 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
441 5 : std::make_unique<TestType>(2 * size);
442 5 : THEN("the assignment throws") { REQUIRE_THROWS(*dhMap = std::move(*bigDh)); }
443 5 : }
444 10 : }
445 60 : }
446 :
447 140 : GIVEN("A reference to a full concrete DataHandlerMap (aka a view)")
448 140 : {
449 10 : index_t size = 314;
450 10 : TestType dh{size};
451 10 : const auto dhMap = dh.getBlock(0, size);
452 10 : auto& dhMapRef = static_cast<MapType&>(*dhMap);
453 :
454 10 : WHEN("Copy-assigning an equally sized view to the map")
455 10 : {
456 5 : const TestType dh2{size};
457 5 : const auto dh2Map = dh2.getBlock(0, size);
458 5 : const auto& dh2MapRef = static_cast<const MapType&>(*dh2Map);
459 5 : dhMapRef = dh2MapRef;
460 :
461 5 : THEN("a shallow copy is performed")
462 5 : {
463 5 : REQUIRE_EQ(useCount(dh), 2);
464 5 : REQUIRE_EQ(dh, dh2);
465 5 : REQUIRE_EQ(dh, dhMapRef);
466 5 : REQUIRE_EQ(dh, dh2MapRef);
467 5 : dhMapRef[0] = 1;
468 5 : REQUIRE_EQ(&dhMapRef[0], &dh[0]);
469 5 : REQUIRE_NE(&dhMapRef[0], &dh2MapRef[0]);
470 5 : }
471 5 : }
472 10 : WHEN("Copy-assigning an differently sized view to the map")
473 10 : {
474 5 : const TestType dh2{3 * size};
475 5 : const auto dh2Map = dh2.getBlock(0, 3 * size);
476 5 : const auto& dh2MapRef = static_cast<const MapType&>(*dh2Map);
477 :
478 5 : THEN("The assigning throws") { REQUIRE_THROWS(dhMapRef = dh2MapRef); }
479 5 : }
480 10 : }
481 :
482 140 : GIVEN("a full DataHandlerMap (aka a view)")
483 140 : {
484 60 : index_t size = 314;
485 60 : TestType dh{size};
486 60 : const auto dhMap = dh.getBlock(0, size);
487 :
488 60 : WHEN("copy assigning a DataHandlerMap through base pointers")
489 60 : {
490 10 : Vector_t<data_t> randVec{size};
491 10 : randVec.setRandom();
492 10 : const std::unique_ptr<DataHandler<data_t>> dh2Ptr = std::make_unique<TestType>(randVec);
493 :
494 10 : THEN("sizes must match")
495 10 : {
496 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
497 5 : std::make_unique<TestType>(2 * size);
498 5 : REQUIRE_THROWS(*dhMap = *bigDh);
499 5 : }
500 :
501 10 : *dhMap = *dh2Ptr;
502 10 : THEN("a shallow copy is performed")
503 10 : {
504 5 : REQUIRE_EQ(useCount(dh), 2);
505 5 : REQUIRE_EQ(dh, *dh2Ptr);
506 5 : REQUIRE_EQ(*dhMap, *dh2Ptr);
507 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
508 5 : dh[0] = 1;
509 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
510 5 : REQUIRE_NE(&dh[0], &(*dh2Ptr)[0]);
511 5 : }
512 10 : }
513 :
514 60 : WHEN("copy assigning a partial DataHandlerMap through base pointers")
515 60 : {
516 10 : const auto dhCopy = dh;
517 10 : Vector_t<data_t> randVec{2 * size};
518 10 : randVec.setRandom();
519 10 : const TestType dh2{randVec};
520 10 : const auto dh2Map = dh2.getBlock(0, size);
521 :
522 10 : THEN("sizes must match")
523 10 : {
524 5 : const auto bigDh = dh2.getBlock(0, 2 * size);
525 5 : REQUIRE_THROWS(*dhMap = *bigDh);
526 5 : }
527 :
528 10 : *dhMap = *dh2Map;
529 10 : THEN("a deep copy is performed")
530 10 : {
531 5 : REQUIRE_EQ(useCount(dh), 1);
532 5 : REQUIRE_EQ(useCount(dhCopy), 1);
533 :
534 1575 : for (index_t i = 0; i < size; i++)
535 5 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
536 :
537 5 : REQUIRE_EQ(*dhMap, *dh2Map);
538 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
539 5 : }
540 10 : }
541 :
542 60 : WHEN("copy assigning a full DataHandlerMap (aka a view) through base pointers")
543 60 : {
544 10 : Vector_t<data_t> randVec{size};
545 10 : randVec.setRandom();
546 10 : const TestType dh2{randVec};
547 10 : const auto dh2Map = dh2.getBlock(0, size);
548 :
549 10 : THEN("sizes must match")
550 10 : {
551 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
552 5 : std::make_unique<TestType>(2 * size);
553 5 : REQUIRE_THROWS(*dhMap = *bigDh->getBlock(0, 2 * size));
554 5 : }
555 :
556 10 : *dhMap = *dh2Map;
557 10 : THEN("a shallow copy is performed")
558 10 : {
559 5 : REQUIRE_EQ(useCount(dh), 2);
560 5 : REQUIRE_EQ(dh, dh2);
561 5 : REQUIRE_EQ(*dhMap, *dh2Map);
562 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
563 5 : dh[0] = 1;
564 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
565 5 : REQUIRE_NE(&dh[0], &dh2[0]);
566 5 : }
567 10 : }
568 :
569 60 : WHEN("\"move\" assigning a DataHandler through base pointers")
570 60 : {
571 10 : Vector_t<data_t> randVec{size};
572 10 : randVec.setRandom();
573 10 : const std::unique_ptr<DataHandler<data_t>> dh2Ptr = std::make_unique<TestType>(randVec);
574 :
575 10 : THEN("sizes must match")
576 10 : {
577 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
578 5 : std::make_unique<TestType>(2 * size);
579 5 : REQUIRE_THROWS(*dhMap = std::move(*bigDh));
580 5 : }
581 :
582 10 : *dhMap = std::move(*dh2Ptr);
583 10 : THEN("a shallow copy is performed")
584 10 : {
585 5 : REQUIRE_EQ(useCount(dh), 2);
586 5 : REQUIRE_EQ(dh, *dh2Ptr);
587 5 : REQUIRE_EQ(*dhMap, *dh2Ptr);
588 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
589 5 : dh[0] = 1;
590 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
591 5 : REQUIRE_NE(&dh[0], &(*dh2Ptr)[0]);
592 5 : }
593 10 : }
594 :
595 60 : WHEN("\"move\" assigning a partial DataHandlerMap through base pointers")
596 60 : {
597 10 : const auto dhCopy = dh;
598 10 : Vector_t<data_t> randVec{2 * size};
599 10 : randVec.setRandom();
600 10 : TestType dh2{randVec};
601 10 : const auto dh2Map = dh2.getBlock(0, size);
602 :
603 10 : THEN("sizes must match")
604 10 : {
605 5 : const auto bigDh = dh2.getBlock(0, 2 * size);
606 5 : REQUIRE_THROWS(*dhMap = std::move(*bigDh));
607 5 : }
608 :
609 10 : *dhMap = std::move(*dh2Map);
610 10 : THEN("a deep copy is performed")
611 10 : {
612 5 : REQUIRE_EQ(useCount(dh), 1);
613 5 : REQUIRE_EQ(useCount(dhCopy), 1);
614 :
615 1575 : for (index_t i = 0; i < size; i++)
616 5 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
617 :
618 5 : REQUIRE_EQ(*dhMap, *dh2Map);
619 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
620 5 : }
621 10 : }
622 :
623 60 : WHEN("\"move\" assigning a full DataHandlerMap (aka a view) through base pointers")
624 60 : {
625 10 : Vector_t<data_t> randVec{size};
626 10 : randVec.setRandom();
627 10 : TestType dh2{randVec};
628 10 : const auto dh2Map = dh2.getBlock(0, size);
629 :
630 10 : THEN("sizes must match")
631 10 : {
632 5 : const std::unique_ptr<DataHandler<data_t>> bigDh =
633 5 : std::make_unique<TestType>(2 * size);
634 5 : REQUIRE_THROWS(*dhMap = std::move(*bigDh->getBlock(0, 2 * size)));
635 5 : }
636 :
637 10 : *dhMap = std::move(*dh2Map);
638 10 : THEN("a shallow copy is performed")
639 10 : {
640 5 : REQUIRE_EQ(useCount(dh), 2);
641 5 : REQUIRE_EQ(dh, dh2);
642 5 : REQUIRE_EQ(*dhMap, *dh2Map);
643 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
644 5 : dh[0] = 1;
645 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
646 5 : REQUIRE_NE(&dh[0], &dh2[0]);
647 5 : }
648 10 : }
649 60 : }
650 140 : }
651 :
652 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing clone()", TestType, datahandlermap_clone)
653 10 : {
654 10 : using data_t = typename TestType::value_type;
655 :
656 10 : GIVEN("a full DataHandlerMap (aka a view)")
657 10 : {
658 5 : index_t size = 728;
659 5 : Vector_t<data_t> dataVec(size);
660 5 : dataVec.setRandom();
661 5 : TestType realDh(dataVec);
662 5 : auto dhPtr = realDh.getBlock(0, size);
663 5 : auto& dh = *dhPtr;
664 :
665 5 : WHEN("cloning")
666 5 : {
667 5 : auto dhClone = dh.clone();
668 :
669 5 : THEN("a shallow copy is produced")
670 5 : {
671 5 : REQUIRE_NE(dhClone.get(), &dh);
672 :
673 5 : REQUIRE_EQ(dhClone->getSize(), dh.getSize());
674 :
675 5 : REQUIRE_EQ(useCount(realDh), 2);
676 :
677 5 : REQUIRE_EQ(*dhClone, dh);
678 :
679 5 : dh[0] = 1;
680 5 : REQUIRE_NE(*dhClone, dh);
681 5 : }
682 5 : }
683 5 : }
684 :
685 10 : GIVEN("a partial DataHandlerMap")
686 10 : {
687 5 : index_t size = 728;
688 5 : Vector_t<data_t> dataVec(size);
689 5 : dataVec.setRandom();
690 5 : TestType realDh(dataVec);
691 5 : auto dhPtr = realDh.getBlock(0, size / 2);
692 5 : auto& dh = *dhPtr;
693 :
694 5 : WHEN("a deep copy is produced")
695 5 : {
696 5 : auto dhClone = dh.clone();
697 :
698 5 : THEN("everything matches")
699 5 : {
700 5 : REQUIRE_NE(dhClone.get(), &dh);
701 :
702 5 : REQUIRE_EQ(dhClone->getSize(), dh.getSize());
703 :
704 5 : REQUIRE_EQ(useCount(realDh), 1);
705 :
706 5 : REQUIRE_EQ(dh, *dhClone);
707 5 : }
708 5 : }
709 5 : }
710 10 : }
711 :
712 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing the reduction operations", TestType,
713 : datahandlermap_reduction)
714 10 : {
715 10 : using data_t = typename TestType::value_type;
716 :
717 10 : GIVEN("some DataHandlerMap")
718 10 : {
719 10 : index_t size = 284;
720 :
721 10 : WHEN("putting in some random data")
722 10 : {
723 10 : auto randVec = generateRandomMatrix<data_t>(size * 2);
724 10 : TestType realDh(randVec);
725 10 : auto dhPtr = realDh.getBlock(size / 3, size);
726 10 : auto& dh = *dhPtr;
727 :
728 10 : THEN("the reductions work as expected")
729 10 : {
730 5 : REQUIRE_UNARY(checkApproxEq(dh.sum(), randVec.middleRows(size / 3, size).sum()));
731 5 : REQUIRE_EQ(dh.l0PseudoNorm(),
732 5 : (randVec.middleRows(size / 3, size).array().cwiseAbs()
733 5 : >= std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon())
734 5 : .count());
735 5 : REQUIRE_UNARY(checkApproxEq(
736 5 : dh.l1Norm(), randVec.middleRows(size / 3, size).array().abs().sum()));
737 5 : REQUIRE_UNARY(checkApproxEq(
738 5 : dh.lInfNorm(), randVec.middleRows(size / 3, size).array().abs().maxCoeff()));
739 5 : REQUIRE_UNARY(checkApproxEq(dh.squaredL2Norm(),
740 5 : randVec.middleRows(size / 3, size).squaredNorm()));
741 5 : REQUIRE_UNARY(
742 5 : checkApproxEq(dh.l2Norm(), randVec.middleRows(size / 3, size).norm()));
743 :
744 5 : auto randVec2 = generateRandomMatrix<data_t>(size);
745 5 : TestType realDh2(randVec2);
746 5 : auto dh2Ptr = realDh2.getBlock(0, size);
747 5 : auto& dh2 = *dh2Ptr;
748 5 : REQUIRE_UNARY(
749 5 : checkApproxEq(dh.dot(dh2), randVec.middleRows(size / 3, size).dot(randVec2)));
750 :
751 5 : TestType dhCPU(randVec2);
752 5 : REQUIRE_UNARY(
753 5 : checkApproxEq(dh.dot(dhCPU), randVec.middleRows(size / 3, size).dot(randVec2)));
754 5 : }
755 :
756 10 : THEN("the dot product expects correctly sized arguments")
757 10 : {
758 5 : index_t wrongSize = size - 1;
759 5 : Vector_t<data_t> randVec2(wrongSize);
760 5 : randVec2.setRandom();
761 5 : TestType dh2(randVec2);
762 :
763 5 : REQUIRE_THROWS_AS(dh.dot(dh2), InvalidArgumentError);
764 5 : }
765 10 : }
766 10 : }
767 10 : }
768 :
769 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing the element-wise operations", TestType,
770 : datahandlermap_elemwise)
771 15 : {
772 15 : using data_t = typename TestType::value_type;
773 :
774 15 : GIVEN("some DataHandlerMap")
775 15 : {
776 15 : index_t size = 567;
777 :
778 15 : WHEN("putting in some random data")
779 15 : {
780 15 : auto randVec = generateRandomMatrix<data_t>(size);
781 15 : TestType realDh(randVec);
782 :
783 15 : auto dhPtr = realDh.getBlock(0, size);
784 15 : auto& dh = static_cast<typename MapToHandler<TestType>::map&>(*dhPtr);
785 :
786 15 : THEN("the element-wise binary vector operations work as expected")
787 15 : {
788 5 : TestType bigDh{2 * size};
789 :
790 5 : REQUIRE_THROWS(dh += bigDh);
791 5 : REQUIRE_THROWS(dh -= bigDh);
792 5 : REQUIRE_THROWS(dh *= bigDh);
793 5 : REQUIRE_THROWS(dh /= bigDh);
794 :
795 5 : TestType realOldDh(randVec);
796 5 : auto oldDhPtr = realOldDh.getBlock(0, size);
797 5 : auto& oldDh = static_cast<typename MapToHandler<TestType>::map&>(*oldDhPtr);
798 :
799 5 : auto randVec2 = generateRandomMatrix<data_t>(size);
800 :
801 5 : TestType dhCPU(randVec2);
802 5 : auto dh2Ptr = dhCPU.getBlock(0, size);
803 5 : auto& dh2 = *dh2Ptr;
804 :
805 5 : dh += dh2;
806 2840 : for (index_t i = 0; i < size; ++i)
807 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + dh2[i]));
808 :
809 5 : dh = oldDh;
810 5 : dh += dhCPU;
811 2840 : for (index_t i = 0; i < size; ++i)
812 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + dhCPU[i]));
813 :
814 5 : dh = oldDh;
815 5 : dh -= dh2;
816 2840 : for (index_t i = 0; i < size; ++i)
817 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - dh2[i]));
818 :
819 5 : dh = oldDh;
820 5 : dh -= dhCPU;
821 2840 : for (index_t i = 0; i < size; ++i)
822 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - dhCPU[i]));
823 :
824 5 : dh = oldDh;
825 5 : dh *= dh2;
826 2840 : for (index_t i = 0; i < size; ++i)
827 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * dh2[i]));
828 :
829 5 : dh = oldDh;
830 5 : dh *= dhCPU;
831 2840 : for (index_t i = 0; i < size; ++i)
832 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * dhCPU[i]));
833 :
834 5 : dh = oldDh;
835 5 : dh /= dh2;
836 2840 : for (index_t i = 0; i < size; ++i)
837 2835 : if (dh2[i] != data_t(0))
838 : // due to floating point arithmetic less precision
839 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / dh2[i]));
840 :
841 5 : dh = oldDh;
842 5 : dh /= dhCPU;
843 2840 : for (index_t i = 0; i < size; ++i)
844 2835 : if (dhCPU[i] != data_t(0))
845 : // due to floating point arithmetic less precision
846 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / dhCPU[i]));
847 5 : }
848 :
849 15 : THEN("the element-wise binary scalar operations work as expected")
850 15 : {
851 5 : TestType realOldDh(randVec);
852 5 : auto oldDhPtr = realOldDh.getBlock(0, size);
853 5 : auto& oldDh = static_cast<typename MapToHandler<TestType>::map&>(*oldDhPtr);
854 5 : data_t scalar = std::is_integral_v<data_t> ? 3 : data_t(3.5f);
855 :
856 5 : dh += scalar;
857 2840 : for (index_t i = 0; i < size; ++i)
858 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] + scalar));
859 :
860 5 : dh = oldDh;
861 5 : dh -= scalar;
862 2840 : for (index_t i = 0; i < size; ++i)
863 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] - scalar));
864 :
865 5 : dh = oldDh;
866 5 : dh *= scalar;
867 2840 : for (index_t i = 0; i < size; ++i)
868 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] * scalar));
869 :
870 5 : dh = oldDh;
871 5 : dh /= scalar;
872 2840 : for (index_t i = 0; i < size; ++i)
873 5 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / scalar));
874 5 : }
875 :
876 15 : THEN("the element-wise assignment of a scalar works as expected")
877 15 : {
878 5 : auto scalar = std::is_integral_v<data_t> ? data_t(47) : data_t(47.11f);
879 :
880 5 : dh = scalar;
881 2840 : for (index_t i = 0; i < size; ++i)
882 5 : REQUIRE_UNARY(checkApproxEq(dh[i], scalar));
883 5 : }
884 15 : }
885 15 : }
886 15 : }
887 :
888 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing referencing blocks", TestType,
889 : datahandlermap_blockref)
890 10 : {
891 10 : using data_t = typename TestType::value_type;
892 :
893 10 : GIVEN("some DataHandlerMap")
894 10 : {
895 5 : index_t size = 728;
896 5 : Vector_t<data_t> dataVec(size);
897 5 : TestType realDh(dataVec);
898 5 : auto dhPtr = realDh.getBlock(0, size);
899 5 : auto& dh = *dhPtr;
900 :
901 5 : WHEN("getting the reference to a block")
902 5 : {
903 5 : REQUIRE_THROWS(dh.getBlock(size, 1));
904 5 : REQUIRE_THROWS(dh.getBlock(0, size + 1));
905 :
906 5 : auto dhBlock = dh.getBlock(size / 3, size / 2);
907 :
908 5 : THEN("returned data handler references the correct elements")
909 5 : {
910 5 : REQUIRE_EQ(dhBlock->getSize(), size / 2);
911 :
912 1825 : for (index_t i = 0; i < size / 2; i++)
913 5 : REQUIRE_EQ(&(*dhBlock)[i], &dh[i + size / 3]);
914 5 : }
915 5 : }
916 5 : }
917 :
918 10 : GIVEN("a const DataHandlerMap")
919 10 : {
920 5 : index_t size = 728;
921 5 : Vector_t<data_t> dataVec(size);
922 5 : const TestType realDh(dataVec);
923 5 : auto dhPtr = realDh.getBlock(0, size);
924 5 : auto& dh = *dhPtr;
925 :
926 5 : WHEN("getting the reference to a block")
927 5 : {
928 5 : REQUIRE_THROWS(dh.getBlock(size, 1));
929 5 : REQUIRE_THROWS(dh.getBlock(0, size + 1));
930 :
931 5 : auto dhBlock = dh.getBlock(size / 3, size / 2);
932 :
933 5 : THEN("returned data handler references the correct elements")
934 5 : {
935 5 : REQUIRE_EQ(dhBlock->getSize(), size / 2);
936 :
937 1825 : for (index_t i = 0; i < size / 2; i++)
938 5 : REQUIRE_EQ(&(*dhBlock)[i], &dh[i + size / 3]);
939 5 : }
940 5 : }
941 5 : }
942 10 : }
943 :
944 : // "instantiate" the test templates for CPU types
945 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_construction, CPUTypeTuple);
946 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_eqoperator, CPUTypeTuple);
947 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_assign, CPUTypeTuple);
948 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_clone, CPUTypeTuple);
949 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_reduction, CPUTypeTuple);
950 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_elemwise, CPUTypeTuple);
951 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_blockref, CPUTypeTuple);
952 :
953 : #ifdef ELSA_CUDA_VECTOR
954 : // "instantiate" the test templates for GPU types
955 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_construction, GPUTypeTuple);
956 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_eqoperator, GPUTypeTuple);
957 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_assign, GPUTypeTuple);
958 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_clone, GPUTypeTuple);
959 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_reduction, GPUTypeTuple);
960 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_elemwise, GPUTypeTuple);
961 : TEST_CASE_TEMPLATE_APPLY(datahandlermap_blockref, GPUTypeTuple);
962 : #endif
963 :
964 : TEST_SUITE_END();
|