Example #1
0
def evolve_fqe_givens_unrestricted(wfn: Wavefunction,
                                   u: np.ndarray) -> Wavefunction:
    """Evolve a wavefunction by u generated from a 1-body Hamiltonian.

    Args:
        wfn: 2^{n} x 1 vector.
        u: (n x n) unitary matrix.

    Returns:
        New evolved wfn object.
    """
    rotations, diagonal = givens_decomposition_square(u.copy())
    # Iterate through each layer and time evolve by the appropriate
    # fermion operators
    for layer in rotations:
        for givens in layer:
            i, j, theta, phi = givens
            if not np.isclose(phi, 0):
                op = of.FermionOperator(((j, 1), (j, 0)), coefficient=-phi)
                wfn = wfn.time_evolve(1.0, op)
            if not np.isclose(theta, 0):
                op = of.FermionOperator(
                    ((i, 1),
                     (j, 0)), coefficient=-1j * theta) + of.FermionOperator(
                         ((j, 1), (i, 0)), coefficient=1j * theta)
                wfn = wfn.time_evolve(1.0, op)

    # evolve the last diagonal phases
    for idx, final_phase in enumerate(diagonal):
        if not np.isclose(final_phase, 1.0):
            op = of.FermionOperator(((idx, 1), (idx, 0)),
                                    -np.angle(final_phase))
            wfn = wfn.time_evolve(1.0, op)

    return wfn
Example #2
0
def evolve_wf_diagonal_coulomb(wf: np.ndarray,
                               vij_mat: np.ndarray,
                               time=1) -> np.ndarray:
    r"""Utility for testing evolution of a full 2^{n} wavefunction via

    :math:`exp{-i time * \sum_{i,j, sigma, tau}v_{i, j}n_{i\sigma}n_{j\tau}}.`

    Args:
        wf: 2^{n} x 1 vector
        vij_mat: List[(n//2 x n//2)] matrices

    Returns:
        New evolved 2^{n} x 1 vector
    """
    norbs = int(np.log2(wf.shape[0]) / 2)
    diagonal_coulomb = of.FermionOperator()
    for i, j in product(range(norbs), repeat=2):
        for sigma, tau in product(range(2), repeat=2):
            diagonal_coulomb += of.FermionOperator(
                (
                    (2 * i + sigma, 1),
                    (2 * i + sigma, 0),
                    (2 * j + tau, 1),
                    (2 * j + tau, 0),
                ),
                coefficient=vij_mat[i, j],
            )
    bigU = expm(-1j * time *
                of.get_sparse_operator(diagonal_coulomb, n_qubits=2 * norbs))
    return bigU @ wf
Example #3
0
    def two_body_sz_adapted(self):
        """
        Doubles generators each with distinct Sz expectation value.

        G^{isigma, jtau, ktau, lsigma) for sigma, tau in 0, 1
        """
        for i, j, k, l in product(range(self.norbs), repeat=4):
            if i < j and k < l:
                op_aa = ((2 * i, 1), (2 * j, 1), (2 * k, 0), (2 * l, 0))
                op_bb = ((2 * i + 1, 1), (2 * j + 1, 1), (2 * k + 1, 0),
                         (2 * l + 1, 0))
                fop_aa = of.FermionOperator(op_aa)
                fop_aa = fop_aa - of.hermitian_conjugated(fop_aa)
                fop_bb = of.FermionOperator(op_bb)
                fop_bb = fop_bb - of.hermitian_conjugated(fop_bb)
                fop_aa = of.normal_ordered(fop_aa)
                fop_bb = of.normal_ordered(fop_bb)
                self.op_pool.append(fop_aa)
                self.op_pool.append(fop_bb)

            op_ab = ((2 * i, 1), (2 * j + 1, 1), (2 * k + 1, 0), (2 * l, 0))
            fop_ab = of.FermionOperator(op_ab)
            fop_ab = fop_ab - of.hermitian_conjugated(fop_ab)
            fop_ab = of.normal_ordered(fop_ab)
            if not np.isclose(fop_ab.induced_norm(), 0):
                self.op_pool.append(fop_ab)
Example #4
0
def ops_pqrs():

    #Doubles
    pairs = []
    for i in range(0, n_spinorbitals):
        for j in range(i+1, n_spinorbitals):
            pairs.append([i,j])
    for pair1 in range(0, len(pairs)):
        for pair2 in range(pair1, len(pairs)):
            a, b = pairs[pair2]
            i, j = pairs[pair1]
            two_elec = openfermion.FermionOperator(((b,1),(a,1),(j,0),(i,0)))-openfermion.FermionOperator(((i,1),(j,1),(a,0),(b,0)))
            if args.filter==False or abs(doubles_hamiltonian[j][i][a][b])>1e-8 or abs(doubles_hamiltonian[b][a][i][j])>1e-8:
                if args.ccfit == True:
                    parameters.append(molecule.ccsd_double_amps[b][a][j][i])
                else:
                    parameters.append(0)
                SQ_CC_ops.append(two_elec)
    #Singles
    for i in range(0, n_spinorbitals):
        for a in range(i, n_spinorbitals):
            one_elec = openfermion.FermionOperator(((a,1),(i,0)))-openfermion.FermionOperator(((i,1),(a,0)))
            if args.filter==False or abs(singles_hamiltonian[a][i])>1e-8 or abs(singles_hamiltonian[i][a])>1e-8:
                if args.ccfit == True:
                    parameters.append(molecule.ccsd_single_amps[a][i])
                else:
                    parameters.append(0)
                SQ_CC_ops.append(one_elec)
    return(SQ_CC_ops, parameters)
Example #5
0
def test_erpa_eom_ham_h2():
    filename = os.path.join(DATA_DIRECTORY, "H2_sto-3g_singlet_0.7414.hdf5")
    molecule = MolecularData(filename=filename)
    reduced_ham = make_reduced_hamiltonian(
        molecule.get_molecular_hamiltonian(), molecule.n_electrons)
    rha_fermion = of.get_fermion_operator(reduced_ham)
    permuted_hijkl = np.einsum('ijlk', reduced_ham.two_body_tensor)
    opdm = np.diag([1] * molecule.n_electrons + [0] *
                   (molecule.n_qubits - molecule.n_electrons))
    tpdm = 2 * of.wedge(opdm, opdm, (1, 1), (1, 1))
    rdms = of.InteractionRDM(opdm, tpdm)
    dim = reduced_ham.one_body_tensor.shape[0] // 2
    full_basis = {}  # erpa basis.  A, B basis in RPA language
    cnt = 0
    for p, q in product(range(dim), repeat=2):
        if p < q:
            full_basis[(p, q)] = cnt
            full_basis[(q, p)] = cnt + dim * (dim - 1) // 2
            cnt += 1
    for rkey in full_basis.keys():
        p, q = rkey
        for ckey in full_basis.keys():
            r, s = ckey
            for sigma, tau in product([0, 1], repeat=2):
                test = erpa_eom_hamiltonian(permuted_hijkl, tpdm,
                                            2 * q + sigma, 2 * p + sigma,
                                            2 * r + tau, 2 * s + tau).real
                qp_op = of.FermionOperator(
                    ((2 * q + sigma, 1), (2 * p + sigma, 0)))
                rs_op = of.FermionOperator(
                    ((2 * r + tau, 1), (2 * s + tau, 0)))
                erpa_op = of.normal_ordered(
                    of.commutator(qp_op, of.commutator(rha_fermion, rs_op)))
                true = rdms.expectation(of.get_interaction_operator(erpa_op))
                assert np.isclose(true, test)
Example #6
0
def _single_term_to_FermionOperator(val, lat_ix1, lat_ix2, ind):
    '''
    Export single term of the hamiltonian to openfermion.

    Parameters
    ----------
    val: number or 2D array

    Returns
    ----------
    op: openfermion.FermionOperator
    '''
    try:
        n_spin1 = val.shape[0]
        n_spin2 = val.shape[1]

        op = openfermion.FermionOperator()
        for spin_ix1 in range(n_spin1):
            for spin_ix2 in range(n_spin2):
                ix1 = ind.index((lat_ix1, spin_ix1, n_spin1))
                ix2 = ind.index((lat_ix2, spin_ix2, n_spin2))
                op += openfermion.FermionOperator(f'{ix1}^ {ix2}',
                                                  val[spin_ix1, spin_ix2])

        return op

    except:
        try:
            ix1 = ind.index((lat_ix1, 0, 1))
            ix2 = ind.index((lat_ix2, 0, 1))
            return openfermion.FermionOperator(f'{ix1}^ {ix2}', val)
        except:
            raise ValueError(f'''
            Cannot construct fermionic operator with
            indices {lat_ix1}, {lat_ix2}, value {val}''')
