gtsam/gtsam/linear/AcceleratedPowerMethod.h

173 lines
6.1 KiB
C
Raw Normal View History

2020-10-13 14:34:24 +08:00
/* ----------------------------------------------------------------------------
* GTSAM Copyright 2010-2019, Georgia Tech Research Corporation,
* Atlanta, Georgia 30332-0415
* All Rights Reserved
* Authors: Frank Dellaert, et al. (see THANKS for the full author list)
* See LICENSE for the license information
* -------------------------------------------------------------------------- */
/**
* @file AcceleratedPowerMethod.h
* @date Sept 2020
* @author Jing Wu
* @brief accelerated power method for fast eigenvalue and eigenvector
* computation
*/
#pragma once
// #include <gtsam/base/Matrix.h>
// #include <gtsam/base/Vector.h>
#include <gtsam/linear/PowerMethod.h>
namespace gtsam {
using Sparse = Eigen::SparseMatrix<double>;
/* ************************************************************************* */
/// MINIMUM EIGENVALUE COMPUTATIONS
// Template argument Operator just needs multiplication operator
template <class Operator>
class AcceleratedPowerMethod : public PowerMethod<Operator> {
/**
* \brief Compute maximum Eigenpair with accelerated power method
2020-10-13 14:34:24 +08:00
*
* References :
* 1) Rosen, D. and Carlone, L., 2017, September. Computational
* enhancements for certifiably correct SLAM. In Proceedings of the
* International Conference on Intelligent Robots and Systems.
* 2) Yulun Tian and Kasra Khosoussi and David M. Rosen and Jonathan P. How,
* 2020, Aug, Distributed Certifiably Correct Pose-Graph Optimization, Arxiv
* 3) C. de Sa, B. He, I. Mitliagkas, C. Ré, and P. Xu, Accelerated
* stochastic power iteration, in Proc. Mach. Learn. Res., no. 84, 2018, pp.
* 5867
*
* It performs the following iteration: \f$ x_{k+1} = A * x_k - \beta *
* x_{k-1} \f$ where A is the aim matrix we want to get eigenpair of, x is the
* Ritz vector
2020-10-13 14:34:24 +08:00
*
*/
double beta_ = 0; // a Polyak momentum term
Vector previousVector_; // store previous vector
public:
// Constructor from aim matrix A (given as Matrix or Sparse), optional intial
// vector as ritzVector
explicit AcceleratedPowerMethod(
const Operator &A, const boost::optional<Vector> initial = boost::none,
2020-10-27 03:40:49 +08:00
double initialBeta = 0.0)
2020-10-13 14:34:24 +08:00
: PowerMethod<Operator>(A, initial) {
// initialize Ritz eigen vector and previous vector
this->ritzVector_ = initial ? initial.get() : Vector::Random(this->dim_);
this->ritzVector_.normalize();
previousVector_ = Vector::Zero(this->dim_);
// initialize beta_
if (!initialBeta) {
estimateBeta();
} else {
beta_ = initialBeta;
}
}
2020-10-13 14:34:24 +08:00
2020-10-27 03:40:49 +08:00
// Run accelerated power iteration on the ritzVector with beta and previous
// two ritzVector, and return x_{k+1} = A * x_k - \beta * x_{k-1} / || A * x_k
// - \beta * x_{k-1} ||
Vector acceleratedPowerIteration (const Vector &x1, const Vector &x0,
const double beta) const {
2020-10-13 14:34:24 +08:00
Vector y = this->A_ * x1 - beta * x0;
y.normalize();
return y;
}
2020-10-27 03:40:49 +08:00
// Run accelerated power iteration on the ritzVector with beta and previous
// two ritzVector, and return x_{k+1} = A * x_k - \beta * x_{k-1} / || A * x_k
// - \beta * x_{k-1} ||
Vector acceleratedPowerIteration () const {
Vector y = acceleratedPowerIteration(this->ritzVector_, previousVector_, beta_);
2020-10-13 14:34:24 +08:00
return y;
}
// Tuning the momentum beta using the Best Heavy Ball algorithm in Ref(3)
void estimateBeta() {
// set beta
2020-10-27 03:40:49 +08:00
Vector initVector = this->ritzVector_;
const double up = initVector.dot( this->A_ * initVector );
const double down = initVector.dot(initVector);
const double mu = up / down;
double maxBeta = mu * mu / 4;
2020-10-13 14:34:24 +08:00
size_t maxIndex;
2020-10-27 03:40:49 +08:00
std::vector<double> betas;
2020-10-13 14:34:24 +08:00
Matrix R = Matrix::Zero(this->dim_, 10);
2020-10-27 03:40:49 +08:00
size_t T = 10;
// run T times of iteration to find the beta that has the largest Rayleigh quotient
for (size_t t = 0; t < T; t++) {
// after each t iteration, reset the betas with the current maxBeta
betas = {2 / 3 * maxBeta, 0.99 * maxBeta, maxBeta, 1.01 * maxBeta,
1.5 * maxBeta};
// iterate through every beta value
2020-10-13 14:34:24 +08:00
for (size_t k = 0; k < betas.size(); ++k) {
2020-10-27 03:40:49 +08:00
// initialize x0 and x00 in each iteration of each beta
Vector x0 = initVector;
Vector x00 = Vector::Zero(this->dim_);
// run 10 steps of accelerated power iteration with this beta
2020-10-13 14:34:24 +08:00
for (size_t j = 1; j < 10; j++) {
if (j < 2) {
2020-10-27 03:40:49 +08:00
R.col(0) = acceleratedPowerIteration(x0, x00, betas[k]);
R.col(1) = acceleratedPowerIteration(R.col(0), x0, betas[k]);
2020-10-13 14:34:24 +08:00
} else {
2020-10-27 03:40:49 +08:00
R.col(j) = acceleratedPowerIteration(R.col(j - 1), R.col(j - 2), betas[k]);
2020-10-13 14:34:24 +08:00
}
}
2020-10-27 03:40:49 +08:00
// compute the Rayleigh quotient for the randomly sampled vector after
// 10 steps of power accelerated iteration
2020-10-13 14:34:24 +08:00
const Vector x = R.col(9);
const double up = x.dot(this->A_ * x);
const double down = x.dot(x);
2020-10-13 14:34:24 +08:00
const double mu = up / down;
2020-10-27 03:40:49 +08:00
// store the momentum with largest Rayleigh quotient and its according index of beta_
2020-10-13 14:34:24 +08:00
if (mu * mu / 4 > maxBeta) {
2020-10-27 03:40:49 +08:00
// save the max beta index
2020-10-13 14:34:24 +08:00
maxIndex = k;
maxBeta = mu * mu / 4;
}
}
}
2020-10-27 03:40:49 +08:00
// set beta_ to momentum with largest Rayleigh quotient
2020-10-13 14:34:24 +08:00
beta_ = betas[maxIndex];
}
2020-10-27 03:40:49 +08:00
// Start the accelerated iteration, after performing the
// accelerated iteration, calculate the ritz error, repeat this
// operation until the ritz error converge. If converged return true, else
// false.
bool compute(size_t maxIterations, double tol) {
// Starting
bool isConverged = false;
for (size_t i = 0; i < maxIterations; i++) {
++(this->nrIterations_);
Vector tmp = this->ritzVector_;
// update the ritzVector after accelerated power iteration
this->ritzVector_ = acceleratedPowerIteration();
// update the previousVector with ritzVector
previousVector_ = tmp;
// update the ritzValue
this->ritzValue_ = this->ritzVector_.dot(this->A_ * this->ritzVector_);
isConverged = this->converged(tol);
if (isConverged) return isConverged;
}
return isConverged;
}
2020-10-13 14:34:24 +08:00
};
} // namespace gtsam