Reputation: 18178
Please consider the following boost::math::pdf
-like implementation of a generic probability density function evaluation:
template<typename T, class Distribution>
inline typename Distribution::result_type pdf(Distribution const& d, T const& x) {
return pdf(d, static_cast<typename Distribution::result_type>(x));
}
template<typename RealType>
RealType pdf(std::uniform_real_distribution<RealType> const& d, RealType const& x)
{
if (d.a() <= x && x <= d.b())
return 1 / (d.b() - d.a());
return 0;
}
I'd like to define another pdf
function which takes a distribution and a vector and evaluates the probability density of each vector component. The function should be invokable for nested vectors as well. I've tried something like this:
template<typename T, class Distribution>
inline typename Distribution::result_type pdf(Distribution const& d, std::vector<T> const& x)
{
return std::reduce(x.begin(), x.end(), typename Distribution::result_type{ 1 },
[&](auto const& p, auto const& x) { return p * pdf(d, x); });
}
Example code:
std::vector<std::vector<double>> x = { {1}, {2, 3}, {4} };
std::vector<double> y = { 1, 2, 3, 4 };
std::uniform_real_distribution<> d;
std::cout << pdf(d, x) << std::endl;
std::cout << pdf(d, y) << std::endl;
This works as it should be. However, if I change std::uniform_real_distribution<> d;
to std::uniform_real_distribution<float> d;
, then I end up in an infinite recursion for the call of pdf(d, x)
(for obvious reasons). So, how do I need to change the code? Maybe such that it works for other containers as well.
Upvotes: 1
Views: 66
Reputation: 303147
Instead of having two overloads for the first case (which basically do the same thing):
template<typename T, class Distribution>
inline typename Distribution::result_type pdf(Distribution const& d, T const& x) {
return pdf(d, static_cast<typename Distribution::result_type>(x));
}
template<typename RealType>
RealType pdf(std::uniform_real_distribution<RealType> const& d, RealType const& x)
{
if (d.a() <= x && x <= d.b())
return 1 / (d.b() - d.a());
return 0;
}
collapse them into one:
template <typename R, typename T, std::enable_if_t<std::is_convertible_v<T, R>, int> = 0>
auto pdf(std::uniform_real_distribution<R> const& d, T x) -> R
{
if (d.a() <= x && x <= d.b()) {
return 1 / (d.b() - d.a());
}
return 0;
}
Or simpler to just block type deduction on the 2nd argument entirely (std::type_identity
is C++20 but is trivial to implement):
template <typename R>
auto pdf(std::uniform_real_distribution<R> const& d, std::type_identity_t<R> x) -> R
Upvotes: 1