Menu
Yao.jlのロゴ

Yao.jlで量子エンタングルメントを理解したい(前編)

目標と必要知識

 必要知識

  • アダマール変換(Hゲート)や初歩の量子計算を理解している
  • 量子の重ね合わせがなんとなく分かっている
  •  目標

  • エンタングルメントをなんとなく理解する
  • Yao.jlのチュートリアルの1を理解する
  • エンタングルメントをYao.jlで作れる

  • エンタングルメントの不可解さの具体的な説明に関しては、いつかできたらと思います。 また、Julia言語や行列(登場はしますが)は触れたこと無くても差し支えないです。

    タイトルをネタっぽくしてみようにも、需要も必要もありませんでした。 こんにちは。ここではタイトルの通り、 Julia言語の量子計算ライブラリである Yao.jlのチュートリアルを行いつつ、 2, 3, 4bitの量子エンタングルメントを作ります。ロゴでかい。 誰でもわかるようにやさしく書いたはず……!

     ソースコードはこちらに保管しております。 大したものではありませんが、適宜お使いください。また、私の実行環境は以下のとおりです。

  • Linux mint 20
  • Julia ver1.5.0
  • vscode
  • ほんとは、JunoとかJupyterでやりたかった……

    目次

    1. Juliaを動かす。
    2. 量子エンタングルメントとは
    3. 使用するゲート
    4. 2qubitで量子エンタングルメントを作る
    5. 数式で理解する
    6. あとがき

    vscodeでJulia言語を動かす

     ここのリンクから、バイナリ取ってくるだけで最低限のREPLは動きます。 私は、vscodeで動かしているので、vscodeの拡張機能を追加する必要がありました。Julia Language Supportというやつです。 上の編集部分で[Ctrl] + [Enter]を押すと一行ごとにREPLで実行されます。すごい。 Juliaプログラミングクックブックには記載がなかったが、なぜかバイナリにpathを通さないと動かなかった。

     あと、Ubuntuにパッケージとして追加されていました。 sudo apt install julia でいけるようです。

     Julia言語の動作が上手く行かなかった場合は、Python及びGUIの量子計算ライブラリである Blueqatで行うと良いと思います。 こちらには詳細なドキュメントもございますので、そちらをご参照されるとわかりやすいと思います。

     それでは、Let's えんたんぐるめんと! ですの!!!

    量子エンタングルメントとは

     とりあえず、wikipediaを見てみましょう。

    量子多体系において現れる、古典確率では説明できない相関やそれに関わる現象
    古典確率……? なんかいろいろ抽象的。これには続きがあって、量子情報理論においては以下の通り。
    LOCC(局所量子操作及び古典通信)で増加しない多体間の相関 wikipedia「量子もつれ」閲覧日:2020/8/31
    なるほど、全くわからんというわけで、「なんらかの相関があるのだな」 ということを念頭にして考えていきましょう。

    使用するゲート

      一応、軽くゲートについて説明します。

     その1アダマール変換 \[ H:= \frac{1}{\sqrt{2}} \pmatrix{1 & 1 \\ 1 & -1} \] というやつです。よく等しい振幅の重ね合わせを生成するときに使われます。 実際の計算の例をあげると、 \[ H |0 \rangle = \frac{1}{\sqrt{2}} \pmatrix{1 & 1 \\ 1 & -1} \pmatrix{1 \\ 0} = \frac{1}{\sqrt{2}} \pmatrix{1 \\ 1} = \frac{1}{\sqrt{2}} (|0 \rangle + |1 \rangle) \] \[ H |1 \rangle = \frac{1}{\sqrt{2}} \pmatrix{1 & 1 \\ 1 & -1} \pmatrix{0 \\ 1} = \frac{1}{\sqrt{2}} \pmatrix{1 \\ -1} = \frac{1}{\sqrt{2}} (|0 \rangle - |1 \rangle) \] といった形になり、0と1が50%ずつ観測されるまさに量子! シュレディンガーの猫! って感じのゲートです。 このHゲートを作用させた結果はとても重要でよくでてきますので、結果だけでも覚えておきましょう。

     その2、Pauli Xゲート。ここではXゲートと記しています。 \[ X := \pmatrix{0 & 1 \\ 1 & 0} \] で表せます。ブロッホ球のX座標 \( \frac{\pi}{2} \) の変換と言えます。 わかりやすく言うと、\( |0 \rangle \)と\( |1 \rangle \)の係数を入れかえます。つまり、 \[ X |0 \rangle = |1 \rangle 及び X |1 \rangle = |0 \rangle \] となるので、古典コンピュータでのNOTゲートともいえるでしょう。

    2qubitで量子エンタングルメントを作る

     量子エンタングルメントでよく言われるのは、 片方の量子ビットがわかるともう片方の量子ビットも100%わかる、ということです。 すごい不思議な現象ですね。

     日常生活に例えると、2つのコインがありランダムでコインの表裏が決定するとき、 片方分かった瞬間にもう片方もわかってしまう、ということです。 予知能力みたいですが、これは確かに強い相関といえます。

     なんとなくイメージがつかめたところで、量子回路を見ていきましょう。 まず、2量子ビットで単純なエンタングルメントであるベル状態を形成する回路を見てみます。

     なんか、横線が引いてあって、文字の書かれた四角とか置いてあります。 現状、多くの量子プログラミングはこのように、楽譜のような回路を記述していく形です。

     初期状態はどちらも \( |0 \rangle \) となっています。 状態Aでは第一量子ビットにHゲートを作用させ、状態Bでは第二量子ビットではCNOTゲートを作用させています。 CNOTゲートがなんなのかは、コードを見ればわかるのでわからない方も読みすすみましょう。 それでは、早速コード書いてどのような結果が観測できるのか、確認しましょう。

    ベル状態のコードを書こう!

     必要なパッケージをダウンロードし、ライブラリを準備します。

    ] add Plots, Yao, Stasbase
     # [Backspace]
    using Plots
    using Yao
    using Statsbase
    
    ]でJuliaのパッケージモードになりますが、 REPLで[Backspace]を押すと、デフォルトのモードに戻ります。 初回は時間がかかりますので、さらーっと読んでお待ちください。 ちなみに、JuliaではPythonと同じように#でコメントをつけます。

     はじめに回路を作っていきます。

    n = 2 # bit数
    Bell_circuit = chain(
        n,
        put(1=>H),
        control(1, 2=>X),
    )
    
    nは量子ビット数ですね。2つ目の構造体のようなもので実際に回路を設計していきます。 Yao.jlではゲートの設置の関数やcircuit関数のはじめの引数にUInt型、すなわち自然数をおくことで量子ビット数を設定することができます。 他にも、量子ビット数を設定することは可能ですが、こちらも後日触れるかもです。

     この変数Bell_circuitはcurcuitという関数で代入されていますが、これはYao.jlで定義されているcircuit型を生成します。 circuit関数の中身にゲート生成する関数を書いていくと記入した順に回路が形成されていきます。

     続いて、ゲートを設置していきます。 1量子ビットに作用するゲートの設置はput関数で行います。 put(1=>H)は第二量子ビットにアダマール変換Hを作用させるということになります。
     次に用いているのはcontrol関数です。 control関数はドキュメントではcontrol(n, ctrl_locs, target)となっています。 第一引数のnは量子ビット数なので、circuit関数内では省略可能ですね。 コードにおける第一引数では制御ビットを指定しており、同第二引数では標的ビットに何を作用させるかを指定します。 第二引数では、put関数と同じようにゲートを設置します。

     ところで、制御ビットとか標的ビットってなんでしょうか?  端的に言うと、制御ビットが\( |0 \rangle \)ならば標的ビットは変化せず、\( |1 \rangle \)ならば設定したゲートが標的ビットに作用されるというものです。 例えば、今回のcontrol(1, 2=>X)の場合、第一量子ビットが\( |1 \rangle \)ならば、Xゲートを作用させるわけですね。

     また、第一引数ではタプルを指定することにより、制御ビット増やすことが可能です。

    Toffoli Gate 3qubit
        julia> control(3, (1,2), 3=>X)
        nqubits: 3
        control(1, 2)
        └─ (3,) X
    
    この場合は、第一、第二量子ビットがどちらも\( |1 \rangle \)ならば、第三量子ビットにXゲートが作用されます。 これはトフォリゲートと呼ばれます。AND演算が可能となるので、論理演算で結構でてくるゲートですね。

     ということは今回の場合、control(1, 2=>X)というのはCNOTを指していることになりますね。これを行列で示すと、 \[ CNOT = \pmatrix{1 & 0 & 0 & 0 \\ 0 & 1 & 0 & 0 \\ 0 & 0 & 0 & 1 \\ 0 & 0 & 1 & 0 } \] となります。

     さてさて、いよいよ回路をシミュレーションしていきます! 以下のコードを順に実行していきましょう。

    apply!(zero_state(n), Bell_circuit)    # いらないが、チュートリアルに従うとこうなる
    
    results = zero_state(n) |> Bell_circuit |> r->measure(r, nshots = 1000)
    
    hist = fit(Histogram, Int.(results), 0:2^n)
    
    Bell_graff = bar(hist.edges[1] .- 0.5, hist.weights, legend =:none)
    

     一行目は、第一引数の初期状態のビットを回路Bell_circuitに適用させるということです。 この行事体はなくても動作します。関数zero_state(n)は量子ビット\( |0 \rangle \)をn個生成するものです。 結果を得るには二行目のようにする必要があります。A |> BはAをBに代入するもので、パイプ演算子といいます。 measureは文字通り、量子ビットを測定して古典ビットに変換する関数ですね。 nshotsで何回シミュレートするか明示します。今回は1000回です。

     こうして、パイプ演算子を2つ用いて得た結果をヒストグラムで示すために三行目のようにします。 resultの型は文字列であるString型の配列なので、fit関数の第二引数では.で各要素にアクセスしIntで整数型に変換します。 第三引数で、ヒストグラムのバーの数を指定しています。最後に4行目でヒストグラムをプロットします。 hist.edges[1] .- 0.5で横軸の目盛りをいい感じにしてます。
    hist.weights, legend =:noneはそれぞれ、「変数histの各ビット列の出現回数」、「ラベルなし」を示しています。

     先程のコードを実行すると、上のようなヒストグラムが得られると思います。 横軸の目盛りはそれぞれ、\( 00, 01, 10, 00 \)の量子ビット列を意味しているので、 結果は量子ビット列\( 00, 11 \)が大体500回ずつでたことになります。

     量子の観測はランダムな確率なはずなのに、この回路だと\( 00, 11 \)の2つしか観測できません。 また、強固な相関である、片方の量子ビットが0ならもう片方も0、1でもまた然り、を満たしている感じがしますね。 次節ではこれを数式で考えていきます。

    数式で理解する

     さて、ゲートがどのような行列なのか、結果がどうなったのかを踏まえて考察していきましょう。  回路は図で示すと以下のようなものでした。

     初期の状態は\( |00 \rangle \)ですね。図の状態Aでは第一量子ビットである\( |0 \rangle \)にHを作用さています。 これは先程のHの定義に基づくと、 \[ \begin{eqnarray*} H |0 \rangle \otimes |0 \rangle &=& \frac{1}{\sqrt{2}} (|0 \rangle + |1 \rangle ) \otimes |0 \rangle \\ &=& \frac{1}{\sqrt{2}}(|00 \rangle + |10 \rangle ) \end{eqnarray*} \] となります。この状態は \( |00 \rangle , |01 \rangle \)の2つの状態の重ね合わせができていると言えます。 現状は、先程の結果のような強い相関はありません。

     続いて、状態Bを見てみましょう。 CNOTゲートは制御ビットが\( |1 \rangle \)ならば、標的ビットにXゲートを作用させるものでした。 先程の計算結果を元にCNOTを作用させると、 \[ \frac{1}{\sqrt{2}}(|00 \rangle + |10 \rangle ) \xrightarrow{CNOT} \frac{1}{\sqrt{2}}(|00 \rangle + |11 \rangle ) \] 重なっている量子ビットの一つである \( |00 \rangle \)は第一量子ビットが0なので、何も変化しません。 一方の\( |10 \rangle \)は第一量子ビットが1なので、第二量子ビットの0にNOTゲートが作用され、1になります。

     よって、計算結果からも\( |00 \rangle, |11 \rangle \)それぞれの可能性が\( \frac{1}{\sqrt{2}} \)の二乗、 \( \frac{1}{2} \)ということが導き出されました。 また、片方で0が観測されたらもう片方の量子ビットも0、片方で1が観測されたらもう片方の量子ビットも1、というふうにもつれているのがわかります。

     量子コンピュータの計算速度が早いと言われる根拠の一端がここにあります。 このような状況の判別が古典コンピュータだと2^2で4回であるのに対し、こちらは一度で状況を全て示せるということになるからです。 このベル状態自体はあまりに単純で計算量も少ないので、実社会には使えませんが、わかりやすいので良く量子計算の入門書にでてきます。 今回のは相関の一例という認識が適切でしょうか。 様々なもつれがありますが、いろいろなアルゴリズムを考える中であんまり意識すること少ない気がします。

    あとがき

     「3qubit, 4qubitと拡張していく」をしたかったのですが、長くなったのでこのあたりで一旦切ろうと思います。 コード自体はさきほどのコードを保管した場所にあるので、ぜひ取り組んでみてください。 それらの数式や説明、後編は近い内にできたらと思います。

     ちょっと興味ある中学生でも理解できるように書いたつもりですが、いささか冗長な感じが否めなせんね。 少しでも皆様の参考になれば嬉しいです。次回は京都に安く言った話でも書くかもしれません。 その近いうちに今回の後編を書く予定です。それでは。

    参考文献

    yao.jl tutorial
    loading...