Skip to content

Commit

Permalink
Merge pull request #2434 from stan-dev/feature/2432-standalone-gq-ser…
Browse files Browse the repository at this point in the history
…vice

Feature/2432 standalone gq service
  • Loading branch information
Bob Carpenter authored Dec 8, 2017
2 parents b149386 + 66bc1e6 commit abd9e40
Show file tree
Hide file tree
Showing 18 changed files with 359,195 additions and 29 deletions.
4 changes: 4 additions & 0 deletions make/tests
Original file line number Diff line number Diff line change
Expand Up @@ -259,7 +259,11 @@ src/test/unit/services/sample/hmc_nuts_dense_inv_metric_test.cpp : src/test/test
src/test/unit/services/sample/hmc_nuts_diag_inv_metric_test.cpp : src/test/test-models/good/mcmc/hmc/common/gauss3D.hpp
src/test/unit/services/sample/hmc_static_dense_inv_metric_test.cpp : src/test/test-models/good/mcmc/hmc/common/gauss3D.hpp
src/test/unit/services/sample/hmc_static_diag_inv_metric_test.cpp : src/test/test-models/good/mcmc/hmc/common/gauss3D.hpp
src/test/unit/services/sample/standalone_gqs_test.cpp : src/test/test-models/good/services/test_gq.hpp
src/test/unit/services/sample/standalone_gqs_bad_test.cpp : src/test/test-models/good/services/test_gq2.hpp
src/test/unit/services/sample/standalone_gqs_big_test.cpp : src/test/test-models/good/services/bym2_offset_only.hpp
src/test/unit/services/util/generate_transitions_test.cpp: src/test/test-models/good/services/test_lp.hpp
src/test/unit/services/util/gq_writer_test.cpp : src/test/test-models/good/services/test_gq.hpp
src/test/unit/services/util/initialize_test.cpp: src/test/test-models/good/services/test_lp.hpp
src/test/unit/services/util/run_adaptive_sampler_test.cpp src/test/unit/services/util/run_sampler_test.cpp: src/test/test-models/good/services/test_lp.hpp
src/test/unit/services/util/mcmc_writer_test.cpp : src/test/test-models/good/services/test_lp.hpp
Expand Down
21 changes: 15 additions & 6 deletions src/stan/lang/generator/constrained_param_names_visgen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,20 @@ namespace stan {
* <code>param_names__</code>
*/
struct constrained_param_names_visgen : public visgen {
/**
* Indentation level.
*/
size_t indent_;

/**
* Construct a constrained parameter names generator for the
* specified stream.
*
* @param[in] indent level of indentation
* @param[in,out] o stream for generating
*/
explicit constrained_param_names_visgen(std::ostream& o) : visgen(o) { }
explicit constrained_param_names_visgen(size_t indent, std::ostream& o)
: visgen(o), indent_(indent) { }

/**
* Generate the parameter names for the specified parameter
Expand All @@ -41,29 +50,29 @@ namespace stan {
combo_dims.push_back(matrix_dims[i]);

for (size_t i = combo_dims.size(); i-- > 0; ) {
generate_indent(1 + combo_dims.size() - i, o_);
generate_indent(indent_ + combo_dims.size() - i, o_);
o_ << "for (int k_" << i << "__ = 1;"
<< " k_" << i << "__ <= ";
generate_expression(combo_dims[i].expr_, NOT_USER_FACING, o_);
o_ << "; ++k_" << i << "__) {" << EOL; // begin (1)
}

generate_indent(2 + combo_dims.size(), o_);
generate_indent(indent_ + 1 + combo_dims.size(), o_);
o_ << "param_name_stream__.str(std::string());" << EOL;

generate_indent(2 + combo_dims.size(), o_);
generate_indent(indent_ + 1 + combo_dims.size(), o_);
o_ << "param_name_stream__ << \"" << name << '"';

for (size_t i = 0; i < combo_dims.size(); ++i)
o_ << " << '.' << k_" << i << "__";
o_ << ';' << EOL;

generate_indent(2 + combo_dims.size(), o_);
generate_indent(indent_ + 1 + combo_dims.size(), o_);
o_ << "param_names__.push_back(param_name_stream__.str());" << EOL;

// end for loop dims
for (size_t i = 0; i < combo_dims.size(); ++i) {
generate_indent(1 + combo_dims.size() - i, o_);
generate_indent(indent_ + combo_dims.size() - i, o_);
o_ << "}" << EOL; // end (1)
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,19 @@ namespace stan {
<< " bool include_gqs__ = true) const {"
<< EOL << INDENT2
<< "std::stringstream param_name_stream__;" << EOL;
constrained_param_names_visgen vis(o);
constrained_param_names_visgen vis1(1, o);
constrained_param_names_visgen vis2(2, o);
for (size_t i = 0; i < prog.parameter_decl_.size(); ++i)
boost::apply_visitor(vis, prog.parameter_decl_[i].decl_);
o << EOL << INDENT2 << "if (!include_gqs__ && !include_tparams__) return;"
<< EOL;
boost::apply_visitor(vis1, prog.parameter_decl_[i].decl_);
o << EOL << INDENT2
<< "if (!include_gqs__ && !include_tparams__) return;" << EOL;
o << EOL << INDENT2 << "if (include_tparams__) {" << EOL;
for (size_t i = 0; i < prog.derived_decl_.first.size(); ++i)
boost::apply_visitor(vis, prog.derived_decl_.first[i].decl_);
boost::apply_visitor(vis2, prog.derived_decl_.first[i].decl_);
o << INDENT2 << "}" << EOL2;
o << EOL << INDENT2 << "if (!include_gqs__) return;" << EOL;
for (size_t i = 0; i < prog.generated_decl_.first.size(); ++i)
boost::apply_visitor(vis, prog.generated_decl_.first[i].decl_);
boost::apply_visitor(vis1, prog.generated_decl_.first[i].decl_);
o << INDENT << "}" << EOL2;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,16 +28,20 @@ namespace stan {
<< " bool include_gqs__ = true) const {"
<< EOL << INDENT2
<< "std::stringstream param_name_stream__;" << EOL;
unconstrained_param_names_visgen vis(o);
unconstrained_param_names_visgen vis1(1, o);
unconstrained_param_names_visgen vis2(2, o);
for (size_t i = 0; i < prog.parameter_decl_.size(); ++i)
boost::apply_visitor(vis, prog.parameter_decl_[i].decl_);
boost::apply_visitor(vis1, prog.parameter_decl_[i].decl_);
o << EOL << INDENT2
<< "if (!include_gqs__ && !include_tparams__) return;" << EOL;
o << EOL << INDENT2 << "if (include_tparams__) {" << EOL;
for (size_t i = 0; i < prog.derived_decl_.first.size(); ++i)
boost::apply_visitor(vis, prog.derived_decl_.first[i].decl_);
boost::apply_visitor(vis2, prog.derived_decl_.first[i].decl_);
o << INDENT2 << "}" << EOL2;

o << EOL << INDENT2 << "if (!include_gqs__) return;" << EOL;
for (size_t i = 0; i < prog.generated_decl_.first.size(); ++i)
boost::apply_visitor(vis, prog.generated_decl_.first[i].decl_);
boost::apply_visitor(vis1, prog.generated_decl_.first[i].decl_);
o << INDENT << "}" << EOL2;
}

Expand Down
5 changes: 2 additions & 3 deletions src/stan/lang/generator/generate_write_array_method.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,6 @@ namespace stan {
boost::apply_visitor(vis_writer, prog.parameter_decl_[i].decl_);
o << EOL;

o << INDENT2 << "if (!include_tparams__) return;"
<< EOL;
generate_comment("declare and define transformed parameters", 2, o);
o << INDENT2 << "double lp__ = 0.0;" << EOL;
generate_void_statement("lp__", 2, o);
Expand All @@ -87,9 +85,10 @@ namespace stan {
o << EOL;

generate_comment("write transformed parameters", 3, o);
o << INDENT3 << "if (include_tparams__) {" << EOL;
for (size_t i = 0; i < prog.derived_decl_.first.size(); ++i)
boost::apply_visitor(vis_writer, prog.derived_decl_.first[i].decl_);
o << EOL;
o << INDENT3 << "}" << EOL;

o << INDENT3 << "if (!include_gqs__) return;"
<< EOL;
Expand Down
19 changes: 13 additions & 6 deletions src/stan/lang/generator/unconstrained_param_names_visgen.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,13 +17,20 @@ namespace stan {
* accumulator <code>param_names__</code>.
*/
struct unconstrained_param_names_visgen : public visgen {
/**
* Indentation level.
*/
size_t indent_;

/**
* Construct an uncontrained parmater names visitor for the
* specified stream.
*
* @param[in] indent level of indentation
* @param[in,out] o stream for generating
*/
explicit unconstrained_param_names_visgen(std::ostream& o) : visgen(o) { }
explicit unconstrained_param_names_visgen(size_t indent, std::ostream& o)
: visgen(o), indent_(indent) { }

// FIXME(carpenter): following function cut-and-pasted from constrained
/**
Expand All @@ -43,24 +50,24 @@ namespace stan {
for (size_t i = 0; i < matrix_dims.size(); ++i)
combo_dims.push_back(matrix_dims[i]);
for (size_t i = combo_dims.size(); i-- > 0; ) {
generate_indent(1 + combo_dims.size() - i, o_);
generate_indent(indent_ + combo_dims.size() - i, o_);
o_ << "for (int k_" << i << "__ = 1;" << " k_" << i << "__ <= ";
generate_expression(combo_dims[i].expr_, NOT_USER_FACING, o_);
o_ << "; ++k_" << i << "__) {" << EOL; // begin (1)
}
// add to accumulator
generate_indent(2 + combo_dims.size(), o_);
generate_indent(indent_ + 1 + combo_dims.size(), o_);
o_ << "param_name_stream__.str(std::string());" << EOL;
generate_indent(2 + combo_dims.size(), o_);
generate_indent(indent_ + 1 + combo_dims.size(), o_);
o_ << "param_name_stream__ << \"" << name << '"';
for (size_t i = 0; i < combo_dims.size(); ++i)
o_ << " << '.' << k_" << i << "__";
o_ << ';' << EOL;
generate_indent(2 + combo_dims.size(), o_);
generate_indent(indent_ + 1 + combo_dims.size(), o_);
o_ << "param_names__.push_back(param_name_stream__.str());" << EOL;
// end for loop dims
for (size_t i = 0; i < combo_dims.size(); ++i) {
generate_indent(1 + combo_dims.size() - i, o_);
generate_indent(indent_ + combo_dims.size() - i, o_);
o_ << "}" << EOL; // end (1)
}
}
Expand Down
92 changes: 92 additions & 0 deletions src/stan/services/sample/standalone_gqs.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
#ifndef STAN_SERVICES_SAMPLE_STANDALONE_GQS_HPP
#define STAN_SERVICES_SAMPLE_STANDALONE_GQS_HPP

#include <stan/callbacks/interrupt.hpp>
#include <stan/callbacks/logger.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/services/error_codes.hpp>
#include <stan/services/util/create_rng.hpp>
#include <stan/services/util/gq_writer.hpp>
#include <string>
#include <vector>

namespace stan {
namespace services {

/**
* Return the number of constrained parameters for the specified
* model.
*
* @tparam Model type of model
* @param[in] model model to query
* @return number of constrained parameters for the model
*/
template <class Model>
int num_constrained_params(const Model& model) {
std::vector<std::string> param_names;
static const bool include_tparams = false;
static const bool include_gqs = false;
model.constrained_param_names(param_names, include_tparams,
include_gqs);
return param_names.size();
}

/**
* Given a set of draws from a fitted model, generate corresponding
* quantities of interest. Data written to callback writer.
* Return code indicates success or type of error.
*
* @tparam Model model class
* @param[in] model instantiated model
* @param[in] draws sequence of draws of unconstrained parameters
* @param[in] seed seed to use for randomization
* @param[in, out] interrupt called every iteration
* @param[in, out] logger logger to which to write warning and error messages
* @param[in, out] sample_writer writer to which draws are written
* @return error code
*/
template <class Model>
int standalone_generate(const Model& model,
const std::vector<std::vector<double> >& draws,
unsigned int seed,
callbacks::interrupt& interrupt,
callbacks::logger& logger,
callbacks::writer& sample_writer) {
if (draws.empty()) {
logger.error("Empty set of draws from fitted model.");
return error_codes::DATAERR;
}

int num_params = num_constrained_params(model);
std::vector<std::string> gq_names;
static const bool include_tparams = false;
static const bool include_gqs = true;
model.constrained_param_names(gq_names, include_tparams, include_gqs);
if (!(static_cast<size_t>(num_params) < gq_names.size())) {
logger.error("Model doesn't generate any quantities of interest.");
return error_codes::CONFIG;
}

util::gq_writer writer(sample_writer, logger, num_params);
boost::ecuyer1988 rng = util::create_rng(seed, 1);
writer.write_gq_names(model);

std::stringstream msg;
for (const std::vector<double>& draw : draws) {
if (static_cast<size_t>(num_params) != draw.size()) {
msg << "Wrong number of params in draws from fitted model. ";
msg << "Expecting " << num_params << " columns, ";
msg << "found " << draws[0].size() << " columns.";
std::string msgstr = msg.str();
logger.error(msgstr);
return error_codes::DATAERR;
}
interrupt(); // call out to interrupt and fail
writer.write_gq_values(model, rng, draw);
}
return error_codes::OK;
}

}
}
#endif
105 changes: 105 additions & 0 deletions src/stan/services/util/gq_writer.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,105 @@
#ifndef STAN_SERVICES_UTIL_GQ_WRITER_HPP
#define STAN_SERVICES_UTIL_GQ_WRITER_HPP

#include <stan/callbacks/logger.hpp>
#include <stan/callbacks/writer.hpp>
#include <stan/mcmc/base_mcmc.hpp>
#include <stan/mcmc/sample.hpp>
#include <stan/model/prob_grad.hpp>
#include <sstream>
#include <iomanip>
#include <string>
#include <vector>

namespace stan {
namespace services {
namespace util {

/**
* gq_writer writes out
*
* @tparam Model Model class
*/
class gq_writer {
private:
callbacks::writer& sample_writer_;
callbacks::logger& logger_;
int num_constrained_params_;

public:
/**
* Constructor.
*
* @param[in,out] sample_writer samples are "written" to this stream
* @param[in,out] logger messages are written through the logger
* @param[in] num_constrained_params offset into write_array gqs
*/
gq_writer(callbacks::writer& sample_writer, callbacks::logger& logger,
int num_constrained_params): sample_writer_(sample_writer),
logger_(logger),
num_constrained_params_(num_constrained_params) { }

/**
* Write names of variables declared in the generated quantities block
* to stream `sample_writer_`.
*
* @tparam M model class
*/
template <class Model>
void write_gq_names(const Model& model) {
static const bool include_tparams = false;
static const bool include_gqs = true;
std::vector<std::string> names;
model.constrained_param_names(names, include_tparams, include_gqs);
std::vector<std::string> gq_names(names.begin()
+ num_constrained_params_,
names.end());
sample_writer_(gq_names);
}

/**
* Calls model's `write_array` method and writes values of
* variables defined in the generated quantities block
* to stream `sample_writer_`.
*
* @tparam M model class
* @tparam RNG pseudo random number generator class
* @param[in] model instantiated model
* @param[in] rng instantiated RNG
* @param[in] draw sequence unconstrained parameters values.
*/
template <class Model, class RNG>
void write_gq_values(const Model& model,
RNG& rng,
const std::vector<double>& draw) {
std::vector<double> values;
std::vector<int> params_i; // unused - no discrete params
std::stringstream ss;
try {
model.write_array(rng,
const_cast<std::vector<double>&>(draw),
params_i,
values,
false,
true,
&ss);
} catch (const std::exception& e) {
if (ss.str().length() > 0)
logger_.info(ss);
logger_.info(e.what());
return;
}
if (ss.str().length() > 0)
logger_.info(ss);

std::vector<double> gq_values(values.begin()
+ num_constrained_params_,
values.end());
sample_writer_(gq_values);
}
};

}
}
}
#endif
Loading

0 comments on commit abd9e40

Please sign in to comment.