Isulat ang custom na transpiler pass
Mga bersyon ng package
Ang code sa pahinang ito ay ginawa gamit ang mga sumusunod na kinakailangan. Inirerekomenda naming gamitin ang mga bersyong ito o mas bago.
qiskit[all]~=2.3.0
Pinapayagan ka ng Qiskit SDK na lumikha ng custom na transpilation passes at patakbuhin ang mga ito sa PassManager object o idagdag ang mga ito sa isang StagedPassManager. Ipapakita namin dito kung paano sumulat ng transpiler pass, na nakatutok sa pagbuo ng pass na nagsasagawa ng Pauli twirling sa mga maingay na quantum gates sa isang quantum circuit. Ginagamit ng halimbawang ito ang DAG, na siyang object na pinamamahalaan ng uri ng pass na TransformationPass.
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
Background: DAG representation
Bago bumuo ng pass, mahalagang ipakilala ang panloob na representasyon ng mga quantum circuit sa Qiskit, ang directed acyclic graph (DAG) (tingnan ang tutorial na ito para sa pangkalahatang-ideya). Para sundin ang mga hakbang na ito, i-install ang graphviz library para sa mga DAG plotting function.
Sa Qiskit, sa loob ng mga transpilation stage, ang mga circuit ay kinakatawan gamit ang DAG. Sa pangkalahatan, ang isang DAG ay binubuo ng mga vertices (kilala rin bilang "nodes") at mga direktibong edges na nag-uugnay ng mga pares ng vertices sa isang partikular na oryentasyon. Ang representasyong ito ay nakaimbak gamit ang mga qiskit.dagcircuit.DAGCircuit object na binubuo ng mga indibidwal na DagNode object. Ang kalamangan ng representasyong ito kumpara sa isang purong listahan ng mga gates (ibig sabihin, isang netlist) ay ang daloy ng impormasyon sa pagitan ng mga operasyon ay malinaw, na nagpapadali ng paggawa ng mga desisyon sa transformation.
Ipinapakita ng halimbawang ito ang DAG sa pamamagitan ng paglikha ng isang simpleng circuit na naghahanda ng Bell state at nag-aaplay ng rotation, depende sa resulta ng pagsukat.
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')
Gamitin ang function na qiskit.tools.visualization.dag_drawer() para makita ang DAG ng circuit na ito. May tatlong uri ng graph nodes: qubit/clbit nodes (berde), operation nodes (asul), at output nodes (pula). Ang bawat edge ay nagpapahiwatig ng daloy ng data (o dependency) sa pagitan ng dalawang nodes.
from qiskit.converters import circuit_to_dag
from qiskit.tools.visualization import dag_drawer
dag = circuit_to_dag(qc)
dag_drawer(dag)

