Is there a proper base class for stan_model?

I’d like to refer to an instance of a stan_model (i.e., an instance of a class which is defined in the stanc generated file) without having the specific class known at compile time. The parent class of is stan::model::prob_grad, so I thought I could use that, but I’m hitting problems using a pointer to a stan::model::prob_grad instance because prob_grad doesn’t define:

  • write_array
  • log_prob

which are used in the stan_model.

Is there a reason why there isn’t an (abstract?) parent class of the stan models which do define these things?

My sense is that the answer is this: class member functions cannot be templated and virtual.

No, there’s no base class. It’s just a concept. A base class, even with virtual functions, wouldn’t be a big performance hit here. But as you point out, inheritance and templating don’t play nicely together.

I’m not sure what you mean by “refer to”. The way to do pass it as an argument is with the type defined by a template parameter:

template <class M>
void foo(const M& m, ...);

Then you can pass in a model of any type and type inference will fill in M for you. You see that pattern in all the code that uses models in Stan.

Thanks. This makes sense. By “refer to” I just mean use a pointer to
prob_grad instead of a pointer to the specific model.

I can see that C++ doesn’t support what I’m trying to do: figure out the
type/class of the specific stan model at runtime. It would be very cool
indeed if it did.

I wonder if I could use boost::any here [1]. I’ll have to look into this
at some point.

[1] http://www.artima.com/cppsource/type_erasure2.html

Could you say more about the use case? The main one we’ve considered is that we can compile an object file that could be linked to different models. By requiring the template parmameter, the class type indeed needs to be resolved at compile time.

Suppose we wanted to compile the Stan algorithms (L-BFGS, HMC, ADVI, diagnosis) once ahead of time. Then we’d compile a model when we got it. In order to link those, we need some interface that doesn’t depend on the algorithms being instantiated with the model class they work on. I don’t think a base class would be too much work or induce too much overhead. We’d just need to define it.

We may be forced to confront this. I’d very much like to work with @seantalts and @syclik on cutting down compile times. I don’t see any reason in principle why we can’t precompile our math and matrix library. We’d still need a header-only density library because of the crazy number of possible instantiations. Or we’d need to figure out a way to have those resolve at runtime—I don’t think the dispatch would be that bad.

That’s exactly the case I’m thinking about.

The particular case that prompted the question is written in Cython, so
I don’t think sharing it would help much.

Is there another thread or issue for the model class refactor that @mitzimorris is working on? Can’t find it… bumping this one.

re: gains from pre-compiling parts of the model, I ran an experiment and compiled just the bernoulli.hpp as a shared object vs. bringing in the cmdstan main.cpp and thus the algorithms and services, and to summarize it seems like about half the time is being spent on math library stuff and half the time on services/algorithms. So @Bob_Carpenter the model class / algo / etc refactor seems pretty worthwhile to me.

Here are the commands I ran:

dyn-160-39-166-41 ~/scm/cmdstan (develop) $ time clang++ -std=c++1y -Wno-unknown-warning-option -Wno-tautological-compare -Wno-sign-compare -O3 -I src -I stan/src -I stan/lib/stan_math/ -I stan/lib/stan_math/lib/eigen_3.3.3 -I stan/lib/stan_math/lib/boost_1.66.0 -I stan/lib/stan_math/lib/sundials_3.1.0/include -DBOOST_RESULT_OF_USE_TR1 -DBOOST_NO_DECLTYPE -DBOOST_DISABLE_ASSERTS -DBOOST_PHOENIX_NO_VARIADIC_EXPRESSION examples/bernoulli/bernoulli.hpp -c -o b.o

real	0m23.562s
user	0m14.563s
sys	0m1.513s
dyn-160-39-166-41 ~/scm/cmdstan (develop) $ time make examples/bernoulli/bernoulli

--- Linking C++ model ---
clang++ -std=c++1y -Wno-unknown-warning-option -Wno-tautological-compare -Wno-sign-compare      -O3 -I src -I stan/src -I stan/lib/stan_math/ -I stan/lib/stan_math/lib/eigen_3.3.3 -I stan/lib/stan_math/lib/boost_1.66.0 -I stan/lib/stan_math/lib/sundials_3.1.0/include    -DBOOST_RESULT_OF_USE_TR1 -DBOOST_NO_DECLTYPE -DBOOST_DISABLE_ASSERTS -DBOOST_PHOENIX_NO_VARIADIC_EXPRESSION            -include-pch stan/src/stan/model/model_header.hpp.gch -include examples/bernoulli/bernoulli.hpp src/cmdstan/main.cpp        stan/lib/stan_math/lib/sundials_3.1.0/lib/libsundials_nvecserial.a stan/lib/stan_math/lib/sundials_3.1.0/lib/libsundials_cvodes.a stan/lib/stan_math/lib/sundials_3.1.0/lib/libsundials_idas.a  -o examples/bernoulli/bernoulli

real	0m42.764s
user	0m37.573s
sys	0m1.231s

page on Stan wiki “Base Model Class (was Stan 3 Model Concept Proposal)” here:
https://github.com/stan-dev/stan/wiki/Base-Model-Class-(was-Stan-3-Model-Concept-Proposal)

That’s very promising. The three of us should sit down and finalize the base class design.

Does this contradict what you told me in person that you thought it wouldn’t be worthwhile to refactor?

I think it’s going to be worthwhile to do this just to make the code simpler.

If you’re addressing me, I thiiink I always thought it would be worthwhile to refactor this (because I’d like to get all of these things compiled ahead of time in the hopes of eventually ditching a runtime C++ toolchain in the very long term), but I hadn’t actually run numbers until that day we talked about it.

Yes, I thought you said that just compiling one function took 95% of the time to compile a whole model or something like that.

We need a base class even if it were slower (which I doubt it would be) in order to be able to query concrete instances for the types of symbols, dimensions of symbols, etc.

1 Like

In some unrelated work I’d done on AOT compilation of the math library it had seemed like most of the compilation time was constant involving the math library, I think I was under the impression it was 80% or something. But either way, I actually measured it in this specific use-case and it seems like it’s only half the time for a simple model. Plus the other reasons to refactor.

We’re starting to design one. See Prelim proposal for model base class.