test_DataContainer.cpp 57.3 KB
Newer Older
Tobias Lasser's avatar
Tobias Lasser committed
1
/**
2
 * @file test_DataContainer.cpp
Tobias Lasser's avatar
Tobias Lasser committed
3
 *
4
 * @brief Tests for DataContainer class
Tobias Lasser's avatar
Tobias Lasser committed
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
Tobias Lasser's avatar
Tobias Lasser committed
9
10
 */

11
#include "doctest/doctest.h"
Tobias Lasser's avatar
Tobias Lasser committed
12
#include "DataContainer.h"
13
#include "IdenticalBlocksDescriptor.h"
14
#include "testHelpers.h"
15
#include "VolumeDescriptor.h"
Jens Petit's avatar
Jens Petit committed
16

17
18
#include <type_traits>

Tobias Lasser's avatar
Tobias Lasser committed
19
using namespace elsa;
20
using namespace doctest;
Tobias Lasser's avatar
Tobias Lasser committed
21

22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
// Provides object to be used with TEMPLATE_PRODUCT_TEST_CASE, necessary because enum cannot be
// passed directly
template <typename T>
struct TestHelperGPU {
    static const DataHandlerType handler_t = DataHandlerType::GPU;
    using data_t = T;
};

// Provides object to be used with TEMPLATE_PRODUCT_TEST_CASE, necessary because enum cannot be
// passed directly
template <typename T>
struct TestHelperCPU {
    static const DataHandlerType handler_t = DataHandlerType::CPU;
    using data_t = T;
};

38
using CPUTypeTuple =
39
40
    std::tuple<TestHelperCPU<float>, TestHelperCPU<double>, TestHelperCPU<complex<float>>,
               TestHelperCPU<complex<double>>, TestHelperCPU<index_t>>;
41
42
43
44

TYPE_TO_STRING(TestHelperCPU<float>);
TYPE_TO_STRING(TestHelperCPU<double>);
TYPE_TO_STRING(TestHelperCPU<index_t>);
45
46
TYPE_TO_STRING(TestHelperCPU<complex<float>>);
TYPE_TO_STRING(TestHelperCPU<complex<double>>);
47

48
49
50
TYPE_TO_STRING(DataContainer<float>);
TYPE_TO_STRING(DataContainer<double>);
TYPE_TO_STRING(DataContainer<index_t>);
51
52
TYPE_TO_STRING(DataContainer<complex<float>>);
TYPE_TO_STRING(DataContainer<complex<double>>);
53

54
55
TYPE_TO_STRING(complex<float>);
TYPE_TO_STRING(complex<double>);
56

57
#ifdef ELSA_CUDA_VECTOR
58
using GPUTypeTuple =
59
60
    std::tuple<TestHelperGPU<float>, TestHelperGPU<double>, TestHelperGPU<complex<float>>,
               TestHelperGPU<complex<double>>, TestHelperGPU<index_t>>;
61
62
63
64

TYPE_TO_STRING(TestHelperGPU<float>);
TYPE_TO_STRING(TestHelperGPU<double>);
TYPE_TO_STRING(TestHelperGPU<index_t>);
65
66
TYPE_TO_STRING(TestHelperGPU<complex<float>>);
TYPE_TO_STRING(TestHelperGPU<complex<double>>);
67
#endif
68
69
70
71
72

TEST_SUITE_BEGIN("core");

TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing construction", TestType,
                          datacontainer_construction)
