Skip to main content
IBM Quantum Platform

M3 를 이용한 샘플러 프리미티브의 읽기 오류 완화

사용 예상 시간: Heron r2 프로세서에서 1분 미만(참고: 이는 예상치일 뿐입니다. 런타임은 다를 수 있습니다.)


배경

추정기 프리미티브와 달리 샘플러 프리미티브에는 오류 완화를 위한 기본 지원 기능이 없습니다. 추정기가 지원하는 방법 중 일부는 기대값을 위해 특별히 설계되었으므로 샘플러 프리미티브에는 적용할 수 없습니다. 샘플러 프리미티브에도 적용할 수 있는 매우 효과적인 방법인 판독 오류 완화는 예외입니다.

M3 키스킷 애드온은 읽기 오류 완화를 위한 효율적인 방법을 구현합니다. 이 튜토리얼에서는 M3 키스킷 애드온을 사용하여 샘플러 프리미티브의 판독 오류를 완화하는 방법을 설명합니다.

리드아웃 오류란 무엇인가?

측정 직전의 큐비트 레지스터의 상태는 다음과 같습니다 계산 기준 상태의 중첩으로 설명됩니다, 또는 밀도 행렬로 설명됩니다. 큐비트 레지스터를 클래식 비트 레지스터로 측정한 다음 두 단계로 진행합니다. 먼저 적절한 양자 측정이 수행됩니다. 즉, 큐비트 레지스터의 상태는 의 상태는 다음과 같은 단일 기준 상태에 투영됩니다 11 s와 00 s의 문자열로 특징지어집니다. 두 번째 단계는 이 기본 상태를 특징짓는 비트스트링을 읽고 을 읽고 기존 컴퓨터 메모리에 기록하는 것입니다. 이 단계를 판독이라고 합니다. 두 번째 단계(판독)에서 첫 번째 단계(기준 상태에 대한 투영)보다 더 많은 오류가 발생하는 것으로 나타났습니다. 판독을 위해서는 미시적인 양자 상태를 감지하여 양자 상태를 감지하고 이를 거시적 영역으로 증폭해야 한다는 점을 상기하면 이해가 됩니다. 판독 공진기는 (트랜지스터) 큐비트에 (트랜스몬) 큐비트에 결합되어 매우 작은 주파수 이동을 경험합니다. 마이크로파 펄스 이 공진기에서 튕겨져 나가면서 공진기의 작은 변화를 경험합니다. 그런 다음 반사된 펄스를 증폭하여 분석합니다. 이는 섬세한 프로세스이며 여러 가지 오류가 발생할 수 있습니다.

중요한 점은 양자 측정과 판독 모두 오차가 발생할 수 있지만, 후자의 경우 이 튜토리얼의 초점인 판독 오류라고 하는 주요 오류가 후자에 더 많이 발생한다는 것입니다.

이론적 배경

샘플링된 비트스트링(클래식 메모리에 저장)이 투영된 양자 상태를 특징짓는 비트스트링과 다른 경우 비트 문자열과 다르면 판독 오류가 발생했다고 말합니다. 이러한 오류는 무작위로 발생하며 샘플마다 상관관계가 없는 것으로 관찰됩니다. 판독 오류를 노이즈가 많은 클래식 채널로 모델링하는 것이 유용하다는 것이 입증되었습니다. 즉, 모든 쌍의 비트 문자열 iijj 의 모든 쌍에 대해 jj 의 실제 값을 가 ii 로 잘못 읽힐 확률이 고정되어 있습니다.

더 정확히 말하면, 모든 비트 문자열 쌍 (i,j)(i, j) 에 대해 (조건부) 확률이 있습니다 Mi,j{M}_{i,j} 이 읽혀질 확률은 ii 이며, 실제 값은 다음과 같습니다 j.j. 즉, 다음과 같습니다,

Mi,j=Pr(readout value is itrue value is j) for i,j(0,...,2n1),(1) {M}_{i,j} = \Pr(\text{readout value is } i | \text{true value is } j) \text{ for } i,j \in (0,...,2^n - 1), \tag{1}

여기서 nn 은 판독 레지스터의 비트 수입니다. 구체적으로 설명하기 위해 ii 는 이진 표현이 다음과 같은 10진수라고 가정합니다 이며, 계산 기준 상태에 레이블을 지정하는 비트 문자열이라고 가정합니다. 2n×2n2^n \times 2^n 매트릭스 M{M}할당 매트릭스라고 합니다. 고정된 참값 jj 의 경우 모든 노이즈 결과에 대한 확률을 합산하면 ii11 이 되어야 합니다. 즉

i=02n1Mi,j=1 for all j \sum_{i=0}^{2^n - 1} {M}_{i,j} = 1 \text{ for all } j

(1)을 만족하는 음수 항목이 없는 행렬을 왼쪽 확률 행렬이라고 합니다. 왼쪽 확률 행렬은 각 열이 11 로 합산되기 때문에 열 확률 행렬이라고도 합니다. 각 요소의 대략적인 값 Mi,j{M}_{i,j} 을 실험적으로 결정합니다 각 기저 상태 j|j \rangle 를 반복적으로 준비한 다음 샘플링된 비트 문자열의 발생 빈도를 를 계산합니다.

실험에 반복 샘플링을 통해 출력 비트 문자열에 대한 확률 분포를 추정하는 것이 포함된 경우, M{M} 를 사용하여 분포 수준에서 판독 오류를 완화할 수 있습니다. 첫 번째 단계는 관심 있는 고정 회로를 여러 번 반복하는 것입니다, 샘플링된 비트 문자열의 히스토그램을 만드는 것입니다. 정규화된 히스토그램은 가능한 비트 문자열에 대한 2n2^n 가능한 비트스트링에 대한 측정된 확률 분포이며, p~R2n{\tilde{p}} \in \mathbb{R}^{2^n} 으로 나타냅니다. 비트 문자열 샘플링의 (추정) 확률 p~i{{\tilde{p}}}_iii 은 모든 실제 비트 문자열 jj 에 대한 합과 같으며, 각각 다음과 같이 가중치가 부여됩니다 로 가중치를 부여하여 ii 로 오인될 확률과 같습니다. 행렬 형식의 이 문은 다음과 같습니다

