2 * Copyright Nick Thompson, 2020
3 * Use, modification and distribution are subject to the
4 * Boost Software License, Version 1.0. (See accompanying file
5 * LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
8 #include "math_unit_test.hpp"
13 #include <boost/random/uniform_real.hpp>
14 #include <boost/random/mersenne_twister.hpp>
15 #include <boost/math/interpolators/quintic_hermite.hpp>
16 #include <boost/math/special_functions/next.hpp>
17 #include <boost/circular_buffer.hpp>
18 #ifdef BOOST_HAS_FLOAT128
19 #include <boost/multiprecision/float128.hpp>
20 using boost::multiprecision::float128
;
24 using boost::math::interpolators::quintic_hermite
;
25 using boost::math::interpolators::cardinal_quintic_hermite
;
26 using boost::math::interpolators::cardinal_quintic_hermite_aos
;
28 template<typename Real
>
31 std::vector
<Real
> x
{0,1,2,3, 9, 22, 81};
32 std::vector
<Real
> y(x
.size());
33 std::vector
<Real
> dydx(x
.size(), 0);
34 std::vector
<Real
> d2ydx2(x
.size(), 0);
40 auto qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
41 for (Real t
= 0; t
<= 81; t
+= 0.25)
43 CHECK_ULP_CLOSE(Real(7), qh(t
), 24);
44 CHECK_ULP_CLOSE(Real(0), qh
.prime(t
), 24);
45 CHECK_ULP_CLOSE(Real(0), qh
.double_prime(t
), 24);
50 template<typename Real
>
53 std::vector
<Real
> x
{0,1,2,3, 4,5,6,7,8,9};
54 std::vector
<Real
> y
= x
;
55 std::vector
<Real
> dydx(x
.size(), 1);
56 std::vector
<Real
> d2ydx2(x
.size(), 0);
58 auto qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
60 for (Real t
= 0; t
<= 9; t
+= 0.25)
62 CHECK_ULP_CLOSE(Real(t
), qh(t
), 2);
63 CHECK_ULP_CLOSE(Real(1), qh
.prime(t
), 2);
64 CHECK_ULP_CLOSE(Real(0), qh
.double_prime(t
), 2);
67 boost::random::mt19937 rng
;
68 boost::random::uniform_real_distribution
<Real
> dis(0.5,1);
72 for (size_t i
= 1; i
< x
.size(); ++i
)
74 x
[i
] = x
[i
-1] + dis(rng
);
79 dydx
.resize(x
.size(), 1);
80 d2ydx2
.resize(x
.size(), 0);
82 qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
84 for (Real t
= xmin
; t
<= xmax
; t
+= 0.125)
86 CHECK_ULP_CLOSE(t
, qh(t
), 2);
87 CHECK_ULP_CLOSE(Real(1), qh
.prime(t
), 100);
88 CHECK_MOLLIFIED_CLOSE(Real(0), qh
.double_prime(t
), 200*std::numeric_limits
<Real
>::epsilon());
92 template<typename Real
>
96 std::vector
<Real
> x
{0,1,2,3, 4,5,6,7,8,9};
97 std::vector
<Real
> y(x
.size());
98 for (size_t i
= 0; i
< y
.size(); ++i
)
103 std::vector
<Real
> dydx(x
.size());
104 for (size_t i
= 0; i
< y
.size(); ++i
) {
108 std::vector
<Real
> d2ydx2(x
.size(), 1);
110 auto qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
112 for (Real t
= 0; t
<= 9; t
+= 0.0078125)
114 CHECK_ULP_CLOSE(Real(t
*t
)/2, qh(t
), 2);
115 CHECK_ULP_CLOSE(t
, qh
.prime(t
), 12);
116 CHECK_ULP_CLOSE(Real(1), qh
.double_prime(t
), 32);
119 boost::random::mt19937 rng
;
120 boost::random::uniform_real_distribution
<Real
> dis(0.5,1);
124 for (size_t i
= 1; i
< x
.size(); ++i
)
126 x
[i
] = x
[i
-1] + dis(rng
);
128 Real xmax
= x
.back();
131 for (size_t i
= 0; i
< y
.size(); ++i
)
136 dydx
.resize(x
.size());
137 for (size_t i
= 0; i
< y
.size(); ++i
)
142 d2ydx2
.resize(x
.size(), 1);
144 qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
146 for (Real t
= xmin
; t
<= xmax
; t
+= 0.125)
148 CHECK_ULP_CLOSE(Real(t
*t
)/2, qh(t
), 4);
149 CHECK_ULP_CLOSE(t
, qh
.prime(t
), 53);
150 CHECK_ULP_CLOSE(Real(1), qh
.double_prime(t
), 700);
154 template<typename Real
>
158 std::vector
<Real
> x
{0,1,2,3, 4,5,6,7,8,9};
159 std::vector
<Real
> y(x
.size());
160 for (size_t i
= 0; i
< y
.size(); ++i
)
162 y
[i
] = x
[i
]*x
[i
]*x
[i
];
165 std::vector
<Real
> dydx(x
.size());
166 for (size_t i
= 0; i
< y
.size(); ++i
) {
167 dydx
[i
] = 3*x
[i
]*x
[i
];
170 std::vector
<Real
> d2ydx2(x
.size());
171 for (size_t i
= 0; i
< y
.size(); ++i
)
176 auto qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
178 for (Real t
= 0; t
<= 9; t
+= 0.0078125)
180 CHECK_ULP_CLOSE(t
*t
*t
, qh(t
), 10);
181 CHECK_ULP_CLOSE(3*t
*t
, qh
.prime(t
), 15);
182 CHECK_ULP_CLOSE(6*t
, qh
.double_prime(t
), 20);
186 template<typename Real
>
190 std::vector
<Real
> x
{0,1,2,3, 4,5,6,7,8,9, 10, 11};
191 std::vector
<Real
> y(x
.size());
192 for (size_t i
= 0; i
< y
.size(); ++i
)
194 y
[i
] = x
[i
]*x
[i
]*x
[i
]*x
[i
];
197 std::vector
<Real
> dydx(x
.size());
198 for (size_t i
= 0; i
< y
.size(); ++i
)
200 dydx
[i
] = 4*x
[i
]*x
[i
]*x
[i
];
203 std::vector
<Real
> d2ydx2(x
.size());
204 for (size_t i
= 0; i
< y
.size(); ++i
)
206 d2ydx2
[i
] = 12*x
[i
]*x
[i
];
209 auto qh
= quintic_hermite(std::move(x
), std::move(y
), std::move(dydx
), std::move(d2ydx2
));
211 for (Real t
= 1; t
<= 11; t
+= 0.0078125)
213 CHECK_ULP_CLOSE(t
*t
*t
*t
, qh(t
), 100);
214 CHECK_ULP_CLOSE(4*t
*t
*t
, qh
.prime(t
), 100);
215 CHECK_ULP_CLOSE(12*t
*t
, qh
.double_prime(t
), 100);
220 template<typename Real
>
221 void test_interpolation_condition()
223 for (size_t n
= 4; n
< 50; ++n
) {
224 std::vector
<Real
> x(n
);
225 std::vector
<Real
> y(n
);
226 std::vector
<Real
> dydx(n
);
227 std::vector
<Real
> d2ydx2(n
);
228 boost::random::mt19937 rd
;
229 boost::random::uniform_real_distribution
<Real
> dis(0,1);
233 for (size_t i
= 1; i
< n
; ++i
) {
234 x
[i
] = x
[i
-1] + dis(rd
);
242 auto dydx_copy
= dydx
;
243 auto d2ydx2_copy
= d2ydx2
;
244 auto s
= quintic_hermite(std::move(x_copy
), std::move(y_copy
), std::move(dydx_copy
), std::move(d2ydx2_copy
));
245 //std::cout << "s = " << s << "\n";
246 for (size_t i
= 0; i
< x
.size(); ++i
) {
247 CHECK_ULP_CLOSE(y
[i
], s(x
[i
]), 2);
248 CHECK_ULP_CLOSE(dydx
[i
], s
.prime(x
[i
]), 2);
249 CHECK_ULP_CLOSE(d2ydx2
[i
], s
.double_prime(x
[i
]), 2);
254 template<typename Real
>
255 void test_cardinal_constant()
258 std::vector
<Real
> y(25);
259 std::vector
<Real
> dydx(y
.size(), 0);
260 std::vector
<Real
> d2ydx2(y
.size(), 0);
265 Real dx
= Real(1)/Real(8);
267 auto qh
= cardinal_quintic_hermite(std::move(y
), std::move(dydx
), std::move(d2ydx2
), x0
, dx
);
269 for (Real t
= x0
; t
<= x0
+ 24*dx
; t
+= 0.25)
271 CHECK_ULP_CLOSE(Real(7), qh(t
), 24);
272 CHECK_ULP_CLOSE(Real(0), qh
.prime(t
), 24);
273 CHECK_ULP_CLOSE(Real(0), qh
.double_prime(t
), 24);
276 std::vector
<std::array
<Real
, 3>> data(25);
277 for (size_t i
= 0; i
< data
.size(); ++i
)
284 auto qh_aos
= cardinal_quintic_hermite_aos(std::move(data
), x0
, dx
);
285 for (Real t
= x0
; t
<= x0
+ 24*dx
; t
+= 0.25)
287 CHECK_ULP_CLOSE(Real(7), qh_aos(t
), 24);
288 CHECK_ULP_CLOSE(Real(0), qh_aos
.prime(t
), 24);
289 CHECK_ULP_CLOSE(Real(0), qh_aos
.double_prime(t
), 24);
292 // Now check the boundaries:
293 auto [tlo
, thi
] = qh
.domain();
296 while (i
++ < samples
)
298 CHECK_ULP_CLOSE(Real(7), qh(tlo
), 2);
299 CHECK_ULP_CLOSE(Real(7), qh(thi
), 2);
300 CHECK_ULP_CLOSE(Real(7), qh_aos(tlo
), 2);
301 CHECK_ULP_CLOSE(Real(7), qh_aos(thi
), 2);
302 CHECK_ULP_CLOSE(Real(0), qh
.prime(tlo
), 2);
303 CHECK_ULP_CLOSE(Real(0), qh
.prime(thi
), 2);
304 CHECK_ULP_CLOSE(Real(0), qh_aos
.prime(tlo
), 2);
305 CHECK_ULP_CLOSE(Real(0), qh_aos
.prime(thi
), 2);
307 tlo
= boost::math::nextafter(tlo
, (std::numeric_limits
<Real
>::max
)());
308 thi
= boost::math::nextafter(thi
, std::numeric_limits
<Real
>::lowest());
313 template<typename Real
>
314 void test_cardinal_linear()
316 std::vector
<Real
> y
{0,1,2,3,4,5,6,7,8,9};
319 std::vector
<Real
> dydx(y
.size(), 1);
320 std::vector
<Real
> d2ydx2(y
.size(), 0);
322 auto qh
= cardinal_quintic_hermite(std::move(y
), std::move(dydx
), std::move(d2ydx2
), x0
, dx
);
324 for (Real t
= 0; t
<= 9; t
+= 0.25) {
325 CHECK_ULP_CLOSE(Real(t
), qh(t
), 2);
326 CHECK_ULP_CLOSE(Real(1), qh
.prime(t
), 2);
327 CHECK_ULP_CLOSE(Real(0), qh
.double_prime(t
), 2);
330 std::vector
<std::array
<Real
, 3>> data(10);
331 for (size_t i
= 0; i
< data
.size(); ++i
) {
337 auto qh_aos
= cardinal_quintic_hermite_aos(std::move(data
), x0
, dx
);
339 for (Real t
= 0; t
<= 9; t
+= 0.25) {
340 CHECK_ULP_CLOSE(Real(t
), qh_aos(t
), 2);
341 CHECK_ULP_CLOSE(Real(1), qh_aos
.prime(t
), 2);
342 CHECK_ULP_CLOSE(Real(0), qh_aos
.double_prime(t
), 2);
345 // Now check the boundaries:
346 auto [tlo
, thi
] = qh
.domain();
349 while (i
++ < samples
)
351 CHECK_ULP_CLOSE(Real(tlo
), qh(tlo
), 2);
352 CHECK_ULP_CLOSE(Real(thi
), qh(thi
), 2);
353 CHECK_ULP_CLOSE(Real(tlo
), qh_aos(tlo
), 2);
354 CHECK_ULP_CLOSE(Real(thi
), qh_aos(thi
), 2);
355 CHECK_ULP_CLOSE(Real(1), qh
.prime(tlo
), 2);
356 CHECK_ULP_CLOSE(Real(1), qh
.prime(thi
), 128);
357 CHECK_ULP_CLOSE(Real(1), qh_aos
.prime(tlo
), 2);
358 CHECK_ULP_CLOSE(Real(1), qh_aos
.prime(thi
), 128);
360 tlo
= boost::math::nextafter(tlo
, (std::numeric_limits
<Real
>::max
)());
361 thi
= boost::math::nextafter(thi
, std::numeric_limits
<Real
>::lowest());
365 template<typename Real
>
366 void test_cardinal_quadratic()
370 std::vector
<Real
> y(10);
371 for (size_t i
= 0; i
< y
.size(); ++i
)
376 std::vector
<Real
> dydx(y
.size());
377 for (size_t i
= 0; i
< y
.size(); ++i
) {
381 std::vector
<Real
> d2ydx2(y
.size(), 1);
383 auto qh
= cardinal_quintic_hermite(std::move(y
), std::move(dydx
), std::move(d2ydx2
), x0
, dx
);
385 for (Real t
= 0; t
<= 9; t
+= 0.0078125) {
386 Real computed
= qh(t
);
387 CHECK_ULP_CLOSE(Real(t
*t
)/2, computed
, 2);
388 CHECK_ULP_CLOSE(t
, qh
.prime(t
), 15);
389 CHECK_ULP_CLOSE(Real(1), qh
.double_prime(t
), 32);
392 std::vector
<std::array
<Real
, 3>> data(10);
393 for (size_t i
= 0; i
< data
.size(); ++i
) {
394 data
[i
][0] = i
*i
/Real(2);
398 auto qh_aos
= cardinal_quintic_hermite_aos(std::move(data
), x0
, dx
);
400 for (Real t
= 0; t
<= 9; t
+= 0.0078125)
402 Real computed
= qh_aos(t
);
403 CHECK_ULP_CLOSE(Real(t
*t
)/2, computed
, 2);
404 CHECK_ULP_CLOSE(t
, qh_aos
.prime(t
), 12);
405 CHECK_ULP_CLOSE(Real(1), qh_aos
.double_prime(t
), 64);
408 // Now check the boundaries:
409 auto [tlo
, thi
] = qh
.domain();
412 while (i
++ < samples
)
414 CHECK_ULP_CLOSE(tlo
*tlo
/2, qh(tlo
), 16);
415 CHECK_ULP_CLOSE(thi
*thi
/2, qh(thi
), 16);
416 CHECK_ULP_CLOSE(tlo
*tlo
/2, qh_aos(tlo
), 16);
417 CHECK_ULP_CLOSE(thi
*thi
/2, qh_aos(thi
), 16);
418 CHECK_ULP_CLOSE(tlo
, qh
.prime(tlo
), 16);
419 CHECK_ULP_CLOSE(thi
, qh
.prime(thi
), 64);
420 CHECK_ULP_CLOSE(tlo
, qh_aos
.prime(tlo
), 16);
421 CHECK_ULP_CLOSE(thi
, qh_aos
.prime(thi
), 64);
423 tlo
= boost::math::nextafter(tlo
, (std::numeric_limits
<Real
>::max
)());
424 thi
= boost::math::nextafter(thi
, std::numeric_limits
<Real
>::lowest());
428 template<typename Real
>
429 void test_cardinal_cubic()
433 std::vector
<Real
> y(10);
434 for (size_t i
= 0; i
< y
.size(); ++i
)
439 std::vector
<Real
> dydx(y
.size());
440 for (size_t i
= 0; i
< y
.size(); ++i
) {
444 std::vector
<Real
> d2ydx2(y
.size());
445 for (size_t i
= 0; i
< y
.size(); ++i
) {
449 auto qh
= cardinal_quintic_hermite(std::move(y
), std::move(dydx
), std::move(d2ydx2
), x0
, dx
);
451 for (Real t
= 0; t
<= 9; t
+= 0.0078125)
453 Real computed
= qh(t
);
454 CHECK_ULP_CLOSE(t
*t
*t
, computed
, 10);
455 CHECK_ULP_CLOSE(3*t
*t
, qh
.prime(t
), 15);
456 CHECK_ULP_CLOSE(6*t
, qh
.double_prime(t
), 39);
459 std::vector
<std::array
<Real
, 3>> data(10);
460 for (size_t i
= 0; i
< data
.size(); ++i
) {
466 auto qh_aos
= cardinal_quintic_hermite_aos(std::move(data
), x0
, dx
);
467 for (Real t
= 0; t
<= 9; t
+= 0.0078125)
469 Real computed
= qh_aos(t
);
470 CHECK_ULP_CLOSE(t
*t
*t
, computed
, 10);
471 CHECK_ULP_CLOSE(3*t
*t
, qh_aos
.prime(t
), 15);
472 CHECK_ULP_CLOSE(6*t
, qh_aos
.double_prime(t
), 30);
476 template<typename Real
>
477 void test_cardinal_quartic()
481 std::vector
<Real
> y(7);
482 for (size_t i
= 0; i
< y
.size(); ++i
)
487 std::vector
<Real
> dydx(y
.size());
488 for (size_t i
= 0; i
< y
.size(); ++i
) {
492 std::vector
<Real
> d2ydx2(y
.size());
493 for (size_t i
= 0; i
< y
.size(); ++i
) {
497 auto qh
= cardinal_quintic_hermite(std::move(y
), std::move(dydx
), std::move(d2ydx2
), x0
, dx
);
499 for (Real t
= 0; t
<= 6; t
+= 0.0078125)
501 CHECK_ULP_CLOSE(Real(t
*t
*t
*t
), qh(t
), 250);
502 CHECK_ULP_CLOSE(4*t
*t
*t
, qh
.prime(t
), 250);
503 CHECK_ULP_CLOSE(12*t
*t
, qh
.double_prime(t
), 250);
506 std::vector
<std::array
<Real
, 3>> data(7);
507 for (size_t i
= 0; i
< data
.size(); ++i
) {
508 data
[i
][0] = i
*i
*i
*i
;
509 data
[i
][1] = 4*i
*i
*i
;
513 auto qh_aos
= cardinal_quintic_hermite_aos(std::move(data
), x0
, dx
);
514 for (Real t
= 0; t
<= 6; t
+= 0.0078125)
516 Real computed
= qh_aos(t
);
517 CHECK_ULP_CLOSE(t
*t
*t
*t
, computed
, 10);
518 CHECK_ULP_CLOSE(4*t
*t
*t
, qh_aos
.prime(t
), 64);
519 CHECK_ULP_CLOSE(12*t
*t
, qh_aos
.double_prime(t
), 128);
526 test_constant
<float>();
527 test_linear
<float>();
528 test_quadratic
<float>();
530 test_quartic
<float>();
531 test_interpolation_condition
<float>();
533 test_cardinal_constant
<float>();
534 test_cardinal_linear
<float>();
535 test_cardinal_quadratic
<float>();
536 test_cardinal_cubic
<float>();
537 test_cardinal_quartic
<float>();
539 test_constant
<double>();
540 test_linear
<double>();
541 test_quadratic
<double>();
542 test_cubic
<double>();
543 test_quartic
<double>();
544 test_interpolation_condition
<double>();
546 test_cardinal_constant
<double>();
547 test_cardinal_linear
<double>();
548 test_cardinal_quadratic
<double>();
549 test_cardinal_cubic
<double>();
550 test_cardinal_quartic
<double>();
552 test_constant
<long double>();
553 test_linear
<long double>();
554 test_quadratic
<long double>();
555 test_cubic
<long double>();
556 test_quartic
<long double>();
557 test_interpolation_condition
<long double>();
559 test_cardinal_constant
<long double>();
560 test_cardinal_linear
<long double>();
561 test_cardinal_quadratic
<long double>();
562 test_cardinal_cubic
<long double>();
563 test_cardinal_quartic
<long double>();
565 #ifdef BOOST_HAS_FLOAT128
566 test_constant
<float128
>();
567 //test_linear<float128>();
568 test_quadratic
<float128
>();
569 test_cubic
<float128
>();
570 test_quartic
<float128
>();
571 test_interpolation_condition
<float128
>();
572 test_cardinal_constant
<float128
>();
573 test_cardinal_linear
<float128
>();
574 test_cardinal_quadratic
<float128
>();
575 test_cardinal_cubic
<float128
>();
576 test_cardinal_quartic
<float128
>();
579 return boost::math::test::report_errors();