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.
- qiskit_aer/VERSION.txt +1 -0
- qiskit_aer/__init__.py +89 -0
- qiskit_aer/aererror.py +30 -0
- qiskit_aer/aerprovider.py +119 -0
- qiskit_aer/backends/__init__.py +20 -0
- qiskit_aer/backends/aer_compiler.py +1085 -0
- qiskit_aer/backends/aer_simulator.py +1025 -0
- qiskit_aer/backends/aerbackend.py +679 -0
- qiskit_aer/backends/backend_utils.py +567 -0
- qiskit_aer/backends/backendconfiguration.py +395 -0
- qiskit_aer/backends/backendproperties.py +590 -0
- qiskit_aer/backends/compatibility.py +287 -0
- qiskit_aer/backends/controller_wrappers.cpython-314-darwin.so +0 -0
- qiskit_aer/backends/libomp.dylib +0 -0
- qiskit_aer/backends/name_mapping.py +306 -0
- qiskit_aer/backends/qasm_simulator.py +925 -0
- qiskit_aer/backends/statevector_simulator.py +330 -0
- qiskit_aer/backends/unitary_simulator.py +316 -0
- qiskit_aer/jobs/__init__.py +35 -0
- qiskit_aer/jobs/aerjob.py +143 -0
- qiskit_aer/jobs/utils.py +66 -0
- qiskit_aer/library/__init__.py +204 -0
- qiskit_aer/library/control_flow_instructions/__init__.py +16 -0
- qiskit_aer/library/control_flow_instructions/jump.py +47 -0
- qiskit_aer/library/control_flow_instructions/mark.py +30 -0
- qiskit_aer/library/control_flow_instructions/store.py +29 -0
- qiskit_aer/library/default_qubits.py +44 -0
- qiskit_aer/library/instructions_table.csv +21 -0
- qiskit_aer/library/save_instructions/__init__.py +44 -0
- qiskit_aer/library/save_instructions/save_amplitudes.py +168 -0
- qiskit_aer/library/save_instructions/save_clifford.py +63 -0
- qiskit_aer/library/save_instructions/save_data.py +129 -0
- qiskit_aer/library/save_instructions/save_density_matrix.py +91 -0
- qiskit_aer/library/save_instructions/save_expectation_value.py +257 -0
- qiskit_aer/library/save_instructions/save_matrix_product_state.py +71 -0
- qiskit_aer/library/save_instructions/save_probabilities.py +156 -0
- qiskit_aer/library/save_instructions/save_stabilizer.py +70 -0
- qiskit_aer/library/save_instructions/save_state.py +79 -0
- qiskit_aer/library/save_instructions/save_statevector.py +120 -0
- qiskit_aer/library/save_instructions/save_superop.py +62 -0
- qiskit_aer/library/save_instructions/save_unitary.py +63 -0
- qiskit_aer/library/set_instructions/__init__.py +19 -0
- qiskit_aer/library/set_instructions/set_density_matrix.py +78 -0
- qiskit_aer/library/set_instructions/set_matrix_product_state.py +83 -0
- qiskit_aer/library/set_instructions/set_stabilizer.py +77 -0
- qiskit_aer/library/set_instructions/set_statevector.py +78 -0
- qiskit_aer/library/set_instructions/set_superop.py +78 -0
- qiskit_aer/library/set_instructions/set_unitary.py +78 -0
- qiskit_aer/noise/__init__.py +265 -0
- qiskit_aer/noise/device/__init__.py +25 -0
- qiskit_aer/noise/device/models.py +397 -0
- qiskit_aer/noise/device/parameters.py +202 -0
- qiskit_aer/noise/errors/__init__.py +30 -0
- qiskit_aer/noise/errors/base_quantum_error.py +119 -0
- qiskit_aer/noise/errors/pauli_error.py +283 -0
- qiskit_aer/noise/errors/pauli_lindblad_error.py +363 -0
- qiskit_aer/noise/errors/quantum_error.py +451 -0
- qiskit_aer/noise/errors/readout_error.py +355 -0
- qiskit_aer/noise/errors/standard_errors.py +498 -0
- qiskit_aer/noise/noise_model.py +1231 -0
- qiskit_aer/noise/noiseerror.py +30 -0
- qiskit_aer/noise/passes/__init__.py +18 -0
- qiskit_aer/noise/passes/local_noise_pass.py +160 -0
- qiskit_aer/noise/passes/relaxation_noise_pass.py +137 -0
- qiskit_aer/primitives/__init__.py +44 -0
- qiskit_aer/primitives/estimator.py +751 -0
- qiskit_aer/primitives/estimator_v2.py +159 -0
- qiskit_aer/primitives/sampler.py +361 -0
- qiskit_aer/primitives/sampler_v2.py +256 -0
- qiskit_aer/quantum_info/__init__.py +32 -0
- qiskit_aer/quantum_info/states/__init__.py +16 -0
- qiskit_aer/quantum_info/states/aer_densitymatrix.py +313 -0
- qiskit_aer/quantum_info/states/aer_state.py +525 -0
- qiskit_aer/quantum_info/states/aer_statevector.py +302 -0
- qiskit_aer/utils/__init__.py +44 -0
- qiskit_aer/utils/noise_model_inserter.py +66 -0
- qiskit_aer/utils/noise_transformation.py +431 -0
- qiskit_aer/version.py +86 -0
- qiskit_aer-0.17.2.dist-info/METADATA +209 -0
- qiskit_aer-0.17.2.dist-info/RECORD +83 -0
- qiskit_aer-0.17.2.dist-info/WHEEL +6 -0
- qiskit_aer-0.17.2.dist-info/licenses/LICENSE.txt +203 -0
- 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)
|