うごくものをつくる

個人的な学習ノート

雑記

【基礎編】量子コンピュータに0+1を計算させる

0 + 1 = 1

当たり前すぎるほど簡単な問題だけど、今日はこれを量子コンピュータに計算してもらおう。

1.  古典的な論理ゲート(OR)

その前に、まずは古典コンピュータでの計算から復習。実際はコンピュータどころか1個数十円の単なるロジックICでもできる計算だけど。論理ゲートとか簡単すぎて知ってるよという人は読み飛ばしてね。

さて、今からやろうとしている足し算は足したい数(入力)が2つ、足した結果(出力)が1つという2入力1出力の論理回路が必要だ。2入力をそれぞれA,Bとし、出力をSとすると、A, B, Sの間にはそれぞれこのような関係がある。

ABS
000
011
101
AとBについての真理値表

足し算なので、A = 0、B = 0なら答えもS = 0だが、どちらかが0、どちらかが1だと答えは1となる。つまり0 + 1 = 1だ。真理値表を見て分かる通り、これは論理和、つまりORゲートそのものである。

(なお、A = 1, B = 1の場合は意図的に表から外している。ORゲートだとA = 1, B = 1 でS = 1となるが、これを足し算だとするなら答えは2進数で「10」でなければならず、桁の繰り上がりについて考えないといけなくなる。当然論理ゲートも単なるORゲートじゃなくて半加算器というものを組まないといけなくなるんだけど、今回の主題はとりあえず量子コンピュータを使ってみることなので、割愛。)

よし、0 + 0、0 + 1、1 + 0という3パターンの計算については、単に2入力をORゲートに通すだけで良いということが分かった。ORゲートはこういうICで市販されている。

4回路OR TC74HC32AP

Aのピンに電圧ロー(= 0)か電圧ハイ(= 1)を入れ、Bのピンにもいずれかの電圧を入れてやると、Sのピンの電圧は上に載せた表の通りになる。Aがハイ、BがローならSはハイ、みたいな感じ。そういうふうに作られたのがORゲートICだ。0 + 0, 0 + 1, 1 + 0の計算結果がわからないのなら、とりあえずORゲートICの入力ピンにこれらの電圧を入れてから、出力ピンの電圧を測定してやればよいのだ。

量子コンピュータの場合も考え方は本質的に同じ。じゃあ早速やってみよう。

2. 量子コンピュータにおけるORゲート

量子コンピュータの場合もビットを論理ゲートに通すわけだが、量子コンピュータにおけるORゲートに相当するものはなんだろうか?

結論から言うと、これが量子ゲートにおけるORゲートだ。図の見方は楽譜みたいな感じで、ビットが線の上を左から右に流れていく。その途中に、青色や紫色で表された記号を通る。ここが論理ゲートであり、この記号のところを通るときに量子ビットの状態は何らかの変化をする(変化しないときもある)。

構成要素を一つずつ見てみよう。

・制御NOTゲート(CNOTゲート)

2入力、2出力の論理ゲート。上の小さい丸が制御ビットで、下の+マークがついている方が標的ビット。CNOTゲートの動作は、「制御ビットに1が入ったときは標的ビットを反転させて(0は1に、1は0に変えて)出力。制御ビットが0だったら、標的ビットには何もせず素通りさせる」というもの。

今回は、入力A,Bの2つに対して、Aを制御ビットとしたCNOT、Bを制御ビットとしたCNOTの2つを作っている。実際にAとBに0や1を入れてみてSの状態を考えてみると簡単にわかることだが、今回の計算においてはこの2つのCNOTの順番は入れ替わっても別に良い。

・トフォリゲート(CCNOTゲート)

トフォリゲートは入力が3つの量子ゲート。さっきのCNOTゲートに似た記号だけど、その働きも似ている。CCNOTゲートという別名から何となく分かる通り、制御ビットが2つ、標的ビットが1つ、という量子ゲートである。

2つの制御ビットがどちらも1だったら、標的ビットを反転させる。制御ビットの片方、もしくは両方が0だと、標的ビットには何もしない。

なお、トフォリというのは人名である。

・観測

これは量子ゲートではない。

量子状態というのは観測するまでどういう状態なのかわからないので、様々な量子ゲートを通して計算を実行したら、最終的にはその結果である量子ビットを観測して値が何になったのかを見なければ、計算結果を得ることはできない。

この記号は、量子ビットの値を観測して、その値を古典ビット(いわゆる普通の電気回路的なビット。メモリだと思ってもらっても良い)に保存しますよという意味の記号である。端っこに書かれている「0」というのは、0番目の古典ビットに記録するという意味。

これで、今回の量子回路の中身はわかった。つまり、量子ビットは「B=1ならSを反転させるゲート」「A=1ならSを反転させるゲート」「AもBも1ならSを反転させるゲート」の3つを通り、最終的にSがどういう状態になったのかを観測し、0番目の古典ビットに記録する、というものだ。この量子回路について真理値表を作ると、先程の古典のORゲートと同じものになるということは、別に量子力学を知らなくても各ゲートの働きから順に考えていけばすぐに分かる。なお、Sの初期値は0だ。

