Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 19 additions & 0 deletions doc/distributions/nc_f.qbk
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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);
Expand Down
104 changes: 103 additions & 1 deletion include/boost/math/distributions/non_central_f.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
Expand Down Expand Up @@ -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.
Expand Down Expand Up @@ -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

Expand Down
2 changes: 2 additions & 0 deletions test/cuda_jamfile
Original file line number Diff line number Diff line change
Expand Up @@ -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 ;
Expand Down
23 changes: 23 additions & 0 deletions test/test_nc_f.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand Down Expand Up @@ -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 )
Expand Down
114 changes: 114 additions & 0 deletions test/test_nc_f_finder_double.cu
Original file line number Diff line number Diff line change
@@ -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;
}
Loading