MUQ  0.4.3
GaussianProcess.cpp
Go to the documentation of this file.
4 
5 using namespace muq::Approximation;
6 using namespace muq::Modeling;
7 using namespace muq::Utilities;
8 
9 double muq::Approximation::nlopt_obj(unsigned n, const double *x, double *nlopt_grad, void *opt_info)
10 {
11 
12  Eigen::Map<const Eigen::VectorXd> params(x,n);
13  double logLikely;
14 
15  OptInfo* info = (OptInfo*) opt_info;
16 
17  info->gp->Kernel()->SetParams(params);
18 
19  if(nlopt_grad){
20  Eigen::Map<Eigen::VectorXd> grad(nlopt_grad, n);
21  logLikely = info->gp->MarginalLogLikelihood(grad);
22  }else{
23  logLikely = info->gp->MarginalLogLikelihood();
24  }
25 
26  return logLikely;
27 }
28 
29 
30 GaussianProcess::GaussianProcess(std::shared_ptr<MeanFunctionBase> meanIn,
31  std::shared_ptr<KernelBase> covKernelIn) :
32  mean(meanIn),
33  covKernel(covKernelIn),
34  inputDim(covKernel->inputDim),
35  coDim(covKernel->coDim),
36  hasNewObs(false)
37 {};
38 
39 GaussianProcess& GaussianProcess::Condition(Eigen::Ref<const Eigen::MatrixXd> const& loc,
40  Eigen::Ref<const Eigen::MatrixXd> const& vals,
41  double obsVar)
42 {
43  auto H = std::make_shared<IdentityOperator>(coDim);
44 
45  assert(loc.cols()>0);
46  assert(vals.cols()==loc.cols());
47  assert(vals.rows()==coDim);
48  assert(obsVar>=0);
49 
50  for(int i=0; i<loc.cols(); ++i){
51  auto obs = std::make_shared<ObservationInformation>(H,
52  loc.col(i),
53  vals.col(i),
54  obsVar*Eigen::MatrixXd::Identity(coDim,coDim));
55 
56  Condition(obs);
57  }
58 
59  return *this;
60 }
61 
62 GaussianProcess& GaussianProcess::Condition(std::shared_ptr<ObservationInformation> obs)
63 {
64  observations.push_back(obs);
65  hasNewObs = true;
66 
67  return *this;
68 }
69 
70 std::shared_ptr<muq::Modeling::Gaussian> GaussianProcess::Discretize(Eigen::MatrixXd const& pts)
71 {
72  Eigen::MatrixXd mean;
73  Eigen::MatrixXd cov;
74 
75  std::tie(mean,cov) = Predict(pts, GaussianProcess::FullCov);
76 
77  Eigen::Map<Eigen::VectorXd> meanMap(mean.data(), mean.rows()*mean.cols());
78 
79  return std::make_shared<muq::Modeling::Gaussian>(meanMap,cov);
80 }
81 
82 Eigen::MatrixXd GaussianProcess::SolveFromEig(Eigen::MatrixXd const& b) const
83 {
84  auto& vecs = covSolverEig.eigenvectors();
85  auto& vals = covSolverEig.eigenvalues();
86 
87  const double tol = std::max(std::numeric_limits<double>::epsilon(), 1e-12*vals.maxCoeff());
88 
89  int numPos = 0;
90  for(int i=vals.size()-1; i>=0; --i){
91  if(vals(i)>tol){
92  numPos++;
93  }else{
94  break;
95  }
96  }
97 
98  return vecs.rightCols(numPos) * vals.tail(numPos).cwiseInverse().asDiagonal() * vecs.rightCols(numPos).transpose() * b;
99 }
100 
102 {
103  if((hasNewObs)&&(observations.size()>0)){
104 
105  obsDim = 0;
106  for(int i=0; i<observations.size(); ++i)
107  obsDim += observations.at(i)->H->rows();
108 
109  // Build the covariance between observation locations
110  Eigen::MatrixXd trainCov(obsDim, obsDim);
111  int currCol = 0;
112  for(int j=0; j<observations.size(); ++j)
113  {
114  int currRow = 0;
115  for(int i=0; i<j; ++i){
116  observations.at(i)->FillCrossCov(observations.at(j), covKernel, trainCov.block(currRow, currCol, observations.at(i)->H->rows(), observations.at(j)->H->rows()));
117  currRow += observations.at(i)->H->rows();
118  }
119  //covKernel->BuildCovariance(observations.at(j)->loc, observations.at(j)->loc);
120  observations.at(j)->FillSelfCov(covKernel, trainCov.block(currRow, currRow, observations.at(j)->H->rows(), observations.at(j)->H->rows()));
121  currCol += observations.at(j)->H->rows();
122  }
123 
124  trainCov.triangularView<Eigen::Lower>() = trainCov.triangularView<Eigen::Upper>().transpose();
125 
126  covSolver.compute(trainCov.selfadjointView<Eigen::Lower>());//.ldlt();
127  useEig = false;
128 
129  // If the LDLT solver failed, try an eigenvalue solver
130  if(covSolver.info() != Eigen::Success){
131  covSolverEig.compute(trainCov);
132  useEig = true;
133  assert(covSolverEig.info()==Eigen::Success);
134  }
135 
136  // Evaluate the mean function
137  Eigen::VectorXd trainDiff(obsDim);
138  int currRow = 0;
139  for(int i=0; i<observations.size(); ++i){
140  auto derivObs = std::dynamic_pointer_cast<DerivativeObservation>(observations.at(i));
141  if(derivObs){
142  trainDiff.segment(currRow, observations.at(i)->H->rows()) = observations.at(i)->obs - observations.at(i)->H->Apply(mean->GetDerivative(observations.at(i)->loc, derivObs->derivCoords));
143  }else{
144  trainDiff.segment(currRow, observations.at(i)->H->rows()) = observations.at(i)->obs - observations.at(i)->H->Apply(mean->Evaluate(observations.at(i)->loc));
145  }
146  currRow += observations.at(i)->H->rows();
147  }
148 
149  if(useEig){
151  }else{
153  }
154 
155  hasNewObs = false;
156  }
157 }
158 
159 
161 {
163 
164  OptInfo info;
165  info.gp = this;
166 
167  Eigen::VectorXd params = covKernel->GetParams();
168 
169  const Eigen::MatrixXd bounds = covKernel->GetParamBounds();
170  Eigen::VectorXd lbs = bounds.row(0);
171  Eigen::VectorXd ubs = bounds.row(1);
172 
173 
174  // nlopt_opt opt;
175 
176  // opt = nlopt_create(NLOPT_LD_LBFGS, params.rows()); /* algorithm and dimensionality */
177  // nlopt_set_lower_bounds(opt, lbs.data());
178  // nlopt_set_upper_bounds(opt, ubs.data());
179  // nlopt_set_vector_storage(opt, 100);
180  // nlopt_set_max_objective(opt, muq::Approximation::nlopt_obj, (void*) &info);
181 
182  // double maxLikely;
183  // nlopt_optimize(opt, params.data(), &maxLikely);
184 
185  // covKernel->SetParams(params);
186 
187  // nlopt_destroy(opt);
188 }
189 
190 
191 Eigen::MatrixXd GaussianProcess::BuildCrossCov(Eigen::MatrixXd const& newLocs)
192 {
193  Eigen::MatrixXd crossCov( obsDim, coDim*newLocs.cols());
194  int currCol = 0;
195 
196  for(int j=0; j<observations.size(); ++j){
197  for(int i=0; i<newLocs.cols(); ++i){
198  observations.at(j)->FillCrossCov(newLocs.col(i), covKernel, crossCov.block(currCol, i*coDim, observations.at(j)->H->rows(), coDim));
199 
200  // Eigen::MatrixXd temp = covKernel
201  // Eigen::MatrixXd temp = covKernel->BuildCovariance(newLocs.col(i), observations.at(j)->loc);
202  // Eigen::MatrixXd temp2 = observations.at(j)->H->Apply( covKernel->BuildCovariance(newLocs.col(i), observations.at(j)->loc));
203  // crossCov.block(i*coDim, currCol, coDim, observations.at(j)->H->rows()) = observations.at(j)->H->Apply( covKernel->BuildCovariance(newLocs.col(i), observations.at(j)->loc)).transpose();
204  }
205  currCol += observations.at(j)->H->rows();
206  }
207 
208  return crossCov.transpose();
209 }
210 std::pair<Eigen::MatrixXd, Eigen::MatrixXd> GaussianProcess::Predict(Eigen::MatrixXd const& newLocs,
211  CovarianceType covType)
212 {
213 
215 
216  // Get the cross covariance between the evaluation points and the observations
217  Eigen::MatrixXd crossCov;
218 
219  Eigen::MatrixXd outputMean(coDim, newLocs.cols());
220  Eigen::MatrixXd outputCov;
221 
222  Eigen::Map<Eigen::VectorXd> postMean(outputMean.data(), coDim*newLocs.cols());
223 
224  if(observations.size()==0){
225  outputMean = mean->Evaluate(newLocs);
226  assert(outputMean.rows()==coDim);
227 
228  }else{
229  // Get the cross covariance between the evaluation points and the observations
230  crossCov = BuildCrossCov(newLocs);
231 
232  postMean = crossCov * sigmaTrainDiff;//covSolver.solve(trainDiff);
233  outputMean += mean->Evaluate(newLocs);
234  }
235 
236  // Only compute the prediction variances, not covariance
237  if(covType == GaussianProcess::DiagonalCov){
238 
239  outputCov.resize(coDim, newLocs.cols());
240 
241  Eigen::MatrixXd priorCov(coDim, coDim);
242  for(int i=0; i<newLocs.cols(); ++i){
243  covKernel->FillCovariance(newLocs.col(i),priorCov);
244 
245  if(observations.size()>0){
246  for(int d=0; d<coDim; ++d){
247  if(useEig){
248  outputCov(d,i) = priorCov(d,d) - crossCov.row(i*coDim+d).dot( SolveFromEig(crossCov.row(i*coDim+d).transpose()).col(0) );
249  }else{
250  outputCov(d,i) = priorCov(d,d) - crossCov.row(i*coDim+d) * covSolver.solve(crossCov.row(i*coDim+d).transpose());
251  }
252  }
253  }else{
254  for(int d=0; d<coDim; ++d)
255  outputCov(d,i) = priorCov(d,d);
256  }
257  }
258 
259  // Predict marginal covariance at each point, but not the covariance between points
260  }else if(covType==GaussianProcess::BlockCov){
261 
262  outputCov.resize(coDim, coDim*newLocs.cols());
263 
264  Eigen::MatrixXd priorCov(coDim, coDim);
265  for(int i=0; i<newLocs.cols(); ++i){
266  covKernel->FillCovariance(newLocs.col(i),priorCov);
267 
268  if(observations.size()>0){
269  if(useEig){
270  outputCov.block(0,coDim*i,coDim,coDim) = priorCov - crossCov.block(i*coDim,0,coDim,crossCov.cols()) * SolveFromEig(crossCov.block(i*coDim,0,coDim,crossCov.cols()).transpose());
271  }else{
272  outputCov.block(0,coDim*i,coDim,coDim) = priorCov - crossCov.block(i*coDim,0,coDim,crossCov.cols()) * covSolver.solve(crossCov.block(i*coDim,0,coDim,crossCov.cols()).transpose());
273  }
274  }else{
275  outputCov.block(0,coDim*i,coDim,coDim) = priorCov;
276  }
277  }
278 
279 
280  // Compute the full joint covariance of all predictions
281  }else if(covType==GaussianProcess::FullCov){
282 
283  Eigen::MatrixXd priorCov(coDim*newLocs.cols(), coDim*newLocs.cols());
284  covKernel->FillCovariance(newLocs,priorCov);
285 
286  // Solve for the posterior covariance
287  if(observations.size()>0){
288  if(useEig){
289  outputCov = priorCov - crossCov * SolveFromEig(crossCov.transpose());
290  }else{
291  outputCov = priorCov - crossCov * covSolver.solve(crossCov.transpose());
292  }
293  }else{
294  outputCov = priorCov;
295  }
296  }
297 
298  // Add a nugget to the covariance diagonal
299  outputCov += nugget*Eigen::MatrixXd::Identity(outputCov.rows(), outputCov.cols());
300  return std::make_pair(outputMean, outputCov);
301 };
302 
303 
304 Eigen::MatrixXd GaussianProcess::PredictMean(Eigen::MatrixXd const& newPts)
305 {
306  if(observations.size()==0)
307  return mean->Evaluate(newPts);
308 
310 
311  // Construct the cross covariance
312  Eigen::MatrixXd crossCov = BuildCrossCov(newPts);
313 
314  Eigen::MatrixXd meanMat(coDim, newPts.size());
315  Eigen::Map<Eigen::VectorXd> meanVec(meanMat.data(), coDim*newPts.cols());
316 
317  meanVec = crossCov * sigmaTrainDiff;
318  meanMat += mean->Evaluate(newPts);
319 
320  return meanMat;
321 }
322 
323 
324 Eigen::MatrixXd GaussianProcess::Sample(Eigen::MatrixXd const& newPts)
325 {
327 
328  Eigen::MatrixXd mean, covariance;
329  std::tie(mean,covariance) = Predict(newPts, GaussianProcess::FullCov);
330 
331  Eigen::MatrixXd output(mean.rows(), mean.cols());
332  Eigen::Map<Eigen::VectorXd> outVec(output.data(), output.rows()*output.cols());
333 
334  // First, try using a Cholesky factorization because it's faster
335  auto fact = covariance.selfadjointView<Eigen::Lower>().llt();
336  if(fact.info() == Eigen::Success){
337  outVec = fact.matrixL()*RandomGenerator::GetNormal(covariance.rows());
338 
339  // If the llt factorization failed, try an eigenvalue decomp
340  }else{
341 
342  Eigen::SelfAdjointEigenSolver<Eigen::MatrixXd> solver(covariance);
343  auto& vals = solver.eigenvalues();
344  auto& vecs = solver.eigenvectors();
345 
346  int numPos = 0;
347  for(int i=vals.size()-1; i>=0; --i){
348  if(vals(i)>std::sqrt(std::numeric_limits<double>::epsilon())){
349  numPos++;
350  }else{
351  break;
352  }
353  }
354 
355  outVec = vecs.rightCols(numPos)*vals.tail(numPos).cwiseSqrt().asDiagonal()*RandomGenerator::GetNormal(numPos);
356  }
357 
358  output += mean;
359 
360  return output;
361 }
362 
363 double GaussianProcess::LogLikelihood(Eigen::MatrixXd const& xs,
364  Eigen::MatrixXd const& vals)
365 {
366 
368 
369  Eigen::MatrixXd mean, covariance;
370  std::tie(mean,covariance) = Predict(xs, GaussianProcess::FullCov);
371 
372  const Eigen::Map<const Eigen::VectorXd> valMap(vals.data(), vals.rows()*vals.cols());
373  Eigen::Map<Eigen::VectorXd> muMap(mean.data(), mean.rows()*mean.cols());
374 
375  Eigen::VectorXd diff = valMap-muMap;
376  auto solver = covariance.selfadjointView<Eigen::Lower>().llt();
377  double logDet = 0;
378  for(int i=0; i<solver.matrixL().rows(); ++i)
379  logDet += 2.0*log(solver.matrixL()(i,i));
380 
381  double logDens = -0.5*diff.dot( solver.solve(diff) ) - 0.5*logDet - 0.5*valMap.size()*log(2.0*M_PI);
382  return logDens;
383 }
384 
385 
387 {
388  Eigen::VectorXd grad;
389  return MarginalLogLikelihood(grad,false);
390 }
391 
392 // Evaluates the log marginal likelihood needed when fitting hyperparameters
393 double GaussianProcess::MarginalLogLikelihood(Eigen::Ref<Eigen::VectorXd> grad,
394  bool computeGrad)
395 {
396  assert(false);
397  // ProcessObservations();
398  //
399  // // Compute the log determinant of the covariance
400  // Eigen::MatrixXd L = covSolver.matrixL();
401  // L = (L*covSolver.vectorD().cwiseSqrt().asDiagonal()).eval();
402  // L = (covSolver.transpositionsP().transpose() * L).eval();
403  // double logDet = 0.0;
404  // for(int i=0; i<L.rows(); ++i)
405  // logDet += 2.0*log(L(i,i));
406  //
407  // // Make the mean prediction and compute the difference with observations
408  // double logLikely = -0.5 * trainDiff.transpose() * sigmaTrainDiff - 0.5*logDet - 0.5*observations.size()*log(2.0*pi);
409  //
410  // if(computeGrad)
411  // {
412  // // Compute the gradient of the log likelihood
413  // const int numParams = covKernel->numParams;
414  // grad.resize(numParams);
415  //
416  // for(int p=0; p<numParams; ++p)
417  // {
418  // // Build the derivative matrix
419  // Eigen::MatrixXd derivMat(obsDim, obsDim);
420  // Eigen::MatrixXd tempDerivMat;
421  // int currRow=0;
422  // int currCol=0;
423  //
424  // for(int j=0; j<observations.size(); ++j)
425  // {
426  // currRow = 0;
427  // for(int i=0; i<=j; ++i)
428  // {
429  // tempDerivMat = covKernel->GetDerivative(observations.at(i)->loc, observations.at(j)->loc, p);
430  // derivMat.block(currRow,currCol, observations.at(i)->H->rows(), observations.at(j)->H->rows()) = observations.at(i)->H->Apply( observations.at(j)->H->Apply(tempDerivMat).transpose() );
431  //
432  // currRow += observations.at(i)->H->rows();
433  // }
434  // currCol += observations.at(j)->H->rows();
435  // }
436  //
437  // grad(p) = 0.5*(sigmaTrainDiff*sigmaTrainDiff.transpose()*derivMat - covSolver.solve(derivMat)).trace();
438  // }
439  // }
440  //
441  // return logLikely;
442 };
Eigen::LDLT< Eigen::MatrixXd > covSolver
virtual Eigen::MatrixXd Sample(Eigen::MatrixXd const &newPts)
GaussianProcess(MeanFunctionBase &meanIn, KernelBase &kernelIn)
std::shared_ptr< KernelBase > covKernel
virtual Eigen::MatrixXd PredictMean(Eigen::MatrixXd const &newPts)
Eigen::MatrixXd BuildCrossCov(Eigen::MatrixXd const &newLocs)
Eigen::SelfAdjointEigenSolver< Eigen::MatrixXd > covSolverEig
virtual GaussianProcess & Condition(Eigen::Ref< const Eigen::MatrixXd > const &loc, Eigen::Ref< const Eigen::MatrixXd > const &vals)
std::shared_ptr< muq::Modeling::Gaussian > Discretize(Eigen::MatrixXd const &pts)
Eigen::MatrixXd SolveFromEig(Eigen::MatrixXd const &b) const
virtual std::pair< Eigen::MatrixXd, Eigen::MatrixXd > Predict(Eigen::MatrixXd const &newLocs, CovarianceType covType)
std::shared_ptr< MeanFunctionBase > mean
std::vector< std::shared_ptr< ObservationInformation > > observations
virtual double LogLikelihood(Eigen::MatrixXd const &xs, Eigen::MatrixXd const &vals)
std::shared_ptr< KernelBase > Kernel()
double nlopt_obj(unsigned n, const double *x, double *nlopt_grad, void *opt_info)