Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
quantum-kittens
GitHub Repository: quantum-kittens/platypus
Path: blob/main/translations/ja/ch-states/atoms-computation.ipynb
4073 views
Kernel: Python 3

計算の原子

量子コンピューターのプログラミングは、今では誰もが自宅から快適におこなうことができます。

でも、何を作ればよいのでしょうか?そもそも量子プログラムとは?量子コンピューターとは何でしょうか?

これらの疑問は、今日の標準のデジタルコンピューターと比較することで答えを見出すことが出来ます。残念ながら、ほとんどの人は実際にデジタルコンピューターの動作原理を理解していません。この記事では、まずはデジタルコンピューターの基本原則から見ていきます。その後、量子コンピューティングの話にスムーズに移行できるよう、量子コンピューター上で計算を行うときと同じツールを使用します。

以下は、このページのコードを使用するために必要なPythonのコードです。

from qiskit import QuantumCircuit, assemble, Aer from qiskit.visualization import plot_histogram

1. 情報をビットに分割する

最初に知っておくべきことは、ビットの概念です。ビットは世界で最も単純なアルファベットになるように設計されています。 0と1の2文字だけで、あらゆる情報を表現できます。

例えば数字です。皆さんは数字を表す際は0、1、2、3、4、5、6、7、8、および9の10個の文字列を使用して表現するでしょう。この文字列では、各桁は10の何乗かを表しています。例えば、9213と書くときは、

9000+200+10+39000 + 200 + 10 + 3

または、10の累乗を強調する方法で表現されます。

(9×103)+(2×102)+(1×101)+(3×100)(9\times10^3) + (2\times10^2) + (1\times10^1) + (3\times10^0)

通常、このシステムは数10に基づいて使用しますが、他の数に基づいたシステムも同様に簡単に使用できます。たとえば、2進数システムは2に基づいています。これは、2つの文字0と1を使用して、数値を2のべき乗の倍数として表現することを意味します。たとえば、9213は10001111111101になります。

9213=(1×213)+(0×212)+(0×211)+(0×210)+(1×29)+(1×28)+(1×27) ,,,+(1×26)+(1×25)+(1×24)+(1×23)+(1×22)+(0×21)+(1×20)9213 = (1 \times 2^{13}) + (0 \times 2^{12}) + (0 \times 2^{11})+ (0 \times 2^{10}) +(1 \times 2^9) + (1 \times 2^8) + (1 \times 2^7) \ ,,, + (1 \times 2^6) + (1 \times 2^5) + (1 \times 2^4) + (1 \times 2^3) + (1 \times 2^2) + (0 \times 2^1) + (1 \times 2^0)

ここでは、10、100、1000などではなく、2、4、8、16、32などの倍数として数値を表現しています。

from qiskit_textbook.widgets import binary_widget binary_widget(nbits=5)

バイナリ文字列として知られるこれらのビット文字列は、単なる数値以上のものを表すために使用できます。たとえば、ビットを使用してテキストを表現する方法があります。使用する任意の文字、数字、または句読点について、この表を使用して、最大8ビットの対応するストリングを検索できます。変換コードは任意に定義されたものですが、広く合意された標準になっており、この記事をインターネット経由で送信するのにも使われています。

このようにしてすべての情報はコンピューターで表現されています。数字、文字、画像、音声のいずれであっても、すべてバイナリ文字列の形で存在します。

私たちの標準的なデジタルコンピューターのように、量子コンピューターはこの同じ基本的な考え方に基づいています。主な違いは、量子的に操作できるビットの変形である 量子ビットを使用していることです。この教科書の残りの部分では、量子ビットとは何か、それらが何ができるか、どのようにそれを行うかを探ります。ただし、このセクションでは、量子についてはまったく話しません。そのため、量子ビットを通常のビットとして使用します。

練習問題

以下の文を完成させてください:

  1. 10進数の「5」は2進数で[[101|11001|110|001]]です。

  2. コンピューターに1ビットがある場合、[[2|1|3|4]]個の異なる状態になる可能性があります。

  3. コンピューターに2ビットがある場合、[[4|3|2|8]]個の異なる状態になる可能性があります。

  4. コンピューターに8ビットがある場合、[[256|128|342]]個の異なる状態になる可能性があります。

  5. nnビットがある場合、それらは[[2n2^n|n×2n×2|n2n^2]]個の異なる状態になる可能性があります。

