# How to get gradients of quantum circuit with PyTorch interface

**URL:** <https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808>\
**Category:** PennyLane Help\
**Created:** [June 28, 2024, 3:08am UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808 "2024-06-28T03:08:35Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![cyx617](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/cyx617/32/2672_2.png) [@cyx617](https://discuss.pennylane.ai/u/cyx617)\
**Post date:** [June 28, 2024, 3:08am UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/1 "2024-06-28T03:08:35Z")

</div>

I plan to use PennyLane and PyTorch to construct a hybrid quantum-classical neural network model (e.g., for binary classification). If the variational quantum circuit in the quantum module uses amplitude encoding, can I print the gradients of the loss function with respect to the quantum parameters? If possible, could you provide a specific mini code example? I want to investigate whether the quantum module will have a barren plateau problem.

---

<div class="post-metadata">

**Author:** ![CatalinaAlbornoz](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/catalinaalbornoz/32/1196_2.png) [@CatalinaAlbornoz](https://discuss.pennylane.ai/u/CatalinaAlbornoz)\
**Post date:** [June 28, 2024, 9:34pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/2 "2024-06-28T21:34:52Z")

</div>

Hi @cyx617 , welcome to the Forum!

Yes you can indeed print the gradients.

I used the full code example at the bottom of the [TorchLayer documentation (Usage details section)](https://docs.pennylane.ai/en/latest/code/api/pennylane.qnn.TorchLayer.html#usage-details).

After `opt.step()` I added

```auto
for p in model.parameters():
          print('p.grad: ',p.grad)

```

This prints the gradients for the different parameters.

I’m not 100% sure that the output is what you would expect (this part is being handled by Torch) but I hope this can give you some pointers.

See the full code below!

```auto
import numpy as np
import pennylane as qml
import torch
import sklearn.datasets

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

@qml.qnode(dev)
def qnode(inputs, weights):
    qml.templates.AngleEmbedding(inputs, wires=range(n_qubits))
    qml.templates.StronglyEntanglingLayers(weights, wires=range(n_qubits))
    return [qml.expval(qml.Z(0)), qml.expval(qml.Z(1))]

weight_shapes = {"weights": (3, n_qubits, 3)}

qlayer = qml.qnn.TorchLayer(qnode, weight_shapes)
clayer1 = torch.nn.Linear(2, 2)
clayer2 = torch.nn.Linear(2, 2)
softmax = torch.nn.Softmax(dim=1)
model = torch.nn.Sequential(clayer1, qlayer, clayer2, softmax)

samples = 100
x, y = sklearn.datasets.make_moons(samples)
y_hot = np.zeros((samples, 2))
y_hot[np.arange(samples), y] = 1

X = torch.tensor(x).float()
Y = torch.tensor(y_hot).float()

opt = torch.optim.SGD(model.parameters(), lr=0.5)
loss = torch.nn.L1Loss()

# Training
epochs = 8
batch_size = 5
batches = samples // batch_size

data_loader = torch.utils.data.DataLoader(list(zip(X, Y)), batch_size=batch_size,
                                          shuffle=True, drop_last=True)

for epoch in range(epochs):

    running_loss = 0

    for x, y in data_loader:
        opt.zero_grad()

        loss_evaluated = loss(model(x), y)
        loss_evaluated.backward()

        opt.step()

        # Added this
        for p in model.parameters():
          print('p.grad: ',p.grad)

        running_loss += loss_evaluated

    avg_loss = running_loss / batches
    print("Average loss over epoch {}: {:.4f}".format(epoch + 1, avg_loss))

```

---

<div class="post-metadata">

**Author:** ![cyx617](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/cyx617/32/2672_2.png) [@cyx617](https://discuss.pennylane.ai/u/cyx617)\
**Post date:** [June 29, 2024, 3:09pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/3 "2024-06-29T15:09:50Z")

</div>

> [@CatalinaAlbornoz](#):
>
> you some

Hi @CatalinaAlbornoz,

Thank you so much for your answer and the code provided! I will try it out.

---

<div class="post-metadata">

**Author:** ![CatalinaAlbornoz](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/catalinaalbornoz/32/1196_2.png) [@CatalinaAlbornoz](https://discuss.pennylane.ai/u/CatalinaAlbornoz)\
**Post date:** [July 2, 2024, 9:11pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/4 "2024-07-02T21:11:02Z")

</div>

Let me know how it goes @cyx617 !

---

<div class="post-metadata">

**Author:** ![cyx617](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/cyx617/32/2672_2.png) [@cyx617](https://discuss.pennylane.ai/u/cyx617)\
**Post date:** [July 4, 2024, 11:57am UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/5 "2024-07-04T11:57:13Z")

</div>

Hi @CatalinaAlbornoz,

The code you sent me runs successfully and prints the gradient of the quantum parameters. However, after modifying the code from [this tutorial](https://pennylane.ai/qml/demos/tutorial_quantum_transfer_learning/) using the same method, I found that the printed gradient values of the quantum parameters are **None**. What could be the reason for this?

I added the following code after `optimizer.zero_grad()` in `tutorial_quantum_transfer_learning.py` :

```auto
            for name, p in model_hybrid.named_parameters():
                print('name:',name,'p.grad: ',p.grad)

```

I think the biggest difference between the mini code example you sent me and the `tutorial_quantum_transfer_learning.py` code is that in `tutorial_quantum_transfer_learning.py` , `qml.qnn.TorchLayer()` is not used to construct the quantum layer.

---

<div class="post-metadata">

**Author:** ![CatalinaAlbornoz](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/catalinaalbornoz/32/1196_2.png) [@CatalinaAlbornoz](https://discuss.pennylane.ai/u/CatalinaAlbornoz)\
**Post date:** [July 5, 2024, 8:08pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/6 "2024-07-05T20:08:09Z")

</div>

Hi @cyx617 ,

I think the issue is on _where_ you’re adding the print statement. The code below is part of the “training and results” section of the [transfer learning demo](https://pennylane.ai/qml/demos/tutorial_quantum_transfer_learning/) (with the addition of the print statement).

When you run `optimizer.zero_grad()` you’re resetting the gradient so it’s normal that you don’t get the expected output if you add the print statement there. Notice that below, within the `if` statement, you have `optimizer.step()`. This is where you’re actually taking the optimization step so that’s where you can add the print statement (`for p in model.parameters(): print('p.grad: ',p.grad)`).

```auto
# Iterate over data.
            n_batches = dataset_sizes[phase] // batch_size
            it = 0
            for inputs, labels in dataloaders[phase]:
                since_batch = time.time()
                batch_size_ = len(inputs)
                inputs = inputs.to(device)
                labels = labels.to(device)
                optimizer.zero_grad()

                # Track/compute gradient and make an optimization step only when training
                with torch.set_grad_enabled(phase == "train"):
                    outputs = model(inputs)
                    _, preds = torch.max(outputs, 1)
                    loss = criterion(outputs, labels)
                    if phase == "train":
                        loss.backward()
                        optimizer.step()
                        
                        # Added this
                        for p in model.parameters():
                            print('p.grad: ',p.grad)

                # Print iteration results

```

Let me know if this works for you!

---

<div class="post-metadata">

**Author:** ![cyx617](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/cyx617/32/2672_2.png) [@cyx617](https://discuss.pennylane.ai/u/cyx617)\
**Post date:** [July 6, 2024, 1:10pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/7 "2024-07-06T13:10:56Z")

</div>

Hi @CatalinaAlbornoz ,

Thank you very much. The modified code works well and finally prints out the gradient values! 😁

---

<div class="post-metadata">

**Author:** ![CatalinaAlbornoz](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/catalinaalbornoz/32/1196_2.png) [@CatalinaAlbornoz](https://discuss.pennylane.ai/u/CatalinaAlbornoz)\
**Post date:** [July 8, 2024, 7:17pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/8 "2024-07-08T19:17:56Z")

</div>

That’s great to hear @cyx617 !

---

<div class="post-metadata">

**Author:** ![mukulgupta](https://avatars.discourse-cdn.com/v4/letter/m/e68b1a/32.png) [@mukulgupta](https://discuss.pennylane.ai/u/mukulgupta)\
**Post date:** [December 12, 2024, 4:38pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/9 "2024-12-12T16:38:59Z")

</div>

> [@cyx617](#):
>
> Hi @CatalinaAlbornoz ,

Do we need to initialise weights here with the argument ‘requires\_grad = True’ for weights to get updated?

---

<div class="post-metadata">

**Author:** ![CatalinaAlbornoz](https://yyz2.discourse-cdn.com/flex012/user_avatar/discuss.pennylane.ai/catalinaalbornoz/32/1196_2.png) [@CatalinaAlbornoz](https://discuss.pennylane.ai/u/CatalinaAlbornoz)\
**Post date:** [December 12, 2024, 4:42pm UTC](https://discuss.pennylane.ai/t/how-to-get-gradients-of-quantum-circuit-with-pytorch-interface/4808/10 "2024-12-12T16:42:24Z")

</div>

Hi @mukulgupta, welcome to the Forum!

In general it’s best to do it.  
In many cases all parameters are treated as trainable by default, so this wouldn’t change anything, but depending on your specific setup (device, interface), things may not be trainable by default. So it’s always good practice to specify it.
