| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
There was a problem hiding this comment.
Overall big fan of the changes! Just a couple of general queries before a full review
Sorry, something went wrong.
| log_softmax_alpha(k).d_ += negative_alpha_m_d_times_softmax_alpha_t_m; | ||
| inline auto log_softmax(T&& x) { | ||
| return apply_vector_unary<T>::apply( | ||
| std::forward<T>(x), [&](const auto& alpha) { |
There was a problem hiding this comment.
| std::forward<T>(x), [&](const auto& alpha) { | |
| std::forward<T>(x), [](auto&& alpha) { |
The apply_vector_unary functors themselves should probably also perfect-forwarding, since they'll be passing their inputs to apply_* functions as well.
Also probably best to remove the reference-capture default while we're here
Sorry, something went wrong.
There was a problem hiding this comment.
The apply_vector_unary functors themselves should probably also perfect-forwarding, since they'll be passing their inputs to apply_* functions as well.
Can you clarify? I'm not seeing in this function how much forwarding can be done. I do the perfect forwarding in the actual code for apply_vector_unary etc if that is was you mean.
Also probably best to remove the reference-capture default while we're here
Agree
Sorry, something went wrong.
| static inline auto apply(const T2& x) { | ||
| return F::fun(x); | ||
| } |
There was a problem hiding this comment.
| static inline auto apply(const T2& x) { | |
| return F::fun(x); | |
| } | |
| static inline auto apply(T2&& x) { | |
| return F::fun(std::forward<T2>(x)); | |
| } |
Should this also be forwarding? As the downstream calls are forwarding their arguments to apply_scalar_unary<*>::apply()
Sorry, something went wrong.
| return apply_vector_unary<T>::apply( | ||
| y, [K](auto&& v) { return cholesky_corr_constrain(v, K); }); | ||
| inline auto cholesky_corr_constrain(T&& y, int K) { | ||
| return apply_vector_unary<std::decay_t<T>>::apply( |
There was a problem hiding this comment.
Not something you necessarily need to change in this PR, but it might be a bit cleaner to move the std::decay_t<T> handling into apply_vector_unary itself
Sorry, something went wrong.
| template <typename T_x, typename T_lp> | ||
| inline auto corr_constrain(const T_x& x, T_lp& lp) { | ||
| inline auto corr_constrain(T_x&& x, T_lp& lp) { | ||
| plain_type_t<T_x> tanh_x = tanh(x); |
There was a problem hiding this comment.
| plain_type_t<T_x> tanh_x = tanh(x); | |
| plain_type_t<T_x> tanh_x = tanh(std::forward<T_x>(x)); |
Sorry, something went wrong.
| template <typename T, typename L, require_all_stan_scalar_t<T, L>* = nullptr, | ||
| require_all_not_st_var<T, L>* = nullptr> | ||
| inline auto lb_constrain(const T& x, const L& lb) { | ||
| inline auto lb_constrain(T&& x, const L& lb) { |
There was a problem hiding this comment.
Looks like the the std::forward<T>(x) is missing from this function, and there are other container overloads in the file which probably need forwarding added as well
Sorry, something went wrong.
There was a problem hiding this comment.
So for functions that just operate on scalars I don't think we need to worry about forwarding as much since those functions will immediately evaluate
Sorry, something went wrong.
| require_not_std_vector_t<M>* = nullptr> | ||
| inline auto offset_multiplier_free(const std::vector<T>& x, const M& mu, | ||
| const std::vector<S>& sigma) { | ||
| inline auto offset_multiplier_free(T&& x, const M& mu, S&& sigma) { |
There was a problem hiding this comment.
I think mu also needs to be forwarded here since it's passed to to_ref below
Sorry, something went wrong.
| divide(subtract(std::forward<T>(y), std::forward<M>(mu_ref)), | ||
| std::forward<S>(sigma_ref))); |
There was a problem hiding this comment.
| divide(subtract(std::forward<T>(y), std::forward<M>(mu_ref)), | |
| std::forward<S>(sigma_ref))); | |
| divide(subtract(std::forward<T>(y), std::forward<decltype(mu_ref)>(mu_ref)), | |
| std::forward<decltype(sigma_ref)>(sigma_ref))); |
Sorry, something went wrong.
| auto&& mu_ref = to_ref(std::forward<M>(mu)); | ||
| auto&& sigma_ref = to_ref(std::forward<S>(sigma)); |
There was a problem hiding this comment.
Should these also be forwarded in the offset_multiplier_free call below?
Sorry, something went wrong.
| lp += log_inv_logit_x + log1m_inv_logit(x); | ||
| return exp(log_inv_logit_x); | ||
| inline auto prob_constrain(T&& x, return_type_t<T>& lp) { | ||
| plain_type_t<T> log_inv_logit_x = log_inv_logit(x); |
There was a problem hiding this comment.
| plain_type_t<T> log_inv_logit_x = log_inv_logit(x); | |
| plain_type_t<T> log_inv_logit_x = log_inv_logit(std::forward<T>(x)); |
Sorry, something went wrong.
|
Re #3208, this branch fixes the issue in test/prob/poisson/poisson_ccdf_log_00000_generated_v_test, but I'm still getting ASAN failures in test/prob/loglogistic/loglogistic_cdf_00001_generated_ffv_test on line 116 of loglogistic_cdf.hpp math/stan/math/prim/prob/loglogistic_cdf.hpp Lines 115 to 116 in f7ccc01 Could be related to #3147? |
Sorry, something went wrong.
…arried through them
|
I do think we should weigh the odds that merging something of this scope during a release window would introduce more issues than it resolves (especially because the issues seem to have been present in the code for a couple years) |
Sorry, something went wrong.
|
I agree this is a large PR. Once I found the bug in one place I realized it was kind of systematic to all of our functions that can take in an expression and then output an expression. Let's talk irl. I agree this is risky before a release, but also think the bug is kind of large and can also happen in user space |
Sorry, something went wrong.
|
Not denying it can happen to users, but as far as we know it hasn’t in the couple years it has been present. So, would it be that bad to hold the fix a couple months for the next release Anyway, especially if @andrjohns thinks he can review it, I’m not opposed to merging, just wanted to ask in the name of due diligence |
Sorry, something went wrong.
|
Yes I agree that it feels risky. But this is a very odd bug and imo I don't feel great about having this in a release when we know it is there. |
Sorry, something went wrong.
Definitely happy to review! But given that the first 2.37 RC was already a month ago and we'd want another RC with pretty decent testing after this many files were touched, I'd just worry that it would push out the broader release schedule. Although I don't have a good sense of how severe/urgent the issue is, so I'm happy to go either way |
Sorry, something went wrong.
The changes to laplace are going to require (at least) one more release candidate anyway. We got a bit caught out with the level of feedback and some travel by various developers which has stretched out the release |
Sorry, something went wrong.
I'd list the issue as severe. I don't think it is so sever we need a 1800 line PR to fix it though. This is mostly to figure out which pieces need changed. My goal here is to pinpoint the underlying issue and check and check which things need changed. Right now I think there are two main issues
This will touch a lot, but not as much as in this PR hopefully. To be able to replicate this issue I think we need to change the expression tests so that they generate use temp matrices. This is actually not that bad of a change. Right now it just generates them as a separate line like the following. auto matrix4 = stan::test::make_arg<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>>(0.4, 1);
auto matrix5 = stan::test::make_arg<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>>(0.4, 1);
auto result6 = stan::math::eval(stan::math::mdivide_right_spd(matrix0_expr2,matrix1_expr3));
auto result7 = stan::math::eval(stan::math::mdivide_right_spd(matrix4,matrix5));But we just need to inline matrix4/5 so that they are temporaries auto result6 = stan::math::eval(stan::math::mdivide_right_spd(matrix0_expr2,matrix1_expr3));
auto result7 = stan::math::eval(stan::math::mdivide_right_spd(
stan::test::make_arg<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>>(0.4, 1),
stan::test::make_arg<Eigen::Matrix<double, Eigen::Dynamic, Eigen::Dynamic>>(0.4, 1))); |
Sorry, something went wrong.
|
Sounds good to me! I'll hold off on any reviews then until you've got 'final' PR |
Sorry, something went wrong.
|
I think I have something smaller that fixes this. Not tiny but not as big as this |
Sorry, something went wrong.
| Back | FazBrowse Home | New Git URL |
Summary
This fixes #3208 by using perfect forwarding for all functions that use our underlying apply family of functions for calling functions on containers and containers and containers. The issue was that the Holder class, when used in the apply functors, did not have enough type information to know which arguments it should take ownership of.
Consider the following function, where all types are passed in via constant reference.
Calling this function with an Eigen expression that has a temporary in it would not give apply_scalar_binary and the Holder inside of apply_scalar_binary enough information to know that the Holder class should own any of the input arguments. As an example we can look at a simplified version of the code used in poisson_lccdf.hpp.
gamma_p uses apply_scalar_binary and log uses apply_scalar_unary. We need to make sure the inputs and results of the gamma_p function do not fall out of scope by the time we go through log and then assign to log_Pi. Before this PR it would be possible for the expression n_val + 1.0 to fall out of scope as well as the result of gamma_p to go out of scope from log after log_Pi is assigned.
To combat this we now use perfect forwarding for all of the functions that use our internal apply family of functors. This should allow the Holder used internally by the apply functors to know which types need to be owned by it to make sure things do not fall out of scope.
Tests
There is no new tests for this. Since it is an isue on gcc I do wonder how we should test this in our CI/CD?
Side Effects
I'd like to think of some test we can write so that, in the future, developers do not accidentally write functions that use the apply family of functors that do not use perfect forwarding.
Release notes
Adds perfect forwarding to all functions that use the apply family of functors.
Checklist
Copyright holder: Steve Bronder
The copyright holder is typically you or your assignee, such as a university or company. By submitting this pull request, the copyright holder is agreeing to the license the submitted work under the following licenses:
- Code: BSD 3-clause (https://opensource.org/licenses/BSD-3-Clause)
- Documentation: CC-BY 4.0 (https://creativecommons.org/licenses/by/4.0/)
the basic tests are passing
the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested