ข้ามไปยังเนื้อหาหลัก

เขียน custom transpiler pass

เวอร์ชันของแพ็กเกจ

โค้ดในหน้านี้พัฒนาโดยใช้ requirements ต่อไปนี้ แนะนำให้ใช้เวอร์ชันเหล่านี้หรือใหม่กว่า

qiskit[all]~=2.3.0

Qiskit SDK ให้สร้าง custom transpilation pass และรันใน PassManager หรือเพิ่มใน StagedPassManager ได้ ที่นี่จะสาธิตวิธีเขียน transpiler pass โดยเน้นที่การสร้าง pass ที่ดำเนินการ Pauli twirling บน Gate ควอนตัมที่มีสัญญาณรบกวนใน Circuit ควอนตัม ตัวอย่างนี้ใช้ DAG ซึ่งเป็นออบเจกต์ที่ถูกจัดการโดย TransformationPass ประเภทของ pass

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit

พื้นฐาน: การแสดง DAG

ก่อนสร้าง pass สิ่งสำคัญคือต้องแนะนำการแสดงภายในของ Circuit ควอนตัมใน Qiskit ซึ่งก็คือ directed acyclic graph (DAG) (ดู บทช่วยสอนนี้ สำหรับภาพรวม) ในการทำตามขั้นตอนเหล่านี้ ให้ติดตั้ง ไลบรารี graphviz สำหรับฟังก์ชันการ plot DAG

ใน Qiskit ภายใน transpilation stage Circuit จะถูกแสดงโดยใช้ DAG โดยทั่วไป DAG ประกอบด้วย vertex (หรือที่เรียกว่า "node") และ edge แบบมีทิศทางที่เชื่อมต่อคู่ของ vertex ในทิศทางเฉพาะ การแสดงนี้จัดเก็บโดยใช้ออบเจกต์ qiskit.dagcircuit.DAGCircuit ที่ประกอบด้วยออบเจกต์ DagNode แต่ละตัว ข้อดีของการแสดงนี้เหนือรายการ Gate ล้วน ๆ (นั่นคือ netlist) คือการไหลของข้อมูลระหว่าง operation เป็นแบบ explicit ทำให้ง่ายต่อการตัดสินใจแปลง

ตัวอย่างนี้แสดง DAG โดยสร้าง Circuit ง่าย ๆ ที่เตรียม Bell state และใช้การหมุน RZR_Z ขึ้นอยู่กับผลการวัด

  from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
import numpy as np

qr = QuantumRegister(3, 'qr')
cr = ClassicalRegister(3, 'cr')
qc = QuantumCircuit(qr, cr)

qc.h(qr[0])
qc.cx(qr[0], qr[1])
qc.measure(qr[0], cr[0])
qc.rz(np.pi/2, qr[1]).c_if(cr, 2)
qc.draw(output='mpl')

Circuit preparing a Bell state and applying an R_Z rotation depending on the measurement outcome.

ใช้ฟังก์ชัน qiskit.tools.visualization.dag_drawer() เพื่อดู DAG ของ Circuit นี้ มี graph node สามประเภท: node qubit/clbit (สีเขียว), node operation (สีน้ำเงิน) และ node output (สีแดง) แต่ละ edge แสดงการไหลของข้อมูล (หรือ dependency) ระหว่าง node สองตัว

from qiskit.converters import circuit_to_dag
from qiskit.tools.visualization import dag_drawer

dag = circuit_to_dag(qc)
dag_drawer(dag)

The circuit's DAG consists of nodes that are connected by directional edges. It is a visual way to represent qubits or classical bits, the operations, and the way that data flows.

Transpiler pass

Transpiler pass จัดประเภทเป็น AnalysisPass หรือ TransformationPass Pass โดยทั่วไปทำงานกับ DAG และ property_set ซึ่งเป็นออบเจกต์คล้าย dictionary สำหรับจัดเก็บคุณสมบัติที่กำหนดโดย analysis pass Analysis pass ทำงานกับทั้ง DAG และ property_set ของมัน ไม่สามารถแก้ไข DAG ได้ แต่สามารถแก้ไข property_set ได้ ซึ่งต่างจาก transformation pass ที่แก้ไข DAG ได้ และอ่าน (แต่เขียนไม่ได้) property_set ตัวอย่างเช่น transformation pass แปล Circuit เป็น ISA หรือดำเนินการ routing pass เพื่อแทรก SWAP Gate ตามความจำเป็น

สร้าง PauliTwirl transpiler pass

ตัวอย่างต่อไปนี้สร้าง transpiler pass ที่เพิ่ม Pauli twirl Pauli twirling เป็นกลยุทธ์การระงับข้อผิดพลาดที่ทำให้การที่ qubit สัมผัสกับ channel ที่มีสัญญาณรบกวนเป็นแบบสุ่ม ซึ่งในตัวอย่างนี้สันนิษฐานว่าเป็น Gate สองคิวบิต (เพราะมีโอกาสเกิดข้อผิดพลาดสูงกว่า Gate คิวบิตเดี่ยวมาก) Pauli twirl ไม่ส่งผลต่อการทำงานของ Gate สองคิวบิต โดยถูกเลือกให้ Pauli ที่ใช้ ก่อน Gate สองคิวบิต (ทางซ้าย) ถูกยกเลิกโดย Pauli ที่ใช้ หลัง Gate สองคิวบิต (ทางขวา) ในแง่นี้ operation สองคิวบิตเหมือนกัน แต่วิธีดำเนินการต่างกัน ประโยชน์อย่างหนึ่งของ Pauli twirling คือเปลี่ยน coherent error เป็น stochastic error ซึ่งสามารถปรับปรุงได้โดยการเฉลี่ยมากขึ้น

Transpiler pass ทำงานบน DAG ดังนั้นเมธอดสำคัญที่ต้อง override คือ .run() ซึ่งรับ DAG เป็น input การ initialize คู่ของ Pauli ตามที่แสดงจะรักษาการทำงานของ Gate สองคิวบิตแต่ละตัว ทำได้ด้วย helper method build_twirl_set ที่วนผ่าน Pauli สองคิวบิตแต่ละตัว (ที่ได้จาก pauli_basis(2)) และหา Pauli อื่นที่รักษาการทำงาน

จาก DAG ใช้เมธอด op_nodes() เพื่อส่งคืน node ทั้งหมด DAG ยังใช้รวบรวม run ได้ ซึ่งเป็นลำดับของ node ที่รันต่อเนื่องบน qubit รวบรวมเป็น single-qubit run ด้วย collect_1q_runs, two-qubit run ด้วย collect_2q_runs และ run ของ node ที่ชื่อ instruction อยู่ใน namelist ด้วย collect_runs DAGCircuit มีเมธอดมากมายสำหรับค้นหาและ traverse graph เมธอดที่ใช้บ่อยคือ topological_op_nodes ที่ให้ node ตามลำดับ dependency เมธอดอื่น เช่น bfs_successors ใช้เพื่อตรวจสอบว่า node โต้ตอบกับ operation ที่ตามมาบน DAG อย่างไร

ในตัวอย่าง ต้องการแทนที่ node แต่ละตัวที่แสดง instruction ด้วย subcircuit ที่สร้างเป็น mini DAG mini DAG มี quantum register สองคิวบิตที่เพิ่มเข้าไป เพิ่ม operation ไปยัง mini DAG โดยใช้ apply_operation_back ซึ่งวาง Instruction บน output ของ mini DAG (ส่วน apply_operation_front จะวางบน input ของ mini DAG) จากนั้นแทนที่ node ด้วย mini DAG โดยใช้ substitute_node_with_dag และกระบวนการดำเนินต่อสำหรับแต่ละ instance ของ CXGate และ ECRGate ใน DAG (ซึ่งสอดคล้องกับ basis Gate สองคิวบิตบน IBM® Backend)

