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 }