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 125 : long elsa::useCount(const DataHandlerCPU<data_t>& dh)
27 : {
28 125 : return dh._data.use_count();
29 : }
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 245 : TYPE_TO_STRING(DataHandlerCPU<float>);
58 245 : TYPE_TO_STRING(DataHandlerCPU<double>);
59 245 : TYPE_TO_STRING(DataHandlerCPU<index_t>);
60 245 : TYPE_TO_STRING(DataHandlerCPU<complex<float>>);
61 245 : 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 220 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing construction", TestType,
78 : datahandlermap_construction)
79 : {
80 : using data_t = typename TestType::value_type;
81 :
82 20 : GIVEN("a certain size")
83 : {
84 10 : index_t size = 314;
85 :
86 15 : WHEN("constructing with a given vector")
87 : {
88 10 : Vector_t<data_t> randVec{size * 2};
89 5 : randVec.setRandom();
90 10 : const TestType dh{randVec};
91 10 : const auto dhMap = dh.getBlock(size / 3, size / 3);
92 :
93 10 : THEN("the DataHandlerMap references the actual vector")
94 : {
95 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
96 :
97 525 : for (index_t i = 0; i < size / 3; ++i)
98 520 : REQUIRE_EQ(&(*dhMap)[i], &dh[i + size / 3]);
99 : }
100 : }
101 :
102 15 : WHEN("copy constructing")
103 : {
104 10 : Vector_t<data_t> randVec{size * 2};
105 5 : randVec.setRandom();
106 10 : const TestType dh{randVec};
107 10 : 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 10 : const auto dhMapCopy = dhMapRef;
112 :
113 10 : THEN("the copy references the actual vector")
114 : {
115 5 : REQUIRE_EQ(dhMap->getSize(), size / 3);
116 :
117 525 : for (index_t i = 0; i < size / 3; ++i)
118 520 : REQUIRE_EQ(&dhMapCopy[i], &dh[i + size / 3]);
119 : }
120 : }
121 : }
122 10 : }
123 :
124 230 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing equality operator", TestType,
125 : datahandlermap_eqoperator)
126 : {
127 : using data_t = typename TestType::value_type;
128 :
129 40 : GIVEN("some DataHandlerMap")
130 : {
131 20 : index_t size = 314;
132 40 : Vector_t<data_t> randVec{size};
133 20 : randVec.setRandom();
134 :
135 40 : const TestType realDh{randVec};
136 40 : const auto dhPtr = realDh.getBlock(0, size);
137 20 : const auto& dh = *dhPtr;
138 :
139 25 : WHEN("comparing to a handler with a different size")
140 : {
141 10 : const TestType dh2{size + 1};
142 10 : THEN("the result is false")
143 : {
144 5 : REQUIRE_FALSE(dh == dh2);
145 5 : REQUIRE_FALSE(dh == *dh2.getBlock(0, size + 1));
146 : }
147 : }
148 :
149 25 : WHEN("comparing to a shallow copy or view of the handler")
150 : {
151 10 : const auto dh2 = realDh;
152 10 : THEN("the result is true")
153 : {
154 5 : REQUIRE_EQ(dh, dh2);
155 5 : REQUIRE_EQ(dh, *realDh.getBlock(0, size));
156 : }
157 : }
158 :
159 25 : WHEN("comparing to a deep copy or a view of the deep copy")
160 : {
161 10 : const TestType dh2{randVec};
162 10 : THEN("the result is true")
163 : {
164 5 : REQUIRE_EQ(dh, dh2);
165 5 : REQUIRE_EQ(dh, *dh2.getBlock(0, size));
166 : }
167 : }
168 :
169 25 : WHEN("comparing to a handler or map with different data")
170 : {
171 5 : randVec[0] += 1;
172 10 : const TestType dh2{randVec};
173 10 : THEN("the result is false")
174 : {
175 5 : REQUIRE_FALSE(dh == dh2);
176 5 : REQUIRE_FALSE(dh == *dh2.getBlock(0, size));
177 : }
178 : }
179 : }
180 20 : }
181 :
182 350 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing assignment to DataHandlerMap", TestType,
183 : datahandlermap_assign)
184 : {
185 : using data_t = typename TestType::value_type;
186 : using MapType = typename MapToHandler<TestType>::map;
187 :
188 140 : const index_t size = 314;
189 :
190 : // Constructo DataHandler with 2x the size
191 280 : TestType dh{2 * size};
192 140 : dh = 0;
193 :
194 : // Create map to the first half of the DH
195 280 : 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 150 : GIVEN("A reference to the concrete DataHandlerMap (TestType)")
201 : {
202 10 : auto& dhMapRef = static_cast<typename MapToHandler<TestType>::map&>(*dhMap);
203 :
204 15 : WHEN("Copy-assigning a DataHandler with the same size to the map")
205 : {
206 10 : Vector_t<data_t> randVec{size};
207 5 : randVec.setRandom();
208 10 : const TestType dh2{randVec};
209 10 : const auto dh2Map = dh2.getBlock(0, size);
210 5 : const auto& dh2MapRef = static_cast<const MapType&>(*dh2Map);
211 :
212 5 : dhMapRef = dh2MapRef;
213 :
214 10 : THEN("a deep copy is performed")
215 : {
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 10 : THEN("Changing the original DataHandler doesn't affect the new one")
229 : {
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 : }
234 : }
235 : }
236 :
237 15 : WHEN("Copy-assigning a DataHandler with a different size to the map")
238 : {
239 10 : const TestType dh2{3 * size};
240 10 : const auto dh2Map = dh2.getBlock(0, 3 * size);
241 : const auto& dh2MapRef =
242 5 : static_cast<const typename MapToHandler<TestType>::map&>(*dh2Map);
243 :
244 15 : THEN("the assignment throws") { REQUIRE_THROWS(dhMapRef = dh2MapRef); }
245 : }
246 : }
247 :
248 200 : GIVEN("Given the base pointer to the map")
249 : {
250 120 : Vector_t<data_t> randVec{size};
251 60 : randVec.setRandom();
252 120 : const std::unique_ptr<const DataHandler<data_t>> dh2Ptr =
253 : std::make_unique<const TestType>(randVec);
254 :
255 65 : WHEN("Copy-assigning a DataHandler base pointer of the same size")
256 : {
257 : // TODO: why do we need dhCopy?
258 10 : const auto dhCopy = dh;
259 5 : *dhMap = *dh2Ptr;
260 10 : THEN("a deep copy is performed")
261 : {
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 1570 : 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 10 : THEN("Changing the original DataHandler doesn't affect the new one")
275 : {
276 5 : REQUIRE_UNARY(checkApproxEq((*dhMap)[0], dh[0]));
277 5 : REQUIRE_UNARY(checkApproxNe((*dhMap)[0], (*dh2Ptr)[0]));
278 : }
279 : }
280 : }
281 :
282 65 : WHEN("Copy-assigning a DataHandler base pointer of a different size")
283 : {
284 10 : const std::unique_ptr<DataHandler<data_t>> bigDh = std::make_unique<TestType>(2 * size);
285 15 : THEN("The assigning throws") { REQUIRE_THROWS(*dhMap = *bigDh); }
286 : }
287 :
288 70 : WHEN("Copy-assigning a block of a DataHandlerMap though the base pointer")
289 : {
290 20 : const auto dhCopy = dh;
291 20 : Vector_t<data_t> randVec{2 * size};
292 10 : randVec.setRandom();
293 :
294 20 : const TestType dh2{randVec};
295 20 : const auto dh2Map = dh2.getBlock(0, size);
296 :
297 15 : WHEN("The sizes of the block and DataHandler are the same")
298 : {
299 5 : *dhMap = *dh2Map;
300 10 : THEN("a deep copy is performed")
301 : {
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 1570 : REQUIRE_EQ(dh[i], dh2[i]);
307 :
308 5 : REQUIRE_EQ(*dhMap, *dh2Map);
309 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
310 : }
311 : }
312 :
313 15 : WHEN("The sizes of the block and DataHandler are different")
314 : {
315 10 : const auto bigDh = dh2.getBlock(0, 2 * size);
316 15 : THEN("Assignment throws") { REQUIRE_THROWS(*dhMap = *bigDh); }
317 : }
318 : }
319 :
320 70 : WHEN("Copy-assigning a full DataHandlerMap (aka a view) through the base pointers")
321 : {
322 20 : const auto dhCopy = dh;
323 20 : Vector_t<data_t> randVec{size};
324 10 : randVec.setRandom();
325 :
326 20 : const TestType dh2{randVec};
327 20 : const auto dh2Map = dh2.getBlock(0, size);
328 :
329 15 : WHEN("The sizes are the same")
330 : {
331 5 : *dhMap = *dh2Map;
332 10 : THEN("a deep copy is performed")
333 : {
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 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
339 :
340 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
341 : }
342 : }
343 :
344 15 : WHEN("The sizes are different")
345 : {
346 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
347 5 : std::make_unique<TestType>(2 * size);
348 20 : THEN("sizes must match") { REQUIRE_THROWS(*dhMap = *bigDh->getBlock(0, 2 * size)); }
349 : }
350 : }
351 :
352 70 : WHEN("\"move\" assigning a DataHandlerMap through the base pointer")
353 : {
354 20 : Vector_t<data_t> randVec{size};
355 10 : randVec.setRandom();
356 20 : const std::unique_ptr<DataHandler<data_t>> dh2Ptr = std::make_unique<TestType>(randVec);
357 :
358 15 : WHEN("The sizes are the same")
359 : {
360 10 : const auto dhCopy = dh;
361 :
362 5 : *dhMap = std::move(*dh2Ptr);
363 10 : THEN("a deep copy is performed")
364 : {
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 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], (*dh2Ptr)[i]));
370 :
371 5 : REQUIRE_EQ(*dhMap, *dh2Ptr);
372 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
373 : }
374 : }
375 :
376 15 : WHEN("The sizes are different")
377 : {
378 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
379 5 : std::make_unique<TestType>(2 * size);
380 15 : THEN("the assignment throws") { REQUIRE_THROWS(*dhMap = std::move(*bigDh)); }
381 : }
382 : }
383 :
384 70 : WHEN("\"move\" assigning a block of a DataHandlerMap through the base pointer")
385 : {
386 20 : const auto dhCopy = dh;
387 20 : Vector_t<data_t> randVec{2 * size};
388 10 : randVec.setRandom();
389 20 : TestType dh2{randVec};
390 20 : const auto dh2Map = dh2.getBlock(0, size);
391 :
392 15 : WHEN("The sizes are the same")
393 : {
394 5 : *dhMap = std::move(*dh2Map);
395 10 : THEN("a deep copy is performed")
396 : {
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 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
402 :
403 5 : REQUIRE_EQ(*dhMap, *dh2Map);
404 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
405 : }
406 : }
407 :
408 15 : WHEN("The sizes are different")
409 : {
410 10 : const auto bigDh = dh2.getBlock(0, 2 * size);
411 15 : THEN("the assignment throws") { REQUIRE_THROWS(*dhMap = std::move(*bigDh)); }
412 : }
413 : }
414 :
415 70 : WHEN("\"move\" assigning a full DataHandlerMap (aka a view) through the base pointer")
416 : {
417 20 : const auto dhCopy = dh;
418 20 : Vector_t<data_t> randVec{size};
419 10 : randVec.setRandom();
420 20 : TestType dh2{randVec};
421 20 : const auto dh2Map = dh2.getBlock(0, size);
422 :
423 15 : WHEN("The sizes are the same")
424 : {
425 5 : *dhMap = std::move(*dh2Map);
426 10 : THEN("a deep copy is performed")
427 : {
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 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
433 :
434 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
435 : }
436 : }
437 :
438 15 : WHEN("The sizes are different")
439 : {
440 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
441 5 : std::make_unique<TestType>(2 * size);
442 15 : THEN("the assignment throws") { REQUIRE_THROWS(*dhMap = std::move(*bigDh)); }
443 : }
444 : }
445 : }
446 :
447 150 : GIVEN("A reference to a full concrete DataHandlerMap (aka a view)")
448 : {
449 10 : index_t size = 314;
450 20 : TestType dh{size};
451 20 : const auto dhMap = dh.getBlock(0, size);
452 10 : auto& dhMapRef = static_cast<MapType&>(*dhMap);
453 :
454 15 : WHEN("Copy-assigning an equally sized view to the map")
455 : {
456 10 : const TestType dh2{size};
457 10 : const auto dh2Map = dh2.getBlock(0, size);
458 5 : const auto& dh2MapRef = static_cast<const MapType&>(*dh2Map);
459 5 : dhMapRef = dh2MapRef;
460 :
461 10 : THEN("a shallow copy is performed")
462 : {
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 : }
471 : }
472 15 : WHEN("Copy-assigning an differently sized view to the map")
473 : {
474 10 : const TestType dh2{3 * size};
475 10 : const auto dh2Map = dh2.getBlock(0, 3 * size);
476 5 : const auto& dh2MapRef = static_cast<const MapType&>(*dh2Map);
477 :
478 15 : THEN("The assigning throws") { REQUIRE_THROWS(dhMapRef = dh2MapRef); }
479 : }
480 : }
481 :
482 200 : GIVEN("a full DataHandlerMap (aka a view)")
483 : {
484 60 : index_t size = 314;
485 120 : TestType dh{size};
486 120 : const auto dhMap = dh.getBlock(0, size);
487 :
488 70 : WHEN("copy assigning a DataHandlerMap through base pointers")
489 : {
490 20 : Vector_t<data_t> randVec{size};
491 10 : randVec.setRandom();
492 20 : const std::unique_ptr<DataHandler<data_t>> dh2Ptr = std::make_unique<TestType>(randVec);
493 :
494 15 : THEN("sizes must match")
495 : {
496 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
497 5 : std::make_unique<TestType>(2 * size);
498 10 : REQUIRE_THROWS(*dhMap = *bigDh);
499 : }
500 :
501 10 : *dhMap = *dh2Ptr;
502 15 : THEN("a shallow copy is performed")
503 : {
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 : }
512 : }
513 :
514 70 : WHEN("copy assigning a partial DataHandlerMap through base pointers")
515 : {
516 20 : const auto dhCopy = dh;
517 20 : Vector_t<data_t> randVec{2 * size};
518 10 : randVec.setRandom();
519 20 : const TestType dh2{randVec};
520 20 : const auto dh2Map = dh2.getBlock(0, size);
521 :
522 15 : THEN("sizes must match")
523 : {
524 10 : const auto bigDh = dh2.getBlock(0, 2 * size);
525 10 : REQUIRE_THROWS(*dhMap = *bigDh);
526 : }
527 :
528 10 : *dhMap = *dh2Map;
529 15 : THEN("a deep copy is performed")
530 : {
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 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
536 :
537 5 : REQUIRE_EQ(*dhMap, *dh2Map);
538 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
539 : }
540 : }
541 :
542 70 : WHEN("copy assigning a full DataHandlerMap (aka a view) through base pointers")
543 : {
544 20 : Vector_t<data_t> randVec{size};
545 10 : randVec.setRandom();
546 20 : const TestType dh2{randVec};
547 20 : const auto dh2Map = dh2.getBlock(0, size);
548 :
549 15 : THEN("sizes must match")
550 : {
551 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
552 5 : std::make_unique<TestType>(2 * size);
553 15 : REQUIRE_THROWS(*dhMap = *bigDh->getBlock(0, 2 * size));
554 : }
555 :
556 10 : *dhMap = *dh2Map;
557 15 : THEN("a shallow copy is performed")
558 : {
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 : }
567 : }
568 :
569 70 : WHEN("\"move\" assigning a DataHandler through base pointers")
570 : {
571 20 : Vector_t<data_t> randVec{size};
572 10 : randVec.setRandom();
573 20 : const std::unique_ptr<DataHandler<data_t>> dh2Ptr = std::make_unique<TestType>(randVec);
574 :
575 15 : THEN("sizes must match")
576 : {
577 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
578 5 : std::make_unique<TestType>(2 * size);
579 10 : REQUIRE_THROWS(*dhMap = std::move(*bigDh));
580 : }
581 :
582 10 : *dhMap = std::move(*dh2Ptr);
583 15 : THEN("a shallow copy is performed")
584 : {
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 : }
593 : }
594 :
595 70 : WHEN("\"move\" assigning a partial DataHandlerMap through base pointers")
596 : {
597 20 : const auto dhCopy = dh;
598 20 : Vector_t<data_t> randVec{2 * size};
599 10 : randVec.setRandom();
600 20 : TestType dh2{randVec};
601 20 : const auto dh2Map = dh2.getBlock(0, size);
602 :
603 15 : THEN("sizes must match")
604 : {
605 10 : const auto bigDh = dh2.getBlock(0, 2 * size);
606 10 : REQUIRE_THROWS(*dhMap = std::move(*bigDh));
607 : }
608 :
609 10 : *dhMap = std::move(*dh2Map);
610 15 : THEN("a deep copy is performed")
611 : {
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 1570 : REQUIRE_UNARY(checkApproxEq(dh[i], dh2[i]));
617 :
618 5 : REQUIRE_EQ(*dhMap, *dh2Map);
619 5 : REQUIRE_EQ(&(*dhMap)[0], &dh[0]);
620 : }
621 : }
622 :
623 70 : WHEN("\"move\" assigning a full DataHandlerMap (aka a view) through base pointers")
624 : {
625 20 : Vector_t<data_t> randVec{size};
626 10 : randVec.setRandom();
627 20 : TestType dh2{randVec};
628 20 : const auto dh2Map = dh2.getBlock(0, size);
629 :
630 15 : THEN("sizes must match")
631 : {
632 10 : const std::unique_ptr<DataHandler<data_t>> bigDh =
633 5 : std::make_unique<TestType>(2 * size);
634 15 : REQUIRE_THROWS(*dhMap = std::move(*bigDh->getBlock(0, 2 * size)));
635 : }
636 :
637 10 : *dhMap = std::move(*dh2Map);
638 15 : THEN("a shallow copy is performed")
639 : {
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 : }
648 : }
649 : }
650 140 : }
651 :
652 220 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing clone()", TestType, datahandlermap_clone)
653 : {
654 : using data_t = typename TestType::value_type;
655 :
656 15 : GIVEN("a full DataHandlerMap (aka a view)")
657 : {
658 5 : index_t size = 728;
659 10 : Vector_t<data_t> dataVec(size);
660 5 : dataVec.setRandom();
661 10 : TestType realDh(dataVec);
662 10 : auto dhPtr = realDh.getBlock(0, size);
663 5 : auto& dh = *dhPtr;
664 :
665 10 : WHEN("cloning")
666 : {
667 10 : auto dhClone = dh.clone();
668 :
669 10 : THEN("a shallow copy is produced")
670 : {
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 : }
682 : }
683 : }
684 :
685 15 : GIVEN("a partial DataHandlerMap")
686 : {
687 5 : index_t size = 728;
688 10 : Vector_t<data_t> dataVec(size);
689 5 : dataVec.setRandom();
690 10 : TestType realDh(dataVec);
691 10 : auto dhPtr = realDh.getBlock(0, size / 2);
692 5 : auto& dh = *dhPtr;
693 :
694 10 : WHEN("a deep copy is produced")
695 : {
696 10 : auto dhClone = dh.clone();
697 :
698 10 : THEN("everything matches")
699 : {
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 : }
708 : }
709 : }
710 10 : }
711 :
712 220 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing the reduction operations", TestType,
713 : datahandlermap_reduction)
714 : {
715 : using data_t = typename TestType::value_type;
716 :
717 20 : GIVEN("some DataHandlerMap")
718 : {
719 10 : index_t size = 284;
720 :
721 20 : WHEN("putting in some random data")
722 : {
723 20 : auto randVec = generateRandomMatrix<data_t>(size * 2);
724 20 : TestType realDh(randVec);
725 20 : auto dhPtr = realDh.getBlock(size / 3, size);
726 10 : auto& dh = *dhPtr;
727 :
728 15 : THEN("the reductions work as expected")
729 : {
730 5 : REQUIRE_UNARY(checkApproxEq(dh.sum(), randVec.middleRows(size / 3, size).sum()));
731 5 : REQUIRE_EQ(dh.l0PseudoNorm(),
732 : (randVec.middleRows(size / 3, size).array().cwiseAbs()
733 : >= std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon())
734 : .count());
735 5 : REQUIRE_UNARY(checkApproxEq(
736 : dh.l1Norm(), randVec.middleRows(size / 3, size).array().abs().sum()));
737 5 : REQUIRE_UNARY(checkApproxEq(
738 : dh.lInfNorm(), randVec.middleRows(size / 3, size).array().abs().maxCoeff()));
739 5 : REQUIRE_UNARY(checkApproxEq(dh.squaredL2Norm(),
740 : randVec.middleRows(size / 3, size).squaredNorm()));
741 5 : REQUIRE_UNARY(
742 : checkApproxEq(dh.l2Norm(), randVec.middleRows(size / 3, size).norm()));
743 :
744 10 : auto randVec2 = generateRandomMatrix<data_t>(size);
745 10 : TestType realDh2(randVec2);
746 10 : auto dh2Ptr = realDh2.getBlock(0, size);
747 5 : auto& dh2 = *dh2Ptr;
748 5 : REQUIRE_UNARY(
749 : checkApproxEq(dh.dot(dh2), randVec.middleRows(size / 3, size).dot(randVec2)));
750 :
751 5 : TestType dhCPU(randVec2);
752 5 : REQUIRE_UNARY(
753 : checkApproxEq(dh.dot(dhCPU), randVec.middleRows(size / 3, size).dot(randVec2)));
754 : }
755 :
756 15 : THEN("the dot product expects correctly sized arguments")
757 : {
758 5 : index_t wrongSize = size - 1;
759 10 : Vector_t<data_t> randVec2(wrongSize);
760 5 : randVec2.setRandom();
761 10 : TestType dh2(randVec2);
762 :
763 10 : REQUIRE_THROWS_AS(dh.dot(dh2), InvalidArgumentError);
764 : }
765 : }
766 : }
767 10 : }
768 :
769 225 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing the element-wise operations", TestType,
770 : datahandlermap_elemwise)
771 : {
772 : using data_t = typename TestType::value_type;
773 :
774 30 : GIVEN("some DataHandlerMap")
775 : {
776 15 : index_t size = 567;
777 :
778 30 : WHEN("putting in some random data")
779 : {
780 30 : auto randVec = generateRandomMatrix<data_t>(size);
781 30 : TestType realDh(randVec);
782 :
783 30 : auto dhPtr = realDh.getBlock(0, size);
784 15 : auto& dh = static_cast<typename MapToHandler<TestType>::map&>(*dhPtr);
785 :
786 20 : THEN("the element-wise binary vector operations work as expected")
787 : {
788 10 : TestType bigDh{2 * size};
789 :
790 10 : REQUIRE_THROWS(dh += bigDh);
791 10 : REQUIRE_THROWS(dh -= bigDh);
792 10 : REQUIRE_THROWS(dh *= bigDh);
793 10 : REQUIRE_THROWS(dh /= bigDh);
794 :
795 10 : TestType realOldDh(randVec);
796 10 : auto oldDhPtr = realOldDh.getBlock(0, size);
797 5 : auto& oldDh = static_cast<typename MapToHandler<TestType>::map&>(*oldDhPtr);
798 :
799 10 : auto randVec2 = generateRandomMatrix<data_t>(size);
800 :
801 10 : TestType dhCPU(randVec2);
802 10 : 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 2835 : 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 2835 : 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 2835 : 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 2835 : 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 2835 : 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 2835 : 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 : }
848 :
849 20 : THEN("the element-wise binary scalar operations work as expected")
850 : {
851 10 : TestType realOldDh(randVec);
852 10 : 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 2835 : 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 2835 : 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 2835 : 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 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], oldDh[i] / scalar));
874 : }
875 :
876 20 : THEN("the element-wise assignment of a scalar works as expected")
877 : {
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 2835 : REQUIRE_UNARY(checkApproxEq(dh[i], scalar));
883 : }
884 : }
885 : }
886 15 : }
887 :
888 220 : TEST_CASE_TEMPLATE_DEFINE("DataHandlerMap: Testing referencing blocks", TestType,
889 : datahandlermap_blockref)
890 : {
891 : using data_t = typename TestType::value_type;
892 :
893 15 : GIVEN("some DataHandlerMap")
894 : {
895 5 : index_t size = 728;
896 10 : Vector_t<data_t> dataVec(size);
897 10 : TestType realDh(dataVec);
898 10 : auto dhPtr = realDh.getBlock(0, size);
899 5 : auto& dh = *dhPtr;
900 :
901 10 : WHEN("getting the reference to a block")
902 : {
903 10 : REQUIRE_THROWS(dh.getBlock(size, 1));
904 10 : REQUIRE_THROWS(dh.getBlock(0, size + 1));
905 :
906 10 : auto dhBlock = dh.getBlock(size / 3, size / 2);
907 :
908 10 : THEN("returned data handler references the correct elements")
909 : {
910 5 : REQUIRE_EQ(dhBlock->getSize(), size / 2);
911 :
912 1825 : for (index_t i = 0; i < size / 2; i++)
913 1820 : REQUIRE_EQ(&(*dhBlock)[i], &dh[i + size / 3]);
914 : }
915 : }
916 : }
917 :
918 15 : GIVEN("a const DataHandlerMap")
919 : {
920 5 : index_t size = 728;
921 10 : Vector_t<data_t> dataVec(size);
922 10 : const TestType realDh(dataVec);
923 10 : auto dhPtr = realDh.getBlock(0, size);
924 5 : auto& dh = *dhPtr;
925 :
926 10 : WHEN("getting the reference to a block")
927 : {
928 10 : REQUIRE_THROWS(dh.getBlock(size, 1));
929 10 : REQUIRE_THROWS(dh.getBlock(0, size + 1));
930 :
931 10 : auto dhBlock = dh.getBlock(size / 3, size / 2);
932 :
933 10 : THEN("returned data handler references the correct elements")
934 : {
935 5 : REQUIRE_EQ(dhBlock->getSize(), size / 2);
936 :
937 1825 : for (index_t i = 0; i < size / 2; i++)
938 1820 : REQUIRE_EQ(&(*dhBlock)[i], &dh[i + size / 3]);
939 : }
940 : }
941 : }
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();
|