xref: /petsc/src/mat/utils/zerodiag.c (revision ad608de0742997380a6eca9f09692878104d9936)
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