Unrestricted equation-of-motion coupled cluster.

ebcc.eom.ueom.UEOM(ebcc, options=None, **kwargs)

Bases: BaseEOM

Unrestricted equation-of-motion coupled cluster.

Initialise the EOM object.

Parameters:
  • ebcc (BaseEBCC) –

    Parent EBCC object.

  • options (Optional[BaseOptions], default: None ) –

    Options for the EOM calculation.

  • **kwargs (Any, default: {} ) –

    Additional keyword arguments used to update options.

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.ueom.IP_UEOM(ebcc, options=None, **kwargs)

Bases: UEOM, BaseIP_EOM

Unrestricted ionisation potential equation-of-motion coupled cluster.

Initialise the EOM object.

Parameters:
  • ebcc (BaseEBCC) –

    Parent EBCC object.

  • options (Optional[BaseOptions], default: None ) –

    Options for the EOM calculation.

  • **kwargs (Any, default: {} ) –

    Additional keyword arguments used to update options.

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.ueom.IP_UEOM.diag(eris=None)

Get the diagonal of the Hamiltonian.

Parameters:
  • eris (Optional[ERIsInputType], default: None ) –

    Electronic repulsion integrals.

Returns:
  • NDArray[T]

    Diagonal of the Hamiltonian.

Source code in ebcc/eom/ueom.py
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.
    """
    parts: Namespace[SpinArrayType] = util.Namespace()

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ip"
    ):
        spin_part: SpinArrayType = util.Namespace()
        for comb in util.generate_spin_combinations(n, excited=True):
            spin_part[comb] = self.ebcc.energy_sum(key, comb)
        parts[name] = spin_part

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ip"):
        raise util.ModelNotImplemented

    return self.amplitudes_to_vector(parts)

ebcc.eom.ueom.IP_UEOM.amplitudes_to_vector(amplitudes)

Construct a vector containing all of the IP-EOM amplitudes.

Parameters:
  • amplitudes (Namespace[SpinArrayType]) –

    IP-EOM amplitudes.

Returns:
  • NDArray[T]

    IP-EOM amplitudes as a vector.

Source code in ebcc/eom/ueom.py
def amplitudes_to_vector(self, amplitudes: Namespace[SpinArrayType]) -> NDArray[T]:
    """Construct a vector containing all of the IP-EOM amplitudes.

    Args:
        amplitudes: IP-EOM amplitudes.

    Returns:
        IP-EOM amplitudes as a vector.
    """
    vectors = []

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ip"
    ):
        for spin in util.generate_spin_combinations(n, excited=True, unique=True):
            vn = amplitudes[name][spin]
            subscript, _ = util.combine_subscripts(key, spin)
            vectors.append(np.ravel(util.compress_axes(subscript, vn)))

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ip"):
        raise util.ModelNotImplemented

    for name, key, nf, nb in self.ansatz.coupling_cluster_ranks(
        spin_type=self.spin_type, which="ip"
    ):
        raise util.ModelNotImplemented

    return np.concatenate(vectors)

ebcc.eom.ueom.IP_UEOM.vector_to_amplitudes(vector)

Construct a namespace of IP-EOM amplitudes from a vector.

Parameters:
  • vector (NDArray[T]) –

    IP-EOM amplitudes as a vector.

Returns:
  • Namespace[SpinArrayType]

    IP-EOM amplitudes.

Source code in ebcc/eom/ueom.py
def vector_to_amplitudes(self, vector: NDArray[T]) -> Namespace[SpinArrayType]:
    """Construct a namespace of IP-EOM amplitudes from a vector.

    Args:
        vector: IP-EOM amplitudes as a vector.

    Returns:
        IP-EOM amplitudes.
    """
    amplitudes: Namespace[SpinArrayType] = util.Namespace()
    i0 = 0
    sizes: dict[tuple[str, ...], int] = {
        (o, s): self.space[i].size(o) for o in "ovOVia" for i, s in enumerate("ab")
    }

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ip"
    ):
        amp: SpinArrayType = util.Namespace()
        for spin in util.generate_spin_combinations(n, excited=True, unique=True):
            subscript, csizes = util.combine_subscripts(key, spin, sizes=sizes)
            size = util.get_compressed_size(subscript, **csizes)
            shape = tuple(self.space["ab".index(s)].size(k) for s, k in zip(spin, key))
            vn_tril = vector[i0 : i0 + size]
            factor = max(
                spin[:n].count(s) for s in set(spin[:n])
            )  # FIXME why? untested for n > 2
            vn = util.decompress_axes(subscript, vn_tril, shape=shape) / factor
            amp[spin] = vn
            i0 += size

        amplitudes[name] = amp

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ip"):
        raise util.ModelNotImplemented

    for name, key, nf, nb in self.ansatz.coupling_cluster_ranks(
        spin_type=self.spin_type, which="ip"
    ):
        raise util.ModelNotImplemented

    assert i0 == len(vector)

    return amplitudes

ebcc.eom.ueom.EA_UEOM(ebcc, options=None, **kwargs)

Bases: UEOM, BaseEA_EOM

Unrestricted electron affinity equation-of-motion coupled cluster.

Initialise the EOM object.

Parameters:
  • ebcc (BaseEBCC) –

    Parent EBCC object.

  • options (Optional[BaseOptions], default: None ) –

    Options for the EOM calculation.

  • **kwargs (Any, default: {} ) –

    Additional keyword arguments used to update options.

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.ueom.EA_UEOM.diag(eris=None)

Get the diagonal of the Hamiltonian.

Parameters:
  • eris (Optional[ERIsInputType], default: None ) –

    Electronic repulsion integrals.

Returns:
  • NDArray[T]

    Diagonal of the Hamiltonian.

Source code in ebcc/eom/ueom.py
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.
    """
    parts: Namespace[SpinArrayType] = util.Namespace()

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ea"
    ):
        spin_part: SpinArrayType = util.Namespace()
        for comb in util.generate_spin_combinations(n, excited=True):
            spin_part[comb] = -self.ebcc.energy_sum(key, comb)
        parts[name] = spin_part

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ea"):
        raise util.ModelNotImplemented

    return self.amplitudes_to_vector(parts)

