def exp_hamiltoniantrot_H2(time, atomic_distance, trotter_order):
    """
    Here we are using some packages related to quantum chemistry to obtain
    hamiltonian of H2 molecule. The we are translating this hamiltonian into
    sequence of pyquil gates. And finally, we are trotterazing the exponent
    of the hamiltonian (exp(-iHt)) of our system and returning the obtained
    pyquil program

    :param time: is t in the exp(iHt). Note the + sign.
    :param atomic_distance: the distance between to H atoms in H2 molecule

    :return: program of the Trotter-Suzuki decomposition of
             hamiltonian exp(iHt) for H2 molecule
    """

    geometry = [['H', [0, 0, 0]],
                ['H', [0, 0, atomic_distance]]]  # H--H distance = 0.74angstrom
    basis = 'sto-3g'
    multiplicity = 1  # (2S+1)
    charge = 0
    h2_molecule = MolecularData(geometry, basis, multiplicity, charge)
    h2_molecule = run_pyscf(h2_molecule)

    h2_qubit_hamiltonian = jordan_wigner(get_fermion_operator(h2_molecule.get_molecular_hamiltonian()))
    pyquil_h2_qubit_hamiltonian = qubitop_to_pyquilpauli(h2_qubit_hamiltonian)

    return trotterization(pyquil_h2_qubit_hamiltonian, float(time), trotter_order)
Пример #2
0
def one_particle(H: sparse.coo_matrix) -> pyquil.paulis.PauliSum:
    """
    Generates a PauliSum(pyquil) given a Hamiltonian-matrix.

    Creates a Hamiltonian operator from a (sparse) matrix. This function uses
    a one-particle formulation and, thus, requires N qubits for an N-dimensional
    Hilbert space.

    @author: Axel, Joel

    :param H: An array (array_like) representing the hamiltonian.
    :return: Hamiltonian as PyQuil PauliSum.
    """
    # Create the Hamiltonian with a and a^dagger
    Hamiltonian = FermionOperator()
    if sparse.issparse(H):
        H = H.tocoo()
        for i, j, data in zip(H.row, H.col, H.data):
            Hamiltonian += data * FermionOperator(((int(i), 1), (int(j), 0)))
    else:
        if not isinstance(H, np.ndarray):
            H = np.asanyarray(H)
        for i in range(H.shape[0]):
            for j in range(H.shape[1]):
                Hamiltonian += H[i, j] * FermionOperator(((i, 1), (j, 0)))

    Hamiltonian = jordan_wigner(Hamiltonian)
    Hamiltonian = qubitop_to_pyquilpauli(Hamiltonian)
    return Hamiltonian
Пример #3
0
def get_ground_energy(interaction_hamil, numLayer):
    fermionop_hamil = FermionOperator()
    for key in interaction_hamil:
        value = interaction_hamil[key]
        fermionop_hamil += FermionOperator(term=key, coefficient=value)

    qubitop_hamil = bravyi_kitaev(fermionop_hamil)
    pauliop_hamil = qubitop_to_pyquilpauli(qubitop_hamil)

    return solve_vqe(pauliop_hamil, numLayer)
Пример #4
0
    def get_exact_expectation_values(self, circuit, qubit_operator, **kwargs):
        if self.device_name != 'wavefunction-simulator' and self.n_samples!=None:
            raise Exception("Exact expectation values work only for the wavefunction simulator and n_samples equal to None.")
        cxn = get_forest_connection(self.device_name)

        # Pyquil does not support PauliSums with no terms.
        if len(qubit_operator.terms) == 0:
            return ExpectationValues(np.zeros((0,)))

        pauli_sum = qubitop_to_pyquilpauli(qubit_operator)
        expectation_values = cxn.expectation(circuit.to_pyquil(), pauli_sum.terms)
        if expectation_values.shape[0] != len(pauli_sum):
            raise(RuntimeError("Expected {} expectation values but received {}.".format(len(pauli_sum), expectation_values.shape[0])))
        return ExpectationValues(expectation_values)
Пример #5
0
def build_qaoa_circuit(params, hamiltonians):
    """Generates a circuit for QAOA. This is not only for QAOA proposed by Farhi
    et al., but also general ansatz where alternating layers of time evolution under 
    two different Hamiltonians H1 and H2 are considered.

    Args:
        hamiltonians (list):
            A list of dict or zquantum.core.qubitoperator.QubitOperator objects representing Hamiltonians
            H1, H2, ..., Hk which forms one layer of the ansatz
                    exp(-i Hk tk) ... exp(-i H2 t2) exp(-i H1 t1)
            For example, in the case of QAOA proposed by Farhi et al, the list the list is then
            [H1, H2] where
                H1 is the Hamiltonian for which the ground state is sought, and
                H2 is the Hamiltonian for which the time evolution act as a diffuser 
                    in the search space.
        params (numpy.ndarray): 
            A list of sets of parameters. Each parameter in a set specifies the time
            duration of evolution under each of the Hamiltonians H1, H2, ... Hk.
            
    Returns:
        zquantum.core.circuit.Circuit: the ansatz circuit
    """

    if mod(len(params), len(hamiltonians)) != 0:
        raise Warning('There are {} input parameters and {} Hamiltonians. Since {} does not divide {} the last layer will be incomplete.'.\
            format(len(params), len(hamiltonians), len(params), len(hamiltonians)))

    # Convert qubit operators from dicts to QubitOperator objects, if needed
    for index, hamiltonian in enumerate(hamiltonians):
        if isinstance(hamiltonian, dict):
            hamiltonians[index] = convert_dict_to_qubitop(hamiltonian)

    output = Circuit()

    # Start with a layer of Hadarmard gates
    n_qubits = count_qubits(hamiltonians[0])
    qubits = [Qubit(qubit_index) for qubit_index in range(n_qubits)]
    output.qubits = qubits
    for qubit_index in range(n_qubits):
        output.gates.append(Gate('H', (qubits[qubit_index], )))

    # Add time evolution layers
    for i in range(params.shape[0]):
        hamiltonian_index = mod(i, len(hamiltonians))
        current_hamiltonian = qubitop_to_pyquilpauli(
            hamiltonians[hamiltonian_index])
        output += time_evolution(current_hamiltonian, params[i])

    return output
Пример #6
0
def multi_particle(H: sparse.coo_matrix) -> pyquil.paulis.PauliSum:
    """
    Creates a Qubit-operator from a (sparse) matrix. This function uses
    (almost) all states and, thus, requires (approximately) log(N) qubits
    for an N-dimensional Hilbert space.

    The idea for converting a matrix element to an operator is to raise/lower
    the qubits differing between two states to convert one basis-state to the
    other. The qubits that are not negated must be checked to have the correct
    value (using an analogue of the counting operator) to not get extra terms
    (one could perhaps allow for extra terms and compensate for them later?).

    0 = up
    1 = down

    (1+Zi)/2 checks that qubit i is 0 (up)
    (1-Zi)/2 checks that qubit i is 1 (down)
    (Xi+1j*Yi)/2 lowers qubit from 1 (down) to 0 (up)
    (Xi-1j*Yi)/2 raises qubit from 0 (up) to 1 (down)

    @author: Joel

    :param H: An array (array-like) representing the hamiltonian.
    :return: Hamiltonian as PyQuil PauliSum.
    """
    # Convert to sparse coo_matrix
    if not sparse.issparse(H):
        H = sparse.coo_matrix(H)
    elif H.getformat() != "coo":
        H = H.tocoo()
    # The main part of the function
    H_op = QubitOperator()
    for i, j, data in zip(H.row, H.col, H.data):
        new_term = QubitOperator(())  # = I
        for qubit in range(int.bit_length(H.shape[0] - 1)):
            if (i ^ j) & (1 << qubit):
                # lower/raise qubit
                new_term *= QubitOperator((qubit, "X"), 1 / 2) + \
                            QubitOperator((qubit, "Y"),
                                          1j * (int(
                                              j & (1 << qubit) != 0) - 1 / 2))
            else:
                # check that qubit has correct value (same as i and j)
                new_term *= QubitOperator((), 1 / 2) + \
                            QubitOperator((qubit, "Z"),
                                          1 / 2 - int(j & (1 << qubit) != 0))
        H_op += data * new_term
    return qubitop_to_pyquilpauli(H_op)
