Hamiltonian simulation
Requirement: isqdeployer
isqdeployer is an all-in-one Python package. It serves as an Object-Oriented Interface for isq
. You can easily install it using pip without the need for any additional installations or configurations.
Spin model
Wavefunction evolving with time
For a quantum system with Hamiltonian , its wavefunction is evolving as , where we have assume that . If we further have , then we have , where we have used the spectral decomposition of . This usually needs us to obtain a diagonalization of . Practically, a more efficient way [1] to simulate the time evolution is used if we have an efficient way to implement operators directly. Here we recall its major ideal.
For any Hamiltonian in the form of , we have , where for some large and therefore . Then we have the following approximation:
So the basic element for simulating the whole Hamiltonian is to simulate each term .
Basic gates in spin model
In spin model, the Hamiltonian can be represented as where at each spin site . Correspondingly the basic gate we need to implement in the circuit is in the form of for the pair of any two sites.
gate implementation
We first consider the term on one site. The corresponding implementation is given by
Similarly, we can directly implement and by Rx and Ry. These terms represent the effect of the magnetic field in each direction.
gate implementation
This is a two-spin interaction with action on one site and on the other site . We first consider the term . This term represents an Ising type of interaction between two spins. Its implementation is given by:
For this gate, we can calculate the output state of each input state in and see that it is equivalent to . Based on this gate, we can easily implement other types. For implementing , for instance, we can use the transformation , where acting on the first qubit (spin). Its implementation is shown by
By the same spirit, we can implement any terms.
Example: Single spin in the magnetic field
Consider a system with Hamiltonian , for input state , the evolution of the wavefunction is give by
We then simulate this process by quantum circuit.
from isqdeployer import Circuit
from isqdeployer.circuitDeployer import PauliHamDeployer
from isqdeployer.utils.pauliHamiltonian import PauliHamiltonian
import numpy as np
import matplotlib.pyplot as plt
ham = PauliHamiltonian(nq=1) # define a Hamiltonian
ham.setOneTerm(xi=1.0,p=[1]) # Hamiltonian = Pauli X, (0,1,2,3) represents (I,X,Y,Z)
cir = Circuit(
nq=1, # only 1 qubit
workDir="/home/user/Desktop/test", # see how it works by inspecting internal process
isInputArg=True, # use additional parameter (t) to increase the speed of the calculation
) # circuit, init state is |0>
t = cir.getInputArg(FI='F', id=0) # set the circuit to contains a parameter t
dep = PauliHamDeployer(circuit=cir) # use a deployer to implement gates
dep.expHt(
h=ham,
t=t,
N=15, # Suzuki decomposition
)
cir.setMeasurement([0])
Tlist = np.arange(0,3,0.1)# the time range we study
results = cir.runJob(paramList=[{'F':[t]} for t in Tlist ]) # batch submit all jobs
#---- plot
prob0 = [res.get('0',0) for res in results]
prob1 = [res.get('1',0) for res in results]
plt.plot(Tlist,prob0,label="Prob(0)")
plt.plot(Tlist,prob1,label="Prob(1)")
plt.xlabel("Time (t)")
plt.ylabel('Probability')
plt.legend()
plt.show()
workDir="/home/user/Desktop/test"
. You can observe intermediate resources in that directory. The original isq file is named resource.isq. The results are calculated using an internal default simulator and are displayed below:
Example: Spin chain model
We now study a less trivial model whose Hamiltonian is given by
The first term represents a uniform magnetic field on the system. The second term represents complex XYZ interactions. This model has very important applications in condensed matter physics.
In this demo, we set up a 3-site system. Parameters are set as , , , and . Here we do not consider the connection between the first and third sites. One interested in periodic boundary conditions can set additional interaction. In addition, in the beginning, we set the first spin in the state while others are in .
from isqdeployer import Circuit
from isqdeployer.circuitDeployer import PauliHamDeployer
from isqdeployer.utils.pauliHamiltonian import PauliHamiltonian
import numpy as np
import matplotlib.pyplot as plt
# --- define the Hamiltonian ---
ham = PauliHamiltonian(nq=3) # define a Hamiltonian
# --- hz term
ham.setOneTerm(xi=-1.,p=[3,0,0])
ham.setOneTerm(xi=-1.,p=[0,3,0])
ham.setOneTerm(xi=-1.,p=[0,0,3])
# ---jx term
ham.setOneTerm(xi=-0.2,p=[1,1,0])
ham.setOneTerm(xi=-0.2,p=[0,1,1])
# ---jy term
ham.setOneTerm(xi=-0.3,p=[2,2,0])
ham.setOneTerm(xi=-0.3,p=[0,2,2])
# ---jz term
ham.setOneTerm(xi=-0.1,p=[3,3,0])
ham.setOneTerm(xi=-0.1,p=[0,3,3])
cir = Circuit(nq=3,isInputArg=True,)
t = cir.getInputArg(FI='F', id=0) # set the circuit to contains a parameter t
cir.X(0) # set the first spin up
dep = PauliHamDeployer(circuit=cir) # use a deployer to implement gates
dep.expHt(
h=ham,
t=t,
N=50, # Suzuki decomposition
)
cir.setMeasurement([0,1,2])
Tlist = np.arange(0,10,0.1)# the time range we study
results = cir.runJob(paramList=[{'F':[t]} for t in Tlist ]) # batch submit all jobs
#---- plot
def countProb(i,res):
r = 0.0
for k, v in res.items():r += (k[i] == "0")*v
return r*2-1
Z0 = [ countProb(0,res) for res in results]
Z1 = [ countProb(1,res) for res in results]
Z2 = [ countProb(2,res) for res in results]
plt.plot(Tlist,Z0)
plt.plot(Tlist,Z1)
plt.plot(Tlist,Z2)
The result of the time evolution of each spin state is shown as follows:
As a comparison, an exact calculation by QuTip is shown in the figure. The precision can further increase by increasing . To be more clear, we check more carefully by the following figure.
As shown in the figure, for case, in a small time regime the result of the calculation matches the exact result very well. However, at large t regime, to obtain a better result we need a larger .
Any other spin mode
One can easily extend the code to make a circuit to simulate an arbitrary spin model with more complicated interaction types. For any (2-local) spin system, we can represent it as . An it can be calcualted by isqdeployer.
Fermion-Hubbard model
The Hubbard model is one of the most important systems in condensed matter physics. Studying this model will reveal much exotic physics in strongly correlated electron systems such as superconductivity and quantum Hall effect. In this tutorial, we study the time evolution of a wave function in the system with such a Hamiltonian.
Hamiltonian
We start from a most general Hamiltonian given by where
and
For a system with spins, the indices and should also be implicit. represents the single particle terms, such as onsite energy and hopping terms. contains many-body terms. For example, if we set and , we obtain the Coulomb interaction
In the most general case, can consider other interactions, such as Hund's interaction, and is written as
In the standard Hubbard, we consider and . Nevertheless, the following discussion is also suited for other two-particle interactions.
Our task is to find an encoding method to transform this Hamiltonian into one with Pauli tensors only. By Jordan–Wigner transformation , the diagonal term of is given by the hopping term (off-diagonal term) is given by
where , , and
and , and .
For real case, we have . Finally the Coulomb interaction is given by
To streamline configuration, we employ a specialized package, qsci, for deploying circuits in scientific calculations. One can install it by:
With qsci, it's straightforward to deploy various quantum algorithms such as VQE, Hubbard model, and many others into circuits with different types of backends.Example
In the following, we will provide a step-by-step example demonstrating coding with isqdeployer and qsci. Let's get started!
Let study a 4-site system with Hamiltonian given by
where we have defined means .We define the Hamiltonian of a Fermion system as follows:
from qsci.secondQuantization.hamiltonian.fermion import SpinfulHamiltonian
hamHubbard = SpinfulHamiltonian()
t=-1.0
U=2.0
mu=1.0
for i in range(4): hamHubbard.setHopping(i=i,j=(i+1)%4,tij=t) # set hopping
for i in range(4): hamHubbard.setCoulombU(i=i,U=U) # set U
for i in range(4): hamHubbard.setOnSiteEnergy(i=i,m=-mu) # set mu
Since there is the spin degree of freedom, we need to use 2 qubits to represent a single site in the Hubbard model. The initial state, as shown in figure, is set by:
Next, we need to choose the Jordan-Wigner (JW) transformation to encode the Hamiltonian into a circuit model:
from qsci.secondQuantization.qubitEncoding import JordanWignerEncoding
jw = JordanWignerEncoding() # use Jordan-Wigner encoding
ham = hamHubbard.exportPauliOperator(jw).exportPauliHamiltonian() # Pauli Hamiltonian
The following section is similar to previous examples, where the simulation of time evolution (controlled by the exp factor) is managed by isqdeployer. A complete runnable code is provided below:
from qsci.secondQuantization.hamiltonian.fermion import SpinfulHamiltonian
from qsci.secondQuantization.qubitEncoding import JordanWignerEncoding
from isqdeployer.circuit import Circuit
from isqdeployer.circuitDeployer.pauliHamiltonian import Deployer
import matplotlib.pyplot as plt
import numpy as np
#---- parameters of Hubbard model ----
t=-1.0
U=2.0
mu=1.0
#-------------- set Hubbard model Hamiltonian -------------------
hamHubbard = SpinfulHamiltonian()
for i in range(4): hamHubbard.setHopping(i=i,j=(i+1)%4,tij=t) # set hopping
for i in range(4): hamHubbard.setCoulombU(i=i,U=U) # set U
for i in range(4): hamHubbard.setOnSiteEnergy(i=i,m=-mu) # set mu
#-------------- encode Hamiltonian into Pauli type
jw = JordanWignerEncoding() # use Jordan-Wigner encoding
ham = hamHubbard.exportPauliOperator(jw).exportPauliHamiltonian() # Pauli Hamiltonian
#-------------- run on circuit
cir = Circuit(nq=8,isInputArg=True) # 4 site Hubbard model map into 8 qubits
cir.X(0).X(1) #-- set initial state
dpl = Deployer(circuit=cir)
dpl.expHt(h=ham,t=cir.getInputArg('F',0),N=50)
cir.setMeasurement(range(8))
#---- run job
tList = np.arange(0,5,0.1)
results = cir.runJob(paramList=[{'F':[t]} for t in tList])
#--- plot
def N(res,i):
r = 0
for k,v in res.items():
r += (k[2*i] == "1")*v
return r
niList = [ [N(res,i) for res in results] for i in range(4) ]
for i in range(4):plt.plot( tList, niList[i], '-o', label=f"$N_{{{i}\\uparrow}}$" )
plt.legend()
plt.title("simulation with N=50")
plt.xlabel("t")
plt.ylabel("particle number")
plt.show()
The results with and are shown below. We also calculate the result by exact method (classical simulation). As we can see, for the case, the result at a small time region agrees with the exact results very well. At the large time region, the time slide becomes large, therefore the error increase. As one can see from the geometry of the system, sites 1 and 3 are symmetric, therefore we know that . To obtain a better result, we can set as shown in the figure.
Reference
- S. Lloyd. “Universal Quantum Simulators.” Science 273 (5278), 1996:1073–78.
- J. Hubbard. "Electron Correlations in Narrow Energy Bands." Proc. R. Soc.Lond. A 276 (1365), 1963: 238–57.
- P. Jordan and E. Wigner. "Ber Das Paulische Quivalenzverbot." Z. Physik 47 (9-10), 1928: 631–51.