Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add more fake backend methods #1765

Merged
merged 14 commits into from
Jul 9, 2024
101 changes: 97 additions & 4 deletions qiskit_ibm_runtime/fake_provider/fake_backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,10 +21,17 @@
import os
import re

from typing import List, Iterable

from qiskit import circuit
from qiskit.providers.models import BackendProperties, BackendConfiguration, PulseDefaults
from typing import List, Iterable, Union

from qiskit import circuit, QuantumCircuit
from qiskit.providers.models import (
BackendProperties,
BackendConfiguration,
PulseDefaults,
BackendStatus,
QasmBackendConfiguration,
PulseBackendConfiguration,
)
from qiskit.providers import BackendV2, BackendV1
from qiskit import pulse
from qiskit.exceptions import QiskitError
Expand Down Expand Up @@ -169,6 +176,92 @@ def _load_json(self, filename: str) -> dict:
the_json = json.load(f_json)
return the_json

def status(self) -> BackendStatus:
"""Return the backend status.

Returns:
The status of the backend.

"""

api_status = {
"backend_name": self.name,
"backend_version": "",
"status_msg": "active",
"operational": True,
"pending_jobs": 0,
}

return BackendStatus.from_dict(api_status)

def properties(self, refresh: bool = False) -> BackendProperties:
"""Return the backend properties

Args:
refresh: If ``True``, re-retrieve the backend properties
from the local file.

Returns:
The backend properties.
"""
if refresh or (self._props_dict is None):
self._set_props_dict_from_json()
return BackendProperties.from_dict(self._props_dict)

def defaults(self, refresh: bool = False) -> PulseDefaults:
kt474 marked this conversation as resolved.
Show resolved Hide resolved
"""Return the pulse defaults for the backend

Args:
refresh: If ``True``, re-retrieve the backend defaults from the
local file.

Returns:
The backend pulse defaults or ``None`` if the backend does not support pulse.
"""
if refresh or self._defs_dict is None:
self._set_defs_dict_from_json()
if self._defs_dict:
return PulseDefaults.from_dict(self._defs_dict) # type: ignore[unreachable]
kt474 marked this conversation as resolved.
Show resolved Hide resolved
return None

def configuration(self) -> Union[QasmBackendConfiguration, PulseBackendConfiguration]:
"""Return the backend configuration."""
return BackendConfiguration.from_dict(self._conf_dict)

def check_faulty(self, circuit: QuantumCircuit) -> None: # pylint: disable=redefined-outer-name
"""Check if the input circuit uses faulty qubits or edges.

Args:
circuit: Circuit to check.

Raises:
ValueError: If an instruction operating on a faulty qubit or edge is found.
"""
if not self.properties():
return

faulty_qubits = self.properties().faulty_qubits()
faulty_gates = self.properties().faulty_gates()
faulty_edges = [tuple(gate.qubits) for gate in faulty_gates if len(gate.qubits) > 1]

for instr in circuit.data:
if instr.operation.name == "barrier":
continue
qubit_indices = tuple(circuit.find_bit(x).index for x in instr.qubits)

for circ_qubit in qubit_indices:
if circ_qubit in faulty_qubits:
raise ValueError(
f"Circuit {circuit.name} contains instruction "
f"{instr} operating on a faulty qubit {circ_qubit}."
)

if len(qubit_indices) == 2 and qubit_indices in faulty_edges:
raise ValueError(
f"Circuit {circuit.name} contains instruction "
f"{instr} operating on a faulty edge {qubit_indices}"
)

@property
def target(self) -> Target:
"""A :class:`qiskit.transpiler.Target` object for the backend.
Expand Down
2 changes: 2 additions & 0 deletions release-notes/unreleased/1765.feat.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
The property ``service``, and methods ``properties``, ``defaults``, ``configuration``,
and ``check_faulty`` have been added to :class:`FakeBackendV2`.
21 changes: 11 additions & 10 deletions test/integration/test_fake_backends.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,7 @@ def test_circuit_on_fake_backend(self, backend, optimization_level):
max_count = max(counts.items(), key=operator.itemgetter(1))[0]
self.assertEqual(max_count, "11")

@data(*FAKE_PROVIDER.backends())
@data(*FAKE_PROVIDER.backends(), *FAKE_PROVIDER_FOR_BACKEND_V2.backends())
def test_to_dict_properties(self, backend):
properties = backend.properties()
if properties:
Expand All @@ -118,7 +118,7 @@ def test_backend_v2_dtm(self, backend):
if backend.dtm:
self.assertLess(backend.dtm, 1e-6)

@data(*FAKE_PROVIDER.backends())
@data(*FAKE_PROVIDER.backends(), *FAKE_PROVIDER_FOR_BACKEND_V2.backends())
def test_to_dict_configuration(self, backend):
configuration = backend.configuration()
if configuration.open_pulse:
Expand All @@ -140,19 +140,20 @@ def test_to_dict_configuration(self, backend):

self.assertIsInstance(configuration.to_dict(), dict)

@data(*FAKE_PROVIDER.backends())
@data(*FAKE_PROVIDER.backends(), *FAKE_PROVIDER_FOR_BACKEND_V2.backends())
def test_defaults_to_dict(self, backend):
if hasattr(backend, "defaults"):
defaults = backend.defaults()
self.assertIsInstance(defaults.to_dict(), dict)
if defaults:
self.assertIsInstance(defaults.to_dict(), dict)

for i in defaults.qubit_freq_est:
self.assertGreater(i, 1e6)
self.assertGreater(i, 1e6)
for i in defaults.qubit_freq_est:
self.assertGreater(i, 1e6)
self.assertGreater(i, 1e6)

for i in defaults.meas_freq_est:
self.assertGreater(i, 1e6)
self.assertGreater(i, 1e6)
for i in defaults.meas_freq_est:
self.assertGreater(i, 1e6)
self.assertGreater(i, 1e6)
else:
self.skipTest("Backend %s does not have defaults" % backend)

Expand Down
Loading