Simulează modelul Ising 2D cu câmp înclinat folosind funcția QESEM
Funcțiile Qiskit sunt o funcționalitate experimentală disponibilă doar utilizatorilor IBM Quantum® Premium Plan, Flex Plan și On-Prem (prin IBM Quantum Platform API) Plan. Ele sunt în stadiu de previzualizare și pot fi modificate.
Estimare de utilizare: 20 de minute pe un procesor Heron r2. (NOTĂ: Aceasta este doar o estimare. Timpul tău de execuție poate varia.)
Fundal
Acest tutorial arată cum să folosești QESEM, Funcția Qiskit de la Qedma, pentru a simula dinamica unui model canonic de spin cuantic — modelul Ising 2D cu câmp înclinat (TFI) cu unghiuri non-Clifford:
unde denotă vecinii apropiați pe o rețea. Simularea evoluției temporale a sistemelor cuantice cu mai mulți corpi este o sarcină dificilă din punct de vedere computațional pentru calculatoarele clasice. Calculatoarele cuantice, prin contrast, sunt concepute în mod natural să realizeze această sarcină eficient. Modelul TFI, în special, a devenit un benchmark popular pe hardware-ul cuantic datorită comportamentului său fizic bogat și implementării prietenoase cu hardware-ul.
În loc să simulăm dinamica în timp continuu, adoptăm modelul Ising cu „kicks" periodic corelat. Dinamica poate fi exprimată exact ca un Circuit cuantic periodic, în care fiecare pas de evoluție constă din trei straturi de Gate-uri fracționale cu doi Qubiți , intercalate cu straturi de Gate-uri cu un singur Qubit și .
Vom folosi unghiuri generice care sunt dificile atât pentru simularea clasică, cât și pentru atenuarea erorilor. Mai exact, am ales , și , plasând modelul departe de orice punct integrabil.
În acest tutorial vom face următoarele:
- Estimăm timpul de execuție QPU preconizat pentru atenuarea completă a erorilor folosind funcțiile de estimare a timpului analitică și empirică ale QESEM.
- Construim și simulăm circuitul modelului Ising 2D cu câmp înclinat folosind layout-uri de Qubiți inspirate din hardware și straturi de Gate-uri.
- Vizualizăm conectivitatea Qubiților dispozitivului și subgrafurile selectate pentru experimentul tău.
- Demonstrăm utilizarea retropropagării operatorilor (OBP) pentru a reduce adâncimea circuitului. Această tehnică elimină operații de la sfârșitul circuitului cu prețul mai multor măsurători ale operatorului.
- Realizăm atenuarea erorilor (EM) nebiasată pentru mai mulți observabili simultan folosind QESEM, comparând rezultatele ideale, zgomotoase și atenuate.
- Analizăm și reprezentăm grafic impactul atenuării erorilor asupra magnetizării pentru diferite adâncimi ale circuitului.
Notă: OBP va returna în general un set de observabili posibil necomutabili. QESEM optimizează automat bazele de măsurare atunci când observabilii țintă conțin termeni necomutabili. Generează seturi candidate de baze de măsurare folosind mai mulți algoritmi euristici și selectează setul care minimizează numărul de baze distincte. Aceasta înseamnă că QESEM grupează observabilii compatibili în baze comune pentru a reduce numărul total de configurații de măsurare necesare, îmbunătățind eficiența.
Despre QESEM
QESEM este un software fiabil, cu acuratețe ridicată, bazat pe caracterizare, care implementează atenuarea erorilor cuasi-probabilistice eficiente și nebiasate. Este conceput pentru a atenua erorile în circuitele cuantice generice și este agnostic față de aplicație. A fost validat pe diverse platforme hardware, inclusiv experimente la scară utilitate pe dispozitivele IBM® Eagle și Heron. Etapele fluxului de lucru QESEM sunt următoarele:
- Caracterizarea dispozitivului - cartografiază fidelitățile Gate-urilor și identifică erorile coerente, oferind date de calibrare în timp real. Această etapă asigură că atenuarea valorifică operațiunile cu cea mai mare fidelitate disponibile.
- Transpilarea conștientă de zgomot - generează și evaluează mapări alternative ale Qubiților, seturi de operații și baze de măsurare, selectând varianta care minimizează timpul de execuție QPU estimat, cu paralelizare opțională pentru a accelera colectarea datelor.
- Suprimarea erorilor - redefinește Gate-urile native, aplică „Pauli twirling" și optimizează controlul la nivel de puls (pe platformele suportate) pentru a îmbunătăți fidelitatea.
- Caracterizarea circuitului - construiește un model de eroare locală personalizat și îl potrivește la măsurătorile QPU pentru a cuantifica zgomotul rezidual.
- Atenuarea erorilor - construiește descompuneri cuasi-probabilistice de mai multe tipuri și eșantionează din ele într-un proces adaptiv care minimizează timpul QPU de atenuare și sensibilitatea la fluctuațiile hardware-ului, obținând acuratețe ridicată la volume mari de circuit.
Pentru mai multe informații despre QESEM și un experiment la scară utilitate al acestui model pe un subgraf de 103 Qubiți cu conectivitate ridicată din geometria heavy-hex nativă a ibm_marrakesh, consultă Atenuarea erorilor de înaltă acuratețe fiabilă pentru circuite cuantice la scară utilitate.

Cerințe
Instalează următoarele pachete Python înainte de a rula notebook-ul:
- Qiskit SDK v2.0.0 sau o versiune ulterioară (
pip install qiskit) - Qiskit Runtime v0.40.0 sau o versiune ulterioară (
pip install qiskit-ibm-runtime) - Qiskit Functions Catalog v0.8.0 sau o versiune ulterioară (
pip install qiskit-ibm-catalog) - Addon Qiskit pentru Operator Backpropagation v0.3.0 sau o versiune ulterioară (
pip install qiskit-addon-obp) - Addon Qiskit Utils v0.1.1 sau o versiune ulterioară (
pip install qiskit-addon-utils) - Simulator Qiskit Aer v0.17.1 sau o versiune ulterioară (
pip install qiskit-aer) - Matplotlib v3.10.3 sau o versiune ulterioară (
pip install matplotlib)
Configurare
Mai întâi, importă bibliotecile relevante:
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-obp qiskit-addon-utils qiskit-aer qiskit-ibm-catalog qiskit-ibm-runtime
%matplotlib inline
from typing import Sequence
import matplotlib.pyplot as plt
import numpy as np
import qiskit
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_aer import AerSimulator
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.visualization import (
plot_gate_map,
)
Apoi, autentifică-te folosind cheia ta API din panoul de control IBM Quantum Platform. Selectează Funcția Qiskit după cum urmează. (Rețineți că, pentru securitate, este recomandat să salvezi acreditările contului tău în mediul tău local, dacă ești pe o mașină de încredere, pentru a nu fi nevoit să introduci cheia API de fiecare dată când te autentifici.)
# Paste here your instance and token strings
instance = "YOUR_INSTANCE"
token = "YOUR_TOKEN"
channel = "ibm_quantum_platform"
catalog = QiskitFunctionsCatalog(
channel=channel, token=token, instance=instance
)
qesem_function = catalog.load("qedma/qesem")
Pasul 1: Mapează intrările clasice la o problemă cuantică
Începem prin a defini o funcție care creează circuitul Trotter:
def trotter_circuit_from_layers(
steps: int,
theta_x: float,
theta_z: float,
theta_zz: float,
layers: Sequence[Sequence[tuple[int, int]]],
init_state: str | None = None,
) -> qiskit.QuantumCircuit:
"""
Generates an ising trotter circuit
:param steps: trotter steps
:param theta_x: RX angle
:param theta_z: RZ angle
:param theta_zz: RZZ angle
:param layers: list of layers (can be list of layers in device)
:param init_state: Initial state to prepare. If None, will not prepare any state. If "+", will
add Hadamard gates to all qubits.
:return: QuantumCircuit
"""
qubits = sorted({i for layer in layers for edge in layer for i in edge})
circ = qiskit.QuantumCircuit(max(qubits) + 1)
if init_state == "+":
print("init_state = +")
for q in qubits:
circ.h(q)
for _ in range(steps):
for q in qubits:
circ.rx(theta_x, q)
circ.rz(theta_z, q)
for layer in layers:
for edge in layer:
circ.rzz(theta_zz, *edge)
circ.barrier(qubits)
return circ
Apoi creăm o funcție pentru a calcula valorile de așteptare ideale folosind AerSimulator.
Rețineți că pentru circuite mari (30 sau mai mulți Qubiți) recomandăm utilizarea valorilor precalculate din simulările PEPS cu propagare prin credințe (BP). Acest cod include valori precalculate pentru 35 de Qubiți ca exemplu, bazate pe abordarea BP pentru evoluția unei rețele tensor PEPS introdusă în această lucrare (pe care o numim PEPS-BP), folosind pachetul Python pentru rețele tensor quimb.
def calculate_ideal_evs(circ, obs, num_qubits, step):
# Predefined results for large circuits - calculated using bppeps for 3, 5, 7, 9 trotter steps
predefined_35 = [
0.79537,
0.78653,
0.79699,
]
if num_qubits == 35:
print(
"Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits."
)
return predefined_35[step]
else:
simulator = AerSimulator()
# Use Estimator primitive to get expectation value
estimator = Estimator(simulator)
sim_result = estimator.run([(circ, [obs])], precision=0.0001).result()
# Extracting the result
ideal_values = sim_result[0].data.evs[0]
return ideal_values
Folosim o mapare a stratului bazată pe hardware preluată din dispozitivul Heron, din care decupăm straturile în funcție de numărul de Qubiți pe care dorim să îi simulăm. Definim subgrafuri pentru 10, 21, 28 și 35 de Qubiți care mențin o structură 2D (te simți liber să schimbi cu subgraful tău preferat):
LAYERS_HERON_R2 = [ # the full set of hardware layers for Heron r2
[
(2, 3),
(6, 7),
(10, 11),
(14, 15),
(20, 21),
(16, 23),
(24, 25),
(17, 27),
(28, 29),
(18, 31),
(32, 33),
(19, 35),
(36, 41),
(42, 43),
(37, 45),
(46, 47),
(38, 49),
(50, 51),
(39, 53),
(60, 61),
(56, 63),
(64, 65),
(57, 67),
(68, 69),
(58, 71),
(72, 73),
(59, 75),
(76, 81),
(82, 83),
(77, 85),
(86, 87),
(78, 89),
(90, 91),
(79, 93),
(94, 95),
(100, 101),
(96, 103),
(104, 105),
(97, 107),
(108, 109),
(98, 111),
(112, 113),
(99, 115),
(116, 121),
(122, 123),
(117, 125),
(126, 127),
(118, 129),
(130, 131),
(119, 133),
(134, 135),
(140, 141),
(136, 143),
(144, 145),
(137, 147),
(148, 149),
(138, 151),
(152, 153),
(139, 155),
],
[
(1, 2),
(3, 4),
(5, 6),
(7, 8),
(9, 10),
(11, 12),
(13, 14),
(21, 22),
(23, 24),
(25, 26),
(27, 28),
(29, 30),
(31, 32),
(33, 34),
(40, 41),
(43, 44),
(45, 46),
(47, 48),
(49, 50),
(51, 52),
(53, 54),
(55, 59),
(61, 62),
(63, 64),
(65, 66),
(67, 68),
(69, 70),
(71, 72),
(73, 74),
(80, 81),
(83, 84),
(85, 86),
(87, 88),
(89, 90),
(91, 92),
(93, 94),
(95, 99),
(101, 102),
(103, 104),
(105, 106),
(107, 108),
(109, 110),
(111, 112),
(113, 114),
(120, 121),
(123, 124),
(125, 126),
(127, 128),
(129, 130),
(131, 132),
(133, 134),
(135, 139),
(141, 142),
(143, 144),
(145, 146),
(147, 148),
(149, 150),
(151, 152),
(153, 154),
],
[
(3, 16),
(7, 17),
(11, 18),
(22, 23),
(26, 27),
(30, 31),
(34, 35),
(21, 36),
(25, 37),
(29, 38),
(33, 39),
(41, 42),
(44, 45),
(48, 49),
(52, 53),
(43, 56),
(47, 57),
(51, 58),
(62, 63),
(66, 67),
(70, 71),
(74, 75),
(61, 76),
(65, 77),
(69, 78),
(73, 79),
(81, 82),
(84, 85),
(88, 89),
(92, 93),
(83, 96),
(87, 97),
(91, 98),
(102, 103),
(106, 107),
(110, 111),
(114, 115),
(101, 116),
(105, 117),
(109, 118),
(113, 119),
(121, 122),
(124, 125),
(128, 129),
(132, 133),
(123, 136),
(127, 137),
(131, 138),
(142, 143),
(146, 147),
(150, 151),
(154, 155),
(0, 1),
(4, 5),
(8, 9),
(12, 13),
(54, 55),
(15, 19),
],
]
subgraphs = { # the subgraphs for the different qubit counts such that it's 2D
10: list(range(22, 29)) + [16, 17, 37],
21: list(range(3, 12)) + list(range(23, 32)) + [16, 17, 18],
28: list(range(3, 12))
+ list(range(23, 32))
+ list(range(45, 50))
+ [16, 17, 18, 37, 38],
35: list(range(3, 12))
+ list(range(21, 32))
+ list(range(41, 50))
+ [16, 17, 18, 36, 37, 38],
42: list(range(3, 12))
+ list(range(21, 32))
+ list(range(41, 50))
+ list(range(63, 68))
+ [16, 17, 18, 36, 37, 38, 56, 57],
}
n_qubits = 35 # 21, 28, 35, 42
layers = [
[
edge
for edge in layer
if edge[0] in subgraphs[n_qubits] and edge[1] in subgraphs[n_qubits]
]
for layer in LAYERS_HERON_R2
]
print(layers)
[[(6, 7), (10, 11), (16, 23), (24, 25), (17, 27), (28, 29), (18, 31), (36, 41), (42, 43), (37, 45), (46, 47), (38, 49)], [(3, 4), (5, 6), (7, 8), (9, 10), (21, 22), (23, 24), (25, 26), (27, 28), (29, 30), (43, 44), (45, 46), (47, 48)], [(3, 16), (7, 17), (11, 18), (22, 23), (26, 27), (30, 31), (21, 36), (25, 37), (29, 38), (41, 42), (44, 45), (48, 49), (4, 5), (8, 9)]]
Acum vizualizăm layout-ul Qubiților pe dispozitivul Heron pentru subgraful selectat:
service = QiskitRuntimeService(
channel=channel,
token=token,
instance=instance,
)
backend = service.backend("ibm_fez") # or any available device
selected_qubits = subgraphs[n_qubits]
num_qubits = backend.configuration().num_qubits
qubit_color = [
"#ff7f0e" if i in selected_qubits else "#d3d3d3"
for i in range(num_qubits)
]
plot_gate_map(
backend=backend,
figsize=(15, 10),
qubit_color=qubit_color,
)
plt.show()

