PennyLaneAI / pennylane

PennyLane is a cross-platform Python library for quantum computing, quantum machine learning, and quantum chemistry. Train a quantum computer the same way as a neural network.
https://pennylane.ai
Apache License 2.0
2.32k stars 595 forks source link

[BUG] pennylane.wires.WireError: Cannot run circuit(s) on default.qubit as they contain wires not found on the device: #5059

Open ccu1tn opened 9 months ago

ccu1tn commented 9 months ago

Expected behavior

I am trying to use this code to caculate for one tensor with dimension 4D (e.x. (32,32,128,2))

Actual behavior

However, It alway appears error "pennylane.wires.WireError: Cannot run circuit(s) on default.qubit as they contain wires not found on the device and next to is a number series: {e.g, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47......}

Additional information

No response

Source code

`class Quantumnet(nn.Module):
    def __init__(self, feat_dim):
        super().__init__()
        self.pre_net = nn.Linear(feat_dim, n_qubits)
        self.bn = nn.BatchNorm2d(n_qubits)
        self.vqc = qml.qnn.TorchLayer(qnode, {"weights": (q_depth, n_qubits, 3)})
        self.post_net = nn.Linear(n_qubits, 2)  # Update in_features to n_qubits
        self.ce = nn.CrossEntropyLoss()
        self.initialize_params()

    def initialize_params(self):
        for m in self.modules():
            if isinstance(m, nn.Linear):
                init.xavier_normal_(m.weight.data)
                if m.bias is not None:
                    m.bias.data.fill_(0)
            elif isinstance(m, nn.BatchNorm1d):
                m.running_mean.data.fill_(0)
                m.running_var.data.fill_(1)

    def forward(self, input_features, labels=None):
        pre_out = self.pre_net(input_features)
        pre_out = self.bn(pre_out)
        q_in = torch.tanh(pre_out) * np.pi / 2.0
        q_out = self.vqc(q_in)
        q_out = self.post_net(q_out)
        loss = self.ce(q_out, labels)
        return loss, (q_out[:, 1] - q_out[:, 0]).squeeze(0)

feat_dim = 2  # set feat_dim to match the last dimension of input_features
n_qubits = 32
model = Quantumnet(feat_dim)

# Create a PyTorch tensor with the shape (32, 32, 128, 2)
input_features = torch.randn(32, 32, 128, feat_dim)

# Assuming you have appropriate labels for your task
labels = torch.randint(2, (batch_size,), dtype=torch.long)

# Call the model's forward method to get the output
output = model(input_features, labels=labels)

# Access the loss and tensor of interest
loss, quantum_output = output

# Print the shape of the quantum output tensor
print('Quantum Output Tensor Shape:', quantum_output.shape)`

Tracebacks

No response

System information


My system: 
Python 3.8
environment conda, 

### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.
albi3ro commented 9 months ago

Thanks for opening this bug @ccu1tn .

Mind posting the definition for the qnode and the device?

ccu1tn commented 9 months ago

@albi3ro Thank you so much. Please, can you share with me one example related to it? I am trying but it still not working. My eamail: babatnvn2019@gmail.com

CatalinaAlbornoz commented 9 months ago

Hi @ccu1tn, thank you for your question.

It's great that your using PennyLane, however it's important to remember that quantum systems are hard to simulate on a classical computer so you will probably be unable to simulate anything with more than 25 qubits on a laptop, and no more than 30 qubits using GPUs or higher compute capability. You may even reach memory limits at numbers lower than these. My recommendation would be to limit your usage to 20 qubits maximum.

On the other hand, PennyLane only supports Python 3.9 and higher so I would recommend that you upgrade your Python and PennyLane versions, or use Google Colab.

Finally, the code you shared is very complex but not complete in the sense that we cannot reproduce your problem because you didn't share your qnode and other information.

I have made a version of your code that runs with no errors, although the circuit isn't actually doing anything and the loss isn't being calculated, so you will need to work on these things.

