1%%%%%%%%%%%%%%%%%%%
2% XLiFE++ is an extended library of finite elements written in C++
3%     Copyright (C) 2014  Lunéville, Eric; Kielbasiewicz, Nicolas; Lafranche, Yvon; Nguyen, Manh-Ha; Chambeyron, Colin
4%
5%     This program is free software: you can redistribute it and/or modify
6%     it under the terms of the GNU General Public License as published by
7%     the Free Software Foundation, either version 3 of the License, or
8%     (at your option) any later version.
9%     This program is distributed in the hope that it will be useful,
10%     but WITHOUT ANY WARRANTY; without even the implied warranty of
11%     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12%     GNU General Public License for more details.
13%     You should have received a copy of the GNU General Public License
14%     along with this program.  If not, see <http://www.gnu.org/licenses/>.
15%%%%%%%%%%%%%%%%%%%
16
17\section {{\libtitle eigenSparse} sub-library}
18
19The {\lib eigenSparse}  provides tools that are useful for solving a wide variety
20of eigenvalue problems. The solvers currently implemented within {\lib eigenSparse} compute a
21partial eigen-decomposition for the generalized eigenvalue problem \\
22\[ Ax = Bx \lambda \]
23We assume that the matrices A and B are large, possibly sparse, and that only their application to a block of vectors is required. \\
24This section outlines the eigenSparse. Three subsections describe the eigenSparse operator/multivector interface, the eigensolver, and the various implementations provided by the eigenSparse. \\
25The lib {\lib eigenSparse} is highly independent of other packages of \xlifepp except for {\lib eigenCore} which provides essential functions in performing the dense arithmetic for Rayleigh-Ritz methods.
26
27\subsection {The Operator/Multivector Interface}
28
29{\lib eigenSparse} utilizes traits classes to define interfaces for
30the scalar field, multivectors, and matrix operators. This allows generic programming techniques to be used when developing numerical algorithms in the {\lib eigenSparse}. {\lib eigenSparse}’s eigensolver is comprised of abstract
31numerical interfaces that are all implemented using templates and the functionality
32of the template arguments is provided through their corresponding trait classes.
33Most classes in {\lib eigenSparse} accept three template parameters:
34\begin{itemize}
35\item a scalar type, describing the field over which the vectors and operators are defined;
36\item a multivector type over the given scalar field, providing a data structure that
37denotes a collection of vectors; and
38\item an operator type over the given scalar field, providing linear operators used to
39define eigenproblems and preconditioners.
40\end{itemize}
41We note that the {\lib eigenSparse} was designed to support block methods,
42defined as those that apply A or B
43to a collection of vectors (a multivector).
44Algorithms in {\lib eigenSparse} are developed at a high-level, where the underlying
45linear algebra objects are opaque. The choice in linear algebra is made through templating,
46and access to the functionality of the underlying objects is provided via the traits classes
47{\class MultiVecTraits} and {\class OperatorTraits}. \\
48These classes define opaque interfaces, specifying the operations that multivector and
49operator classes must support in order to be used in {\lib eigenSparse} without exposing low-level
50details of the underlying data structures.\\
51The {\class MultiVecTraits} and {\class OperatorTraits} classes specify the operations that
52the multivector and operator type must support in order for them to be used
53by {\lib eigenSparse}. \\
54The {\class OperatorTraits} class only needs to provide one method, described in the Table, that
55applies an operator to a multivector. This interface defines the only interaction
56required from an operator, even though the underlying operator may be a matrix,
57spectral transformation, or preconditioner. \\
58
59\begin{center}
60\begin{tabular}{|l|p{11.5cm}|}
61\hline
62\multicolumn{2}{|c|} {\textbf{OperatorTraits<ST,MV,OP>}} \\
63\hline
64\textit{Method name} & \textit{Description}\\
65\hline
66{\cmd apply}(A,X,Y) & Applies the operator A to the multivector X, placing the result in the multivector Y \\
67\hline
68\end{tabular}
69\end{center}
70
71The methods defined by the {\class MultiVecTraits} class, listed in following table, are the nesseary
72creational and arithmetic ones.
73The creational methods generate empty or populated multivectors from a previously
74created multivector. The populated multivectors can be a deep copy, where the
75object contains the storage for the multivector entries, or a shallow copy, where the
76object has a view of another multivector’s storage. A shallow copy is useful when
77only a subset of the columns of a multivector is required for computation, which
78is a situation that commonly occurs during the generation of a subspace.
79\begin{center}
80\begin{tabular}{|l|p{11.5cm}|}
81\hline
82\multicolumn{2}{|c|} {\textbf{MultiVecTraits<ST,MV>}} \\
83\hline
84\textit{Method name} & \textit{Description}\\
85\hline
86{\cmd clone(X,numvecs)}  & Creates a new multivector from X with {\itshape numvecs} vectors \\
87{\cmd cloneCopy(X,index)}  & Creates a new multivector with a copy of the contents of a subset of the multivector X (deep copy) \\
88{\cmd cloneView(X,index)}  & Creates a new multivector that shares the selected contents of a subset of the multivector X (shallow copy)  \\
89\hline
90{\cmd getVecLength(X)} & Returns the vector length of the multivector X  \\
91{\cmd getNumberVecs(X)} & Returns the number of vectors in the multivector X \\
92\hline
93{\cmd mvTimesMatAddMv}(alpha,X,  & Applies a dense matrix D to multivector X and \\
94\noindent D,beta,Y)                                 &  accumulates the result into multivector Y: \\
95						  	        & $Y \leftarrow \alpha XD+\beta Y$ \\
96{\cmd mvAddMv}(alpha,X,beta,Y) & Performs multivector: $Y \leftarrow \alpha X+ \beta Y$ \\
97{\cmd mvTransMv}(alpha,X,Y,D) & Computes the dense matrix $D \leftarrow \alpha X^{H}Y$ \\
98{\cmd mvDot}(X,Y,d) & Computes the corresponding dot products: $d[i] \leftarrow \bar a _{i} y _{i}$ \\
99{\cmd mvScale}(X,d) & Scales the i-th column of a multivector X by $ d[i] $ \\
100{\cmd mvNorm}(X,d) & Computes the 2-norm of each vector of X : $d[i] \leftarrow ||x _{i}|| _{2}$\\
101\hline
102{\cmd setBlock}(X,Y,index)& Copies the vectors in X to a subset of vectors in Y \\
103{\cmd mvInit}(X,alpha)& Replaces each entry in the multivector X with a scalar $\alpha$ \\
104{\cmd mvRandom}(X)& Replaces the entries in the multivector X by random scalars \\
105\hline
106{\cmd mvPrint}(X) & Print the multivector X \\
107\hline
108\end{tabular}
109\end{center}
110The arithmetic methods defined by the {\class multiVecTraits} are essential to the computations required by the Rayleigh-Ritz method and the general eigen-iteration. The {\cmd mvTimesMatAddMv} and {\cmd mvAddMv} methods, for instance, are necessary for updating the approximate eigenpairs and their residuals. {\cmd The mvDot} and {\cmd mvTransMv} methods are required by the orthogonalization procedure of the eigen-iteration. The {\cmd mvScale} and {\cmd mvNorm} methods are  necessary, at the very least, for the computation of approximate eigenpairs and for some termination criteria of the eigen-iteration. Deflation and locking of converged
111eigenvectors necessitates the {\cmd setBlock} method in many cases. Initialization of the bases for the eigen-iteration requires methods such as {\cmd mvRandom} and {\cmd mvInit}. The ability to perform error checking and debugging is supported by methods that give dimensional attributes ({\cmd getVecLength}, {\cmd getNumberVecs}) and allow the users to print out a multivector ({\cmd mvPrint}).\\
112Calling methods of {\class MultiVecTraits} and {\class OperatorTraits} requires that specializations of these traits classes have been implemented for given template arguments. \xlifepp provides the following classes that can be used with these traits classes:
113\begin{itemize}
114\item {\class MultiVectorAdapter},  an extended version of {\class Vector}, allowing the process on block of vector.
115\item {\class LargeMatrixAdapter}, a wrapper of {\class LargeMatrix} in order to take adavantage of multiplication matrix-vector of sparse matrix
116\end{itemize}
117
118\subsubsection{The {\classtitle MultiVectorAdapter} class}
119
120This class provides the implementations of the interfaces along with abstract {\class MultiVec} class. By inheriting directly from {\class MultiVec} class, the implementaion works via run-time polymorphism. This option isn't optimal from performance point of view because of the dispatching cost during run-time. In the near future, we can improve it by implementing a specialization of {\class  MultiVecTraits} class, which allows compile-time polymorphism. \\
121By using the pointer to each vector,  {\class MultiVectorAdapter} class allows to create different objects, which share a same view of memory (shallow copy). This feature is essential when we want only to work on specific column vectors of a multivector.
122\begin{lstlisting}[deletekeywords={[3] length}]
123template <class ScalarType>
124class MultiVecAdapter : public MultiVec<ScalarType>
125{
126  public:
127    typedef std::vector<ScalarType>* VectorPointer;
128
129  //! Constructor for a \c numberVecs vectors of length \c length.
130  MultiVecAdapter(const Number length, const Dimen numberVecs) :
131    length_(length),
132    numberVecs_(numberVecs)
133  {
134    check();
135
136    data_.resize(numberVecs);
137    ownership_.resize(numberVecs);
138
139    // Allocates memory to store the vectors.
140    for (Dimen v = 0 ; v < numberVecs_ ; ++v)
141    {
142      data_[v] = new std::vector<ScalarType>(length);
143      ownership_[v] = true;
144    }
145
146    // Initializes all elements to zero.
147    mvInit(0.0);
148  }
149 ...
150\end{lstlisting}
151
152\subsubsection{The {\classtitle LargeMatrixAdapter} class}
153
154This class is simply a wrapper of {\class LargeMatrix} class in order to take advantage of consuming-time matrix-vector multiplication provided by \xlifepp. The method {\cmd apply} implemnt multiplication of a LargeMatrix with a block of vector. Noting that it's only a way to implement application of an operator to vector. Depending on specific eigenproblem, we can create a new class with a different approach to apply operator; for example, operator in case of spectral transformation to find the not-well separated eigenvalues.
155\begin{lstlisting}[deletekeywords={[3] x, y}]
156  template<typename ScalarType>
157  class LargeMatrixAdapter : public Operator<ScalarType>
158  {
159  private:
160      typedef LargeMatrix<ScalarType> LMatrix;
161  public:
162      LargeMatrixAdapter() { lMatrix_p = NULL; }
163      LargeMatrixAdapter(LMatrix* p) { lMatrix_p = p; }
164
165      LargeMatrixAdapter& operator=(LMatrix matrix) { lMatrix_p = &matrix; return *this; }
166
167
168      //! Destructor.
169      virtual ~LargeMatrixAdapter() {};
170
171      void apply(const MultiVec<ScalarType>& x, MultiVec<ScalarType>& y) const
172      {
173          for (int i = 0; i< x.getNumberVecs(); ++i) {
174              multMatrixVector(*lMatrix_p, *(x[i]), *(y[i]));
175          }
176      }
177  private:
178      LMatrix*  lMatrix_p;
179  };
180\end{lstlisting}
181
182\subsubsection{The {\classtitle LargeMatrixAdapterInverse} class}
183
184Similar to {\class LargeMatrixAdapter}, this class is simply a wrapper of {\class LargeMatrix} in order to take advantage of consuming-time matrix-vector multiplication provided by \xlifepp. The class operates on an already factorized matrix to carry out the multiplication of its inverse (if invertible) with a vector. Instead of invoking the function {\cmd multMatrixVector}, the method {\cmd apply} calls the routine {\cmd multInverMatrixVector} of class {\class LargeMatrix} with the type of factorization.
185
186\begin{lstlisting}[deletekeywords={[3] x, y}]
187  template<typename MatrixType, typename ScalarType>
188  class LargeMatrixAdapterInverse : public Operator<ScalarType>
189  {
190  private:
191      typedef const MatrixType LMatrix;
192  public:
193//      LargeMatrixAdapterInverse() : lMatrix_p(NULL), fac_(_noFactorization) {}
194      LargeMatrixAdapterInverse(LMatrix* p, FactorizationType fac) : lMatrix_p(p), fac_(fac) {}
195
196      LargeMatrixAdapterInverse& operator=(LMatrix matrix) { lMatrix_p = &matrix; fac_ = _noFactorization; return (*this); }
197
198
199      //! Destructor.
200      virtual ~LargeMatrixAdapterInverse() {};
201
202      void apply(const MultiVec<ScalarType>& x, MultiVec<ScalarType>& y) const
203      {
204          for (int i = 0; i< x.getNumberVecs(); ++i) {
205              multInverMatrixVector(*lMatrix_p, *(x[i]), *(y[i]), fac_);
206          }
207      }
208
209  private:
210      LMatrix*  lMatrix_p;
211      FactorizationType fac_;
212  };
213\end{lstlisting}
214
215\subsubsection{The {\classtitle LargeMatrixAdapterInverseGeneralized} class}
216
217On some specific eigen problems, it is better to do spectral transformation, especially, to find not well-seperated eigenvalues. This class serves for this purpose. As indicated by its name, this class works mainly for the spectral transformation of generalized eigen problem. The first operation is multiplication of the one matrix with a vector. The vector result is multiplied by the inverse of the second matrix which is provided in a form of factorized one. Both operations are done in the routine {\cmd apply}.
218\begin{lstlisting}[deletekeywords={[3] x, y}]
219  template<typename MatrixType, typename ScalarType>
220  class LargeMatrixAdapterInverseGeneralized : public Operator<ScalarType>
221  {
222  private:
223      typedef const MatrixType LMatrix;
224  public:
225      LargeMatrixAdapterInverseGeneralized(LMatrix* pA, LMatrix* pB, FactorizationType fac) : lMatrixA_p(pA), lMatrixB_p(pB), fac_(fac) {}
226
227//      LargeMatrixAdapterInverseGeneralized& operator=(LMatrix matrix) { lMatrix_p = &matrix; fac_ = _noFactorization; return (*this); }
228
229
230      //! Destructor.
231      virtual ~LargeMatrixAdapterInverseGeneralized() {};
232
233      void apply(const MultiVec<ScalarType>& x, MultiVec<ScalarType>& y) const
234      {
235          Vector<ScalarType> vecTemp(x.getVecLength());
236          for (int i = 0; i< x.getNumberVecs(); ++i) {
237              multMatrixVector(*lMatrixB_p, *(x[i]), vecTemp);
238              multInverMatrixVector(*lMatrixA_p, vecTemp, *(y[i]), fac_);
239          }
240      }
241
242  private:
243      LMatrix*  lMatrixA_p;
244      LMatrix*  lMatrixB_p;
245      FactorizationType fac_;
246  };
247\end{lstlisting}
248
249\subsection{{\libtitle eigenSparse} framework}
250
251{\lib eigenSparse} is organized with a multi-tiered access for eigensolver algorithms. Users have the choice of interfacing at one of two levels: either working at a high-level with a eigensolver manager or working at a low-level directly with an eigensolver.\\
252Consider as an example the block Davidson iteration. \\
253\begin{tabular} {l}
254\hline
255Block Davidson Algorithm \\
256\hline
257Require: Set an initial guess \textbf{V} and \textbf{H} = [] \\
2581: \textbf{for} $iter=1$  to $iter _{max}$ \textbf{do}\\
2592: ~ \textbf{repeat} \\
2603: ~  ~ ~ ~Compute \textbf{M}-orthonormal basis \textbf{V} for [\textbf{V},\textbf{H}]\\
2614: ~  ~ ~ ~Project \textbf{A} onto \textbf{V}: $\hat A = V^{H}AV$ \\
2625: ~  ~ ~ ~Compute selected eigenpairs (\textbf{Q},\textbf{$\Gamma$}) of $\hat A$: $\hat A Q = Q\Gamma $ \\
2636: ~  ~ ~ ~Compute Ritz vectors: $X = VQ$ \\
2647: ~  ~ ~ ~Compute residuals: $R = AX - MX\Gamma$\\
2658: ~  ~ ~ ~Precondition the residuals: $H = NR$ \\
2669: ~  ~ ~ ~Check convergence and possibly terminate iteration  \\
26710:~ \textbf{until} the matrix \textbf{V} is not expandable \\
26811: ~Restart \textbf{V} \\
26912: \textbf{end for} \\
270\hline
271\end{tabular}
272
273\medskip
274
275\begin{figure} [H]
276\centering
277\includeTikZPict[width=13cm]{solverManager.png}
278\caption{\textbf{SolverManager} class collaboration graph}
279\end{figure}
280
281The linear operators \textbf{A} and \textbf{M} define the eigenproblem to be solved. The linear operator \textbf{N}
282is a preconditioner for the problem, and its application to a block of vectors is required. Example preconditioners \textbf{N}
283include the inverse of the diagonal of \textbf{A} (Jacobi preconditioner), an algebraic multigrid preconditioner for \textbf{A}, or
284a preconditioned iteration that approximates the solution of a linear set of equations with \textbf{A}, e.g., the preconditioned conjugate gradient algorithm. \\
285The \textit{multivector} \textbf{V}, \textbf{X}, and \textbf{R} serve for some purpose. The column-vectors for matrix \textbf{V}
286form the basis for the Rayleigh-Ritz approximation conducted at Step 5-6. We make the specific choice that these vectors are orthonormal with respect to the inner product induced by the Hermitian positive-definite matrix \textbf{M}, but this is not a requirement. \\
287The block Davidson eigensolver as described above is useful for examining some
288of the functionality provided by {\lib eigenSparse}. The algorithm above highlights three levels of
289functionality. The first level is given by Steps 1-12 that constitute the eigensolver
290strategy to solve problem (2). The second level is given by Steps 2-10 that form
291the eigen-iteration. The third level consists of computational steps that can be
292implemented in a variety of manners, so encouraging modularization. Step 3 requires an orthonormalization method. The decisions involved in Step 5 require a
293determination of the eigenvalues and invariant subspace of interest, as well as a
294definition of accuracy. To check convergence in Step 9, several criteria are possible.
295For instance, a norm induced by a matrix other than \textbf{M} may be employed. The
296restarting of this eigensolver (Step 11) can be performed in a variety of ways and
297therefore need not be tightly coupled to the eigensolver. Each of these mechanisms
298provide opportunity for decoupling functionality that need not be implemented in
299a specific manner. \\
300Looking deeper into the algorithm, the iteration repeats until the basis V is full
301(in which case it is time to restart) or some stopping criterion has been satisfied. Many valid
302stopping criteria exist, as well as many different methods for restarting the basis. Both of
303these, however, are distinct from the essential iteration as described above. A user wanting
304to perform block Davidson iterations could ask the solver to perform these iterations until
305a user-specified stopping criterion was satisfied or the basis was full, at which time the user
306would perform a restart. This allows the user complete control over the stopping criteria and the restarting mechanism, and leaves the eigensolver responsible for a relatively simple bit of state and behavior. \\
307The eigensolvers (encapsulating an iteration and the associated state) are derived classes of the abstract base class {\class Eigensolver.}
308The goals of this class are three-fold:
309\begin{itemize}
310\item to define an interface used for checking the status of a solver by a status test;
311\item to contain the essential iteration associated with a particular eigensolver iteration;
312\item to contain the state associated with that iteration.
313\end{itemize}
314The status tests, assembled to describe a specific stopping criterion and queried by the
315eigensolver during the iteration, are represented as subclasses of {\class StatusTest}. The
316communication between status test and eigensolver occurs inside of the \textit{iterate()} method
317provided by each {\class Eigensolver}. This code generally takes the form:
318\begin{lstlisting}[]{}
319SomeEigensolver::iterate() {
320while ( statustest.checkStatus(this) != _passed ) {
321//
322// perform eigensolver iterations
323//
324}
325r
326\end{lstlisting}
327Each {\class StatusTest} provides a method, \textit{checkStatus()}, which through queries to
328the methods provided by {\class Eigensolver}, determines whether the solver meets the criteria defined by that particular status test. After a solver returns from \textit{iterate()}, the caller has the option of accessing the state associated with the solver and re-initializing the solver with a new state. \\
329While this method of interfacing with the solver is powerful, it can be tedious. This method requires that user construct a number of support classes, in addition to managing call to \textit{Eigensolver::iterate()}. The {\class SolverManager} class was developed to
330address this complaint. A solver manager is a class that wraps around an eigensolver, providing additional functionality while also handling lower-level interaction with the eigensolver that a user may not wish to handle. Solver managers are intended to be easy to
331use, while still providing the features and flexibility needed to solve real-world eigenvalue problems. For example, the {\class BlockDavidsonSolMgr} takes only two arguments in its constructor: an {\class Eigenproblem} specifying the eigenvalue problem to be solved and a {\class Parameters} of options specific to this solver manager. The solver manager instantiates an eigensolver, along with the status tests and other support classes needed by the eigensolver. To solve the eigenvalue problem, the user simply calls the \textit{solve()} method of the solver manager. The solver manager performs repeated calls to the eigensolver \textit{iterate()}
332method, performs restarts and locking, and places the final solution into the eigenproblem.
333
334\subsection{{\libtitle eigenSparse} classes}
335
336The following lists and briefly describes the current classes of {\lib eigenSparse}.
337
338\subsubsection{The {\classtitle EigenProblem} class}
339
340{\class EigenProblem} is a container for the components of an eigenvalue problem, as well as the solutions. By requiring eigen problems to derive from {\class EigenProblem}, {\lib eigenSparse} defines a minimum interface that can be expected of all eigenvalue problems by the classes
341that will work with the problems (e.g., eigensolvers and status testers).\\
342\begin{lstlisting}[]{}
343/*!
344 * \class xlifepp::EigenProblem
345  \brief This provides a basic implementation for defining standard or
346  generalized eigenvalue problems.
347*/
348  template<class ScalarType, class MV, class OP>
349  class EigenProblem
350  {
351  public:
352
353    //! Empty constructor - allows xlifepp::EigenProblem to be described at a later time through "Set Methods".
354    EigenProblem();
355
356    //! Standard Eigenvalue Problem Constructor.
357    EigenProblem(const SmartPtr<const OP>& Op, const SmartPtr<MV>& initVec);
358
359    //! Generalized Eigenvalue Problem Constructor.
360    EigenProblem(const SmartPtr<const OP>& Op, const SmartPtr<const OP>& B, const SmartPtr<MV>& initVec);
361
362    //! Copy Constructor.
363    EigenProblem(const Eigenproblem<ScalarType, MV, OP>& Problem);
364
365    //! Destructor.
366    ~EigenProblem() {};
367    ...
368\end{lstlisting}
369Both the eigen problem and the eigensolver in {\lib eigenSparse} are templated on the scalar type,
370the multivector type and the operator type. Before declaring an eigen problem, users must
371choose classes to represent these entities. Having done so, they can begin to specify the
372parameters of the eigen problem. The {\class EigenProblem} class defines set methods for
373the parameters of the eigen problem. These methods are:
374\begin{itemize}
375\item {\cmd setOperator} - set the operator for which the eigenvalues will be computed
376\item {\cmd setA} - set the A operator for the eigenvalue problem $Ax = Mx\lambda$
377\item {\cmd setM} - set the M operator for the eigenvalue problem $Ax = Mx\lambda$
378\item {\cmd setPrec} - set the preconditioner for the eigenvalue problem
379\item {\cmd setInitVec} - set the initial iterate
380\item {\cmd setAuxVecs} - set the auxiliary vectors, a subspace used to constrain the search space for the solution
381\item {\cmd setNEV} - set the number of eigenvalues to be computed
382\item {\cmd setHermitian} - specify whether the problem is Hermitian
383\end{itemize}
384In addition to these {\itshape set} methods, {\class EigenProblem} defines a method {\cmd setProblem()} that gives the class the opportunity to perform any initialization that may be necessary before the problem is handed off to an eigensolver, in addition to verifying that the problem has been adequately defined. \\
385For each of the {\itshape set} methods listed above, there is a corresponding {\itshape get} function. These are the functions used by eigensolvers and solver managers to get the necessary information from the eigenvalue problem. In addition, there are two methods for storing and retrieving the results of the eigenvalue computation:
386\begin{lstlisting}[]{}
387const EigenSolverSolution & getSolution();
388void setSolution(const EigenSolverSolution & sol);
389\end{lstlisting}
390In order to verify if all the parameters are set, {\class EigenProblem} provide a method {\cmd isProblemSet()}
391
392\subsubsection{The {\classtitle EigenSolverSolution} structure}
393
394The {\class EigenSolution} structure was developed in order to facilitate setting and retrieving of solution data.
395\begin{lstlisting}[]{}
396  //!  Struct for storing an eigenproblem solution.
397  template <class ScalarType, class MV>
398  struct EigenSolverSolution {
399    //! The computed eigenvectors
400    SmartPtr<MV> evecs;
401    //! An orthonormal basis for the computed eigenspace
402    SmartPtr<MV> espace;
403    //! The computed eigenvalues
404    std::vector<ValueEigenSolver<ScalarType> >  evals;
405    /*! \brief An index into evecs to allow compressed storage of eigenvectors for real, non-Hermitian problems.
406     *
407     *  index has length numVecs, where each entry is 0, +1, or -1. These have the following interpretation:
408     *     - index[i] == 0: signifies that the corresponding eigenvector is stored as the i column of evecs. This will usually be the
409     *       case when ScalarType is complex, an eigenproblem is Hermitian, or a real, non-Hermitian eigen problem has a real eigenvector.
410     *     - index[i] == +1: signifies that the corresponding eigenvector is stored in two vectors: the real part in the i column of evecs and the <i><b>positive</b></i> imaginary part in the i+1 column of evecs.
411     *     - index[i] == -1: signifies that the corresponding eigenvector is stored in two vectors: the real part in the i-1 column of evecs and the <i><b>negative</b></i> imaginary part in the i column of evecs
412     */
413    std::vector<int>         index;
414    //! The number of computed eigenpairs
415    int numVecs;
416
417    EigenSolverSolution() : evecs(),espace(),evals(0),index(0),numVecs(0) {}
418  };
419\end{lstlisting}
420
421All {\lib eigenSparse} solver managers place the results of the computation in the {\class EigenProblem} class using an {\class EigenSolverSolution} structure. The number of eigen solutions computed is given by field numVecs. The eigenvalues are always stored as two real values, even
422when templated on a complex data type or when the eigenvalues are real. Similarly, the eigen space can always be represented by a multivector of width numVecs, even for non-symmetric eigen problems over the real field. The storage scheme for eigenvectors requires
423more finesse.\\
424When solving real symmetric eigen problems, the eigenvectors can always be chosen to
425be real, and therefore can be stored in a single column of a real multivector. When solving
426eigenproblems over a complex field, whether Hermitian or non-Hermitian, the eigenvectors may be complex, but the multivector is defined over the complex field, so that this poses no problem. However, real non-symmetric problems can have complex eigenvectors, which prohibits a one-for-one storage scheme using a real multivector. Fortunately, the eigenvectors
427in this scenario occur as complex conjugate pairs, so the pair can be stored in two real
428vectors. This permits a compressed storage scheme, which uses an index vector stored in the
429{\class EigenSolverSolution}, allowing conjugate pair eigenvectors to be easily retrieved from evecs.
430The integers in {\cmd EigenSolverSolution::index} take one of three values: $\{0, +1, -1\}$. These values allow the eigenvectors to be retrieved as follows:
431\begin{itemize}
432\item $index[i]=0$: the i-th eigenvector is stored uncompressed in column $i$ of evecs
433\item $index[i] = +1$: the i-th eigenvector is stored compressed, with the real component in column $i$ of evecs and the positive complex component stored in column $i$ + 1 of evecs
434\item $index[i] = -1$: the i-th eigenvector is stored compressed, with the real component in
435column $i$ -1 of evecs and the negative complex component stored in column $i$ of evecs
436\end{itemize}
437Because this storage scheme is only required for non-symmetric problems over the real field,
438all other eigenproblems will result in an index vector composed entirely of zeroes. For the
439real non-symmetric case, the +1 index will always immediately precede the corresponding -1
440index. \\
441
442\subsubsection{The {\classtitle EigenSolver} class}
443
444The {\class EigenSolver} class defines the basic interface that must be met by any eigen solver class in {\lib eigenSparse}. The specific eigensolvers are implemented as derived classes of {\class EigenSolver}. There are two eigen solvers currently implemented in {\lib eigenSparse}
445\begin{itemize}
446\item {\class BlockDavidson} : A block Davidson solver for Hermitian eigenvalue problems
447\item {\class BlockKrylovSchur} : A block Krylov Schur solver for Hermitian or non-Hermitian
448eigenvalue problems.
449\end{itemize}
450The class {\class EigenSolver}, like {\class EigenProblem}, is templated on the scalar type, multivector type and operator type. The options for the eigen solver are passed through the constructor, defined by {\class EigenSolver} to have the following form:
451\begin{lstlisting}[]{}
452template<class ScalarType, class MV, class OP>
453class EigenSolver {
454  public:
455
456  //! @name Constructors/Destructor
457  //@{
458
459  //! Default Constructor.
460  EigenSolver() {};
461
462  //! Basic Constructor.
463  /*! This constructor, implemented by all xlifepp eigensolvers, takes an xlifepp::Eigenproblem,
464    xlifepp::SortManager, xlifepp::OutputManager, and Parameters as input.  These
465    four arguments are sufficient enough for constructing any xlifepp::EigenSolver object.
466  */
467  EigenSolver( const SmartPtr<EigenProblem<ScalarType,MV,OP> >& problem,
468               const SmartPtr<SortManager<ScalarType> >&        sorter,
469               const SmartPtr<OutputManager<ScalarType> >&      printer,
470               const SmartPtr<StatusTest<ScalarType,MV,OP> >&   tester,
471               const SmartPtr<OrthoManager<ScalarType,MV> >&    ortho,
472               Parameters& params );
473...
474\end{lstlisting}
475These classes are used as follows:
476\begin{itemize}
477\item \textbf{problem} - the eigenproblem to be solved; the solver will get the problem operators from here.
478\item \textbf{sorter} - the sort manager selects the significant eigenvalues
479\item \textbf{printer} - the output manager dictates verbosity level in addition to processing output streams
480\item \textbf{tester} - the status tester dictates when the solver should quit iterate() and return to the caller
481\item \textbf{ortho} - the orthogonalization manager defines the inner product and other concepts
482related to orthogonality, in addition to performing these computations for the solver
483\item \textbf{params} - the parameter list specifies eigensolver-specific options; see the documentation
484for a list of options support by individual solvers
485\end{itemize}
486In addition to specifying an iteration, an eigensolver also specifies a concept of state, i.e.  the current data associated with the iteration. After declaring an Eigensolver object, the state of the solver is in an uninitialized state. For most solver, to be initialized mean to be in
487a valid state, containing all of the information necessary for performing eigen solver iterations. \\
488Eigensolver provides two methods concerning initialization: \textit{isInitialized()} indicates whether the solver is initialized or not, and \textit{initialize()} (with no arguments) instructs the solver to initialize itself using random data or the initial vectors stored in the eigenvalue problem. \\
489To ensure that solvers can be used as efficiently as possible, the user needs access to the
490state of the solver. To this end, each eigensolver provides low-level methods for getting and
491setting the state of the solver:
492\begin{itemize}
493\item {\cmd getState()} - returns a solver-specific structure with read-only pointers to the current
494state of the solver.
495\item {\cmd initialize(...)} - accepts a solver-specific structure enabling the user to initialize the
496solver with a particular state.
497\end{itemize}
498The combination of these two methods, along with the flexibility provided by status tests,
499allows the user almost total control over eigen solver iterations. \\
500Moreover, each eigen solver provides two significant types of methods: status methods and solver-
501specific state methods. The status methods are defined by the Eigensolver abstract
502base class and represent the information that a generic status test can request from any
503eigensolver. A list of these methods is given in following table
504\begin{center}
505\begin{tabular}{|l|p{11.5cm}|}
506  \hline
507  \textit{Method name} & \textit{Description}\\
508  \hline
509  {\cmd getNumIters} & Get the current number of iterations. \\
510  {\cmd getRitzValues} & Get the most recent Ritz values. \\
511  {\cmd getRitzVectors} & Get the most recent Ritz vectors. \\
512  {\cmd getRitzIndex} & Get the Ritz index needed for indexing compressed Ritz vectors. \\
513  {\cmd getResNorms} & Get the most recent residual norms, with respect to the OrthoManager. \\
514  {\cmd getRes2Norms} & Get the most recent residual 2-norms. \\
515  {\cmd getRitzRes2Norms} & Get the most recent Ritz residual 2-norms. \\
516  {\cmd getCurSubspaceDim} & Get the current subspace dimension. \\
517  {\cmd getMaxSubspaceDim} & Get the maximum subspace dimension. \\
518  {\cmd getBlockSize} & Get the block size. \\
519  \hline
520\end{tabular}
521\end{center}
522
523\subsubsection{The {\classtitle SolverManager} class}
524
525Using {\lib eigenSparse} by interfacing directly with eigensolvers is extremely powerful, but can be
526tedious. Solver managers provide a way for users to encapsulate specific solving strategies
527inside of an easy-to-use class. Novice users may prefer to use existing solver managers, while
528advanced user may prefer to write custom solver managers.
529{\class SolverManager} defines only two methods: a constructor accepting an {\class EigenProblem} and a parameter list of solver manager-specific options; and a {\cmd solve()} method, taking no arguments and returning either {\tt \_converged} or {\tt \_unconverged}. Consider the following example code:
530\begin{lstlisting}[]{}
531// Create an eigen problem
532SmartPtr<EigenProblem<ST,MV,OP> > problem =
533// Create parameter list to pass into the solver manager
534  Parameters pl;
535  pl.set("Verbosity", (int)verbosity);
536  pl.set("Which", which);
537  pl.set("Orthogonalization", ortho);
538  pl.set("Block Size", blockSize);
539  pl.set("Num Blocks", numBlocks);
540  pl.set("Maximum Restarts", maxRestarts);
541// Create a solver manager
542  BlockDavidsonSolMgr<ST,MV,OP> bdSolverMan(problem, pl);
543// Solve the eigenvalue problem
544  ComputationInfo returnCode = bdSolverMan.solve();
545// Get the solution from the problem
546  EigenSolverSolution<ST,MV> sol = problem->getSolution();
547\end{lstlisting}
548As has been stated before, the goal of the solver manager is to create an eigensolver object,
549along with the support objects needed by the eigensolver. Another purpose of many solver
550managers is to manage and initiate the repeated calls to the underlying solver’s {\cmd iterate()}
551method. For solvers that build a Krylov subspace to some maximum dimension (e.g., {\class BlockKrylovSchur} and {\class BlockDavidson}), the solver manager will also assume the task of restarting the solver when the subspace is full. This is something for which multiple approaches are possible. Also, there may be substantial flexibility in creating the support classes (e.g., sort
552manager, status tests) for the solver. An aggressive solver manager could even go so far as to
553construct a preconditioner for the eigenvalue problem. \\
554These examples are meant to illustrate the flexibility that specific solver managers may
555have in implementing the {\cmd solve()} routine. Some of these options might best be incorporated
556into a single solver manager, which takes orders from the user via the parameter list given in
557the constructor. Some of these options may better be contained in multiple solver managers,
558for the sake of code simplicity. It is even possible to write solver managers that contain other
559solvers managers; motivation for something like this would be to select the optimal solver
560manager at runtime based on some expert knowledge, or to create a hybrid method which
561uses the output from one solver manager to initialize another one. \\
562
563\subsubsection{The {\classtitle StatusTest} class}
564
565the purpose of the {\class StatusTest} should be clear: to give the user or solver manager flexibility in stopping the eigen solver iterations in order to interact directly with the solver. \\
566Many reasons exist for why a user would want to stop the solver from iterating:
567\begin{itemize}
568\item  some convergence criterion has been satisfied and it is time to quit;
569\item some part of the current solution has reached a sufficient accuracy to removed from the
570iteration (“locking”);
571\item the solver has performed a sufficient or excessive number of iterations.
572\end{itemize}
573These are just some commonly seen reasons for ceasing the iteration, and each of these can be so varied in implementation/parametrization as to require some abstract mechanism controlling the iteration. \\
574Below is a list of status tests:
575\begin{itemize}
576\item {\class StatusTestCombo} - this status test allows for the boolean combination of other
577status tests, creating near unlimited potential for complex status tests.
578\item {\class StatusTestOutput} - this status test acts as a wrapper around another status
579test, allowing for printing of status information on a call to checkStatus()
580\item {\class StatusTestMaxIters} - this status test monitors the number of iterations performed by the solver; it can be used to halt the solver at some maximum number of
581iterations or even to require some minimum number of iterations.
582\item {\class StatusTestResNorm} - this status test monitors the residual norms of the current iterate.
583\item {\class StatusTestWithOrdering} - this status test also monitors the residual norms
584of the current iterate, but only considers the residuals along with a set of auxiliary eigenvalues.
585\end{itemize}
586
587\subsubsection{The {\classtitle SortManager} class}
588
589The purpose of a sort manager is to separate the eigensolver classes from the sorting functionality required by those classes. This satisfies the flexibility principle sought by {\lib eigenSparse}, by giving users the opportunity to perform the sorting in whatever manner is deemed to be most appropriate. {\lib eigenSparse} defines an abstract class {\class SortManager} with two methods,
590one for sorting real values and one for sorting complex values:
591\begin{lstlisting}[]{}
592// Sorting with real value
593virtual void sort(std::vector<MagnitudeType>& evals, SmartPtr<std::vector<int> > perm = _smPtrNull, int n = -1) const = 0;
594// Sorting with complex value
595virtual void sort(std::vector<MagnitudeType>& rEvals,
596                          std::vector<MagnitudeType>& iEvals,
597                          SmartPtr<std::vector<int> > perm = _smPtrNull,
598                           int n = -1) const = 0;
599\end{lstlisting}
600
601\subsubsection{The {\classtitle BasicSort} class}
602
603The concrete derived class implements sorting scheme for sorting real values and one for sorting complex values.
604\begin{lstlisting}[]{}
605template<class MagnitudeType>
606class BasicSort : public SortManager<MagnitudeType>
607{
608public:
609    /*! \brief Parameter list driven constructor
610
611        This constructor accepts a paramter list with the following options:
612       - \c "Sort Strategy" - a \c string specifying the desired sorting strategy. See setSortType() for valid options.
613    */
614    BasicSort(Parameters& pl);
615
616    /*! \brief String driven constructor
617
618        Directly pass the string specifying sort strategy. See setSortType() for valid options.
619    */
620    BasicSort(const String& which = "LM");
621
622    //! Destructor
623    virtual ~BasicSort();
624    ...
625\end{lstlisting}
626By default, sort strategy Largest Magnitude (LM) is used. So as to change the strategy, user can easily specify it with {\cmd setSortType} method.
627
628\subsubsection{The {\classtitle OrthoManager} class}
629
630Orthogonalization and orthonormalization are commonly performed computations in iterative eigensolvers; in fact, for some eigensolvers, they represent the dominant cost. Different scenarios may require different approaches (e.g, Euclidean inner product versus $M$ inner
631product, orthogonal projections versus oblique projections). Combined with the plethora of
632available methods for performing these computations, Anasazi has left as much leeway to the
633users as possible. \\
634Orthogonalization of multivectors is performed by derived classes of the abstract class {\class OrthoManager} that provides five methods:
635\begin{itemize}
636\item {\cmd innerProd(X,Y,Z)} - performs the inner product defined by the manager.
637\item {\cmd norm(X)} - computes the norm induced by {\cmd innerProd()}.
638\item {\cmd project(X,C,Q)} - given an orthonormal basis Q, projects X onto to the space perpindicular to colspan(Q), optionally returning the coefficients of X in Q.
639\item {\cmd normalize(X,B)} - returns an orthonormal basis for colspan(X), optionally returning
640the coefficients of X in the computed basis.
641\item {\cmd projectAndNormalize(X,C,B,Q)} - computes an orthonormal basis for subspace
642colspan(X) - colspan(Q), optionally returning the coefficients of X in Q and the new
643basis.
644\end{itemize}
645It should be noted that a call to {\cmd projectAndNormalize()} is not necessarily equivalent
646to a call to {\cmd project()} followed by {\cmd normalize()}. This follows from the fact that, for some
647orthogonalization managers, a call to {\cmd normalize()} may augment the column span of a rank-
648deficient multivector in order to create an orthonormal basis with the same number of columns
649as the input multivector. In this case, the code
650\begin{lstlisting}[]{}
651orthoman.project(X,C,Q);
652orthoman.normalize(X,B);
653\end{lstlisting}
654could result in an orthonormal basis $X$ that is not orthogonal to the basis in $Q$. \\
655\xlifepp provides two orthogonalization managers:
656\begin{itemize}
657\item {\class BasicOrthoManager} - performs orthogonalization using multiple steps of classical Gram-Schmidt.
658\item {\class SVQBOrthoManager} - performs orthogonalization using the SVQB orthogonalization technique described by Stathapoulos and Wu
659\end{itemize}
660The core function of these orthogonalization managers is {\cmd findBasis}, which find an operator-orthonormal basis for a span, with the option of extending the subspace.
661
662\subsubsection{The {\classtitle OutputManager} class}
663
664The output manager exists to provide flexibility with regard to the verbosity of the eigen solver. Each output manager has two primary concerns: what output is printed and where the output is printed to. When working with the output manager, output is classified
665into one of the following message types: \\
666\begin{lstlisting}[]{}
667enum MsgEigenType
668{
669  _errorsEigen = 0,               /*!< Errors [ always printed ] */
670  _warningsEigen = 0x1,             /*!< Internal warnings */
671  _iterationDetailsEigen = 0x2,     /*!< Approximate eigenvalues, errors */
672  _orthoDetailsEigen = 0x4,         /*!< Orthogonalization/orthonormalization details */
673  _finalSummaryEigen = 0x8,        /*!< Final computational summary */
674  _timingDetailsEigen = 0x10,       /*!< Timing details */
675  _statusTestDetailsEigen = 0x20,   /*!< Status test details */
676  _debugEigen = 0x40               /*!< Debugging information */
677};
678 \end{lstlisting}
679Output manager are subclasses of the abstract base class {\class OutputManager}. This class provides the following output-related methods:
680\begin{lstlisting}[deletekeywords={[3] type}]
681template <class ScalarType>
682class OutputManager {
683  public:
684   ...
685  //! Find out whether we need to print out information for this message type.
686  /*! This method is used by the solver to determine whether computations are
687      necessary for this message type.
688  */
689  virtual bool isVerbosity(MsgEigenType type) const = 0;
690
691  //! Send output to the output manager.
692  virtual void print(MsgEigenType type, const String output) = 0;
693
694  //! Create a stream for outputting to.
695  virtual std::ostream& stream(MsgEigenType type) = 0;
696...
697 \end{lstlisting}
698\xlifepp provides a single output manager, {\class BasicOutputManager}. This class accepts an output stream from the user. The output corresponding to the verbosity level of the manager is sent to this stream.
699
700\subsubsection{The {\classtitle SolverUtils} class}
701
702Most of the large-scale eigenvalue algorithms exploit projection processes so as to extract approximate eigenvectors from a given subspace. Inside this subspace, a small matrix eigenvalue problem is obtained. \xlifepp provides {\class SolverUtils} class, a collection of utilities necessary for the solvers.  These utilities include sorting, orthogonalization, projecting/solving local eigen systems,
703\begin{lstlisting}[]{}
704  template<class ScalarType, class MV, class OP>
705  class SolverUtils
706  {
707  public:
708    typedef typename NumTraits<ScalarType>::RealScalar MagnitudeType;
709    typedef typename NumTraits<ScalarType>::Scalar  SCT;
710    typedef MatrixEigenDense<SCT> MatrixDense;
711
712    //! @name Constructor/Destructor
713    //@{
714
715    //! Constructor.
716    SolverUtils();
717
718    //! Destructor.
719    virtual ~SolverUtils() {};
720    ...
721 \end{lstlisting}
722In fact, all utilities along with this class make use of functions of {\lib eigenCore}. The most important function of this class is {\cmd directSolver}, a routine for computing eigen pairs of Hermitian pencil. This method is heavily used during the projection of vectors into a subspace and normalization of these vector with a specified norm. \\
723
724\subsection{Examples}
725
726The following gives sample code for solving a symmetric eigenvalue problem using the Block Davidson solver manager.\\
727The first step in solving an eigenvalue problem is to define the eigenvalue problem. However, before that, we need to make sure define the operator and the multivector.
728\begin{lstlisting}[]{}
729// Load LargeMatrix from file
730LargeMatrix<Real> rMatSym(dataPathTo(rMatrixSym), _dense, 782, _cs, _symmetric);
731// Define the operator (in this case, it's LargeMatrix)
732SmartPtr< const LargeMatrixAdapter<ST> > K(new LargeMatrixAdapter<ST>(&rMatSym));
733// Sometimes, we need a preconditioner to be able to solve the eigenvalue problem
734SmartPtr< const LargeMatrixAdapter<ST> > Prec(new LargeMatrixAdapter<ST>(&matPrec));
735// Define multivector
736SmartPtr<MV> ivec = SmartPtr<MultiVecAdapter<ST> >(new MultiVecAdapter<ST>(rMatSym.nbCols, blockSize));
737// Initialize the multivector  with random values
738ivec->mvRandom();
739 \end{lstlisting}
740Everything is fine then eigenvalue problem is defined as follow:
741\begin{lstlisting}
742SmartPtr<EigenProblem<ST,MV,OP> > problem = SmartPtr<EigenProblem<ST,MV,OP> >(new EigenProblem<ST,MV,OP>(K,ivec));
743// Inform the eigenProblem that the operator cK is symmetric
744problem->setHermitian(true);
745// Set the number of eigenvalues requested
746problem->setNEV(nev);
747\end{lstlisting}
748The second step is to specify parameters for the solver manager, depending on algorithm (Block Davidson or Block Krylov Schur), we can have a different parameters. Nevertheless, there are some essential ones to be specified
749\begin{lstlisting}[]{}
750const int nev = 4; //6
751int blockSize = 5;//6;
752int numBlocks = 5;//14;
753int maxRestarts = 25;//8;
754Real tol = 1.0e-6;
755String which("SM"), ortho("SVQB");
756bool locking = true;
757bool insitu = false;
758MsgEigenType verbosity = _errorsEigen;
759
760Parameters pl;
761pl.set("Verbosity", (int)verbosity);
762pl.set("Which", which);
763pl.set("Orthogonalization", ortho);
764pl.set("Block Size", blockSize);
765pl.set("Num Blocks", numBlocks);
766pl.set("Maximum Restarts", maxRestarts);
767pl.set("Convergence Tolerance", tol);
768pl.set("Use Locking", locking);
769pl.set("Locking Tolerance", tol/10);
770pl.set("In Situ Restarting", insitu);
771\end{lstlisting}
772Till now, we have all the information needed to declare the solver manager and solve the problem:
773\begin{lstlisting}
774BlockDavidsonSolMgr<ST,MV,OP> bdSolverMan(problem, pl);
775ComputationInfo returnCode = bdSolverMan.solve();
776 \end{lstlisting}
777The return value of the solver indicate whether the algorithm succeeds or not.(i.e: whether the requested number of eigenpairs were found to a sufficient accuracy (as defined by the solver manager). Output from {\cmd solve()} routine in this example might look as follows:
778\begin{lstlisting}[language=]
779BlockDavidson Eigen Solver Test : Standard symmetric matrix test with Smallest Magnitude
780--------------------------------------------------------------------------------
781  Direct residual norms computed
782          Eigenvalue         Residual(M)
783----------------------------------------
784        -1.29891e-06         8.63079e-08
785        -1.29973e-06         6.27415e-08
786        -1.30048e-06         7.99246e-08
787        -1.33992e-06         7.09023e-07
788
789 \end{lstlisting}
790