PennyLane 初級 Tutorials : PyTorch とノイズのあるデバイス (翻訳)
翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/26/2019
* 本ページは、PennyLane : Tutorials : Learn PennyLane の次のページを翻訳した上で適宜、補足説明したものです:
* サンプルコードの動作確認はしておりますが、必要な場合には適宜、追加改変しています。
* ご自由にリンクを張って頂いてかまいませんが、sales-info@classcat.com までご一報いただけると嬉しいです。
$$
\def\bra#1{\mathinner{\left\langle{#1}\right|}}
\def\ket#1{\mathinner{\left|{#1}\right\rangle}}
\def\braket#1#2{\mathinner{\left\langle{#1}\middle|#2\right\rangle}}
$$
初級 Tutorials : PyTorch とノイズのあるデバイス
元の 量子ビット回転 チュートリアルを再訪問しましょう、しかしデフォルトの NumPy/autograd QNode インターフェイスを使用する代わりに、PyTorch インターフェイス を使用します。また最適化が noisy 量子ビットにどのように対応するかを見るため、 default.qubit デバイスを noisy forest.qvm デバイスと置き換えます。
貴方自身のコンピュータ上でこのチュートリアルで辿るためには、以下の依存を必要とします :
- Forest SDK、これは量子仮想マシン (QVM) と quilc 量子コンパイラを含みます。ひとたびインストールされれば、QVM と quilc は別の端末ウィンドウでコマンド “quilc -S” と “qvm -S” を実行することにより開始できます。
- PennyLane-Forest プラグイン、PennyLane デバイスとして QVM にアクセスするため。これは pip を通してインストールできます :
pip install pennylane-forest
- PyTorch、PyTorch QNode インターフェイスにアクセスするため。
デバイスをセットアップする
ひとたび上の依存がインストールされたら、必要なパッケージをインストールして量子デバイスのセットアップを開始しましょう。
最初に PennyLane をインポートしてそして、PyTorch インターフェイスを使用していますので、PyTorch も :
import pennylane as qml import torch from torch.autograd import Variable
PennyLane により提供される NumPy のラップされたバージョンをインポートする必要がないことに注意してください、何故ならばデフォルトの QNode NumPy インターフェイスを使用していないからです。NumPy が必要とされる場合には、PyTorch と TensorFlow で使用するために vanilla NumPy をインポートしてかまいません。
次に、デバイスを作成します :
dev = qml.device("forest.qvm", device="2q", noisy=True)
ここで、QVM を通してシミュレートされた、noisy 2-量子ビット系を作成します、望むのであれば、Aspen-1 QPU のような物理デバイス上でモデルを構築することもできるでしょう。
QNode を構築する
デバイスを初期化した今、量子ノードを構築できます。他のチュートリアルのように、(上の回路でエンコードされる) 量子関数を QVM 上で動作する量子ノード qnode デコレータを使用します。
@qml.qnode(dev, interface="torch") def circuit(phi, theta): qml.RX(theta, wires=0) qml.RZ(phi, wires=0) return qml.expval(qml.PauliZ(0))
QNode を ‘PyTorch aware’ にするため、QNode が PyTorch と相互作用するように指定する必要があります。これは interface=’torch’ キーワード引数を渡すことにより成されます。
その結果、QNode は PyTorch tensor を受け取りそして返すようにセットアップされます、そして PyTorch がバックプロパゲーションを遂行するとき自動的に任意の解析的勾配を計算することもできます。
最適化
今では最適化コスト関数を作成できます。系に何某かの追加の複雑さを導入するため、状態 $\left|0\right\rangle$ から状態 $\left|1\right\rangle$ に「量子ビットを反転する」変分回路を単純に訓練するのではなく、100 ステップ毎にターゲット状態変更もしましょう。例えば、最初の 100 ステップについて、ターゲット状態は $\left|1\right\rangle$ です ; それからこれは 100 と 200 ステップのために $\left|0\right\rangle$ に変更し、続いてステップ 200 から 300 の間には状態 $\left|1\right\rangle$ に変更し戻します、等々。
def cost(phi, theta, step): target = -(-1) ** (step // 100) return torch.abs(circuit(phi, theta) - target) ** 2
コスト関数が定義された今、PyTorch 最適化を始めることができます。変分回路の 2 つの自由パラメータを表わす、2 つの変数を作成してそして Adam optimizer を初期化します :
phi = Variable(torch.tensor(1.0), requires_grad=True) theta = Variable(torch.tensor(0.05), requires_grad=True) opt = torch.optim.Adam([phi, theta], lr=0.1)
PyTorch インターフェイスを使用していますので、PennyLane により提供される組込み optimizer ではなく、PyTorch optimizer を使用しなければなりません。組込み optimizer はデフォルトの NumPy/autograd インターフェイスに適用するのみです。
400 ステップの間系を最適化します :
for i in range(400): opt.zero_grad() loss = cost(phi, theta, i) loss.backward() opt.step()
今はパラメータの最終値、そして最終的な回路出力とコスト関数を確認できます :
print(phi) print(theta) print(circuit(phi, theta)) print(cost(phi, theta, 400))
tensor(-0.7055, requires_grad=True) tensor(6.1330, requires_grad=True) tensor(0.9551, dtype=torch.float64, grad_fn=<_TorchQNodeBackward>) tensor(3.7162, dtype=torch.float64, grad_fn=)
コスト関数がステップ依存なので、これは最適化が成功したかを決定するための十分な詳細を提供しません ; 代わりに、Bloch 球上で時間に渡る回路の出力状態をプロットしましょう :
ここで、赤い x は変分回路のターゲット状態で、矢印は変分回路出力状態です。ターゲット状態は変化しますので、回路は新しいターゲット状態を生成することを学習します!
ハイブリッド GPU-QPU 最適化
PyTorch は元々 GPU-accelerated 古典的処理をサポートし、そして Forest は QPU の形式で量子ハードウェアアクセスを提供しますので、非常に僅かの変更で、上のコードをハイブリッド GPU-QPU 最適化として実行できます (次のスクリプトを実行するためには、Rigetti の QCS サービスを使用する必要があることに注意してください) :
import pennylane as qml import torch from torch.autograd import Variable qpu = qml.device("forest.qpu", device="Aspen-1-2Q-B") @qml.qnode(dev, interface="torch") def circuit(phi, theta): qml.RX(theta, wires=0) qml.RZ(phi, wires=0) return qml.expval(qml.PauliZ(0)) def cost(phi, theta, step): target = -(-1) ** (step // 100) return torch.abs(circuit(phi, theta) - target) ** 2 phi = Variable(torch.tensor(1.0, device="cuda"), requires_grad=True) theta = Variable(torch.tensor(0.05, device="cuda"), requires_grad=True) opt = torch.optim.Adam([phi, theta], lr=0.1) for i in range(400): opt.zero_grad() loss = cost(phi, theta, i) loss.backward() opt.step()
GPU をサポートする古典的インターフェイスを使用するとき、QNode は任意の tensor 引数を (特定の量子デバイス上でそれらを適用する前に) 自動的に CPU にコピーします。ひとたび成されれば、それは QNode 結果を含む tensor を返し、そしてそれを任意の更なる古典的処理のために自動的に GPU にコピーし戻します。
以上