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

Unroll 3-or-moreQ pass #1614

Merged
merged 6 commits into from
Jan 24, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
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: 3 additions & 2 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ The format is based on `Keep a Changelog`_.
Added
-----

- New EnlargeWithAncilla pass for adding ancilla qubits after a Layout
selection pass (#1603).
- New Unroll2Q pass for unrolling gates down to just 1q or 2q gates (#1614).
- Added a RunConfig object for configurations for run configurations to be used
in compile and circuits_to_qobj. (#1629)
- Added support for register slicing when applying operations to a register (#1643).
Expand Down Expand Up @@ -136,8 +139,6 @@ Added
- New CXDirection pass for fixing the direction of cx gates (#1410).
- New CheckMap pass for checking if circuit meets mapping requirements (#1433).
- New Optimize1QGate pass for combining chains of 1q rotations (#1442).
- New EnlargeWithAncilla pass for adding ancilla qubits after a Layout
selection pass (#1603).


Changed
Expand Down
10 changes: 9 additions & 1 deletion qiskit/dagcircuit/_dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1259,13 +1259,21 @@ def get_named_nodes(self, *names):
return named_nodes

def get_2q_nodes(self):
"""Get the set of 2-qubit nodes."""
"""Get list of 2-qubit nodes."""
two_q_nodes = []
for node_id, node_data in self.multi_graph.nodes(data=True):
if node_data['type'] == 'op' and len(node_data['qargs']) == 2:
two_q_nodes.append(self.multi_graph.node[node_id])
return two_q_nodes

def get_3q_or_more_nodes(self):
"""Get list of 3-or-more-qubit nodes: (id, data)."""
three_q_nodes = []
for node_id, node_data in self.multi_graph.nodes(data=True):
if node_data['type'] == 'op' and len(node_data['qargs']) >= 3:
three_q_nodes.append((node_id, self.multi_graph.node[node_id]))
return three_q_nodes

def successors(self, node):
"""Returns the successors of a node."""
return self.multi_graph.successors(node)
Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
from .fixed_point import FixedPoint
from .optimize_1q_gates import Optimize1qGates
from .decompose import Decompose
from .unroll_3q_or_more import Unroll3qOrMore
from .commutation_analysis import CommutationAnalysis
from .commutation_transformation import CommutationTransformation
from .mapping.barrier_before_final_measurements import BarrierBeforeFinalMeasurements
Expand Down
2 changes: 1 addition & 1 deletion qiskit/transpiler/passes/decompose.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@

class Decompose(TransformationPass):
"""
Expand a gate in a circle using its decomposition rules.
Expand a gate in a circuit using its decomposition rules.
"""

def __init__(self, gate=None):
Expand Down
34 changes: 34 additions & 0 deletions qiskit/transpiler/passes/unroll_3q_or_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
# -*- coding: utf-8 -*-

# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

"""Pass for decomposing 3q (or more) gates into 2q or 1q gates."""

from qiskit.transpiler._basepasses import TransformationPass


class Unroll3qOrMore(TransformationPass):
"""
Recursively expands 3+ qubit gates until the circuit only contains
1 qubit and 2qubit gates.
"""

def run(self, dag):
"""Expand 3+ qubit gates using their decomposition rules.

Args:
dag(DAGCircuit): input dag
Returns:
DAGCircuit: output dag with maximum node degrees of 2
"""
for node_id, node_data in dag.get_3q_or_more_nodes():
decomposition_rules = node_data["op"].decompositions()

# TODO: allow choosing other possible decompositions
decomposition_dag = self.run(decomposition_rules[0]) # recursively unroll

dag.substitute_node_with_dag(node_id, decomposition_dag)
return dag
67 changes: 67 additions & 0 deletions test/python/transpiler/test_unroll_3q_or_more.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# -*- coding: utf-8 -*-

# Copyright 2018, IBM.
#
# This source code is licensed under the Apache License, Version 2.0 found in
# the LICENSE.txt file in the root directory of this source tree.

"""Test the Unroll3qOrMore pass"""

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.transpiler.passes import Unroll3qOrMore
from qiskit.converters import circuit_to_dag
from qiskit.test import QiskitTestCase


class TestUnroll3qOrMore(QiskitTestCase):
"""Tests the Unroll3qOrMore pass, for unrolling all
gates until reaching only 1q or 2q gates."""

def test_ccx(self):
"""Test decompose CCX.
"""
qr1 = QuantumRegister(2, 'qr1')
qr2 = QuantumRegister(1, 'qr2')
circuit = QuantumCircuit(qr1, qr2)
circuit.ccx(qr1[0], qr1[1], qr2[0])
dag = circuit_to_dag(circuit)
pass_ = Unroll3qOrMore()
after_dag = pass_.run(dag)
op_nodes = after_dag.get_op_nodes(data=True)
self.assertEqual(len(op_nodes), 15)
for node in op_nodes:
op = node[1]["op"]
self.assertIn(op.name, ['h', 't', 'tdg', 'cx'])

def test_cswap(self):
"""Test decompose CSwap (recursively).
"""
qr1 = QuantumRegister(2, 'qr1')
qr2 = QuantumRegister(1, 'qr2')
circuit = QuantumCircuit(qr1, qr2)
circuit.cswap(qr1[0], qr1[1], qr2[0])
dag = circuit_to_dag(circuit)
pass_ = Unroll3qOrMore()
after_dag = pass_.run(dag)
op_nodes = after_dag.get_op_nodes(data=True)
self.assertEqual(len(op_nodes), 17)
for node in op_nodes:
op = node[1]["op"]
self.assertIn(op.name, ['h', 't', 'tdg', 'cx'])

def test_decompose_conditional(self):
"""Test decompose a 3-qubit gate with a conditional.
"""
qr = QuantumRegister(3, 'qr')
cr = ClassicalRegister(1, 'cr')
circuit = QuantumCircuit(qr, cr)
circuit.ccx(qr[0], qr[1], qr[2]).c_if(cr, 0)
dag = circuit_to_dag(circuit)
pass_ = Unroll3qOrMore()
after_dag = pass_.run(dag)
op_nodes = after_dag.get_op_nodes(data=True)
self.assertEqual(len(op_nodes), 15)
for node in op_nodes:
op = node[1]["op"]
self.assertIn(op.name, ['h', 't', 'tdg', 'cx'])
self.assertEqual(node[1]['condition'], (cr, 0))