Source code for quantum_launcher.routines.qiskit_routines.backends.aqt_backend
1""" AQT backend class for Qiskit routines """
2from typing import Literal
3from overrides import override
4
5from qiskit.providers import BackendV1, BackendV2
6from qiskit.primitives import Sampler
7from qiskit_algorithms.optimizers import COBYLA, SPSA
8from qiskit_ibm_runtime import Options
9
10from quantum_launcher.routines.qiskit_routines import QiskitBackend
11from quantum_launcher.exceptions import DependencyError
12
13try:
14 from qiskit_aqt_provider import AQTProvider
15 from qiskit_aqt_provider.primitives import AQTSampler, AQTEstimator
16except ImportError as e:
17 raise DependencyError(e, install_hint='aqt') from e
18
19
[docs]
20class AQTBackend(QiskitBackend):
21 """
22 An extension of QiskitBackend providing support for Alpine Quantum Technologies (AQT) devices.
23
24 Attributes:
25 token (str, optional): AQT token, used for authorization when using real device backends.
26 dotenv_path (str,optional): (recommended) Path to a .env file containing the AQT token. If dotenv_path is not None, the token will be ignored and the token from the .env file will be used.
27
28 Usage Example
29 -------------
30 ::
31
32 backend = AQTBackend(token='valid_token', name='device')
33
34 or
35
36 ::
37
38 backend = AQTBackend(dotenv_path='./.env', name='device')
39
40 with a .env file:
41
42 ::
43
44 AQT_TOKEN=valid_token
45
46 """
47 sampler: AQTSampler
48 estimator: AQTEstimator
49
50 def __init__(
51 self,
52 name: Literal['local_simulator', 'backendv1v2', 'device'],
53 options: Options | None = None,
54 backendv1v2: BackendV1 | BackendV2 | None = None,
55 auto_transpile_level: Literal[0, 1, 2, 3] | None = None,
56 token: str | None = None,
57 dotenv_path: str | None = None,
58 ) -> None:
59
60 # TODO: This will probably need to be updated to handle custom backend urls, when we get our own computer
61 if dotenv_path is None:
62 self.provider = AQTProvider(token if token is not None else "DEFAULT_TOKEN", load_dotenv=False)
63 else:
64 self.provider = AQTProvider(load_dotenv=True, dotenv_path=dotenv_path)
65 super().__init__(name, options=options, backendv1v2=backendv1v2, auto_transpile_level=auto_transpile_level)
66
67 @override
68 def _set_primitives_on_backend_name(self) -> None:
69 if self.name == 'local_simulator':
70 self.name = self.provider.backends(backend_type='offline_simulator', name=r".*no_noise")[0].name
71 elif self.name == 'backendv1v2':
72 if self.backendv1v2 is None:
73 raise ValueError("Please indicate a backend when in backendv1v2 mode.")
74 elif self.name == 'device':
75 available_online_backends = self.provider.backends(backend_type='device')
76 if len(available_online_backends) == 0:
77 raise ValueError(f"No online backends available for token {self.provider.access_token[:5]}...")
78 self.name = available_online_backends[0].name
79 else:
80 raise ValueError(
81 " ".join([
82 f"Unsupported mode for this backend:'{self.name}'."
83 "Please use one of the following: ['local_simulator', 'backendv1v2', 'device']"
84 ])
85 )
86
87 if self.backendv1v2 is None:
88 self.backendv1v2 = self.provider.get_backend(name=self.name)
89
90 self.estimator = AQTEstimator(self.backendv1v2)
91 self.sampler = AQTSampler(self.backendv1v2)
92 self.optimizer = SPSA() if self.name == 'device' else COBYLA()
93
94 self._configure_auto_behavior()
95
96 @property
97 def samplerV1(self) -> Sampler:
98 return self.sampler