Skip to content

Commit

Permalink
Transpiling with basis_gates=None disables unrolling (Qiskit#4263)
Browse files Browse the repository at this point in the history
* allow basis_gates=None

* some docstring and organization

* lint

* reno
  • Loading branch information
Luciano Bello authored Apr 27, 2020
1 parent e1c1af5 commit 7f785dd
Show file tree
Hide file tree
Showing 4 changed files with 55 additions and 22 deletions.
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

0 comments on commit 7f785dd

Please sign in to comment.