def form_rpa_a_matrix_mo_singlet_ss_full(E_MO: np.ndarray, TEI_MO: np.ndarray,
                                         nocc: int) -> np.ndarray:
    r"""Form the same-spin part of the A (CIS) matrix in the MO
    basis. [singlet]

    The equation for element :math:`\{ia,jb\}` is :math:`????`.
    """

    norb = E_MO.shape[0]
    nvirt = norb - nocc
    nov = nocc * nvirt

    ediff = form_vec_energy_differences(
        np.diag(E_MO)[:nocc],
        np.diag(E_MO)[nocc:])

    A = np.empty(shape=(nov, nov))

    for i in range(nocc):
        for a in range(nvirt):
            ia = i * nvirt + a
            for j in range(nocc):
                for b in range(nvirt):
                    jb = j * nvirt + b
                    A[ia,
                      jb] = TEI_MO[a + nocc, i, j,
                                   b + nocc] - TEI_MO[a + nocc, b + nocc, j, i]

    A += np.diag(ediff)

    return A
def form_rpa_a_matrix_mo_singlet_full(E_MO: np.ndarray, TEI_MO: np.ndarray,
                                      nocc: int) -> np.ndarray:
    r"""Form the A (CIS) matrix in the MO basis. [singlet]

    The equation for element :math:`\{ia,jb\}` is
    :math:`\left<aj||ib\right> = \left<aj|ib\right> -
    \left<aj|bi\right> = [ai|jb] - [ab|ji] = 2(ai|jb) - (ab|ji)`. It
    also includes the virt-occ energy difference on the diagonal.
    """

    norb = E_MO.shape[0]
    nvirt = norb - nocc
    nov = nocc * nvirt

    ediff = form_vec_energy_differences(
        np.diag(E_MO)[:nocc],
        np.diag(E_MO)[nocc:])

    A = np.empty(shape=(nov, nov))

    for i in range(nocc):
        for a in range(nvirt):
            ia = i * nvirt + a
            for j in range(nocc):
                for b in range(nvirt):
                    jb = j * nvirt + b
                    A[ia, jb] = (2 * TEI_MO[a + nocc, i, j, b + nocc] -
                                 TEI_MO[a + nocc, b + nocc, j, i])

    A += np.diag(ediff)

    return A
예제 #3
0
 def print_results_nwchem(self) -> str:
     # TODO UHF
     nocc_tot, nvirt_tot, _, _ = self.solver.occupations
     moene = np.diag(self.solver.moenergies[0])
     moene_occ = moene[:nocc_tot]
     moene_virt = moene[nocc_tot:]
     ediff = form_vec_energy_differences(moene_occ,
                                         moene_virt) * HARTREE_TO_EV
     idxsort = np.argsort(ediff)
     ediff_sorted = ediff[idxsort]
     indices_unrestricted_orbwin = form_indices_orbwin(nocc_tot, nvirt_tot)
     indices_sorted = [indices_unrestricted_orbwin[i] for i in idxsort]
     ndiff = 10
     lines = []
     lines.append(f"   {ndiff:>2d} smallest eigenvalue differences (eV) ")
     lines.append(
         "--------------------------------------------------------")
     lines.append(
         "  No. Spin  Occ  Vir  Irrep   E(Occ)    E(Vir)   E(Diff)")
     lines.append(
         "--------------------------------------------------------")
     for idx in range(ndiff):
         iocc, ivirt = indices_sorted[idx]
         lines.append(
             f"{idx + 1:>5d}{1:>5d}{iocc + 1:>5d}{ivirt + 1:>5d} "
             f'{"X":<5}{moene[iocc]:>10.3f}{moene[ivirt]:>10.3f}{ediff_sorted[idx]:>10.3f}'
         )
     lines.append(
         "--------------------------------------------------------")
     return "\n".join(lines)
def form_rpa_a_matrix_mo_singlet_partial(
    E_MO: np.ndarray, TEI_MO_iajb: np.ndarray, TEI_MO_ijab: np.ndarray
) -> np.ndarray:
    r"""Form the A (CIS) matrix in the MO basis. [singlet]

    The equation for element :math:`\{ia,jb\}` is
    :math:`\left<aj||ib\right> = \left<aj|ib\right> -
    \left<aj|bi\right> = [ai|jb] - [ab|ji] = 2(ai|jb) - (ab|ji)`. It
    also includes the virt-occ energy difference on the diagonal.
    """

    shape_iajb = TEI_MO_iajb.shape
    shape_ijab = TEI_MO_ijab.shape
    assert len(shape_iajb) == len(shape_ijab) == 4
    assert shape_iajb[0] == shape_iajb[2] == shape_ijab[0] == shape_ijab[1]
    assert shape_iajb[1] == shape_iajb[3] == shape_ijab[2] == shape_ijab[3]
    nocc = shape_iajb[0]
    nvirt = shape_iajb[1]
    norb = nocc + nvirt
    assert len(E_MO.shape) == 2
    assert E_MO.shape[0] == E_MO.shape[1] == norb
    nov = nocc * nvirt

    ediff = form_vec_energy_differences(np.diag(E_MO)[:nocc], np.diag(E_MO)[nocc:])

    A = 2 * TEI_MO_iajb
    A -= TEI_MO_ijab.swapaxes(1, 2)
    A.shape = (nov, nov)

    A += np.diag(ediff)

    return A
def form_rpa_a_matrix_mo_singlet_ss_partial(
    E_MO: np.ndarray, TEI_MO_iajb: np.ndarray, TEI_MO_ijab: np.ndarray
) -> np.ndarray:
    r"""Form the same-spin part of the A (CIS) matrix in the MO
    basis. [singlet]

    The equation for element :math:`\{ia,jb\}` is :math:`????`.
    """

    shape_iajb = TEI_MO_iajb.shape
    shape_ijab = TEI_MO_ijab.shape
    assert len(shape_iajb) == len(shape_ijab) == 4
    assert shape_iajb[0] == shape_iajb[2] == shape_ijab[0] == shape_ijab[1]
    assert shape_iajb[1] == shape_iajb[3] == shape_ijab[2] == shape_ijab[3]
    nocc = shape_iajb[0]
    nvirt = shape_iajb[1]
    norb = nocc + nvirt
    assert len(E_MO.shape) == 2
    assert E_MO.shape[0] == E_MO.shape[1] == norb
    nov = nocc * nvirt

    ediff = form_vec_energy_differences(np.diag(E_MO)[:nocc], np.diag(E_MO)[nocc:])

    A = TEI_MO_iajb.copy()
    A -= TEI_MO_ijab.swapaxes(1, 2)
    A.shape = (nov, nov)

    A += np.diag(ediff)

    return A
def form_rpa_a_matrix_mo_triplet_partial(E_MO, TEI_MO_ijab):
    r"""Form the A (CIS) matrix in the MO basis. [triplet]

    The equation for element :math:`\{ia,jb\}` is :math:`-
    \left<aj|bi\right> = - [ab|ji] = - (ab|ji)`. It also includes the
    virt-occ energy difference on the diagonal.
    """

    shape_ijab = TEI_MO_ijab.shape
    assert len(shape_ijab) == 4
    assert shape_ijab[0] == shape_ijab[1]
    assert shape_ijab[2] == shape_ijab[3]
    nocc = shape_ijab[0]
    nvirt = shape_ijab[2]
    norb = nocc + nvirt
    assert len(E_MO.shape) == 2
    assert E_MO.shape[0] == E_MO.shape[1] == norb
    nov = nocc * nvirt

    ediff = form_vec_energy_differences(
        np.diag(E_MO)[:nocc],
        np.diag(E_MO)[nocc:])

    A = np.zeros((nocc, nvirt, nocc, nvirt))
    A -= TEI_MO_ijab.swapaxes(1, 2)
    A.shape = (nov, nov)

    A += np.diag(ediff)

    return A
