blob: cd70fd518f02afab8328dc859b04df2123cfc221 [file] [log] [blame]
/* ----------------------------------------------------------------------- *//**
*
* @file boost.hpp
*
*//* ----------------------------------------------------------------------- */
#define LIST_CONTINUOUS_PROB_DISTR \
MADLIB_ITEM(beta) \
MADLIB_ITEM(cauchy) \
MADLIB_ITEM(chi_squared) \
MADLIB_ITEM(fisher_f) \
MADLIB_ITEM(exponential) \
MADLIB_ITEM(extreme_value) \
MADLIB_ITEM(gamma) \
MADLIB_ITEM(inverse_chi_squared) \
MADLIB_ITEM(inverse_gamma) \
MADLIB_ITEM(laplace) \
MADLIB_ITEM(logistic) \
MADLIB_ITEM(lognormal) \
MADLIB_ITEM(non_central_beta) \
MADLIB_ITEM(non_central_chi_squared) \
MADLIB_ITEM(non_central_f) \
MADLIB_ITEM(non_central_t) \
MADLIB_ITEM(normal) \
MADLIB_ITEM(pareto) \
MADLIB_ITEM(rayleigh) \
MADLIB_ITEM(triangular) \
MADLIB_ITEM(uniform) \
MADLIB_ITEM(weibull)
// FIXME: MADLIB-513: Pending Boost bug 6934, we currently do not support the
// inverse Gaussian distribution. https://svn.boost.org/trac/boost/ticket/6934
// MADLIB_ITEM(inverse_gaussian)
// For Student's t distribution, see student.hpp
#define LIST_DISCRETE_PROB_DISTR \
MADLIB_ITEM(bernoulli) \
MADLIB_ITEM(binomial) \
MADLIB_ITEM(geometric) \
MADLIB_ITEM(hypergeometric) \
MADLIB_ITEM(negative_binomial) \
MADLIB_ITEM(poisson)
#define MADLIB_ITEM(dist) \
DECLARE_UDF(prob, dist ## _cdf) \
DECLARE_UDF(prob, dist ## _pdf) \
DECLARE_UDF(prob, dist ## _quantile)
LIST_CONTINUOUS_PROB_DISTR
#undef MADLIB_ITEM
#define MADLIB_ITEM(dist) \
DECLARE_UDF(prob, dist ## _cdf) \
DECLARE_UDF(prob, dist ## _pmf) \
DECLARE_UDF(prob, dist ## _quantile)
LIST_DISCRETE_PROB_DISTR
#undef MADLIB_ITEM
#ifndef MADLIB_MODULES_PROB_BOOST_HPP
#define MADLIB_MODULES_PROB_BOOST_HPP
#include <boost/math/distributions.hpp>
namespace madlib {
namespace modules {
namespace prob {
namespace {
// No need to make this visable beyond this translation unit.
enum ProbFnOverride {
kResultIsReady = 0,
kLetBoostCalculate,
kLetBoostCalculateUsingValue
};
/**
* @brief Via (partial) specialization, this class offers a way to override
* boost's domain checks
*
* Some boost functions have domain checks we would like to override. E.g.,
* boost's CDF and PDF for the Fisher F-distribution raise a domain error if the
* input argument is <0 or infinity. By using madlib::modules::prob::cdf() (and
* pdf/quantile, respectively), these domain checks can be overridden.
* In MADlib, we always prefer returning correct values as opposed to raising
* errors. E.g., for the CDF, it is the correct mathematical behavior to simply
* return 0 if the input argument for the Fisher F-distribution is < 0.
*
* The following functions return \c true if boost's implementation should be
* called, and they return \c false if the function result has alrady been
* stored in \c outResult and boost's implementation should not be called any
* more.
*
* Note that C++03 does not support partial specialization of functions. We
* therefore must partially specialize the whole \c DomainCheck class.
*/
template <class Distribution>
struct DomainCheck {
typedef typename Distribution::value_type RealType;
template <bool Complement>
static ProbFnOverride cdf(const Distribution&, const RealType&, RealType&) {
return kLetBoostCalculate;
}
static ProbFnOverride pdf(const Distribution&, const RealType&, RealType&) {
return kLetBoostCalculate;
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution&, const RealType&,
RealType&) {
return kLetBoostCalculate;
}
};
/**
* @brief Domain-check overrides for distribution functions with support in
* \f$ \mathbb R \f$
*
* We override the domain check by treating -infinity and infinity as part of
* the domain. Some, but not all, boost functions would raise a domain_error
* instead. See:
* http://www.boost.org/doc/libs/1_49_0/libs/math/doc/sf_and_dist/html/math_toolkit/backgrounders/implementation.html#math_toolkit.backgrounders.implementation.handling_of_floating_point_infinity
*/
template <class Distribution>
struct RealDomainCheck {
typedef typename Distribution::value_type RealType;
template <bool Complement>
static ProbFnOverride cdf(const Distribution&, const RealType& inX,
RealType& outResult) {
if (boost::math::isinf(inX)) {
if (inX < 0)
outResult = Complement ? 1 : 0;
else
outResult = Complement ? 0 : 1;
return kResultIsReady;
}
return kLetBoostCalculate;
}
static ProbFnOverride pdf(const Distribution&, const RealType& inX,
RealType& outResult) {
if (boost::math::isinf(inX)) {
outResult = 0;
return kResultIsReady;
}
return kLetBoostCalculate;
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution&, const RealType&, RealType&) {
return kLetBoostCalculate;
}
};
/**
* @brief Domain-check overrides for distribution functions with support in
* \f$ [0, \infty) \f$
*/
template <class Distribution>
struct PositiveDomainCheck : public RealDomainCheck<Distribution> {
typedef RealDomainCheck<Distribution> Base;
typedef typename Base::RealType RealType;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inX < 0) {
outResult = Complement ? 1 : 0;
return kResultIsReady;
}
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inX < 0) {
outResult = 0;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Domain-check overrides for distribution functions with support in
* \f$ [0, 1] \f$
*/
template <class Distribution>
struct ZeroOneDomainCheck : public PositiveDomainCheck<Distribution> {
typedef PositiveDomainCheck<Distribution> Base;
typedef typename Base::RealType RealType;
template <bool Complement>
static ProbFnOverride cdf(const Distribution&, const RealType& inX,
RealType& outResult) {
if (inX < 0)
outResult = Complement ? 1 : 0;
else if (inX > 1)
outResult = Complement ? 0 : 1;
else
return kLetBoostCalculate;
return kResultIsReady;
}
static ProbFnOverride pdf(const Distribution&, const RealType& inX,
RealType& outResult) {
if (inX < 0 || inX > 1) {
outResult = 0;
return kResultIsReady;
}
return kLetBoostCalculate;
}
};
/**
* @brief Domain-check overrides for distribution functions with support in
* \f$ \mathbb Z \f$
*/
template <class Distribution>
struct IntegerDomainCheck : public RealDomainCheck<Distribution> {
typedef RealDomainCheck<Distribution> Base;
typedef typename Base::RealType RealType;
static ProbFnOverride internalMakeIntegral(ProbFnOverride inAction,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"IntegerDomainCheck::internalMakeIntegral(...)";
if (inAction != kResultIsReady) {
if (std::isnan(inX)) {
outResult = boost::math::policies::raise_domain_error<RealType>(
function,
"Random variate must be integral but was: %1%.",
inX,
typename Distribution::policy_type());
inAction = kResultIsReady;
} else {
outResult = std::floor(inX);
inAction = kLetBoostCalculateUsingValue;
}
}
return inAction;
}
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
return internalMakeIntegral(
Base::template cdf<Complement>(inDist, inX, outResult), inX,
outResult);
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
return internalMakeIntegral(
Base::pdf(inDist, inX, outResult), inX, outResult);
}
};
/**
* @brief Domain-check overrides for distribution functions with support in
* \f$ \mathbb N_0 \f$
*/
template <class Distribution>
struct NonNegativeIntegerDomainCheck : public IntegerDomainCheck<Distribution> {
typedef IntegerDomainCheck<Distribution> Base;
typedef typename Base::RealType RealType;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inX < 0) {
outResult = Complement ? 1 : 0;
return kResultIsReady;
}
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inX < 0) {
outResult = 0;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Boost only accepts 0 or 1 for random variate
*
* Due to boost bug 6937, we also need to override the domain check for quantile
*
* https://svn.boost.org/trac/boost/ticket/6937
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::bernoulli_distribution<RealType, Policy> >
: public IntegerDomainCheck<
boost::math::bernoulli_distribution<RealType, Policy>
> {
typedef boost::math::bernoulli_distribution<RealType, Policy> Distribution;
typedef IntegerDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inX < 0)
outResult = Complement ? 1 : 0;
else if (inX > 1)
outResult = Complement ? 0 : 1;
else
return Base::template cdf<Complement>(inDist, inX, outResult);
return kResultIsReady;
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inX < 0 || inX > 1) {
outResult = 0;
return kResultIsReady;
} else
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<bernoulli_distribution<%1%> >::quantile(...)";
if (!boost::math::detail::check_probability(function,
inDist.success_fraction(), &outResult, Policy())) {
return kResultIsReady;
} else if (inP == 1 || inP == 0){
outResult = inP;
return kResultIsReady;
} else {
return Base::template quantile<Complement>(inDist, inP, outResult);
}
}
};
/**
* @brief Boost only accepts a limited range for random variates
*
* Due to boost bug 6937, we also need to override the domain check for quantile
*
* https://svn.boost.org/trac/boost/ticket/6937
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::binomial_distribution<RealType, Policy> >
: public IntegerDomainCheck<
boost::math::binomial_distribution<RealType, Policy>
> {
typedef boost::math::binomial_distribution<RealType, Policy> Distribution;
typedef IntegerDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<binomial_distribution<%1%> >::cdf(...)";
if (!boost::math::binomial_detail::check_dist(function, inDist.trials(),
inDist.success_fraction(), &outResult, Policy())) {
return kResultIsReady;
} else if (inX < 0) {
outResult = Complement ? 1 : 0;
return kResultIsReady;
} else if (inX > inDist.trials()) {
outResult = Complement ? 0 : 1;
return kResultIsReady;
}
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<binomial_distribution<%1%> >::pdf(...)";
if (!boost::math::binomial_detail::check_dist(function, inDist.trials(),
inDist.success_fraction(), &outResult, Policy())) {
return kResultIsReady;
} else if (inX < 0 || inX > inDist.trials()) {
outResult = 0;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<binomial_distribution<%1%> >::quantile(...)";
if (!boost::math::binomial_detail::check_dist(function, inDist.trials(),
inDist.success_fraction(), &outResult, Policy())
|| !boost::math::detail::check_probability(function, inP,
&outResult, Policy())) {
return kResultIsReady;
} else if (inP == 1) {
outResult = inDist.trials();
return kResultIsReady;
} else if (inP == 0) {
outResult = 0;
return kResultIsReady;
} else if (inDist.success_fraction() == 1) {
outResult = inDist.trials();
return kResultIsReady;
} else if (inDist.success_fraction() == 0) {
outResult = 0;
return kResultIsReady;
}
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to override the domain checks
*
* FIXME: No boost bug filed so far
* Boost does not catch the case where lambda is non-finite.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::exponential_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::exponential_distribution<RealType, Policy>
> {
typedef boost::math::exponential_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
static bool check_dist(const char* function, RealType lambda,
RealType* result, const Policy& pol) {
if (!boost::math::isfinite(lambda)) {
*result = boost::math::policies::raise_domain_error<RealType>(
function,
"The scale parameter \"lambda\" must be finite, but was: %1%.",
lambda, pol);
return false;
}
return true;
}
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<exponential_distribution<%1%> >::cdf(...)";
if (!check_dist(function, inDist.lambda(), &outResult, Policy()))
return kResultIsReady;
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<exponential_distribution<%1%> >::pdf(...)";
if (!check_dist(function, inDist.lambda(), &outResult, Policy()))
return kResultIsReady;
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<exponential_distribution<%1%> >::quantile(...)";
if (!check_dist(function, inDist.lambda(), &outResult, Policy()))
return kResultIsReady;
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to override the domain check for
* quantile
*
* FIXME: No boost bug filed so far
* Boost does not catch the case where the location or scale parameters are
* non-finite.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::extreme_value_distribution<RealType, Policy> >
: public RealDomainCheck<
boost::math::extreme_value_distribution<RealType, Policy>
> {
typedef boost::math::extreme_value_distribution<RealType, Policy> Distribution;
typedef RealDomainCheck<Distribution> Base;
static bool check_dist(const char* function, RealType location,
RealType scale, RealType* result, const Policy& pol) {
return
boost::math::detail::check_location(function, location, result, pol)
&& boost::math::detail::check_scale(function, scale, result, pol);
}
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<extreme_value_distribution<%1%> >::cdf(...)";
if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
Policy()))
return kResultIsReady;
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<extreme_value_distribution<%1%> >::pdf(...)";
if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
Policy()))
return kResultIsReady;
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<extreme_value_distribution<%1%> >::quantile(...)";
if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
Policy()))
return kResultIsReady;
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug 6937, we need to override the domain check for
* quantile
*
* https://svn.boost.org/trac/boost/ticket/6937
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::fisher_f_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::fisher_f_distribution<RealType, Policy>
> {
typedef boost::math::fisher_f_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<fisher_f_distribution<%1%> >::quantile(...)";
if (!boost::math::detail::check_df(function,
inDist.degrees_of_freedom1(), &outResult, Policy())
|| !boost::math::detail::check_df(function,
inDist.degrees_of_freedom2(), &outResult, Policy()))
return kResultIsReady;
if (std::isnan(inP)) {
outResult = boost::math::policies::raise_domain_error<RealType>(
function,
"Probability argument is %1%, but must be >= 0 and <= 1!", inP,
Policy());
return kResultIsReady;
}
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug XXXX, we override the domain check for gamma: pdf
*
* For the gamma distribution, boost's pdf always returns 0 for x = 0. That is
* wrong.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::gamma_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::gamma_distribution<RealType, Policy>
> {
typedef boost::math::gamma_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<gamma_distribution<%1%> >::pdf(...)";
RealType shape = inDist.shape();
RealType scale = inDist.scale();
if (!boost::math::detail::check_gamma(function, scale, shape,
&outResult, Policy()))
return kResultIsReady;
if (inX == 0) {
if (shape == 1)
outResult = 1. / scale;
else if (shape < 1)
outResult
= boost::math::policies::raise_overflow_error<RealType>(
function, 0, Policy());
else
return kLetBoostCalculate;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Due to boost bug 6937, we need to override the domain check for
* quantile
*
* https://svn.boost.org/trac/boost/ticket/6937
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::geometric_distribution<RealType, Policy> >
: public NonNegativeIntegerDomainCheck<
boost::math::geometric_distribution<RealType, Policy>
> {
typedef boost::math::geometric_distribution<RealType, Policy> Distribution;
typedef NonNegativeIntegerDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
if (inDist.success_fraction() == 1 && !std::isnan(inX)) {
if (inX < 0)
outResult = Complement ? 1 : 0;
else if (inX > 0)
outResult = Complement ? 0 : 1;
else /* if (inX == 0) */
outResult = 1;
return kResultIsReady;
}
return Base::template cdf<Complement>(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<geometric_distribution<%1%> >::quantile(...)";
if (!boost::math::detail::check_probability(function,
inDist.success_fraction(), &outResult, Policy()))
return kResultIsReady;
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Boost only accepts a limited range for random variates
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::hypergeometric_distribution<RealType, Policy> >
: public NonNegativeIntegerDomainCheck<
boost::math::hypergeometric_distribution<RealType, Policy>
> {
typedef boost::math::hypergeometric_distribution<RealType, Policy> Distribution;
typedef NonNegativeIntegerDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
// inDist's parameters are all unsigned, so we need to be careful in the
// comparison!
if (inX + inDist.total() < inDist.defective() + inDist.sample_count())
outResult = Complement ? 1 : 0;
else if (inX > inDist.sample_count() || inX > inDist.defective())
outResult = Complement ? 0 : 1;
else
return Base::template cdf<Complement>(inDist, inX, outResult);
return kResultIsReady;
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
// inDist's parameters are all unsigned, so we need to be careful in the
// comparison!
if (inX + inDist.total() < inDist.defective() + inDist.sample_count()
|| inX > inDist.sample_count() || inX > inDist.defective()) {
outResult = 0;
return kResultIsReady;
} else
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Boost returns a small non-zero value for quantile(0) instead of 0
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::inverse_gamma_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::inverse_gamma_distribution<RealType, Policy>
> {
typedef boost::math::inverse_gamma_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<inverse_gamma_distribution<%1%> >::quantile(...)";
if(!boost::math::detail::check_inverse_gamma(function, inDist.scale(),
inDist.shape(), &outResult, Policy()))
return kResultIsReady;
else if (inP == 0) {
outResult = 0;
return kResultIsReady;
}
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to override the domain check for
* quantile
*
* FIXME: No boost bug filed so far
* Boost does not catch the case where location or scale are not finite.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::lognormal_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::lognormal_distribution<RealType, Policy>
> {
typedef boost::math::lognormal_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
static bool check_dist(const char* function, RealType location,
RealType scale, RealType* result, const Policy& pol) {
if (!boost::math::detail::check_location(function, location, result,
pol)
|| !boost::math::detail::check_scale(function, scale, result, pol))
return false;
return true;
}
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<lognormal_distribution<%1%> >::pdf(...)";
if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
Policy()))
return kResultIsReady;
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<lognormal_distribution<%1%> >::quantile(...)";
if (!check_dist(function, inDist.location(), inDist.scale(), &outResult,
Policy()))
return kResultIsReady;
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug 6937, we need to override the domain check for
* quantile.
*
* https://svn.boost.org/trac/boost/ticket/6937
*
* Also, we want to raise an error if the success probability is 0, because the
* distribution is not well-defined in that case.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::negative_binomial_distribution<RealType, Policy> >
: public NonNegativeIntegerDomainCheck<
boost::math::negative_binomial_distribution<RealType, Policy>
> {
typedef boost::math::negative_binomial_distribution<RealType, Policy> Distribution;
typedef NonNegativeIntegerDomainCheck<Distribution> Base;
static bool check_dist(const char* function, const RealType& r,
const RealType& p, RealType* result, const Policy& pol) {
if (!boost::math::negative_binomial_detail::check_dist(function, r, p,
result, pol)) {
return false;
} else if (p == 0) {
*result = boost::math::policies::raise_domain_error<RealType>(
function,
"Probability argument is %1%, but must be > 0 and <= 1!",
p, pol);
return false;
}
return true;
}
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<negative_binomial_distribution<%1%> >::cdf(...)";
if (!check_dist(function, inDist.successes(), inDist.success_fraction(),
&outResult, Policy()))
return kResultIsReady;
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<negative_binomial_distribution<%1%> >::pdf(...)";
if (!check_dist(function, inDist.successes(), inDist.success_fraction(),
&outResult, Policy()))
return kResultIsReady;
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<negative_binomial_distribution<%1%> >::quantile(...)";
if (!check_dist(function, inDist.successes(), inDist.success_fraction(),
&outResult, Policy())) {
return kResultIsReady;
} else if (inDist.success_fraction() == 1) {
// distribution is single-point measure, i.e., same for complement
outResult = 0;
return kResultIsReady;
}
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to check the corner cases for the pdf
*
* FIXME: No boost bug filed so far
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::non_central_beta_distribution<RealType, Policy> >
: public ZeroOneDomainCheck<
boost::math::non_central_beta_distribution<RealType, Policy>
> {
typedef boost::math::non_central_beta_distribution<RealType, Policy> Distribution;
typedef ZeroOneDomainCheck<Distribution> Base;
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<non_central_beta_distribution<%1%> >::quantile(...)";
if (inX == 0 || inX == 1) {
if(!boost::math::beta_detail::check_alpha(function, inDist.alpha(),
&outResult, Policy())
|| !boost::math::beta_detail::check_beta(function,
inDist.beta(), &outResult, Policy())
|| !boost::math::detail::check_non_centrality(function,
inDist.non_centrality(), &outResult, Policy()))
return kResultIsReady;
if (inX == 0) {
if (inDist.alpha() < 1)
outResult = boost::math::policies
::raise_overflow_error<RealType>(function, 0, Policy());
else if (inDist.alpha() == 1)
outResult = inDist.beta()
* std::exp(-inDist.non_centrality()/2.);
else /* if (inDist.alpha() > 1) */
outResult = 0;
return kResultIsReady;
} else /* if (inX == 1) */ {
if (inDist.beta() < 1)
outResult = boost::math::policies
::raise_overflow_error<RealType>(function, 0, Policy());
else if (inDist.beta() == 1)
outResult = inDist.alpha() + inDist.non_centrality()/2.;
else /* if (inDist.beta() > 1) */
outResult = 0;
return kResultIsReady;
}
}
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to check the corner cases for the pdf
*
* FIXME: No boost bug filed so far
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::non_central_chi_squared_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::non_central_chi_squared_distribution<RealType, Policy>
> {
typedef boost::math::non_central_chi_squared_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<non_central_chi_squared_distribution<%1%> >::quantile(...)";
if (inX == 0) {
if(!boost::math::detail::check_df(function,
inDist.degrees_of_freedom(), &outResult, Policy())
|| !boost::math::detail::check_non_centrality(function,
inDist.non_centrality(), &outResult, Policy()))
return kResultIsReady;
if (inDist.degrees_of_freedom() < 2)
outResult = boost::math::policies
::raise_overflow_error<RealType>(function, 0, Policy());
else if (inDist.degrees_of_freedom() == 2)
// In this case, f(x) = exp(-lambda/2) * g(x), where g(x)
// is the densitiy of a chi-squared distributed RV with 2
// degrees of freedom, i.e., f(x) = exp(-lambda/2) / 2
outResult = std::exp(-inDist.non_centrality()/2.) / 2.;
else /* if (inDist.degrees_of_freedom() > 2) */
outResult = 0;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to check the corner cases for the pdf
*
* FIXME: No boost bug filed so far
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::non_central_f_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::non_central_f_distribution<RealType, Policy>
> {
typedef boost::math::non_central_f_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<non_central_f_distribution<%1%> >::quantile(...)";
if (inX == 0) {
if(!boost::math::detail::check_df(function,
inDist.degrees_of_freedom1(), &outResult, Policy())
|| !boost::math::detail::check_df(function,
inDist.degrees_of_freedom2(), &outResult, Policy())
|| !boost::math::detail::check_non_centrality(function,
inDist.non_centrality(), &outResult, Policy()))
return kResultIsReady;
if (inDist.degrees_of_freedom1() < 2)
outResult = boost::math::policies
::raise_overflow_error<RealType>(function, 0, Policy());
else if (inDist.degrees_of_freedom1() == 2)
// In this case, f(x) = exp(-lambda/2)
outResult = std::exp(-inDist.non_centrality()/2.);
else /* if (inDist.degrees_of_freedom1() > 2) */
outResult = 0;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
};
/**
* @brief Override the domain check for Pareto: quantile
*
* For the Pareto distribution, boost sometimes returns max_value instead
* of infinity. We override that.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::pareto_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::pareto_distribution<RealType, Policy>
> {
typedef boost::math::pareto_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<pareto_distribution<%1%> >::cdf(...)";
if (inX <= inDist.scale()) {
if (boost::math::detail::check_pareto(function, inDist.scale(),
inDist.shape(), &outResult, Policy()))
outResult = 0;
return kResultIsReady;
}
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<pareto_distribution<%1%> >::pdf(...)";
if (inX < inDist.scale()) {
if (boost::math::detail::check_pareto(function, inDist.scale(),
inDist.shape(), &outResult, Policy()))
outResult = 0;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<pareto_distribution<%1%> >::quantile(...)";
if (!boost::math::detail::check_pareto(function, inDist.scale(),
inDist.shape(), &outResult, Policy()))
return kResultIsReady;
else if (inP == 1) {
outResult = boost::math::policies::raise_overflow_error<RealType>(
function, 0, Policy());
return kResultIsReady;
}
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug 6937, we need to override the domain check for
* quantile
*
* https://svn.boost.org/trac/boost/ticket/6937
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::poisson_distribution<RealType, Policy> >
: public NonNegativeIntegerDomainCheck<
boost::math::poisson_distribution<RealType, Policy>
> {
typedef boost::math::poisson_distribution<RealType, Policy> Distribution;
typedef NonNegativeIntegerDomainCheck<Distribution> Base;
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<poisson_distribution<%1%> >::quantile(...)";
if (!boost::math::poisson_detail::check_dist(function,
inDist.mean(), &outResult, Policy()))
return kResultIsReady;
else if (inP == 1) {
outResult = boost::math::policies::raise_overflow_error<RealType>(
function, 0, Policy());
return kResultIsReady;
}
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bug XXX, we need to override the domain check for
* quantile
*
* FIXME: No boost bug filed so far
* Boost does not catch the case where sigma is NaN.
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::rayleigh_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::rayleigh_distribution<RealType, Policy>
> {
typedef boost::math::rayleigh_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
static bool check_sigma_finite(const char* function, RealType sigma,
RealType* result, const Policy& pol) {
if (!boost::math::isfinite(sigma)) {
*result = boost::math::policies::raise_domain_error<RealType>(
function,
"The scale parameter \"sigma\" must be finite, but was: %1%.",
sigma, pol);
return false;
}
return true;
}
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<rayleigh_distribution<%1%> >::cdf(...)";
if (!check_sigma_finite(function, inDist.sigma(), &outResult, Policy()))
return kResultIsReady;
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist,
const RealType& inX, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<rayleigh_distribution<%1%> >::pdf(...)";
if (!check_sigma_finite(function, inDist.sigma(), &outResult, Policy()))
return kResultIsReady;
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<rayleigh_distribution<%1%> >::quantile(...)";
if (!check_sigma_finite(function, inDist.sigma(), &outResult, Policy()))
return kResultIsReady;
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
/**
* @brief Due to boost bugs 6938 and 6939, we need to override the domain checks
*
* https://svn.boost.org/trac/boost/ticket/6938
* https://svn.boost.org/trac/boost/ticket/6939
*/
template <class RealType, class Policy>
struct DomainCheck<boost::math::weibull_distribution<RealType, Policy> >
: public PositiveDomainCheck<
boost::math::weibull_distribution<RealType, Policy>
> {
typedef boost::math::weibull_distribution<RealType, Policy> Distribution;
typedef PositiveDomainCheck<Distribution> Base;
// BEGIN Copied from boost/math/distributions/weibull.hpp (v1.49), but replaced
// "<" check by "<=".
static bool check_weibull_shape(
const char* function,
RealType shape,
RealType* result, const Policy& pol) {
if((shape <= 0) || !(boost::math::isfinite)(shape)) {
*result = boost::math::policies::raise_domain_error<RealType>(
function,
"Shape parameter is %1%, but must be > 0 !", shape, pol);
return false;
}
return true;
}
// END Copied from boost/math/distributions/weibull.hpp (v1.49)
template <bool Complement>
static ProbFnOverride cdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<weibull_distribution<%1%> >::cdf(...)";
RealType shape = inDist.shape();
if (!check_weibull_shape(function, shape, &outResult, Policy()))
return kResultIsReady;
return Base::template cdf<Complement>(inDist, inX, outResult);
}
static ProbFnOverride pdf(const Distribution& inDist, const RealType& inX,
RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<weibull_distribution<%1%> >::pdf(...)";
RealType shape = inDist.shape();
if (!check_weibull_shape(function, shape, &outResult, Policy()))
return kResultIsReady;
if (inX == 0) {
if (shape == 1)
outResult = 1;
else if (shape < 1)
outResult = boost::math::policies::raise_overflow_error<RealType>(
function, 0, Policy());
else
return kLetBoostCalculate;
return kResultIsReady;
}
return Base::pdf(inDist, inX, outResult);
}
template <bool Complement>
static ProbFnOverride quantile(const Distribution& inDist,
const RealType& inP, RealType& outResult) {
static const char* function = "madlib::modules::prob::<unnamed>::"
"DomainCheck<weibull_distribution<%1%> >::quantile(...)";
RealType shape = inDist.shape();
if (!check_weibull_shape(function, shape, &outResult, Policy()))
return kResultIsReady;
return Base::template quantile<Complement>(inDist, inP, outResult);
}
};
#define DOMAIN_CHECK_OVERRIDE(dist, check) \
template <class RealType, class Policy> \
struct DomainCheck<boost::math::dist ## _distribution<RealType, Policy> > \
: public check<boost::math::dist ## _distribution<RealType, Policy> > { };
DOMAIN_CHECK_OVERRIDE(beta, ZeroOneDomainCheck)
DOMAIN_CHECK_OVERRIDE(chi_squared, PositiveDomainCheck)
DOMAIN_CHECK_OVERRIDE(laplace, RealDomainCheck)
DOMAIN_CHECK_OVERRIDE(non_central_t, RealDomainCheck)
DOMAIN_CHECK_OVERRIDE(triangular, RealDomainCheck)
DOMAIN_CHECK_OVERRIDE(uniform, RealDomainCheck)
// The following lines are currently commented out. Each of these is a boost
// deficiency unfortunately. See above.
// DOMAIN_CHECK_OVERRIDE(exponential, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(extreme_value, RealDomainCheck)
// DOMAIN_CHECK_OVERRIDE(fisher_f, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(gamma, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(geometric, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(lognormal, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(hypergeometric, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(inverse_gamma, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(negative_binomial, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(non_central_beta, ZeroOneDomainCheck)
// DOMAIN_CHECK_OVERRIDE(non_central_chi_squared, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(non_central_f, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(pareto, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(poisson, NonNegativeIntegerDomainCheck)
// DOMAIN_CHECK_OVERRIDE(rayleigh, PositiveDomainCheck)
// DOMAIN_CHECK_OVERRIDE(weibull, PositiveDomainCheck)
#undef DOMAIN_CHECK_OVERRIDE
} // anonymous namespace
#define DEFINE_BOOST_WRAPPER(_dist, _what, _domain_check_what) \
template <class RealType, class Policy> \
inline \
RealType \
_what(const boost::math::_dist ## _distribution<RealType, Policy>& dist, \
const RealType& x) { \
\
typedef boost::math::_dist ## _distribution<RealType, Policy> Dist; \
RealType result; \
switch (DomainCheck<Dist>::_domain_check_what(dist, x, result)) { \
case kResultIsReady: return result; \
case kLetBoostCalculate: return boost::math::_what(dist, x); \
case kLetBoostCalculateUsingValue: \
return boost::math::_what(dist, result); \
default: throw std::logic_error("Unexpected case detected in " \
"domain-check override for a boost probability function."); \
} \
}
#define DEFINE_BOOST_COMPLEMENT_WRAPPER(_dist, _what) \
template <class RealType, class Policy> \
inline \
RealType \
_what(const boost::math::complemented2_type< \
boost::math::_dist ## _distribution<RealType, Policy>, \
RealType \
>& c) { \
typedef boost::math::_dist ## _distribution<RealType, Policy> Dist; \
typedef boost::math::complemented2_type<Dist, RealType> Complement; \
RealType result; \
switch (DomainCheck<Dist>::template _what<true>(c.dist, c.param, result)) { \
case kResultIsReady: return result; \
case kLetBoostCalculate: return boost::math::_what(c); \
case kLetBoostCalculateUsingValue: \
return boost::math::_what(Complement(c.dist, result)); \
default: throw std::logic_error("Unexpected case detected in " \
"domain-check override for a boost probability function."); \
} \
}
#define DEFINE_BOOST_PROBABILITY_DISTR(_dist) \
typedef boost::math::_dist ## _distribution< \
double, boost_mathkit_policy> _dist; \
\
DEFINE_BOOST_WRAPPER(_dist, cdf, template cdf<false>) \
DEFINE_BOOST_COMPLEMENT_WRAPPER(_dist, cdf) \
DEFINE_BOOST_WRAPPER(_dist, pdf, pdf) \
DEFINE_BOOST_WRAPPER(_dist, quantile, template quantile<false>) \
DEFINE_BOOST_COMPLEMENT_WRAPPER(_dist, quantile)
#define MADLIB_ITEM(_dist) \
DEFINE_BOOST_PROBABILITY_DISTR(_dist)
// Note that boost also uses the pdf() if actually a probability mass function
// is meant
LIST_CONTINUOUS_PROB_DISTR
LIST_DISCRETE_PROB_DISTR
#undef MADLIB_ITEM
#undef DEFINE_PROBABILITY_DISTR
#undef DEFINE_BOOST_WRAPPER
#undef LIST_CONTINUOUS_PROB_DISTR
} // namespace prob
} // namespace modules
} // namespace regress
#endif // defined(MADLIB_MODULES_PROB_BOOST_HPP)