qiskit-aer 0.17.2__cp314-cp314-macosx_11_0_arm64.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.cpython-314-darwin.so +0 -0
  14. qiskit_aer/backends/libomp.dylib +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 +6 -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,1085 @@
1
+ # This code is part of Qiskit.
2
+ #
3
+ # (C) Copyright IBM 2018, 2019, 2021
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
+ Compier to convert Qiskit control-flow to Aer backend.
14
+ """
15
+
16
+ import collections
17
+ import itertools
18
+ from copy import copy
19
+ from typing import List
20
+ from warnings import warn
21
+ from concurrent.futures import Executor
22
+ import uuid
23
+ import numpy as np
24
+
25
+ from qiskit.circuit import QuantumCircuit, Clbit, ClassicalRegister, ParameterExpression
26
+ from qiskit.circuit.classical.expr import Expr, Unary, Binary, Var, Value, ExprVisitor, iter_vars
27
+ from qiskit.circuit.classical.types import Bool, Uint
28
+ from qiskit.circuit.library import Initialize
29
+ from qiskit.providers.options import Options
30
+ from qiskit.circuit import Store
31
+ from qiskit.circuit.controlflow import (
32
+ WhileLoopOp,
33
+ ForLoopOp,
34
+ IfElseOp,
35
+ BreakLoopOp,
36
+ ContinueLoopOp,
37
+ SwitchCaseOp,
38
+ CASE_DEFAULT,
39
+ )
40
+ from qiskit.transpiler import PassManager
41
+ from qiskit.transpiler.passes import Decompose
42
+
43
+ from qiskit_aer.aererror import AerError
44
+ from qiskit_aer.noise import NoiseModel
45
+
46
+ # pylint: disable=import-error, no-name-in-module
47
+ from qiskit_aer.backends.controller_wrappers import (
48
+ AerUnaryExpr,
49
+ AerUnaryOp,
50
+ AerBinaryExpr,
51
+ AerBinaryOp,
52
+ AerUintValue,
53
+ AerBoolValue,
54
+ AerUint,
55
+ AerBool,
56
+ AerCast,
57
+ AerVar,
58
+ AerCircuit,
59
+ AerConfig,
60
+ )
61
+
62
+ from .backend_utils import circuit_optypes, CircuitHeader
63
+ from ..library.control_flow_instructions import AerMark, AerJump, AerStore
64
+
65
+
66
+ class AerCompiler:
67
+ """Aer Compiler to convert instructions of control-flow to mark and jump instructions"""
68
+
69
+ def __init__(self):
70
+ self._last_flow_id = -1
71
+
72
+ def compile(self, circuits, optypes=None):
73
+ """compile a circuit that have control-flow instructions.
74
+
75
+ Args:
76
+ circuits (QuantumCircuit or list): The QuantumCircuits to be compiled
77
+ optypes (list): list of instruction type sets for each circuit
78
+ (default: None).
79
+
80
+ Returns:
81
+ list: A list QuantumCircuit without control-flow
82
+ if optypes is None.
83
+ tuple: A tuple of a list of quantum circuits and list of
84
+ compiled circuit optypes for each circuit if
85
+ optypes kwarg is not None.
86
+ """
87
+ if isinstance(circuits, QuantumCircuit):
88
+ circuits = [circuits]
89
+ if optypes is None:
90
+ compiled_optypes = len(circuits) * [None]
91
+ else:
92
+ # Make a shallow copy incase we modify it
93
+ compiled_optypes = list(optypes)
94
+ if isinstance(circuits, list):
95
+ compiled_circuits = []
96
+ for idx, circuit in enumerate(circuits):
97
+ # Resolve initialize
98
+ circuit = self._inline_initialize(circuit, compiled_optypes[idx])
99
+ if self._is_dynamic(circuit, compiled_optypes[idx]):
100
+ pm = PassManager([Decompose(["mark", "jump"])])
101
+ compiled_circ = pm.run(self._inline_circuit(circuit, None, None))
102
+ # compiled_circ._vars_local = inlined_circ._vars_local
103
+ # compiled_circ._vars_input = inlined_circ._vars_input
104
+ # compiled_circ._vars_capture = inlined_circ._vars_capture
105
+ compiled_circuits.append(compiled_circ)
106
+ # Recompute optype for compiled circuit
107
+ compiled_optypes[idx] = circuit_optypes(compiled_circ)
108
+ else:
109
+ compiled_circuits.append(circuit)
110
+ if optypes is None:
111
+ return compiled_circuits
112
+ return compiled_circuits, compiled_optypes
113
+
114
+ if optypes is None:
115
+ return circuits
116
+ return circuits, optypes
117
+
118
+ def _inline_initialize(self, circ, optype):
119
+ """inline initialize.definition gates if statevector is not used"""
120
+ if isinstance(optype, set) and Initialize not in optype:
121
+ return circ
122
+
123
+ for datum in circ.data:
124
+ inst = datum.operation
125
+ if isinstance(inst, Initialize) and (
126
+ (not isinstance(inst.params[0], complex)) or (len(inst.params) == 1)
127
+ ):
128
+ break
129
+ else:
130
+ return circ
131
+
132
+ new_circ = circ.copy()
133
+ new_circ.data = []
134
+ for datum in circ.data:
135
+ inst, qargs, cargs = datum.operation, datum.qubits, datum.clbits
136
+ if isinstance(inst, Initialize) and (
137
+ (not isinstance(inst.params[0], complex)) or (len(inst.params) == 1)
138
+ ):
139
+ # Assume that the decomposed circuit of inst.definition consists of basis gates
140
+ new_circ.compose(inst.definition.decompose(), qargs, cargs, inplace=True)
141
+ else:
142
+ new_circ._append(inst, qargs, cargs)
143
+
144
+ return new_circ
145
+
146
+ @staticmethod
147
+ def _is_dynamic(circuit, optype=None):
148
+ """check whether a circuit contains control-flow instructions"""
149
+ if not isinstance(circuit, QuantumCircuit):
150
+ return False
151
+
152
+ controlflow_types = (
153
+ WhileLoopOp,
154
+ ForLoopOp,
155
+ IfElseOp,
156
+ BreakLoopOp,
157
+ ContinueLoopOp,
158
+ SwitchCaseOp,
159
+ )
160
+
161
+ # Check via optypes
162
+ if isinstance(optype, set):
163
+ return bool(optype.intersection(controlflow_types))
164
+
165
+ # Check via iteration
166
+ for instruction in circuit.data:
167
+ if isinstance(instruction.operation, controlflow_types):
168
+ return True
169
+
170
+ return False
171
+
172
+ def _inline_circuit(self, circ, continue_label, break_label, bit_map=None):
173
+ """convert control-flow instructions to mark and jump instructions
174
+
175
+ Args:
176
+ circ (QuantumCircuit): The QuantumCircuit to be compiled
177
+ continue_label (str): label name for continue.
178
+ break_label (str): label name for break.
179
+ bit_map (dict[Bit, Bit]): mapping of virtual bits in the current circuit to the bit they
180
+ represent in the outermost circuit.
181
+
182
+ Returns:
183
+ QuantumCircuit: QuantumCircuit without control-flow instructions
184
+ """
185
+ ret = circ.copy_empty_like()
186
+ bit_map = {bit: bit for bit in itertools.chain(ret.qubits, ret.clbits)}
187
+
188
+ for instruction in circ.data:
189
+ # The barriers around all control-flow operations is to prevent any non-control-flow
190
+ # operations from ending up topologically "inside" a body. This can happen if the body
191
+ # is not full width on the circuit, and the other operation uses disjoint bits.
192
+ if isinstance(instruction.operation, ForLoopOp):
193
+ ret.barrier()
194
+ self._inline_for_loop_op(instruction, ret, bit_map)
195
+ ret.barrier()
196
+ elif isinstance(instruction.operation, WhileLoopOp):
197
+ ret.barrier()
198
+ self._inline_while_loop_op(instruction, ret, bit_map)
199
+ ret.barrier()
200
+ elif isinstance(instruction.operation, IfElseOp):
201
+ ret.barrier()
202
+ self._inline_if_else_op(instruction, continue_label, break_label, ret, bit_map)
203
+ ret.barrier()
204
+ elif isinstance(instruction.operation, SwitchCaseOp):
205
+ ret.barrier()
206
+ self._inline_switch_case_op(instruction, continue_label, break_label, ret, bit_map)
207
+ ret.barrier()
208
+ elif isinstance(instruction.operation, BreakLoopOp):
209
+ ret._append(
210
+ AerJump(break_label, ret.num_qubits, ret.num_clbits), ret.qubits, ret.clbits
211
+ )
212
+ elif isinstance(instruction.operation, ContinueLoopOp):
213
+ ret._append(
214
+ AerJump(continue_label, ret.num_qubits, ret.num_clbits), ret.qubits, ret.clbits
215
+ )
216
+ elif isinstance(instruction.operation, Store):
217
+ ret._append(
218
+ AerStore(ret.num_qubits, ret.num_clbits, instruction.operation),
219
+ ret.qubits,
220
+ ret.clbits,
221
+ )
222
+ else:
223
+ ret._append(instruction)
224
+ return ret
225
+
226
+ def _convert_jump_conditional(self, cond_tuple, bit_map):
227
+ """Convert a condition tuple according to the wire map."""
228
+ if isinstance(cond_tuple, Expr):
229
+ return cond_tuple
230
+ elif isinstance(cond_tuple[0], Clbit):
231
+ return (bit_map[cond_tuple[0]], cond_tuple[1])
232
+ elif isinstance(cond_tuple[0], ClassicalRegister):
233
+ # ClassicalRegister conditions should already be in the outer circuit.
234
+ return cond_tuple
235
+ elif isinstance(cond_tuple[0], Var):
236
+ if isinstance(cond_tuple[0].var, Clbit):
237
+ expr = Var(bit_map[cond_tuple[0].var], cond_tuple[0].type)
238
+ elif isinstance(cond_tuple[0].var, ClassicalRegister):
239
+ reg = ClassicalRegister(bits=[bit_map[clbit] for clbit in cond_tuple[0].var])
240
+ expr = Var(reg, cond_tuple[0].type)
241
+ else:
242
+ raise AerError(
243
+ f"jump condition does not support this tyep of Var: {cond_tuple[0]}."
244
+ )
245
+ return (expr, cond_tuple[1])
246
+
247
+ raise AerError(f"jump condition does not support {cond_tuple[0].__class__}.")
248
+
249
+ def _list_clbit_from_expr(self, bit_map, expr):
250
+ ret = set()
251
+ for var in iter_vars(expr):
252
+ if isinstance(var.var, Clbit):
253
+ ret.add(bit_map[var.var])
254
+ elif isinstance(var.var, ClassicalRegister):
255
+ ret.update(bit_map[bit] for bit in var.var)
256
+ return ret
257
+
258
+ def _inline_for_loop_op(self, instruction, parent, bit_map):
259
+ """inline for_loop body while iterating its indexset"""
260
+ qargs = [bit_map[q] for q in instruction.qubits]
261
+ cargs = [bit_map[c] for c in instruction.clbits]
262
+ # to avoid wrong topological sorting of command with "empty" block
263
+ if len(qargs) == 0:
264
+ qargs = parent.qubits
265
+ indexset, loop_parameter, body = instruction.operation.params
266
+ inner_bit_map = {
267
+ inner: bit_map[outer]
268
+ for inner, outer in itertools.chain(
269
+ zip(body.qubits, instruction.qubits),
270
+ zip(body.clbits, instruction.clbits),
271
+ )
272
+ }
273
+
274
+ self._last_flow_id += 1
275
+ loop_id = self._last_flow_id
276
+ loop_name = f"loop_{loop_id}"
277
+
278
+ inlined_body = None
279
+ break_label = f"{loop_name}_end"
280
+ for index in indexset:
281
+ continue_label = f"{loop_name}_{index}"
282
+ inlined_body = self._inline_circuit(body, continue_label, break_label, inner_bit_map)
283
+ if loop_parameter is not None:
284
+ inlined_body = inlined_body.assign_parameters({loop_parameter: index})
285
+ for inst in inlined_body:
286
+ parent.append(
287
+ inst.replace(
288
+ qubits=[inner_bit_map[bit] for bit in inst.qubits],
289
+ clbits=[inner_bit_map[bit] for bit in inst.clbits],
290
+ ),
291
+ qargs,
292
+ cargs,
293
+ )
294
+ parent.append(AerMark(continue_label, len(qargs), len(cargs)), qargs, cargs)
295
+
296
+ if inlined_body is not None:
297
+ parent.append(AerMark(break_label, len(qargs), len(cargs)), qargs, cargs)
298
+
299
+ def _inline_while_loop_op(self, instruction, parent, bit_map):
300
+ """inline while_loop body with jump and mark instructions"""
301
+ condition_tuple = self._convert_jump_conditional(instruction.operation.condition, bit_map)
302
+ (body,) = instruction.operation.params
303
+
304
+ self._last_flow_id += 1
305
+ loop_id = self._last_flow_id
306
+ loop_name = f"while_{loop_id}"
307
+
308
+ continue_label = f"{loop_name}_continue"
309
+ loop_start_label = f"{loop_name}_start"
310
+ break_label = f"{loop_name}_end"
311
+ inline_bit_map = {
312
+ inner: bit_map[outer]
313
+ for inner, outer in itertools.chain(
314
+ zip(body.qubits, instruction.qubits),
315
+ zip(body.clbits, instruction.clbits),
316
+ )
317
+ }
318
+ inlined_body = self._inline_circuit(
319
+ body,
320
+ continue_label,
321
+ break_label,
322
+ inline_bit_map,
323
+ )
324
+ qargs = [bit_map[q] for q in instruction.qubits]
325
+ cargs = [bit_map[c] for c in instruction.clbits]
326
+ # to avoid wrong topological sorting of command with "empty" block
327
+ if len(qargs) == 0:
328
+ qargs = parent.qubits
329
+
330
+ if isinstance(condition_tuple, Expr):
331
+ mark_cargs = self._list_clbit_from_expr(bit_map, condition_tuple)
332
+ elif isinstance(condition_tuple[0], Clbit):
333
+ mark_cargs = {bit_map[condition_tuple[0]]}
334
+ else:
335
+ mark_cargs = {bit_map[c] for c in condition_tuple[0]}
336
+ mark_cargs = set(cargs).union(mark_cargs) - set(instruction.clbits)
337
+
338
+ c_if_args = self._convert_jump_conditional(condition_tuple, bit_map)
339
+
340
+ parent.append(AerMark(continue_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
341
+ parent.append(
342
+ AerJump(loop_start_label, len(qargs), len(mark_cargs)).set_conditional(c_if_args),
343
+ qargs,
344
+ mark_cargs,
345
+ )
346
+ parent.append(AerJump(break_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
347
+ parent.append(AerMark(loop_start_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
348
+ for inst in inlined_body:
349
+ parent.append(
350
+ inst.replace(
351
+ qubits=[inline_bit_map[bit] for bit in inst.qubits],
352
+ clbits=[inline_bit_map[bit] for bit in inst.clbits],
353
+ ),
354
+ qargs,
355
+ cargs,
356
+ )
357
+ parent.append(AerJump(continue_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
358
+ parent.append(AerMark(break_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
359
+
360
+ def _inline_if_else_op(self, instruction, continue_label, break_label, parent, bit_map):
361
+ """inline true and false bodies of if_else with jump and mark instructions"""
362
+ condition_tuple = instruction.operation.condition
363
+ true_body, false_body = instruction.operation.params
364
+
365
+ self._last_flow_id += 1
366
+ if_id = self._last_flow_id
367
+ if_name = f"if_{if_id}"
368
+
369
+ if_true_label = f"{if_name}_true"
370
+ if_end_label = f"{if_name}_end"
371
+ if false_body:
372
+ if_else_label = f"{if_name}_else"
373
+ else:
374
+ if_else_label = if_end_label
375
+
376
+ c_if_args = self._convert_jump_conditional(condition_tuple, bit_map)
377
+
378
+ qargs = [bit_map[q] for q in instruction.qubits]
379
+ cargs = [bit_map[c] for c in instruction.clbits]
380
+ # to avoid wrong topological sorting of command with "empty" block
381
+ if len(qargs) == 0:
382
+ qargs = parent.qubits
383
+
384
+ # to avoid wrong topological sorting of command with "empty" block
385
+ if len(qargs) == 0:
386
+ qargs = parent.qubits
387
+
388
+ if isinstance(condition_tuple, Expr):
389
+ mark_cargs = self._list_clbit_from_expr(bit_map, condition_tuple)
390
+ elif isinstance(condition_tuple[0], Clbit):
391
+ mark_cargs = {bit_map[condition_tuple[0]]}
392
+ else:
393
+ mark_cargs = {bit_map[c] for c in condition_tuple[0]}
394
+ mark_cargs = set(cargs).union(mark_cargs) - set(instruction.clbits)
395
+
396
+ true_bit_map = {
397
+ inner: bit_map[outer]
398
+ for inner, outer in itertools.chain(
399
+ zip(true_body.qubits, instruction.qubits),
400
+ zip(true_body.clbits, instruction.clbits),
401
+ )
402
+ }
403
+
404
+ parent.append(
405
+ AerJump(if_true_label, len(qargs), len(mark_cargs)).set_conditional(c_if_args),
406
+ qargs,
407
+ mark_cargs,
408
+ )
409
+ parent.append(AerJump(if_else_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
410
+ parent.append(AerMark(if_true_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
411
+ child = self._inline_circuit(true_body, continue_label, break_label, true_bit_map)
412
+ for inst in child.data:
413
+ parent.append(
414
+ inst.replace(
415
+ qubits=[true_bit_map[bit] for bit in inst.qubits],
416
+ clbits=[true_bit_map[bit] for bit in inst.clbits],
417
+ ),
418
+ qargs,
419
+ cargs,
420
+ )
421
+
422
+ if false_body:
423
+ false_bit_map = {
424
+ inner: bit_map[outer]
425
+ for inner, outer in itertools.chain(
426
+ zip(false_body.qubits, instruction.qubits),
427
+ zip(false_body.clbits, instruction.clbits),
428
+ )
429
+ }
430
+ parent.append(AerJump(if_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
431
+ parent.append(AerMark(if_else_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
432
+ child = self._inline_circuit(false_body, continue_label, break_label, false_bit_map)
433
+ for inst in child.data:
434
+ parent.append(
435
+ inst.replace(
436
+ qubits=[false_bit_map[bit] for bit in inst.qubits],
437
+ clbits=[false_bit_map[bit] for bit in inst.clbits],
438
+ ),
439
+ qargs,
440
+ cargs,
441
+ )
442
+
443
+ parent.append(AerMark(if_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
444
+
445
+ def _inline_switch_case_op(self, instruction, continue_label, break_label, parent, bit_map):
446
+ """inline switch cases with jump and mark instructions"""
447
+ cases = instruction.operation.cases_specifier()
448
+
449
+ self._last_flow_id += 1
450
+ switch_id = self._last_flow_id
451
+ switch_name = f"switch_{switch_id}"
452
+
453
+ qargs = [bit_map[q] for q in instruction.qubits]
454
+ cargs = [bit_map[c] for c in instruction.clbits]
455
+ # to avoid wrong topological sorting of command with "empty" block
456
+ if len(qargs) == 0:
457
+ qargs = parent.qubits
458
+
459
+ if isinstance(instruction.operation.target, Clbit):
460
+ target_clbits = {bit_map[instruction.operation.target]}
461
+ elif isinstance(instruction.operation.target, Expr):
462
+ target_clbits = self._list_clbit_from_expr(bit_map, instruction.operation.target)
463
+ else:
464
+ target_clbits = {bit_map[c] for c in instruction.operation.target}
465
+ mark_cargs = set(cargs).union(target_clbits) - set(instruction.clbits)
466
+
467
+ switch_end_label = f"{switch_name}_end"
468
+ case_default_label = None
469
+ CaseData = collections.namedtuple("CaseData", ["label", "args_list", "bit_map", "body"])
470
+ case_data_list = []
471
+ for i, case in enumerate(cases):
472
+ if case_default_label is not None:
473
+ raise AerError("cases after the default are unreachable")
474
+
475
+ case_data = CaseData(
476
+ label=f"{switch_name}_{i}",
477
+ args_list=[
478
+ (
479
+ self._convert_jump_conditional(
480
+ (instruction.operation.target, switch_val), bit_map
481
+ )
482
+ if switch_val != CASE_DEFAULT
483
+ else []
484
+ )
485
+ for switch_val in case[0]
486
+ ],
487
+ bit_map={
488
+ inner: bit_map[outer]
489
+ for inner, outer in itertools.chain(
490
+ zip(case[1].qubits, instruction.qubits),
491
+ zip(case[1].clbits, instruction.clbits),
492
+ )
493
+ },
494
+ body=case[1],
495
+ )
496
+ case_data_list.append(case_data)
497
+ if CASE_DEFAULT in case[0]:
498
+ case_default_label = case_data.label
499
+
500
+ if case_default_label is None:
501
+ case_default_label = switch_end_label
502
+
503
+ for case_data in case_data_list:
504
+ for case_args in case_data.args_list:
505
+ if len(case_args) > 0:
506
+ if isinstance(case_args[0], Expr):
507
+ case_args = Binary(
508
+ Binary.Op.EQUAL,
509
+ case_args[0],
510
+ Value(case_args[1], case_args[0].type),
511
+ Bool(),
512
+ )
513
+ parent.append(
514
+ AerJump(case_data.label, len(qargs), len(mark_cargs)).set_conditional(
515
+ case_args
516
+ ),
517
+ qargs,
518
+ mark_cargs,
519
+ )
520
+
521
+ parent.append(AerJump(case_default_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
522
+
523
+ for case_data in case_data_list:
524
+ parent.append(AerMark(case_data.label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
525
+ child = self._inline_circuit(
526
+ case_data.body, continue_label, break_label, case_data.bit_map
527
+ )
528
+ for inst in child.data:
529
+ parent.append(
530
+ inst.replace(
531
+ qubits=[case_data.bit_map[bit] for bit in inst.qubits],
532
+ clbits=[case_data.bit_map[bit] for bit in inst.clbits],
533
+ ),
534
+ qargs,
535
+ cargs,
536
+ )
537
+ parent.append(AerJump(switch_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
538
+
539
+ parent.append(AerMark(switch_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs)
540
+
541
+
542
+ def compile_circuit(circuits, optypes=None):
543
+ """
544
+ compile a circuit that have control-flow instructions
545
+ """
546
+ return AerCompiler().compile(circuits, optypes)
547
+
548
+
549
+ BACKEND_RUN_ARG_TYPES = {
550
+ "shots": (int, np.integer),
551
+ "method": (str),
552
+ "device": (str),
553
+ "precision": (str),
554
+ "max_job_size": (int, np.integer),
555
+ "max_shot_size": (int, np.integer),
556
+ "enable_truncation": (bool, np.bool_),
557
+ "executor": Executor,
558
+ "zero_threshold": (float, np.floating),
559
+ "validation_threshold": (int, np.integer),
560
+ "max_parallel_threads": (int, np.integer),
561
+ "max_parallel_experiments": (int, np.integer),
562
+ "max_parallel_shots": (int, np.integer),
563
+ "max_memory_mb": (int, np.integer),
564
+ "fusion_enable": (bool, np.bool_),
565
+ "fusion_verbose": (bool, np.bool_),
566
+ "fusion_max_qubit": (int, np.integer),
567
+ "fusion_threshold": (int, np.integer),
568
+ "accept_distributed_results": (bool, np.bool_),
569
+ "memory": (bool, np.bool_),
570
+ "noise_model": (NoiseModel),
571
+ "seed_simulator": (int, np.integer),
572
+ "cuStateVec_enable": (int, np.integer),
573
+ "blocking_qubits": (int, np.integer),
574
+ "blocking_enable": (bool, np.bool_),
575
+ "chunk_swap_buffer_qubits": (int, np.integer),
576
+ "batched_shots_gpu": (bool, np.bool_),
577
+ "batched_shots_gpu_max_qubits": (int, np.integer),
578
+ "shot_branching_enable": (bool, np.bool_),
579
+ "shot_branching_sampling_enable": (bool, np.bool_),
580
+ "num_threads_per_device": (int, np.integer),
581
+ "statevector_parallel_threshold": (int, np.integer),
582
+ "statevector_sample_measure_opt": (int, np.integer),
583
+ "stabilizer_max_snapshot_probabilities": (int, np.integer),
584
+ "extended_stabilizer_sampling_method": (str),
585
+ "extended_stabilizer_metropolis_mixing_time": (int, np.integer),
586
+ "extended_stabilizer_approximation_error": (float, np.floating),
587
+ "extended_stabilizer_norm_estimation_samples": (int, np.integer),
588
+ "extended_stabilizer_norm_estimation_repetitions": (int, np.integer),
589
+ "extended_stabilizer_parallel_threshold": (int, np.integer),
590
+ "extended_stabilizer_probabilities_snapshot_samples": (int, np.integer),
591
+ "matrix_product_state_truncation_threshold": (float, np.floating),
592
+ "matrix_product_state_max_bond_dimension": (int, np.integer),
593
+ "mps_sample_measure_algorithm": (str),
594
+ "mps_log_data": (bool, np.bool_),
595
+ "mps_swap_direction": (str),
596
+ "chop_threshold": (float, np.floating),
597
+ "mps_parallel_threshold": (int, np.integer),
598
+ "mps_omp_threads": (int, np.integer),
599
+ "mps_lapack": (bool, np.bool_),
600
+ "tensor_network_num_sampling_qubits": (int, np.integer),
601
+ "use_cuTensorNet_autotuning": (bool, np.bool_),
602
+ "parameterizations": (list),
603
+ "fusion_parallelization_threshold": (int, np.integer),
604
+ "target_gpus": (list),
605
+ "runtime_parameter_bind_enable": (bool, np.bool_),
606
+ }
607
+
608
+
609
+ def _validate_option(k, v):
610
+ """validate backend.run arguments"""
611
+ if v is None:
612
+ return v
613
+ if k not in BACKEND_RUN_ARG_TYPES:
614
+ raise AerError(f"invalid argument: name={k}")
615
+ if isinstance(v, BACKEND_RUN_ARG_TYPES[k]):
616
+ return v
617
+
618
+ expected_type = BACKEND_RUN_ARG_TYPES[k][0]
619
+
620
+ if expected_type in (int, float, bool, str):
621
+ try:
622
+ ret = expected_type(v)
623
+ if not isinstance(v, BACKEND_RUN_ARG_TYPES[k]):
624
+ warn(
625
+ f'A type of an option "{k}" should be {expected_type.__name__} '
626
+ "but {v.__class__.__name__} was specified."
627
+ "Implicit cast for an argument has been deprecated as of qiskit-aer 0.12.1.",
628
+ DeprecationWarning,
629
+ stacklevel=5,
630
+ )
631
+ return ret
632
+ except Exception: # pylint: disable=broad-except
633
+ pass
634
+
635
+ raise TypeError(
636
+ f"invalid option type: name={k}, "
637
+ f"type={v.__class__.__name__}, expected={BACKEND_RUN_ARG_TYPES[k][0].__name__}"
638
+ )
639
+
640
+
641
+ def generate_aer_config(
642
+ circuits: List[QuantumCircuit], backend_options: Options, **run_options
643
+ ) -> AerConfig:
644
+ """generates a configuration to run simulation.
645
+
646
+ Args:
647
+ circuits: circuit(s) to be converted
648
+ backend_options: backend options
649
+ run_options: run options
650
+
651
+ Returns:
652
+ AerConfig to run Aer
653
+ """
654
+ num_qubits = max(circuit.num_qubits for circuit in circuits)
655
+ memory_slots = max(circuit.num_clbits for circuit in circuits)
656
+
657
+ config = AerConfig()
658
+ config.memory_slots = memory_slots
659
+ config.n_qubits = num_qubits
660
+ for key, value in backend_options.__dict__.items():
661
+ if hasattr(config, key) and value is not None:
662
+ value = _validate_option(key, value)
663
+ setattr(config, key, value)
664
+ for key, value in run_options.items():
665
+ if hasattr(config, key) and value is not None:
666
+ value = _validate_option(key, value)
667
+ setattr(config, key, value)
668
+ return config
669
+
670
+
671
+ def assemble_circuit(circuit: QuantumCircuit, basis_gates=None):
672
+ """assemble circuit object mapped to AER::Circuit"""
673
+
674
+ num_qubits = circuit.num_qubits
675
+ num_memory = circuit.num_clbits
676
+ extra_creg_idx = 0
677
+
678
+ qreg_sizes = []
679
+ creg_sizes = []
680
+ if (
681
+ isinstance(circuit.global_phase, ParameterExpression)
682
+ and len(circuit.global_phase.parameters) > 0
683
+ ):
684
+ global_phase = 0.0
685
+ else:
686
+ global_phase = float(circuit.global_phase)
687
+
688
+ for qreg in circuit.qregs:
689
+ qreg_sizes.append([qreg.name, qreg.size])
690
+ for creg in circuit.cregs:
691
+ creg_sizes.append([creg.name, creg.size])
692
+
693
+ is_conditional = any(
694
+ getattr(inst.operation, "condition_expr", None)
695
+ or getattr(inst.operation, "condition", None)
696
+ for inst in circuit.data
697
+ )
698
+
699
+ header = CircuitHeader(
700
+ n_qubits=num_qubits,
701
+ qreg_sizes=qreg_sizes,
702
+ memory_slots=num_memory,
703
+ creg_sizes=creg_sizes,
704
+ name=circuit.name,
705
+ global_phase=global_phase,
706
+ )
707
+
708
+ qubit_indices = {qubit: idx for idx, qubit in enumerate(circuit.qubits)}
709
+ clbit_indices = {clbit: idx for idx, clbit in enumerate(circuit.clbits)}
710
+
711
+ aer_circ = AerCircuit()
712
+ aer_circ.set_header(header)
713
+ aer_circ.num_qubits = num_qubits
714
+ aer_circ.num_memory = num_memory
715
+ aer_circ.global_phase_angle = global_phase
716
+
717
+ var_heap_map = {}
718
+ for var in _iter_var_recursive(circuit):
719
+ memory_pos = num_memory + extra_creg_idx
720
+ var_heap_map[var.name] = (memory_pos, var.type.width)
721
+ extra_creg_idx += var.type.width
722
+
723
+ num_of_aer_ops = 0
724
+ index_map = []
725
+ for inst in circuit.data:
726
+ # To convert to a qobj-style conditional, insert a bfunc prior
727
+ # to the conditional instruction to map the creg ?= val condition
728
+ # onto a gating register bit.
729
+ conditional_reg = -1
730
+ conditional_expr = None
731
+ if hasattr(inst.operation, "condition") and inst.operation.condition:
732
+ ctrl_reg, ctrl_val = inst.operation.condition
733
+ mask = 0
734
+ val = 0
735
+ if isinstance(ctrl_reg, Clbit):
736
+ mask = 1 << clbit_indices[ctrl_reg]
737
+ val = (ctrl_val & 1) << clbit_indices[ctrl_reg]
738
+ else:
739
+ for clbit, idx in clbit_indices.items():
740
+ if clbit in ctrl_reg:
741
+ mask |= 1 << idx
742
+ val |= ((ctrl_val >> list(ctrl_reg).index(clbit)) & 1) << idx
743
+ conditional_reg = num_memory + extra_creg_idx
744
+ aer_circ.bfunc(f"0x{mask:X}", f"0x{val:X}", "==", conditional_reg)
745
+ num_of_aer_ops += 1
746
+ extra_creg_idx += 1
747
+ elif hasattr(inst.operation, "condition_expr") and inst.operation.condition_expr:
748
+ conditional_expr = inst.operation.condition_expr
749
+
750
+ num_of_aer_ops += _assemble_op(
751
+ circuit,
752
+ aer_circ,
753
+ inst,
754
+ qubit_indices,
755
+ clbit_indices,
756
+ is_conditional,
757
+ conditional_reg,
758
+ conditional_expr,
759
+ basis_gates,
760
+ )
761
+ index_map.append(num_of_aer_ops - 1)
762
+
763
+ return aer_circ, index_map
764
+
765
+
766
+ def _assemble_type(expr_type):
767
+ if isinstance(expr_type, Uint):
768
+ return AerUint(expr_type.width)
769
+ elif isinstance(expr_type, Bool):
770
+ return AerBool()
771
+ else:
772
+ raise AerError(f"unknown type: {expr_type.__class__}")
773
+
774
+
775
+ def _iter_var_recursive(circuit):
776
+ yield from circuit.iter_vars()
777
+ for instruction in circuit.data:
778
+ for param in instruction.operation.params:
779
+ if isinstance(param, QuantumCircuit):
780
+ yield from _iter_var_recursive(param)
781
+
782
+
783
+ def _find_var_clbits(circuit, var_uuid):
784
+ clbit_index = circuit.num_clbits
785
+ for var in _iter_var_recursive(circuit):
786
+ if var.var == var_uuid:
787
+ return list(range(clbit_index, clbit_index + var.type.width))
788
+ clbit_index += var.type.width
789
+ raise AerError(f"Var is not registed in this circuit: uuid={var_uuid}")
790
+
791
+
792
+ def _assemble_clbit_indices(circ, c):
793
+ if isinstance(c, (ClassicalRegister, list)):
794
+ return [circ.find_bit(cbit).index for cbit in c]
795
+ elif isinstance(c, Clbit):
796
+ return [circ.find_bit(c).index]
797
+ elif isinstance(c, uuid.UUID):
798
+ return _find_var_clbits(circ, c)
799
+ else:
800
+ raise AerError(f"unknown clibt list: {c.__class__}")
801
+
802
+
803
+ def _assemble_unary_operator(op):
804
+ if op is Unary.Op.BIT_NOT:
805
+ return AerUnaryOp.BitNot
806
+ elif op is Unary.Op.LOGIC_NOT:
807
+ return AerUnaryOp.LogicNot
808
+ else:
809
+ raise AerError(f"unknown op: {op}")
810
+
811
+
812
+ _BINARY_OP_MAPPING = {
813
+ Binary.Op.BIT_AND: AerBinaryOp.BitAnd,
814
+ Binary.Op.BIT_OR: AerBinaryOp.BitOr,
815
+ Binary.Op.BIT_XOR: AerBinaryOp.BitXor,
816
+ Binary.Op.LOGIC_AND: AerBinaryOp.LogicAnd,
817
+ Binary.Op.LOGIC_OR: AerBinaryOp.LogicOr,
818
+ Binary.Op.EQUAL: AerBinaryOp.Equal,
819
+ Binary.Op.NOT_EQUAL: AerBinaryOp.NotEqual,
820
+ Binary.Op.LESS: AerBinaryOp.Less,
821
+ Binary.Op.LESS_EQUAL: AerBinaryOp.LessEqual,
822
+ Binary.Op.GREATER: AerBinaryOp.Greater,
823
+ Binary.Op.GREATER_EQUAL: AerBinaryOp.GreaterEqual,
824
+ }
825
+
826
+
827
+ def _assemble_binary_operator(op):
828
+ if op in _BINARY_OP_MAPPING:
829
+ return _BINARY_OP_MAPPING[op]
830
+ else:
831
+ raise AerError(f"unknown op: {op}")
832
+
833
+
834
+ class _AssembleExprImpl(ExprVisitor):
835
+ """Convert from Expr objects to corresponding objects."""
836
+
837
+ def __init__(self, circuit):
838
+ self.circuit = circuit
839
+
840
+ def visit_value(self, node, /):
841
+ """return Aer's value types."""
842
+ # pylint: disable=unused-variable
843
+ if isinstance(node.type, Uint):
844
+ return AerUintValue(node.type.width, node.value)
845
+ elif isinstance(node.type, Bool):
846
+ return AerBoolValue(node.value)
847
+ else:
848
+ raise AerError(f"invalid value type is specified: {node.type.__class__}")
849
+
850
+ def visit_var(self, node, /):
851
+ return AerVar(_assemble_type(node.type), _assemble_clbit_indices(self.circuit, node.var))
852
+
853
+ def visit_cast(self, node, /):
854
+ return AerCast(_assemble_type(node.type), node.operand.accept(self))
855
+
856
+ def visit_unary(self, node, /):
857
+ return AerUnaryExpr(_assemble_unary_operator(node.op), node.operand.accept(self))
858
+
859
+ def visit_binary(self, node, /):
860
+ return AerBinaryExpr(
861
+ _assemble_binary_operator(node.op),
862
+ node.left.accept(self),
863
+ node.right.accept(self),
864
+ )
865
+
866
+ def visit_generic(self, node, /):
867
+ raise AerError(f"unsupported expression is used: {node.__class__}")
868
+
869
+
870
+ def _check_no_conditional(inst_name, conditional_reg):
871
+ if conditional_reg >= 0:
872
+ raise AerError(f"instruction {inst_name} does not support conditional")
873
+
874
+
875
+ def _assemble_op(
876
+ circ,
877
+ aer_circ,
878
+ inst,
879
+ qubit_indices,
880
+ clbit_indices,
881
+ is_conditional,
882
+ conditional_reg,
883
+ conditional_expr,
884
+ basis_gates,
885
+ ):
886
+ operation = inst.operation
887
+ qubits = [qubit_indices[qubit] for qubit in inst.qubits]
888
+ clbits = [clbit_indices[clbit] for clbit in inst.clbits]
889
+ name = operation.name
890
+ label = operation.label
891
+ params = operation.params if hasattr(operation, "params") else None
892
+ copied = False
893
+
894
+ for i, param in enumerate(params):
895
+ if isinstance(param, ParameterExpression) and len(param.parameters) > 0:
896
+ if not copied:
897
+ params = copy(params)
898
+ copied = True
899
+ params[i] = 0.0
900
+
901
+ aer_cond_expr = conditional_expr.accept(_AssembleExprImpl(circ)) if conditional_expr else None
902
+
903
+ # check if there is ctrl_state option
904
+ ctrl_state_pos = name.find("_o")
905
+ if ctrl_state_pos > 0:
906
+ gate_name = name[0:ctrl_state_pos]
907
+ else:
908
+ gate_name = name
909
+
910
+ num_of_aer_ops = 1
911
+ # fmt: off
912
+ if (gate_name in {
913
+ "ccx", "ccz", "cp", "cswap", "csx", "cx", "cy", "cz", "delay", "ecr", "h",
914
+ "id", "mcp", "mcphase", "mcr", "mcrx", "mcry", "mcrz", "mcswap", "mcsx",
915
+ "mcu", "mcu1", "mcu2", "mcu3", "mcx", "mcx_gray", "mcy", "mcz", "p", "r",
916
+ "rx", "rxx", "ry", "ryy", "rz", "rzx", "rzz", "s", "sdg", "swap", "sx", "sxdg",
917
+ "t", "tdg", "u", "x", "y", "z", "u1", "u2", "u3", "cu", "cu1", "cu2", "cu3",
918
+ "crx", "cry", "crz",
919
+ }) and (basis_gates is None or gate_name in basis_gates):
920
+ if ctrl_state_pos > 0:
921
+ # Add x gates for ctrl qubits which state=0
922
+ ctrl_state = int(name[ctrl_state_pos+2:len(name)])
923
+ for i in range(len(qubits)-1):
924
+ if (ctrl_state >> i) & 1 == 0:
925
+ qubits_i = [qubits[i]]
926
+ aer_circ.gate("x", qubits_i, params, [], conditional_reg, aer_cond_expr,
927
+ label if label else "x")
928
+ num_of_aer_ops += 1
929
+ aer_circ.gate(gate_name, qubits, params, [], conditional_reg, aer_cond_expr,
930
+ label if label else gate_name)
931
+ for i in range(len(qubits)-1):
932
+ if (ctrl_state >> i) & 1 == 0:
933
+ qubits_i = [qubits[i]]
934
+ aer_circ.gate("x", qubits_i, params, [], conditional_reg, aer_cond_expr,
935
+ label if label else "x")
936
+ num_of_aer_ops += 1
937
+ else:
938
+ aer_circ.gate(name, qubits, params, [], conditional_reg, aer_cond_expr,
939
+ label if label else name)
940
+ elif name == "measure":
941
+ if is_conditional:
942
+ aer_circ.measure(qubits, clbits, clbits)
943
+ else:
944
+ aer_circ.measure(qubits, clbits, [])
945
+ elif name == "reset":
946
+ aer_circ.reset(qubits, conditional_reg)
947
+ elif name == "diagonal":
948
+ aer_circ.diagonal(qubits, params, conditional_reg, label if label else "diagonal")
949
+ elif name == "unitary":
950
+ aer_circ.unitary(qubits, params[0], conditional_reg, aer_cond_expr,
951
+ label if label else "unitary")
952
+ elif name == "pauli":
953
+ aer_circ.gate(name, qubits, [], params, conditional_reg, aer_cond_expr,
954
+ label if label else name)
955
+ elif name == "initialize":
956
+ _check_no_conditional(name, conditional_reg)
957
+ aer_circ.initialize(qubits, params)
958
+ elif name == "roerror":
959
+ _check_no_conditional(name, conditional_reg)
960
+ aer_circ.roerror(qubits, params)
961
+ elif name == "multiplexer":
962
+ aer_circ.multiplexer(qubits, params, conditional_reg, aer_cond_expr, label if label else name)
963
+ elif name == "kraus":
964
+ aer_circ.kraus(qubits, params, conditional_reg, aer_cond_expr)
965
+ elif name in {
966
+ "save_statevector",
967
+ "save_statevector_dict",
968
+ "save_clifford",
969
+ "save_probabilities",
970
+ "save_probabilities_dict",
971
+ "save_matrix_product_state",
972
+ "save_unitary",
973
+ "save_superop",
974
+ "save_density_matrix",
975
+ "save_state",
976
+ "save_stabilizer",
977
+ }:
978
+ _check_no_conditional(name, conditional_reg)
979
+ aer_circ.save_state(qubits, name, operation._subtype, label if label else name)
980
+ elif name in {"save_amplitudes", "save_amplitudes_sq"}:
981
+ _check_no_conditional(name, conditional_reg)
982
+ aer_circ.save_amplitudes(qubits, name, params, operation._subtype, label if label else name)
983
+ elif name in ("save_expval", "save_expval_var"):
984
+ _check_no_conditional(name, conditional_reg)
985
+ paulis = []
986
+ coeff_reals = []
987
+ coeff_imags = []
988
+ for pauli, coeff in operation.params:
989
+ paulis.append(pauli)
990
+ coeff_reals.append(coeff[0])
991
+ coeff_imags.append(coeff[1])
992
+ aer_circ.save_expval(
993
+ qubits,
994
+ name,
995
+ paulis,
996
+ coeff_reals,
997
+ coeff_imags,
998
+ operation._subtype,
999
+ label if label else name,
1000
+ )
1001
+ elif name == "set_statevector":
1002
+ _check_no_conditional(name, conditional_reg)
1003
+ aer_circ.set_statevector(qubits, params)
1004
+ elif name == "set_unitary":
1005
+ _check_no_conditional(name, conditional_reg)
1006
+ aer_circ.set_unitary(qubits, params)
1007
+ elif name == "set_density_matrix":
1008
+ _check_no_conditional(name, conditional_reg)
1009
+ aer_circ.set_density_matrix(qubits, params)
1010
+ elif name == "set_stabilizer":
1011
+ _check_no_conditional(name, conditional_reg)
1012
+ aer_circ.set_clifford(qubits, params)
1013
+ elif name == "set_superop":
1014
+ _check_no_conditional(name, conditional_reg)
1015
+ aer_circ.set_superop(qubits, params)
1016
+ elif name == "set_matrix_product_state":
1017
+ _check_no_conditional(name, conditional_reg)
1018
+ aer_circ.set_matrix_product_state(qubits, params)
1019
+ elif name == "superop":
1020
+ aer_circ.superop(qubits, params[0], conditional_reg, aer_cond_expr)
1021
+ elif name == "barrier":
1022
+ _check_no_conditional(name, conditional_reg)
1023
+ num_of_aer_ops = 0
1024
+ elif name == "jump":
1025
+ aer_circ.jump(qubits, params, conditional_reg, aer_cond_expr)
1026
+ elif name == "mark":
1027
+ _check_no_conditional(name, conditional_reg)
1028
+ aer_circ.mark(qubits, params)
1029
+ elif name == "qerror_loc":
1030
+ aer_circ.set_qerror_loc(qubits, label if label else name, conditional_reg, aer_cond_expr)
1031
+ elif name in ("for_loop", "while_loop", "if_else"):
1032
+ raise AerError(
1033
+ "control-flow instructions must be converted " f"to jump and mark instructions: {name}"
1034
+ )
1035
+ elif name == "aer_store":
1036
+ if not isinstance(operation.store.lvalue, Var):
1037
+ raise AerError(f"unsupported lvalue : {operation.store.lvalue.__class__}")
1038
+ aer_circ.store(qubits, _assemble_clbit_indices(circ, operation.store.lvalue.var),
1039
+ operation.store.rvalue.accept(_AssembleExprImpl(circ)))
1040
+ num_of_aer_ops = 1
1041
+ elif name == "store":
1042
+ if not isinstance(operation.lvalue, Var):
1043
+ raise AerError(f"unsupported lvalue : {operation.lvalue.__class__}")
1044
+ aer_circ.store(qubits, _assemble_clbit_indices(circ, operation.lvalue.var),
1045
+ operation.rvalue.accept(_AssembleExprImpl(circ)))
1046
+ num_of_aer_ops = 1
1047
+ else:
1048
+ raise AerError(f"unknown instruction: {name}")
1049
+
1050
+ return num_of_aer_ops
1051
+
1052
+
1053
+ def assemble_circuits(circuits: List[QuantumCircuit], basis_gates: list = None) -> List[AerCircuit]:
1054
+ """converts a list of Qiskit circuits into circuits mapped AER::Circuit
1055
+
1056
+ Args:
1057
+ circuits: circuit(s) to be converted
1058
+ basis_gates (list): supported gates to be converted
1059
+
1060
+ Returns:
1061
+ a list of circuits to be run on the Aer backends and
1062
+ a list of index mapping from Qiskit instructions to Aer operations of the circuits
1063
+
1064
+ Examples:
1065
+
1066
+ .. code-block:: python
1067
+
1068
+ from qiskit.circuit import QuantumCircuit
1069
+ from qiskit_aer.backends.aer_compiler import assemble_circuits
1070
+ # Create a circuit to be simulated
1071
+ qc = QuantumCircuit(2, 2)
1072
+ qc.h(0)
1073
+ qc.cx(0, 1)
1074
+ qc.measure_all()
1075
+ # Generate AerCircuit from the input circuit
1076
+ aer_qc_list, idx_maps = assemble_circuits(circuits=[qc])
1077
+ """
1078
+ if basis_gates is not None:
1079
+ basis_gates_set = set(basis_gates)
1080
+ aer_circuits, idx_maps = zip(
1081
+ *[assemble_circuit(circuit, basis_gates_set) for circuit in circuits]
1082
+ )
1083
+ else:
1084
+ aer_circuits, idx_maps = zip(*[assemble_circuit(circuit) for circuit in circuits])
1085
+ return list(aer_circuits), list(idx_maps)