Migrasi dari Sampler ke Executor
Panduan ini menjelaskan cara memindahkan workload quantum sampling dari primitive IBM Quantum® Sampler ke primitive Executor.
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
AerSamplerdiqiskit-aeruntuk 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 diqiskit-ibm-runtimev0.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).
- 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
-
Pemetaan konseptual
Tabel berikut menunjukkan bagaimana konsep Sampler dipetakan ke Executor.
| Konsep | Sampler | Executor |
|---|---|---|
| Import | from qiskit_ibm_runtime import SamplerV2 | from qiskit_ibm_runtime import Executor |
| Input | Daftar PUB (tuple) | QuantumProgram dari objek QuantumProgramItem |
| Circuit dan parameter | tuple (circuit, params, shots) | program.append_circuit_item(circuit, circuit_arguments=...) |
| Twirling | TwirlingOptions | Eksplisit melalui box beranotasi dan samplex (append_samplex_item) |
| Panggilan run | sampler.run([pub, ...]) | executor.run(program) |
| Tipe hasil | PrimitiveResult dari SamplerPubResult | QuantumProgramResult (iterable) |
| Mengakses data | result[0].data.<register> (BitArray) | result[0]["<register>"] (np.ndarray) |
| Mengelola noise | Opsi bawaan | Harus dikomposisi secara manual (anotasi, samplex, NoiseLearnerV3) |
Ringkasan langkah-langkah migrasi
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]
qiskit-ibm-runtimev0.48.0 direkomendasikan karena menambahkan opsimeas_level = "both"dan twirling grouplocal_c1.qiskit >= 2.3.0diperlukan.samplomatic >= 0.18.0diperlukan.
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: MenambahkanCircuitItem, 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: MenambahkansamplexItem, 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 diExecutorOptions.
Contoh:
| Sampler | Executor |
|---|---|
shots | QuantumProgram(shots=...) |
meas_type | QuantumProgram(meas_level=...) |
ExecutorOptions hanya menyimpan pengaturan eksekusi dan environment tingkat rendah
yang tidak mengubah struktur data yang dikembalikan. Ada tiga grup tingkat atas:
-
environment(EnvironmentOptions) -
execution(ExecutionOptions): Berisi lebih sedikit opsi dibandingkan Sampler. Misalnya, tidak ada opsimeas_typeExecutor.
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.
| Tugas | Sampler | Executor |
|---|---|---|
| Dapatkan data register | result[0].data.meas | result[0]["meas"] |
| Tipe data | BitArray | np.ndarray |
| Dictionary counts | result[0].data.meas.get_counts() | Proses array secara manual |
| Beberapa register | result[0].data.<name> per register | result[0]["<name>"] per register |
| Bentuk array CircuitItem | - | (parameter_sets, shots, register_bits) |
| Bentuk array SamplexItem | - | (randomizations, parameter_sets, shots, register_bits) |
| Batalkan twirling pengukuran | Otomatis | result[i]["measurement_flips.<name>"] + XOR |
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"]