package com.github.fommil.netlib; import com.google.common.base.Stopwatch; import lombok.extern.java.Log; import org.netlib.util.doubleW; import org.netlib.util.intW; import java.util.Arrays; import java.util.concurrent.TimeUnit; /** * Port of dssimp from the ARPACK distribution (although note that DSSIMP is incredibly memory inefficient as it * confuses N with NX and ends up setting all memory requirements to be needlessly quadratic). * * @author Sam Halliday * @see <a href="http://forge.scilab.org/index.php/p/arpack-ng/source/tree/master/EXAMPLES/SIMPLE/dssimp.f">ddsimp.f</a> */ @Log public class Dsaupd implements Benchmark.Parameterised { private ARPACK arpack = ARPACK.getInstance(); @Override public long benchmark(int size) { int n = (int) Math.sqrt(size); // calculate 10% of the eigenvalues int eigenvalues = Math.max(1, n / 10); double tolerance = 0.0; int ldv = n; intW nev = new intW(eigenvalues); int ncv = Math.min(2 * eigenvalues, n); String bmat = "I"; String which = "LM"; doubleW tol = new doubleW(tolerance); intW info = new intW(0); int[] iparam = new int[11]; iparam[0] = 1; iparam[2] = 300; iparam[6] = 1; intW ido = new intW(0); // used for initial residual (if info != 0) // and eventually the output residual double[] resid = new double[n]; // Lanczos basis vectors double[] v = new double[ldv * ncv]; // Arnoldi reverse communication double[] workd = new double[3 * n]; // private work array double[] workl = new double[ncv * (ncv + 8)]; int[] ipntr = new int[11]; Stopwatch watch = new Stopwatch(); watch.start(); int i = 0; while (true) { i++; arpack.dsaupd(ido, bmat, n, which, nev.val, tol, resid, ncv, v, ldv, iparam, ipntr, workd, workl, workl.length, info); if (ido.val != -1 && ido.val != 1) break; // could be refactored to handle the other types of mode watch.stop(); av(n, workd, ipntr[0] - 1, ipntr[1] - 1); watch.start(); } watch.stop(); log.info(i + " iterations for " + n); if (info.val < 0) throw new IllegalStateException("info = " + info.val); // double[] d = new double[2 * ncv]; // boolean[] select = new boolean[ncv]; // intW ierr = new intW(0); // double sigma = 0.0; // arpack.dseupd(eigenvectors, "All", select, d, v, ldv, sigma, bmat, n, which, nev, tol.val, resid, ncv, v, ldv, iparam, ipntr, workd, workl, workl.length, ierr); // if (ierr.val != 0) throw new IllegalStateException("ierr = " + ierr.val); return watch.elapsed(TimeUnit.NANOSECONDS); } /* | Perform matrix vector multiplication | | y <--- OP*x | Computes w <--- OP*v, where OP is the nx*nx by nx*nx block tridiagonal matrix | T -I | |-I T -I | OP = | -I T | | ... -I| | -I T| */ private void av(int n, double[] work, int input_offset, int output_offset) { double[] x = Arrays.copyOfRange(work, input_offset, input_offset + n); double[] y = new double[n]; // let T = 2, I = 1 // matrix applied to x, results in y... for (int row = 0; row < n; row++) { for (int col = 0; col < n; col++) { if (col == (row - 1) || col == (row + 1)) { y[row] = y[row] - x[col]; } else if (row == col) { y[row] = y[row] + 2 * x[col]; } } } for (int i = 0; i < n; i++) { work[i + output_offset] = y[i]; } } }