Qiskit-Addon-Hilfsprogramme
Paketversionen
Der Code auf dieser Seite wurde mit den folgenden Anforderungen entwickelt. Wir empfehlen, diese oder neuere Versionen zu verwenden.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-addon-utils~=0.3.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-utils qiskit-ibm-runtime
Das Paket Qiskit-Addon-Hilfsprogramme ist eine Sammlung von Funktionalitäten zur Ergänzung von Workflows, die ein oder mehrere Qiskit-Addons umfassen. Das Paket enthält beispielsweise Funktionen zum Erstellen von Hamiltonoperatoren, zum Generieren von Trotter-Zeitentwicklungs-Circuits und zum Aufteilen und Kombinieren von Quantum-Circuits.
Installation
Es gibt zwei Möglichkeiten, die Qiskit-Addon-Hilfsprogramme zu installieren: über PyPI oder durch Kompilieren aus dem Quellcode. Es wird empfohlen, diese Pakete in einer virtuellen Umgebung zu installieren, um eine Trennung der Paketabhängigkeiten sicherzustellen.
Installation über PyPI
Die einfachste Methode zur Installation des Pakets Qiskit-Addon-Hilfsprogramme ist über PyPI.
pip install 'qiskit-addon-utils'
Installation aus dem Quellcode
Hier klicken, um zu erfahren, wie du dieses Paket manuell installierst.
Wenn du zu diesem Paket beitragen möchtest oder es manuell installieren willst, klone zunächst das Repository:
git clone git@github.com:Qiskit/qiskit-addon-utils.git
und installiere das Paket über pip. Wenn du vorhast, die im Paket-Repository enthaltenen Tutorials auszuführen, installiere auch die Notebook-Abhängigkeiten. Wenn du im Repository entwickeln möchtest, installiere die dev-Abhängigkeiten.
pip install tox jupyterlab -e '.[notebook-dependencies,dev]'
Erste Schritte mit den Hilfsprogrammen
Das Paket qiskit-addon-utils enthält mehrere Module, darunter eines zur Problemgenerierung für die Simulation von Quantensystemen, eines zur Kantenfärbung für eine effizientere Gate-Platzierung in einem Quantum-Circuit sowie eines zum Circuit-Slicing, das bei der Operator-Rückpropagation helfen kann. Die folgenden Abschnitte fassen jedes Modul zusammen. Die API-Dokumentation des Pakets enthält ebenfalls hilfreiche Informationen.
Problemgenerierung
Der Inhalt des Moduls qiskit_addon_utils.problem_generators umfasst:
- Eine Funktion
generate_xyz_hamiltonian(), die eine konnektivitätsbewussteSparsePauliOp-Darstellung eines Ising-Typ-XYZ-Modells generiert:
- Eine Funktion
generate_time_evolution_circuit(), die einen Circuit konstruiert, der die Zeitentwicklung eines gegebenen Operators modelliert. - Drei verschiedene
PauliOrderStrategy-Objekte zur Aufzählung unterschiedlicher Pauli-String-Ordnungen. Dies ist besonders nützlich in Kombination mit der Kantenfärbung und kann als Argument sowohl ingenerate_xyz_hamiltonian()als auch ingenerate_time_evolution_circuit()verwendet werden.
Kantenfärbung
Das Modul qiskit_addon_utils.coloring wird verwendet, um die Kanten einer Coupling Map einzufärben und diese Färbung für eine effizientere Gate-Platzierung in einem Quantum-Circuit zu nutzen. Der Zweck dieser kantengefärbten Coupling Map besteht darin, eine Menge von Kantenfarben zu finden, bei der keine zwei Kanten gleicher Farbe einen gemeinsamen Knoten teilen. Für einen QPU bedeutet das, dass Gates entlang gleichfarbiger Kanten (Qubit-Verbindungen) gleichzeitig ausgeführt werden können und der Circuit schneller abläuft.
Als kurzes Beispiel kannst du die Funktion auto_color_edges() verwenden, um eine Kantenfärbung für einen naiven Circuit zu erzeugen, der ein CZGate entlang jeder Qubit-Verbindung ausführt. Das folgende Code-Snippet verwendet die Coupling Map des FakeSherbrooke-Backends, erstellt diesen naiven Circuit und nutzt dann auto_color_edges(), um einen effizienteren äquivalenten Circuit zu erstellen.
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
from qiskit import QuantumCircuit
from qiskit_addon_utils.coloring import auto_color_edges
from qiskit_addon_utils.slicing import combine_slices, slice_by_depth
from collections import defaultdict
backend = FakeSherbrooke()
coupling_map = backend.coupling_map
# Create naive circuit
circuit = QuantumCircuit(backend.num_qubits)
for edge in coupling_map.graph.edge_list():
circuit.cz(edge[0], edge[1])
# Color the edges of the coupling map
coloring = auto_color_edges(coupling_map)
circuit_with_coloring = QuantumCircuit(backend.num_qubits)
# Make a reverse coloring dict in order to make the circuit
color_to_edge = defaultdict(list)
for edge, color in coloring.items():
color_to_edge[color].append(edge)
# Place edges in order of color
for edges in color_to_edge.values():
for edge in edges:
circuit_with_coloring.cz(edge[0], edge[1])
print(f"The circuit without using edge coloring has depth: {circuit.depth()}")
print(
f"The circuit using edge coloring has depth: {circuit_with_coloring.depth()}"
)
The circuit without using edge coloring has depth: 37
The circuit using edge coloring has depth: 3
Slicing
Das Modul qiskit-addon-utils.slicing enthält Funktionen und Transpiler-Passes zum Arbeiten mit Circuit-„Slices" – zeitartigen Partitionen eines QuantumCircuit, die sich über alle Qubits erstrecken. Diese Slices werden hauptsächlich für die Operator-Rückpropagation verwendet. Die vier Hauptmethoden zum Aufteilen eines Circuits sind nach Gate-Typ, Tiefe, Färbung oder Barrier-Anweisungen. Die Slicing-Funktionen geben eine Liste von QuantumCircuit-Objekten zurück. Aufgeteilte Circuits können mit der Funktion combine_slices() auch wieder zusammengeführt werden. Weitere Informationen findest du in der API-Referenz des Moduls.
Im Folgenden sind einige Beispiele aufgeführt, wie du diese Slices mit dem folgenden Circuit erstellen kannst:
import numpy as np
from qiskit import QuantumCircuit
num_qubits = 9
qc = QuantumCircuit(num_qubits)
qc.ry(np.pi / 4, range(num_qubits))
qubits_1 = [i for i in range(num_qubits) if i % 2 == 0]
qubits_2 = [i for i in range(num_qubits) if i % 2 == 1]
qc.cx(qubits_1[:-1], qubits_2)
qc.cx(qubits_2, qubits_1[1:])
qc.cx(qubits_1[-1], qubits_1[0])
qc.rx(np.pi / 4, range(num_qubits))
qc.rz(np.pi / 4, range(num_qubits))
qc.draw("mpl", scale=0.6)
Falls es keine offensichtliche Möglichkeit gibt, die Struktur eines Circuits für die Operator-Rückpropagation auszunutzen, kannst du den Circuit in Slices einer bestimmten Tiefe aufteilen.
# Slice circuit into partitions of depth 1
slices = slice_by_depth(qc, 1)
# Recombine slices in order to visualize the partitions together
combined_slices = combine_slices(slices, include_barriers=True)
combined_slices.draw("mpl", scale=0.6)
In Fällen wie der Ausführung von Trotter-Circuits zur Modellierung der Dynamik eines Quantensystems kann es vorteilhaft sein, nach Gate-Typ zu slicen.
from qiskit_addon_utils.slicing import slice_by_gate_types
slices = slice_by_gate_types(qc)
# Recombine slices in order to visualize the partitions together
combined_slices = combine_slices(slices, include_barriers=True)
combined_slices.draw("mpl", scale=0.6)
Wenn deine Arbeitslast darauf ausgelegt ist, die physische Qubit-Konnektivität des QPU auszunutzen, auf dem sie ausgeführt wird, kannst du Slices basierend auf der Kantenfärbung erstellen. Das folgende Code-Snippet weist den Circuit-Kanten eine Drei-Färbung zu und teilt den Circuit entsprechend der Kantenfärbung auf. (Hinweis: Dies betrifft nur nicht-lokale Gates. Einzel-Qubit-Gates werden nach Gate-Typ gesliced.)
from qiskit_addon_utils.slicing import slice_by_coloring
# Assign a color to each set of connected qubits
coloring = {}
for i in range(num_qubits - 1):
coloring[(i, i + 1)] = i % 3
coloring[(num_qubits - 1, 0)] = 2
# Create a circuit with operations added in order of color
qc = QuantumCircuit(num_qubits)
qc.ry(np.pi / 4, range(num_qubits))
edges = [
edge for color in range(3) for edge in coloring if coloring[edge] == color
]
for edge in edges:
qc.cx(edge[0], edge[1])
qc.rx(np.pi / 4, range(num_qubits))
qc.rz(np.pi / 4, range(num_qubits))
# Create slices by edge color
slices = slice_by_coloring(qc, coloring=coloring)
# Recombine slices in order to visualize the partitions together
combined_slices = combine_slices(slices, include_barriers=True)
combined_slices.draw("mpl", scale=0.6)
Wenn du eine eigene Slicing-Strategie hast, kannst du stattdessen Barriers im Circuit platzieren, um die Stellen zu markieren, an denen der Circuit aufgeteilt werden soll, und die Funktion slice_by_barriers verwenden.
qc = QuantumCircuit(num_qubits)
qc.ry(np.pi / 4, range(num_qubits))
qc.barrier()
qubits_1 = [i for i in range(num_qubits) if i % 2 == 0]
qubits_2 = [i for i in range(num_qubits) if i % 2 == 1]
qc.cx(qubits_1[:-1], qubits_2)
qc.cx(qubits_2, qubits_1[1:])
qc.cx(qubits_1[-1], qubits_1[0])
qc.barrier()
qc.rx(np.pi / 4, range(num_qubits))
qc.rz(np.pi / 4, range(num_qubits))
qc.draw("mpl", scale=0.6)
Sobald die Barriers gesetzt sind, kannst du jeden Slice einzeln untersuchen.
from qiskit_addon_utils.slicing import slice_by_barriers
slices = slice_by_barriers(qc)
slices[0].draw("mpl", scale=0.6)
slices[1].draw("mpl", scale=0.6)
slices[2].draw("mpl", scale=0.6)
Nächste Schritte
- Lies die Übersicht über das OBP-Addon.
- Verstehe, wie das SQD-Addon funktioniert.
- Mach dich mit dem AQC-Tensor-Addon vertraut.