common.title

Overview
Service overview
Terms of service

Privacy policy

Contact

Sign in
Sign up
common.title

テンソルネットワークの目的別実行

Yuichiro Minato

2023/12/07 07:03

#テンソルネットワーク

ここでは、cuTensorNetを利用する際に、量子計算を実行する際にちょっとコツが必要なので最後確認したいと思います。

テンソル

テンソルネットワークはノードとエッジで構成されており、各ノード/エッジには特定の意味があります。

単一のノードはスカラー量を表します。

 *

単一のノードに一本の脚がある場合、それはベクトルを表します。

 *-

単一のノードに二本の脚がある場合、それは行列を表します。

-*-

脚の数が多い場合は、k階のテンソルを表します。

-*-
 |

縮約

異なるテンソルを接続して縮約を行うことで計算を実行できます。

例)ベクトルと行列を含む計算を行うと、結果はベクトルになります。

-*- -*
-*--*
-*

例)行列と行列を含む計算を行うと、結果は行列になります。

-*- -*-
-*--*-
-*-

分解

さまざまなアルゴリズムを使用してテンソルを分解することもできますが、その中で最も代表的なものの一つがSVD(特異値分解)です。

-*-
-*- -*-

Google Tensornetwork ライブラリ

ライブラリを使用して実行しましょう。

https://github.com/google/TensorNetwork

!pip install tensornetwork
Requirement already satisfied: tensornetwork in /opt/conda/lib/python3.10/site-packages (0.4.6)

Requirement already satisfied: numpy>=1.17 in /opt/conda/lib/python3.10/site-packages (from tensornetwork) (1.23.5)

Requirement already satisfied: graphviz>=0.11.1 in /opt/conda/lib/python3.10/site-packages (from tensornetwork) (0.20.1)

Requirement already satisfied: opt-einsum>=2.3.0 in /opt/conda/lib/python3.10/site-packages (from tensornetwork) (3.3.0)

Requirement already satisfied: h5py>=2.9.0 in /opt/conda/lib/python3.10/site-packages (from tensornetwork) (3.8.0)

Requirement already satisfied: scipy>=1.1 in /opt/conda/lib/python3.10/site-packages (from tensornetwork) (1.10.1)

まず、ツールをロードして、二つのベクトルを準備しましょう。

import numpy as np import tensornetwork as tn a = tn.Node(np.ones((10,))) b = tn.Node(np.ones((10,)))

次に、それらの間にエッジを持つノードを接続します。

edge = a[0] ^ b[0]

最後に、エッジを指定して収縮を行うと、計算が完了します。

final_node = tn.contract(edge) print(final_node.tensor)
10.0

ベクトルと行列を縮約してベクトルに

a = tn.Node(np.ones((5))) b = tn.Node(np.ones((5,5))) edge = a[0] ^ b[0] final_node = tn.contract(edge) print(final_node.tensor)
[5. 5. 5. 5. 5.]

行列と行列を縮約して行列に

a = tn.Node(np.ones((5,3))) b = tn.Node(np.ones((5,2))) edge = a[0] ^ b[0] final_node = tn.contract(edge) print(final_node.tensor)
[[5. 5.]

 [5. 5.]

 [5. 5.]]

テンソルネットワークを量子回路のバックエンドとして

テンソルネットワークのベクトルを量子ビットにマッピングし、行列やテンソルを量子ゲートに適用することで量子回路をシミュレートできます。

今回はquimbを使用して量子回路をシミュレートします。

!pip install quimb
Requirement already satisfied: quimb in /opt/conda/lib/python3.10/site-packages (1.4.0)

Requirement already satisfied: numpy>=1.17 in /opt/conda/lib/python3.10/site-packages (from quimb) (1.23.5)

Requirement already satisfied: scipy>=1.0.0 in /opt/conda/lib/python3.10/site-packages (from quimb) (1.10.1)

Requirement already satisfied: numba>=0.39 in /opt/conda/lib/python3.10/site-packages (from quimb) (0.57.0)

Requirement already satisfied: psutil>=4.3.1 in /opt/conda/lib/python3.10/site-packages (from quimb) (5.9.5)

