Exemple #1
0
        /// <summary>
        /// Drops entries from a sparse matrix
        /// </summary>
        private static int RowPrune(SymbolicColumnStorage A, int n, int[] rr)
        {
            int i, j, nz = 0;

            var ai = A.RowIndices;
            var ap = A.ColumnPointers;

            for (j = 0; j < n; j++)
            {
                i = ap[j];

                // Record new location of col j.
                ap[j] = nz;

                for (; i < ap[j + 1]; i++)
                {
                    if (ai[i] >= rr[1] && ai[i] < rr[2])
                    {
                        // Keep A(i,j).
                        ai[nz] = ai[i];
                        nz++;
                    }
                }
            }

            // Record new nonzero count.
            ap[n] = nz;

            // Remove extra space.
            Array.Resize <int>(ref A.RowIndices, nz);

            return(nz);
        }
Exemple #2
0
        /// <summary>
        /// Permutes a sparse matrix, C = PAQ.
        /// </summary>
        /// <param name="A">m-by-n, column-compressed matrix</param>
        /// <param name="pinv">a permutation vector of length m</param>
        /// <param name="q">a permutation vector of length n</param>
        /// <returns>C = PAQ, null on error</returns>
        private SymbolicColumnStorage Permute(CompressedColumnStorage <T> A, int[] pinv, int[] q)
        {
            int t, j, k, nz = 0;

            int m = A.RowCount;
            int n = A.ColumnCount;

            var ap = A.ColumnPointers;
            var ai = A.RowIndices;

            var result = SymbolicColumnStorage.Create(A);

            var cp = result.ColumnPointers;
            var ci = result.RowIndices;

            for (k = 0; k < n; k++)
            {
                // Column k of C is column q[k] of A
                cp[k] = nz;
                j     = q != null ? (q[k]) : k;
                for (t = ap[j]; t < ap[j + 1]; t++)
                {
                    // Row i of A is row pinv[i] of C
                    ci[nz++] = pinv != null ? (pinv[ai[t]]) : ai[t];
                }
            }

            // Finalize the last column of C
            cp[n] = nz;

            return(result);
        }
Exemple #3
0
        /// <summary>
        /// Symbolic ordering and analysis for QR.
        /// </summary>
        /// <param name="A">Matrix to factorize.</param>
        /// <param name="p">Permutation.</param>
        protected void SymbolicAnalysis(CompressedColumnStorage <T> A, int[] p, bool natural)
        {
            int m = A.RowCount;
            int n = A.ColumnCount;

            var sym = this.S = new SymbolicFactorization();

            // Fill-reducing ordering
            sym.q = p;

            var C = natural ? SymbolicColumnStorage.Create(A) : Permute(A, null, sym.q);

            // etree of C'*C, where C=A(:,q)
            sym.parent = GraphHelper.EliminationTree(m, n, C.ColumnPointers, C.RowIndices, true);
            int[] post = GraphHelper.TreePostorder(sym.parent, n);
            sym.cp = GraphHelper.ColumnCounts(C, sym.parent, post, true); // col counts chol(C'*C)

            bool ok = C != null && sym.parent != null && sym.cp != null && CountV(C, sym);

            if (ok)
            {
                sym.unz = 0;
                for (int k = 0; k < n; k++)
                {
                    sym.unz += sym.cp[k];
                }
            }
        }
Exemple #4
0
        /// <summary>
        /// Symbolic ordering and analysis for QR.
        /// </summary>
        private void SymbolicAnalysis(ColumnOrdering order, CompressedColumnStorage <double> A)
        {
            int m = A.RowCount;
            int n = A.ColumnCount;

            var sym = this.symFactor = new SymbolicFactorization();

            // Fill-reducing ordering
            sym.q = AMD.Generate(A, order);

            var C = order > 0 ? Permute(A, null, sym.q) : SymbolicColumnStorage.Create(A);

            // etree of C'*C, where C=A(:,q)
            sym.parent = GraphHelper.EliminationTree(m, n, C.ColumnPointers, C.RowIndices, true);
            int[] post = GraphHelper.TreePostorder(sym.parent, n);
            sym.cp = GraphHelper.ColumnCounts(C, sym.parent, post, true); // col counts chol(C'*C)

            bool ok = C != null && sym.parent != null && sym.cp != null && CountV(C);

            if (ok)
            {
                sym.unz = 0;
                for (int k = 0; k < n; k++)
                {
                    sym.unz += sym.cp[k];
                }
            }
        }
