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 check for the size during transpiler optimization #7542

Merged
merged 11 commits into from
Mar 19, 2022
7 changes: 5 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import FixedPoint
from qiskit.transpiler.passes import Depth
from qiskit.transpiler.passes import Size
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import ApplyLayout
Expand Down Expand Up @@ -234,10 +235,12 @@ def _direction_condition(property_set):
_reset = RemoveResetInZeroState()

# 9. Merge 1q rotations and cancel CNOT gates iteratively until no more change in depth
# or size of circuit
_depth_check = [Depth(), FixedPoint("depth")]
_size_check = [Size(), FixedPoint("size")]

def _opt_control(property_set):
return not property_set["depth_fixed_point"]
return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"])

_opt = [Optimize1qGatesDecomposition(basis_gates), CXCancellation()]

Expand Down Expand Up @@ -291,7 +294,7 @@ def _contains_delay(property_set):
pm1.append(_direction_check)
pm1.append(_direction, condition=_direction_condition)
pm1.append(_reset)
pm1.append(_depth_check + _opt + _unroll, do_while=_opt_control)
pm1.append(_depth_check + _size_check + _opt + _unroll, do_while=_opt_control)
if inst_map and inst_map.has_custom_gate():
pm1.append(PulseGates(inst_map=inst_map))
if scheduling_method:
Expand Down
9 changes: 7 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import FixedPoint
from qiskit.transpiler.passes import Depth
from qiskit.transpiler.passes import Size
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import CommutativeCancellation
Expand Down Expand Up @@ -268,10 +269,12 @@ def _direction_condition(property_set):
_reset = RemoveResetInZeroState()

# 8. 1q rotation merge and commutative cancellation iteratively until no more change in depth
# and size
_depth_check = [Depth(), FixedPoint("depth")]
_size_check = [Size(), FixedPoint("size")]

def _opt_control(property_set):
return not property_set["depth_fixed_point"]
return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"])

_opt = [
Optimize1qGatesDecomposition(basis_gates),
Expand Down Expand Up @@ -329,7 +332,9 @@ def _contains_delay(property_set):
pm2.append(_direction_check)
pm2.append(_direction, condition=_direction_condition)
pm2.append(_reset)
pm2.append(_depth_check + _opt + _unroll, do_while=_opt_control)

pm2.append(_depth_check + _size_check + _opt + _unroll, do_while=_opt_control)

if inst_map and inst_map.has_custom_gate():
pm2.append(PulseGates(inst_map=inst_map))
if scheduling_method:
Expand Down
12 changes: 8 additions & 4 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
from qiskit.transpiler.passes import EnlargeWithAncilla
from qiskit.transpiler.passes import FixedPoint
from qiskit.transpiler.passes import Depth
from qiskit.transpiler.passes import Size
from qiskit.transpiler.passes import RemoveResetInZeroState
from qiskit.transpiler.passes import Optimize1qGatesDecomposition
from qiskit.transpiler.passes import CommutativeCancellation
Expand Down Expand Up @@ -266,9 +267,10 @@ def _direction_condition(property_set):
# 8. Optimize iteratively until no more change in depth. Removes useless gates
# after reset and before measure, commutes gates and optimizes contiguous blocks.
_depth_check = [Depth(), FixedPoint("depth")]
_size_check = [Size(), FixedPoint("size")]

def _opt_control(property_set):
return not property_set["depth_fixed_point"]
return (not property_set["depth_fixed_point"]) or (not property_set["size_fixed_point"])

_reset = [RemoveResetInZeroState()]

Expand Down Expand Up @@ -346,12 +348,14 @@ def _contains_delay(property_set):
# inserted by UnitarySynthesis which is direction aware but only via
# the coupling map which with a target doesn't give a full picture
if target is not None:
pm3.append(_depth_check + _opt + _unroll + _direction, do_while=_opt_control)
pm3.append(
_depth_check + _size_check + _opt + _unroll + _direction, do_while=_opt_control
)
else:
pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control)
pm3.append(_depth_check + _size_check + _opt + _unroll, do_while=_opt_control)
else:
pm3.append(_reset)
pm3.append(_depth_check + _opt + _unroll, do_while=_opt_control)
pm3.append(_depth_check + _size_check + _opt + _unroll, do_while=_opt_control)
if inst_map and inst_map.has_custom_gate():
pm3.append(PulseGates(inst_map=inst_map))
if scheduling_method:
Expand Down
45 changes: 45 additions & 0 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -919,3 +919,48 @@ def test_input_dag_copy(self):
qc.cx(1, 0)
circ = transpile(qc, basis_gates=["u3", "cz"])
self.assertIsInstance(circ, QuantumCircuit)


@ddt
class TestOptimizationOnSize(QiskitTestCase):
"""Test the optimization levels for optimization based on
both size and depth of the circuit.
See https://github.com/Qiskit/qiskit-terra/pull/7542
"""

@data(2, 3)
def test_size_optimization(self, level):
"""Test the levels for optimization based on size of circuit"""
qc = QuantumCircuit(8)
qc.cx(1, 2)
qc.cx(2, 3)
qc.cx(5, 4)
qc.cx(6, 5)
qc.cx(4, 5)
qc.cx(3, 4)
qc.cx(5, 6)
qc.cx(5, 4)
qc.cx(3, 4)
qc.cx(2, 3)
qc.cx(1, 2)
qc.cx(6, 7)
qc.cx(6, 5)
qc.cx(5, 4)
qc.cx(7, 6)
qc.cx(6, 7)

circ = transpile(qc, optimization_level=level).decompose()

circ_data = circ.data
free_qubits = set([0, 1, 2, 3])

# ensure no gates are using qubits - [0,1,2,3]
for gate in circ_data:
qubits = gate[1]
indices = {circ.find_bit(qubit).index for qubit in qubits}
common = indices.intersection(free_qubits)
for common_qubit in common:
self.assertTrue(common_qubit not in free_qubits)

self.assertLess(circ.size(), qc.size())
self.assertLessEqual(circ.depth(), qc.depth())