|  | // This file is part of Eigen, a lightweight C++ template library | 
|  | // for linear algebra. | 
|  | // | 
|  | // Copyright (C) 2008 Gael Guennebaud <g.gael@free.fr> | 
|  | // | 
|  | // Eigen is free software; you can redistribute it and/or | 
|  | // modify it under the terms of the GNU Lesser General Public | 
|  | // License as published by the Free Software Foundation; either | 
|  | // version 3 of the License, or (at your option) any later version. | 
|  | // | 
|  | // Alternatively, you can redistribute it and/or | 
|  | // modify it under the terms of the GNU General Public License as | 
|  | // published by the Free Software Foundation; either version 2 of | 
|  | // the License, or (at your option) any later version. | 
|  | // | 
|  | // Eigen is distributed in the hope that it will be useful, but WITHOUT ANY | 
|  | // WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS | 
|  | // FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License or the | 
|  | // GNU General Public License for more details. | 
|  | // | 
|  | // You should have received a copy of the GNU Lesser General Public | 
|  | // License and a copy of the GNU General Public License along with | 
|  | // Eigen. If not, see <http://www.gnu.org/licenses/>. | 
|  |  | 
|  | #ifndef EIGEN_RANDOMSETTER_H | 
|  | #define EIGEN_RANDOMSETTER_H | 
|  |  | 
|  | /** Represents a std::map | 
|  | * | 
|  | * \see RandomSetter | 
|  | */ | 
|  | template<typename Scalar> struct StdMapTraits | 
|  | { | 
|  | typedef int KeyType; | 
|  | typedef std::map<KeyType,Scalar> Type; | 
|  | enum { | 
|  | IsSorted = 1 | 
|  | }; | 
|  |  | 
|  | static void setInvalidKey(Type&, const KeyType&) {} | 
|  | }; | 
|  |  | 
|  | #ifdef EIGEN_UNORDERED_MAP_SUPPORT | 
|  | /** Represents a std::unordered_map | 
|  | * | 
|  | * To use it you need to both define EIGEN_UNORDERED_MAP_SUPPORT and include the unordered_map header file | 
|  | * yourself making sure that unordered_map is defined in the std namespace. | 
|  | * | 
|  | * For instance, with current version of gcc you can either enable C++0x standard (-std=c++0x) or do: | 
|  | * \code | 
|  | * #include <tr1/unordered_map> | 
|  | * #define EIGEN_UNORDERED_MAP_SUPPORT | 
|  | * namespace std { | 
|  | *   using std::tr1::unordered_map; | 
|  | * } | 
|  | * \endcode | 
|  | * | 
|  | * \see RandomSetter | 
|  | */ | 
|  | template<typename Scalar> struct StdUnorderedMapTraits | 
|  | { | 
|  | typedef int KeyType; | 
|  | typedef std::unordered_map<KeyType,Scalar> Type; | 
|  | enum { | 
|  | IsSorted = 0 | 
|  | }; | 
|  |  | 
|  | static void setInvalidKey(Type&, const KeyType&) {} | 
|  | }; | 
|  | #endif // EIGEN_UNORDERED_MAP_SUPPORT | 
|  |  | 
|  | #ifdef _DENSE_HASH_MAP_H_ | 
|  | /** Represents a google::dense_hash_map | 
|  | * | 
|  | * \see RandomSetter | 
|  | */ | 
|  | template<typename Scalar> struct GoogleDenseHashMapTraits | 
|  | { | 
|  | typedef int KeyType; | 
|  | typedef google::dense_hash_map<KeyType,Scalar> Type; | 
|  | enum { | 
|  | IsSorted = 0 | 
|  | }; | 
|  |  | 
|  | static void setInvalidKey(Type& map, const KeyType& k) | 
|  | { map.set_empty_key(k); } | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | #ifdef _SPARSE_HASH_MAP_H_ | 
|  | /** Represents a google::sparse_hash_map | 
|  | * | 
|  | * \see RandomSetter | 
|  | */ | 
|  | template<typename Scalar> struct GoogleSparseHashMapTraits | 
|  | { | 
|  | typedef int KeyType; | 
|  | typedef google::sparse_hash_map<KeyType,Scalar> Type; | 
|  | enum { | 
|  | IsSorted = 0 | 
|  | }; | 
|  |  | 
|  | static void setInvalidKey(Type&, const KeyType&) {} | 
|  | }; | 
|  | #endif | 
|  |  | 
|  | /** \class RandomSetter | 
|  | * | 
|  | * \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access | 
|  | * | 
|  | * \param SparseMatrixType the type of the sparse matrix we are updating | 
|  | * \param MapTraits a traits class representing the map implementation used for the temporary sparse storage. | 
|  | *                  Its default value depends on the system. | 
|  | * \param OuterPacketBits defines the number of rows (or columns) manage by a single map object | 
|  | *                        as a power of two exponent. | 
|  | * | 
|  | * This class temporarily represents a sparse matrix object using a generic map implementation allowing for | 
|  | * efficient random access. The conversion from the compressed representation to a hash_map object is performed | 
|  | * in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy | 
|  | * suggest the use of nested blocks as in this example: | 
|  | * | 
|  | * \code | 
|  | * SparseMatrix<double> m(rows,cols); | 
|  | * { | 
|  | *   RandomSetter<SparseMatrix<double> > w(m); | 
|  | *   // don't use m but w instead with read/write random access to the coefficients: | 
|  | *   for(;;) | 
|  | *     w(rand(),rand()) = rand; | 
|  | * } | 
|  | * // when w is deleted, the data are copied back to m | 
|  | * // and m is ready to use. | 
|  | * \endcode | 
|  | * | 
|  | * Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would | 
|  | * involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter | 
|  | * use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order. | 
|  | * To reach optimal performance, this value should be adjusted according to the average number of nonzeros | 
|  | * per rows/columns. | 
|  | * | 
|  | * The possible values for the template parameter MapTraits are: | 
|  | *  - \b StdMapTraits: corresponds to std::map. (does not perform very well) | 
|  | *  - \b GnuHashMapTraits: corresponds to __gnu_cxx::hash_map (available only with GCC) | 
|  | *  - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption) | 
|  | *  - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance) | 
|  | * | 
|  | * The default map implementation depends on the availability, and the preferred order is: | 
|  | * GoogleSparseHashMapTraits, GnuHashMapTraits, and finally StdMapTraits. | 
|  | * | 
|  | * For performance and memory consumption reasons it is highly recommended to use one of | 
|  | * the Google's hash_map implementation. To enable the support for them, you have two options: | 
|  | *  - \#include <google/dense_hash_map> yourself \b before Eigen/Sparse header | 
|  | *  - define EIGEN_GOOGLEHASH_SUPPORT | 
|  | * In the later case the inclusion of <google/dense_hash_map> is made for you. | 
|  | * | 
|  | * \see http://code.google.com/p/google-sparsehash/ | 
|  | */ | 
|  | template<typename SparseMatrixType, | 
|  | template <typename T> class MapTraits = | 
|  | #if defined _DENSE_HASH_MAP_H_ | 
|  | GoogleDenseHashMapTraits | 
|  | #elif defined _HASH_MAP | 
|  | GnuHashMapTraits | 
|  | #else | 
|  | StdMapTraits | 
|  | #endif | 
|  | ,int OuterPacketBits = 6> | 
|  | class RandomSetter | 
|  | { | 
|  | typedef typename SparseMatrixType::Scalar Scalar; | 
|  | typedef typename SparseMatrixType::Index Index; | 
|  |  | 
|  | struct ScalarWrapper | 
|  | { | 
|  | ScalarWrapper() : value(0) {} | 
|  | Scalar value; | 
|  | }; | 
|  | typedef typename MapTraits<ScalarWrapper>::KeyType KeyType; | 
|  | typedef typename MapTraits<ScalarWrapper>::Type HashMapType; | 
|  | static const int OuterPacketMask = (1 << OuterPacketBits) - 1; | 
|  | enum { | 
|  | SwapStorage = 1 - MapTraits<ScalarWrapper>::IsSorted, | 
|  | TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0, | 
|  | SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor, | 
|  | IsUpper = SparseMatrixType::Flags & Upper, | 
|  | IsLower = SparseMatrixType::Flags & Lower | 
|  | }; | 
|  |  | 
|  | public: | 
|  |  | 
|  | /** Constructs a random setter object from the sparse matrix \a target | 
|  | * | 
|  | * Note that the initial value of \a target are imported. If you want to re-set | 
|  | * a sparse matrix from scratch, then you must set it to zero first using the | 
|  | * setZero() function. | 
|  | */ | 
|  | inline RandomSetter(SparseMatrixType& target) | 
|  | : mp_target(&target) | 
|  | { | 
|  | const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize(); | 
|  | const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize(); | 
|  | m_outerPackets = outerSize >> OuterPacketBits; | 
|  | if (outerSize&OuterPacketMask) | 
|  | m_outerPackets += 1; | 
|  | m_hashmaps = new HashMapType[m_outerPackets]; | 
|  | // compute number of bits needed to store inner indices | 
|  | Index aux = innerSize - 1; | 
|  | m_keyBitsOffset = 0; | 
|  | while (aux) | 
|  | { | 
|  | ++m_keyBitsOffset; | 
|  | aux = aux >> 1; | 
|  | } | 
|  | KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset)); | 
|  | for (Index k=0; k<m_outerPackets; ++k) | 
|  | MapTraits<ScalarWrapper>::setInvalidKey(m_hashmaps[k],ik); | 
|  |  | 
|  | // insert current coeffs | 
|  | for (Index j=0; j<mp_target->outerSize(); ++j) | 
|  | for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it) | 
|  | (*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value(); | 
|  | } | 
|  |  | 
|  | /** Destructor updating back the sparse matrix target */ | 
|  | ~RandomSetter() | 
|  | { | 
|  | KeyType keyBitsMask = (1<<m_keyBitsOffset)-1; | 
|  | if (!SwapStorage) // also means the map is sorted | 
|  | { | 
|  | mp_target->setZero(); | 
|  | mp_target->reserve(nonZeros()); | 
|  | Index prevOuter = -1; | 
|  | for (Index k=0; k<m_outerPackets; ++k) | 
|  | { | 
|  | const Index outerOffset = (1<<OuterPacketBits) * k; | 
|  | typename HashMapType::iterator end = m_hashmaps[k].end(); | 
|  | for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it) | 
|  | { | 
|  | const Index outer = (it->first >> m_keyBitsOffset) + outerOffset; | 
|  | const Index inner = it->first & keyBitsMask; | 
|  | if (prevOuter!=outer) | 
|  | { | 
|  | for (Index j=prevOuter+1;j<=outer;++j) | 
|  | mp_target->startVec(j); | 
|  | prevOuter = outer; | 
|  | } | 
|  | mp_target->insertBackByOuterInner(outer, inner) = it->second.value; | 
|  | } | 
|  | } | 
|  | mp_target->finalize(); | 
|  | } | 
|  | else | 
|  | { | 
|  | VectorXi positions(mp_target->outerSize()); | 
|  | positions.setZero(); | 
|  | // pass 1 | 
|  | for (Index k=0; k<m_outerPackets; ++k) | 
|  | { | 
|  | typename HashMapType::iterator end = m_hashmaps[k].end(); | 
|  | for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it) | 
|  | { | 
|  | const Index outer = it->first & keyBitsMask; | 
|  | ++positions[outer]; | 
|  | } | 
|  | } | 
|  | // prefix sum | 
|  | Index count = 0; | 
|  | for (Index j=0; j<mp_target->outerSize(); ++j) | 
|  | { | 
|  | Index tmp = positions[j]; | 
|  | mp_target->_outerIndexPtr()[j] = count; | 
|  | positions[j] = count; | 
|  | count += tmp; | 
|  | } | 
|  | mp_target->_outerIndexPtr()[mp_target->outerSize()] = count; | 
|  | mp_target->resizeNonZeros(count); | 
|  | // pass 2 | 
|  | for (Index k=0; k<m_outerPackets; ++k) | 
|  | { | 
|  | const Index outerOffset = (1<<OuterPacketBits) * k; | 
|  | typename HashMapType::iterator end = m_hashmaps[k].end(); | 
|  | for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it) | 
|  | { | 
|  | const Index inner = (it->first >> m_keyBitsOffset) + outerOffset; | 
|  | const Index outer = it->first & keyBitsMask; | 
|  | // sorted insertion | 
|  | // Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients, | 
|  | // moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a | 
|  | // small fraction of them have to be sorted, whence the following simple procedure: | 
|  | Index posStart = mp_target->_outerIndexPtr()[outer]; | 
|  | Index i = (positions[outer]++) - 1; | 
|  | while ( (i >= posStart) && (mp_target->_innerIndexPtr()[i] > inner) ) | 
|  | { | 
|  | mp_target->_valuePtr()[i+1] = mp_target->_valuePtr()[i]; | 
|  | mp_target->_innerIndexPtr()[i+1] = mp_target->_innerIndexPtr()[i]; | 
|  | --i; | 
|  | } | 
|  | mp_target->_innerIndexPtr()[i+1] = inner; | 
|  | mp_target->_valuePtr()[i+1] = it->second.value; | 
|  | } | 
|  | } | 
|  | } | 
|  | delete[] m_hashmaps; | 
|  | } | 
|  |  | 
|  | /** \returns a reference to the coefficient at given coordinates \a row, \a col */ | 
|  | Scalar& operator() (Index row, Index col) | 
|  | { | 
|  | ei_assert(((!IsUpper) || (row<=col)) && "Invalid access to an upper triangular matrix"); | 
|  | ei_assert(((!IsLower) || (col<=row)) && "Invalid access to an upper triangular matrix"); | 
|  | const Index outer = SetterRowMajor ? row : col; | 
|  | const Index inner = SetterRowMajor ? col : row; | 
|  | const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map | 
|  | const Index outerMinor = outer & OuterPacketMask;  // index of the inner vector in the packet | 
|  | const KeyType key = (KeyType(outerMinor)<<m_keyBitsOffset) | inner; | 
|  | return m_hashmaps[outerMajor][key].value; | 
|  | } | 
|  |  | 
|  | /** \returns the number of non zero coefficients | 
|  | * | 
|  | * \note According to the underlying map/hash_map implementation, | 
|  | * this function might be quite expensive. | 
|  | */ | 
|  | Index nonZeros() const | 
|  | { | 
|  | Index nz = 0; | 
|  | for (Index k=0; k<m_outerPackets; ++k) | 
|  | nz += static_cast<Index>(m_hashmaps[k].size()); | 
|  | return nz; | 
|  | } | 
|  |  | 
|  |  | 
|  | protected: | 
|  |  | 
|  | HashMapType* m_hashmaps; | 
|  | SparseMatrixType* mp_target; | 
|  | Index m_outerPackets; | 
|  | unsigned char m_keyBitsOffset; | 
|  | }; | 
|  |  | 
|  | #endif // EIGEN_RANDOMSETTER_H |