Repository: PennyLaneAI/pennylane (python)

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.

Pull request description:
Fix `metric_tensor` with non-standard operations
**Context:**
The metric tensor for `Adjoint` operations is incorrect, see the linked issue.
This is caused by two different modes in which `qml.generator` is used within `qml.metric_tensor`.
This difference caused some parts to assume that the prefactor of the generator is contained in the applied gates
within the generated tapes, whereas other parts assumed that these prefactors still need to be taken into account in postprocessing. 

**Description of the Change:**
The usage of `qml.generator` is unified to the `"prefactor"` format, separating prefactors out into postprocessing.

**Benefits:**
`metric_tensor` produces correct results for non-standard ops, including `Adjoint`.

**Possible Drawbacks:**
N/A

**Related GitHub Issues:**
closes #3576 

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