From 5edf4e25df7adab1051c9e4b26b3f4f306cd982a Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Sat, 20 Mar 2021 15:19:02 -0400 Subject: [PATCH 01/12] adds file_stream_writer and run_adaptive_sampler method for parallel --- src/stan/callbacks/file_stream_writer.hpp | 119 ++++++++++++++++++ src/stan/callbacks/stream_logger.hpp | 2 +- src/stan/callbacks/stream_writer.hpp | 2 +- src/stan/callbacks/tee_writer.hpp | 2 +- .../services/util/generate_transitions.hpp | 5 +- .../services/util/run_adaptive_sampler.hpp | 87 ++++++++++++- .../callbacks/file_stream_writer_test.cpp | 47 +++++++ 7 files changed, 259 insertions(+), 5 deletions(-) create mode 100644 src/stan/callbacks/file_stream_writer.hpp create mode 100644 src/test/unit/callbacks/file_stream_writer_test.cpp diff --git a/src/stan/callbacks/file_stream_writer.hpp b/src/stan/callbacks/file_stream_writer.hpp new file mode 100644 index 00000000000..1d9725b6656 --- /dev/null +++ b/src/stan/callbacks/file_stream_writer.hpp @@ -0,0 +1,119 @@ +#ifndef STAN_CALLBACKS_FILE_STREAM_WRITER_HPP +#define STAN_CALLBACKS_FILE_STREAM_WRITER_HPP + +#include +#include +#include +#include + +namespace stan { +namespace callbacks { + +/** + * file_stream_writer is an implementation + * of writer that writes to a file. + */ +class file_stream_writer final : public writer { + public: + /** + * Constructs a file 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 file_stream_writer(std::unique_ptr&& output, + const std::string& comment_prefix = "") + : output_(std::move(output)), comment_prefix_(comment_prefix) {} + + + file_stream_writer(); + file_stream_writer(file_stream_writer& other) = delete; + file_stream_writer(file_stream_writer&& other) : + output_(std::move(other.output_)), comment_prefix_(std::move(other.comment_prefix_)) {} + /** + * Virtual destructor + */ + virtual ~file_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()() { *output_ << comment_prefix_ << std::endl; } + + /** + * Writes the comment_prefix then the message followed by a newline. + * + * @param[in] message A string + */ + void operator()(const std::string& message) { + *output_ << comment_prefix_ << message << std::endl; + } + + 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; + + typename std::vector::const_iterator last = v.end(); + --last; + + for (typename std::vector::const_iterator it = v.begin(); it != last; + ++it) + *output_ << *it << ","; + *output_ << v.back() << std::endl; + } +}; + +} +} + +#endif 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/stream_writer.hpp b/src/stan/callbacks/stream_writer.hpp index 6519531c0ac..226f16a76e1 100644 --- a/src/stan/callbacks/stream_writer.hpp +++ b/src/stan/callbacks/stream_writer.hpp @@ -13,7 +13,7 @@ namespace callbacks { * stream_writer is an implementation * of writer that writes to a stream. */ -class stream_writer : public writer { +class stream_writer final : public writer { public: /** * Constructs a stream writer with an output stream 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/services/util/generate_transitions.hpp b/src/stan/services/util/generate_transitions.hpp index 1516c6f0f99..8ad93706c6a 100644 --- a/src/stan/services/util/generate_transitions.hpp +++ b/src/stan/services/util/generate_transitions.hpp @@ -44,7 +44,7 @@ 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 n_chain = 0) { for (int m = 0; m < num_iterations; ++m) { callback(); @@ -52,6 +52,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 (n_chain > 0) { + message << "Chain [" << (n_chain + 1) << "]"; + } message << "Iteration: "; message << std::setw(it_print_width) << m + 1 + start << " / " << finish; message << " [" << std::setw(3) diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index ba0135158dd..39e1f5a88b8 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -34,7 +34,7 @@ namespace util { * @param[in,out] sample_writer writer for draws * @param[in,out] diagnostic_writer writer for diagnostic information */ -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, @@ -87,6 +87,91 @@ void run_adaptive_sampler(Sampler& sampler, Model& model, / 1000.0; writer.write_timing(warm_delta_t, sample_delta_t); } + +template +void run_adaptive_sampler(Sampler& sampler, Model& model, + std::vector>& cont_vector, + int num_warmup, int num_samples, int num_thin, + int refresh, bool save_warmup, RNG& rng, + callbacks::interrupt& interrupt, + callbacks::logger& logger, + std::vector& sample_writer, + std::vector& diagnostic_writer, + size_t n_chains = 0) { + std::vector writers; + writers.reserve(n_chain); + std::vector samples; + samples.reserve(n_chain); + + for (int i = 0; i < n_chain; ++i) { + auto&& sample_writer = sample_writers[i]; + auto&& diagnostic_writer = diagnostic_writers[i]; + auto&& sampler = samplers[i]; + Eigen::Map cont_params(cont_vectors[i].data(), + cont_vectors[i].size()); + sampler.engage_adaptation(); + try { + sampler.z().q = cont_params; + sampler.init_stepsize(logger); + } catch (const std::exception& e) { + logger.info("Exception initializing step size."); + logger.info(e.what()); + return; + } + writers.emplace_back(sample_writer, diagnostic_writer, logger); + samples.emplace_back(cont_params, 0, 0); + } + std::vector warm_delta_v(n_chain, 0); + tbb::parallel_for( + tbb::blocked_range(0, n_chain, 1), + [num_warmup, num_samples, num_thin, refresh, save_warmup, &samples, + &warm_delta_v, &writers, &samplers, &model, &cont_vectors, &rngs, + &interrupt, &logger, &sample_writers, + &diagnostic_writers](const tbb::blocked_range& r) { + for (size_t i = r.begin(); i != r.end(); ++i) { + auto&& writer = writers[i]; + auto&& sampler = samplers[i]; + auto&& samp = samples[i]; + + // Headers + writer.write_sample_names(samp, sampler, model); + writer.write_diagnostic_names(samp, sampler, 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, samp, + model, rngs[i], interrupt, logger, i); + auto end_warm = std::chrono::steady_clock::now(); + warm_delta_v[i] + = std::chrono::duration_cast( + end_warm - start_warm) + .count() + / 1000.0; + sampler.disengage_adaptation(); + auto&& sample_writer = sample_writers[i]; + auto&& sampler = samplers[i]; + writer.write_adapt_finish(sampler); + sampler.write_sampler_state(sample_writer); + + 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, samp, model, + rngs[i], interrupt, logger, i); + auto end_sample = std::chrono::steady_clock::now(); + double sample_delta_t + = std::chrono::duration_cast( + end_sample - start_sample) + .count() + / 1000.0; + writer.write_timing(warm_delta_v[i], sample_delta_t); + } + }, + tbb::simple_partitioner()); +} + } // namespace util } // namespace services } // namespace stan diff --git a/src/test/unit/callbacks/file_stream_writer_test.cpp b/src/test/unit/callbacks/file_stream_writer_test.cpp new file mode 100644 index 00000000000..b24142c381b --- /dev/null +++ b/src/test/unit/callbacks/file_stream_writer_test.cpp @@ -0,0 +1,47 @@ +#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::file_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()); +} From faa5eb0de817c0f41475cb7ca65dc3f41f6d6674 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Sat, 20 Mar 2021 22:20:33 -0400 Subject: [PATCH 02/12] fix grammar error for parallel run_adaptive_sampler --- .../services/util/run_adaptive_sampler.hpp | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index 39e1f5a88b8..094be9d3304 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 @@ -88,17 +89,15 @@ void run_adaptive_sampler(Sampler& sampler, Model& model, writer.write_timing(warm_delta_t, sample_delta_t); } -template -void run_adaptive_sampler(Sampler& sampler, Model& model, - std::vector>& cont_vector, - int num_warmup, int num_samples, int num_thin, - int refresh, bool save_warmup, RNG& rng, +template +void run_adaptive_sampler(std::vector& samplers, Model& model, + std::vector>& cont_vectors, int num_warmup, + int num_samples, int num_thin, int refresh, + bool save_warmup, std::vector& rngs, callbacks::interrupt& interrupt, callbacks::logger& logger, - std::vector& sample_writer, - std::vector& diagnostic_writer, - size_t n_chains = 0) { + std::vector& sample_writers, + std::vector& diagnostic_writers, size_t n_chain) { std::vector writers; writers.reserve(n_chain); std::vector samples; @@ -151,7 +150,6 @@ void run_adaptive_sampler(Sampler& sampler, Model& model, / 1000.0; sampler.disengage_adaptation(); auto&& sample_writer = sample_writers[i]; - auto&& sampler = samplers[i]; writer.write_adapt_finish(sampler); sampler.write_sampler_state(sample_writer); From 751403fa0ae9bdc4f963e70f03ded2edf98e6f37 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sun, 21 Mar 2021 02:35:29 +0000 Subject: [PATCH 03/12] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- src/stan/callbacks/file_stream_writer.hpp | 18 ++++++++---------- .../services/util/run_adaptive_sampler.hpp | 12 +++++++----- .../unit/callbacks/file_stream_writer_test.cpp | 9 ++++++--- 3 files changed, 21 insertions(+), 18 deletions(-) diff --git a/src/stan/callbacks/file_stream_writer.hpp b/src/stan/callbacks/file_stream_writer.hpp index 1d9725b6656..e182321f831 100644 --- a/src/stan/callbacks/file_stream_writer.hpp +++ b/src/stan/callbacks/file_stream_writer.hpp @@ -23,15 +23,15 @@ class file_stream_writer final : public writer { * @param[in] comment_prefix string to stream before each comment line. * Default is "". */ - explicit file_stream_writer(std::unique_ptr&& output, - const std::string& comment_prefix = "") + explicit file_stream_writer(std::unique_ptr&& output, + const std::string& comment_prefix = "") : output_(std::move(output)), comment_prefix_(comment_prefix) {} - file_stream_writer(); file_stream_writer(file_stream_writer& other) = delete; - file_stream_writer(file_stream_writer&& other) : - output_(std::move(other.output_)), comment_prefix_(std::move(other.comment_prefix_)) {} + file_stream_writer(file_stream_writer&& other) + : output_(std::move(other.output_)), + comment_prefix_(std::move(other.comment_prefix_)) {} /** * Virtual destructor */ @@ -51,9 +51,7 @@ class file_stream_writer final : public writer { /** * Get the underlying stream */ - auto& get_stream() { - return *output_; - } + auto& get_stream() { return *output_; } /** * Writes a set of values in csv format followed by a newline. @@ -113,7 +111,7 @@ class file_stream_writer final : public writer { } }; -} -} +} // namespace callbacks +} // namespace stan #endif diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index 094be9d3304..f1a5946689c 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -89,15 +89,17 @@ void run_adaptive_sampler(Sampler& sampler, Model& model, writer.write_timing(warm_delta_t, sample_delta_t); } -template +template void run_adaptive_sampler(std::vector& samplers, Model& model, - std::vector>& cont_vectors, int num_warmup, - int num_samples, int num_thin, int refresh, - bool save_warmup, std::vector& rngs, + std::vector>& cont_vectors, + int num_warmup, int num_samples, int num_thin, + int refresh, bool save_warmup, std::vector& rngs, callbacks::interrupt& interrupt, callbacks::logger& logger, std::vector& sample_writers, - std::vector& diagnostic_writers, size_t n_chain) { + std::vector& diagnostic_writers, + size_t n_chain) { std::vector writers; writers.reserve(n_chain); std::vector samples; diff --git a/src/test/unit/callbacks/file_stream_writer_test.cpp b/src/test/unit/callbacks/file_stream_writer_test.cpp index b24142c381b..b3a64e4dcca 100644 --- a/src/test/unit/callbacks/file_stream_writer_test.cpp +++ b/src/test/unit/callbacks/file_stream_writer_test.cpp @@ -23,7 +23,8 @@ TEST_F(StanInterfaceCallbacksStreamWriter, double_vector) { x.push_back(n); EXPECT_NO_THROW(writer(x)); - EXPECT_EQ("0,1,2,3,4\n", static_cast(writer.get_stream()).str()); + EXPECT_EQ("0,1,2,3,4\n", + static_cast(writer.get_stream()).str()); } TEST_F(StanInterfaceCallbacksStreamWriter, string_vector) { @@ -33,7 +34,8 @@ TEST_F(StanInterfaceCallbacksStreamWriter, string_vector) { 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()); + EXPECT_EQ("0,1,2,3,4\n", + static_cast(writer.get_stream()).str()); } TEST_F(StanInterfaceCallbacksStreamWriter, null) { @@ -43,5 +45,6 @@ TEST_F(StanInterfaceCallbacksStreamWriter, null) { TEST_F(StanInterfaceCallbacksStreamWriter, string) { EXPECT_NO_THROW(writer("message")); - EXPECT_EQ("message\n", static_cast(writer.get_stream()).str()); + EXPECT_EQ("message\n", + static_cast(writer.get_stream()).str()); } From a08ba353b4011cbc1104a40ce1e2add14ddbe0a6 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Sun, 21 Mar 2021 14:23:02 -0400 Subject: [PATCH 04/12] adds test for parallel adaptive --- .../services/util/generate_transitions.hpp | 3 +- .../services/util/run_adaptive_sampler.hpp | 10 +- .../unit/services/instrumented_callbacks.hpp | 37 +++- .../run_adaptive_sampler_parallel_test.cpp | 208 ++++++++++++++++++ 4 files changed, 244 insertions(+), 14 deletions(-) create mode 100644 src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp diff --git a/src/stan/services/util/generate_transitions.hpp b/src/stan/services/util/generate_transitions.hpp index 8ad93706c6a..53261a33435 100644 --- a/src/stan/services/util/generate_transitions.hpp +++ b/src/stan/services/util/generate_transitions.hpp @@ -51,7 +51,8 @@ void generate_transitions(stan::mcmc::base_mcmc& sampler, int num_iterations, if (refresh > 0 && (start + m + 1 == finish || m == 0 || (m + 1) % refresh == 0)) { int it_print_width = std::ceil(std::log10(static_cast(finish))); - std::stringstream message; + std::string mes; + std::stringstream message(mes, std::ios_base::in); if (n_chain > 0) { message << "Chain [" << (n_chain + 1) << "]"; } diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index f1a5946689c..d27225a3c6a 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -124,10 +124,11 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, samples.emplace_back(cont_params, 0, 0); } std::vector warm_delta_v(n_chain, 0); + std::vector sample_delta_v(n_chain, 0); tbb::parallel_for( tbb::blocked_range(0, n_chain, 1), [num_warmup, num_samples, num_thin, refresh, save_warmup, &samples, - &warm_delta_v, &writers, &samplers, &model, &cont_vectors, &rngs, + &warm_delta_v, &sample_delta_v, &writers, &samplers, &model, &cont_vectors, &rngs, &interrupt, &logger, &sample_writers, &diagnostic_writers](const tbb::blocked_range& r) { for (size_t i = r.begin(); i != r.end(); ++i) { @@ -161,15 +162,18 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, refresh, true, false, writer, samp, model, rngs[i], interrupt, logger, i); auto end_sample = std::chrono::steady_clock::now(); - double sample_delta_t + sample_delta_v[i] = std::chrono::duration_cast( end_sample - start_sample) .count() / 1000.0; - writer.write_timing(warm_delta_v[i], sample_delta_t); } }, tbb::simple_partitioner()); + for (int i = 0; i < n_chain; ++i) { + writers[i].write_timing(warm_delta_v[i], sample_delta_v[i]); + } + } } // namespace util diff --git a/src/test/unit/services/instrumented_callbacks.hpp b/src/test/unit/services/instrumented_callbacks.hpp index 5df61c17f74..a0f866aef08 100644 --- a/src/test/unit/services/instrumented_callbacks.hpp +++ b/src/test/unit/services/instrumented_callbacks.hpp @@ -9,7 +9,7 @@ #include #include #include - +#include namespace stan { namespace test { namespace unit { @@ -27,7 +27,7 @@ class instrumented_interrupt : public stan::callbacks::interrupt { unsigned int call_count() { return counter_; } private: - unsigned int counter_; + std::atomic counter_; }; /** @@ -96,9 +96,8 @@ 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; } @@ -137,7 +136,7 @@ class instrumented_writer : public stan::callbacks::writer { std::vector string_values() { return string; }; private: - std::map counter_; + std::map> counter_; std::vector > string_double; std::vector > string_int; std::vector > string_string; @@ -156,35 +155,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/util/run_adaptive_sampler_parallel_test.cpp b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp new file mode 100644 index 00000000000..ed52f2a9ec4 --- /dev/null +++ b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp @@ -0,0 +1,208 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +class ServicesUtil : public testing::Test { + using model_t = stan::mcmc::adapt_unit_e_nuts; + public: + ServicesUtil() + : model(context, 0, &model_log), + rng(3), + cont_vector(3, std::vector{0, 0}), + sampler(), + num_warmup(0), + num_samples(0), + num_thin(1), + refresh(0), + n_chain(3), + save_warmup(false) { + rng.clear(); + for (int i = 0; i < 3; ++i) { + rng[i] = std::move(stan::services::util::create_rng(0, 1)); + sampler.push_back(model_t(model, rng[i])); + sample_writer.push_back(stan::test::unit::instrumented_writer{}); + diagnostic_writer.push_back(stan::test::unit::instrumented_writer{}); + } + } + + std::stringstream model_log; + stan::io::empty_var_context context; + stan_model model; + std::vector> cont_vector; + std::vector rng; + stan::test::unit::instrumented_interrupt interrupt; + std::vector sample_writer, diagnostic_writer; + stan::test::unit::instrumented_logger logger; + std::vector sampler; + int num_warmup, num_samples, num_thin, refresh, n_chain; + bool save_warmup; +}; + +TEST_F(ServicesUtil, all_zero) { + stan::services::util::run_adaptive_sampler( + sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + EXPECT_EQ(0, interrupt.call_count()); + + EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ(logger.call_count(), logger.call_count_info()) + << "No other calls to logger"; + + EXPECT_EQ(8, sample_writer[0].call_count()); + EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) + << "adaptation info + elapsed time"; + EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; + + EXPECT_EQ(6, diagnostic_writer[0].call_count()); + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; + +} + +TEST_F(ServicesUtil, num_warmup_no_save) { + num_warmup = 1000; + stan::services::util::run_adaptive_sampler( + sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + EXPECT_EQ(num_warmup * 3, interrupt.call_count()); + + EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ(logger.call_count(), logger.call_count_info()) + << "No other calls to logger"; + + EXPECT_EQ(8, sample_writer[0].call_count()); + EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) + << "adaptation info + elapsed time"; + EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; + + EXPECT_EQ(6, diagnostic_writer[0].call_count()); + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; +} + +TEST_F(ServicesUtil, num_warmup_save) { + num_warmup = 1000; + save_warmup = true; + stan::services::util::run_adaptive_sampler( + sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + EXPECT_EQ((num_warmup) * 3, interrupt.call_count()); + + EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ(logger.call_count(), logger.call_count_info()) + << "No other calls to logger"; + + EXPECT_EQ(num_warmup + 8, sample_writer[0].call_count()); + EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) + << "adaptation info + elapsed time"; + EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ(num_warmup, sample_writer[0].call_count("vector_double")) + << "warmup draws"; + + EXPECT_EQ(num_warmup + 6, diagnostic_writer[0].call_count()); + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ(num_warmup, diagnostic_writer[0].call_count("vector_double")) + << "warmup draws"; +} + +TEST_F(ServicesUtil, num_samples) { + num_samples = 1000; + stan::services::util::run_adaptive_sampler( + sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + EXPECT_EQ(num_samples * 3, interrupt.call_count()); + + EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ(logger.call_count(), logger.call_count_info()) + << "No other calls to logger"; + + EXPECT_EQ(num_samples + 8, sample_writer[0].call_count()); + EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) + << "adaptation info + elapsed time"; + EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ(num_samples, sample_writer[0].call_count("vector_double")) + << "num_samples draws"; + + EXPECT_EQ(num_samples + 6, diagnostic_writer[0].call_count()); + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ(num_samples, sample_writer[0].call_count("vector_double")) + << "num_samples draws"; +} + +TEST_F(ServicesUtil, num_warmup_save_num_samples_num_thin) { + num_warmup = 500; + save_warmup = true; + num_samples = 500; + num_thin = 10; + stan::services::util::run_adaptive_sampler( + sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + EXPECT_EQ((num_warmup + num_samples) * 3, interrupt.call_count()); + + EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ(logger.call_count(), logger.call_count_info()) + << "No other calls to logger"; + + EXPECT_EQ((num_warmup + num_samples) / num_thin + 8, + sample_writer[0].call_count()); + EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ((num_warmup + num_samples) / num_thin, + sample_writer[0].call_count("vector_double")) + << "thinned warmup and draws"; + + EXPECT_EQ((num_warmup + num_samples) / num_thin + 6, + diagnostic_writer[0].call_count()); + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ((num_warmup + num_samples) / num_thin, + diagnostic_writer[0].call_count("vector_double")) + << "thinned warmup and draws"; +} + +TEST_F(ServicesUtil, num_warmup_num_samples_refresh) { + num_warmup = 500; + num_samples = 500; + refresh = 10; + stan::services::util::run_adaptive_sampler( + sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + EXPECT_EQ((num_warmup + num_samples) * 3, interrupt.call_count()); + + EXPECT_EQ((num_warmup * 3 + num_samples * 3) / refresh + (2 + 3 + 2) * 3, + logger.call_count()) + << "Writes 1 to start warmup, 1 to start post-warmup, and " + << "(num_warmup + num_samples) / refresh, then the elapsed time"; + EXPECT_EQ(logger.call_count(), logger.call_count_info()) + << "No other calls to logger"; + + EXPECT_EQ(num_samples + 8, sample_writer[0].call_count()); + EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ(num_samples, sample_writer[0].call_count("vector_double")) << "draws"; + + EXPECT_EQ(num_samples + 6, diagnostic_writer[0].call_count()); + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; + EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; + EXPECT_EQ(num_samples, diagnostic_writer[0].call_count("vector_double")) + << "draws"; +} From a4c9679f3f2b8efd16360ccd117527ef18980ea5 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Sun, 21 Mar 2021 18:23:32 +0000 Subject: [PATCH 05/12] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../services/util/run_adaptive_sampler.hpp | 11 +++-- .../unit/services/instrumented_callbacks.hpp | 28 ++++++------- .../run_adaptive_sampler_parallel_test.cpp | 42 +++++++++++-------- 3 files changed, 44 insertions(+), 37 deletions(-) diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index d27225a3c6a..042662fe81e 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -128,8 +128,8 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, tbb::parallel_for( tbb::blocked_range(0, n_chain, 1), [num_warmup, num_samples, num_thin, refresh, save_warmup, &samples, - &warm_delta_v, &sample_delta_v, &writers, &samplers, &model, &cont_vectors, &rngs, - &interrupt, &logger, &sample_writers, + &warm_delta_v, &sample_delta_v, &writers, &samplers, &model, + &cont_vectors, &rngs, &interrupt, &logger, &sample_writers, &diagnostic_writers](const tbb::blocked_range& r) { for (size_t i = r.begin(); i != r.end(); ++i) { auto&& writer = writers[i]; @@ -170,10 +170,9 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, } }, tbb::simple_partitioner()); - for (int i = 0; i < n_chain; ++i) { - writers[i].write_timing(warm_delta_v[i], sample_delta_v[i]); - } - + for (int i = 0; i < n_chain; ++i) { + writers[i].write_timing(warm_delta_v[i], sample_delta_v[i]); + } } } // namespace util diff --git a/src/test/unit/services/instrumented_callbacks.hpp b/src/test/unit/services/instrumented_callbacks.hpp index a0f866aef08..0c47312df30 100644 --- a/src/test/unit/services/instrumented_callbacks.hpp +++ b/src/test/unit/services/instrumented_callbacks.hpp @@ -103,33 +103,33 @@ class instrumented_writer : public stan::callbacks::writer { 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; }; @@ -137,13 +137,13 @@ class instrumented_writer : public stan::callbacks::writer { 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::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; }; diff --git a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp index ed52f2a9ec4..48324624148 100644 --- a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp +++ b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp @@ -9,6 +9,7 @@ class ServicesUtil : public testing::Test { using model_t = stan::mcmc::adapt_unit_e_nuts; + public: ServicesUtil() : model(context, 0, &model_log), @@ -21,13 +22,13 @@ class ServicesUtil : public testing::Test { refresh(0), n_chain(3), save_warmup(false) { - rng.clear(); - for (int i = 0; i < 3; ++i) { - rng[i] = std::move(stan::services::util::create_rng(0, 1)); - sampler.push_back(model_t(model, rng[i])); - sample_writer.push_back(stan::test::unit::instrumented_writer{}); - diagnostic_writer.push_back(stan::test::unit::instrumented_writer{}); - } + rng.clear(); + for (int i = 0; i < 3; ++i) { + rng[i] = std::move(stan::services::util::create_rng(0, 1)); + sampler.push_back(model_t(model, rng[i])); + sample_writer.push_back(stan::test::unit::instrumented_writer{}); + diagnostic_writer.push_back(stan::test::unit::instrumented_writer{}); + } } std::stringstream model_log; @@ -36,7 +37,8 @@ class ServicesUtil : public testing::Test { std::vector> cont_vector; std::vector rng; stan::test::unit::instrumented_interrupt interrupt; - std::vector sample_writer, diagnostic_writer; + std::vector sample_writer, + diagnostic_writer; stan::test::unit::instrumented_logger logger; std::vector sampler; int num_warmup, num_samples, num_thin, refresh, n_chain; @@ -60,10 +62,10 @@ TEST_F(ServicesUtil, all_zero) { EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; EXPECT_EQ(6, diagnostic_writer[0].call_count()); - EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) + << "header line"; EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; - } TEST_F(ServicesUtil, num_warmup_no_save) { @@ -84,7 +86,8 @@ TEST_F(ServicesUtil, num_warmup_no_save) { EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; EXPECT_EQ(6, diagnostic_writer[0].call_count()); - EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) + << "header line"; EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; } @@ -95,7 +98,7 @@ TEST_F(ServicesUtil, num_warmup_save) { stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); - EXPECT_EQ((num_warmup) * 3, interrupt.call_count()); + EXPECT_EQ((num_warmup)*3, interrupt.call_count()); EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) @@ -110,7 +113,8 @@ TEST_F(ServicesUtil, num_warmup_save) { << "warmup draws"; EXPECT_EQ(num_warmup + 6, diagnostic_writer[0].call_count()); - EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) + << "header line"; EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; EXPECT_EQ(num_warmup, diagnostic_writer[0].call_count("vector_double")) @@ -137,7 +141,8 @@ TEST_F(ServicesUtil, num_samples) { << "num_samples draws"; EXPECT_EQ(num_samples + 6, diagnostic_writer[0].call_count()); - EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) + << "header line"; EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; EXPECT_EQ(num_samples, sample_writer[0].call_count("vector_double")) @@ -169,7 +174,8 @@ TEST_F(ServicesUtil, num_warmup_save_num_samples_num_thin) { EXPECT_EQ((num_warmup + num_samples) / num_thin + 6, diagnostic_writer[0].call_count()); - EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) + << "header line"; EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; EXPECT_EQ((num_warmup + num_samples) / num_thin, @@ -197,10 +203,12 @@ TEST_F(ServicesUtil, num_warmup_num_samples_refresh) { EXPECT_EQ(1, sample_writer[0].call_count("vector_string")) << "header line"; EXPECT_EQ(2 + 3, sample_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, sample_writer[0].call_count("empty")) << "blank lines"; - EXPECT_EQ(num_samples, sample_writer[0].call_count("vector_double")) << "draws"; + EXPECT_EQ(num_samples, sample_writer[0].call_count("vector_double")) + << "draws"; EXPECT_EQ(num_samples + 6, diagnostic_writer[0].call_count()); - EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) << "header line"; + EXPECT_EQ(1, diagnostic_writer[0].call_count("vector_string")) + << "header line"; EXPECT_EQ(3, diagnostic_writer[0].call_count("string")) << "elapsed time"; EXPECT_EQ(2, diagnostic_writer[0].call_count("empty")) << "blank lines"; EXPECT_EQ(num_samples, diagnostic_writer[0].call_count("vector_double")) From 04836ef2bd7f4055b1fd63e581bbc72c54fa9380 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Sun, 21 Mar 2021 14:43:07 -0400 Subject: [PATCH 06/12] include mutex --- src/test/unit/services/instrumented_callbacks.hpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/test/unit/services/instrumented_callbacks.hpp b/src/test/unit/services/instrumented_callbacks.hpp index 0c47312df30..797959ef0ee 100644 --- a/src/test/unit/services/instrumented_callbacks.hpp +++ b/src/test/unit/services/instrumented_callbacks.hpp @@ -10,6 +10,7 @@ #include #include #include +#include namespace stan { namespace test { namespace unit { From 0608601e01c3c2fb9788ae36619ea1c649949f68 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Sun, 21 Mar 2021 14:54:15 -0400 Subject: [PATCH 07/12] make stream_writer not final --- src/stan/callbacks/stream_writer.hpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/stan/callbacks/stream_writer.hpp b/src/stan/callbacks/stream_writer.hpp index 226f16a76e1..6519531c0ac 100644 --- a/src/stan/callbacks/stream_writer.hpp +++ b/src/stan/callbacks/stream_writer.hpp @@ -13,7 +13,7 @@ namespace callbacks { * stream_writer is an implementation * of writer that writes to a stream. */ -class stream_writer final : public writer { +class stream_writer : public writer { public: /** * Constructs a stream writer with an output stream From 3ebbc5caf42485edb8ddfb38a51f4fe4ac8ffba9 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Sun, 21 Mar 2021 15:29:31 -0400 Subject: [PATCH 08/12] init threadpool --- .../unit/services/util/run_adaptive_sampler_parallel_test.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp index 48324624148..2fd2d91ddaf 100644 --- a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp +++ b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp @@ -7,6 +7,8 @@ #include #include +auto&& blah = stan::math::init_threadpool_tbb(); + class ServicesUtil : public testing::Test { using model_t = stan::mcmc::adapt_unit_e_nuts; From a3bf12b36249cf82a4b81e4a06ad58f159f14638 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 24 Mar 2021 11:25:26 -0400 Subject: [PATCH 09/12] update generate_transitions and cleanup run_adaptive_sampler --- .../services/util/generate_transitions.hpp | 3 +- .../services/util/run_adaptive_sampler.hpp | 41 ++++++++----------- 2 files changed, 19 insertions(+), 25 deletions(-) diff --git a/src/stan/services/util/generate_transitions.hpp b/src/stan/services/util/generate_transitions.hpp index 53261a33435..8ad93706c6a 100644 --- a/src/stan/services/util/generate_transitions.hpp +++ b/src/stan/services/util/generate_transitions.hpp @@ -51,8 +51,7 @@ void generate_transitions(stan::mcmc::base_mcmc& sampler, int num_iterations, if (refresh > 0 && (start + m + 1 == finish || m == 0 || (m + 1) % refresh == 0)) { int it_print_width = std::ceil(std::log10(static_cast(finish))); - std::string mes; - std::stringstream message(mes, std::ios_base::in); + std::stringstream message; if (n_chain > 0) { message << "Chain [" << (n_chain + 1) << "]"; } diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index 042662fe81e..001d4cc9c68 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -100,18 +100,18 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, std::vector& sample_writers, std::vector& diagnostic_writers, size_t n_chain) { - std::vector writers; - writers.reserve(n_chain); + if (n_chain == 0) { + run_adaptive_sampler(samplers[0], model, cont_vectors[0], num_warmup, num_samples, + num_thin, refresh, save_warmup, rngs[0], interrupt, logger, sample_writers[0], + diagnostic_writers[0]); + } std::vector samples; samples.reserve(n_chain); - for (int i = 0; i < n_chain; ++i) { - auto&& sample_writer = sample_writers[i]; - auto&& diagnostic_writer = diagnostic_writers[i]; auto&& sampler = samplers[i]; - Eigen::Map cont_params(cont_vectors[i].data(), - cont_vectors[i].size()); sampler.engage_adaptation(); + Eigen::Map cont_params(cont_vectors[i].data(), + cont_vectors[i].size()); try { sampler.z().q = cont_params; sampler.init_stepsize(logger); @@ -120,19 +120,17 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, logger.info(e.what()); return; } - writers.emplace_back(sample_writer, diagnostic_writer, logger); samples.emplace_back(cont_params, 0, 0); } - std::vector warm_delta_v(n_chain, 0); - std::vector sample_delta_v(n_chain, 0); tbb::parallel_for( tbb::blocked_range(0, n_chain, 1), [num_warmup, num_samples, num_thin, refresh, save_warmup, &samples, - &warm_delta_v, &sample_delta_v, &writers, &samplers, &model, - &cont_vectors, &rngs, &interrupt, &logger, &sample_writers, + &samplers, &model, + &rngs, &interrupt, &logger, &sample_writers, &diagnostic_writers](const tbb::blocked_range& r) { for (size_t i = r.begin(); i != r.end(); ++i) { - auto&& writer = writers[i]; + auto&& sample_writer = sample_writers[i]; + auto writer = services::util::mcmc_writer(sample_writer, diagnostic_writers[i], logger); auto&& sampler = samplers[i]; auto&& samp = samples[i]; @@ -140,39 +138,36 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, writer.write_sample_names(samp, sampler, model); writer.write_diagnostic_names(samp, sampler, model); - auto start_warm = std::chrono::steady_clock::now(); + const 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, samp, model, rngs[i], interrupt, logger, i); - auto end_warm = std::chrono::steady_clock::now(); - warm_delta_v[i] + const auto end_warm = std::chrono::steady_clock::now(); + auto warm_delta = std::chrono::duration_cast( end_warm - start_warm) .count() / 1000.0; sampler.disengage_adaptation(); - auto&& sample_writer = sample_writers[i]; writer.write_adapt_finish(sampler); sampler.write_sampler_state(sample_writer); - auto start_sample = std::chrono::steady_clock::now(); + const 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, samp, model, rngs[i], interrupt, logger, i); - auto end_sample = std::chrono::steady_clock::now(); - sample_delta_v[i] + const auto end_sample = std::chrono::steady_clock::now(); + auto sample_delta = std::chrono::duration_cast( end_sample - start_sample) .count() / 1000.0; + writer.write_timing(warm_delta, sample_delta); } }, tbb::simple_partitioner()); - for (int i = 0; i < n_chain; ++i) { - writers[i].write_timing(warm_delta_v[i], sample_delta_v[i]); - } } } // namespace util From 5c7b0bddd3b60b722ed245c420f07ef211ea31c3 Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Wed, 24 Mar 2021 16:21:38 +0000 Subject: [PATCH 10/12] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- src/stan/services/util/run_adaptive_sampler.hpp | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index 001d4cc9c68..ef325eb807b 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -101,9 +101,10 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, std::vector& diagnostic_writers, size_t n_chain) { if (n_chain == 0) { - run_adaptive_sampler(samplers[0], model, cont_vectors[0], num_warmup, num_samples, - num_thin, refresh, save_warmup, rngs[0], interrupt, logger, sample_writers[0], - diagnostic_writers[0]); + run_adaptive_sampler(samplers[0], model, cont_vectors[0], num_warmup, + num_samples, num_thin, refresh, save_warmup, rngs[0], + interrupt, logger, sample_writers[0], + diagnostic_writers[0]); } std::vector samples; samples.reserve(n_chain); @@ -111,7 +112,7 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, auto&& sampler = samplers[i]; sampler.engage_adaptation(); Eigen::Map cont_params(cont_vectors[i].data(), - cont_vectors[i].size()); + cont_vectors[i].size()); try { sampler.z().q = cont_params; sampler.init_stepsize(logger); @@ -125,12 +126,12 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, tbb::parallel_for( tbb::blocked_range(0, n_chain, 1), [num_warmup, num_samples, num_thin, refresh, save_warmup, &samples, - &samplers, &model, - &rngs, &interrupt, &logger, &sample_writers, + &samplers, &model, &rngs, &interrupt, &logger, &sample_writers, &diagnostic_writers](const tbb::blocked_range& r) { for (size_t i = r.begin(); i != r.end(); ++i) { auto&& sample_writer = sample_writers[i]; - auto writer = services::util::mcmc_writer(sample_writer, diagnostic_writers[i], logger); + auto writer = services::util::mcmc_writer( + sample_writer, diagnostic_writers[i], logger); auto&& sampler = samplers[i]; auto&& samp = samples[i]; From ebeb1f9b1e801b28e5f3418388ca706db21274d3 Mon Sep 17 00:00:00 2001 From: Steve Bronder Date: Wed, 24 Mar 2021 18:25:17 -0400 Subject: [PATCH 11/12] remove statics from softmax metric and cleanup tests --- .../mcmc/hmc/hamiltonians/softabs_metric.hpp | 12 ++--- .../services/util/run_adaptive_sampler.hpp | 36 +++++++-------- .../run_adaptive_sampler_parallel_test.cpp | 44 ++++++++++--------- 3 files changed, 44 insertions(+), 48 deletions(-) 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/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index ef325eb807b..908116d281a 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -106,34 +106,28 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, interrupt, logger, sample_writers[0], diagnostic_writers[0]); } - std::vector samples; - samples.reserve(n_chain); - for (int i = 0; i < n_chain; ++i) { - auto&& sampler = samplers[i]; - sampler.engage_adaptation(); - Eigen::Map cont_params(cont_vectors[i].data(), - cont_vectors[i].size()); - try { - sampler.z().q = cont_params; - sampler.init_stepsize(logger); - } catch (const std::exception& e) { - logger.info("Exception initializing step size."); - logger.info(e.what()); - return; - } - samples.emplace_back(cont_params, 0, 0); - } tbb::parallel_for( tbb::blocked_range(0, n_chain, 1), - [num_warmup, num_samples, num_thin, refresh, save_warmup, &samples, - &samplers, &model, &rngs, &interrupt, &logger, &sample_writers, + [num_warmup, num_samples, num_thin, refresh, save_warmup, + &samplers, &model, &rngs, &interrupt, &logger, &sample_writers, &cont_vectors, &diagnostic_writers](const tbb::blocked_range& r) { for (size_t i = r.begin(); i != r.end(); ++i) { + auto&& sampler = samplers[i]; + sampler.engage_adaptation(); + Eigen::Map cont_params(cont_vectors[i].data(), + cont_vectors[i].size()); + try { + sampler.z().q = cont_params; + sampler.init_stepsize(logger); + } catch (const std::exception& e) { + logger.info("Exception initializing step size."); + logger.info(e.what()); + return; + } + stan::mcmc::sample samp(cont_params, 0, 0); auto&& sample_writer = sample_writers[i]; auto writer = services::util::mcmc_writer( sample_writer, diagnostic_writers[i], logger); - auto&& sampler = samplers[i]; - auto&& samp = samples[i]; // Headers writer.write_sample_names(samp, sampler, model); diff --git a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp index 2fd2d91ddaf..55b710f20f3 100644 --- a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp +++ b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp @@ -9,23 +9,25 @@ auto&& blah = stan::math::init_threadpool_tbb(); +static constexpr size_t num_chains = 5; + class ServicesUtil : public testing::Test { using model_t = stan::mcmc::adapt_unit_e_nuts; public: ServicesUtil() : model(context, 0, &model_log), - rng(3), - cont_vector(3, std::vector{0, 0}), + rng(num_chains), + cont_vector(num_chains, std::vector{0, 0}), sampler(), num_warmup(0), num_samples(0), num_thin(1), refresh(0), - n_chain(3), + n_chain(num_chains), save_warmup(false) { rng.clear(); - for (int i = 0; i < 3; ++i) { + for (int i = 0; i < num_chains; ++i) { rng[i] = std::move(stan::services::util::create_rng(0, 1)); sampler.push_back(model_t(model, rng[i])); sample_writer.push_back(stan::test::unit::instrumented_writer{}); @@ -50,10 +52,10 @@ class ServicesUtil : public testing::Test { TEST_F(ServicesUtil, all_zero) { stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); EXPECT_EQ(0, interrupt.call_count()); - EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -74,10 +76,10 @@ TEST_F(ServicesUtil, num_warmup_no_save) { num_warmup = 1000; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); - EXPECT_EQ(num_warmup * 3, interrupt.call_count()); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + EXPECT_EQ(num_warmup * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -99,10 +101,10 @@ TEST_F(ServicesUtil, num_warmup_save) { save_warmup = true; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); - EXPECT_EQ((num_warmup)*3, interrupt.call_count()); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + EXPECT_EQ((num_warmup) * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -127,10 +129,10 @@ TEST_F(ServicesUtil, num_samples) { num_samples = 1000; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); - EXPECT_EQ(num_samples * 3, interrupt.call_count()); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + EXPECT_EQ(num_samples * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -158,10 +160,10 @@ TEST_F(ServicesUtil, num_warmup_save_num_samples_num_thin) { num_thin = 10; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); - EXPECT_EQ((num_warmup + num_samples) * 3, interrupt.call_count()); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * 3, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -191,10 +193,10 @@ TEST_F(ServicesUtil, num_warmup_num_samples_refresh) { refresh = 10; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, 3); - EXPECT_EQ((num_warmup + num_samples) * 3, interrupt.call_count()); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count()); - EXPECT_EQ((num_warmup * 3 + num_samples * 3) / refresh + (2 + 3 + 2) * 3, + EXPECT_EQ((num_warmup * num_chains + num_samples * num_chains) / refresh + (2 + 3 + 2) * num_chains, logger.call_count()) << "Writes 1 to start warmup, 1 to start post-warmup, and " << "(num_warmup + num_samples) / refresh, then the elapsed time"; From 028bb51db71ec730ff9ab33578713f823fc21a1d Mon Sep 17 00:00:00 2001 From: Stan Jenkins Date: Thu, 25 Mar 2021 00:17:12 +0000 Subject: [PATCH 12/12] [Jenkins] auto-formatting by clang-format version 6.0.0-1ubuntu2~16.04.1 (tags/RELEASE_600/final) --- .../services/util/run_adaptive_sampler.hpp | 4 +- .../run_adaptive_sampler_parallel_test.cpp | 38 ++++++++++++------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/src/stan/services/util/run_adaptive_sampler.hpp b/src/stan/services/util/run_adaptive_sampler.hpp index 908116d281a..dc2105b290a 100644 --- a/src/stan/services/util/run_adaptive_sampler.hpp +++ b/src/stan/services/util/run_adaptive_sampler.hpp @@ -108,8 +108,8 @@ void run_adaptive_sampler(std::vector& samplers, Model& model, } tbb::parallel_for( tbb::blocked_range(0, n_chain, 1), - [num_warmup, num_samples, num_thin, refresh, save_warmup, - &samplers, &model, &rngs, &interrupt, &logger, &sample_writers, &cont_vectors, + [num_warmup, num_samples, num_thin, refresh, save_warmup, &samplers, + &model, &rngs, &interrupt, &logger, &sample_writers, &cont_vectors, &diagnostic_writers](const tbb::blocked_range& r) { for (size_t i = r.begin(); i != r.end(); ++i) { auto&& sampler = samplers[i]; diff --git a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp index 55b710f20f3..5d483864b50 100644 --- a/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp +++ b/src/test/unit/services/util/run_adaptive_sampler_parallel_test.cpp @@ -52,10 +52,12 @@ class ServicesUtil : public testing::Test { TEST_F(ServicesUtil, all_zero) { stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, + num_chains); EXPECT_EQ(0, interrupt.call_count()); - EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) + << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -76,10 +78,12 @@ TEST_F(ServicesUtil, num_warmup_no_save) { num_warmup = 1000; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, + num_chains); EXPECT_EQ(num_warmup * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) + << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -101,10 +105,12 @@ TEST_F(ServicesUtil, num_warmup_save) { save_warmup = true; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); - EXPECT_EQ((num_warmup) * num_chains, interrupt.call_count()); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, + num_chains); + EXPECT_EQ((num_warmup)*num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) + << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -129,10 +135,12 @@ TEST_F(ServicesUtil, num_samples) { num_samples = 1000; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, + num_chains); EXPECT_EQ(num_samples * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) + << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -160,10 +168,12 @@ TEST_F(ServicesUtil, num_warmup_save_num_samples_num_thin) { num_thin = 10; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, + num_chains); EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count()); - EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) << "Writes the elapsed time"; + EXPECT_EQ((3 + 2) * num_chains, logger.call_count()) + << "Writes the elapsed time"; EXPECT_EQ(logger.call_count(), logger.call_count_info()) << "No other calls to logger"; @@ -193,10 +203,12 @@ TEST_F(ServicesUtil, num_warmup_num_samples_refresh) { refresh = 10; stan::services::util::run_adaptive_sampler( sampler, model, cont_vector, num_warmup, num_samples, num_thin, refresh, - save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, num_chains); + save_warmup, rng, interrupt, logger, sample_writer, diagnostic_writer, + num_chains); EXPECT_EQ((num_warmup + num_samples) * num_chains, interrupt.call_count()); - EXPECT_EQ((num_warmup * num_chains + num_samples * num_chains) / refresh + (2 + 3 + 2) * num_chains, + EXPECT_EQ((num_warmup * num_chains + num_samples * num_chains) / refresh + + (2 + 3 + 2) * num_chains, logger.call_count()) << "Writes 1 to start warmup, 1 to start post-warmup, and " << "(num_warmup + num_samples) / refresh, then the elapsed time";