Source code for qlauncher.routines.qiskit.backends.qiskit_backend
1""" Base backend class for Qiskit routines. """
2from typing import Literal
3
4from qiskit.providers import BackendV1, BackendV2
5from qiskit.primitives import (
6 BackendSamplerV2,
7 BackendEstimatorV2,
8 StatevectorEstimator,
9 StatevectorSampler,
10 Sampler
11)
12
13from qiskit_ibm_runtime import Options
14
15from qlauncher.base import Backend
16from qlauncher.routines.qiskit.v2_wrapper import SamplerV2Adapter
17
18from qlauncher.routines.qiskit.backends.utils import (
19 set_estimator_auto_run_behavior, set_sampler_auto_run_behavior,
20 AUTO_TRANSPILE_ESTIMATOR_TYPE, AUTO_TRANSPILE_SAMPLER_TYPE
21)
22
23
[docs]
24class QiskitBackend(Backend):
25 """
26 Base class for backends compatible with qiskit.
27
28 Attributes:
29 name (str): The name of the backend.
30 options (Options | None, optional): The options for the backend. Defaults to None.
31 backendv1v2 (BackendV1 | BackendV2 | None, optional): Predefined backend to use with name 'backendv1v2'. Defaults to None.
32 sampler (BaseSamplerV2): The sampler used for sampling.
33 estimator (BaseEstimatorV2): The estimator used for estimation.
34 """
35
36 def __init__(
37 self,
38 name: Literal['local_simulator', 'backendv1v2'] | str,
39 options: Options | None = None,
40 backendv1v2: BackendV1 | BackendV2 | None = None,
41 auto_transpile_level: Literal[0, 1, 2, 3] | None = None
42 ) -> None:
43 """
44 Args:
45 **name (Literal['local_simulator', 'backendv1v2'] | str)**: Name or mode of operation,
46 'backendv1v2' allows for using a specific backend simulator.
47 **options (Options | None, optional)**: Defaults to None.
48 **backendv1v2 (BackendV1 | BackendV2 | None, optional)**:
49 Used with name 'backendv1v2', sampler and estimator will use it. Defaults to None.
50 **auto_transpile_level (Literal[0, 1, 2, 3] | None, optional)**:
51 Optimization level for automatic transpilation of circuits.
52 - None: Don't transpile.
53 - 0: No optimization (only transpile to compatible gates).
54 - 1: Light optimization.
55 - 2: Heavy optimization.
56 - 3: Heaviest optimization.
57 Defaults to None.
58 """
59 super().__init__(name)
60 self.options = options
61 self.backendv1v2 = backendv1v2
62 self._auto_transpile_level = auto_transpile_level
63 self._auto_assign = False
64 self._samplerV1: Sampler | None = None
65 self._set_primitives_on_backend_name()
66
67 @property
68 def samplerV1(self) -> Sampler:
69 if self._samplerV1 is None:
70 self._samplerV1 = SamplerV2Adapter(self.sampler)
71 return self._samplerV1
72
73 def _set_primitives_on_backend_name(self):
74 if self.name == 'local_simulator':
75 self.estimator = StatevectorEstimator()
76 self.sampler = StatevectorSampler()
77 elif self.name == 'backendv1v2':
78 if self.backendv1v2 is None:
79 raise AttributeError(
80 'Please indicate a backend when in backendv1v2 mode.')
81 self.estimator = BackendEstimatorV2(backend=self.backendv1v2)
82 self.sampler = BackendSamplerV2(backend=self.backendv1v2)
83
84 else:
85 raise ValueError(f"Unsupported mode for this backend:'{self.name}'")
86
87 self._configure_auto_behavior()
88
89 def _configure_auto_behavior(self):
90 """
91 Set auto transpilation and/or auto assignment if turned on, on estimator and sampler if compatible.
92 """
93 do_transpile, level = self._auto_transpile_level != None, int(
94 self._auto_transpile_level if self._auto_transpile_level != None else 0)
95 if isinstance(self.estimator, AUTO_TRANSPILE_ESTIMATOR_TYPE.__constraints__):
96 self.estimator = set_estimator_auto_run_behavior(
97 self.estimator,
98 auto_transpile=do_transpile,
99 auto_transpile_level=level,
100 auto_assign=self._auto_assign
101 )
102 if isinstance(self.sampler, AUTO_TRANSPILE_SAMPLER_TYPE.__constraints__):
103 self.sampler = set_sampler_auto_run_behavior(
104 self.sampler,
105 auto_transpile=do_transpile,
106 auto_transpile_level=level,
107 auto_assign=self._auto_assign
108 )