alpar@1
|
1 |
/* ========================================================================= */
|
alpar@1
|
2 |
/* === AMD_aat ============================================================= */
|
alpar@1
|
3 |
/* ========================================================================= */
|
alpar@1
|
4 |
|
alpar@1
|
5 |
/* ------------------------------------------------------------------------- */
|
alpar@1
|
6 |
/* AMD, Copyright (c) Timothy A. Davis, */
|
alpar@1
|
7 |
/* Patrick R. Amestoy, and Iain S. Duff. See ../README.txt for License. */
|
alpar@1
|
8 |
/* email: davis at cise.ufl.edu CISE Department, Univ. of Florida. */
|
alpar@1
|
9 |
/* web: http://www.cise.ufl.edu/research/sparse/amd */
|
alpar@1
|
10 |
/* ------------------------------------------------------------------------- */
|
alpar@1
|
11 |
|
alpar@1
|
12 |
/* AMD_aat: compute the symmetry of the pattern of A, and count the number of
|
alpar@1
|
13 |
* nonzeros each column of A+A' (excluding the diagonal). Assumes the input
|
alpar@1
|
14 |
* matrix has no errors, with sorted columns and no duplicates
|
alpar@1
|
15 |
* (AMD_valid (n, n, Ap, Ai) must be AMD_OK, but this condition is not
|
alpar@1
|
16 |
* checked).
|
alpar@1
|
17 |
*/
|
alpar@1
|
18 |
|
alpar@1
|
19 |
#include "amd_internal.h"
|
alpar@1
|
20 |
|
alpar@1
|
21 |
GLOBAL size_t AMD_aat /* returns nz in A+A' */
|
alpar@1
|
22 |
(
|
alpar@1
|
23 |
Int n,
|
alpar@1
|
24 |
const Int Ap [ ],
|
alpar@1
|
25 |
const Int Ai [ ],
|
alpar@1
|
26 |
Int Len [ ], /* Len [j]: length of column j of A+A', excl diagonal*/
|
alpar@1
|
27 |
Int Tp [ ], /* workspace of size n */
|
alpar@1
|
28 |
double Info [ ]
|
alpar@1
|
29 |
)
|
alpar@1
|
30 |
{
|
alpar@1
|
31 |
Int p1, p2, p, i, j, pj, pj2, k, nzdiag, nzboth, nz ;
|
alpar@1
|
32 |
double sym ;
|
alpar@1
|
33 |
size_t nzaat ;
|
alpar@1
|
34 |
|
alpar@1
|
35 |
#ifndef NDEBUG
|
alpar@1
|
36 |
AMD_debug_init ("AMD AAT") ;
|
alpar@1
|
37 |
for (k = 0 ; k < n ; k++) Tp [k] = EMPTY ;
|
alpar@1
|
38 |
ASSERT (AMD_valid (n, n, Ap, Ai) == AMD_OK) ;
|
alpar@1
|
39 |
#endif
|
alpar@1
|
40 |
|
alpar@1
|
41 |
if (Info != (double *) NULL)
|
alpar@1
|
42 |
{
|
alpar@1
|
43 |
/* clear the Info array, if it exists */
|
alpar@1
|
44 |
for (i = 0 ; i < AMD_INFO ; i++)
|
alpar@1
|
45 |
{
|
alpar@1
|
46 |
Info [i] = EMPTY ;
|
alpar@1
|
47 |
}
|
alpar@1
|
48 |
Info [AMD_STATUS] = AMD_OK ;
|
alpar@1
|
49 |
}
|
alpar@1
|
50 |
|
alpar@1
|
51 |
for (k = 0 ; k < n ; k++)
|
alpar@1
|
52 |
{
|
alpar@1
|
53 |
Len [k] = 0 ;
|
alpar@1
|
54 |
}
|
alpar@1
|
55 |
|
alpar@1
|
56 |
nzdiag = 0 ;
|
alpar@1
|
57 |
nzboth = 0 ;
|
alpar@1
|
58 |
nz = Ap [n] ;
|
alpar@1
|
59 |
|
alpar@1
|
60 |
for (k = 0 ; k < n ; k++)
|
alpar@1
|
61 |
{
|
alpar@1
|
62 |
p1 = Ap [k] ;
|
alpar@1
|
63 |
p2 = Ap [k+1] ;
|
alpar@1
|
64 |
AMD_DEBUG2 (("\nAAT Column: "ID" p1: "ID" p2: "ID"\n", k, p1, p2)) ;
|
alpar@1
|
65 |
|
alpar@1
|
66 |
/* construct A+A' */
|
alpar@1
|
67 |
for (p = p1 ; p < p2 ; )
|
alpar@1
|
68 |
{
|
alpar@1
|
69 |
/* scan the upper triangular part of A */
|
alpar@1
|
70 |
j = Ai [p] ;
|
alpar@1
|
71 |
if (j < k)
|
alpar@1
|
72 |
{
|
alpar@1
|
73 |
/* entry A (j,k) is in the strictly upper triangular part,
|
alpar@1
|
74 |
* add both A (j,k) and A (k,j) to the matrix A+A' */
|
alpar@1
|
75 |
Len [j]++ ;
|
alpar@1
|
76 |
Len [k]++ ;
|
alpar@1
|
77 |
AMD_DEBUG3 ((" upper ("ID","ID") ("ID","ID")\n", j,k, k,j));
|
alpar@1
|
78 |
p++ ;
|
alpar@1
|
79 |
}
|
alpar@1
|
80 |
else if (j == k)
|
alpar@1
|
81 |
{
|
alpar@1
|
82 |
/* skip the diagonal */
|
alpar@1
|
83 |
p++ ;
|
alpar@1
|
84 |
nzdiag++ ;
|
alpar@1
|
85 |
break ;
|
alpar@1
|
86 |
}
|
alpar@1
|
87 |
else /* j > k */
|
alpar@1
|
88 |
{
|
alpar@1
|
89 |
/* first entry below the diagonal */
|
alpar@1
|
90 |
break ;
|
alpar@1
|
91 |
}
|
alpar@1
|
92 |
/* scan lower triangular part of A, in column j until reaching
|
alpar@1
|
93 |
* row k. Start where last scan left off. */
|
alpar@1
|
94 |
ASSERT (Tp [j] != EMPTY) ;
|
alpar@1
|
95 |
ASSERT (Ap [j] <= Tp [j] && Tp [j] <= Ap [j+1]) ;
|
alpar@1
|
96 |
pj2 = Ap [j+1] ;
|
alpar@1
|
97 |
for (pj = Tp [j] ; pj < pj2 ; )
|
alpar@1
|
98 |
{
|
alpar@1
|
99 |
i = Ai [pj] ;
|
alpar@1
|
100 |
if (i < k)
|
alpar@1
|
101 |
{
|
alpar@1
|
102 |
/* A (i,j) is only in the lower part, not in upper.
|
alpar@1
|
103 |
* add both A (i,j) and A (j,i) to the matrix A+A' */
|
alpar@1
|
104 |
Len [i]++ ;
|
alpar@1
|
105 |
Len [j]++ ;
|
alpar@1
|
106 |
AMD_DEBUG3 ((" lower ("ID","ID") ("ID","ID")\n",
|
alpar@1
|
107 |
i,j, j,i)) ;
|
alpar@1
|
108 |
pj++ ;
|
alpar@1
|
109 |
}
|
alpar@1
|
110 |
else if (i == k)
|
alpar@1
|
111 |
{
|
alpar@1
|
112 |
/* entry A (k,j) in lower part and A (j,k) in upper */
|
alpar@1
|
113 |
pj++ ;
|
alpar@1
|
114 |
nzboth++ ;
|
alpar@1
|
115 |
break ;
|
alpar@1
|
116 |
}
|
alpar@1
|
117 |
else /* i > k */
|
alpar@1
|
118 |
{
|
alpar@1
|
119 |
/* consider this entry later, when k advances to i */
|
alpar@1
|
120 |
break ;
|
alpar@1
|
121 |
}
|
alpar@1
|
122 |
}
|
alpar@1
|
123 |
Tp [j] = pj ;
|
alpar@1
|
124 |
}
|
alpar@1
|
125 |
/* Tp [k] points to the entry just below the diagonal in column k */
|
alpar@1
|
126 |
Tp [k] = p ;
|
alpar@1
|
127 |
}
|
alpar@1
|
128 |
|
alpar@1
|
129 |
/* clean up, for remaining mismatched entries */
|
alpar@1
|
130 |
for (j = 0 ; j < n ; j++)
|
alpar@1
|
131 |
{
|
alpar@1
|
132 |
for (pj = Tp [j] ; pj < Ap [j+1] ; pj++)
|
alpar@1
|
133 |
{
|
alpar@1
|
134 |
i = Ai [pj] ;
|
alpar@1
|
135 |
/* A (i,j) is only in the lower part, not in upper.
|
alpar@1
|
136 |
* add both A (i,j) and A (j,i) to the matrix A+A' */
|
alpar@1
|
137 |
Len [i]++ ;
|
alpar@1
|
138 |
Len [j]++ ;
|
alpar@1
|
139 |
AMD_DEBUG3 ((" lower cleanup ("ID","ID") ("ID","ID")\n",
|
alpar@1
|
140 |
i,j, j,i)) ;
|
alpar@1
|
141 |
}
|
alpar@1
|
142 |
}
|
alpar@1
|
143 |
|
alpar@1
|
144 |
/* --------------------------------------------------------------------- */
|
alpar@1
|
145 |
/* compute the symmetry of the nonzero pattern of A */
|
alpar@1
|
146 |
/* --------------------------------------------------------------------- */
|
alpar@1
|
147 |
|
alpar@1
|
148 |
/* Given a matrix A, the symmetry of A is:
|
alpar@1
|
149 |
* B = tril (spones (A), -1) + triu (spones (A), 1) ;
|
alpar@1
|
150 |
* sym = nnz (B & B') / nnz (B) ;
|
alpar@1
|
151 |
* or 1 if nnz (B) is zero.
|
alpar@1
|
152 |
*/
|
alpar@1
|
153 |
|
alpar@1
|
154 |
if (nz == nzdiag)
|
alpar@1
|
155 |
{
|
alpar@1
|
156 |
sym = 1 ;
|
alpar@1
|
157 |
}
|
alpar@1
|
158 |
else
|
alpar@1
|
159 |
{
|
alpar@1
|
160 |
sym = (2 * (double) nzboth) / ((double) (nz - nzdiag)) ;
|
alpar@1
|
161 |
}
|
alpar@1
|
162 |
|
alpar@1
|
163 |
nzaat = 0 ;
|
alpar@1
|
164 |
for (k = 0 ; k < n ; k++)
|
alpar@1
|
165 |
{
|
alpar@1
|
166 |
nzaat += Len [k] ;
|
alpar@1
|
167 |
}
|
alpar@1
|
168 |
|
alpar@1
|
169 |
AMD_DEBUG1 (("AMD nz in A+A', excluding diagonal (nzaat) = %g\n",
|
alpar@1
|
170 |
(double) nzaat)) ;
|
alpar@1
|
171 |
AMD_DEBUG1 ((" nzboth: "ID" nz: "ID" nzdiag: "ID" symmetry: %g\n",
|
alpar@1
|
172 |
nzboth, nz, nzdiag, sym)) ;
|
alpar@1
|
173 |
|
alpar@1
|
174 |
if (Info != (double *) NULL)
|
alpar@1
|
175 |
{
|
alpar@1
|
176 |
Info [AMD_STATUS] = AMD_OK ;
|
alpar@1
|
177 |
Info [AMD_N] = n ;
|
alpar@1
|
178 |
Info [AMD_NZ] = nz ;
|
alpar@1
|
179 |
Info [AMD_SYMMETRY] = sym ; /* symmetry of pattern of A */
|
alpar@1
|
180 |
Info [AMD_NZDIAG] = nzdiag ; /* nonzeros on diagonal of A */
|
alpar@1
|
181 |
Info [AMD_NZ_A_PLUS_AT] = nzaat ; /* nonzeros in A+A' */
|
alpar@1
|
182 |
}
|
alpar@1
|
183 |
|
alpar@1
|
184 |
return (nzaat) ;
|
alpar@1
|
185 |
}
|