1from typing import TypeVar
2from qiskit import QuantumCircuit, transpile
3from qiskit.providers.backend import BackendV1, BackendV2
4from qiskit.primitives import BackendSamplerV2, BackendEstimatorV2
5from qiskit_ibm_runtime import EstimatorV2, SamplerV2
6
7
8AUTO_TRANSPILE_SAMPLER_TYPE = TypeVar('AUTO_TRANSPILE_SAMPLER_TYPE', BackendSamplerV2, SamplerV2)
9AUTO_TRANSPILE_ESTIMATOR_TYPE = TypeVar('AUTO_TRANSPILE_ESTIMATOR_TYPE', BackendEstimatorV2, EstimatorV2)
10
11
12def _get_transpiled_pubs(pubs: list[tuple | QuantumCircuit], backend: BackendV1 | BackendV2, optimization_level: int = 2) -> list[tuple | QuantumCircuit]:
13 new_pubs = []
14 for pub in pubs:
15 if isinstance(pub, QuantumCircuit):
16 pub = transpile(pub, backend, optimization_level=optimization_level)
17 elif isinstance(pub, tuple):
18 pub = (transpile(pub[0], backend, optimization_level=optimization_level), *pub[1:])
19 new_pubs.append(pub)
20 return new_pubs
21
22
23def _assign_sampler_pubs(pubs: list[tuple | QuantumCircuit]) -> list[QuantumCircuit]:
24 new_pubs = []
25 for pub in pubs:
26 if isinstance(pub, tuple):
27 qc, *params = pub
28 pub = qc.assign_parameters(*params)
29 new_pubs.append(pub)
30 return new_pubs
31
32
33def _assign_estimator_pubs(pubs: list[tuple]) -> list[tuple]:
34 new_pubs = []
35 for pub in pubs:
36 if len(pub) > 2:
37 qc, observable, *params = pub
38 pub = (qc.assign_parameters(*params), observable)
39 new_pubs.append(pub)
40 return new_pubs
41
42
[docs]
43def set_sampler_auto_run_behavior(sampler: AUTO_TRANSPILE_SAMPLER_TYPE, auto_transpile: bool = False, auto_transpile_level: int = 2, auto_assign: bool = False) -> AUTO_TRANSPILE_SAMPLER_TYPE:
44 """
45 Set chosen automatic behavior on a sampler instance.
46
47 Args:
48 sampler (AUTO_TRANSPILE_SAMPLER_TYPE): Compatible sampler instance
49 auto_transpile (bool, optional): Whether to automatically transpile to the samplers backend. Defaults to False.
50 auto_transpile_level (int, optional): What level of optimization to set. Defaults to 2.
51 auto_assign (bool, optional): Whether to automatically assign parameters in case of parameterized circuits. Defaults to False.
52
53 Returns:
54 AUTO_TRANSPILE_SAMPLER_TYPE: Same instance with modified run() method.
55 """
56 func = sampler.run
57
58 def run_wrapper(pubs, *args, shots: int | None = None):
59 if auto_transpile:
60 pubs = _get_transpiled_pubs(pubs, sampler._backend, optimization_level=auto_transpile_level)
61 if auto_assign:
62 pubs = _assign_sampler_pubs(pubs)
63
64 return func(pubs, *args, shots=shots)
65
66 sampler.run = run_wrapper
67 return sampler
68
69
[docs]
70def set_estimator_auto_run_behavior(estimator: AUTO_TRANSPILE_ESTIMATOR_TYPE, auto_transpile: bool = False, auto_transpile_level: int = 2, auto_assign: bool = False) -> AUTO_TRANSPILE_ESTIMATOR_TYPE:
71 """
72 Set chosen automatic behavior on a estimator instance.
73
74 Args:
75 estimator (AUTO_TRANSPILE_ESTIMATOR_TYPE): Compatible estimator instance
76 auto_transpile (bool, optional): Whether to automatically transpile to the estimators backend. Defaults to False.
77 auto_transpile_level (int, optional): What level of optimization to set. Defaults to 2.
78 auto_assign (bool, optional): Whether to automatically assign parameters in case of parameterized circuits. Defaults to False.
79
80 Returns:
81 AUTO_TRANSPILE_ESTIMATOR_TYPE: Same instance with modified run() method.
82 """
83 func = estimator.run
84
85 def run_wrapper(pubs, *args, precision: float | None = None):
86 if auto_transpile:
87 pubs = _get_transpiled_pubs(pubs, estimator._backend, optimization_level=auto_transpile_level)
88 if auto_assign:
89 pubs = _assign_estimator_pubs(pubs)
90 return func(pubs, *args, precision=precision)
91 estimator.run = run_wrapper
92 return estimator