Skip to content
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

Numerically Stable gamma_lcdf Gradients #2767

Merged
merged 3 commits into from
Jun 27, 2022
Merged

Numerically Stable gamma_lcdf Gradients #2767

merged 3 commits into from
Jun 27, 2022

Conversation

andrjohns
Copy link
Collaborator

@andrjohns andrjohns commented Jun 26, 2022

Summary

This PR adapts the improved gamma_p gradients from #780 for use with gamma_lcdf, to improve stability of estimation with large inputs.

Tests

New mix/prob tests added for a range of inputs

Side Effects

N/A

Release notes

Improved numerical stability of gamma_lcdf gradients

Checklist

@andrjohns andrjohns requested a review from spinkney June 26, 2022 10:34
@andrjohns andrjohns changed the title Numericall Stable gamma_lcdf Gradients Numerically Stable gamma_lcdf Gradients Jun 26, 2022
@rok-cesnovar
Copy link
Member

Thanks Andrew!

@spinkney
Copy link
Collaborator

spinkney commented Jun 26, 2022

Would it work to just call gamma_p like in the below? Seems easier to understand.

Like

namespace stan {
namespace math {

template <typename T_y, typename T_shape, typename T_inv_scale>
return_type_t<T_y, T_shape, T_inv_scale> gamma_lcdf(const T_y& y,
                                                    const T_shape& alpha,
                                                    const T_inv_scale& beta) {
  using T_y_ref = ref_type_t<T_y>;
  using T_alpha_ref = ref_type_t<T_shape>;
  using T_beta_ref = ref_type_t<T_inv_scale>;
  static const char* function = "gamma_lcdf";
  check_consistent_sizes(function, "Random variable", y, "Shape parameter",
                         alpha, "Inverse scale parameter", beta);
  T_y_ref y_ref = y;
  T_alpha_ref alpha_ref = alpha;
  T_beta_ref beta_ref = beta;
  check_positive_finite(function, "Shape parameter", alpha_ref);
  check_positive_finite(function, "Inverse scale parameter", beta_ref);
  check_nonnegative(function, "Random variable", y_ref);

  if (size_zero(y, alpha, beta)) {
    return 0;
  }

  auto const y_ = as_column_vector_or_scalar(y_ref);
  auto const alpha_ = as_column_vector_or_scalar(alpha_ref);
  auto const beta_ = as_column_vector_or_scalar(beta_ref);
  
  return log(gamma_p(alpha, beta_ * y_));
}

}  // namespace math
}  // namespace stan
#endif

@andrjohns
Copy link
Collaborator Author

Yep, but then the gradients for the outer log() and the b * y get auto-diffed through, so the sampling won't be as efficient

Copy link
Collaborator

@spinkney spinkney left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think only one thing. You can remove lines 66 - 77 as those were only needed in the previous version.
These lines

  VectorBuilder<!is_constant_all<T_shape>::value, T_partials_return, T_shape>
      gamma_vec(math::size(alpha));
  VectorBuilder<!is_constant_all<T_shape>::value, T_partials_return, T_shape>
      digamma_vec(math::size(alpha));
  if (!is_constant_all<T_shape>::value) {
    for (size_t i = 0; i < stan::math::size(alpha); i++) {
      const T_partials_return alpha_dbl = alpha_vec.val(i);
      gamma_vec[i] = tgamma(alpha_dbl);
      digamma_vec[i] = digamma(alpha_dbl);
    }
  }

Otherwise, looks great!

@andrjohns
Copy link
Collaborator Author

Good catch, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

4 participants