Observați că conectivitatea layout-ului de Qubiți ales nu este neapărat liniară și poate acoperi regiuni mari ale dispozitivului Heron în funcție de numărul selectat de Qubiți.
Acum generăm circuitul Trotter și observabilul de magnetizare medie pentru numărul ales de Qubiți și parametri:
# Chosen parameters:
theta_x = 0.53
theta_z = 0.1
theta_zz = 1.0
steps = 9
circ = trotter_circuit_from_layers(steps, theta_x, theta_z, theta_zz, layers)
print(
f"Circuit 2q layers: {circ.depth(filter_function=lambda instr: len(instr.qubits) == 2)}"
)
print("\nCircuit structure:")
circ.draw("mpl", scale=0.8, fold=-1, idle_wires=False)
plt.show()
observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
[("Z", [q], 1 / n_qubits) for q in subgraphs[n_qubits]],
np.max(subgraphs[n_qubits]) + 1,
) # Average magnetization observable
print(observable)
obs_list = [observable]
Circuit 2q layers: 27
Circuit structure:

SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j])
Pasul 2: Optimizarea problemei pentru execuția pe hardware cuantic
Estimarea timpului QPU cu și fără OBP
În general, utilizatorii vor să știe cât timp QPU este necesar pentru experimentul lor. Aceasta este considerată o problemă dificilă pentru calculatoarele clasice.
QESEM oferă două moduri de estimare a timpului pentru a informa utilizatorii despre fezabilitatea experimentelor lor:
- Estimare analitică a timpului - oferă o estimare foarte aproximativă și nu necesită timp QPU. Aceasta poate fi utilizată pentru a testa dacă o trecere de transpilare ar reduce potențial timpul QPU.
- Estimare empirică a timpului (demonstrată aici) - oferă o estimare destul de bună și folosește câteva minute de timp QPU.
În ambele cazuri, QESEM afișează estimarea timpului pentru atingerea preciziei necesare pentru toate observabilele.
run_on_real_hardware = True
precision = 0.05
if run_on_real_hardware:
backend_name = "ibm_fez"
else:
backend_name = "fake_fez"
# Start a job for empirical time estimation
estimation_job_wo_obp = qesem_function.run(
pubs=[(circ, obs_list)],
instance=instance,
backend_name=backend_name, # E.g. "ibm_brisbane"
options={
"estimate_time_only": "empirical", # "empirical" - gets actual time estimates without running full mitigation
"max_execution_time": 120, # Limits the QPU time, specified in seconds.
"default_precision": precision,
},
)
print(estimation_job_wo_obp.job_id)
print(estimation_job_wo_obp.status())
17d3828e-9fdb-482e-8e9b-392f3eefe313
DONE
# Get the result object (blocking method). Use job.status() in a loop for non-blocking.
# This takes 1-3 minutes
result = estimation_job_wo_obp.result()
print(
f"Empirical time estimation (sec): {result[0].metadata['time_estimation_sec']}"
)
Empirical time estimation (sec): 1200
Acum vom utiliza backpropagation de operator (OBP). (Consultă ghidul OBP pentru mai multe detalii despre addon-ul OBP Qiskit.) Vom crea o funcție care generează feliile de Circuit pentru backpropagation:
def run_backpropagation(circ_vec, observable, steps_vec, max_qwc_groups=8):
"""
Runs backpropagation for a list of circuits and observables.
Returns lists of backpropagated circuits and observables.
"""
op_budget = OperatorBudget(max_qwc_groups=max_qwc_groups)
bp_circuit_vec = []
bp_observable_vec = []
for i, circ in enumerate(circ_vec):
slices = slice_by_gate_types(circ)
bp_observable, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=op_budget,
)
bp_circuit = combine_slices(remaining_slices, include_barriers=True)
bp_circuit_vec.append(bp_circuit)
bp_observable_vec.append(bp_observable)
print(f"n.o. steps: {steps_vec[i]}")
print(f"Backpropagated {metadata.num_backpropagated_slices} slices.")
print(
f"New observable has {len(bp_observable.paulis)} terms, which can be combined into "
f"{len(bp_observable.group_commuting(qubit_wise=True))} groups.\n"
f"After truncation, the error in our observable is bounded by {metadata.accumulated_error(0):.3e}"
)
print("-----------------")
return bp_circuit_vec, bp_observable_vec
Apelăm funcția:
bp_circ_vec, bp_obs_vec = run_backpropagation([circ], observable, [steps])
n.o. steps: 9
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
print("The remaining circuit after backpropagation looks as follows:")
bp_circ_vec[-1].draw("mpl", scale=0.8, fold=-1, idle_wires=False)
None
The remaining circuit after backpropagation looks as follows:

