/** * <p> * Adjusts the values of the Q and R matrices to take in account the effects of inserting * a row to the 'A' matrix at the specified location. This operation requires about 6mn + O(n) flops. * </p> * * <p> * If Q and/or R does not have enough data elements to grow then an exception is thrown. * </p> * * <p> * The adjustment done is by computing a series of planar Givens rotations that make the adjusted R * matrix upper triangular again. This is then used to modify the Q matrix. * </p> * * @param Q The Q matrix which is to be modified, must be big enough to grow. Must be n by n.. Is modified. * @param R The R matrix which is to be modified, must be big enough to grow. Must be m by n. Is modified. * @param row The row being inserted. Not modified. * @param rowIndex Which row index it is to be inserted at. * @param resizeR Should the number of rows in R be changed? The additional rows are all zero. */ public void addRow(FMatrixRMaj Q, FMatrixRMaj R, float[] row, int rowIndex, bool resizeR) { // memory management and check precoditions setQR(Q, R, 1); m_m = m + 1; if (Q.data.Length < m_m * m_m) { throw new ArgumentException("Q matrix does not have enough data to grow"); } if (resizeR && R.data.Length < m_m * n) { throw new ArgumentException("R matrix does not have enough data to grow"); } if (resizeR) { R.reshape(m_m, n, false); } U_tran.reshape(m_m, m_m, false); // apply givens rotation to the first two rows of the augmented R matrix applyFirstGivens(row); applyLaterGivens(); // compute new Q matrix updateInsertQ(rowIndex); // discard the reference since it is no longer needed this.Q = this.R = null; }
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; // get the pivots and transpose them int[] pivots = decomposition.getColPivots(); // solve each column one by one for (int colB = 0; colB < BnumCols; colB++) { x_basic.reshape(numRows, 1); Y.reshape(numRows, 1); // make a copy of this column in the vector for (int i = 0; i < numRows; i++) { Y.data[i] = B.get(i, colB); } // Solve Q*a=b => a = Q'*b CommonOps_FDRM.multTransA(Q, Y, x_basic); // solve for Rx = b using the standard upper triangular solver TriangularSolver_FDRM.solveU(R11.data, x_basic.data, rank); // finish the basic solution by filling in zeros x_basic.reshape(numCols, 1, true); for (int i = rank; i < numCols; i++) { x_basic.data[i] = 0; } if (norm2Solution && rank < numCols) { upgradeSolution(x_basic); } // save the results for (int i = 0; i < numCols; i++) { X.set(pivots[i], colB, x_basic.data[i]); } } }
public override bool setA(FMatrixRMaj A) { _setA(A); if (!decomposition.decompose(A)) { return(false); } rank = decomposition.getRank(); R.reshape(numRows, numCols); decomposition.getR(R, false); // extract the r11 triangle sub matrix R11.reshape(rank, rank); CommonOps_FDRM.extract(R, 0, rank, 0, rank, R11, 0, 0); if (norm2Solution && rank < numCols) { // extract the R12 sub-matrix W.reshape(rank, numCols - rank); CommonOps_FDRM.extract(R, 0, rank, rank, numCols, W, 0, 0); // W=inv(R11)*R12 TriangularSolver_FDRM.solveU(R11.data, 0, R11.numCols, R11.numCols, W.data, 0, W.numCols, W.numCols); // set the identity matrix in the upper portion W.reshape(numCols, W.numCols, true); for (int i = 0; i < numCols - rank; i++) { for (int j = 0; j < numCols - rank; j++) { if (i == j) { W.set(i + rank, j, -1); } else { W.set(i + rank, j, 0); } } } } return(true); }
/** * Performs QR decomposition on A * * @param A not modified. */ public override bool setA(FMatrixRMaj A) { if (A.numRows < A.numCols) { throw new ArgumentException("Can't solve for wide systems. More variables than equations."); } if (A.numRows > maxRows || A.numCols > maxCols) { setMaxSize(A.numRows, A.numCols); } R.reshape(A.numCols, A.numCols); a.reshape(A.numRows, 1); temp.reshape(A.numRows, 1); _setA(A); if (!decomposer.decompose(A)) { return(false); } gammas = decomposer.getGammas(); QR = decomposer.getQR(); decomposer.getR(R, true); return(true); }
private void solveEigenvectorDuplicateEigenvalue(float real, int first, bool isTriangle) { float scale = Math.Abs(real); if (scale == 0) { scale = 1; } eigenvectorTemp.reshape(N, 1, false); eigenvectorTemp.zero(); if (first > 0) { if (isTriangle) { solveUsingTriangle(real, first, eigenvectorTemp); } else { solveWithLU(real, first, eigenvectorTemp); } } eigenvectorTemp.reshape(N, 1, false); for (int i = first; i < N; i++) { Complex_F32 c = _implicit.eigenvalues[N - i - 1]; if (c.isReal() && Math.Abs(c.real - real) / scale < 100.0f * UtilEjml.F_EPS) { eigenvectorTemp.data[i] = 1; FMatrixRMaj v = new FMatrixRMaj(N, 1); CommonOps_FDRM.multTransA(Q, eigenvectorTemp, v); eigenvectors[N - i - 1] = v; NormOps_FDRM.normalizeF(v); eigenvectorTemp.data[i] = 0; } } }
/** * Compute the A matrix from the Q and R matrices. * * @return The A matrix. */ public override FMatrixRMaj getA() { if (A.data.Length < numRows * numCols) { A = new FMatrixRMaj(numRows, numCols); } A.reshape(numRows, numCols, false); CommonOps_FDRM.mult(Q, R, A); return(A); }
public override void invert(FMatrixRMaj A_inv) { if (A_inv.numCols != numRows || A_inv.numRows != numCols) { throw new ArgumentException("Unexpected dimensions for A_inv"); } I.reshape(numRows, numRows); CommonOps_FDRM.setIdentity(I); solve(I, A_inv); }
public override bool setA(FMatrixRMaj A) { if (!base.setA(A)) { return(false); } Q.reshape(A.numRows, A.numRows); decomposition.getQ(Q, false); return(true); }
/** * Updates the Q matrix to take in account the inserted matrix. * * @param rowIndex where the matrix has been inserted. */ private void updateInsertQ(int rowIndex) { Qm.set(Q); Q.reshape(m_m, m_m, false); for (int i = 0; i < rowIndex; i++) { for (int j = 0; j < m_m; j++) { float sum = 0; for (int k = 0; k < m; k++) { sum += Qm.data[i * m + k] * U_tran.data[j * m_m + k + 1]; } Q.data[i * m_m + j] = sum; } } for (int j = 0; j < m_m; j++) { Q.data[rowIndex * m_m + j] = U_tran.data[j * m_m]; } for (int i = rowIndex + 1; i < m_m; i++) { for (int j = 0; j < m_m; j++) { float sum = 0; for (int k = 0; k < m; k++) { sum += Qm.data[(i - 1) * m + k] * U_tran.data[j * m_m + k + 1]; } Q.data[i * m_m + j] = sum; } } }
public override /**/ double quality() { return(SpecializedOps_FDRM.qualityTriangular(R)); } /** * <p> * Upgrades the basic solution to the optimal 2-norm solution. * </p> * * <pre> * First solves for 'z' * * || x_b - P*[ R_11^-1 * R_12 ] * z ||2 * min z || [ - I_{n-r} ] || * * </pre> * * @param X basic solution, also output solution */ protected void upgradeSolution(FMatrixRMaj X) { FMatrixRMaj z = Y; // recycle Y // compute the z which will minimize the 2-norm of X // because of the identity matrix tacked onto the end 'A' should never be singular if (!internalSolver.setA(W)) { throw new InvalidOperationException("This should never happen. Is input NaN?"); } z.reshape(numCols - rank, 1); internalSolver.solve(X, z); // compute X by tweaking the original CommonOps_FDRM.multAdd(-1, W, z, X); }
private void init(FMatrixRBlock orig) { this.A = orig; int height = Math.Min(A.blockLength, A.numRows); V.reshape(height, A.numCols, A.blockLength, false); tmp.reshape(height, A.numCols, A.blockLength, false); if (gammas.Length < A.numCols) { gammas = new float[A.numCols]; } zerosM.reshape(A.blockLength, A.blockLength + 1, false); }
/** * 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); }
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]); } } }
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); }
/** * 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); }
/** * <p> * Adjusts the values of the Q and R matrices to take in account the effects of removing * a row from the 'A' matrix at the specified location. This operation requires about 6mn + O(n) flops. * </p> * * <p> * The adjustment is done by computing a series of planar Givens rotations that make the removed row in Q * equal to [1 0 ... 0]. * </p> * * @param Q The Q matrix. Is modified. * @param R The R matrix. Is modified. * @param rowIndex Which index of the row that is being removed. * @param resizeR should the shape of R be adjusted? */ public void deleteRow(FMatrixRMaj Q, FMatrixRMaj R, int rowIndex, bool resizeR) { setQR(Q, R, 0); if (m - 1 < n) { throw new ArgumentException("Removing any row would make the system under determined."); } m_m = m - 1; U_tran.reshape(m, m, false); if (resizeR) { R.reshape(m_m, n, false); } computeRemoveGivens(rowIndex); updateRemoveQ(rowIndex); updateRemoveR(); // discard the reference since it is no longer needed this.Q = this.R = null; }
/** * <p> * Performs Choleksy decomposition on the provided matrix. * </p> * * <p> * If the matrix is not positive definite then this function will return * false since it can't complete its computations. Not all errors will be * found. * </p> * @return True if it was able to finish the decomposition. */ protected override bool decomposeLower() { if (n < blockWidth) { B.reshape(0, 0, false); } else { B.reshape(blockWidth, n - blockWidth, false); } int numBlocks = n / blockWidth; int remainder = n % blockWidth; if (remainder > 0) { numBlocks++; } B.numCols = n; for (int i = 0; i < numBlocks; i++) { B.numCols -= blockWidth; if (B.numCols > 0) { // apply cholesky to the current block if (!chol.decompose(T, (i * blockWidth) * T.numCols + i * blockWidth, blockWidth)) { return(false); } int indexSrc = i * blockWidth * T.numCols + (i + 1) * blockWidth; int indexDst = (i + 1) * blockWidth * T.numCols + i * blockWidth; // B = L^(-1) * B solveL_special(chol.getL().data, T, indexSrc, indexDst, B); int indexL = (i + 1) * blockWidth * n + (i + 1) * blockWidth; // c = c - a^T*a symmRankTranA_sub(B, T, indexL); } else { int width = remainder > 0 ? remainder : blockWidth; if (!chol.decompose(T, (i * blockWidth) * T.numCols + i * blockWidth, width)) { return(false); } } } // zero the top right corner. for (int i = 0; i < n; i++) { for (int j = i + 1; j < n; j++) { t[i * n + j] = 0.0f; } } return(true); }
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; // get the pivots and transpose them int[] pivots = decomposition.getColPivots(); float[][] qr = decomposition.getQR(); float[] gammas = decomposition.getGammas(); // solve each column one by one for (int colB = 0; colB < BnumCols; colB++) { x_basic.reshape(numRows, 1); Y.reshape(numRows, 1); // make a copy of this column in the vector for (int i = 0; i < numRows; i++) { x_basic.data[i] = B.get(i, colB); } // Solve Q*x=b => x = Q'*b // Q_n*b = (I-gamma*u*u^T)*b = b - u*(gamma*U^T*b) for (int i = 0; i < rank; i++) { float[] u = qr[i]; float vv = u[i]; u[i] = 1; QrHelperFunctions_FDRM.rank1UpdateMultR(x_basic, u, gammas[i], 0, i, numRows, Y.data); u[i] = vv; } // solve for Rx = b using the standard upper triangular solver TriangularSolver_FDRM.solveU(R11.data, x_basic.data, rank); // finish the basic solution by filling in zeros x_basic.reshape(numCols, 1, true); for (int i = rank; i < numCols; i++) { x_basic.data[i] = 0; } if (norm2Solution && rank < numCols) { upgradeSolution(x_basic); } // save the results for (int i = 0; i < numCols; i++) { X.set(pivots[i], colB, x_basic.data[i]); } } }