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

Transpiling with basis_gates=None disables unrolling #4263

Merged
merged 5 commits into from
Apr 27, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
28 changes: 11 additions & 17 deletions qiskit/compiler/transpile.py
Original file line number Diff line number Diff line change
Expand Up @@ -273,17 +273,18 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua

pass_manager_config = transpile_config['pass_manager_config']

# Workaround for ion trap support: If basis gates includes
# Mølmer-Sørensen (rxx) and the circuit includes gates outside the basis,
# first unroll to u3, cx, then run MSBasisDecomposer to target basis.
basic_insts = ['measure', 'reset', 'barrier', 'snapshot']
device_insts = set(pass_manager_config.basis_gates).union(basic_insts)
ms_basis_swap = None
if 'rxx' in pass_manager_config.basis_gates and \
not device_insts >= circuit.count_ops().keys():
ms_basis_swap = pass_manager_config.basis_gates
pass_manager_config.basis_gates = list(
set(['u3', 'cx']).union(pass_manager_config.basis_gates))
if pass_manager_config.basis_gates is not None:
# Workaround for ion trap support: If basis gates includes
# Mølmer-Sørensen (rxx) and the circuit includes gates outside the basis,
# first unroll to u3, cx, then run MSBasisDecomposer to target basis.
basic_insts = ['measure', 'reset', 'barrier', 'snapshot']
device_insts = set(pass_manager_config.basis_gates).union(basic_insts)
if 'rxx' in pass_manager_config.basis_gates and \
not device_insts >= circuit.count_ops().keys():
ms_basis_swap = pass_manager_config.basis_gates
pass_manager_config.basis_gates = list(
set(['u3', 'cx']).union(pass_manager_config.basis_gates))

# we choose an appropriate one based on desired optimization level
level = transpile_config['optimization_level']
Expand Down Expand Up @@ -370,13 +371,6 @@ def _parse_basis_gates(basis_gates, backend, circuits):
all(isinstance(i, str) for i in basis_gates)):
basis_gates = [basis_gates] * len(circuits)

# no basis means don't unroll (all circuit gates are valid basis)
for index, circuit in enumerate(circuits):
basis = basis_gates[index]
if basis is None:
gates_in_circuit = {inst.name for inst, _, _ in circuit.data}
# Other passes might add new gates that need to be supported
basis_gates[index] = list(gates_in_circuit.union(['u3', 'cx']))
return basis_gates


Expand Down
6 changes: 5 additions & 1 deletion qiskit/transpiler/passes/basis/unroller.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,8 @@ def __init__(self, basis):
"""Unroller initializer.

Args:
basis (list[str]): Target basis names to unroll to, e.g. `['u3', 'cx']` .
basis (list[str] or None): Target basis names to unroll to, e.g. `['u3', 'cx']` . If
None, does not unroll any gate.
"""
super().__init__()
self.basis = basis
Expand All @@ -48,6 +49,9 @@ def run(self, dag):
Returns:
DAGCircuit: output unrolled dag
"""
if self.basis is None:
return dag

# Walk through the DAG and expand each non-basis node
for node in dag.op_nodes():
basic_insts = ['measure', 'reset', 'barrier', 'snapshot']
Expand Down
27 changes: 27 additions & 0 deletions releasenotes/notes/transpiling_basis_none-b2f1abdb3c080eca.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
---
fixes:
- |
Bug #3017 is fixed. The function ``qiskit.compiler.transpile()`` honors the parameter ``basis_gates=None``
to allow any gate in the final tranpiled circuit, including gates added by the transpilation process.
This might also have some unintended consequences during optimization. For example,
:class:`qiskit.transpiler.passes.Optimize1qGates` only optimizes the chains of u1, u2, and u3 gates:

.. code-block:: python

from qiskit import *

q = QuantumRegister(1, name='q')
circuit = QuantumCircuit(q)
circuit.h(q[0])
circuit.u1(0.1, q[0])
circuit.u2(0.1, 0.2, q[0])
circuit.h(q[0])
circuit.u3(0.1, 0.2, 0.3, q[0])

result = transpile(circuit, basis_gates=None, optimization_level=3)
result.draw()

.. parsed-literal::
┌───┐┌─────────────┐┌───┐┌─────────────────┐
q_0: ┤ H ├┤ U2(0.1,0.3) ├┤ H ├┤ U3(0.1,0.2,0.3) ├
└───┘└─────────────┘└───┘└─────────────────┘
16 changes: 12 additions & 4 deletions test/python/transpiler/test_preset_passmanagers.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,17 +42,25 @@ def circuit_2532():
class TestPresetPassManager(QiskitTestCase):
"""Test preset passmanagers work as expected."""

@combine(level=[0, 1, 2, 3],
dsc='Test that coupling_map can be None (level={level})',
name='coupling_map_none_level{level}')
@combine(level=[0, 1, 2, 3], name='level{level}')
def test_no_coupling_map(self, level):
"""Test that coupling_map can be None"""
"""Test that coupling_map can be None (level={level})"""
q = QuantumRegister(2, name='q')
circuit = QuantumCircuit(q)
circuit.cz(q[0], q[1])
result = transpile(circuit, basis_gates=['u1', 'u2', 'u3', 'cx'], optimization_level=level)
self.assertIsInstance(result, QuantumCircuit)

@combine(level=[0, 1, 2, 3], name='level{level}')
def test_no_basis_gates(self, level):
"""Test that basis_gates can be None (level={level})"""
q = QuantumRegister(2, name='q')
circuit = QuantumCircuit(q)
circuit.h(q[0])
circuit.cz(q[0], q[1])
result = transpile(circuit, basis_gates=None, optimization_level=level)
self.assertEqual(result, circuit)


@ddt
class TestTranspileLevels(QiskitTestCase):
Expand Down