Example #7
0
    def singlet_t2(self):
        """
        Generate singlet rotations
        T_{ij}^{ab} = T^(v1a, v2b)_{o1a, o2b} + T^(v1b, v2a)_{o1b, o2a} +
                      T^(v1a, v2a)_{o1a, o2a} + T^(v1b, v2b)_{o1b, o2b}

        where v1,v2 are indices of the virtual obritals and o1, o2 are
        indices of the occupied orbitals with respect to the Hartree-Fock
        reference.
        """
        for oo_i in self.occ:
            for oo_j in self.occ:
                for vv_a in self.virt:
                    for vv_b in self.virt:
                        term = of.FermionOperator()
                        for sigma, tau in product(range(2), repeat=2):
                            op = ((2 * vv_a + sigma, 1), (2 * vv_b + tau, 1),
                                  (2 * oo_j + tau, 0), (2 * oo_i + sigma, 0))
                            if (2 * vv_a + sigma == 2 * vv_b + tau
                                    or 2 * oo_j + tau == 2 * oo_i + sigma):
                                continue
                            fop = of.FermionOperator(op, coefficient=0.5)
                            fop = fop - of.hermitian_conjugated(fop)
                            fop = of.normal_ordered(fop)
                            term += fop
                        self.op_pool.append(term)
Example #8
0
def _get_op_Sm(n_site):
    assert n_site % 2 == 0

    op_Sm = openfermion.FermionOperator()
    for i in range(5):
        op_Sm += openfermion.FermionOperator(((2 * i + 1, 1), (2 * i, 0)))
    return op_Sm
