Перейти до основного вмісту
Цей проєкт у бета-версії. Знайшов(-ла) помилку? Відкрити issue на GitHub. Є запитання чи ідея? Запитати в Discussions — будемо раді твоєму відгуку!
Яка якість перекладу на цій сторінці?

Побудова експерименту навчання

Цей посібник показує, як побудувати стандартний експеримент навчання з нуля та використати його, щоб навчити модель на змодельованих зашумлених даних.

  1. Визнач набір гейтів на кільці кубітів

  2. Обери 2-локальну модель Паулі-Ліндблада

  3. Побудуй експеримент навчання

  4. Згенеруй схеми

  5. Налаштуй локальну симуляцію

  6. Запусти програму

  7. Проаналізуй дані

Запуск на реальному обладнанні

Наведені нижче схеми симулюються локально, тому для цього покрокового посібника не потрібні облікові дані IBM Quantum®. Щоб натомість запустити його на реальному пристрої:

  • Крок 1: заміни FakeMarrakesh на реальний бекенд.

  • Крок 5: передай цей бекенд як режим executor і прибери параметри симулятора.

1. Визнач набір гейтів на кільці кубітів​

Побудуй QiskitGateSet із Target бекенда та підмножини його кубітів — тут це дванадцятикубітне кільце FakeMarrakesh — а потім додай до нього один гейт: шар із шести гейтів CZ, що покривають це кільце. За замовчуванням набір гейтів ініціалізується гейтом підготовки P і гейтом вимірювання 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']
Запуск на реальному обладнанні
from qiskit_ibm_runtime import QiskitRuntimeService

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

Виклич draw(), щоб побачити гейт на топології пристрою: кільце кубітів у наборі гейтів і те, які їхні пари гейт заплутує.

gate_set.draw()

2. Обери 2-локальну модель Паулі-Ліндблада​

Визнач, які генератори Паулі-Ліндблада може мати шум, за допомогою PauliLindbladModel. Побудуй цей набір генераторів зі зв’язності набору гейтів за допомогою k_local() — тут це кожен 2-локальний оператор Паулі на з’єднаних парах кубітів унітарного гейта та однокубітні оператори Паулі для підготовки й вимірювання.

from qiskit_noise_learning.models import PauliLindbladModel

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

Виведи кількість невідомих параметрів для моделі шуму кожного гейта.

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

3. Побудуй експеримент навчання​

Зберіть Experiment, компонуючи стадії побудови. Кожна стадія читає те, що записали попередні, тому порядок має значення:

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

Спостерігай ранг матриці плану:

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

4. Згенеруй схеми​

Скомпілюй експеримент за допомогою ExecutorCircuitGenerator, згенерувавши QuantumProgram — одну параметризовану шаблонну схему на кожну глибину фрагмента.

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

Намалюй шаблонну схему для огляду.

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

../_images/4088c7e98baeb5b56925c6c1f276159582c5f7040fbba76de438b919210bcc5f.png

5. Налаштуй локальну симуляцію​

Щоб визначити модель шуму для симуляції, ми використовуємо генератори, що вже є в моделі, з випадковими ймовірностями, щоб побудувати 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}

Ці мапи такі ж широкі, як і пристрій, бо модель виражена в індексації кубітів backend, а симулятор хоче, щоб кожна мапа була такою ж широкою, як гейт, до якого вона застосовується, з індексами Паулі за кубітами гейта у зростальному фізичному порядку. Звузь їх за допомогою keep_qubits(), який відкидає все поза кільцем:

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}

Симулятор називає шум шару за інструкцією box, з якої шар було побудовано; її повертає to_instruction() для будь-якого гейта в наборі гейтів. Зістав кожен box зі своєю мапою, використай метод симуляції стабілізаторів і встанови seed_simulator, щоб змоделювані дані були відтворюваними:

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,
}
},
)
Запуск на реальному обладнанні
executor = Executor(mode=backend)

6. Запусти програму​

Запусти програму, потім відформатуй результати за допомогою collect(), щоб отримати Fit — контейнер, з якого читають і в який пишуть стадії аналізу.

Цей крок однаковий незалежно від того, чи executor вказує на симулятор, чи на реальний пристрій.

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

7. Проаналізуй дані​

Побудуй конвеєр аналізу навколо апроксимації моделі невід’ємним методом найменших квадратів.

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)

Побудуй виміряні середні значення спостережуваної проти їхніх апроксимованих експонент, по одному підграфіку на кожну пару CZ, щоб побачити, чи спади, які апроксимуються, взагалі є експоненційними.

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

Заміни exponential_fit на model_prediction, щоб порівняти ті самі дані зі спадами, які випливають зі ймовірностей, отриманих розв’язанням невід’ємним методом найменших квадратів.

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