public static CompressedColumnStorage FromMathNET(SparseMatrix matrix, ColumnOrdering ordering = ColumnOrdering.MinimumDegreeAtPlusA) { int n = matrix.RowCount; // Check for proper dimensions. if (n != matrix.ColumnCount) { throw new ArgumentException(Resources.MatrixMustBeSparse); } // Get CSR storage. var storage = (SparseCompressedRowMatrixStorage <double>)matrix.Storage; // Create CSparse matrix. var A = new CompressedColumnStorage(n, n); // Assign storage arrays. A.ColumnPointers = storage.RowPointers; A.RowIndices = storage.ColumnIndices; A.Values = storage.Values; return(A); }
/// <summary> /// Creates a sparse QR factorization. /// </summary> /// <param name="A">Column-compressed matrix, symmetric positive definite.</param> /// <param name="order">Ordering method to use (natural or A+A').</param> /// <param name="progress">Report progress (range from 0.0 to 1.0).</param> public static SparseQR Create(CompressedColumnStorage <double> A, ColumnOrdering order, IProgress <double> progress) { Check.NotNull(A, "A"); int m = A.RowCount; int n = A.ColumnCount; var C = new SparseQR(m, n); if (m >= n) { var p = AMD.Generate(A, order); // Ordering and symbolic analysis C.SymbolicAnalysis(A, p, order == ColumnOrdering.Natural); // Numeric QR factorization C.Factorize(A, progress); } else { // Ax=b is underdetermined var AT = A.Transpose(); var p = AMD.Generate(AT, order); // Ordering and symbolic analysis C.SymbolicAnalysis(AT, p, order == ColumnOrdering.Natural); // Numeric QR factorization of A' C.Factorize(AT, progress); } return(C); }
/// <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> /// Creates a sparse Cholesky factorization. /// </summary> /// <param name="A">Column-compressed matrix, symmetric positive definite.</param> /// <param name="p">Permutation.</param> public static SparseCholesky Create(CompressedColumnStorage <Complex> A, int[] p) { return(Create(A, p, null)); }
/// <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> /// Sparse Cholesky downdate, L*L' - w*w' /// </summary> /// <param name="w">The update matrix.</param> /// <returns>False, if updated matrix is not positive definite, otherwise true.</returns> public bool Downdate(CompressedColumnStorage <Complex> w) { return(UpDown(-1, w)); }
/// <summary> /// Sparse Cholesky update/downdate, L*L' + sigma*w*w' /// </summary> /// <param name="sigma">1 = update or -1 = downdate</param> /// <param name="w">The update matrix.</param> /// <returns>False, if updated matrix is not positive definite, otherwise true.</returns> private bool UpDown(int sigma, CompressedColumnStorage <double> w) { int n, p, f, j; double alpha, gamma, w1, w2; double beta = 1, beta2 = 1, delta; var parent = symFactor.parent; if (parent == null) { return(false); } var lp = L.ColumnPointers; var li = L.RowIndices; var lx = L.Values; var cp = w.ColumnPointers; var ci = w.RowIndices; var cx = w.Values; n = L.ColumnCount; if ((p = cp[0]) >= cp[1]) { return(true); // return if C empty } var work = new double[n]; // get workspace f = ci[p]; for (; p < cp[1]; p++) { // f = min (find (C)) f = Math.Min(f, ci[p]); } for (p = cp[0]; p < cp[1]; p++) { work[ci[p]] = cx[p]; } // Walk path f up to root. for (j = f; j != -1; j = parent[j]) { p = lp[j]; alpha = work[j] / lx[p]; // alpha = w(j) / L(j,j) beta2 = beta * beta + sigma * alpha * alpha; if (beta2 <= 0) { break; // TODO: not positive definite, throw? } beta2 = Math.Sqrt(beta2); delta = (sigma > 0) ? (beta / beta2) : (beta2 / beta); gamma = sigma * alpha / (beta2 * beta); lx[p] = delta * lx[p] + ((sigma > 0) ? (gamma * work[j]) : 0); beta = beta2; for (p++; p < lp[j + 1]; p++) { w1 = work[li[p]]; work[li[p]] = w2 = w1 - alpha * lx[p]; lx[p] = delta * lx[p] + gamma * ((sigma > 0) ? w1 : w2); } } return(beta2 > 0); }
/// <summary> /// Creates a sparse QR factorization. /// </summary> /// <param name="A">Column-compressed matrix, symmetric positive definite.</param> /// <param name="order">Ordering method to use (natural or A+A').</param> public static SparseQR Create(CompressedColumnStorage <double> A, ColumnOrdering order) { return(Create(A, order, null)); }
protected override CholmodSparse CreateSparse(CompressedColumnStorage <Complex> matrix, List <GCHandle> handles) { return(CholmodHelper.CreateSparse(matrix, Stype.General, handles)); }
/// <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); }
/// <summary> /// Computes the Kronecker product. /// </summary> private static CompressedColumnStorage <Complex> Kronecker(CompressedColumnStorage <Complex> A, CompressedColumnStorage <Complex> B) { var ap = A.ColumnPointers; var aj = A.RowIndices; var ax = A.Values; var bp = B.ColumnPointers; var bj = B.RowIndices; var bx = B.Values; int rowsA = A.RowCount; int rowsB = B.RowCount; var counts = new int[rowsA * rowsB]; int k = 0; // Count non-zeros in each row of kron(A, B). for (int i = 0; i < rowsA; i++) { for (int j = 0; j < rowsB; j++) { counts[k++] = (ap[i + 1] - ap[i]) * (bp[j + 1] - bp[j]); } } int colsA = A.ColumnCount; int colsB = B.ColumnCount; var C = new SparseMatrix(rowsA * rowsB, colsA * colsB); C.ColumnPointers = new int[colsA * colsB + 1]; int nnz = CumulativeSum(C.ColumnPointers, counts, counts.Length); var cj = C.RowIndices = new int[nnz]; var cx = C.Values = new Complex[nnz]; k = 0; // For each row in A ... for (int ia = 0; ia < rowsA; ia++) { // ... and each row in B ... for (int ib = 0; ib < rowsB; ib++) { // ... get element a_{ij} for (int j = ap[ia]; j < ap[ia + 1]; j++) { var idx = aj[j]; var aij = ax[j]; // ... and multiply it with current row of B for (int s = bp[ib]; s < bp[ib + 1]; s++) { cj[k] = (idx * colsB) + bj[s]; cx[k] = aij * bx[s]; k++; } } } } return(C); }
/// <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)); }
private double ComputeResidual(CompressedColumnStorage <double> A, double[] x, double[] b) { return(Helper.ComputeResidual(A, x, b)); }
/// <summary> /// Create CHOLMOD sparse matrix from managed type. /// </summary> /// <param name="matrix">The source matrix.</param> /// <param name="handles">List of handles.</param> /// <returns></returns> protected abstract CholmodSparse CreateSparse(CompressedColumnStorage <T> matrix, List <GCHandle> handles);
/// <summary> /// Sparse Cholesky update, L*L' + w*w' /// </summary> /// <param name="w">The update matrix.</param> /// <returns>False, if updated matrix is not pos /// itive definite, otherwise true.</returns> public bool Update(CompressedColumnStorage <double> w) { return(UpDown(1, w)); }
/// <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); }
/// <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 }
protected override void Solve() { InitNeuronCell(); //if (SomaOn) { U.SetSubVector(0, myCell.vertCount, setSoma(U, myCell.somaID, vstart)); } int nT; // Number of Time steps List <bool> channels = new List <bool> { false, false, false }; // For adding/removing channels // TODO: NEED TO DO THIS BETTER if (HK_auto) { h = 0.1 * NeuronCell.edgeLengths.Average(); //if (h > 0.29) { h = 0.29; } if (h <= 1) { k = h / 140; } if (h <= 0.5) { k = h / 70; } if (h <= 0.25) { k = h / 35; } if (h <= 0.12) { k = h / 18; } if (h <= 0.06) { k = h / 9; } if (h <= 0.03) { k = h / 5; } } // Number of time steps nT = (int)System.Math.Floor(endTime / k); // set some constants for the HINES matrix double diffConst = (1 / (2 * res * cap)); double cfl = diffConst * k / h; // reaction vector Vector R = Vector.Build.Dense(NeuronCell.vertCount); List <double> reactConst = new List <double> { gk, gna, gl, ek, ena, el }; // temporary voltage vector Vector tempV = Vector.Build.Dense(NeuronCell.vertCount); // Construct sparse RHS and LHS in coordinate storage format, no zeros are stored List <CoordinateStorage <double> > sparse_stencils = makeSparseStencils(NeuronCell, h, k, diffConst); // Compress the sparse matrices CompressedColumnStorage <double> r_csc = CompressedColumnStorage <double> .OfIndexed(sparse_stencils[0]); //null; CompressedColumnStorage <double> l_csc = CompressedColumnStorage <double> .OfIndexed(sparse_stencils[1]); //null; // Permutation matrix----------------------------------------------------------------------// int[] p = new int[NeuronCell.vertCount]; p = Permutation.Create(NeuronCell.vertCount, 0); CompressedColumnStorage <double> Id_csc = CompressedColumnStorage <double> .CreateDiagonal(NeuronCell.vertCount, 1); Id_csc.PermuteRows(p); //--------------------------------------------------------------------------------------------// // for solving Ax = b problem double[] b = new double[NeuronCell.vertCount]; // Apply column ordering to A to reduce fill-in. //var order = ColumnOrdering.MinimumDegreeAtPlusA; // Create Cholesky factorization setup var chl = SparseCholesky.Create(l_csc, p); //var chl = SparseCholesky.Create(l_csc, order); try { for (i = 0; i < nT; i++) { if (SomaOn) { U.SetSubVector(0, NeuronCell.vertCount, setSoma(U, NeuronCell.somaID, vstart)); } mutex.WaitOne(); r_csc.Multiply(U.ToArray(), b); // Peform b = rhs * U_curr // Diffusion solver chl.Solve(b, b); // Set U_next = b U.SetSubVector(0, NeuronCell.vertCount, Vector.Build.DenseOfArray(b)); // Save voltage from diffusion step for state probabilities tempV.SetSubVector(0, NeuronCell.vertCount, U); // Reaction channels[0] = na_ONOFF; channels[1] = k_ONOFF; channels[2] = leak_ONOFF; R.SetSubVector(0, NeuronCell.vertCount, reactF(reactConst, U, N, M, H, channels, NeuronCell.boundaryID)); R.Multiply(k / cap, R); // This is the solution for the voltage after the reaction is included! U.Add(R, U); //Now update state variables using FE on M,N,H N.Add(fN(tempV, N).Multiply(k), N); M.Add(fM(tempV, M).Multiply(k), M); H.Add(fH(tempV, H).Multiply(k), H); //Always reset to IC conditions and boundary conditions (for now) U.SetSubVector(0, NeuronCell.vertCount, boundaryConditions(U, NeuronCell.boundaryID)); if (SomaOn) { U.SetSubVector(0, NeuronCell.vertCount, setSoma(U, NeuronCell.somaID, vstart)); } mutex.ReleaseMutex(); } } catch (Exception e) { GameManager.instance.DebugLogThreadSafe(e); } GameManager.instance.DebugLogSafe("Simulation Over."); }
/// <summary> /// Sparse Cholesky update/downdate, L*L' + sigma*w*w' /// </summary> /// <param name="sigma">1 = update or -1 = downdate</param> /// <param name="w">The update matrix.</param> /// <returns>False, if updated matrix is not positive definite, otherwise true.</returns> private bool UpDown(int sigma, CompressedColumnStorage <Complex> w) { int n, p, f, j; Complex alpha, gamma, w1, w2, phase; double beta = 1, beta2 = 1, delta; var parent = S.parent; if (parent == null) { return(false); } var lp = L.ColumnPointers; var li = L.RowIndices; var lx = L.Values; var cp = w.ColumnPointers; var ci = w.RowIndices; var cx = w.Values; n = L.ColumnCount; if ((p = cp[0]) >= cp[1]) { return(true); // return if C empty } var work = new Complex[n]; // get workspace f = ci[p]; for (; p < cp[1]; p++) { // f = min (find (C)) f = Math.Min(f, ci[p]); } for (p = cp[0]; p < cp[1]; p++) { work[ci[p]] = cx[p]; } // Walk path f up to root. for (j = f; j != -1; j = parent[j]) { p = lp[j]; alpha = work[j] / lx[p]; // alpha = w(j) / L(j,j) beta2 = beta * beta + sigma * (alpha * Complex.Conjugate(alpha)).Real; if (beta2 <= 0) { break; } beta2 = Math.Sqrt(beta2); delta = (sigma > 0) ? (beta / beta2) : (beta2 / beta); gamma = sigma * Complex.Conjugate(alpha) / (beta2 * beta); lx[p] = delta * lx[p] + ((sigma > 0) ? (gamma * work[j]) : 0); beta = beta2; phase = Complex.Abs(lx[p]) / lx[p]; // phase = abs(L(j,j)) / L(j,j) lx[p] *= phase; // L(j,j) = L(j,j) * phase for (p++; p < lp[j + 1]; p++) { w1 = work[li[p]]; work[li[p]] = w2 = w1 - alpha * lx[p]; lx[p] = delta * lx[p] + gamma * ((sigma > 0) ? w1 : w2); lx[p] *= phase; // L(i,j) = L(i,j) * phase } } return(beta2 > 0); }
private static void test01() //****************************************************************************80 // // Purpose: // // TEST01 tests ccs_WRITE using a tiny matrix. // // Discussion: // // This test uses a trivial matrix whose full representation is: // // 2 3 0 0 0 // 3 0 4 0 6 // A = 0 -1 -3 2 0 // 0 0 1 0 0 // 0 4 2 0 1 // // The 1-based CCS representation is // // # ICC CCC ACC // -- --- --- --- // 1 1 1 2 // 2 2 3 // // 3 1 3 3 // 4 3 -1 // 5 5 4 // // 6 2 6 4 // 7 3 -3 // 8 4 1 // 9 5 2 // // 10 3 10 2 // // 11 2 11 6 // 12 5 1 // // 13 * 13 // // Licensing: // // This code is distributed under the GNU LGPL license. // // Modified: // // 18 July 2014 // // Author: // // John Burkardt // { const int N = 5; const int NCC = 12; double[] acc = { 2.0, 3.0, 3.0, -1.0, 4.0, 4.0, -3.0, 1.0, 2.0, 2.0, 6.0, 1.0 } ; int[] ccc = { 1, 3, 6, 10, 11, 13 } ; int[] icc = { 1, 2, 1, 3, 5, 2, 3, 4, 5, 3, 2, 5 } ; const string prefix = "simple"; Console.WriteLine(""); Console.WriteLine("TEST01"); Console.WriteLine(" Write a sparse matrix in CCS format to 3 files."); // // Full storage statistics // Console.WriteLine(""); Console.WriteLine(" Full rows M = " + N + ""); Console.WriteLine(" Full columns N = " + N + ""); Console.WriteLine(" Full storage = " + N * N + ""); // // Decrement the 1-based data. // typeMethods.i4vec_dec(N + 1, ref ccc); typeMethods.i4vec_dec(NCC, ref icc); // // Print the CCS matrix. // CompressedColumnStorage.ccs_print(N, N, NCC, icc, ccc, acc, " The matrix in 0-based CCS format:"); // // Write the matrix to 3 files. // CompressedColumnStorage.ccs_write(prefix, NCC, N, icc, ccc, acc); }
Complex[] temp; // workspace #region Static methods /// <summary> /// Creates a sparse Cholesky factorization. /// </summary> /// <param name="A">Column-compressed matrix, symmetric positive definite.</param> /// <param name="order">Ordering method to use (natural or A+A').</param> public static SparseCholesky Create(CompressedColumnStorage <Complex> A, ColumnOrdering order) { return(Create(A, order, null)); }
/// <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> /// Apply the ith Householder vector to x. /// </summary> protected abstract bool ApplyHouseholder(CompressedColumnStorage <T> V, int i, double beta, T[] x);
Complex[] temp; // workspace #region Static methods /// <summary> /// Creates a LU factorization. /// </summary> /// <param name="A">Column-compressed matrix, must be square.</param> /// <param name="order">Ordering method to use (natural or A+A').</param> /// <param name="tol">Partial pivoting tolerance (form 0.0 to 1.0).</param> public static SparseLU Create(CompressedColumnStorage <Complex> A, ColumnOrdering order, double tol) { return(Create(A, order, tol, null)); }
/// <summary> /// Ordering and symbolic analysis for a LDL' factorization. /// </summary> /// <param name="order">Column ordering.</param> /// <param name="A">Matrix to factorize.</param> private void SymbolicAnalysis(ColumnOrdering order, CompressedColumnStorage <double> A) { int n = A.ColumnCount; var sym = this.S = new SymbolicFactorization(); var ap = A.ColumnPointers; var ai = A.RowIndices; // P = amd(A+A') or natural var P = AMD.Generate(A, order); var Pinv = Permutation.Invert(P); // Output: column pointers and elimination tree. var lp = new int[n + 1]; var parent = new int[n]; // Workspace var lnz = new int[n]; var flag = new int[n]; int i, k, p, kk, p2; for (k = 0; k < n; k++) { // L(k,:) pattern: all nodes reachable in etree from nz in A(0:k-1,k) parent[k] = -1; // parent of k is not yet known 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++) { // A(i,k) is nonzero (original or permuted A) i = (Pinv != null) ? (Pinv[ai[p]]) : (ai[p]); if (i < k) { // follow path from i to root of etree, stop at flagged node for (; flag[i] != k; i = parent[i]) { // find parent of i if not yet determined if (parent[i] == -1) { parent[i] = k; } lnz[i]++; // L(k,i) is nonzero flag[i] = k; // mark i as visited } } } } // construct Lp index array from Lnz column counts lp[0] = 0; for (k = 0; k < n; k++) { lp[k + 1] = lp[k] + lnz[k]; } sym.parent = parent; sym.cp = lp; sym.q = P; sym.pinv = Pinv; }
/// <summary> /// Creates a LU factorization. /// </summary> /// <param name="A">Column-compressed matrix, must be square.</param> /// <param name="order">Ordering method to use (natural or A+A').</param> /// <param name="tol">Partial pivoting tolerance (form 0.0 to 1.0).</param> /// <param name="progress">Report progress (range from 0.0 to 1.0).</param> public static SparseLU Create(CompressedColumnStorage <Complex> A, ColumnOrdering order, double tol, IProgress <double> progress) { return(Create(A, AMD.Generate(A, order), tol, progress)); }
/// <summary> /// Initializes a new instance of the <see cref="CholeskySolver"/> class. /// </summary> /// <param name="a">A.</param> public CholeskySolver(CompressedColumnStorage a) { A = a; }
/// <summary> /// Creates a LU factorization. /// </summary> /// <param name="A">Column-compressed matrix, must be square.</param> /// <param name="p">Fill-reducing column permutation.</param> /// <param name="tol">Partial pivoting tolerance (form 0.0 to 1.0).</param> public static SparseLU Create(CompressedColumnStorage <Complex> A, int[] p, double tol) { return(Create(A, p, tol, null)); }