Line data Source code
1 : /** 2 : * @file test_GradientDescent.cpp 3 : * 4 : * @brief Tests for the GradientDescent class 5 : * 6 : * @author Tobias Lasser - initial code 7 : */ 8 : 9 : #include "doctest/doctest.h" 10 : 11 : #include "GradientDescent.h" 12 : #include "WLSProblem.h" 13 : #include "Problem.h" 14 : #include "Identity.h" 15 : #include "LinearResidual.h" 16 : #include "L2NormPow2.h" 17 : #include "Logger.h" 18 : #include "VolumeDescriptor.h" 19 : #include "testHelpers.h" 20 : #include <iostream> 21 : 22 : using namespace elsa; 23 : using namespace doctest; 24 : 25 : TEST_SUITE_BEGIN("solvers"); 26 : 27 : TYPE_TO_STRING(GradientDescent<float>); 28 : TYPE_TO_STRING(GradientDescent<double>); 29 : 30 : template <template <typename> typename T, typename data_t> 31 : constexpr data_t return_data_t(const T<data_t>&); 32 : 33 : TEST_CASE_TEMPLATE("GradientDescent: Solving a simple linear problem", TestType, 34 : GradientDescent<float>, GradientDescent<double>) 35 4 : { 36 : // Set seed for Eigen Matrices! 37 4 : srand((unsigned int) 666); 38 : 39 4 : using data_t = decltype(return_data_t(std::declval<TestType>())); 40 : // eliminate the timing info from console for the tests 41 4 : Logger::setLevel(Logger::LogLevel::OFF); 42 : 43 4 : GIVEN("a linear problem") 44 4 : { 45 4 : IndexVector_t numCoeff(2); 46 4 : numCoeff << 13, 24; 47 4 : VolumeDescriptor dd(numCoeff); 48 : 49 4 : Eigen::Matrix<data_t, -1, 1> bVec(dd.getNumberOfCoefficients()); 50 4 : bVec.setRandom(); 51 4 : DataContainer dcB(dd, bVec); 52 : 53 4 : Identity<data_t> idOp(dd); 54 : 55 4 : WLSProblem prob(idOp, dcB); 56 4 : data_t epsilon = std::numeric_limits<data_t>::epsilon(); 57 : 58 4 : WHEN("setting up a Gradient Descent solver with fixed step size") 59 4 : { 60 2 : TestType solver{prob, 0.5}; 61 : 62 2 : THEN("the clone works correctly") 63 2 : { 64 2 : auto gdClone = solver.clone(); 65 : 66 2 : REQUIRE_NE(gdClone.get(), &solver); 67 2 : REQUIRE_EQ(*gdClone, solver); 68 : 69 2 : AND_THEN("it works as expected") 70 2 : { 71 2 : auto solution = solver.solve(100); 72 : 73 2 : DataContainer<data_t> resultsDifference = solution - dcB; 74 : 75 : // should have converged for the given number of iterations 76 2 : REQUIRE_LE(resultsDifference.squaredL2Norm(), 77 2 : epsilon * epsilon * dcB.squaredL2Norm()); 78 2 : } 79 2 : } 80 2 : } 81 : 82 4 : WHEN("setting up a Gradient Descent solver with lipschitz step size") 83 4 : { 84 2 : TestType solver{prob}; 85 : 86 2 : THEN("the clone works correctly") 87 2 : { 88 2 : auto gdClone = solver.clone(); 89 : 90 2 : REQUIRE_NE(gdClone.get(), &solver); 91 2 : REQUIRE_EQ(*gdClone, solver); 92 : 93 2 : AND_THEN("it works as expected") 94 2 : { 95 2 : auto solution = solver.solve(10); 96 : 97 2 : DataContainer<data_t> resultsDifference = solution - dcB; 98 : 99 : // should have converged for the given number of iterations 100 2 : REQUIRE_UNARY(checkApproxEq(resultsDifference.squaredL2Norm(), 101 2 : epsilon * epsilon * dcB.squaredL2Norm())); 102 2 : } 103 2 : } 104 2 : } 105 4 : } 106 4 : } 107 : 108 : TEST_CASE_TEMPLATE("GradientDescent: Solving a Tikhonov problem", TestType, GradientDescent<float>, 109 : GradientDescent<double>) 110 4 : { 111 : // Set seed for Eigen Matrices! 112 4 : srand((unsigned int) 666); 113 : 114 4 : using data_t = decltype(return_data_t(std::declval<TestType>())); 115 : // eliminate the timing info from console for the tests 116 4 : Logger::setLevel(Logger::LogLevel::OFF); 117 : 118 4 : GIVEN("a Tikhonov problem") 119 4 : { 120 4 : IndexVector_t numCoeff(2); 121 4 : numCoeff << 13, 24; 122 4 : VolumeDescriptor dd(numCoeff); 123 : 124 4 : Eigen::Matrix<data_t, -1, 1> bVec(dd.getNumberOfCoefficients()); 125 4 : bVec.setRandom(); 126 4 : DataContainer dcB(dd, bVec); 127 : 128 4 : Identity<data_t> idOp(dd); 129 4 : LinearResidual<data_t> linRes(idOp, dcB); 130 4 : L2NormPow2<data_t> func(linRes); 131 : 132 : // the regularization term 133 4 : L2NormPow2<data_t> regFunc(dd); 134 4 : auto lambda = static_cast<data_t>(0.1f); 135 4 : RegularizationTerm<data_t> regTerm(lambda, regFunc); 136 4 : Problem prob(func, regTerm); 137 : 138 4 : WHEN("setting up a Gradient Descent solver with fixed step size") 139 4 : { 140 4 : TestType solver{prob, 0.5}; 141 : 142 4 : THEN("the clone works correctly") 143 4 : { 144 2 : auto gdClone = solver.clone(); 145 : 146 2 : REQUIRE_NE(gdClone.get(), &solver); 147 2 : REQUIRE_EQ(*gdClone, solver); 148 2 : } 149 4 : THEN("it works as expected") 150 4 : { 151 2 : auto solution = solver.solve(10); 152 : 153 2 : DataContainer<data_t> resultsDifference = solution - dcB; 154 : 155 : // should have converged for the given number of iterations 156 : // does not converge to the optimal solution because of the regularization term 157 : // Therefore, just check to fixed value 158 2 : REQUIRE_UNARY(checkApproxEq(resultsDifference.squaredL2Norm(), 0.85f)); 159 2 : } 160 4 : } 161 4 : } 162 4 : } 163 : 164 : TEST_SUITE_END();