Sari la conținutul principal

Scrie un pas de Transpiler personalizat

Versiuni de pachete

Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Recomandăm utilizarea acestor versiuni sau a unora mai noi.

qiskit[all]~=2.3.0

SDK-ul Qiskit îți permite să creezi pași de transpilare personalizați și să îi rulezi în obiectul PassManager sau să îi adaugi la un StagedPassManager. Aici vom demonstra cum să scriem un pas de Transpiler, concentrându-ne pe construirea unui pas care efectuează Pauli twirling pe porțile cuantice zgomotoase dintr-un Circuit cuantic. Acest exemplu folosește DAG-ul, care este obiectul manipulat de tipul de pas TransformationPass.

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

Context: reprezentarea DAG

Înainte de a construi un pas, este important să prezentăm reprezentarea internă a circuitelor cuantice în Qiskit, graful aciclic orientat (DAG) (consultă acest tutorial pentru o prezentare generală). Pentru a urma acești pași, instalează biblioteca graphviz pentru funcțiile de vizualizare a DAG-ului.

În Qiskit, în cadrul etapelor de transpilare, circuitele sunt reprezentate folosind un DAG. În general, un DAG este compus din vârfuri (cunoscute și ca „noduri") și muchii orientate care conectează perechi de vârfuri într-o orientare specifică. Această reprezentare este stocată folosind obiecte qiskit.dagcircuit.DAGCircuit compuse din obiecte individuale DagNode. Avantajul acestei reprezentări față de o listă pură de porți (adică un netlist) este că fluxul de informații între operații este explicit, facilitând luarea deciziilor de transformare.

Acest exemplu ilustrează DAG-ul prin crearea unui circuit simplu care pregătește o stare Bell și aplică o rotație RZR_Z, în funcție de rezultatul măsurătorii.

  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 care pregătește o stare Bell și aplică o rotație R_Z în funcție de rezultatul măsurătorii.

Folosește funcția qiskit.tools.visualization.dag_drawer() pentru a vizualiza DAG-ul acestui Circuit. Există trei tipuri de noduri în graf: noduri qubit/clbit (verde), noduri de operație (albastru) și noduri de ieșire (roșu). Fiecare muchie indică fluxul de date (sau dependența) dintre două noduri.

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

dag = circuit_to_dag(qc)
dag_drawer(dag)

DAG-ul circuitului este compus din noduri conectate prin muchii direcționale. Este o modalitate vizuală de a reprezenta qubiți sau biți clasici, operațiile și modul în care datele circulă.

Pașii Transpiler-ului

Pașii Transpiler-ului sunt clasificați fie ca AnalysisPass, fie ca TransformationPass. Pașii în general lucrează cu DAG-ul și property_set, un obiect asemănător unui dicționar pentru stocarea proprietăților determinate de pașii de analiză. Pașii de analiză lucrează atât cu DAG-ul, cât și cu property_set-ul acestuia. Ei nu pot modifica DAG-ul, dar pot modifica property_set-ul. Aceasta contrastează cu pașii de transformare, care modifică DAG-ul și pot citi (dar nu scrie în) property_set. De exemplu, pașii de transformare traduc un Circuit la ISA al său sau efectuează pași de rutare pentru a insera porți SWAP acolo unde este necesar.

Creează un pas de Transpiler PauliTwirl

Următorul exemplu construiește un pas de Transpiler care adaugă Pauli twirl-uri. Pauli twirling este o strategie de suprimare a erorilor care randomizează modul în care qubiții experimentează canalele zgomotoase, pe care le presupunem a fi porți cu doi qubiți în acest exemplu (deoarece sunt mult mai predispuse la erori decât porțile cu un singur Qubit). Pauli twirl-urile nu afectează operația porților cu doi qubiți. Ele sunt alese astfel încât cele aplicate înainte de poarta cu doi qubiți (la stânga) să fie contrabalansate de cele aplicate după poarta cu doi qubiți (la dreapta). În acest sens, operațiile cu doi qubiți sunt identice, dar modul în care sunt efectuate este diferit. Un beneficiu al Pauli twirling-ului este că transformă erorile coerente în erori stocastice, care pot fi îmbunătățite prin medierea mai multor rezultate.

Pașii Transpiler-ului acționează pe DAG, deci metoda importantă de suprascris este .run(), care primește DAG-ul ca intrare. Inițializarea perechilor de Pauli așa cum este arătat păstrează operația fiecărei porți cu doi qubiți. Aceasta se face cu metoda ajutătoare build_twirl_set, care parcurge fiecare Pauli cu doi qubiți (obținut din pauli_basis(2)) și găsește celălalt Pauli care păstrează operația.

Din DAG, folosește metoda op_nodes() pentru a returna toate nodurile sale. DAG-ul poate fi folosit și pentru a colecta rulări, care sunt secvențe de noduri ce rulează neîntrerupt pe un Qubit. Acestea pot fi colectate ca rulări cu un singur Qubit cu collect_1q_runs, rulări cu doi qubiți cu collect_2q_runs, și rulări de noduri unde numele instrucțiunilor se află într-o listă de nume cu collect_runs. DAGCircuit are multe metode pentru căutarea și traversarea unui graf. O metodă utilizată frecvent este topological_op_nodes, care oferă nodurile într-o ordine de dependență. Alte metode, cum ar fi bfs_successors, sunt folosite în principal pentru a determina cum interacționează nodurile cu operațiunile ulterioare dintr-un DAG.

În exemplu, vrem să înlocuim fiecare nod, reprezentând o instrucțiune, cu un subcircuit construit ca un mini DAG. Mini DAG-ul are un registru cuantic cu doi qubiți adăugat la el. Operațiile sunt adăugate la mini DAG folosind apply_operation_back, care plasează Instruction la ieșirea mini DAG-ului (în timp ce apply_operation_front l-ar plasa la intrarea mini DAG-ului). Nodul este apoi substituit de mini DAG folosind substitute_node_with_dag, iar procesul continuă pentru fiecare instanță de CXGate și ECRGate din DAG (corespunzând porților de bază cu doi qubiți de pe Backend-urile 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

Utilizează pasul de Transpiler PauliTwirl

Codul următor folosește pasul creat mai sus pentru a transpila un Circuit. Consideră un Circuit simplu cu porți cx și ecr.

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

Ieșirea celulei de cod anterioare

Pentru a aplica pasul personalizat, construiește un manager de pași folosind pasul PauliTwirl și rulează-l pe 50 de circuite.

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

Fiecare poartă cu doi qubiți este acum „sandwich-uită" între doi Pauli.

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

Ieșirea celulei de cod anterioare

Operatorii sunt identici dacă se folosește Operator din qiskit.quantum_info:

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

Pași următori

Recomandări