Note
Cette page a été générée à partir de tutorials/noise/4_randomized_benchmarking.ipynb.
Exécuter en mode interactif dans le IBM Quantum lab <https://quantum-computing.ibm.com/jupyter/tutorial/noise/4_randomized_benchmarking.ipynb> _.
Analyse Aomparative Aléatoire (Benchmarking Randomisé)¶
Introduction¶
Analyse comparative aléatoire (Randomization benchmarking RB) est une technique bien connue pour mesurer la performance moyenne des portes en exécutant des séquences de portes Clifford aléatoires qui devraient retourner les qubits à l’état initial. Qiskit Ignis a des outils pour générer simultanément des séquences de portes Clifford à un ou deux qubits.
Ce bloc-notes donne un exemple d’utilisation du module ignis.verification.randomized_benchmarking
. Cet exemple particulier montre comment exécuter le benchmarking randomisé de 2-qubit (RB) simultanément avec le RB à 1 qubit. Il y a aussi des exemples sur la façon d’utiliser certaines des fonctions complémentaires pour prédire la fidélité du RB.
[1]:
#Import general libraries (needed for functions)
import numpy as np
import matplotlib.pyplot as plt
from IPython import display
#Import Qiskit classes
import qiskit
from qiskit.providers.aer.noise import NoiseModel
from qiskit.providers.aer.noise.errors.standard_errors import depolarizing_error, thermal_relaxation_error
#Import the RB Functions
import qiskit.ignis.verification.randomized_benchmarking as rb
1) Sélectionnez les paramètres de l’exécution RB¶
First, we need to choose the following parameters:
nseeds: Le nombre de graines. Pour chaque graine, vous obtiendrez une liste séparée de circuits de sortie dans rb_circs.
length_vector: Le vecteur de longueur de Clifford. Doit être dans un ordre ascendant. Les séquences RB de longueur croissante se développent au-dessus des séquences précédentes.
rb_pattern: Une liste de la forme [[i,j],[k],… qui fera des séquences RB simultanées où Qi,Qj est une séquence RB de 2 qubits et Qk est une séquence à 1-qubits, etc. Le nombre de qubits est la somme des entrées. Pour le RB “regular” (régulier) le qubit_pattern est juste [[0]],[[0,1]].
length_multiplier: Si c’est un tableau, il met à l’échelle chaque rb_sequence par le multiplicateur.
seed_offset: Indique où commencent les graines (par exemple, si nous voulons ajouter plus de graines plus tard).
align_cliffs: Si vrai ajouter une barrière à tous les qubits dans rb_pattern après chaque ensemble de cliffords.
Dans cet exemple, nous avons 3 qubits Q0,Q1,Q2. Nous exécutons 2Q RB (sur qubits Q0,Q2) et 1Q RB (sur qubit Q1) simultanément, où il y a deux fois plus de portes Clifford 1Q.
[2]:
#Number of qubits
nQ = 3
#There are 3 qubits: Q0,Q1,Q2.
#Number of seeds (random sequences)
nseeds = 5
#Number of Cliffords in the sequence (start, stop, steps)
nCliffs = np.arange(1,200,20)
#2Q RB on Q0,Q2 and 1Q RB on Q1
rb_pattern = [[0,2],[1]]
#Do three times as many 1Q Cliffords
length_multiplier = [1,3]
2) Générer les séquences RB¶
Nous générons des séquences RB. Nous commençons par un petit exemple (pour que cela ne prenne pas trop de temps).
Afin de générer les séquences RB rb_circs, qui est une liste de listes de circuits quantiques, nous exécutons la fonction rb.randomized_benchmarking_seq
.
Cette fonction renvoie:
** rb_circs:** Une liste des listes de circuits pour les séquences rb (liste séparée pour chaque graine).
** xdata:** Les longueurs de Clifford (avec multiplicateur le cas échéant).
[3]:
rb_opts = {}
rb_opts['length_vector'] = nCliffs
rb_opts['nseeds'] = nseeds
rb_opts['rb_pattern'] = rb_pattern
rb_opts['length_multiplier'] = length_multiplier
rb_circs, xdata = rb.randomized_benchmarking_seq(**rb_opts)
Par exemple, nous imprimons le circuit correspondant à la première séquence RB:
[4]:
print(rb_circs[0][0])
┌───┐┌───┐┌───┐ ░ ┌───┐ ┌─────┐┌───┐ »
qr_0: |0>────────────■──┤ H ├┤ S ├┤ X ├──────░──┤ X ├─┤ Sdg ├┤ H ├────────────»
┌───┐┌───┐ │ ├───┤└─░─┘├───┤┌───┐ ░ ├───┤ └──░──┘├───┤┌─────┐┌───┐»
qr_1: |0>┤ H ├┤ H ├──┼──┤ S ├──░──┤ H ├┤ S ├────┤ X ├────░───┤ H ├┤ Sdg ├┤ H ├»
├───┤├───┤┌─┴─┐├───┤┌───┐└───┘└───┘ ░ ┌┴───┴┐ ┌───┐ └───┘└─────┘└───┘»
qr_2: |0>┤ H ├┤ S ├┤ X ├┤ H ├┤ S ├───────────░─┤ Sdg ├─┤ H ├──────────────────»
└───┘└───┘└───┘└───┘└───┘ ░ └─────┘ └───┘ »
cr_0: 0 ═════════════════════════════════════════════════════════════════════»
»
cr_1: 0 ═════════════════════════════════════════════════════════════════════»
»
cr_2: 0 ═════════════════════════════════════════════════════════════════════»
»
« ┌─┐
«qr_0: ──■─────────┤M├───────────
« │ ░ └╥┘ ┌─┐
«qr_1: ──┼─────░────╫──────┤M├───
« ┌─┴─┐┌─────┐ ║ ┌───┐└╥┘┌─┐
«qr_2: ┤ X ├┤ Sdg ├─╫─┤ H ├─╫─┤M├
« └───┘└─────┘ ║ └───┘ ║ └╥┘
«cr_0: ═════════════╩═══════╬══╬═
« ║ ║
«cr_1: ═════════════════════╬══╩═
« ║
«cr_2: ═════════════════════╩════
«
Regardez l’Unitaire pour 1 Circuit¶
L’Unitaire représentant chaque circuit RB devrait être l’identité (avec une phase globale), puisque nous multiplions les éléments aléatoires de Clifford, y compris une porte d’inversion calculée. Nous simulons cela à l’aide d’un simulateur unitaire Aer.
[5]:
#Create a new circuit without the measurement
qc = qiskit.QuantumCircuit(*rb_circs[0][-1].qregs,*rb_circs[0][-1].cregs)
for i in rb_circs[0][-1][0:-nQ]:
qc.data.append(i)
[6]:
#The Unitary is an identity (with a global phase)
backend = qiskit.Aer.get_backend('unitary_simulator')
basis_gates = ['u1', 'u2', 'u3', 'cx'] # use U,CX for now
job = qiskit.execute(qc, backend=backend, basis_gates=basis_gates)
print(np.around(job.result().get_unitary(), 3))
[[ 0.+1.j -0.+0.j 0.-0.j 0.-0.j -0.-0.j 0.+0.j -0.+0.j 0.-0.j]
[-0.-0.j 0.+1.j -0.+0.j 0.-0.j 0.-0.j 0.+0.j -0.-0.j -0.+0.j]
[ 0.+0.j -0.+0.j 0.+1.j -0.+0.j -0.-0.j 0.+0.j -0.-0.j 0.+0.j]
[-0.+0.j 0.+0.j -0.-0.j 0.+1.j 0.-0.j -0.-0.j 0.-0.j 0.+0.j]
[ 0.+0.j 0.-0.j 0.+0.j -0.-0.j 0.+1.j -0.+0.j 0.-0.j -0.+0.j]
[ 0.-0.j -0.+0.j -0.+0.j -0.+0.j 0.+0.j 0.+1.j 0.-0.j 0.-0.j]
[ 0.+0.j 0.-0.j -0.+0.j 0.-0.j 0.+0.j 0.-0.j 0.+1.j 0.-0.j]
[-0.-0.j 0.+0.j 0.-0.j -0.+0.j 0.+0.j 0.+0.j 0.+0.j 0.+1.j]]
Définir le modèle de bruit¶
Nous définissons un modèle de bruit pour le simulateur. Pour simuler la décadence, nous ajoutons des probabilités d’erreur dépolarisantes aux portes CNOT et U.
[7]:
noise_model = NoiseModel()
p1Q = 0.002
p2Q = 0.01
noise_model.add_all_qubit_quantum_error(depolarizing_error(p1Q, 1), 'u2')
noise_model.add_all_qubit_quantum_error(depolarizing_error(2*p1Q, 1), 'u3')
noise_model.add_all_qubit_quantum_error(depolarizing_error(p2Q, 2), 'cx')
3) Exécuter les séquences RB sur le simulateur Aer¶
Nous pouvons exécuter les séquences RB soit en utilisant un Qiskit Aer Simulator (avec un modèle de bruit) soit en utilisant un fournisseur de service IBMQ, et obtenir une liste de résultats, result_list
.
[8]:
backend = qiskit.Aer.get_backend('qasm_simulator')
basis_gates = ['u1','u2','u3','cx'] # use U,CX for now
shots = 200
result_list = []
transpile_list = []
import time
for rb_seed,rb_circ_seed in enumerate(rb_circs):
print('Compiling seed %d'%rb_seed)
rb_circ_transpile = qiskit.transpile(rb_circ_seed, basis_gates=basis_gates)
print('Simulating seed %d'%rb_seed)
job = qiskit.execute(rb_circ_transpile, noise_model=noise_model, shots=shots, backend=backend, backend_options={'max_parallel_experiments': 0})
result_list.append(job.result())
transpile_list.append(rb_circ_transpile)
print("Finished Simulating")
Compiling seed 0
Simulating seed 0
Compiling seed 1
Simulating seed 1
Compiling seed 2
Simulating seed 2
Compiling seed 3
Simulating seed 3
Compiling seed 4
Simulating seed 4
Finished Simulating
4) Ajuster les résultats du RB et calculer la fidélité de la porte¶
Obtenir des statistiques sur les probabilités de survie¶
Les résultats dans result_list devraient correspondre à une fonction à décroissance exponentielle \(A \cdot \alpha ^ m + B\), où \(m\) est la longueur de Clifford.
De \(\alpha\) nous pouvons calculer l”Erreur par Clifford (EPC):
(où \(n=nQ\) est le nombre de qubits).
[9]:
#Create an RBFitter object with 1 seed of data
rbfit = rb.fitters.RBFitter(result_list[0], xdata, rb_opts['rb_pattern'])
Tracer Après 1 Graine¶
[10]:
plt.figure(figsize=(15, 6))
for i in range(2):
ax = plt.subplot(1, 2, i+1)
pattern_ind = i
# Plot the essence by calling plot_rb_data
rbfit.plot_rb_data(pattern_ind, ax=ax, add_label=True, show_plt=False)
# Add title and label
ax.set_title('%d Qubit RB'%(len(rb_opts['rb_pattern'][i])), fontsize=18)
plt.show()

Tracer avec le reste des graines¶
Le tracé est mis à jour après chaque graine.
[11]:
rbfit = rb.fitters.RBFitter(result_list[0], xdata, rb_opts['rb_pattern'])
for seed_num, data in enumerate(result_list):#range(1,len(result_list)):
plt.figure(figsize=(15, 6))
axis = [plt.subplot(1, 2, 1), plt.subplot(1, 2, 2)]
# Add another seed to the data
rbfit.add_data([data])
for i in range(2):
pattern_ind = i
# Plot the essence by calling plot_rb_data
rbfit.plot_rb_data(pattern_ind, ax=axis[i], add_label=True, show_plt=False)
# Add title and label
axis[i].set_title('%d Qubit RB - after seed %d'%(len(rb_opts['rb_pattern'][i]), seed_num), fontsize=18)
# Display
display.display(plt.gcf())
# Clear display after each seed and close
display.clear_output(wait=True)
time.sleep(1.0)
plt.close()

Ajouter des images supplémentaires aux données¶
[12]:
shots = 200
result_list = []
transpile_list = []
for rb_seed,rb_circ_seed in enumerate(rb_circs):
print('Compiling seed %d'%rb_seed)
rb_circ_transpile = qiskit.transpile(rb_circ_seed, basis_gates=basis_gates)
print('Simulating seed %d'%rb_seed)
job = qiskit.execute(rb_circ_transpile, noise_model=noise_model, shots=shots, backend=backend, backend_options={'max_parallel_experiments': 0})
result_list.append(job.result())
transpile_list.append(rb_circ_transpile)
print("Finished Simulating")
Compiling seed 0
Simulating seed 0
Compiling seed 1
Simulating seed 1
Compiling seed 2
Simulating seed 2
Compiling seed 3
Simulating seed 3
Compiling seed 4
Simulating seed 4
Finished Simulating
[13]:
#Add this data to the previous fit
rbfit.add_data(result_list)
#Replot
plt.figure(figsize=(15, 6))
for i in range(2):
ax = plt.subplot(1, 2, i+1)
pattern_ind = i
# Plot the essence by calling plot_rb_data
rbfit.plot_rb_data(pattern_ind, ax=ax, add_label=True, show_plt=False)
# Add title and label
ax.set_title('%d Qubit RB'%(len(rb_opts['rb_pattern'][i])), fontsize=18)
plt.show()

