public SimpleSVD(Matrix mat, bool compact) { this.mat = mat; this.is64 = mat is DMatrixRMaj; if (is64) { DMatrixRMaj m = (DMatrixRMaj)mat; svd = (SingularValueDecomposition <T>)DecompositionFactory_DDRM.svd(m.numRows, m.numCols, true, true, compact); } else { FMatrixRMaj m = (FMatrixRMaj)mat; svd = (SingularValueDecomposition <T>)DecompositionFactory_FDRM.svd(m.numRows, m.numCols, true, true, compact); } if (!svd.decompose((T)mat)) { throw new InvalidOperationException("Decomposition failed"); } U = SimpleMatrix <T> .wrap(svd.getU(null, false)); W = SimpleMatrix <T> .wrap(svd.getW(null)); V = SimpleMatrix <T> .wrap(svd.getV(null, false)); // order singular values from largest to smallest if (is64) { var um = U.getMatrix() as DMatrixRMaj; var wm = W.getMatrix() as DMatrixRMaj; var vm = V.getMatrix() as DMatrixRMaj; SingularOps_DDRM.descendingOrder(um, false, wm, vm, false); tol = SingularOps_DDRM.singularThreshold((SingularValueDecomposition_F64 <DMatrixRMaj>)svd); } else { var um = U.getMatrix() as FMatrixRMaj; var wm = W.getMatrix() as FMatrixRMaj; var vm = V.getMatrix() as FMatrixRMaj; SingularOps_FDRM.descendingOrder(um, false, wm, vm, false); tol = SingularOps_FDRM.singularThreshold((SingularValueDecomposition_F32 <FMatrixRMaj>)svd); } }
/** * This method computes the eigen vector with the largest eigen value by using the * direct power method. This technique is the easiest to implement, but the slowest to converge. * Works only if all the eigenvalues are real. * * @param A The matrix. Not modified. * @return If it converged or not. */ public bool computeDirect(FMatrixRMaj A) { initPower(A); bool converged = false; for (int i = 0; i < maxIterations && !converged; i++) { // q0.print(); CommonOps_FDRM.mult(A, q0, q1); float s = NormOps_FDRM.normPInf(q1); CommonOps_FDRM.divide(q1, s, q2); converged = checkConverged(A); } return(converged); }
public void setup(FMatrixRMaj A) { if (A.numRows != A.numCols) { throw new InvalidOperationException("Must be square"); } if (N != A.numRows) { N = A.numRows; this.A = (FMatrixRMaj)A.copy(); u = new FMatrixRMaj(A.numRows, 1); _temp = new FMatrixRMaj(A.numRows, 1); numStepsFind = new int[A.numRows]; } else { this.A.set(A); UtilEjml.memset(numStepsFind, 0, numStepsFind.Length); } // zero all the off numbers that should be zero for a hessenberg matrix for (int i = 2; i < N; i++) { for (int j = 0; j < i - 1; j++) { this.A.set(i, j, 0); } } eigenvalues = new Complex_F32[A.numRows]; for (int i = 0; i < eigenvalues.Length; i++) { eigenvalues[i] = new Complex_F32(); } numEigen = 0; lastExceptional = 0; numExceptional = 0; steps = 0; }
private void initPower(FMatrixRMaj A) { if (A.numRows != A.numCols) { throw new ArgumentException("A must be a square matrix."); } if (seed != null) { q0.set(seed); } else { for (int i = 0; i < A.numRows; i++) { q0.data[i] = 1; } } }
/** * <p> * Performs a rank one update on matrix A using vectors u and w. The results are stored in A.<br> * <br> * A = A + γ u w<sup>T</sup><br> * </p> * <p> * This is called a rank1 update because the matrix u w<sup>T</sup> has a rank of 1. * </p> * * @param gamma A scalar. * @param A A m by m matrix. Modified. * @param u A vector with m elements. Not modified. */ public static void rank1Update(float gamma, FMatrixRMaj A, FMatrixRMaj u, FMatrixRMaj w) { int n = u.getNumElements(); int matrixIndex = 0; for (int i = 0; i < n; i++) { float elementU = u.data[i]; for (int j = 0; j < n; j++) { A.data[matrixIndex++] += gamma * elementU * w.data[j]; } } }
/** * Performs a variety of tests to see if the provided matrix is a valid * covariance matrix. * * @return 0 = is valid 1 = failed positive diagonal, 2 = failed on symmetry, 2 = failed on positive definite */ public static int isValid(FMatrixRMaj cov) { if (!MatrixFeatures_FDRM.isDiagonalPositive(cov)) { return(1); } if (!MatrixFeatures_FDRM.isSymmetric(cov, TOL)) { return(2); } if (!MatrixFeatures_FDRM.isPositiveSemidefinite(cov)) { return(3); } return(0); }
/** * Creates a random matrix where all elements are zero but diagonal elements. Diagonal elements * randomly drawn from a uniform distribution from min to max, inclusive. * * @param numRows Number of rows in the returned matrix.. * @param numCols Number of columns in the returned matrix. * @param min Minimum value of a diagonal element. * @param max Maximum value of a diagonal element. * @param rand Random number generator. * @return A random diagonal matrix. */ public static FMatrixRMaj diagonal(int numRows, int numCols, float min, float max, IMersenneTwister rand) { if (max < min) { throw new ArgumentException("The max must be >= the min"); } FMatrixRMaj ret = new FMatrixRMaj(numRows, numCols); int N = Math.Min(numRows, numCols); float r = max - min; for (int i = 0; i < N; i++) { ret.set(i, i, rand.NextFloat() * r + min); } return(ret); }
/** * Extracts the tridiagonal matrix found in the decomposition. * * @param T If not null then the results will be stored here. Otherwise a new matrix will be created. * @return The extracted T matrix. */ public FMatrixRMaj getT(FMatrixRMaj T) { T = UtilDecompositons_FDRM.checkZeros(T, N, N); T.data[0] = QT.data[0]; T.data[1] = QT.data[1]; for (int i = 1; i < N - 1; i++) { T.set(i, i, QT.get(i, i)); T.set(i, i + 1, QT.get(i, i + 1)); T.set(i, i - 1, QT.get(i - 1, i)); } T.data[(N - 1) * N + N - 1] = QT.data[(N - 1) * N + N - 1]; T.data[(N - 1) * N + N - 2] = QT.data[(N - 2) * N + N - 1]; return(T); }
/** * Creates a random symmetric positive definite matrix. * * @param width The width of the square matrix it returns. * @param rand Random number generator used to make the matrix. * @return The random symmetric positive definite matrix. */ public static FMatrixRMaj symmetricPosDef(int width, IMersenneTwister rand) { // This is not formally proven to work. It just seems to work. FMatrixRMaj a = new FMatrixRMaj(width, 1); FMatrixRMaj b = new FMatrixRMaj(width, width); for (int i = 0; i < width; i++) { a.set(i, 0, rand.NextFloat()); } CommonOps_FDRM.multTransB(a, a, b); for (int i = 0; i < width; i++) { b.add(i, i, 1); } return(b); }
/** * Performs QR decomposition on A * * @param A not modified. */ public override bool setA(FMatrixRMaj A) { if (A.numRows > maxRows || A.numCols > maxCols) { setMaxSize(A.numRows, A.numCols); } _setA(A); if (!decomposer.decompose(A)) { return(false); } Q.reshape(numRows, numRows, false); R.reshape(numRows, numCols, false); decomposer.getQ(Q, false); decomposer.getR(R, false); return(true); }
/** * Sets the size of the matrix being decomposed, declares new memory if needed, * and sets all helper functions to their initial value. */ public void reset(int N) { this.N = N; this.diag = null; this.off = null; if (splits.Length < N) { splits = new int[N]; } numSplits = 0; x1 = 0; x2 = N - 1; steps = numExceptional = lastExceptional = 0; this.Q = null; }
/** * Sets the provided square matrix to be a random symmetric matrix whose values are selected from an uniform distribution * from min to max, inclusive. * * @param A The matrix that is to be modified. Must be square. Modified. * @param min Minimum value an element can have. * @param max Maximum value an element can have. * @param rand Random number generator. */ public static void symmetric(FMatrixRMaj A, float min, float max, IMersenneTwister rand) { if (A.numRows != A.numCols) { throw new ArgumentException("A must be a square matrix"); } float range = max - min; int length = A.numRows; for (int i = 0; i < length; i++) { for (int j = i; j < length; j++) { float val = rand.NextFloat() * range + min; A.set(i, j, val); A.set(j, i, val); } } }
public static FMatrixRMaj convert(FMatrixSparseTriplet src, FMatrixRMaj dst) { if (dst == null) { dst = new FMatrixRMaj(src.numRows, src.numCols); } else { dst.reshape(src.numRows, src.numCols); dst.zero(); } for (int i = 0; i < src.nz_length; i++) { FMatrixSparseTriplet.Element e = src.nz_data[i]; dst.unsafe_set(e.row, e.col, e.value); } return(dst); }
public override bool setA(FMatrixRMaj A) { if (A.numRows != A.numCols) { throw new ArgumentException("Matrix must be square"); } _setA(A); if (decomposer.decompose(A)) { n = A.numCols; vv = decomposer._getVV(); t = decomposer.getT().data; return(true); } else { return(false); } }
/** * Creates a copy of a matrix but swaps the rows as specified by the order array. * * @param order Specifies which row in the dest corresponds to a row in the src. Not modified. * @param src The original matrix. Not modified. * @param dst A Matrix that is a row swapped copy of src. Modified. */ public static FMatrixRMaj copyChangeRow(int[] order, FMatrixRMaj src, FMatrixRMaj dst) { if (dst == null) { dst = new FMatrixRMaj(src.numRows, src.numCols); } else if (src.numRows != dst.numRows || src.numCols != dst.numCols) { throw new ArgumentException("src and dst must have the same dimensions."); } for (int i = 0; i < src.numRows; i++) { int indexDst = i * src.numCols; int indexSrc = order[i] * src.numCols; Array.Copy(src.data, indexSrc, dst.data, indexDst, src.numCols); } return(dst); }
/** * Computes the eigenvalue of the provided tridiagonal matrix. Note that only the upper portion * needs to be tridiagonal. The bottom diagonal is assumed to be the same as the top. * * @param sideLength Number of rows and columns in the input matrix. * @param diag Diagonal elements from tridiagonal matrix. Modified. * @param off Off diagonal elements from tridiagonal matrix. Modified. * @return true if it succeeds and false if it fails. */ public bool process(int sideLength, float[] diag, float[] off, float[] eigenvalues) { if (diag != null) { helper.init(diag, off, sideLength); } if (Q == null) { Q = CommonOps_FDRM.identity(helper.N); } helper.setQ(Q); this.followingScript = true; this.eigenvalues = eigenvalues; this.fastEigenvalues = false; return(_process()); }
public static void inv2(FMatrixRMaj mat, FMatrixRMaj inv, float scale) { float[] data = mat.data; float a11 = data[0] * scale; float a12 = data[1] * scale; float a21 = data[2] * scale; float a22 = data[3] * scale; float m11 = a22; float m12 = -(a21); float m21 = -(a12); float m22 = a11; float det = (a11 * m11 + a12 * m12) / scale; data = inv.data; data[0] = m11 / det; data[1] = m21 / det; data[2] = m12 / det; data[3] = m22 / det; }
public override void solve(FMatrixRMaj b, FMatrixRMaj x) { if (b.numCols != x.numCols || b.numRows != numRows || x.numRows != this.numCols) { throw new ArgumentException("Unexpected matrix size"); } int numCols = b.numCols; float[] dataB = b.data; float[] dataX = x.data; float[] vv = decomp._getVV(); // for( int j = 0; j < numCols; j++ ) { // for( int i = 0; i < this.numCols; i++ ) vv[i] = dataB[i*numCols+j]; // decomp._solveVectorInternal(vv); // for( int i = 0; i < this.numCols; i++ ) dataX[i*numCols+j] = vv[i]; // } for (int j = 0; j < numCols; j++) { int index = j; for (int i = 0; i < this.numCols; i++, index += numCols) { vv[i] = dataB[index]; } decomp._solveVectorInternal(vv); index = j; for (int i = 0; i < this.numCols; i++, index += numCols) { dataX[index] = vv[i]; } } if (doImprove) { improveSol(b, x); } }
/** * Sets the matrix 'inv' equal to the inverse of the matrix that was decomposed. * * @param inv Where the value of the inverse will be stored. Modified. */ public override void invert(FMatrixRMaj inv) { if (inv.numRows != n || inv.numCols != n) { throw new InvalidOperationException("Unexpected matrix dimension"); } if (inv.data == t) { throw new ArgumentException("Passing in the same matrix that was decomposed."); } float[] a = inv.data; if (decomposer.isLower()) { setToInverseL(a); } else { throw new InvalidOperationException("Implement"); } }
public override /**/ double quality() { return(SpecializedOps_FDRM.qualityTriangular(R)); } /** * Solves for X using the QR decomposition. * * @param B A matrix that is n by m. Not modified. * @param X An n by m matrix where the solution is written to. Modified. */ public override void solve(FMatrixRMaj B, FMatrixRMaj X) { if (X.numRows != numCols) { throw new ArgumentException("Unexpected dimensions for X"); } else if (B.numRows != numRows || B.numCols != X.numCols) { throw new ArgumentException("Unexpected dimensions for B"); } int BnumCols = B.numCols; Y.reshape(numRows, 1, false); Z.reshape(numRows, 1, false); // solve each column one by one for (int colB = 0; colB < BnumCols; colB++) { // make a copy of this column in the vector for (int i = 0; i < numRows; i++) { Y.data[i] = B.get(i, colB); } // Solve Qa=b // a = Q'b CommonOps_FDRM.multTransA(Q, Y, Z); // solve for Rx = b using the standard upper triangular solver TriangularSolver_FDRM.solveU(R.data, Z.data, numCols); // save the results for (int i = 0; i < numCols; i++) { X.set(i, colB, Z.data[i]); } } }
/** * An orthogonal matrix that has the following property: T = Q<sup>T</sup>AQ * * @param Q If not null then the results will be stored here. Otherwise a new matrix will be created. * @return The extracted Q matrix. */ public FMatrixRMaj getQ(FMatrixRMaj Q) { Q = UtilDecompositons_FDRM.checkIdentity(Q, N, N); for (int i = 0; i < N; i++) { w[i] = 0; } for (int j = N - 2; j >= 0; j--) { w[j + 1] = 1; for (int i = j + 2; i < N; i++) { w[i] = QT.get(j, i); } QrHelperFunctions_FDRM.rank1UpdateMultR(Q, w, gammas[j + 1], j + 1, j + 1, N, b); // Q.print(); } return(Q); }
/** * Computes the most dominant eigen vector of A using an inverted shifted matrix. * The inverted shifted matrix is defined as <b>B = (A - αI)<sup>-1</sup></b> and * can converge faster if α is chosen wisely. * * @param A An invertible square matrix matrix. * @param alpha Shifting factor. * @return If it converged or not. */ public bool computeShiftInvert(FMatrixRMaj A, float alpha) { initPower(A); LinearSolverDense <FMatrixRMaj> solver = LinearSolverFactory_FDRM.linear(A.numCols); SpecializedOps_FDRM.addIdentity(A, B, -alpha); solver.setA(B); bool converged = false; for (int i = 0; i < maxIterations && !converged; i++) { solver.solve(q0, q1); float s = NormOps_FDRM.normPInf(q1); CommonOps_FDRM.divide(q1, s, q2); converged = checkConverged(A); } return(converged); }
/** * <p> * Induced matrix p = infinity norm.<br> * <br> * ||A||<sub>∞</sub> = max(i=1 to m; sum(j=1 to n; |a<sub>ij</sub>|)) * </p> * * @param A A matrix. * @return the norm. */ public static float inducedPInf(FMatrixRMaj A) { float max = 0; int m = A.numRows; int n = A.numCols; for (int i = 0; i < m; i++) { float total = 0; for (int j = 0; j < n; j++) { total += Math.Abs(A.get(i, j)); } if (total > max) { max = total; } } return(max); }
/** * Creates a new SimpleMatrix which is a copy of the Matrix. * * @param orig The original matrix whose value is copied. Not modified. */ public SimpleMatrix(Matrix orig) { Matrix mat; if (orig is DMatrixRBlock) { DMatrixRMaj a = new DMatrixRMaj(orig.getNumRows(), orig.getNumCols()); ConvertDMatrixStruct.convert((DMatrixRBlock)orig, a); mat = a; } else if (orig is FMatrixRBlock) { FMatrixRMaj a = new FMatrixRMaj(orig.getNumRows(), orig.getNumCols()); ConvertFMatrixStruct.convert((FMatrixRBlock)orig, a); mat = a; } else { mat = orig.copy(); } setMatrix((T)mat); }
public override /**/ double quality() { return(decomp.quality()); } public override void invert(FMatrixRMaj A_inv) { float[] vv = decomp._getVV(); FMatrixRMaj LU = decomp.getLU(); if (A_inv.numCols != LU.numCols || A_inv.numRows != LU.numRows) { throw new ArgumentException("Unexpected matrix dimension"); } int n = A.numCols; float[] dataInv = A_inv.data; for (int j = 0; j < n; j++) { // don't need to change inv into an identity matrix before hand for (int i = 0; i < n; i++) { vv[i] = i == j ? 1 : 0; } decomp._solveVectorInternal(vv); // for( int i = 0; i < n; i++ ) dataInv[i* n +j] = vv[i]; int index = j; for (int i = 0; i < n; i++, index += n) { dataInv[index] = vv[i]; } } } /** * This attempts to improve upon the solution generated by account * for numerical imprecisions. See numerical recipes for more information. It * is assumed that solve has already been run on 'b' and 'x' at least once. * * @param b A matrix. Not modified. * @param x A matrix. Modified. */ public void improveSol(FMatrixRMaj b, FMatrixRMaj x) { if (b.numCols != x.numCols) { throw new ArgumentException("bad shapes"); } float[] dataA = A.data; float[] dataB = b.data; float[] dataX = x.data; int nc = b.numCols; int n = b.numCols; float[] vv = decomp._getVV(); // BigDecimal sdp = new BigDecimal(0); for (int k = 0; k < nc; k++) { for (int i = 0; i < n; i++) { // *NOTE* in the book this is a long float. extra precision might be required float sdp = -dataB[i * nc + k]; // BigDecimal sdp = new BigDecimal(-dataB[ i * nc + k]); for (int j = 0; j < n; j++) { sdp += dataA[i * n + j] * dataX[j * nc + k]; // sdp = sdp.add( BigDecimal.valueOf(dataA[i* n +j] * dataX[ j * nc + k])); } vv[i] = sdp; // vv[i] = sdp.floatValue(); } decomp._solveVectorInternal(vv); for (int i = 0; i < n; i++) { dataX[i * nc + k] -= vv[i]; } } }
/** * <p> * Performs this operation:<br> * <br> * c = c - a<sup>T</sup>a <br> * where c is a submatrix. * </p> * * Only the upper triangle is updated. * * @param a A matrix. * @param c A matrix. * @param startIndexC start of the submatrix in c. */ public static void symmRankTranA_sub(FMatrixRMaj a, FMatrixRMaj c, int startIndexC) { // TODO update so that it doesn't modify/read parts that it shouldn't float[] dataA = a.data; float[] dataC = c.data; // for( int i = 0; i < a.numCols; i++ ) { // for( int k = 0; k < a.numRows; k++ ) { // float valA = dataA[k*a.numCols+i]; // // for( int j = i; j < a.numCols; j++ ) { // dataC[startIndexC+i*c.numCols+j] -= valA * dataA[k*a.numCols+j]; // } // } // } int strideC = c.numCols + 1; for (int i = 0; i < a.numCols; i++) { int indexA = i; int endR = a.numCols; for (int k = 0; k < a.numRows; k++, indexA += a.numCols, endR += a.numCols) { int indexC = startIndexC; float valA = dataA[indexA]; int indexR = indexA; while (indexR < endR) { dataC[indexC++] -= valA * dataA[indexR++]; } } startIndexC += strideC; } }
/** * Test for convergence by seeing if the element with the largest change * is smaller than the tolerance. In some test cases it alternated between * the + and - values of the eigen vector. When this happens it seems to have "converged" * to a non-dominant eigen vector. At least in the case I looked at. I haven't devoted * a lot of time into this issue... */ private bool checkConverged(FMatrixRMaj A) { float worst = 0; float worst2 = 0; for (int j = 0; j < A.numRows; j++) { float val = Math.Abs(q2.data[j] - q0.data[j]); if (val > worst) { worst = val; } val = Math.Abs(q2.data[j] + q0.data[j]); if (val > worst2) { worst2 = val; } } // swap vectors FMatrixRMaj temp = q0; q0 = q2; q2 = temp; if (worst < tol) { return(true); } else if (worst2 < tol) { return(true); } else { return(false); } }
private void solveWithLU(float real, int index, FMatrixRMaj r) { FMatrixRMaj A = new FMatrixRMaj(index, index); CommonOps_FDRM.extract(_implicit.A, 0, index, 0, index, A, 0, 0); for (int i = 0; i < index; i++) { A.add(i, i, -real); } r.reshape(index, 1, false); SpecializedOps_FDRM.subvector(_implicit.A, 0, index, index, false, 0, r); CommonOps_FDRM.changeSign(r); // TODO this must be very inefficient if (!solver.setA(A)) { throw new InvalidOperationException("Solve failed"); } solver.solve(r, r); }
/** * An unsafe but faster version of {@link #normP} that calls routines which are faster * but more prone to overflow/underflow problems. * * @param A Vector or matrix whose norm is to be computed. * @param p The p value of the p-norm. * @return The computed norm. */ public static float fastNormP(FMatrixRMaj A, float p) { if (p == 1) { return(normP1(A)); } else if (p == 2) { return(fastNormP2(A)); } else if (float.IsInfinity(p)) { return(normPInf(A)); } if (MatrixFeatures_FDRM.isVector(A)) { return(fastElementP(A, p)); } else { throw new ArgumentException("Doesn't support induced norms yet."); } }
/** * If needed declares and sets up internal data structures. * * @param A Matrix being decomposed. */ public void init(FMatrixRMaj A) { if (A.numRows != A.numCols) { throw new ArgumentException("Must be square"); } if (A.numCols != N) { N = A.numCols; QT.reshape(N, N, false); if (w.Length < N) { w = new float[N]; gammas = new float[N]; b = new float[N]; } } // just copy the top right triangle QT.set(A); }