Transpiler
Fungsi generate_boxing_pass_manager() adalah alat yang fleksibel dan praktis untuk membangun qiskit.transpiler.PassManager yang bisa mengelompokkan instruksi circuit ke dalam box beranotasi. Ini menjadi alternatif mudah untuk mengelompokkan dan menganotasi circuit secara manual, dan berguna untuk mengotomatiskan workflow, menguji berbagai strategi boxing circuit, dan sebagainya.
Panduan ini mengilustrasikan cara memakai generate_boxing_pass_manager() beserta argumennya. Untuk menyoroti efek tiap argumen fungsi, bagian-bagian berikut terutama merujuk ke circuit di bawah.
from qiskit.circuit import Parameter, QuantumCircuit
circuit = QuantumCircuit(4, 7)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(1)
circuit.cx(1, 0)
circuit.cx(2, 3)
circuit.measure(range(1, 4), range(3))
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
for qubit in range(4):
circuit.rz(Parameter(f"th_{qubit}"), qubit)
circuit.rx(Parameter(f"phi_{qubit}"), qubit)
circuit.rz(Parameter(f"lam_{qubit}"), qubit)
circuit.measure(range(4), range(3, 7))
circuit.draw("mpl", scale=0.8)
Mengelompokkan operasi ke dalam box
Argumen enable_gates bisa diatur ke True atau False untuk menentukan apakah gate dua-qubit harus dikelompokkan ke dalam box. Demikian juga, enable_measures menentukan apakah pengukuran harus dikelompokkan atau tidak. Cuplikan berikut menunjukkan contoh di mana gate dan pengukuran sama-sama dikelompokkan.
from samplomatic.transpiler import generate_boxing_pass_manager
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)

Seperti terlihat pada gambar, pass manager membuat box dari dua tipe: yang berisi satu layer gate dua-qubit, dan yang berisi satu layer pengukuran. Pemisahan ini mencerminkan praktik standar dalam protokol pembelajaran dan mitigasi noise, yang biasanya menargetkan layer operasi yang homogen. Gate dua-qubit dan pengukuran ditempatkan di box paling kiri yang bisa menampungnya, dan setiap gate single-qubit ditempatkan di box yang sama dengan gate dua-qubit atau pengukuran yang mengikutinya.
Kode berikut menunjukkan contoh di mana enable_gates diatur ke False. Seperti terlihat, gate dua-qubit tidak dikelompokkan ke dalam box, begitu juga gate single-qubit yang mendahuluinya.
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=False,
enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
Memilih cara menganotasi box
Semua box gate dua-qubit dan pengukuran pada circuit yang dikembalikan memiliki anotasi left-dressed. Khususnya, semua box yang berisi gate dua-qubit dianotasi dengan Twirl. Untuk box pengukuran, pengguna bisa memilih antara Twirl, BasisTranform (dengan mode diatur ke "measure"), atau keduanya. Kode berikut menghasilkan circuit di mana semua box di-twirl, dan box pengukuran juga dianotasi dengan BasisTranform.
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
measure_annotations="all",
)
transpiled_circuit = boxing_pass_manager.run(circuit)
Menyiapkan circuit untuk injeksi noise
inject_noise_targets memungkinkan kamu menentukan box mana yang harus menerima anotasi InjectNoise. Misalnya, kode berikut menghasilkan circuit di mana box gate dua-qubit memiliki anotasi InjectNoise tetapi box pengukuran tidak.
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates",
)
transpiled_circuit = boxing_pass_manager.run(circuit)
Kalau sebuah circuit berisi dua atau lebih box yang setara hingga perbedaan gate single-qubit, semuanya dianotasi dengan anotasi InjectNoise dengan ref yang sama. Jadi, jumlah ref unik pada circuit yang dikembalikan sama dengan jumlah box unik.
Nilai untuk inject_noise_strategy memengaruhi anotasi InjectNoise. Nilai berikut dimungkinkan:
-
modifier_ref='': Disarankan kalau kamu tidak perlu mengubah peta noise sebelum mengambil sampel darinya. -
modifier_ref=ref: Disarankan kalau semua peta noise perlu diskalakan secara seragam dengan faktor yang sama. -
Nilai
modifier_refyang unik: Disarankan kalau setiap peta noise perlu diskalakan dengan faktor yang berbeda.
Kode berikut menghasilkan circuit di mana box gate dua-qubit memiliki anotasi InjectNoise dengan nilai modifier_ref yang unik.
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates",
inject_noise_strategy="individual_modification",
)
transpiled_circuit = boxing_pass_manager.run(circuit)
Memilih strategi twirling
Boxing pass manager mulai dengan memilih gate entangling dan instruksi pengukuran mana yang harus berada di box yang sama. Setelah itu selesai, box pada umumnya tidak akan selebar penuh—kemungkinan hanya mencakup sebagian qubit yang aktif di circuit atau ada di target backend. Pada konfigurasi umum boxing pass manager, karena semua qubit yang dikenai instruksi box di-twirl (bahkan yang tidak dikenai instruksi apa pun di dalam box), box dengan lebar parsial memengaruhi efek noise-tailoring dari twirling. Qubit yang idle tidak di-twirl dan bisa, misalnya, mengakumulasi error koheren selama box diterapkan. twirling_strategy menyediakan opsi untuk memperluas qubit yang dimiliki tiap box, sehingga mengubah jumlah qubit idle:
-
"active"(default) tidak memperluas box ke qubit idle mana pun. -
"active_accum"memperluas semua box ke qubit yang sudah dikenai instruksi sebelumnya di circuit. Ini berguna kalau kamu ingin membiarkan qubit di awal circuit tetap dalam ground state selama mungkin dengan tidak men-twirl-nya sampai qubit itu menjadi aktif. -
"active_circuit"memperluas semua box ke qubit yang dikenai instruksi apa pun di circuit. Ini berguna kalau kamu punya circuit terstruktur dengan layer entangling yang berulang dan ingin meminimalkan jumlah box unik karena noise tiap box perlu dipelajari. -
"all"memperluas semua box ke semua qubit di semua register kuantum pada circuit. Ini berguna sebagai perluasan dari"active_circuit"kalau kamu ingin semua qubit di QPU selalu di-twirl.
Pada contoh berikut, setiap strategi diterapkan ke circuit dasar, lalu hasilnya digambar.
circuit = QuantumCircuit(5, 2)
circuit.h(range(4))
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.cx(2, 3)
circuit.barrier()
circuit.h(range(4))
circuit.measure([1, 2], [0, 1])
circuit.draw("mpl", scale=0.6)
import matplotlib.pyplot as plt
plt.figure(figsize=(9, 6))
for idx, strategy in enumerate(["active", "active_accum", "active_circuit", "all"]):
ax = plt.subplot(2, 2, idx + 1)
pm = generate_boxing_pass_manager(twirling_strategy=strategy, remove_barriers="never")
transpiled_circuit = pm.run(circuit)
fig = transpiled_circuit.draw("mpl", scale=0.8, ax=ax)
ax.set_title(f"twirling_strategy='{strategy}'")
plt.tight_layout()

