Repository: Qiskit/qiskit

Issue:
FakeBackendV2 runs noise-less simulation if not touching `target` in advance
### Environment

- **Qiskit Terra version**: main branch
- **Python version**: 3.8
- **Operating system**: Mac

### What is happening?

`FakeBackendV2.run` invokes noise-less simulation if it is called before touching `target` (more precisely, if it is called before the internal `_target` object is populated).

### How can we reproduce the issue?

Run the following code.
```
from qiskit.circuit import QuantumCircuit
from qiskit.providers.fake_provider.backends import FakeManilaV2

circ = QuantumCircuit(2, 2)
circ.cx(0, 1)
circ.measure([0, 1], [0, 1])

result = FakeManilaV2().run(circ).result()
counts = result.get_counts()
print(counts)  # -> {'00': 1024} (noise-less result)
```

### What should happen?

Noisy simulation should be run if `qiskit_aer` is installed (as like FakeBackend V1).

### Any suggestions?

[This line](https://github.com/Qiskit/qiskit-terra/blob/4a857f61146fb185878abd8634524941d71f1f6b/qiskit/providers/fake_provider/fake_backend.py#L124) should be replaced with `if self.target:`


Discussion:
Yep this is an easy bug to fix, @itoko do you want to push a PR making your suggested fix? If not I can do it

Edit: Although looking at the code more clearly I think the intent there is to not attempt to build a noise model if there aren't any noise properties, maybe `if self.target and self._props_dict` will fix the issue without potentially attempting to build an empty noise model?

Resolving patch:
diff --git a/qiskit/providers/fake_provider/fake_backend.py b/qiskit/providers/fake_provider/fake_backend.py
--- a/qiskit/providers/fake_provider/fake_backend.py
+++ b/qiskit/providers/fake_provider/fake_backend.py
@@ -121,7 +121,7 @@ def _setup_sim(self):
             from qiskit.providers import aer
 
             self.sim = aer.AerSimulator()
-            if self._props_dict:
+            if self.target and self._props_dict:
                 noise_model = self._get_noise_model_from_backend_v2()
                 self.sim.set_options(noise_model=noise_model)
                 # Update fake backend default too to avoid overwriting


Test patch:
diff --git a/test/python/providers/fake_provider/test_fake_backends.py b/test/python/providers/fake_provider/test_fake_backends.py
--- a/test/python/providers/fake_provider/test_fake_backends.py
+++ b/test/python/providers/fake_provider/test_fake_backends.py
@@ -19,7 +19,7 @@
 from qiskit.qobj import PulseQobj
 from qiskit.test import QiskitTestCase
 from qiskit.providers.fake_provider.utils.configurable_backend import ConfigurableFakeBackend
-from qiskit.providers.fake_provider import FakeAthens
+from qiskit.providers.fake_provider import FakeAthens, FakePerth
 from qiskit.utils import optionals
 
 
@@ -82,3 +82,14 @@ def test_fake_backends_get_kwargs(self):
         raw_counts = backend.run(trans_qc, shots=1000).result().get_counts()
 
         self.assertEqual(sum(raw_counts.values()), 1000)
+
+    @unittest.skipUnless(optionals.HAS_AER, "qiskit-aer is required to run this test")
+    def test_fake_backend_v2_noise_model_always_present(self):
+        """Test that FakeBackendV2 instances always run with noise."""
+        backend = FakePerth()
+        qc = QuantumCircuit(1)
+        qc.x(0)
+        qc.measure_all()
+        res = backend.run(qc, shots=1000).result().get_counts()
+        # Assert noise was present and result wasn't ideal
+        self.assertNotEqual(res, {"1": 1000})