Przejdź do głównej treści

QESEM: Funkcja Qiskit firmy Qedma

Zobacz dokumentację API

Wersje pakietów

Kod na tej stronie został opracowany przy użyciu następujących wymagań. Zalecamy korzystanie z tych wersji lub nowszych.

qiskit[all]~=2.5.0
qiskit-ibm-runtime~=0.47.0
Uwaga

Funkcje Qiskit to funkcja eksperymentalna dostępna wyłącznie dla użytkowników planów IBM Quantum® Premium, Flex oraz On-Prem (za pośrednictwem IBM Quantum Platform API). Mają status wydania podglądowego i mogą ulec zmianie.

Przegląd​

Choć procesory kwantowe (QPU) znacznie się poprawiły w ostatnich latach, błędy wynikające z szumów i niedoskonałości istniejącego sprzętu nadal stanowią kluczowe wyzwanie dla twórców algorytmów kwantowych. W miarę jak dziedzina zbliża się do kwantowych obliczeń w skali użytkowej, których nie można zweryfikować klasycznie, rozwiązania umożliwiające eliminację szumów z gwarantowaną dokładnością stają się coraz ważniejsze. Aby sprostać temu wyzwaniu, Qedma opracowała metodę Quantum Error Mitigation (QESEM), bezproblemowo zintegrowaną z IBM Quantum Platform jako Funkcja Qiskit.

Dzięki QESEM możesz uruchamiać swoje obwody kwantowe na zaszumionych QPU i uzyskiwać wysoce dokładne wyniki wolne od błędów przy minimalnym narzucie czasowym QPU, bliskim fundamentalnym granicom. Aby to osiągnąć, QESEM korzysta z zestawu autorskich metod opracowanych przez Qedmę, służących do charakteryzacji i redukcji błędów. Techniki redukcji błędów obejmują optymalizację Gate, transpilację uwzględniającą szumy, tłumienie błędów (ES) oraz nieobciążone łagodzenie błędów (EM). Dzięki połączeniu tych metod opartych na charakteryzacji możesz uzyskiwać wiarygodne, wolne od błędów wyniki dla ogólnych kwantowych Circuit o dużej objętości, otwierając zastosowania niemożliwe do realizacji w inny sposób.

Pełny opis składowych oraz demonstrację w skali użytkowej znajdziesz w artykule Reliable high-accuracy error mitigation for utility-scale quantum circuits.

Opis​

Możesz używać funkcji QESEM firmy Qedma, aby łatwo szacować i wykonywać swoje Circuit z tłumieniem i łagodzeniem błędów, osiągając większe objętości Circuit i wyższą dokładność. Aby korzystać z QESEM, podajesz Circuit kwantowy, zestaw obserwowalnych do zmierzenia, docelową dokładność statystyczną dla każdej obserwowalnej oraz wybrany QPU. Zanim uruchomisz Circuit do docelowej dokładności, możesz oszacować wymagany czas QPU na podstawie obliczeń analitycznych, które nie wymagają wykonania Circuit. Gdy jesteś zadowolony z oszacowania czasu QPU, możesz wykonać Circuit za pomocą QESEM.

Gdy wykonujesz Circuit, QESEM uruchamia protokół charakteryzacji urządzenia dostosowany do twojego Circuit, uzyskując wiarygodny model szumu dla błędów występujących w Circuit. Na podstawie charakteryzacji QESEM najpierw implementuje transpilację uwzględniającą szumy, aby odwzorować wejściowy Circuit na zestaw fizycznych qubit i bramka, minimalizując szum wpływający na docelową obserwowalną. Obejmuje to natywnie dostępne Gate (CX/CZ na urządzeniach IBM®) oraz dodatkowe Gate zoptymalizowane przez QESEM, tworzące rozszerzony zestaw Gate QESEM. Następnie QESEM uruchamia zestaw Circuit ES i EM opartych na charakteryzacji na QPU i zbiera wyniki pomiarów. Są one następnie przetwarzane klasycznie, aby dostarczyć nieobciążoną wartość oczekiwaną i słupek błędu dla każdej obserwowalnej, odpowiadające żądanej dokładności.

Przegląd Qedma QESEM Wykazano, że QESEM zapewnia wyniki o wysokiej dokładności dla różnorodnych zastosowań kwantowych i na największych objętościach Circuit osiągalnych dziś. QESEM oferuje następujące funkcje dostępne dla użytkownika, zademonstrowane w sekcji benchmarków poniżej:

  • Gwarantowana dokładność: QESEM generuje nieobciążone szacowania wartości oczekiwanych obserwowalnych. Jego metoda EM jest wyposażona w teoretyczne gwarancje, które – w połączeniu z najnowocześniejszą charakteryzacją Qedmy – zapewniają, że łagodzenie błędów zbiega do wyjścia Circuit bez szumów z dokładnością określoną przez użytkownika. W przeciwieństwie do wielu heurystycznych metod EM podatnych na systematyczne błędy lub odchylenia, gwarantowana dokładność QESEM jest niezbędna do uzyskiwania wiarygodnych wyników dla ogólnych Circuit kwantowych i obserwowalnych.
  • Skalowalność do dużych QPU: Czas QPU QESEM zależy od objętości Circuit, ale poza tym jest niezależny od liczby qubit. Qedma zademonstrowała QESEM na największych dostępnych dziś urządzeniach kwantowych, w tym IBM Quantum Eagle o 127 qubit i Heron o 133 qubit.
  • Agnostyczność względem zastosowań: QESEM zademonstrowano na różnorodnych zastosowaniach, w tym symulacji Hamiltonianu, VQE, QAOA i szacowaniu amplitudy. Możesz wprowadzić dowolny Circuit kwantowy i obserwowalną do zmierzenia i uzyskać dokładne wyniki wolne od błędów. Jedyne ograniczenia wynikają ze specyfikacji sprzętu i przydzielonego czasu QPU, które określają dostępne objętości Circuit i dokładności wyjścia. W przeciwieństwie do tego wiele rozwiązań redukcji błędów jest specyficznych dla danego zastosowania lub obejmuje niekontrolowane heurystyki, co sprawia, że nie nadają się do ogólnych Circuit kwantowych i zastosowań.
  • Rozszerzony zestaw Gate: QESEM obsługuje Gate z ułamkowymi kątami i udostępnia zoptymalizowane przez Qedmę Gate Rzz(θ)Rzz(\theta) z ułamkowymi kątami na urządzeniach IBM Quantum Heron i Eagle. Ten rozszerzony zestaw Gate umożliwia efektywniejszą kompilację i odblokowuje objętości Circuit większe nawet o czynnik 2 w porównaniu z domyślną kompilacją CX/CZ.
  • Wielobazowe obserwowalne: QESEM obsługuje obserwowalne wejściowe złożone z wielu niekomutujących ciągów Pauliego, takich jak ogólne Hamiltoniany. Wybór baz pomiarowych oraz optymalizacja alokacji zasobów QPU (strzały i obwód) są następnie wykonywane automatycznie przez QESEM w celu minimalizacji wymaganego czasu QPU dla żądanej dokładności. Ta optymalizacja, uwzględniająca wierności sprzętu i szybkości wykonania, umożliwia uruchamianie głębszych Circuit i uzyskiwanie wyższych dokładności.

Benchmarki​

QESEM zostało przetestowane na szerokiej gamie przypadków użycia i zastosowań. Poniższe przykłady mogą pomóc ci ocenić, jakie typy obciążeń możesz uruchamiać z QESEM.

Kluczową miarą ilościową trudności zarówno łagodzenia błędów, jak i klasycznej symulacji dla danego Circuit i obserwowalnej jest aktywna objętość: liczba bramek CNOT wpływających na obserwowalną w Circuit. Aktywna objętość zależy od głębokości i szerokości Circuit, od wagi obserwowalnej oraz od struktury Circuit, która określa stożek świetlny obserwowalnej. Więcej szczegółów znajdziesz w prezentacji z IBM Quantum Summit 2024. QESEM zapewnia szczególnie dużą wartość w reżimie wysokiej objętości, dając wiarygodne wyniki dla ogólnych Circuit i obserwowalnych.

Aktywna objętość

ZastosowanieLiczba qubitUrządzenieOpis CircuitDokładnośćCzas całkowityUżycie Runtime
Circuit VQE8Eagle (r3)21 warstw łącznie, 9 baz pomiarowych, łańcuch 1D98%35 min14 min
Kicked Ising28Eagle (r3)3 unikalne warstwy x 3 kroki, topologia 2D heavy-hex97%22 min4 min
Kicked Ising28Eagle (r3)3 unikalne warstwy x 8 kroków, topologia 2D heavy-hex97%116 min23 min
Symulacja Hamiltonianu metodą Trottera40Eagle (r3)2 unikalne warstwy x 10 kroków Trottera, łańcuch 1D97%3 godziny25 min
Symulacja Hamiltonianu metodą Trottera119Eagle (r3)3 unikalne warstwy x 9 kroków Trottera, topologia 2D heavy-hex95%6,5 godziny45 min
Kicked Ising136Heron (r2)3 unikalne warstwy x 15 kroków, topologia 2D heavy-hex99%52 min9 min

Dokładność mierzona jest tutaj względem idealnej wartości obserwowalnej: ⟨O⟩ideal−ϵ⟨O⟩ideal\frac{\langle O \rangle_{ideal} - \epsilon}{\langle O \rangle_{ideal}}, gdzie 'ϵ\epsilon' to bezwzględna precyzja łagodzenia błędów (ustawiana przez użytkownika), a ⟨O⟩ideal\langle O \rangle_{ideal} to obserwowalna dla Circuit bez szumów. „Użycie Runtime" mierzy zużycie benchmarku w trybie wsadowym (suma użycia poszczególnych zadań), natomiast „czas całkowity" mierzy użycie w trybie Session (czas ścienny eksperymentu), który obejmuje dodatkowe czasy klasyczne i komunikacyjne. QESEM jest dostępny do wykonania w obu trybach, dzięki czemu możesz jak najlepiej wykorzystać dostępne zasoby.

Obwody Kicked Ising o 28 qubit symulują Dyskretny Kwaziryształ Czasowy badany przez Shinjo et al. (zob. arXiv 2403.16718 i Q2B24 Tokyo) na trzech połączonych pętlach ibm_kawasaki. Parametry Circuit przyjęte tutaj to (θx,θz)=(0.9π,0)(\theta_x, \theta_z) = (0.9 \pi, 0), ze ferromagnetycznym stanem początkowym ∣ψ0⟩=∣0⟩⊗n| \psi_0 \rangle = | 0 \rangle ^{\otimes n}. Mierzoną obserwowalną jest wartość bezwzględna magnetyzacji M=∣128∑i=027⟨Zi⟩∣M = |\frac{1}{28} \sum_{i=0}^{27} \langle Z_i \rangle|. Eksperyment Kicked Ising w skali użytkowej przeprowadzono na 136 najlepszych qubit urządzenia ibm_fez; ten konkretny benchmark przeprowadzono przy kącie Clifforda (θx,θz)=(π,0)(\theta_x, \theta_z) = (\pi, 0), przy którym aktywna objętość rośnie wolno wraz z głębokością Circuit, co – w połączeniu z wysoką wiernością urządzenia – umożliwia wysoką dokładność przy krótkim czasie działania.

Obwody symulacji Hamiltonianu metodą Trottera dotyczą modelu Isinga z polem poprzecznym przy ułamkowych kątach: (θzz,θx)=(π/4,π/8)(\theta_{zz}, \theta_x) = (\pi / 4, \pi /8) i (θzz,θx)=(π/6,π/8)(\theta_{zz}, \theta_x) = (\pi / 6, \pi / 8) odpowiednio (zob. Q2B24 Tokyo). Circuit w skali użytkowej uruchomiono na 119 najlepszych qubit urządzenia ibm_brisbane, natomiast eksperyment z 40 qubit przeprowadzono na najlepszym dostępnym łańcuchu. Dokładność jest podana dla magnetyzacji; wyniki o wysokiej dokładności uzyskano również dla obserwowalnych o większej wadze.

Circuit VQE został opracowany wspólnie z badaczami z Centrum Technologii Kwantowych i Zastosowań w Deutsches Elektronen-Synchrotron (DESY). Docelową obserwowalną był tutaj Hamiltonian składający się z dużej liczby niekomutujących ciągów Pauliego, co podkreśla zoptymalizowaną wydajność QESEM dla obserwowalnych wielobazowych. Łagodzenie błędów zastosowano do klasycznie zoptymalizowanego ansatzu; choć wyniki te są jeszcze nieopublikowane, wyniki tej samej jakości zostaną uzyskane dla różnych Circuit o podobnych właściwościach strukturalnych.

Pierwsze kroki​

Uwierzytelnij się przy użyciu swojego klucza API IBM Quantum Platform i wybierz Funkcję Qiskit QESEM w następujący sposób. (Ten fragment kodu zakłada, że masz już zapisane konto w swoim lokalnym środowisku.)

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-catalog qiskit-ibm-runtime
import qiskit
from qiskit_ibm_catalog import QiskitFunctionsCatalog

catalog = QiskitFunctionsCatalog(channel="ibm_quantum_platform")

# verify that you have access to the function
catalog.list()
# load the function
qesem_function = catalog.load("qedma/qesem")

Przykłady​

Przykład zadania szacowania czasu​

Zadanie szacowania czasu jest przydatne do oszacowania czasu QPU wymaganego dla danego pub i backend_name. backend_name można również ustawić na dowolny backend symulatora; na przykład fake_fez.

QESEM wykorzystuje quasi-probabilistyczną metodę EM opartą na charakteryzacji. Ta metoda ma narzut czasu QPU, który skaluje się w przybliżeniu jako:

TQPU=aeαIF⋅Vaϵ2+bT_{QPU} = a \frac{e^{\alpha IF\cdot V_a}}{\epsilon^2} + b

Gdzie VaV_a to aktywna objętość obwodu, ϵ\epsilon to docelowa precyzja, a IFIF to niewierność bramek natywnych.

Zauważ, że "estimate_time_only": "empirical" wykorzystuje kilka minut czasu QPU do oszacowania czasu wymaganego dla zadania (jeśli backend to prawdziwe urządzenie; jeśli to symulator, wtedy czas QPU nie jest wykorzystywany). Zwykle zajmuje to około 5 minut, ale nie więcej niż 10 minut. Jeśli niewierność zmienia się drastycznie między empirycznym zadaniem szacowania czasu a zadaniem mitygacji, czas QPU również zmieni się drastycznie.

Aby zacząć, wypróbuj ten podstawowy przykład szacowania wymaganego czasu QPU do uruchomienia QESEM dla danego pub:

# This cell is hidden from users
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
backend_name = service.least_busy().name
backend_name = "fake_fez"

circ = qiskit.QuantumCircuit(5)
circ.cx(0, 1)
circ.cx(2, 3)
circ.cx(1, 2)
circ.cx(3, 4)

avg_magnetization = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
[("Z", [q], 1 / 5) for q in range(5)], num_qubits=5
)
other_observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
[("ZZ", [0, 1], 1.0), ("XZ", [1, 4], 0.5)], num_qubits=5
)

time_estimation_job = qesem_function.run(
pubs=[(circ, [avg_magnetization, other_observable])],
options={
"estimate_time_only": "empirical",
},
backend_name=backend_name, # example: "fake_fez", "ibm_fez"
)
time_estimate_result = (
time_estimation_job.result()
) # a list of results per pub (circuit)

Poniższy fragment kodu pokazuje, jak pobrać różne metryki wykonania z zadania szacowania czasu (gdy estimate_time_only jest ustawione):

pub_result = time_estimate_result[0]

print(
f"The estimated QPU time for mitigation for this PUB is: {pub_result.metadata['time_estimation_sec']}"
)
print(
f"The QPU time that this time estimation job took is (here it is 0 because we used fake_fez): {pub_result.metadata['total_qpu_time']}"
)
print(
f"Gates fidelity measured during the experiment: {pub_result.metadata['gate_fidelities']}"
)
print(f"Total shots: {pub_result.metadata['total_shots']}")
print(f"Resource usage breakdown: {pub_result.metadata['resource_usage']}")
The estimated QPU time for mitigation for this PUB is: 300
The QPU time that this time estimation job took is (here it is 0 because we used fake_fez): 0
Gates fidelity measured during the experiment: {'CZ': 0.9951354916722668, 'ID1Q': 0.9991246627329172}
Total shots: 220000
Resource usage breakdown: {'RUNNING: MAPPING': {'CPU_TIME': 33.6066133165732, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 184.53575124032795, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: EXECUTING_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: POST_PROCESSING': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}}

Gdy włączone jest empiryczne szacowanie czasu, QESEM uruchamia niewielką liczbę obwodów, aby zapewnić dokładniejsze oszacowanie czasu QPU. Wyniki mitygacji z tych obwodów są dostępne w polu empirical_estimation_mitigation_results zadania.

Ponieważ te wyniki pochodzą z ograniczonej próbki obwodów, są znacznie mniej dokładne niż ostateczne wyniki uzyskane z pełnego zadania mitygacji QESEM. Jednak gdy obwód jest mały lub docelowa precyzja jest niska, mitygacja przeprowadzona podczas empirycznego szacowania czasu może być wystarczająca, aby osiągnąć pożądaną precyzję, a pełne zadanie mitygacji może nie być konieczne.

Pole empirical_estimation_mitigation_results to lista obiektów PrimitiveResult dla każdego parametru wejściowego. Jeśli obwód nie jest sparametryzowany, lista będzie miała długość 1.

empirical_estimation_mitigation_results = time_estimate_result[0].metadata[
"empirical_estimation_mitigation_results"
][0] # a list per parameter
print("Partial results for the observables:")

print(
f" Mitigated expectation values: {empirical_estimation_mitigation_results.data.evs}"
)
print(
f" Mitigated error bars: {empirical_estimation_mitigation_results.data.stds}"
)
print(
f" Number of shots used for mitigation: {empirical_estimation_mitigation_results.metadata['mitigation_shots']}"
)
transpiled_circ = empirical_estimation_mitigation_results.metadata[
"transpiled_circ"
]
print(f" Qubit mapping: {transpiled_circ['qubit_maps']}")
print(
f" Number of measurement bases: {transpiled_circ['num_measurement_bases']}\n"
)

# results per obs
emp_obs_results = empirical_estimation_mitigation_results.metadata["results"][
0
]
# print(f"Results for each observable: {results}")
print("Results for each observable:")

for i, (obs_array, result_dict) in enumerate(emp_obs_results):
# obs_array, result_dict = results
print(f"Observable {i+1}: {obs_array}")
print(
f" QESEM mitigated value: {result_dict['qesem']['value']} \u00b1 {result_dict['qesem']['error_bar']}"
)
Partial results for the observables:
Mitigated expectation values: [1.00347302 1.00693905]
Mitigated error bars: [0.00304061 0.00714276]
Number of shots used for mitigation: 180000
Qubit mapping: [[[0, 136], [1, 143], [2, 142], [3, 141], [4, 140]]]
Number of measurement bases: 2

Results for each observable:
Observable 1: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
QESEM mitigated value: 1.003473015776871 ± 0.0030406128032204015
Observable 2: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
QESEM mitigated value: 1.0069390542613554 ± 0.0071427606736885925

Przykład zadania mitygacji QESEM​

Poniższy przykład wykonuje zadanie QESEM:

sample_job = qesem_function.run(
pubs=[(circ, [avg_magnetization, other_observable])],
backend_name=backend_name, # example: "ibm_fez"
# options = {
# "estimate_time_only": "empirical",
# "default_precision": 0.2, # Default precision is applied to all pubs that don't have a precision specified, see API reference for more details
# "max_execution_time": 3600, # You can specify a maximum QPU time in seconds, see API reference for more details
# "transpilation_level": "standard", # "minimal_with_layout_opt" for minimal transpilation, see API reference for more details
# "parallel_execution": True, # True for parallel execution, see API reference for more details
# },
)

Aby uzyskać szczegółowy opis każdego pola wejściowego i opcji, zobacz dokumentację API QESEM. Możesz używać znanych interfejsów API Qiskit Serverless, aby sprawdzić status obciążenia swojej Funkcji Qiskit lub zwrócić wyniki:

# Print the ID so you can use it later, if necessary
print(sample_job.job_id)
print(sample_job.status())
sample_result = sample_job.result()
3ac6b2df-15b0-4dc0-8f48-cf14bd20a1c8
DONE

Poniższy fragment kodu pokazuje, jak pobrać wyniki mitigacji oraz metryki wykonania. Zawierają one kluczowe dane umożliwiające głębsze zrozumienie wpływu różnych parametrów na wykonanie QESEM. Mogą być również przydatne podczas pisania artykułu naukowego opartego na twoich badaniach.

for pub_idx, pub_result in enumerate(
sample_result
): # each element in the list is a result for a different pub, here we sent only one pub
print(f"\nPUB {pub_idx}:")
print(
f" The QPU time that this job took is (here it is 0 because we used fake_fez): {pub_result.metadata['total_qpu_time']}"
)
print(
f" Gates fidelity measured during the experiment: {pub_result.metadata['gate_fidelities']}"
)
print(f" Total shots: {pub_result.metadata['total_shots']}")
print(
f" Number of shots used for mitigation: {pub_result.metadata['mitigation_shots']}"
)
print(
f" Resource usage breakdown: {pub_result.metadata['resource_usage']}"
)
PUB 0:
The QPU time that this job took is (here it is 0 because we used fake_fez): 0.0
Gates fidelity measured during the experiment: {'CZ': 0.9953704216147041, 'ID1Q': 0.9991834123567518}
Total shots: 446000
Number of shots used for mitigation: 194000
Resource usage breakdown: {'RUNNING: MAPPING': {'CPU_TIME': 32.52745003718883, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: OPTIMIZING_FOR_HARDWARE': {'CPU_TIME': 257.850521848537, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: WAITING_FOR_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: EXECUTING_QPU': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}, 'RUNNING: POST_PROCESSING': {'CPU_TIME': 0.0, 'GPU_TIME': 0.0, 'QPU_TIME': 0.0}}

W metadata["results"] wyniki są grupowane najpierw według instancji obwodu, a następnie według obserwabli.

print("Full QESEM mitigation results:")

for pub_idx, pub_result in enumerate(sample_result):
print(f"\nPUB {pub_idx}:")

print(f" Mitigated expectation values: {pub_result.data.evs}")
print(f" Mitigated error bars: {pub_result.data.stds}")
noisy_results = pub_result.metadata.get("noisy_results")
print(f" Unmitigated expectation values: {noisy_results.evs}")
print(f" Unmitigated error bars: {noisy_results.stds}")

print(" Transpiled circuits:")
for circ_idx, transpiled_circ in enumerate(
pub_result.metadata["transpiled_circs"]
):
print(f" Circuit {circ_idx}:")
# print(f" Circuit: \n {transpiled_circ['circuit']}") # not printing it because it's long but you can see the transpiled circuit itself
print(f" Qubit mapping: {transpiled_circ['qubit_maps']}")
print(
f" Measurement bases: {transpiled_circ['num_measurement_bases']}"
)

print(" Results for each observable:")
for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
print(f" Circuit {circ_idx}:")
for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
print(f" Observable {obs_idx}: {obs_array}")
print(
f" QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
)
print(
f" Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
)
Full QESEM mitigation results:

PUB 0:
Mitigated expectation values: [1.00648343 1.00636289]
Mitigated error bars: [0.00253812 0.00693586]
Unmitigated expectation values: [0.98031429 0.96357143]
Unmitigated error bars: [0.00124128 0.00578812]
Transpiled circuits:
Circuit 0:
Qubit mapping: [[[0, 140], [1, 141], [2, 142], [3, 143], [4, 136]]]
Measurement bases: 2
Results for each observable:
Circuit 0:
Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
QESEM mitigated value: 1.0064834305181962 ± 0.002538119914534849
Unmitigated value: 0.9803142857142859 ± 0.0012412835813609938
Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
QESEM mitigated value: 1.0063628870614818 ± 0.006935859820870656
Unmitigated value: 0.9635714285714285 ± 0.005788121870526659

Podsumowanie głównych wyników:

  • mitigated: w pełni zmitygowana wartość oczekiwana QESEM.
  • unmitigated: surowy wynik z fizycznym szumem bez mitygacji błędów.

Wyniki ekstrapolacji heurystycznej QESEM​

W standardowym uruchomieniu QESEM z pojedynczą wartością zmiennoprzecinkową precision, wyniki zawierają również automatycznie dostępne punkty skalowania szumu używane do heurystyki QESEM. Te punkty są obliczane bez dodatkowych zasobów QPU.

Skala 1.0 reprezentuje poziom szumu fizycznego urządzenia z mitygacją odczytu (REM), podczas gdy skala 2.0 to uzupełniający punkt wzmocnionego szumu, również z REM. Te punkty są używane do wytworzenia wyniku qesem_heuristic.

  • qesem_heuristic: oszacowanie w stylu ZNE obliczone na podstawie dostępnych danych skalowanych szumem. Obecnie wykorzystuje ekstrapolację wykładniczą.

  • noise_scaling.results_with_REM: wartości oczekiwane przy różnych skalach szumu, wszystkie z mitygacją odczytu (REM).

Subtelnym, ale ważnym szczegółem jest to, że wynik dla skali 1.0 nie jest tym samym co wynik unmitigated. Oba odpowiadają poziomowi szumu fizycznego urządzenia, ale punkt skali 1.0 zawiera mitygację odczytu, podczas gdy unmitigated jej nie zawiera.

print("QESEM heuristic results:")

for pub_idx, pub_result in enumerate(sample_result):
print(f"\nPUB {pub_idx}:")
for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
print(f" Circuit {circ_idx}:")
for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
print(f" Observable {obs_idx}: {obs_array}")
qesem_heuristic = result_dict["qesem_heuristic"][0]
print(" QESEM heuristic:")
print(
f" Value: {qesem_heuristic['value']} ± {qesem_heuristic['error_bar']}"
)
print(
f" Extrapolation: {qesem_heuristic['extrapolation']}"
)
print(
f" Scale factors: {qesem_heuristic['scale_factors']}"
)
noise_scaling = result_dict["noise_scaling"]
print(" Noise scaling results:")
print(
f" Scaling method: {noise_scaling['scaling_method']}"
)
print(" Results with Readout mitigation (REM):")
for rem_result in sorted(
(
item
for item in noise_scaling["results_with_REM"]
if item["scale"] != 0.0
),
key=lambda item: item["scale"],
):
print(
f" Scale factor {rem_result['scale']}: {rem_result['value']} ± {rem_result['error_bar']}"
)
QESEM heuristic results:

PUB 0:
Circuit 0:
Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
QESEM heuristic:
Value: 1.0008161638888535 ± 0.0038859458884403964
Extrapolation: exponential
Scale factors: [1.0, 2.0]
Noise scaling results:
Scaling method: QESEM
Results with Readout mitigation (REM):
Scale factor 1.0: 0.9918395459270772 ± 0.0012565417579355634
Scale factor 2.0: 0.982943441922748 ± 0.0028919278067695018
Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
QESEM heuristic:
Value: 0.9960853148925298 ± 0.013811635038961175
Extrapolation: exponential
Scale factors: [1.0, 2.0]
Noise scaling results:
Scaling method: QESEM
Results with Readout mitigation (REM):
Scale factor 1.0: 0.9902860583785115 ± 0.005921236914723409
Scale factor 2.0: 0.984520565414274 ± 0.006909522262347639
uwaga

Poniższe przykłady skupiają się na danych wejściowych i wynikach specyficznych dla danej funkcji, więc za każdym razem nie wypisują pełnych metryk wykonania. Metadane najwyższego poziomu pokazane wcześniej, takie jak total_qpu_time, gate_fidelities, total_shots, mitigation_shots i resource_usage, są dostępne również dla tych zadań.

Niektóre zmienne z poprzednich przykładów, w tym backend, obserwable i parametry obwodu bazowego, są ponownie wykorzystywane poniżej dla zwięzłości.

uwaga

Wszystkie poniższe przykłady można również uruchomić z empirycznym szacowaniem czasu. Aby to włączyć, przekaż "estimate_time_only": "empirical" w opcjach funkcji.

Przykład obwodu sparametryzowanego​

Wiele algorytmów ocenia ten sam obwód przy kilku wartościach parametrów. Wysłanie sparametryzowanego obwodu jako jednego zadania QESEM pozwala QESEM współdzielić charakteryzację i kalibrację między instancjami obwodu, co może zmniejszyć narzut czasu QPU w porównaniu z uruchamianiem osobnych zadań.

Wysłanie sparametryzowanego obwodu wymaga użycia poziomu transpilacji "minimal_with_layout_opt". Obwody wysyłane na tym poziomie powinny być już wyrażone przy użyciu bramek bazowych backendu, w zależności od backendu. Na tym poziomie QESEM utrzymuje wysłaną strukturę tak blisko oryginalnego obwodu wejściowego, jak to możliwe, respektuje bariery podczas warstwowania (grupowania operacji w warstwy równoległych bramek dwukubitowych) i nadal automatycznie obsługuje mapowanie sprzętowe do kubitów o wysokiej wierności oraz łączność urządzenia.

W praktyce oznacza to, że przed wysłaniem należy przetranspilować obwody do bramek bazowych docelowego backendu. Prosty przykład transpilacji bramek bazowych pokazano poniżej.

QESEM obecnie obsługuje tylko jedną obserwablę na zestaw parametrów. Poniższe dwa wiersze parametrów są łączone z dwiema obserwablami: pierwszy wiersz jest mierzony za pomocą avg_magnetization, a drugi wiersz jest mierzony za pomocą other_observable.

# Transpile to the backend basis gates only. With minimal_with_layout_opt, QESEM handles hardware mapping/connectivity and observable layout internally.
from qiskit_ibm_runtime.fake_provider import FakeFez

backend = FakeFez()
basis = backend.operation_names
print(basis)

param0 = qiskit.circuit.Parameter("param0")
param1 = qiskit.circuit.Parameter("param1")
parametrized_circ = qiskit.QuantumCircuit(5)
parametrized_circ.rx(param0, 0)
parametrized_circ.rx(param1, 1)
parametrized_circ.cx(0, 1)
parametrized_circ.cx(2, 3)
parametrized_circ.cx(1, 2)
parametrized_circ.cx(3, 4)

parametrized_circ = qiskit.transpile(
parametrized_circ, basis_gates=basis, optimization_level=1
)
parametrized_parameter_values = [[0.5, 0.1], [0.0, 0.6]]
parametrized_observables = [avg_magnetization, other_observable]

parametrized_job = qesem_function.run(
pubs=[
(
parametrized_circ,
parametrized_observables,
parametrized_parameter_values,
0.1,
)
],
backend_name=backend_name,
options={
"max_execution_time": 300,
"transpilation_level": "minimal_with_layout_opt",
},
)
print(parametrized_job.job_id)
print(parametrized_job.status())
d1b0e29b-196c-4896-aec6-44a268ebd874
DONE
parametrized_result = parametrized_job.result()
print("Parameterized circuit QESEM results:")

for pub_idx, pub_result in enumerate(parametrized_result):
print(f"\nPUB {pub_idx}:")
print(f" Mitigated expectation values: {pub_result.data.evs}")
print(f" Mitigated error bars: {pub_result.data.stds}")
noisy_results = pub_result.metadata["noisy_results"]
print(f" Unmitigated expectation values: {noisy_results.evs}")
print(f" Unmitigated error bars: {noisy_results.stds}")
print(" Results for each parameter value:")
for param_idx, param_results in enumerate(pub_result.metadata["results"]):
print(
f" Parameter set {param_idx}: {parametrized_parameter_values[param_idx]}"
)
for obs_idx, (obs_array, result_dict) in enumerate(param_results):
print(f" Observable {obs_idx}: {obs_array}")
print(
f" QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
)
print(
f" Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
)
Parameterized circuit QESEM results:

PUB 0:
Mitigated expectation values: [0.92392021 0.82517653]
Mitigated error bars: [0.00565281 0.00616016]
Unmitigated expectation values: [0.9028 0.78771429]
Unmitigated error bars: [0.00335142 0.00925413]
Results for each parameter value:
Parameter set 0: [0.5, 0.1]
Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
QESEM mitigated value: 0.923920210615709 ± 0.005652811570890183
Unmitigated value: 0.9028 ± 0.0033514176105045447
Parameter set 1: [0.0, 0.6]
Observable 0: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
QESEM mitigated value: 0.8251765289285893 ± 0.006160161743353999
Unmitigated value: 0.7877142857142858 ± 0.009254130564027902

Przykład multi-pub​

Wykonanie multi-pub jest przydatne, gdy chcesz uruchomić kilka powiązanych obwodów w jednym zadaniu QESEM. Podobnie jak wykonanie sparametryzowane, może to zmniejszyć narzut, ponieważ QESEM może współdzielić charakteryzację i kalibrację między instancjami obwodu zamiast powtarzać je w osobnych zadaniach.

Jest to szczególnie przydatne dla obwodów o wspólnej strukturze warstw, takich jak obciążenia typu Trotter, gdzie różne obwody ponownie wykorzystują te same unikalne warstwy. W takim przypadku wspólne uruchamianie może zmniejszyć koszt charakteryzacji w porównaniu z niezależnymi zadaniami QESEM.

Zadania multi-pub wymagają "transpilation_level": "minimal_with_layout_opt". Podobnie jak w przykładzie sparametryzowanym, obwody powinny być przetranspilowane do bramek bazowych docelowego backendu przed wysłaniem. QESEM następnie wewnętrznie obsługuje łączność urządzenia, layout i mapowanie do kubitów o wysokiej wierności.

Każdy PUB poniżej zawiera jeden obwód i te same dwie obserwable użyte wcześniej w notatniku, więc zwrócony PrimitiveResult zawiera jeden PubResult na obwód wejściowy.

Poniższy przykład wykorzystuje dwa proste obwody Trottera o tym samym wzorcu warstw: circ_a ma jedną warstwę Trottera, a circ_b powtarza ten sam wzorzec warstw dwukrotnie. Sprawia to, że wspólna struktura jest jednoznaczna.

def make_trotter_circuit(num_qubits, num_layers, zz_angle=0.2, x_angle=0.1):
trotter_circ = qiskit.QuantumCircuit(num_qubits)
for _ in range(num_layers):
for q in range(num_qubits):
trotter_circ.rx(x_angle, q)
trotter_circ.barrier()
for q in range(0, num_qubits - 1, 2):
trotter_circ.rzz(zz_angle, q, q + 1)
trotter_circ.barrier()
for q in range(1, num_qubits - 1, 2):
trotter_circ.rzz(zz_angle, q, q + 1)
trotter_circ.barrier()
return trotter_circ

circ_a = make_trotter_circuit(num_qubits=5, num_layers=1)
circ_b = make_trotter_circuit(num_qubits=5, num_layers=2)
multi_pubs = [
(
qiskit.transpile(qci, basis_gates=basis, optimization_level=1),
[avg_magnetization, other_observable],
)
for qci in [circ_a, circ_b]
]

multi_circ_job = qesem_function.run(
pubs=multi_pubs,
backend_name=backend_name,
options={
"max_execution_time": 300,
"transpilation_level": "minimal_with_layout_opt",
"default_precision": 0.1,
},
)
print(multi_circ_job.job_id)
print(multi_circ_job.status())
e34565b8-7262-4133-a120-de42ce624a99
DONE
multi_circ_result = multi_circ_job.result()
print("Multi-pub QESEM results:")

for pub_idx, pub_result in enumerate(multi_circ_result):
print(f"\nPUB {pub_idx}:")
print(f" Mitigated expectation values: {pub_result.data.evs}")
print(f" Mitigated error bars: {pub_result.data.stds}")
noisy_results = pub_result.metadata["noisy_results"]
print(f" Unmitigated expectation values: {noisy_results.evs}")
print(f" Unmitigated error bars: {noisy_results.stds}")
print(" Results for each observable:")
for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
print(f" Circuit {circ_idx}:")
for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
print(f" Observable {obs_idx}: {obs_array}")
print(
f" QESEM mitigated value: {result_dict['qesem']['value']} ± {result_dict['qesem']['error_bar']}"
)
print(
f" Unmitigated value: {result_dict['unmitigated']['value']} ± {result_dict['unmitigated']['error_bar']}"
)
Multi-pub QESEM results:

PUB 0:
Mitigated expectation values: [0.99502406 1.02209332]
Mitigated error bars: [0.00483819 0.00707488]
Unmitigated expectation values: [0.96934286 0.97271429]
Unmitigated error bars: [0.00124855 0.00617294]
Results for each observable:
Circuit 0:
Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
QESEM mitigated value: 0.9950240647925642 ± 0.004838188086259301
Unmitigated value: 0.9693428571428573 ± 0.0012485470362492692
Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
QESEM mitigated value: 1.0220933230604674 ± 0.007074884384355636
Unmitigated value: 0.9727142857142859 ± 0.006172939375512439

PUB 1:
Mitigated expectation values: [0.98850017 1.02555188]
Mitigated error bars: [0.0077912 0.01672652]
Unmitigated expectation values: [0.93682857 0.95371429]
Unmitigated error bars: [0.00156245 0.00665735]
Results for each observable:
Circuit 0:
Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
QESEM mitigated value: 0.988500171577252 ± 0.007791203181151346
Unmitigated value: 0.9368285714285716 ± 0.001562451883089579
Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
QESEM mitigated value: 1.02555188098689 ± 0.016726524388086233
Unmitigated value: 0.9537142857142858 ± 0.006657345655544263

Przykład quasi-probabilistycznego strojenia błędów (QET)​

Quasi-probabilistyczne strojenie błędów (QET) żąda wartości oczekiwanych przy wybranych czynnikach skalowania szumu. Jest to przydatne do niestandardowych badań skalowania szumu i przepływów pracy ekstrapolacji do zerowego szumu. Skala 1.0 to poziom szumu fizycznego, wartości pomiędzy 0.0 a 1.0 częściowo redukują szum, a wartości powyżej 1.0 wzmacniają szum.

Aby użyć QET z Funkcją Qiskit, przekaż słownik jako precyzję PUB. Słownik mapuje każdą żądaną skalę szumu na jej docelową precyzję. Zwrócone wyniki czynnika skali są przechowywane w noise_scaling.results_with_REM i zawierają mitygację odczytu. Punkt skali 1.0 nie jest więc identyczny z wartością niezmitygowaną, ponieważ 1.0 zawiera mitygację odczytu, podczas gdy unmitigated jej nie zawiera.

Gdy żądana jest skala, QESEM zwraca również uzupełniającą skalę wokół 1.0 bez dodatkowego zużycia QPU. Na przykład żądanie 0.5 może również zwrócić 1.5, a żądanie 1.3 może również zwrócić 0.7. Precyzja na uzupełniającej skali nie jest gwarantowana.

noise_scale_precision = {0.5: 0.15, 1.3: 0.2}

qet_job = qesem_function.run(
pubs=[
(
circ,
[avg_magnetization, other_observable],
None,
noise_scale_precision,
)
],
backend_name=backend_name,
options={"max_execution_time": 300},
)
print(qet_job.job_id)
print(qet_job.status())
8195fa58-f037-4651-8715-36ce1cdc5521
DONE
qet_result = qet_job.result()
print("QET noise-scaling results:")

for pub_idx, pub_result in enumerate(qet_result):
print(f"\nPUB {pub_idx}:")
noisy_results = pub_result.metadata["noisy_results"]
print(f" Unmitigated expectation values: {noisy_results.evs}")
print(f" Unmitigated error bars: {noisy_results.stds}")
print(" Results for each observable:")
for circ_idx, circ_results in enumerate(pub_result.metadata["results"]):
print(f" Circuit {circ_idx}:")
for obs_idx, (obs_array, result_dict) in enumerate(circ_results):
print(f" Observable {obs_idx}: {obs_array}")
noise_scaling = result_dict["noise_scaling"]
print(
f" Scaling method: {noise_scaling['scaling_method']}"
)
print(" Results with Readout mitigation (REM):")
for rem_result in sorted(
(
item
for item in noise_scaling["results_with_REM"]
if item["scale"] != 0.0
),
key=lambda item: item["scale"],
):
print(
f" Scale factor {rem_result['scale']}: {rem_result['value']} ± {rem_result['error_bar']}"
)
QET noise-scaling results:

PUB 0:
Unmitigated expectation values: [0.97822857 0.96171429]
Unmitigated error bars: [0.00123812 0.00672958]
Results for each observable:
Circuit 0:
Observable 0: ObservablesArray({'IIIIZ': 0.2, 'IIIZI': 0.2, 'IIZII': 0.2, 'IZIII': 0.2, 'ZIIII': 0.2}, shape=())
Scaling method: QESEM
Results with Readout mitigation (REM):
Scale factor 0.5: 0.9938730340199383 ± 0.0032116907357568275
Scale factor 0.7: 0.9963976191853445 ± 0.00036300258869586616
Scale factor 1.0: 0.9898115079506586 ± 0.0012525947426560995
Scale factor 1.3: 0.9864667065580341 ± 0.002633221613518526
Scale factor 1.5: 0.9838755527197551 ± 0.002948417797996015
Observable 1: ObservablesArray({'IIIZZ': 1.0, 'ZIIXI': 0.5}, shape=())
Scaling method: QESEM
Results with Readout mitigation (REM):
Scale factor 0.5: 1.0006538544450332 ± 0.002121742014777343
Scale factor 0.7: 1.0004159801523036 ± 0.0021671375357823794
Scale factor 1.0: 0.9898058846339917 ± 0.00690183710903159
Scale factor 1.3: 0.9948946719997267 ± 0.002146532859610311
Scale factor 1.5: 0.9927220368192772 ± 0.0020875057190323882

Pobieranie komunikatów o błędach​

Jeśli status twojego obciążenia to ERROR, użyj job.result(), aby pobrać komunikat o błędzie w następujący sposób:

# Get the result and truncate for readability
result = sample_job.result()
result_str = str(result)
max_length = 500 # Adjust this value as necessary

if len(result_str) > max_length:
truncated = (
result_str[:max_length]
+ f"... (truncated {len(result_str) - max_length} characters)"
)
else:
truncated = result_str

print(truncated)
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(2,), dtype=float64>), stds=np.ndarray(<shape=(2,), dtype=float64>), shape=(2,)), metadata={'gate_fidelities': {'CZ': 0.9979444718552628, 'ID1Q': 0.9991994239814883}, 'total_shots': 498600, 'mitigation_shots': 223400, 'transpiled_circs': [{'circuit': 'OPENQASM 3.0;\ninclude "stdgates.inc";\nbit[76] c0;\nqubit[76] q0;\nrx(0) q0[54];\nrx(0) q0[59];\nrx(0) q0[75];\nrz(pi/2) q0[54];\nrz(pi/2) q0[59];\nrz(pi/2) q0[75];\nrx(pi/2) q0[54];\nr... (truncated 3771 characters)

Uzyskiwanie pomocy technicznej​

Zespół wsparcia Qedma jest do twojej dyspozycji! Jeśli napotkasz jakiekolwiek problemy lub masz pytania dotyczące korzystania z funkcji Qiskit QESEM, nie wahaj się z nami skontaktować. Nasz kompetentny i przyjazny zespół wsparcia jest gotowy pomóc ci w rozwiązaniu wszelkich kwestii technicznych.

Możesz napisać do nas na adres support@qedma.com. Prosimy o podanie jak największej liczby szczegółów dotyczących napotkanego problemu — pomoże nam to udzielić szybkiej i precyzyjnej odpowiedzi. Możesz też skontaktować się ze swoim dedykowanym przedstawicielem Qedma (POC) mailowo lub telefonicznie.

Aby pomóc nam sprawnie rozwiązać twój problem, prosimy o podanie następujących informacji:

  • Szczegółowego opisu problemu

  • Identyfikatora zadania (job ID)

  • Wszelkich istotnych komunikatów o błędach lub kodów błędów

Jesteśmy zobowiązani do zapewnienia ci szybkiego i skutecznego wsparcia, aby korzystanie z naszej funkcji Qiskit było jak najlepszym doświadczeniem.

Stale pracujemy nad udoskonalaniem naszego produktu i chętnie poznamy twoje sugestie! Jeśli masz pomysły na ulepszenie naszych usług lub chciałbyś zobaczyć nowe funkcje, podziel się nimi pod adresem support@qedma.com.

Następne kroki​