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

דוגמאות ל-Executor

גרסאות חבילות

הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנחנו ממליצים להשתמש בגרסאות אלה או בגרסאות חדשות יותר.

qiskit[all]~=2.4.0
qiskit-ibm-runtime~=0.46.1
samplomatic~=0.18.0
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime samplomatic

הדוגמאות בחלק זה ממחישות כמה דרכים נפוצות לשימוש בפרימיטיב Executor. לפני הרצת דוגמאות אלה, עקוב אחר ההוראות ב-התקנת Qiskit ו-התחלה מהירה עם Executor.

לפני שמתחילים

חלק מדוגמאות הקוד בדף זה משתמשות ב-samplex, שהוא חלק מחבילת Samplomatic. לכן, לפני הרצת בלוק קוד זה, יש להתקין את Samplomatic, כפי שמוצג בבלוק הקוד הבא. למידע נוסף, ראה את תיעוד Samplomatic.

pip install samplomatic

# For visualization support, include the visualization dependencies.
# pip install samplomatic[vis]

דוגמה: מעגל עם פרמטרים

דוגמה זו ממחישה כיצד להוסיף פריטי מעגל עם פרמטרים, וכן כיצד להוסיף פריטי samplex. היא מורכבת מהשלבים הבאים:

  1. הגדרת המעגל: יצירת המעגל הרצוי ו-Transpilation שלו.
  2. הכנת samplex: קיבוץ שערים ומדידות לתיבות עם הערות ויצירת זוג תבנית מעגל ו-samplex.
  3. ביצוע: הוספת פריט מעגל ופריט samplex ל-QuantumProgram וביצוע שניהם במשימה אחת.

הגדרת המעגל

הכן מצב GHZ של שלושה קיוביטים, סובב את הקיוביטים סביב ציר Pauli-Z, ומדוד את הקיוביטים בבסיס החישובי.

from qiskit.circuit import Parameter, QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.transpiler import generate_preset_pass_manager
import numpy as np
from samplomatic import build
from samplomatic.transpiler import generate_boxing_pass_manager

# Generate the circuit
circuit = QuantumCircuit(3)
circuit.h(0)
circuit.h(1)
circuit.cz(0, 1)
circuit.h(1)
circuit.h(2)
circuit.cz(1, 2)
circuit.h(2)
circuit.rz(Parameter("theta"), 0)
circuit.rz(Parameter("phi"), 1)
circuit.rz(Parameter("lam"), 2)
circuit.measure_all()

ציין את ה-Backend ובצע Transpile למעגל כדי להשתמש רק בהוראות הנתמכות על ידי ה-QPU (המכונה מעגל ארכיטקטורת מערכת הוראות (ISA)).

# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Transpile the circuit to ISA
preset_pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = preset_pass_manager.run(circuit)

הכנת ה-samplex

השתמש בפונקציית הנוחות generate_boxing_pass_manager ובפרמטרי ה-twirling שלה כדי לקבץ שערים דו-קיוביטיים ומדידות לתיבות ולהחיל הערות twirling.

boxing_pm = generate_boxing_pass_manager(
# Add gate twirling
enable_gates=True,
# Add measurement twirling
enable_measures=True,
)

boxed_circuit = boxing_pm.run(isa_circuit)

השתמש בשיטת build כדי ליצור את תבנית המעגל ואת ה-samplex.

# Build the template circuit and the samplex
template_circuit, samplex = build(boxed_circuit)

ביצוע המעגלים

Executor מריץ אובייקטי QuantumProgram. כל QuantumProgram יכול להכיל מספר פריטים. דוגמה זו מוסיפה פריט מעגל ופריט samplex לביצוע. לפרטים מלאים, ראה קלט ופלט של Executor.

השלב הראשון הוא לאתחל תוכנית ריקה, תוך בקשת 1024 shots לכל תצורה של כל פריט.

# Generate a quantum program
program = QuantumProgram(shots=1024)

צרף את פריט המעגל ל-QuantumProgram. פריט מעגל זה מורכב משני חלקים - המעגל ISA ו-10 מערכות ערכי הפרמטרים שלו.

# Append the circuit and the parameter values to the program
program.append_circuit_item(
isa_circuit,
circuit_arguments=np.random.rand(10, 3), # 10 sets of parameter values
)

צרף את פריט ה-samplex ל-QuantumProgram עם הארגומנטים הבאים:

  • תבנית המעגל וה-samplex שנוצרו על ידי פונקציית build
  • עשר מערכות של ערכי פרמטרים למעגל המקורי
  • מספר האקראיות לביצוע
# Append the template circuit and samplex as a samplex item
program.append_samplex_item(
template_circuit,
samplex=samplex,
samplex_arguments={
"parameter_values": np.random.rand(
10, 3
), # 10 sets of parameter values
},
shape=(2, 14, 10),
)

הרצת משימת Executor

# initialize an Executor with default options
executor = Executor(mode=backend)

# Submit the job
job = executor.run(program)

# Retrieve the result
result = job.result()

שחזר את התוצאה לכל משימה.

# Access the results of the classical register of task #0, the CircuitItem
result_0 = result[0]["meas"]

# Access the results of the classical register of task #1, the SamplexItem
result_1 = result[1]["meas"]

דוגמה: ביצוע PEC

דוגמה זו מראה כיצד להשתמש בפריט samplex לביצוע ביטול שגיאות הסתברותי (PEC) להפחתת שגיאות.

שקול גרסה ממוּראת של מעגל עם עשרה קיוביטים ושתי שכבות ייחודיות של שערי CX. אלה הם המשימות העיקריות:

הצינור מורכב מהשלבים הבאים:

  1. הגדרה: יצירת המעגל הרצוי וקיבוץ פעולותיו לתיבות.
  2. למידה: למידת הרעש של ההוראות שאנחנו רוצים להפחית עם PEC.
  3. ביצוע: הרצת המעגל על Backend.
  4. ניתוח: עיבוד לאחר וניתוח התוצאות.

לצורך השוואה, נריץ את המעגל הממוּרא הזה פעמיים. פעם אחת עם Pauli-twirling בלבד, ופעם עם הפחתת PEC מלאה.

הערה

השימוש לדוגמה זו הוא כ-10 דקות על מעבד Heron r2.

הגדרת המעגל

בחר Backend והכן מעגל של 10 קיוביטים.

from qiskit_ibm_runtime import QiskitRuntimeService, Executor
from qiskit_ibm_runtime.quantum_program import QuantumProgram
from qiskit.circuit import QuantumCircuit, Parameter
from qiskit.transpiler import generate_preset_pass_manager
from samplomatic.transpiler import generate_boxing_pass_manager
from samplomatic import build

# Initialize the service and choose a backend
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

# Prepare a circuit

num_qubits = 10
num_layers = 10

qubits = list(range(num_qubits))
circuit = QuantumCircuit(num_qubits)

for layer_idx in range(num_layers):
circuit.rx(Parameter(f"theta_{layer_idx}"), qubits)
for i in range(num_qubits // 2):
circuit.cz(qubits[2 * i], qubits[2 * i + 1])

circuit.rx(Parameter(f"phi_{layer_idx}"), qubits)
for i in range(num_qubits // 2 - 1):
circuit.cz(qubits[2 * i] + 1, qubits[2 * i + 1] + 1)

circuit.draw("mpl", scale=0.35, fold=100)

Output of the previous code cell

שלב את המעגל עם ההופכי שלו כדי ליצור מעגל ממוּרא.

mirror_circuit = circuit.compose(circuit.inverse())
mirror_circuit.measure_all()

mirror_circuit.draw("mpl", scale=0.35, fold=100)

Output of the previous code cell

הגדר ערכי פרמטרים:

import numpy as np

parameter_values = np.random.rand(mirror_circuit.num_parameters)

השתמש במנהל ה-Pass כדי לבצע Transpile למעגל ולהפוך אותו למעגל ISA.

preset_pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=3,
)

isa_circuit = preset_pass_manager.run(mirror_circuit)

לאחר מכן, קבץ שערים ומדידות לתיבות עם הערות. ניתן לעשות זאת באופן ידני או להשתמש בפונקציה generate_boxing_pass_manager מ-Samplomatic לנוחות. המעגל הראשון יהיה עם twirling בלבד ולכן צריך רק את הערת ה-Twirl. המעגל השני יורץ עם הפחתת PEC מלאה וצריך הן את הערות Twirl והן את InjectNoise.

# Pass manager used to create twirled-annotated boxes.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
)

mirror_circuit_twirl = boxing_pm.run(isa_circuit)

# Pass manager used to create a new boxed circuit with
# both Twirl and InjectNoise annotations.
boxing_pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
inject_noise_targets="gates", # no measurement mitigation
inject_noise_strategy="uniform_modification",
)

mirror_circuit_pec = boxing_pm.run(isa_circuit)

למידת הרעש

כדי למזער את מספר ניסויי למידת הרעש, זהה את ההוראות הייחודיות במעגל השני (זה עם תיבות עם הערת InjectNoise). בהגדרת ייחודיות, שתי הוראות תיבה שוות אם שניהם מתקיים:

  • התוכן שלהן שווה, עד לשערים חד-קיוביטיים.
  • הערת ה-Twirl שלהן שווה (כל הערה אחרת מתעלמים ממנה).

זה מוביל לשלוש הוראות ייחודיות, כלומר תיבות השערים האי-זוגיים והזוגיים, ותיבת המדידה הסופית.

from samplomatic.utils import find_unique_box_instructions

unique_box_instructions = find_unique_box_instructions(
mirror_circuit_pec.data
)
assert len(unique_box_instructions) == 3

אתחל NoiseLearnerV3, בחר את פרמטרי הלמידה על ידי הגדרת האפשרויות שלו, והרץ משימת למידת רעש.

from qiskit_ibm_runtime.noise_learner_v3 import NoiseLearnerV3

