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

          Line data    Source code
       1             : /**
       2             :  * @file test_Pseudohuber.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 "testHelpers.h"
      14             : #include "PseudoHuber.h"
      15             : #include "LinearResidual.h"
      16             : #include "LinearOperator.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             : TEST_SUITE_BEGIN("functionals");
      26             : 
      27          18 : TEST_CASE_TEMPLATE("PseudoHuber: Testing without residual", TestType, float, double)
      28             : {
      29             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
      30             : 
      31          12 :     GIVEN("just data (no residual)")
      32             :     {
      33          12 :         IndexVector_t numCoeff(2);
      34           6 :         numCoeff << 13, 34;
      35          12 :         VolumeDescriptor dd(numCoeff);
      36             : 
      37           6 :         real_t delta = 2;
      38             : 
      39          12 :         WHEN("instantiating")
      40             :         {
      41          12 :             PseudoHuber<TestType> func(dd, delta);
      42             : 
      43           8 :             THEN("the functional is as expected")
      44             :             {
      45           2 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
      46             : 
      47           2 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
      48           2 :                 REQUIRE_UNARY(linRes);
      49           2 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
      50           2 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
      51             :             }
      52             : 
      53           8 :             THEN("a clone behaves as expected")
      54             :             {
      55           4 :                 auto phClone = func.clone();
      56             : 
      57           2 :                 REQUIRE_NE(phClone.get(), &func);
      58           2 :                 REQUIRE_EQ(*phClone, func);
      59             :             }
      60             : 
      61           8 :             THEN("the evaluate, gradient and Hessian work as expected")
      62             :             {
      63           4 :                 Vector dataVec(dd.getNumberOfCoefficients());
      64           2 :                 dataVec.setRandom();
      65           4 :                 DataContainer<TestType> x(dd, dataVec);
      66             : 
      67             :                 // compute the "true" values
      68           2 :                 TestType trueValue = 0;
      69           4 :                 Vector trueGrad(dd.getNumberOfCoefficients());
      70           4 :                 Vector trueScale(dd.getNumberOfCoefficients());
      71         886 :                 for (index_t i = 0; i < dataVec.size(); ++i) {
      72         884 :                     TestType temp = dataVec[i] / delta;
      73         884 :                     TestType tempSq = temp * temp;
      74         884 :                     TestType sqrtOnePTempSq = std::sqrt(static_cast<TestType>(1.0) + tempSq);
      75         884 :                     trueValue += delta * delta * (sqrtOnePTempSq - static_cast<TestType>(1.0));
      76         884 :                     trueGrad[i] = dataVec[i] / sqrtOnePTempSq;
      77         884 :                     trueScale[i] = (sqrtOnePTempSq - tempSq / sqrtOnePTempSq)
      78         884 :                                    / (static_cast<TestType>(1.0) + tempSq);
      79             :                 }
      80             : 
      81           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
      82           4 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
      83           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
      84             : 
      85           2 :                 DataContainer<TestType> dcTrueScale(dd, trueScale);
      86           2 :                 REQUIRE_EQ(func.getHessian(x), leaf(Scaling(dd, dcTrueScale)));
      87             :             }
      88             :         }
      89             :     }
      90           6 : }
      91             : 
      92          18 : TEST_CASE_TEMPLATE("PseudoHuber: Testing with residual", TestType, float, double)
      93             : {
      94             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
      95             : 
      96          12 :     GIVEN("a residual with data")
      97             :     {
      98             :         // linear residual
      99          12 :         IndexVector_t numCoeff(3);
     100           6 :         numCoeff << 3, 7, 11;
     101          12 :         VolumeDescriptor dd(numCoeff);
     102             : 
     103          12 :         Vector randomData(dd.getNumberOfCoefficients());
     104           6 :         randomData.setRandom();
     105          12 :         DataContainer<TestType> b(dd, randomData);
     106             : 
     107          12 :         Identity<TestType> A(dd);
     108             : 
     109          12 :         LinearResidual<TestType> linRes(A, b);
     110             : 
     111           6 :         real_t delta = static_cast<real_t>(1.5);
     112             : 
     113          12 :         WHEN("instantiating")
     114             :         {
     115          12 :             PseudoHuber<TestType> func(linRes, delta);
     116             : 
     117           8 :             THEN("the functional is as expected")
     118             :             {
     119           2 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
     120             : 
     121           2 :                 auto* lRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
     122           2 :                 REQUIRE_UNARY(lRes);
     123           2 :                 REQUIRE_EQ(*lRes, linRes);
     124             :             }
     125             : 
     126           8 :             THEN("a clone behaves as expected")
     127             :             {
     128           4 :                 auto phClone = func.clone();
     129             : 
     130           2 :                 REQUIRE_NE(phClone.get(), &func);
     131           2 :                 REQUIRE_EQ(*phClone, func);
     132             :             }
     133             : 
     134           8 :             THEN("the evaluate, gradient and Hessian work as expected")
     135             :             {
     136           4 :                 Vector dataVec(dd.getNumberOfCoefficients());
     137           2 :                 dataVec.setRandom();
     138           4 :                 DataContainer<TestType> x(dd, dataVec);
     139             : 
     140             :                 // compute the "true" values
     141           2 :                 TestType trueValue = 0;
     142           4 :                 Vector trueGrad(dd.getNumberOfCoefficients());
     143           4 :                 Vector trueScale(dd.getNumberOfCoefficients());
     144         464 :                 for (index_t i = 0; i < dataVec.size(); ++i) {
     145         462 :                     TestType temp = (dataVec[i] - randomData[i]) / delta;
     146         462 :                     TestType tempSq = temp * temp;
     147         462 :                     TestType sqrtOnePTempSq = std::sqrt(static_cast<TestType>(1.0) + tempSq);
     148         462 :                     trueValue += delta * delta * (sqrtOnePTempSq - static_cast<TestType>(1.0));
     149         462 :                     trueGrad[i] = (dataVec[i] - randomData[i]) / sqrtOnePTempSq;
     150         462 :                     trueScale[i] = (sqrtOnePTempSq - tempSq / sqrtOnePTempSq)
     151         462 :                                    / (static_cast<TestType>(1.0) + tempSq);
     152             :                 }
     153             : 
     154           2 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     155           4 :                 DataContainer<TestType> dcTrueGrad(dd, trueGrad);
     156           2 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), dcTrueGrad));
     157             : 
     158           4 :                 auto hessian = func.getHessian(x);
     159           4 :                 auto hx = hessian.apply(x);
     160         464 :                 for (index_t i = 0; i < hx.getSize(); ++i)
     161         462 :                     REQUIRE_UNARY(checkApproxEq(hx[i], dataVec[i] * trueScale[i]));
     162             :             }
     163             :         }
     164             :     }
     165           6 : }
     166             : 
     167             : TEST_SUITE_END();

Generated by: LCOV version 1.15