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:
  • occupied (NDArray[B]) –

    Array containing boolean flags indicating whether or not each orbital is occupied.

  • frozen (NDArray[B]) –

    Array containing boolean flags indicating whether or not each orbital is frozen.

  • active (NDArray[B]) –

    Array containing boolean flags indicating whether or not each orbital is active.

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: NDArray[B] property

Get a boolean mask of occupied orbitals.

ebcc.ham.space.Space.virtual: NDArray[B] cached property

Get a boolean mask of virtual orbitals.

ebcc.ham.space.Space.nmo: int property

Get the number of orbitals.

ebcc.ham.space.Space.nocc: int cached property

Get the number of occupied orbitals.

ebcc.ham.space.Space.nvir: int cached property

Get the number of virtual orbitals.

ebcc.ham.space.Space.correlated: NDArray[B] cached property

Get a boolean mask of correlated orbitals.

ebcc.ham.space.Space.correlated_occupied: NDArray[B] cached property

Get a boolean mask of occupied correlated orbitals.

ebcc.ham.space.Space.correlated_virtual: NDArray[B] cached property

Get a boolean mask of virtual correlated orbitals.

ebcc.ham.space.Space.ncorr: int cached property

Get the number of correlated orbitals.

ebcc.ham.space.Space.ncocc: int cached property

Get the number of occupied correlated orbitals.

ebcc.ham.space.Space.ncvir: int cached property

Get the number of virtual correlated orbitals.

ebcc.ham.space.Space.inactive: NDArray[B] cached property

Get a boolean mask of inactive orbitals.

ebcc.ham.space.Space.inactive_occupied: NDArray[B] cached property

Get a boolean mask of occupied inactive orbitals.

ebcc.ham.space.Space.inactive_virtual: NDArray[B] cached property

Get a boolean mask of virtual inactive orbitals.

ebcc.ham.space.Space.ninact: int cached property

Get the number of inactive orbitals.

ebcc.ham.space.Space.niocc: int cached property

Get the number of occupied inactive orbitals.

ebcc.ham.space.Space.nivir: int cached property

Get the number of virtual inactive orbitals.

ebcc.ham.space.Space.frozen: NDArray[B] property

Get a boolean mask of frozen orbitals.

ebcc.ham.space.Space.frozen_occupied: NDArray[B] cached property

Get a boolean mask of occupied frozen orbitals.

ebcc.ham.space.Space.frozen_virtual: NDArray[B] cached property

Get a boolean mask of virtual frozen orbitals.

ebcc.ham.space.Space.nfroz: int cached property

Get the number of frozen orbitals.

ebcc.ham.space.Space.nfocc: int cached property

Get the number of occupied frozen orbitals.

ebcc.ham.space.Space.nfvir: int cached property

Get the number of virtual frozen orbitals.

ebcc.ham.space.Space.active: NDArray[B] property

Get a boolean mask of active orbitals.

ebcc.ham.space.Space.active_occupied: NDArray[B] cached property

Get a boolean mask of occupied active orbitals.

ebcc.ham.space.Space.active_virtual: NDArray[B] cached property

Get a boolean mask of virtual active orbitals.

ebcc.ham.space.Space.nact: int cached property

Get the number of active orbitals.

ebcc.ham.space.Space.naocc: int cached property

Get the number of occupied active orbitals.

ebcc.ham.space.Space.navir: int 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:
  • char (str) –

    Character to convert.

Returns:
  • int

    Size of the space.

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:
  • char (str) –

    Character to convert.

Returns:
  • NDArray[B]

    Mask of the space.

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:
  • char (str) –

    Character to convert.

Returns:
  • _slice

    Slice of the space.

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:
  • char (str) –

    Character to convert.

Returns:
  • NDArray[B]

    Mask of the space.

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:
  • char (str) –

    Character to convert.

Returns:
  • NDArray[B]

    Mask of the space.

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.oslice(char)

Like slice, but returns only a slice into only the occupied sector.

Parameters:
  • char (str) –

    Character to convert.

Returns:
  • _slice

    Slice of the space.

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:
  • char (str) –

    Character to convert.

Returns:
  • _slice

    Slice of the space.

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.construct_default_space(mf)

Construct a default space.

Parameters:
  • mf (SCF) –

    PySCF mean-field object.

Returns:
  • Union[RConstructSpaceReturnType, UConstructSpaceReturnType]

    The molecular orbital coefficients, the molecular orbital occupation numbers, and the

  • Union[RConstructSpaceReturnType, UConstructSpaceReturnType]

    default space.

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:
  • mf (SCF) –

    PySCF mean-field object.

  • occ_tol (Optional[float], default: 1e-05 ) –

    Threshold in the natural orbital occupation numbers.

  • occ_frac (Optional[float], default: None ) –

    Fraction of the natural orbital occupation numbers to be retained. Overrides occ_tol if both are specified.

  • amplitudes (Optional[Namespace[SpinArrayType]], default: None ) –

    Cluster amplitudes. If provided, use these amplitudes when calculating the MP2 1RDM.

Returns:
  • Union[RConstructSpaceReturnType, UConstructSpaceReturnType]

    The natural orbital coefficients, the natural orbital occupation numbers, and the frozen

  • Union[RConstructSpaceReturnType, UConstructSpaceReturnType]

    natural orbital space.

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 = cast(int, np.sum(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 = cast(int, np.sum(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]),
        )