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 ResetAfterMeasureSimplification transpiler pass #8330

Merged
merged 7 commits into from
Sep 2, 2022
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
2 changes: 2 additions & 0 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@
HoareOptimizer
TemplateOptimization
EchoRZXWeylDecomposition
ResetAfterMeasureSimplification
OptimizeCliffords

Calibration
Expand Down Expand Up @@ -222,6 +223,7 @@
from .optimization import InverseCancellation
from .optimization import EchoRZXWeylDecomposition
from .optimization import CollectLinearFunctions
from .optimization import ResetAfterMeasureSimplification
from .optimization import OptimizeCliffords

# circuit analysis
Expand Down
1 change: 1 addition & 0 deletions qiskit/transpiler/passes/optimization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,5 @@
from .collect_1q_runs import Collect1qRuns
from .echo_rzx_weyl_decomposition import EchoRZXWeylDecomposition
from .collect_linear_functions import CollectLinearFunctions
from .reset_after_measure_simplification import ResetAfterMeasureSimplification
from .optimize_cliffords import OptimizeCliffords
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2021.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Replace resets after measure with a conditional XGate."""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.circuit.library.standard_gates.x import XGate
from qiskit.circuit.reset import Reset
from qiskit.circuit.measure import Measure
from qiskit.dagcircuit.dagcircuit import DAGCircuit
from qiskit.dagcircuit.dagnode import DAGOpNode


class ResetAfterMeasureSimplification(TransformationPass):
"""This pass replaces reset after measure with a conditional X gate.

This optimization is suitable for use on IBM Quantum systems where the
reset operation is performed by a measurement followed by a conditional
x-gate. It might not be desireable on other backends if reset is implemented
differently.
"""

def run(self, dag):
"""Run the pass on a dag."""
for node in dag.op_nodes(Measure):
succ = next(dag.quantum_successors(node))
if isinstance(succ, DAGOpNode) and isinstance(succ.op, Reset):
new_x = XGate()
new_x.condition = (node.cargs[0], 1)
new_dag = DAGCircuit()
new_dag.add_qubits(node.qargs)
new_dag.add_clbits(node.cargs)
new_dag.apply_operation_back(node.op, node.qargs, node.cargs)
new_dag.apply_operation_back(new_x, node.qargs)
dag.remove_op_node(succ)
dag.substitute_node_with_dag(node, new_dag)
return dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
features:
- |
Added a new transpiler pass, :class:`~ResetAfterMeasureSimplification`,
which is used to replace a :class:`~.Reset` operation after a
:class:`~.Measure` with a conditional :class:`~.XGate`. This pass can
be used on backends where a :class:`~.Reset` operation is performed by
doing a measurement and then a conditional X gate so that this will
remove the duplicate implicit :class:`~.Measure` from the :class:`~.Reset`
operation.
140 changes: 140 additions & 0 deletions test/python/transpiler/test_reset_after_measure_simplification.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# This code is licensed under the Apache License, Version 2.0. You may
# obtain a copy of this license in the LICENSE.txt file in the root directory
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
#
# Any modifications or derivative works of this code must retain this
# copyright notice, and modified files need to carry a notice indicating
# that they have been altered from the originals.

"""Test the ResetAfterMeasureSimplification pass"""

from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
from qiskit.circuit.classicalregister import Clbit
from qiskit.transpiler.passes.optimization import ResetAfterMeasureSimplification
from qiskit.test import QiskitTestCase


class TestResetAfterMeasureSimplificationt(QiskitTestCase):
"""Test ResetAfterMeasureSimplification transpiler pass."""

def test_simple(self):
"""Test simple"""
qc = QuantumCircuit(1, 1)
qc.measure(0, 0)
qc.reset(0)

new_qc = ResetAfterMeasureSimplification()(qc)

ans_qc = QuantumCircuit(1, 1)
ans_qc.measure(0, 0)
ans_qc.x(0).c_if(ans_qc.clbits[0], 1)
self.assertEqual(new_qc, ans_qc)

def test_simple_null(self):
"""Test simple no change in circuit"""
qc = QuantumCircuit(1, 1)
qc.measure(0, 0)
qc.x(0)
qc.reset(0)
new_qc = ResetAfterMeasureSimplification()(qc)

self.assertEqual(new_qc, qc)

def test_simple_multi_reg(self):
"""Test simple, multiple registers"""
cr1 = ClassicalRegister(1, "c1")
cr2 = ClassicalRegister(1, "c2")
qr = QuantumRegister(1, "q")
qc = QuantumCircuit(qr, cr1, cr2)
qc.measure(0, 1)
qc.reset(0)

new_qc = ResetAfterMeasureSimplification()(qc)

ans_qc = QuantumCircuit(qr, cr1, cr2)
ans_qc.measure(0, 1)
ans_qc.x(0).c_if(cr2[0], 1)

self.assertEqual(new_qc, ans_qc)

def test_simple_multi_reg_null(self):
"""Test simple, multiple registers, null change"""
cr1 = ClassicalRegister(1, "c1")
cr2 = ClassicalRegister(1, "c2")
qr = QuantumRegister(2, "q")
qc = QuantumCircuit(qr, cr1, cr2)
qc.measure(0, 1)
qc.reset(1) # reset not on same qubit as meas

new_qc = ResetAfterMeasureSimplification()(qc)
self.assertEqual(new_qc, qc)

def test_simple_multi_resets(self):
"""Only first reset is collapsed"""
qc = QuantumCircuit(1, 2)
qc.measure(0, 0)
qc.reset(0)
qc.reset(0)

new_qc = ResetAfterMeasureSimplification()(qc)

ans_qc = QuantumCircuit(1, 2)
ans_qc.measure(0, 0)
ans_qc.x(0).c_if(ans_qc.clbits[0], 1)
ans_qc.reset(0)
self.assertEqual(new_qc, ans_qc)

def test_simple_multi_resets_with_resets_before_measure(self):
"""Reset BEFORE measurement not collapsed"""
qc = QuantumCircuit(2, 2)
qc.measure(0, 0)
qc.reset(0)
qc.reset(1)
qc.measure(1, 1)

new_qc = ResetAfterMeasureSimplification()(qc)

ans_qc = QuantumCircuit(2, 2)
ans_qc.measure(0, 0)
ans_qc.x(0).c_if(Clbit(ClassicalRegister(2, "c"), 0), 1)
ans_qc.reset(1)
ans_qc.measure(1, 1)

self.assertEqual(new_qc, ans_qc)

def test_barriers_work(self):
"""Test that barriers block consolidation"""
qc = QuantumCircuit(1, 1)
qc.measure(0, 0)
qc.barrier(0)
qc.reset(0)

new_qc = ResetAfterMeasureSimplification()(qc)
self.assertEqual(new_qc, qc)

def test_bv_circuit(self):
"""Test Bernstein Vazirani circuit with midcircuit measurement."""
bitstring = "11111"
qc = QuantumCircuit(2, len(bitstring))
qc.x(1)
qc.h(1)
for idx, bit in enumerate(bitstring[::-1]):
qc.h(0)
if int(bit):
qc.cx(0, 1)
qc.h(0)
qc.measure(0, idx)
if idx != len(bitstring) - 1:
qc.reset(0)
# reset control
qc.reset(1)
qc.x(1)
qc.h(1)
new_qc = ResetAfterMeasureSimplification()(qc)
for op in new_qc.data:
if op.operation.name == "reset":
self.assertEqual(op.qubits[0], new_qc.qubits[1])