Skip to main content
IBM Quantum Platform

양자 시뮬레이션

Note

유키오 가와시마 (2024년 5월 30일)

원본 강의의 PDF를 다운로드하세요. 일부 코드 스니펫은 정적 이미지이므로 더 이상 사용되지 않을 수 있습니다.

이 실험을 실행하는 데 걸리는 대략적인 QPU 시간은 7초입니다.

(이 노트북은 대부분 지금은 더 이상 사용되지 않는 키스킷 알고리즘 튜토리얼 노트북에서 가져온 것입니다.)


1. 소개

실시간 진화 기법인 트로터화는 시간 조각에 대한 시스템의 시간 진화를 근사화하기 위해 선택한 양자 게이트를 연속적으로 적용하는 것으로 구성됩니다. 슈뢰딩거 방정식에 따르면, 처음에 ψ(0)\vert\psi(0)\rangle 상태에 있는 시스템의 시간 진화는 다음과 같은 형태를 취합니다:

ψ(t)=eiHtψ(0),\vert \psi(t) \rangle = e^{-i H t} \vert \psi(0) \rangle \text{,}

여기서 HH 은 시스템을 관리하는 시간 독립적인 해밀턴입니다. 폴리 항 H=jajPjH=\sum_j a_j P_j 의 가중 합으로 작성할 수 있는 해밀턴을 고려해 보겠습니다. PjP_jnn 큐비트에 작용하는 폴리 항의 텐서 곱을 나타냅니다. 특히 이러한 폴리 용어는 서로 통용될 수도 있고 그렇지 않을 수도 있습니다. t=0t=0 시점의 상태가 주어졌을 때, 양자 컴퓨터를 사용하여 나중에 ψ(t)|\psi(t)\rangle 시점의 시스템 상태를 어떻게 구할 수 있을까요? 연산자의 지수는 테일러 급수를 통해 가장 쉽게 이해할 수 있습니다:

eiHt=1iHt12H2t2+...e^{-i H t} = 1-iHt-\frac{1}{2}H^2t^2+...

eiZe^{iZ} 같은 아주 기본적인 지수 중 일부는 컴팩트한 양자 게이트 세트를 사용해 양자 컴퓨터에서 쉽게 구현할 수 있습니다. 관심 있는 대부분의 해밀턴은 하나의 임기가 아니라 여러 개의 임기를 가지고 있습니다. H=H1+H2H = H_1+H_2 :

eiHt=1i(H1+H2)t12(H1+H2)2t2+...e^{-i H t} = 1-i(H_1+H_2)t-\frac{1}{2}(H_1+H_2)^2t^2+...

H1H_1H2H_2 이 통근하는 경우 익숙한 경우가 있습니다(아래 숫자 및 변수 aabb 의 경우에도 마찬가지입니다):

ei(a+b)t=eiateibte^{-i (a+b) t} = e^{-i a t}e^{-i b t}

그러나 운영자가 출퇴근하지 않는 경우에는 이러한 방식으로 단순화하기 위해 테일러 계열에서 용어를 재배치할 수 없습니다. 따라서 복잡한 해밀턴을 양자 게이트로 표현하는 것은 어려운 일입니다.

한 가지 해결책은 아주 작은 시간 tt 을 고려하여 테일러 확장에서 1차 항이 지배하도록 하는 것입니다. 그 가정 하에:

ei(H1+H2)t1i(H1+H2)t(1iH1t)(1iH2t)eiH1teiH2te^{-i (H_1+H_2) t} \approx 1-i(H_1+H_2)t \approx (1-i H_1 t)(1-i H_2 t) \approx e^{-i H_1 t}e^{-i H_2 t}

물론 더 오랜 시간 동안 상태를 발전시켜야 할 수도 있습니다. 이러한 작은 단계를 여러 번 반복해서 사용함으로써 달성할 수 있습니다. 이 프로세스를 트로터화라고 합니다:

ψ(t)(jeiajPjt/r)rψ(0),\vert \psi(t) \rangle \approx \left(\prod_j e^{-i a_j P_j t/r} \right)^r \vert\psi(0) \rangle \text{,}

여기 t/rt/r 는 우리가 선택한 타임 슬라이스(진화 단계)입니다. 그 결과 rr 번 적용될 게이트가 생성됩니다. 시간 간격이 작을수록 더 정확한 근사치를 얻을 수 있습니다. 그러나 이는 또한 더 깊은 회로로 이어져 실제로는 더 많은 오류 축적으로 이어집니다(단기 양자 디바이스에서는 무시할 수 없는 문제입니다).

오늘은 N=2N=2N=6N=6 사이트의 선형 격자에서 Ising 모델의 시간 진화를 연구해 보겠습니다. 이러한 격자는 가장 가까운 이웃과만 상호작용하는 스핀( σi\sigma_i )의 배열로 구성됩니다. 이러한 스핀은 \uparrow\downarrow 의 두 가지 방향을 가질 수 있으며, 이는 각각 +1+11-1 의 자화에 해당합니다.

H=Ji=0N2ZiZi+1hi=0N1Xi,H = - J \sum_{i=0}^{N-2} Z_i Z_{i+1} - h \sum_{i=0}^{N-1} X_i \text{,}

여기서 JJ 은 상호작용 에너지를, hh 은 외부 필드의 크기를 나타냅니다(위의 x 방향이지만 이를 수정할 것입니다). 폴리 행렬을 사용하여 이 식을 작성하고 외부 필드가 가로 방향에 대해 α\alpha 각도를 갖는다고 가정해 보겠습니다,

