1 /*
2  * Copyright 2014-2017, Max Planck Society.
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without modification,
6  * are permitted provided that the following conditions are met:
7  *
8  * 1. Redistributions of source code must retain the above copyright notice,
9  *    this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice,
12  *    this list of conditions and the following disclaimer in the documentation
13  *    and/or other materials provided with the distribution.
14  *
15  * 3. Neither the name of the copyright holder nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software without
17  *    specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
21  * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
22  * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
23  * INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
24  * BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
25  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
26  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
27  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
28  * OF THE POSSIBILITY OF SUCH DAMAGE.
29  *
30  */
31 
32 /* Created by Edgar Klenske <edgar.klenske@tuebingen.mpg.de>
33  *
34  * Provides the test cases for the Gaussian Process functionality.
35  *
36  */
37 
38 #include <gtest/gtest.h>
39 #include <vector>
40 #include <iostream>
41 #include "math_tools.h"
42 #include "gaussian_process.h"
43 #include "covariance_functions.h"
44 
45 #include <fstream>
46 
47 class GPTest : public ::testing::Test
48 {
49 public:
GPTest()50     GPTest(): random_vector_(11), location_vector_(11), hyper_parameters_(4), extra_parameters_(1)
51     {
52         random_vector_ << -0.1799, -1.4215, -0.2774,  2.6056, 0.6471, -0.4366,
53                        1.3820,  0.4340,  0.8970, -0.7286, -1.7046;
54         location_vector_ << 0, 0.1000, 0.2000, 0.3000, 0.4000, 0.5000, 0.6000,
55                          0.7000, 0.8000, 0.9000, 1.0000;
56         hyper_parameters_ << 1, 2, 1, 2;
57         extra_parameters_ << 5;
58 
59         covariance_function_ = covariance_functions::PeriodicSquareExponential(hyper_parameters_);
60         covariance_function_.setExtraParameters(extra_parameters_);
61 
62         gp_ = GP(covariance_function_);
63     }
64     GP gp_;
65     Eigen::VectorXd random_vector_;
66     Eigen::VectorXd location_vector_;
67     Eigen::VectorXd hyper_parameters_;
68     Eigen::VectorXd extra_parameters_;
69     covariance_functions::PeriodicSquareExponential covariance_function_;
70 };
71 
72 // This test is based on Matlab computations
TEST_F(GPTest,drawSample_prior_test)73 TEST_F(GPTest, drawSample_prior_test)
74 {
75     Eigen::VectorXd sample = gp_.drawSample(location_vector_, random_vector_);
76     Eigen::VectorXd expected_sample(11);
77     expected_sample << -1.8799, -2.2659, -2.6541, -3.0406, -3.4214, -3.7926, -4.1503, -4.4907, -4.8101, -5.1052, -5.3726;
78     for (int i = 0; i < expected_sample.rows(); i++)
79     {
80         EXPECT_NEAR(sample(i), expected_sample(i), 2e-1);
81     }
82 }
83 
84 // This test is based on statistical expectations (mean)
TEST_F(GPTest,drawSamples_prior_mean_test)85 TEST_F(GPTest, drawSamples_prior_mean_test)
86 {
87 
88     hyper_parameters_ << 1, 1, 1, 1; // use of smaller hypers needs less samples
89 
90     covariance_function_ =
91         covariance_functions::PeriodicSquareExponential(hyper_parameters_);
92     gp_ = GP(covariance_function_);
93 
94     int N = 10000; // number of samples to draw
95     location_vector_ = Eigen::VectorXd(1);
96     location_vector_ << 1;
97     Eigen::MatrixXd sample_collection(location_vector_.rows(), N);
98     sample_collection.setZero(); // just in case
99 
100     for (int i = 0; i < N; i++)
101     {
102         Eigen::VectorXd sample = gp_.drawSample(location_vector_);
103         sample_collection.col(i) = sample;
104     }
105     Eigen::VectorXd sample_mean = sample_collection.rowwise().mean();
106 
107     for (int i = 0; i < sample_mean.rows(); i++)
108     {
109         EXPECT_NEAR(0, sample_mean(i), 1e-1);
110     }
111 }
112 
113 // This test is based on statistical expectations (covariance)
TEST_F(GPTest,drawSamples_prior_covariance_test)114 TEST_F(GPTest, drawSamples_prior_covariance_test)
115 {
116 
117     hyper_parameters_ << 1, 1, 1, 1; // use of smaller hypers needs less samples
118 
119     covariance_function_ =
120         covariance_functions::PeriodicSquareExponential(hyper_parameters_);
121     gp_ = GP(covariance_function_);
122 
123     int N = 20000; // number of samples to draw
124     location_vector_ = Eigen::VectorXd(1);
125     location_vector_ << 1;
126     Eigen::MatrixXd sample_collection(location_vector_.rows(), N);
127     sample_collection.setZero(); // just in case
128 
129     for (int i = 0; i < N; i++)
130     {
131         Eigen::VectorXd sample = gp_.drawSample(location_vector_);
132         sample_collection.col(i) = sample;
133     }
134     Eigen::MatrixXd sample_cov = sample_collection * sample_collection.transpose() / N;
135 
136     Eigen::MatrixXd expected_cov = covariance_function_.evaluate(location_vector_, location_vector_);
137 
138     for (int i = 0; i < sample_cov.rows(); i++)
139     {
140         for (int k = 0; k < sample_cov.cols(); k++)
141         {
142             EXPECT_NEAR(expected_cov(i, k), sample_cov(i, k), 2e-1);
143         }
144     }
145 }
146 
147 
TEST_F(GPTest,setCovarianceFunction)148 TEST_F(GPTest, setCovarianceFunction)
149 {
150     Eigen::VectorXd hyperparams(6);
151     hyperparams << 0.1, 15, 25, 15, 5000, 700;
152 
153     GP instance_gp;
154     EXPECT_TRUE(instance_gp.setCovarianceFunction(covariance_functions::PeriodicSquareExponential(hyperparams.segment(1,4))));
155 
156     GP instance_gp2 = GP(covariance_functions::PeriodicSquareExponential(Eigen::VectorXd::Zero(4)));
157     instance_gp2.setHyperParameters(hyperparams);
158 
159     for (int i = 1; i < 5; i++) // first element is different (non set)
160     {
161         EXPECT_NEAR(instance_gp.getHyperParameters()[i], instance_gp2.getHyperParameters()[i], 1e-8);
162     }
163 }
164 
165 
166 
TEST_F(GPTest,setCovarianceFunction_notworking_after_inference)167 TEST_F(GPTest, setCovarianceFunction_notworking_after_inference)
168 {
169     Eigen::VectorXd hyperparams(5);
170     hyperparams << 0.1, 15, 700, 25, 5000;
171 
172     GP instance_gp;
173     EXPECT_TRUE(instance_gp.setCovarianceFunction(covariance_functions::PeriodicSquareExponential(hyperparams.tail(4))));
174 
175     int N = 250;
176     Eigen::VectorXd location =
177         400 * math_tools::generate_uniform_random_matrix_0_1(N, 1)
178         - 200 * Eigen::MatrixXd::Ones(N, 1);
179 
180     Eigen::VectorXd output_from_converged_hyperparams = instance_gp.drawSample(location);
181 
182     instance_gp.infer(location, output_from_converged_hyperparams);
183     EXPECT_FALSE(instance_gp.setCovarianceFunction(covariance_functions::PeriodicSquareExponential(hyperparams.tail(4))));
184 }
185 
186 // to be moved to some other file
TEST_F(GPTest,periodic_covariance_function_test)187 TEST_F(GPTest, periodic_covariance_function_test)
188 {
189     covariance_functions::PeriodicSquareExponential u;
190     EXPECT_EQ(u.getParameterCount(), 4);
191 
192     GP instance_gp = GP(covariance_functions::PeriodicSquareExponential());
193     ASSERT_EQ(instance_gp.getHyperParameters().size(), 6);
194     instance_gp.setHyperParameters(Eigen::VectorXd::Zero(6)); // should not assert
195 }
196 
197 
198 
TEST_F(GPTest,infer_prediction_clear_test)199 TEST_F(GPTest, infer_prediction_clear_test)
200 {
201     Eigen::VectorXd data_loc(1);
202     data_loc << 1;
203     Eigen::VectorXd data_out(1);
204     data_out << 1;
205     gp_.infer(data_loc, data_out);
206 
207     Eigen::VectorXd prediction_location(2);
208     prediction_location << 1, 2;
209 
210     Eigen::VectorXd prediction = gp_.predict(prediction_location);
211 
212     EXPECT_NEAR(prediction(0), 1, 1e-6);
213     EXPECT_FALSE(std::abs(prediction(1) - 1) < 1e-6);
214 
215     gp_.clearData();
216 
217     prediction = gp_.predict(prediction_location);
218 
219     EXPECT_NEAR(prediction(0), 0, 1e-6);
220     EXPECT_NEAR(prediction(1), 0, 1e-6);
221 }
222 
TEST_F(GPTest,squareDistanceTest)223 TEST_F(GPTest, squareDistanceTest)
224 {
225     Eigen::MatrixXd a(4, 3);
226     Eigen::MatrixXd b(4, 5);
227     Eigen::MatrixXd c(3, 4);
228 
229     a <<  3, 5, 5, 4, 6, 6, 3, 2, 3, 1, 0, 3;
230 
231     b <<  1, 4, 5, 6, 7, 3, 4, 5, 6, 7, 0, 2, 4, 20, 2, 2, 3, -2, -2, 2;
232 
233     c <<  1, 2, 3, 4, 4, 5, 6, 7, 6, 7, 8, 9;
234 
235 
236     Eigen::MatrixXd sqdistc(4, 4);
237     Eigen::MatrixXd sqdistab(3, 5);
238 
239     // Computed by Matlab
240     sqdistc << 0, 3, 12, 27, 3, 0, 3, 12, 12, 3, 0, 3, 27, 12, 3, 0;
241 
242     sqdistab << 15, 6, 15, 311, 27, 33, 14, 9, 329, 9, 35, 6, 27, 315, 7;
243 
244     // Test argument order
245     EXPECT_EQ(math_tools::squareDistance(a, b), math_tools::squareDistance
246               (b, a).transpose());
247 
248     // Test that two identical Matrices give the same result
249     // (wether they are the same object or not)
250     EXPECT_EQ(math_tools::squareDistance(a, Eigen::MatrixXd(a)),
251               math_tools::squareDistance(a, a));
252     EXPECT_EQ(math_tools::squareDistance(a), math_tools::squareDistance(
253                   a, a));
254 
255     // Test that the implementation gives the same result as the Matlab
256     // implementation
257     EXPECT_EQ(math_tools::squareDistance(c, c), sqdistc);
258     EXPECT_EQ(math_tools::squareDistance(a, b), sqdistab);
259 }
260 
TEST_F(GPTest,CovarianceTest2)261 TEST_F(GPTest, CovarianceTest2)
262 {
263     Eigen::VectorXd hyperParams(4), extraParams(1);
264     hyperParams << 1, 2, 1, 2;
265     hyperParams = hyperParams.array().log();
266     extraParams << 500;
267     extraParams = extraParams.array().log();
268 
269     Eigen::VectorXd locations(5), X(3), Y(3);
270     locations << 0, 50, 100, 150, 200;
271     X << 0, 100, 200;
272     Y << 1, -1, 1;
273 
274     covariance_functions::PeriodicSquareExponential covFunc(hyperParams);
275     covFunc.setExtraParameters(extraParams);
276     GP gp(covFunc);
277 
278     Eigen::MatrixXd kxx_matlab(5, 5);
279     Eigen::MatrixXd kxX_matlab(5, 3);
280     Eigen::MatrixXd kXX_matlab(3, 3);
281 
282     kxx_matlab << 8.0000, 3.3046, 2.0043, 1.0803, 0.6553,
283                   3.3046, 8.0000, 3.3046, 2.0043, 1.0803,
284                   2.0043, 3.3046, 8.0000, 3.3046, 2.0043,
285                   1.0803, 2.0043, 3.3046, 8.0000, 3.3046,
286                   0.6553, 1.0803, 2.0043, 3.3046, 8.0000;
287 
288     kxX_matlab << 8.0000, 2.0043, 0.6553, 3.3046, 3.3046, 1.0803, 2.0043, 8.0000, 2.0043, 1.0803, 3.3046, 3.3046, 0.6553, 2.0043, 8.0000;
289 
290     kXX_matlab << 8.0000, 2.0043, 0.6553, 2.0043, 8.0000, 2.0043, 0.6553, 2.0043, 8.0000;
291 
292     Eigen::MatrixXd kxx = covFunc.evaluate(locations, locations);
293     Eigen::MatrixXd kxX = covFunc.evaluate(locations, X);
294     Eigen::MatrixXd kXX = covFunc.evaluate(X, X);
295 
296     for (int col = 0; col < kxx.cols(); col++)
297     {
298         for (int row = 0; row < kxx.rows(); row++)
299         {
300             EXPECT_NEAR(kxx(row, col), kxx_matlab(row, col), 0.003);
301         }
302     }
303 
304     for (int col = 0; col < kxX.cols(); col++)
305     {
306         for (int row = 0; row < kxX.rows(); row++)
307         {
308             EXPECT_NEAR(kxX(row, col), kxX_matlab(row, col), 0.003);
309         }
310     }
311 
312     for (int col = 0; col < kXX.cols(); col++)
313     {
314         for (int row = 0; row < kXX.rows(); row++)
315         {
316             EXPECT_NEAR(kXX(row, col), kXX_matlab(row, col), 0.003);
317         }
318     }
319 }
320 
TEST_F(GPTest,CovarianceTest3)321 TEST_F(GPTest, CovarianceTest3)
322 {
323     Eigen::Matrix<double, 6, 1> hyperParams;
324     hyperParams << 10, 1, 1, 1, 100, 1;
325     hyperParams = hyperParams.array().log();
326 
327     Eigen::VectorXd periodLength(1);
328     periodLength << std::log(80);
329 
330     Eigen::VectorXd locations(5), X(3), Y(3);
331     locations << 0, 50, 100, 150, 200;
332     X << 0, 100, 200;
333 
334     covariance_functions::PeriodicSquareExponential2 covFunc(hyperParams);
335     covFunc.setExtraParameters(periodLength);
336 
337     Eigen::MatrixXd kxx_matlab(5, 5);
338     kxx_matlab <<
339                3.00000, 1.06389, 0.97441, 1.07075, 0.27067,
340                         1.06389, 3.00000, 1.06389, 0.97441, 1.07075,
341                         0.97441, 1.06389, 3.00000, 1.06389, 0.97441,
342                         1.07075, 0.97441, 1.06389, 3.00000, 1.06389,
343                         0.27067, 1.07075, 0.97441, 1.06389, 3.00000;
344 
345     Eigen::MatrixXd kxX_matlab(5, 3);
346     kxX_matlab <<
347                3.00000, 0.97441, 0.27067, 1.06389, 1.06389,
348                         1.07075, 0.97441, 3.00000, 0.97441, 1.07075,
349                         1.06389, 1.06389, 0.27067, 0.97441, 3.00000;
350 
351     Eigen::MatrixXd kXX_matlab(3, 3);
352     kXX_matlab <<
353                3.00000, 0.97441, 0.27067,
354                         0.97441, 3.00000, 0.97441,
355                         0.27067, 0.97441, 3.00000;
356 
357     Eigen::MatrixXd kxx = covFunc.evaluate(locations, locations);
358     Eigen::MatrixXd kxX = covFunc.evaluate(locations, X);
359     Eigen::MatrixXd kXX = covFunc.evaluate(X, X);
360 
361     for (int col = 0; col < kxx.cols(); col++)
362     {
363         for (int row = 0; row < kxx.rows(); row++)
364         {
365             EXPECT_NEAR(kxx(row, col), kxx_matlab(row, col), 0.01);
366         }
367     }
368 
369     for (int col = 0; col < kxX.cols(); col++)
370     {
371         for (int row = 0; row < kxX.rows(); row++)
372         {
373             EXPECT_NEAR(kxX(row, col), kxX_matlab(row, col), 0.01);
374         }
375     }
376 
377     for (int col = 0; col < kXX.cols(); col++)
378     {
379         for (int row = 0; row < kXX.rows(); row++)
380         {
381             EXPECT_NEAR(kXX(row, col), kXX_matlab(row, col), 0.01);
382         }
383     }
384 }
385 
main(int argc,char ** argv)386 int main(int argc, char** argv)
387 {
388     ::testing::InitGoogleTest(&argc, argv);
389     return RUN_ALL_TESTS();
390 }
391