/** * <p> * The condition number of a matrix is used to measure the sensitivity of the linear * system <b>Ax=b</b>. A value near one indicates that it is a well conditioned matrix.<br> * <br> * κ<sub>p</sub> = ||A||<sub>p</sub>||A<sup>-1</sup>||<sub>p</sub> * </p> * <p> * If the matrix is not square then the condition of either A<sup>T</sup>A or AA<sup>T</sup> is computed. * <p> * @param A The matrix. * @param p p-norm * @return The condition number. */ public static float conditionP(FMatrixRMaj A, float p) { if (p == 2) { return(conditionP2(A)); } else if (A.numRows == A.numCols) { // square matrices are the typical case FMatrixRMaj A_inv = new FMatrixRMaj(A.numRows, A.numCols); if (!CommonOps_FDRM.invert(A, A_inv)) { throw new ArgumentException("A can't be inverted."); } return(normP(A, p) * normP(A_inv, p)); } else { FMatrixRMaj pinv = new FMatrixRMaj(A.numCols, A.numRows); CommonOps_FDRM.pinv(A, pinv); return(normP(A, p) * normP(pinv, p)); } }
//@Override public FMatrixRMaj getQ(FMatrixRMaj Q, bool compact) { int minLength = Math.Min(Ablock.numRows, Ablock.numCols); if (Q == null) { if (compact) { Q = new FMatrixRMaj(Ablock.numRows, minLength); CommonOps_FDRM.setIdentity(Q); } else { Q = new FMatrixRMaj(Ablock.numRows, Ablock.numRows); CommonOps_FDRM.setIdentity(Q); } } FMatrixRBlock Qblock = new FMatrixRBlock(); Qblock.numRows = Q.numRows; Qblock.numCols = Q.numCols; Qblock.blockLength = blockLength; Qblock.data = Q.data; ((QRDecompositionHouseholder_FDRB)alg).getQ(Qblock, compact); convertBlockToRow(Q.numRows, Q.numCols, Ablock.blockLength, Q.data); return(Q); }
/** * <p> * To decompose the matrix 'A' it must have full rank. 'A' is a 'm' by 'n' matrix. * It requires about 2n*m<sup>2</sup>-2m<sup>2</sup>/3 flops. * </p> * * <p> * The matrix provided here can be of different * dimension than the one specified in the constructor. It just has to be smaller than or equal * to it. * </p> */ public override bool decompose(FMatrixRMaj A) { setExpectedMaxSize(A.numRows, A.numCols); convertToColumnMajor(A); maxAbs = CommonOps_FDRM.elementMaxAbs(A); // initialize pivot variables setupPivotInfo(); // go through each column and perform the decomposition for (int j = 0; j < minLength; j++) { if (j > 0) { updateNorms(j); } swapColumns(j); // if its degenerate stop processing if (!householderPivot(j)) { break; } updateA(j); rank = j + 1; } return(true); }
/** * <p> * Returns true if the matrix is symmetric within the tolerance. Only square matrices can be * symmetric. * </p> * <p> * A matrix is symmetric if:<br> * |a<sub>ij</sub> - a<sub>ji</sub>| ≤ tol * </p> * * @param m A matrix. Not modified. * @param tol Tolerance for how similar two elements need to be. * @return true if it is symmetric and false if it is not. */ public static bool isSymmetric(FMatrixRMaj m, float tol) { if (m.numCols != m.numRows) { return(false); } float max = CommonOps_FDRM.elementMaxAbs(m); for (int i = 0; i < m.numRows; i++) { for (int j = 0; j < i; j++) { float a = m.get(i, j) / max; float b = m.get(j, i) / max; float diff = Math.Abs(a - b); if (!(diff <= tol)) { return(false); } } } return(true); }
/** * Returns the orthogonal V matrix. * * @param V If not null then the results will be stored here. Otherwise a new matrix will be created. * @return The extracted Q matrix. */ public virtual FMatrixRMaj getV(FMatrixRMaj V, bool transpose, bool compact) { V = handleV(V, transpose, compact, m, n, min); CommonOps_FDRM.setIdentity(V); // UBV.print(); // todo the very first multiplication can be avoided by setting to the rank1update output for (int j = min - 1; j >= 0; j--) { u[j + 1] = 1; for (int i = j + 2; i < n; i++) { u[i] = UBV.get(j, i); } if (transpose) { QrHelperFunctions_FDRM.rank1UpdateMultL(V, u, gammasV[j], j + 1, j + 1, n); } else { QrHelperFunctions_FDRM.rank1UpdateMultR(V, u, gammasV[j], j + 1, j + 1, n, this.b); } } return(V); }
public virtual FMatrixRMaj getU(FMatrixRMaj U, bool transpose, bool compact) { U = BidiagonalDecompositionRow_FDRM.handleU(U, false, compact, m, n, min); if (compact) { // U = Q*U1 FMatrixRMaj Q1 = decompQRP.getQ(null, true); FMatrixRMaj U1 = decompBi.getU(null, false, true); CommonOps_FDRM.mult(Q1, U1, U); } else { // U = [Q1*U1 Q2] FMatrixRMaj Q = decompQRP.getQ(U, false); FMatrixRMaj U1 = decompBi.getU(null, false, true); FMatrixRMaj Q1 = CommonOps_FDRM.extract(Q, 0, Q.numRows, 0, min); FMatrixRMaj tmp = new FMatrixRMaj(Q1.numRows, U1.numCols); CommonOps_FDRM.mult(Q1, U1, tmp); CommonOps_FDRM.insert(tmp, Q, 0, 0); } if (transpose) { CommonOps_FDRM.transpose(U); } return(U); }
//@Override public FMatrixRMaj getV(FMatrixRMaj V, bool transpose) { if (!prefComputeV) { throw new ArgumentException("As requested V was not computed."); } if (transpose) { if (V == null) { return(Vt); } V.set(Vt); } else { if (V == null) { V = new FMatrixRMaj(Vt.numCols, Vt.numRows); } else { V.reshape(Vt.numCols, Vt.numRows); } CommonOps_FDRM.transpose(Vt, V); } return(V); }
/** * Returns the orthogonal U matrix. * * @param U If not null then the results will be stored here. Otherwise a new matrix will be created. * @return The extracted Q matrix. */ public virtual FMatrixRMaj getU(FMatrixRMaj U, bool transpose, bool compact) { U = handleU(U, transpose, compact, m, n, min); CommonOps_FDRM.setIdentity(U); for (int i = 0; i < m; i++) { u[i] = 0; } for (int j = min - 1; j >= 0; j--) { u[j] = 1; for (int i = j + 1; i < m; i++) { u[i] = UBV.get(i, j); } if (transpose) { QrHelperFunctions_FDRM.rank1UpdateMultL(U, u, gammasU[j], j, j, m); } else { QrHelperFunctions_FDRM.rank1UpdateMultR(U, u, gammasU[j], j, j, m, this.b); } } return(U); }
public virtual FMatrixRMaj getT(FMatrixRMaj T) { int N = Ablock.numRows; if (T == null) { T = new FMatrixRMaj(N, N); } else { CommonOps_FDRM.fill(T, 0); } float[] diag = new float[N]; float[] off = new float[N]; ((TridiagonalDecompositionHouseholder_FDRB)alg).getDiagonal(diag, off); T.unsafe_set(0, 0, diag[0]); for (int i = 1; i < N; i++) { T.unsafe_set(i, i, diag[i]); T.unsafe_set(i, i - 1, off[i - 1]); T.unsafe_set(i - 1, i, off[i - 1]); } return(T); }
//@Override public FMatrixRMaj getU(FMatrixRMaj U, bool transpose) { if (!prefComputeU) { throw new ArgumentException("As requested U was not computed."); } if (transpose) { if (U == null) { return(Ut); } U.set(Ut); } else { if (U == null) { U = new FMatrixRMaj(Ut.numCols, Ut.numRows); } else { U.reshape(Ut.numCols, Ut.numRows); } CommonOps_FDRM.transpose(Ut, U); } return(U); }
/** * <p> * Computes a metric which measures the the quality of an eigen value decomposition. If a * value is returned that is close to or smaller than 1e-15 then it is within machine precision. * </p> * <p> * EVD quality is defined as:<br> * <br> * Quality = ||A*V - V*D|| / ||A*V||. * </p> * * @param orig The original matrix. Not modified. * @param eig EVD of the original matrix. Not modified. * @return The quality of the decomposition. */ public static float quality(FMatrixRMaj orig, EigenDecomposition_F32 <FMatrixRMaj> eig) { FMatrixRMaj A = orig; FMatrixRMaj V = EigenOps_FDRM.createMatrixV(eig); FMatrixRMaj D = EigenOps_FDRM.createMatrixD(eig); // L = A*V FMatrixRMaj L = new FMatrixRMaj(A.numRows, V.numCols); CommonOps_FDRM.mult(A, V, L); // R = V*D FMatrixRMaj R = new FMatrixRMaj(V.numRows, D.numCols); CommonOps_FDRM.mult(V, D, R); FMatrixRMaj diff = new FMatrixRMaj(L.numRows, L.numCols); CommonOps_FDRM.subtract(L, R, diff); float top = NormOps_FDRM.normF(diff); float bottom = NormOps_FDRM.normF(L); float error = top / bottom; return(error); }
/** * <p> * Element wise p-norm:<br> * <br> * norm = {∑<sub>i=1:m</sub> ∑<sub>j=1:n</sub> { |a<sub>ij</sub>|<sup>p</sup>}}<sup>1/p</sup> * </p> * * <p> * This is not the same as the induced p-norm used on matrices, but is the same as the vector p-norm. * </p> * * @param A Matrix. Not modified. * @param p p value. * @return The norm's value. */ public static float elementP(FMatrix1Row A, float p) { if (p == 1) { return(CommonOps_FDRM.elementSumAbs(A)); } if (p == 2) { return(normF(A)); } else { float max = CommonOps_FDRM.elementMaxAbs(A); if (max == 0.0f) { return(0.0f); } float total = 0; int size = A.getNumElements(); for (int i = 0; i < size; i++) { float a = A.get(i) / max; total += (float)Math.Pow(Math.Abs(a), p); } return(max * (float)Math.Pow(total, 1.0f / p)); } }
/** * <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 virtual FMatrixRBlock getT(FMatrixRBlock T) { if (T == null) { T = new FMatrixRBlock(A.numRows, A.numCols, A.blockLength); } else { if (T.numRows != A.numRows || T.numCols != A.numCols) { throw new ArgumentException("T must have the same dimensions as the input matrix"); } CommonOps_FDRM.fill(T, 0); } T.set(0, 0, A.data[0]); for (int i = 1; i < A.numRows; i++) { float d = A.get(i - 1, i); T.set(i, i, A.get(i, i)); T.set(i - 1, i, d); T.set(i, i - 1, d); } return(T); }
/** * <p> * Checks to see if a matrix is orthogonal or isometric. * </p> * * @param Q The matrix being tested. Not modified. * @param tol Tolerance. * @return True if it passes the test. */ public static bool isOrthogonal(FMatrixRMaj Q, float tol) { if (Q.numRows < Q.numCols) { throw new ArgumentException("The number of rows must be more than or equal to the number of columns"); } FMatrixRMaj[] u = CommonOps_FDRM.columnsToVector(Q, null); for (int i = 0; i < u.Length; i++) { FMatrixRMaj a = u[i]; for (int j = i + 1; j < u.Length; j++) { float val = VectorVectorMult_FDRM.innerProd(a, u[j]); if (!(Math.Abs(val) <= tol)) { return(false); } } } return(true); }
private bool extractSeparate(int numCols) { if (!computeEigenValues()) { return(false); } // ---- set up the helper to decompose the same tridiagonal matrix // swap arrays instead of copying them to make it slightly faster helper.reset(numCols); diagSaved = helper.swapDiag(diagSaved); offSaved = helper.swapOff(offSaved); // extract the orthogonal from the similar transform V = decomp.getQ(V, true); // tell eigenvector algorithm to update this matrix as it computes the rotators vector.setQ(V); // extract eigenvectors if (!vector.process(-1, null, null, values)) { return(false); } // the ordering of the eigenvalues might have changed values = helper.copyEigenvalues(values); // the V matrix contains the eigenvectors. Convert those into column vectors eigenvectors = CommonOps_FDRM.rowsToVector(V, eigenvectors); return(true); }
/** * <p> * Creates a pivot matrix that exchanges the rows in a matrix: * <br> * A' = P*A<br> * </p> * <p> * For example, if element 0 in 'pivots' is 2 then the first row in A' will be the 3rd row in A. * </p> * * @param ret If null then a new matrix is declared otherwise the results are written to it. Is modified. * @param pivots Specifies the new order of rows in a matrix. * @param numPivots How many elements in pivots are being used. * @param transposed If the transpose of the matrix is returned. * @return A pivot matrix. */ public static FMatrixRMaj pivotMatrix(FMatrixRMaj ret, int[] pivots, int numPivots, bool transposed) { if (ret == null) { ret = new FMatrixRMaj(numPivots, numPivots); } else { if (ret.numCols != numPivots || ret.numRows != numPivots) { throw new ArgumentException("Unexpected matrix dimension"); } CommonOps_FDRM.fill(ret, 0); } if (transposed) { for (int i = 0; i < numPivots; i++) { ret.set(pivots[i], i, 1); } } else { for (int i = 0; i < numPivots; i++) { ret.set(i, pivots[i], 1); } } return(ret); }
public virtual bool setA(FMatrixRMaj A) { pinv.reshape(A.numCols, A.numRows, false); if (!svd.decompose(A)) { return(false); } svd.getU(U_t, true); svd.getV(V, false); float[] S = svd.getSingularValues(); int N = Math.Min(A.numRows, A.numCols); // compute the threshold for singular values which are to be zeroed float maxSingular = 0; for (int i = 0; i < N; i++) { if (S[i] > maxSingular) { maxSingular = S[i]; } } float tau = threshold * Math.Max(A.numCols, A.numRows) * maxSingular; // computer the pseudo inverse of A if (maxSingular != 0.0f) { for (int i = 0; i < N; i++) { float s = S[i]; if (s < tau) { S[i] = 0; } else { S[i] = 1.0f / S[i]; } } } // V*W for (int i = 0; i < V.numRows; i++) { int index = i * V.numCols; for (int j = 0; j < V.numCols; j++) { V.data[index++] *= S[j]; } } // V*W*U^T CommonOps_FDRM.mult(V, U_t, pinv); return(true); }
public void minus(FMatrixRMaj A, /**/ float b, FMatrixRMaj output) { CommonOps_FDRM.subtract(A, b, output); } public void plus(FMatrixRMaj A, /**/ float b, FMatrixRMaj output) { CommonOps_FDRM.add(A, b, output); }
/** * 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); }
/** * <p> * Returns the null-space from the singular value decomposition. The null space is a set of non-zero vectors that * when multiplied by the original matrix return zero. * </p> * * <p> * The null space is found by extracting the columns in V that are associated singular values less than * or equal to the threshold. In some situations a non-compact SVD is required. * </p> * * @param svd A precomputed decomposition. Not modified. * @param nullSpace Storage for null space. Will be reshaped as needed. Modified. * @param tol Threshold for selecting singular values. Try UtilEjml.F_EPS. * @return The null space. */ public static FMatrixRMaj nullSpace(SingularValueDecomposition_F32 <FMatrixRMaj> svd, FMatrixRMaj nullSpace, float tol) { int N = svd.numberOfSingularValues(); float[] s = svd.getSingularValues(); FMatrixRMaj V = svd.getV(null, true); if (V.numRows != svd.numCols()) { throw new ArgumentException( "Can't compute the null space using a compact SVD for a matrix of this size."); } // first determine the size of the null space int numVectors = svd.numCols() - N; for (int i = 0; i < N; i++) { if (s[i] <= tol) { numVectors++; } } // declare output data if (nullSpace == null) { nullSpace = new FMatrixRMaj(numVectors, svd.numCols()); } else { nullSpace.reshape(numVectors, svd.numCols()); } // now extract the vectors int count = 0; for (int i = 0; i < N; i++) { if (s[i] <= tol) { CommonOps_FDRM.extract(V, i, i + 1, 0, V.numCols, nullSpace, count++, 0); } } for (int i = N; i < svd.numCols(); i++) { CommonOps_FDRM.extract(V, i, i + 1, 0, V.numCols, nullSpace, count++, 0); } CommonOps_FDRM.transpose(nullSpace); return(nullSpace); }
/** * Computes the p=∞ norm. If A is a matrix then the induced norm is computed. * * @param A Matrix or vector. * @return The norm. */ public static float normPInf(FMatrixRMaj A) { if (MatrixFeatures_FDRM.isVector(A)) { return(CommonOps_FDRM.elementMaxAbs(A)); } else { return(inducedPInf(A)); } }
/** * 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 static void invert(LinearSolverDense <FMatrixRMaj> solver, FMatrix1Row A, FMatrixRMaj A_inv) { if (A.numRows != A_inv.numRows || A.numCols != A_inv.numCols) { throw new ArgumentException("A and A_inv must have the same dimensions"); } CommonOps_FDRM.setIdentity(A_inv); solver.solve(A_inv, A_inv); }
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 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]); } } }
/** * An alternative implementation of {@link #multTransA_small} that performs well on large * matrices. There is a relative performance hit when used on small matrices. * * @param A A matrix that is m by n. Not modified. * @param B A Vector that has length m. Not modified. * @param C A column vector that has length n. Modified. */ public static void multTransA_reorder(FMatrix1Row A, FMatrixD1 B, FMatrixD1 C) { if (C.numCols != 1) { throw new MatrixDimensionException("C is not a column vector"); } else if (C.numRows != A.numCols) { throw new MatrixDimensionException("C is not the expected length"); } if (B.numRows == 1) { if (A.numRows != B.numCols) { throw new MatrixDimensionException("A and B are not compatible"); } } else if (B.numCols == 1) { if (A.numRows != B.numRows) { throw new MatrixDimensionException("A and B are not compatible"); } } else { throw new MatrixDimensionException("B is not a vector"); } if (A.numRows == 0) { CommonOps_FDRM.fill(C, 0); return; } float B_val = B.get(0); for (int i = 0; i < A.numCols; i++) { C.set(i, A.get(i) * B_val); } int indexA = A.numCols; for (int i = 1; i < A.numRows; i++) { B_val = B.get(i); for (int j = 0; j < A.numCols; j++) { C.plus(j, A.get(indexA++) * B_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); }
/** * Creates a new random symmetric matrix that will have the specified real eigenvalues. * * @param num Dimension of the resulting matrix. * @param rand Random number generator. * @param eigenvalues Set of real eigenvalues that the matrix will have. * @return A random matrix with the specified eigenvalues. */ public static FMatrixRMaj symmetricWithEigenvalues(int num, IMersenneTwister rand, float[] eigenvalues) { FMatrixRMaj V = RandomMatrices_FDRM.orthogonal(num, num, rand); FMatrixRMaj D = CommonOps_FDRM.diag(eigenvalues); FMatrixRMaj temp = new FMatrixRMaj(num, num); CommonOps_FDRM.mult(V, D, temp); CommonOps_FDRM.multTransB(temp, V, D); return(D); }
/** * <p> * Performs a matrix vector multiply.<br> * <br> * c = A * b <br> * and<br> * c = A * b<sup>T</sup> <br> * <br> * c<sub>i</sub> = Sum{ j=1:n, a<sub>ij</sub> * b<sub>j</sub>}<br> * <br> * where A is a matrix, b is a column or transposed row vector, and c is a column vector. * </p> * * @param A A matrix that is m by n. Not modified. * @param B A vector that has length n. Not modified. * @param C A column vector that has length m. Modified. */ public static void mult(FMatrix1Row A, FMatrixD1 B, FMatrixD1 C) { if (C.numCols != 1) { throw new MatrixDimensionException("C is not a column vector"); } else if (C.numRows != A.numRows) { throw new MatrixDimensionException("C is not the expected length"); } if (B.numRows == 1) { if (A.numCols != B.numCols) { throw new MatrixDimensionException("A and B are not compatible"); } } else if (B.numCols == 1) { if (A.numCols != B.numRows) { throw new MatrixDimensionException("A and B are not compatible"); } } else { throw new MatrixDimensionException("B is not a vector"); } if (A.numCols == 0) { CommonOps_FDRM.fill(C, 0); return; } int indexA = 0; int cIndex = 0; float b0 = B.get(0); for (int i = 0; i < A.numRows; i++) { float total = A.get(indexA++) * b0; for (int j = 1; j < A.numCols; j++) { total += A.get(indexA++) * B.get(j); } C.set(cIndex++, total); } }