Sari la conținutul principal

Kerneluri cuantice cu porți fracționale

Estimare de utilizare: sub 30 de secunde pe un procesor Heron r2 (NOTĂ: Aceasta este doar o estimare. Timpul de execuție poate varia.)

Rezultate ale învățării​

  • Ce sunt porțile fracționale și cum reduc adâncimea și durata circuitelor pe QPU-urile IBM®

  • Constrângerile asociate cu utilizarea porților fracționale (în particular, intervalul unghiului RZZ)

  • Cum să construiești un flux de lucru pentru kernelul cuantic care folosește porți fracționale cu IBM Quantum Compute Service

  • Cum să compari metricile de execuție pe hardware (adâncime, durată, numărul de porți non-locale, fidelitate) cu și fără porți fracționale

  • Cum să folosești doar porți RX fracționale, păstrând în același timp fluxul de lucru standard cu modele Qiskit

Cerințe preliminare​

Fundal​

Porți fracționale pe QPU-urile IBM​

Porțile fracționale sunt porți cuantice parametrizate ce permit executarea directă a rotațiilor cu unghi arbitrar (în anumite limite), eliminând nevoia de a le descompune în mai multe porți de bază. Prin valorificarea interacțiunilor native dintre qubiții fizici, poți implementa anumite unitare mai eficient pe hardware.

QPU-urile IBM Quantum® Heron suportă următoarele porți fracționale:

  • RZZ(θ)R_{ZZ}(\theta) pentru 0<θ<π/20 < \theta < \pi / 2
  • RX(θ)R_X(\theta) pentru orice valoare reală θ\theta

Aceste porți pot reduce semnificativ atât adâncimea, cât și durata circuitelor cuantice. Sunt deosebit de avantajoase în aplicațiile care se bazează mult pe RZZR_{ZZ} și RXR_X, cum ar fi simularea Hamiltonianului, Algoritmul de Optimizare Aproximativă Cuantică (QAOA) și metodele de kernel cuantic. În acest tutorial, ne concentrăm pe kernelul cuantic ca exemplu practic.

Limitări​

Porțile fracționale sunt în prezent o funcționalitate experimentală și vin cu câteva restricții:

Porțile fracționale necesită un flux de lucru diferit față de abordarea standard. Acest tutorial explică cum să lucrezi cu porți fracționale printr-o aplicație practică.

Consultă următoarele resurse pentru mai multe detalii despre porțile fracționale.

Abordări ale fluxului de lucru pentru constrângerea unghiului RZZ​

Fluxul de lucru pentru utilizarea porților fracționale urmează în general fluxul Qiskit patterns. Diferența esențială este că toate unghiurile RZZ trebuie să satisfacă constrângerea 0<θ≤π/20 < \theta \leq \pi/2. Există două abordări pentru a asigura respectarea acestei condiții, după cum discutăm mai jos. Recomandăm a doua abordare, iar în acest tutorial o demonstrăm printr-un exemplu inspirat de metoda kernelului cuantic. Pentru a înțelege mai bine unde kernelurile cuantice sunt susceptibile de a fi utile, recomandăm citirea lucrării Liu, Arunachalam & Temme (2021).

Poți parcurge și tutorialul Antrenarea kernelului cuantic și lecția Kerneluri cuantice din cursul de învățare automată cuantică de pe IBM Quantum® Learning.

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-basis-constructor qiskit-ibm-runtime

1. Generează valori de parametri care respectă constrângerea unghiului RZZ​

Dacă ești sigur că toate unghiurile RZZ se încadrează în intervalul valid, poți urma fluxul standard Qiskit patterns. În acest caz, transmiți pur și simplu valorile parametrilor ca parte a unui PUB. Fluxul de lucru se desfășoară astfel.

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)])

Dacă încerci să trimiți un PUB care conține o poartă RZZ cu un unghi în afara intervalului valid, vei întâlni un mesaj de eroare de tipul:

'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].'

Pentru a evita această eroare, folosește a doua abordare descrisă mai jos.

2. Asignează valorile parametrilor circuitelor înainte de transpilare​

Pachetul qiskit-ibm-runtime oferă un pas de transpilare specializat numit FoldRzzAngle. Acest pas transformă circuitele cuantice astfel încât toate unghiurile RZZ să respecte constrângerea unghiului RZZ. Dacă furnizezi backend-ul la generate_preset_pass_manager sau transpile, Qiskit aplică automat FoldRzzAngle circuitelor cuantice. Această abordare necesită să asignezi valorile parametrilor circuitelor cuantice înainte de transpilare. Fluxul de lucru se desfășoară astfel.

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)])

Reține că acest flux de lucru presupune un cost computațional mai mare decât prima abordare, deoarece implică atribuirea valorilor parametrilor circuitelor cuantice și stocarea locală a circuitelor cu parametri asignați.

atenție

Reține o problemă cunoscută în qiskit-ibm-runtime v0.47.0 în care porțile RZZ cu unghiuri invalide ar putea rămâne în circuite chiar și după transpilare, în anumite scenarii.

Consultă qiskit-ibm-runtime#2441 pentru a urmări progresul acestei probleme. Recomandăm următoarea soluție temporară până la rezolvarea ei.

pm = generate_preset_pass_manager(backend=backend, ...)
pm.post_optimization = PassManager(
[
FoldRzzAngle(),
Optimize1qGatesDecomposition(target=backend.target),
RemoveIdentityEquivalent(target=backend.target),
]
)
... = pm.run(...)

Cerințe​

Înainte de a începe acest tutorial, asigură-te că ai instalate următoarele:

  • Qiskit SDK v2.0 sau o versiune ulterioară, cu suport pentru vizualizare
  • Qiskit Runtime v0.41 sau o versiune ulterioară (pip install qiskit-ibm-runtime)
  • Qiskit Aer v0.17 sau o versiune ulterioară (pip install qiskit-aer)
  • Qiskit Basis Constructor (pip install qiskit_basis_constructor)

Configurare​

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

Activează porțile fracționale și verifică porțile de bază​

Pentru a utiliza porțile fracționale, poți obține un backend care le suportă setând opțiunea use_fractional_gates=True. Dacă backend-ul suportă porțile fracționale, vei vedea rzz și rx listate printre porțile sale de bază.

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']

Exemplu pe simulator la scară mică​

În această secțiune, parcurgem cei patru pași ai fluxului de lucru Qiskit patterns pe un simulator, folosind circuitul kernelului cuantic ca exemplu practic.

Pasul 1: Mapează intrările clasice la o problemă cuantică​

Circuit kernel cuantic​

În această secțiune, explorăm circuitul kernelului cuantic folosind porți RZZ pentru a introduce fluxul de lucru pentru porțile fracționale.

Începem prin construirea unui circuit cuantic pentru a calcula intrările individuale ale matricei kernel. Aceasta se face combinând circuite ZZ feature map cu o suprapunere unitară. Funcția kernel preia vectori din spațiul mapat prin caracteristici și returnează produsul lor intern ca intrare a matricei kernel: K(x,y)=⟨Φ(x)∣Φ(y)⟩,K(x, y) = \langle \Phi(x) | \Phi(y) \rangle, unde ∣Φ(x)⟩|\Phi(x)\rangle reprezintă starea cuantică mapată prin caracteristici.

Construim manual un circuit ZZ feature map folosind porți RZZ. Deși Qiskit oferă un zz_feature_map integrat, acesta nu suportă în prezent porțile RZZ începând cu Qiskit v2.4.1 (vezi problema).

Apoi, calculăm funcția kernel pentru intrări identice — de exemplu, K(x,x)=1K(x, x) = 1. Pe calculatoarele cuantice cu zgomot, această valoare poate fi mai mică de 1 din cauza zgomotului. Un rezultat mai aproape de 1 indică un zgomot mai scăzut în execuție. În acest tutorial, ne referim la această valoare ca fidelitate, definită ca fidelity=K(x,x).\text{fidelity} = K(x, x).

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

Circuitele kernelului cuantic și valorile corespunzătoare ale parametrilor sunt generate pentru sisteme cu 4 până la 40 de qubiți, iar fidelitățile acestora sunt evaluate ulterior.

qubits = list(range(4, 12, 2))
circuits = [quantum_kernel(i, reps=reps) for i in qubits]
params = [random_parameters(circ) for circ in circuits]

Circuitul cu patru qubiți este vizualizat mai jos.

circuits[0].draw("mpl", fold=-1)

Output of the previous code cell

În fluxul standard Qiskit patterns, valorile parametrilor sunt transmise de obicei primitivei Sampler sau Estimator ca parte a unui PUB. Totuși, când folosești un backend care suportă porțile fracționale, aceste valori ale parametrilor trebuie asignate explicit circuitului cuantic înainte de transpilare.

b_qc = [
circ.assign_parameters(param) for circ, param in zip(circuits, params)
]
b_qc[0].draw("mpl", fold=-1)

Output of the previous code cell

Pasul 2: Optimizează problema pentru execuția pe hardware quantum​

Transpilăm apoi circuitul folosind managerul de treceri, urmând pattern-ul standard Qiskit. Furnizând un backend care suportă gate-uri fracționare la generate_preset_pass_manager, o trecere specializată numită FoldRzzAngle este inclusă automat. Această trecere modifică circuitul pentru a respecta constrângerile de unghi RZZ. Prin urmare, gate-urile RZZ cu valori negative din figura anterioară sunt transformate în valori pozitive, iar câteva gate-uri X suplimentare sunt adăugate.

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})

Output of the previous code cell

