Construiește un experiment de învățare
Acest tutorial arată cum să construiești de la zero un experiment de învățare standard și să îl folosești pentru a învăța un model din date zgomotoase simulate.
-
Definește un set de porți pe un inel de qubiți
-
Alege un model Pauli-Lindblad 2-local
-
Construiește experimentul de învățare
-
Generează circuitele
-
Configurează simularea locală
-
Rulează programul
-
Analizează datele
Circuitele de mai jos sunt simulate local, așa că acest ghid nu necesită credențiale IBM Quantum®. Pentru a-l rula în schimb pe un dispozitiv real:
-
Pasul 1: înlocuiește
FakeMarrakeshcu un Backend real. -
Pasul 5: transmite acel Backend ca mod al executorului și elimină opțiunile simulatorului.
1. Definește un set de porți pe un inel de qubiți
Construiește un QiskitGateSet dintr-un Target de Backend și un subset al qubiților săi — aici un inel de doisprezece qubiți al FakeMarrakesh — apoi adaugă-i o poartă: un strat de șase porți CZ care acoperă acel inel. Implicit, setul de porți este inițializat cu o poartă de pregătire P și o poartă de măsurare 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")
Apelează draw() pentru a vedea poarta pe topologia dispozitivului: inelul de qubiți din setul de porți și care perechi dintre ei sunt încâlcite de poartă.
gate_set.draw()
2. Alege un model Pauli-Lindblad 2-local
Decide ce generatori Pauli-Lindblad poate avea zgomotul cu un PauliLindbladModel. Construiește acel set de generatori din conectivitatea setului de porți cu k_local() — aici fiecare Pauli 2-local pe perechile de qubiți conectați ai porții unitare și Pauli pe un singur qubit pentru pregătire și măsurare.
from qiskit_noise_learning.models import PauliLindbladModel
model = PauliLindbladModel.k_local(gate_set, gate_k={"cz_gate": 2, "M": 1, "P": 1})
Afișează numărul de parametri necunoscuți pentru modelul de zgomot al fiecărei porți.
{name: len(generators) for name, generators in model.generators.items()}
{'M': 12, 'P': 12, 'cz_gate': 144}
3. Construiește experimentul de învățare
Asamblează un Experiment compunând etape de construcție. Fiecare etapă citește ce au scris etapele anterioare, deci ordinea contează:
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
Observă rangul matricei de proiectare:
print(f"Design matrix rank: {experiment.design_matrix.rank}")
Design matrix rank: 120
4. Generează circuitele
Compilează experimentul cu un ExecutorCircuitGenerator, generând un QuantumProgram — un circuit șablon parametrizat pentru fiecare adâncime de fragment.
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
Desenează un circuit șablon pentru a-l inspecta.
quantum_program.items[0].circuit.draw("mpl", idle_wires=False, fold=False)
5. Configurează simularea locală
Pentru a defini modelul de zgomot pentru simulare, folosim generatorii deja existenți în model, cu rate aleatoare, pentru a construi un 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}
Aceste hărți sunt la fel de late ca dispozitivul, deoarece modelul este exprimat în indexarea qubiților din backend, dar simulatorul vrea ca fiecare hartă să fie la fel de lată ca poarta căreia i se aplică, cu indicii Pauli parcurgând qubiții porții în ordine fizică crescătoare. Îngusteaz-le cu keep_qubits(), care elimină prin urmă tot ce se află în afara inelului:
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}
Simulatorul denumește zgomotul unui strat după instrucțiunea de cutie din care a fost construit stratul, pe care to_instruction() o returnează pentru orice poartă din setul de porți. Asociază fiecare cutie cu harta ei, folosește metoda de simulare cu stabilizatori și setează seed_simulator pentru a face datele simulate reproductibile:
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. Rulează programul
Rulează programul, apoi formatează rezultatele cu collect(), pentru a produce un Fit — containerul din care citesc și în care scriu etapele de analiză.
Acest pas este același indiferent dacă executorul este orientat către un simulator sau către un dispozitiv real.
job = executor.run(quantum_program)
fit = circuit_generator.collect(job.result())
7. Analizează datele
Construiește un flux de analiză în jurul potrivirii prin cele mai mici pătrate nenegative a modelului.
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)
Reprezintă grafic mediile observabilelor măsurate față de exponențialele lor potrivite, câte un subgrafic pentru fiecare pereche CZ, pentru a vedea dacă descreșterile potrivite sunt deloc exponențiale.
fit.plot_qubit_pair_decays(
pairs=cz_pairs,
observable_type="means",
exponential_fit=True,
)
Înlocuiește exponential_fit cu model_prediction pentru a compara aceleași date cu descreșterile implicate de ratele rezultate din rezolvarea prin cele mai mici pătrate nenegative.
fit.plot_qubit_pair_decays(
pairs=cz_pairs,
observable_type="means",
model_prediction=True,
)
