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

Generated by: LCOV version 1.14