Skip to content

Commit

Permalink
Unroll3qOrMore before layout allocation stage in the levels 0, 1, and…
Browse files Browse the repository at this point in the history
… 2 (#7251)

* level 0

* level 0

* level 1 and 2

* level 1 and 2

* always unroll

* reduce diff

* reno

* _unroll3q without coupling map
  • Loading branch information
1ucian0 authored Nov 30, 2021
1 parent 4a94b77 commit fe0abaa
Show file tree
Hide file tree
Showing 5 changed files with 57 additions and 59 deletions.
34 changes: 16 additions & 18 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,20 @@ def level_0_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
unitary_synthesis_method = pass_manager_config.unitary_synthesis_method
unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config

# 1. Choose an initial layout if not set by user (default: trivial layout)
# 1. Decompose so only 1-qubit and 2-qubit gates remain
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
method=unitary_synthesis_method,
min_qubits=3,
plugin_config=unitary_synthesis_plugin_config,
),
Unroll3qOrMore(),
]

# 2. Choose an initial layout if not set by user (default: trivial layout)
_given_layout = SetLayout(initial_layout)

def _choose_layout_condition(property_set):
Expand All @@ -111,24 +124,9 @@ def _choose_layout_condition(property_set):
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

# 2. Extend dag/layout with ancillas using the full coupling map
# 3. Extend dag/layout with ancillas using the full coupling map
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

# 3. Decompose so only 1-qubit and 2-qubit gates remain
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
min_qubits=3,
plugin_config=unitary_synthesis_plugin_config,
),
Unroll3qOrMore(),
]

# 4. Swap to fit the coupling map
_swap_check = CheckMap(coupling_map)

Expand Down Expand Up @@ -247,9 +245,9 @@ def _contains_delay(property_set):
pm0 = PassManager()
if coupling_map or initial_layout:
pm0.append(_given_layout)
pm0.append(_unroll3q)
pm0.append(_choose_layout, condition=_choose_layout_condition)
pm0.append(_embed)
pm0.append(_unroll3q)
pm0.append(_swap_check)
pm0.append(_swap, condition=_swap_condition)
pm0.append(_unroll)
Expand Down
34 changes: 16 additions & 18 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,20 @@ def level_1_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
def _choose_layout_condition(property_set):
return not property_set["layout"]

# 2. Use a better layout on densely connected qubits, if circuit needs swaps
# 2. Decompose so only 1-qubit and 2-qubit gates remain
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
method=unitary_synthesis_method,
min_qubits=3,
plugin_config=unitary_synthesis_plugin_config,
),
Unroll3qOrMore(),
]

# 3. Use a better layout on densely connected qubits, if circuit needs swaps
if layout_method == "trivial":
_improve_layout = TrivialLayout(coupling_map)
elif layout_method == "dense":
Expand All @@ -130,24 +143,9 @@ def _not_perfect_yet(property_set):
and property_set["trivial_layout_score"] != 0
)

# 3. Extend dag/layout with ancillas using the full coupling map
# 4. Extend dag/layout with ancillas using the full coupling map
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

# 4. Decompose so only 1-qubit and 2-qubit gates remain
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
method=unitary_synthesis_method,
backend_props=backend_properties,
min_qubits=3,
plugin_config=unitary_synthesis_plugin_config,
),
Unroll3qOrMore(),
]

# 5. Swap to fit the coupling map
_swap_check = CheckMap(coupling_map)

Expand Down Expand Up @@ -279,10 +277,10 @@ def _contains_delay(property_set):
pm1 = PassManager()
if coupling_map or initial_layout:
pm1.append(_given_layout)
pm1.append(_unroll3q)
pm1.append(_choose_layout_and_score, condition=_choose_layout_condition)
pm1.append(_improve_layout, condition=_not_perfect_yet)
pm1.append(_embed)
pm1.append(_unroll3q)
pm1.append(_swap_check)
pm1.append(_swap, condition=_swap_condition)
pm1.append(_unroll)
Expand Down
40 changes: 19 additions & 21 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,20 @@ def level_2_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
timing_constraints = pass_manager_config.timing_constraints or TimingConstraints()
unitary_synthesis_plugin_config = pass_manager_config.unitary_synthesis_plugin_config

