"""Backend Wrapper (:mod:`qurry.tools.backend.backend_manager`)"""
from typing import Union, Literal, Callable, Optional
from random import random
import warnings
from qiskit.providers import Backend
from .import_simulator import SIM_DEFAULT_SOURCE as sim_default_source, GeneralSimulator
from .import_fake import FAKE_BACKENDV2_SOURCES as fake_default_source, fack_backend_loader
from ...capsule.hoshi import Hoshi
from ...exceptions import QurryDeprecatedWarning
BackendDict = dict[
Union[Literal["real", "sim", "fake", "extra"], str],
dict[str, Backend],
]
"""The dictionary of backends."""
BackendCallSignDict = dict[
Union[Literal["real", "sim", "fake", "extra"], str],
dict[str, str],
]
"""The dictionary of backend callsign."""
def _statesheet_preparings(
check_msg: Hoshi,
desc: str,
backs: list[str],
backs_callsign: dict[str, str],
is_aer_gpu: bool,
):
backs_len = len(backs)
check_msg.divider()
check_msg.h4(desc)
if "Simulator" in desc:
check_msg.newline(
{
"type": "itemize",
"description": "Aer GPU",
"value": is_aer_gpu,
"ljust_description_filler": ".",
}
)
check_msg.newline(
{
"type": "itemize",
"description": "Simulator Provider by",
"value": sim_default_source,
"ljust_description_filler": ".",
}
)
if backs_len == 0:
check_msg.newline(
{
"type": "txt",
"listing_level": 2,
"text": (
"No Backends Available."
+ (
" Choose fake version when initializing the backend wrapper."
if "Fake" in desc
else ""
)
+ (
(
" Real backends need to be loaded by 'BackendManager' "
+ "instead of 'BackendWrapper'."
)
if "IBM" in desc
else ""
)
),
}
)
else:
for i in range(0, backs_len, 3):
tmp_backs = backs[i : i + 3]
tmp_backs_str = ", ".join(tmp_backs) + ("," if len(tmp_backs) == 3 else "")
check_msg.newline(
{
"type": "txt",
"listing_level": 2,
"text": tmp_backs_str,
}
)
if len(backs_callsign) == 0:
check_msg.newline(
{
"type": "txt",
"listing_level": 2,
"text": "No Callsign Added",
}
)
else:
check_msg.newline(
{
"type": "itemize",
"description": f"Available {desc} Backends Callsign",
}
)
for k, v in backs_callsign.items():
check_msg.newline(
{
"type": "itemize",
"description": f"{k}",
"value": f"{v}",
"listing_level": 2,
"ljust_description_filler": ".",
}
)
[docs]
class BackendWrapper:
"""A wrapper for :class:`~qiskit.providers.Backend` to provide more convenient way to use."""
@staticmethod
def _hint_ibmq_sim(name: str) -> str:
return "ibm" + name if "ibm" not in name else name
def __init__(
self,
) -> None:
self.is_aer_gpu = False
backend_fake_callsign, backend_fake = fack_backend_loader()
self.backend_dict: BackendDict = {
"sim": {"sim": GeneralSimulator()},
"real": {},
"fake": {**backend_fake},
"extra": {},
}
self.backend_callsign_dict: BackendCallSignDict = {
"sim": {},
"real": {},
"fake": {**backend_fake_callsign},
"extra": {},
}
if sim_default_source == "qiskit.providers.basicaer":
warnings.warn(
"The qiskit.providers.basicaer is an outdated module, "
+ "you should migrate to qiskit-aer, "
+ "or update qiskit to the latest version.",
category=QurryDeprecatedWarning,
)
if hasattr(self.backend_dict["sim"]["sim"], "available_devices"):
assert isinstance(
self.backend_dict["sim"]["sim"].available_devices, Callable # type: ignore
), "The available_devices should be a callable."
self.is_aer_gpu = (
"GPU" in self.backend_dict["sim"]["sim"].available_devices() # type: ignore
)
self.backend_dict["sim"]["sim"].set_options(device="GPU") # type: ignore
assert self.backend_dict["sim"]["sim"].options.device == "GPU", ( # type: ignore
"GPU is not available, consider to check your CUDA installation."
)
def __repr__(self):
repr_str = f"<{self.__class__.__name__}("
repr_str += f'sim="{sim_default_source}", '
repr_str += f'fake="{fake_default_source}"'
repr_str += ")>"
return repr_str
[docs]
def make_callsign(
self,
sign: str = "Galm 2",
who: str = "solo_wing_pixy",
) -> None:
"""Make a callsign for backend.
Args:
sign (str, optional): The callsign.
who (str, optional): The backend.
Raises:
ValueError: If the callsign already exists.
ValueError: If the backend is unknown.
"""
if sign == "Galm 2" or who == "solo_wing_pixy":
if random() <= 0.2:
print(
"Those who survive a long time on the battlefield "
+ "start to think they're invincible. I bet you do, too, Buddy."
)
for avaiable_type in ["real", "sim", "fake", "extra"]:
if sign in self.backend_callsign_dict[avaiable_type]:
raise ValueError(f"'{sign}' callsign already exists.")
for avaiable_type in ["real", "sim", "fake", "extra"]:
if who in self.backend_dict[avaiable_type]:
self.backend_callsign_dict[avaiable_type][sign] = who
return
raise ValueError(f"'{who}' unknown backend.")
@property
def available_backends(self) -> BackendDict:
"""The available backends."""
return self.backend_dict
@property
def available_backends_callsign(self) -> BackendCallSignDict:
"""The available backends callsign."""
return self.backend_callsign_dict
@property
def available_aer(self) -> list[str]:
"""The available aer backends."""
return list(self.backend_dict["sim"].keys())
@property
def available_aer_callsign(self) -> list[str]:
"""The available aer backends callsign."""
return list(self.backend_callsign_dict["sim"].keys())
@property
def available_ibmq(self) -> list[str]:
"""The available ibmq/ibm backends."""
return list(self.backend_dict["real"].keys())
@property
def available_ibmq_callsign(self) -> list[str]:
"""The available ibmq/ibm backends callsign."""
return list(self.backend_callsign_dict["real"].keys())
@property
def available_fake(self) -> list[str]:
"""The available fake backends."""
return list(self.backend_dict["fake"].keys())
@property
def available_fake_callsign(self) -> list[str]:
"""The available fake backends callsign."""
return list(self.backend_callsign_dict["fake"].keys())
[docs]
def statesheet(self):
"""The statesheet of backend wrapper."""
check_msg = Hoshi(
[
("divider", 60),
("h3", "BackendWrapper Statesheet"),
],
ljust_description_len=35,
ljust_description_filler=".",
)
for desc, backs, backs_callsign in [
("Simulator", self.available_aer, self.backend_callsign_dict["sim"]),
("IBM", self.available_ibmq, self.backend_callsign_dict["real"]),
("Fake", self.available_fake, self.backend_callsign_dict["fake"]),
(
"Extra",
self.available_backends["extra"],
self.backend_callsign_dict["extra"],
),
]:
_statesheet_preparings(
check_msg,
desc,
backs,
backs_callsign,
self.is_aer_gpu,
)
return check_msg
[docs]
def add_backend(
self,
name: str,
backend: Backend,
callsign: Optional[str] = None,
) -> None:
"""Add a backend to backend wrapper.
Args:
name (str): The name of backend.
backend (Backend): The backend.
callsign (Optional[str], optional): The callsign of backend. Defaults to None.
"""
if not isinstance(backend, Backend):
raise TypeError("The backend should be a instance of 'qiskit.providers.Backend'")
if name in self.backend_dict["extra"]:
raise ValueError(f"'{name}' backend already exists.")
self.backend_dict["extra"][name] = backend
if callsign is not None:
self.backend_callsign_dict["extra"][callsign] = name
def __call__(
self,
backend_name: str,
) -> Backend:
for avaiable_type in ["real", "sim", "fake", "extra"]:
if backend_name in self.backend_dict[avaiable_type]:
return self.backend_dict[avaiable_type][backend_name]
if backend_name in self.backend_callsign_dict[avaiable_type]:
return self.backend_dict[avaiable_type][
self.backend_callsign_dict[avaiable_type][backend_name]
]
raise ValueError(f"'{backend_name}' unknown backend or backend callsign.")