-
-
Notifications
You must be signed in to change notification settings - Fork 199
super stable gamma_lccdf #3266
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
spinkney
wants to merge
11
commits into
develop
Choose a base branch
from
fix-gamma-lccdf-v2
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.
+462
−38
Open
super stable gamma_lccdf #3266
Changes from all commits
Commits
Show all changes
11 commits
Select commit
Hold shift + click to select a range
026b39a
super stable gamma_lccdf
spinkney 170bf8d
Merge commit '5df6fc50b02b07109e21134448d1b7f5b2c38444' into HEAD
yashikno f0cfa1c
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot 2125bb6
explicit type
spinkney 1f112d3
remove fwd and fix templating
spinkney 9e5ca4b
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot df09600
remove internal function in lccdf and add two tests for expanded range
spinkney 538f55c
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot 2c6ef87
make order of precision and max_steps the same as grad_reg_lower_inc_…
spinkney 36337f6
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot 4534897
less promotion of types
syclik 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_FUN_LOG_GAMMA_Q_DGAMMA_HPP | ||
| #define STAN_MATH_PRIM_FUN_LOG_GAMMA_Q_DGAMMA_HPP | ||
|
|
||
| #include <stan/math/prim/meta.hpp> | ||
| #include <stan/math/prim/fun/constants.hpp> | ||
| #include <stan/math/prim/fun/digamma.hpp> | ||
| #include <stan/math/prim/fun/exp.hpp> | ||
| #include <stan/math/prim/fun/gamma_p.hpp> | ||
| #include <stan/math/prim/fun/gamma_q.hpp> | ||
| #include <stan/math/prim/fun/grad_reg_inc_gamma.hpp> | ||
| #include <stan/math/prim/fun/inv.hpp> | ||
| #include <stan/math/prim/fun/lgamma.hpp> | ||
| #include <stan/math/prim/fun/log.hpp> | ||
| #include <stan/math/prim/fun/log1m.hpp> | ||
| #include <stan/math/prim/fun/tgamma.hpp> | ||
| #include <stan/math/prim/fun/value_of.hpp> | ||
| #include <cmath> | ||
|
|
||
| namespace stan { | ||
| namespace math { | ||
|
|
||
| /** | ||
| * Result structure containing log(Q(a,z)) and its gradient with respect to a. | ||
| * | ||
| * @tparam T return type | ||
| */ | ||
| template <typename T> | ||
| struct log_gamma_q_result { | ||
| T log_q; ///< log(Q(a,z)) where Q is upper regularized incomplete gamma | ||
| T dlog_q_da; ///< d/da log(Q(a,z)) | ||
| }; | ||
|
|
||
| namespace internal { | ||
|
|
||
| /** | ||
| * Compute log(Q(a,z)) using continued fraction expansion for upper incomplete | ||
| * gamma function. | ||
| * | ||
| * @tparam T_a Type of shape parameter a (double or fvar types) | ||
| * @tparam T_z Type of value parameter z (double or fvar types) | ||
| * @param a Shape parameter | ||
| * @param z Value at which to evaluate | ||
| * @param precision Convergence threshold | ||
| * @param max_steps Maximum number of continued fraction iterations | ||
| * @return log(Q(a,z)) with the return type of T_a and T_z | ||
| */ | ||
| template <typename T_a, typename T_z> | ||
| inline return_type_t<T_a, T_z> log_q_gamma_cf(const T_a& a, const T_z& z, | ||
| double precision = 1e-16, | ||
| int max_steps = 250) { | ||
| using stan::math::lgamma; | ||
| using stan::math::log; | ||
| using stan::math::value_of; | ||
| using std::fabs; | ||
| using T_return = return_type_t<T_a, T_z>; | ||
|
|
||
| const T_return log_prefactor = a * log(z) - z - lgamma(a); | ||
|
|
||
| T_return b = z + 1.0 - a; | ||
| T_return C = (fabs(value_of(b)) >= EPSILON) ? b : T_return(EPSILON); | ||
| T_return D = 0.0; | ||
| T_return f = C; | ||
|
|
||
| for (int i = 1; i <= max_steps; ++i) { | ||
| T_a an = -i * (i - a); | ||
| b += 2.0; | ||
|
|
||
| D = b + an * D; | ||
| if (fabs(D) < EPSILON) { | ||
| D = EPSILON; | ||
| } | ||
| C = b + an / C; | ||
| if (fabs(C) < EPSILON) { | ||
| C = EPSILON; | ||
| } | ||
|
|
||
| D = inv(D); | ||
| T_return delta = C * D; | ||
| f *= delta; | ||
|
|
||
| const double delta_m1 = value_of(fabs(value_of(delta) - 1.0)); | ||
| if (delta_m1 < precision) { | ||
| break; | ||
| } | ||
| } | ||
|
|
||
| return log_prefactor - log(f); | ||
| } | ||
|
|
||
| } // namespace internal | ||
|
|
||
| /** | ||
| * Compute log(Q(a,z)) and its gradient with respect to a using continued | ||
| * fraction expansion, where Q(a,z) = Gamma(a,z) / Gamma(a) is the regularized | ||
| * upper incomplete gamma function. | ||
| * | ||
| * This uses a continued fraction representation for numerical stability when | ||
| * computing the upper incomplete gamma function in log space, along with | ||
| * analytical gradient computation. | ||
| * | ||
| * @tparam T_a type of the shape parameter | ||
| * @tparam T_z type of the value parameter | ||
| * @param a shape parameter (must be positive) | ||
| * @param z value parameter (must be non-negative) | ||
| * @param precision convergence threshold | ||
| * @param max_steps maximum iterations for continued fraction | ||
| * @return structure containing log(Q(a,z)) and d/da log(Q(a,z)) | ||
| */ | ||
| template <typename T_a, typename T_z> | ||
| inline log_gamma_q_result<return_type_t<T_a, T_z>> log_gamma_q_dgamma( | ||
| const T_a& a, const T_z& z, double precision = 1e-16, int max_steps = 250) { | ||
| using std::exp; | ||
| using std::log; | ||
| using T_return = return_type_t<T_a, T_z>; | ||
|
|
||
| const double a_dbl = value_of(a); | ||
| const double z_dbl = value_of(z); | ||
|
|
||
| log_gamma_q_result<T_return> result; | ||
|
|
||
| // For z > a + 1, use continued fraction for better numerical stability | ||
| if (z_dbl > a_dbl + 1.0) { | ||
| result.log_q = internal::log_q_gamma_cf(a_dbl, z_dbl, precision, max_steps); | ||
|
|
||
| // For gradient, use: d/da log(Q) = (1/Q) * dQ/da | ||
| // grad_reg_inc_gamma computes dQ/da | ||
| const double Q_val = exp(result.log_q); | ||
| const double dQ_da | ||
| = grad_reg_inc_gamma(a_dbl, z_dbl, tgamma(a_dbl), digamma(a_dbl)); | ||
| result.dlog_q_da = dQ_da / Q_val; | ||
|
|
||
| } else { | ||
| // For z <= a + 1, use log1m(P(a,z)) for better numerical accuracy | ||
| const double P_val = gamma_p(a_dbl, z_dbl); | ||
| result.log_q = log1m(P_val); | ||
|
|
||
| // Gradient: d/da log(Q) = (1/Q) * dQ/da | ||
| // grad_reg_inc_gamma computes dQ/da | ||
| const double Q_val = exp(result.log_q); | ||
| if (Q_val > 0) { | ||
| const double dQ_da | ||
| = grad_reg_inc_gamma(a_dbl, z_dbl, tgamma(a_dbl), digamma(a_dbl)); | ||
| result.dlog_q_da = dQ_da / Q_val; | ||
| } else { | ||
| // Fallback if Q rounds to zero - use asymptotic approximation | ||
| result.dlog_q_da = log(z_dbl) - digamma(a_dbl); | ||
| } | ||
| } | ||
|
|
||
| return result; | ||
| } | ||
|
|
||
| } // namespace math | ||
| } // namespace stan | ||
|
|
||
| #endif |
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
Oops, something went wrong.
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.
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.
I was having a hard time parsing this logic, could we do the branching logic for the autodiff types first and then do the
use_cfon the inside? It looks like you need branching logic for bothvarandfvartypes.