Execução de algoritmos em hardwares quânticos — Plataforma IBM Quantum — Parte 02

Quantumgercom
10 min readFeb 7, 2024

--

Antes de continuarmos, é importante que você confira a Parte 01 deste tutorial.

Primeiramente, nesta seção, vamos comparar dois jobs que foram rodados em backends diferentes, que já mencionamos, chamados “ibm_kyoto” e “ibm_osaka”. No próximo trecho de código, iremos mostrar como está o status e a contagem de resultados do job que foi rodado no hardware quântico “ibm_kyoto”.

job1 = provider.backend.retrieve_job("cpn5xgp9nad0008pr4kg")

job1.status()
#Output
<JobStatus.DONE: 'job has successfully run'>
counts_hardware1 = job1.result().get_counts()

counts_hardware1
{'00': 53, '10': 413, '01': 453, '11': 105}

Nesse caso, vamos rodar o algoritmo mais uma vez usando uma simulação com a variável chamada “job_simulador1” para guardar e mostrar os resultados.

from qiskit import QuantumCircuit, BasicAer, execute
from qiskit.visualization import plot_histogram


job_simulator1 = execute(qc, simulator, shots=1024, seed_simulator=42)
counts_simulator1 = job_simulator1.result().get_counts()

counts_simulator1
#Output
{'01': 511, '10': 513}

Agora, por meio de um histograma, iremos comparar os resultados da execução do circuito simples entre um simulador sem ruido e um hardware real quântico, que foi o “ibm_kyoto”.

legend=["simulator", "hardware"]

plot_histogram([counts_simulator1, counts_hardware1], title="Results", legend=legend)

Assim, também, iremos comparar o simulador com o backend “ibm_osaka”. Obtendo seu status e seus resultados.

job2 = provider.backend.retrieve_job("cpn62htd7p30008621rg") 

job2.status()
#Output
<JobStatus.DONE: 'job has successfully run'>
counts_hardware2 = job2.result().get_counts()

counts_hardware2
#Output
{'10': 508, '11': 21, '00': 31, '01': 464}
from qiskit import QuantumCircuit, BasicAer, execute
from qiskit.visualization import plot_histogram


job_simulator2 = execute(qc, simulator, shots=1024, seed_simulator=42)
counts_simulator2 = job_simulator2.result().get_counts()

counts_simulator2
#Output
{'01': 511, '10': 513}

Por conseguinte, será plotado o histograma comparando o simulador sem ruido com o backend “ibm_osaka”.

legend=["simulator", "hardware"]

plot_histogram([counts_simulator2, counts_hardware2], title="Results", legend=legend)

Logo, irá se realizar a comparação entre os dois backends citados, juntamente com o simulador sem ruido, por meio do histograma.

legend=["simulator", "hardware - Kyoto","hardware - Osaka"]

plot_histogram([counts_simulator1 , counts_hardware1, counts_hardware2], title="Results", legend=legend)

Ao examinar este histograma, torna-se evidente que ao empregar um simulador sem ruído, é possível obter resultados que se aproximam do que seria gerado em uma execução real desse circuito. Além disso, observamos que o backend “ibm_osaka” está mais próximo dos resultados do simulador e exibe menos ruídos em comparação com o backend “ibm_kyoto”. Assim, o “ibm_osaka” torna-se o melhor para executar e demostrar resultados com menos chance de erros e mais estimados.

Os ruídos seriam os erros que poderiam acontecer durante a execução desse circuito, porém são informações mais próximas do real. Diante disso, para obter resultados mais fidedignos, utilizaremos simuladores com ruído.

Será importado através do qiskit, informações sobre hardwares quânticos que eram disponibilizados pela plataforma da IBM Quantum e foram removidos de uso. Desse modo, será obtido as informações do backend para podermos utilizar como simulador, além de seus ruídos.

Será usado o simulador com ruido do backend “FakeCairoV2”. Além de ser mostrado todas as informações sobre ele e seus resultados na execução do algoritmo.

from qiskit.providers.fake_provider import FakeCairoV2

fake_backend1 = FakeCairoV2()

fake_backend1
#Output
<qiskit.providers.fake_provider.backends.cairo.fake_cairo.FakeCairoV2 at 0x1c3a0123810>
from qiskit_aer.noise import NoiseModel

noise_model1 = NoiseModel.from_backend(fake_backend1)

print(noise_model1)
#Output
NoiseModel:
Basis gates: ['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Instructions with noise: ['x', 'measure', 'sx', 'reset', 'cx', 'id']
Qubits with noise: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26]
Specific qubit errors: [('id', (0,)), ('id', (1,)), ('id', (2,)), ('id', (3,)), ('id', (4,)), ('id', (5,)), ('id', (6,)), ('id', (7,)), ('id', (8,)), ('id', (9,)), ('id', (10,)), ('id', (11,)), ('id', (12,)), ('id', (13,)), ('id', (14,)), ('id', (15,)), ('id', (16,)), ('id', (17,)), ('id', (18,)), ('id', (19,)), ('id', (20,)), ('id', (21,)), ('id', (22,)), ('id', (23,)), ('id', (24,)), ('id', (25,)), ('id', (26,)), ('sx', (0,)), ('sx', (1,)), ('sx', (2,)), ('sx', (3,)), ('sx', (4,)), ('sx', (5,)), ('sx', (6,)), ('sx', (7,)), ('sx', (8,)), ('sx', (9,)), ('sx', (10,)), ('sx', (11,)), ('sx', (12,)), ('sx', (13,)), ('sx', (14,)), ('sx', (15,)), ('sx', (16,)), ('sx', (17,)), ('sx', (18,)), ('sx', (19,)), ('sx', (20,)), ('sx', (21,)), ('sx', (22,)), ('sx', (23,)), ('sx', (24,)), ('sx', (25,)), ('sx', (26,)), ('x', (0,)), ('x', (1,)), ('x', (2,)), ('x', (3,)), ('x', (4,)), ('x', (5,)), ('x', (6,)), ('x', (7,)), ('x', (8,)), ('x', (9,)), ('x', (10,)), ('x', (11,)), ('x', (12,)), ('x', (13,)), ('x', (14,)), ('x', (15,)), ('x', (16,)), ('x', (17,)), ('x', (18,)), ('x', (19,)), ('x', (20,)), ('x', (21,)), ('x', (22,)), ('x', (23,)), ('x', (24,)), ('x', (25,)), ('x', (26,)), ('cx', (6, 7)), ('cx', (7, 6)), ('cx', (12, 13)), ('cx', (13, 12)), ('cx', (7, 10)), ('cx', (10, 7)), ('cx', (8, 11)), ('cx', (11, 8)), ('cx', (3, 2)), ('cx', (2, 3)), ('cx', (4, 7)), ('cx', (7, 4)), ('cx', (9, 8)), ('cx', (8, 9)), ('cx', (17, 18)), ('cx', (18, 17)), ('cx', (1, 0)), ('cx', (0, 1)), ('cx', (15, 12)), ('cx', (12, 15)), ('cx', (1, 4)), ('cx', (4, 1)), ('cx', (20, 19)), ('cx', (19, 20)), ('cx', (26, 25)), ('cx', (25, 26)), ('cx', (21, 23)), ('cx', (23, 21)), ('cx', (21, 18)), ('cx', (18, 21)), ('cx', (16, 14)), ('cx', (14, 16)), ('cx', (13, 14)), ('cx', (14, 13)), ('cx', (24, 25)), ('cx', (25, 24)), ('cx', (11, 14)), ('cx', (14, 11)), ('cx', (24, 23)), ('cx', (23, 24)), ('cx', (10, 12)), ('cx', (12, 10)), ('cx', (22, 19)), ('cx', (19, 22)), ('cx', (5, 8)), ('cx', (8, 5)), ('cx', (22, 25)), ('cx', (25, 22)), ('cx', (3, 5)), ('cx', (5, 3)), ('cx', (16, 19)), ('cx', (19, 16)), ('cx', (18, 15)), ('cx', (15, 18)), ('cx', (1, 2)), ('cx', (2, 1)), ('reset', (0,)), ('reset', (1,)), ('reset', (2,)), ('reset', (3,)), ('reset', (4,)), ('reset', (5,)), ('reset', (6,)), ('reset', (7,)), ('reset', (8,)), ('reset', (9,)), ('reset', (10,)), ('reset', (11,)), ('reset', (12,)), ('reset', (13,)), ('reset', (14,)), ('reset', (15,)), ('reset', (16,)), ('reset', (17,)), ('reset', (18,)), ('reset', (19,)), ('reset', (20,)), ('reset', (21,)), ('reset', (22,)), ('reset', (23,)), ('reset', (24,)), ('reset', (25,)), ('reset', (26,)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('measure', (4,)), ('measure', (5,)), ('measure', (6,)), ('measure', (7,)), ('measure', (8,)), ('measure', (9,)), ('measure', (10,)), ('measure', (11,)), ('measure', (12,)), ('measure', (13,)), ('measure', (14,)), ('measure', (15,)), ('measure', (16,)), ('measure', (17,)), ('measure', (18,)), ('measure', (19,)), ('measure', (20,)), ('measure', (21,)), ('measure', (22,)), ('measure', (23,)), ('measure', (24,)), ('measure', (25,)), ('measure', (26,))]
job_simulator_noisy1 = execute(qc, fake_backend1, shots=1024, seed_simulator=42)
counts_simulator_noisy1 = job_simulator_noisy1.result().get_counts()

counts_simulator_noisy1
#Output
{'10': 483, '01': 509, '00': 20, '11': 12}

O histograma mostrará a comparação entre o simulador sem ruido, o simulado com ruido “FakeCairoV2” e o hardware “ibm_osaka”.

legend=["simulador sem ruído", "simulador com ruído", "hardware - Osaka"]

plot_histogram([counts_simulator1, counts_simulator_noisy1, counts_hardware2], title="Resultados", legend=legend)

Nesse momento, iremos usar como outro exemplo o simulador com ruido “FakeBelemV2”, juntamente com suas informações e dados de seu backend.

from qiskit.providers.fake_provider import FakeBelemV2

fake_backend2 = FakeBelemV2()

fake_backend2
#Output
<qiskit.providers.fake_provider.backends.belem.fake_belem.FakeBelemV2 at 0x1c380600890>
from qiskit_aer.noise import NoiseModel

noise_model2 = NoiseModel.from_backend(fake_backend2)

print(noise_model2)
#Output
NoiseModel:
Basis gates: ['cx', 'delay', 'id', 'measure', 'reset', 'rz', 'sx', 'x']
Instructions with noise: ['x', 'measure', 'sx', 'reset', 'cx', 'id']
Qubits with noise: [0, 1, 2, 3, 4]
Specific qubit errors: [('id', (0,)), ('id', (1,)), ('id', (2,)), ('id', (3,)), ('id', (4,)), ('sx', (0,)), ('sx', (1,)), ('sx', (2,)), ('sx', (3,)), ('sx', (4,)), ('x', (0,)), ('x', (1,)), ('x', (2,)), ('x', (3,)), ('x', (4,)), ('cx', (4, 3)), ('cx', (3, 4)), ('cx', (3, 1)), ('cx', (1, 3)), ('cx', (2, 1)), ('cx', (1, 2)), ('cx', (1, 0)), ('cx', (0, 1)), ('reset', (0,)), ('reset', (1,)), ('reset', (2,)), ('reset', (3,)), ('reset', (4,)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('measure', (4,))]
job_simulator_noisy2 = execute(qc, fake_backend2, shots=1024, seed_simulator=42)
counts_simulator_noisy2= job_simulator_noisy2.result().get_counts()

counts_simulator_noisy2
#OUtput
{'10': 459, '01': 483, '00': 61, '11': 21}

O histograma será gerado para os simuladores e o hardware “ibm_osaka”, comparando-os.

legend=["simulador sem ruído", "simulador com ruído", "hardware - Osaka"]

plot_histogram([counts_simulator1, counts_simulator_noisy2, counts_hardware2], title="Resultados", legend=legend)

O emprego de simuladores com ruído proporciona uma eficaz maneira de simular a execução de circuitos quânticos, permitindo uma estimativa mais precisa do desempenho em um hardware real. Nesse contexto, é possível notar que mesmo os ruídos do simulador são semelhantes aos do backend “ibm_osaka”.

Utilizando o “FakeCambridgeV2” como referência para comparação, também procedemos à obtenção de seus dados e resultados , a fim de simular a execução do circuito.

from qiskit.providers.fake_provider import FakeCambridgeV2

fake_backend3 = FakeCambridgeV2()

fake_backend3
#Output
<qiskit.providers.fake_provider.backends.cambridge.fake_cambridge.FakeCambridgeV2 at 0x7fb325885250>
from qiskit_aer.noise import NoiseModel

noise_model3 = NoiseModel.from_backend(fake_backend3)

print(noise_model3)
#Output
NoiseModel:
Basis gates: ['cx', 'delay', 'id', 'measure', 'u1', 'u2', 'u3']
Instructions with noise: ['id', 'u2', 'cx', 'u3', 'measure']
Qubits with noise: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27]
Specific qubit errors: [('id', (0,)), ('id', (1,)), ('id', (2,)), ('id', (3,)), ('id', (4,)), ('id', (5,)), ('id', (6,)), ('id', (7,)), ('id', (8,)), ('id', (9,)), ('id', (10,)), ('id', (11,)), ('id', (12,)), ('id', (13,)), ('id', (14,)), ('id', (15,)), ('id', (16,)), ('id', (17,)), ('id', (18,)), ('id', (19,)), ('id', (20,)), ('id', (21,)), ('id', (22,)), ('id', (23,)), ('id', (24,)), ('id', (25,)), ('id', (26,)), ('id', (27,)), ('u2', (0,)), ('u2', (1,)), ('u2', (2,)), ('u2', (3,)), ('u2', (4,)), ('u2', (5,)), ('u2', (6,)), ('u2', (7,)), ('u2', (8,)), ('u2', (9,)), ('u2', (10,)), ('u2', (11,)), ('u2', (12,)), ('u2', (13,)), ('u2', (14,)), ('u2', (15,)), ('u2', (16,)), ('u2', (17,)), ('u2', (18,)), ('u2', (19,)), ('u2', (20,)), ('u2', (21,)), ('u2', (22,)), ('u2', (23,)), ('u2', (24,)), ('u2', (25,)), ('u2', (26,)), ('u2', (27,)), ('u3', (0,)), ('u3', (1,)), ('u3', (2,)), ('u3', (3,)), ('u3', (4,)), ('u3', (5,)), ('u3', (6,)), ('u3', (7,)), ('u3', (8,)), ('u3', (9,)), ('u3', (10,)), ('u3', (11,)), ('u3', (12,)), ('u3', (13,)), ('u3', (14,)), ('u3', (15,)), ('u3', (16,)), ('u3', (17,)), ('u3', (18,)), ('u3', (19,)), ('u3', (20,)), ('u3', (21,)), ('u3', (22,)), ('u3', (23,)), ('u3', (24,)), ('u3', (25,)), ('u3', (26,)), ('u3', (27,)), ('cx', (0, 1)), ('cx', (0, 5)), ('cx', (1, 0)), ('cx', (1, 2)), ('cx', (2, 1)), ('cx', (2, 3)), ('cx', (3, 2)), ('cx', (3, 4)), ('cx', (4, 3)), ('cx', (4, 6)), ('cx', (5, 0)), ('cx', (5, 9)), ('cx', (6, 4)), ('cx', (6, 13)), ('cx', (7, 8)), ('cx', (7, 16)), ('cx', (8, 7)), ('cx', (8, 9)), ('cx', (9, 5)), ('cx', (9, 8)), ('cx', (9, 10)), ('cx', (10, 9)), ('cx', (10, 11)), ('cx', (11, 10)), ('cx', (11, 12)), ('cx', (11, 17)), ('cx', (12, 11)), ('cx', (12, 13)), ('cx', (13, 6)), ('cx', (13, 12)), ('cx', (13, 14)), ('cx', (14, 13)), ('cx', (14, 15)), ('cx', (15, 14)), ('cx', (15, 18)), ('cx', (16, 7)), ('cx', (16, 19)), ('cx', (17, 11)), ('cx', (17, 23)), ('cx', (18, 15)), ('cx', (18, 27)), ('cx', (19, 16)), ('cx', (19, 20)), ('cx', (20, 19)), ('cx', (20, 21)), ('cx', (21, 20)), ('cx', (21, 22)), ('cx', (22, 21)), ('cx', (22, 23)), ('cx', (23, 17)), ('cx', (23, 22)), ('cx', (23, 24)), ('cx', (24, 23)), ('cx', (24, 25)), ('cx', (25, 24)), ('cx', (25, 26)), ('cx', (26, 25)), ('cx', (26, 27)), ('cx', (27, 18)), ('cx', (27, 26)), ('measure', (0,)), ('measure', (1,)), ('measure', (2,)), ('measure', (3,)), ('measure', (4,)), ('measure', (5,)), ('measure', (6,)), ('measure', (7,)), ('measure', (8,)), ('measure', (9,)), ('measure', (10,)), ('measure', (11,)), ('measure', (12,)), ('measure', (13,)), ('measure', (14,)), ('measure', (15,)), ('measure', (16,)), ('measure', (17,)), ('measure', (18,)), ('measure', (19,)), ('measure', (20,)), ('measure', (21,)), ('measure', (22,)), ('measure', (23,)), ('measure', (24,)), ('measure', (25,)), ('measure', (26,)), ('measure', (27,))]
job_simulator_noisy3 = execute(qc, fake_backend3, shots=1024, seed_simulator=42)
counts_simulator_noisy3= job_simulator_noisy3.result().get_counts()

counts_simulator_noisy3
#Output
{'11': 166, '01': 388, '00': 98, '10': 372}

Portanto, apresentaremos o histograma do simulador sem ruído, do simulador com ruído “FakeCambridgeV2” e do hardware quântico “ibm_osaka”.

legend=["simulador sem ruído", "simulador com ruído", "hardware - Osaka"]

plot_histogram([counts_simulator1, counts_simulator_noisy3, counts_hardware2], title="Resultados", legend=legend)

Nesse contexto, vamos reunir os três simuladores com ruído mencionados anteriormente e compará-los com o hardware “ibm_osaka” por meio do histograma.

legend=["simulador com ruído - Cairo", "simulador com ruído - Belem","simulador com ruído - Cambrige", "Hardware - Osaka"]

plot_histogram([counts_simulator_noisy1, counts_simulator_noisy2, counts_simulator_noisy3,counts_hardware2], title="Resultados", legend=legend)

Com base no exposto, é possível observar que o simulador com ruído mais próximo foi o de “Belem”. Embora o simulador “Cairo” apresentasse menos ruídos, destaca-se a importância do simulador “Belem” por fornecer resultados muito próximos aos obtidos no hardware real “ibm_osaka”, onde o algoritmo foi efetivamente executado.

Agora, temos a opção de transformar e empregar os dados dos backends, atualmente em operação na IBM Quantum, para simulações com ruído. Dessa forma, concluiremos a importação do “AerSimulator” para demonstrar essas informações.

from qiskit.providers.aer import AerSimulator

fake_backend = AerSimulator.from_backend(backend_least_busy)

No código subsequente, apresentaremos o código do circuito, incluindo a criação da variável “simulador” para sua execução na simulação sem ruído, e outra variável “fake_backend” para transformar o hardware “ibm_kyoto” em um simulador com ruído. Demostrando um histograma comparando eles.

qc = QuantumCircuit(2, 2)

qc.x(1)
qc.h(0)
qc.cx(0, 1)
qc.measure(range(2), range(2))

display(qc.draw("mpl", style="iqp"))

# ==============================
# simulador sem ruído

simulator = BasicAer.get_backend('qasm_simulator')

job_simulator = execute(qc, simulator, shots=1024, seed_simulator=42)
counts_simulator = job_simulator.result().get_counts()

# ==============================
# simulador com ruído

fake_backend = AerSimulator.from_backend(provider.get_backend('ibm_kyoto'))

job_simulator_noisy = execute(qc, fake_backend, shots=1024, seed_simulator=42)
counts_simulator_noisy = job_simulator_noisy.result().get_counts()

# ==============================

legend=["simulador sem ruído", "simulador com ruído - Kyoto","hardware - Kyoto"]
display(plot_histogram([counts_simulator, counts_simulator_noisy,counts_hardware1], title="Resultados", legend=legend))

Nesse outro histograma, será plotado todos os simuladores citados anteriormente, para melhor analise.

legend=["simulador com ruído - Cairo", "simulador com ruído - Belem","simulador com ruído - Cambrige","simulador com ruido - Kyoto", "Hardware "]

plot_histogram([counts_simulator_noisy1, counts_simulator_noisy2, counts_simulator_noisy3,counts_simulator_noisy,counts_hardware1], title="Resultados", legend=legend)

Para concluir nosso tutorial, desenvolveremos um algoritmo de 3 qubits com3 bits clássicos para demonstrar as contagens de suas execuções. Utilizaremos simulador sem ruído e dois simuladores com ruído, chamados de “ibm_brisbane” e “ibm_kyoto”, que são backends ativos na plataforma da IBM Quantum. Apresentaremos o circuito, bem como o histograma das execuções, demostrando como um exemplo simples.

Diante dessas etapas, concluímos o tutorial sobre a execução de algoritmos em computadores quânticos. Obrigado!!

--

--