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

          Line data    Source code
       1             : /**
       2             :  * @file test_Quadric.cpp
       3             :  *
       4             :  * @brief Tests for the Quadric class
       5             :  *
       6             :  * @author Matthias Wieczorek - initial code
       7             :  * @author Maximilian Hornung - modularization
       8             :  * @author David Frank - rewrite
       9             :  * @author Tobias Lasser - modernization
      10             :  */
      11             : 
      12             : #include <doctest/doctest.h>
      13             : 
      14             : #include "testHelpers.h"
      15             : #include "Quadric.h"
      16             : #include "Identity.h"
      17             : #include "Scaling.h"
      18             : #include "VolumeDescriptor.h"
      19             : #include "TypeCasts.hpp"
      20             : 
      21             : using namespace elsa;
      22             : using namespace doctest;
      23             : 
      24           4 : TYPE_TO_STRING(complex<float>);
      25           4 : TYPE_TO_STRING(complex<double>);
      26             : 
      27             : TEST_SUITE_BEGIN("functionals");
      28             : 
      29          68 : TEST_CASE_TEMPLATE("Quadric: Testing without residual", TestType, float, double, complex<float>,
      30             :                    complex<double>)
      31             : {
      32             :     using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
      33             : 
      34          60 :     GIVEN("no operator and no data")
      35             :     {
      36          24 :         IndexVector_t numCoeff(3);
      37          12 :         numCoeff << 13, 11, 7;
      38          24 :         VolumeDescriptor dd(numCoeff);
      39             : 
      40          24 :         WHEN("instantiating")
      41             :         {
      42          24 :             Quadric<TestType> func(dd);
      43             : 
      44          16 :             THEN("the functional is as expected")
      45             :             {
      46           4 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
      47             : 
      48           4 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
      49           4 :                 REQUIRE_UNARY(linRes);
      50           4 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
      51           4 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
      52             : 
      53           4 :                 const auto& gradExpr = func.getGradientExpression();
      54           4 :                 REQUIRE_UNARY_FALSE(gradExpr.hasDataVector());
      55           4 :                 REQUIRE_UNARY_FALSE(gradExpr.hasOperator());
      56             :             }
      57             : 
      58          16 :             THEN("a clone behaves as expected")
      59             :             {
      60           8 :                 auto qClone = func.clone();
      61             : 
      62           4 :                 REQUIRE_NE(qClone.get(), &func);
      63           4 :                 REQUIRE_EQ(*qClone, func);
      64             :             }
      65             : 
      66          16 :             THEN("the evaluate, gradient and Hessian work as expected")
      67             :             {
      68           8 :                 Vector dataVec(dd.getNumberOfCoefficients());
      69           4 :                 dataVec.setRandom();
      70           4 :                 DataContainer<TestType> x(dd, dataVec);
      71             : 
      72           4 :                 TestType trueValue = static_cast<TestType>(0.5) * x.squaredL2Norm();
      73           4 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
      74           4 :                 REQUIRE_UNARY(isApprox(func.getGradient(x), x));
      75           4 :                 REQUIRE_EQ(func.getHessian(x), leaf(Identity<TestType>(dd)));
      76             :             }
      77             :         }
      78             :     }
      79             : 
      80          60 :     GIVEN("an operator but no data")
      81             :     {
      82          24 :         IndexVector_t numCoeff(3);
      83          12 :         numCoeff << 13, 11, 7;
      84          24 :         VolumeDescriptor dd(numCoeff);
      85             : 
      86          24 :         Scaling scalingOp(dd, static_cast<TestType>(3.0));
      87             : 
      88          24 :         WHEN("instantiating")
      89             :         {
      90          24 :             Quadric<TestType> func(scalingOp);
      91             : 
      92          16 :             THEN("the functional is as expected")
      93             :             {
      94           4 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
      95             : 
      96           4 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
      97           4 :                 REQUIRE_UNARY(linRes);
      98           4 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
      99           4 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
     100             : 
     101           4 :                 const auto& gradExpr = func.getGradientExpression();
     102           4 :                 REQUIRE_UNARY_FALSE(gradExpr.hasDataVector());
     103           4 :                 REQUIRE_EQ(gradExpr.getOperator(), scalingOp);
     104             :             }
     105             : 
     106          16 :             THEN("a clone behaves as expected")
     107             :             {
     108           8 :                 auto qClone = func.clone();
     109             : 
     110           4 :                 REQUIRE_NE(qClone.get(), &func);
     111           4 :                 REQUIRE_EQ(*qClone, func);
     112             :             }
     113             : 
     114          16 :             THEN("the evaluate, gradient and Hessian work as expected")
     115             :             {
     116           8 :                 Vector dataVec(dd.getNumberOfCoefficients());
     117           4 :                 dataVec.setRandom();
     118           4 :                 DataContainer<TestType> x(dd, dataVec);
     119             : 
     120           2 :                 TestType trueValue =
     121           4 :                     static_cast<TestType>(0.5) * scalingOp.getScaleFactor() * x.squaredL2Norm();
     122           4 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     123           4 :                 REQUIRE_EQ(func.getGradient(x), scalingOp.getScaleFactor() * x);
     124           4 :                 REQUIRE_EQ(func.getHessian(x), leaf(scalingOp));
     125             :             }
     126             :         }
     127             :     }
     128             : 
     129          60 :     GIVEN("data but no operator")
     130             :     {
     131          24 :         IndexVector_t numCoeff(3);
     132          12 :         numCoeff << 13, 11, 7;
     133          24 :         VolumeDescriptor dd(numCoeff);
     134             : 
     135          24 :         Vector randomData(dd.getNumberOfCoefficients());
     136          12 :         randomData.setRandom();
     137          24 :         DataContainer<TestType> dc(dd, randomData);
     138             : 
     139          24 :         WHEN("instantiating")
     140             :         {
     141          24 :             Quadric<TestType> func(dc);
     142             : 
     143          16 :             THEN("the functional is as expected")
     144             :             {
     145           4 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
     146             : 
     147           4 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
     148           4 :                 REQUIRE_UNARY(linRes);
     149           4 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
     150           4 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
     151             : 
     152           4 :                 const auto& gradExpr = func.getGradientExpression();
     153           4 :                 REQUIRE_EQ(gradExpr.getDataVector(), dc);
     154           4 :                 REQUIRE_UNARY_FALSE(gradExpr.hasOperator());
     155             :             }
     156             : 
     157          16 :             THEN("a clone behaves as expected")
     158             :             {
     159           8 :                 auto qClone = func.clone();
     160             : 
     161           4 :                 REQUIRE_NE(qClone.get(), &func);
     162           4 :                 REQUIRE_EQ(*qClone, func);
     163             :             }
     164             : 
     165          16 :             THEN("the evaluate, gradient and Hessian work as expected")
     166             :             {
     167           8 :                 Vector dataVec(dd.getNumberOfCoefficients());
     168           4 :                 dataVec.setRandom();
     169           4 :                 DataContainer<TestType> x(dd, dataVec);
     170             : 
     171           4 :                 TestType trueValue = static_cast<TestType>(0.5) * x.squaredL2Norm() - x.dot(dc);
     172           4 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     173           4 :                 REQUIRE_EQ(func.getGradient(x), x - dc);
     174           4 :                 REQUIRE_EQ(func.getHessian(x), leaf(Identity<TestType>(dd)));
     175             :             }
     176             :         }
     177             :     }
     178             : 
     179          60 :     GIVEN("an operator and data")
     180             :     {
     181          24 :         IndexVector_t numCoeff(3);
     182          12 :         numCoeff << 13, 11, 7;
     183          24 :         VolumeDescriptor dd(numCoeff);
     184             : 
     185          24 :         Identity<TestType> idOp(dd);
     186             : 
     187          24 :         Vector randomData(dd.getNumberOfCoefficients());
     188          12 :         randomData.setRandom();
     189          24 :         DataContainer<TestType> dc(dd, randomData);
     190             : 
     191          24 :         WHEN("instantiating")
     192             :         {
     193          24 :             Quadric<TestType> func(idOp, dc);
     194             : 
     195          16 :             THEN("the functional is as expected")
     196             :             {
     197           4 :                 REQUIRE_EQ(func.getDomainDescriptor(), dd);
     198             : 
     199           4 :                 auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
     200           4 :                 REQUIRE_UNARY(linRes);
     201           4 :                 REQUIRE_UNARY_FALSE(linRes->hasDataVector());
     202           4 :                 REQUIRE_UNARY_FALSE(linRes->hasOperator());
     203             : 
     204           4 :                 const auto& gradExpr = func.getGradientExpression();
     205           4 :                 REQUIRE(isApprox(gradExpr.getDataVector(), dc));
     206           4 :                 REQUIRE_EQ(gradExpr.getOperator(), idOp);
     207             :             }
     208             : 
     209          16 :             THEN("a clone behaves as expected")
     210             :             {
     211           8 :                 auto qClone = func.clone();
     212             : 
     213           4 :                 REQUIRE_NE(qClone.get(), &func);
     214           4 :                 REQUIRE_EQ(*qClone, func);
     215             :             }
     216             : 
     217          16 :             THEN("the evaluate, gradient and Hessian work as expected")
     218             :             {
     219           8 :                 Vector dataVec(dd.getNumberOfCoefficients());
     220           4 :                 dataVec.setRandom();
     221           8 :                 DataContainer<TestType> x(dd, dataVec);
     222             : 
     223           4 :                 TestType trueValue = static_cast<TestType>(0.5) * x.dot(x) - x.dot(dc);
     224           4 :                 REQUIRE_UNARY(checkApproxEq(func.evaluate(x), trueValue));
     225           4 :                 DataContainer<TestType> grad(dd, (dataVec - randomData).eval());
     226           4 :                 REQUIRE_EQ(func.getGradient(x), grad);
     227           4 :                 REQUIRE_EQ(func.getHessian(x), leaf(idOp));
     228             :             }
     229             :         }
     230             :     }
     231          48 : }
     232             : 
     233             : TEST_SUITE_END();

Generated by: LCOV version 1.15