Repository: PennyLaneAI/pennylane
Pull request #1836 (2021-11-02 10:13:47)

Sorted `tape.get_parameters`
**Context:**
The `tape` method `get_parameters` relies on `tape.trainable_params` (if `trainable_only=True`), which is a `set`.
This means that if for some reason the internal ordering of `trainable_params` is not preserved (which is not guaranteed in python, in contrast to Python 3.7+ `dict`s), `get_parameters` will return the parameters in an incorrect order. In particular, the `classical_jacobian` outputs a permutation if this happens.

**Description of the Change:**
Adding `sorted` to the indices retrieved from `tape.trainable_params` within `tape.get_parameters`

**Benefits:**
The order of the parameters is stable.

**Possible Drawbacks:**
For large tapes this incurs an overhead, but as `trainable_params` typically is "sorted" already, this overhead is _very_ small:
```python
a, b, c = set(range(int(1e3))), set(range(int(1e4))), set(range(int(1e5)))
sorted(a) # Takes ~0.3 millisecs
sorted(b) # Takes ~1 millisecs
sorted(c) # Takes ~8 millisecs
```



josh146: Thanks @dwierichs! Although, this seems to have broken four tests:

```
tests/transforms/test_metric_tensor.py::TestMetricTensor::test_evaluate_block_diag_metric_tensor[parameter-shift]
tests/transforms/test_metric_tensor.py::TestMetricTensor::test_evaluate_block_diag_metric_tensor[backprop]
qchem/tests/test_excitations_to_wires.py::test_integration_with_uccsd[weights0-singles0-doubles0-expected0]
```

Super strange 🤔 

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index ed562c4b9..c4210032e 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -625,6 +625,10 @@
 
 <h3>Improvements</h3>
 
+* Updated the `qml.QNGOptimizer.step_and_cost` method to avoid the use of
+  deprecated functionality.
+  [(#1834)](https://github.com/PennyLaneAI/pennylane/pull/1834)
+
 * The default for an `Operation`'s `control_wires` attribute is now an empty `Wires`
   object instead of the attribute raising a `NonImplementedError`.
   [(#1821)](https://github.com/PennyLaneAI/pennylane/pull/1821)
@@ -877,10 +881,6 @@
   operations with similar properties in `ops/qubit/attributes.py`.
   [(#1763)](https://github.com/PennyLaneAI/pennylane/pull/1763)
 
-* The `template` decorator is now deprecated with a warning message and will be removed
-  in release `v0.20.0`.
-  [(#1794)](https://github.com/PennyLaneAI/pennylane/pull/1794)
-
 * The `qml.inv` function has been removed, `qml.adjoint` should be used
   instead.
   [(#1778)](https://github.com/PennyLaneAI/pennylane/pull/1778)
@@ -895,12 +895,6 @@
 
   If `hybrid=False`, the changed expansion rule might lead to a changed output.
 
-* The `qml.metric_tensor` keyword argument `diag_approx` is deprecated.
-  Approximations can be controlled with the more fine-grained `approx`
-  keyword argument, with `approx="block-diag"` (the default) reproducing
-  the old behaviour.
-  [(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)
-
 * The `default.qubit.torch` device automatically determines if computations
   should be run on a CPU or a GPU and doesn't take a `torch_device` argument
   anymore.
@@ -927,6 +921,19 @@
 
 <h3>Deprecations</h3>
 
+* The `qml.metric_tensor` and `qml.QNGOptimizer` keyword argument `diag_approx`
+  is deprecated.
+  Approximations can be controlled with the more fine-grained `approx` keyword
+  argument, with `approx="block-diag"` (the default) reproducing the old
+  behaviour.
+  [(#1721)](https://github.com/PennyLaneAI/pennylane/pull/1721)
+  [(#1834)](https://github.com/PennyLaneAI/pennylane/pull/1834)
+
+* The `template` decorator is now deprecated with a warning message and will be removed
+  in release `v0.20.0`. It has been removed from different PennyLane functions.
+  [(#1794)](https://github.com/PennyLaneAI/pennylane/pull/1794)
+  [(#1808)](https://github.com/PennyLaneAI/pennylane/pull/1808)
+
 * Allowing cost functions to be differentiated using `qml.grad` or
   `qml.jacobian` without explicitly marking parameters as trainable is being
   deprecated, and will be removed in the next release.
@@ -1026,6 +1033,10 @@
 
 <h3>Bug fixes</h3>
 
+* Fixes a bug where in rare instances the parameters of a tape are returned unsorted
+  by `Tape.get_parameters`.
+  [(#1836)](https://github.com/PennyLaneAI/pennylane/pull/1836)
+
 * Fixes a bug with the arrow width in the `measure` of `qml.circuit_drawer.MPLDrawer`. 
   [(#1823)](https://github.com/PennyLaneAI/pennylane/pull/1823)
 
diff --git a/pennylane/grouping/transformations.py b/pennylane/grouping/transformations.py
index 3bebff16c..5766099c4 100644
--- a/pennylane/grouping/transformations.py
+++ b/pennylane/grouping/transformations.py
@@ -18,7 +18,6 @@ measurement basis, and templates for the circuit implementations.
 import pennylane as qml
 from pennylane.operation import Tensor
 from pennylane.wires import Wires
-from pennylane.templates import template
 from pennylane.grouping.utils import (
     pauli_to_binary,
     are_identical_pauli_words,
@@ -28,7 +27,6 @@ from pennylane.grouping.utils import (
 import numpy as np
 
 
-@template
 def qwc_rotation(pauli_operators):
     """Performs circuit implementation of diagonalizing unitary for a Pauli word.
 
@@ -51,13 +49,16 @@ def qwc_rotation(pauli_operators):
             "All values of input pauli_operators must be either Identity, PauliX, PauliY, or PauliZ instances,"
             " instead got pauli_operators = {}.".format(pauli_operators)
         )
+    with qml.tape.OperationRecorder() as rec:
 
-    for pauli in pauli_operators:
-        if isinstance(pauli, qml.PauliX):
-            qml.RY(-np.pi / 2, wires=pauli.wires)
+        for pauli in pauli_operators:
+            if isinstance(pauli, qml.PauliX):
+                qml.RY(-np.pi / 2, wires=pauli.wires)
 
-        elif isinstance(pauli, qml.PauliY):
-            qml.RX(np.pi / 2, wires=pauli.wires)
+            elif isinstance(pauli, qml.PauliY):
+                qml.RX(np.pi / 2, wires=pauli.wires)
+
+    return rec.queue
 
 
 def diagonalize_pauli_word(pauli_word):
diff --git a/pennylane/ops/qubit/parametric_ops.py b/pennylane/ops/qubit/parametric_ops.py
index 9a820f818..f4622e202 100644
--- a/pennylane/ops/qubit/parametric_ops.py
+++ b/pennylane/ops/qubit/parametric_ops.py
@@ -23,7 +23,6 @@ import numpy as np
 import pennylane as qml
 from pennylane.operation import AnyWires, DiagonalOperation, Operation
 from pennylane.ops.qubit.non_parametric_ops import PauliX, PauliY, PauliZ, Hadamard
-from pennylane.templates.decorator import template
 from pennylane.utils import expand, pauli_eigs
 from pennylane.wires import Wires
 
@@ -512,15 +511,16 @@ class MultiRZ(DiagonalOperation):
         return self._eigvals(*self.parameters, len(self.wires))
 
     @staticmethod
-    @template
     def decomposition(theta, wires):
-        for i in range(len(wires) - 1, 0, -1):
-            qml.CNOT(wires=[wires[i], wires[i - 1]])
+        with qml.tape.OperationRecorder() as rec:
+            for i in range(len(wires) - 1, 0, -1):
+                qml.CNOT(wires=[wires[i], wires[i - 1]])
 
-        RZ(theta, wires=wires[0])
+            RZ(theta, wires=wires[0])
 
-        for i in range(len(wires) - 1):
-            qml.CNOT(wires=[wires[i + 1], wires[i]])
+            for i in range(len(wires) - 1):
+                qml.CNOT(wires=[wires[i + 1], wires[i]])
+        return rec.queue
 
     def adjoint(self):
         return MultiRZ(-self.parameters[0], wires=self.wires)
@@ -730,33 +730,33 @@ class PauliRot(Operation):
         return MultiRZ._eigvals(theta, len(pauli_word))
 
     @staticmethod
-    @template
     def decomposition(theta, pauli_word, wires):
         # Catch cases when the wire is passed as a single int.
         if isinstance(wires, int):
             wires = [wires]
+        with qml.tape.OperationRecorder() as rec:
+            # Check for identity and do nothing
+            if pauli_word == "I" * len(wires):
+                return []
 
-        # Check for identity and do nothing
-        if pauli_word == "I" * len(wires):
-            return
-
-        active_wires, active_gates = zip(
-            *[(wire, gate) for wire, gate in zip(wires, pauli_word) if gate != "I"]
-        )
+            active_wires, active_gates = zip(
+                *[(wire, gate) for wire, gate in zip(wires, pauli_word) if gate != "I"]
+            )
 
-        for wire, gate in zip(active_wires, active_gates):
-            if gate == "X":
-                Hadamard(wires=[wire])
-            elif gate == "Y":
-                RX(np.pi / 2, wires=[wire])
+            for wire, gate in zip(active_wires, active_gates):
+                if gate == "X":
+                    Hadamard(wires=[wire])
+                elif gate == "Y":
+                    RX(np.pi / 2, wires=[wire])
 
-        MultiRZ(theta, wires=list(active_wires))
+            MultiRZ(theta, wires=list(active_wires))
 
-        for wire, gate in zip(active_wires, active_gates):
-            if gate == "X":
-                Hadamard(wires=[wire])
-            elif gate == "Y":
-                RX(-np.pi / 2, wires=[wire])
+            for wire, gate in zip(active_wires, active_gates):
+                if gate == "X":
+                    Hadamard(wires=[wire])
+                elif gate == "Y":
+                    RX(-np.pi / 2, wires=[wire])
+        return rec.queue
 
     def adjoint(self):
         return PauliRot(-self.parameters[0], self.parameters[1], wires=self.wires)
diff --git a/pennylane/optimize/qng.py b/pennylane/optimize/qng.py
index 17a46e309..5f9f85fd0 100644
--- a/pennylane/optimize/qng.py
+++ b/pennylane/optimize/qng.py
@@ -14,8 +14,9 @@
 """Quantum natural gradient optimizer"""
 # pylint: disable=too-many-branches
 # pylint: disable=too-many-arguments
+import warnings
 
-import numpy as np
+from pennylane import numpy as np
 
 import pennylane as qml
 from pennylane.utils import _flatten, unflatten
@@ -150,9 +151,23 @@ class QNGOptimizer(GradientDescentOptimizer):
             to be applied at each optimization step
     """
 
-    def __init__(self, stepsize=0.01, diag_approx=False, lam=0):
+    def __init__(self, stepsize=0.01, approx="block-diag", diag_approx=None, lam=0):
         super().__init__(stepsize)
-        self.diag_approx = diag_approx
+
+        approx_set = False
+        if diag_approx is not None:
+
+            warnings.warn(
+                "The keyword argument diag_approx is deprecated. Please use approx='diag' instead.",
+                UserWarning,
+            )
+            if diag_approx:
+                self.approx = "diag"
+                approx_set = True
+
+        if not approx_set:
+            self.approx = approx
+
         self.metric_tensor = None
         self.lam = lam
 
@@ -192,13 +207,14 @@ class QNGOptimizer(GradientDescentOptimizer):
 
         if recompute_tensor or self.metric_tensor is None:
             if metric_tensor_fn is None:
-                metric_tensor_fn = qml.metric_tensor(qnode, diag_approx=self.diag_approx)
+
+                metric_tensor_fn = qml.metric_tensor(qnode, approx=self.approx)
 
             self.metric_tensor = metric_tensor_fn(*args, **kwargs)
             self.metric_tensor += self.lam * np.identity(self.metric_tensor.shape[0])
 
         g, forward = self.compute_grad(qnode, args, kwargs, grad_fn=grad_fn)
-        new_args = self.apply_grad(g, args)
+        new_args = np.array(self.apply_grad(g, args), requires_grad=True)
 
         if forward is None:
             forward = qnode(*args, **kwargs)
diff --git a/pennylane/tape/tape.py b/pennylane/tape/tape.py
index 8e12e56fc..186331773 100644
--- a/pennylane/tape/tape.py
+++ b/pennylane/tape/tape.py
@@ -803,13 +803,12 @@ class QuantumTape(AnnotatedQueue):
         [0.432, 0.543, 0.133]
         """
         params = []
-        iterator = self.trainable_params if trainable_only else self._par_info
+        iterator = sorted(self.trainable_params) if trainable_only else self._par_info
 
         for p_idx in iterator:
             op = self._par_info[p_idx]["op"]
             op_idx = self._par_info[p_idx]["p_idx"]
             params.append(op.data[op_idx])
-
         return params
 
     def set_parameters(self, params, trainable_only=True):
@@ -855,7 +854,7 @@ class QuantumTape(AnnotatedQueue):
         [4, 1, 6]
         """
         if trainable_only:
-            iterator = zip(self.trainable_params, params)
+            iterator = zip(sorted(self.trainable_params), params)
             required_length = self.num_params
         else:
             iterator = enumerate(params)
diff --git a/pennylane/templates/broadcast.py b/pennylane/templates/broadcast.py
index 95c2e75fe..d0daa83ad 100644
--- a/pennylane/templates/broadcast.py
+++ b/pennylane/templates/broadcast.py
@@ -21,7 +21,6 @@ To add a new pattern:
 """
 # pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
 import pennylane as qml
-from pennylane.templates.decorator import template
 from pennylane.wires import Wires
 
 OPTIONS = {"single", "double", "double_odd", "chain", "ring", "pyramid", "all_to_all", "custom"}
@@ -141,7 +140,6 @@ def _preprocess(parameters, pattern, wires):
     return wire_sequence, parameters
 
 
-@template
 def broadcast(unitary, wires, pattern, parameters=None, kwargs=None):
     r"""Applies a unitary multiple times to a specific pattern of wires.
 
@@ -268,9 +266,6 @@ def broadcast(unitary, wires, pattern, parameters=None, kwargs=None):
 
         .. code-block:: python
 
-            from pennylane.templates import template
-
-            @template
             def mytemplate(pars, wires):
                 qml.Hadamard(wires=wires)
                 qml.RY(pars, wires=wires)
@@ -307,9 +302,6 @@ def broadcast(unitary, wires, pattern, parameters=None, kwargs=None):
 
         .. code-block:: python
 
-            from pennylane.templates import template
-
-            @template
             def mytemplate(pars1, pars2, wires):
                 qml.Hadamard(wires=wires)
                 qml.RY(pars1, wires=wires)
@@ -340,7 +332,6 @@ def broadcast(unitary, wires, pattern, parameters=None, kwargs=None):
 
         .. code-block:: python
 
-            @template
             def mytemplate(pars, wires):
                 qml.Hadamard(wires=wires)
                 qml.RY(pars[0], wires=wires)
@@ -357,7 +348,6 @@ def broadcast(unitary, wires, pattern, parameters=None, kwargs=None):
 
         .. code-block:: python
 
-            @template
             def mytemplate(pars1, pars2, wires):
                 qml.Hadamard(wires=wires)
                 qml.RY(pars1, wires=wires)
@@ -377,7 +367,6 @@ def broadcast(unitary, wires, pattern, parameters=None, kwargs=None):
 
         .. code-block:: python
 
-            @template
             def mytemplate(wires, h=True):
                 if h:
                     qml.Hadamard(wires=wires)
diff --git a/pennylane/templates/layer.py b/pennylane/templates/layer.py
index 83593a89c..5b2348b3e 100644
--- a/pennylane/templates/layer.py
+++ b/pennylane/templates/layer.py
@@ -15,7 +15,6 @@ r"""
 Contains the ``layer`` template constructor.
 """
 # pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
-from pennylane.templates.decorator import template as temp
 from pennylane.math import shape
 
 
@@ -39,7 +38,6 @@ def _preprocess(args, depth):
             )
 
 
-@temp
 def layer(template, depth, *args, **kwargs):
     r"""Repeatedly applies a unitary a given number of times.
 
diff --git a/pennylane/templates/subroutines/arbitrary_unitary.py b/pennylane/templates/subroutines/arbitrary_unitary.py
index f25b5902d..31c1bd637 100644
--- a/pennylane/templates/subroutines/arbitrary_unitary.py
+++ b/pennylane/templates/subroutines/arbitrary_unitary.py
@@ -82,9 +82,8 @@ class ArbitraryUnitary(Operation):
 
     .. code-block:: python
 
-        @qml.template
         def arbitrary_nearest_neighbour_interaction(weights, wires):
-            qml.broadcast(unitary=ArbitraryUnitary, pattern="double", wires=wires, params=weights)
+            qml.broadcast(unitary=ArbitraryUnitary, pattern="double", wires=wires, parameters=weights)
 
     Args:
         weights (tensor_like): The angles of the Pauli word rotations, needs to have length :math:`4^n - 1`
diff --git a/pennylane/templates/subroutines/interferometer.py b/pennylane/templates/subroutines/interferometer.py
index bfc3b6de3..d0feb8b32 100644
--- a/pennylane/templates/subroutines/interferometer.py
+++ b/pennylane/templates/subroutines/interferometer.py
@@ -17,7 +17,6 @@ Contains the ``Interferometer`` template.
 import pennylane as qml
 
 # pylint: disable-msg=too-many-branches,too-many-arguments,protected-access
-from pennylane.templates.decorator import template
 from pennylane.ops import Beamsplitter, Rotation
 from pennylane.wires import Wires
 
@@ -55,7 +54,6 @@ def _preprocess(theta, phi, varphi, wires):
     return shape_varphi
 
 
-@template
 def Interferometer(theta, phi, varphi, wires, mesh="rectangular", beamsplitter="pennylane"):
     r"""General linear interferometer, an array of beamsplitters and phase shifters.
 
@@ -141,46 +139,48 @@ def Interferometer(theta, phi, varphi, wires, mesh="rectangular", beamsplitter="
 
     shape_varphi = _preprocess(theta, phi, varphi, wires)
 
-    if M == 1:
-        # the interferometer is a single rotation
-        Rotation(varphi[0], wires=wires[0])
-        return
-
-    n = 0  # keep track of free parameters
-
-    if mesh == "rectangular":
-        # Apply the Clements beamsplitter array
-        # The array depth is N
-        for l in range(M):
-            for k, (w1, w2) in enumerate(zip(wires[:-1], wires[1:])):
-                # skip even or odd pairs depending on layer
-                if (l + k) % 2 != 1:
-                    if beamsplitter == "clements":
-                        Rotation(phi[n], wires=Wires(w1))
-                        Beamsplitter(theta[n], 0, wires=Wires([w1, w2]))
-                    elif beamsplitter == "pennylane":
-                        Beamsplitter(theta[n], phi[n], wires=Wires([w1, w2]))
-                    else:
-                        raise ValueError(f"did not recognize beamsplitter {beamsplitter}")
-                    n += 1
-
-    elif mesh == "triangular":
-        # apply the Reck beamsplitter array
-        # The array depth is 2*N-3
-        for l in range(2 * M - 3):
-            for k in range(abs(l + 1 - (M - 1)), M - 1, 2):
-                if beamsplitter == "clements":
-                    Rotation(phi[n], wires=wires[k])
-                    Beamsplitter(theta[n], 0, wires=wires.subset([k, k + 1]))
-                elif beamsplitter == "pennylane":
-                    Beamsplitter(theta[n], phi[n], wires=wires.subset([k, k + 1]))
-                else:
-                    raise ValueError(f"did not recognize beamsplitter {beamsplitter} ")
-                n += 1
-    else:
-        raise ValueError(f"did not recognize mesh {mesh}")
-
-    # apply the final local phase shifts to all modes
-    for i in range(shape_varphi[0]):
-        act_on = wires[i]
-        Rotation(varphi[i], wires=act_on)
+    with qml.tape.OperationRecorder() as rec:
+
+        if M == 1:
+            # the interferometer is a single rotation
+            Rotation(varphi[0], wires=wires[0])
+        else:
+            n = 0  # keep track of free parameters
+
+            if mesh == "rectangular":
+                # Apply the Clements beamsplitter array
+                # The array depth is N
+                for l in range(M):
+                    for k, (w1, w2) in enumerate(zip(wires[:-1], wires[1:])):
+                        # skip even or odd pairs depending on layer
+                        if (l + k) % 2 != 1:
+                            if beamsplitter == "clements":
+                                Rotation(phi[n], wires=Wires(w1))
+                                Beamsplitter(theta[n], 0, wires=Wires([w1, w2]))
+                            elif beamsplitter == "pennylane":
+                                Beamsplitter(theta[n], phi[n], wires=Wires([w1, w2]))
+                            else:
+                                raise ValueError(f"did not recognize beamsplitter {beamsplitter}")
+                            n += 1
+
+            elif mesh == "triangular":
+                # apply the Reck beamsplitter array
+                # The array depth is 2*N-3
+                for l in range(2 * M - 3):
+                    for k in range(abs(l + 1 - (M - 1)), M - 1, 2):
+                        if beamsplitter == "clements":
+                            Rotation(phi[n], wires=wires[k])
+                            Beamsplitter(theta[n], 0, wires=wires.subset([k, k + 1]))
+                        elif beamsplitter == "pennylane":
+                            Beamsplitter(theta[n], phi[n], wires=wires.subset([k, k + 1]))
+                        else:
+                            raise ValueError(f"did not recognize beamsplitter {beamsplitter} ")
+                        n += 1
+            else:
+                raise ValueError(f"did not recognize mesh {mesh}")
+
+            # apply the final local phase shifts to all modes
+            for i in range(shape_varphi[0]):
+                act_on = wires[i]
+                Rotation(varphi[i], wires=act_on)
+    return rec.queue


Test patch:
diff --git a/tests/optimize/test_qng.py b/tests/optimize/test_qng.py
index 345e6cdd6..253cb7866 100644
--- a/tests/optimize/test_qng.py
+++ b/tests/optimize/test_qng.py
@@ -1,4 +1,4 @@
-# Copyright 2018-2020 Xanadu Quantum Technologies Inc.
+# Copyright 2018-2021 Xanadu Quantum Technologies Inc.
 
 # Licensed under the Apache License, Version 2.0 (the "License");
 # you may not use this file except in compliance with the License.
@@ -14,6 +14,7 @@
 """Tests for the QNG optimizer"""
 import pytest
 import scipy as sp
+import warnings
 
 import pennylane as qml
 from pennylane import numpy as np
@@ -228,7 +229,7 @@ class TestOptimize:
         # check final cost
         assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
 
-    def test_single_qubit_vqe_using_vqecost(self, tol):
+    def test_single_qubit_vqe_using_vqecost(self, tol, recwarn):
         """Test single-qubit VQE using ExpvalCost
         has the correct QNG value every step, the correct parameter updates,
         and correct cost after 200 steps"""
@@ -252,7 +253,7 @@ class TestOptimize:
             return np.array([da, db])
 
         eta = 0.01
-        init_params = np.array([0.011, 0.012])
+        init_params = np.array([0.011, 0.012], requires_grad=True)
         num_steps = 200
 
         opt = qml.QNGOptimizer(eta)
@@ -275,3 +276,65 @@ class TestOptimize:
 
         # check final cost
         assert np.allclose(cost_fn(theta), -1.41421356, atol=tol, rtol=0)
+        assert len(recwarn) == 0
+
+    def test_single_qubit_vqe_using_expval_h_multiple_input_params(self, tol, recwarn):
+        """Test single-qubit VQE by returning qml.expval(H) in the QNode and
+        check for the correct QNG value every step, the correct parameter updates, and
+        correct cost after 200 steps"""
+        dev = qml.device("default.qubit", wires=1)
+        coeffs = [1, 1]
+        obs_list = [qml.PauliX(0), qml.PauliZ(0)]
+
+        H = qml.Hamiltonian(coeffs=coeffs, observables=obs_list)
+
+        @qml.qnode(dev)
+        def circuit(x, y, wires=0):
+            qml.RX(x, wires=wires)
+            qml.RY(y, wires=wires)
+            return qml.expval(H)
+
+        eta = 0.01
+        x = np.array(0.011, requires_grad=True)
+        y = np.array(0.022, requires_grad=True)
+
+        def gradient(params):
+            """Returns the gradient"""
+            da = -np.sin(params[0]) * (np.cos(params[1]) + np.sin(params[1]))
+            db = np.cos(params[0]) * (np.cos(params[1]) - np.sin(params[1]))
+            return np.array([da, db])
+
+        eta = 0.01
+        num_steps = 200
+
+        opt = qml.QNGOptimizer(eta)
+
+        # optimization for 200 steps total
+        for t in range(num_steps):
+            theta = np.array([x, y])
+            x, y = opt.step(circuit, x, y)
+
+            # check metric tensor
+            res = opt.metric_tensor
+            exp = np.diag([0.25, (np.cos(x) ** 2) / 4])
+            assert np.allclose(res, exp, atol=0.00001, rtol=0)
+
+            # check parameter update
+            theta_new = np.array([x, y])
+            dtheta = eta * sp.linalg.pinvh(exp) @ gradient(theta)
+            assert np.allclose(dtheta, theta - theta_new, atol=0.000001, rtol=0)
+
+        # check final cost
+        assert np.allclose(circuit(x, y), -1.41421356, atol=tol, rtol=0)
+        assert len(recwarn) == 0
+
+    @pytest.mark.parametrize(
+        "diag_approx, approx_expected", [(True, "diag"), (False, "block-diag")]
+    )
+    def test_deprecate_diag_approx(self, diag_approx, approx_expected):
+        """Test that using the diag_approx argument raises a warning due to
+        deprecation."""
+        with pytest.warns(UserWarning, match="keyword argument diag_approx is deprecated"):
+            opt = qml.QNGOptimizer(0.1, diag_approx=True)
+
+        assert opt.approx == "diag"
diff --git a/tests/templates/test_broadcast.py b/tests/templates/test_broadcast.py
index 779f536e1..cfee3411b 100644
--- a/tests/templates/test_broadcast.py
+++ b/tests/templates/test_broadcast.py
@@ -26,38 +26,32 @@ from pennylane.templates.broadcast import wires_pyramid, wires_all_to_all, wires
 from pennylane.wires import Wires
 
 
-@template
 def ConstantTemplate(wires):
     T(wires=wires)
     S(wires=wires)
 
 
-@template
 def ParametrizedTemplate(par1, par2, wires):
     RX(par1, wires=wires)
     RY(par2, wires=wires)
 
 
-@template
 def KwargTemplate(par, wires, a=True):
     if a:
         T(wires=wires)
     RY(par, wires=wires)
 
 
-@template
 def ConstantTemplateDouble(wires):
     T(wires=wires[0])
     CNOT(wires=wires)
 
 
-@template
 def ParametrizedTemplateDouble(par1, par2, wires):
     CRX(par1, wires=wires)
     RY(par2, wires=wires[0])
 
 
-@template
 def KwargTemplateDouble(par, wires, a=True):
     if a:
         T(wires=wires[0])
diff --git a/tests/templates/test_layer.py b/tests/templates/test_layer.py
index 31b0935bb..cdc434c8b 100644
--- a/tests/templates/test_layer.py
+++ b/tests/templates/test_layer.py
@@ -21,7 +21,6 @@ import pennylane as qml
 from pennylane import layer, template
 
 
-@template
 def ConstantCircuit():
 
     qml.PauliX(wires=[0])
@@ -29,7 +28,6 @@ def ConstantCircuit():
     qml.PauliY(wires=[1])
 
 
-@template
 def StaticCircuit(wires, var):
 
     qml.CNOT(wires=[wires[3], wires[1]])
@@ -40,7 +38,6 @@ def StaticCircuit(wires, var):
         qml.Hadamard(wires=wires[0])
 
 
-@template
 def KwargCircuit(wires, **kwargs):
 
     qml.CNOT(wires=[wires[3], wires[1]])
@@ -51,7 +48,6 @@ def KwargCircuit(wires, **kwargs):
         qml.Hadamard(wires=wires[0])
 
 
-@template
 def DynamicCircuit(parameters):
 
     for i in range(2):
@@ -60,7 +56,6 @@ def DynamicCircuit(parameters):
     qml.MultiRZ(parameters[1], wires=[0, 1])
 
 
-@template
 def MultiCircuit(parameters1, parameters2, var1, wires, var2):
 
     if var2 == True:
diff --git a/tests/transforms/test_metric_tensor.py b/tests/transforms/test_metric_tensor.py
index 169a6cea1..fb5060de1 100644
--- a/tests/transforms/test_metric_tensor.py
+++ b/tests/transforms/test_metric_tensor.py
@@ -437,7 +437,7 @@ class TestMetricTensor:
             qml.RY(y, wires=1)
             qml.RZ(z, wires=2)
             non_parametrized_layer(a, b, c)
-            return qml.expval(qml.Hermitian(np.kron(Z, Y), wires=[2, 1]))
+            return qml.expval(qml.PauliY(1) @ qml.PauliZ(2))
 
         layer2_off_diag_second_order = qml.QNode(layer2_off_diag_second_order, dev)