예제 #7
0
    def form_uncoupled_results(self):

        # We avoid the formation of the full Hessian, but the energy
        # differences on the diagonal are still needed. Form them
        # here. The dynamic frequency contribution will be handled
        # inside the main loop.
        nocc_a, _, nocc_b, _ = self.solver.occupations
        moene_alph = np.diag(self.solver.moenergies[0])
        moene_occ_alph = moene_alph[:nocc_a]
        moene_virt_alph = moene_alph[nocc_a:]
        ediff_alph = form_vec_energy_differences(moene_occ_alph,
                                                 moene_virt_alph)
        ediff_supervector_alph = np.concatenate((ediff_alph, ediff_alph))
        ediff_supervector_alph_static = ediff_supervector_alph[..., np.newaxis]
        nov_alph = len(ediff_alph)
        if self.solver.is_uhf:
            moene_beta = np.diag(self.solver.moenergies[1])
            moene_occ_beta = moene_beta[:nocc_b]
            moene_virt_beta = moene_beta[nocc_b:]
            ediff_beta = form_vec_energy_differences(moene_occ_beta,
                                                     moene_virt_beta)
            ediff_supervector_beta = np.concatenate((ediff_beta, ediff_beta))
            ediff_supervector_beta_static = ediff_supervector_beta[...,
                                                                   np.newaxis]
            nov_beta = len(ediff_beta)

        self.uncoupled_results = []

        for f in range(len(self.solver.frequencies)):

            frequency = self.solver.frequencies[f]
            ediff_supervector_alph = ediff_supervector_alph_static.copy()
            ediff_supervector_alph[:nov_alph] = (
                ediff_supervector_alph_static[:nov_alph] - frequency)
            ediff_supervector_alph[nov_alph:] = (
                ediff_supervector_alph_static[nov_alph:] + frequency)
            if self.solver.is_uhf:
                ediff_supervector_beta = ediff_supervector_beta_static.copy()
                ediff_supervector_beta[:nov_beta] = (
                    ediff_supervector_beta_static[:nov_beta] - frequency)
                ediff_supervector_beta[nov_beta:] = (
                    ediff_supervector_beta_static[nov_beta:] + frequency)

            # dim_rows -> (number of operators) * (number of components for each operator)
            # dim_cols -> total number of response vectors
            dim_rows = sum(self.solver.operators[i].
                           mo_integrals_ai_supervector_alph.shape[0]
                           for i in range(len(self.solver.operators)))
            dim_cols = dim_rows

            # FIXME
            results = np.zeros(
                shape=(dim_rows, dim_cols),
                dtype=self.solver.operators[0].
                mo_integrals_ai_supervector_alph.dtype,
            )

            # Form the result blocks between each pair of
            # operators. Ignore any potential symmetry in the final
            # result matrix for now.
            result_blocks = []
            row_starts = []
            col_starts = []
            row_start = 0
            for iop1, op1 in enumerate(self.solver.operators):
                col_start = 0
                for iop2, op2 in enumerate(self.solver.operators):
                    result_block = 0.0
                    result_block_alph = form_results(
                        op1.mo_integrals_ai_supervector_alph,
                        op2.mo_integrals_ai_supervector_alph /
                        ediff_supervector_alph,
                    )
                    result_block += result_block_alph
                    if self.solver.is_uhf:
                        result_block_beta = form_results(
                            op1.mo_integrals_ai_supervector_beta,
                            op2.mo_integrals_ai_supervector_beta /
                            ediff_supervector_beta,
                        )
                        result_block += result_block_beta
                    result_blocks.append(result_block)
                    row_starts.append(row_start)
                    col_starts.append(col_start)
                    col_start += op2.mo_integrals_ai_supervector_alph.shape[0]
                row_start += op1.mo_integrals_ai_supervector_alph.shape[0]

            # Put each of the result blocks back in the main results
            # matrix.
            assert len(row_starts) == len(col_starts) == len(result_blocks)
            for idx, result_block in enumerate(result_blocks):
                nr, nc = result_block.shape
                rs, cs = row_starts[idx], col_starts[idx]
                results[rs:rs + nr, cs:cs + nc] = result_block

            # The / 2 is because of the supervector part.
            if self.solver.is_uhf:
                results = 2 * results / 2
            else:
                results = 4 * results / 2
            self.uncoupled_results.append(results)