Generating all-doubles version of Stan function block

Ok, right, I even found an old stan-users post where we got around that issue already! There’s a bunch of R-specific stuff that would need to be piled up on top of this generated code (helpers to pass the RNG reference or the pstream). It almost sounds as though it might be better to do a small modified code generator in an R package. :/

It could go in the RStan distribution as that seems
to be the motivating use cae. I don’t know about
Python interfacing with Stan function on the fly.

Can you paste in a target of the translation?

Can it be broken up into something that’s not R specific
plus some R-specific calls? If not, then it should definitely
go into RStan. RStan should have access to all of the
stan:: namespace from stan-dev/stan, so you could just
add it in a way that works like the existing stanc interface
in R.

  • Bob

Yes, although if you do ./runTests.py src/test/unit/lang/generator_functions_only_test.cpp
from the root of the branch you’ll get the .hpp output along with all the other code generator output (src/test/test-model/good/doubles_functions-fun.hpp or something like that). Code appended at the bottom.

Yes, the only R-specific part is that you need to insert // [Rcpp::Export]] before each template instantiation. I was just getting frustrated with how byzantine the R build system is. I’m still having trouble seeing how to avoid writing an R-level wrapper because I’m not 100% sure the magic will work with template instantiations (rather than function definitions). The documentation for Rcpp (even in-code) is a little out of sync with the codebase so…

Generated code from this branch follows, it’s a little different because the version below drops the namespaces and moves the usings/typedefs into the function def and adds the R-specific comments… It still passes the same tests.


#include <stan/model/model_header.hpp>

template <typename T0__>
typename boost::math::tools::promote_args<T0__>::type
f01(const T0__& x, std::ostream* pstream__) {
using std::istream;
using std::string;
using std::stringstream;
using std::vector;
using stan::io::dump;
using stan::math::lgamma;
using stan::model::prob_grad;
using namespace stan::math;

typedef Eigen::Matrix<double,Eigen::Dynamic,1> vector_d;
typedef Eigen::Matrix<double,1,Eigen::Dynamic> row_vector_d;
typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> matrix_d;

    typedef typename boost::math::tools::promote_args<T0__>::type fun_scalar_t__;
    typedef fun_scalar_t__ fun_return_scalar_t__;
    const static bool propto__ = true;
    (void) propto__;
    int current_statement_begin__ = -1;
    try {

        current_statement_begin__ = 2;
        return stan::math::promote_scalar<fun_return_scalar_t__>((x + x));
    } catch (const std::exception& e) {
        stan::lang::rethrow_located(e,current_statement_begin__);
        // Next line prevents compiler griping about no return
        throw std::runtime_error("*** IF YOU SEE THIS, PLEASE REPORT A BUG ***");
    }
}

// [[Rcpp::export]]
template double
f01(const double& x, std::ostream* pstream__);

sakrejda Developer
October 25
Bob_Carpenter:
Can you paste in a target of the translation?

Yes, although if you do ./runTests.py src/test/unit/lang/generator_functions_only_test.cpp
from the root of the branch you’ll get the .hpp output along with all the other code generator output (src/test/test-model/good/doubles_functions-fun.hpp or something like that). Code appended at the bottom.

Can it be broken up into something that’s not R specific
plus some R-specific calls? If not, then it should definitely
go into RStan. RStan should have access to all of the
stan:: namespace from stan-dev/stan, so you could just
add it in a way that works like the existing stanc interface
in R.

Yes, the only R-specific part is that you need to insert // [Rcpp::Export]] before each template instantiation.

That we can put into our output of something in stan-dev/stan
because a comment isn’t going to introduce a dependency (I think
it’s OK license-wise to do this).

I was just getting frustrated with how byzantine the R build system is. I’m still having trouble seeing how to avoid writing an R-level wrapper because I’m not 100% sure the magic will work with template instantiations (rather than function definitions). The documentation for Rcpp (even in-code) is a little out of sync with the codebase so…

:-) This is why most documentation in the code itself
is useless! It gets stale, so you always have to read
the code anyway.

Generated code from this branch follows, it’s a little different because the version below drops the namespaces and moves the usings/typedefs into the function def and adds the R-specific comments… It still passes the same tests.

#include <stan/model/model_header.hpp>

template
typename boost::math::tools::promote_args<T0__>::type
f01(const T0__& x, std::ostream* pstream__) {
using std::istream;
using std::string;
using std::stringstream;
using std::vector;
using stan::io::dump;
using stan::math::lgamma;
using stan::model::prob_grad;
using namespace stan::math;

typedef Eigen::Matrix<double,Eigen::Dynamic,1> vector_d;
typedef Eigen::Matrix<double,1,Eigen::Dynamic> row_vector_d;
typedef Eigen::Matrix<double,Eigen::Dynamic,Eigen::Dynamic> matrix_d;

typedef typename boost::math::tools::promote_args<T0__>::type fun_scalar_t__;
typedef fun_scalar_t__ fun_return_scalar_t__;
const static bool propto__ = true;
(void) propto__;
int current_statement_begin__ = -1;
try {

    current_statement_begin__ = 2;
    return stan::math::promote_scalar<fun_return_scalar_t__>((x + x));
} catch (const std::exception& e) {
    stan::lang::rethrow_located(e,current_statement_begin__);
    // Next line prevents compiler griping about no return
    throw std::runtime_error("*** IF YOU SEE THIS, PLEASE REPORT A BUG ***");
}

}

