LCOV - code coverage report
Current view: top level - operators/tests - test_FourierTransform.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 122 122 100.0 %
Date: 2022-02-28 03:37:41 Functions: 15 15 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          22 : TEST_CASE_TEMPLATE("FourierTransform: Testing construction", data_t, float, double)
      23             : {
      24           8 :     GIVEN("a descriptor")
      25             :     {
      26           8 :         IndexVector_t numCoeff(3);
      27           4 :         numCoeff << 13, 45, 28;
      28           8 :         VolumeDescriptor dd(numCoeff);
      29             : 
      30           6 :         WHEN("instantiating a FourierTransform operator")
      31             :         {
      32           4 :             FourierTransform fftOp(dd);
      33             : 
      34           4 :             THEN("the DataDescriptors are as expected")
      35             :             {
      36           2 :                 REQUIRE(fftOp.getDomainDescriptor() == dd);
      37           2 :                 REQUIRE(fftOp.getRangeDescriptor() == dd);
      38             :             }
      39             :         }
      40             : 
      41           6 :         WHEN("cloning a FourierTransform operator")
      42             :         {
      43           4 :             FourierTransform fftOp(dd);
      44           4 :             auto fftOpClone = fftOp.clone();
      45             : 
      46           4 :             THEN("everything matches")
      47             :             {
      48           2 :                 REQUIRE(fftOpClone.get() != &fftOp);
      49           2 :                 REQUIRE(*fftOpClone == fftOp);
      50             :             }
      51             :         }
      52             :     }
      53           4 : }
      54             : 
      55          24 : TEST_CASE_TEMPLATE("FourierTransform: Testing 1d", data_t, float, double)
      56             : {
      57          12 :     GIVEN("a volume descriptor")
      58             :     {
      59          12 :         IndexVector_t numCoeff{1};
      60           6 :         numCoeff << 4;
      61          12 :         VolumeDescriptor dd{numCoeff};
      62             : 
      63          12 :         WHEN("performing 1d fft transform")
      64             :         {
      65             :             using namespace std::complex_literals;
      66          12 :             FourierTransform<complex<data_t>> fftOp{dd};
      67             : 
      68          12 :             Vector_t<complex<data_t>> input{4};
      69             : 
      70             :             // TODO: somehow magically do complex conversions with eigens , operator
      71             :             if constexpr (std::is_same_v<data_t, float>) {
      72          27 :                 input << 0.f + 0.if, 42.f + 0.if, 42.f + 0.if, 0.f + 0.if;
      73             :             } else {
      74          27 :                 input << 0. + 0.i, 42. + 0.i, 42. + 0.i, 0. + 0.i;
      75             :             }
      76          12 :             Vector_t<complex<data_t>> expected{4};
      77             : 
      78             :             // TODO: also check other datahandlers
      79             : 
      80           8 :             THEN("the forward transformed values are correct")
      81             :             {
      82           4 :                 DataContainer<complex<data_t>> inputdc{dd, input, DataHandlerType::CPU};
      83           4 :                 auto output = fftOp.apply(inputdc);
      84             : 
      85             :                 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             :                 } else {
      88           1 :                     expected << 84. + 0.i, -42. - 42.i, 0. + 0.i, -42. + 42.i;
      89             :                 }
      90             : 
      91          10 :                 for (index_t i = 0; i < output.getSize(); ++i) {
      92           8 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
      93             :                 }
      94             :             }
      95             : 
      96           8 :             THEN("the inverse transformed values are correct")
      97             :             {
      98           4 :                 DataContainer<complex<data_t>> inputdc{dd, input, DataHandlerType::CPU};
      99           4 :                 auto output = fftOp.applyAdjoint(inputdc);
     100             : 
     101             :                 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             :                 } else {
     104           1 :                     expected << 21. + 0.i, -10.5 + 10.5i, 0. + 0.i, -10.5 - 10.5i;
     105             :                 }
     106             : 
     107          10 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     108           8 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
     109             :                 }
     110             :             }
     111             : 
     112           8 :             THEN("the forward-inverse transformed values are correct")
     113             :             {
     114           4 :                 DataContainer<complex<data_t>> inputdc{dd, input, DataHandlerType::CPU};
     115             : 
     116           4 :                 auto output = fftOp.applyAdjoint(fftOp.apply(inputdc));
     117             : 
     118             :                 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             :                 } else {
     121           1 :                     expected << 0. + 0.i, 42. + 0.i, 42. + 0.i, 0. + 0.i;
     122             :                 }
     123             : 
     124          10 :                 for (index_t i = 0; i < output.getSize(); ++i) {
     125           8 :                     REQUIRE_UNARY(checkApproxEq(output[i], expected(i)));
     126             :                 }
     127             :             }
     128             :         }
     129             :     }
     130           6 : }
     131             : 
     132          28 : TEST_CASE_TEMPLATE("FourierTransform: 2d test", data_t, float, double)
     133             : {
     134             :     using namespace std::complex_literals;
     135             : 
     136          20 :     GIVEN("some data")
     137             :     {
     138             : 
     139          12 :         WHEN("applying the fft")
     140             :         {
     141           4 :             IndexVector_t size{2};
     142           2 :             size << 4, 4;
     143           4 :             VolumeDescriptor dd{size};
     144             : 
     145           4 :             FourierTransform<complex<data_t>> fftOp{dd};
     146             : 
     147           4 :             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           4 :             auto input = testdata.asComplex();
     153             : 
     154           4 :             auto output = fftOp.apply(input);
     155             : 
     156           4 :             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           4 :             THEN("the result is as expected")
     161             :             {
     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             :                 }
     166             :             }
     167             :         }
     168             : 
     169          12 :         WHEN("applying the adjoint of fft")
     170             :         {
     171           4 :             IndexVector_t size{2};
     172           2 :             size << 4, 4;
     173           4 :             VolumeDescriptor dd{size};
     174             : 
     175           4 :             FourierTransform<complex<data_t>> fftOp{dd};
     176             : 
     177           4 :             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           4 :             auto input = testdata.asComplex();
     183             : 
     184           4 :             auto output = fftOp.apply(input);
     185             : 
     186           4 :             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           4 :             THEN("the result is as expected")
     191             :             {
     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             :                 }
     196             :             }
     197             :         }
     198             : 
     199          12 :         WHEN("applying the fft and inverse fft")
     200             :         {
     201             : 
     202           4 :             IndexVector_t size{4};
     203           2 :             size << 5, 10, 15, 20;
     204           4 :             VolumeDescriptor dd{size};
     205           4 :             FourierTransform<complex<data_t>> fftOp{dd};
     206             : 
     207           4 :             auto [input, randVec] =
     208             :                 generateRandomContainer<complex<data_t>>(dd, DataHandlerType::CPU);
     209             : 
     210           4 :             auto mid = fftOp.apply(input);
     211           4 :             auto output = fftOp.applyAdjoint(mid);
     212             : 
     213           4 :             THEN("the back and forward fourier transform result in the original values")
     214             :             {
     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             :                 }
     219             :             }
     220             :         }
     221             : 
     222          14 :         WHEN("ensuring the linearity of a fft")
     223             :         {
     224             : 
     225           8 :             IndexVector_t size{2};
     226           4 :             size << 20, 10;
     227           8 :             VolumeDescriptor dd{size};
     228             : 
     229           8 :             auto [inputA, randVecA] =
     230             :                 generateRandomContainer<complex<data_t>>(dd, DataHandlerType::CPU);
     231           8 :             auto [inputB, randVecB] =
     232             :                 generateRandomContainer<complex<data_t>>(dd, DataHandlerType::CPU);
     233             : 
     234           8 :             FourierTransform<complex<data_t>> fftOp{dd};
     235             : 
     236           8 :             auto fftA = fftOp.apply(inputA);
     237           8 :             auto fftB = fftOp.apply(inputB);
     238             : 
     239           6 :             THEN("fft(A + B) = fft(A) + fft(B)")
     240             :             {
     241           4 :                 DataContainer<complex<data_t>> fftA_plus_fftB = fftA + fftB;
     242           4 :                 DataContainer<complex<data_t>> A_plus_B = inputA + inputB;
     243             : 
     244           4 :                 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             :                 }
     250             :             }
     251             : 
     252           6 :             THEN("f(x * A) = x * f(A)")
     253             :             {
     254           2 :                 complex<data_t> x = 42;
     255           4 :                 DataContainer<complex<data_t>> fftA_scaled = (fftA * x);
     256             : 
     257           4 :                 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             :                 }
     263             :             }
     264             :         }
     265             : 
     266             :         // TODO impulse function response
     267             :     }
     268          10 : }
     269             : TEST_SUITE_END();

Generated by: LCOV version 1.15