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