量子ゲート方式における量子計算の可視化

20EC020 上田心貴

目次

1. はじめに

1.1. 背景研究

量子計算は複素数も絡み、計算過程が複雑である。しかし、1量子ビットの状態はブロッホ球と呼ばれる球面上の1点で表せる。これを用いて量子操作を可視化できないかと考えた。

1.2. 本研究の目的

本研究は量子コンピュータのゲート方式における量子計算で行われるゲートによる操作を、ブロッホ球を用いて可視化することで量子計算の理解を深める。回転ゲートの可視化の次にCNOT(制御NOT)ゲートの可視化に試みる。

1.3. 本論文の構成

第2章では、量子計算について解説する。量子計算の理解に必要な数学の基本知識や、「なぜゲート方式で量子計算をすることが可能なのか」について、時間発展の式(シュレディンガー方程式)を用いて解説する。また、第3章で用いるProcessingというソフトについて解説する。第3章では、Processingを用いてブロッホ球に地球儀を投影させ、その状態でゲート操作を適用させることによってゲート操作結果を可視化し、各ゲート操作について解説する。第4章ではCNOT(制御NOT)ゲートを、座標点を2個用いてブロッホ球で表現できないか検討した。

2. 量子計算について

2.1. ブラケット記法(1)

 量子計算ではベクトルや行列をブラケット記法というものを用いて表すことが多い。

|ψ〉のように表すのがケットで、〈ψ|のように表されるのがブラである。N次元複素内積空間において標準基底は|0〉,|1〉,…,|n-1〉のように表される。量子計算においては、|j〉は(j + 1)番目の要素が1でそれ以外が0の列ベクトルとして扱われ、〈k|は(k + 1)番目の要素が1でそれ以外が0の行ベクトルとして扱われる。また、n次元複素内積空間の任意の列ベクトルは式(1)のように表せる。

これより, , , と表せる。〈|〉のようにブラとケットで閉じたような形のものは内積を表し、例えば

 

となる。

2.2. 量子ビット(1)

量子力学における系は、数学的にモデル化される。

 量子力学による計算とは

量子力学系は複素内積空間で表され、その系の量子状態はその空間上の単位ベクトルで表される。

これにより、|〉および|〉がある量子力学における系の状態であれば、もまたその系の状態にある。これは重ね合わせの原理と呼ばれる。量子コンピュータにおける情報の基本単位は量子ビットで、キュービット(qubit)で、0と1、その中間的な状態もとることができる。0および1に対応させる状態(基底状態)として、

を対応させると、一般的な量子の状態は

と表す。ただし、α,βは を満たす複素数である。また、本研究では量子ゲート方式による量子計算を扱っている。ゲートというのは、ある量子状態に対して変換を行う演算のことである。

2.3. 測定(1)

量子力学系から情報を得るには、その系の測定をする必要があるため、量子状態を測定することの数学的な記述が与えられている。その記述とは「状態|φ〉に対して標準基底{|j>}による測定を施すと確率|αj|2で測定値jが得られ、測定後の量子状態は得られた測定値jに対応する状態|j〉に変化する」といったものである。量子ビットの場合、{|0〉}と{|1〉}のような標準基底があるが、これらを計算基底という。

 量子状態|φ〉を標準基底{|mi〉}に対して測定するとき、状態が|mi〉に遷移する確率P(mi)はボルンの規則と呼ばれ、数式に表すと

となる。{|mi〉}が|0〉だったとき、式(3)に式(2)を用いると状態が|0〉に遷移する確率は

となる。

2.4. ブロッホ球(1)

式(2)において、α,βは複素数なので極座標表示が可能である。

とすると、

をみたすので

と表せる。このとき式(1)は

 

ただし、

とする。ここで、-|φ〉は位相因子が違うだけで同一の状態を表すことに注意すると

という状態に表現できる。これより、一般の量子状態は図1のように実3次元空間上の球上の点(cosγsinθ, sinγsinθ, cosθ)に対応するベクトルで表現できる。この球をブロッホ球と呼ぶ。

Images of ブロッホ球 - JapaneseClass.jp

図1. ブロッホ球

2.5. なぜゲート方式で量子計算をすることが可能なのか(1),(2),(3),(4)

2.5.1. ユニタリ行列とエルミート行列

ユニタリ行列とは、正方行列U

をみたし、Uの逆行列がUのときの正方行列Uである。ユニタリ行列U, Vを考えるとき、その積UVもまたユニタリ行列であることや、ユニタリ行列の固有値は絶対値が1であることが性質として知られている。また、正方行列Aのとき、Aはエルミート行列という。

2.5.2. 量子状態の時間発展と各ゲートの行列

量子力学系の時間発展はユニタリ行列で表される。Uをユニタリ行列とすると初期の量子状態を|φ(0)〉、量子状態の時間発展を|φ(t)〉とすると

と表すことができる。また、ユニタリ行列の積もまたユニタリ行列であることから、U1, U2, …, Unがあるとき、それらの積U=U1 U2Unもユニタリ行列である。ユニタリ行列U1, U2, …, Unをゲート呼ぶ。ゲートは様々なものが存在するが、本研究では主に基本的なゲートであるXゲート、Yゲート、Zゲート、H(アダマール)ゲート、Sゲート、Sゲート、Tゲート、Tゲート、そしてCNOT(制御NOT)ゲートについて触れる。これらのゲートの行列を以下に示す。

2.6. Processingについて

