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{The \umfpack wrappers}
18
19Written in ANSI/ISO C, UMFPACK library consists of 32 user-callable routines. Nearly all these routines come in four versions, with different sizes of integers and for real or complex floating-point numbers:
20 \begin{itemize}
21\item real double precision, \textbf{int} integers
22\item complex double precision, \textbf{int} integers
23\item real double precision, \textbf{SuiteSparse\_long} integers
24\item complex double precision, \textbf{SuiteSparse\_long} integers
25\end{itemize}
26Only the interfaces to the first two versions are implemented in \xlifepp. However, more versions can be easily added in the need of user. \\
27Because callable UMFPACK routines are written in C, so as to call them  correctly from the C++ environment of \xlifepp,  we must make these routines "extern C". And it's the role of UmfPackWrappers. Only some primary routines of UMFPACK are made interface; however, it's not difficult to include others.
28\vspace{.1cm}
29\begin{lstlisting}
30#ifdef __cplusplus
31extern "C" {
32#endif
33
34int DISYMBOLIC(int n_row, int n_col, const int Ap[], const int Ai[], const double Ax[], void** Symbolic, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
35int DINUMERIC(const int Ap[], const int Ai[], const double Ax[], void* Symbolic, void** Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
36int DISOLVE(int sys, const int Ap[], const int Ai[], const double Ax[], double X[], const double B[], void* Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
37void DIFREESYMBOLIC(void** Symbolic);
38void DIFREENUMERIC(void** Numeric);
39    ...
40
41int ZISYMBOLIC(int n_row, int n_col, const int Ap[], const int Ai[], const double Ax[], const double Az[], void** Symbolic, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
42int ZINUMERIC(const int Ap[], const int Ai[], const double Ax[], const double Az[], void* Symbolic, void** Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
43int ZISOLVE(int sys, const int Ap[], const int Ai[], const double Ax[], const double Az[], double Xx[], double Xz[], const double Bx[], const double Bz[], void* Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
44void ZIFREESYMBOLIC(void** Symbolic);
45void ZIFREENUMERIC(void** Numeric);
46    ...
47\end{lstlisting}
48\vspace{.1cm}
49Routine names beginning with DI correspond to the version of real double precision, \textbf{int} integers, the ones with ZI imply the versions of
50complex double precision, \textbf{int} integers.
51
52\section{The {\classtitle UMFPACK} class}
53
54The {\class UMFPACK} class plays a role of high-level wrapper of routines provided by UmfPackWrappers:
55\begin{itemize}
56\item *SYMBOLIC: performing a symbolic decomposition on the sparcity of a matrix
57\item *NUMERIC: performing a numeric decomposition of a matrix
58\item *FREESYMBOLIC: freeing symbolic object created by *SYMBOLIC
59\item *FREENUMERIC: freeing numeric object created by *NUMERIC
60\item *GETLUNZ: returning the number of non-zeros in lower matrix L and upper matrix U in LU decomposition
61\item *GETNUMERIC: retrieving lower matrix L, upper matrix U, permutation P,Q, and scale factor R
62\item *GETDET: returning determinant of original matrix
63\end{itemize}
64The "*" corresponds to DI-real double precision, or ZI-complex double precision.\\
65All these routines are organized under templated "class-like" interface.
66\vspace{.1cm}
67\begin{lstlisting}
68template<typename OrdinalType, typename ScalarType>
69class UMFPACK
70{
71  public:
72    typedef typename NumTraits<ScalarType>::magnitudeType MagnitudeType;
73
74
75    //! Default Constructor.
76    inline UMFPACK(void) {}
77
78    //! Destructor.
79    inline ~UMFPACK(void) {}
80
81    OrdinalType symbolic(OrdinalType nRow, OrdinalType nCol, const OrdinalType Ap[], const OrdinalType Ai[], const ScalarType Ax[], void** Symbolic, const MagnitudeType Control[UMFPACK\_CONTROL], MagnitudeType Info[UMFPACK\_INFO]);
82
83    OrdinalType numeric(const OrdinalType Ap[], const OrdinalType Ai[], const ScalarType Ax[], void* Symbolic, void** Numeric, const MagnitudeType Control[UMFPACK\_CONTROL], MagnitudeType Info[UMFPACK\_INFO]);
84
85    void freeSymbolic(void** Symbolic);
86    void freeNumeric(void** Numeric);
87
88    OrdinalType solve(OrdinalType sys, const OrdinalType Ap[], const OrdinalType Ai[], const ScalarType Ax[], ScalarType X[], const ScalarType B[], void* Numeric, const MagnitudeType Control[UMFPACK\_CONTROL], MagnitudeType Info[UMFPACK\_INFO]);
89
90    OrdinalType getLunz(OrdinalType* lnz, OrdinalType* unz, OrdinalType* nRow, OrdinalType* nCol, OrdinalType* nzUdiag, void* Numeric);
91
92    OrdinalType getNumeric(OrdinalType Lp[], OrdinalType Lj[], ScalarType Lx[], OrdinalType Up[], OrdinalType Ui[], ScalarType Ux[], OrdinalType P[], OrdinalType Q[], ScalarType Dx[], OrdinalType* doRecip, ScalarType Rs[], void* Numeric);
93
94    OrdinalType getDeterminant(ScalarType* Mx, ScalarType* Ex, void* NumericHandle, ScalarType UserInfo[UMFPACK\_INFO]);
95    ...
96\end{lstlisting}
97\vspace{.1cm}
98 One advantage of this orgnization is to allow the specialization of code corresponding to the two versions: real double precision and complex double precision with \textbf{int} integer.
99\vspace{.1cm}
100\begin{lstlisting}
101template<>
102class UMFPACK<int, double>
103{
104  public:
105    inline UMFPACK(void) {}
106    inline ~UMFPACK(void) {}
107
108    int symbolic(int nRow, int nCol, const int Ap[], const int Ai[], const double Ax[], void** Symbolic, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
109
110    int numeric(const int Ap[], const int Ai[], const double Ax[], void* Symbolic, void** Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
111
112    void freeSymbolic(void** Symbolic);
113
114    void freeNumeric(void** Numeric);
115
116    int solve(int sys, const int Ap[], const int Ai[], const double Ax[], double X[], const double B[], void* Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
117    ...
118\end{lstlisting}
119\vspace{.1cm}
120The specialization of complex version comes with an extra method for the linear system $Ax=b$ with $A$ complex double precision and $b$ real double precision.
121\vspace{.1cm}
122\begin{lstlisting}
123template<>
124class UMFPACK<int, std::complex<double> >
125{
126  public:
127    inline UMFPACK(void) {}
128    inline ~UMFPACK(void) {}
129
130    int symbolic(int nRow, int nCol, const int Ap[], const int Ai[], const std::complex<double> Ax[], void** Symbolic, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
131
132    int numeric(const int Ap[], const int Ai[], const std::complex<double> Ax[], void* Symbolic, void** Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
133
134    void freeSymbolic(void** Symbolic);
135
136    void freeNumeric(void** Numeric);
137
138    int solve(int sys, const int Ap[], const int Ai[], const std::complex<double> Ax[], std::complex<double> X[], const std::complex<double> B[], void* Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
139
140    // Solve in case AX=B with A complex, B real
141    int solveCR(int sys, const int Ap[], const int Ai[], const std::complex<double> Ax[], std::complex<double> X[], const double Bx[], const double Bz[], void* Numeric, const double Control[UMFPACK_CONTROL], double Info[UMFPACK_INFO]);
142    ...
143\end{lstlisting}
144\vspace{.1cm}
145\displayInfos{library=umfpackSupport, header=UmfPack.hpp, implementation=UmfPack.cpp, test={test\_UmfPackSolver.cpp}, header dep={utils.h}}
146
147\section{The {\classtitle UmfPackSolver} class}
148
149The class, as its name, serves for solving the sparse linear system $Ax=b$. Being similar to other solvers of \xlifepp, this solver can be provoked with a call of operator(). For example:
150\vspace{.1cm}
151\begin{lstlisting}
152UmfPackSolver umfpackSolver;
153// With A largeMatrix; b and x are std::vector
154umfpackSolver(A,b,x);
155\end{lstlisting}
156\vspace{.1cm}
157By calling the solver in this way, the storage and values of the {\class largeMatrix} $A$ are extracted and these extractions are converted to the format of UMFPACK including: column pointer, row index and values. These three are inputs to underlying UMFPACK routines. Because extraction and conversion between largeMatrix of \xlifepp and UMFPACK format are expensive, in some cases, it's better to directly make use of the conversion and it can be easily done by calling operator() with more inputs
158 \vspace{.1cm}
159\begin{lstlisting}[deletekeywords={[3] size, colPointer}]
160// Solve AX=B with UmfPack
161// \param[in] colPointer column pointer of CSC-like Umfpack format (after calling function extract2UmfPack of largeMatrix)
162// \param[in] rowIdx row index of CSC-like Umfpack format (after calling function extract2UmfPack of largeMatrix)
163// \param[in] values values of CSC-like Umfpack format (after calling function extract2UmfPack of largeMatrix)
164// \param[in] vecB vector B (vector B should be std::vector)
165// \param[in] vecX vector X (vector X should be std::vector)
166template<class ScalarTypeMat, class VecB, class VecX>
167void operator()(int size, const std::vector<int>& colPointer, const std::vector<int>& rowIdx, const std::vector<ScalarTypeMat>& values, const VecB& vecB, VecX& vecX, UmfPackComputationMode sys=_A)
168\end{lstlisting}
169\vspace{.1cm}
170One of a technical issue on working with different types of scalar value, in this case, there are Real and Complex values, is the static dispatching. For dispatching at runtime, we can use simple {\itshape if-else} statements or the switch statement. However, the {\itshape if-else} statement requires both branches to compile successfully, even when the condition tested by if is known at compile time. To overcome this problem, we use a simple technique that is intially described in Alexandrescu, mapping integral Constants to Types:
171 \vspace{.1cm}
172\begin{lstlisting}[deletekeywords={[3] value}]
173template <int v>
174struct Int2Type
175{
176enum { value = v };
177};
178\end{lstlisting}
179\vspace{.1cm}
180{\class Int2Type} generates a distinct type for each distinct constant integral value passed. This is because
181different template instantiations are distinct types; thus, {\class Int2Type<0>} is different from {\class Int2Type<1>},
182and so on. In addition, the value that generates the type is "saved" in the enum member value. \\
183For a linear system $Ax=b$, we can divide into four cases depending to the scalar type of $A$ and $b$, and corresponding to each case, there is a {\class Int2Type}:
184\begin{itemize}
185\item $A$ real and $b$ real; {\class Int2Type<0>}
186\item $A$ real and $b$ complex; {\class Int2Type<1>}
187\item $A$ complex and $b$ real; {\class Int2Type<2>}
188\item $A$ complex and $b$ complex; {\class Int2Type<3>}
189\end{itemize}
190Because each case above needs processing in a different way, we define an internal class of {\class UmfPackSolver} to take care of it.
191 \vspace{.1cm}
192\begin{lstlisting}[deletekeywords={[3] type}]
193// Auxiliary internal class to process 4 cases of equation AX = B:
194//      + A:real, B: real
195//      + A:real, B:complex
196//      + A:complex, B: real
197//      + A:complex, B:complex
198template<typename ScalarTypeMat, typename VecB, typename VecX>
199class SolverIntern
200{
201  public:
202    typedef typename NumTraits<ScalarTypeMat>::magnitudeType MagType;
203    typedef typename Conditional<NumTraits<ScalarTypeMat>::IsComplex, ScalarTypeMat, typename VectorTrait<VecB>::Type>::type ScalarType;
204
205    SolverIntern() {}
206   ...
207
208\end{lstlisting}
209\vspace{.1cm}
210For example, the case of $A$ real and $b$ real.
211 \vspace{.1cm}
212\begin{lstlisting}[deletekeywords={[3] colPointer, symbolic, numeric}]
213// Solve AX=B in case A Real, B Real
214void solve(int sys, int nRows, int nCols, const std::vector<int>& colPointer, const std::vector<int>& rowIdx, const std::vector<ScalarTypeMat>& values, const VecB& vecB, VecX& vecX, Int2Type<0>)
215{
216  void* symbolic;
217  void* numeric;
218  double* null = (double*) NULL;
219
220  UMFPACK<int, ScalarType> umfPack;
221
222  (void)umfPack.symbolic(nRows, nCols, &(colPointer[0]), &(rowIdx[0]),&(values[0]),&symbolic,null,null);
223  (void)umfPack.numeric(&(colPointer[0]), &(rowIdx[0]), &(values[0]), symbolic, &numeric, null,null);
224  (void)umfPack.solve((int)sys, &(colPointer[0]), &(rowIdx[0]),&(values[0]), &vecX[0], &vecB[0], numeric, null, null);
225  umfPack.freeSymbolic(&symbolic);
226  umfPack.freeNumeric(&numeric);
227}
228\end{lstlisting}
229\vspace{.1cm}
230\displayInfos{library=umfpackSupport, header=UmfPackSolver.hpp, test={test\_UmfPackSolver.cpp}, header dep={utils.h, UmfPack.hpp}}
231
232\section{The {\classtitle UmfPackLU} class}
233
234UMFPACK library allows not only to solve the linear system $Ax=b$ but also to retrieve more information. There are cases where users may wish to do more with the LU factorization of a matrix rather than solve a linear system. And the {\class UmfPackLU} class is for this purpose. Not like to the "non-templated" {\class UmfPackSolver} class, the {\class UmfPackLU} depends on the type of input matrix.
235 \vspace{.1cm}
236\begin{lstlisting}
237template<typename MatrixType>
238class UmfPackLU
239{
240  public:
241    typedef typename MatrixType::ScalarType ScalarType;
242
243    struct LUMatrixType {
244      std::vector<int> outerIdx;
245      std::vector<int> innerIdx;
246      std::vector<ScalarType> values;
247    };
248
249  public:
250    UmfPackLU() { init();}
251    UmfPackLU(const MatrixType& matrix)
252    {
253      init();
254      compute(matrix);
255    }
256...
257\end{lstlisting}
258\vspace{.1cm}
259Apart from solving the linear system $Ax=b$, this class provides some methods to work with LU factorization.
260 \vspace{.1cm}
261\begin{lstlisting}
262inline const LUMatrixType& matrixL() const
263{
264  if (extractedDataAreDirty_) extractData();
265  return (lMatrix_);
266}
267
268inline const LUMatrixType& matrixU() const
269{
270  if (extractedDataAreDirty_) extractData();
271  return (uMatrix_);
272}
273
274inline const std::vector<int>& permutationP() const
275{
276  if (extractedDataAreDirty_) extractData();
277  return (pPerm_);
278}
279
280inline const std::vector<int>& permutationQ() const
281{
282  if (extractedDataAreDirty_) extractData();
283  return (qPerm_);
284}
285
286inline const std::vector<Real>& scaleFactorR() const
287{
288  if (extractedDataAreDirty_) extractData();
289  return (rScaleFact_);
290}
291\end{lstlisting}
292\vspace{.1cm}
293All these methods above return the result of LU factorization. $PAQ=LU$.  The matrix {\bfseries U} is returned in compressed column form (with sorted columns). The matrix {\bfseries L} is returned in compressed row form (with sorted rows). One remarkable thing is that the compressed column (row) form is similar to CSC (CSR) of \xlifepp. These two matrix are returned in form of struct {\class LUMatrixType}
294 \vspace{.1cm}
295\begin{lstlisting}
296struct LUMatrixType
297{
298  std::vector<int> outerIdx;
299  std::vector<int> innerIdx;
300  std::vector<ScalarType> values;
301};
302\end{lstlisting}
303\vspace{.1cm}
304The {\itshape outerIdx} corresponds to the {\itshape colPointer} of CSC and {\itshape rowPointer} of CSR, while the {\itshape innerIdx} corresponds to the {\itshape rowIndex} of CSC and {\itshape colIndex} of CSR. Remember that, not like CSC or CSR, the {\itshape values} of {\class LUMatrixType} struct doesn't contain the "first-position" zero (0). \\
305The permutations P and Q are represented as permutation vectors, where $P[k] = i$ means that row i of the original matrix is the the k-th row of $PAQ$, and where $Q[k]$ = j means that column j of the original matrix is the k-th column of PAQ. \\
306The vector R is the scale factor in the LU factorization. The first value of {\itshape scaleFactorR} defines how the scale factors Rs are to be interpreted. If it's one(1), then the scale factors {\itshape scaleFactorR[i+1]} are to be used by multiplying row i of matrix A by {\itshape scaleFactorR[i]}. Otherwise, the entries in row i are to be divided by {\itshape scaleFactorR[i+1]}. \\
307Before retrieving all these above values, a matrix at least needs factorizing. It can be done in two ways, by constructor
308 \vspace{.1cm}
309\begin{lstlisting}
310UmfPackLU(const MatrixType& matrix)
311{
312  init();
313  compute(matrix);
314}
315\end{lstlisting}
316\vspace{.1cm}
317or by the method {\bfseries compute}
318 \vspace{.1cm}
319\begin{lstlisting}
320/*! Computes the sparse Cholesky decomposition of \a largeMatrix
321 */
322void compute(const MatrixType& matrix)
323{
324  analyzePattern(matrix);
325  factorize(matrix);
326}
327\end{lstlisting}
328\vspace{.1cm}
329In this function, a symbolic decomposition on the sparsity of a matrix is performed by {\bfseries analyzePattern} then a numeric decomposition performed by {\bfseries factorize}. \\
330After the matrix is factorized, its factorization can be used to solve the linear system $Ax=b$ with :
331 \vspace{.1cm}
332\begin{lstlisting}[deletekeywords={[3] x, type}]
333template<typename VectorScalarType>
334void solve(const std::vector<VectorScalarType>& b, std::vector<typename Conditional<NumTraits<ScalarType>::IsComplex, ScalarType, VectorScalarType>::type >& x) const;
335\end{lstlisting}
336\vspace{.1cm}
337One advantage of {\class UmfPackLU} over {\class UmfPackSolver} is that for a same matrix A, it only needs to do factorization once then all these factorizations can be used over and over again to solve the linear system $Ax=b$. Of course, it doesn't have the flexibility of "non-templated" {\class UmfPackSolver}, it needs template arguments in order to define type of matrix. Depending on a specific demand, one can be a better choice than another!!!
338\displayInfos{library=umfpackSupport, header=UmfPackLU.hpp, test={test\_UmfPackSolver.cpp}, header dep={utils.h, UmfPack.hpp}}
339