Jens Petit's avatar
Jens Petit committed
73
{
74
75
    using data_t = typename TestType::data_t;

76
77
    INFO("Testing type: " << TypeName_v<const data_t>);

Jens Petit's avatar
Jens Petit committed
78
79
    GIVEN("a DataDescriptor")
    {
Tobias Lasser's avatar
Tobias Lasser committed
80
81
        IndexVector_t numCoeff(3);
        numCoeff << 17, 47, 91;
82
        VolumeDescriptor desc(numCoeff);
Tobias Lasser's avatar
Tobias Lasser committed
83

Jens Petit's avatar
Jens Petit committed
84
85
        WHEN("constructing an empty DataContainer")
        {
86
            DataContainer<data_t> dc(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
87

88
            THEN("it has the correct DataDescriptor") { REQUIRE_EQ(dc.getDataDescriptor(), desc); }
Tobias Lasser's avatar
Tobias Lasser committed
89

90
            THEN("it has a data vector of correct size")
Jens Petit's avatar
Jens Petit committed
91
            {
92
                REQUIRE_EQ(dc.getSize(), desc.getNumberOfCoefficients());
Tobias Lasser's avatar
Tobias Lasser committed
93
94
95
            }
        }

Jens Petit's avatar
Jens Petit committed
96
97
        WHEN("constructing an initialized DataContainer")
        {
98
            auto data = generateRandomMatrix<data_t>(desc.getNumberOfCoefficients());
Tobias Lasser's avatar
Tobias Lasser committed
99

100
            DataContainer<data_t> dc(desc, data, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
101

102
            THEN("it has the correct DataDescriptor") { REQUIRE_EQ(dc.getDataDescriptor(), desc); }
Tobias Lasser's avatar
Tobias Lasser committed
103

Jens Petit's avatar
Jens Petit committed
104
105
            THEN("it has correctly initialized data")
            {
106
                REQUIRE_EQ(dc.getSize(), desc.getNumberOfCoefficients());
Tobias Lasser's avatar
Tobias Lasser committed
107
108

                for (index_t i = 0; i < dc.getSize(); ++i)
109
                    REQUIRE_UNARY(checkApproxEq(dc[i], data[i]));
Tobias Lasser's avatar
Tobias Lasser committed
110
111
112
113
            }
        }
    }

Jens Petit's avatar
Jens Petit committed
114
115
    GIVEN("another DataContainer")
    {
Tobias Lasser's avatar
Tobias Lasser committed
116
117
        IndexVector_t numCoeff(2);
        numCoeff << 32, 57;
118
        VolumeDescriptor desc(numCoeff);
Tobias Lasser's avatar
Tobias Lasser committed
119

120
        DataContainer<data_t> otherDc(desc, TestType::handler_t);
121
122

        auto randVec = generateRandomMatrix<data_t>(otherDc.getSize());
Tobias Lasser's avatar
Tobias Lasser committed
123
124
125
        for (index_t i = 0; i < otherDc.getSize(); ++i)
            otherDc[i] = randVec(i);

Jens Petit's avatar
Jens Petit committed
126
127
        WHEN("copy constructing")
        {
Tobias Lasser's avatar
Tobias Lasser committed
128
129
            DataContainer dc(otherDc);

Jens Petit's avatar
Jens Petit committed
130
131
            THEN("it copied correctly")
            {
132
133
                REQUIRE_EQ(dc.getDataDescriptor(), otherDc.getDataDescriptor());
                REQUIRE_NE(&dc.getDataDescriptor(), &otherDc.getDataDescriptor());
Tobias Lasser's avatar
Tobias Lasser committed
134

135
                REQUIRE_EQ(dc, otherDc);
Tobias Lasser's avatar
Tobias Lasser committed
136
137
138
            }
        }

Jens Petit's avatar
Jens Petit committed
139
140
        WHEN("copy assigning")
        {
141
            DataContainer<data_t> dc(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
142
143
            dc = otherDc;

Jens Petit's avatar
Jens Petit committed
144
145
            THEN("it copied correctly")
            {
146
147
                REQUIRE_EQ(dc.getDataDescriptor(), otherDc.getDataDescriptor());
                REQUIRE_NE(&dc.getDataDescriptor(), &otherDc.getDataDescriptor());
Tobias Lasser's avatar
Tobias Lasser committed
148

149
                REQUIRE_EQ(dc, otherDc);
Tobias Lasser's avatar
Tobias Lasser committed
150
151
152
            }
        }

Jens Petit's avatar
Jens Petit committed
153
154
        WHEN("move constructing")
        {
Tobias Lasser's avatar
Tobias Lasser committed
155
156
157
158
            DataContainer oldOtherDc(otherDc);

            DataContainer dc(std::move(otherDc));

Jens Petit's avatar
Jens Petit committed
159
160
            THEN("it moved correctly")
            {
161
                REQUIRE_EQ(dc.getDataDescriptor(), oldOtherDc.getDataDescriptor());
Tobias Lasser's avatar
Tobias Lasser committed
162

163
                REQUIRE_EQ(dc, oldOtherDc);
Tobias Lasser's avatar
Tobias Lasser committed
164
165
            }

Jens Petit's avatar
Jens Petit committed
166
            THEN("the moved from object is still valid (but empty)") { otherDc = dc; }
Tobias Lasser's avatar
Tobias Lasser committed
167
168
        }

Jens Petit's avatar
Jens Petit committed
169
170
        WHEN("move assigning")
        {
Tobias Lasser's avatar
Tobias Lasser committed
171
172
            DataContainer oldOtherDc(otherDc);

173
            DataContainer<data_t> dc(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
174
175
            dc = std::move(otherDc);

Jens Petit's avatar
Jens Petit committed
176
177
            THEN("it moved correctly")
            {
178
                REQUIRE_EQ(dc.getDataDescriptor(), oldOtherDc.getDataDescriptor());
Tobias Lasser's avatar
Tobias Lasser committed
179

180
                REQUIRE_EQ(dc, oldOtherDc);
Tobias Lasser's avatar
Tobias Lasser committed
181
182
            }

Jens Petit's avatar
Jens Petit committed
183
            THEN("the moved from object is still valid (but empty)") { otherDc = dc; }
Tobias Lasser's avatar
Tobias Lasser committed
184
185
186
187
        }
    }
}

188
189
TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing the reduction operations", TestType,
                          datacontainer_reduction)
Jens Petit's avatar
Jens Petit committed
190
{
191
192
    using data_t = typename TestType::data_t;

193
194
    INFO("Testing type: " << TypeName_v<const data_t>);

Jens Petit's avatar
Jens Petit committed
195
196
    GIVEN("a DataContainer")
    {
197
198
        IndexVector_t numCoeff(3);
        numCoeff << 11, 73, 45;
199
        VolumeDescriptor desc(numCoeff);
Tobias Lasser's avatar
Tobias Lasser committed
200

201
        WHEN("putting in some random data")
Jens Petit's avatar
Jens Petit committed
202
        {
203
            auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
204

205
            THEN("the reductions work a expected")
Jens Petit's avatar
Jens Petit committed
206
            {
207
208
209
210
211
212
213
214
215
216
217
                REQUIRE_UNARY(checkApproxEq(dc.sum(), randVec.sum()));
                REQUIRE_UNARY(checkApproxEq(
                    dc.l0PseudoNorm(),
                    (randVec.array().cwiseAbs()
                     >= std::numeric_limits<GetFloatingPointType_t<data_t>>::epsilon())
                        .count()));
                REQUIRE_UNARY(checkApproxEq(dc.l1Norm(), randVec.array().abs().sum()));
                REQUIRE_UNARY(checkApproxEq(dc.lInfNorm(), randVec.array().abs().maxCoeff()));
                REQUIRE_UNARY(checkApproxEq(dc.squaredL2Norm(), randVec.squaredNorm()));

                auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
218

219
                REQUIRE_UNARY(checkApproxEq(dc.dot(dc2), randVec.dot(randVec2)));
220
221
222
223
224
225
226
227

                if constexpr (isComplex<data_t>) {
                    CHECK_THROWS(dc.minElement());
                    CHECK_THROWS(dc.maxElement());
                } else {
                    REQUIRE_UNARY(checkApproxEq(dc.minElement(), randVec.array().minCoeff()));
                    REQUIRE_UNARY(checkApproxEq(dc.maxElement(), randVec.array().maxCoeff()));
                }
Tobias Lasser's avatar
Tobias Lasser committed
228
229
230
231
232
            }
        }
    }
}

233
234
TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing element-wise access", TestType,
                          datacontainer_elemwise)
Jens Petit's avatar
Jens Petit committed
235
{
236
237
    using data_t = typename TestType::data_t;

238
239
    INFO("Testing type: " << TypeName_v<const data_t>);

Jens Petit's avatar
Jens Petit committed
240
241
    GIVEN("a DataContainer")
    {
242
243
        IndexVector_t numCoeff(2);
        numCoeff << 47, 11;
244
        VolumeDescriptor desc(numCoeff);
245
        DataContainer<data_t> dc(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
246

247
        WHEN("accessing the elements")
Jens Petit's avatar
Jens Petit committed
248
        {
249
250
251
            IndexVector_t coord(2);
            coord << 17, 4;
            index_t index = desc.getIndexFromCoordinate(coord);
Tobias Lasser's avatar
Tobias Lasser committed
252

253
            THEN("it works as expected when using indices/coordinates")
Jens Petit's avatar
Jens Petit committed
254
            {
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
                // For integral typeps don't have a floating point value
                if constexpr (std::is_integral_v<data_t>) {
                    dc[index] = data_t(2);
                    REQUIRE_UNARY(checkApproxEq(dc[index], 2));
                    REQUIRE_UNARY(checkApproxEq(dc(coord), 2));
                    REQUIRE_UNARY(checkApproxEq(dc(17, 4), 2));

                    dc(coord) = data_t(3);
                    REQUIRE_UNARY(checkApproxEq(dc[index], 3));
                    REQUIRE_UNARY(checkApproxEq(dc(coord), 3));
                    REQUIRE_UNARY(checkApproxEq(dc(17, 4), 3));
                } else {
                    dc[index] = data_t(2.2f);
                    REQUIRE_UNARY(checkApproxEq(dc[index], 2.2f));
                    REQUIRE_UNARY(checkApproxEq(dc(coord), 2.2f));
                    REQUIRE_UNARY(checkApproxEq(dc(17, 4), 2.2f));

                    dc(coord) = data_t(3.3f);
                    REQUIRE_UNARY(checkApproxEq(dc[index], 3.3f));
                    REQUIRE_UNARY(checkApproxEq(dc(coord), 3.3f));
                    REQUIRE_UNARY(checkApproxEq(dc(17, 4), 3.3f));
                }
Tobias Lasser's avatar
Tobias Lasser committed
277
278
279
            }
        }
    }
280

Jens Petit's avatar
Jens Petit committed
281
282
    GIVEN("a DataContainer")
    {
Tobias Lasser's avatar
Tobias Lasser committed
283
284
        IndexVector_t numCoeff(2);
        numCoeff << 47, 11;
285
        VolumeDescriptor desc(numCoeff);
Tobias Lasser's avatar
Tobias Lasser committed
286

Jens Petit's avatar
Jens Petit committed
287
288
        WHEN("putting in some random data")
        {
289
            auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
290

Jens Petit's avatar
Jens Petit committed
291
292
            THEN("the element-wise unary operations work as expected")
            {
293
294
295
296
                DataContainer dcAbs = cwiseAbs(dc);
                for (index_t i = 0; i < dc.getSize(); ++i)
                    REQUIRE_UNARY(checkApproxEq(dcAbs[i], randVec.array().abs()[i]));

Jens Petit's avatar
Jens Petit committed
297
                DataContainer dcSquare = square(dc);
Tobias Lasser's avatar
Tobias Lasser committed
298
                for (index_t i = 0; i < dc.getSize(); ++i)
299
                    REQUIRE_UNARY(checkApproxEq(dcSquare[i], randVec.array().square()[i]));
300
                DataContainer dcSqrt = sqrt(dcSquare);
Tobias Lasser's avatar
Tobias Lasser committed
301
                for (index_t i = 0; i < dc.getSize(); ++i)
302
                    REQUIRE_UNARY(checkApproxEq(dcSqrt[i], randVec.array().square().sqrt()[i]));
303

304
305
                // do exponent check only for floating point types as for integer will likely
                // lead to overflow due to random init over full value range
306
307
308
                if constexpr (!std::is_integral_v<data_t>) {
                    DataContainer dcExp = exp(dc);
                    for (index_t i = 0; i < dc.getSize(); ++i)
309
                        REQUIRE_UNARY(checkApproxEq(dcExp[i], randVec.array().exp()[i]));
310
                }
Tobias Lasser's avatar
Tobias Lasser committed
311

312
                DataContainer dcLog = log(dcSquare);
Tobias Lasser's avatar
Tobias Lasser committed
313
                for (index_t i = 0; i < dc.getSize(); ++i)
314
                    REQUIRE_UNARY(checkApproxEq(dcLog[i], randVec.array().square().log()[i]));
315
316
317
318
319
320
321
322
323
324
325
326
327
328

                DataContainer dcReal = real(dc);
                for (index_t i = 0; i < dc.getSize(); ++i)
                    REQUIRE_UNARY(checkApproxEq(dcReal[i], randVec.array().real()[i]));

                DataContainer dcImag = imag(dc);

                if constexpr (isComplex<data_t>) {
                    for (index_t i = 0; i < dc.getSize(); ++i)
                        REQUIRE_UNARY(checkApproxEq(dcImag[i], randVec.array().imag()[i]));
                } else {
                    for (index_t i = 0; i < dc.getSize(); ++i)
                        REQUIRE_UNARY(checkApproxEq(dcImag[i], 0));
                }
Tobias Lasser's avatar
Tobias Lasser committed
329
330
            }

331
332
            auto scalar = static_cast<data_t>(923.41f);

Jens Petit's avatar
Jens Petit committed
333
334
            THEN("the binary in-place addition of a scalar work as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
335
336
                dc += scalar;
                for (index_t i = 0; i < dc.getSize(); ++i)
337
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) + scalar));
Tobias Lasser's avatar
Tobias Lasser committed
338
339
            }

Jens Petit's avatar
Jens Petit committed
340
341
            THEN("the binary in-place subtraction of a scalar work as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
342
343
                dc -= scalar;
                for (index_t i = 0; i < dc.getSize(); ++i)
344
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) - scalar));
Tobias Lasser's avatar
Tobias Lasser committed
345
346
            }

Jens Petit's avatar
Jens Petit committed
347
348
            THEN("the binary in-place multiplication with a scalar work as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
349
350
                dc *= scalar;
                for (index_t i = 0; i < dc.getSize(); ++i)
351
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) * scalar));
Tobias Lasser's avatar
Tobias Lasser committed
352
353
            }

Jens Petit's avatar
Jens Petit committed
354
355
            THEN("the binary in-place division by a scalar work as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
356
357
                dc /= scalar;
                for (index_t i = 0; i < dc.getSize(); ++i)
358
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) / scalar));
Tobias Lasser's avatar
Tobias Lasser committed
359
360
            }

Jens Petit's avatar
Jens Petit committed
361
362
            THEN("the element-wise assignment of a scalar works as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
363
364
                dc = scalar;
                for (index_t i = 0; i < dc.getSize(); ++i)
365
                    REQUIRE_UNARY(checkApproxEq(dc[i], scalar));
Tobias Lasser's avatar
Tobias Lasser committed
366
367
368
            }
        }

Jens Petit's avatar
Jens Petit committed
369
370
        WHEN("having two containers with random data")
        {
371
372
            auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
            auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
373

Jens Petit's avatar
Jens Petit committed
374
375
            THEN("the element-wise in-place addition works as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
376
377
                dc += dc2;
                for (index_t i = 0; i < dc.getSize(); ++i)
378
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) + randVec2(i)));
Tobias Lasser's avatar
Tobias Lasser committed
379
380
            }

