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 views for var_value<Eigen::Matrix> class #2024

Merged
merged 38 commits into from
Sep 7, 2020
Merged

Conversation

SteveBronder
Copy link
Collaborator

Summary

This adds the views block(), row(), col(), operator()(), coeff() segment(), head(), tail() to var_value and vari_value.

Some things to note

  1. These views are read only and so the returning type and member functions are const.
  2. Blocks of eigen matrices need different Strides so this adds a new template parameter to vari_value that allows the underlying Eigen::Map to have an InnerStride<> or OuterStride<>.
  3. Because the mapped type is const and we need to specify a stride for the vari_type inside of var_value there's an additional template VariType for var_value that allows for the underlying vari_type to be overridden. So for example in the block() function we have
  inline const auto block(Eigen::Index i, Eigen::Index j, Eigen::Index p, Eigen::Index q) const {
    using vari_sub = decltype(vi_->block(1, 1, 3, 3));
    using var_sub = var_value<const typename vari_sub::PlainObject, const typename vari_sub::vari_type>;
    return var_sub(new vari_sub(vi_->block(i, j, p, q)));
  }

where vari_sub::vari_type is an vari_value<Eigen::MatrixXd, Eigen::OuterStride<>>

I'd really like to get rid of the extra templates here but I'm not sure how else to handle the InnerStride<> and OuterStride<> for the underlying Eigen::Map without them.

Tests

Tests were added to var_test and vari_test to check whether subsets are taken correctly.

Side Effects

Not a side effect but some todo's include

  1. Add var_value<Matrix> overrides for the functions head() etc for the stan language to access
  2. Add these subsetting ops to var_value<SparseMatrix>. I wanted to wait on that to make sure the pattern here looked okay before I wrote it out for sparse as well.

Release notes

Replace this text with a short note on what will change if this pull request is merged in which case this will be included in the release notes.

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

stan/math/rev/core/var.hpp Outdated Show resolved Hide resolved
stan/math/rev/core/var.hpp Outdated Show resolved Hide resolved
stan/math/rev/core/vari.hpp Outdated Show resolved Hide resolved
stan/math/rev/core/vari.hpp Outdated Show resolved Hide resolved
stan/math/rev/core/vari.hpp Outdated Show resolved Hide resolved
* @param j Column index
*/
inline const auto coeff(Eigen::Index i, Eigen::Index j) const {
return vari_value<double>(val_(i, j), adj_(i, j));
Copy link
Contributor

@t4c1 t4c1 Aug 20, 2020

Choose a reason for hiding this comment

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

Making just a copy of the value and adjoint is not the correct thing to do here, as setting adjoint of the copy will not propagate it back to the matrix.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes that's intented since you can't assign to subsets of a var_value<>. That's also why these are all returning const types

Copy link
Contributor

Choose a reason for hiding this comment

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

Unfortunately there still is a problem. Even if you only use a value in a rev operation (and not change it), that rev operation still needs to update the adjoint and that adjoint needs to propagate. I think we need a scalar var view with separate pointers to value and adjoint.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Yes so I agree with this, what I'm trying to figure out right now is that for the var to decide if it holds a view or an actual vari_value I'm doing

  using vari_type = std::conditional_t<is_plain_type<value_type>::value,
                                       vari_value<value_type>, vari_view<T>>;

So this is fine and good for Block types and other views, but for views into coefficients value_type/T will be a double which is the plain type so this will become a vari_value<double>. We don't want to explicitly have a second template parameter to set the vari_type to, can you think of another way to handle this?

Copy link
Contributor

Choose a reason for hiding this comment

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

I would make a separate class var_view (not vari). This also avoids two pointer dereferences (one to get vari and second to get value/adjoint) )when operationg on such a scalar. Also since this class is only used for scalars we don't need to template it.

We could also make this object be implicitly convertible to var_value<double>, registering the callback to propagate adjoint on the stack. Although for performance reason we could also not (or at least make it explicit) and explicitly use var_view wherever this is used.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Interesting! Okay I'll work on this today

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

So I had a bit of a hard time getting what you suggested to work. In var<mat>.coeff I added a callback to propogate the adjoint of the var_value<double> back to the original var<mat>. I think this makes since assuming that the two cases this will be used 99% of the time are

  1. assigning a cell of a static matrix to a var
real a = -B[i];
  1. iterating over a static matrix to assign to the cells in a dynamic matrix
matrix[N, M] A; // assume static
matrix[N, M] B; // assume dynamic
for (i in 1:N) {
  B[i] = -A[i]; // very inefficient but a thing a user might want to do
}

so for both of these we will need to make a var anyway so having a var_view would not help. Are there normal cases I'm missing that a var_view would be more efficient? If you have any time to take a crack at this I'd be interested in seeing what you have in mind!

