/** * <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(DMatrixRMaj Q, DMatrixRMaj R, double[] 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(DMatrixRMaj B, DMatrixRMaj 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_DDRM.multTransA(Q, Y, x_basic); // solve for Rx = b using the standard upper triangular solver TriangularSolver_DDRM.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(DMatrixRMaj 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_DDRM.extract(R, 0, rank, 0, rank, R11, 0, 0); if (norm2Solution && rank < numCols) { // extract the R12 sub-matrix W.reshape(rank, numCols - rank); CommonOps_DDRM.extract(R, 0, rank, rank, numCols, W, 0, 0); // W=inv(R11)*R12 TriangularSolver_DDRM.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); }
/** * Creates a zeros matrix only if A does not already exist. If it does exist it will fill * the lower triangular portion with zeros. */ public static DMatrixRMaj checkZerosLT(DMatrixRMaj A, int numRows, int numCols) { if (A == null) { return(new DMatrixRMaj(numRows, numCols)); } else if (numRows != A.numRows || numCols != A.numCols) { A.reshape(numRows, numCols); A.zero(); } else { for (int i = 0; i < A.numRows; i++) { int index = i * A.numCols; int end = index + Math.Min(i, A.numCols); while (index < end) { A.data[index++] = 0; } } } return(A); }
/** * Must be called before any other functions. Declares and sets up internal data structures. * * @param numSamples Number of samples that will be processed. * @param sampleSize Number of elements in each sample. */ public void setup(int numSamples, int sampleSize) { mean = new double[sampleSize]; A.reshape(numSamples, sampleSize, false); sampleIndex = 0; numComponents = -1; }
/** * Computes the best fit set of polynomial coefficients to the provided observations. * * @param samplePoints where the observations were sampled. * @param observations A set of observations. */ public void fit(double[] samplePoints, double[] observations) { // Create a copy of the observations and put it into a matrix y.reshape(observations.Length, 1, false); Array.Copy(observations, 0, y.data, 0, observations.Length); // reshape the matrix to avoid unnecessarily declaring new memory // save values is set to false since its old values don't matter A.reshape(y.numRows, coef.numRows, false); // set up the A matrix for (int i = 0; i < observations.Length; i++) { double obs = 1; for (int j = 0; j < coef.numRows; j++) { A.set(i, j, obs); obs *= samplePoints[i]; } } // process the A matrix and see if it failed if (!solver.setA(A)) { throw new InvalidOperationException("Solver failed"); } // solver the the coefficients solver.solve(y, coef); }
/** * Converts {@link DMatrixRBlock} into {@link DMatrixRMaj} * * @param src Input matrix. * @param dst Output matrix. If null a new matrix will be declared. * @return Converted matrix. */ public static DMatrixRMaj convert(DMatrixRBlock src, DMatrixRMaj dst) { if (dst != null) { dst.reshape(src.NumRows, src.NumCols); } else { dst = new DMatrixRMaj(src.numRows, src.numCols); } for (int i = 0; i < src.numRows; i += src.blockLength) { int blockHeight = Math.Min(src.blockLength, src.numRows - i); for (int j = 0; j < src.numCols; j += src.blockLength) { int blockWidth = Math.Min(src.blockLength, src.numCols - j); int indexSrc = i * src.numCols + blockHeight * j; int indexDstRow = i * dst.numCols + j; for (int k = 0; k < blockHeight; k++) { System.Array.Copy(src.data, indexSrc, dst.data, indexDstRow, blockWidth); indexSrc += blockWidth; indexDstRow += dst.numCols; } } } return(dst); }
/** * Performs QR decomposition on A * * @param A not modified. */ public override bool setA(DMatrixRMaj 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); }
/** * Computes a basis (the principal components) from the most dominant eigenvectors. * * @param numComponents Number of vectors it will use to describe the data. Typically much * smaller than the number of elements in the input vector. */ public void computeBasis(int numComponents) { if (numComponents > A.getNumCols()) { throw new ArgumentException("More components requested that the data's length."); } if (sampleIndex != A.getNumRows()) { throw new ArgumentException("Not all the data has been added"); } if (numComponents > sampleIndex) { throw new ArgumentException("More data needed to compute the desired number of components"); } this.numComponents = numComponents; // compute the mean of all the samples for (int i = 0; i < A.getNumRows(); i++) { for (int j = 0; j < mean.Length; j++) { mean[j] += A.get(i, j); } } for (int j = 0; j < mean.Length; j++) { mean[j] /= A.getNumRows(); } // subtract the mean from the original data for (int i = 0; i < A.getNumRows(); i++) { for (int j = 0; j < mean.Length; j++) { A.set(i, j, A.get(i, j) - mean[j]); } } // Compute SVD and save time by not computing U SingularValueDecomposition <DMatrixRMaj> svd = DecompositionFactory_DDRM.svd(A.numRows, A.numCols, false, true, false); if (!svd.decompose(A)) { throw new InvalidOperationException("SVD failed"); } V_t = svd.getV(null, true); DMatrixRMaj W = svd.getW(null); // Singular values are in an arbitrary order initially SingularOps_DDRM.descendingOrder(null, false, W, V_t, true); // strip off unneeded components and find the basis V_t.reshape(numComponents, mean.Length, true); }
public static DMatrixRMaj ensureZeros(DMatrixRMaj A, int numRows, int numCols) { if (A == null) { return(new DMatrixRMaj(numRows, numCols)); } A.reshape(numRows, numCols); A.zero(); return(A); }
private void solveEigenvectorDuplicateEigenvalue(double real, int first, bool isTriangle) { double 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_F64 c = _implicit.eigenvalues[N - i - 1]; if (c.isReal() && Math.Abs(c.real - real) / scale < 100.0 * UtilEjml.EPS) { eigenvectorTemp.data[i] = 1; DMatrixRMaj v = new DMatrixRMaj(N, 1); CommonOps_DDRM.multTransA(Q, eigenvectorTemp, v); eigenvectors[N - i - 1] = v; NormOps_DDRM.normalizeF(v); eigenvectorTemp.data[i] = 0; } } }
public static DMatrixRMaj ensureIdentity(DMatrixRMaj A, int numRows, int numCols) { if (A == null) { return(CommonOps_DDRM.identity(numRows, numCols)); } A.reshape(numRows, numCols); CommonOps_DDRM.setIdentity(A); return(A); }
/** * Compute the A matrix from the Q and R matrices. * * @return The A matrix. */ public override DMatrixRMaj getA() { if (A.data.Length < numRows * numCols) { A = new DMatrixRMaj(numRows, numCols); } A.reshape(numRows, numCols, false); CommonOps_DDRM.mult(Q, R, A); return(A); }
public override void invert(DMatrixRMaj A_inv) { if (A_inv.numCols != numRows || A_inv.numRows != numCols) { throw new ArgumentException("Unexpected dimensions for A_inv"); } I.reshape(numRows, numRows); CommonOps_DDRM.setIdentity(I); solve(I, A_inv); }
public override bool setA(DMatrixRMaj 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++) { double 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++) { double 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; } } }
private void init(DMatrixRBlock 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 double[A.numCols]; } zerosM.reshape(A.blockLength, A.blockLength + 1, false); }
public override /**/ double quality() { return(SpecializedOps_DDRM.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(DMatrixRMaj X) { DMatrixRMaj 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_DDRM.multAdd(-1, W, z, X); }
protected void setToRequiredSize(DMatrixRMaj matrix) { int matrixRow = 0; int matrixCol = 0; List <Item> row = new List <Item>(); for (int i = 0; i < items.Count; i++) { Item item = items[i]; if (item.endRow) { Item v = row[0]; int numRows = v.getRows(); int numCols = v.getColumns(); for (int j = 1; j < row.Count; j++) { v = row[j]; if (v.getRows() != numRows) { throw new InvalidOperationException("Row miss-matched. " + numRows + " " + v.getRows()); } numCols += v.getColumns(); } matrixRow += numRows; if (matrixCol == 0) { matrixCol = numCols; } else if (matrixCol != numCols) { throw new ParseError("Row " + matrixRow + " has an unexpected number of columns; expected = " + matrixCol + " found = " + numCols); } row.Clear(); } else { row.Add(item); } } matrix.reshape(matrixRow, matrixCol); }
/** * Performs QR decomposition on A * * @param A not modified. */ public override bool setA(DMatrixRMaj 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_DDRM.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(DMatrixRMaj B, DMatrixRMaj 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_DDRM.multTransA(Q, Y, Z); // solve for Rx = b using the standard upper triangular solver TriangularSolver_DDRM.solveU(R.data, Z.data, numCols); // save the results for (int i = 0; i < numCols; i++) { X.set(i, colB, Z.data[i]); } } }
/** * If needed declares and sets up internal data structures. * * @param A Matrix being decomposed. */ public void init(DMatrixRMaj 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 double[N]; gammas = new double[N]; b = new double[N]; } } // just copy the top right triangle QT.set(A); }
private void solveWithLU(double real, int index, DMatrixRMaj r) { DMatrixRMaj A = new DMatrixRMaj(index, index); CommonOps_DDRM.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_DDRM.subvector(_implicit.A, 0, index, index, false, 0, r); CommonOps_DDRM.changeSign(r); // TODO this must be very inefficient if (!solver.setA(A)) { throw new InvalidOperationException("Solve failed"); } solver.solve(r, r); }
/** * <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(DMatrixRMaj Q, DMatrixRMaj 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.0; } } return(true); }
public override void solve(DMatrixRMaj B, DMatrixRMaj 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(); double[][] qr = decomposition.getQR(); double[] 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++) { double[] u = qr[i]; double vv = u[i]; u[i] = 1; QrHelperFunctions_DDRM.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_DDRM.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]); } } }
/** * Computes the QR decomposition of the provided matrix. * * @param A Matrix which is to be decomposed. Not modified. */ public void decompose(DMatrixRMaj A) { this.QR = (DMatrixRMaj)A.copy(); int N = Math.Min(A.numCols, A.numRows); gammas = new double[A.numCols]; DMatrixRMaj A_small = new DMatrixRMaj(A.numRows, A.numCols); DMatrixRMaj A_mod = new DMatrixRMaj(A.numRows, A.numCols); DMatrixRMaj v = new DMatrixRMaj(A.numRows, 1); DMatrixRMaj Q_k = new DMatrixRMaj(A.numRows, A.numRows); for (int i = 0; i < N; i++) { // reshape temporary variables A_small.reshape(QR.numRows - i, QR.numCols - i, false); A_mod.reshape(A_small.numRows, A_small.numCols, false); v.reshape(A_small.numRows, 1, false); Q_k.reshape(v.getNumElements(), v.getNumElements(), false); // use extract matrix to get the column that is to be zeroed CommonOps_DDRM.extract(QR, i, QR.numRows, i, i + 1, v, 0, 0); double max = CommonOps_DDRM.elementMaxAbs(v); if (max > 0 && v.getNumElements() > 1) { // normalize to reduce overflow issues CommonOps_DDRM.divide(v, max); // compute the magnitude of the vector double tau = NormOps_DDRM.normF(v); if (v.get(0) < 0) { tau *= -1.0; } double u_0 = v.get(0) + tau; double gamma = u_0 / tau; CommonOps_DDRM.divide(v, u_0); v.set(0, 1.0); // extract the submatrix of A which is being operated on CommonOps_DDRM.extract(QR, i, QR.numRows, i, QR.numCols, A_small, 0, 0); // A = (I - γ*u*u<sup>T</sup>)A CommonOps_DDRM.setIdentity(Q_k); CommonOps_DDRM.multAddTransB(-gamma, v, v, Q_k); CommonOps_DDRM.mult(Q_k, A_small, A_mod); // save the results CommonOps_DDRM.insert(A_mod, QR, i, i); CommonOps_DDRM.insert(v, QR, i, i); QR.unsafe_set(i, i, -tau * max); // save gamma for recomputing Q later on gammas[i] = gamma; } } }