Posted at

量子をプログラミングで操作するQuantum as Codeの時代へ


はじめに

量子コンピュータが急激に発展するにつれて、これまでなかったような概念が情報処理に導入され始めています。特に近年日本でも広がりつつある「量子アニーリング」などの量子効果を活用した新しいマシンの挙動を説明するには、従来のコンピュータのシミュレーションを通じて数値計算的に行うよりも、量子コンピュータ上で振る舞いをダイレクトに記述する方が自然で簡単に思えます。Quantum as Codeという量子効果を明示的にプログラムコードで記述することで、新時代のプログラミングが実現できるという時代が近づいてきています。

Quantum as Codeは量子アニーリングだけではなく、様々な量子効果を量子ゲート方式のコンピュータ上で量子シミュレーションやベクトル操作を通じて再現できます。基本的な量子計算の上で再現できるような量子効果の実装が日々研究開発されています。

ご存知の方はご存知の通り成り上がりのへっぽこ技術者なので間違ってるところは指摘いただければ学びます。


Quantum as Codeとは?

従来のコンピュータでは量子にまつわる挙動や量子効果をシミュレーションで再現するのが難しくなっています。量子アニーリングなどは従来コンピュータにおいてはモンテカルロ法で効率的にシミュレーションができます。乱数生成器を用意して、サンプリングの元となる関数を用意し、乱数を振りながら熱統計力学的に焼きなまし処理を再現します。量子においても、量子効果を導入した横磁場の効果を数学的に近似展開し、このような分配関数の中に組み込んでしまいます。

量子アニーリングのシミュレーションは量子モンテカルロ計算のようなシミュレーションが利用できる一方、横磁場よりもより複雑な量子効果が現れる場合には、量子モンテカルロ計算のようなサイコロを振って分布を見るような計算が少し難しくなります。そのようなモンテカルロ計算の弱点を補うように様々な工夫をするのですが、頑張るほどに理解がしづらくなってきます。

そこで量子ゲート型の量子コンピュータの登場です。量子ゲート型のコンピュータの上では、このようなモンテカルロ計算のような処理をせずに、直感的に量子効果を学ぶことができます。まずは簡単に量子ゲート方式と量子シミュレーションについてみてみましょう。

以前のこちらの記事を参照してから学ぶとなおわかりやすいと思いますが、適宜難しいところは飛ばしてみて概要を掴んでみてください。

【まとめ】量子コンピュータと量子アニーリングの現時点まとめ

https://qiita.com/YuichiroMinato/items/2d5ceaa31f819cdc2601


量子ゲート型量子コンピュータの計算原理

量子ゲート型の量子コンピュータでは、「量子ビット」と呼ばれる特殊な量子状態を持つことのできるビットと、その量子ビットを操作する「量子ゲート」と呼ばれる演算操作があります。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3231383639342f65643231646136362d323133332d626363622d623764342d3934626365656136633138642e706e67.png

量子ビットは0に初期化されながら、量子ゲートと呼ばれる演算を時間的に断続的に行いながら、最後にその変化した量子状態を測定を通じて取り出します。少しわかりやすくOSSのBlueqatを使った例を示しながらみてみましょう。BlueqatはPython3.6以上にインストールできるPythonベースのOSSです。

pip install blueqat

でpipからインストールできます。Pythonが使える人はやってみてください。では、早速やってみましょう。


初期化

初期化は通常0からスタートします。量子ビットはすべて0の状態からスタートし、ゲート操作に備えます。データの入力自体もこの時点では行わず、このあとのゲート操作の中でデータ入力用の操作を行うことで入力を完了させます。

from blueqat import Circuit

#これで準備ができます。
Circuit()


ゲート操作

ゲート操作は0に初期化された量子ビットに「データ入力」や「ゲート演算」を施します。複雑なデータ入力は後述するとしてまずは簡単なデータ入力から見てみたいと思います。

量子コンピュータのビットはこれまでのコンピュータの01だけでなく、さまざまな中間表現を取ることができます。中間表現に関してはあとで学ぶとして、まずは一番簡単な01の切り替えを見てみます。

実は量子コンピュータはデータの表現が大幅に広がっていますが、従来のコンピュータのように使える汎用性というものがあります。下記は従来型のコンピュータの0と1をNOTゲートと呼ばれるもので切り替えますが、量子コンピュータでは名前が変わっただけで、Xゲートを使うことでNOTと同じように0と1を切り替えることができます。下記の図では0や1以外の+や-も利用できますが、まずは01だけでやってみましょう。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3231383639342f35623730393764372d306131632d376334372d376232332d3266613836376633316230392e706e67.png

データ入力として0に初期化された量子ビットにXゲートを操作することで、0の量子ビットを1にすることができます。

Circuit().x[0]

これは、0番目の量子ビットにXゲートをかけるという演算になります。これでデータは0から1になりました。複数量子ビットを使ってデータを操作したい場合には、複数の量子ビットを指定することで行うことができます。

Circuit().x[0,1,2]

こちらはPythonのスライス記法と呼ばれるものをベースに作られており、0番目と1番目と2番目の量子ビットにXゲートをかけるという意味になります。

次にせっかく準備した3量子ビットのこの回路の真ん中の量子ビットだけを0に戻す演算操作をしてみます。これには、1番目の量子ビットに再度xゲートを適用することで実現できます。

Circuit().x[0,1,2].x[1]

このようにカンマで連続してつなげることで連続して量子ビットに操作をすることができます。これはPythonでのメソッドチェーンと呼ばれる手法です。


測定

最後に演算が終わったら結果を取り出します。量子コンピュータの開発ツールでは2種類の結果の取り出し方があります。

1、状態ベクトルの取り出し(シミュレータのみ)

2、サンプリング

実機の量子コンピュータでは2番目のサンプリングを通じて結果を取り出しますが、量子コンピュータの一般的なSDKでは「状態ベクトル」というものかサンプリングでの01の計算結果のどちらかを取得することができます。

状態ベクトルは計算結果を確率振幅と呼ばれる古典の量子ビットに直す前の値が直接取得できます。実際にシミュレータや実機で同様の計算をした場合、出てくる答えは、この状態ベクトルの確率振幅に準じて分布が取得できます。

状態ベクトルを得るには、

Circuit().x[0,1,2].x[1].run()

のように.run()をつけます。そうすることにより、

array([0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 0.+0.j, 1.+0.j, 0.+0.j, 0.+0.j])

このような配列を得ることができました。これは複素数で表された状態ベクトルと呼ばれるもので、よく見ると0から数えて5番目に1が立っています。これは5番目の状態が$1^2=1$の確率で出てくるという意味です。状態ベクトルの読み方は、3量子ビットの2進数表記を順番に書いた順にならんでいます。3量子ビットの場合には、

$$000,001,010,011,100,101,110,111$$

この8通りの組み合わせが答えとして出てきます。実際にはこのうちの1つが出てくるのですが、シミュレータではこの確率分布を見ることができます。

5番目に相当するのは、$101$ですので、これは0に初期化された量子ビットが一度すべて1に変換され、万七の量子ビットだけが再度反転して0になったという答えに一致します。

量子状態を表現するには、量子状態であるということがわかりやすいようにブラケット記号と呼ばれるものをつけて、

$$\mid 000 \rangle,\mid 001 \rangle,\mid 010 \rangle,\mid 011 \rangle,\mid 100 \rangle,\mid 101 \rangle,\mid 110 \rangle,\mid 111 \rangle$$

のように表現します。


サンプリング

上記の測定を実機で行う場合には上記のような確率分布は直接は測定できません。01の分布で出ます。実際にシミュレータでも同様の表現ができますのでやってみましょう。

Circuit().x[0,1,2].x[1].m[:].run(shots=100)

今度は少し最後の測定部分が異なります。.m[:]というのがついています。measurementのmで量子ビットを測定するという意味です。ここでは:がついていますが、これは全ての量子ビットを測定するというスライス記法です。また、.run()にshots=100が追加されています。これは100回の施行を行うという意味です。先ほどの状態ベクトルと異なり、状態の分布を見なくてはいけないのでたくさんの施行を行う必要があります。

Counter({'101': 100})

結果はこのように100回101という答えが出てきました。実際にはより複雑な計算をすることによりこの施行回数に合わせて分布にばらつきが出ます。


01のデータから三次元の複素数データへ

量子コンピュータは従来型のコンピュータと異なる点は、01のビットだけでなく「波動」を操作することができる点です。波動は並ですから、位相や角度と呼ばれるパラメータを持っています。量子コンピュータでは、この01の粒子としてのビットの情報と位相による波動の情報の両方を複素数として持つことができます。

68747470733a2f2f71696974612d696d6167652d73746f72652e73332e616d617a6f6e6177732e636f6d2f302f3231383639342f37373666356333352d323730642d613338652d373939332d6531343936623563323865622e706e67.png

ここでは細かい部分は触れませんが、回転ゲートなどを利用することによってこの状態ベクトルの01だけでなく複素数で非常に複雑な状態を保つことができます。そのような複雑な量子状態も簡単なゲート操作によって取得することができます。

Circuit().h[0].t[0].run()

#=>
array([0.70710678+0.j , 0.5 +0.5j])


量子シミュレーション

このような複雑なデータを簡単なPythonのコーディングと操作によって制御ができる一方で、さらに強力なツールがあります。それが量子シミュレーションです。量子シミュレーションは人工的に量子の状態を制御することで、多数の粒子の複雑な相互作用を記述することができます。

量子コンピュータのゲートモデルではこの量子シミュレーションも1つの目玉アプリケーションとなっています。Quantum as Codeの名の通り、人工的に複雑な量子の振る舞いを量子ゲートのような用意されたツールを使って表現することができます。この方法を使って、量子アニーリングのような量子効果を利用した計算原理をソフトウェア上でプログラミングしてしまおうということです。

量子シミュレーションは主に量子状態を時間で連続的に変化させることで実現できます。

$$\mid \Psi \rangle = U\mid \Psi_0 \rangle$$

