/// <summary> /// Returns the bi-diagonal matrix B of the transform. /// </summary> /// <returns>the B matrix</returns> public RealMatrix getB() { if (cachedB == null) { int m = householderVectors.Length; int n = householderVectors[0].Length; double[][] ba = new double[m][]; for (int i = 0; i < main.Length; ++i) { ba[i][i] = main[i]; if (m < n) { if (i > 0) { ba[i][i - 1] = secondary[i - 1]; } } else { if (i < main.Length - 1) { ba[i][i + 1] = secondary[i]; } } } cachedB = MatrixUtils.createRealMatrix(ba); } // return the cached matrix return(cachedB); }
/// <inheritdoc/> public new RealMatrix outerProduct(RealVector v) { if (v is ArrayRealVector) { double[] vData = ((ArrayRealVector)v).data; int m = data.Length; int n = vData.Length; RealMatrix outp = MatrixUtils.createRealMatrix(m, n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { outp.setEntry(i, j, data[i] * vData[j]); } } return(outp); } else { int m = data.Length; int n = v.getDimension(); RealMatrix outp = MatrixUtils.createRealMatrix(m, n); for (int i = 0; i < m; i++) { for (int j = 0; j < n; j++) { outp.setEntry(i, j, data[i] * v.getEntry(j)); } } return(outp); } }
/// <summary> /// Returns the Hessenberg matrix H of the transform. /// </summary> /// <returns>the H matrix</returns> public RealMatrix getH() { if (cachedH == null) { int m = householderVectors.Length; double[][] h = new double[m][]; for (int i = 0; i < m; ++i) { if (i > 0) { // copy the entry of the lower sub-diagonal h[i][i - 1] = householderVectors[i][i - 1]; } // copy upper triangular part of the matrix for (int j = i; j < m; ++j) { h[i][j] = householderVectors[i][j]; } } cachedH = MatrixUtils.createRealMatrix(h); } // return the cached matrix return(cachedH); }
/// <summary> /// Get the inverse of the decomposed matrix. /// </summary> /// <returns>the inverse matrix.</returns> /// <exception cref="SingularMatrixException"> if the decomposed matrix is singular. /// </exception> public RealMatrix getInverse() { if (!isNonSingular()) { throw new SingularMatrixException(); } int m = realEigenvalues.Length; double[][] invData = new double[m][]; for (int i = 0; i < m; ++i) { double[] invI = invData[i]; for (int j = 0; j < m; ++j) { double invIJ = 0; for (int k = 0; k < m; ++k) { double[] vK = eigenvectors[k].getDataRef(); invIJ += vK[i] * vK[j] / realEigenvalues[k]; } invI[j] = invIJ; } } return(MatrixUtils.createRealMatrix(invData)); }
/// <summary> /// Returns the matrix P of the transform. /// <para>P is an orthogonal matrix, i.e. its inverse is also its transpose.</para> /// </summary> /// <returns>the P matrix</returns> public RealMatrix getP() { if (cachedP == null) { cachedP = MatrixUtils.createRealMatrix(matrixP); } return(cachedP); }
/// <summary> /// Returns the transpose of the matrix L of the decomposition. /// <para>L^T is an upper-triangular matrix</para> /// </summary> /// <returns>the transpose of the matrix L of the decomposition</returns> public RealMatrix getLT() { if (cachedLT == null) { cachedLT = MatrixUtils.createRealMatrix(lTData); } // return the cached matrix return(cachedLT); }
/// <summary> /// Returns the quasi-triangular Schur matrix T of the transform. /// </summary> /// <returns>the T matrix</returns> public RealMatrix getT() { if (cachedT == null) { cachedT = MatrixUtils.createRealMatrix(matrixT); } // return the cached matrix return(cachedT); }
/// <summary> /// Gets the matrix V of the decomposition. /// V is an orthogonal matrix, i.e. its transpose is also its inverse. /// The columns of V are the eigenvectors of the original matrix. /// No assumption is made about the orientation of the system axes formed /// by the columns of V (e.g. in a 3-dimension space, V can form a left- /// or right-handed system). /// </summary> /// <returns>the V matrix.</returns> public RealMatrix getV() { if (cachedV == null) { int m = eigenvectors.Length; cachedV = MatrixUtils.createRealMatrix(m, m); for (int k = 0; k < m; ++k) { cachedV.setColumnVector(k, eigenvectors[k]); } } // return the cached matrix return(cachedV); }
/// <summary> /// Returns the matrix U of the transform. /// <para>U is an orthogonal matrix, i.e. its transpose is also its inverse.</para> /// </summary> /// <returns>the U matrix</returns> public RealMatrix getU() { if (cachedU == null) { int m = householderVectors.Length; int n = householderVectors[0].Length; int p = main.Length; int diagOffset = (m >= n) ? 0 : 1; double[] diagonal = (m >= n) ? main : secondary; double[][] ua = new double[m][]; // fill up the part of the matrix not affected by Householder transforms for (int k = m - 1; k >= p; --k) { ua[k][k] = 1; } // build up first part of the matrix by applying Householder transforms for (int k = p - 1; k >= diagOffset; --k) { double[] hK = householderVectors[k]; ua[k][k] = 1; if (hK[k - diagOffset] != 0.0) { for (int j = k; j < m; ++j) { double alpha = 0; for (int i = k; i < m; ++i) { alpha -= ua[i][j] * householderVectors[i][k - diagOffset]; } alpha /= diagonal[k - diagOffset] * hK[k - diagOffset]; for (int i = k; i < m; ++i) { ua[i][j] += -alpha * householderVectors[i][k - diagOffset]; } } } } if (diagOffset > 0) { ua[0][0] = 1; } cachedU = MatrixUtils.createRealMatrix(ua); } // return the cached matrix return(cachedU); }
/// <summary> /// Returns the matrix V of the transform. /// <para>V is an orthogonal matrix, i.e. its transpose is also its inverse.</para> /// </summary> /// <returns>the V matrix</returns> public RealMatrix getV() { if (cachedV == null) { int m = householderVectors.Length; int n = householderVectors[0].Length; int p = main.Length; int diagOffset = (m >= n) ? 1 : 0; double[] diagonal = (m >= n) ? secondary : main; double[][] va = new double[n][]; // fill up the part of the matrix not affected by Householder transforms for (int k = n - 1; k >= p; --k) { va[k][k] = 1; } // build up first part of the matrix by applying Householder transforms for (int k = p - 1; k >= diagOffset; --k) { double[] hK = householderVectors[k - diagOffset]; va[k][k] = 1; if (hK[k] != 0.0) { for (int j = k; j < n; ++j) { double beta = 0; for (int i = k; i < n; ++i) { beta -= va[i][j] * hK[i]; } beta /= diagonal[k - diagOffset] * hK[k]; for (int i = k; i < n; ++i) { va[i][j] += -beta * hK[i]; } } } } if (diagOffset > 0) { va[0][0] = 1; } cachedV = MatrixUtils.createRealMatrix(va); } // return the cached matrix return(cachedV); }
/// <summary> /// Returns the matrix P of the transform. /// <para>P is an orthogonal matrix, i.e. its inverse is also its transpose.</para> /// </summary> /// <returns>the P matrix</returns> public RealMatrix getP() { if (cachedP == null) { int n = householderVectors.Length; int high = n - 1; double[][] pa = new double[n][]; for (int i = 0; i < n; i++) { for (int j = 0; j < n; j++) { pa[i][j] = (i == j) ? 1 : 0; } } for (int m = high - 1; m >= 1; m--) { if (householderVectors[m][m - 1] != 0.0) { for (int i = m + 1; i <= high; i++) { ort[i] = householderVectors[i][m - 1]; } for (int j = m; j <= high; j++) { double g = 0.0; for (int i = m; i <= high; i++) { g += ort[i] * pa[i][j]; } // Double division avoids possible underflow g = (g / ort[m]) / householderVectors[m][m - 1]; for (int i = m; i <= high; i++) { pa[i][j] += g * ort[i]; } } } } cachedP = MatrixUtils.createRealMatrix(pa); } return(cachedP); }
/// <summary> /// Returns the transpose of the matrix Q of the decomposition. /// <para>Q is an orthogonal matrix</para> /// </summary> /// <returns>the transpose of the Q matrix, Q^T</returns> public RealMatrix getQT() { if (cachedQT == null) { // QT is supposed to be m x m int n = qrt.Length; int m = qrt[0].Length; double[][] qta = new double[m][]; /* * Q = Q1 Q2 ... Q_m, so Q is formed by first constructing Q_m and then * applying the Householder transformations Q_(m-1),Q_(m-2),...,Q1 in * succession to the result */ for (int minor = m - 1; minor >= FastMath.min(m, n); minor--) { qta[minor][minor] = 1.0d; } for (int minor = FastMath.min(m, n) - 1; minor >= 0; minor--) { double[] qrtMinor = qrt[minor]; qta[minor][minor] = 1.0d; if (qrtMinor[minor] != 0.0) { for (int col = minor; col < m; col++) { double alpha = 0; for (int row = minor; row < m; row++) { alpha -= qta[col][row] * qrtMinor[row]; } alpha /= rDiag[minor] * qrtMinor[minor]; for (int row = minor; row < m; row++) { qta[col][row] += -alpha * qrtMinor[row]; } } } } cachedQT = MatrixUtils.createRealMatrix(qta); } // return the cached matrix return(cachedQT); }
/// <summary> /// Returns the transpose of the matrix Q of the transform. /// <para>Q is an orthogonal matrix, i.e. its transpose is also its inverse.</para> /// </summary> /// <returns>the Q matrix</returns> public RealMatrix getQT() { if (cachedQt == null) { int m = householderVectors.Length; double[][] qta = new double[m][]; // build up first part of the matrix by applying Householder transforms for (int k = m - 1; k >= 1; --k) { double[] hK = householderVectors[k - 1]; qta[k][k] = 1; if (hK[k] != 0.0) { double inv = 1.0 / (secondary[k - 1] * hK[k]); double beta = 1.0 / secondary[k - 1]; qta[k][k] = 1 + beta * hK[k]; for (int i = k + 1; i < m; ++i) { qta[k][i] = beta * hK[i]; } for (int j = k + 1; j < m; ++j) { beta = 0; for (int i = k + 1; i < m; ++i) { beta += qta[j][i] * hK[i]; } beta *= inv; qta[j][k] = beta * hK[k]; for (int i = k + 1; i < m; ++i) { qta[j][i] += beta * hK[i]; } } } } qta[0][0] = 1; cachedQt = MatrixUtils.createRealMatrix(qta); } // return the cached matrix return(cachedQt); }
/// <summary> /// Returns the Householder reflector vectors. /// <para>H is a lower trapezoidal matrix whose columns represent /// each successive Householder reflector vector. This matrix is used /// to compute Q.</para> /// </summary> /// <returns>a matrix containing the Householder reflector vectors</returns> public RealMatrix getH() { if (cachedH == null) { int n = qrt.Length; int m = qrt[0].Length; double[][] ha = new double[m][]; for (int i = 0; i < m; ++i) { for (int j = 0; j < FastMath.min(i + 1, n); ++j) { ha[i][j] = qrt[j][i] / -rDiag[j]; } } cachedH = MatrixUtils.createRealMatrix(ha); } // return the cached matrix return(cachedH); }
/// <summary> /// Returns the matrix R of the decomposition. /// <para>R is an upper-triangular matrix</para> /// </summary> /// <returns>the R matrix</returns> public RealMatrix getR() { if (cachedR == null) { // R is supposed to be m x n int n = qrt.Length; int m = qrt[0].Length; double[][] ra = new double[m][]; // copy the diagonal from rDiag and the upper triangle of qr for (int row = FastMath.min(m, n) - 1; row >= 0; row--) { ra[row][row] = rDiag[row]; for (int col = row + 1; col < n; col++) { ra[row][col] = qrt[col][row]; } } cachedR = MatrixUtils.createRealMatrix(ra); } // return the cached matrix return(cachedR); }
/// <summary> /// Returns the tridiagonal matrix T of the transform. /// </summary> /// <returns>the T matrix</returns> public RealMatrix getT() { if (cachedT == null) { int m = main.Length; double[][] ta = new double[m][]; for (int i = 0; i < m; ++i) { ta[i][i] = main[i]; if (i > 0) { ta[i][i - 1] = secondary[i - 1]; } if (i < main.Length - 1) { ta[i][i + 1] = secondary[i]; } } cachedT = MatrixUtils.createRealMatrix(ta); } // return the cached matrix return(cachedT); }
/// <summary> /// Calculates the compact Singular Value Decomposition of the given matrix. /// </summary> /// <param name="matrix">Matrix to decompose.</param> public SingularValueDecomposition(RealMatrix matrix) { double[][] A; // "m" is always the largest dimension. if (matrix.getRowDimension() < matrix.getColumnDimension()) { transposed = true; A = matrix.transpose().getData(); m = matrix.getColumnDimension(); n = matrix.getRowDimension(); } else { transposed = false; A = matrix.getData(); m = matrix.getRowDimension(); n = matrix.getColumnDimension(); } singularValues = new double[n]; double[][] U = new double[m][]; double[][] V = new double[n][]; double[] e = new double[n]; double[] work = new double[m]; // Reduce A to bidiagonal form, storing the diagonal elements // in s and the super-diagonal elements in e. int nct = FastMath.min(m - 1, n); int nrt = FastMath.max(0, n - 2); for (int k = 0; k < FastMath.max(nct, nrt); k++) { if (k < nct) { // Compute the transformation for the k-th column and // place the k-th diagonal in s[k]. // Compute 2-norm of k-th column without under/overflow. singularValues[k] = 0; for (int i = k; i < m; i++) { singularValues[k] = FastMath.hypot(singularValues[k], A[i][k]); } if (singularValues[k] != 0) { if (A[k][k] < 0) { singularValues[k] = -singularValues[k]; } for (int i = k; i < m; i++) { A[i][k] /= singularValues[k]; } A[k][k] += 1; } singularValues[k] = -singularValues[k]; } for (int j = k + 1; j < n; j++) { if (k < nct && singularValues[k] != 0) { // Apply the transformation. double t = 0; for (int i = k; i < m; i++) { t += A[i][k] * A[i][j]; } t = -t / A[k][k]; for (int i = k; i < m; i++) { A[i][j] += t * A[i][k]; } } // Place the k-th row of A into e for the // subsequent calculation of the row transformation. e[j] = A[k][j]; } if (k < nct) { // Place the transformation in U for subsequent back // multiplication. for (int i = k; i < m; i++) { U[i][k] = A[i][k]; } } if (k < nrt) { // Compute the k-th row transformation and place the // k-th super-diagonal in e[k]. // Compute 2-norm without under/overflow. e[k] = 0; for (int i = k + 1; i < n; i++) { e[k] = FastMath.hypot(e[k], e[i]); } if (e[k] != 0) { if (e[k + 1] < 0) { e[k] = -e[k]; } for (int i = k + 1; i < n; i++) { e[i] /= e[k]; } e[k + 1] += 1; } e[k] = -e[k]; if (k + 1 < m && e[k] != 0) { // Apply the transformation. for (int i = k + 1; i < m; i++) { work[i] = 0; } for (int j = k + 1; j < n; j++) { for (int i = k + 1; i < m; i++) { work[i] += e[j] * A[i][j]; } } for (int j = k + 1; j < n; j++) { double t = -e[j] / e[k + 1]; for (int i = k + 1; i < m; i++) { A[i][j] += t * work[i]; } } } // Place the transformation in V for subsequent // back multiplication. for (int i = k + 1; i < n; i++) { V[i][k] = e[i]; } } } // Set up the final bidiagonal matrix or order p. int p = n; if (nct < n) { singularValues[nct] = A[nct][nct]; } if (m < p) { singularValues[p - 1] = 0; } if (nrt + 1 < p) { e[nrt] = A[nrt][p - 1]; } e[p - 1] = 0; // Generate U. for (int j = nct; j < n; j++) { for (int i = 0; i < m; i++) { U[i][j] = 0; } U[j][j] = 1; } for (int k = nct - 1; k >= 0; k--) { if (singularValues[k] != 0) { for (int j = k + 1; j < n; j++) { double t = 0; for (int i = k; i < m; i++) { t += U[i][k] * U[i][j]; } t = -t / U[k][k]; for (int i = k; i < m; i++) { U[i][j] += t * U[i][k]; } } for (int i = k; i < m; i++) { U[i][k] = -U[i][k]; } U[k][k] = 1 + U[k][k]; for (int i = 0; i < k - 1; i++) { U[i][k] = 0; } } else { for (int i = 0; i < m; i++) { U[i][k] = 0; } U[k][k] = 1; } } // Generate V. for (int k = n - 1; k >= 0; k--) { if (k < nrt && e[k] != 0) { for (int j = k + 1; j < n; j++) { double t = 0; for (int i = k + 1; i < n; i++) { t += V[i][k] * V[i][j]; } t = -t / V[k + 1][k]; for (int i = k + 1; i < n; i++) { V[i][j] += t * V[i][k]; } } } for (int i = 0; i < n; i++) { V[i][k] = 0; } V[k][k] = 1; } // Main iteration loop for the singular values. int pp = p - 1; while (p > 0) { int k; int kase; // Here is where a test for too many iterations would go. // This section of the program inspects for // negligible elements in the s and e arrays. On // completion the variables kase and k are set as follows. // kase = 1 if s(p) and e[k-1] are negligible and k<p // kase = 2 if s(k) is negligible and k<p // kase = 3 if e[k-1] is negligible, k<p, and // s(k), ..., s(p) are not negligible (qr step). // kase = 4 if e(p-1) is negligible (convergence). for (k = p - 2; k >= 0; k--) { double threshold = TINY + EPS * (FastMath.abs(singularValues[k]) + FastMath.abs(singularValues[k + 1])); // the following condition is written this way in order // to break out of the loop when NaN occurs, writing it // as "if (FastMath.abs(e[k]) <= threshold)" would loop // indefinitely in case of NaNs because comparison on NaNs // always return false, regardless of what is checked // see issue MATH-947 if (!(FastMath.abs(e[k]) > threshold)) { e[k] = 0; break; } } if (k == p - 2) { kase = 4; } else { int ks; for (ks = p - 1; ks >= k; ks--) { if (ks == k) { break; } double t = (ks != p ? FastMath.abs(e[ks]) : 0) + (ks != k + 1 ? FastMath.abs(e[ks - 1]) : 0); if (FastMath.abs(singularValues[ks]) <= TINY + EPS * t) { singularValues[ks] = 0; break; } } if (ks == k) { kase = 3; } else if (ks == p - 1) { kase = 1; } else { kase = 2; k = ks; } } k++; // Perform the task indicated by kase. switch (kase) { // Deflate negligible s(p). case 1: { double f = e[p - 2]; e[p - 2] = 0; for (int j = p - 2; j >= k; j--) { double t = FastMath.hypot(singularValues[j], f); double cs = singularValues[j] / t; double sn = f / t; singularValues[j] = t; if (j != k) { f = -sn * e[j - 1]; e[j - 1] = cs * e[j - 1]; } for (int i = 0; i < n; i++) { t = cs * V[i][j] + sn * V[i][p - 1]; V[i][p - 1] = -sn * V[i][j] + cs * V[i][p - 1]; V[i][j] = t; } } } break; // Split at negligible s(k). case 2: { double f = e[k - 1]; e[k - 1] = 0; for (int j = k; j < p; j++) { double t = FastMath.hypot(singularValues[j], f); double cs = singularValues[j] / t; double sn = f / t; singularValues[j] = t; f = -sn * e[j]; e[j] = cs * e[j]; for (int i = 0; i < m; i++) { t = cs * U[i][j] + sn * U[i][k - 1]; U[i][k - 1] = -sn * U[i][j] + cs * U[i][k - 1]; U[i][j] = t; } } } break; // Perform one qr step. case 3: { // Calculate the shift. double maxPm1Pm2 = FastMath.max(FastMath.abs(singularValues[p - 1]), FastMath.abs(singularValues[p - 2])); double scale = FastMath.max(FastMath.max(FastMath.max(maxPm1Pm2, FastMath.abs(e[p - 2])), FastMath.abs(singularValues[k])), FastMath.abs(e[k])); double sp = singularValues[p - 1] / scale; double spm1 = singularValues[p - 2] / scale; double epm1 = e[p - 2] / scale; double sk = singularValues[k] / scale; double ek = e[k] / scale; double b = ((spm1 + sp) * (spm1 - sp) + epm1 * epm1) / 2.0; double c = (sp * epm1) * (sp * epm1); double shift = 0; if (b != 0 || c != 0) { shift = FastMath.sqrt(b * b + c); if (b < 0) { shift = -shift; } shift = c / (b + shift); } double f = (sk + sp) * (sk - sp) + shift; double g = sk * ek; // Chase zeros. for (int j = k; j < p - 1; j++) { double t = FastMath.hypot(f, g); double cs = f / t; double sn = g / t; if (j != k) { e[j - 1] = t; } f = cs * singularValues[j] + sn * e[j]; e[j] = cs * e[j] - sn * singularValues[j]; g = sn * singularValues[j + 1]; singularValues[j + 1] = cs * singularValues[j + 1]; for (int i = 0; i < n; i++) { t = cs * V[i][j] + sn * V[i][j + 1]; V[i][j + 1] = -sn * V[i][j] + cs * V[i][j + 1]; V[i][j] = t; } t = FastMath.hypot(f, g); cs = f / t; sn = g / t; singularValues[j] = t; f = cs * e[j] + sn * singularValues[j + 1]; singularValues[j + 1] = -sn * e[j] + cs * singularValues[j + 1]; g = sn * e[j + 1]; e[j + 1] = cs * e[j + 1]; if (j < m - 1) { for (int i = 0; i < m; i++) { t = cs * U[i][j] + sn * U[i][j + 1]; U[i][j + 1] = -sn * U[i][j] + cs * U[i][j + 1]; U[i][j] = t; } } } e[p - 2] = f; } break; // Convergence. default: { // Make the singular values positive. if (singularValues[k] <= 0) { singularValues[k] = singularValues[k] < 0 ? -singularValues[k] : 0; for (int i = 0; i <= pp; i++) { V[i][k] = -V[i][k]; } } // Order the singular values. while (k < pp) { if (singularValues[k] >= singularValues[k + 1]) { break; } double t = singularValues[k]; singularValues[k] = singularValues[k + 1]; singularValues[k + 1] = t; if (k < n - 1) { for (int i = 0; i < n; i++) { t = V[i][k + 1]; V[i][k + 1] = V[i][k]; V[i][k] = t; } } if (k < m - 1) { for (int i = 0; i < m; i++) { t = U[i][k + 1]; U[i][k + 1] = U[i][k]; U[i][k] = t; } } k++; } p--; } break; } } // Set the small value tolerance used to calculate rank and pseudo-inverse tol = FastMath.max(m * singularValues[0] * EPS, FastMath.sqrt(Precision.SAFE_MIN)); if (!transposed) { cachedU = MatrixUtils.createRealMatrix(U); cachedV = MatrixUtils.createRealMatrix(V); } else { cachedU = MatrixUtils.createRealMatrix(V); cachedV = MatrixUtils.createRealMatrix(U); } }
/// <summary> /// Parse a string to produce a <see cref="RealMatrix"/> object. /// </summary> /// <param name="source">String to parse.</param> /// <param name="pos">input/ouput parsing parameter.</param> /// <returns>the parsed <see cref="RealMatrix"/> object.</returns> public RealMatrix parse(String source, ref Int32 pos) { int initialIndex = pos; String trimmedPrefix = prefix.Trim(); String trimmedSuffix = suffix.Trim(); String trimmedRowPrefix = rowPrefix.Trim(); String trimmedRowSuffix = rowSuffix.Trim(); String trimmedColumnSeparator = columnSeparator.Trim(); String trimmedRowSeparator = rowSeparator.Trim(); // parse prefix CompositeFormat.parseAndIgnoreWhitespace(source, pos); if (!CompositeFormat.parseFixedstring(source, trimmedPrefix)) { return(null); } // parse components List <List <double> > matrix = new List <List <double> >(); List <double> rowComponents = new List <double>(); for (Boolean loop = true; loop;) { if (rowComponents.Count != 0) { CompositeFormat.parseAndIgnoreWhitespace(source, pos); if (!CompositeFormat.parseFixedstring(source, trimmedColumnSeparator)) { if (trimmedRowSuffix.Length != 0 && !CompositeFormat.parseFixedstring(source, trimmedRowSuffix)) { return(null); } else { CompositeFormat.parseAndIgnoreWhitespace(source, pos); if (CompositeFormat.parseFixedstring(source, trimmedRowSeparator)) { matrix.Add(rowComponents); rowComponents = new List <double>(); continue; } else { loop = false; } } } } else { CompositeFormat.parseAndIgnoreWhitespace(source, pos); if (trimmedRowPrefix.Length != 0 && !CompositeFormat.parseFixedstring(source, trimmedRowPrefix)) { return(null); } } if (loop) { CompositeFormat.parseAndIgnoreWhitespace(source, pos); double component = CompositeFormat.parseNumber(source, CultureInfo.CurrentCulture); if (Double.IsNaN(component)) { rowComponents.Add(component); } else { if (rowComponents.Count == 0) { loop = false; } else { // invalid component // set index back to initial, error index should already be set pos = initialIndex; return(null); } } } } if (rowComponents.Count != 0) { matrix.Add(rowComponents); } // parse suffix CompositeFormat.parseAndIgnoreWhitespace(source, pos); if (!CompositeFormat.parseFixedstring(source, trimmedSuffix)) { return(null); } // do not allow an empty matrix if (matrix.Count == 0) { pos = initialIndex; return(null); } // build vector double[][] data = new double[matrix.Count][]; int row = 0; foreach (List <double> rowList in matrix) { data[row] = new double[rowList.Count]; for (int i = 0; i < rowList.Count; i++) { data[row][i] = rowList[i]; } row++; } return(MatrixUtils.createRealMatrix(data)); }