Amplitude encoding and noise model

Hello!

I am trying to work with a hybrid neural network with noise models in Pennylane. As an example, I used the code in the demo of using torch layers in Turning quantum nodes into Torch Layers | PennyLane Demos. In this code, I want to use amplitude encoding instead of angle encoding and add a noise channel, such as a depolarizing channel. I have therefore used default.mixed simulator. However, I get the error after a few iterations, ValueError: State vectors have to be of norm 1.0, vector 0 has squared norm nan. This code works when I remove noise channel and work with default.qubit device. Also, if I remove the random seed and run on default.mixed without any noise, I get the same error for some of the runs. Therefore the bottom line issue is not able to perform training using default.mixed when amplitude encoding is used for quantum embedding.

Do you have any advice on how to make amplitude encoding work with noise models or using default.mixed? Please see the code that I using below. I appreciate your response. Thanks !

import pennylane as qml
import matplotlib.pyplot as plt
import numpy as np
from sklearn.datasets import make_moons
import torch

# Set random seeds
torch.manual_seed(42)
np.random.seed(42)

X, y = make_moons(n_samples=200, noise=0.1)
y_ = torch.unsqueeze(torch.tensor(y), 1)  # used for one-hot encoded labels
y_hot = torch.scatter(torch.zeros((200, 2)), 1, y_, 1)


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

@qml.qnode(dev)
def qnode(inputs, weights):
    qml.AmplitudeEmbedding(inputs, wires=range(n_qubits), normalize=True)
    qml.BasicEntanglerLayers(weights, wires=range(n_qubits))
    qml.DepolarizingChannel(0.1, wires=0)
    return [qml.expval(qml.PauliZ(wires=i)) for i in range(n_qubits)]

n_layers = 6
weight_shapes = {"weights": (n_layers, n_qubits)}

qlayer = qml.qnn.TorchLayer(qnode, weight_shapes)
clayer_1 = torch.nn.Linear(2, 4)
clayer_2 = torch.nn.Linear(2, 2)
softmax = torch.nn.Softmax(dim=1)
layers = [clayer_1, qlayer, clayer_2, softmax]
model = torch.nn.Sequential(*layers)

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

X = torch.tensor(X, requires_grad=True).float()
y_hot = y_hot.float()

batch_size = 5
batches = 200 // batch_size

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

epochs = 6

for epoch in range(epochs):

    running_loss = 0

    for xs, ys in data_loader:
        opt.zero_grad()

        loss_evaluated = loss(model(xs), ys)
        loss_evaluated.backward()

        opt.step()

        running_loss += loss_evaluated

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

y_pred = model(X)
predictions = torch.argmax(y_pred, axis=1).detach().numpy()

correct = [1 if p == p_true else 0 for p, p_true in zip(predictions, y)]
accuracy = sum(correct) / len(correct)
print(f"Accuracy: {accuracy * 100}%")

full error message below:

