Files
mlpack/fastlib/trilinos/include/AnasaziBasicOrthoManager.hpp
T

894 lines
36 KiB
C++

// @HEADER
// ***********************************************************************
//
// Anasazi: Block Eigensolvers Package
// Copyright (2004) Sandia Corporation
//
// Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive
// license for use of this work by or on behalf of the U.S. Government.
//
// This library is free software; you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as
// published by the Free Software Foundation; either version 2.1 of the
// License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful, but
// WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
// USA
// Questions? Contact Michael A. Heroux (maherou@sandia.gov)
//
// ***********************************************************************
// @HEADER
/*! \file AnasaziBasicOrthoManager.hpp
\brief Basic implementation of the Anasazi::OrthoManager class
*/
#ifndef ANASAZI_BASIC_ORTHOMANAGER_HPP
#define ANASAZI_BASIC_ORTHOMANAGER_HPP
/*! \class Anasazi::BasicOrthoManager
\brief An implementation of the Anasazi::MatOrthoManager that performs orthogonalization
using (potentially) multiple steps of classical Gram-Schmidt.
\author Chris Baker, Ulrich Hetmaniuk, Rich Lehoucq, and Heidi Thornquist
*/
// #define ANASAZI_BASICORTHO_DEBUG
#include "AnasaziConfigDefs.hpp"
#include "AnasaziMultiVecTraits.hpp"
#include "AnasaziOperatorTraits.hpp"
#include "AnasaziMatOrthoManager.hpp"
#include "Teuchos_TimeMonitor.hpp"
namespace Anasazi {
template<class ScalarType, class MV, class OP>
class BasicOrthoManager : public MatOrthoManager<ScalarType,MV,OP> {
private:
typedef typename Teuchos::ScalarTraits<ScalarType>::magnitudeType MagnitudeType;
typedef Teuchos::ScalarTraits<ScalarType> SCT;
typedef MultiVecTraits<ScalarType,MV> MVT;
typedef OperatorTraits<ScalarType,MV,OP> OPT;
public:
//! @name Constructor/Destructor
//@{
//! Constructor specifying re-orthogonalization tolerance.
BasicOrthoManager( Teuchos::RCP<const OP> Op = Teuchos::null, typename Teuchos::ScalarTraits<ScalarType>::magnitudeType kappa = 1.5625 );
//! Destructor
~BasicOrthoManager() {}
//@}
//! @name Accessor routines
//@{
//! Set parameter for re-orthogonalization threshold.
void setKappa( typename Teuchos::ScalarTraits<ScalarType>::magnitudeType kappa ) { kappa_ = kappa; }
//! Return parameter for re-orthogonalization threshold.
typename Teuchos::ScalarTraits<ScalarType>::magnitudeType getKappa() const { return kappa_; }
//@}
//! @name Methods implementing Anasazi::MatOrthoManager
//@{
/*! \brief Given a list of mutually orthogonal and internally orthonormal bases \c Q, this method
* projects a multivector \c X onto the space orthogonal to the individual <tt>Q[i]</tt>,
* optionally returning the coefficients of \c X for the individual <tt>Q[i]</tt>. All of this is done with respect
* to the inner product innerProd().
*
* After calling this routine, \c X will be orthogonal to each of the <tt>Q[i]</tt>.
*
@param X [in/out] The multivector to be modified.<br>
On output, the columns of \c X will be orthogonal to each <tt>Q[i]</tt>, satisfying
\f[
X_{out} = X_{in} - \sum_i Q[i] \langle Q[i], X_{in} \rangle
\f]
@param MX [in/out] The image of \c X under the inner product operator \c Op.
If \f$ MX != 0\f$: On input, this is expected to be consistent with \c Op \cdot X. On output, this is updated consistent with updates to \c X.
If \f$ MX == 0\f$ or \f$ Op == 0\f$: \c MX is not referenced.
@param C [out] The coefficients of \c X in the bases <tt>Q[i]</tt>. If <tt>C[i]</tt> is a non-null pointer
and <tt>C[i]</tt> matches the dimensions of \c X and <tt>Q[i]</tt>, then the coefficients computed during the orthogonalization
routine will be stored in the matrix <tt>C[i]</tt>, similar to calling
\code
innerProd( Q[i], X, C[i] );
\endcode
If <tt>C[i]</tt> points to a Teuchos::SerialDenseMatrix with size
inconsistent with \c X and \c <tt>Q[i]</tt>, then a std::invalid_argument
exception will be thrown. Otherwise, if <tt>C.size() < i</tt> or
<tt>C[i]</tt> is a null pointer, the caller will not have access to the
computed coefficients.
@param Q [in] A list of multivector bases specifying the subspaces to be orthogonalized against, satisfying
\f[
\langle Q[i], Q[j] \rangle = I \quad\textrm{if}\quad i=j
\f]
and
\f[
\langle Q[i], Q[j] \rangle = 0 \quad\textrm{if}\quad i \neq j\ .
\f]
*/
void projectMat (
MV &X,
Teuchos::RCP<MV> MX = Teuchos::null,
Teuchos::Array<Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > > C = Teuchos::tuple(Teuchos::null),
Teuchos::Array<Teuchos::RCP<const MV> > Q = Teuchos::tuple(Teuchos::null) ) const;
/*! \brief This method takes a multivector \c X and attempts to compute an orthonormal basis for \f$colspan(X)\f$, with respect to innerProd().
*
* The method uses classical Gram-Schmidt with selective reorthogonalization. As a result, the coefficient matrix \c B is upper triangular.
*
* This routine returns an integer \c rank stating the rank of the computed basis. If \c X does not have full rank and the normalize() routine does
* not attempt to augment the subspace, then \c rank may be smaller than the number of columns in \c X. In this case, only the first \c rank columns of
* output \c X and first \c rank rows of \c B will be valid.
*
* The method attempts to find a basis with dimension equal to the number of columns in \c X. It does this by augmenting linearly dependent
* vectors in \c X with random directions. A finite number of these attempts will be made; therefore, it is possible that the dimension of the
* computed basis is less than the number of vectors in \c X.
*
@param X [in/out] The multivector to be modified.<br>
On output, the first \c rank columns of \c X satisfy
\f[
\langle X[i], X[j] \rangle = \delta_{ij}\ .
\f]
Also,
\f[
X_{in}(1:m,1:n) = X_{out}(1:m,1:rank) B(1:rank,1:n)
\f]
where \c m is the number of rows in \c X and \c n is the number of columns in \c X.
@param MX [in/out] The image of \c X under the inner product operator \c Op.
If \f$ MX != 0\f$: On input, this is expected to be consistent with \c Op \cdot X. On output, this is updated consistent with updates to \c X.
If \f$ MX == 0\f$ or \f$ Op == 0\f$: \c MX is not referenced.
@param B [out] The coefficients of the original \c X with respect to the computed basis. If \c B is a non-null pointer and \c B matches the dimensions of \c B, then the
coefficients computed during the orthogonalization routine will be stored in \c B, similar to calling
\code
innerProd( Xout, Xin, B );
\endcode
If \c B points to a Teuchos::SerialDenseMatrix with size inconsistent with \c X, then a std::invalid_argument exception will be thrown. Otherwise, if \c B is null, the caller will not have
access to the computed coefficients. This matrix is not necessarily triangular (as in a QR factorization); see the documentation of specific orthogonalization managers.<br>
The first rows in \c B corresponding to the valid columns in \c X will be upper triangular.
@return Rank of the basis computed by this method, less than or equal to the number of columns in \c X. This specifies how many columns in the returned \c X and rows in the returned \c B are valid.
*/
int normalizeMat (
MV &X,
Teuchos::RCP<MV> MX = Teuchos::null,
Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > B = Teuchos::tuple(Teuchos::null) ) const;
/*! \brief Given a set of bases <tt>Q[i]</tt> and a multivector \c X, this method computes an orthonormal basis for \f$colspan(X) - \sum_i colspan(Q[i])\f$.
*
* This routine returns an integer \c rank stating the rank of the computed basis. If the subspace \f$colspan(X) - \sum_i colspan(Q[i])\f$ does not
* have dimension as large as the number of columns of \c X and the orthogonalization manager doe not attempt to augment the subspace, then \c rank
* may be smaller than the number of columns of \c X. In this case, only the first \c rank columns of output \c X and first \c rank rows of \c B will
* be valid.
*
* The method attempts to find a basis with dimension the same as the number of columns in \c X. It does this by augmenting linearly dependent
* vectors with random directions. A finite number of these attempts will be made; therefore, it is possible that the dimension of the
* computed basis is less than the number of vectors in \c X.
*
@param X [in/out] The multivector to be modified.<br>
On output, the first \c rank columns of \c X satisfy
\f[
\langle X[i], X[j] \rangle = \delta_{ij} \quad \textrm{and} \quad \langle X, Q[i] \rangle = 0\ .
\f]
Also,
\f[
X_{in}(1:m,1:n) = X_{out}(1:m,1:rank) B(1:rank,1:n) + \sum_i Q[i] C[i]
\f]
where \c m is the number of rows in \c X and \c n is the number of columns in \c X.
@param MX [in/out] The image of \c X under the inner product operator \c Op.
If \f$ MX != 0\f$: On input, this is expected to be consistent with \c Op \cdot X. On output, this is updated consistent with updates to \c X.
If \f$ MX == 0\f$ or \f$ Op == 0\f$: \c MX is not referenced.
@param C [out] The coefficients of \c X in the <tt>Q[i]</tt>. If <tt>C[i]</tt> is a non-null pointer
and <tt>C[i]</tt> matches the dimensions of \c X and <tt>Q[i]</tt>, then the coefficients computed during the orthogonalization
routine will be stored in the matrix <tt>C[i]</tt>, similar to calling
\code
innerProd( Q[i], X, C[i] );
\endcode
If <tt>C[i]</tt> points to a Teuchos::SerialDenseMatrix with size
inconsistent with \c X and \c <tt>Q[i]</tt>, then a std::invalid_argument
exception will be thrown. Otherwise, if <tt>C.size() < i</tt> or
<tt>C[i]</tt> is a null pointer, the caller will not have access to the
computed coefficients.
@param B [out] The coefficients of the original \c X with respect to the computed basis. If \c B is a non-null pointer and \c B matches the dimensions of \c B, then the
coefficients computed during the orthogonalization routine will be stored in \c B, similar to calling
\code
innerProd( Xout, Xin, B );
\endcode
If \c B points to a Teuchos::SerialDenseMatrix with size inconsistent with \c X, then a std::invalid_argument exception will be thrown. Otherwise, if \c B is null, the caller will not have
access to the computed coefficients. This matrix is not necessarily triangular (as in a QR factorization); see the documentation of specific orthogonalization managers.<br>
The first rows in \c B corresponding to the valid columns in \c X will be upper triangular.
@param Q [in] A list of multivector bases specifying the subspaces to be orthogonalized against, satisfying
\f[
\langle Q[i], Q[j] \rangle = I \quad\textrm{if}\quad i=j
\f]
and
\f[
\langle Q[i], Q[j] \rangle = 0 \quad\textrm{if}\quad i \neq j\ .
\f]
@return Rank of the basis computed by this method, less than or equal to the number of columns in \c X. This specifies how many columns in the returned \c X and rows in the returned \c B are valid.
*/
int projectAndNormalizeMat (
MV &X,
Teuchos::RCP<MV> MX = Teuchos::null,
Teuchos::Array<Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > > C = Teuchos::tuple(Teuchos::null),
Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > B = Teuchos::null,
Teuchos::Array<Teuchos::RCP<const MV> > Q = Teuchos::tuple(Teuchos::null) ) const;
//@}
//! @name Error methods
//@{
/*! \brief This method computes the error in orthonormality of a multivector, measured
* as the Frobenius norm of the difference <tt>innerProd(X,Y) - I</tt>.
* The method has the option of exploiting a caller-provided \c MX.
*/
typename Teuchos::ScalarTraits<ScalarType>::magnitudeType
orthonormErrorMat(const MV &X, Teuchos::RCP<const MV> MX = Teuchos::null) const;
/*! \brief This method computes the error in orthogonality of two multivectors, measured
* as the Frobenius norm of <tt>innerProd(X,Y)</tt>.
* The method has the option of exploiting a caller-provided \c MX.
*/
typename Teuchos::ScalarTraits<ScalarType>::magnitudeType
orthogErrorMat(const MV &X1, Teuchos::RCP<const MV> MX1, const MV &X2) const;
//@}
private:
//! Parameter for re-orthogonalization.
MagnitudeType kappa_;
// ! Routine to find an orthonormal basis for the
int findBasis(MV &X, Teuchos::RCP<MV> MX,
Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > C,
bool completeBasis, int howMany = -1 ) const;
//
// Internal timers
//
Teuchos::RCP<Teuchos::Time> timerReortho_;
};
//////////////////////////////////////////////////////////////////////////////////////////////////
// Constructor
template<class ScalarType, class MV, class OP>
BasicOrthoManager<ScalarType,MV,OP>::BasicOrthoManager( Teuchos::RCP<const OP> Op,
typename Teuchos::ScalarTraits<ScalarType>::magnitudeType kappa ) :
MatOrthoManager<ScalarType,MV,OP>(Op),
kappa_(kappa),
timerReortho_(Teuchos::TimeMonitor::getNewTimer("BasicOrthoManager::Re-orthogonalization"))
{}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Compute the distance from orthonormality
template<class ScalarType, class MV, class OP>
typename Teuchos::ScalarTraits<ScalarType>::magnitudeType
BasicOrthoManager<ScalarType,MV,OP>::orthonormErrorMat(const MV &X, Teuchos::RCP<const MV> MX) const {
const ScalarType ONE = SCT::one();
int rank = MVT::GetNumberVecs(X);
Teuchos::SerialDenseMatrix<int,ScalarType> xTx(rank,rank);
innerProdMat(X,X,MX,xTx);
for (int i=0; i<rank; i++) {
xTx(i,i) -= ONE;
}
return xTx.normFrobenius();
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Compute the distance from orthogonality
template<class ScalarType, class MV, class OP>
typename Teuchos::ScalarTraits<ScalarType>::magnitudeType
BasicOrthoManager<ScalarType,MV,OP>::orthogErrorMat(const MV &X1, Teuchos::RCP<const MV> MX1, const MV &X2) const {
int r1 = MVT::GetNumberVecs(X1);
int r2 = MVT::GetNumberVecs(X2);
Teuchos::SerialDenseMatrix<int,ScalarType> xTx(r2,r1);
innerProdMat(X2,X1,MX1,xTx);
return xTx.normFrobenius();
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Find an Op-orthonormal basis for span(X) - span(W)
template<class ScalarType, class MV, class OP>
int BasicOrthoManager<ScalarType, MV, OP>::projectAndNormalizeMat(
MV &X, Teuchos::RCP<MV> MX,
Teuchos::Array<Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > > C,
Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > B,
Teuchos::Array<Teuchos::RCP<const MV> > Q ) const {
int nq = Q.length();
int xc = MVT::GetNumberVecs( X );
int xr = MVT::GetVecLength( X );
int rank;
/* if the user doesn't want to store the coefficients,
* allocate some local memory for them
*/
if ( B == Teuchos::null ) {
B = Teuchos::rcp( new Teuchos::SerialDenseMatrix<int,ScalarType>(xc,xc) );
}
/****** DO NO MODIFY *MX IF _hasOp == false ******/
if (this->_hasOp) {
if (MX == Teuchos::null) {
// we need to allocate space for MX
MX = MVT::Clone(X,MVT::GetNumberVecs(X));
OPT::Apply(*(this->_Op),X,*MX);
this->_OpCounter += MVT::GetNumberVecs(X);
}
}
else {
// Op == I --> MX = X (ignore it if the user passed it in)
MX = Teuchos::rcp( &X, false );
}
int mxc = MVT::GetNumberVecs( *MX );
int mxr = MVT::GetVecLength( *MX );
// short-circuit
TEST_FOR_EXCEPTION( xc == 0 || xr == 0, std::invalid_argument, "Anasazi::BasicOrthoManager::projectAndNormalizeMat(): X must be non-empty" );
int numbas = 0;
for (int i=0; i<nq; i++) {
numbas += MVT::GetNumberVecs( *Q[i] );
}
// check size of B
TEST_FOR_EXCEPTION( B->numRows() != xc || B->numCols() != xc, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectAndNormalizeMat(): Size of X must be consistant with size of B" );
// check size of X and MX
TEST_FOR_EXCEPTION( xc<0 || xr<0 || mxc<0 || mxr<0, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectAndNormalizeMat(): MVT returned negative dimensions for X,MX" );
// check size of X w.r.t. MX
TEST_FOR_EXCEPTION( xc!=mxc || xr!=mxr, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectAndNormalizeMat(): Size of X must be consistant with size of MX" );
// check feasibility
TEST_FOR_EXCEPTION( numbas+xc > xr, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectAndNormalizeMat(): Orthogonality constraints not feasible" );
// orthogonalize all of X against Q
projectMat(X,MX,C,Q);
Teuchos::SerialDenseMatrix<int,ScalarType> oldCoeff(xc,1);
// start working
rank = 0;
int numTries = 10; // each vector in X gets 10 random chances to escape degeneracy
int oldrank = -1;
do {
int curxsize = xc - rank;
// orthonormalize X, but quit if it is rank deficient
// we can't let findBasis generated random vectors to complete the basis,
// because it doesn't know about Q; we will do this ourselves below
rank = findBasis(X,MX,B,false,curxsize);
if (rank < xc && numTries == 10) {
// we quit on this vector, and for the first time;
// save the coefficient information, because findBasis will overwrite it
for (int i=0; i<xc; i++) {
oldCoeff(i,0) = (*B)(i,rank);
}
}
if (oldrank != -1 && rank != oldrank) {
// we moved on; restore the previous coefficients
for (int i=0; i<xc; i++) {
(*B)(i,oldrank) = oldCoeff(i,0);
}
}
if (rank == xc) {
// we are done
break;
}
else {
TEST_FOR_EXCEPTION( rank < oldrank, OrthoError,
"Anasazi::BasicOrthoManager::projectAndNormalizeMat(): basis lost rank; this shouldn't happen");
if (rank != oldrank) {
// we added a basis vector from random info; reset the chance counter
numTries = 10;
}
// store old rank
oldrank = rank;
// has this vector run out of chances to escape degeneracy?
if (numTries <= 0) {
break;
}
// use one of this vector's chances
numTries--;
// randomize troubled direction
#ifdef ANASAZI_BASICORTHO_DEBUG
cout << "Random for column " << rank << endl;
#endif
Teuchos::RCP<MV> curX, curMX;
std::vector<int> ind(1);
ind[0] = rank;
curX = MVT::CloneView(X,ind);
MVT::MvRandom(*curX);
if (this->_hasOp) {
curMX = MVT::CloneView(*MX,ind);
OPT::Apply( *(this->_Op), *curX, *curMX );
this->_OpCounter += MVT::GetNumberVecs(*curX);
}
// orthogonalize against Q
// if !this->_hasOp, the curMX will be ignored.
// we don't care about these coefficients; in fact, we need to preserve the previous coeffs
projectMat(*curX,curMX,Teuchos::null,Q);
}
} while (1);
// this should never raise an exception; but our post-conditions oblige us to check
TEST_FOR_EXCEPTION( rank > xc || rank < 0, std::logic_error,
"Anasazi::BasicOrthoManager::projectAndNormalizeMat(): Debug error in rank variable." );
return rank;
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Find an Op-orthonormal basis for span(X), with rank numvectors(X)
template<class ScalarType, class MV, class OP>
int BasicOrthoManager<ScalarType, MV, OP>::normalizeMat(
MV &X, Teuchos::RCP<MV> MX,
Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > B ) const {
// call findBasis, with the instruction to try to generate a basis of rank numvecs(X)
return findBasis(X, MX, B, true );
}
//////////////////////////////////////////////////////////////////////////////////////////////////
template<class ScalarType, class MV, class OP>
void BasicOrthoManager<ScalarType, MV, OP>::projectMat(
MV &X, Teuchos::RCP<MV> MX,
Teuchos::Array<Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > > C,
Teuchos::Array<Teuchos::RCP<const MV> > Q) const {
// For the inner product defined by the operator Op or the identity (Op == 0)
// -> Orthogonalize X against each Q[i]
// Modify MX accordingly
//
// Note that when Op is 0, MX is not referenced
//
// Parameter variables
//
// X : Vectors to be transformed
//
// MX : Image of the block vector X by the mass matrix
//
// Q : Bases to orthogonalize against. These are assumed orthonormal, mutually and independently.
//
ScalarType ONE = SCT::one();
int xc = MVT::GetNumberVecs( X );
int xr = MVT::GetVecLength( X );
int nq = Q.length();
std::vector<int> qcs(nq);
// short-circuit
if (nq == 0 || xc == 0 || xr == 0) {
return;
}
int qr = MVT::GetVecLength ( *Q[0] );
// if we don't have enough C, expand it with null references
// if we have too many, resize to throw away the latter ones
// if we have exactly as many as we have Q, this call has no effect
C.resize(nq);
/****** DO NO MODIFY *MX IF _hasOp == false ******/
if (this->_hasOp) {
if (MX == Teuchos::null) {
// we need to allocate space for MX
MX = MVT::Clone(X,MVT::GetNumberVecs(X));
OPT::Apply(*(this->_Op),X,*MX);
this->_OpCounter += MVT::GetNumberVecs(X);
}
}
else {
// Op == I --> MX = X (ignore it if the user passed it in)
MX = Teuchos::rcp( &X, false );
}
int mxc = MVT::GetNumberVecs( *MX );
int mxr = MVT::GetVecLength( *MX );
// check size of X and Q w.r.t. common sense
TEST_FOR_EXCEPTION( xc<0 || xr<0 || mxc<0 || mxr<0, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectMat(): MVT returned negative dimensions for X,MX" );
// check size of X w.r.t. MX and Q
TEST_FOR_EXCEPTION( xc!=mxc || xr!=mxr || xr!=qr, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectMat(): Size of X not consistant with MX,Q" );
// tally up size of all Q and check/allocate C
int baslen = 0;
for (int i=0; i<nq; i++) {
TEST_FOR_EXCEPTION( MVT::GetVecLength( *Q[i] ) != qr, std::invalid_argument,
"Anasazi::BasicOrthoManager::projectMat(): Q lengths not mutually consistant" );
qcs[i] = MVT::GetNumberVecs( *Q[i] );
TEST_FOR_EXCEPTION( qr < qcs[i], std::invalid_argument,
"Anasazi::BasicOrthoManager::projectMat(): Q has less rows than columns" );
baslen += qcs[i];
// check size of C[i]
if ( C[i] == Teuchos::null ) {
C[i] = Teuchos::rcp( new Teuchos::SerialDenseMatrix<int,ScalarType>(qcs[i],xc) );
}
else {
TEST_FOR_EXCEPTION( C[i]->numRows() != qcs[i] || C[i]->numCols() != xc , std::invalid_argument,
"Anasazi::BasicOrthoManager::projectMat(): Size of Q not consistant with size of C" );
}
}
// Perform the Gram-Schmidt transformation for a block of vectors
// Compute the initial Op-norms
std::vector<ScalarType> oldDot( xc );
MVT::MvDot( X, *MX, &oldDot );
Teuchos::Array<Teuchos::RCP<MV> > MQ(nq);
// Define the product Q^T * (Op*X)
for (int i=0; i<nq; i++) {
// Multiply Q' with MX
innerProdMat(*Q[i],X,MX,*C[i]);
// Multiply by Q and subtract the result in X
MVT::MvTimesMatAddMv( -ONE, *Q[i], *C[i], ONE, X );
// Update MX, with the least number of applications of Op as possible
if (this->_hasOp) {
if (xc <= qcs[i]) {
OPT::Apply( *(this->_Op), X, *MX);
this->_OpCounter += MVT::GetNumberVecs(X);
}
else {
// this will possibly be used again below; don't delete it
MQ[i] = MVT::Clone( *Q[i], qcs[i] );
OPT::Apply( *(this->_Op), *Q[i], *MQ[i] );
this->_OpCounter += MVT::GetNumberVecs(*Q[i]);
MVT::MvTimesMatAddMv( -ONE, *MQ[i], *C[i], ONE, *MX );
}
}
}
// Compute new Op-norms
std::vector<ScalarType> newDot(xc);
MVT::MvDot( X, *MX, &newDot );
// determine (individually) whether to do another step of classical Gram-Schmidt
for (int j = 0; j < xc; ++j) {
if ( SCT::magnitude(kappa_*newDot[j]) < SCT::magnitude(oldDot[j]) ) {
Teuchos::TimeMonitor lcltimer( *timerReortho_ );
for (int i=0; i<nq; i++) {
Teuchos::SerialDenseMatrix<int,ScalarType> C2(*C[i]);
// Apply another step of classical Gram-Schmidt
innerProdMat(*Q[i],X,MX,C2);
*C[i] += C2;
MVT::MvTimesMatAddMv( -ONE, *Q[i], C2, ONE, X );
// Update MX, with the least number of applications of Op as possible
if (this->_hasOp) {
if (MQ[i].get()) {
// MQ was allocated and computed above; use it
MVT::MvTimesMatAddMv( -ONE, *MQ[i], C2, ONE, *MX );
}
else if (xc <= qcs[i]) {
// MQ was not allocated and computed above; it was cheaper to use X before and it still is
OPT::Apply( *(this->_Op), X, *MX);
this->_OpCounter += MVT::GetNumberVecs(X);
}
}
}
break;
} // if (kappa_*newDot[j] < oldDot[j])
} // for (int j = 0; j < xc; ++j)
}
//////////////////////////////////////////////////////////////////////////////////////////////////
// Find an Op-orthonormal basis for span(X), with the option of extending the subspace so that
// the rank is numvectors(X)
template<class ScalarType, class MV, class OP>
int BasicOrthoManager<ScalarType, MV, OP>::findBasis(
MV &X, Teuchos::RCP<MV> MX,
Teuchos::RCP<Teuchos::SerialDenseMatrix<int,ScalarType> > B,
bool completeBasis, int howMany ) const {
using std::cout;
using std::endl;
// For the inner product defined by the operator Op or the identity (Op == 0)
// -> Orthonormalize X
// Modify MX accordingly
//
// Note that when Op is 0, MX is not referenced
//
// Parameter variables
//
// X : Vectors to be orthonormalized
//
// MX : Image of the multivector X under the operator Op
//
// Op : Pointer to the operator for the inner product
//
// TODO: add reference
// kappa= Coefficient determining when to perform a second Gram-Schmidt step
// Default value = 1.5625 = (1.25)^2 (as suggested in Parlett's book)
//
const ScalarType ONE = SCT::one();
const MagnitudeType ZERO = SCT::magnitude(SCT::zero());
const ScalarType EPS = SCT::eps();
int xc = MVT::GetNumberVecs( X );
int xr = MVT::GetVecLength( X );
if (howMany == -1) {
howMany = xc;
}
/*******************************************************
* If _hasOp == false, we will not reference MX below *
*******************************************************/
// if Op==null, MX == X (via pointer)
// Otherwise, either the user passed in MX or we will allocated and compute it
if (this->_hasOp) {
if (MX == Teuchos::null) {
// we need to allocate space for MX
MX = MVT::Clone(X,xc);
OPT::Apply(*(this->_Op),X,*MX);
this->_OpCounter += MVT::GetNumberVecs(X);
}
}
/* if the user doesn't want to store the coefficients,
* allocate some local memory for them
*/
if ( B == Teuchos::null ) {
B = Teuchos::rcp( new Teuchos::SerialDenseMatrix<int,ScalarType>(xc,xc) );
}
int mxc = (this->_hasOp) ? MVT::GetNumberVecs( *MX ) : xc;
int mxr = (this->_hasOp) ? MVT::GetVecLength( *MX ) : xr;
// check size of C, B
TEST_FOR_EXCEPTION( xc == 0 || xr == 0, std::invalid_argument,
"Anasazi::BasicOrthoManager::findBasis(): X must be non-empty" );
TEST_FOR_EXCEPTION( B->numRows() != xc || B->numCols() != xc, std::invalid_argument,
"Anasazi::BasicOrthoManager::findBasis(): Size of X not consistant with size of B" );
TEST_FOR_EXCEPTION( xc != mxc || xr != mxr, std::invalid_argument,
"Anasazi::BasicOrthoManager::findBasis(): Size of X not consistant with size of MX" );
TEST_FOR_EXCEPTION( xc > xr, std::invalid_argument,
"Anasazi::BasicOrthoManager::findBasis(): Size of X not feasible for normalization" );
TEST_FOR_EXCEPTION( howMany < 0 || howMany > xc, std::invalid_argument,
"Anasazi::BasicOrthoManager::findBasis(): Invalid howMany parameter" );
/* xstart is which column we are starting the process with, based on howMany
* columns before xstart are assumed to be Op-orthonormal already
*/
int xstart = xc - howMany;
for (int j = xstart; j < xc; j++) {
// numX represents the number of currently orthonormal columns of X
int numX = j;
// j represents the index of the current column of X
// these are different interpretations of the same value
//
// set the lower triangular part of R to zero
for (int i=j+1; i<xc; ++i) {
(*B)(i,j) = ZERO;
}
// Get a view of the vector currently being worked on.
std::vector<int> index(1);
index[0] = j;
Teuchos::RCP<MV> Xj = MVT::CloneView( X, index );
Teuchos::RCP<MV> MXj;
if ((this->_hasOp)) {
// MXj is a view of the current vector in MX
MXj = MVT::CloneView( *MX, index );
}
else {
// MXj is a pointer to Xj, and MUST NOT be modified
MXj = Xj;
}
// Get a view of the previous vectors.
std::vector<int> prev_idx( numX );
Teuchos::RCP<const MV> prevX, prevMX;
if (numX > 0) {
for (int i=0; i<numX; ++i) prev_idx[i] = i;
prevX = MVT::CloneView( X, prev_idx );
if (this->_hasOp) {
prevMX = MVT::CloneView( *MX, prev_idx );
}
}
bool rankDef = true;
/* numTrials>0 will denote that the current vector was randomized for the purpose
* of finding a basis vector, and that the coefficients of that vector should
* not be stored in B
*/
for (int numTrials = 0; numTrials < 10; numTrials++) {
// Make storage for these Gram-Schmidt iterations.
Teuchos::SerialDenseMatrix<int,ScalarType> product(numX, 1);
std::vector<ScalarType> oldDot( 1 ), newDot( 1 );
//
// Save old MXj vector and compute Op-norm
//
Teuchos::RCP<MV> oldMXj = MVT::CloneCopy( *MXj );
MVT::MvDot( *Xj, *MXj, &oldDot );
// Xj^H Op Xj should be real and positive, by the Hermitian positive definiteness of Op
TEST_FOR_EXCEPTION( SCT::real(oldDot[0]) < ZERO, OrthoError,
"Anasazi::BasicOrthoManager::findBasis(): Negative definiteness discovered in inner product" );
if (numX > 0) {
// Apply the first step of Gram-Schmidt
// product <- prevX^T MXj
innerProdMat(*prevX,*Xj,MXj,product);
// Xj <- Xj - prevX prevX^T MXj
// = Xj - prevX product
MVT::MvTimesMatAddMv( -ONE, *prevX, product, ONE, *Xj );
// Update MXj
if (this->_hasOp) {
// MXj <- Op*Xj_new
// = Op*(Xj_old - prevX prevX^T MXj)
// = MXj - prevMX product
MVT::MvTimesMatAddMv( -ONE, *prevMX, product, ONE, *MXj );
}
// Compute new Op-norm
MVT::MvDot( *Xj, *MXj, &newDot );
// Check if a correction is needed.
if ( SCT::magnitude(kappa_*newDot[0]) < SCT::magnitude(oldDot[0]) ) {
// Apply the second step of Gram-Schmidt
// This is the same as above
Teuchos::SerialDenseMatrix<int,ScalarType> P2(numX,1);
innerProdMat(*prevX,*Xj,MXj,P2);
product += P2;
MVT::MvTimesMatAddMv( -ONE, *prevX, P2, ONE, *Xj );
if ((this->_hasOp)) {
MVT::MvTimesMatAddMv( -ONE, *prevMX, P2, ONE, *MXj );
}
} // if (kappa_*newDot[0] < oldDot[0])
} // if (numX > 0)
// Compute Op-norm with old MXj
MVT::MvDot( *Xj, *oldMXj, &newDot );
// save the coefficients, if we are working on the original vector and not a randomly generated one
if (numTrials == 0) {
for (int i=0; i<numX; i++) {
(*B)(i,j) = product(i,0);
}
}
// Check if Xj has any directional information left after the orthogonalization.
#ifdef ANASAZI_BASICORTHO_DEBUG
cout << "olddot: " << SCT::magnitude(oldDot[0]) << " newdot: " << SCT::magnitude(newDot[0]);
#endif
if ( SCT::magnitude(newDot[0]) > SCT::magnitude(oldDot[0]*EPS*EPS) && SCT::real(newDot[0]) > ZERO ) {
#ifdef ANASAZI_BASICORTHO_DEBUG
cout << " ACCEPTED" << endl;
#endif
// Normalize Xj.
// Xj <- Xj / sqrt(newDot)
ScalarType diag = SCT::squareroot(SCT::magnitude(newDot[0]));
MVT::MvAddMv( ONE/diag, *Xj, ZERO, *Xj, *Xj );
if (this->_hasOp) {
// Update MXj.
MVT::MvAddMv( ONE/diag, *MXj, ZERO, *MXj, *MXj );
}
// save it, if it corresponds to the original vector and not a randomly generated one
if (numTrials == 0) {
(*B)(j,j) = diag;
}
// We are not rank deficient in this vector. Move on to the next vector in X.
rankDef = false;
break;
}
else {
#ifdef ANASAZI_BASICORTHO_DEBUG
cout << " REJECTED" << endl;
#endif
// There was nothing left in Xj after orthogonalizing against previous columns in X.
// X is rank deficient.
// reflect this in the coefficients
(*B)(j,j) = ZERO;
if (completeBasis) {
// Fill it with random information and keep going.
#ifdef ANASAZI_BASICORTHO_DEBUG
cout << "Random for column " << j << endl;
#endif
MVT::MvRandom( *Xj );
if (this->_hasOp) {
OPT::Apply( *(this->_Op), *Xj, *MXj );
this->_OpCounter += MVT::GetNumberVecs(*Xj);
}
}
else {
rankDef = true;
break;
}
} // if (norm > oldDot*EPS*EPS)
} // for (numTrials = 0; numTrials < 10; ++numTrials)
// if rankDef == true, then quit and notify user of rank obtained
if (rankDef == true) {
MVT::MvInit( *Xj, ZERO );
if (this->_hasOp) {
MVT::MvInit( *MXj, ZERO );
}
TEST_FOR_EXCEPTION( completeBasis, OrthoError,
"Anasazi::BasicOrthoManager::findBasis(): Unable to complete basis" );
return j;
}
} // for (j = 0; j < xc; ++j)
return xc;
}
} // namespace Anasazi
#endif // ANASAZI_BASIC_ORTHOMANAGER_HPP