Пример #7
0
def one_particle_ucc(h, reference=1, trotter_order=1, trotter_steps=1):
    """
    UCC-style ansatz preserving particle number.

    @author: Joel, Carl

    :param np.ndarray h: The hamiltonian matrix.
    :param int reference: the binary number corresponding to the reference
        state (which must be a Fock-state).
    :param int trotter_order: trotter order in suzuki_trotter
    :param int trotter_steps: trotter steps in suzuki_trotter
    :return: function(theta) which returns the ansatz Program
    """
    dim = h.shape[0]

    terms = []
    for occupied in range(dim):
        if reference & (1 << occupied):
            for unoccupied in range(dim):
                if not reference & (1 << unoccupied):
                    term = FermionOperator(((unoccupied, 1), (occupied, 0))) \
                           - FermionOperator(((occupied, 1), (unoccupied, 0)))
                    terms.append(qubitop_to_pyquilpauli(jordan_wigner(term)))

    exp_maps = trotterize(terms, trotter_order, trotter_steps)

    def wrap(theta):
        """
        Returns the ansatz Program.

        :param np.ndarray theta: parameters
        :return: the Program
        :rtype: pyquil.Program
        """
        prog = Program()
        for qubit in range(int.bit_length(reference)):
            if reference & (1 << qubit):
                prog += X(qubit)
        for idx, exp_map in enumerate(exp_maps):
            for exp in exp_map:
                prog += exp(theta[idx])
        return prog

    return wrap
Пример #8
0
def expectation(p1, p2, p3, multi=1):
    """ 
    Return UCC expectation value for a specified geometry 
    * First runs a psi4 ccsd calculation to get single and double
      amplitudes to use as ansatz for UCC
    * Generates a Hamiltonian for the specified geometry
    * Obtains expectation value using VQE 
    """
    geometry = [['O', p1], ['H', p2], ['H', p3]]
    molecule = MolecularData(geometry,
                             basis='sto-3g',
                             multiplicity=multi,
                             description=str(round(rad, 2)) + "_" +
                             str(round(ang, 2)))
    # Run Psi4.
    molecule = run_psi4(molecule, run_ccsd=1, run_fci=1)
    # Print out some results of calculation.
    print('\nRAD: {}, ANG: {}\n'.format(rad, ang))
    print('FCI energy: {} Hartree.'.format(molecule.fci_energy))

    singles_initial = molecule.ccsd_single_amps.flatten()
    doubles_initial = molecule.ccsd_double_amps.flatten()
    amps = np.concatenate((singles_initial, doubles_initial), axis=0)

    print("Compiling the Hamiltonian...")
    hamiltonian = jordan_wigner(
        get_fermion_operator(molecule.get_molecular_hamiltonian()))
    hamiltonian.compress()
    hamiltonian = qubitop_to_pyquilpauli(hamiltonian)
    print("Hamiltonian complete")

    vqe = VQE(minimizer=minimize,
              minimizer_kwargs={
                  'method': 'nelder-mead',
                  'options': {
                      'fatol': 1.5e-3
                  }
              })
    result = vqe.expectation(ansatz(amps), hamiltonian, None, qvm)
    print("VQE Expectation Value: {} Hartree".format(result))

    return result
Пример #9
0
def get_molecule_openfermion(molecule, eigen_index=0):
    # check if the molecule is in the molecules data directory
    if molecule.name + '.hdf5' in os.listdir(DATA_DIRECTORY):
        print("\tLoading File from {}".format(DATA_DIRECTORY))
        molecule.load()
    else:
        # compute properties with run_psi4
        molecule = run_psi4(molecule, run_fci=True)
        print("\tPsi4 Calculation Completed")
        print("\tSaved in {}".format(DATA_DIRECTORY))
        molecule.save()

    fermion_hamiltonian = molecule.get_molecular_hamiltonian()
    qubitop_hamiltonian = jordan_wigner(fermion_hamiltonian)
    psum = qubitop_to_pyquilpauli(qubitop_hamiltonian)
    ham = tensor_up(psum, molecule.n_qubits)
    if isinstance(ham, (csc_matrix, csr_matrix)):
        ham = ham.toarray()
    w, v = np.linalg.eigh(ham)
    gs_wf = v[:, [eigen_index]]
    n_density = gs_wf.dot(np.conj(gs_wf).T)
    return molecule, v[:, [eigen_index]], n_density, w[eigen_index]
