1 #include "MUQ/SamplingAlgorithms/SingleChainMCMC.h"
2
3 #include <chrono>
4
5 #include "MUQ/Utilities/AnyHelpers.h"
6 #include "MUQ/Utilities/StringUtilities.h"
7
8 #include "MUQ/SamplingAlgorithms/MarkovChain.h"
9
10 namespace pt = boost::property_tree;
11 using namespace muq::Utilities;
12 using namespace muq::SamplingAlgorithms;
13
SingleChainMCMC(pt::ptree pt,std::shared_ptr<AbstractSamplingProblem> const & problem)14 SingleChainMCMC::SingleChainMCMC(pt::ptree pt,
15 std::shared_ptr<AbstractSamplingProblem> const& problem) :
16 SamplingAlgorithm(std::make_shared<MarkovChain>()),
17 printLevel(pt.get("PrintLevel",3))
18 {
19 Setup(pt, problem);
20 }
21
22 #if MUQ_HAS_PARCER
SingleChainMCMC(pt::ptree pt,std::shared_ptr<AbstractSamplingProblem> const & problem,std::shared_ptr<parcer::Communicator> const & comm)23 SingleChainMCMC::SingleChainMCMC(pt::ptree pt,
24 std::shared_ptr<AbstractSamplingProblem> const& problem,
25 std::shared_ptr<parcer::Communicator> const& comm) :
26 SamplingAlgorithm(std::make_shared<MarkovChain>(), comm),
27 printLevel(pt.get("PrintLevel",3))
28 {
29 Setup(pt, problem);
30 }
31
SingleChainMCMC(pt::ptree pt,std::shared_ptr<parcer::Communicator> const & comm,std::vector<std::shared_ptr<TransitionKernel>> const & kernelsIn)32 SingleChainMCMC::SingleChainMCMC(pt::ptree pt,
33 std::shared_ptr<parcer::Communicator> const& comm,
34 std::vector<std::shared_ptr<TransitionKernel> > const& kernelsIn) :
35 SamplingAlgorithm(std::make_shared<MarkovChain>(), comm),
36 printLevel(pt.get("PrintLevel",3))
37 {
38 Setup(pt, kernelsIn);
39 }
40
41 #endif
42
SingleChainMCMC(boost::property_tree::ptree pt,std::vector<std::shared_ptr<TransitionKernel>> const & kernelsIn)43 SingleChainMCMC::SingleChainMCMC(boost::property_tree::ptree pt,
44 std::vector<std::shared_ptr<TransitionKernel> > const& kernelsIn) :
45 SamplingAlgorithm(std::make_shared<MarkovChain>()),
46 printLevel(pt.get("PrintLevel",3))
47 {
48 Setup(pt,kernelsIn);
49 }
50
Setup(pt::ptree pt,std::vector<std::shared_ptr<TransitionKernel>> const & kernelsIn)51 void SingleChainMCMC::Setup(pt::ptree pt,
52 std::vector<std::shared_ptr<TransitionKernel>> const& kernelsIn) {
53
54 numSamps = pt.get<unsigned int>("NumSamples");
55 burnIn = pt.get("BurnIn",0);
56
57 kernels = kernelsIn;
58
59 scheduler = std::make_shared<ThinScheduler>(pt);
60 schedulerQOI = std::make_shared<ThinScheduler>(pt);
61 assert(scheduler);
62 assert(schedulerQOI);
63
64 #if MUQ_HAS_PARCER
65
66 for(int i=0; i<kernels.size(); ++i)
67 kernels.at(i)->SetCommunicator(comm);
68
69 #endif
70
71 }
72
Setup(pt::ptree pt,std::shared_ptr<AbstractSamplingProblem> const & problem)73 void SingleChainMCMC::Setup(pt::ptree pt, std::shared_ptr<AbstractSamplingProblem> const& problem) {
74
75 std::string kernelString = pt.get<std::string>("KernelList");
76
77 std::vector<std::string> kernelNames = StringUtilities::Split(kernelString, ',');
78
79 std::vector<std::shared_ptr<TransitionKernel>> kernelVec;
80 unsigned int numBlocks = kernelNames.size();
81 kernelVec.resize(numBlocks);
82
83 // Add the block id to a child tree and construct a kernel for each block
84 for(int i=0; i<numBlocks; ++i) {
85 boost::property_tree::ptree subTree = pt.get_child(kernelNames.at(i));
86 subTree.put("BlockIndex",i);
87
88 problem->AddOptions(subTree);
89 kernelVec.at(i) = TransitionKernel::Construct(subTree, problem);
90 }
91
92 Setup(pt, kernelVec);
93 }
94
PrintStatus(std::string prefix,unsigned int currInd) const95 void SingleChainMCMC::PrintStatus(std::string prefix, unsigned int currInd) const
96 {
97 std::cout << prefix << int(std::floor(double((currInd - 1) * 100) / double(numSamps))) << "% Complete" << std::endl;
98 if(printLevel>1){
99 for(int blockInd=0; blockInd<kernels.size(); ++blockInd){
100 std::cout << prefix << " Block " << blockInd << ":\n";
101 kernels.at(blockInd)->PrintStatus(prefix + " ");
102 }
103 }
104 }
105
RunImpl(std::vector<Eigen::VectorXd> const & x0)106 std::shared_ptr<SampleCollection> SingleChainMCMC::RunImpl(std::vector<Eigen::VectorXd> const& x0) {
107 if( !x0.empty() ) { SetState(x0); }
108
109 // What is the next iteration that we want to print at
110 const unsigned int printIncr = std::floor(numSamps / double(10));
111 unsigned int nextPrintInd = printIncr;
112
113 // Run until we've received the desired number of samples
114 if(printLevel>0)
115 std::cout << "Starting single chain MCMC sampler..." << std::endl;
116
117 while(sampNum < numSamps)
118 {
119 // Should we print
120 if(sampNum > nextPrintInd){
121 if(printLevel>0){
122 PrintStatus(" ", sampNum);
123 }
124 nextPrintInd += printIncr;
125 }
126
127 Sample();
128 }
129
130
131 if(printLevel>0){
132 PrintStatus(" ", numSamps+1);
133 std::cout << "Completed in " << totalTime << " seconds." << std::endl;
134 }
135
136 return samples;
137 }
138
Sample()139 void SingleChainMCMC::Sample() {
140 auto startTime = std::chrono::high_resolution_clock::now();
141
142 std::vector<std::shared_ptr<SamplingState> > newStates;
143
144 // Loop through each parameter block
145 for(int blockInd=0; blockInd<kernels.size(); ++blockInd){
146 // kernel prestep
147 kernels.at(blockInd)->PreStep(sampNum, prevState);
148
149 // use the kernel to get the next state(s)
150 newStates = kernels.at(blockInd)->Step(sampNum, prevState);
151
152 // save when these samples where created
153 const double now = std::chrono::duration<double>(std::chrono::high_resolution_clock::now()-startTime).count();
154 for( auto it=newStates.begin(); it!=newStates.end(); ++it ) { (*it)->meta["time"] = now; }
155
156 // kernel post-processing
157 kernels.at(blockInd)->PostStep(sampNum, newStates);
158
159 // add the new states to the SampleCollection (this also increments sampNum)
160 prevState = SaveSamples(newStates, lastSavedState, sampNum);
161 }
162
163 auto endTime = std::chrono::high_resolution_clock::now();
164 totalTime += std::chrono::duration<double>(endTime - startTime).count();
165 }
166
SaveSamples(std::vector<std::shared_ptr<SamplingState>> const & newStates,std::shared_ptr<SamplingState> & lastSavedState,unsigned int & sampNum) const167 std::shared_ptr<SamplingState> SingleChainMCMC::SaveSamples(std::vector<std::shared_ptr<SamplingState> > const& newStates, std::shared_ptr<SamplingState>& lastSavedState, unsigned int& sampNum) const {
168 for( auto it : newStates ) {
169 // save the sample, if we want to
170 if( ShouldSave(sampNum) ) {
171 samples->Add(it);
172 if (it->HasMeta("QOI")) {
173 std::shared_ptr<SamplingState> qoi = AnyCast(it->meta["QOI"]);
174 QOIs->Add(qoi);
175 }
176 }
177
178 // increment the number of samples and break of we hit the max. number
179 if( ++sampNum>=numSamps ) { return it; }
180 }
181
182 return newStates.back();
183 }
184
ShouldSave(unsigned int const sampNum) const185 bool SingleChainMCMC::ShouldSave(unsigned int const sampNum) const { return sampNum>=burnIn && scheduler->ShouldSave(sampNum); }
186
SetState(std::vector<Eigen::VectorXd> const & x0)187 void SingleChainMCMC::SetState(std::vector<Eigen::VectorXd> const& x0) {
188 prevState = std::make_shared<SamplingState>(x0);
189 samples->Add(prevState);
190 }
191