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

WIP: Add TSVD #184

Open
wants to merge 18 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 9 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
1 change: 1 addition & 0 deletions include/El/lapack_like/spectral.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -1959,6 +1959,7 @@ DistMatrix<Int,VR,STAR> HessenbergSpectralCloud
#include <El/lapack_like/spectral/Schur.hpp>
#include <El/lapack_like/spectral/HermitianEig.hpp>
#include <El/lapack_like/spectral/SVD.hpp>
#include <El/lapack_like/spectral/TSVD.hpp>
#include <El/lapack_like/spectral/Lanczos.hpp>
#include <El/lapack_like/spectral/ProductLanczos.hpp>

Expand Down
273 changes: 273 additions & 0 deletions include/El/lapack_like/spectral/TSVD.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,273 @@
/***
*
* TSVD Based off of implementation by Andreas Noack. See: http://github.com/andreasnoack/TSVD.jl
*/
#ifndef EL_LAPACKLIKE_SPECTRAL_TSVD_HPP
#define EL_LAPACKLIKE_SPECTRAL_TSVD_HPP
#include <tuple>
#include <El/blas_like/level3.hpp>

namespace El {

template< typename Real>
Real CopySign(const Real& x, const Real& y){ return y >= Real(0) ? Abs(x): -Abs(x); }

struct BidiagInfo {
Int numVecsReorth;
}; //struct BidiagInfo

template< typename F>
struct BidiagCtrl {
bool reorthIn=false;
Base<F> reorthogTol=Pow(limits::Epsilon<Base<F>>(), Base<F>(.7));
Base<F> convValTol=Pow(limits::Epsilon<Base<F>>(), Base<F>(.7));
Base<F> convVecTol=Pow(limits::Epsilon<Base<F>>(), Base<F>(.5));
bool partialProject=false;
}; //end struct BidiagCtrl

//TODO: Make batch with Gemv
template< typename F>
Int reorth( DistMatrix<F>& Q,
Int j,
std::vector<Base<F>>& termList,
const BidiagCtrl<F>& ctrl,
DistMatrix<F>& x,
Matrix<Base<F>>& diagList){
typedef Base<F> Real;
const Real eps = limits::Epsilon<Real>();
Int numVecsReorth = 0;
termList.emplace_back( Real( 1));
for( Int i = 0; i < j; ++i){
auto qi = Q(ALL, IR(i));
Axpy( -Dot(qi, x), qi, x);
termList[i] = eps;
numVecsReorth++;
}
Real alpha = Nrm2( x);
diagList.Set(j,0,alpha);
Scale(Real(1)/alpha, x);
auto qj = Q(ALL,IR(j));
qj = x;
return numVecsReorth;
}



template< typename F,
typename ForwardOperator,
typename AdjointOperator>
BidiagInfo
BidiagLanczos(
Int m,
Int n,
const ForwardOperator& A,
const AdjointOperator& AAdj,
Int steps,
Base<F> tau,
Matrix<Base<F>>& mainDiag,
Matrix<Base<F>>& superDiag,
DistMatrix<F>& U,
DistMatrix<F>& V,
//Base<F>& normA, //can be an estimate?
std::vector<Base<F>>& muList,
std::vector<Base<F>>& nuList,
const BidiagCtrl<F>& ctrl){
typedef Base<F> Real;
typedef DistMatrix<F> DM;
const Real eps = limits::Epsilon<Real>();

bool reorthB = ctrl.reorthIn;
Int numVecsReorth = 0;
Int iter = mainDiag.size()-1;
std::vector< Base<F>> maxMuList, maxNuList;
maxMuList.reserve(steps);
maxNuList.reserve(steps);

DM v = V( ALL, IR(iter));
DM u = U( ALL, IR(iter+1));
DM vOld( v);
DM uOld( u);
Real beta = superDiag[iter];
for( Int j = iter+1; j <= iter+steps; ++j)
{
vOld = v;
v = u;
AAdj( v);
Axpy( -beta, vOld, v);
Real alpha = Nrm2(v);
//run omega recurrence
bool foundInaccurate = false;
for(Int i = 0; i < j; ++i)
{
Real nu = superDiag.Get(i,0)*muList[i+1] +
mainDiag.Get(i,0)*muList[i] - beta*nuList[i];
nu = (nu + CopySign(tau, nu))/alpha;
foundInaccurate |= (Abs(nu) > ctrl.reorthogTol);
nuList[i] = nu;
}
Real maxElt = *std::max_element( nuList.begin(), nuList.end(), Abs);
maxNuList.emplace_back( maxElt);
if( reorthB || foundInaccurate ){
numVecsReorth += reorth( V, j, nuList, ctrl, v, mainDiag);
}

//The u step
uOld = u;
u = v;
// apply the operator
A(u);
Axpy(-alpha, uOld, u);
beta = Nrm2( u);

//compute omega recurrence
foundInaccurate=false;
for( Int i = 0; i <= j; ++i){
Real mu = mainDiag.Get(i,0)*nuList[i] - alpha*muList[i];
if (i > 0){
mu += superDiag.Get(i-1, 0)*nuList[i-1];
}
mu = (mu + CopySign(tau, mu))/beta;
foundInaccurate |= (Abs(mu) > ctrl.reorthogTol);
muList[ i] = mu;
}
maxElt = *std::max_element( muList.begin(), muList.end(), Abs);
maxMuList.emplace_back( maxElt);
muList.emplace_back( 1);
if( reorthB || foundInaccurate){
numVecsReorth += reorth( U, j, muList, ctrl, u, superDiag);
}
}
BidiagInfo info;
info.numVecsReorth = numVecsReorth;
return info;
}


/**
* TSVD
*/
template< typename F,
typename ForwardOperator,
typename AdjointOperator>
inline Int
TSVD(
Int m,
Int n,
const ForwardOperator& A,
const AdjointOperator& AAdj,
Int nVals,
AbstractDistMatrix<F>& U,
AbstractDistMatrix<F>& S,
AbstractDistMatrix<F>& V,
AbstractDistMatrix<F>& initialVec, //should be of length m
Int maxIter=Int(1000),
const BidiagCtrl<F>& ctrl=BidiagCtrl<F>()) //requires InfiniteCharacteristic<F> && ForwardOperator::ScalarType is F
{
typedef DistMatrix<F> DM;
typedef Matrix<Base<F>> RealMatrix;
typedef Base<F> Real;
maxIter = Min(maxIter,Min(m,n));
const Grid& g = initialVec.Grid();
//1) Compute nu, a tolerance for reorthoganlization
auto tau = InfinityNorm(A); //TODO: Impl this?

Real initNorm = Nrm2(initialVec);
Scale(Real(1)/initNorm, initialVec);
DM Vtilde( n, maxIter, g);
auto v = Vtilde(ALL, 0);
v = initialVec;
AAdj( v); //v = A'initialVec;
Real alpha = Nrm2(v); //record the norm, for reorth.
Scale(Real(1)/alpha, v); //make v a unit vector
Real nu = 1 + tau/alpha; //think of nu = 1+\epsilon

DM Utilde(m, maxIter, g);
auto u0 = Utilde(ALL, 0);
auto u1 = Utilde(ALL, 1);
u0 = initialVec;
u1 = v;
A( u1);
Axpy(-alpha, u0, u1);
Real beta = Nrm2(u1);
Scale(Real(1)/beta,u1);
Real mu = tau/beta;


RealMatrix mainDiag( maxIter+1, 1);
RealMatrix superDiag( maxIter, 1, g);
std::vector<Base<F>> muList, nuList;
muList.reserve( maxIter);
nuList.reserve( maxIter);
muList.push_back( mu);
muList.push_back( 1);
nuList.push_back( 1);

RealMatrix sOld( maxIter+1, 1);
Int blockSize = Max(nVals, 50);
for(Int i = 0; i < maxIter; i+=blockSize){
Int numSteps = Min( blockSize, maxIter-i);
BidiagLanczos
( m, n, A, AAdj, numSteps,
tau, mainDiag, superDiag, Utilde, Vtilde,
muList, nuList, ctrl);
auto s = mainDiag;
auto superDiagCopy = superDiag;
RealMatrix VTHat(i+numSteps,nVals);
RealMatrix UHat(nVals,i+numSteps);
lapack::BidiagSVDQRAlg
( 'U', i+numSteps, nVals, nVals,
s.Buffer(), superDiagCopy.Buffer(),
VTHat.Buffer(), VTHat.LDim(),
UHat.Buffer(), UHat.LDim());

Real beta = superDiag.Get(i+numSteps-1,0);
bool converged=true;
for(Int j = 0; j < nVals; ++j){
if( Abs(s.Get(j,0) - sOld.Get(j,0)) > ctrl.convValTol){
converged = false;
}
if( beta*Abs(VTHat.Get(j, i+numSteps)) > ctrl.convVecTol){
converged = false;
}
if( beta*Abs(UHat.Get(i+numSteps, j)) > ctrl.convVecTol){
converged=false;
}
}
if( converged){
El::Gemm(El::NORMAL, El::ADJOINT, Real(1), Vtilde, VTHat, Real(0), V);
El::Gemm(El::NORMAL, El::NORMAL, Real(1), Utilde, UHat, Real(0), U);
DistMatrix<F,STAR,STAR> S_STAR_STAR( S.Grid());
S_STAR_STAR.LockedAttach( S.Height(), S.Width(), S.Buffer(), S.LDim() );
Copy(S_STAR_STAR, S);
return i+numSteps;
}
sOld = s;
}
}

/**
* TSVD don't provide initial guess.
*/
template< typename F,
typename ForwardOperator,
typename AdjointOperator>
inline Int
TSVD(
Int m,
Int n,
const ForwardOperator& A,
const AdjointOperator& AAdj,
Int nVals,
AbstractDistMatrix<F>& U,
AbstractDistMatrix<F>& S,
AbstractDistMatrix<F>& V,
Int maxIter=Int(1000),
const BidiagCtrl<F>& ctrl=BidiagCtrl<F>()) {
DistMatrix<F> initialVec(U.Grid());
F center = rand();
Uniform( initialVec, m, 1, center);
return TSVD(m ,n, A, AAdj, nVals, U,S,V, initialVec, maxIter, ctrl);
}

} //end namespace El
#endif //ifndef EL_LAPACKLIKE_SPECTRAL_TSVD
Loading