Lewati ke konten utama

Migrasi dari Sampler ke Executor

Panduan ini menjelaskan cara memindahkan workload quantum sampling dari primitive IBM Quantum® Sampler ke primitive Executor.

Rilis beta

Primitive Executor merupakan bagian dari directed execution model. Semua komponen dalam directed execution model saat ini masih dalam versi beta dan mungkin belum stabil. Kamu diundang untuk mengujinya dan memberikan feedback dengan membuka issue di repositori GitHub Samplomatic atau qiskit-ibm-runtime.

Haruskah kamu migrasi?

Tidak semua orang harus migrasi dari Sampler ke Executor. Ada banyak perbedaan antara primitive-primitive ini, tetapi panduan berikut bisa membantumu memutuskan apakah harus migrasi:

Migrasi ke Executor jika kamu adalah ilmuwan informasi kuantum yang menjalankan eksperimen berskala utilitas dan membutuhkan kontrol yang detail dan dapat direproduksi atas teknik seperti Pauli twirling, noise-model learning dan injection, serta perubahan basis — atau yang membutuhkan salah satu kemampuan tambahan yang disediakan oleh Executor.

Tetap gunakan Sampler jika kamu menginginkan antarmuka yang sederhana dan tingkat tinggi serta ingin primitive tersebut mengelola error suppression dan mitigasi untukmu.

Keterbatasan dan catatan penting

Karena Executor dan directed execution model masih dalam versi beta, perhatikan hal berikut sebelum kamu memutuskan untuk migrasi:

  • Belum ada dukungan simulator: Berbeda dengan Sampler, yang memiliki implementasi AerSampler di qiskit-aer untuk simulasi lokal, saat ini belum ada backend simulator untuk Executor. Dukungan simulator diperkirakan akan segera hadir. Sementara itu, kamu tetap bisa memeriksa dan mengambil sampel template circuit secara lokal untuk memvalidasi alur kerjamu sebelum mengirimkannya ke hardware.

  • Panduan ini hanya membahas Sampler, bukan Estimator. Migrasi dari Estimator ke Executor jauh lebih rumit dibandingkan migrasi dari Sampler karena Estimator menghitung expectation value alih-alih mengembalikan sampel mentah. Mereproduksi perilaku Estimator dengan Executor membutuhkan post-processing tambahan. Fungsi utilitas untuk membantu migrasi dari Estimator ke Executor masih dalam pengembangan, jadi panduan ini dengan sengaja hanya menjelaskan alur kerja Sampler.

Perbedaan utama antara Executor dan Sampler

Sampler dan Executor sama-sama mengambil sampel dari output register circuit kuantum, tetapi keduanya menargetkan pengguna yang berbeda:

  • Sampler adalah abstraksi tingkat tinggi. Sampler memiliki karakteristik berikut:

    • Sampler memiliki error suppression bawaan (dynamical decoupling dan twirling).

    • Sampler membuat keputusan implisit untukmu.

    • Sampler dirancang agar pengembang algoritma bisa fokus pada inovasi, bukan konversi data.

  • Executor merupakan bagian dari directed execution model. Executor berbeda dari Sampler dalam banyak hal dan memiliki karakteristik berikut:

    • Executor tidak memiliki error suppression atau mitigasi bawaan. Sebagai gantinya, kamu menyatakan maksud desainmu di sisi client (dengan menggunakan anotasi circuit dan samplex), dan pembuatan varian circuit yang mahal dipindahkan ke sisi server.

    • Executor tidak membuat keputusan implisit. Executor mengikuti perintahmu secara persis, memberikan kontrol dan transparansi penuh.

    • Executor dan Samplomatic bersama-sama menyediakan kemampuan tambahan yang tidak ditawarkan Sampler, termasuk (tetapi tidak terbatas pada) hal berikut:

      • Lebih banyak twirling group: Samplomatic memungkinkanmu memilih twirling group mana yang akan diterapkan per box, alih-alih terbatas pada satu strategi tunggal yang diterapkan Sampler untukmu. Samplomatic juga mendukung twirling group selain Pauli, seperti twirling group "local_c1".
      • Pengukuran kerneled dan classified bersamaan: Menetapkan QuantumProgram.meas_level = "both" (ditambahkan di qiskit-ibm-runtime v0.48.0) meminta agar pengukuran classified dan kerneled sama-sama ada di hasil, alih-alih memilih satu jenis pengukuran per job.
      • Twirling untuk circuit dengan fractional gate: Executor bisa menerapkan twirling ke circuit yang berisi fractional gate.
      • Mitigasi error yang detail dan composable: Misalnya, memilih layer circuit mana yang akan dimitigasi dan menyesuaikan noise rate yang disuntikkan ke circuit.
      Catatan
      • Kemampuan baru di masa depan diperkirakan akan dirilis ke Executor terlebih dahulu dan mungkin tidak diporting ke Sampler. Jika kamu bergantung pada akses ke fitur terbaru, Executor adalah pilihan yang lebih future-proof.
      • Paket dasar Qiskit belum menyediakan base class untuk primitive Executor (Qiskit menyediakannya untuk SamplerV2).

