Skip to content

Commit

Permalink
Transpiler should raise when conflicting parameters, not just ignore …
Browse files Browse the repository at this point in the history
…them silently (Qiskit#4060)

* Qiskit#4003 (review)

* cast circuit parameter as a list sooner than later

* multiple circuits for passmanager.run

* remove pass_manager from the transpile_args

* set default optimization in a single place

* conflicting args

* lint

* reno

* lint!

* empty pass manager is a pass manager

* other redundant param

* deprecate parameter

* remove deprecated tranpile call from test.python.transpiler

* remove deprecated tranpile call from test.python.compiler

* last fix

* unsed import

* test/python/circuit/test_unitary.py

* unused import

* execute

* lint

* reno

Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
  • Loading branch information
Luciano Bello and mergify[bot] authored Apr 2, 2020
1 parent 968259d commit 377c7df
Show file tree
Hide file tree
Showing 21 changed files with 132 additions and 125 deletions.
95 changes: 56 additions & 39 deletions qiskit/compiler/transpile.py
Original file line number Diff line number Diff line change
Expand Up @@ -159,27 +159,60 @@ def callback_func(**kwargs):
The transpiled circuit(s).
Raises:
TranspilerError: in case of bad inputs to transpiler or errors in passes
TranspilerError: in case of bad inputs to transpiler (like conflicting parameters)
or errors in passes
"""
circuits = circuits if isinstance(circuits, list) else [circuits]

# transpiling schedules is not supported yet.
if isinstance(circuits, Schedule) or \
(isinstance(circuits, list) and all(isinstance(c, Schedule) for c in circuits)):
if all(isinstance(c, Schedule) for c in circuits):
warnings.warn("Transpiling schedules is not supported yet.", UserWarning)
if len(circuits) == 1:
return circuits[0]
return circuits

if pass_manager:
if pass_manager is not None:
_check_conflicting_argument(optimization_level=optimization_level, basis_gates=basis_gates,
coupling_map=coupling_map, seed_transpiler=seed_transpiler,
backend_properties=backend_properties,
initial_layout=initial_layout, layout_method=layout_method,
routing_method=routing_method, backend=backend)

warnings.warn("The parameter pass_manager in transpile is being deprecated. "
"The preferred way to tranpile a circuit using a custom pass manager is"
" pass_manager.run(circuit)", DeprecationWarning, stacklevel=2)
return pass_manager.run(circuits, output_name=output_name, callback=callback)

if optimization_level is None:
# Take optimization level from the configuration or 1 as default.
config = user_config.get_config()
optimization_level = config.get('transpile_optimization_level', None)
optimization_level = config.get('transpile_optimization_level', 1)

# Get transpile_args to configure the circuit transpilation job(s)
circuits = circuits if isinstance(circuits, list) else [circuits]
transpile_args = _parse_transpile_args(circuits, backend, basis_gates, coupling_map,
backend_properties, initial_layout,
layout_method, routing_method,
seed_transpiler, optimization_level,
pass_manager, callback, output_name)
callback, output_name)

_check_circuits_coupling_map(circuits, transpile_args, backend)

# Transpile circuits in parallel
circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args)))

if len(circuits) == 1:
return circuits[0]
return circuits


def _check_conflicting_argument(**kargs):
conflicting_args = [arg for arg, value in kargs.items() if value]
if conflicting_args:
raise TranspilerError("The parameters pass_manager conflicts with the following "
"parameter(s): {}.".format(', '.join(conflicting_args)))


def _check_circuits_coupling_map(circuits, transpile_args, backend):
# Check circuit width against number of qubits in coupling_map(s)
coupling_maps_list = list(config['pass_manager_config'].coupling_map for config in
transpile_args)
Expand All @@ -200,13 +233,6 @@ def callback_func(**kwargs):
'is greater than maximum ({}) '.format(max_qubits) +
'in the coupling_map')

# Transpile circuits in parallel
circuits = parallel_map(_transpile_circuit, list(zip(circuits, transpile_args)))

if len(circuits) == 1:
return circuits[0]
return circuits


def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> QuantumCircuit:
"""Select a PassManager and run a single circuit through it.
Expand Down Expand Up @@ -241,26 +267,19 @@ def _transpile_circuit(circuit_config_tuple: Tuple[QuantumCircuit, Dict]) -> Qua
pass_manager_config.basis_gates = list(
set(['u3', 'cx']).union(pass_manager_config.basis_gates))

if transpile_config['pass_manager'] is not None:
# either the pass manager is already selected...
pass_manager = transpile_config['pass_manager']
# we choose an appropriate one based on desired optimization level
level = transpile_config['optimization_level']

if level == 0:
pass_manager = level_0_pass_manager(pass_manager_config)
elif level == 1:
pass_manager = level_1_pass_manager(pass_manager_config)
elif level == 2:
pass_manager = level_2_pass_manager(pass_manager_config)
elif level == 3:
pass_manager = level_3_pass_manager(pass_manager_config)
else:
# or we choose an appropriate one based on desired optimization level (default: level 1)
if transpile_config['optimization_level'] is not None:
level = transpile_config['optimization_level']
else:
level = 1

if level == 0:
pass_manager = level_0_pass_manager(pass_manager_config)
elif level == 1:
pass_manager = level_1_pass_manager(pass_manager_config)
elif level == 2:
pass_manager = level_2_pass_manager(pass_manager_config)
elif level == 3:
pass_manager = level_3_pass_manager(pass_manager_config)
else:
raise TranspilerError("optimization_level can range from 0 to 3.")
raise TranspilerError("optimization_level can range from 0 to 3.")

if ms_basis_swap is not None:
pass_manager.append(MSBasisDecomposer(ms_basis_swap))
Expand All @@ -273,7 +292,7 @@ def _parse_transpile_args(circuits, backend,
basis_gates, coupling_map, backend_properties,
initial_layout, layout_method, routing_method,
seed_transpiler, optimization_level,
pass_manager, callback, output_name) -> List[Dict]:
callback, output_name) -> List[Dict]:
"""Resolve the various types of args allowed to the transpile() function through
duck typing, overriding args, etc. Refer to the transpile() docstring for details on
what types of inputs are allowed.
Expand All @@ -300,15 +319,14 @@ def _parse_transpile_args(circuits, backend,
routing_method = _parse_routing_method(routing_method, num_circuits)
seed_transpiler = _parse_seed_transpiler(seed_transpiler, num_circuits)
optimization_level = _parse_optimization_level(optimization_level, num_circuits)
pass_manager = _parse_pass_manager(pass_manager, num_circuits)
output_name = _parse_output_name(output_name, circuits)
callback = _parse_callback(callback, num_circuits)

list_transpile_args = []
for args in zip(basis_gates, coupling_map, backend_properties,
initial_layout, layout_method, routing_method,
seed_transpiler, optimization_level,
pass_manager, output_name, callback):
output_name, callback):
transpile_args = {'pass_manager_config': PassManagerConfig(basis_gates=args[0],
coupling_map=args[1],
backend_properties=args[2],
Expand All @@ -317,9 +335,8 @@ def _parse_transpile_args(circuits, backend,
routing_method=args[5],
seed_transpiler=args[6]),
'optimization_level': args[7],
'pass_manager': args[8],
'output_name': args[9],
'callback': args[10]}
'output_name': args[8],
'callback': args[9]}
list_transpile_args.append(transpile_args)

return list_transpile_args
Expand Down
32 changes: 22 additions & 10 deletions qiskit/execute.py
Original file line number Diff line number Diff line change
Expand Up @@ -216,16 +216,21 @@ def execute(experiments, backend,
"""

