Source code for qlauncher.routines.qiskit.backends.aqt_backend
1"""AQT backend class for Qiskit routines"""
2
3from typing import Literal
4
5from overrides import override
6from qiskit.primitives import BaseEstimatorV2, BaseSamplerV2
7from qiskit.providers import BackendV1, BackendV2
8from qiskit_ibm_runtime import Options
9
10from qlauncher.exceptions import DependencyError
11from qlauncher.routines.qiskit.adapters import (
12 EstimatorV1ToEstimatorV2Adapter,
13 SamplerV1ToSamplerV2Adapter,
14 TranslatingSampler,
15 TranslatingSamplerV1,
16)
17from qlauncher.routines.qiskit.backends.qiskit_backend import QiskitBackend
18from qlauncher.routines.qiskit.mitigation_suppression.base import CircuitExecutionMethod
19
20try:
21 from qiskit_aqt_provider import AQTProvider
22 from qiskit_aqt_provider.primitives import AQTEstimator, AQTSampler
23except ImportError as e:
24 raise DependencyError(e, install_hint='aqt') from e
25
26
[docs]
27class AQTBackend(QiskitBackend):
28 """
29 An extension of QiskitBackend providing support for Alpine Quantum Technologies (AQT) devices.
30
31 Attributes:
32 token (str, optional): AQT token, used for authorization when using real device backends.
33 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.
34
35 Usage Example
36 -------------
37 ::
38
39 backend = AQTBackend(token='valid_token', name='device')
40
41 or
42
43 ::
44
45 backend = AQTBackend(dotenv_path='./.env', name='device')
46
47 with a .env file:
48
49 ::
50
51 AQT_TOKEN = valid_token
52
53 """
54
55 sampler: BaseSamplerV2
56 estimator: BaseEstimatorV2
57
58 def __init__(
59 self,
60 name: Literal['local_simulator', 'backendv1v2', 'device'],
61 options: Options | None = None,
62 backendv1v2: BackendV1 | BackendV2 | None = None,
63 auto_transpile_level: Literal[0, 1, 2, 3] | None = None,
64 error_mitigation_strategy: CircuitExecutionMethod | None = None,
65 token: str | None = None,
66 direct_access_url: str | None = None,
67 dotenv_path: str | None = None,
68 ) -> None:
69 self._direct_access_url = direct_access_url
70 if dotenv_path is None:
71 self.provider = AQTProvider(token if token is not None else 'DEFAULT_TOKEN', load_dotenv=False)
72 else:
73 self.provider = AQTProvider(load_dotenv=True, dotenv_path=dotenv_path)
74 super().__init__(
75 name,
76 options=options,
77 backendv1v2=backendv1v2,
78 auto_transpile_level=auto_transpile_level,
79 error_mitigation_strategy=error_mitigation_strategy,
80 )
81
82 @override
83 def _set_primitives_on_backend_name(self) -> None:
84 if self.name == 'local_simulator':
85 self.name = self.provider.backends(backend_type='offline_simulator', name=r'.*no_noise')[0].name
86 elif self.name == 'backendv1v2':
87 if self.backendv1v2 is None and self._direct_access_url:
88 self.backendv1v2 = self.provider.get_direct_access_backend(self._direct_access_url)
89 elif self.backendv1v2 is None:
90 raise ValueError('Please indicate a backend when in backendv1v2 mode.')
91 elif self.name == 'device':
92 available_online_backends = self.provider.backends(backend_type='device')
93 if len(available_online_backends) == 0:
94 raise ValueError(f'No online backends available for token {self.provider.access_token[:5]}...')
95 self.name = available_online_backends[0].name
96 else:
97 raise ValueError(
98 ' '.join(
99 [
100 f"Unsupported mode for this backend:'{self.name}'."
101 "Please use one of the following: ['local_simulator', 'backendv1v2', 'device']"
102 ]
103 )
104 )
105
106 if self.backendv1v2 is None:
107 self.backendv1v2 = self.provider.get_backend(name=self.name)
108
109 self._estimatorv1 = AQTEstimator(self.backendv1v2)
110 self.estimator = EstimatorV1ToEstimatorV2Adapter(self._estimatorv1)
111 self._samplerV1 = TranslatingSamplerV1(AQTSampler(self.backendv1v2), self.compatible_circuit)
112 self.sampler = SamplerV1ToSamplerV2Adapter(self._samplerV1)
113
114 self._configure_auto_behavior()