Fidelity de la Porte Prédite¶
À partir des erreurs de dépolarisation connues sur la simulation, nous pouvons prédire la fidelity. Nous devons d’abord compter le nombre de gates par Clifford.
La fonction gates_per_clifford prend une liste de circuits RB transpilés et donne le nombre de portes de base dans chaque circuit.
[22]:
#Count the number of single and 2Q gates in the 2Q Cliffords
gates_per_cliff = rb.rb_utils.gates_per_clifford(transpile_list,xdata[0],basis_gates,rb_opts['rb_pattern'][0])
for basis_gate in basis_gates:
print("Number of %s gates per Clifford: %f "%(basis_gate ,
np.mean([gates_per_cliff[0][basis_gate],
gates_per_cliff[2][basis_gate]])))
Number of u1 gates per Clifford: 0.271522
Number of u2 gates per Clifford: 0.923587
Number of u3 gates per Clifford: 0.490109
Number of cx gates per Clifford: 1.526739
La fonction calculate_2q_epc donne des erreurs mesurées dans les portes de base qui ont été utilisées pour construire le Clifford. Il part du principe que l’erreur dans les portes sous-jacentes est dépolarisante. Il génère l’erreur par un Clifford à 2-qubit.
L’entrée de cette fonction est : - gate_per_cliff: dictionnaire de porte par Clifford. - epg_2q: EPG estimé par le modèle d’erreur. - qubit_pair: index de deux qubits pour calculer EPC. - list_epgs_1q: liste de qubit-unique EPGs des qubits listés dans qubit_pair
. - two_qubit_name: nom de portes de deux qubit dans basis gates
(par défaut cx
).
[15]:
# Error per gate from noise model
epgs_1q = {'u1': 0, 'u2': p1Q/2, 'u3': 2*p1Q/2}
epg_2q = p2Q*3/4
pred_epc = rb.rb_utils.calculate_2q_epc(
gate_per_cliff=gates_per_cliff,
epg_2q=epg_2q,
qubit_pair=[0, 2],
list_epgs_1q=[epgs_1q, epgs_1q])
# Calculate the predicted epc
print("Predicted 2Q Error per Clifford: %e"%pred_epc)
Predicted 2Q Error per Clifford: 1.591172e-02
Exécuter une séquence RB avec des erreurs T1,T2¶
Nous choisissons maintenant des séquences RB qui ne contiennent que des Cliffords 2-qubits.
Nous exécutons ces séquences comme avant, mais avec un modèle de bruit étendu avec une erreur de relaxation thermique T1/T2 et ajustons la courbe en dégradation exponentielle.
[16]:
rb_opts2 = rb_opts.copy()
rb_opts2['rb_pattern'] = [[0,1]]
rb_opts2['length_multiplier'] = 1
rb_circs2, xdata2 = rb.randomized_benchmarking_seq(**rb_opts2)
noise_model2 = NoiseModel()
#Add T1/T2 noise to the simulation
t1 = 100.
t2 = 80.
gate1Q = 0.1
gate2Q = 0.5
noise_model2.add_all_qubit_quantum_error(thermal_relaxation_error(t1,t2,gate1Q), 'u2')
noise_model2.add_all_qubit_quantum_error(thermal_relaxation_error(t1,t2,2*gate1Q), 'u3')
noise_model2.add_all_qubit_quantum_error(
thermal_relaxation_error(t1,t2,gate2Q).tensor(thermal_relaxation_error(t1,t2,gate2Q)), 'cx')
[17]:
backend = qiskit.Aer.get_backend('qasm_simulator')
basis_gates = ['u1','u2','u3','cx'] # use U,CX for now
shots = 500
result_list2 = []
transpile_list2 = []
for rb_seed,rb_circ_seed in enumerate(rb_circs2):
print('Compiling seed %d'%rb_seed)
rb_circ_transpile = qiskit.transpile(rb_circ_seed, basis_gates=basis_gates)
print('Simulating seed %d'%rb_seed)
job = qiskit.execute(rb_circ_transpile, noise_model=noise_model, shots=shots, backend=backend, backend_options={'max_parallel_experiments': 0})
result_list2.append(job.result())
transpile_list2.append(rb_circ_transpile)
print("Finished Simulating")
Compiling seed 0
Simulating seed 0
Compiling seed 1
Simulating seed 1
Compiling seed 2
Simulating seed 2
Compiling seed 3
Simulating seed 3
Compiling seed 4
Simulating seed 4
Finished Simulating
[18]:
#Create an RBFitter object
rbfit = rb.RBFitter(result_list2, xdata2, rb_opts2['rb_pattern'])
plt.figure(figsize=(10, 6))
ax = plt.gca()
# Plot the essence by calling plot_rb_data
rbfit.plot_rb_data(0, ax=ax, add_label=True, show_plt=False)
# Add title and label
ax.set_title('2 Qubit RB with T1/T2 noise', fontsize=18)
plt.show()