ebcc.eom.ueom.EA_UEOM.amplitudes_to_vector(amplitudes)

Construct a vector containing all of the EA-EOM amplitudes.

Parameters:
  • amplitudes (Namespace[SpinArrayType]) –

    EA-EOM amplitudes.

Returns:
  • NDArray[T]

    EA-EOM amplitudes as a vector.

Source code in ebcc/eom/ueom.py
def amplitudes_to_vector(self, amplitudes: Namespace[SpinArrayType]) -> NDArray[T]:
    """Construct a vector containing all of the EA-EOM amplitudes.

    Args:
        amplitudes: EA-EOM amplitudes.

    Returns:
        EA-EOM amplitudes as a vector.
    """
    vectors = []

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ea"
    ):
        for spin in util.generate_spin_combinations(n, excited=True, unique=True):
            vn = amplitudes[name][spin]
            subscript, _ = util.combine_subscripts(key, spin)
            vectors.append(np.ravel(util.compress_axes(subscript, vn)))

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ea"):
        raise util.ModelNotImplemented

    for name, key, nf, nb in self.ansatz.coupling_cluster_ranks(
        spin_type=self.spin_type, which="ea"
    ):
        raise util.ModelNotImplemented

    return np.concatenate(vectors)

ebcc.eom.ueom.EA_UEOM.vector_to_amplitudes(vector)

Construct a namespace of EA-EOM amplitudes from a vector.

Parameters:
  • vector (NDArray[T]) –

    EA-EOM amplitudes as a vector.

Returns:
  • Namespace[SpinArrayType]

    EA-EOM amplitudes.

Source code in ebcc/eom/ueom.py
def vector_to_amplitudes(self, vector: NDArray[T]) -> Namespace[SpinArrayType]:
    """Construct a namespace of EA-EOM amplitudes from a vector.

    Args:
        vector: EA-EOM amplitudes as a vector.

    Returns:
        EA-EOM amplitudes.
    """
    amplitudes: Namespace[SpinArrayType] = util.Namespace()
    i0 = 0
    sizes: dict[tuple[str, ...], int] = {
        (o, s): self.space[i].size(o) for o in "ovOVia" for i, s in enumerate("ab")
    }

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ea"
    ):
        amp: SpinArrayType = util.Namespace()
        for spin in util.generate_spin_combinations(n, excited=True, unique=True):
            subscript, csizes = util.combine_subscripts(key, spin, sizes=sizes)
            size = util.get_compressed_size(subscript, **csizes)
            shape = tuple(self.space["ab".index(s)].size(k) for s, k in zip(spin, key))
            vn_tril = vector[i0 : i0 + size]
            factor = max(
                spin[:n].count(s) for s in set(spin[:n])
            )  # FIXME why? untested for n > 2
            vn = util.decompress_axes(subscript, vn_tril, shape=shape) / factor
            amp[spin] = vn
            i0 += size

        amplitudes[name] = amp

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ea"):
        raise util.ModelNotImplemented

    for name, key, nf, nb in self.ansatz.coupling_cluster_ranks(
        spin_type=self.spin_type, which="ea"
    ):
        raise util.ModelNotImplemented

    assert i0 == len(vector)

    return amplitudes

