Lumaktaw sa pangunahing nilalaman

Mag-migrate sa Qiskit Runtime V2 primitives

babala

Ang mga orihinal na primitives (tinatawag na V1 primitives), V1 Sampler at V1 Estimator, ay na-deprecate na sa qiskit-ibm-runtime 0.23. Tinanggal na ang suporta para sa mga ito noong 15 Agosto 2024.

Dahil sa deprecation ng mga V1 primitives, dapat na i-migrate ang lahat ng code para gamitin ang mga V2 interface. Inilalarawan ng gabay na ito kung ano ang nagbago sa Qiskit Runtime V2 primitives (available sa qiskit-ibm-runtime 0.21.0) at kung bakit, inilarawan nang detalyado ang bawat bagong primitive, at nagbibigay ng mga halimbawa para matulungan kang i-migrate ang code mula sa paggamit ng mga legacy primitive patungo sa mga V2 primitive. Lahat ng halimbawa sa gabay ay gumagamit ng Qiskit Runtime primitives, pero sa pangkalahatan, ang parehong mga pagbabago ay naaangkop din sa iba pang mga implementasyon ng primitive. Ang mga function na natatangi sa Qiskit Runtime tulad ng error mitigation ay nananatiling natatangi sa Qiskit Runtime.

Para sa impormasyon tungkol sa mga pagbabago sa Qiskit reference primitives (tinatawag na ngayon na statevector primitives), tingnan ang seksyon ng qiskit.primitives sa pahina ng Qiskit 1.0 feature changes. Tingnan ang StatevectorSampler at StatevectorEstimator para sa mga V2 primitive reference implementation.

Pangkalahatang-ideya​

Ipinakilala ang Bersyon 2 ng mga primitives na may bagong base class para sa parehong Sampler at Estimator (BaseSamplerV2 at BaseEstimatorV2), kasama ang mga bagong uri para sa kanilang mga input at output.

Pinahihintulutan ng bagong interface na tukuyin mo ang isang Circuit at maraming observables (kung gumagamit ng Estimator) at mga set ng parameter value para sa Circuit na iyon, para ang mga sweep sa mga set ng parameter value at observables ay maaaring tukuyin nang mahusay. Dati, kailangan mong tukuyin nang paulit-ulit ang parehong Circuit para tumugma sa laki ng data na pagsamasamahin. Gayundin, habang maaari ka pa ring gumamit ng resilience_level (kung gumagamit ng Estimator) bilang simpleng kontrol, binibigyan ka ng V2 primitives ng kakayahang i-on o i-off ang mga indibidwal na paraan ng error mitigation / suppression para i-customize ang mga ito ayon sa iyong pangangailangan.

Para mabawasan ang kabuuang oras ng pagsasagawa ng job, tinatanggap lamang ng V2 primitives ang mga Circuit at observable na gumagamit ng mga instruction na sinusuportahan ng target na QPU (quantum processing unit). Ang mga Circuit at observable na ito ay tinutukoy bilang instruction set architecture (ISA) circuits at observables. Hindi nagsasagawa ang V2 primitives ng mga operasyon ng layout, routing, at translation. Tingnan ang dokumentasyon ng transpilation para sa mga tagubilin sa pag-transform ng mga Circuit.

Ang Sampler V2 ay pinahusay para makatutok sa pangunahing gawain nito na pag-sample ng output register mula sa pagpapatupad ng mga quantum circuit. Ibinabalik nito ang mga sample, ang uri nito ay tinukoy ng programa, nang walang mga timbang. Ang output data ay pinaghiwalay din ayon sa mga pangalan ng output register na tinukoy ng programa. Ang pagbabagong ito ay nagbibigay-daan sa hinaharap na suporta para sa mga Circuit na may classical control flow.

Tingnan ang EstimatorV2 API reference at SamplerV2 API reference para sa buong detalye.

Mga pangunahing pagbabago​

Import​

