1 #ifndef STAN_SERVICES_UTIL_INITIALIZE_HPP
2 #define STAN_SERVICES_UTIL_INITIALIZE_HPP
3 
4 #include <stan/callbacks/logger.hpp>
5 #include <stan/callbacks/writer.hpp>
6 #include <stan/io/var_context.hpp>
7 #include <stan/io/random_var_context.hpp>
8 #include <stan/io/chained_var_context.hpp>
9 #include <stan/model/log_prob_grad.hpp>
10 #include <stan/math/prim/arr/fun/sum.hpp>
11 #include <sstream>
12 #include <string>
13 #include <vector>
14 
15 namespace stan {
16 namespace services {
17 namespace util {
18 
19 /**
20  * Returns a valid initial value of the parameters of the model
21  * on the unconstrained scale.
22  *
23  * For identical inputs (model, init, rng, init_radius), this
24  * function will produce the same initialization.
25  *
26  * Initialization first tries to use the provided
27  * <code>stan::io::var_context</code>, then it will generate
28  * random uniform values from -init_radius to +init_radius for missing
29  * parameters.
30  *
31  * When the <code>var_context</code> provides all variables or
32  * the init_radius is 0, this function will only evaluate the
33  * log probability of the model with the unconstrained
34  * parameters once to see if it's valid.
35  *
36  * When at least some of the initialization is random, it will
37  * randomly initialize until it finds a set of unconstrained
38  * parameters that are valid or it hits <code>MAX_INIT_TRIES =
39  * 100</code> (hard-coded).
40  *
41  * Valid initialization is defined as a finite, non-NaN value for the
42  * evaluation of the log probability density function and all its
43  * gradients.
44  *
45  * @tparam Jacobian indicates whether to include the Jacobian term when
46  *   evaluating the log density function
47  * @tparam Model the type of the model class
48  * @tparam RNG the type of the random number generator
49  *
50  * @param[in] model the model
51  * @param[in] init a var_context with initial values
52  * @param[in,out] rng random number generator
53  * @param[in] init_radius the radius for generating random values.
54  *   A value of 0 indicates that the unconstrained parameters (not
55  *   provided by init) should be initialized with 0.
56  * @param[in] print_timing indicates whether a timing message should
57  *   be printed to the logger
58  * @param[in,out] logger logger for messages
59  * @param[in,out] init_writer init writer (on the unconstrained scale)
60  * @throws exception passed through from the model if the model has a
61  *   fatal error (not a std::domain_error)
62  * @throws std::domain_error if the model can not be initialized and
63  *   the model does not have a fatal error (only allows for
64  *   std::domain_error)
65  * @return valid unconstrained parameters for the model
66  */
67 template <bool Jacobian = true, class Model, class RNG>
initialize(Model & model,stan::io::var_context & init,RNG & rng,double init_radius,bool print_timing,stan::callbacks::logger & logger,stan::callbacks::writer & init_writer)68 std::vector<double> initialize(Model& model,
69                                stan::io::var_context& init,
70                                RNG& rng,
71                                double init_radius,
72                                bool print_timing,
73                                stan::callbacks::logger& logger,
74                                stan::callbacks::writer&
75                                init_writer) {
76   std::vector<double> unconstrained;
77   std::vector<int> disc_vector;
78 
79   bool is_fully_initialized = true;
80   bool any_initialized = false;
81   std::vector<std::string> param_names;
82   model.get_param_names(param_names);
83   for (size_t n = 0; n < param_names.size(); n++) {
84     is_fully_initialized &= init.contains_r(param_names[n]);
85     any_initialized |= init.contains_r(param_names[n]);
86   }
87 
88   bool is_initialized_with_zero = init_radius == 0.0;
89 
90   int MAX_INIT_TRIES = is_fully_initialized || is_initialized_with_zero
91                        ? 1 : 100;
92   int num_init_tries = 0;
93   for (; num_init_tries < MAX_INIT_TRIES; num_init_tries++) {
94     std::stringstream msg;
95     try {
96       stan::io::random_var_context
97           random_context(model, rng, init_radius, is_initialized_with_zero);
98 
99       if (!any_initialized) {
100         unconstrained = random_context.get_unconstrained();
101       } else {
102         stan::io::chained_var_context context(init, random_context);
103 
104         model.transform_inits(context,
105                               disc_vector,
106                               unconstrained,
107                               &msg);
108       }
109     } catch (std::domain_error& e) {
110       if (msg.str().length() > 0)
111         logger.info(msg);
112       logger.info("Rejecting initial value:");
113       logger.info("  Error evaluating the log probability"
114                   " at the initial value.");
115       logger.info(e.what());
116       continue;
117     } catch (std::exception& e) {
118       if (msg.str().length() > 0)
119         logger.info(msg);
120       logger.info("Unrecoverable error evaluating the log probability"
121                   " at the initial value.");
122       logger.info(e.what());
123       throw;
124     }
125 
126     msg.str("");
127     double log_prob(0);
128     try {
129       // we evaluate the log_prob function with propto=false
130       // because we're evaluating with `double` as the type of
131       // the parameters.
132       log_prob = model.template log_prob<false, Jacobian>
133                  (unconstrained, disc_vector, &msg);
134       if (msg.str().length() > 0)
135         logger.info(msg);
136     } catch (std::domain_error& e) {
137       if (msg.str().length() > 0)
138         logger.info(msg);
139       logger.info("Rejecting initial value:");
140       logger.info("  Error evaluating the log probability"
141                   " at the initial value.");
142       logger.info(e.what());
143       continue;
144     } catch (std::exception& e) {
145       if (msg.str().length() > 0)
146         logger.info(msg);
147       logger.info("Unrecoverable error evaluating the log probability"
148                   " at the initial value.");
149       logger.info(e.what());
150       throw;
151     }
152     if (!boost::math::isfinite(log_prob)) {
153       logger.info("Rejecting initial value:");
154       logger.info("  Log probability evaluates to log(0),"
155                   " i.e. negative infinity.");
156       logger.info("  Stan can't start sampling from this"
157                   " initial value.");
158       continue;
159     }
160     std::stringstream log_prob_msg;
161     std::vector<double> gradient;
162     clock_t start_check = clock();
163     try {
164       // we evaluate this with propto=true since we're
165       // evaluating with autodiff variables
166       log_prob = stan::model::log_prob_grad<true, Jacobian>
167                  (model, unconstrained, disc_vector,
168                   gradient, &log_prob_msg);
169     } catch (const std::exception& e) {
170       if (log_prob_msg.str().length() > 0)
171         logger.info(log_prob_msg);
172       logger.info(e.what());
173       throw;
174     }
175     clock_t end_check = clock();
176     double deltaT = static_cast<double>(end_check - start_check)
177                     / CLOCKS_PER_SEC;
178     if (log_prob_msg.str().length() > 0)
179       logger.info(log_prob_msg);
180 
181     bool gradient_ok = boost::math::isfinite(stan::math::sum(gradient));
182 
183     if (!gradient_ok) {
184       logger.info("Rejecting initial value:");
185       logger.info("  Gradient evaluated at the initial value"
186                   " is not finite.");
187       logger.info("  Stan can't start sampling from this"
188                   " initial value.");
189     }
190     if (gradient_ok && print_timing) {
191       logger.info("");
192       std::stringstream msg1;
193       msg1 << "Gradient evaluation took " << deltaT << " seconds";
194       logger.info(msg1);
195 
196       std::stringstream msg2;
197       msg2 << "1000 transitions using 10 leapfrog steps"
198            << " per transition would take"
199            << " " << 1e4 * deltaT << " seconds.";
200       logger.info(msg2);
201 
202       logger.info("Adjust your expectations accordingly!");
203       logger.info("");
204       logger.info("");
205     }
206     if (gradient_ok) {
207       init_writer(unconstrained);
208       return unconstrained;
209     }
210   }
211 
212   if (!is_initialized_with_zero) {
213     logger.info("");
214     std::stringstream msg;
215     msg << "Initialization between (-" << init_radius
216         << ", " << init_radius << ") failed after"
217         << " " << MAX_INIT_TRIES <<  " attempts. ";
218     logger.info(msg);
219     logger.info(" Try specifying initial values,"
220                 " reducing ranges of constrained values,"
221                 " or reparameterizing the model.");
222   }
223   throw std::domain_error("Initialization failed.");
224 }
225 
226 }
227 }
228 }
229 #endif
230