From fc78d93d294a854cd3a4c967936b892464bc4b8c Mon Sep 17 00:00:00 2001 From: Jun Doi Date: Thu, 11 Apr 2024 16:55:57 +0900 Subject: [PATCH] Fix AerCompiler to use custom pass manager to decompose control flow ops (#2095) * Fix AerCompiler to use basis_gates built from input circuit * use custom pass manager to decompose dontrol flow ops * remove unused import --- qiskit_aer/backends/aer_compiler.py | 54 +++++++++---------- qiskit_aer/backends/aerbackend.py | 4 +- ...ise_dynamic_circuits-59c6cf0061e956a8.yaml | 9 ++++ 3 files changed, 35 insertions(+), 32 deletions(-) create mode 100644 releasenotes/notes/fix_noise_dynamic_circuits-59c6cf0061e956a8.yaml diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index bbb2e18a25..3d1965e25f 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -36,7 +36,10 @@ SwitchCaseOp, CASE_DEFAULT, ) -from qiskit.compiler import transpile +from qiskit.transpiler import PassManager +from qiskit.transpiler.passes import Decompose + + from qiskit.qobj import QobjExperimentHeader from qiskit_aer.aererror import AerError from qiskit_aer.noise import NoiseModel @@ -67,13 +70,11 @@ class AerCompiler: def __init__(self): self._last_flow_id = -1 - def compile(self, circuits, basis_gates=None, optypes=None): + def compile(self, circuits, optypes=None): """compile a circuit that have control-flow instructions. Args: circuits (QuantumCircuit or list): The QuantumCircuits to be compiled - basis_gates (list): basis gates to decompose sub-circuits - (default: None). optypes (list): list of instruction type sets for each circuit (default: None). @@ -92,17 +93,14 @@ def compile(self, circuits, basis_gates=None, optypes=None): # Make a shallow copy incase we modify it compiled_optypes = list(optypes) if isinstance(circuits, list): - basis_gates = basis_gates + ["mark", "jump"] compiled_circuits = [] for idx, circuit in enumerate(circuits): # Resolve initialize circuit = self._inline_initialize(circuit, compiled_optypes[idx]) if self._is_dynamic(circuit, compiled_optypes[idx]): - compiled_circ = transpile( - self._inline_circuit(circuit, None, None), - basis_gates=basis_gates, - optimization_level=0, - ) + pm = PassManager([Decompose(["mark", "jump"])]) + compiled_circ = pm.run(self._inline_circuit(circuit, None, None)) + compiled_circuits.append(compiled_circ) # Recompute optype for compiled circuit compiled_optypes[idx] = circuit_optypes(compiled_circ) @@ -214,7 +212,6 @@ def _inline_circuit(self, circ, continue_label, break_label, bit_map=None): ) else: ret._append(instruction) - return ret def _convert_jump_conditional(self, cond_tuple, bit_map): @@ -272,7 +269,9 @@ def _inline_for_loop_op(self, instruction, parent, bit_map): inlined_body = self._inline_circuit(body, continue_label, break_label, inner_bit_map) if loop_parameter is not None: inlined_body = inlined_body.assign_parameters({loop_parameter: index}) - parent.append(inlined_body, qargs, cargs) + # parent.append(inlined_body, qargs, cargs) + for inst in inlined_body: + parent.append(inst, qargs, cargs) parent.append(AerMark(continue_label, len(qargs), len(cargs)), qargs, cargs) if inlined_body is not None: @@ -323,7 +322,8 @@ def _inline_while_loop_op(self, instruction, parent, bit_map): ) parent.append(AerJump(break_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(AerMark(loop_start_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) - parent.append(inlined_body, qargs, cargs) + for inst in inlined_body: + parent.append(inst, qargs, cargs) parent.append(AerJump(continue_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(AerMark(break_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) @@ -371,9 +371,9 @@ def _inline_if_else_op(self, instruction, continue_label, break_label, parent, b ) parent.append(AerJump(if_else_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(AerMark(if_true_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) - parent.append( - self._inline_circuit(true_body, continue_label, break_label, true_bit_map), qargs, cargs - ) + child = self._inline_circuit(true_body, continue_label, break_label, true_bit_map) + for inst in child.data: + parent.append(inst, qargs, cargs) if false_body: false_bit_map = { @@ -385,11 +385,9 @@ def _inline_if_else_op(self, instruction, continue_label, break_label, parent, b } parent.append(AerJump(if_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(AerMark(if_else_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) - parent.append( - self._inline_circuit(false_body, continue_label, break_label, false_bit_map), - qargs, - cargs, - ) + child = self._inline_circuit(false_body, continue_label, break_label, false_bit_map) + for inst in child.data: + parent.append(inst, qargs, cargs) parent.append(AerMark(if_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) @@ -468,23 +466,21 @@ def _inline_switch_case_op(self, instruction, continue_label, break_label, paren for case_data in case_data_list: parent.append(AerMark(case_data.label, len(qargs), len(mark_cargs)), qargs, mark_cargs) - parent.append( - self._inline_circuit( - case_data.body, continue_label, break_label, case_data.bit_map - ), - qargs, - cargs, + child = self._inline_circuit( + case_data.body, continue_label, break_label, case_data.bit_map ) + for inst in child.data: + parent.append(inst, qargs, cargs) parent.append(AerJump(switch_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) parent.append(AerMark(switch_end_label, len(qargs), len(mark_cargs)), qargs, mark_cargs) -def compile_circuit(circuits, basis_gates=None, optypes=None): +def compile_circuit(circuits, optypes=None): """ compile a circuit that have control-flow instructions """ - return AerCompiler().compile(circuits, basis_gates, optypes) + return AerCompiler().compile(circuits, optypes) BACKEND_RUN_ARG_TYPES = { diff --git a/qiskit_aer/backends/aerbackend.py b/qiskit_aer/backends/aerbackend.py index bc95a45de6..58ca3d3d7a 100644 --- a/qiskit_aer/backends/aerbackend.py +++ b/qiskit_aer/backends/aerbackend.py @@ -517,9 +517,7 @@ def _compile(self, circuits, **run_options): optypes = [circuit_optypes(circ) for circ in circuits] # Compile Qasm3 instructions - circuits, optypes = compile_circuit( - circuits, basis_gates=self.configuration().basis_gates, optypes=optypes - ) + circuits, optypes = compile_circuit(circuits, optypes=optypes) # run option noise model circuits, noise_model, run_options = self._assemble_noise_model( diff --git a/releasenotes/notes/fix_noise_dynamic_circuits-59c6cf0061e956a8.yaml b/releasenotes/notes/fix_noise_dynamic_circuits-59c6cf0061e956a8.yaml new file mode 100644 index 0000000000..c302bcaff3 --- /dev/null +++ b/releasenotes/notes/fix_noise_dynamic_circuits-59c6cf0061e956a8.yaml @@ -0,0 +1,9 @@ +--- +fixes: + - | + When the circuit is a dynamic circuit, the input circuit was not correctly + transpiled in AerCompiler that causes wrong noise simulation result, + because some gates noise to be applied were transpiled to other gates. + + This fix uses custom pass manager to decompose only jump and mark ops + Aer uses internally.