From a6d98dd7f0c69e0226f2162e559154c5375d3dac Mon Sep 17 00:00:00 2001 From: Matthew Treinish Date: Thu, 20 Oct 2022 10:40:13 -0400 Subject: [PATCH] Fix parallel dispatch with target argument in transpile() (#8952) * Fix parallel dispatch with target argument in transpile() If a custom target was specified when calling the transpile() function with more than one circuit the parallel dispatch of the shared custom target would fail because the basis_gates created from the target was a dict keys type. Pickle is unable to serialize a dict keys object apparently and this would cause an exception to be raised. This commit fixes this by casting the basis_gates from the target as a list so that it can be serialized. * Use unittest.mock.patch.dict to set parallel execution in tests * Fix parallel flag for test * Fix parallel test setup In the previous patches we were attempting to use unittest.mock.patch.dict to override the env variable used to control parallel execution in qiskit. However, the issue with doing this is that the env variable is read at import time and stored in a global variable as we don't expect the env variable to change dynamically during the execution of a script. To workaround this the mocks are removed and instead a setUp() method is added to the test class to override the whatever the environment default is and instead hardcode parallel_map to run in parallel for the test and then switch it back to the earlier value after the test finishes. This lets us dynamically adjust the default behavior for parallel execution for this test class. Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com> (cherry picked from commit ef9259ded6c6ed910d408483e49667257ba283cc) --- qiskit/compiler/transpiler.py | 2 +- ...t-transpile-parallel-772f943a08d0570b.yaml | 7 +++++ test/python/compiler/test_transpiler.py | 31 +++++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) create mode 100644 releasenotes/notes/fix-target-transpile-parallel-772f943a08d0570b.yaml diff --git a/qiskit/compiler/transpiler.py b/qiskit/compiler/transpiler.py index ed01e335d1e2..26900e00df31 100644 --- a/qiskit/compiler/transpiler.py +++ b/qiskit/compiler/transpiler.py @@ -633,7 +633,7 @@ def _parse_transpile_args( if coupling_map is None: coupling_map = target.build_coupling_map() if basis_gates is None: - basis_gates = target.operation_names + basis_gates = list(target.operation_names) if instruction_durations is None: instruction_durations = target.durations() if inst_map is None: diff --git a/releasenotes/notes/fix-target-transpile-parallel-772f943a08d0570b.yaml b/releasenotes/notes/fix-target-transpile-parallel-772f943a08d0570b.yaml new file mode 100644 index 000000000000..e42320017597 --- /dev/null +++ b/releasenotes/notes/fix-target-transpile-parallel-772f943a08d0570b.yaml @@ -0,0 +1,7 @@ +--- +fixes: + - | + Fixed an issue with the :func:`~.transpile` where it would previously + fail with a ``TypeError`` if a custom :class:`~.Target` object was + passed in via the ``target`` argument and a list of multiple circuits + were specified for the ``circuits`` argument. diff --git a/test/python/compiler/test_transpiler.py b/test/python/compiler/test_transpiler.py index 8ab952b6f95f..292134f9b58b 100644 --- a/test/python/compiler/test_transpiler.py +++ b/test/python/compiler/test_transpiler.py @@ -60,6 +60,7 @@ from qiskit.quantum_info import Operator, random_unitary from qiskit.transpiler.passmanager_config import PassManagerConfig from qiskit.transpiler.preset_passmanagers import level_0_pass_manager +from qiskit.tools import parallel class CustomCX(Gate): @@ -1789,3 +1790,33 @@ def test_custom_multiple_circuits(self): self.assertEqual(len(transpiled), 2) self.assertEqual(transpiled[0], expected) self.assertEqual(transpiled[1], expected) + + +@ddt +class TestTranspileParallel(QiskitTestCase): + """Test transpile() in parallel.""" + + def setUp(self): + super().setUp() + + # Force parallel execution to True to test multiprocessing for this class + original_val = parallel.PARALLEL_DEFAULT + + def restore_default(): + parallel.PARALLEL_DEFAULT = original_val + + self.addCleanup(restore_default) + parallel.PARALLEL_DEFAULT = True + + @data(0, 1, 2, 3) + def test_parallel_with_target(self, opt_level): + """Test that parallel dispatch works with a manual target.""" + qc = QuantumCircuit(2) + qc.h(0) + qc.cx(0, 1) + qc.measure_all() + target = FakeMumbaiV2().target + res = transpile([qc] * 3, target=target, optimization_level=opt_level) + self.assertIsInstance(res, list) + for circ in res: + self.assertIsInstance(circ, QuantumCircuit)