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