shared_ptr<Epetra_CrsMatrix> sparseCholesky(const Epetra_CrsMatrix &mat) { // Note: we assume the matrix mat is symmetric and positive-definite size_t size = mat.NumGlobalCols(); if (mat.NumGlobalRows() != size) throw std::invalid_argument("sparseCholesky(): matrix must be square"); int *rowOffsets = 0; int *colIndices = 0; double *values = 0; mat.ExtractCrsDataPointers(rowOffsets, colIndices, values); Epetra_SerialComm comm; Epetra_LocalMap rowMap(static_cast<int>(size), 0 /* index_base */, comm); Epetra_LocalMap columnMap(static_cast<int>(size), 0 /* index_base */, comm); shared_ptr<Epetra_CrsMatrix> result = boost::make_shared<Epetra_CrsMatrix>( Copy, rowMap, columnMap, mat.GlobalMaxNumEntries()); arma::Mat<double> localMat; arma::Mat<double> localCholesky; std::vector<bool> processed(size, false); for (size_t r = 0; r < size; ++r) { if (processed[r]) continue; int localSize = rowOffsets[r + 1] - rowOffsets[r]; localMat.set_size(localSize, localSize); localMat.fill(0.); localCholesky.set_size(localSize, localSize); for (int s = 0; s < localSize; ++s) { int row = colIndices[rowOffsets[r] + s]; for (int c = 0; c < localSize; ++c) { int col = colIndices[rowOffsets[row] + c]; if (col != colIndices[rowOffsets[r] + c]) throw std::invalid_argument("sparseCholesky(): matrix is not " "block-diagonal"); localMat(s, c) = values[rowOffsets[row] + c]; } } assert(arma::norm(localMat - localMat.t(), "fro") < 1e-12 * arma::norm(localMat, "fro")); localCholesky = arma::chol(localMat); // localCholesky: U for (int s = 0; s < localSize; ++s) { int row = colIndices[rowOffsets[r] + s]; processed[row] = true; #ifndef NDEBUG int errorCode = #endif result->InsertGlobalValues(row, s + 1 /* number of values */, localCholesky.colptr(s), colIndices + rowOffsets[r]); assert(errorCode == 0); } } result->FillComplete(columnMap, rowMap); return result; }
bool CrsMatrixInfo( const Epetra_CrsMatrix & A, ostream & os ) { int MyPID = A.Comm().MyPID(); // take care that matrix is already trasformed bool IndicesAreGlobal = A.IndicesAreGlobal(); if( IndicesAreGlobal == true ) { if( MyPID == 0 ) { os << "WARNING : matrix must be transformed to local\n"; os << " before calling CrsMatrixInfo\n"; os << " Now returning...\n"; } return false; } int NumGlobalRows = A.NumGlobalRows(); int NumGlobalNonzeros = A.NumGlobalNonzeros(); int NumGlobalCols = A.NumGlobalCols(); double NormInf = A.NormInf(); double NormOne = A.NormOne(); int NumGlobalDiagonals = A.NumGlobalDiagonals(); int GlobalMaxNumEntries = A.GlobalMaxNumEntries(); int IndexBase = A.IndexBase(); bool StorageOptimized = A.StorageOptimized(); bool LowerTriangular = A.LowerTriangular(); bool UpperTriangular = A.UpperTriangular(); bool NoDiagonal = A.NoDiagonal(); // these variables identifies quantities I have to compute, // since not provided by Epetra_CrsMatrix double MyFrobeniusNorm( 0.0 ), FrobeniusNorm( 0.0 ); double MyMinElement( DBL_MAX ), MinElement( DBL_MAX ); double MyMaxElement( DBL_MIN ), MaxElement( DBL_MIN ); double MyMinAbsElement( DBL_MAX ), MinAbsElement( DBL_MAX ); double MyMaxAbsElement( 0.0 ), MaxAbsElement( 0.0 ); int NumMyRows = A.NumMyRows(); int * NzPerRow = new int[NumMyRows]; int Row; // iterator on rows int Col; // iterator on cols int MaxNumEntries = A.MaxNumEntries(); double * Values = new double[MaxNumEntries]; int * Indices = new int[MaxNumEntries]; double Element, AbsElement; // generic nonzero element and its abs value int NumEntries; double * Diagonal = new double [NumMyRows]; // SumOffDiagonal is the sum of absolute values for off-diagonals double * SumOffDiagonal = new double [NumMyRows]; for( Row=0 ; Row<NumMyRows ; ++Row ) { SumOffDiagonal[Row] = 0.0; } int * IsDiagonallyDominant = new int [NumMyRows]; int GlobalRow; // cycle over all matrix elements for( Row=0 ; Row<NumMyRows ; ++Row ) { GlobalRow = A.GRID(Row); NzPerRow[Row] = A.NumMyEntries(Row); A.ExtractMyRowCopy(Row,NzPerRow[Row],NumEntries,Values,Indices); for( Col=0 ; Col<NumEntries ; ++Col ) { Element = Values[Col]; AbsElement = abs(Element); if( Element<MyMinElement ) MyMinElement = Element; if( Element>MyMaxElement ) MyMaxElement = Element; if( AbsElement<MyMinAbsElement ) MyMinAbsElement = AbsElement; if( AbsElement>MyMaxAbsElement ) MyMaxAbsElement = AbsElement; if( Indices[Col] == Row ) Diagonal[Row] = Element; else SumOffDiagonal[Row] += abs(Element); MyFrobeniusNorm += pow(Element,2); } } // analise storage per row int MyMinNzPerRow( NumMyRows ), MinNzPerRow( NumMyRows ); int MyMaxNzPerRow( 0 ), MaxNzPerRow( 0 ); for( Row=0 ; Row<NumMyRows ; ++Row ) { if( NzPerRow[Row]<MyMinNzPerRow ) MyMinNzPerRow=NzPerRow[Row]; if( NzPerRow[Row]>MyMaxNzPerRow ) MyMaxNzPerRow=NzPerRow[Row]; } // a test to see if matrix is diagonally-dominant int MyDiagonalDominance( 0 ), DiagonalDominance( 0 ); int MyWeakDiagonalDominance( 0 ), WeakDiagonalDominance( 0 ); for( Row=0 ; Row<NumMyRows ; ++Row ) { if( abs(Diagonal[Row])>SumOffDiagonal[Row] ) ++MyDiagonalDominance; else if( abs(Diagonal[Row])==SumOffDiagonal[Row] ) ++MyWeakDiagonalDominance; } // reduction operations A.Comm().SumAll(&MyFrobeniusNorm, &FrobeniusNorm, 1); A.Comm().MinAll(&MyMinElement, &MinElement, 1); A.Comm().MaxAll(&MyMaxElement, &MaxElement, 1); A.Comm().MinAll(&MyMinAbsElement, &MinAbsElement, 1); A.Comm().MaxAll(&MyMaxAbsElement, &MaxAbsElement, 1); A.Comm().MinAll(&MyMinNzPerRow, &MinNzPerRow, 1); A.Comm().MaxAll(&MyMaxNzPerRow, &MaxNzPerRow, 1); A.Comm().SumAll(&MyDiagonalDominance, &DiagonalDominance, 1); A.Comm().SumAll(&MyWeakDiagonalDominance, &WeakDiagonalDominance, 1); // free memory delete Values; delete Indices; delete Diagonal; delete SumOffDiagonal; delete IsDiagonallyDominant; delete NzPerRow; // simply no output for MyPID>0, only proc 0 write on os if( MyPID != 0 ) return true; os << "*** general Information about the matrix\n"; os << "Number of Global Rows = " << NumGlobalRows << endl; os << "Number of Global Cols = " << NumGlobalCols << endl; os << "is the matrix square = " << ((NumGlobalRows==NumGlobalCols)?"yes":"no") << endl; os << "||A||_\\infty = " << NormInf << endl; os << "||A||_1 = " << NormOne << endl; os << "||A||_F = " << sqrt(FrobeniusNorm) << endl; os << "Number of nonzero diagonal entries = " << NumGlobalDiagonals << "( " << 1.0* NumGlobalDiagonals/NumGlobalRows*100 << " %)\n"; os << "Nonzero per row : min = " << MinNzPerRow << " average = " << 1.0*NumGlobalNonzeros/NumGlobalRows << " max = " << MaxNzPerRow << endl; os << "Maximum number of nonzero elements/row = " << GlobalMaxNumEntries << endl; os << "min( a_{i,j} ) = " << MinElement << endl; os << "max( a_{i,j} ) = " << MaxElement << endl; os << "min( abs(a_{i,j}) ) = " << MinAbsElement << endl; os << "max( abs(a_{i,j}) ) = " << MaxAbsElement << endl; os << "Number of diagonal dominant rows = " << DiagonalDominance << " (" << 100.0*DiagonalDominance/NumGlobalRows << " % of total)\n"; os << "Number of weakly diagonal dominant rows = " << WeakDiagonalDominance << " (" << 100.0*WeakDiagonalDominance/NumGlobalRows << " % of total)\n"; os << "*** Information about the Trilinos storage\n"; os << "Base Index = " << IndexBase << endl; os << "is storage optimized = " << ((StorageOptimized==true)?"yes":"no") << endl; os << "are indices global = " << ((IndicesAreGlobal==true)?"yes":"no") << endl; os << "is matrix lower triangular = " << ((LowerTriangular==true)?"yes":"no") << endl; os << "is matrix upper triangular = " << ((UpperTriangular==true)?"yes":"no") << endl; os << "are there diagonal entries = " << ((NoDiagonal==false)?"yes":"no") << endl; return true; }
// rebuild a single subblock Epetra_CrsMatrix void rebuildSubBlock(int i,int j,const Epetra_CrsMatrix & A,const std::vector<std::pair<int,RCP<Epetra_Map> > > & subMaps,Epetra_CrsMatrix & mat) { // get the number of variables families int numVarFamily = subMaps.size(); TEUCHOS_ASSERT(i>=0 && i<numVarFamily); TEUCHOS_ASSERT(j>=0 && j<numVarFamily); TEUCHOS_ASSERT(mat.Filled()); const Epetra_Map & gRowMap = *subMaps[i].second; const Epetra_Map & rowMap = *Teuchos::get_extra_data<RCP<Epetra_Map> >(subMaps[i].second,"contigMap"); int colFamilyCnt = subMaps[j].first; // compute the number of global variables // and the row and column block offset int numGlobalVars = 0; int rowBlockOffset = 0; int colBlockOffset = 0; for(int k=0;k<numVarFamily;k++) { numGlobalVars += subMaps[k].first; // compute block offsets if(k<i) rowBlockOffset += subMaps[k].first; if(k<j) colBlockOffset += subMaps[k].first; } // copy all global rows to here Epetra_Import import(gRowMap,A.RowMap()); Epetra_CrsMatrix localA(Copy,gRowMap,0); localA.Import(A,import,Insert); // clear out the old matrix mat.PutScalar(0.0); // get entry information int numMyRows = rowMap.NumMyElements(); int maxNumEntries = A.GlobalMaxNumEntries(); // for extraction std::vector<int> indices(maxNumEntries); std::vector<double> values(maxNumEntries); // for insertion std::vector<int> colIndices(maxNumEntries); std::vector<double> colValues(maxNumEntries); // insert each row into subblock // let FillComplete handle column distribution for(int localRow=0;localRow<numMyRows;localRow++) { int numEntries = -1; int globalRow = gRowMap.GID(localRow); int contigRow = rowMap.GID(localRow); TEUCHOS_ASSERT(globalRow>=0); TEUCHOS_ASSERT(contigRow>=0); // extract a global row copy int err = localA.ExtractGlobalRowCopy(globalRow, maxNumEntries, numEntries, &values[0], &indices[0]); TEUCHOS_ASSERT(err==0); int numOwnedCols = 0; for(int localCol=0;localCol<numEntries;localCol++) { int globalCol = indices[localCol]; // determinate which block this column ID is in int block = globalCol / numGlobalVars; bool inFamily = true; // test the beginning of the block inFamily &= (block*numGlobalVars+colBlockOffset <= globalCol); inFamily &= ((block*numGlobalVars+colBlockOffset+colFamilyCnt) > globalCol); // is this column in the variable family if(inFamily) { int familyOffset = globalCol-(block*numGlobalVars+colBlockOffset); colIndices[numOwnedCols] = block*colFamilyCnt + familyOffset; colValues[numOwnedCols] = values[localCol]; numOwnedCols++; } } // insert it into the new matrix mat.SumIntoGlobalValues(contigRow,numOwnedCols,&colValues[0],&colIndices[0]); } }
// int // main(int argc, char * argv[]) TEUCHOS_UNIT_TEST(tALOperator, test) { // Build communicator #ifdef HAVE_MPI Epetra_MpiComm Comm(MPI_COMM_WORLD); #else Epetra_SerialComm Comm; #endif // Get process information int myPID = Comm.MyPID(); out << "MPI_PID = " << myPID << ", UNIX_PID = " << getpid() << std::endl; // Maps. int dim = 2, numVel = 3, numPre = 2, errCode; Epetra_Map mapVel(numVel, 0, Comm), mapPre(numPre, 0, Comm); Epetra_Map mapAll(numVel * dim + numPre, 0, Comm); // Reorder. std::vector<int> reorderedVec; int numMyLen = mapVel.NumMyElements(); int * myGlb; myGlb = mapVel.MyGlobalElements(); for(int i = 0; i < dim; i++) for(int j = 0; j < numMyLen; j++) reorderedVec.push_back(myGlb[j] + numVel * i); numMyLen = mapPre.NumMyElements(); myGlb = mapPre.MyGlobalElements(); for(int j = 0; j < numMyLen; j++) reorderedVec.push_back(myGlb[j] + numVel * dim); Teuchos::RCP < Epetra_Map > mapReorder = Teuchos::rcp( new Epetra_Map(-1, reorderedVec.size(), &reorderedVec[0], 0, Comm)); Teuchos::RCP < Epetra_Import > importReorder = Teuchos::rcp(new Epetra_Import(*mapReorder, mapAll)); std::vector<std::vector<int> > blockedVec; numMyLen = mapVel.NumMyElements(); myGlb = mapVel.MyGlobalElements(); for(int i = 0; i < dim; i++) { reorderedVec.clear(); for(int j = 0; j < numMyLen; j++) reorderedVec.push_back(myGlb[j] + numVel * i); blockedVec.push_back(reorderedVec); } numMyLen = mapPre.NumMyElements(); myGlb = mapPre.MyGlobalElements(); reorderedVec.clear(); for(int j = 0; j < numMyLen; j++) reorderedVec.push_back(myGlb[j] + numVel * dim); blockedVec.push_back(reorderedVec); // Read matrices and vector. Epetra_CrsMatrix *ptrMat = 0, *ptrMp = 0; TEUCHOS_ASSERT(EpetraExt::MatrixMarketFileToCrsMatrix("data/tOpMat.mm", mapAll, ptrMat)==0); TEUCHOS_ASSERT(EpetraExt::MatrixMarketFileToCrsMatrix("data/tOpMp.mm", mapPre, ptrMp)==0); LinearOp lpMp = Thyra::epetraLinearOp(Teuchos::rcpFromRef(*ptrMp)); // This vector is computed by Matlab for comparison. Epetra_Vector *ptrExact = 0; TEUCHOS_ASSERT(EpetraExt::MatrixMarketFileToVector("data/tOpRhs.mm", mapAll, ptrExact)==0); // Reorder matrix. RCP < Epetra_CrsMatrix > mat = Teuchos::rcp(new Epetra_CrsMatrix(Copy, *mapReorder, ptrMat->GlobalMaxNumEntries())); errCode = mat->Import(*ptrMat, *importReorder, Insert); errCode = mat->FillComplete(); // Build augmented Lagrangian-based operator. Teko::NS::ALOperator al(blockedVec, mat, lpMp); // Initialize vectors. Epetra_Vector x(*mapReorder, false), b(*mapReorder, false); x.PutScalar(1.0); b.PutScalar(0.0); // Apply operator. al.Apply(x, b); // Compare computed vector and exact vector. b.Update(-1.0, *ptrExact, 1.0); double norm2; b.Norm2(&norm2); if(norm2 < 1.0e-15) { out << "Test:ALOperator: Passed." << std::endl; errCode = 0; } else { out << "Test:ALOperator: Failed." << std::endl; errCode = -1; } delete ptrMat; delete ptrMp; delete ptrExact; TEST_ASSERT(errCode==0); }