Skip to content

Commit a936073

Browse files
committed
add yule_simon_rng
1 parent 2d953bf commit a936073

3 files changed

Lines changed: 113 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: 51 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
#ifndef STAN_MATH_PRIM_PROB_YULE_SIMON_RNG_HPP
2+
#define STAN_MATH_PRIM_PROB_YULE_SIMON_RNG_HPP
3+
4+
#include <stan/math/prim/meta.hpp>
5+
#include <stan/math/prim/prob/exponential_rng.hpp>
6+
#include <stan/math/prim/prob/neg_binomial_rng.hpp>
7+
8+
namespace stan {
9+
namespace math {
10+
11+
/** \ingroup prob_dists
12+
* Return a yule-simon random variate with the given shape parameter,
13+
* using the given random number generator.
14+
*
15+
* alpha can be a scalar or a one-dimensional container.
16+
*
17+
* @tparam T_alpha type of shape parameter
18+
* @tparam RNG type of random number generator
19+
*
20+
* @param alpha (Sequence of) shape parameter(s)
21+
* @param rng random number generator
22+
* @return (Sequence of) yule-simon random variate(s)
23+
* @throw std::domain_error if alpha is nonpositive
24+
*/
25+
template <typename T_alpha, typename RNG>
26+
inline auto yule_simon_rng(const T_alpha &alpha, RNG &rng) {
27+
using T_alpha_ref = ref_type_t<T_alpha>;
28+
static constexpr const char *function = "yule_simon_rng";
29+
30+
T_alpha_ref alpha_ref = alpha;
31+
check_positive_finite(function, "Shape parameter", alpha_ref);
32+
33+
using T_w = decltype(exponential_rng(alpha_ref, rng));
34+
T_w w = exponential_rng(alpha_ref, rng);
35+
36+
scalar_seq_view<T_w> w_vec(w);
37+
size_t size_w = stan::math::size(w);
38+
39+
VectorBuilder<true, int, T_alpha> output(size_w);
40+
for (size_t n = 0; n < size_w; ++n) {
41+
double p = exp(-w_vec.val(n));
42+
double odds_ratio_p = exp(log(p) - log1m(p));
43+
output[n] = neg_binomial_rng(1.0, odds_ratio_p, rng) + 1;
44+
}
45+
46+
return output.data();
47+
}
48+
49+
} // namespace math
50+
} // namespace stan
51+
#endif
Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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+
// Valid parameters should not throw
42+
EXPECT_NO_THROW(stan::math::yule_simon_rng(1.0, rng));
43+
EXPECT_NO_THROW(stan::math::yule_simon_rng(2.0, rng));
44+
EXPECT_NO_THROW(stan::math::yule_simon_rng(10.0, rng));
45+
46+
// Invalid parameters should throw domain_error
47+
EXPECT_THROW(stan::math::yule_simon_rng(-0.5, rng), std::domain_error);
48+
EXPECT_THROW(stan::math::yule_simon_rng(0.0, rng), std::domain_error);
49+
EXPECT_THROW(stan::math::yule_simon_rng(-10.0, rng), std::domain_error);
50+
51+
// Infinity should throw
52+
EXPECT_THROW(stan::math::yule_simon_rng(stan::math::positive_infinity(), rng),
53+
std::domain_error);
54+
55+
// NaN should throw
56+
EXPECT_THROW(stan::math::yule_simon_rng(stan::math::not_a_number(), rng),
57+
std::domain_error);
58+
}

0 commit comments

Comments
 (0)