Перейти до основного вмісту

Налагодження завдань Qiskit Runtime

Версії пакетів

Код на цій сторінці розроблено з використанням наступних залежностей. Рекомендуємо використовувати ці або новіші версії.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17

Перед відправкою ресурсомісткого завдання Qiskit Runtime на виконання на реальному залізі, ти можеш скористатися класом Neat (Noisy Estimator Analyzer Tool) з Qiskit Runtime, щоб перевірити, чи правильно налаштоване твоє завдання Estimator, чи воно, ймовірно, повернe точні результати, чи використовує найбільш відповідні опції для вказаної задачі тощо.

Neat виконує кліффордизацію вхідних схем для ефективної симуляції, зберігаючи при цьому їхню структуру та глибину. Кліффордові схеми зазнають схожого рівня шуму і є хорошим наближенням для вивчення оригінальної схеми, що цікавить.

Наступні приклади ілюструють ситуації, де Neat може бути корисним інструментом. Для початку імпортуй відповідні пакети та автентифікуйся в сервісі Qiskit Runtime.

Підготовка середовища

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
import random

from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
from qiskit_ibm_runtime.debug_tools import Neat

from qiskit_aer.noise import NoiseModel, depolarizing_error
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Generate a preset pass manager
# This will be used to convert the abstract circuit to an equivalent Instruction Set Architecture (ISA) circuit.
pm = generate_preset_pass_manager(backend=backend, optimization_level=0)

# Set the random seed
random.seed(10)

Ініціалізація цільової схеми

Розглянемо шестикубітну схему з такими властивостями:

  • Чергує випадкові повороти RZ з шарами вентилів CNOT.
  • Має дзеркальну структуру, тобто застосовує унітарний оператор U, а потім його обернений.
def generate_circuit(n_qubits, n_layers):
r"""
A function to generate a pseudo-random a circuit with ``n_qubits`` qubits and
``2*n_layers`` entangling layers of the type used in this notebook.
"""
# An array of random angles
angles = [
[random.random() for q in range(n_qubits)] for s in range(n_layers)
]

qc = QuantumCircuit(n_qubits)
qubits = list(range(n_qubits))

# do random circuit
for layer in range(n_layers):
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(angles[layer][q_idx], qubit)

# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)

# undo random circuit
for layer in range(n_layers)[::-1]:
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)

# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(-angles[layer][q_idx], qubit)

return qc

# Generate a random circuit
qc = generate_circuit(6, 3)
# Convert the abstract circuit to an equivalent ISA circuit.
isa_qc = pm.run(qc)

qc.draw("mpl", idle_wires=0)

Output of the previous code cell

Обери одно-Паулівські оператори Z як спостережувані та використай їх для ініціалізації примітивних уніфікованих блоків (PUB).

# Initialize the observables
obs = ["ZIIIII", "IZIIII", "IIZIII", "IIIZII", "IIIIZI", "IIIIIZ"]
print(f"Observables: {obs}")

# Map the observables to the backend's layout
isa_obs = [SparsePauliOp(o).apply_layout(isa_qc.layout) for o in obs]

# Initialize the PUBs, which consist of six-qubit circuits with `n_layers` 1, ..., 6
all_n_layers = [1, 2, 3, 4, 5, 6]

pubs = [(pm.run(generate_circuit(6, n)), isa_obs) for n in all_n_layers]
Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']

Кліффордизація схем

Раніше визначені схеми PUB не є кліффордовими, що ускладнює їхню класичну симуляцію. Однак можна скористатися методом to_clifford класу Neat, щоб відобразити їх на кліффордові схеми для більш ефективної симуляції. Метод to_clifford є обгорткою навколо транспілятор-паса ConvertISAToClifford, який також можна використовувати незалежно. Зокрема, він замінює некліффордові однокубітні вентилі в оригінальній схемі на кліффордові однокубітні вентилі, але не змінює двокубітні вентилі, кількість кубітів або глибину схеми.

Дивись Ефективна симуляція стабілізаторних схем з примітивами Qiskit Aer для отримання додаткової інформації про симуляцію кліффордових схем. Спочатку ініціалізуй Neat.

# You could specify a custom `NoiseModel` here. If `None`, `Neat`
# pulls the noise model from the given backend
noise_model = None

# Initialize `Neat`
analyzer = Neat(backend, noise_model)

Далі виконай кліффордизацію PUB-ів.

clifford_pubs = analyzer.to_clifford(pubs)

clifford_pubs[0].circuit.draw("mpl", idle_wires=0)

Output of the previous code cell

Застосування 1: Аналіз впливу шуму на результати роботи схеми

Цей приклад показує, як використовувати Neat для вивчення впливу різних моделей шуму на PUB-и залежно від глибини схеми шляхом запуску симуляцій в ідеальних (ideal_sim) та шумних (noisy_sim) умовах. Це може бути корисним для формування очікувань щодо якості експериментальних результатів перед запуском завдання на QPU. Щоб дізнатися більше про моделі шуму, дивись Точна та шумна симуляція з примітивами Qiskit Aer.

Симульовані результати підтримують математичні операції і тому можуть порівнюватися між собою (або з експериментальними результатами) для обчислення показників якості.

обережно

QPU може зазнавати різних видів шуму. Модель шуму Qiskit Aer, що використовується тут, симулює лише деякі з них і тому, ймовірно, є менш суворою, ніж шум на реальному QPU.

Для отримання докладної інформації про те, які похибки включаються при ініціалізації моделі шуму з QPU, дивись довідку API Aer NoiseModel.