Menentukan cara memperlakukan barrier
Secara default, barrier dihapus dari circuit setelah semua gate entangling dan instruksi pengukuran selesai di-box sesuai instruksi, termasuk perluasannya akibat pilihan twirling_strategy, tetapi sebelum gate single-qubit ditambahkan ke box tepat di sebelah kanannya, kalau memungkinkan.
Misalnya, circuit berikut berisi sebuah barrier:
circuit_with_barrier = QuantumCircuit(4)
circuit_with_barrier.h(range(4))
circuit_with_barrier.cz(0, 1)
circuit_with_barrier.barrier()
circuit_with_barrier.cz(2, 3)
circuit_with_barrier.measure_all()
circuit_with_barrier.draw("mpl", scale=0.8)
Perilaku default mencegah kedua gate entangling berada di box yang sama, meskipun gate single-qubit tetap bisa diserap ke box tersedia berikutnya walaupun ada barrier.
boxing_pass_manager = generate_boxing_pass_manager()
transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
Mengatur remove_barriers ke 'never' mempertahankan barrier sehingga gate single-qubit dibatasi.
boxing_pass_manager = generate_boxing_pass_manager(remove_barriers="never")
transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
Terakhir, kalau barrier dihapus segera, entangler tidak lagi dibatasi untuk berada di box yang terpisah.
boxing_pass_manager = generate_boxing_pass_manager(remove_barriers="immediately")
transpiled_circuit = boxing_pass_manager.run(circuit_with_barrier)
transpiled_circuit.draw("mpl", scale=0.8)
Cara kerja stratifikasi
Boxing pass manager menjalankan dua pass utama secara berurutan:
-
GroupGatesIntoBoxes— Menstratifikasi gate entangling ke dalam box. Gate single-qubit diabaikan pada tahap ini; hanya gate dua-qubit yang dikelompokkan. -
AbsorbSingleQubitGates— Menyerap setiap gate single-qubit ke box terdekat yang memenuhi syarat di sebelah kanannya.
Selama stratifikasi, GroupGatesIntoBoxes menelusuri DAG circuit dalam urutan topologis dan menempatkan setiap gate dua-qubit di grup paling awal yang qubit-nya belum terisi. Tiga jenis instruksi bertindak sebagai delimiter yang mem-flush grup saat ini untuk qubit-qubit mereka, memaksa gate berikutnya pada qubit tersebut masuk ke grup baru:
-
Barrier
-
Instruksi box yang sudah ada
-
Measurements
Gate single-qubit bukan delimiter — gate itu tidak berpengaruh pada pengelompokan dan diserahkan ke AbsorbSingleQubitGates untuk ditangani. Saat ini tidak ada analisis komutativitas yang dilakukan; pengelompokan mengikuti urutan topologis circuit apa adanya.
Mengapa remove_barriers="after_stratification" adalah default yang direkomendasikan
Mode default-nya adalah "after_stratification" karena menggabungkan dua keuntungan:
-
Barrier membatasi cara gate entangling dikelompokkan (barrier bertindak sebagai delimiter selama stratifikasi).
-
Barrier dihapus sebelum
AbsorbSingleQubitGatesdijalankan, sehingga gate single-qubit tetap bisa mengalir melewati bekas posisi barrier dan diserap ke box terdekat.
Sebaliknya:
-
"never"mempertahankan barrier sepanjang proses, yang menjebak gate single-qubit di antara barrier dan box, mencegah penyerapannya dan menyisakan rotasi tanpa twirl di circuit. -
"immediately"menghapus barrier sebelum stratifikasi, sehingga tidak berpengaruh pada pengelompokan. -
"finally"berguna kalau kamu ingin barrier ada di circuit output (misalnya untuk visualisasi), tetapi gate single-qubit di antara barrier dan box tidak diserap.
Box circuit ISA dengan barrier
Circuit dengan struktur berulang — seperti simulasi Hamiltonian Trotterized, ansatz variasional, dan urutan amplifikasi error — biasanya dibangun secara programatik lalu di-transpile ke bentuk ISA. Menambahkan box secara manual ke circuit yang sudah di-transpile itu merepotkan, jadi alur kerja yang natural adalah sebagai berikut:
-
Bangun circuit logis, dengan menyisipkan barrier di batas-batas layer yang ingin kamu jadikan batas box.
-
Transpile ke circuit ISA. Transpiler Qiskit mempertahankan barrier selama routing dan optimasi.
-
Jalankan
generate_boxing_pass_manager()pada circuit ISA.
Barrier dibutuhkan ketika ujung satu layer tidak mencakup semua qubit. Kalau tidak, algoritma greedy akan menempatkan gate dari layer berikutnya—pada qubit yang "bebas" tersebut—ke dalam grup yang sama. Sebagai contoh konkret, bayangkan simulasi Heisenberg ZZ dua langkah pada ring 5 qubit. Setiap langkah Trotter punya tiga layer bond: bond genap (0,1), (2,3), bond ganjil (1,2), (3,4), dan bond pembungkus (4,0). Setelah layer bond ganjil, qubit 0 dan 4 sama-sama bebas. Tanpa barrier, bond pembungkus (4,0) akan dikelompokkan secara greedy dengan bond dari langkah Trotter berikutnya yang memakai qubit bebas tersebut, sehingga gate dari langkah yang berbeda tercampur dalam satu box.
from qiskit.circuit import Parameter, QuantumCircuit
n = 5 # 5-qubit ring: bonds (0,1), (1,2), (2,3), (3,4), (4,0)
dt = Parameter("dt")
heisenberg = QuantumCircuit(n)
for step in range(2):
# Layer 1: even bonds (0,1) and (2,3)
for idx in range(0, n - 1, 2):
heisenberg.rzz(dt, idx, idx + 1)
heisenberg.barrier()
# Layer 2: odd bonds (1,2) and (3,4) -- qubits 0 and 4 are now free
for idx in range(1, n - 1, 2):
heisenberg.rzz(dt, idx, idx + 1)
heisenberg.barrier()
# Layer 3: wrap bond (4,0) -- both qubits were free after layer 2
heisenberg.rzz(dt, n - 1, 0)
if step < 1:
heisenberg.barrier() # separates the two Trotter steps
heisenberg.measure_all()
heisenberg.draw("mpl", scale=0.7)
Kode berikut men-transpile circuit ke bentuk ISA, dengan asumsi perangkat yang mendasarinya punya CX sebagai entangler native. Perhatikan bahwa barrier tetap bertahan setelah transpilasi dan masih terlihat di circuit hasilnya.
from qiskit.transpiler import generate_preset_pass_manager
preset_pm = generate_preset_pass_manager(
basis_gates=["rz", "sx", "cx"],
coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]],
optimization_level=1,
)
isa_circuit = preset_pm.run(heisenberg)
isa_circuit.draw("mpl", scale=0.5, fold=200)
Selanjutnya, terapkan boxing pass manager ke circuit ISA. Masing-masing dari tiga layer bond menjadi box tersendiri di setiap langkah Trotter.
boxing_pm = generate_boxing_pass_manager()
boxed_isa = boxing_pm.run(isa_circuit)
boxed_isa.draw("mpl", scale=0.5, fold=100)
Menggabungkan pass manager Samplomatic ke preset pass manager Qiskit
Kode di bawah ini menunjukkan cara menggabungkan generate_boxing_pass_manager() ke dalam preset pass manager Qiskit. Circuit hasil transpilasi berbentuk ISA dan berisi box.
from qiskit.transpiler import generate_preset_pass_manager
preset_pass_manager = generate_preset_pass_manager(
basis_gates=["rz", "sx", "cx"],
coupling_map=[[0, 1], [1, 2]],
optimization_level=0,
)
boxing_pass_manager = generate_boxing_pass_manager()
# Run the boxing pass manager after the scheduling stage
preset_pass_manager.post_scheduling = boxing_pass_manager
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(0, 2)
circuit.measure_all()
transpiled_circuit = preset_pass_manager.run(circuit)
transpiled_circuit.draw("mpl", scale=0.8)
Pola ini juga cocok digabungkan dengan alur kerja boxing ISA berbasis barrier. Boxing pass manager berjalan sebagai langkah pasca-penjadwalan, setelah barrier dari circuit logis asli bertahan melewati transpilasi, sehingga barrier tersebut memandu stratifikasi sesuai harapan.
preset_pm = generate_preset_pass_manager(
basis_gates=["rz", "sx", "cx"],
coupling_map=[[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]],
optimization_level=1,
)
preset_pm.post_scheduling = generate_boxing_pass_manager()
boxed_heisenberg = preset_pm.run(heisenberg)
boxed_heisenberg.draw("mpl", scale=0.5, fold=100)
Memeriksa dan memodifikasi anotasi
Setelah auto-boxing, kamu mungkin ingin memeriksa anotasi pada box tertentu atau menyesuaikannya. Misalnya, untuk menambahkan injeksi noise, menghapus anotasi, atau mengubah grup twirling. Samplomatic menyediakan sejumlah fungsi utilitas di samplomatic.utils untuk keperluan ini. Semuanya mengembalikan circuit baru tanpa mengubah yang asli.
extend_annotations() menambahkan satu atau beberapa anotasi ke setiap box di circuit.
from samplomatic import InjectNoise, Twirl
from samplomatic.utils import extend_annotations
boxed = generate_boxing_pass_manager().run(circuit_with_barrier)
# Add InjectNoise to every box
with_noise = extend_annotations(boxed, InjectNoise("my_noise_ref"))
with_noise.draw("mpl", scale=0.8)
filter_annotations() hanya mempertahankan anotasi dengan tipe tertentu dan membuang sisanya. replace_annotations() menerapkan sebuah callable ke setiap anotasi; callable tersebut mengembalikan daftar anotasi sebagai penggantinya (kembalikan [] untuk menghapus, daftar berelemen tunggal untuk mengganti, atau daftar berelemen banyak untuk memperluas).
from samplomatic.utils import filter_annotations, replace_annotations
# Keep only Twirl annotations, drop everything else
twirl_only = filter_annotations(with_noise, Twirl)
# Replace the twirling group on every Twirl annotation
updated = replace_annotations(
twirl_only, lambda a: [Twirl(group="local_c1")] if isinstance(a, Twirl) else [a]
)
Fungsi serbaguna map_annotations() menerima callable apa pun yang memetakan daftar anotasi ke daftar baru, sehingga kamu punya kendali penuh atas transformasi per box. strip_annotations() adalah wrapper praktis yang menghapus semua anotasi dari setiap box.
Untuk perubahan massal seperti menambahkan InjectNoise ke semua box gate, parameter inject_noise_targets dari generate_boxing_pass_manager() adalah opsi yang paling ringkas. Utilitas ini berguna untuk edit selektif per box yang tidak dicakup oleh parameter pass manager.
Membangun circuit
Setiap pass manager yang dihasilkan oleh generate_boxing_pass_manager() mengembalikan circuit yang bisa diubah dengan sukses menjadi pasangan template/samplex oleh build(). Misalnya, kode berikut memanggil fungsi build() pada circuit yang dihasilkan oleh boxing pass manager.
from samplomatic import build
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit)
template, samplex = build(transpiled_circuit)
Agar circuit hasil transpilasi apa pun bisa dibangun dengan sukses, pass manager tahu cara menyertakan box tambahan saat diperlukan. Misalnya, perhatikan circuit di bawah ini, yang berakhir dengan qubit 0 yang tidak diukur.
circuit_with_unmeasured_qubit = QuantumCircuit(4, 3)
circuit_with_unmeasured_qubit.cz(0, 1)
circuit_with_unmeasured_qubit.cz(2, 3)
for qubit in range(4):
circuit_with_unmeasured_qubit.rz(Parameter(f"th_{qubit}"), qubit)
circuit_with_unmeasured_qubit.rx(Parameter(f"phi_{qubit}"), qubit)
circuit_with_unmeasured_qubit.rz(Parameter(f"lam_{qubit}"), qubit)
circuit_with_unmeasured_qubit.measure(range(1, 4), range(3))
circuit_with_unmeasured_qubit.draw("mpl", scale=0.8)
Menggambar box left-dressed di sekitar gate dan pengukuran akan menghasilkan circuit dengan virtual gate yang tidak terkumpul pada qubit 0, dan memanggil build() pada circuit ini akan menimbulkan error. Untuk menghindarinya, pass manager yang dikembalikan oleh generate_boxing_pass_manager() bisa menambahkan box right-dressed yang bertindak sebagai pengumpul. Misalnya, pada kode berikut, qubit 0 diakhiri dengan box right-dressed yang menampung virtual gate yang belum terkumpul. Gate single-qubit yang bekerja pada qubit 0 juga ditempatkan di dalam box agar kedalaman circuit hasilnya sekecil mungkin.
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)
transpiled_circuit = boxing_pass_manager.run(circuit_with_unmeasured_qubit)
transpiled_circuit.draw("mpl", scale=0.8)
Pada contoh lain, sebuah box right-dressed ditambahkan untuk mengumpulkan virtual gate yang kalau tidak akan tetap tidak terkumpul akibat pengukuran yang tidak di-box.
boxing_pass_manager = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=False,
)
transpiled_circuit = boxing_pass_manager.run(circuit_with_unmeasured_qubit)
transpiled_circuit.draw("mpl", scale=0.8)