Doxygen 1.9.1
Toolkit for Adaptive Stochastic Modeling and Non-Intrusive ApproximatioN: Tasmanian v8.2 (development)
tsgDreamSample.hpp
Go to the documentation of this file.
1 /*
2  * Copyright (c) 2017, Miroslav Stoyanov
3  *
4  * This file is part of
5  * Toolkit for Adaptive Stochastic Modeling And Non-Intrusive ApproximatioN: TASMANIAN
6  *
7  * Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
8  *
9  * 1. Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
10  *
11  * 2. Redistributions in binary form must reproduce the above copyright notice, this list of conditions
12  * and the following disclaimer in the documentation and/or other materials provided with the distribution.
13  *
14  * 3. Neither the name of the copyright holder nor the names of its contributors may be used to endorse
15  * or promote products derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES,
18  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19  * IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
20  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
21  * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
22  * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
23  *
24  * UT-BATTELLE, LLC AND THE UNITED STATES GOVERNMENT MAKE NO REPRESENTATIONS AND DISCLAIM ALL WARRANTIES, BOTH EXPRESSED AND IMPLIED.
25  * THERE ARE NO EXPRESS OR IMPLIED WARRANTIES OF MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE, OR THAT THE USE OF THE SOFTWARE WILL NOT INFRINGE ANY PATENT,
26  * COPYRIGHT, TRADEMARK, OR OTHER PROPRIETARY RIGHTS, OR THAT THE SOFTWARE WILL ACCOMPLISH THE INTENDED RESULTS OR THAT THE SOFTWARE OR ITS USE WILL NOT RESULT IN INJURY OR DAMAGE.
27  * THE USER ASSUMES RESPONSIBILITY FOR ALL LIABILITIES, PENALTIES, FINES, CLAIMS, CAUSES OF ACTION, AND COSTS AND EXPENSES, CAUSED BY, RESULTING FROM OR ARISING OUT OF,
28  * IN WHOLE OR IN PART THE USE, STORAGE OR DISPOSAL OF THE SOFTWARE.
29  */
30 
31 #ifndef __TASMANIAN_DREAM_SAMPLE_HPP
32 #define __TASMANIAN_DREAM_SAMPLE_HPP
33 
34 #include "tsgDreamState.hpp"
35 #include "tsgDreamCoreRandom.hpp"
36 #include "tsgDreamCorePDF.hpp"
37 
60 namespace TasDREAM{
61 
69 using DreamDomain = std::function<bool(std::vector<double> const &x)>;
70 
75 inline DreamDomain hypercube(std::vector<double> const &lower, std::vector<double> const &upper){
76  return [=](const std::vector<double> &x)->bool{
77  auto il = lower.begin(), iu = upper.begin();
78  for(auto v : x) if ((v < *il++) || (v > *iu++)) return false;
79  return true;
80  };
81 }
82 
89 inline void no_update(std::vector<double> &){}
90 
97 inline double const_one(){ return 1.0; }
98 
110 template<int weight_percent>
111 double const_percent(){ return ((double) weight_percent) / 100.0; }
112 
120 inline void uniform_prior(TypeSamplingForm, const std::vector<double> &, std::vector<double> &values){ values.clear(); }
121 
142 using DreamPDF = std::function<void(const std::vector<double> &candidates, std::vector<double> &values)>;
143 
157 using DreamModel = std::function<void(const std::vector<double> &candidates, std::vector<double> &outputs)>;
158 
175 using DreamLikelihood = std::function<void(TypeSamplingForm form, const std::vector<double> &model_outputs, std::vector<double> &likely)>;
176 
187 using DreamPrior = std::function<void(TypeSamplingForm form, const std::vector<double> &candidates, std::vector<double> &values)>;
188 
200 using DreamMergedLikelyModel = std::function<void(const std::vector<double> &candidates, std::vector<double> &values)>;
201 
259 template<TypeSamplingForm form = regform>
261  DreamLikelihood likelihood,
262  DreamPrior prior){
263  return [=](const std::vector<double> &candidates, std::vector<double> &values)->void{
264  std::vector<double> model_outs;
265  model(candidates, model_outs);
266  likelihood(form, model_outs, values);
267 
268  std::vector<double> prior_vals(values.size());
269  prior(form, candidates, prior_vals);
270 
271  auto iv = values.begin();
272  if (form == regform){
273  for(auto p : prior_vals) *iv++ *= p;
274  }else{
275  for(auto p : prior_vals) *iv++ += p;
276  }
277  };
278 }
279 
290 template<TypeSamplingForm form = regform>
292  DreamPrior prior){
293  return [=](const std::vector<double> &candidates, std::vector<double> &values)->void{
294  likelihood_model(candidates, values);
295 
296  std::vector<double> prior_vals(values.size());
297  prior(form, candidates, prior_vals);
298 
299  auto iv = values.begin();
300  if (form == regform){
301  for(auto p : prior_vals) *iv++ *= p;
302  }else{
303  for(auto p : prior_vals) *iv++ += p;
304  }
305  };
306 }
307 
308 
413 template<TypeSamplingForm form = regform>
414 void SampleDREAM(int num_burnup, int num_collect,
415  DreamPDF probability_distribution,
416  DreamDomain inside,
417  TasmanianDREAM &state,
418  std::function<void(std::vector<double> &x)> independent_update = no_update,
419  std::function<double(void)> differential_update = const_one,
420  std::function<double(void)> get_random01 = tsgCoreUniform01){
421 
422  size_t num_chains = (size_t) state.getNumChains(), num_dimensions = (size_t) state.getNumDimensions();
423  double unitlength = (double) num_chains;
424 
425  if (num_chains == 0) return; // no sampling with a null state
426 
427  if (!state.isStateReady()) throw std::runtime_error("ERROR: DREAM sampling requires that the setState() has been called first on the TasmanianDREAM.");
428 
429  if (!state.isPDFReady()) // initialize probability density (if not initialized already)
430  state.setPDFvalues(probability_distribution);
431 
432  if (num_collect > 0) // pre-allocate memory for the new history
433  state.expandHistory(num_collect);
434 
435  int total_iterations = std::max(num_burnup, 0) + std::max(num_collect, 0);
436  for(int t = 0; t < total_iterations; t++){
437  std::vector<double> candidates, values;
438  candidates.reserve(num_chains * num_dimensions);
439  values.reserve(num_chains);
440 
441  std::vector<bool> valid(num_chains, true); // keep track whether the samples need to be evaluated
442 
443  for(size_t i=0; i<num_chains; i++){
444  std::vector<double> propose(num_dimensions);
445 
446  size_t jindex = (size_t) (get_random01() * unitlength);
447  size_t kindex = (size_t) (get_random01() * unitlength);
448  if (jindex >= num_chains) jindex = num_chains - 1; // this is needed in case get_random01() returns 1
449  if (kindex >= num_chains) jindex = num_chains - 1;
450 
451  state.getIJKdelta(i, jindex, kindex, differential_update(), propose); // propose = s_i + w ( s_k - s_j)
452  independent_update(propose); // propose += correction
453 
454  if (inside(propose)){
455  candidates.insert(candidates.end(), propose.begin(), propose.end());
456  values.resize(values.size() + 1);
457  }else{
458  valid[i] = false;
459  }
460  }
461 
462  if (!candidates.empty()) // block the pathological case of all proposals leaving the domain
463  probability_distribution(candidates, values);
464 
465  std::vector<double> new_state(num_chains * num_dimensions), new_values(num_chains);
466 
467  auto icand = candidates.begin(); // loop over all candidates and values, accept or reject
468  auto ival = values.begin();
469 
470  size_t accepted = 0;
471 
472  for(size_t i=0; i<num_chains; i++){
473  bool keep_new = valid[i]; // if not valid, automatically reject
474  if (valid[i]){ // apply random test
475  if (*ival > state.getPDFvalue(i)){ // if the new value has higher probability, automatically accept
476  keep_new = true;
477  }else{
478  if (form == regform){
479  keep_new = (*ival / state.getPDFvalue(i) >= get_random01()); // keep if the new value has higher probability
480  }else{
481  keep_new = (*ival - state.getPDFvalue(i) >= log(get_random01()));
482  }
483  //std::cout << "Trsh = " << *ival / state.getPDFvalue(i) << " " << ((keep_new) ? "Accept" : "Reject") << std:: endl;
484  }
485  }
486 
487  if (keep_new){
488  std::copy_n(icand, num_dimensions, new_state.begin() + i * num_dimensions);
489  new_values[i] = *ival;
490  accepted++; // accepted one more proposal
491  }else{ // reject and reuse the old state
492  state.getChainState((int) i, &*(new_state.begin() + i * num_dimensions));
493  new_values[i] = state.getPDFvalue(i);
494  }
495 
496  if (valid[i]){ // kept or rejected, if this sample was valid then move to the next sample in the list
497  std::advance(icand, num_dimensions);
498  ival++;
499  }
500  }
501 
502  state.setState(new_state);
503  state.setPDFvalues(new_values);
504 
505  if (t >= num_burnup)
506  state.saveStateHistory(accepted);
507  }
508 }
509 
510 
520 template<TypeSamplingForm form = regform>
521 void SampleDREAM(int num_burnup, int num_collect,
522  DreamPDF probability_distribution,
523  DreamDomain inside,
524  TasmanianDREAM &state,
525  TypeDistribution dist, double magnitude,
526  std::function<double(void)> differential_update = const_one,
527  std::function<double(void)> get_random01 = tsgCoreUniform01){
528  if (dist == dist_uniform){
529  SampleDREAM<form>(num_burnup, num_collect, probability_distribution, inside, state,
530  [&](std::vector<double> &x)->void{ applyUniformUpdate(x, magnitude, get_random01); }, differential_update, get_random01);
531  }else if (dist == dist_gaussian){
532  SampleDREAM<form>(num_burnup, num_collect, probability_distribution, inside, state,
533  [&](std::vector<double> &x)->void{ applyGaussianUpdate(x, magnitude, get_random01); }, differential_update, get_random01);
534  }else{ // assuming none
535  SampleDREAM<form>(num_burnup, num_collect, probability_distribution, inside, state, no_update, differential_update, get_random01);
536  }
537 }
538 
539 }
540 
541 #endif
Contains the current state and the history of the DREAM chains.
Definition: tsgDreamState.hpp:70
double getPDFvalue(size_t i) const
Return the value of the probability_distribution of the i-th chain.
Definition: tsgDreamState.hpp:155
void saveStateHistory(size_t num_accepted)
Appends the current state to the history.
int getNumDimensions() const
Return the number of dimensions.
Definition: tsgDreamState.hpp:91
void setPDFvalues(const std::vector< double > &new_values)
Set the current set of pdf values to the new_values.
bool isStateReady() const
Return true if the state has already been initialized with setState().
Definition: tsgDreamState.hpp:98
void getChainState(size_t i, double *x) const
Return the state of the i-th chain, store an array x of size at least num_dimensions.
Definition: tsgDreamState.hpp:147
void expandHistory(int num_snapshots)
Allocate (expand) internal storage for the history snapshots, avoids reallocating data when saving a ...
void getIJKdelta(size_t i, size_t j, size_t k, double w, std::vector< double > &x) const
Used by the DREAM sampler, , where s indicates the current i, j, and k-th state vectors.
int getNumChains() const
Return the number of chains.
Definition: tsgDreamState.hpp:93
bool isPDFReady() const
Return true if the pdf values have already been initialized with setPDF().
Definition: tsgDreamState.hpp:100
void setState(const std::vector< double > &new_state)
Set the current DREAM state to the new_state.
TypeDistribution
Indicates a specific probability distribution for the associated function.
Definition: tsgDreamEnumerates.hpp:102
TypeSamplingForm
Describes whether sampling should be done with the regular or logarithm form of the probability densi...
Definition: tsgDreamEnumerates.hpp:90
@ dist_gaussian
Gaussian or Normal distribution defined by mean and variance.
Definition: tsgDreamEnumerates.hpp:107
@ dist_uniform
Uniform distribution.
Definition: tsgDreamEnumerates.hpp:104
@ regform
Use the standard form for the probability density.
Definition: tsgDreamEnumerates.hpp:92
void applyGaussianUpdate(std::vector< double > &x, double magnitude, std::function< double(void)> get_random01=tsgCoreUniform01)
Add a correction to every entry in x, sue Gaussian distribution with zero mean and standard deviation...
Definition: tsgDreamCoreRandom.hpp:77
double tsgCoreUniform01()
Default random sampler, using rand() divided by RAND_MAX.
Definition: tsgDreamCoreRandom.hpp:61
void applyUniformUpdate(std::vector< double > &x, double magnitude, std::function< double(void)> get_random01=tsgCoreUniform01)
Add a correction to every entry in x, use uniform samples over (-magnitude, magnitude).
Definition: tsgDreamCoreRandom.hpp:67
std::function< void(const std::vector< double > &candidates, std::vector< double > &values)> DreamMergedLikelyModel
Generic signature for a combination of a likelihood and a model.
Definition: tsgDreamSample.hpp:200
double const_percent()
Template that returns a constant based on the percentage, i.e., weight_percent / 100....
Definition: tsgDreamSample.hpp:111
void no_update(std::vector< double > &)
Dummy function that does not make any changes to the vector as default for the independent_update() i...
Definition: tsgDreamSample.hpp:89
std::function< void(const std::vector< double > &candidates, std::vector< double > &outputs)> DreamModel
Generic model signature used by Tasmanian.
Definition: tsgDreamSample.hpp:157
void SampleDREAM(int num_burnup, int num_collect, DreamPDF probability_distribution, DreamDomain inside, TasmanianDREAM &state, std::function< void(std::vector< double > &x)> independent_update=no_update, std::function< double(void)> differential_update=const_one, std::function< double(void)> get_random01=tsgCoreUniform01)
Core template for the sampling algorithm.
Definition: tsgDreamSample.hpp:414
DreamDomain hypercube(std::vector< double > const &lower, std::vector< double > const &upper)
Make a lambda that matches the inside signature in SampleDREAM(), test if the vector x is in the hype...
Definition: tsgDreamSample.hpp:75
DreamPDF posterior(DreamModel model, DreamLikelihood likelihood, DreamPrior prior)
Combines the three components of a Bayesian posterior into a single distribution.
Definition: tsgDreamSample.hpp:260
void uniform_prior(TypeSamplingForm, const std::vector< double > &, std::vector< double > &values)
Uniform prior distribution for both regular and log form.
Definition: tsgDreamSample.hpp:120
double const_one()
Dummy function that returns 1.0, used as default for the differential_update() in SampleDREAM().
Definition: tsgDreamSample.hpp:97
std::function< void(const std::vector< double > &candidates, std::vector< double > &values)> DreamPDF
Generic probability distribution used by Tasmanian.
Definition: tsgDreamSample.hpp:142
std::function< void(TypeSamplingForm form, const std::vector< double > &model_outputs, std::vector< double > &likely)> DreamLikelihood
Generic likelihood signature used by Tasmanian.
Definition: tsgDreamSample.hpp:175
std::function< void(TypeSamplingForm form, const std::vector< double > &candidates, std::vector< double > &values)> DreamPrior
Generic signature for the prior distributions used by Tasmanian.
Definition: tsgDreamSample.hpp:187
std::function< bool(std::vector< double > const &x)> DreamDomain
Generic test function whether a sample belongs in the domain.
Definition: tsgDreamSample.hpp:69
Encapsulates the Tasmanian DREAM module.
Definition: TasmanianDREAM.hpp:80
Gives the unscaled formulas for several probability distributions.
Core random sampling methods.
The container class holding the DREAM history.