Jens Petit's avatar
Jens Petit committed
381
382
            THEN("the element-wise in-place subtraction works as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
383
384
                dc -= dc2;
                for (index_t i = 0; i < dc.getSize(); ++i)
385
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) - randVec2(i)));
Tobias Lasser's avatar
Tobias Lasser committed
386
387
            }

Jens Petit's avatar
Jens Petit committed
388
389
            THEN("the element-wise in-place multiplication works as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
390
391
                dc *= dc2;
                for (index_t i = 0; i < dc.getSize(); ++i)
392
                    REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) * randVec2(i)));
Tobias Lasser's avatar
Tobias Lasser committed
393
394
            }

Jens Petit's avatar
Jens Petit committed
395
396
            THEN("the element-wise in-place division works as expected")
            {
Tobias Lasser's avatar
Tobias Lasser committed
397
398
                dc /= dc2;
                for (index_t i = 0; i < dc.getSize(); ++i)
399
                    if (dc2[i] != data_t(0))
400
                        REQUIRE_UNARY(checkApproxEq(dc[i], randVec(i) / randVec2(i)));
Tobias Lasser's avatar
Tobias Lasser committed
401
402
            }
        }
403
404
405
406
407

        WHEN("having two containers with real and complex data each")
        {
            auto [dcReals1, realsVec1] = generateRandomContainer<real_t>(desc, TestType::handler_t);
            auto [dcComps1, compsVec1] =
408
                generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
409

410
411
            THEN("the element-wise maximum operation works as expected for two real "
                 "DataContainers")
412
413
414
415
416
417
418
419
420
421
422
423
424
425
            {
                auto [dcReals2, realsVec2] =
                    generateRandomContainer<real_t>(desc, TestType::handler_t);

                DataContainer dcCWiseMax = cwiseMax(dcReals1, dcReals2);
                for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
                    REQUIRE_UNARY(
                        checkApproxEq(dcCWiseMax[i], realsVec1.array().max(realsVec2.array())[i]));
            }

            THEN("the element-wise maximum operation works as expected for a real and a complex "
                 "DataContainer")
            {
                auto [dcComps2, compsVec2] =
426
                    generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
427
428
429
430
431
432
433
434
435
436
437

                DataContainer dcCWiseMax = cwiseMax(dcReals1, dcComps2);
                for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
                    REQUIRE_UNARY(checkApproxEq(dcCWiseMax[i],
                                                realsVec1.array().max(compsVec2.array().abs())[i]));
            }

            THEN("the element-wise maximum operation works as expected for a complex and a real "
                 "DataContainer")
            {
                auto [dcComps2, compsVec2] =
438
                    generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
439
440
441
442
443
444
445
446
447
448

                DataContainer dcCWiseMax = cwiseMax(dcComps2, dcReals1);
                for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
                    REQUIRE_UNARY(checkApproxEq(dcCWiseMax[i],
                                                compsVec2.array().abs().max(realsVec1.array())[i]));
            }

            THEN("the element-wise maximum operation works as expected for two DataContainers")
            {
                auto [dcComps2, compsVec2] =
449
                    generateRandomContainer<complex<real_t>>(desc, TestType::handler_t);
450
451
452
453
454
455
456

                DataContainer dcCWiseMax = cwiseMax(dcComps1, dcComps2);
                for (index_t i = 0; i < dcCWiseMax.getSize(); ++i)
                    REQUIRE_UNARY(checkApproxEq(
                        dcCWiseMax[i], compsVec1.array().abs().max(compsVec2.array().abs())[i]));
            }
        }
Tobias Lasser's avatar
Tobias Lasser committed
457
458
459
    }
}

460
461
462
TEST_CASE_TEMPLATE_DEFINE(
    "DataContainer: Testing the arithmetic operations with DataContainers arguments", TestType,
    datacontainer_arithmetic)
Jens Petit's avatar
Jens Petit committed
463
{
464
465
    using data_t = typename TestType::data_t;

466
467
    INFO("Testing type: " << TypeName_v<const data_t>);

Jens Petit's avatar
Jens Petit committed
468
469
    GIVEN("some DataContainers")
    {
Tobias Lasser's avatar
Tobias Lasser committed
470
471
        IndexVector_t numCoeff(3);
        numCoeff << 52, 7, 29;
472
        VolumeDescriptor desc(numCoeff);
Tobias Lasser's avatar
Tobias Lasser committed
473

474
475
        auto [dc, randVec] = generateRandomContainer<data_t>(desc, TestType::handler_t);
        auto [dc2, randVec2] = generateRandomContainer<data_t>(desc, TestType::handler_t);
Tobias Lasser's avatar
Tobias Lasser committed
476

Jens Petit's avatar
Jens Petit committed
477
478
        THEN("the binary element-wise operations work as expected")
        {
479
            DataContainer resultPlus = dc + dc2;
Tobias Lasser's avatar
Tobias Lasser committed
480
            for (index_t i = 0; i < dc.getSize(); ++i)
481
                REQUIRE_UNARY(checkApproxEq(resultPlus[i], dc[i] + dc2[i]));
Tobias Lasser's avatar
Tobias Lasser committed
482

483
            DataContainer resultMinus = dc - dc2;
Tobias Lasser's avatar
Tobias Lasser committed
484
            for (index_t i = 0; i < dc.getSize(); ++i)
485
                REQUIRE_UNARY(checkApproxEq(resultMinus[i], dc[i] - dc2[i]));
Tobias Lasser's avatar
Tobias Lasser committed
486

487
            DataContainer resultMult = dc * dc2;
Tobias Lasser's avatar
Tobias Lasser committed
488
            for (index_t i = 0; i < dc.getSize(); ++i)
489
                REQUIRE_UNARY(checkApproxEq(resultMult[i], dc[i] * dc2[i]));
Tobias Lasser's avatar
Tobias Lasser committed
490

491
            DataContainer resultDiv = dc / dc2;
Tobias Lasser's avatar
Tobias Lasser committed
492
            for (index_t i = 0; i < dc.getSize(); ++i)
493
                if (dc2[i] != data_t(0))
494
                    REQUIRE_UNARY(checkApproxEq(resultDiv[i], dc[i] / dc2[i]));
Tobias Lasser's avatar
Tobias Lasser committed
495
496
        }

Jens Petit's avatar
Jens Petit committed
497
498
        THEN("the operations with a scalar work as expected")
        {
499
            data_t scalar = static_cast<data_t>(4.92f);
Tobias Lasser's avatar
Tobias Lasser committed
500

501
            DataContainer resultScalarPlus = scalar + dc;
Tobias Lasser's avatar
Tobias Lasser committed
502
            for (index_t i = 0; i < dc.getSize(); ++i)
503
                REQUIRE_UNARY(checkApproxEq(resultScalarPlus[i], scalar + dc[i]));
Tobias Lasser's avatar
Tobias Lasser committed
504

505
            DataContainer resultPlusScalar = dc + scalar;
Tobias Lasser's avatar
Tobias Lasser committed
506
            for (index_t i = 0; i < dc.getSize(); ++i)
507
                REQUIRE_UNARY(checkApproxEq(resultPlusScalar[i], dc[i] + scalar));
Tobias Lasser's avatar
Tobias Lasser committed
508

509
            DataContainer resultScalarMinus = scalar - dc;
Tobias Lasser's avatar
Tobias Lasser committed
510
            for (index_t i = 0; i < dc.getSize(); ++i)
511
                REQUIRE_UNARY(checkApproxEq(resultScalarMinus[i], scalar - dc[i]));
Tobias Lasser's avatar
Tobias Lasser committed
512

513
            DataContainer resultMinusScalar = dc - scalar;
Tobias Lasser's avatar
Tobias Lasser committed
514
            for (index_t i = 0; i < dc.getSize(); ++i)
515
                REQUIRE_UNARY(checkApproxEq(resultMinusScalar[i], dc[i] - scalar));
Tobias Lasser's avatar
Tobias Lasser committed
516

517
            DataContainer resultScalarMult = scalar * dc;
Tobias Lasser's avatar
Tobias Lasser committed
518
            for (index_t i = 0; i < dc.getSize(); ++i)
519
                REQUIRE_UNARY(checkApproxEq(resultScalarMult[i], scalar * dc[i]));
Tobias Lasser's avatar
Tobias Lasser committed
520

521
            DataContainer resultMultScalar = dc * scalar;
Tobias Lasser's avatar
Tobias Lasser committed
522
            for (index_t i = 0; i < dc.getSize(); ++i)
523
                REQUIRE_UNARY(checkApproxEq(resultMultScalar[i], dc[i] * scalar));
Tobias Lasser's avatar
Tobias Lasser committed
524

525
            DataContainer resultScalarDiv = scalar / dc;
Tobias Lasser's avatar
Tobias Lasser committed
526
            for (index_t i = 0; i < dc.getSize(); ++i)
527
                if (dc[i] != data_t(0))
528
                    REQUIRE_UNARY(checkApproxEq(resultScalarDiv[i], scalar / dc[i]));
Tobias Lasser's avatar
Tobias Lasser committed
529

530
            DataContainer resultDivScalar = dc / scalar;
Tobias Lasser's avatar
Tobias Lasser committed
531
            for (index_t i = 0; i < dc.getSize(); ++i)
532
                REQUIRE_UNARY(checkApproxEq(resultDivScalar[i], dc[i] / scalar));
Jens Petit's avatar
Jens Petit committed
533
534
535
536
        }
    }
}