Exemple #5
0
        /// <summary>
        /// Ordering and symbolic analysis for a Cholesky factorization.
        /// </summary>
        /// <param name="A">Matrix to factorize.</param>
        /// <param name="p">Permutation.</param>
        private void SymbolicAnalysis(CompressedColumnStorage <Complex> A, int[] p)
        {
            int n = A.ColumnCount;

            var sym = this.S = new SymbolicFactorization();

            // Find inverse permutation.
            sym.pinv = Permutation.Invert(p);

            // C = spones(triu(A(P,P)))
            var C = PermuteSym(A, sym.pinv, false);

            // Find etree of C.
            sym.parent = GraphHelper.EliminationTree(n, n, C.ColumnPointers, C.RowIndices, false);

            // Postorder the etree.
            var post = GraphHelper.TreePostorder(sym.parent, n);

            // Find column counts of chol(C)
            var c = GraphHelper.ColumnCounts(SymbolicColumnStorage.Create(C, false), sym.parent, post, false);

            sym.cp = new int[n + 1];

            // Find column pointers for L
            sym.unz = sym.lnz = Helper.CumulativeSum(sym.cp, c, n);
        }
        public void TestEmptyTranspose(int rows, int columns)
        {
            var A = new SymbolicColumnStorage(rows, columns, 0, true);
            var B = A.Transpose();

            Assert.IsNotNull(B);
        }
        public void TestEmptyAdd(int rows, int columns)
        {
            var A = new SymbolicColumnStorage(rows, columns, 0, true);
            var B = new SymbolicColumnStorage(rows, columns, 0, true);

            var C = A.Add(B);

            Assert.IsNotNull(C);
        }
Exemple #8
0
        // 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);
        }
Exemple #9
0
        /// <summary>
        /// Find nonzero pattern of Cholesky L(k,1:k-1) using etree and triu(A(:,k))
        /// </summary>
        public static int EtreeReach(SymbolicColumnStorage A, int k, int[] parent, int[] s, int[] w)
        {
            int i, p, n, len;

            if (parent == null || s == null || w == null)
            {
                return(-1);                                            // check inputs
            }
            int top = n = A.ColumnCount;

            int[] Ap = A.ColumnPointers;
            int[] Ai = A.RowIndices;

            //CS_MARK(w, k);
            w[k] = -w[k] - 2; // mark node k as visited

            for (p = Ap[k]; p < Ap[k + 1]; p++)
            {
                i = Ai[p]; // A(i,k) is nonzero
                if (i > k)
                {
                    continue;                             // only use upper triangular part of A
                }
                for (len = 0; !(w[i] < 0); i = parent[i]) // traverse up etree
                {
                    s[len++] = i;                         // L(k,i) is nonzero
                    //CS_MARK(w, i);
                    w[i] = -w[i] - 2;                     // mark i as visited
                }
                while (len > 0)
                {
                    s[--top] = s[--len]; // push path onto stack
                }
            }
            for (p = top; p < n; p++)
            {
                //CS_MARK(w, s[p]);
                w[s[p]] = -w[s[p]] - 2; // unmark all nodes
            }
            //CS_MARK(w, k);
            w[k] = -w[k] - 2; // unmark node k

            return(top);      // s [top..n-1] contains pattern of L(k,:)
        }
