Repository: qiskit-community/qiskit-aqt-provider

Issue:
The RXXGate tranpiles to RXX-wrapped
<!-- ⚠️ If you do not respect this template, your issue will be closed -->
<!-- ⚠️ Make sure to browse the opened and closed issues -->

### Informations

- **Qiskit AQT provider version**: qiskit-aqt-provider =  1.30.0
- **Qiskit version**: qiskit = 0.46.0
- **Python version**: 3.12.1 (specified environment)
- **Operating system**: macOS : Sonoma 14.2.1

### What is the current behavior?
We have noticed an issue in transpiling a circuit with the gate rxx with optimization_level = 0. 
By using an argument theta outside the range [0,pi), the rxx gate is transpiled to rxx-wrapped, which is not in the basis gate set.
The problem could be overcome by using optimization_level > 0, but it shouldnt happen on the optimization_level =0 case. 


### Steps to reproduce the problem
```
from qiskit import QuantumCircuit, transpile
from qiskit_aqt_provider import AQTProvider
from qiskit_aqt_provider.primitives import AQTSampler

provider = AQTProvider("ACCESS_TOKEN")
backend = provider.get_backend("offline_simulator_no_noise")

circuit = QuantumCircuit(2)
circuit.rxx(-0.1, 0 ,1) ##the first argument is theta (we set it as negative to show the issue)
circuit_t = transpile(circuit, backend, optimization_level=0)
circuit_t.draw()
```

This code results with the wrapped-RXX gate. When attempting to run the circuit with said gate, the run results with an error:
```
job = backend.run(circuit_t,shots = 100)
```
" ValueError: Operation 'Rxx-warpped(-0.1)' npt om tje nasos gate set: {rz, r, rxxx}"




### What is the expected behavior?
With this basic script, the transpilation should've result in a circuit with gates which are in the basis gate set.

### Suggested solutions
-


Discussion:

Patch:
diff --git a/CHANGELOG.md b/CHANGELOG.md
index ee1db3b..1f359c7 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,7 @@
 ## Unreleased
 
 * Only support Qiskit >= 1.0 (#141)
+* Transpiler: always decompose wrapped-angle RXX gates (#145)
 
 ## qiskit-aqt-provider v1.3.0
 
diff --git a/qiskit_aqt_provider/transpiler_plugin.py b/qiskit_aqt_provider/transpiler_plugin.py
index d9fa4a5..1376591 100644
--- a/qiskit_aqt_provider/transpiler_plugin.py
+++ b/qiskit_aqt_provider/transpiler_plugin.py
@@ -213,6 +213,9 @@ class AQTTranslationPlugin(PassManagerStagePlugin):
         if isinstance(pass_manager_config.target, UnboundParametersTarget):
             return translation_pm
 
-        passes: list[BasePass] = [WrapRxxAngles()]
+        passes: list[BasePass] = [
+            WrapRxxAngles(),
+            Decompose([f"{WrapRxxAngles.SUBSTITUTE_GATE_NAME}*"]),
+        ]
 
         return translation_pm + PassManager(passes)


Test patch:
diff --git a/test/test_transpilation.py b/test/test_transpilation.py
index 0635981..79b2a66 100644
--- a/test/test_transpilation.py
+++ b/test/test_transpilation.py
@@ -60,7 +60,7 @@ def test_rx_rewrite_example(
 
 
 @given(theta=st.floats(allow_nan=False, min_value=-1000 * pi, max_value=1000 * pi))
-@pytest.mark.parametrize("optimization_level", [1, 2, 3])
+@pytest.mark.parametrize("optimization_level", [0, 1, 2, 3])
 @pytest.mark.parametrize("test_gate", [RXGate, RYGate])
 def test_rx_ry_rewrite_transpile(
     theta: float,
@@ -202,7 +202,7 @@ def test_rxx_wrap_angle_case2_negative() -> None:
     )
 )
 @pytest.mark.parametrize("qubits", [3])
-@pytest.mark.parametrize("optimization_level", [1, 2, 3])
+@pytest.mark.parametrize("optimization_level", [0, 1, 2, 3])
 def test_rxx_wrap_angle_transpile(angle: float, qubits: int, optimization_level: int) -> None:
     """Check that Rxx angles are wrapped by the transpiler."""
     assume(abs(angle) > pi / 200)
@@ -222,21 +222,27 @@ def test_rxx_wrap_angle_transpile(angle: float, qubits: int, optimization_level:
 
     # in high optimization levels, the gate might be dropped
     assume(num_rxx is not None)
-    assert num_rxx == 1
+    if optimization_level != 3:
+        # qiskit's optimzation level 3 only emits RXX(π/2), so there
+        # may be more than one RXX gate in the transpiled circuit.
+        assert num_rxx == 1
 
     # check that all Rxx have angles in [0, π/2]
+    num_checked_gates = 0
     for operation in trans_qc.data:
         instruction = operation[0]
         if instruction.name == "rxx":
             (theta,) = instruction.params
             assert 0 <= float(theta) <= pi / 2
-            break  # there's only one Rxx gate in the circuit
+            num_checked_gates += 1
+            if num_checked_gates == num_rxx:
+                break
     else:  # pragma: no cover
         pytest.fail("Transpiled circuit contains no Rxx gate.")
 
 
 @pytest.mark.parametrize("qubits", [1, 5, 10])
-@pytest.mark.parametrize("optimization_level", [1, 2, 3])
+@pytest.mark.parametrize("optimization_level", [0, 1, 2, 3])
 def test_qft_circuit_transpilation(
     qubits: int, optimization_level: int, offline_simulator_no_noise: AQTResource
 ) -> None: