Skip to content

Commit

Permalink
Add measure_all, measure_active, remove_final_measures methods (#2993)
Browse files Browse the repository at this point in the history
* Add measure_all, measure_active, remove_final_measurement methods

* Add testing for measure_all, measure_active, remove_final_measurements

* Rename remove_final_measurements to remove_final_measures

* Rename test_remove_final_measurements to test_remove_final_measures

* Fix incorrect method call in test_remove_final_measures

* Fix remove_final_measures to only delete ClassicalRegister if it is unused. Fix measure_active to only check for non-idle qubits instead of  non-idle qubits and clbits

* Add test_remove_final_measures_multiple_measures

* Remove 'gates' from docstrings when referring to measurements

* Rename remove_final_measures to remove_final_measurements

* Rework remove_final_measurements to use RemoveFinalMeasurements pass

* Add RemoveFinalMeasurements pass

* Add remove_final_measurements module

* Rework test_remove_final_measurements_multiple_measures to work with new circuit.remove_final_measurements

* Add pylint disable-cyclic-import to remove_final_measurements

* tests

* fix test

* test to avoid name conflict

* avoid code repetition

* Add QuantumCircuit methods to add and remove final measurements

* Add newline at end of file

* Change inst.control to inst.condition
  • Loading branch information
Matt-Stypulkoski authored and Luciano committed Oct 4, 2019
1 parent ef828be commit 7ac8f89
Show file tree
Hide file tree
Showing 5 changed files with 343 additions and 0 deletions.
63 changes: 63 additions & 0 deletions qiskit/circuit/quantumcircuit.py
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,69 @@ def copy(self, name=None):
cpy.name = name
return cpy

def _create_creg(self, length, name):
""" Creates a creg, checking if ClassicalRegister with same name exists
"""
if name in [creg.name for creg in self.cregs]:
save_prefix = ClassicalRegister.prefix
ClassicalRegister.prefix = name
new_creg = ClassicalRegister(length)
ClassicalRegister.prefix = save_prefix
else:
new_creg = ClassicalRegister(length, name)
return new_creg

def measure_active(self):
"""Adds measurement to all non-idle qubits. Creates a new ClassicalRegister with
a size equal to the number of non-idle qubits being measured.
"""
from qiskit.converters.circuit_to_dag import circuit_to_dag
dag = circuit_to_dag(self)
qubits_to_measure = [qubit for qubit in self.qubits if qubit not in dag.idle_wires()]
new_creg = self._create_creg(len(qubits_to_measure), 'measure')
self.add_register(new_creg)
self.barrier()
self.measure(qubits_to_measure, new_creg)

def measure_all(self):
"""Adds measurement to all qubits. Creates a new ClassicalRegister with a
size equal to the number of qubits being measured.
"""
new_creg = self._create_creg(len(self.qubits), 'measure')
self.add_register(new_creg)
self.barrier()
self.measure(self.qubits, new_creg)

def remove_final_measurements(self):
"""Removes final measurement on all qubits if they are present.
Deletes the ClassicalRegister that was used to store the values from these measurements
if it is idle.
"""
# pylint: disable=cyclic-import
from qiskit.transpiler.passes import RemoveFinalMeasurements
from qiskit.converters import circuit_to_dag
dag = circuit_to_dag(self)
remove_final_meas = RemoveFinalMeasurements()
new_dag = remove_final_meas.run(dag)

# Set self's cregs and instructions to match the new DAGCircuit's
self.data.clear()
self.cregs = list(new_dag.cregs.values())

for node in new_dag.topological_op_nodes():
qubits = []
for qubit in node.qargs:
qubits.append(new_dag.qregs[qubit.register.name][qubit.index])

clbits = []
for clbit in node.cargs:
clbits.append(new_dag.cregs[clbit.register.name][clbit.index])

# Get arguments for classical condition (if any)
inst = node.op.copy()
inst.condition = node.condition
self.append(inst, qubits, clbits)

@staticmethod
def from_qasm_file(path):
"""Take in a QASM file and generate a QuantumCircuit object.
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 @@ -51,3 +51,4 @@
from .mapping.lookahead_swap import LookaheadSwap
from .remove_diagonal_gates_before_measure import RemoveDiagonalGatesBeforeMeasure
from .mapping.stochastic_swap import StochasticSwap
from .remove_final_measurements import RemoveFinalMeasurements
88 changes: 88 additions & 0 deletions qiskit/transpiler/passes/remove_final_measurements.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
# -*- coding: utf-8 -*-

# This code is part of Qiskit.
#
# (C) Copyright IBM 2017, 2019.
#
# 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.


"""
This pass removes final barriers and final measurements, as well as the
ClassicalRegisters they are connected to if the ClassicalRegister
is unused. Measurements and barriers are considered final if they are
followed by no other operations (aside from other measurements or barriers.)
"""

from qiskit.transpiler.basepasses import TransformationPass
from qiskit.dagcircuit import DAGCircuit


class RemoveFinalMeasurements(TransformationPass):
"""Removes final measurements and barriers at the end of a circuit."""

def run(self, dag):
"""Return a circuit with final measurements and barriers removed."""

final_op_types = ['measure', 'barrier']
final_ops = []
cregs_to_remove = dict()
clbits_with_final_measures = set()

for candidate_node in dag.named_nodes(*final_op_types):
is_final_op = True

for _, child_successors in dag.bfs_successors(candidate_node):
if any(suc.type == 'op' and suc.name not in final_op_types
for suc in child_successors):
is_final_op = False
break

if is_final_op:
final_ops.append(candidate_node)

if not final_ops:
return dag

new_dag = DAGCircuit()

for node in final_ops:
for carg in node.cargs:
# Add the clbit that was attached to the measure we are removing
clbits_with_final_measures.add(carg)
dag.remove_op_node(node)

# If the clbit is idle, add its register to list of registers we may remove
for clbit in clbits_with_final_measures:
if clbit in dag.idle_wires():
if clbit.register in cregs_to_remove:
cregs_to_remove[clbit.register] += 1
else:
cregs_to_remove[clbit.register] = 1

# Remove creg if all of its clbits were added above
for key, val in zip(list(dag.cregs.keys()), list(dag.cregs.values())):
if val in cregs_to_remove and cregs_to_remove[val] == val.size:
del dag.cregs[key]

# Fill new DAGCircuit
for qreg in dag.qregs.values():
new_dag.add_qreg(qreg)
for creg in dag.cregs.values():
new_dag.add_creg(creg)

for node in dag.topological_op_nodes():
# copy the condition over too
if node.condition:
new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs,
condition=node.condition)
else:
new_dag.apply_operation_back(node.op, qargs=node.qargs, cargs=node.cargs)

return new_dag
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
---
features:
- |
Introduced new methods in ``QuantumCircuit`` which allows the seamless adding or removing
of measurements at the end of a circuit.
``measure_all()``
Adds a ``barrier`` followed by a ``measure`` operation to all qubits in the circuit.
Creates a ``ClassicalRegister`` of size equal to the number of qubits in the circuit,
which store the measurements.
``measure_active()``
Adds a ``barrier`` followed by a ``measure`` operation to all active qubits in the circuit.
A qubit is active if it has at least one other operation acting upon it.
Creates a ``ClassicalRegister`` of size equal to the number of active qubits in the circuit,
which store the measurements.
``remove_final_measurements()``
Removes all final measurements and preceeding ``barrier`` from a circuit.
A measurement is considered "final" if it is not followed by any other operation,
excluding barriers and other measurements.
After the measurements are removed, if all of the classical bits in the ``ClassicalRegister``
are idle (have no operations attached to them), then the ``ClassicalRegister`` is removed.
Examples::
# Using measure_all()
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.measure_all()
circuit.draw()
# A ClassicalRegister with prefix measure was created.
# It has 2 clbits because there are 2 qubits to measure
┌───┐ ░ ┌─┐
q_0: |0>┤ H ├─░─┤M├───
└───┘ ░ └╥┘┌─┐
q_1: |0>──────░──╫─┤M├
░ ║ └╥┘
measure_0: 0 ═════════╩══╬═
measure_1: 0 ════════════╩═
# Using measure_active()
circuit = QuantumCircuit(2)
circuit.h(0)
circuit.measure_active()
circuit.draw()
# This ClassicalRegister only has 1 clbit because only 1 qubit is active
┌───┐ ░ ┌─┐
q_0: |0>┤ H ├─░─┤M├
└───┘ ░ └╥┘
q_1: |0>──────░──╫─
░ ║
measure_0: 0 ═════════╩═
# Using remove_final_measurements()
# Assuming circuit_all and circuit_active are the circuits from the measure_all and
# measure_active examples above respectively
circuit_all.remove_final_measurements()
circuit_all.draw()
# The ClassicalRegister is removed because, after the measurements were removed,
# all of its clbits were idle
┌───┐
q_0: |0>┤ H ├
└───┘
q_1: |0>─────
circuit_active.remove_final_measurements()
circuit_active.draw()
# This will result in the same circuit
┌───┐
q_0: |0>┤ H ├
└───┘
q_1: |0>─────
107 changes: 107 additions & 0 deletions test/python/circuit/test_circuit_operations.py
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,113 @@ def test_copy_circuit(self):

self.assertEqual(qc, qc.copy())

def test_measure_active(self):
"""Test measure_active
Applies measurements only to non-idle qubits. Creates a ClassicalRegister of size equal to
the amount of non-idle qubits to store the measured values.
"""
qr = QuantumRegister(4)
cr = ClassicalRegister(2, 'measure')

circuit = QuantumCircuit(qr)
circuit.h(qr[0])
circuit.h(qr[2])
circuit.measure_active()

expected = QuantumCircuit(qr)
expected.h(qr[0])
expected.h(qr[2])
expected.add_register(cr)
expected.barrier()
expected.measure([qr[0], qr[2]], [cr[0], cr[1]])

self.assertEqual(expected, circuit)

def test_measure_active_repetition(self):
"""Test measure_active in a circuit with a 'measure' creg.
measure_active should be aware that the creg 'measure' might exists.
"""
qr = QuantumRegister(2)
cr = ClassicalRegister(2, 'measure')

circuit = QuantumCircuit(qr, cr)
circuit.h(qr)
circuit.measure_active()

self.assertEqual(len(circuit.cregs), 2) # Two cregs
self.assertEqual(len(circuit.cregs[0]), 2) # Both length 2
self.assertEqual(len(circuit.cregs[1]), 2)

def test_measure_all(self):
"""Test measure_all applies measurements to all qubits.
Creates a ClassicalRegister of size equal to the total amount of qubits to
store those measured values.
"""
qr = QuantumRegister(2)
cr = ClassicalRegister(2, 'measure')

circuit = QuantumCircuit(qr)
circuit.measure_all()

expected = QuantumCircuit(qr, cr)
expected.barrier()
expected.measure(qr, cr)

self.assertEqual(expected, circuit)

def test_measure_all_repetition(self):
"""Test measure_all in a circuit with a 'measure' creg.
measure_all should be aware that the creg 'measure' might exists.
"""
qr = QuantumRegister(2)
cr = ClassicalRegister(2, 'measure')

circuit = QuantumCircuit(qr, cr)
circuit.measure_all()

self.assertEqual(len(circuit.cregs), 2) # Two cregs
self.assertEqual(len(circuit.cregs[0]), 2) # Both length 2
self.assertEqual(len(circuit.cregs[1]), 2)

def test_remove_final_measurements(self):
"""Test remove_final_measurements
Removes all measurements at end of circuit.
"""
qr = QuantumRegister(2)
cr = ClassicalRegister(2, 'measure')

circuit = QuantumCircuit(qr, cr)
circuit.measure(qr, cr)
circuit.remove_final_measurements()

expected = QuantumCircuit(qr)

self.assertEqual(expected, circuit)

def test_remove_final_measurements_multiple_measures(self):
"""Test remove_final_measurements only removes measurements at the end of the circuit
remove_final_measurements should not remove measurements in the beginning or middle of the
circuit.
"""
qr = QuantumRegister(2)
cr = ClassicalRegister(1)

circuit = QuantumCircuit(qr, cr)
circuit.measure(qr[0], cr)
circuit.h(0)
circuit.measure(qr[0], cr)
circuit.h(0)
circuit.measure(qr[0], cr)
circuit.remove_final_measurements()

expected = QuantumCircuit(qr, cr)
expected.measure(qr[0], cr)
expected.h(0)
expected.measure(qr[0], cr)
expected.h(0)

self.assertEqual(expected, circuit)

def test_mirror(self):
"""Test mirror method reverses but does not invert."""
qc = QuantumCircuit(2, 2)
Expand Down

0 comments on commit 7ac8f89

Please sign in to comment.