Line data Source code
1 : /** 2 : * @file test_WeightedL1Norm.cpp 3 : * 4 : * @brief Tests for the WeightedL1Norm class 5 : * 6 : * @author Andi Braimllari 7 : */ 8 : 9 : #include "WeightedL1Norm.h" 10 : #include "LinearResidual.h" 11 : #include "Identity.h" 12 : #include "VolumeDescriptor.h" 13 : #include "testHelpers.h" 14 : #include "TypeCasts.hpp" 15 : 16 : #include <doctest/doctest.h> 17 : 18 : using namespace elsa; 19 : using namespace doctest; 20 : 21 : TEST_SUITE_BEGIN("functionals"); 22 : 23 : TEST_CASE_TEMPLATE("WeightedL1Norm: Testing the weighted, l1 norm functional", TestType, float, 24 : double) 25 18 : { 26 18 : using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>; 27 : 28 18 : GIVEN("a linear residual and weights with a non-positive element") 29 18 : { 30 2 : IndexVector_t numCoeff(2); 31 2 : numCoeff << 25, 27; 32 2 : VolumeDescriptor dd(numCoeff); 33 : 34 2 : Vector randomData(dd.getNumberOfCoefficients()); 35 2 : randomData.setRandom(); 36 2 : DataContainer<TestType> b(dd, randomData); 37 : 38 2 : Identity<TestType> A(dd); 39 : 40 2 : LinearResidual<TestType> linRes(A, b); 41 : 42 : // scaling operator 43 2 : DataContainer<TestType> scaleFactors(dd); 44 2 : scaleFactors = 1; 45 2 : scaleFactors[3] = -8; 46 : 47 2 : WHEN("instantiating an WeightedL1Norm object") 48 2 : { 49 2 : THEN("an InvalidArgumentError is thrown") 50 2 : { 51 2 : REQUIRE_THROWS_AS(WeightedL1Norm<TestType>{scaleFactors}, InvalidArgumentError); 52 2 : REQUIRE_THROWS_AS(WeightedL1Norm<TestType>(linRes, scaleFactors), 53 2 : InvalidArgumentError); 54 2 : } 55 2 : } 56 2 : } 57 : 58 18 : GIVEN("weights of value 1 and no residual") 59 18 : { 60 8 : IndexVector_t numCoeff(2); 61 8 : numCoeff << 7, 17; 62 8 : VolumeDescriptor dd(numCoeff); 63 : 64 8 : DataContainer<TestType> scaleFactors(dd); 65 8 : scaleFactors = 1; 66 : 67 8 : WHEN("instantiating an WeightedL1Norm object") 68 8 : { 69 8 : WeightedL1Norm<TestType> func(scaleFactors); 70 : 71 8 : THEN("the functional is as expected") 72 8 : { 73 2 : REQUIRE(func.getDomainDescriptor() == dd); 74 2 : REQUIRE(func.getWeightingOperator() == scaleFactors); 75 : 76 2 : const auto* linRes = 77 2 : dynamic_cast<const LinearResidual<TestType>*>(&func.getResidual()); 78 2 : REQUIRE(linRes); 79 2 : REQUIRE(linRes->hasOperator() == false); 80 2 : REQUIRE(linRes->hasDataVector() == false); 81 2 : } 82 : 83 8 : THEN("a clone behaves as expected") 84 8 : { 85 2 : auto wl1Clone = func.clone(); 86 : 87 2 : REQUIRE(wl1Clone.get() != &func); 88 2 : REQUIRE(*wl1Clone == func); 89 2 : } 90 : 91 8 : Vector dataVec(dd.getNumberOfCoefficients()); 92 8 : dataVec.setRandom(); 93 8 : DataContainer<TestType> x(dd, dataVec); 94 : 95 8 : THEN("the evaluate works as expected") 96 8 : { 97 2 : REQUIRE(func.evaluate(x) == Approx(scaleFactors.dot(cwiseAbs(x)))); 98 2 : } 99 : 100 8 : THEN("the gradient and Hessian throw as expected") 101 8 : { 102 2 : REQUIRE_THROWS_AS(func.getGradient(x), LogicError); 103 2 : REQUIRE_THROWS_AS(func.getHessian(x), LogicError); 104 2 : } 105 8 : } 106 8 : } 107 : 108 18 : GIVEN("different sizes of the linear residual and weighting operator") 109 18 : { 110 : // linear residual 111 2 : IndexVector_t numCoeff(2); 112 2 : numCoeff << 47, 11; 113 2 : VolumeDescriptor dd(numCoeff); 114 : 115 : // linear residual 116 2 : IndexVector_t otherNumCoeff(3); 117 2 : otherNumCoeff << 15, 24, 4; 118 2 : VolumeDescriptor otherDD(otherNumCoeff); 119 : 120 2 : Vector randomData(dd.getNumberOfCoefficients()); 121 2 : randomData.setRandom(); 122 2 : DataContainer<TestType> b(dd, randomData); 123 : 124 2 : Identity<TestType> A(dd); 125 : 126 2 : LinearResidual<TestType> linRes(A, b); 127 : 128 : // scaling operator 129 2 : DataContainer<TestType> scaleFactors(otherDD); 130 2 : scaleFactors = 1; 131 : 132 2 : WHEN("instantiating an WeightedL1Norm object") 133 2 : { 134 2 : THEN("an InvalidArgumentError is thrown") 135 2 : { 136 2 : REQUIRE_THROWS_AS(WeightedL1Norm<TestType>(linRes, scaleFactors), 137 2 : InvalidArgumentError); 138 2 : } 139 2 : } 140 2 : } 141 : 142 18 : GIVEN("weights of value 1 and a linear residual") 143 18 : { 144 : // linear residual 145 6 : IndexVector_t numCoeff(2); 146 6 : numCoeff << 47, 11; 147 6 : VolumeDescriptor dd(numCoeff); 148 : 149 6 : Vector randomData(dd.getNumberOfCoefficients()); 150 6 : randomData.setRandom(); 151 6 : DataContainer<TestType> b(dd, randomData); 152 : 153 6 : Identity<TestType> A(dd); 154 : 155 6 : LinearResidual<TestType> linRes(A, b); 156 : 157 : // scaling operator 158 6 : DataContainer<TestType> scaleFactors(dd); 159 6 : scaleFactors = 1; 160 : 161 6 : WHEN("instantiating an WeightedL1Norm object") 162 6 : { 163 6 : WeightedL1Norm<TestType> func(linRes, scaleFactors); 164 : 165 6 : THEN("the functional is as expected") 166 6 : { 167 2 : REQUIRE(func.getDomainDescriptor() == dd); 168 2 : REQUIRE(func.getWeightingOperator() == scaleFactors); 169 : 170 2 : const auto* lRes = 171 2 : dynamic_cast<const LinearResidual<TestType>*>(&func.getResidual()); 172 2 : REQUIRE(lRes); 173 2 : REQUIRE(*lRes == linRes); 174 2 : } 175 : 176 6 : THEN("a clone behaves as expected") 177 6 : { 178 2 : auto wl1Clone = func.clone(); 179 : 180 2 : REQUIRE(wl1Clone.get() != &func); 181 2 : REQUIRE(*wl1Clone == func); 182 2 : } 183 : 184 6 : THEN("the evaluate, gradient and Hessian work was expected") 185 6 : { 186 2 : Vector dataVec(dd.getNumberOfCoefficients()); 187 2 : dataVec.setRandom(); 188 2 : DataContainer<TestType> x(dd, dataVec); 189 : 190 2 : REQUIRE(func.evaluate(x) == Approx(scaleFactors.dot(cwiseAbs(x - b)))); 191 2 : REQUIRE_THROWS_AS(func.getGradient(x), LogicError); 192 2 : REQUIRE_THROWS_AS(func.getHessian(x), LogicError); 193 2 : } 194 6 : } 195 6 : } 196 18 : } 197 : 198 : TEST_SUITE_END();