Path: blob/main/chemistry-simulations/aux_files/krylov/qchem/eigenvaluesolver.py
583 views
import numpy as np1import scipy.linalg as la23def eigen(H, S, *, cond_limit=1e12, eig_tol=1e-10, verbose=False):4"""5Solve H C = S C E in double precision with automatic stabilisation.67• Prints κ(S) before and, if projection happens, after stabilisation.8• Projects out tiny eigenvalues of S when κ(S) is too large.910Parameters11----------12H, S : (n, n) ndarray (real/complex)13The Hamiltonian (H) and overlap (S) matrices.14cond_limit : float15Trigger value for printing the projection warning.16eig_tol : float17Relative cut-off (s_val < eig_tol * s_val_max are removed).18verbose : bool19Print diagnostics.20"""21H = np.asarray(H, dtype=np.complex128)22S = np.asarray(S, dtype=np.complex128)2324# 1. Diagonalise S (assumed Hermitian)25s_vals, U = la.eigh(S)26# Drop negative round-off noise by clipping to zero27s_vals = np.clip(s_vals, 0.0, None)2829pos = s_vals[s_vals > 0]30if pos.size == 0:31raise np.linalg.LinAlgError("S is numerically singular: no positive eigenvalues.")3233kappa0 = pos.max() / pos.min()34if verbose:35print(f"κ(S) before projection : {kappa0:.3e}")3637# 2. Decide which eigenvalues to keep38s_max = pos.max()39# Define an absolute cutoff based on machine precision and the relative tolerance40abs_cut = max(np.finfo(s_vals.dtype).eps, eig_tol * s_max)4142keep = s_vals > abs_cut4344# Check if we discarded everything45if not np.any(keep):46raise np.linalg.LinAlgError(f"All eigenvalues discarded; S is singular (cutoff={abs_cut:.2e}).")4748# Conditionally print the projection message if the condition number was the trigger49if verbose and kappa0 > cond_limit:50n_removed = S.shape[0] - np.sum(keep)51if n_removed > 0:52print(f" projecting out {n_removed} eigenvalue(s) < {abs_cut:.3e}")5354lam_keep = s_vals[keep] # Now guaranteed to be strictly positive55U_keep = U[:, keep]5657# 3. Build the transformation matrix X = U_keep @ diag(1/sqrt(lam_keep))58# Broadcasting correctly scales each column of U_keep59X = U_keep / np.sqrt(lam_keep)6061# 4. Solve the reduced (and well-conditioned) standard eigenvalue problem62H_prime = (X.conj().T @ H) @ X63e_prime, C_prime = la.eigh(H_prime) # Use eigh since H_prime is Hermitian6465# 5. Back-transform eigenvectors to the original basis66C = X @ C_prime6768# 6. Report improved condition number if projection happened69if verbose and kappa0 > cond_limit:70kappa1 = lam_keep.max() / lam_keep.min()71print(f"κ(S) after projection : {kappa1:.3e}")7273return e_prime, C7475