1 /*
2     SPDX-FileCopyrightText: 2014-2017 Max Planck Society.
3     All rights reserved.
4 
5     SPDX-License-Identifier: BSD-3-Clause
6 */
7 
8 /* Created by Edgar Klenske <edgar.klenske@tuebingen.mpg.de>
9  *
10  * Provides the test cases for the Gaussian Process functionality.
11  *
12  */
13 
14 #include <gtest/gtest.h>
15 #include <vector>
16 #include <iostream>
17 #include "gaussian_process_guider.h"
18 
19 #include <fstream>
20 #include <thread>
21 
22 class GPGTest : public ::testing::Test
23 {
24 public:
25     static const double DefaultControlGain; // control gain
26     static const double DefaultPeriodLengthsForInference; // minimal number of period lengths for full prediction
27     static const double DefaultMinMove;
28 
29     static const double DefaultLengthScaleSE0Ker; // length-scale of the long-range SE-kernel
30     static const double DefaultSignalVarianceSE0Ker; // signal variance of the long-range SE-kernel
31     static const double DefaultLengthScalePerKer; // length-scale of the periodic kernel
32     static const double DefaultPeriodLengthPerKer; // P_p, period-length of the periodic kernel
33     static const double DefaultSignalVariancePerKer; // signal variance of the periodic kernel
34     static const double DefaultLengthScaleSE1Ker; // length-scale of the short-range SE-kernel
35     static const double DefaultSignalVarianceSE1Ker; // signal variance of the short range SE-kernel
36 
37     static const double DefaultPeriodLengthsForPeriodEstimation; // minimal number of period lengts for PL estimation
38     static const int    DefaultNumPointsForApproximation; // number of points used in the GP approximation
39     static const double DefaultPredictionGain; // amount of GP prediction to blend in
40 
41     static const bool   DefaultComputePeriod;
42 
43     GaussianProcessGuider* GPG;
44 
GPGTest()45     GPGTest(): GPG(0)
46     {
47         GaussianProcessGuider::guide_parameters parameters;
48         parameters.control_gain_ = DefaultControlGain;
49         parameters.min_periods_for_inference_ = DefaultPeriodLengthsForInference;
50         parameters.min_move_ = DefaultMinMove;
51         parameters.SE0KLengthScale_ = DefaultLengthScaleSE0Ker;
52         parameters.SE0KSignalVariance_ = DefaultSignalVarianceSE0Ker;
53         parameters.PKLengthScale_ = DefaultLengthScalePerKer;
54         parameters.PKPeriodLength_ = DefaultPeriodLengthPerKer;
55         parameters.PKSignalVariance_ = DefaultSignalVariancePerKer;
56         parameters.SE1KLengthScale_ = DefaultLengthScaleSE1Ker;
57         parameters.SE1KSignalVariance_ = DefaultSignalVarianceSE1Ker;
58         parameters.min_periods_for_period_estimation_ = DefaultPeriodLengthsForPeriodEstimation;
59         parameters.points_for_approximation_ = DefaultNumPointsForApproximation;
60         parameters.prediction_gain_ = DefaultPredictionGain;
61         parameters.compute_period_ = DefaultComputePeriod;
62 
63         GPG = new GaussianProcessGuider(parameters);
64         GPG->SetLearningRate(1.0); // disable smooth learning
65     }
66 
~GPGTest()67     ~GPGTest()
68     {
69         delete GPG;
70     }
71 };
72 
73 const double GPGTest::DefaultControlGain                   = 0.8; // control gain
74 const double GPGTest::DefaultPeriodLengthsForInference     = 1.0; // minimal number of period lengths for full prediction
75 const double GPGTest::DefaultMinMove                       = 0.2;
76 
77 const double GPGTest::DefaultLengthScaleSE0Ker             = 500.0; // length-scale of the long-range SE-kernel
78 const double GPGTest::DefaultSignalVarianceSE0Ker          = 10.0; // signal variance of the long-range SE-kernel
79 const double GPGTest::DefaultLengthScalePerKer             = 10.0; // length-scale of the periodic kernel
80 const double GPGTest::DefaultPeriodLengthPerKer            = 100.0; // P_p, period-length of the periodic kernel
81 const double GPGTest::DefaultSignalVariancePerKer          = 10.0; // signal variance of the periodic kernel
82 const double GPGTest::DefaultLengthScaleSE1Ker             = 5.0; // length-scale of the short-range SE-kernel
83 const double GPGTest::DefaultSignalVarianceSE1Ker          = 1.0; // signal variance of the short range SE-kernel
84 
85 const double GPGTest::DefaultPeriodLengthsForPeriodEstimation = 2.0; // minimal number of period lengts for PL estimation
86 const int GPGTest::DefaultNumPointsForApproximation        = 100; // number of points used in the GP approximation
87 const double GPGTest::DefaultPredictionGain                = 1.0; // amount of GP prediction to blend in
88 
89 const bool GPGTest::DefaultComputePeriod                   = true;
90 
91 #include <iterator>
92 #include <iostream>
93 #include <fstream>
94 #include <sstream>
95 #include <vector>
96 #include <string>
97 
98 #include "guide_performance_tools.h"
99 
TEST_F(GPGTest,simple_result_test)100 TEST_F(GPGTest, simple_result_test)
101 {
102     double result = 0.0;
103 
104     // disable hysteresis blending
105     GPG->SetPeriodLengthsInference(0.0);
106 
107     // for an empty dataset, deduceResult should return zero
108     result = GPG->deduceResult(3.0);
109     EXPECT_NEAR(result, 0, 1e-6);
110 
111     // for an empty dataset, result is equivalent to a P-controller
112     result = GPG->result(1.0, 2.0, 3.0);
113     EXPECT_NEAR(result, 0.8, 1e-6); // result should be measurement x control gain
114 
115     GPG->save_gp_data();
116 }
117 
TEST_F(GPGTest,period_identification_test)118 TEST_F(GPGTest, period_identification_test)
119 {
120     // first: prepare a nice GP with a sine wave
121     double period_length = 300;
122     double max_time = 10*period_length;
123     int resolution = 500;
124     Eigen::VectorXd timestamps = Eigen::VectorXd::LinSpaced(resolution + 1, 0, max_time);
125     Eigen::VectorXd measurements = 50*(timestamps.array()*2*M_PI/period_length).sin();
126     Eigen::VectorXd controls = 0*measurements;
127     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
128 
129     // feed data to the GPGuider
130     for (int i = 0; i < timestamps.size(); ++i)
131     {
132         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
133     }
134     GPG->result(0.15, 2.0, 3.0);
135 
136     EXPECT_NEAR(GPG->GetGPHyperparameters()[PKPeriodLength], period_length, 1e0);
137 
138     GPG->save_gp_data();
139 }
140 
TEST_F(GPGTest,min_move_test)141 TEST_F(GPGTest, min_move_test)
142 {
143     // disable hysteresis blending
144     GPG->SetPeriodLengthsInference(0.0);
145 
146     // simple min-moves (without GP data)
147     EXPECT_NEAR(GPG->result(0.15, 2.0, 3.0), 0, 1e-6);
148     GPG->reset();
149     EXPECT_NEAR(GPG->result(0.25, 2.0, 3.0), 0.25*0.8, 1e-6);
150     GPG->reset();
151     EXPECT_NEAR(GPG->result(-0.15, 2.0, 3.0), 0, 1e-6);
152     GPG->reset();
153     EXPECT_NEAR(GPG->result(-0.25, 2.0, 3.0), -0.25*0.8, 1e-6);
154     GPG->reset();
155 
156     GPG->save_gp_data();
157 }
158 
TEST_F(GPGTest,gp_prediction_test)159 TEST_F(GPGTest, gp_prediction_test)
160 {
161 
162     // first: prepare a nice GP with a sine wave
163     double period_length = 300;
164     double max_time = 5*period_length;
165     int resolution = 600;
166     double prediction_length = 3.0;
167     Eigen::VectorXd locations(2);
168     Eigen::VectorXd predictions(2);
169     Eigen::VectorXd timestamps = Eigen::VectorXd::LinSpaced(resolution + 1, 0, max_time);
170     Eigen::VectorXd measurements = 50*(timestamps.array()*2*M_PI/period_length).sin();
171     Eigen::VectorXd controls = 0*measurements;
172     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
173 
174     // feed data to the GPGuider
175     for (int i = 0; i < timestamps.size(); ++i)
176     {
177         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
178     }
179     locations << max_time, max_time + prediction_length;
180     predictions = 50*(locations.array()*2*M_PI/period_length).sin();
181     // the first case is with an error smaller than min_move_
182     EXPECT_NEAR(GPG->result(0.15, 2.0, prediction_length, max_time), predictions[1]-predictions[0], 2e-1);
183     GPG->reset();
184 
185     // feed data to the GPGuider
186     for (int i = 0; i < timestamps.size(); ++i)
187     {
188         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
189     }
190     // the first case is with an error larger than min_move_
191     EXPECT_NEAR(GPG->result(0.25, 2.0, prediction_length, max_time), 0.25*0.8+predictions[1]-predictions[0], 2e-1);
192 
193     GPG->save_gp_data();
194 }
195 
TEST_F(GPGTest,parameters_test)196 TEST_F(GPGTest, parameters_test)
197 {
198     EXPECT_NEAR(GPG->GetControlGain(), DefaultControlGain, 1e-6);
199     EXPECT_NEAR(GPG->GetPeriodLengthsInference(), DefaultPeriodLengthsForInference, 1e-6);
200     EXPECT_NEAR(GPG->GetMinMove(), DefaultMinMove, 1e-6);
201 
202     std::vector<double> parameters = GPG->GetGPHyperparameters();
203     EXPECT_NEAR(parameters[SE0KLengthScale], DefaultLengthScaleSE0Ker, 1e-6);
204     EXPECT_NEAR(parameters[SE0KSignalVariance], DefaultSignalVarianceSE0Ker, 1e-6);
205     EXPECT_NEAR(parameters[PKLengthScale], DefaultLengthScalePerKer, 1e-6);
206     EXPECT_NEAR(parameters[PKSignalVariance], DefaultSignalVariancePerKer, 1e-6);
207     EXPECT_NEAR(parameters[SE1KLengthScale], DefaultLengthScaleSE1Ker, 1e-6);
208     EXPECT_NEAR(parameters[SE1KSignalVariance], DefaultSignalVarianceSE1Ker, 1e-6);
209     EXPECT_NEAR(parameters[PKPeriodLength], DefaultPeriodLengthPerKer, 1e-6);
210 
211     EXPECT_NEAR(GPG->GetPeriodLengthsPeriodEstimation(), DefaultPeriodLengthsForPeriodEstimation, 1e-6);
212     EXPECT_NEAR(GPG->GetNumPointsForApproximation(), DefaultNumPointsForApproximation, 1e-6);
213     EXPECT_NEAR(GPG->GetPredictionGain(), DefaultPredictionGain, 1e-6);
214     EXPECT_NEAR(GPG->GetBoolComputePeriod(), DefaultComputePeriod, 1e-6);
215 
216     GPG->save_gp_data();
217 }
218 
TEST_F(GPGTest,timer_test)219 TEST_F(GPGTest, timer_test)
220 {
221     int wait = 500;
222 
223     GPG->result(1.0, 2.0, 3.0);
224     std::this_thread::sleep_for(std::chrono::milliseconds(wait));
225 
226     auto time_start = std::chrono::system_clock::now();
227     GPG->result(1.0, 2.0, 3.0);
228     double first_time = GPG->get_second_last_point().timestamp;
229     std::this_thread::sleep_for(std::chrono::milliseconds(wait));
230     auto time_end = std::chrono::system_clock::now();
231     GPG->result(1.0, 2.0, 3.0);
232     double second_time = GPG->get_second_last_point().timestamp;
233 
234     EXPECT_NEAR(second_time - first_time, std::chrono::duration<double>(time_end - time_start).count(), 1e-1);
235 
236     GPG->save_gp_data();
237 }
238 
TEST_F(GPGTest,gp_projection_test)239 TEST_F(GPGTest, gp_projection_test)
240 {   // this test should fail when output projections are disabled and should pass when they are enabled
241 
242     // first: prepare a nice GP with a sine wave
243     double period_length = 300;
244     double max_time = 5*period_length;
245     int resolution = 600;
246     double prediction_length = 3.0;
247     Eigen::VectorXd locations(2);
248     Eigen::VectorXd predictions(2);
249     Eigen::VectorXd timestamps = Eigen::VectorXd::LinSpaced(resolution + 1, 0, max_time);
250     Eigen::VectorXd measurements = 50*(timestamps.array()*2*M_PI/period_length).sin();
251     Eigen::VectorXd controls = 0*measurements;
252     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
253 
254     Eigen::VectorXd sine_noise = 5*(timestamps.array()*2*M_PI/26).sin(); // smaller "disturbance" to add
255 
256     measurements = measurements + sine_noise;
257 
258     // feed data to the GPGuider
259     for (int i = 0; i < timestamps.size(); ++i)
260     {
261         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
262     }
263     locations << max_time, max_time + prediction_length;
264     predictions = 50*(locations.array()*2*M_PI/period_length).sin();
265     // the first case is with an error smaller than min_move_
266     EXPECT_NEAR(GPG->result(0.0, 2.0, prediction_length, max_time), predictions[1]-predictions[0], 3e-1);
267     GPG->reset();
268 
269     GPG->save_gp_data();
270 }
271 
TEST_F(GPGTest,linear_drift_identification_test)272 TEST_F(GPGTest, linear_drift_identification_test)
273 {   // when predicting one period length ahead, only linear drift should show
274 
275     // first: prepare a nice GP with a sine wave
276     double period_length = 300;
277     double max_time = 3*period_length;
278     int resolution = 300;
279     double prediction_length = period_length; // necessary to only see the drift
280     Eigen::VectorXd locations(2);
281     Eigen::VectorXd predictions(2);
282     Eigen::VectorXd timestamps = Eigen::VectorXd::LinSpaced(resolution + 1, 0, max_time);
283     Eigen::VectorXd measurements = 0*timestamps;
284     Eigen::VectorXd sine_data = 50*(timestamps.array()*2*M_PI/period_length).sin();
285     Eigen::VectorXd drift = 0.25*timestamps; // drift to add
286     Eigen::VectorXd gear_function = sine_data + drift;
287     Eigen::VectorXd controls(timestamps.size());
288     controls << gear_function.tail(gear_function.size()-1) - gear_function.head(gear_function.size()-1), 0;
289     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
290 
291     std::vector<double> parameters = GPG->GetGPHyperparameters();
292     parameters[SE0KSignalVariance] = 1e-10; // disable long-range SE kernel
293     parameters[SE1KSignalVariance] = 1e-10; // disable short-range SE kernel
294     parameters[PKPeriodLength] = period_length; // set exact period length
295     GPG->SetBoolComputePeriod(false); // use the exact period length
296     GPG->SetGPHyperparameters(parameters);
297 
298     GPG->SetNumPointsForApproximation(2000); // need all data points for exact drift
299 
300     // feed data to the GPGuider
301     for (int i = 0; i < timestamps.size(); ++i)
302     {
303         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
304     }
305     locations << 5000, 5000 + prediction_length;
306     predictions = 0.25*locations; // only predict linear drift here
307     // the first case is with an error smaller than min_move_
308     EXPECT_NEAR(GPG->result(0.0, 100.0, prediction_length, max_time), predictions[1]-predictions[0], 2e-1);
309 
310     GPG->save_gp_data();
311 }
312 
TEST_F(GPGTest,data_preparation_test)313 TEST_F(GPGTest, data_preparation_test)
314 {   // no matter whether the gear function shows up in the controls or in the measurements,
315     // the predictions should be identical
316 
317     // first: prepare a nice GP with a sine wave
318     double period_length = 300;
319     double max_time = 3*period_length;
320     int resolution = 200;
321     double prediction_length = 3.0;
322     Eigen::VectorXd timestamps = Eigen::VectorXd::LinSpaced(resolution + 1, 0, max_time);
323     Eigen::VectorXd measurements(timestamps.size());
324     Eigen::VectorXd sine_data = 50*(timestamps.array()*2*M_PI/period_length).sin();
325     Eigen::VectorXd controls(timestamps.size());
326     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
327 
328     // first option: the error was "compensated" and therefore only shows up in the controls
329     controls << sine_data.tail(sine_data.size()-1) - sine_data.head(sine_data.size()-1), 0;
330     measurements = 0*timestamps;
331 
332     // feed data to the GPGuider
333     for (int i = 0; i < timestamps.size(); ++i)
334     {
335         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
336     }
337     double controlled_result = GPG->result(0.0, 2.0, prediction_length, max_time);
338     GPG->reset();
339 
340     // second option: the error is not compensated and therefore visible in the measurement
341     controls = 0*controls;
342     measurements = sine_data;
343 
344     // feed data to the GPGuider
345     for (int i = 0; i < timestamps.size(); ++i)
346     {
347         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
348     }
349     double measured_result = GPG->result(0.0, 2.0, prediction_length, max_time);
350 
351     EXPECT_NEAR(measured_result, controlled_result, 1e-1);
352 
353     GPG->save_gp_data();
354 }
355 
356 // The period identification should work on real data, with irregular timestamps
TEST_F(GPGTest,real_data_test)357 TEST_F(GPGTest, real_data_test)
358 {
359     double time = 0.0;
360     double measurement = 0.0;
361     double SNR = 0.0;
362     double control = 0.0;
363 
364     std::ifstream file("dataset01.csv");
365 
366     int i = 0;
367     CSVRow row;
368     while(file >> row)
369     {
370         // ignore special lines: "INFO", "Frame", "DROP"
371         if (row[0][0] == 'I' || row[0][0] == 'F' || row[2][1] == 'D')
372         {
373             continue;
374         }
375         else
376         {
377             ++i;
378         }
379         time = std::stod(row[1]);
380         measurement = std::stod(row[5]);
381         control = std::stod(row[7]);
382         SNR = std::stod(row[16]);
383 
384         GPG->inject_data_point(time, measurement, SNR, control);
385     }
386 
387     EXPECT_GT(i, 0) << "dataset01.csv was empty or not present";
388 
389     GPG->result(0.0, 25.0, 3.0, time);
390 
391     EXPECT_NEAR(GPG->GetGPHyperparameters()[PKPeriodLength],483.0,5);
392 
393     GPG->save_gp_data();
394 }
395 
TEST_F(GPGTest,parameter_filter_test)396 TEST_F(GPGTest, parameter_filter_test)
397 {
398     double period_length = 0.0;
399 
400     std::ifstream infile("dataset02.csv");
401 
402     Eigen::VectorXd filtered_period_lengths(1000); // must be longer than the dataset
403 
404     std::vector<double> hypers = GPG->GetGPHyperparameters();
405     hypers[PKPeriodLength] = 483; // initialize close to final value
406     GPG->SetGPHyperparameters(hypers);
407     GPG->SetLearningRate(0.01);
408 
409     int i = 0;
410     CSVRow row;
411     while(infile >> row)
412     {
413         if (row[0][0] == 'p') // ignore the first line
414         {
415             continue;
416         }
417         else
418         {
419             ++i;
420         }
421         period_length = std::stod(row[0]);
422 
423         GPG->UpdatePeriodLength(period_length);
424         filtered_period_lengths(i - 1) = GPG->GetGPHyperparameters()[PKPeriodLength];
425     }
426     filtered_period_lengths.conservativeResize(i);
427 
428     EXPECT_GT(i, 0) << "dataset02.csv was empty or not present";
429 
430     double std_dev = std::numeric_limits<double>::infinity();
431 
432     if (i > 10)
433     {
434         Eigen::VectorXd period_lengths_tail = filtered_period_lengths.tail(10);
435         std_dev = math_tools::stdandard_deviation(period_lengths_tail);
436     }
437 
438     EXPECT_LT(std_dev, 0.1);
439 
440     GPG->save_gp_data();
441 }
442 
TEST_F(GPGTest,period_interpolation_test)443 TEST_F(GPGTest, period_interpolation_test)
444 {
445     // first: prepare a nice GP with a sine wave
446     double period_length = 317;
447     double max_time = 2345;
448     int resolution = 527;
449     Eigen::VectorXd timestamps = Eigen::VectorXd::LinSpaced(resolution + 1, 0, max_time);
450     Eigen::VectorXd measurements = 50*(timestamps.array()*2*M_PI/period_length).sin();
451     Eigen::VectorXd controls = 0*measurements;
452     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
453 
454     // feed data to the GPGuider
455     for (int i = 0; i < timestamps.size(); ++i)
456     {
457         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
458     }
459     GPG->result(0.15, 2.0, 3.0);
460 
461     EXPECT_NEAR(GPG->GetGPHyperparameters()[PKPeriodLength], period_length, 1e0);
462 
463     GPG->save_gp_data();
464 }
465 
TEST_F(GPGTest,data_regularization_test)466 TEST_F(GPGTest, data_regularization_test)
467 {
468     // first: prepare a nice GP with a sine wave
469     double period_length = 300;
470     double max_time = 20000;
471     int resolution = 8192;
472 
473     // second: mess up the grid of time stamps
474     Eigen::VectorXd timestamps(resolution);
475     timestamps << Eigen::VectorXd::LinSpaced(resolution/2, 0, max_time/6),
476         Eigen::VectorXd::LinSpaced(resolution/2, max_time/6 + 0.5, max_time);
477     timestamps += 0.5*math_tools::generate_normal_random_matrix(resolution, 1);
478 
479     Eigen::VectorXd measurements = 50*(timestamps.array()*2*M_PI/period_length).sin();
480     Eigen::VectorXd controls = 0*measurements;
481     Eigen::VectorXd SNRs = 100*Eigen::VectorXd::Ones(resolution + 1);
482 
483     // feed data to the GPGuider
484     for (int i = 0; i < timestamps.size(); ++i)
485     {
486         GPG->inject_data_point(timestamps[i], measurements[i], SNRs[i], controls[i]);
487     }
488     GPG->result(0.15, 2.0, 3.0);
489 
490     EXPECT_NEAR(GPG->GetGPHyperparameters()[PKPeriodLength], period_length, 1);
491 
492     GPG->save_gp_data();
493 }
494 
495 /**
496  * This "test" is used to log the identified period length to file. This functionality
497  * can be useful for debugging and for assessing the value of the period interpolation,
498  * data regulatization and Kalman filtering techniques.
499  */
TEST_F(GPGTest,DISABLED_log_period_length)500 TEST_F(GPGTest, DISABLED_log_period_length)
501 {
502     double time = 0.0;
503     double measurement = 0.0;
504     double SNR = 0.0;
505     double control = 0.0;
506 
507     std::ifstream file("dataset01.csv");
508 
509     std::ofstream outfile;
510     outfile.open("period_lengths_reg_int_kf.csv", std::ios_base::out);
511     outfile << "period_length\n";
512 
513     int i = 0;
514     CSVRow row;
515     while(file >> row)
516     {
517         // ignore special lines: "INFO", "Frame", "DROP"
518         if (row[0][0] == 'I' || row[0][0] == 'F' || row[2][1] == 'D')
519         {
520             continue;
521         }
522         else
523         {
524             ++i;
525         }
526         time = std::stod(row[1]);
527         measurement = std::stod(row[5]);
528         control = std::stod(row[7]);
529         SNR = std::stod(row[16]);
530 
531         GPG->inject_data_point(time, measurement, SNR, control);
532 
533         GPG->UpdateGP();
534         outfile << std::setw(8) << GPG->GetGPHyperparameters()[PKPeriodLength] << "\n";
535     }
536     outfile.close();
537 }
538 
539 // This is the dataset of an user who experienced a NaN-issue.
540 // It should, of course, return a non-NaN value (a.k.a.: a number).
TEST_F(GPGTest,real_data_test_nan_issue)541 TEST_F(GPGTest, real_data_test_nan_issue)
542 {
543     Eigen::ArrayXXd data = read_data_from_file("dataset03.csv");
544 
545     Eigen::ArrayXd controls = data.row(2);
546 
547     for (int i = 0; i < data.cols(); ++i)
548     {
549         GPG->inject_data_point(data(0,i), data(1,i), data(3,i), data(2,i));
550     }
551 
552     EXPECT_GT(data.cols(), 0) << "dataset was empty or not present";
553 
554     double result = GPG->result(0.622, 15.32, 2.0);
555 
556     EXPECT_FALSE(math_tools::isNaN(result));
557 
558     GPG->save_gp_data();
559 }
560 
main(int argc,char ** argv)561 int main(int argc, char** argv)
562 {
563     ::testing::InitGoogleTest(&argc, argv);
564     return RUN_ALL_TESTS();
565 }
566