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

Adds support for var<matrix> in scalar_seq_view<> distributions #2214

Closed
wants to merge 28 commits into from

Conversation

SteveBronder
Copy link
Collaborator

@SteveBronder SteveBronder commented Nov 26, 2020

Summary

Adds a .val(i) method to scalar_seq_view<T> and a specialization of scalar_seq_view<> for var<matrix> types. When T has a autodiff value type .val(i) get's back the value from the autodiff type. Else if T is an arithmetic type it just returns that value

Tests

I actually don't know how to test this yet, is there a way to conditionally test this for some of the distributions in the distributions tests? Otherwise I have a separate branch for distributions are vectorized and I need to sort out the last bits of vector_seq_view. Should we wait to turn on var<matrix> tests until all of those are tested?

Side Effects

idt so

Release notes

Adds methods to helpers in probability distributions to support var<matrix>

Checklist

  • Math issue How to add static matrix? #1805

  • 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

    • unit tests pass (to run, use: ./runTests.py test/unit)
    • header checks pass, (make test-headers)
    • dependencies checks pass, (make test-math-dependencies)
    • docs build, (make doxygen)
    • code passes the built in C++ standards checks (make cpplint)
  • the code is written in idiomatic C++ and changes are documented in the doxygen

  • the new changes are tested

Comment on lines -36 to 38
inline T&& as_column_vector_or_scalar(T&& a) {
inline auto as_column_vector_or_scalar(T&& a) {
return std::forward<T>(a);
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

@t4c1 I changed this because of what we talked about in the past where passing in an rvalue and returning an rvalue can cause the destructor to go off. Should we just have specializations for doubles where it passes back the correct reference type?

Copy link
Contributor

Choose a reason for hiding this comment

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

We don't need references for doubles. Although this will always return value (even when it should return a lvalue reference). What you wanted here is decltype(auto). What I would prefer here is T - it will behave exactly the same.

} // namespace math
} // namespace stan

#endif
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

TODO: Need to write tests for all these new functions (I think we can delete some of them)

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Also update docs

@bbbales2
Copy link
Member

bbbales2 commented Dec 2, 2020

@SteveBronder got the basic normal lpdf/cdf/lcdf/lccdf tests working. I'm running all the tests now to find out if there are broken distributions.

Copy link
Contributor

@t4c1 t4c1 left a comment

Choose a reason for hiding this comment

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

This PR is getting quite large. Can you split it up? Maybe keep just one distribution in the first one and leave the reminder for another one.

* @param i index
* @return the element at the specified position in the container
*/
auto operator[](int i) const { return c_.coeffRef(i); }
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
auto operator[](int i) const { return c_.coeffRef(i); }
auto operator[](int i) const { return c_.coeff(i); }

Same change for other functions returning a value.

template <typename T, require_eigen_t<T>* = nullptr,
require_not_plain_type_t<T>* = nullptr>
inline auto to_value_array_or_scalar(T&& v) {
return value_of(v.eval()).array();
Copy link
Contributor

Choose a reason for hiding this comment

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

Why eval? Also this should use a holder for rvalues (as should other non-scalar overloads of this and other new helper functions).

Copy link
Contributor

Choose a reason for hiding this comment

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

Actually I think none of these overloads should use eval. If you need eval you should add a separate helper that also evals the result. Or if you dont need non-evaling ones you can skip them, but if some helper also evals it should have eval in its name.

@@ -159,7 +158,8 @@ return_type_t<T_size1, T_size2> beta_binomial_lpmf(const T_n& n, const T_N& N,
+= digamma_n_plus_alpha[i] + digamma_diff[i] - digamma_alpha[i];
if (!is_constant_all<T_size2>::value)
ops_partials.edge2_.partials_[i]
+= digamma(value_of(N_vec[i] - n_vec[i] + beta_vec[i]))
// is this a typo??
+= digamma(value_of(N_vec[i] - n_vec[i] + beta_vec[i])) // ??
Copy link
Contributor

Choose a reason for hiding this comment

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

Obviously invoking value_of on each term separately makes more sense.

+ !is_constant_all<T_scale>::value
+ !is_constant_all<T_y>::value
>= 2>(
inv_beta * std::move(exp_y_m_mu_over_beta) - inv_beta);
Copy link
Contributor

Choose a reason for hiding this comment

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

That move does not do anything.

Comment on lines +96 to +97
= -std::move(y_minus_mu_over_beta) * std::move(scaled_diff)
- std::move(inv_beta);
Copy link
Contributor

Choose a reason for hiding this comment

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

Nor do these.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yeah my b I think I was doing something silly here when testing this out

Comment on lines +87 to +88
auto&& sigma_val_vec
= to_ref(as_column_vector_or_scalar(value_of_rec(sigma_ref)));
Copy link
Contributor

Choose a reason for hiding this comment

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

if as_column_vector_or_scalar is called woth a lvalue it does not need to construct a holder, so previous version is better.

Comment on lines +103 to +107
auto&& x_val = to_ref_if<!is_constant<T_beta>::value>(value_of_rec(x_ref));
auto&& y_val_vec = as_column_vector_or_scalar(value_of_rec(y_ref));
auto&& alpha_val_vec = as_column_vector_or_scalar(value_of_rec(alpha_ref));
auto&& beta_val_vec = to_ref_if<!is_constant<T_x>::value>(
as_column_vector_or_scalar(value_of_rec(beta_ref)));
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you need these to be non-const for some reason?

const T_partials_return alpha_dbl = value_of(alpha_vec[n]);
const T_partials_return log_dbl = log(y_min_vec.val(n) / y_vec.val(n));
const T_partials_return y_min_inv_dbl = 1.0 / y_vec.val(n);
const T_partials_return alpha_dbl = y_vec.val(n);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
const T_partials_return alpha_dbl = y_vec.val(n);
const T_partials_return alpha_dbl = alpha_vec.val(n);

@bbbales2
Copy link
Member

I haven't looked through this yet. Let me know if I'm holding anything up by not doing that.

@SteveBronder
Copy link
Collaborator Author

No idt so, I just have a lot of other stuff on my plate and haven't had time to come back to this

@SteveBronder
Copy link
Collaborator Author

@t4c1 I think your right this should be broken up. But the one issue here is that I'd really like to not mess with the distributions test code too much as there's not meta level tests to test it and it's pretty dense/old. I think what would be good here is to break this up into three PRs.

  1. PR for backend stuff like array_or_scalar, seq_view, etc support for var<matrix>
  2. Getting all the LDLT and decomposition stuff working for var<matrix>
  3. A PR that then uses all that stuff in the distributions.

Doing all the distributions at once is brutal but without it we have to tweak the distribution tests to only run for var<matrix> for only the distributions in the successive PRs. Then we'd remove those changes to conditionally run the `var code for the distributions once all those PRs go in. tbh I think both Ben and I tried having a hack at doing the conditional distribution testing and neither of us sorted it out.

@bbbales2
Copy link
Member

I think what would be good here is to break this up into three PRs

I like this. Happy to do the big boring review ("A PR that then uses all that stuff in the distributions.")

I'm working on the ldlt/multivariate stuff now.

only run for var for only the distributions in the successive PRs

Yeah, good point, we're writing new test files and we write them for all or none (this file)

@bbbales2
Copy link
Member

There's only one big PR left for this right? It should just be the testing framework stuff?

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.

5 participants