Tpetra Matrix/Vector Services  Version of the Day
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
lesson03_power_method.cpp
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 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines