ข้ามไปยังเนื้อหาหลัก
โครงการนี้อยู่ในช่วงเบต้า พบข้อผิดพลาด? สร้าง GitHub Issue มีคำถามหรือไอเดีย? ถามใน Discussions — ยินดีรับฟังความคิดเห็น!
คุณภาพการแปลในหน้านี้เป็นอย่างไร?

วิธีปรับแต่งตัวเลือกการตั้งค่า

การคำนวณขอบเขตนั้นแม่นยำในหลักการ แต่เป็นการประมาณในทางปฏิบัติ: การวิวัฒน์เทอมความคลาดเคลื่อนแบบ Pauli ผ่าน circuit ให้ตัวดำเนินการที่มีจำนวนเทอมมากแบบเอกซ์โพเนนเชียล และการหา spectral norm ของคอมมิวเตเตอร์ที่ได้ก็มีต้นทุนสูงในตัวมันเอง ตัวเลือกด้านล่างควบคุมต้นทุนนั้น โดยแบ่ง เป็นสามกลุ่ม:

  • ตัวเลือกกลุ่ม Accuracy เปลี่ยนค่าตัวเลขของขอบเขต โดยแลกความแน่นของขอบเขตกับต้นทุน

  • กลุ่ม Scope กำหนดว่าจะคำนวณขอบเขต ใดบ้าง

  • ตัวเลือกกลุ่ม Execution เปลี่ยน วิธี ที่การคำนวณทำงาน (ไม่ใช่สิ่งที่คำนวณ)

ตัวเลือกค่าเริ่มต้นควบคุมผลต่อผลลัพธ์
Accuracy
evolution_max_terms1_000_000จำนวนเทอมของตัวดำเนินการที่รอดจากการวิวัฒน์ต่ำลง: ถูกลง ขอบเขตหลวมขึ้น
atol_simplify1e-8เทอมของคอมมิวเตเตอร์ใดที่ถูกทิ้งเพราะเล็กน้อยเกินไปสูงขึ้น: ถูกลง ขอบเขตหลวมขึ้น
eigval_max_qubits14คอมมิวเตเตอร์กว้างได้แค่ไหนจึงจะคำนวณ spectral norm ของมันได้ต่ำลง: ถูกลง ขอบเขตหลวมขึ้น
atol_eigenvalue1e-10เมื่อไรที่ตัวแก้ค่าลักษณะเฉพาะแบบวนซ้ำนับว่าลู่เข้าแล้วสูงขึ้น: ถูกลง ขอบเขตแม่นยำน้อยลง
Scope
max_num_boxesNoneจำนวน box ที่คำนวณขอบเขตbox ที่เกินขีดจำกัดจะคงขอบเขตธรรมดาที่ 2.0
Execution
timeoutNoneงบเวลาจริงสำหรับการคำนวณทั้งหมดงานที่เหลือจะคงขอบเขตธรรมดาที่ 2.0
num_processes1จำนวนโปรเซสทำงานที่คำนวณขอบเขตแบบขนานไม่มีผลโดยตัวมันเอง

ขอบเขตจะมีประโยชน์ก็ต่อเมื่อมัน ถูกต้อง: มันต้องไม่ประเมินผลของเทอมความคลาดเคลื่อนที่มีต่อ observable ต่ำเกินจริง การผ่อนปรนตัวเลือกใดๆ เหล่านี้ นอกจาก atol_eigenvalue มีแต่ทำให้ขอบเขต ใหญ่ขึ้น (อนุรักษ์นิยมขึ้น) ไม่มีทางเล็กลง ดังนั้นราคาของการตั้งค่าหลวมคือ sampling overhead ที่มากขึ้น ไม่ใช่ค่าคาดหมายที่ผิด - atol_eigenvalue เป็นข้อยกเว้น

นิยาม circuit​

