Sari la conținutul principal
Acest proiect este în versiune beta. Ai găsit o eroare? Deschide un issue pe GitHub. Ai o întrebare sau o idee? Întreabă în Discussions — ne-ar plăcea să aflăm părerea ta!
Cum este calitatea traducerii pe această pagină?

Scrie-ți propria trecere de Transpiler

Versiuni de pachete

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

qiskit[all]~=2.6.1

SDK-ul Qiskit îți permite să creezi treceri de transpilare personalizate și să le rulezi în obiectul PassManager sau să le adaugi la un StagedPassManager. Aici vom demonstra cum să scrii o trecere de transpiler, concentrându-ne pe construirea unei treceri care realizează Pauli twirling asupra porților cuantice zgomotoase dintr-un circuit cuantic. Acest exemplu folosește DAG-ul, care este obiectul manipulat de tipul de trecere TransformationPass.

Context: reprezentarea DAG

Înainte de a construi o trecere, este important să introducem reprezentarea internă a circuitelor cuantice în Qiskit, graful aciclic direcționat (DAG) (vezi acest tutorial pentru o prezentare generală). Pentru a urma acești pași, instalează biblioteca graphviz pentru funcțiile de reprezentare grafică 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 direcționate care conectează perechi de vârfuri într-o orientare anume. Această reprezentare este stocată folosind obiecte qiskit.dagcircuit.DAGCircuit, care sunt compuse din obiecte individuale DagNode. Avantajul acestei reprezentări față de o simplă listă de porți (adică o netlist) este că fluxul de informații între operații este explicit, ceea ce facilitează 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])
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.

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

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.

Trecerile Transpiler-ului​

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

Creează o trecere de Transpiler PauliTwirl​

Următorul exemplu construiește o trecere de Transpiler care adaugă Pauli twirl-uri. Pauli twirling este o strategie de suprimare a erorilor care randomizează modul în care qubiții sunt afectați de 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.

Trecerile 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țiile 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 înlocuit cu mini DAG-ul 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®).

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
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ă trecerea de Transpiler PauliTwirl​

Codul următor folosește trecerea 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 trecerea personalizată, construiește un manager de treceri folosind trecerea 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 încadrată între doi operatori 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