횡자기장 이징 모델의 시간적 변화를 시뮬레이션한다
예상 사용 시간: Nighthawk r2 프로세서 기준 105초 (참고: 이는 단지 추정치일 뿐입니다.) (실행 시간은 다를 수 있습니다.)
학습 성과
- Julia를 사용하여 양자 회로를 트랜스파일하고 하드웨어에서 실행하는 방법을 알아보세요
- 측정 결과를 후처리하여 기대값을 계산하는 방법을 알아보세요
- 트로터 근사 오차와 하드웨어 노이즈가 결합하여 미치는 영향을 정량화하기 위해, 하드웨어 결과를 고전적 시뮬레이션과 비교하여 벤치마킹하는 방법을 알아보세요
전제조건
이 튜토리얼을 시작하기 전에 다음 주제에 대해 미리 숙지해 두시기 바랍니다:
배경
Julia는 주로 수치 및 과학 계산을 위해 설계된 동적 프로그래밍 언어입니다. 이 시스템의 고성능 수치 계산 능력 덕분에 양자 시뮬레이션 워크플로우에 안성맞춤입니다. 이 튜토리얼에서는 Julia를 사용하여 고전적인 전처리 및 후처리(예: 해밀토니안 구축, ODE(상미분방정식) 솔버 실행, 기대값 계산 등)는 물론 양자 하드웨어 작업 조율까지 수행함으로써, 언어나 환경을 전환할 필요가 없도록 하는 방법을 보여드립니다.
이 튜토리얼에서는 Julia에서 IBM Quantum® 하드웨어와 연동하기 위해 Qiskit 생태계의 두 가지 패키지를 사용합니다. 하나는 Qiskit C 라이브러리를 Qiskit.jl 래핑하여 Julia에서 회로 구성 및 트랜스파일링 기능을 제공하는 패키지이며, 다른 하나는 IBM Quantum Compute 서비스 클라이언트를 통해 IBM Quantum 하드웨어에 QiskitIBMRuntime.jl 연결하여 Julia에서 직접 작업을 제출하고 결과를 가져올 수 있게 해주는 패키지입니다.
이 튜토리얼에서는 가장 가까운 이웃 간 상호작용을 갖는 1D 사슬 위에서 횡장 이징 모델의 트로터화 진화를 살펴봅니다:
시간 진화 를 구현하기 위해, 시간 구간 을 단계로 나누고 를 정의한다. 2차 트로터-스즈키 분해에 따르면 다음과 같다:
회로 구성에 있어, 각 트로터 단계는 단일 큐비트 회전과 2-큐비트 게이트의 순서로 구현된다. 이 회로는 교대로 배치된 큐비트에 X 게이트를 적용하여 네엘 상태 를 준비하는 것으로 시작됩니다. 이후의 각 트로터 단계에서는 (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 값을 입력하세요. 이 셀은 문서 저장소에서 Manifest.toml Project.toml 와 를 이 노트북 옆의 env_tutorial/time-evolution/ 폴더로 다운로드한 다음, 해당 환경을 활성화하고 기록된 정확한 패키지 버전을 설치합니다. 이 프로젝트 파일은 환경을 개괄적으로 설명하며, 예를 들어 [deps] 섹션에는 모든 종속성이 나열되어 있습니다. 매니페스트 파일은 모든 패키지(간접 의존성 포함)의 정확한 버전을 고정하므로, 환경을 재현 가능하게 만듭니다. 자세한 내용은 Julia 문서를 참조하십시오.
다음 종속성이 환경에 설치됩니다.
양자 회로 구성 및 실행을 위해:
Qiskit.jlQiskitIBMRuntime.jl
고전적 시뮬레이션의 경우:
OrdinaryDiffEq.jlTensorNetworkQuantumSimulator.jl
후처리 결과 및 시각화에 대해서는:
StatsBase.jlPlots.jl
이 튜토리얼은 0.6.0Qiskit.jl 버전과 0.3.1QiskitIBMRuntime.jl 버전에서 테스트되었습니다.
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 계정과 API 토큰이 필요합니다 qiskit-ibm-runtime. “하드웨어에서 첫 번째 서킷 실행 하기” 항목에 있는 “설치 및 인증” 단계를 따라 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)) & 1Output:
bit_at
소규모 시뮬레이터 예시
우리는 앞서 설명한 횡자장 이징(transverse-field Ising) 모델로 기술되는, 큐비트로 구성된 1D 사슬을 고려한다. 고려 대상 시스템에 대해, 아래에서 시스템 크기 N, 트로터 단계 크기 δt, 그리고 총 트로터 단계 수 를 명시한다 r_max. 총 진화 시간은 δt * r_max. Julia는 유니코드 식별자를 지원합니다. 노트북이나 Julia REPL에서 δt;와 같은 기호를 입력하려면, 를 입력한 후 \delta Tab 키를 눌러 입력하십시오 δ. 자세한 내용은 Julia 유니코드 입력 관련 문서를 참조하십시오.
정확한 해
양자 하드웨어의 결과를 비교하기 위한 기준선을 마련하기 위해, 먼저 소규모 문제에 대한 고전적 시뮬레이션 워크플로우를 시연합니다. 우리는 이징 해밀토니안을 스파스 행렬로 구성한 다음, 에서 ODEProblem 를 사용하여 슈뢰딩거 방정식을 수치적으로 적분함으로써 정확한 시간 진화를 구합니다 OrdinaryDiffEq.jl. 이 접근법은 큐비트 수에 따라 기하급수적으로 복잡도가 증가합니다 . 또한 차원 상태 벡터 전체를 저장해야 합니다. 의 경우, 힐베르트 공간의 차원이 이미 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)Output:
1048576×1048576 SparseMatrixCSC{ComplexF64, Int64} with 22020096 stored entries:
⎡⣿⣿⣾⢦⡀⠳⣄⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎤
⎢⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⡀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠙⢦⡈⠳⣼⣿⣿⡆⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠙⢦⡀⠀⠀⠁⠀⠈⠈⠉⣿⣿⣾⢦⡀⠳⣄⠀⠀⠈⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠺⣟⢻⣶⣿⡂⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⢤⡈⠻⠻⠿⣧⣤⣠⡈⠳⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠀⣻⣿⣿⣙⣦⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡈⠳⣼⣿⣿⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠈⠳⣄⎥
⎢⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⣿⣿⡟⢦⡈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⠀⠈⠳⣄⠀⠀⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⠀⠀⠀⠈⠳⣄⠀⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⠀⠀⠀⠀⠀⠈⠳⣄⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠀⠀⡀⠀⠀⠙⢦⠈⠳⡿⣿⣿⣀⡀⡀⠀⢀⠀⠀⠈⠳⣄⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠸⣿⣿⡟⢦⡈⠳⣄⠀⠀⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠈⠻⣍⣿⣿⣯⠀⠈⠳⣄⠀⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠐⢦⡈⠋⠛⢻⣶⣦⣦⡈⠓⎥
⎢⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠀⠀⠀⠀⠀⠙⢦⡀⠀⠀⠙⢦⡀⠨⣿⠿⣧⣽⡦⎥
⎣⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠀⠀⠀⠀⠀⠙⢦⠀⠀⠀⠙⢦⠈⠳⡿⣿⣿⎦
우리는 슈뢰딩거 방정식의 우변을 ‘인플레이스(in-place)’ 형태로 정의하며 schrodinger!(dψ, ψ, H, t), 이는 희소 행렬-벡터 곱셈을 사용하여 를 계산합니다. 그런 다음, 네엘 ODEProblem (Néel) 상태를 초기 조건으로 설정하고 구간에서 방정식을 풀며, 각 시간 단계 에서 상태를 저장합니다. 사용된 솔버는 비강성 문제에 적합한 표준 명시적 4차/5차 룽게-쿠타(Runge-Kutta) 방법인 입니다 Tsit5().
# 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)Output:
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]
상태 벡터 를 나타내는 이 해를 통해, 시간의 함수로서 단일 큐비트 기대값 으로 표현되는 사이트당 자화도를 구할 수 있다. 이를 트로터화 회로에서 얻은 결과와 비교한다.
# 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]Output:
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
트로터화 회로의 소규모 시뮬레이션
이하에서는 우리의 회로 구성 방식을 검증하기 위해 TensorNetworkQuantumSimulator.jl, 에서 지원하는 텐서 네트워크 기법을 활용하여 잡음이 없는 회로에 대한 고전적 시뮬레이션 결과를 제시한다. 이러한 방법들은 양자 하드웨어에서 얻은 결과와 비교할 수 있는 기준이 됩니다.
먼저 named_grid((N,)), 각 정점이 튜플 인 를 사용하여 격자를 1D 체인 그래프로 정의합니다 (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
endOutput:
make_trotter_circuit_tn (generic function with 1 method)
텐서 네트워크 수축을 위해 신념 전파 알고리즘을 사용합니다. 이 방법은 얽힘이 제한된 회로에서는 효율적이지만, 회로 깊이가 증가함에 따라 얽힘이 커지면 정확도가 떨어집니다. 및 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)
endOutput:
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단계: 고전적 입력값을 양자 문제에 매핑하기
이제 를 사용하여 트로터화(Trotterized)된 시간 진화 회로를 구성합니다 Qiskit.jl. 이 회로는 텐서 네트워크 버전을 그대로 반영합니다. 즉, 네엘(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)Output:
QuantumCircuit with 20 qubits, 20 clbits
instructions: 89
우리는 진화 시간 에 해당하는 트로터 단계 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]Output:
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.nameOutput:
backend.name = "ibm_phoenix"
"ibm_phoenix"
target = target_from_backend(backend, service)Output:
Target with 120 qubits
instructions: 8
tqc_list = [transpile(qc, target)[1] for qc in qc_list]Output:
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])Output:
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
아래에는 각 트로터 단계별 2-큐비트 게이트 수와 회로 깊이를 표시했습니다. 예상대로, 두 값 모두 단계 수에 따라 선형적으로 증가합니다. 게이트의 소수점 버전은 아직 C API를 통해 사용할 수 없다는 점에 유의하십시오( qiskit-ibm-runtime-c#29 참조). 그 결과, 각각은 하나의 2-큐비트 게이트 대신 두 개의 2-큐비트 게이트로 변환되어 RZZGate , 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))")
endOutput:
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 primitives를 사용하여 실행하기
이제 트랜스파일된 회로를 shots 지정된 조건으로 작업 Sampler (job) 형태로 백엔드에 제출할 수 있습니다.
shots = 1024
job_list = [run_sampler_job(service, backend, tqc, shots) for tqc in tqc_list]Output:
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)
endOutput:
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 함수는 작업이 완료될 때까지 실행이 차단된다는 점에 유의하십시오.
all_samples = [get_sampler_job_results(job, service) for job in job_list]Output:
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단계: 후처리를 수행하고 원하는 기존 형식으로 결과를 반환합니다
양자 하드웨어에서 얻은 비트스트링 샘플을 바탕으로, 모든 샷에 걸쳐 의 평균값을 구하여 사이트당 자화량(단일 큐비트의 기대값)을 계산합니다. 여기서 는 큐비트 에 대해 측정된 비트입니다. 그런 다음 큐비트와 트로터 단계에 따른 자화량을 히트맵으로 그래프에 표시하고, 정확한 고전적 시뮬레이션, 노이즈 없는 텐서 네트워크 시뮬레이션, 하드웨어 실행이라는 세 가지 방법을 나란히 비교합니다.
# 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]Output:
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축)와 트로터 단계(y축)에 따른 사이트 자화 를 보여줍니다. 에서 를 적용했을 때, 총 진화 시간은 이며, 이 시간은 초기 반강자성 패턴이 아직 소멸되지 않을 만큼 충분히 짧아서 세 가지 방법 모두에서 강하게 교대하는 패턴이 나타납니다. 이제 고전적 방법과 텐서 네트워크의 결과가 매우 잘 일치하며, 이는 이 단계 크기에서 트로터 오차가 작음을 확인해 줍니다. 하드웨어 결과는 대체로 다른 두 결과와 유사한 양상을 보이지만, 일부 큐비트는 다른 큐비트보다 시뮬레이션 결과와 더 큰 차이를 보이는데, 이는 백엔드 전반에 걸친 큐비트 품질의 편차를 반영한 것이다. 회로 깊이가 증가함에 따라, 이러한 편차는 후속 트로터 단계에서 점점 더 커집니다.
# 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
)Output:
대규모 하드웨어 예시
단일 워크플로우 내의 1~4단계
이제 우리는 위의 네 단계를 모두 하나의 워크플로로 통합하며, 이는 정밀한 고전적 시뮬레이션으로는 도달할 수 없는 규모에 이릅니다. 자화 상태를 위치별로 각각 구하는 대신, 반강자성 질서의 단일 스칼라 측정값인 ‘스태거드 자화’를 추적합니다:
합계 식에서 부호가 번갈아 나타나기 때문에 신호를 확인할 수 있습니다. 네엘(Néel) 초기 상태 에서는 모든 항이 를 기여하므로 가 되지만, 단순 평균 는 모든 에 대해 항상 0이 된다. 횡자장이 교대 패턴을 뒤섞기 때문에 는 로 수렴하며, 따라서 스태거드 자화(staggered magnetization)는 시간 진화 과정에서 초기 질서가 얼마나 많이 유지되는지를 알려준다.
또한 이 예제를 통해 트로터(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]Output:
δ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))
endOutput:
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]Output:
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) 솔버를 통해 정확한 해를 구할 수 없는데, 이는 상태 벡터만으로도 개의 진폭이 필요하기 때문이다. 대신, 우리는 동일한 1D 체인을 훨씬 더 정밀한 트로터 단계( , )로 시뮬레이션한 노이즈 없는 텐서 네트워크 시뮬레이션 결과를 기준값으로 사용하는데, 여기서 트로터 오차는 하드웨어에서 실행한 어떤 결과와 비교해도 무시할 수 있을 정도로 작습니다. 이 참조 값 자체도 대략적인 것입니다. 현재 이 값의 주요 오차는 앞서 논의한 채권 차원 절단이며, 이는 각 실행에 대해 절단 정확도로 보고됩니다.
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)Output:
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])
아래 그래프는 세 가지 트로터 단계 크기에 따른 시간 경과에 따른 엇갈린 자화 변화를, 잡음이 없는 텐서 네트워크 기준선(검은색 점선)과 비교하여 보여줍니다.
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
pltOutput:
전반적으로, 가장 거친 트로터 단계 (주황색 점)가 텐서 네트워크 기준치로부터 가장 큰 편차를 보이며, 이는 아마도 트로터 오류의 영향이 크게 작용한 것으로 보인다. 더 세분화된 단계인 (녹색 점)에서는 하드웨어 결과가 기준값과 더 근접하게 일치합니다. 가장 정밀한 단계인 (보라색 점)에서 트로터 오류는 가장 작지만, 보다 일치도가 떨어집니다. 단계 크기를 절반으로 줄이면 각 시간 지점에 두 배의 2-큐비트 게이트가 필요하며, 이로 인한 추가적인 잡음이 트로터 오류의 감소 효과를 상쇄합니다. 따라서 하드웨어에서 트로터화(Trotterized) 회로를 구현할 때 를 선택하는 것은 트로터 오차와 추가 게이트에서 발생하는 노이즈의 누적 사이에서 타협점을 찾는 문제이다.
다음 단계
다음 자료가 도움이 될 수도 있습니다:
-
Qiskit.jl 그리고 GitHub 에 있는 ‘ QiskitIBMRuntime.jl’