PEC مع SLC
إلغاء الخطأ الاحتمالي (PEC) هو تقنية لتخفيف الضوضاء الكمّية في حسابات القيم المتوقعة للمرصودات. تتيح لك حساب تقدير غير منحاز للقيم المتوقعة. تعمل هذه التقنية بتنفيذ مجموعة من الدوائر المضوضَّة التي حُقن فيها معكوس نموذج ضوضاء متعلَّم. ثم يُقرَّب نموذج الضوضاء إلى قناة Pauli باستخدام لفّ Pauli (المعروف أيضًا بـالتجميع العشوائي). يمكن تعلّم نماذج الضوضاء هذه بكفاءة ويمكن تخفيفها باستخدام PEC (انظر Probabilistic error cancellation with sparse Pauli-Lindblad models on noisy quantum processors لمزيد من التفاصيل).
يتطلب PEC حملًا أُسّيًا من اللقطات لحساب تقدير غير منحاز. Shaded Lightcones (SLC) هي تقنية تستخدم موارد كلاسيكية لتحديد حدود لحساسية الدائرة للأخطاء عبر أجزائها. وباستخدام هذه الحدود، يستطيع المستخدم ضبط الانحياز في تقدير المرصود بشكل متحكَّم به.
يوضح هذا الدليل كيفية استخدام PEC مع SLC لتخفيف ضوضاء البوابات بحمل لقطات أقل مقارنةً بـ PEC التقليدي.
سير العمل كما يلي:
- حدّد دائرة ومرصودًا.
- احسب القيم المتوقعة المضوضَّة غير المخفَّفة.
- احسب الحدود الأمامية والخلفية للانحياز.
- تعلّم ضوضاء طبقات التشابك باستخدام qiskit-noise-learning.
- استخدم الضوضاء المتعلَّمة لدمج الحدود المحسوبة في مصفوفة المقاييس المحلية.
- اختر حد الانحياز المطلوب بناءً على المفاضلة بين حمل اللقطات والانحياز.
- جهّز برنامج PEC + SLC، ونفّذ البرنامج، وعالج النتائج.
- قارن القيم المتوقعة المخفَّفة وحمل اللقطات.
حدّد دائرة ومرصودًا
جهّز دائرة مرآة لتطور زمني بطريقة Trotter لهاميلتوني سلسلة Ising أحادية البعد على ثمانية كيوبتات. اعكس الدائرة لإلغاء وحدويّتها (unitary) وتكوين دائرة هوية، بحيث تكون القيمة المتوقعة المثالية لكل مرصود معروفة وتساوي 1 دون الحاجة إلى حسابها. بالإضافة إلى ذلك، يضاعف الانعكاس عمق الدائرة، وهو ما يضاعف بدوره أثر الضوضاء.
استخدم الزاوية لجعل الديناميكيات غير 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
احسب القيم المتوقعة المضوضَّة غير المخفَّفة
يحتوي QuantumProgram على دائرة كمّية وsamplex بصيغة مناسبة للمُنفِّذ الأولي Executor. يبني MitigationTask.prepare كائن QuantumProgram من الدائرة والمرصودات وقيم المعاملات، ويضع الدائرة في صناديق (boxes) وفق خيارات التصنيد المعطاة. اضبط خيار التصنيد measure_annotations على "change_basis" لتدوير القياسات إلى الأسس المطلوبة، وأضف add_tags: "unique_box" ليتمكن المحاكي من ربط نموذج ضوضاء بصناديق الدائرة. يُلفّ البرنامج بطريقة Pauli افتراضيًا، وهو ما يحوّل على العتاد الضوضاء المتماسكة إلى ضوضاء Pauli التي تفترضها طرق التخفيف. يحدد num_randomizations عدد النسخ الملتفّة التي تُؤخذ من كل دائرة، ويحدد shots_per_randomization عدد اللقطات التي تحصل عليها كل نسخة، وبالتالي فإن حاصل ضربهما هو حد اللقطات لكل دائرة.
إذا كان لديك وصول إلى QPU، يمكنك تنفيذ البرنامج باستخدام Executor مبني من backend تابع لـ QiskitRuntimeService. يستخدم هذا المثال AerSimulator لتشغيل البرنامج محليًا. يحقن خيار المحاكي layer_noise_model الخاص به ضوضاء من نموذج ضوضاء Pauli-Lindblad معيّن على كل طبقة من بوابات التشابك. هنا نولّد نموذج ضوضاء Pauli-Lindblad عشوائيًا بخطأ لكل بوابة طبقية (EPLG) يساوي 0.01، أي أعلى بنحو رتبة قدر واحدة من معالجات Heron QPU الحالية النموذجية، لتضخيم أثر استخدام SLC حتى على مسألة صغيرة النطاق.
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
احسب الحدود الأمامية والخلفية للانحياز
قبل إنفاق أي وقت QPU على تعلّم الضوضاء، يستخدم SLC المحاكاة الكلاسيكية لتحديد حد لمساهمة كل خطأ محتمل في انحياز المرصود. يتم ذلك في مرحلتين متكاملتين:
-
الانتشار الأمامي: يُنشر كل خطأ Pauli أماميًا عبر بقية الدائرة ويُقيَّم مبدّله (commutator) مع المرصود. الخطأ الذي يضادّ المبادلة مع المرصود هو النوع الوحيد القادر على إحداث انحياز في النتيجة، لذلك تعطي هذه المرحلة حدًا أعلى لتأثير كل خطأ أماميًا.
-
الانتشار الخلفي: يُنشر كل خطأ خلفيًا إلى بداية الدائرة ويُقيَّم مبدّله مع الحالة الابتدائية. ولأن الدائرة تبدأ من حالة الأصفار الكاملة، فإن الأخطاء التي تتبادل معها لا يمكن أن تؤثر في النتيجة، مما يشدّد الحد من الاتجاه الآخر.
يشكّل الحدّان معًا نطاقًا ضيقًا حول أسوأ مساهمة لكل خطأ في الانحياز. وفي الخطوة التالية تُدمج هذه الحدود مع معدلات الضوضاء المتعلَّمة لترتيب الأخطاء التي تستحق التصحيح وتلك التي يمكن تركها بأمان. خيار التصنيد inject_noise_strategy="individual_modification" مطلوب هنا حتى يكون لكل تعليق InjectNoise في الدائرة ذات الصناديق هوية فريدة يمكن ربط حدود SLC بها.
في المسائل الأكبر نطاقًا، قد تتطلب هذه الحسابات موارد حوسبة كلاسيكية كبيرة.
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)
تعلّم ضوضاء طبقات التشابك
لحساب تقدير نهائي لمدى تأثير كل خطأ في انحياز القيمة المتوقعة، ولمدى كلفة تصحيحه، نتعلّم معدلات الضوضاء ونطبّقها على حدود الضوضاء المتوقعة التي حسبناها سابقًا.
يفترض PEC (وكذلك PEC+SLC) نموذج ضوضاء ملتفًّا لكل طبقة من بوابات التشابك في الدائرة. إذا افترضت نموذج ضوضاء ماركوفيًا، فيُفترض أن عمق الدائرة لا يؤثر على قناة الضوضاء لطبقة تشابك معيّنة. لذلك يكفي تعلّم نموذج الضوضاء لكل طبقة فريدة من بوابات التشابك. يمكن تقسيم الدائرة المستخدمة في هذا الدليل إلى طبقتين فريدتين. من المهم تصميم دوائر بأقل مجموعة ممكنة من طبقات التشابك الفريدة إذا أردت تعلّم نموذج ضوضاء، لأن الطبقات الفريدة يجب تعلّمها بشكل مستقل. وإذا كان في دائرتك عدد كبير جدًا من الطبقات الفريدة، فقد يصبح تعلّم الضوضاء غير ممكن بسبب كلفة أخذ العينات لتعلّم طبقات كثيرة.
يُستخدم qiskit-noise-learning لتعلّم الضوضاء. يكرّر المتعلّم كل طبقة fragment_depths مرة في دوائر التعلّم الخاصة به، ويلفّ كل دائرة num_randomizations مرة، ثم يطابق تلاشي الدقّات المقاسة مع نموذج Pauli-Lindblad. يمكن لهذه المعاملات أن تؤثر في دقة النموذج المتعلَّم. وكلما كان نموذج الضوضاء المتعلَّم أدق، كان إلغاء الضوضاء أدق.
يرسم الكود التالي نموذج الضوضاء المتعلَّم مع دمج الطبقتين في خريطة واحدة. كل كيوبت يظهر كدائرة مقسّمة تمثّل معدلات X وY وZ ذات الوزن 1 مجموعةً على الطبقتين، وتُظهر كل حافة معدلات الوزن 2 المتعلَّمة. على سبيل المثال، معدل 3e-4 يعني أن الخطأ يحدث تقريبًا في ثلاثة من كل 10,000 استخدام للطبقة.
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()
استخدم الضوضاء المتعلَّمة لدمج الحدود المحسوبة في مصفوفة المقاييس المحلية
بعد أن أصبحت لدينا الحدود الكلاسيكية (من الخطوة السابقة) ومعدلات الضوضاء المتعلَّمة، يمكننا الآن حساب مصفوفة المقاييس المحلية — وهي وزن لكل خطأ يحدد مدى صرامة إلغاء PEC لكل حد ضوضاء على حدة.
الفكرة الأساسية في SLC أن الأخطاء ليست كلها بحاجة إلى إلغاء كامل. الخطأ الذي يكون حدّاه الأمامي والخلفي صغيرين له أثر ضئيل على المرصود بصرف النظر عن معدله، لذلك يمكن تركه غير مخفَّف جزئيًا أو كليًا. يدمج merge_bounds الحدود الكلاسيكية مع معدلات Pauli-Lindblad المتعلَّمة، ثم يُسند compute_local_scales لكل خطأ مقياسًا بين 0 (مُهمَل تمامًا) و1 (مُلغى بالكامل)، وفق bias_tolerance يحدده المستخدم. الأخطاء ذات المساهمة الكبيرة المقدَّرة في الانحياز تحصل على مقياس قريب من 1 (معاملة PEC كاملة)، بينما تحصل الأخطاء ذات المساهمة الصغيرة على مقياس أقل، مما يقلل حمل أخذ العينات الذي تتسبب فيه.
يوفّر SLC مقبضًا متصلًا بين الطرفين — PEC التقليدي (انحياز صفري وأعلى حمل) وعدم التخفيف إطلاقًا (حمل صفري وانحياز ضوضاء كامل). يتحكم المعامل bias_tolerance في هذا المقبض: ضبطه على 0 يستعيد PEC الكامل، بينما تتيح القيم الأكبر قدرًا أكبر من الانحياز المتبقي مقابل حمل أخذ عينات γ² أقل.
تمسح هذه الخطوة قيم bias_tolerance عبر نطاق من القيم وتحسب حمل أخذ العينات الناتج لكل منها. يساعدك رسم هذه النقاط مقابل حمل PEC الكامل على اختيار نقطة عمل تناسب ميزانية اللقطات لديك. على سبيل المثال، قد يقلل تحمّل انحياز متبقٍّ بنسبة 5 % حمل أخذ العينات برتبة قدر واحدة مقارنةً بـ PEC الكامل، مما يجعل الحساب ممكنًا على QPUs اليوم حيث لم يكن ممكنًا لولا ذلك.
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')
اختر حد الانحياز المطلوب بناءً على المفاضلة بين حمل اللقطات والانحياز
في هذا الدليل نختار تحمّل انحياز بنسبة 5%. لاحظ أن حساب الانحياز هذا يعتمد على نموذج الضوضاء المتعلَّم، وقد لا يتطابق تمامًا مع الضوضاء الحقيقية للجهاز.
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
)
جهّز برنامج PEC + SLC، ونفّذ البرنامج، وعالج النتائج
بعد اختيار المقاييس المحلية، يمكننا بناء برنامج التنفيذ الكامل PEC+SLC. ينشئ الاستدعاء PEC.prepare كائن QuantumProgram يشفّر استراتيجية حقن الضوضاء المضادة في تعليقات صناديق الدائرة. يؤدي تمرير noise_gain="slc_local_scales" إلى توجيه المُنفِّذ لاستخدام الأوزان لكل خطأ التي ضبطها set_slc_local_scales، بدل تطبيق نفس كسب الضوضاء العكسي الكامل على كل خطأ كما يفعل PEC التقليدي.
للحصول على مقارنة عادلة، يُجهَّز مهمة PEC ثانية بـ noise_gain=0، وهو ما يقابل إلغاء PEC الكامل دون أي سماح بانحياز SLC. تُضاف كلتا المهمتين إلى نفس QuantumProgram لتتشاركا مهمة تنفيذ واحدة — إذ تتداخل دوائرهما وتُنفَّذان معًا، أي أن أي انجراف في العتاد يؤثر في الاثنتين بالتساوي.
بعد اكتمال المهمة، يحوّل postprocess سلاسل البتات الخام المقيسة إلى قيم متوقعة، مطبّقًا عامل إعادة التحجيم المناسب لـ PEC وهو γ لكل مهمة.
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)
قارن القيم المتوقعة المخفَّفة وحمل اللقطات
القيمة المتوقعة المثالية للدائرة المعكوسة هي 1 بحكم البناء، لذلك فإن انحياز أي تقدير هو ببساطة |1 - result|. تطبع هذه الخلية الأخيرة الانحياز وإجمالي عدد اللقطات لثلاثة سيناريوهات جنبًا إلى جنب:
-
غير مخفَّف: القيمة المتوقعة المضوضَّة الخام دون تطبيق أي إلغاء للأخطاء.
-
PEC الكامل: إلغاء أخطاء بانحياز صفري باستخدام نموذج الضوضاء العكسي الكامل على كل طبقة، بحمل أخذ العينات γ² الكامل.
-
PEC+SLC: إلغاء أخطاء موجَّه بالمقاييس المحلية لـ shaded-lightcone، مع تحمّل
bias_toleranceالمختار مقابل عدد لقطات أقل بكثير.
تُظهر مقارنة الصفوف الثلاثة الفائدة الملموسة لـ SLC: يبقى انحياز PEC+SLC المخفَّف أقل بكثير من الانحياز غير المخفَّف، بينما يكون إجمالي عدد اللقطات أصغر بشكل ملحوظ مما تطلّبه PEC الكامل.
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