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/linear/PowerMethod.h>
|
|
|
|
|
|
|
|
|
|
|
|
namespace gtsam {
|
|
|
|
|
|
|
|
|
|
|
|
using Sparse = Eigen::SparseMatrix<double>;
|
|
|
|
|
|
|
2020-11-16 11:49:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* \brief Compute maximum Eigenpair with accelerated power method
|
|
|
|
|
|
*
|
|
|
|
|
|
* References :
|
2020-12-11 14:01:40 +08:00
|
|
|
|
* 1) G. Golub and C. V. Loan, Matrix Computations, 3rd ed. Baltimore, Johns
|
|
|
|
|
|
* Hopkins University Press, 1996, pp.405-411
|
|
|
|
|
|
* 2) Rosen, D. and Carlone, L., 2017, September. Computational
|
2020-11-16 11:49:36 +08:00
|
|
|
|
* enhancements for certifiably correct SLAM. In Proceedings of the
|
|
|
|
|
|
* International Conference on Intelligent Robots and Systems.
|
2020-12-11 14:01:40 +08:00
|
|
|
|
* 3) Yulun Tian and Kasra Khosoussi and David M. Rosen and Jonathan P. How,
|
2020-11-16 11:49:36 +08:00
|
|
|
|
* 2020, Aug, Distributed Certifiably Correct Pose-Graph Optimization, Arxiv
|
2020-12-11 14:01:40 +08:00
|
|
|
|
* 4) C. de Sa, B. He, I. Mitliagkas, C. Ré, and P. Xu, “Accelerated
|
2020-11-16 11:49:36 +08:00
|
|
|
|
* stochastic power iteration,” in Proc. Mach. Learn. Res., no. 84, 2018, pp.
|
|
|
|
|
|
* 58–67
|
|
|
|
|
|
*
|
|
|
|
|
|
* 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-12-11 14:01:40 +08:00
|
|
|
|
*
|
2020-11-16 11:49:36 +08:00
|
|
|
|
* Template argument Operator just needs multiplication operator
|
|
|
|
|
|
*
|
|
|
|
|
|
*/
|
2020-10-13 14:34:24 +08:00
|
|
|
|
template <class Operator>
|
|
|
|
|
|
class AcceleratedPowerMethod : public PowerMethod<Operator> {
|
|
|
|
|
|
|
|
|
|
|
|
double beta_ = 0; // a Polyak momentum term
|
|
|
|
|
|
|
|
|
|
|
|
Vector previousVector_; // store previous vector
|
|
|
|
|
|
|
|
|
|
|
|
public:
|
2020-11-16 11:49:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Constructor from aim matrix A (given as Matrix or Sparse), optional intial
|
|
|
|
|
|
* vector as ritzVector
|
|
|
|
|
|
*/
|
2020-10-13 14:34:24 +08:00
|
|
|
|
explicit AcceleratedPowerMethod(
|
2020-10-23 02:19:05 +08:00
|
|
|
|
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
|
2020-10-23 06:05:09 +08:00
|
|
|
|
this->ritzVector_ = initial ? initial.get() : Vector::Random(this->dim_);
|
|
|
|
|
|
this->ritzVector_.normalize();
|
|
|
|
|
|
previousVector_ = Vector::Zero(this->dim_);
|
2020-10-23 02:19:05 +08:00
|
|
|
|
|
|
|
|
|
|
// initialize beta_
|
2021-01-19 13:07:21 +08:00
|
|
|
|
beta_ = initialBeta;
|
2020-10-23 06:05:09 +08:00
|
|
|
|
}
|
2020-10-13 14:34:24 +08:00
|
|
|
|
|
2020-11-16 11:49:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Run accelerated power iteration to get ritzVector with beta and previous
|
|
|
|
|
|
* two ritzVector x0 and x00, and return y = (A * x0 - \beta * x00) / || A * x0
|
|
|
|
|
|
* - \beta * x00 ||
|
|
|
|
|
|
*/
|
2020-10-27 03:40:49 +08:00
|
|
|
|
Vector acceleratedPowerIteration (const Vector &x1, const Vector &x0,
|
2020-10-23 02:19:05 +08:00
|
|
|
|
const double beta) const {
|
2020-10-13 14:34:24 +08:00
|
|
|
|
Vector y = this->A_ * x1 - beta * x0;
|
|
|
|
|
|
y.normalize();
|
|
|
|
|
|
return y;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2020-11-16 11:49:36 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Run accelerated power iteration to get ritzVector with beta and previous
|
|
|
|
|
|
* two ritzVector x0 and x00, and return y = (A * x0 - \beta * x00) / || A * x0
|
|
|
|
|
|
* - \beta * x00 ||
|
|
|
|
|
|
*/
|
2020-10-27 03:40:49 +08:00
|
|
|
|
Vector acceleratedPowerIteration () const {
|
|
|
|
|
|
Vector y = acceleratedPowerIteration(this->ritzVector_, previousVector_, beta_);
|
2020-10-13 14:34:24 +08:00
|
|
|
|
return y;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2021-01-19 13:07:21 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* Tuning the momentum beta using the Best Heavy Ball algorithm in Ref(3), T
|
|
|
|
|
|
* is the iteration time to find beta with largest Rayleigh quotient
|
|
|
|
|
|
*/
|
|
|
|
|
|
double estimateBeta(const size_t T = 10) const {
|
2020-11-02 09:46:42 +08:00
|
|
|
|
// set initial estimation of maxBeta
|
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);
|
2020-10-23 02:19:05 +08:00
|
|
|
|
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
|
|
|
|
// 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_);
|
2021-01-19 13:07:21 +08:00
|
|
|
|
// 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 {
|
2021-01-19 13:07:21 +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);
|
2020-10-23 06:05:09 +08:00
|
|
|
|
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-11-16 11:49:36 +08:00
|
|
|
|
return betas[maxIndex];
|
2020-10-13 14:34:24 +08:00
|
|
|
|
}
|
2020-10-27 03:40:49 +08:00
|
|
|
|
|
2020-11-16 11:49:36 +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.
|
|
|
|
|
|
*/
|
2020-10-27 03:40:49 +08:00
|
|
|
|
bool compute(size_t maxIterations, double tol) {
|
|
|
|
|
|
// Starting
|
|
|
|
|
|
bool isConverged = false;
|
|
|
|
|
|
|
2020-11-02 09:46:42 +08:00
|
|
|
|
for (size_t i = 0; i < maxIterations && !isConverged; i++) {
|
2020-10-27 03:40:49 +08:00
|
|
|
|
++(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);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return isConverged;
|
|
|
|
|
|
}
|
2020-10-13 14:34:24 +08:00
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
} // namespace gtsam
|