from openfermion.utils import hermitian_conjugated
from forestopenfermion import qubitop_to_pyquilpauli, pyquilpauli_to_qubitop

from pyquil.quil import Program
from pyquil.gates import X
from pyquil.paulis import exponentiate
from pyquil.api import QVMConnection

hubbard_hamiltonian = FermionOperator()
spatial_orbitals = 4
for i in range(spatial_orbitals):
    electron_hop_alpha = FermionOperator(((2*i, 1), (2*((i+1) % spatial_orbitals), 0)))
    electron_hop_beta = FermionOperator(((2*i+1, 1), ((2 * ((i+1) % spatial_orbitals) + 1), 0)))
    hubbard_hamiltonian += -1 * (electron_hop_alpha + hermitian_conjugated(electron_hop_alpha))
    hubbard_hamiltonian += -1 * (electron_hop_beta + hermitian_conjugated(electron_hop_beta))
    hubbard_hamiltonian += FermionOperator(((2*i, 1), (2*i, 0), (2*i+1, 1), (2*i+1, 0)), 4.0)
hubbard_term_generator = jordan_wigner(hubbard_hamiltonian)
pyquil_hubbard_generator = qubitop_to_pyquilpauli(hubbard_term_generator)


localized_electrons_program = Program()
localized_electrons_program.inst([X(0), X(1)])
pyquil_program = Program()
for term in pyquil_hubbard_generator.terms:
    pyquil_program += exponentiate(0.1*term)
print (localized_electrons_program + pyquil_program)
qvm = QVMConnection()
print(qvm.run(pyquil_program, [0], 10))
wf = qvm.wavefunction(pyquil_program)
print(wf)
Пример #11
0
# Generate and populate instance of MolecularData.
molecule = MolecularData(geometry, basis, spin, description="h3")

molecule = run_pyscf(molecule,
                     run_scf=run_scf,
                     run_mp2=run_mp2,
                     run_cisd=run_cisd,
                     run_ccsd=run_ccsd,
                     run_fci=run_fci)

# Use a Jordan-Wigner encoding, and compress to remove 0 imaginary components
qubit_hamiltonian = jordan_wigner(molecule.get_molecular_hamiltonian())
qubit_hamiltonian.compress()

pauli_hamiltonian = qubitop_to_pyquilpauli(qubit_hamiltonian)

vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'})

# angle = 2.0
# result = vqe_inst.expectation(small_ansatz([angle]), pauli_hamiltonian, None, qvm)
# print(result)

angle_range = np.linspace(0.0, 2 * np.pi, 20)
data = [
    vqe_inst.expectation(small_ansatz([angle]), pauli_hamiltonian, None, qvm)
    for angle in angle_range
]

import matplotlib.pyplot as plt
plt.xlabel('Angle [radians]')
Пример #12
0
        # generate the spin-adapted classical coupled-cluster amplitude to use as the input for the
        # circuit
        packed_amps = uccsd_singlet_get_packed_amplitudes(
            molecule.ccsd_single_amps,
            molecule.ccsd_double_amps,
            molecule.n_qubits,
            molecule.n_electrons,
        )
        theta = packed_amps[-1]  # always take the doubles amplitude

        # now that we're done setting up the Hamiltonian and grabbing initial opt parameters
        # we can switch over to how to run things
        ucc_program = ucc_circuit(theta)

        paulis_bk_hamiltonian = qubitop_to_pyquilpauli(bk_hamiltonian)
        bk_mat = tensor_up(paulis_bk_hamiltonian, [0, 1])

        w, v = np.linalg.eigh(bk_mat)

        wf = qvm.wavefunction(ucc_program)
        wf = wf.amplitudes.reshape((-1, 1))

        tenergy = np.conj(wf).T.dot(bk_mat).dot(wf)[0, 0].real

        # observable = objective_fun(theta, hamiltonian=paulis_bk_hamiltonian, quantum_resource=qvm)
        observable = objective_fun(theta, hamiltonian=bk_mat, quantum_resource=qvm)

        result = minimize(
            objective_fun, x0=theta, args=(bk_mat, qvm), method="CG", options={"disp": True}
        )
Пример #13
0
    def __init__(self,
                 ansatz,
                 molecule,
                 mean_field=None,
                 backend_options={"backend": "wavefunction_simulator"}):
        """Initialize the settings for simulation.

        If the mean field is not provided it is automatically calculated.

        Args:
            ansatz (OpenFermionParametricSolver.Ansatze): Ansatz for the quantum solver.
            molecule (pyscf.gto.Mole): The molecule to simulate.
            mean_field (pyscf.scf.RHF): The mean field of the molecule.
        """

        # Check the ansatz
        assert (isinstance(ansatz, RigettiParametricSolver.Ansatze))
        self.ansatz = ansatz

        # Calculate the mean field if the user has not already done it.
        if not mean_field:
            mean_field = scf.RHF(molecule)
            mean_field.verbose = 0
            mean_field.scf()

            if not mean_field.converged:
                orb_temp = mean_field.mo_coeff
                occ_temp = mean_field.mo_occ
                nr = scf.newton(mean_field)
                energy = nr.kernel(orb_temp, occ_temp)
                mean_field = nr

        # Check the convergence of the mean field
        if not mean_field.converged:
            warnings.warn(
                "RigettiParametricSolver simulating with mean field not converged.",
                RuntimeWarning)

        self.molecule = molecule
        self.mean_field = mean_field

        # Initialize the amplitudes (parameters to be optimized)
        self.optimized_amplitudes = []

        # Set the parameters for Openfermion
        self.of_mole = self._build_of_molecule(molecule, mean_field)

        # Set the fermionic Hamiltonian
        self.f_hamiltonian = self.of_mole.get_molecular_hamiltonian()

        # Transform the fermionic Hamiltonian into qubit Hamiltonian
        self.qubit_hamiltonian = jordan_wigner(self.f_hamiltonian)
        self.qubit_hamiltonian.compress()
        # Also stores the Rigetti/Forest qubit Hamiltonian
        self.forest_qubit_hamiltonian = qubitop_to_pyquilpauli(
            self.qubit_hamiltonian)

        # Set the dimension of the amplitudes
        if ansatz == RigettiParametricSolver.Ansatze.UCCSD:
            no_occupied = int(np.ceil(molecule.nelectron / 2))
            no_virtual = len(mean_field.mo_energy) - no_occupied
            no_t1 = no_occupied * no_virtual
            no_t2 = no_t1 * (no_t1 + 1) / 2
            self.amplitude_dimension = int(no_t1 + no_t2)

        # Set the number of qubits
        self.n_qubits = self.of_mole.n_qubits

        # Instantiate backend for computation
        self.backend_options = dict()
        if ("backend" in backend_options) and (backend_options["backend"] !=
                                               "wavefunction_simulator"):
            self.backend_options["backend"] = get_qc(
                backend_options["backend"])
        else:
            self.backend_options["backend"] = WavefunctionSimulator()
        self.backend_options["n_shots"] = backend_options["n_shots"] if (
            "n_shots" in backend_options) else 1000
Пример #14
0
    def get_rdm(self):
        """Obtain the RDMs from the optimized amplitudes.

        Obtain the RDMs from the optimized amplitudes by using the
        same function for energy evaluation.
        The RDMs are computed by using each fermionic Hamiltonian term,
        transforming them and computing the elements one-by-one.
        Note that the Hamiltonian coefficients will not be multiplied
        as in the energy evaluation.
        The first element of the Hamiltonian is the nuclear repulsion
        energy term, not the Hamiltonian term.

        Returns:
            (numpy.array, numpy.array): One & two-particle RDMs (rdm1_np & rdm2_np, float64).

        Raises:
            RuntimeError: If no simulation has been run.
        """
        if len(self.optimized_amplitudes) == 0:
            raise RuntimeError(
                "Cannot retrieve RDM because no simulation has been run.")

        # Save our accurate hamiltonian
        tmp_hamiltonian = self.qubit_hamiltonian

        # Initialize the RDM arrays
        rdm1_np = np.zeros((self.of_mole.n_orbitals, ) * 2)
        rdm2_np = np.zeros((self.of_mole.n_orbitals, ) * 4)

        # Loop over each element of Hamiltonian (non-zero value)
        for ikey, key in enumerate(self.f_hamiltonian):
            length = len(key)
            # Treat one-body and two-body term accordingly
            if (length == 2):
                pele, qele = int(key[0][0]), int(key[1][0])
                iele, jele = pele // 2, qele // 2
            if (length == 4):
                pele, qele, rele, sele = int(key[0][0]), int(key[1][0]), int(
                    key[2][0]), int(key[3][0])
                iele, jele, kele, lele = pele // 2, qele // 2, rele // 2, sele // 2

            # Select the Hamiltonian element (Set coefficient to one)
            hamiltonian_temp = self.of_mole.get_molecular_hamiltonian()
            for jkey, key2 in enumerate(hamiltonian_temp):
                hamiltonian_temp[key2] = 1. if (key == key2
                                                and ikey != 0) else 0.

            # Qubitize the element
            qubit_hamiltonian2 = jordan_wigner(hamiltonian_temp)
            qubit_hamiltonian2.compress()

            # Overwrite with the temp hamiltonian
            self.qubit_hamiltonian = qubit_hamiltonian2
            self.forest_qubit_hamiltonian = qubitop_to_pyquilpauli(
                self.qubit_hamiltonian)

            # Calculate the energy with the temp hamiltonian
            opt_energy2 = self.simulate(self.optimized_amplitudes)

            # Put the values in np arrays (differentiate 1- and 2-RDM)
            if (length == 2):
                rdm1_np[iele, jele] = rdm1_np[iele, jele] + opt_energy2
            elif (length == 4):
                if ((iele != lele) or (jele != kele)):
                    rdm2_np[lele, iele, kele,
                            jele] = rdm2_np[lele, iele, kele,
                                            jele] + 0.5 * opt_energy2
                    rdm2_np[iele, lele, jele,
                            kele] = rdm2_np[iele, lele, jele,
                                            kele] + 0.5 * opt_energy2
                else:
                    rdm2_np[iele, lele, jele,
                            kele] = rdm2_np[iele, lele, jele,
                                            kele] + opt_energy2

        # Restore the accurate hamiltonian
        self.qubit_hamiltonian = tmp_hamiltonian

        return rdm1_np, rdm2_np
Пример #15
0
    def simulate(self, amplitudes):
        """Perform the simulation for the molecule.

        Args:
            amplitudes (list): The initial amplitudes (float64).
        Returns:
            float64: The total energy (energy).
        Raise:
            ValueError: If the dimension of the amplitude list is incorrect.
        """
        if len(amplitudes) != self.amplitude_dimension:
            raise ValueError("Incorrect dimension for amplitude list.")

        #Anti-hermitian operator and its qubit form
        generator = uccsd_singlet_generator(amplitudes, self.of_mole.n_qubits,
                                            self.of_mole.n_electrons)
        jw_generator = jordan_wigner(generator)
        pyquil_generator = qubitop_to_pyquilpauli(jw_generator)

        p = Program(Pragma('INITIAL_REWIRING', ['"GREEDY"']))
        # Set initial wavefunction (Hartree-Fock)
        for i in range(self.of_mole.n_electrons):
            p.inst(X(i))
        # Trotterization (unitary for UCCSD state preparation)
        for term in pyquil_generator.terms:
            term.coefficient = np.imag(term.coefficient)
            p += exponentiate(term)
        p.wrap_in_numshots_loop(self.backend_options["n_shots"])

        #  Do not simulate if no operator was passed
        if len(self.qubit_hamiltonian.terms) == 0:
            return 0.
        else:
            # Run computation using the right backend
            if isinstance(self.backend_options["backend"],
                          WavefunctionSimulator):
                energy = self.backend_options["backend"].expectation(
                    prep_prog=p, pauli_terms=self.forest_qubit_hamiltonian)
            else:
                # Set up experiment, each setting corresponds to a particular measurement basis
                settings = [
                    ExperimentSetting(in_state=TensorProductState(),
                                      out_operator=forest_term)
                    for forest_term in self.forest_qubit_hamiltonian.terms
                ]
                experiment = TomographyExperiment(settings=settings, program=p)
                print(experiment, "\n")
                results = self.backend_options["backend"].experiment(
                    experiment)

                energy = 0.
                coefficients = [
                    forest_term.coefficient
                    for forest_term in self.forest_qubit_hamiltonian.terms
                ]
                for i in range(len(results)):
                    energy += results[i].expectation * coefficients[i]

            energy = np.real(energy)

        # Save the amplitudes so we have the optimal ones for RDM calculation
        self.optimized_amplitudes = amplitudes

        return energy