@SteveBronder
Copy link
Collaborator Author

Ugh I think we updated doxygen and it's catching a new doc error. Gonna look into it

@SteveBronder
Copy link
Collaborator Author

Ack this might be a doxygen bug. @serban-nicusor-toptal do you know what version of doxygen Jenkin's uses? If it's from 1.8.14-1.8.17 it might be the bug below.

doxygen/doxygen#6754

@serban-nicusor-toptal
Copy link
Contributor

serban-nicusor-toptal commented Aug 25, 2020

Hey @SteveBronder
gelman-linux has:

:~$ doxygen --version
1.8.17
(1.8.17-0ubuntu2).

Should I update it ? Latest or any specific version ?

@SteveBronder
Copy link
Collaborator Author

It looks like the amici folks were able to work around this and I just asked them what the problem was for them so we could potentially fix it here.

AMICI-dev/AMICI#507

I'm not sure updating doxygen would work. I'm going to set doxygen warnings to not error out for a bit. I'm on a debian buster setup right now that only has access to doxygen 1.8.13 so can't replicate the error but I think I can replicate this on my home desktop

@stan-buildbot
Copy link
Contributor


Name Old Result New Result Ratio Performance change( 1 - new / old )
gp_pois_regr/gp_pois_regr.stan 4.28 4.26 1.01 0.59% faster
low_dim_corr_gauss/low_dim_corr_gauss.stan 0.02 0.02 0.98 -2.19% slower
eight_schools/eight_schools.stan 0.09 0.09 1.03 3.12% faster
gp_regr/gp_regr.stan 0.2 0.2 0.99 -0.53% slower
irt_2pl/irt_2pl.stan 5.23 5.25 1.0 -0.32% slower
performance.compilation 89.86 87.57 1.03 2.55% faster
low_dim_gauss_mix_collapse/low_dim_gauss_mix_collapse.stan 8.26 8.2 1.01 0.68% faster
pkpd/one_comp_mm_elim_abs.stan 26.82 28.04 0.96 -4.56% slower
sir/sir.stan 136.57 142.32 0.96 -4.21% slower
gp_regr/gen_gp_data.stan 0.04 0.04 1.0 0.0% slower
low_dim_gauss_mix/low_dim_gauss_mix.stan 3.29 3.47 0.95 -5.36% slower
pkpd/sim_one_comp_mm_elim_abs.stan 0.38 0.39 0.97 -2.69% slower
arK/arK.stan 1.84 2.57 0.72 -39.65% slower
arma/arma.stan 0.59 0.73 0.8 -24.67% slower
garch/garch.stan 0.61 0.72 0.85 -18.14% slower
Mean result: 0.949659224092

Jenkins Console Log
Blue Ocean
Commit hash: 7a06a38


Machine information ProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010

CPU:
Intel(R) Xeon(R) CPU E5-1680 v2 @ 3.00GHz

G++:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

Clang:
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

