# Inputs dimension mix with batch dimension in qml.qnn.TorchLayer

**URL:** <https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824>\
**Category:** PennyLane Help\
**Created:** [December 16, 2023, 5:05pm UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824 "2023-12-16T17:05:11Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![Yian\_Chen](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/yian_chen/32/360_2.png) [@Yian\_Chen](https://discuss.pennylane.ai/u/Yian_Chen)\
**Post date:** [December 16, 2023, 5:05pm UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/1 "2023-12-16T17:05:11Z")

</div>

Hi, I found that when using `qml.qnn.TorchLayer`, it recognizes batch dimension differently from version `pennylane==0.30.0` against versions higher than `pennylane==0.31.0`. Suppose the shape of a single batch of data is x=(N,D), where N and D are the dimension of batch and data respectively. Consider forwarding x through a `qml.qnn.TorchLayer` L:

- In `pennylane==0.30.0`  
It behaves correctly that L(x) is also in shape (N,\text{some output shape})  
. To be more precise, if the dimension of x is now (N\_1,N\_2,N\_3,D), the output dimension of L(X) will be (N\_1,N\_2,N\_3,\text{some output shape})

- In `pennylane>=0.31.0`  
It seems to forwarding all data in a batch as inputs. When I print out the shape of `inputs` in the circuit, the shape is exactly the shape of x, i.e., (N,D)

Here is the code for reproducing the error (or maybe it was designed on purpose 🤔):

```auto
import sys
import torch
import torch.nn as nn
import pennylane as qml
print(f"Python version: {sys.version}")
print(f"PyTorch version: {torch. __version__ }")
print(f"Pennylane version: {qml. __version__ }")

device = qml.device("default.qubit", wires=2)

@qml.qnode(device)
def circuit(inputs):
    print(f"circuit inputs shape = {inputs.shape}")
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

class Model(nn.Module):
    def __init__ (self):
        super(). __init__ ()
        torch_layer = qml.qnn.TorchLayer(circuit, weight_shapes={})
        self.net = nn.Sequential(torch_layer)
    def forward(self, x):
        print(f"model x shape = {x.shape}")
        x = self.net(x)
        print(f"output shape = {x.shape}")
        return x

N, D = 5, 3
model = Model()
y = model(torch.rand(N, D))

```

> Outputs in `0.30.0`:  
> model x shape = torch.Size([3, 2])  
> circuit inputs shape = torch.Size([2])  
> circuit inputs shape = torch.Size([2])  
> circuit inputs shape = torch.Size([2])  
> output shape = torch.Size([3])

> Outputs in `0.31.0`:  
> model x shape = torch.Size([3, 2])  
> circuit inputs shape = torch.Size([3, 2])  
> RuntimeError: shape ‘[3]’ is invalid for input of size 1

Dependencies:

- `python 3.9.12`
- `pip==23.3.1`
- `torch==2.1.2`

---

<div class="post-metadata">

**Author:** ![Yian\_Chen](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/yian_chen/32/360_2.png) [@Yian\_Chen](https://discuss.pennylane.ai/u/Yian_Chen)\
**Post date:** [December 17, 2023, 2:55am UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/2 "2023-12-17T02:55:48Z")

</div>

Also an additional question, since usually a data might not be 1-dimensional (although we still can reshape it to 1-D than reshape back). I was wondering whether `TorchLayer` or quantum circuits can specify the dimension of batch and inputs, just like [Conv2d](https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html). Thanks in advance 😄

---

<div class="post-metadata">

**Author:** ![isaacdevlugt](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/isaacdevlugt/32/1159_2.png) [@isaacdevlugt](https://discuss.pennylane.ai/u/isaacdevlugt)\
**Post date:** [December 18, 2023, 5:18pm UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/3 "2023-12-18T17:18:48Z")

</div>

Hey @Yian_Chen,

I think your example is highlighting a non-issue that has a vague error message. Essentially the problem is down to your circuit not doing anything with `inputs`, but Torch sees `inputs` as having a batch dimension and is expecting your circuit output to have a batch dimension as well. But, if you run your circuit outside of Torch, you get this:

```auto
@qml.qnode(device)
def circuit(inputs):
    print(f"circuit inputs shape = {inputs.shape}")

    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

circuit(np.random.uniform(0, 1, size=(100))

```

```auto
circuit inputs shape = (100,)
tensor(0., requires_grad=True)

```

PennyLane doesn’t do any broadcasting because there’s no instructions for what to do when you aren’t using `inputs` 🙂.

If I put in something that deals with `inputs`, then PennyLane will give you an output with a leading dimension that matches the inputs’ leading dimension:

```auto
@qml.qnode(device)
def circuit(inputs):
    print(f"circuit inputs shape = {inputs.shape}")
    qml.AngleEmbedding(inputs, wires=range(2))
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

circuit(np.random.uniform(0, 1, size=(100, 2)))

```

```auto
circuit inputs shape = (100, 2)
tensor([0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0., 0.,
        0., 0., 0., 0.], requires_grad=True)

```

Putting this back together with your torch example:

```auto
@qml.qnode(device)
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    qml.AngleEmbedding(inputs, wires=range(2))
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

torch_layer = qml.qnn.TorchLayer(circuit, weight_shapes = {"weights": (0)})
net = nn.Sequential(torch_layer)

N, D = 5, 2
net(torch.rand(N, D))

```

```auto
tensor([0., 0., 0., 0., 0.])

```

You just need to do something with `inputs` 🙂

As for the PennyLane version discrepancy, this might be due to some bugs that were fixed or features that were added to our parameter broadcasting functionality (it’s a relatively new feature; about 1 year old). I’m using the latest version of PennyLane (v0.33.1) 🙂

Hopefully this also answers your second post! If not, let me know 👍

---

<div class="post-metadata">

**Author:** ![Yian\_Chen](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/yian_chen/32/360_2.png) [@Yian\_Chen](https://discuss.pennylane.ai/u/Yian_Chen)\
**Post date:** [December 19, 2023, 4:23am UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/4 "2023-12-19T04:23:12Z")

</div>

Thank you @isaacdevlugt , the code above is reproducible and the explanations are really clear 😄. I notice that you have used `qml.AngleEmbedding `, so that `qml.qnn.TorchLayer ` somehow knows the batch dimension. However, due to the design of my circuit, there is no corresponding function I can use for embedding. Particularly, I need to use for loops to embed the `inputs`. I try to modify your code that can roughly demonstrate what I want to do:

- Code from your example

```python
# pennylane==0.33.1
@qml.qnode(device)
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    qml.AngleEmbedding(inputs, wires=range(2))
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

```

```bash
circuit inputs shape = torch.Size([5, 2])
tensor([0., 0., 0., 0., 0.])

```

- What if I need to use for loop

```python
# pennylane==0.33.1
@qml.qnode(device)
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    for i in range(2):
        qml.RX(inputs[i], wires=i)
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

```

```bash
circuit inputs shape = torch.Size([5, 2])
RuntimeError: shape '[5]' is invalid for input of size 2

```

The only difference is that I use `qml.RX` explicitly to embed the `inputs`.

* * *

On the other hand, I notice the difference between `0.30.0` and `0.33.1` comes from how `qml.qnn.TorchLayer` feed in the inputs. So suppose you set

```python
device = qml.device("default.qubit", wires=2)

@qml.qnode(device)
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    qml.AngleEmbedding(inputs, wires=range(2))
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

torch_layer = qml.qnn.TorchLayer(circuit, weight_shapes = {"weights": (0)})
net = nn.Sequential(torch_layer)

batch_size = (5,4)
inputs_shape = (3,2)
x = torch.rand(*batch_size, *inputs_shape)
print(f"net(x) shape = {net(x).shape}")

```

- In `Pennylane==0.30.0`

```bash
circuit inputs shape = torch.Size([2])
net(x) shape = torch.Size([5, 4, 3])

```

- In `Pennylane==0.33.1`, which

```bash
circuit inputs shape = torch.Size([60, 2])
net(x) shape = torch.Size([5, 4, 3])

```

So for both versions, they recognize [5,4,3] as the batch dimension (actually I just want [5,4]), but different `inputs` shape. Nonetheless, in old version (`0.30.0`), it is easy to solved with

```python
@qml.qnode(device)
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    inputs = inputs.reshape(3,2)
    for i in range(len(inputs)):
        qml.AngleEmbedding(inputs[i], wires=range(2))
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

torch_layer = qml.qnn.TorchLayer(circuit, weight_shapes = {"weights": (0)})
net = nn.Sequential(torch_layer)

batch_size = (5,4)
inputs_shape = (3,2)
x = torch.rand(*batch_size, *inputs_shape)
x = torch.flatten(x, start_dim=-2, end_dim=-1)
print(f"net(x) shape = {net(x).shape}")

```

```bash
circuit inputs shape = torch.Size([6])
net(x) shape = torch.Size([5, 4, 1])

```

However, in newer versions, if the inputs is fed with whole batch (e.g. `torch.Size([20, 6])` in the example above), I think I can’t use the for loop.

---

<div class="post-metadata">

**Author:** ![isaacdevlugt](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/isaacdevlugt/32/1159_2.png) [@isaacdevlugt](https://discuss.pennylane.ai/u/isaacdevlugt)\
**Post date:** [December 19, 2023, 11:31pm UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/6 "2023-12-19T23:31:18Z")

</div>

> I notice that you have used `qml.AngleEmbedding `, so that `qml.qnn.TorchLayer ` somehow knows the batch dimension. However, due to the design of my circuit, there is no corresponding function I can use for embedding.

I’m not using `AngleEmbedding` just so that torch can see a batch dimension, I’m using it because there _must_ be something done to the inputs, and `AngleEmbedding` is one of the many ways to do something with inputs.

I’m a little confused about your use case and why something like this won’t suite your needs. Could you tell me what your data is and how do you want to embed it into a quantum layer?

---

<div class="post-metadata">

**Author:** ![Yian\_Chen](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/yian_chen/32/360_2.png) [@Yian\_Chen](https://discuss.pennylane.ai/u/Yian_Chen)\
**Post date:** [December 20, 2023, 3:30am UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/7 "2023-12-20T03:30:08Z")

</div>

Hi @isaacdevlugt , thanks for your reply. I apologize for the confusion of my question, what I meant is that one can use _pennylane’s embedding functions_ (e.g. `AngleEmbedding `, `AmplitudeEmbedding`, `IQPEmbedding`). But what if my embedding method is so weird that pennylane doesn’t provide a template? In such a situation, if I don’t use _pennylane’s embedding functions_, then I cannot do something with `inputs` properly.

For example, suppose one of the data in a single batch is x=(x\_0, x\_1) with 2 features, and my `weird_embedding` method looks like

```python
def weird_embedding(x):
    # x is supposed to be a single data, not a batch of data
    for i in range(len(x)-1):
        qml.CRX(x[i], wires=[i, (i+1)])
        qml.T(wires=i)
        qml.CRY(x[i], wires=[i, (i+1)])
        qml.S(wires=(i+1))
        qml.CRZ(x[i], wires=[i, (i+1)])
    qml.Hadamard(wires=0)

```

Instead of using _pennylane’s embedding functions_, I want to use my own designed embedding functions. Now another problem shows up, in `0.30.0` the `inputs` is default to be feed **one by one** , so I just need to write

```python
device = qml.device("default.qubit", wires=2)
@qml.qnode(device)
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    weird_embedding(inputs)
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

torch_layer = qml.qnn.TorchLayer(circuit, weight_shapes = {"weights": (0)})
net = nn.Sequential(torch_layer)

batch_size = (5,4,3)
inputs_shape = (2)
dataset = torch.rand(*batch_size, inputs_shape)
print(f"net(dataset) shape = {net(dataset).shape}")

```

Note the shape of `inputs` is

```bash
# version 0.30.0
circuit inputs shape = torch.Size([2])

```

On the other hand, in `0.33.1`, I understand I need to do something with `inputs`, e.g.

```python
# this is fine
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    qml.AmplitudeEmbedding(inputs, wires=range(2), pad_with=0, normalize=True)
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

# but this is not fine
def circuit(inputs, weights):
    print(f"circuit inputs shape = {inputs.shape}")
    weird_embedding(inputs)
    qml.Hadamard(wires=0)
    qml.CNOT(wires=(0,1))
    return qml.expval(qml.PauliX(0))

```

Also notice that the shape of `inputs` is now (not feed one by one but whole batch of data)

```bash
# version 0.33.1
circuit inputs shape = torch.Size([60, 2])

```

So if I want to use my own `weird_embedding`, then it will break down since now the `inputs` is the whole batch, **not** one by one. So my question is how can I do such that my `wried_embedding` can handle the batch data just like _pennylane’s embedding functions_? Thank you 😄

---

<div class="post-metadata">

**Author:** ![isaacdevlugt](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/isaacdevlugt/32/1159_2.png) [@isaacdevlugt](https://discuss.pennylane.ai/u/isaacdevlugt)\
**Post date:** [December 21, 2023, 9:18pm UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/8 "2023-12-21T21:18:19Z")

</div>

Ah! Thank you. This clears things up a bit 👍

There are a couple of different ways that you can make a custom embedding procedure and have it work with broadcasting. You can assume that the input has a leading (batch) dimension like this:

```auto
def weird_embedding(x):
    qml.CRX(x[:, 0], wires=[0, 1])
    qml.T(wires=0)
    qml.CRY(x[:, 1], wires=[0, 1])
    qml.S(wires=1)
    qml.CRZ(x[:, 2], wires=[0, 1])

```

You can then call it like this:

```auto
dev = qml.device("default.qubit", wires=2)

@qml.qnode(dev)
def circuit(x):
    weird_embedding(x)
    return [qml.expval(qml.PauliZ(i)) for i in range(2)]

x = np.random.uniform(0, np.pi, size=(50,3))

circuit(x)

```

```auto
[tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
         1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
         1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
         1., 1.], requires_grad=True),
 tensor([1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
         1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
         1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1., 1.,
         1., 1.], requires_grad=True)]

```

But, in the case that `x` is just _one_ data point (not a batch), then this way doesn’t generalize like it would with three separate variables:

```python
def weird_embedding(a, b, c):
    # x is supposed to be a single data, not a batch of data
    qml.CRX(a, wires=[0, 1])
    qml.T(wires=0)
    qml.CRY(b, wires=[0, 1])
    qml.S(wires=1)
    qml.CRZ(c, wires=[0, 1])

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

@qml.qnode(dev)
def circuit(a, b, c):
    weird_embedding(a, b, c)
    return [qml.expval(qml.PauliZ(i)) for i in range(2)]

a = np.random.uniform(0, np.pi, size=(50,))
b = np.random.uniform(0, np.pi, size=(50,))
c = np.random.uniform(0, np.pi, size=(50,))

circuit(a, b, c)

```

When this is in the context of a torch layer, you’re stuck with just having `inputs` — you can’t split it up into `input1`, `input2`, etc., for example. So you’ll have to write some logic at the start of your quantum functions to discern if `inputs is a batch or not. E.g.,

```auto
def weird_embedding(x):
    if len(x.shape) > 1:
        # is a batch of data
        a, b, c = x[:,0], x[:, 1], x[:, 2]
    else:
        # is a single data point
        a, b, c = x[0], x[1], x[2]

    qml.CRX(a, wires=[0, 1])
    qml.T(wires=0)
    qml.CRY(b, wires=[0, 1])
    qml.S(wires=1)
    qml.CRZ(c, wires=[0, 1])

```

You can find more info here: [Templates — PennyLane 0.33.0 documentation](https://docs.pennylane.ai/en/stable/introduction/templates.html?highlight=template#custom-templates)

Let me know if this helps!

---

<div class="post-metadata">

**Author:** ![Yian\_Chen](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/yian_chen/32/360_2.png) [@Yian\_Chen](https://discuss.pennylane.ai/u/Yian_Chen)\
**Post date:** [December 22, 2023, 1:13am UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/9 "2023-12-22T01:13:55Z")

</div>

Hi @isaacdevlugt , thanks for your solution! It works fine and I now know how to modify my codes for `0.33.1`. Merry Christmas!

---

<div class="post-metadata">

**Author:** ![isaacdevlugt](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/isaacdevlugt/32/1159_2.png) [@isaacdevlugt](https://discuss.pennylane.ai/u/isaacdevlugt)\
**Post date:** [December 22, 2023, 8:59pm UTC](https://discuss.pennylane.ai/t/inputs-dimension-mix-with-batch-dimension-in-qml-qnn-torchlayer/3824/10 "2023-12-22T20:59:30Z")

</div>

Awesome! Glad to hear that we finally figured it out 😃

Happy holidays!