Пример #16
0
def build_qaoa_circuit_grads(params, hamiltonians):
    """ Generates gradient circuits and corresponding factors for the QAOA ansatz
        defined in the function build_qaoa_circuit.

    Args:
        hamiltonians (list):
            A list of dict or zquantum.core.qubitoperator.QubitOperator objects representing Hamiltonians
            H1, H2, ..., Hk which forms one layer of the ansatz
                    exp(-i Hk tk) ... exp(-i H2 t2) exp(-i H1 t1)
            For example, in the case of QAOA proposed by Farhi et al, the list the list is then
            [H1, H2] where
                H1 is the Hamiltonian for which the ground state is sought, and
                H2 is the Hamiltonian for which the time evolution act as a diffuser 
                    in the search space.
        params (numpy.ndarray): 
            A list of sets of parameters. Each parameter in a set specifies the time
            duration of evolution under each of the Hamiltonians H1, H2, ... Hk.
            
    Returns:
        gradient_circuits (list of lists of zquantum.core.circuit.Circuit: the circuits)
        circuit_factors (list of lists of floats): combination coefficients for the expectation
            values of the list of circuits.
    """
    if mod(len(params), len(hamiltonians)) != 0:
        raise Warning('There are {} input parameters and {} Hamiltonians. Since {} does not divide {} the last layer will be incomplete.'.\
            format(len(params), len(hamiltonians), len(params), len(hamiltonians)))

    # Convert qubit operators from dicts to QubitOperator objects, if needed
    for index, hamiltonian in enumerate(hamiltonians):
        if isinstance(hamiltonian, dict):
            hamiltonians[index] = convert_dict_to_qubitop(hamiltonian)

    hadamard_layer = Circuit()

    # Start with a layer of Hadarmard gates
    n_qubits = count_qubits(hamiltonians[0])
    qubits = [Qubit(qubit_index) for qubit_index in range(n_qubits)]
    hadamard_layer.qubits = qubits
    for qubit_index in range(n_qubits):
        hadamard_layer.gates.append(Gate('H', (qubits[qubit_index], )))

    # Add time evolution layers
    gradient_circuits = []
    circuit_factors = []

    for index1 in range(params.shape[0]):

        hamiltonian_index1 = mod(index1, len(hamiltonians))
        current_hamiltonian = qubitop_to_pyquilpauli(
            hamiltonians[hamiltonian_index1])
        derivative_circuits_for_index1, factors = time_evolution_derivatives(
            current_hamiltonian, params[index1])
        param_circuits = []

        for derivative_circuit in derivative_circuits_for_index1:

            output_circuit = Circuit()
            output_circuit.qubits = qubits
            output_circuit += hadamard_layer

            for index2 in range(params.shape[0]):
                hamiltonian_index2 = mod(index2, len(hamiltonians))
                if index2 == index1:
                    output_circuit += derivative_circuit
                else:
                    current_hamiltonian = qubitop_to_pyquilpauli(
                        hamiltonians[hamiltonian_index2])
                    output_circuit += time_evolution(current_hamiltonian,
                                                     params[index2])
            param_circuits.append(output_circuit)

        circuit_factors.append(factors)
        gradient_circuits.append(param_circuits)

    return gradient_circuits, circuit_factors
