/** * <p> * The vector associated will the smallest singular value is returned as the null space * of the decomposed system. A right null space is returned if 'isRight' is set to true, * and a left null space if false. * </p> * * @param svd A precomputed decomposition. Not modified. * @param isRight true for right null space and false for left null space. Right is more commonly used. * @param nullVector Optional storage for a vector for the null space. Modified. * @return Vector in V associated with smallest singular value.. */ public static DMatrixRMaj nullVector(SingularValueDecomposition_F64 <DMatrixRMaj> svd, bool isRight, DMatrixRMaj nullVector) { int N = svd.numberOfSingularValues(); double[] s = svd.getSingularValues(); DMatrixRMaj A = isRight ? svd.getV(null, true) : svd.getU(null, false); if (isRight) { if (A.numRows != svd.numCols()) { throw new ArgumentException( "Can't compute the null space using a compact SVD for a matrix of this size."); } if (nullVector == null) { nullVector = new DMatrixRMaj(svd.numCols(), 1); } } else { if (A.numCols != svd.numRows()) { throw new ArgumentException( "Can't compute the null space using a compact SVD for a matrix of this size."); } if (nullVector == null) { nullVector = new DMatrixRMaj(svd.numRows(), 1); } } int smallestIndex = -1; if (isRight && svd.numCols() > svd.numRows()) { smallestIndex = svd.numCols() - 1; } else if (!isRight && svd.numCols() < svd.numRows()) { smallestIndex = svd.numRows() - 1; } else { // find the smallest singular value double smallestValue = double.MaxValue; for (int i = 0; i < N; i++) { if (s[i] < smallestValue) { smallestValue = s[i]; smallestIndex = i; } } } // extract the null space if (isRight) { SpecializedOps_DDRM.subvector(A, smallestIndex, 0, A.numRows, true, 0, nullVector); } else { SpecializedOps_DDRM.subvector(A, 0, smallestIndex, A.numRows, false, 0, nullVector); } return(nullVector); }
/** * <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 DEigenpair computeEigenVector(DMatrixRMaj A, double eigenvalue) { if (A.numRows != A.numCols) { throw new ArgumentException("Must be a square matrix."); } DMatrixRMaj M = new DMatrixRMaj(A.numRows, A.numCols); DMatrixRMaj x = new DMatrixRMaj(A.numRows, 1); DMatrixRMaj b = new DMatrixRMaj(A.numRows, 1); CommonOps_DDRM.fill(b, 1); // perturb the eigenvalue slightly so that its not an exact solution the first time // eigenvalue -= eigenvalue*UtilEjml.EPS*10; double origEigenvalue = eigenvalue; SpecializedOps_DDRM.addIdentity(A, M, -eigenvalue); double threshold = NormOps_DDRM.normPInf(A) * UtilEjml.EPS; double prevError = double.MaxValue; bool hasWorked = false; LinearSolverDense <DMatrixRMaj> solver = LinearSolverFactory_DDRM.linear(M.numRows); double perp = 0.0001; 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_DDRM.hasUncountable(x)) { failed = true; } if (failed) { if (!hasWorked) { // if it failed on the first trial try perturbing it some more double val = i % 2 == 0 ? 1.0 - perp : 1.0 + perp; // maybe this should be turn into a parameter allowing the user // to configure the wise of each step eigenvalue = origEigenvalue * Math.Pow(val, i / 2 + 1); SpecializedOps_DDRM.addIdentity(A, M, -eigenvalue); } else { // otherwise assume that it was so accurate that the matrix was singular // and return that result return(new DEigenpair(eigenvalue, b)); } } else { hasWorked = true; b.set(x); NormOps_DDRM.normalizeF(b); // compute the residual CommonOps_DDRM.mult(M, b, x); double error = NormOps_DDRM.normPInf(x); if (error - prevError > UtilEjml.EPS * 10) { // if the error increased it is probably converging towards a different // eigenvalue // CommonOps.set(b,1); prevError = double.MaxValue; hasWorked = false; double val = i % 2 == 0 ? 1.0 - perp : 1.0 + perp; eigenvalue = origEigenvalue * Math.Pow(val, 1); } else { // see if it has converged if (error <= threshold || Math.Abs(prevError - error) <= UtilEjml.EPS) { return(new DEigenpair(eigenvalue, b)); } // update everything prevError = error; eigenvalue = VectorVectorMult_DDRM.innerProdA(b, A, b); } SpecializedOps_DDRM.addIdentity(A, M, -eigenvalue); } } return(null); }