diff options
-rw-r--r-- | Eigen/src/Sparse/SparseMatrix.h | 269 | ||||
-rw-r--r-- | Eigen/src/Sparse/SparseMatrixBase.h | 18 | ||||
-rw-r--r-- | test/sparse_basic.cpp | 22 |
3 files changed, 276 insertions, 33 deletions
diff --git a/Eigen/src/Sparse/SparseMatrix.h b/Eigen/src/Sparse/SparseMatrix.h index 0e175ec6e..61b4d6896 100644 --- a/Eigen/src/Sparse/SparseMatrix.h +++ b/Eigen/src/Sparse/SparseMatrix.h @@ -93,17 +93,28 @@ class SparseMatrix Index m_outerSize; Index m_innerSize; Index* m_outerIndex; + Index* m_innerNonZeros; // optional, if null then the data are compressed CompressedStorage<Scalar,Index> m_data; + + Eigen::Map<Matrix<Index,Dynamic,1> > innerNonZeros() { return Eigen::Map<Matrix<Index,Dynamic,1> >(m_innerNonZeros, m_innerNonZeros?m_outerSize:0); } + const Eigen::Map<const Matrix<Index,Dynamic,1> > innerNonZeros() const { return Eigen::Map<const Matrix<Index,Dynamic,1> >(m_innerNonZeros, m_innerNonZeros?m_outerSize:0); } public: + + inline bool compressed() const { return m_innerNonZeros==0; } inline Index rows() const { return IsRowMajor ? m_outerSize : m_innerSize; } inline Index cols() const { return IsRowMajor ? m_innerSize : m_outerSize; } inline Index innerSize() const { return m_innerSize; } inline Index outerSize() const { return m_outerSize; } - inline Index innerNonZeros(Index j) const { return m_outerIndex[j+1]-m_outerIndex[j]; } - + /** \returns the number of non zeros in the inner vector \a j + */ + inline Index innerNonZeros(Index j) const + { + return m_innerNonZeros ? m_innerNonZeros[j] : m_outerIndex[j+1]-m_outerIndex[j]; + } + inline const Scalar* _valuePtr() const { return &m_data.value(0); } inline Scalar* _valuePtr() { return &m_data.value(0); } @@ -120,7 +131,8 @@ class SparseMatrix { const Index outer = IsRowMajor ? row : col; const Index inner = IsRowMajor ? col : row; - return m_data.atInRange(m_outerIndex[outer], m_outerIndex[outer+1], inner); + Index end = m_innerNonZeros ? m_outerIndex[outer] + m_innerNonZeros[outer] : m_outerIndex[outer+1]; + return m_data.atInRange(m_outerIndex[outer], end, inner); } inline Scalar& coeffRef(Index row, Index col) @@ -129,7 +141,7 @@ class SparseMatrix const Index inner = IsRowMajor ? col : row; Index start = m_outerIndex[outer]; - Index end = m_outerIndex[outer+1]; + Index end = m_innerNonZeros ? m_outerIndex[outer] + m_innerNonZeros[outer] : m_outerIndex[outer+1]; eigen_assert(end>=start && "you probably called coeffRef on a non finalized matrix"); eigen_assert(end>start && "coeffRef cannot be called on a zero coefficient"); const Index p = m_data.searchLowerIndex(start,end-1,inner); @@ -141,21 +153,119 @@ class SparseMatrix class InnerIterator; - /** Removes all non zeros */ + /** Removes all non zeros but keep allocated memory */ inline void setZero() { m_data.clear(); memset(m_outerIndex, 0, (m_outerSize+1)*sizeof(Index)); + if(m_innerNonZeros) + memset(m_innerNonZeros, 0, (m_outerSize)*sizeof(Index)); } /** \returns the number of non zero coefficients */ - inline Index nonZeros() const { return static_cast<Index>(m_data.size()); } + inline Index nonZeros() const + { + if(m_innerNonZeros) + return innerNonZeros().sum(); + return static_cast<Index>(m_data.size()); + } - /** Preallocates \a reserveSize non zeros */ + /** Preallocates \a reserveSize non zeros. + * + * Precondition: the matrix must be in compressed mode. */ inline void reserve(Index reserveSize) { + eigen_assert(compressed() && "This function does not make sense in non compressed mode."); m_data.reserve(reserveSize); } + + /** Preallocates \a reserveSize non zeros. + * + * Precondition: the matrix must be in compressed mode. */ + template<class SizesType> + inline void reserve(const SizesType& reserveSizes, const typename SizesType::value_type& enableif = typename SizesType::value_type()) + { + EIGEN_UNUSED_VARIABLE(enableif); + + if(compressed()) + { + std::size_t totalReserveSize = 0; +// std::cerr << "reserve from compressed format\n"; + // turn the matrix into non-compressed mode + m_innerNonZeros = new Index[m_outerSize]; + + // temporarily use m_innerSizes to hold the new starting points. + Index* newOuterIndex = m_innerNonZeros; + + Index count = 0; + for(Index j=0; j<m_outerSize; ++j) + { + newOuterIndex[j] = count; + count += reserveSizes[j] + (m_outerIndex[j+1]-m_outerIndex[j]); + totalReserveSize += reserveSizes[j]; + } +// std::cerr << "data.r " << totalReserveSize << "\n"; + m_data.reserve(totalReserveSize); +// std::cerr << "data.r OK\n"; + std::ptrdiff_t previousOuterIndex = m_outerIndex[m_outerSize]; + for(std::ptrdiff_t j=m_outerSize-1; j>=0; --j) + { + ptrdiff_t innerNNZ = previousOuterIndex - m_outerIndex[j]; +// std::cerr << j << " innerNNZ=" << innerNNZ << "\n"; + for(std::ptrdiff_t i=innerNNZ-1; i>=0; --i) + { +// std::cerr << " " << i << " " << newOuterIndex[j]+i << "\n"; + m_data.index(newOuterIndex[j]+i) = m_data.index(m_outerIndex[j]+i); + m_data.value(newOuterIndex[j]+i) = m_data.value(m_outerIndex[j]+i); + } + previousOuterIndex = m_outerIndex[j]; + m_outerIndex[j] = newOuterIndex[j]; + m_innerNonZeros[j] = innerNNZ; + } +// std::cerr << "OK" << "\n"; + m_outerIndex[m_outerSize] = m_outerIndex[m_outerSize-1] + m_innerNonZeros[m_outerSize-1] + reserveSizes[m_outerSize-1]; + + m_data.resize(m_outerIndex[m_outerSize]); + +// std::cout << Matrix<Index,1,Dynamic>::Map(m_outerIndex, m_outerSize+1) << "\n"; +// std::cout << Matrix<Index,1,Dynamic>::Map(m_innerNonZeros, m_outerSize) << "\n"; + } + else + { +// std::cerr << "reserve from uncompressed format\n"; + Index* newOuterIndex = new Index[m_outerSize+1]; + Index count = 0; + for(Index j=0; j<m_outerSize; ++j) + { + newOuterIndex[j] = count; + Index alreadyReserved = (m_outerIndex[j+1]-m_outerIndex[j]) - m_innerNonZeros[j]; + Index toReserve = std::max<std::ptrdiff_t>(reserveSizes[j], alreadyReserved); + count += toReserve + m_innerNonZeros[j]; + } + newOuterIndex[m_outerSize] = count; + + m_data.resize(count); + for(ptrdiff_t j=m_outerSize-1; j>=0; --j) + { + std::ptrdiff_t offset = newOuterIndex[j] - m_outerIndex[j]; + if(offset>0) + { +// std::cout << "offset=" << offset << " m_data.size()=" << m_data.size() << "\n"; + std::ptrdiff_t innerNNZ = m_innerNonZeros[j]; + for(std::ptrdiff_t i=innerNNZ-1; i>=0; --i) + { +// std::cout << newOuterIndex[j]+i << " <- " << m_outerIndex[j]+i << "\n"; + m_data.index(newOuterIndex[j]+i) = m_data.index(m_outerIndex[j]+i); + m_data.value(newOuterIndex[j]+i) = m_data.value(m_outerIndex[j]+i); + } + } + } + + std::swap(m_outerIndex, newOuterIndex); + delete[] newOuterIndex; + } + + } //--- low level purely coherent filling --- @@ -213,6 +323,16 @@ class SparseMatrix */ EIGEN_DONT_INLINE Scalar& insert(Index row, Index col) { + if(compressed()) + return insertCompressed(row,col); + else + return insertUncompressed(row,col); + } + + EIGEN_DONT_INLINE Scalar& insertCompressed(Index row, Index col) + { + eigen_assert(compressed()); + const Index outer = IsRowMajor ? row : col; const Index inner = IsRowMajor ? col : row; @@ -314,28 +434,108 @@ class SparseMatrix m_data.index(p) = inner; return (m_data.value(p) = 0); } + + class SingletonVector + { + Index m_index; + Index m_value; + public: + typedef Index value_type; + SingletonVector(Index i, Index v) + : m_index(i), m_value(v) + {} + + Index operator[](Index i) const { return i==m_index ? m_value : 0; } + }; + EIGEN_DONT_INLINE Scalar& insertUncompressed(Index row, Index col) + { + eigen_assert(!compressed()); + + const Index outer = IsRowMajor ? row : col; + const Index inner = IsRowMajor ? col : row; + + std::ptrdiff_t room = m_outerIndex[outer+1] - m_outerIndex[outer]; + std::ptrdiff_t innerNNZ = m_innerNonZeros[outer]; + if(innerNNZ>=room) + { + // this inner vector is full, we need to reallocate the whole buffer :( + reserve(SingletonVector(outer,std::max<std::ptrdiff_t>(2,innerNNZ))); + } + + Index startId = m_outerIndex[outer]; + Index p = startId + m_innerNonZeros[outer]; + while ( (p > startId) && (m_data.index(p-1) > inner) ) + { + m_data.index(p) = m_data.index(p-1); + m_data.value(p) = m_data.value(p-1); + --p; + } + + m_innerNonZeros[outer]++; + m_data.index(p) = inner; + return (m_data.value(p) = 0); + } /** Must be called after inserting a set of non zero entries. */ inline void finalize() { - Index size = static_cast<Index>(m_data.size()); - Index i = m_outerSize; - // find the last filled column - while (i>=0 && m_outerIndex[i]==0) - --i; - ++i; - while (i<=m_outerSize) + if(compressed()) { - m_outerIndex[i] = size; + Index size = static_cast<Index>(m_data.size()); + Index i = m_outerSize; + // find the last filled column + while (i>=0 && m_outerIndex[i]==0) + --i; ++i; + while (i<=m_outerSize) + { + m_outerIndex[i] = size; + ++i; + } + } + } + + void makeCompressed() + { + if(compressed()) + return; + +// std::cout << Matrix<Index,1,Dynamic>::Map(m_outerIndex, m_outerSize+1) << "\n"; +// std::cout << Matrix<Index,1,Dynamic>::Map(m_innerNonZeros, m_outerSize) << "\n"; +// std::cout << Matrix<Index,1,Dynamic>::Map(&m_data.index(0), nonZeros()) << "\n"; +// std::cout << Matrix<Scalar,1,Dynamic>::Map(&m_data.value(0), nonZeros()) << "\n"; + + Index oldStart = m_outerIndex[1]; + m_outerIndex[1] = m_innerNonZeros[0]; + for(Index j=1; j<m_outerSize; ++j) + { + Index nextOldStart = m_outerIndex[j+1]; + std::ptrdiff_t offset = oldStart - m_outerIndex[j]; + if(offset>0) + { + for(Index k=0; k<m_innerNonZeros[j]; ++k) + { + m_data.index(m_outerIndex[j]+k) = m_data.index(oldStart+k); + m_data.value(m_outerIndex[j]+k) = m_data.value(oldStart+k); + } + } + m_outerIndex[j+1] = m_outerIndex[j] + m_innerNonZeros[j]; + oldStart = nextOldStart; } + delete[] m_innerNonZeros; + m_innerNonZeros = 0; + m_data.resize(m_outerIndex[m_outerSize]); + m_data.squeeze(); +// std::cout << Matrix<Index,1,Dynamic>::Map(m_outerIndex, m_outerSize+1) << "\n"; +// std::cout << Matrix<Index,1,Dynamic>::Map(&m_data.index(0), nonZeros()) << "\n"; +// std::cout << Matrix<Scalar,1,Dynamic>::Map(&m_data.value(0), nonZeros()) << "\n"; } - /** Suppress all nonzeros which are smaller than \a reference under the tolerence \a epsilon */ + /** Suppress all nonzeros which are \b much \b smaller \b than \a reference under the tolerence \a epsilon */ void prune(Scalar reference, RealScalar epsilon = NumTraits<RealScalar>::dummy_precision()) { prune(default_prunning_func(reference,epsilon)); @@ -385,26 +585,33 @@ class SparseMatrix m_outerIndex = new Index [outerSize+1]; m_outerSize = outerSize; } + if(m_innerNonZeros) + { + delete[] m_innerNonZeros; + m_innerNonZeros = 0; + } memset(m_outerIndex, 0, (m_outerSize+1)*sizeof(Index)); } /** Low level API - * Resize the nonzero vector to \a size */ + * Resize the nonzero vector to \a size + * \deprecated */ void resizeNonZeros(Index size) { + // TODO remove this function m_data.resize(size); } /** Default constructor yielding an empty \c 0 \c x \c 0 matrix */ inline SparseMatrix() - : m_outerSize(-1), m_innerSize(0), m_outerIndex(0) + : m_outerSize(-1), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) { resize(0, 0); } /** Constructs a \a rows \c x \a cols empty matrix */ inline SparseMatrix(Index rows, Index cols) - : m_outerSize(0), m_innerSize(0), m_outerIndex(0) + : m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) { resize(rows, cols); } @@ -412,14 +619,14 @@ class SparseMatrix /** Constructs a sparse matrix from the sparse expression \a other */ template<typename OtherDerived> inline SparseMatrix(const SparseMatrixBase<OtherDerived>& other) - : m_outerSize(0), m_innerSize(0), m_outerIndex(0) + : m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) { *this = other.derived(); } /** Copy constructor */ inline SparseMatrix(const SparseMatrix& other) - : Base(), m_outerSize(0), m_innerSize(0), m_outerIndex(0) + : Base(), m_outerSize(0), m_innerSize(0), m_outerIndex(0), m_innerNonZeros(0) { *this = other.derived(); } @@ -431,6 +638,7 @@ class SparseMatrix std::swap(m_outerIndex, other.m_outerIndex); std::swap(m_innerSize, other.m_innerSize); std::swap(m_outerSize, other.m_outerSize); + std::swap(m_innerNonZeros, other.m_innerNonZeros); m_data.swap(other.m_data); } @@ -444,8 +652,20 @@ class SparseMatrix else { resize(other.rows(), other.cols()); - memcpy(m_outerIndex, other.m_outerIndex, (m_outerSize+1)*sizeof(Index)); - m_data = other.m_data; + if(m_innerNonZeros) + { + delete[] m_innerNonZeros; + m_innerNonZeros = 0; + } + if(other.compressed()) + { + memcpy(m_outerIndex, other.m_outerIndex, (m_outerSize+1)*sizeof(Index)); + m_data = other.m_data; + } + else + { + Base::operator=(other); + } } return *this; } @@ -625,7 +845,8 @@ class SparseMatrix<Scalar,_Options,_Index>::InnerIterator { public: InnerIterator(const SparseMatrix& mat, Index outer) - : m_values(mat._valuePtr()), m_indices(mat._innerIndexPtr()), m_outer(outer), m_id(mat.m_outerIndex[outer]), m_end(mat.m_outerIndex[outer+1]) + : m_values(mat._valuePtr()), m_indices(mat._innerIndexPtr()), m_outer(outer), m_id(mat.m_outerIndex[outer]), + m_end(mat.m_outerIndex[outer+1]) {} inline InnerIterator& operator++() { m_id++; return *this; } diff --git a/Eigen/src/Sparse/SparseMatrixBase.h b/Eigen/src/Sparse/SparseMatrixBase.h index c01981bc9..74506ad71 100644 --- a/Eigen/src/Sparse/SparseMatrixBase.h +++ b/Eigen/src/Sparse/SparseMatrixBase.h @@ -189,15 +189,15 @@ template<typename Derived> class SparseMatrixBase : public EigenBase<Derived> SparseMatrixBase() : m_isRValue(false) { /* TODO check flags */ } - inline Derived& operator=(const Derived& other) - { -// std::cout << "Derived& operator=(const Derived& other)\n"; -// if (other.isRValue()) -// derived().swap(other.const_cast_derived()); -// else - this->operator=<Derived>(other); - return derived(); - } +// inline Derived& operator=(const Derived& other) +// { +// // std::cout << "Derived& operator=(const Derived& other)\n"; +// // if (other.isRValue()) +// // derived().swap(other.const_cast_derived()); +// // else +// this->operator=<Derived>(other); +// return derived(); +// } template<typename OtherDerived> Derived& operator=(const ReturnByValue<OtherDerived>& other) diff --git a/test/sparse_basic.cpp b/test/sparse_basic.cpp index a6c148591..d4579e4c9 100644 --- a/test/sparse_basic.cpp +++ b/test/sparse_basic.cpp @@ -140,6 +140,28 @@ template<typename SparseMatrixType> void sparse_basic(const SparseMatrixType& re m2.finalize(); VERIFY_IS_APPROX(m2,m1); } + + // test insert (un-compressed) + for(int mode=0;mode<4;++mode) + { + DenseMatrix m1(rows,cols); + m1.setZero(); + SparseMatrixType m2(rows,cols); + VectorXi r(VectorXi::Constant(m2.outerSize(), ((mode%2)==0) ? m2.innerSize() : std::max<int>(1,m2.innerSize()/8))); + m2.reserve(r); + for (int k=0; k<rows*cols; ++k) + { + int i = internal::random<int>(0,rows-1); + int j = internal::random<int>(0,cols-1); + if (m1.coeff(i,j)==Scalar(0)) + m2.insert(i,j) = m1(i,j) = internal::random<Scalar>(); + if(mode==3) + m2.reserve(r); + } + m2.finalize(); + m2.makeCompressed(); + VERIFY_IS_APPROX(m2,m1); + } // test basic computations { |