|
Tpetra Matrix/Vector Services
Version of the Day
|
00001 /* 00002 // @HEADER 00003 // *********************************************************************** 00004 // 00005 // Tpetra: Templated Linear Algebra Services Package 00006 // Copyright (2008) Sandia Corporation 00007 // 00008 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation, 00009 // the U.S. Government retains certain rights in this software. 00010 // 00011 // Redistribution and use in source and binary forms, with or without 00012 // modification, are permitted provided that the following conditions are 00013 // met: 00014 // 00015 // 1. Redistributions of source code must retain the above copyright 00016 // notice, this list of conditions and the following disclaimer. 00017 // 00018 // 2. Redistributions in binary form must reproduce the above copyright 00019 // notice, this list of conditions and the following disclaimer in the 00020 // documentation and/or other materials provided with the distribution. 00021 // 00022 // 3. Neither the name of the Corporation nor the names of the 00023 // contributors may be used to endorse or promote products derived from 00024 // this software without specific prior written permission. 00025 // 00026 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY 00027 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 00028 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 00029 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE 00030 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 00031 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 00032 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 00033 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 00034 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 00035 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 00036 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 00037 // 00038 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00039 // 00040 // ************************************************************************ 00041 // @HEADER 00042 */ 00043 00044 #ifndef TPETRA_DETAILS_FIXEDHASHTABLE_DEF_HPP 00045 #define TPETRA_DETAILS_FIXEDHASHTABLE_DEF_HPP 00046 00047 #ifdef DOXYGEN_USE_ONLY 00048 #include "Tpetra_Details_FixedHashTable_decl.hpp" 00049 #endif 00050 00051 #include <Teuchos_as.hpp> 00052 #include "MurmurHash3.hpp" 00053 00054 00055 namespace Tpetra { 00056 namespace Details { 00057 00058 template<typename KeyType, typename ValueType> 00059 int FixedHashTable<KeyType, ValueType>::hashFunc (const KeyType key) const { 00060 #ifdef TPETRA_USE_MURMUR_HASH 00061 uint32_t k; 00062 MurmurHash3_x86_32 ((void *) &key, sizeof (KeyType), 1, (void *) &k); 00063 return (int) (k % size_); 00064 #else 00065 // We are using Epetra's hash function by default, as we have 00066 // observed that it is much faster than the Murmur hash 00067 // function. However, this is not a good hash function for general 00068 // sets of keys. For our typical use case, this is good. Use 00069 // Murmur hash if the maps are sparse. 00070 const unsigned int seed = (2654435761U); 00071 00072 #ifdef HAVE_TPETRA_DEBUG 00073 TEUCHOS_TEST_FOR_EXCEPTION( 00074 size_ == 0, std::logic_error, "Tpetra::Details::FixedHashTable::hashFunc: " 00075 "size_ == 0. Please report this bug to the Tpetra developers."); 00076 #endif // HAVE_TPETRA_DEBUG 00077 00078 const int intkey = (int) ((key & 0x000000007fffffffLL) + 00079 ((key & 0x7fffffff80000000LL) >> 31)); 00080 return (int) ((seed ^ intkey) % size_); 00081 #endif 00082 } 00083 00084 template<typename KeyType, typename ValueType> 00085 int 00086 FixedHashTable<KeyType, ValueType>::getRecommendedSize (const int size) 00087 { 00088 // A large list of prime numbers. 00089 // Based on a recommendation by Andres Valloud in hash forums. 00090 // There are only enough primes here so that between any number N and 2*N, 00091 // there will be at least about 8 to choose from (except the first 10). 00092 // This is a balance between a small list of primes, and getting a 00093 // collection size that doesn't waste too much space. In addition, 00094 // the primes in this table were chosen so that they do not divide 00095 // 256^k +- a, for 1<=k<=8, and -32<=a<=32. This is so that 00096 // using them as a modulo will not have a tendency to just throw away 00097 // the most significant bits of the object's hash. The primes (except the 00098 // first ten) or not close to any power of two to avoid aliasing 00099 // between hash functions based on bit manipulation and the moduli. 00100 int primes [ ] = { 00101 3, 7, 13, 23, 53, 97, 193, 389, 769, 1543, 00102 2237, 2423, 2617, 2797, 2999, 3167, 3359, 3539, 00103 3727, 3911, 4441 , 4787 , 5119 , 5471 , 5801 , 6143 , 6521 , 6827 00104 , 7177 , 7517 , 7853 , 8887 , 9587 , 10243 , 10937 , 11617 , 12289 00105 , 12967 , 13649 , 14341 , 15013 , 15727 00106 , 17749 , 19121 , 20479 , 21859 , 23209 , 24593 , 25939 , 27329 00107 , 28669 , 30047 , 31469 , 35507 , 38231 , 40961 , 43711 , 46439 00108 , 49157 , 51893 , 54617 , 57347 , 60077 , 62801 , 70583 , 75619 00109 , 80669 , 85703 , 90749 , 95783 , 100823 , 105871 , 110909 , 115963 00110 , 120997 , 126031 , 141157 , 151237 , 161323 , 171401 , 181499 , 191579 00111 , 201653 , 211741 , 221813 , 231893 , 241979 , 252079 00112 , 282311 , 302483 , 322649 , 342803 , 362969 , 383143 , 403301 , 423457 00113 , 443629 , 463787 , 483953 , 504121 , 564617 , 604949 , 645313 , 685609 00114 , 725939 , 766273 , 806609 , 846931 , 887261 , 927587 , 967919 , 1008239 00115 , 1123477 , 1198397 , 1273289 , 1348177 , 1423067 , 1497983 , 1572869 00116 , 1647761 , 1722667 , 1797581 , 1872461 , 1947359 , 2022253 00117 , 2246953 , 2396759 , 2546543 , 2696363 , 2846161 , 2995973 , 3145739 00118 , 3295541 , 3445357 , 3595117 , 3744941 , 3894707 , 4044503 00119 , 4493921 , 4793501 , 5093089 , 5392679 , 5692279 , 5991883 , 6291469 00120 , 6591059 , 6890641 , 7190243 , 7489829 , 7789447 , 8089033 00121 , 8987807 , 9586981 , 10186177 , 10785371 , 11384539 , 11983729 00122 , 12582917 , 13182109 , 13781291 , 14380469 , 14979667 , 15578861 00123 , 16178053 , 17895707 , 19014187 , 20132683 , 21251141 , 22369661 00124 , 23488103 , 24606583 , 25725083 , 26843549 , 27962027 , 29080529 00125 , 30198989 , 31317469 , 32435981 , 35791397 , 38028379 , 40265327 00126 , 42502283 , 44739259 , 46976221 , 49213237 , 51450131 , 53687099 00127 , 55924061 , 58161041 , 60397993 , 62634959 , 64871921 00128 , 71582857 , 76056727 , 80530643 , 85004567 , 89478503 , 93952427 00129 , 98426347 , 102900263 , 107374217 , 111848111 , 116322053 , 120795971 00130 , 125269877 , 129743807 , 143165587 , 152113427 , 161061283 , 170009141 00131 , 178956983 , 187904819 , 196852693 , 205800547 , 214748383 , 223696237 00132 , 232644089 , 241591943 , 250539763 , 259487603 , 268435399 }; 00133 00134 int hsize = primes[220] ; 00135 for (int i = 0; i < 221; ++i) { 00136 if (size <= primes[i]) { 00137 hsize = primes[i]; 00138 break; 00139 } 00140 } 00141 return hsize; 00142 } 00143 00144 template<typename KeyType, typename ValueType> 00145 FixedHashTable<KeyType, ValueType>:: 00146 FixedHashTable (const ArrayView<const KeyType>& keys) : 00147 size_ (0), 00148 rawPtr_ (NULL), 00149 rawVal_ (NULL), 00150 hasDuplicateKeys_ (false) // to revise in init() 00151 { 00152 init (keys, Teuchos::as<ValueType> (0)); 00153 } 00154 00155 template<typename KeyType, typename ValueType> 00156 FixedHashTable<KeyType, ValueType>:: 00157 FixedHashTable (const ArrayView<const KeyType>& keys, 00158 const ValueType startingValue) : 00159 size_ (0), 00160 rawPtr_ (NULL), 00161 rawVal_ (NULL), 00162 hasDuplicateKeys_ (false) // to revise in init() 00163 { 00164 init (keys, startingValue); 00165 } 00166 00167 template<typename KeyType, typename ValueType> 00168 FixedHashTable<KeyType, ValueType>:: 00169 FixedHashTable (const ArrayView<const KeyType>& keys, 00170 const ArrayView<const ValueType>& vals) : 00171 size_ (0), 00172 rawPtr_ (NULL), 00173 rawVal_ (NULL), 00174 hasDuplicateKeys_ (false) // to revise in init() 00175 { 00176 init (keys, vals); 00177 } 00178 00179 template<typename KeyType, typename ValueType> 00180 void 00181 FixedHashTable<KeyType, ValueType>:: 00182 init (const ArrayView<const KeyType>& keys, 00183 const ValueType startingValue) 00184 { 00185 using Teuchos::arcp; 00186 using Teuchos::arcp_const_cast; 00187 using Teuchos::ArrayRCP; 00188 using Teuchos::as; 00189 00190 const size_type numKeys = keys.size (); 00191 const size_type size = getRecommendedSize (as<int> (numKeys)); 00192 #ifdef HAVE_TPETRA_DEBUG 00193 TEUCHOS_TEST_FOR_EXCEPTION( 00194 size == 0 && numKeys != 0, std::logic_error, 00195 "Tpetra::Details::FixedHashTable constructor: " 00196 "getRecommendedSize(" << numKeys << ") returned zero, " 00197 "even though the number of keys " << numKeys << " is nonzero. " 00198 "Please report this bug to the Tpetra developers."); 00199 #endif // HAVE_TPETRA_DEBUG 00200 00201 // We have to set the size_ internal state before calling the hash 00202 // function, since the hash function uses it. 00203 size_ = as<KeyType> (size); 00204 00205 ArrayRCP<size_type> ptr (size + 1, 0); 00206 // The constructor that takes just a size argument automatically 00207 // fills the data. We don't need to waste time filling it here 00208 // because we will do so below. The try/catch block isn't strictly 00209 // necessary; we could just give "new std::pair<...> [numKeys]" to 00210 // the ArrayRCP constructor, since no other arguments of the 00211 // constructor might throw before entering the constructor. 00212 ArrayRCP<std::pair<KeyType, ValueType> > val; 00213 std::pair<KeyType, ValueType>* rawVal = NULL; 00214 try { 00215 rawVal = new std::pair<KeyType, ValueType> [numKeys]; 00216 val = arcp<std::pair<KeyType, ValueType> > (rawVal, 0, numKeys, true); 00217 } catch (...) { 00218 if (rawVal != NULL) { 00219 delete [] rawVal; 00220 } 00221 throw; 00222 } 00223 00224 // Compute number of entries in each hash table position. 00225 for (size_type k = 0; k < numKeys; ++k) { 00226 const int hashVal = hashFunc (keys[k]); 00227 // Shift over one, so that counts[j] = ptr[j+1]. See below. 00228 ++ptr[hashVal+1]; 00229 00230 if (ptr[hashVal+1] > 1) { 00231 hasDuplicateKeys_ = true; 00232 } 00233 } 00234 00235 // Compute row offsets via prefix sum: 00236 // 00237 // ptr[i+1] = \sum_{j=0}^{i} counts[j]. 00238 // 00239 // Thus, ptr[i+1] - ptr[i] = counts[i], so that ptr[i+1] = ptr[i] + 00240 // counts[i]. If we stored counts[i] in ptr[i+1] on input, then the 00241 // formula is ptr[i+1] += ptr[i]. 00242 for (size_type i = 0; i < size; ++i) { 00243 ptr[i+1] += ptr[i]; 00244 } 00245 //ptr[0] = 0; // We've already done this when initializing ptr above. 00246 00247 // curRowStart[i] is the offset of the next element in row i. 00248 ArrayRCP<size_type> curRowStart (size, 0); 00249 00250 // Fill in the hash table. 00251 for (size_type k = 0; k < numKeys; ++k) { 00252 const KeyType key = keys[k]; 00253 const ValueType theVal = startingValue + as<ValueType> (k); 00254 const int hashVal = hashFunc (key); 00255 00256 const size_type offset = curRowStart[hashVal]; 00257 const size_type curPos = ptr[hashVal] + offset; 00258 00259 val[curPos].first = key; 00260 val[curPos].second = theVal; 00261 ++curRowStart[hashVal]; 00262 } 00263 00264 // "Commit" the computed arrays. 00265 ptr_ = arcp_const_cast<const size_type> (ptr); 00266 00267 // FIXME (mfh 25 Apr 2013) arcp_const_cast on val_ and 00268 // val_.getRawPtr() both cause a hang with MPI for some reason. Not 00269 // sure what's going on. Anyway, calling val.release(), recreating 00270 // val_ by hand, and using the released raw pointer as rawVal_, 00271 // seems to fix the problem. 00272 00273 //val_ = arcp_const_cast<const std::pair<KeyType, ValueType> > (val); 00274 const std::pair<KeyType, ValueType>* valRaw = val.release (); 00275 val_ = ArrayRCP<const std::pair<KeyType, ValueType> > (valRaw, 0, numKeys, true); 00276 //val_ = arcp<const std::pair<KeyType, ValueType> > (valRaw, 0, numKeys, true); 00277 rawPtr_ = ptr_.getRawPtr (); 00278 // rawVal_ = val_.getRawPtr (); 00279 rawVal_ = valRaw; 00280 } 00281 00282 00283 template<typename KeyType, typename ValueType> 00284 void 00285 FixedHashTable<KeyType, ValueType>:: 00286 init (const ArrayView<const KeyType>& keys, 00287 const ArrayView<const ValueType>& vals) 00288 { 00289 using Teuchos::arcp; 00290 using Teuchos::arcp_const_cast; 00291 using Teuchos::ArrayRCP; 00292 using Teuchos::as; 00293 00294 const size_type numKeys = keys.size (); 00295 const size_type size = getRecommendedSize (as<int> (numKeys)); 00296 #ifdef HAVE_TPETRA_DEBUG 00297 TEUCHOS_TEST_FOR_EXCEPTION( 00298 size == 0 && numKeys != 0, std::logic_error, 00299 "Tpetra::Details::FixedHashTable constructor: " 00300 "getRecommendedSize(" << numKeys << ") returned zero, " 00301 "even though the number of keys " << numKeys << " is nonzero. " 00302 "Please report this bug to the Tpetra developers."); 00303 #endif // HAVE_TPETRA_DEBUG 00304 00305 // We have to set the size_ internal state before calling the hash 00306 // function, since the hash function uses it. 00307 size_ = as<KeyType> (size); 00308 00309 ArrayRCP<size_type> ptr (size + 1, 0); 00310 // The constructor that takes just a size argument automatically 00311 // fills the data. We don't need to waste time filling it here 00312 // because we will do so below. The try/catch block isn't strictly 00313 // necessary; we could just give "new std::pair<...> [numKeys]" to 00314 // the ArrayRCP constructor, since no other arguments of the 00315 // constructor might throw before entering the constructor. 00316 ArrayRCP<std::pair<KeyType, ValueType> > val; 00317 std::pair<KeyType, ValueType>* rawVal = NULL; 00318 try { 00319 rawVal = new std::pair<KeyType, ValueType> [numKeys]; 00320 val = arcp<std::pair<KeyType, ValueType> > (rawVal, 0, numKeys, true); 00321 } catch (...) { 00322 if (rawVal != NULL) { 00323 delete [] rawVal; 00324 } 00325 throw; 00326 } 00327 00328 // Compute number of entries in each hash table position. 00329 for (size_type k = 0; k < numKeys; ++k) { 00330 const int hashVal = hashFunc (keys[k]); 00331 // Shift over one, so that counts[j] = ptr[j+1]. See below. 00332 ++ptr[hashVal+1]; 00333 00334 if (ptr[hashVal+1] > 1) { 00335 hasDuplicateKeys_ = true; 00336 } 00337 } 00338 00339 // Compute row offsets via prefix sum: 00340 // 00341 // ptr[i+1] = \sum_{j=0}^{i} counts[j]. 00342 // 00343 // Thus, ptr[i+1] - ptr[i] = counts[i], so that ptr[i+1] = ptr[i] + 00344 // counts[i]. If we stored counts[i] in ptr[i+1] on input, then the 00345 // formula is ptr[i+1] += ptr[i]. 00346 for (size_type i = 0; i < size; ++i) { 00347 ptr[i+1] += ptr[i]; 00348 } 00349 //ptr[0] = 0; // We've already done this when initializing ptr above. 00350 00351 // curRowStart[i] is the offset of the next element in row i. 00352 ArrayRCP<size_type> curRowStart (size, 0); 00353 00354 // Fill in the hash table. 00355 for (size_type k = 0; k < numKeys; ++k) { 00356 const KeyType key = keys[k]; 00357 const ValueType theVal = vals[k]; 00358 const int hashVal = hashFunc (key); 00359 00360 const size_type offset = curRowStart[hashVal]; 00361 const size_type curPos = ptr[hashVal] + offset; 00362 00363 val[curPos].first = key; 00364 val[curPos].second = theVal; 00365 ++curRowStart[hashVal]; 00366 } 00367 00368 // "Commit" the computed arrays. 00369 ptr_ = arcp_const_cast<const size_type> (ptr); 00370 00371 // FIXME (mfh 25 Apr 2013) arcp_const_cast on val_ and 00372 // val_.getRawPtr() both cause a hang with MPI for some reason. Not 00373 // sure what's going on. Anyway, calling val.release(), recreating 00374 // val_ by hand, and using the released raw pointer as rawVal_, 00375 // seems to fix the problem. 00376 00377 //val_ = arcp_const_cast<const std::pair<KeyType, ValueType> > (val); 00378 const std::pair<KeyType, ValueType>* valRaw = val.release (); 00379 val_ = ArrayRCP<const std::pair<KeyType, ValueType> > (valRaw, 0, numKeys, true); 00380 //val_ = arcp<const std::pair<KeyType, ValueType> > (valRaw, 0, numKeys, true); 00381 rawPtr_ = ptr_.getRawPtr (); 00382 // rawVal_ = val_.getRawPtr (); 00383 rawVal_ = valRaw; 00384 } 00385 00386 00387 template<typename KeyType, typename ValueType> 00388 FixedHashTable<KeyType, ValueType>:: 00389 FixedHashTable (const FixedHashTable & obj) : 00390 size_ (obj.size_), 00391 ptr_ (obj.ptr_), 00392 val_ (obj.val_), 00393 rawPtr_ (obj.rawPtr_), 00394 rawVal_ (obj.rawVal_), 00395 hasDuplicateKeys_ (obj.hasDuplicateKeys_) 00396 {} 00397 00398 template<typename KeyType, typename ValueType> 00399 ValueType 00400 FixedHashTable<KeyType, ValueType>:: 00401 get (const KeyType key) const 00402 { 00403 const int hashVal = hashFunc (key); 00404 #ifdef HAVE_TPETRA_DEBUG 00405 00406 const size_type start = ptr_[hashVal]; 00407 const size_type end = ptr_[hashVal+1]; 00408 for (size_type k = start; k < end; ++k) { 00409 if (val_[k].first == key) { 00410 return val_[k].second; 00411 } 00412 } 00413 #else 00414 const size_type start = rawPtr_[hashVal]; 00415 const size_type end = rawPtr_[hashVal+1]; 00416 for (size_type k = start; k < end; ++k) { 00417 if (rawVal_[k].first == key) { 00418 return rawVal_[k].second; 00419 } 00420 } 00421 #endif // HAVE_TPETRA_DEBUG 00422 return Teuchos::OrdinalTraits<ValueType>::invalid (); 00423 } 00424 00425 template <typename KeyType, typename ValueType> 00426 std::string FixedHashTable<KeyType, ValueType>::description() const { 00427 std::ostringstream oss; 00428 oss << "FixedHashTable<" 00429 << Teuchos::TypeNameTraits<KeyType>::name () << "," 00430 << Teuchos::TypeNameTraits<ValueType>::name () << ">: " 00431 << "{ numKeys: " << val_.size () 00432 << ", tableSize: " << ptr_.size () << " }"; 00433 return oss.str(); 00434 } 00435 00436 template <typename KeyType, typename ValueType> 00437 void 00438 FixedHashTable<KeyType, ValueType>:: 00439 describe (Teuchos::FancyOStream &out, 00440 const Teuchos::EVerbosityLevel verbLevel) const 00441 { 00442 using std::endl; 00443 using std::setw; 00444 using Teuchos::OSTab; 00445 using Teuchos::rcpFromRef; 00446 using Teuchos::TypeNameTraits; 00447 using Teuchos::VERB_DEFAULT; 00448 using Teuchos::VERB_NONE; 00449 using Teuchos::VERB_LOW; 00450 using Teuchos::VERB_EXTREME; 00451 00452 Teuchos::EVerbosityLevel vl = verbLevel; 00453 if (vl == VERB_DEFAULT) vl = VERB_LOW; 00454 00455 if (vl == VERB_NONE) { 00456 // do nothing 00457 } 00458 else if (vl == VERB_LOW) { 00459 out << this->description() << endl; 00460 } 00461 else { // MEDIUM, HIGH or EXTREME 00462 out << "FixedHashTable:" << endl; 00463 { 00464 OSTab tab1 (rcpFromRef (out)); 00465 00466 const std::string label = this->getObjectLabel (); 00467 if (label != "") { 00468 out << "label: " << label << endl; 00469 } 00470 out << "Template parameters:" << endl; 00471 { 00472 OSTab tab2 (rcpFromRef (out)); 00473 out << "KeyType: " << TypeNameTraits<KeyType>::name () << endl 00474 << "ValueType: " << TypeNameTraits<ValueType>::name () << endl; 00475 } 00476 00477 const size_type tableSize = size_; 00478 const size_type numKeys = val_.size (); 00479 00480 out << "Table parameters:" << endl; 00481 { 00482 OSTab tab2 (rcpFromRef (out)); 00483 out << "numKeys: " << numKeys << endl 00484 << "tableSize: " << tableSize << endl; 00485 } 00486 00487 if (vl >= VERB_EXTREME) { 00488 out << "Contents: "; 00489 if (tableSize == 0 || numKeys == 0) { 00490 out << "[]" << endl; 00491 } else { 00492 out << "[ " << endl; 00493 { 00494 OSTab tab2 (rcpFromRef (out)); 00495 for (size_type i = 0; i < tableSize; ++i) { 00496 OSTab tab3 (rcpFromRef (out)); 00497 out << "["; 00498 for (size_type k = ptr_[i]; k < ptr_[i+1]; ++k) { 00499 out << "(" << val_[k].first << "," << val_[k].second << ")"; 00500 if (k + 1 < ptr_[i+1]) { 00501 out << ", "; 00502 } 00503 } 00504 out << "]" << endl; 00505 } // for each table position i 00506 } 00507 out << "]" << endl; 00508 } // The table contains entries 00509 } // vl >= VERB_EXTREME 00510 } 00511 out << endl; 00512 } // if vl > VERB_LOW 00513 } 00514 00515 } // namespace Details 00516 } // namespace Tpetra 00517 00518 // Macro that explicitly instantiates FixedHashTable for the given local 00519 // ordinal (LO) and global ordinal (GO) types. Note that FixedHashTable's 00520 // template parameters occur in the opposite order of most Tpetra 00521 // classes. This is because FixedHashTable performs global-to-local 00522 // lookup, and the convention in templated C++ lookup tables (such as 00523 // std::map) is <KeyType, ValueType>. 00524 // 00525 // This macro must be explanded within the Tpetra::Details namespace. 00526 #define TPETRA_DETAILS_FIXEDHASHTABLE_INSTANT_DEFAULTNODE(LO,GO) \ 00527 template class FixedHashTable< GO , LO >; \ 00528 00529 #endif // TPETRA_DETAILS_FIXEDHASHTABLE_DEF_HPP
1.7.6.1