Skip to content

Commit 490c8b5

Browse files
authored
Merge pull request #3285 from lingium/feature/issue-3239-yule-simon-rng
add yule_simon_rng
2 parents 8055bb4 + 6e1eb53 commit 490c8b5

3 files changed

Lines changed: 110 additions & 0 deletions

File tree

stan/math/prim/prob.hpp

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -320,5 +320,9 @@
320320
#include <stan/math/prim/prob/wishart_cholesky_rng.hpp>
321321
#include <stan/math/prim/prob/wishart_lpdf.hpp>
322322
#include <stan/math/prim/prob/wishart_rng.hpp>
323+
#include <stan/math/prim/prob/yule_simon_cdf.hpp>
324+
#include <stan/math/prim/prob/yule_simon_lccdf.hpp>
325+
#include <stan/math/prim/prob/yule_simon_lcdf.hpp>
323326
#include <stan/math/prim/prob/yule_simon_lpmf.hpp>
327+
#include <stan/math/prim/prob/yule_simon_rng.hpp>
324328
#endif
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
#ifndef STAN_MATH_PRIM_PROB_YULE_SIMON_RNG_HPP
2+
#define STAN_MATH_PRIM_PROB_YULE_SIMON_RNG_HPP
3+
4+
#include <utility>
5+
#include <stan/math/prim/meta.hpp>
6+
#include <stan/math/prim/fun/exp.hpp>
7+
#include <stan/math/prim/fun/log.hpp>
8+
#include <stan/math/prim/fun/log1m.hpp>
9+
#include <stan/math/prim/prob/exponential_rng.hpp>
10+
#include <stan/math/prim/prob/neg_binomial_rng.hpp>
11+
12+
namespace stan {
13+
namespace math {
14+
15+
/** \ingroup prob_dists
16+
* Return a yule-simon random variate with the given shape parameter,
17+
* using the given random number generator.
18+
*
19+
* alpha can be a scalar or a one-dimensional container.
20+
*
21+
* @tparam T_alpha type of shape parameter
22+
* @tparam RNG type of random number generator
23+
*
24+
* @param alpha (Sequence of) shape parameter(s)
25+
* @param rng random number generator
26+
* @return (Sequence of) yule-simon random variate(s)
27+
* @throw std::domain_error if alpha is nonpositive
28+
*/
29+
template <typename T_alpha, typename RNG>
30+
inline auto yule_simon_rng(T_alpha&& alpha, RNG& rng) {
31+
static constexpr const char* function = "yule_simon_rng";
32+
decltype(auto) alpha_ref = to_ref(std::forward<T_alpha>(alpha));
33+
check_positive_finite(function, "Shape parameter", alpha_ref);
34+
35+
auto w = exponential_rng(std::forward<decltype(alpha_ref)>(alpha_ref), rng);
36+
auto w_arr = as_array_or_scalar(w);
37+
const auto p = stan::math::exp(-w_arr);
38+
const auto odds_ratio_p
39+
= stan::math::exp(stan::math::log(p) - stan::math::log1m(p));
40+
41+
if constexpr (is_stan_scalar_v<T_alpha>) {
42+
return neg_binomial_rng(1.0, odds_ratio_p, rng) + 1;
43+
} else {
44+
return to_array_1d(
45+
as_array_or_scalar(neg_binomial_rng(1.0, std::move(odds_ratio_p), rng))
46+
+ 1);
47+
}
48+
}
49+
50+
} // namespace math
51+
} // namespace stan
52+
#endif
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#include <stan/math/prim.hpp>
2+
#include <stan/math/prim/prob/yule_simon_rng.hpp>
3+
#include <test/unit/math/prim/prob/vector_rng_test_helper.hpp>
4+
#include <test/unit/math/prim/prob/VectorIntRNGTestRig.hpp>
5+
#include <gtest/gtest.h>
6+
#include <boost/random/mersenne_twister.hpp>
7+
#include <boost/math/distributions.hpp>
8+
#include <limits>
9+
#include <vector>
10+
11+
class YuleSimonTestRig : public VectorIntRNGTestRig {
12+
public:
13+
YuleSimonTestRig()
14+
: VectorIntRNGTestRig(10000, 10, {1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
15+
{2.1, 4.1, 10.1}, {1, 2, 3}, {-3.0, -2.0, 0.0},
16+
{-3, -1, 0}) {}
17+
18+
template <typename T1, typename T2, typename T3, typename T_rng>
19+
auto generate_samples(const T1& alpha, const T2&, const T3&,
20+
T_rng& rng) const {
21+
return stan::math::yule_simon_rng(alpha, rng);
22+
}
23+
24+
template <typename T1>
25+
double pmf(int y, T1 alpha, double, double) const {
26+
return std::exp(stan::math::yule_simon_lpmf(y, alpha));
27+
}
28+
};
29+
30+
TEST(ProbDistributionsYuleSimon, errorCheck) {
31+
check_dist_throws_all_types(YuleSimonTestRig());
32+
}
33+
34+
TEST(ProbDistributionsYuleSimon, distributionCheck) {
35+
check_counts_real(YuleSimonTestRig());
36+
}
37+
38+
TEST(ProbDistributionsYuleSimon, error_check) {
39+
boost::random::mt19937 rng;
40+
41+
EXPECT_NO_THROW(stan::math::yule_simon_rng(1.0, rng));
42+
EXPECT_NO_THROW(stan::math::yule_simon_rng(2.0, rng));
43+
EXPECT_NO_THROW(stan::math::yule_simon_rng(10.0, rng));
44+
45+
EXPECT_THROW(stan::math::yule_simon_rng(-0.5, rng), std::domain_error);
46+
EXPECT_THROW(stan::math::yule_simon_rng(0.0, rng), std::domain_error);
47+
EXPECT_THROW(stan::math::yule_simon_rng(-10.0, rng), std::domain_error);
48+
49+
EXPECT_THROW(stan::math::yule_simon_rng(stan::math::positive_infinity(), rng),
50+
std::domain_error);
51+
52+
EXPECT_THROW(stan::math::yule_simon_rng(stan::math::not_a_number(), rng),
53+
std::domain_error);
54+
}

0 commit comments

Comments
 (0)