Processingはビジュアルアートに適したプログラム言語で、javaと同じような構文で記述ができる。Processingを使った理由は、ゲート操作に行列を用いた計算結果をθ,γに反映させることでゲート操作の可視化を図ったからである。ブロッホ球上の点を、計算結果を基に操作したかったためProcessingを用いた。点の移動だけではどのように移動したのかが分かりにくいため、本研究ではブロッホ球に地球儀を投影させた。

3. ゲート適用の可視化

地球の写真を投影したブロッホ球に各ゲートを適用させ、適用前と適用後の様子を可視化した。ブロッホ球を2つ用意し、球1をゲート適用前、球2をゲート適用後とした。ブロッホ球に投影させる地球画像は、地球画像をPythonでcsvファイルにした後、C:/pythoncode/earth_colors.csvとしてプログラムに用いた。csvファイルを出力するプログラムと変換させた地球画像を図2に示す。地球画像は、そのまま用いるとブロッホ球に投影させたときに反転してしまうため、地球画像の段階で反転させてある。地球画像のパスをC:/pythoncode/earth_R_image.jpgとした。

図2. ブロッホ球に反映させた地球の画像(パス→C:/pythoncode/earth_R_image.jpg)

3.1. Xゲートの適用

3.1.1. Xゲート適用後のθとγの求め方

   Xゲートの行列は

である。量子状態を式(3)の

とし、ゲート適用後のθ, γをそれぞれθ’, γ’とすると、

Xゲート適用後の量子状態|φ’〉は

また、式(4)とXゲートの行列を用いて計算すると

ここで、式(6)と式(7)で|0〉,|1〉の係数の比較を行うと

左の式において、両辺の絶対値を取ると

式(6)の0≤θ’≤πとθ,γ∈ℝより、絶対値を外すと

また、0≤θ’≤πより

よってθ’は

γ’は|0〉と|1〉の係数の位相差より求める。|0〉の係数をα、|1〉の係数をβとし

て表すと

となる。Xゲートを適用したときの|1〉の係数は

よりarg(β)=0となる。

よって

偏角を求めたいので

したがって、

3.1.2. Xゲート適用の可視化に用いたProcessingのプログラム

Xゲート適用の可視化に用いたProcessingのプログラムは付録に示す。

3.1.3. Xゲート適用後のブロッホ球(地球反映)

 Processingのプログラムを実行したときのスクリーンショットを図3~6に示す。

図3. Xゲート適用したときのブロッホ球(1)(左:球1 右:球2)

図4. Xゲート適用したときのブロッホ球(2) (左:球1 右:球2)

図5. Xゲート適用したときのブロッホ球(3)_下(y軸の正の方)から見る視点 (左:球1 右:球2)

ダイアグラム, 概略図
自動的に生成された説明

図.6 Xゲート適用したときのブロッホ球(3)_上(y軸の負の方)から見る視点 (左:球1 右:球2)

図3の球1の真ん中に見えるのが北極で、球2の真ん中に見えるのが南極である。図3の右:球1を見ると、図1の球1の反対側に南極があることがわかる。図4を見ると、球1は|+〉付近のアフリカ大陸が下と右に凸の形になっているが、球2は上と左に凸の形になっている。これらより、x軸を中心に180度回転していると分かる。

3.2. Yゲートの適用

3.2.1. Yゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.2.2. Yゲート適用の可視化に用いたProcessingのプログラム

Yゲート適用の可視化に用いたProcessingのプログラムを付録示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplyYGateに変換する。

3.2.3. Yゲート適用後のブロッホ球(地球反映)

 Processingのプログラムを実行したときのスクリーンショットを図7~10に示す。

図7. Yゲート適用したときのブロッホ球(1) (左:球1 右:球2)

グラフィカル ユーザー インターフェイス
低い精度で自動的に生成された説明

図8. Yゲート適用したときのブロッホ球(2) (左:球2 右:球1)

グラフィカル ユーザー インターフェイス
中程度の精度で自動的に生成された説明

図9. Yゲート適用したときのブロッホ球(3) (左:球1 右:球2)

図10. Yゲート適用したときのブロッホ球(4)_下(y軸の正の方)から見る視点 (左:球1 右:球2)

 図7,8を見ると球1に北極、球2に南極が見えるため180度回転していることがわかる。図7,8と図3,4と比べると南極の形が違うことから回転してる方向が違うことが分かる。図8の球1と図9の球2のアフリカ大陸を見ると、図8の球1はx軸の正の付近に見えるが、図9の球2はx軸の負の付近に見える。図10を見ると、球1と球2で南アメリカ大陸の形の上下左右が反転していることがわかる。これより、y軸を中心に180度回転していることがわかる。

3.3. Zゲートの適用

3.3.1. Zゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.3.2. Zゲート適用の可視化に用いたProcessingのプログラム

 Zゲート適用の可視化に用いたProcessingのプログラムを付録に示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplyZGateに変換する。

3.3.3. Zゲート適用後のブロッホ球(地球反映)

 Processingのプログラムを実行したときのスクリーンショットを図11~13に示す。

図11. Zゲート適用したときのブロッホ球(1) (左:球1 右:球2)

図12. Zゲート適用したときのブロッホ球(2) (左:球1 右:球2)

ダイアグラム
低い精度で自動的に生成された説明