Пример #17
0
from example2 import molecule

from openfermion.transforms import jordan_wigner, get_fermion_operator
from forestopenfermion import qubitop_to_pyquilpauli

from pyquil.quil import Program
from pyquil.gates import X
from pyquil.paulis import exponentiate
from pyquil.api import QVMConnection

from grove.pyvqe.vqe import VQE
from scipy.optimize import minimize

h2_qubit_hamiltonian = jordan_wigner(
    get_fermion_operator(molecule.get_molecular_hamiltonian()))
pyquil_hamiltonian = qubitop_to_pyquilpauli(h2_qubit_hamiltonian)
print(pyquil_hamiltonian)
#
# electrons_program = Program()
# electrons_program.inst([X(0), X(1)])
pyquil_program = Program()
for term in pyquil_hamiltonian.terms:
    pyquil_program += exponentiate(0.1 * term)
print(pyquil_program)
qvm = QVMConnection()
wf = qvm.wavefunction(pyquil_program)
print(wf)
vqe_inst = VQE(minimizer=minimize, minimizer_kwargs={'method': 'nelder-mead'})
result = vqe_inst.expectation(pyquil_program, pyquil_hamiltonian, None, qvm)
print(result)
Пример #18
0
def multi_particle_ucc(h, reference=0, trotter_order=1, trotter_steps=1):
    """
    UCC-style ansatz that doesn't preserve anything (i.e. uses all basis
    states). This is basically an implementation of create_arbitrary_state
    (however, theta will not match the coefficients in the superposition)
    built on pyquil.paulis.exponentiate_map.

    One idea is to use lowering and raising operators and "check operators"
    instead of Xs. This results in a more straight-forward mapping of theta
    to the coefficients since no previously produced state will be mapped
    to other states in a later stage. To clarify: with the current
    implementation the state |1 1 0> will both be produced by exp(X2 X1)
    operating on |0 0 0> and by exp(X2) operating on exp(X1)|0 0 0> (which
    contains a |0 1 0> term). This could improve potential bad properties
    with the current implementation. However, it might be difficult to
    create commuting hermitian terms, which is required in
    exponential_map_commuting_pauli_terms.

    If this function is called multiple times, particularly if theta has the
    same length in all calls, caching terms might significantly increase
    performance.

    @author: Joel

    :param np.ndarray h: The hamiltonian matrix.
    :param reference: integer representation of reference state
    :param trotter_order: Trotter order in trotterization
    :param trotter_steps: Trotter steps in trotterization
    :return: function(theta) which returns the ansatz Program. -1j*theta[i] is
        the coefficient in front of the term prod_k X_k^bit(i,k) where
        bit(i, k) is the k'th bit of i in binary, in the exponent.
    """
    dim = h.shape[0]

    terms = []
    for state in range(dim):
        if state != reference:
            term = QubitOperator(())
            for qubit in range(int.bit_length(state)):
                if (state ^ reference) & (1 << qubit):
                    # lower/raise qubit
                    term *= QubitOperator((qubit, "X"), 1 / 2) \
                            + QubitOperator((qubit, "Y"), 1j * (int(
                                reference & (1 << qubit) != 0) - 1 / 2))
                else:
                    # check that qubit has correct value (same as i and j)
                    term *= QubitOperator((), 1 / 2) \
                            + QubitOperator((qubit, "Z"), 1 / 2 - int(
                                reference & (1 << qubit) != 0))
            terms.append(
                qubitop_to_pyquilpauli(term - hermitian_conjugated(term)))

    exp_maps = trotterize(terms, trotter_order, trotter_steps)

    def wrap(theta):
        """
        Returns the ansatz Program.

        :param np.ndarray theta: parameters
        :return: the Program
        :rtype: pyquil.Program
        """
        prog = Program()
        for qubit in range(int.bit_length(reference)):
            if reference & (1 << qubit):
                prog += X(qubit)
        for idx, exp_map in enumerate(exp_maps):
            for exp in exp_map:
                prog += exp(theta[idx])
        return prog

    return wrap