Skip to content

Commit

Permalink
Integrate FinalizeLayouts into the PassManager harness
Browse files Browse the repository at this point in the history
This commit integrates the function that finalize layouts was performing
into the passmanager harnesss. We'll always need to run the equivalent
of finalize layout if any passes are setting a virtual permutation so
using a standalone pass that can be forgotten is potentially error
prone. This inlines the logic as part of the passmanager's output
preparation stage so we always finalize the layout.
  • Loading branch information
mtreinish committed May 2, 2024
1 parent 5cbc67d commit e755bcd
Show file tree
Hide file tree
Showing 6 changed files with 44 additions and 95 deletions.
2 changes: 0 additions & 2 deletions qiskit/transpiler/passes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,6 @@
ElidePermutations
NormalizeRXAngle
OptimizeAnnotated
FinalizeLayouts
Calibration
=============
Expand Down Expand Up @@ -241,7 +240,6 @@
from .optimization import ElidePermutations
from .optimization import NormalizeRXAngle
from .optimization import OptimizeAnnotated
from .optimization import FinalizeLayouts

# circuit analysis
from .analysis import ResourceEstimation
Expand Down
1 change: 0 additions & 1 deletion qiskit/transpiler/passes/optimization/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,3 @@
from .elide_permutations import ElidePermutations
from .normalize_rx_angle import NormalizeRXAngle
from .optimize_annotated import OptimizeAnnotated
from .finalize_layouts import FinalizeLayouts
75 changes: 0 additions & 75 deletions qiskit/transpiler/passes/optimization/finalize_layouts.py

This file was deleted.

45 changes: 44 additions & 1 deletion qiskit/transpiler/passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
from qiskit.passmanager.exceptions import PassManagerError
from .basepasses import BasePass
from .exceptions import TranspilerError
from .layout import TranspileLayout
from .layout import TranspileLayout, Layout

_CircuitsT = Union[List[QuantumCircuit], QuantumCircuit]

Expand Down Expand Up @@ -69,6 +69,7 @@ def _passmanager_backend(
) -> QuantumCircuit:
out_program = dag_to_circuit(passmanager_ir, copy_operations=False)

self._finalize_layouts(passmanager_ir)
out_name = kwargs.get("output_name", None)
if out_name is not None:
out_program.name = out_name
Expand Down Expand Up @@ -96,6 +97,48 @@ def _passmanager_backend(

return out_program

def _finalize_layouts(self, dag):
if (virtual_permutation_layout := self.property_set["virtual_permutation_layout"]) is None:
return

self.property_set.pop("virtual_permutation_layout")

# virtual_permutation_layout is usually created before extending the layout with ancillas,
# so we extend the permutation to be identity on ancilla qubits
original_qubit_indices = self.property_set.get("original_qubit_indices", None)
for oq in original_qubit_indices:
if oq not in virtual_permutation_layout:
virtual_permutation_layout[oq] = original_qubit_indices[oq]

t_qubits = dag.qubits

if (t_initial_layout := self.property_set.get("layout", None)) is None:
t_initial_layout = Layout(dict(enumerate(t_qubits)))

if (t_final_layout := self.property_set.get("final_layout", None)) is None:
t_final_layout = Layout(dict(enumerate(t_qubits)))

# Ordered list of original qubits
original_qubits_reverse = {v: k for k, v in original_qubit_indices.items()}
original_qubits = []
for i in range(len(original_qubits_reverse)):
original_qubits.append(original_qubits_reverse[i])

virtual_permutation_layout_inv = virtual_permutation_layout.inverse(
original_qubits, original_qubits
)

t_initial_layout_inv = t_initial_layout.inverse(original_qubits, t_qubits)

# ToDo: this can possibly be made simpler
new_final_layout = t_initial_layout_inv
new_final_layout = new_final_layout.compose(virtual_permutation_layout_inv, original_qubits)
new_final_layout = new_final_layout.compose(t_initial_layout, original_qubits)
new_final_layout = new_final_layout.compose(t_final_layout, t_qubits)

self.property_set["layout"] = t_initial_layout
self.property_set["final_layout"] = new_final_layout

def append(
self,
passes: Task | list[Task],
Expand Down
6 changes: 0 additions & 6 deletions releasenotes/notes/add-elide-swaps-b0a4c373c9af1efd.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,3 @@ features:
The pass also sets the ``virtual_permutation_layout`` property set, storing
the permutation of the virtual qubits that was optimized away.
- |
Added a new transpiler pass, :class:`~.FinalizeLayouts`,
which is designed to run after the :ref:`routing_stage` and update the
``final_layout`` property set based on ``layout`` and
``virtual_permutation_layout``.
10 changes: 0 additions & 10 deletions test/python/transpiler/test_elide_permutations.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
from qiskit.circuit.quantumcircuit import QuantumCircuit
from qiskit.circuit.library.generalized_gates import PermutationGate
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes.optimization.finalize_layouts import FinalizeLayouts
from qiskit.transpiler.passes.optimization.elide_permutations import ElidePermutations
from qiskit.circuit.controlflow import IfElseOp
from qiskit.quantum_info import Operator
Expand Down Expand Up @@ -317,7 +316,6 @@ def test_not_run_after_layout(self):
optimization_level=3, initial_layout=list(range(2, -1, -1)), seed_transpiler=42
)
spm.layout += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))
self.assertIn("swap", res.count_ops())
Expand All @@ -335,7 +333,6 @@ def test_unitary_equivalence(self):
with self.subTest("no coupling map"):
spm = generate_preset_pass_manager(optimization_level=3, seed_transpiler=42)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))

Expand All @@ -344,7 +341,6 @@ def test_unitary_equivalence(self):
optimization_level=3, seed_transpiler=42, coupling_map=CouplingMap.from_line(3)
)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))

Expand All @@ -366,7 +362,6 @@ def test_unitary_equivalence_routing_and_basis_translation(self):
with self.subTest("no coupling map"):
spm = generate_preset_pass_manager(optimization_level=3, seed_transpiler=42)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))

Expand All @@ -378,7 +373,6 @@ def test_unitary_equivalence_routing_and_basis_translation(self):
basis_gates=["u", "cz"],
)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))

Expand All @@ -390,7 +384,6 @@ def test_unitary_equivalence_routing_and_basis_translation(self):
basis_gates=["u", "cz"],
)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))

Expand All @@ -403,7 +396,6 @@ def test_unitary_equivalence_routing_and_basis_translation(self):
coupling_map=CouplingMap.from_line(5),
)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)
self.assertTrue(Operator.from_circuit(res).equiv(Operator(qc)))

Expand All @@ -414,7 +406,6 @@ def test_unitary_equivalence_routing_and_basis_translation(self):
coupling_map=CouplingMap.from_line(8),
)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)

qc_with_ancillas = QuantumCircuit(8)
Expand All @@ -429,7 +420,6 @@ def test_unitary_equivalence_routing_and_basis_translation(self):
coupling_map=CouplingMap.from_line(8),
)
spm.init += ElidePermutations()
spm.post_optimization = PassManager([FinalizeLayouts()])
res = spm.run(qc)

qc_with_ancillas = QuantumCircuit(8)
Expand Down

0 comments on commit e755bcd

Please sign in to comment.