/// <summary> /// Convert a coordinate storage to compressed sparse column (CSC) format. /// </summary> /// <param name="storage">Coordinate storage.</param> /// <param name="cleanup">Remove and sum duplicate entries.</param> /// <returns>Compressed sparse column storage.</returns> public static CompressedColumnStorage <T> ToCompressedColumnStorage <T>(CoordinateStorage <T> storage, bool cleanup = true) where T : struct, IEquatable <T>, IFormattable { int nrows = storage.RowCount; int ncols = storage.ColumnCount; var values = storage.Values; var rowind = storage.RowIndices; var colind = storage.ColumnIndices; int p, k, nz = storage.NonZerosCount; var columnPointers = new int[ncols + 1]; var columnCounts = new int[ncols]; for (k = 0; k < nz; k++) { // Count columns columnCounts[colind[k]]++; } // Get row pointers int valueCount = Helper.CumulativeSum(columnPointers, columnCounts, ncols); var result = CompressedColumnStorage <T> .Create(nrows, ncols); var rowIndices = new int[valueCount]; var storageValues = new T[valueCount]; for (k = 0; k < nz; k++) { p = columnCounts[colind[k]]++; rowIndices[p] = rowind[k]; storageValues[p] = values[k]; } result.RowIndices = rowIndices; result.ColumnPointers = columnPointers; result.Values = storageValues; result.SortIndices(); if (cleanup) { result.Cleanup(); } return(result); }
/// <summary> /// Compute the numeric LDL' factorization of PAP'. /// </summary> void Factorize(CompressedColumnStorage <double> A) { int n = A.ColumnCount; var ap = A.ColumnPointers; var ai = A.RowIndices; var ax = A.Values; int[] parent = S.parent; int[] P = S.q; int[] Pinv = S.pinv; this.D = new double[n]; this.L = CompressedColumnStorage <double> .Create(n, n, S.cp[n]); Array.Copy(S.cp, L.ColumnPointers, n + 1); var lp = L.ColumnPointers; var li = L.RowIndices; var lx = L.Values; // Workspace var y = new double[n]; var pattern = new int[n]; var flag = new int[n]; var lnz = new int[n]; double yi, l_ki; int i, k, p, kk, p2, len, top; for (k = 0; k < n; k++) { // compute nonzero Pattern of kth row of L, in topological order y[k] = 0.0; // Y(0:k) is now all zero top = n; // stack for pattern is empty flag[k] = k; // mark node k as visited lnz[k] = 0; // count of nonzeros in column k of L kk = (P != null) ? (P[k]) : (k); // kth original, or permuted, column p2 = ap[kk + 1]; for (p = ap[kk]; p < p2; p++) { i = (Pinv != null) ? (Pinv[ai[p]]) : (ai[p]); // get A(i,k) if (i <= k) { y[i] += ax[p]; // scatter A(i,k) into Y (sum duplicates) for (len = 0; flag[i] != k; i = parent[i]) { pattern[len++] = i; // L(k,i) is nonzero flag[i] = k; // mark i as visited } while (len > 0) { pattern[--top] = pattern[--len]; } } } // compute numerical values kth row of L (a sparse triangular solve) D[k] = y[k]; // get D(k,k) and clear Y(k) y[k] = 0.0; for (; top < n; top++) { i = pattern[top]; // Pattern [top:n-1] is pattern of L(:,k) yi = y[i]; // get and clear Y(i) y[i] = 0.0; p2 = lp[i] + lnz[i]; for (p = lp[i]; p < p2; p++) { y[li[p]] -= lx[p] * yi; } l_ki = yi / D[i]; // the nonzero entry L(k,i) D[k] -= l_ki * yi; li[p] = k; // store L(k,i) in column form of L lx[p] = l_ki; lnz[i]++; // increment count of nonzeros in col i } if (D[k] == 0.0) { // failure, D(k,k) is zero throw new Exception("Diagonal element is zero."); } } }
/// <summary> /// Sparse QR factorization [V,beta,pinv,R] = qr(A) /// </summary> protected void Factorize(CompressedColumnStorage <T> A, IProgress progress) { T zero = Helper.ZeroOf <T>(); int i, p, p1, top, len, col; int m = A.RowCount; int n = A.ColumnCount; var ap = A.ColumnPointers; var ai = A.RowIndices; var ax = A.Values; int[] q = S.q; int[] parent = S.parent; int[] pinv = S.pinv; int m2 = S.m2; int vnz = S.lnz; int rnz = S.unz; int[] leftmost = S.leftmost; int[] w = new int[m2 + n]; // get int workspace T[] x = new T[m2]; // get double workspace int s = m2; // offset into w // Allocate result V, R and beta var V = this.Q = CompressedColumnStorage <T> .Create(m2, n, vnz); var R = this.R = CompressedColumnStorage <T> .Create(m2, n, rnz); var b = this.beta = new double[n]; var rp = R.ColumnPointers; var ri = R.RowIndices; var rx = R.Values; var vp = V.ColumnPointers; var vi = V.RowIndices; var vx = V.Values; for (i = 0; i < m2; i++) { w[i] = -1; // clear w, to mark nodes } double current = 0.0; double step = n / 100.0; rnz = 0; vnz = 0; for (int k = 0; k < n; k++) // compute V and R { // Progress reporting. if (k >= current) { current += step; if (progress != null) { progress.Report(k / (double)n); } } rp[k] = rnz; // R(:,k) starts here vp[k] = p1 = vnz; // V(:,k) starts here w[k] = k; // add V(k,k) to pattern of V vi[vnz++] = k; top = n; col = q != null ? q[k] : k; for (p = ap[col]; p < ap[col + 1]; p++) // find R(:,k) pattern { i = leftmost[ai[p]]; // i = min(find(A(i,q))) for (len = 0; w[i] != k; i = parent[i]) // traverse up to k { //len++; w[s + len++] = i; w[i] = k; } while (len > 0) { --top; --len; w[s + top] = w[s + len]; // push path on stack } i = pinv[ai[p]]; // i = permuted row of A(:,col) x[i] = ax[p]; // x (i) = A(:,col) if (i > k && w[i] < k) // pattern of V(:,k) = x (k+1:m) { vi[vnz++] = i; // add i to pattern of V(:,k) w[i] = k; } } for (p = top; p < n; p++) // for each i in pattern of R(:,k) { i = w[s + p]; // R(i,k) is nonzero ApplyHouseholder(V, i, b[i], x); // apply (V(i),Beta(i)) to x ri[rnz] = i; // R(i,k) = x(i) rx[rnz++] = x[i]; x[i] = zero; if (parent[i] == k) { vnz = V.Scatter(i, zero, w, null, k, V, vnz); } } for (p = p1; p < vnz; p++) // gather V(:,k) = x { vx[p] = x[vi[p]]; x[vi[p]] = zero; } ri[rnz] = k; // R(k,k) = norm (x) rx[rnz++] = CreateHouseholder(vx, p1, ref b[k], vnz - p1); // [v,beta]=house(x) } rp[n] = rnz; // finalize R vp[n] = vnz; // finalize V }
/// <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 }
/// <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 }
/// <summary> /// [L,U,pinv] = lu(A, [q lnz unz]). lnz and unz can be guess. /// </summary> private void Factorize(CompressedColumnStorage <Complex> A, double tol, IProgress <double> progress) { int[] q = S.q; int i; int lnz = S.lnz; int unz = S.unz; this.L = CompressedColumnStorage <Complex> .Create(n, n, lnz); this.U = CompressedColumnStorage <Complex> .Create(n, n, unz); this.pinv = new int[n]; // Workspace var x = this.temp; var xi = new int[2 * n]; for (i = 0; i < n; i++) { // No rows pivotal yet. pinv[i] = -1; } lnz = unz = 0; int ipiv, top, p, col; Complex pivot; double a, t; int[] li, ui; int[] lp = L.ColumnPointers; int[] up = U.ColumnPointers; Complex[] lx, ux; double current = 0.0; double step = n / 100.0; // Now compute L(:,k) and U(:,k) for (int k = 0; k < n; k++) { // Progress reporting. if (k >= current) { current += step; if (progress != null) { progress.Report(k / (double)n); } } // Triangular solve lp[k] = lnz; // L(:,k) starts here up[k] = unz; // U(:,k) starts here if (lnz + n > L.Values.Length) { L.Resize(2 * L.Values.Length + n); } if (unz + n > U.Values.Length) { U.Resize(2 * U.Values.Length + n); } li = L.RowIndices; ui = U.RowIndices; lx = L.Values; ux = U.Values; col = q != null ? (q[k]) : k; top = SolveSp(L, A, col, xi, x, pinv, true); // x = L\A(:,col) // Find pivot ipiv = -1; a = -1; for (p = top; p < n; p++) { i = xi[p]; // x(i) is nonzero if (pinv[i] < 0) // Row i is not yet pivotal { if ((t = Complex.Abs(x[i])) > a) { a = t; // Largest pivot candidate so far ipiv = i; } } else // x(i) is the entry U(pinv[i],k) { ui[unz] = pinv[i]; ux[unz++] = x[i]; } } if (ipiv == -1 || a <= 0.0) { throw new Exception("No pivot element found."); } if (pinv[col] < 0 && Complex.Abs(x[col]) >= a * tol) { ipiv = col; } // Divide by pivot pivot = x[ipiv]; // the chosen pivot ui[unz] = k; // last entry in U(:,k) is U(k,k) ux[unz++] = pivot; pinv[ipiv] = k; // ipiv is the kth pivot row li[lnz] = ipiv; // first entry in L(:,k) is L(k,k) = 1 lx[lnz++] = 1.0; for (p = top; p < n; p++) // L(k+1:n,k) = x / pivot { i = xi[p]; if (pinv[i] < 0) // x(i) is an entry in L(:,k) { li[lnz] = i; // save unpermuted row in L lx[lnz++] = x[i] / pivot; // scale pivot column } x[i] = 0.0; // x [0..n-1] = 0 for next k } } // Finalize L and U lp[n] = lnz; up[n] = unz; li = L.RowIndices; // fix row indices of L for final pinv for (p = 0; p < lnz; p++) { li[p] = pinv[li[p]]; } // Remove extra space from L and U L.Resize(0); U.Resize(0); }
/// <summary> /// Convert a coordinate storage to compressed sparse column (CSC) format. /// </summary> /// <param name="storage">Coordinate storage.</param> /// <param name="cleanup">Remove and sum duplicate entries.</param> /// <param name="inplace">Do the conversion in place (re-using the coordinate storage arrays).</param> /// <returns>Compressed sparse column storage.</returns> internal static CompressedColumnStorage <T> ToCompressedColumnStorage_ <T>(CoordinateStorage <T> storage, bool cleanup = true, bool inplace = false) where T : struct, IEquatable <T>, IFormattable { int nrows = storage.RowCount; int ncols = storage.ColumnCount; int nz = storage.NonZerosCount; var result = CompressedColumnStorage <T> .Create(nrows, ncols); var ap = result.ColumnPointers = new int[ncols + 1]; if (nz == 0) { return(result); } var values = storage.Values; var rowind = storage.RowIndices; var colind = storage.ColumnIndices; if (inplace) { ConvertInPlace(ncols, nz, values, rowind, colind, ap); result.RowIndices = rowind; result.Values = values; // Make sure the data can't be accessed through the coordinate storage. storage.Invalidate(); } else { var columnCounts = new int[ncols]; for (int k = 0; k < nz; k++) { // Count columns columnCounts[colind[k]]++; } // Get column pointers int valueCount = Helper.CumulativeSum(ap, columnCounts, ncols); var ai = new int[valueCount]; var ax = new T[valueCount]; for (int k = 0; k < nz; k++) { int p = columnCounts[colind[k]]++; ai[p] = rowind[k]; ax[p] = values[k]; } result.RowIndices = ai; result.Values = ax; } Helper.SortIndices(result); if (cleanup) { result.Cleanup(); } return(result); }