H=Ji=0N2ZiZi+1hi=0N1(sinαZi+cosαXi).H = -J \sum_{i=0}^{N-2} Z_i Z_{i+1} -h \sum_{i=0}^{N-1} (\sin\alpha Z_i + \cos\alpha X_i) \text{.}

이 해밀턴은 외부 필드의 효과를 쉽게 연구할 수 있다는 점에서 유용합니다. 계산 기준에서 시스템은 다음과 같이 인코딩됩니다:

양자 상태
스핀 표현
0000\lvert 0 0 0 0 \rangle\uparrow\uparrow\uparrow\uparrow
1000\lvert 1 0 0 0 \rangle\downarrow\uparrow\uparrow\uparrow
\ldots\ldots
1111\lvert 1 1 1 1 \rangle\downarrow\downarrow\downarrow\downarrow

우리는 이러한 양자 시스템의 시간 진화를 조사하기 시작할 것입니다. 보다 구체적으로 자화와 같은 시스템의 특정 속성의 시간 변화에 따른 변화를 시각화합니다.

1.1 요구사항

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

  • 키스킷 SDK v1.2 이상 ( pip install qiskit )
  • Qiskit Runtime v0.30 이상 ( pip install qiskit-ibm-runtime )
  • Numpy v1.24.1 이상 < 2 ( pip install numpy )

1.2 라이브러리를 가져옵니다

이 노트북에서는 사용되지 않지만 유용할 수 있는 일부 라이브러리( MatrixExponential, QDrift)가 포함되어 있습니다. 시간이 된다면 직접 체험해 보세요!

# Check the version of Qiskit
import qiskit

qiskit.__version__

Output:

'2.0.2'
# Import the qiskit library
import numpy as np
import matplotlib.pylab as plt
import warnings

from qiskit import QuantumCircuit
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.primitives import StatevectorEstimator
from qiskit.quantum_info import Statevector, SparsePauliOp
from qiskit.synthesis import (
    SuzukiTrotter,
    LieTrotter,
)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager

from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2

warnings.filterwarnings("ignore")

2. 문제 파악하기

2.1 횡방향 이징 해밀토니안 정의

여기서는 1-D 횡단 필드 아이싱 모델을 고려합니다.

먼저 시스템 파라미터 NN, JJ, hh, α\alpha 를 받아 해밀턴을 SparsePauliOp 로 반환하는 함수를 만들겠습니다. A SparsePauliOp 는 가중 폴리 항의 관점에서 연산자를 희소하게 표현한 것입니다.

def get_hamiltonian(nqubits, J, h, alpha):
    # List of Hamiltonian terms as 3-tuples containing
    # (1) the Pauli string,
    # (2) the qubit indices corresponding to the Pauli string,
    # (3) the coefficient.
    ZZ_tuples = [("ZZ", [i, i + 1], -J) for i in range(0, nqubits - 1)]
    Z_tuples = [("Z", [i], -h * np.sin(alpha)) for i in range(0, nqubits)]
    X_tuples = [("X", [i], -h * np.cos(alpha)) for i in range(0, nqubits)]

    # We create the Hamiltonian as a SparsePauliOp, via the method
    # `from_sparse_list`, and multiply by the interaction term.
    hamiltonian = SparsePauliOp.from_sparse_list(
        [*ZZ_tuples, *Z_tuples, *X_tuples], num_qubits=nqubits
    )
    return hamiltonian.simplify()

해밀토니안을 정의하라

지금 고려하는 시스템의 크기는 N=6N=6, J=0.2J=0.2, h=1.2h=1.2α=π8.0\alpha=\frac{\pi}{8.0} 을 예로 들 수 있습니다.

n_qubits = 6

hamiltonian = get_hamiltonian(nqubits=n_qubits, J=0.2, h=1.2, alpha=np.pi / 8.0)
hamiltonian

Output:

SparsePauliOp(['IIIIZZ', 'IIIZZI', 'IIZZII', 'IZZIII', 'ZZIIII', 'IIIIIZ', 'IIIIZI', 'IIIZII', 'IIZIII', 'IZIIII', 'ZIIIII', 'IIIIIX', 'IIIIXI', 'IIIXII', 'IIXIII', 'IXIIII', 'XIIIII'],
              coeffs=[-0.2       +0.j, -0.2       +0.j, -0.2       +0.j, -0.2       +0.j,
 -0.2       +0.j, -0.45922012+0.j, -0.45922012+0.j, -0.45922012+0.j,
 -0.45922012+0.j, -0.45922012+0.j, -0.45922012+0.j, -1.10865544+0.j,
 -1.10865544+0.j, -1.10865544+0.j, -1.10865544+0.j, -1.10865544+0.j,
 -1.10865544+0.j])

2.2 시간 진화 시뮬레이션의 매개변수를 설정하십시오

여기서는 세 가지 트로터화 기법을 살펴보겠습니다:

  • 거짓말 트로터(첫 번째 주문)
  • 2 차 스즈키 트로터
  • 네 번째 스즈키 트로터

후자의 두 가지는 연습과 부록에서 사용됩니다.

num_timesteps = 60
evolution_time = 30.0
dt = evolution_time / num_timesteps
product_formula_lt = LieTrotter()
product_formula_st2 = SuzukiTrotter(order=2)
product_formula_st4 = SuzukiTrotter(order=4)

2.3 양자 회로 1 준비 (초기 상태)

