Skip to content

Commit

Permalink
Ensure UnitaryGate's are preserved by transpile if in basis gates
Browse files Browse the repository at this point in the history
This commit tweaks the preset passmanager definitions to not include the
UnitarySynthesis pass as part of basis translation if the 'unitary' gate
is in the basis gates. Previously in Qiskit#6124 and Qiskit#6349 the preset
passmanagers were updated to leverage the user specified unitary
synthesis method instead of implicitly using the default as part of the
unroll 3q and unroll custom definition passes. This however had the
unintended side effect of always synthesizing unitary gates even if it
was in the basis (which is the case on Aer). This commit fixes this by
only running unitary synthesis as part of the basis translation step if
it's not in the basis.

A potential follow-on here is to make the unroll 3q transpiler pass
basis aware (since right now it will implicitly run unitary synthesis
internally) and add a similar logic around the use of the
UnitarySynthesis pass for unrolling 3q or larger gates. The unroll 3q
transpiler stage suffers from this same issue. However, this wasn't
done here because the way 3q unrolling is used is to reduce the gates to
be less than 3 qubits so the layout and routing phase can deal work with
the gates in the circuit would likely cause issues if a unitary gate
larger than 2 qubits was in the circuit being transpiled.
  • Loading branch information
mtreinish committed Oct 5, 2021
1 parent f59a1fd commit 49fcc41
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 34 deletions.
21 changes: 13 additions & 8 deletions qiskit/transpiler/preset_passmanagers/level0.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,14 +157,19 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
),
if basis_gates is not None and "unitary" not in basis_gates:
_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
)
]
else:
_unroll = []
_unroll += [
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
Expand Down
21 changes: 13 additions & 8 deletions qiskit/transpiler/preset_passmanagers/level1.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,16 +177,21 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_unroll = [
if basis_gates is not None and "unitary" not in basis_gates:
# 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,
),
_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
)
]
else:
_unroll = []
_unroll += [
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
Expand Down
25 changes: 15 additions & 10 deletions qiskit/transpiler/preset_passmanagers/level2.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,16 +211,21 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_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,
),
# Use unitary synthesis for basis aware decomposition of UnitaryGates before
# custom unrolling
if basis_gates is not None and "unitary" not in basis_gates:
_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
)
]
else:
_unroll = []
_unroll += [
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
Expand Down
21 changes: 13 additions & 8 deletions qiskit/transpiler/preset_passmanagers/level3.py
Original file line number Diff line number Diff line change
Expand Up @@ -214,14 +214,19 @@ def _swap_condition(property_set):
elif translation_method == "translator":
from qiskit.circuit.equivalence_library import SessionEquivalenceLibrary as sel

_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
),
if basis_gates is not None and "unitary" not in basis_gates:
_unroll = [
UnitarySynthesis(
basis_gates,
approximation_degree=approximation_degree,
coupling_map=coupling_map,
backend_props=backend_properties,
method=unitary_synthesis_method,
)
]
else:
_unroll = []
_unroll += [
UnrollCustomDefinitions(sel, basis_gates),
BasisTranslator(sel, basis_gates),
]
Expand Down
10 changes: 10 additions & 0 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
)
from qiskit.converters import circuit_to_dag
from qiskit.circuit.library import GraphState
from qiskit.quantum_info import random_unitary


def emptycircuit():
Expand Down Expand Up @@ -98,6 +99,15 @@ def test_level0_keeps_reset(self):
result = transpile(circuit, basis_gates=None, optimization_level=0)
self.assertEqual(result, circuit)

@combine(level=[0, 1, 2, 3], name="level{level}")
def test_unitary_is_preserved_if_in_basis(self, level):
"""Test that a unitary is not synthesized if in the basis."""
qc = QuantumCircuit(2)
qc.unitary(random_unitary(4, seed=42), [0, 1])
qc.measure_all()
result = transpile(qc, basis_gates=["cx", "u", "unitary"], optimization_level=level)
self.assertEqual(result, qc)

@combine(level=[0, 1, 2, 3], name="level{level}")
def test_respect_basis(self, level):
"""Test that all levels respect basis"""
Expand Down

0 comments on commit 49fcc41

Please sign in to comment.