Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add control flow support to UnitarySynthesis pass #8565

Merged
merged 5 commits into from
Sep 29, 2022
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
from qiskit.circuit.gate import Gate
from qiskit.circuit.instruction import Instruction
from qiskit.circuit.parameterexpression import ParameterExpression
from qiskit.circuit.controlflow import ControlFlowOp
from qiskit.dagcircuit.exceptions import DAGCircuitError
from qiskit.dagcircuit.dagnode import DAGNode, DAGOpNode, DAGInNode, DAGOutNode
from qiskit.utils import optionals as _optionals
Expand Down Expand Up @@ -1438,6 +1439,10 @@ def multi_qubit_ops(self):
ops.append(node)
return ops

def control_flow_ops(self):
"""return control flow operations"""
return self.op_nodes(op=ControlFlowOp)
ewinston marked this conversation as resolved.
Show resolved Hide resolved

def longest_path(self):
"""Returns the longest path in the dag as a list of DAGOpNodes, DAGInNodes, and DAGOutNodes."""
return [self._multi_graph[x] for x in rx.dag_longest_path(self._multi_graph)]
Expand Down
10 changes: 9 additions & 1 deletion qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
from copy import deepcopy
from itertools import product

from qiskit.converters import circuit_to_dag
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.transpiler import CouplingMap, Target
from qiskit.transpiler.basepasses import TransformationPass
from qiskit.transpiler.exceptions import TranspilerError
Expand Down Expand Up @@ -331,6 +331,14 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
dag.substitute_node_with_dag(node, synth_dag[0], wires=synth_dag[1])
else:
dag.substitute_node_with_dag(node, synth_dag)
for node in dag.control_flow_ops():
updated_blocks = []
for block in node.op.blocks:
dag_block = circuit_to_dag(block)
updated_dag_block = self.run(dag_block)
updated_circ_block = dag_to_circuit(updated_dag_block)
updated_blocks.append(updated_circ_block)
node.op = node.op.replace_blocks(updated_blocks)
ewinston marked this conversation as resolved.
Show resolved Hide resolved
return dag


Expand Down
46 changes: 44 additions & 2 deletions test/python/transpiler/test_unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@
from qiskit.test import QiskitTestCase
from qiskit.providers.fake_provider import FakeVigo, FakeMumbaiFractionalCX
from qiskit.providers.fake_provider.fake_backend_v2 import FakeBackendV2, FakeBackend5QV2
from qiskit.circuit import QuantumCircuit, QuantumRegister
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.library import QuantumVolume
from qiskit.converters import circuit_to_dag
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.transpiler.passes import UnitarySynthesis
from qiskit.quantum_info.operators import Operator
from qiskit.quantum_info.random import random_unitary
Expand Down Expand Up @@ -724,6 +724,48 @@ def test_reverse_direction(self, opt_level):
for instr in tqc.get_instructions("ecr"):
self.assertEqual((0, 1), (tqc_index[instr.qubits[0]], tqc_index[instr.qubits[1]]))

def test_if_simple(self):
"""Test a simple if statement."""
basis_gates = {"u", "cx"}
qr = QuantumRegister(2)
cr = ClassicalRegister(2)

qc_uni = QuantumCircuit(2)
qc_uni.h(0)
qc_uni.cx(0, 1)
qc_uni_mat = Operator(qc_uni)

qc_true_body = QuantumCircuit(2)
qc_true_body.unitary(qc_uni_mat, [0, 1])

qc = QuantumCircuit(qr, cr)
qc.if_test((cr, 1), qc_true_body, [0, 1], [0, 1])
dag = circuit_to_dag(qc)
cdag = UnitarySynthesis(basis_gates=basis_gates).run(dag)
cqc = dag_to_circuit(cdag)
cbody = cqc.data[0].operation.params[0]
self.assertEqual(cbody.count_ops().keys(), basis_gates)
self.assertEqual(qc_uni_mat, Operator(cbody))

def test_nested_control_flow(self):
"""Test unrolling nested control flow blocks."""
qr = QuantumRegister(2)
cr = ClassicalRegister(1)
qc_uni1 = QuantumCircuit(2)
qc_uni1.swap(0, 1)
qc_uni1_mat = Operator(qc_uni1)

qc = QuantumCircuit(qr, cr)
with qc.for_loop(range(3)):
with qc.while_loop((cr, 0)):
qc.unitary(qc_uni1_mat, [0, 1])
dag = circuit_to_dag(qc)
cdag = UnitarySynthesis(basis_gates=["u", "cx"]).run(dag)
cqc = dag_to_circuit(cdag)
cbody = cqc.data[0].operation.params[2].data[0].operation.params[0]
self.assertEqual(cbody.count_ops().keys(), {"u", "cx"})
self.assertEqual(qc_uni1_mat, Operator(cbody))


if __name__ == "__main__":
unittest.main()