/** * Performs matrix addition:<br> * C = αA + βB * * @param alpha scalar value multiplied against A * @param A Matrix * @param beta scalar value multiplied against B * @param B Matrix * @param C Output matrix. * @param gw (Optional) Storage for internal workspace. Can be null. * @param gx (Optional) Storage for internal workspace. Can be null. */ public static void add(double alpha, DMatrixSparseCSC A, double beta, DMatrixSparseCSC B, DMatrixSparseCSC C, IGrowArray gw, DGrowArray gx) { double[] x = TriangularSolver_DSCC.adjust(gx, A.numRows); int[] w = TriangularSolver_DSCC.adjust(gw, A.numRows, A.numRows); C.indicesSorted = false; C.nz_length = 0; for (int col = 0; col < A.numCols; col++) { C.col_idx[col] = C.nz_length; ImplSparseSparseMult_DSCC.multAddColA(A, col, alpha, C, col + 1, x, w); ImplSparseSparseMult_DSCC.multAddColA(B, col, beta, C, col + 1, x, w); // take the values in the dense vector 'x' and put them into 'C' int idxC0 = C.col_idx[col]; int idxC1 = C.col_idx[col + 1]; for (int i = idxC0; i < idxC1; i++) { C.nz_values[i] = x[C.nz_rows[i]]; } } }
/** * <p> * Solves for x in the following equation:<br> * <br> * A*x = b * </p> * * <p> * If the system could not be solved then false is returned. If it returns true * that just means the algorithm finished operating, but the results could still be bad * because 'A' is singular or nearly singular. * </p> * * <p> * If repeat calls to solve are being made then one should consider using {@link LinearSolverFactory_DSCC} * instead. * </p> * * <p> * It is ok for 'b' and 'x' to be the same matrix. * </p> * * @param a (Input) A matrix that is m by n. Not modified. * @param b (Input) A matrix that is n by k. Not modified. * @param x (Output) A matrix that is m by k. Modified. * * @return true if it could invert the matrix false if it could not. */ public static bool solve(DMatrixSparseCSC a, DMatrixRMaj b, DMatrixRMaj x) { LinearSolverSparse <DMatrixSparseCSC, DMatrixRMaj> solver; if (a.numRows > a.numCols) { solver = LinearSolverFactory_DSCC.qr(FillReducing.NONE); // todo specify a filling that makes sense } else { solver = LinearSolverFactory_DSCC.lu(FillReducing.NONE); } // Ensure that the input isn't modified if (solver.modifiesA()) { a = (DMatrixSparseCSC)a.copy(); } if (solver.modifiesB()) { b = (DMatrixRMaj)b.copy(); } // decompose then solve the matrix if (!solver.setA(a)) { return(false); } solver.solve(b, x); return(true); }
/** * Computes the quality of a triangular matrix, where the quality of a matrix * is defined in {@link LinearSolverDense#quality()}. In * this situation the quality os the absolute value of the product of * each diagonal element divided by the magnitude of the largest diagonal element. * If all diagonal elements are zero then zero is returned. * * @param T A matrix. * @return the quality of the system. */ public static double qualityTriangular(DMatrixSparseCSC T) { int N = Math.Min(T.numRows, T.numCols); double max = T.unsafe_get(0, 0); for (int i = 1; i < N; i++) { max = Math.Max(max, Math.Abs(T.unsafe_get(i, i))); } if (max == 0.0) { return(0.0); } double quality = 1.0; for (int i = 0; i < N; i++) { quality *= T.unsafe_get(i, i) / max; } return(Math.Abs(quality)); }
public static void removeZeros(DMatrixSparseCSC A, double tol) { int offset = 0; for (int i = 0; i < A.numCols; i++) { int idx0 = A.col_idx[i] + offset; int idx1 = A.col_idx[i + 1]; for (int j = idx0; j < idx1; j++) { double val = A.nz_values[j]; if (Math.Abs(val) > tol) { A.nz_rows[j - offset] = A.nz_rows[j]; A.nz_values[j - offset] = val; } else { offset++; } } A.col_idx[i + 1] -= offset; } A.nz_length -= offset; }
/** * <p>Determines which elements in 'X' will be non-zero when the system below is solved for.</p> * G*X = B * * <p>xi will contain a list of ordered row indexes in B which will be modified starting at xi[top] to xi[n-1]. top * is the value returned by this function.</p> * * <p>See cs_reach in dsparse library to understand the algorithm. This code follow the spirit but not * the details because of differences in the contract.</p> * * @param G (Input) Lower triangular system matrix. Diagonal elements are assumed to be not zero. Not modified. * @param B (Input) Matrix B. Not modified. * @param colB Column in B being solved for * @param pinv (Input, Optional) Column pivots in G. Null if no pivots. * @param xi (Output) List of row indices in B which are non-zero in graph order. Must have length B.numRows * @param gwork workspace array used internally. Can be null. * @return Returns the index of the first element in the xi list. Also known as top. */ public static int searchNzRowsInB(DMatrixSparseCSC G, DMatrixSparseCSC B, int colB, int[] pinv, int[] xi, IGrowArray gwork) { if (xi.Length < B.numRows) { throw new ArgumentException("xi must be at least this long: " + B.numRows); } // this is a change from csparse. CSparse marks an entry by modifying G then reverting it. This can cause // weird unexplained behavior when people start using threads... int[] w = adjust(gwork, B.numRows * 2, B.numRows); // use 'w' as a marker to know which rows in B have been examined. 0 = unexamined and 1 = examined int idx0 = B.col_idx[colB]; int idx1 = B.col_idx[colB + 1]; int top = G.numRows; for (int i = idx0; i < idx1; i++) { int rowB = B.nz_rows[i]; if (w[rowB] == 0) { top = searchNzRowsInB_DFS(rowB, G, top, pinv, xi, w); } } return(top); }
/** * Computes the solution to the triangular system. * * @param G (Input) Lower or upper triangular matrix. diagonal elements must be non-zero. Not modified. * @param lower true for lower triangular and false for upper * @param B (Input) Matrix. Not modified. * @param X (Output) Solution * @param g_x (Optional) Storage for workspace. * @param g_xi (Optional) Storage for workspace. * @param g_w (Optional) Storage for workspace. */ public static void solve(DMatrixSparseCSC G, bool lower, DMatrixSparseCSC B, DMatrixSparseCSC X, DGrowArray g_x, IGrowArray g_xi, IGrowArray g_w) { double[] x = adjust(g_x, G.numRows); if (g_xi == null) { g_xi = new IGrowArray(); } int[] xi = adjust(g_xi, G.numRows); X.nz_length = 0; X.col_idx[0] = 0; X.indicesSorted = false; for (int colB = 0; colB < B.numCols; colB++) { int top = solve(G, lower, B, colB, x, null, g_xi, g_w); int nz_count = X.numRows - top; if (X.nz_values.Length < X.nz_length + nz_count) { X.growMaxLength(X.nz_length * 2 + nz_count, true); } for (int p = top; p < X.numRows; p++, X.nz_length++) { X.nz_rows[X.nz_length] = xi[p]; X.nz_values[X.nz_length] = x[xi[p]]; } X.col_idx[colB + 1] = X.nz_length; } }
/** * Initializes class data structures and parameters */ void initialize(DMatrixSparseCSC A) { m = A.numRows; n = A.numCols; int s = 4 * n + (ata ? (n + m + 1) : 0); gw.reshape(s); w = gw.data; // compute the transpose of A At.reshape(A.numCols, A.numRows, A.nz_length); CommonOps_DSCC.transpose(A, At, gw); // initialize w //Arrays.fill(w, 0, s, -1); // assign all values in workspace to -1 for (var i = 0; i < s; i++) { w[i] = -1; } ancestor = 0; maxfirst = n; prevleaf = 2 * n; first = 3 * n; }
/** * <p> * Performs a matrix inversion operation that does not modify the original * and stores the results in another matrix. The two matrices must have the * same dimension.<br> * <br> * B = A<sup>-1</sup> * </p> * * <p> * If the algorithm could not invert the matrix then false is returned. If it returns true * that just means the algorithm finished. The results could still be bad * because the matrix is singular or nearly singular. * </p> * * <p> * For medium to large matrices there might be a slight performance boost to using * {@link LinearSolverFactory_DSCC} instead. * </p> * * @param A (Input) The matrix that is to be inverted. Not modified. * @param inverse (Output) Where the inverse matrix is stored. Modified. * @return true if it could invert the matrix false if it could not. */ public static bool invert(DMatrixSparseCSC A, DMatrixRMaj inverse) { if (A.numRows != A.numCols) { throw new ArgumentException("A must be a square matrix"); } if (A.numRows != inverse.numRows || A.numCols != inverse.numCols) { throw new ArgumentException("A and inverse must have the same shape."); } LinearSolverSparse <DMatrixSparseCSC, DMatrixRMaj> solver; solver = LinearSolverFactory_DSCC.lu(FillReducing.NONE); // Ensure that the input isn't modified if (solver.modifiesA()) { A = (DMatrixSparseCSC)A.copy(); } DMatrixRMaj I = CommonOps_DDRM.identity(A.numRows); // decompose then solve the matrix if (!solver.setA(A)) { return(false); } solver.solve(I, inverse); return(true); }
/** * Converts the permutation matrix into a vector * @param P (Input) Permutation matrix * @param vector (Output) Permutation vector */ public static void permutationVector(DMatrixSparseCSC P, int[] vector) { if (P.numCols != P.numRows) { throw new ArgumentException("Expected a square matrix"); } else if (P.nz_length != P.numCols) { throw new ArgumentException("Expected N non-zero elements in permutation matrix"); } else if (vector.Length < P.numCols) { throw new ArgumentException("vector is too short"); } int M = P.numCols; for (int i = 0; i < M; i++) { if (P.col_idx[i + 1] != i + 1) { throw new ArgumentException("Unexpected number of elements in a column"); } vector[P.nz_rows[i]] = i; } }
public static DMatrixRMaj convert(DMatrixSparseCSC src, DMatrixRMaj dst) { if (dst == null) { dst = new DMatrixRMaj(src.numRows, src.numCols); } else { dst.reshape(src.numRows, src.numCols); dst.zero(); } int idx0 = src.col_idx[0]; for (int j = 1; j <= src.numCols; j++) { int idx1 = src.col_idx[j]; for (int i = idx0; i < idx1; i++) { int row = src.nz_rows[i]; double val = src.nz_values[i]; dst.unsafe_set(row, j - 1, val); } idx0 = idx1; } return(dst); }
public static bool isTranspose(DMatrixSparseCSC A, DMatrixSparseCSC B, double tol) { if (A.numCols != B.numRows || A.numRows != B.numCols) { return(false); } if (A.nz_length != B.nz_length) { return(false); } if (!A.indicesSorted) { throw new ArgumentException("A must have sorted indicies"); } DMatrixSparseCSC Btran = new DMatrixSparseCSC(B.numCols, B.numRows, B.nz_length); CommonOps_DSCC.transpose(B, Btran, null); Btran.sortIndices(null); for (int i = 0; i < B.nz_length; i++) { if (A.nz_rows[i] != Btran.nz_rows[i]) { return(false); } if (Math.Abs(A.nz_values[i] - Btran.nz_values[i]) > tol) { return(false); } } return(true); }
/** * Converts DMatrixSparseTriplet into a DMatrixSparseCSC. Duplicate elements in triplet will result in an * illegal matrix in output having duplicate elements. * * @param src Original matrix which is to be copied. Not modified. * @param dst Destination. Will be a copy. Modified. * @param histStorage Workspace. Can be null. */ public static DMatrixSparseCSC convert(DMatrixSparseTriplet src, DMatrixSparseCSC dst, IGrowArray histStorage) { dst = UtilEjml.reshapeOrDeclare(dst, src.numRows, src.numCols, src.nz_length); int[] hist = UtilEjml.adjustClear(histStorage, src.numCols); // compute the number of elements in each columns for (int i = 0; i < src.nz_length; i++) { hist[src.nz_rowcol.data[i * 2 + 1]]++; } // define col_idx dst.histogramToStructure(hist); System.Array.Copy(dst.col_idx, 0, hist, 0, dst.numCols); // now write the row indexes and the values for (int i = 0; i < src.nz_length; i++) { int row = src.nz_rowcol.data[i * 2]; int col = src.nz_rowcol.data[i * 2 + 1]; double value = src.nz_value.data[i]; int index = hist[col]++; dst.nz_rows[index] = row; dst.nz_values[index] = value; } dst.indicesSorted = false; return(dst); }
/** * Computes and applies the fill reduction permutation. Either A is returned (unmodified) or the permutated * version of A. * @param A Input matrix. unmodified. * @return A permuted matrix. Might be A or a different matrix. */ public DMatrixSparseCSC apply(DMatrixSparseCSC A) { if (fillReduce == null) { return(A); } fillReduce.process(A); IGrowArray gp = fillReduce.getRow(); if (pinv.Length < gp.Length) { pinv = new int[gp.Length]; } CommonOps_DSCC.permutationInverse(gp.data, pinv, gp.Length); if (symmetric) { CommonOps_DSCC.permuteSymmetric(A, pinv, Aperm, gw); } else { CommonOps_DSCC.permuteRowInv(pinv, A, Aperm); } return(Aperm); }
/** * Performs the performing operation x = x + A(:,i)*alpha * * <p>NOTE: This is the same as cs_scatter() in csparse.</p> */ public static void multAddColA(DMatrixSparseCSC A, int colA, double alpha, DMatrixSparseCSC C, int mark, double[] x, int[] w) { int idxA0 = A.col_idx[colA]; int idxA1 = A.col_idx[colA + 1]; for (int j = idxA0; j < idxA1; j++) { int row = A.nz_rows[j]; if (w[row] < mark) { if (C.nz_length >= C.nz_rows.Length) { C.growMaxLength(C.nz_length * 2 + 1, true); } w[row] = mark; C.nz_rows[C.nz_length] = row; C.col_idx[mark] = ++C.nz_length; x[row] = A.nz_values[j] * alpha; } else { x[row] += A.nz_values[j] * alpha; } } }
public static void mult(DMatrixSparseCSC A, DMatrixRMaj B, DMatrixRMaj C) { C.zero(); // C(i,j) = sum_k A(i,k) * B(k,j) for (int k = 0; k < A.numCols; k++) { int idx0 = A.col_idx[k]; int idx1 = A.col_idx[k + 1]; for (int indexA = idx0; indexA < idx1; indexA++) { int i = A.nz_rows[indexA]; double valueA = A.nz_values[indexA]; int indexB = k * B.numCols; int indexC = i * C.numCols; int end = indexB + B.numCols; // for (int j = 0; j < B.numCols; j++) { while (indexB < end) { C.data[indexC++] += valueA * B.data[indexB++]; } } } }
public static DMatrixSparseTriplet convert(DMatrixSparseCSC src, DMatrixSparseTriplet dst) { if (dst == null) { dst = new DMatrixSparseTriplet(src.numRows, src.numCols, src.nz_length); } else { dst.reshape(src.numRows, src.numCols); } int i0 = src.col_idx[0]; for (int col = 0; col < src.numCols; col++) { int i1 = src.col_idx[col + 1]; for (int i = i0; i < i1; i++) { int row = src.nz_rows[i]; dst.addItem(row, col, src.nz_values[i]); } i0 = i1; } return(dst); }
/** * <p> * Checks to see if a matrix is orthogonal or isometric. * </p> * * @param Q The matrix being tested. Not modified. * @param tol Tolerance. * @return True if it passes the test. */ public static bool isOrthogonal(DMatrixSparseCSC Q, double tol) { if (Q.numRows < Q.numCols) { throw new ArgumentException("The number of rows must be more than or equal to the number of columns"); } IGrowArray gw = new IGrowArray(); DGrowArray gx = new DGrowArray(); for (int i = 0; i < Q.numRows; i++) { for (int j = i + 1; j < Q.numCols; j++) { double val = CommonOps_DSCC.dotInnerColumns(Q, i, Q, j, gw, gx); if (!(Math.Abs(val) <= tol)) { return(false); } } } return(true); }
public static bool isIdentity(DMatrixSparseCSC A, double tol) { if (A.numCols != A.numRows) { return(false); } if (A.nz_length != A.numCols) { return(false); } for (int i = 1; i <= A.numCols; i++) { if (A.col_idx[i] != i) { return(false); } if (Math.Abs(A.nz_values[i - 1] - 1) > tol) { return(false); } } return(true); }
/** * Checks to see if row indicies are sorted into ascending order. O(N) * @return true if sorted and false if not */ public static bool checkIndicesSorted(DMatrixSparseCSC A) { for (int j = 0; j < A.numCols; j++) { int idx0 = A.col_idx[j]; int idx1 = A.col_idx[j + 1]; if (idx0 != idx1 && A.nz_rows[idx0] >= A.numRows) { return(false); } for (int i = idx0 + 1; i < idx1; i++) { int row = A.nz_rows[i]; if (A.nz_rows[i - 1] >= row) { return(false); } if (row >= A.numRows) { return(false); } } } return(true); }
/** * Applies the row permutation specified by the vector to the input matrix and save the results * in the output matrix. output[perm[j],:] = input[j,:] * * @param permInv (Input) Inverse permutation vector. Specifies new order of the rows. * @param input (Input) Matrix which is to be permuted * @param output (Output) Matrix which has the permutation stored in it. Is reshaped. */ public static void permuteRowInv(int[] permInv, DMatrixSparseCSC input, DMatrixSparseCSC output) { if (input.numRows > permInv.Length) { throw new ArgumentException("permutation vector must have at least as many elements as input has rows"); } output.reshape(input.numRows, input.numCols, input.nz_length); output.nz_length = input.nz_length; output.indicesSorted = false; Array.Copy(input.nz_values, 0, output.nz_values, 0, input.nz_length); Array.Copy(input.col_idx, 0, output.col_idx, 0, input.numCols + 1); int idx0 = 0; for (int i = 0; i < input.numCols; i++) { int idx1 = output.col_idx[i + 1]; for (int j = idx0; j < idx1; j++) { output.nz_rows[j] = permInv[input.nz_rows[j]]; } idx0 = idx1; } }
/** * Creates a submatrix by extracting the specified rows from A. rows = {row0 %le; i %le; row1}. * @param A (Input) matrix * @param row0 First row. Inclusive * @param row1 Last row+1. * @param out (Output, Option) Storage for output matrix * @return The submatrix */ public static DMatrixSparseCSC extractRows(DMatrixSparseCSC A, int row0, int row1, DMatrixSparseCSC output) { if (output == null) { output = new DMatrixSparseCSC(1, 1, 1); } output.reshape(row1 - row0, A.numCols, A.nz_length); // output.col_idx[0] = 0; // output.nz_length = 0; for (int col = 0; col < A.numCols; col++) { int idx0 = A.col_idx[col]; int idx1 = A.col_idx[col + 1]; for (int i = idx0; i < idx1; i++) { int row = A.nz_rows[i]; if (row >= row0 && row < row1) { output.nz_values[output.nz_length] = A.nz_values[i]; output.nz_rows[output.nz_length++] = row - row0; } } output.col_idx[col + 1] = output.nz_length; } return(output); }
/// <summary> /// Create a simple matrix of the specified type /// </summary> /// <param name="numRows"> The number of rows in the matrix. </param> /// <param name="numCols"> The number of columns in the matrix. </param> /// <param name="type"> The matrix type </param> public SimpleMatrix(int numRows, int numCols, MatrixType type) { switch (type.tipo) { case Types.DDRM: mat = new DMatrixRMaj(numRows, numCols); break; case Types.FDRM: //mat = new FMatrixRMaj(numRows, numCols); break; case Types.ZDRM: mat = new ZMatrixRMaj(numRows, numCols); break; case Types.CDRM: //mat = new CMatrixRMaj(numRows, numCols); break; case Types.DSCC: mat = new DMatrixSparseCSC(numRows, numCols); break; case Types.FSCC: //mat = new FMatrixSparseCSC(numRows, numCols); break; default: throw new Exception("Unknown matrix type"); } }
/** * Converts the permutation vector into a matrix. B = P*A. B[p[i],:] = A[i,:] * @param p (Input) Permutation vector * @param inverse (Input) If it is the inverse. B[i,:] = A[p[i],:) * @param P (Output) Permutation matrix */ public static DMatrixSparseCSC permutationMatrix(int[] p, bool inverse, int N, DMatrixSparseCSC P) { if (P == null) { P = new DMatrixSparseCSC(N, N, N); } else { P.reshape(N, N, N); } P.indicesSorted = true; P.nz_length = N; // each column should have one element inside of it if (!inverse) { for (int i = 0; i < N; i++) { P.col_idx[i + 1] = i + 1; P.nz_rows[p[i]] = i; P.nz_values[i] = 1; } } else { for (int i = 0; i < N; i++) { P.col_idx[i + 1] = i + 1; P.nz_rows[i] = p[i]; P.nz_values[i] = 1; } } return(P); }
/** * Randomly generates matrix with the specified number of non-zero elements filled with values from min to max. * * @param numRows Number of rows * @param numCols Number of columns * @param nz_total Total number of non-zero elements in the matrix * @param min Minimum element value, inclusive * @param max Maximum element value, inclusive * @param rand Random number generator * @return Randomly generated matrix */ public static DMatrixSparseCSC rectangle(int numRows, int numCols, int nz_total, double min, double max, IMersenneTwister rand) { nz_total = Math.Min(numCols * numRows, nz_total); int[] selected = UtilEjml.shuffled(numRows * numCols, nz_total, rand); Array.Sort(selected, 0, nz_total); DMatrixSparseCSC ret = new DMatrixSparseCSC(numRows, numCols, nz_total); ret.indicesSorted = true; // compute the number of elements in each column int[] hist = new int[numCols]; for (int i = 0; i < nz_total; i++) { hist[selected[i] / numRows]++; } // define col_idx ret.colsum(hist); for (int i = 0; i < nz_total; i++) { int row = selected[i] % numRows; ret.nz_rows[i] = row; ret.nz_values[i] = rand.NextDouble() * (max - min) + min; } return(ret); }
/** * Checks to see if the matrix is symmetric to within tolerance. * * @param A Matrix being tested. Not modified. * @param tol Tolerance that defines how similar two values must be to be considered identical * @return true if symmetric or false if not */ public static bool isSymmetric(DMatrixSparseCSC A, double tol) { if (A.numRows != A.numCols) { return(false); } int N = A.numCols; for (int i = 0; i < N; i++) { int idx0 = A.col_idx[i]; int idx1 = A.col_idx[i + 1]; for (int index = idx0; index < idx1; index++) { int j = A.nz_rows[index]; double value_ji = A.nz_values[index]; double value_ij = A.get(i, j); if (Math.Abs(value_ij - value_ji) > tol) { return(false); } } } return(true); }
public static bool checkSortedFlag(DMatrixSparseCSC A) { if (A.indicesSorted) { return(checkIndicesSorted(A)); } return(true); }
public static DMatrixSparseCSC identity(int numRows, int numCols) { int min = Math.Min(numRows, numCols); DMatrixSparseCSC A = new DMatrixSparseCSC(numRows, numCols, min); setToIdentity(A); return(A); }
/** * Performs matrix multiplication. C = A*B * * @param A Matrix * @param B Dense Matrix * @param C Dense Matrix */ public static void mult(DMatrixSparseCSC A, DMatrixRMaj B, DMatrixRMaj C) { if (A.numRows != C.numRows || B.numCols != C.numCols) { throw new ArgumentException("Inconsistent matrix shapes"); } ImplSparseSparseMult_DSCC.mult(A, B, C); }
public static DMatrixSparseCSC triangleUpper(int dimen, int hessenberg, int nz_total, double min, double max, IMersenneTwister rand) { DMatrixSparseCSC L = triangleLower(dimen, hessenberg, nz_total, min, max, rand); DMatrixSparseCSC U = (DMatrixSparseCSC)L.createLike(); CommonOps_DSCC.transpose(L, U, null); return(U); }
/** * Performs an element-wise multiplication.<br> * C[i,j] = A[i,j]*B[i,j]<br> * All matrices must have the same shape. * * @param A (Input) Matrix. * @param B (Input) Matrix * @param C (Ouptut) Matrix. * @param gw (Optional) Storage for internal workspace. Can be null. * @param gx (Optional) Storage for internal workspace. Can be null. */ public static void elementMult(DMatrixSparseCSC A, DMatrixSparseCSC B, DMatrixSparseCSC C, IGrowArray gw, DGrowArray gx) { if (A.numCols != B.numCols || A.numRows != B.numRows || A.numCols != C.numCols || A.numRows != C.numRows) { throw new ArgumentException("All inputs must have the same number of rows and columns"); } ImplCommonOps_DSCC.elementMult(A, B, C, gw, gx); }