3. プログラム

さて、IBMの量子コンピュータにこのような量子回路で計算を実行してもらうにはどういうプログラムを書けば良いのだろう。

説明はあとからすることにして、一旦プログラムを下に記す。

# Qiskitライブラリーを導入
from qiskit import *
from qiskit.visualization import *
# 描画のためのライブラリーを導入
import matplotlib.pyplot as plt

# 2量子ビット回路を用意
q = QuantumRegister(3)
c = ClassicalRegister(1)
qc = QuantumCircuit(q,c)

qc.cx(1,2)
qc.cx(0,2)
qc.ccx(0,1,2)

qc.measure(2,0)
# 回路を描画
qc.draw(output="mpl")

# QASMシミュレータで実行する
simulator = Aer.get_backend('qasm_simulator')
job = execute(qc, backend=simulator, shots=100)
result = job.result()

#  測定された回数を表示
counts = result.get_counts(qc)
print(counts)

## ヒストグラムで測定された確率をプロット
from qiskit.visualization import *
plot_histogram( counts )

上から順に説明すると、まず最初は量子計算の実行とその結果を出力するのに必要なライブラリをインポートする処理。これがないとなにもできないので、コピペすればOK。

次に、q = QuantumRegister(n)として、n個の量子ビットを作る。今回はA, B, Sの3つの量子ビットを作るので、カッコの中のnは3となる。同様にして、c = ClassicalRegister(n)として古典ビットをnビット作る。これは最後にSの値を記録するためのものなので1つでよく、カッコの中は1とする。そして、これらの3量子ビットqと1古典ビットcによってqcという量子回路を作りますよという記述のqc = QuantumCircuit(q,c)を書く。名前はここではqcとしたが、別に何でもいい。

なお、量子ビットをこうして作ったとき、各量子ビットの最初の値は必ず「0」である。量子ビット聞くと「これは0と1の重ね合わせなんだ……」と思う人がたまにいるが、量子ビットは0と1の重ね合わせ状態を入れることもできるというだけで、別に重ね合わさっていない純粋状態の0や1を入れることもできる。

qc.cx(x,y)と書くと、量子回路qcのx番目の量子ビットを制御ビット、y番目の量子ビットを標的ビットとしてCNOTゲートを作れる。今回は、qc.cx(1,2)でBが制御ビットのCNOTを、qc.cx(0,2)でAが制御ビットのCNOTを作った。

同じような感じで、qc.ccx(x,y,z)と書くと、x番目とy番目の量子ビットを制御ビット、z番目の量子ビットを標的ビットとしたトフォリゲートが作れる。今回の量子回路ではもちろんqc.ccx(0,1,2)とする。

最後に、qc.measure(x,y)で、x番目の量子ビットの状態を観測し、y番目の古典ビットに保存する。ここでは2番目の量子ビットであるSの状態を古典ビットに保存したいので、qc.measure(2,0)となる。古典ビットは一つしか無いので、0番目に保存する。

qc.draw(output=”mpl”)で、今作った回路図を画像として出力できる。

さて、Sの値としてどういうものが得られるのか量子コンピュータ実機に計算させてもいいが、待ち時間が長かったりするので、作った量子回路にミスがないか最初に確認する程度ならまずシミュレータを使おう。QASMシミュレータというのが用意されている。シミュレータのbackendにqasm_simulatorを指定しよう。

job = execute(qc, backend=simulator, shots=100)で実際に計算実行命令が出せる。ここで、最後のshots=○○というのは、計算を何回実行するかというのを指定している。量子コンピュータは確率的な動作なので、同じ量子回路なのに毎回異なる結果が出るというのは普通なことだ。なので、何度も計算させてみて、最も多く出た答えを、多数決的に「計算結果」として採用しましょう、という作戦を取る。まあ今回は入力としては0とか1とかの純粋状態なので、確率論的な部分は無いんだけど……

ここでは100回同じ計算をさせる設定とした。

さあ、これを実行してみると、計算結果をグラフにして出してくれる(プログラムの最後の部分)。それがこちら。

なんだこれ、という見た目だが、これは棒グラフである。要素が1個しかないので、棒というより四角だけど。縦軸が出現確率を表す。横軸が観測した量子ビットの値。文字は斜めに表示される。つまり上の図は「0」という一つの要素だけからなる棒グラフということだ。

つまり、Sを観測してみると、「0」である確率が1.000、つまり100%ということで、絶対にS=0というわけだ。

考えてみると、量子ビットは初めに用意した時は必ず0なのだからA=B=S=0で、量子回路の2つのCNOTもトフォリゲートも作動せず、Sは初期値の0から一切変化することはなく、観測してみたら100%必ず0が出る……当たり前の結果だ。0+0の答えは0という事がわかる。

今回は0+1を計算させたいので、まずBの値を0ではなく1にしてから、それ以降の計算をさせよう。そのためにはもう一つ量子ゲートを用意する。

・Xゲート(NOTゲート)