If you want to keep working with Torch and PennyLane my recommendation is to check this demo and this demo, as well as this section of the documentation.

Here's the modified code

n_qubits = 8 #32 # Modified this
q_depth = 2 # Added this
batch_size = 2 # Added this

dev = qml.device('default.qubit', wires=8) # Added this

# Added the qnode
@qml.qnode(dev)
def qnode(inputs,weights):
  qml.Hadamard(wires=0)
  qml.CNOT(wires=[0,1])
  exp_vals = [qml.expval(qml.PauliZ(position)) for position in range(n_qubits)]
  return tuple(exp_vals)

class Quantumnet(nn.Module):
    def __init__(self, feat_dim):
        super().__init__()
        self.pre_net = nn.Linear(feat_dim, n_qubits)
        self.bn = nn.BatchNorm2d(n_qubits)
        self.vqc = qml.qnn.TorchLayer(qnode, {"weights": (q_depth, n_qubits, 3)})
        self.post_net = nn.Linear(n_qubits, 2)
        self.ce = nn.CrossEntropyLoss()
        #self.initialize_params() # Commented this
        self.q_params = nn.Parameter(0.01 * torch.randn(q_depth * n_qubits)) # Added this

    # Commented this
    """def initialize_params(self):
        for m in self.modules():
            if isinstance(m, nn.Linear):
                nn.init.xavier_normal_(m.weight.data) # added nn
                if m.bias is not None:
                    m.bias.data.fill_(0)
            elif isinstance(m, nn.BatchNorm1d):
                m.running_mean.data.fill_(0)
                m.running_var.data.fill_(1)"""

    def forward(self, input_features, labels=None):
        pre_out = self.pre_net(input_features)
        #pre_out = self.bn(pre_out) # Commented this
        q_in = torch.tanh(pre_out) * np.pi / 2.0
        # Commented this
        """q_out = self.vqc(q_in)
        q_out = self.post_net(q_out)
        loss = self.ce(q_out, labels)
        return loss, (q_out[:, 1] - q_out[:, 0]).squeeze(0)"""
        # Added this whole section below
        # Apply the quantum circuit to each element of the batch and append to q_out
        q_out = torch.Tensor(0, n_qubits)
        for elem in q_in:
            q_out_elem = torch.hstack(qnode(elem, self.q_params)).float().unsqueeze(0)
            q_out = torch.cat((q_out, q_out_elem))
        # return the two-dimensional prediction from the postprocessing layer
        return self.post_net(q_out)

feat_dim = 2
model = Quantumnet(feat_dim)

# Create a PyTorch tensor with the shape (n_qubits, n_qubits, 128, 2) # Modified this
input_features = torch.randn(n_qubits, n_qubits, 128, feat_dim) # Modified this

# Assuming you have appropriate labels for your task
labels = torch.randint(2, (batch_size,), dtype=torch.long)

# Call the model's forward method to get the output
output = model(input_features, labels=labels)

# Access the loss and tensor of interest
#loss, quantum_output = output # Commented this
quantum_output = output # Added this

# Print the shape of the quantum output tensor
print('Quantum Output Tensor Shape:', quantum_output.shape)

Let me know if this is more clear for you, and for future questions the best place to ask them is our discussion forum! There our answers to your questions can also help others.

ccu1tn commented 9 months ago

Dear @CatalinaAlbornoz , thank you for your detial help. This is very useful with me. Btw, in order to see deffirence between classical computing and quantum computing. I must convert every part in original program to quantum program, right, sir?

CatalinaAlbornoz commented 8 months ago

I'm glad this helped you @ccu1tn! Actually most quantum computing programs work as a hybrid with parts of the code being a classical program and part of it being a quantum program.

I think you'll really enjoy our page on Hybrid Computation and if you have any further questions feel free to open a new topic on our discussion forum! It can lead to really interesting conversations with other members of the community.

ccu1tn commented 8 months ago

