Eksperimen skala utilitas III
Toshinari Itoko, Tamiya Onodera, Kifumi Numata (19 Juli 2024)
Download pdf dari kuliah aslinya. Perhatikan bahwa beberapa cuplikan kode mungkin sudah usang karena ini adalah gambar statis.
Perkiraan waktu QPU untuk menjalankan eksperimen pertama ini adalah 12 menit 30 detik. Ada eksperimen tambahan di bawah yang membutuhkan sekitar 4 menit.
(Catatan: notebook ini mungkin tidak selesai dijalankan dalam waktu yang tersedia di Open Plan. Pastikan kamu menggunakan sumber daya komputasi kuantum dengan bijak.)
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime rustworkx
import qiskit
qiskit.__version__
'2.0.2'
import qiskit_ibm_runtime
qiskit_ibm_runtime.__version__
'0.40.1'
import numpy as np
import rustworkx as rx
from qiskit import QuantumCircuit
from qiskit.visualization import plot_histogram
from qiskit.visualization import plot_gate_map
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.providers import BackendV2
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Sampler, Estimator, Batch, SamplerOptions
1. Pendahuluan​
Mari kita tinjau singkat tentang GHZ state, dan distribusi seperti apa yang mungkin kamu harapkan dari Sampler ketika diterapkan pada state tersebut. Kemudian kita akan menjelaskan tujuan dari pelajaran ini secara eksplisit.
1.1 GHZ state​
GHZ state (Greenberger-Horne-Zeilinger state) untuk qubit didefinisikan sebagai
Secara alami, state ini dapat dibuat untuk 6 qubit dengan Circuit kuantum berikut.
N = 6
qc = QuantumCircuit(N, N)
qc.h(0)
for i in range(N - 1):
qc.cx(0, i + 1)
# qc.measure_all()
qc.barrier()
qc.measure(list(range(N)), list(range(N)))
qc.draw(output="mpl", idle_wires=False, scale=0.5)
print("Depth:", qc.depth())
Depth: 7
Kedalamannya tidak terlalu besar, meski kamu tahu dari pelajaran sebelumnya bahwa kamu bisa melakukan lebih baik. Mari kita pilih Backend dan transpile Circuit ini.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
backend.name
# or
# backend = service.least_busy(operational=True)
# backend.name
'ibm_kingston'
pm = generate_preset_pass_manager(3, backend=backend)
qc_transpiled = pm.run(qc)
qc_transpiled.draw(output="mpl", idle_wires=False, fold=-1)
print("Depth:", qc_transpiled.depth())
print(
"Two-qubit Depth:",
qc_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)
Depth: 27
Two-qubit Depth: 11
Kedalaman two-qubit hasil transpilasi juga tidak terlalu besar. Tapi untuk bekerja dengan GHZ state pada lebih banyak qubit, kamu jelas perlu memikirkan cara mengoptimalkan Circuit. Mari kita jalankan ini menggunakan Sampler dan lihat apa yang dikembalikan oleh komputer kuantum nyata.
sampler = Sampler(mode=backend)
shots = 40000
job = sampler.run([qc_transpiled], shots=shots)
job_id = job.job_id()
print(job_id)
d147y20n2txg008jvv70
job.status()
'DONE'
job = service.job(job_id)
result = job.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))

