mirror of
https://github.com/boostorg/math.git
synced 2026-02-24 16:12:15 +00:00
Merge branch 'non-central-f-inverse' of https://github.com/JacobHass8/math into pr1345
This commit is contained in:
@@ -26,6 +26,11 @@
|
||||
|
||||
// Accessor to non-centrality parameter lambda:
|
||||
BOOST_MATH_GPU_ENABLED RealType non_centrality()const;
|
||||
|
||||
// Parameter finders:
|
||||
BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const RealType x, const RealType v1, const RealType v2, const RealType p);
|
||||
template <class A, class B, class C, class D>
|
||||
BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const complemented3_type<A,B,C,D>& c);
|
||||
};
|
||||
|
||||
}} // namespaces
|
||||
@@ -53,6 +58,20 @@ for different values of [lambda]:
|
||||
|
||||
[graph nc_f_pdf]
|
||||
|
||||
BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const RealType x, const RealType v1, const RealType v2, const RealType p);
|
||||
|
||||
This function returns the non centrality parameter /lambda/ such that:
|
||||
|
||||
`cdf(non_central_chi_squared<RealType, Policy>(v1, v2, lambda), x) == p`
|
||||
|
||||
template <class A, class B, class C, class D>
|
||||
BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const complemented4_type<A,B,C,D>& c);
|
||||
|
||||
When called with argument `boost::math::complement(x, v1, v2, q)`
|
||||
this function returns the non centrality parameter /lambda/ such that:
|
||||
|
||||
`cdf(complement(non_central_chi_squared<RealType, Policy>(v1, v2, lambda), x)) == q`.
|
||||
|
||||
[h4 Member Functions]
|
||||
|
||||
BOOST_MATH_GPU_ENABLED non_central_f_distribution(RealType v1, RealType v2, RealType lambda);
|
||||
|
||||
@@ -17,11 +17,71 @@
|
||||
#include <boost/math/distributions/detail/generic_mode.hpp>
|
||||
#include <boost/math/special_functions/pow.hpp>
|
||||
#include <boost/math/policies/policy.hpp>
|
||||
#include <boost/math/distributions/complement.hpp> // complements
|
||||
#include <boost/math/distributions/fisher_f.hpp>
|
||||
#include <boost/math/special_functions/relative_difference.hpp>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
namespace math
|
||||
{
|
||||
namespace detail
|
||||
{
|
||||
template <class RealType, class Policy>
|
||||
struct non_centrality_finder_f
|
||||
{
|
||||
BOOST_MATH_GPU_ENABLED non_centrality_finder_f(const RealType x_, const RealType v1_, const RealType v2_, const RealType p_, const bool c)
|
||||
: x(x_), v1(v1_), v2(v2_), p(p_), comp(c) {}
|
||||
|
||||
BOOST_MATH_GPU_ENABLED RealType operator()(RealType nc) const
|
||||
{
|
||||
non_central_f_distribution<RealType, Policy> d(v1, v2, nc);
|
||||
return comp ?
|
||||
RealType(p - cdf(complement(d, x)))
|
||||
: RealType(cdf(d, x) - p);
|
||||
}
|
||||
private:
|
||||
RealType x, v1, v2, p;
|
||||
bool comp;
|
||||
};
|
||||
|
||||
template <class RealType, class Policy>
|
||||
BOOST_MATH_GPU_ENABLED RealType find_non_centrality_f(const RealType x, const RealType v1, const RealType v2, const RealType p, const RealType q, const Policy& pol)
|
||||
{
|
||||
constexpr auto function = "non_central_f<%1%>::find_non_centrality";
|
||||
|
||||
if ( p == 0 || q == 0) {
|
||||
return policies::raise_domain_error<RealType>(function, "Can't find non centrality parameter when the probability is <=0 or >=1, only possible answer is %1%", // LCOV_EXCL_LINE
|
||||
RealType(boost::math::numeric_limits<RealType>::quiet_NaN()), Policy()); // LCOV_EXCL_LINE
|
||||
}
|
||||
|
||||
// Check if nc = 0 (which is just the F-distribution)
|
||||
// I first tried comparing to boost::math::tools::epsilon<RealType>(),
|
||||
// but this was never satisfied for floats or doubles. Only
|
||||
// long doubles would correctly return 0.
|
||||
fisher_f_distribution<RealType, Policy> dist(v1, v2);
|
||||
if (boost::math::relative_difference(pdf(dist, x), p) < 1e-7){
|
||||
return 0;
|
||||
}
|
||||
|
||||
non_centrality_finder_f<RealType, Policy> f(x, v1, v2, p < q ? p : q, p < q ? false : true);
|
||||
RealType guess = RealType(10); // Starting guess.
|
||||
RealType factor = RealType(2); // How big steps to take when searching.
|
||||
boost::math::uintmax_t max_iter = policies::get_max_root_iterations<Policy>();
|
||||
tools::eps_tolerance<RealType> tol(policies::digits<RealType, Policy>());
|
||||
|
||||
boost::math::pair<RealType, RealType> result_bracket = tools::bracket_and_solve_root(
|
||||
f, guess, factor, false, tol, max_iter, pol);
|
||||
|
||||
RealType result = result_bracket.first + (result_bracket.second - result_bracket.first)/2;
|
||||
if (max_iter >= policies::get_max_root_iterations<Policy>()) {
|
||||
return policies::raise_evaluation_error<RealType>(function, "Unable to locate solution in a reasonable time:" // LCOV_EXCL_LINE
|
||||
" or there is no answer to problem. Current best guess is %1%", result, Policy()); // LCOV_EXCL_LINE
|
||||
}
|
||||
return result;
|
||||
}
|
||||
} // namespace detail
|
||||
|
||||
template <class RealType = double, class Policy = policies::policy<> >
|
||||
class non_central_f_distribution
|
||||
{
|
||||
@@ -58,6 +118,49 @@ namespace boost
|
||||
{ // Private data getter function.
|
||||
return ncp;
|
||||
}
|
||||
BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const RealType x, const RealType v1, const RealType v2, const RealType p)
|
||||
{
|
||||
constexpr auto function = "non_central_f_distribution<%1%>::find_non_centrality";
|
||||
typedef typename policies::evaluation<RealType, Policy>::type eval_type;
|
||||
typedef typename policies::normalise<
|
||||
Policy,
|
||||
policies::promote_float<false>,
|
||||
policies::promote_double<false>,
|
||||
policies::discrete_quantile<>,
|
||||
policies::assert_undefined<> >::type forwarding_policy;
|
||||
eval_type result = detail::find_non_centrality_f(
|
||||
static_cast<eval_type>(x),
|
||||
static_cast<eval_type>(v1),
|
||||
static_cast<eval_type>(v2),
|
||||
static_cast<eval_type>(p),
|
||||
static_cast<eval_type>(1-p),
|
||||
forwarding_policy());
|
||||
return policies::checked_narrowing_cast<RealType, forwarding_policy>(
|
||||
result,
|
||||
function);
|
||||
}
|
||||
template <class A, class B, class C, class D>
|
||||
BOOST_MATH_GPU_ENABLED static RealType find_non_centrality(const complemented4_type<A,B,C, D>& c)
|
||||
{
|
||||
constexpr auto function = "non_central_f_distribution<%1%>::find_non_centrality";
|
||||
typedef typename policies::evaluation<RealType, Policy>::type eval_type;
|
||||
typedef typename policies::normalise<
|
||||
Policy,
|
||||
policies::promote_float<false>,
|
||||
policies::promote_double<false>,
|
||||
policies::discrete_quantile<>,
|
||||
policies::assert_undefined<> >::type forwarding_policy;
|
||||
eval_type result = detail::find_non_centrality_f(
|
||||
static_cast<eval_type>(c.dist),
|
||||
static_cast<eval_type>(c.param1),
|
||||
static_cast<eval_type>(c.param2),
|
||||
static_cast<eval_type>(1-c.param3),
|
||||
static_cast<eval_type>(c.param3),
|
||||
forwarding_policy());
|
||||
return policies::checked_narrowing_cast<RealType, forwarding_policy>(
|
||||
result,
|
||||
function);
|
||||
}
|
||||
private:
|
||||
// Data member, initialized by constructor.
|
||||
RealType v1; // alpha.
|
||||
@@ -404,7 +507,6 @@ namespace boost
|
||||
Policy());
|
||||
return (x / (1 - x)) * (c.dist.degrees_of_freedom2() / c.dist.degrees_of_freedom1());
|
||||
} // quantile complement.
|
||||
|
||||
} // namespace math
|
||||
} // namespace boost
|
||||
|
||||
|
||||
@@ -165,6 +165,8 @@ run test_nc_f_pdf_double.cu ;
|
||||
run test_nc_f_pdf_float.cu ;
|
||||
run test_nc_f_quan_double.cu ;
|
||||
run test_nc_f_quan_float.cu ;
|
||||
run test_nc_f_finder_double.cu ;
|
||||
run test_nc_f_finder_float.cu ;
|
||||
|
||||
run test_nc_chi_squared_cdf_double.cu ;
|
||||
run test_nc_chi_squared_cdf_float.cu ;
|
||||
|
||||
@@ -141,6 +141,10 @@ void test_spot(
|
||||
quantile(dist, P), x, tol * 10);
|
||||
BOOST_CHECK_CLOSE(
|
||||
quantile(complement(dist, Q)), x, tol * 10);
|
||||
BOOST_CHECK_CLOSE(
|
||||
dist.find_non_centrality(x, a, b, P), ncp, tol * 10);
|
||||
BOOST_CHECK_CLOSE(
|
||||
dist.find_non_centrality(boost::math::complement(x, a, b, Q)), ncp, tol * 10);
|
||||
}
|
||||
if(boost::math::tools::digits<RealType>() > 50)
|
||||
{
|
||||
@@ -323,6 +327,25 @@ void test_spots(RealType)
|
||||
{
|
||||
BOOST_CHECK_CLOSE(cdf(boost::math::non_central_f_distribution<RealType>(static_cast<RealType>(1e-100L), 3.f, 1.5f), static_cast<RealType>(1e100L)), static_cast<RealType>(0.6118152873453990639132215575213809716459L), tolerance);
|
||||
}
|
||||
|
||||
// Check find_non_centrality_f edge case handling
|
||||
// Case when nc=0
|
||||
RealType a = 5;
|
||||
RealType b = 2;
|
||||
RealType nc = 0;
|
||||
RealType x_vals[] = { 0.25, 1.25, 10, 100};
|
||||
boost::math::non_central_f_distribution<RealType> dist_no_centrality(a, b, nc);
|
||||
for (RealType x : x_vals)
|
||||
{
|
||||
RealType P = pdf(dist_no_centrality, x);
|
||||
BOOST_CHECK_CLOSE(dist.find_non_centrality(x, a, b, P), static_cast<RealType>(0), tolerance);
|
||||
}
|
||||
// Case when P=1 or P=0
|
||||
BOOST_MATH_CHECK_THROW(dist.find_non_centrality(x, a, b, 1), std::domain_error);
|
||||
BOOST_MATH_CHECK_THROW(dist.find_non_centrality(x, a, b, 0), std::domain_error);
|
||||
// Case when Q=1 or Q=0
|
||||
BOOST_MATH_CHECK_THROW(dist.find_non_centrality(boost::math::complement(x, a, b, 1)), std::domain_error);
|
||||
BOOST_MATH_CHECK_THROW(dist.find_non_centrality(boost::math::complement(x, a, b, 0)), std::domain_error);
|
||||
} // template <class RealType>void test_spots(RealType)
|
||||
|
||||
BOOST_AUTO_TEST_CASE( test_main )
|
||||
|
||||
114
test/test_nc_f_finder_double.cu
Normal file
114
test/test_nc_f_finder_double.cu
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright John Maddock 2016.
|
||||
// Copyright Matt Borland 2024.
|
||||
// Use, modification and distribution are subject to the
|
||||
// Boost Software License, Version 1.0. (See accompanying file
|
||||
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <boost/math/distributions/non_central_f.hpp>
|
||||
#include <boost/math/special_functions/relative_difference.hpp>
|
||||
#include <boost/random/mersenne_twister.hpp>
|
||||
#include <boost/random/uniform_real_distribution.hpp>
|
||||
#include "cuda_managed_ptr.hpp"
|
||||
#include "stopwatch.hpp"
|
||||
|
||||
// For the CUDA runtime routines (prefixed with "cuda_")
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
typedef double float_type;
|
||||
|
||||
/**
|
||||
* CUDA Kernel Device code
|
||||
*
|
||||
*/
|
||||
__global__ void cuda_test(const float_type *in1, float_type *out, int numElements)
|
||||
{
|
||||
int i = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
|
||||
if (i < numElements)
|
||||
{
|
||||
boost::math::non_central_f_distribution<float_type> dist(5, 5, 5);
|
||||
out[i] = dist.find_non_centrality(5, 5, 5, in1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Host main routine
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
try{
|
||||
|
||||
// Error code to check return values for CUDA calls
|
||||
cudaError_t err = cudaSuccess;
|
||||
|
||||
// Print the vector length to be used, and compute its size
|
||||
int numElements = 50000;
|
||||
std::cout << "[Vector operation on " << numElements << " elements]" << std::endl;
|
||||
|
||||
// Allocate the managed input vector A
|
||||
cuda_managed_ptr<float_type> input_vector1(numElements);
|
||||
|
||||
// Allocate the managed output vector C
|
||||
cuda_managed_ptr<float_type> output_vector(numElements);
|
||||
|
||||
boost::random::mt19937 gen;
|
||||
boost::random::uniform_real_distribution<float_type> dist(0.1, 0.9);
|
||||
// Initialize the input vectors
|
||||
for (int i = 0; i < numElements; ++i)
|
||||
{
|
||||
input_vector1[i] = dist(gen);
|
||||
}
|
||||
|
||||
// Launch the Vector Add CUDA Kernel
|
||||
int threadsPerBlock = 256;
|
||||
int blocksPerGrid =(numElements + threadsPerBlock - 1) / threadsPerBlock;
|
||||
std::cout << "CUDA kernel launch with " << blocksPerGrid << " blocks of " << threadsPerBlock << " threads" << std::endl;
|
||||
|
||||
watch w;
|
||||
cuda_test<<<blocksPerGrid, threadsPerBlock>>>(input_vector1.get(), output_vector.get(), numElements);
|
||||
cudaDeviceSynchronize();
|
||||
std::cout << "CUDA kernal done in " << w.elapsed() << "s" << std::endl;
|
||||
|
||||
err = cudaGetLastError();
|
||||
if (err != cudaSuccess)
|
||||
{
|
||||
std::cerr << "Failed to launch non_central_f distribution kernel (error code " << cudaGetErrorString(err) << ")!" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Verify that the result vector is correct
|
||||
std::vector<float_type> results;
|
||||
results.reserve(numElements);
|
||||
w.reset();
|
||||
|
||||
boost::math::non_central_f_distribution<float_type> non_central_dist(5, 5, 5);
|
||||
for(int i = 0; i < numElements; ++i)
|
||||
{
|
||||
results.push_back(non_central_dist.find_non_centrality(5, 5, 5, input_vector1[i]));
|
||||
}
|
||||
double t = w.elapsed();
|
||||
// check the results
|
||||
for(int i = 0; i < numElements; ++i)
|
||||
{
|
||||
if (boost::math::epsilon_difference(output_vector[i], results[i]) > 100.0)
|
||||
{
|
||||
std::cerr << "Result verification failed at element " << i << "!" << std::endl;
|
||||
std::cerr << "Error rate was: " << boost::math::epsilon_difference(output_vector[i], results[i]) << "eps" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Test PASSED with calculation time: " << t << "s" << std::endl;
|
||||
std::cout << "Done\n";
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
std::cerr << "Stopped with exception: " << e.what() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
114
test/test_nc_f_finder_float.cu
Normal file
114
test/test_nc_f_finder_float.cu
Normal file
@@ -0,0 +1,114 @@
|
||||
// Copyright John Maddock 2016.
|
||||
// Copyright Matt Borland 2024.
|
||||
// Use, modification and distribution are subject to the
|
||||
// Boost Software License, Version 1.0. (See accompanying file
|
||||
// LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
|
||||
|
||||
#define BOOST_MATH_OVERFLOW_ERROR_POLICY ignore_error
|
||||
|
||||
#include <iostream>
|
||||
#include <iomanip>
|
||||
#include <vector>
|
||||
#include <boost/math/distributions/non_central_f.hpp>
|
||||
#include <boost/math/special_functions/relative_difference.hpp>
|
||||
#include <boost/random/mersenne_twister.hpp>
|
||||
#include <boost/random/uniform_real_distribution.hpp>
|
||||
#include "cuda_managed_ptr.hpp"
|
||||
#include "stopwatch.hpp"
|
||||
|
||||
// For the CUDA runtime routines (prefixed with "cuda_")
|
||||
#include <cuda_runtime.h>
|
||||
|
||||
typedef float float_type;
|
||||
|
||||
/**
|
||||
* CUDA Kernel Device code
|
||||
*
|
||||
*/
|
||||
__global__ void cuda_test(const float_type *in1, float_type *out, int numElements)
|
||||
{
|
||||
int i = blockDim.x * blockIdx.x + threadIdx.x;
|
||||
|
||||
if (i < numElements)
|
||||
{
|
||||
boost::math::non_central_f_distribution<float_type> dist(5, 5, 5);
|
||||
out[i] = dist.find_non_centrality(5, 5, 5, in1[i]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Host main routine
|
||||
*/
|
||||
int main(void)
|
||||
{
|
||||
try{
|
||||
|
||||
// Error code to check return values for CUDA calls
|
||||
cudaError_t err = cudaSuccess;
|
||||
|
||||
// Print the vector length to be used, and compute its size
|
||||
int numElements = 50000;
|
||||
std::cout << "[Vector operation on " << numElements << " elements]" << std::endl;
|
||||
|
||||
// Allocate the managed input vector A
|
||||
cuda_managed_ptr<float_type> input_vector1(numElements);
|
||||
|
||||
// Allocate the managed output vector C
|
||||
cuda_managed_ptr<float_type> output_vector(numElements);
|
||||
|
||||
boost::random::mt19937 gen;
|
||||
boost::random::uniform_real_distribution<float_type> dist(0.1, 0.9);
|
||||
// Initialize the input vectors
|
||||
for (int i = 0; i < numElements; ++i)
|
||||
{
|
||||
input_vector1[i] = dist(gen);
|
||||
}
|
||||
|
||||
// Launch the Vector Add CUDA Kernel
|
||||
int threadsPerBlock = 256;
|
||||
int blocksPerGrid =(numElements + threadsPerBlock - 1) / threadsPerBlock;
|
||||
std::cout << "CUDA kernel launch with " << blocksPerGrid << " blocks of " << threadsPerBlock << " threads" << std::endl;
|
||||
|
||||
watch w;
|
||||
cuda_test<<<blocksPerGrid, threadsPerBlock>>>(input_vector1.get(), output_vector.get(), numElements);
|
||||
cudaDeviceSynchronize();
|
||||
std::cout << "CUDA kernal done in " << w.elapsed() << "s" << std::endl;
|
||||
|
||||
err = cudaGetLastError();
|
||||
if (err != cudaSuccess)
|
||||
{
|
||||
std::cerr << "Failed to launch non_central_f distribution kernel (error code " << cudaGetErrorString(err) << ")!" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// Verify that the result vector is correct
|
||||
std::vector<float_type> results;
|
||||
results.reserve(numElements);
|
||||
w.reset();
|
||||
|
||||
boost::math::non_central_f_distribution<float_type> non_central_dist(5, 5, 5);
|
||||
for(int i = 0; i < numElements; ++i)
|
||||
{
|
||||
results.push_back(non_central_dist.find_non_centrality(5, 5, 5, input_vector1[i]));
|
||||
}
|
||||
double t = w.elapsed();
|
||||
// check the results
|
||||
for(int i = 0; i < numElements; ++i)
|
||||
{
|
||||
if (boost::math::epsilon_difference(output_vector[i], results[i]) > 100.0)
|
||||
{
|
||||
std::cerr << "Result verification failed at element " << i << "!" << std::endl;
|
||||
std::cerr << "Error rate was: " << boost::math::epsilon_difference(output_vector[i], results[i]) << "eps" << std::endl;
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
}
|
||||
|
||||
std::cout << "Test PASSED with calculation time: " << t << "s" << std::endl;
|
||||
std::cout << "Done\n";
|
||||
}
|
||||
catch(const std::exception& e)
|
||||
{
|
||||
std::cerr << "Stopped with exception: " << e.what() << std::endl;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
Reference in New Issue
Block a user