learner = NoiseLearnerV3(backend)

learner.options.shots_per_randomization = 128
learner.options.num_randomizations = 32
learner.options.layer_pair_depths = [0, 1, 2, 4, 16, 32]

learner_job = learner.run(unique_box_instructions)

learner_job.job_id()
learner_result = learner_job.result()

המר את result לאובייקט הנדרש על ידי ה-samplex באמצעות שיטת result.to_dict.

noise_maps = learner_result.to_dict(
instructions=unique_box_instructions, require_refs=False
)

ביצוע המעגלים

Executor מריץ אובייקטי QuantumProgram. כל QuantumProgram יכול להכיל מספר פריטים, שמצורפים לתוכנית. כל פריט הוא משימה שהתוכנית מבצעת.

אתחל תוכנית ריקה, תוך בקשת 1000 shots לכל תצורה של כל פריט.

from qiskit_ibm_runtime.quantum_program import QuantumProgram

# Initialize an empty QuantumProgram
program = QuantumProgram(shots=1000)

לאחר מכן, בנה את תבנית המעגל וה-samplex עבור mirror_circuit_twirl וצרף אותם לתוכנית. בקש גם 900 אקראיות מה-samplex. זה אומר שה-samplex יצור 900 מערכות פרמטרים, וכל מערכת תורץ 1000 פעמים (מספר ה-shots) ב-QPU.

זוהי המשימה הראשונה של התוכנית (תוצאה 0).

template_twirl, samplex_twirl = build(mirror_circuit_twirl)

program.append_samplex_item(
template_twirl,
samplex=samplex_twirl,
samplex_arguments={"parameter_values": parameter_values},
shape=(900,),
)

באופן דומה, צרף את תבנית המעגל וה-samplex שנבנו עבור mirror_circuit_pec, תוך בקשת 900 אקראיות. זוהי המשימה השנייה של התוכנית (תוצאה 1).

template_pec, samplex_pec = build(mirror_circuit_pec)

program.append_samplex_item(
template_pec,
samplex=samplex_pec,
samplex_arguments={
"parameter_values": parameter_values,
"pauli_lindblad_maps": noise_maps,
"noise_scales": {
ref: -1.0 for ref in noise_maps
}, # Set the scales to -1 for PEC
},
shape=(900,),
)

ייבא את Executor והגש משימה.

from qiskit_ibm_runtime.executor import Executor

executor = Executor(backend)
executor_job = executor.run(program)

executor_job.job_id()

executor_results = executor_job.result()
executor_results

twirl_result = executor_results[0]

print(f"Twirl result keys:\n {list(twirl_result.keys())}\n")
print(f"Shape of results: {twirl_result['meas'].shape}")

pec_result = executor_results[1]

print(f"PEC result keys:\n {list(pec_result.keys())}\n")
print(f"Shape of results: {pec_result['meas'].shape}")
Twirl result keys:
['meas', 'measurement_flips.meas']

Shape of results: (900, 1000, 10)
PEC result keys:
['meas', 'measurement_flips.meas', 'pauli_signs']

Shape of results: (900, 1000, 10)

ניתוח התוצאות

לבסוף, בצע עיבוד לאחר של התוצאות לאמידת ערכי הציפייה של אופרטורי Pauli-Z חד-קיוביטיים הפועלים על כל אחד מעשרת הקיוביטים הפעילים (ערך צפוי: 1.0).

# Undo measurement twirling
twirl_result_unflipped = (
twirl_result["meas"] ^ twirl_result["measurement_flips.meas"]
)

# Calculate the expectation values of single-qubit Z operators
exp_vals = 1 - 2 * twirl_result_unflipped.mean(axis=1).mean(axis=0)

for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.77
Qubit 1 -> 0.76
Qubit 2 -> 0.66
Qubit 3 -> 0.71
Qubit 4 -> 0.69
Qubit 5 -> 0.67
Qubit 6 -> 0.62
Qubit 7 -> 0.59
Qubit 8 -> 0.62
Qubit 9 -> 0.68
# Undo measurement twirling
pec_result_unflipped = (
pec_result["meas"] ^ pec_result["measurement_flips.meas"]
)

# Calculate the signs for PEC mitigation
signs = np.prod((-1) ** pec_result["pauli_signs"], axis=-1)
signs = signs.reshape((signs.shape[0], 1))

# Calculate the expectation values of single-qubit Z operators as required by
# PEC mitigation
exp_vals = 1 - (2 * pec_result_unflipped.mean(axis=1) * signs).mean(axis=0)

for qubit, val in enumerate(exp_vals):
print(f"Qubit {qubit} -> {np.round(val, 2)}")
Qubit 0 -> 0.98
Qubit 1 -> 0.99
Qubit 2 -> 0.96
Qubit 3 -> 0.98
Qubit 4 -> 0.98
Qubit 5 -> 0.98
Qubit 6 -> 0.98
Qubit 7 -> 0.95
Qubit 8 -> 0.95
Qubit 9 -> 0.94

השלבים הבאים

המלצות