図13. Zゲート適用したときのブロッホ球(3) (左:球1 右:球2)

 図11の北極を見比べると球1と球2で上下左右が反転されており、球1がy=0の平面よりも下に面積が多く存在しているのに対し、球2はy=0の平面よりも上に面積が多く存在している。これよりz軸を中心に180度回転していることがわかる。図12の球1と図13の球2のアフリカ大陸を見ても、球1ではx軸の正の付近に位置していたが球2ではx軸の負の付近に位置しているため、180度回転している様子がわかる。

3.4. Hゲートの適用

3.4.1. Hゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.4.2. Hゲート適用の可視化に用いたProcessingのプログラム

 Hゲート適用の可視化に用いたProcessingのプログラムを付録に示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplyHadamardGateに変換する。

3.4.3. Hゲート適用後のブロッホ球(地球反映)

 Processingのプログラムを実行したときのスクリーンショットを図14~18に示す。

図14. Hゲート適用したときのブロッホ球(1) (左:球1 右:球2)

グラフィカル ユーザー インターフェイス
低い精度で自動的に生成された説明

図15. Hゲート適用したときのブロッホ球(2) (左:球1 右:球2)

ダイアグラム
中程度の精度で自動的に生成された説明

図16. Hゲート適用したときのブロッホ球(3)_下(y軸の正の方)から見る視点

(左:球1 右:球2)

ダイアグラム
自動的に生成された説明

図17. Hゲート適用したときのブロッホ球(4)_上(y軸の負の方)から見る視点

(左:球1 右:球2)

 図14を見ると、球1のx軸の正の方に位置していたアフリカ大陸が球2ではz軸に位置しているため90度回転しており、上下左右も反転している。図15の球1と図16の球2の南アメリカ大陸を見ると、球1では下(y軸の正の方)の方に位置していたが球2では上の方(y軸の負の方)に位置しており、180度回転していることが分かる。これらをまとめるとx=z,y=0の直線を中心に180度回転していることがわかる。

3.5. Sゲートの適用

3.5.1. Sゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.5.2. Sゲート適用の可視化に用いたProcessingのプログラム

 Sゲート適用の可視化に用いたProcessingのプログラムを付録に示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplySGateに変換する。

3.5.3. Sゲート適用後のブロッホ球(地球反映)

 Processingのプログラムを実行したときのスクリーンショットを図18~20に示す。

グラフ, ダイアグラム
自動的に生成された説明

図18. Sゲート適用したときのブロッホ球(1) (左:球1 右:球2)

グラフィカル ユーザー インターフェイス, グラフ
自動的に生成された説明

図19. Sゲート適用したときのブロッホ球(2) (左:球1 右:球2)

ダイアグラム
自動的に生成された説明

図20. Sゲート適用したときのブロッホ球(3)_下(y軸の正の方)から見る視点 (左:球1 右:球2)

 図18を見ると、球1と球2で北極の位置は変わっておらず、向きだけ変わっている。図19の球1と図20の球2のアフリカ大陸を見ると、球1はx軸の正の方に位置しているのに対し、球2はy軸の正の方に位置していることがわかる。また、球1と球2のアフリカ大陸の形に注目すると、z軸を中心に90度回転していることがわかる。

3.6. Sゲートの適用

3.6.1. Sゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.6.2. Sゲート適用の可視化に用いたProcessingのプログラム

Sゲート適用の可視化に用いたProcessingのプログラムを付録に示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplySdGateに変換する。

3.6.3. Sゲート適用後のブロッホ球(地球反映)

Processingのプログラムを実行したときのスクリーンショットを図21~23に示す。

グラフ, ダイアグラム
中程度の精度で自動的に生成された説明

図21. Sゲート適用したときのブロッホ球(1) (左:球1 右:球2)

グラフィカル ユーザー インターフェイス, グラフ
自動的に生成された説明

図22. Sゲート適用したときのブロッホ球(2) (左:球1 右:球2)

ダイアグラム
自動的に生成された説明

図23. Sゲート適用したときのブロッホ球(3)_上(y軸の負の方)から見る視点 (左:球1 右:球2)

 図21を見ると、球1と球2で北極の位置は変わっておらず、向きだけ変わっている。図21と3.5.2. Sゲート適用の図18を比べると、球2の北極の向きが違うことがわかる。図22の球1と図23の球2のアフリカ大陸を見ると、球1はx軸の正の方に位置しているのに対し、球2はy軸の負の方に位置していることがわかる。また、球1と球2のアフリカ大陸の形に注目すると、z軸を中心に負の方向に90度回転していることがわかる。

3.7. Tゲートの適用

3.7.1. Tゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.7.2. Tゲート適用の可視化に用いたProcessingのプログラム

Tゲート適用の可視化に用いたProcessingのプログラムを付録示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplyTGateに変換する。

3.7.3. Tゲート適用後のブロッホ球(地球反映)

Processingのプログラムを実行したときのスクリーンショットを図24~26に示す。

図24. Tゲート適用したときのブロッホ球(1) (左:球1 右:球2)

図25. Tゲート適用したときのブロッホ球(2) (左:球1 右:球2)

ダイアグラム
自動的に生成された説明

図26. Tゲート適用したときのブロッホ球(3)_下(y軸の正の方)から見る視点 (左:球1 右:球2)

 図24を見ると、球1と球2で北極の位置は変わっておらず、向きだけ変わっている。図24と3.5.2. Sゲート適用の図18を比べると、球2の北極の向きが違うことがわかる。図25の球1と図26の球2のアフリカ大陸を見ると、球1はx軸の正の方に位置しているのに対し、球2はx軸とy軸の真ん中付近に位置している。加えて、図26の球1と球2のアフリカ大陸の形に注目すると、z軸を中心に45度回転していることがわかる。