Para sa backward compatibility, kailangan mong explicitly i-import ang mga V2 primitives. Ang pagtukoy ng import <primitive>V2 as <primitive> ay hindi required, pero mas pinapadali nito ang pag-transition ng code sa V2.

babala

Pagkatapos na hindi na sinusuportahan ang mga V1 primitives, ang import <primitive> ay mag-i-import ng V2 na bersyon ng tinukoy na primitive.

from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import SamplerV2 as Sampler

Input at output​

Input​

Parehong SamplerV2 at EstimatorV2 ay tumatanggap ng isa o higit pang primitive unified blocs (PUBs) bilang input. Ang bawat PUB ay isang tuple na naglalaman ng isang Circuit at ang data na ibino-broadcast sa Circuit na iyon, na maaaring maraming observables at mga parameter. Bawat PUB ay nagbabalik ng resulta.

  • Format ng Sampler V2 PUB: (<circuit>, <parameter values>, <shots>), kung saan ang <parameter values> at <shots> ay opsyonal.
  • Format ng Estimator V2 PUB: (<circuit>, <observables>, <parameter values>, <precision>), kung saan ang <parameter values> at <precision> ay opsyonal. Ginagamit ang Numpy broadcasting rules kapag pinagsama ang mga observable at parameter value.

Bukod dito, ginawa rin ang mga sumusunod na pagbabago:

  • Ang Estimator V2 ay may bagong precision na argument sa run() na paraan na tumutukoy sa target na precision ng mga estimate ng expectation value.
  • Ang Sampler V2 ay may shots na argument sa run() na paraan nito.
Mga halimbawa​

Halimbawa ng Estimator V2 na gumagamit ng precision sa run():

# Estimate expectation values for two PUBs, both with 0.05 precision.
estimator.run([(circuit1, obs_array1), (circuit2, obs_array_2)], precision=0.05)

Halimbawa ng Sampler V2 na gumagamit ng shots sa run():

# Sample two circuits at 128 shots each.
sampler.run([circuit1, circuit2], shots=128)

# Sample two circuits at different amounts of shots.
# The "None"s are necessary as placeholders
# for the lack of parameter values in this example.
sampler.run([
(circuit1, None, 123),
(circuit2, None, 456),
])

Output​

Ang output ay nasa format na PubResult na ngayon. Ang PubResult ay ang data at metadata na nagmumula sa pagpapatupad ng isang PUB.

  • Patuloy na nagbabalik ng mga expectation value ang Estimator V2.

  • Ang data na bahagi ng isang Estimator V2 PubResult ay naglalaman ng parehong mga expectation value at standard error (stds). Ang V1 ay nagbabalik ng variance sa metadata.

  • Ang Sampler V2 ay nagbabalik ng per-shot na mga measurement sa anyo ng mga bitstring, sa halip na ang mga quasi-probability distribution mula sa V1 na interface. Ipinapakita ng mga bitstring ang mga resulta ng measurement, pinapanatili ang pagkakasunud-sunod ng shot kung paano ito nasukat.

  • Ang Sampler V2 ay may mga convenience method tulad ng get_counts() para makatulong sa migration.

  • Inaayos ng mga result object ng Sampler V2 ang data ayon sa mga pangalan ng classical register ng mga input circuit, para sa compatibility sa mga dynamic circuit. Sa default, ang pangalan ng classical register ay meas, tulad ng ipinakita sa sumusunod na halimbawa. Kapag tinutukoy ang iyong Circuit, kung gumawa ka ng isa o higit pang classical register na may hindi default na pangalan, gamitin ang pangalang iyon para makuha ang mga resulta. Mahahanap mo ang pangalan ng classical register sa pamamagitan ng pagpapatakbo ng <circuit_name>.cregs. Halimbawa, qc.cregs.

    # Define a quantum circuit with 2 qubits
    circuit = QuantumCircuit(2)
    circuit.h(0)
    circuit.cx(0, 1)
    circuit.measure_all()
    circuit.draw()
            β”Œβ”€β”€β”€β”      β–‘ β”Œβ”€β”
    q_0: ─ H β”œβ”€β”€β– β”€β”€β”€β–‘β”€β”€Mβ”œβ”€β”€β”€
    β””β”€β”€β”€β”˜β”Œβ”€β”΄β”€β” β–‘ β””β•₯β”˜β”Œβ”€β”
    q_1: ────── X β”œβ”€β–‘β”€β”€β•«β”€β”€Mβ”œ
    β””β”€β”€β”€β”˜ β–‘ β•‘ β””β•₯β”˜
    meas: 2/══════════════╩══╩═
    0 1