Putem observa că backpropagation a redus două straturi ale Circuit-ului. Acum că avem Circuit-ul redus și observabilele extinse, să facem estimarea timpului pentru Circuit-ul cu backpropagation aplicat:
# Start a job for empirical time estimation
estimation_job_obp = qesem_function.run(
pubs=[(bp_circ_vec[-1], [bp_obs_vec[-1]])],
instance=instance,
backend_name=backend_name,
options={
"estimate_time_only": "empirical",
"max_execution_time": 120,
"default_precision": precision,
},
)
print(estimation_job_obp.job_id)
print(estimation_job_obp.status())
8bae699d-a16b-4d39-bbd9-d123fbcce55d
DONE
result_obp = estimation_job_obp.result()
print(
f"Empirical time estimation (sec): {result_obp[0].metadata['time_estimation_sec']}"
)
Empirical time estimation (sec): 900
Observăm că OBP reduce costul de timp pentru atenuarea erorilor din Circuit.
Pasul 3: Execuția folosind primitivele Qiskit
Rularea pe Backend real
Acum rulăm experimentul complet pe câțiva pași Trotter. Numărul de Qubiți, precizia necesară și timpul maxim QPU pot fi modificate în funcție de resursele QPU disponibile. Reține că restricționarea timpului maxim QPU va afecta precizia finală, după cum vei vedea în graficul final de mai jos.
Analizăm patru Circuit-uri cu 5, 7 și 9 pași Trotter la o precizie de 0.05, comparând valorile lor de așteptare ideale, zgomotoase și atenuate la erori:
steps_vec = [5, 7, 9]
circ_vec = []
for steps in steps_vec:
circ = trotter_circuit_from_layers(
steps, theta_x, theta_z, theta_zz, layers
)
circ_vec.append(circ)
Din nou, aplicăm OBP pe fiecare Circuit pentru a reduce timpul de rulare:
bp_circ_vec_35, bp_obs_vec_35 = run_backpropagation(
circ_vec, observable, steps_vec
)
n.o. steps: 5
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
n.o. steps: 7
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
n.o. steps: 9
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
Acum rulăm un lot de joburi QESEM complete. Limităm timpul maxim de rulare QPU pentru fiecare punct, pentru un control mai bun al bugetului QPU.
run_on_real_hardware = True
precision = 0.05
if run_on_real_hardware:
backend_name = "ibm_marrakesh"
else:
backend_name = "fake_fez"
# Running full jobs for:
pubs_list = [
[(bp_circ_vec_35[i], bp_obs_vec_35[i])] for i in range(len(bp_obs_vec_35))
]
# Initiating multiple jobs for different lengths
job_list = []
for pubs in pubs_list:
job_obp = qesem_function.run(
pubs=pubs,
instance=instance,
backend_name=backend_name, # E.g. "ibm_brisbane"
options={
"max_execution_time": 300, # Limits the QPU time, specified in seconds.
"default_precision": 0.05,
},
)
job_list.append(job_obp)
Aici verificăm starea fiecărui job:
for job in job_list:
print(job.status())
DONE
DONE
DONE
DONE
Pasul 4: Post-procesarea și returnarea rezultatului în formatul clasic dorit
Când toate joburile au terminat de rulat, putem compara valoarea lor de așteptare zgomotoasă și cea atenuată.
ideal_values = []
noisy_values = []
error_mitigated_values = []
error_mitigated_stds = []
for i in range(len(job_list)):
job = job_list[i]
result = job.result() # Blocking - takes 3-5 minutes
noisy_results = result[0].metadata["noisy_results"]
ideal_val = calculate_ideal_evs(circ_vec[i], observable, n_qubits, i)
print("---------------------------------")
print(f"Ideal: {ideal_val}")
print(f"Noisy: {noisy_results.evs}")
print(f"QESEM: {result[0].data.evs} \u00b1 {result[0].data.stds}")
ideal_values.append(ideal_val)
noisy_values.append(noisy_results.evs)
error_mitigated_values.append(result[0].data.evs)
error_mitigated_stds.append(result[0].data.stds)
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.79537
Noisy: 0.7039237951821501
QESEM: 0.7828018244130982 ± 0.013257266977728376
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.78653
Noisy: 0.6478583812958806
QESEM: 0.7875259197423828 ± 0.02703045139248604
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.79699
Noisy: 0.6171787879868142
QESEM: 0.6918791909168913 ± 0.0740873782039517
În final, putem reprezenta grafic magnetizarea față de numărul de pași. Aceasta rezumă beneficiul utilizării Funcției Qiskit QESEM pentru atenuarea erorilor fără bias pe dispozitive cuantice zgomotoase.
plt.plot(steps_vec, ideal_values, "--", label="ideal")
plt.scatter(steps_vec, noisy_values, label="noisy")
plt.errorbar(
steps_vec,
error_mitigated_values,
yerr=error_mitigated_stds,
fmt="o",
capsize=5,
label="QESEM mitigation",
)
plt.legend()
plt.xlabel("n.o. steps")
plt.ylabel("Magnetization")
Text(0, 0.5, 'Magnetization')
Al nouălea pas are o marjă de eroare statistică mare deoarece am limitat timpul QPU la 5 minute. Dacă rulezi acest pas timp de 15 minute (după cum sugerează estimarea empirică a timpului), vei obține o marjă de eroare mai mică. Prin urmare, valoarea atenuată va deveni mai apropiată de valoarea ideală.