p~=Mp,,(2) {\tilde{p}} = {M} {\vec{p}}, \tag{2},

여기서 p{\vec{p}} 은 실제 분포입니다. 즉, 판독 오류는 다음과 같이 곱하는 효과가 있습니다 비트 문자열에 대한 이상적인 분포 p{\vec{p}} 에 할당 행렬 M{M} 을 곱하여 관측된 분포 p~{\tilde{p}} 를 생성합니다. p~{\tilde{p}}M{M} 을 측정했지만 p{\vec{p}} 에 직접 액세스할 수 없습니다. 원칙적으로 다음과 같이 합니다 회로의 비트 문자열의 실제 분포를 구합니다 p{\vec{p}} 에 대한 방정식 (2)를 수치적으로 풀면 됩니다.

계속 진행하기 전에 이 순진한 접근 방식의 몇 가지 중요한 특징에 주목할 필요가 있습니다.

  • 실제로 방정식 (2)는 M{M} 을 반전시켜서 풀 수 없습니다. 소프트웨어 라이브러리의 선형 대수 소프트웨어 라이브러리의 루틴은 더 안정적이고 정확하며 효율적인 방법을 사용합니다.
  • M{M} 을 추정할 때는 판독 오류만 발생한다고 가정했습니다. 특히 상태 준비 및 양자 측정 오류는 없었을 것으로 추정합니다 또는 적어도 다른 방식으로 완화되었다고 가정합니다. 이것이 좋은 가정이라면 M{M} 은 실제로는 읽기 오류만 나타냅니다. 하지만 M{M} 을 사용하여 비트 문자열에 대한 측정된 분포를 수정할 때는 을 사용할 때는 그러한 가정을 하지 않습니다. 사실, 우리는 흥미로운 회로에 게이트 오류와 같은 노이즈가 발생할 수 있습니다. "실제" 배포 에는 여전히 다른 방법으로 완화되지 않은 오류의 영향이 포함됩니다.

이 방법은 일부 상황에서는 유용하지만 몇 가지 제한 사항이 있습니다.

M{M} 을 추정하는 데 필요한 공간 및 시간 리소스는 nn 에서 기하급수적으로 증가합니다:

  • M{M}p~{\tilde{p}} 의 추정치는 한정된 표본으로 인해 통계적 오차가 있을 수 있습니다. 이 노이즈는 원하는 만큼 작게 만들 수 있습니다 더 많은 샷을 촬영하는 대가로 (드리프트 하드웨어 매개 변수의 시간 척도까지 M{M} 에서 시스템 오류를 초래하는 드리프트 하드웨어 파라미터의 시간 척도까지 ). 그러나 완화 작업을 수행할 때 관찰되는 비트 문자열에 대한 가정을 하지 않으면 에 대한 가정을 하지 않으면 M{M} 을 추정하는 데 필요한 샷 수가 증가합니다 nn 에서 적어도 기하급수적으로 증가합니다.
  • M{M}2n×2n2^n \times 2^n 행렬입니다. n>10n>10 을 저장하는 데 필요한 메모리 양은 M{M} 이 이면 고성능 노트북에서 사용할 수 있는 메모리보다 더 큽니다.

추가 제한 사항은 다음과 같습니다:

  • 복구된 분포 p{\vec{p}} 는 하나 이상의 이상의 음수 확률을 가질 수 있습니다(여전히 1로 합산됨). 한 가지 해결책 는 Mpp~2||{M} {\vec{p}} - {\tilde{p}}||^2 을 최소화하는 것입니다 p{\vec{p}} 의 각 항목은 음수가 아니어야 합니다. 그러나 이러한 메서드의 런타임은 메서드의 런타임은 방정식 (2)를 직접 푸는 것보다 훨씬 더 깁니다.
  • 이 완화 절차는 비트 문자열에 대한 확률 분포 수준에서 작동합니다 수준에서 작동합니다. 특히 개별적으로 관찰된 비트 문자열의 오류는 수정할 수 없습니다 관찰된 비트 문자열의 오류를 수정할 수 없습니다.

Qiskit M3 애드온: 더 긴 비트열로의 확장

표준 숫자 선형 대수 루틴을 사용하여 방정식 (2)를 푸는 것은 약 10비트 이하의 비트 문자열로 제한됩니다. M3 은 훨씬 긴 비트 문자열을 처리할 수 있습니다. 이를 가능하게 하는 M3 의 두 가지 핵심 속성은 다음과 같습니다:

  • 비트 모음 중 3차 이상의 판독 오차 상관관계는 의 상관관계는 무시할 수 있는 것으로 간주되어 무시됩니다. 원칙적으로, 더 많은 샷을 찍는 대가로 더 높은 상관관계를 추정할 수 있습니다.
  • M{M} 을 명시적으로 구성하는 대신 훨씬 더 작은 유효 행렬을 사용하여 p~{\tilde{p}} 을 구성할 때 수집된 비트스트링에 대해서만 확률을 기록하는 훨씬 작은 유효 행렬을 사용합니다.

높은 수준에서 절차는 다음과 같이 작동합니다.

먼저, M{M} 에 대한 단순하고 효과적인 설명을 구성할 수 있는 빌딩 블록을 구성합니다. 그런 다음, 관심 있는 회로를 반복적으로 실행하고 비트 문자열을 수집합니다 p~{\tilde{p}} 와 빌딩 블록의 도움으로 효과적인 M{M} 을 구성하는 데 사용하는 비트스트링을 수집합니다.

더 정확하게는,

  • 단일 큐비트 할당 행렬은 각 큐비트에 대해 추정됩니다. 이를 위해, 우리는 반복적으로 큐비트 레지스터를 올 제로 상태 0...0|0 ... 0 \rangle 로 준비한 다음 올 원 상태에서 상태 1...1|1 ... 1 \rangle 에서 각 큐비트가 읽혀질 확률을 기록합니다 을 읽을 확률을 기록합니다.

  • 차수 3 이상의 상관관계는 무시할 수 있는 것으로 간주되어 무시됩니다.

    대신 2×22 \times 2 단일 큐비트의 nn 할당 행렬과 4×44 \times 4 2-큐비트 할당의 n(n1)/2n(n-1)/2 숫자 행렬로 구성합니다. 이러한 1큐비트 및 2큐비트 할당 행렬은 나중에 사용할 수 있도록 저장됩니다 사용을 위해 저장됩니다.

  • 회로를 반복적으로 샘플링하여 p~{\tilde{p}} 을 구성한 후 를 구성한 후, M{M} 에 대한 효과적인 근사치를 구성합니다 비트스트링만을 사용하여 p~{\tilde{p}} 에 대한 유효 근사치를 구축합니다. 이 유효 행렬 는 이전 항목에서 설명한 단일 큐비트 행렬과 2큐비트 행렬을 사용하여 구축됩니다. 이 행렬의 선형 차원은 최대 다음과 같은 수입니다 를 구성하는 데 사용된 샷의 수만큼이며, 이는 전체 할당 행렬 p~{\tilde{p}} 의 차원 전체 할당 행렬 M{M} 의 차원 2n2^n 보다 훨씬 작습니다.

기술적인 자세한 내용은 M3 에서 양자 컴퓨터에서 측정 오류의 확장 가능한 완화를 참조하세요.

M3 의 양자 알고리즘 적용

숨겨진 시프트 문제에 M3 의 판독 완화 기능을 적용합니다. 숨겨진 시프트 문제와 숨겨진 하위 그룹 문제와 같은 밀접하게 관련된 문제는 원래 내결함성 설정에서 고안되었습니다(더 정확하게는 내결함성 QPU가 가능하다는 것이 증명되기 전입니다!). 하지만 사용 가능한 프로세서도 함께 연구합니다. 127-큐비트 IBM® QPU에서 얻은 숨겨진 시프트 문제의 변형에 대해 얻은 알고리즘 지수 속도 향상의 예는 이 논문 ( arXiv 버전 )에서 확인할 수 있습니다.

아래에서 모든 연산은 부울 연산입니다. 즉, a,bZ2={0,1}a, b \in \mathbb{Z}_2 = \{0, 1\} 의 경우 a+ba + b 은 논리적 XOR 함수입니다. 또한 곱셈 a×ba \times b (또는 aba b )은 논리 AND 함수입니다. 의 경우 x,y{0,1}nx, y \in \{0, 1\}^n, x+yx + y 는 XOR을 비트 단위로 적용하여 정의됩니다. 도트 제품 :Z2nZ2\cdot: {\mathbb{Z}_2^n} \rightarrow \mathbb{Z}_2 은 에 의해 정의됩니다 xy=ixiyix \cdot y = \sum_i x_i y_i.

아다마르 연산자와 푸리에 변환

양자 알고리즘을 구현할 때 하다마드 연산자를 푸리에 변환으로 사용하는 것은 매우 일반적입니다. 계산 기준 상태는 클래식 상태라고도 합니다. 이들은 기존 비트스트링과 일대일 관계에 있습니다. 고전 상태에 대한 nn -큐비트 하다마드 연산자는 부울 하이퍼큐브의 푸리에 변환으로 볼 수 있습니다:

Hn=12nx,yZ2n(1)xyyx.H^{\otimes n} = \frac{1}{\sqrt{2^n}} \sum_{x,y \in {\mathbb{Z}_2^n}} (-1)^{x \cdot y} {|{y}\rangle}{\langle{x}|}.

고정 비트 문자열 ss 에 해당하는 상태 s{|{s}\rangle} 를 생각해 보겠습니다. HnH^{\otimes n} 을 적용하고 xs=δx,s{\langle {x}|{s}\rangle} = \delta_{x,s} 을 사용합니다, s{|{s}\rangle} 의 푸리에 변환은 다음과 같이 쓸 수 있음을 알 수 있습니다

Hns=12nyZ2n(1)syy. H^{\otimes n} {|{s}\rangle} = \frac{1}{\sqrt{2^n}} \sum_{y \in {\mathbb{Z}_2^n}} (-1)^{s \cdot y} {|{y}\rangle}.

하다 마드는 그 자체로 역입니다, HnHn=(HH)n=InH^{\otimes n} H^{\otimes n} = (H H)^{\otimes n} = I^{\otimes n}. 따라서 역 푸리에 변환은 HnH^{\otimes n} 와 같은 연산자입니다. 명시적으로, 우리는 가지고 있습니다,

s=HnHns=Hn12nyZ2n(1)syy. {|{s}\rangle} = H^{\otimes n} H^{\otimes n} {|{s}\rangle} = H^{\otimes n} \frac{1}{\sqrt{2^n}} \sum_{y \in {\mathbb{Z}_2^n}} (-1)^{s \cdot y} {|{y}\rangle}.

숨겨진 시프트 문제

숨겨진 교대 근무 문제의 간단한 예를 들어 보겠습니다. 문제는 함수에 대한 입력의 일정한 변화를 식별하는 것입니다. 우리가 고려하는 함수는 도트 프로덕트입니다. 숨겨진 시프트에 대한 양자 속도를 인정하는 대규모 함수 클래스의 가장 간단한 멤버 함수의 가장 단순한 멤버입니다 문제에 대한 양자 속도 향상을 인정하는 가장 간단한 함수 클래스입니다.