3.8. Tゲートの適用

3.8.1. Tゲート適用後のθとγの求め方

   3.1.1.と同様にして求める。

3.8.2. Tゲート適用の可視化に用いたProcessingのプログラム

Tゲート適用の可視化に用いたProcessingのプログラムを付録に示す。なお、Xゲート適用のプログラムのfloat[] applyXGate(float x, float y, float z)のクラスとその呼び出しの文float[] transformed = applyXGate(x, y, z);以外は同様なため、該当箇所以外は省略する。呼び出しの文はapplyXGateをapplyTdGateに変換する。

3.8.3. Tゲート適用後のブロッホ球(地球反映)

 Processingのプログラムを実行したときのスクリーンショットを図27~26に示す。

図27. Tゲート適用したときのブロッホ球(1) (左:球1 右:球2)

グラフ, バブル チャート
自動的に生成された説明

図28. Tゲート適用したときのブロッホ球(2) (左:球1 右:球2)

図29. Tゲート適用したときのブロッホ球(3)_上(y軸の負の方)から見る視点 (左:球1 右:球2)

 図27を見ると、球1と球2で北極の位置は変わっておらず、向きだけ変わっている。図27と3.5.2. Sゲート適用の図15を比べると、球2の北極の向きが違うことがわかる。また、図27と3.7.2. Tゲート適用の図24の球2を比べると、球2が反対向きに回転していることがわかる。図28の球1と図29の球2のアフリカ大陸を見ると、球1はx軸の正の方に位置しているのに対し、球2はx軸とy軸の負の方の真ん中付近に位置している。加えて、図28の球1と球2のアフリカ大陸の形に注目すると、z軸を中心に負の方へ45度回転していることがわかる。

3.9. まとめ

Xゲートはⅹ軸を中心に180度回転、Yゲートはy軸を中心に180度回転、Zゲートはz軸を中心に180度回転、Hゲートはx=z,y=0の直線を中心に180度回転、Sゲートはz軸を中心に90度回転、Sゲートはz軸を中心に-90度回転、Tゲートはz軸を中心に45度回転、Tゲートはz軸を中心に-45度回転、することがわかった。

4. CNOT(制御NOT)ゲート表現の検討

4.1. CNOT(制御NOT)ゲート

CNOT(制御)NOTゲートの入出力の関係を表1に示す。

表1. CNOT

入力1入力1出力1出力2
|0〉|0〉|0〉|0〉
|0〉|1〉|0〉|1〉
|1〉|0〉|1〉|1〉
|1〉|1〉|1〉|0〉

  入力1を制御ビットといい、入力2はターゲットビットと呼ばれる。制御ビット(入力1)が|0〉のとき、ターゲットビット(入力2)の状態はそのまま出力される。制御ビットが|1〉のとき、ターゲットビットが反転されて出力される。CNOTゲートは

の行列で表される。2量子ビットの量子状態を表す式を

とすると、

 となる。|01〉は|0〉(|01)の0部分)が第1量子ビットで|1〉(|01)の1部分)が第2量子ビットである。γがある項を見ると、CNOTゲート適用前が|10〉で制御ビットが|1〉、ターゲットビットが|0〉であるのに対し、CNOTゲート適用後は|11〉で制御ビットが|1〉、ターゲットビットが|1〉でゲート適用前と後でターゲットビットが反転していることがわかる。

4.2. CNOTゲートの可視化の検討

  CNOTゲートはその他の回転ゲートと違い2量子ビットの操作である。そのため通常はブロッホ球の1点では表せないが、部分トレースという操作を行う事で2量子ビットを2つの1量子ビットに分解し、1つのブロッホ球上に2点で表すことができるのではないかと考えた。

4.2.1. 部分トレース

   行列A,B

としたときに行列A,Bのテンソル積というものをとると、

のように表す。行列A,Bのテンソル積の行列Bの部分トレースをすると

  と表す。行列Aの部分トレースをすると

  と表す。例えば|01〉の密度行列というものを表すとき、|01〉〈01|と表現する。これを計算すると、

となるこれらを利用し、1量子ビットのテンソル積である2量子ビットを、1量子ビット2つに分解できるのではないかと考えた。

4.2.2. CNOTゲートの可視化の検討に用いたプログラム

 回転ゲートに用いたプログラムとは違いブロッホ球に地球画像を合成しておらず、半透明な球として実装した。結論を述べると、CNOTゲートの可視化は失敗している。可視化の検討に用いたプログラムを付録図.へ、プログラムの実行結果を図30~36に示す。ベル状態は

を表している。

図30. CNOTゲート可視化プログラムの実行結果_入力|00〉

グラフ, バブル チャート
自動的に生成された説明

図31. CNOTゲート可視化プログラムの実行結果_入力|01〉

グラフ, バブル チャート
自動的に生成された説明

図32. CNOTゲート可視化プログラムの実行結果_入力|10〉

グラフ, バブル チャート
自動的に生成された説明

図33. CNOTゲート可視化プログラムの実行結果_入力|11〉

グラフ, バブル チャート
自動的に生成された説明

図34. ベル状態(CNOTゲート適用前)

グラフ, バブル チャート
自動的に生成された説明

図35. ベル状態(CNOTゲート適用後)

グラフ, バブル チャート
自動的に生成された説明

図36. 初期状態を量子ビット1,2がブロッホ球の原点にくるようにして

CNOTゲートを適用させたときの実行結果(可逆性のテスト)

