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=bool)
    self._frozen = np.asarray(frozen, dtype=bool)
    self._active = np.asarray(active, dtype=bool)

    # Checks:
    if not (self._occupied.size == self._frozen.size == self._active.size):
        raise ValueError("The sizes of the space arrays must match.")
    if (self._frozen & self._active).any():
        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] 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 property

Get the number of occupied orbitals.

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

Get the number of virtual orbitals.

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

Get a boolean mask of correlated orbitals.

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

Get a boolean mask of occupied correlated orbitals.

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

Get a boolean mask of virtual correlated orbitals.

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

Get the number of correlated orbitals.

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

Get the number of occupied correlated orbitals.

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

Get the number of virtual correlated orbitals.

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

Get a boolean mask of inactive orbitals.

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

Get a boolean mask of occupied inactive orbitals.

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

Get a boolean mask of virtual inactive orbitals.

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

Get the number of inactive orbitals.

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

Get the number of occupied inactive orbitals.

ebcc.ham.space.Space.nivir: int 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] property

Get a boolean mask of occupied frozen orbitals.

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

Get a boolean mask of virtual frozen orbitals.

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

Get the number of frozen orbitals.

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

Get the number of occupied frozen orbitals.

ebcc.ham.space.Space.nfvir: int 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] property

Get a boolean mask of occupied active orbitals.

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

Get a boolean mask of virtual active orbitals.

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

Get the number of active orbitals.

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

Get the number of occupied active orbitals.

ebcc.ham.space.Space.navir: int 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.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.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=bool)
        active = np.zeros(mo_occ.shape, dtype=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 = MP2(mf)
    if not amplitudes:
        solver.kernel()
        dm1 = solver.make_rdm1().astype(types[float])
    else:
        if isinstance(amplitudes.t2, util.Namespace):
            t2 = (amplitudes.t2.aaaa, amplitudes.t2.abab, amplitudes.t2.bbbb)
            dm1 = solver.make_rdm1(t2=t2).astype(types[float])
        else:
            dm1 = solver.make_rdm1(t2=amplitudes.t2).astype(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 = (mo_occ > 0).astype(int).sum()

        # 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 / n.sum()) <= occ_frac
        num_active_vir = active_vir.astype(int).sum()

        # 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=bool)
        frozen = np.zeros(mo_occ.shape, dtype=bool)
        frozen[nocc + num_active_vir :] = True
        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(
            dm1[0].astype(types[float]),
            mf.mo_energy[0].astype(types[float]),
            mf.mo_coeff[0].astype(types[float]),
            mf.mo_occ[0].astype(types[float]),
        )
        coeff_b, occ_b, space_b = _construct(
            dm1[1].astype(types[float]),
            mf.mo_energy[1].astype(types[float]),
            mf.mo_coeff[1].astype(types[float]),
            mf.mo_occ[1].astype(types[float]),
        )
        return (coeff_a, coeff_b), (occ_a, occ_b), (space_a, space_b)
    else:
        return _construct(
            dm1.astype(types[float]),
            mf.mo_energy.astype(types[float]),
            mf.mo_coeff.astype(types[float]),
            mf.mo_occ.astype(types[float]),
        )