EpetraExt  Development
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines
EpetraExt_BlockAdjacencyGraph.cpp
Go to the documentation of this file.
00001 //@HEADER
00002 // ***********************************************************************
00003 //
00004 //     EpetraExt: Epetra Extended - Linear Algebra Services Package
00005 //                 Copyright (2011) Sandia Corporation
00006 //
00007 // Under the terms of Contract DE-AC04-94AL85000 with Sandia Corporation,
00008 // the U.S. Government retains certain rights in this software.
00009 //
00010 // Redistribution and use in source and binary forms, with or without
00011 // modification, are permitted provided that the following conditions are
00012 // met:
00013 //
00014 // 1. Redistributions of source code must retain the above copyright
00015 // notice, this list of conditions and the following disclaimer.
00016 //
00017 // 2. Redistributions in binary form must reproduce the above copyright
00018 // notice, this list of conditions and the following disclaimer in the
00019 // documentation and/or other materials provided with the distribution.
00020 //
00021 // 3. Neither the name of the Corporation nor the names of the
00022 // contributors may be used to endorse or promote products derived from
00023 // this software without specific prior written permission.
00024 //
00025 // THIS SOFTWARE IS PROVIDED BY SANDIA CORPORATION "AS IS" AND ANY
00026 // EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00027 // IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
00028 // PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL SANDIA CORPORATION OR THE
00029 // CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
00030 // EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
00031 // PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
00032 // PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
00033 // LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
00034 // NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
00035 // SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00036 //
00037 // Questions? Contact Michael A. Heroux (maherou@sandia.gov)
00038 //
00039 // ***********************************************************************
00040 //@HEADER
00041 
00042 #include <EpetraExt_BlockAdjacencyGraph.h>
00043 
00044 #include <Epetra_CrsMatrix.h>
00045 #include <Epetra_CrsGraph.h>
00046 #include <Epetra_Map.h>
00047 #include <Epetra_Comm.h>
00048 
00049 #include <math.h>
00050 #include <cstdlib>
00051 
00052 namespace EpetraExt {
00053 
00054   int compare_ints(const void *a, const void *b)
00055   {
00056     return(*((int *) a) - *((int *) b));
00057   }
00058 
00059   int ceil31log2(int n)
00060   { // Given 1 <= n < 2^31, find l such that 2^(l-1) < n <= 2^(l)
00061     int l=0, m = 1;
00062     while ((n > m) && (l < 31)) {
00063       m = 2*m;
00064       ++l;
00065     }
00066     return(l);
00067   }
00068 //  Purpose: Compute the block connectivity graph of a matrix.
00069 //  An nrr by nrr sparse matrix admits a (Dulmage-Mendelsohn)
00070 //  permutation to block triangular form with nbrr blocks.
00071 //  Abstractly, the block triangular form corresponds to a partition
00072 //  of the set of integers {0,...,n-1} into nbrr disjoint sets.
00073 //  The graph of the sparse matrix, with nrr vertices, may be compressed
00074 //  into the graph of the blocks, a graph with nbrr vertices, that is
00075 //  called here the block connectivity graph.
00076 //     The partition of the rows and columns of B is represented by
00077 //  r(0:nbrr),  0 = r(0) < r(1) < .. < r(nbrr) = nrr,
00078 //  The graph (Mp,Mj) of the nbrr x nbrr matrix is represened by
00079 //  a sparse matrix in sparse coordinate format.
00080 //  Mp: row indices, dimension determined here (nzM).
00081 //  Mj: column indices, dimension determined here (nzM).
00082 //  The integer vector, weights, of block sizes  (dimension nbrr) is also
00083 //  computed here.
00084 //     The case of nbrr proportional to nrr is critical.  One must
00085 //  efficiently look up the column indices of B in the partition.
00086 //  This is done here using a binary search tree, so that the
00087 //  look up cost is nzB*log2(nbrr).
00088 
00089   template<typename int_type>
00090   Teuchos::RCP<Epetra_CrsGraph> BlockAdjacencyGraph::compute( Epetra_CrsGraph& B, int nbrr, std::vector<int>&r, std::vector<double>& weights, bool verbose)
00091   {
00092     // Check if the graph is on one processor.
00093     int myMatProc = -1, matProc = -1;
00094     int myPID = B.Comm().MyPID();
00095     for (int proc=0; proc<B.Comm().NumProc(); proc++)
00096       {
00097         if (B.NumGlobalEntries64() == B.NumMyEntries())
00098           myMatProc = myPID;
00099       }
00100     B.Comm().MaxAll( &myMatProc, &matProc, 1 );
00101 
00102     if( matProc == -1)
00103       { std::cout << "FAIL for Global!  All CrsGraph entries must be on one processor!\n"; abort(); }
00104 
00105     int i= 0, j = 0, k, l = 0, p, pm, q = -1, ns = 0;
00106     int tree_height;
00107     int error = -1;    /* error detected, possibly a problem with the input */
00108     (void) error; // silence "set but not used" warning
00109     int nrr;           /* number of rows in B */
00110     int nzM = 0;       /* number of edges in graph */
00111     int m = 0;         /* maximum number of nonzeros in any block row of B */
00112     int* colstack = 0; /* stack used to process each block row */
00113     int* bstree = 0;   /* binary search tree */
00114     std::vector<int_type> Mi, Mj, Mnum(nbrr+1,0);
00115     nrr = B.NumMyRows();
00116     if ( matProc == myPID )
00117     {
00118       if ( verbose ) { std::printf(" Matrix Size = %d      Number of Blocks = %d\n",nrr, nbrr); }
00119     }
00120     else
00121     {
00122       nrr = -1;     /* Prevent processor from doing any computations */
00123     }
00124     bstree = csr_bst(nbrr);  /* 0 : nbrr-1 */
00125     tree_height = ceil31log2(nbrr) + 1;
00126     error = -1;
00127 
00128     l = 0; j = 0; m = 0;
00129     for( i = 0; i < nrr; i++ ){
00130       if( i >= r[l+1] ){
00131         ++l;                 /* new block row */
00132         m = EPETRA_MAX(m,j) ;   /* nonzeros in block row */
00133         j = B.NumGlobalIndices(i);
00134       }else{
00135         j += B.NumGlobalIndices(i);
00136       }
00137     }
00138     /* one more time for the final block */
00139      m = EPETRA_MAX(m,j) ;   /* nonzeros in block row */
00140 
00141     colstack = (int*) malloc( EPETRA_MAX(m,1) * sizeof(int) );
00142     // The compressed graph is actually computed twice,
00143     // due to concerns about memory limitations.  First,
00144     // without memory allocation, just nzM is computed.
00145     // Next Mj is allocated. Then, the second time, the
00146     // arrays are actually populated.
00147     nzM = 0; q = -1; l = 0;
00148     int * indices;
00149     int numEntries;
00150     for( i = 0; i <= nrr; i++ ){
00151       if( i >= r[l+1] ){
00152         if( q > 0 ) std::qsort(colstack,q+1,sizeof(int),compare_ints); /* sort stack */
00153         if( q >= 0 ) ns = 1; /* l, colstack[0] M */
00154         for( j=1; j<=q ; j++ ){ /* delete copies */
00155           if( colstack[j] > colstack[j-1] ) ++ns;
00156         }
00157         nzM += ns; /*M->p[l+1] = M->p[l] + ns;*/
00158         ++l;
00159         q = -1;
00160       }
00161       if( i < nrr ){
00162         B.ExtractMyRowView( i, numEntries, indices );
00163         for( k = 0; k < numEntries; k++){
00164           j = indices[k];  ns = 0; p = 0;
00165           while( (r[bstree[p]] > j)  ||  (j >= r[bstree[p]+1])  ){
00166             if( r[bstree[p]] > j){
00167               p = 2*p+1;
00168             }else{
00169               if( r[bstree[p]+1] <= j) p = 2*p+2;
00170             }
00171             ++ns;
00172             if( p > nbrr || ns > tree_height ) {
00173               error = j;
00174               std::printf("error: p %d  nbrr %d  ns %d %d\n",p,nbrr,ns,j); break;
00175             }
00176           }
00177           colstack[++q] = bstree[p];
00178         }
00179         //if( error >-1 ){ std::printf("%d\n",error); break; }
00180         // p > nbrr is a fatal error that is ignored
00181       }
00182     }
00183 
00184     if ( matProc == myPID && verbose )
00185       std::printf("nzM =  %d \n", nzM );
00186     Mi.resize( nzM );
00187     Mj.resize( nzM );
00188     q = -1; l = 0; pm = -1;
00189     for( i = 0; i <= nrr; i++ ){
00190       if( i >= r[l+1] ){
00191         if( q > 0 ) std::qsort(colstack,q+1,sizeof(colstack[0]),compare_ints); /* sort stack */
00192         if( q >= 0 ){
00193           Mi[++pm] = l;
00194           Mj[pm] = colstack[0];
00195         }
00196         for( j=1; j<=q ; j++ ){ /* delete copies */
00197           if( colstack[j] > colstack[j-1] ){ /* l, colstack[j] */
00198             Mi[++pm] = l;
00199             Mj[pm] = colstack[j];
00200           }
00201         }
00202         ++l;
00203         Mnum[l] = pm + 1;
00204 
00205         /* sparse row format: M->p[l+1] = M->p[l] + ns; */
00206         q = -1;
00207       }
00208       if( i < nrr ){
00209         B.ExtractMyRowView( i, numEntries, indices );
00210         for( k = 0; k < numEntries; k++){
00211           j = indices[k]; ns = 0; p = 0;
00212           while( (r[bstree[p]] > j)  ||  (j >= r[bstree[p]+1])  ){
00213             if( r[bstree[p]] > j){
00214               p = 2*p+1;
00215             }else{
00216               if( r[bstree[p]+1] <= j) p = 2*p+2;
00217             }
00218             ++ns;
00219           }
00220           colstack[++q] = bstree[p];
00221         }
00222       }
00223     }
00224     if ( bstree ) free ( bstree );
00225     if ( colstack ) free( colstack );
00226 
00227     // Compute weights as number of rows in each block.
00228     weights.resize( nbrr );
00229     for( l=0; l<nbrr; l++) weights[l] = r[l+1] - r[l];
00230 
00231     // Compute Epetra_CrsGraph and return
00232     Teuchos::RCP<Epetra_Map> newMap;
00233     if ( matProc == myPID )
00234       newMap = Teuchos::rcp( new Epetra_Map(nbrr, nbrr, 0, B.Comm() ) );
00235     else
00236       newMap = Teuchos::rcp( new Epetra_Map( nbrr, 0, 0, B.Comm() ) );
00237     Teuchos::RCP<Epetra_CrsGraph> newGraph = Teuchos::rcp( new Epetra_CrsGraph( Copy, *newMap, 0 ) );
00238     for( l=0; l<newGraph->NumMyRows(); l++) {
00239       newGraph->InsertGlobalIndices( l, Mnum[l+1]-Mnum[l], &Mj[Mnum[l]] );
00240     }
00241     newGraph->FillComplete();
00242 
00243     return (newGraph);
00244   }
00245 
00246   Teuchos::RCP<Epetra_CrsGraph> BlockAdjacencyGraph::compute( Epetra_CrsGraph& B, int nbrr, std::vector<int>&r, std::vector<double>& weights, bool verbose) {
00247 #ifndef EPETRA_NO_32BIT_GLOBAL_INDICES
00248   if(B.RowMap().GlobalIndicesInt()) {
00249     return compute<int>(B, nbrr, r, weights, verbose);
00250   }
00251   else
00252 #endif
00253 #ifndef EPETRA_NO_64BIT_GLOBAL_INDICES
00254   if(B.RowMap().GlobalIndicesLongLong()) {
00255     return compute<long long>(B, nbrr, r, weights, verbose);
00256   }
00257   else
00258 #endif
00259     throw "EpetraExt::BlockAdjacencyGraph::compute: GlobalIndices type unknown";
00260   }
00261 
00262   /*
00263    * bst(n) returns the complete binary tree, stored in an integer array of dimension n.
00264    * index i has children 2i+1 and 2i+2, root index 0.
00265    * A binary search of a sorted n-array: find array(k)
00266    * i=0; k = tree(i);
00267    * if < array( k ), i = 2i+1;
00268    * elif > array( k ), i = 2i+2;
00269    * otherwise exit
00270    */
00271   int* BlockAdjacencyGraph::csr_bst( int n )
00272   {
00273     int i, l=1, m, nstack = 0, nexp=0, os, *array, *stack;
00274     int max_nstack = 0;
00275     if( n == 0 ) return(NULL);
00276     while( l <= n ){
00277       l = 2*l;
00278       ++nexp;
00279     } /* n < l = 2^nexp */
00280     array = (int *) malloc( n * sizeof(int) );
00281     stack = (int *) malloc(3*nexp * sizeof(int) );
00282     stack[3*nstack] = 0; stack[3*nstack+1] = 0; stack[3*nstack+2] = n;
00283     ++nstack;
00284     /*if( debug ) std::printf("stack : %d %d %d\n", stack[0] , stack[1], stack[2] );*/
00285     while( nstack > 0 ){
00286       --nstack;
00287       i = stack[3*nstack]; os = stack[3*nstack+1]; m = stack[3*nstack+2];
00288       array[i] = csr_bstrootindex(m) + os; /* 5 */
00289       if( 2*i+2 < n){   /* right child                4     3      1                      3 - 5 - 1     */
00290         stack[3*nstack] = 2*i+2; stack[3*nstack+1] = array[i] + 1 ; stack[3*nstack+2] = m-array[i]-1+os; /* 6 10 -3 */
00291         ++nstack;
00292       }
00293       if( 2*i+1 < n){   /* left  child */
00294         stack[3*nstack] = 2*i+1; stack[3*nstack+1] = os; stack[3*nstack+2] = array[i] - os; /* 5 4 5 */
00295         ++nstack;
00296       }
00297       if( nstack > max_nstack ) max_nstack =  nstack;
00298     }
00299     free( stack );
00300     return(array);
00301   }
00302 
00303   /*
00304     Given a complete binary search tree with n nodes, return
00305     the index of the root node.  A nodeless tree, n=0, has no
00306     root node (-1).  Nodes are indexed from 0 to n-1.
00307   */
00308   int BlockAdjacencyGraph::csr_bstrootindex( int n )
00309   {
00310     int l = 1, nexp = 0, i;
00311     if( n == 0) return(-1);
00312     while( l <= n ){
00313       l = 2*l;
00314       ++nexp;
00315     } /* n < l = 2^nexp */
00316     i = l/2 - 1;
00317     if(n<4) return(i);
00318     if (n-i < l/4)
00319       return( n - l/4  );
00320     else
00321       return(i);
00322   }
00323 
00324 } //namespace EpetraExt
00325 
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Defines