Quantenkernel mit fraktionalen Gates
Geschätzte Laufzeit: unter 30 Sekunden auf einem Heron-r2-Prozessor (HINWEIS: Dies ist nur eine Schätzung. Deine tatsächliche Laufzeit kann abweichen.)
Lernziele
-
Was fraktionale Gates sind und wie sie die Circuit-Tiefe und -Dauer auf IBM®-QPUs reduzieren
-
Die Einschränkungen bei der Verwendung fraktionaler Gates (insbesondere der RZZ-Winkelbereich)
-
Wie man einen Quantenkern-Workflow konstruiert, der fraktionale Gates mit dem IBM Quantum Compute Service verwendet
-
Wie man Hardware-Ausführungsmetriken (Tiefe, Dauer, Anzahl nicht-lokaler Gates, Fidelity) mit und ohne fraktionale Gates vergleicht
-
Wie man ausschließlich fraktionale RX-Gates verwendet und dabei den Standard-Qiskit-Patterns-Workflow beibehält
Voraussetzungen
-
Der Qiskit-Patterns-Workflow
-
Der Leitfaden Fraktionale Gates
-
Das Tutorial Quantenkernel-Training und die Lektion Quantenkernel des Kurses Quantum Machine Learning
Hintergrund
Fraktionale Gates auf IBM QPUs
Fraktionale Gates sind parametrisierte Quantengates, die die direkte Ausführung von Rotationen mit beliebigen Winkeln (innerhalb bestimmter Grenzen) ermöglichen, wodurch die Notwendigkeit entfällt, diese in mehrere Basis-Gates zu zerlegen. Durch Nutzung der nativen Wechselwirkungen zwischen physikalischen Qubits lassen sich bestimmte unitäre Operationen effizienter auf der Hardware implementieren.
IBM Quantum® Heron QPUs unterstützen folgende fraktionale Gates:
- für
- für jeden reellen Wert
Diese Gates können sowohl die Tiefe als auch die Dauer von Quantencircuits erheblich reduzieren. Sie sind besonders vorteilhaft in Anwendungen, die stark auf und setzen, wie z. B. Hamiltonsimulation, der Quantum Approximate Optimization Algorithm (QAOA) und Quantenkernmethoden. In diesem Tutorial konzentrieren wir uns auf den Quantenkern als praktisches Beispiel.
Einschränkungen
Fraktionale Gates sind derzeit ein experimentelles Feature und unterliegen einigen Einschränkungen:
- ist auf Winkel im Bereich beschränkt.
- Die Verwendung fraktionaler Gates wird nicht unterstützt für dynamische Circuits, Pauli Twirling, probabilistische Fehlerauslöschung (PEC) und Zero-Noise-Extrapolation (ZNE) (mit probabilistischer Fehlerverstärkung (PEA)).
Fraktionale Gates erfordern im Vergleich zum Standardvorgehen einen anderen Workflow. Dieses Tutorial erklärt anhand einer praktischen Anwendung, wie du mit fraktionalen Gates arbeitest.
Weitere Details zu fraktionalen Gates findest du hier:
Workflow-Ansätze für die RZZ-Winkelbedingung
Der Workflow für die Verwendung fraktionaler Gates folgt im Allgemeinen dem Qiskit-Patterns-Workflow. Der wesentliche Unterschied besteht darin, dass alle RZZ-Winkel die Bedingung erfüllen müssen. Es gibt zwei Ansätze, um sicherzustellen, dass diese Bedingung erfüllt ist, die wir im Folgenden besprechen. Wir empfehlen den zweiten Ansatz und demonstrieren ihn in diesem Tutorial anhand eines Beispiels, das von der Quantenkernel-Methode inspiriert ist. Um besser zu verstehen, wo Quantenkernel wahrscheinlich nützlich sind, empfehlen wir die Lektüre von Liu, Arunachalam & Temme (2021).
Du kannst auch das Tutorial Quantenkernel-Training und die Lektion Quantenkernel im Kurs Quantum Machine Learning auf IBM Quantum® Learning durcharbeiten.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer", "qiskit_basis_constructor": "qiskit-basis-constructor", "qiskit_ibm_runtime": "qiskit-ibm-runtime"}
_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")
1. Parameterwerte generieren, die die RZZ-Winkelbedingung erfüllen
Wenn du sicher bist, dass alle RZZ-Winkel innerhalb des gültigen Bereichs liegen, kannst du den Standard-Qiskit-Patterns-Workflow verwenden. In diesem Fall übergibst du die Parameterwerte einfach als Teil eines PUB. Der Workflow läuft wie folgt ab:
pm = generate_preset_pass_manager(backend=backend, ...)
t_circuit = pm.run(circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit, parameter_values)])
estimator.run([(t_circuit, t_observable, parameter_values)])
Wenn du versuchst, einen PUB mit einem RZZ-Gate zu übermitteln, dessen Winkel außerhalb des gültigen Bereichs liegt, erhältst du eine Fehlermeldung wie:
'The instruction rzz is supported only for angles in the range [0, pi/2], but an angle (20.0) outside of this range has been requested; via parameter value(s) γ[0]=10.0, substituted in parameter expression 2.0*γ[0].'
Um diesen Fehler zu vermeiden, verwende den weiter unten beschriebenen zweiten Ansatz.
2. Parameterwerte vor der Transpilierung dem Circuit zuweisen
Das qiskit-ibm-runtime-Paket stellt einen spezialisierten Transpiler-Pass namens FoldRzzAngle bereit.
Dieser Pass transformiert Quantencircuits so, dass alle RZZ-Winkel die RZZ-Winkelbedingung erfüllen.
Wenn du dem generate_preset_pass_manager oder transpile ein Backend übergibst, wendet Qiskit FoldRzzAngle automatisch auf die Quantencircuits an.
Das erfordert, dass du Parameterwerte den Quantencircuits vor der Transpilierung zuweist.
Der Workflow läuft wie folgt ab:
pm = generate_preset_pass_manager(backend=backend, ...)
b_circuit = circuit.assign_parameters(parameter_values)
t_circuit = pm.run(b_circuit)
t_observable = observable.apply_layout(t_circuit.layout)
sampler.run([(t_circuit,)])
estimator.run([(t_circuit, t_observable)])
Beachte, dass dieser Workflow einen höheren Rechenaufwand als der erste Ansatz mit sich bringt, da er das Zuweisen von Parameterwerten zu Quantencircuits und das lokale Speichern der parametergebundenen Circuits beinhaltet.
Beachte ein bekanntes Problem in qiskit-ibm-runtime v0.47.0, bei dem RZZ-Gates mit ungültigen Winkeln in bestimmten Szenarien auch nach der Transpilierung im Circuit verbleiben können.
Verfolge den Fortschritt bei diesem Problem unter qiskit-ibm-runtime#2441. Wir empfehlen den folgenden Workaround, bis das Problem behoben ist.
pm = generate_preset_pass_manager(backend=backend, ...)
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
... = pm.run(...)
Anforderungen
Stelle vor dem Start dieses Tutorials sicher, dass folgende Pakete installiert sind:
- Qiskit SDK v2.0 oder höher, mit Visualisierungs-Unterstützung
- Qiskit Runtime v0.41 oder höher (
pip install qiskit-ibm-runtime) - Qiskit Aer v0.17 oder höher (
pip install qiskit-aer) - Qiskit Basis Constructor (
pip install qiskit_basis_constructor)
Einrichtung
import matplotlib.pyplot as plt
import numpy as np
from qiskit import QuantumCircuit, generate_preset_pass_manager
from qiskit.circuit import ParameterVector
from qiskit.circuit.library import UGate, n_local, unitary_overlap
from qiskit.transpiler import Target, PassManager
from qiskit.transpiler.passes import (
Optimize1qGatesDecomposition,
RemoveIdentityEquivalent,
)
from qiskit_aer.primitives import SamplerV2 as AerSampler
from qiskit_basis_constructor import DEFAULT_EQUIVALENCE_LIBRARY
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
from qiskit_ibm_runtime.transpiler.passes import FoldRzzAngle
Fraktionale Gates aktivieren und Basis-Gates prüfen
Um fraktionale Gates zu nutzen, kannst du ein Backend, das diese unterstützt, über die Option use_fractional_gates=True abrufen.
Wenn das Backend fraktionale Gates unterstützt, siehst du rzz und rx in der Liste seiner Basis-Gates.
service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
) # backend should be a heron device or later
backend_name = backend.name
backend_c = service.backend(backend_name) # w/o fractional gates
backend_f = service.backend(
backend_name, use_fractional_gates=True
) # w/ fractional gates
print(f"Backend: {backend_name}")
print(f"No fractional gates: {backend_c.basis_gates}")
print(f"With fractional gates: {backend_f.basis_gates}")
if "rzz" not in backend_f.basis_gates:
print(f"Backend {backend_name} does not support fractional gates")
Backend: ibm_marrakesh
No fractional gates: ['cz', 'id', 'rz', 'sx', 'x']
With fractional gates: ['cz', 'id', 'rx', 'rz', 'rzz', 'sx', 'x']
Kleines Simulator-Beispiel
In diesem Abschnitt gehen wir die vier Schritte des Qiskit-Patterns-Workflows auf einem Simulator durch und verwenden dabei den Quantenkern-Circuit als praktisches Beispiel.
Schritt 1: Klassische Eingaben auf ein Quantenproblem abbilden
Quantenkern-Circuit
In diesem Abschnitt untersuchen wir den Quantenkern-Circuit unter Verwendung von RZZ-Gates, um den Workflow für fraktionale Gates vorzustellen.
Wir beginnen mit dem Aufbau eines Quantencircuits zur Berechnung einzelner Einträge der Kernmatrix. Dazu werden ZZ-Feature-Map-Circuits mit einem unitären Overlap kombiniert. Die Kernfunktion nimmt Vektoren im feature-gemappten Raum und gibt deren inneres Produkt als Eintrag der Kernmatrix zurück: wobei den feature-gemappten Quantenzustand darstellt.
Wir konstruieren manuell einen ZZ-Feature-Map-Circuit mit RZZ-Gates.
Obwohl Qiskit eine eingebaute zz_feature_map bereitstellt, unterstützt diese Stand Qiskit v2.4.1 derzeit keine RZZ-Gates (siehe Issue).
Als Nächstes berechnen wir die Kernfunktion für identische Eingaben – zum Beispiel . Auf verrauschten Quantencomputern kann dieser Wert aufgrund von Rauschen kleiner als 1 sein. Ein Ergebnis näher an 1 deutet auf geringeres Rauschen bei der Ausführung hin. In diesem Tutorial bezeichnen wir diesen Wert als Fidelity, definiert als
optimization_level = 2
shots = 2000
reps = 3
rng = np.random.default_rng(seed=123)
def my_zz_feature_map(num_qubits: int, reps: int = 1) -> QuantumCircuit:
x = ParameterVector("x", num_qubits * reps)
qc = QuantumCircuit(num_qubits)
qc.h(range(num_qubits))
for k in range(reps):
K = k * num_qubits
for i in range(num_qubits):
qc.rz(x[i + K], i)
pairs = [(i, i + 1) for i in range(num_qubits - 1)]
for i, j in pairs[0::2] + pairs[1::2]:
qc.rzz((np.pi - x[i + K]) * (np.pi - x[j + K]), i, j)
return qc
def quantum_kernel(num_qubits: int, reps: int = 1) -> QuantumCircuit:
qc = my_zz_feature_map(num_qubits, reps=reps)
inner_product = unitary_overlap(qc, qc, "x", "y", insert_barrier=True)
inner_product.measure_all()
return inner_product
def random_parameters(inner_product: QuantumCircuit) -> np.ndarray:
return np.tile(rng.random(inner_product.num_parameters // 2), 2)
def fidelity(result) -> float:
ba = result.data.meas
return ba.get_int_counts().get(0, 0) / ba.num_shots
Quantenkern-Circuits und die zugehörigen Parameterwerte werden für Systeme mit 4 bis 40 Qubits generiert, und anschließend werden deren Fidelities ausgewertet.
qubits = list(range(4, 12, 2))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
Der Vier-Qubit-Circuit wird unten visualisiert.
circuits[0].draw("mpl", fold=-1)
Im Standard-Qiskit-Patterns-Workflow werden Parameterwerte typischerweise als Teil eines PUB an den Sampler oder Estimator übergeben. Wenn jedoch ein Backend verwendet wird, das fraktionale Gates unterstützt, müssen diese Parameterwerte dem Quantencircuit explizit vor der Transpilierung zugewiesen werden.
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)
Schritt 2: Problem für die Quantenhardware-Ausführung optimieren
Anschließend transpilieren wir den Circuit mit dem Pass Manager gemäß dem Standard-Qiskit-Patterns-Workflow.
Durch Übergabe eines Backends, das fraktionale Gates unterstützt, an generate_preset_pass_manager wird automatisch ein spezialisierter Pass namens FoldRzzAngle eingebunden.
Dieser Pass modifiziert den Circuit, damit er die RZZ-Winkelbedingungen einhält.
Dadurch werden RZZ-Gates mit negativen Werten aus der vorherigen Abbildung in positive Werte umgewandelt und einige zusätzliche X-Gates hinzugefügt.
backend_f = service.backend(name=backend_name, use_fractional_gates=True)
# pm_f includes `FoldRzzAngle` pass
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_f
)
pm_f.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend_f.target),
RemoveIdentityEquivalent(target=backend_f.target),
]
)
t_qc_f = pm_f.run(b_qc)
print(t_qc_f[0].count_ops())
t_qc_f[0].draw("mpl", fold=-1)
OrderedDict({'rz': 35, 'rzz': 18, 'x': 13, 'rx': 9, 'measure': 4, 'barrier': 2})
Um die Auswirkungen fraktionaler Gates zu bewerten, messen wir die Anzahl der nicht-lokalen Gates (CZ und RZZ für dieses Backend), zusammen mit Circuit-Tiefen und -Dauern, und vergleichen diese Metriken später mit denen eines Standard-Workflows.
nnl_f = [qc.num_nonlocal_gates() for qc in t_qc_f]
depth_f = [qc.depth() for qc in t_qc_f]
duration_f = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc_f
]
Schritt 3: Mit Qiskit-Primitiven ausführen
Wir führen den transpilierten Circuit mit dem Backend aus, das fraktionale Gates unterstützt.
sampler_f = AerSampler.from_backend(backend_f)
job = sampler_f.run(t_qc_f, shots=shots)
print(job.job_id())
085ce928-767e-4200-93bf-3905e5411cfe
Schritt 4: Nachverarbeitung und Rückgabe des Ergebnisses im gewünschten klassischen Format
Den Kernfunktionswert erhältst du, indem du die Wahrscheinlichkeit des Null-Bitstrings 00...00 in der Ausgabe misst.
result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)
[0.929, 0.882, 0.8645, 0.817]
Vergleich von Workflow und Circuit ohne fraktionale Gates
In diesem Abschnitt stellen wir den Standard-Qiskit-Patterns-Workflow mit einem Backend vor, das keine fraktionalen Gates unterstützt. Beim Vergleich der transpilierten Circuits wirst du feststellen, dass die Version mit fraktionalen Gates (aus dem vorherigen Abschnitt) kompakter ist als die Version ohne.
# step 1: map classical inputs to quantum problem
# `circuits` and `params` from the previous section are reused here
# step 2: optimize circuits
backend_c = service.backend(backend_name) # w/o fractional gates
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc_c = pm_c.run(circuits)
print(t_qc_c[0].count_ops())
t_qc_c[0].draw("mpl", fold=-1)
OrderedDict({'rz': 130, 'sx': 80, 'cz': 36, 'measure': 4, 'barrier': 2})
nnl_c = [qc.num_nonlocal_gates() for qc in t_qc_c]
depth_c = [qc.depth() for qc in t_qc_c]
duration_c = [
qc.estimate_duration(backend_c.target, unit="u") for qc in t_qc_c
]
# step 3: execute
sampler_c = AerSampler.from_backend(backend_c)
job = sampler_c.run(pubs=zip(t_qc_c, params), shots=shots)
print(job.job_id())
f2cca29d-7263-4976-9e51-13a91b75c3ae
# step 4: post-processing
result = job.result()
fidelity_c = [fidelity(res) for res in result]
print(fidelity_c)
[0.8625, 0.7605, 0.702, 0.671]
Vergleich von Tiefen, Dauern und Fidelities
In diesem Abschnitt vergleichen wir die Anzahl der nicht-lokalen Gates und die Fidelities zwischen Circuits mit und ohne fraktionale Gates. Dies verdeutlicht die potenziellen Vorteile der Verwendung fraktionaler Gates in Bezug auf Ausführungseffizienz und -qualität.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x116af3cb0>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11ea4f4d0>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x1247fc440>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x120b792b0>
Großes Hardware-Beispiel
In diesem Abschnitt bewerten wir den Quantenkern-Workflow mit und ohne fraktionale Gates auf Quantenhardware mit bis zu 40 Qubits.
Schritte 1–4 kombiniert
Der Workflow folgt der gleichen Struktur wie das kleine Beispiel. Wir transpilieren alle Circuits mit und ohne fraktionale Gates, sammeln Metriken und übermitteln die Circuits anschließend an echte Quantenhardware.
# -------------------------Step 1-------------------------
qubits = list(range(4, 44, 4))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]
b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
def benchmark(b_qc, backend):
# -------------------------Step 2-------------------------
pm = generate_preset_pass_manager(optimization_level, backend=backend)
if "rzz" in backend.target.operation_names:
# workaround until https://github.com/Qiskit/qiskit-ibm-runtime/issues/2441 is resolved
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
t_qc = pm.run(b_qc)
nnl = [qc.num_nonlocal_gates() for qc in t_qc]
depth = [qc.depth() for qc in t_qc]
duration = [
qc.estimate_duration(backend_f.target, unit="u") for qc in t_qc
]
# -------------------------Step 3-------------------------
sampler = SamplerV2(mode=backend)
sampler.options.dynamical_decoupling.enable = True
sampler.options.dynamical_decoupling.sequence_type = "XY4"
sampler.options.dynamical_decoupling.skip_reset_qubits = True
sampler.options.environment.job_tags = ["TUT_FG"]
job = sampler.run(t_qc, shots=shots)
job_id = job.job_id()
return nnl, depth, duration, job_id
def postprocessing(job_id: str):
# -------------------------Step 4-------------------------
job = service.job(job_id)
result = job.result()
fidelities = [fidelity(result=res) for res in result]
usage = job.usage()
return fidelities, usage
backend_f = service.backend(backend_name, use_fractional_gates=True)
nnl_f, depth_f, duration_f, job_id_f = benchmark(
b_qc, backend_f
) # step 2 & 3
print("job id (w/ fractional gates):", job_id_f)
fidelity_f, usage_f = postprocessing(job_id_f) # step 4
job id (w/ fractional gates): d8uasitbh0os73eqnpig
backend_c = service.backend(backend_name, use_fractional_gates=False)
nnl_c, depth_c, duration_c, job_id_c = benchmark(b_qc, backend_c)
print("job id (w/o fractional gates):", job_id_c)
fidelity_c, usage_c = postprocessing(job_id_c)
job id (w/o fractional gates): d8uav3lposuc738pruug
Anschließend vergleichen wir die Metriken.
plt.plot(qubits, depth_c, "-o", label="no fractional gates")
plt.plot(qubits, depth_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("depth")
plt.title("Comparison of depths")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x12461e660>
plt.plot(qubits, duration_c, "-o", label="no fractional gates")
plt.plot(qubits, duration_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("duration (µs)")
plt.title("Comparison of durations")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11f2ac980>
plt.plot(qubits, nnl_c, "-o", label="no fractional gates")
plt.plot(qubits, nnl_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("number of non-local gates")
plt.title("Comparison of numbers of non-local gates")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x125c91be0>
plt.plot(qubits, fidelity_c, "-o", label="no fractional gates")
plt.plot(qubits, fidelity_f, "-o", label="with fractional gates")
plt.xlabel("number of qubits")
plt.ylabel("fidelity")
plt.title("Comparison of fidelities")
plt.grid()
plt.legend()
<matplotlib.legend.Legend at 0x11fcf6e40>
Wir vergleichen die QPU-Nutzungszeit mit und ohne fraktionale Gates. Die Ergebnisse in der folgenden Zelle zeigen, dass die QPU-Nutzungszeiten nahezu identisch sind.
print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")
no fractional gates: 8 seconds
fractional gates: 8 seconds
Erweitertes Thema: Ausschließliche Verwendung fraktionaler RX-Gates
Die Notwendigkeit des geänderten Workflows bei der Verwendung fraktionaler Gates ergibt sich hauptsächlich aus der Einschränkung der RZZ-Gate-Winkel. Wenn du jedoch nur die fraktionalen RX-Gates verwendest und die fraktionalen RZZ-Gates ausschließt, kannst du weiterhin dem Standard-Qiskit-Patterns-Workflow folgen. Dieser Ansatz kann dennoch sinnvolle Vorteile bieten, insbesondere in Circuits mit einer großen Anzahl von RX-Gates und U-Gates, indem die Gesamtanzahl der Gates reduziert und die Leistung potenziell verbessert wird. In diesem Abschnitt zeigen wir, wie du deine Circuits nur mit fraktionalen RX-Gates optimierst, ohne RZZ-Gates zu verwenden.
Dazu stellen wir eine Hilfsfunktion bereit, mit der du ein bestimmtes Basis-Gate in einem Target-Objekt deaktivieren kannst. Hier verwenden wir sie, um RZZ-Gates zu deaktivieren.
def remove_instruction_from_target(target: Target, gate_name: str) -> Target:
new_target = Target(
description=target.description,
num_qubits=target.num_qubits,
dt=target.dt,
granularity=target.granularity,
min_length=target.min_length,
pulse_alignment=target.pulse_alignment,
acquire_alignment=target.acquire_alignment,
qubit_properties=target.qubit_properties,
concurrent_measurements=target.concurrent_measurements,
)
for name, qarg_map in target.items():
if name == gate_name:
continue
instruction = target.operation_from_name(name)
if qarg_map == {None: None}:
qarg_map = None
new_target.add_instruction(instruction, qarg_map, name=name)
return new_target
Als Beispiel verwenden wir einen Circuit, der aus U-, CZ- und RZZ-Gates besteht.
qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")
Zuerst transpilieren wir den Circuit für ein Backend, das keine fraktionalen Gates unterstützt.
pm_c = generate_preset_pass_manager(
optimization_level=optimization_level, backend=backend_c
)
t_qc = pm_c.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 23, 'sx': 16, 'cz': 4})
Dann transpilieren wir denselben Circuit mit fraktionalen RX-Gates, ohne RZZ-Gates zu verwenden. Dies führt zu einer leichten Reduzierung der Gesamtanzahl an Gates dank der effizienteren Implementierung der RX-Gates.
backend_f = service.backend(backend_name, use_fractional_gates=True)
target = remove_instruction_from_target(backend_f.target, "rzz")
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 22, 'sx': 14, 'cz': 4, 'rx': 1})
U-Gates mit fraktionalen RX-Gates optimieren
In diesem Abschnitt zeigen wir, wie man U-Gates mithilfe fraktionaler RX-Gates optimiert, aufbauend auf demselben Circuit, der im vorherigen Abschnitt eingeführt wurde. Wir transpilieren den Circuit ausschließlich mit fraktionalen RX-Gates, ohne RZZ-Gates. Durch Einführung einer benutzerdefinierten Zerlegungsregel, wie unten gezeigt, können wir die Anzahl der für die Implementierung eines U-Gates erforderlichen Einzel-Qubit-Gates reduzieren.
Dieses Feature wird derzeit in diesem GitHub-Issue diskutiert.
# special decomposition rule for UGate
x = ParameterVector("x", 3)
zxz = QuantumCircuit(1)
zxz.rz(x[2] - np.pi / 2, 0)
zxz.rx(x[0], 0)
zxz.rz(x[1] + np.pi / 2, 0)
DEFAULT_EQUIVALENCE_LIBRARY.add_equivalence(UGate(x[0], x[1], x[2]), zxz)
Als Nächstes wenden wir den Transpiler mit der constructor-beta-Übersetzung des qiskit-basis-constructor-Pakets an.
Als Ergebnis wird die Gesamtanzahl der Gates im Vergleich zur vorherigen Transpilierung reduziert.
pm_f = generate_preset_pass_manager(
optimization_level=optimization_level,
target=target,
translation_method="constructor-beta",
)
t_qc = pm_f.run(qc)
print(t_qc.count_ops())
t_qc.draw("mpl")
OrderedDict({'rz': 16, 'rx': 9, 'cz': 4})
Nächste Schritte
Wenn du diese Arbeit interessant fandest, könnte dich folgendes Material interessieren:
- Leitfaden Fraktionale Gates
- Wann fraktionale Gates nicht verwendet werden sollten
FoldRzzAngleTranspiler-Pass-API-Referenz- Das Tutorial Quantenkernel-Training
- Die Lektion Quantenkernel im Kurs Quantum Machine Learning