Nous comptons à nouveau le nombre de gates per Clifford comme avant, et calculons two-qubit Clifford gate error, en utilisant les erreurs de porte primitives prédites à partir de la limite de cohérence.
[23]:
#Count the number of single and 2Q gates in the 2Q Cliffords
gates_per_cliff = rb.rb_utils.gates_per_clifford(transpile_list2,xdata[0],basis_gates,rb_opts2['rb_pattern'][0])
for basis_gate in basis_gates:
print("Number of %s gates per Clifford: %f "%(basis_gate ,
np.mean([gates_per_cliff[0][basis_gate],
gates_per_cliff[1][basis_gate]])))
Number of u1 gates per Clifford: 0.250761
Number of u2 gates per Clifford: 0.969565
Number of u3 gates per Clifford: 0.489130
Number of cx gates per Clifford: 1.503696
[20]:
# Predicted primitive gate errors from the coherence limit
u2_error = rb.rb_utils.coherence_limit(1,[t1],[t2],gate1Q)
u3_error = rb.rb_utils.coherence_limit(1,[t1],[t2],2*gate1Q)
epg_2q = rb.rb_utils.coherence_limit(2,[t1,t1],[t2,t2],gate2Q)
epgs_1q = {'u1': 0, 'u2': u2_error, 'u3': u3_error}
pred_epc = rb.rb_utils.calculate_2q_epc(
gate_per_cliff=gates_per_cliff,
epg_2q=epg_2q,
qubit_pair=[0, 1],
list_epgs_1q=[epgs_1q, epgs_1q])
# Calculate the predicted epc
print("Predicted 2Q Error per Clifford: %e"%pred_epc)
Predicted 2Q Error per Clifford: 1.313111e-02
[21]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | 0.16.1 |
Terra | 0.13.0.dev0+98fd14e |
Aer | 0.4.1 |
Ignis | 0.3.0.dev0+dd2c926 |
Aqua | 0.6.4 |
IBM Q Provider | 0.5.0 |
System information | |
Python | 3.7.6 (default, Jan 8 2020, 19:59:22) [GCC 7.3.0] |
OS | Linux |
CPUs | 8 |
Memory (Gb) | 62.920127868652344 |
Fri Mar 27 15:44:43 2020 IST |
This code is a part of Qiskit
© Copyright IBM 2017, 2020.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
[ ]: