!pip --no-cache-dir install -U torch
Requirement already satisfied: torch in /opt/conda/lib/python3.10/site-packages (2.1.2)
Requirement already satisfied: filelock in /opt/conda/lib/python3.10/site-packages (from torch) (3.13.1)
Requirement already satisfied: typing-extensions in /opt/conda/lib/python3.10/site-packages (from torch) (4.5.0)
Requirement already satisfied: sympy in /opt/conda/lib/python3.10/site-packages (from torch) (1.12)
Requirement already satisfied: networkx in /opt/conda/lib/python3.10/site-packages (from torch) (3.2.1)
Requirement already satisfied: jinja2 in /opt/conda/lib/python3.10/site-packages (from torch) (3.1.2)
Requirement already satisfied: fsspec in /opt/conda/lib/python3.10/site-packages (from torch) (2023.5.0)
Requirement already satisfied: nvidia-cuda-nvrtc-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Requirement already satisfied: nvidia-cuda-runtime-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Requirement already satisfied: nvidia-cuda-cupti-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Requirement already satisfied: nvidia-cudnn-cu12==8.9.2.26 in /opt/conda/lib/python3.10/site-packages (from torch) (8.9.2.26)
Requirement already satisfied: nvidia-cublas-cu12==12.1.3.1 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.3.1)
Requirement already satisfied: nvidia-cufft-cu12==11.0.2.54 in /opt/conda/lib/python3.10/site-packages (from torch) (11.0.2.54)
Requirement already satisfied: nvidia-curand-cu12==10.3.2.106 in /opt/conda/lib/python3.10/site-packages (from torch) (10.3.2.106)
Requirement already satisfied: nvidia-cusolver-cu12==11.4.5.107 in /opt/conda/lib/python3.10/site-packages (from torch) (11.4.5.107)
Requirement already satisfied: nvidia-cusparse-cu12==12.1.0.106 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.0.106)
Requirement already satisfied: nvidia-nccl-cu12==2.18.1 in /opt/conda/lib/python3.10/site-packages (from torch) (2.18.1)
Requirement already satisfied: nvidia-nvtx-cu12==12.1.105 in /opt/conda/lib/python3.10/site-packages (from torch) (12.1.105)
Requirement already satisfied: triton==2.1.0 in /opt/conda/lib/python3.10/site-packages (from torch) (2.1.0)
Requirement already satisfied: nvidia-nvjitlink-cu12 in /opt/conda/lib/python3.10/site-packages (from nvidia-cusolver-cu12==11.4.5.107->torch) (12.3.101)
Requirement already satisfied: MarkupSafe>=2.0 in /opt/conda/lib/python3.10/site-packages (from jinja2->torch) (2.1.2)
Requirement already satisfied: mpmath>=0.19 in /opt/conda/lib/python3.10/site-packages (from sympy->torch) (1.3.0)
あんまり詳しくは書かないのですが、PyTorchを使って量子計算や機械学習を進めます。
まずは簡単な量子回路を書いてみますが、基本的には、
- 量子ビット
- 量子ゲート
- 測定
- 期待値を取るときのハミルトニアン
などあたりで表現が多少これまでの手法と異なります。
説明のためにはこれまでのような量子回路描画用のツールよりも単純にNetworkXのようなものを使ってみます。
import matplotlib.pyplot as plt
import networkx as nx
import numpy as np
G = nx.Graph()
nx.add_path(G, ["q0", "H", "C", "M0"])
nx.add_path(G, ["q1", "X", "M1"])
nx.add_path(G, ["C", "X"])
pos = {"q0":[0, 0], "H":[1, 0], "C":[2, 0], "M0":[3, 0], "q1":[0, -1], "X":[2, -1], "M1":[3, -1]}
nx.draw(G, pos, with_labels = True)
plt.show()
<Figure size 640x480 with 1 Axes>
q0とq1が量子ビットに対応し、通常は状態ベクトルが個別に|0> = [1,0] として与えられます。
Hゲートは量子ゲートなのでユニタリ行列が。CXゲートに関しては、NetworkX上ではCとXが分離していますが、通常は腕が2本、ノードが点1つの行列になっているはずですが、今回はノードが2つ、それぞれのノードに対して腕が3本ずつになっています。測定もM0とM1でノードが割り振られています。
以前でもどこかで記事を書いた気がしますが、また最初からやってみます。
import torch.optim as optim
import torch
import numpy as np
q0は|0>なので、
同様に
q1も設定します。
q0 = torch.tensor([1.,0])
q1 = torch.tensor([1,0])
次にHゲートです。こちらは行列ですので、素直に行列で実装します。
H = torch.tensor([[1.,1],[1,-1]])/np.sqrt(2)
試しにq0とHを計算するには、q0とHを行列演算すればいいのでした。
M0= torch.matmul(q0,H)
print(M0)
tensor([0.7071, 0.7071])
無事状態ベクトルが取れましたね。
続いてeinsumでも計算してみたいと思います。基本的には同じことなのですが、うまくいきました。
M0 = torch.einsum("a,ab->b", (q0, H))
print(M0)
tensor([0.7071, 0.7071])
おそらくeinsumで計算した方が簡単そうですね。
後ほど変分回路での計算もしてみたいので、勾配情報を含んで量子計算できるかみてみます。
勾配を持たせるには状態ベクトルや量子ゲートをfloatで書き直す必要がありました。
q0 = torch.tensor([1.,0.])
H = torch.tensor([[1.,1],[1,-1]],requires_grad=True)/np.sqrt(2)
計算してみます。
M0 = torch.einsum("a,ab->b", (q0, H))
print(M0)
tensor([0.7071, 0.7071], grad_fn=<ViewBackward0>)
今度はベル状態を作ってみます。
量子ビットを二つとHゲート、CXゲートを準備します。
q0 = torch.tensor([1.,0])
q1 = torch.tensor([1.,0])
H = torch.tensor([[1.,1],[1,-1]])/np.sqrt(2)
CX = torch.tensor([[1.,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]])
試しにちょっとずつ計算してみます。まずは、q0にHゲートをかけてみます。
res1 = torch.einsum("a,ab->b", (q0,H))
print(res1)
tensor([0.7071, 0.7071])
次にこの量子状態とq1をCXに入れてみます。ただ、CXはこのまま使うのではなく、テンソルに変形します。行列からテンソルにします。
CX_four = CX.reshape([2,2,2,2])
print(CX_four)
tensor([[[[1., 0.],
[0., 0.]],
[[0., 1.],
[0., 0.]]],
[[[0., 0.],
[0., 1.]],
[[0., 0.],
[1., 0.]]]])
これでorder-4のテンソルになりました。計算をしてみます。
res2 = torch.einsum("a,b,abcd->cd", (res1, q1, CX_four)).reshape(4)
print(res2)
tensor([0.7071, 0.0000, 0.0000, 0.7071])
どうでしょうか、ベル状態ができました。簡単ですね。
もちろんまとめてやっても大丈夫です。
res2 = torch.einsum("a,ab,c,bcde->de", (q0, H, q1, CX_four)).reshape(4)
print(res2)
tensor([0.7071, 0.0000, 0.0000, 0.7071])
できました。
せっかくなので量子古典の変分計算もしてみたいと思います。今回はHの代わりにRY(なるべく複素数を使いたくないので、、、)を置いてみて、変分アルゴで|11>を作ってみます。
|11>の判定として、ハミルトニアンの期待値を使ってみたいと思います。Z0+Z1の値が-2になるように学習をしたいと思います。今回は期待値を求めたいので、Zを配置したテンソルを準備します。Z0の期待値を図に書いてみると、
G = nx.Graph()
nx.add_path(G, [0,1,2,3,4,5,6])
nx.add_path(G, [7,8,10,11])
nx.add_path(G, [2,8])
nx.add_path(G, [4,10])
pos = {0:[0, 0], 1:[1, 0], 2:[2, 0], 3:[3, 0], 4:[4, 0], 5:[5, 0], 6:[6, 0], 7:[0, -1], 8:[2, -1], 10:[4, -1], 11:[6, -1], }
nx.draw(G, pos)
plt.show()
<Figure size 640x480 with 1 Axes>
同様に<psi|Z0+Z1|psi>を求めれば良いことになります。
#パラメータをランダム初期化
a = torch.tensor([np.random.rand()],requires_grad=True)
#最適化アルゴリズムを見つけてパラメータを最適化します
op = optim.Adam([a],lr=0.05)
#量子ビットを準備
q0 = torch.tensor([1.,0])
q1 = torch.tensor([1.,0])
#期待値測定用のZを準備します
Z = torch.tensor([[1.,0],[0,-1]])
#計算結果格納用のリスト
arr = []
#最適化ループを回します
for _ in range(100):
#RYゲートですが、aをうまく使って勾配を失わないようにpytorchの関数を使います。
RY = torch.tensor([[1,0],[0,1]])*torch.cos(a/2) + torch.tensor([[0,-1],[-1,0]])*torch.sin(a/2)
#途中までテンソルの計算を終わらせておきます。
net1 = torch.einsum("a,ab,c,bcde->de", (q0, RY, q1, CX_four))
net2 = torch.einsum("a,ab,c,bcde->de", (q0, RY, q1, CX_four))
#Z0とZ1の期待値をそれぞれ求めてたし合わせます。期待値が-2になれば両方のビットは1になるはず。
expt = torch.einsum("ab,cb,ac->", (net1, net2, Z)) + torch.einsum("ab,ad,bd->", (net1, net2, Z))
#期待値をリストに格納
arr.append(expt.detach().numpy())
#最適化
op.zero_grad()
expt.backward()
op.step()
#描画
plt.plot(arr)
plt.show()
<Figure size 640x480 with 1 Axes>
PyTorchでVQEができました。aのパラメータが最適化されているかどうかみます。
print(a)
tensor([3.1281], requires_grad=True)
ほぼ3.14になっていて、RYで量子ビットが1に。その後CXで両方1に。|11>が実現できました。
PyTorchの機能だけで量子計算ができました。GPUなどの活用だけでなく、シームレスに古典NNと統合できますね!以上です。