Mga halimbawa ng Estimator (input at output)​

# Estimator V1: Execute 1 circuit with 4 observables
job = estimator_v1.run([circuit] * 4, [obs1, obs2, obs3, obs4])
evs = job.result().values

# Estimator V2: Execute 1 circuit with 4 observables
job = estimator_v2.run([(circuit, [obs1, obs2, obs3, obs4])])
evs = job.result()[0].data.evs

Mga halimbawa ng Sampler (input at output)​

  # Sampler V1: Execute 1 circuit with 3 parameter sets
job = sampler_v1.run([circuit] * 3, [vals1, vals2, vals3])
dists = job.result().quasi_dists

# Sampler V2: Executing 1 circuit with 3 parameter sets
job = sampler_v2.run([(circuit, [vals1, vals2, vals3])])
counts = job.result()[0].data.meas.get_counts()

Halimbawa na gumagamit ng iba't ibang output register

from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

alpha = ClassicalRegister(5, "alpha")
beta = ClassicalRegister(7, "beta")
qreg = QuantumRegister(12)

circuit = QuantumCircuit(qreg, alpha, beta)
circuit.h(0)
circuit.measure(qreg[:5], alpha)
circuit.measure(qreg[5:], beta)

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=12)
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
print(f" >> Counts for the alpha output register: {pub_result.data.alpha.get_counts()}")
print(f" >> Counts for the beta output register: {pub_result.data.beta.get_counts()}")

Mga opsyon​

Iba ang paraan ng pagtukoy ng mga opsyon sa mga V2 primitives:

  • Ang SamplerV2 at EstimatorV2 ay may magkahiwalay na mga klase ng opsyon. Makikita mo ang mga available na opsyon at ma-update ang mga value ng opsyon sa panahon ng o pagkatapos ng primitive initialization.
  • Sa halip na ang set_options() na paraan, ang mga opsyon ng V2 primitive ay may update() na paraan na nag-a-apply ng mga pagbabago sa options na attribute.
  • Kung hindi mo tinukoy ang value para sa isang opsyon, bibigyan ito ng espesyal na value na Unset at gagamitin ang mga default ng server.
  • Para sa mga V2 primitives, ang options na attribute ay ang dataclass na uri sa Python. Maaari mong gamitin ang built-in na asdict na paraan para i-convert ito sa isang dictionary.

Tingnan ang API reference para sa listahan ng mga available na opsyon.

from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(backend, options={"resilience_level": 2})

# Setting options after primitive initialization
# This uses auto complete.
estimator.options.default_shots = 4000
# This does bulk update.
estimator.options.update(default_shots=4000, resilience_level=2)

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(estimator.options))
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2 as Sampler

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
sampler = Sampler(backend, options={"default_shots": 4096})

# Setting options after primitive initialization
# This uses auto complete.
sampler.options.dynamical_decoupling.enable = True
# Turn on gate twirling. Requires qiskit_ibm_runtime 0.23.0 or later.
sampler.options.twirling.enable_gates = True

# This does bulk update. The value for default_shots is overridden if you specify shots with run() or in the PUB.
sampler.options.update(default_shots=1024, dynamical_decoupling={"sequence_type": "XpXm"})

# Print the dictionary format.
# Server defaults are used for unset options.
print(asdict(sampler.options))

