GP functor gp_lpdf questions

Dear all,

I’m opening this thread to ask some questions about issue #1011, the GP Functor specification. It’s been beautifully outlined for me, to make my job a lot easier/faster. I’m anticipating mostly C++ questions.

So I’m looking at the algebra solver to see how we’re passing in functional arguments. It looks like all the test cases written are declared as a struct, and then passed into the algebra solver with no arguments. I’ve also check out Bob’s lotka-volterra test case to see how user defined functions are are generated in C++, looks like we’ve got about the same thing as in the test cases (cmdstan 2.18):

struct dz_dt_functor__ {
    template <typename T0__, typename T1__, typename T2__, typename T3__>
        std::vector<typename boost::math::tools::promote_args<T0__, T1__, T2__, T3__>::type>
    operator()(const T0__& t,
          const std::vector<T1__>& z,
          const std::vector<T2__>& theta,
          const std::vector<T3__>& x_r,
          const std::vector<int>& x_i, std::ostream* pstream__) const {
        return dz_dt(t, z, theta, x_r, x_i, pstream__);
    }
};

So a few questions…

  1. can I assume any user defined function is generated in c++ as a struct?
  2. with that said, for testing functional stuff can I just implement my own struct, as what’s done in: test/unit/math/rev/mat/functor/util_algebra_solver.hpp, for say, the RBF (cov_exp_quad)?
  3. will the functionality break if a struct becomes a class? are there any C++ conventions we should follow?
  4. anything else I can do to make sure what I’ve written is compatible with the language?
1 Like

Whatever it is, it’ll act like a functor in that you’ll be able to apply it. We might replace the explicit structs with closures (lambdas) at some point, but they’re essentially structs under the hood.

Yes.

struct { 
  ... 
};

is just syntactic sugar for:

class {
  public:
  ...
};

That’s a bit too open-ended to answer. Best thing to do is write a bit and have someone look it over if you’re worried about wandering too far down the wrong path.

I’ve answered my question. Something like this is what I was looking for:

struct L_cov_exp_quad_functor__ {
    template <typename T0__, typename T1__, typename T2__>
        Eigen::Matrix<typename boost::math::tools::promote_args<T0__, T1__, T2__>::type,
                      Eigen::Dynamic,Eigen::Dynamic>
    operator()(const std::vector<Eigen::Matrix<T0__, Eigen::Dynamic,1> >& x, // 1-D GP
               const T1__& sigma,
               const T2__& length_scale,
               std::ostream* pstream__) const {
      typedef typename boost::math::tools::promote_args<T0__, T1__>::type scalar;
      Eigen::Matrix<scalar, -1, -1> cov =
        gp_exp_quad_cov(x, sigma, length_scale);


      return cov;
    }
};
1 Like

The body of the function can be simplified to just

return  gp_exp_quad_cov(x, sigma, length_scale);

The gp_exp_quad_cov function itself will return the right type result (if it didn’t it wouldn’t be assignable).

@drezap

We can avoid a variadic template by using something like this, where we just store the parameters in a list theta:

struct normal_ll {
const Matrix<double, Dynamic, 1> y_;
normal_ll(const Matrix<double, Dynamic, 1>& y) : y_(y) { }
template <typename T>
T operator()(const Matrix<T, Dynamic, 1>& theta) const {
T mu = theta[0];
T sigma = theta[1];
T lp = 0;
for (int n = 0; n < y_.size(); ++n)
lp += normal_log(y_[n], mu, sigma);
return lp;
}
};