주 콘텐츠로 건너뛰기

기댓값 추정을 위한 와이어 커팅

사용 예상 시간: Heron 프로세서에서 약 22초 (참고: 이는 예상치이며, 실제 실행 시간은 다를 수 있습니다.)

학습 목표

이 튜토리얼을 완료하면 다음을 이해할 수 있습니다:

  • qiskit-addon-cutting을 사용하여 대형 회로를 더 작은 서브회로로 분할함으로써 노이즈의 영향을 줄이는 방법

사전 요구 사항

이 튜토리얼을 진행하기 전에 다음 주제에 익숙해지는 것을 권장합니다:

  • 이 워크플로에서 사용되는 Sampler 프리미티브 사용 방법

배경

Circuit-knitting은 회로를 게이트 수 또는 qubit 수가 더 적은 여러 개의 작은 서브회로로 분할하는 다양한 방법을 포괄하는 용어입니다. 각 서브회로는 독립적으로 실행될 수 있으며, 최종 결과는 각 서브회로의 실행 결과에 대한 고전 후처리를 통해 얻습니다. 이 기법은 Circuit cutting Qiskit addon을 통해 사용할 수 있으며, 기법에 대한 자세한 설명은 문서와 기타 입문 자료에서 확인할 수 있습니다.

이 튜토리얼은 회로를 와이어 방향으로 분할하는 **와이어 커팅(wire cutting)**이라는 방법을 다룹니다 [1], [2]. 고전 회로에서는 분할 지점의 상태가 결정론적으로 0 또는 1로 확정되므로 분할이 간단하지만, 양자 회로에서는 절단 지점에서 Qubit의 상태가 일반적으로 혼합 상태입니다. 따라서 각 서브회로는 서로 다른 기저(보통 Pauli 기저 [3], [4]와 같은 토모그래피적으로 완전한 기저)로 여러 번 측정되어야 하며, 그에 대응하는 고유 상태로 준비되어야 합니다. 아래 그림(출처: [7])은 4-Qubit GHZ 상태를 세 개의 서브회로로 와이어 커팅하는 예시를 보여줍니다. 여기서 MjM_j는 기저 집합(보통 Pauli X, Y, Z)을 나타내고, PiP_i는 고유 상태 집합(보통 0|0\rangle, 1|1\rangle, +|+\rangle, +i|+i\rangle)을 나타냅니다.

wc-1.png wc-2.png

각 서브회로는 qubit 수와 게이트 수가 더 적기 때문에 노이즈의 영향을 덜 받을 것으로 예상됩니다. 이 튜토리얼은 이 방법을 사용하여 시스템의 노이즈를 효과적으로 억제하는 예시를 보여줍니다.

요구 사항

이 튜토리얼을 시작하기 전에 다음이 설치되어 있는지 확인하세요:

  • 시각화 지원이 포함된 Qiskit SDK v2.0 이상
  • Qiskit Runtime v0.22 이상 ( pip install qiskit-ibm-runtime )
  • Circuit cutting Qiskit addon v0.10.0 이상 (pip install qiskit-addon-cutting)
  • Qiskit addon utils 0.3 이상 (pip install qiskit-addon-utils)
  • Qiskit Aer (pip install qiskit-aer )

설정

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
import numpy as np
import matplotlib.pyplot as plt

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.quantum_info import PauliList, SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator
from qiskit.result import sampled_expectation_value

from qiskit_addon_cutting.instructions import CutWire
from qiskit_addon_cutting import (
cut_wires,
expand_observables,
partition_problem,
generate_cutting_experiments,
reconstruct_expectation_values,
)

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, Batch

소규모 시뮬레이터 예시

이 튜토리얼은 1차원(1D) 다체 국소화(MBL) 회로를 시뮬레이션하는 Qiskit 패턴을 구현합니다. MBL 회로는 하드웨어 효율적인 회로로, θ\thetaϕ\vec{\phi} 두 가지 파라미터로 매개변수화됩니다. θ\theta00으로 설정하고 모든 Qubit의 초기 상태를 0|0\rangle으로 준비하면, ϕ\vec{\phi}의 값에 무관하게 모든 qubit 위치 ii에 대해 Zi\langle Z_i \rangle의 이상적인 기댓값은 +1+1입니다. 이 회로에 대한 자세한 내용은 이 논문에서 확인할 수 있습니다.

노이즈 없는 시뮬레이터에서는 회로 절단 유무에 관계없이 동일한 기댓값을 얻습니다.

1단계: 고전 입력을 양자 문제로 변환

1D MBL 회로 구성

먼저, 1D MBL 회로를 구성하는 함수를 소개합니다.

class MBLChainCircuit(QuantumCircuit):
def __init__(
self, num_qubits: int, depth: int, use_cut: bool = False
) -> None:
super().__init__(
num_qubits, name=f"MBLChainCircuit<{num_qubits}, {depth}>"
)
evolution = MBLChainEvolution(num_qubits, depth, use_cut)
self.compose(evolution, inplace=True)

class MBLChainEvolution(QuantumCircuit):
def __init__(self, num_qubits: int, depth: int, use_cut) -> None:
super().__init__(
num_qubits, name=f"MBLChainEvolution<{num_qubits}, {depth}>"
)

theta = Parameter("θ")
phis = ParameterVector("φ", num_qubits)

for layer in range(depth):
layer_parity = layer % 2
# print("layer parity", layer_parity)
for qubit in range(layer_parity, num_qubits - 1, 2):
# print(qubit)
self.cz(qubit, qubit + 1)
self.u(theta, 0, np.pi, qubit)
self.u(theta, 0, np.pi, qubit + 1)
if (
use_cut
and layer_parity == 0
and (
qubit == num_qubits // 2 - 1
or qubit == num_qubits // 2
)
):
self.append(CutWire(), [num_qubits // 2])
if use_cut and layer < depth - 1 and layer_parity == 1:
if qubit == num_qubits // 2:
self.append(CutWire(), [qubit])
for qubit in range(num_qubits):
self.p(phis[qubit], qubit)
num_qubits = 10
depth = 2
mbl = MBLChainCircuit(num_qubits, depth)
mbl.draw("mpl", fold=-1)

Output of the previous code cell

모든 Qubit에 대해 O=1niZiO = \frac{1}{n} \sum_i Z_i의 평균 기댓값을 계산합니다. Zi=1\langle Z_i \rangle = 1 (\forall ii)의 이상적인 기댓값을 가지므로, OO의 이상적인 기댓값도 11입니다. 파라미터 ϕ\phi는 무작위로 선택됩니다.

np.random.seed(42)
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis

Circuit을 분할하기 위해 원하는 위치에 CutWire를 삽입하여 회로에 절단 위치를 표시해야 합니다. 이 튜토리얼에서는 균등 분할을 선택합니다. MBL 회로는 함수에서 use_cut=True로 설정하면 원래 회로의 qubit 수를 nn이라 할 때 n2\frac{n}{2} 번째 qubit 이후에 적절히 절단 표시를 삽입하도록 설계되어 있습니다. 또한 무작위로 생성된 파라미터를 회로에 할당했습니다.

mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_cut.draw("mpl", fold=-1)

Output of the previous code cell

2단계: 양자 하드웨어 실행을 위한 문제 최적화

회로를 더 작은 서브회로로 절단

이제 qiskit-addon-cutting을 사용하여 회로를 두 개의 더 작은 서브회로로 분할합니다. qiskit-addon-cutting은 가상 Move 게이트를 추가하여 qubit 수를 적절히 조정함으로써 와이어 절단 위치를 분리합니다. 이제 이 가상 게이트가 포함된 회로를 생성합니다. 와이어 절단이 하나이므로, 연관된 qubit 수가 1만큼 증가합니다.

mbl_move = cut_wires(mbl_cut)
mbl_move.draw("mpl", fold=-1)

Output of the previous code cell

관측량 구성 및 확장

앞서 정의한 바와 같이, 관측량은 각 Qubit에서 ZZ의 평균이 됩니다. 그런데 가상 Move 게이트를 삽입하면 회로의 유효 qubit 수가 증가합니다. 이 qubit 수 변화를 반영하도록 관측량도 확장해야 합니다. 가상 Move 게이트를 위해 추가된 여분의 Qubit에는 관측량이 항상 항등 연산(II)으로 작용합니다.

observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
observable
PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',
'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',
'IIIIIIIIZI', 'IIIIIIIIIZ'])
new_obs = expand_observables(observable, mbl, mbl_move)
new_obs
PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',
'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',
'IIIIIIIIIZI', 'IIIIIIIIIIZ'])

이제 Move 게이트를 따라 회로를 분할하여 서브회로와 서브관측량을 얻을 수 있습니다. 서브관측량은 각 서브회로와 연관된 원래 관측량의 일부입니다.

partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables

두 서브회로를 시각화합니다:

subcircuits[0].draw("mpl", fold=-1)

Output of the previous code cell

subcircuits[1].draw("mpl", fold=-1)

Output of the previous code cell

Move 연산을 사용하여 관측량을 확장하려면 PauliList 데이터 구조가 필요합니다. 원래 회로의 기댓값을 재구성하려면 SparsePauliOp 형식의 관측량이 필요합니다.

M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)

