예제 #1
0
    def test_apply_spinful_fermionop(self):
        """
        Make sure the spin-orbital reordering is working by comparing
        apply operation
        """
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')
        wfn.normalize()
        cirq_wf = to_cirq(wfn).reshape((-1, 1))

        op_to_apply = FermionOperator()
        test_state = copy.deepcopy(wfn)
        test_state.set_wfn('zero')
        for p, q, r, s in product(range(2), repeat=4):
            op = FermionOperator(
                ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)),
                coefficient=numpy.random.randn())
            op_to_apply += op + hermitian_conjugated(op)
            test_state += wfn.apply(op + hermitian_conjugated(op))

        opmat = get_sparse_operator(op_to_apply, n_qubits=4).toarray()
        new_state_cirq = opmat @ cirq_wf

        # this part is because we need to pass a normalized wavefunction
        norm_constant = new_state_cirq.conj().T @ new_state_cirq
        new_state_cirq /= numpy.sqrt(norm_constant)
        new_state_wfn = from_cirq(new_state_cirq.flatten(), thresh=1.0E-12)
        new_state_wfn.scale(numpy.sqrt(norm_constant))

        self.assertTrue(
            numpy.allclose(test_state.get_coeff((2, 0)),
                           new_state_wfn.get_coeff((2, 0))))
예제 #2
0
    def test_apply_number(self):
        norb = 4
        test = numpy.random.rand(norb, norb)
        diag = numpy.random.rand(norb * 2)
        diag2 = copy.deepcopy(diag)
        e_0 = 0
        for i in range(norb):
            e_0 += diag[i + norb]
            diag2[i + norb] = -diag[i + norb]
        hamil = diagonal_hamiltonian.Diagonal(diag2, e_0=e_0)
        hamil._conserve_number = False
        wfn = Wavefunction([[4, 2, norb]], broken=['number'])
        wfn.set_wfn(strategy='from_data', raw_data={(4, 2): test})
        out1 = wfn.apply(hamil)

        hamil = diagonal_hamiltonian.Diagonal(diag)
        wfn = Wavefunction([[4, 2, norb]])
        wfn.set_wfn(strategy='from_data', raw_data={(4, 2): test})
        out2 = wfn.apply(hamil)

        self.assertTrue(
            numpy.allclose(out1._civec[(4, 2)].coeff,
                           out2._civec[(4, 2)].coeff))
예제 #3
0
def get_acse_residual_fqe(fqe_wf: Wavefunction, fqe_ham: RestrictedHamiltonian,
                          norbs: int) -> np.ndarray:
    """Get the ACSE block by using reduced density operators that are Sz spin
    adapted

    R^{ij}_{lk} = <psi | [i^ j^ k l, A] | psi>

    alpha-alpha, beta-beta, alpha-beta, and beta-alpha blocks

    we do not compression over alpha-alpha or beta-beta so these are still
    norbs**2 in linear dimension. In other words, we do computation on
    elements we know should be zero. This is for simplicity in the code.

    Args:
        fqe_wf:  fqe.Wavefunction object to calculate expectation value with
        fqe_ham: fqe.RestrictedHamiltonian operator corresponding to a chemical
                 Hamiltonian
        norbs: Number of orbitals. Number of spatial orbitals

    Returns:
        Gradient of the i^ j^ k l operator
    """
    acse_aa = np.zeros((norbs, norbs, norbs, norbs), dtype=np.complex128)
    acse_bb = np.zeros((norbs, norbs, norbs, norbs), dtype=np.complex128)
    acse_ab = np.zeros((norbs, norbs, norbs, norbs), dtype=np.complex128)

    fqe_appA = fqe_wf.apply(fqe_ham)

    for p, q, r, s in product(range(norbs), repeat=4):
        # alpha-alpha block real
        if p != q and r != s:
            rdo = ((2 * p, 1), (2 * q, 1), (2 * r, 0), (2 * s, 0))
            rdo = 1j * (of.FermionOperator(rdo) -
                        of.hermitian_conjugated(of.FermionOperator(rdo)))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_aa[p, q, r, s] = (val2 - val1) / 2j

            # alpha-alpha block imag
            rdo = ((2 * p, 1), (2 * q, 1), (2 * r, 0), (2 * s, 0))
            rdo = of.FermionOperator(rdo) + of.hermitian_conjugated(
                of.FermionOperator(rdo))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_aa[p, q, r, s] += (val2 - val1) / 2

            # beta-beta block real
            rdo = (
                (2 * p + 1, 1),
                (2 * q + 1, 1),
                (2 * r + 1, 0),
                (2 * s + 1, 0),
            )
            rdo = 1j * (of.FermionOperator(rdo) -
                        of.hermitian_conjugated(of.FermionOperator(rdo)))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_bb[p, q, r, s] += (val2 - val1) / 2j

            # beta-beta block imag
            rdo = (
                (2 * p + 1, 1),
                (2 * q + 1, 1),
                (2 * r + 1, 0),
                (2 * s + 1, 0),
            )
            rdo = of.FermionOperator(rdo) + of.hermitian_conjugated(
                of.FermionOperator(rdo))
            val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
            val2 = np.conjugate(val1)
            acse_bb[p, q, r, s] += (val2 - val1) / 2

        # alpha-beta block real
        rdo = ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0))
        rdo = 1j * (of.FermionOperator(rdo) -
                    of.hermitian_conjugated(of.FermionOperator(rdo)))
        val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
        val2 = np.conjugate(val1)
        acse_ab[p, q, r, s] += (val2 - val1) / 2j

        # alpha-beta block imag
        rdo = ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0))
        rdo = of.FermionOperator(rdo) + of.hermitian_conjugated(
            of.FermionOperator(rdo))
        val1 = fqe.util.vdot(fqe_appA, fqe_wf.apply(rdo))
        val2 = np.conjugate(val1)  # fqe.util.vdot(fqe_wf.apply(rdo), fqe_appA)
        acse_ab[p, q, r, s] += (val2 - val1) / 2

    # unroll residual blocks into full matrix
    acse_residual = np.zeros((2 * norbs, 2 * norbs, 2 * norbs, 2 * norbs),
                             dtype=np.complex128)
    acse_residual[::2, ::2, ::2, ::2] = acse_aa
    acse_residual[1::2, 1::2, 1::2, 1::2] = acse_bb
    acse_residual[::2, 1::2, 1::2, ::2] = acse_ab
    acse_residual[::2, 1::2, ::2, 1::2] = np.einsum("ijkl->ijlk", -acse_ab)
    acse_residual[1::2, ::2, ::2, 1::2] = np.einsum("ijkl->jilk", acse_ab)
    acse_residual[1::2, ::2,
                  1::2, ::2] = np.einsum("ijkl->ijlk",
                                         -acse_residual[1::2, ::2, ::2, 1::2])

    return acse_residual