Skip to content

Commit

Permalink
Rely on UnitarySynthesis pass for unrolling UnitaryGates
Browse files Browse the repository at this point in the history
This commit adds the UnitarySynthesis pass to the preset pass manager
anywhere unrolling of custom or wide gates was done. Previously, we only
ever called the UnitarySynthesis pass in the preset pass managers if the
basis translation method was set to 'synthesis' or we were using level3
and then it was part of the optimization pass (as part of 2q peephole
optimization). This was an issue in that we're implicitly calling the
_define() method of the class whenever we're unrolling gates >= 3q or
gates with a custom definition. The _define() method is basically
identical to the UnitarySynthesis pass except it doesn't expose the
options to set a basis gate or approximation degree, which would result
in the output gates from unitary gates in the unroll steps are always in
the U3 basis. This meant we would be converting unitary gates to u3 (and
cx) which would result in a conversion to the target basis later, even
if we could just go to the target basis directly.

This is also future proofing for #6124 where a plugin interface is added
to the UnitarySynthesis pass and can potentially be used for arbitrary
sized unitaries.

At the same time this change caught an issue qith the SingleQubitUnitary
gate where the name was duplicated with the UnitaryGate which would
result in errors when UnitarySynthesis was called because the
UnitarySynthesis pass looks for gate named 'unitary' to run on. This is
fixed and the SingleQubitUnitary gate's name is changed to 'squ' to
differentiate it from the UnitaryGate.
  • Loading branch information
mtreinish committed May 5, 2021
1 parent 2eee566 commit a501fce
Show file tree
Hide file tree
Showing 6 changed files with 56 additions and 8 deletions.
2 changes: 1 addition & 1 deletion qiskit/extensions/quantum_initializer/squ.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def __init__(self, unitary_matrix, mode="ZYZ", up_to_diagonal=False, u=None):
self._diag = None

# Create new gate
super().__init__("unitary", 1, [unitary_matrix])
super().__init__("squ", 1, [unitary_matrix])

def inverse(self):
"""Return the inverse.
Expand Down
13 changes: 11 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,11 @@ def _choose_layout_condition(property_set):
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

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

# 4. Swap to fit the coupling map
_swap_check = CheckMap(coupling_map)
Expand Down Expand Up @@ -139,9 +143,14 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)]
_unroll = [
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
elif translation_method == "synthesis":
_unroll = [
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
17 changes: 15 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,11 @@ def _not_perfect_yet(property_set):
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

# 4. Decompose so only 1-qubit and 2-qubit gates remain
_unroll3q = Unroll3qOrMore()
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
Unroll3qOrMore(),
]

# 5. Swap to fit the coupling map
_swap_check = CheckMap(coupling_map)
Expand Down Expand Up @@ -159,9 +163,18 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)]
_unroll = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates before
# custom unrolling
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
elif translation_method == "synthesis":
_unroll = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates before
# collection
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
17 changes: 15 additions & 2 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,11 @@ def _csp_not_found_match(property_set):
_embed = [FullAncillaAllocation(coupling_map), EnlargeWithAncilla(), ApplyLayout()]

# 3. Unroll to 1q or 2q gates
_unroll3q = Unroll3qOrMore()
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
Unroll3qOrMore(),
]

# 4. Swap to fit the coupling map
_swap_check = CheckMap(coupling_map)
Expand Down Expand Up @@ -193,9 +197,18 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_unroll = [UnrollCustomDefinitions(sel, basis_gates), BasisTranslator(sel, basis_gates)]
_unroll = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates before
# custom unrolling
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
elif translation_method == "synthesis":
_unroll = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates before
# collection
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
6 changes: 5 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,11 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:
approximation_degree = pass_manager_config.approximation_degree

# 1. Unroll to 1q or 2q gates
_unroll3q = Unroll3qOrMore()
_unroll3q = [
# Use unitary synthesis for basis aware decomposition of UnitaryGates
UnitarySynthesis(basis_gates, approximation_degree=approximation_degree),
Unroll3qOrMore(),
]

# 2. Layout on good qubits if calibration info available, otherwise on dense links
_given_layout = SetLayout(initial_layout)
Expand Down
9 changes: 9 additions & 0 deletions releasenotes/notes/squ-gate-name-785b7896300a92ef.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
---
upgrade:
- |
The :attr:`~qiskit.extensions.SingleQubitUnitary.name` attribute of the
:class:`~qiskit.extensions.SingleQubitUnitary` gate class has been changed
from ``unitary`` to ``squ``. This was necessary to avoid a conflict with
the :class:`~qiskit.extensions.UnitaryGate` class's name which was also
``unitary`` since the 2 gates are not the same and don't have the same
implementation (and can't be used interchangeably).

0 comments on commit a501fce

Please sign in to comment.