Error mitigation at suppression​

  • Dahil ang Sampler V2 ay nagbabalik ng mga sample nang walang postprocessing, hindi nito sinusuportahan ang mga resilience level.

  • Hindi sinusuportahan ng Sampler V2 ang optimization_level.

  • Ang Estimator V2 ay mag-aalis ng suporta para sa optimization_level sa o bandang 30 Setyembre 2024.

  • Hindi sinusuportahan ng Estimator V2 ang resilience level 3. Ito ay dahil ang resilience level 3 sa V1 Estimator ay gumagamit ng Probabilistic Error Cancellation (PEC), na napatunayang nagbibigay ng unbiased na mga resulta sa halaga ng exponential na oras ng pagpoproseso. Tinanggal ang Level 3 para mapansin ang trade-off na iyon. Gayunpaman, maaari ka pa ring gumamit ng PEC bilang paraan ng error mitigation sa pamamagitan ng pagtukoy ng opsyon na pec_mitigation.

  • Sinusuportahan ng Estimator V2 ang resilience_level 0-2, tulad ng inilarawan sa sumusunod na talahanayan. Ang mga opsyong ito ay mas advanced kaysa sa mga katapat nito sa V1. Maaari ka ring explicitly mag-on / mag-off ng mga indibidwal na paraan ng error mitigation / suppression.

    Level 1Level 2
    Measurement twirlingMeasurement twirling
    Readout error mitigationReadout error mitigation
    ZNE
from dataclasses import asdict
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Setting options during primitive initialization
estimator = Estimator(backend)

# Set resilience_level to 0
estimator.options.resilience_level = 0

# Turn on measurement error mitigation
estimator.options.resilience.measure_mitigation = True
from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

print(f">> dynamical decoupling sequence to use: {sampler.options.dynamical_decoupling.sequence_type}")

Transpilation​

Ang mga V2 primitives ay sumusuporta lamang sa mga Circuit na sumusunod sa Instruction Set Architecture (ISA) ng isang partikular na backend. Dahil hindi nagsasagawa ang mga primitives ng mga operasyon ng layout, routing, at translation, ang mga kaukulang opsyon ng transpilation mula sa V1 ay hindi sinusuportahan.

Katayuan ng job​

Ang mga V2 primitives ay may bagong RuntimeJobV2 na klase, na nagmamana mula sa BasePrimitiveJob. Ang status() na paraan ng bagong klaseng ito ay nagbabalik ng string sa halip na isang JobStatus enum mula sa Qiskit. Tingnan ang RuntimeJobV2 API reference para sa mga detalye.

job = estimator.run(...)

# check if a job is still running
print(f"Job {job.job_id()} is still running: {job.status() == "RUNNING"}")

Mga hakbang para mag-migrate sa Estimator V2​

  1. Palitan ang from qiskit_ibm_runtime import Estimator ng from qiskit_ibm_runtime import EstimatorV2 as Estimator.

  2. Alisin ang anumang from qiskit_ibm_runtime import Options na mga statement, dahil hindi ginagamit ng mga V2 primitives ang klase ng Options. Sa halip, maaari kang magpasa ng mga opsyon bilang dictionary kapag nagsi-initialize ng klase ng EstimatorV2 (halimbawa estimator = Estimator(backend, options={"dynamical_decoupling": {"enable": True}})), o itakda ang mga ito pagkatapos ng initialization:

    estimator = Estimator(backend)
    estimator.options.dynamical_decoupling.enable = True
  3. Suriin ang lahat ng sinusuportahang opsyon at gumawa ng mga update ayon sa pagkakailangan.

  4. Pagsamahin ang bawat Circuit na gusto mong patakbuhin kasama ang mga observable at parameter value na gusto mong ilapat sa Circuit sa isang tuple (isang PUB). Halimbawa, gamitin ang (circuit1, observable1, parameter_set1) kung gusto mong patakbuhin ang circuit1 na may observable1 at parameter_set1.

  5. Maaaring kailanganin mong baguhin ang hugis ng iyong mga array ng observable o mga set ng parameter kung gusto mong ilapat ang outer product ng mga ito. Halimbawa, ang isang array ng mga observable na may hugis (4, 1) at isang array ng mga set ng parameter na may hugis (1, 6) ay magbibigay sa iyo ng resulta na (4, 6) na mga expectation value. Tingnan ang Numpy broadcasting rules para sa higit pang detalye.

  6. Maaari mong opsyonal na tukuyin ang precision na gusto mo para sa partikular na PUB na iyon.

  7. I-update ang run() na paraan ng estimator para maipasa ang listahan ng mga PUB. Halimbawa, run([(circuit1, observable1, parameter_set1)]). Maaari kang opsyonal na tukuyin ang isang precision dito, na ilalapat sa lahat ng PUB.

  8. Ang mga resulta ng Estimator V2 job ay napangkat ayon sa mga PUB. Makikita mo ang expectation value at standard error para sa bawat PUB sa pamamagitan ng pag-index dito. Halimbawa:

pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")

Mga kumpletong halimbawa ng Estimator​

Magpatakbo ng isang eksperimento​

Gamitin ang Estimator para alamin ang expectation value ng isang circuit-observable pair.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import EstimatorV2 as Estimator, QiskitRuntimeService

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)
estimator = Estimator(backend)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
observable = SparsePauliOp("Z" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)

job = estimator.run([(isa_circuit, isa_observable)])
result = job.result()

print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

Magpatakbo ng maraming eksperimento sa iisang job​

Gamitin ang Estimator para alamin ang mga expectation value ng maraming circuit-observable pair.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)

n_qubits = 3
rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
observables = [
SparsePauliOp("X" * n_qubits),
SparsePauliOp("Y" * n_qubits),
SparsePauliOp("Z" * n_qubits),
]

isa_circuits = pm.run(circuits)
isa_observables = [ob.apply_layout(isa_circuits[0].layout) for ob in observables]

estimator = Estimator(backend)
job = estimator.run([(isa_circuits[0], isa_observables[0]),(isa_circuits[1], isa_observables[1]),(isa_circuits[2], isa_observables[2])])
job_result = job.result()
for idx in range(len(job_result)):
pub_result = job_result[idx]
print(f">>> Expectation values for PUB {idx}: {pub_result.data.evs}")
print(f">>> Standard errors for PUB {idx}: {pub_result.data.stds}")

Magpatakbo ng mga parameterized na circuit​

Gamitin ang Estimator para magpatakbo ng maraming eksperimento sa iisang job, gamit ang mga parameter value para mapataas ang muling paggamit ng circuit. Sa sumusunod na halimbawa, pansinin na ang mga hakbang 1 at 2 ay pareho para sa V1 at V2.

import numpy as np

from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem

theta = Parameter("ΞΈ")

chsh_circuit = QuantumCircuit(2)
chsh_circuit.h(0)
chsh_circuit.cx(0, 1)
chsh_circuit.ry(theta, 0)

number_of_phases = 21
phases = np.linspace(0, 2 * np.pi, number_of_phases)
individual_phases = [[ph] for ph in phases]

ZZ = SparsePauliOp.from_list([("ZZ", 1)])
ZX = SparsePauliOp.from_list([("ZX", 1)])
XZ = SparsePauliOp.from_list([("XZ", 1)])
XX = SparsePauliOp.from_list([("XX", 1)])
ops = [ZZ, ZX, XZ, XX]

# Step 2: Optimize problem for quantum execution.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
chsh_isa_circuit = pm.run(chsh_circuit)
isa_observables = [operator.apply_layout(chsh_isa_circuit.layout) for operator in ops]

from qiskit_ibm_runtime import EstimatorV2 as Estimator

# Step 3: Execute using Qiskit primitives.

# Reshape observable array for broadcasting
reshaped_ops = np.fromiter(isa_observables, dtype=object)
reshaped_ops = reshaped_ops.reshape((4, 1))