537
538
TEST_CASE_TEMPLATE_DEFINE("DataContainer: Testing creation of Maps through DataContainer", TestType,
                          datacontainer_maps)
Jens Petit's avatar
Jens Petit committed
539
{
540
541
    using data_t = typename TestType::data_t;

542
543
    INFO("Testing type: " << TypeName_v<const data_t>);

544
    GIVEN("a non-blocked container")
Jens Petit's avatar
Jens Petit committed
545
    {
Jens Petit's avatar
Jens Petit committed
546
547
        IndexVector_t numCoeff(3);
        numCoeff << 52, 7, 29;
548
        VolumeDescriptor desc(numCoeff);
Jens Petit's avatar
Jens Petit committed
549

550
551
        DataContainer<data_t> dc(desc, TestType::handler_t);
        const DataContainer<data_t> constDc(desc, TestType::handler_t);
Jens Petit's avatar
Jens Petit committed
552

553
        WHEN("trying to reference a block")
Jens Petit's avatar
Jens Petit committed
554
        {
555
            THEN("an exception occurs")
Jens Petit's avatar
Jens Petit committed
556
            {
557
558
                REQUIRE_THROWS(dc.getBlock(0));
                REQUIRE_THROWS(constDc.getBlock(0));
Jens Petit's avatar
Jens Petit committed
559
560
561
            }
        }

562
        WHEN("creating a view")
Jens Petit's avatar
Jens Petit committed
563
        {
564
565
            IndexVector_t numCoeff(1);
            numCoeff << desc.getNumberOfCoefficients();
566
            VolumeDescriptor linearDesc(numCoeff);
567
568
            auto linearDc = dc.viewAs(linearDesc);
            auto linearConstDc = constDc.viewAs(linearDesc);
Jens Petit's avatar
Jens Petit committed
569

570
            THEN("view has the correct descriptor and data")
Jens Petit's avatar
Jens Petit committed
571
            {
572
573
                REQUIRE_EQ(linearDesc, linearDc.getDataDescriptor());
                REQUIRE_EQ(&linearDc[0], &dc[0]);
Jens Petit's avatar
Jens Petit committed
574

575
576
                REQUIRE_EQ(linearDesc, linearConstDc.getDataDescriptor());
                REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
Jens Petit's avatar
Jens Petit committed
577

578
579
580
581
                AND_THEN("view is not a shallow copy")
                {
                    const auto dcCopy = dc;
                    const auto constDcCopy = constDc;
Jens Petit's avatar
Jens Petit committed
582

583
                    linearDc[0] = 1;
584
585
                    REQUIRE_EQ(&linearDc[0], &dc[0]);
                    REQUIRE_NE(&linearDc[0], &dcCopy[0]);
Jens Petit's avatar
Jens Petit committed
586

587
                    linearConstDc[0] = 1;
588
589
                    REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
                    REQUIRE_NE(&linearConstDc[0], &constDcCopy[0]);
590
                }
Jens Petit's avatar
Jens Petit committed
591
            }
592
593
        }
    }
Jens Petit's avatar
Jens Petit committed
594

595
596
597
598
    GIVEN("a blocked container")
    {
        IndexVector_t numCoeff(2);
        numCoeff << 52, 29;
599
        VolumeDescriptor desc(numCoeff);
600
        index_t numBlocks = 7;
601
        IdenticalBlocksDescriptor blockDesc(numBlocks, desc);
Jens Petit's avatar
Jens Petit committed
602

603
604
        DataContainer<data_t> dc(blockDesc, TestType::handler_t);
        const DataContainer<data_t> constDc(blockDesc, TestType::handler_t);
Jens Petit's avatar
Jens Petit committed
605

606
607
608
        WHEN("referencing a block")
        {
            THEN("block has the correct descriptor and data")
Jens Petit's avatar
Jens Petit committed
609
            {
610
611
612
                for (index_t i = 0; i < numBlocks; i++) {
                    auto dcBlock = dc.getBlock(i);
                    const auto constDcBlock = constDc.getBlock(i);
Jens Petit's avatar
Jens Petit committed
613

614
615
                    REQUIRE_EQ(dcBlock.getDataDescriptor(), blockDesc.getDescriptorOfBlock(i));
                    REQUIRE_EQ(&dcBlock[0], &dc[0] + blockDesc.getOffsetOfBlock(i));
616

617
618
                    REQUIRE_EQ(constDcBlock.getDataDescriptor(), blockDesc.getDescriptorOfBlock(i));
                    REQUIRE_EQ(&constDcBlock[0], &constDc[0] + blockDesc.getOffsetOfBlock(i));
619
                }
Jens Petit's avatar
Jens Petit committed
620
621
622
            }
        }

Jens Petit's avatar
Jens Petit committed
623
        WHEN("creating a view")
Jens Petit's avatar
Jens Petit committed
624
        {
625
626
            IndexVector_t numCoeff(1);
            numCoeff << blockDesc.getNumberOfCoefficients();
627
            VolumeDescriptor linearDesc(numCoeff);
628
629
630
631
632
            auto linearDc = dc.viewAs(linearDesc);
            auto linearConstDc = constDc.viewAs(linearDesc);

            THEN("view has the correct descriptor and data")
            {
633
634
                REQUIRE_EQ(linearDesc, linearDc.getDataDescriptor());
                REQUIRE_EQ(&linearDc[0], &dc[0]);
635

636
637
                REQUIRE_EQ(linearDesc, linearConstDc.getDataDescriptor());
                REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
Jens Petit's avatar
Jens Petit committed
638

639
640
641
642
643
644
                AND_THEN("view is not a shallow copy")
                {
                    const auto dcCopy = dc;
                    const auto constDcCopy = constDc;

                    linearDc[0] = 1;
645
646
                    REQUIRE_EQ(&linearDc[0], &dc[0]);
                    REQUIRE_NE(&linearDc[0], &dcCopy[0]);
647
648

                    linearConstDc[0] = 1;
649
650
                    REQUIRE_EQ(&linearConstDc[0], &constDc[0]);
                    REQUIRE_NE(&linearConstDc[0], &constDcCopy[0]);
651
652
                }
            }
Tobias Lasser's avatar
Tobias Lasser committed
653
654
        }
    }
655
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
662
663
664
{
    GIVEN("A CPU DataContainer with random data")
    {
        IndexVector_t numCoeff(3);
        numCoeff << 52, 7, 29;
665
        VolumeDescriptor desc(numCoeff);
666
667
668
669

        DataContainer<TestType> dcCPU(desc, DataHandlerType::CPU);
        DataContainer<TestType> dcGPU(desc, DataHandlerType::GPU);

670
        auto randVec = generateRandomMatrix<TestType>(dcCPU.getSize());
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690

        for (index_t i = 0; i < dcCPU.getSize(); ++i) {
            dcCPU[i] = randVec(i);
            dcGPU[i] = randVec(i);
        }

        WHEN("Trying to call loadToCPU on CPU container")
        {
            THEN("Throws") { REQUIRE_THROWS(dcCPU.loadToCPU()); }
        }

        WHEN("Trying to call loadToGPU on GPU container")
        {
            THEN("Throws") { REQUIRE_THROWS(dcGPU.loadToGPU()); }
        }

        WHEN("Loading to GPU from CPU")
        {
            DataContainer dcGPU2 = dcCPU.loadToGPU();

691
            REQUIRE_EQ(dcGPU2.getDataHandlerType(), DataHandlerType::GPU);
692
693
694
695

            THEN("all elements have to be the same")
            {
                for (index_t i = 0; i < dcCPU.getSize(); ++i) {
696
                    REQUIRE_UNARY(checkApproxEq(dcGPU2[i], dcGPU[i]));
697
698
699
700
701
702
703
704
                }
            }
        }

        WHEN("Loading to CPU from GPU")
        {
            DataContainer dcCPU2 = dcGPU.loadToCPU();

705
            REQUIRE_EQ(dcCPU2.getDataHandlerType(), DataHandlerType::CPU);
706
707
708
709

            THEN("all elements have to be the same")
            {
                for (index_t i = 0; i < dcCPU.getSize(); ++i) {
710
                    REQUIRE_UNARY(checkApproxEq(dcCPU2[i], dcGPU[i]));
711
712
713
714
715
716
717
718
719
720
                }
            }
        }

        WHEN("copy-assigning a GPU to a CPU container")
        {
            dcCPU = dcGPU;

            THEN("it should be a GPU container")
            {
721
                REQUIRE_EQ(dcCPU.getDataHandlerType(), DataHandlerType::GPU);
722
723
            }

724
725
726
727
728
729
730
731
732
            AND_THEN("they should be equal")
            {
                REQUIRE_EQ(dcCPU, dcGPU);
                REQUIRE_EQ(dcCPU.getSize(), dcGPU.getSize());

                for (index_t i = 0; i < dcCPU.getSize(); ++i) {
                    REQUIRE_UNARY(checkApproxEq(dcCPU[i], dcGPU[i]));
                }
            }
733
734
735
736
737
738
739
740
        }

        WHEN("copy-assigning a CPU to a GPU container")
        {
            dcGPU = dcCPU;

            THEN("it should be a GPU container")
            {
741
                REQUIRE_EQ(dcGPU.getDataHandlerType(), DataHandlerType::CPU);
742
743
            }

744
745
746
747
748
749
750
751
752
            AND_THEN("they should be equal")
            {
                REQUIRE_EQ(dcCPU, dcGPU);
                REQUIRE_EQ(dcCPU.getSize(), dcGPU.getSize());

                for (index_t i = 0; i < dcCPU.getSize(); ++i) {
                    REQUIRE_UNARY(checkApproxEq(dcCPU[i], dcGPU[i]));
                }
            }
753
754
755
756
757
        }
    }
}
#endif

758
TEST_CASE("DataContainer: Testing iterators for DataContainer")
Jens Petit's avatar
Jens Petit committed
759
760
761
{
    GIVEN("A 1D container")
    {
762
763
764
        constexpr index_t size = 20;
        IndexVector_t numCoeff(1);
        numCoeff << size;
765
        VolumeDescriptor desc(numCoeff);
766
767
768
769

        DataContainer dc1(desc);
        DataContainer dc2(desc);

Jens Petit's avatar
Jens Petit committed
770
        Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size);
771
772
773
774
775
776
777
        Eigen::VectorXf randVec2 = Eigen::VectorXf::Random(size);

        for (index_t i = 0; i < size; ++i) {
            dc1[i] = randVec1(i);
            dc2[i] = randVec2(i);
        }

Jens Petit's avatar
Jens Petit committed
778
779
        THEN("We can iterate forward")
        {
780
781
            int i = 0;
            for (auto v = dc1.cbegin(); v != dc1.cend(); v++) {
782
                REQUIRE_UNARY(checkApproxEq(*v, randVec1[i++]));
783
            }
784
            REQUIRE_EQ(i, size);
785
786
        }

Jens Petit's avatar
Jens Petit committed
787
788
        THEN("We can iterate backward")
        {
789
790
            int i = size;
            for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
791
                REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
792
            }
793
            REQUIRE_EQ(i, 0);
794
795
        }

Jens Petit's avatar
Jens Petit committed
796
797
        THEN("We can iterate and mutate")
        {
798
799
            int i = 0;
            for (auto& v : dc1) {
Jens Petit's avatar
Jens Petit committed
800
                v = v * 2;
801
                REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
802
            }
803
            REQUIRE_EQ(i, size);
804
805
806

            i = 0;
            for (auto v : dc1) {
807
                REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
808
            }
809
            REQUIRE_EQ(i, size);
810
811
        }

Jens Petit's avatar
Jens Petit committed
812
813
        THEN("We can use STL algorithms")
        {
814
815
            REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
            REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
816
817
        }
    }