Exemple #10
0
        /// <summary>
        /// Compute the Numeric Cholesky factorization, L = chol (A, [pinv parent cp]).
        /// </summary>
        /// <returns>Numeric Cholesky factorization</returns>
        private void Factorize(CompressedColumnStorage <Complex> A, IProgress <double> progress)
        {
            Complex d, lki;
            int     top, i, p, k, cci;

            int n = A.ColumnCount;

            // Allocate workspace.
            var c = new int[n];
            var s = new int[n];

            var x = this.temp;

            var colp   = S.cp;
            var pinv   = S.pinv;
            var parent = S.parent;

            var C = pinv != null?PermuteSym(A, pinv, true) : A;

            var cp = C.ColumnPointers;
            var ci = C.RowIndices;
            var cx = C.Values;

            this.L = CompressedColumnStorage <Complex> .Create(n, n, colp[n]);

            var lp = L.ColumnPointers;
            var li = L.RowIndices;
            var lx = L.Values;

            for (k = 0; k < n; k++)
            {
                lp[k] = c[k] = colp[k];
            }

            double current = 0.0;
            double step    = n / 100.0;

            for (k = 0; k < n; k++) // compute L(k,:) for L*L' = C
            {
                // Progress reporting.
                if (k >= current)
                {
                    current += step;

                    if (progress != null)
                    {
                        progress.Report(k / (double)n);
                    }
                }

                // Find nonzero pattern of L(k,:)
                top  = GraphHelper.EtreeReach(SymbolicColumnStorage.Create(C, false), k, parent, s, c);
                x[k] = 0;                           // x (0:k) is now zero
                for (p = cp[k]; p < cp[k + 1]; p++) // x = full(triu(C(:,k)))
                {
                    if (ci[p] <= k)
                    {
                        x[ci[p]] = cx[p];
                    }
                }
                d    = x[k]; // d = C(k,k)
                x[k] = 0;    // clear x for k+1st iteration

                // Triangular solve
                for (; top < n; top++)       // solve L(0:k-1,0:k-1) * x = C(:,k)
                {
                    i    = s[top];           // s [top..n-1] is pattern of L(k,:)
                    lki  = x[i] / lx[lp[i]]; // L(k,i) = x (i) / L(i,i)
                    x[i] = 0;                // clear x for k+1st iteration
                    cci  = c[i];
                    for (p = lp[i] + 1; p < cci; p++)
                    {
                        x[li[p]] -= lx[p] * lki;
                    }
                    d    -= lki * Complex.Conjugate(lki); // d = d - L(k,i)*L(k,i)
                    p     = c[i]++;
                    li[p] = k;                            // store L(k,i) in column i
                    lx[p] = Complex.Conjugate(lki);
                }
                // Compute L(k,k)
                if (d.Real <= 0 || d.Imaginary != 0)
                {
                    throw new Exception(Resources.MatrixSymmetricPositiveDefinite);
                }

                p     = c[k]++;
                li[p] = k; // store L(k,k) = sqrt (d) in column k
                lx[p] = Complex.Sqrt(d);
            }
            lp[n] = colp[n]; // finalize L
        }