Dear @CatalinaAlbornoz, My target is want to convert a completed classical program to quantum program. I already follow some tutorials on this page: https://pennylane.ai/qml/demonstrations/ but it still not working. Could you perhaps a little detial tutorial me so that I can run my application successfully, sir?

ccu1tn commented 8 months ago

@albi3ro, Here my full program, It can not run with wires=2

import torch.nn as nn
import torch
import torch.nn.functional as F 
import math
from torch.nn import init
import pennylane as qml
from pennylane import numpy as np
batch_size = 2
n_qubits = 4
q_depth = 2
dev = qml.device('default.qubit', wires=0)

def combinations(values):
    r = []
    for i in range(len(values)):
        for j in range(i + 1, len(values)):
            r.append((i, j))
    print('r', r)
    return r

def H_layer(nqubits):
    for idx in range(nqubits):
        qml.Hadamard(wires=idx)

def layer(weights, j):
    for i in range(weights.shape[1]):
        qml.RX(weights[j, i, 0], wires=i)
        qml.RY(weights[j, i, 1], wires=i)
        qml.RZ(weights[j, i, 2], wires=i)
        combs = combinations(range(weights.shape[1]))
        for i, k in combs:
            if j % 2 == 1:
                i, k = k, i
            qml.CNOT(wires=[i, k])

def RY_layer(w):
    for idx, element in enumerate(w):
        qml.RY(element, wires=idx)

@qml.qnode(dev)
def qnode(inputs, weights):
    H_layer(n_qubits)
    RY_layer(inputs)
    for j in range(weights.shape[0]):
           layer(weights, j)
    expvals = [qml.expval(qml.PauliZ(i)) for i in range(weights.shape[1])] 
    return expvals

class Quantumnet(nn.Module):
      def __init__(self, feat_dim):
            super().__init__()
            self.pre_net = nn.Linear(feat_dim, n_qubits)
            # self.bn = nn.BatchNorm1d(n_qubits)
            self.bn = nn.BatchNorm2d(n_qubits)
            self.vqc = qml.qnn.TorchLayer(qnode, {"weights": (q_depth, n_qubits, 3)})
            self.post_net = nn.Linear(n_qubits, 2)
            self.ce = nn.CrossEntropyLoss()
            self.initialize_params()

    def initialize_params(self):
        for m in self.modules():
            if isinstance(m, nn.Linear):
                init.xavier_normal_(m.weight.data)
                if m.bias is not None:
                    m.bias.data.fill_(0)
            elif isinstance(m, nn.BatchNorm1d):
                m.running_mean.data.fill_(0)
                m.running_var.data.fill_(1)

    def forward(self, input_features, labels= None):
        pre_out = self.pre_net(input_features)
        pre_out = self.bn(pre_out)
        q_in = torch.tanh(pre_out) * np.pi / 2.0
        q_out = self.vqc(q_in)
        q_out = self.post_net(q_out)
        loss = self.ce(q_out, labels)
        print('loss', loss)
        return loss, (q_out[:, 1] - q_out[:, 0]).squeeze(0)

feat_dim = 2
model = Quantumnet(feat_dim)
input_features = torch.randn(n_qubits, n_qubits, 128, feat_dim) 
labels = torch.randint(2, (batch_size,), dtype=torch.long)
output = model(input_features, labels=labels)
quantum_output = output 
print('Quantum Output Tensor Shape:', quantum_output.shape)
CatalinaAlbornoz commented 8 months ago

Hi @ccu1tn thank you for your question.

It's hard to read your question because the formatting is off. In general it's recommended to ask these questions on our discussion forum, where you can easily format your code as explained in this video.

Usually turning some classical workflow into a quantum one can be tricky because you need to plug in classical data into a quantum circuit, which depends a lot on the dataset itself. So my recommendation would be to follow these two tutorials (here and here) and play with the classical and quantum layers to make sure you understand them before you change the dataset. Because when you change the dataset you will probably need to change the sizes of a lot of inputs an outputs.

I hope this helps!

Let me know if you have a more specific question about a certain part of the workflow, qnodes, etc.