stan/math/prim/meta/is_eigen_dense_plain_type.hpp Outdated Show resolved Hide resolved
stan/math/prim/meta/plain_type.hpp Outdated Show resolved Hide resolved
@@ -70,7 +70,7 @@ struct plain_type<T, require_t<is_detected<T, internal::plain_type_check_t>>> {
*/
template <typename T>
struct plain_type<T,
require_t<bool_constant<
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 had to add this so that it would use the default for kernel expressions. Or should we add a PlainObject to kernel expressions?

Copy link
Contributor

Choose a reason for hiding this comment

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

Or should we add a PlainObject to kernel expressions?

No. Adding a require for eigen types here is fine. If/when we do need a plain_type for kernel expressions we should add a specialization, not PlainObject.

@SteveBronder
Copy link
Collaborator Author

@serban-nicusor-toptal it looks like the distrubtion tests are failing because the jenkins machine ran out of disk storage, do you know how to fix that?

Comment on lines +375 to +377
reverse_pass_callback([vari_coeff, this, i]() mutable {
this->vi_->adj_(i) += vari_coeff->adj_;
});
Copy link
Contributor

Choose a reason for hiding this comment

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

I don't like having to do a copy for the reverse pass. I guess it works, so we can leave it for now, but I sure am revisiting this soon.

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 it's a bummer, but I think given the use case it make sense no? Like if most of the time we are using .coeff() to assign to a var then I think we need to do something like this right? But I'm fine with it for now so I can go start the plumbing up in the stan repo so we can kick of work on the compiler stuff

Copy link
Contributor

Choose a reason for hiding this comment

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

Can you wait a few days with changes in stan repo and compiler? I have an idea I would like to try on this views. I think we can make them assignable, so there is no need for thius type of matrices to be static. This would allow us to eventuallly completely replace matrix<var> with var<matrix>.

stan/math/prim/meta/plain_type.hpp Outdated Show resolved Hide resolved
@stan-buildbot
Copy link
Contributor


Name Old Result New Result Ratio Performance change( 1 - new / old )
gp_pois_regr/gp_pois_regr.stan 4.22 4.16 1.01 1.29% faster
low_dim_corr_gauss/low_dim_corr_gauss.stan 0.02 0.02 0.97 -2.86% slower
eight_schools/eight_schools.stan 0.09 0.09 0.98 -2.05% slower
gp_regr/gp_regr.stan 0.2 0.2 1.0 -0.37% slower
irt_2pl/irt_2pl.stan 5.21 5.27 0.99 -1.17% slower
performance.compilation 89.92 87.77 1.02 2.39% faster
low_dim_gauss_mix_collapse/low_dim_gauss_mix_collapse.stan 8.23 8.5 0.97 -3.24% slower
pkpd/one_comp_mm_elim_abs.stan 27.06 29.0 0.93 -7.14% slower
sir/sir.stan 139.32 130.55 1.07 6.3% faster
gp_regr/gen_gp_data.stan 0.04 0.05 1.0 -0.22% slower
low_dim_gauss_mix/low_dim_gauss_mix.stan 3.36 3.37 1.0 -0.34% slower
pkpd/sim_one_comp_mm_elim_abs.stan 0.38 0.38 1.01 1.3% faster
arK/arK.stan 2.58 2.55 1.01 1.23% faster
arma/arma.stan 0.73 0.72 1.01 1.16% faster
garch/garch.stan 0.73 0.73 1.0 -0.13% slower
Mean result: 0.998270965974

Jenkins Console Log
Blue Ocean
Commit hash: 434261b


Machine information ProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010

CPU:
Intel(R) Xeon(R) CPU E5-1680 v2 @ 3.00GHz

G++:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

Clang:
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

@SteveBronder
Copy link
Collaborator Author

@t4c1 when you get a min can you take a look at this?

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.

Looks good, there is just some cleanup left to do.

stan/math/prim/meta/plain_type.hpp Outdated Show resolved Hide resolved
stan/math/rev/core/var.hpp Outdated Show resolved Hide resolved
stan/math/rev/core/vari.hpp Outdated Show resolved Hide resolved
@stan-buildbot
Copy link
Contributor


Name Old Result New Result Ratio Performance change( 1 - new / old )
gp_pois_regr/gp_pois_regr.stan 4.18 4.26 0.98 -1.96% slower
low_dim_corr_gauss/low_dim_corr_gauss.stan 0.02 0.02 1.01 1.28% faster
eight_schools/eight_schools.stan 0.09 0.09 0.97 -2.63% slower
gp_regr/gp_regr.stan 0.19 0.2 0.99 -0.87% slower
irt_2pl/irt_2pl.stan 5.23 5.24 1.0 -0.07% slower
performance.compilation 88.61 87.46 1.01 1.29% faster
low_dim_gauss_mix_collapse/low_dim_gauss_mix_collapse.stan 8.19 8.25 0.99 -0.77% slower
pkpd/one_comp_mm_elim_abs.stan 27.5 28.1 0.98 -2.17% slower
sir/sir.stan 130.36 141.6 0.92 -8.63% slower
gp_regr/gen_gp_data.stan 0.04 0.04 1.0 -0.33% slower
low_dim_gauss_mix/low_dim_gauss_mix.stan 3.35 3.34 1.0 0.43% faster
pkpd/sim_one_comp_mm_elim_abs.stan 0.38 0.38 0.98 -2.19% slower
arK/arK.stan 2.57 2.55 1.01 0.82% faster
arma/arma.stan 0.75 0.74 1.01 0.55% faster
garch/garch.stan 0.73 0.72 1.01 0.56% faster
Mean result: 0.990841536556

Jenkins Console Log
Blue Ocean
Commit hash: 77b3ec2


Machine information ProductName: Mac OS X ProductVersion: 10.11.6 BuildVersion: 15G22010

CPU:
Intel(R) Xeon(R) CPU E5-1680 v2 @ 3.00GHz

G++:
Configured with: --prefix=/Applications/Xcode.app/Contents/Developer/usr --with-gxx-include-dir=/usr/include/c++/4.2.1
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

Clang:
Apple LLVM version 7.0.2 (clang-700.1.81)
Target: x86_64-apple-darwin15.6.0
Thread model: posix

@t4c1 t4c1 merged commit f235b6c into develop Sep 7, 2020
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.

7 participants