Base classes for ebcc.eom
.
ebcc.eom.base.BaseOptions(nroots=5, e_tol=1e-06, max_iter=100, max_space=12, koopmans=False, left=False)
dataclass
Options for EOM calculations.
Parameters: |
|
---|
ebcc.eom.base.BaseEOM(ebcc, options=None, **kwargs)
Bases: ABC
Base class for equation-of-motion coupled cluster.
Initialise the EOM object.
Parameters: |
|
---|
Source code in ebcc/eom/base.py
def __init__(
self,
ebcc: BaseEBCC,
options: Optional[BaseOptions] = None,
**kwargs: Any,
) -> None:
"""Initialise the EOM object.
Args:
ebcc: Parent `EBCC` object.
options: Options for the EOM calculation.
**kwargs: Additional keyword arguments used to update `options`.
"""
# Options:
if options is None:
options = self.Options()
self.options = options
for key, val in kwargs.items():
setattr(self.options, key, val)
# Parameters:
self.ebcc = ebcc
self.space = ebcc.space
self.ansatz = ebcc.ansatz
self.log = ebcc.log
# Attributes:
self.converged = False
self.e: NDArray[T] = np.zeros((0,), dtype=types[float])
self.v: NDArray[T] = np.zeros((0, 0), dtype=types[float])
# Logging:
self.log.info(f"\n{ANSI.B}{ANSI.U}{self.name}{ANSI.R}")
self.log.debug(f"{ANSI.B}{'*' * len(self.name)}{ANSI.R}")
self.log.debug("")
self.log.info(f"{ANSI.B}Options{ANSI.R}:")
self.log.info(f" > nroots: {ANSI.y}{self.options.nroots}{ANSI.R}")
self.log.info(f" > e_tol: {ANSI.y}{self.options.e_tol}{ANSI.R}")
self.log.info(f" > max_iter: {ANSI.y}{self.options.max_iter}{ANSI.R}")
self.log.info(f" > max_space: {ANSI.y}{self.options.max_space}{ANSI.R}")
self.log.debug("")
ebcc.eom.base.BaseEOM.excitation_type: str
abstractmethod
property
Get the type of excitation.
ebcc.eom.base.BaseEOM.spin_type: str
property
Get the spin type.
ebcc.eom.base.BaseEOM.name: str
property
Get the name of the method.
ebcc.eom.base.BaseEOM.nmo: Any
property
Get the number of MOs.
ebcc.eom.base.BaseEOM.nocc: Any
property
Get the number of occupied MOs.
ebcc.eom.base.BaseEOM.nvir: Any
property
Get the number of virtual MOs.
ebcc.eom.base.BaseEOM.amplitudes_to_vector(amplitudes)
abstractmethod
Construct a vector containing all of the amplitudes used in the given ansatz.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
@abstractmethod
def amplitudes_to_vector(self, amplitudes: Namespace[SpinArrayType]) -> NDArray[T]:
"""Construct a vector containing all of the amplitudes used in the given ansatz.
Args:
amplitudes: Cluster amplitudes.
Returns:
Cluster amplitudes as a vector.
"""
pass
ebcc.eom.base.BaseEOM.vector_to_amplitudes(vector)
abstractmethod
Construct amplitudes from a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
@abstractmethod
def vector_to_amplitudes(self, vector: NDArray[T]) -> Namespace[SpinArrayType]:
"""Construct amplitudes from a vector.
Args:
vector: Cluster amplitudes as a vector.
Returns:
Cluster amplitudes.
"""
pass
ebcc.eom.base.BaseEOM.matvec(vector, eris=None, ints=None, left=False)
abstractmethod
Apply the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
@abstractmethod
def matvec(
self,
vector: NDArray[T],
eris: Optional[ERIsInputType] = None,
ints: Optional[Namespace[NDArray[T]]] = None,
left: bool = False,
) -> NDArray[T]:
"""Apply the Hamiltonian to a vector.
Args:
vector: State vector to apply the Hamiltonian to.
eris: Electronic repulsion integrals.
ints: Intermediate products.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Resulting vector.
"""
pass
ebcc.eom.base.BaseEOM.matvec_intermediates(eris=None, left=False)
abstractmethod
Get the intermediates for application of the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
@abstractmethod
def matvec_intermediates(
self, eris: Optional[ERIsInputType] = None, left: bool = False
) -> Namespace[NDArray[T]]:
"""Get the intermediates for application of the Hamiltonian to a vector.
Args:
eris: Electronic repulsion integrals.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Intermediate products.
"""
pass
ebcc.eom.base.BaseEOM.diag(eris=None)
abstractmethod
Get the diagonal of the Hamiltonian.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
@abstractmethod
def diag(self, eris: Optional[ERIsInputType] = None) -> NDArray[T]:
"""Get the diagonal of the Hamiltonian.
Args:
eris: Electronic repulsion integrals.
Returns:
Diagonal of the Hamiltonian.
"""
pass
ebcc.eom.base.BaseEOM.get_pick(guesses=None, real_system=True)
Get the function to pick the eigenvalues matching the criteria.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def get_pick(
self, guesses: Optional[NDArray[T]] = None, real_system: bool = True
) -> PickFunctionType:
"""Get the function to pick the eigenvalues matching the criteria.
Args:
guesses: Initial guesses for the roots.
real_system: Whether the system is real-valued.
Returns:
Function to pick the eigenvalues.
"""
if self.options.koopmans:
assert guesses is not None
def pick(
w: NDArray[T], v: NDArray[T], nroots: int, env: dict[str, Any]
) -> tuple[NDArray[T], NDArray[T], int]:
"""Pick the eigenvalues."""
x0 = to_numpy(lib.linalg_helper._gen_x0(env["v"], env["xs"]))
s = to_numpy(guesses).conj() @ x0.T
s = numpy.einsum("pi,pi->i", s.conj(), s)
arg = numpy.argsort(-s)[:nroots]
w, v, idx = lib.linalg_helper._eigs_cmplx2real(
to_numpy(w),
to_numpy(v),
arg,
real_system,
)
return w, v, idx
else:
def pick(
w: NDArray[T], v: NDArray[T], nroots: int, env: dict[str, Any]
) -> tuple[NDArray[T], NDArray[T], int]:
"""Pick the eigenvalues."""
real_idx = numpy.where(abs(numpy.imag(w)) < 1e-3)[0]
w, v, idx = lib.linalg_helper._eigs_cmplx2real(
to_numpy(w),
to_numpy(v),
real_idx,
real_system,
)
return w, v, idx
return pick
ebcc.eom.base.BaseEOM.get_guesses(diag=None)
Get the initial guesses vectors.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def get_guesses(self, diag: Optional[NDArray[T]] = None) -> list[NDArray[T]]:
"""Get the initial guesses vectors.
Args:
diag: Diagonal of the Hamiltonian.
Returns:
Initial guesses.
"""
if diag is None:
diag = self.diag()
arg = self._argsort_guesses(diag)
nroots = min(self.options.nroots, diag.size)
guesses: list[NDArray[T]] = []
for root, guess in enumerate(arg[:nroots]):
guesses.append(np.eye(1, diag.size, guess, dtype=types[float])[0])
return guesses
ebcc.eom.base.BaseEOM.callback(envs)
Callback function for the eigensolver.
Source code in ebcc/eom/base.py
def callback(self, envs: dict[str, Any]) -> None: # noqa: B027
"""Callback function for the eigensolver.""" # noqa: D401
pass
ebcc.eom.base.BaseEOM.davidson(eris=None, guesses=None)
Solve the EOM Hamiltonian using the Davidson solver.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def davidson(
self,
eris: Optional[ERIsInputType] = None,
guesses: Optional[list[NDArray[T]]] = None,
) -> NDArray[T]:
"""Solve the EOM Hamiltonian using the Davidson solver.
Args:
eris: Electronic repulsion integrals.
guesses: Initial guesses for the roots.
Returns:
Energies of the roots.
"""
timer = util.Timer()
# Get the ERIs:
eris = self.ebcc.get_eris(eris)
self.log.output(
"Solving for %s excitations using the Davidson solver.", self.excitation_type.upper()
)
# Get the matrix-vector products and the diagonal:
ints = self.matvec_intermediates(eris=eris, left=self.options.left)
matvecs = lambda vs: [
to_numpy(self.matvec(np.asarray(v), eris=eris, ints=ints, left=self.options.left))
for v in vs
]
diag = self.diag(eris=eris)
# Get the guesses:
if guesses is None:
guesses = self.get_guesses(diag=diag)
# Solve the EOM Hamiltonian:
nroots = min(len(guesses), self.options.nroots)
pick = self.get_pick(guesses=np.stack(guesses))
converged, e, v = lib.davidson_nosym1(
matvecs,
[to_numpy(g) for g in guesses],
to_numpy(diag),
tol=self.options.e_tol,
nroots=nroots,
pick=pick,
max_cycle=self.options.max_iter,
max_space=self.options.max_space,
callback=self.callback,
verbose=0,
)
e = np.asarray(e)
v = np.asarray(v)
# Check for convergence:
if all(converged):
self.log.debug("")
self.log.output(f"{ANSI.g}Converged.{ANSI.R}")
else:
self.log.debug("")
self.log.warning(
f"{ANSI.r}Failed to converge {sum(not c for c in converged)} roots.{ANSI.R}"
)
# Update attributes:
self.converged = converged
self.e = np.asarray(e, dtype=types[float])
self.v = np.transpose(np.asarray(v, dtype=types[float]))
self.log.debug("")
self.log.output(
f"{ANSI.B}{'Root':>4s} {'Energy':>16s} {'Weight':>13s} {'Conv.':>8s}{ANSI.R}"
)
for n, (en, vn, cn) in enumerate(zip(self.e, np.transpose(self.v), converged)):
r1n = self.vector_to_amplitudes(vn)["r1"]
qpwt = self._quasiparticle_weight(r1n)
self.log.output(
f"%4d %16.10f %13.5g {[ANSI.r, ANSI.g][bool(cn)]}%8s{ANSI.R}",
n,
np.ravel(en)[0],
qpwt,
bool(cn),
)
self.log.debug("")
self.log.debug("Time elapsed: %s", timer.format_time(timer()))
self.log.debug("")
return self.e
ebcc.eom.base.BaseIP_EOM(ebcc, options=None, **kwargs)
Bases: BaseEOM
Base class for ionisation-potential EOM-CC.
Initialise the EOM object.
Parameters: |
|
---|
Source code in ebcc/eom/base.py
def __init__(
self,
ebcc: BaseEBCC,
options: Optional[BaseOptions] = None,
**kwargs: Any,
) -> None:
"""Initialise the EOM object.
Args:
ebcc: Parent `EBCC` object.
options: Options for the EOM calculation.
**kwargs: Additional keyword arguments used to update `options`.
"""
# Options:
if options is None:
options = self.Options()
self.options = options
for key, val in kwargs.items():
setattr(self.options, key, val)
# Parameters:
self.ebcc = ebcc
self.space = ebcc.space
self.ansatz = ebcc.ansatz
self.log = ebcc.log
# Attributes:
self.converged = False
self.e: NDArray[T] = np.zeros((0,), dtype=types[float])
self.v: NDArray[T] = np.zeros((0, 0), dtype=types[float])
# Logging:
self.log.info(f"\n{ANSI.B}{ANSI.U}{self.name}{ANSI.R}")
self.log.debug(f"{ANSI.B}{'*' * len(self.name)}{ANSI.R}")
self.log.debug("")
self.log.info(f"{ANSI.B}Options{ANSI.R}:")
self.log.info(f" > nroots: {ANSI.y}{self.options.nroots}{ANSI.R}")
self.log.info(f" > e_tol: {ANSI.y}{self.options.e_tol}{ANSI.R}")
self.log.info(f" > max_iter: {ANSI.y}{self.options.max_iter}{ANSI.R}")
self.log.info(f" > max_space: {ANSI.y}{self.options.max_space}{ANSI.R}")
self.log.debug("")
ebcc.eom.base.BaseIP_EOM.excitation_type: str
property
Get the type of excitation.
ebcc.eom.base.BaseIP_EOM.matvec(vector, eris=None, ints=None, left=False)
Apply the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def matvec(
self,
vector: NDArray[T],
eris: Optional[ERIsInputType] = None,
ints: Optional[Namespace[NDArray[T]]] = None,
left: bool = False,
) -> NDArray[T]:
"""Apply the Hamiltonian to a vector.
Args:
vector: State vector to apply the Hamiltonian to.
eris: Electronic repulsion integrals.
ints: Intermediate products.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Resulting vector.
"""
if not ints:
ints = self.matvec_intermediates(eris=eris, left=left)
amplitudes = self.vector_to_amplitudes(vector)
func, kwargs = self.ebcc._load_function(
f"hbar_{'l' if left else ''}matvec_ip",
eris=eris,
ints=ints,
amplitudes=self.ebcc.amplitudes,
excitations=amplitudes,
)
res: Namespace[SpinArrayType] = func(**kwargs)
res = util.Namespace(**{key.rstrip("new"): val for key, val in res.items()})
return self.amplitudes_to_vector(res)
ebcc.eom.base.BaseIP_EOM.matvec_intermediates(eris=None, left=False)
Get the intermediates for application of the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def matvec_intermediates(
self, eris: Optional[ERIsInputType] = None, left: bool = False
) -> Namespace[NDArray[T]]:
"""Get the intermediates for application of the Hamiltonian to a vector.
Args:
eris: Electronic repulsion integrals.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Intermediate products.
"""
func, kwargs = self.ebcc._load_function(
f"hbar_{'l' if left else ''}matvec_ip_intermediates",
eris=eris,
amplitudes=self.ebcc.amplitudes,
)
res: Namespace[NDArray[T]] = util.Namespace(**func(**kwargs))
return res
ebcc.eom.base.BaseEA_EOM(ebcc, options=None, **kwargs)
Bases: BaseEOM
Base class for electron-affinity EOM-CC.
Initialise the EOM object.
Parameters: |
|
---|
Source code in ebcc/eom/base.py
def __init__(
self,
ebcc: BaseEBCC,
options: Optional[BaseOptions] = None,
**kwargs: Any,
) -> None:
"""Initialise the EOM object.
Args:
ebcc: Parent `EBCC` object.
options: Options for the EOM calculation.
**kwargs: Additional keyword arguments used to update `options`.
"""
# Options:
if options is None:
options = self.Options()
self.options = options
for key, val in kwargs.items():
setattr(self.options, key, val)
# Parameters:
self.ebcc = ebcc
self.space = ebcc.space
self.ansatz = ebcc.ansatz
self.log = ebcc.log
# Attributes:
self.converged = False
self.e: NDArray[T] = np.zeros((0,), dtype=types[float])
self.v: NDArray[T] = np.zeros((0, 0), dtype=types[float])
# Logging:
self.log.info(f"\n{ANSI.B}{ANSI.U}{self.name}{ANSI.R}")
self.log.debug(f"{ANSI.B}{'*' * len(self.name)}{ANSI.R}")
self.log.debug("")
self.log.info(f"{ANSI.B}Options{ANSI.R}:")
self.log.info(f" > nroots: {ANSI.y}{self.options.nroots}{ANSI.R}")
self.log.info(f" > e_tol: {ANSI.y}{self.options.e_tol}{ANSI.R}")
self.log.info(f" > max_iter: {ANSI.y}{self.options.max_iter}{ANSI.R}")
self.log.info(f" > max_space: {ANSI.y}{self.options.max_space}{ANSI.R}")
self.log.debug("")
ebcc.eom.base.BaseEA_EOM.excitation_type: str
property
Get the type of excitation.
ebcc.eom.base.BaseEA_EOM.matvec(vector, eris=None, ints=None, left=False)
Apply the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def matvec(
self,
vector: NDArray[T],
eris: Optional[ERIsInputType] = None,
ints: Optional[Namespace[NDArray[T]]] = None,
left: bool = False,
) -> NDArray[T]:
"""Apply the Hamiltonian to a vector.
Args:
vector: State vector to apply the Hamiltonian to.
eris: Electronic repulsion integrals.
ints: Intermediate products.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Resulting vector.
"""
if not ints:
ints = self.matvec_intermediates(eris=eris, left=left)
amplitudes = self.vector_to_amplitudes(vector)
func, kwargs = self.ebcc._load_function(
f"hbar_{'l' if left else ''}matvec_ea",
eris=eris,
ints=ints,
amplitudes=self.ebcc.amplitudes,
excitations=amplitudes,
)
res: Namespace[SpinArrayType] = func(**kwargs)
res = util.Namespace(**{key.rstrip("new"): val for key, val in res.items()})
return self.amplitudes_to_vector(res)
ebcc.eom.base.BaseEA_EOM.matvec_intermediates(eris=None, left=False)
Get the intermediates for application of the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def matvec_intermediates(
self, eris: Optional[ERIsInputType] = None, left: bool = False
) -> Namespace[NDArray[T]]:
"""Get the intermediates for application of the Hamiltonian to a vector.
Args:
eris: Electronic repulsion integrals.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Intermediate products.
"""
func, kwargs = self.ebcc._load_function(
f"hbar_{'l' if left else ''}matvec_ea_intermediates",
eris=eris,
amplitudes=self.ebcc.amplitudes,
)
res: Namespace[NDArray[T]] = util.Namespace(**func(**kwargs))
return res
ebcc.eom.base.BaseEE_EOM(ebcc, options=None, **kwargs)
Bases: BaseEOM
Base class for electron-electron EOM-CC.
Initialise the EOM object.
Parameters: |
|
---|
Source code in ebcc/eom/base.py
def __init__(
self,
ebcc: BaseEBCC,
options: Optional[BaseOptions] = None,
**kwargs: Any,
) -> None:
"""Initialise the EOM object.
Args:
ebcc: Parent `EBCC` object.
options: Options for the EOM calculation.
**kwargs: Additional keyword arguments used to update `options`.
"""
# Options:
if options is None:
options = self.Options()
self.options = options
for key, val in kwargs.items():
setattr(self.options, key, val)
# Parameters:
self.ebcc = ebcc
self.space = ebcc.space
self.ansatz = ebcc.ansatz
self.log = ebcc.log
# Attributes:
self.converged = False
self.e: NDArray[T] = np.zeros((0,), dtype=types[float])
self.v: NDArray[T] = np.zeros((0, 0), dtype=types[float])
# Logging:
self.log.info(f"\n{ANSI.B}{ANSI.U}{self.name}{ANSI.R}")
self.log.debug(f"{ANSI.B}{'*' * len(self.name)}{ANSI.R}")
self.log.debug("")
self.log.info(f"{ANSI.B}Options{ANSI.R}:")
self.log.info(f" > nroots: {ANSI.y}{self.options.nroots}{ANSI.R}")
self.log.info(f" > e_tol: {ANSI.y}{self.options.e_tol}{ANSI.R}")
self.log.info(f" > max_iter: {ANSI.y}{self.options.max_iter}{ANSI.R}")
self.log.info(f" > max_space: {ANSI.y}{self.options.max_space}{ANSI.R}")
self.log.debug("")
ebcc.eom.base.BaseEE_EOM.excitation_type: str
property
Get the type of excitation.
ebcc.eom.base.BaseEE_EOM.matvec(vector, eris=None, ints=None, left=False)
Apply the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def matvec(
self,
vector: NDArray[T],
eris: Optional[ERIsInputType] = None,
ints: Optional[Namespace[NDArray[T]]] = None,
left: bool = False,
) -> NDArray[T]:
"""Apply the Hamiltonian to a vector.
Args:
vector: State vector to apply the Hamiltonian to.
eris: Electronic repulsion integrals.
ints: Intermediate products.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Resulting vector.
"""
if not ints:
ints = self.matvec_intermediates(eris=eris, left=left)
amplitudes = self.vector_to_amplitudes(vector)
func, kwargs = self.ebcc._load_function(
f"hbar_{'l' if left else ''}matvec_ee",
eris=eris,
ints=ints,
amplitudes=self.ebcc.amplitudes,
excitations=amplitudes,
)
res: Namespace[SpinArrayType] = func(**kwargs)
res = util.Namespace(**{key.rstrip("new"): val for key, val in res.items()})
return self.amplitudes_to_vector(res)
ebcc.eom.base.BaseEE_EOM.matvec_intermediates(eris=None, left=False)
Get the intermediates for application of the Hamiltonian to a vector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/eom/base.py
def matvec_intermediates(
self, eris: Optional[ERIsInputType] = None, left: bool = False
) -> Namespace[NDArray[T]]:
"""Get the intermediates for application of the Hamiltonian to a vector.
Args:
eris: Electronic repulsion integrals.
left: Whether to apply the left-hand side of the Hamiltonian.
Returns:
Intermediate products.
"""
func, kwargs = self.ebcc._load_function(
f"hbar_{'l' if left else ''}matvec_ee_intermediates",
eris=eris,
amplitudes=self.ebcc.amplitudes,
)
res: Namespace[NDArray[T]] = util.Namespace(**func(**kwargs))
return res