diff --git a/qiskit/transpiler/passes/routing/stochastic_swap.py b/qiskit/transpiler/passes/routing/stochastic_swap.py index 2fb8ca217a56..d4738f4b3f1f 100644 --- a/qiskit/transpiler/passes/routing/stochastic_swap.py +++ b/qiskit/transpiler/passes/routing/stochastic_swap.py @@ -384,7 +384,7 @@ def _transpile_controlflow_multiblock(self, cf_op, current_layout): for i, block in enumerate(cf_op.blocks): dag_block = circuit_to_dag(block) - _pass = self.__class__(new_coupling, initial_layout=None, seed=self.seed) + _pass = self.__class__(new_coupling, initial_layout=None, seed=(i + 1) * self.seed) updated_dag_block = _pass.run(dag_block) block_dags.append(updated_dag_block) block_layouts.append(_pass.property_set["final_layout"].copy()) @@ -401,6 +401,7 @@ def _transpile_controlflow_multiblock(self, cf_op, current_layout): new_coupling, block_layouts[i], block_layouts[maxind], + seed = i*self.seed, ) match_dag = layout_xform.run(dag) block_circuits.append(dag_to_circuit(match_dag)) @@ -422,12 +423,13 @@ def _transpile_controlflow_looping(self, cf_op, current_layout): new_coupling = layout_transform(self.coupling_map, current_layout) dag_block = circuit_to_dag(cf_op.blocks[0]) - _pass = self.__class__(new_coupling, initial_layout=None, seed=self.seed) + _pass = self.__class__(new_coupling, initial_layout=None, seed=2 * self.seed) start_qreg = QuantumRegister(len(self._qubit_indices), "q") start_layout = Layout.generate_trivial_layout(start_qreg) updated_dag_block = _pass.run(dag_block) updated_layout = _pass.property_set["final_layout"].copy() - layout_xform = LayoutTransformation(new_coupling, updated_layout, start_layout) + layout_xform = LayoutTransformation(new_coupling, updated_layout, + start_layout, seed=self.seed) match_dag = layout_xform.run(updated_dag_block) match_circ = dag_to_circuit(match_dag) return cf_op.replace_blocks([match_circ]), current_layout diff --git a/qiskit/transpiler/passes/utils/check_map.py b/qiskit/transpiler/passes/utils/check_map.py index 809129ecb86a..ea476a846908 100644 --- a/qiskit/transpiler/passes/utils/check_map.py +++ b/qiskit/transpiler/passes/utils/check_map.py @@ -13,6 +13,7 @@ """Check if a DAG circuit is already mapped to a coupling map.""" from qiskit.transpiler.basepasses import AnalysisPass +from qiskit.circuit.controlflow import ControlFlowOp class CheckMap(AnalysisPass): @@ -41,6 +42,7 @@ def run(self, dag): Args: dag (DAGCircuit): DAG to map. """ + from qiskit.converters import circuit_to_dag self.property_set["is_swap_mapped"] = True if self.coupling_map is None or len(self.coupling_map.graph) == 0: @@ -64,3 +66,7 @@ def run(self, dag): ) self.property_set["is_swap_mapped"] = False return + for cf_instr in dag.op_nodes(op=ControlFlowOp): + for block in cf_instr.op.blocks: + dag_block = circuit_to_dag(block) + self.run(dag_block) diff --git a/test/python/transpiler/test_check_map.py b/test/python/transpiler/test_check_map.py index bf5245fac0dc..2fd008405091 100644 --- a/test/python/transpiler/test_check_map.py +++ b/test/python/transpiler/test_check_map.py @@ -14,7 +14,7 @@ import unittest -from qiskit import QuantumRegister, QuantumCircuit +from qiskit import QuantumRegister, QuantumCircuit, ClassicalRegister from qiskit.transpiler.passes import CheckMap from qiskit.transpiler import CouplingMap from qiskit.converters import circuit_to_dag @@ -85,6 +85,38 @@ def test_swap_mapped_false(self): self.assertFalse(pass_.property_set["is_swap_mapped"]) + def test_swap_mapped_cf_true(self): + """Check control flow blocks are mapped.""" + num_qubits = 3 + coupling = CouplingMap([(i, i + 1) for i in range(num_qubits - 1)]) + qr = QuantumRegister(3) + cr = ClassicalRegister(3) + circuit = QuantumCircuit(qr, cr) + true_body = QuantumCircuit(qr) + true_body.swap(0, 1) + true_body.cx(2, 1) + circuit.if_else((cr[0], 0), true_body, None, qr, cr) + dag = circuit_to_dag(circuit) + pass_ = CheckMap(coupling) + pass_.run(dag) + self.assertTrue(pass_.property_set["is_swap_mapped"]) + + def test_swap_mapped_cf_false(self): + """Check control flow blocks are mapped.""" + num_qubits = 3 + coupling = CouplingMap([(i, i + 1) for i in range(num_qubits - 1)]) + qr = QuantumRegister(3) + cr = ClassicalRegister(3) + circuit = QuantumCircuit(qr, cr) + true_body = QuantumCircuit(qr) + true_body.cx(0, 2) + circuit.if_else((cr[0], 0), true_body, None, qr, cr) + dag = circuit_to_dag(circuit) + pass_ = CheckMap(coupling) + pass_.run(dag) + self.assertFalse(pass_.property_set["is_swap_mapped"]) + + if __name__ == "__main__": unittest.main() diff --git a/test/python/transpiler/test_stochastic_swap.py b/test/python/transpiler/test_stochastic_swap.py index 5245907a4c5a..9ff3c61b4f99 100644 --- a/test/python/transpiler/test_stochastic_swap.py +++ b/test/python/transpiler/test_stochastic_swap.py @@ -20,6 +20,7 @@ from qiskit.converters import circuit_to_dag, dag_to_circuit from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit from qiskit.test import QiskitTestCase +from qiskit.transpiler.passes.utils import CheckMap from qiskit.quantum_info.analysis.distance import hellinger_distance from qiskit.utils import optionals @@ -928,10 +929,8 @@ def test_controlflow_no_layout_change(self): expected.measure(qreg, creg[[0, 2, 1, 3, 4]]) self.assertEqual(cqc, expected) - @unittest.skipUnless(optionals.HAS_AER, "Qiskit Aer is required to run this test") - #@data(1, 2, 3, 4, 5) - #def test_controlflow_for_loop(self, nloops): - def test_controlflow_for_loop(self): + @data(1, 2, 3) + def test_controlflow_for_loop(self, nloops): nloops=1 """test for loop""" num_qubits = 3 @@ -945,56 +944,37 @@ def test_controlflow_for_loop(self): for_body.cx(0, 2) loop_parameter = None qc.for_loop(range(nloops), loop_parameter, for_body, qreg, creg) - qc.barrier() qc.measure(qreg, creg) dag = circuit_to_dag(qc) cdag = StochasticSwap(coupling, seed=687).run(dag) cqc = dag_to_circuit(cdag) - sim = Aer.get_backend("aer_simulator") - in_results = sim.run(qc, shots=4096, seed_simulator=10).result().get_counts() - out_results = sim.run(cqc, shots=4096, seed_simulator=11).result().get_counts() expected = QuantumCircuit(qreg, creg) expected.h(0) expected.x(1) efor_body = QuantumCircuit(qreg, creg) - efor_body.swap(1, 2) - efor_body.cx(0, 1) - efor_body.swap(1, 2) + efor_body.swap(0, 1) + efor_body.cx(1, 2) + efor_body.swap(0, 1) loop_parameter = None expected.for_loop(range(nloops), loop_parameter, efor_body, qreg, creg) - expected.barrier() - expected.measure(qreg, creg) - print('') - print(cqc) - print(cqc.data[2].operation.params[2]) - print(expected) - print(expected.data[2].operation.params[2]) - self.assertLess(hellinger_distance(in_results, out_results), 0.01) - for instr in cqc.data[2].operation.params[2] - print(node.name) - if node.op.name == 'swap': - print(repr(node)) - - #self.assertEqual(cqc, expected) + expected.measure(qreg, creg) + self.assertEqual(cqc, expected) - @unittest.skipUnless(optionals.HAS_AER, "Qiskit Aer is required to run this test") def test_controlflow_while_loop(self): """test while loop""" - from qiskit.circuit.library.standard_gates import CCXGate - - shots = 100 num_qubits = 4 qreg = QuantumRegister(num_qubits, "q") creg = ClassicalRegister(len(qreg)) coupling = CouplingMap([(i, i + 1) for i in range(num_qubits - 1)]) + check_map_pass = CheckMap(coupling) qc = QuantumCircuit(qreg, creg) while_body = QuantumCircuit(qreg, creg) while_body.reset(qreg[2:]) while_body.h(qreg[2:]) - while_body.compose(CCXGate().definition, [2, 3, 0], inplace=True) - while_body.measure(qreg[0], creg[0]) + while_body.cx(0, 3) + while_body.measure(qreg[3], creg[3]) qc.while_loop((creg, 0), while_body, qc.qubits, qc.clbits) qc.barrier() qc.measure(qreg, creg) @@ -1002,16 +982,28 @@ def test_controlflow_while_loop(self): dag = circuit_to_dag(qc) cdag = StochasticSwap(coupling, seed=58).run(dag) cqc = dag_to_circuit(cdag) - sim = Aer.get_backend("aer_simulator") - in_results = sim.run(qc, shots=shots, seed_simulator=10).result().get_counts() - out_results = sim.run(cqc, shots=shots, seed_simulator=11).result().get_counts() - self.assertLess(hellinger_distance(in_results, out_results), 0.01) + expected = QuantumCircuit(qreg, creg) + ewhile_body = QuantumCircuit(qreg, creg) + ewhile_body.reset(qreg[2:]) + ewhile_body.h(qreg[2:]) + ewhile_body.swap(0, 1) + ewhile_body.swap(2, 3) + ewhile_body.cx(1, 2) + ewhile_body.measure(qreg[2], creg[3]) + ewhile_body.swap(1, 0) + ewhile_body.swap(3, 2) + expected.while_loop((creg, 0), ewhile_body, expected.qubits, expected.clbits) + expected.barrier() + expected.measure(qreg, creg) + check_map_pass.run(circuit_to_dag(expected)) + self.assertEqual(cqc, expected) + self.assertTrue(check_map_pass.property_set["is_swap_mapped"]) @unittest.skipUnless(optionals.HAS_AER, "Qiskit Aer is required to run this test") @data(392, 2023, 45, 27, 68) def test_controlflow_nested_inner_cnot(self, seed): """test swap in nested if else controlflow construct; swap in inner""" - num_qubits = 5 + num_qubits = 3 qreg = QuantumRegister(num_qubits, "q") creg = ClassicalRegister(num_qubits) coupling = CouplingMap([(i, i + 1) for i in range(num_qubits - 1)]) @@ -1024,24 +1016,51 @@ def test_controlflow_nested_inner_cnot(self, seed): for_body = QuantumCircuit(qreg, creg) for_body.delay(10, 0) - for_body.cx(1, 3) + for_body.cx(0, 2) loop_parameter = None true_body.for_loop(range(3), loop_parameter, for_body, qreg, creg) false_body = QuantumCircuit(qreg, creg) false_body.y(0) qc.if_else((creg[0], 0), true_body, false_body, qreg, creg) - qc.barrier() qc.measure(qreg, creg) dag = circuit_to_dag(qc) cdag = StochasticSwap(coupling, seed=seed).run(dag) cqc = dag_to_circuit(cdag) + expected = QuantumCircuit(qreg, creg) + expected.h(0) + expected.x(1) + expected.measure(0, 0) + etrue_body = QuantumCircuit(qreg, creg) + etrue_body.x(0) + + efor_body = QuantumCircuit(qreg, creg) + efor_body.delay(10, 0) + efor_body.swap(0, 1) + efor_body.cx(1, 2) + efor_body.swap(1, 0) + etrue_body.for_loop(range(3), loop_parameter, efor_body, qreg, creg) + + efalse_body = QuantumCircuit(qreg, creg) + efalse_body.y(0) + expected.if_else((creg[0], 0), etrue_body, efalse_body, qreg, creg) + expected.measure(qreg, creg) + print('') + print(cqc) + print(expected) + print(cqc.data[3].operation.params[0]) + print(expected.data[3].operation.params[0]) + print(cqc.data[3].operation.params[0].data[1].operation.params[2]) + print(expected.data[3].operation.params[0].data[1].operation.params[2]) + breakpoint() sim = Aer.get_backend("aer_simulator") in_results = sim.run(qc, shots=4096, seed_simulator=10).result().get_counts() out_results = sim.run(cqc, shots=4096, seed_simulator=11).result().get_counts() self.assertLess(hellinger_distance(in_results, out_results), 0.01) + check_map_pass.run(circuit_to_dag(expected)) + @unittest.skipUnless(optionals.HAS_AER, "Qiskit Aer is required to run this test") @data(392, 2023, 45, 27, 68)