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 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:
  • 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.xmask(char)

Like mask, but returns only a mask into only the correlated sector.

Parameters:
  • char (str) –

    Character to convert.

Returns:
  • NDArray[B]

    Mask of the space.

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:
  • 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.Space.xslice(char)

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

Parameters:
  • char (str) –

    Character to convert.

Returns:
  • _slice

    Slice of the space.

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:
  • 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 = 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]),
        )