qiskit-aer 0.17.2__cp314-cp314-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (83) hide show
  1. qiskit_aer/VERSION.txt +1 -0
  2. qiskit_aer/__init__.py +89 -0
  3. qiskit_aer/aererror.py +30 -0
  4. qiskit_aer/aerprovider.py +119 -0
  5. qiskit_aer/backends/__init__.py +20 -0
  6. qiskit_aer/backends/aer_compiler.py +1085 -0
  7. qiskit_aer/backends/aer_simulator.py +1025 -0
  8. qiskit_aer/backends/aerbackend.py +679 -0
  9. qiskit_aer/backends/backend_utils.py +567 -0
  10. qiskit_aer/backends/backendconfiguration.py +395 -0
  11. qiskit_aer/backends/backendproperties.py +590 -0
  12. qiskit_aer/backends/compatibility.py +287 -0
  13. qiskit_aer/backends/controller_wrappers.cp314-win_amd64.pyd +0 -0
  14. qiskit_aer/backends/libopenblas.dll +0 -0
  15. qiskit_aer/backends/name_mapping.py +306 -0
  16. qiskit_aer/backends/qasm_simulator.py +925 -0
  17. qiskit_aer/backends/statevector_simulator.py +330 -0
  18. qiskit_aer/backends/unitary_simulator.py +316 -0
  19. qiskit_aer/jobs/__init__.py +35 -0
  20. qiskit_aer/jobs/aerjob.py +143 -0
  21. qiskit_aer/jobs/utils.py +66 -0
  22. qiskit_aer/library/__init__.py +204 -0
  23. qiskit_aer/library/control_flow_instructions/__init__.py +16 -0
  24. qiskit_aer/library/control_flow_instructions/jump.py +47 -0
  25. qiskit_aer/library/control_flow_instructions/mark.py +30 -0
  26. qiskit_aer/library/control_flow_instructions/store.py +29 -0
  27. qiskit_aer/library/default_qubits.py +44 -0
  28. qiskit_aer/library/instructions_table.csv +21 -0
  29. qiskit_aer/library/save_instructions/__init__.py +44 -0
  30. qiskit_aer/library/save_instructions/save_amplitudes.py +168 -0
  31. qiskit_aer/library/save_instructions/save_clifford.py +63 -0
  32. qiskit_aer/library/save_instructions/save_data.py +129 -0
  33. qiskit_aer/library/save_instructions/save_density_matrix.py +91 -0
  34. qiskit_aer/library/save_instructions/save_expectation_value.py +257 -0
  35. qiskit_aer/library/save_instructions/save_matrix_product_state.py +71 -0
  36. qiskit_aer/library/save_instructions/save_probabilities.py +156 -0
  37. qiskit_aer/library/save_instructions/save_stabilizer.py +70 -0
  38. qiskit_aer/library/save_instructions/save_state.py +79 -0
  39. qiskit_aer/library/save_instructions/save_statevector.py +120 -0
  40. qiskit_aer/library/save_instructions/save_superop.py +62 -0
  41. qiskit_aer/library/save_instructions/save_unitary.py +63 -0
  42. qiskit_aer/library/set_instructions/__init__.py +19 -0
  43. qiskit_aer/library/set_instructions/set_density_matrix.py +78 -0
  44. qiskit_aer/library/set_instructions/set_matrix_product_state.py +83 -0
  45. qiskit_aer/library/set_instructions/set_stabilizer.py +77 -0
  46. qiskit_aer/library/set_instructions/set_statevector.py +78 -0
  47. qiskit_aer/library/set_instructions/set_superop.py +78 -0
  48. qiskit_aer/library/set_instructions/set_unitary.py +78 -0
  49. qiskit_aer/noise/__init__.py +265 -0
  50. qiskit_aer/noise/device/__init__.py +25 -0
  51. qiskit_aer/noise/device/models.py +397 -0
  52. qiskit_aer/noise/device/parameters.py +202 -0
  53. qiskit_aer/noise/errors/__init__.py +30 -0
  54. qiskit_aer/noise/errors/base_quantum_error.py +119 -0
  55. qiskit_aer/noise/errors/pauli_error.py +283 -0
  56. qiskit_aer/noise/errors/pauli_lindblad_error.py +363 -0
  57. qiskit_aer/noise/errors/quantum_error.py +451 -0
  58. qiskit_aer/noise/errors/readout_error.py +355 -0
  59. qiskit_aer/noise/errors/standard_errors.py +498 -0
  60. qiskit_aer/noise/noise_model.py +1231 -0
  61. qiskit_aer/noise/noiseerror.py +30 -0
  62. qiskit_aer/noise/passes/__init__.py +18 -0
  63. qiskit_aer/noise/passes/local_noise_pass.py +160 -0
  64. qiskit_aer/noise/passes/relaxation_noise_pass.py +137 -0
  65. qiskit_aer/primitives/__init__.py +44 -0
  66. qiskit_aer/primitives/estimator.py +751 -0
  67. qiskit_aer/primitives/estimator_v2.py +159 -0
  68. qiskit_aer/primitives/sampler.py +361 -0
  69. qiskit_aer/primitives/sampler_v2.py +256 -0
  70. qiskit_aer/quantum_info/__init__.py +32 -0
  71. qiskit_aer/quantum_info/states/__init__.py +16 -0
  72. qiskit_aer/quantum_info/states/aer_densitymatrix.py +313 -0
  73. qiskit_aer/quantum_info/states/aer_state.py +525 -0
  74. qiskit_aer/quantum_info/states/aer_statevector.py +302 -0
  75. qiskit_aer/utils/__init__.py +44 -0
  76. qiskit_aer/utils/noise_model_inserter.py +66 -0
  77. qiskit_aer/utils/noise_transformation.py +431 -0
  78. qiskit_aer/version.py +86 -0
  79. qiskit_aer-0.17.2.dist-info/METADATA +209 -0
  80. qiskit_aer-0.17.2.dist-info/RECORD +83 -0
  81. qiskit_aer-0.17.2.dist-info/WHEEL +5 -0
  82. qiskit_aer-0.17.2.dist-info/licenses/LICENSE.txt +203 -0
  83. qiskit_aer-0.17.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,159 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2024.
4
+ #
5
+ # This code is licensed under the Apache License, Version 2.0. You may
6
+ # obtain a copy of this license in the LICENSE.txt file in the root directory
7
+ # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
+ #
9
+ # Any modifications or derivative works of this code must retain this
10
+ # copyright notice, and modified files need to carry a notice indicating
11
+ # that they have been altered from the originals.
12
+
13
+ """Estimator V2 implementation for Aer."""
14
+
15
+ from __future__ import annotations
16
+
17
+ from collections.abc import Iterable
18
+ from dataclasses import dataclass, field
19
+
20
+ import numpy as np
21
+ from qiskit.primitives.base import BaseEstimatorV2
22
+ from qiskit.primitives.containers import DataBin, EstimatorPubLike, PrimitiveResult, PubResult
23
+ from qiskit.primitives.containers.estimator_pub import EstimatorPub
24
+ from qiskit.primitives.primitive_job import PrimitiveJob
25
+ from qiskit.quantum_info import Pauli
26
+
27
+ from qiskit_aer import AerSimulator
28
+
29
+
30
+ @dataclass
31
+ class Options:
32
+ """Options for :class:`~.EstimatorV2`."""
33
+
34
+ default_precision: float = 0.0
35
+ """The default precision to use if none are specified in :meth:`~run`.
36
+ """
37
+
38
+ backend_options: dict = field(default_factory=dict)
39
+ """backend_options: Options passed to AerSimulator."""
40
+
41
+ run_options: dict = field(default_factory=dict)
42
+ """run_options: Options passed to run."""
43
+
44
+
45
+ class EstimatorV2(BaseEstimatorV2):
46
+ """Evaluates expectation values for provided quantum circuit and observable combinations.
47
+
48
+ Run a fast simulation using Aer.
49
+ Sampling from a normal distribution ``N(expval, precison)`` when set to ``precision``.
50
+
51
+ * ``default_precision``: The default precision to use if none are specified in :meth:`~run`.
52
+ Default: 0.0.
53
+
54
+ * ``backend_options``: Options passed to AerSimulator.
55
+ Default: {}.
56
+
57
+ * ``run_options``: Options passed to :meth:`AerSimulator.run`.
58
+ Default: {}.
59
+ """
60
+
61
+ def __init__(
62
+ self,
63
+ *,
64
+ options: dict | None = None,
65
+ ):
66
+ """
67
+ Args:
68
+ options: The options to control the default precision (``default_precision``),
69
+ the backend options (``backend_options``), and
70
+ the runtime options (``run_options``).
71
+ """
72
+ self._options = Options(**options) if options else Options()
73
+ self._backend = AerSimulator(**self.options.backend_options)
74
+
75
+ @classmethod
76
+ def from_backend(cls, backend, **options):
77
+ """make new sampler that uses external backend"""
78
+ estimator = cls(**options)
79
+ if isinstance(backend, AerSimulator):
80
+ estimator._backend = backend
81
+ else:
82
+ estimator._backend = AerSimulator.from_backend(backend)
83
+ return estimator
84
+
85
+ @property
86
+ def options(self) -> Options:
87
+ """Return the options"""
88
+ return self._options
89
+
90
+ def run(
91
+ self, pubs: Iterable[EstimatorPubLike], *, precision: float | None = None
92
+ ) -> PrimitiveJob[PrimitiveResult[PubResult]]:
93
+ if precision is None:
94
+ precision = self._options.default_precision
95
+ coerced_pubs = [EstimatorPub.coerce(pub, precision) for pub in pubs]
96
+ self._validate_pubs(coerced_pubs)
97
+ job = PrimitiveJob(self._run, coerced_pubs)
98
+ job._submit()
99
+ return job
100
+
101
+ def _validate_pubs(self, pubs: list[EstimatorPub]):
102
+ for i, pub in enumerate(pubs):
103
+ if pub.precision < 0.0:
104
+ raise ValueError(
105
+ f"The {i}-th pub has precision less than 0 ({pub.precision}). ",
106
+ "But precision should be equal to or larger than 0.",
107
+ )
108
+
109
+ def _run(self, pubs: list[EstimatorPub]) -> PrimitiveResult[PubResult]:
110
+ return PrimitiveResult([self._run_pub(pub) for pub in pubs], metadata={"version": 2})
111
+
112
+ def _run_pub(self, pub: EstimatorPub) -> PubResult:
113
+ circuit = pub.circuit.copy()
114
+ observables = pub.observables
115
+ parameter_values = pub.parameter_values
116
+ precision = pub.precision
117
+
118
+ # calculate broadcasting of parameters and observables
119
+ param_shape = parameter_values.shape
120
+ param_indices = np.fromiter(np.ndindex(param_shape), dtype=object).reshape(param_shape)
121
+ bc_param_ind, bc_obs = np.broadcast_arrays(param_indices, observables)
122
+
123
+ parameter_binds = {}
124
+ param_array = parameter_values.as_array(circuit.parameters)
125
+ parameter_binds = {p: param_array[..., i].ravel() for i, p in enumerate(circuit.parameters)}
126
+
127
+ # save expval
128
+ paulis = {pauli for obs_dict in observables.ravel() for pauli in obs_dict.keys()}
129
+ for pauli in paulis:
130
+ circuit.save_expectation_value(
131
+ Pauli(pauli), qubits=range(circuit.num_qubits), label=pauli
132
+ )
133
+ result = self._backend.run(
134
+ circuit, parameter_binds=[parameter_binds], **self.options.run_options
135
+ ).result()
136
+
137
+ # calculate expectation values (evs) and standard errors (stds)
138
+ flat_indices = list(param_indices.ravel())
139
+ evs = np.zeros_like(bc_param_ind, dtype=float)
140
+ stds = np.full(bc_param_ind.shape, precision)
141
+ for index in np.ndindex(*bc_param_ind.shape):
142
+ param_index = bc_param_ind[index]
143
+ flat_index = flat_indices.index(param_index)
144
+ for pauli, coeff in bc_obs[index].items():
145
+ expval = result.data(flat_index)[pauli]
146
+ evs[index] += expval * coeff
147
+ if precision > 0:
148
+ rng = np.random.default_rng(self.options.run_options.get("seed_simulator"))
149
+ if not np.all(np.isreal(evs)):
150
+ raise ValueError("Given operator is not Hermitian and noise cannot be added.")
151
+ evs = rng.normal(evs, precision, evs.shape)
152
+ return PubResult(
153
+ DataBin(evs=evs, stds=stds, shape=evs.shape),
154
+ metadata={
155
+ "target_precision": precision,
156
+ "circuit_metadata": pub.circuit.metadata,
157
+ "simulator_metadata": result.metadata,
158
+ },
159
+ )
@@ -0,0 +1,361 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2022.
4
+ #
5
+ # This code is licensed under the Apache License, Version 2.0. You may
6
+ # obtain a copy of this license in the LICENSE.txt file in the root directory
7
+ # of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
8
+ #
9
+ # Any modifications or derivative works of this code must retain this
10
+ # copyright notice, and modified files need to carry a notice indicating
11
+ # that they have been altered from the originals.
12
+
13
+ """
14
+ Sampler class.
15
+ """
16
+
17
+ from __future__ import annotations
18
+
19
+ from collections.abc import Sequence, Iterable
20
+
21
+ from warnings import warn
22
+ import numpy as np
23
+ from qiskit.circuit import ParameterExpression, QuantumCircuit, Qubit
24
+ from qiskit.circuit.library.data_preparation import Initialize
25
+ from qiskit.compiler import transpile
26
+ from qiskit.exceptions import QiskitError
27
+ from qiskit.primitives import BaseSamplerV1, SamplerResult
28
+ from qiskit.quantum_info import Statevector
29
+ from qiskit.result import QuasiDistribution
30
+
31
+ from .. import AerSimulator
32
+
33
+
34
+ def init_circuit(state: QuantumCircuit | Statevector) -> QuantumCircuit:
35
+ """Initialize state by converting the input to a quantum circuit.
36
+
37
+ Args:
38
+ state: The state as quantum circuit or statevector.
39
+
40
+ Returns:
41
+ The state as quantum circuit.
42
+ """
43
+ if isinstance(state, QuantumCircuit):
44
+ return state
45
+ if not isinstance(state, Statevector):
46
+ state = Statevector(state)
47
+ qc = QuantumCircuit(state.num_qubits)
48
+ qc.append(Initialize(state), qargs=range(state.num_qubits))
49
+ return qc
50
+
51
+
52
+ def final_measurement_mapping(circuit: QuantumCircuit) -> dict[int, int]:
53
+ """Return the final measurement mapping for the circuit.
54
+
55
+ Dict keys label measured qubits, whereas the values indicate the
56
+ classical bit onto which that qubits measurement result is stored.
57
+
58
+ Parameters:
59
+ circuit: Input quantum circuit.
60
+
61
+ Returns:
62
+ Mapping of qubits to classical bits for final measurements.
63
+ """
64
+ active_qubits = list(range(circuit.num_qubits))
65
+ active_cbits = list(range(circuit.num_clbits))
66
+
67
+ # Find final measurements starting in back
68
+ mapping = {}
69
+ for item in circuit._data[::-1]:
70
+ if item.operation.name == "measure":
71
+ cbit = circuit.find_bit(item.clbits[0]).index
72
+ qbit = circuit.find_bit(item.qubits[0]).index
73
+ if cbit in active_cbits and qbit in active_qubits:
74
+ mapping[qbit] = cbit
75
+ active_cbits.remove(cbit)
76
+ active_qubits.remove(qbit)
77
+ elif item.operation.name not in ["barrier", "delay"]:
78
+ for qq in item.qubits:
79
+ _temp_qubit = circuit.find_bit(qq).index
80
+ if _temp_qubit in active_qubits:
81
+ active_qubits.remove(_temp_qubit)
82
+
83
+ if not active_cbits or not active_qubits:
84
+ break
85
+
86
+ # Sort so that classical bits are in numeric order low->high.
87
+ mapping = dict(sorted(mapping.items(), key=lambda item: item[1]))
88
+ return mapping
89
+
90
+
91
+ def _bits_key(bits: tuple[Qubit, ...], circuit: QuantumCircuit) -> tuple:
92
+ return tuple(
93
+ (
94
+ circuit.find_bit(bit).index,
95
+ tuple((reg[0].size, reg[0].name, reg[1]) for reg in circuit.find_bit(bit).registers),
96
+ )
97
+ for bit in bits
98
+ )
99
+
100
+
101
+ def _format_params(param):
102
+ if isinstance(param, np.ndarray):
103
+ return param.data.tobytes()
104
+ elif isinstance(param, QuantumCircuit):
105
+ return _circuit_key(param)
106
+ elif isinstance(param, Iterable):
107
+ return tuple(param)
108
+ return param
109
+
110
+
111
+ def _circuit_key(circuit: QuantumCircuit, functional: bool = True) -> tuple:
112
+ """Private key function for QuantumCircuit.
113
+
114
+ This is the workaround until :meth:`QuantumCircuit.__hash__` will be introduced.
115
+ If key collision is found, please add elements to avoid it.
116
+
117
+ Args:
118
+ circuit: Input quantum circuit.
119
+ functional: If True, the returned key only includes functional data (i.e. execution related).
120
+
121
+ Returns:
122
+ Composite key for circuit.
123
+ """
124
+ functional_key: tuple = (
125
+ circuit.num_qubits,
126
+ circuit.num_clbits,
127
+ circuit.num_parameters,
128
+ tuple( # circuit.data
129
+ (
130
+ _bits_key(data.qubits, circuit), # qubits
131
+ _bits_key(data.clbits, circuit), # clbits
132
+ data.operation.name, # operation.name
133
+ tuple(_format_params(param) for param in data.operation.params), # operation.params
134
+ )
135
+ for data in circuit.data
136
+ ),
137
+ None if circuit._op_start_times is None else tuple(circuit._op_start_times),
138
+ )
139
+ if functional:
140
+ return functional_key
141
+ return (
142
+ circuit.name,
143
+ *functional_key,
144
+ )
145
+
146
+
147
+ class Sampler(BaseSamplerV1):
148
+ """
149
+ Aer implementation of Sampler class.
150
+
151
+ :Run Options:
152
+
153
+ - **shots** (None or int) --
154
+ The number of shots. If None, it calculates the probabilities exactly.
155
+ Otherwise, it samples from multinomial distributions.
156
+
157
+ - **seed** (int) --
158
+ Set a fixed seed for ``seed_simulator``. If shots is None, this option is ignored.
159
+
160
+ .. note::
161
+ Precedence of seeding is as follows:
162
+
163
+ 1. ``seed_simulator`` in runtime (i.e. in :meth:`__call__`)
164
+ 2. ``seed`` in runtime (i.e. in :meth:`__call__`)
165
+ 3. ``seed_simulator`` of ``backend_options``.
166
+ 4. default.
167
+ """
168
+
169
+ def __init__(
170
+ self,
171
+ *,
172
+ backend_options: dict | None = None,
173
+ transpile_options: dict | None = None,
174
+ run_options: dict | None = None,
175
+ skip_transpilation: bool = False,
176
+ ):
177
+ """
178
+ Args:
179
+ backend_options: Options passed to AerSimulator.
180
+ transpile_options: Options passed to transpile.
181
+ run_options: Options passed to run.
182
+ skip_transpilation: if True, transpilation is skipped.
183
+ """
184
+ warn(
185
+ "Sampler has been deprecated as of Aer 0.15, please use SamplerV2 instead.",
186
+ DeprecationWarning,
187
+ stacklevel=3,
188
+ )
189
+ super().__init__(options=run_options)
190
+ # These two private attributes used to be created by super, but were deprecated in Qiskit
191
+ # 0.46. See https://github.com/Qiskit/qiskit/pull/11051
192
+ self._circuits = []
193
+ self._parameters = []
194
+
195
+ self._backend = AerSimulator()
196
+ backend_options = {} if backend_options is None else backend_options
197
+ self._backend.set_options(**backend_options)
198
+ self._transpile_options = {} if transpile_options is None else transpile_options
199
+ self._skip_transpilation = skip_transpilation
200
+
201
+ self._transpiled_circuits = {}
202
+ self._circuit_ids = {}
203
+
204
+ def _call(
205
+ self,
206
+ circuits: Sequence[int],
207
+ parameter_values: Sequence[Sequence[float]],
208
+ **run_options,
209
+ ) -> SamplerResult:
210
+ seed = run_options.pop("seed", None)
211
+ if seed is not None:
212
+ run_options.setdefault("seed_simulator", seed)
213
+
214
+ is_shots_none = "shots" in run_options and run_options["shots"] is None
215
+ self._transpile(circuits, is_shots_none)
216
+
217
+ experiment_manager = _ExperimentManager()
218
+ for i, value in zip(circuits, parameter_values):
219
+ if len(value) != len(self._parameters[i]):
220
+ raise QiskitError(
221
+ f"The number of values ({len(value)}) does not match "
222
+ f"the number of parameters ({len(self._parameters[i])})."
223
+ )
224
+
225
+ experiment_manager.append(
226
+ key=i,
227
+ parameter_bind=dict(zip(self._parameters[i], value)),
228
+ experiment_circuit=self._transpiled_circuits[(i, is_shots_none)],
229
+ )
230
+
231
+ result = self._backend.run(
232
+ experiment_manager.experiment_circuits,
233
+ parameter_binds=experiment_manager.parameter_binds,
234
+ **run_options,
235
+ ).result()
236
+
237
+ # Postprocessing
238
+ metadata = []
239
+ quasis = []
240
+ for i in experiment_manager.experiment_indices:
241
+ if is_shots_none:
242
+ probabilities = result.data(i)["probabilities"]
243
+ num_qubits = result.results[i].metadata["num_qubits"]
244
+ quasi_dist = QuasiDistribution(
245
+ {f"{k:0{num_qubits}b}": v for k, v in probabilities.items()}
246
+ )
247
+ quasis.append(quasi_dist)
248
+ metadata.append({"shots": None, "simulator_metadata": result.results[i].metadata})
249
+ else:
250
+ counts = result.get_counts(i)
251
+ shots = sum(counts.values())
252
+ quasis.append(
253
+ QuasiDistribution(
254
+ {k.replace(" ", ""): v / shots for k, v in counts.items()},
255
+ shots=shots,
256
+ )
257
+ )
258
+ metadata.append({"shots": shots, "simulator_metadata": result.results[i].metadata})
259
+
260
+ return SamplerResult(quasis, metadata)
261
+
262
+ def _run(
263
+ self,
264
+ circuits: Sequence[QuantumCircuit],
265
+ parameter_values: Sequence[Sequence[float]],
266
+ **run_options,
267
+ ):
268
+ # pylint: disable=no-name-in-module, import-error, import-outside-toplevel, no-member
269
+ from typing import List
270
+
271
+ from qiskit.primitives.primitive_job import PrimitiveJob
272
+
273
+ circuit_indices: List[int] = []
274
+ for circuit in circuits:
275
+ index = self._circuit_ids.get(_circuit_key(circuit))
276
+ if index is not None:
277
+ circuit_indices.append(index)
278
+ else:
279
+ circuit_indices.append(len(self._circuits))
280
+ self._circuit_ids[_circuit_key(circuit)] = len(self._circuits)
281
+ self._circuits.append(circuit)
282
+ self._parameters.append(circuit.parameters)
283
+ job = PrimitiveJob(self._call, circuit_indices, parameter_values, **run_options)
284
+ # The public submit method was removed in Qiskit 0.46
285
+ (job.submit if hasattr(job, "submit") else job._submit)() # pylint: disable=no-member
286
+ return job
287
+
288
+ @staticmethod
289
+ def _preprocess_circuit(circuit: QuantumCircuit):
290
+ circuit = init_circuit(circuit)
291
+ q_c_mapping = final_measurement_mapping(circuit)
292
+ if set(range(circuit.num_clbits)) != set(q_c_mapping.values()):
293
+ raise QiskitError(
294
+ "Some classical bits are not used for measurements. "
295
+ f"The number of classical bits {circuit.num_clbits}, "
296
+ f"the used classical bits {set(q_c_mapping.values())}."
297
+ )
298
+ c_q_mapping = sorted((c, q) for q, c in q_c_mapping.items())
299
+ qargs = [q for _, q in c_q_mapping]
300
+ circuit = circuit.remove_final_measurements(inplace=False)
301
+ circuit.save_probabilities_dict(qargs)
302
+ return circuit
303
+
304
+ def _transpile_circuit(self, circuit):
305
+ self._backend.set_max_qubits(circuit.num_qubits)
306
+ transpiled = transpile(
307
+ circuit,
308
+ self._backend,
309
+ **self._transpile_options,
310
+ )
311
+ return transpiled
312
+
313
+ def _transpile(self, circuit_indices: Sequence[int], is_shots_none: bool):
314
+ to_handle = [
315
+ i for i in set(circuit_indices) if (i, is_shots_none) not in self._transpiled_circuits
316
+ ]
317
+ if to_handle:
318
+ circuits = (self._circuits[i] for i in to_handle)
319
+ if is_shots_none:
320
+ circuits = (self._preprocess_circuit(circ) for circ in circuits)
321
+ if not self._skip_transpilation:
322
+ circuits = (self._transpile_circuit(circ) for circ in circuits)
323
+ for i, circuit in zip(to_handle, circuits):
324
+ self._transpiled_circuits[(i, is_shots_none)] = circuit
325
+
326
+
327
+ class _ExperimentManager:
328
+ def __init__(self):
329
+ self.keys: list[int] = []
330
+ self.experiment_circuits: list[QuantumCircuit] = []
331
+ self.parameter_binds: list[dict[ParameterExpression, list[float]]] = []
332
+ self._input_indices: list[list[int]] = []
333
+ self._num_experiment: int = 0
334
+
335
+ def __len__(self):
336
+ return self._num_experiment
337
+
338
+ @property
339
+ def experiment_indices(self):
340
+ """indices of experiments"""
341
+ return np.argsort(sum(self._input_indices, [])).tolist()
342
+
343
+ def append(
344
+ self,
345
+ key: tuple[int, int],
346
+ parameter_bind: dict[ParameterExpression, float],
347
+ experiment_circuit: QuantumCircuit,
348
+ ):
349
+ """append experiments"""
350
+ if parameter_bind and key in self.keys:
351
+ key_index = self.keys.index(key)
352
+ for k, vs in self.parameter_binds[key_index].items():
353
+ vs.append(parameter_bind[k])
354
+ self._input_indices[key_index].append(self._num_experiment)
355
+ else:
356
+ self.experiment_circuits.append(experiment_circuit)
357
+ self.keys.append(key)
358
+ self.parameter_binds.append({k: [v] for k, v in parameter_bind.items()})
359
+ self._input_indices.append([self._num_experiment])
360
+
361
+ self._num_experiment += 1