Average loss over epoch 1: 0.4693
Average loss over epoch 2: 0.3122
Average loss over epoch 3: 0.2106
Average loss over epoch 4: 0.1749
Traceback (most recent call last):
  File "C:\Users\hedge\AppData\Roaming\JetBrains\PyCharmEdu2022.1\scratches\scratch_1.py", line 58, in <module>
    loss_evaluated = loss(model(xs), ys)
                          ^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\torch\nn\modules\module.py", line 1532, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\torch\nn\modules\module.py", line 1541, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\torch\nn\modules\container.py", line 217, in forward
    input = module(input)
            ^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\torch\nn\modules\module.py", line 1532, in _wrapped_call_impl
    return self._call_impl(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\torch\nn\modules\module.py", line 1541, in _call_impl
    return forward_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\qnn\torch.py", line 404, in forward
    results = self._evaluate_qnode(inputs)
              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\qnn\torch.py", line 430, in _evaluate_qnode
    res = self.qnode(**kwargs)
          ^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\workflow\qnode.py", line 905, in __call__
    return self._impl_call(*args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\workflow\qnode.py", line 881, in _impl_call
    res = qml.execute(
          ^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\workflow\execution.py", line 227, in execute
    tapes, post_processing = transform_program(tapes)
                             ^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\transforms\core\transform_program.py", line 580, in __call__
    new_tapes, fn = transform(tape, *targs, **tkwargs)
                    ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\devices\legacy_facade.py", line 92, in legacy_device_expand_fn
    new_tape = _set_shots(device, tape.shots)(device.expand_fn)(tape)
               ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\AppData\Local\Programs\Python\Python311\Lib\contextlib.py", line 81, in inner
    return func(*args, **kwds)
           ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\devices\_legacy_device.py", line 715, in expand_fn
    return self.default_expand_fn(circuit, max_expansion=max_expansion)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\devices\_legacy_device.py", line 687, in default_expand_fn
    circuit = _local_tape_expand(
              ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\devices\_legacy_device.py", line 79, in _local_tape_expand
    obj = QuantumScript(obj.decomposition())
                        ^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\operation.py", line 1321, in decomposition
    return self.compute_decomposition(
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\ops\qubit\state_preparation.py", line 334, in compute_decomposition
    return [MottonenStatePreparation(state, wires)]
            ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\capture\capture_meta.py", line 89, in __call__
    return type.__call__(cls, *args, **kwargs)
           ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages\pennylane\templates\state_preparations\mottonen.py", line 317, in __init__
    raise ValueError(
ValueError: State vectors have to be of norm 1.0, vector 0 has squared norm nan

the output of qml.about().

Name: PennyLane
Version: 0.40.0
Summary: 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.
Home-page: https://github.com/PennyLaneAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: C:\Users\hedge\PycharmProjects\cfd-quantum\venv\Lib\site-packages
Requires: appdirs, autograd, autoray, cachetools, diastatic-malt, networkx, numpy, packaging, pennylane-lightning, requests, rustworkx, scipy, tomlkit, typing-extensions
Required-by: PennyLane-qiskit, PennyLane-Rigetti, PennyLane_Lightning

Platform info:           Windows-10-10.0.26100-SP0
Python version:          3.11.9
Numpy version:           1.26.4
Scipy version:           1.14.1
Installed devices:
- default.clifford (PennyLane-0.40.0)
- default.gaussian (PennyLane-0.40.0)
- default.mixed (PennyLane-0.40.0)
- default.qubit (PennyLane-0.40.0)
- default.qutrit (PennyLane-0.40.0)
- default.qutrit.mixed (PennyLane-0.40.0)
- default.tensor (PennyLane-0.40.0)
- null.qubit (PennyLane-0.40.0)
- reference.qubit (PennyLane-0.40.0)
- lightning.qubit (PennyLane_Lightning-0.40.0)
- qiskit.aer (PennyLane-qiskit-0.41.0.post0)
- qiskit.basicaer (PennyLane-qiskit-0.41.0.post0)
- qiskit.basicsim (PennyLane-qiskit-0.41.0.post0)
- qiskit.remote (PennyLane-qiskit-0.41.0.post0)
- rigetti.numpy_wavefunction (PennyLane-Rigetti-0.36.0.post0)
- rigetti.qpu (PennyLane-Rigetti-0.36.0.post0)
- rigetti.qvm (PennyLane-Rigetti-0.36.0.post0)
- rigetti.wavefunction (PennyLane-Rigetti-0.36.0.post0)

After reading similar issues about weights having NaN values in classical neural networks, in Pytorch forum, I tried some methods, like reducing leaning rate, and changing activation function to tanh. It seems to reduce the occurance of NaN values (which I read happens due to exploding gardients). However, the same issue is not happened when using statevector simulation, but happens in density matrix calculation when using amplitude encoding. In fact, I did not face the issue using angle embedding using density matrix. If someone has any insights on why amplitude encoding may not work with density matrix formalism, please let me know. Thank you!

Hi @Pratibha_Hegde ,

Thanks for posting these insights here.
I’m using PennyLane version v0.41.1 and I get a different error, but similarly I find that default.qubit works well while default.mixed doesn’t.

When this happens it’s usually because some of the functions under the hood behave differently. For instance, some gradients and decompositions may be defined for default.qubit but not default.mixed, leading to different methods and approximations.

In your case the problem seems to be caused by MottonenStatePreparation. If you check out the docs you can see more details about it.

If you can use a different embedding that might be the easiest solution. Debugging problems with MottonenStatePreparation is usually not so easy. You can also try using Torch directly instead of using Torchlayer and/or removing the batching. This could potentially simplify the problem and make it easier to resolve.

I hope this helps!