estimator = Estimator(backend, options={"default_shots": int(1e4)})
job = estimator.run([(chsh_isa_circuit, reshaped_ops, individual_phases)])
# Get results for the first (and only) PUB
pub_result = job.result()[0]
print(f">>> Expectation values: {pub_result.data.evs}")
print(f">>> Standard errors: {pub_result.data.stds}")
print(f">>> Metadata: {pub_result.metadata}")

Gumamit ng mga session at advanced na opsyon​

Tuklasin ang mga session at advanced na opsyon para ma-optimize ang pagganap ng circuit sa mga QPU.

babala

Ang sumusunod na code block ay magbabalik ng error para sa mga user na nasa Open Plan dahil gumagamit ito ng mga session. Ang mga workload sa Open Plan ay maaari lamang tumakbo sa job mode o batch mode.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp, random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, Session, EstimatorV2 as Estimator

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
observable = SparsePauliOp("X" * n_qubits)
another_observable = SparsePauliOp("Y" * n_qubits)

pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
another_isa_observable = another_observable.apply_layout(another_isa_circuit.layout)

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
estimator = Estimator()

estimator.options.resilience_level = 1

job = estimator.run([(isa_circuit, isa_observable)])
another_job = estimator.run([(another_isa_circuit, another_isa_observable)])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Expectation value: {result[0].data.evs}")
print(f" > Metadata: {result[0].metadata}")

# second job
print(f" > Another Expectation value: {another_result[0].data.evs}")
print(f" > More Metadata: {another_result[0].metadata}")

Mga hakbang para mag-migrate sa Sampler V2​

  1. Palitan ang from qiskit_ibm_runtime import Sampler ng from qiskit_ibm_runtime import SamplerV2 as Sampler.
  2. Alisin ang anumang from qiskit_ibm_runtime import Options na statement, dahil hindi ginagamit ng V2 primitives ang klase ng Options. Sa halip, maaari kang magpasa ng mga opsyon bilang dictionary kapag sini-initialize ang klase ng SamplerV2 (halimbawa, sampler = Sampler(backend, options={"default_shots": 1024})), o itakda ang mga ito pagkatapos ng initialization:
    sampler = Sampler(backend)
    sampler.options.default_shots = 1024
  3. Suriin ang lahat ng mga sinusuportahang opsyon at gumawa ng mga pagbabago kung kinakailangan.
  4. Pangkatin ang bawat circuit na gusto mong patakbuhin kasama ang mga observable at parameter value na gusto mong ilapat sa circuit sa isang tuple (isang PUB). Halimbawa, gamitin ang (circuit1, parameter_set1) kung gusto mong patakbuhin ang circuit1 kasama ang parameter_set1. Maaari kang opsyonal na tukuyin ang shots na gusto mo para sa partikular na PUB na iyon.
  5. I-update ang sampler na run() na method para ipasa ang listahan ng mga PUB. Halimbawa, run([(circuit1, parameter_set1)]). Maaari kang opsyonal na tukuyin ang shots dito, na mag-aaplai sa lahat ng PUB.
  6. Ang mga resulta ng Sampler V2 job ay naka-grupo ayon sa mga PUB. Makikita mo ang output data para sa bawat PUB sa pamamagitan ng pag-index dito. Habang nagbabalik ang Sampler V2 ng mga unweighted na sample, ang result class ay may convenience method para makakuha ng mga count. Halimbawa:
pub_result = job.result()[0]
print(f">>> Counts: {pub_result.data.meas.get_counts()}")
print(f">>> Per-shot measurement: {pub_result.data.meas.get_counts()}")
tala

Kailangan mo ang pangalan ng classical register para makuha ang mga resulta. Sa default, ito ay pinangalanang meas kapag ginamit mo ang measure_all(). Kapag tinukoy mo ang iyong circuit, kung gumawa ka ng isa o higit pang classical register na may hindi default na pangalan, gamitin ang pangalan na iyon para makuha ang mga resulta. Mahahanap mo ang pangalan ng classical register sa pamamagitan ng pagpapatakbo ng <circuit_name>.cregs. Halimbawa, qc.cregs.

