private static void swapRowOrCol(FMatrixRMaj M, bool tran, int i, int bigIndex) { float tmp; if (tran) { // swap the rows for (int col = 0; col < M.numCols; col++) { tmp = M.get(i, col); M.set(i, col, M.get(bigIndex, col)); M.set(bigIndex, col, tmp); } } else { // swap the columns for (int row = 0; row < M.numRows; row++) { tmp = M.get(row, i); M.set(row, i, M.get(row, bigIndex)); M.set(row, bigIndex, tmp); } } }
/** * <p> * Adjusts the matrices so that the singular values are in descending order. * </p> * * <p> * In most implementations of SVD the singular values are automatically arranged in in descending * order. In EJML this is not the case since it is often not needed and some computations can * be saved by not doing that. * </p> * * @param U Matrix. Modified. * @param tranU is U transposed or not. * @param W Diagonal matrix with singular values. Modified. * @param V Matrix. Modified. * @param tranV is V transposed or not. */ // TODO the number of copies can probably be reduced here public static void descendingOrder(FMatrixRMaj U, bool tranU, FMatrixRMaj W, FMatrixRMaj V, bool tranV) { int numSingular = Math.Min(W.numRows, W.numCols); checkSvdMatrixSize(U, tranU, W, V, tranV); for (int i = 0; i < numSingular; i++) { float bigValue = -1; int bigIndex = -1; // find the smallest singular value in the submatrix for (int j = i; j < numSingular; j++) { float v = W.get(j, j); if (v > bigValue) { bigValue = v; bigIndex = j; } } // only swap if the current index is not the smallest if (bigIndex == i) { continue; } if (bigIndex == -1) { // there is at least one uncountable singular value. just stop here break; } float tmp = W.get(i, i); W.set(i, i, bigValue); W.set(bigIndex, bigIndex, tmp); if (V != null) { swapRowOrCol(V, tranV, i, bigIndex); } if (U != null) { swapRowOrCol(U, tranU, i, bigIndex); } } }
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); }
/** * <p> * Puts all the real eigenvectors into the columns of a matrix. If an eigenvalue is imaginary * then the corresponding eigenvector will have zeros in its column. * </p> * * @param eig An eigenvalue decomposition which has already decomposed a matrix. * @return An m by m matrix containing eigenvectors in its columns. */ public static FMatrixRMaj createMatrixV(EigenDecomposition_F32 <FMatrixRMaj> eig) { int N = eig.getNumberOfEigenvalues(); FMatrixRMaj V = new FMatrixRMaj(N, N); for (int i = 0; i < N; i++) { Complex_F32 c = eig.getEigenvalue(i); if (c.isReal()) { FMatrixRMaj v = eig.getEigenVector(i); if (v != null) { for (int j = 0; j < N; j++) { V.set(j, i, v.get(j, 0)); } } } } return(V); }
/** * Makes a draw on the distribution. The results are added to parameter 'x' */ public void next(FMatrixRMaj x) { for (int i = 0; i < r.numRows; i++) { r.set(i, 0, (float)rand.NextGaussian()); } CommonOps_FDRM.multAdd(A, r, x); }
/** * 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); }
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]); } } }
/** * 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); } } }
/** * <p> * Creates a randomly generated set of orthonormal vectors. At most it can generate the same * number of vectors as the dimension of the vectors. * </p> * * <p> * This is done by creating random vectors then ensuring that they are orthogonal * to all the ones previously created with reflectors. * </p> * * <p> * NOTE: This employs a brute force O(N<sup>3</sup>) algorithm. * </p> * * @param dimen dimension of the space which the vectors will span. * @param numVectors How many vectors it should generate. * @param rand Used to create random vectors. * @return Array of N random orthogonal vectors of unit length. */ // is there a faster algorithm out there? This one is a bit sluggish public static FMatrixRMaj[] span(int dimen, int numVectors, IMersenneTwister rand) { if (dimen < numVectors) { throw new ArgumentException("The number of vectors must be less than or equal to the dimension"); } FMatrixRMaj[] u = new FMatrixRMaj[numVectors]; u[0] = RandomMatrices_FDRM.rectangle(dimen, 1, -1, 1, rand); NormOps_FDRM.normalizeF(u[0]); for (int i = 1; i < numVectors; i++) { // Console.WriteLine(" i = "+i); FMatrixRMaj a = new FMatrixRMaj(dimen, 1); FMatrixRMaj r = null; for (int j = 0; j < i; j++) { // Console.WriteLine("j = "+j); if (j == 0) { r = RandomMatrices_FDRM.rectangle(dimen, 1, -1, 1, rand); } // find a vector that is normal to vector j // u[i] = (1/2)*(r + Q[j]*r) a.set(r); VectorVectorMult_FDRM.householder(-2.0f, u[j], r, a); CommonOps_FDRM.add(r, a, a); CommonOps_FDRM.scale(0.5f, a); // UtilEjml.print(a); FMatrixRMaj t = a; a = r; r = t; // normalize it so it doesn't get too small float val = NormOps_FDRM.normF(r); if (val == 0 || float.IsNaN(val) || float.IsInfinity(val)) { throw new InvalidOperationException("Failed sanity check"); } CommonOps_FDRM.divide(r, val); } u[i] = r; } return(u); }
/** * 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; } } }
/** * Multiplied a transpose orthogonal matrix Q by the specified rotator. This is used * to update the U and V matrices. Updating the transpose of the matrix is faster * since it only modifies the rows. * * * @param Q Orthogonal matrix * @param m Coordinate of rotator. * @param n Coordinate of rotator. * @param c cosine of rotator. * @param s sine of rotator. */ protected void updateRotator(FMatrixRMaj Q, int m, int n, float c, float s) { int rowA = m * Q.numCols; int rowB = n * Q.numCols; // for( int i = 0; i < Q.numCols; i++ ) { // float a = Q.get(rowA+i); // float b = Q.get(rowB+i); // Q.set( rowA+i, c*a + s*b); // Q.set( rowB+i, -s*a + c*b); // } // Console.WriteLine("------ AFter Update Rotator "+m+" "+n); // Q.print(); // Console.WriteLine(); int endA = rowA + Q.numCols; for (; rowA != endA; rowA++, rowB++) { float a = Q.get(rowA); float b = Q.get(rowB); Q.set(rowA, c * a + s * b); Q.set(rowB, -s * a + c * b); } }
/** * <p> * Computes the F norm of the difference between the two Matrices:<br> * <br> * Sqrt{∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> ( a<sub>ij</sub> - b<sub>ij</sub>)<sup>2</sup>} * </p> * <p> * This is often used as a cost function. * </p> * * @see NormOps_FDRM#fastNormF * * @param a m by n matrix. Not modified. * @param b m by n matrix. Not modified. * * @return The F normal of the difference matrix. */ public static float diffNormF(FMatrixD1 a, FMatrixD1 b) { if (a.numRows != b.numRows || a.numCols != b.numCols) { throw new ArgumentException("Both matrices must have the same shape."); } int size = a.getNumElements(); FMatrixRMaj diff = new FMatrixRMaj(size, 1); for (int i = 0; i < size; i++) { diff.set(i, b.get(i) - a.get(i)); } return(NormOps_FDRM.normF(diff)); }
/** * Creates a random vector that is inside the specified span. * * @param span The span the random vector belongs in. * @param rand RNG * @return A random vector within the specified span. */ public static FMatrixRMaj insideSpan(FMatrixRMaj[] span, float min, float max, IMersenneTwister rand) { FMatrixRMaj A = new FMatrixRMaj(span.Length, 1); FMatrixRMaj B = new FMatrixRMaj(span[0].getNumElements(), 1); for (int i = 0; i < span.Length; i++) { B.set(span[i]); float val = rand.NextFloat() * (max - min) + min; CommonOps_FDRM.scale(val, B); CommonOps_FDRM.add(A, B, A); } return(A); }
/** * <p> * A diagonal matrix where real diagonal element contains a real eigenvalue. If an eigenvalue * is imaginary then zero is stored in its place. * </p> * * @param eig An eigenvalue decomposition which has already decomposed a matrix. * @return A diagonal matrix containing the eigenvalues. */ public static FMatrixRMaj createMatrixD(EigenDecomposition_F32 <FMatrixRMaj> eig) { int N = eig.getNumberOfEigenvalues(); FMatrixRMaj D = new FMatrixRMaj(N, N); for (int i = 0; i < N; i++) { Complex_F32 c = eig.getEigenvalue(i); if (c.isReal()) { D.set(i, i, c.real); } } return(D); }
private void createMinor(FMatrix1Row mat) { int w = minWidth - 1; int firstRow = (width - w) * width; for (int i = 0; i < numOpen; i++) { int col = open[i]; int srcIndex = firstRow + col; int dstIndex = i; for (int j = 0; j < w; j++) { tempMat.set(dstIndex, mat.get(srcIndex)); dstIndex += w; srcIndex += width; } } }
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; } } }
/** * 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); }
/** * 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); }
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]); } } }
/** * 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); }
/** * Creates an upper triangular matrix whose values are selected from a uniform distribution. If hessenberg * is greater than zero then a hessenberg matrix of the specified degree is created instead. * * @param dimen Number of rows and columns in the matrix.. * @param hessenberg 0 for triangular matrix and > 0 for hessenberg matrix. * @param min minimum value an element can be. * @param max maximum value an element can be. * @param rand random number generator used. * @return The randomly generated matrix. */ public static FMatrixRMaj triangularUpper(int dimen, int hessenberg, float min, float max, IMersenneTwister rand) { if (hessenberg < 0) { throw new InvalidOperationException("hessenberg must be more than or equal to 0"); } float range = max - min; FMatrixRMaj A = new FMatrixRMaj(dimen, dimen); for (int i = 0; i < dimen; i++) { int start = i <= hessenberg ? 0 : i - hessenberg; for (int j = start; j < dimen; j++) { A.set(i, j, rand.NextFloat() * range + min); } } return(A); }
/** * <p> * Creates a random matrix which will have the provided singular values. The length of sv * is assumed to be the rank of the matrix. This can be useful for testing purposes when one * needs to ensure that a matrix is not singular but randomly generated. * </p> * * @param numRows Number of rows in generated matrix. * @param numCols NUmber of columns in generated matrix. * @param rand Random number generator. * @param sv Singular values of the matrix. * @return A new matrix with the specified singular values. */ public static FMatrixRMaj singleValues(int numRows, int numCols, IMersenneTwister rand, float[] sv) { FMatrixRMaj U = RandomMatrices_FDRM.orthogonal(numRows, numRows, rand); FMatrixRMaj V = RandomMatrices_FDRM.orthogonal(numCols, numCols, rand); FMatrixRMaj S = new FMatrixRMaj(numRows, numCols); int min = Math.Min(numRows, numCols); min = Math.Min(min, sv.Length); for (int i = 0; i < min; i++) { S.set(i, i, sv[i]); } FMatrixRMaj tmp = new FMatrixRMaj(numRows, numCols); CommonOps_FDRM.mult(U, S, tmp); CommonOps_FDRM.multTransB(tmp, V, S); return(S); }
/** * Give a string of numbers it returns a DenseMatrix */ public static FMatrixRMaj parse_FDRM(string s, int numColumns) { string[] vals = s.Split('+'); // there is the possibility the first element could be empty int start = string.IsNullOrEmpty(vals[0]) ? 1 : 0; // covert it from string to doubles int numRows = (vals.Length - start) / numColumns; FMatrixRMaj ret = new FMatrixRMaj(numRows, numColumns); int index = start; for (int i = 0; i < numRows; i++) { for (int j = 0; j < numColumns; j++) { ret.set(i, j, float.Parse(vals[index++])); } } return(ret); }
/** * <p> * Given an eigenvalue it computes an eigenvector using inverse iteration: * <br> * for i=1:MAX {<br> * (A - μI)z<sup>(i)</sup> = q<sup>(i-1)</sup><br> * q<sup>(i)</sup> = z<sup>(i)</sup> / ||z<sup>(i)</sup>||<br> * λ<sup>(i)</sup> = q<sup>(i)</sup><sup>T</sup> A q<sup>(i)</sup><br> * }<br> * </p> * <p> * NOTE: If there is another eigenvalue that is very similar to the provided one then there * is a chance of it converging towards that one instead. The larger a matrix is the more * likely this is to happen. * </p> * @param A Matrix whose eigenvector is being computed. Not modified. * @param eigenvalue The eigenvalue in the eigen pair. * @return The eigenvector or null if none could be found. */ public static FEigenpair computeEigenVector(FMatrixRMaj A, float eigenvalue) { if (A.numRows != A.numCols) { throw new ArgumentException("Must be a square matrix."); } FMatrixRMaj M = new FMatrixRMaj(A.numRows, A.numCols); FMatrixRMaj x = new FMatrixRMaj(A.numRows, 1); FMatrixRMaj b = new FMatrixRMaj(A.numRows, 1); CommonOps_FDRM.fill(b, 1); // perturb the eigenvalue slightly so that its not an exact solution the first time // eigenvalue -= eigenvalue*UtilEjml.F_EPS*10; float origEigenvalue = eigenvalue; SpecializedOps_FDRM.addIdentity(A, M, -eigenvalue); float threshold = NormOps_FDRM.normPInf(A) * UtilEjml.F_EPS; float prevError = float.MaxValue; bool hasWorked = false; LinearSolverDense <FMatrixRMaj> solver = LinearSolverFactory_FDRM.linear(M.numRows); float perp = 0.0001f; for (int i = 0; i < 200; i++) { bool failed = false; // if the matrix is singular then the eigenvalue is within machine precision // of the true value, meaning that x must also be. if (!solver.setA(M)) { failed = true; } else { solver.solve(b, x); } // see if solve silently failed if (MatrixFeatures_FDRM.hasUncountable(x)) { failed = true; } if (failed) { if (!hasWorked) { // if it failed on the first trial try perturbing it some more float val = i % 2 == 0 ? 1.0f - perp : 1.0f + perp; // maybe this should be turn into a parameter allowing the user // to configure the wise of each step eigenvalue = origEigenvalue * (float)Math.Pow(val, i / 2 + 1); SpecializedOps_FDRM.addIdentity(A, M, -eigenvalue); } else { // otherwise assume that it was so accurate that the matrix was singular // and return that result return(new FEigenpair(eigenvalue, b)); } } else { hasWorked = true; b.set(x); NormOps_FDRM.normalizeF(b); // compute the residual CommonOps_FDRM.mult(M, b, x); float error = NormOps_FDRM.normPInf(x); if (error - prevError > UtilEjml.F_EPS * 10) { // if the error increased it is probably converging towards a different // eigenvalue // CommonOps.set(b,1); prevError = float.MaxValue; hasWorked = false; float val = i % 2 == 0 ? 1.0f - perp : 1.0f + perp; eigenvalue = origEigenvalue * (float)Math.Pow(val, 1); } else { // see if it has converged if (error <= threshold || Math.Abs(prevError - error) <= UtilEjml.F_EPS) { return(new FEigenpair(eigenvalue, b)); } // update everything prevError = error; eigenvalue = VectorVectorMult_FDRM.innerProdA(b, A, b); } SpecializedOps_FDRM.addIdentity(A, M, -eigenvalue); } } return(null); }
public bool bulgeDoubleStepQn(int i, float a11, float a21, float a31, float threshold, bool set) { float max; if (normalize) { float absA11 = Math.Abs(a11); float absA21 = Math.Abs(a21); float absA31 = Math.Abs(a31); max = absA11 > absA21 ? absA11 : absA21; if (absA31 > max) { max = absA31; } // if( max <= Math.Abs(A.get(i,i))*UtilEjml.F_EPS ) { if (max <= threshold) { if (set) { A.set(i, i - 1, 0); A.set(i + 1, i - 1, 0); A.set(i + 2, i - 1, 0); } return(false); } a11 /= max; a21 /= max; a31 /= max; } else { max = 1; } // compute the reflector using the b's above float tau = (float)Math.Sqrt(a11 * a11 + a21 * a21 + a31 * a31); if (a11 < 0) { tau = -tau; } float div = a11 + tau; u.set(i, 0, 1); u.set(i + 1, 0, a21 / div); u.set(i + 2, 0, a31 / div); gamma = div / tau; // compute A_1 = Q_1^T * A * Q_1 // apply Q*A - just do the 3 rows QrHelperFunctions_FDRM.rank1UpdateMultR(A, u.data, gamma, 0, i, i + 3, _temp.data); if (set) { A.set(i, i - 1, -max * tau); A.set(i + 1, i - 1, 0); A.set(i + 2, i - 1, 0); } if (printHumps) { Console.WriteLine(" After Q. A ="); A.print(); } // apply A*Q - just the three things QrHelperFunctions_FDRM.rank1UpdateMultL(A, u.data, gamma, 0, i, i + 3); // Console.WriteLine(" after Q*A*Q "); // A.print(); if (checkUncountable && MatrixFeatures_FDRM.hasUncountable(A)) { throw new InvalidOperationException("bad matrix"); } 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]); } } }