ebcc.eom.ueom.EE_UEOM(ebcc, options=None, **kwargs)

Bases: UEOM, BaseEE_EOM

Unrestricted electron-electron equation-of-motion coupled cluster.

Initialise the EOM object.

Parameters:
  • ebcc (BaseEBCC) –

    Parent EBCC object.

  • options (Optional[BaseOptions], default: None ) –

    Options for the EOM calculation.

  • **kwargs (Any, default: {} ) –

    Additional keyword arguments used to update options.

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.ueom.EE_UEOM.diag(eris=None)

Get the diagonal of the Hamiltonian.

Parameters:
  • eris (Optional[ERIsInputType], default: None ) –

    Electronic repulsion integrals.

Returns:
  • NDArray[T]

    Diagonal of the Hamiltonian.

Source code in ebcc/eom/ueom.py
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.
    """
    parts: Namespace[SpinArrayType] = util.Namespace()

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ee"
    ):
        spin_part: SpinArrayType = util.Namespace()
        for comb in util.generate_spin_combinations(n, unique=True):
            spin_part[comb] = self.ebcc.energy_sum(key, comb)
        parts[name] = spin_part

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ee"):
        raise util.ModelNotImplemented

    return self.amplitudes_to_vector(parts)

ebcc.eom.ueom.EE_UEOM.amplitudes_to_vector(amplitudes)

Construct a vector containing all of the EE-EOM amplitudes.

Parameters:
  • amplitudes (Namespace[SpinArrayType]) –

    EE-EOM amplitudes.

Returns:
  • NDArray[T]

    EE-EOM amplitudes as a vector.

Source code in ebcc/eom/ueom.py
def amplitudes_to_vector(self, amplitudes: Namespace[SpinArrayType]) -> NDArray[T]:
    """Construct a vector containing all of the EE-EOM amplitudes.

    Args:
        amplitudes: EE-EOM amplitudes.

    Returns:
        EE-EOM amplitudes as a vector.
    """
    vectors = []

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ee"
    ):
        for spin in util.generate_spin_combinations(n, unique=True):
            vn = amplitudes[name][spin]
            subscript, _ = util.combine_subscripts(key, spin)
            vectors.append(np.ravel(util.compress_axes(subscript, vn)))

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ee"):
        raise util.ModelNotImplemented

    for name, key, nf, nb in self.ansatz.coupling_cluster_ranks(
        spin_type=self.spin_type, which="ee"
    ):
        raise util.ModelNotImplemented

    return np.concatenate(vectors)

ebcc.eom.ueom.EE_UEOM.vector_to_amplitudes(vector)

Construct a namespace of EE-EOM amplitudes from a vector.

Parameters:
  • vector (NDArray[T]) –

    EE-EOM amplitudes as a vector.

Returns:
  • Namespace[SpinArrayType]

    EE-EOM amplitudes.

Source code in ebcc/eom/ueom.py
def vector_to_amplitudes(self, vector: NDArray[T]) -> Namespace[SpinArrayType]:
    """Construct a namespace of EE-EOM amplitudes from a vector.

    Args:
        vector: EE-EOM amplitudes as a vector.

    Returns:
        EE-EOM amplitudes.
    """
    amplitudes: Namespace[SpinArrayType] = util.Namespace()
    i0 = 0
    sizes: dict[tuple[str, ...], int] = {
        (o, s): self.space[i].size(o) for o in "ovOVia" for i, s in enumerate("ab")
    }

    for name, key, n in self.ansatz.fermionic_cluster_ranks(
        spin_type=self.spin_type, which="ee"
    ):
        amp: SpinArrayType = util.Namespace()
        for spin in util.generate_spin_combinations(n, unique=True):
            subscript, csizes = util.combine_subscripts(key, spin, sizes=sizes)
            size = util.get_compressed_size(subscript, **csizes)
            shape = tuple(self.space["ab".index(s)].size(k) for s, k in zip(spin, key))
            vn_tril = vector[i0 : i0 + size]
            factor = max(
                spin[:n].count(s) for s in set(spin[:n])
            )  # FIXME why? untested for n > 2
            vn = util.decompress_axes(subscript, vn_tril, shape=shape) / factor
            amp[spin] = vn
            i0 += size

        amplitudes[name] = amp

    for name, key, n in self.ansatz.bosonic_cluster_ranks(spin_type=self.spin_type, which="ee"):
        raise util.ModelNotImplemented

    for name, key, nf, nb in self.ansatz.coupling_cluster_ranks(
        spin_type=self.spin_type, which="ee"
    ):
        raise util.ModelNotImplemented

    assert i0 == len(vector)

    return amplitudes