LCOV - code coverage report
Current view: top level - functionals/tests - test_TransmissionLogLikelihood.cpp (source / functions) Hit Total Coverage
Test: test_coverage.info.cleaned Lines: 158 160 98.8 %
Date: 2022-02-28 03:37:41 Functions: 10 10 100.0 %

          Line data    Source code
       1             : /**
       2             :  * @file test_TransmissionLogLikelihood.cpp
       3             :  *
       4             :  * @brief Tests for the TransmissionLogLikelihood class
       5             :  *
       6             :  * @author Matthias Wieczorek - initial code
       7             :  * @author David Frank - rewrite
       8             :  * @author Tobias Lasser - rewrite
       9             :  */
      10             : 
      11             : #include <doctest/doctest.h>
      12             : 
      13             : #include <cmath>
      14             : #include "testHelpers.h"
      15             : #include "TransmissionLogLikelihood.h"
      16             : #include "LinearResidual.h"
      17             : #include "Scaling.h"
      18             : #include "Identity.h"
      19             : #include "VolumeDescriptor.h"
      20             : #include "TypeCasts.hpp"
      21             : 
      22             : using namespace elsa;
      23             : using namespace doctest;
      24             : 
      25             : TYPE_TO_STRING(complex<float>);
      26             : TYPE_TO_STRING(complex<double>);
      27             : 
      28             : TEST_SUITE_BEGIN("functionals");
      29             : 
      30          22 : TEST_CASE_TEMPLATE("TransmissionLogLikelihood: Testing with only data no residual", TestType, float,
      31             :                    double)
      32             : {
      33             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
      34             : 
      35          20 :     GIVEN("just data (no residual)")
      36             :     {
      37          20 :         IndexVector_t numCoeff(3);
      38          10 :         numCoeff << 3, 7, 13;
      39          20 :         VolumeDescriptor dd(numCoeff);
      40             : 
      41          20 :         Vector y(dd.getNumberOfCoefficients());
      42          10 :         y.setRandom();
      43          20 :         DataContainer<TestType> dcY(dd, y);
      44             : 
      45          20 :         Vector b(dd.getNumberOfCoefficients());
      46          10 :         b.setRandom();
      47             :         // ensure b has positive values (due to log)
      48        2740 :         for (index_t i = 0; i < dd.getNumberOfCoefficients(); ++i) {
      49        2730 :             if (b[i] < 0)
      50        1300 :                 b[i] *= -1;
      51        2730 :             if (b[i] == 0)
      52           0 :                 b[i] += 1;
      53             :         }
      54          20 :         DataContainer<TestType> dcB(dd, b);
      55             : 
      56          16 :         WHEN("instantiating without r")
      57             :         {
      58          12 :             TransmissionLogLikelihood func(dd, dcY, dcB);
      59             : 
      60           8 :             THEN("the functional is as expected")
      61             :             {
      62           2 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
      63             : 
      64           2 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
      65           2 :                 REQUIRE_UNARY(linRes);
      66           2 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
      67           2 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
      68             :             }
      69             : 
      70           8 :             THEN("a clone behaves as expected")
      71             :             {
      72           4 :                 auto tmllClone = func.clone();
      73             : 
      74           2 :                 REQUIRE_NE(tmllClone.get(), &func);
      75           2 :                 REQUIRE_EQ(*tmllClone, func);
      76             :             }
      77             : 
      78           8 :             THEN("the evaluate, gradient and Hessian work as expected")
      79             :             {
      80           4 :                 Vector dataVec(dd.getNumberOfCoefficients());
      81           2 :                 dataVec.setRandom();
      82           4 :                 DataContainer<TestType> x(dd, dataVec);
      83             : 
      84             :                 // compute the "true" values
      85           2 :                 TestType trueValue = 0;
      86           4 :                 Vector trueGrad(dd.getNumberOfCoefficients());
      87           4 :                 Vector trueScale(dd.getNumberOfCoefficients());
      88         548 :                 for (index_t i = 0; i < dataVec.size(); ++i) {
      89         546 :                     auto temp = b[i] * std::exp(-dataVec[i]);
      90         546 :                     trueValue += temp - y[i] * std::log(temp);
      91         546 :                     trueGrad[i] = y[i] - temp;
      92         546 :                     trueScale[i] = temp;
      93             :                 }
      94             : 
      95           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
      96           4 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
      97           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
      98             : 
      99           2 :                 DataContainer<TestType> dcTrueScale(dd, trueScale);
     100           2 :                 REQUIRE_EQ(func.getHessian(x), leaf(Scaling(dd, dcTrueScale)));
     101             :             }
     102             :         }
     103             : 
     104          14 :         WHEN("instantiating with r")
     105             :         {
     106           8 :             Vector r(dd.getNumberOfCoefficients());
     107           4 :             r.setRandom();
     108             :             // ensure non-negative values
     109        1096 :             for (index_t i = 0; i < r.size(); ++i)
     110        1092 :                 if (r[i] < 0)
     111         572 :                     r[i] *= -1;
     112           8 :             DataContainer<TestType> dcR(dd, r);
     113             : 
     114           8 :             TransmissionLogLikelihood func(dd, dcY, dcB, dcR);
     115             : 
     116           6 :             THEN("a clone behaves as expected")
     117             :             {
     118           4 :                 auto tmllClone = func.clone();
     119             : 
     120           2 :                 REQUIRE_NE(tmllClone.get(), &func);
     121           2 :                 REQUIRE_EQ(*tmllClone, func);
     122             :             }
     123             : 
     124           6 :             THEN("the evaluate, gradient and Hessian work as expected")
     125             :             {
     126           4 :                 Vector dataVec(dd.getNumberOfCoefficients());
     127           2 :                 dataVec.setRandom();
     128           4 :                 DataContainer<TestType> x(dd, dataVec);
     129             : 
     130             :                 // compute the true values
     131           2 :                 TestType trueValue = 0;
     132           4 :                 Vector trueGrad(dd.getNumberOfCoefficients());
     133           4 :                 Vector trueScale(dd.getNumberOfCoefficients());
     134         548 :                 for (index_t i = 0; i < dataVec.size(); ++i) {
     135         546 :                     auto temp = b[i] * std::exp(-dataVec[i]);
     136         546 :                     auto tempR = temp + r[i];
     137         546 :                     trueValue += tempR - y[i] * std::log(tempR);
     138         546 :                     trueGrad[i] = (y[i] * temp) / tempR - temp;
     139         546 :                     trueScale[i] = temp + (r[i] * y[i] * temp) / (tempR * tempR);
     140             :                 }
     141             : 
     142           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     143           4 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
     144           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
     145             : 
     146           2 :                 DataContainer<TestType> dcTrueScale(dd, trueScale);
     147           2 :                 REQUIRE_EQ(func.getHessian(x), leaf(Scaling(dd, dcTrueScale)));
     148             :             }
     149             :         }
     150             :     }
     151          10 : }
     152             : 
     153          22 : TEST_CASE_TEMPLATE("TransmissionLogLikelihood: Testing with residual", TestType, float, double)
     154             : {
     155             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
     156             : 
     157          20 :     GIVEN("a residual with data")
     158             :     {
     159          20 :         IndexVector_t numCoeff(2);
     160          10 :         numCoeff << 13, 17;
     161          20 :         VolumeDescriptor dd(numCoeff);
     162             : 
     163          20 :         Vector resData(dd.getNumberOfCoefficients());
     164          10 :         resData.setRandom();
     165          20 :         DataContainer<TestType> dcResData(dd, resData);
     166          20 :         Identity<TestType> idOp(dd);
     167          20 :         LinearResidual<TestType> linRes(idOp, dcResData);
     168             : 
     169          20 :         Vector y(dd.getNumberOfCoefficients());
     170          10 :         y.setRandom();
     171          20 :         DataContainer<TestType> dcY(dd, y);
     172             : 
     173          20 :         Vector b(dd.getNumberOfCoefficients());
     174          10 :         b.setRandom();
     175             :         // ensure b has positive values (due to log)
     176        2220 :         for (index_t i = 0; i < dd.getNumberOfCoefficients(); ++i) {
     177        2210 :             if (b[i] < 0)
     178        1130 :                 b[i] *= -1;
     179        2210 :             if (b[i] == 0)
     180           0 :                 b[i] += 1;
     181             :         }
     182          20 :         DataContainer<TestType> dcB(dd, b);
     183             : 
     184          16 :         WHEN("instantiating without r")
     185             :         {
     186          12 :             TransmissionLogLikelihood func(linRes, dcY, dcB);
     187             : 
     188           8 :             THEN("the functional is as expected")
     189             :             {
     190           2 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
     191             : 
     192           2 :                 auto* lRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
     193           2 :                 REQUIRE_UNARY(lRes);
     194           2 :                 REQUIRE_EQ(*lRes, linRes);
     195             :             }
     196             : 
     197           8 :             THEN("a clone behaves as expected")
     198             :             {
     199           4 :                 auto tmllClone = func.clone();
     200             : 
     201           2 :                 REQUIRE_NE(tmllClone.get(), &func);
     202           2 :                 REQUIRE_EQ(*tmllClone, func);
     203             :             }
     204             : 
     205           8 :             THEN("the evaluate, gradient and Hessian work as expected")
     206             :             {
     207           4 :                 Vector dataVec(dd.getNumberOfCoefficients());
     208           2 :                 dataVec.setRandom();
     209           4 :                 DataContainer<TestType> x(dd, dataVec);
     210             : 
     211             :                 // compute the "true" values
     212           2 :                 TestType trueValue = 0;
     213           4 :                 Vector trueGrad(dd.getNumberOfCoefficients());
     214           4 :                 Vector trueScale(dd.getNumberOfCoefficients());
     215         444 :                 for (index_t i = 0; i < dataVec.size(); ++i) {
     216         442 :                     auto temp = b[i] * std::exp(-dataVec[i] + resData[i]);
     217         442 :                     trueValue += temp - y[i] * std::log(temp);
     218         442 :                     trueGrad[i] = y[i] - temp;
     219         442 :                     trueScale[i] = temp;
     220             :                 }
     221             : 
     222           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     223           4 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
     224           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
     225             : 
     226           4 :                 auto hessian = func.getHessian(x);
     227           4 :                 auto hx = hessian.apply(x);
     228         444 :                 for (index_t i = 0; i < hx.getSize(); ++i)
     229         442 :                     REQUIRE_EQ(hx[i], dataVec[i] * trueScale[i]);
     230             :             }
     231             :         }
     232             : 
     233          14 :         WHEN("instantiating with r")
     234             :         {
     235           8 :             Vector r(dd.getNumberOfCoefficients());
     236           4 :             r.setRandom();
     237             :             // ensure non-negative values
     238         888 :             for (index_t i = 0; i < r.size(); ++i)
     239         884 :                 if (r[i] < 0)
     240         432 :                     r[i] *= -1;
     241           8 :             DataContainer<TestType> dcR(dd, r);
     242             : 
     243           8 :             TransmissionLogLikelihood func(linRes, dcY, dcB, dcR);
     244             : 
     245           6 :             THEN("a clone behaves as expected")
     246             :             {
     247           4 :                 auto tmllClone = func.clone();
     248             : 
     249           2 :                 REQUIRE_NE(tmllClone.get(), &func);
     250           2 :                 REQUIRE_EQ(*tmllClone, func);
     251             :             }
     252             : 
     253           6 :             THEN("the evaluate, gradient and Hessian work as expected")
     254             :             {
     255           4 :                 Vector dataVec(dd.getNumberOfCoefficients());
     256           2 :                 dataVec.setRandom();
     257           4 :                 DataContainer<TestType> x(dd, dataVec);
     258             : 
     259             :                 // compute the true values
     260           2 :                 TestType trueValue = 0;
     261           4 :                 Vector trueGrad(dd.getNumberOfCoefficients());
     262           4 :                 Vector trueScale(dd.getNumberOfCoefficients());
     263         444 :                 for (index_t i = 0; i < dataVec.size(); ++i) {
     264         442 :                     auto temp = b[i] * std::exp(-dataVec[i] + resData[i]);
     265         442 :                     auto tempR = temp + r[i];
     266         442 :                     trueValue += tempR - y[i] * std::log(tempR);
     267         442 :                     trueGrad[i] = (y[i] * temp) / tempR - temp;
     268         442 :                     trueScale[i] = temp + (r[i] * y[i] * temp) / (tempR * tempR);
     269             :                 }
     270             : 
     271           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     272           4 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
     273           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
     274             : 
     275           4 :                 auto hessian = func.getHessian(x);
     276           4 :                 auto hx = hessian.apply(x);
     277         444 :                 for (index_t i = 0; i < hx.getSize(); ++i)
     278         442 :                     REQUIRE_UNARY(checkApproxEq(hx[i], dataVec[i] * trueScale[i]));
     279             :             }
     280             :         }
     281             :     }
     282          10 : }
     283             : 
     284             : TEST_SUITE_END();

Generated by: LCOV version 1.15