Nota
Esta página foi gerada a partir do tutorials/noise/4_randomized_benchmarking.ipynb.
Execute interativamente no IBM Quantum lab.
Benchmarking Randomizado¶
Introdução¶
benchmarking de randomização (RB) é uma técnica bem conhecida para medir o desempenho médio do portão, executando sequências de portões Clifford aleatórios que devem devolver os qubits para o estado inicial. O Qiskit Ignis possui ferramentas para gerar sequências de portão Clifford de um e dois qubit simultaneamente.
Este notebook dá um exemplo de como usar o módulo ignis.verification.randomized_benchmarking
. Este exemplo específico mostra como executar o benchmarking randomizado 2-qubit (RB) simultâneo com o 1-qubit RB. Há também exemplos sobre como usar algumas funções complementares para prever a fidelidade 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) Selecione os Parâmetros da Execução do RB¶
Primeiro, precisamos escolher os seguintes parâmetros:
nseeds: O número de sementes. Para cada semente você receberá uma lista separada de circuitos de saída em rb_circs.
length_vector: O vetor de comprimento dos comprimentos Clifford. Deve estar em ordem crescente. Sequências de RB de comprimento crescentes incrementam sobre as sequências anteriores.
rb_pattern: Uma lista da forma [[i,j],[k],…] que fará sequências de RB simultâneas onde Qi,Qj são uma sequência de RB de 2 qubits e Qk é uma sequência de 1 qubit, etc. O número de qubits é a soma das entradas. Para um RB ‘regular’ o qubit_pattern é apenas [[0]],[[0,1]].
length_multiplier: Se este for uma matriz, ele escala cada rb_sequence pelo multiplicador.
seed_offset: Onde iniciar as sementes (por exemplo, se quisermos adicionar mais sementes depois).
** align_cliffs:** Se verdadeiro, adiciona uma barreira em todos os qubits do rb_pattern após cada conjunto de cliffords.
Neste exemplo, temos 3 qubits Q0,Q1,Q2. Estamos executando simultaneamente o 2Q RB (no qubits Q0,Q2) e 1Q RB (no qubit Q1), onde há o dobro de portões 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) Gere as sequências de RB¶
Geramos as sequências de RB. Começamos com um pequeno exemplo (de modo que não leve muito tempo para executar).
A fim de gerar as sequências de RB rb_circs, que é uma lista de listas de circuitos quânticos, executamos a função``rb.randomized_benchmarking_seq``.
Esta função retorna:
rb_circs: Uma lista de listas de circuitos para as sequências de rb (lista separada para cada semente).
xdata: Os comprimentos Clifford (com multiplicador, se aplicável).
[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)
Como exemplo, imprimimos o circuito correspondente à primeira sequência de 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: ═════════════════════╩════
«
Veja a Unitária para 1 Circuito¶
O Unitário representando cada circuito RB deve ser a identidade (com uma fase global), já que multiplicamos elementos Clifford aleatórios, incluindo um portão de reversão computada. Simulamos isso usando um simulador de unidade 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]]
Definir o modelo de ruído¶
Definimos um modelo de ruído para o simulador. Para simular o decaimento, adicionamos probabilidades de erro de despolarizamento às portas CNOT e 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) Execute as sequências de RB no simulador Aer¶
Podemos executar as sequências de RB usando um Qiskit Aer Simulador (com algum modelo de ruído) ou usando um provedor IBMQ, e obter uma lista de resultados, 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) Ajuste os resultados do RB e calcule a fidelidade da porta¶
Obter estatísticas sobre as probabilidades de sobrevivência¶
Os resultados em result_list devem ajustar-se a uma função exponencialmente decrescente \(A \cdot \alpha ^ m + B\), na qual \(m\) é o comprimento Clifford.
A partir de \(\alpha\) podemos calcular o Erro por Clifford (EPC):
(onde \(n=nQ\) é o número de qubits).
[9]:
#Create an RBFitter object with 1 seed of data
rbfit = rb.fitters.RBFitter(result_list[0], xdata, rb_opts['rb_pattern'])
Gráfico Depois de 1 sequência¶
[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()

Gráfico com o resto das sequências¶
O gráfico está sendo atualizado após cada sequência.
[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()

Adicionar mais execuções (shots) aos dados¶
[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()

Fidelidade da Porta Prevista¶
A partir dos erros de despolarização conhecidos na simulação podemos prever a fidelidade. Primeiro precisamos contar o número de portas por Clifford.
A função gates_per_clifford recebe uma lista de circuitos de RB transpilados e retorna o número de portas da base em cada circuito.
[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
A função calculate_2q_epc dá erros medidos nas portas da base que foram usadas para construir o Clifford. Ela assume que o erro nas portas subjacentes é despolarizante. Ela tem como saída o erro de um Clifford de 2 qubits.
A entrada para esta função é: - gate_per_cliff: dicionário de porta por Clifford. - epg_2q: EPG estimado por modelo de erro. - qubit_pair: índice de dois qubits para calcular EPC. - list_epgs_1q: lista de EPGs de qubit único do qubit listado em qubit_pair
. - two_qubit_name: nome da porta de dois qubits no basis gates
(padrão é 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
Execute uma Sequência de RB com Erros T1, T2¶
Escolhemos agora sequências de RB que contêm apenas Cliffords de 2 qubits.
Executamos essas sequências como antes, mas com um modelo de ruído estendido com erro de relaxamento térmico T1/T2, e ajustamos a curva de decaimento exponencial.
[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()

Contamos novamente o número de portas por Clifford como antes, e calculamos o erro de porta Clifford de dois bits, usando os erros das portas primitivas previstos a partir do limite de coerência.
[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.
[ ]: