What's the correct `require_xxx` to put in my template to specialize to none of the types being vars or matrix_vs?

I’ve run head first into the undocumented require_any/all/nots. I’m looking for a template parameter to flag to say “nothing is a var”.

Context I’m plugging along at https://github.com/stan-dev/math/issues/1787 and, in particular, trying to get a reverse mode specialization for add (which is matrix addition).

My guiding light in this is the reverse mode specification for multiply, but it has some templating tricks that are a touch beyond my understanding.

The relevant signature for the reverse mode specialization of multiply is:

template <typename Mat1, typename Mat2,
          require_all_eigen_t<Mat1, Mat2>* = nullptr,
          require_any_eigen_vt<is_var, Mat1, Mat2>* = nullptr,
          require_not_eigen_row_and_col_t<Mat1, Mat2>* = nullptr>
inline auto multiply(const Mat1& A, const Mat2& B) {/*...*/}

All well and good. (Tbqh I’m not sure why we’re checking something is a null pointer but whatevs, I’m willing to ignore that). Maybe my one question is that I have no idea what require_not_eigen_row_and_col_t<Mat1, Mat2> is checking.

But nevertheless, this works and my reverse mode checks all fly through.

The problem
The code when nothing is a var is easy, but I’m struggling with the signature. Here the multiply version doesn’t help because it’s specialized twice: once for doubles (to use OpenCL) and once for everything else. I would like to collapse this down to just one thing (there’s no point doing openCL for a matrix add).

The two signatures in the multiply file are

template <typename Mat1, typename Mat2,
          require_all_eigen_t<Mat1, Mat2>* = nullptr,
          require_all_same_t<double, value_type_t<Mat1>,
                             value_type_t<Mat2>>* = nullptr,
          require_not_eigen_row_and_col_t<Mat1, Mat2>* = nullptr>
inline auto multiply(const Mat1& m1, const Mat2& m2) { /* ... */}

and

template <typename Mat1, typename Mat2,
          require_all_eigen_vt<std::is_arithmetic, Mat1, Mat2>* = nullptr,
          require_any_not_same_t<double, value_type_t<Mat1>,
                                 value_type_t<Mat2>>* = nullptr,
          require_not_eigen_row_and_col_t<Mat1, Mat2>* = nullptr>
inline auto multiply(const Mat1& m1, const Mat2& m2) { /* ... */}

What is the correct require to say "anything but a var"?

Any help would be greatly appreciated!

template <typename Mat1, typename Mat2,
          require_all_eigen_t<Mat1, Mat2>* = nullptr,
          require_all_same_t<double, value_type_t<Mat1>, value_type_t<Mat2>>* = nullptr,
          require_not_eigen_row_and_col_t<Mat1, Mat2>* = nullptr>

Yeah the require_* can be a bit dense at first glance. This template is specified to only be called when both inputs are eigen types, containing doubles, that both aren’t column vectors or row vectors (i.e. matrices of doubles).

What is the correct require to say "anything but a var "?

The short answer is:

template <typename Mat1, typename Mat2, require_all_not_var_t<Mat1, Mat2>* = nullptr>

The long answer is that it depends on the other templates for the function, otherwise multiple templates will satisfy the conditions. For example, both of the above templates could be called for multiply(MatrixXd, MatrixXd). So you might need to add require_not_* to other templates so that they’re not ambiguous.

1 Like

Steve also put together some more comprehensive documentation for the require generics over on the math documentation site

As Andrew mentioned I have a good bit of docs on the site, but now I’m realizing there should be a comment somewhere telling people to go to the site w/ a link. Dan where did you first try to find docs for the requires? That may be a good place to put it.

Also if they are weird and you don’t like them, don’t use em’! I personally like them but I also wrote them so idt I’m a good judge. There’s some optimization left on the table but there’s nothing wrong with full signatures.

require_arithmetic_t<T>* = nullptr is a bit of a trick. The requires are just fancy aliases for

std::enable_if_t<type_trait_check<T>::value>

i.e. for arithmetic this is essentially

std::enable_if_t<std::is_arithmetic<T>::value>

std::enable_if_t returns type void if successful so

require_arithmetic_t<T>* = nullptr

becomes

void* = nullptr

which is an unnamed template non-type parameter of type void* with a default value of a nullptr. That’s all gibberish but the end result is nice in that void* is effectively useless as a template parameter but when the std::enable_if_t fails the signature drops out during Argument Dependent Lookup (ADL) through Subsitition Failure Is Not An Error (SFINAE). Acronyms lol

This should probably have a better name but it’s just checking that Mat1 is not a row vector and mat2 is not a column vector for the non-dot product specialization.

template <typename T, require_not_var_t<T>* = nullptr>

For an Eigen matrix you can do something like

template <typename T>
using is_not_var = bool_constant<!is_var<T>::value>;

template <typename EigMat, // check the value_type is non-var
 require_eigen_mat_vt<is_not_var, EigMat>* = nullptr>

or

template <typename EigMat, 
  require_eigen_t<EigMat>* = nullptr,
  require_var_t<value_type_t<EigMat>>* = nullptr>
1 Like

Thank you both for all your help! I’ve got a PR ready to go in the morning (once the local tests run)

Thank you so much!

Amazing! Thanks! For some reason when google sent me to a 404 here. Very weird!

Google, which didn’t work. I’m not sure where the docs are supposed to be.

They’re acutally good! They could probably stand a couple of harder examples. Where do they live? I can make a PR with your examples from below and one where it’s either a matrix_v or a var, which took me a lot of swearing and a lighting a votive candle to fix.

Terrifyingly I think I understood that!

require_not_eigen_rowvec_and_colvec_t? Thanks! That’s the one I just couldn’t work out (I didn’t need it it turns out). If there was a way to generate a list of all possible requires, it should be easy to see the ones with less clear names.

Odd! Try the link here

http://mc-stan.org/math/d1/db9/group__require__meta.html

Or alt go here, click module on the lhs toc, then the pseudo concepts with requires

http://mc-stan.org/math

Doesn’t that just require them not to be var types? If you want them to all be primitives, it should be require_all_arithmetic_t (not sure we have that one [yet]).

This gets tricky with scoping. But don’t you just mean an Eigen matrix type? I’ve been urging us to refer to the R = -1, C = -1 cases as matrices, the R = -1, C = 1 cases as (column) vectors, and the R = 1, C = -1 case as row vectors. They’re three completely unrelated template specializations.

Yep, that’s what Daniel was after (i.e. one specialisation for var and one for not var)