Přeskočit na hlavní obsah

Napište vlastní průchod Transpileru

Verze balíčků

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

qiskit[all]~=2.3.0

Qiskit SDK ti umožňuje vytvářet vlastní průchody transpilace a spouštět je v objektu PassManager nebo je přidávat do StagedPassManager. Zde si ukážeme, jak napsat průchod Transpileru, přičemž se zaměříme na vytvoření průchodu, který provádí Pauliho twirling na hlučných kvantových Gate v kvantovém Circuit. Tento příklad využívá DAG, což je objekt, se kterým pracuje typ průchodu TransformationPass.

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

Pozadí: reprezentace pomocí DAG

Před vytvořením průchodu je důležité představit si interní reprezentaci kvantových Circuit v Qiskitu – orientovaný acyklický graf (DAG) (přehled najdeš v tomto tutoriálu). Chceš-li postupovat krok za krokem, nainstaluj knihovnu graphviz pro funkce vykreslování DAG.

V Qiskitu jsou Circuit v průběhu fází transpilace reprezentovány pomocí DAG. Obecně se DAG skládá z vrcholů (také nazývaných „uzly") a orientovaných hran, které propojují dvojice vrcholů v určitém směru. 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 prostému seznamu Gate (tzv. netlista) je to, že tok informací mezi operacemi je explicitní, což usnadňuje rozhodování o transformacích.

Tento příklad ilustruje DAG tak, že vytvoří jednoduchý 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])
qc.rz(np.pi/2, qr[1]).c_if(cr, 2)
qc.draw(output='mpl')

Circuit připravující Bellův stav a aplikující rotaci R_Z v závislosti na výsledku měření.

Funkci qiskit.tools.visualization.dag_drawer() použij k zobrazení DAG tohoto Circuit. Existují tři druhy uzlů grafu: uzly Qubit/clbit (zelené), uzly operací (modré) a výstupní uzly (červené). Každá hrana znázorňuje tok dat (nebo závislost) mezi dvěma uzly.

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

dag = circuit_to_dag(qc)
dag_drawer(dag)

DAG Circuit se skládá z uzlů propojených orientovanými hranami. Jde o vizuální způsob, jak znázornit Qubity nebo klasické bity, operace a tok dat.

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í