Some comments in line.
We could also do a hangout to discuss more.
> On Jun 29, 2015, at 11:13 AM, Michael Betancourt <
betan...@gmail.com> wrote:
>
> Will everyone take some time to look through the proposed refractor
> on feature/refactor_io_reader_writer? There’s a lot more work to be
> done but the basic pattern is in place and I don’t want to implement
> everything only to have the pattern change later.
>
> The biggest change is that src/stan/io/writer.hpp and src/stan/io/reader.hpp
> have been refactored into src/stan/io/transforms, with one file per
> transform.
I think that's a very good idea.
> This not only cleans up the model code, simplifies testing, etc,
> but admits new methods in var_context,
>
> void var_context::get_unconstrained_vector(std::vector<double>& parameters,
> const std::string& name,
> const std::string& scope,
> size_t N,
> stan::io::vector_transform& transform) {
> vector_t constr_val = vector(name, N);
> transform.unconstrain(constr_val, parameters);
> }
I'm afraid I didn't understand the intent of this method.
* The standard convention is to make the return type non-constant args
last in argument lists (we haven't been so good about that, but it's
another thing I try to do and Google code urges us to do).
* What's the scope argument? It's not used in the implementation.
* (See below --- I rethought this after writing, but want to throw it
out there anyway.)
Why is an unconstrain transform an argument rather than being
built into the function named get_unconstrained_vector. Is the idea
that you might pass in either logit + bounds or log + location + sign?
I'd prefer just having mulitple functions exposed to the API client
so that the creating of the transform and getting of the unconstrained vec
was one call:
void get_lb_unconstrained(
const string& name,
double lb,
size_t n,
vector<double>& unconstrained_vec);
And then a whole bunch more. They could call the more generic
thing above if there's a reason to have the transforms not be
static.
> The new var_context (and its derivations) can be found in
> src/stan/interface/var_context. Note also the structured accessors,
>
> double var_context::scalar(const std::string& name)
> vector_t var_context::vector(const std::string& name, size_t N)
> row_vector_t var_context::row_vector(const std::string& name, size_t N)
> matrix_t var_context::matrix(const std::string& name, size_t n_rows, size_t n_cols)
Right --- this is more what I was thinking.
But now I realize we're looking at
no bound
lower bound
upper bound
lower & upper bound
crossed with:
real
vector
row vector
matrix
for each function. I'd just built those up over time in a really
awful way in the generated code itself.
OK, so now I'm not so sure about my suggestion above. But then
I'm also not so sure on how far to go with traits to reduce the
total number of functions (which all else being equal is a good thing).
Or how to encapsulate --- do I pass the matrix readers an unconstrained
reader?
> With these changes the model code becomes substantially cleaner.
> For example, in the constructor something like
>
> context__.validate_dims("data initialization", "a", "matrix_d", context__.to_vec(5,5));
> validate_non_negative_index("a", "5", 5);
> validate_non_negative_index("a", "5", 5);
> a = matrix_d(5,5);
> vals_r__ = context__.vals_r("a");
> pos__ = 0;
> size_t a_k_mat_lim__ = 5;
> for (size_t n_mat__ = 0; n_mat__ < a_k_mat_lim__; ++n_mat__) {
> for (size_t m_mat__ = 0; m_mat__ < a_k_mat_lim__; ++m_mat__) {
> a(m_mat__,n_mat__) = vals_r__[pos__++];
> }
> }
>
> stan::math::check_cov_matrix(function__,"a",a);
>
> becomes
>
> validate_non_negative_index("a", "5", 5);
> validate_non_negative_index("a", "5", 5);
> a = context__.matrix(“a”, 5, 5);
> stan::math::check_cov_matrix(function__,"a",a);
This will be awesome and very easy to change in the generator.
And thanks for working out this detail so I don't have to reason
through it myself.
> Similarly,
>
> if (!(context__.contains_r("beta")))
> throw std::runtime_error("variable beta missing");
> vals_r__ = context__.vals_r("beta");
> pos__ = 0U;
> context__.validate_dims("initialization", "beta", "vector_d", context__.to_vec(5));
> vector_d beta(5);
> for (int j1__ = 0U; j1__ < 5; ++j1__)
> beta(j1__) = vals_r__[pos__++];
> try {
> writer__.ordered_unconstrain(beta);
> } catch (const std::exception& e) {
> throw std::runtime_error(std::string("Error transforming variable beta: ") + e.what());
> }
>
> becomes
>
> try {
> context__.get_unconstrained_vector(params_r, “beta”, “initialization”, 5, stan::io::vector_ordered(5));
> } catch(const std::exception& e) {
> throw std::runtime_error(e.what());
> }
* I'm missing where the assignment to the local variable is, so I think
that should be:
beta = context__.get_unconstrained_vector(...);
or
context__.get_unconstrained_vector(..., beta);
* Is the "initialization" just for error messages? If so, we can perhaps
try/catch around the whole block to add a location if it's helpful so
we don't have to clutter all these up --- though I guess I'm not too bothered
by this
* Whatever we do, we shouldn't have those char* converted to std::string
on the fly if we can help it. We found that to have a pretty high overhead
in tight interfaces (I'm suring it's doing it now, but it'd be nice to
get rid of).
* I think we can go one step further and roll the exception
throwing into the get_unconstrained_vector operation itself
* Having to redundantly specify the 5 is a place programmers
(like me during code gen) can make errors.
Is there a way to make vector_ordered a simple transform that
takes size arguments? The issue is then what to do with transforms
that don't require sizes?
I can see this going by way of templates, with:
context__.get_unconstrained_vector<vector_ordered>(params_r, "beta", 5);
and that's implemented by calling a static function
vector_ordered::transform(5);
But then I'm not quite sure on how to make that more general to deal with
all the permutations:
context__.get_unconstrained_matrix<lower_bound>(params_r, "Sigma", 0, 5, 7);
The lower bound of 0 needs to get to the lower_bound constraint and the 5 x 7
dims needs to be handled by get_unconstrained_matrix.
I'm back to thinking just blowing it all out wouldn't be such a bad idea.
> We can also change the log_prob code and move from using std::vectors in all of the model
> methods to Eigen::Vectors is desired.
Didn't that already happen? Or are we still wrapping? We could perhaps provide
both if we could deal with the abstraction properly --- they both give us size() and
operator[].
> It’d be nice to settle on this pattern as soon as possible so I can start the onerous task
> of implementing everything.
Agreed. If you want help, I can implement some of it.
> If you don’t like it then suggest an alternative — we’re already
> way behind on the refactor. Also, I’ll be at a conference tomorrow so I won’t be online but
> I should have plenty of email access.
This should keep you busy during boring talks.
- Bob
>
> --
> You received this message because you are subscribed to the Google Groups "stan development mailing list" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to
stan-dev+u...@googlegroups.com.
> For more options, visit
https://groups.google.com/d/optout.
>
>