Przejdź do głównej treści
Ten projekt jest w wersji beta. Znalazłeś błąd? Otwórz zgłoszenie na GitHub. Masz pytanie lub pomysł? Zapytaj w Discussions — chętnie poznamy Twoją opinię!
Jak oceniasz jakość tłumaczenia tej strony?

Zbuduj eksperyment uczący

Ten samouczek pokazuje, jak zbudować standardowy eksperyment uczący od podstaw i użyć go do nauczenia modelu na symulowanych zaszumionych danych.

  1. Zdefiniuj zbiór bramek na pierścieniu kubitów

  2. Wybierz 2-lokalny model Pauliego-Lindblada

  3. Zbuduj eksperyment uczący

  4. Wygeneruj obwody

  5. Skonfiguruj symulację lokalną

  6. Uruchom program

  7. Przeanalizuj dane

Uruchamianie na prawdziwym sprzęcie

Poniższe obwody są symulowane lokalnie, więc ten przewodnik nie wymaga danych uwierzytelniających IBM Quantum®. Aby zamiast tego uruchomić go na prawdziwym urządzeniu:

  • Krok 1: zastąp FakeMarrakesh prawdziwym backendem.

  • Krok 5: przekaż ten backend jako tryb executora i usuń opcje symulatora.

1. Zdefiniuj zbiór bramek na pierścieniu kubitów​

Zbuduj QiskitGateSet na podstawie Target backendu i podzbioru jego kubitów — tutaj dwunastokubitowego pierścienia FakeMarrakesh — a następnie dodaj do niego jedną bramkę: warstwę sześciu bramek CZ pokrywających ten pierścień. Domyślnie zbiór bramek jest inicjalizowany bramką przygotowania P i bramką pomiaru 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']
Uruchamianie na prawdziwym sprzęcie
from qiskit_ibm_runtime import QiskitRuntimeService

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

Wywołaj draw(), aby zobaczyć bramkę na topologii urządzenia: pierścień kubitów w zbiorze bramek oraz to, które ich pary bramka splata.

gate_set.draw()

2. Wybierz 2-lokalny model Pauliego-Lindblada​

Zdecyduj, jakie generatory Pauliego-Lindblada może mieć szum, za pomocą PauliLindbladModel. Zbuduj ten zbiór generatorów na podstawie łączności zbioru bramek za pomocą k_local() — tutaj każdy 2-lokalny operator Pauliego na połączonych parach kubitów bramki unitarnej oraz jednokubitowe operatory Pauliego dla przygotowania i pomiaru.

from qiskit_noise_learning.models import PauliLindbladModel

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

Wyświetl liczbę nieznanych parametrów modelu szumu każdej bramki.

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

3. Zbuduj eksperyment uczący​

Złóż Experiment, komponując etapy buildera. Każdy etap odczytuje to, co zapisały wcześniejsze etapy, więc kolejność ma znaczenie:

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

Sprawdź rząd macierzy projektowej:

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

4. Wygeneruj obwody​

Skompiluj eksperyment za pomocą ExecutorCircuitGenerator, generując QuantumProgram — jeden sparametryzowany obwód szablonowy na każdą głębokość fragmentu.

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

Narysuj obwód szablonowy, aby go przejrzeć.

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

../_images/4088c7e98baeb5b56925c6c1f276159582c5f7040fbba76de438b919210bcc5f.png

5. Skonfiguruj symulację lokalną​

Aby zdefiniować model szumu do symulacji, używamy generatorów już obecnych w modelu z losowymi współczynnikami, aby skonstruować PauliLindbladMap.

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()
if name != "P"
}
{name: noise_map.num_qubits for name, noise_map in true_maps.items()}
{'M': 156, 'cz_gate': 156}

Te mapy są tak szerokie jak urządzenie, ponieważ model jest wyrażony w indeksowaniu kubitów backend, ale symulator oczekuje, że każda mapa będzie tak szeroka jak bramka, do której się odnosi, z indeksami Pauliego przebiegającymi po kubitach bramki w rosnącej kolejności fizycznej. Zawęź je za pomocą keep_qubits(), który śledzi (traces out) wszystko poza pierścieniem:

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, 'cz_gate': 12}

Symulator nazywa szum warstwy według instrukcji boxa, z której warstwę zbudowano, a którą to_instruction() zwraca dla dowolnej bramki w zbiorze bramek. Połącz każdy box w parę z jego mapą, użyj metody symulacji stabilizatorowej i ustaw seed_simulator, aby symulowane dane były powtarzalne:

from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor

executor = Executor(
mode=AerSimulator(method="stabilizer"),
options={
"simulator": {
"layer_noise_model": [
(gate_set[name].to_instruction(), noise_map)
for name, noise_map in noise_dict.items()
],
"seed_simulator": 42,
}
},
)
Uruchamianie na prawdziwym sprzęcie
executor = Executor(mode=backend)

6. Uruchom program​

Uruchom program, a następnie sformatuj wyniki za pomocą collect(), aby uzyskać Fit — kontener, z którego etapy analizy odczytują dane i do którego je zapisują.

Ten krok jest taki sam niezależnie od tego, czy executor wskazuje na symulator, czy na prawdziwe urządzenie.

job = executor.run(quantum_program)
fit = circuit_generator.collect(job.result())

7. Przeanalizuj dane​

Zbuduj potok analizy wokół dopasowania modelu metodą nieujemnych najmniejszych kwadratów.

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)
/opt/hostedtoolcache/Python/3.15.1/x64/lib/python3.15/site-packages/qiskit_noise_learning/analysis/model_solve.py:250: UserWarning: 7 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, 148, 149, 151, 152, 154.
system = LinearSystemData.from_fit(fit)

Narysuj zmierzone średnie obserwabli na tle ich dopasowanych funkcji wykładniczych, jeden wykres pomocniczy na parę CZ, aby sprawdzić, czy dopasowywane zaniki są w ogóle wykładnicze.

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

Zamień exponential_fit na model_prediction, aby porównać te same dane z zanikami wynikającymi ze współczynników otrzymanych z rozwiązania metodą nieujemnych najmniejszych kwadratów.

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