1*ad608de0SBarry Smith #ifndef lint 2*ad608de0SBarry Smith static char vcid[] = "$Id: zerodiag.c,v 1.1 1994/03/18 00:27:07 gropp Exp $"; 3*ad608de0SBarry Smith #endif 4*ad608de0SBarry Smith 5*ad608de0SBarry Smith /* 6*ad608de0SBarry Smith This file contains routines to reorder a matrix so that the diagonal 7*ad608de0SBarry Smith elements meet a simple criteria. The most common use is expected to 8*ad608de0SBarry Smith be reordering matrices with zero diagonal elements for incomplete 9*ad608de0SBarry Smith factorizations. A pivoting (partial or pairwise) routine is planned. 10*ad608de0SBarry Smith */ 11*ad608de0SBarry Smith 12*ad608de0SBarry Smith #include "tools.h" 13*ad608de0SBarry Smith #include <math.h> 14*ad608de0SBarry Smith #include "sparse/spmat.h" 15*ad608de0SBarry Smith #include "sparse/sppriv.h" 16*ad608de0SBarry Smith #define SWAP(a,b) {int _t; _t = a; a = b; b = _t; } 17*ad608de0SBarry Smith #define SWAPD(a,b) {double _t; _t = a; a = b; b = _t; } 18*ad608de0SBarry Smith 19*ad608de0SBarry Smith int SpiZeroFindPre( ); 20*ad608de0SBarry Smith 21*ad608de0SBarry Smith /*@ 22*ad608de0SBarry Smith SpUnSymmetricReorderForZeroDiagonal - Reorder the matrix so that no 23*ad608de0SBarry Smith zeros (or small elements) are on the diagonal 24*ad608de0SBarry Smith 25*ad608de0SBarry Smith Input Parameters: 26*ad608de0SBarry Smith . mat - matrix to reorder 27*ad608de0SBarry Smith . atol - elements smaller than this in magnitude are candidates for 28*ad608de0SBarry Smith reordering 29*ad608de0SBarry Smith . rmap,cmap - row and column permutations. On entrance, they should be 30*ad608de0SBarry Smith either the identity ordering or copies of the matrix's 31*ad608de0SBarry Smith row and column permutations. On exit, they will be the 32*ad608de0SBarry Smith modified permutations. 33*ad608de0SBarry Smith 34*ad608de0SBarry Smith Error Conditions: 35*ad608de0SBarry Smith $ No memory 36*ad608de0SBarry Smith $ Unable to find suitable reording 37*ad608de0SBarry Smith 38*ad608de0SBarry Smith Notes: 39*ad608de0SBarry Smith This is not intended as a replacement for pivoting for matrices that 40*ad608de0SBarry Smith have ``bad'' structure; use the pivoting factorization routine in that 41*ad608de0SBarry Smith case. 42*ad608de0SBarry Smith 43*ad608de0SBarry Smith After this routine has been used, you MUST recompute the icolmap 44*ad608de0SBarry Smith mapping (this is the inverse column mapping; since this routine 45*ad608de0SBarry Smith modifies the column mapping, the inverse column mapping becomes 46*ad608de0SBarry Smith invalid. The reason that it is not "corrected" here is because 47*ad608de0SBarry Smith additional orderings can be applied before the inverse column 48*ad608de0SBarry Smith mapping is needed. THIS DECISION MAY CHANGE if it proves a poor 49*ad608de0SBarry Smith choice. Use the routine SpInverse to recompute the inverse column mapping. 50*ad608de0SBarry Smith 51*ad608de0SBarry Smith Algorithm: 52*ad608de0SBarry Smith Column pivoting is used. Choice of column is made by looking at the 53*ad608de0SBarry Smith non-zero elements in the row. This algorithm is simple and fast but 54*ad608de0SBarry Smith does NOT guarentee that a non-singular or well conditioned 55*ad608de0SBarry Smith principle submatrix will be produced. 56*ad608de0SBarry Smith @*/ 57*ad608de0SBarry Smith void SpUnSymmetricReorderForZeroDiagonal( mat, atol, rmap, cmap ) 58*ad608de0SBarry Smith SpMat *mat; 59*ad608de0SBarry Smith double atol; 60*ad608de0SBarry Smith int *rmap, *cmap; 61*ad608de0SBarry Smith { 62*ad608de0SBarry Smith int prow, k, nz, n, repl, *j, *col, *row; 63*ad608de0SBarry Smith double *v, repla; 64*ad608de0SBarry Smith 65*ad608de0SBarry Smith SPLITTOMAT(mat); 66*ad608de0SBarry Smith 67*ad608de0SBarry Smith col = cmap; 68*ad608de0SBarry Smith row = rmap; 69*ad608de0SBarry Smith n = mat->rows; 70*ad608de0SBarry Smith 71*ad608de0SBarry Smith for (prow=0; prow<n; prow++) { 72*ad608de0SBarry Smith SpScatterFromRow( mat, row[prow], &nz, &j, &v ); 73*ad608de0SBarry Smith for (k=0; k<nz; k++) 74*ad608de0SBarry Smith if (col[j[k]] == prow) break; 75*ad608de0SBarry Smith if (k >= nz || fabs(v[k]) <= atol) { 76*ad608de0SBarry Smith /* Element too small or zero; find the best candidate */ 77*ad608de0SBarry Smith repl = prow; 78*ad608de0SBarry Smith repla = (k >= nz) ? 0.0 : fabs(v[k]); 79*ad608de0SBarry Smith for (k=0; k<nz; k++) 80*ad608de0SBarry Smith if (col[j[k]] > prow && fabs(v[k]) > repla) { 81*ad608de0SBarry Smith repl = col[j[k]]; 82*ad608de0SBarry Smith repla = fabs(v[k]); 83*ad608de0SBarry Smith } 84*ad608de0SBarry Smith if (prow == repl) { 85*ad608de0SBarry Smith /* Now we need to look for an element that allows us 86*ad608de0SBarry Smith to pivot with a previous column. To do this, we need 87*ad608de0SBarry Smith to be sure that we don't introduce a zero in a previous 88*ad608de0SBarry Smith diagonal */ 89*ad608de0SBarry Smith if (!SpiZeroFindPre( mat, prow, row, col, repla, atol, 90*ad608de0SBarry Smith &repl, &repla )) { 91*ad608de0SBarry Smith SETERRC(1,"Can not reorder matrix"); 92*ad608de0SBarry Smith break; 93*ad608de0SBarry Smith } 94*ad608de0SBarry Smith } 95*ad608de0SBarry Smith SWAP(col[prow],col[repl]); 96*ad608de0SBarry Smith } 97*ad608de0SBarry Smith } 98*ad608de0SBarry Smith } 99*ad608de0SBarry Smith 100*ad608de0SBarry Smith /*@ 101*ad608de0SBarry Smith SpSymmetricReorderForZeroDiagonal - Reorder the matrix so that no 102*ad608de0SBarry Smith zeros (or small elements) are on the diagonal 103*ad608de0SBarry Smith 104*ad608de0SBarry Smith Input Parameters: 105*ad608de0SBarry Smith . mat - matrix to reorder 106*ad608de0SBarry Smith . atol - elements smaller than this in magnitude are candidates for 107*ad608de0SBarry Smith reordering 108*ad608de0SBarry Smith . rmap,cmap - row and column permutations. On entrance, they should be 109*ad608de0SBarry Smith either the identity ordering or copies of the matrix's 110*ad608de0SBarry Smith row and column permutations. On exit, they will be the 111*ad608de0SBarry Smith moidified permutations. 112*ad608de0SBarry Smith 113*ad608de0SBarry Smith Error Conditions: 114*ad608de0SBarry Smith $ No memory 115*ad608de0SBarry Smith $ Unable to find suitable reording 116*ad608de0SBarry Smith 117*ad608de0SBarry Smith Notes: 118*ad608de0SBarry Smith This is not intended as a replacement for pivoting for matrices that 119*ad608de0SBarry Smith have ``bad'' structure; use the pivoting factorization routine in that 120*ad608de0SBarry Smith case (those routines are not yet available). 121*ad608de0SBarry Smith 122*ad608de0SBarry Smith After this routine has been used, you MUST recompute the icolmap 123*ad608de0SBarry Smith mapping (this is the inverse column mapping; since this routine 124*ad608de0SBarry Smith modifies the column mapping, the inverse column mapping becomes 125*ad608de0SBarry Smith invalid. The reason that it is not "corrected" here is because 126*ad608de0SBarry Smith additional orderings can be applied before the inverse column 127*ad608de0SBarry Smith mapping is needed. THIS DECISION MAY CHANGE if it proves a poor 128*ad608de0SBarry Smith choice. Use the routine SpInverse to recompute the inverse column 129*ad608de0SBarry Smith mapping. 130*ad608de0SBarry Smith 131*ad608de0SBarry Smith Algorithm: 132*ad608de0SBarry Smith This is more complicated than the unsymmetric version, since a zero 133*ad608de0SBarry Smith diagonal element can not be moved off the diagonal by symmetric 134*ad608de0SBarry Smith permutations. 135*ad608de0SBarry Smith Rather, we try to insure that a zero diagonal will be subject to some 136*ad608de0SBarry Smith fill before it is encountered. We do this as follows 137*ad608de0SBarry Smith 138*ad608de0SBarry Smith For each zero diagonal element, look down that row for a non-zero entry 139*ad608de0SBarry Smith such that the diagonal under (in the same column) is non-zero. The 140*ad608de0SBarry Smith criteria is to pick the largest corresponding diagonal (other criteria 141*ad608de0SBarry Smith are possible, such as a balance or a "no-fill" estimate of the 142*ad608de0SBarry Smith resulting pivot sizes). To speed this computation up, we gather some 143*ad608de0SBarry Smith auxillery data: location of the diagonal elements and their values. 144*ad608de0SBarry Smith We have to update these structures as we choose a reordering. 145*ad608de0SBarry Smith @*/ 146*ad608de0SBarry Smith void SpSymmetricReorderForZeroDiagonal( mat, atol, rmap, cmap ) 147*ad608de0SBarry Smith SpMat *mat; 148*ad608de0SBarry Smith double atol; 149*ad608de0SBarry Smith int *rmap, *cmap; 150*ad608de0SBarry Smith { 151*ad608de0SBarry Smith int prow, k, nz, n, repl, *j, *col, *row, cloc; 152*ad608de0SBarry Smith double *v, repla; 153*ad608de0SBarry Smith double *diagv, offd, rval; 154*ad608de0SBarry Smith 155*ad608de0SBarry Smith SPLITTOMAT(mat); 156*ad608de0SBarry Smith 157*ad608de0SBarry Smith n = mat->rows; 158*ad608de0SBarry Smith col = cmap; 159*ad608de0SBarry Smith row = rmap; 160*ad608de0SBarry Smith diagv = (double *)MALLOC( n * sizeof(double) ); CHKPTR(diagv); 161*ad608de0SBarry Smith /* load the diagonal values */ 162*ad608de0SBarry Smith for (prow=0; prow<n; prow++) { 163*ad608de0SBarry Smith SpScatterFromRow( mat, row[prow], &nz, &j, &v ); 164*ad608de0SBarry Smith rval = 0.0; 165*ad608de0SBarry Smith for (k=0; k<nz; k++) 166*ad608de0SBarry Smith if (col[j[k]] == prow) { 167*ad608de0SBarry Smith rval = v[k]; 168*ad608de0SBarry Smith break; 169*ad608de0SBarry Smith } 170*ad608de0SBarry Smith if (k >= nz) { 171*ad608de0SBarry Smith /* No diagonal element. Add it */ 172*ad608de0SBarry Smith SpAddValue( mat, 0.0, prow, prow ); 173*ad608de0SBarry Smith } 174*ad608de0SBarry Smith diagv[row[prow]] = fabs(rval); 175*ad608de0SBarry Smith } 176*ad608de0SBarry Smith 177*ad608de0SBarry Smith for (prow=0; prow<n; prow++) { 178*ad608de0SBarry Smith if (diagv[prow] > atol) continue; 179*ad608de0SBarry Smith /* If we knew that the column indices in a row were sorted, we'd only 180*ad608de0SBarry Smith have to look at the values after the diagonal's location. Since 181*ad608de0SBarry Smith we are not making that assumption, we have to look at every element. 182*ad608de0SBarry Smith This lets us use this routine after applying a fill-reducing ordering 183*ad608de0SBarry Smith (though in that case, the diagonal elements should already have been 184*ad608de0SBarry Smith introduced into the structure). */ 185*ad608de0SBarry Smith /* For each column, look for the largest diagonal element */ 186*ad608de0SBarry Smith SpScatterFromRow( mat, row[prow], &nz, &j, &v ); 187*ad608de0SBarry Smith repl = prow; 188*ad608de0SBarry Smith repla = diagv[row[prow]]; 189*ad608de0SBarry Smith for (k=0; k<nz; k++) 190*ad608de0SBarry Smith if ((cloc = col[j[k]]) > prow && diagv[cloc] > repla) { 191*ad608de0SBarry Smith repl = j[k]; 192*ad608de0SBarry Smith repla = diagv[cloc]; 193*ad608de0SBarry Smith offd = v[k]; 194*ad608de0SBarry Smith } 195*ad608de0SBarry Smith if (prow == repl) { 196*ad608de0SBarry Smith SETERRC(1,"Can not reorder matrix"); 197*ad608de0SBarry Smith return; 198*ad608de0SBarry Smith } 199*ad608de0SBarry Smith /* Compute an estimate of the replaced diagonal */ 200*ad608de0SBarry Smith diagv[row[prow]] = fabs( offd * offd / repla ); 201*ad608de0SBarry Smith SWAPD(diagv[row[prow]],diagv[row[repl]]); 202*ad608de0SBarry Smith 203*ad608de0SBarry Smith SWAP(col[prow],col[repl]); 204*ad608de0SBarry Smith if (col != row) 205*ad608de0SBarry Smith SWAP(row[prow],row[repl]); 206*ad608de0SBarry Smith } 207*ad608de0SBarry Smith FREE(diagv); 208*ad608de0SBarry Smith } 209*ad608de0SBarry Smith 210*ad608de0SBarry Smith /* Given a current row and current permutation, find a column permutation 211*ad608de0SBarry Smith that removes a zero diagonal */ 212*ad608de0SBarry Smith int SpiZeroFindPre( mat, prow, row, col, repla, atol, rc, rcv ) 213*ad608de0SBarry Smith SpMat *mat; 214*ad608de0SBarry Smith int prow, *row, *col, *rc; 215*ad608de0SBarry Smith double repla, atol, *rcv; 216*ad608de0SBarry Smith { 217*ad608de0SBarry Smith int k, nz, repl, *j, kk, nnz, *jj; 218*ad608de0SBarry Smith double *v, *vv; 219*ad608de0SBarry Smith 220*ad608de0SBarry Smith SpScatterFromRow( mat, row[prow], &nz, &j, &v ); 221*ad608de0SBarry Smith for (k=0; k<nz; k++) { 222*ad608de0SBarry Smith if (col[j[k]] < prow && fabs(v[k]) > repla) { 223*ad608de0SBarry Smith /* See if this one will work */ 224*ad608de0SBarry Smith repl = col[j[k]]; 225*ad608de0SBarry Smith SpScatterFromRow( mat, row[repl], &nnz, &jj, &vv ); 226*ad608de0SBarry Smith for (kk=0; kk<nnz; kk++) { 227*ad608de0SBarry Smith if (col[jj[kk]] == prow && fabs(vv[kk]) > atol) { 228*ad608de0SBarry Smith *rcv = fabs(v[k]); 229*ad608de0SBarry Smith *rc = repl; 230*ad608de0SBarry Smith return 1; 231*ad608de0SBarry Smith } 232*ad608de0SBarry Smith } 233*ad608de0SBarry Smith } 234*ad608de0SBarry Smith } 235*ad608de0SBarry Smith return 0; 236*ad608de0SBarry Smith } 237