// [[Rcpp::export]]
template double
f01(const double& x, std::ostream* pstream__);

This isn’t right — there’s nothing after template. Doesn’t it need template<>
if it’s a specializtion? But you can overload it instead and just drop that
“template” modifier.

  • Bob
// [[Rcpp::export]]
template double
f01(const double& x, std::ostream* pstream__);

[quote]
This isn’t right — there’s nothing after template. Doesn’t it need template<>
if it’s a specializtion? But you can overload it instead and just drop that
"template" modifier.
[\quote]

I had to look this up—we don’t want to specialize we want to instantiate the templates so that they become a type rather than just a fully specified template so the correct syntax is to omit the <>

Also, it passes the relevant test (obvious what the test is if you look at the branch diff vs. develop) so it “works” that far.

K

Specifically:

C++ syntax never ceases to amaze. No idea why you’d
use that other than the more obvious template<>, but
it shouldn’t matter.

I was thinking the overload would be simpler, and then
templates don’t need to get involved at all on the R side.

sakrejda Developer
October 25

// [[Rcpp::export]]
template double
f01(const double& x, std::ostream* pstream__);

[quote]
This isn’t right — there’s nothing after template. Doesn’t it need template<>
if it’s a specializtion? But you can overload it instead and just drop that
“template” modifier.
[\quote]

I had to look this up—we don’t want to specialize we want to instantiate the templates so that they become a type rather than just a fully specified template so the correct syntax is to omit the <>

OK. I’d think just an overload would be simpler.

I’m still not quite sure I understand the operational
difference between instantiation and full specialization.
I guess it’s a matter of when the code gets generated or if
it gets generated automatically as only instantiated code
gets generated I believe.

Also, it passes the relevant test (obvious what the test is if you look at the branch diff vs. develop) so it “works” that far.

That’s usually not such a good test — there are a lot of
things that will work in g++ or clang++ that won’t work elsewhere.
Especially with all the C99 libs and C++11 and beyond support.

  • Bob

At this point as long as you’re ok with the changes on the code generator in the branch (I’ll update it so it matches what you’ve seen here) I think either overloads or instantiations would work. The most complicated part is going to be the last 10% of playing nice with the R build system so if I get that going I’ll turn this into a pull request… I don’t want to pull request and then find out it needs to change.

I’m looking at the diff vs. develop and am wondering about a few
things.

  1. Should this be a separate function? I don’t like functions that
    have bunches of flags on them because it tends to cause a combinatoric mess
    when they interact with each other. There are now two flags, so four
    different instantiations.

  2. There’s still a template parameter for RNG in the case of RNG functions.
    Do you want to make that a specific type for Stan?

  3. In general, arguments should line up vertically to functions where possible, e.g.,

int foo(int a,
int b);

  1. In generate_functions_cpp, why are you including a namespace? I thought
    they had to be at the top level for R?

  2. Don’t the using statements need to come before the functions in
    generate_functions_cpp?

Otherwise, everything looks OK.

  • Bob

Hey, thanks for taking a look:

  1. Yes I’m happy to make it a separate function, I also did some cut-and-paste in the code generator (so did everybody else it looks like!) because I didn’t want to refactor the whole thing for a small change. I’ll clean up some of that for a final pull.

  2. The template parameter for the RNG should be more specific but I think with one more layer it can be a pointer argument that the R level can handle with some helpers. This is one of the things that made me step back and go figure out exactly how this needs to fit into a specific system (R’s in this case).

  3. Yes will do indentation.

4(1). The final instantiations after the final “using” statements end up outside the namespace but most of the autogenerated code ends up inside the namespace so it seemed cleaner but I’ve gone back to doing it the other way around now with usings/typedef inside the functions and functions in the outside namespace.

5(2). The using statements at the end pull the functions outside the namespace but I’m not doing that anymore.

So it looks like this can be put on ice for the moment because getting it to work with R/Rcpp in a package has a few extra complications due to the package build system. I’m working that out now and I’ll do a pull request once I have something functional.

Thanks!

Krzysztof

4(1). The final instantiations after the final “using” statements end up outside the namespace but most of the autogenerated code ends up inside the namespace so it seemed cleaner but I’ve gone back to doing it the other way around now with usings/typedef inside the functions and functions in the outside namespace.

Thanks. That’s the cleaner and safer way to do it from the
perspective of other code including your code.

5(2). The using statements at the end pull the functions outside the namespace but I’m not doing that anymore.

It doesn’t pull the function outside the namespace so much as
make the function (with its existing namespace) visible within
the scope of the using statement. It’s a subtle point, but it
makes a difference when it comes to resolving things like
template specializations.

So it looks like this can be put on ice for the moment because getting it to work with R/Rcpp in a package has a few extra complications due to the package build system. I’m working that out now and I’ll do a pull request once I have something functional.

OK. That’s the way it goes with these deeply complicated
and dependent systems.