x,yZ2mx,y \in {\mathbb{Z}_2^m}mm 길이의 비트 문자열로 설정합니다. f:Z2m×Z2m{1,1}{f}: {\mathbb{Z}_2^m} \times {\mathbb{Z}_2^m} \rightarrow \{-1,1\} 을 다음과 같이 정의합니다

f(x,y)=(1)xy. {f}(x, y) = (-1)^{x \cdot y}.

a,bZ2ma,b \in {\mathbb{Z}_2^m}mm 길이의 고정 비트스트링으로 정의합니다. 또한 g:Z2m×Z2m{1,1}g: {\mathbb{Z}_2^m} \times {\mathbb{Z}_2^m} \rightarrow \{-1,1\} 을 다음과 같이 정의합니다

g(x,y)=f(x+a,y+b)=(1)(x+a)(y+b), g(x, y) = {f}(x+a, y+b) = (-1)^{(x+a) \cdot (y+b)},

여기서 aabb 은 (숨김) 매개변수입니다. 하나는 ff 를 구현하고 다른 하나는 gg 을 구현하는 두 개의 블랙박스가 주어집니다. 우리는 그들이 위에 정의 된 함수를 계산한다는 것을 알고 있다고 가정합니다 aabb 도 모른다고 가정합니다. 게임은 숨겨진 비트 문자열(시프트)과 aabbffgg 에 쿼리하여 구하는 것입니다. 이 게임을 고전적으로 플레이하면 분명합니다, aabb 를 결정하려면 O(2m)O(2m) 쿼리가 필요합니다. 예를 들어, 쌍의 한 요소는 모두 0이고 다른 요소는 정확히 하나의 요소가 11 으로 설정된 모든 문자열 쌍으로 gg 을 쿼리할 수 있습니다. 각 쿼리에서 aa 또는 bb 중 하나의 요소를 학습합니다. 그러나 블랙박스를 양자 회로로 구현하면 다음과 같은 것을 알 수 있습니다 ffgg 에 대한 단일 쿼리로 aabb 을 결정할 수 있습니다.

알고리즘 복잡성의 맥락에서 블랙박스를 오라클이라고 합니다. 불투명할 뿐만 아니라 오라클은 입력을 소비하고 즉시 출력을 생성하여 알고리즘의 복잡성 예산에 아무것도 추가하지 않고 복잡성 예산을 추가하지 않습니다. 실제로 현재 상황에서 ffgg 를 구현하는 오라클이 효율적인 것으로 보입니다.

ffgg 용 양자 회로

ffgg 을 양자 회로로 구현하려면 다음 재료가 필요합니다.

단일 큐비트 클래식 상태의 경우 x1,y1{|{x_1}\rangle}, {|{y_1}\rangle}, x1,y1Z2x_1,y_1 \in \mathbb{Z}_2, 제어- ZZ 게이트 CZ{CZ} 는 다음과 같이 작성할 수 있습니다

CZx1y1x1=(1)x1y1x1x1y1.{CZ} {|{x_1}\rangle}{|{y_1}\rangle}{x_1} = (-1)^{x_1 y_1} {|{x_1}\rangle}{x_1}{|{y_1}\rangle}.

우리는 mm CZ 게이트, (x1,y1)(x_1, y_1), (x2,y2)(x_2, y_2), 그리고 (xm,ym)(x_m, y_m) 을 통해 운영할 것입니다. 이 운영자를 CZx,y{CZ}_{x,y} 이라고 부릅니다.

Uf=CZx,yU_f = {CZ}_{x,y} 의 퀀텀 버전입니다 f=f(x,y){f} = {f}(x,y) :

