I think we can construct a simple, modular configuration object for each of our services using multiple inheritance. Here’s a bare-bones example for some of nuts(_diag_e_adapt). The point would be that other services would be able to share these objects. Everything gets validated modularly, then more validation can also happen at higher levels.
I’m only using these two base config classes as an example—we’d want to make them as reusable as possible. They’re easy to compose into bigger chunks just by defining a class that inherits from them all and calls their constructors and validators.
Me, too, which is why this approach didn’t occur to me initially.
The CmdStan config used to be in the services, too, until the big refactor, which was undertaken to eliminate that style of config. So yes, we (mainly @syclik and me) thought about it and disliked it so much that we (all @syclik on the coding side) rebuilt the current services layer to eliminate it.
I don’t like the hierarchical organization—I’m never ever able to figure out CmdStan inputs without pulling out the manual and looking at the call skeletons. I want to flatten everything out like a normal command.
Also, the dynamic casting is intrinsically dangerous (in that it can throw runtime type errors), but that’s not my main concern.
I’m pretty sure we’ll be able to knock what we’re doing down to a few service functions:
nuts, nuts_dense // all Euclidean cases with and without adaptation
simulate // now Fixed_param
bfgs, l_bfgs // @bgoodri thinks there's utility in maintaining bfgs as well as l-bfgs
advi, advi_dense // only two of these
I would very much like to see these reflected in the CmdStan and other interface calls. Right now, the rstan::stan()call in R or the sampling option in CmdStan cover all of the NUTS and static HMC cases, so there are lots of argument interactions.
I don’t think anyone will miss the static HMC calls or the Newton optimizer. My goal originally was to have functions that didn’t have any nasty argument interactions (of the form, parameter a is only valid if parameter b is defined and vice-versa, which you get by putting all 7 of these together) and could thus easily support defaults and modular error checking.
Until we find other ones that are better! I’m not trying to imply that Stan is always and forever just implementing nuts and nuts_dense. People have been asking about other algorithms, and I always say the same thing: we take pull requests, but if you want to get it merged, it will have to have some comprehensive comparisons to our current approaches.
If you want to add it to the current CmdStan, then we should leave the old way of doing things intact until we upgrade to CmdStan3 and remove everything that’s deprecated.
The first thing to do is lay down a functional spec—just a description of what the new commands will look like from a user point of view. We then want to follow the same basic flat structure with simple out-of-process wrappers for Python and R.