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