Hamiltonian simulation
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.
In demoLib, we can use 4 parameters to represent all kinds of spin interactions (including single-spin interaction and any two-spin interaction). We will see that later in an example.
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. Following is the content in the isQ file spin_singleX.isq
.
//spin_singleX.isq
import std;
import physics.spin;
procedure main(int N[], double t[]) {
qbit Q[1];
// allocate resources for variables
double F[100];
bool B[100];
int I[100];
Initialization(F,B,I); // initialized calculation
// set interactions type para1 para2 value
appendInteraction(F,B,I, 0, 0, 0 , 1.0 );
TimeEvolutionDeploy (F,B,I, Q, t[0] , N[0] );
M(Q);
}
appendInteraction
once. The list of all possible interactions is given by:
Interaction types
0 : X i = para1
1 : Y i = para1
2 : Z i = para1
3 : XX i = para1, j = para2
4 : XY i = para1, j = para2
5 : XZ i = para1, j = para2
6 : YY i = para1, j = para2
7 : YZ i = para1, j = para2
8 : ZZ i = para1, j = para2
type=0
represents the magnetic field in the x-direction. It needs only one position parameter i
, so para2 can be anything (here we set 0). We can compile this file by isqc compile spin_singleX.isq
, after which we should obtain a .so
file spin_singleX.so
. The .so
file can be sent to the simulator. One should notice that this file needs two additional parameters and .
Here we use a local simulator to test the result. isQ contains a default quantum simulator. More information about it can be found via isqc simulate --help
. Here we include some Python scripts to simplify the code. The following Python code runs the circuit many times to collect the data.
import demoLib
import numpy as np
import matplotlib.pyplot as plt
# load local simulator
sim = demoLib.simulator("spin_singleX.so")
N = 1 # N = 1 in Suzuki decomposition
T = np.arange(0,3.0,0.1) # range of t for plot
Y0 = []
Y1 = []
for t in T:
res = sim.runCircuit(F=[t],I=[N],shots=5000)
p0 = sim.calculate( res=res, func = lambda l: int(l[0] == 0) )
p1 = sim.calculate( res=res, func = lambda l: int(l[0] == 1) )
Y0.append(p0)
Y1.append(p1)
plt.plot(T,Y0,'-o',label='Prob(0)')
plt.plot(T,Y1,'-o',label='Prob(1)')
plt.xlabel("t")
plt.ylabel("Probability")
plt.legend()
import demoLib
is used to load the auxiliary package. The simulator needs an input of the so file. sim.runCircuit
will run the circuit to obtain the measurement. The function sim.calculate
is used to calculate any observable, where . The meaning of arguments passed in
sim.calculate
may be less obvious. One needs to input a definition of a function:
l
contains each bit value 0 or 1. The result is shown as follows:
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 . The isQ code file to translate this circuit is spin_chain.isq
whose content is shown by
// spin_chain.isq
import std;
import physics.spin;
procedure main( int N[], double t[] ) {
qbit Q[3]; //spin chain contains 3 sites
// allocate resource for variables
double F[100];
bool B[100];
int I[100];
// initialized calculation
Initialization(F,B,I);
// first qubit in |1>, others are |0>
X(Q[0]);
// set interactions type para1 para2 value
appendInteraction(F,B,I, 2, 0, 0 , -1.0 ); //hz0
appendInteraction(F,B,I, 2, 1, 0 , -1.0 ); //hz1
appendInteraction(F,B,I, 2, 2, 0 , -1.0 ); //hz2
appendInteraction(F,B,I, 3, 0, 1 , -0.2 ); //jx01
appendInteraction(F,B,I, 3, 1, 2 , -0.2 ); //jx12
appendInteraction(F,B,I, 6, 0, 1 , -0.3 ); //jy01
appendInteraction(F,B,I, 6, 1, 2 , -0.3 ); //jy12
appendInteraction(F,B,I, 8, 0, 1 , -0.1 ); //jz01
appendInteraction(F,B,I, 8, 1, 2 , -0.1 ); //jz12
TimeEvolutionDeploy (F,B,I, Q, t[0] , N[0] );
M (Q);
}
import demoLib
import numpy as np
import matplotlib.pyplot as plt
# load local simulator
sim = demoLib.simulator("spin_chain.so")
N = 20 # N = 1 in Suzuki decomposition
T = np.arange(0,10.0,0.1) # range of t for plot
Z0,Z1,Z2 = [],[],[]
for t in T:
res = sim.runCircuit(F=[t],I=[N],shots=5000)
z0 = sim.calculate(res=res, func = lambda l: 1-l[0]*2)
z1 = sim.calculate(res=res, func = lambda l: 1-l[1]*2)
z2 = sim.calculate(res=res, func = lambda l: 1-l[2]*2)
Z0.append(z0)
Z1.append(z1)
Z2.append(z2)
plt.plot(T,Z0,'-o',label='$\\langle \sigma_0 \\rangle$')
plt.plot(T,Z1,'-o',label='$\\langle \sigma_1 \\rangle$')
plt.plot(T,Z2,'-o',label='$\\langle \sigma_2 \\rangle$')
plt.legend()
plt.xlabel(r'$t$',size=15)
plt.ylabel(r'$\langle\sigma_z\rangle$',size=15)
plt.title(f'Dynamics of a Heisenberg spin chain (N={N})');
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 . For each , we can use at most 4 parameters (Type , , , ) to specify the term.
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
Example
In the following, we will give an example step by step showing coding with isQ. This example needs one to download the package demoLib first. Let us start!
Let study a 4-site system with Hamiltonian given by
where we have defined means . Therefore, the hopping terms are acting like a ring. Since there is the spin degree of freedom, we need to use 2 qubits to represent a single site in the Hubbard model. Therefore, we need to define 8 qubits by
We set the parameters as , , . The tedious progress for deploying is packaged insidedemoLib
. For instance, setting the hopping term between site 0 and site 1 is done by one-line code:
where F
, B
, and I
are source spaces, and the user need not care about it. The 4-th and 5-th variables represent the hopping sites. The last variable pass in the value of t
. As we can see, by our lib it is very easy to set a term in Hamiltonian. We set initial state as full filling the first site, as shown in Figure. The corresponding code is
Then we would like to study the wavefunction at any time . A full code that is runnable is given by
// hubbard_main.isq
import std;
import physics.hubbard;
procedure main(int intPara[] , double douPara[]) {
int N;
double T;
double t, U, m; // parameters in Hubbard model
qbit Q[8]; // use 8 qubit to encode 4 sites in Hubbard model
// allocate resource for storing Hamiltonian
double F[100];
bool B[1];
int I[200];
Initialization(F,B,I); // start calculation
t = -1.0; // hopping term
U = 2.0; // Coulomb U term
m = -1.0; // onsite energy
N = intPara[0]; // time slides
T = douPara[0]; // evolution time
// set there is a spin up particle on each site as the initial state
X(Q[0]);
X(Q[1]);
// set interactions
appendHubbardInteraction_Hopping(F,B,I, 0, 1, t ); //hopping between (0,1)
appendHubbardInteraction_Hopping(F,B,I, 1, 2, t ); //hopping between (1,2)
appendHubbardInteraction_Hopping(F,B,I, 2, 3, t ); //hopping between (2,3)
appendHubbardInteraction_Hopping(F,B,I, 0, 3, t ); //hopping between (0,3)
appendHubbardInteraction_OnSite(F,B,I, 0, m); // onsite energy on site 0
appendHubbardInteraction_OnSite(F,B,I, 1, m); // onsite energy on site 1
appendHubbardInteraction_OnSite(F,B,I, 2, m); // onsite energy on site 2
appendHubbardInteraction_OnSite(F,B,I, 3, m); // onsite energy on site 3
appendHubbardInteraction_CoulombU(F,B,I, 0, U); // U on site 0
appendHubbardInteraction_CoulombU(F,B,I, 1, U); // U on site 1
appendHubbardInteraction_CoulombU(F,B,I, 2, U); // U on site 2
appendHubbardInteraction_CoulombU(F,B,I, 3, U); // U on site 3
TimeEvolutionDeploy(F,B,I,Q,T,N); // deploy gates on circuit
M(Q);
}
T=0.5
and N=2
)
To visualize the result, we calculate the particle number of spin up on each site at any time. To plot the result, we use a more efficient simulator by the following Python script
import demoLib
import numpy as np
import matplotlib.pyplot as plt
import os
# load local simulator
sim = demoLib.simulator("hubbard_main.so")
N = 50 # Suzuki decomposition
T = np.arange(0,5.0,0.1) # range of t for plot
Y0,Y1,Y2,Y3 = [],[],[],[],
for t in T:
res = sim.runCircuit(F=[t],I=[N],shots=5000)
p0 = sim.calculate( res=res, func = lambda l: int(l[0] == 1) );Y0.append(p0)
p1 = sim.calculate( res=res, func = lambda l: int(l[2] == 1) );Y1.append(p1)
p2 = sim.calculate( res=res, func = lambda l: int(l[4] == 1) );Y2.append(p2)
p3 = sim.calculate( res=res, func = lambda l: int(l[6] == 1) );Y3.append(p3)
plt.plot(T,Y0,'-o',label='$N_{0\\uparrow}$')
plt.plot(T,Y1,'o' ,label='$N_{1\\uparrow}$')
plt.plot(T,Y2,'-o',label='$N_{2\\uparrow}$')
plt.plot(T,Y3,'--',label='$N_{3\\uparrow}$')
plt.xlabel("t")
plt.ylabel("particle number")
plt.legend()
plt.title(f'simulation with N = {N}')
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.