Prelim proposal for model base class


#1

Here’s a preliminary spec that is complete up to, but not including getting the constrained type and bounds from a parameter. I’ll work on adding that next.

#include <stan/model/header.hpp>

namespace stan {
namespace model_base {

model_base();
virtual ~model_base();

virtual void set_data(const var_context& data);

// T : { double, var, fvar<var>, fvar<fvar<var>> };
// P : "_propto", "";     J: "", "_nojacobian"
virtual T log_densityPJ(const Matrix<T, -1, 1>& theta_tr) const;

virtual void transform(const var_context& theta, VectorXd& theta_tr) const;

virtual void inv_transform_generate(stan::model::rng& rng,
                                    const VectorXd& theta_tr,
                                    bool tps, bool gqs, VectorXd& theta) const;

virtual string name() const;

virtual size_t num_params() const;
virtual size_t num_transformed_params() const;

virtual vector<size_t> param_sizes() const;
virtual vector<size_t> transformed_param_sizes() const;

virtual vector<string> param_names() const;
virtual vector<string> transformed_param_names() const;

virtual vector<vector<size_t>> param_indexes() const;
virtual vector<size_t> transformed_param_indexes() const;

}  // namespace stan
}  // namespace model

Notes and Questions

Remember, no templating of base class methods!

  1. It probably wasn’t clear that the index outputs for a 2 \times 3 matrix would be { {1,1}, {1, 2}, {1, 3}, {2, 1}, {2, 2}, {2, 3}}. Everything is in first-index first indexing (row-major for matrices and 2D arrays, for example)

  2. only need size_t for transformed parameter indexes because they’re all vectors

  3. Add move iterators for more scalable param_name and param_indexes

virtual MoveIterator<string> param_names_begin() const;
virtual MoveIterator<string> param_names_end() const;
virtual MoveIterator<vector<size_t>> param_indexes_begin() const;
virtual MoveIterator<vector<size_t>> param_indexes_end() const;
  1. Define functors in base class
   // T : { double, var, fvar<var>, fvar<fvar<var>> };
   T operator()(const Matrix<T, -1, 1>& theta_raw) const {
     return log_density(theta);  // unmarked case
   }

and delegate the calls to them.

  1. We can fill in move semantics or skip it.

  2. The models are no longer immutable—the whole data can be reset. This isn’t critical.

  3. The model header is the only include (it might call generic user includes
    in the mode of Eigen)

  4. The rng is fixed to a type stan::model::rng


Is there a proper base class for stan_model?
#2

This is related to two previous topics:


#3

I think we also need

virtual vector<string> data_names() const;
virtual vector<string> data_types() const;
virtual vector<string> data_sizes() const;

and also for transformed data unless we just want the data ones to cover both blocks.


#4

What does the implementation of

virtual vector<vector<size_t>> param_indexes() const;

do?


#5

We should probably do all of:

  • data
  • transformed data
  • parameters
  • transformed parameters
  • generated quantities

My original plan was to have five flags in the calls, like we do now for the last three in write_array(), but I forgot about everything other than parameters writing it out.

I should’ve included an example! It’s intended to represent the indexes of each element. So if you had

matrix[3, 2] a;
vector[2] b;

then it would return

{1,1}, {1,2}, {1,3}, {2,1}, {2,2}, {2,3}, {1}, {2}

corresponding to

a[1,1], a[1,2], a[1,3], a[2,1], a[2,2], a[2,3], b[1], b[2]

This scheme will work for tuples and ragged arrays. It corresponds to what gets printed out by CmdStan as headers,

a.1.1, a.1.2, a.1.3, a.2.1, a.2.2, a.2.3, b.1, b.2