2. ダイアグラムを使った計算

量子ビットを使用する場合もビットを使用する場合も、入力を必要な出力に変換するためにそれらを操作する必要があります。少数のビット用の非常に単純なプログラムでは、このプロセスを回路図と呼ばれる図で表すと便利です。入力は左側にあり、出力は右側にあり、演算はその間の不可解な記号によって表されます。これらの演算に用いられる記号は、主に歴史的な背景から「ゲート」と呼ばれます。

標準的なビットベースのコンピューターでの回路の例を次に示します。これが具体的に何を意味するかまではわからなくても、これらの回路がどのようなものか、イメージだけをもっていただければ大丈夫です。

image1

量子コンピューターも基本的には同じ考え方ですが、入力、出力、および演算に使用する記号は異なる表現を使います。上記のビットベースのコンピューターで示した例と同じプロセスを表す量子回路を次に示します。

image2

このセクションの残りの部分では、回路の構築方法を説明します。最後に、上記の回路を作成する方法、それが何をするのか、なぜ有用なのかを理解しましょう。

3. はじめての量子回路

量子回路を作成するには3つのステップが必要です。最初に入力をエンコードし、次に実際の計算を行い、最後に出力を抽出します。最初の量子回路では、最後の工程に焦点を当てます。まず、8つの量子ビットと8つの出力を持つ回路を作成します。

qc_output = QuantumCircuit(8)

この回路はqc_outputと呼ばれ、QiskitのQuantumCircuitを使用して作成されます。 QuantumCircuitは、量子回路内の量子ビット数を引数として取ります。

量子回路での出力の抽出は、 measure_all()と呼ばれる操作を使用します。各測定は、特定の量子ビットに対して指定した出力ビットに出力するよう指示します。コマンドqc_output.measure_all()は、回路qc_outputの各量子ビットに測定を追加し、出力を書き込むためのいくつかの古典ビットも追加します。

qc_output.measure_all()

回路の中に何かが追加されたので、作成した回路の中身をみてみましょう。

qc_output.draw(initial_state=True)
Image in a Jupyter notebook

量子ビットは常に初期化され、出力0を返します。上記の回路の量子ビットには何もしていないので、これはまさに測定した場合に得られる結果です。これは、回路を何度も実行し、結果をヒストグラムにプロットすることで確認できます。結果は常に00000000 :各量子ビットについて0であることがわかります。

sim = Aer.get_backend('aer_simulator') result = sim.run(qc_output).result() counts = result.get_counts() plot_histogram(counts)
Image in a Jupyter notebook

何度も実行して結果をヒストグラムとして表示する理由は、量子コンピューターの結果にランダム性がある可能性があるためです。今回は量子的なことは何もしていないので、確実に00000000の結果が得られます。

この結果は、理想的な量子コンピューターが何をするかを計算する標準的なコンピューターである量子シミュレーターから得られることに注意してください。シミュレーションは少数の量子ビット(〜30量子ビット)でのみ可能ですが、それでも、最初の量子回路を設計するときに非常に便利なツールです。実際のデバイスで実行するには、 Aer.get_backend('aer_simulator')を使用するデバイスのバックエンドオブジェクトに置き換えるだけです。

4. 例:加算回路を作成する

4.1入力のエンコード

次に、別々のバイナリ文字列を入力としてエンコードする方法を見てみましょう。これには、NOTゲートと呼ばれるものが必要です。これは、コンピューターで実行できる最も基本的な操作です。ビット値を反転するだけで01になり、 10になります。量子ビットの場合、NOTの仕事をするのはxと呼ばれる操作です。

以下では、エンコードのジョブ専用の新しい回路を作成し、qc_encodeと呼びます。ここでは量子ビットの数のみを指定します。

qc_encode = QuantumCircuit(8) qc_encode.x(7) qc_encode.draw()
Image in a Jupyter notebook

結果の抽出は、以前の回路qc_outputを使用して実行できます。

qc_encode.measure_all() qc_encode.draw()
Image in a Jupyter notebook

こうしてふたつの回路を組み合わせて実行して結果を表示させることができます。

sim = Aer.get_backend('aer_simulator') result = sim.run(qc_encode).result() counts = result.get_counts() plot_histogram(counts)
Image in a Jupyter notebook

