-
-
Notifications
You must be signed in to change notification settings - Fork 190
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
Added a few varmat functions (Issue #2101) #2106
Conversation
…4.1 (tags/RELEASE_600/final)
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.
Questions @SteveBronder
|
||
T_return res = (arena_x_val.array() * arena_x_val.array()).colwise().sum(); | ||
|
||
if (x.size() > 0) { |
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.
What is the proper way to return a size zero thing?
When the input and output are both size zero, I just pass the input through.
If I have a valid return type and just return a {}
for the zero case, sometimes I end up with something that doesn't have a vari and everything downstream explodes.
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'd just return x, since we also know that is size 0 (and that means it doesn't hold anything)
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.
The return type is different than the argument type in this case.
stan/math/rev/fun/matrix_power.hpp
Outdated
arena_t<plain_type_t<T>> arena_M = M_ref; | ||
|
||
powers[0] = Eigen::MatrixXd::Identity(N, N); | ||
powers[1] = value_of(M_ref).eval(); |
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 needed to add the eval here otherwise I got this error when running the mix tests:
[==========] Running 1 test from 1 test suite.
[----------] Global test environment set-up.
[----------] 1 test from MathMatrixPower
[ RUN ] MathMatrixPower.ad_tests
matrix_power_test: lib/eigen_3.3.7/Eigen/src/Core/DenseBase.h:257: void Eigen::DenseBase<Eigen::Map<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 8, Eigen::Stride<0, 0> > >::resize(Eigen::Index, Eigen::Index) [Derived = Eigen::Map<Eigen::Matrix<double, -1, -1, 0, -1, -1>, 8, Eigen::Stride<0, 0> >]: Assertion `rows == this->rows() && cols == this->cols() && "DenseBase::resize() does not actually allow to resize."' failed.
Aborted (core dumped)
test/unit/math/mix/fun/matrix_power_test --gtest_output="xml:test/unit/math/mix/fun/matrix_power_test.xml" failed
exit now (09/25/20 16:44:55 EDT)
I'm not sure what that was about.
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 think you need to give them a default size when you make the vector like
arena_t<std::vector<Eigen::MatrixXd>> powers(n + 1, Eigen::MatrixXd(N, N));
Otherwise the vector holds a bunch of default initialized matrices
stan/math/rev/fun/tcrossprod.hpp
Outdated
arena_t<plain_type_t<T>> arena_M = M; | ||
arena_t<Eigen::MatrixXd> arena_M_val = value_of(arena_M); | ||
|
||
arena_t<T_var> res = arena_M_val * arena_M_val.transpose(); |
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.
This is a better way to handle vars for the matvar case:
math/stan/math/rev/fun/tcrossprod.hpp
Line 39 in 0aaea95
arena_matrix<Eigen::Matrix<var, T::RowsAtCompileTime, T::RowsAtCompileTime>> |
Should we just be in the habit of doing both implementations? The reverse pass callback code is different. They're really kinda different things.
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 think we can handle both matvar and varmat in the same overload if we are smart about it.
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.
Well the big difference is that if we know the output is symmetric, then we only need to build new varis on the diagonal and one half of the other side of the matrix.
The issue then is that the reverse mode code is different from how we'd handle the varmats. The off diagonal adjoints of the matvar have been incremented twice, whereas the off diagonal adjoints of the mat var (of which there are two) have each been incremented once.
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 think it's totally fine to have separate functions when stuff is different
stan/math/rev/fun/tcrossprod.hpp
Outdated
arena_t<plain_type_t<T>> arena_M = M; | ||
arena_t<Eigen::MatrixXd> arena_M_val = value_of(arena_M); | ||
|
||
arena_t<T_var> res = arena_M_val * arena_M_val.transpose(); |
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 think we can handle both matvar and varmat in the same overload if we are smart about it.
stan/math/rev/fun/tcrossprod.hpp
Outdated
return MMt; | ||
|
||
arena_t<plain_type_t<T>> arena_M = M; | ||
arena_t<Eigen::MatrixXd> arena_M_val = value_of(arena_M); |
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.
Hmm, I think we need to do something here to avoid the copy in the varmat case. The easiest option would be to change the var to use the arena matrix internally instead of maps, so the arena_matrix constructor here will be able to do a shallow copy.
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 thinking something similar yesterday. idt it would add any unreasonable bulk and would make stuff flow a lot more naturally. I want to get in all the big changes from #2064 then we can do this in a separate PR
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.
Actually this was pretty easy-peasy!
feature/multi-expression-vari-views...feature/vari_value-arena-matrix
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.
Nice, can you open a separate pull request with this? I don't wanna slow this up going through here.
Also the code here: #2106 (comment) reminds me that maybe this would work:
const auto& arena_M_val = to_arena(value_of(arena_M));
If it see's an arena variable it just forwards it along I think.
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.
Bunch o' comments!
|
||
T_return res = (arena_x_val.array() * arena_x_val.array()).colwise().sum(); | ||
|
||
if (x.size() > 0) { |
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'd just return x, since we also know that is size 0 (and that means it doesn't hold anything)
using T_return = promote_var_matrix_t<Eigen::RowVectorXd, Mat>; | ||
|
||
arena_t<plain_type_t<Mat>> arena_x = x; | ||
arena_t<Eigen::MatrixXd> arena_x_val = value_of(arena_x); |
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.
Could we do
arena_t<Eigen::MatrixXd> arena_x_val = value_of(arena_x); | |
arena_t<decltype(value_of(arena_x))> arena_x_val = value_of(arena_x); |
So for matrix<var>
that will give back an arena_matrix<Matrix<var>>
and for var<matrix>
will give back an eigen map. That should avoid a copy here in the var<matrix>
case
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.
value_of(arena_x)
is (an expressions that evaluates to) MatrixXd
, so arena_t<decltype(value_of(arena_x))>
equals arena_t<Eigen::MatrixXd>
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.
Ah good call.
Another note though, does this make a copy for var<matrix>
rn? I think after #2064 this will be better since then we use arena_matrix
on the backend of var_value<matrix>
stan/math/rev/fun/tcrossprod.hpp
Outdated
return MMt; | ||
|
||
arena_t<plain_type_t<T>> arena_M = M; | ||
arena_t<Eigen::MatrixXd> arena_M_val = value_of(arena_M); |
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 thinking something similar yesterday. idt it would add any unreasonable bulk and would make stuff flow a lot more naturally. I want to get in all the big changes from #2064 then we can do this in a separate PR
stan/math/rev/fun/tcrossprod.hpp
Outdated
arena_t<plain_type_t<T>> arena_M = M; | ||
arena_t<Eigen::MatrixXd> arena_M_val = value_of(arena_M); | ||
|
||
arena_t<T_var> res = arena_M_val * arena_M_val.transpose(); |
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 think it's totally fine to have separate functions when stuff is different
…4.1 (tags/RELEASE_600/final)
I responded to the comments and went through and tried to make these as fast as possible for I'm re-running the benchmarks now but not great news.
I didn't check Anyway if anyone wants to mess with these to make them faster, have at it, I'm okay with where this is at so I'll just be trying to get a green light. |
…4.1 (tags/RELEASE_600/final)
…4.1 (tags/RELEASE_600/final)
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.
Question!
stan/math/rev/fun/tcrossprod.hpp
Outdated
if (M.rows() == 0) { | ||
return {}; | ||
template <typename T, require_rev_matrix_t<T>* = nullptr> | ||
inline promote_var_matrix_t<Eigen::MatrixXd, T> tcrossprod(const T& M) { |
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.
@t4c1, @SteveBronder I think there's a problem with either plain_type_t<T>
or promote_var_matrix_t
or my use thereof.
If the return type here is plain_type_t<T>
, you'll get errors compiling crossprod
(here). crossprod(x)
is implemented as tcrossprod(x.transpose())
.
When I changed the return type to promote_var_matrix_t<T>
, things worked.
I added pretty_prints for the types:
T: stan::math::test::type_name() [Arg = Eigen::Transpose<const Eigen::Matrix<stan::math::var_value<double>, -1, -1, 0, -1, -1> >
plain_type_t : stan::math::test::type_name() [Arg = Eigen::Matrix<stan::math::var_value<double>, -1, -1, 1, -1, -1>
promote_var_matrix_t : stan::math::test::type_name() [Arg = Eigen::Matrix<stan::math::var_value<double>, -1, -1, 0, -1, -1>
Is it me or it is a template?
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.
Sorry I'll look into this tmrw morning. The jenkins link sends me to
./test/unit/math/serializer.hpp:199:11: error: no matching member function for call to 'push_back'
vals_.push_back(x);
~~~~~~^~~~~~~~~
./test/unit/math/serializer.hpp:291:5: note: in instantiation of function template specialization 'stan::test::serializer<stan::math::var_value<double> >::write<Eigen::Matrix<stan::math::var_value<double>, -1, -1, 1, -1, -1> >' requested here
s.write(x);
I'm not seeing how it's related to the return type. Is there a reason you can't use auto
as the return type?
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.
Is there a reason you can't use auto as the return type
We need explicit return types here because the return type gets build internally as an arena_t<T_Return>
and you gotta set the return type to T_Return
to get it to cast to a non-arena thing.
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.
Looking at the different template arguments between the types:
Eigen::Matrix<stan::math::var_value<double>, -1, -1, 1, -1, -1>
Eigen::Matrix<stan::math::var_value<double>, -1, -1, 0, -1, -1>
That third integer is the 'Options' flag, which controls storage order. I would guess (emphasis on the guess) that the .transpose()
operator maps the matrix to row-major storage, so the plain type of the input (and the eventual return type) is a row-major matrix and then compilation fails when you try to return a column-major matrix instead. No idea why plain_type_t
and promote_var_matrix_t
would handle that differently though
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.
From the type printouts it seems plain_type_t is returning a row major matrix (yes, it can do that in some cases!). Any function not generalized for expresions is still expecting its inputs to be col major, so I guess that causes compilation failures.
This means that we shouldn't use plain_type_t (or .eval() and auto) for return values in the few functions that eval expresions, for which Eigen prefer row major types.
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.
Won't it be more of an issue with expressions since they'll propagate the row-major matrix which could lead to unexpected behaviour further down the chain?
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.
Functions handling expressions also handle row major matrices, so it won't matter.
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 mostly thinking that any looping is assuming column-major ordering (i.e., loops over column first), so some programs could end up slower if a function returned a row-major matrix at some point. But I don't know how much looping we still have at the moment
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.
We need explicit return types here because the return type gets build internally as an arena_t<T_Return> and you gotta set the return type to T_Return to get it to cast to a non-arena thing.
promote_var_matrix_t
returns a var<matrix>
or matrix<var>
so I usually just wrap the return like return ret_type(ret)
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 think we should consider any remaining loops over matrices as potential performance issues and replace them with Eigen expressions.
I'm reading the docs right now for eigen products and should we be using https://eigen.tuxfamily.org/dox/TopicWritingEfficientProductExpression.html So like this const auto& adjL = (adj.transpose() + adj)
* arena_L_val.template triangularView<Eigen::Lower>();
arena_L.adj() += adjL.template triangularView<Eigen::Lower>(); should be const auto& adjL = (adj.transpose() + adj)
* arena_L_val.template triangularView<Eigen::Lower>();
arena_L.adj().noalias() += adjL.template triangularView<Eigen::Lower>(); |
Oh interesting. I'll try it out real quick. |
I got a compile error (edit: added full error):
|
I decided it would be better to make On a side note I think we should rename |
@@ -22,5 +22,42 @@ struct is_rev_matrix< | |||
math::disjunction<is_eigen<T>, is_eigen<value_type_t<T>>>>> | |||
: std::true_type {}; | |||
|
|||
/** \ingroup type_trait |
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.
These docs don't appear to be for these functions.
@rok-cesnovar is this test messed up? It says it's been stuck in one place for 17 hours |
Seems so. Restarted now. If it repeats we need to look into it. |
Which test is it hanging on? |
Jenkins Console Log Machine informationProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010CPU: G++: Clang: |
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.
lgtm! @t4c1 do you want to do another pass through? Else I'm cool to merge this when tests pass
Jenkins Console Log Machine informationProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010CPU: G++: Clang: |
@SteveBronder I'm gonna fix the stuff @t4c1 pointed out (just so we aren't doing the same thing at the same time) |
Jenkins Console Log Machine informationProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010CPU: G++: Clang: |
@t4c1 good to go? |
Summary
This adds
var_value<Eigen::Matrix<T, R, C>>
implementations for columns_dot_self, determinant, dot_self, inverse, log_determinant, matrix_power, multiply_lower_tri_self_transpose, and tcrossprod. (part of #2101)Side Effects
Should be none
Release notes
Added
var_value<Eigen::Matrix<T, R, C>>
specializations for columns_dot_self, determinant, dot_self, inverse, log_determinant, matrix_power, multiply_lower_tri_self_transpose, and tcrossprod.Checklist
Math issue Make functions with custom autodiff
var<mat>
friendly #2101Copyright holder: Columbia University
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
./runTests.py test/unit
)make test-headers
)make test-math-dependencies
)make doxygen
)make cpplint
)the code is written in idiomatic C++ and changes are documented in the doxygen
the new changes are tested