from functools import partial import numpy as np from .common import Benchmark, safe_import with safe_import(): from scipy import array, r_, ones, arange, sort, diag, cos, rand, pi from scipy.linalg import eigh, orth, cho_factor, cho_solve import scipy.sparse from scipy.sparse.linalg import lobpcg from scipy.sparse.linalg._interface import LinearOperator def _sakurai(n): """ Example taken from T. Sakurai, H. Tadano, Y. Inadomi and U. Nagashima A moment-based method for large-scale generalized eigenvalue problems Appl. Num. Anal. Comp. Math. Vol. 1 No. 2 (2004) """ A = scipy.sparse.eye(n, n) d0 = array(r_[5, 6*ones(n-2), 5]) d1 = -4*ones(n) d2 = ones(n) B = scipy.sparse.spdiags([d2, d1, d0, d1, d2], [-2, -1, 0, 1, 2], n, n) k = arange(1, n+1) w_ex = sort(1. / (16.*pow(cos(0.5*k*pi/(n+1)), 4))) # exact eigenvalues return A, B, w_ex def _mikota_pair(n): # Mikota pair acts as a nice test since the eigenvalues # are the squares of the integers n, n=1,2,... x = arange(1, n + 1) B = diag(1. / x) y = arange(n - 1, 0, -1) z = arange(2 * n - 1, 0, -2) A = diag(z) - diag(y, -1) - diag(y, 1) return A.astype(float), B.astype(float) def _as2d(ar): if ar.ndim == 2: return ar else: # Assume 1! aux = np.array(ar, copy=False) aux.shape = (ar.shape[0], 1) return aux def _precond(LorU, lower, x): y = cho_solve((LorU, lower), x) return _as2d(y) class Bench(Benchmark): params = [ [], ['lobpcg', 'eigh'] ] param_names = ['n', 'solver'] def __init__(self): self.time_mikota.__func__.params = list(self.params) self.time_mikota.__func__.params[0] = [128, 256, 512, 1024, 2048] self.time_mikota.__func__.setup = self.setup_mikota self.time_sakurai.__func__.params = list(self.params) self.time_sakurai.__func__.params[0] = [50, 400] self.time_sakurai.__func__.setup = self.setup_sakurai def setup_mikota(self, n, solver): self.shape = (n, n) self.A, self.B = _mikota_pair(n) if solver == 'eigh' and n >= 512: # skip: slow, and not useful to benchmark raise NotImplementedError() def setup_sakurai(self, n, solver): self.shape = (n, n) self.A, self.B, all_eigenvalues = _sakurai(n) self.A_dense = self.A.A self.B_dense = self.B.A def time_mikota(self, n, solver): m = 10 if solver == 'lobpcg': X = rand(n, m) X = orth(X) LorU, lower = cho_factor(self.A, lower=0, overwrite_a=0) M = LinearOperator(self.shape, matvec=partial(_precond, LorU, lower), matmat=partial(_precond, LorU, lower)) eigs, vecs = lobpcg(self.A, X, self.B, M, tol=1e-4, maxiter=40) else: eigh(self.A, self.B, eigvals_only=True, eigvals=(0, m - 1)) def time_sakurai(self, n, solver): m = 3 if solver == 'lobpcg': X = rand(n, m) eigs, vecs, resnh = lobpcg(self.A, X, self.B, tol=1e-6, maxiter=500, retResidualNormsHistory=1) else: eigh(self.A_dense, self.B_dense, eigvals_only=True, eigvals=(0, m - 1)) # Retain old benchmark results (remove this if changing the benchmark) time_mikota.version = "a1fb679758f7e5cf79d18cc4930afdff999fccc142fe7a4f63e73b39ab1f58bb" time_sakurai.version = "7c38d449924fb71f777bd408072ecc883b8b05e53a6544e97da3887fbc10b235"