Przykłady Executora
Wersje pakietów
Kod na tej stronie został opracowany z użyciem poniższych wymagań. Zalecamy używanie tych wersji lub nowszych.
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
Przykłady w tej sekcji ilustrują niektóre typowe sposoby użycia prymitywu Executor. Przed uruchomieniem tych przykładów postępuj zgodnie z instrukcjami w Instalacja Qiskit i Szybki start Executor.
Przed rozpoczęciem
Niektóre z przykładów kodu na tej stronie używają samplex, który jest częścią pakietu Samplomatic. Dlatego przed uruchomieniem tych bloków kodu musisz zainstalować Samplomatic, jak pokazano w poniższym bloku kodu. Więcej informacji znajdziesz w dokumentacji Samplomatic.
pip install samplomatic
# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]
Przykład: Obwód sparametryzowany
Ten przykład ilustruje, jak dodawać elementy obwodu z parametrami, a także jak dodawać elementy samplex. Składa się z następujących kroków:
- Konfiguracja obwodu: Generuj i transpiluj docelowy obwód.
- Przygotowanie samplexa: Grupuj bramki i pomiary w opisane pola i generuj parę szablon obwodu — samplex.
- Wykonanie: Dodaj element obwodu i element samplex do
QuantumProgrami wykonaj oba w jednym zadaniu.
Konfiguracja obwodu
Przygotuj trzy-qubitowy stan GHZ, obróć qubity wokół osi Pauliego-Z i zmierz qubity w bazie obliczeniowej.
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()
Określ backend i transpiluj obwód, aby używał tylko instrukcji obsługiwanych przez QPU (nazywanych obwodem architektury zestawu instrukcji (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)
Przygotowanie samplexa
Użyj funkcji pomocniczej generate_boxing_pass_manager i jej parametrów twirling, aby grupować bramki dwu-qubitowe i pomiary w pola i stosować adnotacje 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)
Użyj metody build, aby wygenerować szablon obwodu i samplex.
# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)
Wykonanie obwodów
Executor uruchamia obiekty QuantumProgram. Każdy QuantumProgram może zawierać kilka elementów. Ten przykład dodaje element obwodu i element samplex do wykonania. Pełne szczegóły znajdziesz w Wejście i wyjście Executora.
Pierwszym krokiem jest zainicjowanie pustego programu, żądając 1024 shotów dla każdej konfiguracji każdego elementu.
# Generate a quantum program
program = QuantumProgram(shots=1024)
Dołącz element obwodu do QuantumProgram. Ten element obwodu składa się z dwóch części — obwodu ISA i 10 zestawów jego wartości parametrów.
# 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
)
Dołącz element samplex do QuantumProgram z następującymi argumentami:
- Szablon obwodu i samplex wygenerowane przez funkcję
build - Dziesięć zestawów wartości parametrów dla oryginalnego obwodu
- Liczba randomizacji do wykonania
# 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),
)
Uruchamianie zadania Executor
# initialize an Executor with default options
executor = Executor(mode=backend)
# Submit the job
job = executor.run(program)
# Retrieve the result
result = job.result()
Pobierz wynik dla każdego zadania.
# 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"]
Przykład: Wykonywanie PEC
Ten przykład pokazuje, jak używać elementu samplex do wykonywania probabilistycznego anulowania błędów (PEC) do mitygacji błędów.
Rozważ lustrzaną wersję obwodu z dziesięcioma qubitami i dwiema unikalnymi warstwami bramek CX. Oto główne zadania:
- Wykonaj obwód z twirlingiem.
- Wykonaj obwód z mitygacją PEC, jak w artykule "Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors".
Potok składa się z następujących kroków:
- Konfiguracja: Generuj docelowy obwód i grupuj jego operacje w pola.
- Uczenie: Naucz się szumu instrukcji, które chcesz mitygować za pomocą PEC.
- Wykonanie: Uruchom obwód na backendzie.
- Analiza: przetwarzanie końcowe i analiza wyników.
Dla porównania uruchomimy ten lustrzany obwód dwukrotnie. Raz z zastosowanym tylko twirlingiem Pauliego, i raz z zastosowaną mitygacją PEC.
Użycie dla tego przykładu wynosi około 10 minut na procesorze Heron r2.
Konfiguracja obwodu
Wybierz backend i przygotuj 10-qubitowy obwód.
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)
Połącz obwód z jego odwrotnością, aby stworzyć lustrzany obwód.
mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()
mirror_circuit.draw("mpl", scale=0.35, fold=100)
Ustaw pewne wartości parametrów:
import numpy as np
parameter_values = np.random.rand(mirror_circuit.num_parameters)
Użyj menedżera przejść do transpilacji obwodu na obwód ISA.
preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)
isa_circuit = preset_pass_manager.run(mirror_circuit)
Następnie zgrupuj bramki i pomiary w opisane pola. Możesz to zrobić ręcznie lub użyć funkcji generate_boxing_pass_manager z Samplomatic dla wygody. Pierwszy obwód będzie miał zastosowany tylko twirling, dlatego potrzebuje tylko adnotacji Twirl. Drugi obwód będzie uruchamiany z pełną mitygacją PEC i potrzebuje zarówno adnotacji Twirl, jak i 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)
Uczenie szumu
Aby zminimalizować liczbę eksperymentów uczenia szumu, zidentyfikuj unikalne instrukcje w drugim obwodzie (tym z polami opisanymi adnotacją InjectNoise). Przy definiowaniu unikalności dwie instrukcje pól są równe, jeśli oba z poniższych warunków są spełnione:
- Ich zawartość jest równa, z dokładnością do bramek jednoqubitowych.
- Ich adnotacja
Twirljest równa (każda inna adnotacja jest pomijana).
Prowadzi to do trzech unikalnych instrukcji, mianowicie nieparzystych i parzystych pól bramek oraz końcowego pola pomiarowego.
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
Zainicjuj NoiseLearnerV3, wybierz parametry uczenia poprzez ustawienie jego opcji i uruchom zadanie uczenia szumu.
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()
Przekonwertuj result na obiekt wymagany przez samplex za pomocą metody result.to_dict.
noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)
Wykonanie obwodów
Executor uruchamia obiekty QuantumProgram. Każdy QuantumProgram może zawierać kilka elementów, które są dołączane do programu. Każdy element to zadanie dla programu do wykonania.
Zainicjuj pusty program, żądając 1000 shotów dla każdej konfiguracji każdego elementu.
from qiskit_ibm_runtime.quantum_program import QuantumProgram
# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)
Następnie zbuduj szablon obwodu i samplex dla mirror_circuit_twirl i dołącz je do programu. Zażądaj też 900 randomizacji od samplexa. Oznacza to, że samplex wygeneruje 900 zestawów parametrów, a każdy zestaw będzie wykonywany 1000 razy (liczba shotów) na QPU.
To jest pierwsze zadanie programu (wynik 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,),
)
Podobnie dołącz szablon obwodu i samplex zbudowany dla mirror_circuit_pec, żądając 900 randomizacji. To jest drugie zadanie programu (wynik 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,),
)
Importuj Executor i prześlij zadanie.
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)
Analiza wyników
Na koniec przetwórz końcowo wyniki, aby estymować wartości oczekiwane jednoqubitowych operatorów Pauliego-Z działających na każdym z dziesięciu aktywnych qubitów (oczekiwana wartość: 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
Następne kroki
- Przejrzyj omówienie broadcastingu.
- Dowiedz się, jak używać opcji Executora.
- Zapoznaj się z modelem skierowanego wykonania.
- Przejrzyj dokumentację Samplomatic.
- Dowiedz się, jak łączyć różne techniki mitygacji błędów podczas używania modelu skierowanego wykonania w tutorialu Probabilistic error cancellation with shaded lightcones.