先ほど述べたように、提案したCNOTゲートの可視化には失敗している。例えば図31は、入力が|01〉なので分解した後の量子ビット1(赤い座標点)に第1量子ビット、量子ビット2(青い座標点)に第2量子ビットを対応させたのだが、作成したCNOTゲートに適用させると量子ビット1と量子ビット2が逆となっている。図33も同様である。図34はベル状態(エンタングルメントの状態の一種)を再現したものだが、これにCNOTゲートを適用させると量子ビット1,2がブロッホ球の中心に移動した。初期状態をブロッホ球の中心に来るように調整してCNOTゲートを適用させた結果、図36のようになった。量子ビット2がブロッホ球上から外れてしまっている。なぜ失敗したのか。私は、部分トレースをするときの要素の欠落に原因があるのではないかと考えた。

行列A,Bのテンソル積をそれぞれの行列で部分トレースしたときに、式(8)の

が部分トレースしたときの要素として入っていない。ここで情報の欠落があったのではないかと考えた。これを解決するには欠落した要素として組み込む必要性があるが、具体的な解決策は考えられていない。

4.3. まとめ

第3章ではXゲート、Yゲート、Zゲート、Hゲート、Sゲート、Sゲート、Tゲート、Tゲートについて、ゲートを適用したときの変化を可視化するために地球を模したブロッホ球を用いることを考え、Processingで表現した。ブロッホ球に量子状態を表す点を複数描画し、それぞれに緯度と経度、その地点の色を反映させた。その後色を持った点のそれぞれにゲートを適用させ、変化を確認した。その結果、これらのゲートによる変換はどのような回転をさせるものなのかが見て分かるようになったと私は考える。

CNOTゲートの可視化は、2量子ビットの表現をすることから考え、部分トレースを用いることで2量子ビットを2つの2量子ビットに分解することで1つのブロッホ球で表現できるのではないかと考えた。しかし、提案した方法ではCNOTゲートの可視化はできなかった。部分トレースで2量子ビットを1量子ビット2つに分解させたときに欠落してしまう要素があり、それが失敗に繋がったのではないかという問題点が見つけられた。

1量子ビットの計算はブロッホ球のように3次元で表せるため、視覚的にも計算的にも理解しやすいかもしれないが、2量子ビットになるだけで視覚化は困難になり、計算も複雑になることが理解できた。

5. 参考文献

6. 付録

  
  import csv
  from PIL import Image
  # 画像ファイルのパス
  earth_image_path = r"C:\pythoncode\earth_R_image.jpg"
  output_csv_path = r"C:\pythoncode\earth_colors.csv" # 出力CSVファイルのパス
  # 画像を開く
  try:
  image = Image.open(earth_image_path)
  print("Image loaded successfully")
  except Exception as e:
  print(f"Failed to load image: {e}")
  exit()
  # 画像サイズを取得
  width, height = image.size
  print(f"Image size: {width}x{height}")
  # CSVにデータを保存
  try:
  with open(output_csv_path, mode="w", newline="") as file:
  writer = csv.writer(file)
   
  # ヘッダー行を追加
  writer.writerow(["Latitude", "Longitude", "Color (R, G, B)"])
   
  # データ行を追加
  for y in range(0, height, 10): # y座標を10ピクセル刻み
  for x in range(0, width, 10): # x座標を10ピクセル刻み
  if 0 <= x < width and 0 <= y < height:
  color = image.getpixel((x, y)) # RGB形式で取得
  latitude = 90 - (y / height) * 180 # 緯度を計算
  longitude = (x / width) * 360 - 180 # 経度を計算
  writer.writerow([latitude, longitude, color]) # 書き込み
  print(f"Data successfully saved to {output_csv_path}")
  except Exception as e:
  print(f"Failed to save data to CSV: {e}")
  
  

図.37 csv変換ファイル(python)

  
  float r = 150; // 球の半径
  PShape blochSphere1, blochSphere2;
  Table earthData; // CSVデータ用
  ArrayList<float[]> originalCoordinates = new ArrayList<>(); // 元の座標点のリスト
  ArrayList<float[]> transformedCoordinates = new ArrayList<>(); // ゲート適用後の座標点のリスト
  ArrayList<Integer> colors = new ArrayList<>(); // 各点の色リスト
  // カメラ位置のための変数
  float camD = 700;
  float camtheta = 0;
  float camgamma = 0;
  float camthetaStep = radians(15);
  float camgammaStep = radians(15);
  void setup() {
  size(1000, 1000, P3D);
  noStroke();
  lights();
  // CSVデータの読み込み
  String filePath = "C:/pythoncode/earth_colors.csv"; // CSVの絶対パス
  earthData = loadTable(filePath, "header"); // ファイルをロード
   
  if (earthData == null) {
  println("Error: CSV file could not be loaded.");
  exit(); // ファイルがロードできない場合はプログラムを終了
  }
  println("CSV file loaded successfully.");
   
  loadEarthData(); // データの読み込み
  }
  void draw() {
  background(255);
   
  // カメラの設定
  float camCenterX = width / 2;
  float camCenterY = height / 2;
  float camCenterZ = height / 2;
  float camX = camD * sin(camgamma) * cos(camtheta);
  float camY = camD * sin(camgamma) * sin(camtheta);
  float camZ = camD * cos(camgamma);
   
  camera(camX + camCenterX, camY + camCenterY, camZ + camCenterZ, camCenterX, camCenterY, 0, 0, 1, 0);
  translate(width / 2 - 300, height / 2, 0);
  ortho();
  int step = 16; // 点を間引く間隔(例: 20ステップごとに描画)
  // 球1(元の座標点)を描画
  for (int i = 0; i < originalCoordinates.size(); i += step) {
  float[] original = originalCoordinates.get(i);
  int c = colors.get(i);
  float x = original[0];
  float y = original[1];
  float z = original[2];
  fill(c);
  noStroke();
  pushMatrix();
  translate(x, y, z);
  sphere(3);
  popMatrix();
  }
  // 球2(ゲート適用後の座標点)を描画
  pushMatrix();
  translate(600, 0, 0); // 400ピクセル右に移動
  for (int i = 0; i < transformedCoordinates.size(); i += step) {
  float[] transformed = transformedCoordinates.get(i);
  int c = colors.get(i);
  float x = transformed[0];
  float y = transformed[1];
  float z = transformed[2];
  fill(c);
  noStroke();
  pushMatrix();
  translate(x, y, z);
  sphere(3);
  popMatrix();
  }
  popMatrix();
   
  // 3D座標軸を描画
  drawAxes(0);
  drawAxes(600);
   
  // 球の直線x=zを描画
  drawLineXZ(0);
  drawLineXZ(600);
   
  // xy平面における円を描画(|0>と|1>を分ける)
  draw0or1(0);
  draw0or1(600);
   
  // 軸名描画
  drawShaft(0);
  drawShaft(600);
   
  // 球の座標軸とブロッホ球の交点を描画
  drawRedCoordinates(0);
  drawRedCoordinates(600);
  }
  // CSVデータを読み込み、元の座標、色、および変換後の座標をリストに格納
  void loadEarthData() {
  for (TableRow row : earthData.rows()) {
  try {
  // 緯度・経度データを文字列として取得してアポストロフィを削除
  String latString = row.getString("Latitude").replace("'", "").trim();
  String lonString = row.getString("Longitude").replace("'", "").trim();
  // 緯度・経度を数値に変換
  Float latitude = Float.parseFloat(latString);
  Float longitude = Float.parseFloat(lonString);
  if (latitude == null || longitude == null) {
  println("Error: Missing latitude or longitude data in row: " + row);
  continue; // この行をスキップ
  }
  // 球面座標に変換
  float theta = radians(90 - latitude);
  float gamma = radians(longitude);
  float x = r * sin(theta) * cos(gamma);
  float y = r * sin(theta) * sin(gamma);
  float z = r * cos(theta);
  originalCoordinates.add(new float[] {x, y, z});
  // Xゲート適用後の座標を計算
  float[] transformed = applyXGate(x, y, z);
  transformedCoordinates.add(transformed);
  // 色データの処理
  String colorString = row.getString("Color (R, G, B)");
  if (colorString == null) {
  println("Error: Missing color data in row: " + row);
  continue;
  }
  colorString = colorString.replaceAll("[()]", "");
  String[] rgb = colorString.split(",");
  if (rgb.length != 3) {
  println("Error: Invalid color format in row: " + row);
  continue;
  }
  int r = int(rgb[0].trim());
  int g = int(rgb[1].trim());
  int b = int(rgb[2].trim());
  int c = color(r, g, b);
  colors.add(c);
  } catch (Exception e) {
  println("Error processing row: " + e.getMessage());
  }
  }
  }
  // Xゲートを適用して新しい座標を返す
  float[] applyXGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // Xゲートの変換式
  float a = sin(theta / 2);
  float newTheta = 2 * acos(a);
   
  float newGamma = -gamma;
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  // 球の座標軸を描画
  void drawAxes(float xOffset) {
  pushMatrix();
  stroke(0);
  translate(xOffset, 0, 0);
  line(0, 0, 0, 300, 0, 0); // x軸
  line(0, 0, 0, 0, 300, 0); // y軸
  line(0, 0, 0, 0, 0, 300); // z軸
  popMatrix();
  }
  // 球の座標軸とブロッホ球の交点を描画
  void drawRedCoordinates(float xOffset) {
  noStroke();
  fill(255, 0, 0);
   
  pushMatrix();
  translate(xOffset + r, 0, 0);
  sphere(8);
  popMatrix();
   
  pushMatrix();
  translate(xOffset - r, 0, 0);
  sphere(8);
  popMatrix();
   
  pushMatrix();
  translate(xOffset, r, 0);
  sphere(8);
  popMatrix();
   
  pushMatrix();
  translate(xOffset, -r, 0);
  sphere(8);
  popMatrix();
   
  pushMatrix();
  translate(xOffset, 0, r);
  sphere(8);
  popMatrix();
   
  pushMatrix();
  translate(xOffset, 0, -r);
  sphere(8);
  popMatrix();
  }
  // 球の直線x=zを描画
  void drawLineXZ(float xOffset) {
  pushMatrix();
  stroke(0);
  translate(xOffset, 0, 0);
  line (-200, 0, -200, 200, 0, 200);
  popMatrix();
  }
  // xy平面における円を描画(|0>と|1>を分ける)
  void draw0or1 (float xOffset) {
  pushMatrix();
  stroke(0);
  noFill();
  ellipse(xOffset, 0, 2 * r, 2 * r);
  popMatrix();
  }
  // 軸名描画
  void drawShaft(float xOffset) {
  pushMatrix();
  fill(0);
  textSize(35);
  text("x",xOffset + 315 , 0, 0);
  text("y",xOffset, 315, 0);
  text("z",xOffset, 0, 315);
  text("|0>",xOffset + 15, -15, 200);
  text("|1>",xOffset -15, -15, - 200);
  text("|+>",xOffset + 215, -15, 0);
  text("|->",xOffset - 215, -15, 0);
  text("|i>",xOffset + 15, 15 + 200, 0);
  text("|-i>",xOffset + 15, -15 - 200, 0);
  popMatrix();
  }
  void keyPressed(){
  // カメラの移動
  if (keyCode == UP) camtheta -= camthetaStep;
  if (keyCode == DOWN) camtheta += camthetaStep;
  if (keyCode == LEFT) camgamma -= camgammaStep;
  if (keyCode == RIGHT) camgamma += camgammaStep;
  }

