Space definition.
ebcc.ham.space.Space(occupied, frozen, active)
Space class.
.. code-block:: none
─┬─ ┌──────────┐
│ │ frozen │
│ ├──────────┤ ─┬─
virtual │ │ active │ │
│ ├──────────┤ │ correlated
│ │ inactive │ │
─┼─ ├══════════┤ ─┼─
│ │ inactive │ │
│ ├──────────┤ │ correlated
occupied │ │ active │ │
│ ├──────────┤ ─┴─
│ │ frozen │
─┴─ └──────────┘
Initialise the space.
Parameters: |
|
---|
Source code in ebcc/ham/space.py
def __init__(
self,
occupied: NDArray[B],
frozen: NDArray[B],
active: NDArray[B],
) -> None:
"""Initialise the space.
Args:
occupied: Array containing boolean flags indicating whether or not each orbital is
occupied.
frozen: Array containing boolean flags indicating whether or not each orbital is frozen.
active: Array containing boolean flags indicating whether or not each orbital is active.
"""
self._occupied = np.asarray(occupied, dtype=np.bool_)
self._frozen = np.asarray(frozen, dtype=np.bool_)
self._active = np.asarray(active, dtype=np.bool_)
# Checks:
if not (self._occupied.size == self._frozen.size == self._active.size):
raise ValueError("The sizes of the space arrays must match.")
if np.any(np.bitwise_and(self._frozen, self._active)):
raise ValueError("Frozen and active orbitals must be mutually exclusive.")
ebcc.ham.space.Space.occupied
property
Get a boolean mask of occupied orbitals.
ebcc.ham.space.Space.virtual
cached
property
Get a boolean mask of virtual orbitals.
ebcc.ham.space.Space.nmo
property
Get the number of orbitals.
ebcc.ham.space.Space.nocc
cached
property
Get the number of occupied orbitals.
ebcc.ham.space.Space.nvir
cached
property
Get the number of virtual orbitals.
ebcc.ham.space.Space.correlated
cached
property
Get a boolean mask of correlated orbitals.
ebcc.ham.space.Space.correlated_occupied
cached
property
Get a boolean mask of occupied correlated orbitals.
ebcc.ham.space.Space.correlated_virtual
cached
property
Get a boolean mask of virtual correlated orbitals.
ebcc.ham.space.Space.ncorr
cached
property
Get the number of correlated orbitals.
ebcc.ham.space.Space.ncocc
cached
property
Get the number of occupied correlated orbitals.
ebcc.ham.space.Space.ncvir
cached
property
Get the number of virtual correlated orbitals.
ebcc.ham.space.Space.inactive
cached
property
Get a boolean mask of inactive orbitals.
ebcc.ham.space.Space.inactive_occupied
cached
property
Get a boolean mask of occupied inactive orbitals.
ebcc.ham.space.Space.inactive_virtual
cached
property
Get a boolean mask of virtual inactive orbitals.
ebcc.ham.space.Space.ninact
cached
property
Get the number of inactive orbitals.
ebcc.ham.space.Space.niocc
cached
property
Get the number of occupied inactive orbitals.
ebcc.ham.space.Space.nivir
cached
property
Get the number of virtual inactive orbitals.
ebcc.ham.space.Space.frozen
property
Get a boolean mask of frozen orbitals.
ebcc.ham.space.Space.frozen_occupied
cached
property
Get a boolean mask of occupied frozen orbitals.
ebcc.ham.space.Space.frozen_virtual
cached
property
Get a boolean mask of virtual frozen orbitals.
ebcc.ham.space.Space.nfroz
cached
property
Get the number of frozen orbitals.
ebcc.ham.space.Space.nfocc
cached
property
Get the number of occupied frozen orbitals.
ebcc.ham.space.Space.nfvir
cached
property
Get the number of virtual frozen orbitals.
ebcc.ham.space.Space.active
property
Get a boolean mask of active orbitals.
ebcc.ham.space.Space.active_occupied
cached
property
Get a boolean mask of occupied active orbitals.
ebcc.ham.space.Space.active_virtual
cached
property
Get a boolean mask of virtual active orbitals.
ebcc.ham.space.Space.nact
cached
property
Get the number of active orbitals.
ebcc.ham.space.Space.naocc
cached
property
Get the number of occupied active orbitals.
ebcc.ham.space.Space.navir
cached
property
Get the number of virtual active orbitals.
ebcc.ham.space.Space.__repr__()
Get a string representation of the space.
Source code in ebcc/ham/space.py
def __repr__(self) -> str:
"""Get a string representation of the space."""
out = "(%do, %dv)" % (self.nocc, self.nvir)
parts = []
if self.nfroz:
parts.append("(%do, %dv) frozen" % (self.nfocc, self.nfvir))
if self.nact:
parts.append("(%do, %dv) active" % (self.naocc, self.navir))
if len(parts):
out += " [" + ", ".join(parts) + "]"
return out
ebcc.ham.space.Space.size(char)
Convert a character corresponding to a space to the size of that space.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def size(self, char: str) -> int:
"""Convert a character corresponding to a space to the size of that space.
Args:
char: Character to convert.
Returns:
Size of the space.
"""
return {
"x": self.ncorr,
"o": self.ncocc,
"O": self.naocc,
"i": self.niocc,
"v": self.ncvir,
"V": self.navir,
"a": self.nivir,
}[char]
ebcc.ham.space.Space.mask(char)
Convert a character corresponding to a space to a mask of that space.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def mask(self, char: str) -> NDArray[B]:
"""Convert a character corresponding to a space to a mask of that space.
Args:
char: Character to convert.
Returns:
Mask of the space.
"""
return {
"x": self.correlated,
"o": self.correlated_occupied,
"O": self.active_occupied,
"i": self.inactive_occupied,
"v": self.correlated_virtual,
"V": self.active_virtual,
"a": self.inactive_virtual,
}[char]
ebcc.ham.space.Space.slice(char)
cached
Convert a character corresponding to a space to a slice of that space.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
@functools.lru_cache(maxsize=128) # noqa: B019
def slice(self, char: str) -> _slice:
"""Convert a character corresponding to a space to a slice of that space.
Args:
char: Character to convert.
Returns:
Slice of the space.
"""
# Check that the respective mask is contiguous
mask = self.mask(char)
first = np.argmax(mask)
size = self.size(char)
if not np.all(mask[first : first + size]):
raise ValueError(
f"Space '{char}' is not contiguous. In order to slice into this space, "
"the `mask` method must be used. If you see this error internally, it is "
"likely that you have constructed a disjoint space. Please reorder the "
"orbitals in the space."
)
return slice(first, first + size)
ebcc.ham.space.Space.omask(char)
Like mask
, but returns only a mask into only the occupied sector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def omask(self, char: str) -> NDArray[B]:
"""Like `mask`, but returns only a mask into only the occupied sector.
Args:
char: Character to convert.
Returns:
Mask of the space.
"""
return self.mask(char)[self.occupied]
ebcc.ham.space.Space.vmask(char)
Like mask
, but returns only a mask into only the virtual sector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def vmask(self, char: str) -> NDArray[B]:
"""Like `mask`, but returns only a mask into only the virtual sector.
Args:
char: Character to convert.
Returns:
Mask of the space.
"""
return self.mask(char)[self.virtual]
ebcc.ham.space.Space.xmask(char)
Like mask
, but returns only a mask into only the correlated sector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def xmask(self, char: str) -> NDArray[B]:
"""Like `mask`, but returns only a mask into only the correlated sector.
Args:
char: Character to convert.
Returns:
Mask of the space.
"""
return self.mask(char)[self.correlated]
ebcc.ham.space.Space.oslice(char)
Like slice
, but returns only a slice into only the occupied sector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def oslice(self, char: str) -> _slice:
"""Like `slice`, but returns only a slice into only the occupied sector.
Args:
char: Character to convert.
Returns:
Slice of the space.
"""
s = self.slice(char)
nocc = self.nocc
return slice(s.start, min(s.stop, nocc))
ebcc.ham.space.Space.vslice(char)
Like slice
, but returns only a slice into only the virtual sector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def vslice(self, char: str) -> _slice:
"""Like `slice`, but returns only a slice into only the virtual sector.
Args:
char: Character to convert.
Returns:
Slice of the space.
"""
s = self.slice(char)
nocc = self.nocc
return slice(max(s.start, nocc) - nocc, s.stop - nocc)
ebcc.ham.space.Space.xslice(char)
Like slice
, but returns only a slice into only the correlated sector.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def xslice(self, char: str) -> _slice:
"""Like `slice`, but returns only a slice into only the correlated sector.
Args:
char: Character to convert.
Returns:
Slice of the space.
"""
s = self.slice(char)
nfocc = self.nfocc
return slice(s.start - nfocc, s.stop - nfocc)
ebcc.ham.space.construct_default_space(mf)
Construct a default space.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def construct_default_space(mf: SCF) -> Union[RConstructSpaceReturnType, UConstructSpaceReturnType]:
"""Construct a default space.
Args:
mf: PySCF mean-field object.
Returns:
The molecular orbital coefficients, the molecular orbital occupation numbers, and the
default space.
"""
def _construct(mo_occ: NDArray[T]) -> Space:
"""Build the default space."""
frozen = np.zeros(mo_occ.shape, dtype=np.bool_)
active = np.zeros(mo_occ.shape, dtype=np.bool_)
space = Space(
occupied=mo_occ > 0,
frozen=frozen,
active=active,
)
return space
# Construct the default space
if mf.mo_occ.ndim == 2:
space_a = _construct(mf.mo_occ[0])
space_b = _construct(mf.mo_occ[1])
return mf.mo_coeff, mf.mo_occ, (space_a, space_b)
else:
return mf.mo_coeff, mf.mo_occ, _construct(mf.mo_occ)
ebcc.ham.space.construct_fno_space(mf, occ_tol=1e-05, occ_frac=None, amplitudes=None)
Construct a frozen natural orbital space.
Parameters: |
|
---|
Returns: |
|
---|
Source code in ebcc/ham/space.py
def construct_fno_space(
mf: SCF,
occ_tol: Optional[float] = 1e-5,
occ_frac: Optional[float] = None,
amplitudes: Optional[Namespace[SpinArrayType]] = None,
) -> Union[RConstructSpaceReturnType, UConstructSpaceReturnType]:
"""Construct a frozen natural orbital space.
Args:
mf: PySCF mean-field object.
occ_tol: Threshold in the natural orbital occupation numbers.
occ_frac: Fraction of the natural orbital occupation numbers to be retained. Overrides
`occ_tol` if both are specified.
amplitudes: Cluster amplitudes. If provided, use these amplitudes when calculating the MP2
1RDM.
Returns:
The natural orbital coefficients, the natural orbital occupation numbers, and the frozen
natural orbital space.
"""
# Get the MP2 1RDM
solver = pyscf.mp.mp2.MP2(mf)
dm1: NDArray[T]
if not amplitudes:
solver.kernel()
dm1 = np.asarray(solver.make_rdm1(), dtype=types[float])
else:
if isinstance(amplitudes.t2, util.Namespace):
t2 = (amplitudes.t2.aaaa, amplitudes.t2.abab, amplitudes.t2.bbbb)
dm1 = np.asarray(solver.make_rdm1(t2=t2), dtype=types[float])
else:
dm1 = np.asarray(solver.make_rdm1(t2=amplitudes.t2), dtype=types[float])
# def _construct(dm1, mo_energy, mo_coeff, mo_occ):
def _construct(
dm1: NDArray[T],
mo_energy: NDArray[T],
mo_coeff: NDArray[T],
mo_occ: NDArray[T],
) -> RConstructSpaceReturnType:
# Get the number of occupied orbitals
nocc = np.count_nonzero(mo_occ > 0)
# Calculate the natural orbitals
n, c = np.linalg.eigh(dm1[nocc:, nocc:])
n, c = n[::-1], c[:, ::-1]
# Truncate the natural orbitals
if occ_frac is None:
active_vir = n > occ_tol
else:
active_vir = np.cumsum(n / np.sum(n)) <= occ_frac
num_active_vir = np.count_nonzero(active_vir)
# Canonicalise the natural orbitals
fock_vv = np.diag(mo_energy[nocc:])
fock_vv = util.einsum("ab,au,bv->uv", fock_vv, c, c)
_, c_can = np.linalg.eigh(fock_vv[active_vir][:, active_vir])
# Transform the natural orbitals
no_coeff_avir = util.einsum(
"pi,iq,qj->pj", mo_coeff[:, nocc:], c[:, :num_active_vir], c_can
)
no_coeff_fvir = mo_coeff[:, nocc:] @ c[:, num_active_vir:]
no_coeff_occ = mo_coeff[:, :nocc]
no_coeff = np.concatenate((no_coeff_occ, no_coeff_avir, no_coeff_fvir), axis=1)
# Build the natural orbital space
active = np.zeros(mo_occ.shape, dtype=np.bool_)
frozen = np.concatenate(
(
np.zeros((nocc + num_active_vir,), dtype=np.bool_),
np.ones((mo_occ.size - nocc - num_active_vir,), dtype=np.bool_),
)
)
no_space = Space(
occupied=mo_occ > 0,
frozen=frozen,
active=active,
)
return no_coeff, mo_occ, no_space
# Construct the natural orbitals
if mf.mo_occ.ndim == 2:
coeff_a, occ_a, space_a = _construct(
np.asarray(dm1[0], dtype=types[float]),
np.asarray(mf.mo_energy[0], dtype=types[float]),
np.asarray(mf.mo_coeff[0], dtype=types[float]),
np.asarray(mf.mo_occ[0], dtype=types[float]),
)
coeff_b, occ_b, space_b = _construct(
np.asarray(dm1[1], dtype=types[float]),
np.asarray(mf.mo_energy[1], dtype=types[float]),
np.asarray(mf.mo_coeff[1], dtype=types[float]),
np.asarray(mf.mo_occ[1], dtype=types[float]),
)
return (coeff_a, coeff_b), (occ_a, occ_b), (space_a, space_b)
else:
return _construct(
np.asarray(dm1, dtype=types[float]),
np.asarray(mf.mo_energy, dtype=types[float]),
np.asarray(mf.mo_coeff, dtype=types[float]),
np.asarray(mf.mo_occ, dtype=types[float]),
)