Skip to main content
IBM Quantum Platform

Lärmlernhilfe

  • Der Code auf dieser Seite wurde unter Berücksichtigung der folgenden Anforderungen entwickelt. Wir empfehlen die Verwendung dieser oder neuerer Versionen.

    qiskit[all]~=2.5.2
    qiskit-ibm-runtime~=0.47.0
    samplomatic~=0.21.0
    

Die Fehlerminderungstechniken PEA und PEC nutzen beide eine Komponente zum Rauschlernen, die auf einem Pauli-Lindblad-Rauschmodell basiert. Diese wird in der Regel während der Ausführung verwaltet, nachdem ein oder mehrere Jobs über qiskit-ibm-runtime übermittelt wurden, ohne dass ein lokaler Zugriff auf das angepasste Rauschmodell besteht. Seit der Version qiskit-ibm-runtimev0.27.1 wurden jedoch eine Klasse NoiseLearner und NoiseLearnerOptions eine zugehörige Klasse erstellt, um die Ergebnisse dieser Experimente zum Lernen mit Rauschen abzurufen. NoiseLearnerResult Diese Ergebnisse können dann lokal als gespeichert und in späteren Experimenten als Eingabe verwendet werden. Diese Seite bietet einen Überblick über die Verwendung und die verfügbaren Optionen.

Außerdem gibt es ab der Version qiskit-ibm-runtimev0.47.0 eine neue NoiseLearnerV3 Klasse, die mit der Executor-Primitive kompatibel ist. Diese neue Version, die ebenfalls Teil des Modells der gerichteten Ausführung ist, bietet Ihnen die Möglichkeit, die Ebenen, die Sie trainieren möchten, explizit anzugeben.

Note

NoiseLearner funktioniert nur mit dem serverseitigen „ EstimatorV2 “ und NoiseLearnerV3 nur mit „Executor“ sowie dem neuen clientseitigen „Estimator“ (eingeführt in qiskit-ibm-runtime v0.50.0 ).


NoiseLearner

Übersicht

In dieser NoiseLearner Vorlesung werden Experimente durchgeführt, die Rauschprozesse auf der Grundlage eines Pauli-Lindblad-Rauschmodells für eine (oder mehrere) Schaltungen charakterisieren. Es verfügt über eine run() Methode, die die Lernversuche ausführt und entweder eine Liste von Schaltkreisen oder ein PUB als Eingabe entgegennimmt und ein Objekt NoiseLearnerResult zurückgibt, das die erlernten Rauschkanäle sowie Metadaten zu den übermittelten Aufträgen enthält. Nachfolgend finden Sie einen Codeausschnitt, der die Verwendung des Hilfsprogramms veranschaulicht.

from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2
from qiskit_ibm_runtime.noise_learner import NoiseLearner
from qiskit_ibm_runtime.options import (
    NoiseLearnerOptions,
    ResilienceOptionsV2,
    EstimatorOptions,
)

# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]

circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
    circuit.cx(pair[0], pair[1])
for pair in odd_edges:
    circuit.cx(pair[0], pair[1])

# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()

# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_learn = pm.run(circuit)

# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend)
job = learner.run([circuit_to_learn])
noise_model = job.result()

Das Ergebnis ist NoiseLearnerResult.data eine Liste von Objekten LayerError , die das Rauschmodell für jede einzelne Verschränkungsschicht enthalten, die zu den Zielschaltungen gehört. Jedes speichert LayerError die Schichteninformationen in Form einer Schaltung und einer Reihe von Qubit-Bezeichnungen sowie die PauliLindbladError für die jeweilige Schicht gelernten Parameter des Rauschmodells.

import numpy

print(
    f"Noise learner result contains {len(noise_model.data)} entries"
    f" and has the following type:\n {type(noise_model)}\n"
)
print(
    f"Each element of `NoiseLearnerResult` then contains"
    f" an object of type:\n {type(noise_model.data[0])}\n"
)
# Results are truncated
with numpy.printoptions(threshold=200):
    print(
        f"And each of these `LayerError` objects possess"
        f" data on the generators for the error channel: \n"
        f"{noise_model.data[0].error.generators}\n"
    )
# Results are truncated
with numpy.printoptions(threshold=200):
    print(
        f"Along with the error rates: \n{noise_model.data[0].error.rates}\n"
    )

Output:

Noise learner result contains 2 entries and has the following type:
 <class 'qiskit_ibm_runtime.utils.noise_learner_result.NoiseLearnerResult'>

Each element of `NoiseLearnerResult` then contains an object of type:
 <class 'qiskit_ibm_runtime.utils.noise_learner_result.LayerError'>

And each of these `LayerError` objects possess data on the generators for the error channel: 
['IIIIIIIIIIIIIIIIIIIIIIIIIIX', 'IIIIIIIIIIIIIIIIIIIIIIIIIIY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIXI',
 'IIIIIIIIIIIIIIIIIIIIIIIIIXX', 'IIIIIIIIIIIIIIIIIIIIIIIIIXY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIXZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIYI',
 'IIIIIIIIIIIIIIIIIIIIIIIIIYX', 'IIIIIIIIIIIIIIIIIIIIIIIIIYY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIYZ', 'IIIIIIIIIIIIIIIIIIIIIIIIIZI',
 'IIIIIIIIIIIIIIIIIIIIIIIIIZX', 'IIIIIIIIIIIIIIIIIIIIIIIIIZY',
 'IIIIIIIIIIIIIIIIIIIIIIIIIZZ', 'IIIIIIIIIIIIIIIIIIIIIIIIXII',
 'IIIIIIIIIIIIIIIIIIIIIIIIXXI', 'IIIIIIIIIIIIIIIIIIIIIIIIXYI',
 'IIIIIIIIIIIIIIIIIIIIIIIIXZI', 'IIIIIIIIIIIIIIIIIIIIIIIIYII',
 'IIIIIIIIIIIIIIIIIIIIIIIIYXI', 'IIIIIIIIIIIIIIIIIIIIIIIIYYI',
 'IIIIIIIIIIIIIIIIIIIIIIIIYZI', 'IIIIIIIIIIIIIIIIIIIIIIIIZII',
 'IIIIIIIIIIIIIIIIIIIIIIIIZXI', 'IIIIIIIIIIIIIIIIIIIIIIIIZYI',
 'IIIIIIIIIIIIIIIIIIIIIIIIZZI', 'IIIIIIIIIIIIIIIIIIIIIIIXIII',
 'IIIIIIIIIIIIIIIIIIIIIIIXXII', 'IIIIIIIIIIIIIIIIIIIIIIIXYII',
 'IIIIIIIIIIIIIIIIIIIIIIIXZII', 'IIIIIIIIIIIIIIIIIIIIIIIYIII',
 'IIIIIIIIIIIIIIIIIIIIIIIYXII', 'IIIIIIIIIIIIIIIIIIIIIIIYYII',
 'IIIIIIIIIIIIIIIIIIIIIIIYZII', 'IIIIIIIIIIIIIIIIIIIIIIIZIII',
 'IIIIIIIIIIIIIIIIIIIIIIIZXII', 'IIIIIIIIIIIIIIIIIIIIIIIZYII',
 'IIIIIIIIIIIIIIIIIIIIIIIZZII', 'IIIIIIIIIIIIIIIIIIIIIIXIIII',
 'IIIIIIIIIIIIIIIIIIIIIIXXIII', 'IIIIIIIIIIIIIIIIIIIIIIXYIII',
 'IIIIIIIIIIIIIIIIIIIIIIXZIII', 'IIIIIIIIIIIIIIIIIIIIIIYIIII',
 'IIIIIIIIIIIIIIIIIIIIIIYXIII', 'IIIIIIIIIIIIIIIIIIIIIIYYIII',
 'IIIIIIIIIIIIIIIIIIIIIIYZIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIII',
 'IIIIIIIIIIIIIIIIIIIIIIZXIII', 'IIIIIIIIIIIIIIIIIIIIIIZYIII',
 'IIIIIIIIIIIIIIIIIIIIIIZZIII', 'IIIIIIIIIIIIIIIIIIIIIXIIIII',
 'IIIIIIIIIIIIIIIIIIIIIXIIIIX', 'IIIIIIIIIIIIIIIIIIIIIXIIIIY',
 'IIIIIIIIIIIIIIIIIIIIIXIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIYIIIII',
 'IIIIIIIIIIIIIIIIIIIIIYIIIIX', 'IIIIIIIIIIIIIIIIIIIIIYIIIIY',
 'IIIIIIIIIIIIIIIIIIIIIYIIIIZ', 'IIIIIIIIIIIIIIIIIIIIIZIIIII',
 'IIIIIIIIIIIIIIIIIIIIIZIIIIX', 'IIIIIIIIIIIIIIIIIIIIIZIIIIY',
 'IIIIIIIIIIIIIIIIIIIIIZIIIIZ', 'IIIIIIIIIIIIIIIIIIIIXIIIIII',
 'IIIIIIIIIIIIIIIIIIIIXIXIIII', 'IIIIIIIIIIIIIIIIIIIIXIYIIII',
 'IIIIIIIIIIIIIIIIIIIIXIZIIII', 'IIIIIIIIIIIIIIIIIIIIYIIIIII',
 'IIIIIIIIIIIIIIIIIIIIYIXIIII', 'IIIIIIIIIIIIIIIIIIIIYIYIIII',
 'IIIIIIIIIIIIIIIIIIIIYIZIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIII',
 'IIIIIIIIIIIIIIIIIIIIZIXIIII', 'IIIIIIIIIIIIIIIIIIIIZIYIIII', ...]

Along with the error rates: 
[0.00037 0.00043 0.00032 ... 0.00031 0.00035 0.00017]

Das Attribut LayerError.error des Rausch-Lernergebnisses enthält die Generatoren und Fehlerraten des angepassten Pauli-Lindblad-Modells, das folgende Form hat

Λ(ρ)=exp⁡∑jrj(PjρPj†−ρ),\Lambda(\rho) = \exp{\sum_j r_j \left(P_j \rho P_j^\dagger - \rho\right)},

wobei rjr_j die LayerError.rates und PjP_j die in LayerError.generators angegebenen Pauli-Operatoren sind.

Optionen zum Erlernen von Geräuschen

Bei der Instanziierung eines NoiseLearner Objekts können Sie zwischen mehreren Eingabeoptionen wählen. Diese Optionen sind in der Klasse qiskit_ibm_runtime.options.NoiseLearnerOptions „“ zusammengefasst und umfassen unter anderem die Möglichkeit, die maximale Anzahl der zu lernenden Schichten, die Anzahl der Randomisierungen sowie die Twirling-Strategie festzulegen. Ausführliche Informationen finden Sie in der NoiseLearnerOptions API-Dokumentation.

Im Folgenden finden Sie ein einfaches Beispiel, das zeigt, wie man das NoiseLearnerOptions in einem NoiseLearner Experiment verwendet:

# Build a GHZ circuit
circuit = QuantumCircuit(10)
circuit.h(0)
circuit.cx(range(0, 9), range(1, 10))
# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()

# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_to_run = pm.run(circuit_to_learn)

# Instantiate a NoiseLearnerOptions object
learner_options = NoiseLearnerOptions(
    max_layers_to_learn=3, num_randomizations=32, twirling_strategy="all"
)

# Instantiate a NoiseLearner object and execute the noise learning program
learner = NoiseLearner(mode=backend, options=learner_options)
job = learner.run([circuit_to_run])
noise_model = job.result()

Eingangsrauschmodell zu einer Primitive

Das auf der Schaltung trainierte Rauschmodell kann auch als Eingabe für die Primitive EstimatorV2 „ IBM Quantum “ verwendet werden. Dies kann auf verschiedene Arten an die Primitive übergeben werden. Die folgenden drei Beispiele zeigen, wie Sie das Rauschmodell direkt an das estimator.options Attribut übergeben können, indem Sie vor der Instanziierung einer Estimator-Primitive ein ResilienceOptionsV2 Objekt verwenden und ein entsprechend formatiertes Dictionary übergeben.

# Pass the noise model to the `estimator.options` attribute directly
estimator = EstimatorV2(mode=backend)
estimator.options.resilience.layer_noise_model = noise_model
# Specify options through a ResilienceOptionsV2 object
resilience_options = ResilienceOptionsV2(layer_noise_model=noise_model)
estimator_options = EstimatorOptions(resilience=resilience_options)
estimator = EstimatorV2(mode=backend, options=estimator_options)
# Specify options by using a dictionary
options_dict = {
    "resilience_level": 2,
    "resilience": {"layer_noise_model": noise_model},
}

estimator = EstimatorV2(mode=backend, options=options_dict)

Nachdem das Rauschmodell an das EstimatorV2 Objekt übergeben wurde, kann es wie gewohnt zum Ausführen von Workloads und zur Fehlerbehebung verwendet werden.


NoiseLearnerV3

Übersicht

NoiseLearnerÄhnlich wie führt die NoiseLearnerV3 Klasse Experimente durch, die Rauschprozesse auf der Grundlage eines Pauli-Lindblad-Rauschmodells für eine oder mehrere Schaltungen charakterisieren. Die run() Methode nimmt eine Liste von Anweisungen entgegen, von denen jede eine mit Twirls annotierte BoxOp Struktur sein muss, die ISA -Operationen enthält.

