Migracja z Sampler i Estimator po stronie serwera na wersje po stronie klienta
Ten przewodnik opisuje, jak migrować z implementacji po stronie serwera Sampler i Estimator IBM Quantum® do ich nowych implementacji po stronie klienta w
qiskit-ibm-runtime. Interfejsy i opcje pozostają w dużej mierze niezmienione, więc większość kodu
działa bez zmian, ale istnieją pewne różnice w zachowaniu, które warto zrozumieć.
Kontekst
Sampler i Estimator to prymitywne interfejsy zdefiniowane w Qiskit. IBM Quantum
Compute Service (dawniej Qiskit Runtime) historycznie dostarczał implementację
tych prymitywów w ramach swojego środowiska uruchomieniowego. Gdy wywołujesz sampler.run() lub
estimator.run(), żądanie jest wysyłane do usługi, a całe obliczenia — w tym
tłumienie i łagodzenie błędów — odbywają się po stronie serwera.
To doświadczenie typu black-box jest wygodne: nie musisz martwić się o szczegóły implementacji. Utrudnia to jednak debugowanie, dostosowywanie i uczenie się na podstawie prymitywów, ponieważ nie widzisz, co dzieje się podczas przetwarzania.
Nowo wprowadzony model wykonania kierowanego przyjmuje przeciwne podejście i zapewnia doświadczenie typu white-box. Wszystkie intencje projektowe są przechwytywane po stronie klienta, a pojedynczy prymityw po stronie serwera Executor przetwarza te dane wejściowe dokładnie tak, jak zostało to zlecone — nie podejmuje żadnych ukrytych decyzji w twoim imieniu.
Począwszy od qiskit-ibm-runtime v0.50.0, Sampler i Estimator zostały ponownie zaimplementowane
po stronie klienta na bazie Executora. Zapewniają taką samą wygodę i
abstrakcję jak wcześniej, a teraz możesz sprawdzić szczegóły implementacji, gdy tego
potrzebujesz. Ponieważ interfejsy i opcje pozostają w dużej mierze takie same, migracja powinna przebiegać
płynnie.
Uwaga: IBM Quantum obsługuje tylko wersję 2 interfejsów Sampler i Estimator (BaseSamplerV2 i BaseEstimatorV2). Dlatego w tym przewodniku są one po prostu nazywane Sampler i Estimator.
Aktualizacja importów
Obecnie musisz jawnie importować nowe implementacje z ich dedykowanych modułów:
from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
W najbliższej przyszłości importy najwyższego poziomu będą odwoływać się do nowych implementacji po stronie klienta i żadna zmiana kodu nie będzie wymagana:
# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator
Podobnie, jeśli tworzysz obiekty opcji typowanych, musisz importować je z
qiskit_ibm_runtime.options_models, lub po prostu przekazać zwykły zagnieżdżony słownik:
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
Co pozostaje bez zmian
-
Konstrukcja prymitywu z
modeioptions. -
Sygnatura
run()i format PUB. -
Drzewo opcji (
options.twirling,options.resilience,options.default_shots, i tak dalej). -
Struktura danych wyniku zwracana przez
job.result().
Niekompatybilne zmiany w nowym Samplerze
| Change | Migration action |
|---|---|
Bazowym prymitywem jest teraz Executor. Zarówno interfejs użytkownika IBM Quantum Platform, jak i job.primitive_id będą pokazywać executor zamiast sampler. | Zaktualizuj kod, który odwołuje się do job.primitive_id. |
Nowa implementacja mapuje dane wejściowe Samplera na dane wejściowe Executora, więc job.inputs zwraca dane wejściowe Executora. | Zaktualizuj kod, który odwołuje się do job.inputs. Zobacz Dane wejściowe zadania. |
Więcej przetwarzania wstępnego i końcowego odbywa się teraz po stronie klienta, więc sampler.run() i job.result() mogą trwać dłużej niż wcześniej. | Włącz logowanie na poziomie INFO, aby śledzić postęp przetwarzania po stronie klienta. Zobacz Włączanie logowania INFO. |
Metadane obwodu są kopiowane do metadanych wyniku. Typy danych dozwolone w metadanych wyniku są teraz ograniczone do str, float, int, bool oraz list lub słowników tych typów. | Jeśli potrzebujesz innych typów danych, najpierw zakoduj je jako łańcuch znaków (na przykład za pomocą base64). |
Klasy opcji (options_models.SamplerOptions i tak dalej) są teraz modelami Pydantic zamiast dataclasses, więc nie można ich już konwertować na słowniki Pythona za pomocą asdict(). | Zamiast tego użyj options.model_dump(). |
Klasy opcji, które wcześniej miały sufiks V2 (ExecutionOptionsV2 i tak dalej), już go nie mają, ponieważ prymitywy V1 nie są już obsługiwane. | Usuń sufiks V2 z tych klas opcji: zastąp ExecutionOptionsV2 przez ExecutionOptions, ResilienceOptionsV2 przez ResilienceOptions, a SamplerExecutionOptionsV2 przez SamplerExecutionOptions. |
Jeśli twirling jest włączony i wszystkie z shots (w PUB-ach lub w run()), shots_per_randomization i num_randomizations są określone, to num_randomizations * shots_per_randomization ma pierwszeństwo przed shots. | Pomiń num_randomizations i shots_per_randomization, jeśli chcesz, aby użyta została wartość shots. |
Część walidacji danych wejściowych przeniesiono na stronę serwera i teraz zgłasza RuntimeError zamiast IBMInputValueError. | Zaktualizuj typy wyjątków przechwytywane przez twój kod. |
| Mieszane wartości shots w ramach jednego zadania nie są już obsługiwane. | Prześlij osobne zadanie dla każdej wartości shots. Zobacz Podział zadania, aby poznać uwagi na ten temat. |
Niekompatybilne zmiany w nowym Estimatorze
| Change | Migration action |
|---|---|
Bazowym prymitywem jest teraz Executor. Zarówno interfejs użytkownika IBM Quantum Platform, jak i job.primitive_id będą pokazywać executor zamiast estimator. | Zaktualizuj kod, który odwołuje się do job.primitive_id. |
Nowa implementacja mapuje dane wejściowe Estimatora na dane wejściowe Executora, więc job.inputs zwraca dane wejściowe Executora. | Zaktualizuj kod, który odwołuje się do job.inputs. Zobacz Dane wejściowe zadania. |
Więcej przetwarzania wstępnego i końcowego odbywa się teraz po stronie klienta, więc estimator.run() i job.result() mogą trwać dłużej niż wcześniej. | Włącz logowanie na poziomie INFO, aby śledzić postęp przetwarzania po stronie klienta. Zobacz Włączanie logowania INFO. |
Metadane obwodu są kopiowane do metadanych wyniku. Typy danych dozwolone w metadanych wyniku są teraz ograniczone do str, float, int, bool oraz list lub słowników tych typów. | Jeśli potrzebujesz innych typów danych, najpierw zakoduj je jako łańcuch znaków (na przykład za pomocą base64). |
Klasy opcji (options_models.EstimatorOptions i tak dalej) są teraz modelami Pydantic zamiast dataclasses, więc nie można ich już konwertować na słowniki Pythona za pomocą asdict(). | Zamiast tego użyj options.model_dump(). |
Klasy opcji, które wcześniej miały sufiks V2 (ExecutionOptionsV2 i tak dalej), już go nie mają, ponieważ prymitywy V1 nie są już obsługiwane. | Usuń sufiks V2 z tych klas opcji: zastąp ExecutionOptionsV2 przez ExecutionOptions, a ResilienceOptionsV2 przez ResilienceOptions. |
| Wszystkie opcje wejściowe są zwracane w metadanych wyniku, a nie tylko wybrany podzbiór. | Brak — to informacja. |
Część walidacji danych wejściowych przeniesiono na stronę serwera i teraz zgłasza RuntimeError zamiast IBMInputValueError. | Zaktualizuj typy wyjątków przechwytywane przez twój kod. |
| Nie ma już niejawnego uczenia się szumu dla PEA i PEC. Uczenie się szumu pomiarowego dla TREX jest nadal obsługiwane. | Naucz się modeli szumu oddzielnie i przekaż je do Estimatora. Zobacz Wykonywanie jawnego uczenia się szumu dla PEA i PEC. |
Typ wejściowy ResilienceOptions.layer_noise_model jest inny i można go skonstruować na podstawie wyników NoiseLearnerV3. | Zobacz Wykonywanie jawnego uczenia się szumu dla PEA i PEC, aby dowiedzieć się, jak nauczyć się modeli szumu za pomocą NoiseLearnerV3 i przekazać je do Estimatora. |
MeasureNoiseLearningOptions.shots_per_randomization nie jest już obsługiwane. | Dla wszystkich obwodów w zadaniu używana jest jedna wartość shots, w tym dla obwodów uczenia się szumu pomiarowego. Jeśli musisz użyć innej wartości shots, zastosuj TREX za pomocą qiskit-mitigation poza Estimatorem. |
| Mieszane wartości precision w ramach jednego zadania nie są już obsługiwane. | Prześlij osobne zadanie dla każdej żądanej wartości precision. Zobacz Podział zadania, aby poznać uwagi na ten temat. |
Opcja seed_estimator nie jest już obsługiwana. | Usuń wszelkie przypisania options.seed_estimator (ustawienie go zgłasza ValidationError). Nie ma odpowiednika po stronie klienta, więc wyniki nie są już odtwarzalne za pomocą tego ziarna. |
Włączanie logowania INFO
Ponieważ więcej pracy odbywa się teraz po stronie klienta, warto widzieć postęp tego
przetwarzania. Włącz logowanie na poziomie INFO dla loggera qiskit_ibm_runtime:
import logging
logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)
Wykonywanie jawnego uczenia się szumu dla PEA i PEC
Nowy Estimator nie wykonuje już niejawnego uczenia się szumu, gdy wybrana jest metoda
łagodzenia błędów PEA lub PEC. Musisz jawnie nauczyć się modeli szumu i przekazać je.
Użyj nowego NoiseLearnerV3, aby kontrolować sposób
stratyfikacji obwodów na warstwy. Przyjmuje on listę opakowanych instrukcji obwodu (na przykład
unikalnych warstw) jako dane wejściowe.
PEA i PEC wymagają teraz tego jawnego wzorca. Nie pomijaj kroku uczenia się szumu, ponieważ twój kod się nie powiedzie. Uczenie się szumu pomiarowego dla TREX nie jest tym objęte i nadal działa jak wcześniej.
Podobnie, jeśli twój kod używa NoiseLearner i przekazuje wynikowy model szumu do Estimatora po stronie serwera, musisz migrować do NoiseLearnerV3. NIE używaj starszego NoiseLearner, który jest niekompatybilny z nowym Estimatorem.
Wszystkie opcje uczenia się szumu w Estimatorze po stronie serwera (LayerNoiseLearningOptions) mapują się bezpośrednio na opcję NoiseLearnerV3 (NoiseLearnerV3Options), z wyjątkiem max_layers_to_learn. Liczba warstw do nauczenia jest zamiast tego oparta na liczbie warstw przekazanych do NoiseLearnerV3.
Na przykład:
Estimator po stronie serwera (z włączonym PEC):
from qiskit_ibm_runtime import Estimator
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64
job = estimator.run(pubs)
Estimator po stronie klienta (z włączonym PEC):
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)
# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
learner_result = learner_job.result()
# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)
# Now execute the target PUBs.
job = estimator.run(pubs)
Migracja z NoiseLearner do NoiseLearnerV3
NoiseLearner działa tylko z implementacją Estimator po stronie serwera. Dlatego jeśli twój kod używa NoiseLearner do uczenia się modelu szumu i przekazywania go do Estimator, musisz zaktualizować kod, aby używał NoiseLearnerV3.
Zobacz przewodnik Migracja z NoiseLearner do NoiseLearnerV3, aby uzyskać szczegóły.
Podział zadania
Gdy musisz podzielić jedno zadanie na kilka, ponieważ mieszane wartości shots lub precision w ramach jednego zadania nie są już obsługiwane, weź pod uwagę następujące kwestie:
-
Grupuj PUB-y według ich wartości docelowej — jedno zadanie na odrębną wartość, a nie jedno zadanie na PUB. Podział to przegrupowanie, więc całkowita liczba przesyłanych PUB-ów się nie zmienia. Na przykład, mając
[A@0.01, B@0.05, C@0.01], prześlij dwa zadania:[A, C]przyprecision=0.01i[B]przyprecision=0.05. PrzesyłanieAiCjako osobnych zadań jest mniej wydajne, ponieważ każde zadanie wiąże się ze stałym narzutem. -
Naucz się raz i użyj modeli szumu we wszystkich podzielonych zadaniach. Wydajniej jest uruchomić pojedyncze zadanie
NoiseLearnerV3na sumie wszystkich warstw. Wynik zadania noise learner zawiera listę obiektówNoiseLearnerV3Result, po jednym dla każdej instrukcji wejściowej, w tej samej kolejności co lista wejściowa. Możesz użyć wyniku tego zadania noise learner we wszystkich podzielonych zadaniach (Estimator), a modele szumu dla warstw nieobecnych w PUB-ach danego podzielonego zadania są ignorowane. -
Najpierw prześlij wszystkie podzielone zadania w
Batch, a następnie zbierz ich wyniki. Tryb wykonaniaBatchzapewnia wydajne równoległe wykonanie, gdy jest wiele zadań. Jednakjob.result()jest blokujące, więc wywoływanie go wewnątrz pętli przesyłania serializuje zadania i niweczy korzyści z używaniaBatch. Upewnij się, że stosujesz wzorzec najpierw-prześlij-wszystko-potem-zbierz (pokazany poniżej).
W poniższym przykładzie pub1 i pub2 wymagają precision=0.5, natomiast pub3 wymaga precision=0.1:
group1_pubs = [pub1, pub2]
group2_pubs = [pub3]
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True
# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)
# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))
# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]
Struktura danych wejściowych zadania
Nowa implementacja mapuje dane wejściowe Samplera lub Estimatora na dane wejściowe Executora, więc job.inputs zwraca słownik zawierający dane wejściowe Executora. Ten słownik ma następujące klucze:
-
options: Dane wejścioweExecutorOption. -
quantum_program: Dane wejścioweQuantumProgram -
schema_version: Użyta wersja schematu po stronie serwera.
Jeśli twój kod używał job.inputs['options'] do znalezienia opcji określonych dla zadania, teraz możesz zamiast tego użyć job.result().metadata['options'].
Testowanie lokalne z fałszywym backendem
Przed przesłaniem do sprzętu możesz zweryfikować zmigrowany kod względem backendu Fake*,
aby wcześnie wychwycić wszelkie błędy składniowe. Zwróć uwagę na następujące szczegóły dotyczące trybu testowania lokalnego:
-
Nie odtwarza wyników sprzętowych. Lokalna symulacja z szumem nie odwzorowuje idealnie szumu rzeczywistego urządzenia, dlatego wyniki mogą się różnić. Uruchomienie waliduje jednak, że ścieżki opcji i typy wartości są poprawne.
-
NoiseLearnerV3nie ma trybu testowania lokalnego: jegomodeakceptuje tylko prawdziwyBackend,SessionlubBatch, więc nie możesz wykonać kroku uczenia się szumu względem fałszywego backendu. Zamiast tego zweryfikuj tę część kodu względem dokumentacji APINoiseLearnerV3. Potwierdź, że konstruktor, kształt danych wejściowychrun(instructions)oraz wszelkie funkcje pomocnicze (takie jak funkcja pomocnicza unikalnych warstw) są używane zgodnie z dokumentacją.
Cliffordyzacja obwodu dla wydajnej symulacji lokalnej
Fałszywy backend używa symulatora statevector (z szumem), którego koszt rośnie wykładniczo wraz z
liczbą kubitów i głębokością. Dlatego realistyczny obwód obciążenia może się zawiesić lub wyczerpać pamięć. Ponieważ
testowanie lokalne musi jedynie sprawdzić ścieżki opcji (a nie odtworzyć wyniki fizyczne),
najpierw zredukuj obwód do obwodu Clifford za pomocą
ConvertISAToClifford,
który zaokrągla każdy kąt RZ/RZZ/RX do najbliższej wielokrotności π/2. Obwody Clifford
symulują się wydajnie (symulacja stabilizatorowa) niezależnie od rozmiaru.
from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford
clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive
ConvertISAToClifford wymaga obwodu ISA jako danych wejściowych (wyniku
generate_preset_pass_manager(...).run(...) skierowanego na backend). Musisz uwzględnić następujące konsekwencje
podczas konstruowania lokalnego PUB:
-
Atrybut
.layoutjest usuwany. Obwód po cliffordyzacji zachowuje tę samą liczbę kubitów, aleclifford.layoutma wartośćNone, więcobservable.apply_layout(clifford.layout)kończy się niepowodzeniem. Zamiast tego rozłóż obserwablę na podstawie obwodu ISA sprzed cliffordyzacji:isa_obs = observable.apply_layout(isa_circuit.layout), a następnie uruchom(clifford, isa_obs). -
Parametry są wiązane. Zaokrąglenie kątów rotacji zamienia parametryczny obwód ISA w konkretny obwód Clifford, więc
clifford.num_parametersstaje się0. PUB, który nadal zawiera tablicę wartości parametrów, kończy się niepowodzeniem konwersji. Dla uruchomienia lokalnego usuń tablicę parametrów z PUB; uruchomienie na sprzęcie zachowuje oryginalny obwód parametryczny i jego wartości.
Kolejne kroki
- Model wykonania kierowanego
- Dane wejściowe i wyjściowe Estimatora
- Określanie opcji Estimatora
- Dane wejściowe i wyjściowe Samplera
- Określanie opcji Samplera
- Pomocnik uczenia się szumu (NoiseLearnerV3)
- Dokumentacja API NoiseLearnerV3
- Przebieg transpilera
ConvertISAToClifford - Techniki łagodzenia i tłumienia błędów