Pentru a evalua impactul gate-urilor fracționare, calculăm numărul de gate-uri nelocale (CZ și RZZ pentru acest backend), împreună cu adâncimile și duratele circuitelor, și comparăm aceste metrici cu cele din fluxul de lucru standard mai târziu.

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
]

Pasul 3: Execută folosind primitivele Qiskit​

Rulăm circuitul transpilat cu backend-ul care suportă gate-uri fracționare.

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

Pasul 4: Post-procesează și returnează rezultatul în formatul clasic dorit​

Poți obține valoarea funcției kernel K(x,x)K(x, x) măsurând probabilitatea șirului de biți toți-zero 00...00 în ieșire.

result = job.result()
fidelity_f = [fidelity(result=res) for res in result]
print(fidelity_f)
[0.929, 0.882, 0.8645, 0.817]

Comparație între fluxul de lucru și circuitul fără gate-uri fracționare​

În această secțiune, prezentăm fluxul de lucru standard Qiskit patterns folosind un backend care nu suportă gate-uri fracționare. Comparând circuitele transpilate, vei observa că versiunea cu gate-uri fracționare (din secțiunea anterioară) este mai compactă decât cea fără gate-uri fracționare.

# 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})

Output of the previous code cell

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]

Comparație între adâncimi, durate și fidelități​

În această secțiune, comparăm numărul de gate-uri nelocale și fidelitățile între circuitele cu și fără gate-uri fracționare. Aceasta evidențiază beneficiile potențiale ale utilizării gate-urilor fracționare în ceea ce privește eficiența și calitatea execuției.

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

Exemplu pe hardware la scară mare​

În această secțiune, evaluăm fluxul de lucru al kernelului cuantic cu și fără porți fracționale pe hardware cuantic cu până la 40 de qubiți.

Pașii 1-4 combinați​

Fluxul de lucru urmează aceeași structură ca exemplul la scară mică. Transpilăm toate circuitele cu și fără porți fracționale, colectăm metrici, apoi trimitem circuitele către hardware cuantic real.

# -------------------------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

Apoi comparăm metricile.

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

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>

Output of the previous code cell

Comparăm timpul de utilizare al QPU-ului cu și fără gate-uri fracționare. Rezultatele din celula următoare arată că timpii de utilizare ai QPU-ului sunt aproape identici.

print(f"no fractional gates: {usage_c} seconds")
print(f"fractional gates: {usage_f} seconds")
no fractional gates: 8 seconds
fractional gates: 8 seconds

Subiect avansat: Folosind doar porți RX fracționale​

Necesitatea unui flux de lucru modificat atunci când se folosesc porți fracționale provine în principal din restricția asupra unghiurilor porții RZZ. Cu toate acestea, dacă folosești doar porțile RX fracționale și excluzi porțile RZZ fracționale, poți continua să urmezi fluxul de lucru standard al tiparelor Qiskit. Această abordare poate oferi în continuare beneficii semnificative, în special în Circuit-uri care implică un număr mare de porți RX și U, reducând numărul total de porți și îmbunătățind potențial performanța. În această secțiune, demonstrăm cum să îți optimizezi Circuit-urile folosind doar porți RX fracționale, omițând porțile RZZ.

Pentru a susține acest lucru, furnizăm o funcție utilitară care îți permite să dezactivezi un anumit basis gate dintr-un obiect Target. Aici, o folosim pentru a dezactiva porțile RZZ.

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

Folosim un circuit format din porți U, CZ și RZZ ca exemplu.

qc = n_local(3, "u", "cz", "linear", reps=1)
qc.rzz(1.1, 0, 1)
qc.draw("mpl")

Output of the previous code cell

Mai întâi transpilăm Circuit-ul pentru un Backend care nu suportă porți fracționale.

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})

Output of the previous code cell

Apoi, transpilăm același Circuit folosind porți RX fracționale, excluzând porțile RZZ. Aceasta conduce la o ușoară reducere a numărului total de porți, datorită implementării mai eficiente a porților RX.

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})

Output of the previous code cell

Optimizarea porților U cu porți RX fracționale​

În această secțiune, demonstrăm cum să optimizăm porțile U folosind porți RX fracționale, construind pe același circuit introdus în secțiunea anterioară. Transpilăm circuitul folosind doar porți RX fracționale, excluzând porțile RZZ. Introducând o regulă de descompunere personalizată, după cum se arată mai jos, putem reduce numărul de porți pe un singur qubit necesare pentru a implementa o poartă U.

Această funcționalitate este în prezent în discuție în această problemă GitHub.

# 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)

În continuare, aplicăm Transpiler-ul folosind traducerea constructor-beta furnizată de pachetul qiskit-basis-constructor. Ca rezultat, numărul total de porți este redus comparativ cu transpilarea anterioară.

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})

Output of the previous code cell

Pași următori​

Recomandări

Dacă ai găsit acest material interesant, ai putea fi interesat de următoarele materiale: