Repository: PennyLaneAI/pennylane

Issue:
[BUG] `metric_tensor` does not take `qml.adjoint` correctly into account
### Expected behavior

The transform `metric_tensor` respects the `qml.adjoint` transform.

### Actual behavior

In the following example, the `adjoint` of one particular operation in the circuit is realized in three ways:
1. Via classical preprocessing of the rotation angle, corresponding to inversion.
2. Via the deprecated in-place inversion, yielding the same metric tensor as 1.
3. Via `qml.adjoint`, yielding a differing metric tensor.

```python
dev = qml.device("default.qubit", wires=3)

@qml.qnode(dev, interface="jax")
def circuit(x):
    qml.RX(x[0], wires=0)
    qml.CNOT([0, 1])
    qml.RZ(0.4, wires=1)
    qml.CNOT([0, 1])
    qml.RX(x[0], wires=0)
#     qml.RZ(-x[1], wires=0) # Produces the correct MT via preprocessing
#     qml.RZ(x[1], wires=0).inv() # Produces the correct MT
#     qml.adjoint(qml.RZ)(x[1], wires=0) # Produces the wrong MT
    return qml.expval(qml.PauliZ(0))

x = pnp.array([0.543, 0.352], requires_grad = True)
y = pnp.array(-0.654, requires_grad = True)

print(qml.transforms.metric_tensor(circuit)(x))
```

### Additional information

_No response_

### Source code

_No response_

### Tracebacks

_No response_

### System information

```shell
Name: PennyLane
Version: 0.28.0.dev0
Summary: PennyLane is a Python quantum machine learning library by Xanadu Inc.
Home-page: https://github.com/XanaduAI/pennylane
Author: 
Author-email: 
License: Apache License 2.0
Location: /home/david/venvs/xenv/lib/python3.10/site-packages
Requires: appdirs, autograd, autoray, cachetools, networkx, numpy, pennylane-lightning, requests, retworkx, scipy, semantic-version, toml
Required-by: PennyLane-Lightning

Platform info:           Linux-5.15.0-56-generic-x86_64-with-glibc2.35
Python version:          3.10.6
Numpy version:           1.23.4
Scipy version:           1.9.3
Installed devices:
- default.gaussian (PennyLane-0.28.0.dev0)
- default.mixed (PennyLane-0.28.0.dev0)
- default.qubit (PennyLane-0.28.0.dev0)
- default.qubit.autograd (PennyLane-0.28.0.dev0)
- default.qubit.jax (PennyLane-0.28.0.dev0)
- default.qubit.tf (PennyLane-0.28.0.dev0)
- default.qubit.torch (PennyLane-0.28.0.dev0)
- default.qutrit (PennyLane-0.28.0.dev0)
- null.qubit (PennyLane-0.28.0.dev0)
- lightning.qubit (PennyLane-Lightning-0.27.0)
```


### Existing GitHub issues

- [X] I have searched existing GitHub issues to make sure the issue does not already exist.

Discussion:
codecov[bot]: # [Codecov](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3579?src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) Report
> Merging [#3579](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3579?src=pr&el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (10ccda5) into [master](https://codecov.io/gh/PennyLaneAI/pennylane/commit/b3b0bf8526d279bc88dc0a284f4dd1671eef236d?el=desc&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) (b3b0bf8) will **not change** coverage.
> The diff coverage is `100.00%`.

```diff
@@           Coverage Diff           @@
##           master    #3579   +/-   ##
=======================================
  Coverage   99.87%   99.87%           
=======================================
  Files         314      314           
  Lines       28019    28020    +1     
=======================================
+ Hits        27983    27984    +1     
  Misses         36       36           
```


| [Impacted Files](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3579?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI) | Coverage Δ | |
|---|---|---|
| [pennylane/transforms/metric\_tensor.py](https://codecov.io/gh/PennyLaneAI/pennylane/pull/3579/diff?src=pr&el=tree&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI#diff-cGVubnlsYW5lL3RyYW5zZm9ybXMvbWV0cmljX3RlbnNvci5weQ==) | `100.00% <100.00%> (ø)` | |

Help us with your feedback. Take ten seconds to tell us [how you rate us](https://about.codecov.io/nps?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI). Have a feature suggestion? [Share it here.](https://app.codecov.io/gh/feedback/?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=PennyLaneAI)

josh146: Nice catch @dwierichs! Thinking out loud here --- should we remove the 'legacy' modes from `qml.generator`?
dwierichs: >should we remove the 'legacy' modes from qml.generator? 

I'm not sure. Within the codebase, `qml.generator` is not used in a lot of places, but both `format="observable"` and the default `format="prefactor"` are used.

Patch:
diff --git a/doc/releases/changelog-dev.md b/doc/releases/changelog-dev.md
index 6adf859bb..d2c4de503 100644
--- a/doc/releases/changelog-dev.md
+++ b/doc/releases/changelog-dev.md
@@ -172,6 +172,10 @@
 
 <h3>Bug fixes</h3>
 
+* Fixed a bug in `qml.transforms.metric_tensor` where prefactors of operation generators were taken
+  into account multiple times, leading to wrong outputs for non-standard operations.
+  [(#3579)](https://github.com/PennyLaneAI/pennylane/pull/3579)
+
 * Uses a local random number generator where possible to avoid mutating the global random state.
   [(#3624)](https://github.com/PennyLaneAI/pennylane/pull/3624)
 
@@ -181,7 +185,7 @@
 
 * Fixed the wires for the Y decomposition in the ZX calculus transform.
   [(#3598)](https://github.com/PennyLaneAI/pennylane/pull/3598)
-*
+
 * `qml.pauli.PauliWord` is now pickle-able.
   [(#3588)](https://github.com/PennyLaneAI/pennylane/pull/3588)
 
diff --git a/pennylane/transforms/metric_tensor.py b/pennylane/transforms/metric_tensor.py
index 080d86d49..f016eb2aa 100644
--- a/pennylane/transforms/metric_tensor.py
+++ b/pennylane/transforms/metric_tensor.py
@@ -479,7 +479,7 @@ def _get_gen_op(op, allow_nonunitary, aux_wire):
 
     except KeyError as e:
         if allow_nonunitary:
-            mat = qml.matrix(qml.generator(op, format="observable"))
+            mat = qml.matrix(qml.generator(op)[0])
             return qml.ControlledQubitUnitary(mat, control_wires=aux_wire, wires=op.wires)
 
         raise ValueError(


Test patch:
diff --git a/tests/returntypes/transforms/test_metric_tensor_new.py b/tests/returntypes/transforms/test_metric_tensor_new.py
index 7cf36b62b..4adefef5c 100644
--- a/tests/returntypes/transforms/test_metric_tensor_new.py
+++ b/tests/returntypes/transforms/test_metric_tensor_new.py
@@ -317,7 +317,7 @@ class TestMetricTensor:
             x, y, z, h, g, f = params
             non_parametrized_layer(a, b, c)
             qml.RX(x, wires=0)
-            qml.RY(-y, wires=1).inv()
+            qml.adjoint(qml.RY)(-y, wires=1)
             qml.RZ(z, wires=2)
             non_parametrized_layer(a, b, c)
             qml.RY(f, wires=1)
@@ -735,10 +735,10 @@ def fubini_ansatz2(params, wires=None):
     qml.RX(fixed_pars[1], wires=0)
     qml.Rot(*fixed_pars[2:5], wires=1)
     qml.CNOT(wires=[0, 1])
-    qml.RY(params0, wires=0)
+    qml.RY(params0, wires=0) ** 0.4
     qml.RY(params0, wires=1)
     qml.CNOT(wires=[0, 1])
-    qml.RX(params1, wires=0).inv()
+    qml.adjoint(qml.RX)(params1, wires=0)
     qml.RX(params1, wires=1).inv()
 
 
diff --git a/tests/transforms/test_metric_tensor.py b/tests/transforms/test_metric_tensor.py
index 796cc32f0..6effbbc4f 100644
--- a/tests/transforms/test_metric_tensor.py
+++ b/tests/transforms/test_metric_tensor.py
@@ -104,7 +104,8 @@ class TestMetricTensor:
             qml.RY(np.array(1.0, requires_grad=True), wires=0)
             qml.CNOT(wires=[0, 1])
             qml.PhaseShift(np.array(1.0, requires_grad=True), wires=1)
-            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1))
+            qml.expval(qml.PauliX(0))
+            qml.expval(qml.PauliX(1))
 
         tape = qml.tape.QuantumScript.from_queue(q)
         tapes, _ = qml.metric_tensor(tape, approx="block-diag")
@@ -123,12 +124,10 @@ class TestMetricTensor:
         assert isinstance(tapes[1].operations[3], qml.Hadamard)
 
         # third parameter subcircuit
-        assert len(tapes[2].operations) == 4
+        assert len(tapes[2].operations) == 3
         assert isinstance(tapes[2].operations[0], qml.RX)
         assert isinstance(tapes[2].operations[1], qml.RY)
         assert isinstance(tapes[2].operations[2], qml.CNOT)
-        # Phase shift generator
-        assert isinstance(tapes[2].operations[3], qml.QubitUnitary)
 
     def test_construct_subcircuit_layers(self):
         """Test correct subcircuits constructed
@@ -155,7 +154,9 @@ class TestMetricTensor:
             qml.RZ(params[7], wires=2)
             qml.CNOT(wires=[0, 1])
             qml.CNOT(wires=[1, 2])
-            return qml.expval(qml.PauliX(0)), qml.expval(qml.PauliX(1)), qml.expval(qml.PauliX(2))
+            qml.expval(qml.PauliX(0))
+            qml.expval(qml.PauliX(1))
+            qml.expval(qml.PauliX(2))
 
         tape = qml.tape.QuantumScript.from_queue(q)
         tapes, _ = qml.metric_tensor(tape, approx="block-diag")
@@ -315,7 +316,7 @@ class TestMetricTensor:
             x, y, z, h, g, f = params
             non_parametrized_layer(a, b, c)
             qml.RX(x, wires=0)
-            qml.RY(-y, wires=1).inv()
+            qml.adjoint(qml.RY)(-y, wires=1)
             qml.RZ(z, wires=2)
             non_parametrized_layer(a, b, c)
             qml.RY(f, wires=1)
@@ -736,7 +737,7 @@ def fubini_ansatz2(params, wires=None):
     qml.RY(params0, wires=0)
     qml.RY(params0, wires=1)
     qml.CNOT(wires=[0, 1])
-    qml.RX(params1, wires=0).inv()
+    qml.adjoint(qml.RX)(params1, wires=0)
     qml.RX(params1, wires=1).inv()
 
 
@@ -770,7 +771,7 @@ def fubini_ansatz4(params00, params_rest, wires=None):
     qml.CNOT(wires=[0, 1])
     qml.CNOT(wires=[1, 2])
     qml.RY(fixed_pars[4], wires=0)
-    qml.RX(params00, wires=0)
+    qml.RX(params00, wires=0) ** 0.5
     qml.CNOT(wires=[0, 1])
     qml.RX(params01, wires=1)
     qml.RZ(params10, wires=1)
@@ -809,7 +810,7 @@ def fubini_ansatz8(params, wires=None):
     qml.RZ(fixed_pars[0], wires=[1])
     qml.CNOT(wires=[0, 1])
     qml.RX(params0, wires=[0])
-    qml.RX(params0, wires=[1])
+    qml.IsingXX(params0, wires=[1, 0])
     qml.CNOT(wires=[0, 1])
     qml.RY(fixed_pars[4], wires=[1])
     qml.RY(params1, wires=[0])
@@ -1081,6 +1082,7 @@ class TestDifferentiability:
     dev = qml.device("default.qubit", wires=3)
 
     @pytest.mark.autograd
+    @pytest.mark.filterwarnings("ignore:Attempted to compute the gradient")
     def test_autograd_diag(self, diff_method, tol, ansatz, weights, expected_diag_jac):
         """Test metric tensor differentiability in the autograd interface"""
         circuit = self.get_circuit(ansatz)
@@ -1108,6 +1110,7 @@ class TestDifferentiability:
             assert qml.math.allclose(jac, expected_diag_jac(*weights), atol=tol, rtol=0)
 
     @pytest.mark.autograd
+    @pytest.mark.filterwarnings("ignore:Attempted to compute the gradient")
     def test_autograd(self, diff_method, tol, ansatz, weights, expected_diag_jac):
         """Test metric tensor differentiability in the autograd interface"""
         circuit = self.get_circuit(ansatz)
@@ -1324,6 +1327,7 @@ def test_error_not_available_aux_wire():
         qml.metric_tensor(circuit, aux_wire=404)(x)
 
 
+@pytest.mark.filterwarnings("ignore:An auxiliary wire is not available")
 def test_error_aux_wire_replaced():
     """Tests that even if an aux_wire is provided, it is superseded by a device
     wire if it does not exist itself on the device, so that the metric_tensor is