/// <summary> /// find strongly connected components of A /// </summary> /// <param name="A"></param> /// <param name="n"></param> /// <returns></returns> internal static StronglyConnectedComponents Generate(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 StronglyConnectedComponents(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; } Array.Resize(ref D.r, nb + 1); return(D); }
/// <summary> /// Compute coarse and then fine Dulmage-Mendelsohn decomposition. seed /// optionally selects a randomized algorithm. /// </summary> /// <param name="matrix">column-compressed matrix</param> /// <param name="seed">0: natural, -1: reverse, random order otherwise</param> /// <returns>Dulmage-Mendelsohn analysis</returns> public static DulmageMendelsohn Generate <T>(CompressedColumnStorage <T> matrix, int seed = 0) where T : struct, IEquatable <T>, IFormattable { int i, j, k, cnz, nc, nb1, nb2; int[] Cp, ps, rs; bool ok; // We are not interested in the actual matrix values. var A = SymbolicColumnStorage.Create(matrix); // Maximum matching int m = A.RowCount; int n = A.ColumnCount; var result = new DulmageMendelsohn(m, n); // allocate result int[] p = result.p; int[] q = result.q; int[] r = result.r; int[] s = result.s; int[] cc = result.cc; int[] rr = result.rr; int[] jimatch = MaximumMatching.Generate(A, seed); // max transversal if (jimatch == null) { return(null); } // Coarse decomposition for (j = 0; j < n; j++) { s[j] = -1; // unmark all cols for bfs } for (i = 0; i < m; i++) { r[i] = -1; // unmark all rows for bfs } result.BreadthFirstSearch(A, n, r, s, q, jimatch, m, 0, 1); // find C1, R1 from C0*/ ok = result.BreadthFirstSearch(A, m, s, r, p, jimatch, 0, m, 3); // find R3, C3 from R0*/ if (!ok) { return(null); } result.Unmatched(n, s, q, cc, 0); // unmatched set C0 result.Matched(n, s, jimatch, m, p, q, cc, rr, 1, 1); // set R1 and C1 result.Matched(n, s, jimatch, m, p, q, cc, rr, 2, -1); // set R2 and C2 result.Matched(n, s, jimatch, m, p, q, cc, rr, 3, 3); // set R3 and C3 result.Unmatched(m, r, p, rr, 3); // unmatched set R0 // Fine decomposition int[] pinv = Permutation.Invert(p); // pinv=p' var C = SymbolicColumnStorage.Create(matrix); A.Permute(pinv, q, C); // C=A(p,q) (it will hold A(R2,C2)) Cp = C.ColumnPointers; nc = cc[3] - cc[2]; // delete cols C0, C1, and C3 from C if (cc[2] > 0) { for (j = cc[2]; j <= cc[3]; j++) { Cp[j - cc[2]] = Cp[j]; } } C.Reshape(-1, nc); if (rr[2] - rr[1] < m) // delete rows R0, R1, and R3 from C { RowPrune(C, nc, rr); cnz = Cp[nc]; int[] Ci = C.RowIndices; if (rr[1] > 0) { for (k = 0; k < cnz; k++) { Ci[k] -= rr[1]; } } } C.Reshape(nc, -1); var scc = StronglyConnectedComponents.Generate(C, nc); // find strongly connected components of C*/ // Combine coarse and fine decompositions ps = scc.p; // C(ps,ps) is the permuted matrix rs = scc.r; // kth block is rs[k]..rs[k+1]-1 nb1 = scc.nb; // # of blocks of A(R2,C2) for (k = 0; k < nc; k++) { s[k] = q[ps[k] + cc[2]]; } for (k = 0; k < nc; k++) { q[k + cc[2]] = s[k]; } for (k = 0; k < nc; k++) { r[k] = p[ps[k] + rr[1]]; } for (k = 0; k < nc; k++) { p[k + rr[1]] = r[k]; } nb2 = 0; // create the fine block partitions r[0] = s[0] = 0; if (cc[2] > 0) { nb2++; // leading coarse block A (R1, [C0 C1]) } for (k = 0; k < nb1; k++) // coarse block A (R2,C2) { r[nb2] = rs[k] + rr[1]; // A (R2,C2) splits into nb1 fine blocks s[nb2] = rs[k] + cc[2]; nb2++; } if (rr[2] < m) { r[nb2] = rr[2]; // trailing coarse block A ([R3 R0], C3) s[nb2] = cc[3]; nb2++; } r[nb2] = m; s[nb2] = n; result.nb = nb2; // Remove unused space Array.Resize(ref result.r, nb2 + 1); Array.Resize(ref result.s, nb2 + 1); return(result); }