量子エラー訂正(2021年春 Lab3)#
歴史的背景#
ショアのアルゴリズムは量子コンピューターに価値あるユースケースを与えました。しかし、量子力学特有のノイズの多さから、このようなアルゴリズムを実行できるハードウェアを作るのは非常に困難でした。1995年、ショアは量子情報を複数の量子ビットで共有することで誤差を減らすという画期的な論文を発表しました。[1]
それ以来、数十年の間に大きな進歩がありました。新しい形式の誤り訂正符号が発見され、それらを中心とした大きな理論的枠組みが構築されました。1997年にKitaevが提案した表面符号が有力な候補として浮上し、その後も多くのバリエーションが登場しています。しかし、量子ハードウェアの特性に合わせてコードを調整するには、まだ多くの課題が残っています。[2]
この課題では、回路に人工的な「エラー」を挿入するケースを考えます。あなたの課題は、これらの追加ゲートを識別できるように回路を設計することです。
次に、作成した回路を実際のデバイスに実装する方法を考える必要があります。つまり、量子デバイス上の量子ビットのレイアウトに合わせてソリューションを調整します。量子もつれの状態にあるゲート(最もノイズの多いゲート)の数がどれだけ少ないかで採点されます。
参考文献#
Shor, Peter W. "Scheme for reducing decoherence in quantum computer memory." Physical review A 52.4 (1995): R2493.
Dennis, Eric, et al. "Topological quantum memory." Journal of Mathematical Physics 43.9 (2002): 4452-4505.
エラーがもたらす課題#
エラーは、量子ビットに何らかの予期せぬ誤操作がなされたときに発生します。その影響で、回路がおかしくなってしまうのです。実機で動かしてみると、不思議な結果になることがありますが、それはすべてこのエラーのせいです。
予期せぬ誤操作はたくさんありますが、ここでは2種類のエラーしか存在しないと仮定します:ビット反転と位相反転です。
ビット反転は、x
ゲートと同じ効果があります。1つの量子ビットの状態を0から1に反転させます。
位相反転は、z
ゲートと同じ効果を持ち、重ね合わせ状態に\(-1\)の位相を導入します。簡単に言うと、1つの量子ビットの\(|+\rangle\)状態を\(|-\rangle\)に反転させるもので、その逆も然りです。
任意のエラーをこの2つだけで考えることができるのは、任意のエラーは何らかの行列で表すことができ、任意の行列は行列\(X\)と\(Z\)で書くことができるからです。具体的には、ある適切に選択された値 \(\alpha\), \(\beta\), \(\gamma\), \(\delta\)について、任意の1量子ビットの行列\(M\)を以下のように書き表すことができます。
ゆえに、したがって、この行列をある単一の量子ビット状態\(|\psi\rangle\)に適用すると、次のようになります。
結果として得られる重ね合わせは、初期状態、エラーが単なるビット反転の場合の状態、位相反転の場合の状態、そして両方の場合の状態で構成されます。もし、ビット反転と位相反転のどちらが起きたかを測定する方法があれば、状態は1つの可能性に集約されます。そして、私たちの複雑なエラーは、単純なビットまたは位相の反転になります。
では、ビット反転なのか位相反転なのか(あるいはその両方なのか)をどうやって検出するのでしょうか。そして、それがわかったら、どうすればいいのか。これらの疑問を解決するのが、量子エラー訂正です。
極めて単純な例#
多くの人が最初に書く量子回路の一つは、ふたつの量子ビットのもつれ(エンタングルメント)状態でしょう。今回の量子エラー訂正の旅も、まずはこの単純な例からみていきます。
from qiskit import QuantumCircuit
# Make an entangled pair
qc_init = QuantumCircuit(2)
qc_init.h(0)
qc_init.cx(0,1)
qc_init.draw('mpl')
from qiskit_aer import AerSimulator
def get_counts(qc: QuantumCircuit):
qc.measure_all()
backend = AerSimulator()
result = backend.run(qc).result()
return result.get_counts()
qc = qc_init.copy()
get_counts(qc)
{'00': 496, '11': 528}
ここでは、回路を動作させたときに期待される結果、つまり、「00」と「11」が同じ確率で発生することがわかります。
しかし、同じ回路でも、手動でビット反転の「エラー」を挿入するとどうなるでしょうか。
# Make bit flip error
qc_insert = QuantumCircuit(2)
qc_insert.x(0)
# Add it to our original circuit
qc = qc_init.copy()
qc = qc.compose(qc_insert)
qc.draw('mpl')
get_counts(qc)
{'01': 492, '10': 532}
今度は結果が違っていて、01
と10
です。2つのビット値は、常に一致していたものが、常に一致しなくなっています。このようにして、エラーの影響を検出します。
もうひとつの検出方法は、さらにいくつかのゲートを使ってエンタングルメントを元に戻すことです。エラーがなければ、最初の\(|00\rangle\)の状態に戻ります。
# Undo entanglement
qc_syn = QuantumCircuit(2)
qc_syn.cx(0,1)
qc_syn.h(0)
# Add this after the error
qc = qc_init.copy()
qc = qc.compose(qc_syn)
qc.draw('mpl')
get_counts(qc)
{'00': 1024}
しかし、量子ビットの1つにエラーがあるとどうなるでしょうか?さまざまなエラーを挿入してみましょう。
初期化の qc_init
、エラーを挿入する qc_insert
、そして最終的な測定で明確な答えが得られるようにする qc_syn
という回路です。
# Define an error
qc_insert = QuantumCircuit(2)
qc_insert.x(0)
# Undo entanglement
qc_syn = QuantumCircuit(2)
qc_syn.cx(0,1)
qc_syn.h(0)
# Add this after the error
qc = qc_init.copy()
qc = qc.compose(qc_insert)
qc = qc.compose(qc_syn)
qc.draw('mpl')
get_counts(qc)
{'10': 1024}
出力を見ると、エラーの状況が正確にわかります。ビット反転と位相反転の両方が検出できます。左側のビット値は、ビット反転があった場合(つまり、x(0)
またはx(1)
を挿入した場合)にのみ1
となります。右側のビット値も同様に,位相反転があること(z(0)
またはz(1)
が挿入されていること)を示しています。
このように、ビットや位相の反転を検出して区別する能力は非常に便利です。しかし、それだけでは十分ではありません。どのような種類のエラーが発生しているかがわかるだけで、どこでエラーが発生しているかはわかりません。より詳細な情報がなければ、これらの演算の影響を計算から取り除く方法を考えることはできません。したがって、量子エラー訂正には、もっと大きくて優れたものが必要なのです。
それを実装するのがあなたの役目です。ここには、あなたが提出しなければならないもののリストがあります。ここに書かれていることは、この後の例で説明されています。
表面符号#
Exercise
2つの量子ビットのx
とz
のエラーを検出できる回路を作ってください。
自分で解決策を考えてもよいですし、あるいは、以下に示す完成間近の解答に手を加えるだけでも構いません。
2つの回路を供給する必要があります。
qc_init
: 量子ビット(少なくとも2つ)を望ましい初期状態にします。qc_syn
: 量子ビットのサブセットを測定します。
挿入される人工的なエラーは、2つの特定の量子ビット上の
x
およびz
ゲートです。これに使用する2つの量子ビットを選択する必要があります(リストerror_qubits
として提供されます)。挿入されるエラーのセットは、16通りあります(エラーがないという些細なケースを含む)。
qc_syn
の測定結果は、それぞれに固有のビット文字列を出力する必要があります。これが満たされない場合、採点者はエラーメッセージ 'Please make sure the circuit is created to the initial layout.' を返します。
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
この例では、符号化量子ビットと呼ばれる5つの量子ビットを使用します。それらを記録するために、特別な量子レジスタを定義します。
code = QuantumRegister(5,'code')
さらに、シンドローム量子ビットと呼ばれる4つの量子ビットを追加します。
Tip
シンドローム量子ビット
シンドローム量子ビットは符号化量子ビットのエラー検出と識別を行うための量子ビットです。
直接符号化量子ビットを測定してしまうと、測定により量子状態が崩壊してしまい、正しい誤り訂正が困難となってしまうため、
エンタングルメントを利用して符号化量子ビットのエラー情報をシンドローム量子ビットに転送して測定します。
syn = QuantumRegister(4,'syn')
同様に、シンドローム量子ビットを測定する際に使用される4つの出力ビット用のレジスタを定義します。
out = ClassicalRegister(4,'output')
ここで、符号化量子ビットは4つの三角形の角を形成し、シンドローム量子ビットはそれぞれの三角形の内側に存在するようなレイアウトを考えます。
c0----------c1
| \ s0 / |
| \ / |
| s1 c2 s2 |
| / \ |
| / s3 \ |
c3----------c4
3つの量子ビットが互いに隣り合って形成されている各三角形に対して、スタビライザー演算を関連付けます。
スタビライザ―演算は両側面の量子ビットはZZZ、上下の量子ビットには、XXXとします。
まず始めに、符号化量子ビットのエラーを解析するためのシンドローム測定回路を構築します。
qc_syn = QuantumCircuit(code,syn,out)
# Left ZZZ
qc_syn.cx(code[0],syn[1])
qc_syn.cx(code[2],syn[1])
qc_syn.cx(code[3],syn[1])
qc_syn.barrier()
# Right ZZZ
qc_syn.cx(code[1],syn[2])
qc_syn.cx(code[2],syn[2])
qc_syn.cx(code[4],syn[2])
qc_syn.barrier()
# Top XXX
qc_syn.h(syn[0])
qc_syn.cx(syn[0],code[0])
qc_syn.cx(syn[0],code[1])
qc_syn.cx(syn[0],code[2])
qc_syn.h(syn[0])
qc_syn.barrier()
# Bottom XXX
qc_syn.h(syn[3])
qc_syn.cx(syn[3],code[2])
qc_syn.cx(syn[3],code[3])
qc_syn.cx(syn[3],code[4])
qc_syn.h(syn[3])
qc_syn.barrier()
# Measure the auxilliary qubits
qc_syn.measure(syn,out)
qc_syn.draw('mpl')
次に初期化回路として、情報を符号化するための回路を構築します。エラーが無い場合にこの回路で符号化した量子状態に対してシンドローム測定を行ったときに、出力が確実に0000
になるようにします。
Tip
符号化回路
この回路は複数の物理量子ビットを用いて論理的な0と1に符号化します。
このとき、誤り訂正が実現可能なように符号化を行う必要があり、そのためにはスタビライザ演算に対して不変となるような状態(固有状態)で符号化される必要があります。
qc_init = QuantumCircuit(code,syn,out)
qc_init.h(syn[0])
qc_init.cx(syn[0],code[0])
qc_init.cx(syn[0],code[1])
qc_init.cx(syn[0],code[2])
qc_init.cx(code[2],syn[0])
qc_init.h(syn[3])
qc_init.cx(syn[3],code[2])
qc_init.cx(syn[3],code[3])
qc_init.cx(syn[3],code[4])
qc_init.cx(code[4],syn[3])
qc_init.barrier()
qc_init.draw('mpl')
事実か確認しましょう。
qc = qc_init.compose(qc_syn)
get_counts(qc)
{'000000111 0000': 253,
'000000000 0000': 259,
'000011011 0000': 252,
'000011100 0000': 260}
それでは、2つの符号量子ビットにエラーであるx
, z
ゲートを適用できる回路を作ってみましょう。
5個の符号量子ビットのうち、適切な2個を選ぶ必要があります。この符号では、反対側の角を選択する必要があります。
error_qubits = [0,4]
ここで,0と4は,以下のリストの量子ビットの位置を示しており,量子ビットcode[0]
とcode[4]
です。
qc.qubits
[Qubit(QuantumRegister(5, 'code'), 0),
Qubit(QuantumRegister(5, 'code'), 1),
Qubit(QuantumRegister(5, 'code'), 2),
Qubit(QuantumRegister(5, 'code'), 3),
Qubit(QuantumRegister(5, 'code'), 4),
Qubit(QuantumRegister(4, 'syn'), 0),
Qubit(QuantumRegister(4, 'syn'), 1),
Qubit(QuantumRegister(4, 'syn'), 2),
Qubit(QuantumRegister(4, 'syn'), 3)]
正しく符号化しているかどうかをチェックするために、次のような関数を使って人工的なエラーを導入する回路を作ることができます。ここでは、例えば、error_qubits[0]
の x
を表す x0
のように追加したいエラーを errors
に単純なテキスト文字列としてリストアップしています。
def insert(errors,error_qubits,code,syn,out):
qc_insert = QuantumCircuit(code,syn,out)
if 'x0' in errors:
qc_insert.x(error_qubits[0])
if 'x1' in errors:
qc_insert.x(error_qubits[1])
if 'z0' in errors:
qc_insert.z(error_qubits[0])
if 'z1' in errors:
qc_insert.z(error_qubits[1])
return qc_insert
可能性のある16通りすべてではなく、1つのエラーが導入された4つのケースだけを見てみましょう。
for error in ['x0','x1','z0','z1']:
qc = qc_init.compose(insert([error],error_qubits,code,syn,out)).compose(qc_syn)
print('\nFor error '+error+':')
counts = get_counts(qc)
for output in counts:
print('Output was',output,'for',counts[output],'shots.')
For error x0:
Output was 001011101 0010 for 258 shots.
Output was 001011010 0010 for 270 shots.
Output was 001000110 0010 for 255 shots.
Output was 001000001 0010 for 241 shots.
For error x1:
Output was 010010000 0100 for 246 shots.
Output was 010001011 0100 for 263 shots.
Output was 010010111 0100 for 236 shots.
Output was 010001100 0100 for 279 shots.
For error z0:
Output was 000111011 0001 for 254 shots.
Output was 000100000 0001 for 274 shots.
Output was 000100111 0001 for 253 shots.
Output was 000111100 0001 for 243 shots.
For error z1:
Output was 100000000 1000 for 247 shots.
Output was 100011011 1000 for 273 shots.
Output was 100011100 1000 for 254 shots.
Output was 100000111 1000 for 250 shots.
ここでは、特定のエラーが発生したときに、出力の各ビットが 1
になることがわかります。左端のビットが error_qubits[1]
で z
を検出し、次のビットが error_qubits[1]
で x
を検出し、以下同様です。
一番左の出力は,
code[1]
のz
を表しています.左から2番目の出力は
code[1]
のx
を表しています。左から 3 番目の出力は、
code[0]
のx
を表します。一番右の出力は
code[0]
上のz
を表す。
より多くのエラーが回路に影響を与えると、どのエラーが発生したかを明確に判断することが難しくなります。しかし、シンドロームの読み出しを継続的に行い、より多くの結果を得て、デコーディングというプロセスでデータを分析することで、エラーの影響を修正するのに十分な判断が可能となります。
このような検討事項は、今回の課題では扱わないかわりに、よりシンプルな事項でありながら同じくらい重要な事項にフォーカスします。
エラーの数が少なければ少ないほど、またエラーの内容が単純であればあるほど、エラー訂正の質が向上します。そのためには、使用しているデバイスに合わせてエラー訂正の手順を調整する必要があります。
今回の回路がどのように構成されているかを簡単に理解するために、2量子ビットゲートがいくつあるかを見てみましょう。
Additional information#
Created by: James Wootton, Rahul Pratap Singh
翻訳者: 小林有里
Version: 1.0.0