Depanarea joburilor Qiskit Runtime
Versiuni de pachete
Codul de pe această pagină a fost dezvoltat folosind următoarele cerințe. Îți recomandăm să folosești aceste versiuni sau unele mai noi.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
qiskit-aer~=0.17
Înainte de a trimite un workload Qiskit Runtime care consumă multe resurse pentru a fi executat pe hardware, poți folosi clasa Qiskit Runtime Neat (Noisy Estimator Analyzer Tool) pentru a verifica că workload-ul tău Estimator este configurat corect, că este probabil să returneze rezultate precise, că folosește cele mai potrivite opțiuni pentru problema specificată și altele.
Neat cliffordizează Circuit-urile de intrare pentru o simulare eficientă, păstrând totodată structura și adâncimea acestora. Circuit-urile Clifford suferă niveluri similare de zgomot și sunt un bun substitut pentru studierea Circuit-ului original de interes.
Următoarele exemple ilustrează situații în care Neat poate fi o resursă utilă.
Mai întâi, importă pachetele relevante și autentifică-te la serviciul Qiskit Runtime.
Pregătirea mediului
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-aer qiskit-ibm-runtime
import numpy as np
import random
from qiskit.circuit import QuantumCircuit
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator
from qiskit_ibm_runtime.debug_tools import Neat
from qiskit_aer.noise import NoiseModel, depolarizing_error
# Choose the least busy backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Generate a preset pass manager
# This will be used to convert the abstract circuit to an equivalent Instruction Set Architecture (ISA) circuit.
pm = generate_preset_pass_manager(backend=backend, optimization_level=0)
# Set the random seed
random.seed(10)
Inițializarea unui Circuit țintă
Consideră un Circuit cu șase Qubit-uri care are următoarele proprietăți:
- Alternează între rotații
RZaleatorii și straturi de Gate-uriCNOT. - Are o structură în oglindă, adică aplică un unitar
Uurmat de inversul său.
def generate_circuit(n_qubits, n_layers):
r"""
A function to generate a pseudo-random a circuit with ``n_qubits`` qubits and
``2*n_layers`` entangling layers of the type used in this notebook.
"""
# An array of random angles
angles = [
[random.random() for q in range(n_qubits)] for s in range(n_layers)
]
qc = QuantumCircuit(n_qubits)
qubits = list(range(n_qubits))
# do random circuit
for layer in range(n_layers):
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(angles[layer][q_idx], qubit)
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)
# undo random circuit
for layer in range(n_layers)[::-1]:
# cx gates
control_qubits = (
qubits[::2] if layer % 2 == 0 else qubits[1 : n_qubits - 1 : 2]
)
for qubit in control_qubits:
qc.cx(qubit, qubit + 1)
# rotations
for q_idx, qubit in enumerate(qubits):
qc.rz(-angles[layer][q_idx], qubit)
return qc
# Generate a random circuit
qc = generate_circuit(6, 3)
# Convert the abstract circuit to an equivalent ISA circuit.
isa_qc = pm.run(qc)
qc.draw("mpl", idle_wires=0)
Alege operatori Z simpli-Pauli ca observabile și folosește-i pentru a inițializa blocurile primitive unificate (PUB-uri).
# Initialize the observables
obs = ["ZIIIII", "IZIIII", "IIZIII", "IIIZII", "IIIIZI", "IIIIIZ"]
print(f"Observables: {obs}")
# Map the observables to the backend's layout
isa_obs = [SparsePauliOp(o).apply_layout(isa_qc.layout) for o in obs]
# Initialize the PUBs, which consist of six-qubit circuits with `n_layers` 1, ..., 6
all_n_layers = [1, 2, 3, 4, 5, 6]
pubs = [(pm.run(generate_circuit(6, n)), isa_obs) for n in all_n_layers]
Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']
Cliffordizarea Circuit-urilor
Circuit-urile PUB definite anterior nu sunt Clifford, ceea ce le face dificil de simulat clasic. Totuși, poți folosi metoda Neat to_clifford pentru a le mapa la Circuit-uri Clifford în vederea unei simulări mai eficiente. Metoda to_clifford este un wrapper în jurul pasului Transpiler ConvertISAToClifford, care poate fi folosit și independent. În particular, înlocuiește Gate-urile cu un singur Qubit non-Clifford din Circuit-ul original cu Gate-uri Clifford cu un singur Qubit, dar nu modifică Gate-urile cu doi Qubiți, numărul de Qubiți sau adâncimea Circuit-ului.
Consultă Simularea eficientă a Circuit-urilor stabilizatoare cu primitivele Qiskit Aer pentru mai multe informații despre simularea Circuit-urilor Clifford.
Mai întâi, inițializează Neat.
# You could specify a custom `NoiseModel` here. If `None`, `Neat`
# pulls the noise model from the given backend
noise_model = None
# Initialize `Neat`
analyzer = Neat(backend, noise_model)
Apoi, cliffordizează PUB-urile.
clifford_pubs = analyzer.to_clifford(pubs)
clifford_pubs[0].circuit.draw("mpl", idle_wires=0)
Application 1: Analizează impactul zgomotului asupra rezultatelor Circuit-ului
Acest exemplu arată cum să folosești Neat pentru a studia impactul diferitelor modele de zgomot asupra PUB-urilor în funcție de adâncimea Circuit-ului, rulând simulări atât în condiții ideale (ideal_sim), cât și în condiții cu zgomot (noisy_sim). Acest lucru poate fi util pentru a stabili așteptări privind calitatea rezultatelor experimentale înainte de a rula un job pe un QPU. Pentru a afla mai multe despre modelele de zgomot, consultă Simulare exactă și cu zgomot cu primitivele Qiskit Aer.
Rezultatele simulate suportă operații matematice și, prin urmare, pot fi comparate între ele (sau cu rezultate experimentale) pentru a calcula figuri de merit.
Un QPU poate fi afectat de diferite tipuri de zgomot. Modelul de zgomot Qiskit Aer folosit aici simulează doar o parte dintre ele și, prin urmare, este probabil să fie mai puțin sever decât zgomotul de pe un QPU real.
Pentru detalii despre ce erori sunt incluse atunci când inițializezi un model de zgomot de la un QPU, consultă referința API Aer NoiseModel.
Începe prin a efectua simulări clasice ideale și cu zgomot.
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
print(f"Ideal results:\n {ideal_results}\n")
# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
print(f"Noisy results:\n {noisy_results}\n")
Ideal results:
NeatResult([NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.])), NeatPubResult(vals=array([1., 1., 1., 1., 1., 1.]))])
Noisy results:
NeatResult([NeatPubResult(vals=array([0.99023438, 0.99609375, 0.9921875 , 0.99023438, 0.99414062,
0.99414062])), NeatPubResult(vals=array([0.984375 , 0.99414062, 0.98242188, 0.98828125, 0.98632812,
0.99414062])), NeatPubResult(vals=array([0.96679688, 0.97070312, 0.95898438, 0.97851562, 0.98046875,
0.98828125])), NeatPubResult(vals=array([0.9453125 , 0.953125 , 0.97070312, 0.96875 , 0.98242188,
0.99023438])), NeatPubResult(vals=array([0.93164062, 0.9375 , 0.953125 , 0.96875 , 0.96484375,
0.98046875])), NeatPubResult(vals=array([0.92578125, 0.921875 , 0.93359375, 0.953125 , 0.95898438,
0.9765625 ]))])
Apoi, aplică operații matematice pentru a calcula diferența absolută. Restul ghidului folosește diferența absolută ca figură de merit pentru a compara rezultatele ideale cu cele cu zgomot sau cu rezultatele experimentale, dar pot fi definite figuri de merit similare.
Diferența absolută arată că impactul zgomotului crește odată cu dimensiunile Circuit-urilor.
# Figure of merit: Absolute difference
def rdiff(res1, re2):
r"""The absolute difference between `res1` and re2`.
--> The closer to `0`, the better.
"""
d = abs(res1 - re2)
return np.round(d.vals * 100, 2)
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
vals = rdiff(ideal_res, noisy_res)
# Print the mean absolute difference for the observables
mean_vals = np.round(np.mean(vals), 2)
print(
f"Mean absolute difference between ideal and noisy results for circuits with {all_n_layers[idx]} layers:\n {mean_vals}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
0.72%
Mean absolute difference between ideal and noisy results for circuits with 2 layers:
1.17%
Mean absolute difference between ideal and noisy results for circuits with 3 layers:
2.6%
Mean absolute difference between ideal and noisy results for circuits with 4 layers:
3.16%
Mean absolute difference between ideal and noisy results for circuits with 5 layers:
4.4%
Mean absolute difference between ideal and noisy results for circuits with 6 layers:
5.5%
Poți urma aceste orientări aproximative și simplificate pentru a îmbunătăți Circuit-urile de acest tip:
- Dacă diferența absolută medie este mai mare de 90%, atenuarea probabil nu va ajuta.
- Dacă diferența absolută medie este mai mică de 90%, Probabilistic Error Amplification (PEA) va putea probabil să îmbunătățească rezultatele.
- Dacă diferența absolută medie este mai mică de 80%, ZNE cu plierea Gate-urilor va putea, de asemenea, probabil să îmbunătățească rezultatele.
Deoarece toate diferențele absolute de mai sus sunt mai mici de 90%, aplicarea PEA la Circuit-ul original va îmbunătăți, sperăm, calitatea rezultatelor sale. Poți specifica modele de zgomot diferite în analizor. Exemplul următor efectuează același test, dar adaugă un model de zgomot personalizat.
# Set up a noise model with strength 0.02 on every two-qubit gate
noise_model = NoiseModel()
for qubits in backend.coupling_map:
noise_model.add_quantum_error(
depolarizing_error(0.02, 2), ["ecr", "cx"], qubits
)
# Update the analyzer's noise model
analyzer.noise_model = noise_model
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Perform a noisy simulation with the backend's noise model
noisy_results = analyzer.noisy_sim(clifford_pubs)
# Compare the results
for idx, (ideal_res, noisy_res) in enumerate(
zip(ideal_results, noisy_results)
):
values = rdiff(ideal_res, noisy_res)
# Print the mean absolute difference for the observables
mean_values = np.round(np.mean(values), 2)
print(
f"Mean absolute difference between ideal and noisy results for circuits with {all_n_layers[idx]} layers:\n {mean_values}%\n"
)
Mean absolute difference between ideal and noisy results for circuits with 1 layers:
0.0%
Mean absolute difference between ideal and noisy results for circuits with 2 layers:
0.0%
Mean absolute difference between ideal and noisy results for circuits with 3 layers:
0.0%
Mean absolute difference between ideal and noisy results for circuits with 4 layers:
0.0%
Mean absolute difference between ideal and noisy results for circuits with 5 layers:
0.0%
Mean absolute difference between ideal and noisy results for circuits with 6 layers:
0.0%
După cum se arată, dat fiind un model de zgomot, poți încerca să cuantifici impactul zgomotului asupra PUB-urilor de interes (în versiunea lor Cliffordizată) înainte de a le rula pe un QPU.
Application 2: Benchmark different strategies
Acest exemplu folosește Neat pentru a ajuta la identificarea celor mai bune opțiuni pentru PUB-urile tale. Pentru aceasta, ia în considerare rularea unei probleme de estimare cu PEA, care nu poate fi simulată cu qiskit_aer. Poți folosi Neat pentru a determina care factori de amplificare a zgomotului vor funcționa cel mai bine, apoi să folosești acești factori când rulezi experimentul original pe un QPU.
# Generate a circuit with six qubits and six layers
isa_qc = pm.run(generate_circuit(6, 3))
# Use the same observables as previously
pubs = [(isa_qc, isa_obs)]
clifford_pubs = analyzer.to_clifford(pubs)
noise_factors = [
[1, 1.1],
[1, 1.1, 1.2],
[1, 1.5, 2],
[1, 1.5, 2, 2.5, 3],
[1, 4],
]
# Run the PUBs on a QPU
estimator = Estimator(backend)
estimator.options.default_shots = 100000
estimator.options.twirling.enable_gates = True
estimator.options.twirling.enable_measure = True
estimator.options.twirling.shots_per_randomization = 100
estimator.options.resilience.measure_mitigation = True
estimator.options.resilience.zne_mitigation = True
estimator.options.resilience.zne.amplifier = "pea"
jobs = []
for factors in noise_factors:
estimator.options.resilience.zne.noise_factors = factors
jobs.append(estimator.run(clifford_pubs))
results = [job.result() for job in jobs]
# Perform a noiseless simulation
ideal_results = analyzer.ideal_sim(clifford_pubs)
# Look at the mean absolute difference to quickly tell the best choice for your options
for factors, res in zip(noise_factors, results):
d = rdiff(ideal_results[0], res[0])
print(
f"Mean absolute difference for factors {factors}:\n {np.round(np.mean(d), 2)}%\n"
)
Mean absolute difference for factors [1, 1.1]:
6.83%
Mean absolute difference for factors [1, 1.1, 1.2]:
8.76%
Mean absolute difference for factors [1, 1.5, 2]:
8.03%
Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
10.17%
Mean absolute difference for factors [1, 4]:
8.02%
Rezultatul cu cea mai mică diferență sugerează ce opțiuni să alegi.
Pașii următori
- Învață despre Simularea exactă și cu zgomot cu primitivele Qiskit Aer.
- Învață despre opțiunile disponibile în Qiskit Runtime.
- Învață despre Tehnici de atenuare și suprimare a erorilor.
- Vizitează subiectul Transpilare cu manageri de trecere.
- Învață cum să transpilezi Circuit-uri ca parte a fluxurilor de lucru Qiskit patterns folosind Qiskit Runtime.
- Consultă documentația API a instrumentelor de depanare.