@@ -29,6 +29,66 @@ struct log_gamma_q_result {
2929 T dlog_q_da; // /< d/da log(Q(a,z))
3030};
3131
32+ namespace internal {
33+
34+ /* *
35+ * Compute log(Q(a,z)) using continued fraction expansion for upper incomplete
36+ * gamma function.
37+ *
38+ * @tparam T_a Type of shape parameter a (double or fvar types)
39+ * @tparam T_z Type of value parameter z (double or fvar types)
40+ * @param a Shape parameter
41+ * @param z Value at which to evaluate
42+ * @param max_steps Maximum number of continued fraction iterations
43+ * @param precision Convergence threshold
44+ * @return log(Q(a,z)) with same type as T_a and T_z
45+ */
46+ template <typename T_a, typename T_z>
47+ inline auto log_q_gamma_cf (const T_a& a, const T_z& z, int max_steps = 250 ,
48+ double precision = 1e-16 ) {
49+ using stan::math::lgamma;
50+ using stan::math::log;
51+ using stan::math::value_of;
52+ using std::fabs;
53+ using T_return = return_type_t <T_a, T_z>;
54+
55+ const T_return a_ret = a;
56+ const T_return z_ret = z;
57+ const auto log_prefactor = a_ret * log (z_ret) - z_ret - lgamma (a_ret);
58+
59+ auto b = z_ret + 1.0 - a_ret;
60+ auto C = (fabs (value_of (b)) >= EPSILON) ? b : T_return (EPSILON);
61+ auto D = T_return (0.0 );
62+ auto f = C;
63+
64+ for (int i = 1 ; i <= max_steps; ++i) {
65+ auto an = -i * (i - a_ret);
66+ b += 2.0 ;
67+
68+ D = b + an * D;
69+ if (fabs (value_of (D)) < EPSILON) {
70+ D = T_return (EPSILON);
71+ }
72+ C = b + an / C;
73+ if (fabs (value_of (C)) < EPSILON) {
74+ C = T_return (EPSILON);
75+ }
76+
77+ D = 1.0 / D;
78+ auto delta = C * D;
79+ f *= delta;
80+
81+ const double delta_m1 = value_of (fabs (value_of (delta) - 1.0 ));
82+ if (delta_m1 < precision) {
83+ break ;
84+ }
85+ }
86+
87+ return log_prefactor - log (f);
88+ }
89+
90+ } // namespace internal
91+
3292/* *
3393 * Compute log(Q(a,z)) and its gradient with respect to a using continued
3494 * fraction expansion, where Q(a,z) = Gamma(a,z) / Gamma(a) is the regularized
@@ -50,7 +110,6 @@ template <typename T_a, typename T_z>
50110inline log_gamma_q_result<return_type_t <T_a, T_z>> log_gamma_q_dgamma (
51111 const T_a& a, const T_z& z, int max_steps = 250 , double precision = 1e-16 ) {
52112 using std::exp;
53- using std::fabs;
54113 using std::log;
55114 using T_return = return_type_t <T_a, T_z>;
56115
@@ -61,39 +120,8 @@ inline log_gamma_q_result<return_type_t<T_a, T_z>> log_gamma_q_dgamma(
61120
62121 // For z > a + 1, use continued fraction for better numerical stability
63122 if (z_dbl > a_dbl + 1.0 ) {
64- // Continued fraction for Q(a,z) in log space
65- // log(Q(a,z)) = log_prefactor - log(continued_fraction)
66- const double log_prefactor = a_dbl * log (z_dbl) - z_dbl - lgamma (a_dbl);
67-
68- double b = z_dbl + 1.0 - a_dbl;
69- double C = (fabs (b) >= EPSILON) ? b : EPSILON;
70- double D = 0.0 ;
71- double f = C;
72-
73- for (int i = 1 ; i <= max_steps; ++i) {
74- const double an = -i * (i - a_dbl);
75- b += 2.0 ;
76-
77- D = b + an * D;
78- if (fabs (D) < EPSILON) {
79- D = EPSILON;
80- }
81- C = b + an / C;
82- if (fabs (C) < EPSILON) {
83- C = EPSILON;
84- }
85-
86- D = 1.0 / D;
87- const double delta = C * D;
88- f *= delta;
89-
90- const double delta_m1 = fabs (delta - 1.0 );
91- if (delta_m1 < precision) {
92- break ;
93- }
94- }
95-
96- result.log_q = log_prefactor - log (f);
123+ result.log_q
124+ = internal::log_q_gamma_cf (a_dbl, z_dbl, max_steps, precision);
97125
98126 // For gradient, use: d/da log(Q) = (1/Q) * dQ/da
99127 // grad_reg_inc_gamma computes dQ/da
0 commit comments