Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adds option to turn off psis resampling and lp calculation for pathfinder #3249

Merged
merged 22 commits into from
Jan 8, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
53790e3
Adds option to turn off psis resampling and lp calculation for pathfi…
SteveBronder Jan 2, 2024
ef527c9
cpplint
SteveBronder Jan 2, 2024
f2369ae
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 2, 2024
b7c9fe7
Merge remote-tracking branch 'origin/develop' into feature/no-psis-or…
SteveBronder Jan 3, 2024
b7d44e8
update with review
SteveBronder Jan 3, 2024
26b1656
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 3, 2024
93c3958
fix typo in vals vs values for unique_stream_writer
SteveBronder Jan 4, 2024
d69a2df
add virtual overloads for vector and row vector printing for unique_s…
SteveBronder Jan 4, 2024
1e776fa
Remove array from writer overloads and have pathfinder only write wit…
SteveBronder Jan 5, 2024
5c8aa63
Merge commit '7e9342f13e9b71e5aac2040a90c59aa56fd12842' into HEAD
yashikno Jan 5, 2024
6e7c832
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 5, 2024
ed0449a
add signatures for blocks
SteveBronder Jan 5, 2024
e14b330
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 5, 2024
eb1d865
names for writer block arguments
SteveBronder Jan 5, 2024
1284bb1
use Ref instead of multiple overloads for writer
SteveBronder Jan 5, 2024
de22ba4
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 5, 2024
62d154c
Remove logic for row vector
SteveBronder Jan 5, 2024
eb11a48
Remove logic for row vector
SteveBronder Jan 5, 2024
6892ccb
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 5, 2024
6b4932c
Use {} instead of () for CommaFormat defaults in unique_stream_writer
SteveBronder Jan 5, 2024
08251a2
[Jenkins] auto-formatting by clang-format version 10.0.0-4ubuntu1
stan-buildbot Jan 5, 2024
1ac35a7
remove old test code
SteveBronder Jan 5, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
83 changes: 82 additions & 1 deletion src/stan/callbacks/unique_stream_writer.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,69 @@ class unique_stream_writer final : public writer {
return;
write_vector(values);
}
/**
* 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 an Eigen vector
*/
void operator()(const Eigen::Matrix<double, -1, 1>& values) {
if (output_ == nullptr)
return;
Eigen::IOFormat CommaInitFmt(Eigen::StreamPrecision, Eigen::DontAlignCols,
", ", "", "", "\n", "", "");
WardBrian marked this conversation as resolved.
Show resolved Hide resolved
*output_ << values.transpose().format(CommaInitFmt);
}

/**
* 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 an Eigen row vector
*/
void operator()(const Eigen::Matrix<double, 1, -1>& values) {
if (output_ == nullptr)
return;
Eigen::IOFormat CommaInitFmt(Eigen::StreamPrecision, Eigen::DontAlignCols,
", ", "", "", "\n", "", "");
*output_ << values.format(CommaInitFmt);
}

/**
* 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 an Eigen column array
*/
void operator()(const Eigen::Array<double, -1, 1>& values) {
if (output_ == nullptr)
return;
Eigen::IOFormat CommaInitFmt(Eigen::StreamPrecision, Eigen::DontAlignCols,
", ", "", "", "\n", "", "");
*output_ << values.transpose().format(CommaInitFmt);
}

/**
* 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 an Eigen row array
*/
void operator()(const Eigen::Array<double, 1, -1>& values) {
if (output_ == nullptr)
return;
Eigen::IOFormat CommaInitFmt(Eigen::StreamPrecision, Eigen::DontAlignCols,
", ", "", "", "\n", "", "");
*output_ << values.format(CommaInitFmt);
}

