Amesos Package Browser (Single Doxygen Collection) Development
Amesos_TestSolver.cpp
Go to the documentation of this file.
00001 // @HEADER
00002 // ***********************************************************************
00003 // 
00004 //                Amesos: Direct Sparse Solver Package
00005 //                 Copyright (2004) 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307
00023 // USA
00024 // Questions? Contact Michael A. Heroux (maherou@sandia.gov) 
00025 // 
00026 // ***********************************************************************
00027 // @HEADER
00028 
00029 #include "Amesos_ConfigDefs.h"
00030 #include "Teuchos_ParameterList.hpp"
00031 #include <string>
00032 //  #include "Trilinos_Util_ReadTriples2Epetra.h"
00033 #include "Trilinos_Util.h"
00034 //  #include "Trilinos_Util_ReadMatrixMarket2Epetra.h"
00035 #include "Epetra_LocalMap.h"
00036 #include "Epetra_Map.h"
00037 #include "Epetra_Vector.h"
00038 #include "Epetra_Export.h"
00039 #include "Epetra_CrsMatrix.h"
00040 #include "Epetra_LinearProblem.h"
00041 #include "Amesos_Time.h"
00042 
00043 #ifdef TEST_SPOOLES
00044 #include "SpoolesOO.h"
00045 #endif
00046 #ifdef HAVE_AMESOS_SUPERLU
00047 #include "Amesos_Superlu.h"
00048 #endif
00049 #ifdef HAVE_AMESOS_SUPERLUDIST
00050 #include "Amesos_Superludist.h"
00051 #endif
00052 #ifdef HAVE_AMESOS_SLUD
00053 #include "SuperludistOO.h"
00054 #endif
00055 #ifdef HAVE_AMESOS_SLUS
00056 #include "Epetra_SLU.h"
00057 #endif
00058 #ifdef HAVE_AMESOS_SLUD2
00059 #include "Superludist2_OO.h"
00060 #endif
00061 #ifdef TEST_AZTEC
00062 #include "AztecOO.h"
00063 #endif
00064 #ifdef HAVE_AMESOS_DSCPACK
00065 #include "Amesos_Dscpack.h"
00066 #endif
00067 #ifdef HAVE_AMESOS_LAPACK
00068 #include "Amesos_Lapack.h"
00069 #endif
00070 #ifdef HAVE_AMESOS_UMFPACK
00071 #include "Amesos_Umfpack.h"
00072 #endif
00073 #ifdef HAVE_AMESOS_KLU
00074 #include "Amesos_Klu.h"
00075 #endif
00076 #ifdef HAVE_AMESOS_SCALAPACK
00077 #include "Amesos_Scalapack.h"
00078 #endif
00079 #ifdef HAVE_AMESOS_TAUCS
00080 #include "Amesos_Taucs.h"
00081 #endif
00082 #ifdef HAVE_AMESOS_PARDISO
00083 #include "Amesos_Pardiso.h"
00084 #endif
00085 #ifdef HAVE_AMESOS_PARAKLETE
00086 #include "Amesos_Paraklete.h"
00087 #endif
00088 #ifdef HAVE_AMESOS_MUMPS
00089 #include "Amesos_Mumps.h"
00090 #endif
00091 
00092 #include "Amesos_TestSolver.h"
00093 #include "CrsMatrixTranspose.h"
00094 #include "SparseDirectTimingVars.h" 
00095 
00096 //
00097 //  Amesos_TestSolver.cpp reads in a matrix in Harwell-Boeing format, 
00098 //  calls one of the sparse direct solvers and computes the error 
00099 //  and residual.  
00100 //
00101 //  It reads the matrix in on a single processor and can pass that
00102 //  matrix to the solver or it can convert that matrix to a 
00103 //  distributed matrix and pass the distributed matrix to the solver.
00104 //
00105 //  Amesos_TestSolver can test either A x = b or A^T x = b.
00106 //  This can be a bit confusing because sparse direct solvers 
00107 //  use compressed column storage - the transpose of Trilinos'
00108 //  sparse row storage.
00109 //
00110 //  Matrices:
00111 //    readA - Serial.  As read from the file.
00112 //    transposeA - Serial.  The transpose of readA.
00113 //    serialA - if (transpose) then transposeA else readA 
00114 //    distributedA - readA distributed to all processes
00115 //    passA - if ( distributed ) then distributedA else serialA
00116 //
00117 //
00118 
00119 int Amesos_TestSolver( Epetra_Comm &Comm, char *matrix_file, 
00120            SparseSolverType SparseSolver,
00121            bool transpose, 
00122            int special, AMESOS_MatrixType matrix_type ) {
00123 
00124 
00125   Epetra_Map * readMap;
00126   Epetra_CrsMatrix * readA; 
00127   Epetra_Vector * readx; 
00128   Epetra_Vector * readb;
00129   Epetra_Vector * readxexact;
00130    
00131   std::string FileName = matrix_file ;
00132   int FN_Size = FileName.size() ; 
00133   std::string LastFiveBytes = FileName.substr( EPETRA_MAX(0,FN_Size-5), FN_Size );
00134   std::string LastFourBytes = FileName.substr( EPETRA_MAX(0,FN_Size-4), FN_Size );
00135   bool NonContiguousMap = false; 
00136 
00137   if ( LastFiveBytes == ".triU" ) { 
00138     // Call routine to read in unsymmetric Triplet matrix
00139     NonContiguousMap = true; 
00140     EPETRA_CHK_ERR( Trilinos_Util_ReadTriples2Epetra( matrix_file, false, Comm, readMap, readA, readx, 
00141                   readb, readxexact, NonContiguousMap ) );
00142   } else {
00143     if ( LastFiveBytes == ".triS" ) { 
00144       NonContiguousMap = true; 
00145       // Call routine to read in symmetric Triplet matrix
00146       EPETRA_CHK_ERR( Trilinos_Util_ReadTriples2Epetra( matrix_file, true, Comm, readMap, readA, readx, 
00147               readb, readxexact, NonContiguousMap ) );
00148     } else {
00149       if (  LastFourBytes == ".mtx" ) { 
00150   EPETRA_CHK_ERR( Trilinos_Util_ReadMatrixMarket2Epetra( matrix_file, Comm, readMap, 
00151                      readA, readx, readb, readxexact) );
00152       } else {
00153   // Call routine to read in HB problem
00154   Trilinos_Util_ReadHb2Epetra( matrix_file, Comm, readMap, readA, readx, 
00155                  readb, readxexact) ;
00156       }
00157     }
00158   }
00159 
00160   Epetra_CrsMatrix transposeA(Copy, *readMap, 0);
00161   Epetra_CrsMatrix *serialA ; 
00162 
00163   if ( transpose ) {
00164     assert( CrsMatrixTranspose( readA, &transposeA ) == 0 ); 
00165     serialA = &transposeA ; 
00166   } else {
00167     serialA = readA ; 
00168   }
00169 
00170   Epetra_RowMatrix * passA = 0; 
00171   Epetra_Vector * passx = 0; 
00172   Epetra_Vector * passb = 0;
00173   Epetra_Vector * passxexact = 0;
00174   Epetra_Vector * passresid = 0;
00175   Epetra_Vector * passtmp = 0;
00176 
00177   // Create uniform distributed map
00178   Epetra_Map map(readMap->NumGlobalElements(), 0, Comm);
00179   Epetra_Map* map_;
00180 
00181   if( NonContiguousMap ) {
00182     //
00183     //  map gives us NumMyElements and MyFirstElement;
00184     //
00185     int NumGlobalElements =  readMap->NumGlobalElements();
00186     int NumMyElements = map.NumMyElements();
00187     int MyFirstElement = map.MinMyGID();
00188     std::vector<int> MapMap_( NumGlobalElements );
00189     readMap->MyGlobalElements( &MapMap_[0] ) ;
00190     Comm.Broadcast( &MapMap_[0], NumGlobalElements, 0 ) ; 
00191     map_ = new Epetra_Map( NumGlobalElements, NumMyElements, &MapMap_[MyFirstElement], 0, Comm);
00192   } else {
00193     map_ = new Epetra_Map( map ) ; 
00194   }
00195 
00196 
00197   Epetra_CrsMatrix A(Copy, *map_, 0);
00198 
00199 
00200   const Epetra_Map &OriginalMap = serialA->RowMatrixRowMap() ; 
00201   assert( OriginalMap.SameAs(*readMap) ); 
00202   Epetra_Export exporter(OriginalMap, *map_);
00203   Epetra_Export exporter2(OriginalMap, *map_);
00204   Epetra_Export MatrixExporter(OriginalMap, *map_);
00205   Epetra_CrsMatrix AwithDiag(Copy, *map_, 0);
00206 
00207   Epetra_Vector x(*map_);
00208   Epetra_Vector b(*map_);
00209   Epetra_Vector xexact(*map_);
00210   Epetra_Vector resid(*map_);
00211   Epetra_Vector readresid(*readMap);
00212   Epetra_Vector tmp(*map_);
00213   Epetra_Vector readtmp(*readMap);
00214 
00215   //  Epetra_Vector xcomp(*map_);      // X as computed by the solver
00216   bool distribute_matrix = ( matrix_type == AMESOS_Distributed ) ; 
00217   if ( distribute_matrix ) { 
00218     // Create Exporter to distribute read-in matrix and vectors
00219     //
00220     //  Initialize x, b and xexact to the values read in from the file
00221     //
00222     x.Export(*readx, exporter, Add);
00223     b.Export(*readb, exporter, Add);
00224     xexact.Export(*readxexact, exporter, Add);
00225     Comm.Barrier();
00226     
00227     A.Export(*serialA, exporter, Add);
00228     assert(A.FillComplete()==0);    
00229     
00230     Comm.Barrier();
00231 
00232     passA = &A; 
00233 
00234     passx = &x; 
00235     passb = &b;
00236     passxexact = &xexact;
00237     passresid = &resid;
00238     passtmp = &tmp;
00239 
00240   } else { 
00241 
00242     passA = serialA; 
00243     passx = readx; 
00244     passb = readb;
00245     passxexact = readxexact;
00246     passresid = &readresid;
00247     passtmp = &readtmp;
00248   }
00249 
00250   Epetra_MultiVector CopyB( *passb ) ;
00251 
00252 
00253   double Anorm = passA->NormInf() ; 
00254   SparseDirectTimingVars::SS_Result.Set_Anorm(Anorm) ;
00255 
00256   Epetra_LinearProblem Problem(  (Epetra_RowMatrix *) passA, 
00257          (Epetra_MultiVector *) passx, 
00258          (Epetra_MultiVector *) passb );
00259   
00260 
00261   for ( int i = 0; i < 1+special ; i++ ) { 
00262     Epetra_Time TotalTime( Comm ) ; 
00263     
00264     if ( false ) { 
00265       //  TEST_UMFPACK is never set by configure
00266 #ifdef HAVE_AMESOS_SUPERLUDIST
00267     } else if ( SparseSolver == SUPERLUDIST ) {
00268   Teuchos::ParameterList ParamList ;
00269   ParamList.set( "MaxProcs", -3 );
00270   Amesos_Superludist A_Superludist( Problem ) ; 
00271 
00272   //ParamList.set( "Redistribute", true );
00273   //ParamList.set( "AddZeroToDiag", true );
00274   Teuchos::ParameterList& SuperludistParams = ParamList.sublist("Superludist") ;
00275   ParamList.set( "MaxProcs", -3 );
00276 
00277   EPETRA_CHK_ERR( A_Superludist.SetParameters( ParamList ) ); 
00278   EPETRA_CHK_ERR( A_Superludist.SetUseTranspose( transpose ) ); 
00279   EPETRA_CHK_ERR( A_Superludist.SymbolicFactorization(  ) ); 
00280   EPETRA_CHK_ERR( A_Superludist.NumericFactorization(  ) ); 
00281   EPETRA_CHK_ERR( A_Superludist.Solve(  ) ); 
00282 #endif
00283 #ifdef HAVE_AMESOS_DSCPACK
00284     } else if ( SparseSolver == DSCPACK ) {
00285       
00286       Teuchos::ParameterList ParamList ;
00287       ParamList.set( "MaxProcs", -3 );
00288 
00289       Amesos_Dscpack A_dscpack( Problem ) ; 
00290       EPETRA_CHK_ERR( A_dscpack.SetParameters( ParamList ) ); 
00291       EPETRA_CHK_ERR( A_dscpack.SymbolicFactorization(  ) ); 
00292       EPETRA_CHK_ERR( A_dscpack.NumericFactorization(  ) ); 
00293       EPETRA_CHK_ERR( A_dscpack.Solve(  ) ); 
00294 #endif
00295 #ifdef HAVE_AMESOS_SCALAPACK
00296     } else if ( SparseSolver == SCALAPACK ) {
00297 
00298       Teuchos::ParameterList ParamList ;
00299       Amesos_Scalapack A_scalapack( Problem ) ; 
00300       ParamList.set( "MaxProcs", -3 );
00301       EPETRA_CHK_ERR( A_scalapack.SetParameters( ParamList ) ); 
00302       EPETRA_CHK_ERR( A_scalapack.SetUseTranspose( transpose ) ); 
00303       EPETRA_CHK_ERR( A_scalapack.SymbolicFactorization(  ) ); 
00304       EPETRA_CHK_ERR( A_scalapack.NumericFactorization(  ) ); 
00305       EPETRA_CHK_ERR( A_scalapack.Solve(  ) ); 
00306 
00307 #endif
00308 #ifdef HAVE_AMESOS_TAUCS
00309     } else if ( SparseSolver == TAUCS ) {
00310 
00311       Teuchos::ParameterList ParamList ;
00312       Amesos_Taucs A_taucs( Problem ) ; 
00313       ParamList.set( "MaxProcs", -3 );
00314       EPETRA_CHK_ERR( A_taucs.SetParameters( ParamList ) ); 
00315       EPETRA_CHK_ERR( A_taucs.SetUseTranspose( transpose ) ); 
00316       EPETRA_CHK_ERR( A_taucs.SymbolicFactorization(  ) ); 
00317       EPETRA_CHK_ERR( A_taucs.NumericFactorization(  ) ); 
00318       EPETRA_CHK_ERR( A_taucs.Solve(  ) ); 
00319 
00320 #endif
00321 #ifdef HAVE_AMESOS_PARDISO
00322     } else if ( SparseSolver == PARDISO ) {
00323 
00324       Teuchos::ParameterList ParamList ;
00325       Amesos_Pardiso A_pardiso( Problem ) ; 
00326       ParamList.set( "MaxProcs", -3 );
00327       EPETRA_CHK_ERR( A_pardiso.SetParameters( ParamList ) ); 
00328       EPETRA_CHK_ERR( A_pardiso.SetUseTranspose( transpose ) ); 
00329       EPETRA_CHK_ERR( A_pardiso.SymbolicFactorization(  ) ); 
00330       EPETRA_CHK_ERR( A_pardiso.NumericFactorization(  ) ); 
00331       EPETRA_CHK_ERR( A_pardiso.Solve(  ) ); 
00332 
00333 #endif
00334 #ifdef HAVE_AMESOS_PARAKLETE
00335     } else if ( SparseSolver == PARAKLETE ) {
00336 
00337       Teuchos::ParameterList ParamList ;
00338       Amesos_Paraklete A_paraklete( Problem ) ; 
00339       ParamList.set( "MaxProcs", -3 );
00340       EPETRA_CHK_ERR( A_paraklete.SetParameters( ParamList ) ); 
00341       EPETRA_CHK_ERR( A_paraklete.SetUseTranspose( transpose ) ); 
00342       EPETRA_CHK_ERR( A_paraklete.SymbolicFactorization(  ) ); 
00343       EPETRA_CHK_ERR( A_paraklete.NumericFactorization(  ) ); 
00344       EPETRA_CHK_ERR( A_paraklete.Solve(  ) ); 
00345 
00346 #endif
00347 #ifdef HAVE_AMESOS_MUMPS
00348     } else if ( SparseSolver == MUMPS ) {
00349 
00350       Teuchos::ParameterList ParamList ;
00351       Amesos_Mumps A_mumps( Problem ) ; 
00352       ParamList.set( "MaxProcs", -3 );
00353       EPETRA_CHK_ERR( A_mumps.SetParameters( ParamList ) ); 
00354       EPETRA_CHK_ERR( A_mumps.SetUseTranspose( transpose ) ); 
00355       EPETRA_CHK_ERR( A_mumps.SymbolicFactorization(  ) ); 
00356       EPETRA_CHK_ERR( A_mumps.NumericFactorization(  ) ); 
00357       EPETRA_CHK_ERR( A_mumps.Solve(  ) ); 
00358 
00359 #endif
00360 #ifdef HAVE_AMESOS_SUPERLU
00361     } else if ( SparseSolver == SUPERLU ) {
00362 
00363       Teuchos::ParameterList ParamList ;
00364       Amesos_Superlu A_superlu( Problem ) ; 
00365       ParamList.set( "MaxProcs", -3 );
00366       EPETRA_CHK_ERR( A_superlu.SetParameters( ParamList ) ); 
00367       EPETRA_CHK_ERR( A_superlu.SetUseTranspose( transpose ) ); 
00368       EPETRA_CHK_ERR( A_superlu.SymbolicFactorization(  ) ); 
00369       EPETRA_CHK_ERR( A_superlu.NumericFactorization(  ) ); 
00370       EPETRA_CHK_ERR( A_superlu.Solve(  ) ); 
00371 
00372 #endif
00373 #ifdef HAVE_AMESOS_LAPACK
00374     } else if ( SparseSolver == LAPACK ) {
00375 
00376       Teuchos::ParameterList ParamList ;
00377       Amesos_Lapack A_lapack( Problem ) ; 
00378       ParamList.set( "MaxProcs", -3 );
00379       EPETRA_CHK_ERR( A_lapack.SetParameters( ParamList ) ); 
00380       EPETRA_CHK_ERR( A_lapack.SetUseTranspose( transpose ) ); 
00381       EPETRA_CHK_ERR( A_lapack.SymbolicFactorization(  ) ); 
00382       EPETRA_CHK_ERR( A_lapack.NumericFactorization(  ) ); 
00383       EPETRA_CHK_ERR( A_lapack.Solve(  ) ); 
00384 #endif
00385 #ifdef HAVE_AMESOS_UMFPACK
00386     } else if ( SparseSolver == UMFPACK ) {
00387 
00388       Teuchos::ParameterList ParamList ;
00389       Amesos_Umfpack A_umfpack( Problem ) ; 
00390       ParamList.set( "MaxProcs", -3 );
00391       EPETRA_CHK_ERR( A_umfpack.SetParameters( ParamList ) ); 
00392       EPETRA_CHK_ERR( A_umfpack.SetUseTranspose( transpose ) ); 
00393       EPETRA_CHK_ERR( A_umfpack.SymbolicFactorization(  ) ); 
00394       EPETRA_CHK_ERR( A_umfpack.NumericFactorization(  ) ); 
00395       EPETRA_CHK_ERR( A_umfpack.Solve(  ) ); 
00396 #endif
00397 #ifdef HAVE_AMESOS_KLU
00398     } else if ( SparseSolver == KLU ) {
00399 
00400 
00401       using namespace Teuchos;
00402 
00403       Amesos_Time AT; 
00404       int setupTimePtr = -1, symTimePtr = -1, numTimePtr = -1, refacTimePtr = -1, solveTimePtr = -1;
00405       AT.CreateTimer(Comm, 2);
00406       AT.ResetTimer(0);
00407 
00408       Teuchos::ParameterList ParamList ;
00409       // ParamList.set("OutputLevel",2);
00410       Amesos_Klu A_klu( Problem ); 
00411       ParamList.set( "MaxProcs", -3 );
00412       ParamList.set( "TrustMe", false );
00413       // ParamList.set( "Refactorize", true );
00414       EPETRA_CHK_ERR( A_klu.SetParameters( ParamList ) ) ; 
00415       EPETRA_CHK_ERR( A_klu.SetUseTranspose( transpose ) ); 
00416       setupTimePtr = AT.AddTime("Setup", setupTimePtr, 0);
00417       EPETRA_CHK_ERR( A_klu.SymbolicFactorization(  ) ); 
00418       symTimePtr = AT.AddTime("Symbolic", symTimePtr, 0);
00419       EPETRA_CHK_ERR( A_klu.NumericFactorization(  ) ); 
00420       numTimePtr = AT.AddTime("Numeric", numTimePtr, 0);
00421       EPETRA_CHK_ERR( A_klu.NumericFactorization(  ) ); 
00422       refacTimePtr = AT.AddTime("Refactor", refacTimePtr, 0);
00423       // for ( int i=0; i<100000 ; i++ ) 
00424       EPETRA_CHK_ERR( A_klu.Solve(  ) ); 
00425       solveTimePtr = AT.AddTime("Solve", solveTimePtr, 0);
00426 
00427       double SetupTime = AT.GetTime(setupTimePtr);
00428       double SymbolicTime = AT.GetTime(symTimePtr);
00429       double NumericTime = AT.GetTime(numTimePtr);
00430       double RefactorTime = AT.GetTime(refacTimePtr);
00431       double SolveTime = AT.GetTime(solveTimePtr);
00432 
00433       std::cout << __FILE__ << "::"  << __LINE__ << " SetupTime = " << SetupTime << std::endl ; 
00434       std::cout << __FILE__ << "::"  << __LINE__ << " SymbolicTime = " << SymbolicTime - SetupTime << std::endl ; 
00435       std::cout << __FILE__ << "::"  << __LINE__ << " NumericTime = " << NumericTime - SymbolicTime<< std::endl ; 
00436       std::cout << __FILE__ << "::"  << __LINE__ << " RefactorTime = " << RefactorTime - NumericTime << std::endl ; 
00437       std::cout << __FILE__ << "::"  << __LINE__ << " SolveTime = " << SolveTime - RefactorTime << std::endl ; 
00438 
00439 #endif
00440     } else { 
00441       SparseDirectTimingVars::log_file << "Solver not implemented yet" << std::endl ;
00442       std::cerr << "\n\n####################  Requested solver not available on this platform ##################### ATS\n" << std::endl ;
00443       std::cout << " SparseSolver = " << SparseSolver << std::endl ; 
00444       std::cerr << " SparseSolver = " << SparseSolver << std::endl ; 
00445     }
00446     
00447     SparseDirectTimingVars::SS_Result.Set_Total_Time( TotalTime.ElapsedTime() ); 
00448   }  // end for (int i=0; i<special; i++ ) 
00449 
00450   //
00451   //  Compute the error = norm(xcomp - xexact )
00452   //
00453   double error;
00454   passresid->Update(1.0, *passx, -1.0, *passxexact, 0.0);
00455 
00456   passresid->Norm2(&error);
00457   SparseDirectTimingVars::SS_Result.Set_Error(error) ;
00458 
00459   //  passxexact->Norm2(&error ) ; 
00460   //  passx->Norm2(&error ) ; 
00461 
00462   //
00463   //  Compute the residual = norm(Ax - b)
00464   //
00465   double residual ; 
00466 
00467   passA->Multiply( transpose, *passx, *passtmp);
00468   passresid->Update(1.0, *passtmp, -1.0, *passb, 0.0); 
00469   //  passresid->Update(1.0, *passtmp, -1.0, CopyB, 0.0); 
00470   passresid->Norm2(&residual);
00471 
00472   SparseDirectTimingVars::SS_Result.Set_Residual(residual) ;
00473     
00474   double bnorm; 
00475   passb->Norm2( &bnorm ) ; 
00476   SparseDirectTimingVars::SS_Result.Set_Bnorm(bnorm) ;
00477 
00478   double xnorm; 
00479   passx->Norm2( &xnorm ) ; 
00480   SparseDirectTimingVars::SS_Result.Set_Xnorm(xnorm) ;
00481 
00482   delete readA;
00483   delete readx;
00484   delete readb;
00485   delete readxexact;
00486   delete readMap;
00487   delete map_;
00488   
00489   Comm.Barrier();
00490 
00491   return 0;
00492 }
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines