Sari la conținutul principal

Migrează de la Sampler și Estimator server-side la client-side

Acest ghid descrie cum să migrezi de la implementările server-side ale IBM Quantum® Sampler și Estimator la noile lor implementări client-side în qiskit-ibm-runtime. Interfețele și opțiunile sunt în mare parte neschimbate, astfel încât majoritatea codului rulează ca atare, dar există câteva diferențe de comportament de înțeles.

Context​

Sampler și Estimator sunt interfețe primitive definite în Qiskit. IBM Quantum Compute Service (fostul Qiskit Runtime) a oferit istoric implementarea acestor primitive în interiorul mediului său de runtime. Când apelezi sampler.run() sau estimator.run(), cererea este trimisă către serviciu, iar întregul calcul — inclusiv suprimarea și atenuarea erorilor — are loc pe partea de server.

Această experiență de tip cutie neagră este convenabilă: nu trebuie să îți faci griji cu privire la detaliile de implementare. Dar face de asemenea primitivele greu de depanat, personalizat sau din care să înveți, deoarece nu poți vedea ce se întâmplă în timpul procesării.

Modelul de execuție direcționată nou introdus adoptă abordarea opusă și oferă o experiență de tip cutie albă. Toate intențiile de design sunt capturate pe partea de client, iar o singură primitivă server-side Executor procesează acele intrări exact așa cum sunt direcționate — nu ia decizii implicite în numele tău.

Începând cu qiskit-ibm-runtime v0.50.0, Sampler și Estimator sunt reimplementate pe partea de client deasupra Executor. Ele oferă aceeași comoditate și abstractizare ca înainte, iar acum poți inspecta detaliile de implementare atunci când ai nevoie de acest lucru. Deoarece interfețele și opțiunile rămân în mare parte aceleași, migrarea ar trebui să fie fără probleme.

Notă: IBM Quantum suportă doar versiunea 2 a interfețelor Sampler și Estimator (BaseSamplerV2 și BaseEstimatorV2). Prin urmare, în acest ghid sunt denumite simplu Sampler și Estimator.

Actualizează importurile​

Astăzi, trebuie să imporți explicit noile implementări din modulele lor dedicate:

from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator

În viitorul apropiat, importurile de nivel superior se vor rezolva la noile implementări client-side, și nu va fi necesară nicio modificare de cod:

# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator

În mod similar, dacă construiești obiecte de opțiuni tipizate, trebuie să le imporți din qiskit_ibm_runtime.options_models în schimb, sau pur și simplu să pasezi un dict simplu imbricat:

from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions

Ce rămâne la fel​

  • Construcția primitivelor cu un mode și options.

  • Semnătura run() și formatul PUB.

  • Arborele de opțiuni (options.twirling, options.resilience, options.default_shots, și așa mai departe).

  • Structura datelor rezultatului returnată de job.result().

Modificări incompatibile în noul Sampler​

ModificareAcțiune de migrare
Primitiva subiacentă este acum Executor. Atât interfața utilizatorului IBM Quantum Platform, cât și job.primitive_id vor afișa executor în loc de sampler.Actualizează orice cod care face referire la job.primitive_id.
Noua implementare mapează intrările Sampler la intrările Executor, astfel încât job.inputs returnează intrări Executor.Actualizează orice cod care face referire la job.inputs. Vezi Structura intrărilor jobului.
Mai multă preprocesare și postprocesare are loc acum pe partea de client, astfel încât sampler.run() și job.result() ar putea dura mai mult decât înainte.Activează logarea INFO pentru a urmări progresul procesării de pe partea de client. Vezi Activează logarea INFO.
Metadatele circuitului sunt copiate în metadatele rezultatului. Tipurile de date permise în metadatele rezultatului sunt acum limitate la str, float, int, bool, și liste sau dicționare ale acestor tipuri.Dacă ai nevoie de alte tipuri de date, codifică-le mai întâi ca șir de caractere (de exemplu, cu base64).
Clasele de opțiuni (options_models.SamplerOptions și așa mai departe) sunt acum modele Pydantic în loc de dataclass-uri, astfel încât nu mai pot fi convertite în dicționare Python folosind asdict().Folosește options.model_dump() în schimb.
Clasele de opțiuni care anterior aveau sufixul V2 (ExecutionOptionsV2 și așa mai departe) nu mai au acest sufix, deoarece primitivele V1 nu mai sunt suportate.Elimină sufixul V2 al acestor clase de opțiuni: înlocuiește ExecutionOptionsV2 cu ExecutionOptions, ResilienceOptionsV2 cu ResilienceOptions, și SamplerExecutionOptionsV2 cu SamplerExecutionOptions.
Dacă twirling este activat și shots (în PUB-uri sau în run()), shots_per_randomization, și num_randomizations sunt toate specificate, atunci num_randomizations * shots_per_randomization are prioritate față de shots.Omite num_randomizations și shots_per_randomization dacă dorești ca valoarea shots să fie utilizată.
O parte din validarea intrărilor a fost mutată pe partea de server și acum ridică RuntimeError în loc de IBMInputValueError.Actualizează tipurile de excepții pe care codul tău le prinde.
Valorile mixte de shots într-un singur job nu mai sunt suportate.Trimite un job separat pentru fiecare valoare de shots. Vezi Împărțirea jobului pentru considerații.

Modificări incompatibile în noul Estimator​

ModificareAcțiune de migrare
Primitiva subiacentă este acum Executor. Atât interfața utilizatorului IBM Quantum Platform, cât și job.primitive_id vor afișa executor în loc de estimator.Actualizează orice cod care face referire la job.primitive_id.
Noua implementare mapează intrările Estimator la intrările Executor, astfel încât job.inputs returnează intrări Executor.Actualizează orice cod care face referire la job.inputs. Vezi Structura intrărilor jobului.
Mai multă preprocesare și postprocesare are loc acum pe partea de client, astfel încât estimator.run() și job.result() ar putea dura mai mult decât înainte.Activează logarea INFO pentru a urmări progresul procesării de pe partea de client. Vezi Activează logarea INFO.
Metadatele circuitului sunt copiate în metadatele rezultatului. Tipurile de date permise în metadatele rezultatului sunt acum limitate la str, float, int, bool, și liste sau dicționare ale acestor tipuri.Dacă ai nevoie de alte tipuri de date, codifică-le mai întâi ca șir de caractere (de exemplu, cu base64).
Clasele de opțiuni (options_models.EstimatorOptions și așa mai departe) sunt acum modele Pydantic în loc de dataclass-uri, astfel încât nu mai pot fi convertite în dicționare Python folosind asdict().Folosește options.model_dump() în schimb.
Clasele de opțiuni care aveau anterior sufixul V2 (ExecutionOptionsV2 și așa mai departe) nu mai au acest sufix, deoarece primitivele V1 nu mai sunt suportate.Elimină sufixul V2 al acestor clase de opțiuni: înlocuiește ExecutionOptionsV2 cu ExecutionOptions și ResilienceOptionsV2 cu ResilienceOptions.
Toate opțiunile de intrare sunt returnate în metadatele rezultatului, în loc de un subset selectat.Niciuna — acest lucru este informativ.
O parte din validarea intrărilor a fost mutată pe partea de server și acum ridică RuntimeError în loc de IBMInputValueError.Actualizează tipurile de excepții pe care codul tău le prinde.
Nu mai există învățare implicită a zgomotului pentru PEA și PEC. Învățarea zgomotului de măsurare pentru TREX este încă suportată.Învață modelele de zgomot separat și transmite-le către Estimator. Vezi Realizează învățarea explicită a zgomotului pentru PEA și PEC.
Tipul de intrare al ResilienceOptions.layer_noise_model este diferit și poate fi construit din rezultatele NoiseLearnerV3.Vezi Realizează învățarea explicită a zgomotului pentru PEA și PEC despre cum să înveți modelele de zgomot folosind NoiseLearnerV3 și să le transmiți către Estimator.
MeasureNoiseLearningOptions.shots_per_randomization nu mai este suportat.O singură valoare de shots este utilizată pentru toate circuitele din job, inclusiv circuitele de învățare a zgomotului de măsurare. Dacă trebuie să folosești o valoare de shots diferită, aplică TREX cu qiskit-mitigation în afara Estimator.
Valorile mixte de precizie într-un singur job nu mai sunt suportate.Trimite un job separat pentru fiecare precizie dorită. Vezi Împărțirea jobului pentru considerații.
Opțiunea seed_estimator nu mai este suportată.Elimină orice atribuire options.seed_estimator (setarea ei ridică o ValidationError). Nu există un echivalent pe partea de client, astfel încât rezultatele nu mai sunt reproductibile prin acest seed.

Activează logarea INFO​

Deoarece mai multă muncă are loc acum pe partea de client, este util să vezi progresul acelei procesări. Activează logarea de nivel INFO pentru logger-ul qiskit_ibm_runtime:

import logging

logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)

Realizează învățarea explicită a zgomotului pentru PEA și PEC​

Noul Estimator nu mai efectuează învățarea implicită a zgomotului atunci când este selectată metoda de atenuare a erorilor PEA sau PEC. Trebuie să înveți modelele de zgomot explicit și să le transmiți. Folosește noul NoiseLearnerV3 pentru a controla modul în care circuitele sunt stratificate în straturi. Acesta primește ca intrare o listă de instrucțiuni de circuit încadrate (de exemplu, straturile unice).

Important

PEA și PEC acum necesită acest tipar explicit. Nu sări peste pasul de învățare a zgomotului sau codul tău va eșua. Învățarea zgomotului de măsurare pentru TREX nu este afectată și continuă să funcționeze ca înainte.

În mod similar, dacă codul tău folosește NoiseLearner și transmite modelul de zgomot rezultat către Estimator server-side, trebuie să migrezi la NoiseLearnerV3. NU folosi vechiul NoiseLearner, care este incompatibil cu noul Estimator.

Toate opțiunile de învățare a zgomotului din Estimator server-side (LayerNoiseLearningOptions) se mapează direct la opțiunea NoiseLearnerV3 (NoiseLearnerV3Options), cu excepția max_layers_to_learn. Numărul de straturi de învățat se bazează în schimb pe numărul de straturi transmise către NoiseLearnerV3.

De exemplu:

Estimator server-side (cu PEC activat):

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 client-side (cu PEC activat):

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)

Migrează de la NoiseLearner la NoiseLearnerV3​

NoiseLearner funcționează doar cu implementarea server-side a Estimator. Prin urmare, dacă codul tău folosește NoiseLearner pentru a învăța modelul de zgomot și a-l transmite către Estimator, trebuie să îți actualizezi codul pentru a folosi NoiseLearnerV3.

Vezi ghidul Migrează de la NoiseLearner la NoiseLearnerV3 pentru detalii.

Împărțirea jobului​

Când trebuie să împarți un job în mai multe, deoarece valorile mixte de shots sau precizie într-un singur job nu mai sunt suportate, ia în considerare următoarele:

  • Grupează PUB-urile după valoarea lor țintă — un job pentru fiecare valoare distinctă, nu un job pentru fiecare PUB. Împărțirea este o regrupare, astfel încât numărul total de PUB-uri pe care le trimiți nu se schimbă. De exemplu, dat fiind [A@0.01, B@0.05, C@0.01], trimite două joburi: [A, C] la precision=0.01 și [B] la precision=0.05. Trimiterea lui A și C ca joburi separate este mai puțin eficientă, deoarece fiecare job vine cu un cost suplimentar fix.

  • Învață o singură dată și folosește modelele de zgomot în toate joburile împărțite. Este mai eficient să rulezi un singur job NoiseLearnerV3 peste reuniunea tuturor straturilor. Rezultatul unui job de învățare a zgomotului conține o listă de obiecte NoiseLearnerV3Result, unul pentru fiecare instrucțiune de intrare, și este în aceeași ordine ca lista de intrare. Poți folosi rezultatul acestui job de învățare a zgomotului în toate joburile (Estimator) împărțite, iar modelele de zgomot pentru straturile care nu sunt în PUB-urile unui job împărțit sunt ignorate.

  • Trimite toate joburile împărțite într-un Batch mai întâi, apoi colectează rezultatele lor. Modul de execuție Batch oferă execuție paralelă eficientă atunci când există mai multe joburi. Cu toate acestea, job.result() este blocant, astfel încât apelarea sa în interiorul buclei de trimitere serializează joburile și anulează beneficiile folosirii Batch. Asigură-te că folosești tiparul trimite-tot-apoi-colectează (arătat mai jos).