from qiskit.dagcircuit import DAGCircuit
from qiskit.circuit import QuantumCircuit, QuantumRegister, Gate
from qiskit.circuit.library import CXGate, ECRGate
from qiskit.transpiler import PassManager
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.quantum_info import Operator, pauli_basis

import numpy as np

from typing import Iterable, Optional
class PauliTwirl(TransformationPass):
"""Add Pauli twirls to two-qubit gates."""

def __init__(
self,
gates_to_twirl: Optional[Iterable[Gate]] = None,
):
"""
Args:
gates_to_twirl: Names of gates to twirl. The default behavior is to twirl all
two-qubit basis gates, `cx` and `ecr` for IBM backends.
"""
if gates_to_twirl is None:
gates_to_twirl = [CXGate(), ECRGate()]
self.gates_to_twirl = gates_to_twirl
self.build_twirl_set()
super().__init__()

def build_twirl_set(self):
"""
Build a set of Paulis to twirl for each gate and store internally as .twirl_set.
"""
self.twirl_set = {}

# iterate through gates to be twirled
for twirl_gate in self.gates_to_twirl:
twirl_list = []

# iterate through Paulis on left of gate to twirl
for pauli_left in pauli_basis(2):
# iterate through Paulis on right of gate to twirl
for pauli_right in pauli_basis(2):
# save pairs that produce identical operation as gate to twirl
if (Operator(pauli_left) @ Operator(twirl_gate)).equiv(
Operator(twirl_gate) @ pauli_right
):
twirl_list.append((pauli_left, pauli_right))

self.twirl_set[twirl_gate.name] = twirl_list

def run(
self,
dag: DAGCircuit,
) -> DAGCircuit:
# collect all nodes in DAG and proceed if it is to be twirled
twirling_gate_classes = tuple(
gate.base_class for gate in self.gates_to_twirl
)
for node in dag.op_nodes():
if not isinstance(node.op, twirling_gate_classes):
continue

# random integer to select Pauli twirl pair
pauli_index = np.random.randint(
0, len(self.twirl_set[node.op.name])
)
twirl_pair = self.twirl_set[node.op.name][pauli_index]

# instantiate mini_dag and attach quantum register
mini_dag = DAGCircuit()
register = QuantumRegister(2)
mini_dag.add_qreg(register)

# apply left Pauli, gate to twirl, and right Pauli to empty mini-DAG
mini_dag.apply_operation_back(
twirl_pair[0].to_instruction(), [register[0], register[1]]
)
mini_dag.apply_operation_back(node.op, [register[0], register[1]])
mini_dag.apply_operation_back(
twirl_pair[1].to_instruction(), [register[0], register[1]]
)

# substitute gate to twirl node with twirling mini-DAG
dag.substitute_node_with_dag(node, mini_dag)

return dag

ใช้งาน PauliTwirl transpiler pass

โค้ดต่อไปนี้ใช้ pass ที่สร้างไว้ข้างต้นเพื่อ transpile Circuit ลองพิจารณา Circuit ง่าย ๆ ที่มี cx และ ecr Gate

qc = QuantumCircuit(3)
qc.cx(0, 1)
qc.ecr(1, 2)
qc.ecr(1, 0)
qc.cx(2, 1)
qc.draw("mpl")

Output of the previous code cell

ในการใช้ custom pass สร้าง pass manager โดยใช้ PauliTwirl pass และรันบน 50 Circuit

pm = PassManager([PauliTwirl()])
twirled_qcs = [pm.run(qc) for _ in range(50)]

Gate สองคิวบิตแต่ละตัวถูกคร่อมระหว่าง Pauli สองตัวแล้ว

twirled_qcs[-1].draw("mpl")

Output of the previous code cell

operator เหมือนกันถ้าใช้ Operator จาก qiskit.quantum_info:

np.all([Operator(twirled_qc).equiv(qc) for twirled_qc in twirled_qcs])
np.True_

ขั้นตอนถัดไป

คำแนะนำ
Source: IBM Quantum docs — updated 27 เม.ย. 2569
English version on doQumentation — updated 7 พ.ค. 2569
This translation based on the English version of 11 มี.ค. 2569