Почни з виконання ідеальних та шумних класичних симуляцій.

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
print(f"Ideal results:\n {ideal_results}\n")

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
print(f"Noisy results:\n {noisy_results}\n")
Ideal results:
NeatResult([NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.]))])
Noisy results:
NeatResult([NeatPubResult(vals=array([0.99023438, 0.99609375, 0.9921875 , 0.99023438, 0.99414062,
0.99414062])), NeatPubResult(vals=array([0.984375 , 0.99414062, 0.98242188, 0.98828125, 0.98632812,
0.99414062])), NeatPubResult(vals=array([0.96679688, 0.97070312, 0.95898438, 0.97851562, 0.98046875,
0.98828125])), NeatPubResult(vals=array([0.9453125 , 0.953125 , 0.97070312, 0.96875 , 0.98242188,
0.99023438])), NeatPubResult(vals=array([0.93164062, 0.9375 , 0.953125 , 0.96875 , 0.96484375,
0.98046875])), NeatPubResult(vals=array([0.92578125, 0.921875 , 0.93359375, 0.953125 , 0.95898438,
0.9765625 ]))])

Далі застосуй математичні операції для обчислення абсолютної різниці. У решті посібника як показник якості використовується абсолютна різниця для порівняння ідеальних результатів із шумними або експериментальними, однак можна налаштувати й інші показники.

Абсолютна різниця показує, що вплив шуму зростає зі збільшенням розміру схем.

# Figure of merit: Absolute difference
def rdiff(res1, re2):
r"""The absolute difference between `res1` and re2`.

--> The closer to `0`, the better.
"""
d = abs(res1 - re2)
return np.round(d.vals * 100, 2)

for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
vals = rdiff(ideal_res, noisy_res)

# Print the mean absolute difference for the observables
mean_vals = np.round(np.mean(vals), 2)
print(
f"Mean absolute difference between ideal and noisy results for circuits with {all_n_layers[idx]} layers:\n {mean_vals}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
0.72%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
1.17%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
2.6%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
3.16%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
4.4%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
5.5%

Можна дотримуватися таких спрощених орієнтовних рекомендацій для покращення схем цього типу:

  • Якщо середня абсолютна різниця більша за 90%, мітигація, швидше за все, не допоможе.
  • Якщо середня абсолютна різниця менша за 90%, Probabilistic Error Amplification (PEA), імовірно, зможе покращити результати.
  • Якщо середня абсолютна різниця менша за 80%, ZNE з фолдингом вентилів також, ймовірно, зможе покращити результати.

Оскільки всі наведені вище абсолютні різниці менші за 90%, застосування PEA до оригінальної схеми, скоріш за все, покращить якість її результатів. Можна задати різні моделі шуму в аналізаторі. Наступний приклад виконує той самий тест, але додає власну модель шуму.

# Set up a noise model with strength 0.02 on every two-qubit gate
noise_model = NoiseModel()
for qubits in backend.coupling_map:
noise_model.add_quantum_error(
depolarizing_error(0.02, 2), ["ecr", "cx"], qubits
)

# Update the analyzer's noise model
analyzer.noise_model = noise_model

# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)

# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)

# Compare the results
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
values = rdiff(ideal_res, noisy_res)

# Print the mean absolute difference for the observables
mean_values = np.round(np.mean(values), 2)
print(
f"Mean absolute difference between ideal and noisy results for circuits with {all_n_layers[idx]} layers:\n {mean_values}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
0.0%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
0.0%

Як показано, маючи модель шуму, можна спробувати кількісно оцінити вплив шуму на (кліффордизовану версію) PUB-ів, що цікавлять, ще до запуску їх на QPU.

Застосування 2: Порівняльний аналіз різних стратегій

Цей приклад використовує Neat, щоб допомогти визначити найкращі параметри для твоїх PUB-ів. Для цього розглянемо задачу оцінювання з PEA, яку не можна симулювати за допомогою qiskit_aer. Можна скористатися Neat, щоб визначити, які коефіцієнти підсилення шуму працюватимуть найкраще, а потім використати ці коефіцієнти при запуску оригінального експерименту на QPU.

# Generate a circuit with six qubits and six layers
isa_qc = pm.run(generate_circuit(6, 3))

# Use the same observables as previously
pubs = [(isa_qc, isa_obs)]
clifford_pubs = analyzer.to_clifford(pubs)
noise_factors = [
[1, 1.1],
[1, 1.1, 1.2],
[1, 1.5, 2],
[1, 1.5, 2, 2.5, 3],
[1, 4],
]
# Run the PUBs on a QPU
estimator = Estimator(backend)
estimator.options.default_shots = 100000
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.shots_per_randomization = 100
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.amplifier = "pea"

jobs = []
for factors in noise_factors:
estimator.options.resilience.zne.noise_factors = factors
jobs.append(estimator.run(clifford_pubs))

results = [job.result() for job in jobs]
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Look at the mean absolute difference to quickly tell the best choice for your options
for factors, res in zip(noise_factors, results):
d = rdiff(ideal_results[0], res[0])
print(
f"Mean absolute difference for factors {factors}:\n {np.round(np.mean(d), 2)}%\n"
)
Mean absolute difference for factors [1, 1.1]:
6.83%

Mean absolute difference for factors [1, 1.1, 1.2]:
8.76%

Mean absolute difference for factors [1, 1.5, 2]:
8.03%

Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
10.17%

Mean absolute difference for factors [1, 4]:
8.02%

Результат з найменшою різницею підказує, які параметри слід обрати.

Наступні кроки