În exemplul următor, pub1 și pub2 necesită precision=0.5, în timp ce pub3 necesită 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]

Structura intrărilor jobului​

Noua implementare mapează intrările Sampler sau Estimator la intrările Executor, astfel încât job.inputs returnează un dicționar care conține intrările Executor. Acest dicționar are următoarele chei:

  • options: ExecutorOption de intrare.

  • quantum_program: QuantumProgram de intrare

  • schema_version: Versiunea schemei utilizate pe partea de server.

Dacă codul tău folosea job.inputs['options'] pentru a găsi opțiunile specificate pentru job, poți folosi acum job.result().metadata['options'] în schimb.

Testează local cu un backend fals​

Înainte de a trimite către hardware, poți valida codul migrat față de un backend Fake* pentru a detecta din timp orice erori de sintaxă. Reține următoarele detalii despre modul de testare local:

  • Nu reproduce rezultatele hardware-ului. Simularea zgomotoasă locală nu replică perfect zgomotul dispozitivului real, și prin urmare rezultatele ar putea diferi. Rularea validează totuși că traseele opțiunilor și tipurile de valori sunt corecte.

  • NoiseLearnerV3 nu are niciun mod de testare local: mode-ul său acceptă doar un Backend, Session, sau Batch real, astfel încât nu poți exersa pasul de învățare a zgomotului față de un backend fals. Verifică acea parte a codului tău față de referința API NoiseLearnerV3 în schimb. Confirmă că constructorul, forma de intrare run(instructions), și orice ajutor (cum ar fi ajutorul de straturi unice) sunt folosite conform documentației.

Clifordizează circuitul pentru simulare locală eficientă​

Un backend fals folosește un simulator (zgomotos) de vector de stare, al cărui cost crește exponențial cu numărul de qubiți și adâncimea. Astfel, un circuit de volum de lucru realist poate bloca sau epuiza memoria. Deoarece testarea locală trebuie doar să exerseze traseele opțiunilor (nu să reproducă rezultate fizice), redu circuitul la unul Clifford mai întâi cu ConvertISAToClifford, care rotunjește fiecare unghi RZ/RZZ/RX la cel mai apropiat multiplu de π/2. Circuitele Clifford se simulează eficient (simulare stabilizator) indiferent de dimensiune.

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 necesită un circuit ISA ca intrare (rezultatul lui generate_preset_pass_manager(...).run(...) care vizează backend-ul). Trebuie să ții cont de următoarele consecințe la construirea PUB-ului local:

  • Atributul .layout este eliminat. Circuitul Clifordizat păstrează același număr de qubiți, dar clifford.layout este None, astfel încât observable.apply_layout(clifford.layout) eșuează. Aplică layout-ul observabilului din circuitul ISA pre-Clifford în schimb: isa_obs = observable.apply_layout(isa_circuit.layout), apoi rulează (clifford, isa_obs).

  • Parametrii sunt legați. Rotunjirea unghiurilor de rotație transformă un circuit ISA parametric într-unul Clifford concret, astfel încât clifford.num_parameters devine 0. Un PUB care încă conține un vector de valori de parametri eșuează la coerciție. Pentru rularea locală, elimină vectorul de parametri din PUB; rularea pe hardware păstrează circuitul parametric original și valorile sale.

Pașii următori​