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

Update log_prob method to take constrained_params in init format #1108

Merged
merged 14 commits into from Nov 7, 2022
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
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
5 changes: 5 additions & 0 deletions src/cmdstan/arguments/arg_log_prob.hpp
Expand Up @@ -8,6 +8,11 @@

namespace cmdstan {

/**
* Argument used for calling the log_prob method to calculate the log density
* and its gradients with respect to a user-provided set of parameter values
* on the constrained and/or unconstrained scale
*/
class arg_log_prob : public categorical_argument {
public:
arg_log_prob() {
Expand Down
7 changes: 4 additions & 3 deletions src/cmdstan/arguments/arg_log_prob_constrained_params.hpp
Expand Up @@ -6,9 +6,10 @@
namespace cmdstan {
/**
* Argument for providing a file of parameters on the constrained scale
* for use with the 'log_prob' method. The file can be in CSV or JSON format
* and should contain a variable 'params_r' with either a vector or list/array
* of vectors of constrained parameter values.
* for use with the 'log_prob' method. The file can be in JSON or R Dump format,
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
* using the same structure as the 'init' argument. Like the 'init' argument, if
* the file has a '.json' extension it is treated as a JSON file, otherwise it
* is treated as an RDump file.
*/
class arg_log_prob_constrained_params : public string_argument {
public:
Expand Down
5 changes: 4 additions & 1 deletion src/cmdstan/arguments/arg_log_prob_output_file.hpp
Expand Up @@ -4,7 +4,10 @@
#include <cmdstan/arguments/singleton_argument.hpp>

namespace cmdstan {

/**
* Sub-argument to provide a custom path and filename for the output from
* the log_prob method
*/
class arg_log_prob_output_file : public string_argument {
public:
arg_log_prob_output_file() : string_argument() {
Expand Down
4 changes: 3 additions & 1 deletion src/cmdstan/arguments/arg_log_prob_unconstrained_params.hpp
Expand Up @@ -8,7 +8,9 @@ namespace cmdstan {
* Argument for providing a file of parameters on the unconstrained scale
* for use with the 'log_prob' method. The file can be in CSV or JSON format
* and should contain a variable 'params_r' with either a vector or list/array
* of vectors of unconstrained parameter values.
* of vectors of unconstrained parameter values. Like the 'init' argument, if
* the file has a '.json' extension it is treated as a JSON file, otherwise it
* is treated as an RDump file.
*/
class arg_log_prob_unconstrained_params : public string_argument {
public:
Expand Down
77 changes: 33 additions & 44 deletions src/cmdstan/command.hpp
Expand Up @@ -485,7 +485,7 @@ int command(int argc, const char *argv[]) {
return std::string("_" + std::to_string(i + id));
}
};
for (int i = 0; i < num_chains; i++) {
for (int i = 0; i < num_chains; ++i) {
auto output_filename = output_name + name_iterator(i) + output_ending;
auto unique_fstream
= std::make_unique<std::fstream>(output_filename, std::fstream::out);
Expand All @@ -504,7 +504,7 @@ int command(int argc, const char *argv[]) {
diagnostic_writers.emplace_back(nullptr, "# ");
}
}
for (int i = 0; i < num_chains; i++) {
for (int i = 0; i < num_chains; ++i) {
write_stan(sample_writers[i]);
write_model(sample_writers[i], model.model_name());
write_datetime(sample_writers[i]);
Expand Down Expand Up @@ -620,8 +620,13 @@ int command(int argc, const char *argv[]) {
std::shared_ptr<stan::io::var_context> upars_context
= get_var_context(u_fname);

u_params_r = (*upars_context).vals_r("params_r");
dims_u_params_r = (*upars_context).dims_r("params_r");
u_params_r = upars_context->vals_r("params_r");
if (u_params_r.size() == 0) {
msg << "Unconstrained parameters file has no variable 'params_r' with "
"unconstrained parameter values!";
throw std::invalid_argument(msg.str());
}
dims_u_params_r = upars_context->dims_r("params_r");

// Detect whether multiple sets of parameter values have been passed
// and set the sizes accordingly
Expand All @@ -644,50 +649,34 @@ int command(int argc, const char *argv[]) {
throw std::invalid_argument(msg.str());
}

size_t c_params_vec_size = 0;
size_t c_params_size = 0;
std::vector<double> c_params_r;
std::vector<size_t> dims_c_params_r;
if (!(cpars_file->is_default())) {
std::string c_fname(cpars_file->value());
std::ifstream c_stream(c_fname.c_str());

std::shared_ptr<stan::io::var_context> cpars_context
= get_var_context(c_fname);

c_params_r = (*cpars_context).vals_r("params_r");
dims_c_params_r = (*cpars_context).dims_r("params_r");

c_params_vec_size = dims_c_params_r.size() == 2 ? dims_c_params_r[0] : 1;
c_params_size = dims_c_params_r.size() == 2 ? dims_c_params_r[1]
: dims_c_params_r[0];
}

if (c_params_size > 0 && c_params_size != param_names.size()) {
msg << "Incorrect number of constrained parameters provided! "
"Model has "
<< param_names.size() << " parameters but " << c_params_size
<< " were found.";
throw std::invalid_argument(msg.str());
}

int has_cpars = !cpars_file->is_default();
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
// Store in single nested array to allow single loop for calc and print
size_t num_par_sets = c_params_vec_size + u_params_vec_size;
size_t num_par_sets = u_params_vec_size + has_cpars;
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
std::vector<std::vector<double>> params_r_ind(num_par_sets);

// Use Map with inner stride to operate on all values from parameter set
using StrideT = Eigen::Stride<1, Eigen::Dynamic>;
std::vector<int> dummy_params_i;
for (size_t i = 0; i < c_params_vec_size; i++) {
Eigen::Map<Eigen::VectorXd, 0, StrideT> map_r(
c_params_r.data() + i, c_params_size, StrideT(1, c_params_vec_size));
if (!cpars_file->is_default()) {
std::string cpars_filename(cpars_file->value());

stan::io::array_var_context context(param_names, map_r, param_dimss);
model.transform_inits(context, dummy_params_i, params_r_ind[i], &msg);
std::shared_ptr<stan::io::var_context> cpars_context
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
= get_var_context(cpars_filename);
std::vector<std::string> input_cpar_names;
cpars_context->names_r(input_cpar_names);

for (const std::string &m_param_name : param_names) {
if (!cpars_context->contains_r(m_param_name)) {
msg << "Constrained value(s) for parameter " << m_param_name
<< " not found!";
throw std::invalid_argument(msg.str());
}
}
model.transform_inits((*cpars_context), dummy_params_i, params_r_ind[0],
bob-carpenter marked this conversation as resolved.
Show resolved Hide resolved
&msg);
}

for (size_t i = c_params_vec_size; i < num_par_sets; i++) {
size_t iter = i - c_params_vec_size;
// Use Map with inner stride to operate on all values from parameter set
using StrideT = Eigen::Stride<1, Eigen::Dynamic>;
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
for (size_t i = has_cpars; i < num_par_sets; ++i) {
size_t iter = i - has_cpars;
Eigen::Map<Eigen::VectorXd, 0, StrideT> map_r(
u_params_r.data() + iter, u_params_size,
StrideT(1, u_params_vec_size));
Expand All @@ -702,14 +691,14 @@ int command(int argc, const char *argv[]) {

std::vector<std::string> p_names;
model.constrained_param_names(p_names, false, false);
for (size_t i = 1; i < p_names.size(); i++) {
for (size_t i = 1; i < p_names.size(); ++i) {
output_stream << "g_" << p_names[i] << ",";
}
output_stream << "g_" << p_names.back() << "\n";
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
try {
double lp;
std::vector<double> gradients;
for (size_t i = 0; i < num_par_sets; i++) {
for (size_t i = 0; i < num_par_sets; ++i) {
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
if (jacobian_adjust) {
lp = stan::model::log_prob_grad<false, true>(
model, params_r_ind[i], dummy_params_i, gradients);
Expand Down
6 changes: 3 additions & 3 deletions src/test/test-models/bern_constrained_params.R
@@ -1,3 +1,3 @@
params_r <-
structure(c(0.5, 0.1, -1.5, -3.5, 2.6, 1.6),
.Dim = c(2, 3))
theta <- 0.1
mu1 <- 2.0
mu2 <- 4.0
6 changes: 5 additions & 1 deletion src/test/test-models/bern_constrained_params.json
@@ -1 +1,5 @@
{"params_r":[[0.5, -1.5, 2.6], [0.1, -3.5, 1.6]]}
{
"theta": 0.1,
"mu1" : 2.0,
"mu2" : 4.0
}
5 changes: 4 additions & 1 deletion src/test/test-models/bern_constrained_params_short.json
@@ -1 +1,4 @@
{"params_r":[[0.5, -1.5, 2.6], [0.1, -3.5]]}
{
andrjohns marked this conversation as resolved.
Show resolved Hide resolved
"theta": 0.1,
"mu1" : 2.0
}