초기 상태를 만듭니다. 여기서는 스핀 구성으로 시작하겠습니다 \uparrow\uparrow\downarrow\downarrow\uparrow\uparrow.

initial_circuit = QuantumCircuit(n_qubits)
initial_circuit.prepare_state("001100")
# Change reps and see the difference when you decompose the circuit
initial_circuit.decompose(reps=1).draw("mpl")

Output:

Output of the previous code cell

2.4 양자 회로 2 준비 (시간 진화를 위한 단일 회로)

여기서는 거짓말 트로터를 사용하여 단일 시간 단계에 대한 회로를 구성합니다.

거짓말 제품 공식(1차)은 클래스에서 구현됩니다 LieTrotter 클래스에서 구현됩니다. 1차 공식은 서론에서 설명한 근사치로 구성되며, 합계의 행렬 지수는 행렬 지수의 곱으로 근사화됩니다:

eH1+H2eH1eH2e^{H_1+H_2} \approx e^{H_1} e^{H_2}

앞서 언급했듯이, 매우 깊은 회로는 오류가 누적되어 최신 양자 컴퓨터에 문제를 일으킵니다. 2큐비트 게이트는 단일 큐비트 게이트보다 오류율이 높기 때문에, 특히 관심의 대상이 되는 것은 2큐비트 회로 깊이입니다. 실제로 중요한 것은 변환 후의 2큐비트 회로 깊이입니다(양자 컴퓨터가 실제로 실행하는 회로이기 때문입니다). 하지만 지금부터라도 시뮬레이터를 사용하여 이 회로의 연산을 세어보는 습관을 기르도록 하겠습니다.

single_step_evolution_gates_lt = PauliEvolutionGate(
    hamiltonian, dt, synthesis=product_formula_lt
)
single_step_evolution_lt = QuantumCircuit(n_qubits)
single_step_evolution_lt.append(
    single_step_evolution_gates_lt, single_step_evolution_lt.qubits
)

print(
    f"""
Trotter step with Lie-Trotter
-----------------------------
Depth: {single_step_evolution_lt.decompose(reps=3).depth()}
Gate count: {len(single_step_evolution_lt.decompose(reps=3))}
Nonlocal gate count: {single_step_evolution_lt.decompose(reps=3).num_nonlocal_gates()}
Gate breakdown: {", ".join([f"{k.upper()}: {v}" for k, v in single_step_evolution_lt.decompose(reps=3).count_ops().items()])}
"""
)
single_step_evolution_lt.decompose(reps=3).draw("mpl", fold=-1)

Output:


Trotter step with Lie-Trotter
-----------------------------
Depth: 17
Gate count: 27
Nonlocal gate count: 10
Gate breakdown: U3: 12, CX: 10, U1: 5

Output of the previous code cell

2.5 측정할 연산자를 설정하십시오

자화 연산자 iZi/N\sum_i \langle Z_i \rangle / N평균 스핀 상관 연산자 iZiZi+1/(N1)\sum_i \langle Z_i Z_{i+1} \rangle/ (N - 1) 를 정의해 보겠습니다.

magnetization = (
    SparsePauliOp.from_sparse_list(
        [("Z", [i], 1.0) for i in range(0, n_qubits)], num_qubits=n_qubits
    )
    / n_qubits
)
correlation = SparsePauliOp.from_sparse_list(
    [("ZZ", [i, i + 1], 1.0) for i in range(0, n_qubits - 1)], num_qubits=n_qubits
) / (n_qubits - 1)
print("magnetization : ", magnetization)
print("correlation : ", correlation)

Output:

magnetization :  SparsePauliOp(['IIIIIZ', 'IIIIZI', 'IIIZII', 'IIZIII', 'IZIIII', 'ZIIIII'],
              coeffs=[0.16666667+0.j, 0.16666667+0.j, 0.16666667+0.j, 0.16666667+0.j,
 0.16666667+0.j, 0.16666667+0.j])
correlation :  SparsePauliOp(['IIIIZZ', 'IIIZZI', 'IIZZII', 'IZZIII', 'ZZIIII'],
              coeffs=[0.2+0.j, 0.2+0.j, 0.2+0.j, 0.2+0.j, 0.2+0.j])

2.6 시간 진화 시뮬레이션 수행

에너지(해밀턴의 기대값), 자화(자화 연산자의 기대값), 평균 스핀 상관관계(평균 스핀 상관관계 연산자의 기대값)를 모니터링합니다. 키스킷의 StatevectorEstimator ( EstimatorV2 ) 프리미티브는 관측값의 기대값을 추정합니다 ( ψO^ψ\langle\psi\vert\hat{O}\vert\psi\rangle ).

# Initiate the circuit
evolved_state = QuantumCircuit(initial_circuit.num_qubits)
# Start from the initial spin configuration
evolved_state.append(initial_circuit, evolved_state.qubits)
# Initiate Estimator (V2)
estimator = StatevectorEstimator()
# Set number of shots
shots = 10000
# Translate the precision required from the number of shots
precision = np.sqrt(1 / shots)
energy_list = []
mag_list = []
corr_list = []
# Estimate expectation values for t=0.0
job = estimator.run(
    [(evolved_state, [hamiltonian, magnetization, correlation])], precision=precision
)
# Get estimated expectation values
evs = job.result()[0].data.evs
energy_list.append(evs[0])
mag_list.append(evs[1])
corr_list.append(evs[2])
# Start time evolution
for n in range(num_timesteps):
    # Expand the circuit to describe delta-t
    evolved_state.append(single_step_evolution_gates_lt, evolved_state.qubits)
    # Estimate expectation values at delta-t
    job = estimator.run(
        [(evolved_state, [hamiltonian, magnetization, correlation])],
        precision=precision,
    )
    # Retrieve results (expectation values)
    evs = job.result()[0].data.evs
    energy_list.append(evs[0])
    mag_list.append(evs[1])
    corr_list.append(evs[2])
# Transform the list of expectation values (at each time step) to arrays
energy_array = np.array(energy_list)
mag_array = np.array(mag_list)
corr_array = np.array(corr_list)

2.7 관측 가능한 양의 시간에 따른 변화를 그래프로 나타내라

시간에 대해 측정한 기대값을 플롯합니다.

fig, axes = plt.subplots(3, sharex=True)
times = np.linspace(0, evolution_time, num_timesteps + 1)  # includes initial state
axes[0].plot(
    times,
    energy_array,
    label="First order",
    marker="x",
    c="darkmagenta",
    ls="-",
    lw=0.8,
)
axes[1].plot(
    times, mag_array, label="First order", marker="x", c="darkmagenta", ls="-", lw=0.8
)
axes[2].plot(
    times, corr_array, label="First order", marker="x", c="darkmagenta", ls="-", lw=0.8
)
axes[0].set_ylabel("Energy")
axes[1].set_ylabel("Magnetization")
axes[2].set_ylabel("Mean spin correlation")
axes[2].set_xlabel("Time")
fig.suptitle("Observable evolution")

Output:

Text(0.5, 0.98, 'Observable evolution')
Output of the previous code cell

3. 연습 1. 2차 스즈키-트로터(Suzuki–Trotter) 방법을 이용한 시뮬레이션 수행

이제 위에 표시된 거짓말 트로터의 예에 따라 2차 스즈키 트로터로 시뮬레이션을 수행해 보겠습니다.

2차 스즈키 트로터는 SuzukiTrotter 클래스를 통해 키스킷에서 사용할 수 있습니다. 이 공식을 사용하면 2차 분해가 됩니다:

eH1+H2eH1/2eH2eH1/2e^{H_1+H_2} \approx e^{H_1/2}e^{H_2}e^{H_1/2}

3.1 단일 시간 단계에 대한 회로를 구성하십시오

product_formula_st2 ( SuzukiTrotter(order=2 ))를 사용하여 2차 스즈키 트로터를 사용하여 단일 시간 단계에 대한 회로를 구성합니다. 또한 회로의 게이트 수와 깊이를 세고 거짓말 트로터와 비교합니다.

# Modify the line below (Use PauliEvolutionGate)
single_step_evolution_gates_st2 = PauliEvolutionGate(
    hamiltonian, dt, synthesis=product_formula_st2
)
single_step_evolution_st2 = QuantumCircuit(n_qubits)
single_step_evolution_st2.append(
    single_step_evolution_gates_st2, single_step_evolution_st2.qubits
)
# Let us print some stats
print(
    f"""
Trotter step with second-order Suzuki-Trotter
-----------------------------
Depth: {single_step_evolution_st2.decompose(reps=3).depth()}
Gate count: {len(single_step_evolution_st2.decompose(reps=3))}
Nonlocal gate count: {single_step_evolution_st2.decompose(reps=3).num_nonlocal_gates()}
Gate breakdown: {", ".join([f"{k.upper()}: {v}" for k, v in single_step_evolution_st2.decompose(reps=3).count_ops().items()])}
"""
)
single_step_evolution_st2.decompose(reps=2).draw("mpl", fold=-1)

Output:


Trotter step with second-order Suzuki-Trotter
-----------------------------
Depth: 34
Gate count: 53
Nonlocal gate count: 20
Gate breakdown: U3: 23, CX: 20, U1: 10

Output of the previous code cell

3.2 시간 진화 시뮬레이션 수행

2차 스즈키 트로터를 사용하여 시간 진화를 수행합니다.

# Initiate the circuit
evolved_state = QuantumCircuit(initial_circuit.num_qubits)
# Start from the initial spin configuration
evolved_state.append(initial_circuit, evolved_state.qubits)
# Initiate Estimator (V2)
estimator = StatevectorEstimator()
# Set number of shots
shots = 10000
# Translate the precision required from the number of shots
precision = np.sqrt(1 / shots)
energy_list_st2 = []
mag_list_st2 = []
corr_list_st2 = []
# Estimate expectation values for t=0.0
job = estimator.run(
    [(evolved_state, [hamiltonian, magnetization, correlation])], precision=precision
)
# Get estimated expectation values
evs = job.result()[0].data.evs
energy_list_st2.append(evs[0])
mag_list_st2.append(evs[1])
corr_list_st2.append(evs[2])
# Start time evolution
for n in range(num_timesteps):
    # Expand the circuit to describe delta-t
    evolved_state.append(single_step_evolution_gates_st2, evolved_state.qubits)
    # Estimate expectation values at delta-t
    job = estimator.run(
        [(evolved_state, [hamiltonian, magnetization, correlation])],
        precision=precision,
    )
    # Retrieve results (expectation values)
    evs = job.result()[0].data.evs
    energy_list_st2.append(evs[0])
    mag_list_st2.append(evs[1])
    corr_list_st2.append(evs[2])
# Transform the list of expectation values (at each time step) to arrays
energy_array_st2 = np.array(energy_list_st2)
mag_array_st2 = np.array(mag_list_st2)
corr_array_st2 = np.array(corr_list_st2)

3.3 2차 스즈키-트로터 결과를 그래프로 나타내다

axes[0].plot(
    times,
    energy_array_st2,
    label="Second Order",
    marker="x",
    c="limegreen",
    ls="-",
    lw=0.8,
)
axes[1].plot(
    times,
    mag_array_st2,
    label="Second Order",
    marker="x",
    c="limegreen",
    ls="-",
    lw=0.8,
)
axes[2].plot(
    times,
    corr_array_st2,
    label="Second Order",
    marker="x",
    c="limegreen",
    ls="-",
    lw=0.8,
)

# Replace the legend
# legend.remove()
legend = fig.legend(
    *axes[0].get_legend_handles_labels(),
    bbox_to_anchor=(1.0, 0.5),
    loc="center left",
    framealpha=0.5,
)
fig

Output:

Output of the previous code cell

3.4 정확한 결과와 비교

아래 데이터는 기존 컴퓨터에서 미리 계산된 정확한 결과입니다.

exact_times = np.array(
    [
        0.0,
        0.3,
        0.6,
        0.8999999999999999,
        1.2,
        1.5,
        1.7999999999999998,
        2.1,
        2.4,
        2.6999999999999997,
        3.0,
        3.3,
        3.5999999999999996,
        3.9,
        4.2,
        4.5,
        4.8,
        5.1,
        5.3999999999999995,
        5.7,
        6.0,
        6.3,
        6.6,
        6.8999999999999995,
        7.199999999999999,
        7.5,
        7.8,
        8.1,
        8.4,
        8.7,
        9.0,
        9.299999999999999,
        9.6,
        9.9,
        10.2,
        10.5,
        10.799999999999999,
        11.1,
        11.4,
        11.7,
        12.0,
        12.299999999999999,
        12.6,
        12.9,
        13.2,
        13.5,
        13.799999999999999,
        14.1,
        14.399999999999999,
        14.7,
        15.0,
        15.299999999999999,
        15.6,
        15.899999999999999,
        16.2,
        16.5,
        16.8,
        17.099999999999998,
        17.4,
        17.7,
        18.0,
        18.3,
        18.599999999999998,
        18.9,
        19.2,
        19.5,
        19.8,
        20.099999999999998,
        20.4,
        20.7,
        21.0,
        21.3,
        21.599999999999998,
        21.9,
        22.2,
        22.5,
        22.8,
        23.099999999999998,
        23.4,
        23.7,
        24.0,
        24.3,
        24.599999999999998,
        24.9,
        25.2,
        25.5,
        25.8,
        26.099999999999998,
        26.4,
        26.7,
        27.0,
        27.3,
        27.599999999999998,
        27.9,
        28.2,
        28.5,
        28.799999999999997,
        29.099999999999998,
        29.4,
        29.7,
        30.0,
    ]
)
exact_energy = np.array(
    [
        -1.1184402376762155,
        -1.1184402376762157,
        -1.1184402376762157,
        -1.1184402376762148,
        -1.1184402376762153,
        -1.1184402376762155,
        -1.1184402376762148,
        -1.118440237676216,
        -1.118440237676216,
        -1.1184402376762166,
        -1.1184402376762148,
        -1.118440237676216,
        -1.1184402376762153,
        -1.1184402376762148,
        -1.118440237676217,
        -1.118440237676215,
        -1.1184402376762161,
        -1.1184402376762157,
        -1.118440237676217,
        -1.1184402376762161,
        -1.1184402376762137,
        -1.1184402376762161,
        -1.1184402376762161,
        -1.118440237676218,
        -1.1184402376762155,
        -1.1184402376762166,
        -1.1184402376762155,
        -1.1184402376762137,
        -1.1184402376762186,
        -1.1184402376762215,
        -1.1184402376762148,
        -1.118440237676216,
        -1.1184402376762166,
        -1.1184402376762148,
        -1.1184402376762121,
        -1.1184402376762166,
        -1.1184402376762181,
        -1.1184402376762137,
        -1.1184402376762148,
        -1.1184402376762193,
        -1.1184402376762108,
        -1.1184402376762144,
        -1.118440237676217,
        -1.1184402376762197,
        -1.1184402376762153,
        -1.1184402376762161,
        -1.1184402376762184,
        -1.1184402376762126,
        -1.118440237676214,
        -1.118440237676214,
        -1.1184402376762161,
        -1.118440237676212,
        -1.1184402376762164,
        -1.118440237676217,
        -1.1184402376762121,
        -1.1184402376762157,
        -1.1184402376762212,
        -1.1184402376762217,
        -1.1184402376762206,
        -1.118440237676222,
        -1.1184402376762166,
        -1.118440237676212,
        -1.1184402376762137,
        -1.11844023767622,
        -1.1184402376762206,
        -1.118440237676219,
        -1.1184402376762153,
        -1.1184402376762164,
        -1.118440237676209,
        -1.1184402376762144,
        -1.1184402376762161,
        -1.118440237676216,
        -1.1184402376762173,
        -1.118440237676214,
        -1.1184402376762093,
        -1.1184402376762184,
        -1.1184402376762126,
        -1.118440237676213,
        -1.1184402376762195,
        -1.1184402376762095,
        -1.1184402376762075,
        -1.1184402376762197,
        -1.1184402376762141,
        -1.1184402376762146,
        -1.1184402376762184,
        -1.118440237676218,
        -1.1184402376762224,
        -1.118440237676219,
        -1.118440237676218,
        -1.1184402376762206,
        -1.1184402376762168,
        -1.118440237676221,
        -1.118440237676218,
        -1.1184402376762148,
        -1.1184402376762106,
        -1.1184402376762173,
        -1.118440237676216,
        -1.118440237676216,
        -1.1184402376762113,
        -1.1184402376762275,
        -1.1184402376762195,
    ]
)
exact_magnetization = np.array(
    [
        0.3333333333333333,
        0.26316769633415005,
        0.0912947227110664,
        -0.09317712543141576,
        -0.20391854332115245,
        -0.19318196655046493,
        -0.06411527074401464,
        0.12558269854206197,
        0.28252754464640606,
        0.3264196194042506,
        0.2361586169847769,
        0.060894367906122224,
        -0.10842387093076275,
        -0.18636359582538073,
        -0.1338364343947887,
        0.020284606520827753,
        0.19151142743926025,
        0.2905341647678381,
        0.2723014646745304,
        0.15147481733047252,
        -0.008179102877790292,
        -0.1242999208732406,
        -0.1372529247781061,
        -0.04083616185958952,
        0.11066094926716476,
        0.23140661570567636,
        0.2587109403786205,
        0.1868237670027325,
        0.061201779383143744,
        -0.051391248969654205,
        -0.09843899603365061,
        -0.061297056158849166,
        0.04199010081939773,
        0.15861461430963147,
        0.22336830674799552,
        0.20179555623336537,
        0.11407111438609417,
        0.01609419104778282,
        -0.04239611796730001,
        -0.04249123521065924,
        0.008850291714888112,
        0.08780898151558082,
        0.1561486776507056,
        0.17627348772811832,
        0.13870676179652253,
        0.07205869195282538,
        0.018300003064909465,
        0.0001095640839572417,
        0.015157929316037586,
        0.05077755280969454,
        0.09245534457650838,
        0.12206907551110702,
        0.12284950557969157,
        0.09570215398601932,
        0.06294378255078983,
        0.045503313813986014,
        0.043389819499542556,
        0.046725117769796744,
        0.054956411358382404,
        0.0713814528253614,
        0.08743689703248492,
        0.08951216359166674,
        0.07878386475305985,
        0.06955669116405788,
        0.06639892435963689,
        0.05890378761746903,
        0.04541796525844558,
        0.0414221088331947,
        0.05499634106912299,
        0.07409418836014572,
        0.08371859070160165,
        0.08211623987959302,
        0.07615055161378328,
        0.06702584458783024,
        0.051891407742740085,
        0.038049378383635625,
        0.03825614149768043,
        0.054183218463525695,
        0.0753534475741016,
        0.08853147112587295,
        0.08767917178542013,
        0.07709383184439536,
        0.06308595032042386,
        0.0498812359204284,
        0.04299040064096167,
        0.04769159891460652,
        0.06483569572288776,
        0.08698035745435016,
        0.10047391641776235,
        0.09747255683203637,
        0.08098863187287358,
        0.05959496723987331,
        0.04383882265040485,
        0.04232138798062125,
        0.05720514169944535,
        0.08201306299870219,
        0.10274898262000469,
        0.10707552455080133,
        0.09210856128265357,
        0.06379922105742579,
        0.03624325103307953,
    ]
)
exact_correlation = np.array(
    [
        0.2,
        0.1247704225763532,
        0.01943938494098705,
        0.03854917181332821,
        0.11196616231067426,
        0.0906546700356683,
        0.01629373561896267,
        0.011352652889791095,
        0.0636185676540077,
        0.09543834437789013,
        0.10058518161011307,
        0.11829217731417431,
        0.1397812224038133,
        0.12316460402216707,
        0.08541383059335775,
        0.06144846844403662,
        0.020246372880505827,
        -0.02693683090021662,
        0.003919250903281282,
        0.1117419430168554,
        0.19676155181256794,
        0.18594408880783336,
        0.1002673802566004,
        0.03821525827438024,
        0.04485205090247377,
        0.05348102743040269,
        0.03160026140008638,
        0.033437649060464834,
        0.10486939975320728,
        0.20249469538955758,
        0.19735507621013149,
        0.0553097261765083,
        -0.04889114490131667,
        0.011685690974970964,
        0.11705971535823065,
        0.11681165998194759,
        0.06637091239560744,
        0.10936684225958895,
        0.20225454101061405,
        0.16284420833341812,
        -0.0025823294931362067,
        -0.0763416631752919,
        0.02985268630418397,
        0.15234468006771007,
        0.14606385406970995,
        0.0935341856492092,
        0.12325421854361143,
        0.17130422930386324,
        0.10383730044042278,
        -0.031333159406547614,
        -0.05241572078596815,
        0.07722509925347705,
        0.17642188574256007,
        0.12765340239966838,
        0.06309968945093776,
        0.11574687130499339,
        0.16978282647206913,
        0.0736143632571229,
        -0.05356602733119409,
        -0.0009649396796768892,
        0.15921620111869142,
        0.17760366431811037,
        0.04736297330213485,
        0.012122870263181897,
        0.13268065586830521,
        0.1728473023503636,
        0.03999259331072221,
        -0.036997053070222885,
        0.06951528580242439,
        0.1769169993516561,
        0.12290448295710298,
        0.012897784654866427,
        0.02859435620982225,
        0.12895847695150875,
        0.13629536955485938,
        0.05394621059822597,
        0.02298040588184324,
        0.07036499900317271,
        0.11706448623132719,
        0.10435285842074606,
        0.055721236329964965,
        0.04676334743672697,
        0.08417924910022263,
        0.10611161955304965,
        0.089304171047322,
        0.06098589533081194,
        0.06314519797488709,
        0.09431492621892917,
        0.09667836915967139,
        0.0651298357290882,
        0.05176966009147416,
        0.06727229484222669,
        0.08871788283607947,
        0.09907054249093444,
        0.09785167773502176,
        0.09277216140054353,
        0.07520999642062785,
        0.05894392248382922,
        0.07236135251622376,
        0.08608284185200156,
        0.07282922961856123,
    ]
)
axes[0].plot(exact_times, exact_energy, c="k", ls=":", label="Exact")
axes[1].plot(exact_times, exact_magnetization, c="k", ls=":", label="Exact")
axes[2].plot(exact_times, exact_correlation, c="k", ls=":", label="Exact")
# Replace the legend
legend.remove()
# Select the labels of only the first axis
legend = fig.legend(
    *axes[0].get_legend_handles_labels(),
    bbox_to_anchor=(1.0, 0.5),
    loc="center left",
    framealpha=0.5,
)
fig.tight_layout()
fig

Output:

Output of the previous code cell

4. 양자 하드웨어 실행

다음으로 양자 하드웨어에서 시간 진화 시뮬레이션을 실행합니다. 더 작은 문제인 격자 크기 N=2 에 대해 작업하겠습니다. α\alpha 매개 변수를 변경하여 파동 함수의 역학 차이를 확인합니다.

4.1 1단계. 고전적 입력을 양자 문제에 매핑하다

시뮬레이션의 초기 설정을 선택합니다:

n_qubits_2 = 2
dt_2 = 1.6
product_formula = LieTrotter(reps=1)

그런 다음 초기 회로를 설정합니다:

초기 스핀 구성은 "다운-업"입니다

# We prepare an initial state ↓↑ (10).
# Note that Statevector and SparsePauliOp interpret the qubits from right to left
initial_circuit_2 = QuantumCircuit(n_qubits_2)
initial_circuit_2.prepare_state("10")
# Change reps and see the difference when you decompose the circuit
initial_circuit_2.decompose(reps=1).draw("mpl")

Output:

Output of the previous code cell

이제 이상적인 상태 벡터 시뮬레이터를 사용하여 기준값을 계산합니다.

bar_width = 0.1
# initial_state = Statevector.from_label("10")
final_time = 1.6
eps = 1e-5

# We create the list of angles in radians, with a small epsilon
# the exactly longitudinal field, which would present no dynamics at all
alphas = np.linspace(-np.pi / 2 + eps, np.pi / 2 - eps, 5)

for i, alpha in enumerate(alphas):
    evolved_state_2 = QuantumCircuit(initial_circuit_2.num_qubits)
    evolved_state_2.append(initial_circuit_2, evolved_state_2.qubits)
    hamiltonian_2 = get_hamiltonian(nqubits=2, J=0.2, h=1.0, alpha=alpha)
    single_step_evolution_gates_2 = PauliEvolutionGate(
        hamiltonian_2, dt_2, synthesis=product_formula
    )
    evolved_state_2.append(single_step_evolution_gates_2, evolved_state_2.qubits)
    evolved_state_2 = Statevector(evolved_state_2)
    # Dictionary of probabilities
    amplitudes_dict = evolved_state_2.probabilities_dict()
    labels = list(amplitudes_dict.keys())
    values = list(amplitudes_dict.values())
    # Convert angle to degrees
    alpha_str = f"$\\alpha={int(np.round(alpha * 180 / np.pi))}^\\circ$"
    plt.bar(np.arange(4) + i * bar_width, values, bar_width, label=alpha_str, alpha=0.7)

plt.xticks(np.arange(4) + 2 * bar_width, labels)
plt.xlabel("Measurement")
plt.ylabel("Probability")
plt.suptitle(
    f"Measurement probabilities at $t={final_time}$, for various field angles $\\alpha$\n"
    f"Initial state: 10, Linear lattice of size $L=2$"
)
plt.legend()

Output:

<matplotlib.legend.Legend at 0x11c816590>
Output of the previous code cell

우리는 처음에 ψ(0)=10\vert\psi(0)\rangle = \vert10\rangle 에 해당하는 일련의 스핀 \downarrow\uparrow 으로 시스템을 준비했습니다. 횡단 필드( α=0\alpha=0^\circ ) 아래에서 t=1.6t=1.6 에 대해 진화하도록 한 후, 우리는 \uparrow\downarrow 즉 스핀 스왑을 거의 확실하게 측정할 수 있습니다. (레이블은 오른쪽에서 왼쪽으로 해석됩니다). 필드가 세로인 경우 ( α=±90\alpha=\pm90^\circ ), 우리는 진화가 없으므로 처음에 준비된 상태 그대로 시스템을 측정할 것입니다 ( \downarrow\uparrow ). 중간 각도를 사용하면 α=±45\alpha=\pm45^\circ 에서 모든 조합을 다른 확률로 측정 할 수 있으며 67 %의 확률로 스핀 스왑이 가장 가능성이 높습니다.

하드웨어 실험용 회로 구성

