유틸리티 규모 실험 III
토시나리 이토코, 타미야 오노데라, 키후미 누마타 (2024년 7월 19일)
원본 강의의 PDF를 다운로드하세요. 일부 코드 스니펫은 정적 이미지이므로 더 이상 사용되지 않을 수 있습니다.
이 첫 번째 실험을 실행하는 데 걸리는 대략적인 QPU 시간은 12m 30초입니다. 아래에 약 4m가 필요한 추가 실험이 있습니다.
(참고: 이 노트북은 오픈 플랜에서 허용된 시간 내에 평가되지 않을 수 있습니다. 양자 컴퓨팅 리소스를 현명하게 사용하세요.)
import qiskit
qiskit.__version__Output:
'2.0.2'
import qiskit_ibm_runtime
qiskit_ibm_runtime.__version__Output:
'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, SamplerOptions1. 소개
GHZ 상태와 Sampler 에 적용된 분포의 종류에 대해 간략하게 살펴보겠습니다. 그런 다음 이 강의의 목표를 명시적으로 설명하겠습니다.
1.1 GHZ 상태
큐비트에 대한 GHZ 상태(그린버거-혼-자일링거 상태)는 다음과 같이 정의됩니다
당연히 다음과 같은 양자 회로를 사용하여 6 큐비트를 생성할 수 있습니다.
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)Output:
print("Depth:", qc.depth())Output:
Depth: 7
이전 강의를 통해 더 잘할 수 있다는 것을 알고 있지만 깊이가 너무 크지는 않습니다. 백엔드를 선택하고 이 회로를 트랜스파일해 보겠습니다.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
backend.name
# or
# backend = service.least_busy(operational=True)
# backend.nameOutput:
'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)Output:
print("Depth:", qc_transpiled.depth())
print(
"Two-qubit Depth:",
qc_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)Output:
Depth: 27
Two-qubit Depth: 11
이번에도 트랜스파일된 2쿼비트 깊이는 너무 크지 않습니다. 그러나 더 많은 큐비트에서 GHZ 상태로 작업하려면 회로 최적화에 대해 분명히 생각해야 합니다. Sampler 을 사용하여 실행하고 실제 양자 컴퓨터의 결과를 확인해 보겠습니다.
sampler = Sampler(mode=backend)
shots = 40000
job = sampler.run([qc_transpiled], shots=shots)
job_id = job.job_id()
print(job_id)Output:
d147y20n2txg008jvv70
job.status()Output:
'DONE'
job = service.job(job_id)
result = job.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))Output:
이것은 6-큐비트 GHZ 회로의 결과입니다. 보시다시피 모든 및 모든 의 상태가 지배적이지만 오류는 상당합니다. Eagle 장치로 얼마나 큰 GHZ 회로를 만들 수 있는지, 그러면서도 올바른 상태가 50% 이상일 가능성이 있는 결과를 얻을 수 있는지 확인해 보겠습니다.
1.2 당신의 목표
20큐비트 이상의 GHZ 회로를 구축하여 측정 시 GHZ 상태의 충실도 > 0.5 참고:
- Eagle 디바이스(
min_num_qubits=127)를 사용하고 촬영 횟수를 40,000회로 설정해야 합니다. execute_ghz_fidelity함수를 사용하여 GHZ 회로를 실행하고check_ghz_fidelity_from_jobs함수를 사용하여 충실도를 계산해야 합니다.
이 연습은 이 과정에서 지금까지 배운 내용을 활용하는 독립적인 연습입니다.
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()],
}이 노트에서는 16큐비트와 30큐비트를 사용하여 좋은 GHZ 상태를 만들기 위한 세 가지 전략을 적용합니다. 이러한 접근 방식은 이전 강의에서 이미 알고 있는 전략을 기반으로 합니다.
2. 전략 1. 잡음 인식 큐비트 선택
먼저 백엔드를 지정합니다. 특정 백엔드의 속성으로 광범위하게 작업할 것이므로 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}")Output:
Device ibm_strasbourg Loaded with 127 qubits
Two Qubit Gate: ecr
많은 2큐비트 게이트가 포함된 회로를 만들려고 합니다. 2큐비트 게이트를 구현할 때 오류가 가장 낮은 큐비트를 사용하는 것이 합리적입니다. 보고된 2q-gate 오류를 기반으로 최적의 '큐비트 체인'을 찾는 것은 간단한 문제가 아닙니다. 하지만 사용할 최적의 큐비트를 결정하는 데 도움이 되는 몇 가지 함수를 정의할 수 있습니다.
coupling_map = backend.target.build_coupling_map(twoq_gate)
G = coupling_map.graphdef 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위의 함수를 사용해 그래프의 모든 노드 쌍 사이에서 N 큐비트의 모든 단순 경로를 찾겠습니다(참조: all_pairs_all_simple_paths ).
그런 다음 위에서 만든 path_fidelity 함수를 사용하여 경로 충실도가 가장 높은 최적의 큐비트 체인을 찾습니다.
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)}")Output:
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)Output:
array([55, 49, 48, 47, 46, 45, 54, 64, 65, 66, 73, 85, 86, 87, 88, 89],
dtype=uint64)
커플링 맵 다이어그램에서 분홍색으로 표시된 최적의 큐비트 체인을 그려보겠습니다.
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)
)Output:
2.1 최적 큐비트 체인에 GHZ 회로를 구축하라
먼저 H 게이트를 적용할 체인 중간에 큐비트를 선택합니다. 이렇게 하면 회로의 깊이가 약 절반으로 줄어듭니다.
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)Output:
ghz1.depth()Output:
10
pm = generate_preset_pass_manager(1, backend=backend)
ghz1_transpiled = pm.run(ghz1)
ghz1_transpiled.draw(output="mpl", idle_wires=False, fold=-1)Output:
print("Depth:", ghz1_transpiled.depth())
print(
"Two-qubit Depth:",
ghz1_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)Output:
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())Output:
DONE DONE
위의 작업 상태가 '완료'가 된 후 다음 셀을 실행하여 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,
)Output:
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))Output:
이 결과는 기준을 충족하지 않습니다. 다음 아이디어로 넘어가겠습니다.
3. 전략 2. 균형 잡힌 큐비트 트리
다음 아이디어는 균형 잡힌 큐비트 트리를 찾는 것입니다. 체인이 아닌 트리를 사용하면 회로 깊이가 더 낮아져야 합니다. 그 전에 "불량" 판독 오류가 있는 노드와 "불량" 게이트 오류가 있는 에지를 커플링 그래프에서 제거합니다.
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)Output:
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)불량 에지와 불량 큐비트 없이 커플링 맵 그래프를 그려보겠습니다.
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),
)Output:
이전과 마찬가지로 16-비트 GHZ 상태를 만들려고 합니다.
N = 16betweenness_centrality 함수를 호출하여 루트 노드에 대한 큐비트를 찾습니다. 사이 중심성 값이 가장 높은 노드가 그래프의 중앙에 위치합니다. 참조: https://www.rustworkx.org/tutorial/betweenness_centrality.html
또는 수동으로 선택할 수도 있습니다.
# central = 65 #Select the center node manually
c_degree = dict(rx.betweenness_centrality(g))
central = max(c_degree, key=c_degree.get)
centralOutput:
66
루트 노드에서 시작하여 BFS(폭 우선 검색)로 트리를 생성합니다. 참조: 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)Output:
Qubits selected: [54, 55, 63, 64, 65, 66, 67, 68, 69, 70, 73, 83, 84, 85, 86, 87]
커플링 맵 다이어그램에서 분홍색으로 표시된 선택된 큐비트를 플롯해 보겠습니다.
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),
)Output:
큐비트의 트리 구조를 보여드리겠습니다.
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")Output:
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)Output:
ghz2.depth()Output:
8
pm = generate_preset_pass_manager(1, backend=backend)
ghz2_transpiled = pm.run(ghz2)
ghz2_transpiled.draw(output="mpl", idle_wires=False, fold=-1)Output:
print("Depth:", ghz2_transpiled.depth())
print(
"Two-qubit Depth:",
ghz2_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)Output:
Depth: 22
Two-qubit Depth: 6
이제 회로의 깊이가 체인 구조의 깊이보다 훨씬 낮아졌습니다.
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())Output:
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,
)Output:
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
균형 잡힌 트리 구조로 기준을 성공적으로 통과했습니다!
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))Output:
이제 더 큰 GHZ 상태인 30-큐비트 GHZ 상태를 생성해 보겠습니다.
3.1 N = 30
저희는 키스킷 패턴 프레임워크를 따를 것입니다.
- 1단계: 양자 회로 및 연산자에 문제 매핑하기
- 2단계: 대상 하드웨어에 최적화
- 3단계: 대상 하드웨어에서 실행
- 4단계: 포스트 프로세스 결과
1단계: 문제를 양자 회로 및 연산자로 매핑하고 2단계: 대상 하드웨어에 최적화
여기서는 루트 노드를 수동으로 선택합니다.
central = 62 # Select the center node manually
# c_degree = dict(rx.betweenness_centrality(g))
# central = max(c_degree, key=c_degree.get)
# centralN = 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)Output:
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),
)Output:
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")Output:
이 트리의 깊이는 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)Output:
ghz3.depth()Output:
11
pm = generate_preset_pass_manager(1, backend=backend)
ghz3_transpiled = pm.run(ghz3)
ghz3_transpiled.draw(output="mpl", idle_wires=False, fold=-1)Output:
print("Depth:", ghz3_transpiled.depth())
print(
"Two-qubit Depth:",
ghz3_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)Output:
Depth: 31
Two-qubit Depth: 9
3.2 다른 루트 노드를 수동으로 선택하십시오
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)Output:
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")Output:
이 트리의 깊이는 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)Output:
ghz3.depth()Output:
11
pm = generate_preset_pass_manager(1, backend=backend)
ghz3_transpiled = pm.run(ghz3)
ghz3_transpiled.draw(output="mpl", idle_wires=False, fold=-1)Output:
print("Depth:", ghz3_transpiled.depth())
print(
"Two-qubit Depth:",
ghz3_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)Output:
Depth: 30
Two-qubit Depth: 9
놀랍게도 트리 깊이는 5에서 6으로 증가한 반면, 2쿼트 깊이는 9에서 8로 감소했습니다! 따라서 후자의 회로를 사용하겠습니다.
3단계: 대상 하드웨어에서 실행
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())Output:
DONE DONE
4단계: 결과 후처리
N = 30
# Check fidelity from job IDs
res = check_ghz_fidelity_from_jobs(
sampler_job=job_s,
estimator_job=job_e,
num_qubits=N,
)Output:
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
보시다시피 이 결과는 기준을 충족하지 못했습니다.
# It will take some time
result = job_s.result()
plot_histogram(result[0].data.c.get_counts(), figsize=(30, 5))Output:
4. 전략 3. 오류 억제 옵션으로 실행
Sampler의 ‘ V2 ’에서 오류 억제 옵션을 설정할 수 있습니다. 자세한 내용은 샘플러 노이즈 관리 가이드와 API ExecutionOptionsV2 참조 문서를 참조하십시오.
opts = SamplerOptions()
opts.dynamical_decoupling.enable = True
opts.execution.rep_delay = 0.0005
opts.twirling.enable_gates = Trueres = 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())Output:
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,
)Output:
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))Output:
결과가 개선되었지만 여전히 기준을 충족하지 못했습니다.
지금까지 세 가지 아이디어를 살펴봤습니다. 이러한 아이디어를 결합하고 확장하거나 자신만의 아이디어를 떠올려 더 나은 GHZ 회로를 만들 수 있습니다. 이제 목표를 다시 검토해 보겠습니다.
5. 당신의 목표 (요약)
측정 결과가 기준을 충족하도록 20큐비트 이상의 GHZ 회로를 구축합니다: GHZ 상태의 충실도 > 0.5.
- Eagle 디바이스(예:
ibm_brisbane)를 사용하고 촬영 횟수를 40,000회로 설정해야 합니다. execute_ghz_fidelity함수를 사용하여 GHZ 회로를 실행하고check_ghz_fidelity_from_jobs함수를 사용하여 충실도를 계산해야 합니다. 기준을 충족하는 가장 큰 큐비트인 GHZ 회로를 찾아야 합니다. 아래에 코드를 작성하고check_ghz_fidelity_from_jobs함수를 사용하여 결과를 표시합니다.
이제 이전 자료에서와 동일한 GHZ 워크플로우를 Heron 기기에서 구현합니다. 이를 통해 Heron 프로세서의 레이아웃과 기능에 대해 어느 정도 경험할 수 있습니다. 새로운 전략이 도입되지 않습니다.
이 다음 실험을 실행하는 데 걸리는 대략적인 QPU 시간은 4분 40초입니다.
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}")Output:
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)Output:
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),
)Output:
N = 40
central = 100 # Select the center node manually
# c_degree = dict(rx.betweenness_centrality(g))
# central = max(c_degree, key=c_degree.get)
# centralclass 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)Output:
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),
)Output:
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")Output:
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)Output:
ghz_h.depth()Output:
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)Output:
print("Depth:", ghz_h_transpiled.depth())
print(
"Two-qubit Depth:",
ghz_h_transpiled.depth(filter_function=lambda x: x.operation.num_qubits == 2),
)Output:
Depth: 45
Two-qubit Depth: 13
opts = SamplerOptions()
opts.dynamical_decoupling.enable = True
opts.execution.rep_delay = 0.0005
opts.twirling.enable_gates = Trueres = 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())Output:
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,
)Output:
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))Output:
축하합니다! 유틸리티 규모의 양자 컴퓨팅에 대한 소개를 완료하셨습니다! 이제 여러분은 양자 우위를 추구하는 데 의미 있는 기여를 할 준비가 되었습니다! IBM 퀀텀®을 개인 양자 여정의 일부로 만들어 주셔서 감사합니다.
수강 후 설문조사
이 과정을 완료하신 것을 축하드립니다! 잠시 시간을 내어 다음의 간단한 설문조사에 참여하여 강좌를 개선하는 데 도움을 주세요. 여러분의 피드백은 콘텐츠 제공과 사용자 경험을 개선하는 데 사용됩니다. 감사합니다!