Repository: Qiskit/qiskit

Issue:
BackendV2Converter fails to wrap Aer simulator
### Environment

- **Qiskit Terra version**: 8ec6749
- **Qiskit Aer version**: 0.11.2
- **Python version**: 3.10.9
- **Operating system**: Arch Linux


### What is happening?

See title.

### How can we reproduce the issue?

```python
from qiskit import Aer
from qiskit.providers import BackendV2Converter
backend = BackendV2Converter(Aer.get_backend('aer_simulator'))
```

```
KeyError                                  Traceback (most recent call last)
~/projects/qiskit-terra/qiskit/providers/models/backendconfiguration.py in __getattr__(self, name)
    383         try:
--> 384             return self._data[name]
    385         except KeyError as ex:

KeyError: 'online_date'

The above exception was the direct cause of the following exception:

AttributeError                            Traceback (most recent call last)
<ipython-input-1-38c84b7d22f9> in <module>
      1 from qiskit import Aer
      2 from qiskit.providers import BackendV2Converter
----> 3 backend = BackendV2Converter(Aer.get_backend('aer_simulator'))

~/projects/qiskit-terra/qiskit/providers/backend_compat.py in __init__(self, backend, name_mapping, add_delay)
    221             name=backend.name(),
    222             description=self._config.description,
--> 223             online_date=self._config.online_date,
    224             backend_version=self._config.backend_version,
    225         )

~/projects/qiskit-terra/qiskit/providers/models/backendconfiguration.py in __getattr__(self, name)
    384             return self._data[name]
    385         except KeyError as ex:
--> 386             raise AttributeError(f"Attribute {name} is not defined") from ex
    387 
    388     @classmethod

AttributeError: Attribute online_date is not defined
```

### What should happen?

It should work.

### Any suggestions?

From @mtreinish :
>The converter is meant to be general but we never tested it with aer and I guess it misses that the online date field is optional


Discussion:

Resolving patch:
diff --git a/qiskit/providers/backend_compat.py b/qiskit/providers/backend_compat.py
--- a/qiskit/providers/backend_compat.py
+++ b/qiskit/providers/backend_compat.py
@@ -138,9 +138,7 @@ def convert_to_target(
     for op in combined_global_ops:
         if op not in target:
             if op in name_mapping:
-                target.add_instruction(
-                    name_mapping[op], {(bit,): None for bit in range(target.num_qubits)}
-                )
+                target.add_instruction(name_mapping[op], name=op)
             else:
                 raise QiskitError(
                     f"Operation name '{op}' does not have a known mapping. Use "
@@ -220,7 +218,7 @@ def __init__(
             provider=backend.provider,
             name=backend.name(),
             description=self._config.description,
-            online_date=self._config.online_date,
+            online_date=getattr(self._config, "online_date", None),
             backend_version=self._config.backend_version,
         )
         self._options = self._backend._options


Test patch:
diff --git a/test/python/providers/test_fake_backends.py b/test/python/providers/test_fake_backends.py
--- a/test/python/providers/test_fake_backends.py
+++ b/test/python/providers/test_fake_backends.py
@@ -14,6 +14,7 @@
 # pylint: disable=missing-module-docstring
 
 import operator
+import unittest
 
 from test import combine
 from ddt import ddt, data
@@ -34,6 +35,30 @@
 from qiskit.providers.backend_compat import BackendV2Converter
 from qiskit.providers.backend import BackendV2
 from qiskit.utils import optionals
+from qiskit.circuit.library import (
+    SXGate,
+    MCPhaseGate,
+    MCXGate,
+    RZGate,
+    RXGate,
+    U2Gate,
+    U1Gate,
+    U3Gate,
+    YGate,
+    ZGate,
+    PauliGate,
+    SwapGate,
+    RGate,
+    MCXGrayCode,
+    RYGate,
+)
+from qiskit.circuit import ControlledGate, Parameter
+from qiskit.quantum_info.operators.channel.quantum_channel import QuantumChannel
+from qiskit.quantum_info.operators.channel.kraus import Kraus
+from qiskit.quantum_info.operators.channel import SuperOp
+from qiskit.extensions import Initialize, UnitaryGate
+from qiskit.extensions.quantum_initializer import DiagonalGate, UCGate
+from qiskit.circuit.controlflow import IfElseOp, WhileLoopOp, ForLoopOp
 
 FAKE_PROVIDER_FOR_BACKEND_V2 = FakeProviderForBackendV2()
 FAKE_PROVIDER = FakeProvider()
@@ -193,3 +218,243 @@ def test_converter_delay_circuit(self):
         qc.measure_all()
         res = transpile(qc, backend_v2)
         self.assertIn("delay", res.count_ops())
+
+    @unittest.skipUnless(optionals.HAS_AER, "Aer required for this test")
+    def test_converter_simulator(self):
+        class MCSXGate(ControlledGate):
+            def __init__(self, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcsx",
+                    1 + num_ctrl_qubits,
+                    [],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=SXGate(),
+                )
+
+        class MCYGate(ControlledGate):
+            def __init__(self, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcy",
+                    1 + num_ctrl_qubits,
+                    [],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=YGate(),
+                )
+
+        class MCZGate(ControlledGate):
+            def __init__(self, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcz",
+                    1 + num_ctrl_qubits,
+                    [],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=ZGate(),
+                )
+
+        class MCRXGate(ControlledGate):
+            def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcrx",
+                    1 + num_ctrl_qubits,
+                    [theta],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=RXGate(theta),
+                )
+
+        class MCRYGate(ControlledGate):
+            def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcry",
+                    1 + num_ctrl_qubits,
+                    [theta],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=RYGate(theta),
+                )
+
+        class MCRZGate(ControlledGate):
+            def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcrz",
+                    1 + num_ctrl_qubits,
+                    [theta],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=RZGate(theta),
+                )
+
+        class MCRGate(ControlledGate):
+            def __init__(self, theta, phi, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcr",
+                    1 + num_ctrl_qubits,
+                    [theta, phi],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=RGate(theta, phi),
+                )
+
+        class MCU1Gate(ControlledGate):
+            def __init__(self, theta, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcu1",
+                    1 + num_ctrl_qubits,
+                    [theta],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=U1Gate(theta),
+                )
+
+        class MCU2Gate(ControlledGate):
+            def __init__(self, theta, lam, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcu2",
+                    1 + num_ctrl_qubits,
+                    [theta, lam],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=U2Gate(theta, lam),
+                )
+
+        class MCU3Gate(ControlledGate):
+            def __init__(self, theta, lam, phi, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcu3",
+                    1 + num_ctrl_qubits,
+                    [theta, phi, lam],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=U3Gate(theta, phi, lam),
+                )
+
+        class MCUGate(ControlledGate):
+            def __init__(self, theta, lam, phi, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcu",
+                    1 + num_ctrl_qubits,
+                    [theta, phi, lam],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=U3Gate(theta, phi, lam),
+                )
+
+        class MCSwapGate(ControlledGate):
+            def __init__(self, num_ctrl_qubits, ctrl_state=None):
+                super().__init__(
+                    "mcswap",
+                    2 + num_ctrl_qubits,
+                    [],
+                    None,
+                    num_ctrl_qubits,
+                    ctrl_state=ctrl_state,
+                    base_gate=SwapGate(),
+                )
+
+        from qiskit_aer import AerSimulator
+        from qiskit_aer.library import (
+            SaveExpectationValue,
+            SaveAmplitudes,
+            SaveStatevectorDict,
+            SaveSuperOp,
+            SaveClifford,
+            SaveMatrixProductState,
+            SaveDensityMatrix,
+            SaveProbabilities,
+            SaveStatevector,
+            SetDensityMatrix,
+            SetUnitary,
+            SaveState,
+            SetMatrixProductState,
+            SaveUnitary,
+            SetSuperOp,
+            SaveExpectationValueVariance,
+            SaveStabilizer,
+            SetStatevector,
+            SetStabilizer,
+            SaveAmplitudesSquared,
+            SaveProbabilitiesDict,
+        )
+        from qiskit_aer.noise.errors import ReadoutError
+        from qiskit_aer.noise.noise_model import QuantumErrorLocation
+
+        sim = AerSimulator()
+        phi = Parameter("phi")
+        lam = Parameter("lam")
+        backend = BackendV2Converter(
+            sim,
+            name_mapping={
+                "mcsx": MCSXGate,
+                "mcp": MCPhaseGate,
+                "mcphase": MCPhaseGate,
+                "quantum_channel": QuantumChannel,
+                "initialize": Initialize,
+                "save_expval": SaveExpectationValue,
+                "diagonal": DiagonalGate,
+                "save_amplitudes": SaveAmplitudes,
+                "roerror": ReadoutError,
+                "mcrx": MCRXGate,
+                "kraus": Kraus,
+                "save_statevector_dict": SaveStatevectorDict,
+                "mcx": MCXGate,
+                "mcu1": MCU1Gate,
+                "mcu2": MCU2Gate,
+                "mcu3": MCU3Gate,
+                "save_superop": SaveSuperOp,
+                "multiplexer": UCGate,
+                "mcy": MCYGate,
+                "superop": SuperOp,
+                "save_clifford": SaveClifford,
+                "save_matrix_product_state": SaveMatrixProductState,
+                "save_density_matrix": SaveDensityMatrix,
+                "save_probabilities": SaveProbabilities,
+                "if_else": IfElseOp,
+                "while_loop": WhileLoopOp,
+                "for_loop": ForLoopOp,
+                "save_statevector": SaveStatevector,
+                "mcu": MCUGate,
+                "set_density_matrix": SetDensityMatrix,
+                "qerror_loc": QuantumErrorLocation,
+                "unitary": UnitaryGate,
+                "mcz": MCZGate,
+                "pauli": PauliGate,
+                "set_unitary": SetUnitary,
+                "save_state": SaveState,
+                "mcswap": MCSwapGate,
+                "set_matrix_product_state": SetMatrixProductState,
+                "save_unitary": SaveUnitary,
+                "mcr": MCRGate,
+                "mcx_gray": MCXGrayCode,
+                "mcrz": MCRZGate,
+                "set_superop": SetSuperOp,
+                "save_expval_var": SaveExpectationValueVariance,
+                "save_stabilizer": SaveStabilizer,
+                "set_statevector": SetStatevector,
+                "mcry": MCRYGate,
+                "set_stabilizer": SetStabilizer,
+                "save_amplitudes_sq": SaveAmplitudesSquared,
+                "save_probabilities_dict": SaveProbabilitiesDict,
+                "cu2": U2Gate(phi, lam).control(),
+            },
+        )
+        self.assertIsInstance(backend, BackendV2)
+        res = transpile(self.circuit, backend)
+        job = backend.run(res)
+        result = job.result()
+        counts = result.get_counts()
+        max_count = max(counts.items(), key=operator.itemgetter(1))[0]
+        self.assertEqual(max_count, "11")