Apply clang-format to source 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, 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),
92               std::back_inserter(expectedSimilarityGradient));
93     for (auto& x : expectedSimilarityGradient)
94     {
95         x /= comparedDensity.asConstView().mapping().required_span_size();
96     }
97
98     FloatingPointTolerance tolerance(defaultFloatTolerance());
99
100     // Need this conversion to vector of float, because Pointwise requires size()
101     // member function not provided by basic_mdspan
102     const basic_mdspan<const float, dynamicExtents3D> gradient =
103             measure.gradient(comparedDensity.asConstView());
104     ArrayRef<const float> gradientView(gradient.data(),
105                                        gradient.data() + gradient.mapping().required_span_size());
106     EXPECT_THAT(expectedSimilarityGradient, Pointwise(FloatEq(tolerance), gradientView));
107 }
108
109 TEST(DensitySimilarityTest, GradientThrowsIfDensitiesDontMatch)
110 {
111     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
112     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
113                                      referenceDensity.asConstView());
114
115     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 5);
116     EXPECT_THROW(measure.gradient(comparedDensity.asConstView()), RangeError);
117 }
118
119 TEST(DensitySimilarityTest, SimilarityThrowsIfDensitiesDontMatch)
120 {
121     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
122     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
123                                      referenceDensity.asConstView());
124     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 5);
125     EXPECT_THROW(measure.similarity(comparedDensity.asConstView()), RangeError);
126 }
127
128 TEST(DensitySimilarityTest, CopiedMeasureInnerProductIsCorrect)
129 {
130     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
131     std::iota(begin(referenceDensity), end(referenceDensity), 0);
132
133     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::innerProduct,
134                                      referenceDensity.asConstView());
135
136     DensitySimilarityMeasure                            copiedMeasure = measure;
137     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
138     std::iota(begin(comparedDensity), end(comparedDensity), -18);
139
140     // 0*(-18) + 1*(-17) .. + 26 * 8 / Number elements
141     const float expectedSimilarity =
142             -117.0 / comparedDensity.asConstView().mapping().required_span_size();
143     EXPECT_FLOAT_EQ(expectedSimilarity, copiedMeasure.similarity(comparedDensity.asConstView()));
144 }
145
146 TEST(DensitySimilarityTest, RelativeEntropyOfSameDensityIsZero)
147 {
148     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
149     std::iota(begin(referenceDensity), end(referenceDensity), -2);
150
151     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::relativeEntropy,
152                                      referenceDensity.asConstView());
153
154     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
155     std::iota(begin(comparedDensity), end(comparedDensity), -2);
156
157     const float expectedSimilarity = 0;
158     EXPECT_REAL_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
159 }
160
161
162 TEST(DensitySimilarityTest, RelativeEntropyIsCorrect)
163 {
164     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
165     std::iota(begin(referenceDensity), end(referenceDensity), -2);
166
167     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::relativeEntropy,
168                                      referenceDensity.asConstView());
169
170     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
171     std::iota(begin(comparedDensity), end(comparedDensity), -1);
172
173     const real expectedSimilarity = 22.468290398724498791;
174     EXPECT_REAL_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
175 }
176
177 TEST(DensitySimilarityTest, RelativeEntropyGradientIsCorrect)
178 {
179     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
180     std::iota(begin(referenceDensity), end(referenceDensity), -1);
181
182     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::relativeEntropy,
183                                      referenceDensity.asConstView());
184
185     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
186     std::iota(begin(comparedDensity), end(comparedDensity), -2);
187
188     // Need this conversion to ArrayRef, because Pointwise requires size()
189     // member function not provided by basic_mdspan
190     const basic_mdspan<const float, dynamicExtents3D> gradient =
191             measure.gradient(comparedDensity.asConstView());
192     ArrayRef<const float> gradientView(gradient.data(),
193                                        gradient.data() + gradient.mapping().required_span_size());
194
195     TestReferenceData    refData;
196     TestReferenceChecker checker(refData.rootChecker());
197     checker.setDefaultTolerance(defaultFloatTolerance());
198     checker.checkSequence(gradientView.begin(), gradientView.end(), "relative-entropy-gradient");
199 }
200
201 TEST(DensitySimilarityTest, CrossCorrelationIsOne)
202 {
203     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(100, 100, 100);
204     std::iota(begin(referenceDensity), end(referenceDensity), 10000);
205
206     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
207                                      referenceDensity.asConstView());
208
209     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(100, 100, 100);
210     std::iota(begin(comparedDensity), end(comparedDensity), -10000);
211
212     const real expectedSimilarity = 1;
213     EXPECT_REAL_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
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     EXPECT_REAL_EQ(expectedSimilarity, measure.similarity(comparedDensity.asConstView()));
233 }
234
235 TEST(DensitySimilarityTest, CrossCorrelationGradientIsZeroWhenCorrelated)
236 {
237     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(30, 30, 30);
238     std::iota(begin(referenceDensity), end(referenceDensity), -1);
239
240     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
241                                      referenceDensity.asConstView());
242
243     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(30, 30, 30);
244     std::iota(begin(comparedDensity), end(comparedDensity), -2);
245
246     // Need this conversion to ArrayRef, because Pointwise requires size()
247     // member function not provided by basic_mdspan
248     const basic_mdspan<const float, dynamicExtents3D> gradient =
249             measure.gradient(comparedDensity.asConstView());
250     ArrayRef<const float> gradientView(gradient.data(),
251                                        gradient.data() + gradient.mapping().required_span_size());
252
253     std::array<float, 27000> expectedSimilarityGradient = {};
254
255     EXPECT_THAT(expectedSimilarityGradient, Pointwise(FloatEq(defaultFloatTolerance()), gradientView));
256 }
257
258 TEST(DensitySimilarityTest, CrossCorrelationGradientIsCorrect)
259 {
260     MultiDimArray<std::vector<float>, dynamicExtents3D> referenceDensity(3, 3, 3);
261     std::iota(begin(referenceDensity), end(referenceDensity), -1);
262
263     DensitySimilarityMeasure measure(DensitySimilarityMeasureMethod::crossCorrelation,
264                                      referenceDensity.asConstView());
265
266     MultiDimArray<std::vector<float>, dynamicExtents3D> comparedDensity(3, 3, 3);
267     std::iota(begin(comparedDensity), end(comparedDensity), -2);
268
269     // some non-linear transformation, so that we break the correlation
270     for (float& valueToCompare : comparedDensity)
271     {
272         valueToCompare *= valueToCompare;
273     }
274
275     // Need this conversion to ArrayRef, because Pointwise requires size()
276     // member function not provided by basic_mdspan
277     const basic_mdspan<const float, dynamicExtents3D> gradient =
278             measure.gradient(comparedDensity.asConstView());
279     ArrayRef<const float> gradientView(gradient.data(),
280                                        gradient.data() + gradient.mapping().required_span_size());
281
282     TestReferenceData    refData;
283     TestReferenceChecker checker(refData.rootChecker());
284     checker.setDefaultTolerance(defaultFloatTolerance());
285     checker.checkSequence(gradientView.begin(), gradientView.end(), "cross-correlation-gradient");
286 }
287
288 } // namespace test
289
290 } // namespace gmx