Pemetaan konseptual

Tabel berikut menunjukkan bagaimana konsep Sampler dipetakan ke Executor.

KonsepSamplerExecutor
Importfrom qiskit_ibm_runtime import SamplerV2from qiskit_ibm_runtime import Executor
InputDaftar PUB (tuple)QuantumProgram dari objek QuantumProgramItem
Circuit dan parametertuple (circuit, params, shots)program.append_circuit_item(circuit, circuit_arguments=...)
TwirlingTwirlingOptionsEksplisit melalui box beranotasi dan samplex (append_samplex_item)
Panggilan runsampler.run([pub, ...])executor.run(program)
Tipe hasilPrimitiveResult dari SamplerPubResultQuantumProgramResult (iterable)
Mengakses dataresult[0].data.<register> (BitArray)result[0]["<register>"] (np.ndarray)
Mengelola noiseOpsi bawaanHarus dikomposisi secara manual (anotasi, samplex, NoiseLearnerV3)

Ringkasan langkah-langkah migrasi

  1. Instal Samplomatic.

  2. Ubah import.

  3. Ganti tuple PUB.

  4. Ubah cara shot diekspresikan.

  5. Perbarui opsi lain sesuai kebutuhan.

  6. Perbarui perintah run.

  7. Perbarui cara parsing hasil.

  8. Batalkan twirling.

Langkah 1. Instal paket yang diperlukan

Executor dan directed execution model membutuhkan paket samplomatic:

pip install qiskit qiskit-ibm-runtime samplomatic

# For visualization support:
# pip install samplomatic[vis]
Catatan versi
  • qiskit-ibm-runtime v0.48.0 direkomendasikan karena menambahkan opsi meas_level = "both" dan twirling group local_c1.
  • qiskit >= 2.3.0 diperlukan.
  • samplomatic >= 0.18.0 diperlukan.

Langkah 2. Ubah import

Sampler:

from qiskit_ibm_runtime import SamplerV2 as Sampler

Executor:

from qiskit_ibm_runtime import Executor, QuantumProgram

Langkah 3. Ganti tuple PUB dengan QuantumProgram

Alih-alih meneruskan daftar tuple (PUB), saat menggunakan Executor, kamu membangun QuantumProgram dan menambahkan item ke dalamnya.

QuantumProgram menerima item circuit dan item samplex:

  • append_circuit_item: Menambahkan CircuitItem, yaitu sebuah circuit dan (secara opsional) nilai parameternya. Item ini dieksekusi apa adanya, tanpa randomisasi apa pun.

    Gunakan ini ketika kamu hanya ingin mengambil sampel circuit, persis seperti yang dilakukan Sampler dengan PUB yang tidak memiliki twirling; misalnya, saat mengirimkan job sampling biasa, atau saat kamu sudah menyertakan secara manual varian yang kamu inginkan.

  • append_samplex_item: Menambahkan samplexItem, yaitu sebuah template circuit ditambah samplex yang menghasilkan set parameter acak di sisi server.

    Gunakan ini ketika kamu ingin konten circuit diacak. Kasus utamanya adalah dengan twirling (gate atau measurement) atau noise injection. Kemampuan ini menggantikan twirling bawaan Sampler.

Sebuah QuantumProgram bisa menerima kedua jenis item; setiap item yang ditambahkan dieksekusi sebagai tugas independen dan menghasilkan entri sendiri di hasil. Secara umum, gunakan append_circuit_item ketika circuitmu tidak perlu diacak. Jika tidak, gunakan append_samplex_item.

Bagian selanjutnya menunjukkan masing-masing secara berurutan: circuit berparameter yang menggunakan append_circuit_item, dan migrasi twirling dengan menggunakan append_samplex_item.

Dalam contoh kode berikut, isa_circuit mengacu pada circuit yang telah ditranspilasi agar sesuai dengan Instruction Set Architecture (ISA) backend target. isa_circuit ini berisi dua parameter.

Langkah 3a. Migrasi circuit berparameter

Dengan Sampler, nilai parameter merupakan elemen kedua dari tuple PUB. Dengan Executor, teruskan nilai tersebut sebagai circuit_arguments ke append_circuit_item.

Sampler:

params = np.random.rand(10, circuit.num_parameters) # 10 parameter sets
pubs = (isa_circuit, params)

Executor

program = QuantumProgram(shots=1024)
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, circuit.num_parameters), # 10 sets
)

# CircuitItem result shape: (parameter_sets, shots, register_bits) -> (10, 1024, 2)
result_0 = result[0]["meas"]

Langkah 3b. Migrasi twirling bawaan ke anotasi eksplisit

Ini adalah perubahan paling signifikan. Sampler menerapkan twirling untukmu dengan menggunakan opsi. Dengan Executor, kamu mendeklarasikan maksud tersebut secara eksplisit dengan menggunakan box beranotasi dan samplex (dari Samplomatic).

Sampler (twirling dengan menggunakan opsi):

sampler = Sampler(mode=backend)
sampler.options.twirling.enable_gates = True
sampler.options.twirling.enable_measure = True

Executor (twirling dengan menggunakan box dan samplex):

from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# 1. Group gates and measurements into annotated boxes with twirling annotations
boxes_pm = generate_boxing_pass_manager(
enable_gates=True, # gate twirling
enable_measures=True, # measurement twirling
)
boxed_circuit = boxes_pm.run(isa_circuit)

# 2. Build the (template circuit, samplex) pair.
# The template circuit's single-qubit gates are replaced by parameterized gates;
# the samplex encodes how to generate the randomized parameters at runtime.
template_circuit, samplex = build(boxed_circuit)

# 3. Append as a samplex item, specifying the number of randomizations
program = QuantumProgram(shots=1024)
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(10, 2), # original circuit params
},
shape=(28, 10), # 28 randomizations x 10 parameter sets
)

Karena template circuit dan samplex dibangun di sisi client, kamu bisa memeriksa dan mengambil sampelnya secara lokal untuk memverifikasi output sebelum mengirim apa pun ke hardware.

Verifikasi: Ambil sampel template circuit secara lokal

Kamu bisa menarik randomisasi dari samplex dan mengikatnya ke template circuit untuk memastikan bahwa samplex menghasilkan nilai parameter yang kamu harapkan. Nilai parameter yang dikembalikan oleh samplex.sample secara langsung kompatibel dengan parameter template circuit.

# Check which inputs the samplex requires (for the twirling example above,
# this is just the original circuit's parameter values).
print(samplex.inputs())

# Bind the required inputs, then draw a few randomizations locally.
inputs = samplex.inputs().bind(
parameter_values=np.random.rand(2), # one set of the original circuit's params
)
outputs = samplex.sample(inputs, num_randomizations=3)

# Assign one randomization's parameter values to the template circuit and inspect it.
bound_template = template_circuit.assign_parameters(outputs["parameter_values"][0])
bound_template.draw("mpl", idle_wires=False)

Untuk lebih lanjut, kamu bisa memverifikasi bahwa setiap randomisasi secara logis setara dengan circuit asli, misalnya, dengan mengonversi keduanya menjadi objek Operator dan membandingkan implementasi unitary-nya (setelah memperhitungkan koreksi outputs["measurement_flips.<register>"] yang membatalkan twirling measurement), atau dengan membandingkan expectation value dari StatevectorSampler atau StatevectorEstimator lokal yang dijalankan. Lihat panduan Samplomatic Samplex inputs and outputs untuk penjelasan lengkap.

Langkah 4. Ubah cara shot diminta

Pindahkan shot dari PUB ke QuantumProgram(shots=...). Di Executor, shots berlaku untuk seluruh job. Kirimkan beberapa job jika kamu membutuhkan jumlah shot yang berbeda.

Sampler:

# Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit, None, 25)])

Executor:

# Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

Langkah 5. Perbarui opsi sesuai kebutuhan

Ada lebih sedikit opsi yang tersedia untuk Executor dibandingkan Sampler, karena pilihan mitigasi error kini berada di anotasi dan samplex-mu, bukan di opsi.

Ada juga perbedaan struktural mengenai tempat pengaturan disimpan.

  • Dengan Sampler, semuanya, termasuk pilihan yang memengaruhi post-processing hasil, dikonfigurasi di opsi primitive atau di PUB.

  • Dengan Executor, pilihan yang memengaruhi bagaimana hasil job dibentuk dan diproses ditetapkan di QuantumProgram, bukan di ExecutorOptions.

Contoh:

SamplerExecutor
shotsQuantumProgram(shots=...)
meas_typeQuantumProgram(meas_level=...)

ExecutorOptions hanya menyimpan pengaturan eksekusi dan environment tingkat rendah yang tidak mengubah struktur data yang dikembalikan. Ada tiga grup tingkat atas:

Perlu diketahui bahwa opsi twirling dan dynamical_decoupling ada di Sampler tetapi tidak di Executor. Sebagai gantinya, nilai opsi tersebut diekspresikan melalui directed execution model.

Example:

from qiskit_ibm_runtime import Executor, ExecutorOptions

options = ExecutorOptions(
environment={"log_level": "INFO"},
execution={"init_qubits": True},
)
# or mutate after construction:
options = ExecutorOptions()
options.environment.log_level = "INFO"
options.execution.init_qubits = True

executor = Executor(mode=backend, options=options)

Langkah 6. Perbarui perintah run

Input untuk job Executor adalah program, bukan PUB.

Sampler:

# Submit a job
sampler.run([(isa_circuit, parameter_values)])

Executor:

# Submit a job
executor.run(program)

Langkah 7. Ubah cara kamu mengakses hasil

Di Executor, hasil berupa array NumPy, bukan objek BitArray. Gunakan string nama sebagai indeks (result[0]["meas"]) dan dapatkan np.ndarray sebagai hasilnya. Kamu tidak perlu mengingat path atribut .data.<register>.

Untuk memperbarui dari Sampler ke Executor, ubah result[i].data.<reg> (BitArray) menjadi result[i]["<reg>"] (np.ndarray), lalu tulis ulang post-processing berbasis get_counts sebagai operasi NumPy.

TugasSamplerExecutor
Dapatkan data registerresult[0].data.measresult[0]["meas"]
Tipe dataBitArraynp.ndarray
Dictionary countsresult[0].data.meas.get_counts()Proses array secara manual
Beberapa registerresult[0].data.<name> per registerresult[0]["<name>"] per register
Bentuk array CircuitItem-(parameter_sets, shots, register_bits)
Bentuk array SamplexItem-(randomizations, parameter_sets, shots, register_bits)
Batalkan twirling pengukuranOtomatisresult[i]["measurement_flips.<name>"] + XOR
catatan

BitArray milik Sampler menawarkan helper (get_counts, slice_bits, slice_shots, expectation_values, dan post-selection mask). Executor mengembalikan array NumPy mentah sehingga kamu bisa melakukan post-processing ini dengan operasi NumPy standar.

Langkah 8. Menangani hasil twirled (koreksi bit-flip)

Saat kamu menerapkan measurement twirling melalui SamplexItem, Executor mengembalikan pengukuran mentah (twirled) beserta koreksi bit-flip yang diperlukan untuk membatalkan twirling. Kamu perlu menerapkannya secara manual; tidak ada yang dikoreksi secara implisit.

Saat menggunakan Executor, batalkan twirling secara eksplisit dengan menggunakan koreksi measurement_flips.<reg> dan XOR, seperti yang ditunjukkan pada contoh berikut:

# SamplexItem result shape: (randomizations, parameter_sets, shots, register_bits)
result_1 = result[1]["meas"] # example: (28, 10, 1024, 2)

# Bit-flip corrections to undo measurement twirling
flips_1 = result[1]["measurement_flips.meas"] # example: (28, 10, 1, 2)

# Undo the twirling through classical XOR (broadcasts over the shots axis)
unflipped_result_1 = result_1 ^ flips_1

Tidak ada langkah setara di Sampler karena ia membatalkan twirling untukmu secara otomatis.

Contoh lengkap: Migrasi job sampling dasar

Sampler

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler

# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Run — shots are passed to run()
sampler = Sampler(mode=backend)
job = sampler.run([(isa_circuit,)], shots=25)
result = job.result()

# 5. Access results: a BitArray keyed by register name
counts = result[0].data.meas.get_counts()

Executor

import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram

# 1. Account + backend (unchanged)
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit (unchanged)
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.measure_all()

# 3. Transpile to ISA (unchanged)
pm = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_circuit = pm.run(circuit)

# 4. Build a QuantumProgram — shots are on the program
program = QuantumProgram(shots=25)
program.append_circuit_item(isa_circuit)

# 5. Run
executor = Executor(mode=backend)
job = executor.run(program)
result = job.result()

# 6. Access results: a plain np.ndarray keyed by register name
# shape = (shots, register_bits)
meas = result[0]["meas"]

Langkah berikutnya