2012-05-25 18:17:57 +02:00
// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2012 Désiré Nuentsa-Wakam <desire.nuentsa_wakam@inria.fr>
2014-09-01 15:00:19 +02:00
// Copyright (C) 2012-2014 Gael Guennebaud <gael.guennebaud@inria.fr>
2012-05-25 18:17:57 +02:00
//
2012-08-01 11:38:32 +02:00
// This Source Code Form is subject to the terms of the Mozilla
// Public License v. 2.0. If a copy of the MPL was not distributed
// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
2012-05-25 18:17:57 +02:00
2012-08-03 13:05:27 +02:00
# ifndef EIGEN_SPARSE_LU_H
# define EIGEN_SPARSE_LU_H
2012-05-25 18:17:57 +02:00
2012-06-01 18:44:51 +02:00
namespace Eigen {
2014-12-04 22:48:53 +01:00
template < typename _MatrixType , typename _OrderingType = COLAMDOrdering < typename _MatrixType : : StorageIndex > > class SparseLU ;
2013-05-14 17:15:23 +02:00
template < typename MappedSparseMatrixType > struct SparseLUMatrixLReturnType ;
template < typename MatrixLType , typename MatrixUType > struct SparseLUMatrixUReturnType ;
2013-07-17 09:30:25 +02:00
2013-01-12 11:55:16 +01:00
/** \ingroup SparseLU_Module
* \ class SparseLU
*
* \ brief Sparse supernodal LU factorization for general matrices
*
* This class implements the supernodal LU factorization for general matrices .
* It uses the main techniques from the sequential SuperLU package
* ( http : //crd-legacy.lbl.gov/~xiaoye/SuperLU/). It handles transparently real
* and complex arithmetics with single and double precision , depending on the
* scalar type of your input matrix .
* The code has been optimized to provide BLAS - 3 operations during supernode - panel updates .
* It benefits directly from the built - in high - performant Eigen BLAS routines .
* Moreover , when the size of a supernode is very small , the BLAS calls are avoided to
* enable a better optimization from the compiler . For best performance ,
* you should compile it with NDEBUG flag to avoid the numerous bounds checking on vectors .
*
* An important parameter of this class is the ordering method . It is used to reorder the columns
* ( and eventually the rows ) of the matrix to reduce the number of new elements that are created during
* numerical factorization . The cheapest method available is COLAMD .
* See \ link OrderingMethods_Module the OrderingMethods module \ endlink for the list of
* built - in and external ordering methods .
*
* Simple example with key steps
* \ code
* VectorXd x ( n ) , b ( n ) ;
* SparseMatrix < double , ColMajor > A ;
2013-01-29 16:21:24 +01:00
* SparseLU < SparseMatrix < scalar , ColMajor > , COLAMDOrdering < Index > > solver ;
2013-01-12 11:55:16 +01:00
* // fill A and b;
* // Compute the ordering permutation vector from the structural pattern of A
* solver . analyzePattern ( A ) ;
* // Compute the numerical factorization
* solver . factorize ( A ) ;
* //Use the factors to solve the linear system
* x = solver . solve ( b ) ;
* \ endcode
*
* \ warning The input matrix A should be in a \ b compressed and \ b column - major form .
* Otherwise an expensive copy will be made . You can call the inexpensive makeCompressed ( ) to get a compressed matrix .
*
* \ note Unlike the initial SuperLU implementation , there is no step to equilibrate the matrix .
* For badly scaled matrices , this step can be useful to reduce the pivoting during factorization .
* If this is the case for your matrices , you can try the basic scaling method at
* " unsupported/Eigen/src/IterativeSolvers/Scaling.h "
*
* \ tparam _MatrixType The type of the sparse matrix . It must be a column - major SparseMatrix < >
2013-07-17 09:30:25 +02:00
* \ tparam _OrderingType The ordering method to use , either AMD , COLAMD or METIS . Default is COLMAD
2015-10-08 10:50:39 +02:00
*
* \ implsparsesolverconcept
2013-01-12 11:55:16 +01:00
*
* \ sa \ ref TutorialSparseDirectSolvers
* \ sa \ ref OrderingMethods_Module
*/
2012-06-13 18:26:05 +02:00
template < typename _MatrixType , typename _OrderingType >
2014-12-04 22:48:53 +01:00
class SparseLU : public SparseSolverBase < SparseLU < _MatrixType , _OrderingType > > , public internal : : SparseLUImpl < typename _MatrixType : : Scalar , typename _MatrixType : : StorageIndex >
2012-05-25 18:17:57 +02:00
{
2014-09-01 15:00:19 +02:00
protected :
typedef SparseSolverBase < SparseLU < _MatrixType , _OrderingType > > APIBase ;
using APIBase : : m_isInitialized ;
2012-05-25 18:17:57 +02:00
public :
2014-09-01 15:00:19 +02:00
using APIBase : : _solve_impl ;
2012-05-25 18:17:57 +02:00
typedef _MatrixType MatrixType ;
2012-06-13 18:26:05 +02:00
typedef _OrderingType OrderingType ;
2012-05-25 18:17:57 +02:00
typedef typename MatrixType : : Scalar Scalar ;
2012-06-13 18:26:05 +02:00
typedef typename MatrixType : : RealScalar RealScalar ;
2014-12-04 22:48:53 +01:00
typedef typename MatrixType : : StorageIndex StorageIndex ;
typedef SparseMatrix < Scalar , ColMajor , StorageIndex > NCMatrix ;
typedef internal : : MappedSuperNodalMatrix < Scalar , StorageIndex > SCMatrix ;
2012-06-07 19:06:22 +02:00
typedef Matrix < Scalar , Dynamic , 1 > ScalarVector ;
2014-12-04 22:48:53 +01:00
typedef Matrix < StorageIndex , Dynamic , 1 > IndexVector ;
typedef PermutationMatrix < Dynamic , Dynamic , StorageIndex > PermutationType ;
typedef internal : : SparseLUImpl < Scalar , StorageIndex > Base ;
2012-09-25 09:53:40 +02:00
2012-05-25 18:17:57 +02:00
public :
2014-09-01 15:00:19 +02:00
SparseLU ( ) : m_lastError ( " " ) , m_Ustore ( 0 , 0 , 0 , 0 , 0 , 0 ) , m_symmetricmode ( false ) , m_diagpivotthresh ( 1.0 ) , m_detPermR ( 1 )
2012-05-25 18:17:57 +02:00
{
initperfvalues ( ) ;
}
2014-09-23 14:28:23 +02:00
explicit SparseLU ( const MatrixType & matrix ) : m_lastError ( " " ) , m_Ustore ( 0 , 0 , 0 , 0 , 0 , 0 ) , m_symmetricmode ( false ) , m_diagpivotthresh ( 1.0 ) , m_detPermR ( 1 )
2012-05-25 18:17:57 +02:00
{
2012-06-14 18:45:04 +02:00
initperfvalues ( ) ;
2012-05-25 18:17:57 +02:00
compute ( matrix ) ;
}
~ SparseLU ( )
{
2012-06-11 18:52:26 +02:00
// Free all explicit dynamic pointers
2012-05-25 18:17:57 +02:00
}
void analyzePattern ( const MatrixType & matrix ) ;
void factorize ( const MatrixType & matrix ) ;
2012-09-25 09:53:40 +02:00
void simplicialfactorize ( const MatrixType & matrix ) ;
2012-06-11 18:52:26 +02:00
/**
2013-07-17 09:30:25 +02:00
* Compute the symbolic and numeric factorization of the input sparse matrix .
* The input matrix should be in column - major storage .
*/
2012-06-11 18:52:26 +02:00
void compute ( const MatrixType & matrix )
{
// Analyze
analyzePattern ( matrix ) ;
//Factorize
factorize ( matrix ) ;
2012-06-13 18:26:05 +02:00
}
2012-05-25 18:17:57 +02:00
2015-02-13 18:57:41 +01:00
inline Index rows ( ) const { return m_mat . rows ( ) ; }
inline Index cols ( ) const { return m_mat . cols ( ) ; }
2012-05-25 18:17:57 +02:00
/** Indicate that the pattern of the input matrix is symmetric */
void isSymmetric ( bool sym )
{
m_symmetricmode = sym ;
}
2013-05-14 17:15:23 +02:00
/** \returns an expression of the matrix L, internally stored as supernodes
2013-07-17 09:30:25 +02:00
* The only operation available with this expression is the triangular solve
* \ code
* y = b ; matrixL ( ) . solveInPlace ( y ) ;
* \ endcode
*/
2013-01-25 20:38:26 +01:00
SparseLUMatrixLReturnType < SCMatrix > matrixL ( ) const
2012-05-25 18:17:57 +02:00
{
2013-01-25 20:38:26 +01:00
return SparseLUMatrixLReturnType < SCMatrix > ( m_Lstore ) ;
2012-05-25 18:17:57 +02:00
}
2013-05-14 17:15:23 +02:00
/** \returns an expression of the matrix U,
2013-07-17 09:30:25 +02:00
* The only operation available with this expression is the triangular solve
* \ code
* y = b ; matrixU ( ) . solveInPlace ( y ) ;
* \ endcode
*/
2014-12-04 22:48:53 +01:00
SparseLUMatrixUReturnType < SCMatrix , MappedSparseMatrix < Scalar , ColMajor , StorageIndex > > matrixU ( ) const
2013-05-14 17:15:23 +02:00
{
2014-12-04 22:48:53 +01:00
return SparseLUMatrixUReturnType < SCMatrix , MappedSparseMatrix < Scalar , ColMajor , StorageIndex > > ( m_Lstore , m_Ustore ) ;
2013-05-14 17:15:23 +02:00
}
/**
2013-07-17 09:30:25 +02:00
* \ returns a reference to the row matrix permutation \ f $ P_r \ f $ such that \ f $ P_r A P_c ^ T = L U \ f $
* \ sa colsPermutation ( )
*/
2013-05-14 17:15:23 +02:00
inline const PermutationType & rowsPermutation ( ) const
{
return m_perm_r ;
}
/**
2013-07-17 09:30:25 +02:00
* \ returns a reference to the column matrix permutation \ f $ P_c ^ T \ f $ such that \ f $ P_r A P_c ^ T = L U \ f $
* \ sa rowsPermutation ( )
*/
2013-05-14 17:15:23 +02:00
inline const PermutationType & colsPermutation ( ) const
{
return m_perm_c ;
}
2013-01-25 20:38:26 +01:00
/** Set the threshold used for a diagonal entry to be an acceptable pivot. */
2013-02-25 18:05:57 +01:00
void setPivotThreshold ( const RealScalar & thresh )
2012-07-18 16:59:00 +02:00
{
2013-01-25 20:38:26 +01:00
m_diagpivotthresh = thresh ;
2012-07-18 16:59:00 +02:00
}
2013-01-25 20:38:26 +01:00
2014-09-01 15:00:19 +02:00
# ifdef EIGEN_PARSED_BY_DOXYGEN
/** \returns the solution X of \f$ A X = B \f$ using the current decomposition of A.
*
* \ warning the destination matrix X in X = this - > solve ( B ) must be colmun - major .
*
* \ sa compute ( )
*/
template < typename Rhs >
inline const Solve < SparseLU , Rhs > solve ( const MatrixBase < Rhs > & B ) const ;
# endif // EIGEN_PARSED_BY_DOXYGEN
2013-07-17 09:30:25 +02:00
/** \brief Reports whether previous computation was successful.
2012-06-14 18:45:04 +02:00
*
* \ returns \ c Success if computation was succesful ,
2013-01-11 17:16:14 +01:00
* \ c NumericalIssue if the LU factorization reports a problem , zero diagonal for instance
2012-06-14 18:45:04 +02:00
* \ c InvalidInput if the input matrix is invalid
*
* \ sa iparm ( )
*/
ComputationInfo info ( ) const
{
eigen_assert ( m_isInitialized & & " Decomposition is not initialized. " ) ;
return m_info ;
}
2013-07-17 09:30:25 +02:00
2013-01-25 20:38:26 +01:00
/**
2013-07-17 09:30:25 +02:00
* \ returns A string describing the type of error
*/
2013-01-25 20:38:26 +01:00
std : : string lastErrorMessage ( ) const
{
return m_lastError ;
}
2013-06-11 14:46:13 +02:00
2012-06-13 18:26:05 +02:00
template < typename Rhs , typename Dest >
2014-09-01 15:00:19 +02:00
bool _solve_impl ( const MatrixBase < Rhs > & B , MatrixBase < Dest > & X_base ) const
2012-06-15 17:23:54 +02:00
{
2013-07-30 08:05:10 +02:00
Dest & X ( X_base . derived ( ) ) ;
2012-06-15 17:23:54 +02:00
eigen_assert ( m_factorizationIsOk & & " The matrix should be factorized first " ) ;
2012-06-13 18:26:05 +02:00
EIGEN_STATIC_ASSERT ( ( Dest : : Flags & RowMajorBit ) = = 0 ,
THIS_METHOD_IS_ONLY_FOR_COLUMN_MAJOR_MATRICES ) ;
2012-07-13 17:32:25 +02:00
// Permute the right hand side to form X = Pr*B
// on return, X is overwritten by the computed solution
2013-05-14 17:15:23 +02:00
X . resize ( B . rows ( ) , B . cols ( ) ) ;
2013-11-15 11:19:19 +01:00
// this ugly const_cast_derived() helps to detect aliasing when applying the permutations
2013-05-14 17:15:23 +02:00
for ( Index j = 0 ; j < B . cols ( ) ; + + j )
2013-11-15 11:19:19 +01:00
X . col ( j ) = rowsPermutation ( ) * B . const_cast_derived ( ) . col ( j ) ;
2012-06-13 18:26:05 +02:00
2013-05-14 17:15:23 +02:00
//Forward substitution with L
this - > matrixL ( ) . solveInPlace ( X ) ;
this - > matrixU ( ) . solveInPlace ( X ) ;
2012-06-13 18:26:05 +02:00
// Permute back the solution
2013-05-14 17:15:23 +02:00
for ( Index j = 0 ; j < B . cols ( ) ; + + j )
X . col ( j ) = colsPermutation ( ) . inverse ( ) * X . col ( j ) ;
2012-06-13 18:26:05 +02:00
return true ;
2012-06-08 17:23:38 +02:00
}
2013-07-17 09:30:25 +02:00
2013-06-11 14:46:13 +02:00
/**
* \ returns the absolute value of the determinant of the matrix of which
* * this is the QR decomposition .
*
* \ warning a determinant can be very big or small , so for matrices
* of large enough dimension , there is a risk of overflow / underflow .
* One way to work around that is to use logAbsDeterminant ( ) instead .
*
* \ sa logAbsDeterminant ( ) , signDeterminant ( )
2013-07-17 09:30:25 +02:00
*/
2013-08-20 14:13:41 +02:00
Scalar absDeterminant ( )
2013-06-11 14:46:13 +02:00
{
2013-08-20 14:12:42 +02:00
using std : : abs ;
2013-06-11 14:46:13 +02:00
eigen_assert ( m_factorizationIsOk & & " The matrix should be factorized first. " ) ;
// Initialize with the determinant of the row matrix
Scalar det = Scalar ( 1. ) ;
2014-10-17 16:52:56 +02:00
// Note that the diagonal blocks of U are stored in supernodes,
2013-06-11 14:46:13 +02:00
// which are available in the L part :)
for ( Index j = 0 ; j < this - > cols ( ) ; + + j )
{
for ( typename SCMatrix : : InnerIterator it ( m_Lstore , j ) ; it ; + + it )
{
2014-10-17 16:52:56 +02:00
if ( it . index ( ) = = j )
2013-06-11 14:46:13 +02:00
{
2013-08-20 16:06:13 +09:00
det * = abs ( it . value ( ) ) ;
2013-06-11 14:46:13 +02:00
break ;
}
}
2013-08-20 14:13:41 +02:00
}
return det ;
}
/** \returns the natural log of the absolute value of the determinant of the matrix
* of which * * this is the QR decomposition
*
* \ note This method is useful to work around the risk of overflow / underflow that ' s
* inherent to the determinant computation .
*
* \ sa absDeterminant ( ) , signDeterminant ( )
*/
Scalar logAbsDeterminant ( ) const
{
using std : : log ;
using std : : abs ;
2013-06-11 14:46:13 +02:00
2013-08-20 14:13:41 +02:00
eigen_assert ( m_factorizationIsOk & & " The matrix should be factorized first. " ) ;
Scalar det = Scalar ( 0. ) ;
for ( Index j = 0 ; j < this - > cols ( ) ; + + j )
{
for ( typename SCMatrix : : InnerIterator it ( m_Lstore , j ) ; it ; + + it )
{
if ( it . row ( ) < j ) continue ;
if ( it . row ( ) = = j )
{
det + = log ( abs ( it . value ( ) ) ) ;
break ;
}
}
}
return det ;
}
2013-06-11 14:46:13 +02:00
2013-08-20 14:13:41 +02:00
/** \returns A number representing the sign of the determinant
*
* \ sa absDeterminant ( ) , logAbsDeterminant ( )
*/
Scalar signDeterminant ( )
{
eigen_assert ( m_factorizationIsOk & & " The matrix should be factorized first. " ) ;
2015-02-16 19:09:22 +01:00
// Initialize with the determinant of the row matrix
Index det = 1 ;
// Note that the diagonal blocks of U are stored in supernodes,
// which are available in the L part :)
for ( Index j = 0 ; j < this - > cols ( ) ; + + j )
{
for ( typename SCMatrix : : InnerIterator it ( m_Lstore , j ) ; it ; + + it )
{
if ( it . index ( ) = = j )
{
if ( it . value ( ) < 0 )
det = - det ;
else if ( it . value ( ) = = 0 )
return 0 ;
break ;
}
}
}
return det * m_detPermR * m_detPermC ;
}
/** \returns The determinant of the matrix.
*
* \ sa absDeterminant ( ) , logAbsDeterminant ( )
*/
Scalar determinant ( )
{
eigen_assert ( m_factorizationIsOk & & " The matrix should be factorized first. " ) ;
// Initialize with the determinant of the row matrix
Scalar det = Scalar ( 1. ) ;
// Note that the diagonal blocks of U are stored in supernodes,
// which are available in the L part :)
for ( Index j = 0 ; j < this - > cols ( ) ; + + j )
{
for ( typename SCMatrix : : InnerIterator it ( m_Lstore , j ) ; it ; + + it )
{
if ( it . index ( ) = = j )
{
det * = it . value ( ) ;
break ;
}
}
}
2015-02-16 19:18:12 +01:00
return ( m_detPermR * m_detPermC ) > 0 ? det : - det ;
2013-08-20 14:13:41 +02:00
}
2012-06-13 18:26:05 +02:00
2012-05-25 18:17:57 +02:00
protected :
// Functions
2012-06-13 18:26:05 +02:00
void initperfvalues ( )
{
2015-01-30 17:24:40 +01:00
m_perfv . panel_size = 16 ;
2012-09-10 12:41:26 +02:00
m_perfv . relax = 1 ;
2012-10-30 15:09:48 +01:00
m_perfv . maxsuper = 128 ;
m_perfv . rowblk = 16 ;
m_perfv . colblk = 8 ;
2012-09-04 12:21:07 +02:00
m_perfv . fillfactor = 20 ;
2012-06-13 18:26:05 +02:00
}
2012-05-25 18:17:57 +02:00
// Variables
mutable ComputationInfo m_info ;
bool m_factorizationIsOk ;
bool m_analysisIsOk ;
2013-01-25 20:38:26 +01:00
std : : string m_lastError ;
2012-05-25 18:17:57 +02:00
NCMatrix m_mat ; // The input (permuted ) matrix
SCMatrix m_Lstore ; // The lower triangular matrix (supernodal)
2014-12-04 22:48:53 +01:00
MappedSparseMatrix < Scalar , ColMajor , StorageIndex > m_Ustore ; // The upper triangular matrix
2012-05-25 18:17:57 +02:00
PermutationType m_perm_c ; // Column permutation
PermutationType m_perm_r ; // Row permutation
2012-06-07 19:06:22 +02:00
IndexVector m_etree ; // Column elimination tree
2012-05-25 18:17:57 +02:00
2013-01-25 20:38:26 +01:00
typename Base : : GlobalLU_t m_glu ;
2012-06-12 18:19:59 +02:00
2013-01-25 20:38:26 +01:00
// SparseLU options
2012-05-25 18:17:57 +02:00
bool m_symmetricmode ;
// values for performance
2015-02-13 18:57:41 +01:00
internal : : perfvalues m_perfv ;
2012-05-25 18:17:57 +02:00
RealScalar m_diagpivotthresh ; // Specifies the threshold used for a diagonal entry to be an acceptable pivot
2015-02-13 18:57:41 +01:00
Index m_nnzL , m_nnzU ; // Nonzeros in L and U factors
2015-02-16 19:09:22 +01:00
Index m_detPermR , m_detPermC ; // Determinants of the permutation matrices
2012-05-25 18:17:57 +02:00
private :
2013-06-12 18:02:13 +02:00
// Disable copy constructor
SparseLU ( const SparseLU & ) ;
2012-05-25 18:17:57 +02:00
} ; // End class SparseLU
2013-06-11 14:46:13 +02:00
2012-06-12 18:19:59 +02:00
// Functions needed by the anaysis phase
2012-05-25 18:17:57 +02:00
/**
2013-07-17 09:30:25 +02:00
* Compute the column permutation to minimize the fill - in
*
* - Apply this permutation to the input matrix -
*
* - Compute the column elimination tree on the permuted matrix
*
* - Postorder the elimination tree and the column permutation
*
*/
2012-06-11 18:52:26 +02:00
template < typename MatrixType , typename OrderingType >
2012-06-13 18:26:05 +02:00
void SparseLU < MatrixType , OrderingType > : : analyzePattern ( const MatrixType & mat )
2012-05-25 18:17:57 +02:00
{
2012-06-11 18:52:26 +02:00
//TODO It is possible as in SuperLU to compute row and columns scaling vectors to equilibrate the matrix mat.
2014-10-06 11:42:31 +02:00
// Firstly, copy the whole input matrix.
m_mat = mat ;
// Compute fill-in ordering
2012-07-06 20:18:16 +02:00
OrderingType ord ;
2014-10-06 11:42:31 +02:00
ord ( m_mat , m_perm_c ) ;
2012-07-18 16:59:00 +02:00
2012-05-25 18:17:57 +02:00
// Apply the permutation to the column of the input matrix
2014-10-06 11:42:31 +02:00
if ( m_perm_c . size ( ) )
{
2013-01-25 20:38:26 +01:00
m_mat . uncompress ( ) ; //NOTE: The effect of this command is only to create the InnerNonzeros pointers. FIXME : This vector is filled but not subsequently used.
2014-10-06 11:42:31 +02:00
// Then, permute only the column pointers
2014-12-04 22:48:53 +01:00
ei_declare_aligned_stack_constructed_variable ( StorageIndex , outerIndexPtr , mat . cols ( ) + 1 , mat . isCompressed ( ) ? const_cast < StorageIndex * > ( mat . outerIndexPtr ( ) ) : 0 ) ;
2014-10-06 11:42:31 +02:00
// If the input matrix 'mat' is uncompressed, then the outer-indices do not match the ones of m_mat, and a copy is thus needed.
if ( ! mat . isCompressed ( ) )
IndexVector : : Map ( outerIndexPtr , mat . cols ( ) + 1 ) = IndexVector : : Map ( m_mat . outerIndexPtr ( ) , mat . cols ( ) + 1 ) ;
// Apply the permutation and compute the nnz per column.
2013-01-29 16:21:24 +01:00
for ( Index i = 0 ; i < mat . cols ( ) ; i + + )
2013-01-25 20:38:26 +01:00
{
2013-07-16 15:15:53 +02:00
m_mat . outerIndexPtr ( ) [ m_perm_c . indices ( ) ( i ) ] = outerIndexPtr [ i ] ;
m_mat . innerNonZeroPtr ( ) [ m_perm_c . indices ( ) ( i ) ] = outerIndexPtr [ i + 1 ] - outerIndexPtr [ i ] ;
2013-01-25 20:38:26 +01:00
}
2012-07-27 16:38:20 +02:00
}
2014-10-06 11:42:31 +02:00
2012-05-25 18:17:57 +02:00
// Compute the column elimination tree of the permuted matrix
2013-01-11 17:16:14 +01:00
IndexVector firstRowElt ;
internal : : coletree ( m_mat , m_etree , firstRowElt ) ;
2012-07-06 20:18:16 +02:00
2012-05-25 18:17:57 +02:00
// In symmetric mode, do not do postorder here
2012-06-11 18:52:26 +02:00
if ( ! m_symmetricmode ) {
2012-06-07 19:06:22 +02:00
IndexVector post , iwork ;
2012-05-25 18:17:57 +02:00
// Post order etree
2015-02-16 13:19:05 +01:00
internal : : treePostorder ( StorageIndex ( m_mat . cols ( ) ) , m_etree , post ) ;
2012-05-25 18:17:57 +02:00
2012-07-06 20:18:16 +02:00
2012-05-25 18:17:57 +02:00
// Renumber etree in postorder
2013-01-29 16:21:24 +01:00
Index m = m_mat . cols ( ) ;
2012-06-13 18:26:05 +02:00
iwork . resize ( m + 1 ) ;
2013-01-29 16:21:24 +01:00
for ( Index i = 0 ; i < m ; + + i ) iwork ( post ( i ) ) = post ( m_etree ( i ) ) ;
2012-06-11 18:52:26 +02:00
m_etree = iwork ;
// Postmultiply A*Pc by post, i.e reorder the matrix according to the postorder of the etree
2013-01-25 20:38:26 +01:00
PermutationType post_perm ( m ) ;
2013-01-29 16:21:24 +01:00
for ( Index i = 0 ; i < m ; i + + )
2012-06-14 18:45:04 +02:00
post_perm . indices ( ) ( i ) = post ( i ) ;
2012-07-13 17:32:25 +02:00
// Combine the two permutations : postorder the permutation for future use
2013-01-25 20:38:26 +01:00
if ( m_perm_c . size ( ) ) {
m_perm_c = post_perm * m_perm_c ;
}
2012-07-06 20:18:16 +02:00
2012-05-25 18:17:57 +02:00
} // end postordering
2012-06-11 18:52:26 +02:00
2012-06-13 18:26:05 +02:00
m_analysisIsOk = true ;
2012-05-25 18:17:57 +02:00
}
2012-09-25 09:53:40 +02:00
// Functions needed by the numerical factorization phase
2012-06-12 18:19:59 +02:00
2012-06-14 18:45:04 +02:00
2012-05-25 18:17:57 +02:00
/**
2013-07-17 09:30:25 +02:00
* - Numerical factorization
* - Interleaved with the symbolic factorization
* On exit , info is
*
* = 0 : successful factorization
*
* > 0 : if info = i , and i is
*
* < = A - > ncol : U ( i , i ) is exactly zero . The factorization has
* been completed , but the factor U is exactly singular ,
* and division by zero will occur if it is used to solve a
* system of equations .
*
* > A - > ncol : number of bytes allocated when memory allocation
* failure occurred , plus A - > ncol . If lwork = - 1 , it is
* the estimated amount of space needed , plus A - > ncol .
*/
2012-06-13 18:26:05 +02:00
template < typename MatrixType , typename OrderingType >
void SparseLU < MatrixType , OrderingType > : : factorize ( const MatrixType & matrix )
2012-05-25 18:17:57 +02:00
{
2013-01-25 20:38:26 +01:00
using internal : : emptyIdxLU ;
2012-06-13 18:26:05 +02:00
eigen_assert ( m_analysisIsOk & & " analyzePattern() should be called first " ) ;
2012-06-11 18:52:26 +02:00
eigen_assert ( ( matrix . rows ( ) = = matrix . cols ( ) ) & & " Only for squared matrices " ) ;
2015-02-13 18:57:41 +01:00
typedef typename IndexVector : : Scalar StorageIndex ;
2012-06-13 18:26:05 +02:00
2014-09-01 17:16:32 +02:00
m_isInitialized = true ;
2012-06-13 18:26:05 +02:00
2012-06-11 18:52:26 +02:00
// Apply the column permutation computed in analyzepattern()
2012-07-27 16:38:20 +02:00
// m_mat = matrix * m_perm_c.inverse();
m_mat = matrix ;
2013-01-25 20:38:26 +01:00
if ( m_perm_c . size ( ) )
2012-07-27 16:38:20 +02:00
{
2013-01-25 20:38:26 +01:00
m_mat . uncompress ( ) ; //NOTE: The effect of this command is only to create the InnerNonzeros pointers.
//Then, permute only the column pointers
2015-02-13 18:57:41 +01:00
const StorageIndex * outerIndexPtr ;
2013-07-16 15:56:05 +02:00
if ( matrix . isCompressed ( ) ) outerIndexPtr = matrix . outerIndexPtr ( ) ;
2013-07-16 15:15:53 +02:00
else
{
2015-02-13 18:57:41 +01:00
StorageIndex * outerIndexPtr_t = new StorageIndex [ matrix . cols ( ) + 1 ] ;
2013-07-16 15:56:05 +02:00
for ( Index i = 0 ; i < = matrix . cols ( ) ; i + + ) outerIndexPtr_t [ i ] = m_mat . outerIndexPtr ( ) [ i ] ;
outerIndexPtr = outerIndexPtr_t ;
2013-07-16 15:15:53 +02:00
}
2013-01-29 16:21:24 +01:00
for ( Index i = 0 ; i < matrix . cols ( ) ; i + + )
2013-01-25 20:38:26 +01:00
{
2013-07-16 15:15:53 +02:00
m_mat . outerIndexPtr ( ) [ m_perm_c . indices ( ) ( i ) ] = outerIndexPtr [ i ] ;
m_mat . innerNonZeroPtr ( ) [ m_perm_c . indices ( ) ( i ) ] = outerIndexPtr [ i + 1 ] - outerIndexPtr [ i ] ;
2013-01-25 20:38:26 +01:00
}
2013-07-16 15:15:53 +02:00
if ( ! matrix . isCompressed ( ) ) delete [ ] outerIndexPtr ;
2013-01-25 20:38:26 +01:00
}
else
{ //FIXME This should not be needed if the empty permutation is handled transparently
m_perm_c . resize ( matrix . cols ( ) ) ;
2015-02-16 13:19:05 +01:00
for ( StorageIndex i = 0 ; i < matrix . cols ( ) ; + + i ) m_perm_c . indices ( ) ( i ) = i ;
2012-07-27 16:38:20 +02:00
}
2012-05-25 18:17:57 +02:00
2013-01-29 16:21:24 +01:00
Index m = m_mat . rows ( ) ;
Index n = m_mat . cols ( ) ;
Index nnz = m_mat . nonZeros ( ) ;
Index maxpanel = m_perfv . panel_size * m ;
2012-07-13 17:32:25 +02:00
// Allocate working storage common to the factor routines
2013-01-29 16:21:24 +01:00
Index lwork = 0 ;
Index info = Base : : memInit ( m , n , nnz , lwork , m_perfv . fillfactor , m_perfv . panel_size , m_glu ) ;
2012-06-11 18:52:26 +02:00
if ( info )
{
2013-01-25 20:38:26 +01:00
m_lastError = " UNABLE TO ALLOCATE WORKING MEMORY \n \n " ;
2012-06-11 18:52:26 +02:00
m_factorizationIsOk = false ;
return ;
}
2012-05-25 18:17:57 +02:00
2012-06-14 18:45:04 +02:00
// Set up pointers for integer working arrays
2012-07-13 17:32:25 +02:00
IndexVector segrep ( m ) ; segrep . setZero ( ) ;
IndexVector parent ( m ) ; parent . setZero ( ) ;
IndexVector xplore ( m ) ; xplore . setZero ( ) ;
2012-06-14 18:45:04 +02:00
IndexVector repfnz ( maxpanel ) ;
IndexVector panel_lsub ( maxpanel ) ;
2012-07-10 19:16:57 +02:00
IndexVector xprune ( n ) ; xprune . setZero ( ) ;
2013-01-25 20:38:26 +01:00
IndexVector marker ( m * internal : : LUNoMarker ) ; marker . setZero ( ) ;
2012-06-08 17:23:38 +02:00
2012-05-25 18:17:57 +02:00
repfnz . setConstant ( - 1 ) ;
panel_lsub . setConstant ( - 1 ) ;
// Set up pointers for scalar working arrays
2012-06-14 18:45:04 +02:00
ScalarVector dense ;
dense . setZero ( maxpanel ) ;
ScalarVector tempv ;
2013-01-25 20:38:26 +01:00
tempv . setZero ( internal : : LUnumTempV ( m , m_perfv . panel_size , m_perfv . maxsuper , /*m_perfv.rowblk*/ m ) ) ;
2012-05-25 18:17:57 +02:00
// Compute the inverse of perm_c
2012-07-13 17:32:25 +02:00
PermutationType iperm_c ( m_perm_c . inverse ( ) ) ;
2012-05-25 18:17:57 +02:00
// Identify initial relaxed snodes
2012-06-07 19:06:22 +02:00
IndexVector relax_end ( n ) ;
2012-06-14 18:45:04 +02:00
if ( m_symmetricmode = = true )
2013-01-25 20:38:26 +01:00
Base : : heap_relax_snode ( n , m_etree , m_perfv . relax , marker , relax_end ) ;
2012-05-25 18:17:57 +02:00
else
2013-01-25 20:38:26 +01:00
Base : : relax_snode ( n , m_etree , m_perfv . relax , marker , relax_end ) ;
2012-05-25 18:17:57 +02:00
2012-07-06 20:18:16 +02:00
2012-06-14 18:45:04 +02:00
m_perm_r . resize ( m ) ;
2012-07-13 17:32:25 +02:00
m_perm_r . indices ( ) . setConstant ( - 1 ) ;
2012-05-25 18:17:57 +02:00
marker . setConstant ( - 1 ) ;
2013-07-10 23:48:26 +02:00
m_detPermR = 1 ; // Record the determinant of the row permutation
2012-05-25 18:17:57 +02:00
2013-01-25 20:38:26 +01:00
m_glu . supno ( 0 ) = emptyIdxLU ; m_glu . xsup . setConstant ( 0 ) ;
2012-09-25 09:53:40 +02:00
m_glu . xsup ( 0 ) = m_glu . xlsub ( 0 ) = m_glu . xusub ( 0 ) = m_glu . xlusup ( 0 ) = Index ( 0 ) ;
2012-05-25 18:17:57 +02:00
// Work on one 'panel' at a time. A panel is one of the following :
// (a) a relaxed supernode at the bottom of the etree, or
// (b) panel_size contiguous columns, <panel_size> defined by the user
2013-01-29 16:21:24 +01:00
Index jcol ;
2012-06-07 19:06:22 +02:00
IndexVector panel_histo ( n ) ;
2012-06-11 18:52:26 +02:00
Index pivrow ; // Pivotal row number in the original row matrix
2013-01-29 16:21:24 +01:00
Index nseg1 ; // Number of segments in U-column above panel row jcol
Index nseg ; // Number of segments in each U-column
Index irep ;
Index i , k , jj ;
2012-06-11 18:52:26 +02:00
for ( jcol = 0 ; jcol < n ; )
2012-05-25 18:17:57 +02:00
{
2012-10-30 15:17:58 +01:00
// Adjust panel size so that a panel won't overlap with the next relaxed snode.
2013-01-29 16:21:24 +01:00
Index panel_size = m_perfv . panel_size ; // upper bound on panel width
2012-10-30 15:17:58 +01:00
for ( k = jcol + 1 ; k < ( std : : min ) ( jcol + panel_size , n ) ; k + + )
{
2013-01-25 20:38:26 +01:00
if ( relax_end ( k ) ! = emptyIdxLU )
2012-10-30 15:17:58 +01:00
{
panel_size = k - jcol ;
break ;
}
}
if ( k = = n )
panel_size = n - jcol ;
// Symbolic outer factorization on a panel of columns
2013-01-25 20:38:26 +01:00
Base : : panel_dfs ( m , panel_size , jcol , m_mat , m_perm_r . indices ( ) , nseg1 , dense , panel_lsub , segrep , repfnz , xprune , marker , parent , xplore , m_glu ) ;
2012-10-30 15:17:58 +01:00
// Numeric sup-panel updates in topological order
2013-01-25 20:38:26 +01:00
Base : : panel_bmod ( m , panel_size , jcol , nseg1 , dense , tempv , segrep , repfnz , m_glu ) ;
2012-10-30 15:17:58 +01:00
// Sparse LU within the panel, and below the panel diagonal
for ( jj = jcol ; jj < jcol + panel_size ; jj + + )
{
k = ( jj - jcol ) * m ; // Column index for w-wide arrays
2012-05-25 18:17:57 +02:00
2012-10-30 15:17:58 +01:00
nseg = nseg1 ; // begin after all the panel segments
//Depth-first-search for the current column
VectorBlock < IndexVector > panel_lsubk ( panel_lsub , k , m ) ;
VectorBlock < IndexVector > repfnz_k ( repfnz , k , m ) ;
2013-01-25 20:38:26 +01:00
info = Base : : column_dfs ( m , jj , m_perm_r . indices ( ) , m_perfv . maxsuper , nseg , panel_lsubk , segrep , repfnz_k , xprune , marker , parent , xplore , m_glu ) ;
2012-06-07 19:06:22 +02:00
if ( info )
2012-05-25 18:17:57 +02:00
{
2013-01-25 20:38:26 +01:00
m_lastError = " UNABLE TO EXPAND MEMORY IN COLUMN_DFS() " ;
2012-05-29 17:55:38 +02:00
m_info = NumericalIssue ;
m_factorizationIsOk = false ;
return ;
2012-05-25 18:17:57 +02:00
}
2012-10-30 15:17:58 +01:00
// Numeric updates to this column
VectorBlock < ScalarVector > dense_k ( dense , k , m ) ;
VectorBlock < IndexVector > segrep_k ( segrep , nseg1 , m - nseg1 ) ;
2013-01-25 20:38:26 +01:00
info = Base : : column_bmod ( jj , ( nseg - nseg1 ) , dense_k , tempv , segrep_k , repfnz_k , jcol , m_glu ) ;
2012-10-30 15:17:58 +01:00
if ( info )
2012-05-25 18:17:57 +02:00
{
2013-01-25 20:38:26 +01:00
m_lastError = " UNABLE TO EXPAND MEMORY IN COLUMN_BMOD() " ;
2012-10-30 15:17:58 +01:00
m_info = NumericalIssue ;
m_factorizationIsOk = false ;
return ;
2012-05-25 18:17:57 +02:00
}
2012-06-11 18:52:26 +02:00
2012-10-30 15:17:58 +01:00
// Copy the U-segments to ucol(*)
2013-01-25 20:38:26 +01:00
info = Base : : copy_to_ucol ( jj , nseg , segrep , repfnz_k , m_perm_r . indices ( ) , dense_k , m_glu ) ;
2012-10-30 15:17:58 +01:00
if ( info )
{
2013-01-25 20:38:26 +01:00
m_lastError = " UNABLE TO EXPAND MEMORY IN COPY_TO_UCOL() " ;
2012-10-30 15:17:58 +01:00
m_info = NumericalIssue ;
m_factorizationIsOk = false ;
return ;
2012-05-25 18:17:57 +02:00
}
2012-10-30 15:17:58 +01:00
// Form the L-segment
2013-01-25 20:38:26 +01:00
info = Base : : pivotL ( jj , m_diagpivotthresh , m_perm_r . indices ( ) , iperm_c . indices ( ) , pivrow , m_glu ) ;
2012-10-30 15:17:58 +01:00
if ( info )
2012-05-25 18:17:57 +02:00
{
2013-01-25 20:38:26 +01:00
m_lastError = " THE MATRIX IS STRUCTURALLY SINGULAR ... ZERO COLUMN AT " ;
std : : ostringstream returnInfo ;
returnInfo < < info ;
m_lastError + = returnInfo . str ( ) ;
2012-10-30 15:17:58 +01:00
m_info = NumericalIssue ;
m_factorizationIsOk = false ;
return ;
2012-05-25 18:17:57 +02:00
}
2013-06-11 14:46:13 +02:00
// Update the determinant of the row permutation matrix
2015-02-16 19:09:22 +01:00
// FIXME: the following test is not correct, we should probably take iperm_c into account and pivrow is not directly the row pivot.
if ( pivrow ! = jj ) m_detPermR = - m_detPermR ;
2013-06-11 14:46:13 +02:00
2012-10-30 15:17:58 +01:00
// Prune columns (0:jj-1) using column jj
2013-01-25 20:38:26 +01:00
Base : : pruneL ( jj , m_perm_r . indices ( ) , pivrow , nseg , segrep , repfnz_k , xprune , m_glu ) ;
2012-05-25 18:17:57 +02:00
2012-10-30 15:17:58 +01:00
// Reset repfnz for this column
for ( i = 0 ; i < nseg ; i + + )
2012-05-25 18:17:57 +02:00
{
2012-10-30 15:17:58 +01:00
irep = segrep ( i ) ;
2013-01-25 20:38:26 +01:00
repfnz_k ( irep ) = emptyIdxLU ;
2012-10-30 15:17:58 +01:00
}
} // end SparseLU within the panel
jcol + = panel_size ; // Move to the next panel
2012-05-25 18:17:57 +02:00
} // end for -- end elimination
2012-05-31 17:10:29 +02:00
2015-02-16 19:09:22 +01:00
m_detPermR = m_perm_r . determinant ( ) ;
m_detPermC = m_perm_c . determinant ( ) ;
2012-05-31 17:10:29 +02:00
// Count the number of nonzeros in factors
2013-01-25 20:38:26 +01:00
Base : : countnz ( n , m_nnzL , m_nnzU , m_glu ) ;
2012-05-31 17:10:29 +02:00
// Apply permutation to the L subscripts
2015-02-16 19:09:22 +01:00
Base : : fixupL ( n , m_perm_r . indices ( ) , m_glu ) ;
2012-05-31 17:10:29 +02:00
2012-06-01 18:44:51 +02:00
// Create supernode matrix L
2012-06-13 18:26:05 +02:00
m_Lstore . setInfos ( m , n , m_glu . lusup , m_glu . xlusup , m_glu . lsub , m_glu . xlsub , m_glu . supno , m_glu . xsup ) ;
// Create the column major upper sparse matrix U;
2014-12-04 22:48:53 +01:00
new ( & m_Ustore ) MappedSparseMatrix < Scalar , ColMajor , StorageIndex > ( m , n , m_nnzU , m_glu . xusub . data ( ) , m_glu . usub . data ( ) , m_glu . ucol . data ( ) ) ;
2012-06-08 17:23:38 +02:00
2012-05-29 17:55:38 +02:00
m_info = Success ;
2012-06-13 18:26:05 +02:00
m_factorizationIsOk = true ;
2012-05-25 18:17:57 +02:00
}
2013-01-25 20:38:26 +01:00
template < typename MappedSupernodalType >
2013-07-10 23:48:26 +02:00
struct SparseLUMatrixLReturnType : internal : : no_assignment_operator
2013-01-25 20:38:26 +01:00
{
typedef typename MappedSupernodalType : : Scalar Scalar ;
2014-09-23 14:28:23 +02:00
explicit SparseLUMatrixLReturnType ( const MappedSupernodalType & mapL ) : m_mapL ( mapL )
2013-01-25 20:38:26 +01:00
{ }
2015-02-13 18:57:41 +01:00
Index rows ( ) { return m_mapL . rows ( ) ; }
Index cols ( ) { return m_mapL . cols ( ) ; }
2013-01-25 20:38:26 +01:00
template < typename Dest >
void solveInPlace ( MatrixBase < Dest > & X ) const
{
m_mapL . solveInPlace ( X ) ;
}
const MappedSupernodalType & m_mapL ;
} ;
2013-05-14 17:15:23 +02:00
template < typename MatrixLType , typename MatrixUType >
2013-07-10 23:48:26 +02:00
struct SparseLUMatrixUReturnType : internal : : no_assignment_operator
2013-05-14 17:15:23 +02:00
{
typedef typename MatrixLType : : Scalar Scalar ;
2014-09-23 14:28:23 +02:00
explicit SparseLUMatrixUReturnType ( const MatrixLType & mapL , const MatrixUType & mapU )
2013-05-14 17:15:23 +02:00
: m_mapL ( mapL ) , m_mapU ( mapU )
{ }
2015-02-13 18:57:41 +01:00
Index rows ( ) { return m_mapL . rows ( ) ; }
Index cols ( ) { return m_mapL . cols ( ) ; }
2013-05-14 17:15:23 +02:00
template < typename Dest > void solveInPlace ( MatrixBase < Dest > & X ) const
{
2014-12-04 22:48:53 +01:00
Index nrhs = X . cols ( ) ;
Index n = X . rows ( ) ;
2013-05-14 17:15:23 +02:00
// Backward solve with U
for ( Index k = m_mapL . nsuper ( ) ; k > = 0 ; k - - )
{
Index fsupc = m_mapL . supToCol ( ) [ k ] ;
Index lda = m_mapL . colIndexPtr ( ) [ fsupc + 1 ] - m_mapL . colIndexPtr ( ) [ fsupc ] ; // leading dimension
Index nsupc = m_mapL . supToCol ( ) [ k + 1 ] - fsupc ;
Index luptr = m_mapL . colIndexPtr ( ) [ fsupc ] ;
if ( nsupc = = 1 )
{
for ( Index j = 0 ; j < nrhs ; j + + )
{
X ( fsupc , j ) / = m_mapL . valuePtr ( ) [ luptr ] ;
}
}
else
{
2015-08-07 23:10:56 +02:00
Map < const Matrix < Scalar , Dynamic , Dynamic , ColMajor > , 0 , OuterStride < > > A ( & ( m_mapL . valuePtr ( ) [ luptr ] ) , nsupc , nsupc , OuterStride < > ( lda ) ) ;
Map < Matrix < Scalar , Dynamic , Dynamic , ColMajor > , 0 , OuterStride < > > U ( & ( X ( fsupc , 0 ) ) , nsupc , nrhs , OuterStride < > ( n ) ) ;
2013-05-14 17:15:23 +02:00
U = A . template triangularView < Upper > ( ) . solve ( U ) ;
}
for ( Index j = 0 ; j < nrhs ; + + j )
{
for ( Index jcol = fsupc ; jcol < fsupc + nsupc ; jcol + + )
{
typename MatrixUType : : InnerIterator it ( m_mapU , jcol ) ;
for ( ; it ; + + it )
{
Index irow = it . index ( ) ;
X ( irow , j ) - = X ( jcol , j ) * it . value ( ) ;
}
}
}
} // End For U-solve
}
const MatrixLType & m_mapL ;
const MatrixUType & m_mapU ;
} ;
2013-07-10 23:48:26 +02:00
2012-05-25 18:17:57 +02:00
} // End namespace Eigen
2012-10-30 15:17:58 +01:00
2012-09-25 09:53:40 +02:00
# endif