Example #9
0
def _get_op_Sp(n_site):
    assert n_site % 2 == 0

    op_Sp = openfermion.FermionOperator()
    for i in range(n_site // 2):
        op_Sp += openfermion.FermionOperator(((2 * i, 1), (2 * i + 1, 0)))
    return op_Sp
Example #10
0
    def make_excitation_generator(
            self,
            indices: typing.Iterable[typing.Tuple[int,
                                                  int]]) -> QubitHamiltonian:
        """
        Notes
        ----------
        Creates the transformed hermitian generator of UCC type unitaries:
              M(a^\dagger_{a_0} a_{i_0} a^\dagger{a_1}a_{i_1} ... - h.c.)
              where the qubit map M depends is self.transformation

        Parameters
        ----------
        indices : typing.Iterable[typing.Tuple[int, int]] :
            List of tuples [(a_0, i_0), (a_1, i_1), ... ] - recommended format, in spin-orbital notation (alpha odd numbers, beta even numbers)
            can also be given as one big list: [a_0, i_0, a_1, i_1 ...]
        Returns
        -------
        type
            1j*Transformed qubit excitation operator, depends on self.transformation
        """
        # check indices and convert to list of tuples if necessary
        if len(indices) == 0:
            raise TequilaException(
                "make_excitation_operator: no indices given")
        elif not isinstance(indices[0], typing.Iterable):
            if len(indices) % 2 != 0:
                raise TequilaException(
                    "make_excitation_generator: unexpected input format of indices\n"
                    "use list of tuples as [(a_0, i_0),(a_1, i_1) ...]\n"
                    "or list as [a_0, i_0, a_1, i_1, ... ]\n"
                    "you gave: {}".format(indices))
            converted = [(indices[2 * i], indices[2 * i + 1])
                         for i in range(len(indices) // 2)]
        else:
            converted = indices

        # convert to openfermion input format
        ofi = []
        dag = []
        for pair in converted:
            assert (len(pair) == 2)
            ofi += [
                (int(pair[0]), 1), (int(pair[1]), 0)
            ]  # openfermion does not take other types of integers like numpy.int64
            dag += [(int(pair[0]), 0), (int(pair[1]), 1)]

        op = openfermion.FermionOperator(tuple(ofi),
                                         1.j)  # 1j makes it hermitian
        op += openfermion.FermionOperator(tuple(reversed(dag)), -1.j)
        qop = QubitHamiltonian(qubit_hamiltonian=self.transformation(op))

        # check if the operator is hermitian and cast coefficients to floats
        # in order to avoid trouble with the simulation backends
        assert qop.is_hermitian()
        for k, v in qop.qubit_operator.terms.items():
            qop.qubit_operator.terms[k] = to_float(v)

        qop = qop.simplify()
        return qop
Example #11
0
def _get_op_Sz(n_site):
    assert n_site % 2 == 0

    op_Sz = openfermion.FermionOperator()
    for i in range(n_site // 2):
        op_Sz += openfermion.FermionOperator(((2 * i, 1), (2 * i, 0)),
                                             +0.5)  # alpha
        op_Sz += openfermion.FermionOperator(((2 * i + 1, 1), (2 * i + 1, 0)),
                                             -0.5)  # beta
    return op_Sz
Example #12
0
def evolve_fqe_givens_sector(wfn: Wavefunction, u: np.ndarray,
                             sector='alpha') -> Wavefunction:
    """Evolve a wavefunction by u generated from a 1-body Hamiltonian.

    Args:
        wfn: FQE Wavefunction on n-orbitals
        u: (n x n) unitary matrix.
        sector: Optional either 'alpha' or 'beta' indicating which sector
                to rotate
    Returns:
        New evolved wfn object.
    """
    if sector == 'alpha':
        sigma = 0
    elif sector == 'beta':
        sigma = 1
    else:
        raise ValueError("Bad section variable.  Either (alpha) or (beta)")

    if not np.isclose(u.shape[0], wfn.norb()):
        raise ValueError(
            "unitary is not specified for the correct number of orbitals")

    rotations, diagonal = givens_decomposition_square(u.copy())
    # Iterate through each layer and time evolve by the appropriate
    # fermion operators
    for layer in rotations:
        for givens in layer:
            i, j, theta, phi = givens
            if not np.isclose(phi, 0):
                op = of.FermionOperator(
                    ((2 * j + sigma, 1), (2 * j + sigma, 0)), coefficient=-phi)
                wfn = wfn.time_evolve(1.0, op)
            if not np.isclose(theta, 0):
                op = of.FermionOperator(((2 * i + sigma, 1),
                                         (2 * j + sigma, 0)),
                                        coefficient=-1j * theta) + \
                     of.FermionOperator(((2 * j + sigma, 1),
                                         (2 * i + sigma, 0)),
                                        coefficient=1j * theta)
                wfn = wfn.time_evolve(1.0, op)

    # evolve the last diagonal phases
    for idx, final_phase in enumerate(diagonal):
        if not np.isclose(final_phase, 1.0):
            op = of.FermionOperator(
                ((2 * idx + sigma, 1), (2 * idx + sigma, 0)),
                -np.angle(final_phase))
            wfn = wfn.time_evolve(1.0, op)

    return wfn
Example #13
0
 def one_body_sz_adapted(self):
     # alpha-alpha rotation
     # beta-beta rotation
     for i, j in product(range(self.norbs), repeat=2):
         if i > j:
             op_aa = ((2 * i, 1), (2 * j, 0))
             op_bb = ((2 * i + 1, 1), (2 * j + 1, 0))
             fop_aa = of.FermionOperator(op_aa)
             fop_aa = fop_aa - of.hermitian_conjugated(fop_aa)
             fop_bb = of.FermionOperator(op_bb)
             fop_bb = fop_bb - of.hermitian_conjugated(fop_bb)
             fop_aa = of.normal_ordered(fop_aa)
             fop_bb = of.normal_ordered(fop_bb)
             self.op_pool.append(fop_aa)
             self.op_pool.append(fop_bb)
Example #14
0
def _test():
    s1 = sympy.Symbol('spam')
    s2 = sympy.Symbol('egg')
    w1 = WrappedExpr(s1)
    w2 = WrappedExpr(s2)

    print(isinstance(w1, float))
    print(hash(s1))
    print()

    print(w1 + w2)
    print(type(w1 + w2))
    print()

    print(w1 + 100)
    print(type(w1 + 100))
    print(type((w1 + 100).expr))
    print()

    print(100 + w1)
    print(type(100 + w1))
    print()

    print(100. + w1)
    print(type(100. + w1))
    print()

    c = 100 + 2j
    print(type(c))
    print(c + w1)
    print(type(c + w1))

    print(w1 - w2)
    print(w1 - w1)
    print('aaa', pow(w1, w2))
    print('aaa', pow(w1, w2, 2))
    print('aaa', pow(w1, w2, w1))

    print(copy.copy(w1))
    print(copy.deepcopy(w1))

    import openfermion

    fop = openfermion.FermionOperator('1^ 2', w1)
    print(fop)
    print(fop + fop)
    fop += fop * 2 + openfermion.FermionOperator('', 1)
    print(fop)
Example #15
0
def evolve_fqe_charge_charge_sector(wfn: Wavefunction,
                                    vij_mat: np.ndarray,
                                    sector='alpha',
                                    time=1) -> Wavefunction:
    r"""Utility for testing evolution of a full 2^{n} wavefunction via

    :math:`exp{-i time * \sum_{i,j}v_{i, j}n_{i, alpha}n_{j, beta}}.`

    Args:
        wfn: fqe_wf with sdim = n
        vij_mat: List[(n x n] matrices n is the spatial orbital rank
        time: evolution time.

    Returns:
        New evolved 2^{2 * n} x 1 vector
    """
    if sector == 'alpha':
        sigma = 0
    elif sector == 'beta':
        sigma = 1
    else:
        raise ValueError("Sector must be alpha or beta.")

    norbs = vij_mat.shape[0]
    for p, q in product(range(norbs), repeat=2):
        if np.isclose(vij_mat[p, q], 0):
            continue
        fop = of.FermionOperator(((2 * p + sigma, 1), (2 * p + sigma, 0),
                                  (2 * q + sigma, 1), (2 * q + sigma, 0)),
                                 coefficient=vij_mat[p, q])
        wfn = wfn.time_evolve(time, fop)
    return wfn
Example #16
0
def _get_op_S2(n_site):
    op_Sp = _get_op_Sp(n_site)
    op_Sm = _get_op_Sm(n_site)
    op_Sz = _get_op_Sz(n_site)

    op_S2 = op_Sp * op_Sm + op_Sz * (op_Sz - openfermion.FermionOperator(
        (), 1))
    return op_S2
 def fermion_generator(self) -> openfermion.FermionOperator:
     """The FermionOperator G such that the gate's unitary is exp(-i t G)
     with exponent t using the Jordan-Wigner transformation."""
     half_generator = sum((
         w * G
         for w, G in zip(self.weights, self.fermion_generator_components())),
                          openfermion.FermionOperator())
     return half_generator + openfermion.hermitian_conjugated(half_generator)
def test_simulate_trotter_bad_hamiltonian_type_raises_error():
    qubits = cirq.LineQubit.range(2)
    hamiltonian = openfermion.FermionOperator()
    time = 1.0
    with pytest.raises(TypeError):
        _ = next(simulate_trotter(qubits, hamiltonian, time,
                                  algorithm=None))
    with pytest.raises(TypeError):
        _ = next(simulate_trotter(qubits, hamiltonian, time,
                                  algorithm=LINEAR_SWAP_NETWORK))
Example #19
0
    def generalized_two_body_minimal(self):
        """
        Doubles generators each with distinct Sz expectation value.

        """
        for i, j, k, l in product(range(2 * self.norbs), repeat=4):
            if i < j and k < l:
                op = ((i, 1), (j, 1), (k, 0), (l, 0))
                fop_aa = of.FermionOperator(op)
                fop_aa = fop_aa - of.hermitian_conjugated(fop_aa)
                self.op_pool.append(fop_aa)
def test_double_excitation_matches_fermionic_evolution(half_turns):
    gate = DoubleExcitation ** half_turns

    op = openfermion.FermionOperator('3^ 2^ 1 0')
    op += openfermion.hermitian_conjugated(op)
    matrix_op = openfermion.get_sparse_operator(op)

    time_evol_op = scipy.linalg.expm(-1j * matrix_op * half_turns * numpy.pi)
    time_evol_op = time_evol_op.todense()

    cirq.testing.assert_allclose_up_to_global_phase(
        gate.matrix(), time_evol_op, atol=1e-7)
Example #21
0
def test_double_excitation_matches_fermionic_evolution(exponent):
    gate = ofc.DoubleExcitation ** exponent

    op = openfermion.FermionOperator('3^ 2^ 1 0')
    op += openfermion.hermitian_conjugated(op)
    matrix_op = openfermion.get_sparse_operator(op)

    time_evol_op = scipy.linalg.expm(-1j * matrix_op * exponent * numpy.pi)
    time_evol_op = time_evol_op.todense()

    cirq.testing.assert_allclose_up_to_global_phase(
        cirq.unitary(gate), time_evol_op, atol=1e-7)
def test_op_pool():
    op = OperatorPool(2, [0], [1])
    op.singlet_t2()
    true_generator = of.FermionOperator(((1, 1), (0, 1), (3, 0), (2, 0))) - \
                     of.FermionOperator(((3, 1), (2, 1), (1, 0), (0, 0)))

    assert np.isclose(
        of.normal_ordered(op.op_pool[0] - true_generator).induced_norm(), 0)

    op = OperatorPool(2, [0], [1])
    op.two_body_sz_adapted()
    assert len(op.op_pool) == 20

    true_generator0 = of.FermionOperator('1^ 0^ 2 1') - \
                      of.FermionOperator('2^ 1^ 1 0')
    assert np.isclose(
        of.normal_ordered(op.op_pool[0] - true_generator0).induced_norm(), 0)

    true_generator_end = of.FermionOperator('3^ 0^ 3 2') - \
                         of.FermionOperator('3^ 2^ 3 0')
    assert np.isclose(
        of.normal_ordered(op.op_pool[-1] - true_generator_end).induced_norm(),
        0)

    op = OperatorPool(2, [0], [1])
    op.one_body_sz_adapted()
    for gen in op.op_pool:
        for ladder_idx, _ in gen.terms.items():
            # check if one body terms are generated
            assert len(ladder_idx) == 2
Example #23
0
def test_vbc_time_evolve():
    molecule = build_h4square_moleculardata()
    oei, tei = molecule.get_integrals()
    nele = molecule.n_electrons
    nalpha = nele // 2
    nbeta = nele // 2
    sz = 0
    norbs = oei.shape[0]
    nso = 2 * norbs
    fqe_wf = fqe.Wavefunction([[nele, sz, norbs]])
    fqe_wf.set_wfn(strategy='random')
    fqe_wf.normalize()
    nfqe_wf = fqe.get_number_conserving_wavefunction(nele, norbs)
    nfqe_wf.sector((nele, sz)).coeff = fqe_wf.sector((nele, sz)).coeff
    _, tpdm = nfqe_wf.sector((nele, sz)).get_openfermion_rdms()
    d3 = nfqe_wf.sector((nele, sz)).get_three_pdm()

    adapt = VBC(oei, tei, nalpha, nbeta, iter_max=50)
    acse_residual = two_rdo_commutator_symm(adapt.reduced_ham.two_body_tensor,
                                            tpdm, d3)
    sos_op = adapt.get_takagi_tensor_decomp(acse_residual, None)

    test_wf = copy.deepcopy(nfqe_wf)
    test_wf = sos_op.time_evolve(test_wf)

    true_wf = copy.deepcopy(nfqe_wf)
    for v, cc in zip(sos_op.basis_rotation, sos_op.charge_charge):
        vc = v.conj()
        new_tensor = np.einsum('pi,si,ij,qj,rj->pqrs', v, vc, -1j * cc, v, vc)
        if np.isclose(np.linalg.norm(new_tensor), 0):
            continue
        fop = of.FermionOperator()
        for p, q, r, s in product(range(nso), repeat=4):
            op = ((p, 1), (s, 0), (q, 1), (r, 0))
            fop += of.FermionOperator(op, coefficient=new_tensor[p, q, r, s])
        fqe_op = build_hamiltonian(1j * fop, conserve_number=True)
        true_wf = true_wf.time_evolve(1, fqe_op)
    true_wf = evolve_fqe_givens_unrestricted(true_wf, sos_op.one_body_rotation)

    assert np.isclose(abs(fqe.vdot(true_wf, test_wf))**2, 1)
Example #24
0
def get_opdm(wf, num_orbitals, transform=of.jordan_wigner):
    opdm_hw = np.zeros((num_orbitals, num_orbitals), dtype=np.complex128)
    creation_ops = [
        of.get_sparse_operator(transform(of.FermionOperator(((p, 1)))),
                               n_qubits=num_orbitals)
        for p in range(num_orbitals)
    ]
    # not using display style objects
    for p, q in product(range(num_orbitals), repeat=2):
        operator = creation_ops[p] @ creation_ops[q].conj().transpose()
        opdm_hw[p, q] = wf.conj().T @ operator @ wf

    return opdm_hw
Example #25
0
def get_fermion_op(coeff_tensor) -> of.FermionOperator:
    r"""Returns an openfermion.FermionOperator from the given coeff_tensor.

    Given A[i, j, k, l] of A = \sum_{ijkl}A[i, j, k, l]i^ j^ k^ l
    return the FermionOperator A.

    Args:
        coeff_tensor: Coefficients for 4-mode operator

    Returns:
        A FermionOperator object
    """
    if len(coeff_tensor.shape) == 4:
        nso = coeff_tensor.shape[0]
        fermion_op = of.FermionOperator()
        for p, q, r, s in product(range(nso), repeat=4):
            if p == q or r == s:
                continue
            op = ((p, 1), (q, 1), (r, 0), (s, 0))
            fop = of.FermionOperator(op, coefficient=coeff_tensor[p, q, r, s])
            fermion_op += fop
        return fermion_op

    elif len(coeff_tensor.shape) == 2:
        nso = coeff_tensor.shape[0]
        fermion_op = of.FermionOperator()
        for p, q in product(range(nso), repeat=2):
            oper = ((p, 1), (q, 0))
            fop = of.FermionOperator(oper, coefficient=coeff_tensor[p, q])
            fermion_op += fop
        return fermion_op

    else:
        raise ValueError(
            "Arg `coeff_tensor` should have dimension 2 or 4 but has dimension"
            f" {len(coeff_tensor.shape)}.")
def test_nbody_spin_sectors():
    op = of.FermionOperator(((3, 1), (4, 1), (2, 0), (0, 0)),
                            coefficient=1.0 + 0.5j)
    # op += of.hermitian_conjugated(op)
    (
        coefficient,
        parity,
        alpha_sub_ops,
        beta_sub_ops,
    ) = gather_nbody_spin_sectors(op)
    assert np.isclose(coefficient.real, 1.0)
    assert np.isclose(coefficient.imag, 0.5)
    assert np.isclose(parity, -1)
    assert tuple(map(tuple, alpha_sub_ops)) == ((4, 1), (2, 0), (0, 0))
    assert tuple(map(tuple, beta_sub_ops)) == ((3, 1), )
Example #27
0
def evolve_wf_givens(wfn: np.ndarray, u: np.ndarray) -> np.ndarray:
    """Utility for testing evolution of a full 2^{n} wavefunction.

    Args:
        wfn: 2^{n} x 1 vector.
        u: (n//2 x n//2) unitary matrix.

    Returns:
        New evolved 2^{n} x 1 vector.
    """
    rotations, diagonal = givens_decomposition_square(u.copy())
    n_qubits = u.shape[0] * 2
    # Iterate through each layer and time evolve by the appropriate
    # fermion operators
    for layer in rotations:
        for givens in layer:
            i, j, theta, phi = givens
            op = of.FermionOperator(((2 * j, 1), (2 * j, 0)), coefficient=-phi)
            op += of.FermionOperator(((2 * j + 1, 1), (2 * j + 1, 0)),
                                     coefficient=-phi)
            wfn = (expm(-1j * of.get_sparse_operator(op, n_qubits=n_qubits))
                   @ wfn)

            op = of.FermionOperator(
                ((2 * i, 1),
                 (2 * j, 0)), coefficient=-1j * theta) + of.FermionOperator(
                     ((2 * j, 1), (2 * i, 0)), coefficient=1j * theta)
            op += of.FermionOperator(
                ((2 * i + 1, 1), (2 * j + 1, 0)),
                coefficient=-1j * theta) + of.FermionOperator(
                    ((2 * j + 1, 1), (2 * i + 1, 0)), coefficient=1j * theta)
            wfn = (expm(-1j * of.get_sparse_operator(op, n_qubits=n_qubits))
                   @ wfn)

    # evolve the last diagonal phases
    for idx, final_phase in enumerate(diagonal):
        if not np.isclose(final_phase, 1.0):
            op = of.FermionOperator(((2 * idx, 1), (2 * idx, 0)),
                                    -np.angle(final_phase))
            op += of.FermionOperator(((2 * idx + 1, 1), (2 * idx + 1, 0)),
                                     -np.angle(final_phase))
            wfn = (expm(-1j * of.get_sparse_operator(op, n_qubits=n_qubits))
                   @ wfn)

    return wfn
    def _tensor_construct(self, rank, conjugates, updown):
        """
        General procedure for evaluating the expected value of second quantized ops

        :param Int rank: number of second quantized operators to product out
        :param List conjugates: Indicator of the conjugate type of the second
                                quantized operator
        :param List updown: SymmOrbitalDensity matrices are index by spatial orbital
                            Indices.  This value is self.dim/2 for Fermionic systems.
                            When projecting out expected values to form the marginals,
                            we need to know if the spin-part of the spatial basis
                            funciton.  updown is a list corresponding with the
                            conjugates telling us if we are projecting an up spin or
                            down spin.  0 is for up. 1 is for down.

                            Example: to get the 1-RDM alpha-block we would pass the
                            following:

                                rank = 2, conjugates = [-1, 1], updown=[0, 0]

        :returns: a tensor of (rank) specified by input
        :rytpe: np.ndarray
        """
        # self.dim/2 because rank is now spatial basis function rank
        tensor = np.zeros(tuple([int(self.dim / 2)] * rank), dtype=complex)
        of_con_upper = [
            1 if np.isclose(x, -1) else 0 for x in conjugates[:rank // 2]
        ]
        of_con_lower = [
            1 if np.isclose(x, -1) else 0 for x in conjugates[rank // 2:]
        ]
        updown_upper = updown[:rank // 2]
        updown_lower = updown[rank // 2:]
        for indices in product(range(self.dim // 2), repeat=rank):
            fop_u = [(2 * x + y, of_con_upper[idx]) for idx, (
                x, y) in enumerate(zip(indices[:rank // 2], updown_upper))]
            fop_l = [(2 * x + y, of_con_lower[idx]) for idx, (
                x, y) in enumerate(zip(indices[rank // 2:], updown_lower))]
            print(fop_u + fop_l[::-1])
            op = of.get_sparse_operator(of.FermionOperator(
                tuple(fop_u + fop_l[::-1])),
                                        n_qubits=self.dim)
            tensor[indices] = (op @ self.rho).diagonal().sum()
        return tensor
Example #29
0
    def map_state(self, state: list, *args, **kwargs) -> list:
        """
        Expects a state in spin-orbital ordering
        Returns the corresponding qubit state in the class encoding
        :param state:
            basis-state as occupation number vector in spin orbitals
            sorted as: [0_up, 0_down, 1_up, 1_down, ... N_up, N_down]
            with N being the number of spatial orbitals
        :return:
            basis-state as qubit state in the corresponding mapping
        """
        """Does a really lazy workaround ... but it works
        :return: Hartree-Fock Reference as binary-number

        Parameters
        ----------
        reference_orbitals: list:
            give list of doubly occupied orbitals
            default is None which leads to automatic list of the
            first n_electron/2 orbitals

        Returns
        -------

        """
        # default is a lazy workaround, but it workds
        n_qubits = 2 * self.n_orbitals

        spin_orbitals = sorted([i for i, x in enumerate(state) if int(x) == 1])

        string = "1.0 ["
        for i in spin_orbitals:
            string += str(i) + "^ "
        string += "]"

        fop = openfermion.FermionOperator(string, 1.0)
        op = self(fop)
        from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
        wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits)
        wfn = wfn.apply_qubitoperator(operator=op)
        assert (len(wfn.keys()) == 1)
        key = list(wfn.keys())[0].array
        return key
Example #30
0
    def reference_state(self,
                        reference_orbitals: list = None,
                        n_qubits: int = None) -> BitString:
        """Does a really lazy workaround ... but it works
        :return: Hartree-Fock Reference as binary-number

        Parameters
        ----------
        reference_orbitals: list:
            give list of doubly occupied orbitals
            default is None which leads to automatic list of the
            first n_electron/2 orbitals

        Returns
        -------

        """

        if reference_orbitals is None:
            reference_orbitals = [i for i in range(self.n_electrons // 2)]

        spin_orbitals = sorted([2 * i for i in reference_orbitals] +
                               [2 * i + 1 for i in reference_orbitals])

        if n_qubits is None:
            n_qubits = 2 * self.n_orbitals

        string = ""

        for i in spin_orbitals:
            string += str(i) + "^ "

        fop = openfermion.FermionOperator(string, 1.0)

        op = QubitHamiltonian(qubit_hamiltonian=self.transformation(fop))
        from tequila.wavefunction.qubit_wavefunction import QubitWaveFunction
        wfn = QubitWaveFunction.from_int(0, n_qubits=n_qubits)
        wfn = wfn.apply_qubitoperator(operator=op)
        assert (len(wfn.keys()) == 1)
        keys = [k for k in wfn.keys()]
        return keys[-1]