דלג לתוכן הראשי

חיתוך Gate להפחתת רוחב Circuit

במחברת זו נעבור על שלבי דפוס Qiskit תוך שימוש בחיתוך Circuit כדי להפחית את מספר ה-Qubit ב-Circuit. נחתוך Gate-ים כדי לאפשר לנו לשחזר את ערך הציפייה של Circuit בן ארבעה Qubit באמצעות ניסויים של שני Qubit בלבד.

אלה הם השלבים שנעבור:

  • שלב 1: מיפוי הבעיה ל-Circuit קוונטיים ואופרטורים:
    • מיפוי ההמילטוניאן ל-Circuit קוונטי.
  • שלב 2: אופטימיזציה לחומרה היעד [משתמש בתוסף החיתוך]:
    • חיתוך ה-Circuit וה-Observable.
    • Transpile של תת-הניסויים עבור החומרה.
  • שלב 3: הרצה על חומרת היעד:
    • הרצת תת-הניסויים שהתקבלו בשלב 2 באמצעות ה-primitive ‏Sampler.
  • שלב 4: עיבוד תוצאות לאחר הרצה [משתמש בתוסף החיתוך]:
    • שילוב תוצאות שלב 3 כדי לשחזר את ערך הציפייה של ה-Observable הנדון.

שלב 1: מיפוי

יצירת Circuit לחיתוך

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
from qiskit.circuit.library import efficient_su2

qc = efficient_su2(4, entanglement="linear", reps=2)
qc.assign_parameters([0.4] * len(qc.parameters), inplace=True)

qc.draw("mpl", scale=0.8)

Quantum circuit diagram

ציון Observable

from qiskit.quantum_info import SparsePauliOp

observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])

שלב 2: אופטימיזציה

הפרדת ה-Circuit וה-Observable לפי חלוקת Qubit שצוינה

כל תווית ב-partition_labels מתאימה ל-Qubit של ה-circuit באותו האינדקס. Qubit-ים עם תווית חלוקה משותפת יקובצו יחד, ו-Gate-ים לא-לוקאליים המשתרעים על פני יותר מחלוקה אחת ייחתכו.

שים לב: ארגומנט ה-observables ל-partition_problem הוא מסוג PauliList. מקדמי ופאזות של איברי ה-Observable מתעלמים בזמן פירוק הבעיה והרצת תת-הניסויים. ניתן להחיל אותם מחדש בזמן שחזור ערך הציפייה.

from qiskit_addon_cutting import partition_problem

partitioned_problem = partition_problem(
circuit=qc, partition_labels="AABB", observables=observable.paulis
)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
bases = partitioned_problem.bases

ויזואליזציה של הבעיה המפורקת

subobservables
{'A': PauliList(['II', 'ZI', 'ZZ', 'XI', 'ZZ', 'IX']),
'B': PauliList(['ZZ', 'IZ', 'II', 'XI', 'ZI', 'IX'])}
subcircuits["A"].draw("mpl", scale=0.8)

Quantum circuit diagram

subcircuits["B"].draw("mpl", scale=0.8)

Quantum circuit diagram

חישוב עלות הדגימה עבור החיתוכים שנבחרו

כאן אנחנו חותכים שני Gate-ים מסוג CNOT, מה שמוביל לעלות דגימה של 929^2.

למידע נוסף על עלות הדגימה הנובעת מחיתוך Circuit, ראה את חומר ההסבר.

import numpy as np

print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 81.0

יצירת תת-הניסויים להרצה על ה-Backend

generate_cutting_experiments מקבל ארגומנטים מסוג circuits/observables כמילונים שממפים תוויות חלוקת Qubit לה-subcircuit/subobservables המתאים.

כדי לסמלץ את ערך הציפייה של ה-Circuit בגודלו המלא, נוצרים תת-ניסויים רבים מתוך ההתפלגות הפסאודו-הסתברותית המשותפת של ה-Gate-ים המפורקים ואז מורצים על Backend אחד או יותר. מספר הדגימות הנלקחות מההתפלגות נשלט על ידי num_samples, ומקדם משולב אחד ניתן לכל דגימה ייחודית. למידע נוסף על אופן חישוב המקדמים, ראה את חומר ההסבר.

from qiskit_addon_cutting import generate_cutting_experiments

subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits, observables=subobservables, num_samples=np.inf
)

בחירת Backend

כאן אנחנו משתמשים ב-Backend מדומה, מה שיגרום ל-Qiskit Runtime לרוץ במצב מקומי (כלומר, על סימולטור מקומי).

from qiskit_ibm_runtime.fake_provider import FakeManilaV2

backend = FakeManilaV2()

הכנת תת-הניסויים ל-Backend

עלינו לבצע Transpile ל-Circuit-ים עם ה-Backend שלנו כיעד לפני שליחתם ל-Qiskit Runtime.

from qiskit.transpiler import generate_preset_pass_manager

# Transpile the subexperiments to ISA circuits
pass_manager = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_subexperiments = {
label: pass_manager.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}

שלב 3: הרצה

הרצת תת-הניסויים באמצעות ה-primitive ‏Sampler של Qiskit Runtime

from qiskit_ibm_runtime import SamplerV2, Batch

# Submit each partition's subexperiments to the Qiskit Runtime Sampler
# primitive, in a single batch so that the jobs will run back-to-back.
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
/home/garrison/Qiskit/qiskit-ibm-runtime/qiskit_ibm_runtime/session.py:157: UserWarning: Session is not supported in local testing mode or when using a simulator.
warnings.warn(
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}

שלב 4: עיבוד לאחר הרצה

שחזור ערך הציפייה

שחזור ערכי הציפייה לכל איבר Observable ושילובם לשחזור ערך הציפייה עבור ה-Observable המקורי.

from qiskit_addon_cutting import reconstruct_expectation_values

# Get expectation values for each observable term
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)

# Reconstruct final expectation value
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)

השוואת ערך הציפייה המשוחזר עם ערך הציפייה המדויק מה-Circuit וה-Observable המקוריים

from qiskit_aer.primitives import EstimatorV2

estimator = EstimatorV2()
exact_expval = estimator.run([(qc, observable)]).result()[0].data.evs
print(f"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}")
print(f"Exact expectation value: {np.round(exact_expval, 8)}")
print(f"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}")
print(
f"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}"
)
Reconstructed expectation value: 0.6991539
Exact expectation value: 0.56254612
Error in estimation: 0.13660778
Relative error in estimation: 0.24283836