Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add ellipsoid walks: Dikin, John and Vaidya walk. #39

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion R-proj/src/Makevars
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PKG_CPPFLAGS=-I../../external/boost -I../../external/LPsolve_src/run_headers -I../../external/minimum_ellipsoid -I../../include -I../../include/volume -I../../include/generators -I../../include/samplers -I../../include/annealing -I../../include/convex_bodies
PKG_CPPFLAGS=-I../../external/boost -I../../external/LPsolve_src/run_headers -I../../external/minimum_ellipsoid -I../../include -I../../include/volume -I../../include/generators -I../../include/samplers -I../../include/samplers/ellipsoid_walks -I../../include/annealing -I../../include/convex_bodies
PKG_CXXFLAGS= -lm -ldl -Wno-ignored-attributes -DBOOST_NO_AUTO_PTR
CXX_STD = CXX11

Expand Down
2 changes: 1 addition & 1 deletion R-proj/src/Makevars.win
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
PKG_CPPFLAGS=-I../../external/boost -I../../external/LPsolve_src/run_headers -I../../external/minimum_ellipsoid -I../../include -I../../include/volume -I../../include/generators -I../../include/samplers -I../../include/annealing -I../../include/convex_bodies
PKG_CPPFLAGS=-I../../external/boost -I../../external/LPsolve_src/run_headers -I../../external/minimum_ellipsoid -I../../include -I../../include/volume -I../../include/generators -I../../include/samplers -I../../include/samplers/ellipsoid_walks -I../../include/annealing -I../../include/convex_bodies
PKG_CXXFLAGS= -lm -ldl -Wno-ignored-attributes -DBOOST_NO_AUTO_PTR
CXX_STD = CXX11

Expand Down
58 changes: 51 additions & 7 deletions R-proj/src/sample_points.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,9 @@
#include "sample_only.h"
#include "simplex_samplers.h"
#include "vpolyintersectvpoly.h"
#include "dikin_walker.h"
#include "vaidya_walker.h"
#include "john_walker.h"


//' Sample points from a convex Polytope (H-polytope, V-polytope or a zonotope) or use direct methods for uniform sampling from the unit or the canonical or an arbitrary \eqn{d}-dimensional simplex and the boundary or the interior of a \eqn{d}-dimensional hypersphere
Expand All @@ -42,6 +45,9 @@
//' \item{\code{dimension} }{ An integer that declares the dimension when exact sampling is enabled for a simplex or a hypersphere.}
//' \item{\code{radius} }{ The radius of the \eqn{d}-dimensional hypersphere. The default value is \eqn{1}.}
//' \item{\code{BW_rad} }{ The radius for the ball walk.}
//' \item{\code{Dikin} }{Dikin walk}
//' \item{\code{John} }{John walk}
//' \item{\code{Vaidya} }{Vaidya walk}
//' }
//' @param InnerPoint A \eqn{d}-dimensional numerical vector that defines a point in the interior of polytope P.
//'
Expand Down Expand Up @@ -101,7 +107,8 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable<Rcpp::Reference> P = R_NilValue

int type, dim, numpoints;
NT radius = 1.0, delta = -1.0;
bool set_mean_point = false, cdhr = true, rdhr = false, ball_walk = false, gaussian = false;
bool set_mean_point = false, cdhr = false, rdhr = false, ball_walk = false, gaussian = false,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why having all those bool vars, one per walk type and not just one enum that takes the value related to the walk type, e.g. enum Walk { RDHnR, CDHnR, Ball, Billiard, Vaidya, John, Dikin, ... };

dikin = false, john = false, vaidya = false;
std::list<Point> randPoints;
std::pair<Point, NT> InnerBall;

Expand Down Expand Up @@ -194,18 +201,18 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable<Rcpp::Reference> P = R_NilValue