Requirement already satisfied: cytoolz>=0.8.0 in /opt/conda/lib/python3.10/site-packages (from quimb) (0.12.1)

Requirement already satisfied: tqdm>=4 in /opt/conda/lib/python3.10/site-packages (from quimb) (4.65.0)

Requirement already satisfied: toolz>=0.8.0 in /opt/conda/lib/python3.10/site-packages (from cytoolz>=0.8.0->quimb) (0.12.0)

Requirement already satisfied: llvmlite<0.41,>=0.40.0dev0 in /opt/conda/lib/python3.10/site-packages (from numba>=0.39->quimb) (0.40.0)
#%config InlineBackend.figure_formats = ['svg'] import quimb as qu import quimb.tensor as qtn from collections import Counter

80量子ビットを準備して、GHZ状態を作成します。

#number of qubits N = 80 #initialization of circuit circ = qtn.Circuit(N) #apply Hgate to the first qubit circ.h(0) # making GHZ using CX chain for i in range(N-1): circ.cx(i, i+1) # get sampling from quantum state Counter(circ.sample(1))
Counter({'00000000000000000000000000000000000000000000000000000000000000000000000000000000': 1})

構築した量子回路を使用してサンプルを取ります。

%%time # get 100samples Counter(circ.sample(100))
CPU times: user 750 ms, sys: 12.1 ms, total: 762 ms

Wall time: 823 ms
Counter({'00000000000000000000000000000000000000000000000000000000000000000000000000000000': 54,

         '11111111111111111111111111111111111111111111111111111111111111111111111111111111': 46})

より大きな回路を作りましょう。

circ = qtn.Circuit(10) for i in range(10): circ.apply_gate('H', i, gate_round=0) for r in range(1, 9): # even pairs for i in range(0, 10, 2): circ.apply_gate('CNOT', i, i + 1, gate_round=r) # Y-rotations for i in range(10): circ.apply_gate('RZ', 1.234, i, gate_round=r) # odd pairs for i in range(1, 9, 2): circ.apply_gate('CZ', i, i + 1, gate_round=r) # X-rotations for i in range(10): circ.apply_gate('RX', 1.234, i, gate_round=r) # h gate for i in range(10): circ.apply_gate('H', i, gate_round=r + 1) circ
<Circuit(n=10, num_gates=252, gate_opts={'contract': 'auto-split-gate', 'propagate_tags': 'register'})>

次に、量子回路を描きましょう。

circ.psi.draw(color=['PSI0', 'H', 'CNOT', 'RZ', 'RX', 'CZ'])
<Figure size 600x600 with 1 Axes>output

ここでは、量子ゲートは実際には行列で表されることが期待されますが、ユニタリ行列は3階のテンソルに分解されます。テンソルネットワークでは、ユニタリ行列が常に使用されるわけではありません。さらに、量子ビットの状態ベクトルは、各量子ビットのベクトルが独立して描かれて表されます。

状態ベクトル、振幅、期待値、サンプリング

テンソルネットワークを使用して量子回路の計算を行う際には、状態ベクトル、確率振幅、期待値、またはサンプリングなど、事前に目的を決定する必要があります。

状態ベクトル

すべてのノードを収縮させると、単一のベクトルが得られます。

circ.to_dense()
[[ 0.022278+0.044826j]

 [ 0.047567+0.001852j]

 [-0.028239+0.01407j ]

 ...

 [ 0.016   -0.008447j]

 [-0.025437-0.015225j]

 [-0.033285-0.030653j]]

振幅

ビット文字列を指定しテンソルを接続することで、対応する確率振幅を得ることができます。

circ.amplitude('0000011111')
(0.004559038599179494+0.02661946964089579j)

期待値

同じ量子回路を、対応するハミルトニアンの単一項を挿入して操作することで、期待値を得ることができます。

circ.local_expectation(qu.pauli('X') & qu.pauli('Z'), (4, 5))
(-0.07785735654723336+3.903127820947816e-17j)

サンプル

サンプリングを行うことができます。

