Skip to main content
IBM Quantum Platform

Déboguer des tâches avec NEAT

  • Le code de cette page a été développé en tenant compte des exigences suivantes. Nous recommandons d'utiliser ces versions ou des versions plus récentes.

    qiskit[all]~=2.5.1
    qiskit-ibm-runtime~=0.47.0
    qiskit-aer~=0.17
    

Vous pouvez utiliser la Neat classe pour analyser l'impact du bruit sur une charge de travail Estimator. Pour vérifier la syntaxe, utilisez le mode de test local.


Neat utilisation de la classe

Avant de soumettre une charge de travail gourmande en ressources pour exécution sur du matériel, vous pouvez utiliser la classe NEAT (Noisy Estimator Analyzer Tool) de l' IBM Quantum, dans le module Compute, pour vérifier que votre charge de travail d'estimation est correctement configurée, qu'elle est susceptible de fournir des résultats précis, qu'elle utilise les options les plus adaptées au problème spécifié, et bien plus encore.

Neat Cliffordiise les circuits d'entrée pour une simulation efficace, tout en conservant sa structure et sa profondeur. Les circuits de Clifford souffrent de niveaux de bruit similaires et constituent un bon moyen d'étudier le circuit original qui nous intéresse.

Commencez par importer les paquets nécessaires et authentifiez-vous auprès du service de calcul d' IBM Quantum.

Préparation de l'environnement

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)

Initialiser un circuit cible

Considérons un circuit à six qubits qui possède les propriétés suivantes :

  • Alternance de rotations aléatoires RZ et de couches de portes CNOT.
  • A une structure en miroir, c'est-à-dire qu'il applique une U unitaire suivie de son inverse.
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)

Output:

Output of the previous code cell

Choisissez les opérateurs de Pauli Z comme observables et utilisez-les pour initialiser les blocs primitifs unifiés (PUB).

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

Output:

Observables: ['ZIIIII', 'IZIIII', 'IIZIII', 'IIIZII', 'IIIIZI', 'IIIIIZ']

Cliffordiser les circuits

Les circuits PUB définis précédemment ne sont pas de type Clifford, ce qui les rend difficiles à simuler classiquement. Cependant, vous pouvez utiliser la méthode Neat to_clifford pour les convertir en circuits de Clifford afin d'obtenir une simulation plus efficace. La méthode to_clifford est une enveloppe autour de la passe ConvertISAToClifford qui peut également être utilisée indépendamment. En particulier, il remplace les portes à un qubit non Clifford du circuit original par des portes à un qubit Clifford, mais il ne modifie pas les portes à deux qubits, le nombre de qubits ou la profondeur du circuit.

Voir Simulation efficace de circuits stabilisateurs avec les primitives Qiskit Aer pour plus d'informations sur la simulation de circuits de Clifford.

Tout d'abord, initialiser 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)

Ensuite, il faut clifforiser les PUB.

clifford_pubs = analyzer.to_clifford(pubs)

clifford_pubs[0].circuit.draw("mpl", idle_wires=0)

Output:

Output of the previous code cell

Application 1 : Analyser l'impact du bruit sur les sorties du circuit

Cet exemple montre comment utiliser Neat pour étudier l'impact de différents modèles de bruit sur les PUB en fonction de la profondeur du circuit en effectuant des simulations dans des conditions idéales (ideal_sim) et bruitées (noisy_sim). Cela peut être utile pour définir des attentes quant à la qualité des résultats expérimentaux avant d'exécuter une tâche sur un QPU. Pour en savoir plus sur les modèles de bruit, consultez Simulation exacte et bruyante avec les primitives Aer de Qiskit.

Les résultats simulés permettent d'effectuer des opérations mathématiques et peuvent donc être comparés entre eux (ou avec des résultats expérimentaux) pour calculer des chiffres de mérite.

Caution

Une QPU peut être affectée par différents types de bruit. Le modèle de bruit Qiskit Aer utilisé ici ne simule que certains d'entre eux et est donc susceptible d'être moins grave que le bruit d'une QPU réelle.

Pour plus de détails sur les erreurs incluses lors de l'initialisation d'un modèle de bruit à partir d'une QPU, voir la référence de l'API Aer NoiseModel API reference.

Commencez par effectuer des simulations classiques idéales et bruyantes.

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

Output:

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.9921875 , 0.99023438, 0.99023438, 0.99023438, 0.97265625,
       0.97070312])), NeatPubResult(vals=array([0.98046875, 0.98632812, 0.98828125, 0.9921875 , 0.96289062,
       0.9765625 ])), NeatPubResult(vals=array([0.96289062, 0.96875   , 0.953125  , 0.953125  , 0.95507812,
       0.9609375 ])), NeatPubResult(vals=array([0.94726562, 0.95507812, 0.93945312, 0.94921875, 0.94921875,
       0.95898438])), NeatPubResult(vals=array([0.91992188, 0.92382812, 0.91015625, 0.921875  , 0.92382812,
       0.9375    ])), NeatPubResult(vals=array([0.90039062, 0.9140625 , 0.90234375, 0.92578125, 0.91601562,
       0.94335938]))])

Ensuite, appliquez des opérations mathématiques pour calculer la différence absolue. Dans la suite du guide, la différence absolue est utilisée comme chiffre de mérite pour comparer des résultats idéaux à des résultats bruyants ou expérimentaux, mais des chiffres de mérite similaires peuvent être établis.

La différence absolue montre que l'impact du bruit augmente avec la taille des circuits.

# 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 "
        f"for circuits with {all_n_layers[idx]} layers:\n  {mean_vals}%\n"
    )

Output:

Mean absolute difference between ideal and noisy results for circuits with 1 layers:
  1.56%

Mean absolute difference between ideal and noisy results for circuits with 2 layers:
  1.89%

Mean absolute difference between ideal and noisy results for circuits with 3 layers:
  4.1%

Mean absolute difference between ideal and noisy results for circuits with 4 layers:
  5.01%

Mean absolute difference between ideal and noisy results for circuits with 5 layers:
  7.72%

Mean absolute difference between ideal and noisy results for circuits with 6 layers:
  8.3%

Vous pouvez suivre ces lignes directrices grossières et simplifiées pour améliorer les circuits de ce type :

  • Si la différence moyenne absolue est supérieure à 90 %, l'atténuation ne sera probablement d'aucune utilité.
  • Si la différence moyenne absolue est inférieure à 90 %, l' amplification probabiliste des erreurs (APE) pourra probablement améliorer les résultats.
  • Si la différence moyenne absolue est inférieure à 80 %, les ZNE avec repli des portes pourront probablement améliorer les résultats.

Comme toutes les différences absolues ci-dessus sont inférieures à 90 %, l'application de la PEA au circuit original devrait améliorer la qualité de ses résultats.

Vous pouvez spécifier différents modèles de bruit dans l'analyseur. L'exemple suivant effectue le même test mais ajoute un modèle de bruit personnalisé.

# 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 "
        f"for circuits with {all_n_layers[idx]} layers:\n  {mean_values}%\n"
    )

Output:

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%

Comme indiqué, étant donné un modèle de bruit, vous pouvez essayer de quantifier l'impact du bruit sur les PUB (version Cliffordiisée des PUB) qui vous intéressent avant de les exécuter sur une QPU.


Application 2 : Comparer différentes stratégies

Cet exemple utilise Neat pour aider à identifier les meilleures options pour vos PUBs. Pour ce faire, considérons l'exécution d'un problème d'estimation avec PEA, qui ne peut pas être simulé avec qiskit_aer. Vous pouvez utiliser Neat pour déterminer les facteurs d'amplification du bruit les plus efficaces, puis utiliser ces facteurs lorsque vous réalisez l'expérience originale sur une 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 determine
# 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 "
        f"{factors}:\n  {np.round(np.mean(d), 2)}%\n"
    )

Output:

Mean absolute difference for factors [1, 1.1]:
  12.04%

Mean absolute difference for factors [1, 1.1, 1.2]:
  3.79%

Mean absolute difference for factors [1, 1.5, 2]:
  4.73%

Mean absolute difference for factors [1, 1.5, 2, 2.5, 3]:
  3.65%

Mean absolute difference for factors [1, 4]:
  2.72%

Le résultat présentant la plus petite différence suggère les options à choisir.


Etapes suivantes

Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.