public static void assertEigen( Matrix eigens, VectorIterable corpus, int numEigensToCheck, double errorMargin, boolean isSymmetric) { for (int i = 0; i < numEigensToCheck; i++) { Vector e = eigens.getRow(i); if (e.getLengthSquared() == 0) { continue; } Vector afterMultiply = isSymmetric ? corpus.times(e) : corpus.timesSquared(e); double dot = afterMultiply.dot(e); double afterNorm = afterMultiply.getLengthSquared(); double error = 1 - dot / Math.sqrt(afterNorm * e.getLengthSquared()); assertTrue( "Error margin: {" + error + " too high! (for eigen " + i + ')', Math.abs(error) < errorMargin); } }
/** * For the distributed case, the best guess at a useful initialization state for Lanczos we'll * chose to be uniform over all input dimensions, L_2 normalized. */ public static Vector getInitialVector(VectorIterable corpus) { Vector initialVector = new DenseVector(corpus.numCols()); initialVector.assign(1.0 / Math.sqrt(corpus.numCols())); return initialVector; }
/** * Solves the system Ax = b, where A is a linear operator and b is a vector. Uses the specified * preconditioner to improve numeric stability and possibly speed convergence. This version of * solve() allows control over the termination and iteration parameters. * * @param a The matrix A. * @param b The vector b. * @param preconditioner The preconditioner to apply. * @param maxIterations The maximum number of iterations to run. * @param maxError The maximum amount of residual error to tolerate. The algorithm will run until * the residual falls below this value or until maxIterations are completed. * @return The result x of solving the system. * @throws IllegalArgumentException if the matrix is not square, if the size of b is not equal to * the number of columns of A, if maxError is less than zero, or if maxIterations is not * positive. */ public Vector solve( VectorIterable a, Vector b, Preconditioner preconditioner, int maxIterations, double maxError) { if (a.numRows() != a.numCols()) { throw new IllegalArgumentException("Matrix must be square, symmetric and positive definite."); } if (a.numCols() != b.size()) { throw new CardinalityException(a.numCols(), b.size()); } if (maxIterations <= 0) { throw new IllegalArgumentException("Max iterations must be positive."); } if (maxError < 0.0) { throw new IllegalArgumentException("Max error must be non-negative."); } Vector x = new DenseVector(b.size()); iterations = 0; Vector residual = b.minus(a.times(x)); residualNormSquared = residual.dot(residual); log.info("Conjugate gradient initial residual norm = {}", Math.sqrt(residualNormSquared)); double previousConditionedNormSqr = 0.0; Vector updateDirection = null; while (Math.sqrt(residualNormSquared) > maxError && iterations < maxIterations) { Vector conditionedResidual; double conditionedNormSqr; if (preconditioner == null) { conditionedResidual = residual; conditionedNormSqr = residualNormSquared; } else { conditionedResidual = preconditioner.precondition(residual); conditionedNormSqr = residual.dot(conditionedResidual); } ++iterations; if (iterations == 1) { updateDirection = new DenseVector(conditionedResidual); } else { double beta = conditionedNormSqr / previousConditionedNormSqr; // updateDirection = residual + beta * updateDirection updateDirection.assign(Functions.MULT, beta); updateDirection.assign(conditionedResidual, Functions.PLUS); } Vector aTimesUpdate = a.times(updateDirection); double alpha = conditionedNormSqr / updateDirection.dot(aTimesUpdate); // x = x + alpha * updateDirection PLUS_MULT.setMultiplicator(alpha); x.assign(updateDirection, PLUS_MULT); // residual = residual - alpha * A * updateDirection PLUS_MULT.setMultiplicator(-alpha); residual.assign(aTimesUpdate, PLUS_MULT); previousConditionedNormSqr = conditionedNormSqr; residualNormSquared = residual.dot(residual); log.info( "Conjugate gradient iteration {} residual norm = {}", iterations, Math.sqrt(residualNormSquared)); } return x; }