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

          Line data    Source code
       1             : /**
       2             :  * @file test_Huber.cpp
       3             :  *
       4             :  * @brief Tests for the Huber class
       5             :  *
       6             :  * @author Matthias Wieczorek - initial code
       7             :  * @author David Frank - rewrite
       8             :  * @author Tobias Lasser - modernization
       9             :  */
      10             : 
      11             : #include <doctest/doctest.h>
      12             : 
      13             : #include "Huber.h"
      14             : #include "LinearResidual.h"
      15             : #include "Identity.h"
      16             : #include "VolumeDescriptor.h"
      17             : #include "TypeCasts.hpp"
      18             : #include "testHelpers.h"
      19             : 
      20             : using namespace elsa;
      21             : using namespace doctest;
      22             : 
      23             : TYPE_TO_STRING(complex<float>);
      24             : TYPE_TO_STRING(complex<double>);
      25             : 
      26             : TEST_SUITE_BEGIN("functionals");
      27             : 
      28          22 : TEST_CASE_TEMPLATE("Huber: Testing without residual only data", TestType, float, double)
      29             : {
      30             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
      31             : 
      32          20 :     GIVEN("just data (no residual)")
      33             :     {
      34          20 :         IndexVector_t numCoeff(3);
      35          10 :         numCoeff << 7, 16, 29;
      36          20 :         VolumeDescriptor dd(numCoeff);
      37             : 
      38          10 :         real_t delta = 10;
      39             : 
      40          20 :         WHEN("instantiating")
      41             :         {
      42          20 :             Huber<TestType> func(dd, delta);
      43             : 
      44          12 :             THEN("the functional is as expected")
      45             :             {
      46           2 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
      47             : 
      48           2 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
      49           2 :                 REQUIRE_UNARY(linRes);
      50           2 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
      51           2 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
      52             :             }
      53             : 
      54          12 :             THEN("a clone behaves as expected")
      55             :             {
      56           4 :                 auto huberClone = func.clone();
      57             : 
      58           2 :                 REQUIRE_NE(huberClone.get(), &func);
      59           2 :                 REQUIRE_EQ(*huberClone, func);
      60             :             }
      61             : 
      62          20 :             Vector dataVec(dd.getNumberOfCoefficients());
      63          10 :             dataVec.setRandom();
      64             : 
      65             :             // fix the first entries to be bigger/smaller than delta
      66          10 :             dataVec[0] = delta + 1;
      67          10 :             dataVec[1] = delta + 2;
      68          10 :             dataVec[2] = delta + 3;
      69          10 :             dataVec[3] = delta - 1;
      70          10 :             dataVec[4] = delta - 2;
      71          10 :             dataVec[5] = delta - 3;
      72             : 
      73          20 :             DataContainer<TestType> x(dd, dataVec);
      74             : 
      75             :             // compute the "true" values
      76          10 :             TestType trueValue = 0;
      77          20 :             Vector trueGrad(dd.getNumberOfCoefficients());
      78       32490 :             for (index_t i = 0; i < dataVec.size(); ++i) {
      79       32480 :                 TestType value = dataVec[i];
      80       32480 :                 if (std::abs(value) <= delta) {
      81       32450 :                     trueValue += 0.5f * value * value;
      82       32450 :                     trueGrad[i] = value;
      83             :                 } else {
      84          30 :                     trueValue += delta * (std::abs(value) - 0.5f * delta);
      85          30 :                     trueGrad[i] = (value > 0) ? delta : -delta;
      86             :                 }
      87             :             }
      88             : 
      89          12 :             THEN("the evaluate works as expected")
      90             :             {
      91           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
      92             :             }
      93          12 :             THEN("the gradient works as expected")
      94             :             {
      95           2 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
      96           2 :                 REQUIRE_UNARY(checkApproxEq(func.getGradient(x), dcTrueGrad));
      97             :             }
      98          12 :             THEN("the Hessian works as expected")
      99             :             {
     100           4 :                 auto hessian = func.getHessian(x);
     101           4 :                 auto hx = hessian.apply(x);
     102        6498 :                 for (index_t i = 0; i < hx.getSize(); ++i)
     103        6496 :                     REQUIRE_UNARY(
     104             :                         checkApproxEq(hx[i], ((std::abs(dataVec[i]) <= delta) ? x[i] : 0)));
     105             :             }
     106             :         }
     107             :     }
     108          10 : }
     109             : 
     110          22 : TEST_CASE_TEMPLATE("Huber<TestType>: Testing with residual", TestType, float, double)
     111             : {
     112             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
     113             : 
     114          20 :     GIVEN("a residual with data")
     115             :     {
     116             :         // linear residual
     117          20 :         IndexVector_t numCoeff(2);
     118          10 :         numCoeff << 47, 11;
     119          20 :         VolumeDescriptor dd(numCoeff);
     120             : 
     121          20 :         Vector randomData(dd.getNumberOfCoefficients());
     122          10 :         randomData.setRandom();
     123          20 :         DataContainer<TestType> b(dd, randomData);
     124             : 
     125          20 :         Identity<TestType> A(dd);
     126             : 
     127          20 :         LinearResidual<TestType> linRes(A, b);
     128             : 
     129          10 :         real_t delta = 20;
     130             : 
     131          20 :         WHEN("instantiating")
     132             :         {
     133          20 :             Huber<TestType> func(linRes, delta);
     134             : 
     135          12 :             THEN("the functional is as expected")
     136             :             {
     137           2 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
     138             : 
     139           2 :                 auto* lRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
     140           2 :                 REQUIRE_UNARY(lRes);
     141           2 :                 REQUIRE_EQ(*lRes, linRes);
     142             :             }
     143             : 
     144          12 :             THEN("a clone behaves as expected")
     145             :             {
     146           4 :                 auto huberClone = func.clone();
     147             : 
     148           2 :                 REQUIRE_NE(huberClone.get(), &func);
     149           2 :                 REQUIRE_EQ(*huberClone, func);
     150             :             }
     151             : 
     152          20 :             Vector dataVec(dd.getNumberOfCoefficients());
     153          10 :             dataVec.setRandom();
     154          20 :             DataContainer<TestType> x(dd, dataVec);
     155             : 
     156             :             // compute the "true" values
     157          10 :             TestType trueValue = 0;
     158          20 :             Vector trueGrad(dd.getNumberOfCoefficients());
     159        5180 :             for (index_t i = 0; i < dataVec.size(); ++i) {
     160        5170 :                 TestType value = dataVec[i] - randomData[i];
     161        5170 :                 if (std::abs(value) <= delta) {
     162        5170 :                     trueValue += 0.5f * value * value;
     163        5170 :                     trueGrad[i] = value;
     164             :                 } else {
     165           0 :                     trueValue += delta * (std::abs(value) - 0.5f * delta);
     166           0 :                     trueGrad[i] = (value > 0) ? delta : -delta;
     167             :                 }
     168             :             }
     169             : 
     170          12 :             THEN("the evaluate works as expected")
     171             :             {
     172           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     173             :             }
     174             : 
     175          12 :             THEN("the gradient works as expected")
     176             :             {
     177           2 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
     178           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
     179             :             }
     180          12 :             THEN("the Hessian works as expected")
     181             :             {
     182             : 
     183           4 :                 auto hessian = func.getHessian(x);
     184           4 :                 auto hx = hessian.apply(x);
     185        1036 :                 for (index_t i = 0; i < hx.getSize(); ++i)
     186        1034 :                     REQUIRE(hx[i] == ((std::abs(dataVec[i] - randomData[i]) <= delta) ? x[i] : 0));
     187             :             }
     188             :         }
     189             :     }
     190          10 : }
     191             : 
     192             : TEST_SUITE_END();

Generated by: LCOV version 1.15