The
QubitOperatordatastructure in OpenFermion is the main point of contact between OpenFermion and Forest. Translation of theQubitOperatortoPauliTermsandPauliSumsis the interface that is constructed in the OpenFermion–Forest module.Fortunately, when traversing layers of abstraction in OpenFermion, the QubitOperator naturally appears in all types of simulations. Upon translation into the language of pyQuil, connections to the Forest-QVM or an alternative QVM (such as reference-qvm) that understand pyQuil
Programobjects can be initialized. The following demonstration starts with the interface between theQubitOperatordata structure and thePauliTermandPauliSumdata structures of pyQuil, and then demonstrates how to cnostruct and simulate Hamiltonians starting from OpenFermion.
In [1]:
from openfermion.ops import QubitOperator
from forestopenfermion import pyquilpauli_to_qubitop, qubitop_to_pyquilpauli
The interface contains 2 methods that mediate the translation of
PauliTermandPauliSumstoQubitOperatorsand vice-versa.
In [2]:
qubit_op = QubitOperator('X0 Y1 Z2')
pauli_term = qubitop_to_pyquilpauli(qubit_op)
print(pauli_term)
qubit_op_sum = QubitOperator('X1 Y5 Z2', coefficient=8) + QubitOperator('Y4 Z2', coefficient=1.5)
pauli_term_sum = qubitop_to_pyquilpauli(qubit_op_sum)
print(pauli_term_sum)
We can convert back from a
PauliSumobject to aQubitOperator
In [3]:
reversed_term = pyquilpauli_to_qubitop(pauli_term)
print(reversed_term.isclose(qubit_op)) # should return True
reversed_sum = pyquilpauli_to_qubitop(pauli_term_sum)
print(reversed_sum.isclose(qubit_op_sum)) # shuold return True
Let's generate the hopping terms for the Hubbard model Hamiltonian on 4-spatial sites.
In [4]:
from openfermion.ops import FermionOperator
from openfermion.transforms import jordan_wigner
from openfermion.utils import hermitian_conjugated
In [5]:
# we'll construct the Hamiltonian terms
hopping_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)))
hopping_hamiltonian += electron_hop_alpha + hermitian_conjugated(electron_hop_alpha)
hopping_hamiltonian += electron_hop_beta + hermitian_conjugated(electron_hop_beta)
We can turn the hopping hamiltonian into
QubitOperatorterms on2 * (spatial_orbital)qubits using the OpenFermion Jorda-Wigner routine. OpenFermion-Forest provides an interface to convert theQubitOperatorobjects into pyquil objects and generate a Quil program from their exponentiation. The Quil program was generated by taking each PauliTerm and converting to a set of gates according to arXiv:1306.3991. Once the user has data in the pyQuil format, more pyquil tools, such as a Trotterization engine, can be used.
In [6]:
from pyquil.quil import Program
from forestopenfermion import exponentiate
hopping_term_generator = jordan_wigner(hopping_hamiltonian)
pyquil_program = exponentiate(hopping_term_generator)
print(pyquil_program)
The returned value from
exponentiateis a pyQuilProgramobject. The object has some nice features such as a dagger function 🗡, easy classical control-flow construction, and introspection 🧐. The circuit can be simulated w/ or w/o noise by running on the Forest-QVM or on reference-qvm. In order to pyquil Programs on the Forest-QVM you'll need to sign up on the Forest Home Page for a key.
In [10]:
from pyquil.api import QVMConnection
qvm = QVMConnection()
wf = qvm.wavefunction(pyquil_program)
The resulting
Wavefunctionobject from pyQuil contains pretty printing features and the ability to access the wavefunction.
In [11]:
print(wf.amplitudes)
We can also pretty-print the wavefunction (on by default) which prints the amplitudes and bitstrings in an easy to read fromat.
In [12]:
print(wf)