bivariate_statistics_test.cpp 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170
  1. /*
  2. * (C) Copyright Nick Thompson 2018.
  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)
  6. */
  7. #include <iostream>
  8. #include <iomanip>
  9. #include <vector>
  10. #include <array>
  11. #include <forward_list>
  12. #include <algorithm>
  13. #include <random>
  14. #include <boost/core/lightweight_test.hpp>
  15. #include <boost/numeric/ublas/vector.hpp>
  16. #include <boost/math/constants/constants.hpp>
  17. #include <boost/math/statistics/univariate_statistics.hpp>
  18. #include <boost/math/statistics/bivariate_statistics.hpp>
  19. #include <boost/multiprecision/cpp_bin_float.hpp>
  20. #include <boost/multiprecision/cpp_complex.hpp>
  21. using boost::multiprecision::cpp_bin_float_50;
  22. using boost::multiprecision::cpp_complex_50;
  23. /*
  24. * Test checklist:
  25. * 1) Does it work with multiprecision?
  26. * 2) Does it work with .cbegin()/.cend() if the data is not altered?
  27. * 3) Does it work with ublas and std::array? (Checking Eigen and Armadillo will make the CI system really unhappy.)
  28. * 4) Does it work with std::forward_list if a forward iterator is all that is required?
  29. * 5) Does it work with complex data if complex data is sensible?
  30. */
  31. using boost::math::statistics::means_and_covariance;
  32. using boost::math::statistics::covariance;
  33. template<class Real>
  34. void test_covariance()
  35. {
  36. std::cout << std::setprecision(std::numeric_limits<Real>::digits10+1);
  37. Real tol = std::numeric_limits<Real>::epsilon();
  38. using std::abs;
  39. // Covariance of a single thing is zero:
  40. std::array<Real, 1> u1{8};
  41. std::array<Real, 1> v1{17};
  42. auto [mu_u1, mu_v1, cov1] = means_and_covariance(u1, v1);
  43. BOOST_TEST(abs(cov1) < tol);
  44. BOOST_TEST(abs(mu_u1 - 8) < tol);
  45. BOOST_TEST(abs(mu_v1 - 17) < tol);
  46. std::array<Real, 2> u2{8, 4};
  47. std::array<Real, 2> v2{3, 7};
  48. auto [mu_u2, mu_v2, cov2] = means_and_covariance(u2, v2);
  49. BOOST_TEST(abs(cov2+4) < tol);
  50. BOOST_TEST(abs(mu_u2 - 6) < tol);
  51. BOOST_TEST(abs(mu_v2 - 5) < tol);
  52. std::vector<Real> u3{1,2,3};
  53. std::vector<Real> v3{1,1,1};
  54. auto [mu_u3, mu_v3, cov3] = means_and_covariance(u3, v3);
  55. // Since v is constant, covariance(u,v) = 0 against everything any u:
  56. BOOST_TEST(abs(cov3) < tol);
  57. BOOST_TEST(abs(mu_u3 - 2) < tol);
  58. BOOST_TEST(abs(mu_v3 - 1) < tol);
  59. // Make sure we pull the correct symbol out of means_and_covariance:
  60. cov3 = covariance(u3, v3);
  61. BOOST_TEST(abs(cov3) < tol);
  62. cov3 = covariance(v3, u3);
  63. // Covariance is symmetric: cov(u,v) = cov(v,u)
  64. BOOST_TEST(abs(cov3) < tol);
  65. // cov(u,u) = sigma(u)^2:
  66. cov3 = covariance(u3, u3);
  67. Real expected = Real(2)/Real(3);
  68. BOOST_TEST(abs(cov3 - expected) < tol);
  69. std::mt19937 gen(15);
  70. // Can't template standard library on multiprecision, so use double and cast back:
  71. std::uniform_real_distribution<double> dis(-1.0, 1.0);
  72. std::vector<Real> u(500);
  73. std::vector<Real> v(500);
  74. for(size_t i = 0; i < u.size(); ++i)
  75. {
  76. u[i] = (Real) dis(gen);
  77. v[i] = (Real) dis(gen);
  78. }
  79. Real mu_u = boost::math::statistics::mean(u);
  80. Real mu_v = boost::math::statistics::mean(v);
  81. Real sigma_u_sq = boost::math::statistics::variance(u);
  82. Real sigma_v_sq = boost::math::statistics::variance(v);
  83. auto [mu_u_, mu_v_, cov_uv] = means_and_covariance(u, v);
  84. BOOST_TEST(abs(mu_u - mu_u_) < tol);
  85. BOOST_TEST(abs(mu_v - mu_v_) < tol);
  86. // Cauchy-Schwartz inequality:
  87. BOOST_TEST(cov_uv*cov_uv <= sigma_u_sq*sigma_v_sq);
  88. // cov(X, X) = sigma(X)^2:
  89. Real cov_uu = covariance(u, u);
  90. BOOST_TEST(abs(cov_uu - sigma_u_sq) < tol);
  91. Real cov_vv = covariance(v, v);
  92. BOOST_TEST(abs(cov_vv - sigma_v_sq) < tol);
  93. }
  94. template<class Real>
  95. void test_correlation_coefficient()
  96. {
  97. using boost::math::statistics::correlation_coefficient;
  98. Real tol = std::numeric_limits<Real>::epsilon();
  99. std::vector<Real> u{1};
  100. std::vector<Real> v{1};
  101. Real rho_uv = correlation_coefficient(u, v);
  102. BOOST_TEST(abs(rho_uv - 1) < tol);
  103. u = {1,1};
  104. v = {1,1};
  105. rho_uv = correlation_coefficient(u, v);
  106. BOOST_TEST(abs(rho_uv - 1) < tol);
  107. u = {1, 2, 3};
  108. v = {1, 2, 3};
  109. rho_uv = correlation_coefficient(u, v);
  110. BOOST_TEST(abs(rho_uv - 1) < tol);
  111. u = {1, 2, 3};
  112. v = {-1, -2, -3};
  113. rho_uv = correlation_coefficient(u, v);
  114. BOOST_TEST(abs(rho_uv + 1) < tol);
  115. rho_uv = correlation_coefficient(v, u);
  116. BOOST_TEST(abs(rho_uv + 1) < tol);
  117. u = {1, 2, 3};
  118. v = {0, 0, 0};
  119. rho_uv = correlation_coefficient(v, u);
  120. BOOST_TEST(abs(rho_uv) < tol);
  121. u = {1, 2, 3};
  122. v = {0, 0, 3};
  123. rho_uv = correlation_coefficient(v, u);
  124. // mu_u = 2, sigma_u^2 = 2/3, mu_v = 1, sigma_v^2 = 2, cov(u,v) = 1.
  125. BOOST_TEST(abs(rho_uv - sqrt(Real(3))/Real(2)) < tol);
  126. }
  127. int main()
  128. {
  129. test_covariance<float>();
  130. test_covariance<double>();
  131. test_covariance<long double>();
  132. test_covariance<cpp_bin_float_50>();
  133. test_correlation_coefficient<float>();
  134. test_correlation_coefficient<double>();
  135. test_correlation_coefficient<long double>();
  136. test_correlation_coefficient<cpp_bin_float_50>();
  137. return boost::report_errors();
  138. }