앞서 논의한 바와 같이, 각 절단에서 업스트림 회로는 Pauli 기저로 측정되어야 하고, 다운스트림 회로는 해당 기저의 고유 상태로 준비되어야 합니다. generate_cutting_experiments 함수는 이러한 모든 필요한 회로와 재구성에 필요한 각 회로의 계수를 생성합니다. 자세한 내용은 이 논문을 참고하세요.

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

백엔드에 맞게 회로 트랜스파일하기

첫 번째 시뮬레이션 예시에서는 회로를 백엔드의 기본 게이트 집합으로 트랜스파일합니다:

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
)

print(backend)
<IBMBackend('ibm_fez')>

3단계: Qiskit 프리미티브를 사용한 실행

이제 각 부분 실험을 실행합니다:

pm_basis = generate_preset_pass_manager(
optimization_level=2, basis_gates=backend.configuration().basis_gates
)
basis_subexperiments = {
label: pm_basis.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
sampler = SamplerV2(mode=AerSimulator())
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in basis_subexperiments.items()
}

4단계: 후처리 및 원하는 고전 형식으로 결과 반환

이제 각 부분 실험 실행의 결과를 가져와서 절단되지 않은 회로의 기댓값을 재구성합니다:

# Retrieve results
results = {label: job.result() for label, job in jobs.items()}
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
reconstructed_expval
np.float64(0.9953821063041687)
methods = [
"Uncut",
"Wire cut",
]
values = [
1,
reconstructed_expval,
] # since the ideal expectation value in noiseless simulation is +1

ax = plt.gca()
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
ax.set_ylabel(r"$M_Z$", fontsize=12)
Text(0, 0.5, '$M_Z$')

Output of the previous code cell

대규모 하드웨어 예시

이제 60-Qubit MBL 회로에 대해 와이어 절단을 시연합니다. 절단된 회로와 절단되지 않은 회로 모두 IBM Quantum® 하드웨어에서 실행됩니다:

num_qubits = 60
depth = 2

# construct the circuit
mbl = MBLChainCircuit(num_qubits, depth)

# create parameters
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis

# construct the cut circuit
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_move = cut_wires(mbl_cut)

# Define observable and expand to account for the wire cut
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
new_obs = expand_observables(observable, mbl, mbl_move)

# Construct a SparsePauliOp version of the observable for later use in reconstruction
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)

# Partition the circuit and get subcircuits and subobservables
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables

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

# Transpile the subexperiments to the backend
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
isa_subexperiments = {
label: pm.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}

# Execute the subexperiments and retrieve results
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
sampler.options.environment.job_tags = ["TUT_WC"]
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
results = {label: job.result() for label, job in jobs.items()}

# Reconstruct the expectation value of the original observable
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real

# Compute the uncut circuit to obtain the noisy expectation value for comparison
sampler = SamplerV2(mode=backend)
sampler.options.environment.job_tags = ["TUT_WC"]

if mbl.num_clbits == 0:
mbl.measure_all()
isa_mbl = pm.run(mbl)

pub = (isa_mbl, params)
uncut_job = sampler.run([pub])

uncut_counts = uncut_job.result()[0].data.meas.get_counts()
uncut_expval = sampled_expectation_value(uncut_counts, M_z)

# visualize the results
ax = plt.gca()
methods = ["uncut", "cut"]
values = [uncut_expval, reconstructed_expval]

plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
plt.axhline(y=1, color="k", linestyle="--")
plt.text(0.3, 0.95, "Exact result")
plt.show()

Output of the previous code cell

uncut_expval
0.9202473958333336

다음 단계

추천

이 내용이 흥미로웠다면 다음 자료도 살펴보세요:

참고 문헌

[1] Peng, T., Harrow, A. W., Ozols, M., & Wu, X. (2020). Simulating large quantum circuits on a small quantum computer. Physical review letters, 125(15), 150504.

[2] Tang, W., Tomesh, T., Suchara, M., Larson, J., & Martonosi, M. (2021, April). Cutqc: using small quantum computers for large quantum circuit evaluations. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).

[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). Quantum circuit cutting with maximum-likelihood tomography. npj Quantum Information, 7(1), 64.

[4] Majumdar, R., & Wood, C. J. (2022). Error mitigated quantum circuit cutting. arXiv preprint arXiv:2211.13431.

[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V., & Simmhan, Y. (2023). Parallelizing Quantum-Classical Workloads: Profiling the Impact of Splitting Techniques. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.

[6] Bhoumik, D., Majumdar, R., Saha, A., & Sur-Kolay, S. (2023). Distributed Scheduling of Quantum Circuits with Noise and Time Optimization. arXiv preprint arXiv:2309.06005.

[7] Majumdar, R. (2024). Efficient Reduction of Resources and Noise in Discrete Quantum Computing Circuits (Doctoral dissertation, Indian Statistical Institute - Kolkata). https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar&cbl=2026366&diss=y