Line data Source code
1 : /**
2 : * @file test_WeightedL2NormPow2.cpp
3 : *
4 : * @brief Tests for the WeightedL2NormPow2 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 "WeightedL2NormPow2.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("WeightedL2NormPow2: Testing without residual", TestType, float, double,
29 : complex<float>, complex<double>)
30 20 : {
31 20 : using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
32 20 : using Scalar = GetFloatingPointType_t<TestType>;
33 :
34 20 : GIVEN("just data (no residual)")
35 20 : {
36 20 : IndexVector_t numCoeff(2);
37 20 : numCoeff << 7, 17;
38 20 : VolumeDescriptor dd(numCoeff);
39 :
40 20 : Vector scalingData(dd.getNumberOfCoefficients());
41 20 : scalingData.setRandom();
42 20 : DataContainer<TestType> scaleFactors(dd, scalingData);
43 :
44 20 : Scaling<TestType> scalingOp(dd, scaleFactors);
45 :
46 20 : WHEN("instantiating")
47 20 : {
48 20 : WeightedL2NormPow2<TestType> func(scalingOp);
49 :
50 20 : THEN("the functional is as expected")
51 20 : {
52 4 : REQUIRE_EQ(func.getDomainDescriptor(), dd);
53 4 : REQUIRE_EQ(func.getWeightingOperator(), scalingOp);
54 :
55 4 : auto* linRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
56 4 : REQUIRE_UNARY(linRes);
57 4 : REQUIRE_UNARY_FALSE(linRes->hasOperator());
58 4 : REQUIRE_UNARY_FALSE(linRes->hasDataVector());
59 4 : }
60 :
61 20 : THEN("a clone behaves as expected")
62 20 : {
63 4 : auto wl2Clone = func.clone();
64 :
65 4 : REQUIRE_NE(wl2Clone.get(), &func);
66 4 : REQUIRE_EQ(*wl2Clone, func);
67 4 : }
68 :
69 20 : Vector dataVec(dd.getNumberOfCoefficients());
70 20 : dataVec.setRandom();
71 20 : DataContainer<TestType> x(dd, dataVec);
72 :
73 20 : Vector Wx = scalingData.array() * dataVec.array();
74 :
75 20 : THEN("the evaluate works as expected")
76 20 : {
77 : // TODO: with complex numbers this for some reason doesn't work, the result is
78 : // always the negation of the expected
79 4 : if constexpr (std::is_floating_point_v<TestType>)
80 4 : REQUIRE_UNARY(checkApproxEq(func.evaluate(x),
81 4 : static_cast<Scalar>(0.5) * Wx.dot(dataVec)));
82 4 : }
83 :
84 20 : THEN("the gradient works as expected")
85 20 : {
86 4 : DataContainer<TestType> dcWx(dd, Wx);
87 4 : REQUIRE_UNARY(isApprox(func.getGradient(x), dcWx));
88 4 : }
89 :
90 20 : THEN("the Hessian works as expected")
91 20 : {
92 4 : REQUIRE_EQ(func.getHessian(x), leaf(scalingOp));
93 4 : }
94 20 : }
95 20 : }
96 20 : }
97 :
98 : TEST_CASE_TEMPLATE("WeightedL2NormPow2: Testing with residual", TestType, float, double,
99 : complex<float>, complex<double>)
100 20 : {
101 20 : using Vector = Eigen::Matrix<TestType, Eigen::Dynamic, 1>;
102 20 : using Scalar = GetFloatingPointType_t<TestType>;
103 :
104 20 : GIVEN("a residual with data")
105 20 : {
106 : // linear residual
107 20 : IndexVector_t numCoeff(2);
108 20 : numCoeff << 47, 11;
109 20 : VolumeDescriptor dd(numCoeff);
110 :
111 20 : Vector randomData(dd.getNumberOfCoefficients());
112 20 : randomData.setRandom();
113 20 : DataContainer<TestType> b(dd, randomData);
114 :
115 20 : Identity<TestType> A(dd);
116 :
117 20 : LinearResidual linRes(A, b);
118 :
119 : // scaling operator
120 20 : Vector scalingData(dd.getNumberOfCoefficients());
121 20 : scalingData.setRandom();
122 20 : DataContainer<TestType> scaleFactors(dd, scalingData);
123 :
124 20 : Scaling scalingOp(dd, scaleFactors);
125 :
126 20 : WHEN("instantiating")
127 20 : {
128 20 : WeightedL2NormPow2 func(linRes, scalingOp);
129 :
130 20 : THEN("the functional is as expected")
131 20 : {
132 4 : REQUIRE_EQ(func.getDomainDescriptor(), dd);
133 4 : REQUIRE_EQ(func.getWeightingOperator(), scalingOp);
134 :
135 4 : auto* lRes = downcast_safe<LinearResidual<TestType>>(&func.getResidual());
136 4 : REQUIRE_UNARY(lRes);
137 4 : REQUIRE_EQ(*lRes, linRes);
138 4 : }
139 :
140 20 : THEN("a clone behaves as expected")
141 20 : {
142 4 : auto wl2Clone = func.clone();
143 :
144 4 : REQUIRE_NE(wl2Clone.get(), &func);
145 4 : REQUIRE_EQ(*wl2Clone, func);
146 4 : }
147 :
148 20 : Vector dataVec(dd.getNumberOfCoefficients());
149 20 : dataVec.setRandom();
150 20 : DataContainer<TestType> x(dd, dataVec);
151 :
152 20 : Vector WRx = scalingData.array() * (dataVec - randomData).array();
153 :
154 20 : THEN("the evaluate works was expected")
155 20 : {
156 : // TODO: with complex numbers this for some reason doesn't work, the result is
157 : // always the negation of the expected
158 4 : if constexpr (std::is_floating_point_v<TestType>)
159 4 : REQUIRE_UNARY(
160 4 : checkApproxEq(func.evaluate(x),
161 4 : static_cast<Scalar>(0.5) * WRx.dot(dataVec - randomData)));
162 4 : }
163 :
164 20 : THEN("the gradient works was expected")
165 20 : {
166 4 : DataContainer<TestType> dcWRx(dd, WRx);
167 4 : REQUIRE_UNARY(isApprox(func.getGradient(x), dcWRx));
168 4 : }
169 :
170 20 : THEN("the Hessian works was expected")
171 20 : {
172 4 : auto hessian = func.getHessian(x);
173 4 : Vector Wx = scalingData.array() * dataVec.array();
174 4 : DataContainer<TestType> dcWx(dd, Wx);
175 4 : REQUIRE_UNARY(isApprox(hessian.apply(x), dcWx));
176 4 : }
177 20 : }
178 20 : }
179 20 : }
180 :
181 : TEST_SUITE_END();
|