Jens Petit's avatar
Jens Petit committed
818
819
    GIVEN("A 2D container")
    {
820
821
822
        constexpr index_t size = 20;
        IndexVector_t numCoeff(2);
        numCoeff << size, size;
823
        VolumeDescriptor desc(numCoeff);
824
825
826
827
828
829
830
831
832

        DataContainer dc1(desc);

        Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size);

        for (index_t i = 0; i < dc1.getSize(); ++i) {
            dc1[i] = randVec1[i];
        }

Jens Petit's avatar
Jens Petit committed
833
834
        THEN("We can iterate forward")
        {
835
            int i = 0;
Jens Petit's avatar
Jens Petit committed
836
            for (auto v : dc1) {
837
                REQUIRE_UNARY(checkApproxEq(v, randVec1[i++]));
838
            }
839
            REQUIRE_EQ(i, size * size);
840
841
        }

Jens Petit's avatar
Jens Petit committed
842
843
        THEN("We can iterate backward")
        {
844
845
            int i = size * size;
            for (auto v = dc1.crbegin(); v != dc1.crend(); v++) {
846
                REQUIRE_UNARY(checkApproxEq(*v, randVec1[--i]));
847
            }
848
            REQUIRE_EQ(i, 0);
849
850
        }

Jens Petit's avatar
Jens Petit committed
851
852
        THEN("We can iterate and mutate")
        {
853
854
            int i = 0;
            for (auto& v : dc1) {
Jens Petit's avatar
Jens Petit committed
855
                v = v * 2;
856
                REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
857
            }
858
            REQUIRE_EQ(i, size * size);
859
860
861

            i = 0;
            for (auto v : dc1) {
862
                REQUIRE_UNARY(checkApproxEq(v, 2 * randVec1[i++]));
863
            }
864
            REQUIRE_EQ(i, size * size);
865
866
        }

