Portuguese, Brazilian
Idiomas
English
Japanese
German
Korean
Portuguese, Brazilian
French
Shortcuts

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):

\[EPC = \frac{2^n-1}{2^n} (1-\alpha)\]

(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()
../../_images/tutorials_noise_4_randomized_benchmarking_20_0.png

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()
../../_images/tutorials_noise_4_randomized_benchmarking_22_0.png

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()
../../_images/tutorials_noise_4_randomized_benchmarking_25_0.png

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()
../../_images/tutorials_noise_4_randomized_benchmarking_34_0.png

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 SoftwareVersion
Qiskit0.16.1
Terra0.13.0.dev0+98fd14e
Aer0.4.1
Ignis0.3.0.dev0+dd2c926
Aqua0.6.4
IBM Q Provider0.5.0
System information
Python3.7.6 (default, Jan 8 2020, 19:59:22) [GCC 7.3.0]
OSLinux
CPUs8
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.

[ ]: