크릴로프 양자 대각화
이번 크릴로프 양자 대각선화(KQD) 강의에서는 다음과 같은 질문에 답해드리겠습니다:
- 일반적으로 크릴로프 방식이란 무엇인가요?
- 크릴로프 방식은 왜 작동하며 어떤 조건에서 작동하나요?
- 양자 컴퓨팅은 어떤 역할을 하나요?
계산의 양자 부분은 주로 참고 문헌 [1] 의 연구를 기반으로 합니다.
아래 동영상은 클래식 컴퓨팅의 크릴로프 메서드에 대한 개요와 사용 동기, 그리고 해당 워크스트림에서 양자 컴퓨팅이 어떤 역할을 할 수 있는지 설명합니다. 이어지는 텍스트는 더 자세한 내용을 제공하며 고전적인 방법과 양자 컴퓨터를 사용하여 크릴로프 방법을 구현합니다.
1. 크릴로프 방법 소개
크릴로프 부분 공간 방법은 크릴로프 부분 공간 이라고 불리는 것을 중심으로 구축된 여러 가지 방법을 지칭할 수 있습니다. 이에 대한 전체 검토는 이 강의의 범위를 벗어나지만 참고자료 [2-4 ]를 참고하면 훨씬 더 많은 배경 지식을 얻을 수 있습니다. 여기서는 크릴로프 부분공간이 무엇인지, 고유값 문제를 푸는 데 유용한 방법과 이유는 무엇인지, 마지막으로 양자 컴퓨터에서 어떻게 구현할 수 있는지에 대해 집중적으로 살펴보겠습니다.
정의: 대칭적이고 양의 반정적 행렬 이 주어지면, 차수의 크릴로프 공간 은 행렬 의 상위 거듭제곱을 까지 곱하여 얻은 벡터와 기준 벡터 가 걸쳐 있는 공간입니다.
위의 벡터는 크릴로프 아공간이라고 부르는 공간에 걸쳐 있지만, 직교할 것이라고 생각할 이유는 없습니다. 종종 그램-슈미트 직교 정규 화와 유사한 반복적인 직교 정규화 프로세스를 사용합니다. 여기서는 각각의 새 벡터가 생성될 때 다른 벡터와 직교하도록 만들어지므로 프로세스가 약간 다릅니다. 이러한 맥락에서 이를 아놀디 반복이라고 합니다. 초기 벡터 로 시작하여 다음 벡터 를 생성한 다음 에서 투영을 빼서 이 두 번째 벡터가 첫 번째 벡터에 직교하도록 합니다. 즉
이제 이후
다음 벡터에 대해서도 동일한 작업을 수행하여 앞의 두 벡터와 직교하도록 합니다:
모든 벡터에 대해 이 과정을 반복하면 크릴로프 공간에 대한 완전한 정형 기반이 생깁니다. 직교 벡터는 반드시 전체 공간에 걸쳐야 하므로 여기서 직교화 프로세스를 수행하면 이 0을 반환한다는 점에 유의하세요. 이후의 모든 벡터는 해당 벡터의 배수가 되므로 어떤 벡터가 의 고유 벡터인 경우에도 이 프로세스는 0을 산출합니다.
1.1 간단한 예시: 수작업으로 크릴로프
그 과정을 볼 수 있도록 아주 작은 행렬에서 크릴로프 부분공간 생성의 한 세대를 살펴봅시다. 관심 있는 초기 매트릭스 부터 시작합니다:
이 작은 예제에서는 손으로도 쉽게 고유 벡터와 고유값을 구할 수 있습니다. 여기에 수치화된 솔루션을 보여드립니다.
# One might use linalg.eigh here, but later matrices may not be Hermitian. So we use
# linalg.eig in this lesson.
import numpy as np
A = np.array([[4, -1, 0], [-1, 4, -1], [0, -1, 4]])
eigenvalues, eigenvectors = np.linalg.eig(A)
print("The eigenvalues are ", eigenvalues)
print("The eigenvectors are ", eigenvectors)Output:
The eigenvalues are [2.58578644 4. 5.41421356]
The eigenvectors are [[ 5.00000000e-01 -7.07106781e-01 5.00000000e-01]
[ 7.07106781e-01 1.37464400e-16 -7.07106781e-01]
[ 5.00000000e-01 7.07106781e-01 5.00000000e-01]]
나중에 비교할 수 있도록 여기에 기록합니다:
Krylov 하위 공간인 의 차원을 늘릴 때 이 프로세스가 어떻게 작동하는지(또는 실패하는지) 연구해보고자 합니다. 이를 위해 이 프로세스를 적용해 보겠습니다:
- 무작위로 선택한 벡터 (위와 같이 이미 정규화된 경우 )로 시작하여 전체 벡터 공간의 부분공간을 생성합니다.
- 전체 행렬 을 해당 부분 공간에 투영하고 투영된 행렬의 고유값을 구합니다 .
- 그램-슈미트 직교화와 유사한 프로세스를 사용하여 더 많은 벡터를 생성하고 직교가 되도록 하여 하위 공간의 크기를 늘립니다.
- 을 더 큰 부분 공간에 투영하고 결과 행렬의 고유값( )을 구합니다.
- 고유값이 수렴할 때까지(또는 이 장난감의 경우 원래 행렬의 전체 벡터 공간에 걸친 벡터를 생성할 때까지 ) 이 작업을 반복합니다.
일반적인 크릴로프 방법의 구현에서는 모든 크릴로프 하위 공간에 투영된 행렬의 고유값 문제를 풀지 않아도 됩니다. 원하는 차원의 하위 공간을 구성하고 해당 하위 공간에 행렬을 투영한 다음 투영된 행렬을 대각선화할 수 있습니다. 각 하위 공간 차원에서의 투영 및 대각선화는 수렴을 확인하기 위해서만 수행됩니다.
치수:
임의의 벡터를 선택합니다
아직 정규화되지 않은 경우 정규화합니다.
이제 행렬 을 이 하나의 벡터의 부분 공간에 투영합니다:
이것은 행렬에 벡터가 하나만 포함된 경우 행렬을 크릴로프 부분공간에 투영한 것입니다( ). 이 행렬의 고유값은 사소하게도 4입니다. 이를 의 고유값(이 경우 단 하나)에 대한 0차 추정치라고 생각할 수 있습니다. 비록 잘못된 추정치이기는 하지만 정확한 크기 순서입니다.
치수:
이제 이전 벡터에 연산을 통해 하위 공간에서 다음 벡터를 생성합니다:
이제 직교성을 보장하기 위해 이 벡터를 이전 벡터에 투영한 값을 뺍니다.
아직 정규화되지 않은 경우 정규화합니다. 이 경우 벡터는 이미 정규화되었으므로
이제 행렬 A를 이 두 벡터의 부분 공간에 투영합니다:
이 행렬의 고유값을 결정하는 문제는 여전히 남아 있습니다. 하지만 이 매트릭스는 전체 매트릭스보다 약간 작습니다. 매우 큰 행렬을 포함하는 문제에서는 이 작은 하위 공간으로 작업하는 것이 매우 유리할 수 있습니다.
여전히 좋은 추정치는 아니지만 0차 추정치보다는 낫습니다. 프로세스를 명확히 하기 위해 이 작업을 한 번 더 반복할 예정입니다. 그러나 이렇게 하면 다음 반복에서 3x3 행렬을 대각선으로 만들게 되므로 시간이나 계산 능력을 절약하지 못하므로 이 방법의 요점이 약해집니다.
치수:
이제 이전 벡터에서 A를 연산하여 하위 공간에 다음 벡터를 생성합니다:
이제 직교성을 보장하기 위해 이 벡터를 이전 두 벡터에 투영한 값을 뺍니다.
아직 정규화되지 않은 경우 정규화합니다. 이 경우 벡터는 이미 정규화되었으므로
이제 행렬 을 이 벡터의 하위 공간에 투영합니다:
이제 고유값을 결정합니다:
이 고유값은 원래 행렬 의 고유값과 정확히 일치합니다. 원래 행렬 의 전체 벡터 공간에 걸쳐 있도록 크릴로프 부분공간을 확장했기 때문에 반드시 그래야 합니다.
이 예시에서는 크릴로프 방식이 직접 대각선화보다 특별히 쉬워 보이지 않을 수 있습니다. 실제로 뒷부분에서 살펴보겠지만 크릴로프 방법은 특정 행렬 차원 이상에서만 유리하며, 이는 매우 큰 행렬의 고유값/고유벡터 문제를 해결하는 데 도움을 주기 위한 것입니다.
여기서는 '수작업'으로 작업한 예제만 보여드리지만, 아래 섹션 2에서는 계산 예제를 보여드리겠습니다.
용어 설명
흔히 오해하는 것은 주어진 문제에 대해 하나의 크릴로프 하위 공간만 존재한다는 것입니다. 물론 행렬을 적용할 수 있는 초기 벡터가 많기 때문에 가능한 크릴로프 부분공간도 많습니다. 여기서는 특정 예제에 대해 이미 정의된 특정 크릴로프 하위 공간을 지칭할 때만 " 크릴로프 하위 공간"이라는 표현을 사용합니다. 일반적인 문제 해결 접근 방식에 대해서는 " 크릴로프 하위 공간"을 참조하겠습니다. 마지막으로 '크릴로프 스페이스 '를 참조하는 것이 유효하다는 점을 명확히 합니다. 초기 공간에서 행렬을 부분 공간으로 투영하는 맥락에서 사용되기 때문에 흔히 '크릴로프 부분 공간 '이라고 부릅니다. 이러한 맥락에 따라 여기서는 주로 하위 공간으로 지칭하겠습니다.
이해도 점검
크릴로프 아공간 의 차원을 관심 행렬의 차원 이상으로 확장하는 것이 (가) 유용하지 않은 이유와 (나) 가능한 이유를 설명하십시오.
(a) 벡터를 생성하는 과정에서 정교범준화를 수행하고 있으므로, 이러한 벡터들로 구성된 집합 은 완전 기저를 이룬다. 즉, 이 벡터들의 선형 조합을 사용하여 해당 공간 내의 모든 벡터를 생성할 수 있다.
(b) 직교화 과정은 새로운 벡터의 모든 기존 벡터에 대한 투영을 빼는 것으로 이루어진다. 만약 이전의 모든 벡터들이 전체 벡터 공간을 생성한다면, 전체 부분공간으로의 투영을 뺀 결과는 항상 영벡터가 된다.
동료 연구원이 작은 예제 행렬에 크릴로프 방법을 적용해 보여주는 상황을 가정해 보자. 매트릭스 와 초기 벡터 의 선택에 문제가 있는 건가요?
및
동료가 실수로 초기 벡터로 고유 벡터를 선택했습니다. 초기 벡터에 행렬을 적용하면 단순히 고유값으로 스케일링된 동일한 벡터를 반환합니다. 이렇게 하면 차원이 증가하는 하위 공간이 생성되지 않습니다. 동료에게 다른 초기 벡터를 선택하도록 조언하고 고유 벡터가 아닌지 확인합니다.
주어진 행렬에 크릴로프(Krylov) 방법을 적용하되, 적절한 새로운 초기 벡터를 선택하십시오. 크릴로프 부분공간에 대한 0차 및 1차 근사값을 적어 보십시오.
초기 벡터의 선택에 따라 다양한 답이 나올 수 있습니다. 저희가 선택하겠습니다:
를 얻으려면 을 에 한 번 적용한 다음 을 다음과 같이 직교시킵니다
0차에서 크릴로프 부분공간으로의 투영은 다음과 같습니다
1 차에서 이 크릴로프 부분공간에 투영하는 것은 다음과 같습니다
이 작업은 손으로 할 수도 있지만 넘피를 사용하면 가장 쉽게 할 수 있습니다:
import numpy as np vstar = np.array([[1/np.sqrt(3),1/np.sqrt(3),1/np.sqrt(3)],[-1/np.sqrt(6),np.sqrt(2/3),-1/np.sqrt(6)]] ) A = np.array([[1, 1, 0], [1, 1, 1], [0, 1, 1]]) v = np.array([[1/np.sqrt(3),-1/np.sqrt(6)],[1/np.sqrt(3),np.sqrt(2/3)],[1/np.sqrt(3),-1/np.sqrt(6)]]) proj = vstar@A@v print(proj) eigenvalues, eigenvectors = np.linalg.eig(proj) print("The eigenvalues are ", eigenvalues) print("The eigenvectors are ", eigenvectors)outputs:
[[ 2.33333333 0.47140452] [ 0.47140452 -0.33333333]] The eigenvalues are [ 2.41421356 -0.41421356] The eigenvectors are [[ 0.98559856 -0.16910198] [ 0.16910198 0.98559856]]최소 고유값 추정치는 -0.414 입니다.
1.2 크릴로프 방법의 유형
"크릴로프 아공간 방법"은 대규모 선형 시스템과 고유값 문제를 푸는 데 사용되는 여러 반복 기법 중 하나를 지칭할 수 있습니다. 이들 모두의 공통점은 크릴로프 부분 공간에서 대략적인 솔루션을 구축한다는 것입니다
여기서 은 초기 추측입니다(참고 [5] 참조). 이 하위 공간에서 최적의 근사치를 선택하는 방식이 다르며 수렴 속도, 메모리 사용량, 전체 계산 비용과 같은 요소의 균형을 맞추는 방식이 다릅니다. 이 강의의 초점은 크릴로프 아공간 방법의 맥락에서 양자 컴퓨팅을 활용하는 것입니다. 이러한 방법에 대한 자세한 논의는 이 강의의 범위를 벗어납니다. 아래의 간략한 정의는 맥락을 위한 것이며, 이러한 방법을 더 자세히 조사하기 위한 몇 가지 참고 자료가 포함되어 있습니다.
공액 그라디언트(CG) 방법 : 이 방법은 대칭, 양의 정적 선형 시스템을 푸는 데 사용됩니다 [6]. 각 반복에서 오차의 A-노름을 최소화하므로 이산 타원형 PDE로 인해 발생하는 시스템에 특히 효과적입니다 [7]. 다음 섹션에서는 이 접근법을 사용해 왜 크릴로프 아공간이 선형 시스템의 개선된 솔루션을 탐색하는 데 효과적인 아공간이 될 수 있는지에 대해 설명하겠습니다.
일반화된 최소 잔차(GMRES) 방법 : 일반적인 비대칭 선형 시스템을 풀기 위해 설계되었습니다. 각 반복에서 크릴로프 공간에 대한 잔여 노멀을 최소화하여 강력하지만 대규모 시스템에서는 잠재적으로 메모리 집약적일 수 있습니다 [7].
최소 잔차(MINRES) 방법 : 이 방법은 대칭 무기한 선형 시스템을 푸는 데 사용됩니다. GMRES와 유사하지만 계산 비용을 줄이기 위해 행렬 대칭성을 활용합니다 [8].
주목할 만한 다른 접근법으로는 고유값 문제에 대한 아놀디의 방법과 밀접한 관련이 있는 완전 직교화 방법(FOM), 이중 공액 그라데이션( BiCG ) 방법, 유도 차원 축소(IDR) 방법 등이 있습니다.
1.3 크릴로프 부분공간 방법이 작동하는 이유
여기에서는 크릴로프 아공간 방법이 가파른 하강의 렌즈를 통해 행렬 고유 벡터 근사치를 반복적으로 개선하여 행렬 고유값을 근사화하는 효율적인 방법이라는 것을 설명합니다. 접지 상태에 대한 초기 추측이 주어졌을 때 가장 빠르게 수렴하는 초기 추측을 연속적으로 수정하는 공간은 크릴로프 부분공간이라고 주장할 것입니다. 컨버전스 동작에 대한 엄격한 증명이 부족합니다.
관심 행렬( )이 대칭이고 양정확하다고 가정합니다. 따라서 위의 CG 메서드와 가장 관련이 있습니다. 여기서는 희소성에 대해 가정하지 않으며, 이 반드시 해밀턴주의자이어야 한다고 주장하지도 않습니다(해밀턴주의자라면 그래야 합니다).
일반적으로 다음과 같은 형태의 문제를 해결하고자 합니다
여기서 은 고유값 문제에서와 같이 어떤 상수라고 생각할 수 있습니다. 하지만 현재로서는 문제 진술이 더 일반적입니다.
대략적인 솔루션인 벡터로 시작합니다. 섹션 1.1 의 과 사이에는 유사점이 있지만 여기서는 이를 활용하지 않습니다. 에 오류가 있습니다
또한 잔여
여기서는 자본 을 사용하여 잔여를 크릴로프 하위 공간 의 차원과 구분합니다.
이제 다음과 같은 형식의 수정 단계를 만들고 싶습니다
를 사용하면 근사치가 개선될 것으로 기대합니다. 여기 는 아직 결정되지 않은 벡터입니다. 을 수정 후 오류로 설정합니다. 그러면
진정한 
우리는 매트릭스에 의해 변환될 때 오류가 어떻게 작동하는지 궁금합니다. 따라서 오류의 -규범을 계산해 보겠습니다. 즉
여기서 의 대칭을 사용했고 여기서 는 와 독립적인 상수입니다. 섹션 1.2 에서 언급했듯이 오류의 -규범은 우리가 최소화하기 위해 선택할 수 있는 유일한 양은 아니지만, 좋은 양입니다. 선택한 보정 벡터에 따라 이 수량이 어떻게 달라지는지 확인하고 싶습니다 따라서 함수를 정의하여 다음과 같이 설정합니다
는 -norm에서 측정된 수정값 의 함수인 에 불과합니다. 따라서 이 가능한 한 작도록 을 선택하려고 합니다. 이를 위해 의 그라데이션을 계산합니다. 의 대칭을 사용하여 다음과 같이 계산합니다
기울기는 가장 가파르게 상승하는 방향을 가리키며, 그 반대는 함수가 가장 많이 감소하는 방향, 즉 가장 가파르게 하강하는 방향을 나타냅니다. 초기 추측으로는 , 여기서 이 있습니다 따라서 함수는 잔류 방향에서 가장 많이 감소합니다 따라서 초기 선택은 일부 스칼라 에 대해 벡터 를 추가하는 것이 가장 유리할 것입니다.
다음 단계에서는 다시 벡터 를 선택하고 현재 근사값에 해당 값을 더합니다. 이전과 동일한 인수를 사용하여 일부 스칼라 에 대해 를 선택합니다. 이런 식으로 계속 진행하면 벡터의 반복은 다음과 같습니다
마찬가지로 , 등을 순서대로 추가하여 개선된 견적을 선택할 수 있는 공간을 구축하고자 합니다. 예상 벡터는 다음과 같습니다
이제 다음과 같은 관계를 사용하여
우리는
즉, 정답 에 가장 효율적으로 근사하는 공간은 에서 행렬 을 연속적으로 연산하여 구축한 공간입니다. 크릴로프 부분공간은 가장 가파른 하강 방향의 벡터가 연속적으로 걸쳐 있는 공간입니다.
마지막으로, 저희는 이 접근 방식의 확장성에 대해 수치적으로 주장하지 않았으며, 희소 행렬의 비교 이점에 대해서도 논의하지 않았음을 다시 한 번 강조합니다. 이는 크릴로프 서브스페이스 메서드 사용에 대한 동기를 부여하고 직관적인 의미를 더하기 위한 것입니다. 이제 이러한 메서드의 동작을 수치로 살펴보겠습니다.
이해도 점검
위의 워크플로에서는 오류의 -규범을 최소화하는 방법을 제안했습니다. 접지 상태와 고유값을 구할 때 최소화할 수 있는 다른 수량은 무엇일까요?
오차의 -규범 대신 잔차 벡터를 사용하는 것을 상상할 수 있습니다. 오차 벡터 자체를 고려하는 것이 유용한 경우가 있을 수 있습니다.
2. 고전적 계산에서의 크릴로프 방법
이 섹션에서는 아놀디 반복을 계산적으로 구현하여 고유값 문제를 푸는 데 크릴로프 부분공간을 활용할 수 있도록 합니다. 먼저 이를 소규모 예제에 적용한 다음 관심 행렬의 크기가 커짐에 따라 계산 시간이 어떻게 확장되는지 살펴보겠습니다. 여기서 핵심 아이디어는 크릴로프 공간에 걸친 벡터의 생성이 필요한 총 컴퓨팅 시간에 큰 영향을 미친다는 것입니다. 필요한 메모리는 특정 크릴로프 메서드에 따라 다릅니다. 하지만 메모리 제약으로 인해 기존 크릴로프 메서드의 사용이 제한될 수 있습니다.
2.1 간단한 소규모 예시
크릴로프 부분공간을 만드는 과정에서 부분공간에 있는 벡터를 직교 정규화해야 합니다. 하위 공간 vknown (정규화되지 않은 것으로 가정)에서 기존 벡터와 하위 공간 vnext 에 추가할 후보 벡터를 가져와 vnext 를 vknown 에 직교하고 정규화하는 함수를 정의해 보겠습니다. 크릴로프 부분 공간에서 확립된 모든 벡터에 대해 이 과정을 거쳐 완전한 정규 집합을 보장하는 함수를 추가로 정의해 보겠습니다.
# vknown is some established vector in our subspace. vnext is one we wish to add,
# which must be orthogonal to vknown.
def orthog_pair(vknown, vnext):
vknown = vknown / np.sqrt(vknown.T @ vknown)
diffvec = vknown.T @ vnext * vknown
vnext = vnext - diffvec
return vnext
# v is the candidate vector to be added to our subspace. s is the existing subspace.
def orthoset(v, s):
v = v / np.sqrt(v.T @ v)
temp = v
for i in range(len(s)):
temp = orthog_pair(s[i], temp)
v = temp / np.sqrt(temp.T @ temp)
return v이제 크릴로프 벡터의 공간이 원래 행렬의 전체 공간에 걸쳐 있을 때까지 반복적으로 점점 더 큰 크릴로프 부분공간을 구축하는 함수를 정의해 보겠습니다. 이렇게 하면 크릴로프 부분공간 방법을 사용하여 얻은 고유값이 크릴로프 부분공간 차원에 따라 정확한 값과 얼마나 잘 일치하는지 확인할 수 있습니다. 중요한 것은 krylov_full_build 함수가 크릴로프 벡터, 투영된 해밀토니안, 고유값, 필요한 시간을 반환한다는 점입니다.
# Necessary imports and definitions to track time in microseconds
import time
def time_mus():
return int(time.time() * 1000000)
# This function constructs a Krylov subspace that spans the whole space of the original matrix.
# Input:
# v0 : initial vector
# matrix : original matrix to be diagonalized
# Output:
# ks : Krylov vectors
# Hs : projected Hamiltonians
# eigs : eigenvalues
# k_tot_times : time required for the operation
def krylov_full_build(v0, matrix):
t0 = time_mus()
b = v0 / np.sqrt(v0 @ v0.T)
A = matrix
ks = []
ks.append(b)
Hs = []
eigs = []
Hs.append(b.T @ A @ b)
eigs.append(np.array([b.T @ A @ b]))
k_tot_times = []
for j in range(len(A) - 1):
vec = A @ ks[j].T
ortho = orthoset(vec, ks)
ks.append(ortho)
ksarray = np.array(ks)
Hs.append(ksarray @ A @ ksarray.T)
eigs.append(np.linalg.eig(Hs[j + 1]).eigenvalues)
k_tot_times.append(time_mus() - t0)
# Return the Krylov vectors, the projected Hamiltonians, the eigenvalues,
# and the total time required.
return (ks, Hs, eigs, k_tot_times)아직은 매우 작지만 수작업으로 할 수 있는 것보다 큰 매트릭스에서 이를 테스트해 보겠습니다.
# Define our small test matrix
test_matrix = np.array(
[
[4, -1, 0, 1, 0],
[-1, 4, -1, 2, 1],
[0, -1, 4, 3, 3],
[1, 2, 3, 4, 0],
[0, 1, 3, 0, 4],
]
)
# Give the test matrix and an initial guess as arguments in the function defined above.
# Calculate outputs.
test_ks, test_Hs, test_eigs, text_k_tot_times = krylov_full_build(
np.array([0.5, 0.5, 0, 0.5, 0.5]), test_matrix
)마지막 단계(크릴로프 공간이 원래 행렬의 전체 벡터 공간인 경우)에서 크릴로프 방법의 고유값이 정확한 수치 대각선화의 고유값과 정확히 일치하는지 확인하여 함수를 확인할 수 있습니다:
print(np.linalg.eig(test_matrix).eigenvalues)
print(test_eigs[len(test_matrix) - 1])Output:
[-1.36956923 8.43756009 2.9040308 5.34436028 4.68361806]
[-1.36956923 8.43756009 2.9040308 4.68361806 5.34436028]
성공적이었습니다. 물론 실제로 중요한 것은 크릴로프 아공간 차원에 따라 근사치가 얼마나 좋은지입니다. 우리는 종종 기저 상태 및 기타 최소 고유값을 찾는 데 관심이 있기 때문에(그리고 아래에 설명된 다른 대수적 이유 때문에) 크릴로프 아공간 차원의 함수로서 최저 고유값의 추정치를 살펴 보겠습니다. 즉
def errors(matrix, krylov_eigs):
targ_min = min(np.linalg.eig(matrix).eigenvalues)
err = []
for i in range(len(matrix)):
err.append(min(krylov_eigs[i]) - targ_min)
return errimport matplotlib.pyplot as plt
krylov_error = errors(test_matrix, test_eigs)
plt.plot(krylov_error)
plt.axhline(y=0, color="red", linestyle="--") # Add dashed red line at y=0
plt.xlabel("Order of Krylov subspace") # Add x-axis label
plt.ylabel("Error in minimum eigenvalue") # Add y-axis label
plt.show()Output:
크릴로프 부분공간이 까지 커지면 최소 고유값이 상당히 정확하게 도달하고 다음과 같이 완벽해짐을 알 수 있습니다
2.2 시간 스케일링과 행렬 차원
다음과 같은 방식으로 크릴로프 방식이 정확한 수치 아이겐솔버보다 유리할 수 있음을 확신해 보겠습니다:
- 무작위 행렬 구성(희소 행렬이 아니며, KQD에 이상적인 애플리케이션은 아님)
- NumPy 을 직접 사용하는 방법과 크릴로프 부분공간을 사용하는 두 가지 방법을 사용하여 고유값을 결정합니다.
- 크릴로프 추정치를 수락하기 전에 고유값이 얼마나 정확해야 하는지에 대한 컷오프를 선택합니다.
- 이 두 가지 방법으로 해결하는 데 필요한 벽 시간을 비교하세요.
주의 사항: 아래에서 자세히 설명하겠지만, 크릴로프 양자 대각선화는 행렬 표현이 희소하거나 소수의 통근 파울리 연산자 그룹을 사용하여 작성할 수 있는 연산자에 가장 잘 적용됩니다. 여기서 사용하는 무작위 행렬은 이러한 설명에 맞지 않습니다. 이는 기존 크릴로프 메서드가 유용할 수 있는 규모를 조사하는 데에만 유용합니다. 둘째, 크릴로프 방법을 사용할 때 다양한 크기의 크릴로프 부분공간을 사용하여 고유값을 계산합니다. 접지 상태 고유값에 대해 필요한 정확도를 달성하는 최소 차원 크릴로프 부분공간에 필요한 시간을 보고합니다. 다시 말하지만, 이것은 정확한 해를 사용하여 필요한 차원을 평가하기 때문에 정확한 아이겐솔버가 풀기 어려운 문제를 푸는 것과는 약간 다릅니다.
먼저 무작위 행렬 세트를 생성합니다.
import numpy as np
# Set the random seed
np.random.seed(42)
# how many random matrices will we make
num_matrix = 200
matrices = []
for m in range(1, num_matrix):
matrices.append(np.random.rand(m, m))이제 numpy를 사용하여 각 행렬을 직접 대각선화합니다. 나중에 비교하기 위해 대각선화에 필요한 시간을 계산합니다.
matrix_numpy_times = []
matrix_numpy_eigs = []
for mm in range(num_matrix - 1):
t0 = time_mus()
matrix_numpy_eigs.append(min(np.linalg.eig(matrices[mm]).eigenvalues))
matrix_numpy_times.append(time_mus() - t0)
plt.plot(matrix_numpy_times)
plt.xlabel("Dimension of matrix") # Add x-axis label
plt.ylabel("Time to diagonalize (microsec)") # Add y-axis label
plt.show()Output:
위 이미지에서 125 차원 주변의 비정상적으로 높은 시간은 행렬의 무작위 특성 또는 사용된 클래식 프로세서에서의 구현으로 인한 것일 수 있지만 재현할 수는 없습니다. 코드를 다시 실행하면 다른 비정상 피크가 있는 다른 프로필이 생성됩니다.
이제 각 행렬에 대해 크릴로프 부분공간을 구축하고 단계적으로 고유값을 계산합니다. 각 단계에서 지정된 절대 오차 범위 내에서 가장 낮은 고유값이 얻어졌는지 확인합니다. 지정된 오차 내에서 고유값을 먼저 제공하는 하위 공간은 계산 시간을 기록할 하위 공간입니다. 이 셀을 실행하는 데는 프로세서 속도에 따라 몇 분 정도 걸릴 수 있습니다. 평가를 건너뛰거나 대각선 행렬의 최대 차원을 자유롭게 줄이세요. 미리 계산된 결과를 보는 것으로 충분합니다.
# Choose the absolute error you can tolerate, and make a list for tracking the Krylov subspace size
# at which that error is achieved.
abserr = 0.05
accept_subspace_size = []
# Lists to store total time spent on the Krylov method, and the subset of that time spent on
# diagonalizing the projected matrix.
matrix_krylov_tot_times = []
matrix_krylov_dim = []
# Step through all our random matrices
for mm in range(0, num_matrix - 1):
test_ks, test_Hs, test_eigs, test_k_tot_times = krylov_full_build(
np.ones(len(matrices[mm])), matrices[mm]
)
# We have not yet found a Krylov subspace that produces our minimum eigenvalue to
# within the required error.
found = 0
for j in range(0, len(matrices[mm]) - 1):
# If we still haven't found the desired subspace...
if found == 0:
# ...but if this one satisfies the requirement, then record everything
if (
abs((min(test_eigs[j]) - matrix_numpy_eigs[mm]) / matrix_numpy_eigs[mm])
< abserr
):
accept_subspace_size.append(j)
matrix_krylov_tot_times.append(test_k_tot_times[j])
matrix_krylov_dim.append(mm)
found = 1이 두 가지 방법에서 얻은 시간을 비교하기 위해 그래프로 그려 보겠습니다:
plt.plot(matrix_numpy_times, color="blue")
plt.plot(matrix_krylov_dim, matrix_krylov_tot_times, color="green")
plt.xlabel("Dimension of matrix") # Add x-axis label
plt.ylabel("Time to diagonalize (microsec)") # Add y-axis label
plt.show()Output:
이는 실제 소요 시간이지만, 논의의 편의를 위해 인접한 몇 개의 점/행렬 차원에 대한 평균을 구하여 이러한 곡선을 평활화해 보겠습니다. 이 작업은 아래에서 수행됩니다:
smooth_numpy_times = []
smooth_krylov_times = []
# Choose the number of adjacent points over which to average forward;
# the same will be used backward.
smooth_steps = 10
# We will do this smoothing for all points/matrix dimensions
for i in range(len(matrix_krylov_tot_times)):
# Ensure we don't exceed the boundaries of our lists
start = max(0, i - smooth_steps)
end = min(len(matrix_krylov_tot_times) - 1, i + smooth_steps)
# Dummy variables for accumulating an average over adjacent points. This is done for both Krylov
# and the NumPy calculations.
smooth_count = 0
smooth_numpy_sum = 0
smooth_krylov_sum = 0
for j in range(start, end):
smooth_numpy_sum = smooth_numpy_sum + matrix_numpy_times[j]
smooth_krylov_sum = smooth_krylov_sum + matrix_krylov_tot_times[j]
smooth_count = smooth_count + 1
# Appending the averaged adjacent values to our new smooth lists
smooth_numpy_times.append(smooth_numpy_sum / smooth_count)
smooth_krylov_times.append(smooth_krylov_sum / smooth_count)plt.plot(smooth_numpy_times, color="blue")
plt.plot(smooth_krylov_times, color="green")
plt.xlabel("Dimension of matrix") # Add x-axis label
plt.ylabel("Time to diagonalize (smoothed, microsec)") # Add y-axis label
plt.show()Output:
크릴로프 하위 공간의 구축에 필요한 시간은 처음에는 눔피의 전체 대각선화에 필요한 시간을 초과한다는 점에 유의하세요. 그러나 행렬의 크기가 커질수록 크릴로프 방식이 유리해집니다. 이는 허용 오차를 낮추더라도 마찬가지이지만 행렬 크기가 커질수록 이점이 더 커집니다. 이 부분은 자세히 살펴볼 가치가 있습니다.
수치 대각선화의 시간 복잡도는 (알고리즘에 따라 약간의 차이가 있음)입니다. 벡터의 직교 기저를 생성하는 시간 복잡도도 입니다. 따라서 크릴로프 방법의 장점은 정규 기저를 사용하는 것이 아니라 관심 있는 고유값을 효과적으로 골라내는 특정 정규 기저를 사용하는 것과 관련이 있습니다. 우리는 이미 이 강의의 첫 번째 섹션에서 증명의 스케치에서 이를 살펴봤으며, 이는 크릴로프 메서드의 수렴 보장에 매우 중요합니다.
지금까지의 진행 상황을 살펴보겠습니다:
- 매우 큰 행렬의 경우 크릴로프 부분 공간 방법은 기존의 대각선화 알고리즘보다 필요한 오차 범위 내에서 대략적인 고유값을 더 빠르게 구할 수 있습니다.
- 이렇게 매우 큰 행렬의 경우, 크릴로프 부분공간 생성은 크릴로프 부분공간 방법에서 가장 많은 시간이 소요되는 부분입니다.
- 따라서 크릴로프 서브스페이스를 효율적으로 생성하는 방법은 매우 유용할 것입니다. 바로 이 지점에서 양자 컴퓨터가 등장합니다.
이해도 점검
위의 대각화 시간에 대한 행렬 차원의 평활화 그래프를 참조하십시오.
(a) 이 그래프에 따르면, 크릴로프(Krylov) 방법이 어느 정도의 행렬 차원부터 더 빨라지기 시작했는가?
(b) 크릴로프(Krylov) 방법이 더 빨라지는 그 기준치는 계산의 어떤 측면에서 달라질 수 있는가?
(a) 계산을 다시 수행하면 결과가 달라질 수 있지만, 크릴로프(Krylov) 방법은 차원이 약 80~85가 되면 계산 속도가 빨라집니다.
(b) 가능한 답은 여러 가지가 있다. 중요한 요소로는 우리가 요구하는 정밀도와 대각화 대상 행렬의 희소성이 있습니다.
3. 시간 진화를 통한 크릴로프
지금까지 설명한 모든 작업은 고전적으로 수행할 수 있습니다. 그렇다면 언제 어떻게 양자 컴퓨터를 사용할 수 있을까요? 매우 큰 행렬의 경우, 크릴로프 방식은 긴 계산 시간과 많은 양의 메모리가 필요할 수 있습니다. 에서 의 행렬 연산에 필요한 시간은 최악의 경우 와 같이 확장됩니다. 벡터에 희소 행렬을 곱하는 것(고전적인 크릴로프 유형 솔버의 일반적인 경우)도 과 같은 시간 복잡도 스케일링이 있습니다. 이 작업은 하위 공간에서 원하는 모든 벡터에 대해 수행됩니다. 하위 공간 차원 은 일반적으로 의 상당 부분이 아니며 종종 과 같이 스케일링됩니다. 따라서 모든 벡터를 생성하면 최악의 경우 처럼 스케일링됩니다. 직교화와 같은 다른 단계도 있지만, 이것이 염두에 두어야 할 주요 스케일링입니다.
양자 컴퓨팅을 사용하면 문제의 속성을 변경하여 필요한 시간과 리소스의 규모를 결정할 수 있습니다. 전반적으로 행렬 크기( )에 의존하는 대신 해밀턴을 구성하는 샷 수와 비교대 폴리 항의 수와 같은 것을 볼 수 있습니다. 어떻게 작동하는지 살펴보겠습니다.
3.1 시간에 따른 변화
양자 상태를 시간 진화시키는 연산자는 (특히 양자 컴퓨팅에서는 을 표기에서 빼는 것이 매우 일반적입니다)라는 점을 기억하세요. 이러한 연산자의 지수 함수를 이해하고 심지어 실현하는 한 가지 방법은 테일러 급수 확장을 살펴보는 것입니다. 이 연산은 어떤 초기 벡터 에 작용하여 초기 상태에 적용된 의 거듭제곱이 증가하는 항의 합을 산출합니다. 초기 추측 상태를 시간 진화시켜 크릴로프 하위 공간을 만들 수 있을 것 같습니다!
주의할 점은 실제 양자 컴퓨터에서 시간 진화를 실현하는 것입니다. 해밀턴의 많은 용어는 서로 통용되지 않습니다. 따라서 같은 간단한 지수 연산자는 간단한 회로에 해당하지만, 일반적인 해밀턴 연산자는 그렇지 않습니다. 그리고 지수에는 통근이 아닌 용어가 포함되어 있기 때문에 숫자처럼 단순히 지수를 단순 곱으로 분해할 수 없습니다.
따라서 이것은 사소한 일이 아니지만 양자 컴퓨팅에서 잘 연구된 과정입니다. 양자 컴퓨터에서 트로터라이제이션이라는 프로세스를 사용하여 시간 진화를 수행하는데, 이는 그 자체로 풍부한 주제입니다 [10]. 그러나 매우 높은 수준에서는 시간 진화를 매우 작은 단계(예: 단계의 크기 )로 나누면 용어의 비공변성의 영향을 제한할 수 있습니다.
여기서 .
고전적인 맥락에서 H의 거듭제곱을 직접 사용하여 생성한 차수 r의 크릴로프 부분공간을 "파워 크릴로프 부분공간"이라고 부르겠습니다.
이제 단일 시간 진화 연산자 를 사용하여 유사한 공간을 생성합니다. 이를 "단일 크릴로프 공간" 이라고 부릅니다. 우리가 고전적으로 사용하는 파워 크릴로프 아공간 은 이 단일 연산자가 아니므로 양자 컴퓨터에서 직접 생성할 수 없습니다. 단일 크릴로프 부분공간을 사용하면 파워 크릴로프 부분공간과 유사한 수렴 보장, 즉 초기 상태 가 기하급수적으로 소멸하지 않는 실제 기저 상태와 겹치고 고유값 사이에 충분한 간격이 있는 한 기저 상태 오차가 효율적으로 수렴한다는 것을 보여줄 수 있습니다. 컨버전스에 대한 보다 정확한 논의는 참고 [1] 을 참조하세요.
여기서 의 거듭제곱은 서로 다른 시간 단계가 됩니다( 의 거듭제곱은 시간만큼 앞으로 나아갑니다 ). 총 시간 동안 시간 진화하는 하위 공간의 요소에 이라는 레이블을 붙일 수 있습니다.
해밀턴 H를 단일 크릴로프 부분공간인 에 투영할 수 있습니다. 즉, 기준으로 의 각 행렬 요소를 계산합니다. 이 예상 매트릭스를 이라고 합니다.
3.2 양자 컴퓨터에서 구현하는 방법
의 행렬 요소는 양자 컴퓨터를 사용하여 추정할 수 있는 기대값 으로 주어집니다. 은 서로 다른 큐비트에 대한 폴리 연산자의 합으로 쓸 수 있으며, 모든 폴리 연산자를 동시에 측정할 수 있는 것은 아니라는 점에 유의하세요. 폴리 용어를 통근 용어 그룹으로 분류하고 모든 용어를 한 번에 측정할 수 있습니다. 하지만 모든 용어를 다루기 위해서는 이러한 그룹이 많이 필요할 수 있습니다. 따라서 기간을 분할할 수 있는 별개의 출퇴근 그룹( )의 수가 중요해집니다.
여기서 는 형태의 파울리 벡터이거나, 서로 교환 관계를 이루는 이러한 파울리 벡터들의 집합이다. 를 측정 가능한 연산자들의 이러한 합으로 표현할 수 있다는 점을 고려할 때, 의 행렬 요소에 대한 다음 표현들은 IBM Quantum 기본 추정기를 사용하여 구현할 수 있다.
여기서 은 단일 크릴로프 공간의 벡터이고 은 선택한 시간 단계 의 배수입니다. 양자 컴퓨터에서 각 행렬 요소의 계산은 양자 상태 간의 중첩을 얻을 수 있는 모든 알고리즘으로 수행할 수 있습니다. 이 레슨에서는 하다마드 테스트에 중점을 두겠습니다. 의 차원이 이라고 가정하면, 하위 공간에 투영된 해밀턴은 의 차원을 갖습니다. 이 충분히 작으면(일반적으로 이 고유값 추정치의 수렴을 얻기에 충분합니다), 투영된 해밀토니안 을 고전적으로 쉽게 대각선화할 수 있습니다. 하지만 크릴로프 공간 벡터의 비직교성 때문에 을 직접 대각선으로 만들 수는 없습니다. 겹치는 부분을 측정하고 행렬을 구성해야 합니다
이를 통해 비직교 공간에서 고유값 문제(일반화된 고유값 문제라고도 함)를 해결할 수 있습니다
그런 다음 이 일반화된 고유값 문제의 해를 살펴봄으로써 의 고유값과 고유 상태의 추정치를 구할 수 있습니다. 예를 들어, 접지 상태 에너지의 추정치는 가장 작은 고유값 과 해당 고유 벡터 에서 접지 상태를 구하여 얻을 수 있습니다. 의 계수는 에 걸쳐 있는 여러 벡터의 기여도를 결정합니다.
일반화된 고유값 문제
을 단순히 대각선화할 수 없는 이유는 무엇인가요? 에는 크릴로프 기저의 기하학적 정보(아주 특별한 경우를 제외하고는 모두 직교하지 않음)가 포함되어 있으므로 자체는 전체 해밀턴의 투영을 설명하지 않으므로 고유값은 전체 해밀턴의 고유값과 특별한 관계가 없으며 임의의 값이 될 수 있습니다. 일반화된 고유값 문제를 풀려면 전체 해밀턴을 크릴로프 공간에 투영한 것에 해당하는 근사 고유값과 고유 벡터를 구해야 합니다.
그림은 서로 다른 양자 상태 간의 중첩을 계산하는 데 사용되는 방법인 수정된 하다마드 테스트의 회로 표현을 보여줍니다. 각 행렬 요소 에 대해 상태 , 사이의 Hadamard 테스트가 수행됩니다. 이는 그림에서 행렬 요소의 색 구성표와 해당 , 연산으로 강조 표시되어 있습니다. 따라서 투영된 해밀토니안 의 모든 행렬 요소를 계산하려면 크릴로프 공간 벡터의 가능한 모든 조합에 대한 일련의 하다마드 테스트가 필요합니다. 하다마르 테스트 회로의 상단 와이어는 X 또는 Y 기준으로 측정되는 안실라 큐비트이며, 그 기대값에 따라 상태 간 중첩 값이 결정됩니다. 아래쪽 와이어는 시스템 해밀턴의 모든 큐비트를 나타냅니다. 연산은 안실라 큐비트의 상태에 의해 제어되는 상태의 시스템 큐비트를 준비하고( )와 마찬가지로 연산은 시스템 해밀턴 의 파울리 분해를 나타냅니다. 이를 양자 컴퓨터에서 구현하는 방법은 아래에 자세히 설명되어 있습니다.
4. 양자 컴퓨터에서의 크릴로프 양자 대각화
이제 실제 양자 컴퓨터에서 크릴로프 양자 대각선화를 구현해 보겠습니다. 몇 가지 유용한 패키지를 가져오는 것부터 시작하겠습니다.
import numpy as np
import scipy as sp
import matplotlib.pylab as plt
from typing import Union, List
import warnings
from qiskit.quantum_info import SparsePauliOp, Pauli
from qiskit.circuit import Parameter
from qiskit import QuantumCircuit, QuantumRegister
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.synthesis import LieTrotter
# from qiskit.providers.fake_provider import Fake20QV1
from qiskit_ibm_runtime import QiskitRuntimeService, EstimatorV2 as Estimator, Batch
import itertools as it
warnings.filterwarnings("ignore")방금 설명한 일반화된 고유값 문제를 해결하기 위해 아래 함수를 정의합니다.
def solve_regularized_gen_eig(
h: np.ndarray,
s: np.ndarray,
threshold: float,
k: int = 1,
return_dimn: bool = False,
) -> Union[float, List[float]]:
"""
Method for solving the generalized eigenvalue problem with regularization
Args:
h (numpy.ndarray):
The effective representation of the matrix in our Krylov subspace
s (numpy.ndarray):
The matrix of overlaps between vectors of our Krylov subspace
threshold (float):
Cut-off value for the eigenvalue of s
k (int):
Number of eigenvalues to return
return_dimn (bool):
Whether to return the size of the regularized subspace
Returns:
lowest k-eigenvalue(s) that are the solution of the regularized generalized eigenvalue problem
"""
s_vals, s_vecs = sp.linalg.eigh(s)
s_vecs = s_vecs.T
good_vecs = np.array([vec for val, vec in zip(s_vals, s_vecs) if val > threshold])
h_reg = good_vecs.conj() @ h @ good_vecs.T
s_reg = good_vecs.conj() @ s @ good_vecs.T
if k == 1:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][0], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][0]
else:
if return_dimn:
return sp.linalg.eigh(h_reg, s_reg)[0][:k], len(good_vecs)
else:
return sp.linalg.eigh(h_reg, s_reg)[0][:k]적어도 초기 벤치마킹에서는 컨버전스 동작을 확인하기 위해 정확한 고전적인 솔루션을 아는 것이 유용합니다. 아래 함수는 해밀턴과 큐비트 수를 인수로 사용하여 해밀턴의 기저 상태 에너지를 계산합니다.
def single_particle_gs(H_op, n_qubits):
"""
Find the ground state of the single particle(excitation) sector
"""
H_x = []
for p, coeff in H_op.to_list():
H_x.append(set([i for i, v in enumerate(Pauli(p).x) if v]))
H_z = []
for p, coeff in H_op.to_list():
H_z.append(set([i for i, v in enumerate(Pauli(p).z) if v]))
H_c = H_op.coeffs
print("n_sys_qubits", n_qubits)
n_exc = 1
sub_dimn = int(sp.special.comb(n_qubits + 1, n_exc))
print("n_exc", n_exc, ", subspace dimension", sub_dimn)
few_particle_H = np.zeros((sub_dimn, sub_dimn), dtype=complex)
sparse_vecs = [
set(vec) for vec in it.combinations(range(n_qubits + 1), r=n_exc)
] # list all of the possible sets of n_exc indices of 1s in n_exc-particle states
m = 0
for i, i_set in enumerate(sparse_vecs):
for j, j_set in enumerate(sparse_vecs):
m += 1
if len(i_set.symmetric_difference(j_set)) <= 2:
for p_x, p_z, coeff in zip(H_x, H_z, H_c):
if i_set.symmetric_difference(j_set) == p_x:
sgn = ((-1j) ** len(p_x.intersection(p_z))) * (
(-1) ** len(i_set.intersection(p_z))
)
else:
sgn = 0
few_particle_H[i, j] += sgn * coeff
gs_en = min(np.linalg.eigvalsh(few_particle_H))
print("single particle ground state energy: ", gs_en)
return gs_en4.1 1단계: 문제를 양자 회로 및 연산자로 매핑하기
이제 해밀턴을 정의하겠습니다. 위의 함수는 해밀턴을 인수로 받아 기저 상태만 반환한다는 점에서 위의 함수와 다르며, 고전적인 방식으로 동작합니다. 여기서 정의하는 해밀턴은 모든 에너지 고유 상태의 에너지 레벨을 결정하며, 이 해밀턴은 파울리 연산자를 사용하여 구성하고 양자 컴퓨터에서 구현할 수 있습니다.
공간에서 어떤 방향이든 가질 수 있는 스핀의 사슬에 해당하는 해밀턴을 '하이젠베르크 사슬'이라고 합니다. 스핀은 가장 가까운 이웃( 및 스핀)의 영향을 받을 수 있지만 더 먼 이웃의 영향을 받지는 않는다고 가정합니다. 또한 스핀이 서로 다른 축을 가리킬 때 스핀 간의 상호작용이 달라질 가능성도 허용합니다. 이러한 비대칭성은 예를 들어 스핀이 내장된 결정 격자의 구조로 인해 때때로 발생합니다.
# Define problem Hamiltonian.
n_qubits = 10
# coupling strength for XX, YY, and ZZ interactions
JX = 1
JY = 3
JZ = 2
# Define the Hamiltonian:
H_int = [["I"] * n_qubits for _ in range(3 * (n_qubits - 1))]
for i in range(n_qubits - 1):
H_int[i][i] = "Z"
H_int[i][i + 1] = "Z"
for i in range(n_qubits - 1):
H_int[n_qubits - 1 + i][i] = "X"
H_int[n_qubits - 1 + i][i + 1] = "X"
for i in range(n_qubits - 1):
H_int[2 * (n_qubits - 1) + i][i] = "Y"
H_int[2 * (n_qubits - 1) + i][i + 1] = "Y"
H_int = ["".join(term) for term in H_int]
H_tot = [
(term, JZ)
if term.count("Z") == 2
else (term, JY)
if term.count("Y") == 2
else (term, JX)
for term in H_int
]
# Get operator
H_op = SparsePauliOp.from_list(H_tot)
print(H_tot)Output:
[('ZZIIIIIIII', 2), ('IZZIIIIIII', 2), ('IIZZIIIIII', 2), ('IIIZZIIIII', 2), ('IIIIZZIIII', 2), ('IIIIIZZIII', 2), ('IIIIIIZZII', 2), ('IIIIIIIZZI', 2), ('IIIIIIIIZZ', 2), ('XXIIIIIIII', 1), ('IXXIIIIIII', 1), ('IIXXIIIIII', 1), ('IIIXXIIIII', 1), ('IIIIXXIIII', 1), ('IIIIIXXIII', 1), ('IIIIIIXXII', 1), ('IIIIIIIXXI', 1), ('IIIIIIIIXX', 1), ('YYIIIIIIII', 3), ('IYYIIIIIII', 3), ('IIYYIIIIII', 3), ('IIIYYIIIII', 3), ('IIIIYYIIII', 3), ('IIIIIYYIII', 3), ('IIIIIIYYII', 3), ('IIIIIIIYYI', 3), ('IIIIIIIIYY', 3)]
아래 코드는 해밀턴을 단일 입자 상태로 제한하고 스펙트럼 규범을 사용하여 시간 단계에 적합한 크기를 설정합니다 . 시간 단계 dt 의 값을 휴리스틱하게 선택합니다(해밀턴 노멀의 상한을 기준으로). 참고 [9]에 따르면 충분히 작은 타임스텝은 이며, 이 값을 과대평가하면 고에너지 상태의 기여도가 크릴로프 공간의 최적 상태까지 손상시킬 수 있으므로 어느 시점까지는 과대평가하는 것보다 과소평가하는 것이 바람직합니다. 반면에 을 너무 작게 선택하면 크릴로프 기저 벡터가 시간 간격마다 덜 달라지기 때문에 크릴로프 아공간 컨디셔닝이 악화됩니다.
# Get Hamiltonian restricted to single-particle states
single_particle_H = np.zeros((n_qubits, n_qubits))
for i in range(n_qubits):
for j in range(i + 1):
for p, coeff in H_op.to_list():
p_x = Pauli(p).x
p_z = Pauli(p).z
if all(p_x[k] == ((i == k) + (j == k)) % 2 for k in range(n_qubits)):
sgn = ((-1j) ** sum(p_z[k] and p_x[k] for k in range(n_qubits))) * (
(-1) ** p_z[i]
)
else:
sgn = 0
single_particle_H[i, j] += sgn * coeff
for i in range(n_qubits):
for j in range(i + 1, n_qubits):
single_particle_H[i, j] = np.conj(single_particle_H[j, i])
# Set dt according to spectral norm
dt = np.pi / np.linalg.norm(single_particle_H, ord=2)
dtOutput:
np.float64(0.17453292519943295)
시간 진화에서 사용할 트로터 단계의 수를 지정합니다. 또한 최대 크릴로프 차원을 4로 지정합니다. 이 크릴로프 차원은 실제적인 응용을 위해 충분히 크지 않습니다. 그러나 이 예시에는 충분합니다. 또한, 더 작은 차원에서도 수렴 여부를 확인할 것입니다. 후속 강의에서 해밀토니안을 더 큰 부분공간으로 확장하고 투영하는 방법을 탐구할 것입니다.
# Set parameters for quantum Krylov algorithm
krylov_dim = 4 # size of krylov subspace
num_trotter_steps = 4
dt_circ = dt / num_trotter_steps상태 준비
기준 상태와 일부 겹치는 참조 상태( )를 선택합니다. 이 해밀턴의 경우, 중간 큐비트에 여자가 있는 a 상태( )를 참조 상태로 사용합니다.
qc_state_prep = QuantumCircuit(n_qubits)
qc_state_prep.x(int(n_qubits / 2) + 1)
qc_state_prep.draw("mpl", scale=0.5)Output:
시간에 따른 변화
주어진 해밀턴에 의해 생성된 시간 진화 연산자( )를 Lie-Trotter 근사를 통해 구현할 수 있습니다. 간단하게 하기 위해 시간 진화 회로에 내장된 PauliEvolutionGate 을 사용합니다. 이에 대한 일반적인 구문은 다음과 같습니다.
t = Parameter("t")
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=t, synthesis=LieTrotter(reps=num_trotter_steps)
)
qr = QuantumRegister(n_qubits)
qc_evol = QuantumCircuit(qr)
qc_evol.append(evol_gate, qargs=qr)Output:
<qiskit.circuit.instructionset.InstructionSet at 0x7ccaa4664250>
아래 버전은 Hadamard 테스트에서 사용하되, 시간을 앞당겨 .
아다마르 검정
하다마드 테스트를 사용하여 및 그램 행렬 의 행렬 요소를 모두 계산하고자 한다는 점을 기억하세요. 먼저 의 구성에 초점을 맞추어 이러한 맥락에서 이것이 어떻게 작동하는지 살펴 보겠습니다. 전체 프로세스는 아래에 그래픽으로 설명되어 있습니다. 색상이 지정된 상태 준비 블록( )의 레이어는 하위 공간에서 및 의 모든 조합에 대해 이 프로세스가 수행됨을 상기시켜 줍니다.
표시된 단계의 시스템 상태는 다음과 같습니다:
여기서 은 해밀턴의 분해에서 파울리 항입니다(여러 개의 통근 파울리 항의 선형 조합은 단일 항이 아니므로 불가능합니다. 나중에 보여줄 다른 구조를 사용하여 그룹화가 가능합니다) , 은 단일 크릴로프 공간의 , 벡터를 준비하는 제어 연산이며 을 사용합니다. 이 회로에 와 의 측정값을 적용하면 필요한 행렬 요소의 실수 부분과 허수 부분이 각각 계산됩니다.
위의 4단계부터 0번째 큐비트에 하다마드 게이트 를 적용합니다.
그런 다음 또는 을 측정합니다.
. 마찬가지로 을 측정하면
앞서 설정한 시간 진화에 이러한 단계를 추가하면 다음과 같이 작성할 수 있습니다.
## Create the time-evo op circuit
evol_gate = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
## Create the time-evo op dagger circuit
evol_gate_d = PauliEvolutionGate(
H_op, time=dt, synthesis=LieTrotter(reps=num_trotter_steps)
)
evol_gate_d = evol_gate_d.inverse()
# Put pieces together
qc_reg = QuantumRegister(n_qubits)
qc_temp = QuantumCircuit(qc_reg)
qc_temp.compose(qc_state_prep, inplace=True)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate, qargs=qc_reg)
for _ in range(num_trotter_steps):
qc_temp.append(evol_gate_d, qargs=qc_reg)
qc_temp.compose(qc_state_prep.inverse(), inplace=True)
# Create controlled version of the circuit
controlled_U = qc_temp.to_gate().control(1)
# Create hadamard test circuit for real part
qr = QuantumRegister(n_qubits + 1)
qc_real = QuantumCircuit(qr)
qc_real.h(0)
qc_real.append(controlled_U, list(range(n_qubits + 1)))
qc_real.h(0)
print("Circuit for calculating the real part of the overlap in S via Hadamard test")
qc_real.draw("mpl", fold=-1, scale=0.5)Output:
Circuit for calculating the real part of the overlap in S via Hadamard test
트로터 회로의 깊이에 대해서는 이미 경고한 바 있습니다. 이러한 조건에서 하다마드 테스트를 수행하면 특히 네이티브 게이트로 분해하면 훨씬 더 심층적인 회로를 얻을 수 있습니다. 디바이스의 토폴로지를 고려하면 이 수치는 훨씬 더 늘어납니다. 따라서 양자 컴퓨터에서 시간을 사용하기 전에 회로의 2큐비트 깊이를 확인하는 것이 좋습니다.
print(
"Number of layers of 2Q operations",
qc_real.decompose(reps=2).depth(lambda x: x[0].num_qubits == 2),
)Output:
Number of layers of 2Q operations 14401
이 정도 깊이의 회로는 최신 양자 컴퓨터에서 사용 가능한 결과를 반환할 수 없습니다. 및 을 구축하려면 더 나은 방법이 필요합니다. 이것이 바로 아래에 소개된 효율적인 하다마드 테스트의 이유입니다.
4. 2단계 2. 대상 하드웨어에 맞게 회로 및 연산자를 최적화합니다
효율적인 하다마르 검정
몇 가지 근사치를 도입하고 해밀턴 모델에 대한 몇 가지 가정에 의존하여 얻은 Hadamard 테스트에 대한 심층 회로를 최적화할 수 있습니다. 예를 들어 다음과 같은 하다마드 테스트를 위한 회로를 생각해 보세요:
해밀턴의 에서 의 고유값인 을 고전적으로 계산할 수 있다고 가정합니다. 이는 해밀턴이 U(1) 대칭을 유지할 때 충족됩니다. 이것은 강력한 가정처럼 보일 수 있지만, 해밀턴의 작용에 영향을 받지 않는 진공 상태(이 경우 상태에 매핑됨)가 있다고 가정하는 것이 안전한 경우가 많이 있습니다. 예를 들어 안정 분자(전자의 수가 보존된 상태)를 설명하는 화학 해밀턴의 경우에 해당합니다. 예를 들어, 게이트 가 원하는 기준 상태 를 준비한다고 가정할 때, 화학을 위한 HF 상태 는 단일 큐비트 NOT의 산물이므로 제어- 는 CNOT의 산물일 뿐입니다. 그런 다음 위의 회로는 측정 전에 다음 상태를 구현합니다:
에서 2단계부터 3단계까지 고전적인 시뮬레이션 가능한 위상 이동( )을 사용했습니다. 따라서 기대값은 다음과 같습니다
이러한 가정을 사용하여 더 적은 수의 제어 연산으로 관심 연산자의 기대값을 작성할 수 있었습니다. 실제로는 제어된 상태 준비( )만 구현하면 되고 시간 진화는 구현하지 않아도 됩니다. 위와 같이 계산을 재구성하면 결과 회로의 깊이를 크게 줄일 수 있습니다.
보너스로, 이제 폴리 연산자가 중간에 제어 게이트가 아닌 회로의 끝에서 측정값으로 나타나므로 위에 주어진 분해 에서처럼 다른 통근 폴리 연산자와 함께 측정할 수 있다는 점에 유의하세요.
Trotter 분해로 시간 진화 연산자 분해
시간 진화 연산자를 정확히 구현하는 대신, 트로터 분해를 사용하여 그 근사값을 구현할 수 있다. 특정 순서의 트로터 분해를 여러 번 반복하면 근사 과정에서 발생하는 오차를 더욱 줄일 수 있다. 다음에서는 고려 중인 해밀토니안의 상호작용 그래프(가장 가까운 이웃 상호작용만 해당)에 대해 가장 효율적인 방식으로 Trotter 구현을 직접 구축합니다. 실제 구현에서는 펄리 회전 , , 을 결합 강도 및 과 매개변수화된 각도 로 삽입합니다. 이는 의 근사 구현에 해당합니다. 펄리 회전의 정의와 구현하려는 시간 진화의 차이로 인해, 매개변수를 사용하여 의 시간 진화를 달성해야 합니다. 또한, 트로터 단계의 홀수 횟수 반복에 대해 연산 순서를 반전시킵니다. 이는 기능적으로 동등하지만 인접한 연산을 단일 유니터리로 합성할 수 있게 합니다. 이는 일반적인 PauliEvolutionGate() 기능을 사용하여 얻는 것보다 훨씬 얕은 회로를 제공합니다.
t = Parameter("t")
# Create instruction for rotation about XX+YY-ZZ:
Rxyz_circ = QuantumCircuit(2)
Rxyz_circ.rxx(2 * JX * t, 0, 1)
Rxyz_circ.ryy(2 * JY * t, 0, 1)
Rxyz_circ.rzz(2 * JZ * t, 0, 1)
Rxyz_instr = Rxyz_circ.to_instruction(label="R J_x XX + J_y YY + J_z ZZ")
interaction_list = [
[[i, i + 1] for i in range(0, n_qubits - 1, 2)],
[[i, i + 1] for i in range(1, n_qubits - 1, 2)],
] # linear chain
qr = QuantumRegister(n_qubits)
trotter_step_circ = QuantumCircuit(qr)
for i, color in enumerate(interaction_list):
for interaction in color:
trotter_step_circ.append(Rxyz_instr, interaction)
if i < len(interaction_list) - 1:
trotter_step_circ.barrier()
reverse_trotter_step_circ = trotter_step_circ.reverse_ops()
qc_evol = QuantumCircuit(qr)
for step in range(num_trotter_steps):
if step % 2 == 0:
qc_evol = qc_evol.compose(trotter_step_circ)
else:
qc_evol = qc_evol.compose(reverse_trotter_step_circ)
qc_evol.decompose().draw("mpl", fold=-1, scale=0.5)Output:
이 효율적인 하다마드 테스트를 위해 초기 상태를 다시 준비합니다.
control = 0
excitation = int(n_qubits / 2) + 1
controlled_state_prep = QuantumCircuit(n_qubits + 1)
controlled_state_prep.cx(control, excitation)
controlled_state_prep.draw("mpl", fold=-1, scale=0.5)Output:
및 의 행렬 원소를 하다마르 검사를 통해 계산하기 위한 템플릿 회로
하다마드 테스트에 사용된 회로의 유일한 차이점은 시간 진화 연산자와 측정된 관측값의 위상입니다. 따라서 시간 진화 연산자에 따라 달라지는 게이트의 자리 표시자를 사용하여 하다마드 테스트의 일반 회로를 나타내는 템플릿 회로를 준비할 수 있습니다.
# Parameters for the template circuits
parameters = []
for idx in range(1, krylov_dim):
parameters.append(dt_circ * (idx))# Create modified hadamard test circuit
qr = QuantumRegister(n_qubits + 1)
qc = QuantumCircuit(qr)
qc.h(0)
qc.compose(controlled_state_prep, list(range(n_qubits + 1)), inplace=True)
qc.barrier()
qc.compose(qc_evol, list(range(1, n_qubits + 1)), inplace=True)
qc.barrier()
qc.x(0)
qc.compose(controlled_state_prep.inverse(), list(range(n_qubits + 1)), inplace=True)
qc.x(0)
qc.decompose().draw("mpl", fold=-1)Output:
print(
"The optimized circuit has 2Q gates depth: ",
qc.decompose().decompose().depth(lambda x: x[0].num_qubits == 2),
)Output:
The optimized circuit has 2Q gates depth: 50
이 깊이는 기존 하다마드 테스트에 비해 크게 줄어든 것입니다. 이 깊이는 최신 양자 컴퓨터에서 관리할 수 있지만 여전히 상당히 높습니다. 유용한 결과를 얻으려면 최첨단 오류 완화 기능을 사용해야 합니다.
양자 크릴로프 계산을 실행할 백엔드를 선택하면 해당 양자 컴퓨터에서 실행하기 위해 회로를 트랜스파일할 수 있습니다.
# Use the least-busy backend or specify a quantum computer using the syntax commented out below.
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
# Or you may choose a specify backend and channel if necessary for your workflow.
# service = QiskitRuntimeService(channel="ibm_quantum_platform")
# backend = service.backend("ibm_fez")이제 회로와 운영자를 투명하게 공개합니다.
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
target = backend.target
basis_gates = list(target.operation_names)
pm = generate_preset_pass_manager(
optimization_level=3, backend=backend, basis_gates=basis_gates
)
qc_trans = pm.run(qc)print(qc_trans.depth(lambda x: x[0].num_qubits == 2))
print(qc_trans.count_ops())
qc_trans.draw("mpl", fold=-1, idle_wires=False, scale=0.5)Output:
36
OrderedDict([('rz', 410), ('sx', 361), ('cz', 156), ('x', 18), ('barrier', 6)])
최적화 후에는 트랜스파일된 2쿼비트 깊이가 더욱 줄어듭니다.
4.3 3단계. IBM Quantum 기본 유형을 사용하여 실행
이제 Estimator로 실행할 PUB를 생성합니다.
# Define observables to measure for S
observable_S_real = "I" * (n_qubits) + "X"
observable_S_imag = "I" * (n_qubits) + "Y"
observable_op_real = SparsePauliOp(
observable_S_real
) # define a sparse pauli operator for the observable
observable_op_imag = SparsePauliOp(observable_S_imag)
layout = qc_trans.layout # get layout of transpiled circuit
observable_op_real = observable_op_real.apply_layout(
layout
) # apply physical layout to the observable
observable_op_imag = observable_op_imag.apply_layout(layout)
observable_S_real = (
observable_op_real.paulis.to_labels()
) # get the label of the physical observable
observable_S_imag = observable_op_imag.paulis.to_labels()
observables_S = [[observable_S_real], [observable_S_imag]]
# Define observables to measure for H
# Hamiltonian terms to measure
observable_list = []
for pauli, coeff in zip(H_op.paulis, H_op.coeffs):
# print(pauli)
observable_H_real = pauli[::-1].to_label() + "X"
observable_H_imag = pauli[::-1].to_label() + "Y"
observable_list.append([observable_H_real])
observable_list.append([observable_H_imag])
layout = qc_trans.layout
observable_trans_list = []
for observable in observable_list:
observable_op = SparsePauliOp(observable)
observable_op = observable_op.apply_layout(layout)
observable_trans_list.append([observable_op.paulis.to_labels()])
observables_H = observable_trans_list
# Define a sweep over parameter values
params = np.vstack(parameters).T
# Estimate the expectation value for all combinations of
# observables and parameter values, where the pub result will have
# shape (# observables, # parameter values).
pub = (qc_trans, observables_S + observables_H, params)의 회로는 고전적으로 계산할 수 있습니다. 양자 컴퓨터를 사용하여 사례로 넘어가기 전에 이 작업을 수행합니다.
from qiskit.quantum_info import StabilizerState, Pauli
qc_cliff = qc.assign_parameters({t: 0})
# Get expectation values from experiment
S_expval_real = StabilizerState(qc_cliff).expectation_value(
Pauli("I" * (n_qubits) + "X")
)
S_expval_imag = StabilizerState(qc_cliff).expectation_value(
Pauli("I" * (n_qubits) + "Y")
)
# Get expectation values
S_expval = S_expval_real + 1j * S_expval_imag
H_expval = 0
for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):
# Get expectation values from experiment
expval_real = StabilizerState(qc_cliff).expectation_value(
Pauli(pauli[::-1].to_label() + "X")
)
expval_imag = StabilizerState(qc_cliff).expectation_value(
Pauli(pauli[::-1].to_label() + "Y")
)
expval = expval_real + 1j * expval_imag
# Fill-in matrix elements
H_expval += coeff * expval
print(H_expval)Output:
(10+0j)
효율적인 하다마드 테스트를 통해 게이트 깊이를 크게 줄일 수 있었지만, 여전히 최첨단 오류 완화가 필요한 수준입니다. 아래에서는 사용 중인 완화 조치의 속성을 지정합니다. 사용되는 모든 방법이 중요하지만, 특히 확률적 오류 증폭(PEA) 에 주목할 필요가 있습니다. 이 강력한 기술에는 엄청난 양의 양자 오버헤드가 발생합니다. 여기서 수행되는 계산은 실제 양자 컴퓨터에서 실행하는 데 20분 이상 걸릴 수 있습니다. 아래 매개변수를 사용하여 정밀도를 높이거나 낮추면 결과적으로 오버헤드가 발생할 수 있습니다. 아래의 기본 설정은 충실도가 높은 결과를 제공합니다.
# Experiment options
num_randomizations = 300
num_randomizations_learning = 20
max_batch_circuits = 20
shots_per_randomization = 100
learning_pair_depths = [0, 4, 24]
noise_factors = [1, 1.3, 1.6]
# Base option formatting
options = {
# Builtin resilience settings for ZNE
"resilience": {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": {"noise_factors": noise_factors},
# TREX noise learning configuration
"measure_noise_learning": {
"num_randomizations": num_randomizations_learning,
"shots_per_randomization": shots_per_randomization,
},
# PEA noise model configuration
"layer_noise_learning": {
"max_layers_to_learn": 10,
"layer_pair_depths": learning_pair_depths,
"shots_per_randomization": shots_per_randomization,
"num_randomizations": num_randomizations_learning,
},
},
# Randomization configuration
"twirling": {
"num_randomizations": num_randomizations,
"shots_per_randomization": shots_per_randomization,
"strategy": "all",
},
# Experimental settings for PEA method
"experimental": {
# # Just in case, disable any further qiskit transpilation not related to twirling / DD
# "skip_transpilation": True,
# Execution configuration
"execution": {
"max_pubs_per_batch_job": max_batch_circuits,
"fast_parametric_update": True,
},
# Error Mitigation configuration
"resilience": {
# ZNE Configuration
"zne": {
"amplifier": "pea",
"return_all_extrapolated": True,
"return_unextrapolated": True,
"extrapolated_noise_factors": [0] + noise_factors,
}
},
},
}마지막으로 Estimator를 사용하여 및 에 대한 회로를 실행합니다.
# This job required 17 minutes of QPU time to run on a Heron r2 processor. This is only an estimate.
# Your execution time may vary.
with Batch(backend=backend) as batch:
# Estimator
estimator = Estimator(mode=batch, options=options)
job = estimator.run([pub], precision=1)4.4 4단계. 결과를 후처리하고 분석한다
양자 컴퓨터에서 얻은 것은 의 개별 행렬 요소와 의 행렬 요소를 구성하는 통근 파울리 그룹입니다. 이 용어들을 결합하여 행렬을 복구해야 일반화된 고유값 문제를 해결할 수 있습니다.
# Store the outputs as 'results'.
results = job.result()[0]유효 해밀토니안과 중첩 행렬 계산
먼저 제어되지 않은 시간 진화 동안 상태에 의해 누적된 위상을 계산합니다
prefactors = [
np.exp(-1j * sum([c for p, c in H_op.to_list() if "Z" in p]) * i * dt)
for i in range(1, krylov_dim)
]회로 실행 결과를 얻으면 데이터를 후처리하여 다음과 같은 행렬 요소를 계산할 수 있습니다
# Assemble S, the overlap matrix of dimension D:
S_first_row = np.zeros(krylov_dim, dtype=complex)
S_first_row[0] = 1 + 0j
# Add in ancilla-only measurements:
for i in range(krylov_dim - 1):
# Get expectation values from experiment
expval_real = results.data.evs[0][0][i] # automatic extrapolated evs if ZNE is used
expval_imag = results.data.evs[1][0][i] # automatic extrapolated evs if ZNE is used
# Get expectation values
expval = expval_real + 1j * expval_imag
S_first_row[i + 1] += prefactors[i] * expval
S_first_row_list = S_first_row.tolist() # for saving purposes
S_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)
# Distribute entries from first row across matrix:
for i, j in it.product(range(krylov_dim), repeat=2):
if i >= j:
S_circ[j, i] = S_first_row[i - j]
else:
S_circ[j, i] = np.conj(S_first_row[j - i])from sympy import Matrix
Matrix(S_circ)Output:
그리고 행렬 요소는
import itertools
# Assemble S, the overlap matrix of dimension D:
H_first_row = np.zeros(krylov_dim, dtype=complex)
H_first_row[0] = H_expval
for obs_idx, (pauli, coeff) in enumerate(zip(H_op.paulis, H_op.coeffs)):
# Add in ancilla-only measurements:
for i in range(krylov_dim - 1):
# Get expectation values from experiment
expval_real = results.data.evs[2 + 2 * obs_idx][0][
i
] # automatic extrapolated evs if ZNE is used
expval_imag = results.data.evs[2 + 2 * obs_idx + 1][0][
i
] # automatic extrapolated evs if ZNE is used
# Get expectation values
expval = expval_real + 1j * expval_imag
H_first_row[i + 1] += prefactors[i] * coeff * expval
H_first_row_list = H_first_row.tolist()
H_eff_circ = np.zeros((krylov_dim, krylov_dim), dtype=complex)
# Distribute entries from first row across matrix:
for i, j in itertools.product(range(krylov_dim), repeat=2):
if i >= j:
H_eff_circ[j, i] = H_first_row[i - j]
else:
H_eff_circ[j, i] = np.conj(H_first_row[j - i])from sympy import Matrix
Matrix(H_eff_circ)Output:
마지막으로 에 대한 일반화된 고유값 문제를 풀 수 있습니다:
를 클릭하고 접지 상태 에너지의 추정치를 얻습니다
gnd_en_circ_est_list = []
for d in range(1, krylov_dim + 1):
# Solve generalized eigenvalue problem
gnd_en_circ_est = solve_regularized_gen_eig(
H_eff_circ[:d, :d], S_circ[:d, :d], threshold=1e-1
)
gnd_en_circ_est_list.append(gnd_en_circ_est)
print("The estimated ground state energy is: ", gnd_en_circ_est)Output:
The estimated ground state energy is: 10.0
The estimated ground state energy is: 5.933953916292923
The estimated ground state energy is: 4.4101773995740645
The estimated ground state energy is: 3.921288588521255
단일 입자 섹터의 경우, 해밀턴 섹터의 접지 상태를 고전적으로 효율적으로 계산할 수 있습니다
gs_en = single_particle_gs(H_op, n_qubits)Output:
n_sys_qubits 10
n_exc 1 , subspace dimension 11
single particle ground state energy: 2.391547869638771
len(H_op)Output:
27
plt.plot(
range(1, krylov_dim + 1),
gnd_en_circ_est_list,
color="blue",
linestyle="-.",
label="KQD estimate",
)
plt.plot(
range(1, krylov_dim + 1),
[gs_en] * krylov_dim,
color="red",
linestyle="-",
label="exact",
)
plt.xticks(range(1, krylov_dim + 1), range(1, krylov_dim + 1))
plt.legend()
plt.xlabel("Krylov space dimension")
plt.ylabel("Energy")
plt.title("Estimating Ground state energy with Krylov Quantum Diagonalization")
plt.show()Output:
5. 논의 및 확장
요약하자면, 참조 상태로 시작한 다음 다양한 기간 동안 진화시켜 단일 크릴로프 하위 공간을 생성합니다. 해밀턴을 해당 하위 공간에 투영합니다. 또한 서브스페이스 벡터의 중첩도 추정합니다. 마지막으로 저차원의 일반화된 고유값 문제를 고전적으로 해결합니다.
크릴로프 기법을 고전적으로 사용할 때와 양자역학적으로 사용할 때의 계산 비용을 결정하는 요소를 비교해 보겠습니다. 모든 단계에서 클래식 접근 방식과 퀀텀 접근 방식 사이에 완벽한 아날로그가 있는 것은 아닙니다. 이 표에는 고려해야 할 다양한 단계의 스케일링이 정리되어 있습니다.
해밀턴 사람들은 일반적으로 (서로 통근하지 않기 때문에) 동시에 측정할 수 없는 용어가 있다는 것을 기억하세요. 해밀턴의 항을 모두 동시에 측정할 수 있는 통근 폴리 연산자 그룹으로 분류하고, 서로 통근하지 않는 항을 모두 설명하기 위해 이러한 그룹이 많이 필요할 수 있습니다. 양자 컴퓨터에서 을 구축하려면 해밀턴의 각 출퇴근 폴리 문자열 그룹에 대해 별도의 측정이 필요하며, 각각에 대해 많은 샷이 필요합니다. 서로 다른 행렬 요소에 대해 이 작업을 수행해야 하며, 이는 서로 다른 시간 진화 계수의 조합에 해당합니다. 이를 줄이는 방법도 있지만, 대략적인 처리 방법에서는 의 요소를 추정해야 하며, 이는 와 같은 스케일입니다. 마지막으로, 투영된 공간에서 일반화된 고유값 문제를 푸는 데는 고전적으로 다음과 같은 시간이 걸립니다
양자 크릴로프 대각선화는 해밀토니안에서 통근하는 파울리 그룹의 수가 상대적으로 적은 경우에 유용할 수 있음을 알 수 있습니다. 이러한 확장 종속성은 크릴로프 방식이 유용할 수 있는 애플리케이션과 그렇지 않을 가능성이 높은 애플리케이션을 제시합니다. 일부 해밀턴은 큐비트에 매핑할 때 복잡도가 높아서, 몇 개의 통근 그룹으로 쉽게 분할할 수 없는 많은 비통근 폴리 문자열을 포함합니다. 예를 들어 양자 화학 문제를 예로 들 수 있습니다. 이러한 복잡성은 단기적으로 양자 컴퓨터가 해결해야 할 두 가지 주요 과제를 제시합니다:
- 의 각 요소를 추정하는 것은 용어 수가 많기 때문에 계산 비용이 많이 듭니다.
- 필요한 트로터 회로는 엄청나게 깊어집니다.
위의 두 가지 사항은 양자 컴퓨터가 내결함성에 도달하면 덜 문제가 되겠지만, 단기적으로는 고려해야 할 사항입니다. 양자 화학보다 "단순한" 매핑을 가진 시스템이라도 해밀턴의 비통근 용어가 너무 많으면 동일한 장애를 경험할 수 있습니다. 크릴로프 방법은 해밀턴을 상대적으로 적은 수의 출퇴근 폴리 그룹으로 분할할 수 있고 를 트로터 회로에서 구현하기 쉬운 경우에 가장 유용합니다. 예를 들어 물리학에서 관심 있는 많은 격자 모델의 경우 이 두 가지 조건이 모두 충족됩니다. KQD는 접지 상태에 대해 알려진 정보가 거의 없는 경우에 특히 유용합니다. 이는 고유한 융합 보장 기능과 부족한 지상 상태 지식으로 인해 대체 방법을 사용할 수 없는 시나리오에서의 적용 가능성에서 비롯됩니다.
KQD는 강력한 도구이지만, 프로토콜의 시간 소모적인 측면, 특히 예상 해밀턴의 각 요소와 크릴로프 상태의 중첩을 추정하는 것은 개선의 여지가 있는 부분입니다. 다른 접근 방식으로는 다음 단원의 주제인 샘플링 기반 방법과 함께 크릴로프 방법을 활용하는 방법이 있습니다.
6. 부록
부록 I: 실시간 진화로부터의 크릴로프 부분공간
단일 크릴로프 공간은 다음과 같이 정의됩니다
를 으로 설정할 수 있습니다. 이 짝수라고 가정한 다음 을 정의합니다. 해밀턴을 위의 크릴로프 공간에 투영하면 크릴로프 공간과 구분할 수 없다는 것을 알 수 있습니다
즉, 모든 시간 진화가 시간 간격만큼 뒤로 이동하는 경우입니다. 구분할 수 없는 이유는 매트릭스 요소가
는 진화 시간의 전반적인 변화에 따라 변하지 않는데, 이는 시간 진화가 해밀턴과 함께 이동하기 때문입니다. 홀수 의 경우 에 대한 분석을 사용할 수 있습니다.
우리는 이 크릴로프 공간 어딘가에 저에너지 상태가 보장되어 있다는 것을 보여주고 싶습니다. 이를 위해 [3] 의 정리 3.1 에서 도출한 다음 결과를 사용합니다:
주장 1: 해밀턴의 스펙트럼 범위(즉, 기저 상태 에너지와 최대 에너지 사이)에 있는 에너지의 경우 함수가 존재합니다....
- 에서 떨어져 있는 의 모든 값에 대해, 즉 기하급수적으로 억제됩니다
- 의 선형 조합은
아래에서 그 증거를 제시하지만, 전체적이고 엄격한 논증을 이해하고 싶지 않다면 건너뛰어도 됩니다. 지금은 위 주장의 의미에 초점을 맞추고자 합니다. 위의 속성 3을 통해 위의 시프트된 크릴로프 공간에 상태가 포함되어 있음을 알 수 있습니다. 이것이 우리의 저에너지 상태입니다. 이유를 알아보려면 에너지 고유 기반에 을 입력하세요:
여기서 은 kth 에너지 고유 상태이고 은 초기 상태에서의 진폭 입니다. 이를 표현하면 은 다음과 같이 됩니다
가 고유 상태 에 작용할 때 를 로 대체할 수 있다는 사실을 이용합니다. 따라서 이 상태의 에너지 오류는
이를 이해하기 쉬운 상한값으로 바꾸기 위해 먼저 분자의 합계를 와 로 구분합니다:
첫 번째 항의 상한은 으로 설정할 수 있습니다,
여기서 첫 번째 단계는 분자의 합이 분모 합의 하위 집합이기 때문에 , 두 번째 단계는 분자의 합이 분모 합의 하위 집합이기 때문에 로 이어집니다. 두 번째 항의 경우, 먼저 분모를 으로 낮추고 : 모든 것을 다시 합치면 다음과 같이 됩니다
남은 내용을 단순화하기 위해 이 모든 에 대해 의 정의에 따라 을 알 수 있습니다. 또한 상위 바운딩 과 상위 바운딩 은 다음과 같습니다
이는 에도 적용되므로 을 목표 오차와 동일하게 설정하면 위의 오차 바인딩은 크릴로프 차원 을 통해 기하급수적으로 수렴합니다. 또한 의 경우 항은 위의 바운드에서 실제로 완전히 사라집니다.
논증을 완료하기 위해 먼저 위는 크릴로프 공간에서 가장 낮은 에너지 상태의 에너지 오차가 아니라 특정 상태 의 에너지 오차라는 점에 주목합니다. 그러나 (라일리-리츠) 변동 원리에 따르면 크릴로프 공간에서 가장 낮은 에너지 상태의 에너지 오차는 크릴로프 공간의 모든 상태의 에너지 오차에 의해 상한이 정해지기 때문에 위는 또한 가장 낮은 에너지 상태의 에너지 오차, 즉 크릴로프 양자 대각선화 알고리즘의 출력에 대한 상한이기도 합니다.
노트북에서 설명한 노이즈와 임계값 절차를 추가로 고려하는 위와 유사한 분석을 수행할 수 있습니다. 이 분석에 대해서는 [2] 및 [4] 를 참조하세요.
부록 II: 청구항 1의 증명
다음은 대부분 [3], 정리 3.1: 를 으로 하고 를 최대 차수의 잔차 다항식(0에서의 값이 1인 다항식)의 공간 으로 하자. 에 대한 해는
다음과 같음
이며 해당 최소값은
양자 크릴로프 공간을 생성하는 실시간 진화가 바로 복소 지수이기 때문에 이를 자연스럽게 복소 지수로 표현할 수 있는 함수로 변환하고자 합니다. 이를 위해 해밀턴의 스펙트럼 범위 내에서 다음과 같은 에너지 변환을 범위의 숫자로 도입하는 것이 편리합니다
여기서 은 이 되는 시간 간격입니다. 이 에서 멀어질수록 과 이 커지는 것을 볼 수 있습니다.
이제 매개변수 a, b, d가 , , d = int( r/2 )로 설정된 다항식 을 사용하여 함수를 정의합니다:
여기서 은 접지 상태 에너지입니다. 을 삽입하면 이 차수 의 삼각 다항식, 즉 에 대한 의 선형 조합이라는 것을 알 수 있습니다. 또한 위의 의 정의에서 및 스펙트럼 범위의 모든 에 대해 우리는 다음과 같습니다
참조 자료:
[1] https://arxiv.org/abs/2407.14431
[2] https://arxiv.org/abs/1811.09025
[3] https://people.math.ethz.ch/~mhg/pub/biksm.pdf
[4] https://academic.oup.com/book/36426
[5] https://en.wikipedia.org/wiki/Krylov\하위 공간
크릴로프 부분공간 방법 [6가지] : 원리와 분석, 요르그 리센, 즈데넥 스트라코스 https://academic.oup.com/book/36426
희소 선형 시스템을 위한 [7가지] 반복적 방법" by Yousef Saad
[8] 최수청, 크리스토퍼 페이지의 "MINRES-QLP: 무기한 또는 특이 대칭 시스템을 위한 크릴로프 아공간 방법", 최수청, 크리스토퍼 페이지, 그리고 마이클 손더스 ( https://epubs.siam.org/doi/10.1137/100787921 )
[9] 에단 N. 에펄리, 린 린, 나카츠카사 유지. "양자 아공간 대각선화 이론". SIAM 행렬 분석 및 응용 저널 43, 1263-1290 (2022).