Line data Source code
1 : /**
2 : * @file test_L1Norm.cpp
3 : *
4 : * @brief Tests for the L1Norm 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 "L1Norm.h"
15 : #include "LinearResidual.h"
16 : #include "Identity.h"
17 : #include "VolumeDescriptor.h"
18 : #include "TypeCasts.hpp"
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 : TEST_CASE_TEMPLATE("L1Norm: Testing without residual", TestType, float, double, complex<float>,
29 : complex<double>)
30 12 : {
31 12 : using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
32 :
33 12 : GIVEN("just data (no residual)")
34 12 : {
35 12 : IndexVector_t numCoeff(1);
36 12 : numCoeff << 4;
37 12 : VolumeDescriptor dd(numCoeff);
38 :
39 12 : WHEN("instantiating")
40 12 : {
41 12 : L1Norm<TestType> func(dd);
42 :
43 12 : THEN("the functional is as expected")
44 12 : {
45 4 : REQUIRE_EQ(func.getDomainDescriptor(), dd);
46 :
47 4 : auto& residual = func.getResidual();
48 4 : auto* linRes = downcast_safe<LinearResidual<TestType>>(&residual);
49 4 : REQUIRE_UNARY(linRes);
50 4 : REQUIRE_UNARY_FALSE(linRes->hasDataVector());
51 4 : REQUIRE_UNARY_FALSE(linRes->hasOperator());
52 4 : }
53 :
54 12 : THEN("a clone behaves as expected")
55 12 : {
56 4 : auto l1Clone = func.clone();
57 :
58 4 : REQUIRE_NE(l1Clone.get(), &func);
59 4 : REQUIRE_EQ(*l1Clone, func);
60 4 : }
61 :
62 12 : THEN("the evaluate, gradient and Hessian work as expected")
63 12 : {
64 4 : Vector dataVec(dd.getNumberOfCoefficients());
65 4 : dataVec << -9, -4, 0, 1;
66 4 : DataContainer<TestType> dc(dd, dataVec);
67 :
68 4 : REQUIRE(checkApproxEq(func.evaluate(dc), 14));
69 4 : REQUIRE_THROWS_AS(func.getGradient(dc), LogicError);
70 4 : REQUIRE_THROWS_AS(func.getHessian(dc), LogicError);
71 4 : }
72 12 : }
73 12 : }
74 12 : }
75 :
76 : TEST_CASE_TEMPLATE("L1Norm: Testing with residual", TestType, float, double, complex<float>,
77 : complex<double>)
78 12 : {
79 12 : using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
80 :
81 12 : GIVEN("a residual with data")
82 12 : {
83 12 : IndexVector_t numCoeff(1);
84 12 : numCoeff << 4;
85 12 : VolumeDescriptor dd(numCoeff);
86 :
87 12 : Vector randomData(dd.getNumberOfCoefficients());
88 12 : randomData.setRandom();
89 12 : DataContainer<TestType> dc(dd, randomData);
90 :
91 12 : Identity<TestType> idOp(dd);
92 :
93 12 : LinearResidual<TestType> linRes(idOp, dc);
94 :
95 12 : WHEN("instantiating")
96 12 : {
97 12 : L1Norm<TestType> func(linRes);
98 :
99 12 : THEN("the functional is as expected")
100 12 : {
101 4 : REQUIRE_EQ(func.getDomainDescriptor(), dd);
102 :
103 4 : auto& residual = func.getResidual();
104 4 : auto* lRes = downcast_safe<LinearResidual<TestType>>(&residual);
105 4 : REQUIRE_UNARY(lRes);
106 4 : REQUIRE_EQ(*lRes, linRes);
107 4 : }
108 :
109 12 : THEN("a clone behaves as expected")
110 12 : {
111 4 : auto l1Clone = func.clone();
112 :
113 4 : REQUIRE_NE(l1Clone.get(), &func);
114 4 : REQUIRE_EQ(*l1Clone, func);
115 4 : }
116 :
117 12 : THEN("the evaluate, gradient and Hessian work as expected")
118 12 : {
119 4 : Vector dataVec(dd.getNumberOfCoefficients());
120 4 : dataVec.setRandom();
121 4 : DataContainer<TestType> x(dd, dataVec);
122 :
123 4 : REQUIRE_UNARY(
124 4 : checkApproxEq(func.evaluate(x), (dataVec - randomData).template lpNorm<1>()));
125 4 : REQUIRE_THROWS_AS(func.getGradient(x), LogicError);
126 4 : REQUIRE_THROWS_AS(func.getHessian(x), LogicError);
127 4 : }
128 12 : }
129 12 : }
130 12 : }
131 :
132 : TEST_SUITE_END();
|