Ini adalah hasil dari GHZ Circuit 6-qubit. Seperti yang kamu lihat, state semua dan semua memang mendominasi, tapi errornya cukup besar. Mari kita coba lihat seberapa besar GHZ Circuit yang bisa kamu buat dengan perangkat Eagle, sambil tetap mendapatkan hasil di mana state yang benar setidaknya lebih dari 50% kemungkinannya.
1.2 Tujuanmu​
Bangun GHZ Circuit untuk 20 qubit atau lebih sehingga, setelah pengukuran, fidelitas GHZ state-mu > 0.5. Catatan:
- Kamu perlu menggunakan perangkat Eagle (
min_num_qubits=127) dan mengatur jumlah shots sebesar 40.000. - Kamu harus menjalankan GHZ Circuit menggunakan fungsi
execute_ghz_fidelity, dan menghitung fidelitas menggunakan fungsicheck_ghz_fidelity_from_jobs.
Ini dimaksudkan sebagai latihan mandiri, di mana kamu memanfaatkan apa yang sudah kamu pelajari sejauh ini dalam kursus ini.
def execute_ghz_fidelity(
ghz_circuit: QuantumCircuit, # Quantum circuit to create GHZ state (Circuit after Routing or without Routing), Classical register name is "c"
physical_qubits: list[int], # Physical qubits to represent GHZ state
backend: BackendV2,
sampler_options: dict | SamplerOptions | None = None,
):
N_SHOTS = 40_000
N = len(physical_qubits)
base_circuit = ghz_circuit.remove_final_measurements(inplace=False)
# M_k measurement circuits
mk_circuits = []
for k in range(1, N + 1):
circuit = base_circuit.copy()
# change measurement basis
for q in physical_qubits:
circuit.rz(-k * np.pi / N, q)
circuit.h(q)
mk_circuits.append(circuit)
obs = SparsePauliOp.from_sparse_list(
[("Z" * N, physical_qubits, 1)], num_qubits=backend.num_qubits
)
job_ids = []
pm1 = generate_preset_pass_manager(1, backend=backend)
org_transpiled = pm1.run(ghz_circuit)
mk_transpiled = pm1.run(mk_circuits)
with Batch(backend=backend):
sampler = Sampler(options=sampler_options)
sampler.options.twirling.enable_measure = True
job = sampler.run([org_transpiled], shots=N_SHOTS)
job_ids.append(job.job_id())
# print(f"Sampler job id: {job.job_id()}, shots={N_SHOTS}")
estimator = Estimator() # TREX is applied as default
estimator.options.dynamical_decoupling.enable = True
estimator.options.execution.rep_delay = 0.0005
estimator.options.twirling.enable_measure = True
job2 = estimator.run([(circ, obs) for circ in mk_transpiled], precision=1 / 100)
job_ids.append(job2.job_id())
# print("Estimator job id:", job2.job_id())
return [job.job_id(), job2.job_id()]
def check_ghz_fidelity_from_jobs(
sampler_job,
estimator_job,
num_qubits,
shots=40_000,
):
N = num_qubits
sampler_result = sampler_job.result()
counts = sampler_result[0].data.c.get_counts()
all_zero = counts.get("0" * N, 0) / shots
all_one = counts.get("1" * N, 0) / shots
top3 = sorted(counts, key=counts.get, reverse=True)[:3]
print(
f"N={N}: |00..0>: {counts.get('0'*N, 0)}, |11..1>: {counts.get('1'*N, 0)}, |3rd>: {counts.get(top3[2], 0)} ({top3[2]})"
)
print(f"P(|00..0>)={all_zero}, P(|11..1>)={all_one}")
estimator_result = estimator_job.result()
non_diagonal = (1 / N) * sum(
(-1) ** k * estimator_result[k - 1].data.evs for k in range(1, N + 1)
)
print(f"REM: Coherence (non-diagonal): {non_diagonal:.6f}")
fidelity = 0.5 * (all_zero + all_one + non_diagonal)
sigma = 0.5 * np.sqrt(
(1 - all_zero - all_one) * (all_zero + all_one) / shots
+ sum(estimator_result[k].data.stds ** 2 for k in range(N)) / (N * N)
)
print(f"GHZ fidelity = {fidelity:.6f} ± {sigma:.6f}")
if fidelity - 2 * sigma > 0.5:
print("GME (genuinely multipartite entangled) test: Passed")
else:
print("GME (genuinely multipartite entangled) test: Failed")
return {
"fidelity": fidelity,
"sigma": sigma,
"shots": shots,
"job_ids": [sampler_job.job_id(), estimator_job.job_id()],
}
Dalam notebook ini, kita akan menerapkan tiga strategi untuk membuat GHZ state yang baik menggunakan 16 qubit dan 30 qubit. Pendekatan-pendekatan ini dibangun di atas strategi yang sudah kamu ketahui dari pelajaran sebelumnya.
2. Strategi 1. Pemilihan qubit berbasis noise​
Pertama, kita tentukan Backend-nya. Karena kita akan bekerja secara intensif dengan properti Backend tertentu, ada baiknya kita menentukan satu Backend, daripada menggunakan opsi least_busy.
backend = service.backend("ibm_strasbourg") # eagle
twoq_gate = "ecr"
print(f"Device {backend.name} Loaded with {backend.num_qubits} qubits")
print(f"Two Qubit Gate: {twoq_gate}")
Device ibm_strasbourg Loaded with 127 qubits
Two Qubit Gate: ecr
Kita akan membangun Circuit yang melibatkan banyak Gate dua qubit. Masuk akal untuk menggunakan qubit-qubit yang memiliki error paling rendah saat mengimplementasikan Gate dua qubit tersebut. Menemukan "rantai qubit" terbaik berdasarkan error Gate 2q yang dilaporkan adalah masalah yang tidak trivial. Tapi kita bisa mendefinisikan beberapa fungsi untuk membantu kita menentukan qubit mana yang terbaik untuk digunakan.
coupling_map = backend.target.build_coupling_map(twoq_gate)
G = coupling_map.graph
def to_edges(path): # create edges list from node paths
edges = []
prev_node = None
for node in path:
if prev_node is not None:
if G.has_edge(prev_node, node):
edges.append((prev_node, node))
else:
edges.append((node, prev_node))
prev_node = node
return edges
def path_fidelity(path, correct_by_duration: bool = True, readout_scale: float = None):
"""Compute an estimate of the total fidelity of 2-qubit gates on a path.
If `correct_by_duration` is true, each gate fidelity is worsen by
scale = max_duration / duration, that is, gate_fidelity^scale.
If `readout_scale` > 0 is supplied, readout_fidelity^readout_scale
for each qubit on the path is multiplied to the total fielity.
The path is given in node indices form, for example, [0, 1, 2].
An external function `to_edges` is used to obtain edge list, for example, [(0, 1), (1, 2)]."""
path_edges = to_edges(path)
max_duration = max(backend.target[twoq_gate][qs].duration for qs in path_edges)
def gate_fidelity(qpair):
duration = backend.target[twoq_gate][qpair].duration
scale = max_duration / duration if correct_by_duration else 1.0
# 1.25 = (d+1)/d with d = 4
return max(0.25, 1 - (1.25 * backend.target[twoq_gate][qpair].error)) ** scale
def readout_fidelity(qubit):
return max(0.25, 1 - backend.target["measure"][(qubit,)].error)
total_fidelity = np.prod(
[gate_fidelity(qs) for qs in path_edges]
) # two qubits gate fidelity for each path
if readout_scale:
total_fidelity *= (
np.prod([readout_fidelity(q) for q in path]) ** readout_scale
) # multiply readout fidelity
return total_fidelity
def flatten(paths, cutoff=None): # cutoff is for not making run time too large
return [
path
for s, s_paths in paths.items()
for t, st_paths in s_paths.items()
for path in st_paths[:cutoff]
if s < t
]
N = 16 # Number of qubits to use in the GHZ circuit
num_qubits_in_chain = N
Kita akan menggunakan fungsi-fungsi di atas untuk menemukan semua simple path dengan N qubit di antara semua pasang node dalam graf (Referensi: all_pairs_all_simple_paths).
Kemudian, menggunakan fungsi path_fidelity yang dibuat di atas, kita akan menemukan rantai qubit terbaik yang memiliki fidelitas path terbesar.
from functools import partial
%%time
paths = rx.all_pairs_all_simple_paths(
G.to_undirected(multigraph=False),
min_depth=num_qubits_in_chain,
cutoff=num_qubits_in_chain,
)
paths = flatten(paths, cutoff=25) # If you have time, you could set a larger cutoff.
if not paths:
raise Exception(
f"No qubit chain with length={num_qubits_in_chain} exists in {backend.name}. Try smaller num_qubits_in_chain."
)
print(f"Selecting the best from {len(paths)} candidate paths")
best_qubit_chain = max(
paths, key=partial(path_fidelity, correct_by_duration=True, readout_scale=1.0)
)
assert len(best_qubit_chain) == num_qubits_in_chain
print(f"Predicted (best possible) process fidelity: {path_fidelity(best_qubit_chain)}")
Selecting the best from 6046 candidate paths
Predicted (best possible) process fidelity: 0.8929026784775056
CPU times: user 284 ms, sys: 10.9 ms, total: 295 ms
Wall time: 295 ms
np.array(best_qubit_chain)
array([55, 49, 48, 47, 46, 45, 54, 64, 65, 66, 73, 85, 86, 87, 88, 89],
dtype=uint64)
Mari kita plot rantai qubit terbaik, ditunjukkan dengan warna pink, dalam diagram coupling map.
qubit_color = []
for i in range(133):
if i in best_qubit_chain:
qubit_color.append("#ff00dd") # pink
else:
qubit_color.append("#8c00ff") # purple
plot_gate_map(
backend, qubit_color=qubit_color, qubit_size=50, font_size=25, figsize=(6, 6)
)

