-
-
Notifications
You must be signed in to change notification settings - Fork 211
Add discrete_range_cdf, discrete_range_lcdf and discrete_range_lccdf #1716
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
41737b7
ebad00e
a35873a
c329bab
58ebbf8
680b4ad
093501f
0bc6f24
f0e9e51
54ad239
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #ifndef STAN_MATH_PRIM_PROB_DISCRETE_RANGE_CCDF_LOG_HPP | ||
| #define STAN_MATH_PRIM_PROB_DISCRETE_RANGE_CCDF_LOG_HPP | ||
|
|
||
| #include <stan/math/prim/prob/discrete_range_lccdf.hpp> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** \ingroup prob_dists | ||
| * @deprecated use <code>discrete_range_lccdf</code> | ||
| */ | ||
| template <typename T_y, typename T_lower, typename T_upper> | ||
| double discrete_range_ccdf_log(const T_y& y, const T_lower& lower, | ||
| const T_upper& upper) { | ||
| return discrete_range_lccdf(y, lower, upper); | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #ifndef STAN_MATH_PRIM_PROB_DISCRETE_RANGE_CDF_HPP | ||
| #define STAN_MATH_PRIM_PROB_DISCRETE_RANGE_CDF_HPP | ||
|
|
||
| #include <stan/math/prim/meta.hpp> | ||
| #include <stan/math/prim/err.hpp> | ||
| #include <stan/math/prim/fun/constants.hpp> | ||
| #include <stan/math/prim/fun/inv.hpp> | ||
| #include <stan/math/prim/fun/log.hpp> | ||
| #include <stan/math/prim/fun/max_size.hpp> | ||
| #include <stan/math/prim/fun/size_zero.hpp> | ||
| #include <cmath> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** \ingroup prob_dists | ||
| * Return the CDF of a discrete range distribution for the given y, | ||
| * lower and upper bounds (all integers). | ||
| * | ||
| * `y`, `lower` and `upper` can each be a scalar or a one-dimensional container. | ||
| * Any container arguments must be the same size. | ||
| * | ||
| * @tparam T_y type of scalar, either int or std::vector<int> | ||
| * @tparam T_lower type of lower bound, either int or std::vector<int> | ||
| * @tparam T_upper type of upper bound, either int or std::vector<int> | ||
| * | ||
| * @param y integer random variable | ||
| * @param lower integer lower bound | ||
| * @param upper integer upper bound | ||
| * @return The CDF evaluated at the specified arguments. If containers are | ||
| * supplied, returns the product of the CDFs. | ||
| * @throw std::domain_error if upper is smaller than lower. | ||
| * @throw std::invalid_argument if non-scalar arguments are of different | ||
| * sizes. | ||
| */ | ||
| template <typename T_y, typename T_lower, typename T_upper> | ||
| double discrete_range_cdf(const T_y& y, const T_lower& lower, | ||
| const T_upper& upper) { | ||
| static const char* function = "discrete_range_cdf"; | ||
| check_consistent_sizes(function, "Lower bound parameter", lower, | ||
| "Upper bound parameter", upper); | ||
| check_greater_or_equal(function, "Upper bound parameter", upper, lower); | ||
|
|
||
| if (size_zero(y, lower, upper)) { | ||
| return 1; | ||
| } | ||
|
|
||
| scalar_seq_view<T_y> y_vec(y); | ||
| scalar_seq_view<T_lower> lower_vec(lower); | ||
| scalar_seq_view<T_upper> upper_vec(upper); | ||
| size_t N = max_size(y, lower, upper); | ||
|
|
||
| for (size_t n = 0; n < N; ++n) { | ||
| const int y_dbl = y_vec[n]; | ||
| if (y_dbl < lower_vec[n]) { | ||
| return 0; | ||
| } | ||
| if (y_dbl > upper_vec[n]) { | ||
| return 1; | ||
| } | ||
| } | ||
|
|
||
| double cdf(1.0); | ||
| for (size_t n = 0; n < N; n++) { | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. [optional]
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also thought that the operations that could be precomputed here were only sums, rather than more involved operations, so I'll leave this. I have a PR for other distributions that tries to clean up this kind of operations. |
||
| const double y_dbl = y_vec[n]; | ||
| const double lower_dbl = lower_vec[n]; | ||
| const double upper_dbl = upper_vec[n]; | ||
| cdf *= (y_dbl - lower_dbl + 1) / (upper_dbl - lower_dbl + 1); | ||
| } | ||
| return cdf; | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,20 @@ | ||
| #ifndef STAN_MATH_PRIM_PROB_DISCRETE_RANGE_CDF_LOG_HPP | ||
| #define STAN_MATH_PRIM_PROB_DISCRETE_RANGE_CDF_LOG_HPP | ||
|
|
||
| #include <stan/math/prim/prob/discrete_range_lcdf.hpp> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** \ingroup prob_dists | ||
| * @deprecated use <code>discrete_range_lcdf</code> | ||
| */ | ||
| template <typename T_y, typename T_lower, typename T_upper> | ||
| double discrete_range_cdf_log(const T_y& y, const T_lower& lower, | ||
| const T_upper& upper) { | ||
| return discrete_range_lcdf(y, lower, upper); | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #ifndef STAN_MATH_PRIM_PROB_DISCRETE_RANGE_LCCDF_HPP | ||
| #define STAN_MATH_PRIM_PROB_DISCRETE_RANGE_LCCDF_HPP | ||
|
|
||
| #include <stan/math/prim/meta.hpp> | ||
| #include <stan/math/prim/err.hpp> | ||
| #include <stan/math/prim/fun/constants.hpp> | ||
| #include <stan/math/prim/fun/inv.hpp> | ||
| #include <stan/math/prim/fun/log.hpp> | ||
| #include <stan/math/prim/fun/max_size.hpp> | ||
| #include <stan/math/prim/fun/size_zero.hpp> | ||
| #include <cmath> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** \ingroup prob_dists | ||
| * Return the log CCDF of a discrete range distribution for the given y, | ||
| * lower and upper bounds (all integers). | ||
| * | ||
| * `y`, `lower` and `upper` can each be a scalar or a one-dimensional container. | ||
| * Any container arguments must be the same size. | ||
| * | ||
| * @tparam T_y type of scalar, either int or std::vector<int> | ||
| * @tparam T_lower type of lower bound, either int or std::vector<int> | ||
| * @tparam T_upper type of upper bound, either int or std::vector<int> | ||
| * | ||
| * @param y integer random variable | ||
| * @param lower integer lower bound | ||
| * @param upper integer upper bound | ||
| * @return The log CCDF evaluated at the specified arguments. If containers are | ||
| * supplied, returns the sum of the log CCDFs. | ||
| * @throw std::domain_error if upper is smaller than lower. | ||
| * @throw std::invalid_argument if non-scalar arguments are of different | ||
| * sizes. | ||
| */ | ||
| template <typename T_y, typename T_lower, typename T_upper> | ||
| double discrete_range_lccdf(const T_y& y, const T_lower& lower, | ||
| const T_upper& upper) { | ||
| static const char* function = "discrete_range_lccdf"; | ||
| check_consistent_sizes(function, "Lower bound parameter", lower, | ||
| "Upper bound parameter", upper); | ||
| check_greater_or_equal(function, "Upper bound parameter", upper, lower); | ||
|
|
||
| if (size_zero(y, lower, upper)) { | ||
| return 0; | ||
| } | ||
|
|
||
| scalar_seq_view<T_y> y_vec(y); | ||
| scalar_seq_view<T_lower> lower_vec(lower); | ||
| scalar_seq_view<T_upper> upper_vec(upper); | ||
| size_t N = max_size(y, lower, upper); | ||
|
|
||
| for (size_t n = 0; n < N; ++n) { | ||
| const int y_dbl = y_vec[n]; | ||
| if (y_dbl < lower_vec[n]) { | ||
| return 0; | ||
| } | ||
| if (y_dbl > upper_vec[n]) { | ||
| return LOG_ZERO; | ||
| } | ||
| } | ||
|
|
||
| double ccdf(0.0); | ||
| for (size_t n = 0; n < N; n++) { | ||
| const int y_dbl = y_vec[n]; | ||
| const int lower_dbl = lower_vec[n]; | ||
| const int upper_dbl = upper_vec[n]; | ||
| ccdf += log(upper_dbl - y_dbl) - log(upper_dbl - lower_dbl + 1); | ||
| } | ||
| return ccdf; | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,75 @@ | ||
| #ifndef STAN_MATH_PRIM_PROB_DISCRETE_RANGE_LCDF_HPP | ||
| #define STAN_MATH_PRIM_PROB_DISCRETE_RANGE_LCDF_HPP | ||
|
|
||
| #include <stan/math/prim/meta.hpp> | ||
| #include <stan/math/prim/err.hpp> | ||
| #include <stan/math/prim/fun/constants.hpp> | ||
| #include <stan/math/prim/fun/inv.hpp> | ||
| #include <stan/math/prim/fun/log.hpp> | ||
| #include <stan/math/prim/fun/max_size.hpp> | ||
| #include <stan/math/prim/fun/size_zero.hpp> | ||
| #include <cmath> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** \ingroup prob_dists | ||
| * Return the log CDF of a discrete range distribution for the given y, | ||
| * lower and upper bounds (all integers). | ||
| * | ||
| * `y`, `lower` and `upper` can each be a scalar or a one-dimensional container. | ||
| * Any container arguments must be the same size. | ||
| * | ||
| * @tparam T_y type of scalar, either int or std::vector<int> | ||
| * @tparam T_lower type of lower bound, either int or std::vector<int> | ||
| * @tparam T_upper type of upper bound, either int or std::vector<int> | ||
| * | ||
| * @param y integer random variable | ||
| * @param lower integer lower bound | ||
| * @param upper integer upper bound | ||
| * @return The log CDF evaluated at the specified arguments. If containers are | ||
| * supplied, returns the sum of the log CDFs. | ||
| * @throw std::domain_error if upper is smaller than lower. | ||
| * @throw std::invalid_argument if non-scalar arguments are of different | ||
| * sizes. | ||
| */ | ||
| template <typename T_y, typename T_lower, typename T_upper> | ||
| double discrete_range_lcdf(const T_y& y, const T_lower& lower, | ||
| const T_upper& upper) { | ||
| static const char* function = "discrete_range_lcdf"; | ||
| check_consistent_sizes(function, "Lower bound parameter", lower, | ||
| "Upper bound parameter", upper); | ||
| check_greater_or_equal(function, "Upper bound parameter", upper, lower); | ||
|
|
||
| if (size_zero(y, lower, upper)) { | ||
| return 0; | ||
| } | ||
|
|
||
| scalar_seq_view<T_y> y_vec(y); | ||
| scalar_seq_view<T_lower> lower_vec(lower); | ||
| scalar_seq_view<T_upper> upper_vec(upper); | ||
| size_t N = max_size(y, lower, upper); | ||
|
|
||
| for (size_t n = 0; n < N; ++n) { | ||
| const int y_dbl = y_vec[n]; | ||
| if (y_dbl < lower_vec[n]) { | ||
| return LOG_ZERO; | ||
| } | ||
| if (y_dbl > upper_vec[n]) { | ||
| return 0; | ||
| } | ||
| } | ||
|
|
||
| double cdf(0.0); | ||
| for (size_t n = 0; n < N; n++) { | ||
| const int y_dbl = y_vec[n]; | ||
| const int lower_dbl = lower_vec[n]; | ||
| const int upper_dbl = upper_vec[n]; | ||
| cdf += log(y_dbl - lower_dbl + 1) - log(upper_dbl - lower_dbl + 1); | ||
|
mcol marked this conversation as resolved.
|
||
| } | ||
| return cdf; | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
| #endif | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,65 @@ | ||
| // Arguments: Ints, Ints, Ints | ||
| #include <stan/math/prim.hpp> | ||
|
|
||
| using stan::math::var; | ||
| using std::vector; | ||
|
|
||
| class AgradCcdfLogDiscreteRange : public AgradCcdfLogTest { | ||
| public: | ||
| void valid_values(vector<vector<double>>& parameters, | ||
| vector<double>& ccdf_log) { | ||
| vector<double> param(3); | ||
|
|
||
| param[0] = 3; // y | ||
| param[1] = 1; // lower | ||
| param[2] = 5; // upper | ||
| parameters.push_back(param); | ||
| ccdf_log.push_back(log(2.0 / 5)); // expected ccdf_log | ||
|
|
||
| param[0] = 9; // y | ||
| param[1] = 5; // lower | ||
| param[2] = 15; // upper | ||
| parameters.push_back(param); | ||
| ccdf_log.push_back(log(6.0 / 11)); // expected ccdf_log | ||
|
|
||
| param[0] = 0; // y | ||
| param[1] = -4; // lower | ||
| param[2] = 5; // upper | ||
| parameters.push_back(param); | ||
| ccdf_log.push_back(log(5.0 / 10)); // expected ccdf_log | ||
| } | ||
|
|
||
| void invalid_values(vector<size_t>& /*index*/, vector<double>& /*value*/) { | ||
| // y | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. These are going to need the out-of-support tests. In general, this shouldn't have three lines of comments double spaced. A simple
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've added prim test for out-of-support values. The invalid_values section of distribution test doesn't let you express that easily, they are designed to capture values that are always invalid (say a negative rate parameter in a Poisson distribution). The formatting follows what's done in all other tests. |
||
|
|
||
| // lower | ||
|
|
||
| // upper | ||
| } | ||
|
|
||
| bool has_lower_bound() { return false; } | ||
|
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'm not exactly sure how these tests work, but this distribution has a lower and upper bound.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Those are used for distributions that have a fixed support, say the |
||
|
|
||
| bool has_upper_bound() { return false; } | ||
|
|
||
| template <class T_y, class T_lower, class T_upper, typename T3, typename T4, | ||
| typename T5> | ||
| stan::return_type_t<T_y, T_lower, T_upper> ccdf_log(const T_y& y, | ||
| const T_lower& lower, | ||
| const T_upper& upper, | ||
| const T3&, const T4&, | ||
| const T5&) { | ||
| return stan::math::discrete_range_lccdf(y, lower, upper); | ||
| } | ||
|
|
||
| template <class T_y, class T_lower, class T_upper, typename T3, typename T4, | ||
| typename T5> | ||
| stan::return_type_t<T_y, T_lower, T_upper> ccdf_log_function( | ||
| const T_y& y, const T_lower& lower, const T_upper& upper, const T3&, | ||
| const T4&, const T5&) { | ||
| if (y < lower || y > upper) { | ||
| return stan::math::LOG_ZERO; | ||
|
mcol marked this conversation as resolved.
|
||
| } | ||
|
|
||
| return log((upper - y) / (upper - lower + 1)); | ||
| } | ||
| }; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
[optional]
We have not in general marked our local args as
const. It's not a mistake per se, but it clutters the code up a lot.