Xゲートは、入力された量子ビットを反転させる。つまり入力に0や1のような純粋状態が入ったときの動作としては、古典のNOTゲートと同じだ。0を1に、1を0に変えて出力する。

先程の量子回路の入力Bが通る最初の量子ゲートとして、Xゲートを新たに追加する。これで、Bの値は最初0だったので、Xゲートによって反転して1になってから、以降のCNOTゲートに入っていく。

プログラム上は、qc.x(1)というのを、CNOTゲートを作り始める前(コードの12行目)の前に追加する。カッコの中は何番目の量子ビットにXゲートを作るのかというのを表している。今回はBは1番目の量子ビットなのでカッコの中は1。

これでA=0, B=1なので、Sの値は0+1という処理の結果が入るはずだ。実行してグラフを出してみると……

先ほどと違い、「1」が確立100%で出てくるということがわかる。つまり0+1の答えは1というわけだ。

4. 量子コンピュータに計算させる

今の計算を、シミュレータではなく本物の量子コンピュータにさせてみよう。

本物の量子コンピュータを使うには、まずIBM のQuantum Experienceに会員登録してIBMidを作ることが必要だ。無料。

https://quantum-computing.ibm.com/

IBMidを作ったらログインしてアカウントページ(https://quantum-computing.ibm.com/account)を開くと、こんな画面になる。

赤枠で囲ったところに、登録したアカウントのAPIトークンが記載されている(最初は見えないようになっている)。青いCopy tokenというところをクリックすると、APIトークンがコピーされる。このAPIトークンが量子コンピュータ利用の際に必要となる。

先程のコードの #QASMシミュレータで実行する というコメント文以降を、次のようなコードに置き換える。「自分のAPIトークン」というところには、自分のアカウントのAPIトークンを入れる。さっきコピーしたので、ペーストするだけ。

## 初めて実デバイスで実行するときはこれを実行
from qiskit import IBMQ
IBMQ.save_account('自分のAPIトークン')

# アカウント情報をロードして、いま使える量子コンピュータを確認する
IBMQ.load_account()
provider = IBMQ.get_provider(hub='ibm-q')
provider.backends()

# 最もすいている(順番待ちの少ない)バックエンドを選ぶ
from qiskit.providers.ibmq import least_busy
large_enough_devices = IBMQ.get_provider().backends(filters=lambda x: x.configuration().n_qubits > 3 and not x.configuration().simulator) 
print(large_enough_devices)
real_backend = least_busy(large_enough_devices)   

print("ベストなバックエンドは " + real_backend.name())

# 上記のバックエンドで実行
job = execute(qc,real_backend)

# ジョブの実行状態を確認
from qiskit.tools.monitor import job_monitor
job_monitor(job)

# 結果を出力
real_result= job.result()
print(real_result.get_counts(qc))
plot_histogram(real_result.get_counts(qc))

これを実行すると、IBMが所有・設置しているいくつかの量子コンピュータの中で、プログラム実行時点で一番順番待ちの少ないものを自動で選び、その量子コンピュータに計算命令を出す。

一番空いている、とは言っても全く待たずにすぐ計算が行われるということはおそらく無い。数分~数十分待たされることになると思う。特に日本時間の夜にはあちらは昼だったりするので、混雑していることが多い。待つしかない。

しばらく待ったら結果がプロットされる。こうなった。

先程のシミュレータの場合は100%の確率で1という値が最終結果だったが、今回は1が92%、0が8%という結果となった。本来ならばこのORゲートによる結果は1になるはずなんだけど、量子コンピュータというのはノイズの影響を受けて本来0である量子ビットが1に変わってしまったり、またその逆が起こったりしてしまうために、最終的な計算結果は必ずしも理論通りにはならない。

いずれはそういうノイズによるエラーが発生してもうまいことそれを補正してなかったことにできる量子コンピュータができるかもしれない。世界中の研究者・開発者がそれを目指している。が、現状ではまだまだそれはできないので、このようにノイズの影響というのはそのまま目に見える形で現れる。

ちなみに、計算実行時に特に回数を指定しなかった場合は、デフォルトで1024回同じ計算が実行される。今回は最終的な量子ビットSの観測結果として「1」が942回、「0」が82回観測された。もちろんこれは偶発的なノイズによるものなので、もう一度このプログラムを実行して量子コンピュータを動かしてみると、それぞれが観測される回数は微妙に変わってくる。が、「1」の方が圧倒的に多いというのは変わらないはず。

5. まとめ

・Qiskitで量子回路のORゲートを組み、そのゲートに量子ビットを通す操作を量子コンピュータに依頼して、0 + 1の結果を得ることができた。

・量子コンピュータ実機はノイズの影響を受ける。

今回は試しにとりあえず実機を動かすところまでを書いたが、量子コンピュータの最大の特徴とも言える重ね合わせ状態や量子もつれ状態を全く使わない処理なので何の面白みもない。次回はそのあたりを活用した計算などを行っていく記事を書きたい。

返信する

メールアドレスが公開されることはありません。 が付いている欄は必須項目です