diff --git a/src/stan/callbacks/stream_logger.hpp b/src/stan/callbacks/stream_logger.hpp
index 6e6cca07e64..677add7c707 100644
--- a/src/stan/callbacks/stream_logger.hpp
+++ b/src/stan/callbacks/stream_logger.hpp
@@ -14,7 +14,7 @@ namespace callbacks {
* logger that writes messages to separate
* std::stringstream outputs.
*/
-class stream_logger : public logger {
+class stream_logger final : public logger {
private:
std::ostream& debug_;
std::ostream& info_;
diff --git a/src/stan/callbacks/tee_writer.hpp b/src/stan/callbacks/tee_writer.hpp
index 8eca4af61a7..c491832e9ec 100644
--- a/src/stan/callbacks/tee_writer.hpp
+++ b/src/stan/callbacks/tee_writer.hpp
@@ -16,7 +16,7 @@ namespace callbacks {
* For any call to this writer, it will tee the call to both writers
* provided in the constructor.
*/
-class tee_writer : public writer {
+class tee_writer final : public writer {
public:
/**
* Constructor accepting two writers.
diff --git a/src/stan/callbacks/unique_stream_writer.hpp b/src/stan/callbacks/unique_stream_writer.hpp
new file mode 100644
index 00000000000..b576a1d77a5
--- /dev/null
+++ b/src/stan/callbacks/unique_stream_writer.hpp
@@ -0,0 +1,127 @@
+#ifndef STAN_CALLBACKS_UNIQUE_STREAM_WRITER_HPP
+#define STAN_CALLBACKS_UNIQUE_STREAM_WRITER_HPP
+
+#include
+#include
+#include
+#include
+
+namespace stan {
+namespace callbacks {
+
+/**
+ * unique_stream_writer is an implementation
+ * of writer that holds a unique pointer to the stream it is
+ * writing to.
+ * @tparam Stream A type with with a valid `operator<<(std::string)`
+ */
+template
+class unique_stream_writer final : public writer {
+ public:
+ /**
+ * Constructs a unique stream writer with an output stream
+ * and an optional prefix for comments.
+ *
+ * @param[in, out] A unique pointer to a type inheriting from `std::ostream`
+ * @param[in] comment_prefix string to stream before each comment line.
+ * Default is "".
+ */
+ explicit unique_stream_writer(std::unique_ptr&& output,
+ const std::string& comment_prefix = "")
+ : output_(std::move(output)), comment_prefix_(comment_prefix) {}
+
+ unique_stream_writer();
+ unique_stream_writer(unique_stream_writer& other) = delete;
+ unique_stream_writer(unique_stream_writer&& other)
+ : output_(std::move(other.output_)),
+ comment_prefix_(std::move(other.comment_prefix_)) {}
+ /**
+ * Virtual destructor
+ */
+ virtual ~unique_stream_writer() {}
+
+ /**
+ * Writes a set of names on a single line in csv format followed
+ * by a newline.
+ *
+ * Note: the names are not escaped.
+ *
+ * @param[in] names Names in a std::vector
+ */
+ void operator()(const std::vector& names) {
+ write_vector(names);
+ }
+ /**
+ * Get the underlying stream
+ */
+ auto& get_stream() { return *output_; }
+
+ /**
+ * Writes a set of values in csv format followed by a newline.
+ *
+ * Note: the precision of the output is determined by the settings
+ * of the stream on construction.
+ *
+ * @param[in] state Values in a std::vector
+ */
+ void operator()(const std::vector& state) { write_vector(state); }
+
+ /**
+ * Writes the comment_prefix to the stream followed by a newline.
+ */
+ void operator()() {
+ std::stringstream streamer;
+ streamer << comment_prefix_ << std::endl;
+ *output_ << streamer.str();
+ }
+
+ /**
+ * Writes the comment_prefix then the message followed by a newline.
+ *
+ * @param[in] message A string
+ */
+ void operator()(const std::string& message) {
+ std::stringstream streamer;
+ streamer << comment_prefix_ << message << std::endl;
+ *output_ << streamer.str();
+ }
+
+ private:
+ /**
+ * Output stream
+ */
+ std::unique_ptr output_;
+
+ /**
+ * Comment prefix to use when printing comments: strings and blank lines
+ */
+ std::string comment_prefix_;
+
+ /**
+ * Writes a set of values in csv format followed by a newline.
+ *
+ * Note: the precision of the output is determined by the settings
+ * of the stream on construction.
+ *
+ * @param[in] v Values in a std::vector
+ */
+ template
+ void write_vector(const std::vector& v) {
+ if (v.empty())
+ return;
+ using const_iter = typename std::vector::const_iterator;
+ const_iter last = v.end();
+ --last;
+ std::stringstream streamer;
+ for (const_iter it = v.begin(); it != last; ++it) {
+ streamer << *it << ",";
+ }
+ streamer << v.back() << std::endl;
+ *output_ << streamer.str();
+ }
+};
+
+} // namespace callbacks
+} // namespace stan
+
+#endif
diff --git a/src/stan/mcmc/hmc/hamiltonians/softabs_metric.hpp b/src/stan/mcmc/hmc/hamiltonians/softabs_metric.hpp
index 10f24dffe48..da2e392c90b 100644
--- a/src/stan/mcmc/hmc/hamiltonians/softabs_metric.hpp
+++ b/src/stan/mcmc/hmc/hamiltonians/softabs_metric.hpp
@@ -174,26 +174,26 @@ class softabs_metric : public base_hamiltonian {
// Threshold below which a power series
// approximation of the softabs function is used
- static double lower_softabs_thresh;
+ static constexpr double lower_softabs_thresh = 1e-4;
// Threshold above which an asymptotic
// approximation of the softabs function is used
- static double upper_softabs_thresh;
+ static constexpr double upper_softabs_thresh = 18;
// Threshold below which an exact derivative is
// used in the Jacobian calculation instead of
// finite differencing
- static double jacobian_thresh;
+ static constexpr double jacobian_thresh = 1e-10;
};
template
-double softabs_metric::lower_softabs_thresh = 1e-4;
+constexpr double softabs_metric::lower_softabs_thresh;
template
-double softabs_metric::upper_softabs_thresh = 18;
+constexpr double softabs_metric::upper_softabs_thresh;
template
-double softabs_metric::jacobian_thresh = 1e-10;
+constexpr double softabs_metric::jacobian_thresh;
} // namespace mcmc
} // namespace stan
#endif
diff --git a/src/stan/services/sample/hmc_nuts_dense_e_adapt.hpp b/src/stan/services/sample/hmc_nuts_dense_e_adapt.hpp
index cb644a346c6..2733cff1f40 100644
--- a/src/stan/services/sample/hmc_nuts_dense_e_adapt.hpp
+++ b/src/stan/services/sample/hmc_nuts_dense_e_adapt.hpp
@@ -66,7 +66,6 @@ int hmc_nuts_dense_e_adapt(
callbacks::writer& sample_writer, callbacks::writer& diagnostic_writer) {
boost::ecuyer1988 rng = util::create_rng(random_seed, chain);
- std::vector disc_vector;
std::vector cont_vector = util::initialize(
model, init, rng, init_radius, true, logger, init_writer);
@@ -156,6 +155,209 @@ int hmc_nuts_dense_e_adapt(
interrupt, logger, init_writer, sample_writer, diagnostic_writer);
}
+/**
+ * Runs multiple chains of NUTS with adaptation using dense Euclidean metric
+ * with a pre-specified Euclidean metric.
+ *
+ * @tparam Model Model class
+ * @tparam InitContextPtr A pointer with underlying type derived from
+ `stan::io::var_context`
+ * @tparam InitInvContextPtr A pointer with underlying type derived from
+ `stan::io::var_context`
+ * @tparam SamplerWriter A type derived from `stan::callbacks::writer`
+ * @tparam DiagnosticWriter A type derived from `stan::callbacks::writer`
+ * @tparam InitWriter A type derived from `stan::callbacks::writer`
+ * @param[in] model Input model to test (with data already instantiated)
+ * @param[in] num_chains The number of chains to run in parallel. `init`,
+ * `init_inv_metric`, `init_writer`, `sample_writer`, and `diagnostic_writer`
+ must
+ * be the same length as this value.
+ * @param[in] init An std vector of init var contexts for initialization of each
+ * chain.
+ * @param[in] init_inv_metric An std vector of var contexts exposing an initial
+ * diagonal inverse Euclidean metric for each chain (must be positive definite)
+ * @param[in] random_seed random seed for the random number generator
+ * @param[in] init_chain_id first chain id. The pseudo random number generator
+ * will advance by for each chain by an integer sequence from `init_chain_id` to
+ * `init_chain_id+num_chains-1`
+ * @param[in] init_radius radius to initialize
+ * @param[in] num_warmup Number of warmup samples
+ * @param[in] num_samples Number of samples
+ * @param[in] num_thin Number to thin the samples
+ * @param[in] save_warmup Indicates whether to save the warmup iterations
+ * @param[in] refresh Controls the output
+ * @param[in] stepsize initial stepsize for discrete evolution
+ * @param[in] stepsize_jitter uniform random jitter of stepsize
+ * @param[in] max_depth Maximum tree depth
+ * @param[in] delta adaptation target acceptance statistic
+ * @param[in] gamma adaptation regularization scale
+ * @param[in] kappa adaptation relaxation exponent
+ * @param[in] t0 adaptation iteration offset
+ * @param[in] init_buffer width of initial fast adaptation interval
+ * @param[in] term_buffer width of final fast adaptation interval
+ * @param[in] window initial width of slow adaptation interval
+ * @param[in,out] interrupt Callback for interrupts
+ * @param[in,out] logger Logger for messages
+ * @param[in,out] init_writer std vector of Writer callbacks for unconstrained
+ inits of each chain.
+ * @param[in,out] sample_writer std vector of Writers for draws of each chain.
+ * @param[in,out] diagnostic_writer std vector of Writers for diagnostic
+ * information of each chain.
+ * @return error_codes::OK if successful
+ */
+template
+int hmc_nuts_dense_e_adapt(
+ Model& model, size_t num_chains, const std::vector& init,
+ const std::vector& init_inv_metric,
+ unsigned int random_seed, unsigned int init_chain_id, double init_radius,
+ int num_warmup, int num_samples, int num_thin, bool save_warmup,
+ int refresh, double stepsize, double stepsize_jitter, int max_depth,
+ double delta, double gamma, double kappa, double t0,
+ unsigned int init_buffer, unsigned int term_buffer, unsigned int window,
+ callbacks::interrupt& interrupt, callbacks::logger& logger,
+ std::vector& init_writer,
+ std::vector& sample_writer,
+ std::vector& diagnostic_writer) {
+ if (num_chains == 1) {
+ return hmc_nuts_dense_e_adapt(
+ model, *init[0], *init_inv_metric[0], random_seed, init_chain_id,
+ init_radius, num_warmup, num_samples, num_thin, save_warmup, refresh,
+ stepsize, stepsize_jitter, max_depth, delta, gamma, kappa, t0,
+ init_buffer, term_buffer, window, interrupt, logger, init_writer[0],
+ sample_writer[0], diagnostic_writer[0]);
+ }
+ using sample_t = stan::mcmc::adapt_dense_e_nuts;
+ std::vector rngs;
+ rngs.reserve(num_chains);
+ std::vector> cont_vectors;
+ cont_vectors.reserve(num_chains);
+ std::vector samplers;
+ samplers.reserve(num_chains);
+ try {
+ for (int i = 0; i < num_chains; ++i) {
+ rngs.emplace_back(util::create_rng(random_seed, init_chain_id + i));
+ cont_vectors.emplace_back(util::initialize(
+ model, *init[i], rngs[i], init_radius, true, logger, init_writer[i]));
+ Eigen::MatrixXd inv_metric = util::read_dense_inv_metric(
+ *init_inv_metric[i], model.num_params_r(), logger);
+ util::validate_dense_inv_metric(inv_metric, logger);
+
+ samplers.emplace_back(model, rngs[i]);
+ samplers[i].set_metric(inv_metric);
+ samplers[i].set_nominal_stepsize(stepsize);
+ samplers[i].set_stepsize_jitter(stepsize_jitter);
+ samplers[i].set_max_depth(max_depth);
+
+ samplers[i].get_stepsize_adaptation().set_mu(log(10 * stepsize));
+ samplers[i].get_stepsize_adaptation().set_delta(delta);
+ samplers[i].get_stepsize_adaptation().set_gamma(gamma);
+ samplers[i].get_stepsize_adaptation().set_kappa(kappa);
+ samplers[i].get_stepsize_adaptation().set_t0(t0);
+ samplers[i].set_window_params(num_warmup, init_buffer, term_buffer,
+ window, logger);
+ }
+ } catch (const std::domain_error& e) {
+ return error_codes::CONFIG;
+ }
+ tbb::parallel_for(tbb::blocked_range(0, num_chains, 1),
+ [num_warmup, num_samples, num_thin, refresh, save_warmup,
+ num_chains, init_chain_id, &samplers, &model, &rngs,
+ &interrupt, &logger, &sample_writer, &cont_vectors,
+ &diagnostic_writer](const tbb::blocked_range& r) {
+ for (size_t i = r.begin(); i != r.end(); ++i) {
+ util::run_adaptive_sampler(
+ samplers[i], model, cont_vectors[i], num_warmup,
+ num_samples, num_thin, refresh, save_warmup,
+ rngs[i], interrupt, logger, sample_writer[i],
+ diagnostic_writer[i], init_chain_id + i,
+ num_chains);
+ }
+ },
+ tbb::simple_partitioner());
+ return error_codes::OK;
+}
+
+/**
+ * Runs multiple chains of NUTS with adaptation using dense Euclidean metric,
+ * with identity matrix as initial inv_metric.
+ *
+ * @tparam Model Model class
+ * @tparam InitContextPtr A pointer with underlying type derived from
+ * `stan::io::var_context`
+ * @tparam InitWriter A type derived from `stan::callbacks::writer`
+ * @tparam SamplerWriter A type derived from `stan::callbacks::writer`
+ * @tparam DiagnosticWriter A type derived from `stan::callbacks::writer`
+ * @param[in] model Input model to test (with data already instantiated)
+ * @param[in] num_chains The number of chains to run in parallel. `init`,
+ * `init_writer`, `sample_writer`, and `diagnostic_writer` must be the same
+ * length as this value.
+ * @param[in] init An std vector of init var contexts for initialization of each
+ * chain.
+ * @param[in] random_seed random seed for the random number generator
+ * @param[in] init_chain_id first chain id. The pseudo random number generator
+ * will advance by for each chain by an integer sequence from `init_chain_id` to
+ * `init_chain_id+num_chains-1`
+ * @param[in] init_radius radius to initialize
+ * @param[in] num_warmup Number of warmup samples
+ * @param[in] num_samples Number of samples
+ * @param[in] num_thin Number to thin the samples
+ * @param[in] save_warmup Indicates whether to save the warmup iterations
+ * @param[in] refresh Controls the output
+ * @param[in] stepsize initial stepsize for discrete evolution
+ * @param[in] stepsize_jitter uniform random jitter of stepsize
+ * @param[in] max_depth Maximum tree depth
+ * @param[in] delta adaptation target acceptance statistic
+ * @param[in] gamma adaptation regularization scale
+ * @param[in] kappa adaptation relaxation exponent
+ * @param[in] t0 adaptation iteration offset
+ * @param[in] init_buffer width of initial fast adaptation interval
+ * @param[in] term_buffer width of final fast adaptation interval
+ * @param[in] window initial width of slow adaptation interval
+ * @param[in,out] interrupt Callback for interrupts
+ * @param[in,out] logger Logger for messages
+ * @param[in,out] init_writer std vector of Writer callbacks for unconstrained
+ * inits of each chain.
+ * @param[in,out] sample_writer std vector of Writers for draws of each chain.
+ * @param[in,out] diagnostic_writer std vector of Writers for diagnostic
+ * information of each chain.
+ * @return error_codes::OK if successful
+ */
+template
+int hmc_nuts_dense_e_adapt(
+ Model& model, size_t num_chains, const std::vector& init,
+ unsigned int random_seed, unsigned int init_chain_id, double init_radius,
+ int num_warmup, int num_samples, int num_thin, bool save_warmup,
+ int refresh, double stepsize, double stepsize_jitter, int max_depth,
+ double delta, double gamma, double kappa, double t0,
+ unsigned int init_buffer, unsigned int term_buffer, unsigned int window,
+ callbacks::interrupt& interrupt, callbacks::logger& logger,
+ std::vector& init_writer,
+ std::vector& sample_writer,
+ std::vector& diagnostic_writer) {
+ if (num_chains == 1) {
+ return hmc_nuts_dense_e_adapt(
+ model, *init[0], random_seed, init_chain_id, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init_writer[0], sample_writer[0],
+ diagnostic_writer[0]);
+ }
+ std::vector> unit_e_metrics;
+ unit_e_metrics.reserve(num_chains);
+ for (size_t i = 0; i < num_chains; ++i) {
+ unit_e_metrics.emplace_back(std::make_unique(
+ util::create_unit_e_dense_inv_metric(model.num_params_r())));
+ }
+ return hmc_nuts_dense_e_adapt(
+ model, num_chains, init, unit_e_metrics, random_seed, init_chain_id,
+ init_radius, num_warmup, num_samples, num_thin, save_warmup, refresh,
+ stepsize, stepsize_jitter, max_depth, delta, gamma, kappa, t0,
+ init_buffer, term_buffer, window, interrupt, logger, init_writer,
+ sample_writer, diagnostic_writer);
+}
+
} // namespace sample
} // namespace services
} // namespace stan
diff --git a/src/stan/services/sample/hmc_nuts_diag_e_adapt.hpp b/src/stan/services/sample/hmc_nuts_diag_e_adapt.hpp
index 8d03373e1a5..9689fa4a9a1 100644
--- a/src/stan/services/sample/hmc_nuts_diag_e_adapt.hpp
+++ b/src/stan/services/sample/hmc_nuts_diag_e_adapt.hpp
@@ -24,6 +24,11 @@ namespace sample {
* with a pre-specified Euclidean metric.
*
* @tparam Model Model class
+ * @tparam InitContextPtr A type derived from `stan::io::var_context`
+ * @tparam InitMetricContext A type derived from `stan::io::var_context`
+ * @tparam SamplerWriter A type derived from `stan::callbacks::writer`
+ * @tparam DiagnosticWriter A type derived from `stan::callbacks::writer`
+ * @tparam InitWriter A type derived from `stan::callbacks::writer`
* @param[in] model Input model to test (with data already instantiated)
* @param[in] init var context for initialization
* @param[in] init_inv_metric var context exposing an initial diagonal
@@ -53,7 +58,7 @@ namespace sample {
* @param[in,out] diagnostic_writer Writer for diagnostic information
* @return error_codes::OK if successful
*/
-template
+template
int hmc_nuts_diag_e_adapt(
Model& model, const stan::io::var_context& init,
const stan::io::var_context& init_inv_metric, unsigned int random_seed,
@@ -66,7 +71,6 @@ int hmc_nuts_diag_e_adapt(
callbacks::writer& sample_writer, callbacks::writer& diagnostic_writer) {
boost::ecuyer1988 rng = util::create_rng(random_seed, chain);
- std::vector disc_vector;
std::vector cont_vector = util::initialize(
model, init, rng, init_radius, true, logger, init_writer);
@@ -133,7 +137,7 @@ int hmc_nuts_diag_e_adapt(
* @param[in,out] diagnostic_writer Writer for diagnostic information
* @return error_codes::OK if successful
*/
-template
+template
int hmc_nuts_diag_e_adapt(
Model& model, const stan::io::var_context& init, unsigned int random_seed,
unsigned int chain, double init_radius, int num_warmup, int num_samples,
@@ -143,10 +147,8 @@ int hmc_nuts_diag_e_adapt(
unsigned int window, callbacks::interrupt& interrupt,
callbacks::logger& logger, callbacks::writer& init_writer,
callbacks::writer& sample_writer, callbacks::writer& diagnostic_writer) {
- stan::io::dump dmp
+ stan::io::dump unit_e_metric
= util::create_unit_e_diag_inv_metric(model.num_params_r());
- stan::io::var_context& unit_e_metric = dmp;
-
return hmc_nuts_diag_e_adapt(
model, init, unit_e_metric, random_seed, chain, init_radius, num_warmup,
num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
@@ -154,6 +156,209 @@ int hmc_nuts_diag_e_adapt(
interrupt, logger, init_writer, sample_writer, diagnostic_writer);
}
+/**
+ * Runs multiple chains of HMC with NUTS with adaptation using diagonal
+ * Euclidean metric with a pre-specified Euclidean metric.
+ *
+ * @tparam Model Model class
+ * @tparam InitContextPtr A pointer with underlying type derived from
+ `stan::io::var_context`
+ * @tparam InitInvContextPtr A pointer with underlying type derived from
+ `stan::io::var_context`
+ * @tparam SamplerWriter A type derived from `stan::callbacks::writer`
+ * @tparam DiagnosticWriter A type derived from `stan::callbacks::writer`
+ * @tparam InitWriter A type derived from `stan::callbacks::writer`
+ * @param[in] model Input model to test (with data already instantiated)
+ * @param[in] num_chains The number of chains to run in parallel. `init`,
+ * `init_inv_metric`, `init_writer`, `sample_writer`, and `diagnostic_writer`
+ must
+ * be the same length as this value.
+ * @param[in] init An std vector of init var contexts for initialization of each
+ * chain.
+ * @param[in] init_inv_metric An std vector of var contexts exposing an initial
+ diagonal inverse Euclidean metric for each chain (must be positive definite)
+ * @param[in] random_seed random seed for the random number generator
+ * @param[in] init_chain_id first chain id. The pseudo random number generator
+ * will advance for each chain by an integer sequence from `init_chain_id` to
+ * `init_chain_id + num_chains - 1`
+ * @param[in] init_radius radius to initialize
+ * @param[in] num_warmup Number of warmup samples
+ * @param[in] num_samples Number of samples
+ * @param[in] num_thin Number to thin the samples
+ * @param[in] save_warmup Indicates whether to save the warmup iterations
+ * @param[in] refresh Controls the output
+ * @param[in] stepsize initial stepsize for discrete evolution
+ * @param[in] stepsize_jitter uniform random jitter of stepsize
+ * @param[in] max_depth Maximum tree depth
+ * @param[in] delta adaptation target acceptance statistic
+ * @param[in] gamma adaptation regularization scale
+ * @param[in] kappa adaptation relaxation exponent
+ * @param[in] t0 adaptation iteration offset
+ * @param[in] init_buffer width of initial fast adaptation interval
+ * @param[in] term_buffer width of final fast adaptation interval
+ * @param[in] window initial width of slow adaptation interval
+ * @param[in,out] interrupt Callback for interrupts
+ * @param[in,out] logger Logger for messages
+ * @param[in,out] init_writer std vector of Writer callbacks for unconstrained
+ * inits of each chain.
+ * @param[in,out] sample_writer std vector of Writers for draws of each chain.
+ * @param[in,out] diagnostic_writer std vector of Writers for diagnostic
+ * information of each chain.
+ * @return error_codes::OK if successful
+ */
+template
+int hmc_nuts_diag_e_adapt(
+ Model& model, size_t num_chains, const std::vector& init,
+ const std::vector& init_inv_metric,
+ unsigned int random_seed, unsigned int init_chain_id, double init_radius,
+ int num_warmup, int num_samples, int num_thin, bool save_warmup,
+ int refresh, double stepsize, double stepsize_jitter, int max_depth,
+ double delta, double gamma, double kappa, double t0,
+ unsigned int init_buffer, unsigned int term_buffer, unsigned int window,
+ callbacks::interrupt& interrupt, callbacks::logger& logger,
+ std::vector& init_writer,
+ std::vector& sample_writer,
+ std::vector& diagnostic_writer) {
+ if (num_chains == 1) {
+ return hmc_nuts_diag_e_adapt(
+ model, *init[0], *init_inv_metric[0], random_seed, init_chain_id,
+ init_radius, num_warmup, num_samples, num_thin, save_warmup, refresh,
+ stepsize, stepsize_jitter, max_depth, delta, gamma, kappa, t0,
+ init_buffer, term_buffer, window, interrupt, logger, init_writer[0],
+ sample_writer[0], diagnostic_writer[0]);
+ }
+ using sample_t = stan::mcmc::adapt_diag_e_nuts;
+ std::vector rngs;
+ rngs.reserve(num_chains);
+ std::vector> cont_vectors;
+ cont_vectors.reserve(num_chains);
+ std::vector samplers;
+ samplers.reserve(num_chains);
+ try {
+ for (int i = 0; i < num_chains; ++i) {
+ rngs.emplace_back(util::create_rng(random_seed, init_chain_id + i));
+ cont_vectors.emplace_back(util::initialize(
+ model, *init[i], rngs[i], init_radius, true, logger, init_writer[i]));
+ samplers.emplace_back(model, rngs[i]);
+ Eigen::VectorXd inv_metric = util::read_diag_inv_metric(
+ *init_inv_metric[i], model.num_params_r(), logger);
+ util::validate_diag_inv_metric(inv_metric, logger);
+
+ samplers[i].set_metric(inv_metric);
+ samplers[i].set_nominal_stepsize(stepsize);
+ samplers[i].set_stepsize_jitter(stepsize_jitter);
+ samplers[i].set_max_depth(max_depth);
+
+ samplers[i].get_stepsize_adaptation().set_mu(log(10 * stepsize));
+ samplers[i].get_stepsize_adaptation().set_delta(delta);
+ samplers[i].get_stepsize_adaptation().set_gamma(gamma);
+ samplers[i].get_stepsize_adaptation().set_kappa(kappa);
+ samplers[i].get_stepsize_adaptation().set_t0(t0);
+ samplers[i].set_window_params(num_warmup, init_buffer, term_buffer,
+ window, logger);
+ }
+ } catch (const std::domain_error& e) {
+ return error_codes::CONFIG;
+ }
+ tbb::parallel_for(tbb::blocked_range(0, num_chains, 1),
+ [num_warmup, num_samples, num_thin, refresh, save_warmup,
+ num_chains, init_chain_id, &samplers, &model, &rngs,
+ &interrupt, &logger, &sample_writer, &cont_vectors,
+ &diagnostic_writer](const tbb::blocked_range& r) {
+ for (size_t i = r.begin(); i != r.end(); ++i) {
+ util::run_adaptive_sampler(
+ samplers[i], model, cont_vectors[i], num_warmup,
+ num_samples, num_thin, refresh, save_warmup,
+ rngs[i], interrupt, logger, sample_writer[i],
+ diagnostic_writer[i], init_chain_id + i,
+ num_chains);
+ }
+ },
+ tbb::simple_partitioner());
+ return error_codes::OK;
+}
+
+/**
+ * Runs multiple chains of HMC with NUTS with adaptation using diagonal
+ * Euclidean metric.
+ *
+ * @tparam Model Model class
+ * @tparam InitContextPtr A pointer with underlying type derived from
+ * `stan::io::var_context`
+ * @tparam SamplerWriter A type derived from `stan::callbacks::writer`
+ * @tparam DiagnosticWriter A type derived from `stan::callbacks::writer`
+ * @tparam InitWriter A type derived from `stan::callbacks::writer`
+ * @param[in] model Input model to test (with data already instantiated)
+ * @param[in] num_chains The number of chains to run in parallel. `init`,
+ * `init_writer`, `sample_writer`, and `diagnostic_writer` must be the same
+ * length as this value.
+ * @param[in] init An std vector of init var contexts for initialization of each
+ * chain.
+ * @param[in] random_seed random seed for the random number generator
+ * @param[in] init_chain_id first chain id. The pseudo random number generator
+ * will advance by for each chain by an integer sequence from `init_chain_id` to
+ * `init_chain_id+num_chains-1`
+ * @param[in] init_radius radius to initialize
+ * @param[in] num_warmup Number of warmup samples
+ * @param[in] num_samples Number of samples
+ * @param[in] num_thin Number to thin the samples
+ * @param[in] save_warmup Indicates whether to save the warmup iterations
+ * @param[in] refresh Controls the output
+ * @param[in] stepsize initial stepsize for discrete evolution
+ * @param[in] stepsize_jitter uniform random jitter of stepsize
+ * @param[in] max_depth Maximum tree depth
+ * @param[in] delta adaptation target acceptance statistic
+ * @param[in] gamma adaptation regularization scale
+ * @param[in] kappa adaptation relaxation exponent
+ * @param[in] t0 adaptation iteration offset
+ * @param[in] init_buffer width of initial fast adaptation interval
+ * @param[in] term_buffer width of final fast adaptation interval
+ * @param[in] window initial width of slow adaptation interval
+ * @param[in,out] interrupt Callback for interrupts
+ * @param[in,out] logger Logger for messages
+ * @param[in,out] init_writer std vector of Writer callbacks for unconstrained
+ * inits of each chain.
+ * @param[in,out] sample_writer std vector of Writers for draws of each chain.
+ * @param[in,out] diagnostic_writer std vector of Writers for diagnostic
+ * information of each chain.
+ * @return error_codes::OK if successful
+ */
+template
+int hmc_nuts_diag_e_adapt(
+ Model& model, size_t num_chains, const std::vector& init,
+ unsigned int random_seed, unsigned int init_chain_id, double init_radius,
+ int num_warmup, int num_samples, int num_thin, bool save_warmup,
+ int refresh, double stepsize, double stepsize_jitter, int max_depth,
+ double delta, double gamma, double kappa, double t0,
+ unsigned int init_buffer, unsigned int term_buffer, unsigned int window,
+ callbacks::interrupt& interrupt, callbacks::logger& logger,
+ std::vector& init_writer,
+ std::vector& sample_writer,
+ std::vector& diagnostic_writer) {
+ if (num_chains == 1) {
+ return hmc_nuts_diag_e_adapt(
+ model, *init[0], random_seed, init_chain_id, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init_writer[0], sample_writer[0],
+ diagnostic_writer[0]);
+ }
+ std::vector> unit_e_metrics;
+ unit_e_metrics.reserve(num_chains);
+ for (size_t i = 0; i < num_chains; ++i) {
+ unit_e_metrics.emplace_back(std::make_unique(
+ util::create_unit_e_diag_inv_metric(model.num_params_r())));
+ }
+ return hmc_nuts_diag_e_adapt(
+ model, num_chains, init, unit_e_metrics, random_seed, init_chain_id,
+ init_radius, num_warmup, num_samples, num_thin, save_warmup, refresh,
+ stepsize, stepsize_jitter, max_depth, delta, gamma, kappa, t0,
+ init_buffer, term_buffer, window, interrupt, logger, init_writer,
+ sample_writer, diagnostic_writer);
+}
+
} // namespace sample
} // namespace services
} // namespace stan
diff --git a/src/stan/services/util/create_rng.hpp b/src/stan/services/util/create_rng.hpp
index 9b32d4509c0..d63d6c79f1b 100644
--- a/src/stan/services/util/create_rng.hpp
+++ b/src/stan/services/util/create_rng.hpp
@@ -24,7 +24,7 @@ namespace util {
*/
inline boost::ecuyer1988 create_rng(unsigned int seed, unsigned int chain) {
using boost::uintmax_t;
- static uintmax_t DISCARD_STRIDE = static_cast(1) << 50;
+ static constexpr uintmax_t DISCARD_STRIDE = static_cast(1) << 50;
boost::ecuyer1988 rng(seed);
rng.discard(DISCARD_STRIDE * chain);
return rng;
diff --git a/src/stan/services/util/create_unit_e_dense_inv_metric.hpp b/src/stan/services/util/create_unit_e_dense_inv_metric.hpp
index 60de41e0875..ee9afc670a6 100644
--- a/src/stan/services/util/create_unit_e_dense_inv_metric.hpp
+++ b/src/stan/services/util/create_unit_e_dense_inv_metric.hpp
@@ -17,17 +17,12 @@ namespace util {
* @return var_context
*/
inline stan::io::dump create_unit_e_dense_inv_metric(size_t num_params) {
- Eigen::MatrixXd inv_metric(num_params, num_params);
- inv_metric.setIdentity();
- size_t num_elements = num_params * num_params;
+ auto num_params_str = std::to_string(num_params);
+ std::string dims("),.Dim=c(" + num_params_str + ", " + num_params_str + "))");
+ Eigen::IOFormat RFmt(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", ",",
+ "", "", "inv_metric <- structure(c(", dims);
std::stringstream txt;
- txt << "inv_metric <- structure(c(";
- for (size_t i = 0; i < num_elements; i++) {
- txt << inv_metric(i);
- if (i < num_elements - 1)
- txt << ", ";
- }
- txt << "),.Dim=c(" << num_params << ", " << num_params << "))";
+ txt << Eigen::MatrixXd::Identity(num_params, num_params).format(RFmt);
return stan::io::dump(txt);
}
} // namespace util
diff --git a/src/stan/services/util/create_unit_e_diag_inv_metric.hpp b/src/stan/services/util/create_unit_e_diag_inv_metric.hpp
index 18375e68f94..e14a80e3283 100644
--- a/src/stan/services/util/create_unit_e_diag_inv_metric.hpp
+++ b/src/stan/services/util/create_unit_e_diag_inv_metric.hpp
@@ -16,14 +16,11 @@ namespace util {
* @return var_context
*/
inline stan::io::dump create_unit_e_diag_inv_metric(size_t num_params) {
+ std::string dims("),.Dim=c(" + std::to_string(num_params) + "))");
+ Eigen::IOFormat RFmt(Eigen::StreamPrecision, Eigen::DontAlignCols, ", ", ",",
+ "", "", "inv_metric <- structure(c(", dims);
std::stringstream txt;
- txt << "inv_metric <- structure(c(";
- for (size_t i = 0; i < num_params; ++i) {
- txt << "1.0";
- if (i < num_params - 1)
- txt << ", ";
- }
- txt << "),.Dim=c(" << num_params << "))";
+ txt << Eigen::VectorXd::Ones(num_params).format(RFmt);
return stan::io::dump(txt);
}
} // namespace util
diff --git a/src/stan/services/util/generate_transitions.hpp b/src/stan/services/util/generate_transitions.hpp
index 1516c6f0f99..5b1aa1a27dc 100644
--- a/src/stan/services/util/generate_transitions.hpp
+++ b/src/stan/services/util/generate_transitions.hpp
@@ -36,6 +36,8 @@ namespace util {
* @param[in,out] base_rng random number generator
* @param[in,out] callback interrupt callback called once an iteration
* @param[in,out] logger logger for messages
+ * @param[in] num_chains The number of chains used in the program. This
+ * is used in generate transitions to print out the chain number.
*/
template
void generate_transitions(stan::mcmc::base_mcmc& sampler, int num_iterations,
@@ -44,7 +46,8 @@ void generate_transitions(stan::mcmc::base_mcmc& sampler, int num_iterations,
util::mcmc_writer& mcmc_writer,
stan::mcmc::sample& init_s, Model& model,
RNG& base_rng, callbacks::interrupt& callback,
- callbacks::logger& logger) {
+ callbacks::logger& logger, size_t chain_id = 1,
+ size_t num_chains = 1) {
for (int m = 0; m < num_iterations; ++m) {
callback();
@@ -52,6 +55,9 @@ void generate_transitions(stan::mcmc::base_mcmc& sampler, int num_iterations,
&& (start + m + 1 == finish || m == 0 || (m + 1) % refresh == 0)) {
int it_print_width = std::ceil(std::log10(static_cast(finish)));
std::stringstream message;
+ if (num_chains != 1) {
+ message << "Chain [" << chain_id << "] ";
+ }
message << "Iteration: ";
message << std::setw(it_print_width) << m + 1 + start << " / " << finish;
message << " [" << std::setw(3)
diff --git a/src/stan/services/util/initialize.hpp b/src/stan/services/util/initialize.hpp
index fd180819b99..696013531c9 100644
--- a/src/stan/services/util/initialize.hpp
+++ b/src/stan/services/util/initialize.hpp
@@ -65,9 +65,10 @@ namespace util {
* std::domain_error)
* @return valid unconstrained parameters for the model
*/
-template
-std::vector initialize(Model& model, const stan::io::var_context& init,
- RNG& rng, double init_radius, bool print_timing,
+template
+std::vector initialize(Model& model, const InitContext& init, RNG& rng,
+ double init_radius, bool print_timing,
stan::callbacks::logger& logger,
stan::callbacks::writer& init_writer) {
std::vector unconstrained;
diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp
index ba0135158dd..7115b138fc3 100644
--- a/src/stan/services/util/run_adaptive_sampler.hpp
+++ b/src/stan/services/util/run_adaptive_sampler.hpp
@@ -5,6 +5,7 @@
#include
#include
#include
+#include
#include
#include
@@ -33,8 +34,11 @@ namespace util {
* @param[in,out] logger logger for messages
* @param[in,out] sample_writer writer for draws
* @param[in,out] diagnostic_writer writer for diagnostic information
+ * @param[in] chain_id The id for a given chain.
+ * @param[in] num_chains The number of chains used in the program. This
+ * is used in generate transitions to print out the chain number.
*/
-template
+template
void run_adaptive_sampler(Sampler& sampler, Model& model,
std::vector& cont_vector, int num_warmup,
int num_samples, int num_thin, int refresh,
@@ -42,7 +46,8 @@ void run_adaptive_sampler(Sampler& sampler, Model& model,
callbacks::interrupt& interrupt,
callbacks::logger& logger,
callbacks::writer& sample_writer,
- callbacks::writer& diagnostic_writer) {
+ callbacks::writer& diagnostic_writer,
+ size_t chain_id = 1, size_t num_chains = 1) {
Eigen::Map cont_params(cont_vector.data(),
cont_vector.size());
@@ -66,7 +71,8 @@ void run_adaptive_sampler(Sampler& sampler, Model& model,
auto start_warm = std::chrono::steady_clock::now();
util::generate_transitions(sampler, num_warmup, 0, num_warmup + num_samples,
num_thin, refresh, save_warmup, true, writer, s,
- model, rng, interrupt, logger);
+ model, rng, interrupt, logger, chain_id,
+ num_chains);
auto end_warm = std::chrono::steady_clock::now();
double warm_delta_t = std::chrono::duration_cast(
end_warm - start_warm)
@@ -79,7 +85,8 @@ void run_adaptive_sampler(Sampler& sampler, Model& model,
auto start_sample = std::chrono::steady_clock::now();
util::generate_transitions(sampler, num_samples, num_warmup,
num_warmup + num_samples, num_thin, refresh, true,
- false, writer, s, model, rng, interrupt, logger);
+ false, writer, s, model, rng, interrupt, logger,
+ chain_id, num_chains);
auto end_sample = std::chrono::steady_clock::now();
double sample_delta_t = std::chrono::duration_cast(
end_sample - start_sample)
@@ -87,6 +94,7 @@ void run_adaptive_sampler(Sampler& sampler, Model& model,
/ 1000.0;
writer.write_timing(warm_delta_t, sample_delta_t);
}
+
} // namespace util
} // namespace services
} // namespace stan
diff --git a/src/test/test-models/good/optimization/rosenbrock.stan b/src/test/test-models/good/optimization/rosenbrock.stan
index db310524279..6cb7b8f8dec 100644
--- a/src/test/test-models/good/optimization/rosenbrock.stan
+++ b/src/test/test-models/good/optimization/rosenbrock.stan
@@ -5,4 +5,3 @@ parameters {
model {
target += -(pow(1 - x, 2) + 100 * pow(y - pow(x, 2), 2));
}
-
diff --git a/src/test/unit/callbacks/unique_stream_writer_test.cpp b/src/test/unit/callbacks/unique_stream_writer_test.cpp
new file mode 100644
index 00000000000..6fca0f1808f
--- /dev/null
+++ b/src/test/unit/callbacks/unique_stream_writer_test.cpp
@@ -0,0 +1,50 @@
+#include
+#include
+#include
+
+class StanInterfaceCallbacksStreamWriter : public ::testing::Test {
+ public:
+ StanInterfaceCallbacksStreamWriter()
+ : writer(std::make_unique(std::stringstream{})) {}
+
+ void SetUp() {
+ static_cast(writer.get_stream()).str(std::string());
+ static_cast(writer.get_stream()).clear();
+ }
+ void TearDown() {}
+
+ stan::callbacks::unique_stream_writer writer;
+};
+
+TEST_F(StanInterfaceCallbacksStreamWriter, double_vector) {
+ const int N = 5;
+ std::vector x;
+ for (int n = 0; n < N; ++n)
+ x.push_back(n);
+
+ EXPECT_NO_THROW(writer(x));
+ EXPECT_EQ("0,1,2,3,4\n",
+ static_cast(writer.get_stream()).str());
+}
+
+TEST_F(StanInterfaceCallbacksStreamWriter, string_vector) {
+ const int N = 5;
+ std::vector x;
+ for (int n = 0; n < N; ++n)
+ x.push_back(boost::lexical_cast(n));
+
+ EXPECT_NO_THROW(writer(x));
+ EXPECT_EQ("0,1,2,3,4\n",
+ static_cast(writer.get_stream()).str());
+}
+
+TEST_F(StanInterfaceCallbacksStreamWriter, null) {
+ EXPECT_NO_THROW(writer());
+ EXPECT_EQ("\n", static_cast(writer.get_stream()).str());
+}
+
+TEST_F(StanInterfaceCallbacksStreamWriter, string) {
+ EXPECT_NO_THROW(writer("message"));
+ EXPECT_EQ("message\n",
+ static_cast(writer.get_stream()).str());
+}
diff --git a/src/test/unit/services/check_adaptation.hpp b/src/test/unit/services/check_adaptation.hpp
index e5d3f52dd4c..af863ea8139 100644
--- a/src/test/unit/services/check_adaptation.hpp
+++ b/src/test/unit/services/check_adaptation.hpp
@@ -13,11 +13,7 @@ namespace stan {
namespace test {
namespace unit {
-double stod(const std::string& val) {
- char tmp[val.length()];
- strcpy(tmp, val.c_str());
- return atof(tmp);
-}
+double stod(const std::string& val) { return atof(val.c_str()); }
void check_adaptation(const size_t& num_params,
const std::vector& param_vals,
diff --git a/src/test/unit/services/instrumented_callbacks.hpp b/src/test/unit/services/instrumented_callbacks.hpp
index 5df61c17f74..797959ef0ee 100644
--- a/src/test/unit/services/instrumented_callbacks.hpp
+++ b/src/test/unit/services/instrumented_callbacks.hpp
@@ -9,7 +9,8 @@
#include
#include
#include
-
+#include
+#include
namespace stan {
namespace test {
namespace unit {
@@ -27,7 +28,7 @@ class instrumented_interrupt : public stan::callbacks::interrupt {
unsigned int call_count() { return counter_; }
private:
- unsigned int counter_;
+ std::atomic counter_;
};
/**
@@ -96,55 +97,54 @@ class instrumented_writer : public stan::callbacks::writer {
unsigned int call_count() {
unsigned int n = 0;
- for (std::map::iterator it = counter_.begin();
- it != counter_.end(); ++it)
- n += it->second;
+ for (auto& it : counter_)
+ n += it.second;
return n;
}
unsigned int call_count(std::string s) { return counter_[s]; }
- std::vector > string_double_values() {
+ std::vector> string_double_values() {
return string_double;
};
- std::vector > string_int_values() {
+ std::vector> string_int_values() {
return string_int;
};
- std::vector > string_string_values() {
+ std::vector> string_string_values() {
return string_string;
};
- std::vector > >
+ std::vector>>
string_pdouble_int_values() {
return string_pdouble_int;
};
- std::vector >
+ std::vector>
string_pdouble_int_int_values() {
return string_pdouble_int_int;
};
- std::vector > vector_string_values() {
+ std::vector> vector_string_values() {
return vector_string;
};
- std::vector > vector_double_values() {
+ std::vector> vector_double_values() {
return vector_double;
};
std::vector string_values() { return string; };
private:
- std::map counter_;
- std::vector > string_double;
- std::vector > string_int;
- std::vector > string_string;
- std::vector > > string_pdouble_int;
- std::vector > string_pdouble_int_int;
- std::vector > vector_string;
- std::vector > vector_double;
+ std::map> counter_;
+ std::vector> string_double;
+ std::vector> string_int;
+ std::vector> string_string;
+ std::vector>> string_pdouble_int;
+ std::vector> string_pdouble_int_int;
+ std::vector> vector_string;
+ std::vector> vector_double;
std::vector string;
};
@@ -156,35 +156,53 @@ class instrumented_writer : public stan::callbacks::writer {
*/
class instrumented_logger : public stan::callbacks::logger {
public:
+ std::mutex logger_guard;
instrumented_logger() {}
- void debug(const std::string& message) { debug_.push_back(message); }
+ void debug(const std::string& message) {
+ std::lock_guard guard(logger_guard);
+ debug_.push_back(message);
+ }
void debug(const std::stringstream& message) {
+ std::lock_guard guard(logger_guard);
debug_.push_back(message.str());
}
- void info(const std::string& message) { info_.push_back(message); }
+ void info(const std::string& message) {
+ std::lock_guard guard(logger_guard);
+ info_.push_back(message);
+ }
void info(const std::stringstream& message) {
+ std::lock_guard guard(logger_guard);
info_.push_back(message.str());
}
- void warn(const std::string& message) { warn_.push_back(message); }
+ void warn(const std::string& message) {
+ std::lock_guard guard(logger_guard);
+ warn_.push_back(message);
+ }
void warn(const std::stringstream& message) {
+ std::lock_guard guard(logger_guard);
warn_.push_back(message.str());
}
- void error(const std::string& message) { error_.push_back(message); }
+ void error(const std::string& message) {
+ std::lock_guard guard(logger_guard);
+ error_.push_back(message);
+ }
void error(const std::stringstream& message) {
+ std::lock_guard guard(logger_guard);
error_.push_back(message.str());
}
void fatal(const std::string& message) { fatal_.push_back(message); }
void fatal(const std::stringstream& message) {
+ std::lock_guard guard(logger_guard);
fatal_.push_back(message.str());
}
diff --git a/src/test/unit/services/sample/hmc_nuts_dense_e_adapt_parallel_match_test.cpp b/src/test/unit/services/sample/hmc_nuts_dense_e_adapt_parallel_match_test.cpp
new file mode 100644
index 00000000000..901afbc6ab7
--- /dev/null
+++ b/src/test/unit/services/sample/hmc_nuts_dense_e_adapt_parallel_match_test.cpp
@@ -0,0 +1,108 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+auto&& blah = stan::math::init_threadpool_tbb();
+
+static constexpr size_t num_chains = 4;
+class ServicesSampleHmcNutsDenseEAdaptParMatch : public testing::Test {
+ public:
+ ServicesSampleHmcNutsDenseEAdaptParMatch()
+ : model(std::make_unique(
+ data_context, 0, &model_log)) {
+ for (int i = 0; i < num_chains; ++i) {
+ init.push_back(stan::test::unit::instrumented_writer{});
+ par_parameters.emplace_back(std::make_unique(), "#");
+ seq_parameters.emplace_back(std::make_unique(), "#");
+ diagnostic.push_back(stan::test::unit::instrumented_writer{});
+ context.push_back(std::make_shared());
+ }
+ }
+ stan::io::empty_var_context data_context;
+ std::stringstream model_log;
+ stan::test::unit::instrumented_logger logger;
+ std::vector init;
+ using str_writer = stan::callbacks::unique_stream_writer;
+ std::vector par_parameters;
+ std::vector seq_parameters;
+ std::vector diagnostic;
+ std::vector> context;
+ std::unique_ptr model;
+};
+
+/**
+ * This test checks that running multiple chains in one call
+ * with the same initial id is the same as running multiple calls
+ * with incrementing chain ids.
+ */
+TEST_F(ServicesSampleHmcNutsDenseEAdaptParMatch, single_multi_match) {
+ constexpr unsigned int random_seed = 0;
+ constexpr unsigned int chain = 0;
+ constexpr double init_radius = 0;
+ constexpr int num_warmup = 200;
+ constexpr int num_samples = 400;
+ constexpr int num_thin = 5;
+ constexpr bool save_warmup = true;
+ constexpr int refresh = 0;
+ constexpr double stepsize = 0.1;
+ constexpr double stepsize_jitter = 0;
+ constexpr int max_depth = 8;
+ constexpr double delta = .1;
+ constexpr double gamma = .1;
+ constexpr double kappa = .1;
+ constexpr double t0 = .1;
+ constexpr unsigned int init_buffer = 50;
+ constexpr unsigned int term_buffer = 50;
+ constexpr unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+ int return_code = stan::services::sample::hmc_nuts_dense_e_adapt(
+ *model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, par_parameters, diagnostic);
+
+ EXPECT_EQ(0, return_code);
+
+ int num_output_lines = (num_warmup + num_samples) / num_thin;
+ EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count());
+ for (int i = 0; i < num_chains; ++i) {
+ stan::test::unit::instrumented_writer seq_init;
+ stan::test::unit::instrumented_writer seq_diagnostic;
+ return_code = stan::services::sample::hmc_nuts_dense_e_adapt(
+ *model, *(context[i]), random_seed, i, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, seq_init, seq_parameters[i], seq_diagnostic);
+ EXPECT_EQ(0, return_code);
+ }
+ std::vector par_res;
+ for (int i = 0; i < num_chains; ++i) {
+ auto par_str = par_parameters[i].get_stream().str();
+ auto sub_par_str = par_str.substr(par_str.find("Elements") - 1);
+ std::istringstream sub_par_stream(sub_par_str);
+ Eigen::MatrixXd par_mat
+ = stan::test::read_stan_sample_csv(sub_par_stream, 80, 9);
+ par_res.push_back(par_mat);
+ }
+ std::vector seq_res;
+ for (int i = 0; i < num_chains; ++i) {
+ auto seq_str = seq_parameters[i].get_stream().str();
+ auto sub_seq_str = seq_str.substr(seq_str.find("Elements") - 1);
+ std::istringstream sub_seq_stream(sub_seq_str);
+ Eigen::MatrixXd seq_mat
+ = stan::test::read_stan_sample_csv(sub_seq_stream, 80, 9);
+ seq_res.push_back(seq_mat);
+ }
+ for (int i = 0; i < num_chains; ++i) {
+ Eigen::MatrixXd diff_res
+ = (par_res[i].array() - seq_res[i].array()).matrix();
+ EXPECT_MATRIX_EQ(diff_res, Eigen::MatrixXd::Zero(80, 9));
+ }
+}
diff --git a/src/test/unit/services/sample/hmc_nuts_dense_e_adapt_parallel_test.cpp b/src/test/unit/services/sample/hmc_nuts_dense_e_adapt_parallel_test.cpp
new file mode 100644
index 00000000000..1f8d2a117b9
--- /dev/null
+++ b/src/test/unit/services/sample/hmc_nuts_dense_e_adapt_parallel_test.cpp
@@ -0,0 +1,175 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+auto&& blah = stan::math::init_threadpool_tbb();
+
+static constexpr size_t num_chains = 4;
+class ServicesSampleHmcNutsDenseEAdaptPar : public testing::Test {
+ public:
+ ServicesSampleHmcNutsDenseEAdaptPar() : model(data_context, 0, &model_log) {
+ for (int i = 0; i < num_chains; ++i) {
+ init.push_back(stan::test::unit::instrumented_writer{});
+ parameter.push_back(stan::test::unit::instrumented_writer{});
+ diagnostic.push_back(stan::test::unit::instrumented_writer{});
+ context.push_back(std::make_shared());
+ }
+ }
+ stan::io::empty_var_context data_context;
+ std::stringstream model_log;
+ stan::test::unit::instrumented_logger logger;
+ std::vector init;
+ std::vector parameter;
+ std::vector diagnostic;
+ std::vector> context;
+ stan_model model;
+};
+
+TEST_F(ServicesSampleHmcNutsDenseEAdaptPar, call_count) {
+ unsigned int random_seed = 0;
+ unsigned int chain = 1;
+ double init_radius = 0;
+ int num_warmup = 200;
+ int num_samples = 400;
+ int num_thin = 5;
+ bool save_warmup = true;
+ int refresh = 0;
+ double stepsize = 0.1;
+ double stepsize_jitter = 0;
+ int max_depth = 8;
+ double delta = .1;
+ double gamma = .1;
+ double kappa = .1;
+ double t0 = .1;
+ unsigned int init_buffer = 50;
+ unsigned int term_buffer = 50;
+ unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+
+ int return_code = stan::services::sample::hmc_nuts_dense_e_adapt(
+ model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, parameter, diagnostic);
+
+ EXPECT_EQ(0, return_code);
+
+ int num_output_lines = (num_warmup + num_samples) / num_thin;
+ EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count());
+ for (int i = 0; i < num_chains; ++i) {
+ EXPECT_EQ(1, parameter[i].call_count("vector_string"));
+ EXPECT_EQ(num_output_lines, parameter[i].call_count("vector_double"));
+ EXPECT_EQ(1, diagnostic[i].call_count("vector_string"));
+ EXPECT_EQ(num_output_lines, diagnostic[i].call_count("vector_double"));
+ }
+}
+
+TEST_F(ServicesSampleHmcNutsDenseEAdaptPar, parameter_checks) {
+ unsigned int random_seed = 0;
+ unsigned int chain = 1;
+ double init_radius = 0;
+ int num_warmup = 200;
+ int num_samples = 400;
+ int num_thin = 5;
+ bool save_warmup = true;
+ int refresh = 0;
+ double stepsize = 0.1;
+ double stepsize_jitter = 0;
+ int max_depth = 8;
+ double delta = .1;
+ double gamma = .1;
+ double kappa = .1;
+ double t0 = .1;
+ unsigned int init_buffer = 50;
+ unsigned int term_buffer = 50;
+ unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+
+ int return_code = stan::services::sample::hmc_nuts_dense_e_adapt(
+ model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, parameter, diagnostic);
+
+ for (size_t i = 0; i < num_chains; ++i) {
+ std::vector> parameter_names;
+ parameter_names = parameter[i].vector_string_values();
+ std::vector> parameter_values;
+ parameter_values = parameter[i].vector_double_values();
+ std::vector> diagnostic_names;
+ diagnostic_names = diagnostic[i].vector_string_values();
+ std::vector> diagnostic_values;
+ diagnostic_values = diagnostic[i].vector_double_values();
+
+ // Expectations of parameter parameter names.
+ ASSERT_EQ(9, parameter_names[0].size());
+ EXPECT_EQ("lp__", parameter_names[0][0]);
+ EXPECT_EQ("accept_stat__", parameter_names[0][1]);
+ EXPECT_EQ("stepsize__", parameter_names[0][2]);
+ EXPECT_EQ("treedepth__", parameter_names[0][3]);
+ EXPECT_EQ("n_leapfrog__", parameter_names[0][4]);
+ EXPECT_EQ("divergent__", parameter_names[0][5]);
+ EXPECT_EQ("energy__", parameter_names[0][6]);
+ EXPECT_EQ("x", parameter_names[0][7]);
+ EXPECT_EQ("y", parameter_names[0][8]);
+
+ // Expect one name per parameter value.
+ EXPECT_EQ(parameter_names[0].size(), parameter_values[0].size());
+ EXPECT_EQ(diagnostic_names[0].size(), diagnostic_values[0].size());
+
+ EXPECT_EQ((num_warmup + num_samples) / num_thin, parameter_values.size());
+
+ // Expect one call to set parameter names, and one set of output per
+ // iteration.
+ EXPECT_EQ("lp__", diagnostic_names[0][0]);
+ EXPECT_EQ("accept_stat__", diagnostic_names[0][1]);
+ }
+ EXPECT_EQ(return_code, 0);
+}
+
+TEST_F(ServicesSampleHmcNutsDenseEAdaptPar, output_regression) {
+ unsigned int random_seed = 0;
+ unsigned int chain = 1;
+ double init_radius = 0;
+ int num_warmup = 200;
+ int num_samples = 400;
+ int num_thin = 5;
+ bool save_warmup = true;
+ int refresh = 0;
+ double stepsize = 0.1;
+ double stepsize_jitter = 0;
+ int max_depth = 8;
+ double delta = .1;
+ double gamma = .1;
+ double kappa = .1;
+ double t0 = .1;
+ unsigned int init_buffer = 50;
+ unsigned int term_buffer = 50;
+ unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+
+ stan::services::sample::hmc_nuts_dense_e_adapt(
+ model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, parameter, diagnostic);
+
+ for (auto&& init_it : init) {
+ std::vector init_values;
+ init_values = init_it.string_values();
+
+ EXPECT_EQ(0, init_values.size());
+ }
+
+ EXPECT_EQ(num_chains, logger.find_info("Elapsed Time:"));
+ EXPECT_EQ(num_chains, logger.find_info("seconds (Warm-up)"));
+ EXPECT_EQ(num_chains, logger.find_info("seconds (Sampling)"));
+ EXPECT_EQ(num_chains, logger.find_info("seconds (Total)"));
+ EXPECT_EQ(0, logger.call_count_error());
+}
diff --git a/src/test/unit/services/sample/hmc_nuts_diag_e_adapt_parallel_match_test.cpp b/src/test/unit/services/sample/hmc_nuts_diag_e_adapt_parallel_match_test.cpp
new file mode 100644
index 00000000000..c4167419cbf
--- /dev/null
+++ b/src/test/unit/services/sample/hmc_nuts_diag_e_adapt_parallel_match_test.cpp
@@ -0,0 +1,108 @@
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+auto&& blah = stan::math::init_threadpool_tbb();
+
+static constexpr size_t num_chains = 4;
+class ServicesSampleHmcNutsDiagEAdaptParMatch : public testing::Test {
+ public:
+ ServicesSampleHmcNutsDiagEAdaptParMatch()
+ : model(std::make_unique(
+ data_context, 0, &model_log)) {
+ for (int i = 0; i < num_chains; ++i) {
+ init.push_back(stan::test::unit::instrumented_writer{});
+ par_parameters.emplace_back(std::make_unique(), "#");
+ seq_parameters.emplace_back(std::make_unique(), "#");
+ diagnostic.push_back(stan::test::unit::instrumented_writer{});
+ context.push_back(std::make_shared());
+ }
+ }
+ stan::io::empty_var_context data_context;
+ std::stringstream model_log;
+ stan::test::unit::instrumented_logger logger;
+ std::vector init;
+ using str_writer = stan::callbacks::unique_stream_writer;
+ std::vector par_parameters;
+ std::vector seq_parameters;
+ std::vector diagnostic;
+ std::vector> context;
+ std::unique_ptr model;
+};
+
+/**
+ * This test checks that running multiple chains in one call
+ * with the same initial id is the same as running multiple calls
+ * with incrementing chain ids.
+ */
+TEST_F(ServicesSampleHmcNutsDiagEAdaptParMatch, single_multi_match) {
+ constexpr unsigned int random_seed = 0;
+ constexpr unsigned int chain = 0;
+ constexpr double init_radius = 0;
+ constexpr int num_warmup = 200;
+ constexpr int num_samples = 400;
+ constexpr int num_thin = 5;
+ constexpr bool save_warmup = true;
+ constexpr int refresh = 0;
+ constexpr double stepsize = 0.1;
+ constexpr double stepsize_jitter = 0;
+ constexpr int max_depth = 8;
+ constexpr double delta = .1;
+ constexpr double gamma = .1;
+ constexpr double kappa = .1;
+ constexpr double t0 = .1;
+ constexpr unsigned int init_buffer = 50;
+ constexpr unsigned int term_buffer = 50;
+ constexpr unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+ int return_code = stan::services::sample::hmc_nuts_diag_e_adapt(
+ *model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, par_parameters, diagnostic);
+
+ EXPECT_EQ(0, return_code);
+
+ int num_output_lines = (num_warmup + num_samples) / num_thin;
+ EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count());
+ for (int i = 0; i < num_chains; ++i) {
+ stan::test::unit::instrumented_writer seq_init;
+ stan::test::unit::instrumented_writer seq_diagnostic;
+ return_code = stan::services::sample::hmc_nuts_diag_e_adapt(
+ *model, *(context[i]), random_seed, i, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, seq_init, seq_parameters[i], seq_diagnostic);
+ EXPECT_EQ(0, return_code);
+ }
+ std::vector par_res;
+ for (int i = 0; i < num_chains; ++i) {
+ auto par_str = par_parameters[i].get_stream().str();
+ auto sub_par_str = par_str.substr(par_str.find("Diagonal") - 1);
+ std::istringstream sub_par_stream(sub_par_str);
+ Eigen::MatrixXd par_mat
+ = stan::test::read_stan_sample_csv(sub_par_stream, 80, 9);
+ par_res.push_back(par_mat);
+ }
+ std::vector seq_res;
+ for (int i = 0; i < num_chains; ++i) {
+ auto seq_str = seq_parameters[i].get_stream().str();
+ auto sub_seq_str = seq_str.substr(seq_str.find("Diagonal") - 1);
+ std::istringstream sub_seq_stream(sub_seq_str);
+ Eigen::MatrixXd seq_mat
+ = stan::test::read_stan_sample_csv(sub_seq_stream, 80, 9);
+ seq_res.push_back(seq_mat);
+ }
+ for (int i = 0; i < num_chains; ++i) {
+ Eigen::MatrixXd diff_res
+ = (par_res[i].array() - seq_res[i].array()).matrix();
+ EXPECT_MATRIX_EQ(diff_res, Eigen::MatrixXd::Zero(80, 9));
+ }
+}
diff --git a/src/test/unit/services/sample/hmc_nuts_diag_e_adapt_parallel_test.cpp b/src/test/unit/services/sample/hmc_nuts_diag_e_adapt_parallel_test.cpp
new file mode 100644
index 00000000000..453b03adcdd
--- /dev/null
+++ b/src/test/unit/services/sample/hmc_nuts_diag_e_adapt_parallel_test.cpp
@@ -0,0 +1,175 @@
+#include
+#include
+#include
+#include
+#include
+#include
+
+auto&& blah = stan::math::init_threadpool_tbb();
+
+static constexpr size_t num_chains = 4;
+class ServicesSampleHmcNutsDiagEAdaptPar : public testing::Test {
+ public:
+ ServicesSampleHmcNutsDiagEAdaptPar() : model(data_context, 0, &model_log) {
+ for (int i = 0; i < num_chains; ++i) {
+ init.push_back(stan::test::unit::instrumented_writer{});
+ parameter.push_back(stan::test::unit::instrumented_writer{});
+ diagnostic.push_back(stan::test::unit::instrumented_writer{});
+ context.push_back(std::make_shared());
+ }
+ }
+ stan::io::empty_var_context data_context;
+ std::stringstream model_log;
+ stan::test::unit::instrumented_logger logger;
+ std::vector init;
+ std::vector parameter;
+ std::vector diagnostic;
+ std::vector> context;
+ stan_model model;
+};
+
+TEST_F(ServicesSampleHmcNutsDiagEAdaptPar, call_count) {
+ unsigned int random_seed = 0;
+ unsigned int chain = 1;
+ double init_radius = 0;
+ int num_warmup = 200;
+ int num_samples = 400;
+ int num_thin = 5;
+ bool save_warmup = true;
+ int refresh = 0;
+ double stepsize = 0.1;
+ double stepsize_jitter = 0;
+ int max_depth = 8;
+ double delta = .1;
+ double gamma = .1;
+ double kappa = .1;
+ double t0 = .1;
+ unsigned int init_buffer = 50;
+ unsigned int term_buffer = 50;
+ unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+
+ int return_code = stan::services::sample::hmc_nuts_diag_e_adapt(
+ model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, parameter, diagnostic);
+
+ EXPECT_EQ(0, return_code);
+
+ int num_output_lines = (num_warmup + num_samples) / num_thin;
+ EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count());
+ for (int i = 0; i < num_chains; ++i) {
+ EXPECT_EQ(1, parameter[i].call_count("vector_string"));
+ EXPECT_EQ(num_output_lines, parameter[i].call_count("vector_double"));
+ EXPECT_EQ(1, diagnostic[i].call_count("vector_string"));
+ EXPECT_EQ(num_output_lines, diagnostic[i].call_count("vector_double"));
+ }
+}
+
+TEST_F(ServicesSampleHmcNutsDiagEAdaptPar, parameter_checks) {
+ unsigned int random_seed = 0;
+ unsigned int chain = 1;
+ double init_radius = 0;
+ int num_warmup = 200;
+ int num_samples = 400;
+ int num_thin = 5;
+ bool save_warmup = true;
+ int refresh = 0;
+ double stepsize = 0.1;
+ double stepsize_jitter = 0;
+ int max_depth = 8;
+ double delta = .1;
+ double gamma = .1;
+ double kappa = .1;
+ double t0 = .1;
+ unsigned int init_buffer = 50;
+ unsigned int term_buffer = 50;
+ unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+
+ int return_code = stan::services::sample::hmc_nuts_diag_e_adapt(
+ model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, parameter, diagnostic);
+
+ for (size_t i = 0; i < num_chains; ++i) {
+ std::vector> parameter_names;
+ parameter_names = parameter[i].vector_string_values();
+ std::vector> parameter_values;
+ parameter_values = parameter[i].vector_double_values();
+ std::vector> diagnostic_names;
+ diagnostic_names = diagnostic[i].vector_string_values();
+ std::vector> diagnostic_values;
+ diagnostic_values = diagnostic[i].vector_double_values();
+
+ // Expectations of parameter parameter names.
+ ASSERT_EQ(9, parameter_names[0].size());
+ EXPECT_EQ("lp__", parameter_names[0][0]);
+ EXPECT_EQ("accept_stat__", parameter_names[0][1]);
+ EXPECT_EQ("stepsize__", parameter_names[0][2]);
+ EXPECT_EQ("treedepth__", parameter_names[0][3]);
+ EXPECT_EQ("n_leapfrog__", parameter_names[0][4]);
+ EXPECT_EQ("divergent__", parameter_names[0][5]);
+ EXPECT_EQ("energy__", parameter_names[0][6]);
+ EXPECT_EQ("x", parameter_names[0][7]);
+ EXPECT_EQ("y", parameter_names[0][8]);
+
+ // Expect one name per parameter value.
+ EXPECT_EQ(parameter_names[0].size(), parameter_values[0].size());
+ EXPECT_EQ(diagnostic_names[0].size(), diagnostic_values[0].size());
+
+ EXPECT_EQ((num_warmup + num_samples) / num_thin, parameter_values.size());
+
+ // Expect one call to set parameter names, and one set of output per
+ // iteration.
+ EXPECT_EQ("lp__", diagnostic_names[0][0]);
+ EXPECT_EQ("accept_stat__", diagnostic_names[0][1]);
+ }
+ EXPECT_EQ(return_code, 0);
+}
+
+TEST_F(ServicesSampleHmcNutsDiagEAdaptPar, output_regression) {
+ unsigned int random_seed = 0;
+ unsigned int chain = 1;
+ double init_radius = 0;
+ int num_warmup = 200;
+ int num_samples = 400;
+ int num_thin = 5;
+ bool save_warmup = true;
+ int refresh = 0;
+ double stepsize = 0.1;
+ double stepsize_jitter = 0;
+ int max_depth = 8;
+ double delta = .1;
+ double gamma = .1;
+ double kappa = .1;
+ double t0 = .1;
+ unsigned int init_buffer = 50;
+ unsigned int term_buffer = 50;
+ unsigned int window = 100;
+ stan::test::unit::instrumented_interrupt interrupt;
+ EXPECT_EQ(interrupt.call_count(), 0);
+
+ stan::services::sample::hmc_nuts_diag_e_adapt(
+ model, num_chains, context, random_seed, chain, init_radius, num_warmup,
+ num_samples, num_thin, save_warmup, refresh, stepsize, stepsize_jitter,
+ max_depth, delta, gamma, kappa, t0, init_buffer, term_buffer, window,
+ interrupt, logger, init, parameter, diagnostic);
+
+ for (auto&& init_it : init) {
+ std::vector init_values;
+ init_values = init_it.string_values();
+
+ EXPECT_EQ(0, init_values.size());
+ }
+
+ EXPECT_EQ(num_chains, logger.find_info("Elapsed Time:"));
+ EXPECT_EQ(num_chains, logger.find_info("seconds (Warm-up)"));
+ EXPECT_EQ(num_chains, logger.find_info("seconds (Sampling)"));
+ EXPECT_EQ(num_chains, logger.find_info("seconds (Total)"));
+ EXPECT_EQ(0, logger.call_count_error());
+}
diff --git a/src/test/unit/services/util.hpp b/src/test/unit/services/util.hpp
new file mode 100644
index 00000000000..f283fa6f49f
--- /dev/null
+++ b/src/test/unit/services/util.hpp
@@ -0,0 +1,45 @@
+#ifndef STAN_SRC_TEST_UNIT_SERVICES_UTIL_HPP
+#define STAN_SRC_TEST_UNIT_SERVICES_UTIL_HPP
+
+#include
+#include
+#include
+#include
+
+namespace stan {
+namespace test {
+/**
+ * Read a CSV into an Eigen matrix.
+ * @param in An input string stream holding the CSV
+ * @param rows Number of rows
+ * @param cols Number of columns.
+ */
+Eigen::MatrixXd read_stan_sample_csv(std::istringstream& in, int rows,
+ int cols) {
+ std::string line;
+ int row = 0;
+ int col = 0;
+ Eigen::MatrixXd res = Eigen::MatrixXd(rows, cols);
+ while (std::getline(in, line)) {
+ if (line.find("#") != std::string::npos) {
+ continue;
+ }
+ const char* ptr = line.c_str();
+ int len = line.length();
+ col = 0;
+
+ const char* start = ptr;
+ for (int i = 0; i < len; i++) {
+ if (ptr[i] == ',') {
+ res(row, col++) = atof(start);
+ start = ptr + i + 1;
+ }
+ }
+ res(row, col) = atof(start);
+ row++;
+ }
+ return res;
+}
+} // namespace test
+} // namespace stan
+#endif