Mga kumpletong halimbawa ng Sampler​

Magpatakbo ng isang eksperimento​

Gamitin ang Sampler para malaman ang mga counts o quasi-probability distribution ng isang Circuit.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

mat = np.real(random_hermitian(n_qubits, seed=1234))
circuit = IQP(mat)
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

sampler = Sampler(backend)
job = sampler.run([isa_circuit])
result = job.result()

Magpatakbo ng maraming eksperimento sa iisang job​

Gamitin ang Sampler para malaman ang mga counts o quasi-probability distribution ng maraming Circuit sa iisang job.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

service = QiskitRuntimeService()

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

n_qubits = 127

rng = np.random.default_rng()
mats = [np.real(random_hermitian(n_qubits, seed=rng)) for _ in range(3)]
circuits = [IQP(mat) for mat in mats]
for circuit in circuits:
circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuits = pm.run(circuits)

sampler = Sampler(backend)
job = sampler.run(isa_circuits)
result = job.result()

for idx, pub_result in enumerate(result):
print(f" > Counts for pub {idx}: {pub_result.data.meas.get_counts()}")

Magpatakbo ng mga parameterized na Circuit​

Magpatakbo ng ilang eksperimento sa iisang job, gamit ang mga parameter value para mapataas ang muling paggamit ng Circuit.

import numpy as np
from qiskit.circuit.library import RealAmplitudes
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService

# Step 1: Map classical inputs to a quantum problem
num_qubits = 127
circuit = RealAmplitudes(num_qubits=num_qubits, reps=2)
circuit.measure_all()

# Define three sets of parameters for the circuit
rng = np.random.default_rng(1234)
parameter_values = [
rng.uniform(-np.pi, np.pi, size=circuit.num_parameters) for _ in range(3)
]

# Step 2: Optimize problem for quantum execution.

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False, min_num_qubits=num_qubits)

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)

# Step 3: Execute using Qiskit primitives.

from qiskit_ibm_runtime import SamplerV2 as Sampler

sampler = Sampler(backend)
job = sampler.run([(isa_circuit, parameter_values)])
result = job.result()
# Get results for the first (and only) PUB
pub_result = result[0]
# Get counts from the classical register "meas".
print(f" >> Counts for the meas output register: {pub_result.data.meas.get_counts()}")

Gumamit ng mga session at advanced na opsyon​

Tuklasin ang mga session at advanced na opsyon para ma-optimize ang performance ng Circuit sa mga QPU.

babala

Ang sumusunod na code block ay magbabalik ng error para sa mga gumagamit ng Open Plan dahil gumagamit ito ng mga session. Ang mga workload sa Open Plan ay maaari lamang tumakbo sa job mode o batch mode.

import numpy as np
from qiskit.circuit.library import IQP
from qiskit.quantum_info import random_hermitian
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler, Session

n_qubits = 127

rng = np.random.default_rng(1234)
mat = np.real(random_hermitian(n_qubits, seed=rng))
circuit = IQP(mat)
circuit.measure_all()
mat = np.real(random_hermitian(n_qubits, seed=rng))
another_circuit = IQP(mat)
another_circuit.measure_all()

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
another_isa_circuit = pm.run(another_circuit)

service = QiskitRuntimeService()

# Turn on dynamical decoupling with sequence XpXm.
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XpXm"

backend = service.least_busy(operational=True, simulator=False, min_num_qubits=127)

with Session(backend=backend) as session:
sampler = Sampler()
job = sampler.run([isa_circuit])
another_job = sampler.run([another_isa_circuit])
result = job.result()
another_result = another_job.result()

# first job
print(f" > Counts for job 1: {result[0].data.meas.get_counts()}")

# second job
print(f" > Counts for job 2: {another_result[0].data.meas.get_counts()}")

Mga susunod na hakbang​

Mga Rekomendasyon