Das Ergebnis eines Auftrags NoiseLearnerV3 enthält eine Liste von NoiseLearnerV3Result Objekten, eines für jede Eingabeanweisung. Der folgende Code zeigt, wie das Hilfsprogramm verwendet wird.

from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap
from qiskit.transpiler import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions


# Build a circuit with two entangling layers
num_qubits = 27
edges = list(CouplingMap.from_line(num_qubits, bidirectional=False))
even_edges = edges[::2]
odd_edges = edges[1::2]

circuit = QuantumCircuit(num_qubits)
for pair in even_edges:
    circuit.cx(pair[0], pair[1])
for pair in odd_edges:
    circuit.cx(pair[0], pair[1])

# Choose a backend to run on
service = QiskitRuntimeService()
backend = service.least_busy()

# Transpile the circuit for execution
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_circuit = pm.run(circuit)

# Run the boxing pass manager to group instructions into annotated boxes
boxing_pm = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=False,
    inject_noise_targets="gates",  # no measurement mitigation
    inject_noise_strategy="uniform_modification",
)
boxed_circuit = boxing_pm.run(isa_circuit)

# Find unique boxed instructions
unique_box_instructions = find_unique_box_instructions(boxed_circuit.data)
print(f"Found {len(unique_box_instructions)} unique layers")
print(
    f"Each instruction is of type {type(unique_box_instructions[0].operation)}"
)
print(
    f"And has annotations: {unique_box_instructions[0].operation.annotations}"
)

# Instantiate a NoiseLearnerV3 object and execute the noise learning program
learner = NoiseLearnerV3(backend)
learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner_job = learner.run(unique_box_instructions)
learner_result = learner_job.result()

Output:

Found 3 unique layers
Each instruction is of type <class 'qiskit.circuit.controlflow.box.BoxOp'>
And has annotations: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r223A', modifier_ref='', site='after')]

Das Ergebnis der Funktion ist eine Liste von NoiseLearnerV3Result Objekten, wobei jedes Objekt einem in ein Eingabefeld eingegebenen Befehlssatz entspricht. NoiseLearnerV3Result verfügt über eine to_pauli_lindblad_map() Methode, die ein PauliLindbladMap Objekt zurückgibt, das Methoden zum Abrufen der Generatoren, Fehlerraten und weiterer Informationen enthält.

print(
    f"The Noise learner V3 result contains {len(learner_result)} entries"
    f" and each has the following type:\n {type(learner_result[0])}\n"
)
noise_map = learner_result[0].to_pauli_lindblad_map()
print(
    f"After converting to PauliLindbladMap, you can extract data "
    f" on the generators for the error channel "
    f"(truncated to 3): \n{noise_map.generators()[:3]}\n"
)
with numpy.printoptions(threshold=20):
    print(
        f"Along with the error rates "
        f"(truncated to 3): \n{noise_map.rates[:3]}\n"
    )

Output:

The Noise learner V3 result contains 3 entries and each has the following type:
 <class 'qiskit_ibm_runtime.results.noise_learner_v3.NoiseLearnerV3Result'>

After converting to PauliLindbladMap, you can extract data  on the generators for the error channel (truncated to 3): 
<QubitSparsePauliList with 3 elements on 27 qubits: [X_5, Y_5, Z_5]>

Along with the error rates (truncated to 3): 
[2.3e-04 9.0e-05 1.1e-04]

Optionen zum Erlernen von Geräuschen

NoiseLearnerV3 bietet verschiedene Optionen, darunter unter anderem die Anzahl der Randomisierungen und die Tiefe der Ebenenpaare. Ähnlich wie bei den Primitiven können Sie die Optionen während oder nach der Instanziierung des NoiseLearnerV3 Objekts festlegen. Das vorangegangene Codebeispiel hat gezeigt, wie man die Optionen shots_per_randomization und num_randomizations einstellt. Ausführliche Informationen finden Sie in der NoiseLearnerV3Options API-Dokumentation.

Ein Rauschmodell in den Executor eingeben

Executor folgt den in den Schaltungsanmerkungen (in Form eines Samplex) und den Optionen festgelegten Designvorgaben. InjectNoise ist die Anmerkung, mit der festgelegt wird, wo Rauschen eingefügt werden soll, und das pauli_lindblad_maps Argument samplex gibt an, welche Rauschkarte verwendet werden soll.

Der Ablauf im vorigen Beispiel läuft über den Boxing-Pass-Manager, der Anweisungen in kommentierte Blöcke gruppiert. Der entsprechende Code wird hier zum besseren Verständnis eingefügt.

  • inject_noise_targets=”gates” legt fest, dass die InjectNoise Anmerkungen zu Feldern hinzugefügt werden sollen, die Verflechtungen enthalten.
  • inject_noise_strategy="uniform_modification" legt fest, dass allen gleichwertigen Feldern mit InjectNoise Anmerkungen dieselben ref Werte und modifier_ref zugewiesen werden.
    • InjectNoise.ref ist eine eindeutige Kennung, die dazu dient, diesem Feld ein Lärmmodell zuzuweisen.
    • InjectNoise.modifier_ref ermöglicht die Skalierung des einer Box zugewiesenen Rauschmodells durch Multiplikationsfaktoren.
boxing_pm = generate_boxing_pass_manager(
    enable_gates=True,
    enable_measures=False,
    inject_noise_targets="gates",  # no measurement mitigation
    inject_noise_strategy="uniform_modification",
)

Der Schaltkreis aus dem vorherigen Beispiel enthält drei Kästchen, von denen zwei Anmerkungen mit unterschiedlichen ref Attributen enthalten InjectNoise (da sie nicht gleichwertig sind).

# box_circuit comes from the example above
for idx, instruction in enumerate(boxed_circuit):
    # The `InjectNoise` annotation defines which boxes to inject noise.
    print(f"Annotations of box #{idx}: {instruction.operation.annotations}\n")

Output:

Annotations of box #0: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r223A', modifier_ref='r223A', site='after')]

Annotations of box #1: [Twirl(group='pauli', dressing='left', decomposition='rzsx'), InjectNoise(ref='r621A', modifier_ref='r621A', site='after')]

Annotations of box #2: [Twirl(group='pauli', dressing='right', decomposition='rzsx')]

Das Ergebnis des NoiseLearnerV3 Auftrags muss in ein Dictionary konvertiert werden, bevor es an den Executor übergeben wird. Die Schlüssel dieses Wörterbuchs sind die InjectNoise.ref Attribute, und die Werte sind die entsprechenden Rauschkarten. Diese Zuordnung teilt Executor mit, welche Rauschmodelle an welcher Stelle eingefügt werden sollen.

Der folgende Code zeigt, wie man den Schaltkreis und das NoiseLearnerV3 Ergebnis aus dem vorherigen Beispiel an den Executor übergibt, der die Schaltkreisvarianten mit den eingefügten Rauschmodellen generiert und diese auf der Hardware ausführt.

from qiskit_ibm_runtime.quantum_program import QuantumProgram
from samplomatic import build

# Generate a quantum program
program = QuantumProgram(shots=1000)

# Build the template circuit and samplex pair
template_circuit, samplex = build(boxed_circuit)

# Convert the NoiseLearnerV3 result to a dictionary
noise_maps = learner_result.to_dict(
    instructions=unique_box_instructions, require_refs=False
)

# Append the samplex item and execute
program.append_samplex_item(
    template_circuit,
    samplex=samplex,
    samplex_arguments={
        "pauli_lindblad_maps": noise_maps,
    },
)

executor = Executor(backend)
executor_job = executor.run(program)

Verwendung von „ NoiseLearnerV3 “ mit dem clientseitigen Estimator

Der folgende Code zeigt, wie das Hilfsprogramm mit dem neuen clientseitigen Estimator verwendet wird

from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, NoiseLearnerV3
from qiskit_ibm_runtime.executor_estimator import Estimator


# 1. Account + backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# 2. Circuit + observable
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.cx(0, 1)

observable = SparsePauliOp("ZZ")

# 3. Transpile to ISA
pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
pubs = [(isa_circuit, isa_observable)]

# 4. Initialize Estimator with options
estimator = Estimator(backend)
estimator.options.resilience.pec_mitigation = True

# 5. Extract the unique boxed layers from PUBs
layers = estimator.find_unique_layers(pubs)

# 6. Learn the noise model for those layers.
learner = NoiseLearnerV3(backend)
learner_job = learner.run(layers)
learner_result = learner_job.result()

# 7. Convert the result to Pauli-Lindblad maps and pass them to Estimator.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
estimator.options.resilience.layer_noise_model = zip(
    layers, pauli_lindblad_maps
)

# 8. Execute the target PUBs.
job = estimator.run(pubs)
result = job.result()

Nächste Schritte

Empfehlungen
War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.