Ufxy=CZx,yxy=(1)xyxy.%\CZ_{x,y} {|#1\rangle}{z} = U_f {|{x}\rangle}{|{y}\rangle} = {CZ}_{x,y} {|{x}\rangle}{|{y}\rangle} = (-1)^{x \cdot y} {|{x}\rangle}{|{y}\rangle}.

또한 비트 문자열 이동을 구현해야 합니다. xx 등록 Xa1XamX^{a_1}\cdots X^{a_m} 에서 운영자를 다음과 같이 표시합니다 XaX_a 로 표시하고 yy 등록 Xb=Xb1XbmX_b = X^{b_1}\cdots X^{b_m} 에도 마찬가지로 표시합니다. 이러한 연산자는 단일 비트가 11 인 경우 XX 을 적용하고 II 인 경우 00 을 적용합니다. 그러면 우리는

XaXbxy=x+ay+b. X_a X_b {|{x}\rangle}{|{y}\rangle} = {|{x+a}\rangle}{|{y+b}\rangle}.

두 번째 블랙박스 gg 는 단일 UgU_g 에 의해 구현됩니다

Ug=XaXbCZx,yXaXb.%U_g {|{x}\rangle}{|{y}\rangle} = X_aX_b \CZ_{x,y} X_aX_b {|{x}\rangle}{|{y}\rangle}. U_g = X_aX_b {CZ}_{x,y} X_aX_b.

이를 확인하기 위해 오른쪽에서 왼쪽으로 연산자를 상태 xy{|{x}\rangle}{|{y}\rangle} 에 적용합니다. First

XaXbxy=x+ay+b. X_a X_b {|{x}\rangle}{|{y}\rangle} = {|{x+a}\rangle}{|{y+b}\rangle}.

그런 다음

CZx,yx+ay+b=(1)(x+a)(y+b)x+ay+b. {CZ}_{x,y} {|{x+a}\rangle}{|{y+b}\rangle} = (-1)^{(x+a)\cdot (y+b)} {|{x+a}\rangle}{|{y+b}\rangle}.

또한

XaXb(1)(x+a)(y+b)x+ay+b=(1)(x+a)(y+b)xy, X^a X^b (-1)^{(x+a)\cdot (y+b)} {|{x+a}\rangle}{|{y+b}\rangle} = (-1)^{(x+a)\cdot (y+b)} {|{x}\rangle}{|{y}\rangle},

의 양자 버전인 f(x+a,y+b)f(x+a, y+b).

숨겨진 시프트 알고리즘

이제 숨겨진 교대 근무 문제를 해결하기 위해 조각을 맞춰 보겠습니다. 먼저 0으로 초기화된 레지스터에 하다마드를 적용합니다.

H2m=HmHm0m0m=122mx,yZ2m(1)xyxy.H^{\otimes 2m} = H^{\otimes m} \otimes H^{\otimes m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} = \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{x \cdot y} {|{x}\rangle}{|{y}\rangle}.

다음으로 gg 오라클을 쿼리하여 다음 위치에 도달합니다

UgH2m0m0m=122mx,yZ2m(1)(x+a)(y+b)xyU_g H^{\otimes 2m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} = \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{(x+a) \cdot (y+b)} {|{x}\rangle}{|{y}\rangle} 122mx,yZ2m(1)xy+xb+yaxy.\approx \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{x \cdot y + x \cdot b + y \cdot a} {|{x}\rangle}{|{y}\rangle}.

마지막 줄에서는 상수 글로벌 위상 계수 (1)ab(-1)^{a \cdot b} 를 생략했습니다, 를 생략하고 위상까지 동등함을 \approx 으로 나타냅니다. 다음으로, ff 오라클을 적용하면 (1)xy(-1)^{x \cdot y} 의 다른 인자가 도입되어 이미 존재하는 인자가 취소됩니다 존재합니다. 그런 다음

UfUgH2m0m0m122mx,yZ2m(1)xb+yaxy.U_f U_g H^{\otimes 2m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} \approx \frac{1}{\sqrt{2^{2m}}} \sum_{x, y \in {\mathbb{Z}_2^m}} (-1)^{x \cdot b + y \cdot a} {|{x}\rangle}{|{y}\rangle}.

마지막 단계는 역 푸리에 변환( H2m=HmHmH^{\otimes 2m} = H^{\otimes m} \otimes H^{\otimes m} )을 적용하는 것입니다, 를 적용하면

H2mUfUgH2m0m0mba.H^{\otimes 2m} U_f U_g H^{\otimes 2m} {{|{0}\rangle}^{\otimes m}}{{|{0}\rangle}^{\otimes m}} \approx {|{b}\rangle}{|{a}\rangle}.

회로가 완성되었습니다. 노이즈가 없는 경우, 양자 레지스터를 샘플링하면 다음과 같이 됩니다 비트 문자열 b,ab, a 을 확률 11 로 반환합니다.

부울 내부 곱은 소위 구부러진 함수의 예입니다. 여기서는 구부러진 함수를 정의하지 않겠습니다 정의하지는 않겠지만 "입력의 일부 선형 하위 공간에 대한 출력의 종속성을 악용하려는 공격에 대해 출력의 종속성을 악용하려는 공격에 대해 최대한 저항력이 있다"는 점만 언급하겠습니다 이 인용문은 고도로 비선형적인 부울 함수를 위한 양자 알고리즘 문서에서 가져온 것입니다 는 여러 클래스의 구부러진 함수에 대해 효율적인 히든 시프트 알고리즘을 제공합니다. 이 튜토리얼의 알고리즘은 문서의 3.1 섹션에 나와 있습니다.

보다 일반적인 경우, 숨겨진 시프트( sZns \in \mathbb{Z}^n )를 찾는 회로는 다음과 같습니다

HnUf~HnUgHn0n=s. H^{\otimes n} U_{\tilde{f}} H^{\otimes n} U_g H^{\otimes n} {|{0}\rangle}^{\otimes n} = {|{s}\rangle}.

일반적인 경우 ffgg 은 단일 변수의 함수입니다. 내부 제품의 예시에서 f(x,y)f(z)f(x, y) \to f(z) 을 사용하면 다음과 같은 형태가 됩니다, 와 zzxxyy 의 연결과 같고, ss 는 과 의 연결과 같습니다 과 aabb 의 연결과 같습니다. 일반적인 경우에는 정확히 두 개의 오라클이 필요합니다: gg 에 대한 오라클 하나와 f~\tilde{f} 에 대한 오라클 하나, 여기서 후자는 구부러진 함수 ff이중으로 알려진 함수입니다. 내부 곱 함수는 자체 이중 속성 f~=f\tilde{f}=f 을 갖습니다.

내부 제품의 숨겨진 시프트에 대한 회로에서 우리는 일반적인 경우 회로에 나타나는 중간 레이어를 생략했습니다 의 중간 레이어를 생략했습니다. 일반적인 경우 이 레이어가 필요하지만, 우리는 이 레이어를 생략하여 약간의 깊이를 절약했습니다 출력은 원하는 ab{|{a}\rangle}{|{b}\rangle} 대신 ba{|{b}\rangle}{|{a}\rangle} 이므로 포스트 프로세싱의 약간을 희생하여 약간의 깊이를 절약했습니다.


요구사항

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

  • Qiskit SDK v2.1 또는 이후 버전, 시각화 지원 기능 포함
  • Qiskit Runtime v0.41 이상 (pip install qiskit-ibm-runtime)
  • M3 키스킷 애드온 v3.0 (pip install mthree)

설정

from collections.abc import Iterator, Sequence
from random import Random
from qiskit.circuit import (
    CircuitInstruction,
    QuantumCircuit,
    QuantumRegister,
    Qubit,
)
from qiskit.circuit.library import CZGate, HGate, XGate
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
import timeit
import matplotlib.pyplot as plt
from qiskit_ibm_runtime import SamplerV2 as Sampler
import mthree

1단계: 고전적 입력을 양자 문제에 매핑하기

먼저, 숨겨진 시프트 문제를 구현하는 함수를 QuantumCircuit 로 작성합니다.

def apply_hadamards(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:
    """Apply a Hadamard gate to every qubit."""
    for q in qubits:
        yield CircuitInstruction(HGate(), [q], [])


def apply_shift(
    qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
    """Apply X gates where the bits of the shift are equal to 1."""
    for i, q in zip(range(shift.bit_length()), qubits):
        if shift >> i & 1:
            yield CircuitInstruction(XGate(), [q], [])


def oracle_f(qubits: Sequence[Qubit]) -> Iterator[CircuitInstruction]:
    """Apply the f oracle."""
    for i in range(0, len(qubits) - 1, 2):
        yield CircuitInstruction(CZGate(), [qubits[i], qubits[i + 1]])


def oracle_g(
    qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
    """Apply the g oracle."""
    yield from apply_shift(qubits, shift)
    yield from oracle_f(qubits)
    yield from apply_shift(qubits, shift)


def determine_hidden_shift(
    qubits: Sequence[Qubit], shift: int
) -> Iterator[CircuitInstruction]:
    """Determine the hidden shift."""
    yield from apply_hadamards(qubits)
    yield from oracle_g(qubits, shift)
    # We omit this layer in exchange for post processing
    # yield from apply_hadamards(qubits)
    yield from oracle_f(qubits)
    yield from apply_hadamards(qubits)


def run_hidden_shift_circuit(n_qubits, rng):
    hidden_shift = rng.getrandbits(n_qubits)

    qubits = QuantumRegister(n_qubits, name="q")
    circuit = QuantumCircuit.from_instructions(
        determine_hidden_shift(qubits, hidden_shift), qubits=qubits
    )
    circuit.measure_all()
    # Format the hidden shift as a string.
    hidden_shift_string = format(hidden_shift, f"0{n_qubits}b")
    return (circuit, hidden_shift, hidden_shift_string)


def display_circuit(circuit):
    return circuit.remove_final_measurements(inplace=False).draw(
        "mpl", idle_wires=False, scale=0.5, fold=-1
    )

작은 예부터 시작하겠습니다:

n_qubits = 6
random_seed = 12345
rng = Random(random_seed)
circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(
    n_qubits, rng
)

print(f"Hidden shift string {hidden_shift_string}")

display_circuit(circuit)

Output:

Hidden shift string 011010
Output of the previous code cell

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

job_tags = [
    f"shift {hidden_shift_string}",
    f"n_qubits {n_qubits}",
    f"seed = {random_seed}",
]
job_tags

Output:

['shift 011010', 'n_qubits 6', 'seed = 12345']
# Uncomment this to run the circuits on a quantum computer on IBMCloud.
service = QiskitRuntimeService()
backend = service.least_busy(
    operational=True, simulator=False, min_num_qubits=100
)

# from qiskit_ibm_runtime.fake_provider import FakeMelbourneV2
# backend = FakeMelbourneV2()
# backend.refresh(service)

print(f"Using backend {backend.name}")


def get_isa_circuit(circuit, backend):
    pass_manager = generate_preset_pass_manager(
        optimization_level=3, backend=backend, seed_transpiler=1234
    )
    isa_circuit = pass_manager.run(circuit)
    return isa_circuit


isa_circuit = get_isa_circuit(circuit, backend)
display_circuit(isa_circuit)

Output:

Using backend ibm_kingston
Output of the previous code cell

3단계: Qiskit primitives 를 사용하여 회로 실행

# submit job for solving the hidden shift problem using the Sampler primitive
NUM_SHOTS = 50_000


def run_sampler(backend, isa_circuit, num_shots):
    sampler = Sampler(mode=backend)
    sampler.options.environment.job_tags
    pubs = [(isa_circuit, None, NUM_SHOTS)]
    job = sampler.run(pubs)
    return job


def setup_mthree_mitigation(isa_circuit, backend):
    # retrieve the final qubit mapping so mthree knows which qubits to calibrate
    qubit_mapping = mthree.utils.final_measurement_mapping(isa_circuit)

    # submit jobs for readout error calibration
    mit = mthree.M3Mitigation(backend)
    mit.cals_from_system(qubit_mapping, rep_delay=None)

    return mit, qubit_mapping
job = run_sampler(backend, isa_circuit, NUM_SHOTS)
mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)

4단계: 결과 후처리 및 전통적인 형식으로 반환

위의 이론적 논의에서 우리는 입력 abab 에 대해 출력 baba 을 기대한다고 결정했습니다. 추가적인 복잡성은 더 간단한 (사전 변환된) 회로를 갖기 위해 필요한 CZ 게이트를 인접한 큐비트 쌍 사이에 큐비트 쌍 사이에 필요한 CZ 게이트를 삽입해야 한다는 것입니다. 이는 비트 문자열 aabba1b1a2b2a1 b1 a2 b2 \ldots 로 인터리빙하는 것과 같습니다. 출력 문자열 baba 도 비슷한 방식으로 인터리빙됩니다: b1a1b2a2b1 a1 b2 a2 \ldots. 아래의 unscramble 함수는 함수는 입력 문자열과 출력 문자열을 직접 비교할 수 있도록 출력 문자열을 b1a1b2a2b1 a1 b2 a2 \ldots 에서 a1b1a2b2a1 b1 a2 b2 \ldots 로 변환합니다.

# retrieve bitstring counts
def get_bitstring_counts(job):
    result = job.result()
    pub_result = result[0]
    counts = pub_result.data.meas.get_counts()
    return counts, pub_result
counts, pub_result = get_bitstring_counts(job)

두 비트 문자열 사이의 해밍 거리는 비트가 서로 다른 인덱스의 수입니다.

def hamming_distance(s1, s2):
    weight = 0
    for c1, c2 in zip(s1, s2):
        (c1, c2) = (int(c1), int(c2))
        if (c1 == 1 and c2 == 1) or (c1 == 0 and c2 == 0):
            weight += 1

    return weight
# Replace string of form a1b1a2b2... with b1a1b2a1...
# That is, reverse order of successive pairs of bits.
def unscramble(bitstring):
    ps = [bitstring[i : i + 2][::-1] for i in range(0, len(bitstring), 2)]
    return "".join(ps)


def find_hidden_shift_bitstring(counts, hidden_shift_string):
    # convert counts to probabilities
    probs = {
        unscramble(bitstring): count / NUM_SHOTS
        for bitstring, count in counts.items()
    }

    # Retrieve the most probable bitstring.
    most_probable = max(probs, key=lambda x: probs[x])

    print(f"Expected hidden shift string: {hidden_shift_string}")
    if most_probable == hidden_shift_string:
        print("Most probable bitstring matches hidden shift 😊.")
    else:
        print("Most probable bitstring didn't match hidden shift ☹️.")
    print("Top 10 bitstrings and their probabilities:")
    display(
        {
            k: (v, hamming_distance(hidden_shift_string, k))
            for k, v in sorted(
                probs.items(), key=lambda x: x[1], reverse=True
            )[:10]
        }
    )

    return probs, most_probable
probs, most_probable = find_hidden_shift_bitstring(
    counts, hidden_shift_string
)

Output:

Expected hidden shift string: 011010
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their probabilities:
{'011010': (0.9743, 6),
 '001010': (0.00812, 5),
 '010010': (0.0063, 5),
 '011000': (0.00554, 5),
 '011011': (0.00492, 5),
 '011110': (0.00044, 5),
 '001000': (0.00012, 4),
 '010000': (8e-05, 4),
 '001011': (6e-05, 4),
 '000010': (6e-05, 4)}

M3 을 사용하여 판독 오류 완화를 적용하기 전에 가장 가능성이 높은 비트 문자열의 확률을 기록해 보겠습니다.

max_probability_before_M3 = probs[most_probable]
max_probability_before_M3

Output:

0.9743

이제 우리는 M3 에서 학습한 판독 보정을 카운트 값에 적용합니다. 이 함수는 준확률 apply_corrections 분포를 반환합니다. 다음은 합계가 11float 객체 목록입니다. 그러나 일부 값은 음수일 수 있습니다.

def perform_mitigation(mit, counts, qubit_mapping):
    # mitigate readout error
    quasis = mit.apply_correction(counts, qubit_mapping)

    # print results
    most_probable_after_m3 = unscramble(max(quasis, key=lambda x: quasis[x]))

    is_hidden_shift_identified = most_probable_after_m3 == hidden_shift_string
    if is_hidden_shift_identified:
        print("Most probable bitstring matches hidden shift 😊.")
    else:
        print("Most probable bitstring didn't match hidden shift ☹️.")
    print("Top 10 bitstrings and their quasi-probabilities:")
    topten = {
        unscramble(k): f"{v:.2e}"
        for k, v in sorted(quasis.items(), key=lambda x: x[1], reverse=True)[
            :10
        ]
    }
    max_probability_after_M3 = float(topten[most_probable_after_m3])
    display(topten)

    return max_probability_after_M3, is_hidden_shift_identified
print(f"Expected hidden shift string: {hidden_shift_string}")
max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(
    mit, counts, qubit_mapping
)

Output:

Expected hidden shift string: 011010
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their quasi-probabilities:
{'011010': '1.01e+00',
 '001010': '8.75e-04',
 '001000': '7.38e-05',
 '010000': '4.51e-05',
 '111000': '2.18e-05',
 '001011': '1.74e-05',
 '000010': '6.42e-06',
 '011001': '-7.18e-06',
 '011000': '-4.53e-04',
 '010010': '-1.28e-03'}

M3 보정 적용 전후의 숨겨진 시프트 문자열 식별 비교

def compare_before_and_after_M3(
    max_probability_before_M3,
    max_probability_after_M3,
    is_hidden_shift_identified,
):
    is_probability_improved = (
        max_probability_after_M3 > max_probability_before_M3
    )
    print(f"Most probable probability before M3: {max_probability_before_M3}")
    print(f"Most probable probability after M3: {max_probability_after_M3}")
    if is_hidden_shift_identified and is_probability_improved:
        print("Readout error mitigation effective! 😊")
    else:
        print("Readout error mitigation not effective. ☹️")
compare_before_and_after_M3(
    max_probability_before_M3,
    max_probability_after_M3,
    is_hidden_shift_identified,
)

Output:

Most probable probability before M3: 0.9743
Most probable probability after M3: 1.01
Readout error mitigation effective! 😊

M3 가 요구하는 CPU 시간이 샷 수에 따라 어떻게 변화하는지 그래프를 작성하라

# Collect samples for numbers of shots varying from 5000 to 25000.
shots_range = range(5000, NUM_SHOTS + 1, 2500)
times = []
for shots in shots_range:
    print(f"Applying M3 correction to {shots} shots...")
    t0 = timeit.default_timer()
    _ = mit.apply_correction(
        pub_result.data.meas.slice_shots(range(shots)).get_counts(),
        qubit_mapping,
    )
    t1 = timeit.default_timer()
    print(f"\tDone in {t1 - t0} seconds.")
    times.append(t1 - t0)

fig, ax = plt.subplots()
ax.plot(shots_range, times, "o--")
ax.set_xlabel("Shots")
ax.set_ylabel("Time (s)")
ax.set_title("Time to apply M3 correction")

Output:

Applying M3 correction to 5000 shots...
	Done in 0.003321983851492405 seconds.
Applying M3 correction to 7500 shots...
	Done in 0.004425413906574249 seconds.
Applying M3 correction to 10000 shots...
	Done in 0.006366567220538855 seconds.
Applying M3 correction to 12500 shots...
	Done in 0.0071477219462394714 seconds.
Applying M3 correction to 15000 shots...
	Done in 0.00860048783943057 seconds.
Applying M3 correction to 17500 shots...
	Done in 0.010026784148067236 seconds.
Applying M3 correction to 20000 shots...
	Done in 0.011459112167358398 seconds.
Applying M3 correction to 22500 shots...
	Done in 0.012727141845971346 seconds.
Applying M3 correction to 25000 shots...
	Done in 0.01406092382967472 seconds.
Applying M3 correction to 27500 shots...
	Done in 0.01546052098274231 seconds.
Applying M3 correction to 30000 shots...
	Done in 0.016769016161561012 seconds.
Applying M3 correction to 32500 shots...
	Done in 0.019537431187927723 seconds.
Applying M3 correction to 35000 shots...
	Done in 0.019739801064133644 seconds.
Applying M3 correction to 37500 shots...
	Done in 0.021093040239065886 seconds.
Applying M3 correction to 40000 shots...
	Done in 0.022840639110654593 seconds.
Applying M3 correction to 42500 shots...
	Done in 0.023974396288394928 seconds.
Applying M3 correction to 45000 shots...
	Done in 0.026412792038172483 seconds.
Applying M3 correction to 47500 shots...
	Done in 0.026364430785179138 seconds.
Applying M3 correction to 50000 shots...
	Done in 0.02820305060595274 seconds.
Text(0.5, 1.0, 'Time to apply M3 correction')
Output of the previous code cell

줄거리 해석

위의 플롯은 M3 보정을 적용하는 데 필요한 시간이 촬영 횟수에 따라 선형적으로 증가하는 것을 보여줍니다.


확장

n_qubits = 80
rng = Random(12345)
circuit, hidden_shift, hidden_shift_string = run_hidden_shift_circuit(
    n_qubits, rng
)

print(f"Hidden shift string {hidden_shift_string}")

Output:

Hidden shift string 00000010100110101011101110010001010000110011101001101010101001111001100110000111
isa_circuit = get_isa_circuit(circuit, backend)
job = run_sampler(backend, isa_circuit, NUM_SHOTS)
mit, qubit_mapping = setup_mthree_mitigation(isa_circuit, backend)
counts, pub_result = get_bitstring_counts(job)
probs, most_probable = find_hidden_shift_bitstring(
    counts, hidden_shift_string
)

Output:

Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their probabilities:
{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': (0.50402,
  80),
 '00000010100110101011101110010001010000110011100001101010101001111001100110000111': (0.0396,
  79),
 '00000010100110101011101110010001010000110011101001101010101001111001100100000111': (0.0323,
  79),
 '00000010100110101011101110010001010000110011101001101010101001101001100110000111': (0.01936,
  79),
 '00000010100110101011101110010011010000110011101001101010101001111001100110000111': (0.01432,
  79),
 '00000010100110101011101110010001010000110011101001101010101001011001100110000111': (0.0101,
  79),
 '00000010100110101011101110010001010000110011101001101010101001110001100110000111': (0.00924,
  79),
 '00000010100110101011101110010001010000010011101001101010101001111001100110000111': (0.00908,
  79),
 '00000010100110101011100110010001010000110011101001101010101001111001100110000111': (0.00888,
  79),
 '00000010100110101011101110010001010000110011101001100010101001111001100110000111': (0.0082,
  79)}

올바른 숨겨진 시프트 문자열이 발견되었음을 알 수 있습니다. 또한, 다음으로 가능성이 높은 9개의 비트 문자열은 단 한 위치에서만 잘못되었습니다.

가장 가능성이 높은 확률을 기록합니다:

max_probability_before_M3 = probs[most_probable]
max_probability_before_M3

Output:

0.50402
print(f"Expected hidden shift string: {hidden_shift_string}")
max_probability_after_M3, is_hidden_shift_identified = perform_mitigation(
    mit, counts, qubit_mapping
)

Output:

Expected hidden shift string: 00000010100110101011101110010001010000110011101001101010101001111001100110000111
Most probable bitstring matches hidden shift 😊.
Top 10 bitstrings and their quasi-probabilities:
{'00000010100110101011101110010001010000110011101001101010101001111001100110000111': '9.85e-01',
 '00000010100110101011101110010001010000110011100001101010101001111001100110000111': '6.84e-03',
 '00000010100110101011100110010001010000110011101001101010101001111001100110000111': '3.87e-03',
 '00000010100110101011101110010011010000110011101001101010101001111001100110000111': '3.42e-03',
 '00000010100110101011101110010001010000110011101001101010101001111001100100000111': '3.30e-03',
 '00000010100110101011101110010001010000110011101001101010101001110001100110000111': '3.28e-03',
 '00000010100010101011101110010001010000110011101001101010101001111001100110000111': '2.62e-03',
 '00000010100110101011101110010001010000110011101001101010101001101001100110000111': '2.43e-03',
 '00000010100110101011101110010000010000110011101001101010101001111001100110000111': '1.73e-03',
 '00000010100110101011101110010001010000110011101001101010101001111001000110000111': '1.63e-03'}
compare_before_and_after_M3(
    max_probability_before_M3,
    max_probability_after_M3,
    is_hidden_shift_identified,
)

Output:

Most probable probability before M3: 0.54348
Most probable probability after M3: 0.99
Readout error mitigation effective! 😊

그 결과 판독 오류가 주요 오류 원인으로 나타났으며 M3 완화 기능이 효과적인 것으로 나타났습니다.

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