このプサイのマークは量子状態と呼ばれる上記の量子ビットの状態のことを表します(波動関数とも言います)。ここにある任意のゲート操作をすることでこの量子状態を変化させることができます。Uと書いてあるのはユニタリ行列のことで、量子ゲート方式の超電導量子ビットの量子コンピュータを使うときに使う演算子の総称です。実際にはここにもっと具体的なゲートが入ります。

量子シミュレーションは主に時間発展シミュレーションと呼ばれる連続したユニタリ計算によって成り立ちますが、その1つ1つは、

$$\mid \psi (t)\rangle = exp(-i/ \hbar H(t-t_0))\mid \psi(t_0)\rangle$$

このようにハミルトニアンと呼ばれると期待問題を量子で記述したものを使って計算を行います。詳しい計算の手順は、下記を参照してみてください。

【まとめ】量子コンピュータと量子アニーリングの現時点まとめ

https://qiita.com/YuichiroMinato/items/2d5ceaa31f819cdc2601


量子アニーリングをシミュレートする

量子アニーリングは横磁場という量子効果を準備し、それを少しずつ弱めながら計算を進めていきます。最初に大事になるのは横磁場によるハミルトニアンというもので、

$$H_d$$

としてみます。ときたい問題のハミルトニアンを

$$H_c$$

として用意した場合、これらの2つを使って量子断熱計算の要領で交換をすることで求めたい問題のハミルトニアンの最小固有値を求めることができます。

最初は$H_d$からスタートして、だんだんと$H_d$を弱めていき最終的に0にして、逆に$H_c$は最初は0からだんだんと強めて最終的に係数を1にします。式としては、

$$H = sH_c + (1-s)H_d$$

を採用し、sはステップによって0から1に段階的に変化します。こちらを量子ゲート方式で進めるには、時間発展シミュレーションを準備します。

$$\mid \psi_{\beta, \gamma} \rangle = e^{-i \beta_1 H_{d}}e^{-i \gamma_1 H_{c}}

e^{-i \beta_0 H_{d}}e^{-i \gamma_0 H_{c}} \mid + \rangle$$

こちらは+状態と呼ばれるHゲートを全ての量子ビットに適用した状態で重ね合わせ状態を作ります。その状態から順番にシミュレーションをします。

古典のハミルトニアン、横磁場ハミルトニアン、古典ハミルトニアン、、、の順番に係数を変えながら進めていくことで量子断熱計算ができます。

詳しいことを今回はみませんが、これをコードとして書くことで実行できます。実際にPythonで実行するには関数となっていて、

from blueqat import vqe

from blueqat.pauli import qubo_bit as q

hamiltonian = -3*q(0)-3*q(1)-3*q(2)-3*q(3)-3*q(4)+2*q(0)*q(1)+2*q(0)*q(2)+2*q(0)*q(3)+2*q(0)*q(4)+2*q(1)*q(2)+2*q(1)*q(3)+2*q(1)*q(4)+2*q(2)*q(3)+2*q(2)*q(4)+2*q(3)*q(4)
step = 2

result = vqe.Vqe(vqe.QaoaAnsatz(hamiltonian, step)).run()
print(result.most_common(12))

とすることで、

(((1, 0, 0, 1, 0), 0.09652768125318405), ((0, 0, 1, 1, 0), 0.09652768125318405), ((0, 1, 0, 1, 0), 0.09652768125318403), ((0, 0, 0, 1, 1), 0.09652768125318403), ((0, 1, 1, 0, 0), 0.096527681253184), ((0, 0, 1, 0, 1), 0.096527681253184), ((1, 1, 0, 0, 0), 0.09652768125318399), ((1, 0, 1, 0, 0), 0.09652768125318399), ((0, 1, 0, 0, 1), 0.09652768125318399), ((1, 0, 0, 0, 1), 0.09652768125318395), ((0, 0, 0, 0, 0), 0.007633192722372811), ((1, 1, 1, 1, 1), 0.002734968922138756))

のように結果が求まります。上記のhamiltonianでqと書かれているのが01で書かれたハミルトニアンです。このように量子効果を用いたシミュレーションであれば、ステップ数などを決めてシミュレーションを行うことができます。ハードウェアで開発されていたことがコードとなって量子効果を操作するという時代が来たのはとても感慨深いことです。


組合せ最適化以外の多々の用途

上記量子アニーリングの場合には、求めるハミルトニアンは最終的に行き着く古典のハミルトニアンと呼ばれる量子ゲートではpauliZに相当する計算ができます。また、使用しているアルゴリズムも古典をつかっているため必然的に社会問題など私たちの目に見える世界の問題を解くことになります。

一方で量子の力を使って解けるのは必ずしも社会問題だけではありません。量子性の強い問題やこれまで計算しづらかった量子系の謎を解くための科学計算用途にも大きく役立つことと思います。

これまでは量子化学や最適化問題などが多かったですが、近年機械学習への応用がとても進んでいますので、その辺りも今後どのように量子計算や量子シミュレーションで実装すれば良いかを迫ってみたいと思います。以上です。