Skip to content

Commit 68dcb74

Browse files
csskenekburns
andauthored
Reuse the LU transform when solving for left eigenvectors using the sparse solver (#277)
* Modify sparse evp solver to reuse the LU transform from the right evp solve. * Update code to remove blank lines, correct conj(sigma) in comment, and tidy up if condition. * Change solve_adjoint -> solve_H and evals_adjoint->left_evals for clarity. * Consolidate solve_H logic --------- Co-authored-by: Keaton J. Burns <[email protected]>
1 parent 19a7ada commit 68dcb74

File tree

3 files changed

+35
-9
lines changed

3 files changed

+35
-9
lines changed

dedalus/core/solvers.py

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -250,15 +250,13 @@ def solve_sparse(self, subproblem, N, target, rebuild_matrices=False, left=False
250250
# Solve as sparse general eigenvalue problem
251251
A = sp.L_min
252252
B = - sp.M_min
253-
# Solve for the right eigenvectors
254-
self.eigenvalues, pre_right_evecs = scipy_sparse_eigs(A=A, B=B, N=N, target=target, matsolver=self.matsolver, **kw)
255-
self.right_eigenvectors = self.eigenvectors = sp.pre_right @ pre_right_evecs
253+
# Solve for the right (and optionally left) eigenvectors
254+
eig_output = scipy_sparse_eigs(A=A, B=B, left=left, N=N, target=target, matsolver=self.matsolver, **kw)
255+
256256
if left:
257-
# Solve for the left eigenvectors
258257
# Note: this definition of "left eigenvectors" is consistent with the documentation for scipy.linalg.eig
259-
self.left_eigenvalues, pre_left_evecs = scipy_sparse_eigs(A=A.getH(), B=B.getH(),
260-
N=N, target=np.conjugate(target),
261-
matsolver=self.matsolver, **kw)
258+
self.eigenvalues, pre_right_evecs, self.left_eigenvalues, pre_left_evecs = eig_output
259+
self.right_eigenvectors = self.eigenvectors = sp.pre_right @ pre_right_evecs
262260
self.left_eigenvectors = sp.pre_left.H @ pre_left_evecs
263261
self.modified_left_eigenvectors = (sp.M_min @ sp.pre_right_pinv).H @ pre_left_evecs
264262
# Check that eigenvalues match
@@ -278,6 +276,9 @@ def solve_sparse(self, subproblem, N, target, rebuild_matrices=False, left=False
278276
norms = np.diag(pre_left_evecs.T.conj() @ sp.M_min @ pre_right_evecs)
279277
self.left_eigenvectors /= np.conj(norms)
280278
self.modified_left_eigenvectors /= np.conj(norms)
279+
else:
280+
self.eigenvalues, pre_right_evecs = eig_output
281+
self.right_eigenvectors = self.eigenvectors = sp.pre_right @ pre_right_evecs
281282

282283
def set_state(self, index, subsystem):
283284
"""

dedalus/libraries/matsolvers.py

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,9 @@ def __init__(self, matrix, solver=None):
2424
def solve(self, vector):
2525
pass
2626

27+
def solve_H(self,vector):
28+
raise NotImplementedError("%s has not implemented 'solve_H' method" % type(self))
29+
2730

2831
@add_solver
2932
class DummySolver(SolverBase):
@@ -145,6 +148,14 @@ def __init__(self, matrix, solver=None):
145148
def solve(self, vector):
146149
return self.LU.solve(vector, trans=self.trans)
147150

151+
def solve_H(self,vector):
152+
if self.trans == "N":
153+
return self.LU.solve(vector, trans="H")
154+
elif self.trans == "H":
155+
return self.LU.solve(vector)
156+
elif self.trans == "T":
157+
return np.conj(self.LU.solve(np.conj(vector)))
158+
148159

149160
@add_solver
150161
class SuperluNaturalFactorized(_SuperluFactorizedBase):

dedalus/tools/array.py

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -395,7 +395,7 @@ def drop_empty_rows(mat):
395395
return mat[nonempty_rows]
396396

397397

398-
def scipy_sparse_eigs(A, B, N, target, matsolver, **kw):
398+
def scipy_sparse_eigs(A, B, left, N, target, matsolver, **kw):
399399
"""
400400
Perform targeted eigenmode search using the scipy/ARPACK sparse solver
401401
for the reformulated generalized eigenvalue problem
@@ -410,6 +410,8 @@ def scipy_sparse_eigs(A, B, N, target, matsolver, **kw):
410410
Sparse matrices for generalized eigenvalue problem
411411
N : int
412412
Number of eigenmodes to return
413+
left: boolean
414+
Whether to solve for the left eigenvectors or not
413415
target : complex
414416
Target σ for eigenvalue search
415417
matsolver : matrix solver class
@@ -427,7 +429,19 @@ def matvec(x):
427429
evals, evecs = spla.eigs(D, k=N, which='LM', sigma=None, **kw)
428430
# Rectify eigenvalues
429431
evals = 1 / evals + target
430-
return evals, evecs
432+
if left:
433+
# Build sparse linear operator representing (A^H - conj(σ)B^H)^I B^H = C^-H B^H = left_D
434+
# Note: left_D is not equal to D^H
435+
def matvec_left(x):
436+
return solver.solve_H(B.H.dot(x))
437+
left_D = spla.LinearOperator(dtype=A.dtype, shape=A.shape, matvec=matvec_left)
438+
# Solve using scipy sparse algorithm
439+
left_evals, left_evecs = spla.eigs(left_D, k=N, which='LM', sigma=None, **kw)
440+
# Rectify left eigenvalues
441+
left_evals = 1 / left_evals + np.conj(target)
442+
return evals, evecs, left_evals, left_evecs
443+
else:
444+
return evals, evecs
431445

432446

433447
def interleave_matrices(matrices):

0 commit comments

Comments
 (0)