MUQ  0.4.3
DRKernel.cpp
Go to the documentation of this file.
3 
7 
8 #include <iomanip>
9 
10 namespace pt = boost::property_tree;
11 using namespace muq::Utilities;
12 using namespace muq::Modeling;
13 using namespace muq::SamplingAlgorithms;
14 
16 
17 
18 DRKernel::DRKernel(pt::ptree const& pt,
19  std::shared_ptr<AbstractSamplingProblem> problem) : DRKernel(pt,
20  problem,
21  CreateProposals(pt, problem),
22  CreateScales(pt))
23 {
24 
25 
26 
27 }
28 
29 
30 void DRKernel::SetBlockInd(int newBlockInd)
31 {
32  blockInd = newBlockInd;
33  for(auto& proposal : proposals)
34  proposal->SetBlockInd(newBlockInd);
35 };
36 
37 DRKernel::DRKernel(boost::property_tree::ptree const& pt,
38  std::shared_ptr<AbstractSamplingProblem> problem,
39  std::vector<std::shared_ptr<MCMCProposal>> proposalsIn) : DRKernel(pt, problem, proposalsIn, std::vector<double>(proposalsIn.size(),1.0)){}
40 
41 DRKernel::DRKernel(pt::ptree const& pt,
42  std::shared_ptr<AbstractSamplingProblem> problem,
43  std::vector<std::shared_ptr<MCMCProposal>> proposalsIn,
44  std::vector<double> scales) : TransitionKernel(pt, problem),
45  proposals(proposalsIn),
46  propScales(scales)
47 {
48  // Update the unique set of proposals
49  for(auto& proposal : proposals)
50  uniqueProps.insert(proposal);
51 
52  numProposalCalls = Eigen::VectorXi::Zero(proposals.size());
53  numProposalAccepts = Eigen::VectorXi::Zero(proposals.size());
54 
55  for(unsigned int i=0; i<propScales.size(); ++i){
56  if(std::abs(propScales.at(i)-1.0)>std::numeric_limits<double>::epsilon()){
57  isScaled = true;
58  }
59  }
60 
61 }
62 
63 void DRKernel::PostStep(unsigned int const t,
64  std::vector<std::shared_ptr<SamplingState>> const& state){
65 
66  for(auto& proposal : uniqueProps)
67  proposal->Adapt(t,state);
68 }
69 
70 
71 std::shared_ptr<SamplingState> DRKernel::SampleProposal(unsigned int stage,
72  std::shared_ptr<SamplingState> const& state) const
73 {
74  std::shared_ptr<SamplingState> prop = proposals.at(stage)->Sample(state);
75  if(isScaled){
76  prop->state.at(blockInd) = (prop->state.at(blockInd) - state->state.at(blockInd))*propScales.at(stage) + state->state.at(blockInd);
77  }
78  return prop;
79 }
80 
81 double DRKernel::EvaluateProposal(unsigned int stage,
82  std::shared_ptr<SamplingState> const& x,
83  std::shared_ptr<SamplingState> const& y) const
84 {
85 
86  if(isScaled){
87  const double dim = x->state.at(blockInd).size();
88 
89  std::shared_ptr<SamplingState> yCopy = std::make_shared<SamplingState>(*y);
90  yCopy->state.at(blockInd) = x->state.at(blockInd) + (yCopy->state.at(blockInd) - x->state.at(blockInd))/propScales.at(stage);
91  return proposals.at(stage)->LogDensity(x, yCopy) - dim*std::log(propScales.at(stage));
92  }else{
93  return proposals.at(stage)->LogDensity(x,y);
94  }
95 }
96 
97 
98 
99 std::vector<std::shared_ptr<SamplingState>> DRKernel::Step(unsigned int const t,
100  std::shared_ptr<SamplingState> prevState){
101 
102  const unsigned int numStages = proposals.size();
103 
104  std::vector<double> logDensities; // vector previous density evaluations
105  logDensities.reserve(numStages);
106  std::vector<std::shared_ptr<SamplingState>> proposedPoints; // vector of previously proposed point
107  proposedPoints.reserve(numStages);
108 
109 
110  // If the previous state does not have a density value or it needs to be recomputed, evaluate the density
111  double currentTarget;
112  if( prevState->HasMeta("LogTarget") && !reeval ){
113  currentTarget = AnyCast( prevState->meta["LogTarget"]);
114  }else{
115  currentTarget = problem->LogDensity(prevState);
116  if (problem->numBlocksQOI > 0) {
117  prevState->meta["QOI"] = problem->QOI();
118  }
119  prevState->meta["LogTarget"] = currentTarget;
120  }
121 
122  logDensities.push_back( currentTarget );
123  proposedPoints.push_back( prevState );
124 
125  std::shared_ptr<SamplingState> proposedState;
126  Eigen::VectorXd alphas = Eigen::VectorXd::Zero(numStages);
127 
128  // loop over delayed rejection stages, will break after acceptance
129  for (int stage = 0; stage < numStages; ++stage) {
130 
131  // build and store proposal at next step:
132  numProposalCalls(stage)++;
133 
134  proposedState = SampleProposal(stage, prevState);
135 
136  // The following metadata is needed by the expensive sampling problem
137  if(prevState->HasMeta("iteration"))
138  proposedState->meta["iteration"] = proposedState->meta["iteration"];
139  proposedState->meta["IsProposal"] = true;
140 
141  // Evaluate the density at the proposal
142  double propTarget = problem->LogDensity(proposedState);
143  proposedState->meta["LogTarget"] = propTarget;
144 
145  // add proposed point to lists
146  logDensities.push_back( propTarget );
147  proposedPoints.push_back( proposedState );
148 
149  // call recursive routine to evaluate acceptance probability
150  alphas(stage) = Alpha(logDensities, proposedPoints);
151 
152 
153  // If accepted, return the accepted point
154  if (RandomGenerator::GetUniform() < alphas(stage)) {
155 
156  if (problem->numBlocksQOI > 0) {
157  proposedState->meta["QOI"] = problem->QOI();
158  }
159 
160  numProposalAccepts(stage)++;
161  proposedState->meta["IsProposal"] = false;
162 
163  return std::vector<std::shared_ptr<SamplingState>>(1, proposedState);
164  }
165 
166  } // end of loop over stages
167 
168  return std::vector<std::shared_ptr<SamplingState>>(1,prevState); //could only be here if we've rejected at every stage
169 }
170 
171 std::vector<double> DRKernel::CreateScales(boost::property_tree::ptree const& pt)
172 {
173  std::string proposalList = pt.get<std::string>("Proposal");
174  std::vector<std::string> proposalNames = StringUtilities::Split(proposalList);
175 
176  std::vector<double> output;
177 
178  // If the proposals are manually set, then set all the scales to one
179  if(proposalNames.size()>1){
180  output.resize(proposalNames.size(), 1.0);
181 
182  }else{
183 
184  int numStages = pt.get<int>("NumStages");
185  assert(numStages>0);
186 
187  std::string scaleType = pt.get<std::string>("ScaleFunction","Power");
188  double scale = pt.get("Scale", 2.0);
189 
190  output.resize(numStages);
191 
192  if(scaleType=="Power"){
193  for(int i=0; i<numStages; ++i)
194  output.at(i) = scale / std::pow(2.0, double(i));
195 
196  } else if(scaleType=="Linear") {
197  for(int i=0; i<numStages; ++i)
198  output.at(i) = scale / (double(i)+1.0);
199 
200  }else{
201  std::string msg = "ERROR: In DRKernel::CreateScales, invalid scale function \"" + scaleType + "\" specified in options. Valid options are \"Power\" or \"Linear\"";
202  throw std::invalid_argument(msg);
203  }
204  }
205 
206  return output;
207 
208 }
209 
210 void DRKernel::PrintStatus(const std::string prefix) const
211 {
212  std::stringstream msg;
213  msg << std::setprecision(2);
214  msg << prefix << "DR: Number of calls = " << numProposalCalls.transpose() << "\n";
215  msg << prefix << "DR: Cumulative Accept Rate = ";
216  double numCalls = static_cast<double>(numProposalCalls(0));
217  double rate = 100.0 * static_cast<double>(numProposalAccepts(0));
218  msg << std::setw(4) << std::fixed << std::setprecision(1) << rate / numCalls << "%";
219 
220  for (int i = 1; i < numProposalAccepts.size(); ++i) {
221  rate += 100.0 * static_cast<double>(numProposalAccepts(i));
222  msg << ", " << std::setw(4) << std::fixed << std::setprecision(1) << rate / numCalls << "%";
223  }
224 
225  std::cout << msg.str() << std::endl;
226 }
227 
228 std::vector<std::shared_ptr<MCMCProposal>> DRKernel::CreateProposals(pt::ptree const& pt, std::shared_ptr<AbstractSamplingProblem> const& problem)
229 {
230  // Extract the proposal parts from the ptree
231  std::string proposalList = pt.get<std::string>("Proposal");
232  std::vector<std::string> proposalNames = StringUtilities::Split(proposalList);
233  std::vector<std::shared_ptr<MCMCProposal>> proposals;
234 
235  // Generate all of the proposals
236  for(auto& proposalName : proposalNames){
237  boost::property_tree::ptree subTree = pt.get_child(proposalName);
238  subTree.put("BlockIndex", pt.get("BlockIndex",0));
239 
240  // Construct the proposal
241  auto proposal = MCMCProposal::Construct(subTree, problem);
242  assert(proposal);
243 
244  proposals.push_back(proposal);
245  }
246 
247  if(proposals.size()==1){
248  int numStages = pt.get("NumStages",-1);
249  if(numStages>1){
250  auto prop = proposals.at(0);
251  proposals.resize(numStages, prop);
252  }
253  }
254 
255  return proposals;
256 }
REGISTER_TRANSITION_KERNEL(DRKernel) DRKernel
Definition: DRKernel.cpp:15
An implementation of the delayed rejection kernel.
Definition: DRKernel.h:63
std::vector< double > propScales
Definition: DRKernel.h:155
double EvaluateProposal(unsigned int stage, std::shared_ptr< SamplingState > const &x, std::shared_ptr< SamplingState > const &y) const
Definition: DRKernel.cpp:81
double Alpha(VecType1 &likelies, VecType2 &proposed_points) const
Definition: DRKernel.h:167
std::set< std::shared_ptr< MCMCProposal > > uniqueProps
Definition: DRKernel.h:152
DRKernel(boost::property_tree::ptree const &pt, std::shared_ptr< AbstractSamplingProblem > problem)
static std::vector< double > CreateScales(boost::property_tree::ptree const &pt)
Definition: DRKernel.cpp:171
Eigen::VectorXi numProposalCalls
Definition: DRKernel.h:159
Eigen::VectorXi numProposalAccepts
Definition: DRKernel.h:162
std::vector< std::shared_ptr< MCMCProposal > > proposals
Definition: DRKernel.h:145
virtual std::vector< std::shared_ptr< SamplingState > > Step(unsigned int const t, std::shared_ptr< SamplingState > prevState) override
Definition: DRKernel.cpp:99
static std::vector< std::shared_ptr< MCMCProposal > > CreateProposals(boost::property_tree::ptree const &pt, std::shared_ptr< AbstractSamplingProblem > const &problem)
Definition: DRKernel.cpp:228
virtual void PostStep(unsigned int const t, std::vector< std::shared_ptr< SamplingState >> const &state) override
Allow the kernel to adapt given a new state.
Definition: DRKernel.cpp:63
std::shared_ptr< SamplingState > SampleProposal(unsigned int stage, std::shared_ptr< SamplingState > const &state) const
Definition: DRKernel.cpp:71
static std::shared_ptr< MCMCProposal > Construct(boost::property_tree::ptree const &pt, std::shared_ptr< AbstractSamplingProblem > const &probIn)
Static constructor for the transition kernel.
Defines the transition kernel used by an MCMC algorithm.
std::shared_ptr< AbstractSamplingProblem > problem
The sampling problem that evaluates/samples the target distribution.
const bool reeval
true: reevaluate the log density (even if one already exists), false: use stored log density
Class for easily casting boost::any's in assignment operations.
Definition: AnyHelpers.h:34
std::vector< std::string > Split(std::string str, char delim=',')
Split a string into parts based on a particular character delimiter. Also Strips whitespace from part...