Mga transpiler pass​
Ang mga transpiler pass ay inuuri bilang isang AnalysisPass o isang TransformationPass. Ang mga pass sa pangkalahatan ay gumagana sa DAG at ang property_set, isang dictionary-like object para sa pag-iimbak ng mga katangiang tinutukoy ng mga analysis pass. Ang mga analysis pass ay gumagana sa parehong DAG at ang property_set nito. Hindi nila mababago ang DAG, ngunit maaari nilang baguhin ang property_set. Ito ay kakaibang sa mga transformation pass, na binabago ang DAG, at maaaring basahin (ngunit hindi magsulat) ang property_set. Halimbawa, ang mga transformation pass ay nagsasalin ng circuit sa ISA nito o nagsasagawa ng routing passes para mag-insert ng SWAP gates kung saan kinakailangan.
Lumikha ng PauliTwirl na transpiler pass​
Ang sumusunod na halimbawa ay nagtatayo ng transpiler pass na nagdaragdag ng mga Pauli twirl. Ang Pauli twirling ay isang estratehiya sa pagsugpo ng error na nagra-randomize ng paraan kung paano naranasan ng mga qubit ang mga maingay na channel, na ipinapalagay naming mga two-qubit gates sa halimbawang ito (dahil mas madaling magkamali kumpara sa mga single-qubit gates). Hindi naapektuhan ng mga Pauli twirl ang operasyon ng mga two-qubit gates. Pinili ang mga ito upang ang mga inilapat bago ang two-qubit gate (sa kaliwa) ay makontra ng mga inilapat pagkatapos ng two-qubit gate (sa kanan). Sa ganitong paraan, magkapareho ang mga two-qubit operation, ngunit iba ang paraan ng pagganap ng mga ito. Isa sa mga benepisyo ng Pauli twirling ay ginagawa nitong stochastic errors ang mga coherent errors, na maaaring mapabuti sa pamamagitan ng mas maraming pag-average.
Ang mga transpiler pass ay kumikilos sa DAG, kaya ang mahalagang method na i-override ay ang .run(), na tumatanggap ng DAG bilang input. Ang pag-initialize ng mga pares ng Paulis tulad ng ipinakita ay nagpapanatili ng operasyon ng bawat two-qubit gate. Ito ay ginagawa gamit ang helper method na build_twirl_set, na dumadaan sa bawat two-qubit Pauli (tulad ng nakuha mula sa pauli_basis(2)) at hinahanap ang iba pang Pauli na nagpapanatili ng operasyon.
Mula sa DAG, gamitin ang op_nodes() method para ibalik ang lahat ng nodes nito. Maaari rin gamitin ang DAG para mangolekta ng mga run, na mga sequence ng nodes na tumatakbo nang walang putol sa isang qubit. Maaaring makolekta ang mga ito bilang single-qubit runs na may collect_1q_runs, two-qubit runs na may collect_2q_runs, at mga run ng nodes kung saan ang mga pangalan ng instruksyon ay nasa namelist na may collect_runs. Ang DAGCircuit ay may maraming method para sa paghahanap at pag-traverse ng graph. Isang karaniwang ginagamit na method ay ang topological_op_nodes, na nagbibigay ng mga nodes sa isang dependency ordering. Ang iba pang mga method tulad ng bfs_successors ay pangunahing ginagamit upang matukoy kung paano nakikipag-ugnayan ang mga nodes sa mga kasunod na operasyon sa isang DAG.
Sa halimbawa, gusto naming palitan ang bawat node, na kumakatawan sa isang instruksyon, ng isang subcircuit na itinayo bilang isang mini DAG. Ang mini DAG ay may two-qubit quantum register na idinagdag dito. Ang mga operasyon ay idinaragdag sa mini DAG sa pamamagitan ng paggamit ng apply_operation_back, na naglalagay ng Instruction sa output ng mini DAG (samantalang ang apply_operation_front ay maglalagay nito sa input ng mini DAG). Ang node ay pinapalitan ng mini DAG sa pamamagitan ng paggamit ng substitute_node_with_dag, at ang proseso ay nagpapatuloy sa bawat instance ng CXGate at ECRGate sa DAG (katumbas ng mga two-qubit basis gates sa mga 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
Gamitin ang PauliTwirl na transpiler pass​
Ginagamit ng sumusunod na code ang pass na nilikha sa itaas para i-transpile ang isang circuit. Isaalang-alang ang isang simpleng circuit na may cx at ecr gates.
qc = QuantumCircuit(3)
qc.cx(0, 1)
qc.ecr(1, 2)
qc.ecr(1, 0)
qc.cx(2, 1)
qc.draw("mpl")
Para ilapat ang custom pass, bumuo ng pass manager gamit ang PauliTwirl pass at patakbuhin ito sa 50 circuits.
pm = PassManager([PauliTwirl()])
twirled_qcs = [pm.run(qc) for _ in range(50)]
Ang bawat two-qubit gate ay nakabalot na ngayon sa pagitan ng dalawang Paulis.
twirled_qcs[-1].draw("mpl")
Magkapareho ang mga operator kung gagamitin ang Operator mula sa qiskit.quantum_info:
np.all([Operator(twirled_qc).equiv(qc) for twirled_qc in twirled_qcs])
np.True_
Mga susunod na hakbang​
- Para matuto kung paano gamitin ang function na
generate_preset_passmanagersa halip na sumulat ng sariling mga pass, magsimula sa paksa ng Transpilation default settings and configuration options. - Subukan ang gabay na Compare transpiler settings.
- Suriin ang transpiler API documentation.