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

Pass to remove diagonal gates before measurement #2208

Merged
merged 25 commits into from
Apr 27, 2019
Merged
Show file tree
Hide file tree
Changes from 24 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
2 changes: 2 additions & 0 deletions CHANGELOG.rst
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ Added
wires instead of named bits.
- Added a ``OptimizeSwapBeforeMeasure`` pass that removes the swap gates when they
are followed by a measurement instruction, moving the latter to the proper wire. (#1890)
- Added a ``RemoveDiagonalGatesBeforeMeasure`` pass that removes the diagonal gates when they
are followed by a measurement instruction. (#2208)
- Added a ``CommutativeCancellation`` pass that cancels self-inverse gates and combines
rotations about the Z axis, leveraging previously-found gate commutation relations. (#2012)
- Added a ``Collect2qBlocks`` pass that analyzes the circuit for uninterrupted sequences
Expand Down
11 changes: 11 additions & 0 deletions qiskit/dagcircuit/dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -1089,6 +1089,17 @@ def predecessors(self, node):

return self._multi_graph.predecessors(node)

def quantum_predecessors(self, node):
"""Returns list of the predecessors of a node that are
connected by a quantum edge as DAGNodes."""

predecessors = []
for predecessor in self.predecessors(node):
if isinstance(self._multi_graph.get_edge_data(predecessor, node, key=0)['wire'][0],
QuantumRegister):
predecessors.append(predecessor)
return predecessors

def ancestors(self, node):
"""Returns set of the ancestors of a node as DAGNodes."""
if isinstance(node, int):
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 @@ -37,5 +37,6 @@
from .mapping.noise_adaptive_layout import NoiseAdaptiveLayout
from .mapping.basic_swap import BasicSwap
from .mapping.lookahead_swap import LookaheadSwap
from .remove_diagonal_gates_before_measure import RemoveDiagonalGatesBeforeMeasure
from .mapping.stochastic_swap import StochasticSwap
from .mapping.legacy_swap import LegacySwap
44 changes: 44 additions & 0 deletions qiskit/transpiler/passes/remove_diagonal_gates_before_measure.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# -*- coding: utf-8 -*-

# Copyright 2019, 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.


"""
Transpiler pass to remove diagonal gates (like RZ, T, Z, etc) before
a measurement. Including diagonal 2Q gates.
"""

from qiskit.circuit import Measure
from qiskit.extensions.standard import RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate,\
CzGate, CrzGate, Cu1Gate, RZZGate
from qiskit.transpiler.basepasses import TransformationPass


class RemoveDiagonalGatesBeforeMeasure(TransformationPass):
"""Remove diagonal gates (like RZ, T, Z, etc) before a measurement.
Including diagonal 2Q gates."""

def run(self, dag):
"""Return a new circuit that has been optimized."""
diagonal_1q_gates = (RZGate, ZGate, TGate, SGate, TdgGate, SdgGate, U1Gate)
diagonal_2q_gates = (CzGate, CrzGate, Cu1Gate, RZZGate)

nodes_to_remove = set()
for measure in dag.op_nodes(Measure):
predecessor = dag.quantum_predecessors(measure)[0]

if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_1q_gates):
nodes_to_remove.add(predecessor)

if predecessor.type == 'op' and isinstance(predecessor.op, diagonal_2q_gates):
successors = dag.quantum_successors(predecessor)
if all([s.type == 'op' and isinstance(s.op, Measure) for s in successors]):
nodes_to_remove.add(predecessor)

for node_to_remove in nodes_to_remove:
dag.remove_op_node(node_to_remove)

return dag
18 changes: 18 additions & 0 deletions test/python/test_dagcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,24 @@ def test_quantum_successors(self):
self.assertEqual(successor_cnot[0].type, 'out')
self.assertIsInstance(successor_cnot[1].op, Reset)

def test_quantum_predecessors(self):
"""The method dag.quantum_predecessors() returns predecessors connected by quantum edges"""
self.dag.apply_operation_back(Reset(), [self.qubit0], [])
self.dag.apply_operation_back(CnotGate(), [self.qubit0, self.qubit1], [])
self.dag.apply_operation_back(Measure(), [self.qubit1, self.clbit1], [])

predecessor_measure = self.dag.quantum_predecessors(
self.dag.named_nodes('measure').pop())
self.assertEqual(len(predecessor_measure), 1)
cnot_node = predecessor_measure[0]

self.assertIsInstance(cnot_node.op, CnotGate)

predecessor_cnot = self.dag.quantum_predecessors(cnot_node)
self.assertEqual(len(predecessor_cnot), 2)
self.assertEqual(predecessor_cnot[1].type, 'in')
self.assertIsInstance(predecessor_cnot[0].op, Reset)

def test_get_gates_nodes(self):
"""The method dag.gate_nodes() returns all gate nodes"""
self.dag.apply_operation_back(HGate(), [self.qubit0], [])
Expand Down
Loading