|
Tpetra Matrix/Vector Services
Version of the Day
|
00001 //@HEADER 00002 // ************************************************************************ 00003 // 00004 // Tpetra: Linear Algebra Services Package 00005 // Copyright (2009) Sandia Corporation 00006 // 00007 // Under terms of Contract DE-AC04-94AL85000, there is a non-exclusive 00008 // license for use of this work by or on behalf of the U.S. Government. 00009 // 00010 // This library is free software; you can redistribute it and/or modify 00011 // it under the terms of the GNU Lesser General Public License as 00012 // published by the Free Software Foundation; either version 2.1 of the 00013 // License, or (at your option) any later version. 00014 // 00015 // This library is distributed in the hope that it will be useful, but 00016 // WITHOUT ANY WARRANTY; without even the implied warranty of 00017 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00018 // Lesser General Public License for more details. 00019 // 00020 // You should have received a copy of the GNU Lesser General Public 00021 // License along with this library; if not, write to the Free Software 00022 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 00023 // USA 00024 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 00025 // 00026 // ************************************************************************ 00027 //@HEADER 00028 00036 #include <Teuchos_Array.hpp> 00037 #include <Teuchos_ScalarTraits.hpp> 00038 #include <Teuchos_OrdinalTraits.hpp> 00039 #include <Teuchos_RCP.hpp> 00040 #include <Teuchos_GlobalMPISession.hpp> 00041 #include <Teuchos_oblackholestream.hpp> 00042 00043 #include "Tpetra_DefaultPlatform.hpp" 00044 #include "Tpetra_Version.hpp" 00045 #include "Tpetra_Map.hpp" 00046 #include "Tpetra_MultiVector.hpp" 00047 #include "Tpetra_Vector.hpp" 00048 #include "Tpetra_CrsMatrix.hpp" 00049 00050 // Implementation of the power method for estimating the eigenvalue of 00051 // maximum magnitude of a matrix. This function returns the 00052 // eigenvalue estimate. 00053 // 00054 // TpetraOperatorType: the type of the Tpetra::Operator specialization 00055 // used to represent the sparse matrix or operator A. 00056 // 00057 // A: The sparse matrix or operator, as a Tpetra::Operator. 00058 // niters: Maximum number of iterations of the power method. 00059 // tolerance: If the 2-norm of the residual A*x-lambda*x (for the 00060 // current eigenvalue estimate lambda) is less than this, stop 00061 // iterating. The complicated expression for the type ensures that 00062 // if the type of entries in the matrix A (scalar_type) is complex, 00063 // then we'll be using a real-valued type ("magnitude") for the 00064 // tolerance. (You can't compare complex numbers using less than, 00065 // so you can't test for convergence using a complex number.) 00066 // out: output stream to which to print the current status of the 00067 // power method. 00068 template <class TpetraOperatorType> 00069 typename TpetraOperatorType::scalar_type 00070 powerMethod (const TpetraOperatorType& A, 00071 const size_t niters, 00072 const typename Teuchos::ScalarTraits<typename TpetraOperatorType::scalar_type>::magnitudeType tolerance, 00073 std::ostream& out); 00074 00075 int 00076 main (int argc, char *argv[]) 00077 { 00078 // global_size_t: Tpetra defines this unsigned integer type big 00079 // enough to hold any global dimension or amount of data. 00080 using Tpetra::global_size_t; 00081 using Teuchos::Array; 00082 using Teuchos::ArrayView; 00083 using Teuchos::ArrayRCP; 00084 using Teuchos::arcp; 00085 using Teuchos::RCP; 00086 using Teuchos::rcp; 00087 using Teuchos::tuple; 00088 using std::cerr; 00089 using std::cout; 00090 using std::endl; 00091 00092 // Template parameters for Tpetra objects. Tpetra::CrsMatrix has 00093 // five template parameters, but all but the first of them have 00094 // defaults. 00095 typedef double scalar_type; 00096 typedef int local_ordinal_type; 00097 typedef long global_ordinal_type; 00098 00099 // Convenience typedef. 00100 typedef Teuchos::ScalarTraits<scalar_type>::magnitudeType magnitude_type; 00101 00102 Teuchos::oblackholestream blackhole; 00103 Teuchos::GlobalMPISession mpiSession (&argc, &argv, &blackhole); 00104 RCP<const Teuchos::Comm<int> > comm = Tpetra::DefaultPlatform::getDefaultPlatform().getComm(); 00105 00106 const size_t myRank = comm->getRank(); 00107 //const size_t numProcs = comm->getSize(); 00108 00109 // Make an output stream (for verbose output) that only prints on 00110 // Proc 0 of the communicator. 00111 Teuchos::oblackholestream blackHole; 00112 std::ostream& out = (myRank == 0) ? std::cout : blackHole; 00113 00114 // Print the current version of Tpetra. 00115 out << Tpetra::version() << endl << endl; 00116 00117 // The number of rows and columns in the matrix. 00118 const global_size_t numGlobalElements = 50; 00119 00120 // Construct a Map that puts approximately the same number of 00121 // equations on each processor. 00122 const global_ordinal_type indexBase = 0; 00123 typedef Tpetra::Map<local_ordinal_type, global_ordinal_type> map_type; 00124 RCP<const map_type> map = rcp (new map_type (numGlobalElements, indexBase, comm)); 00125 00126 // Get the list of global indices that this process owns. In this 00127 // example, this is unnecessary, because we know that we created a 00128 // contiguous Map (see above). (Thus, we really only need the min 00129 // and max global index on this process.) However, in general, we 00130 // don't know what global indices the Map owns, so if we plan to add 00131 // entries into the sparse matrix using global indices, we have to 00132 // get the list of global indices this process owns. 00133 const size_t numMyElements = map->getNodeNumElements(); 00134 ArrayView<const global_ordinal_type> myGlobalElements = map->getNodeElementList (); 00135 00136 out << endl << "Creating the sparse matrix" << endl; 00137 00138 // Create a Tpetra sparse matrix whose rows have distribution given by the Map. 00139 typedef Tpetra::CrsMatrix<scalar_type, local_ordinal_type, global_ordinal_type> mat_type; 00140 RCP<mat_type> A = rcp (new mat_type (map, 0)); 00141 00142 // Fill the sparse matrix, one row at a time. 00143 const scalar_type two = static_cast<scalar_type>( 2.0); 00144 const scalar_type negOne = static_cast<scalar_type>(-1.0); 00145 for (size_t i = 0; i < numMyElements; ++i) { 00146 // A(0, 0:1) = [2, -1] 00147 if (myGlobalElements[i] == 0) { 00148 A->insertGlobalValues (myGlobalElements[i], 00149 tuple<global_ordinal_type> (myGlobalElements[i], myGlobalElements[i]+1), 00150 tuple<scalar_type> (two, negOne)); 00151 } 00152 // A(N-1, N-2:N-1) = [-1, 2] 00153 else if (static_cast<global_size_t> (myGlobalElements[i]) == numGlobalElements - 1) { 00154 A->insertGlobalValues (myGlobalElements[i], 00155 tuple<global_ordinal_type> (myGlobalElements[i]-1, myGlobalElements[i]), 00156 tuple<scalar_type> (negOne, two)); 00157 } 00158 // A(i, i-1:i+1) = [-1, 2, -1] 00159 else { 00160 A->insertGlobalValues (myGlobalElements[i], 00161 tuple<global_ordinal_type> (myGlobalElements[i]-1, myGlobalElements[i], myGlobalElements[i]+1), 00162 tuple<scalar_type> (negOne, two, negOne)); 00163 } 00164 } 00165 00166 // Tell the sparse matrix that we are done adding entries to it. 00167 A->fillComplete (); 00168 00169 // Number of iterations 00170 const size_t niters = static_cast<size_t> (500); 00171 // Desired (absolute) residual tolerance 00172 const magnitude_type tolerance = 1.0e-2; 00173 00174 // Run the power method and report the result. 00175 scalar_type lambda = powerMethod<mat_type> (*A, niters, tolerance, out); 00176 out << endl << "Estimated max eigenvalue: " << lambda << endl; 00177 00178 // 00179 // Now we're going to change values in the sparse matrix and run the 00180 // power method again. In Tpetra, if fillComplete() has been 00181 // called, you have to call resumeFill() before you may change the 00182 // matrix (either its values or its structure). 00183 // 00184 00185 // 00186 // Increase diagonal dominance 00187 // 00188 out << endl << "Increasing magnitude of A(0,0), solving again" << endl; 00189 00190 // Must call resumeFill() before changing the matrix, even its values. 00191 A->resumeFill(); 00192 00193 if (A->getRowMap()->isNodeGlobalElement (0)) { 00194 // Get a copy of the row with with global index 0. Modify the 00195 // diagonal entry of that row. Submit the modified values to the 00196 // matrix. 00197 const global_ordinal_type idOfFirstRow = 0; 00198 size_t numEntriesInRow = A->getNumEntriesInGlobalRow (idOfFirstRow); 00199 Array<scalar_type> rowvals (numEntriesInRow); 00200 Array<global_ordinal_type> rowinds (numEntriesInRow); 00201 00202 // Fill rowvals and rowinds with the values resp. (global) column 00203 // indices of the sparse matrix entries owned by the calling 00204 // process. 00205 // 00206 // Note that it's legal (though we don't exercise it in this 00207 // example) for the row Map of the sparse matrix not to be one to 00208 // one. This means that more than one process might own entries 00209 // in the first row. In general, multiple processes might own the 00210 // (0,0) entry, so that the global A(0,0) value is really the sum 00211 // of all processes' values for that entry. However, scaling the 00212 // entry by a constant factor distributes across that sum, so it's 00213 // OK to do so. 00214 // 00215 // The parentheses after rowinds and rowvalues indicate "a view of 00216 // the Array's data." Array::operator() returns an ArrayView. 00217 A->getGlobalRowCopy (idOfFirstRow, rowinds (), rowvals (), numEntriesInRow); 00218 for (size_t i = 0; i < numEntriesInRow; i++) { 00219 if (rowinds[i] == idOfFirstRow) { 00220 // We have found the diagonal entry; modify it. 00221 rowvals[i] *= 10.0; 00222 } 00223 } 00224 // "Replace global values" means modify the values, but not the 00225 // structure of the sparse matrix. If the specified columns 00226 // aren't already populated in this row on this process, then this 00227 // method throws an exception. If you want to modify the 00228 // structure (by adding new entries), you'll need to call 00229 // insertGlobalValues(). 00230 A->replaceGlobalValues (idOfFirstRow, rowinds (), rowvals ()); 00231 } 00232 00233 // Call fillComplete() again to signal that we are done changing the 00234 // matrix. 00235 A->fillComplete (); 00236 00237 // Run the power method again. 00238 lambda = powerMethod<mat_type> (*A, niters, tolerance, out); 00239 out << endl << "Estimated max eigenvalue: " << lambda << endl; 00240 00241 // This tells the Trilinos test framework that the test passed. 00242 if (myRank == 0) { 00243 std::cout << "End Result: TEST PASSED" << std::endl; 00244 } 00245 00246 return 0; 00247 } 00248 00249 00250 template <class TpetraOperatorType> 00251 typename TpetraOperatorType::scalar_type 00252 powerMethod (const TpetraOperatorType& A, 00253 const size_t niters, 00254 const typename Teuchos::ScalarTraits<typename TpetraOperatorType::scalar_type>::magnitudeType tolerance, 00255 std::ostream& out) 00256 { 00257 using std::endl; 00258 typedef typename TpetraOperatorType::scalar_type scalar_type; 00259 typedef typename TpetraOperatorType::local_ordinal_type local_ordinal_type; 00260 typedef typename TpetraOperatorType::global_ordinal_type global_ordinal_type; 00261 typedef typename TpetraOperatorType::node_type node_type; 00262 00263 typedef Teuchos::ScalarTraits<scalar_type> STS; 00264 typedef typename STS::magnitudeType magnitude_type; 00265 typedef Teuchos::ScalarTraits<magnitude_type> STM; 00266 00267 // The type of a Tpetra vector with the same template parameters as those of A. 00268 typedef Tpetra::Vector<scalar_type, local_ordinal_type, global_ordinal_type, node_type> vec_type; 00269 00270 // Create three vectors for iterating the power method. Since the 00271 // power method computes z = A*q, q should be in the domain of A and 00272 // z should be in the range. (Obviously the power method requires 00273 // that the domain and the range are equal, but it's a good idea to 00274 // get into the habit of thinking whether a particular vector 00275 // "belongs" in the domain or range of the matrix.) The residual 00276 // vector "resid" is of course in the range of A. 00277 vec_type q (A.getDomainMap ()); 00278 vec_type z (A.getRangeMap ()); 00279 vec_type resid (A.getRangeMap ()); 00280 00281 // Fill the iteration vector z with random numbers to start. Don't 00282 // have grand expectations about the quality of our pseudorandom 00283 // number generator; this is usually good enough for eigensolvers. 00284 z.randomize(); 00285 00286 // lambda: the current approximation of the eigenvalue of maximum magnitude. 00287 // normz: the 2-norm of the current iteration vector z. 00288 // residual: the 2-norm of the current residual vector "resid" 00289 scalar_type lambda = STS::zero(); 00290 magnitude_type normz = STM::zero(); 00291 magnitude_type residual = STM::zero(); 00292 00293 const scalar_type one = STS::one(); 00294 const scalar_type zero = STS::zero(); 00295 00296 // How often to report progress in the power method. Reporting 00297 // progress requires computing a residual which can be expensive. 00298 // However, if you don't compute the residual often enough, you 00299 // might keep iterating even after you've converged. 00300 const size_t reportFrequency = 10; 00301 00302 // Do the power method, until the method has converged or the 00303 // maximum iteration count has been reached. 00304 for (size_t iter = 0; iter < niters; ++iter) { 00305 normz = z.norm2 (); // Compute the 2-norm of z 00306 q.scale (one / normz, z); // q := z / normz 00307 A.apply (q, z); // z := A * q 00308 lambda = q.dot (z); // Approx. max eigenvalue 00309 00310 // Compute and report the residual norm every reportFrequency 00311 // iterations, or if we've reached the maximum iteration count. 00312 if (iter % reportFrequency == 0 || iter + 1 == niters) { 00313 resid.update (one, z, -lambda, q, zero); // z := A*q - lambda*q 00314 residual = resid.norm2(); // 2-norm of the residual vector 00315 out << "Iteration " << iter << ":" << endl 00316 << "- lambda = " << lambda << endl 00317 << "- ||A*q - lambda*q||_2 = " << residual << endl; 00318 } 00319 if (residual < tolerance) { 00320 out << "Converged after " << iter << " iterations" << endl; 00321 break; 00322 } else if (iter+1 == niters) { 00323 out << "Failed to converge after " << niters << " iterations" << endl; 00324 break; 00325 } 00326 } 00327 return lambda; 00328 }
1.7.6.1