public void TestEmptyTranspose(int rows, int columns) { var A = new SymbolicColumnStorage(rows, columns, 0, true); var B = A.Transpose(); Assert.IsNotNull(B); }
// breadth-first search for coarse decomposition (C0,C1,R1 or R0,R3,C3) private bool BreadthFirstSearch(SymbolicColumnStorage A, int n, int[] wi, int[] wj, int[] queue, int[] jimatch, int imatch_offset, int jmatch_offset, int mark) { // cs_bfs int[] Ap, Ai; int head = 0, tail = 0, j, i, p, j2; for (j = 0; j < n; j++) // place all unmatched nodes in queue { if (jimatch[imatch_offset + j] >= 0) { continue; // skip j if matched } wj[j] = 0; // j in set C0 (R0 if transpose) queue[tail++] = j; // place unmatched col j in queue } if (tail == 0) { return(true); // quick return if no unmatched nodes } // Transpose if requested SymbolicColumnStorage C = (mark == 1) ? A.Clone() : A.Transpose(); if (C == null) { return(false); // bfs of C=A' to find R3,C3 from R0 } Ap = C.ColumnPointers; Ai = C.RowIndices; while (head < tail) // while queue is not empty { j = queue[head++]; // get the head of the queue for (p = Ap[j]; p < Ap[j + 1]; p++) { i = Ai[p]; if (wi[i] >= 0) { continue; // skip if i is marked } wi[i] = mark; // i in set R1 (C3 if transpose) j2 = jimatch[jmatch_offset + i]; // traverse alternating path to j2 if (wj[j2] >= 0) { continue; // skip j2 if it is marked } wj[j2] = mark; // j2 in set C1 (R3 if transpose) queue[tail++] = j2; // add j2 to queue } } //if (mark != 1) SparseMatrix.spfree(C); // free A' if it was created return(true); }
/// <summary> /// Column counts for Cholesky (LL'=A or LL'=A'A) and QR, given parent and post ordering. /// </summary> public static int[] ColumnCounts(SymbolicColumnStorage A, int[] parent, int[] post, bool ata) { int i, j, k, J, p, q, jleaf = 0; int[] ATp, ATi, colcount, delta, head = null, next = null; if (parent == null || post == null) { return(null); // check inputs } int m = A.RowCount; int n = A.ColumnCount; delta = colcount = new int[n]; // allocate result var AT = A.Transpose(); // AT = A' // w is ancestor int[] w = new int[n]; // get workspace int[] maxfirst = new int[n]; int[] prevleaf = new int[n]; int[] first = new int[n]; for (k = 0; k < n; k++) { w[k] = -1; // clear workspace w [0..s-1] } Array.Copy(w, maxfirst, n); Array.Copy(w, prevleaf, n); Array.Copy(w, first, n); for (k = 0; k < n; k++) // find first [j] { j = post[k]; delta[j] = (first[j] == -1) ? 1 : 0; // delta[j]=1 if j is a leaf for (; j != -1 && first[j] == -1; j = parent[j]) { first[j] = k; } } ATp = AT.ColumnPointers; ATi = AT.RowIndices; if (ata) // Init ata { head = new int[n + 1]; next = new int[m]; Array.Copy(w, head, n); head[n] = -1; for (k = 0; k < n; k++) { w[post[k]] = k; // invert post } for (i = 0; i < m; i++) { for (k = n, p = ATp[i]; p < ATp[i + 1]; p++) { k = Math.Min(k, w[ATi[p]]); } next[i] = head[k]; // place row i in linked list k head[k] = i; } } for (i = 0; i < n; i++) { w[i] = i; // each node in its own set } for (k = 0; k < n; k++) { j = post[k]; // j is the kth node in postordered etree if (parent[j] != -1) { delta[parent[j]]--; // j is not a root } //int HEAD(k,j) (ata ? head [k] : j) //int NEXT(J) (ata ? next [J] : -1) for (J = (ata ? head[k] : j); J != -1; J = (ata ? next[J] : -1)) // J=j for LL'=A case { for (p = ATp[J]; p < ATp[J + 1]; p++) { i = ATi[p]; q = IsLeaf(i, j, first, maxfirst, prevleaf, w, ref jleaf); if (jleaf >= 1) { delta[j]++; // A(i,j) is in skeleton } if (jleaf == 2) { delta[q]--; // account for overlap in q } } } if (parent[j] != -1) { w[j] = parent[j]; } } for (j = 0; j < n; j++) // sum up delta's of each child { if (parent[j] != -1) { colcount[parent[j]] += colcount[j]; } } return(colcount); // success: free workspace }
/// <summary> /// Finds the strongly connected components of a square matrix. /// </summary> /// <returns>strongly connected components, null on error</returns> private static DulmageMendelsohn FindScc(SymbolicColumnStorage A, int n) { // matrix A temporarily modified, then restored int i, k, b, nb = 0, top; int[] xi, p, r, Ap, ATp; var AT = A.Transpose(); // AT = A' Ap = A.ColumnPointers; ATp = AT.ColumnPointers; xi = new int[2 * n + 1]; // get workspace var D = new DulmageMendelsohn(n, 0); // allocate result p = D.p; r = D.r; top = n; for (i = 0; i < n; i++) // first dfs(A) to find finish times (xi) { if (!(Ap[i] < 0)) { top = GraphHelper.DepthFirstSearch(i, A.ColumnPointers, A.RowIndices, top, xi, xi, n, null); } } for (i = 0; i < n; i++) { //CS_MARK(Ap, i); Ap[i] = -(Ap[i]) - 2; // restore A; unmark all nodes } top = n; nb = n; for (k = 0; k < n; k++) // dfs(A') to find strongly connnected comp { i = xi[k]; // get i in reverse order of finish times if (ATp[i] < 0) { continue; // skip node i if already ordered } r[nb--] = top; // node i is the start of a component in p top = GraphHelper.DepthFirstSearch(i, AT.ColumnPointers, AT.RowIndices, top, p, xi, n, null); } r[nb] = 0; // first block starts at zero; shift r up for (k = nb; k <= n; k++) { r[k - nb] = r[k]; } D.nb = nb = n - nb; // nb = # of strongly connected components for (b = 0; b < nb; b++) // sort each block in natural order { for (k = r[b]; k < r[b + 1]; k++) { xi[p[k]] = b; } } for (b = 0; b <= nb; b++) { xi[n + b] = r[b]; } for (i = 0; i < n; i++) { p[xi[n + xi[i]]++] = i; } return(D); }
// Construct matrix C private static SymbolicColumnStorage ConstructMatrix(SymbolicColumnStorage A, ColumnOrdering order) { SymbolicColumnStorage result = null; // Compute A' var AT = A.Transpose(); int m = A.RowCount; int n = A.ColumnCount; if (order == ColumnOrdering.MinimumDegreeAtPlusA) { if (n != m) { throw new ArgumentException(Resources.MatrixSquare, "A"); } // Return A+A' result = A.Add(AT); } else if (order == ColumnOrdering.MinimumDegreeStS) { // Drop dense columns from AT int dense, p, p2 = 0; // Find dense threshold dense = Math.Max(16, 10 * (int)Math.Sqrt(n)); dense = Math.Min(n - 2, dense); var colptr = AT.ColumnPointers; var rowind = AT.RowIndices; for (int j = 0; j < m; j++) { // Column j of AT starts here. p = colptr[j]; // New column j starts here. colptr[j] = p2; if (colptr[j + 1] - p > dense) { // Skip dense column j continue; } for (; p < colptr[j + 1]; p++) { rowind[p2++] = rowind[p]; } } colptr[m] = p2; // Return A'*A with no dense rows result = AT.Multiply(AT.Transpose()); } else { // Return A'*A result = AT.Multiply(A); } // Drop diagonal entries. result.Keep(KeepOffDiag); return(result); }
/// <summary> /// Find a maximum transveral (zero-free diagonal). Seed optionally selects a /// randomized algorithm. /// </summary> /// <param name="A">column-compressed matrix</param> /// <param name="seed">0: natural, -1: reverse, randomized otherwise</param> /// <returns>row and column matching, size m+n</returns> public static int[] Generate(SymbolicColumnStorage A, int seed) { int i, j, k, p, n2 = 0, m2 = 0; int[] jimatch, w, cheap, js, iss, ps, Cp, q; int n = A.ColumnCount; int m = A.RowCount; int[] Ap = A.ColumnPointers; int[] Ai = A.RowIndices; //[jmatch [0..m-1]; imatch [0..n-1]] w = jimatch = new int[m + n]; // allocate result for (k = 0, j = 0; j < n; j++) // count nonempty rows and columns { n2 += (Ap[j] < Ap[j + 1]) ? 1 : 0; for (p = Ap[j]; p < Ap[j + 1]; p++) { w[Ai[p]] = 1; k += (j == Ai[p]) ? 1 : 0; // count entries already on diagonal } } if (k == Math.Min(m, n)) // quick return if diagonal zero-free { for (i = 0; i < k; i++) { jimatch[i] = i; } for (; i < m; i++) { jimatch[i] = -1; } for (j = 0; j < k; j++) { jimatch[m + j] = j; } for (; j < n; j++) { jimatch[m + j] = -1; } return(jimatch); } for (i = 0; i < m; i++) { m2 += w[i]; } // Transpose if needed SymbolicColumnStorage C = (m2 < n2) ? A.Transpose() : A.Clone(); if (C == null) { return(jimatch); } n = C.ColumnCount; m = C.RowCount; Cp = C.ColumnPointers; int jmatch_offset = (m2 < n2) ? n : 0; int imatch_offset = (m2 < n2) ? 0 : m; w = new int[n]; // get workspace cheap = new int[n]; js = new int[n]; iss = new int[n]; ps = new int[n]; for (j = 0; j < n; j++) { cheap[j] = Cp[j]; // for cheap assignment } for (j = 0; j < n; j++) { w[j] = -1; // all columns unflagged } for (i = 0; i < m; i++) { jimatch[jmatch_offset + i] = -1; // nothing matched yet } q = Permutation.Create(n, seed); // q = random permutation for (k = 0; k < n; k++) // augment, starting at column q[k] { Augment(q[k], C.ColumnPointers, C.RowIndices, jimatch, jmatch_offset, cheap, w, js, iss, ps); } for (j = 0; j < n; j++) { jimatch[imatch_offset + j] = -1; // find row match } for (i = 0; i < m; i++) { if (jimatch[jmatch_offset + i] >= 0) { jimatch[imatch_offset + jimatch[jmatch_offset + i]] = i; } } return(jimatch); }