Přeskočit na hlavní obsah

Napiš vlastní průchod Transpileru

Verze balíčků

Kód na této stránce byl vyvinut s použitím následujících požadavků. Doporučujeme používat tyto verze nebo novější.

qiskit[all]~=2.5.2

Qiskit SDK ti umožňuje vytvářet vlastní transpilační passy a spouštět je v objektu PassManager nebo je přidat do StagedPassManager. Zde si ukážeme, jak napsat transpiler pass, se zaměřením na vytvoření passu, který provádí Pauli twirling na zašuměných kvantových hradlech v kvantovém obvodu. Tento příklad používá DAG, což je objekt, se kterým manipuluje typ passu TransformationPass.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit
Pozadí: reprezentace DAG

Než začneš sestavovat pass, je důležité představit vnitřní reprezentaci kvantových obvodů v Qiskitu, orientovaný acyklický graf (DAG) (přehled najdeš v tomto tutoriálu). Pro provedení těchto kroků nainstaluj knihovnu graphviz pro funkce vykreslování DAG.

V Qiskitu jsou obvody v rámci fází transpilace reprezentovány pomocí DAG. Obecně se DAG skládá z vrcholů (nazývaných také „uzly“) a orientovaných hran, které spojují dvojice vrcholů v určité orientaci. Tato reprezentace je uložena pomocí objektů qiskit.dagcircuit.DAGCircuit, které se skládají z jednotlivých objektů DagNode. Výhodou této reprezentace oproti čistému seznamu hradel (tj. netlistu) je, že tok informací mezi operacemi je explicitní, což usnadňuje rozhodování o transformacích.

Tento příklad ilustruje DAG vytvořením jednoduchého Circuit, který připraví Bellův stav a aplikuje rotaci RZR_Z v závislosti na výsledku měření.

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])
with qc.if_test((cr, 2)):
qc.rz(np.pi/2, qr[1])
qc.draw(output='mpl')

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

Pomocí funkce qiskit.tools.visualization.dag_drawer() zobrazíš DAG tohoto obvodu. Existují tři druhy uzlů grafu: uzly qubit/clbit (zelené), operační uzly (modré) a výstupní uzly (červené). Každá hrana označuje tok dat (nebo závislost) mezi dvěma uzly.

from qiskit.converters import circuit_to_dag
from qiskit.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.

Průchody Transpileru​

Průchody Transpileru jsou klasifikovány buď jako AnalysisPass, nebo jako TransformationPass. Průchody obecně pracují s DAG a property_set – slovníkovým objektem pro ukládání vlastností zjištěných analytickými průchody. Analytické průchody pracují jak s DAG, tak s jeho property_set. Nemohou DAG upravovat, ale mohou měnit property_set. To je v kontrastu s transformačními průchody, které DAG upravují a mohou property_set číst (ale ne zapisovat). Transformační průchody například překládají Circuit do jeho ISA nebo provádějí průchody směrování, které vkládají Gate SWAP tam, kde je to potřeba.

Vytvoření průchodu Transpileru PauliTwirl​

Následující příklad vytvoří průchod Transpileru, který přidává Pauliho twirls. Pauliho twirling je strategie potlačování chyb, která náhodně mění způsob, jakým Qubity procházejí hlučnými kanály – v tomto příkladu předpokládáme, že jde o dvoukubitové Gate (protože jsou mnohem náchylnější k chybám než jednokubitové Gate). Pauliho twirls neovlivňují operaci dvoukubitových Gate. Jsou zvoleny tak, aby ty aplikované před dvoukubitovým Gate (vlevo) byly vyváženy těmi aplikovanými za dvoukubitovým Gate (vpravo). V tomto smyslu jsou dvoukubitové operace identické, ale způsob jejich provedení je odlišný. Jednou z výhod Pauliho twirling je to, že mění koherentní chyby na stochastické, které lze zlepšit průměrováním více výsledků.

Průchody Transpileru pracují s DAG, takže nejdůležitější metodou, kterou je třeba přepsat, je .run(), která přijímá DAG jako vstup. Inicializace párů Pauliů, jak je znázorněno, zachovává operaci každého dvoukubitového Gate. To se provádí pomocnou metodou build_twirl_set, která prochází každý dvoukubitový Pauli (získaný z pauli_basis(2)) a hledá jiný Pauli, který operaci zachovává.

Z DAG lze pomocí metody op_nodes() vrátit všechny jeho uzly. DAG lze také použít ke shromažďování sekvencí – jde o posloupnosti uzlů, které nepřerušeně běží na Qubitu. Ty lze sbírat jako jednokubitové sekvence pomocí collect_1q_runs, dvoukubitové sekvence pomocí collect_2q_runs a sekvence uzlů, jejichž názvy instrukcí jsou v seznamu názvů, pomocí collect_runs. DAGCircuit má mnoho metod pro prohledávání a procházení grafu. Jedna běžně používaná metoda je topological_op_nodes, která poskytuje uzly v pořadí závislostí. Jiné metody, jako bfs_successors, se používají především ke zjištění, jak uzly interagují s následnými operacemi v DAG.

V příkladu chceme nahradit každý uzel, reprezentující instrukci, podgrafem vytvořeným jako mini DAG. Mini DAG má přidaný dvoukubitový kvantový registr. Operace se do mini DAG přidávají pomocí apply_operation_back, která umísťuje Instruction na výstup mini DAG (zatímco apply_operation_front by ho umístil na vstup mini DAG). Uzel je poté nahrazen mini DAG pomocí substitute_node_with_dag a proces pokračuje přes každý výskyt CXGate a ECRGate v DAG (odpovídající dvoukubitovým základním Gate na Backend IBM®).

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

Použití průchodu Transpileru PauliTwirl​

Následující kód používá výše vytvořený průchod k transpilaci Circuit. Uvažujme jednoduchý Circuit s Gate cx a ecr.

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

Výstup předchozí buňky kódu

Chceš-li použít vlastní průchod, vytvoř správce průchodů pomocí průchodu PauliTwirl a spusť ho na 50 Circuit.

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

Každý dvoukubitový Gate je nyní obklopen dvěma Paulimy.

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

Výstup předchozí buňky kódu

Operátory jsou stejné, pokud se použije Operator z qiskit.quantum_info:

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

Další kroky​

Doporučení