2.1 Membangun GHZ Circuit pada rantai qubit terbaik​
Kita memilih qubit di tengah rantai untuk pertama-tama menerapkan Gate H. Ini seharusnya mengurangi kedalaman Circuit hingga sekitar setengahnya.
ghz1 = QuantumCircuit(max(best_qubit_chain) + 1, N)
ghz1.h(best_qubit_chain[N // 2])
for i in range(N // 2, 0, -1):
ghz1.cx(best_qubit_chain[i], best_qubit_chain[i - 1])
for i in range(N // 2, N - 1, +1):
ghz1.cx(best_qubit_chain[i], best_qubit_chain[i + 1])
ghz1.barrier() # for visualization
ghz1.measure(best_qubit_chain, list(range(N)))
ghz1.draw(output="mpl", idle_wires=False, scale=0.5, fold=-1)
ghz1.depth()
10
pm = generate_preset_pass_manager(1, backend=backend)
ghz1_transpiled = pm.run(ghz1)
ghz1_transpiled.draw(output="mpl", idle_wires=False, fold=-1)

print("Depth:", ghz1_transpiled.depth())
print(
"Two-qubit Depth:",
ghz1_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)
Depth: 27
Two-qubit Depth: 8
opts = SamplerOptions()
res = execute_ghz_fidelity(
ghz_circuit=ghz1,
physical_qubits=best_qubit_chain,
backend=backend,
sampler_options=opts,
)
job_s = service.job(res[0]) # Use your job id showed above.
job_e = service.job(res[1])
print(job_s.status(), job_e.status())
DONE DONE
Pastikan kamu menjalankan sel berikutnya setelah status job di atas menjadi 'DONE', untuk menampilkan hasilnya menggunakan fungsi check_ghz_fidelity_from_jobs.
N = 16
# Check fidelity from job IDs
res = check_ghz_fidelity_from_jobs(
sampler_job=job_s,
estimator_job=job_e,
num_qubits=N,
)
N=16: |00..0>: 153, |11..1>: 8681, |3rd>: 2262 (1111111111101111)
P(|00..0>)=0.003825, P(|11..1>)=0.217025
REM: Coherence (non-diagonal): 0.073809
GHZ fidelity = 0.147329 ± 0.002438
GME (genuinely multipartite entangled) test: Failed
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))

Hasil ini tidak memenuhi kriteria. Mari kita beralih ke ide berikutnya.
3. Strategi 2. Pohon qubit yang seimbang​
Ide berikutnya adalah mencari pohon qubit yang seimbang. Dengan menggunakan pohon daripada rantai, kedalaman Circuit seharusnya menjadi lebih rendah. Sebelum itu, kita hapus node dengan error readout yang "buruk" dan edge dengan error Gate yang "buruk" dari graf coupling.
BAD_READOUT_ERROR_THRESHOLD = 0.1
BAD_ECRGATE_ERROR_THRESHOLD = 0.1
bad_readout_qubits = [
q
for q in range(backend.num_qubits)
if backend.target["measure"][(q,)].error > BAD_READOUT_ERROR_THRESHOLD
]
bad_ecrgate_edges = [
qpair
for qpair in backend.target["ecr"]
if backend.target["ecr"][qpair].error > BAD_ECRGATE_ERROR_THRESHOLD
]
print("Bad readout qubits:", bad_readout_qubits)
print("Bad ECR gates:", bad_ecrgate_edges)
Bad readout qubits: [19, 28, 41, 72, 91, 114, 120]
Bad ECR gates: []
g = backend.coupling_map.graph.copy().to_undirected()
g.remove_edges_from(
bad_ecrgate_edges
) # remove edge first (otherwise might fail with a NoEdgeBetweenNodes error)
g.remove_nodes_from(bad_readout_qubits)
Mari kita gambar graf coupling map tanpa edge dan qubit yang buruk.
qubit_color = []
for i in range(133):
if i in bad_readout_qubits:
qubit_color.append("#000000") # black
else:
qubit_color.append("#8c00ff") # purple
line_color = []
for e in backend.target.build_coupling_map().get_edges():
if e in bad_ecrgate_edges:
line_color.append("#ffffff") # white
else:
line_color.append("#888888") # gray
plot_gate_map(
backend,
qubit_color=qubit_color,
line_color=line_color,
qubit_size=50,
font_size=25,
figsize=(6, 6),
)

Kita akan mencoba membuat state GHZ 16-Qubit seperti sebelumnya.
N = 16
Kita panggil fungsi betweenness_centrality untuk mencari Qubit sebagai node root. Node dengan nilai betweenness centrality tertinggi berada di pusat graf. Referensi: https://www.rustworkx.org/tutorial/betweenness_centrality.html
Atau kamu bisa memilihnya secara manual.
# central = 65 #Select the center node manually
c_degree = dict(rx.betweenness_centrality(g))
central = max(c_degree, key=c_degree.get)
central
66
Mulai dari node root, buat pohon menggunakan breadth first search (BFS). Referensi: https://qiskit.org/ecosystem/rustworkx/apiref/rustworkx.bfs_search.html#rustworkx-bfs-search
class TreeEdgesRecorder(rx.visit.BFSVisitor):
def __init__(self, N):
self.edges = []
self.N = N
def tree_edge(self, edge):
self.edges.append(edge)
if len(self.edges) >= self.N - 1:
raise rx.visit.StopSearch()
vis = TreeEdgesRecorder(N)
rx.bfs_search(g, [central], vis)
best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))
# print('Tree edges:', vis.edges)
print("Qubits selected:", best_qubits)
Qubits selected: [54, 55, 63, 64, 65, 66, 67, 68, 69, 70, 73, 83, 84, 85, 86, 87]
Mari kita plot Qubit-qubit yang dipilih, ditampilkan dengan warna pink, pada diagram coupling map.
qubit_color = []
for i in range(133):
if i in bad_readout_qubits:
qubit_color.append("#000000") # black
elif i in best_qubits:
qubit_color.append("#ff00dd") # pink
else:
qubit_color.append("#8c00ff") # purple
plot_gate_map(
backend,
qubit_color=qubit_color,
line_color=line_color,
qubit_size=50,
font_size=25,
figsize=(6, 6),
)

Mari kita tampilkan struktur pohon dari Qubit-qubit tersebut.
from rustworkx.visualization import graphviz_draw
tree = rx.PyDiGraph()
tree.extend_from_weighted_edge_list(vis.edges)
tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])
graphviz_draw(tree, method="dot")
ghz2 = QuantumCircuit(max(best_qubits) + 1, N)
ghz2.h(tree.edge_list()[0][0]) # apply H-gate to the root node
# Apply CNOT from the root node to the each edge.
for u, v in tree.edge_list():
ghz2.cx(u, v)
ghz2.barrier() # for visualization
ghz2.measure(best_qubits, list(range(N)))
ghz2.draw(output="mpl", idle_wires=False, scale=0.5)
ghz2.depth()
8
pm = generate_preset_pass_manager(1, backend=backend)
ghz2_transpiled = pm.run(ghz2)
ghz2_transpiled.draw(output="mpl", idle_wires=False, fold=-1)

print("Depth:", ghz2_transpiled.depth())
print(
"Two-qubit Depth:",
ghz2_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)
Depth: 22
Two-qubit Depth: 6
Kedalaman Circuit sekarang menjadi jauh lebih rendah dibandingkan struktur rantai.
res = execute_ghz_fidelity(
ghz_circuit=ghz2,
physical_qubits=best_qubits,
backend=backend,
sampler_options=opts,
)
job_s = service.job(res[0]) # Use your job id showed above.
job_e = service.job(res[1])
print(job_s.status(), job_e.status())
DONE DONE
N = 16
# Check fidelity from job IDs
res = check_ghz_fidelity_from_jobs(
sampler_job=job_s,
estimator_job=job_e,
num_qubits=N,
)
N=16: |00..0>: 9509, |11..1>: 10978, |3rd>: 1795 (1111110111111111)
P(|00..0>)=0.237725, P(|11..1>)=0.27445
REM: Coherence (non-diagonal): 0.606515
GHZ fidelity = 0.559345 ± 0.003188
GME (genuinely multipartite entangled) test: Passed
Kita berhasil melewati kriteria dengan struktur pohon yang seimbang!
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))

Sekarang, mari kita coba membuat state GHZ yang lebih besar: state GHZ 30-Qubit.
3.1 N = 30​
Kita akan mengikuti kerangka kerja Qiskit patterns.
- Langkah 1: Pemetaan masalah ke Circuit dan operator kuantum
- Langkah 2: Optimasi untuk hardware target
- Langkah 3: Eksekusi pada hardware target
- Langkah 4: Pasca-pemrosesan hasil
Langkah 1: Pemetaan masalah ke Circuit dan operator kuantum serta Langkah 2: Optimasi untuk hardware target​
Di sini kita memilih node root secara manual.
central = 62 # Select the center node manually
# c_degree = dict(rx.betweenness_centrality(g))
# central = max(c_degree, key=c_degree.get)
# central
N = 30
vis = TreeEdgesRecorder(N)
rx.bfs_search(g, [central], vis)
best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))
print("Qubits selected:", best_qubits)
Qubits selected: [34, 35, 42, 43, 44, 45, 46, 47, 48, 53, 54, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 71, 73, 77, 84, 85, 86]
qubit_color = []
for i in range(133):
if i in bad_readout_qubits:
qubit_color.append("#000000")
elif i in best_qubits:
qubit_color.append("#ff00dd")
else:
qubit_color.append("#8c00ff")
line_color = []
for e in backend.target.build_coupling_map().get_edges():
if e in bad_ecrgate_edges:
line_color.append("#ffffff")
else:
line_color.append("#888888")
plot_gate_map(
backend,
qubit_color=qubit_color,
line_color=line_color,
qubit_size=50,
font_size=25,
figsize=(6, 6),
)

