LCOV - code coverage report
Current view: top level - operators/tests - test_Dictionary.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 152 152 100.0 %
Date: 2022-02-28 03:37:41 Functions: 20 20 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file test_Dictionary.cpp
       3             :  *
       4             :  * @brief Tests for Dictionary class
       5             :  *
       6             :  * @author Jonas Buerger - main code
       7             :  */
       8             : 
       9             : #include "doctest/doctest.h"
      10             : #include "Dictionary.h"
      11             : #include "IdenticalBlocksDescriptor.h"
      12             : #include "VolumeDescriptor.h"
      13             : #include "testHelpers.h"
      14             : 
      15             : using namespace elsa;
      16             : using namespace doctest;
      17             : 
      18             : TEST_SUITE_BEGIN("core");
      19             : 
      20          36 : TEST_CASE_TEMPLATE("Constructing a Dictionary operator ", data_t, float, double)
      21             : {
      22          18 :     GIVEN("a descriptor for a signal and the number of atoms")
      23             :     {
      24          12 :         VolumeDescriptor dd({5});
      25           6 :         index_t nAtoms(10);
      26             : 
      27          10 :         WHEN("instantiating an Dictionary operator")
      28             :         {
      29           8 :             Dictionary dictOp(dd, nAtoms);
      30             : 
      31           6 :             THEN("the DataDescriptors are as expected")
      32             :             {
      33           4 :                 VolumeDescriptor representationDescriptor({nAtoms});
      34           2 :                 REQUIRE_EQ(dictOp.getDomainDescriptor(), representationDescriptor);
      35           2 :                 REQUIRE_EQ(dictOp.getRangeDescriptor(), dd);
      36             :             }
      37             : 
      38           6 :             AND_THEN("the atoms are normalized")
      39             :             {
      40          22 :                 for (int i = 0; i < dictOp.getNumberOfAtoms(); ++i) {
      41          20 :                     REQUIRE_EQ(dictOp.getAtom(i).l2Norm(), Approx(1));
      42             :                 }
      43             :             }
      44             :         }
      45             : 
      46           8 :         WHEN("cloning a Dictionary operator")
      47             :         {
      48           4 :             Dictionary dictOp(dd, nAtoms);
      49           4 :             auto dictOpClone = dictOp.clone();
      50             : 
      51           4 :             THEN("everything matches")
      52             :             {
      53           2 :                 REQUIRE_NE(dictOpClone.get(), &dictOp);
      54           2 :                 REQUIRE_EQ(*dictOpClone, dictOp);
      55             :             }
      56             :         }
      57             :     }
      58             : 
      59          16 :     GIVEN("some initial data")
      60             :     {
      61           8 :         VolumeDescriptor dd({5});
      62           4 :         index_t nAtoms(10);
      63           8 :         IdenticalBlocksDescriptor ibd(nAtoms, dd);
      64           8 :         auto randomDictionary = generateRandomMatrix<data_t>(ibd.getNumberOfCoefficients());
      65           8 :         DataContainer<data_t> dict(ibd, randomDictionary);
      66             : 
      67           8 :         WHEN("instantiating an Dictionary operator")
      68             :         {
      69           8 :             Dictionary dictOp(dict);
      70             : 
      71           6 :             THEN("the DataDescriptors are as expected")
      72             :             {
      73           4 :                 VolumeDescriptor representationDescriptor({nAtoms});
      74           2 :                 REQUIRE_EQ(dictOp.getDomainDescriptor(), representationDescriptor);
      75           2 :                 REQUIRE_EQ(dictOp.getRangeDescriptor(), dd);
      76             :             }
      77             : 
      78           6 :             AND_THEN("the atoms are normalized")
      79             :             {
      80          22 :                 for (int i = 0; i < dictOp.getNumberOfAtoms(); ++i) {
      81          20 :                     REQUIRE_EQ(dictOp.getAtom(i).l2Norm(), Approx(1));
      82             :                 }
      83             :             }
      84             :         }
      85             :     }
      86             : 
      87          14 :     GIVEN("some invalid initial data")
      88             :     {
      89           4 :         VolumeDescriptor dd({5});
      90           4 :         DataContainer<data_t> invalidDict(dd);
      91             : 
      92           4 :         WHEN("instantiating an Dictionary operator")
      93             :         {
      94           4 :             THEN("an exception is thrown")
      95             :             {
      96           4 :                 REQUIRE_THROWS_AS(Dictionary{invalidDict}, InvalidArgumentError);
      97             :             }
      98             :         }
      99             :     }
     100          12 : }
     101             : 
     102          38 : TEST_CASE_TEMPLATE("Accessing Dictionary atoms ", data_t, float, double)
     103             : {
     104          28 :     GIVEN("some dictionary operator")
     105             :     {
     106          28 :         VolumeDescriptor dd({5});
     107          14 :         index_t nAtoms(10);
     108          28 :         IdenticalBlocksDescriptor ibd(nAtoms, dd);
     109          28 :         auto randomDictionary = generateRandomMatrix<data_t>(ibd.getNumberOfCoefficients());
     110          28 :         DataContainer<data_t> dict(ibd, randomDictionary);
     111             : 
     112             :         // normalize the atoms beforehand so we can compare
     113         154 :         for (int i = 0; i < nAtoms; ++i) {
     114         280 :             auto block = dict.getBlock(i);
     115         140 :             block /= block.l2Norm();
     116             :         }
     117          28 :         Dictionary dictOp(dict);
     118             : 
     119          16 :         WHEN("accessing an atom")
     120             :         {
     121           2 :             index_t i = 4;
     122           4 :             DataContainer<data_t> atom = dictOp.getAtom(i);
     123           2 :             THEN("the data is correct") { REQUIRE_EQ(atom, dict.getBlock(i)); }
     124             :         }
     125             : 
     126          16 :         WHEN("accessing an atom from a const dictionary reference")
     127             :         {
     128           2 :             index_t i = 4;
     129           2 :             const Dictionary<data_t>& constDictOp(dictOp);
     130           4 :             auto atom = constDictOp.getAtom(i);
     131           2 :             THEN("the data is correct") { REQUIRE_EQ(atom, dict.getBlock(i)); }
     132             :         }
     133             : 
     134          16 :         WHEN("accessing an atom with an invalid index")
     135             :         {
     136           2 :             index_t i = 42;
     137           4 :             THEN("an exception is thrown")
     138             :             {
     139           4 :                 REQUIRE_THROWS_AS(dictOp.getAtom(i), InvalidArgumentError);
     140             :             }
     141             :         }
     142             : 
     143          16 :         WHEN("updating an atom")
     144             :         {
     145           2 :             index_t i = 4;
     146           4 :             auto randomData = generateRandomMatrix<data_t>(dd.getNumberOfCoefficients());
     147           4 :             DataContainer<data_t> newAtom(dd, randomData);
     148             : 
     149           2 :             dictOp.updateAtom(i, newAtom);
     150           4 :             THEN("the data is correct (and normalized)")
     151             :             {
     152           2 :                 REQUIRE_EQ(dictOp.getAtom(i), (newAtom / newAtom.l2Norm()));
     153             :             }
     154             :         }
     155             : 
     156          16 :         WHEN("updating an atom with itself")
     157             :         {
     158             :             // this test makes sure that a normalized atom doesn't get normalized again
     159             : 
     160           2 :             index_t i = 4;
     161           4 :             DataContainer<data_t> newAtom = dictOp.getAtom(i);
     162             : 
     163           2 :             dictOp.updateAtom(i, newAtom);
     164           2 :             THEN("the data is identical") { REQUIRE_EQ(dictOp.getAtom(i), newAtom); }
     165             :         }
     166             : 
     167          16 :         WHEN("updating an atom with an invalid index")
     168             :         {
     169           2 :             index_t i = 42;
     170           4 :             DataContainer<data_t> dummyAtom(dd);
     171           4 :             THEN("an exception is thrown")
     172             :             {
     173           4 :                 REQUIRE_THROWS_AS(dictOp.updateAtom(i, dummyAtom), InvalidArgumentError);
     174             :             }
     175             :         }
     176             : 
     177          16 :         WHEN("updating an atom with invalid data")
     178             :         {
     179           2 :             index_t i = 4;
     180           4 :             VolumeDescriptor invalidDesc({dd.getNumberOfCoefficients() + 1});
     181           4 :             DataContainer<data_t> invalid(invalidDesc);
     182           4 :             THEN("an exception is thrown")
     183             :             {
     184           4 :                 REQUIRE_THROWS_AS(dictOp.updateAtom(i, invalid), InvalidArgumentError);
     185             :             }
     186             :         }
     187             :     }
     188          14 : }
     189             : 
     190          26 : TEST_CASE_TEMPLATE("Getting the support of a dictionary ", data_t, float, double)
     191             : {
     192             : 
     193           4 :     GIVEN("some dictionary and a support vector")
     194             :     {
     195           4 :         VolumeDescriptor dd({5});
     196           2 :         const index_t nAtoms(10);
     197           4 :         IdenticalBlocksDescriptor ibd(nAtoms, dd);
     198           4 :         auto randomDictionary = generateRandomMatrix<data_t>(ibd.getNumberOfCoefficients());
     199           4 :         DataContainer<data_t> dict(ibd, randomDictionary);
     200           4 :         Dictionary dictOp(dict);
     201             : 
     202           4 :         IndexVector_t support(3);
     203           2 :         support << 1, 5, 7;
     204             : 
     205           4 :         WHEN("getting the support of the dictionary")
     206             :         {
     207           4 :             auto purgedDict = dictOp.getSupportedDictionary(support);
     208             : 
     209           4 :             THEN("the data is correct")
     210             :             {
     211           8 :                 for (index_t i = 0; i < purgedDict.getNumberOfAtoms(); ++i) {
     212           6 :                     REQUIRE_EQ(purgedDict.getAtom(i), dictOp.getAtom(support[i]));
     213             :                 }
     214             :             }
     215             :         }
     216             :     }
     217           2 : }
     218             : 
     219          32 : TEST_CASE_TEMPLATE("Using the Dictionary ", data_t, float, double)
     220             : {
     221             : 
     222          16 :     GIVEN("some dictionary")
     223             :     {
     224          16 :         VolumeDescriptor dd({2});
     225           8 :         const index_t nAtoms(4);
     226          16 :         IdenticalBlocksDescriptor ibd(nAtoms, dd);
     227          16 :         Eigen::Matrix<data_t, Eigen::Dynamic, 1> dictData(ibd.getNumberOfCoefficients());
     228           8 :         dictData << 1, 2, 3, 4, 5, 6, 7, 8;
     229          16 :         DataContainer<data_t> dict(ibd, dictData);
     230             :         /*  1,3,5,7
     231             :             2,4,6,8 */
     232          16 :         Dictionary dictOp(dict);
     233             : 
     234             :         // construct a eigen matrix corresponding to the dictionary so we can check the results
     235           8 :         Eigen::Map<Eigen::Matrix<data_t, Eigen::Dynamic, nAtoms>> matDictData(
     236             :             dictData.data(), dd.getNumberOfCoefficients(), nAtoms);
     237          40 :         for (index_t i = 0; i < matDictData.cols(); ++i) {
     238          32 :             matDictData.col(i).normalize();
     239             :         }
     240             : 
     241          10 :         WHEN("applying the dictionary to a representation vector")
     242             :         {
     243           4 :             VolumeDescriptor representationDescriptor({nAtoms});
     244           2 :             Eigen::Matrix<data_t, Eigen::Dynamic, 1> inputData(
     245           2 :                 representationDescriptor.getNumberOfCoefficients());
     246           2 :             inputData << 2, 3, 4, 5;
     247           4 :             DataContainer input(representationDescriptor, inputData);
     248           4 :             auto output = dictOp.apply(input);
     249             : 
     250           4 :             THEN("the result is as expected")
     251             :             {
     252           4 :                 Eigen::Matrix<data_t, Eigen::Dynamic, 1> expected(dd.getNumberOfCoefficients());
     253           2 :                 expected = matDictData * inputData;
     254           2 :                 DataContainer expectedOutput(dd, expected);
     255           2 :                 REQUIRE_UNARY(isApprox(expectedOutput, output));
     256             :             }
     257             :         }
     258             : 
     259          10 :         WHEN("applying the adjoint dictionary to a matching vector")
     260             :         {
     261           4 :             Eigen::Matrix<data_t, Eigen::Dynamic, 1> inputData(dd.getNumberOfCoefficients());
     262           2 :             inputData << 2, 3;
     263           4 :             DataContainer input(dd, inputData);
     264           4 :             auto output = dictOp.applyAdjoint(input);
     265             : 
     266           4 :             THEN("the result is as expected")
     267             :             {
     268           4 :                 VolumeDescriptor expectedDescriptor({nAtoms});
     269           2 :                 Eigen::Matrix<data_t, Eigen::Dynamic, 1> expected(
     270           2 :                     expectedDescriptor.getNumberOfCoefficients());
     271           2 :                 expected = matDictData.transpose() * inputData;
     272           2 :                 DataContainer expectedOutput(expectedDescriptor, expected);
     273           2 :                 REQUIRE_UNARY(isApprox(expectedOutput, output));
     274             :             }
     275             :         }
     276             : 
     277          10 :         WHEN("applying the dictionary to a non-matching vector")
     278             :         {
     279           4 :             VolumeDescriptor invalidDesc({nAtoms + 1});
     280           4 :             DataContainer<data_t> invalid(invalidDesc);
     281           4 :             THEN("an exception is thrown")
     282             :             {
     283           4 :                 REQUIRE_THROWS_AS(dictOp.apply(invalid), InvalidArgumentError);
     284             :             }
     285             :         }
     286             : 
     287          10 :         WHEN("applying the adjoint dictionary to a non-matching vector")
     288             :         {
     289           4 :             VolumeDescriptor invalidDesc({dd.getNumberOfCoefficients() + 1});
     290           4 :             DataContainer<data_t> invalid(invalidDesc);
     291           4 :             THEN("an exception is thrown")
     292             :             {
     293           4 :                 REQUIRE_THROWS_AS(dictOp.applyAdjoint(invalid), InvalidArgumentError);
     294             :             }
     295             :         }
     296             :     }
     297           8 : }
     298             : TEST_SUITE_END();

Generated by: LCOV version 1.15