Pytorch interface for models


#1

Hello

I’m trying to make a neural networks using the interface Pytorch Pennylane. To do so I have to build a complex pytorch which will have to to have some actions on the device.

I firstly just tried to recode the rotation tutorial but using a model and I end up with an error I can not understand.

import pennylane as qml
import torch
import torch.autograd
import torch.nn as nn
import torch.nn.functional as F



class test_Rot(nn.Module):
    def __init__(self, n_wire):
        super(test_Rot, self).__init__()
        self.n_wire = n_wire
        self.phi = torch.tensor(0.011, requires_grad = True)
        self.theta = torch.tensor(0.012, requires_grad = True)
        
    def forward(self,):
        print(self.phi,self.theta)
        qml.RX(self.phi, wires=0)
        qml.RY(self.theta, wires=0)
        

dev1 = qml.device('default.qubit', wires=1)
testcl = test_Rot(dev1,1)

@qml.qnode(dev1, interface='torch')
def circuit(classifier):
    classifier()
    return qml.expval.PauliZ(0)

print('testcircuit')
print(circuit(testcl))

That’s where it stop, even if the rest of the code should be :

def cost(classifier):
    return(circuit() - 1)
        

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

for i in range(400):
    print(i)
    opt.zero_grad()
    loss = cost(testcl)
    loss.backward()
    opt.step()

My error message is:

testcircuit
Traceback (most recent call last):
  File "tests.py", line 43, in <module>
    print(circuit(testcl))
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\interfaces\torch.py", line 390, in custom_apply
    return _TorchQNode.apply(keyword_values, *args)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\interfaces\torch.py", line 307, in forward
    res = qnode(*ctx.args, **ctx.kwargs)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\qnode.py", line 457, in __call__
    return self.evaluate(args, **kwargs)  # args as one tuple
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\autograd\tracer.py", line 48, in f_wrapped
    return f_raw(*args, **kwargs)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\qnode.py", line 471, in evaluate
    self.construct(args, **kwargs)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\qnode.py", line 274, in construct
    variables = unflatten(temp, args)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\utils.py", line 99, in unflatten
    res, tail = _unflatten(np.asarray(flat), model)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\utils.py", line 88, in _unflatten
    val, flat = _unflatten(flat, x)
  File "C:\Users\barthelemy\.conda\envs\penny\lib\site-packages\pennylane\utils.py", line 92, in _unflatten
    raise TypeError('Unsupported type in the model: {}'.format(type(model)))
TypeError: Unsupported type in the model: <class '__main__.test_Rot'>

I have put a print at the entrance of the circuit function, the error comes before that.

I’m not sure to see what I did wrong.

Best regards

Barthélémy


#2

Hi @barthelemymp. Great question!

One thing to keep in mind is that:

  • a PyTorch nn module can include quantum operations, but they must be contained within QNodes

  • A QNode itself cannot contain anything other than quantum operations, so you cannot place a nn module inside a QNode.

I’ve rearranged your example to satisfy the above:

import pennylane as qml
import torch
import torch.autograd
import torch.nn as nn
import torch.nn.functional as F

dev1 = qml.device('default.qubit', wires=1)

# define neural network

class NeuralNet(nn.Module):
    def __init__(self, classifier, n_wire):
        super(NeuralNet, self).__init__()
        self.classifier = classifier
        self.n_wire = n_wire
        self.phi = torch.tensor(0.011, requires_grad = True)
        self.theta = torch.tensor(0.012, requires_grad = True)
        
    def forward(self):
        print(self.phi,self.theta)

        @qml.qnode(dev1, interface='torch')
        def circuit(*params):
            self.classifier(*params)
            return qml.expval.PauliZ(0)

        return circuit(self.phi, self.theta)

# define custom classifiers

def test_rot(phi, theta):
    qml.RX(phi, wires=0)
    qml.RY(theta, wires=0)

# perform the optimization

testcl = NeuralNet(test_rot, 1)
opt = torch.optim.Adam([testcl.phi, testcl.theta], lr = 0.1) 

for i in range(400):
    opt.zero_grad()
    output = testcl()
    loss = output - 1
    loss.backward()
    opt.step()

Note that the QNode is now defined and executed inside the nn.forward method. You could even use more than one QNode here, or mix a combination of QNode and classical layers.

Hope this helps!


#3

Thank you Josh for this Quick answer.
However in my example my classifier was the forward of the nn.Module I was creating (so it can not be used to initalize itself).

My goal was to decompose my circuit in 2 pyTorch NeuralNet, defining qml operation in their forward, and then having only one circuit(Qnode decorated) that use my two neuralNet

example I define 2 NeuralNet which are supposed to act on my quantumstate when I call their forward.

class NeuralNet1(nn.Module):
    def __init__(self,  ...):
        super(NeuralNet, self).__init__()

    def forward(self):
        qml.operations...

class NeuralNet2(nn.Module):
    def __init__(self,  ...):
        super(NeuralNet2, self).__init__()

    def forward(self):
        qml.operations...

and then make my Qnode

NN1 = NeuralNet1(...)
NN2 = NeuralNet2(...)

@qml.qnode(dev1, interface='torch')
def circuit(*params):
     NN1() #make the operations of the forward of the NN1
     NN2() #make the operations of the forward of the NN2 on the same device (it continues the operations made by NN1)
return qml.expval...

The probleme is that if I define the qnode inside the forward, I will need to use differents QNode in a row, which seems a bit tricky because I want my second qnode to act on the state of the device after the first QNode, and not to restart from the default |0000000> state.

There is a lot of questions in one, and I hope it is understandable…

Thank you again for your help!

b


#4

Hi @barthelemymp, unfortunately that pattern will not work, as you cannot place a PyTorch neural network inside a QNode. PyTorch neural networks are designed for classical processing, and cannot be run on quantum hardware or simulators.

Instead of using the nn module, is there any reason why you can’t create custom functions? For example, like:

def NeuralNet1(params):
    qml.operations...

def NeuralNet2(params):
    qml.operations...

@qml.qnode(dev1, interface='torch')
def circuit(params):
    layer1, layer2 = params
    NeuralNet1(layer1) #make the operations of the forward of the NN1
    NeuralNet2(layer2) #make the operations of the forward of the NN2 on the same device (it continues the operations made by NN1)
    return qml.expval...

#5

Thank you,

I just wanted to have a nice architecture that could work with the Pytorch dataLoader.

But the trick of defining my circuits inside the forward path works.

Thank you again,

barthélémy