/** * <p> * Applies the reflectors that have been computed previously to the specified row. * <br> * A = A + u*v^T + v*u^T only along the specified row in A. * </p> * * @param blockLength * @param A Contains the reflectors and the row being updated. * @param V Contains previously computed 'v' vectors. * @param row The row of 'A' that is to be updated. */ public static void applyReflectorsToRow(int blockLength, FSubmatrixD1 A, FSubmatrixD1 V, int row) { int height = Math.Min(blockLength, A.row1 - A.row0); float[] dataA = A.original.data; float[] dataV = V.original.data; int indexU, indexV; // for each previously computed reflector for (int i = 0; i < row; i++) { int width = Math.Min(blockLength, A.col1 - A.col0); indexU = A.original.numCols * A.row0 + height * A.col0 + i * width + row; indexV = V.original.numCols * V.row0 + height * V.col0 + i * width + row; float u_row = (i + 1 == row) ? 1.0f : dataA[indexU]; float v_row = dataV[indexV]; // take in account the leading one float before = A.get(i, i + 1); A.set(i, i + 1, 1); // grab only the relevant row from A = A + u*v^T + v*u^T VectorOps_FDRB.add_row(blockLength, A, row, 1, V, i, u_row, A, row, row, A.col1 - A.col0); VectorOps_FDRB.add_row(blockLength, A, row, 1, A, i, v_row, A, row, row, A.col1 - A.col0); A.set(i, i + 1, before); } }
/** * <p> * Computes the 'y' vector and stores the result in 'v'<br> * <br> * y = -γ(A + U*V^T + V*U^T)u * </p> * * @param blockLength * @param A Contains the reflectors and the row being updated. * @param V Contains previously computed 'v' vectors. * @param row The row of 'A' that is to be updated. */ public static void computeY(int blockLength, FSubmatrixD1 A, FSubmatrixD1 V, int row, float gamma) { // Elements in 'y' before 'row' are known to be zero and the element at 'row' // is not used. Thus only elements after row and after are computed. // y = A*u multA_u(blockLength, A, V, row); for (int i = 0; i < row; i++) { // y = y + u_i*v_i^t*u + v_i*u_i^t*u // v_i^t*u float dot_v_u = BlockHouseHolder_FDRB.innerProdRow(blockLength, A, row, V, i, 1); // u_i^t*u float dot_u_u = BlockHouseHolder_FDRB.innerProdRow(blockLength, A, row, A, i, 1); // y = y + u_i*(v_i^t*u) // the ones in these 'u' are skipped over since the next submatrix of A // is only updated VectorOps_FDRB.add_row(blockLength, V, row, 1, A, i, dot_v_u, V, row, row + 1, A.col1 - A.col0); // y = y + v_i*(u_i^t*u) // the 1 in U is taken account above VectorOps_FDRB.add_row(blockLength, V, row, 1, V, i, dot_u_u, V, row, row + 1, A.col1 - A.col0); } // y = -gamma*y VectorOps_FDRB.scale_row(blockLength, V, row, -gamma, V, row, row + 1, V.col1 - V.col0); }
/** * <p> * Given an already computed tridiagonal decomposition, compute the V row block vector.<br> * <br> * y(:) = A*u<br> * v(i) = y - (1/2)*γ*(y^T*u)*u * </p> */ public static void computeV_blockVector(int blockLength, FSubmatrixD1 A, float[] gammas, FSubmatrixD1 V) { int blockHeight = Math.Min(blockLength, A.row1 - A.row0); if (blockHeight <= 1) { return; } int width = A.col1 - A.col0; int num = Math.Min(width - 1, blockHeight); for (int i = 0; i < num; i++) { float gamma = gammas[A.row0 + i]; // compute y computeY(blockLength, A, V, i, gamma); // compute v from y computeRowOfV(blockLength, A, V, i, gamma); } }
/** * <p> * Performs a matrix multiplication on {@link FMatrixRBlock} submatrices.<br> * <br> * c = c + a * b <br> * <br> * </p> * * <p> * It is assumed that all submatrices start at the beginning of a block and end at the end of a block. * </p> * * @param blockLength Size of the blocks in the submatrix. * @param A A submatrix. Not modified. * @param B A submatrix. Not modified. * @param C Result of the operation. Modified, */ public static void multPlus(int blockLength, FSubmatrixD1 A, FSubmatrixD1 B, FSubmatrixD1 C) { // checkInput( blockLength,A,B,C); for (int i = A.row0; i < A.row1; i += blockLength) { int heightA = Math.Min(blockLength, A.row1 - i); for (int j = B.col0; j < B.col1; j += blockLength) { int widthB = Math.Min(blockLength, B.col1 - j); int indexC = (i - A.row0 + C.row0) * C.original.numCols + (j - B.col0 + C.col0) * heightA; for (int k = A.col0; k < A.col1; k += blockLength) { int widthA = Math.Min(blockLength, A.col1 - k); int indexA = i * A.original.numCols + k * heightA; int indexB = (k - A.col0 + B.row0) * B.original.numCols + j * widthA; InnerMultiplication_FDRB.blockMultPlus(A.original.data, B.original.data, C.original.data, indexA, indexB, indexC, heightA, widthA, widthB); } } } }
public static bool lower(FSubmatrixD1 T) { int n = T.row1 - T.row0; int indexT = T.row0 * T.original.numCols + T.col0 * n; return(lower(T.original.data, indexT, n)); }
/** * <p> * Computes W from the householder reflectors stored in the columns of the row block * submatrix Y. * </p> * * <p> * Y = v<sup>(1)</sup><br> * W = -β<sub>1</sub>v<sup>(1)</sup><br> * for j=2:r<br> * z = -β(I +WY<sup>T</sup>)v<sup>(j)</sup> <br> * W = [W z]<br> * Y = [Y v<sup>(j)</sup>]<br> * end<br> * <br> * where v<sup>(.)</sup> are the house holder vectors, and r is the block length. Note that * Y already contains the householder vectors so it does not need to be modified. * </p> * * <p> * Y and W are assumed to have the same number of rows and columns. * </p> */ public static void computeW_row(int blockLength, FSubmatrixD1 Y, FSubmatrixD1 W, float[] beta, int betaIndex) { int heightY = Y.row1 - Y.row0; CommonOps_FDRM.fill(W.original, 0); // W = -beta*v(1) BlockHouseHolder_FDRB.scale_row(blockLength, Y, W, 0, 1, -beta[betaIndex++]); int min = Math.Min(heightY, W.col1 - W.col0); // set up rest of the rows for (int i = 1; i < min; i++) { // w=-beta*(I + W*Y^T)*u float b = -beta[betaIndex++]; // w = w -beta*W*(Y^T*u) for (int j = 0; j < i; j++) { float yv = BlockHouseHolder_FDRB.innerProdRow(blockLength, Y, i, Y, j, 1); VectorOps_FDRB.add_row(blockLength, W, i, 1, W, j, b * yv, W, i, 1, Y.col1 - Y.col0); } //w=w -beta*u + stuff above BlockHouseHolder_FDRB.add_row(blockLength, Y, i, b, W, i, 1, W, i, 1, Y.col1 - Y.col0); } }
public static void multTransB(FMatrixRBlock A, FMatrixRBlock B, FMatrixRBlock C) { if (A.numCols != B.numCols) { throw new ArgumentException("Columns in A are incompatible with columns in B"); } if (A.numRows != C.numRows) { throw new ArgumentException("Rows in A are incompatible with rows in C"); } if (B.numRows != C.numCols) { throw new ArgumentException("Rows in B are incompatible with columns in C"); } if (A.blockLength != B.blockLength || A.blockLength != C.blockLength) { throw new ArgumentException("Block lengths are not all the same."); } int blockLength = A.blockLength; FSubmatrixD1 Asub = new FSubmatrixD1(A, 0, A.numRows, 0, A.numCols); FSubmatrixD1 Bsub = new FSubmatrixD1(B, 0, B.numRows, 0, B.numCols); FSubmatrixD1 Csub = new FSubmatrixD1(C, 0, C.numRows, 0, C.numCols); MatrixMult_FDRB.multTransB(blockLength, Asub, Bsub, Csub); }
/** * <p> * Performs a tridiagonal decomposition on the upper row only. * </p> * * <p> * For each row 'a' in 'A': * Compute 'u' the householder reflector. * y(:) = A*u * v(i) = y - (1/2)*(y^T*u)*u * a(i+1) = a(i) - u*γ*v^T - v*u^t * </p> * * @param blockLength Size of a block * @param A is the row block being decomposed. Modified. * @param gammas Householder gammas. * @param V Where computed 'v' are stored in a row block. Modified. */ public static void tridiagUpperRow(int blockLength, FSubmatrixD1 A, float[] gammas, FSubmatrixD1 V) { int blockHeight = Math.Min(blockLength, A.row1 - A.row0); if (blockHeight <= 1) { return; } int width = A.col1 - A.col0; int num = Math.Min(width - 1, blockHeight); int applyIndex = Math.Min(width, blockHeight); // step through rows in the block for (int i = 0; i < num; i++) { // compute the new reflector and save it in a row in 'A' BlockHouseHolder_FDRB.computeHouseHolderRow(blockLength, A, gammas, i); float gamma = gammas[A.row0 + i]; // compute y computeY(blockLength, A, V, i, gamma); // compute v from y computeRowOfV(blockLength, A, V, i, gamma); // Apply the reflectors to the next row in 'A' only if (i + 1 < applyIndex) { applyReflectorsToRow(blockLength, A, V, i + 1); } } }
public virtual void invert(FMatrixRBlock A_inv) { FMatrixRBlock T = decomposer.getT(null); if (A_inv.numRows != T.numRows || A_inv.numCols != T.numCols) { throw new ArgumentException("Unexpected number or rows and/or columns"); } if (temp == null || temp.Length < blockLength * blockLength) { temp = new float[blockLength * blockLength]; } // zero the upper triangular portion of A_inv MatrixOps_FDRB.zeroTriangle(true, A_inv); FSubmatrixD1 L = new FSubmatrixD1(T); FSubmatrixD1 B = new FSubmatrixD1(A_inv); // invert L from cholesky decomposition and write the solution into the lower // triangular portion of A_inv // B = inv(L) TriangularSolver_FDRB.invert(blockLength, false, L, B, temp); // B = L^-T * B // todo could speed up by taking advantage of B being lower triangular // todo take advantage of symmetry TriangularSolver_FDRB.solveL(blockLength, L, B, true); }
/** * <p> * Computes the householder vector from the specified row * </p> * * <p> * The householder vector 'u' is computed as follows:<br> * <br> * u(1) = 1 <br> * u(i) = x(i)/(τ + x(1))<br> * </p> * * The first element is implicitly assumed to be one and not written. * * @return If there was any problems or not. true = no problem. */ public static bool computeHouseHolderRow(int blockLength, FSubmatrixD1 Y, float[] gamma, int i) { float max = BlockHouseHolder_FDRB.findMaxRow(blockLength, Y, i, i + 1); if (max == 0.0f) { return(false); } else { // computes tau and normalizes u by max float tau = computeTauAndDivideRow(blockLength, Y, i, i + 1, max); // divide u by u_0 float u_0 = Y.get(i, i + 1) + tau; VectorOps_FDRB.div_row(blockLength, Y, i, u_0, Y, i, i + 1, Y.col1 - Y.col0); gamma[Y.row0 + i] = u_0 / tau; // after the reflector is applied the column would be all zeros but be -tau in the first element Y.set(i, i + 1, -tau * max); } return(true); }
/** * Divides the elements at the specified column by 'val'. Takes in account * leading zeros and one. */ public static void divideElementsCol(int blockLength, FSubmatrixD1 Y, int col, float val) { int width = Math.Min(blockLength, Y.col1 - Y.col0); float[] dataY = Y.original.data; for (int i = Y.row0; i < Y.row1; i += blockLength) { int height = Math.Min(blockLength, Y.row1 - i); int index = i * Y.original.numCols + height * Y.col0 + col; if (i == Y.row0) { index += width * (col + 1); for (int k = col + 1; k < height; k++, index += width) { dataY[index] /= val; } } else { int endIndex = index + width * height; //for( int k = 0; k < height; k++ for (; index != endIndex; index += width) { dataY[index] /= val; } } } }
/** * <p> * Sets W to its initial value using the first column of 'y' and the value of 'b': * <br> * W = -βv<br> * <br> * where v = Y(:,0). * </p> * * @param blockLength size of the inner block * @param W Submatrix being initialized. * @param Y Contains householder vector * @param widthB How wide the W block matrix is. * @param b beta */ public static void initializeW(int blockLength, FSubmatrixD1 W, FSubmatrixD1 Y, int widthB, float b) { float[] dataW = W.original.data; float[] dataY = Y.original.data; for (int i = W.row0; i < W.row1; i += blockLength) { int heightW = Math.Min(blockLength, W.row1 - i); int indexW = i * W.original.numCols + heightW * W.col0; int indexY = i * Y.original.numCols + heightW * Y.col0; // take in account the first element in V being 1 if (i == W.row0) { dataW[indexW] = -b; indexW += widthB; indexY += widthB; for (int k = 1; k < heightW; k++, indexW += widthB, indexY += widthB) { dataW[indexW] = -b * dataY[indexY]; } } else { for (int k = 0; k < heightW; k++, indexW += widthB, indexY += widthB) { dataW[indexW] = -b * dataY[indexY]; } } } }
public static float innerProdRowSymm(int blockLength, FSubmatrixD1 A, int rowA, FSubmatrixD1 B, int rowB, int zeroOffset) { int offset = rowA + zeroOffset; if (offset + B.col0 >= B.col1) { return(0); } if (offset < rowB) { // take in account the one in 'A' float total = B.get(offset, rowB); total += VectorOps_FDRB.dot_row_col(blockLength, A, rowA, B, rowB, offset + 1, rowB); total += VectorOps_FDRB.dot_row(blockLength, A, rowA, B, rowB, rowB, A.col1 - A.col0); return(total); } else { // take in account the one in 'A' float total = B.get(rowB, offset); total += VectorOps_FDRB.dot_row(blockLength, A, rowA, B, rowB, offset + 1, A.col1 - A.col0); return(total); } }
/** * <p> * Performs a matrix multiplication with a transpose on {@link FMatrixRBlock} submatrices.<br> * <br> * c = a * b <sup>T</sup> <br> * <br> * </p> * * <p> * It is assumed that all submatrices start at the beginning of a block and end at the end of a block. * </p> * * @param blockLength Length of the blocks in the submatrix. * @param A A submatrix. Not modified. * @param B A submatrix. Not modified. * @param C Result of the operation. Modified, */ public static void multTransB(int blockLength, FSubmatrixD1 A, FSubmatrixD1 B, FSubmatrixD1 C) { for (int i = A.row0; i < A.row1; i += blockLength) { int heightA = Math.Min(blockLength, A.row1 - i); for (int j = B.row0; j < B.row1; j += blockLength) { int widthC = Math.Min(blockLength, B.row1 - j); int indexC = (i - A.row0 + C.row0) * C.original.numCols + (j - B.row0 + C.col0) * heightA; for (int k = A.col0; k < A.col1; k += blockLength) { int widthA = Math.Min(blockLength, A.col1 - k); int indexA = i * A.original.numCols + k * heightA; int indexB = j * B.original.numCols + (k - A.col0 + B.col0) * widthC; if (k == A.col0) { InnerMultiplication_FDRB.blockMultSetTransB(A.original.data, B.original.data, C.original.data, indexA, indexB, indexC, heightA, widthA, widthC); } else { InnerMultiplication_FDRB.blockMultPlusTransB(A.original.data, B.original.data, C.original.data, indexA, indexB, indexC, heightA, widthA, widthC); } } } } }
/** * Special multiplication that takes in account the zeros and one in Y, which * is the matrix that stores the householder vectors. * */ public static void multAdd_zeros(int blockLength, FSubmatrixD1 Y, FSubmatrixD1 B, FSubmatrixD1 C) { int widthY = Y.col1 - Y.col0; for (int i = Y.row0; i < Y.row1; i += blockLength) { int heightY = Math.Min(blockLength, Y.row1 - i); for (int j = B.col0; j < B.col1; j += blockLength) { int widthB = Math.Min(blockLength, B.col1 - j); int indexC = (i - Y.row0 + C.row0) * C.original.numCols + (j - B.col0 + C.col0) * heightY; for (int k = Y.col0; k < Y.col1; k += blockLength) { int indexY = i * Y.original.numCols + k * heightY; int indexB = (k - Y.col0 + B.row0) * B.original.numCols + j * widthY; if (i == Y.row0) { multBlockAdd_zerosone(Y.original.data, B.original.data, C.original.data, indexY, indexB, indexC, heightY, widthY, widthB); } else { InnerMultiplication_FDRB.blockMultPlus(Y.original.data, B.original.data, C.original.data, indexY, indexB, indexC, heightY, widthY, widthB); } } } } }
/** * <p> * Computes the householder vector that is used to create reflector for the column. * The results are stored in the original matrix. * </p> * * <p> * The householder vector 'u' is computed as follows:<br> * <br> * u(1) = 1 <br> * u(i) = x(i)/(τ + x(1))<br> * </p> * * The first element is implicitly assumed to be one and not written. * * @return If there was any problems or not. true = no problem. */ public static bool computeHouseHolderCol(int blockLength, FSubmatrixD1 Y, float[] gamma, int i) { float max = BlockHouseHolder_FDRB.findMaxCol(blockLength, Y, i); if (max == 0.0f) { return(false); } else { // computes tau and normalizes u by max float tau = computeTauAndDivideCol(blockLength, Y, i, max); // divide u by u_0 float u_0 = Y.get(i, i) + tau; divideElementsCol(blockLength, Y, i, u_0); gamma[Y.col0 + i] = u_0 / tau; tau *= max; // after the reflector is applied the column would be all zeros but be -tau in the first element Y.set(i, i, -tau); } return(true); }
/** * <p> * A = (I + W Y<sup>T</sup>)<sup>T</sup>A<BR> * A = A + Y (W<sup>T</sup>A)<BR> * <br> * where A is a submatrix of the input matrix. * </p> */ protected void updateA(FSubmatrixD1 A) { setW(); A.row0 = Y.row0; A.row1 = Y.row1; A.col0 = Y.col1; A.col1 = Y.original.numCols; WTA.row0 = 0; WTA.col0 = 0; WTA.row1 = W.col1 - W.col0; WTA.col1 = A.col1 - A.col0; WTA.original.reshape(WTA.row1, WTA.col1, false); if (A.col1 > A.col0) { BlockHouseHolder_FDRB.computeW_Column(blockLength, Y, W, temp, gammas, Y.col0); MatrixMult_FDRB.multTransA(blockLength, W, A, WTA); BlockHouseHolder_FDRB.multAdd_zeros(blockLength, Y, WTA, A); } else if (saveW) { BlockHouseHolder_FDRB.computeW_Column(blockLength, Y, W, temp, gammas, Y.col0); } }
/** * Computes Y<sup>T</sup>v<sup>(j)</sup>. Where Y are the columns before 'col' and v is the column * at 'col'. The zeros and ones are taken in account. The solution is a vector with 'col' elements. * * width of Y must be along the block of original matrix A * * @param temp Temporary storage of least length 'col' */ public static void computeY_t_V(int blockLength, FSubmatrixD1 Y, int col, float[] temp) { int widthB = Y.col1 - Y.col0; for (int j = 0; j < col; j++) { temp[j] = innerProdCol(blockLength, Y, col, widthB, j, widthB); } }
/** * Performs a standard bidiagonal decomposition just on the outer blocks of the provided matrix * * @param blockLength * @param A * @param gammasU */ public static bool bidiagOuterBlocks(int blockLength, FSubmatrixD1 A, float[] gammasU, float[] gammasV) { // Console.WriteLine("---------- Orig"); // A.original.print(); int width = Math.Min(blockLength, A.col1 - A.col0); int height = Math.Min(blockLength, A.row1 - A.row0); int min = Math.Min(width, height); for (int i = 0; i < min; i++) { //--- Apply reflector to the column // compute the householder vector if (!BlockHouseHolder_FDRB.computeHouseHolderCol(blockLength, A, gammasU, i)) { return(false); } // apply to rest of the columns in the column block BlockHouseHolder_FDRB.rank1UpdateMultR_Col(blockLength, A, i, gammasU[A.col0 + i]); // apply to the top row block BlockHouseHolder_FDRB.rank1UpdateMultR_TopRow(blockLength, A, i, gammasU[A.col0 + i]); Console.WriteLine("After column stuff"); A.original.print(); //-- Apply reflector to the row if (!BlockHouseHolder_FDRB.computeHouseHolderRow(blockLength, A, gammasV, i)) { return(false); } // apply to rest of the rows in the row block BlockHouseHolder_FDRB.rank1UpdateMultL_Row(blockLength, A, i, i + 1, gammasV[A.row0 + i]); Console.WriteLine("After update row"); A.original.print(); // apply to the left column block // TODO THIS WON'T WORK!!!!!!!!!!!!! // Needs the whole matrix to have been updated by the left reflector to compute the correct solution // rank1UpdateMultL_LeftCol(blockLength,A,i,i+1,gammasV[A.row0+i]); Console.WriteLine("After row stuff"); A.original.print(); } return(true); }
/** * <p> * From the specified row of Y tau is computed and each element is divided by 'max'. * See code below: * </p> * * <pre> * for j=row:Y.numCols * Y[row][j] = u[row][j] / max * tau = tau + u[row][j]*u[row][j] * end * tau = sqrt(tau) * if( Y[row][row] < 0 ) * tau = -tau; * </pre> * * @param row Which row in the block will be processed * @param colStart The first column that computation of tau will start at * @param max used to normalize and prevent buffer over flow * */ public static float computeTauAndDivideRow(int blockLength, FSubmatrixD1 Y, int row, int colStart, float max) { int height = Math.Min(blockLength, Y.row1 - Y.row0); float[] dataY = Y.original.data; float top = 0; float norm2 = 0; int startJ = Y.col0 + colStart - colStart % blockLength; colStart = colStart % blockLength; for (int j = startJ; j < Y.col1; j += blockLength) { int width = Math.Min(blockLength, Y.col1 - j); int index = Y.row0 * Y.original.numCols + height * j + row * width; if (j == startJ) { index += colStart; // save this value so that the sign can be determined later on top = dataY[index] /= max; norm2 += top * top; index++; for (int k = colStart + 1; k < width; k++) { float val = dataY[index++] /= max; norm2 += val * val; } } else { for (int k = 0; k < width; k++) { float val = dataY[index++] /= max; norm2 += val * val; } } } norm2 = (float)Math.Sqrt(norm2); if (top < 0) { norm2 = -norm2; } return(norm2); }
/** * <p> * Computes the inner product of column vector 'colA' against column vector 'colB' while taking account leading zeros and one.<br> * <br> * ret = a<sup>T</sup>*b * </p> * * <p> * Column A is assumed to be a householder vector. Element at 'colA' is one and previous ones are zero. * </p> * * @param blockLength * @param A block aligned submatrix. * @param colA Column inside the block of first column vector. * @param widthA how wide the column block that colA is inside of. * @param colB Column inside the block of second column vector. * @param widthB how wide the column block that colB is inside of. * @return dot product of the two vectors. */ public static float innerProdCol(int blockLength, FSubmatrixD1 A, int colA, int widthA, int colB, int widthB) { float total = 0; float[] data = A.original.data; // first column in the blocks int colBlockA = A.col0 + colA - colA % blockLength; int colBlockB = A.col0 + colB - colB % blockLength; colA = colA % blockLength; colB = colB % blockLength; // compute dot product down column vectors for (int i = A.row0; i < A.row1; i += blockLength) { int height = Math.Min(blockLength, A.row1 - i); int indexA = i * A.original.numCols + height * colBlockA + colA; int indexB = i * A.original.numCols + height * colBlockB + colB; if (i == A.row0) { // handle leading zeros indexA += widthA * (colA + 1); indexB += widthB * colA; // handle leading one total = data[indexB]; indexB += widthB; // standard vector dot product int endA = indexA + (height - colA - 1) * widthA; for (; indexA != endA; indexA += widthA, indexB += widthB) { // for( int k = col+1; k < height; k++ , indexU += width, indexA += width ) { total += data[indexA] * data[indexB]; } } else { // standard vector dot product int endA = indexA + widthA * height; // for( int k = 0; k < height; k++ ) { for (; indexA != endA; indexA += widthA, indexB += widthB) { total += data[indexA] * data[indexB]; } } } return(total); }
/** * Inverts an upper or lower triangular block submatrix. * * @param blockLength * @param upper Is it upper or lower triangular. * @param T Triangular matrix that is to be inverted. Overwritten with solution. Modified. * @param temp Work space variable that is size blockLength*blockLength. */ public static void invert(int blockLength, bool upper, FSubmatrixD1 T, float[] temp) { if (upper) { throw new ArgumentException("Upper triangular matrices not supported yet"); } if (temp.Length < blockLength * blockLength) { throw new ArgumentException("Temp must be at least blockLength*blockLength long."); } int M = T.row1 - T.row0; float[] dataT = T.original.data; int offsetT = T.row0 * T.original.numCols + M * T.col0; for (int i = 0; i < M; i += blockLength) { int heightT = Math.Min(T.row1 - (i + T.row0), blockLength); int indexII = offsetT + T.original.numCols * (i + T.row0) + heightT * (i + T.col0); for (int j = 0; j < i; j += blockLength) { int widthX = Math.Min(T.col1 - (j + T.col0), blockLength); for (int w = 0; w < temp.Length; w++) { temp[w] = 0; } for (int k = j; k < i; k += blockLength) { int widthT = Math.Min(T.col1 - (k + T.col0), blockLength); int indL = offsetT + T.original.numCols * (i + T.row0) + heightT * (k + T.col0); int indX = offsetT + T.original.numCols * (k + T.row0) + widthT * (j + T.col0); InnerMultiplication_FDRB.blockMultMinus(dataT, dataT, temp, indL, indX, 0, heightT, widthT, widthX); } int indexX = offsetT + T.original.numCols * (i + T.row0) + heightT * (j + T.col0); InnerTriangularSolver_FDRB.solveL(dataT, temp, heightT, widthX, heightT, indexII, 0); Array.Copy(temp, 0, dataT, indexX, widthX * heightT); } InnerTriangularSolver_FDRB.invertLower(dataT, heightT, indexII); } }
/** * <p> * Row vector scale:<br> * scale: b<sub>i</sub> = α*a<sub>i</sub><br> * where 'a' and 'b' are row vectors within the row block vector A and B. * </p> * * @param A submatrix. Not modified. * @param rowA which row in A the vector is contained in. * @param alpha scale factor. * @param B submatrix that the results are written to. Modified. * @param offset Index at which the vectors start at. * @param end Index at which the vectors end at. */ public static void scale_row(int blockLength, FSubmatrixD1 A, int rowA, float alpha, FSubmatrixD1 B, int rowB, int offset, int end) { float[] dataA = A.original.data; float[] dataB = B.original.data; // handle the case where offset is more than a block int startI = offset - offset % blockLength; offset = offset % blockLength; // handle rows in any block int rowBlockA = A.row0 + rowA - rowA % blockLength; rowA = rowA % blockLength; int rowBlockB = B.row0 + rowB - rowB % blockLength; rowB = rowB % blockLength; int heightA = Math.Min(blockLength, A.row1 - rowBlockA); int heightB = Math.Min(blockLength, B.row1 - rowBlockB); for (int i = startI; i < end; i += blockLength) { int segment = Math.Min(blockLength, end - i); int widthA = Math.Min(blockLength, A.col1 - A.col0 - i); int widthB = Math.Min(blockLength, B.col1 - B.col0 - i); int indexA = rowBlockA * A.original.numCols + (A.col0 + i) * heightA + rowA * widthA; int indexB = rowBlockB * B.original.numCols + (B.col0 + i) * heightB + rowB * widthB; if (i == startI) { indexA += offset; indexB += offset; for (int j = offset; j < segment; j++) { dataB[indexB++] = alpha * dataA[indexA++]; } } else { for (int j = 0; j < segment; j++) { dataB[indexB++] = alpha * dataA[indexA++]; } } } }
/** * <p> * Row vector add:<br> * add: c<sub>i</sub> = α*a<sub>i</sub> + βB<sub>i</sub><br> * where 'a', 'b', and 'c' are row vectors within the row block vectors of A, B, and C respectively. * </p> * * @param blockLength Length of each inner matrix block. * @param A submatrix. Not modified. * @param rowA which row in A the vector is contained in. * @param alpha scale factor of A * @param B submatrix. Not modified. * @param rowB which row in B the vector is contained in. * @param beta scale factor of B * @param C submatrix where the results are written to. Modified. * @param rowC which row in C is the vector contained. * @param offset Index at which the vectors start at. * @param end Index at which the vectors end at. */ public static void add_row(int blockLength, FSubmatrixD1 A, int rowA, float alpha, FSubmatrixD1 B, int rowB, float beta, FSubmatrixD1 C, int rowC, int offset, int end) { int heightA = Math.Min(blockLength, A.row1 - A.row0); int heightB = Math.Min(blockLength, B.row1 - B.row0); int heightC = Math.Min(blockLength, C.row1 - C.row0); // handle the case where offset is more than a block int startI = offset - offset % blockLength; offset = offset % blockLength; float[] dataA = A.original.data; float[] dataB = B.original.data; float[] dataC = C.original.data; for (int i = startI; i < end; i += blockLength) { int segment = Math.Min(blockLength, end - i); int widthA = Math.Min(blockLength, A.col1 - A.col0 - i); int widthB = Math.Min(blockLength, B.col1 - B.col0 - i); int widthC = Math.Min(blockLength, C.col1 - C.col0 - i); int indexA = A.row0 * A.original.numCols + (A.col0 + i) * heightA + rowA * widthA; int indexB = B.row0 * B.original.numCols + (B.col0 + i) * heightB + rowB * widthB; int indexC = C.row0 * C.original.numCols + (C.col0 + i) * heightC + rowC * widthC; if (i == startI) { indexA += offset; indexB += offset; indexC += offset; for (int j = offset; j < segment; j++) { dataC[indexC++] = alpha * dataA[indexA++] + beta * dataB[indexB++]; } } else { for (int j = 0; j < segment; j++) { dataC[indexC++] = alpha * dataA[indexA++] + beta * dataB[indexB++]; } } } }
/** * <p> * Rank N update function for a symmetric inner submatrix and only operates on the upper * triangular portion of the submatrix.<br> * <br> * A = A - B <sup>T</sup>B * </p> */ public static void symmRankNMinus_U(int blockLength, FSubmatrixD1 A, FSubmatrixD1 B) { int heightB = B.row1 - B.row0; if (heightB > blockLength) { throw new ArgumentException("Height of B cannot be greater than the block length"); } int N = B.col1 - B.col0; if (A.col1 - A.col0 != N) { throw new ArgumentException("A does not have the expected number of columns based on B's width"); } if (A.row1 - A.row0 != N) { throw new ArgumentException("A does not have the expected number of rows based on B's width"); } for (int i = B.col0; i < B.col1; i += blockLength) { int indexB_i = B.row0 * B.original.numCols + i * heightB; int widthB_i = Math.Min(blockLength, B.col1 - i); int rowA = i - B.col0 + A.row0; int heightA = Math.Min(blockLength, A.row1 - rowA); for (int j = i; j < B.col1; j += blockLength) { int widthB_j = Math.Min(blockLength, B.col1 - j); int indexA = rowA * A.original.numCols + (j - B.col0 + A.col0) * heightA; int indexB_j = B.row0 * B.original.numCols + j * heightB; if (i == j) { // only the upper portion of this block needs to be modified since it is along a diagonal multTransABlockMinus_U(B.original.data, A.original.data, indexB_i, indexB_j, indexA, heightB, widthB_i, widthB_j); } else { multTransABlockMinus(B.original.data, A.original.data, indexB_i, indexB_j, indexA, heightB, widthB_i, widthB_j); } } } }
/** * <p> * Multiples the appropriate submatrix of A by the specified reflector and stores * the result ('y') in V.<br> * <br> * y = A*u<br> * </p> * * @param blockLength * @param A Contains the 'A' matrix and 'u' vector. * @param V Where resulting 'y' row vectors are stored. * @param row row in matrix 'A' that 'u' vector and the row in 'V' that 'y' is stored in. */ public static void multA_u(int blockLength, FSubmatrixD1 A, FSubmatrixD1 V, int row) { int heightMatA = A.row1 - A.row0; for (int i = row + 1; i < heightMatA; i++) { float val = innerProdRowSymm(blockLength, A, row, A, i, 1); V.set(row, i, val); } }
public virtual bool decompose(FMatrixRBlock orig) { if (orig.numCols != orig.numRows) { throw new ArgumentException("Input matrix must be square."); } init(orig); FSubmatrixD1 subA = new FSubmatrixD1(A); FSubmatrixD1 subV = new FSubmatrixD1(V); FSubmatrixD1 subU = new FSubmatrixD1(A); int N = orig.numCols; for (int i = 0; i < N; i += A.blockLength) { // Console.WriteLine("-------- triag i "+i); int height = Math.Min(A.blockLength, A.numRows - i); subA.col0 = subU.col0 = i; subA.row0 = subU.row0 = i; subU.row1 = subU.row0 + height; subV.col0 = i; subV.row1 = height; subV.original.reshape(subV.row1, subV.col1, false); // bidiagonalize the top row TridiagonalHelper_FDRB.tridiagUpperRow(A.blockLength, subA, gammas, subV); // apply Householder reflectors to the lower portion using block multiplication if (subU.row1 < orig.numCols) { // take in account the 1 in the last row. The others are skipped over. float before = subU.get(A.blockLength - 1, A.blockLength); subU.set(A.blockLength - 1, A.blockLength, 1); // A = A + U*V^T + V*U^T multPlusTransA(A.blockLength, subU, subV, subA); multPlusTransA(A.blockLength, subV, subU, subA); subU.set(A.blockLength - 1, A.blockLength, before); } } return(true); }
/** * <p> * From the specified column of Y tau is computed and each element is divided by 'max'. * See code below: * </p> * * <pre> * for i=col:Y.numRows * Y[i][col] = u[i][col] / max * tau = tau + u[i][col]*u[i][col] * end * tau = sqrt(tau) * if( Y[col][col] < 0 ) * tau = -tau; * </pre> * */ public static float computeTauAndDivideCol(int blockLength, FSubmatrixD1 Y, int col, float max) { int width = Math.Min(blockLength, Y.col1 - Y.col0); float[] dataY = Y.original.data; float top = 0; float norm2 = 0; for (int i = Y.row0; i < Y.row1; i += blockLength) { int height = Math.Min(blockLength, Y.row1 - i); int index = i * Y.original.numCols + height * Y.col0 + col; if (i == Y.row0) { index += width * col; // save this value so that the sign can be determined later on top = dataY[index] /= max; norm2 += top * top; index += width; for (int k = col + 1; k < height; k++, index += width) { float val = dataY[index] /= max; norm2 += val * val; } } else { for (int k = 0; k < height; k++, index += width) { float val = dataY[index] /= max; norm2 += val * val; } } } norm2 = (float)Math.Sqrt(norm2); if (top < 0) { norm2 = -norm2; } return(norm2); }
/** * <p> * Rank N update function for a symmetric inner submatrix and only operates on the lower * triangular portion of the submatrix.<br> * <br> * A = A - B*B<sup>T</sup><br> * </p> */ public static void symmRankNMinus_L(int blockLength, FSubmatrixD1 A, FSubmatrixD1 B) { int widthB = B.col1 - B.col0; if (widthB > blockLength) { throw new ArgumentException("Width of B cannot be greater than the block length"); } int N = B.row1 - B.row0; if (A.col1 - A.col0 != N) { throw new ArgumentException("A does not have the expected number of columns based on B's height"); } if (A.row1 - A.row0 != N) { throw new ArgumentException("A does not have the expected number of rows based on B's height"); } for (int i = B.row0; i < B.row1; i += blockLength) { int heightB_i = Math.Min(blockLength, B.row1 - i); int indexB_i = i * B.original.numCols + heightB_i * B.col0; int rowA = i - B.row0 + A.row0; int heightA = Math.Min(blockLength, A.row1 - rowA); for (int j = B.row0; j <= i; j += blockLength) { int widthB_j = Math.Min(blockLength, B.row1 - j); int indexA = rowA * A.original.numCols + (j - B.row0 + A.col0) * heightA; int indexB_j = j * B.original.numCols + widthB_j * B.col0; if (i == j) { multTransBBlockMinus_L(B.original.data, A.original.data, indexB_i, indexB_j, indexA, widthB, heightB_i, widthB_j); } else { multTransBBlockMinus(B.original.data, A.original.data, indexB_i, indexB_j, indexA, widthB, heightB_i, widthB_j); } } } }
/** * Specialized version of applyQ() that allows the zeros in an identity matrix * to be taken advantage of depending on if isIdentity is true or not. * * @param B * @param isIdentity If B is an identity matrix. */ public void applyQ(FMatrixRBlock B, bool isIdentity) { int minDimen = Math.Min(dataA.numCols, dataA.numRows); FSubmatrixD1 subB = new FSubmatrixD1(B); W.col0 = W.row0 = 0; Y.row1 = W.row1 = dataA.numRows; WTA.row0 = WTA.col0 = 0; int start = minDimen - minDimen % blockLength; if (start == minDimen) { start -= blockLength; } if (start < 0) { start = 0; } // (Q1^T * (Q2^T * (Q3^t * A))) for (int i = start; i >= 0; i -= blockLength) { Y.col0 = i; Y.col1 = Math.Min(Y.col0 + blockLength, dataA.numCols); Y.row0 = i; if (isIdentity) { subB.col0 = i; } subB.row0 = i; setW(); WTA.row1 = Y.col1 - Y.col0; WTA.col1 = subB.col1 - subB.col0; WTA.original.reshape(WTA.row1, WTA.col1, false); // Compute W matrix from reflectors stored in Y if (!saveW) { BlockHouseHolder_FDRB.computeW_Column(blockLength, Y, W, temp, gammas, Y.col0); } // Apply the Qi to Q BlockHouseHolder_FDRB.multTransA_vecCol(blockLength, Y, subB, WTA); MatrixMult_FDRB.multPlus(blockLength, W, WTA, subB); } }