PennyLane : 使用方法 : PyTorch インターフェイス

PennyLane 使用方法 : PyTorch インターフェイス (翻訳)

翻訳 : (株)クラスキャット セールスインフォメーション
作成日時 : 10/19/2019

* 本ページは、PennyLane : Using 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}}
$$

 

使用方法 : PyTorch インターフェイス

PennyLane は変分回路から成る量子ノードを他のプログラミングと機械学習フレームワークと統合しています。そのようなフレームワークはインターフェイスと呼ばれます。変分回路をどのようにプログラムするかの前のセクションで暗黙的に使用されたデフォルトのインターフェイスは NumPy です。

 
PennyLane を PyTorch と組み合わせて使用するには、PyTorch-互換な量子ノードを生成しなければなりません。基本的な QNode は QNode デコレータで interface=’torch’ フラグを使用するか、QNode.to_torch 関数を呼び出すことにより、PyTorch と相互接続する量子ノードに変換できます。内部的には、変換は TorchQNode 関数により実行されます、これは新しい量子ノード・オブジェクトを返します。

Note
PennyLane で PyTorch インターフェイスを使用するには、最初に PyTorch をインストールして次を通してそれを PennyLane と一緒にインポートしなければなりません :

import pennylane as qml
import torch

 

デコレータを通して構築

QNode デコレータ は PennyLane で PyTorch-capable QNode を作成するための推奨される方法です。単純に interface=’torch’ キーワード引数を指定します :

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev, interface='torch')
def circuit1(phi, theta):
    qml.RX(phi[0], wires=0)
    qml.RY(phi[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.PhaseShift(theta, wires=0)
    return qml.expval(qml.PauliZ(0)), qml.expval(qml.Hadamard(1))

QNode circuit1() は今では PyTorch-capable QNode で、入力として torch.tensor オブジェクトを受け取り、そして torch.tensor オブジェクトを返します。torch.autograd.Function からサブクラス化して、それは今では任意の他の PyTorch 関数のように使用できます :

>>> phi = torch.tensor([0.5, 0.1])
>>> theta = torch.tensor(0.2)
>>> circuit1(phi, theta)
tensor([0.8776, 0.6880], dtype=torch.float64)

 

NumPy QNode から構築

時に、QNode オブジェクトを直接インスタンス化することはより便利です、例えば、複数のデバイスに渡り同じ量子関数を再利用する、あるいは異なる古典的インターフェイスを使用することさえ望む場合です :

dev1 = qml.device('default.qubit', wires=2)
dev2 = qml.device('forest.wavefunction', wires=2)

def circuit2(phi, theta):
    qml.RX(phi[0], wires=0)
    qml.RY(phi[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.PhaseShift(theta, wires=0)
    return qml.expval(qml.PauliZ(0)), qml.expval(qml.Hadamard(1))

qnode1 = qml.QNode(circuit2, dev1)
qnode2 = qml.QNode(circuit2, dev2)

to_torch() メソッドを使用してデフォルトの NumPy-interfacing QNode を PyTorch-interfacing QNode に変換できます :

>>> qnode1_torch = qnode1.to_torch()
>>> qnode1_torch

内部的には、QNode.to_torch メソッドは変換を行なうために TorchQNode 関数を使用します。

 

PyTorch を使用する量子勾配

PyTorch-interfacing QNode は任意の他の torch.autograd.Function のように動作しますので、PyTorch で勾配を計算するために使用される標準メソッドが使用できます。

例えば :

from torch.autograd import Variable

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev, interface='torch')
def circuit3(phi, theta):
    qml.RX(phi[0], wires=0)
    qml.RY(phi[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.PhaseShift(theta, wires=0)
    return qml.expval(qml.PauliZ(0))

phi = Variable(torch.tensor([0.5, 0.1]), requires_grad=True)
theta = Variable(torch.tensor(0.2), requires_grad=True)
result = circuit3(phi, theta)

今は、バックプロパゲーションを遂行して勾配を累積します :

>>> result.backward()
>>> phi.grad
tensor([-0.4794,  0.0000])
>>> theta.grad
tensor(-5.5511e-17)

 

PyTorch を使用する最適化

Torch インターフェイスを使用してハイブリッド古典的量子モデルを最適化するためには、PyTorch が提供する optimizer か、貴方自身のカスタム PyTorch optimizer を利用しなければ なりませんPennyLane optimizer は Torch インターフェイスでは使用できません

例えば、GPU 上で処理される古典的ノードで、重み x が 0.5 の期待値という結果になるように Torch-interfacing QNode (下) を最適化するためには、次を行なうことができます :

import torch
from torch.autograd import Variable
import pennylane as qml

dev = qml.device('default.qubit', wires=2)

@qml.qnode(dev, interface='torch')
def circuit4(phi, theta):
    qml.RX(phi[0], wires=0)
    qml.RZ(phi[1], wires=1)
    qml.CNOT(wires=[0, 1])
    qml.RX(theta, wires=0)
    return qml.expval(qml.PauliZ(0))

def cost(phi, theta):
    return torch.abs(circuit4(phi, theta) - 0.5)**2

phi = Variable(torch.tensor([0.011, 0.012]), requires_grad=True)
theta = Variable(torch.tensor(0.05), requires_grad=True)

opt = torch.optim.Adam([phi, theta], lr = 0.1)

steps = 200

def closure():
    opt.zero_grad()
    loss = cost(phi, theta)
    loss.backward()
    return loss

for i in range(steps):
    opt.step(closure)

最終的な重みと回路値は :

>>> phi_final, theta_final = opt.param_groups[0]['params']
>>> phi_final, theta_final
(tensor([0.7345, 0.0120], device='cuda:0', requires_grad=True), tensor(0.8316, device='cuda:0', requires_grad=True))
>>> circuit(phi_final, theta_final)
tensor(0.5000, device='cuda:0', dtype=torch.float64, grad_fn=<_TorchQNodeBackward>)

Note
より進んだ PyTorch モデルについて、Torch-interfacing QNode はカスタム PyTorch モジュール (torch.nn.Module) で層を構築するために使用できます。

より詳細については https://pytorch.org/docs/stable/notes/extending.html#adding-a-module を見てください。

 

以上