ご覧のとおり、今回は 10000000 という結果が出力されました。

反転したビットは、量子ビット7に由来し、文字列の左端にあります。これは、Qiskitが文字列のビットに右から左に番号を付けるためです。この慣習に違和感を覚えたなら、心配しないでください。文字列のビットにどういう順番で番号を付けるかは好みが分かれるところで、他の教科書だと違う順番に番号を付けている場合もあるでしょう。Qiskitでは文字列のビットに右から左に番号を付けるということだけ覚えておいてください。いずれにせよ、量子ビットを使って数字を表す場合、Qiskitの方法には利点があります。なぜなら、私たちが慣れ親しんでいるバイナリ表現と同様、量子ビット7は、数に2^7がいくつあるかを示してくれるからです。つまり、量子ビット7の反転(0→1)操作は、8ビットコンピューター上で[[128|256|64|32]]という数字をエンコードするのと同じことなのです。

今度は自分で別の番号を書いてみてください。たとえば、あなたの年齢です。検索エンジンを使用して、数値が2進数でどのように表示されるかを調べ(「0b」が含まれている場合は無視します)、128歳未満の場合は左側に0を追加します。

qc_encode = QuantumCircuit(8) qc_encode.x(1) qc_encode.x(5) qc_encode.draw()
Image in a Jupyter notebook

これでコンピューターへの情報のエンコードの仕方がわかりました。次はエンコードした情報の処理方法、エンコードした入力からのアウトプットの出力の仕方についてです。

4.2 足し算の仕方を思い出してみよう

入力から出力を得るには、解く問題が必要です。基本的な算数からおさらいをしましょう。小学校で、比較的大きな数を足し算をするときは、扱いやすい大きさに分解しました。例えば以下の問題はどうやって解きますか?

9213 + 1854 = ????

ここでは 一桁ずつ右から順番に足していきます。まずは3+4

9213 + 1854 = ???7

そして 1+5

9213 + 1854 = ??67

つづいて 2+8=10 結果が2桁となったため、1を保持して次の桁に繰り上げを行う必要があります。

9213 + 1854 = ?067 ¹

そして最後に 9+1+1=11を計算することで答えを求めることができます。

9213 + 1854 = 11067 ¹

これは単純な足し算の例かもしれませんが、すべてのアルゴリズムの基本的な原理を示しています。アルゴリズムが数学的な問題を解決するように設計されているか、テキストや画像を処理するように設計されているかに関係なく、私たちは常に大きなタスクを小さくシンプルなステップに分解します。

コンピューターで実行するには、アルゴリズムを可能な限り小さくて最も簡単な手順にコンパイルする必要があります。これらがどのように見えるかを見るために、上記の足し算の問題をもう一度、ただし今度はバイナリでやってみましょう。

10001111111101 + 00011100111110 = ??????????????

2番目の文字列の左端に余分な0がたくさんついていることに注目してください。これは、2つの文字列の長さを揃えるためです。

最初のタスクは、右端の1 + 0を実行することです。バイナリでは、任意の数体系と同様に、答えは1です。2番目の列の0 + 1に対しても同じ結果が得られます。

10001111111101 + 00011100111110 = ????????????11

次に1+1=2ですが、 バイナリでは、数値2は 10と記述されるため、2ビット必要です。つまり、10進数の10の場合と同様に、1を保持する必要があります。

10001111111101 + 00011100111110 = ???????????011 ¹

次の桁では1+1+1を計算します. 三個の数を合計する必要があるのでコンピューターにとってはやや複雑な処理になってきました。しかしこれも毎回二個のビットの足し算という、より単純なオペレーションにコンパイルすることができます。 まずは最初の二つの1から足していきます。

1 + 1 = 10

つづいて、この 10と最後の1を足す必要があります。これは、いつも通りそれぞれ同じ長さの文字列として並べて上下を足すことで実行できます。

10 + 01 = 11

答えは 11(3としても知られています)です。

ここで残りの問題に戻ります。 上記の答え11はさらに繰り上がりを生みます。

10001111111101 + 00011100111110 = ??????????1011 ¹¹

ここで再び1+1+1を行う必要がでてきました。しかし、私たちはすでにこの問題の解き方を知っていますので、大したことではありません。

実はここから先の問題の解き方を我々はすでに知っています。残っている桁の計算を二つのビットの足し算に分解すると、計算が必要な組合せは四つしかないためです。 四つの計算結果を次に示します(一貫性を保つために、すべての回答を2ビットで記述します)。

