דלג לתוכן הראשי
פרויקט זה בשלב בטא. מצאת באג? פתח issue ב-GitHub. יש שאלה או רעיון? שאל ב-Discussions — נשמח לשמוע ממך!
מה איכות התרגום בדף זה?

בניית ניסוי למידה

המדריך הזה מראה איך לבנות ניסוי למידה סטנדרטי מאפס, ולהשתמש בו כדי ללמוד מודל מתוך נתונים רועשים מדומים.

  1. הגדרת קבוצת שערים על טבעת של Qubit-ים

  2. בחירת מודל Pauli-Lindblad מקומי-2

  3. בניית ניסוי הלמידה

  4. יצירת מעגלים

  5. הגדרת סימולציה מקומית

  6. הרצת התוכנית

  7. ניתוח הנתונים

הרצה על חומרה אמיתית

המעגלים שלהלן מדומים מקומית, ולכן המדריך הזה לא דורש אישורי IBM Quantum®. כדי להריץ אותו במקום זה על התקן אמיתי:

  • שלב 1: מחליפים את FakeMarrakesh ב-Backend אמיתי.

  • שלב 5: מעבירים את ה-Backend הזה כמצב (mode) של ה-executor, ומסירים את אפשרויות הסימולטור.

1. הגדרת קבוצת שערים על טבעת של Qubit-ים​

בונים QiskitGateSet מתוך Target של Backend ותת-קבוצה של ה-Qubit-ים שלו — כאן טבעת של שנים-עשר Qubit-ים של 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() כדי לראות את השער על טופולוגיית ההתקן: טבעת ה-Qubit-ים בקבוצת השערים, ואילו זוגות מהם השער מקשר באיחוד (entangles).

gate_set.draw()

2. בחירת מודל Pauli-Lindblad מקומי-2​

מחליטים אילו מחוללי Pauli-Lindblad מותר שיהיו לרעש באמצעות PauliLindbladModel. בונים את קבוצת המחוללים מהקישוריות של קבוצת השערים באמצעות k_local() — כאן כל פאולי מקומי-2 על זוגות Qubit-ים מחוברים של השער היוניטרי, ופאולי של Qubit בודד להכנה ולמדידה.

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 על ידי הרכבת שלבי builder. כל שלב קורא את מה ששלבים קודמים כתבו, ולכן הסדר חשוב:

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}

המפות האלה רחבות כמו ההתקן, כי המודל מבוטא באינדוקס ה-Qubit-ים של backend, אבל הסימולטור רוצה שכל מפה תהיה רחבה כמו השער שעליו היא מיושמת, כשאינדקסי הפאולי רצים על ה-Qubit-ים של השער בסדר פיזי עולה. מצמצמים אותן באמצעות keep_qubits(), שעושה trace out לכל מה שמחוץ לטבעת:

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}

הסימולטור נותן שם לרעש של שכבה לפי הוראת הקופסה שממנה השכבה נבנתה, ש-to_instruction() מחזירה לכל שער בקבוצת השערים. מזווגים כל קופסה עם המפה שלה, משתמשים בשיטת סימולציית ה-stabilizer, וקובעים את 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)

משרטטים את הממוצעים הנמדדים של ה-observable מול האקספוננטים המותאמים שלהם, תת-גרף אחד לכל זוג 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,
)