On 03/06, Bob Carpenter wrote:
>
> > On Mar 6, 2015, at 1:51 AM, Allen B. Riddell <
a...@ariddell.org> wrote:
> >
> > On 03/05, Bob Carpenter wrote:
> >
> > Yes, binary and text (JSON in Protocol Buffers 3.0) serialization is
> > available. (As an aside, I'm not a huge fan of PB in general but for
> > dealing with C++ interop specifically it seems like the best tool.)
>
> Sounds good.
>
>
> ...
>
> > Ok, I think we're on the same page here. Yes, I think the signature
> > should be std::vector<std::vector<T>> and that one should guarantee (not
> > hard, IIRC) that within each std::vector<T> memory is contiguous. I
> > *think* you can cast that to an Eigen matrix (in Stan). This would avoid
> > having Python or R touch Eigen.
>
> Not quite. vector<vector<T>> has no contiguity guarantee.
> But Eigen::Matrix does. You can't quite cast---you need
> to use their Eigen::Map expression template.
>
> We don't want to use vector<vector<T>> for anything compute
> intensive because of the lack of contiguity guarantee.
What if there's contiguous memory for the inner vector, the vector<T>?
Can Eigen work with that? Or does the entire thing need to be
contiguous?
>
> >>> should also clarify that I mean going from a JSON serialized C++ class
> >>> (or similar) to the C++ class itself --- that's what's (moderately)
> >>> painful.
> >>
> >> I think that's pretty much impossible in general, since C++ classes
> >> can be anything. We could write specific cases if the data's all
> >> strings or numbers.
> >
> > But in the specific case of var_context, Protocol Buffers could do it
> > easily.
>
> Agreed. I didn't realize that's what we were talking about.
> How was Eigen related to this?
>
No -- more or less separate. It seems that the chains are too large to
serialize, no matter how efficiently one goes about it.
>
> > Remember what var_context is:
> >
> > std::map<std::string,
> > std::pair<std::vector<double>,
> > std::vector<size_t> > > vars_r_;
> > std::map<std::string,
> > std::pair<std::vector<int>,
> > std::vector<size_t> > > vars_i_;
> > std::vector<double> const empty_vec_r_;
> > std::vector<int> const empty_vec_i_;
> > std::vector<size_t> const empty_vec_ui_;
>
> I like to think of the var_context as an interface. That's
> a specific implementation of var_context I used for the dump
> data format.
>
> I think if we went to something like JSON or protocol buffers,
> then following the shape of the data would be more natural
> than this mess, which I only introduced for convenience for the
> case at hand.
>
>
> > Serializing and deserializing (binary or text) is the sort of thing
> > Protocol Buffers was made for as far as I can tell.
>
> Yes, but only for structured data like this --- not for general
> C++ classes, which can have anything in them, like pointers and
> references and file handles and ... That's why I was confused
> earlier.
You're right -- function pointers and file handles need to be handled
separately. But I don't think we pass file handles except for the stderr
stream, right?
Just to check, I'm looking at the stan_args configuration class in R --
I think the function and file pointers are already handled separately. I
think the entire thing could be Protocol Buffer/JSON-ized. (In some
respects this seems easier than var_context.)
Data in the C++ class rstan::stan_args (which is, btw, 600 LOC):
unsigned int random_seed;
unsigned int chain_id;
std::string init;
SEXP init_list;
double init_radius;
std::string sample_file; // the file for outputting the samples
bool append_samples;
bool sample_file_flag; // true: write out to a file; false, do not
stan_args_method_t method;
std::string diagnostic_file;
bool diagnostic_file_flag;
union {
struct {
int iter; // number of iterations
int refresh; //
sampling_algo_t algorithm;
int warmup; // number of warmup
int thin;
bool save_warmup; // weather to save warmup samples (always true now)
int iter_save; // number of iterations saved
int iter_save_wo_warmup; // number of iterations saved wo warmup
bool adapt_engaged;
double adapt_gamma;
double adapt_delta;
double adapt_kappa;
unsigned int adapt_init_buffer;
unsigned int adapt_term_buffer;
unsigned int adapt_window;
double adapt_t0;
sampling_metric_t metric; // UNIT_E, DIAG_E, DENSE_E;
double stepsize; // defaut to 1;
double stepsize_jitter;
int max_treedepth; // for NUTS, default to 10.
double int_time; // for HMC, default to 2 * pi
} sampling;
struct {
int iter; // default to 2000
int refresh; // default to 100
optim_algo_t algorithm; // Newton, (L)BFGS
bool save_iterations; // default to false
double init_alpha; // default to 0.001, for (L)BFGS
double tol_obj; // default to 1e-12, for (L)BFGS
double tol_grad; // default to 1e-8, for (L)BFGS
double tol_param; // default to 1e-8, for (L)BFGS
double tol_rel_obj; // default to 1e4, for (L)BFGS
double tol_rel_grad; // default to 1e7, for (L)BFGS
int history_size; // default to 5, for LBFGS only
} optim;
struct {
double epsilon; // default to 1e-6, for test_grad
double error; // default to 1e-6, for test_grad
} test_grad;
} ctrl;