PEC dengan SLC
Probabilistic error cancellation (PEC) adalah teknik untuk memitigasi noise kuantum dalam perhitungan expectation value observable. Teknik ini memungkinkan kamu menghitung estimasi expectation value yang tidak bias. Cara kerjanya adalah menjalankan sekumpulan circuit ber-noise yang disuntiki invers dari model noise yang dipelajari. Model noise tersebut kemudian diaproksimasi menjadi kanal Pauli dengan memakai Pauli twirling (juga dikenal sebagai randomized compiling). Model noise seperti ini bisa dipelajari secara efisien dan bisa dimitigasi dengan PEC (lihat Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors untuk detail lebih lanjut).
PEC membutuhkan shot overhead eksponensial untuk menghitung estimasi yang tidak bias. Shaded Lightcones (SLC) adalah teknik yang memakai sumber daya klasik untuk membatasi sensitivitas terhadap error di seluruh circuit. Dengan batas-batas ini, pengguna dapat menyetel bias pada estimasi observable secara terkendali.
Panduan ini menunjukkan cara memakai PEC yang dikombinasikan dengan SLC untuk memitigasi noise gate dengan shot overhead yang lebih rendah dibandingkan PEC biasa.
Alur kerjanya sebagai berikut:
- Tentukan circuit dan observable.
- Hitung expectation value ber-noise tanpa mitigasi.
- Hitung batas maju dan mundur untuk bias.
- Pelajari noise layer entangling dengan qiskit-noise-learning.
- Gunakan noise yang dipelajari untuk menggabungkan batas yang dihitung ke dalam matriks local scales.
- Pilih batas bias yang diinginkan berdasarkan trade-off antara shot overhead dan bias.
- Siapkan program PEC + SLC, jalankan program, lalu lakukan post-processing.
- Bandingkan expectation value hasil mitigasi dan shot overhead.
Tentukan circuit dan observable
Siapkan circuit cermin dari evolusi waktu Trotterized sebuah Hamiltonian rantai Ising 1D pada delapan qubit. Cerminkan circuit untuk meniadakan unitary-nya dan membentuk circuit identitas, sehingga expectation value ideal setiap observable diketahui bernilai 1 tanpa perlu menghitungnya. Selain itu, pencerminan menggandakan kedalaman circuit, yang pada gilirannya menggandakan efek noise.
Gunakan sudut agar dinamikanya non-Clifford.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_slc": "qiskit-addon-slc", "qiskit_aer": "qiskit-aer", "qiskit_ibm_runtime": "qiskit-ibm-runtime", "qiskit_mitigation": "qiskit-mitigation", "qiskit_noise_learning": "qiskit-noise-learning", "samplomatic": "samplomatic"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import warnings
import numpy as np
from qiskit.circuit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit_mitigation.utils import get_measurement_bases
warnings.filterwarnings("ignore")
def construct_ising_circuit(
num_qubits: int,
num_trotter_steps: int,
rx_angle: float,
) -> QuantumCircuit:
circuit = QuantumCircuit(num_qubits)
for _ in range(num_trotter_steps):
circuit.rx(rx_angle, range(num_qubits))
for first_qubit in (1, 2):
for idx in range(first_qubit, num_qubits, 2):
# equivalent to Rzz(-pi/2):
circuit.sdg([idx - 1, idx])
circuit.cz(idx - 1, idx)
return circuit
def prepare_basis(
circuit: QuantumCircuit, basis: list[int]
) -> QuantumCircuit:
# basis is a list of integer values from 0 to 3. These map to the basis measurement as:
# 0 = I; 1 = Z; 2 = X; 3 = Y
assert len(basis) == circuit.num_qubits
out_circ = circuit.copy_empty_like()
for qb, bas in enumerate(basis):
if bas == 2:
out_circ.h(qb)
elif bas == 3:
out_circ.rx(-np.pi / 2, qb)
out_circ.barrier()
out_circ.compose(circuit, inplace=True)
return out_circ
def mirror_circuit(circuit: QuantumCircuit) -> QuantumCircuit:
mirror_circ = circuit.copy_empty_like()
mirror_circ.compose(circuit.inverse(), inplace=True)
mirror_circ.barrier()
mirror_circ.compose(circuit, inplace=True)
mirror_circ.measure_active()
return mirror_circ
# define observables
num_qubits = 8 # Width of the Ising chain
# The observable to measure: one X component (on qubit 2) and one Z component (on qubit 5)
target_obs_sparse = [("XZ", [2, 5], 1.0)]
observable = SparsePauliOp.from_sparse_list(
target_obs_sparse, num_qubits=num_qubits
)
# Determine the measurement basis each qubit requires for this observable.
bases, _ = get_measurement_bases(observable)
# Instantiate the mirrored circuit and prepend the basis-preparation gates
num_trotter_steps = 6
rx_angle = np.pi / 4
circuit = construct_ising_circuit(num_qubits, num_trotter_steps, rx_angle)
mirrored_circuit = mirror_circuit(circuit)
mirrored_circuit = prepare_basis(mirrored_circuit, bases[0])
print("Visualization of 1 mirrored Trotter step")
prepare_basis(
mirror_circuit(construct_ising_circuit(num_qubits, 1, rx_angle)), bases[0]
).draw("mpl", scale=0.6, fold=-1)
Visualization of 1 mirrored Trotter step
Hitung expectation value ber-noise tanpa mitigasi
Sebuah QuantumProgram berisi circuit kuantum dan samplex dalam format yang sesuai untuk primitive Executor. MitigationTask.prepare membangun QuantumProgram dari circuit, observable, dan nilai parameter, lalu membungkus circuit dalam box sesuai opsi boxing yang diberikan. Atur opsi boxing measure_annotations ke "change_basis" untuk memutar pengukuran ke basis yang diperlukan, dan tambahkan add_tags: "unique_box" agar simulator dapat memetakan model noise ke box-box circuit. Program ini di-twirl dengan Pauli secara default, yang pada hardware mengubah noise koheren menjadi noise Pauli yang diasumsikan oleh metode mitigasi. num_randomizations menentukan berapa banyak instance twirled dari setiap circuit yang disampel dan shots_per_randomization menentukan berapa banyak shot yang diterima tiap instance, sehingga hasil kali keduanya adalah batas shot per circuit.
Jika kamu punya akses QPU, kamu bisa menjalankan program dengan Executor yang dibangun dari backend milik QiskitRuntimeService. Contoh ini memakai AerSimulator untuk menjalankan program secara lokal. Opsi simulator layer_noise_model miliknya menyuntikkan noise dari model noise Pauli-Lindblad tertentu pada setiap layer gate entangling. Di sini kita membuat model noise Pauli-Lindblad acak dengan error per layered gate (EPLG) sebesar 0.01, hampir satu orde besaran lebih tinggi daripada QPU Heron saat ini, untuk memperbesar efek pemakaian SLC bahkan pada masalah berskala kecil.
from qiskit.circuit import CircuitInstruction
from qiskit.quantum_info import PauliLindbladMap, pauli_basis
from qiskit_aer import AerSimulator
from qiskit_ibm_runtime import Executor
from qiskit_mitigation import MitigationTask, find_combined_unique_layers
from samplomatic import Tag
from samplomatic.utils import get_annotation
def generate_random_pauli_lindblad_noise(
layers,
num_qubits: int,
eplg: float = 3e-3,
seed: int | None = None,
) -> list[tuple[CircuitInstruction, PauliLindbladMap]]:
"""Generate random Pauli-Lindblad noise over the full Pauli basis."""
layer_noise_model = []
for layer in layers:
layer_instructions = layer.operation.params[0]
contain_measurement = any(
inst.name.startswith("measure") for inst in layer_instructions
)
if contain_measurement: # do not add measurement noise
layer_noise = PauliLindbladMap.identity(num_qubits)
else:
edges = [
(inst.qubits[0]._index, inst.qubits[1]._index)
for inst in layer_instructions
] # extract the edges of the layer
basis_paulis = [p for p in pauli_basis(2) if np.sum(p.x + p.z)]
rng = np.random.default_rng(seed=seed)
num_generators = len(basis_paulis)
rates = rng.random(len(basis_paulis)) * eplg / num_generators
layer_noise = PauliLindbladMap.from_sparse_list(
[
(pauli.to_label(), edge, rate)
for pauli, rate in zip(basis_paulis, rates, strict=True)
for edge in edges
],
num_qubits=num_qubits,
)
layer_noise_model.append((layer, layer_noise))
return layer_noise_model
num_randomizations = 400
shots_per_randomization = 16
boxing_options = {"add_tags": "unique_box"}
# find the unique layers to generate the layered noise for
box_layers = find_combined_unique_layers(
[mirrored_circuit], custom_boxing_options=boxing_options
)
layer_noise_model = generate_random_pauli_lindblad_noise(
box_layers, num_qubits, eplg=1e-2, seed=42
)
# a local noisy simulator in place of a QPU backend
noisy_executor = Executor(
AerSimulator(),
options={"simulator": {"layer_noise_model": layer_noise_model}},
)
task = MitigationTask()
program = task.prepare(
circuit=mirrored_circuit,
observables=observable,
parameters=None,
num_randomizations=num_randomizations,
shots_per_randomization=shots_per_randomization,
custom_boxing_options=boxing_options,
)
program._semantic_role = "simulation" # flag the program for the simulator
job_unmit_res = noisy_executor.run(program).result()
unmitigated_res = task.postprocess(job_unmit_res)
print(f"unmitigated result: {unmitigated_res.data.evs[0]}")
unmitigated result: 0.63125
Hitung batas maju dan mundur untuk bias
Sebelum menghabiskan waktu QPU untuk pembelajaran noise, SLC memakai simulasi klasik untuk membatasi kontribusi setiap potensi error terhadap bias observable. Ini dilakukan dalam dua tahap yang saling melengkapi:
-
Propagasi maju: setiap error Pauli dipropagasikan maju melalui sisa circuit dan komutatornya dengan observable dievaluasi. Error yang anti-komut dengan observable adalah satu-satunya jenis yang dapat membiaskan hasil, sehingga tahap ini menghasilkan batas atas untuk dampak maju setiap error.
-
Propagasi mundur: setiap error dipropagasikan mundur ke awal circuit dan komutatornya dengan keadaan awal dievaluasi. Karena circuit dimulai dalam keadaan all-zero, error yang komut dengannya tidak dapat memengaruhi hasil, sehingga batasnya semakin ketat dari arah lain.
Kedua batas ini bersama-sama membentuk kurung yang ketat di sekitar kontribusi kasus terburuk setiap error terhadap bias. Pada langkah berikutnya, batas-batas ini digabungkan dengan laju noise yang dipelajari untuk meranking error mana yang layak dikoreksi dan mana yang aman dibiarkan. Opsi boxing inject_noise_strategy="individual_modification" diperlukan di sini agar setiap anotasi InjectNoise dalam circuit ber-box memiliki identitas unik yang dapat dikaitkan dengan batas SLC.
Untuk masalah berskala lebih besar, perhitungan ini mungkin membutuhkan sumber daya komputasi klasik yang besar.
from qiskit_addon_slc.bounds import (
compute_backward_bounds,
compute_forward_bounds,
tighten_with_speed_limit,
)
from qiskit_mitigation import PEC
slc_boxing_options = {
"add_tags": "unique_box",
"inject_noise_strategy": "individual_modification",
}
slc_pec_task = PEC()
boxed_circuit, noise_model_paulis = (
slc_pec_task.generate_slc_bounds_computation_input(
mirrored_circuit, custom_boxing_options=slc_boxing_options
)
)
forward_bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable
)
tighten_forward_bounds = tighten_with_speed_limit(
forward_bounds, boxed_circuit, noise_model_paulis, observable
)
backward_bounds = compute_backward_bounds(boxed_circuit, noise_model_paulis)
Pelajari noise layer entangling
Untuk menghitung estimasi akhir seberapa besar tiap error memengaruhi bias expectation value, serta seberapa mahal biaya mengoreksinya, kita mempelajari laju noise dan menerapkannya pada batas noise terprediksi yang sudah kita hitung sebelumnya.
PEC (maupun PEC+SLC) mengasumsikan model noise twirled untuk setiap layer gate entangling di dalam circuit. Jika kamu mengasumsikan model noise Markovian, kedalaman circuit diasumsikan tidak memengaruhi kanal noise dari suatu layer entangling. Karena itu, hanya model noise dari setiap layer gate entangling yang unik yang perlu dipelajari. Circuit yang dipakai di panduan ini dapat dibagi menjadi dua layer unik. Merancang circuit dengan himpunan minimal layer entangling unik itu penting jika kamu ingin mempelajari model noise, karena layer-layer unik harus dipelajari secara independen. Jika circuit kamu punya terlalu banyak layer unik, pembelajaran noise bisa menjadi tidak layak karena biaya sampling untuk mempelajari banyak layer.
qiskit-noise-learning dipakai untuk mempelajari noise. Learner mengulang setiap layer sebanyak fragment_depths kali di dalam circuit pembelajarannya, men-twirl setiap circuit sebanyak num_randomizations kali, lalu mencocokkan peluruhan fidelity hasil pengukuran ke model Pauli-Lindblad. Parameter-parameter ini dapat memengaruhi akurasi model yang dipelajari. Makin akurat model noise yang dipelajari, makin akurat pula pembatalan noisenya.
Kode berikut menggambar model noise yang dipelajari dengan kedua layer digabung menjadi satu peta. Setiap qubit berupa diagram pie dari laju X, Y, dan Z berbobot-1 yang dijumlahkan pada kedua layer, dan setiap edge menunjukkan laju berbobot-2 yang dipelajari. Misalnya, laju 3e-4 berarti error itu terjadi kira-kira tiga dari setiap 10.000 kali pemakaian layer.
from qiskit.providers.fake_provider import GenericBackendV2
from qiskit.quantum_info import PauliList
from qiskit_ibm_runtime.results import LayerError, PauliLindbladError
from qiskit_ibm_runtime.visualization.embeddings import Embedding
from qiskit_noise_learning.aer_executor import AerExecutor
from qiskit_noise_learning.noise_learner import LearningOptions, NoiseLearner
from samplomatic import InjectNoise
line_coupling = [(i, i + 1) for i in range(num_qubits - 1)]
backend = GenericBackendV2(
num_qubits=num_qubits,
basis_gates=["id", "rz", "sx", "x", "cz"],
coupling_map=line_coupling,
)
# first learn only the layers PEC will inject noise into, i.e. not the measurement layer
pec_layers = [
layer
for layer in slc_pec_task.find_unique_layers(
mirrored_circuit, slc_boxing_options
)
if get_annotation(layer.operation, InjectNoise)
]
layers = slc_pec_task.find_unique_layers(mirrored_circuit, slc_boxing_options)
learning_options = LearningOptions(
fragment_depths=[2, 4, 8, 16],
num_randomizations=250,
shots_per_randomizations=16,
)
# qiskit-noise-learning is not compatible with the Executor in local mode,
# and requires the simulator present in the package for running simulation
noise_dict = {
get_annotation(layer.operation, Tag).ref: noise_layer
for layer, noise_layer in layer_noise_model
}
noise_learner_simulator = AerExecutor(
qasm_simulator=AerSimulator(), noise_dict=noise_dict
)
learner = NoiseLearner(
backend, options=learning_options, executor=noise_learner_simulator
)
learned_noise = (
learner.run(pec_layers).result().to_dict()
) # keyed by InjectNoise reference
# Draw the learned model on the line, laid out as a line with the qubits in order.
# Composing the two layers' maps sums their rates, giving one map over all the edges.
embedding = Embedding(
[(0, 0), (0, 2), (0, 4), (2, 4), (4, 4), (4, 2), (4, 0), (2, 0)],
line_coupling,
)
first, second = learned_noise.values()
learned_model = first.compose(second).simplify()
error = PauliLindbladError(
PauliList(
learned_model.get_qubit_sparse_pauli_list_copy().to_pauli_list()
),
learned_model.rates,
)
fig = LayerError(circuit, list(range(num_qubits)), error).draw_map(
embedding, radius=0.4, height=460, width=620
)
fig.update_traces(
marker={
"cmin": 0,
"cmax": max(learned_model.rates),
"colorbar": {"tickformat": ".1e", "title": "rate"},
},
selector={"mode": "markers"},
)
fig.show()
Gunakan noise yang dipelajari untuk menggabungkan batas yang dihitung ke dalam matriks local scales
Dengan batas klasik (dari langkah sebelumnya) dan laju noise yang dipelajari di tangan, sekarang kita bisa menghitung matriks local scales — bobot per error yang menentukan seberapa agresif PEC harus membatalkan setiap suku noise.
Ide kunci SLC adalah bahwa tidak semua error perlu dibatalkan sepenuhnya. Error yang batas maju dan mundurnya sama-sama kecil memiliki efek yang dapat diabaikan pada observable terlepas dari lajunya, sehingga bisa dibiarkan sebagian atau seluruhnya tanpa mitigasi. merge_bounds menggabungkan batas klasik dengan laju Pauli-Lindblad yang dipelajari, lalu compute_local_scales memberikan setiap error skala antara 0 (diabaikan sepenuhnya) dan 1 (dibatalkan sepenuhnya), dengan tunduk pada bias_tolerance yang ditentukan pengguna. Error dengan estimasi kontribusi bias besar menerima skala mendekati 1 (perlakuan PEC penuh), sedangkan error dengan kontribusi kecil menerima skala lebih rendah, sehingga mengurangi sampling overhead yang mereka bawa.
SLC menyediakan kenop kontinu di antara dua ekstrem — PEC biasa (bias nol, overhead tertinggi) dan tanpa mitigasi sama sekali (overhead nol, bias noise penuh). Parameter bias_tolerance mengendalikan kenop ini: menyetelnya ke 0 memulihkan PEC penuh, sedangkan nilai yang lebih besar mengizinkan lebih banyak bias residual sebagai ganti sampling overhead γ² yang lebih rendah.
Langkah ini menyapu bias_tolerance pada rentang nilai dan menghitung sampling overhead yang dihasilkan untuk masing-masing. Memplot titik-titik ini terhadap overhead PEC penuh membantu kamu memilih titik kerja yang sesuai dengan anggaran shot kamu. Misalnya, menoleransi bias residual 5 % dapat mengurangi sampling overhead hingga satu orde besaran dibandingkan PEC penuh, sehingga komputasi menjadi layak di QPU saat ini, padahal sebelumnya tidak.
import matplotlib.pyplot as plt
from qiskit_addon_slc.bounds import compute_local_scales, merge_bounds
merged_bounds = merge_bounds(
boxed_circuit,
tighten_forward_bounds,
backward_bounds,
noise_rates=learned_noise,
)
biases = []
costs = []
for bias in [0.0, *np.arange(0.01, 0.11, 0.01).tolist()]:
_, cost_, bias_ = compute_local_scales(
boxed_circuit,
merged_bounds,
noise_rates=learned_noise,
bias_tolerance=bias,
)
biases.append(bias_)
costs.append(cost_)
# Compute tha full PEC overhead and compare it with the overhead and bias of different SLC configurations
vanilla_gamma = slc_pec_task.calculate_gamma(
slc_pec_task.boxed_circuit, learned_noise, 1
)
xticks = np.arange(0, 11, 2)
fig, ax = plt.subplots()
ax.scatter(
[0], [vanilla_gamma**2], marker="D", c="tab:orange", label="full PEC"
)
ax.plot(
100 * np.array(biases),
np.array(costs),
"o-",
c="tab:blue",
label="PEC+SLC",
)
ax.set_xticks(xticks, [f"{x:.2f}" for x in xticks])
ax.set_xlabel("Remaining bias [%]")
ax.set_ylabel(r"Sampling overhead, $\gamma^2$")
ax.grid()
ax.legend()
fig.suptitle("PEC sampling overhead reduction due to SLC")
Optimal spacetime partitioning not implemented! Just partitioning list of noisy boxes.
Text(0.5, 0.98, 'PEC sampling overhead reduction due to SLC')
Pilih batas bias yang diinginkan berdasarkan trade-off antara shot overhead dan bias
Dalam panduan ini kita memilih toleransi bias sebesar 5%. Perhatikan bahwa perhitungan bias ini didasarkan pada model noise yang dipelajari, yang mungkin tidak sepenuhnya selaras dengan noise nyata dari device.
chosen_bias_threshold = 0.05
local_scales, sampling_cost, bias = compute_local_scales(
boxed_circuit, merged_bounds, learned_noise, bias_tolerance=bias
)
slc_pec_task.set_slc_local_scales(
local_scales, float(np.sqrt(sampling_cost)), bias
)
Siapkan program PEC + SLC, jalankan program, lalu lakukan post-processing
Dengan local scales yang sudah dipilih, kita bisa membangun program eksekusi PEC+SLC lengkap. Pemanggilan PEC.prepare membangun QuantumProgram yang mengodekan strategi injeksi anti-noise ke dalam anotasi box circuit. Memberikan noise_gain="slc_local_scales" menginstruksikan executor untuk memakai bobot per error yang ditetapkan oleh set_slc_local_scales, alih-alih menerapkan gain inverse-noise penuh yang sama pada setiap error seperti yang dilakukan PEC biasa.
Untuk menghasilkan perbandingan yang adil, task PEC kedua disiapkan dengan noise_gain=0, yang berkaitan dengan pembatalan PEC penuh tanpa kelonggaran bias SLC. Kedua program ditambahkan ke QuantumProgram yang sama sehingga berbagi satu job eksekusi — circuit-nya disisipkan berselang-seling dan dijalankan bersama, artinya drift hardware memengaruhi keduanya secara setara.
Setelah job selesai, postprocess mengonversi bitstring pengukuran mentah menjadi expectation value, dengan menerapkan faktor penskalaan ulang PEC γ yang sesuai untuk setiap task.
program_pec = slc_pec_task.prepare(
circuit=mirrored_circuit,
observables=[observable],
parameters=None,
num_randomizations=num_randomizations,
shots_per_randomization=shots_per_randomization,
custom_boxing_options=slc_boxing_options,
noise_maps=learned_noise, # PEC option
noise_gain="slc_local_scales", # PEC option. Defines the usage of SLC
max_sampling_overhead=100, # PEC option
)
full_pec_task = PEC()
program_pec = full_pec_task.prepare(
circuit=mirrored_circuit,
observables=[observable],
parameters=None,
num_randomizations=num_randomizations,
shots_per_randomization=shots_per_randomization,
custom_boxing_options=boxing_options,
quantum_program=program_pec,
noise_maps=learned_noise, # PEC option
noise_gain=0, # PEC option
max_sampling_overhead=100, # PEC option
)
# Execute
program_pec._semantic_role = (
"simulation" # flag the program for the simulator
)
job_pec_results = noisy_executor.run(program_pec).result()
# post-processing
pec_slc_res = slc_pec_task.postprocess(job_pec_results)
full_pec_res = full_pec_task.postprocess(job_pec_results)
Bandingkan expectation value hasil mitigasi dan shot overhead
Expectation value ideal dari circuit cermin adalah 1 secara konstruksi, sehingga bias dari estimasi apa pun cukup |1 - result|. Sel terakhir ini mencetak bias dan jumlah total shot untuk tiga skenario secara berdampingan:
-
Tanpa mitigasi: expectation value ber-noise mentah tanpa pembatalan error.
-
PEC penuh: pembatalan error tanpa bias dengan model inverse-noise lengkap pada setiap layer, pada sampling overhead γ² penuh.
-
PEC+SLC: pembatalan error yang dipandu oleh local scales shaded-lightcone, menoleransi
bias_toleranceyang dipilih sebagai ganti jumlah shot yang jauh lebih rendah.
Membandingkan ketiga baris menunjukkan manfaat konkret SLC: bias hasil mitigasi PEC+SLC tetap jauh di bawah bias tanpa mitigasi sementara total jumlah shot jauh lebih kecil daripada yang dibutuhkan PEC penuh.
print(
f"Unmitigated result bias: {np.abs(1 - unmitigated_res.data.evs[0]) * 100:.2f}%, \
total shots ran: {job_unmit_res[0]['_meas'].shape[0] * job_unmit_res[0]['_meas'].shape[2]}"
)
print(
f"Full PEC final result bias: {np.abs(1 - full_pec_res.data.evs[0]) * 100:.2f}%, \
total shots ran: {job_pec_results[1]['_meas'].shape[0] * job_pec_results[1]['_meas'].shape[2]}"
)
print(
f"PEC+SLC final result bias: {np.abs(1 - pec_slc_res.data.evs[0]) * 100:.2f}%, \
total shots ran: {job_pec_results[0]['_meas'].shape[0] * job_pec_results[0]['_meas'].shape[2]}"
)
Unmitigated result bias: 36.88%, total shots ran: 6400
Full PEC final result bias: 0.97%, total shots ran: 48528
PEC+SLC final result bias: 1.46%, total shots ran: 24480