for item in circ.sample(1): print(item)
1110101110

Kerasにおけるテンソルネットワークを使用したニューラルネットワークの高速化

このGoogleの記事から、テンソルネットワークを使用した行列分解のテクニックを学び、ニューラルネットワークを高速化する方法を学びます。

https://blog.tensorflow.org/2020/02/speeding-up-neural-networks-using-tensornetwork-in-keras.html

通常の全結合ニューラルネットワーク

!pip install tensorflow
Collecting tensorflow

  Downloading tensorflow-2.15.0-cp310-cp310-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (475.2 MB)

     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 475.2/475.2 MB 821.1 kB/s eta 0:00:0000:0100:01

[?25hCollecting absl-py>=1.0.0 (from tensorflow)

  Downloading absl_py-2.0.0-py3-none-any.whl (130 kB)

     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 130.2/130.2 kB 2.3 MB/s eta 0:00:00a 0:00:01

[?25hCollecting astunparse>=1.6.0 (from tensorflow)

  Downloading astunparse-1.6.3-py2.py3-none-any.whl (12 kB)

Collecting flatbuffers>=23.5.26 (from tensorflow)

  Downloading flatbuffers-23.5.26-py2.py3-none-any.whl (26 kB)
import tensorflow as tf import tensornetwork as tn import matplotlib.pyplot as plt tn.set_default_backend("tensorflow")

次に、それらのノードを「a」と「b」という名前のノードに分解するTNlayerを作成します。

class TNLayer(tf.keras.layers.Layer): def __init__(self): super(TNLayer, self).__init__() # Create the variables for the layer. self.a_var = tf.Variable(tf.random.normal( shape=(32, 32, 2), stddev=1.0/32.0), name="a", trainable=True) self.b_var = tf.Variable(tf.random.normal(shape=(32, 32, 2), stddev=1.0/32.0), name="b", trainable=True) self.bias = tf.Variable(tf.zeros(shape=(32, 32)), name="bias", trainable=True) def call(self, inputs): # Define the contraction. # We break it out so we can parallelize a batch using # tf.vectorized_map (see below). def f(input_vec, a_var, b_var, bias_var): # Reshape to a matrix instead of a vector. input_vec = tf.reshape(input_vec, (32,32)) # Now we create the network. a = tn.Node(a_var, backend="tensorflow") b = tn.Node(b_var, backend="tensorflow") x_node = tn.Node(input_vec, backend="tensorflow") a[1] ^ x_node[0] b[1] ^ x_node[1] a[2] ^ b[2] # The TN should now look like this # | | # a --- b # \ / # x # Now we begin the contraction. c = a @ x_node result = (c @ b).tensor # To make the code shorter, we also could've used Ncon. # The above few lines of code is the same as this: # result = tn.ncon([x, a_var, b_var], [[1, 2], [-1, 1, 3], [-2, 2, 3]]) # Finally, add bias. return result + bias_var # To deal with a batch of items, we can use the tf.vectorized_map # function. # https://www.tensorflow.org/api_docs/python/tf/vectorized_map result = tf.vectorized_map( lambda vec: f(vec, self.a_var, self.b_var, self.bias), inputs) return tf.nn.relu(tf.reshape(result, (-1, 1024)))

まず、それぞれ1024ノードを持つ2つのレイヤーを検討しましょう。

Dense = tf.keras.layers.Dense fc_model = tf.keras.Sequential( [ tf.keras.Input(shape=(2,)), Dense(1024, activation=tf.nn.swish), Dense(1024, activation=tf.nn.swish), Dense(1, activation=None)]) fc_model.summary()
Model: "sequential_5"

_________________________________________________________________

 Layer (type)                Output Shape              Param #   

=================================================================

 dense_13 (Dense)            (None, 1024)              3072      

                                                                 

 dense_14 (Dense)            (None, 1024)              1049600   

                                                                 

 dense_15 (Dense)            (None, 1)                 1025      

                                                                 

前のレイヤーをTNに置き換えます。

tn_model = tf.keras.Sequential( [ tf.keras.Input(shape=(2,)), Dense(1024, activation=tf.nn.relu), # Here use a TN layer instead of the dense layer. TNLayer(), Dense(1, activation=None) ] ) tn_model.summary()
Model: "sequential_6"

_________________________________________________________________

 Layer (type)                Output Shape              Param #   

=================================================================

 dense_16 (Dense)            (None, 1024)              3072      

                                                                 

 tn_layer_2 (TNLayer)        (None, 1024)              5120      

                                                                 

 dense_17 (Dense)            (None, 1)                 1025      

                                                                 

パラメータの数が減少したかどうかを確認できます。

次に、トレーニングを進めます。

X = np.concatenate([np.random.randn(20, 2) + np.array([3, 3]), np.random.randn(20, 2) + np.array([-3, -3]), np.random.randn(20, 2) + np.array([-3, 3]), np.random.randn(20, 2) + np.array([3, -3]),]) Y = np.concatenate([np.ones((40)), -np.ones((40))])

全結合モデルを訓練します。

fc_model.compile(optimizer="adam", loss="mean_squared_error") fc_model.fit(X, Y, epochs=300, verbose=1)
Epoch 1/300

3/3 [==============================] - 1s 18ms/step - loss: 0.2224

Epoch 2/300

3/3 [==============================] - 0s 27ms/step - loss: 0.1684

Epoch 3/300

3/3 [==============================] - 0s 31ms/step - loss: 0.1041

Epoch 4/300

3/3 [==============================] - 0s 25ms/step - loss: 0.0508

Epoch 5/300

3/3 [==============================] - 0s 19ms/step - loss: 0.0572
<keras.src.callbacks.History at 0x7f61271938e0>

次にテンソルネットワークモデルを訓練します。

tn_model.compile(optimizer="adam", loss="mean_squared_error") tn_model.fit(X, Y, epochs=300, verbose=1)
Epoch 1/300

3/3 [==============================] - 1s 6ms/step - loss: 0.0076

Epoch 2/300

3/3 [==============================] - 0s 5ms/step - loss: 0.0063

Epoch 3/300

3/3 [==============================] - 0s 6ms/step - loss: 0.0047

Epoch 4/300

3/3 [==============================] - 0s 5ms/step - loss: 0.0043

Epoch 5/300

3/3 [==============================] - 0s 6ms/step - loss: 0.0035
<keras.src.callbacks.History at 0x7f6126a91750>

予測の結果を確認しましょう。

全結合モデルの結果プロット

h = 1.0 x_min, x_max = X[:, 0].min() - 5, X[:, 0].max() + 5 y_min, y_max = X[:, 1].min() - 5, X[:, 1].max() + 5 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # here "model" is your model's prediction (classification) function Z = fc_model.predict(np.c_[xx.ravel(), yy.ravel()]) # Put the result into a color plot Z = Z.reshape(xx.shape) plt.contourf(xx, yy, Z) plt.axis('off') # Plot also the training points plt.scatter(X[:, 0], X[:, 1], c=Y, cmap=plt.cm.Paired)
13/13 [==============================] - 0s 6ms/step
<matplotlib.collections.PathCollection at 0x7f6126d13730>
<Figure size 640x480 with 1 Axes>

テンソルネットワークモデルの結果プロット

h = 1.0 x_min, x_max = X[:, 0].min() - 5, X[:, 0].max() + 5 y_min, y_max = X[:, 1].min() - 5, X[:, 1].max() + 5 xx, yy = np.meshgrid(np.arange(x_min, x_max, h), np.arange(y_min, y_max, h)) # here "model" is your model's prediction (classification) function Z = tn_model.predict(np.c_[xx.ravel(), yy.ravel()]) # Put the result into a color plot Z = Z.reshape(xx.shape) plt.contourf(xx, yy, Z) plt.axis('off') # Plot also the training points plt.scatter(X[:, 0], X[:, 1], c=Y, cmap=plt.cm.Paired)
13/13 [==============================] - 0s 2ms/step
<matplotlib.collections.PathCollection at 0x7f6126ebbe80>
<Figure size 640x480 with 1 Axes>

PyTorchを利用した量子回路(おまけ)

!pip install torch
Collecting torch

  Downloading torch-2.1.1-cp310-cp310-manylinux1_x86_64.whl (670.2 MB)

     ━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━ 670.2/670.2 MB 1.1 MB/s eta 0:00:0000:0100:03

[?25hCollecting filelock (from torch)

  Downloading filelock-3.13.1-py3-none-any.whl (11 kB)

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) (2.8.8)

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)
import matplotlib.pyplot as plt import torch.optim as optim import torch import numpy as np %matplotlib inline #qubit x = torch.tensor([1., 0.]) #variational parameter a = torch.tensor([0.2], requires_grad=True) #list for result arr = [] #the first variable is list of paramters. op = optim.Adam([a],lr=0.05) for _ in range(100): y = [[torch.cos(a/2),-torch.sin(a/2)], [torch.sin(a/2),torch.cos(a/2)]] z = [x[0]*y[0][0]+x[1]*y[0][1], x[0]*y[1][0]+x[1]*y[1][1]] expt = torch.abs(z[0])**2 - torch.abs(z[1])**2 arr.append(expt.item()) # Add the item to the arr list op.zero_grad() expt.backward() op.step() plt.plot(arr) plt.show()
<Figure size 640x480 with 1 Axes>output

テンソル操作の基本を理解することは、量子コンピューティングと機械学習の両方の理解を進めるのに役立ちます。

cuTensorNetの利用方法

今回はQiskitからcuTensorNetに読み込んで実行する方法を確認します。

import itertools import cupy as cp import numpy as np import qiskit from qiskit.circuit.random import random_circuit from cuquantum import contract from cuquantum import CircuitToEinsum

ランダムな量子回路を生成する

num_qubits = 7 depth = 6 circuit = random_circuit(num_qubits, depth, seed=3) circuit.draw(output='mpl')

倍精度を対象としたコンバーターオブジェクトを構築する

この例では、テンソルのオペランドをCuPy配列として生成します(backend=cupyを設定することにより)

myconverter = CircuitToEinsum(circuit, dtype='complex128', backend=cp)

状態ベクトルを計算する ψ| \psi\rangle

expression, operands = myconverter.state_vector() sv = contract(expression, *operands) print(f'wavefunction coefficient shape: {sv.shape}') print(type(operands[0])) print(type(sv))

ビット文字列の振幅を計算する bψ\langle b| \psi\rangle

bitstring = '0000000' expression, operands = myconverter.amplitude(bitstring) print(f'einsum expression:\n{expression} \n') amplitude = contract(expression, *operands) probability = abs(amplitude) ** 2 print(f'for bitstring {bitstring}, amplitude: {amplitude}, probability: {probability}\n') amplitude_from_sv = sv[0,0,0,0,0,0,0] amp_diff = abs(amplitude-amplitude_from_sv) print(f'difference from state vector {amp_diff}')

期待値計算 ψO^ψ\langle \psi|\hat{O}| \psi\rangle

この例では、パウリ文字列 IXXZZIIIXXZZII の期待値を計算します。比較のために、オペレータと縮約された密度行列を縮約して同じ値を計算します。

pauli_string = 'IXXZZII' expression, operands = myconverter.expectation(pauli_string, lightcone=True) expec = contract(expression, *operands) print(f'expectation value for {pauli_string}: {expec}') # expectation value from reduced density matrix qubits = myconverter.qubits where = qubits[1:5] rdm_expression, rdm_operands = myconverter.reduced_density_matrix(where, lightcone=True) rdm = contract(rdm_expression, *rdm_operands) pauli_x = cp.asarray([[0,1],[1,0]], dtype=myconverter.dtype) pauli_z = cp.asarray([[1,0],[0,-1]], dtype=myconverter.dtype) expec_from_rdm = cp.einsum('abcdABCD,aA,bB,cC,dD->', rdm, pauli_x, pauli_x, pauli_z, pauli_z) print(f"is expectation value in agreement?", cp.allclose(expec, expec_from_rdm))

© 2024, blueqat Inc. All rights reserved