VQE for the Transverse-Field Ising Model with Amazon Braket
This Amazon Braket notebook applies the Variational Quantum Eigensolver (VQE) to the transverse-field Ising model, a paradigmatic model of quantum magnetism and a standard benchmark for quantum simulation. The transverse-field Ising model describes a chain of spins with nearest-neighbor interactions competing against a transverse magnetic field, and it famously exhibits a quantum phase transition as the field strength is varied. VQE finds the model's ground-state energy by preparing a parameterized trial state on the quantum computer, measuring the expectation value of the Ising Hamiltonian, and using a classical optimizer to minimize that energy, relying on the variational principle to bound the true ground state from above. The notebook constructs the Ising Hamiltonian as a sum of Pauli terms, builds a parameterized ansatz circuit with the Braket SDK, evaluates the energy by measuring each term on a Braket simulator, and runs the classical optimization loop to converge to the ground-state energy. By comparing the result against exact diagonalization for small systems, it validates the approach and illustrates how variational methods probe the physics of interacting spin systems. It is a clear, hands-on example of using VQE for quantum many-body simulation on Amazon Braket.
# --- Setup cell added by QCR (not part of the original tutorial) ---# Source: amazon-braket/amazon-braket-examples @ 0c0818f315479aab9deebed7e7ed7533ac581923, Apache License 2.0.# Installs the example's dependencies. If a later cell still reports a missing# package, restart the runtime/kernel and run again from the top.
%pip install -q amazon-braket-sdk==1.117.3 matplotlib
In [1]:
from braket.tracking import Tracker
t = Tracker().start()
SOLVING THE TRANSVERSE ISING MODEL WITH VQE
In this tutorial we show how to solve for the ground state of the Transverse Ising Model, arguably one of the most prominent, canonical quantum spin systems, using the variational quantum eigenvalue solver (VQE).
The VQE algorithm belongs to the class of hybrid quantum algorithms (leveraging both classical as well as quantum compute), that are widely believed to be the working horse for the current NISQ (noisy intermediate-scale quantum) era.
To validate our approach we benchmark our results with exact results as obtained from a Jordan-Wigner transformation.
We provide a step-by-step walkthrough explaining the VQE quantum algorithm and show how to build the corresponding parametrized quantum circuit ansatz using the Braket SDK, with simple modular building blocks (that can be re-used for other purposes).
While we demonstrate our proof-of-concept approach using classical simulators for circuit execution, our code could in principle be run on actual quantum hardware by simply changing the definition of the device object.
BACKGROUND: THE VARIATIONAL QUANTUM EIGENSOLVER (VQE)
Quantum computers hold the promise to outperform even the most-powerful classical computers on a range of computational problems in (for example) optimization, chemistry, material science and cryptography.
The canonical set of quantum algorithms (such as Shor's or Grover's quantum algorithms), however, comes with hardware requirements (such as a large number of quantum gates) that are currently not available with state-of-the-art technology.
Specifically, these algorithms are typically believed to be feasible only with fault-tolerance as provided by quantum error correction.
In the current noisy intermediate-scale (NISQ) era, near-term quantum computers do not have a large enough number of physical qubits for the implementation of error correction protocols, making this canonical set of quantum algorithms unsuitable for near-term devices. Against this background, the near-term focus has widely shifted to the class of hybrid quantum algorithms that do not require quantum error correction.
In these hybrid quantum algorithms are the noisy near-term quantum computers are used as co-processors only, within a larger classical optimization loop, as sketched in the schematic figure below.
Here, the undesired effects of noise are suppressed by deliberately limiting the quantum circuits on the quantum processing unit (QPU) to short bursts of the calculation, and the need for long coherence times (as required for the standard set of quantum algorithms) is traded for a classical overhead due to (possibly many) measurement repetitions and (essentially error-free) classical processing.
Variational Quantum Algorithms: Specifically, variational quantum algorithms such as the Variational Quantum Eigensolver (VQE) [1, 2] or the Quantum Approximate Optimization Algorithm (QAOA) [3] belong to this emerging class of hybrid quantum algorithms.
These are widely believed to be promising candidates for the demonstration of a quantum advantage, already with near-term (NISQ) devices in areas such as quantum chemistry [4], condensed matter simulations [5], and discrete optimization tasks [6].
Variational Quantum Computing vs. Deep Learning: The working principle of variational quantum computing is very much reminiscent of training deep neural networks:
When you train a neural network, you have an objective function that you want to minimize, typically characterized by the error on your training set.
To minimize that error, typically you start out with an initial guess for the weights in your network.
The coprocessor, in that case a GPU, takes these weights which define the exact operation to execute and the output of the neural network is computed.
This output is then used to calculate the value of your objective function, which in turn is used by the CPU to make an educated guess to update the weights and the cycle continues.
Variational quantum algorithms, a specific form of hybrid algorithms, work in the very same way, using parametrized quantum circuits rather than parametrized neural networks and replacing the GPU with a QPU.
Here, you start with an initial guess for the parameters that define your circuit, have the QPU execute that circuit, perform measurements to calculate an objective function, pass this value (together with the current values of the parameters) back to the CPU and have this classical CPU update the parameters based on that information.
Of course, coordinating that workflow for quantum computers is much more challenging than in the previous case. Quantum computers are located in specialized laboratory facilities, are typically single threaded, and have special latency requirements.
This is exactly the undifferentiated heavy-lifting that Amazon Braket takes away for you such that we can focus on our scientific problem.
For the sake of this introductory tutorial, we simply use a classical circuit simulator (that mimic the behaviour of a quantum machine) as device to execute our quantum circuits.
Within Amazon Braket, the workflow, however, is exactly the same.
BACKGROUND: THE TRANSVERSE ISING MODEL
While VQE is a very general approach, for concreteness we will focus on applying VQE to the one-dimensional Transverse Ising Model (TIM). The TIM belongs to a the broader class of many-body spin systems that are inherently hard to study on classical computers as the dimension of the Hilbert space grows exponentially with the number of particles in the system. With the help of a quantum computer, however, we can study these many-body systems with less overhead as the number of qubits required only grows polynomially. Even more so, the specific TIM is an integrable system and can be solved exactly, as shown in [7]. We will use these exact results as a benchmark for our approximate VQE results.
The transverse field Ising model is a quantum version of the classical Ising model that describes a lattice of spins with nearest neighbour interactions of strength (as set by the alignment or anti-alignment of spin projections along the axis), as well as an external magnetic field along the axis with strength , creating an energetic bias for one x-axis spin direction over the other.
In one dimension, the Hamiltonian describing the TIM for spin- particles reads:
Transforming the spin variables to qubits, we obtain:
Here, denotes the strength of the transverse magnetic field (in units of the hopping matrix element that we have set to unity).
Symmetries and Phases: The Hamiltonian possesses a symmetry, as it is invariant under the unitary transformation of flipping all qubits along the -direction by an angle of . Formally, this property can be expressed as , since and , with being a global rotation around the -axis by angle .
The 1D model then allows for two phases, depending on whether the ground state breaks or preserves this global spin-flip symmetry [8]:
Ordered Phase: When the transverse field is small, the system is in the ordered phase. In this phase the ground state breaks the spin-flip symmetry. Thus, the ground state is in fact two-fold degenerate.
Mathematically, if is a ground state of the Hamiltonian, then is a ground state as well. Taken together, these two distinct states span the degenerate ground state space.
Consider the following example for : In this case, the ground state space is spanned by the states and , that is, with all the qubits aligned along the axis.
Disordered Phase: In contrast, when , the system is in the disordered phase. Here, the ground state does preserve the spin-flip symmetry, and is nondegenerate (as opposed to the ordered phase discussed above).
Consider the following example when : Here, the ground state is simply the state aligned with the external magnetic field, , with every qubit (spin) pointing in the direction.
There is a quantum phase transition at separating these two phases.
IMPORTS and SETUP
In [2]:
# general importsimport time
from datetime import datetime
import matplotlib.pyplot as plt
import numpy as np
from scipy.optimize import minimize
# magic word for producing visualizations in notebook
%matplotlib inline
In [3]:
# Ensure consistent results
np.random.seed(0)
# Flag to trigger writing results plot to file
SAVE_FIG = False
# Set up device: Local Simulator
device = LocalSimulator()
In [ ]:
# example code for other backends# from braket.aws import AwsDevice# from braket.devices import Devices# choose the on-demand simulator to run your circuit# device = AwsDevice(Devices.Amazon.SV1)# choose the Ionq device to run your circuit# device = AwsDevice(Devices.IonQ.ForteEnterprise1)# choose the IQM device to run your circuit# device = AwsDevice(Devices.IQM.Garnet)# choose the Rigetti device to run your circuit# device = AwsDevice(Devices.Rigetti.Cepheus1108Q)
PROBLEM SETUP
In this section we develop a set of useful helper functions that we will explain in detail below.
Specifically we provide simple building blocks for the core modules of our VQE algorithm, that is (i) a function called circuit that defines the parametrized ansatz, (ii) a function called objective_function that takes a list of variational parameters as input, and returns the cost associated with those parameters and finally (iii) a function train to run the entire VQE algorithm for given ansatz.
This way we can solve the problem in a clean and modular approach.
In [7]:
# helper function to set up interaction termdefget_ising_interactions(n_qubits):
"""Function to setup Ising interaction term"""# set number of qubits
ising = np.zeros((n_qubits, n_qubits))
# set nearest-neighbour interactions to nonzero values onlyfor ii inrange(n_qubits - 1):
ising[ii][ii + 1] = -1# add periodic boundary conditions
ising[0][n_qubits - 1] = -1print("Ising matrix:\n", ising)
return ising
In [8]:
# function to build the VQE ansatzdefcircuit(params, n_qubits):
"""Function to return full VQE circuit ansatz
input: parameter list with three parameters
"""# instantiate circuit object
circuit = Circuit()
# add Hadamard gate on first qubit
circuit.rz(0, params[0]).ry(0, params[1])
# apply series of CNOT gatesfor ii inrange(1, n_qubits):
circuit.cnot(control=0, target=ii)
# add parametrized single-qubit rotations around yfor qubit inrange(n_qubits):
gate = Circuit().ry(qubit, params[2])
circuit.add(gate)
return circuit
# function that computes cost function for given paramsdefobjective_function(params, J, b_field, n_qubits, n_shots, verbose=False):
"""Objective function takes a list of variational parameters as input,
and returns the cost associated with those parameters
"""global CYCLE
CYCLE += 1if verbose:
print("==================================" * 2)
print("Calling the quantum circuit. Cycle:", CYCLE)
# obtain a quantum circuit instance from the parameters
vqe_circuit = circuit(params, n_qubits)
circuit_zz = Circuit(vqe_circuit).sample(observables.Z())
# run the circuit on appropriate device
task_zz = device.run(circuit_zz, shots=n_shots)
# Hb term: construct the circuit for measuring in the X-basis
circuit_b = Circuit(vqe_circuit).sample(observables.X())
# run the circuit (with H rotation at end)
task_b = device.run(circuit_b, shots=n_shots)
# Collect results from devices (wait for results, if necessary)
result_zz = task_zz.result()
result_b = task_b.result()
# Compute Hzz term# Hzz term: get approx energy expectation value (factor 1/4 for Pauli vs spin 1/2)
expectations_zz = np.array(result_zz.values[0])
energy_expect_zz = 0.25 * np.einsum("ik,ij,jk", expectations_zz, J, expectations_zz) / n_shots
# Compute Hb term# Hb term: get approx energy expectation value (factor 1/2 for Pauli vs spin 1/2)
energy_expect_b = -1 * b_field / 2 * np.array(result_b.values[0]).sum() / n_shots
# get total energy expectation value
energy_expect = energy_expect_zz + energy_expect_b
# per site
energy_expect_ind = energy_expect / n_qubits
# print energy expectation valueif verbose:
print("Energy expectation value:", energy_expect)
print("Energy expectation value (per particle):", energy_expect_ind)
return energy_expect
# The function to execute the training: run classical minimization.deftrain(J, b_field, options, n_qubits, n_shots, n_initial=10, verbose=False):
"""Function to run VQE algorithm with several random seeds for initialization"""print("Starting the training.")
if verbose:
print("==================================" * 3)
print("Running VQE OPTIMIZATION.")
# initialize vectors for results per random seed
cost_energy = []
angles = []
# optimize for different random initializations: avoid local optimafor ii inrange(n_initial):
# print counterif verbose:
run_init = ii + 1print("Running VQE OPTIMIZATION for random seed NUMBER", run_init)
# randomly initialize variational parameters within appropriate bounds
params0 = np.random.uniform(0, 2 * np.pi, 3).tolist()
# set bounds for search space# run classical optimization
result = minimize(
objective_function,
params0,
args=(J, b_field, n_qubits, n_shots, verbose),
options=options,
method="COBYLA",
# bounds=bnds
)
# store result of classical optimization
result_energy = result.fun
cost_energy.append(result_energy)
result_angle = result.x
angles.append(result_angle)
if verbose:
print("Optimal avg energy:", result_energy)
print("Optimal angles:", result_angle)
# reset cycle countglobal CYCLE
CYCLE = 0# store energy minimum (over different initial configurations)
energy_min = np.min(cost_energy)
optim_angles = angles[np.argmin(cost_energy)]
if verbose:
print("Energy per initial seeds:", cost_energy)
print("Minimal energy:", energy_min)
print("Optimal variational angles:", optim_angles)
return energy_min
Illustration of the VQE ansatz
VQE ansatz: VQE tries to find the lowest energy configuration of a given Hamiltonian, such as that of a chemical system or some many-body spin system (as studied here). Being a variational quantum-classical algorithm, VQE uses the QPU for state preparation and measurement subroutines, and the classical computer to post-process the measurement results and update the parametrized VQE ansatz according to an update rule such as gradient descent.
VQE, however, does not come without limitations.
Akin to what happens in deep learning, the quality of our results will depend very much on the trial architecture of our circuit (i.e., the trial wave function) and its the expressive power.
In other words, to approximate the ground state we are looking for, VQE can only operate within the bounds of the general ansatz we are using (the so-called ansatz space), by having a quantum computer prepare this very ansatz state with a parameterized gate sequence, and then have a classical optimizer iteratively update the optimal parameters.
Here, we will be guided by physical intuition and symmetry arguments to make an educated guess for our variational ansatz.
In general, VQE makes us of the variational principle, by preparing a parametrized trial wavefunction , and trying to find the optimal set of parameters , according to the following objective
Here, denotes the (true) lowest energy eigenvalue of the Hamiltonian .
Since (for sufficiently large systems with more than qubits) classical computers are unable to efficiently prepare, store and measure the wavefunction, we use the quantum computer for this subroutine.
We then use the classical computer to iteratively update the parameters using some optimization algorithm [9].
VQE ansatz for TIM: Next, we need to choose an ansatz appropriate for the system under study, the Transverse Ising Model (TIM).
We use an ansatz that can account for quantum entanglement.
Because of the symmetry discussed above, for any given state the rotated state is a degenerate state with the same energy.
Therefore, we take our ansatz as a linear superposition of these two degenerate states, . How can we prepare such a state on a quantum computer? We use the following sequence of parametrized gates [10]:
First, starting from we apply a general single qubit rotation to the first qubit to obtain the state
where the parameters can be learned in the training process.
Then, we apply a sequence of CNOT gates as is done for the preparation of GHZ states.
The first CNOT between the first and second qubits prepares the state
We continue with CNOT gates till we arrive at the parametrized ansatz
Finally, to account for different polarization directions we apply parametrized single qubit rotations around the axis to arrive at our VQE trial ansatz state
with .
Illustration: Below, we illustrate our ansatz (as prepared by our method circuit) with a circuit diagram for a small number of qubits and a fixed set of classical parameters. We print both our VQE ansatz as well as the modified circuit with a layer of single-qubit Hadamard gates H attached at the end, as needed to measure the projection with for all qubits. The latter is needed to compute the expectation value for the transverse field term . With this simple visualization we can convince ourselves that we have implemented the circuit ansatz as desired.
In [9]:
# visualize VQE circuit example
N = 4
params = [0.1, 0.2, 0.3]
vqe_circuit = circuit(params, N)
print("1. Printing VQE test circuit:")
print(vqe_circuit)
# Hb term: construct the circuit for measuring in the X-basisprint()
print("2. Apply Hadamard to measure in x-basis:")
print(Circuit(vqe_circuit).h(range(N)))
We are now ready to run some VQE simulation experiments. First of all, you can play and experiment yourself with the number of qubits . Secondly, you may also experiment with the classical optimizer. Since we are using an off-the-shelf scipy minimizer (as described in more detail here), you can simply swap between different optimizers by setting the method parameter accordingly, as done above in the line result = minimize(..., method='SLSQP'). Some popular options readily available within this library include Nelder-Mead, BFGS and COBYLA.
As a precautionary warning, note that the classical optimization step may get stuck in a local optimum, rather than finding the global minimum for our parametrized VQE ansatz wavefunction.
To address this issue, we run several optimization loops, starting from different random parameter seeds.
You can set the number of these optimization loops using the n_initial parameter, as shown below.
While this brute-force approach does not provide any guarantee to find the global optimum, from a pragmatic point of view at least it does increase the odds of finding an acceptable solution, at the expense of potentially having to run many more circuits on the QPU.
For a more detailed and sophisticated discussion of classical optimization of VQE we refer to Ref.[11].
In [10]:
# set up the problem
SHOTS = 1_000
N = 4# number of qubits
n_initial = 2# number of random seeds to explore optimization landscape
verbose = False# control amount of print output# set counters
CYCLE = 0# set up Ising matrix with nearest neighbour interactions and PBC
J = get_ising_interactions(N)
# set options for classical optimization
options = {"maxiter": 20}
if verbose:
options["disp"] = True# kick off training
start = time.time()
# parameter scan
xvalues = np.arange(0.01, 2.2, 0.2)
results = []
results_site = []
for bb in xvalues:
b_field = bb
print("Strength of magnetic field:", b_field)
energy_min = train(
J,
b_field,
options=options,
n_qubits=N,
n_shots=SHOTS,
n_initial=n_initial,
verbose=verbose,
)
results.append(energy_min)
results_site.append(energy_min / N)
# reset counters
CYCLE = 0
end = time.time()
# print execution timeprint("Code execution time [sec]:", end - start)
# print optimized resultsprint("Optimal energies:", results)
print("Optimal energies (per site):", results_site)
Ising matrix:
[[ 0. -1. 0. -1.]
[ 0. 0. -1. 0.]
[ 0. 0. 0. -1.]
[ 0. 0. 0. 0.]]
Strength of magnetic field: 0.01
Starting the training.
Strength of magnetic field: 0.21000000000000002
Starting the training.
Strength of magnetic field: 0.41000000000000003
Starting the training.
Strength of magnetic field: 0.6100000000000001
Starting the training.
Strength of magnetic field: 0.81
Starting the training.
Strength of magnetic field: 1.01
Starting the training.
Strength of magnetic field: 1.2100000000000002
Starting the training.
Strength of magnetic field: 1.4100000000000001
Starting the training.
Strength of magnetic field: 1.61
Starting the training.
Strength of magnetic field: 1.81
Starting the training.
Strength of magnetic field: 2.01
Starting the training.
Code execution time [sec]: 13.935059070587158
Optimal energies: [-1.00077, -1.05113, -1.17539, -1.3623600000000002, -1.70419, -2.04102, -2.4217400000000002, -2.85636, -3.2278000000000002, -3.65519, -4.039]
Optimal energies (per site): [-0.2501925, -0.2627825, -0.2938475, -0.34059000000000006, -0.4260475, -0.510255, -0.6054350000000001, -0.71409, -0.8069500000000001, -0.9137975, -1.00975]
In [11]:
# plot the VQE results for the energy per site
plt.plot(xvalues, results_site, "m--o")
plt.xlabel("transverse field $B [J]$")
plt.ylabel("groundstate energy per site $E_{0}/N [J]$")
plt.show()
BENCHMARKING OUR VQE ANSATZ WITH EXACT RESULTS
As detailed in the seminal paper by [7], the paradigmatic TIM can be solved with the help of a (highly non-local) Jordan-Wigner transformation that expresses the spin (qubit) variables as fermionic variables, and leading to a sum of local quadratic terms containing fermionic creation and annihilation operators.
The resulting Hamiltonian is mathematically identical to that of a superconductor in the mean field Bogoliubov deGennes formalism and can be completely understood in the same standard way.
Specifically, the exact excitation spectrum and eigenvalues can be determined by Fourier transforming into momentum space and diagonalizing the Hamiltonian.
Here, we just use the known results from Ref.[7] to recover the exact ground-state energy with the helper function defined below, and refer the interested reader to the broad set of literature on the TIM for further details. The original paper is available online here.
In [12]:
# helper function to numerically solve for gs energy of TIMdefnum_integrate_gs(B):
"""Numerically integrate exact band to get gs energy of TIM
this should give -E_0/(N*J) by Pfeufy
Here set J=1 (units of energy)
"""# lamba_ratio (setting J=1): compare thesis
ll = 1 / (2 * B)
# set energy
gs_energy = 0# numerical integration
step_size = 0.0001
k_values = np.arange(0, np.pi, step_size)
integration_values = [step_size * np.sqrt(1 + ll**2 + 2 * ll * np.cos(kk)) for kk in k_values]
integral = np.sum(integration_values)
gs_energy = 1 * integral / (4 * np.pi * ll)
return gs_energy
In [13]:
# plot exact gs energy of TIM vs VQE results
x = np.arange(0.01, 2.2, 0.2)
y = [-1 * num_integrate_gs(xx) for xx in x]
# plot exact results
plt.plot(x, y, "b--s", label="exact")
# plot vqe results
plt.plot(xvalues, results_site, "m--o", label="VQE")
plt.xlabel("magnetic field $B [J]$")
plt.ylabel("groundstate energy $E_{0}/N [J]$")
plt.tight_layout()
plt.legend()
# save figureif SAVE_FIG:
time_now = datetime.strftime(datetime.now(), "%Y%m%d%H%M%S")
filename = "vqe_tim_gs-energy_" + time_now + ".png"
plt.savefig(filename, dpi=700)
As shown above, for sufficiently large number of seeds (n_initial) we approximate the exact results reasonably well with our relatively simple three-parameter VQE ansatz, in the whole magnetic field region under consideration.
REFERENCES
[1] A. Peruzzo, J. McClean, P. Shadbolt, M.-H. Yung, X.-Q. Zhou, P. J. Love, A. Aspuru-Guzik, and J. L. Obrien, Nature communications 5, 56 (2014).
[2] J. R. McClean, J. Romero, R. Babbush, and A. Aspuru-Guzik, The theory of variational hybrid quantum-classical algorithms, New Journal of Physics 18, 023023 (2016).
[3] E. Farhi, J. Goldstone, and S. Gutmann, arXiv 1411.4028 (2014).
[4] Y. Cao, J. Romero, J. P. Olson, M. Degroote, P. D. Johnson, M. Kieferov´a, I. D. Kivlichan, T. Menke, B. Peropadre, N. P. Sawaya, et al., Chemical reviews 119,
10856 (2019).
[5] A. Smith, M. Kim, F. Pollmann, and J. Knolle, npj
Quantum Information 5, 1 (2019).
[6] L. Zhou, S.-T. Wang, S. Choi, H. Pichler, and M. D.
Lukin, arXiv 1812.01041 (2018).
[7] P. Pfeuty, The One-Dimensional Ising Model with a Transverse Field, Annals of Physics 57, 79-90 (1970).
[9] S. McArdle, S. Endo, A. Aspuru-Guzik, S. Benjamin, X. Yuan, Quantum computational chemistry, Rev. Mod. Phys. 92, 15003 (2020).
[10] Abhijith J. et al., Quantum Algorithm Implementations for Beginners, arXiv:1804.03719 (2018).
[11] D. Wierichs, C. Gogolin, M. Kastoryano, Avoiding local minima in variational quantum eigensolvers with the natural gradient optimizer, arXiv:2004.14666 (2020).
APPENDIX
In [15]:
print("Quantum Task Summary")
print(t.quantum_tasks_statistics())
print(f"Estimated cost to run this example: {t.qpu_tasks_cost() + t.simulator_tasks_cost()} USD")
Quantum Task Summary
{}
Estimated cost to run this example: 0 USD
Join the Discussion
Comments (0)
No comments yet. Be the first to share your thoughts!
Indexed by QCR Librarian
This entry was created automatically from publicly available records. QCR links to public sources and only stores repository content where the license permits redistribution.
Join the Discussion
Comments (0)
No comments yet. Be the first to share your thoughts!