common.title

Docs
Quantum Circuit
TYTAN CLOUD

QUANTUM GAMING


Overview
Terms of service

Privacy policy

Contact
Event
Project
Research

Sign in
Sign up
common.title

[基礎]テンソルネットワークで量子もつれ回路をCNOT回路の分析

Yuichiro Minato

2023/04/03 13:28

1

こんにちは、テンソルネットがわかりづらいという質問を結構インターンでもらいましたので、ちょっとやってみます。

1、まずは普通の量子もつれ回路を作ってみます。

量子もつれ回路はH-CXで作れます。Cは制御ビットを表しています。

q- --H-- --C--
|
q- ----- --X--

今回はGoogleのtensornetworkライブラリを使いました。もちろんcuTensorNetでもいいです。

!pip install tensornetwork

まずはツールを読み込み、初期状態の状態ベクトルを準備します。HゲートとCXゲートを設定します。CXゲートは4*4ではなく、2*2*2*2のように腕の数を行列からrank4のテンソルに変更しておきます。

import numpy as np
import tensornetwork as tn

#量子ビットを準備
psi0 = tn.Node(np.array([1,0])) 
psi1 = tn.Node(np.array([1,0]))

#Hゲートを準備
H = tn.Node(np.array([[1,1],[1,-1]])/np.sqrt(2))
CX = tn.Node(np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]).reshape(2,2,2,2))

次にエッジを作ります。psi0の量子状態はまずHと繋いでからCXに。psi1の方はCXに直接繋ぎます。

繋いだら縮約を実行し、最終的に得られたノードをテンソルとして取り出します。

最終的なテンソルは状態ベクトルの形になってるはずなので、ベクトルの形に変更をします。

edge1 = psi0[0] ^ H[0]
edge2 = H[1] ^ CX[0]
edge3 = psi1[0] ^ CX[1]

con1 = tn.contract(edge1)
con2 = tn.contract(edge2)
con3 = tn.contract(edge3)

print(con3.tensor.reshape(4))

そうすると、

[0.70710678 0. 0. 0.70710678]

きちんと|00>と|11>状態の確率振幅が得られました。

2、次にCNOTゲートの仕組みをテンソルで確認

次にCNOTゲートの仕組みをテンソルで確認します。制御ビットとターゲットビットの仕組みをテンソル縮約で確認します。

--C--
|
--X--

上記のようにCを制御ビット、Xをターゲットビットとします。CXゲートは条件付きゲートですから、制御ビット側が0の時にはターゲットビットは何もせず、制御ビット側が1の時にはターゲットビットはXの操作をします。

今回は制御側の動きがどうターゲットビット側のテンソルに影響するのかみてみます。下記のように状態を置きます。制御側のビットは|0>を採用します。測定はもちろん|0>が得られるはずです。

#量子ビットと測定を準備
psi0 = tn.Node(np.array([1,0])) 
m0 = tn.Node(np.array([1,0]))

#CXゲートを準備
CX = tn.Node(np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]).reshape(2,2,2,2))

このように表現しました。

|0>--C-- M=|0>
|
--X--

エッジを作成し縮約します。

edge1 = psi0[0] ^ CX[0] 
edge2 = m0[0] ^ CX[2]

con1 = tn.contract(edge1)
con2 = tn.contract(edge2)

制御ビット側の入力(初期量子状態)と出力側(測定結果の量子状態)を縮約した結果、このテンソルは行列になります。

--U--

縮約結果のノードは行列になっているはずです。確認します。

print(con2.tensor)

[[1 0]
[0 1]]

単位行列になりました。制御側のビットの値が|0>の場合には、ターゲットビット側は単位行列が適用されています。次に制御側を|1>にしてみます。

#量子ビットを準備
psi0 = tn.Node(np.array([0,1])) 
m0 = tn.Node(np.array([0,1]))

#CXゲートを準備
CX = tn.Node(np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]).reshape(2,2,2,2))

初期状態と測定結果を変更した以外は同じです。

edge1 = psi0[0] ^ CX[0] 
edge2 = m0[0] ^ CX[2]

con1 = tn.contract(edge1)
con2 = tn.contract(edge2)

print(con2.tensor)

計算結果は、

[[0 1]
[1 0]]

Xゲートとなりました。制御ビットの値によって行列が変化するのがわかりました。最後に制御側の入力量子状態を任意の量子状態とします。

import sympy as sym
a, b = sym.symbols('a b')

#量子ビットを準備
psi1 = tn.Node(np.array([a,b]))
m1 = tn.Node(np.array([a,b]))

#CXゲートを準備
CX = tn.Node(np.array([[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]).reshape(2,2,2,2))

量子回路自体は先ほどと同じです。初期の量子状態を確率振幅[a,b]と設定しました。

edge1 = psi1[0] ^ CX[0] 
edge2 = m1[0] ^ CX[2]

con1 = tn.contract(edge1)
con2 = tn.contract(edge2)

print(con2.tensor)

計算結果は、

[[a**2 b**2]
[b**2 a**2]]

こうなりました。確かに制御側の量子ビットの量子状態によってターゲット側の行列が変化していますね。複数のマルチコントロールドゲートでも同様の計算結果が得られると思いますので、ぜひ試してみてください。

© 2025, blueqat Inc. All rights reserved