0+0 = 00 (in decimal, this is 0+0=0) 0+1 = 01 (in decimal, this is 0+1=1) 1+0 = 01 (in decimal, this is 1+0=1) 1+1 = 10 (in decimal, this is 1+1=2)

これは半加算器(half adder)と呼ばれています。私たちが量子ビットでつくるコンピューターも、この半加算器さえ実装できれば、それらをつなぎ合わせることでどのような数でも足すことができるようになるのです。

4.3 Qiskitで足し算を行う

それでは、Qiskitを使って独自の半加算器を作成してみましょう。この回路には、入力をエンコードする部分、アルゴリズムを実行する部分、および結果を抽出する部分が含まれます。最初の入力をエンコードする部分は、新しい入力を使用するたびに変更する必要がありますが、残りは常に同じままです。

half adder implemented on a quantum circuit

追加する2つのビットは、量子ビット0と1にエンコードされます。上記の例では、これらの量子ビットの両方で1をエンコードしているため、 1+1の解を求めます。結果は2ビットの文字列になります。これは量子ビット2と3から読み取られ、それぞれ古典ビット0と1に格納されます。

コンピューティングの基本操作は、論理ゲートで行われます。これまでに回答を手動で書き出すためにNOTゲートを使いましたが、半加算器を作成するのにはこれだけでは十分ではありません。コンピューターに実際の計算を行わせるには、さらに強力なゲートが必要になります。

どのようなゲートが必要になってくるのかをみるために、半加算器がすべきことをもう一度見てみましょう。

0+0 = 00 0+1 = 01 1+0 = 01 1+1 = 10

これら4つの答えすべての右端のビットは、足し合わせる2つのビットが同じか異なるかによって決定されます。そのため、2つのビットが等しい0+01+1の場合、答えの右端のビットが[[0|1]]になります。異なるビットを足し合わせる0+11+0の場合、右端のビットは[[1|0]]です。

この部分を正しくするには、2ビットが異なるかどうかを判断できるものが必要です。従来、デジタル計算の研究では、これはXORゲートと呼ばれていました。

入力1入力2XOR出力
000
011
101
110

量子コンピューティングにおいては, XORゲートの仕事は制御NOTゲートが行います。長い名前なので、通常は単にCNOTと呼びます。 Qiskitでは更に短く cxと記述します。回路図の中では、下図のように描かれています。

qc_cnot = QuantumCircuit(2) qc_cnot.cx(0,1) qc_cnot.draw()
Image in a Jupyter notebook

これは、量子ビットのペアに適用されます。 1つは制御量子ビットとして機能します(小さなドットが付いたもの)。もう1つは、ターゲット量子ビットとして機能します(内部に+がある大きな円)。

CNOTの効果を説明する方法はいくつかあります。 一つは、2つの入力ビットを調べて、それらが同じか異なるかを確認することです。次に、ターゲット量子ビットに回答を書き込みます。2つの入力ビットが同じ場合はターゲットは 0になり、異なる場合は 1になります。

CNOTを説明する別の方法は、コントロールが 1の場合、ターゲットに対してNOTを行い、それ以外は何もしないと言うことです。この説明は、前の説明同様に有効です(ゲートの名前もここからつけられています)。

取り得る入力をそれぞれ試して、CNOTを試してみてください。たとえば、入力 01でCNOTをテストする回路を次に示します。

qc = QuantumCircuit(2,2) qc.x(0) qc.cx(0,1) qc.measure(0,0) qc.measure(1,1) qc.draw()
Image in a Jupyter notebook

この回路を実行すると、出力が 11であることがわかります。次のいずれかの理由からそうなることが考えられます。

  • CNOTは入力値が互いに異なることを検出して 1を出力しています。これは、量子ビット1の状態(ビット列の左側にあることを思い出してください)を上書きし、 0111に変換することで行います。

  • CNOTは、量子ビット0が状態 1であることを確認し、量子ビット1にNOTを適用します。これにより、量子ビット1の01に変換されます。そして、01 11に変換します。

CNOTゲートのすべての可能な入力と対応する出力を示す表を次に示します。

入力(q1 q0)出力(q1 q0)
0000
0111
1010
1101