Exemple #11
0
        /// <summary>
        /// Compute the Numeric Cholesky factorization, L = chol (A, [pinv parent cp]).
        /// </summary>
        /// <returns>Numeric Cholesky factorization</returns>
        private void Factorize(CompressedColumnStorage <double> A)
        {
            double d, lki;
            int    top, i, p, k;

            int n = A.ColumnCount;

            // Allocate workspace.
            var c = new int[n];
            var s = new int[n];
            var x = new double[n];

            var colp   = symFactor.cp;
            var pinv   = symFactor.pinv;
            var parent = symFactor.parent;

            var C = pinv != null?PermuteSym(A, pinv, true) : A;

            var cp = C.ColumnPointers;
            var ci = C.RowIndices;
            var cx = C.Values;

            this.L = CompressedColumnStorage <double> .Create(n, n, colp[n]);

            var lp = L.ColumnPointers;
            var li = L.RowIndices;
            var lx = L.Values;

            //var lst = new List<int>();

            var percent = 0;

            for (k = 0; k < n; k++)
            {
                lp[k] = c[k] = colp[k];
            }
            for (k = 0; k < n; k++) // compute L(k,:) for L*L' = C
            {
                if (100 * k / n != percent)
                {
                    Progress = percent = (100 * k) / n;

                    //Console.WriteLine("{0}% solve", percent);
                }

                // Find nonzero pattern of L(k,:)
                top  = GraphHelper.EtreeReach(SymbolicColumnStorage.Create(C, false), k, parent, s, c);
                x[k] = 0;                           // x (0:k) is now zero

                var tmp = cp[k + 1];
                for (p = cp[k]; p < tmp; p++) // x = full(triu(C(:,k)))
                {
                    if (ci[p] <= k)
                    {
                        x[ci[p]] = cx[p];
                    }
                }
                d    = x[k]; // d = C(k,k)
                x[k] = 0;    // clear x for k+1st iteration



                // Triangular solve
                for (; top < n; top++)       // solve L(0:k-1,0:k-1) * x = C(:,k)
                {
                    i    = s[top];           // s [top..n-1] is pattern of L(k,:)
                    lki  = x[i] / lx[lp[i]]; // L(k,i) = x (i) / L(i,i)
                    x[i] = 0;                // clear x for k+1st iteration

                    p = lp[i] + 1;

                    var cci = c[i];
                    for (p = lp[i] + 1; p < cci; p++)
                    {
                        x[li[p]] -= lx[p] * lki;
                    }



                    d    -= lki * lki; // d = d - L(k,i)*L(k,i)
                    p     = c[i]++;
                    li[p] = k;         // store L(k,i) in column i
                    lx[p] = lki;
                }
                // Compute L(k,k)
                if (d <= 0)
                {
                    throw new NotPositiveDefiniteException("not pos def"); // TODO: ex
                }

                p     = c[k]++;
                li[p] = k; // store L(k,k) = sqrt (d) in column k
                lx[p] = Math.Sqrt(d);
            }

            lp[n] = colp[n]; // finalize L
        }
Exemple #12
0
        /// <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
        }
Exemple #13
0
        /// <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);
        }
Exemple #14
0
        // 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);
        }
