LCOV - code coverage report
Current view: top level - elsa/operators/tests - test_FourierTransform.cpp (source / functions) Hit Total Coverage
Test: coverage-all.lcov Lines: 187 187 100.0 %
Date: 2022-08-25 03:05:39 Functions: 6 6 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file test_FourierTransform.cpp
       3             :  *
       4             :  * @brief Tests for the fourier transform operator
       5             :  *
       6             :  * @author Jonas Jelten
       7             :  */
       8             : 
       9             : #include "ExpressionPredicates.h"
      10             : #include "doctest/doctest.h"
      11             : #include "elsaDefines.h"
      12             : #include "testHelpers.h"
      13             : #include "FourierTransform.h"
      14             : #include "VolumeDescriptor.h"
      15             : #include "DataHandlerCPU.h"
      16             : #include <type_traits>
      17             : 
      18             : using namespace elsa;
      19             : 
      20             : TEST_SUITE_BEGIN("core");
      21             : 
      22             : TEST_CASE_TEMPLATE("FourierTransform: Testing construction", data_t, float, double)
      23           4 : {
      24           4 :     GIVEN("a descriptor")
      25           4 :     {
      26           4 :         IndexVector_t numCoeff(3);
      27           4 :         numCoeff << 13, 45, 28;
      28           4 :         VolumeDescriptor dd(numCoeff);
      29             : 
      30           4 :         WHEN("instantiating a FourierTransform operator")
      31           4 :         {
      32           2 :             FourierTransform fftOp(dd);
      33             : 
      34           2 :             THEN("the DataDescriptors are as expected")
      35           2 :             {
      36           2 :                 REQUIRE(fftOp.getDomainDescriptor() == dd);
      37           2 :                 REQUIRE(fftOp.getRangeDescriptor() == dd);
      38           2 :             }
      39           2 :         }
      40             : 
      41           4 :         WHEN("cloning a FourierTransform operator")
      42           4 :         {
      43           2 :             FourierTransform fftOp(dd);
      44           2 :             auto fftOpClone = fftOp.clone();
      45             : 
      46           2 :             THEN("everything matches")
      47           2 :             {
      48           2 :                 REQUIRE(fftOpClone.get() != &fftOp);
      49           2 :                 REQUIRE(*fftOpClone == fftOp);
      50           2 :             }
      51           2 :         }
      52           4 :     }
      53           4 : }
      54             : 
      55             : TEST_CASE_TEMPLATE("FourierTransform: Testing 1d", data_t, float, double)
      56           6 : {
      57           6 :     GIVEN("a volume descriptor")
      58           6 :     {
      59           6 :         IndexVector_t numCoeff{1};
      60           6 :         numCoeff << 4;
      61           6 :         VolumeDescriptor dd{numCoeff};
      62             : 
      63           6 :         WHEN("performing 1d fft transform")
      64           6 :         {
      65           6 :             using namespace std::complex_literals;
      66           6 :             FourierTransform<complex<data_t>> fftOp{dd};
      67             : 
      68           6 :             Vector_t<complex<data_t>> input{4};
      69             : 
      70             :             // TODO: somehow magically do complex conversions with eigens , operator
      71           6 :             if constexpr (std::is_same_v<data_t, float>) {
      72           3 :                 input << 0.f + 0.if, 42.f + 0.if, 42.f + 0.if, 0.f + 0.if;
      73           3 :             } else {
      74           3 :                 input << 0. + 0.i, 42. + 0.i, 42. + 0.i, 0. + 0.i;
      75           3 :             }
      76           6 :             Vector_t<complex<data_t>> expected{4};
      77             : 
      78             :             // TODO: also check other datahandlers
      79             : 
      80           6 :             THEN("the forward transformed values are correct")
      81           6 :             {
      82           2 :                 DataContainer<complex<data_t>> inputdc{dd, input, DataHandlerType::CPU};
      83           2 :                 auto output = fftOp.apply(inputdc);
      84             : 
      85           2 :                 if constexpr (std::is_same_v<data_t, float>) {
      86           1 :                     expected << 84.f + 0.if, -42.f - 42.if, 0.f + 0.if, -42.f + 42.if;
      87           1 :                 } else {
      88           1 :                     expected << 84. + 0.i, -42. - 42.i, 0. + 0.i, -42. + 42.i;
      89           1 :                 }
      90             : 
      91          10 :                 for (index_t i = 0; i < output.getSize(); ++i) {
      92           8 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
      93           8 :                 }
      94           2 :             }
      95             : 
      96           6 :             THEN("the inverse transformed values are correct")
      97           6 :             {
      98           2 :                 DataContainer<complex<data_t>> inputdc{dd, input, DataHandlerType::CPU};
      99           2 :                 auto output = fftOp.applyAdjoint(inputdc);
     100             : 
     101           2 :                 if constexpr (std::is_same_v<data_t, float>) {
     102           1 :                     expected << 21.f + 0.if, -10.5f + 10.5if, 0.f + 0.if, -10.5f - 10.5if;
     103           1 :                 } else {
     104           1 :                     expected << 21. + 0.i, -10.5 + 10.5i, 0. + 0.i, -10.5 - 10.5i;
     105           1 :                 }
     106             : 
     107          10 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     108           8 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
     109           8 :                 }
     110           2 :             }
     111             : 
     112           6 :             THEN("the forward-inverse transformed values are correct")
     113           6 :             {
     114           2 :                 DataContainer<complex<data_t>> inputdc{dd, input, DataHandlerType::CPU};
     115             : 
     116           2 :                 auto output = fftOp.applyAdjoint(fftOp.apply(inputdc));
     117             : 
     118           2 :                 if constexpr (std::is_same_v<data_t, float>) {
     119           1 :                     expected << 0.f + 0.if, 42.f + 0.if, 42.f + 0.if, 0.f + 0.if;
     120           1 :                 } else {
     121           1 :                     expected << 0. + 0.i, 42. + 0.i, 42. + 0.i, 0. + 0.i;
     122           1 :                 }
     123             : 
     124          10 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     125           8 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
     126           8 :                 }
     127           2 :             }
     128           6 :         }
     129           6 :     }
     130           6 : }
     131             : 
     132             : TEST_CASE_TEMPLATE("FourierTransform: 2d test", data_t, float, double)
     133          10 : {
     134          10 :     using namespace std::complex_literals;
     135             : 
     136          10 :     GIVEN("some data")
     137          10 :     {
     138             : 
     139          10 :         WHEN("applying the fft")
     140          10 :         {
     141           2 :             IndexVector_t size{2};
     142           2 :             size << 4, 4;
     143           2 :             VolumeDescriptor dd{size};
     144             : 
     145           2 :             FourierTransform<complex<data_t>> fftOp{dd};
     146             : 
     147           2 :             DataContainer<data_t> testdata{dd};
     148           2 :             testdata = 0.0;
     149           2 :             testdata(2, 2) = 42.0f;
     150           2 :             testdata(0, 2) = 42.0f;
     151             : 
     152           2 :             auto input = testdata.asComplex();
     153             : 
     154           2 :             auto output = fftOp.apply(input);
     155             : 
     156           2 :             Vector_t<complex<data_t>> expected{4 * 4};
     157             :             // transposed because vector!
     158           2 :             expected << 84, 0, 84, 0, -84, 0, -84, 0, 84, 0, 84, 0, -84, 0, -84, 0;
     159             : 
     160           2 :             THEN("the result is as expected")
     161           2 :             {
     162           2 :                 REQUIRE(output.getSize() == input.getSize());
     163          34 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     164          32 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
     165          32 :                 }
     166           2 :             }
     167           2 :         }
     168             : 
     169          10 :         WHEN("applying the adjoint of fft")
     170          10 :         {
     171           2 :             IndexVector_t size{2};
     172           2 :             size << 4, 4;
     173           2 :             VolumeDescriptor dd{size};
     174             : 
     175           2 :             FourierTransform<complex<data_t>> fftOp{dd};
     176             : 
     177           2 :             DataContainer<data_t> testdata{dd};
     178           2 :             testdata = 0.0;
     179           2 :             testdata(2, 2) = 42.0f;
     180           2 :             testdata(0, 2) = 42.0f;
     181             : 
     182           2 :             auto input = testdata.asComplex();
     183             : 
     184           2 :             auto output = fftOp.apply(input);
     185             : 
     186           2 :             Vector_t<complex<data_t>> expected{4 * 4};
     187             :             // transposed because vector!
     188           2 :             expected << 84, 0, 84, 0, -84, 0, -84, 0, 84, 0, 84, 0, -84, 0, -84, 0;
     189             : 
     190           2 :             THEN("the result is as expected")
     191           2 :             {
     192           2 :                 REQUIRE(output.getSize() == input.getSize());
     193          34 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     194          32 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
     195          32 :                 }
     196           2 :             }
     197           2 :         }
     198             : 
     199          10 :         WHEN("applying the fft and inverse fft")
     200          10 :         {
     201             : 
     202           2 :             IndexVector_t size{4};
     203           2 :             size << 5, 10, 15, 20;
     204           2 :             VolumeDescriptor dd{size};
     205           2 :             FourierTransform<complex<data_t>> fftOp{dd};
     206             : 
     207           2 :             auto [input, randVec] =
     208           2 :                 generateRandomContainer<complex<data_t>>(dd, DataHandlerType::CPU);
     209             : 
     210           2 :             auto mid = fftOp.apply(input);
     211           2 :             auto output = fftOp.applyAdjoint(mid);
     212             : 
     213           2 :             THEN("the back and forward fourier transform result in the original values")
     214           2 :             {
     215           2 :                 REQUIRE(output.getSize() == input.getSize());
     216       30002 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     217       30000 :                     REQUIRE_UNARY(checkApproxEq(output[i], randVec(i)));
     218       30000 :                 }
     219           2 :             }
     220           2 :         }
     221             : 
     222          10 :         WHEN("ensuring the linearity of a fft")
     223          10 :         {
     224             : 
     225           4 :             IndexVector_t size{2};
     226           4 :             size << 20, 10;
     227           4 :             VolumeDescriptor dd{size};
     228             : 
     229           4 :             auto [inputA, randVecA] =
     230           4 :                 generateRandomContainer<complex<data_t>>(dd, DataHandlerType::CPU);
     231           4 :             auto [inputB, randVecB] =
     232           4 :                 generateRandomContainer<complex<data_t>>(dd, DataHandlerType::CPU);
     233             : 
     234           4 :             FourierTransform<complex<data_t>> fftOp{dd};
     235             : 
     236           4 :             auto fftA = fftOp.apply(inputA);
     237           4 :             auto fftB = fftOp.apply(inputB);
     238             : 
     239           4 :             THEN("fft(A + B) = fft(A) + fft(B)")
     240           4 :             {
     241           2 :                 DataContainer<complex<data_t>> fftA_plus_fftB = fftA + fftB;
     242           2 :                 DataContainer<complex<data_t>> A_plus_B = inputA + inputB;
     243             : 
     244           2 :                 auto fftAB = fftOp.apply(A_plus_B);
     245             : 
     246           2 :                 REQUIRE(fftAB.getSize() == fftA_plus_fftB.getSize());
     247         402 :                 for (index_t i = 0; i < fftAB.getSize(); ++i) {
     248         400 :                     REQUIRE_UNARY(checkApproxEq(fftA_plus_fftB[i], fftAB[i]));
     249         400 :                 }
     250           2 :             }
     251             : 
     252           4 :             THEN("f(x * A) = x * f(A)")
     253           4 :             {
     254           2 :                 complex<data_t> x = 42;
     255           2 :                 DataContainer<complex<data_t>> fftA_scaled = (fftA * x);
     256             : 
     257           2 :                 auto fftAx = fftOp.apply(inputA * x);
     258             : 
     259           2 :                 REQUIRE(fftA_scaled.getSize() == fftAx.getSize());
     260         402 :                 for (index_t i = 0; i < fftA_scaled.getSize(); ++i) {
     261         400 :                     REQUIRE_UNARY(checkApproxEq(fftA_scaled[i], fftAx[i]));
     262         400 :                 }
     263           2 :             }
     264           4 :         }
     265             : 
     266             :         // TODO impulse function response
     267          10 :     }
     268          10 : }
     269             : TEST_SUITE_END();

Generated by: LCOV version 1.14