# 1. Search for a perfect layout, or choose a dense layout, if no layout given
# 1. Unroll to 1q or 2q gates
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
method=unitary_synthesis_method,
min_qubits=3,
plugin_config=unitary_synthesis_plugin_config,
),
Unroll3qOrMore(),
]

# 2. Search for a perfect layout, or choose a dense layout, if no layout given
_given_layout = SetLayout(initial_layout)

def _choose_layout_condition(property_set):
Expand All @@ -121,7 +134,7 @@ def _choose_layout_condition(property_set):
Layout2qDistance(coupling_map, property_name="trivial_layout_score"),
]
)
# 1b. If a trivial layout wasn't perfect (ie no swaps are needed) then try using
# 2b. If a trivial layout wasn't perfect (ie no swaps are needed) then try using
# CSP layout to find a perfect layout
_choose_layout_1 = (
[]
Expand All @@ -132,7 +145,7 @@ def _choose_layout_condition(property_set):
def _trivial_not_perfect(property_set):
# Verify that a trivial layout is perfect. If trivial_layout_score > 0
# the layout is not perfect. The layout is unconditionally set by trivial
# layout so we need to clear it before contuing.
# layout so we need to clear it before continuing.
if property_set["trivial_layout_score"] is not None:
if property_set["trivial_layout_score"] != 0:
return True
Expand All @@ -151,7 +164,7 @@ def _csp_not_found_match(property_set):
return True
return False

# 1c. if CSP layout doesn't converge on a solution use layout_method (dense) to get a layout
# 2c. if CSP layout doesn't converge on a solution use layout_method (dense) to get a layout
if layout_method == "trivial":
_choose_layout_2 = TrivialLayout(coupling_map)
elif layout_method == "dense":
Expand All @@ -163,24 +176,9 @@ def _csp_not_found_match(property_set):
else:
raise TranspilerError("Invalid layout method %s." % layout_method)

# 2. Extend dag/layout with ancillas using the full coupling map
# 3. Extend dag/layout with ancillas using the full coupling map
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

# 3. Unroll to 1q or 2q gates
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
min_qubits=3,
plugin_config=unitary_synthesis_plugin_config,
),
Unroll3qOrMore(),
]

# 4. Swap to fit the coupling map
_swap_check = CheckMap(coupling_map)

Expand Down Expand Up @@ -316,11 +314,11 @@ def _contains_delay(property_set):
pm2 = PassManager()
if coupling_map or initial_layout:
pm2.append(_given_layout)
pm2.append(_unroll3q)
pm2.append(_choose_layout_0, condition=_choose_layout_condition)
pm2.append(_choose_layout_1, condition=_trivial_not_perfect)
pm2.append(_choose_layout_2, condition=_csp_not_found_match)
pm2.append(_embed)
pm2.append(_unroll3q)
pm2.append(_swap_check)
pm2.append(_swap, condition=_swap_condition)
pm2.append(_unroll)
Expand Down
2 changes: 0 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -114,8 +114,6 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
plugin_config=unitary_synthesis_plugin_config,
min_qubits=3,
Expand Down
6 changes: 6 additions & 0 deletions releasenotes/notes/7156-df1a60c608b93184.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
fixes:
- |
Fixed `#7156 <https://github.com/Qiskit/qiskit-terra/issues/7156>`__ .
Many layout methods ignore 3-or-more qubit gates resulting in expected layout allocation decisions.
The pass :class:`qiskit.transpiler.passes.Unroll3qOrMore` is now being executed before the layout pass.

0 comments on commit fe0abaa

Please sign in to comment.