Exemple #15
0
        /// <summary>
        /// Generate minimum degree ordering of A+A' (if A is symmetric) or A'A.
        /// </summary>
        /// <param name="A">Column-compressed matrix</param>
        /// <param name="order">Column ordering method</param>
        /// <returns>amd(A+A') if A is symmetric, or amd(A'A) otherwise, null on
        /// error or for natural ordering</returns>
        /// <remarks>
        /// See Chapter 7.1 (Fill-reducing orderings: Minimum degree ordering) in
        /// "Direct Methods for Sparse Linear Systems" by Tim Davis.
        /// </remarks>
        public static int[] Generate <T>(CompressedColumnStorage <T> A, ColumnOrdering order)
            where T : struct, IEquatable <T>, IFormattable
        {
            int[] Cp, Ci, P, W, nv, next, head, elen, degree, w, hhead;

            int d, dk, dext, lemax = 0, e, elenk, eln, i, j, k, k1,
                k2, k3, jlast, ln, dense, nzmax, mindeg = 0, nvi, nvj, nvk, mark, wnvi,
                cnz, nel = 0, p, p1, p2, p3, p4, pj, pk, pk1, pk2, pn, q, n;
            bool ok;
            int  h;

            n = A.ColumnCount;

            if (order == ColumnOrdering.Natural)
            {
                // TODO: return null here?
                return(Permutation.Create(n));
            }

            var C = ConstructMatrix(SymbolicColumnStorage.Create(A), order);

            Cp  = C.ColumnPointers;
            cnz = Cp[n];

            // Find dense threshold
            dense = Math.Max(16, 10 * (int)Math.Sqrt(n));
            dense = Math.Min(n - 2, dense);

            // add elbow room to C
            if (!C.Resize(cnz + cnz / 5 + 2 * n))
            {
                return(null);
            }

            P      = new int[n + 1]; // allocate result
            W      = new int[n + 1]; // get workspace
            w      = new int[n + 1];
            degree = new int[n + 1];

            elen = new int[n + 1]; // Initialized to 0's

            // Initialize quotient graph
            for (k = 0; k < n; k++)
            {
                W[k] = Cp[k + 1] - Cp[k];
            }
            W[n]  = 0;
            nzmax = C.RowIndices.Length;
            Ci    = C.RowIndices;

            for (i = 0; i <= n; i++)
            {
                P[i]      = -1;
                w[i]      = 1;    // node i is alive
                degree[i] = W[i]; // degree of node i
            }

            next  = new int[n + 1];
            hhead = new int[n + 1];
            head  = new int[n + 1];
            nv    = new int[n + 1];

            Array.Copy(P, next, n + 1);
            Array.Copy(P, head, n + 1);  // degree list i is empty
            Array.Copy(P, hhead, n + 1); // hash list i is empty
            Array.Copy(w, nv, n + 1);    // node i is just one node

            mark    = Clear(0, 0, w, n); // clear w
            elen[n] = -2;                // n is a dead element
            Cp[n]   = -1;                // n is a root of assembly tree
            w[n]    = 0;                 // n is a dead element

            // Initialize degree lists
            for (i = 0; i < n; i++)
            {
                d = degree[i];
                if (d == 0)       // node i is empty
                {
                    elen[i] = -2; // element i is dead
                    nel++;
                    Cp[i] = -1;   // i is a root of assembly tree
                    w[i]  = 0;
                }
                else if (d > dense)   // node i is dense
                {
                    nv[i]   = 0;      // absorb i into element n
                    elen[i] = -1;     // node i is dead
                    nel++;
                    Cp[i] = -(n + 2); // FLIP(n)
                    nv[n]++;
                }
                else
                {
                    if (head[d] != -1)
                    {
                        P[head[d]] = i;
                    }
                    next[i] = head[d]; // put node i in degree list d
                    head[d] = i;
                }
            }
            while (nel < n) // while (selecting pivots) do
            {
                // Select node of minimum approximate degree
                for (k = -1; mindeg < n && (k = head[mindeg]) == -1; mindeg++)
                {
                    ;
                }
                if (next[k] != -1)
                {
                    P[next[k]] = -1;
                }
                head[mindeg] = next[k]; // remove k from degree list
                elenk        = elen[k]; // elenk = |Ek|
                nvk          = nv[k];   // # of nodes k represents
                nel         += nvk;     // nv[k] nodes of A eliminated

                // Garbage collection
                if (elenk > 0 && cnz + mindeg >= nzmax)
                {
                    for (j = 0; j < n; j++)
                    {
                        if ((p = Cp[j]) >= 0) // j is a live node or element
                        {
                            Cp[j] = Ci[p];    // save first entry of object
                            Ci[p] = -(j + 2); // first entry is now CS_FLIP(j)
                        }
                    }
                    for (q = 0, p = 0; p < cnz;)      // scan all of memory
                    {
                        if ((j = FLIP(Ci[p++])) >= 0) // found object j
                        {
                            Ci[q] = Cp[j];            // restore first entry of object
                            Cp[j] = q++;              // new pointer to object j
                            for (k3 = 0; k3 < W[j] - 1; k3++)
                            {
                                Ci[q++] = Ci[p++];
                            }
                        }
                    }
                    cnz = q; // Ci [cnz...nzmax-1] now free
                }

                // Construct new element
                dk    = 0;
                nv[k] = -nvk;                   // flag k as in Lk
                p     = Cp[k];
                pk1   = (elenk == 0) ? p : cnz; // do in place if elen[k] == 0
                pk2   = pk1;
                for (k1 = 1; k1 <= elenk + 1; k1++)
                {
                    if (k1 > elenk)
                    {
                        e  = k;            // search the nodes in k
                        pj = p;            // list of nodes starts at Ci[pj]*/
                        ln = W[k] - elenk; // length of list of nodes in k
                    }
                    else
                    {
                        e  = Ci[p++]; // search the nodes in e
                        pj = Cp[e];
                        ln = W[e];    // length of list of nodes in e
                    }
                    for (k2 = 1; k2 <= ln; k2++)
                    {
                        i = Ci[pj++];
                        if ((nvi = nv[i]) <= 0)
                        {
                            continue;     // node i dead, or seen
                        }
                        dk       += nvi;  // degree[Lk] += size of node i
                        nv[i]     = -nvi; // negate nv[i] to denote i in Lk
                        Ci[pk2++] = i;    // place i in Lk
                        if (next[i] != -1)
                        {
                            P[next[i]] = P[i];
                        }
                        if (P[i] != -1) // remove i from degree list
                        {
                            next[P[i]] = next[i];
                        }
                        else
                        {
                            head[degree[i]] = next[i];
                        }
                    }
                    if (e != k)
                    {
                        Cp[e] = -(k + 2); // absorb e into k // FLIP(k)
                        w[e]  = 0;        // e is now a dead element
                    }
                }
                if (elenk != 0)
                {
                    cnz = pk2;   // Ci [cnz...nzmax] is free
                }
                degree[k] = dk;  // external degree of k - |Lk\i|
                Cp[k]     = pk1; // element k is in Ci[pk1..pk2-1]
                W[k]      = pk2 - pk1;
                elen[k]   = -2;  // k is now an element

                // Find set differences
                mark = Clear(mark, lemax, w, n); // clear w if necessary
                for (pk = pk1; pk < pk2; pk++)   // scan 1: find |Le\Lk|
                {
                    i = Ci[pk];
                    if ((eln = elen[i]) <= 0)
                    {
                        continue;                              // skip if elen[i] empty
                    }
                    nvi  = -nv[i];                             // nv [i] was negated
                    wnvi = mark - nvi;
                    for (p = Cp[i]; p <= Cp[i] + eln - 1; p++) // scan Ei
                    {
                        e = Ci[p];
                        if (w[e] >= mark)
                        {
                            w[e] -= nvi;             // decrement |Le\Lk|
                        }
                        else if (w[e] != 0)          // ensure e is a live element
                        {
                            w[e] = degree[e] + wnvi; // 1st time e seen in scan 1
                        }
                    }
                }

                // Degree update
                for (pk = pk1; pk < pk2; pk++) // scan2: degree update
                {
                    i  = Ci[pk];               // consider node i in Lk
                    p1 = Cp[i];
                    p2 = p1 + elen[i] - 1;
                    pn = p1;
                    for (h = 0, d = 0, p = p1; p <= p2; p++) // scan Ei
                    {
                        e = Ci[p];
                        if (w[e] != 0)          // e is an unabsorbed element
                        {
                            dext = w[e] - mark; // dext = |Le\Lk|
                            if (dext > 0)
                            {
                                d       += dext; // sum up the set differences
                                Ci[pn++] = e;    // keep e in Ei
                                h       += e;    // compute the hash of node i
                            }
                            else
                            {
                                Cp[e] = -(k + 2); // aggressive absorb. e.k // FLIP(k)
                                w[e]  = 0;        // e is a dead element
                            }
                        }
                    }
                    elen[i] = pn - p1 + 1; // elen[i] = |Ei|
                    p3      = pn;
                    p4      = p1 + W[i];
                    for (p = p2 + 1; p < p4; p++) // prune edges in Ai
                    {
                        j = Ci[p];
                        if ((nvj = nv[j]) <= 0)
                        {
                            continue;       // node j dead or in Lk
                        }
                        d       += nvj;     // degree(i) += |j|
                        Ci[pn++] = j;       // place j in node list of i
                        h       += j;       // compute hash for node i
                    }
                    if (d == 0)             // check for mass elimination
                    {
                        Cp[i]   = -(k + 2); // absorb i into k // FLIP(k)
                        nvi     = -nv[i];
                        dk     -= nvi;      // |Lk| -= |i|
                        nvk    += nvi;      // |k| += nv[i]
                        nel    += nvi;
                        nv[i]   = 0;
                        elen[i] = -1; // node i is dead
                    }
                    else
                    {
                        degree[i] = Math.Min(degree[i], d);   // update degree(i)
                        Ci[pn]    = Ci[p3];                   // move first node to end
                        Ci[p3]    = Ci[p1];                   // move 1st el. to end of Ei
                        Ci[p1]    = k;                        // add k as 1st element in of Ei
                        W[i]      = pn - p1 + 1;              // new len of adj. list of node i
                        h         = ((h < 0) ? (-h) : h) % n; // finalize hash of i
                        next[i]   = hhead[h];                 // place i in hash bucket
                        hhead[h]  = i;
                        P[i]      = h;                        // save hash of i in last[i]
                    }
                } // scan2 is done
                degree[k] = dk;                               // finalize |Lk|
                lemax     = Math.Max(lemax, dk);
                mark      = Clear(mark + lemax, lemax, w, n); // clear w

                // Supernode detection
                for (pk = pk1; pk < pk2; pk++)
                {
                    i = Ci[pk];
                    if (nv[i] >= 0)
                    {
                        continue;    // skip if i is dead
                    }
                    h        = P[i]; // scan hash bucket of node i
                    i        = hhead[h];
                    hhead[h] = -1;   // hash bucket will be empty
                    for (; i != -1 && next[i] != -1; i = next[i], mark++)
                    {
                        ln  = W[i];
                        eln = elen[i];
                        for (p = Cp[i] + 1; p <= Cp[i] + ln - 1; p++)
                        {
                            w[Ci[p]] = mark;
                        }
                        jlast = i;
                        for (j = next[i]; j != -1;)  // compare i with all j
                        {
                            ok = (W[j] == ln) && (elen[j] == eln);
                            for (p = Cp[j] + 1; ok && p <= Cp[j] + ln - 1; p++)
                            {
                                if (w[Ci[p]] != mark)
                                {
                                    ok = false;                   // compare i and j
                                }
                            }
                            if (ok)                     // i and j are identical
                            {
                                Cp[j]       = -(i + 2); // absorb j into i // FLIP(i)
                                nv[i]      += nv[j];
                                nv[j]       = 0;
                                elen[j]     = -1;      // node j is dead
                                j           = next[j]; // delete j from hash bucket
                                next[jlast] = j;
                            }
                            else
                            {
                                jlast = j; // j and i are different
                                j     = next[j];
                            }
                        }
                    }
                }

                // Finalize new element
                for (p = pk1, pk = pk1; pk < pk2; pk++) // finalize Lk
                {
                    i = Ci[pk];
                    if ((nvi = -nv[i]) <= 0)
                    {
                        continue;                 // skip if i is dead
                    }
                    nv[i] = nvi;                  // restore nv[i]
                    d     = degree[i] + dk - nvi; // compute external degree(i)
                    d     = Math.Min(d, n - nel - nvi);
                    if (head[d] != -1)
                    {
                        P[head[d]] = i;
                    }
                    next[i]   = head[d]; // put i back in degree list
                    P[i]      = -1;
                    head[d]   = i;
                    mindeg    = Math.Min(mindeg, d); // find new minimum degree
                    degree[i] = d;
                    Ci[p++]   = i;                   // place i in Lk
                }
                nv[k] = nvk;                         // # nodes absorbed into k
                if ((W[k] = p - pk1) == 0)           // length of adj list of element k
                {
                    Cp[k] = -1;                      // k is a root of the tree
                    w[k]  = 0;                       // k is now a dead element
                }
                if (elenk != 0)
                {
                    cnz = p;             // free unused space in Lk
                }
            }

            // Postordering
            for (i = 0; i < n; i++)
            {
                Cp[i] = -(Cp[i] + 2);                     // fix assembly tree // FLIP(Cp[i])
            }
            for (j = 0; j <= n; j++)
            {
                head[j] = -1;
            }
            for (j = n; j >= 0; j--) // place unordered nodes in lists
            {
                if (nv[j] > 0)
                {
                    continue;              // skip if j is an element
                }
                next[j]     = head[Cp[j]]; // place j in list of its parent
                head[Cp[j]] = j;
            }
            for (e = n; e >= 0; e--) // place elements in lists
            {
                if (nv[e] <= 0)
                {
                    continue;             // skip unless e is an element
                }
                if (Cp[e] != -1)
                {
                    next[e]     = head[Cp[e]]; // place e in list of its parent
                    head[Cp[e]] = e;
                }
            }
            for (k = 0, i = 0; i <= n; i++) // postorder the assembly tree
            {
                if (Cp[i] == -1)
                {
                    k = GraphHelper.TreeDepthFirstSearch(i, k, head, next, P, w);
                }
            }
            return(P);
        }
 /// <summary>
 /// Compute strongly connected components of matrix.
 /// </summary>
 /// <param name="matrix">column-compressed matrix</param>
 /// <returns>Strongly connected components</returns>
 public static StronglyConnectedComponents Generate <T>(CompressedColumnStorage <T> matrix)
     where T : struct, IEquatable <T>, IFormattable
 {
     return(Generate(SymbolicColumnStorage.Create(matrix), matrix.ColumnCount));
 }
        /// <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);
        }