Jens Petit's avatar
Jens Petit committed
867
868
        THEN("We can use STL algorithms")
        {
869
870
            REQUIRE_EQ(*std::min_element(dc1.cbegin(), dc1.cend()), randVec1.minCoeff());
            REQUIRE_EQ(*std::max_element(dc1.cbegin(), dc1.cend()), randVec1.maxCoeff());
871
872
        }
    }
Jens Petit's avatar
Jens Petit committed
873
874
    GIVEN("A 3D container")
    {
875
876
877
        constexpr index_t size = 20;
        IndexVector_t numCoeff(3);
        numCoeff << size, size, size;
878
        VolumeDescriptor desc(numCoeff);
879
880
881

        DataContainer dc1(desc);

Jens Petit's avatar
Jens Petit committed
882
        Eigen::VectorXf randVec1 = Eigen::VectorXf::Random(size * size * size);
883
884
885
886
887

        for (index_t i = 0; i < dc1.getSize(); ++i) {
            dc1[i] = randVec1[i];
        }

Jens Petit's avatar
Jens Petit committed
888
889
        THEN("We can iterate forward")
        {
890
891
            int i = 0;
            for (auto v : dc1) {
892
                REQUIRE_UNARY(checkApproxEq(v, randVec1[i++]));
893
            }
894
            REQUIRE_EQ(i, size * size * size);
895
896
        }

Jens Petit's avatar
Jens Petit committed
897
898
        THEN("We can iterate backward")
        {
David Frank's avatar