/**
* Writes multiple rows and columns of values in csv format.
Expand All @@ -88,7 +151,25 @@ class unique_stream_writer final : public writer {
* parameters in the rows and samples in the columns. The matrix is then
* transposed for the output.
*/
void operator()(const Eigen::MatrixXd& values) {
void operator()(const Eigen::Matrix<double, -1, -1>& values) {
if (output_ == nullptr)
return;
Eigen::IOFormat CommaInitFmt(Eigen::StreamPrecision, Eigen::DontAlignCols,
", ", "", "", "\n", "", "");
*output_ << values.transpose().format(CommaInitFmt);
}

/**
* Writes multiple rows and columns of values in csv format.
*
* Note: the precision of the output is determined by the settings
* of the stream on construction.
*
* @param[in] values An array of values. The input is expected to have
* parameters in the rows and samples in the columns. The array is then
* transposed for the output.
*/
void operator()(const Eigen::Array<double, -1, -1>& values) {
WardBrian marked this conversation as resolved.
Show resolved Hide resolved
if (output_ == nullptr)
return;
Eigen::IOFormat CommaInitFmt(Eigen::StreamPrecision, Eigen::DontAlignCols,
Expand Down
52 changes: 52 additions & 0 deletions src/stan/callbacks/writer.hpp
WardBrian marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,58 @@ class writer {
* transposed for the output.
*/
virtual void operator()(const Eigen::MatrixXd& values) {}

/**
* Writes multiple rows and columns of values in csv format.
*
* Note: the precision of the output is determined by the settings
* of the stream on construction.
*
* @param[in] values An array of values. The input is expected to have
* parameters in the rows and samples in the columns. The array is then
* transposed for the output.
*/
virtual void operator()(const Eigen::Array<double, -1, -1>& vals) {}

/**
* 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 an Eigen vector
*/
virtual void operator()(const Eigen::Matrix<double, -1, 1>& values) {}

/**
* 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 an Eigen row vector
*/
virtual void operator()(const Eigen::Matrix<double, 1, -1>& values) {}

/**
* 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 an Eigen column array
*/
virtual void operator()(const Eigen::Array<double, -1, 1>& values) {}

/**
* 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 an Eigen row array
*/
virtual void operator()(const Eigen::Array<double, 1, -1>& values) {}
};

} // namespace callbacks
Expand Down
101 changes: 64 additions & 37 deletions src/stan/services/pathfinder/multi.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,16 @@ namespace pathfinder {
* @param[in,out] parameter_writer output for parameter values
* @param[in,out] diagnostic_writer output for diagnostics values,
* `error_codes::SOFTWARE` for failures
* @param[in] calculate_lp Whether single pathfinder should return lp
* calculations. If `true`, calculates the joint log probability for each
* sample. If `false`, (`num_draws` - `num_elbo_draws`) of the joint log
* probability calculations will be `NA` and psis resampling will not be
* performed.
* @param[in] psis_resampling If `true`, psis resampling is performed over the
* samples returned by all of the individual pathfinders and `num_multi_draws`
* samples are written to `parameter_writer`. If `false`, no psis resampling is
* performed and (`num_paths` * `num_draws`) samples are written to
* `parameter_writer`.
* @return error_codes::OK if successful
*/
template <class Model, typename InitContext, typename InitWriter,
Expand All @@ -92,7 +102,8 @@ inline int pathfinder_lbfgs_multi(
callbacks::logger& logger, InitWriter&& init_writers,
std::vector<SingleParamWriter>& single_path_parameter_writer,
std::vector<SingleDiagnosticWriter>& single_path_diagnostic_writer,
ParamWriter& parameter_writer, DiagnosticWriter& diagnostic_writer) {
ParamWriter& parameter_writer, DiagnosticWriter& diagnostic_writer,
bool calculate_lp = true, bool psis_resample = true) {
const auto start_pathfinders_time = std::chrono::steady_clock::now();
std::vector<std::string> param_names;
param_names.push_back("lp_approx__");
Expand All @@ -117,7 +128,7 @@ inline int pathfinder_lbfgs_multi(
num_elbo_draws, num_draws, save_iterations, refresh,
interrupt, logger, init_writers[iter],
single_path_parameter_writer[iter],
single_path_diagnostic_writer[iter]);
single_path_diagnostic_writer[iter], calculate_lp);
if (unlikely(std::get<0>(pathfinder_ret) != error_codes::OK)) {
logger.error(std::string("Pathfinder iteration: ")
+ std::to_string(iter) + " failed.");
Expand Down Expand Up @@ -158,53 +169,69 @@ inline int pathfinder_lbfgs_multi(
for (auto&& ilpr : individual_lp_ratios) {
num_returned_samples += ilpr.size();
}
Eigen::Array<double, Eigen::Dynamic, 1> lp_ratios(num_returned_samples);
// Rows are individual parameters and columns are samples per iteration
Eigen::Array<double, Eigen::Dynamic, Eigen::Dynamic> samples(
individual_samples[0].rows(), num_returned_samples);
Eigen::Index filling_start_row = 0;
for (size_t i = 0; i < successful_pathfinders; ++i) {
const Eigen::Index individ_num_samples = individual_lp_ratios[i].size();
lp_ratios.segment(filling_start_row, individ_num_samples)
= individual_lp_ratios[i];
const Eigen::Index individ_num_samples = individual_samples[i].cols();
samples.middleCols(filling_start_row, individ_num_samples)
= individual_samples[i];
filling_start_row += individ_num_samples;
}
const auto tail_len = std::min(0.2 * num_returned_samples,
3 * std::sqrt(num_returned_samples));
Eigen::Array<double, Eigen::Dynamic, 1> weight_vals
= stan::services::psis::psis_weights(lp_ratios, tail_len, logger);
boost::ecuyer1988 rng
= util::create_rng<boost::ecuyer1988>(random_seed, stride_id);
boost::variate_generator<
boost::ecuyer1988&,
boost::random::discrete_distribution<Eigen::Index, double>>
rand_psis_idx(rng,
boost::random::discrete_distribution<Eigen::Index, double>(
boost::iterator_range<double*>(
weight_vals.data(),
weight_vals.data() + weight_vals.size())));
for (size_t i = 0; i <= num_multi_draws - 1; ++i) {
parameter_writer(samples.col(rand_psis_idx()));
double psis_delta_time = 0;
if (psis_resample && calculate_lp) {
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
Eigen::Array<double, Eigen::Dynamic, 1> lp_ratios(num_returned_samples);
filling_start_row = 0;
for (size_t i = 0; i < successful_pathfinders; ++i) {
const Eigen::Index individ_num_samples = individual_lp_ratios[i].size();
lp_ratios.segment(filling_start_row, individ_num_samples)
= individual_lp_ratios[i];
filling_start_row += individ_num_samples;
}

const auto tail_len = std::min(0.2 * num_returned_samples,
3 * std::sqrt(num_returned_samples));
Eigen::Array<double, Eigen::Dynamic, 1> weight_vals
= stan::services::psis::psis_weights(lp_ratios, tail_len, logger);
boost::ecuyer1988 rng
= util::create_rng<boost::ecuyer1988>(random_seed, stride_id);
boost::variate_generator<
boost::ecuyer1988&,
boost::random::discrete_distribution<Eigen::Index, double>>
rand_psis_idx(
rng, boost::random::discrete_distribution<Eigen::Index, double>(
boost::iterator_range<double*>(
weight_vals.data(),
weight_vals.data() + weight_vals.size())));
for (size_t i = 0; i <= num_multi_draws - 1; ++i) {
parameter_writer(samples.col(rand_psis_idx()));
}
const auto end_psis_time = std::chrono::steady_clock::now();
psis_delta_time
= stan::services::util::duration_diff(start_psis_time, end_psis_time);

} else {
parameter_writer(samples);
}
const auto end_psis_time = std::chrono::steady_clock::now();
double psis_delta_time
= stan::services::util::duration_diff(start_psis_time, end_psis_time);
parameter_writer();
const auto time_header = std::string("Elapsed Time: ");
std::string optim_time_str = time_header
+ std::to_string(pathfinders_delta_time)
+ " seconds (Pathfinders)";
std::string optim_time_str
= time_header + std::to_string(pathfinders_delta_time)
+ std::string(" seconds")
+ ((psis_resample && calculate_lp) ? " (Pathfinders)" : " (Total)");
parameter_writer(optim_time_str);
std::string psis_time_str = std::string(time_header.size(), ' ')
+ std::to_string(psis_delta_time)
+ " seconds (PSIS)";
parameter_writer(psis_time_str);
std::string total_time_str
= std::string(time_header.size(), ' ')
+ std::to_string(pathfinders_delta_time + psis_delta_time)
+ " seconds (Total)";
parameter_writer(total_time_str);
if (psis_resample && calculate_lp) {
std::string psis_time_str = std::string(time_header.size(), ' ')
+ std::to_string(psis_delta_time)
+ " seconds (PSIS)";
parameter_writer(psis_time_str);
std::string total_time_str
= std::string(time_header.size(), ' ')
+ std::to_string(pathfinders_delta_time + psis_delta_time)
+ " seconds (Total)";
parameter_writer(total_time_str);
}
WardBrian marked this conversation as resolved.
Show resolved Hide resolved
parameter_writer();
return 0;
}
Expand Down