circuit_list = []
for i, alpha in enumerate(alphas):
    evolved_state_2 = QuantumCircuit(initial_circuit_2.num_qubits)
    evolved_state_2.append(initial_circuit_2, evolved_state_2.qubits)
    hamiltonian_2 = get_hamiltonian(nqubits=2, J=0.2, h=1.0, alpha=alpha)
    single_step_evolution_gates_2 = PauliEvolutionGate(
        hamiltonian_2, dt_2, synthesis=product_formula
    )
    evolved_state_2.append(single_step_evolution_gates_2, evolved_state_2.qubits)
    evolved_state_2.measure_all()
    circuit_list.append(evolved_state_2)

4.2 2단계. 대상 하드웨어에 대한 최적화

백엔드를 지정합니다.

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

Output:

'ibm_strasbourg'

그런 다음 선택한 백엔드에 대한 회로를 트랜스파일합니다.

pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
circuit_isa = pm.run(circuit_list)

회로를 확인하세요.

circuit_isa[1].draw("mpl", idle_wires=False)

Output:

Output of the previous code cell

4.3 3단계. IBM Quantum 기본 연산으로 실행

키스킷의 Sampler ( V2 ) 프리미티브는 측정된 비트스트링의 개수를 제공합니다.

sampler = SamplerV2(mode=backend)
job = sampler.run(circuit_isa)
job_id = job.job_id()
print("job id:", job_id)

Output:

job id: d13pswfmya70008ek070

결과 저장

results = job.result()

4.4 4단계. 후처리 결과

파동 함수를 분석하는 것에 해당하는 비트 문자열의 히스토그램을 구성하고 위에 표시된 이상적인 값과 비교합니다.

list_temp = ["00", "01", "10", "11"]

for i, alpha in enumerate(alphas):
    # Dictionary of probabilities
    amplitudes_dict = results[i].data.meas.get_counts()
    values = []
    for str_temp in list_temp:
        values.append(
            amplitudes_dict[str_temp] / 4096.0
        )  # divided by default number of shots
    # Convert angle to degrees
    alpha_str = f"$\\alpha={int(np.round(alpha * 180 / np.pi))}^\\circ$"
    plt.bar(np.arange(4) + i * bar_width, values, bar_width, label=alpha_str, alpha=0.7)

plt.xticks(np.arange(4) + 2 * bar_width, labels)
plt.xlabel("Measurement")
plt.ylabel("Probabilities")
plt.suptitle(
    f"Measurement probabilities at $t={final_time}$, for various field angles $\\alpha$\n"
    f"Initial state: 10, Linear lattice of size $L=2$"
)
plt.legend()

Output:

<matplotlib.legend.Legend at 0x11d7af990>
Output of the previous code cell

여기서는 고차(4차) 스즈키 트로터를 사용하여 회로를 구성하는 예제를 보여드리겠습니다. 이제 위의 예시에 따라 4차 스즈키 트로터로 회로 시뮬레이션을 구성해 보겠습니다.

4차 스즈키 트로터는 SuzukiTrotter 클래스를 통해 키스킷에서 사용할 수 있습니다. 4차는 다음 재귀 관계를 사용하여 평가할 수 있습니다. 다음 방정식에서 스즈키-트로터의 순서는 " 2k "로 표시된다는 점에 유의하세요.

U^ST(2k)(t)=[U^ST(2k2)(pkt)]2U^ST(2k2)((14pk)t)[U^ST(2k2)(pkt)]2\hat{U}_{ST(2k)}\left(t\right) = \left[ \hat{U}_{ST(2k-2)}\left(p_k t\right) \right]^2 \hat{U}_{ST(2k-2)}\left( (1- 4 p_k) t\right)\left[ \hat{U}_{ST(2k-2)}\left(p_k t\right) \right]^2 pk=1/(4412k1)p_k = 1 / \left(4-4^{\frac{1}{2k-1}}\right)

단일 시간 단계에 대한 회로를 구성하십시오

product_formula_st4 ( SuzukiTrotter(order=4 ))를 사용하여 4차 스즈키 트로터를 사용하여 단일 시간 단계에 대한 회로를 구성합니다. 또한 회로의 게이트 수와 깊이를 세어 라이-트로터와 2차 스즈키-트로터와 비교합니다.

# Modify the line below (Use PauliEvolutionGate)
single_step_evolution_gates_st4 = PauliEvolutionGate(
    hamiltonian, dt, synthesis=product_formula_st4
)
single_step_evolution_st4 = QuantumCircuit(n_qubits)
single_step_evolution_st4.append(
    single_step_evolution_gates_st4, single_step_evolution_st4.qubits
)
# Let us print some stats
print(
    f"""
Trotter step with second-order Suzuki-Trotter
-----------------------------
Depth: {single_step_evolution_st4.decompose(reps=3).depth()}
Gate count: {len(single_step_evolution_st4.decompose(reps=3))}
Nonlocal gate count: {single_step_evolution_st4.decompose(reps=3).num_nonlocal_gates()}
Gate breakdown: {", ".join([f"{k.upper()}: {v}" for k, v in single_step_evolution_st4.decompose(reps=3).count_ops().items()])}
"""
)
single_step_evolution_st4.decompose(reps=2).draw("mpl", fold=-1)

Output:


Trotter step with second-order Suzuki-Trotter
-----------------------------
Depth: 170
Gate count: 265
Nonlocal gate count: 100
Gate breakdown: U3: 115, CX: 100, U1: 50

Output of the previous code cell
# Check Qiskit version
import qiskit

qiskit.__version__

Output:

'2.0.2'
이 페이지가 도움이 되었습니까?
GitHub에서 버그, 오타를 보고하거나 컨텐츠를 요청하십시오.