Skip to content

Commit

Permalink
Add min_qubits kwarg to UnitarySynthesis pass (#6349)
Browse files Browse the repository at this point in the history
* Rely on UnitarySynthesis pass for unrolling UnitaryGates

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.

* Add option to set a minimum size to unitarysynthesis pass

This commit adds a new option to the UnitarySynthesis pass constructor,
min_qubits, which is used to specify a minimimum size unitary to
synthesize. If the unitary is smaller than that it will be skipped. This
is then used by the UnitarySynthesis instance in the unroll3q phase so
we don't decompose 1 or 2q unitaries before routing.

* Fix rebase error

* Correct oversights from rebase

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
mtreinish and mergify[bot] authored Sep 30, 2021
1 parent 9fa9565 commit 5b5b376
Show file tree
Hide file tree
Showing 7 changed files with 118 additions and 6 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
8 changes: 7 additions & 1 deletion qiskit/transpiler/passes/synthesis/unitary_synthesis.py
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ def __init__(
natural_direction: Union[bool, None] = None,
synth_gates: Union[List[str], None] = None,
method: str = "default",
min_qubits: int = None,
):
"""Synthesize unitaries over some basis gates.
Expand Down Expand Up @@ -131,11 +132,14 @@ def __init__(
['unitary']. If None and `pulse_optimzie` == True,
default to ['unitary', 'swap']
method (str): The unitary synthesis method plugin to use.
min_qubits: The minimum number of qubits in the unitary to synthesize. If this is set
and the unitary is less than the specified number of qubits it will not be
synthesized.
"""
super().__init__()
self._basis_gates = basis_gates
self._approximation_degree = approximation_degree
self._min_qubits = min_qubits
self.method = method
self.plugins = plugin.UnitarySynthesisPluginManager()
self._coupling_map = coupling_map
Expand Down Expand Up @@ -194,6 +198,8 @@ def run(self, dag: DAGCircuit) -> DAGCircuit:
plugin_method._approximation_degree = self._approximation_degree

for node in dag.named_nodes(*self._synth_gates):
if self._min_qubits is not None and len(node.qargs) < self._min_qubits:
continue
if plugin_method.supports_coupling_map:
kwargs["coupling_map"] = (
self._coupling_map,
Expand Down
22 changes: 21 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,14 @@ def _choose_layout_condition(property_set):

# 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,
),
Unroll3qOrMore(),
]
Expand Down Expand Up @@ -155,9 +157,27 @@ 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,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
),
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
elif translation_method == "synthesis":
_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
min_qubits=3,
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
26 changes: 25 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -133,12 +133,14 @@ def _not_perfect_yet(property_set):

# 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,
),
Unroll3qOrMore(),
]
Expand Down Expand Up @@ -175,9 +177,31 @@ 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,
coupling_map=coupling_map,
method=unitary_synthesis_method,
backend_props=backend_properties,
),
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,
coupling_map=coupling_map,
method=unitary_synthesis_method,
backend_props=backend_properties,
min_qubits=3,
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
26 changes: 25 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -167,12 +167,14 @@ def _csp_not_found_match(property_set):

# 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,
),
Unroll3qOrMore(),
]
Expand Down Expand Up @@ -209,9 +211,31 @@ 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,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
),
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,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
min_qubits=3,
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
22 changes: 21 additions & 1 deletion qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -108,12 +108,14 @@ def level_3_pass_manager(pass_manager_config: PassManagerConfig) -> PassManager:

# 1. 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,
),
Unroll3qOrMore(),
]
Expand Down Expand Up @@ -212,9 +214,27 @@ 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,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
),
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
elif translation_method == "synthesis":
_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
min_qubits=3,
),
Unroll3qOrMore(),
Collect2qBlocks(),
ConsolidateBlocks(basis_gates=basis_gates),
Expand Down
18 changes: 18 additions & 0 deletions releasenotes/notes/squ-gate-name-785b7896300a92ef.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
---
features:
- |
The :class:`~qiskit.transpiler.passes.UnitarySynthesis` transpiler pass in
:mod:`qiskit.transpiler.passes` has a new kwarg in the constructor,
``min_qubits``. When specified this can be set to an ``int`` value which
is the minimum size :class:`~qiskit.extensions.UnitaryGate` object to
run the unitary synthesis on. If a :class:`~qiskit.extensions.UnitaryGate`
in a :class:`~qiskit.circuit.QuantumCircuit` uses fewer qubits it will
be skipped by that instance of the pass.
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 5b5b376

Please sign in to comment.