図.38 Xゲート適用のプログラム


  // Yゲートを適用して新しい座標を返す
  float[] applyYGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // Yゲートの変換式
  float a = sin(theta / 2);
  float newTheta = 2 * acos(a);
   
  float b = sin(gamma);
  float c = -cos(gamma);
  float d = 0;
  float e = cos(theta / 2);
  float newGamma = atan2(e, d) - atan2(c, b);
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.39 Yゲート適用のプログラム

  
  // Zゲートを適用して新しい座標を返す
  float[] applyZGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // Zゲートの変換式
  float newTheta = theta;
   
  float newGamma = gamma + PI;
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.40 Zゲート適用のプログラム

  
  // アダマールゲートを適用して新しい座標を返す
  float[] applyHadamardGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // アダマールゲートの変換式
  float a = sqrt((1 + sin(theta) * cos(gamma)) / 2);
  float newTheta = 2 * acos(a);
   
  float b = cos(theta / 2) + (cos(gamma) * sin(theta / 2));
  float c = sin(gamma) * sin(theta / 2);
  float d = cos(theta / 2) - (cos(gamma) * sin(theta / 2));
  float e = -sin(gamma) * sin(theta / 2);
  float newGamma = atan2(e, d) - atan2(c, b);
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.41 Hゲート適用のプログラム

  
  // Sゲートを適用して新しい座標を返す
  float[] applySGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // Sゲートの変換式
  float newTheta = theta;
  float newGamma = gamma + (PI / 2);
   
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.42 Sゲート適用のプログラム

  
  // S†ゲートを適用して新しい座標を返す
  float[] applySdGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // S†ゲートの変換式
  float newTheta = theta;
  float newGamma = gamma + ((3 * PI) / 2);
   
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.43 Sゲート適用のプログラム

  
  // Tゲートを適用して新しい座標を返す
  float[] applyTGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // Tゲートの変換式
  float newTheta = theta;
  float newGamma = gamma + (PI / 4);
   
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.44 Tゲート適用のプログラム

  
  // T†ゲートを適用して新しい座標を返す
  float[] applyTdGate(float x, float y, float z) {
  // 球面座標に変換
  float theta = acos(z / r);
  float gamma = atan2(y, x);
  // T†ゲートの変換式
  float newTheta = theta;
  float newGamma = gamma + ((7 * PI) / 4);
   
  // 新しい座標を計算
  float newX = r * sin(newTheta) * cos(newGamma);
  float newY = r * sin(newTheta) * sin(newGamma);
  float newZ = r * cos(newTheta);
  return new float[] {newX, newY, newZ};
  }
  
  

図.45 Tゲートの適用のプログラム

  
  // 2量子ビットを1つのブロッホ球に描画
  PVector blochPoint1, blochPoint2;
  float r =150;
  // カメラ位置のための変数
  float camD = 700;
  float camtheta = 0;
  float camgamma = 0;
  float camthetaStep = radians(15); // カメラ移動のステップ
  float camgammaStep = radians(15); // カメラ移動のステップ
  // CNOTゲートの行列を定義
  float[][] cnotMatrix = {
  {1, 0, 0, 0},
  {0, 1, 0, 0},
  {0, 0, 0, 1},
  {0, 0, 1, 0}
  };
  // CNOTゲートを適用する関数
  float[][][] applyCNOT(float[][][] state) {
  float[][][] newState = new float[4][4][2];
  for (int i = 0; i < 4; i++) {
  for (int j = 0; j < 4; j++) {
  for (int k = 0; k < 2; k++) {
  newState[i][j][k] = 0;
  for (int m = 0; m < 4; m++) {
  for (int n = 0; n < 4; n++) {
  newState[i][j][k] += state[m][n][k] * cnotMatrix[m][i] * cnotMatrix[n][j];
  }
  }
  }
  }
  }
  return newState;
  }
  void setup() {
  size(1000, 1000, P3D);
  hint(ENABLE_DEPTH_SORT); // 奥行き順に描画
  }
  void draw() {
  background(255);
  lights(); // 照明効果を追加
   
  // 初期状態を|10⟩
  float[][][] state = {
  {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}, // c00
  {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}}, // c01
  {{0.0, 0.0}, {0.0, 0.0}, {1.0, 0.0}, {0.0, 0.0}}, // c10
  {{0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}, {0.0, 0.0}} // c11
  };
  // CNOTゲートを適用
  state = applyCNOT(state);
   
  println("CNOT適用後の状態:");
  printState(state);
   
  // 部分トレースを計算して各量子ビットの密度行列を求める
  float[][] rho1 = partialTrace(state, 1); // 量子ビット1
  float[][] rho2 = partialTrace(state, 2); // 量子ビット2
   
  printDensityMatrix(rho1, "量子ビット1");
  printDensityMatrix(rho2, "量子ビット2");
  // 密度行列からθとφを計算
  blochPoint1 = calculateCoordinates(rho1); // 量子ビット1の座標点
  blochPoint2 = calculateCoordinates(rho2); // 量子ビット2の座標点
   
  println("量子ビット1の座標: " + blochPoint1);
  println("量子ビット2の座標: " + blochPoint2);
  // カメラ設定と描画処理はそのまま
  float camX = camD * sin(camgamma) * cos(camtheta);
  float camY = camD * sin(camgamma) * sin(camtheta);
  float camZ = camD * cos(camgamma);
  camera(camX, camY, camZ, 0, 0, 0, 0, 1, 0);
  ortho();
  pushMatrix();
  noStroke();
  fill(0, 150,150, 125);
  sphere(r); // 球体
  popMatrix();
   
  // 1つのブロッホ球に量子ビット1と量子ビット2の状態を描画
  pushMatrix();
  drawBlochSphere(blochPoint1, blochPoint2); // 量子ビット1と2の座標点
  popMatrix();
   
  // 3D座標軸を描画
  drawAxes(0);
  draw0or1(0);
  drawShaft(0);
   
  // 3D座標軸を描画
  drawAxes(0);
  // xy平面における円を描画(|0>と|1>を分ける)
  draw0or1(0);
   
  // 軸名描画
  drawShaft(0);
  }
  // 部分トレースの計算関数
  float[][] partialTrace(float[][][] state, int qubit) {
  float[][] rho = new float[2][2];
  if (qubit == 1) {
  rho[0][0] = state[0][0][0] + state[2][2][0];
  rho[1][1] = state[1][1][0] + state[3][3][0];
  rho[0][1] = state[0][1][0] + state[2][3][0];
  rho[1][0] = state[1][0][0] + state[3][2][0];
  } else if (qubit == 2) {
  rho[0][0] = state[0][0][0] + state[1][1][0];
  rho[1][1] = state[2][2][0] + state[3][3][0];
  rho[0][1] = state[0][2][0] + state[1][3][0];
  rho[1][0] = state[2][0][0] + state[3][1][0];
  }
  return rho;
  }
  PVector calculateCoordinates(float[][] rho) {
  float x = 2 * rho[0][1]; // 修正:虚数成分を考慮する場合は + rho[1][0]
  float y = 2 * rho[1][0]; // 修正:虚数成分を考慮する場合は + rho[0][1]
  float z = rho[0][0] - rho[1][1];
  return new PVector(x, y, z);
  }
  // 1つのブロッホ球に量子ビット1と量子ビット2の座標点を描画
  void drawBlochSphere(PVector point1, PVector point2) {
  // 量子ビット2(青い点)
  pushMatrix();
  stroke(0, 0, 255, 150); // 青色
  strokeWeight(20);
  point(point2.x * r, point2.y * r, point2.z * r);
  popMatrix();
  // 量子ビット1(赤い点)
  pushMatrix();
  stroke(255, 0, 0, 150); // 赤色
  strokeWeight(20);
  point(point1.x * r, point1.y * r, point1.z * r);
  popMatrix();
  }
  // 球の座標軸を描画
  void drawAxes(float xOffset) {
  pushMatrix();
  stroke(0);
  strokeWeight(1);
  translate(xOffset, 0, 0);
  line(0, 0, 0, 300, 0, 0); // x軸
  line(0, 0, 0, 0, 300, 0); // y軸
  line(0, 0, 0, 0, 0, 300); // z軸
  popMatrix();
  }
  // xy平面における円を描画(|0>と|1>を分ける)
  void draw0or1 (float xOffset) {
  pushMatrix();
  stroke(0);
  fill(0,0,0,70);
  ellipse(xOffset, 0, 2 * r, 2 * r);
  popMatrix();
  }
  // 軸名描画
  void drawShaft(float xOffset) {
  pushMatrix();
  fill(0);
  textSize(35);
  text("x",xOffset + 315 , 0, 0);
  text("y",xOffset, 315, 0);
  text("z",xOffset, 0, 315);
  text("|0>",xOffset + 15, -15, 200);
  text("|1>",xOffset -15, -15, - 200);
  text("|+>",xOffset + 215, -15, 0);
  text("|->",xOffset - 215, -15, 0);
  text("|i>",xOffset + 15, 15 + 200, 0);
  text("|-i>",xOffset + 15, -15 - 200, 0);
  popMatrix();
  }
  void printState(float[][][] state) {
  for (int i = 0; i < 4; i++) {
  for (int j = 0; j < 4; j++) {
  println("state[" + i + "][" + j + "] = (" + state[i][j][0] + ", " + state[i][j][1] + ")");
  }
  }
  }
  void printDensityMatrix(float[][] rho, String label) {
  println(label + " 密度行列:");
  for (int i = 0; i < 2; i++) {
  for (int j = 0; j < 2; j++) {
  print(rho[i][j] + " ");
  }
  println();
  }
  }
  void keyPressed() {
  // カメラの移動
  if (keyCode == UP) {
  camtheta -= camthetaStep; // カメラを上に移動
  } else if (keyCode == DOWN) {
  camtheta += camthetaStep; // カメラを下に移動
  } else if (keyCode == LEFT) {
  camgamma -= camgammaStep; // カメラを左に移動
  } else if (keyCode == RIGHT) {
  camgamma += camgammaStep; // カメラを右に移動
  }
  }
  
  

図.46 提案したCNOTゲート可視化プログラム