Приклади Executor
Версії пакетів
Код на цій сторінці розроблено з такими вимогами. Рекомендуємо використовувати ці версії або новіші.
qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
samplomatic~=0.18.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic
Приклади в цьому розділі ілюструють деякі поширені способи використання примітиву Executor. Перед запуском цих прикладів виконай інструкції з Встановлення Qiskit та Швидкий старт з Executor.
Перш ніж почати
Деякі приклади коду на цій сторінці використовують samplex, який є частиною пакету Samplomatic. Тому перед запуском цих блоків коду потрібно встановити Samplomatic, як показано в наступному блоці коду. Для отримання додаткової інформації дивись документацію Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]
Приклад: Параметризоване коло
Цей приклад ілюструє, як додавати елементи кола з параметрами, а також як додавати елементи samplex. Він складається з таких кроків:
- Налаштування кола: згенеруй і транспілюй цільове коло.
- Підготовка samplex: згрупуй вентилі та вимірювання в анотовані блоки та згенеруй пару шаблону кола та samplex.
- Виконання: додай елемент кола та елемент samplex до
QuantumProgramі виконай обидва в одному завданні.
Налаштування кола
Підготуй трикубітний стан GHZ, поверни кубіти навколо осі Паулі-Z та виміряй кубіти у обчислювальному базисі.
from qiskit.circuit import Parameter, QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager
# Generate the circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
circuit.rz(Parameter("lam"), 2)
circuit.measure_all()
Вкажи Backend і транспілюй коло, щоб воно використовувало лише інструкції, підтримувані QPU (так звана схема архітектури набору інструкцій (ISA)).
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Transpile the circuit to ISA
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = preset_pass_manager.run(circuit)
Підготовка samplex
Використай зручну функцію generate_boxing_pass_manager та її параметри twirling, щоб згрупувати двокубітні вентилі та вимірювання у блоки та застосувати анотації twirling.
boxing_pm = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)
boxed_circuit = boxing_pm.run(isa_circuit)
Використай метод build для генерації шаблону кола та samplex.
# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)
Виконання кіл
Executor запускає об'єкти QuantumProgram. Кожна QuantumProgram може містити кілька елементів. Цей приклад додає елемент кола та елемент samplex для виконання. Повні деталі дивись у розділі Вхідні та вихідні дані Executor.
Перший крок — ініціалізувати порожню програму, запитавши 1024 shots для кожної конфігурації кожного елемента.
# Generate a quantum program
program = QuantumProgram(shots=1024)
Додай елемент кола до QuantumProgram. Цей елемент кола складається з двох частин: ISA-кола та 10 наборів значень його параметрів.
# Append the circuit and the parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, 3), # 10 sets of parameter values
)
Додай елемент samplex до QuantumProgram з такими аргументами:
- Шаблон кола та samplex, згенеровані функцією
build - Десять наборів значень параметрів для вихідного кола
- Кількість рандомізацій для виконання
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(
10, 3
), # 10 sets of parameter values
},
shape=(2, 14, 10),
)
Запуск завдання Executor
# initialize an Executor with default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()
Отримай результат для кожного завдання.
# Access the results of the classical register of task #0, the CircuitItem
result_0 = result[0]["meas"]
# Access the results of the classical register of task #1, the SamplexItem
result_1 = result[1]["meas"]
Приклад: Виконання PEC
Цей приклад показує, як використовувати елемент samplex для виконання імовірнісного скасування помилок (PEC) з метою пом'якшення помилок.
Розглянемо дзеркальну версію кола з десятьма кубітами та двома унікальними шарами вентилів CX. Ось основні завдання:
- Виконай коло з twirling.
- Виконай коло з пом'якшенням PEC, як у статті "Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors".
Конвеєр складається з таких кроків:
- Налаштування: згенеруй цільове коло та згрупуй його операції у блоки.
- Навчання: навчи шум інструкцій, які ми хочемо пом'якшити за допомогою PEC.
- Виконання: запусти коло на Backend.
- Аналіз: постобробка та аналіз результатів.
Для порівняння ми запустимо це дзеркальне коло двічі. Один раз — лише з застосованим Pauli-twirling, і один раз — з застосованим пом'якшенням PEC.
Використання для цього прикладу становить приблизно 10 хвилин на процесорі Heron r2.
Налаштування кола
Вибери Backend і підготуй 10-кубітне коло.
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.transpiler import generate_preset_pass_manager
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic import build
# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Prepare a circuit
num_qubits = 10
num_layers = 10
qubits = list(range(num_qubits))
circuit = QuantumCircuit(num_qubits)
for layer_idx in range(num_layers):
circuit.rx(Parameter(f"theta_{layer_idx}"), qubits)
for i in range(num_qubits // 2):
circuit.cz(qubits[2 * i], qubits[2 * i + 1])
circuit.rx(Parameter(f"phi_{layer_idx}"), qubits)
for i in range(num_qubits // 2 - 1):
circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)
circuit.draw("mpl", scale=0.35, fold=100)
Поєднай коло з його оберненим, щоб створити дзеркальне коло.
mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()
mirror_circuit.draw("mpl", scale=0.35, fold=100)
Встанови кілька значень параметрів:
import numpy as np
parameter_values = np.random.rand(mirror_circuit.num_parameters)
Використай менеджер проходів для транспіляції кола, щоб воно стало ISA-колом.
preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)
isa_circuit = preset_pass_manager.run(mirror_circuit)
Далі згрупуй вентилі та вимірювання в анотовані блоки. Це можна зробити вручну або скористатися функцією generate_boxing_pass_manager з Samplomatic для зручності. Перше коло матиме лише застосоване twirling і тому потребує лише анотації Twirl. Друге коло буде запущено з повним пом'якшенням PEC і потребує анотацій Twirl та InjectNoise.
# Pass manager used to create twirled-annotated boxes.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
mirror_circuit_twirl = boxing_pm.run(isa_circuit)
# Pass manager used to create a new boxed circuit with
# both Twirl and InjectNoise annotations.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)
mirror_circuit_pec = boxing_pm.run(isa_circuit)
Навчання шуму
Щоб мінімізувати кількість експериментів з навчання шуму, визначи унікальні інструкції у другому колі (тому, що має блоки з анотацією InjectNoise). При визначенні унікальності дві інструкції блоку вважаються рівними, якщо обидві наступні умови виконуються:
- Їхній вміст рівний, аж до однокубітних вентилів.
- Їхня анотація
Twirlрівна (будь-яка інша анотація ігнорується).
Це призводить до трьох унікальних інструкцій, а саме непарних і парних блоків вентилів та фінального блоку вимірювання.
from samplomatic.utils import find_unique_box_instructions
unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3
Ініціалізуй NoiseLearnerV3, вибери параметри навчання, встановивши його опції, та запусти завдання навчання шуму.
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]
learner_job = learner.run(unique_box_instructions)
learner_job.job_id()
learner_result = learner_job.result()
Перетвори result на об'єкт, необхідний для samplex, використовуючи метод result.to_dict.
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)
Виконання кіл
Executor запускає об'єкти QuantumProgram. Кожна QuantumProgram може містити кілька елементів, які додаються до програми. Кожен елемент — це завдання для виконання програмою.
Ініціалізуй порожню програму, запитавши 1000 shots для кожної конфігурації кожного елемента.
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)
Далі побудуй шаблон кола та samplex для mirror_circuit_twirl і додай їх до програми. Також запроси 900 рандомізацій від samplex. Це означає, що samplex згенерує 900 наборів параметрів, і кожен набір буде виконано 1000 разів (кількість shots) на QPU.
Це перше завдання програми (результат 0).
template_twirl, samplex_twirl = build(mirror_circuit_twirl)
program.append_samplex_item(
template_twirl,
samplex=samplex_twirl,
samplex_arguments={"parameter_values": parameter_values},
shape=(900,),
)
Аналогічно, додай шаблон кола та samplex, побудовані для mirror_circuit_pec, запитавши 900 рандомізацій. Це друге завдання програми (результат 1).
template_pec, samplex_pec = build(mirror_circuit_pec)
program.append_samplex_item(
template_pec,
samplex=samplex_pec,
samplex_arguments={
"parameter_values": parameter_values,
"pauli_lindblad_maps": noise_maps,
"noise_scales": {
ref: -1.0 for ref in noise_maps
}, # Set the scales to -1 for PEC
},
shape=(900,),
)
Імпортуй Executor і надішли завдання.
from qiskit_ibm_runtime.executor import Executor
executor = Executor(backend)
executor_job = executor.run(program)
executor_job.job_id()
executor_results = executor_job.result()
executor_results
twirl_result = executor_results[0]
print(f"Twirl result keys:\n {list(twirl_result.keys())}\n")
print(f"Shape of results: {twirl_result['meas'].shape}")
pec_result = executor_results[1]
print(f"PEC result keys:\n {list(pec_result.keys())}\n")
print(f"Shape of results: {pec_result['meas'].shape}")
Twirl result keys:
['meas', 'measurement_flips.meas']
Shape of results: (900, 1000, 10)
PEC result keys:
['meas', 'measurement_flips.meas', 'pauli_signs']
Shape of results: (900, 1000, 10)
Аналіз результатів
Нарешті, постобробимо результати для оцінки очікуваних значень однокубітних операторів Паулі-Z, що діють на кожному з десяти активних кубітів (очікуване значення: 1.0).
# Undo measurement twirling
twirl_result_unflipped = (
twirl_result["meas"] ^ twirl_result["measurement_flips.meas"]
)
# Calculate the expectation values of single-qubit Z operators
exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.77
Qubit 1 -> 0.76
Qubit 2 -> 0.66
Qubit 3 -> 0.71
Qubit 4 -> 0.69
Qubit 5 -> 0.67
Qubit 6 -> 0.62
Qubit 7 -> 0.59
Qubit 8 -> 0.62
Qubit 9 -> 0.68
# Undo measurement twirling
pec_result_unflipped = (
pec_result["meas"] ^ pec_result["measurement_flips.meas"]
)
# Calculate the signs for PEC mitigation
signs = np.prod((-1) ** pec_result["pauli_signs"], axis=-1)
signs = signs.reshape((signs.shape[0], 1))
# Calculate the expectation values of single-qubit Z operators as required by
# PEC mitigation
exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)
for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.98
Qubit 1 -> 0.99
Qubit 2 -> 0.96
Qubit 3 -> 0.98
Qubit 4 -> 0.98
Qubit 5 -> 0.98
Qubit 6 -> 0.98
Qubit 7 -> 0.95
Qubit 8 -> 0.95
Qubit 9 -> 0.94
Наступні кроки
- Переглянь огляд broadcasting.
- Дізнайся, як використовувати параметри Executor.
- Зрозумій модель спрямованого виконання.
- Переглянь документацію Samplomatic.
- Дізнайся про комбінування різних методів пом'якшення помилок при використанні моделі спрямованого виконання у посібнику Probabilistic error cancellation with shaded lightcones.