Apply re-formatting to C++ in src/ tree.
[alexxy/gromacs.git] / src / gromacs / math / tests / densityfit.cpp
1 /*
2  * This file is part of the GROMACS molecular simulation package.
3  *
4  * Copyright (c) 2019,2020, by the GROMACS development team, led by
5  * Mark Abraham, David van der Spoel, Berk Hess, and Erik Lindahl,
6  * and including many others, as listed in the AUTHORS file in the
7  * top-level source directory and at http://www.gromacs.org.
8  *
9  * GROMACS is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public License
11  * as published by the Free Software Foundation; either version 2.1
12  * of the License, or (at your option) any later version.
13  *
14  * GROMACS is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with GROMACS; if not, see
21  * http://www.gnu.org/licenses, or write to the Free Software Foundation,
22  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA.
23  *
24  * If you want to redistribute modifications to GROMACS, please
25  * consider that scientific software is very special. Version
26  * control is crucial - bugs must be traceable. We will be happy to
27  * consider code for inclusion in the official distribution, but
28  * derived work must not be called official GROMACS. Details are found
29  * in the README & COPYING files - if they are missing, get the
30  * official version at http://www.gromacs.org.
31  *
32  * To help us fund GROMACS development, we humbly ask that you cite
33  * the research papers on the package. Check out http://www.gromacs.org.
34  */
35 /*! \internal \file
36  * \brief
37  * Tests density fitting routines.
38  *
39  * \author Christian Blau <blau@kth.se>
40  * \ingroup module_math
41  */
42 #include "gmxpre.h"
43
44 #include "gromacs/math/densityfit.h"
45
46 #include <numeric>
47
48 #include <gtest/gtest.h>
49
50 #include "gromacs/math/multidimarray.h"
51
52 #include "testutils/refdata.h"
53 #include "testutils/testasserts.h"
54 #include "testutils/testmatchers.h"
55
56 namespace gmx
57 {
58
59 namespace test
60 {
61
62 TEST(DensitySimilarityTest, InnerProductIsCorrect)
63 {
64     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
65     std::iota(begin(referenceDensity), end(referenceDensity), 0);
66
67     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
68                                      referenceDensity.asConstView());
69
70     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
71     std::iota(begin(comparedDensity), end(comparedDensity), -18);
72
73     // 0*(-18) + 1*(-17) .. + 26 * 8 / Number elements
74     const float expectedSimilarity =
75             -117.0 / comparedDensity.asConstView().mapping().required_span_size();
76     EXPECT_FLOAT_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
77 }
78
79 TEST(DensitySimilarityTest, InnerProductGradientIsCorrect)
80 {
81     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
82     std::iota(begin(referenceDensity), end(referenceDensity), 0);
83
84     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
85                                      referenceDensity.asConstView());
86
87     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
88     std::iota(begin(comparedDensity), end(comparedDensity), -18);
89
90     std::vector<float> expectedSimilarityGradient;
91     std::copy(begin(referenceDensity), end(referenceDensity), std::back_inserter(expectedSimilarityGradient));
92     for (auto& x : expectedSimilarityGradient)
93     {
94         x /= comparedDensity.asConstView().mapping().required_span_size();
95     }
96
97     FloatingPointTolerance tolerance(defaultFloatTolerance());
98
99     // Need this conversion to vector of float, because Pointwise requires size()
100     // member function not provided by basic_mdspan
101     const basic_mdspan<const float, dynamicExtents3D> gradient =
102             measure.gradient(comparedDensity.asConstView());
103     ArrayRef<const float> gradientView(gradient.data(),
104                                        gradient.data() + gradient.mapping().required_span_size());
105     EXPECT_THAT(expectedSimilarityGradient, Pointwise(FloatEq(tolerance), gradientView));
106 }
107
108 TEST(DensitySimilarityTest, GradientThrowsIfDensitiesDontMatch)
109 {
110     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
111     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
112                                      referenceDensity.asConstView());
113
114     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 5);
115     EXPECT_THROW(measure.gradient(comparedDensity.asConstView()), RangeError);
116 }
117
118 TEST(DensitySimilarityTest, SimilarityThrowsIfDensitiesDontMatch)
119 {
120     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
121     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
122                                      referenceDensity.asConstView());
123     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 5);
124     EXPECT_THROW(measure.similarity(comparedDensity.asConstView()), RangeError);
125 }
126
127 TEST(DensitySimilarityTest, CopiedMeasureInnerProductIsCorrect)
128 {
129     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
130     std::iota(begin(referenceDensity), end(referenceDensity), 0);
131
132     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
133                                      referenceDensity.asConstView());
134
135     DensitySimilarityMeasure                            copiedMeasure = measure;
136     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
137     std::iota(begin(comparedDensity), end(comparedDensity), -18);
138
139     // 0*(-18) + 1*(-17) .. + 26 * 8 / Number elements
140     const float expectedSimilarity =
141             -117.0 / comparedDensity.asConstView().mapping().required_span_size();
142     EXPECT_FLOAT_EQ(expectedSimilarity, copiedMeasure.similarity(comparedDensity.asConstView()));
143 }
144
145 TEST(DensitySimilarityTest, RelativeEntropyOfSameDensityIsZero)
146 {
147     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
148     std::iota(begin(referenceDensity), end(referenceDensity), -2);
149
150     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::relativeEntropy,
151                                      referenceDensity.asConstView());
152
153     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
154     std::iota(begin(comparedDensity), end(comparedDensity), -2);
155
156     const float expectedSimilarity = 0;
157     EXPECT_REAL_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
158 }
159
160
161 TEST(DensitySimilarityTest, RelativeEntropyIsCorrect)
162 {
163     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
164     std::iota(begin(referenceDensity), end(referenceDensity), -2);
165
166     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::relativeEntropy,
167                                      referenceDensity.asConstView());
168
169     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
170     std::iota(begin(comparedDensity), end(comparedDensity), -1);
171
172     const real expectedSimilarity = 22.468290398724498791;
173     EXPECT_REAL_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
174 }
175
176 TEST(DensitySimilarityTest, RelativeEntropyGradientIsCorrect)
177 {
178     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
179     std::iota(begin(referenceDensity), end(referenceDensity), -1);
180
181     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::relativeEntropy,
182                                      referenceDensity.asConstView());
183
184     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
185     std::iota(begin(comparedDensity), end(comparedDensity), -2);
186
187     // Need this conversion to ArrayRef, because Pointwise requires size()
188     // member function not provided by basic_mdspan
189     const basic_mdspan<const float, dynamicExtents3D> gradient =
190             measure.gradient(comparedDensity.asConstView());
191     ArrayRef<const float> gradientView(gradient.data(),
192                                        gradient.data() + gradient.mapping().required_span_size());
193
194     TestReferenceData    refData;
195     TestReferenceChecker checker(refData.rootChecker());
196     checker.setDefaultTolerance(defaultFloatTolerance());
197     checker.checkSequence(gradientView.begin(), gradientView.end(), "relative-entropy-gradient");
198 }
199
200 TEST(DensitySimilarityTest, CrossCorrelationIsOne)
201 {
202     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(100, 100, 100);
203     std::iota(begin(referenceDensity), end(referenceDensity), 10000);
204
205     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
206                                      referenceDensity.asConstView());
207
208     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(100, 100, 100);
209     std::iota(begin(comparedDensity), end(comparedDensity), -10000);
210
211     const real             expectedSimilarity = 1;
212     FloatingPointTolerance tolerance(relativeToleranceAsUlp(1.0, 100'000));
213     EXPECT_REAL_EQ_TOL(expectedSimilarity, measure.similarity(comparedDensity.asConstView()), tolerance);
214 }
215
216 TEST(DensitySimilarityTest, CrossCorrelationIsMinusOneWhenAntiCorrelated)
217 {
218     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(100, 100, 100);
219     std::iota(begin(referenceDensity), end(referenceDensity), 10000);
220     for (auto& referenceDensityValue : referenceDensity)
221     {
222         referenceDensityValue *= -1;
223     }
224
225     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
226                                      referenceDensity.asConstView());
227
228     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(100, 100, 100);
229     std::iota(begin(comparedDensity), end(comparedDensity), -10000);
230
231     const real             expectedSimilarity = -1;
232     FloatingPointTolerance tolerance(relativeToleranceAsUlp(-1.0, 100'000));
233     EXPECT_REAL_EQ_TOL(expectedSimilarity, measure.similarity(comparedDensity.asConstView()), tolerance);
234 }
235
236 TEST(DensitySimilarityTest, CrossCorrelationGradientIsZeroWhenCorrelated)
237 {
238     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(30, 30, 30);
239     std::iota(begin(referenceDensity), end(referenceDensity), -1);
240
241     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
242                                      referenceDensity.asConstView());
243
244     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(30, 30, 30);
245     std::iota(begin(comparedDensity), end(comparedDensity), -2);
246
247     // Need this conversion to ArrayRef, because Pointwise requires size()
248     // member function not provided by basic_mdspan
249     const basic_mdspan<const float, dynamicExtents3D> gradient =
250             measure.gradient(comparedDensity.asConstView());
251     ArrayRef<const float> gradientView(gradient.data(),
252                                        gradient.data() + gradient.mapping().required_span_size());
253
254     std::array<float, 27000> expectedSimilarityGradient = {};
255
256     EXPECT_THAT(expectedSimilarityGradient, Pointwise(FloatEq(defaultFloatTolerance()), gradientView));
257 }
258
259 TEST(DensitySimilarityTest, CrossCorrelationGradientIsCorrect)
260 {
261     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
262     std::iota(begin(referenceDensity), end(referenceDensity), -1);
263
264     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
265                                      referenceDensity.asConstView());
266
267     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
268     std::iota(begin(comparedDensity), end(comparedDensity), -2);
269
270     // some non-linear transformation, so that we break the correlation
271     for (float& valueToCompare : comparedDensity)
272     {
273         valueToCompare *= valueToCompare;
274     }
275
276     // Need this conversion to ArrayRef, because Pointwise requires size()
277     // member function not provided by basic_mdspan
278     const basic_mdspan<const float, dynamicExtents3D> gradient =
279             measure.gradient(comparedDensity.asConstView());
280     ArrayRef<const float> gradientView(gradient.data(),
281                                        gradient.data() + gradient.mapping().required_span_size());
282
283     TestReferenceData    refData;
284     TestReferenceChecker checker(refData.rootChecker());
285     checker.setDefaultTolerance(defaultFloatTolerance());
286     checker.checkSequence(gradientView.begin(), gradientView.end(), "cross-correlation-gradient");
287 }
288
289 } // namespace test
290
291 } // namespace gmx