횡자기장 이징 모델의 시간 전개 시뮬레이션
사용량 추정: Nighthawk r2 프로세서에서 105초 (참고: 추정치일 뿐이며 실제 실행 시간은 다를 수 있습니다.)
학습 목표
-
Julia를 사용해 하드웨어에서 양자 회로를 Transpile하고 실행하는 방법 배우기
-
측정 결과를 후처리하여 기댓값을 계산하는 방법 배우기
-
하드웨어 결과를 고전 시뮬레이션과 비교 평가하여 트로터 근사 오차와 하드웨어 잡음의 복합 효과를 정량화하는 방법 배우기
사전 지식
이 튜토리얼을 시작하기 전에 다음 주제를 익혀 두세요.
배경
Julia는 주로 수치 및 과학 계산을 위해 설계된 동적 프로그래밍 언어입니다. 고성능 수치 계산 능력 덕분에 양자 시뮬레이션 워크플로에 자연스럽게 어울립니다. 이 튜토리얼에서는 Julia가 고전적인 전처리와 후처리(예: 해밀토니안 구성, ODE(상미분방정식) 풀이기 실행, 기댓값 계산)와 양자 하드웨어 잡의 조율에 모두 사용되는 방식을 보여 주며, 언어나 환경을 전환할 필요가 없습니다.
Julia에서 IBM Quantum® 하드웨어와 연동하기 위해, 이 튜토리얼은 Qiskit 생태계의 두 패키지를 사용합니다. Qiskit.jl은 Qiskit C 라이브러리를 감싸 Julia에서 회로 구성과 Transpile 기능을 제공하며, QiskitIBMRuntime.jl은 IBM Quantum Compute Service 클라이언트를 통해 IBM Quantum 하드웨어에 연결하여 Julia에서 직접 잡을 제출하고 결과를 가져올 수 있게 해 줍니다.
이 튜토리얼에서는 최근접 이웃 상호작용을 가진 1차원 사슬 위의 횡자기장 이징 모델에 대한 트로터화된 시간 전개를 다룹니다.
시간 전개 를 구현하기 위해 시간 간격 를 개의 단계로 나누고 로 정의합니다. 2차 트로터-스즈키 분해는 다음과 같습니다.
회로 구성에서는 각 트로터 단계를 단일 큐비트 회전과 2큐비트 게이트의 연속으로 구현합니다. 회로는 번갈아 있는 큐비트에 X 게이트를 사용해 Néel 상태 를 준비하는 것으로 시작합니다. 이후 각 트로터 단계는 (1) 모든 큐비트에 , (2) 사슬을 따라 이웃한 각 쌍에 , (3) 모든 큐비트에 다시 를 적용합니다. 전체 회로 깊이는 트로터 단계 수 에 따라 선형으로 증가합니다.
요구 사항
이 튜토리얼은 macOS 또는 Linux가 필요합니다. Qiskit.jl은 현재 Windows를 지원하지 않습니다(이 오픈 이슈에서 추적 중).
시작하려면 Julia 다운로드 페이지의 안내에 따라 Julia를 설치하세요. 이 튜토리얼은 Julia 1.11로 개발되었으며, juliaup add 1.11로 설치할 수 있습니다.
다음으로 터미널에서 아래 명령을 실행하여 Julia 패키지 IJulia를 전역 환경에 설치하세요. 그러면 Jupyter 노트북 안에서 Julia를 실행할 수 있습니다.
julia -e 'using Pkg; Pkg.add("IJulia")'
Julia의 내장 패키지 관리자를 사용해 프로젝트 환경을 설정합니다. 환경을 설정하는 방법은 두 가지입니다.
옵션 1: 임시 환경. 다음 코드 셀을 실행하여 임시 환경을 설정하고 필요한 패키지를 설치할 수 있습니다.
옵션 2: 테스트된 환경을 정확히 재현합니다. 아래 셀에서 download_toml_files = true로 설정하세요. 이 셀은 문서 저장소에서 Project.toml과 Manifest.toml을 이 노트북 옆의 env_tutorial/time-evolution/ 폴더로 다운로드한 다음, 해당 환경을 활성화하고 기록된 정확한 패키지 버전을 설치합니다. 프로젝트 파일은 환경을 개괄적으로 설명하며, 예를 들어 [deps] 섹션에는 모든 의존성이 나열됩니다. 매니페스트 파일은 (간접 의존성을 포함해) 모든 패키지의 정확한 버전을 고정하여 환경을 재현 가능하게 만듭니다. 자세한 내용은 Julia 문서를 참고하세요.
다음 의존성이 환경에 설치됩니다.
양자 회로 구성 및 실행용:
Qiskit.jlQiskitIBMRuntime.jl
고전 시뮬레이션용:
OrdinaryDiffEq.jlTensorNetworkQuantumSimulator.jl
결과 후처리 및 시각화용:
StatsBase.jlPlots.jl
이 튜토리얼은 Qiskit.jl 버전 0.6.0과 QiskitIBMRuntime.jl 버전 0.3.1에서 테스트되었습니다.
using Pkg
using Downloads
download_toml_files = false
if !download_toml_files
# Option 1: Install the latest versions of the required packages into a temporary environment
Pkg.activate(mktempdir(); io=devnull)
Pkg.add([
PackageSpec(name="Qiskit"),
PackageSpec(name="QiskitIBMRuntime"),
PackageSpec(name="Python_jll"),
PackageSpec(name="OrdinaryDiffEq"),
PackageSpec(name="TensorNetworkQuantumSimulator"),
PackageSpec(name="StatsBase"),
PackageSpec(name="Plots"),
]; io=devnull)
else
# Option 2: Install the exact tested versions pinned in the downloaded Project.toml and Manifest.toml
base_url = "https://raw.githubusercontent.com/Qiskit/documentation/main/docs/tutorials/assets/time-evolution/julia"
env_dir = joinpath(@__DIR__, "env_tutorial", "time-evolution")
mkpath(env_dir)
for file in ("Project.toml", "Manifest.toml")
Downloads.download("$base_url/$file", joinpath(env_dir, file))
end
Pkg.activate(env_dir; io=devnull)
Pkg.instantiate(; io=devnull) # installs the exact versions recorded in Manifest.toml
end
여기까지 노트북을 실행하기 위한 Julia 프로젝트 환경을 설정했습니다. IBM 양자 처리 장치에서 워크플로를 실행하려면 IBM Quantum 계정과 qiskit-ibm-runtime에서 서비스를 인스턴스화하기 위한 API 토큰이 필요합니다. 하드웨어에서 첫 회로 실행하기 항목의 "Install and authenticate" 단계를 따라 API 토큰을 생성하고 인스턴스 CRN을 찾으세요.
설정
using Qiskit
using Qiskit.Operations
using QiskitIBMRuntime
using StatsBase
using OrdinaryDiffEq
using SparseArrays
using LinearAlgebra
using TensorNetworkQuantumSimulator
using Plots: plot, plot!, heatmap, @layout, mm
또한 비트 문자열 v에서 위치 i에 있는 비트의 값을 반환하는 다음 유틸리티 함수를 정의합니다. 예를 들어 v = 6(이진수 110)인 경우,
bit_at(6, 1)은0을 반환하고,bit_at(6, 2)는1을 반환하며,bit_at(6, 3)은1을 반환합니다.
이는 Qiskit에서 사용하는 리틀 엔디안 규칙을 따릅니다. 위치 i는 최하위("가장 오른쪽") 비트부터 인덱싱됩니다.
"""
bit_at(v::Integer, i::Integer) = (v >> (i-1)) & 1
Return the value of the bit at position `i` in `v`.
"""
bit_at(v::Integer, i::Integer) = (v >> (i-1)) & 1
bit_at
소규모 시뮬레이터 예제
위에서 설명한 횡자기장 Ising 모형으로 기술되는 개 큐비트의 1차원 사슬을 고려합니다. 대상 시스템에 대해 아래에서 시스템 크기 N, Trotter 단계 크기 δt, 전체 Trotter 단계 수 r_max를 지정합니다. 전체 시간 발전 시간은 δt * r_max입니다. Julia는 δt와 같은 유니코드 식별자를 지원합니다. 노트북이나 Julia REPL에서 \delta를 입력한 다음 Tab을 누르면 δ가 입력됩니다. 자세한 내용은 Julia 유니코드 입력 문서를 참조하세요.
정확한 해
양자 하드웨어에서 얻은 결과를 비교할 기준선을 마련하기 위해, 먼저 소규모 문제에 대한 고전 시뮬레이션 워크플로를 보여 드립니다. Ising 해밀토니안을 희소 행렬로 구성한 다음, OrdinaryDiffEq.jl의 ODEProblem을 사용해 슈뢰딩거 방정식을 수치적으로 적분하여 정확한 시간 발전을 구합니다. 이 접근 방식은 큐비트 수 에 대해 지수적으로 증가합니다. 전체 차원 상태 벡터를 저장해야 하기 때문입니다. 이면 힐베르트 공간의 차원이 이미 100만 개를 넘어 더 큰 시스템에는 적용하기 어렵습니다.
N = 20
δt = 0.05 # Trotter step size
r_max = 10 # total number of Trotter steps
h = fill(1.0, N)
J = fill(1.0, N-1)
# Build the Ising Hamiltonian as a sparse 2^n × 2^n matrix
function build_ising_hamiltonian(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int)
dim = 2^n
# diagonal ZZ terms
diag_terms = zeros(Float64, dim)
for i in 1:n-1
for b in 0:dim-1
bi = bit_at(b, i) # bit at position i
bi_next = bit_at(b, i+1) # bit at position i + 1
diag_terms[b+1] += J[i] * (1-2bi) * (1-2bi_next)
end
end
H = spdiagm(0 => complex(diag_terms))
# off-diagonal local X terms
for i in 1:n
mask = 1 << (i-1)
cols = [xor(b, mask) + 1 for b in 0:dim-1]
H += h[i] * sparse(1:dim, cols, ones(ComplexF64, dim), dim, dim)
end
return H
end
H_ising = build_ising_hamiltonian(h, J, N)
1048576×1048576 SparseMatrixCSC{ComplexF64, Int64} with 22020096 stored entries:
⎡⣿⣿⣾⢦⡀⠳⣄⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤
⎢⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⡀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠙⢦⡈⠳⣼⣿⣿⡆⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠙⢦⡀⠀⠀⠁⠀⠈⠈⠉⣿⣿⣾⢦⡀⠳⣄⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡈⠳⣼⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⎥
⎢⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡟⢦⡈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠙⢦⠈⠳⡿⣿⣿⣀⡀⡀⠀⢀⠀⠀⠈⠳⣄⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠸⣿⣿⡟⢦⡈⠳⣄⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠈⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠐⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⎥
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠙⢦⠈⠳⡿⣿⣿⎦
슈뢰딩거 방정식의 우변을 제자리(in-place) 형태 schrodinger!(dψ, ψ, H, t)로 정의하며, 이는 희소 행렬-벡터 곱셈을 사용해 를 계산합니다. 그런 다음 Néel 상태를 초기 조건으로 하여 ODEProblem을 설정하고 시간 구간 에서 풀며, 각 시간 단계 마다 상태를 저장합니다. 사용한 풀이기는 Tsit5()로, 비강성(non-stiff) 문제에 적합한 표준 명시적 4/5차 Runge-Kutta 방법입니다.
# initial state |0101...01⟩
ψ0 = zeros(ComplexF64, 2^N)
neel_index = sum(1 << (i-1) for i in 1:2:N)
ψ0[neel_index + 1] = 1.0
function schrodinger!(dψ::AbstractVector, ψ::AbstractVector, H::AbstractMatrix, t::Real)
mul!(dψ, H, ψ)
dψ .*= -im
end
tspan = (0.0, r_max * δt)
prob = ODEProblem(schrodinger!, ψ0, tspan, H_ising)
sol = solve(prob, Tsit5(), saveat=δt)
retcode: Success
Interpolation: 1st order linear
t: 11-element Vector{Float64}:
0.0
0.05
0.1
0.15
0.2
0.25
0.3
0.35
0.4
0.45
0.5
u: 11-element Vector{Vector{ComplexF64}}:
[0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im … 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im, 0.0 + 0.0im]
[-7.612535273941705e-14 + 2.5737401150886516e-29im, 7.616818423232204e-14 - 1.6944517510822304e-12im, -8.565903349044157e-17 + 3.2368190823993794e-15im, -7.61682079584434e-14 - 1.078515326911689e-15im, 1.523363843119942e-13 - 1.6912125988569567e-12im, 3.5867824743714624e-11 + 5.083352813839524e-12im, -7.608249752495255e-14 - 4.317561743447096e-15im, 7.616820798431766e-14 - 1.6944516451719627e-12im, -8.570258514925147e-17 + 3.2384113624188233e-15im, -7.606534127496737e-14 - 5.398093819616815e-15im … -7.591102113501652e-14 - 7.55501666902211e-15im, 1.5233640806402347e-13 - 1.691212597789024e-12im, -4.283149391646935e-17 + 3.2390475588024777e-15im, -7.61253567131055e-14 - 4.319154099332238e-15im, 1.28498500555455e-16 + 4.773388002812907e-18im, -8.570258413862945e-17 + 3.2384113624488572e-15im, -7.612534879063769e-14 - 3.2390464923709225e-15im, 1.5233638826330585e-13 - 1.6912127047672224e-12im, -4.283149290692347e-17 + 3.237455101084436e-15im, -7.612535273941595e-14 - 1.8338992189508272e-31im]
[-8.680854672628291e-11 - 3.427254579020333e-25im, 8.709110140864061e-11 - 8.699033104505336e-10im, -5.649235901782656e-13 + 8.601572275260816e-12im, -8.709221846767839e-11 - 2.859732693854631e-12im, 1.7418295344900034e-10 - 8.612608133903897e-10im, 8.41248317362033e-9 + 2.6096671235273274e-9im, -8.652487588621059e-11 - 1.1500395899126693e-11im, 8.709222358844944e-11 - 8.699014655672987e-10im, -5.669821718119223e-13 + 8.629638578162805e-12im, -8.641073444337035e-11 - 1.4395664161950265e-11im … -8.538755211456945e-11 - 2.0113079067784756e-11im, 1.7418407565004836e-10 - 8.612606969068827e-10im, -2.825548799186401e-13 + 8.640787361594646e-12im, -8.680873675466659e-11 - 1.152847038256832e-11im, 8.478635815548866e-13 + 8.382372443494577e-14im, -5.669819735592012e-13 + 8.629638588149376e-12im, -8.680836165042751e-11 - 8.640671375786309e-12im, 1.7418313902498964e-10 - 8.61262658275938e-10im, -2.82554682353394e-13 + 8.612701741862262e-12im, -8.680854672628374e-11 - 2.811149115586865e-25im]
[-4.2861942928308275e-9 - 4.7678984538985196e-24im, 4.318192995176613e-9 - 2.8269371315624182e-8im, -6.395173522788358e-11 + 6.447233357075582e-10im, -4.318468070877377e-9 - 2.1364257686596578e-10im, 8.636571975410676e-9 - 2.7617721500979347e-8im, 1.7289660717976908e-7 + 8.480086489483005e-8im, -4.253920910159066e-9 - 8.649865429907717e-10im, 4.318470318624579e-9 - 2.826906169988642e-8im, -6.446071076018044e-11 + 6.495016281145607e-10im, -4.240844230135151e-9 - 1.0846764703342651e-9im … -4.124170626784771e-9 - 1.5115818225612714e-9im, 8.636849310525618e-9 - 2.7617681592276622e-8im, -3.199878870600196e-11 + 6.513863923787342e-10im, -4.2862418335791054e-9 - 8.697676141630365e-10im, 9.60478168658149e-11 + 1.4206678132640617e-11im, -6.446062402203318e-11 + 6.495016332435747e-10im, -4.286148929585427e-9 - 6.513467397401749e-10im, 8.636617557977306e-9 - 2.7618031117900408e-8im, -3.1998702345812497e-11 + 6.466014984949206e-10im, -4.286194292830829e-9 + 4.870909626742163e-24im]
[-6.079348924748445e-8 + 6.709717322272516e-24im, 6.162943323939352e-8 - 2.9592110353110093e-7im, -1.6696556398383817e-9 + 1.2400459826013194e-8im, -6.164292787255694e-8 - 4.088131560944853e-9im, 1.2326810978249792e-7 - 2.832733452226872e-7im, 1.253532043962043e-6 + 8.875042569125039e-7im, -5.994408800994472e-8 - 1.6725258647088304e-8im, 6.164314112979357e-8 - 2.9591021577421156e-7im, -1.6948387141108349e-9 + 1.2572865374243683e-8im, -5.959595610442699e-8 - 2.1031419496566967e-8im … -5.651357562833814e-8 - 2.9192023780832225e-8im, 1.2328181992163385e-7 - 2.832706038202527e-7im, -8.359520177503641e-10 + 1.2640019589288568e-8im, -6.079590106381668e-8 - 1.689785040734888e-8im, 2.5106376181130386e-9 + 5.084778396177926e-10im, -1.6948306165735295e-9 + 1.2572866042727832e-8im, -6.079128573701664e-8 - 1.2637312624047932e-8im, 1.2327033399936622e-7 - 2.8328423313378375e-7im, -8.359439919088748e-10 + 1.2467163914771088e-8im, -6.079348924748447e-8 + 1.0331673240130025e-23im]
[-4.193552407608965e-7 - 1.840220498495959e-23im, 4.2862657866184504e-7 - 1.605297190776032e-6im, -1.8502762435969943e-8 + 1.0856276308591437e-7im, -4.288690355597115e-7 - 3.554458373697522e-8im, 8.574220964863143e-7 - 1.4932465582703908e-6im, 4.849187224415274e-6 + 4.812233679729101e-6im, -4.098425461298137e-7 - 1.4745065418194435e-7im, 4.288753395920834e-7 - 1.6051467128773302e-6im, -1.896035285782807e-8 + 1.1102835700521827e-7im, -4.0588320553359593e-7 - 1.8611022850670223e-7im … -3.711838427223188e-7 - 2.569391306045151e-7im, 8.57670971098675e-7 - 1.4931825847645462e-6im, -9.271568570757063e-9 + 1.1197289579952942e-7im, -4.194005371452928e-7 - 1.4992045110994755e-7im, 2.787088237718031e-8 + 7.194933454521717e-9im, -1.8960118645415406e-8 + 1.1102838165568638e-7im, -4.193161709409199e-7 - 1.1191024844294344e-7im, 8.574617742181924e-7 - 1.4933970418540967e-6im, -9.271337900947239e-9 + 1.0949690324053836e-7im, -4.193552407608964e-7 + 1.6524848989693985e-22im]
[-1.7823579016922897e-6 - 1.087640585040274e-21im, 1.840838369770109e-6 - 5.569854913729441e-6im, -1.1658899308162594e-7 + 5.632670364049069e-7im, -1.843111911436975e-6 - 1.8277839588324167e-7im, 3.6832950716080163e-6 - 4.979757871286645e-6im, 1.1823232043972897e-5 + 1.668138856075027e-5im, -1.7216192586061922e-6 - 7.718155525067347e-7im, 1.8432004842500001e-6 - 5.56872697042903e-6im, -1.209408931510706e-7 + 5.825677201706816e-7im, -1.6958321412209815e-6 - 9.789484717969276e-7im … -1.4727630743780977e-6 - 1.3421173522554028e-6im, 3.6856596566327488e-6 - 4.9790105233659736e-6im, -5.848359517965976e-8 + 5.898080282235565e-7im, -1.7828068643880377e-6 - 7.911632992433939e-7im, 1.760517112427114e-7 + 5.557422909554899e-8im, -1.209376913040035e-7 + 5.825681268981909e-7im, -1.7819976541714096e-6 - 5.890838036292954e-7im, 3.6836637838442504e-6 - 4.980885908360778e-6im, -5.848046807781978e-8 + 5.703874412047737e-7im, -1.7823579016922876e-6 + 1.4997773495573015e-22im]
[-5.2719847869895295e-6 + 8.772351797140282e-21im, 5.515796864069832e-6 - 1.3769890958036113e-5im, -4.854583030811284e-7 + 1.984102714841576e-6im, -5.52914007682216e-6 - 6.365426377065764e-7im, 1.1041341496086288e-5 - 1.1652942788167796e-5im, 1.9151485012220565e-5 + 4.1169949873337434e-5im, -5.0149560721693026e-6 - 2.748560607544284e-6im, 5.529875137426081e-6 - 1.3764484293927507e-5im, -5.114444270677655e-7 + 2.0817439894898367e-6im, -4.903067916522327e-6 - 3.508133202204623e-6im … -3.950715241692067e-6 - 4.767013437270117e-6im, 1.1055449702075777e-5 - 1.1647607717886785e-5im, -2.4383689790869913e-7 + 2.1174362562762014e-6im, -5.274804158840664e-6 - 2.846520575369006e-6im, 7.355815857851513e-7 + 2.7656731682993024e-7im, -5.114187341304747e-7 + 2.0817477909234415e-6im, -5.2699143882793144e-6 - 2.112332735357189e-6im, 1.1043481329588082e-5 - 1.1658350328429614e-5im, -2.438120770802614e-7 + 2.018953103020384e-6im, -5.271984786989539e-6 + 6.138686657683503e-21im]
[-1.1617825704147756e-5 + 1.0312565819538484e-21im, 1.2349337633136464e-5 - 2.5744303624623147e-5im, -1.454293291805138e-6 + 5.123664098726546e-6im, -1.2403650700016836e-5 - 1.6202581807656819e-6im, 2.473960492472325e-5 - 2.0155561130978555e-5im, 1.9144096039811722e-5 + 7.6742969783544e-5im, -1.0832694804431494e-5 - 7.1930875309620025e-6im, 1.2407719644833958e-5 - 2.572612053989065e-5im, -1.56229208977908e-6 + 5.474515092672054e-6im, -1.0480130191816845e-5 - 9.254685626031852e-6im … -7.537537188446326e-6 - 1.2435206581306264e-5im, 2.4798220145598703e-5 - 2.0129513964044365e-5im, -7.316420138432232e-7 + 5.598791194770797e-6im, -1.1630271281791021e-5 - 7.545396775206743e-6im, 2.2142322134173096e-6 + 9.742099793903422e-7im, -1.5621555243617513e-6 + 5.474538012007301e-6im, -1.1609612670484607e-5 - 5.574263610620754e-6im, 2.4748196626943606e-5 - 2.0173749496261283e-5im, -7.315119289888009e-7 + 5.243914596674148e-6im, -1.1617825704147746e-5 + 2.0077178062353968e-21im]
[-1.9812558848038747e-5 - 9.187798066332235e-22im, 2.147271081932555e-5 - 3.757217609507472e-5im, -3.2943974661872274e-6 + 1.0138423201098428e-5im, -2.163583224409355e-5 - 3.1479593611068202e-6im, 4.3072956212126986e-5 - 2.6216146005671797e-5im, 4.770433131249609e-6 + 0.00011143181451766211im, -1.7992013464296718e-5 - 1.4466566267165694e-5im, 2.1652013724844488e-5 - 3.752687773861608e-5im, -3.6269134427165884e-6 + 1.1086241359275966e-5im, -1.7142579310500252e-5 - 1.8804566182208357e-5im … -1.0216935244391546e-5 - 2.491043927093904e-5im, 4.325353440439837e-5 - 2.6122963951736878e-5im, -1.6606359562313976e-6 + 1.140941830342574e-5im, -1.9853711381944784e-5 - 1.541920404485947e-5im, 5.050007317756615e-6 + 2.569541043839736e-6im, -3.6263960453836696e-6 + 1.1086337766500889e-5im, -1.97886585017898e-5 - 1.1323323936752868e-5im, 4.309833408283902e-5 - 2.6261466566058982e-5im, -1.6601519712867477e-6 + 1.044756873253024e-5im, -1.981255884803875e-5 + 7.339651656276922e-20im]
[-2.6605994524636198e-5 + 1.1289591858171765e-19im, 2.9532145628332138e-5 - 4.333877076489454e-5im, -5.793688435837011e-6 + 1.572023652812328e-5im, -2.9906819721527362e-5 - 4.767746092973603e-6im, 5.9370303731772103e-5 - 2.51584831603933e-5im, -2.1873141629015348e-5 + 0.00012741720406900944im, -2.3313208885217603e-5 - 2.288272975975095e-5im, 2.995521974547466e-5 - 4.325274969426522e-5im, -6.580507052690018e-6 + 1.770923159800008e-5im, -2.1702668886267368e-5 - 3.0142728729320425e-5im … -8.927183090969304e-6 - 3.921197964153728e-5im, 5.979856139686108e-5 - 2.4903653161242186e-5im, -2.9274853045629046e-6 + 1.8356702741523666e-5im, -2.6711940545518465e-5 - 2.488368314305582e-5im, 8.967724203429812e-6 + 5.238269352768273e-6im, -6.579046665900031e-6 + 1.770952822000892e-5im, -2.6553193423229552e-5 - 1.812661956307733e-5im, 5.942741740138485e-5 - 2.5244572524328294e-5im, -2.9261511036959703e-6 + 1.6330572728121542e-5im, -2.660599452463617e-5 - 3.440870688152987e-20im]
상태 벡터 를 나타내는 해로부터 사이트당 자화를 얻을 수 있으며, 이는 시간에 따른 단일 큐비트 기댓값으로 표현됩니다. 이를 Trotter화된 회로에서 얻은 결과와 비교합니다.
# get a single-qubit expectation value ⟨Z_qubit⟩ from a full state vector, weighting ±1 by |amplitude|²
function z_expval_from_state(ψ::AbstractVector{<:Complex}, qubit::Int, n::Int)
s = 0.0
for b in 0:2^n-1
bit = bit_at(b, qubit)
s += (1 - 2bit) * abs2(ψ[b+1])
end
return s
end
classical_magnetizations = [z_expval_from_state(sol.u[r+1], q, N)
for r in 0:r_max, q in 1:N]
11×20 Matrix{Float64}:
-1.0 1.0 -1.0 1.0 … 1.0 -1.0 1.0
-0.995021 0.995034 -0.995034 0.995034 0.995034 -0.995034 0.995021
-0.980189 0.980386 -0.980386 0.980386 0.980386 -0.980386 0.980189
-0.955994 0.956968 -0.956968 0.956968 0.956968 -0.956968 0.955994
-0.922667 0.925652 -0.925653 0.925653 0.925653 -0.925652 0.922667
-0.881106 0.888117 -0.88812 0.88812 … 0.88812 -0.888117 0.881106
-0.832251 0.846116 -0.846129 0.846129 0.846129 -0.846116 0.832251
-0.776957 0.801257 -0.801298 0.801298 0.801298 -0.801257 0.776957
-0.715858 0.754749 -0.75486 0.75486 0.75486 -0.754749 0.715858
-0.649744 0.707628 -0.707895 0.707895 0.707895 -0.707628 0.649744
-0.580117 0.661272 -0.661841 0.661842 … 0.661841 -0.661272 0.580117
Trotter화된 회로의 소규모 시뮬레이션
다음에서는 TensorNetworkQuantumSimulator.jl이 지원하는 텐서 네트워크 방법을 사용해 노이즈 없는 회로를 고전적으로 시뮬레이션하여 회로 구성을 검증합니다. 이러한 방법은 양자 하드웨어의 결과와 비교할 기준선을 제공합니다.
먼저 named_grid((N,))를 사용해 격자를 1차원 사슬 그래프로 정의하며, 각 꼭짓점은 튜플 (i,)입니다. 그런 다음 회로 게이트를 튜플 (gate_name, qubit_indices, gate_parameter)의 리스트로 지정하며, 이는 텐서 네트워크 시뮬레이터의 입력 형식으로 사용됩니다.
# 1D chain graph — vertices are named (1,), (2,), ..., (N,)
g = named_grid((N,))
# Gates to prepare Néel state |0101…⟩, X on every other site
neel_state_gates(n::Int) = [("X", [(i,)]) for i in 1:2:n]
# Gates for one second-order Trotter step of size δt
trotter_step_gates(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real) = vcat(
[("Rx", [(i,)], h[i] * δt) for i in 1:n],
[("Rzz", [(i,), (i+1,)], 2 * J[i] * δt) for i in 1:n-1],
[("Rx", [(i,)], h[i] * δt) for i in 1:n])
# Make a list of gates: Néel state preparation followed by n_trotter_steps Trotter steps
function make_trotter_circuit_tn(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real,
n_trotter_steps::Int)
circuit = []
# Neel state initialization
append!(circuit, neel_state_gates(n))
for _ in 1:n_trotter_steps
append!(circuit, trotter_step_gates(h, J, n, δt))
end
return circuit
end
make_trotter_circuit_tn (generic function with 1 method)
텐서 네트워크 축약에는 신뢰 전파(belief propagation) 알고리즘을 사용합니다. 이 방법은 얽힘이 제한된 회로에는 효율적이지만, 회로 깊이에 따라 얽힘이 커지면 정확도가 떨어집니다. maxdim과 cutoff 매개변수는 정확도와 계산 비용 사이의 절충을 조절합니다. 마찬가지로, 나중에 비교할 수 있도록 각 사이트의 자화를 계산합니다.
apply_kwargs = (; maxdim=32, cutoff=1e-10, normalize_tensors=true)
tn_magnetizations = zeros(r_max+1, N)
# |↑↑…↑⟩ product state, wrapped in a belief propagation cache
tn_initial_state(g::NamedGraph) = BeliefPropagationCache(
tensornetworkstate(ComplexF32, v -> "↑", g, "S=1/2"))
# Apply a gate list to a TN state; returns the evolved state and the
# truncation fidelity, such as ∏(1 - ε) over all gate applications
function apply_gates_to_tn_state(circuit::Vector, ψ_bpc::BeliefPropagationCache; apply_kwargs::NamedTuple)
ψ_bpc, errs = apply_gates(circuit, ψ_bpc; apply_kwargs)
return ψ_bpc, prod(1.0 .- errs)
end
# ⟨Z_q⟩ on every site of a tensor-network state
z_expvals_from_tn_state(ψ_bpc::BeliefPropagationCache, n::Int) =
[real(expect(ψ_bpc, [("Z", [(q,)])])[1]) for q in 1:n]
for r in 0:r_max
circuit = make_trotter_circuit_tn(h, J, N, δt, r)
ψ_bpc, fidelity = apply_gates_to_tn_state(circuit, tn_initial_state(g); apply_kwargs)
println("fidelity at Trotter step $(r) was $(fidelity)")
tn_magnetizations[r+1, :] = z_expvals_from_tn_state(ψ_bpc, N)
end
fidelity at Trotter step 0 was 1.0
fidelity at Trotter step 1 was 1.0
fidelity at Trotter step 2 was 1.0
fidelity at Trotter step 3 was 0.9999999999999679
fidelity at Trotter step 4 was 0.9999999999976941
fidelity at Trotter step 5 was 0.9999999999476229
fidelity at Trotter step 6 was 0.9999999993741544
fidelity at Trotter step 7 was 0.9999999993647009
fidelity at Trotter step 8 was 0.9999999992323603
fidelity at Trotter step 9 was 0.9999999980892764
fidelity at Trotter step 10 was 0.9999999980892698
1단계: 고전적 입력을 양자 문제로 매핑하기
이제 Qiskit.jl을 사용해 Trotter화된 시간 발전 회로를 구성합니다. 이 회로는 텐서 네트워크 버전을 그대로 따릅니다. Néel 상태를 초기화하고, 및 게이트로 이루어진 Trotter 단계를 번 적용한 다음, 마지막으로 모든 큐비트를 Z 기저에서 측정합니다.
function make_trotter_circuit(h::AbstractVector{<:Real}, J::AbstractVector{<:Real}, n::Int, δt::Real, n_trotter_steps::Int)
qc = QuantumCircuit(n, n)
# Neel state initialization
for i in 1:2:n
x!(qc, i)
end
# Trotter evolution
for _ in 1:n_trotter_steps
for i in 1:n
rx!(qc, h[i] * δt, i)
end
for i in 1:n-1
rzz!(qc, 2* J[i] * δt, i, i+1)
end
for i in 1:n
rx!(qc, h[i] * δt, i)
end
end
# measure in Z basis
for i in 1:n
measure!(qc, i, i)
end
return qc
end
qc = make_trotter_circuit(h, J, N, δt, 1)
QuantumCircuit with 20 qubits, 20 clbits
instructions: 89
Trotter 단계 0부터 10까지에 해당하는 회로 리스트를 만듭니다. 이는 발전 시간 에 대응합니다.
# prepare a list of circuits with different Trotter steps
qc_list = [make_trotter_circuit(h, J, N, δt, r) for r in 0:r_max]
11-element Vector{QuantumCircuit}:
QuantumCircuit(20, 20; 30 instructions)
QuantumCircuit(20, 20; 89 instructions)
QuantumCircuit(20, 20; 148 instructions)
QuantumCircuit(20, 20; 207 instructions)
QuantumCircuit(20, 20; 266 instructions)
QuantumCircuit(20, 20; 325 instructions)
QuantumCircuit(20, 20; 384 instructions)
QuantumCircuit(20, 20; 443 instructions)
QuantumCircuit(20, 20; 502 instructions)
QuantumCircuit(20, 20; 561 instructions)
QuantumCircuit(20, 20; 620 instructions)
2단계: 양자 하드웨어 실행에 맞게 문제 최적화하기
양자 하드웨어에서 실행하려면 먼저 회로를 트랜스파일해야 합니다. 여기에는 회로를 매핑할 물리 큐비트 집합 선택, 게이트를 백엔드의 네이티브 명령어 집합으로 다시 컴파일, 결과 회로 깊이 최적화 단계가 포함됩니다. least_busy()로 사용 가능한 백엔드 중 가장 한가한 백엔드를 자동으로 선택하고, target_from_backend()로 해당 백엔드의 네이티브 게이트 집합과 큐비트 연결성을 가져오며, transpile()로 컴파일을 수행합니다.
service = Service()
search_results = backend_search(service)
backend = least_busy(search_results)
@show backend.name
backend.name = "ibm_phoenix"
"ibm_phoenix"
target = target_from_backend(backend, service)
Target with 120 qubits
instructions: 8
tqc_list = [transpile(qc, target)[1] for qc in qc_list]
11-element Vector{QuantumCircuit}:
QuantumCircuit(120, 20; 30 instructions)
QuantumCircuit(120, 20; 211 instructions)
QuantumCircuit(120, 20; 344 instructions)
QuantumCircuit(120, 20; 475 instructions)
QuantumCircuit(120, 20; 606 instructions)
QuantumCircuit(120, 20; 737 instructions)
QuantumCircuit(120, 20; 868 instructions)
QuantumCircuit(120, 20; 999 instructions)
QuantumCircuit(120, 20; 1130 instructions)
QuantumCircuit(120, 20; 1261 instructions)
QuantumCircuit(120, 20; 1392 instructions)
트랜스파일 후에는 컴파일된 회로의 두 가지 속성을 살펴봅니다. get_circuit_layout()은 회로에 선택된 물리 큐비트 인덱스의 집합을 반환합니다. two_qubit_depth()는 2큐비트 게이트 깊이, 즉 회로에서 2큐비트 연산으로 이루어진 가장 긴 사슬의 길이를 계산하며, 이는 하드웨어에서 노이즈가 누적되는 정도를 보여 주는 유용한 지표입니다.
function get_circuit_layout(tqc::QuantumCircuit)
return Set(q for inst in tqc.data for q in inst.qubits)
end
get_circuit_layout(tqc_list[2])
Set{Int64} with 20 elements:
35
110
58
12
24
37
23
22
47
69
36
80
109
90
57
34
13
59
70
100
아래에서는 각 Trotter 단계의 2큐비트 게이트 수와 회로 깊이를 출력합니다. 예상대로 둘 다 단계 수에 따라 선형적으로 증가합니다. 분수 게이트는 아직 C API를 통해 사용할 수 없습니다(qiskit-ibm-runtime-c#29 참조). 그 결과 각 RZZGate는 하나가 아니라 두 개의 2큐비트 게이트로 트랜스파일되어 2큐비트 게이트 수가 늘어납니다.
two_qubit_count(qc::QuantumCircuit) = count(inst -> length(inst.qubits) == 2, qc.data)
function two_qubit_depth(qc::QuantumCircuit)
qubit_depth = Dict{Int,Int}()
for inst in qc.data
length(inst.qubits) == 2 || continue # skip non-two-qubit gates
d = maximum(get(qubit_depth, q, 0) for q in inst.qubits)
for q in inst.qubits
qubit_depth[q] = d + 1
end
end
return isempty(qubit_depth) ? 0 : maximum(values(qubit_depth))
end
for (i, tqc) in enumerate(tqc_list)
println("r=$(i-1): 2q gate count=$(two_qubit_count(tqc)), 2q gate depth=$(two_qubit_depth(tqc))")
end
r=0: 2q gate count=0, 2q gate depth=0
r=1: 2q gate count=38, 2q gate depth=38
r=2: 2q gate count=76, 2q gate depth=42
r=3: 2q gate count=114, 2q gate depth=46
r=4: 2q gate count=152, 2q gate depth=50
r=5: 2q gate count=190, 2q gate depth=54
r=6: 2q gate count=228, 2q gate depth=58
r=7: 2q gate count=266, 2q gate depth=62
r=8: 2q gate count=304, 2q gate depth=66
r=9: 2q gate count=342, 2q gate depth=70
r=10: 2q gate count=380, 2q gate depth=74
3단계: Qiskit 프리미티브를 사용해 실행하기
이제 트랜스파일된 회로를 shots를 지정하여 Sampler 작업으로 백엔드에 제출할 수 있습니다.
shots = 1024
job_list = [run_sampler_job(service, backend, tqc, shots) for tqc in tqc_list]
11-element Vector{QiskitIBMRuntime.Job}:
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000003427f72f0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d1b4e0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2dcb180)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b1e24840)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b266e640)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d1b690)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c17980)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b315b9b0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c16090)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c195f0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2d33f70)
for (i, job) in enumerate(job_list)
status = get_job_status(job, service)
println("Job $i: ", status)
end
Job 1: Completed
Job 2: Completed
Job 3: Completed
Job 4: Completed
Job 5: Completed
Job 6: Completed
Job 7: Completed
Job 8: Completed
Job 9: Completed
Job 10: Completed
Job 11: Completed
작업이 완료되면 결과를 가져올 수 있습니다. get_sampler_job_results 함수는 작업이 완료될 때까지 대기(block)한다는 점에 유의하세요.
all_samples = [get_sampler_job_results(job, service) for job in job_list]
11-element Vector{QiskitIBMRuntime.Samples}:
[[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0] … [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 1], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1], [1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 1], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0] … [1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
[[0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0] … [1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0], [1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0]]
[[0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1], [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0], [0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1], [0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0] … [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1]]
[[1, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 1, 0], [1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0] … [1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0], [1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 0, 0], [1, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0], [0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0], [1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0]]
4단계: 후처리하여 원하는 고전적 형식으로 결과 반환하기
양자 하드웨어에서 얻은 비트 문자열 샘플로부터, 모든 샷에 대해 를 평균하여 사이트당 자화(단일 큐비트 기댓값)를 계산합니다. 여기서 는 큐비트 의 측정된 비트입니다. 그런 다음 큐비트와 Trotter 단계에 대한 히트맵으로 자화를 그려, 정확한 고전 시뮬레이션, 노이즈 없는 텐서 네트워크 시뮬레이션, 하드웨어 실행의 세 가지 방법을 나란히 비교합니다.
# Compute expectation values
# 0 -> 1, 1 -> -1
z_expval(samples, i) = mean((-1)^s[i] for s in samples)
magnetizations = [z_expval(all_samples[i], q) for i in 1:length(all_samples), q in 1:N]
11×20 Matrix{Float64}:
-0.996094 0.998047 -0.990234 0.998047 … 1.0 -0.988281 0.994141
-0.925781 0.980469 -0.878906 0.96875 0.970703 -0.976562 0.988281
-0.916016 0.992188 -0.837891 0.957031 0.962891 -0.966797 0.953125
-0.884766 0.970703 -0.824219 0.90625 0.908203 -0.939453 0.892578
-0.835938 0.96875 -0.814453 0.884766 0.931641 -0.902344 0.908203
-0.777344 0.958984 -0.78125 0.871094 … 0.935547 -0.876953 0.939453
-0.732422 0.933594 -0.767578 0.8125 0.884766 -0.835938 0.890625
-0.650391 0.916016 -0.771484 0.771484 0.853516 -0.773438 0.837891
-0.537109 0.902344 -0.705078 0.742188 0.875 -0.662109 0.833984
-0.472656 0.923828 -0.652344 0.728516 0.839844 -0.695312 0.808594
-0.4375 0.923828 -0.613281 0.681641 … 0.890625 -0.658203 0.8125
아래 세 패널은 큐비트 인덱스(x축)와 Trotter 단계(y축)의 함수로 사이트 자화 를 보여 줍니다. , 일 때 전체 발전 시간은 로, 초기 반강자성 패턴이 아직 사라지지 않을 만큼 짧으며, 세 방법 모두 강하게 교대하는 패턴을 보입니다. 고전 결과와 텐서 네트워크 결과는 이제 매우 잘 일치하며, 이는 이 단계 크기에서 Trotter 오차가 작다는 것을 확인해 줍니다. 하드웨어 결과는 나머지 두 결과를 대체로 따르지만, 일부 큐비트는 다른 큐비트보다 시뮬레이션에서 더 많이 벗어나는데, 이는 백엔드 전반에 걸친 큐비트 품질의 차이를 반영합니다. 이러한 편차는 회로 깊이가 증가하는 이후의 Trotter 단계에서 커집니다.
# plot magnetization as a function of time
l = @layout [a{0.3w} b{0.3w} c{0.44w}]
plot(
heatmap(classical_magnetizations, title="Classical", clims=(-1,1), color=:RdBu,
xlabel="Qubit", ylabel="Trotter steps", colorbar=false),
heatmap(tn_magnetizations, title="Tensor Network", clims=(-1,1), color=:RdBu,
xlabel="Qubit", ylabel="Trotter steps", colorbar=false),
heatmap(magnetizations, title="Hardware", clims=(-1,1), color=:RdBu,
xlabel="Qubit", ylabel="Trotter steps", colorbar=true),
layout=l, size=(900,300),
bottom_margin=5mm, left_margin=5mm, right_margin=6mm
)
대규모 하드웨어 예제
하나의 워크플로로 구성한 1~4단계
이제 위의 네 단계를 모두 하나의 워크플로로 결합하며, 정확한 고전 시뮬레이션으로는 다룰 수 없는 규모에서 실행합니다. 사이트별로 자화를 분해하는 대신, 반강자성 질서를 나타내는 단일 스칼라 척도인 엇갈린 자화(staggered magnetization)를 추적합니다.
합산에서 교대하는 부호 덕분에 신호가 눈에 보이게 됩니다. Néel 초기 상태 에서는 모든 항이 을 기여하므로 인 반면, 단순 평균 은 모든 에 대해 항등적으로 0이 됩니다. 횡자기장이 교대하는 패턴을 흩뜨리면 는 을 향해 감소하므로, 엇갈린 자화는 시간 발전 동안 초기 질서가 얼마나 남아 있는지를 알려 줍니다.
이 예제를 사용해 Trotter 단계 크기가 정확도에 미치는 영향도 살펴봅니다. 전체 발전 시간을 로 고정하고 Trotter 단계 수를 로 변화시키며, 따라서 입니다. 하드웨어에서는 두 가지 오차 원인이 서로 경쟁합니다. 가 작을수록 Trotter 오차는 줄지만, 그에 비례해 더 많은 2큐비트 게이트가 필요하고 이는 더 많은 하드웨어 노이즈를 누적시킵니다.
# -------------------------Step 1-------------------------
# Map classical inputs to a quantum problem.
N_large = 100
g_large = named_grid((N_large,))
h_large = fill(1.0, N_large) # transverse field on every site
J_large = fill(1.0, N_large - 1) # nearest-neighbor ZZ couplings on the chain
T_total = 1.5 # fixed total evolution time
r_list = [3, 6, 12] # varying Trotter steps; δt = T_total/r
sweep = [(r, k) for r in r_list for k in 0:r]
qc_list_large = [make_trotter_circuit(h_large, J_large, N_large, T_total/r, k)
for (r, k) in sweep]
# -------------------------Step 2-------------------------
# Optimize the problem for quantum hardware execution.
tqc_list_large = [transpile(qc, target)[1] for qc in qc_list_large]
# Print the 2q gate count and depth of the deepest circuit at each δt",
for r in r_list
i = findfirst(==((r, r)), sweep) # the k = r circuit reaches the full T_total
println(" δt = $(round(T_total/r, digits=4)) → $(r+1) time points, ",
"deepest circuit = $(r) Trotter steps, ",
"2q count = $(two_qubit_count(tqc_list_large[i])), ",
"2q depth = $(two_qubit_depth(tqc_list_large[i]))")
end
# -------------------------Step 3-------------------------
# Execute using Qiskit primitives.
shots_large = 4096
job_list_large = [run_sampler_job(service, backend, tqc, shots_large)
for tqc in tqc_list_large]
δt = 0.5 → 4 time points, deepest circuit = 3 Trotter steps, 2q count = 594, 2q depth = 206
δt = 0.25 → 7 time points, deepest circuit = 6 Trotter steps, 2q count = 1188, 2q depth = 218
δt = 0.125 → 13 time points, deepest circuit = 12 Trotter steps, 2q count = 2376, 2q depth = 242
24-element Vector{QiskitIBMRuntime.Job}:
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1a750)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0a270)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c21a50)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e05120)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c2a6e0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0d140)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e0b640)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1a200)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b312eb90)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c22060)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b31ea4e0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c1ccf0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000104c2af50)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b312ffb0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b0e89700)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e15070)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e10d40)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004af5a5d10)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e12500)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e14870)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e10730)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b237ca60)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x00000004b2c713a0)
QiskitIBMRuntime.Job(Ptr{QiskitIBMRuntime.LibQiskitIBMRuntime.Job} @0x0000000102e13e90)
# Run this cell to check the job status
# Run the following cell for post-processing after all jobs complete
for (i, job) in enumerate(job_list_large)
r, k = sweep[i]
println("Job $i (δt=$(round(T_total/r, digits=4)), k=$k): ",
get_job_status(job, service))
end
Job 1 (δt=0.5, k=0): Completed
Job 2 (δt=0.5, k=1): Completed
Job 3 (δt=0.5, k=2): Completed
Job 4 (δt=0.5, k=3): Completed
Job 5 (δt=0.25, k=0): Completed
Job 6 (δt=0.25, k=1): Completed
Job 7 (δt=0.25, k=2): Completed
Job 8 (δt=0.25, k=3): Completed
Job 9 (δt=0.25, k=4): Completed
Job 10 (δt=0.25, k=5): Completed
Job 11 (δt=0.25, k=6): Completed
Job 12 (δt=0.125, k=0): Completed
Job 13 (δt=0.125, k=1): Completed
Job 14 (δt=0.125, k=2): Completed
Job 15 (δt=0.125, k=3): Completed
Job 16 (δt=0.125, k=4): Completed
Job 17 (δt=0.125, k=5): Completed
Job 18 (δt=0.125, k=6): Completed
Job 19 (δt=0.125, k=7): Completed
Job 20 (δt=0.125, k=8): Completed
Job 21 (δt=0.125, k=9): Completed
Job 22 (δt=0.125, k=10): Completed
Job 23 (δt=0.125, k=11): Completed
Job 24 (δt=0.125, k=12): Completed
# -------------------------Step 4-------------------------
# Post-process and return the result in the desired classical format.
# Run this cell after all jobs are completed
# ⟨M_s⟩ = (1/N) Σ (-1)^i ⟨Z_i⟩, averaged over hardware shots
function staggered_magnetization(samples::AbstractVector{<:AbstractVector}, n::Int)
s = 0.0
for sample in samples
s += sum((-1)^i * (1 - 2 * sample[i]) for i in 1:n) / n
end
return s / length(samples)
end
all_samples_large = [get_sampler_job_results(job, service) for job in job_list_large]
mags_hardware = [staggered_magnetization(s, N_large) for s in all_samples_large]
24-element Vector{Float64}:
0.9880371093750097
0.697231445312484
0.44886718750000276
0.29333496093750067
0.9882324218750095
0.8109619140625324
0.6457958984374791
0.4808593749999992
0.36695312500000055
0.2963671875000012
0.23831542968750055
0.9884472656250093
0.8566455078125492
0.7951171875000282
0.7050732421874865
0.610673828124981
0.529980468749995
0.4656054687500018
0.4149316406250024
0.3799462890625011
0.35178710937500185
0.3185888671875003
0.31287597656250016
0.29621093750000077
에서는 위에서 사용한 ODE 풀이기의 정확한 해를 구할 수 없습니다. 상태 벡터만으로도 개의 진폭이 필요하기 때문입니다. 대신 동일한 1차원 사슬을 훨씬 더 세밀한 Trotter 단계(, )로 노이즈 없이 텐서 네트워크 시뮬레이션한 결과를 기준으로 사용하며, 이 경우 Trotter 오차는 하드웨어에서 실행하는 어떤 와 비교해도 무시할 만합니다. 이 기준 자체도 근사입니다. 지배적인 오차는 앞에서 논의한 결합 차원(bond-dimension) 절단이며, 각 실행에 대해 절단 충실도로 보고됩니다.
apply_kwargs = (; maxdim=64, cutoff=1e-10, normalize_tensors=true)
# Calculate the staggered magnetization given a tensor network state
staggered_magnetization(ψ_bpc::BeliefPropagationCache, n::Int) =
sum((-1)^q * m for (q, m) in enumerate(z_expvals_from_tn_state(ψ_bpc, n))) / n
# Evolve a TN state and record the staggered magnetization at each step.
function compute_staggered_magnetization_tn(δt::Real, nsteps::Int; record_every::Int = 1)
init_gates = neel_state_gates(N_large)
step_gates = trotter_step_gates(h_large, J_large, N_large, δt)
ψ_bpc, fid = apply_gates_to_tn_state(init_gates, tn_initial_state(g_large); apply_kwargs)
times = [0.0]
mags = [staggered_magnetization(ψ_bpc, N_large)]
for k in 1:nsteps
ψ_bpc, fid_step = apply_gates_to_tn_state(step_gates, ψ_bpc; apply_kwargs)
fid *= fid_step
if k % record_every == 0
push!(times, k * δt)
push!(mags, staggered_magnetization(ψ_bpc, N_large))
end
end
println(" δt=$(round(δt, digits=5)), $(nsteps) steps: truncation fidelity ≈ $(round(fid, digits=5))")
(times, mags)
end
# If the fidelity drifts from 1, raise `maxdim` in `apply_kwargs`.
# Under current setting, the tensor network simulation takes ~ 3 minutes on a laptop.
println("Tensor-network reference:")
r_ref = 96
times_ref, mags_ref = compute_staggered_magnetization_tn(T_total / r_ref, r_ref; record_every = r_ref ÷ 12)
Tensor-network reference:
δt=0.01562, 96 steps: truncation fidelity ≈ 1.0
([0.0, 0.125, 0.25, 0.375, 0.5, 0.625, 0.75, 0.875, 1.0, 1.125, 1.25, 1.375, 1.5], Float32[1.0, 0.9695576, 0.8870231, 0.77430177, 0.6560442, 0.5499543, 0.46285573, 0.39298016, 0.33518773, 0.28527063, 0.24147007, 0.20369667, 0.17206171])
아래 그래프는 세 가지 Trotter 단계 크기에 대한 시간에 따른 엇갈린 자화를 노이즈 없는 텐서 네트워크 기준(검은색 점선)과 함께 보여 줍니다.
plt = plot(xlabel = "Time", ylabel = "Staggered magnetization",
title = "N = $(N_large) on $(backend.name), T = $(T_total)",
legend = :topright, ylims = (-0.05, 1.05), size = (820, 480),
bottom_margin = 5mm, left_margin = 5mm)
# Plot tensor network reference
plot!(plt, times_ref, mags_ref, lw = 2, ls = :dash, color = :black,
label = "tensor network, δt → 0")
# Plot hardware result per Trotter step size
for (r, stop) in zip(r_list, cumsum(r_list .+ 1))
plot!(plt, range(0, T_total, length = r + 1), mags_hardware[(stop - r):stop],
marker = :circle, markersize = 4, lw = 2,
label = "hardware, δt = $(round(T_total / r, digits = 4))")
end
plt
전반적으로 가장 거친 Trotter 단계 (주황색 점)가 텐서 네트워크 기준에서 가장 크게 벗어나며, 아마도 Trotter 오차의 기여가 클 것으로 보입니다. 더 세밀한 단계 (초록색 점)에서는 하드웨어 결과가 기준과 더 가깝게 일치합니다. 가장 세밀한 단계 (보라색 점)에서는 Trotter 오차가 가장 작지만, 일치도는 일 때보다 나쁩니다. 단계 크기가 절반이 되면 각 시간 지점마다 두 배 많은 2큐비트 게이트가 필요하고, 추가된 노이즈가 Trotter 오차 감소분을 상쇄하고도 남기 때문입니다. 따라서 하드웨어에서 Trotter화된 회로의 를 선택하는 것은 Trotter 오차와 추가 게이트로 인해 누적되는 노이즈 사이의 절충입니다.
다음 단계
다음 자료에도 관심이 있을 수 있습니다.