from rustworkx.visualization import graphviz_draw
tree = rx.PyDiGraph()
tree.extend_from_weighted_edge_list(vis.edges)
tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])
graphviz_draw(tree, method="dot")

Kedalaman pohon ini adalah 5.
ghz3 = QuantumCircuit(max(best_qubits) + 1, N)
ghz3.h(tree.edge_list()[0][0]) # apply H-gate to the root node
# Apply CNOT from the root node to the each edge.
for u, v in tree.edge_list():
ghz3.cx(u, v)
ghz3.barrier() # for visualization
ghz3.measure(best_qubits, list(range(N)))
ghz3.draw(output="mpl", idle_wires=False, fold=-1)

ghz3.depth()
11
pm = generate_preset_pass_manager(1, backend=backend)
ghz3_transpiled = pm.run(ghz3)
ghz3_transpiled.draw(output="mpl", idle_wires=False, fold=-1)

print("Depth:", ghz3_transpiled.depth())
print(
"Two-qubit Depth:",
ghz3_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)
Depth: 31
Two-qubit Depth: 9
3.2 Pilih node root yang berbeda secara manual​
central = 54
vis = TreeEdgesRecorder(N)
rx.bfs_search(g, [central], vis)
best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))
print("Qubits selected:", best_qubits)
Qubits selected: [23, 24, 25, 34, 35, 42, 43, 44, 45, 46, 47, 48, 49, 50, 54, 55, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 73, 84, 85, 86]
from rustworkx.visualization import graphviz_draw
tree = rx.PyDiGraph()
tree.extend_from_weighted_edge_list(vis.edges)
tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])
graphviz_draw(tree, method="dot")

Kedalaman pohon ini adalah 6.
ghz3 = QuantumCircuit(max(best_qubits) + 1, N)
ghz3.h(tree.edge_list()[0][0]) # apply H-gate to the root node
# Apply CNOT from the root node to the each edge.
for u, v in tree.edge_list():
ghz3.cx(u, v)
ghz3.barrier() # for visualization
ghz3.measure(best_qubits, list(range(N)))
ghz3.draw(output="mpl", idle_wires=False, fold=-1)

ghz3.depth()
11
pm = generate_preset_pass_manager(1, backend=backend)
ghz3_transpiled = pm.run(ghz3)
ghz3_transpiled.draw(output="mpl", idle_wires=False, fold=-1)

print("Depth:", ghz3_transpiled.depth())
print(
"Two-qubit Depth:",
ghz3_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)
Depth: 30
Two-qubit Depth: 9
Yang mengejutkan, meskipun kedalaman pohon meningkat dari 5 ke 6, kedalaman dua-Qubit justru turun dari 9 ke 8! Jadi mari kita gunakan Circuit yang terakhir.
Langkah 3: Eksekusi pada hardware target​
res = execute_ghz_fidelity(
ghz_circuit=ghz3,
physical_qubits=best_qubits,
backend=backend,
sampler_options=opts,
)
job_s = service.job(res[0]) # Use your job id showed above.
job_e = service.job(res[1])
print(job_s.status(), job_e.status())
DONE DONE
Langkah 4: Pasca-pemrosesan hasil​
N = 30
# Check fidelity from job IDs
res = check_ghz_fidelity_from_jobs(
sampler_job=job_s,
estimator_job=job_e,
num_qubits=N,
)
N=30: |00..0>: 4, |11..1>: 218, |3rd>: 265 (111111111111111011111111111111)
P(|00..0>)=0.0001, P(|11..1>)=0.00545
REM: Coherence (non-diagonal): 0.187073
GHZ fidelity = 0.096312 ± 0.003254
GME (genuinely multipartite entangled) test: Failed
Seperti yang kamu lihat, hasil ini belum memenuhi kriteria.
# It will take some time
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))

