source: pacpussensors/trunk/Vislab/lib3dv/eigen/unsupported/Eigen/src/SparseExtra/RandomSetter.h@ 136

Last change on this file since 136 was 136, checked in by ldecherf, 7 years ago

Doc

File size: 11.5 KB
Line 
1// This file is part of Eigen, a lightweight C++ template library
2// for linear algebra.
3//
4// Copyright (C) 2008 Gael Guennebaud <gael.guennebaud@inria.fr>
5//
6// This Source Code Form is subject to the terms of the Mozilla
7// Public License v. 2.0. If a copy of the MPL was not distributed
8// with this file, You can obtain one at http://mozilla.org/MPL/2.0/.
9
10#ifndef EIGEN_RANDOMSETTER_H
11#define EIGEN_RANDOMSETTER_H
12
13namespace Eigen {
14
15/** Represents a std::map
16 *
17 * \see RandomSetter
18 */
19template<typename Scalar> struct StdMapTraits
20{
21 typedef int KeyType;
22 typedef std::map<KeyType,Scalar> Type;
23 enum {
24 IsSorted = 1
25 };
26
27 static void setInvalidKey(Type&, const KeyType&) {}
28};
29
30#ifdef EIGEN_UNORDERED_MAP_SUPPORT
31/** Represents a std::unordered_map
32 *
33 * To use it you need to both define EIGEN_UNORDERED_MAP_SUPPORT and include the unordered_map header file
34 * yourself making sure that unordered_map is defined in the std namespace.
35 *
36 * For instance, with current version of gcc you can either enable C++0x standard (-std=c++0x) or do:
37 * \code
38 * #include <tr1/unordered_map>
39 * #define EIGEN_UNORDERED_MAP_SUPPORT
40 * namespace std {
41 * using std::tr1::unordered_map;
42 * }
43 * \endcode
44 *
45 * \see RandomSetter
46 */
47template<typename Scalar> struct StdUnorderedMapTraits
48{
49 typedef int KeyType;
50 typedef std::unordered_map<KeyType,Scalar> Type;
51 enum {
52 IsSorted = 0
53 };
54
55 static void setInvalidKey(Type&, const KeyType&) {}
56};
57#endif // EIGEN_UNORDERED_MAP_SUPPORT
58
59#ifdef _DENSE_HASH_MAP_H_
60/** Represents a google::dense_hash_map
61 *
62 * \see RandomSetter
63 */
64template<typename Scalar> struct GoogleDenseHashMapTraits
65{
66 typedef int KeyType;
67 typedef google::dense_hash_map<KeyType,Scalar> Type;
68 enum {
69 IsSorted = 0
70 };
71
72 static void setInvalidKey(Type& map, const KeyType& k)
73 { map.set_empty_key(k); }
74};
75#endif
76
77#ifdef _SPARSE_HASH_MAP_H_
78/** Represents a google::sparse_hash_map
79 *
80 * \see RandomSetter
81 */
82template<typename Scalar> struct GoogleSparseHashMapTraits
83{
84 typedef int KeyType;
85 typedef google::sparse_hash_map<KeyType,Scalar> Type;
86 enum {
87 IsSorted = 0
88 };
89
90 static void setInvalidKey(Type&, const KeyType&) {}
91};
92#endif
93
94/** \class RandomSetter
95 *
96 * \brief The RandomSetter is a wrapper object allowing to set/update a sparse matrix with random access
97 *
98 * \param SparseMatrixType the type of the sparse matrix we are updating
99 * \param MapTraits a traits class representing the map implementation used for the temporary sparse storage.
100 * Its default value depends on the system.
101 * \param OuterPacketBits defines the number of rows (or columns) manage by a single map object
102 * as a power of two exponent.
103 *
104 * This class temporarily represents a sparse matrix object using a generic map implementation allowing for
105 * efficient random access. The conversion from the compressed representation to a hash_map object is performed
106 * in the RandomSetter constructor, while the sparse matrix is updated back at destruction time. This strategy
107 * suggest the use of nested blocks as in this example:
108 *
109 * \code
110 * SparseMatrix<double> m(rows,cols);
111 * {
112 * RandomSetter<SparseMatrix<double> > w(m);
113 * // don't use m but w instead with read/write random access to the coefficients:
114 * for(;;)
115 * w(rand(),rand()) = rand;
116 * }
117 * // when w is deleted, the data are copied back to m
118 * // and m is ready to use.
119 * \endcode
120 *
121 * Since hash_map objects are not fully sorted, representing a full matrix as a single hash_map would
122 * involve a big and costly sort to update the compressed matrix back. To overcome this issue, a RandomSetter
123 * use multiple hash_map, each representing 2^OuterPacketBits columns or rows according to the storage order.
124 * To reach optimal performance, this value should be adjusted according to the average number of nonzeros
125 * per rows/columns.
126 *
127 * The possible values for the template parameter MapTraits are:
128 * - \b StdMapTraits: corresponds to std::map. (does not perform very well)
129 * - \b GnuHashMapTraits: corresponds to __gnu_cxx::hash_map (available only with GCC)
130 * - \b GoogleDenseHashMapTraits: corresponds to google::dense_hash_map (best efficiency, reasonable memory consumption)
131 * - \b GoogleSparseHashMapTraits: corresponds to google::sparse_hash_map (best memory consumption, relatively good performance)
132 *
133 * The default map implementation depends on the availability, and the preferred order is:
134 * GoogleSparseHashMapTraits, GnuHashMapTraits, and finally StdMapTraits.
135 *
136 * For performance and memory consumption reasons it is highly recommended to use one of
137 * the Google's hash_map implementation. To enable the support for them, you have two options:
138 * - \#include <google/dense_hash_map> yourself \b before Eigen/Sparse header
139 * - define EIGEN_GOOGLEHASH_SUPPORT
140 * In the later case the inclusion of <google/dense_hash_map> is made for you.
141 *
142 * \see http://code.google.com/p/google-sparsehash/
143 */
144template<typename SparseMatrixType,
145 template <typename T> class MapTraits =
146#if defined _DENSE_HASH_MAP_H_
147 GoogleDenseHashMapTraits
148#elif defined _HASH_MAP
149 GnuHashMapTraits
150#else
151 StdMapTraits
152#endif
153 ,int OuterPacketBits = 6>
154class RandomSetter
155{
156 typedef typename SparseMatrixType::Scalar Scalar;
157 typedef typename SparseMatrixType::Index Index;
158
159 struct ScalarWrapper
160 {
161 ScalarWrapper() : value(0) {}
162 Scalar value;
163 };
164 typedef typename MapTraits<ScalarWrapper>::KeyType KeyType;
165 typedef typename MapTraits<ScalarWrapper>::Type HashMapType;
166 static const int OuterPacketMask = (1 << OuterPacketBits) - 1;
167 enum {
168 SwapStorage = 1 - MapTraits<ScalarWrapper>::IsSorted,
169 TargetRowMajor = (SparseMatrixType::Flags & RowMajorBit) ? 1 : 0,
170 SetterRowMajor = SwapStorage ? 1-TargetRowMajor : TargetRowMajor
171 };
172
173 public:
174
175 /** Constructs a random setter object from the sparse matrix \a target
176 *
177 * Note that the initial value of \a target are imported. If you want to re-set
178 * a sparse matrix from scratch, then you must set it to zero first using the
179 * setZero() function.
180 */
181 inline RandomSetter(SparseMatrixType& target)
182 : mp_target(&target)
183 {
184 const Index outerSize = SwapStorage ? target.innerSize() : target.outerSize();
185 const Index innerSize = SwapStorage ? target.outerSize() : target.innerSize();
186 m_outerPackets = outerSize >> OuterPacketBits;
187 if (outerSize&OuterPacketMask)
188 m_outerPackets += 1;
189 m_hashmaps = new HashMapType[m_outerPackets];
190 // compute number of bits needed to store inner indices
191 Index aux = innerSize - 1;
192 m_keyBitsOffset = 0;
193 while (aux)
194 {
195 ++m_keyBitsOffset;
196 aux = aux >> 1;
197 }
198 KeyType ik = (1<<(OuterPacketBits+m_keyBitsOffset));
199 for (Index k=0; k<m_outerPackets; ++k)
200 MapTraits<ScalarWrapper>::setInvalidKey(m_hashmaps[k],ik);
201
202 // insert current coeffs
203 for (Index j=0; j<mp_target->outerSize(); ++j)
204 for (typename SparseMatrixType::InnerIterator it(*mp_target,j); it; ++it)
205 (*this)(TargetRowMajor?j:it.index(), TargetRowMajor?it.index():j) = it.value();
206 }
207
208 /** Destructor updating back the sparse matrix target */
209 ~RandomSetter()
210 {
211 KeyType keyBitsMask = (1<<m_keyBitsOffset)-1;
212 if (!SwapStorage) // also means the map is sorted
213 {
214 mp_target->setZero();
215 mp_target->makeCompressed();
216 mp_target->reserve(nonZeros());
217 Index prevOuter = -1;
218 for (Index k=0; k<m_outerPackets; ++k)
219 {
220 const Index outerOffset = (1<<OuterPacketBits) * k;
221 typename HashMapType::iterator end = m_hashmaps[k].end();
222 for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
223 {
224 const Index outer = (it->first >> m_keyBitsOffset) + outerOffset;
225 const Index inner = it->first & keyBitsMask;
226 if (prevOuter!=outer)
227 {
228 for (Index j=prevOuter+1;j<=outer;++j)
229 mp_target->startVec(j);
230 prevOuter = outer;
231 }
232 mp_target->insertBackByOuterInner(outer, inner) = it->second.value;
233 }
234 }
235 mp_target->finalize();
236 }
237 else
238 {
239 VectorXi positions(mp_target->outerSize());
240 positions.setZero();
241 // pass 1
242 for (Index k=0; k<m_outerPackets; ++k)
243 {
244 typename HashMapType::iterator end = m_hashmaps[k].end();
245 for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
246 {
247 const Index outer = it->first & keyBitsMask;
248 ++positions[outer];
249 }
250 }
251 // prefix sum
252 Index count = 0;
253 for (Index j=0; j<mp_target->outerSize(); ++j)
254 {
255 Index tmp = positions[j];
256 mp_target->outerIndexPtr()[j] = count;
257 positions[j] = count;
258 count += tmp;
259 }
260 mp_target->makeCompressed();
261 mp_target->outerIndexPtr()[mp_target->outerSize()] = count;
262 mp_target->resizeNonZeros(count);
263 // pass 2
264 for (Index k=0; k<m_outerPackets; ++k)
265 {
266 const Index outerOffset = (1<<OuterPacketBits) * k;
267 typename HashMapType::iterator end = m_hashmaps[k].end();
268 for (typename HashMapType::iterator it = m_hashmaps[k].begin(); it!=end; ++it)
269 {
270 const Index inner = (it->first >> m_keyBitsOffset) + outerOffset;
271 const Index outer = it->first & keyBitsMask;
272 // sorted insertion
273 // Note that we have to deal with at most 2^OuterPacketBits unsorted coefficients,
274 // moreover those 2^OuterPacketBits coeffs are likely to be sparse, an so only a
275 // small fraction of them have to be sorted, whence the following simple procedure:
276 Index posStart = mp_target->outerIndexPtr()[outer];
277 Index i = (positions[outer]++) - 1;
278 while ( (i >= posStart) && (mp_target->innerIndexPtr()[i] > inner) )
279 {
280 mp_target->valuePtr()[i+1] = mp_target->valuePtr()[i];
281 mp_target->innerIndexPtr()[i+1] = mp_target->innerIndexPtr()[i];
282 --i;
283 }
284 mp_target->innerIndexPtr()[i+1] = inner;
285 mp_target->valuePtr()[i+1] = it->second.value;
286 }
287 }
288 }
289 delete[] m_hashmaps;
290 }
291
292 /** \returns a reference to the coefficient at given coordinates \a row, \a col */
293 Scalar& operator() (Index row, Index col)
294 {
295 const Index outer = SetterRowMajor ? row : col;
296 const Index inner = SetterRowMajor ? col : row;
297 const Index outerMajor = outer >> OuterPacketBits; // index of the packet/map
298 const Index outerMinor = outer & OuterPacketMask; // index of the inner vector in the packet
299 const KeyType key = (KeyType(outerMinor)<<m_keyBitsOffset) | inner;
300 return m_hashmaps[outerMajor][key].value;
301 }
302
303 /** \returns the number of non zero coefficients
304 *
305 * \note According to the underlying map/hash_map implementation,
306 * this function might be quite expensive.
307 */
308 Index nonZeros() const
309 {
310 Index nz = 0;
311 for (Index k=0; k<m_outerPackets; ++k)
312 nz += static_cast<Index>(m_hashmaps[k].size());
313 return nz;
314 }
315
316
317 protected:
318
319 HashMapType* m_hashmaps;
320 SparseMatrixType* mp_target;
321 Index m_outerPackets;
322 unsigned char m_keyBitsOffset;
323};
324
325} // end namespace Eigen
326
327#endif // EIGEN_RANDOMSETTER_H
Note: See TracBrowser for help on using the repository browser.