Repository: PennyLaneAI/pennylane
Pull request #5631 (2024-05-02 20:16:35)

More accurate validation of trainable parameters for adjoint diff
**Context:**
When adjoint differentiation is used, a warning is incorrectly raised when the parameters of a measured observable is not actually trainable.

**Description of the Change:**
Update the `validate_adjoint_trainable_params` to use `qml.operation.is_trainable` instead of `tape.trainable_params`.

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.

Patch:
diff --git a/pennylane/devices/preprocess.py b/pennylane/devices/preprocess.py
index f61dd6f9a..148313a01 100644
--- a/pennylane/devices/preprocess.py
+++ b/pennylane/devices/preprocess.py
@@ -25,7 +25,6 @@ import pennylane as qml
 from pennylane import Snapshot
 from pennylane.operation import Tensor, StatePrepBase
 from pennylane.measurements import (
-    MeasurementProcess,
     StateMeasurement,
     SampleMeasurement,
 )
@@ -236,16 +235,13 @@ def validate_adjoint_trainable_params(
                 "Differentiating with respect to the input parameters of state-prep operations "
                 "is not supported with the adjoint differentiation method."
             )
-    for k in tape.trainable_params:
-        mp_or_op = tape[tape._par_info[k]["op_idx"]]
-        if isinstance(mp_or_op, MeasurementProcess):
+    for m in tape.measurements:
+        if m.obs and qml.operation.is_trainable(m.obs):
             warnings.warn(
-                "Differentiating with respect to the input parameters of "
-                f"{mp_or_op.obs.name} is not supported with the "
-                "adjoint differentiation method. Gradients are computed "
-                "only with regards to the trainable parameters of the circuit.\n\n Mark "
-                "the parameters of the measured observables as non-trainable "
-                "to silence this warning.",
+                f"Differentiating with respect to the input parameters of {m.obs.name} "
+                "is not supported with the adjoint differentiation method. Gradients are computed "
+                "only with regards to the trainable parameters of the circuit.\n\n Mark the "
+                "parameters of the measured observables as non-trainable to silence this warning.",
                 UserWarning,
             )
     return (tape,), null_postprocessing


Test patch:
diff --git a/tests/devices/default_qubit/test_default_qubit_preprocessing.py b/tests/devices/default_qubit/test_default_qubit_preprocessing.py
index 1c67fbd0b..19bd0b606 100644
--- a/tests/devices/default_qubit/test_default_qubit_preprocessing.py
+++ b/tests/devices/default_qubit/test_default_qubit_preprocessing.py
@@ -835,7 +835,7 @@ class TestAdjointDiffTapeValidation:
         expectation value of a Hermitian operator emits a warning if the
         parameters to Hermitian are trainable."""
 
-        mx = qml.matrix(qml.PauliX(0) @ qml.PauliY(2))
+        mx = qml.numpy.array(qml.matrix(qml.PauliX(0) @ qml.PauliY(2)))
         qs = qml.tape.QuantumScript([], [qml.expval(qml.Hermitian(mx, wires=[0, 2]))])
 
         qs.trainable_params = {0}
diff --git a/tests/devices/test_preprocess.py b/tests/devices/test_preprocess.py
index 33e1573c4..316626f50 100644
--- a/tests/devices/test_preprocess.py
+++ b/tests/devices/test_preprocess.py
@@ -12,6 +12,7 @@
 # See the License for the specific language governing permissions and
 # limitations under the License.
 """Unit tests for preprocess in devices/qubit."""
+import warnings
 
 import pytest
 
@@ -138,10 +139,18 @@ def test_no_sampling():
 
 def test_validate_adjoint_trainable_params_obs_warning():
     """Tests warning raised for validate_adjoint_trainable_params with trainable observables."""
-    tape = qml.tape.QuantumScript([], [qml.expval(2 * qml.PauliX(0))])
+
+    params = qml.numpy.array(0.123)
+    tape = qml.tape.QuantumScript([], [qml.expval(2 * qml.RX(params, wires=0))])
     with pytest.warns(UserWarning, match="Differentiating with respect to the input "):
         validate_adjoint_trainable_params(tape)
 
+    params_non_trainable = qml.numpy.array(0.123, requires_grad=False)
+    tape = qml.tape.QuantumScript([], [qml.expval(2 * qml.RX(params_non_trainable, wires=0))])
+    with warnings.catch_warnings():
+        warnings.simplefilter("error")  # assert no warning raised
+        validate_adjoint_trainable_params(tape)
+
 
 def test_validate_adjoint_trainable_params_state_prep_error():
     """Tests error raised for validate_adjoint_trainable_params with trainable state-preps."""