02 - Compare Qiskit Simulators with different simulation methods
This notebook is a tutorial on how to use different simulator backends to solve the same problem and compare their simulation results. This tutorial compares the performance of a perfect noise-free statvector simulator, a noise-free shot-based simulator, and finally a noisy simulator emulating a real QPU. This notebook uses qiskit simulators for each of the three categories. We employ: - qiskit.statevector_simulator
- noise-free qiskit.qasm_simulator
- noisy qiskit.qasm_simulator
with a real device noise profile
Begin by importing necessary modules
[1]:
#some regular python libraries
import networkx as nx
import numpy as np
from pprint import pprint
import matplotlib.pyplot as plt
#import problem classes from OQ for easy problem creation
from openqaoa.problems import MaximumCut, NumberPartition
#import the QAOA workflow model
from openqaoa import QAOA
#import method to specify the device
from openqaoa.backends import create_device
Create a problem instance
We begin by creating a problem instance for a simple MaximumCut problem for a random graph created using the python networkx
module.
[2]:
nodes = 5
edge_probability = 0.5
g = nx.generators.fast_gnp_random_graph(n=nodes,p=edge_probability, seed=30)
# import graph plotter from openqaoa
from openqaoa.utilities import plot_graph
plot_graph(g)
[3]:
# Use the MaximumCut class to instantiate the problem.
maxcut_prob = MaximumCut(g)
# The property `qubo` translates the problem into a binary Qubo problem.
# The binary values can be access via the `asdict()` method.
maxcut_qubo = maxcut_prob.qubo
Extract the exact solution for a small enough problem
[4]:
hamiltonian = maxcut_qubo.hamiltonian
# import the brute-force solver to obtain exact solution
from openqaoa.utilities import ground_state_hamiltonian
energy, configuration = ground_state_hamiltonian(hamiltonian)
print(f"Ground State energy: {energy}, Solution: {configuration}")
Ground State energy: -3.0, Solution: ['11000', '10100', '11100', '11010', '00101', '00011', '01011', '00111']
QAOA models with different devices
Here we create, three different QAOA models with each of the three devices mentioned above
[5]:
# initialize model with statevector_simulator
q_sv = QAOA()
# device
qiskit_sv = create_device(location='local', name='qiskit.statevector_simulator')
q_sv.set_device(qiskit_sv)
# circuit properties
q_sv.set_circuit_properties(p=2, param_type='standard', init_type='rand', mixer_hamiltonian='x')
# classical optimizer properties
q_sv.set_classical_optimizer(method='COBYLA', maxiter=200, tol=0.001,
cost_progress=True, parameter_log=True)
q_sv.compile(maxcut_qubo)
[6]:
# initialize model with shot-based simulator
q_shot = QAOA()
# device
qiskit_shot = create_device(location='local', name='qiskit.qasm_simulator')
q_shot.set_device(qiskit_shot)
# circuit properties
q_shot.set_circuit_properties(p=2, param_type='standard', init_type='rand', mixer_hamiltonian='x')
# backend properties
q_shot.set_backend_properties(n_shots = 200)
# classical optimizer properties
q_shot.set_classical_optimizer(method='COBYLA', maxiter=200, tol=0.001,
cost_progress=True, parameter_log=True)
q_shot.compile(maxcut_qubo)
Import a noise-model
Finally, import a noise model from a qiskit QPU to be used for noisy simulation with qiskit simulator.
[7]:
from qiskit import Aer
from qiskit.test.mock import FakeVigo
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer import QasmSimulator
device_backend = FakeVigo()
device = QasmSimulator.from_backend(device_backend)
noise_model = NoiseModel.from_backend(device)
/tmp/ipykernel_2231657/3184544667.py:2: DeprecationWarning: The module 'qiskit.test.mock' is deprecated since Qiskit Terra 0.21.0, and will be removed 3 months or more later. Instead, you should import the desired object directly 'qiskit.providers.fake_provider'.
from qiskit.test.mock import FakeVigo
[8]:
# initialize model with noisy shot-based simulator
q_noisy_shot = QAOA()
# device
qiskit_noisy_shot = create_device(location='local', name='qiskit.qasm_simulator')
q_noisy_shot.set_device(qiskit_noisy_shot)
# circuit properties
q_noisy_shot.set_circuit_properties(p=2, param_type='standard', init_type='rand', mixer_hamiltonian='x')
# backend properties
q_noisy_shot.set_backend_properties(n_shots = 200, noise_model = noise_model)
# classical optimizer properties
q_noisy_shot.set_classical_optimizer(method='COBYLA', maxiter=200, tol=0.001,
cost_progress=True, parameter_log=True)
q_noisy_shot.compile(maxcut_qubo)
Optimization
Once the models are compiled, we can begin the optimization process. We optimize the problem with all three backends and then plot the results for comparison.
[9]:
q_sv.optimize()
[10]:
q_shot.optimize()
[11]:
q_noisy_shot.optimize()
Results
[12]:
results_sv = q_sv.result
results_shot = q_shot.result
results_noisy_shot = q_noisy_shot.result
[13]:
fig, ax = plt.subplots(1,1,figsize=(12,8))
fig, ax = results_sv.plot_cost(ax=ax,label='Statevector Simulator')
fig, ax = results_shot.plot_cost(ax=ax,color='teal', label='Noise-free shot simulator with 500 shots')
fig, ax = results_noisy_shot.plot_cost(ax=ax,color='green', label='Noisy shot simulator with 500 shots and noise model from ibmq_manila')
[ ]: