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