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_sampler_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 _get_transpiled_estimator_pubs(pubs: list[tuple], backend: BackendV1 | BackendV2, optimization_level: int = 2) -> list[tuple | QuantumCircuit]:
24 new_pubs = []
25 for pub in pubs:
26 circuit, operator, *args = pub
27 transp_circ: QuantumCircuit = transpile(circuit, backend, optimization_level=optimization_level)
28 transp_op = operator.apply_layout(transp_circ.layout, num_qubits=transp_circ.num_qubits)
29 pub = (transp_circ, transp_op, *args)
30 new_pubs.append(pub)
31 return new_pubs
32
33
34def _assign_sampler_pubs(pubs: list[tuple | QuantumCircuit]) -> list[QuantumCircuit]:
35 new_pubs = []
36 for pub in pubs:
37 if isinstance(pub, tuple):
38 qc, *params = pub
39 pub = qc.assign_parameters(*params)
40 new_pubs.append(pub)
41 return new_pubs
42
43
44def _assign_estimator_pubs(pubs: list[tuple]) -> list[tuple]:
45 new_pubs = []
46 for pub in pubs:
47 if len(pub) > 2:
48 qc, observable, *params = pub
49 pub = (qc.assign_parameters(*params), observable)
50 new_pubs.append(pub)
51 return new_pubs
52
53
[docs]
54def 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:
55 """
56 Set chosen automatic behavior on a sampler instance.
57
58 Args:
59 sampler (AUTO_TRANSPILE_SAMPLER_TYPE): Compatible sampler instance
60 auto_transpile (bool, optional): Whether to automatically transpile to the samplers backend. Defaults to False.
61 auto_transpile_level (int, optional): What level of optimization to set. Defaults to 2.
62 auto_assign (bool, optional): Whether to automatically assign parameters in case of parameterized circuits. Defaults to False.
63
64 Returns:
65 AUTO_TRANSPILE_SAMPLER_TYPE: Same instance with modified run() method.
66 """
67 func = sampler.run
68
69 def run_wrapper(pubs, *args, shots: int | None = None):
70 if auto_transpile:
71 pubs = _get_transpiled_sampler_pubs(pubs, sampler._backend, optimization_level=auto_transpile_level)
72 if auto_assign:
73 pubs = _assign_sampler_pubs(pubs)
74
75 return func(pubs, *args, shots=shots)
76
77 sampler.run = run_wrapper
78 return sampler
79
80
[docs]
81def 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:
82 """
83 Set chosen automatic behavior on a estimator instance.
84
85 Args:
86 estimator (AUTO_TRANSPILE_ESTIMATOR_TYPE): Compatible estimator instance
87 auto_transpile (bool, optional): Whether to automatically transpile to the estimators backend. Defaults to False.
88 auto_transpile_level (int, optional): What level of optimization to set. Defaults to 2.
89 auto_assign (bool, optional): Whether to automatically assign parameters in case of parameterized circuits. Defaults to False.
90
91 Returns:
92 AUTO_TRANSPILE_ESTIMATOR_TYPE: Same instance with modified run() method.
93 """
94 func = estimator.run
95
96 def run_wrapper(pubs, *args, precision: float | None = None):
97 if auto_transpile:
98 pubs = _get_transpiled_estimator_pubs(pubs, estimator._backend, optimization_level=auto_transpile_level)
99 if auto_assign:
100 pubs = _assign_estimator_pubs(pubs)
101 return func(pubs, *args, precision=precision)
102 estimator.run = run_wrapper
103 return estimator