if(!WalkType.isNotNull() || Rcpp::as<std::string>(WalkType).compare(std::string("CDHR"))==0){
cdhr = true;
rdhr = false;
ball_walk = false;
} else if (Rcpp::as<std::string>(WalkType).compare(std::string("RDHR"))==0) {
cdhr = false;
rdhr = true;
ball_walk = false;
}else if (Rcpp::as<std::string>(WalkType).compare(std::string("Dikin"))==0) {
dikin = true;
} else if (Rcpp::as<std::string>(WalkType).compare(std::string("John"))==0) {
john = true;
} else if (Rcpp::as<std::string>(WalkType).compare(std::string("Vaidya"))==0) {
vaidya = true;
} else if (Rcpp::as<std::string>(WalkType).compare(std::string("BW"))==0) {
if (Rcpp::as<Rcpp::List>(Parameters).containsElementNamed("BW_rad")) {
delta = Rcpp::as<NT>(Rcpp::as<Rcpp::List>(Parameters)["BW_rad"]);
}
cdhr = false;
rdhr = false;
ball_walk = true;
} else {
throw Rcpp::exception("Unknown walk type!");
Expand Down Expand Up @@ -235,6 +242,8 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable<Rcpp::Reference> P = R_NilValue
HP.init(dim, Rcpp::as<MT>(Rcpp::as<Rcpp::Reference>(P).field("A")),
Rcpp::as<VT>(Rcpp::as<Rcpp::Reference>(P).field("b")));

HP.normalize();

if (!set_mean_point || ball_walk) {
InnerBall = HP.ComputeInnerBall();
if (!set_mean_point) MeanPoint = InnerBall.first;
Expand Down Expand Up @@ -298,6 +307,41 @@ Rcpp::NumericMatrix sample_points(Rcpp::Nullable<Rcpp::Reference> P = R_NilValue

switch (type) {
case 1: {

if (dikin || john || vaidya) {

MT A = HP.get_mat(), samples = MT::Zero(A.cols(), numpoints);
VT b = HP.get_vec(), new_sample = VT::Zero(A.cols()), p0 = Eigen::Map<VT>(&InnerBall.first.get_coeffs()[0], A.cols());
NT r = InnerBall.second;

if (dikin) {
DikinWalker <NT> dikinw = DikinWalker<NT>(p0, A, b, r);
for (int i = 0; i < numpoints; ++i) {
for (int j = 0; j < walkL; ++j) {
dikinw.doSample(new_sample);
}
samples.col(i) = new_sample;
}
} else if (john) {
JohnWalker<NT> johnw = JohnWalker<NT>(p0, A, b, r);
for (int i = 0; i < numpoints; ++i) {
for (int j = 0; j < walkL; ++j) {
johnw.doSample(new_sample);
}
samples.col(i) = new_sample;
}
} else {
VaidyaWalker<NT> vaidyaw = VaidyaWalker<NT>(p0, A, b, r);
for (int i = 0; i < numpoints; ++i) {
for (int j = 0; j < walkL; ++j) {
vaidyaw.doSample(new_sample);
}
samples.col(i) = new_sample;
}
}
return Rcpp::wrap(samples);
}

sampling_only<Point>(randPoints, HP, walkL, numpoints, gaussian,
a, MeanPoint, var1, var2);
break;
Expand Down
4 changes: 4 additions & 0 deletions include/cartesian_geom/point.h
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,10 @@ class point
void set_coord(const unsigned int i, FT coord) {
coeffs[i] = coord;
}

Coeff get_coeffs() {
return coeffs;
}

FT operator[] (const unsigned int i) {
return coeffs[i];
Expand Down
12 changes: 12 additions & 0 deletions include/convex_bodies/polytopes.h
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,18 @@ class HPolytope{
}


void normalize() {

NT row_norm;
for (int i = 0; i < num_of_hyperplanes(); ++i) {
row_norm = A.row(i).norm();
A.row(i) = A.row(i) / row_norm;
b(i) = b(i) / row_norm;
}

}


// Apply linear transformation, of square matrix T^{-1}, in H-polytope P:= Ax<=b
void linear_transformIt(MT T) {
A = A * T;
Expand Down
93 changes: 93 additions & 0 deletions include/samplers/ellipsoid_walks/dikin_walker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
// Code from https://github.com/rzrsk/vaidya-walk
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You can add
// Related publication: Chen et al. - "Fast MCMC Sampling Algorithms on Polytopes", Journal of Machine Learning Research 19 (2018) 1-86.
similarly to all files

Also please add the info and copyright notes as in all the files of the library


// Modified by Apostolos Chalkis, as part of Google Summer of Code 2019 program.

#ifndef PWALK_DIKIN_WALKER_HPP_
#define PWALK_DIKIN_WALKER_HPP_

#include <Eigen/Dense>
#include "math_functions.h"
#include "walker.h"

//namespace pwalk {

template <typename Dtype>
class DikinWalker: public Walker<Dtype> {
public:
DikinWalker(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& initialization, const Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>& cons_A, const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& cons_b, const Dtype r) : Walker<Dtype>(initialization, cons_A, cons_b), r_(r){}

// getter for radius
Dtype getRadius() {
return r_;
}

void proposal(Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample){
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> gaussian_step = Eigen::Matrix<Dtype, Eigen::Dynamic, 1>::Zero(this->nb_dim_);
sample_gaussian<Dtype>(this->nb_dim_, 0., 1., gaussian_step);

// get hessian
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_sqrt_inv_hess = Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>::Zero(this->nb_dim_, this->nb_dim_);
sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess);

new_sample = this->curr_sample_ + r_ / std::sqrt(Dtype(this->nb_dim_)) * (new_sqrt_inv_hess * gaussian_step);
}

bool acceptRejectReverse(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample){
// get hessian on x
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_sqrt_inv_hess_x = Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>::Zero(this->nb_dim_, this->nb_dim_);
sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess_x);
// get hessian on y
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_sqrt_inv_hess_y = Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>::Zero(this->nb_dim_, this->nb_dim_);
sqrtInvHessBarrier(new_sample, new_sqrt_inv_hess_y);

Dtype scale = r_/std::sqrt(Dtype(this->nb_dim_));
Dtype p_y_to_x = gaussian_density<Dtype>(this->curr_sample_, new_sample, new_sqrt_inv_hess_y.inverse()/scale);
Dtype p_x_to_y = gaussian_density<Dtype>(new_sample, this->curr_sample_, new_sqrt_inv_hess_x.inverse()/scale);

Dtype ar_ratio = std::min<Dtype>(1., p_y_to_x/p_x_to_y);

Dtype random_num = rng_uniform<Dtype>(0., 1.);
// lazy version of the walk
if (random_num > ar_ratio) {
return false;
}

return true;
}

bool doSample(Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample, const Dtype lazy = Dtype(0.5)){
proposal(new_sample);
this->nb_curr_samples_ += 1;
// for lazy markov chain
Dtype random_num = rng_uniform<Dtype>(0., 1.);
// check balance and check in polytope
if (random_num < lazy && this->checkInPolytope(new_sample) && acceptRejectReverse(new_sample)){
this->curr_sample_ = new_sample;
return true;
} else {
new_sample = this->curr_sample_;
return false;
}
}

void sqrtInvHessBarrier(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample, Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>& new_sqrt_inv_hess){
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> inv_slack = (this->cons_b_ - this->cons_A_ * new_sample).cwiseInverse();

Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> half_hess = inv_slack.asDiagonal()* this->cons_A_;
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_hess = half_hess.transpose() * half_hess;

// compute eigenvectors and eigenvalues
Eigen::SelfAdjointEigenSolver<Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> > es(new_hess);

Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> V = es.eigenvectors();
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> Dv = es.eigenvalues();
new_sqrt_inv_hess = V * Dv.cwiseInverse().cwiseSqrt().asDiagonal() * V.transpose();
}

private:
const Dtype r_;
};

//} // namespace pwalk

#endif // PWALK_DIKIN_WALKER_HPP_
134 changes: 134 additions & 0 deletions include/samplers/ellipsoid_walks/john_walker.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// Code from https://github.com/rzrsk/vaidya-walk

// Modified by Apostolos Chalkis, as part of Google Summer of Code 2019 program.

#ifndef PWALK_JOHN_WALKER_HPP_
#define PWALK_JOHN_WALKER_HPP_

#include <cmath>
#include <Eigen/Dense>
#include "math_functions.h"
#include "walker.h"

//namespace pwalk {

template <typename Dtype>
class JohnWalker: public Walker<Dtype> {
public:
JohnWalker(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& initialization, const Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>& cons_A, const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& cons_b, const Dtype r) : Walker<Dtype>(initialization, cons_A, cons_b), r_(r), alpha_(1. - 1. / std::log2(2.*Dtype(cons_A.rows())/Dtype(cons_A.cols()))), beta_(Dtype(cons_A.cols())/2./Dtype(cons_A.rows())), curr_weight_(Eigen::Matrix<Dtype, Eigen::Dynamic, 1>::Ones(cons_A.rows())){}

// getter for radius
Dtype getRadius() {
return r_;
}

void proposal(Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample){
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> gaussian_step = Eigen::Matrix<Dtype, Eigen::Dynamic, 1>::Zero(this->nb_dim_);
sample_gaussian<Dtype>(this->nb_dim_, 0., 1., gaussian_step);

// get hessian
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_sqrt_inv_hess = Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>::Zero(this->nb_dim_, this->nb_dim_);
sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess);

new_sample = this->curr_sample_ + r_ / std::sqrt(Dtype(this->nb_dim_)) * (new_sqrt_inv_hess * gaussian_step);
}

bool acceptRejectReverse(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample){
// get hessian on y
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_sqrt_inv_hess_y = Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>::Zero(this->nb_dim_, this->nb_dim_);
sqrtInvHessBarrier(new_sample, new_sqrt_inv_hess_y);
// get hessian on x
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_sqrt_inv_hess_x = Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>::Zero(this->nb_dim_, this->nb_dim_);
sqrtInvHessBarrier(this->curr_sample_, new_sqrt_inv_hess_x);

Dtype scale = r_ / std::sqrt(Dtype(this->nb_dim_));
Dtype p_y_to_x = gaussian_density<Dtype>(this->curr_sample_, new_sample, new_sqrt_inv_hess_y.inverse()/scale);
Dtype p_x_to_y = gaussian_density<Dtype>(new_sample, this->curr_sample_, new_sqrt_inv_hess_x.inverse()/scale);

Dtype ar_ratio = std::min<Dtype>(1., p_y_to_x/p_x_to_y);

Dtype random_num = rng_uniform<Dtype>(0., 1.);
// lazy version of the walk
if (random_num > ar_ratio) {
return false;
}

return true;
}

bool doSample(Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample, const Dtype lazy = Dtype(0.5)){
proposal(new_sample);
this->nb_curr_samples_ += 1;
// for lazy markov chain
Dtype random_num = rng_uniform<Dtype>(0., 1.);
// check balance and check in polytope
if (random_num < lazy && this->checkInPolytope(new_sample) && acceptRejectReverse(new_sample)){
this->curr_sample_ = new_sample;
return true;
} else {
new_sample = this->curr_sample_;
return false;
}
}

void sqrtInvHessBarrier(const Eigen::Matrix<Dtype, Eigen::Dynamic, 1>& new_sample, Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic>& new_sqrt_inv_hess){
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> inv_slack = (this->cons_b_ - this->cons_A_ * new_sample).cwiseInverse();

Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> half_hess = inv_slack.asDiagonal()* this->cons_A_;

Eigen::Matrix<Dtype, Eigen::Dynamic, 1> gradient;
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> score;
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> weight_half_alpha;
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> weight_half_hess;
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_hess;
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> new_hess_inv;
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> beta_ones = beta_ * Eigen::Matrix<Dtype, Eigen::Dynamic, 1>::Ones(this->nb_cons_);

Eigen::Matrix<Dtype, Eigen::Dynamic, 1> next_weight = curr_weight_;
// compute scores using gradient descent
do {
curr_weight_ = next_weight;
weight_half_alpha = curr_weight_.array().pow(alpha_/2.);

weight_half_hess = (weight_half_alpha.cwiseProduct(inv_slack)).asDiagonal() * this->cons_A_ ;

new_hess = weight_half_hess.transpose() * weight_half_hess;

new_hess_inv = new_hess.inverse();

score = ((weight_half_hess * new_hess_inv).cwiseProduct(weight_half_hess)).rowwise().sum();

// gradient = Eigen::Matrix<Dtype, Eigen::Dynamic, 1>::Ones(this->nb_cons_) - (score + beta_ones).cwiseQuotient(curr_weight_);

// curr_weight_ = (curr_weight_ - 0.5 * gradient).cwiseMax(beta_ones);
next_weight = (0.5*(curr_weight_ + score + beta_ones)).cwiseMax(beta_ones);

} while ((next_weight - curr_weight_).template lpNorm<Eigen::Infinity>() > Dtype(0.00001));

// std::cout << "inv_slack" << inv_slack.transpose() << std::endl;
// std::cout << "score" << score.transpose() << std::endl;
// std::cout << "curr_weight_ " << curr_weight_.transpose() << std::endl;

// compute john hessian
Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> john_new_hess = half_hess.transpose() * curr_weight_.asDiagonal() * half_hess;

// compute eigenvectors and eigenvalues
Eigen::SelfAdjointEigenSolver<Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> > es(john_new_hess);

Eigen::Matrix<Dtype, Eigen::Dynamic, Eigen::Dynamic> V = es.eigenvectors();
Eigen::Matrix<Dtype, Eigen::Dynamic, 1> Dv = es.eigenvalues();
new_sqrt_inv_hess = V * Dv.cwiseInverse().cwiseSqrt().asDiagonal() * V.transpose();
}

private:
const Dtype r_;
const Dtype alpha_;
const Dtype beta_;

Eigen::Matrix<Dtype, Eigen::Dynamic, 1> curr_weight_;
};

//} // namespace pwalk

#endif // PWALK_JOHN_WALKER_HPP_

Loading