Repository: PennyLaneAI/pennylane
Pull request #5497 (2024-04-11 13:37:03)

[bugfix] QNSPSAOptimizer improperly handles optimization on lightning.qubit
**Context:** The `QNSPSAOptimizer` improperly handles optimization on devices that don't follow the new API design.

**Description of the Change:** In the workflow of the `step_and_cost` method of `QNSPSAOptimizer`, the code now checks whether the device follows the new API guidelines before calling the `preprocess` function (which is not present in the old device interface). If this is not the case, the code simply calls the `qml.execute` function.

**Benefits:** Now, `qml.QNSPSAOptimizer` can handle legacy devices that do not follow the new API design, such as `default.qubit.legacy`. Therefore, the following code now works:

```
from pennylane import numpy as np
import pennylane as qml

dev = qml.device("default.qubit.legacy", wires=2)

@qml.qnode(dev)
def cost(params):
    qml.RX(params[0], wires=0)
    qml.CRY(params[1], wires=[0, 1])
    return qml.expval(qml.Z(0) @ qml.Z(1))

opt = qml.QNSPSAOptimizer(stepsize=5e-2)
params = np.random.rand(2)

for _ in range(10):
    params, loss = opt.step_and_cost(cost, params)
```

**Possible Drawbacks:** None that I can think of.

**Related GitHub Issues:** #5460

[sc-60727], [sc-60328]


github-actions[bot]: Hello. You may have forgotten to update the changelog!
Please edit [`doc/releases/changelog-dev.md`](/PennyLaneAI/pennylane/blob/master/doc/releases/changelog-dev.md) with:
* A one-to-two sentence description of the change. You may include a small working example for new features.
* A link back to this PR.
* Your name (or GitHub username) in the contributors section.
codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5497?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.66%. Comparing base [(`1089bb3`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/1089bb3448081f4fde7bc3b4b792f1abda3555ff?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`7d748a3`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5497?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 1 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5497      +/-   ##
==========================================
- Coverage   99.66%   99.66%   -0.01%     
==========================================
  Files         402      403       +1     
  Lines       37635    37390     -245     
==========================================
- Hits        37510    37264     -246     
- Misses        125      126       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5497?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).

codecov[bot]: ## [Codecov](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5497?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
All modified and coverable lines are covered by tests :white_check_mark:
> Project coverage is 99.66%. Comparing base [(`1089bb3`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/commit/1089bb3448081f4fde7bc3b4b792f1abda3555ff?dropdown=coverage&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) to head [(`d157bb6`)](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5497?dropdown=coverage&src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).
> Report is 5 commits behind head on master.


<details><summary>Additional details and impacted files</summary>


```diff
@@            Coverage Diff             @@
##           master    #5497      +/-   ##
==========================================
- Coverage   99.66%   99.66%   -0.01%     
==========================================
  Files         402      404       +2     
  Lines       37635    37556      -79     
==========================================
- Hits        37510    37430      -80     
- Misses        125      126       +1     
```



</details>

[:umbrella: View full report in Codecov by Sentry](https://app.codecov.io/gh/PennyLaneAI/pennylane/pull/5497?dropdown=coverage&src=pr&el=continue&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).   
:loudspeaker: Have feedback on the report? [Share it here](https://about.codecov.io/codecov-pr-comment-feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI).


Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index d0edbac6b..71880d760 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -339,6 +339,9 @@
 
 <h3>Bug fixes 🐛</h3>
 
+* The `qml.QNSPSAOptimizer` now correctly handles optimization for legacy devices that do not follow the new API design.
+  [(#5497)](https://github.com/PennyLaneAI/pennylane/pull/5497)
+
 * Operators applied to all wires are now drawn correctly in a circuit with mid-circuit measurements.
   [(#5501)](https://github.com/PennyLaneAI/pennylane/pull/5501)
 
diff --git a/pennylane/optimize/qnspsa.py b/pennylane/optimize/qnspsa.py
index be183d285..da1df886e 100644
--- a/pennylane/optimize/qnspsa.py
+++ b/pennylane/optimize/qnspsa.py
@@ -439,10 +439,16 @@ class QNSPSAOptimizer:
 
         cost.construct(params_next, kwargs)
         tape_loss_next = cost.tape.copy(copy_operations=True)
-        program, _ = cost.device.preprocess()
-        loss_curr, loss_next = qml.execute(
-            [tape_loss_curr, tape_loss_next], cost.device, None, transform_program=program
-        )
+
+        if isinstance(cost.device, qml.devices.Device):
+            program, _ = cost.device.preprocess()
+
+            loss_curr, loss_next = qml.execute(
+                [tape_loss_curr, tape_loss_next], cost.device, None, transform_program=program
+            )
+
+        else:
+            loss_curr, loss_next = qml.execute([tape_loss_curr, tape_loss_next], cost.device, None)
 
         # self.k has been updated earlier
         ind = (self.k - 2) % self.last_n_steps.size


Test patch:
diff --git a/tests/optimize/test_qnspsa.py b/tests/optimize/test_qnspsa.py
index 40c8bb37a..0ffd652c6 100644
--- a/tests/optimize/test_qnspsa.py
+++ b/tests/optimize/test_qnspsa.py
@@ -347,8 +347,12 @@ class TestQNSPSAOptimizer:
             new_params_tensor_expected,
         )
 
-    def test_step_and_cost_with_non_trainable_input(self, finite_diff_step, seed):
-        """Test step_and_cost() function with the qnode with non-trainable input."""
+    @pytest.mark.parametrize("device_name", ["default.qubit", "default.qubit.legacy"])
+    def test_step_and_cost_with_non_trainable_input(self, device_name, finite_diff_step, seed):
+        """
+        Test step_and_cost() function with the qnode with non-trainable input,
+        both using the `default.qubit` and `default.qubit.legacy` device.
+        """
         regularization = 1e-3
         stepsize = 1e-2
         opt = qml.QNSPSAOptimizer(
@@ -362,7 +366,7 @@ class TestQNSPSAOptimizer:
         )
         # a deep copy of the same opt, to be applied to qnode_reduced
         target_opt = deepcopy(opt)
-        dev = qml.device("default.qubit", wires=2)
+        dev = qml.device(device_name, wires=2)
         non_trainable_param = np.random.rand(1)
         non_trainable_param.requires_grad = False