Hi everyone,
Here's a proposal for a change to the PyStan/RStan/CmdStan interface
that would eliminate a lot of code duplication. It also happens to be an
easier change than any of the proposed Stan 3.0 API changes
(
https://github.com/stan-dev/rstan/wiki/Interfaces-3.0-Spec). I'd
welcome any comments.
---
Sampler and optimizing parameters (e.g., adapt_gamma, stepsize_jitter,
...) are gathered and validated by RStan, PyStan, and Stan using nearly
identical code. It would be advantageous if checking to see if
parameters make sense occurred in one place in the Stan C++ library.
Interfaces (RStan/PyStan/etc) could provide a set of arguments to Stan,
Stan could validate the arguments and then return a coherent StanArgs
instance (or throw an informative exception). This change would require
modest changes to existing code.
RStan provides an example of what the StanArgs class might look like in:
rstan/rstan/inst/include/rstan/stan_args.hpp.
One challenge is that (de)serializing C++ classes directly is impossible
because C++ lacks reflection. On the other hand, there are some JSON libraries
out there, including one that's built from Boost, that would make the work of
constructing an instance easier. In this scenario, PyStan/RStan would submit a
prepared set of arguments for validation in JSON. CmdStan might even use this
interface directly since command-line arguments are already strings.
For some additional background, here's an example from PyStan and RStan of
duplicated code (stan_fit.hpp):
```
if (model.num_params_r() == 0 && algorithm != Fixed_param) {
throw std::runtime_error("Must use algorithm=\"Fixed_param\" for model that has no parameters.");
}
```
and CmdStan:
```
if (model.num_params_r() == 0 && algo->value() != "fixed_param") {
<< "Must use algorithm=fixed_param for model that has no parameters."
```
The main reason to consolidate validation of arguments is that keeping track of
default values (when the user doesn't provide a value for, say,
`stepsize_jitter`) in 3+ different places creates a situation where it's easy
for a developer to make a mistake and to have one interface fall out of sync
with the others.
Thanks,
Allen
p.s. Estimate of the lines of code dealing with validating sampler/optimizing arguments:
3% of lines of code in `rstan/rstan/R`
25% of lines of code in `rstan/rstan/inst/include/rstan`
6% of lines of code in `pystan/*.py`.
30% of lines of code in `pystan/*.hpp`.