Standalone functions, need code generator help

features

#1

Hey, so the rstan standalone functions thing is close but when Rcpp and Boost template metaprogramming come together it creates something uglier than I want to think about. Either I need to resolve some of that or get a workaround from the parser.

The problem is Rcpp generates code based on our current stan-dev/stan code that doesn’t quite match the explicit doubles instantiation. So the compiler skips our instantiation and then tries to use Rcpp’s traits with our templated function and that doesn’t work (boost::promote_args chokes on Rcpp’s input_parameter traits). If I put the templated version in it’s own namespace (“stan”) and I call it from the instantiation explicitly ( double f_rng(...) {return stan::f_rng<double....>(...); } ). Could someone familiar with the code generation (@Bob_Carpenter or @mitzimorris?) suggest how to make this change?

Alternatively, does someone understand off hand why hiding the templated function within a namespace is necessary within the following code? This code is as-generated except I hid the templated version in the namespace and called it from the instantiation (stan::f_rng). The error you get otherwise is (so promote_args isn’t made to work with Rcpp or vice-versa, that makes sense):

 In instantiation of ‘typename boost::math::tools::promote_args<T>::type f_rng(const T0__&, RNG&, std::ostream*) [with T0__ = Rcpp::ConstReferenceInputParameter<double>; RNG = Rcpp::ConstReferenceInputParameter<boost::random::additive_combine_engine<boost::random::linear_congruential_engine<unsigned int, 40014u, 0u, 2147483563u>, boost::random::linear_congruential_engine<unsigned int, 40692u, 0u, 2147483399u> >&>; typename boost::math::tools::promote_args<T>::type = Rcpp::ConstReferenceInputParameter<double>; std::ostream = std::basic_ostream<char>]’:
file2e5a2ea9e478.cpp:103:65:   required from here
file2e5a2ea9e478.cpp:38:76: error: no matching function for call to ‘Rcpp::ConstReferenceInputParameter<double>::ConstReferenceInputParameter(double)’
         fun_scalar_t__ DUMMY_VAR__(std::numeric_limits<double>::quiet_NaN());

// Code generated by Stan version 2.17.0

// [[Rcpp::depends(rstan)]]
#include <exporter.h>
#include <RcppEigen.h>
#include <stan/model/standalone_functions_header.hpp>
#include <stan/model/model_header.hpp>

using std::istream;
using std::string;
using std::stringstream;
using std::vector;
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;

stan::io::program_reader prog_reader__() {
    stan::io::program_reader reader;
    reader.add_event(0, 0, "start", "unkown file name");
    reader.add_event(8, 8, "end", "unkown file name");
    return reader;
}

namespace stan {
  template <typename T0__, class RNG>
  typename boost::math::tools::promote_args<T0__>::type
  f_rng(const T0__& mu, RNG& base_rng__, std::ostream* pstream__) {
      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__;
          fun_scalar_t__ DUMMY_VAR__(std::numeric_limits<double>::quiet_NaN());
          (void) DUMMY_VAR__;  // suppress unused var warning
  
      int current_statement_begin__ = -1;
      try {
          {
          current_statement_begin__ = 4;
          fun_scalar_t__ x;
          (void) x;  // dummy to suppress unused var warning
  
          stan::math::initialize(x, std::numeric_limits<double>::quiet_NaN());
          stan::math::fill(x,DUMMY_VAR__);
          stan::math::assign(x,normal_rng(mu,1, base_rng__));
  
  
          current_statement_begin__ = 5;
          return stan::math::promote_scalar<fun_return_scalar_t__>(x);
          }
      } catch (const std::exception& e) {
          stan::lang::rethrow_located(e, current_statement_begin__, prog_reader__());
          // Next line prevents compiler griping about no return
          throw std::runtime_error("*** IF YOU SEE THIS, PLEASE REPORT A BUG ***");
      }
  }
  
  
  struct f_rng_functor__ {
      template <typename T0__, class RNG>
          typename boost::math::tools::promote_args<T0__>::type
      operator()(const T0__& mu, RNG& base_rng__, std::ostream* pstream__) const {
          return f_rng(mu, base_rng__, pstream__);
      }
  };
}

// [[Rcpp::export]]
double
f_rng(const double& mu, boost::ecuyer1988& base_rng__, std::ostream* pstream__ = 0){
  return stan::f_rng<double, boost::ecuyer1988>(mu, base_rng__, pstream__);
}





#include <Rcpp.h>
// __create_rng
boost::ecuyer1988 __create_rng(int seed);
RcppExport SEXP sourceCpp_1___create_rng(SEXP seedSEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::RNGScope rcpp_rngScope_gen;
    Rcpp::traits::input_parameter< int >::type seed(seedSEXP);
    rcpp_result_gen = Rcpp::wrap(__create_rng(seed));
    return rcpp_result_gen;
END_RCPP
}
// f_rng
double f_rng(const double& mu, boost::ecuyer1988& base_rng__, std::ostream* pstream__);
RcppExport SEXP sourceCpp_1_f_rng(SEXP muSEXP, SEXP base_rng__SEXP, SEXP pstream__SEXP) {
BEGIN_RCPP
    Rcpp::RObject rcpp_result_gen;
    Rcpp::RNGScope rcpp_rngScope_gen;
    Rcpp::traits::input_parameter< const double& >::type mu(muSEXP);
    Rcpp::traits::input_parameter< boost::ecuyer1988& >::type base_rng__(base_rng__SEXP);
    Rcpp::traits::input_parameter< std::ostream* >::type pstream__(pstream__SEXP);
    rcpp_result_gen = Rcpp::wrap(f_rng(mu, base_rng__, pstream__));
    return rcpp_result_gen;
END_RCPP
}

#2

Missed this a week ago. Do you still need help with the generator? It’s relatively simple other than the handling of variant types.

No idea about the specifics of your matching case or what all that Rcpp stuff does.

C++ name lookup looks at all the included names that match, then chooses the one with the best matching signature (if there are two equally specific matches, it fails due to ambiguity). Only base templates are included at this stage, not specializations. If there are arguments defined in a namespace, functions in that namespace are automatically included (argument-dependent lookup). Then, if you matched a template, the compiler will find the most specific template specialization that matches your arguments, which again has to be unique.

I’d be a bit worried about the include of all of the Stan namespace leaking. We should start just qualifying all the Stan math that’s being used.


#3

I haven’t managed to get to this yet. Could we do a chat to figure out a
few details? I think ideally the generated Stan functions should go in a
namespace (stan::functions? stan::math? Stan::generated?) and for Rcpp we
can dump an instantiated version into the global namespace that calls the
templated version with full namespace qualification. Does that seem
reasonable on the Stan end?


#4

How about Thursday afternoon some time for chatting?

Yes, I thought that was the plan. I don’t think it should go in the the stan:: namespace if that can be avoided—the code isn’t part of Stan per se.

There’s a namespace automatically generated for the model and we could just follow that. Otherwise, just make something up like rstan::rcpp or something.


#5

If the namespace shouldn’t be in stan:: it’s easier, that was one of my
questions (I wasn’t sure why we set up the namespaces vector but now it
makes sense). I’ll just pass the namepaces vector further down and rstan
can call it with an ‘rstan::’ namespace. I’m driving to Boston Thursday
afternoon so I’ll get in touch separately to find a time.