半加算器の場合、いずれの入力も結果で上書きしたくありません。そこで結果を異なる量子ビットのペアに書き込みたいと思います。これは2つのCNOTを使用することで実現できます。

qc_ha = QuantumCircuit(4,2) # encode inputs in qubits 0 and 1 qc_ha.x(0) # For a=0, remove this line. For a=1, leave it. qc_ha.x(1) # For b=0, remove this line. For b=1, leave it. qc_ha.barrier() # use cnots to write the XOR of the inputs on qubit 2 qc_ha.cx(0,2) qc_ha.cx(1,2) qc_ha.barrier() # extract outputs qc_ha.measure(2,0) # extract XOR value qc_ha.measure(3,1) qc_ha.draw()
Image in a Jupyter notebook

半加算器の完成まであと半分の道のりとなりました。出力のもう1つのビット、つまり、量子ビット3が残っています。

4つの取り得る足し算の答えをもう一度見ると、このビットが 0ではなく1であるケースが1つ: 1 + 1 = 10しかないことに気付くでしょう。追加するビットが両方とも1の場合にのみ発生します。

出力のこの部分を計算するには、コンピューターに両方の入力が1であるかどうかを確認させることができます。そうである場合、そしてそうである場合にのみ、量子ビット3でNOTゲートを実行する必要があります。これにより、この場合にのみ必要な値1に反転し、必要な出力が得られます。

これには、新しいゲートが必要です。CNOTのようですが、制御量子ビットが1つではなく2つになります。これは、両方の制御量子ビットの状態が 1の場合にのみ、ターゲット量子ビットに対してNOTを実行します。この新しいゲートはトフォリと呼ばれ、ブール論理ゲート上のANDゲートに相当します。

QiskitではToffoliは ccx というコマンドで実装されます。

qc_ha = QuantumCircuit(4,2) # encode inputs in qubits 0 and 1 qc_ha.x(0) # For a=0, remove the this line. For a=1, leave it. qc_ha.x(1) # For b=0, remove the this line. For b=1, leave it. qc_ha.barrier() # use cnots to write the XOR of the inputs on qubit 2 qc_ha.cx(0,2) qc_ha.cx(1,2) # use ccx to write the AND of the inputs on qubit 3 qc_ha.ccx(0,1,3) qc_ha.barrier() # extract outputs qc_ha.measure(2,0) # extract XOR value qc_ha.measure(3,1) # extract AND value qc_ha.draw()
Image in a Jupyter notebook

この例では、2つの入力ビットが両方とも1であるため、1+1を計算しています。何が得られるか見てみましょう。

qobj = assemble(qc_ha) counts = sim.run(qobj).result().get_counts() plot_histogram(counts)
/home/divs/.local/lib/python3.8/site-packages/qiskit/utils/deprecation.py:62: DeprecationWarning: Using a qobj for run() is deprecated as of qiskit-aer 0.9.0 and will be removed no sooner than 3 months from that release date. Transpiled circuits should now be passed directly using `backend.run(circuits, **run_options). return func(*args, **kwargs)
Image in a Jupyter notebook

結果は 「2」 のバイナリ表現である「10」です。有名な数学的問題 1+1 を解決できるコンピューターを構築しました!

ここで残る3つの足し算の入力で同じ事を試してみると、アルゴリズムがそれらに対しても正しい結果を与えることを示すことができます。

半加算器は足し算に必要な要素をすべて含んでいます。NOT, CNOTそしてToffoliゲートを用いることで、どのような大きさの数字の組合せでも足し合わせることができるプログラムを構築することができます。

この3つのゲートは、足し算だけでなくコンピューティングにおけるすべてを行うのに十分です。実際、CNOTなしでも実行できます。値が1のビットを作るために本当に必要なのはNOTゲートだけです。トフォリゲートは本質的には数学の世界における原子のようなもので、すべての問題解決手法をコンパイルできる最も単純な要素と言えます。

このように、量子計算は原子を分割して行っていきます。

import qiskit.tools.jupyter %qiskit_version_table
/home/divs/.local/lib/python3.8/site-packages/qiskit/aqua/__init__.py:86: DeprecationWarning: The package qiskit.aqua is deprecated. It was moved/refactored to qiskit-terra For more information see <https://github.com/Qiskit/qiskit-aqua/blob/main/README.md#migration-guide> warn_package('aqua', 'qiskit-terra')