Nobisuke
Dekisugi
RAG
Privacy policy
2024/01/08 13:25
Copy !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)
Display all output >>>
あんまり詳しくは書かないのですが、PyTorchを使って量子計算や機械学習を進めます。 まずは簡単な量子回路を書いてみますが、基本的には、
などあたりで表現が多少これまでの手法と異なります。
説明のためにはこれまでのような量子回路描画用のツールよりも単純にNetworkXのようなものを使ってみます。
Copy 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でノードが割り振られています。
以前でもどこかで記事を書いた気がしますが、また最初からやってみます。
Copy import torch.optim as optim import torch import numpy as np
q0は|0>なので、 同様に q1も設定します。
Copy q0 = torch.tensor([1.,0]) q1 = torch.tensor([1,0])
次にHゲートです。こちらは行列ですので、素直に行列で実装します。
Copy H = torch.tensor([[1.,1],[1,-1]])/np.sqrt(2)
試しにq0とHを計算するには、q0とHを行列演算すればいいのでした。
Copy M0= torch.matmul(q0,H)
Copy print(M0)
tensor([0.7071, 0.7071])
無事状態ベクトルが取れましたね。
続いてeinsumでも計算してみたいと思います。基本的には同じことなのですが、うまくいきました。
Copy M0 = torch.einsum("a,ab->b", (q0, H)) print(M0)
tensor([0.7071, 0.7071])
おそらくeinsumで計算した方が簡単そうですね。 後ほど変分回路での計算もしてみたいので、勾配情報を含んで量子計算できるかみてみます。 勾配を持たせるには状態ベクトルや量子ゲートをfloatで書き直す必要がありました。
Copy q0 = torch.tensor([1.,0.]) H = torch.tensor([[1.,1],[1,-1]],requires_grad=True)/np.sqrt(2)
計算してみます。
Copy M0 = torch.einsum("a,ab->b", (q0, H)) print(M0)
tensor([0.7071, 0.7071], grad_fn=<ViewBackward0>)
今度はベル状態を作ってみます。 量子ビットを二つとHゲート、CXゲートを準備します。
Copy 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ゲートをかけてみます。
Copy res1 = torch.einsum("a,ab->b", (q0,H)) print(res1)
tensor([0.7071, 0.7071])
次にこの量子状態とq1をCXに入れてみます。ただ、CXはこのまま使うのではなく、テンソルに変形します。行列からテンソルにします。
Copy CX_four = CX.reshape([2,2,2,2]) print(CX_four)
tensor([[[[1., 0.],
[0., 0.]],
[[0., 1.],
[0., 0.]]],
[[[0., 0.],
[0., 1.]],
Display all output >>>
これでorder-4のテンソルになりました。計算をしてみます。
Copy res2 = torch.einsum("a,b,abcd->cd", (res1, q1, CX_four)).reshape(4) print(res2)
tensor([0.7071, 0.0000, 0.0000, 0.7071])
どうでしょうか、ベル状態ができました。簡単ですね。 もちろんまとめてやっても大丈夫です。
Copy 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の期待値を図に書いてみると、
Copy 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>を求めれば良いことになります。
Copy #パラメータをランダム初期化 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のパラメータが最適化されているかどうかみます。
Copy print(a)
tensor([3.1281], requires_grad=True)
ほぼ3.14になっていて、RYで量子ビットが1に。その後CXで両方1に。|11>が実現できました。 PyTorchの機能だけで量子計算ができました。GPUなどの活用だけでなく、シームレスに古典NNと統合できますね!以上です。
© 2024, blueqat Inc. All rights reserved