การทดลองต่อไปนี้ใช้ circuit Ising แบบ Trotter และ observable เดียวกับคู่มือเริ่มต้นอย่างรวดเร็ว ที่ ขนาดนี้ ทุกตัวเลือกมีผลที่มองเห็นได้ ขณะที่แต่ละการคำนวณยังใช้เวลาไม่ถึงหนึ่งวินาที

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_slc": "qiskit-addon-slc", "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 numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Pauli
from qiskit_addon_slc.utils import generate_noise_model_paulis
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic.utils import find_unique_box_instructions

def trotter_ising_circuit(num_qubits, num_steps, rx_angle, rzz_angle):
"""Trotterized transverse-field Ising evolution on a 1D chain."""
circuit = QuantumCircuit(num_qubits)
for _ in range(num_steps):
circuit.rx(rx_angle, range(num_qubits))
circuit.barrier()
for start in (0, 1): # even then odd bonds
for i in range(start, num_qubits - 1, 2):
circuit.rzz(rzz_angle, i, i + 1)
circuit.barrier()
return circuit

num_qubits = 10
circuit = trotter_ising_circuit(
num_qubits, num_steps=4, rx_angle=np.pi / 16, rzz_angle=-np.pi / 3
)
observable = Pauli("I" * num_qubits).compose("Z", [num_qubits // 2])

boxing_pass = generate_boxing_pass_manager(
inject_noise_targets="all",
inject_noise_strategy="individual_modification",
inject_noise_site="after",
twirling_strategy="active",
remove_barriers="never",
)
boxed_circuit = boxing_pass.run(circuit)
noise_model_paulis = generate_noise_model_paulis(
find_unique_box_instructions(boxed_circuit)
)

print(f"noisy layers: {len(noise_model_paulis)}")
print(
f"Pauli error terms: {sum(len(p) for p in noise_model_paulis.values())}"
)
noisy layers: 2
Pauli error terms: 198

ฟังก์ชันช่วยเล็กๆ สรุปขอบเขตแต่ละชุดในบรรทัดเดียว ผลของตัวเลือกจึงปรากฏเป็น ตัวเลขไม่กี่ตัว แทนที่จะเป็นอาร์เรย์เป็นกำแพง

ทุกขอบเขตอยู่ระหว่าง 0 ถึงขอบเขตธรรมดาที่ 2.0 สรุปจะรายงานค่าเฉลี่ยของขอบเขตและ ส่วนเบี่ยงเบนมาตรฐาน ตามด้วยการแบ่งเทอมเป็นกลุ่มเหล่านี้:

  • เทอมที่มีขอบเขตเป็น 0 พอดี เพราะสลับที่ได้กับ observable ที่ผ่านการวิวัฒน์
  • เทอมที่คำนวณขอบเขตได้ระหว่าง 0 ถึง 2.0
  • เทอมที่ค้างอยู่ที่ค่าธรรมดา 2.0

โปรดสังเกตว่า ค่าเฉลี่ยที่ต่ำกว่าคือแน่นกว่า และแน่นกว่าหมายถึง sampling overhead ที่เล็กลงในภายหลัง

การกวาดค่าตัวเลือกหนึ่งตัวจะทำเครื่องหมายค่าเริ่มต้นของตัวเลือกนั้นด้วย *:

import inspect
import time

from qiskit_addon_slc.bounds import compute_forward_bounds

def summarize(rates, label, elapsed):
"""Prints a one-line summary of a set of bounds."""
# The bounds are floating-point results, so a term counts as bounded by 0, or as left at the
# trivial 2.0, when it is within rounding error of that value.
exact = np.isclose(rates, 0.0, rtol=0.0, atol=1e-12)
trivial = np.isclose(rates, 2.0, rtol=0.0, atol=1e-12)
computed = ~exact & ~trivial
print(
f"{label:28} mean {rates.mean():.4f} ± {rates.std():.3f} │ "
f"at 0: {exact.mean():4.0%} computed: {computed.mean():4.0%} "
f"at 2.0: {trivial.mean():4.0%} │ "
f"{elapsed:4.2f}s"
)

def forward(label, **kwargs):
"""Computes the forward bounds, summarizes them and returns all their rates."""
start = time.perf_counter()
bounds = compute_forward_bounds(
boxed_circuit, noise_model_paulis, observable, **kwargs
)
elapsed = time.perf_counter() - start
rates = np.concatenate([bound.rates for bound in bounds.values()])
summarize(rates, label, elapsed)
return rates

def sweep(option, values):
"""Computes the forward bounds for each value of one option, marking the default with `*`."""
default = (
inspect.signature(compute_forward_bounds).parameters[option].default
)
for value in values:
marker = "*" if value == default else " "
forward(f"{marker} {option}={value:g}", **{option: value})

baseline = forward(" defaults")
defaults mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.29s

ความแม่นยำ​

evolution_max_terms​

การวิวัฒน์เทอมความคลาดเคลื่อนแบบ Pauli ผ่าน gate ที่ไม่ใช่ Clifford จะเปลี่ยนมันเป็นผลรวมของ Pauli และผลรวมนั้น โตตามความลึกของ circuit evolution_max_terms จำกัดจำนวนเทอมเหล่านั้นที่เก็บไว้ 1-norm ของ สิ่งที่ถูกทิ้งจะถูกบวกเข้าไปในขอบเขต จึงไม่มีอะไรสูญหายไปเงียบๆ

การกวาดค่าเริ่มจากแน่นกว่าค่าเริ่มต้นไปจนถึงการตัดอย่างสุดโต่ง การลดเพดานทำให้ การคำนวณถูกลงและขอบเขตหลวมขึ้น

sweep("evolution_max_terms", (10_000_000, 1_000_000, 1000, 100, 10, 2))
evolution_max_terms=1e+07 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.38s
* evolution_max_terms=1e+06 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.31s
evolution_max_terms=1000 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.30s
evolution_max_terms=100 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.29s
evolution_max_terms=10 mean 0.3927 ± 0.711 │ at 0: 59% computed: 31% at 2.0: 9% │ 0.28s
evolution_max_terms=2 mean 0.5245 ± 0.792 │ at 0: 59% computed: 23% at 2.0: 18% │ 0.21s

การเพิ่มเพดานเกินค่าเริ่มต้นไม่เปลี่ยนอะไรในที่นี้ สำหรับ circuit ขนาดนี้ ค่าเริ่มต้น เก็บทุกเทอมไว้อยู่แล้ว ขอบเขตจึงแน่นที่สุดเท่าที่ตัวเลือกนี้ทำได้ นั่นคือ สถานการณ์ทั่วไป การทำให้ตัวเลือก accuracy แน่นขึ้นจะคุ้มก็ต่อเมื่อค่าเริ่มต้นเป็นตัวจำกัดจริงๆ และวิธีค้นหาคือการทดลองด้านบน ลองรันซ้ำด้วยค่าที่แน่นกว่าแล้วดูว่า ขอบเขตขยับหรือไม่

คำเตือน: รูทีนการแพร่กระจายเบื้องหลังถือว่า evolution_max_terms เป็น ความจุที่ต้อง จองล่วงหน้า ไม่ใช่เพดานที่ใช้ตรวจสอบ การส่งค่าที่ในนามไม่จำกัด เช่น numpy.iinfo(numpy.uint).max จะขอหน่วยความจำระดับเทระไบต์จากตัวจัดสรรและทำให้โปรเซสล้มเลิก ให้ใช้ตัวเลขที่สมเหตุสมผล ค่าเริ่มต้นหนึ่งล้านก็ใจกว้างพอแล้ว

atol_simplify​

หลังจากสร้างคอมมิวเตเตอร์แล้ว เทอมที่มีสัมประสิทธิ์ต่ำกว่า atol_simplify จะถูกทิ้ง เช่นเดียวกับ เพดานการวิวัฒน์ 1-norm ที่สูญเสียไปด้วยวิธีนี้จะถูกบวกเข้าไปในขอบเขต

sweep("atol_simplify", (0.0, 1e-8, 1e-4, 1e-2, 1e-1))
atol_simplify=0 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.32s
* atol_simplify=1e-08 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.31s
atol_simplify=0.0001 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.33s
atol_simplify=0.01 mean 0.3656 ± 0.685 │ at 0: 59% computed: 35% at 2.0: 6% │ 0.31s
atol_simplify=0.1 mean 0.3837 ± 0.697 │ at 0: 59% computed: 31% at 2.0: 10% │ 0.31s

eigval_max_qubits​

ขอบเขตที่แน่นที่สุดที่มีคือ spectral norm ของคอมมิวเตเตอร์ ซึ่งต้องคำนวณค่าลักษณะเฉพาะ สิ่งนี้แพงขึ้นเมื่อคอมมิวเตเตอร์กว้างขึ้น ดังนั้น eigval_max_qubits กำหนดความกว้าง ที่เกินจากนั้นจะเลิกพยายามและหันไปใช้อสมการสามเหลี่ยมแทน ขอบเขตนั้น 2∑i∣ci∣2 \sum_i |c_i| ของสัมประสิทธิ์ cic_i ของคอมมิวเตเตอร์ ถูกต้องแต่หลวมกว่า

การตั้งเป็นศูนย์บังคับให้ใช้ทางสำรองทุกที่ ซึ่งแสดงประโยชน์ของตัวแก้ค่าลักษณะเฉพาะ ในตัวเลขเดียว

sweep("eigval_max_qubits", (20, 14, 8, 4, 2, 0))
eigval_max_qubits=20 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.31s
* eigval_max_qubits=14 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.30s
eigval_max_qubits=8 mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.32s
eigval_max_qubits=4 mean 0.3742 ± 0.694 │ at 0: 59% computed: 35% at 2.0: 6% │ 0.30s
eigval_max_qubits=2 mean 0.4983 ± 0.809 │ at 0: 59% computed: 23% at 2.0: 18% │ 0.27s
eigval_max_qubits=0 mean 0.5081 ± 0.810 │ at 0: 59% computed: 21% at 2.0: 19% │ 0.27s

แม้ปิดตัวแก้ค่าลักษณะเฉพาะ ค่าเฉลี่ยก็ยังต่ำกว่าค่าธรรมดา 2.0 มาก สัดส่วน ของเทอมที่มีขอบเขตเป็น 0 ไม่เปลี่ยน: เทอมที่สลับที่ได้พอดีกับ observable ที่ผ่านการวิวัฒน์ มีคอมมิวเตเตอร์เป็นศูนย์ ซึ่งถูกจดจำได้ก่อนจะพิจารณาการคำนวณค่าลักษณะเฉพาะใดๆ ส่วน เทอมอื่น ขอบเขตจากอสมการสามเหลี่ยมถึง 2.0 สำหรับบางเทอมและถูกจำกัดไว้ที่นั่น แต่สำหรับอีกมากมัน ยังเล็กกว่า เทอมเหล่านั้นจึงคงขอบเขตที่คำนวณได้ต่ำกว่า 2.0

atol_eigenvalue​

เมื่อมีการคำนวณ spectral norm จริง atol_eigenvalue คือค่าความคลาดเคลื่อนที่ยอมรับได้สำหรับการลู่เข้าของ ตัวแก้ค่าลักษณะเฉพาะแบบวนซ้ำ แต่คอมมิวเตเตอร์ส่วนใหญ่ไม่เคยไปถึงตัวแก้นั้น:

  • คอมมิวเตเตอร์ที่กระทำกับไม่เกินสี่ qubit จะถูกทำเป็นทแยงมุมอย่างแม่นยำโดยตรง

  • คอมมิวเตเตอร์ที่กว้างกว่านั้นจะถูกลดเหลือจำนวน qubit น้อยที่สุดที่เทอม Pauli ของมันต้องใช้ก่อน และถ้ารูปแบบที่ลดแล้วกระทำกับไม่เกินแปด qubit ก็จะถูกทำเป็นทแยงมุมอย่างแม่นยำ

เฉพาะคอมมิวเตเตอร์ที่หลังการลดนั้นยังต้องใช้เกินแปด qubit เท่านั้นที่ไปถึงตัวแก้ แบบวนซ้ำ ซึ่งเป็นที่เดียวที่ความคลาดเคลื่อนที่ยอมรับได้นี้ใช้ circuit ขนาดที่ใช้ในคู่มือนี้ไม่เคยไป ถึงขั้นนั้น การกวาดค่าตัวเลือกนี้จึงให้ขอบเขตเหมือนกันทุกค่า มันมีความสำคัญสำหรับ light cone ที่กว้างบนอุปกรณ์ขนาดใหญ่

ต่างจากตัวเลือก accuracy อื่น การผ่อน atol_eigenvalue ไม่ ปลอดภัยในทิศทางหนึ่ง ตัวแก้ ค่าลักษณะเฉพาะแบบวนซ้ำเข้าใกล้ค่าลักษณะเฉพาะสุดขั้วจากด้านในของสเปกตรัม ดังนั้นค่าประมาณ ที่ยังลู่เข้าไม่เต็มที่จะประเมิน spectral norm และขอบเขตต่ำเกินจริง ค่าเริ่มต้น จึงถูกตั้งให้แน่นโดยเจตนาด้วยเหตุนั้น ถ้าตัวแก้ลู่เข้าไม่ได้ ทางสำรองอสมการสามเหลี่ยม จะเข้ามาแทน ซึ่งกลับมาถูกต้องอีกครั้ง

ขอบเขต​

max_num_boxes​

บางครั้งขีดจำกัดที่มีประโยชน์ไม่ใช่ความแม่นยำแต่เป็นความลึก max_num_boxes หยุดการสร้างการคำนวณขอบเขต เมื่อเกินจำนวน box ที่กำหนด และทุกเลเยอร์ที่เกินจากนั้นจะถูกกำหนดขอบเขตธรรมดา ที่ 2.0 แทน

ขีดจำกัดนับคำสั่ง BoxOp ที่มีสัญญาณรบกวน (คำสั่งที่มีคำอธิบายประกอบ InjectNoise) แทนที่จะเป็น เช่น ความลึกในหน่วย gate วิธีนี้ผูกมันกับโครงสร้างของ circuit ที่ผู้ใช้มองเห็น และ หลีกเลี่ยงการพึ่งพาการอ้างอิงคำอธิบายประกอบที่สร้างอัตโนมัติ ซึ่งอาจเปลี่ยนระหว่าง การรัน transpilation

นี่คือทางเลือกตามธรรมชาติสำหรับ circuit ที่สองครึ่งของมันจัดการด้วยขอบเขตไปข้างหน้าและย้อนกลับ ตามลำดับ: คำนวณแต่ละอย่างลึกเท่าที่จะใช้เท่านั้น

from qiskit.circuit import BoxOp
from samplomatic import InjectNoise

# The limit counts the noisy boxes encountered while iterating the circuit, not the unique ones:
# a repeated layer is one noise model but several boxes. Boxes without an `InjectNoise`
# annotation carry no error terms and do not count.
num_boxes = sum(
1
for inst in boxed_circuit.data
if isinstance(inst.operation, BoxOp)
and any(
isinstance(annotation, InjectNoise)
for annotation in inst.operation.annotations
)
)
print(
f"the circuit has {num_boxes} noisy boxes drawn from "
f"{len(noise_model_paulis)} distinct noise models\n"
)

for limit in (None, 6, 4, 2, 1):
marker = "*" if limit is None else " "
forward(f"{marker} max_num_boxes={limit}", max_num_boxes=limit)
the circuit has 9 noisy boxes drawn from 2 distinct noise models

* max_num_boxes=None mean 0.3639 ± 0.684 │ at 0: 59% computed: 36% at 2.0: 5% │ 0.33s
max_num_boxes=6 mean 0.7486 ± 0.935 │ at 0: 53% computed: 18% at 2.0: 30% │ 0.18s
max_num_boxes=4 mean 1.1506 ± 0.980 │ at 0: 40% computed: 5% at 2.0: 55% │ 0.11s
max_num_boxes=2 mean 1.5707 ± 0.821 │ at 0: 21% computed: 0% at 2.0: 79% │ 0.05s
max_num_boxes=1 mean 1.8157 ± 0.579 │ at 0: 9% computed: 0% at 2.0: 91% │ 0.03s

การรันงาน​

timeout​

timeout คืองบเวลาจริงเป็นวินาที เมื่อหมดเวลา ขอบเขตที่คำนวณเสร็จแล้วจะถูกเก็บไว้ และทุกอย่างที่ค้างอยู่จะคงขอบเขตธรรมดาที่ 2.0 ผลลัพธ์จึงใช้ได้เสมอ เพียงแต่ หลวมขึ้นยิ่งตัดเร็ว

มันไม่ใช่เส้นตายเด็ดขาด: งานที่กำลังรันอยู่จะได้รับอนุญาตให้ทำจนเสร็จ การเรียกจึงอาจเกิน งบเวลาได้เท่ากับระยะเวลาของหนึ่งงาน

num_processes​

ขอบเขตของเทอมความคลาดเคลื่อนต่างกันเป็นอิสระต่อกัน ดังนั้น num_processes กระจายมันไปยังโปรเซสทำงาน โดยตัวมันเอง มันมีผลแค่ระยะเวลาที่การคำนวณใช้ ไม่เคยมีผลต่อผลลัพธ์

หมายเหตุ: การใช้ timeout ร่วมกับ num_processes ทำให้ผลลัพธ์ขึ้นกับจังหวะเวลา: งานใดที่ เสร็จก่อนงบเวลาหมดนั้นไม่แน่นอน ขอบเขตยังคงถูกต้อง แต่ไม่สามารถ ทำซ้ำได้จากการรันหนึ่งไปอีกการรันหนึ่งอีกต่อไป

เริ่มต้นจากตรงไหน​

ค่าเริ่มต้นถูกเลือกให้ได้ขอบเขตที่แน่นบน circuit ขนาดที่พอจ่ายไหว เมื่อ การคำนวณช้าเกินไป ลองใช้ตัวเลือกตามลำดับคร่าวๆ นี้:

  1. num_processes: เพิ่มความเร็วฟรีๆ โดยไม่เสียความแม่นยำ

  2. max_num_boxes: ถ้าคุณรู้ล่วงหน้าว่าสนใจลึกเข้าไปใน circuit แค่ไหน

  3. eigval_max_qubits: ข้ามขั้นตอนที่แพงที่สุดสำหรับคอมมิวเตเตอร์ที่กว้างที่สุด ซึ่งมักเป็น ส่วนน้อยของเทอม

  4. evolution_max_terms และ atol_simplify: สองตัวนี้กระทบความแม่นยำทั้งหมด จึงปรับ เป็นอันดับสุดท้าย โดยตรวจด้วยการกวาดค่าแบบข้างบนเพื่อดูว่าขอบเขตขยับไปแค่ไหน

  5. timeout: การหยุดแบบเด็ดขาดมากกว่าตัวช่วยปรับแต่ง สำหรับเมื่อการคำนวณต้องเสร็จตามเส้นตาย ไม่ว่าคุณภาพจะเป็นอย่างไร

ยิ่งขอบเขตหลวม sampling overhead ที่ compute_local_scales จะรายงานสำหรับ ความคลาดเคลื่อนของ bias ที่กำหนดก็ยิ่งใหญ่ขึ้น overhead นั้นคือปริมาณที่ควรจับตาขณะปรับแต่ง: มันบอกคุณว่า การประมาณมีต้นทุนจริงเท่าไร