1*bf2c3715SXin Li // This file is part of Eigen, a lightweight C++ template library
2*bf2c3715SXin Li // for linear algebra.
3*bf2c3715SXin Li //
4*bf2c3715SXin Li // Copyright (C) 2012 Désiré Nuentsa-Wakam <[email protected]>
5*bf2c3715SXin Li // Copyright (C) 2012 Gael Guennebaud <[email protected]>
6*bf2c3715SXin Li //
7*bf2c3715SXin Li // This Source Code Form is subject to the terms of the Mozilla
8*bf2c3715SXin Li // Public License v. 2.0. If a copy of the MPL was not distributed
9*bf2c3715SXin Li // with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
10*bf2c3715SXin Li
11*bf2c3715SXin Li #ifndef EIGEN_SPARSELU_SUPERNODAL_MATRIX_H
12*bf2c3715SXin Li #define EIGEN_SPARSELU_SUPERNODAL_MATRIX_H
13*bf2c3715SXin Li
14*bf2c3715SXin Li namespace Eigen {
15*bf2c3715SXin Li namespace internal {
16*bf2c3715SXin Li
17*bf2c3715SXin Li /** \ingroup SparseLU_Module
18*bf2c3715SXin Li * \brief a class to manipulate the L supernodal factor from the SparseLU factorization
19*bf2c3715SXin Li *
20*bf2c3715SXin Li * This class contain the data to easily store
21*bf2c3715SXin Li * and manipulate the supernodes during the factorization and solution phase of Sparse LU.
22*bf2c3715SXin Li * Only the lower triangular matrix has supernodes.
23*bf2c3715SXin Li *
24*bf2c3715SXin Li * NOTE : This class corresponds to the SCformat structure in SuperLU
25*bf2c3715SXin Li *
26*bf2c3715SXin Li */
27*bf2c3715SXin Li /* TODO
28*bf2c3715SXin Li * InnerIterator as for sparsematrix
29*bf2c3715SXin Li * SuperInnerIterator to iterate through all supernodes
30*bf2c3715SXin Li * Function for triangular solve
31*bf2c3715SXin Li */
32*bf2c3715SXin Li template <typename _Scalar, typename _StorageIndex>
33*bf2c3715SXin Li class MappedSuperNodalMatrix
34*bf2c3715SXin Li {
35*bf2c3715SXin Li public:
36*bf2c3715SXin Li typedef _Scalar Scalar;
37*bf2c3715SXin Li typedef _StorageIndex StorageIndex;
38*bf2c3715SXin Li typedef Matrix<StorageIndex,Dynamic,1> IndexVector;
39*bf2c3715SXin Li typedef Matrix<Scalar,Dynamic,1> ScalarVector;
40*bf2c3715SXin Li public:
MappedSuperNodalMatrix()41*bf2c3715SXin Li MappedSuperNodalMatrix()
42*bf2c3715SXin Li {
43*bf2c3715SXin Li
44*bf2c3715SXin Li }
MappedSuperNodalMatrix(Index m,Index n,ScalarVector & nzval,IndexVector & nzval_colptr,IndexVector & rowind,IndexVector & rowind_colptr,IndexVector & col_to_sup,IndexVector & sup_to_col)45*bf2c3715SXin Li MappedSuperNodalMatrix(Index m, Index n, ScalarVector& nzval, IndexVector& nzval_colptr, IndexVector& rowind,
46*bf2c3715SXin Li IndexVector& rowind_colptr, IndexVector& col_to_sup, IndexVector& sup_to_col )
47*bf2c3715SXin Li {
48*bf2c3715SXin Li setInfos(m, n, nzval, nzval_colptr, rowind, rowind_colptr, col_to_sup, sup_to_col);
49*bf2c3715SXin Li }
50*bf2c3715SXin Li
~MappedSuperNodalMatrix()51*bf2c3715SXin Li ~MappedSuperNodalMatrix()
52*bf2c3715SXin Li {
53*bf2c3715SXin Li
54*bf2c3715SXin Li }
55*bf2c3715SXin Li /**
56*bf2c3715SXin Li * Set appropriate pointers for the lower triangular supernodal matrix
57*bf2c3715SXin Li * These infos are available at the end of the numerical factorization
58*bf2c3715SXin Li * FIXME This class will be modified such that it can be use in the course
59*bf2c3715SXin Li * of the factorization.
60*bf2c3715SXin Li */
setInfos(Index m,Index n,ScalarVector & nzval,IndexVector & nzval_colptr,IndexVector & rowind,IndexVector & rowind_colptr,IndexVector & col_to_sup,IndexVector & sup_to_col)61*bf2c3715SXin Li void setInfos(Index m, Index n, ScalarVector& nzval, IndexVector& nzval_colptr, IndexVector& rowind,
62*bf2c3715SXin Li IndexVector& rowind_colptr, IndexVector& col_to_sup, IndexVector& sup_to_col )
63*bf2c3715SXin Li {
64*bf2c3715SXin Li m_row = m;
65*bf2c3715SXin Li m_col = n;
66*bf2c3715SXin Li m_nzval = nzval.data();
67*bf2c3715SXin Li m_nzval_colptr = nzval_colptr.data();
68*bf2c3715SXin Li m_rowind = rowind.data();
69*bf2c3715SXin Li m_rowind_colptr = rowind_colptr.data();
70*bf2c3715SXin Li m_nsuper = col_to_sup(n);
71*bf2c3715SXin Li m_col_to_sup = col_to_sup.data();
72*bf2c3715SXin Li m_sup_to_col = sup_to_col.data();
73*bf2c3715SXin Li }
74*bf2c3715SXin Li
75*bf2c3715SXin Li /**
76*bf2c3715SXin Li * Number of rows
77*bf2c3715SXin Li */
rows()78*bf2c3715SXin Li Index rows() const { return m_row; }
79*bf2c3715SXin Li
80*bf2c3715SXin Li /**
81*bf2c3715SXin Li * Number of columns
82*bf2c3715SXin Li */
cols()83*bf2c3715SXin Li Index cols() const { return m_col; }
84*bf2c3715SXin Li
85*bf2c3715SXin Li /**
86*bf2c3715SXin Li * Return the array of nonzero values packed by column
87*bf2c3715SXin Li *
88*bf2c3715SXin Li * The size is nnz
89*bf2c3715SXin Li */
valuePtr()90*bf2c3715SXin Li Scalar* valuePtr() { return m_nzval; }
91*bf2c3715SXin Li
valuePtr()92*bf2c3715SXin Li const Scalar* valuePtr() const
93*bf2c3715SXin Li {
94*bf2c3715SXin Li return m_nzval;
95*bf2c3715SXin Li }
96*bf2c3715SXin Li /**
97*bf2c3715SXin Li * Return the pointers to the beginning of each column in \ref valuePtr()
98*bf2c3715SXin Li */
colIndexPtr()99*bf2c3715SXin Li StorageIndex* colIndexPtr()
100*bf2c3715SXin Li {
101*bf2c3715SXin Li return m_nzval_colptr;
102*bf2c3715SXin Li }
103*bf2c3715SXin Li
colIndexPtr()104*bf2c3715SXin Li const StorageIndex* colIndexPtr() const
105*bf2c3715SXin Li {
106*bf2c3715SXin Li return m_nzval_colptr;
107*bf2c3715SXin Li }
108*bf2c3715SXin Li
109*bf2c3715SXin Li /**
110*bf2c3715SXin Li * Return the array of compressed row indices of all supernodes
111*bf2c3715SXin Li */
rowIndex()112*bf2c3715SXin Li StorageIndex* rowIndex() { return m_rowind; }
113*bf2c3715SXin Li
rowIndex()114*bf2c3715SXin Li const StorageIndex* rowIndex() const
115*bf2c3715SXin Li {
116*bf2c3715SXin Li return m_rowind;
117*bf2c3715SXin Li }
118*bf2c3715SXin Li
119*bf2c3715SXin Li /**
120*bf2c3715SXin Li * Return the location in \em rowvaluePtr() which starts each column
121*bf2c3715SXin Li */
rowIndexPtr()122*bf2c3715SXin Li StorageIndex* rowIndexPtr() { return m_rowind_colptr; }
123*bf2c3715SXin Li
rowIndexPtr()124*bf2c3715SXin Li const StorageIndex* rowIndexPtr() const
125*bf2c3715SXin Li {
126*bf2c3715SXin Li return m_rowind_colptr;
127*bf2c3715SXin Li }
128*bf2c3715SXin Li
129*bf2c3715SXin Li /**
130*bf2c3715SXin Li * Return the array of column-to-supernode mapping
131*bf2c3715SXin Li */
colToSup()132*bf2c3715SXin Li StorageIndex* colToSup() { return m_col_to_sup; }
133*bf2c3715SXin Li
colToSup()134*bf2c3715SXin Li const StorageIndex* colToSup() const
135*bf2c3715SXin Li {
136*bf2c3715SXin Li return m_col_to_sup;
137*bf2c3715SXin Li }
138*bf2c3715SXin Li /**
139*bf2c3715SXin Li * Return the array of supernode-to-column mapping
140*bf2c3715SXin Li */
supToCol()141*bf2c3715SXin Li StorageIndex* supToCol() { return m_sup_to_col; }
142*bf2c3715SXin Li
supToCol()143*bf2c3715SXin Li const StorageIndex* supToCol() const
144*bf2c3715SXin Li {
145*bf2c3715SXin Li return m_sup_to_col;
146*bf2c3715SXin Li }
147*bf2c3715SXin Li
148*bf2c3715SXin Li /**
149*bf2c3715SXin Li * Return the number of supernodes
150*bf2c3715SXin Li */
nsuper()151*bf2c3715SXin Li Index nsuper() const
152*bf2c3715SXin Li {
153*bf2c3715SXin Li return m_nsuper;
154*bf2c3715SXin Li }
155*bf2c3715SXin Li
156*bf2c3715SXin Li class InnerIterator;
157*bf2c3715SXin Li template<typename Dest>
158*bf2c3715SXin Li void solveInPlace( MatrixBase<Dest>&X) const;
159*bf2c3715SXin Li template<bool Conjugate, typename Dest>
160*bf2c3715SXin Li void solveTransposedInPlace( MatrixBase<Dest>&X) const;
161*bf2c3715SXin Li
162*bf2c3715SXin Li
163*bf2c3715SXin Li
164*bf2c3715SXin Li
165*bf2c3715SXin Li
166*bf2c3715SXin Li protected:
167*bf2c3715SXin Li Index m_row; // Number of rows
168*bf2c3715SXin Li Index m_col; // Number of columns
169*bf2c3715SXin Li Index m_nsuper; // Number of supernodes
170*bf2c3715SXin Li Scalar* m_nzval; //array of nonzero values packed by column
171*bf2c3715SXin Li StorageIndex* m_nzval_colptr; //nzval_colptr[j] Stores the location in nzval[] which starts column j
172*bf2c3715SXin Li StorageIndex* m_rowind; // Array of compressed row indices of rectangular supernodes
173*bf2c3715SXin Li StorageIndex* m_rowind_colptr; //rowind_colptr[j] stores the location in rowind[] which starts column j
174*bf2c3715SXin Li StorageIndex* m_col_to_sup; // col_to_sup[j] is the supernode number to which column j belongs
175*bf2c3715SXin Li StorageIndex* m_sup_to_col; //sup_to_col[s] points to the starting column of the s-th supernode
176*bf2c3715SXin Li
177*bf2c3715SXin Li private :
178*bf2c3715SXin Li };
179*bf2c3715SXin Li
180*bf2c3715SXin Li /**
181*bf2c3715SXin Li * \brief InnerIterator class to iterate over nonzero values of the current column in the supernodal matrix L
182*bf2c3715SXin Li *
183*bf2c3715SXin Li */
184*bf2c3715SXin Li template<typename Scalar, typename StorageIndex>
185*bf2c3715SXin Li class MappedSuperNodalMatrix<Scalar,StorageIndex>::InnerIterator
186*bf2c3715SXin Li {
187*bf2c3715SXin Li public:
InnerIterator(const MappedSuperNodalMatrix & mat,Index outer)188*bf2c3715SXin Li InnerIterator(const MappedSuperNodalMatrix& mat, Index outer)
189*bf2c3715SXin Li : m_matrix(mat),
190*bf2c3715SXin Li m_outer(outer),
191*bf2c3715SXin Li m_supno(mat.colToSup()[outer]),
192*bf2c3715SXin Li m_idval(mat.colIndexPtr()[outer]),
193*bf2c3715SXin Li m_startidval(m_idval),
194*bf2c3715SXin Li m_endidval(mat.colIndexPtr()[outer+1]),
195*bf2c3715SXin Li m_idrow(mat.rowIndexPtr()[mat.supToCol()[mat.colToSup()[outer]]]),
196*bf2c3715SXin Li m_endidrow(mat.rowIndexPtr()[mat.supToCol()[mat.colToSup()[outer]]+1])
197*bf2c3715SXin Li {}
198*bf2c3715SXin Li inline InnerIterator& operator++()
199*bf2c3715SXin Li {
200*bf2c3715SXin Li m_idval++;
201*bf2c3715SXin Li m_idrow++;
202*bf2c3715SXin Li return *this;
203*bf2c3715SXin Li }
value()204*bf2c3715SXin Li inline Scalar value() const { return m_matrix.valuePtr()[m_idval]; }
205*bf2c3715SXin Li
valueRef()206*bf2c3715SXin Li inline Scalar& valueRef() { return const_cast<Scalar&>(m_matrix.valuePtr()[m_idval]); }
207*bf2c3715SXin Li
index()208*bf2c3715SXin Li inline Index index() const { return m_matrix.rowIndex()[m_idrow]; }
row()209*bf2c3715SXin Li inline Index row() const { return index(); }
col()210*bf2c3715SXin Li inline Index col() const { return m_outer; }
211*bf2c3715SXin Li
supIndex()212*bf2c3715SXin Li inline Index supIndex() const { return m_supno; }
213*bf2c3715SXin Li
214*bf2c3715SXin Li inline operator bool() const
215*bf2c3715SXin Li {
216*bf2c3715SXin Li return ( (m_idval < m_endidval) && (m_idval >= m_startidval)
217*bf2c3715SXin Li && (m_idrow < m_endidrow) );
218*bf2c3715SXin Li }
219*bf2c3715SXin Li
220*bf2c3715SXin Li protected:
221*bf2c3715SXin Li const MappedSuperNodalMatrix& m_matrix; // Supernodal lower triangular matrix
222*bf2c3715SXin Li const Index m_outer; // Current column
223*bf2c3715SXin Li const Index m_supno; // Current SuperNode number
224*bf2c3715SXin Li Index m_idval; // Index to browse the values in the current column
225*bf2c3715SXin Li const Index m_startidval; // Start of the column value
226*bf2c3715SXin Li const Index m_endidval; // End of the column value
227*bf2c3715SXin Li Index m_idrow; // Index to browse the row indices
228*bf2c3715SXin Li Index m_endidrow; // End index of row indices of the current column
229*bf2c3715SXin Li };
230*bf2c3715SXin Li
231*bf2c3715SXin Li /**
232*bf2c3715SXin Li * \brief Solve with the supernode triangular matrix
233*bf2c3715SXin Li *
234*bf2c3715SXin Li */
235*bf2c3715SXin Li template<typename Scalar, typename Index_>
236*bf2c3715SXin Li template<typename Dest>
solveInPlace(MatrixBase<Dest> & X)237*bf2c3715SXin Li void MappedSuperNodalMatrix<Scalar,Index_>::solveInPlace( MatrixBase<Dest>&X) const
238*bf2c3715SXin Li {
239*bf2c3715SXin Li /* Explicit type conversion as the Index type of MatrixBase<Dest> may be wider than Index */
240*bf2c3715SXin Li // eigen_assert(X.rows() <= NumTraits<Index>::highest());
241*bf2c3715SXin Li // eigen_assert(X.cols() <= NumTraits<Index>::highest());
242*bf2c3715SXin Li Index n = int(X.rows());
243*bf2c3715SXin Li Index nrhs = Index(X.cols());
244*bf2c3715SXin Li const Scalar * Lval = valuePtr(); // Nonzero values
245*bf2c3715SXin Li Matrix<Scalar,Dynamic,Dest::ColsAtCompileTime, ColMajor> work(n, nrhs); // working vector
246*bf2c3715SXin Li work.setZero();
247*bf2c3715SXin Li for (Index k = 0; k <= nsuper(); k ++)
248*bf2c3715SXin Li {
249*bf2c3715SXin Li Index fsupc = supToCol()[k]; // First column of the current supernode
250*bf2c3715SXin Li Index istart = rowIndexPtr()[fsupc]; // Pointer index to the subscript of the current column
251*bf2c3715SXin Li Index nsupr = rowIndexPtr()[fsupc+1] - istart; // Number of rows in the current supernode
252*bf2c3715SXin Li Index nsupc = supToCol()[k+1] - fsupc; // Number of columns in the current supernode
253*bf2c3715SXin Li Index nrow = nsupr - nsupc; // Number of rows in the non-diagonal part of the supernode
254*bf2c3715SXin Li Index irow; //Current index row
255*bf2c3715SXin Li
256*bf2c3715SXin Li if (nsupc == 1 )
257*bf2c3715SXin Li {
258*bf2c3715SXin Li for (Index j = 0; j < nrhs; j++)
259*bf2c3715SXin Li {
260*bf2c3715SXin Li InnerIterator it(*this, fsupc);
261*bf2c3715SXin Li ++it; // Skip the diagonal element
262*bf2c3715SXin Li for (; it; ++it)
263*bf2c3715SXin Li {
264*bf2c3715SXin Li irow = it.row();
265*bf2c3715SXin Li X(irow, j) -= X(fsupc, j) * it.value();
266*bf2c3715SXin Li }
267*bf2c3715SXin Li }
268*bf2c3715SXin Li }
269*bf2c3715SXin Li else
270*bf2c3715SXin Li {
271*bf2c3715SXin Li // The supernode has more than one column
272*bf2c3715SXin Li Index luptr = colIndexPtr()[fsupc];
273*bf2c3715SXin Li Index lda = colIndexPtr()[fsupc+1] - luptr;
274*bf2c3715SXin Li
275*bf2c3715SXin Li // Triangular solve
276*bf2c3715SXin Li Map<const Matrix<Scalar,Dynamic,Dynamic, ColMajor>, 0, OuterStride<> > A( &(Lval[luptr]), nsupc, nsupc, OuterStride<>(lda) );
277*bf2c3715SXin Li Map< Matrix<Scalar,Dynamic,Dest::ColsAtCompileTime, ColMajor>, 0, OuterStride<> > U (&(X(fsupc,0)), nsupc, nrhs, OuterStride<>(n) );
278*bf2c3715SXin Li U = A.template triangularView<UnitLower>().solve(U);
279*bf2c3715SXin Li
280*bf2c3715SXin Li // Matrix-vector product
281*bf2c3715SXin Li new (&A) Map<const Matrix<Scalar,Dynamic,Dynamic, ColMajor>, 0, OuterStride<> > ( &(Lval[luptr+nsupc]), nrow, nsupc, OuterStride<>(lda) );
282*bf2c3715SXin Li work.topRows(nrow).noalias() = A * U;
283*bf2c3715SXin Li
284*bf2c3715SXin Li //Begin Scatter
285*bf2c3715SXin Li for (Index j = 0; j < nrhs; j++)
286*bf2c3715SXin Li {
287*bf2c3715SXin Li Index iptr = istart + nsupc;
288*bf2c3715SXin Li for (Index i = 0; i < nrow; i++)
289*bf2c3715SXin Li {
290*bf2c3715SXin Li irow = rowIndex()[iptr];
291*bf2c3715SXin Li X(irow, j) -= work(i, j); // Scatter operation
292*bf2c3715SXin Li work(i, j) = Scalar(0);
293*bf2c3715SXin Li iptr++;
294*bf2c3715SXin Li }
295*bf2c3715SXin Li }
296*bf2c3715SXin Li }
297*bf2c3715SXin Li }
298*bf2c3715SXin Li }
299*bf2c3715SXin Li
300*bf2c3715SXin Li template<typename Scalar, typename Index_>
301*bf2c3715SXin Li template<bool Conjugate, typename Dest>
solveTransposedInPlace(MatrixBase<Dest> & X)302*bf2c3715SXin Li void MappedSuperNodalMatrix<Scalar,Index_>::solveTransposedInPlace( MatrixBase<Dest>&X) const
303*bf2c3715SXin Li {
304*bf2c3715SXin Li using numext::conj;
305*bf2c3715SXin Li Index n = int(X.rows());
306*bf2c3715SXin Li Index nrhs = Index(X.cols());
307*bf2c3715SXin Li const Scalar * Lval = valuePtr(); // Nonzero values
308*bf2c3715SXin Li Matrix<Scalar,Dynamic,Dest::ColsAtCompileTime, ColMajor> work(n, nrhs); // working vector
309*bf2c3715SXin Li work.setZero();
310*bf2c3715SXin Li for (Index k = nsuper(); k >= 0; k--)
311*bf2c3715SXin Li {
312*bf2c3715SXin Li Index fsupc = supToCol()[k]; // First column of the current supernode
313*bf2c3715SXin Li Index istart = rowIndexPtr()[fsupc]; // Pointer index to the subscript of the current column
314*bf2c3715SXin Li Index nsupr = rowIndexPtr()[fsupc+1] - istart; // Number of rows in the current supernode
315*bf2c3715SXin Li Index nsupc = supToCol()[k+1] - fsupc; // Number of columns in the current supernode
316*bf2c3715SXin Li Index nrow = nsupr - nsupc; // Number of rows in the non-diagonal part of the supernode
317*bf2c3715SXin Li Index irow; //Current index row
318*bf2c3715SXin Li
319*bf2c3715SXin Li if (nsupc == 1 )
320*bf2c3715SXin Li {
321*bf2c3715SXin Li for (Index j = 0; j < nrhs; j++)
322*bf2c3715SXin Li {
323*bf2c3715SXin Li InnerIterator it(*this, fsupc);
324*bf2c3715SXin Li ++it; // Skip the diagonal element
325*bf2c3715SXin Li for (; it; ++it)
326*bf2c3715SXin Li {
327*bf2c3715SXin Li irow = it.row();
328*bf2c3715SXin Li X(fsupc,j) -= X(irow, j) * (Conjugate?conj(it.value()):it.value());
329*bf2c3715SXin Li }
330*bf2c3715SXin Li }
331*bf2c3715SXin Li }
332*bf2c3715SXin Li else
333*bf2c3715SXin Li {
334*bf2c3715SXin Li // The supernode has more than one column
335*bf2c3715SXin Li Index luptr = colIndexPtr()[fsupc];
336*bf2c3715SXin Li Index lda = colIndexPtr()[fsupc+1] - luptr;
337*bf2c3715SXin Li
338*bf2c3715SXin Li //Begin Gather
339*bf2c3715SXin Li for (Index j = 0; j < nrhs; j++)
340*bf2c3715SXin Li {
341*bf2c3715SXin Li Index iptr = istart + nsupc;
342*bf2c3715SXin Li for (Index i = 0; i < nrow; i++)
343*bf2c3715SXin Li {
344*bf2c3715SXin Li irow = rowIndex()[iptr];
345*bf2c3715SXin Li work.topRows(nrow)(i,j)= X(irow,j); // Gather operation
346*bf2c3715SXin Li iptr++;
347*bf2c3715SXin Li }
348*bf2c3715SXin Li }
349*bf2c3715SXin Li
350*bf2c3715SXin Li // Matrix-vector product with transposed submatrix
351*bf2c3715SXin Li Map<const Matrix<Scalar,Dynamic,Dynamic, ColMajor>, 0, OuterStride<> > A( &(Lval[luptr+nsupc]), nrow, nsupc, OuterStride<>(lda) );
352*bf2c3715SXin Li Map< Matrix<Scalar,Dynamic,Dest::ColsAtCompileTime, ColMajor>, 0, OuterStride<> > U (&(X(fsupc,0)), nsupc, nrhs, OuterStride<>(n) );
353*bf2c3715SXin Li if(Conjugate)
354*bf2c3715SXin Li U = U - A.adjoint() * work.topRows(nrow);
355*bf2c3715SXin Li else
356*bf2c3715SXin Li U = U - A.transpose() * work.topRows(nrow);
357*bf2c3715SXin Li
358*bf2c3715SXin Li // Triangular solve (of transposed diagonal block)
359*bf2c3715SXin Li new (&A) Map<const Matrix<Scalar,Dynamic,Dynamic, ColMajor>, 0, OuterStride<> > ( &(Lval[luptr]), nsupc, nsupc, OuterStride<>(lda) );
360*bf2c3715SXin Li if(Conjugate)
361*bf2c3715SXin Li U = A.adjoint().template triangularView<UnitUpper>().solve(U);
362*bf2c3715SXin Li else
363*bf2c3715SXin Li U = A.transpose().template triangularView<UnitUpper>().solve(U);
364*bf2c3715SXin Li
365*bf2c3715SXin Li }
366*bf2c3715SXin Li
367*bf2c3715SXin Li }
368*bf2c3715SXin Li }
369*bf2c3715SXin Li
370*bf2c3715SXin Li
371*bf2c3715SXin Li } // end namespace internal
372*bf2c3715SXin Li
373*bf2c3715SXin Li } // end namespace Eigen
374*bf2c3715SXin Li
375*bf2c3715SXin Li #endif // EIGEN_SPARSELU_MATRIX_H
376