1 #include "MUQ/SamplingAlgorithms/ExpensiveSamplingProblem.h"
2 
3 #include "MUQ/Utilities/RandomGenerator.h"
4 
5 #include "MUQ/Modeling/Distributions/Gaussian.h"
6 
7 #include "MUQ/SamplingAlgorithms/SamplingState.h"
8 
9 namespace pt = boost::property_tree;
10 using namespace muq::Utilities;
11 using namespace muq::Modeling;
12 using namespace muq::Approximation;
13 using namespace muq::SamplingAlgorithms;
14 
ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const & target,pt::ptree pt)15 ExpensiveSamplingProblem::ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const& target, pt::ptree pt) : SamplingProblem(target), centroid(Eigen::VectorXd::Zero(target->inputSizes(0))) {
16   // create the local regressor
17   reg = std::make_shared<LocalRegression>(target, pt.get_child(pt.get<std::string>("RegressionOptions")));
18 
19   // must becaused after reg is created
20   SetUp(pt);
21 }
22 
ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const & target,Eigen::VectorXd const & centroid,pt::ptree pt)23 ExpensiveSamplingProblem::ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const& target, Eigen::VectorXd const& centroid, pt::ptree pt) : SamplingProblem(target), centroid(centroid) {
24   // create the local regressor
25   reg = std::make_shared<LocalRegression>(target, pt.get_child(pt.get<std::string>("RegressionOptions")));
26 
27   // must becaused after reg is created
28   SetUp(pt);
29 }
30 
31 #if MUQ_HAS_PARCER
ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const & target,boost::property_tree::ptree pt,std::shared_ptr<parcer::Communicator> comm)32 ExpensiveSamplingProblem::ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const& target, boost::property_tree::ptree pt, std::shared_ptr<parcer::Communicator> comm) : SamplingProblem(target), centroid(Eigen::VectorXd::Zero(target->inputSizes(0))) {
33   // create the local regressor
34   reg = std::make_shared<LocalRegression>(target, pt.get_child(pt.get<std::string>("RegressionOptions")), comm);
35 
36   // must becaused after reg is created
37   SetUp(pt);
38 }
39 
ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const & target,Eigen::VectorXd const & centroid,boost::property_tree::ptree pt,std::shared_ptr<parcer::Communicator> comm)40 ExpensiveSamplingProblem::ExpensiveSamplingProblem(std::shared_ptr<muq::Modeling::ModPiece> const& target, Eigen::VectorXd const& centroid, boost::property_tree::ptree pt, std::shared_ptr<parcer::Communicator> comm) : SamplingProblem(target), centroid(centroid) {
41   // create the local regressor
42   reg = std::make_shared<LocalRegression>(target, pt.get_child(pt.get<std::string>("RegressionOptions")), comm);
43 
44   // must becaused after reg is created
45   SetUp(pt);
46 }
47 #endif
48 
SetUp(boost::property_tree::ptree & pt)49 void ExpensiveSamplingProblem::SetUp(boost::property_tree::ptree& pt) {
50   assert(reg);
51 
52   // can only have one input
53   assert(target->numInputs==1);
54 
55   beta = std::pair<double, double>(pt.get<double>("BetaScale", 0.0), -pt.get<double>("BetaExponent", RAND_MAX));
56   assert(beta.second<0.0);
57 
58   tau0 = pt.get<double>("FirstLevelLength", 1.0);
59 
60   gamma = std::pair<double, double>(std::log(pt.get<double>("GammaScale", 1.0)), pt.get<double>("GammaExponent", 1.0));
61   assert(gamma.second>0.0);
62 
63   lyapunovPara.first = pt.get<double>("LyapunovScale", 1.0);
64   lyapunovPara.second = pt.get<double>("LyapunovExponent", 1.0);
65   eta = pt.get<double>("TailCorrection", 0.0);
66 }
67 
LogDensity(unsigned int const step,std::shared_ptr<SamplingState> const & state,AbstractSamplingProblem::SampleType type)68 double ExpensiveSamplingProblem::LogDensity(unsigned int const step, std::shared_ptr<SamplingState> const& state, AbstractSamplingProblem::SampleType type) {
69   std::vector<Eigen::VectorXd> neighbors, results;
70 
71   bool addThreshold = (type==AbstractSamplingProblem::SampleType::Proposed);
72 
73   double threshold = RefineSurrogate(step, state, neighbors, results);
74 
75   if( type==AbstractSamplingProblem::SampleType::Accepted ) {
76     lastLyapunov = LogLyapunovFunction(state->state[0]);
77     lastThreshold = threshold;
78   }
79 
80   //double threshold = RefineSurrogate(step, state, neighbors, results);
81   assert(neighbors.size()==results.size());
82   assert(neighbors.size()>=reg->kn);
83 
84   // agument the threshold
85   threshold *= (lastLyapunov<LogLyapunovFunction(state->state[0]) ? -1.0 : 1.0);
86 
87   // set cumulative refinement
88   if( beta.first>0.0 ) { state->meta["cumulative random refinement"] = cumbeta; }
89   state->meta["cumulative structural refinement"] = cumgamma;
90 
91   const double logtarg = reg->EvaluateRegressor(state->state[0],
92                                 std::vector<Eigen::VectorXd>(neighbors.begin(), neighbors.begin()+reg->kn),
93                                 std::vector<Eigen::VectorXd>(results.begin(), results.begin()+reg->kn)) (0);
94 
95   return logtarg + (addThreshold ? eta*(lastThreshold+threshold)*state->state[0].norm()*std::fabs(logtarg) : 0.0);
96 }
97 
LogLyapunovFunction(Eigen::VectorXd const & x) const98 double ExpensiveSamplingProblem::LogLyapunovFunction(Eigen::VectorXd const& x) const {
99   /*(const Eigen::VectorXd diff = x-centroid;
100 
101   Eigen::VectorXd scale = Eigen::VectorXd::Constant(diff.size(), lyapunovScale);
102   scale(0) = 1.0;
103   scale(1) = 1.0;
104 
105   return diff.dot(scale.asDiagonal()*diff);*/
106 
107   //return lyapunovScale*(x-centroid).norm();
108   return lyapunovPara.first*std::pow((x-centroid).norm(), lyapunovPara.second);
109 }
110 
CheckNumNeighbors(std::shared_ptr<SamplingState> const & state)111 void ExpensiveSamplingProblem::CheckNumNeighbors(std::shared_ptr<SamplingState> const& state) {
112   while( reg->CacheSize()<reg->kn ) {
113     auto gauss = std::make_shared<muq::Modeling::Gaussian>(state->state[0], std::pow(std::exp(gamma.first), 1.0/(1.0+reg->Order()))*Eigen::VectorXd::Ones(state->state[0].size()));
114     const Eigen::VectorXd& x = gauss->Sample();
115     reg->Add(x);
116     ++cumkappa;
117   }
118 }
119 
CheckNeighbors(std::shared_ptr<SamplingState> const & state,std::vector<Eigen::VectorXd> & neighbors,std::vector<Eigen::VectorXd> & results) const120 void ExpensiveSamplingProblem::CheckNeighbors(
121   std::shared_ptr<SamplingState> const& state,
122   std::vector<Eigen::VectorXd>& neighbors,
123   std::vector<Eigen::VectorXd>& results) const {
124     reg->NearestNeighbors(state->state[0], neighbors, results);
125     assert(neighbors.size()==results.size());
126 }
127 
ErrorThreshold(Eigen::VectorXd const & x) const128 double ExpensiveSamplingProblem::ErrorThreshold(Eigen::VectorXd const& x) const {
129   return LogLyapunovFunction(x) + gamma.first - gamma.second*std::log((double)level);
130 }
131 
RefineSurrogate(std::shared_ptr<SamplingState> const & state,double const radius,std::vector<Eigen::VectorXd> & neighbors,std::vector<Eigen::VectorXd> & results)132 double ExpensiveSamplingProblem::RefineSurrogate(
133   std::shared_ptr<SamplingState> const& state,
134   double const radius,
135   std::vector<Eigen::VectorXd>& neighbors,
136   std::vector<Eigen::VectorXd>& results) {
137     // compute the poisedness constant
138     const std::tuple<Eigen::VectorXd, double, unsigned int>& lambda = reg->PoisednessConstant(state->state[0], neighbors);
139 
140     // if the point is already in the cache
141     if( reg->InCache(std::get<0>(lambda)) ) {
142       // choose a random point
143       Eigen::VectorXd point = Eigen::VectorXd::Random(state->state[0].size());
144       point *= RandomGenerator::GetUniform()*radius/point.norm();
145       point += state->state[0];
146 
147       // find the closest point
148       int index = 0;
149       double dist = RAND_MAX;
150       for( unsigned int i=0; i<reg->kn; ++i ) {
151         double newdist = (point-state->state[0]).norm();
152         if( newdist<dist ) { dist = newdist; index = i; }
153       }
154 
155       assert(dist>0.0);
156       assert(dist<=radius); // max is the diameter
157 
158       RefineSurrogate(point, index, neighbors, results);
159     } else {
160       RefineSurrogate(std::get<0>(lambda), std::get<2>(lambda), neighbors, results);
161     }
162 
163   return std::get<1>(lambda);
164 }
165 
RefineSurrogate(unsigned int const step,std::shared_ptr<SamplingState> const & state,std::vector<Eigen::VectorXd> & neighbors,std::vector<Eigen::VectorXd> & results)166 double ExpensiveSamplingProblem::RefineSurrogate(unsigned int const step, std::shared_ptr<SamplingState> const& state, std::vector<Eigen::VectorXd>& neighbors, std::vector<Eigen::VectorXd>& results) {
167   // make sure we have enough points
168   CheckNumNeighbors(state);
169 
170   // get the nearest nearest neighbors
171   CheckNeighbors(state, neighbors, results);
172 
173   // get the error indicator (using the first kn neighbors)
174   double error, radius;
175   std::tie(error, radius) = reg->ErrorIndicator(state->state[0], neighbors);
176 
177   // random refinement
178   if( RandomGenerator::GetUniform()<beta.first*std::pow((double)step, beta.second) ) {
179     RefineSurrogate(state, radius, neighbors, results);
180     ++cumbeta;
181 
182     // recompute the error indicator
183     std::tie(error, radius) = reg->ErrorIndicator(state->state[0], std::vector<Eigen::VectorXd>(neighbors.begin(), neighbors.begin()+reg->kn));
184   }
185 
186   // check to see if we should increment the level
187   if( step>tau0*std::pow((double)level, 2.0*gamma.second) ) { ++level; }
188 
189   // compute (and store) the error threshold
190   const double threshold = ErrorThreshold(state->state[0]);
191   state->meta["error threshold"] = std::exp(threshold);
192   state->meta["error indicator"] = std::exp(error);
193 
194   // structural refinement
195   if( error>threshold ) {
196     double lambda = RefineSurrogate(state, radius, neighbors, results);
197     ++cumgamma;
198   }
199 
200   return std::exp(threshold);
201 }
202 
RefineSurrogate(Eigen::VectorXd const & point,unsigned int const index,std::vector<Eigen::VectorXd> & neighbors,std::vector<Eigen::VectorXd> & results)203 void ExpensiveSamplingProblem::RefineSurrogate(Eigen::VectorXd const& point, unsigned int const index, std::vector<Eigen::VectorXd>& neighbors, std::vector<Eigen::VectorXd>& results) {
204   assert(!reg->InCache(point));
205   const Eigen::VectorXd& result = reg->Add(point);
206 
207   assert(neighbors.size()==results.size());
208   assert(index<neighbors.size());
209 
210   neighbors.push_back(point);
211   results.push_back(result);
212   std::iter_swap(neighbors.end()-1, neighbors.begin()+index);
213   std::iter_swap(results.end()-1, results.begin()+index);
214 }
215 
CacheSize() const216 unsigned int ExpensiveSamplingProblem::CacheSize() const { return reg->CacheSize(); }
217 
218 
AddOptions(boost::property_tree::ptree & pt) const219 void ExpensiveSamplingProblem::AddOptions(boost::property_tree::ptree & pt) const {
220   pt.put("ReevaluateAcceptedDensity", true);
221 }
222