-
-
Notifications
You must be signed in to change notification settings - Fork 207
Generalized normal distribution #3157
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
Open
jachymb
wants to merge
14
commits into
stan-dev:develop
Choose a base branch
from
jachymb:generalized_normal
base: develop
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Open
Changes from 8 commits
Commits
Show all changes
14 commits
Select commit
Hold shift + click to select a range
4200120
Generalized normal lpdf
jachymb 84ea5ed
Generalized normal lpdf - fix corner cases
jachymb 9d9b1cf
Generalized normal lpdf - fix corner cases
jachymb 383b51d
comments
jachymb 180b153
test file
jachymb 80c4d61
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot 9898e0b
Merge branch 'stan-dev:develop' into generalized_normal
jachymb 3911299
restore develop state for changes in normal and double_exponential
jachymb 800eb41
Incorporate changes requested by @SteveBronder
jachymb a152b70
Merge branch 'stan-dev:develop' into generalized_normal
jachymb 21daa5e
resolve merge conflict by autoformatter
jachymb 4dab045
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot a75b1f3
Merge branch 'develop' into generalized_normal
c969a3a
fix boundary at y==mu
File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,156 @@ | ||
| #ifndef STAN_MATH_PRIM_PROB_GENERALIZED_NORMAL_LPDF_HPP | ||
| #define STAN_MATH_PRIM_PROB_GENERALIZED_NORMAL_LPDF_HPP | ||
|
|
||
| #include <cmath> | ||
| #include <stan/math/prim/err.hpp> | ||
| #include <stan/math/prim/functor/partials_propagator.hpp> | ||
| #include <stan/math/prim/fun/abs.hpp> | ||
| #include <stan/math/prim/fun/as_value_column_array_or_scalar.hpp> | ||
| #include <stan/math/prim/fun/constants.hpp> | ||
| #include <stan/math/prim/fun/digamma.hpp> | ||
| #include <stan/math/prim/fun/lgamma.hpp> | ||
| #include <stan/math/prim/fun/log.hpp> | ||
| #include <stan/math/prim/fun/max_size.hpp> | ||
| #include <stan/math/prim/fun/multiply_log.hpp> | ||
| #include <stan/math/prim/fun/pow.hpp> | ||
| #include <stan/math/prim/fun/sign.hpp> | ||
| #include <stan/math/prim/fun/size.hpp> | ||
| #include <stan/math/prim/fun/size_zero.hpp> | ||
| #include <stan/math/prim/fun/square.hpp> | ||
| #include <stan/math/prim/fun/to_ref.hpp> | ||
| #include <stan/math/prim/fun/value_of.hpp> | ||
| #include <stan/math/prim/meta.hpp> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** \ingroup prob_dists | ||
| * The log of the generalized normal density for the specified scalar(s) given | ||
| * the specified location, scale and shape parameters. y, mu, alpha, or beta can | ||
| * each be either a scalar or a vector. Any vector inputs must be the same | ||
| * length. | ||
| * | ||
| * <p>The result log probability is defined to be the sum of the | ||
| * log probabilities for each observation/mean/scale/shape tuple. | ||
| * | ||
| * @tparam T_y type of scalar | ||
| * @tparam T_loc type of location parameter | ||
| * @tparam T_scale type of scale parameter | ||
| * @tparam T_shape type of shape parameter | ||
| * @param y (Sequence of) scalar(s) | ||
| * @param mu (Sequence of) location parameter(s) | ||
| * @param alpha (Sequence of) scale parameter(s) | ||
| * @param beta (Sequence of) shape parameter(s) | ||
| * @return The log of the product of the densities | ||
| * @throw std::domain_error if alpha or beta is not positive | ||
| */ | ||
| template <bool propto, typename T_y, typename T_loc, typename T_scale, | ||
| typename T_shape, | ||
| require_all_not_nonscalar_prim_or_rev_kernel_expression_t< | ||
| T_y, T_loc, T_scale, T_shape>* = nullptr> | ||
| inline return_type_t<T_y, T_loc, T_scale, T_shape> generalized_normal_lpdf( | ||
| T_y&& y, T_loc&& mu, T_scale&& alpha, T_shape&& beta) { | ||
| using T_partials_return = partials_return_t<T_y, T_loc, T_scale, T_shape>; | ||
| using T_y_ref = ref_type_if_not_constant_t<T_y>; | ||
| using T_mu_ref = ref_type_if_not_constant_t<T_loc>; | ||
| using T_alpha_ref = ref_type_if_not_constant_t<T_scale>; | ||
| using T_beta_ref = ref_type_if_not_constant_t<T_shape>; | ||
| static constexpr const char* function = "generalized_normal_lpdf"; | ||
| check_consistent_sizes(function, "Random variable", y, "Location parameter", | ||
| mu, "Scale parameter", alpha, "Shape parameter", beta); | ||
|
|
||
| T_y_ref y_ref = std::forward<T_y>(y); | ||
| T_mu_ref mu_ref = std::forward<T_loc>(mu); | ||
| T_alpha_ref alpha_ref = std::forward<T_scale>(alpha); | ||
| T_beta_ref beta_ref = std::forward<T_shape>(beta); | ||
|
|
||
| decltype(auto) y_val = to_ref(as_value_column_array_or_scalar(y_ref)); | ||
| decltype(auto) mu_val = to_ref(as_value_column_array_or_scalar(mu_ref)); | ||
| decltype(auto) alpha_val = to_ref(as_value_column_array_or_scalar(alpha_ref)); | ||
| decltype(auto) beta_val = to_ref(as_value_column_array_or_scalar(beta_ref)); | ||
|
|
||
| check_not_nan(function, "Random variable", y_val); | ||
| check_finite(function, "Location parameter", mu_val); | ||
| check_positive(function, "Scale parameter", alpha_val); | ||
| check_positive(function, "Shape parameter", | ||
| beta_val); // With β = +∞ this could be defined to be uniform, | ||
| // but we don't support that. | ||
|
|
||
| if (size_zero(y, mu, alpha, beta)) { | ||
| return 0; | ||
| } | ||
| if (!include_summand<propto, T_y, T_loc, T_scale, T_shape>::value) { | ||
| return 0; | ||
| } | ||
|
|
||
| const auto& inv_beta1p | ||
| = to_ref_if<!is_constant<T_shape>::value>(inv(beta_val) + 1); | ||
| const auto& diff | ||
| = to_ref_if<!is_constant_all<T_y, T_loc>::value>(y_val - mu_val); | ||
| const auto& inv_alpha = to_ref(inv(alpha_val)); | ||
| const auto& scaled_abs_diff | ||
| = to_ref_if<!is_constant_all<T_y, T_loc, T_shape>::value>(abs(diff) | ||
| * inv_alpha); | ||
| const auto& scaled_abs_diff_pow | ||
| = to_ref_if<!is_constant_all<T_scale, T_shape>::value>( | ||
| pow(scaled_abs_diff, beta_val)); | ||
| const size_t N = max_size(y, mu, alpha, beta); | ||
|
|
||
| T_partials_return logp = -sum(scaled_abs_diff_pow); | ||
|
|
||
| if (include_summand<propto>::value) { | ||
| logp -= LOG_TWO * N; | ||
| } | ||
| if (include_summand<propto, T_scale>::value) { | ||
| logp -= sum(log(alpha_val)) * (N / math::size(alpha)); | ||
| } | ||
| if (include_summand<propto, T_shape>::value) { | ||
| logp -= sum(lgamma(inv_beta1p)) * (N / math::size(beta)); | ||
| } | ||
|
|
||
| auto ops_partials | ||
| = make_partials_propagator(y_ref, mu_ref, alpha_ref, beta_ref); | ||
|
|
||
| if (!is_constant_all<T_y, T_loc>::value) { | ||
| // note: The partial derivatives for y, mu are undefined when y == mu && | ||
| // beta < 1. The derivative limit as mu -> y goes: | ||
| // to 0 from both sides if beta > 1 (defined as 0) | ||
| // to +1/alpha from right but -1/alpha from left if beta == 1 (defined as | ||
| // 0, consistent with double_exponential_lpdf) to +∞ from right but -∞ | ||
| // from left as y -> mu if beta < 1 (undefined) | ||
|
jachymb marked this conversation as resolved.
Outdated
|
||
| const auto& rep_deriv | ||
| = to_ref_if < !is_constant<T_y>::value | ||
| && !is_constant<T_loc>::value | ||
| > (sign(diff) * beta_val * pow(scaled_abs_diff, beta_val - 1) | ||
| * inv_alpha); | ||
|
jachymb marked this conversation as resolved.
Outdated
|
||
| if (!is_constant<T_y>::value) { | ||
| partials<0>(ops_partials) = -rep_deriv; | ||
| } | ||
| if (!is_constant<T_loc>::value) { | ||
| partials<1>(ops_partials) = std::move(rep_deriv); | ||
| } | ||
| } | ||
| if (!is_constant<T_scale>::value) { | ||
| partials<2>(ops_partials) | ||
| = (beta_val * scaled_abs_diff_pow - 1) * inv_alpha; | ||
| } | ||
| if (!is_constant<T_shape>::value) { | ||
| partials<3>(ops_partials) | ||
| = digamma(inv_beta1p) * inv_square(beta_val) | ||
| - multiply_log(scaled_abs_diff_pow, scaled_abs_diff); | ||
| } | ||
|
|
||
| return ops_partials.build(logp); | ||
| } | ||
|
|
||
| template <typename T_y, typename T_loc, typename T_scale, typename T_shape> | ||
| inline return_type_t<T_y, T_loc, T_scale, T_shape> generalized_normal_lpdf( | ||
| T_y&& y, T_loc&& mu, T_scale&& alpha, T_shape&& beta) { | ||
| return generalized_normal_lpdf<false>( | ||
| std::forward<T_y>(y), std::forward<T_loc>(mu), | ||
| std::forward<T_scale>(alpha), std::forward<T_shape>(beta)); | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
| #endif | ||
176 changes: 176 additions & 0 deletions
176
test/prob/generalized_normal/generalized_normal_test.hpp
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,176 @@ | ||
| // Arguments: Doubles, Doubles, Doubles, Doubles | ||
| #include <stan/math/prim/prob/generalized_normal_lpdf.hpp> | ||
| #include <stan/math/prim/fun/abs.hpp> | ||
| #include <stan/math/prim/fun/log.hpp> | ||
| #include <stan/math/prim/fun/pow.hpp> | ||
| #include <stan/math/prim/fun/tgamma.hpp> | ||
| #include <stan/math/prim/fun/constants.hpp> | ||
|
|
||
| using std::numeric_limits; | ||
| using std::vector; | ||
|
|
||
| class AgradDistributionGeneralizedNormal : public AgradDistributionTest { | ||
| public: | ||
| void valid_values(vector<vector<double> >& parameters, | ||
| vector<double>& log_prob) { | ||
| vector<double> param(4); | ||
|
|
||
| param[0] = 0; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 2; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -0.57236494292470008707171367567652935582); // expected log_prob | ||
|
|
||
| param[0] = 1; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 2; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -1.5723649429247000870717136756765293558); // expected log_prob | ||
|
|
||
| param[0] = -2; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 2; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -4.5723649429247000870717136756765293558); // expected log_prob | ||
|
|
||
| param[0] = -3.5; // y | ||
| param[1] = 1.9; // mu | ||
| param[2] = 7.2; // alpha | ||
| param[3] = 2; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -3.1089459689467097140959090592117462617); // expected log_prob | ||
|
|
||
| param[0] = 0; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 1; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -0.69314718055994530941723212145817656808); // expected log_prob | ||
|
|
||
| param[0] = 1; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 1; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -1.6931471805599453094172321214581765681); // expected log_prob | ||
|
|
||
| param[0] = -2; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 1; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -2.6931471805599453094172321214581765681); // expected log_prob | ||
|
|
||
| param[0] = -3.5; // y | ||
| param[1] = 1.9; // mu | ||
| param[2] = 7.2; // alpha | ||
| param[3] = 1; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -3.4172282065819549364414275049933934740); // expected log_prob | ||
|
|
||
| param[0] = 0; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 1.5; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -0.59083234759930449611508182336583846717); // expected log_prob | ||
|
|
||
| param[0] = 1; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 1.5; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -1.5908323475993044961150818233658384672); // expected log_prob | ||
|
|
||
| param[0] = -2; // y | ||
| param[1] = 0; // mu | ||
| param[2] = 1; // alpha | ||
| param[3] = 1.5; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -3.4192594723454945937184592717852346243); // expected log_prob | ||
|
|
||
| param[0] = -3.5; // y | ||
| param[1] = 1.9; // mu | ||
| param[2] = 7.2; // alpha | ||
| param[3] = 1.5; // beta | ||
| parameters.push_back(param); | ||
| log_prob.push_back( | ||
| -3.2144324264596431082120695849657575107); // expected log_prob | ||
| } | ||
|
|
||
| void invalid_values(vector<size_t>& index, vector<double>& value) { | ||
| // y | ||
|
|
||
| // mu | ||
| index.push_back(1U); | ||
| value.push_back(numeric_limits<double>::infinity()); | ||
|
|
||
| index.push_back(1U); | ||
| value.push_back(-numeric_limits<double>::infinity()); | ||
|
|
||
| // alpha | ||
| index.push_back(2U); | ||
| value.push_back(0.0); | ||
|
|
||
| index.push_back(2U); | ||
| value.push_back(-1.0); | ||
|
|
||
| index.push_back(2U); | ||
| value.push_back(-numeric_limits<double>::infinity()); | ||
|
|
||
| // beta | ||
| index.push_back(3U); | ||
| value.push_back(0.0); | ||
|
|
||
| index.push_back(3U); | ||
| value.push_back(-1.0); | ||
|
|
||
| index.push_back(3U); | ||
| value.push_back(-numeric_limits<double>::infinity()); | ||
| } | ||
|
|
||
| template <typename T_y, typename T_loc, typename T_scale, typename T_shape, | ||
| typename T5, typename T6> | ||
| stan::return_type_t<T_y, T_loc, T_scale, T_shape> log_prob( | ||
| const T_y& y, const T_loc& mu, const T_scale& alpha, const T_shape& beta, | ||
| const T5&, const T6&) { | ||
| return stan::math::generalized_normal_lpdf(y, mu, alpha, beta); | ||
| } | ||
|
|
||
| template <bool propto, typename T_y, typename T_loc, typename T_scale, | ||
| typename T_shape, typename T5, typename T6> | ||
| stan::return_type_t<T_y, T_loc, T_scale, T_shape> log_prob( | ||
| const T_y& y, const T_loc& mu, const T_scale& alpha, const T_shape& beta, | ||
| const T5&, const T6&) { | ||
| return stan::math::generalized_normal_lpdf<propto>(y, mu, alpha, beta); | ||
| } | ||
|
|
||
| template <typename T_y, typename T_loc, typename T_scale, typename T_shape, | ||
| typename T5, typename T6> | ||
| stan::return_type_t<T_y, T_loc, T_scale, T_shape> log_prob_function( | ||
| const T_y& y, const T_loc& mu, const T_scale& alpha, const T_shape& beta, | ||
| const T5&, const T6&) { | ||
| using stan::math::abs; | ||
| using stan::math::inv; | ||
| using stan::math::lgamma; | ||
| using stan::math::log; | ||
| using stan::math::LOG_TWO; | ||
|
|
||
| return -LOG_TWO - log(alpha) - lgamma(1.0 + inv(beta)) | ||
| - pow(abs(y - mu) / alpha, beta); | ||
| } | ||
| }; |
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.