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