4. Strategi 3. Jalankan dengan opsi error suppression​
Kamu bisa mengatur opsi error suppression di Sampler V2. Lihat panduan Configure error mitigation dan referensi API ExecutionOptionsV2 untuk informasi lebih lanjut.
opts = SamplerOptions()
opts.dynamical_decoupling.enable = True
opts.execution.rep_delay = 0.0005
opts.twirling.enable_gates = True
res = execute_ghz_fidelity(
ghz_circuit=ghz3,
physical_qubits=best_qubits,
backend=backend,
sampler_options=opts,
)
job_s = service.job(res[0]) # Use your job id showed above.
job_e = service.job(res[1])
print(job_s.status(), job_e.status())
DONE DONE
N = 30
# Check fidelity from job IDs
res = check_ghz_fidelity_from_jobs(
sampler_job=job_s,
estimator_job=job_e,
num_qubits=N,
)
N=30: |00..0>: 1459, |11..1>: 1543, |3rd>: 359 (111111111111111111111111111110)
P(|00..0>)=0.036475, P(|11..1>)=0.038575
REM: Coherence (non-diagonal): 0.165532
GHZ fidelity = 0.120291 ± 0.003369
GME (genuinely multipartite entangled) test: Failed
# It will take some time
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))

Hasilnya membaik tetapi masih belum memenuhi kriteria.
Kita sudah melihat tiga ide sejauh ini. Kamu bisa menggabungkan dan mengembangkan ide-ide ini, atau kamu bisa datang dengan ide sendiri untuk membuat Circuit GHZ yang lebih baik. Sekarang mari kita tinjau kembali tujuannya.
5. Tujuanmu (rekap)​
Buat sebuah GHZ Circuit untuk 20 qubit atau lebih agar hasil pengukurannya memenuhi kriteria: Fidelitas GHZ state-mu > 0.5.
- Kamu perlu menggunakan perangkat Eagle (seperti
ibm_brisbane) dan mengatur jumlah shots sebesar 40.000. - Kamu harus mengeksekusi GHZ Circuit menggunakan fungsi
execute_ghz_fidelity, dan menghitung fidelitas menggunakan fungsicheck_ghz_fidelity_from_jobs. Kamu perlu menemukan GHZ Circuit dengan qubit terbanyak yang memenuhi kriteria tersebut. Tulis kode kamu di bawah ini, dan tampilkan hasilnya dengan fungsicheck_ghz_fidelity_from_jobs.
Sekarang kita menerapkan alur kerja GHZ yang sama seperti pada materi sebelumnya, tetapi pada perangkat Heron. Ini memberimu pengalaman dengan tata letak dan fitur prosesor Heron. Tidak ada strategi baru yang diperkenalkan.
Perkiraan waktu QPU untuk menjalankan eksperimen berikutnya adalah 4 menit 40 detik.
service = QiskitRuntimeService()
backend = service.backend("ibm_kingston")
# backend = service.backend("ibm_fez")
twoq_gate = "cz"
print(f"Device {backend.name} Loaded with {backend.num_qubits} qubits")
print(f"Two Qubit Gate: {twoq_gate}")
Device ibm_kingston Loaded with 156 qubits
Two Qubit Gate: cz
BAD_READOUT_ERROR_THRESHOLD = 0.1
BAD_CZGATE_ERROR_THRESHOLD = 0.1
bad_readout_qubits = [
q
for q in range(backend.num_qubits)
if backend.target["measure"][(q,)].error > BAD_READOUT_ERROR_THRESHOLD
]
bad_czgate_edges = [
qpair
for qpair in backend.target["cz"]
if backend.target["cz"][qpair].error > BAD_CZGATE_ERROR_THRESHOLD
]
print("Bad readout qubits:", bad_readout_qubits)
print("Bad CZ gates:", bad_czgate_edges)
Bad readout qubits: [112, 113, 120, 131, 146]
Bad CZ gates: [(111, 112), (112, 111), (112, 113), (113, 112), (120, 121), (121, 120), (130, 131), (131, 130), (145, 146), (146, 145), (146, 147), (147, 146)]
g = backend.coupling_map.graph.copy().to_undirected()
g.remove_edges_from(
bad_czgate_edges
) # remove edge first (otherwise might fail with a NoEdgeBetweenNodes error)
g.remove_nodes_from(bad_readout_qubits)
qubit_color = []
for i in range(backend.num_qubits):
if i in bad_readout_qubits:
qubit_color.append("#000000") # black
else:
qubit_color.append("#8c00ff") # purple
line_color = []
for e in backend.target.build_coupling_map().get_edges():
if e in bad_czgate_edges:
line_color.append("#ffffff") # white
else:
line_color.append("#888888") # gray
plot_gate_map(
backend,
qubit_color=qubit_color,
line_color=line_color,
qubit_size=60,
font_size=30,
figsize=(10, 10),
)

