Skip to main content
IBM Quantum Platform

Crea un esperimento didattico

Questo tutorial illustra come creare da zero un esperimento di apprendimento standard e utilizzarlo per addestrare un modello a partire da dati simulati con rumore.

  1. Definire un insieme di porte su un anello di qubit
  2. Scegliere un modello di Pauli-Lindblad a 2 locali
  3. Realizza l'esperimento didattico
  4. Generare circuiti
  5. Configurazione della simulazione locale
  6. Esegui il programma
  7. Analizzare i dati
Esecuzione su hardware reale

I circuiti riportati di seguito sono simulati localmente, pertanto questa guida non richiede credenziali IBM Quantum®. Tre modifiche lo trasformano in un dispositivo reale; ciascuna di esse è nuovamente contrassegnata nei punti in cui è applicabile:

  • Passaggio 1 : sostituire FakeMarrakesh con un backend reale.
  • Passaggio 5 : saltalo.
  • Passaggio 6 : inviare tramite Executor.

1. Definire un insieme di gate su un anello di qubit

Costruisci un QiskitGateSet partendo da un backend Target e da un sottoinsieme dei suoi qubit — in questo caso un anello di dodici qubit di FakeMarrakesh — quindi aggiungi un gate: uno strato di sei gate CZ che copre quell’anello. Per impostazione predefinita, il set di gate viene inizializzato con un gate di preparazione e P un gate di misura M.

from qiskit_ibm_runtime.fake_provider import FakeMarrakesh

from qiskit_noise_learning.gate_sets import QiskitGateSet

backend = FakeMarrakesh()

qubit_subset = [*range(25, 30), *range(37, 39), *range(45, 50)]
cz_pairs = [(25, 26), (27, 28), (29, 38), (37, 45), (46, 47), (48, 49)]

gate_set = QiskitGateSet(backend.num_qubits, target=backend.target, qubit_subset=qubit_subset)

with gate_set.build_new_gate("cz_gate", latex_str=r"\mathrm{CZ}") as builder:
    for pair in cz_pairs:
        builder.circuit.cz(*pair)

list(gate_set)
['M', 'P', 'cz_gate']
Esecuzione su hardware reale
from qiskit_ibm_runtime import QiskitRuntimeService

backend = QiskitRuntimeService().backend("ibm_marrakesh")

Chiamare draw() per visualizzare il gate nella topologia del dispositivo: l'anello di qubit nel set di gate e quali coppie di qubit il gate intreccia.

gate_set.draw()

2. Scegliere un modello di Pauli-Lindblad a 2 locali

Decidi quali generatori di Pauli-Lindblad il rumore può avere con un PauliLindbladModel. Costruisci quel gruppo elettrogeno a partire dalla connettività del set di gate con k_local() — qui ogni operatore di Pauli a 2-locale su coppie di qubit collegate del gate unitario, e operatori di Pauli su singolo qubit per la preparazione e la misurazione.

from qiskit_noise_learning.models import PauliLindbladModel

model = PauliLindbladModel.k_local(gate_set, gate_k={"cz_gate": 2, "M": 1, "P": 1})

Visualizza il numero di parametri sconosciuti per il modello di rumore di ciascun gate.

{name: len(generators) for name, generators in model.generators.items()}
{'M': 12, 'P': 12, 'cz_gate': 144}

3. Realizzare l'esperimento didattico

Assemblare un Experiment combinando le fasi di costruzione. Ogni fase legge ciò che è stato scritto nelle fasi precedenti, quindi l'ordine è importante:

from qiskit_noise_learning.experiment_builder import (
    BindFragmentDepths,
    CompleteSequences,
    EvenDepthVanillaPaths,
    Experiment,
    GenerateInstructionSequences,
    IdentifyRelations,
    MergeInstructionSequences,
    SPAMPaths,
    VanillaInstructionSequences,
)

experiment_builder = (
    # Add standard vanilla learning paths and instruction sequences, and identify relations
    EvenDepthVanillaPaths()
    + VanillaInstructionSequences()
    + IdentifyRelations()
    # Add paths for learning SPAM, then generate and merge instruction sequences for measuring them
    + SPAMPaths()
    + GenerateInstructionSequences()
    + MergeInstructionSequences()
    # Finalize by completing the instruction sequences and setting experiment depths
    + CompleteSequences()
    + BindFragmentDepths([2, 16, 32, 64])
)

experiment = experiment_builder.run(
    Experiment(fidelity_model=model, shots=20, randomizations=50)
)

print(f"Number of paths: {len(experiment.paths)}")
print(f"Number of instruction sequences: {len(experiment.instruction_sequences)}")
Number of paths: 156
Number of instruction sequences: 37

Si osservi il rango della matrice di progetto:

print(f"Design matrix rank: {experiment.design_matrix.rank}")
Design matrix rank: 120

4. Generare circuiti

Compilare l'esperimento con un ExecutorCircuitGenerator, generando un QuantumProgram — un circuito modello parametrizzato per ogni profondità del frammento.

from qiskit_noise_learning.circuit_generator import ExecutorCircuitGenerator

circuit_generator = ExecutorCircuitGenerator(gate_set)
quantum_program = circuit_generator.generate(experiment)

print(f"Number of template circuits: {len(quantum_program.items)}")
Number of template circuits: 5

Disegna uno schema del circuito da esaminare.

quantum_program.items[0].circuit.draw("mpl", idle_wires=False, fold=False)
../_images/dcdaa347d38e1035a0396479bb23edae0b60dfe36ef934e052f8f8b1377174a3.png

5. Configurare la simulazione locale

An AerExecutor esegue un programma su un simulatore Aer locale, iniettando rumore di Pauli-Lindblad nelle barriere che Samplomatic posiziona attorno a ciascun gate ruotato.

A differenza del rumore selezionato con cura nella guida al protocollo, assegnare a ciascuno dei 168 generatori del modello una frequenza casuale indipendente. I generatori del modello sono già esattamente i Pauli da inserire in un PauliLindbladMap, quindi abbiniamo a ciascuno di essi una frequenza e costruiamo direttamente la mappa per ogni gate:

import numpy as np
from qiskit.quantum_info import PauliLindbladMap

rng = np.random.default_rng(1234)

true_maps = {
    name: PauliLindbladMap.from_terms(
        [
            PauliLindbladMap.GeneratorTerm(rate, generator)
            for generator, rate in zip(generators, rng.uniform(2e-4, 1.5e-3, len(generators)))
        ]
    )
    for name, generators in model.generators.items()
}
{name: noise_map.num_qubits for name, noise_map in true_maps.items()}
{'M': 156, 'P': 156, 'cz_gate': 156}

Tali mappe hanno la stessa larghezza del dispositivo, poiché il modello è espresso nell’indicizzazione dei qubit di backend’s, ma AerExecutor vuole che ogni mappa abbia la stessa larghezza del gate a cui si applica, con gli indici di Pauli che scorrono sui qubit del gate in ordine fisico crescente. Restringili con keep_qubits(), che traccia tutto ciò che si trova fuori dall'anello:

noise_dict = {
    name: noise_map.keep_qubits(qubit_subset) for name, noise_map in true_maps.items()
}
{name: noise_map.num_qubits for name, noise_map in noise_dict.items()}
{'M': 12, 'P': 12, 'cz_gate': 12}

Creare un'istanza AerExecutor utilizzando il metodo stabilizzatore. Impostare root_seed in modo da garantire la riproducibilità dei dati simulati.

from qiskit_aer import AerSimulator

from qiskit_noise_learning.aer_executor import AerExecutor

executor = AerExecutor(
    AerSimulator(method="stabilizer"), noise_dict=noise_dict, root_seed=42
)
Esecuzione su hardware reale

Salta completamente questo passaggio.


6. Eseguire il programma

Esegui il programma, quindi formatta i risultati con collect(), per generare un Fit — il contenitore da cui le fasi di analisi leggono e su cui scrivono.

job = executor.run(quantum_program)
fit = circuit_generator.collect(job.result())
Esecuzione su hardware reale
from qiskit_ibm_runtime import Executor

executor = Executor(mode=backend)

7. Analizzare i dati

Realizza una pipeline di analisi basata sull'adattamento del modello tramite il metodo dei minimi quadrati non negativi.

from qiskit_noise_learning.analysis import (
    ComputeObservables,  # computes observables from raw data
    CurveFitObservables,  # performs exponential fitting
    LeastSquaresSolve,  # solves model with (nonnegative) least squares
)

analyzer = ComputeObservables() + CurveFitObservables() + LeastSquaresSolve()

fit = analyzer.run(fit)
/home/kaelyn/projects/open-source/notes/qiskit-noise-learning/qiskit_noise_learning/analysis/model_solve.py:250: UserWarning: 12 of 156 row(s) of the linear system have a non-positive or non-finite uncertainty, so they carry no usable statistical weight. Positions in LinearSystemData.row_labels: 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, and 2 more.
  system = LinearSystemData.from_fit(fit)

Rappresentare graficamente le medie delle grandezze osservabili misurate rispetto alle curve esponenziali ottenute dall'adattamento, con un grafico secondario per ogni coppia CZ , per verificare se i decadimenti oggetto dell'adattamento siano effettivamente esponenziali.

fit.plot_qubit_pair_decays(
    pairs=cz_pairs,
    observable_type="means",
    exponential_fit=True,
)

Scambiare con exponential_fit model_prediction per confrontare gli stessi dati con i decadimenti impliciti nei tassi ottenuti dalla soluzione del metodo dei minimi quadrati non negativi.

fit.plot_qubit_pair_decays(
    pairs=cz_pairs,
    observable_type="means",
    model_prediction=True,
)
Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.