1 #ifndef __STAN__GM__COMMAND_HPP__
2 #define __STAN__GM__COMMAND_HPP__
5 #include <boost/date_time/posix_time/posix_time_types.hpp>
6 #include <boost/math/special_functions/fpclassify.hpp>
7 #include <boost/random/additive_combine.hpp>
8 #include <boost/random/uniform_real_distribution.hpp>
29 std::cout << std::endl;
30 std::cout <<
"Compiled Stan Graphical Model Command" << std::endl;
31 std::cout << std::endl;
33 std::cout <<
"USAGE: " << cmd <<
" [options]" << std::endl;
34 std::cout << std::endl;
36 std::cout <<
"OPTIONS:" << std::endl;
37 std::cout << std::endl;
41 "Display this information");
45 "Read data from specified dump-format file",
46 "required if model declares data");
50 "Use initial values from specified file or zero values if <file>=0",
51 "default is random initialization");
55 "File into which samples are written",
56 "default = samples.csv");
60 "Append samples to existing file if it exists",
61 "does not write header in append mode");
65 "Random number generation seed",
66 "default = randomly generated from time");
70 "Markov chain identifier",
75 "Total number of iterations, including warmup",
80 "Discard the specified number of initial samples",
81 "default = iter / 2");
85 "Period between saved samples after warm up",
86 "default = max(1, floor(iter - warmup) / 1000)");
90 "Period between samples updating progress report print (0 for no printing)",
91 "default = max(1,iter/200))");
94 "leapfrog_steps",
"int",
95 "Number of leapfrog steps; -1 for no-U-turn adaptation",
99 "max_treedepth",
"int",
100 "Limit NUTS leapfrog steps to 2^max_tree_depth; -1 for no limit",
105 "Initial value for step size, or -1 to set automatically",
109 "epsilon_pm",
"[0,1]",
110 "Sample epsilon +/- epsilon * epsilon_pm",
114 "equal_step_sizes",
"",
115 "Use same step size for every parameter with NUTS",
116 "default is to estimate varying step sizes during warmup");
120 "Accuracy target for step-size adaptation (higher means smaller step sizes)",
125 "Gamma parameter for dual averaging step-size adaptation",
130 "Save the warmup samples");
134 "Test gradient calculations using finite differences");
138 "Fit point estimate of hidden parameters by maximizing log joint probability using Nesterov's accelerated gradient method");
141 "point_estimate_newton",
"",
142 "Fit point estimate of hidden parameters by maximizing log joint probability using Newton's method");
146 "Use a nondiagonal matrix to do the sampling");
150 "Preset an estimated covariance matrix");
152 std::cout << std::endl;
158 || ((n + 1) % refresh == 0) );
161 template <
class Sampler,
class Model,
class RNG>
169 std::ostream& sample_file_stream,
170 std::vector<double>& params_r,
171 std::vector<int>& params_i,
175 sampler.set_params(params_r,params_i);
178 std::cout << std::endl;
182 for (
int m = 0; m < num_iterations; ++m) {
184 std::cout <<
"Iteration: ";
185 std::cout << std::setw(it_print_width) << (m + 1)
186 <<
" / " << num_iterations;
187 std::cout <<
" [" << std::setw(3)
188 <<
static_cast<int>((100.0 * (m + 1))/num_iterations)
190 std::cout << ((m < num_warmup) ?
" (Adapting)" :
" (Sampling)");
191 std::cout << std::endl;
194 if (m < num_warmup) {
195 if (save_warmup && (m % num_thin) == 0) {
199 sample_file_stream << sample.
log_prob() <<
',';
200 sampler.write_sampler_params(sample_file_stream);
203 model.write_csv(base_rng,params_r,params_i,sample_file_stream,&std::cout);
208 if (epsilon_adapt && sampler.adapting()) {
210 sampler.write_adaptation_params(sample_file_stream);
212 if (((m - num_warmup) % num_thin) != 0) {
219 sample_file_stream << sample.
log_prob() <<
',';
220 sampler.write_sampler_params(sample_file_stream);
223 model.write_csv(base_rng,params_r,params_i,sample_file_stream,&std::cout);
230 o <<
"#" << std::endl;
232 template <
typename M>
235 o <<
"# " << msg << std::endl;
237 template <
typename K,
typename V>
241 o <<
"# " << key <<
"=" << val << std::endl;
244 template <
class Model>
254 std::string data_file;
255 command.
val(
"data",data_file);
256 std::fstream data_stream(data_file.c_str(),
261 Model model(data_var_context, &std::cout);
263 bool point_estimate = command.
has_flag(
"point_estimate");
264 bool point_estimate_newton = command.
has_flag(
"point_estimate_newton");
266 std::string sample_file =
"samples.csv";
267 command.
val(
"samples",sample_file);
269 unsigned int num_iterations = 2000U;
270 command.
val(
"iter",num_iterations);
272 unsigned int num_warmup = num_iterations / 2;
273 command.
val(
"warmup",num_warmup);
275 unsigned int calculated_thin = (num_iterations - num_warmup) / 1000U;
276 unsigned int num_thin = (calculated_thin > 1) ? calculated_thin : 1U;
277 command.
val(
"thin",num_thin);
279 bool user_supplied_thin = command.
has_key(
"thin");
281 int leapfrog_steps = -1;
282 command.
val(
"leapfrog_steps",leapfrog_steps);
285 command.
val(
"epsilon",epsilon);
287 int max_treedepth = 10;
288 command.
val(
"max_treedepth",max_treedepth);
290 double epsilon_pm = 0.0;
291 command.
val(
"epsilon_pm",epsilon_pm);
292 if (epsilon_pm < 0.0 || epsilon_pm > 1.0) {
293 std::stringstream ss;
294 ss <<
"epsilon_pm must be between 0 and 1"
295 <<
"; found epsilon_pm=" << epsilon_pm;
296 throw std::invalid_argument(ss.str());
299 bool epsilon_adapt = epsilon <= 0.0;
301 bool equal_step_sizes = command.
has_flag(
"equal_step_sizes");
304 command.
val(
"delta", delta);
307 command.
val(
"gamma", gamma);
309 int refresh = num_iterations / 200;
310 refresh = refresh <= 0 ? 1 : refresh;
311 command.
val(
"refresh",refresh);
313 bool nondiag_mass = command.
has_flag(
"nondiag_mass");
315 std::string cov_file =
"";
316 command.
val(
"cov_matrix", cov_file);
318 unsigned int random_seed = 0;
320 bool well_formed = command.
val(
"seed",random_seed);
322 std::string seed_val;
323 command.
val(
"seed",seed_val);
324 std::cerr <<
"value for seed must be integer"
325 <<
"; found value=" << seed_val << std::endl;
330 = (boost::posix_time::microsec_clock::universal_time() -
331 boost::posix_time::ptime(boost::posix_time::min_date_time))
332 .total_milliseconds();
336 if (command.
has_key(
"chain_id")) {
337 bool well_formed = command.
val(
"chain_id",chain_id);
338 if (!well_formed || chain_id < 0) {
339 std::string chain_id_val;
340 command.
val(
"chain_id",chain_id_val);
341 std::cerr <<
"value for chain_id must be positive integer"
342 <<
"; found chain_id=" << chain_id_val
352 typedef boost::ecuyer1988 rng_t;
353 rng_t base_rng(random_seed);
355 static boost::uintmax_t DISCARD_STRIDE =
static_cast<boost::uintmax_t
>(1) << 50;
357 base_rng.discard(DISCARD_STRIDE * (chain_id - 1));
359 std::vector<int> params_i;
360 std::vector<double> params_r;
362 std::string init_val;
364 int num_init_tries = 1;
367 command.
val(
"init",init_val);
368 if (init_val ==
"0") {
369 params_i = std::vector<int>(model.num_params_i(),0);
370 params_r = std::vector<double>(model.num_params_r(),0.0);
373 std::fstream init_stream(init_val.c_str(),std::fstream::in);
374 if (init_stream.fail()) {
375 std::string msg(
"ERROR: specified init file does not exist: ");
377 throw std::invalid_argument(msg);
381 model.transform_inits(init_var_context,params_i,params_r);
382 }
catch (
const std::exception&
e) {
383 std::cerr <<
"Error during user-specified initialization:"
391 init_val =
"random initialization";
393 boost::random::uniform_real_distribution<double>
394 init_range_distribution(-2.0,2.0);
395 boost::variate_generator<rng_t&,
396 boost::random::uniform_real_distribution<double> >
397 init_rng(base_rng,init_range_distribution);
399 params_i = std::vector<int>(model.num_params_i(),0);
400 params_r = std::vector<double>(model.num_params_r());
403 std::vector<double> init_grad;
404 static int MAX_INIT_TRIES = 100;
405 for (num_init_tries = 1; num_init_tries <= MAX_INIT_TRIES; ++num_init_tries) {
406 for (
size_t i = 0; i < params_r.size(); ++i)
407 params_r[i] = init_rng();
409 double init_log_prob;
411 init_log_prob = model.grad_log_prob(params_r,params_i,init_grad,&std::cout);
412 }
catch (std::domain_error
e) {
414 init_log_prob = -std::numeric_limits<double>::infinity();
418 for (
size_t i = 0; i < init_grad.size(); ++i)
423 if (num_init_tries > MAX_INIT_TRIES) {
424 std::cout << std::endl << std::endl
425 <<
"Initialization failed after " << MAX_INIT_TRIES
427 <<
" Try specifying initial values,"
428 <<
" reducing ranges of constrained values,"
429 <<
" or reparameterizing the model."
435 bool save_warmup = command.
has_flag(
"save_warmup");
437 bool append_samples = command.
has_flag(
"append_samples");
438 std::ios_base::openmode samples_append_mode
440 ? (std::fstream::out | std::fstream::app)
443 if (command.
has_flag(
"test_grad")) {
444 std::cout << std::endl <<
"TEST GRADIENT MODE" << std::endl;
445 return model.test_gradients(params_r,params_i);
448 if (point_estimate_newton) {
449 std::cout <<
"STAN OPTIMIZATION COMMAND" << std::endl;
451 std::cout <<
"data = (specified model requires no data)" << std::endl;
453 std::cout <<
"data = " << data_file << std::endl;
455 std::cout <<
"init = " << init_val << std::endl;
456 if (num_init_tries > 0)
457 std::cout <<
"init tries = " << num_init_tries << std::endl;
459 std::cout <<
"output = " << sample_file << std::endl;
460 std::cout <<
"save_warmup = " << save_warmup<< std::endl;
462 std::cout <<
"seed = " << random_seed
463 <<
" (" << (command.
has_key(
"seed")
465 :
"randomly generated") <<
")"
468 std::fstream sample_stream(sample_file.c_str(),
469 samples_append_mode);
471 write_comment(sample_stream,
"Point Estimate Generated by Stan");
482 sample_stream <<
"lp__,";
483 model.write_csv_header(sample_stream);
485 std::vector<double> gradient;
488 lp = model.grad_log_prob(params_r, params_i, gradient);
489 }
catch (std::domain_error
e) {
491 lp = -std::numeric_limits<double>::infinity();
494 double lastlp = lp - 1;
495 std::cout <<
"initial log joint probability = " << lp << std::endl;
497 while ((lp - lastlp) /
fabs(lp) > 1e-8) {
500 std::cout <<
"Iteration ";
501 std::cout << std::setw(2) << (m + 1) <<
". ";
502 std::cout <<
"Log joint probability = " << std::setw(10) << lp;
503 std::cout <<
". Improved by " << (lp - lastlp) <<
".";
504 std::cout << std::endl;
511 sample_stream << lp <<
',';
512 model.write_csv(base_rng,params_r,params_i,sample_stream);
516 sample_stream << lp <<
',';
517 model.write_csv(base_rng,params_r,params_i,sample_stream);
522 if (point_estimate) {
523 std::cout <<
"STAN OPTIMIZATION COMMAND" << std::endl;
525 std::cout <<
"data = (specified model requires no data)" << std::endl;
527 std::cout <<
"data = " << data_file << std::endl;
529 std::cout <<
"init = " << init_val << std::endl;
530 if (num_init_tries > 0)
531 std::cout <<
"init tries = " << num_init_tries << std::endl;
533 std::cout <<
"output = " << sample_file << std::endl;
534 std::cout <<
"save_warmup = " << save_warmup<< std::endl;
536 std::cout <<
"seed = " << random_seed
537 <<
" (" << (command.
has_key(
"seed")
539 :
"randomly generated") <<
")"
542 std::fstream sample_stream(sample_file.c_str(),
543 samples_append_mode);
545 write_comment(sample_stream,
"Point Estimate Generated by Stan");
556 sample_stream <<
"lp__,";
557 model.write_csv_header(sample_stream);
561 double lp = ng.
logp();
563 double lastlp = lp - 1;
564 std::cout <<
"initial log joint probability = " << lp << std::endl;
566 for (
size_t i = 0; i < num_iterations; i++) {
571 std::cout <<
"Iteration ";
572 std::cout << std::setw(2) << (m + 1) <<
". ";
573 std::cout <<
"Log joint probability = " << std::setw(10) << lp;
574 std::cout <<
". Improved by " << (lp - lastlp) <<
".";
575 std::cout << std::endl;
580 sample_stream << lp <<
',';
581 model.write_csv(base_rng,params_r,params_i,sample_stream);
585 sample_stream << lp <<
',';
586 model.write_csv(base_rng,params_r,params_i,sample_stream);
591 std::cout <<
"STAN SAMPLING COMMAND" << std::endl;
593 std::cout <<
"data = (specified model requires no data)" << std::endl;
595 std::cout <<
"data = " << data_file << std::endl;
597 std::cout <<
"init = " << init_val << std::endl;
598 if (num_init_tries > 0)
599 std::cout <<
"init tries = " << num_init_tries << std::endl;
601 std::cout <<
"samples = " << sample_file << std::endl;
602 std::cout <<
"append_samples = " << append_samples << std::endl;
603 std::cout <<
"save_warmup = " << save_warmup<< std::endl;
605 std::cout <<
"seed = " << random_seed
606 <<
" (" << (command.
has_key(
"seed")
608 :
"randomly generated") <<
")"
610 std::cout <<
"chain_id = " << chain_id
611 <<
" (" << (command.
has_key(
"chain_id")
616 std::cout <<
"iter = " << num_iterations << std::endl;
617 std::cout <<
"warmup = " << num_warmup << std::endl;
618 std::cout <<
"thin = " << num_thin
619 << (user_supplied_thin ?
" (user supplied)" :
" (default)")
622 std::cout <<
"equal_step_sizes = " << equal_step_sizes << std::endl;
623 std::cout <<
"nondiag_mass = " << nondiag_mass << std::endl;
624 std::cout <<
"leapfrog_steps = " << leapfrog_steps << std::endl;
625 std::cout <<
"max_treedepth = " << max_treedepth << std::endl;;
626 std::cout <<
"epsilon = " << epsilon << std::endl;;
627 std::cout <<
"epsilon_pm = " << epsilon_pm << std::endl;;
628 std::cout <<
"delta = " << delta << std::endl;
629 std::cout <<
"gamma = " << gamma << std::endl;
631 std::fstream sample_stream(sample_file.c_str(),
632 samples_append_mode);
659 clock_t start = clock();
662 max_treedepth, epsilon,
663 epsilon_pm, epsilon_adapt,
668 if (!append_samples) {
669 sample_stream <<
"lp__,";
671 model.write_csv_header(sample_stream);
676 sample_from(nuts_nondiag_sampler,epsilon_adapt,refresh,
677 num_iterations,num_warmup,num_thin,save_warmup,
678 sample_stream,params_r,params_i,
682 else if (leapfrog_steps < 0 && !equal_step_sizes) {
685 max_treedepth, epsilon,
686 epsilon_pm, epsilon_adapt,
691 if (!append_samples) {
692 sample_stream <<
"lp__,";
694 model.write_csv_header(sample_stream);
700 num_iterations,num_warmup,num_thin,save_warmup,
701 sample_stream,params_r,params_i,
704 }
else if (leapfrog_steps < 0 && equal_step_sizes) {
708 max_treedepth, epsilon,
709 epsilon_pm, epsilon_adapt,
716 if (!append_samples) {
717 sample_stream <<
"lp__,";
719 model.write_csv_header(sample_stream);
723 num_iterations,num_warmup,num_thin,save_warmup,
724 sample_stream,params_r,params_i,
732 epsilon, epsilon_pm, epsilon_adapt,
739 if (!append_samples) {
740 sample_stream <<
"lp__,";
742 model.write_csv_header(sample_stream);
746 num_iterations,num_warmup,num_thin,save_warmup,
747 sample_stream,params_r,params_i,
750 clock_t end = clock();
751 double deltaT = (double)(end - start) / CLOCKS_PER_SEC;
752 std::cout << std::endl
753 <<
"Elapsed Time: " << deltaT <<
" seconds"
756 sample_stream.close();
757 std::cout << std::endl << std::endl;