Thanks --- that was really helpful. More inline below.
> On Jun 2, 2015, at 12:47 PM, Ben Goodrich <
goodri...@gmail.com> wrote:
>
> Just going through some of the details of this ...
>
> On Thursday, May 21, 2015 at 6:06:47 PM UTC-4, Bob Carpenter wrote:
> 1. Compilation
> ------------------------------------------------------------
> The compilation function compiles a Stan program to a dynamic shared
> object (DSO) and optionally saves it.
>
> compiled_program
> rtan::stan_compile( # DEFAULTS
> source = (<string> || <path>) # no default
> name = <string>, # empty
> boost = <path>, # use BH
> eigen = <path>, # use RcppEigen
> opt = ("fast" || "debug"), # "fast"
> save_dso = <bool>, # FALSE
> debug_code = <bool> # TRUE
> );
> * Is there any reason to keep the name option?
>
> I don't think it hurts anything and the name comes up in some of the printed output.
What it hurts is readability of the doc and maintainability of
the code.
> * How long-lived is the "save_dso" option?
> - Where is it saved?
>
> It is kind of confusing. The save_dso option currently just makes the dso part of the stanfit object but you have to manually save the stanfit object to the disk to reuse it in a different session.
I'd like to make the more fine-grained objects available, like
being able to take the DSO and give it new data without recompiling.
(More on this below.)
> But this is partially obsoleted by the 2.7.0 behavior if the auto_write option is enabled, in which case it is saving the compiled model object to the disk (in the same directory as the .stan file).
>
> * If we allow optimize settings, is there a way to have it scope
> over just the call and not affect the R global build environment?
>
> The inline package doesn't permit passing opt and debug_code to CXX. That has to come from a ~./R/Makevars file.
>
> * Is there a way we can have make-like behavior here where if there's
> a saved version and the model file or code have not changed, don't
> do anything?
>
> Already implemented for 2.7.0.
Fantastic. It's working now? I thought you'd said it was very fragile.
> * Do we need the other existing compiler options ("presentation1",
> "presentation2", and "small")?
>
> Can't pass them anyway but they were just aliases for -O1, -O2, and -Os.
What does "can't pass them" mean? If they're impossible, we should remove
them from the doc.
> * Should the save_dso provide a path rather than a boolean?
>
> No. In 2.7.0, it only looks for a foo.rda file in the same directory as foo.stan.
OK
> * Is it possible to save the DSO across sessions?
>
> Not for the cxxdso object per se but yes for the stanmodel object that has a slot for the cxxdso object.
Would it be possible? It would be super great if we had
a DSO wrapper object and it was saveable, and an instantiated
object that could be saved.
> * Should the save_dso defalt be true and if so, use temp?
>
> Yes, that is what it does currently. I don't see much reason to ever specify save_dso = FALSE.
OK, then I think we should get rid of the option.
> * Do we ever need NDEBUG=false? That is, shouldn't we just always
> have -DNDEBUG set to turn off debugging?
>
> Can't pass it, but in the future I would like NDEBUG to turn off some checks. But that is a Stan thing rather than an interface thing.
Couldn't we do a preliminary call to set_cppo() or the like, or
would that run afoul of CRAN policy on explicitness of side effects?
> compiled_program
> --------------------
> This should only include the compiled Stan program and methods
> to do variable conversion, extract names, compute log densities, etc.
>
> Okay, but this would be backwards uncompatible since log_prob() and stuff are currently defined for stanfit objects because they need the data associated with them. So they are going to have to take data arguments. And we should have log_hessian() and maybe laplace_approximation().
I would very much like to move away from packing so much into
the fit object. I think we should maintain backward compatibility
until Stan 3, but there's no reason we can't introduce things the way
we want them and mark others as deprecated.
You're right that I missed a level here.
1. Compiled program --- this is the DSO thing.
2. Instantiated program class --- this is the program wedded
to its data.
We actually need the instance in (2) to do anything I suggested.
> 2. NUTS
> ------------------------------------------------------------
>
> mcmc_fit
> rstan::stan_nuts(
>
> I disagree with having so many suffixed functions for users. I think we should instead code something like
>
> setMethod("sampling", signature(program = "compiled_program", config = "stan_sample"),
> definition = function(program, config, data, pars, init, ...) {
> # implement
> })
>
> where an object of stan_nuts (or stan_fixed or whatever) class could be passed to the config argument or if not, a default one would be instantiated and used.
OK --- Rob unwound what all this meant for me, which I think means
that it'll be used as follows:
> program <- stan_model(...);
> fit <- sampling(program, config, ...);
where the config is a required argument to sampling().
Then the idea's to do what Michael likes and have a big config object
that works for everything --- optimization, MCMC, diagnosis, and whatever
else comes up? Or do you like breaking things out into sampling(),
optimizing(), diagnosing(), vbing() etc.?
I'd want to make things flatter, but I can live with this.
With this, would we need the top-level stan() object to make
people happy (again, I don't want to rip it out ASAP --- not until
an RStan 3 was solidified).
> * Return value just includes the result in such a way that it
> can be extracted as before.
>
> OK
>
> * Return value won't include the compiled program (or source).
>
> I don't see how including the compiled_program would hurt anything
It makes the objects more complicated and it makes it hard to
build a valid return object without one. Then functions need to all
be defined conditionally onw hether one's there or not if it's optional,
which is hard to debug and maintain and doc.
> * Should return value include timing information?
>
> Some people have asked for it, although currdently we don't do so programatically
This isn't a big deal.
> * There's no append_samples = <bool>
>
> OK
>
> * There's no option to compile the model from within the stan_nuts
> call.
>
> I don't think there should be a stan_nuts() call, but either way, it should pass the compiled_model in.
OK.
> * data defaults to top-level environment with lazy read (not how
> it's done now); list is list of values as before
>
> I'll implement that for 2.7.0 since no one objected to what I posted before. It is technically the environments in R's lexical order rather than always the top-level environment.
Sorry -- I didn't mean top-level environment, I meant whatever
environment the RStan method/function was called from.
> * Instead of "chains" and a start ID, there's just a "chain_ids"
>
> Currently (and I think it should stay this way in the future) sampling() only does one chain, so it only needs to know the singular chain_id for the chain it is doing. Whatever is driving it whether it is in serial or parallel should be responsible for passing it the seed, chain_id, etc.
I'm guessing users like Andrew (or even me) are going to want something
that runs multiple chains and returns a single object on which we can print(),
plot(), and extract(). So I think this has to deal with multiple chains,
or at least we need top-level drivers that are included that do that.
> * Default num_warmup and num_samples very small by default (but shouldn't
> be so small as to trigger scary messages about adaptation)
>
> Are you proposing changing the defaults of num_warmup and num_samples (1000 each)? If so, then we should do that for all the interfaces.
Yes, I think we agreed on 100 for all the interfaces.
We should make sure everyone agrees. Too many people turn on 2000 and
then go away for the weekened.
> * Metric is specified by name of type or by value; the names default
> to unit values for init
> * Those adapt parameters are all for adapt_stepsize, right?
>
> Yeah, although I think the various configs should have their own class structure.
I agree.
> * Are there adaptation parameters for mass_matrix, like regularization?
>
> Not currently but we could add them.
I think we'd need to add them through the Stan C++ side
first.
Some of these questions were intended more generally, not about
the current state of RStan or 2.7.
> * params_to_save defaults to NA (or all), or a positive list of whole
> parameter names.
>
> I'm not sure whether it is worth renaming the pars argument params_to_save, in part because in 2.7.0 there is an include = TRUE argument, so you could specify pars = "Sigma", include = FALSE to exclude Sigma but include everything else.
OK.
> * sample_file gets a copy of the output written to it, but return
> object still created --- should this just move so that the return
> object has a save on it? Or should setting this cause the return of
> this function to be NA?
>
> I think it should still return the regular stuff, in addition to writing the sample_file(s).
>
> mcmc_fit
> --------------------
>
>
> 3. EHMC
> ------------------------------------------------------------
> Plain old HMC looks just like NUTS, but with max_treedepth replaced
> by integration time:
>
> I would say they should be siblings in the class hierarchy for stan_config objects but get called the same way.
Looks like Michael strongly agrees with this. We absolutely
have to get Michael's approval on whatever we call anything that
smacks of "HMC". I'm very confused as to why Michael thinks
everyone's confused, but that's the nature of confusion.
> 4. Optimize
> ------------------------------------------------------------
>
> optimization_fit
> stan_lbfgs(
>
> Same deal. I think stan_lbfgs should be a class that inherits from stan_bfgs class with an additional slot for history_size but the user would call something like
>
> stan_optimize(compiled_program, config, data, init, verbose, ...)
OK --- I don't know what stan_bfgs is. But in general, I don't like
the idea of lbfgs inheriting from bfgs because it's not an ISA relationship.
They could both inherit from a shared class that does what's in common, which
is how Michael has a lot of the MCMC classes set up in Stan C++.
> * Should verbose be a boolean?
>
> Yes
>
> * What does current as_vector do? Current doc says "flag indicating whether a
> vector is used to for the point estimate found. A list can be
> used instead by specifying it to be FALSE"
>
> If as_vector = TRUE, then only a vector of optimal parameters is returned. If as_vector = FALSE, then there is more stuff returned with the list.
Do we ever want as_vector = TRUE? If not, let's get rid of it.
That is, if it's not too heavy, I think it's easier to return the whole
thing the same way all the time, then have an extractor or converter
for the result.
> * What's the behavior of tolerances if more than one's specified? I'd
> think it should be read as AND or as OR.
>
> Don't know.
This just needs to go in the doc. It's explained in either the Stan
manual or the CmdStan doc, but I can never remember.
> * No option to specify parameter return as vector --- use a compiled
> model's conversion.
>
> But the compiled_model doesn't know what the optimal parameter values are. At least it doesn't currently.
Right, those would have to be passed in.
> optimization_fit
> --------------------
> $par : parameters (as list)
> $value : lp__
>
> And $hessian optionally
>
> 5. Diagnose
> ------------------------------------------------------------
>
> diagnostics
> rstan::stan_diagnose(
>
> I think stan_diagnose should be a subclass of stan_config with epsilon and error_threshold that can be passed to stan_sample() or stan_optimize() like any other stan_config object.
Unless that subclass is a common subclass, then I don't like it
for the reasons above. Only subclass for ISA relationships.
> diagnostics
> --------------------
> ?? not sure what should go in here --- it needs to be printable
>
> It doesn't exactly print (in the R sense currently) but the gradients get written to R's stream from C++. I don't think the gradients get saved anywhere currently, although we could do that.
If it returns an object, that object should have a print() method
and ideally a plot() method. Then if you just call it without assigning,
it'll do the right thing and print, right?
- Bob