From 8586ef122b2fa8543b65db1a5a37cf48293f2905 Mon Sep 17 00:00:00 2001 From: Gadi Aleksandrowicz Date: Sun, 6 Oct 2024 15:58:31 +0300 Subject: [PATCH 1/3] Fixes a bug causing infinite run when using conditional with empty body --- qiskit_aer/backends/aer_compiler.py | 16 ++++++++++++++ .../aer_simulator/test_conditional.py | 21 ++++++++++++++++++- 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/qiskit_aer/backends/aer_compiler.py b/qiskit_aer/backends/aer_compiler.py index a0c75247cc..846eb10052 100644 --- a/qiskit_aer/backends/aer_compiler.py +++ b/qiskit_aer/backends/aer_compiler.py @@ -259,6 +259,9 @@ def _inline_for_loop_op(self, instruction, parent, bit_map): """inline for_loop body while iterating its indexset""" qargs = [bit_map[q] for q in instruction.qubits] cargs = [bit_map[c] for c in instruction.clbits] + # to avoid wrong topological sorting of command with "empty" block + if len(qargs) == 0: + qargs = parent.qubits indexset, loop_parameter, body = instruction.operation.params inner_bit_map = { inner: bit_map[outer] @@ -320,6 +323,9 @@ def _inline_while_loop_op(self, instruction, parent, bit_map): ) qargs = [bit_map[q] for q in instruction.qubits] cargs = [bit_map[c] for c in instruction.clbits] + # to avoid wrong topological sorting of command with "empty" block + if len(qargs) == 0: + qargs = parent.qubits if isinstance(condition_tuple, Expr): mark_cargs = self._list_clbit_from_expr(bit_map, condition_tuple) @@ -371,6 +377,13 @@ def _inline_if_else_op(self, instruction, continue_label, break_label, parent, b qargs = [bit_map[q] for q in instruction.qubits] cargs = [bit_map[c] for c in instruction.clbits] + # to avoid wrong topological sorting of command with "empty" block + if len(qargs) == 0: + qargs = parent.qubits + + # to avoid wrong topological sorting of command with "empty" block + if len(qargs) == 0: + qargs = parent.qubits if isinstance(condition_tuple, Expr): mark_cargs = self._list_clbit_from_expr(bit_map, condition_tuple) @@ -439,6 +452,9 @@ def _inline_switch_case_op(self, instruction, continue_label, break_label, paren qargs = [bit_map[q] for q in instruction.qubits] cargs = [bit_map[c] for c in instruction.clbits] + # to avoid wrong topological sorting of command with "empty" block + if len(qargs) == 0: + qargs = parent.qubits if isinstance(instruction.operation.target, Clbit): target_clbits = {bit_map[instruction.operation.target]} diff --git a/test/terra/backends/aer_simulator/test_conditional.py b/test/terra/backends/aer_simulator/test_conditional.py index ec33fd404b..e374299675 100644 --- a/test/terra/backends/aer_simulator/test_conditional.py +++ b/test/terra/backends/aer_simulator/test_conditional.py @@ -16,7 +16,7 @@ from test.terra.reference import ref_conditionals from test.terra.backends.simulator_test_case import SimulatorTestCase, supported_methods -from qiskit import QuantumCircuit +from qiskit import QuantumCircuit, ClassicalRegister from qiskit.circuit.library import DiagonalGate @@ -380,3 +380,22 @@ def test_conditional_diagonal(self): self.assertNotEqual(result.data(circuit)["base"], result.data(circuit0)["diff"]) self.assertEqual(result.data(circuit)["base"], result.data(circuit1)["equal"]) + +class TestConditionalErrors(SimulatorTestCase): + def test_infinite_run_error(self): + backend = self.backend(method="statevector", device="CPU") + backend.set_options(max_parallel_experiments=0) + + main_circ = QuantumCircuit(1) + creg_0 = ClassicalRegister(1) + main_circ.add_register(creg_0) + main_circ.measure(0, creg_0[0]) + main_circ.x(0) + with main_circ.if_test((creg_0[0],0)) as else_1: + pass + with else_1: + pass + main_circ.measure_active() + + result = backend.run(main_circ, shots=1).result() + self.assertSuccess(result) From cd57c53128960599ab51071dedfa09d599dbaa04 Mon Sep 17 00:00:00 2001 From: Gadi Aleksandrowicz Date: Sun, 6 Oct 2024 16:19:42 +0300 Subject: [PATCH 2/3] Linting --- test/terra/backends/aer_simulator/test_conditional.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/test/terra/backends/aer_simulator/test_conditional.py b/test/terra/backends/aer_simulator/test_conditional.py index e374299675..56dc936781 100644 --- a/test/terra/backends/aer_simulator/test_conditional.py +++ b/test/terra/backends/aer_simulator/test_conditional.py @@ -381,6 +381,7 @@ def test_conditional_diagonal(self): self.assertNotEqual(result.data(circuit)["base"], result.data(circuit0)["diff"]) self.assertEqual(result.data(circuit)["base"], result.data(circuit1)["equal"]) + class TestConditionalErrors(SimulatorTestCase): def test_infinite_run_error(self): backend = self.backend(method="statevector", device="CPU") @@ -391,7 +392,7 @@ def test_infinite_run_error(self): main_circ.add_register(creg_0) main_circ.measure(0, creg_0[0]) main_circ.x(0) - with main_circ.if_test((creg_0[0],0)) as else_1: + with main_circ.if_test((creg_0[0], 0)) as else_1: pass with else_1: pass From 471efbcc7b89cd013a47e0d88a44ce1703adce65 Mon Sep 17 00:00:00 2001 From: Gadi Aleksandrowicz Date: Tue, 29 Oct 2024 15:21:23 +0200 Subject: [PATCH 3/3] Release note --- ...infinite_run_on_empty_conditional-d9e6d31ffa1c3bc3.yaml | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 releasenotes/notes/fix_infinite_run_on_empty_conditional-d9e6d31ffa1c3bc3.yaml diff --git a/releasenotes/notes/fix_infinite_run_on_empty_conditional-d9e6d31ffa1c3bc3.yaml b/releasenotes/notes/fix_infinite_run_on_empty_conditional-d9e6d31ffa1c3bc3.yaml new file mode 100644 index 0000000000..10bcad90b8 --- /dev/null +++ b/releasenotes/notes/fix_infinite_run_on_empty_conditional-d9e6d31ffa1c3bc3.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Using a conditional with empty body caused the transpile run to + mix the order of gates in the circuit, leading sometimes to infinite + simulations. + This fix ensures that the order of gates is preserved.