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

Unroll3qOrMore before layout allocation stage in the levels 0, 1, and 2 #7251

Merged
merged 12 commits into from
Nov 30, 2021
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.