/** * \file GaussianProcessInterpolation.cpp * * Copyright 2007-2013 IMP Inventors. All rights reserved. */ #include #include #include #include #include #include #include #include #include #include IMPISD_BEGIN_NAMESPACE GaussianProcessInterpolation::GaussianProcessInterpolation( FloatsList x, Floats sample_mean, Floats sample_std, unsigned n_obs, UnivariateFunction *mean_function, BivariateFunction *covariance_function, Particle *sigma, double sparse_cutoff) : Object("GaussianProcessInterpolation%1%"), x_(x), n_obs_(n_obs), mean_function_(mean_function), covariance_function_(covariance_function), sigma_(sigma), cutoff_(sparse_cutoff) { //O(M) //store dimensions M_ = x.size(); N_ = x[0].size(); sigma_val_ = Scale(sigma_).get_nuisance(); //basic checks IMP_USAGE_CHECK(sample_mean.size() == M_, "sample_mean should have the same size as x"); IMP_USAGE_CHECK(sample_std.size() == M_, "sample_std should have the same size as x"); IMP_USAGE_CHECK(mean_function->get_ndims_x() == N_, "mean_function should have " << N_ << " input dimensions"); IMP_USAGE_CHECK(mean_function->get_ndims_y() == 1, "mean_function should have 1 output dimension"); IMP_USAGE_CHECK(covariance_function->get_ndims_x1() == N_, "covariance_function should have " << N_ << " input dimensions for first vector"); IMP_USAGE_CHECK(covariance_function->get_ndims_x2() == N_, "covariance_function should have " << N_ << " input dimensions for second vector"); IMP_USAGE_CHECK(covariance_function->get_ndims_y() == 1, "covariance_function should have 1 output dimension"); IMP_IF_CHECK(USAGE_AND_INTERNAL) { Scale::decorate_particle(sigma); } // set all flags to false = need update. force_mean_update(); force_covariance_update(); //compute needed matrices compute_I(sample_mean); compute_S(sample_std); } void GaussianProcessInterpolation::force_mean_update() { flag_m_ = false; flag_m_gpir_ = false; flag_OmiIm_ = false; } void GaussianProcessInterpolation::force_covariance_update() { flag_Omi_ = false; flag_OmiIm_ = false; flag_W_ = false; flag_Omega_ = false; flag_Omega_gpir_ = false; // the gpi restraint needs to know when // to update the mvn's Sigma:=Omega matrix. } void GaussianProcessInterpolation::compute_I(Floats mean) { I_ = VectorXd (M_); IMP_LOG_TERSE( "I: "); for (unsigned i=0; ihas_changed(); if (ret) covariance_function_->update(); if (flag_Omi_) flag_Omi_ = !ret; if (flag_OmiIm_) flag_OmiIm_ = !ret; if (flag_W_) flag_W_ = !ret; if (flag_Omega_) flag_Omega_ = !ret; if (flag_Omega_gpir_) flag_Omega_gpir_ = !ret; //the case of Omega is a bit peculiar //Omega = W + sigma*S/N so it depends on W and sigma. //W is treated by flag_W_ and sigma is updated here //since it's just a double double si = Scale(sigma_).get_nuisance(); if (std::abs(sigma_val_ - si) > 1e-7) { //sigma has changed, update its value sigma_val_ = si; //and mark Omega as outdated flag_Omega_ = false; flag_Omega_gpir_ = false; flag_Omi_ = false; flag_OmiIm_ = false; } IMP_LOG_TERSE( "update_flags_covariance: ret " << ret << " flag_Omi_ " << flag_Omi_ << " flag_OmiIm_ " << flag_OmiIm_ << " flag_W_ " << flag_W_ << " flag_Omega_ " << flag_Omega_ << " flag_Omega_gpir_ " << flag_Omega_gpir_ << std::endl ); } VectorXd GaussianProcessInterpolation::get_wx_vector(Floats xval) const { const_cast(this)->update_flags_covariance(); IMP_LOG_TERSE(" get_wx_vector at q= " << xval[0] << " "); VectorXd wx(M_); for (unsigned i=0; i(this)->update_flags_covariance(); unsigned nm = get_number_of_m_particles(); //derivative wrt mean particles and/or sigma is zero if (p <= nm) return VectorXd::Zero(M_); VectorXd ret(M_); for (unsigned i=0; iget_derivative_matrix(p-(nm+1),xv)(0,1); } return ret; } VectorXd GaussianProcessInterpolation::get_wx_vector_second_derivative( Floats xval, unsigned i, unsigned j) const { const_cast(this)->update_flags_covariance(); unsigned nm = get_number_of_m_particles(); //derivative wrt mean particles and/or sigma is zero if (i <= nm || j <= nm) return VectorXd::Zero(M_); VectorXd ret(M_); for (unsigned q=0; qget_second_derivative_matrix( i-(nm+1), j-(nm+1), xv)(0,1); } return ret; } VectorXd GaussianProcessInterpolation::get_OmiIm() const { IMP_LOG_TERSE( "get_OmiIm()" << std::endl); const_cast(this)->update_flags_mean(); const_cast(this)->update_flags_covariance(); if (!flag_OmiIm_) { IMP_LOG_TERSE( "need to update OmiIm_" << std::endl); const_cast(this)->compute_OmiIm(); const_cast(this)->flag_OmiIm_ = true; IMP_LOG_TERSE( "done updating OmiIm_" << std::endl); } return OmiIm_; } void GaussianProcessInterpolation::compute_OmiIm() { VectorXd I(get_I()); VectorXd m(get_m()); MatrixXd Omi(get_Omi()); IMP_LOG_TERSE( "OmiIm "); OmiIm_ = Omi*(I-m); IMP_LOG_TERSE( std::endl); } VectorXd GaussianProcessInterpolation::get_m() const { IMP_LOG_TERSE( "get_m()" << std::endl); const_cast(this)->update_flags_mean(); if (!flag_m_) { IMP_LOG_TERSE( "need to update m" << std::endl); const_cast(this)->compute_m(); const_cast(this)->flag_m_ = true; IMP_LOG_TERSE( "done updating m" << std::endl); } return m_; } void GaussianProcessInterpolation::compute_m() { m_ = (*mean_function_)(x_); } unsigned GaussianProcessInterpolation::get_number_of_m_particles() const { return mean_function_->get_number_of_particles(); } bool GaussianProcessInterpolation::get_m_particle_is_optimized(unsigned i) const { return mean_function_->get_particle_is_optimized(i); } VectorXd GaussianProcessInterpolation::get_m_derivative(unsigned particle) const { const_cast(this)->update_flags_mean(); return mean_function_->get_derivative_vector(particle, x_); } VectorXd GaussianProcessInterpolation::get_m_second_derivative( unsigned p1, unsigned p2) const { const_cast(this)->update_flags_mean(); return mean_function_->get_second_derivative_vector(p1, p2, x_); } void GaussianProcessInterpolation::add_to_m_particle_derivative( unsigned particle, double value, DerivativeAccumulator &accum) { mean_function_->add_to_particle_derivative(particle, value, accum); } MatrixXd GaussianProcessInterpolation::get_Omega() const { IMP_LOG_TERSE( "get_Omega()" << std::endl); //updates sigma as well const_cast(this)->update_flags_covariance(); if (!flag_Omega_) { IMP_LOG_TERSE( "need to update Omega" << std::endl); const_cast(this)->compute_Omega(); const_cast(this)->flag_Omega_ = true; //leave flag_Omega_gpir_ to false so that the gpir is notified //if it wants to update some stuff on its own. IMP_LOG_TERSE( "done updating Omega" << std::endl); } return Omega_; } void GaussianProcessInterpolation::compute_Omega() { //sigma_val_ is up-to-date because update_flags_covariance was just called Omega_ = get_W() + sigma_val_ * MatrixXd(get_S())/n_obs_; } unsigned GaussianProcessInterpolation::get_number_of_Omega_particles() const { //sigma is optimized locally return covariance_function_->get_number_of_particles() + 1; } bool GaussianProcessInterpolation::get_Omega_particle_is_optimized(unsigned i) const { //sigma is optimized locally if (i==0) { return Scale(sigma_).get_nuisance_is_optimized(); } else { return covariance_function_->get_particle_is_optimized(i-1); } } MatrixXd GaussianProcessInterpolation::get_Omega_derivative(unsigned particle) const { const_cast(this)->update_flags_covariance(); //Omega = W + sigma*S/n_obs if (particle == 0) { //sigma return MatrixXd(get_S())/n_obs_; } else { return covariance_function_->get_derivative_matrix(particle-1, x_); } } MatrixXd GaussianProcessInterpolation::get_Omega_second_derivative( unsigned p1, unsigned p2) const { const_cast(this)->update_flags_covariance(); //Omega = W + sigma*S/n_obs if (p1 == 0 || p2 == 0) { //sigma return MatrixXd::Zero(M_,M_); } else { return covariance_function_->get_second_derivative_matrix( p1-1, p2-1, x_); } } void GaussianProcessInterpolation::add_to_Omega_particle_derivative( unsigned particle, double value, DerivativeAccumulator &accum) { if (particle == 0) { Scale(sigma_).add_to_nuisance_derivative(value, accum); } else { covariance_function_->add_to_particle_derivative(particle-1, value, accum); } } MatrixXd GaussianProcessInterpolation::get_Omi() const { IMP_LOG_TERSE( "get_Omi()" << std::endl); const_cast(this)->update_flags_covariance(); if (!flag_Omi_) { IMP_LOG_TERSE( "need to update Omi" << std::endl); const_cast(this)->compute_Omi(); const_cast(this)->flag_Omi_ = true; IMP_LOG_TERSE( "done updating Omi" << std::endl); } return Omi_; } void GaussianProcessInterpolation::compute_Omi() { //get Omega=W+S/N MatrixXd WpS = get_Omega(); IMP_LOG_TERSE(" compute_inverse: Cholesky" << std::endl); //compute Cholesky decomp Eigen::LDLT ldlt; ldlt.compute(WpS); if (!ldlt.isPositive()) IMP_THROW("Matrix is not positive semidefinite!", ModelException); //get inverse IMP_LOG_TERSE(" compute_inverse: inverse" << std::endl); Omi_= ldlt.solve(MatrixXd::Identity(M_,M_)); } MatrixXd GaussianProcessInterpolation::get_W() const { IMP_LOG_TERSE( "get_W()" << std::endl); const_cast(this)->update_flags_covariance(); if (!flag_W_) { IMP_LOG_TERSE( "need to update W" << std::endl); const_cast(this)->compute_W(); const_cast(this)->flag_W_ = true; IMP_LOG_TERSE( "done updating W" << std::endl); } return W_; } void GaussianProcessInterpolation::compute_W() { W_ = (*covariance_function_)(x_); } VectorXd GaussianProcessInterpolation::get_dcov_dwq(Floats q) const { VectorXd wq(get_wx_vector(q)); MatrixXd Omi(get_Omi()); return -2*Omi*wq; } MatrixXd GaussianProcessInterpolation::get_dcov_dOm(Floats q) const { VectorXd wq(get_wx_vector(q)); MatrixXd Omi(get_Omi()); VectorXd ret(Omi*wq); return ret*ret.transpose(); } MatrixXd GaussianProcessInterpolation::get_d2cov_dwq_dwq() const { return -2*get_Omi(); } MatrixXd GaussianProcessInterpolation::get_d2cov_dwq_dOm(Floats q, unsigned m) const { MatrixXd Omi(get_Omi()); VectorXd wq(get_wx_vector(q)); VectorXd L(Omi*wq); MatrixXd ret(L*Omi.col(m).transpose()); return ret + ret.transpose(); } MatrixXd GaussianProcessInterpolation::get_d2cov_dOm_dOm(Floats q, unsigned m, unsigned n) const { MatrixXd Omi(get_Omi()); VectorXd wq(get_wx_vector(q)); VectorXd L(Omi*wq); MatrixXd tmp(Omi.col(m)*L.transpose()); return -L(n)*(tmp + tmp.transpose()); } /* MatrixXd GaussianProcessInterpolation::get_posterior_mean_hessian(Floats x, unsigned p1, unsigned p2) const { return MatrixXd::Zero(1,1); } MatrixXd GaussianProcessInterpolation::get_posterior_covariance_hessian( Floats x, unsigned p1, unsigned p2) const { return MatrixXd::Zero(1,1); } */ void GaussianProcessInterpolation::do_show(std::ostream &out) const { out << "Interpolation via gaussian process" << std::endl; } ParticlesTemp GaussianProcessInterpolation::get_input_particles() const { ParticlesTemp ret; ParticlesTemp ret1 = mean_function_->get_input_particles(); ret.insert(ret.end(),ret1.begin(),ret1.end()); ret.push_back(sigma_); ParticlesTemp ret2 = covariance_function_->get_input_particles(); ret.insert(ret.end(),ret2.begin(),ret2.end()); return ret; } ContainersTemp GaussianProcessInterpolation::get_input_containers() const { ContainersTemp ret; ContainersTemp ret1 = mean_function_->get_input_containers(); ret.insert(ret.end(),ret1.begin(),ret1.end()); ContainersTemp ret2 = covariance_function_->get_input_containers(); ret.insert(ret.end(),ret2.begin(),ret2.end()); return ret; } IMPISD_END_NAMESPACE