Bekerja dengan DAG dalam transpiler pass
Dalam Qiskit, di dalam tahapan transpilasi, Circuit direpresentasikan menggunakan DAG. Secara umum, DAG terdiri dari simpul (dikenal juga sebagai "node") dan sisi berarah yang menghubungkan pasangan simpul dalam orientasi tertentu. Representasi ini disimpan menggunakan objek qiskit.dagcircuit.DAGCircuit yang terdiri dari objek-objek DagNode individual. Keuntungan representasi ini dibandingkan daftar Gate murni (yaitu netlist) adalah bahwa aliran informasi antar operasi menjadi eksplisit, sehingga lebih mudah untuk membuat keputusan transformasi.
Panduan ini mendemonstrasikan cara bekerja dengan DAG dan menggunakannya untuk menulis transpiler pass kustom. Panduan dimulai dengan membangun Circuit sederhana dan memeriksa representasi DAG-nya, kemudian mengeksplorasi operasi DAG dasar dan mengimplementasikan pass BasicMapper kustom.
Bangun Circuit dan periksa DAG-nyaβ
Cuplikan kode di bawah ini mengilustrasikan DAG dengan membuat Circuit sederhana yang mempersiapkan Bell state dan menerapkan rotasi , bergantung pada hasil pengukuran.
Versi paket
Kode di halaman ini dikembangkan menggunakan persyaratan berikut. Kami menyarankan menggunakan versi ini atau yang lebih baru.
qiskit[all]~=2.3.0
# Added by doQumentation β required packages for this notebook
!pip install -q qiskit
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.converters import circuit_to_dag
from qiskit.visualization import circuit_drawer
from qiskit.visualization.dag_visualization import dag_drawer
# Create circuit
q = QuantumRegister(3, "q")
c = ClassicalRegister(3, "c")
circ = QuantumCircuit(q, c)
circ.h(q[0])
circ.cx(q[0], q[1])
circ.measure(q[0], c[0])
# Qiskit 2.0 uses if_test instead of c_if
with circ.if_test((c, 2)):
circ.rz(0.5, q[1])
circuit_drawer(circ, output="mpl")
Dalam DAG, ada tiga jenis node graf: node input Qubit/clbit (hijau), node operasi (biru), dan node output (merah). Setiap sisi menunjukkan aliran data (atau ketergantungan) antara dua node. Gunakan fungsi qiskit.tools.visualization.dag_drawer() untuk melihat DAG dari Circuit ini. (Install library Graphviz untuk menjalankannya.)
# Convert to DAG
dag = circuit_to_dag(circ)
dag_drawer(dag)

Operasi DAG dasarβ
Contoh kode di bawah ini mendemonstrasikan operasi umum dengan DAG, termasuk mengakses node, menambahkan operasi, dan mengganti subcircuit. Operasi-operasi ini membentuk fondasi untuk membangun transpiler pass.
Dapatkan semua node operasi dalam DAGβ
Metode op_nodes() mengembalikan daftar iterable dari objek DAGOpNode dalam Circuit:
dag.op_nodes()
[DAGOpNode(op=Instruction(name='h', num_qubits=1, num_clbits=0, params=[]), qargs=(<Qubit register=(3, "q"), index=0>,), cargs=()),
DAGOpNode(op=Instruction(name='cx', num_qubits=2, num_clbits=0, params=[]), qargs=(<Qubit register=(3, "q"), index=0>, <Qubit register=(3, "q"), index=1>), cargs=()),
DAGOpNode(op=Instruction(name='measure', num_qubits=1, num_clbits=1, params=[]), qargs=(<Qubit register=(3, "q"), index=0>,), cargs=(<Clbit register=(3, "c"), index=0>,)),
DAGOpNode(op=Instruction(name='if_else', num_qubits=1, num_clbits=3, params=[<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7f912f47db10>, None]), qargs=(<Qubit register=(3, "q"), index=1>,), cargs=(<Clbit register=(3, "c"), index=0>, <Clbit register=(3, "c"), index=1>, <Clbit register=(3, "c"), index=2>))]
Setiap node adalah instansi dari kelas DAGOpNode:
node = dag.op_nodes()[3]
print("node name:", node.name)
print("op:", node.op)
print("qargs:", node.qargs)
print("cargs:", node.cargs)
print("condition:", node.op.condition)
node name: if_else
op: Instruction(name='if_else', num_qubits=1, num_clbits=3, params=[<qiskit.circuit.quantumcircuit.QuantumCircuit object at 0x7f912f4ceed0>, None])
qargs: (<Qubit register=(3, "q"), index=1>,)
cargs: (<Clbit register=(3, "c"), index=0>, <Clbit register=(3, "c"), index=1>, <Clbit register=(3, "c"), index=2>)
condition: (ClassicalRegister(3, 'c'), 2)
Tambahkan operasi ke belakangβ
Sebuah operasi ditambahkan ke akhir DAGCircuit menggunakan metode apply_operation_back(). Ini menambahkan Gate yang ditentukan untuk bekerja pada Qubit yang diberikan setelah semua operasi yang ada dalam Circuit.
from qiskit.circuit.library import HGate
dag.apply_operation_back(HGate(), qargs=[q[0]])
dag_drawer(dag)

Tambahkan operasi ke depanβ
Sebuah operasi ditambahkan ke awal DAGCircuit menggunakan metode apply_operation_front(). Ini menyisipkan Gate yang ditentukan sebelum semua operasi yang ada dalam Circuit, sehingga menjadikannya operasi pertama yang dieksekusi.
from qiskit.circuit.library import CCXGate
dag.apply_operation_front(CCXGate(), qargs=[q[0], q[1], q[2]])
dag_drawer(dag)

Ganti node dengan subcircuitβ
Sebuah node yang merepresentasikan operasi tertentu dalam DAGCircuit diganti dengan subcircuit. Pertama, sub-DAG baru dibangun dengan urutan Gate yang diinginkan, kemudian node target disubstitusikan oleh sub-DAG ini menggunakan substitute_node_with_dag(), dengan tetap mempertahankan koneksi ke bagian Circuit lainnya.
from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit.library import CHGate, U2Gate, CXGate
# Build sub-DAG
mini_dag = DAGCircuit()
p = QuantumRegister(2, "p")
mini_dag.add_qreg(p)
mini_dag.apply_operation_back(CHGate(), qargs=[p[1], p[0]])
mini_dag.apply_operation_back(U2Gate(0.1, 0.2), qargs=[p[1]])
# Replace CX with mini_dag
cx_node = dag.op_nodes(op=CXGate).pop()
dag.substitute_node_with_dag(cx_node, mini_dag, wires=[p[0], p[1]])
dag_drawer(dag)

Setelah semua transformasi selesai, DAG bisa dikonversi kembali menjadi objek QuantumCircuit biasa. Inilah cara pipeline Transpiler bekerja. Sebuah Circuit diambil, diproses dalam bentuk DAG, dan Circuit yang sudah ditransformasi dihasilkan sebagai output.
from qiskit.converters import dag_to_circuit
new_circ = dag_to_circuit(dag)
circuit_drawer(new_circ, output="mpl")
Implementasikan pass BasicMapperβ
Struktur DAG bisa dimanfaatkan untuk menulis transpiler pass. Dalam contoh di bawah ini, sebuah pass BasicMapper diimplementasikan untuk memetakan Circuit sembarang ke perangkat dengan konektivitas Qubit yang terbatas. Untuk panduan tambahan, lihat panduan tentang menulis transpiler pass kustom.
Pass ini didefinisikan sebagai TransformationPass, artinya ia memodifikasi Circuit. Ia melakukannya dengan menelusuri DAG lapis per lapis, memeriksa apakah setiap instruksi memenuhi batasan yang diberlakukan oleh coupling map perangkat. Jika pelanggaran terdeteksi, jalur swap ditentukan dan Gate SWAP yang diperlukan disisipkan sesuai kebutuhan.
Saat membuat transpiler pass, keputusan pertama adalah memilih apakah pass harus mewarisi dari TransformationPass atau AnalysisPass. Transformation pass dirancang untuk memodifikasi Circuit, sedangkan analysis pass hanya dimaksudkan untuk mengekstrak informasi yang akan digunakan oleh pass berikutnya. Fungsionalitas utama kemudian diimplementasikan dalam metode run(dag). Terakhir, pass harus didaftarkan dalam modul qiskit.transpiler.passes.
Dalam pass ini secara khusus, DAG ditelusuri lapis per lapis (di mana setiap lapisan berisi operasi yang bekerja pada set Qubit yang terpisah sehingga bisa dieksekusi secara independen). Untuk setiap operasi, jika batasan coupling map tidak terpenuhi, jalur swap yang sesuai diidentifikasi, dan swap yang diperlukan disisipkan untuk membawa Qubit yang terlibat menjadi bersebelahan.
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler import Layout
from qiskit.circuit.library import SwapGate
class BasicSwap(TransformationPass):
def __init__(self, coupling_map, initial_layout=None):
super().__init__()
self.coupling_map = coupling_map
self.initial_layout = initial_layout
def run(self, dag):
new_dag = DAGCircuit()
for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)
if self.initial_layout is None:
self.initial_layout = Layout.generate_trivial_layout(
*dag.qregs.values()
)
current_layout = self.initial_layout.copy()
for layer in dag.serial_layers():
subdag = layer["graph"]
for gate in subdag.two_qubit_ops():
q0, q1 = gate.qargs
p0 = current_layout[q0]
p1 = current_layout[q1]
if self.coupling_map.distance(p0, p1) != 1:
path = self.coupling_map.shortest_undirected_path(p0, p1)
for i in range(len(path) - 2):
wire1, wire2 = path[i], path[i + 1]
qubit1 = current_layout[wire1]
qubit2 = current_layout[wire2]
new_dag.apply_operation_back(
SwapGate(), qargs=[qubit1, qubit2]
)
current_layout.swap(wire1, wire2)
new_dag.compose(
subdag, qubits=current_layout.reorder_bits(new_dag.qubits)
)
return new_dag
Pass ini sekarang bisa diuji pada Circuit contoh kecil. Sebuah pass manager dibangun dengan pass yang baru didefinisikan di dalamnya. Circuit contoh kemudian diberikan ke pass manager ini, dan Circuit baru yang sudah ditransformasi diperoleh sebagai output.
from qiskit.transpiler import CouplingMap, PassManager
from qiskit import QuantumRegister, QuantumCircuit
q = QuantumRegister(7, "q")
in_circ = QuantumCircuit(q)
in_circ.h(q[0])
in_circ.cx(q[0], q[4])
in_circ.cx(q[2], q[3])
in_circ.cx(q[6], q[1])
in_circ.cx(q[5], q[0])
in_circ.rz(0.1, q[2])
in_circ.cx(q[5], q[0])
coupling = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 5], [5, 6]]
coupling_map = CouplingMap(couplinglist=coupling)
pm = PassManager()
pm.append(BasicSwap(coupling_map))
out_circ = pm.run(in_circ)
in_circ.draw(output="mpl")
out_circ.draw(output="mpl")
Langkah selanjutnyaβ
- Tinjau panduan tentang membuat transpiler pass kustom
- Pelajari cara Membuat dan mentranspilasi terhadap Backend kustom
- Coba panduan Bandingkan pengaturan Transpiler.
- Tinjau Dokumentasi API DAG Circuit.