N = 40
central = 100 # Select the center node manually
# c_degree = dict(rx.betweenness_centrality(g))
# central = max(c_degree, key=c_degree.get)
# central
class TreeEdgesRecorder(rx.visit.BFSVisitor):
def __init__(self, N):
self.edges = []
self.N = N
def tree_edge(self, edge):
self.edges.append(edge)
if len(self.edges) >= self.N - 1:
raise rx.visit.StopSearch()
vis = TreeEdgesRecorder(N)
rx.bfs_search(g, [central], vis)
best_qubits = sorted(list(set(q for e in vis.edges for q in (e[0], e[1]))))
print("Qubits selected:", best_qubits)
Qubits selected: [61, 65, 76, 77, 80, 81, 82, 83, 84, 85, 86, 87, 96, 97, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 116, 117, 121, 122, 123, 124, 125, 126, 127, 136, 140, 141, 142, 143, 144, 145]
qubit_color = []
for i in range(backend.num_qubits):
if i in bad_readout_qubits:
qubit_color.append("#000000")
elif i in best_qubits:
qubit_color.append("#ff00dd")
else:
qubit_color.append("#8c00ff")
line_color = []
for e in backend.target.build_coupling_map().get_edges():
if e in bad_czgate_edges:
line_color.append("#ffffff")
else:
line_color.append("#888888")
plot_gate_map(
backend,
qubit_color=qubit_color,
line_color=line_color,
qubit_size=60,
font_size=30,
figsize=(10, 10),
)

from rustworkx.visualization import graphviz_draw
tree = rx.PyDiGraph()
tree.extend_from_weighted_edge_list(vis.edges)
tree.remove_nodes_from([n for n in range(max(best_qubits) + 1) if n not in best_qubits])
graphviz_draw(tree, method="dot")

ghz_h = QuantumCircuit(max(best_qubits) + 1, N)
ghz_h.h(tree.edge_list()[0][0]) # apply H-gate to the root node
# Apply CNOT from the root node to the each edge.
for u, v in tree.edge_list():
ghz_h.cx(u, v)
ghz_h.barrier() # for visualization
ghz_h.measure(best_qubits, list(range(N)))
ghz_h.draw(output="mpl", idle_wires=False, fold=-1)

ghz_h.depth()
15
pm = generate_preset_pass_manager(1, backend=backend)
ghz_h_transpiled = pm.run(ghz_h)
ghz_h_transpiled.draw(output="mpl", idle_wires=False, fold=-1)

print("Depth:", ghz_h_transpiled.depth())
print(
"Two-qubit Depth:",
ghz_h_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)
Depth: 45
Two-qubit Depth: 13
opts = SamplerOptions()
opts.dynamical_decoupling.enable = True
opts.execution.rep_delay = 0.0005
opts.twirling.enable_gates = True
res = execute_ghz_fidelity(
ghz_circuit=ghz_h,
physical_qubits=best_qubits,
backend=backend,
sampler_options=opts,
)
job_s = service.job(res[0]) # Use your job id showed above.
job_e = service.job(res[1])
print(job_s.status(), job_e.status())
RUNNING RUNNING
# Check fidelity from job IDs
N = 40
res = check_ghz_fidelity_from_jobs(
sampler_job=job_s,
estimator_job=job_e,
num_qubits=N,
)
N=40: |00..0>: 3186, |11..1>: 3277, |3rd>: 620 (1111111011111111111111111111111111111111)
P(|00..0>)=0.07965, P(|11..1>)=0.081925
REM: Coherence (non-diagonal): 0.029987
GHZ fidelity = 0.095781 ± 0.002619
GME (genuinely multipartite entangled) test: Failed
# It will take some time
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))

Selamat! Kamu telah menyelesaikan pengantar komputasi kuantum skala utilitas! Kamu kini siap untuk memberikan kontribusi bermakna dalam pencarian quantum advantage! Terima kasih telah menjadikan IBM Quantum® bagian dari perjalanan kuantum pribadimu.
Survei pasca-kursus​
Selamat telah menyelesaikan kursus ini! Luangkan sebentar untuk membantu kami meningkatkan kursus dengan mengisi survei singkat berikut. Masukan kamu akan digunakan untuk meningkatkan konten dan pengalaman pengguna kami. Terima kasih!
Note: This survey is provided by IBM Quantum and relates to the original English content. To give feedback on doQumentation's website, translations, or code execution, please open a GitHub issue.