// This file is part of Eigen, a lightweight C++ template library // for linear algebra. // // Copyright (C) 2009 Gael Guennebaud <gael.guennebaud@inria.fr> // // 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/. #ifndef EIGEN_SPARSE_SELFADJOINTVIEW_H #define EIGEN_SPARSE_SELFADJOINTVIEW_H namespace Eigen { /** \ingroup SparseCore_Module * \class SparseSelfAdjointView * * \brief Pseudo expression to manipulate a triangular sparse matrix as a selfadjoint matrix. * * \param MatrixType the type of the dense matrix storing the coefficients * \param UpLo can be either \c #Lower or \c #Upper * * This class is an expression of a sefladjoint matrix from a triangular part of a matrix * with given dense storage of the coefficients. It is the return type of MatrixBase::selfadjointView() * and most of the time this is the only way that it is used. * * \sa SparseMatrixBase::selfadjointView() */ template<typename Lhs, typename Rhs, int UpLo> class SparseSelfAdjointTimeDenseProduct; template<typename Lhs, typename Rhs, int UpLo> class DenseTimeSparseSelfAdjointProduct; namespace internal { template<typename MatrixType, unsigned int UpLo> struct traits<SparseSelfAdjointView<MatrixType,UpLo> > : traits<MatrixType> { }; template<int SrcUpLo,int DstUpLo,typename MatrixType,int DestOrder> void permute_symm_to_symm(const MatrixType& mat, SparseMatrix<typename MatrixType::Scalar,DestOrder,typename MatrixType::Index>& _dest, const typename MatrixType::Index* perm = 0); template<int UpLo,typename MatrixType,int DestOrder> void permute_symm_to_fullsymm(const MatrixType& mat, SparseMatrix<typename MatrixType::Scalar,DestOrder,typename MatrixType::Index>& _dest, const typename MatrixType::Index* perm = 0); } template<typename MatrixType, unsigned int UpLo> class SparseSelfAdjointView : public EigenBase<SparseSelfAdjointView<MatrixType,UpLo> > { public: typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Index Index; typedef Matrix<Index,Dynamic,1> VectorI; typedef typename MatrixType::Nested MatrixTypeNested; typedef typename internal::remove_all<MatrixTypeNested>::type _MatrixTypeNested; inline SparseSelfAdjointView(const MatrixType& matrix) : m_matrix(matrix) { eigen_assert(rows()==cols() && "SelfAdjointView is only for squared matrices"); } inline Index rows() const { return m_matrix.rows(); } inline Index cols() const { return m_matrix.cols(); } /** \internal \returns a reference to the nested matrix */ const _MatrixTypeNested& matrix() const { return m_matrix; } _MatrixTypeNested& matrix() { return m_matrix.const_cast_derived(); } /** \returns an expression of the matrix product between a sparse self-adjoint matrix \c *this and a sparse matrix \a rhs. * * Note that there is no algorithmic advantage of performing such a product compared to a general sparse-sparse matrix product. * Indeed, the SparseSelfadjointView operand is first copied into a temporary SparseMatrix before computing the product. */ template<typename OtherDerived> SparseSparseProduct<typename OtherDerived::PlainObject, OtherDerived> operator*(const SparseMatrixBase<OtherDerived>& rhs) const { return SparseSparseProduct<typename OtherDerived::PlainObject, OtherDerived>(*this, rhs.derived()); } /** \returns an expression of the matrix product between a sparse matrix \a lhs and a sparse self-adjoint matrix \a rhs. * * Note that there is no algorithmic advantage of performing such a product compared to a general sparse-sparse matrix product. * Indeed, the SparseSelfadjointView operand is first copied into a temporary SparseMatrix before computing the product. */ template<typename OtherDerived> friend SparseSparseProduct<OtherDerived, typename OtherDerived::PlainObject > operator*(const SparseMatrixBase<OtherDerived>& lhs, const SparseSelfAdjointView& rhs) { return SparseSparseProduct<OtherDerived, typename OtherDerived::PlainObject>(lhs.derived(), rhs); } /** Efficient sparse self-adjoint matrix times dense vector/matrix product */ template<typename OtherDerived> SparseSelfAdjointTimeDenseProduct<MatrixType,OtherDerived,UpLo> operator*(const MatrixBase<OtherDerived>& rhs) const { return SparseSelfAdjointTimeDenseProduct<MatrixType,OtherDerived,UpLo>(m_matrix, rhs.derived()); } /** Efficient dense vector/matrix times sparse self-adjoint matrix product */ template<typename OtherDerived> friend DenseTimeSparseSelfAdjointProduct<OtherDerived,MatrixType,UpLo> operator*(const MatrixBase<OtherDerived>& lhs, const SparseSelfAdjointView& rhs) { return DenseTimeSparseSelfAdjointProduct<OtherDerived,_MatrixTypeNested,UpLo>(lhs.derived(), rhs.m_matrix); } /** Perform a symmetric rank K update of the selfadjoint matrix \c *this: * \f$ this = this + \alpha ( u u^* ) \f$ where \a u is a vector or matrix. * * \returns a reference to \c *this * * To perform \f$ this = this + \alpha ( u^* u ) \f$ you can simply * call this function with u.adjoint(). */ template<typename DerivedU> SparseSelfAdjointView& rankUpdate(const SparseMatrixBase<DerivedU>& u, const Scalar& alpha = Scalar(1)); /** \internal triggered by sparse_matrix = SparseSelfadjointView; */ template<typename DestScalar,int StorageOrder> void evalTo(SparseMatrix<DestScalar,StorageOrder,Index>& _dest) const { internal::permute_symm_to_fullsymm<UpLo>(m_matrix, _dest); } template<typename DestScalar> void evalTo(DynamicSparseMatrix<DestScalar,ColMajor,Index>& _dest) const { // TODO directly evaluate into _dest; SparseMatrix<DestScalar,ColMajor,Index> tmp(_dest.rows(),_dest.cols()); internal::permute_symm_to_fullsymm<UpLo>(m_matrix, tmp); _dest = tmp; } /** \returns an expression of P H P^-1 */ SparseSymmetricPermutationProduct<_MatrixTypeNested,UpLo> twistedBy(const PermutationMatrix<Dynamic,Dynamic,Index>& perm) const { return SparseSymmetricPermutationProduct<_MatrixTypeNested,UpLo>(m_matrix, perm); } template<typename SrcMatrixType,int SrcUpLo> SparseSelfAdjointView& operator=(const SparseSymmetricPermutationProduct<SrcMatrixType,SrcUpLo>& permutedMatrix) { permutedMatrix.evalTo(*this); return *this; } SparseSelfAdjointView& operator=(const SparseSelfAdjointView& src) { PermutationMatrix<Dynamic> pnull; return *this = src.twistedBy(pnull); } template<typename SrcMatrixType,unsigned int SrcUpLo> SparseSelfAdjointView& operator=(const SparseSelfAdjointView<SrcMatrixType,SrcUpLo>& src) { PermutationMatrix<Dynamic> pnull; return *this = src.twistedBy(pnull); } // const SparseLLT<PlainObject, UpLo> llt() const; // const SparseLDLT<PlainObject, UpLo> ldlt() const; protected: typename MatrixType::Nested m_matrix; mutable VectorI m_countPerRow; mutable VectorI m_countPerCol; }; /*************************************************************************** * Implementation of SparseMatrixBase methods ***************************************************************************/ template<typename Derived> template<unsigned int UpLo> const SparseSelfAdjointView<Derived, UpLo> SparseMatrixBase<Derived>::selfadjointView() const { return derived(); } template<typename Derived> template<unsigned int UpLo> SparseSelfAdjointView<Derived, UpLo> SparseMatrixBase<Derived>::selfadjointView() { return derived(); } /*************************************************************************** * Implementation of SparseSelfAdjointView methods ***************************************************************************/ template<typename MatrixType, unsigned int UpLo> template<typename DerivedU> SparseSelfAdjointView<MatrixType,UpLo>& SparseSelfAdjointView<MatrixType,UpLo>::rankUpdate(const SparseMatrixBase<DerivedU>& u, const Scalar& alpha) { SparseMatrix<Scalar,MatrixType::Flags&RowMajorBit?RowMajor:ColMajor> tmp = u * u.adjoint(); if(alpha==Scalar(0)) m_matrix.const_cast_derived() = tmp.template triangularView<UpLo>(); else m_matrix.const_cast_derived() += alpha * tmp.template triangularView<UpLo>(); return *this; } /*************************************************************************** * Implementation of sparse self-adjoint time dense matrix ***************************************************************************/ namespace internal { template<typename Lhs, typename Rhs, int UpLo> struct traits<SparseSelfAdjointTimeDenseProduct<Lhs,Rhs,UpLo> > : traits<ProductBase<SparseSelfAdjointTimeDenseProduct<Lhs,Rhs,UpLo>, Lhs, Rhs> > { typedef Dense StorageKind; }; } template<typename Lhs, typename Rhs, int UpLo> class SparseSelfAdjointTimeDenseProduct : public ProductBase<SparseSelfAdjointTimeDenseProduct<Lhs,Rhs,UpLo>, Lhs, Rhs> { public: EIGEN_PRODUCT_PUBLIC_INTERFACE(SparseSelfAdjointTimeDenseProduct) SparseSelfAdjointTimeDenseProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} template<typename Dest> void scaleAndAddTo(Dest& dest, const Scalar& alpha) const { EIGEN_ONLY_USED_FOR_DEBUG(alpha); // TODO use alpha eigen_assert(alpha==Scalar(1) && "alpha != 1 is not implemented yet, sorry"); typedef typename internal::remove_all<Lhs>::type _Lhs; typedef typename _Lhs::InnerIterator LhsInnerIterator; enum { LhsIsRowMajor = (_Lhs::Flags&RowMajorBit)==RowMajorBit, ProcessFirstHalf = ((UpLo&(Upper|Lower))==(Upper|Lower)) || ( (UpLo&Upper) && !LhsIsRowMajor) || ( (UpLo&Lower) && LhsIsRowMajor), ProcessSecondHalf = !ProcessFirstHalf }; for (Index j=0; j<m_lhs.outerSize(); ++j) { LhsInnerIterator i(m_lhs,j); if (ProcessSecondHalf) { while (i && i.index()<j) ++i; if(i && i.index()==j) { dest.row(j) += i.value() * m_rhs.row(j); ++i; } } for(; (ProcessFirstHalf ? i && i.index() < j : i) ; ++i) { Index a = LhsIsRowMajor ? j : i.index(); Index b = LhsIsRowMajor ? i.index() : j; typename Lhs::Scalar v = i.value(); dest.row(a) += (v) * m_rhs.row(b); dest.row(b) += numext::conj(v) * m_rhs.row(a); } if (ProcessFirstHalf && i && (i.index()==j)) dest.row(j) += i.value() * m_rhs.row(j); } } private: SparseSelfAdjointTimeDenseProduct& operator=(const SparseSelfAdjointTimeDenseProduct&); }; namespace internal { template<typename Lhs, typename Rhs, int UpLo> struct traits<DenseTimeSparseSelfAdjointProduct<Lhs,Rhs,UpLo> > : traits<ProductBase<DenseTimeSparseSelfAdjointProduct<Lhs,Rhs,UpLo>, Lhs, Rhs> > {}; } template<typename Lhs, typename Rhs, int UpLo> class DenseTimeSparseSelfAdjointProduct : public ProductBase<DenseTimeSparseSelfAdjointProduct<Lhs,Rhs,UpLo>, Lhs, Rhs> { public: EIGEN_PRODUCT_PUBLIC_INTERFACE(DenseTimeSparseSelfAdjointProduct) DenseTimeSparseSelfAdjointProduct(const Lhs& lhs, const Rhs& rhs) : Base(lhs,rhs) {} template<typename Dest> void scaleAndAddTo(Dest& /*dest*/, const Scalar& /*alpha*/) const { // TODO } private: DenseTimeSparseSelfAdjointProduct& operator=(const DenseTimeSparseSelfAdjointProduct&); }; /*************************************************************************** * Implementation of symmetric copies and permutations ***************************************************************************/ namespace internal { template<typename MatrixType, int UpLo> struct traits<SparseSymmetricPermutationProduct<MatrixType,UpLo> > : traits<MatrixType> { }; template<int UpLo,typename MatrixType,int DestOrder> void permute_symm_to_fullsymm(const MatrixType& mat, SparseMatrix<typename MatrixType::Scalar,DestOrder,typename MatrixType::Index>& _dest, const typename MatrixType::Index* perm) { typedef typename MatrixType::Index Index; typedef typename MatrixType::Scalar Scalar; typedef SparseMatrix<Scalar,DestOrder,Index> Dest; typedef Matrix<Index,Dynamic,1> VectorI; Dest& dest(_dest.derived()); enum { StorageOrderMatch = int(Dest::IsRowMajor) == int(MatrixType::IsRowMajor) }; Index size = mat.rows(); VectorI count; count.resize(size); count.setZero(); dest.resize(size,size); for(Index j = 0; j<size; ++j) { Index jp = perm ? perm[j] : j; for(typename MatrixType::InnerIterator it(mat,j); it; ++it) { Index i = it.index(); Index r = it.row(); Index c = it.col(); Index ip = perm ? perm[i] : i; if(UpLo==(Upper|Lower)) count[StorageOrderMatch ? jp : ip]++; else if(r==c) count[ip]++; else if(( UpLo==Lower && r>c) || ( UpLo==Upper && r<c)) { count[ip]++; count[jp]++; } } } Index nnz = count.sum(); // reserve space dest.resizeNonZeros(nnz); dest.outerIndexPtr()[0] = 0; for(Index j=0; j<size; ++j) dest.outerIndexPtr()[j+1] = dest.outerIndexPtr()[j] + count[j]; for(Index j=0; j<size; ++j) count[j] = dest.outerIndexPtr()[j]; // copy data for(Index j = 0; j<size; ++j) { for(typename MatrixType::InnerIterator it(mat,j); it; ++it) { Index i = it.index(); Index r = it.row(); Index c = it.col(); Index jp = perm ? perm[j] : j; Index ip = perm ? perm[i] : i; if(UpLo==(Upper|Lower)) { Index k = count[StorageOrderMatch ? jp : ip]++; dest.innerIndexPtr()[k] = StorageOrderMatch ? ip : jp; dest.valuePtr()[k] = it.value(); } else if(r==c) { Index k = count[ip]++; dest.innerIndexPtr()[k] = ip; dest.valuePtr()[k] = it.value(); } else if(( (UpLo&Lower)==Lower && r>c) || ( (UpLo&Upper)==Upper && r<c)) { if(!StorageOrderMatch) std::swap(ip,jp); Index k = count[jp]++; dest.innerIndexPtr()[k] = ip; dest.valuePtr()[k] = it.value(); k = count[ip]++; dest.innerIndexPtr()[k] = jp; dest.valuePtr()[k] = numext::conj(it.value()); } } } } template<int _SrcUpLo,int _DstUpLo,typename MatrixType,int DstOrder> void permute_symm_to_symm(const MatrixType& mat, SparseMatrix<typename MatrixType::Scalar,DstOrder,typename MatrixType::Index>& _dest, const typename MatrixType::Index* perm) { typedef typename MatrixType::Index Index; typedef typename MatrixType::Scalar Scalar; SparseMatrix<Scalar,DstOrder,Index>& dest(_dest.derived()); typedef Matrix<Index,Dynamic,1> VectorI; enum { SrcOrder = MatrixType::IsRowMajor ? RowMajor : ColMajor, StorageOrderMatch = int(SrcOrder) == int(DstOrder), DstUpLo = DstOrder==RowMajor ? (_DstUpLo==Upper ? Lower : Upper) : _DstUpLo, SrcUpLo = SrcOrder==RowMajor ? (_SrcUpLo==Upper ? Lower : Upper) : _SrcUpLo }; Index size = mat.rows(); VectorI count(size); count.setZero(); dest.resize(size,size); for(Index j = 0; j<size; ++j) { Index jp = perm ? perm[j] : j; for(typename MatrixType::InnerIterator it(mat,j); it; ++it) { Index i = it.index(); if((int(SrcUpLo)==int(Lower) && i<j) || (int(SrcUpLo)==int(Upper) && i>j)) continue; Index ip = perm ? perm[i] : i; count[int(DstUpLo)==int(Lower) ? (std::min)(ip,jp) : (std::max)(ip,jp)]++; } } dest.outerIndexPtr()[0] = 0; for(Index j=0; j<size; ++j) dest.outerIndexPtr()[j+1] = dest.outerIndexPtr()[j] + count[j]; dest.resizeNonZeros(dest.outerIndexPtr()[size]); for(Index j=0; j<size; ++j) count[j] = dest.outerIndexPtr()[j]; for(Index j = 0; j<size; ++j) { for(typename MatrixType::InnerIterator it(mat,j); it; ++it) { Index i = it.index(); if((int(SrcUpLo)==int(Lower) && i<j) || (int(SrcUpLo)==int(Upper) && i>j)) continue; Index jp = perm ? perm[j] : j; Index ip = perm? perm[i] : i; Index k = count[int(DstUpLo)==int(Lower) ? (std::min)(ip,jp) : (std::max)(ip,jp)]++; dest.innerIndexPtr()[k] = int(DstUpLo)==int(Lower) ? (std::max)(ip,jp) : (std::min)(ip,jp); if(!StorageOrderMatch) std::swap(ip,jp); if( ((int(DstUpLo)==int(Lower) && ip<jp) || (int(DstUpLo)==int(Upper) && ip>jp))) dest.valuePtr()[k] = numext::conj(it.value()); else dest.valuePtr()[k] = it.value(); } } } } template<typename MatrixType,int UpLo> class SparseSymmetricPermutationProduct : public EigenBase<SparseSymmetricPermutationProduct<MatrixType,UpLo> > { public: typedef typename MatrixType::Scalar Scalar; typedef typename MatrixType::Index Index; protected: typedef PermutationMatrix<Dynamic,Dynamic,Index> Perm; public: typedef Matrix<Index,Dynamic,1> VectorI; typedef typename MatrixType::Nested MatrixTypeNested; typedef typename internal::remove_all<MatrixTypeNested>::type _MatrixTypeNested; SparseSymmetricPermutationProduct(const MatrixType& mat, const Perm& perm) : m_matrix(mat), m_perm(perm) {} inline Index rows() const { return m_matrix.rows(); } inline Index cols() const { return m_matrix.cols(); } template<typename DestScalar, int Options, typename DstIndex> void evalTo(SparseMatrix<DestScalar,Options,DstIndex>& _dest) const { // internal::permute_symm_to_fullsymm<UpLo>(m_matrix,_dest,m_perm.indices().data()); SparseMatrix<DestScalar,(Options&RowMajor)==RowMajor ? ColMajor : RowMajor, DstIndex> tmp; internal::permute_symm_to_fullsymm<UpLo>(m_matrix,tmp,m_perm.indices().data()); _dest = tmp; } template<typename DestType,unsigned int DestUpLo> void evalTo(SparseSelfAdjointView<DestType,DestUpLo>& dest) const { internal::permute_symm_to_symm<UpLo,DestUpLo>(m_matrix,dest.matrix(),m_perm.indices().data()); } protected: MatrixTypeNested m_matrix; const Perm& m_perm; }; } // end namespace Eigen #endif // EIGEN_SPARSE_SELFADJOINTVIEW_H