# transpiling the circuits using given transpile options
experiments = transpile(experiments,
basis_gates=basis_gates,
coupling_map=coupling_map,
backend_properties=backend_properties,
initial_layout=initial_layout,
seed_transpiler=seed_transpiler,
optimization_level=optimization_level,
backend=backend,
pass_manager=pass_manager,
)
if pass_manager is not None:
_check_conflicting_argument(optimization_level=optimization_level, basis_gates=basis_gates,
coupling_map=coupling_map, seed_transpiler=seed_transpiler,
backend_properties=backend_properties,
initial_layout=initial_layout, backend=backend)
experiments = pass_manager.run(experiments)
else:
experiments = transpile(experiments,
basis_gates=basis_gates,
coupling_map=coupling_map,
backend_properties=backend_properties,
initial_layout=initial_layout,
seed_transpiler=seed_transpiler,
optimization_level=optimization_level,
backend=backend)

if schedule_circuit:
if isinstance(experiments, Schedule) or isinstance(experiments[0], Schedule):
Expand Down Expand Up @@ -260,3 +265,10 @@ def execute(experiments, backend,

# executing the circuits on the backend and returning the job
return backend.run(qobj, **run_config)


def _check_conflicting_argument(**kargs):
conflicting_args = [arg for arg, value in kargs.items() if value]
if conflicting_args:
raise QiskitError("The parameters pass_manager conflicts with the following "
"parameter(s): {}.".format(', '.join(conflicting_args)))
2 changes: 2 additions & 0 deletions qiskit/transpiler/passmanager.py
Original file line number Diff line number Diff line change
Expand Up @@ -212,6 +212,8 @@ def callback_func(**kwargs):
"""
if isinstance(circuits, QuantumCircuit):
return self._run_single_circuit(circuits, output_name, callback)
elif len(circuits) == 1:
return self._run_single_circuit(circuits[0], output_name, callback)
else:
return self._run_several_circuits(circuits, output_name, callback)

Expand Down
4 changes: 0 additions & 4 deletions releasenotes/notes/fix-3925-c0e40ece078f2ce1.yaml

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
---
other:
- |
The function ```iqiskit.compiler.transpile``` now raises an error when conlicting parameters are passed, instead of ignoring them silently.
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
deprecations:
- |
The parameter ```pass_manager``` in the function ```transpile``` is going to be removed.
The preferred way to transpile a circuit with a custom pass manger is ```pass_manager.run(circuit)```.
5 changes: 1 addition & 4 deletions test/python/circuit/test_unitary.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,10 +23,8 @@
import qiskit
from qiskit.extensions.unitary import UnitaryGate
from qiskit.test import QiskitTestCase
from qiskit import BasicAer
from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
from qiskit.transpiler import PassManager
from qiskit.compiler import transpile
from qiskit.converters import circuit_to_dag, dag_to_circuit
from qiskit.transpiler.passes import CXCancellation

Expand Down Expand Up @@ -102,7 +100,6 @@ def test_1q_unitary(self):

def test_2q_unitary(self):
"""test 2 qubit unitary matrix"""
backend = BasicAer.get_backend('qasm_simulator')
qr = QuantumRegister(2)
cr = ClassicalRegister(2)
qc = QuantumCircuit(qr, cr)
Expand All @@ -114,7 +111,7 @@ def test_2q_unitary(self):
qc.append(uni2q, [qr[0], qr[1]])
passman = PassManager()
passman.append(CXCancellation())
qc2 = transpile(qc, backend, pass_manager=passman)
qc2 = passman.run(qc)
# test of qasm output
self.log.info(qc2.qasm())
# test of text drawer
Expand Down
4 changes: 1 addition & 3 deletions test/python/compiler/test_compiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -204,9 +204,7 @@ def test_compile_pass_manager(self):
qrtrue = assemble(transpile(qc, backend, seed_transpiler=8),
seed_simulator=42)
rtrue = backend.run(qrtrue).result()
qrfalse = assemble(transpile(qc, backend, seed_transpiler=8,
pass_manager=PassManager()),
seed_simulator=42)
qrfalse = assemble(PassManager().run(qc), seed_simulator=42)
rfalse = backend.run(qrfalse).result()
self.assertEqual(rtrue.get_counts(), rfalse.get_counts())

Expand Down
4 changes: 2 additions & 2 deletions test/python/compiler/test_transpiler.py
Original file line number Diff line number Diff line change
Expand Up @@ -535,7 +535,7 @@ def test_pass_manager_empty(self):
resources_before = circuit.count_ops()

pass_manager = PassManager()
out_circuit = transpile(circuit, pass_manager=pass_manager)
out_circuit = pass_manager.run(circuit)
resources_after = out_circuit.count_ops()

self.assertDictEqual(resources_before, resources_after)
Expand Down Expand Up @@ -717,7 +717,7 @@ def test_custom_multiple_circuits(self):
)
passmanager = level_0_pass_manager(pm_conf)

transpiled = transpile([qc, qc], pass_manager=passmanager)
transpiled = passmanager.run([qc, qc])

expected = QuantumCircuit(QuantumRegister(2, 'q'))
expected.u2(0, 3.141592653589793, 0)
Expand Down
7 changes: 2 additions & 5 deletions test/python/transpiler/test_collect_2q_blocks.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,9 @@

from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.converters import circuit_to_dag
from qiskit.compiler import transpile
from qiskit.transpiler import PassManager
from qiskit.transpiler.passes import Collect2qBlocks
from qiskit.test import QiskitTestCase
from qiskit.test.mock import FakeMelbourne


class TestCollect2qBlocks(QiskitTestCase):
Expand Down Expand Up @@ -130,12 +128,11 @@ def test_block_with_classical_register(self):
if(c0==0) u2(0.25*pi, 0.25*pi) q[0];
"""
qc = QuantumCircuit.from_qasm_str(qasmstr)
backend = FakeMelbourne()

pass_manager = PassManager()
pass_manager.append(Collect2qBlocks())

transpile(qc, backend, pass_manager=pass_manager)
pass_manager.run(qc)

self.assertEqual([['cx']],
[[n.name for n in block]
Expand Down Expand Up @@ -178,7 +175,7 @@ def test_do_not_merge_conditioned_gates(self):
pass_manager = PassManager()
pass_manager.append(Collect2qBlocks())

transpile(qc, pass_manager=pass_manager)
pass_manager.run(qc)
self.assertEqual([['cx']],
[[n.name for n in block]
for block in pass_manager.property_set['block_list']])
Expand Down
Loading

0 comments on commit 377c7df

Please sign in to comment.