Exemple #18
0
        /// <summary>
        /// Compute nnz(V) = S.lnz, S.pinv, S.leftmost, S.m2 from A and S.parent
        /// </summary>
        private bool CountV(SymbolicColumnStorage A, SymbolicFactorization S)
        {
            int i, k, p, pa;

            int[] pinv, leftmost, parent = S.parent;

            int m = A.RowCount;
            int n = A.ColumnCount;

            int[] ap = A.ColumnPointers;
            int[] ai = A.RowIndices;

            S.pinv     = pinv = new int[m + n]; // allocate pinv,
            S.leftmost = leftmost = new int[m]; // and leftmost

            var w    = new int[m];              // get workspace
            var head = new int[n];
            var tail = new int[n];
            var nque = new int[n]; // Initialized to 0's

            for (k = 0; k < n; k++)
            {
                head[k] = -1;                     // queue k is empty
            }
            for (k = 0; k < n; k++)
            {
                tail[k] = -1;
            }
            for (i = 0; i < m; i++)
            {
                leftmost[i] = -1;
            }
            for (k = n - 1; k >= 0; k--)
            {
                for (p = ap[k]; p < ap[k + 1]; p++)
                {
                    leftmost[ai[p]] = k; // leftmost[i] = min(find(A(i,:)))
                }
            }
            for (i = m - 1; i >= 0; i--) // scan rows in reverse order
            {
                pinv[i] = -1;            // row i is not yet ordered
                k       = leftmost[i];
                if (k == -1)
                {
                    continue;          // row i is empty
                }
                if (nque[k]++ == 0)
                {
                    tail[k] = i;   // first row in queue k
                }
                w[i]    = head[k]; // put i at head of queue k
                head[k] = i;
            }
            S.lnz = 0;
            S.m2  = m;
            for (k = 0; k < n; k++) // find row permutation and nnz(V)
            {
                i = head[k];        // remove row i from queue k
                S.lnz++;            // count V(k,k) as nonzero
                if (i < 0)
                {
                    i = S.m2++; // add a fictitious row
                }
                pinv[i] = k;    // associate row i with V(:,k)
                if (--nque[k] <= 0)
                {
                    continue;               // skip if V(k+1:m,k) is empty
                }
                S.lnz += nque[k];           // nque [k] is nnz (V(k+1:m,k))
                if ((pa = parent[k]) != -1) // move all rows to parent of k
                {
                    if (nque[pa] == 0)
                    {
                        tail[pa] = tail[k];
                    }
                    w[tail[k]] = head[pa];
                    head[pa]   = w[i];
                    nque[pa]  += nque[k];
                }
            }
            for (i = 0; i < m; i++)
            {
                if (pinv[i] < 0)
                {
                    pinv[i] = k++;
                }
            }

            return(true);
        }
        public void TestConstructor(int rows, int columns)
        {
            var A = new SymbolicColumnStorage(rows, columns, 0, true);

            Assert.IsNotNull(A);
        }
Exemple #20
0
        /// <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 = FindScc(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);
        }